diff --git a/.ci/build-mac.sh b/.ci/build-mac.sh index 9e16fce4ec..ae0341cc0e 100755 --- a/.ci/build-mac.sh +++ b/.ci/build-mac.sh @@ -13,31 +13,18 @@ export HOMEBREW_NO_AUTO_UPDATE=1 export HOMEBREW_NO_INSTALLED_DEPENDENTS_CHECK=1 export HOMEBREW_NO_ENV_HINTS=1 export HOMEBREW_NO_INSTALL_CLEANUP=1 -brew update brew install -f --overwrite --quiet ccache "llvm@$LLVM_COMPILER_VER" brew link -f --overwrite --quiet "llvm@$LLVM_COMPILER_VER" -if [ "$AARCH64" -eq 1 ]; then - brew install -f --overwrite --quiet googletest opencv@4 sdl3 vulkan-headers vulkan-loader molten-vk - brew unlink --quiet ffmpeg fmt qtbase qtsvg qtdeclarative protobuf || true -else - 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 python@3.14 opencv@4 "llvm@$LLVM_COMPILER_VER" sdl3 vulkan-headers vulkan-loader molten-vk - arch -x86_64 /usr/local/bin/brew unlink --quiet ffmpeg qtbase qtsvg qtdeclarative protobuf || true -fi +brew install -f --overwrite --quiet googletest opencv@4 sdl3 vulkan-headers vulkan-loader molten-vk +brew unlink --quiet ffmpeg fmt qtbase qtsvg qtdeclarative protobuf || true export CXX=clang++ export CC=clang export BREW_PATH; -if [ "$AARCH64" -eq 1 ]; then - BREW_PATH="$(brew --prefix)" - export BREW_BIN="/opt/homebrew/bin" - export BREW_SBIN="/opt/homebrew/sbin" -else - BREW_PATH="$("/usr/local/bin/brew" --prefix)" - export BREW_BIN="/usr/local/bin" - export BREW_SBIN="/usr/local/sbin" -fi +BREW_PATH="$(brew --prefix)" +export BREW_BIN="$BREW_PATH/bin" +export BREW_SBIN="$BREW_PATH/sbin" export WORKDIR; WORKDIR="$(pwd)" @@ -68,7 +55,7 @@ ditto "/tmp/Qt/$QT_VER" "qt-downloader/$QT_VER" export Qt6_DIR="$WORKDIR/qt-downloader/$QT_VER/clang_64/lib/cmake/Qt$QT_VER_MAIN" export SDL3_DIR="$BREW_PATH/opt/sdl3/lib/cmake/SDL3" -export PATH="/opt/homebrew/opt/llvm@$LLVM_COMPILER_VER/bin:$PATH" +export PATH="$BREW_PATH/opt/llvm@$LLVM_COMPILER_VER/bin:$PATH" export LDFLAGS="-L$BREW_PATH/opt/llvm@$LLVM_COMPILER_VER/lib/c++ -L$BREW_PATH/opt/llvm@$LLVM_COMPILER_VER/lib/unwind -lunwind" export VULKAN_SDK @@ -82,12 +69,13 @@ LLVM_DIR="$BREW_PATH/opt/llvm@$LLVM_COMPILER_VER" git submodule -q update --init --depth=1 --jobs=8 $(awk '/path/ && !/llvm/ && !/opencv/ && !/SDL/ && !/feralinteractive/ { print $3 }' .gitmodules) mkdir build && cd build || exit 1 +# The below should be uncommented once bugs with Qt 6 QListWidgets when using the OS 26 visual style are resolved. +# sudo xcode-select -switch /Applications/Xcode_26.3.app/Contents/Developer -if [ "$AARCH64" -eq 1 ]; then cmake .. \ -DBUILD_RPCS3_TESTS="${RUN_UNIT_TESTS}" \ -DRUN_RPCS3_TESTS="${RUN_UNIT_TESTS}" \ - -DCMAKE_OSX_DEPLOYMENT_TARGET=14.4 \ + -DCMAKE_OSX_DEPLOYMENT_TARGET=15.0 \ -DCMAKE_OSX_SYSROOT="$(xcrun --sdk macosx --show-sdk-path)" \ -DMACOSX_BUNDLE_SHORT_VERSION_STRING="${COMM_TAG}" \ -DMACOSX_BUNDLE_BUNDLE_VERSION="${COMM_COUNT}" \ @@ -102,31 +90,8 @@ cmake .. \ -DUSE_SYSTEM_SDL=ON \ -DUSE_SYSTEM_OPENCV=ON \ -G Ninja -else -cmake .. \ - -DBUILD_RPCS3_TESTS=OFF \ - -DRUN_RPCS3_TESTS=OFF \ - -DCMAKE_OSX_ARCHITECTURES=x86_64 \ - -DCMAKE_SYSTEM_PROCESSOR=x86_64 \ - -DCMAKE_TOOLCHAIN_FILE=buildfiles/cmake/TCDarwinX86_64.cmake \ - -DCMAKE_OSX_DEPLOYMENT_TARGET=14.4 \ - -DCMAKE_OSX_SYSROOT="$(xcrun --sdk macosx --show-sdk-path)" \ - -DMACOSX_BUNDLE_SHORT_VERSION_STRING="${COMM_TAG}" \ - -DMACOSX_BUNDLE_BUNDLE_VERSION="${COMM_COUNT}"\ - -DSTATIC_LINK_LLVM=ON \ - -DUSE_SDL=ON \ - -DUSE_DISCORD_RPC=ON \ - -DUSE_AUDIOUNIT=ON \ - -DUSE_SYSTEM_FFMPEG=OFF \ - -DUSE_NATIVE_INSTRUCTIONS=OFF \ - -DUSE_PRECOMPILED_HEADERS=OFF \ - -DUSE_SYSTEM_MVK=ON \ - -DUSE_SYSTEM_SDL=ON \ - -DUSE_SYSTEM_OPENCV=ON \ - -G Ninja -fi -ninja; build_status=$?; +ninja -j4; build_status=$?; cd .. diff --git a/.ci/deploy-mac.sh b/.ci/deploy-mac.sh index b37b27c1e2..045b93ac9f 100755 --- a/.ci/deploy-mac.sh +++ b/.ci/deploy-mac.sh @@ -7,7 +7,6 @@ cd bin git clone --revision=a075e5e417f87675ea3137b7365f3e5a99608d72 https://github.com/KhronosGroup/MoltenVK.git cd MoltenVK ./fetchDependencies --macos -sudo xcode-select -switch /Applications/Xcode_16.2.app/Contents/Developer make macos MVK_USE_METAL_PRIVATE_API=1 cd ../ @@ -84,7 +83,7 @@ echo "[InternetShortcut]" > Quickstart.url echo "URL=https://rpcs3.net/quickstart" >> Quickstart.url echo "IconIndex=0" >> Quickstart.url -if [ "$AARCH64" -eq 1 ]; then +if [ "$(arch)" = "arm64" ]; then ARCHIVE_FILEPATH="$BUILD_ARTIFACTSTAGINGDIRECTORY/rpcs3-v${LVER}_macos_aarch64.7z" else ARCHIVE_FILEPATH="$BUILD_ARTIFACTSTAGINGDIRECTORY/rpcs3-v${LVER}_macos.7z" diff --git a/.github/workflows/rpcs3.yml b/.github/workflows/rpcs3.yml index e6877387b3..515555a224 100644 --- a/.github/workflows/rpcs3.yml +++ b/.github/workflows/rpcs3.yml @@ -123,15 +123,15 @@ jobs: matrix: include: - name: Intel - AARCH64: 0 + runs-on: macos-15-intel UPLOAD_COMMIT_HASH: 51ae32f468089a8169aaf1567de355ff4a3e0842 UPLOAD_REPO_FULL_NAME: rpcs3/rpcs3-binaries-mac - name: Apple Silicon - AARCH64: 1 + runs-on: macos-15 UPLOAD_COMMIT_HASH: 8e21bdbc40711a3fccd18fbf17b742348b0f4281 UPLOAD_REPO_FULL_NAME: rpcs3/rpcs3-binaries-mac-arm64 name: RPCS3 Mac ${{ matrix.name }} - runs-on: macos-14 + runs-on: ${{ matrix.runs-on }} env: CCACHE_DIR: /tmp/ccache_dir QT_VER: '6.11.1' @@ -140,8 +140,7 @@ jobs: RELEASE_MESSAGE: ../GitHubReleaseMessage.txt UPLOAD_COMMIT_HASH: ${{ matrix.UPLOAD_COMMIT_HASH }} UPLOAD_REPO_FULL_NAME: ${{ matrix.UPLOAD_REPO_FULL_NAME }} - AARCH64: ${{ matrix.AARCH64 }} - RUN_UNIT_TESTS: github.event_name == 'pull_request' && 'ON' || 'OFF' + RUN_UNIT_TESTS: ${{ matrix.name == 'Apple Silicon' && (github.event_name == 'pull_request' && 'ON') || 'OFF' }} steps: - name: Checkout repository uses: actions/checkout@main diff --git a/3rdparty/ffmpeg b/3rdparty/ffmpeg index 42300548cb..649d9d9353 160000 --- a/3rdparty/ffmpeg +++ b/3rdparty/ffmpeg @@ -1 +1 @@ -Subproject commit 42300548cb6363b4496e7f0041a461daafd52dfe +Subproject commit 649d9d9353184a17ae78d9df4a6fbbe7456f0428 diff --git a/3rdparty/pine/pine_server.h b/3rdparty/pine/pine_server.h index fe88ddb474..5c59e87da3 100644 --- a/3rdparty/pine/pine_server.h +++ b/3rdparty/pine/pine_server.h @@ -164,14 +164,17 @@ namespace pine return IPCBuffer{ 5, MakeFailIPC(ret_buffer) }; }; - const auto write_string = [&](const std::string& str) + const auto write_string = [&](std::string_view str) { if (!SafetyChecks(buf_cnt, 0, ret_cnt, str.size() + 1 + sizeof(u32), buf_size)) return false; ToArray(ret_buffer, ::narrow(str.size() + 1), ret_cnt); ret_cnt += sizeof(u32); - memcpy(&ret_buffer[ret_cnt], str.data(), str.size()); - ret_cnt += str.size(); + if (str.size()) + { + std::memcpy(&ret_buffer[ret_cnt], str.data(), str.size()); + ret_cnt += str.size(); + } ret_buffer[ret_cnt++] = '\0'; return true; }; @@ -512,7 +515,7 @@ namespace pine */ static inline bool SafetyChecks(usz command_len, usz command_size, usz reply_len, usz reply_size = 0, usz buf_size = MAX_IPC_SIZE - 1) { - bool res = ((command_len + command_size) > buf_size || + const bool res = ((command_len + command_size) > buf_size || (reply_len + reply_size) >= MAX_IPC_RETURN_SIZE); if (res) [[unlikely]] return false; @@ -524,8 +527,8 @@ namespace pine pine_server() noexcept { #ifdef _WIN32 - WSADATA wsa; - struct sockaddr_in server; + WSADATA wsa {}; + struct sockaddr_in server {}; m_sock = INVALID_SOCKET; m_msgsock = INVALID_SOCKET; @@ -579,7 +582,7 @@ namespace pine fmt::append(m_socket_name, ".%d", slot); } - struct sockaddr_un server; + struct sockaddr_un server {}; m_sock = socket(AF_UNIX, SOCK_STREAM, 0); if (m_sock < 0) diff --git a/3rdparty/pugixml b/3rdparty/pugixml index ee86beb30e..c8033ce9d0 160000 --- a/3rdparty/pugixml +++ b/3rdparty/pugixml @@ -1 +1 @@ -Subproject commit ee86beb30e4973f5feffe3ce63bfa4fbadf72f38 +Subproject commit c8033ce9d039e7f9d134877c363397b3cfe20816 diff --git a/Utilities/Config.cpp b/Utilities/Config.cpp index 87d4c14690..6b2e3bf841 100644 --- a/Utilities/Config.cpp +++ b/Utilities/Config.cpp @@ -757,7 +757,7 @@ void cfg::log_entry::from_default() std::pair cfg::device_info::get_usb_ids() const { - auto string_to_hex = [](const std::string& str) -> u16 + auto string_to_hex = [](std::string_view str) -> u16 { u16 value = 0x0000; if (!str.empty() && std::from_chars(str.data(), str.data() + str.size(), value, 16).ec != std::errc{}) diff --git a/Utilities/JIT.h b/Utilities/JIT.h index d18f795563..aba30c3400 100644 --- a/Utilities/JIT.h +++ b/Utilities/JIT.h @@ -525,7 +525,7 @@ class jit_compiler final atomic_t m_disk_space = umax; public: - jit_compiler(const std::unordered_map& _link, const std::string& _cpu, u32 flags = 0, std::function symbols_cement = {}) noexcept; + jit_compiler(const std::unordered_map& _link, std::string_view _cpu, u32 flags = 0, std::function symbols_cement = {}) noexcept; jit_compiler& operator=(thread_state) noexcept; ~jit_compiler() noexcept; @@ -571,7 +571,7 @@ public: u64 get(const std::string& name); // Get CPU info - static std::string cpu(const std::string& _cpu); + static std::string cpu(std::string_view _cpu); // Get system triple (PPU) static std::string triple1(); diff --git a/Utilities/JITLLVM.cpp b/Utilities/JITLLVM.cpp index 66a7d3e678..3bfb30cea2 100644 --- a/Utilities/JITLLVM.cpp +++ b/Utilities/JITLLVM.cpp @@ -551,9 +551,9 @@ public: } }; -std::string jit_compiler::cpu(const std::string& _cpu) +std::string jit_compiler::cpu(std::string_view _cpu) { - std::string m_cpu = _cpu; + std::string m_cpu = std::string(_cpu); if (m_cpu.empty()) { @@ -681,7 +681,7 @@ bool jit_compiler::add_sub_disk_space(ssz space) }).second; } -jit_compiler::jit_compiler(const std::unordered_map& _link, const std::string& _cpu, u32 flags, std::function symbols_cement) noexcept +jit_compiler::jit_compiler(const std::unordered_map& _link, std::string_view _cpu, u32 flags, std::function symbols_cement) noexcept : m_context(new llvm::LLVMContext) , m_cpu(cpu(_cpu)) { diff --git a/Utilities/StrFmt.cpp b/Utilities/StrFmt.cpp index d68ef51cb5..cd0fe2b5dc 100644 --- a/Utilities/StrFmt.cpp +++ b/Utilities/StrFmt.cpp @@ -921,6 +921,13 @@ std::string_view fmt::trim_front_sv(std::string_view source, std::string_view va void fmt::trim_back(std::string& source, std::string_view values) { const usz index = source.find_last_not_of(values); + + if (index == source.npos) + { + source.clear(); + return; + } + source.resize(index + 1); } diff --git a/Utilities/cheat_info.cpp b/Utilities/cheat_info.cpp index 7745d26732..e67e103214 100644 --- a/Utilities/cheat_info.cpp +++ b/Utilities/cheat_info.cpp @@ -29,7 +29,7 @@ void fmt_class_string::format(std::string& out, u64 arg) bool cheat_info::from_str(std::string_view cheat_line) { - const auto cheat_vec = fmt::split(cheat_line, {"@@@"}, false); + auto cheat_vec = fmt::split(cheat_line, {"@@@"}, false); s64 val64 = 0; if (cheat_vec.size() != 5 || !try_to_int64(&val64, cheat_vec[2], 0, cheat_type_max - 1)) @@ -38,11 +38,11 @@ bool cheat_info::from_str(std::string_view cheat_line) return false; } - game = cheat_vec[0]; - description = cheat_vec[1]; + game = std::move(cheat_vec[0]); + description = std::move(cheat_vec[1]); type = cheat_type{::narrow(val64)}; offset = std::stoul(cheat_vec[3]); - red_script = cheat_vec[4]; + red_script = std::move(cheat_vec[4]); return true; } diff --git a/Utilities/stack_trace.cpp b/Utilities/stack_trace.cpp index ef188db614..0a95269848 100644 --- a/Utilities/stack_trace.cpp +++ b/Utilities/stack_trace.cpp @@ -119,18 +119,18 @@ namespace utils if (sym->NameLen) { - const auto function_name = wstr_to_utf8(sym->Name, static_cast(sym->NameLen)); + std::string function_name = wstr_to_utf8(sym->Name, static_cast(sym->NameLen)); // Attempt to get file and line information if available DWORD unused2; if (SymGetLineFromAddrW64(hProcess, reinterpret_cast(pointer), &unused2, &line_info)) { - const auto full_path = fmt::format("%s:%u %s", wstr_to_utf8(line_info.FileName, -1), line_info.LineNumber, function_name); - result.push_back(full_path); + std::string full_path = fmt::format("%s:%u %s", wstr_to_utf8(line_info.FileName, -1), line_info.LineNumber, function_name); + result.push_back(std::move(full_path)); } else { - result.push_back(function_name); + result.push_back(std::move(function_name)); } } else diff --git a/Utilities/stack_trace.h b/Utilities/stack_trace.h index d0cec0cf4c..d186a698f4 100644 --- a/Utilities/stack_trace.h +++ b/Utilities/stack_trace.h @@ -14,7 +14,7 @@ namespace utils // Printing utilities template - concept Logger = requires (T& t, const std::string& msg) + concept Logger = requires (T& t, std::string_view msg) { { t.print(msg) }; }; @@ -28,7 +28,7 @@ namespace utils : log(chan) {} - void print(const std::string& s) + void print(std::string_view s) { log.error("%s", s); } diff --git a/Utilities/transactional_storage.h b/Utilities/transactional_storage.h index 1be923233e..87b9d63aac 100644 --- a/Utilities/transactional_storage.h +++ b/Utilities/transactional_storage.h @@ -33,7 +33,7 @@ public: std::lock_guard lock(mutex); if (std::shared_ptr new_val = std::invoke(func); new_val) { - storage.push_back(new_val); + storage.push_back(std::move(new_val)); } delete_unused(); } diff --git a/Utilities/version.cpp b/Utilities/version.cpp index 0c3cbb6da1..8c63560743 100644 --- a/Utilities/version.cpp +++ b/Utilities/version.cpp @@ -24,29 +24,27 @@ namespace utils return (m_hi << 24) | (m_mid << 16) | (m_lo << 8) | ((uint(m_type) & 0xf) << 4) | (m_type_index & 0xf); } - std::string version::to_string() const + std::string version::to_string(bool simple) const { - std::string version = std::to_string(hi()) + "." + std::to_string(mid()); + std::string version = fmt::format("%d.%d", hi(), mid()); if (lo()) { - version += '.'; - version += std::to_string(lo()); + fmt::append(version, ".%d", lo()); } - if (type() != version_type::release) + if (!simple && type() != version_type::release) { if (!postfix().empty()) { - version += "-" + postfix(); + fmt::append(version, "-%s", postfix()); } - version += ' '; - version += utils::to_string(type()); + fmt::append(version, " %s", utils::to_string(type())); if (type_index() > 1) { - version += " " + std::to_string(type_index()); + fmt::append(version, " %d", type_index()); } } diff --git a/Utilities/version.h b/Utilities/version.h index 00180b5436..316c1ae96f 100644 --- a/Utilities/version.h +++ b/Utilities/version.h @@ -23,10 +23,10 @@ namespace utils uint m_lo; version_type m_type = version_type::release; uint m_type_index = 1; - const char* m_postfix; + std::string_view m_postfix; public: - constexpr version(uint hi, uint mid, uint lo, version_type type, uint type_index, const char* postfix) + constexpr version(uint hi, uint mid, uint lo, version_type type, uint type_index, std::string_view postfix) : m_hi(hi) , m_mid(mid) , m_lo(lo) @@ -56,7 +56,7 @@ namespace utils return m_type; } - std::string postfix() const + std::string_view postfix() const { return m_postfix; } @@ -67,7 +67,7 @@ namespace utils } uint to_hex() const; - std::string to_string() const; + std::string to_string(bool simple = false) const; }; // Generic version comparison (e.g. 0.0.5 vs 1.3) diff --git a/bin/GuiConfigs/check_mark_white.png b/bin/GuiConfigs/check_mark_white.png index f9b94ccb87..dd1b888dd6 100644 Binary files a/bin/GuiConfigs/check_mark_white.png and b/bin/GuiConfigs/check_mark_white.png differ diff --git a/bin/GuiConfigs/list_arrow_blue.png b/bin/GuiConfigs/list_arrow_blue.png index 6875377f35..da934a3bc1 100644 Binary files a/bin/GuiConfigs/list_arrow_blue.png and b/bin/GuiConfigs/list_arrow_blue.png differ diff --git a/bin/GuiConfigs/list_arrow_down_blue.png b/bin/GuiConfigs/list_arrow_down_blue.png index 18230006dc..baeee3e269 100644 Binary files a/bin/GuiConfigs/list_arrow_down_blue.png and b/bin/GuiConfigs/list_arrow_down_blue.png differ diff --git a/bin/GuiConfigs/list_arrow_down_green.png b/bin/GuiConfigs/list_arrow_down_green.png index 1ecfc6d288..1d691db108 100644 Binary files a/bin/GuiConfigs/list_arrow_down_green.png and b/bin/GuiConfigs/list_arrow_down_green.png differ diff --git a/bin/GuiConfigs/list_arrow_down_white.png b/bin/GuiConfigs/list_arrow_down_white.png index 12bd7b59dd..3708819116 100644 Binary files a/bin/GuiConfigs/list_arrow_down_white.png and b/bin/GuiConfigs/list_arrow_down_white.png differ diff --git a/bin/GuiConfigs/list_arrow_green.png b/bin/GuiConfigs/list_arrow_green.png index 239b31c8d0..6bc0ad79e4 100644 Binary files a/bin/GuiConfigs/list_arrow_green.png and b/bin/GuiConfigs/list_arrow_green.png differ diff --git a/bin/GuiConfigs/list_arrow_white.png b/bin/GuiConfigs/list_arrow_white.png index aa0ac4cae2..72f51af554 100644 Binary files a/bin/GuiConfigs/list_arrow_white.png and b/bin/GuiConfigs/list_arrow_white.png differ diff --git a/bin/Icons/ui/L1.png b/bin/Icons/ui/L1.png index 8a1d87c540..62b170aa79 100644 Binary files a/bin/Icons/ui/L1.png and b/bin/Icons/ui/L1.png differ diff --git a/bin/Icons/ui/L2.png b/bin/Icons/ui/L2.png index 3b938d19ac..f20162f882 100644 Binary files a/bin/Icons/ui/L2.png and b/bin/Icons/ui/L2.png differ diff --git a/bin/Icons/ui/R1.png b/bin/Icons/ui/R1.png index 722e405c60..0bd24e92bc 100644 Binary files a/bin/Icons/ui/R1.png and b/bin/Icons/ui/R1.png differ diff --git a/bin/Icons/ui/R2.png b/bin/Icons/ui/R2.png index f05529fea9..7573a13715 100644 Binary files a/bin/Icons/ui/R2.png and b/bin/Icons/ui/R2.png differ diff --git a/bin/Icons/ui/circle.png b/bin/Icons/ui/circle.png index 6ed88bb0a6..efe9078481 100644 Binary files a/bin/Icons/ui/circle.png and b/bin/Icons/ui/circle.png differ diff --git a/bin/Icons/ui/cross.png b/bin/Icons/ui/cross.png index f74e38379c..2a4a9855d5 100644 Binary files a/bin/Icons/ui/cross.png and b/bin/Icons/ui/cross.png differ diff --git a/bin/Icons/ui/dpad.png b/bin/Icons/ui/dpad.png index 5a31103da5..4752c94477 100644 Binary files a/bin/Icons/ui/dpad.png and b/bin/Icons/ui/dpad.png differ diff --git a/bin/Icons/ui/dpad_down.png b/bin/Icons/ui/dpad_down.png index dddcc0d479..ac3ed664c3 100644 Binary files a/bin/Icons/ui/dpad_down.png and b/bin/Icons/ui/dpad_down.png differ diff --git a/bin/Icons/ui/dpad_left.png b/bin/Icons/ui/dpad_left.png index 78beb6350c..4582cc1eea 100644 Binary files a/bin/Icons/ui/dpad_left.png and b/bin/Icons/ui/dpad_left.png differ diff --git a/bin/Icons/ui/dpad_right.png b/bin/Icons/ui/dpad_right.png index 94a6590b33..891f6ac4dd 100644 Binary files a/bin/Icons/ui/dpad_right.png and b/bin/Icons/ui/dpad_right.png differ diff --git a/bin/Icons/ui/dpad_up.png b/bin/Icons/ui/dpad_up.png index 5bc87051e6..f4f812c544 100644 Binary files a/bin/Icons/ui/dpad_up.png and b/bin/Icons/ui/dpad_up.png differ diff --git a/bin/Icons/ui/fade_bottom.png b/bin/Icons/ui/fade_bottom.png index dbb7d04b4c..9430ff5f52 100644 Binary files a/bin/Icons/ui/fade_bottom.png and b/bin/Icons/ui/fade_bottom.png differ diff --git a/bin/Icons/ui/fade_top.png b/bin/Icons/ui/fade_top.png index 661225ec8b..b55c895c68 100644 Binary files a/bin/Icons/ui/fade_top.png and b/bin/Icons/ui/fade_top.png differ diff --git a/bin/Icons/ui/home/256/bug-solid.png b/bin/Icons/ui/home/256/bug-solid.png index 2c88980603..f25f0cf696 100644 Binary files a/bin/Icons/ui/home/256/bug-solid.png and b/bin/Icons/ui/home/256/bug-solid.png differ diff --git a/bin/Icons/ui/home/256/circle-left-solid.png b/bin/Icons/ui/home/256/circle-left-solid.png index f6b2e58768..9176fd6854 100644 Binary files a/bin/Icons/ui/home/256/circle-left-solid.png and b/bin/Icons/ui/home/256/circle-left-solid.png differ diff --git a/bin/Icons/ui/home/256/display-solid.png b/bin/Icons/ui/home/256/display-solid.png index 84cc043b07..483c05992b 100644 Binary files a/bin/Icons/ui/home/256/display-solid.png and b/bin/Icons/ui/home/256/display-solid.png differ diff --git a/bin/Icons/ui/home/256/floppy-disk-solid.png b/bin/Icons/ui/home/256/floppy-disk-solid.png index 3701361569..4c0c7c0c4c 100644 Binary files a/bin/Icons/ui/home/256/floppy-disk-solid.png and b/bin/Icons/ui/home/256/floppy-disk-solid.png differ diff --git a/bin/Icons/ui/home/256/gamepad-solid.png b/bin/Icons/ui/home/256/gamepad-solid.png index fe08f1bc58..fbba1f473d 100644 Binary files a/bin/Icons/ui/home/256/gamepad-solid.png and b/bin/Icons/ui/home/256/gamepad-solid.png differ diff --git a/bin/Icons/ui/home/256/gauge-solid.png b/bin/Icons/ui/home/256/gauge-solid.png index 5b6d731a9d..afd4d6b87d 100644 Binary files a/bin/Icons/ui/home/256/gauge-solid.png and b/bin/Icons/ui/home/256/gauge-solid.png differ diff --git a/bin/Icons/ui/home/256/headphones-solid.png b/bin/Icons/ui/home/256/headphones-solid.png index feb015695d..9c43b48cc9 100644 Binary files a/bin/Icons/ui/home/256/headphones-solid.png and b/bin/Icons/ui/home/256/headphones-solid.png differ diff --git a/bin/Icons/ui/home/256/home.png b/bin/Icons/ui/home/256/home.png index 622984c913..b2879b3492 100644 Binary files a/bin/Icons/ui/home/256/home.png and b/bin/Icons/ui/home/256/home.png differ diff --git a/bin/Icons/ui/home/256/maximize-solid.png b/bin/Icons/ui/home/256/maximize-solid.png index 65eaa43308..45819d1b7b 100644 Binary files a/bin/Icons/ui/home/256/maximize-solid.png and b/bin/Icons/ui/home/256/maximize-solid.png differ diff --git a/bin/Icons/ui/home/256/play-button-arrowhead.png b/bin/Icons/ui/home/256/play-button-arrowhead.png index 37e9cefe93..10634c819e 100644 Binary files a/bin/Icons/ui/home/256/play-button-arrowhead.png and b/bin/Icons/ui/home/256/play-button-arrowhead.png differ diff --git a/bin/Icons/ui/home/256/power-off-solid.png b/bin/Icons/ui/home/256/power-off-solid.png index cf0ab44839..ea9ddaa9cf 100644 Binary files a/bin/Icons/ui/home/256/power-off-solid.png and b/bin/Icons/ui/home/256/power-off-solid.png differ diff --git a/bin/Icons/ui/home/256/rotate-left-solid.png b/bin/Icons/ui/home/256/rotate-left-solid.png index 5eaf29edba..284ea56547 100644 Binary files a/bin/Icons/ui/home/256/rotate-left-solid.png and b/bin/Icons/ui/home/256/rotate-left-solid.png differ diff --git a/bin/Icons/ui/home/256/screenshot.png b/bin/Icons/ui/home/256/screenshot.png index 091e2bbc2e..f897d99449 100644 Binary files a/bin/Icons/ui/home/256/screenshot.png and b/bin/Icons/ui/home/256/screenshot.png differ diff --git a/bin/Icons/ui/home/256/settings.png b/bin/Icons/ui/home/256/settings.png index d682b9b27c..ecc8fbf2d9 100644 Binary files a/bin/Icons/ui/home/256/settings.png and b/bin/Icons/ui/home/256/settings.png differ diff --git a/bin/Icons/ui/home/256/sliders-solid.png b/bin/Icons/ui/home/256/sliders-solid.png index cfb030721e..29d609a51c 100644 Binary files a/bin/Icons/ui/home/256/sliders-solid.png and b/bin/Icons/ui/home/256/sliders-solid.png differ diff --git a/bin/Icons/ui/home/256/trophy-solid.png b/bin/Icons/ui/home/256/trophy-solid.png index 1bd98a9ac9..66cf3c52b9 100644 Binary files a/bin/Icons/ui/home/256/trophy-solid.png and b/bin/Icons/ui/home/256/trophy-solid.png differ diff --git a/bin/Icons/ui/home/256/user-group-solid.png b/bin/Icons/ui/home/256/user-group-solid.png index a154fa5ba9..b157bf62ad 100644 Binary files a/bin/Icons/ui/home/256/user-group-solid.png and b/bin/Icons/ui/home/256/user-group-solid.png differ diff --git a/bin/Icons/ui/home/256/video-camera.png b/bin/Icons/ui/home/256/video-camera.png index 9f5460fba6..e15055e9a7 100644 Binary files a/bin/Icons/ui/home/256/video-camera.png and b/bin/Icons/ui/home/256/video-camera.png differ diff --git a/bin/Icons/ui/home/32/bug-solid.png b/bin/Icons/ui/home/32/bug-solid.png index 401fa0ea27..bce7ebd391 100644 Binary files a/bin/Icons/ui/home/32/bug-solid.png and b/bin/Icons/ui/home/32/bug-solid.png differ diff --git a/bin/Icons/ui/home/32/circle-left-solid.png b/bin/Icons/ui/home/32/circle-left-solid.png index 1acf201d63..917d012a06 100644 Binary files a/bin/Icons/ui/home/32/circle-left-solid.png and b/bin/Icons/ui/home/32/circle-left-solid.png differ diff --git a/bin/Icons/ui/home/32/display-solid.png b/bin/Icons/ui/home/32/display-solid.png index 8a48c89494..7be58625a5 100644 Binary files a/bin/Icons/ui/home/32/display-solid.png and b/bin/Icons/ui/home/32/display-solid.png differ diff --git a/bin/Icons/ui/home/32/floppy-disk-solid.png b/bin/Icons/ui/home/32/floppy-disk-solid.png index 836c6523c3..2616eab996 100644 Binary files a/bin/Icons/ui/home/32/floppy-disk-solid.png and b/bin/Icons/ui/home/32/floppy-disk-solid.png differ diff --git a/bin/Icons/ui/home/32/gamepad-solid.png b/bin/Icons/ui/home/32/gamepad-solid.png index b783fee792..da1db84526 100644 Binary files a/bin/Icons/ui/home/32/gamepad-solid.png and b/bin/Icons/ui/home/32/gamepad-solid.png differ diff --git a/bin/Icons/ui/home/32/gauge-solid.png b/bin/Icons/ui/home/32/gauge-solid.png index 9853811ae2..156b1085c1 100644 Binary files a/bin/Icons/ui/home/32/gauge-solid.png and b/bin/Icons/ui/home/32/gauge-solid.png differ diff --git a/bin/Icons/ui/home/32/headphones-solid.png b/bin/Icons/ui/home/32/headphones-solid.png index e2cfabb07c..996ed79fdb 100644 Binary files a/bin/Icons/ui/home/32/headphones-solid.png and b/bin/Icons/ui/home/32/headphones-solid.png differ diff --git a/bin/Icons/ui/home/32/home.png b/bin/Icons/ui/home/32/home.png index 2ec05eab24..e40425cf5c 100644 Binary files a/bin/Icons/ui/home/32/home.png and b/bin/Icons/ui/home/32/home.png differ diff --git a/bin/Icons/ui/home/32/maximize-solid.png b/bin/Icons/ui/home/32/maximize-solid.png index 7a0a9e4fb6..42763ebf40 100644 Binary files a/bin/Icons/ui/home/32/maximize-solid.png and b/bin/Icons/ui/home/32/maximize-solid.png differ diff --git a/bin/Icons/ui/home/32/play-button-arrowhead.png b/bin/Icons/ui/home/32/play-button-arrowhead.png index 6ce5505f4d..c99e11a516 100644 Binary files a/bin/Icons/ui/home/32/play-button-arrowhead.png and b/bin/Icons/ui/home/32/play-button-arrowhead.png differ diff --git a/bin/Icons/ui/home/32/power-off-solid.png b/bin/Icons/ui/home/32/power-off-solid.png index 21ebad8e69..59a914ed77 100644 Binary files a/bin/Icons/ui/home/32/power-off-solid.png and b/bin/Icons/ui/home/32/power-off-solid.png differ diff --git a/bin/Icons/ui/home/32/rotate-left-solid.png b/bin/Icons/ui/home/32/rotate-left-solid.png index b466d28a8e..d637819fe9 100644 Binary files a/bin/Icons/ui/home/32/rotate-left-solid.png and b/bin/Icons/ui/home/32/rotate-left-solid.png differ diff --git a/bin/Icons/ui/home/32/screenshot.png b/bin/Icons/ui/home/32/screenshot.png index 97255279b4..55ced711ba 100644 Binary files a/bin/Icons/ui/home/32/screenshot.png and b/bin/Icons/ui/home/32/screenshot.png differ diff --git a/bin/Icons/ui/home/32/settings.png b/bin/Icons/ui/home/32/settings.png index eeaeceef7d..c049ef1b75 100644 Binary files a/bin/Icons/ui/home/32/settings.png and b/bin/Icons/ui/home/32/settings.png differ diff --git a/bin/Icons/ui/home/32/sliders-solid.png b/bin/Icons/ui/home/32/sliders-solid.png index ca4c75df23..3b0c61ede5 100644 Binary files a/bin/Icons/ui/home/32/sliders-solid.png and b/bin/Icons/ui/home/32/sliders-solid.png differ diff --git a/bin/Icons/ui/home/32/trophy-solid.png b/bin/Icons/ui/home/32/trophy-solid.png index 9d94e14b97..fde5c76ef0 100644 Binary files a/bin/Icons/ui/home/32/trophy-solid.png and b/bin/Icons/ui/home/32/trophy-solid.png differ diff --git a/bin/Icons/ui/home/32/user-group-solid.png b/bin/Icons/ui/home/32/user-group-solid.png index 114c45599d..989f7a8e5f 100644 Binary files a/bin/Icons/ui/home/32/user-group-solid.png and b/bin/Icons/ui/home/32/user-group-solid.png differ diff --git a/bin/Icons/ui/home/32/video-camera.png b/bin/Icons/ui/home/32/video-camera.png index d8fa8dfaaf..976ae49ca0 100644 Binary files a/bin/Icons/ui/home/32/video-camera.png and b/bin/Icons/ui/home/32/video-camera.png differ diff --git a/bin/Icons/ui/left_stick.png b/bin/Icons/ui/left_stick.png index 101b99cb17..d4302c58e1 100644 Binary files a/bin/Icons/ui/left_stick.png and b/bin/Icons/ui/left_stick.png differ diff --git a/bin/Icons/ui/loading.png b/bin/Icons/ui/loading.png index 33288035c7..a6a3a59768 100644 Binary files a/bin/Icons/ui/loading.png and b/bin/Icons/ui/loading.png differ diff --git a/bin/Icons/ui/new.png b/bin/Icons/ui/new.png index ac21f0e9e6..ee3273b2e2 100644 Binary files a/bin/Icons/ui/new.png and b/bin/Icons/ui/new.png differ diff --git a/bin/Icons/ui/right_stick.png b/bin/Icons/ui/right_stick.png index 374f589bf9..d0e32caef4 100644 Binary files a/bin/Icons/ui/right_stick.png and b/bin/Icons/ui/right_stick.png differ diff --git a/bin/Icons/ui/save.png b/bin/Icons/ui/save.png index c1683f7223..a0c216a326 100644 Binary files a/bin/Icons/ui/save.png and b/bin/Icons/ui/save.png differ diff --git a/bin/Icons/ui/select.png b/bin/Icons/ui/select.png index b493aff682..fb20526566 100644 Binary files a/bin/Icons/ui/select.png and b/bin/Icons/ui/select.png differ diff --git a/bin/Icons/ui/spinner-24.png b/bin/Icons/ui/spinner-24.png index 088b5d180a..6239bc9dee 100644 Binary files a/bin/Icons/ui/spinner-24.png and b/bin/Icons/ui/spinner-24.png differ diff --git a/bin/Icons/ui/square.png b/bin/Icons/ui/square.png index 0be20e14c9..09af27a6ac 100644 Binary files a/bin/Icons/ui/square.png and b/bin/Icons/ui/square.png differ diff --git a/bin/Icons/ui/start.png b/bin/Icons/ui/start.png index 3822fd85ff..38d7927467 100644 Binary files a/bin/Icons/ui/start.png and b/bin/Icons/ui/start.png differ diff --git a/bin/Icons/ui/triangle.png b/bin/Icons/ui/triangle.png index 50ba8572d8..cf1037d906 100644 Binary files a/bin/Icons/ui/triangle.png and b/bin/Icons/ui/triangle.png differ diff --git a/rpcs3.sln b/rpcs3.sln index 4cb0830474..748946d239 100644 --- a/rpcs3.sln +++ b/rpcs3.sln @@ -117,6 +117,13 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "rpcs3_test", "rpcs3\tests\r {70CD65B0-91D6-4FAE-9A7B-4AF55D0D1B12} = {70CD65B0-91D6-4FAE-9A7B-4AF55D0D1B12} EndProjectSection EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "pugixml", "pugixml", "{F520F4A6-27D1-4DD2-9BB2-E0625826D0A3}" + ProjectSection(SolutionItems) = preProject + 3rdparty\pugixml\src\pugiconfig.hpp = 3rdparty\pugixml\src\pugiconfig.hpp + 3rdparty\pugixml\src\pugixml.cpp = 3rdparty\pugixml\src\pugixml.cpp + 3rdparty\pugixml\src\pugixml.hpp = 3rdparty\pugixml\src\pugixml.hpp + EndProjectSection +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|x64 = Debug|x64 @@ -274,6 +281,7 @@ Global {8846A9AA-5539-4C91-8301-F54260E1A07A} = {6C3B64A0-8F8A-4DC4-8C0B-D71EBEED7FA8} {3C67A2FF-4710-402A-BE3E-31B0CB0576DF} = {6C3B64A0-8F8A-4DC4-8C0B-D71EBEED7FA8} {D1CBF84E-07F8-4ACB-9CD2-BD205FDEEE1E} = {02EA681E-C7D8-13C7-8484-4AC65E1B71E8} + {F520F4A6-27D1-4DD2-9BB2-E0625826D0A3} = {6C3B64A0-8F8A-4DC4-8C0B-D71EBEED7FA8} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {06CC7920-E085-4B81-9582-8DE8AAD42510} diff --git a/rpcs3/CMakeLists.txt b/rpcs3/CMakeLists.txt index 6178f9c7d1..2fa497467c 100644 --- a/rpcs3/CMakeLists.txt +++ b/rpcs3/CMakeLists.txt @@ -192,6 +192,7 @@ if(BUILD_RPCS3_TESTS) tests/test_rsx_cfg.cpp tests/test_rsx_fp_asm.cpp tests/test_dmux_pamf.cpp + tests/test_spu_analyser.cpp ) target_link_libraries(rpcs3_test diff --git a/rpcs3/Crypto/key_vault.cpp b/rpcs3/Crypto/key_vault.cpp index 6fa9aa0684..92d308e9f8 100644 --- a/rpcs3/Crypto/key_vault.cpp +++ b/rpcs3/Crypto/key_vault.cpp @@ -5,7 +5,7 @@ LOG_CHANNEL(key_vault_log, "KEY_VAULT"); -SELF_KEY::SELF_KEY(u64 ver_start, u64 ver_end, u16 rev, u32 type, const std::string& e, const std::string& r, const std::string& pb, const std::string& pr, u32 ct) +SELF_KEY::SELF_KEY(u64 ver_start, u64 ver_end, u16 rev, u32 type, std::string_view e, std::string_view r, std::string_view pb, std::string_view pr, u32 ct) { version_start = ver_start; version_end = ver_end; diff --git a/rpcs3/Crypto/key_vault.h b/rpcs3/Crypto/key_vault.h index 4dff84992b..36344defe2 100644 --- a/rpcs3/Crypto/key_vault.h +++ b/rpcs3/Crypto/key_vault.h @@ -30,7 +30,7 @@ struct SELF_KEY u8 priv[0x15]{}; u32 curve_type{}; - SELF_KEY(u64 ver_start, u64 ver_end, u16 rev, u32 type, const std::string& e, const std::string& r, const std::string& pb, const std::string& pr, u32 ct); + SELF_KEY(u64 ver_start, u64 ver_end, u16 rev, u32 type, std::string_view e, std::string_view r, std::string_view pb, std::string_view pr, u32 ct); }; constexpr u32 PASSPHRASE_KEY_LEN = 16; diff --git a/rpcs3/Crypto/unedat.cpp b/rpcs3/Crypto/unedat.cpp index 1ffaa33fc3..6fea6c1859 100644 --- a/rpcs3/Crypto/unedat.cpp +++ b/rpcs3/Crypto/unedat.cpp @@ -679,7 +679,7 @@ u128 GetEdatRifKeyFromRapFile(const fs::file& rap_file) return rifkey; } -bool VerifyEDATHeaderWithKLicense(const fs::file& input, const std::string& input_file_name, const u8* custom_klic, NPD_HEADER* npd_out) +bool VerifyEDATHeaderWithKLicense(const fs::file& input, std::string_view input_file_name, const u8* custom_klic, NPD_HEADER* npd_out) { // Setup NPD and EDAT/SDAT structs. NPD_HEADER NPD; @@ -702,7 +702,7 @@ bool VerifyEDATHeaderWithKLicense(const fs::file& input, const std::string& inpu // Perform header validation (EDAT only). char real_file_name[CRYPTO_MAX_PATH]{}; - extract_file_name(input_file_name.c_str(), real_file_name); + extract_file_name(input_file_name, real_file_name); if (!validate_npd_hashes(real_file_name, custom_klic, &NPD, &EDAT, false)) { edat_log.error("NPD hash validation failed!"); @@ -810,7 +810,7 @@ bool EDATADecrypter::ReadHeader() { // extract key from RIF char real_file_name[CRYPTO_MAX_PATH]{}; - extract_file_name(m_file_name.c_str(), real_file_name); + extract_file_name(m_file_name, real_file_name); if (!validate_npd_hashes(real_file_name, reinterpret_cast(&dec_key), &npdHeader, &edatHeader, false)) { diff --git a/rpcs3/Crypto/unedat.h b/rpcs3/Crypto/unedat.h index 31fce60d4e..cc91509a5e 100644 --- a/rpcs3/Crypto/unedat.h +++ b/rpcs3/Crypto/unedat.h @@ -62,7 +62,7 @@ struct EDAT_HEADER extern fs::file DecryptEDAT(const fs::file& input, const std::string& input_file_name, int mode, u8 *custom_klic); extern void read_npd_edat_header(const fs::file* input, NPD_HEADER& NPD, EDAT_HEADER& EDAT); -extern bool VerifyEDATHeaderWithKLicense(const fs::file& input, const std::string& input_file_name, const u8* custom_klic, NPD_HEADER *npd_out = nullptr); +extern bool VerifyEDATHeaderWithKLicense(const fs::file& input, std::string_view input_file_name, const u8* custom_klic, NPD_HEADER *npd_out = nullptr); u128 GetEdatRifKeyFromRapFile(const fs::file& rap_file); diff --git a/rpcs3/Crypto/utils.cpp b/rpcs3/Crypto/utils.cpp index 7c1d1df309..7913221380 100644 --- a/rpcs3/Crypto/utils.cpp +++ b/rpcs3/Crypto/utils.cpp @@ -24,13 +24,13 @@ // Auxiliary functions (endian swap, xor). // Bytes conversion auxiliary function. -void bytes_to_hex(std::string& hex_str, const unsigned char* data, unsigned int data_length) +void bytes_to_hex(std::string& hex_str, const unsigned char* data, usz data_length) { - size_t str_length = data_length * 2; + const usz str_length = data_length * 2; hex_str.resize(str_length); - for (size_t i = 0; i < str_length; i += 2) + for (usz i = 0; i < str_length; i += 2) { const auto [ptr, err] = std::to_chars(hex_str.data() + i, hex_str.data() + i + 2, *data++, 16); if (err != std::errc()) @@ -49,19 +49,28 @@ void bytes_to_hex(std::string& hex_str, const unsigned char* data, unsigned int } // Hex string conversion auxiliary function. -void hex_to_bytes(unsigned char* data, std::string_view hex_str, unsigned int str_length) +void hex_to_bytes(unsigned char* data, std::string_view hex_str, usz str_length, std::string* error) { - const auto strn_length = (str_length > 0) ? str_length : hex_str.size(); + const usz strn_length = (str_length > 0) ? str_length : hex_str.size(); // Don't convert if the string length is odd. if ((strn_length % 2) == 0) { - for (size_t i = 0; i < strn_length; i += 2) + for (usz i = 0; i < strn_length; i += 2) { const auto [ptr, err] = std::from_chars(hex_str.data() + i, hex_str.data() + i + 2, *data++, 16); if (err != std::errc()) { - fmt::throw_exception("Failed to read hex string: %s", std::make_error_code(err).message()); + std::string msg = fmt::format("Failed to read hex string: %s (hex='%s')", std::make_error_code(err).message(), hex_str); + + if (error) + { + *error = std::move(msg); + } + else + { + fmt::throw_exception("%s", msg); + } } } } @@ -127,17 +136,15 @@ void cmac_hash_forge(unsigned char *key, int /*key_len*/, unsigned char *in, usz aes_cmac(&ctx, in_len, in, hash); } -char* extract_file_name(const char* file_path, char real_file_name[CRYPTO_MAX_PATH]) +char* extract_file_name(std::string_view file_path, char real_file_name[CRYPTO_MAX_PATH]) { - std::string_view v(file_path); - - if (const auto pos = v.find_last_of(fs::delim); pos != umax) + if (const auto pos = file_path.find_last_of(fs::delim); pos != umax) { - v.remove_prefix(pos + 1); + file_path.remove_prefix(pos + 1); } std::span r(real_file_name, CRYPTO_MAX_PATH); - strcpy_trunc(r, v); + strcpy_trunc(r, file_path); return real_file_name; } diff --git a/rpcs3/Crypto/utils.h b/rpcs3/Crypto/utils.h index 6e8dab9734..780226d3b9 100644 --- a/rpcs3/Crypto/utils.h +++ b/rpcs3/Crypto/utils.h @@ -11,15 +11,15 @@ enum { CRYPTO_MAX_PATH = 4096 }; -char* extract_file_name(const char* file_path, char real_file_name[CRYPTO_MAX_PATH]); +char* extract_file_name(std::string_view file_path, char real_file_name[CRYPTO_MAX_PATH]); std::string sha256_get_hash(const char* data, usz size, bool lower_case); // Bytes conversion auxiliary function. -void bytes_to_hex(std::string& hex_str, const unsigned char* data, unsigned int data_length); +void bytes_to_hex(std::string& hex_str, const unsigned char* data, usz data_length); // Hex string conversion auxiliary function. -void hex_to_bytes(unsigned char* data, std::string_view hex_str, unsigned int str_length); +void hex_to_bytes(unsigned char* data, std::string_view hex_str, usz str_length, std::string* error = nullptr); // Crypto functions (AES128-CBC, AES128-ECB, SHA1-HMAC and AES-CMAC). void aescbc128_decrypt(unsigned char *key, unsigned char *iv, unsigned char *in, unsigned char *out, usz len); diff --git a/rpcs3/Emu/CMakeLists.txt b/rpcs3/Emu/CMakeLists.txt index a0322da814..045fa30769 100644 --- a/rpcs3/Emu/CMakeLists.txt +++ b/rpcs3/Emu/CMakeLists.txt @@ -128,7 +128,7 @@ target_sources(rpcs3_emu PRIVATE ../Loader/TAR.cpp ../Loader/ISO.cpp ../Loader/iso_cache.cpp - ../Loader/iso_validation.cpp + ../Loader/content_validation.cpp ../Loader/TROPUSR.cpp ../Loader/TRP.cpp ) @@ -588,6 +588,7 @@ if(NOT ANDROID AND NOT APPLE) RSX/GL/glutils/buffer_object.cpp RSX/GL/glutils/capabilities.cpp RSX/GL/glutils/common.cpp + RSX/GL/glutils/ex.cpp RSX/GL/glutils/fbo.cpp RSX/GL/glutils/image.cpp RSX/GL/glutils/program.cpp diff --git a/rpcs3/Emu/CPU/Backends/AArch64/AArch64ASM.cpp b/rpcs3/Emu/CPU/Backends/AArch64/AArch64ASM.cpp index 4c8a01e0e2..5ca0e77e95 100644 --- a/rpcs3/Emu/CPU/Backends/AArch64/AArch64ASM.cpp +++ b/rpcs3/Emu/CPU/Backends/AArch64/AArch64ASM.cpp @@ -46,26 +46,26 @@ namespace aarch64 { compiled_instruction_t i{}; i.asm_ = inst; - m_instructions.push_back(i); + m_instructions.push_back(std::move(i)); } void UASM::emit1(const char* inst, const Arg& arg0, const std::vector& clobbered) { int arg_id = 0; - fmt_replacement_list_t repl = { + const fmt_replacement_list_t repl = { { "{0}", arg0.to_string(&arg_id) } }; compiled_instruction_t i{}; i.asm_ = fmt::replace_all(inst, repl); embed_args(i, { arg0 }, clobbered); - m_instructions.push_back(i); + m_instructions.push_back(std::move(i)); } void UASM::emit2(const char* inst, const Arg& arg0, const Arg& arg1, const std::vector& clobbered) { int arg_id = 0; - fmt_replacement_list_t repl = { + const fmt_replacement_list_t repl = { { "{0}", arg0.to_string(&arg_id) }, { "{1}", arg1.to_string(&arg_id) }, }; @@ -73,13 +73,13 @@ namespace aarch64 compiled_instruction_t i{}; i.asm_ = fmt::replace_all(inst, repl); embed_args(i, { arg0, arg1 }, clobbered); - m_instructions.push_back(i); + m_instructions.push_back(std::move(i)); } void UASM::emit3(const char* inst, const Arg& arg0, const Arg& arg1, const Arg& arg2, const std::vector& clobbered) { int arg_id = 0; - fmt_replacement_list_t repl = { + const fmt_replacement_list_t repl = { { "{0}", arg0.to_string(&arg_id) }, { "{1}", arg1.to_string(&arg_id) }, { "{2}", arg2.to_string(&arg_id) }, @@ -88,13 +88,13 @@ namespace aarch64 compiled_instruction_t i{}; i.asm_ = fmt::replace_all(inst, repl); embed_args(i, { arg0, arg1, arg2 }, clobbered); - m_instructions.push_back(i); + m_instructions.push_back(std::move(i)); } void UASM::emit4(const char* inst, const Arg& arg0, const Arg& arg1, const Arg& arg2, const Arg& arg3, const std::vector& clobbered) { int arg_id = 0; - fmt_replacement_list_t repl = { + const fmt_replacement_list_t repl = { { "{0}", arg0.to_string(&arg_id) }, { "{1}", arg1.to_string(&arg_id) }, { "{2}", arg2.to_string(&arg_id) }, @@ -104,14 +104,14 @@ namespace aarch64 compiled_instruction_t i{}; i.asm_ = fmt::replace_all(inst, repl); embed_args(i, { arg0, arg1, arg2, arg3 }, clobbered); - m_instructions.push_back(i); + m_instructions.push_back(std::move(i)); } void UASM::insert(llvm::IRBuilder<>* irb, llvm::LLVMContext& ctx) const { for (const auto& inst : m_instructions) { - auto constraints = fmt::merge(inst.constraints, ","); + const auto constraints = fmt::merge(inst.constraints, ","); llvm_asm(irb, inst.asm_, inst.args, constraints, ctx); } } diff --git a/rpcs3/Emu/CPU/Backends/AArch64/AArch64JIT.cpp b/rpcs3/Emu/CPU/Backends/AArch64/AArch64JIT.cpp index 91aedffd85..2fda215664 100644 --- a/rpcs3/Emu/CPU/Backends/AArch64/AArch64JIT.cpp +++ b/rpcs3/Emu/CPU/Backends/AArch64/AArch64JIT.cpp @@ -219,7 +219,7 @@ namespace aarch64 return result; } - gpr GHC_frame_preservation_pass::get_base_register_for_call(const std::string& callee_name, gpr default_reg) + gpr GHC_frame_preservation_pass::get_base_register_for_call(std::string_view callee_name, gpr default_reg) { // We go over the base_register_lookup table and find the first matching pattern for (const auto& pattern : m_config.base_register_lookup) diff --git a/rpcs3/Emu/CPU/Backends/AArch64/AArch64JIT.h b/rpcs3/Emu/CPU/Backends/AArch64/AArch64JIT.h index 701fe85be9..f5a616e32c 100644 --- a/rpcs3/Emu/CPU/Backends/AArch64/AArch64JIT.h +++ b/rpcs3/Emu/CPU/Backends/AArch64/AArch64JIT.h @@ -45,7 +45,7 @@ namespace aarch64 bool use_stack_frames = true; // Allocate a stack frame for each function. The gateway can alternatively manage a global stack to use as scratch. bool optimize = true; // Optimize instructions when possible. Set to false when debugging. u32 hypervisor_context_offset = 0; // Offset within the "thread" object where we can find the hypervisor context (registers configured at gateway). - std::function exclusion_callback; // [Optional] Callback run on each function before transform. Return "true" to exclude from frame processing. + std::function exclusion_callback; // [Optional] Callback run on each function before transform. Return "true" to exclude from frame processing. std::vector> base_register_lookup; // [Optional] Function lookup table to determine the location of the "thread" context. std::vector faux_function_list; // [Optional] List of faux block names to treat as untrusted - typically fake functions representing codecaves. }; @@ -67,7 +67,7 @@ namespace aarch64 bool is_faux_function(const std::string& function_name); - gpr get_base_register_for_call(const std::string& callee_name, gpr default_reg = gpr::x19); + gpr get_base_register_for_call(std::string_view callee_name, gpr default_reg = gpr::x19); void process_leaf_function(llvm::IRBuilder<>* irb, llvm::Function& f); diff --git a/rpcs3/Emu/CPU/CPUTranslator.h b/rpcs3/Emu/CPU/CPUTranslator.h index f7a97fe493..4cb3aca770 100644 --- a/rpcs3/Emu/CPU/CPUTranslator.h +++ b/rpcs3/Emu/CPU/CPUTranslator.h @@ -3781,6 +3781,23 @@ public: #endif } + template + value_t vperm2d128From512(T1 a, T2 b, T3 c) + { + value_t result; + value_t perm512; + + const auto data0 = a.eval(m_ir); + const auto index128 = b.eval(m_ir); + const auto data1 = c.eval(m_ir); + + const auto index512 = m_ir->CreateInsertVector(get_type(), llvm::UndefValue::get(get_type()), index128, m_ir->getInt64(0)); + perm512.value = m_ir->CreateCall(get_intrinsic(llvm::Intrinsic::x86_avx512_vpermi2var_d_512), {data0, index512, data1}); + + result.value = m_ir->CreateExtractVector(get_type(), perm512.value, m_ir->getInt64(0)); + return result; + } + template value_t gf2p8affineqb(T1 a, T2 b, u8 c) { diff --git a/rpcs3/Emu/Cell/Modules/cellAudio.cpp b/rpcs3/Emu/Cell/Modules/cellAudio.cpp index b28809ef03..557c7ef13b 100644 --- a/rpcs3/Emu/Cell/Modules/cellAudio.cpp +++ b/rpcs3/Emu/Cell/Modules/cellAudio.cpp @@ -1019,6 +1019,13 @@ void cell_audio_thread::operator()() // Destroy ringbuffer ringbuffer.reset(); + + // Destroy the audio backend on this thread (the one that created it in cfg.reset()). + // The backend's ctor calls CoInitializeEx here on Windows; releasing it on a different + // thread (g_fxo->clear() runs on the GUI thread during Kill()) would land the matching + // CoUninitialize on the GUI thread, draining its OLE reference and silently breaking the + // main window's file drag&drop. Keep COM init/teardown balanced on this thread. + cfg.backend.reset(); } audio_port* cell_audio_thread::open_port() @@ -1662,36 +1669,35 @@ error_code AudioSetNotifyEventQueue(ppu_thread& ppu, u64 key, u32 iFlags) lv2_sleep(20, &ppu); // Dirty hack for sound: confirm the creation of _mxr000 event queue by _cellsurMixerMain thread - constexpr u64 c_mxr000 = 0x8000cafe0246030; + constexpr u64 c_mxr000 = 0x8000cafe02460300; if (key == c_mxr000 || key == 0) { - bool has_sur_mixer_thread = false; - - for (usz count = 0; !lv2_event_queue::find(c_mxr000) && count < 100; count++) + const bool has_sur_mixer_thread = idm::select>([&](u32 id, named_thread& test_ppu) { - if (has_sur_mixer_thread || idm::select>([&](u32 id, named_thread& test_ppu) + // Confirm thread existence + if (id == ppu.id) { - // Confirm thread existence - if (id == ppu.id) - { - return false; - } - - const auto ptr = test_ppu.ppu_tname.load(); - - if (!ptr) - { - return false; - } - - return *ptr == "_cellsurMixerMain"sv; - }).ret) - { - has_sur_mixer_thread = true; + return false; } - else + + const auto ptr = test_ppu.ppu_tname.load(); + + if (!ptr) { + return false; + } + + return *ptr == "_cellsurMixerMain"sv; + }).ret; + + bool was_mxr000_queue_found = false; + + for (usz count = 0; has_sur_mixer_thread && count < 100; count++) + { + if (lv2_event_queue::find(c_mxr000)) + { + was_mxr000_queue_found = true; break; } @@ -1701,13 +1707,14 @@ error_code AudioSetNotifyEventQueue(ppu_thread& ppu, u64 key, u32 iFlags) return {}; } - cellAudio.error("AudioSetNotifyEventQueue(): Waiting for _mxr000. x%d", count); + (count < 3 ? cellAudio.warning : cellAudio.error)("AudioSetNotifyEventQueue(): Waiting for _mxr000. x%d", count); lv2_sleep(50'000, &ppu); } - if (has_sur_mixer_thread && lv2_event_queue::find(c_mxr000)) + if (key == 0 && was_mxr000_queue_found) { + // Correct key value argument key = c_mxr000; } } diff --git a/rpcs3/Emu/Cell/Modules/cellMusicExport.cpp b/rpcs3/Emu/Cell/Modules/cellMusicExport.cpp index c01d4b7a51..0d90ab9afd 100644 --- a/rpcs3/Emu/Cell/Modules/cellMusicExport.cpp +++ b/rpcs3/Emu/Cell/Modules/cellMusicExport.cpp @@ -72,7 +72,7 @@ struct music_export }; -bool check_music_path(const std::string& file_path) +bool check_music_path(std::string_view file_path) { if (file_path.size() >= CELL_MUSIC_EXPORT_UTIL_HDD_PATH_MAX) { diff --git a/rpcs3/Emu/Cell/Modules/cellPhotoExport.cpp b/rpcs3/Emu/Cell/Modules/cellPhotoExport.cpp index 473bd435e7..541879153f 100644 --- a/rpcs3/Emu/Cell/Modules/cellPhotoExport.cpp +++ b/rpcs3/Emu/Cell/Modules/cellPhotoExport.cpp @@ -74,7 +74,7 @@ struct photo_export }; -bool check_photo_path(const std::string& file_path) +bool check_photo_path(std::string_view file_path) { if (file_path.size() >= CELL_PHOTO_EXPORT_UTIL_HDD_PATH_MAX) { diff --git a/rpcs3/Emu/Cell/Modules/cellSaveData.cpp b/rpcs3/Emu/Cell/Modules/cellSaveData.cpp index 7878e86642..22086bdf5c 100644 --- a/rpcs3/Emu/Cell/Modules/cellSaveData.cpp +++ b/rpcs3/Emu/Cell/Modules/cellSaveData.cpp @@ -1155,15 +1155,16 @@ static NEVER_INLINE error_code savedata_op(ppu_thread& ppu, u32 operation, u32 v } } - auto delete_save = [&]() + const auto delete_save = [&]() { - strcpy_trunc(doneGet->dirName, save_entries[selected].dirName); + const SaveDataEntry& entry = ::at32(save_entries, selected); + strcpy_trunc(doneGet->dirName, entry.dirName); doneGet->hddFreeSizeKB = 40 * 1024 * 1024 - 256; // Read explanation in cellHddGameCheck doneGet->excResult = CELL_OK; std::memset(doneGet->reserved, 0, sizeof(doneGet->reserved)); - const std::string old_path = base_dir + ".backup_" + save_entries[selected].escaped + "/"; - const std::string del_path = base_dir + save_entries[selected].escaped + "/"; + const std::string old_path = base_dir + ".backup_" + entry.escaped + "/"; + const std::string del_path = base_dir + entry.escaped + "/"; const fs::dir _dir(del_path); u64 size_bytes = 0; @@ -1456,7 +1457,7 @@ static NEVER_INLINE error_code savedata_op(ppu_thread& ppu, u32 operation, u32 v } else { - fmt::throw_exception("Invalid savedata selected"); + fmt::throw_exception("Invalid savedata selected (selected=%d)", selected); } } } diff --git a/rpcs3/Emu/Cell/Modules/cellSsl.cpp b/rpcs3/Emu/Cell/Modules/cellSsl.cpp index 7de808bd25..e515c2e69b 100644 --- a/rpcs3/Emu/Cell/Modules/cellSsl.cpp +++ b/rpcs3/Emu/Cell/Modules/cellSsl.cpp @@ -75,7 +75,7 @@ error_code cellSslGetMemoryInfo() return CELL_OK; } -std::string getCert(const std::string& certPath, const int certID, const bool isNormalCert) +std::string getCert(std::string_view certPath, const int certID, const bool isNormalCert) { int newID = certID; diff --git a/rpcs3/Emu/Cell/Modules/cellVideoExport.cpp b/rpcs3/Emu/Cell/Modules/cellVideoExport.cpp index 8c0ed93771..30ede6e7a3 100644 --- a/rpcs3/Emu/Cell/Modules/cellVideoExport.cpp +++ b/rpcs3/Emu/Cell/Modules/cellVideoExport.cpp @@ -76,7 +76,7 @@ struct video_export }; -bool check_movie_path(const std::string& file_path) +bool check_movie_path(std::string_view file_path) { if (file_path.size() >= CELL_VIDEO_EXPORT_UTIL_HDD_PATH_MAX) { diff --git a/rpcs3/Emu/Cell/Modules/libmixer.cpp b/rpcs3/Emu/Cell/Modules/libmixer.cpp index e92b5c6cf4..74b05b8d0b 100644 --- a/rpcs3/Emu/Cell/Modules/libmixer.cpp +++ b/rpcs3/Emu/Cell/Modules/libmixer.cpp @@ -35,49 +35,49 @@ struct SurMixerConfig { std::mutex mutex; - u32 audio_port; - s32 priority; - u32 ch_strips_1; - u32 ch_strips_2; - u32 ch_strips_6; - u32 ch_strips_8; + u32 audio_port = 0; + s32 priority = 0; + u32 ch_strips_1 = 0; + u32 ch_strips_2 = 0; + u32 ch_strips_6 = 0; + u32 ch_strips_8 = 0; - vm::ptr cb; - vm::ptr cb_arg; + vm::ptr cb {}; + vm::ptr cb_arg {}; - f32 mixdata[8 * 256]; - u64 mixcount; + f32 mixdata[8 * 256] {}; + u64 mixcount = 0; }; struct SSPlayer { - bool m_created; // SSPlayerCreate/Remove - bool m_connected; // AANConnect/Disconnect - bool m_active; // SSPlayerPlay/Stop - u32 m_channels; // 1 or 2 - u32 m_addr; - u32 m_samples; - u32 m_loop_start; - u32 m_loop_mode; - u32 m_position; - float m_level; - float m_speed; - float m_x; - float m_y; - float m_z; + bool m_created = false; // SSPlayerCreate/Remove + bool m_connected = false; // AANConnect/Disconnect + bool m_active = false; // SSPlayerPlay/Stop + u32 m_channels = 0; // 1 or 2 + u32 m_addr = 0; + u32 m_samples = 0; + u32 m_loop_start = 0; + u32 m_loop_mode = 0; + u32 m_position = 0; + f32 m_level = 0.0f; + f32 m_speed = 0.0f; + f32 m_x = 0.0f; + f32 m_y = 0.0f; + f32 m_z = 0.0f; }; // TODO: use fxm -SurMixerConfig g_surmx; +SurMixerConfig g_surmx {}; std::vector g_ssp; -s32 cellAANAddData(u32 aan_handle, u32 aan_port, u32 offset, vm::ptr addr, u32 samples) +s32 cellAANAddData(u32 aan_handle, u32 aan_port, u32 offset, vm::ptr addr, u32 samples) { libmixer.trace("cellAANAddData(aan_handle=0x%x, aan_port=0x%x, offset=0x%x, addr=*0x%x, samples=%d)", aan_handle, aan_port, offset, addr, samples); u32 type = aan_port >> 16; - u32 port = aan_port & 0xffff; + const u32 port = aan_port & 0xffff; switch (type) { @@ -94,7 +94,8 @@ s32 cellAANAddData(u32 aan_handle, u32 aan_port, u32 offset, vm::ptr addr if (port >= g_surmx.ch_strips_8) type = 0; break; default: - type = 0; break; + type = 0; + break; } if (aan_handle != 0x11111111 || samples != 256 || !type || offset != 0) @@ -110,7 +111,7 @@ s32 cellAANAddData(u32 aan_handle, u32 aan_port, u32 offset, vm::ptr addr // mono upmixing for (u32 i = 0; i < samples; i++) { - const float center = addr[i]; + const f32 center = addr[i]; g_surmx.mixdata[i * 8 + 0] += center; g_surmx.mixdata[i * 8 + 1] += center; } @@ -120,8 +121,8 @@ s32 cellAANAddData(u32 aan_handle, u32 aan_port, u32 offset, vm::ptr addr // stereo upmixing for (u32 i = 0; i < samples; i++) { - const float left = addr[i * 2 + 0]; - const float right = addr[i * 2 + 1]; + const f32 left = addr[i * 2 + 0]; + const f32 right = addr[i * 2 + 1]; g_surmx.mixdata[i * 8 + 0] += left; g_surmx.mixdata[i * 8 + 1] += right; } @@ -131,12 +132,12 @@ s32 cellAANAddData(u32 aan_handle, u32 aan_port, u32 offset, vm::ptr addr // 5.1 upmixing for (u32 i = 0; i < samples; i++) { - const float left = addr[i * 6 + 0]; - const float right = addr[i * 6 + 1]; - const float center = addr[i * 6 + 2]; - const float low_freq = addr[i * 6 + 3]; - const float rear_left = addr[i * 6 + 4]; - const float rear_right = addr[i * 6 + 5]; + const f32 left = addr[i * 6 + 0]; + const f32 right = addr[i * 6 + 1]; + const f32 center = addr[i * 6 + 2]; + const f32 low_freq = addr[i * 6 + 3]; + const f32 rear_left = addr[i * 6 + 4]; + const f32 rear_right = addr[i * 6 + 5]; g_surmx.mixdata[i * 8 + 0] += left; g_surmx.mixdata[i * 8 + 1] += right; g_surmx.mixdata[i * 8 + 2] += center; @@ -205,13 +206,13 @@ s32 cellSSPlayerCreate(vm::ptr handle, vm::ptr config) std::lock_guard lock(g_surmx.mutex); - SSPlayer p; + SSPlayer p {}; p.m_created = true; p.m_connected = false; p.m_active = false; p.m_channels = config->channels; - g_ssp.push_back(p); + g_ssp.push_back(std::move(p)); *handle = ::size32(g_ssp) - 1; return CELL_OK; } @@ -366,7 +367,7 @@ struct surmixer_thread : ppu_thread { //u64 stamp0 = get_guest_system_time(); - memset(g_surmx.mixdata, 0, sizeof(g_surmx.mixdata)); + std::memset(g_surmx.mixdata, 0, sizeof(g_surmx.mixdata)); if (g_surmx.cb) { g_surmx.cb(*this, g_surmx.cb_arg, static_cast(g_surmx.mixcount), 256); @@ -381,10 +382,10 @@ struct surmixer_thread : ppu_thread for (auto& p : g_ssp) if (p.m_active && p.m_created) { auto v = vm::ptrl::make(p.m_addr); // 16-bit LE audio data - float left = 0.0f; - float right = 0.0f; - float speed = std::fabs(p.m_speed); - float fpos = 0.0f; + f32 left = 0.0f; + f32 right = 0.0f; + f32 speed = std::fabs(p.m_speed); + f32 fpos = 0.0f; for (s32 i = 0; i < 256; i++) if (p.m_active) { u32 pos = p.m_position; @@ -454,7 +455,7 @@ struct surmixer_thread : ppu_thread //u64 stamp2 = get_guest_system_time(); - auto buf = vm::_ptr(port.addr.addr() + (g_surmx.mixcount % port.num_blocks) * port.num_channels * AUDIO_BUFFER_SAMPLES * sizeof(float)); + auto buf = vm::_ptr(port.addr.addr() + (g_surmx.mixcount % port.num_blocks) * port.num_channels * AUDIO_BUFFER_SAMPLES * sizeof(f32)); for (auto& mixdata : g_surmx.mixdata) { @@ -497,7 +498,7 @@ s32 cellSurMixerCreate(vm::cptr config) port->num_channels = 8; port->num_blocks = 16; port->attr = 0; - port->size = port->num_channels * port->num_blocks * AUDIO_BUFFER_SAMPLES * sizeof(float); + port->size = port->num_channels * port->num_blocks * AUDIO_BUFFER_SAMPLES * sizeof(f32); port->level = 1.0f; port->level_set.store({ 1.0f, 0.0f }); @@ -574,7 +575,7 @@ s32 cellSurMixerStart() return CELL_OK; } -s32 cellSurMixerSetParameter(u32 param, float value) +s32 cellSurMixerSetParameter(u32 param, f32 value) { libmixer.todo("cellSurMixerSetParameter(param=0x%x, value=%f)", param, value); return CELL_OK; @@ -596,7 +597,7 @@ s32 cellSurMixerFinalize() return CELL_OK; } -s32 cellSurMixerSurBusAddData(u32 busNo, u32 offset, vm::ptr addr, u32 samples) +s32 cellSurMixerSurBusAddData(u32 busNo, u32 offset, vm::ptr addr, u32 samples) { if (busNo < 8 && samples == 256 && offset == 0) { diff --git a/rpcs3/Emu/Cell/Modules/libmixer.h b/rpcs3/Emu/Cell/Modules/libmixer.h index 724ca121f3..f311a65ea0 100644 --- a/rpcs3/Emu/Cell/Modules/libmixer.h +++ b/rpcs3/Emu/Cell/Modules/libmixer.h @@ -41,8 +41,8 @@ enum CELL_SURMIXER_PARAM_REVERBLEVEL = 41, // in dB }; -static const float CELL_SURMIXER_CONT_MUTEON = 1.0; -static const float CELL_SURMIXER_CONT_MUTEOFF = 0.0; +static constexpr f32 CELL_SURMIXER_CONT_MUTEON = 1.0f; +static constexpr f32 CELL_SURMIXER_CONT_MUTEOFF = 0.0f; enum { @@ -137,15 +137,15 @@ struct CellSSPlayerCommonParam struct CellSurMixerPosition { - be_t x; - be_t y; - be_t z; + be_t x; + be_t y; + be_t z; }; struct CellSSPlayerRuntimeInfo { - be_t level; - be_t speed; + be_t level; + be_t speed; CellSurMixerPosition position; }; @@ -163,6 +163,6 @@ struct CellSurMixerChStripParam be_t param; be_t attribute_addr; be_t dBSwitch; - be_t floatVal; + be_t floatVal; be_t intVal; }; diff --git a/rpcs3/Emu/Cell/PPUThread.cpp b/rpcs3/Emu/Cell/PPUThread.cpp index 11bfd40eca..82564a124a 100644 --- a/rpcs3/Emu/Cell/PPUThread.cpp +++ b/rpcs3/Emu/Cell/PPUThread.cpp @@ -5177,7 +5177,7 @@ bool ppu_initialize(const ppu_module& info, bool check_only, u64 file_s settings += ppu_settings::contains_symbol_resolver; // Avoid invalidating all modules for this purpose // Write version, hash, CPU, settings - fmt::append(obj_name, "v7-kusa-%s-%s-%s.obj", fmt::base57(output, 16), fmt::base57(settings), jit_compiler::cpu(g_cfg.core.llvm_cpu)); + fmt::append(obj_name, "v7-kusa-%s-%s-%s.obj", fmt::base57(output, 16), fmt::base57(settings), jit_compiler::cpu(g_cfg.core.llvm_cpu.to_string())); } if (cpu ? cpu->state.all_of(cpu_flag::exit) : Emu.IsStopped()) @@ -5318,7 +5318,7 @@ bool ppu_initialize(const ppu_module& info, bool check_only, u64 file_s { // Use another JIT instance - jit_compiler jit2({}, g_cfg.core.llvm_cpu, 0x1); + jit_compiler jit2({}, g_cfg.core.llvm_cpu.to_string(), 0x1); ppu_initialize2(jit2, part, cache_path, obj_name); } @@ -5351,7 +5351,7 @@ bool ppu_initialize(const ppu_module& info, bool check_only, u64 file_s // Initialize compiler instance while (jits.size() < utils::aligned_div(module_counter, c_moudles_per_jit) && is_being_used_in_emulation) { - jits.emplace_back(std::make_shared(s_link_table, g_cfg.core.llvm_cpu, 0, symbols_cement)); + jits.emplace_back(std::make_shared(s_link_table, g_cfg.core.llvm_cpu.to_string(), 0, symbols_cement)); for (const auto& [addr, func] : *shared_map) { diff --git a/rpcs3/Emu/Cell/PPUTranslator.cpp b/rpcs3/Emu/Cell/PPUTranslator.cpp index 9125aa5eeb..7cba32d9f7 100644 --- a/rpcs3/Emu/Cell/PPUTranslator.cpp +++ b/rpcs3/Emu/Cell/PPUTranslator.cpp @@ -961,7 +961,7 @@ void PPUTranslator::WriteMemory(Value* addr, Value* value, bool is_be, u32 align m_ir->CreateAlignedStore(value, GetMemory(addr), llvm::MaybeAlign{align})->setVolatile(true); } -void PPUTranslator::CompilationError(const std::string& error) +void PPUTranslator::CompilationError(std::string_view error) { ppu_log.error("LLVM: [0x%08x] Error: %s", m_addr + (m_reloc ? m_reloc->addr : 0), error); } diff --git a/rpcs3/Emu/Cell/PPUTranslator.h b/rpcs3/Emu/Cell/PPUTranslator.h index 010945656e..be84d8061e 100644 --- a/rpcs3/Emu/Cell/PPUTranslator.h +++ b/rpcs3/Emu/Cell/PPUTranslator.h @@ -334,7 +334,7 @@ public: } // Handle compilation errors - void CompilationError(const std::string& error); + void CompilationError(std::string_view error); PPUTranslator(llvm::LLVMContext& context, llvm::Module* _module, const ppu_module& info, llvm::ExecutionEngine& engine); ~PPUTranslator(); diff --git a/rpcs3/Emu/Cell/SPUASMJITRecompiler.cpp b/rpcs3/Emu/Cell/SPUASMJITRecompiler.cpp index 5100bc3ded..2addc37017 100644 --- a/rpcs3/Emu/Cell/SPUASMJITRecompiler.cpp +++ b/rpcs3/Emu/Cell/SPUASMJITRecompiler.cpp @@ -2799,32 +2799,35 @@ void spu_recompiler::FREST(spu_opcode_t op) void spu_recompiler::FRSQEST(spu_opcode_t op) { const XmmLink& va = XmmGet(op.ra, XmmType::Float); + const XmmLink& vz = XmmAlloc(); + const XmmLink& v1 = XmmAlloc(); const XmmLink& v_fraction = XmmAlloc(); const XmmLink& v_exponent = XmmAlloc(); c->movdqa(v_fraction, va); c->movdqa(v_exponent, va); + // (exponent==0)? 0xFF : 190 - (exponent + 1) / 2 + c->paddd(v_exponent, v_exponent); + c->pxor(vz, vz); + c->pavgb(v_exponent, vz); + c->movdqa(v1, XmmConst(v128::from32p(190 << 24))); + c->psubb(v1, v_exponent); + c->pcmpeqb(v_exponent, vz); + c->por(v_exponent, v1); + c->psrld(v_exponent, 1); + c->pand(v_exponent, XmmConst(v128::from32p(0xFF << 23))); + c->psrld(v_fraction, 18); - c->psrld(v_exponent, 23); - - c->andps(v_fraction, XmmConst(v128::from32p(0x3F))); - c->andps(v_exponent, XmmConst(v128::from32p(0xFF))); - + c->pand(v_fraction, XmmConst(v128::from32p(0x3F))); const u64 fraction_lut_addr = reinterpret_cast(spu_frsqest_fraction_lut); - const u64 exponent_lut_addr = reinterpret_cast(spu_frsqest_exponent_lut); c->movabs(*arg0, fraction_lut_addr); - c->movabs(*arg1, exponent_lut_addr); for (u32 index = 0; index < 4; index++) { c->pextrd(*qw0, v_fraction, index); c->mov(*qw1, asmjit::x86::dword_ptr(*arg0, *qw0, 2)); c->pinsrd(v_fraction, *qw1, index); - - c->pextrd(*qw0, v_exponent, index); - c->mov(*qw1, asmjit::x86::dword_ptr(*arg1, *qw0, 2)); - c->pinsrd(v_exponent, *qw1, index); } c->orps(v_fraction, v_exponent); @@ -3343,23 +3346,31 @@ void spu_recompiler::HGT(spu_opcode_t op) void spu_recompiler::CLZ(spu_opcode_t op) { + const XmmLink& va = XmmGet(op.ra, XmmType::Int); + if (utils::has_avx512()) { - const XmmLink& va = XmmGet(op.ra, XmmType::Int); const XmmLink& vt = XmmAlloc(); c->vplzcntd(vt, va); c->movdqa(SPU_OFF_128(gpr, op.rt), vt); return; } - c->mov(qw0->r32(), 32 + 31); - for (u32 i = 0; i < 4; i++) // unrolled loop - { - c->bsr(*addr, SPU_OFF_32(gpr, op.ra, &v128::_u32, i)); - c->cmovz(*addr, qw0->r32()); - c->xor_(*addr, 31); - c->mov(SPU_OFF_32(gpr, op.rt, &v128::_u32, i), *addr); - } + // Use signed conversion to float, as exponent is ilog2 + // "Negative" values are zeroed due to saturation subtract + constexpr u32 exp_bias = 127; + + const XmmLink& vf = XmmAlloc(); + const XmmLink& v1 = XmmAlloc(); + c->cvtdq2ps(vf, va); // only correct with round-towards-zero + c->psrld(vf, 23); + c->pxor(v1, v1); + c->pcmpeqd(v1, va); + c->pand(v1, XmmConst(v128::from32p(32 ^ (31 + exp_bias)))); + c->pxor(v1, XmmConst(v128::from32p(31 + exp_bias))); + c->psubusw(v1, vf); // (x==0)? 32 : 31 - (exponent - exp_bias) + c->movdqa(SPU_OFF_128(gpr, op.rt), v1); + return; } void spu_recompiler::XSWD(spu_opcode_t op) diff --git a/rpcs3/Emu/Cell/SPUCommonRecompiler.cpp b/rpcs3/Emu/Cell/SPUCommonRecompiler.cpp index d78bc25508..ab92524d27 100644 --- a/rpcs3/Emu/Cell/SPUCommonRecompiler.cpp +++ b/rpcs3/Emu/Cell/SPUCommonRecompiler.cpp @@ -3973,11 +3973,6 @@ spu_program spu_recompiler_base::analyse(const be_t* ls, u32 entry_point, s it++; } - if (out_target_list) - { - out_target_list->insert(m_targets.begin(), m_targets.end()); - } - // Remove unnecessary target lists for (auto it = m_targets.begin(); it != m_targets.end();) { @@ -3991,7 +3986,13 @@ spu_program spu_recompiler_base::analyse(const be_t* ls, u32 entry_point, s for (auto it2 = it->second.begin(); it2 != it->second.end();) { - if (*it2 < lsa || *it2 >= limit) + // Drop targets out of range, OR pointing at a block that cleanup + // removed above (m_block_info cleared) - the dead in-range edges + // that otherwise leave dangling targets in m_bbs. Pruning them here + // keeps m_targets self-consistent. The pre-existing get_block_targets + // / get_block_preds guards are retained, plus a belt-and-suspenders + // initiate_patterns guard added below - all defense in depth. + if (*it2 < lsa || *it2 >= limit || !m_block_info[*it2 / 4]) { it2 = it->second.erase(it2); removed = true; @@ -4010,6 +4011,14 @@ spu_program spu_recompiler_base::analyse(const be_t* ls, u32 entry_point, s it++; } + // Export the now-pruned, self-consistent target map AFTER the prune above, + // so callers don't see the dead in-range edges (or stale out-of-range keys) + // we just removed from m_targets. + if (out_target_list) + { + out_target_list->insert(m_targets.begin(), m_targets.end()); + } + // Fill holes which contain only NOP and LNOP instructions (TODO: compile) for (u32 i = 0, nnop = 0, vsize = 0; i <= result.data.size(); i++) { @@ -4564,7 +4573,7 @@ spu_program spu_recompiler_base::analyse(const be_t* ls, u32 entry_point, s for (u32 ia = addr; ia < addr + bb.size * 4; ia += 4) { // Decode instruction again - op.opcode = std::bit_cast>(result.data[(ia - lsa) / 41]); + op.opcode = std::bit_cast>(result.data[(ia - lsa) / 4]); last_inst = g_spu_itype.decode(op.opcode); // Propagate some constants @@ -5257,6 +5266,14 @@ spu_program spu_recompiler_base::analyse(const be_t* ls, u32 entry_point, s const auto initiate_patterns = [&](block_reg_state_iterator& block_state_it, u32 bpc, bool is_multi_block) { + // Defense in depth: cleanup now prunes dead in-range edges from m_targets, + // but still skip a bpc whose block was removed (matches get_block_targets) + // so no consumer can ever deref a stale target (e.g. a stop-trap return). + if (!m_block_info[bpc / 4] || !m_bbs.count(bpc)) + { + return; + } + // Initiate patterns (that are initiated on block start) const auto& bb_body = ::at32(m_bbs, bpc); @@ -5319,7 +5336,19 @@ spu_program spu_recompiler_base::analyse(const be_t* ls, u32 entry_point, s { targets_count = 0; - const u32 cond_next = block_pc + ::at32(m_bbs, block_pc).size * 4; + // Belt-and-suspenders (PR #18935): block_pc is the previous block's + // fall-through (cond_next), a live m_bbs key only because the m_targets + // prune keeps dead edges out of block.targets. If that ever regressed, + // the ::at32 below would abort with the same "Range check failed" as + // the bug we fixed - bail cleanly instead of dereferencing a non-block. + const auto block_it = m_bbs.find(block_pc); + if (block_it == m_bbs.end() || !m_block_info[block_pc / 4]) + { + invalid = true; + break; + } + + const u32 cond_next = block_pc + block_it->second.size * 4; valid = false; bool is_end = false; @@ -8721,7 +8750,7 @@ spu_program spu_recompiler_base::analyse(const be_t* ls, u32 entry_point, s // This difference cannot be known at analyzer time but from observing callers. static constexpr std::initializer_list allowed_patterns = { - "620oYSe8uQqq9eTkhWfMqoEXX0us"sv, // CellSpurs JobChain acquire pattern + "disabled_620oYSe8uQqq9eTkhWfMqoEXX0us"sv, // CellSpurs JobChain acquire pattern (disabled for now) }; allow_pattern = std::any_of(allowed_patterns.begin(), allowed_patterns.end(), FN(pattern_hash == x)); diff --git a/rpcs3/Emu/Cell/SPULLVMRecompiler.cpp b/rpcs3/Emu/Cell/SPULLVMRecompiler.cpp index f976dd2d87..392f2f339b 100644 --- a/rpcs3/Emu/Cell/SPULLVMRecompiler.cpp +++ b/rpcs3/Emu/Cell/SPULLVMRecompiler.cpp @@ -77,7 +77,7 @@ void spu_llvm_set_compile_context(spu_llvm_compile_context* context) noexcept class spu_llvm_recompiler : public spu_recompiler_base, public cpu_translator { // JIT Instance - jit_compiler m_jit{{}, jit_compiler::cpu(g_cfg.core.llvm_cpu)}; + jit_compiler m_jit{{}, jit_compiler::cpu(g_cfg.core.llvm_cpu.to_string())}; // Interpreter table size power const u8 m_interp_magn; @@ -128,7 +128,6 @@ class spu_llvm_recompiler : public spu_recompiler_base, public cpu_translator // Global LUTs llvm::GlobalVariable* m_spu_frest_fraction_lut{}; llvm::GlobalVariable* m_spu_frsqest_fraction_lut{}; - llvm::GlobalVariable* m_spu_frsqest_exponent_lut{}; // Helpers (interpreter) llvm::GlobalVariable* m_scale_float_to{}; @@ -1592,7 +1591,7 @@ public: clear_transforms(); #ifdef ARCH_ARM64 { - auto should_exclude_function = [](const std::string& fn_name) + auto should_exclude_function = [](std::string_view fn_name) { return fn_name.starts_with("spu_") || fn_name.starts_with("tr_"); }; @@ -1623,7 +1622,6 @@ public: // LUTs for some instructions m_spu_frest_fraction_lut = new llvm::GlobalVariable(*m_module, llvm::ArrayType::get(GetType(), 32), true, llvm::GlobalValue::PrivateLinkage, llvm::ConstantDataArray::get(m_context, spu_frest_fraction_lut)); m_spu_frsqest_fraction_lut = new llvm::GlobalVariable(*m_module, llvm::ArrayType::get(GetType(), 64), true, llvm::GlobalValue::PrivateLinkage, llvm::ConstantDataArray::get(m_context, spu_frsqest_fraction_lut)); - m_spu_frsqest_exponent_lut = new llvm::GlobalVariable(*m_module, llvm::ArrayType::get(GetType(), 256), true, llvm::GlobalValue::PrivateLinkage, llvm::ConstantDataArray::get(m_context, spu_frsqest_exponent_lut)); } virtual spu_function_t compile(spu_program&& _func) override @@ -6329,7 +6327,30 @@ public: void CLZ(spu_opcode_t op) { +#ifdef ARCH_ARM64 set_vr(op.rt, ctlz(get_vr(op.ra))); +#else + if (m_use_avx512) + { + set_vr(op.rt, ctlz(get_vr(op.ra))); + return; + } + + // Implement manually since LLVM can't take advantage of round-towards-zero. + // Helpful as when converting to a float the exponent is always floor(ilog2) + + constexpr u32 exp_bias = 127; + value_t flt; + + const auto a = get_vr(op.ra); + flt.value = m_ir->CreateSIToFP(a.value, get_type()); // only correct with round-towards-zero! + const auto exp = bitcast(flt) >> 23; + + // "Negative" values cause saturation due to float's sign bit + const auto offset = select(a == 0, splat(32), splat(exp_bias + 31)); + const auto lzcnt = sub_sat(bitcast(offset), bitcast(exp)); + set_vr(op.rt, bitcast(lzcnt)); +#endif } void XSWD(spu_opcode_t op) @@ -7477,13 +7498,23 @@ public: const auto a_sign = (a & splat(0x80000000)); value_t final_result = eval(splat(0)); - for (u32 i = 0; i < 4; i++) + if (m_use_avx512) { - const auto eval_fraction = eval(extract(a_fraction, i)); + value_t lo_lut; + value_t hi_lut; + lo_lut.value = llvm::ConstantDataVector::get(m_context, llvm::ArrayRef(spu_frest_fraction_lut, 16)); + hi_lut.value = llvm::ConstantDataVector::get(m_context, llvm::ArrayRef(spu_frest_fraction_lut + 16, 16)); - value_t r_fraction = load_const(m_spu_frest_fraction_lut, eval_fraction); - - final_result = eval(insert(final_result, i, r_fraction)); + final_result = vperm2d128From512(lo_lut, a_fraction, hi_lut); + } + else + { + for (u32 i = 0; i < 4; i++) + { + const auto eval_fraction = eval(extract(a_fraction, i)); + value_t r_fraction = load_const(m_spu_frest_fraction_lut, eval_fraction); + final_result = eval(insert(final_result, i, r_fraction)); + } } //final_result = eval(select(final_result != (0), final_result, bitcast(pshufb(splat(0), bitcast(final_result))))); @@ -7506,22 +7537,23 @@ public: { const auto a = bitcast(value(ci->getOperand(0))); + // (exponent==0)? 0xFF : 190 - (exponent + 1) / 2 + const auto a_exponent = a & splat(0xFF << 23); + const auto h_exponent = (a_exponent + (a_exponent & splat(1 << 23))) >> splat(1); + const auto r_exponent = splat(190 << 23) - h_exponent; + const auto final_exponent = select(a_exponent == 0, splat(0xFF << 23), r_exponent); + const auto a_fraction = (a >> splat(18)) & splat(0x3F); - const auto a_exponent = (a >> splat(23)) & splat(0xFF); - value_t final_result = eval(splat(0)); + value_t final_fraction = eval(splat(0)); for (u32 i = 0; i < 4; i++) { const auto eval_fraction = eval(extract(a_fraction, i)); - const auto eval_exponent = eval(extract(a_exponent, i)); - value_t r_fraction = load_const(m_spu_frsqest_fraction_lut, eval_fraction); - value_t r_exponent = load_const(m_spu_frsqest_exponent_lut, eval_exponent); - - final_result = eval(insert(final_result, i, eval(r_fraction | r_exponent))); + final_fraction = eval(insert(final_fraction, i, r_fraction)); } - return bitcast(final_result); + return bitcast(final_fraction | final_exponent); }); set_vr(op.rt, frsqest(get_vr(op.ra))); @@ -8529,13 +8561,23 @@ public: const auto a_sign = (a & splat(0x80000000)); value_t b = eval(splat(0)); - for (u32 i = 0; i < 4; i++) + if (m_use_avx512) { - const auto eval_fraction = eval(extract(a_fraction, i)); + value_t lo_lut; + value_t hi_lut; + lo_lut.value = llvm::ConstantDataVector::get(m_context, llvm::ArrayRef(spu_frest_fraction_lut, 16)); + hi_lut.value = llvm::ConstantDataVector::get(m_context, llvm::ArrayRef(spu_frest_fraction_lut + 16, 16)); - value_t r_fraction = load_const(m_spu_frest_fraction_lut, eval_fraction); - - b = eval(insert(b, i, r_fraction)); + b = vperm2d128From512(lo_lut, a_fraction, hi_lut); + } + else + { + for (u32 i = 0; i < 4; i++) + { + const auto eval_fraction = eval(extract(a_fraction, i)); + value_t r_fraction = load_const(m_spu_frest_fraction_lut, eval_fraction); + b = eval(insert(b, i, r_fraction)); + } } b = eval(b | fix_exponent | a_sign); @@ -8552,21 +8594,25 @@ public: register_intrinsic("spu_rsqrte", [&](llvm::CallInst* ci) { const auto a = bitcast(value(ci->getOperand(0))); + + // (exponent==0)? 0xFF : 190 - (exponent + 1) / 2 + const auto a_exponent = a & splat(0xFF << 23); + const auto h_exponent = (a_exponent + (a_exponent & splat(1 << 23))) >> splat(1); + const auto r_exponent = splat(190 << 23) - h_exponent; + const auto final_exponent = select(a_exponent == 0, splat(0xFF << 23), r_exponent); + const auto a_fraction = (a >> splat(18)) & splat(0x3F); - const auto a_exponent = (a >> splat(23)) & splat(0xFF); - value_t b = eval(splat(0)); + value_t final_fraction = eval(splat(0)); for (u32 i = 0; i < 4; i++) { const auto eval_fraction = eval(extract(a_fraction, i)); - const auto eval_exponent = eval(extract(a_exponent, i)); - value_t r_fraction = load_const(m_spu_frsqest_fraction_lut, eval_fraction); - value_t r_exponent = load_const(m_spu_frsqest_exponent_lut, eval_exponent); - - b = eval(insert(b, i, eval(r_fraction | r_exponent))); + final_fraction = eval(insert(final_fraction, i, r_fraction)); } + const auto b = eval(final_fraction | final_exponent); + const auto base = (b & 0x007ffc00u) << 9; // Base fraction const auto ymul = (b & 0x3ff) * (a & 0x7ffff); // Step fraction * Y fraction (fixed point at 2^-32) const auto comparison = (ymul > base); // Should exponent be adjusted? diff --git a/rpcs3/Emu/Cell/lv2/sys_rsx.cpp b/rpcs3/Emu/Cell/lv2/sys_rsx.cpp index 4ef4e04d85..c91a1aa1aa 100644 --- a/rpcs3/Emu/Cell/lv2/sys_rsx.cpp +++ b/rpcs3/Emu/Cell/lv2/sys_rsx.cpp @@ -18,7 +18,7 @@ LOG_CHANNEL(sys_rsx); // Unknown error code returned by sys_rsx_context_attribute enum sys_rsx_error : s32 { - SYS_RSX_CONTEXT_ATTRIBUTE_ERROR = -17 + LV1_ILLEGAL_PARAMETER_VALUE = -17 }; template<> @@ -28,7 +28,7 @@ void fmt_class_string::format(std::string& out, u64 arg) { switch (error) { - STR_CASE(SYS_RSX_CONTEXT_ATTRIBUTE_ERROR); + STR_CASE(LV1_ILLEGAL_PARAMETER_VALUE); } return unknown; @@ -169,12 +169,28 @@ error_code sys_rsx_device_close(cpu_thread& cpu) * @param a6 (IN): E.g. Immediate value passed in cellGcmSys is 16. * @param a7 (IN): E.g. Immediate value passed in cellGcmSys is 8. */ -error_code sys_rsx_memory_allocate(cpu_thread& cpu, vm::ptr mem_handle, vm::ptr mem_addr, u32 size, u64 flags, u64 a5, u64 a6, u64 a7) +error_code sys_rsx_memory_allocate(cpu_thread& cpu, vm::ptr mem_handle, vm::ptr mem_addr, u64 size, u64 flags, u64 a5, u64 a6, u64 a7) { - cpu.state += cpu_flag::wait; - sys_rsx.warning("sys_rsx_memory_allocate(mem_handle=*0x%x, mem_addr=*0x%x, size=0x%x, flags=0x%llx, a5=0x%llx, a6=0x%llx, a7=0x%llx)", mem_handle, mem_addr, size, flags, a5, a6, a7); + // size == 0 yields available size, unimplemented + ensure(size != 0); + + if (size & 0xFFFFF) + { + return LV1_ILLEGAL_PARAMETER_VALUE; + } + + // This is a result from how the argument is treated internally + size %= 0x100000 * 0x1'0000'00000; + + if (size > 0x1000'0000) + { + return LV1_ILLEGAL_PARAMETER_VALUE; + } + + cpu.state += cpu_flag::wait; + if (vm::falloc(rsx::constants::local_mem_base, size, vm::video)) { rsx::get_current_renderer()->local_mem_size = size; @@ -395,7 +411,7 @@ error_code sys_rsx_context_free(ppu_thread& ppu, u32 context_id) * @param size (IN): Size of mapping area in bytes. E.g. 0x00200000 * @param flags (IN): */ -error_code sys_rsx_context_iomap(cpu_thread& cpu, u32 context_id, u32 io, u32 ea, u32 size, u64 flags) +error_code sys_rsx_context_iomap(cpu_thread& cpu, u32 context_id, u64 io, u64 ea, u64 size, u64 flags) { cpu.state += cpu_flag::wait; @@ -403,8 +419,12 @@ error_code sys_rsx_context_iomap(cpu_thread& cpu, u32 context_id, u32 io, u32 ea const auto render = rsx::get_current_renderer(); - if (!size || io & 0xFFFFF || ea + u64{size} > rsx::constants::local_mem_base || ea & 0xFFFFF || size & 0xFFFFF || - context_id != 0x55555555 || render->main_mem_size < io + u64{size}) + if (!size || io & 0xFFFFF || size > 0x200'00000 || size > std::min(~io, ~size) || ea & 0xFFFFF || size & 0xFFFFF) + { + return CELL_EINVAL; + } + + if (context_id != 0x55555555 || render->main_mem_size < io + size) { return CELL_EINVAL; } @@ -419,7 +439,7 @@ error_code sys_rsx_context_iomap(cpu_thread& cpu, u32 context_id, u32 io, u32 ea vm::writer_lock rlock; - for (u32 addr = ea, end = ea + size; addr < end; addr += 0x100000) + for (u64 addr = ea, end = ea + size; addr < end; addr += 0x100000) { if (!vm::check_addr(addr, vm::page_readable | (addr < 0x20000000 ? 0 : vm::page_1m_size))) { @@ -458,7 +478,7 @@ error_code sys_rsx_context_iomap(cpu_thread& cpu, u32 context_id, u32 io, u32 ea * @param io (IN): IO address. E.g. 0x00600000 (Start page 6) * @param size (IN): Size to unmap in byte. E.g. 0x00200000 */ -error_code sys_rsx_context_iounmap(cpu_thread& cpu, u32 context_id, u32 io, u32 size) +error_code sys_rsx_context_iounmap(cpu_thread& cpu, u32 context_id, u64 io, u64 size) { cpu.state += cpu_flag::wait; @@ -466,8 +486,12 @@ error_code sys_rsx_context_iounmap(cpu_thread& cpu, u32 context_id, u32 io, u32 const auto render = rsx::get_current_renderer(); - if (!size || size & 0xFFFFF || io & 0xFFFFF || context_id != 0x55555555 || - render->main_mem_size < io + u64{size}) + if (!size || size & 0xFFFFF || io & 0xFFFFF || size > 0x200'00000 || size > ~io) + { + return CELL_EINVAL; + } + + if (context_id != 0x55555555 || render->main_mem_size < io + size) { return CELL_EINVAL; } @@ -481,7 +505,7 @@ error_code sys_rsx_context_iounmap(cpu_thread& cpu, u32 context_id, u32 io, u32 std::scoped_lock lock(render->sys_rsx_mtx); - for (const u32 end = (io >>= 20) + (size >>= 20); io < end;) + for (const u64 end = (io >>= 20) + (size >>= 20); io < end;) { auto& table = render->iomap_table; @@ -626,7 +650,7 @@ error_code sys_rsx_context_attribute(u32 context_id, u32 package_id, u64 a3, u64 const u8 id = a3 & 0xFF; if (id > 7) { - return SYS_RSX_CONTEXT_ATTRIBUTE_ERROR; + return LV1_ILLEGAL_PARAMETER_VALUE; } std::lock_guard lock(render->sys_rsx_mtx); @@ -676,7 +700,7 @@ error_code sys_rsx_context_attribute(u32 context_id, u32 package_id, u64 a3, u64 { if (a3 > 7) { - return SYS_RSX_CONTEXT_ATTRIBUTE_ERROR; + return LV1_ILLEGAL_PARAMETER_VALUE; } // NOTE: There currently seem to only be 2 active heads on PS3 @@ -784,7 +808,7 @@ error_code sys_rsx_context_attribute(u32 context_id, u32 package_id, u64 a3, u64 if (a3 >= std::size(render->zculls)) { - return SYS_RSX_CONTEXT_ATTRIBUTE_ERROR; + return LV1_ILLEGAL_PARAMETER_VALUE; } if (!render->is_fifo_idle()) diff --git a/rpcs3/Emu/Cell/lv2/sys_rsx.h b/rpcs3/Emu/Cell/lv2/sys_rsx.h index 44f02a697d..9b06b88ea4 100644 --- a/rpcs3/Emu/Cell/lv2/sys_rsx.h +++ b/rpcs3/Emu/Cell/lv2/sys_rsx.h @@ -127,12 +127,12 @@ struct RsxDisplayInfo // SysCalls error_code sys_rsx_device_open(cpu_thread& cpu); error_code sys_rsx_device_close(cpu_thread& cpu); -error_code sys_rsx_memory_allocate(cpu_thread& cpu, vm::ptr mem_handle, vm::ptr mem_addr, u32 size, u64 flags, u64 a5, u64 a6, u64 a7); +error_code sys_rsx_memory_allocate(cpu_thread& cpu, vm::ptr mem_handle, vm::ptr mem_addr, u64 size, u64 flags, u64 a5, u64 a6, u64 a7); error_code sys_rsx_memory_free(cpu_thread& cpu, u32 mem_handle); error_code sys_rsx_context_allocate(cpu_thread& cpu, vm::ptr context_id, vm::ptr lpar_dma_control, vm::ptr lpar_driver_info, vm::ptr lpar_reports, u64 mem_ctx, u64 system_mode); error_code sys_rsx_context_free(ppu_thread& ppu, u32 context_id); -error_code sys_rsx_context_iomap(cpu_thread& cpu, u32 context_id, u32 io, u32 ea, u32 size, u64 flags); -error_code sys_rsx_context_iounmap(cpu_thread& cpu, u32 context_id, u32 io, u32 size); +error_code sys_rsx_context_iomap(cpu_thread& cpu, u32 context_id, u64 io, u64 ea, u64 size, u64 flags); +error_code sys_rsx_context_iounmap(cpu_thread& cpu, u32 context_id, u64 io, u64 size); error_code sys_rsx_context_attribute(u32 context_id, u32 package_id, u64 a3, u64 a4, u64 a5, u64 a6); error_code sys_rsx_device_map(cpu_thread& cpu, vm::ptr dev_addr, vm::ptr a2, u32 dev_id); error_code sys_rsx_device_unmap(cpu_thread& cpu, u32 dev_id); diff --git a/rpcs3/Emu/Cell/lv2/sys_rsxaudio.cpp b/rpcs3/Emu/Cell/lv2/sys_rsxaudio.cpp index c5204b295c..95ce7803e2 100644 --- a/rpcs3/Emu/Cell/lv2/sys_rsxaudio.cpp +++ b/rpcs3/Emu/Cell/lv2/sys_rsxaudio.cpp @@ -1240,7 +1240,9 @@ bool rsxaudio_data_thread::enqueue_data(RsxaudioPort dst, bool silence, const vo { auto& backend_thread = g_fxo->get(); - if (dst == RsxaudioPort::SERIAL) + switch (dst) + { + case RsxaudioPort::SERIAL: { if (!silence) { @@ -1258,7 +1260,7 @@ bool rsxaudio_data_thread::enqueue_data(RsxaudioPort dst, bool silence, const vo backend_thread.add_data(cont); return cont.data_was_used(); } - else if (dst == RsxaudioPort::SPDIF_0) + case RsxaudioPort::SPDIF_0: { if (!silence) { @@ -1273,7 +1275,7 @@ bool rsxaudio_data_thread::enqueue_data(RsxaudioPort dst, bool silence, const vo backend_thread.add_data(cont); return cont.data_was_used(); } - else if (dst == RsxaudioPort::SPDIF_1) + case RsxaudioPort::SPDIF_1: { if (!silence) { @@ -1288,6 +1290,11 @@ bool rsxaudio_data_thread::enqueue_data(RsxaudioPort dst, bool silence, const vo backend_thread.add_data(cont); return cont.data_was_used(); } + case RsxaudioPort::INVALID: + { + break; + } + } return false; } @@ -1361,7 +1368,7 @@ u8 rsxaudio_backend_thread::get_channel_count() const rsxaudio_backend_thread::emu_audio_cfg rsxaudio_backend_thread::get_emu_cfg() { // Get max supported channel count - AudioChannelCnt out_ch_cnt = AudioBackend::get_max_channel_count(0); // CELL_AUDIO_OUT_PRIMARY + const AudioChannelCnt out_ch_cnt = AudioBackend::get_max_channel_count(0); // CELL_AUDIO_OUT_PRIMARY emu_audio_cfg cfg = { @@ -1413,6 +1420,20 @@ void rsxaudio_backend_thread::operator()() { lock.unlock(); backend_stop(); + + // Destroy the backend on this thread, the one that created it in backend_init(). + // The backend's ctor calls CoInitializeEx here on Windows; if it is instead released + // by ~rsxaudio_backend_thread() (which runs on the GUI thread via g_fxo->clear() + // during Kill()), the matching CoUninitialize lands on the GUI thread, draining its + // OLE reference and silently breaking the main window's file drag&drop. Keep COM + // init/teardown balanced on this thread. + if (backend) + { + backend->Close(); + backend->SetWriteCallback(nullptr); + backend->SetStateCallback(nullptr); + backend = nullptr; + } return; } @@ -2271,9 +2292,9 @@ void rsxaudio_periodic_tmr::cancel_wait() void rsxaudio_periodic_tmr::enable_vtimer(u32 vtimer_id, u32 rate, u64 crnt_time) { - ensure(vtimer_id < VTIMER_MAX && rate); + ensure(rate); - vtimer& vtimer = vtmr_pool[vtimer_id]; + vtimer& vtimer = ::at32(vtmr_pool, vtimer_id); const f64 new_blk_time = get_blk_time(rate); // Avoid timer reset when possible @@ -2288,26 +2309,20 @@ void rsxaudio_periodic_tmr::enable_vtimer(u32 vtimer_id, u32 rate, u64 crnt_time void rsxaudio_periodic_tmr::disable_vtimer(u32 vtimer_id) { - ensure(vtimer_id < VTIMER_MAX); - - vtimer& vtimer = vtmr_pool[vtimer_id]; + vtimer& vtimer = ::at32(vtmr_pool, vtimer_id); vtimer.active = false; } bool rsxaudio_periodic_tmr::is_vtimer_behind(u32 vtimer_id, u64 crnt_time) const { - ensure(vtimer_id < VTIMER_MAX); - - const vtimer& vtimer = vtmr_pool[vtimer_id]; + const vtimer& vtimer = ::at32(vtmr_pool, vtimer_id); return is_vtimer_behind(vtimer, crnt_time); } void rsxaudio_periodic_tmr::vtimer_skip_periods(u32 vtimer_id, u64 crnt_time) { - ensure(vtimer_id < VTIMER_MAX); - - vtimer& vtimer = vtmr_pool[vtimer_id]; + vtimer& vtimer = ::at32(vtmr_pool, vtimer_id); if (is_vtimer_behind(vtimer, crnt_time)) { @@ -2317,9 +2332,7 @@ void rsxaudio_periodic_tmr::vtimer_skip_periods(u32 vtimer_id, u64 crnt_time) void rsxaudio_periodic_tmr::vtimer_incr(u32 vtimer_id, u64 crnt_time) { - ensure(vtimer_id < VTIMER_MAX); - - vtimer& vtimer = vtmr_pool[vtimer_id]; + vtimer& vtimer = ::at32(vtmr_pool, vtimer_id); if (is_vtimer_behind(vtimer, crnt_time)) { @@ -2329,18 +2342,14 @@ void rsxaudio_periodic_tmr::vtimer_incr(u32 vtimer_id, u64 crnt_time) bool rsxaudio_periodic_tmr::is_vtimer_active(u32 vtimer_id) const { - ensure(vtimer_id < VTIMER_MAX); - - const vtimer& vtimer = vtmr_pool[vtimer_id]; + const vtimer& vtimer = ::at32(vtmr_pool, vtimer_id); return vtimer.active; } u64 rsxaudio_periodic_tmr::vtimer_get_sched_time(u32 vtimer_id) const { - ensure(vtimer_id < VTIMER_MAX); - - const vtimer& vtimer = vtmr_pool[vtimer_id]; + const vtimer& vtimer = ::at32(vtmr_pool, vtimer_id); return static_cast(vtimer.blk_cnt * vtimer.blk_time); } diff --git a/rpcs3/Emu/Cell/lv2/sys_timer.cpp b/rpcs3/Emu/Cell/lv2/sys_timer.cpp index 8a2419097d..e65fa10432 100644 --- a/rpcs3/Emu/Cell/lv2/sys_timer.cpp +++ b/rpcs3/Emu/Cell/lv2/sys_timer.cpp @@ -37,8 +37,8 @@ lv2_timer::lv2_timer(utils::serial& ar) , source(ar) , data1(ar) , data2(ar) - , expire(ar.pop()) - , period(ar.pop()) + , expire(ar.pop()) + , period(ar.pop()) { } diff --git a/rpcs3/Emu/GDB.cpp b/rpcs3/Emu/GDB.cpp index 20e6c82cd4..5c6eb11396 100644 --- a/rpcs3/Emu/GDB.cpp +++ b/rpcs3/Emu/GDB.cpp @@ -102,7 +102,7 @@ std::string u32_to_padded_hex(u32 value) template T hex_to(std::string_view val) { - T result; + T result {}; auto [ptr, err] = std::from_chars(val.data(), val.data() + val.size(), result, 16); if (err != std::errc()) { @@ -361,7 +361,7 @@ void gdb_thread::ack(bool accepted) send_char(accepted ? '+' : '-'); } -void gdb_thread::send_cmd(const std::string& cmd) +void gdb_thread::send_cmd(std::string_view cmd) { u8 checksum = 0; std::string buf; @@ -376,7 +376,7 @@ void gdb_thread::send_cmd(const std::string& cmd) send(buf.c_str(), static_cast(buf.length())); } -bool gdb_thread::send_cmd_ack(const std::string& cmd) +bool gdb_thread::send_cmd_ack(std::string_view cmd) { while (true) { @@ -465,7 +465,7 @@ std::string gdb_thread::get_reg(ppu_thread* thread, u32 rid) } } -bool gdb_thread::set_reg(ppu_thread* thread, u32 rid, const std::string& value) +bool gdb_thread::set_reg(ppu_thread* thread, u32 rid, std::string_view value) { switch (rid) { diff --git a/rpcs3/Emu/GDB.h b/rpcs3/Emu/GDB.h index 75d272f9d7..0de02ffe25 100644 --- a/rpcs3/Emu/GDB.h +++ b/rpcs3/Emu/GDB.h @@ -44,10 +44,10 @@ class gdb_thread //acknowledge packet, either as accepted or declined void ack(bool accepted); //sends command body cmd to client - void send_cmd(const std::string& cmd); + void send_cmd(std::string_view cmd); //sends command to client until receives positive acknowledgement //returns false in case some error happened, and command wasn't sent - bool send_cmd_ack(const std::string& cmd); + bool send_cmd_ack(std::string_view cmd); //appends encoded char c to string str, and returns checksum. encoded byte can occupy 2 bytes static u8 append_encoded_char(char c, std::string& str); //convert u8 to 2 byte hexademical representation @@ -57,7 +57,7 @@ class gdb_thread //returns register value as hex string by register id (in gdb), in case of wrong id returns empty string static std::string get_reg(ppu_thread* thread, u32 rid); //sets register value to hex string by register id (in gdb), in case of wrong id returns false - static bool set_reg(ppu_thread* thread, u32 rid, const std::string& value); + static bool set_reg(ppu_thread* thread, u32 rid, std::string_view value); //returns size of register with id rid in bytes, zero if invalid rid is provided static u32 get_reg_size(ppu_thread* thread, u32 rid); //send reason of stop, returns false if sending response failed diff --git a/rpcs3/Emu/Io/PadHandler.cpp b/rpcs3/Emu/Io/PadHandler.cpp index ccd81d2805..178ae9f4fa 100644 --- a/rpcs3/Emu/Io/PadHandler.cpp +++ b/rpcs3/Emu/Io/PadHandler.cpp @@ -11,7 +11,7 @@ PadHandlerBase::PadHandlerBase(pad_handler type) : m_type(type) { } -std::vector> PadHandlerBase::find_key_combos(const std::unordered_map& map, const std::string& cfg_string) +std::vector> PadHandlerBase::find_key_combos(const std::unordered_map& map, std::string_view cfg_string) { std::vector> key_codes; @@ -232,7 +232,7 @@ pad_capabilities PadHandlerBase::get_capabilities(const std::string& /*pad_id*/) }; } -cfg_pad* PadHandlerBase::get_config(const std::string& pad_id) +cfg_pad* PadHandlerBase::get_config(std::string_view pad_id) { int index = 0; @@ -595,52 +595,52 @@ std::array>, PadHandlerBase::button::button_count> Pad if (!device || !cfg) return mapping; - mapping[button::up] = find_key_combos(button_list, cfg->up); - mapping[button::down] = find_key_combos(button_list, cfg->down); - mapping[button::left] = find_key_combos(button_list, cfg->left); - mapping[button::right] = find_key_combos(button_list, cfg->right); - mapping[button::cross] = find_key_combos(button_list, cfg->cross); - mapping[button::square] = find_key_combos(button_list, cfg->square); - mapping[button::circle] = find_key_combos(button_list, cfg->circle); - mapping[button::triangle] = find_key_combos(button_list, cfg->triangle); - mapping[button::start] = find_key_combos(button_list, cfg->start); - mapping[button::select] = find_key_combos(button_list, cfg->select); - mapping[button::l1] = find_key_combos(button_list, cfg->l1); - mapping[button::l2] = find_key_combos(button_list, cfg->l2); - mapping[button::l3] = find_key_combos(button_list, cfg->l3); - mapping[button::r1] = find_key_combos(button_list, cfg->r1); - mapping[button::r2] = find_key_combos(button_list, cfg->r2); - mapping[button::r3] = find_key_combos(button_list, cfg->r3); - mapping[button::ls_left] = find_key_combos(button_list, cfg->ls_left); - mapping[button::ls_right] = find_key_combos(button_list, cfg->ls_right); - mapping[button::ls_down] = find_key_combos(button_list, cfg->ls_down); - mapping[button::ls_up] = find_key_combos(button_list, cfg->ls_up); - mapping[button::rs_left] = find_key_combos(button_list, cfg->rs_left); - mapping[button::rs_right] = find_key_combos(button_list, cfg->rs_right); - mapping[button::rs_down] = find_key_combos(button_list, cfg->rs_down); - mapping[button::rs_up] = find_key_combos(button_list, cfg->rs_up); - mapping[button::ps] = find_key_combos(button_list, cfg->ps); + mapping[button::up] = find_key_combos(button_list, cfg->up.to_string()); + mapping[button::down] = find_key_combos(button_list, cfg->down.to_string()); + mapping[button::left] = find_key_combos(button_list, cfg->left.to_string()); + mapping[button::right] = find_key_combos(button_list, cfg->right.to_string()); + mapping[button::cross] = find_key_combos(button_list, cfg->cross.to_string()); + mapping[button::square] = find_key_combos(button_list, cfg->square.to_string()); + mapping[button::circle] = find_key_combos(button_list, cfg->circle.to_string()); + mapping[button::triangle] = find_key_combos(button_list, cfg->triangle.to_string()); + mapping[button::start] = find_key_combos(button_list, cfg->start.to_string()); + mapping[button::select] = find_key_combos(button_list, cfg->select.to_string()); + mapping[button::l1] = find_key_combos(button_list, cfg->l1.to_string()); + mapping[button::l2] = find_key_combos(button_list, cfg->l2.to_string()); + mapping[button::l3] = find_key_combos(button_list, cfg->l3.to_string()); + mapping[button::r1] = find_key_combos(button_list, cfg->r1.to_string()); + mapping[button::r2] = find_key_combos(button_list, cfg->r2.to_string()); + mapping[button::r3] = find_key_combos(button_list, cfg->r3.to_string()); + mapping[button::ls_left] = find_key_combos(button_list, cfg->ls_left.to_string()); + mapping[button::ls_right] = find_key_combos(button_list, cfg->ls_right.to_string()); + mapping[button::ls_down] = find_key_combos(button_list, cfg->ls_down.to_string()); + mapping[button::ls_up] = find_key_combos(button_list, cfg->ls_up.to_string()); + mapping[button::rs_left] = find_key_combos(button_list, cfg->rs_left.to_string()); + mapping[button::rs_right] = find_key_combos(button_list, cfg->rs_right.to_string()); + mapping[button::rs_down] = find_key_combos(button_list, cfg->rs_down.to_string()); + mapping[button::rs_up] = find_key_combos(button_list, cfg->rs_up.to_string()); + mapping[button::ps] = find_key_combos(button_list, cfg->ps.to_string()); - mapping[button::skateboard_ir_nose] = find_key_combos(button_list, cfg->ir_nose); - mapping[button::skateboard_ir_tail] = find_key_combos(button_list, cfg->ir_tail); - mapping[button::skateboard_ir_left] = find_key_combos(button_list, cfg->ir_left); - mapping[button::skateboard_ir_right] = find_key_combos(button_list, cfg->ir_right); - mapping[button::skateboard_tilt_left] = find_key_combos(button_list, cfg->tilt_left); - mapping[button::skateboard_tilt_right] = find_key_combos(button_list, cfg->tilt_right); + mapping[button::skateboard_ir_nose] = find_key_combos(button_list, cfg->ir_nose.to_string()); + mapping[button::skateboard_ir_tail] = find_key_combos(button_list, cfg->ir_tail.to_string()); + mapping[button::skateboard_ir_left] = find_key_combos(button_list, cfg->ir_left.to_string()); + mapping[button::skateboard_ir_right] = find_key_combos(button_list, cfg->ir_right.to_string()); + mapping[button::skateboard_tilt_left] = find_key_combos(button_list, cfg->tilt_left.to_string()); + mapping[button::skateboard_tilt_right] = find_key_combos(button_list, cfg->tilt_right.to_string()); if (b_has_pressure_intensity_button) { - mapping[button::pressure_intensity_button] = find_key_combos(button_list, cfg->pressure_intensity_button); + mapping[button::pressure_intensity_button] = find_key_combos(button_list, cfg->pressure_intensity_button.to_string()); } if (b_has_analog_limiter_button) { - mapping[button::analog_limiter_button] = find_key_combos(button_list, cfg->analog_limiter_button); + mapping[button::analog_limiter_button] = find_key_combos(button_list, cfg->analog_limiter_button.to_string()); } if (b_has_orientation) { - mapping[button::orientation_reset_button] = find_key_combos(button_list, cfg->orientation_reset_button); + mapping[button::orientation_reset_button] = find_key_combos(button_list, cfg->orientation_reset_button.to_string()); } return mapping; diff --git a/rpcs3/Emu/Io/PadHandler.h b/rpcs3/Emu/Io/PadHandler.h index 0f38f0cb3b..4db058e597 100644 --- a/rpcs3/Emu/Io/PadHandler.h +++ b/rpcs3/Emu/Io/PadHandler.h @@ -193,7 +193,7 @@ protected: std::shared_ptr m_pad_for_pad_settings; // Search an unordered map for a string value and return the found combos - static std::vector> find_key_combos(const std::unordered_map& map, const std::string& cfg_string); + static std::vector> find_key_combos(const std::unordered_map& map, std::string_view cfg_string); // Search an unordered map for a combo and return the found key codes static std::set find_key_codes(const std::unordered_map& map, const pad::combo& combo); @@ -324,7 +324,7 @@ protected: virtual void get_mapping(const pad_ensemble& binding); void TranslateButtonPress(const std::shared_ptr& device, u32 keyCode, bool& pressed, u16& val, bool use_stick_multipliers, bool ignore_stick_threshold = false, bool ignore_trigger_threshold = false); void init_configs(); - cfg_pad* get_config(const std::string& pad_id); + cfg_pad* get_config(std::string_view pad_id); static void set_raw_orientation(ps_move_data& move_data, f32 accel_x, f32 accel_y, f32 accel_z, f32 gyro_x, f32 gyro_y, f32 gyro_z); static void set_raw_orientation(Pad& pad); diff --git a/rpcs3/Emu/Io/RB3MidiDrums.cpp b/rpcs3/Emu/Io/RB3MidiDrums.cpp index 4bdc253c69..88ccb17178 100644 --- a/rpcs3/Emu/Io/RB3MidiDrums.cpp +++ b/rpcs3/Emu/Io/RB3MidiDrums.cpp @@ -313,7 +313,7 @@ usb_device_rb3_midi_drums::Definition::Definition(std::string name, const std::s , create_state{create_state} {} -usb_device_rb3_midi_drums::usb_device_rb3_midi_drums(const std::array& location, const std::string& device_name) +usb_device_rb3_midi_drums::usb_device_rb3_midi_drums(const std::array& location, std::string_view device_name) : usb_device_emulated(location) { m_id_to_note_mapping = midi::create_id_to_note_mapping(); @@ -563,8 +563,8 @@ void usb_device_rb3_midi_drums::interrupt_transfer(u32 buf_size, u8* buf, u32 /* } else { - bool is_cancel = kit_state.snare >= midi::min_velocity(); - bool is_accept = kit_state.floor_tom >= midi::min_velocity(); + const bool is_cancel = kit_state.snare >= midi::min_velocity(); + const bool is_accept = kit_state.floor_tom >= midi::min_velocity(); if (hold_kick && (is_cancel || is_accept)) { // Hold kick brings up the song category selector menu, which can be dismissed using accept/cancel buttons. diff --git a/rpcs3/Emu/Io/RB3MidiDrums.h b/rpcs3/Emu/Io/RB3MidiDrums.h index 9d13a81812..8d71ce93ef 100644 --- a/rpcs3/Emu/Io/RB3MidiDrums.h +++ b/rpcs3/Emu/Io/RB3MidiDrums.h @@ -109,7 +109,7 @@ enum class Note : u8 class usb_device_rb3_midi_drums : public usb_device_emulated { public: - usb_device_rb3_midi_drums(const std::array& location, const std::string& device_name); + usb_device_rb3_midi_drums(const std::array& location, std::string_view device_name); ~usb_device_rb3_midi_drums(); void control_transfer(u8 bmRequestType, u8 bRequest, u16 wValue, u16 wIndex, u16 wLength, u32 buf_size, u8* buf, UsbTransfer* transfer) override; diff --git a/rpcs3/Emu/Io/RB3MidiGuitar.cpp b/rpcs3/Emu/Io/RB3MidiGuitar.cpp index 923fc3a98b..573944dd52 100644 --- a/rpcs3/Emu/Io/RB3MidiGuitar.cpp +++ b/rpcs3/Emu/Io/RB3MidiGuitar.cpp @@ -6,7 +6,7 @@ LOG_CHANNEL(rb3_midi_guitar_log); -usb_device_rb3_midi_guitar::usb_device_rb3_midi_guitar(const std::array& location, const std::string& device_name, bool twentytwo_fret) +usb_device_rb3_midi_guitar::usb_device_rb3_midi_guitar(const std::array& location, std::string_view device_name, bool twentytwo_fret) : usb_device_emulated(location) { // For the 22-fret guitar (Fender Squier), the only thing that's different diff --git a/rpcs3/Emu/Io/RB3MidiGuitar.h b/rpcs3/Emu/Io/RB3MidiGuitar.h index 3a93f95d61..6c20aa5a2a 100644 --- a/rpcs3/Emu/Io/RB3MidiGuitar.h +++ b/rpcs3/Emu/Io/RB3MidiGuitar.h @@ -36,7 +36,7 @@ private: void write_state(u8* buf); public: - usb_device_rb3_midi_guitar(const std::array& location, const std::string& device_name, bool twentytwo_fret); + usb_device_rb3_midi_guitar(const std::array& location, std::string_view device_name, bool twentytwo_fret); ~usb_device_rb3_midi_guitar(); void control_transfer(u8 bmRequestType, u8 bRequest, u16 wValue, u16 wIndex, u16 wLength, u32 buf_size, u8* buf, UsbTransfer* transfer) override; diff --git a/rpcs3/Emu/Io/RB3MidiKeyboard.cpp b/rpcs3/Emu/Io/RB3MidiKeyboard.cpp index f96f138628..f22ad800b7 100644 --- a/rpcs3/Emu/Io/RB3MidiKeyboard.cpp +++ b/rpcs3/Emu/Io/RB3MidiKeyboard.cpp @@ -6,7 +6,7 @@ LOG_CHANNEL(rb3_midi_keyboard_log); -usb_device_rb3_midi_keyboard::usb_device_rb3_midi_keyboard(const std::array& location, const std::string& device_name) +usb_device_rb3_midi_keyboard::usb_device_rb3_midi_keyboard(const std::array& location, std::string_view device_name) : usb_device_emulated(location) { device = UsbDescriptorNode(USB_DESCRIPTOR_DEVICE, UsbDeviceDescriptor{0x0200, 0x00, 0x00, 0x00, 64, 0x12ba, 0x2338, 0x01, 0x01, 0x02, 0x00, 0x01}); diff --git a/rpcs3/Emu/Io/RB3MidiKeyboard.h b/rpcs3/Emu/Io/RB3MidiKeyboard.h index c5042af239..131fedc0c3 100644 --- a/rpcs3/Emu/Io/RB3MidiKeyboard.h +++ b/rpcs3/Emu/Io/RB3MidiKeyboard.h @@ -41,7 +41,7 @@ private: void write_state(u8* buf); public: - usb_device_rb3_midi_keyboard(const std::array& location, const std::string& device_name); + usb_device_rb3_midi_keyboard(const std::array& location, std::string_view device_name); ~usb_device_rb3_midi_keyboard(); void control_transfer(u8 bmRequestType, u8 bRequest, u16 wValue, u16 wIndex, u16 wLength, u32 buf_size, u8* buf, UsbTransfer* transfer) override; diff --git a/rpcs3/Emu/Io/camera_config.cpp b/rpcs3/Emu/Io/camera_config.cpp index 4016447321..f1fabe5531 100644 --- a/rpcs3/Emu/Io/camera_config.cpp +++ b/rpcs3/Emu/Io/camera_config.cpp @@ -77,7 +77,7 @@ void cfg_camera::camera_setting::from_string(std::string_view text) return; } - const std::vector list = fmt::split(text, { "," }); + const std::vector list = fmt::split_sv(text, { "," }); if (list.size() != member_count) { @@ -85,9 +85,15 @@ void cfg_camera::camera_setting::from_string(std::string_view text) return; } - const auto to_integer = [](const std::string& str, int& out) -> bool + const auto to_integer = [](std::string_view str, int& out) -> bool { - auto [ptr, ec] = std::from_chars(str.c_str(), str.c_str() + str.size(), out); + if (str.empty()) + { + camera_log.error("Empty string cannot be interpreted as integer."); + return false; + } + + auto [ptr, ec] = std::from_chars(str.data(), str.data() + str.size(), out); if (ec != std::errc{}) { camera_log.error("String '%s' cannot be interpreted as integer.", str); @@ -96,11 +102,17 @@ void cfg_camera::camera_setting::from_string(std::string_view text) return true; }; - const auto to_double = [](const std::string& str, double& out) -> bool + const auto to_double = [](std::string_view str, double& out) -> bool { + if (str.empty()) + { + camera_log.error("Empty string cannot be interpreted as double."); + return false; + } + char* end{}; - out = std::strtod(str.c_str(), &end); - if (end != str.c_str() + str.size()) + out = std::strtod(str.data(), &end); + if (end != str.data() + str.size()) { camera_log.error("String '%s' cannot be interpreted as double.", str); return false; diff --git a/rpcs3/Emu/Memory/vm.cpp b/rpcs3/Emu/Memory/vm.cpp index 9e460a7aa2..6372e6e3cc 100644 --- a/rpcs3/Emu/Memory/vm.cpp +++ b/rpcs3/Emu/Memory/vm.cpp @@ -483,6 +483,14 @@ namespace vm { auto& bits = get_range_lock_bits(true); + if (!!bits) + { + if (i == 0 && g_cfg.core.ppu_reservation_priority_over_spu) + { + busy_wait(5000); + } + } + if (!range_lock) { if (!bits && bits.compare_and_swap_test(0, u64{umax})) @@ -1062,15 +1070,15 @@ namespace vm return size; } - bool check_addr(u32 addr, u8 flags, u32 size) + bool check_addr(u64 addr, u8 flags, u32 size) { if (size == 0) { return true; } - // Overflow checking - if (0x10000'0000ull - addr < size) + // u64 addressing is not supported at the moment + if (addr > u32{umax} || 0x10000'0000ull - addr < size) { return false; } @@ -1078,7 +1086,7 @@ namespace vm // Always check this flag flags |= page_allocated; - for (u32 i = addr / 4096, max = (addr + size - 1) / 4096; i <= max;) + for (u64 i = addr / 4096, max = (addr + size - 1) / 4096; i <= max;) { auto state = +g_pages[i]; diff --git a/rpcs3/Emu/Memory/vm.h b/rpcs3/Emu/Memory/vm.h index 87e798aadb..c59e32d5b4 100644 --- a/rpcs3/Emu/Memory/vm.h +++ b/rpcs3/Emu/Memory/vm.h @@ -78,10 +78,10 @@ namespace vm bool page_protect(u32 addr, u32 size, u8 flags_test = 0, u8 flags_set = 0, u8 flags_clear = 0); // Check flags for specified memory range (unsafe) - bool check_addr(u32 addr, u8 flags, u32 size); + bool check_addr(u64 addr, u8 flags, u32 size); template - inline bool check_addr(u32 addr, u8 flags = page_readable) + inline bool check_addr(u64 addr, u8 flags = page_readable) { extern std::array g_pages; diff --git a/rpcs3/Emu/NP/clans_client.cpp b/rpcs3/Emu/NP/clans_client.cpp index c529d5ea38..2e7bb88407 100644 --- a/rpcs3/Emu/NP/clans_client.cpp +++ b/rpcs3/Emu/NP/clans_client.cpp @@ -145,10 +145,15 @@ namespace clan clan_request_ctx() { curl = curl_easy_init(); - if (curl) + + if (!curl) { - curl_easy_setopt(curl, CURLOPT_SSL_OPTIONS, CURLSSLOPT_NATIVE_CA); + clan_log.error("curl_easy_init failed"); + return; } + + CURLcode err = curl_easy_setopt(curl, CURLOPT_SSL_OPTIONS, CURLSSLOPT_NATIVE_CA); + if (err != CURLE_OK) clan_log.error("curl_easy_setopt(CURLOPT_SSL_OPTIONS): %s", curl_easy_strerror(err)); } ~clan_request_ctx() @@ -225,8 +230,8 @@ namespace clan CURL* curl = ctx->curl; ClanRequestType req_type = ClanRequestType::FUNC; - const pugi::xml_node clan = xml_body.child("clan"); - if (clan && clan.child("ticket")) + const pugi::xml_node clan = xml_body.child("clan"sv); + if (clan && clan.child("ticket"sv)) { req_type = ClanRequestType::SEC; } @@ -245,24 +250,39 @@ namespace clan std::vector response_buffer; - curl_easy_setopt(curl, CURLOPT_URL, url.c_str()); - curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, curl_write_callback); - curl_easy_setopt(curl, CURLOPT_WRITEDATA, &response_buffer); - curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, err_buf); + CURLcode err = curl_easy_setopt(curl, CURLOPT_URL, url.c_str()); + if (err != CURLE_OK) clan_log.error("curl_easy_setopt(CURLOPT_URL): %s", curl_easy_strerror(err)); + + err = curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, curl_write_callback); + if (err != CURLE_OK) clan_log.error("curl_easy_setopt(CURLOPT_WRITEFUNCTION): %s", curl_easy_strerror(err)); + + err = curl_easy_setopt(curl, CURLOPT_WRITEDATA, &response_buffer); + if (err != CURLE_OK) clan_log.error("curl_easy_setopt(CURLOPT_WRITEDATA): %s", curl_easy_strerror(err)); + + err = curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, err_buf); + if (err != CURLE_OK) clan_log.error("curl_easy_setopt(CURLOPT_ERRORBUFFER): %s", curl_easy_strerror(err)); // WARN: This disables certificate verification! - curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L); - curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L); + err = curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L); + if (err != CURLE_OK) clan_log.error("curl_easy_setopt(CURLOPT_SSL_VERIFYHOST): %s", curl_easy_strerror(err)); - curl_easy_setopt(curl, CURLOPT_POST, 1); - curl_easy_setopt(curl, CURLOPT_POSTFIELDS, xml.c_str()); - curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, xml.size()); + err = curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L); + if (err != CURLE_OK) clan_log.error("curl_easy_setopt(CURLOPT_SSL_VERIFYPEER): %s", curl_easy_strerror(err)); - const CURLcode res = curl_easy_perform(curl); + err = curl_easy_setopt(curl, CURLOPT_POST, 1); + if (err != CURLE_OK) clan_log.error("curl_easy_setopt(CURLOPT_POST): %s", curl_easy_strerror(err)); - if (res != CURLE_OK) + err = curl_easy_setopt(curl, CURLOPT_POSTFIELDS, xml.c_str()); + if (err != CURLE_OK) clan_log.error("curl_easy_setopt(CURLOPT_POSTFIELDS): %s", curl_easy_strerror(err)); + + err = curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, xml.size()); + if (err != CURLE_OK) clan_log.error("curl_easy_setopt(CURLOPT_POSTFIELDSIZE): %s", curl_easy_strerror(err)); + + err = curl_easy_perform(curl); + + if (err != CURLE_OK) { - clan_log.error("curl_easy_perform() failed: %s", curl_easy_strerror(res)); + clan_log.error("curl_easy_perform() failed: %s", curl_easy_strerror(err)); clan_log.error("Error buffer: %s", err_buf); return SCE_NP_CLANS_ERROR_BAD_REQUEST; } @@ -276,11 +296,11 @@ namespace clan return SCE_NP_CLANS_ERROR_BAD_RESPONSE; } - const pugi::xml_node clan_result = out_response.child("clan"); + const pugi::xml_node clan_result = out_response.child("clan"sv); if (!clan_result) return SCE_NP_CLANS_ERROR_BAD_RESPONSE; - const pugi::xml_attribute result = clan_result.attribute("result"); + const pugi::xml_attribute result = clan_result.attribute("result"sv); if (!result) return SCE_NP_CLANS_ERROR_BAD_RESPONSE; @@ -338,11 +358,11 @@ namespace clan return SCE_NP_CLANS_ERROR_SERVICE_UNAVAILABLE; pugi::xml_document doc = pugi::xml_document(); - pugi::xml_node clan = doc.append_child("clan"); + pugi::xml_node clan = doc.append_child("clan"sv); - clan.append_child("ticket").text().set(ticket.c_str()); - clan.append_child("start").text().set(paging.startPos); - clan.append_child("max").text().set(paging.max); + clan.append_child("ticket"sv).text().set(ticket); + clan.append_child("start"sv).text().set(paging.startPos); + clan.append_child("max"sv).text().set(paging.max); pugi::xml_document response = pugi::xml_document(); const SceNpClansError clan_res = send_request(req_id, ClanRequestAction::GetClanList, ClanManagerOperationType::VIEW, doc, response); @@ -350,37 +370,37 @@ namespace clan if (clan_res != SCE_NP_CLANS_SUCCESS) return clan_res; - const pugi::xml_node clan_result = response.child("clan"); - const pugi::xml_node list = clan_result.child("list"); + const pugi::xml_node clan_result = response.child("clan"sv); + const pugi::xml_node list = clan_result.child("list"sv); - const pugi::xml_attribute results = list.attribute("results"); + const pugi::xml_attribute results = list.attribute("results"sv); const uint32_t results_count = results.as_uint(); - const pugi::xml_attribute total = list.attribute("total"); + const pugi::xml_attribute total = list.attribute("total"sv); const uint32_t total_count = total.as_uint(); int i = 0; - for (pugi::xml_node info = list.child("info"); info; info = info.next_sibling("info"), i++) + for (pugi::xml_node info = list.child("info"sv); info; info = info.next_sibling("info"sv), i++) { - const pugi::xml_attribute id = info.attribute("id"); + const pugi::xml_attribute id = info.attribute("id"sv); const uint32_t clan_id = id.as_uint(); - const pugi::xml_node name = info.child("name"); + const pugi::xml_node name = info.child("name"sv); const std::string name_str = name.text().as_string(); - const pugi::xml_node tag = info.child("tag"); + const pugi::xml_node tag = info.child("tag"sv); const std::string tag_str = tag.text().as_string(); - const pugi::xml_node role = info.child("role"); + const pugi::xml_node role = info.child("role"sv); const int32_t role_int = role.text().as_uint(); - const pugi::xml_node status = info.child("status"); + const pugi::xml_node status = info.child("status"sv); const uint32_t status_int = status.text().as_uint(); - const pugi::xml_node onlinename = info.child("onlinename"); + const pugi::xml_node onlinename = info.child("onlinename"sv); const std::string onlinename_str = onlinename.text().as_string(); - const pugi::xml_node members = info.child("members"); + const pugi::xml_node members = info.child("members"sv); const uint32_t members_int = members.text().as_uint(); SceNpClansEntry entry = SceNpClansEntry{ @@ -411,8 +431,8 @@ namespace clan SceNpClansError clans_client::get_clan_info(u32 req_id, SceNpClanId clan_id, SceNpClansClanInfo& clan_info) { pugi::xml_document doc = pugi::xml_document(); - pugi::xml_node clan = doc.append_child("clan"); - clan.append_child("id").text().set(clan_id); + pugi::xml_node clan = doc.append_child("clan"sv); + clan.append_child("id"sv).text().set(clan_id); pugi::xml_document response = pugi::xml_document(); const SceNpClansError clan_res = send_request(req_id, ClanRequestAction::GetClanInfo, ClanManagerOperationType::VIEW, doc, response); @@ -420,14 +440,14 @@ namespace clan if (clan_res != SCE_NP_CLANS_SUCCESS) return clan_res; - const pugi::xml_node clan_result = response.child("clan"); - const pugi::xml_node info = clan_result.child("info"); + const pugi::xml_node clan_result = response.child("clan"sv); + const pugi::xml_node info = clan_result.child("info"sv); - const std::string name_str = info.child("name").text().as_string(); - const std::string tag_str = info.child("tag").text().as_string(); - const uint32_t members_int = info.child("members").text().as_uint(); - const std::string date_created_str = info.child("date-created").text().as_string(); - const std::string description_str = info.child("description").text().as_string(); + const std::string name_str = info.child("name"sv).text().as_string(); + const std::string tag_str = info.child("tag"sv).text().as_string(); + const uint32_t members_int = info.child("members"sv).text().as_uint(); + const std::string date_created_str = info.child("date-created"sv).text().as_string(); + const std::string description_str = info.child("description"sv).text().as_string(); clan_info = SceNpClansClanInfo{ .info = SceNpClansClanBasicInfo{ @@ -454,12 +474,12 @@ namespace clan return SCE_NP_CLANS_ERROR_SERVICE_UNAVAILABLE; pugi::xml_document doc = pugi::xml_document(); - pugi::xml_node clan = doc.append_child("clan"); - clan.append_child("ticket").text().set(ticket.c_str()); - clan.append_child("id").text().set(clan_id); + pugi::xml_node clan = doc.append_child("clan"sv); + clan.append_child("ticket"sv).text().set(ticket); + clan.append_child("id"sv).text().set(clan_id); const std::string jid_str = fmt::format(JID_FORMAT, np::npid_to_string(np_id)); - clan.append_child("jid").text().set(jid_str.c_str()); + clan.append_child("jid"sv).text().set(jid_str); pugi::xml_document response = pugi::xml_document(); const SceNpClansError clan_res = send_request(req_id, ClanRequestAction::GetMemberInfo, ClanManagerOperationType::VIEW, doc, response); @@ -467,10 +487,10 @@ namespace clan if (clan_res != SCE_NP_CLANS_SUCCESS) return clan_res; - const pugi::xml_node clan_result = response.child("clan"); - const pugi::xml_node member_info = clan_result.child("info"); + const pugi::xml_node clan_result = response.child("clan"sv); + const pugi::xml_node member_info = clan_result.child("info"sv); - const pugi::xml_attribute member_jid = member_info.attribute("jid"); + const pugi::xml_attribute member_jid = member_info.attribute("jid"sv); const std::string member_jid_str = member_jid.as_string(); const std::string member_username = fmt::split(member_jid_str, {"@"})[0]; @@ -484,13 +504,13 @@ namespace clan np::string_to_npid(member_username, member_npid); } - const pugi::xml_node role = member_info.child("role"); + const pugi::xml_node role = member_info.child("role"sv); const uint32_t role_int = role.text().as_uint(); - const pugi::xml_node status = member_info.child("status"); + const pugi::xml_node status = member_info.child("status"sv); const uint32_t status_int = status.text().as_uint(); - const pugi::xml_node description = member_info.child("description"); + const pugi::xml_node description = member_info.child("description"sv); const std::string description_str = description.text().as_string(); mem_info = SceNpClansMemberEntry @@ -515,11 +535,11 @@ namespace clan return SCE_NP_CLANS_ERROR_SERVICE_UNAVAILABLE; pugi::xml_document doc = pugi::xml_document(); - pugi::xml_node clan = doc.append_child("clan"); - clan.append_child("ticket").text().set(ticket.c_str()); - clan.append_child("id").text().set(clan_id); - clan.append_child("start").text().set(paging.startPos); - clan.append_child("max").text().set(paging.max); + pugi::xml_node clan = doc.append_child("clan"sv); + clan.append_child("ticket"sv).text().set(ticket); + clan.append_child("id"sv).text().set(clan_id); + clan.append_child("start"sv).text().set(paging.startPos); + clan.append_child("max"sv).text().set(paging.max); pugi::xml_document response = pugi::xml_document(); const SceNpClansError clan_res = send_request(req_id, ClanRequestAction::GetMemberList, ClanManagerOperationType::VIEW, doc, response); @@ -527,19 +547,19 @@ namespace clan if (clan_res != SCE_NP_CLANS_SUCCESS) return clan_res; - const pugi::xml_node clan_result = response.child("clan"); - const pugi::xml_node list = clan_result.child("list"); + const pugi::xml_node clan_result = response.child("clan"sv); + const pugi::xml_node list = clan_result.child("list"sv); - const pugi::xml_attribute results = list.attribute("results"); + const pugi::xml_attribute results = list.attribute("results"sv); const uint32_t results_count = results.as_uint(); - const pugi::xml_attribute total = list.attribute("total"); + const pugi::xml_attribute total = list.attribute("total"sv); const uint32_t total_count = total.as_uint(); int i = 0; - for (pugi::xml_node member_info = list.child("info"); member_info; member_info = member_info.next_sibling("info")) + for (pugi::xml_node member_info = list.child("info"sv); member_info; member_info = member_info.next_sibling("info"sv)) { - const std::string member_jid = member_info.attribute("jid").as_string(); + const std::string member_jid = member_info.attribute("jid"sv).as_string(); const std::string member_username = fmt::split(member_jid, {"@"})[0]; SceNpId member_npid; @@ -552,9 +572,9 @@ namespace clan np::string_to_npid(member_username, member_npid); } - const uint32_t role_int = member_info.child("role").text().as_uint(); - const uint32_t status_int = member_info.child("status").text().as_uint(); - const std::string description_str = member_info.child("description").text().as_string(); + const uint32_t role_int = member_info.child("role"sv).text().as_uint(); + const uint32_t status_int = member_info.child("status"sv).text().as_uint(); + const std::string description_str = member_info.child("description"sv).text().as_string(); SceNpClansMemberEntry entry = SceNpClansMemberEntry { @@ -583,11 +603,11 @@ namespace clan const std::string ticket = get_clan_ticket(nph); pugi::xml_document doc = pugi::xml_document(); - pugi::xml_node clan = doc.append_child("clan"); - clan.append_child("ticket").text().set(ticket.c_str()); - clan.append_child("id").text().set(clan_id); - clan.append_child("start").text().set(paging.startPos); - clan.append_child("max").text().set(paging.max); + pugi::xml_node clan = doc.append_child("clan"sv); + clan.append_child("ticket"sv).text().set(ticket); + clan.append_child("id"sv).text().set(clan_id); + clan.append_child("start"sv).text().set(paging.startPos); + clan.append_child("max"sv).text().set(paging.max); pugi::xml_document response = pugi::xml_document(); const SceNpClansError clan_res = send_request(req_id, ClanRequestAction::GetBlacklist, ClanManagerOperationType::VIEW, doc, response); @@ -595,19 +615,19 @@ namespace clan if (clan_res != SCE_NP_CLANS_SUCCESS) return clan_res; - const pugi::xml_node clan_result = response.child("clan"); - const pugi::xml_node list = clan_result.child("list"); + const pugi::xml_node clan_result = response.child("clan"sv); + const pugi::xml_node list = clan_result.child("list"sv); - const pugi::xml_attribute results = list.attribute("results"); + const pugi::xml_attribute results = list.attribute("results"sv); const uint32_t results_count = results.as_uint(); - const pugi::xml_attribute total = list.attribute("total"); + const pugi::xml_attribute total = list.attribute("total"sv); const uint32_t total_count = total.as_uint(); int i = 0; - for (pugi::xml_node member = list.child("entry"); member; member = member.next_sibling("entry")) + for (pugi::xml_node member = list.child("entry"sv); member; member = member.next_sibling("entry"sv)) { - const pugi::xml_node member_jid = member.child("jid"); + const pugi::xml_node member_jid = member.child("jid"sv); const std::string member_jid_str = member_jid.text().as_string(); const std::string member_username = fmt::split(member_jid_str, {"@"})[0]; @@ -618,7 +638,7 @@ namespace clan } else { - np::string_to_npid(member_username.c_str(), member_npid); + np::string_to_npid(member_username, member_npid); } SceNpClansBlacklistEntry entry = SceNpClansBlacklistEntry @@ -646,12 +666,12 @@ namespace clan return SCE_NP_CLANS_ERROR_SERVICE_UNAVAILABLE; pugi::xml_document doc = pugi::xml_document(); - pugi::xml_node clan = doc.append_child("clan"); - clan.append_child("ticket").text().set(ticket.c_str()); - clan.append_child("id").text().set(clan_id); + pugi::xml_node clan = doc.append_child("clan"sv); + clan.append_child("ticket"sv).text().set(ticket); + clan.append_child("id"sv).text().set(clan_id); const std::string jid_str = fmt::format(JID_FORMAT, np::npid_to_string(np_id)); - clan.append_child("jid").text().set(jid_str.c_str()); + clan.append_child("jid"sv).text().set(jid_str); pugi::xml_document response = pugi::xml_document(); return send_request(req_id, ClanRequestAction::RecordBlacklistEntry, ClanManagerOperationType::UPDATE, doc, response); @@ -664,12 +684,12 @@ namespace clan return SCE_NP_CLANS_ERROR_SERVICE_UNAVAILABLE; pugi::xml_document doc = pugi::xml_document(); - pugi::xml_node clan = doc.append_child("clan"); - clan.append_child("ticket").text().set(ticket.c_str()); - clan.append_child("id").text().set(clan_id); + pugi::xml_node clan = doc.append_child("clan"sv); + clan.append_child("ticket"sv).text().set(ticket); + clan.append_child("id"sv).text().set(clan_id); const std::string jid_str = fmt::format(JID_FORMAT, np::npid_to_string(np_id)); - clan.append_child("jid").text().set(jid_str.c_str()); + clan.append_child("jid"sv).text().set(jid_str); pugi::xml_document response = pugi::xml_document(); return send_request(req_id, ClanRequestAction::DeleteBlacklistEntry, ClanManagerOperationType::UPDATE, doc, response); @@ -678,16 +698,16 @@ namespace clan SceNpClansError clans_client::clan_search(u32 req_id, const SceNpClansPagingRequest& paging, const SceNpClansSearchableName& search, std::vector& clan_list, SceNpClansPagingResult& page_result) { pugi::xml_document doc = pugi::xml_document(); - pugi::xml_node clan = doc.append_child("clan"); - clan.append_child("start").text().set(paging.startPos); - clan.append_child("max").text().set(paging.max); + pugi::xml_node clan = doc.append_child("clan"sv); + clan.append_child("start"sv).text().set(paging.startPos); + clan.append_child("max"sv).text().set(paging.max); - pugi::xml_node filter = clan.append_child("filter"); - pugi::xml_node name = filter.append_child("name"); + pugi::xml_node filter = clan.append_child("filter"sv); + pugi::xml_node name = filter.append_child("name"sv); const std::string op_name = fmt::format("%s", static_cast(static_cast(search.nameSearchOp))); - name.append_attribute("op").set_value(op_name.c_str()); - name.append_attribute("value").set_value(search.name); + name.append_attribute("op"sv).set_value(op_name); + name.append_attribute("value"sv).set_value(search.name); pugi::xml_document response = pugi::xml_document(); const SceNpClansError clan_res = send_request(req_id, ClanRequestAction::ClanSearch, ClanManagerOperationType::VIEW, doc, response); @@ -695,22 +715,22 @@ namespace clan if (clan_res != SCE_NP_CLANS_SUCCESS) return clan_res; - const pugi::xml_node clan_result = response.child("clan"); - const pugi::xml_node list = clan_result.child("list"); + const pugi::xml_node clan_result = response.child("clan"sv); + const pugi::xml_node list = clan_result.child("list"sv); - const pugi::xml_attribute results = list.attribute("results"); + const pugi::xml_attribute results = list.attribute("results"sv); const uint32_t results_count = results.as_uint(); - const pugi::xml_attribute total = list.attribute("total"); + const pugi::xml_attribute total = list.attribute("total"sv); const uint32_t total_count = total.as_uint(); int i = 0; - for (pugi::xml_node node = list.child("info"); node; node = node.next_sibling("info")) + for (pugi::xml_node node = list.child("info"sv); node; node = node.next_sibling("info"sv)) { - const uint32_t clan_id = node.attribute("id").as_uint(); - const std::string name_str = node.child("name").text().as_string(); - const std::string tag_str = node.child("tag").text().as_string(); - const uint32_t members_int = node.child("members").text().as_uint(); + const uint32_t clan_id = node.attribute("id"sv).as_uint(); + const std::string name_str = node.child("name"sv).text().as_string(); + const std::string tag_str = node.child("tag"sv).text().as_string(); + const uint32_t members_int = node.child("members"sv).text().as_uint(); SceNpClansClanBasicInfo entry = SceNpClansClanBasicInfo { @@ -744,11 +764,11 @@ namespace clan return SCE_NP_CLANS_ERROR_SERVICE_UNAVAILABLE; pugi::xml_document doc = pugi::xml_document(); - pugi::xml_node clan = doc.append_child("clan"); - clan.append_child("ticket").text().set(ticket.c_str()); + pugi::xml_node clan = doc.append_child("clan"sv); + clan.append_child("ticket"sv).text().set(ticket); - clan.append_child("name").text().set(name.data()); - clan.append_child("tag").text().set(tag.data()); + clan.append_child("name"sv).text().set(name.data()); + clan.append_child("tag"sv).text().set(tag.data()); pugi::xml_document response = pugi::xml_document(); const SceNpClansError clan_res = send_request(req_id, ClanRequestAction::CreateClan, ClanManagerOperationType::UPDATE, doc, response); @@ -756,8 +776,8 @@ namespace clan if (clan_res != SCE_NP_CLANS_SUCCESS) return clan_res; - const pugi::xml_node clan_result = response.child("clan"); - const pugi::xml_attribute id = clan_result.attribute("id"); + const pugi::xml_node clan_result = response.child("clan"sv); + const pugi::xml_attribute id = clan_result.attribute("id"sv); const uint32_t clan_id_int = id.as_uint(); *clan_id = clan_id_int; @@ -772,9 +792,9 @@ namespace clan return SCE_NP_CLANS_ERROR_SERVICE_UNAVAILABLE; pugi::xml_document doc = pugi::xml_document(); - pugi::xml_node clan = doc.append_child("clan"); - clan.append_child("ticket").text().set(ticket.c_str()); - clan.append_child("id").text().set(clan_id); + pugi::xml_node clan = doc.append_child("clan"sv); + clan.append_child("ticket"sv).text().set(ticket); + clan.append_child("id"sv).text().set(clan_id); pugi::xml_document response = pugi::xml_document(); const SceNpClansError clan_res = send_request(req_id, ClanRequestAction::DisbandClan, ClanManagerOperationType::UPDATE, doc, response); @@ -792,9 +812,9 @@ namespace clan return SCE_NP_CLANS_ERROR_SERVICE_UNAVAILABLE; pugi::xml_document doc = pugi::xml_document(); - pugi::xml_node clan = doc.append_child("clan"); - clan.append_child("ticket").text().set(ticket.c_str()); - clan.append_child("id").text().set(clan_id); + pugi::xml_node clan = doc.append_child("clan"sv); + clan.append_child("ticket"sv).text().set(ticket); + clan.append_child("id"sv).text().set(clan_id); pugi::xml_document response = pugi::xml_document(); return send_request(req_id, ClanRequestAction::RequestMembership, ClanManagerOperationType::UPDATE, doc, response); @@ -807,9 +827,9 @@ namespace clan return SCE_NP_CLANS_ERROR_SERVICE_UNAVAILABLE; pugi::xml_document doc = pugi::xml_document(); - pugi::xml_node clan = doc.append_child("clan"); - clan.append_child("ticket").text().set(ticket.c_str()); - clan.append_child("id").text().set(clan_id); + pugi::xml_node clan = doc.append_child("clan"sv); + clan.append_child("ticket"sv).text().set(ticket); + clan.append_child("id"sv).text().set(clan_id); pugi::xml_document response = pugi::xml_document(); return send_request(req_id, ClanRequestAction::CancelRequestMembership, ClanManagerOperationType::UPDATE, doc, response); @@ -822,12 +842,12 @@ namespace clan return SCE_NP_CLANS_ERROR_SERVICE_UNAVAILABLE; pugi::xml_document doc = pugi::xml_document(); - pugi::xml_node clan = doc.append_child("clan"); - clan.append_child("ticket").text().set(ticket.c_str()); - clan.append_child("id").text().set(clan_id); + pugi::xml_node clan = doc.append_child("clan"sv); + clan.append_child("ticket"sv).text().set(ticket); + clan.append_child("id"sv).text().set(clan_id); const std::string jid_str = fmt::format(JID_FORMAT, np::npid_to_string(np_id)); - clan.append_child("jid").text().set(jid_str.c_str()); + clan.append_child("jid"sv).text().set(jid_str); pugi::xml_document response = pugi::xml_document(); return send_request(req_id, allow ? ClanRequestAction::AcceptMembershipRequest : ClanRequestAction::DeclineMembershipRequest, ClanManagerOperationType::UPDATE, doc, response); @@ -840,12 +860,12 @@ namespace clan return SCE_NP_CLANS_ERROR_SERVICE_UNAVAILABLE; pugi::xml_document doc = pugi::xml_document(); - pugi::xml_node clan = doc.append_child("clan"); - clan.append_child("ticket").text().set(ticket.c_str()); - clan.append_child("id").text().set(clan_id); + pugi::xml_node clan = doc.append_child("clan"sv); + clan.append_child("ticket"sv).text().set(ticket); + clan.append_child("id"sv).text().set(clan_id); const std::string jid_str = fmt::format(JID_FORMAT, np::npid_to_string(np_id)); - clan.append_child("jid").text().set(jid_str.c_str()); + clan.append_child("jid"sv).text().set(jid_str); pugi::xml_document response = pugi::xml_document(); return send_request(req_id, ClanRequestAction::SendInvitation, ClanManagerOperationType::UPDATE, doc, response); @@ -858,12 +878,12 @@ namespace clan return SCE_NP_CLANS_ERROR_SERVICE_UNAVAILABLE; pugi::xml_document doc = pugi::xml_document(); - pugi::xml_node clan = doc.append_child("clan"); - clan.append_child("ticket").text().set(ticket.c_str()); - clan.append_child("id").text().set(clan_id); + pugi::xml_node clan = doc.append_child("clan"sv); + clan.append_child("ticket"sv).text().set(ticket); + clan.append_child("id"sv).text().set(clan_id); const std::string jid_str = fmt::format(JID_FORMAT, np::npid_to_string(np_id)); - clan.append_child("jid").text().set(jid_str.c_str()); + clan.append_child("jid"sv).text().set(jid_str); pugi::xml_document response = pugi::xml_document(); return send_request(req_id, ClanRequestAction::CancelInvitation, ClanManagerOperationType::UPDATE, doc, response); @@ -876,9 +896,9 @@ namespace clan return SCE_NP_CLANS_ERROR_SERVICE_UNAVAILABLE; pugi::xml_document doc = pugi::xml_document(); - pugi::xml_node clan = doc.append_child("clan"); - clan.append_child("ticket").text().set(ticket.c_str()); - clan.append_child("id").text().set(clan_id); + pugi::xml_node clan = doc.append_child("clan"sv); + clan.append_child("ticket"sv).text().set(ticket); + clan.append_child("id"sv).text().set(clan_id); pugi::xml_document response = pugi::xml_document(); return send_request(req_id, accept ? ClanRequestAction::AcceptInvitation : ClanRequestAction::DeclineInvitation, ClanManagerOperationType::UPDATE, doc, response); @@ -891,17 +911,17 @@ namespace clan return SCE_NP_CLANS_ERROR_SERVICE_UNAVAILABLE; pugi::xml_document doc = pugi::xml_document(); - pugi::xml_node clan = doc.append_child("clan"); - clan.append_child("ticket").text().set(ticket.c_str()); - clan.append_child("id").text().set(clan_id); + pugi::xml_node clan = doc.append_child("clan"sv); + clan.append_child("ticket"sv).text().set(ticket); + clan.append_child("id"sv).text().set(clan_id); - pugi::xml_node role = clan.append_child("onlinename"); - role.text().set(np::npid_to_string(nph.get_npid()).c_str()); + pugi::xml_node role = clan.append_child("onlinename"sv); + role.text().set(np::npid_to_string(nph.get_npid())); - pugi::xml_node description = clan.append_child("description"); + pugi::xml_node description = clan.append_child("description"sv); description.text().set(info.description); - pugi::xml_node status = clan.append_child("bin-attr1"); + pugi::xml_node status = clan.append_child("bin-attr1"sv); byte bin_attr_1[SCE_NP_CLANS_MEMBER_BINARY_ATTRIBUTE1_MAX_SIZE * 2 + 1] = {0}; uint32_t bin_attr_1_size = UINT32_MAX; @@ -913,10 +933,10 @@ namespace clan // `reinterpret_cast` used to let the compiler select the correct overload of `set` status.text().set(reinterpret_cast(bin_attr_1)); - pugi::xml_node allow_msg = clan.append_child("allow-msg"); + pugi::xml_node allow_msg = clan.append_child("allow-msg"sv); allow_msg.text().set(static_cast(info.allowMsg)); - pugi::xml_node size = clan.append_child("size"); + pugi::xml_node size = clan.append_child("size"sv); size.text().set(info.binData1Size); pugi::xml_document response = pugi::xml_document(); @@ -930,13 +950,13 @@ namespace clan return SCE_NP_CLANS_ERROR_SERVICE_UNAVAILABLE; pugi::xml_document doc = pugi::xml_document(); - pugi::xml_node clan = doc.append_child("clan"); - clan.append_child("ticket").text().set(ticket.c_str()); - clan.append_child("id").text().set(clan_id); + pugi::xml_node clan = doc.append_child("clan"sv); + clan.append_child("ticket"sv).text().set(ticket); + clan.append_child("id"sv).text().set(clan_id); // TODO: implement binary and integer attributes (not implemented in server yet) - pugi::xml_node description = clan.append_child("description"); + pugi::xml_node description = clan.append_child("description"sv); description.text().set(info.description); pugi::xml_document response = pugi::xml_document(); @@ -950,9 +970,9 @@ namespace clan return SCE_NP_CLANS_ERROR_SERVICE_UNAVAILABLE; pugi::xml_document doc = pugi::xml_document(); - pugi::xml_node clan = doc.append_child("clan"); - clan.append_child("ticket").text().set(ticket.c_str()); - clan.append_child("id").text().set(clan_id); + pugi::xml_node clan = doc.append_child("clan"sv); + clan.append_child("ticket"sv).text().set(ticket); + clan.append_child("id"sv).text().set(clan_id); pugi::xml_document response = pugi::xml_document(); return send_request(req_id, ClanRequestAction::JoinClan, ClanManagerOperationType::UPDATE, doc, response); @@ -965,9 +985,9 @@ namespace clan return SCE_NP_CLANS_ERROR_SERVICE_UNAVAILABLE; pugi::xml_document doc = pugi::xml_document(); - pugi::xml_node clan = doc.append_child("clan"); - clan.append_child("ticket").text().set(ticket.c_str()); - clan.append_child("id").text().set(clan_id); + pugi::xml_node clan = doc.append_child("clan"sv); + clan.append_child("ticket"sv).text().set(ticket); + clan.append_child("id"sv).text().set(clan_id); pugi::xml_document response = pugi::xml_document(); return send_request(req_id, ClanRequestAction::LeaveClan, ClanManagerOperationType::UPDATE, doc, response); @@ -980,12 +1000,12 @@ namespace clan return SCE_NP_CLANS_ERROR_SERVICE_UNAVAILABLE; pugi::xml_document doc = pugi::xml_document(); - pugi::xml_node clan = doc.append_child("clan"); - clan.append_child("ticket").text().set(ticket.c_str()); - clan.append_child("id").text().set(clan_id); + pugi::xml_node clan = doc.append_child("clan"sv); + clan.append_child("ticket"sv).text().set(ticket); + clan.append_child("id"sv).text().set(clan_id); const std::string jid_str = fmt::format(JID_FORMAT, np::npid_to_string(np_id)); - clan.append_child("jid").text().set(jid_str.c_str()); + clan.append_child("jid"sv).text().set(jid_str); pugi::xml_document response = pugi::xml_document(); return send_request(req_id, ClanRequestAction::KickMember, ClanManagerOperationType::UPDATE, doc, response); @@ -998,14 +1018,14 @@ namespace clan return SCE_NP_CLANS_ERROR_SERVICE_UNAVAILABLE; pugi::xml_document doc = pugi::xml_document(); - pugi::xml_node clan = doc.append_child("clan"); - clan.append_child("ticket").text().set(ticket.c_str()); - clan.append_child("id").text().set(clan_id); + pugi::xml_node clan = doc.append_child("clan"sv); + clan.append_child("ticket"sv).text().set(ticket); + clan.append_child("id"sv).text().set(clan_id); const std::string jid_str = fmt::format(JID_FORMAT, np::npid_to_string(np_id)); - clan.append_child("jid").text().set(jid_str.c_str()); + clan.append_child("jid"sv).text().set(jid_str); - pugi::xml_node role_node = clan.append_child("role"); + pugi::xml_node role_node = clan.append_child("role"sv); role_node.text().set(static_cast(role)); pugi::xml_document response = pugi::xml_document(); @@ -1019,11 +1039,11 @@ namespace clan return SCE_NP_CLANS_ERROR_SERVICE_UNAVAILABLE; pugi::xml_document doc = pugi::xml_document(); - pugi::xml_node clan = doc.append_child("clan"); - clan.append_child("ticket").text().set(ticket.c_str()); - clan.append_child("id").text().set(clan_id); - clan.append_child("start").text().set(paging.startPos); - clan.append_child("max").text().set(paging.max); + pugi::xml_node clan = doc.append_child("clan"sv); + clan.append_child("ticket"sv).text().set(ticket); + clan.append_child("id"sv).text().set(clan_id); + clan.append_child("start"sv).text().set(paging.startPos); + clan.append_child("max"sv).text().set(paging.max); pugi::xml_document response = pugi::xml_document(); const SceNpClansError clan_res = send_request(req_id, ClanRequestAction::RetrieveAnnouncements, ClanManagerOperationType::VIEW, doc, response); @@ -1031,25 +1051,25 @@ namespace clan if (clan_res != SCE_NP_CLANS_SUCCESS) return clan_res; - const pugi::xml_node clan_result = response.child("clan"); - const pugi::xml_node list = clan_result.child("list"); + const pugi::xml_node clan_result = response.child("clan"sv); + const pugi::xml_node list = clan_result.child("list"sv); - const pugi::xml_attribute results = list.attribute("results"); + const pugi::xml_attribute results = list.attribute("results"sv); const uint32_t results_count = results.as_uint(); - const pugi::xml_attribute total = list.attribute("total"); + const pugi::xml_attribute total = list.attribute("total"sv); const uint32_t total_count = total.as_uint(); int i = 0; - for (pugi::xml_node node = list.child("msg-info"); node; node = node.next_sibling("msg-info")) + for (pugi::xml_node node = list.child("msg-info"sv); node; node = node.next_sibling("msg-info"sv)) { - const pugi::xml_attribute id = node.attribute("id"); + const pugi::xml_attribute id = node.attribute("id"sv); const uint32_t msg_id = id.as_uint(); - const std::string subject_str = node.child("subject").text().as_string(); - const std::string msg_str = node.child("msg").text().as_string(); - const std::string author_jid = node.child("jid").text().as_string(); - const std::string msg_date = node.child("msg-date").text().as_string(); + const std::string subject_str = node.child("subject"sv).text().as_string(); + const std::string msg_str = node.child("msg"sv).text().as_string(); + const std::string author_jid = node.child("jid"sv).text().as_string(); + const std::string msg_date = node.child("msg-date"sv).text().as_string(); SceNpId author_npid; const std::string author_username = fmt::split(author_jid, {"@"})[0]; @@ -1099,17 +1119,17 @@ namespace clan return SCE_NP_CLANS_ERROR_SERVICE_UNAVAILABLE; pugi::xml_document doc = pugi::xml_document(); - pugi::xml_node clan = doc.append_child("clan"); - clan.append_child("ticket").text().set(ticket.c_str()); - clan.append_child("id").text().set(clan_id); + pugi::xml_node clan = doc.append_child("clan"sv); + clan.append_child("ticket"sv).text().set(ticket); + clan.append_child("id"sv).text().set(clan_id); - pugi::xml_node subject = clan.append_child("subject"); + pugi::xml_node subject = clan.append_child("subject"sv); subject.text().set(announcement.subject); - pugi::xml_node msg = clan.append_child("msg"); + pugi::xml_node msg = clan.append_child("msg"sv); msg.text().set(announcement.body); - pugi::xml_node expire_date = clan.append_child("expire-date"); + pugi::xml_node expire_date = clan.append_child("expire-date"sv); expire_date.text().set(duration); pugi::xml_document response = pugi::xml_document(); @@ -1118,8 +1138,8 @@ namespace clan if (clan_res != SCE_NP_CLANS_SUCCESS) return clan_res; - const pugi::xml_node clan_result = response.child("clan"); - const pugi::xml_node msg_id_node = clan_result.child("id"); + const pugi::xml_node clan_result = response.child("clan"sv); + const pugi::xml_node msg_id_node = clan_result.child("id"sv); msg_id = msg_id_node.text().as_uint(); return SCE_NP_CLANS_SUCCESS; @@ -1132,10 +1152,10 @@ namespace clan return SCE_NP_CLANS_ERROR_SERVICE_UNAVAILABLE; pugi::xml_document doc = pugi::xml_document(); - pugi::xml_node clan = doc.append_child("clan"); - clan.append_child("ticket").text().set(ticket.c_str()); - clan.append_child("id").text().set(clan_id); - clan.append_child("msg-id").text().set(announcement_id); + pugi::xml_node clan = doc.append_child("clan"sv); + clan.append_child("ticket"sv).text().set(ticket); + clan.append_child("id"sv).text().set(clan_id); + clan.append_child("msg-id"sv).text().set(announcement_id); pugi::xml_document response = pugi::xml_document(); return send_request(req_id, ClanRequestAction::DeleteAnnouncement, ClanManagerOperationType::UPDATE, doc, response); diff --git a/rpcs3/Emu/NP/ip_address.cpp b/rpcs3/Emu/NP/ip_address.cpp index 7d9d972d7c..fe0124c74d 100644 --- a/rpcs3/Emu/NP/ip_address.cpp +++ b/rpcs3/Emu/NP/ip_address.cpp @@ -66,7 +66,7 @@ namespace np return sockaddr_ipv6; } - u32 register_ip(const std::string& ip_bytes) + u32 register_ip(std::string_view ip_bytes) { if (ip_bytes.size() == 4) { diff --git a/rpcs3/Emu/NP/ip_address.h b/rpcs3/Emu/NP/ip_address.h index f019e299cb..39205a007d 100644 --- a/rpcs3/Emu/NP/ip_address.h +++ b/rpcs3/Emu/NP/ip_address.h @@ -66,7 +66,7 @@ namespace np std::vector> ipv4_to_ipv6; }; - u32 register_ip(const std::string& ip_bytes); + u32 register_ip(std::string_view ip_bytes); enum class IPV6_SUPPORT : u8 { diff --git a/rpcs3/Emu/NP/np_handler.cpp b/rpcs3/Emu/NP/np_handler.cpp index 0d628e299e..6b4ada8e84 100644 --- a/rpcs3/Emu/NP/np_handler.cpp +++ b/rpcs3/Emu/NP/np_handler.cpp @@ -1303,7 +1303,7 @@ namespace np } } - bool np_handler::error_and_disconnect(const std::string& error_msg) + bool np_handler::error_and_disconnect(std::string_view error_msg) { rpcn_log.error("%s", error_msg); rpcn.reset(); diff --git a/rpcs3/Emu/NP/np_handler.h b/rpcs3/Emu/NP/np_handler.h index 575fe68941..940ad6eac3 100644 --- a/rpcs3/Emu/NP/np_handler.h +++ b/rpcs3/Emu/NP/np_handler.h @@ -267,7 +267,7 @@ namespace np error_code abort_request(u32 req_id); // For signaling - void req_sign_infos(const std::string& npid, u32 conn_id); + void req_sign_infos(std::string_view npid, u32 conn_id); // For UPNP void upnp_add_port_mapping(u16 internal_port, std::string_view protocol); @@ -297,7 +297,7 @@ namespace np // Various generic helpers bool discover_ip_address(); bool discover_ether_address(); - bool error_and_disconnect(const std::string& error_msg); + bool error_and_disconnect(std::string_view error_msg); // Notification handlers void notif_user_joined_room(vec_stream& noti); diff --git a/rpcs3/Emu/NP/np_requests.cpp b/rpcs3/Emu/NP/np_requests.cpp index d9dd6dc337..fb1fb0cfe8 100644 --- a/rpcs3/Emu/NP/np_requests.cpp +++ b/rpcs3/Emu/NP/np_requests.cpp @@ -814,7 +814,7 @@ namespace np cb_info_opt->queue_callback(req_id, 0, error_code, 0); } - void np_handler::req_sign_infos(const std::string& npid, u32 conn_id) + void np_handler::req_sign_infos(std::string_view npid, u32 conn_id) { const u32 req_id = get_req_id(REQUEST_ID_HIGH::MISC); { diff --git a/rpcs3/Emu/NP/rpcn_client.cpp b/rpcs3/Emu/NP/rpcn_client.cpp index 96ab505abf..da590278f5 100644 --- a/rpcs3/Emu/NP/rpcn_client.cpp +++ b/rpcs3/Emu/NP/rpcn_client.cpp @@ -967,7 +967,7 @@ namespace rpcn server_info_received = false; } - bool rpcn_client::connect(const std::string& host) + bool rpcn_client::connect(std::string_view host) { rpcn_log.warning("connect: Attempting to connect"); @@ -1172,7 +1172,7 @@ namespace rpcn return true; } - bool rpcn_client::login(const std::string& npid, const std::string& password, const std::string& token) + bool rpcn_client::login(std::string_view npid, std::string_view password, std::string_view token) { if (npid.empty()) { @@ -1338,7 +1338,7 @@ namespace rpcn return error; } - ErrorType rpcn_client::resend_token(const std::string& npid, const std::string& password) + ErrorType rpcn_client::resend_token(std::string_view npid, std::string_view password) { if (authentified) { @@ -1469,7 +1469,7 @@ namespace rpcn return error; } - std::optional rpcn_client::add_friend(const std::string& friend_username) + std::optional rpcn_client::add_friend(std::string_view friend_username) { std::vector data; std::copy(friend_username.begin(), friend_username.end(), std::back_inserter(data)); @@ -1494,7 +1494,7 @@ namespace rpcn return error; } - bool rpcn_client::remove_friend(const std::string& friend_username) + bool rpcn_client::remove_friend(std::string_view friend_username) { std::vector data; std::copy(friend_username.begin(), friend_username.end(), std::back_inserter(data)); @@ -2159,7 +2159,7 @@ namespace rpcn return forge_request_with_com_id(serialized, communication_id, CommandType::SendRoomMessage, req_id); } - bool rpcn_client::req_sign_infos(u32 req_id, const std::string& npid) + bool rpcn_client::req_sign_infos(u32 req_id, std::string_view npid) { std::vector data; std::copy(npid.begin(), npid.end(), std::back_inserter(data)); @@ -2168,7 +2168,7 @@ namespace rpcn return forge_send(CommandType::RequestSignalingInfos, req_id, data); } - bool rpcn_client::req_ticket(u32 req_id, const std::string& service_id, const std::vector& cookie) + bool rpcn_client::req_ticket(u32 req_id, std::string_view service_id, const std::vector& cookie) { std::vector data; std::copy(service_id.begin(), service_id.end(), std::back_inserter(data)); @@ -2859,7 +2859,7 @@ namespace rpcn memcpy(data.data(), com_id_str.data(), COMMUNICATION_ID_SIZE); } - bool rpcn_client::forge_request_with_com_id(const std::string& serialized_data, const SceNpCommunicationId& com_id, CommandType command, u64 packet_id) + bool rpcn_client::forge_request_with_com_id(std::string_view serialized_data, const SceNpCommunicationId& com_id, CommandType command, u64 packet_id) { const usz bufsize = serialized_data.size(); std::vector data(COMMUNICATION_ID_SIZE + sizeof(u32) + bufsize); @@ -2872,7 +2872,7 @@ namespace rpcn return forge_send(command, packet_id, data); } - bool rpcn_client::forge_request_with_data(const std::string& serialized_data, CommandType command, u64 packet_id) + bool rpcn_client::forge_request_with_data(std::string_view serialized_data, CommandType command, u64 packet_id) { const usz bufsize = serialized_data.size(); std::vector data(sizeof(u32) + bufsize); @@ -2897,14 +2897,14 @@ namespace rpcn return packet; } - bool rpcn_client::error_and_disconnect(const std::string& error_msg) + bool rpcn_client::error_and_disconnect(std::string_view error_msg) { connected = false; rpcn_log.error("%s", error_msg); return false; } - bool rpcn_client::error_and_disconnect_notice(const std::string& error_msg) + bool rpcn_client::error_and_disconnect_notice(std::string_view error_msg) { connected = false; rpcn_log.notice("%s", error_msg); diff --git a/rpcs3/Emu/NP/rpcn_client.h b/rpcs3/Emu/NP/rpcn_client.h index daa60d90e7..e37b1bb2e0 100644 --- a/rpcs3/Emu/NP/rpcn_client.h +++ b/rpcs3/Emu/NP/rpcn_client.h @@ -166,7 +166,7 @@ public: vec.push_back(*(reinterpret_cast(&value) + index)); } } - void insert_string(const std::string& str) const + void insert_string(std::string_view str) const { std::copy(str.begin(), str.end(), std::back_inserter(vec)); vec.push_back(0); @@ -277,8 +277,8 @@ namespace rpcn bool send_packet(const std::vector& packet); private: - bool connect(const std::string& host); - bool login(const std::string& npid, const std::string& password, const std::string& token); + bool connect(std::string_view host); + bool login(std::string_view npid, std::string_view password, std::string_view token); void disconnect(); public: @@ -297,12 +297,12 @@ namespace rpcn void remove_friend_cb(friend_cb_func, void* cb_param); ErrorType create_user(std::string_view npid, std::string_view password, std::string_view online_name, std::string_view avatar_url, std::string_view email); - ErrorType resend_token(const std::string& npid, const std::string& password); + ErrorType resend_token(std::string_view npid, std::string_view password); ErrorType send_reset_token(std::string_view npid, std::string_view email); ErrorType reset_password(std::string_view npid, std::string_view token, std::string_view password); ErrorType delete_account(); - std::optional add_friend(const std::string& friend_username); - bool remove_friend(const std::string& friend_username); + std::optional add_friend(std::string_view friend_username); + bool remove_friend(std::string_view friend_username); u32 get_num_friends() const; u32 get_num_blocks() const; @@ -346,8 +346,8 @@ namespace rpcn bool set_userinfo(u32 req_id, const SceNpCommunicationId& communication_id, const SceNpMatching2SetUserInfoRequest* req); bool ping_room_owner(u32 req_id, const SceNpCommunicationId& communication_id, u64 room_id); bool send_room_message(u32 req_id, const SceNpCommunicationId& communication_id, const SceNpMatching2SendRoomMessageRequest* req); - bool req_sign_infos(u32 req_id, const std::string& npid); - bool req_ticket(u32 req_id, const std::string& service_id, const std::vector& cookie); + bool req_sign_infos(u32 req_id, std::string_view npid); + bool req_ticket(u32 req_id, std::string_view service_id, const std::vector& cookie); bool send_message(const message_data& msg_data, const std::set& npids); bool get_board_infos(u32 req_id, const SceNpCommunicationId& communication_id, SceNpScoreBoardId board_id); bool record_score(u32 req_id, const SceNpCommunicationId& communication_id, SceNpScoreBoardId board_id, SceNpScorePcId char_id, SceNpScoreValue score, const std::optional comment, const std::optional> score_data); @@ -396,12 +396,12 @@ namespace rpcn std::vector forge_request(rpcn::CommandType command, u64 packet_id, const std::vector& data) const; bool forge_send(rpcn::CommandType command, u64 packet_id, const std::vector& data); - bool forge_request_with_com_id(const std::string& serialized_data, const SceNpCommunicationId& com_id, CommandType command, u64 packet_id); - bool forge_request_with_data(const std::string& serialized_data, CommandType command, u64 packet_id); + bool forge_request_with_com_id(std::string_view serialized_data, const SceNpCommunicationId& com_id, CommandType command, u64 packet_id); + bool forge_request_with_data(std::string_view serialized_data, CommandType command, u64 packet_id); bool forge_send_reply(rpcn::CommandType command, u64 packet_id, const std::vector& data, std::vector& reply_data); - bool error_and_disconnect(const std::string& error_mgs); - bool error_and_disconnect_notice(const std::string& error_msg); + bool error_and_disconnect(std::string_view error_mgs); + bool error_and_disconnect_notice(std::string_view error_msg); std::string get_wolfssl_error(WOLFSSL* wssl, int error) const; diff --git a/rpcs3/Emu/NP/upnp_handler.cpp b/rpcs3/Emu/NP/upnp_handler.cpp index c19e30d441..4ebd9084d1 100644 --- a/rpcs3/Emu/NP/upnp_handler.cpp +++ b/rpcs3/Emu/NP/upnp_handler.cpp @@ -119,13 +119,14 @@ void upnp_handler::add_port_redir(const std::string& addr, u16 internal_port, st if (!m_active) return; + const std::string protocol_str(protocol); + std::lock_guard lock(m_mutex); - if (m_bindings[std::string(protocol)].contains(internal_port)) + if (m_bindings[protocol_str].contains(internal_port)) return; const std::string internal_port_str = fmt::format("%d", internal_port); - const std::string protocol_str(protocol); const u32 max_port = std::min(static_cast(internal_port) + 100, 0xFFFFu); int res = 0; diff --git a/rpcs3/Emu/RSX/Common/TextureUtils.cpp b/rpcs3/Emu/RSX/Common/TextureUtils.cpp index 3cfc3aad3b..96b9e3e8bb 100644 --- a/rpcs3/Emu/RSX/Common/TextureUtils.cpp +++ b/rpcs3/Emu/RSX/Common/TextureUtils.cpp @@ -595,8 +595,7 @@ namespace for (int row = 0; row < row_count; ++row) { - rsx::memory_transfer_cmd cmd{ dst_, src_, width_in_bytes }; - result.push_back(cmd); + result.push_back(rsx::memory_transfer_cmd{ dst_, src_, width_in_bytes }); src_ += src_pitch_in_bytes; dst_ += dst_pitch_in_bytes; } diff --git a/rpcs3/Emu/RSX/Common/surface_store.h b/rpcs3/Emu/RSX/Common/surface_store.h index 4b3aaa0605..7dce727402 100644 --- a/rpcs3/Emu/RSX/Common/surface_store.h +++ b/rpcs3/Emu/RSX/Common/surface_store.h @@ -1197,7 +1197,7 @@ namespace rsx info.src_area.width = width; } - result.push_back(info); + result.push_back(std::move(info)); } }; diff --git a/rpcs3/Emu/RSX/Common/texture_cache.h b/rpcs3/Emu/RSX/Common/texture_cache.h index 295090a5bc..81ff75b9a8 100644 --- a/rpcs3/Emu/RSX/Common/texture_cache.h +++ b/rpcs3/Emu/RSX/Common/texture_cache.h @@ -3180,7 +3180,7 @@ namespace rsx subres.pitch_in_block = full_width; subres.depth = 1; subres.data = { vm::_ptr(image_base), static_cast::size_type>(src.pitch * image_height) }; - subresource_layout.push_back(subres); + subresource_layout.push_back(std::move(subres)); const u32 gcm_format = helpers::get_sized_blit_format(src_is_argb8, dst_is_depth_surface, is_format_convert); const auto rsx_range = address_range32::start_length(image_base, src.pitch * image_height); @@ -3321,7 +3321,7 @@ namespace rsx subres.pitch_in_block = pitch_in_block; subres.depth = 1; subres.data = { vm::get_super_ptr(dst_base_address), static_cast::size_type>(dst.pitch * dst_dimensions.height) }; - subresource_layout.push_back(subres); + subresource_layout.push_back(std::move(subres)); cached_dest = upload_image_from_cpu(cmd, rsx_range, dst_dimensions.width, dst_dimensions.height, 1, 1, dst.pitch, preferred_dst_format, rsx::texture_upload_context::blit_engine_dst, subresource_layout, diff --git a/rpcs3/Emu/RSX/Common/texture_cache_helpers.h b/rpcs3/Emu/RSX/Common/texture_cache_helpers.h index 8898830034..510dc3f136 100644 --- a/rpcs3/Emu/RSX/Common/texture_cache_helpers.h +++ b/rpcs3/Emu/RSX/Common/texture_cache_helpers.h @@ -968,9 +968,10 @@ namespace rsx // Check for upscaling if requested if (apply_upscaling) { + const auto base_mip = as_surface_type(sections.front()); auto& mip = sections.back(); std::tie(mip.dst_w, mip.dst_h) = rsx::apply_resolution_scale( - as_surface_type(mip)->resolution_scaling_config, + base_mip->resolution_scaling_config, mip.dst_w, mip.dst_h, level0_attr.width, level0_attr.height); } diff --git a/rpcs3/Emu/RSX/GL/GLDraw.cpp b/rpcs3/Emu/RSX/GL/GLDraw.cpp index 57a9b08a17..cc16350a5b 100644 --- a/rpcs3/Emu/RSX/GL/GLDraw.cpp +++ b/rpcs3/Emu/RSX/GL/GLDraw.cpp @@ -8,6 +8,8 @@ namespace gl { + extern GLenum tex_min_filter(rsx::texture_minify_filter min_filter); + inline GLenum comparison_op(rsx::comparison_function op) { return static_cast(op); @@ -410,8 +412,26 @@ void GLGSRender::load_texture_env() if (is_depth_reconstructed || is_snorm) { + GLint adjusted_min_filter = GL_NEAREST; + if (!is_depth_reconstructed) [[ unlikely ]] + { + switch (gl::tex_min_filter(tex.min_filter())) + { + default: + break; + case GL_LINEAR_MIPMAP_LINEAR: + case GL_LINEAR_MIPMAP_NEAREST: + case GL_NEAREST_MIPMAP_LINEAR: + case GL_NEAREST_MIPMAP_NEAREST: + // This is a hack and an unfortunate one at that as there is no feasible workaround. + // Doing full trilinear filtering in a shader is just dumb, approximate it instead with NEAREST_NEAREST. + adjusted_min_filter = GL_NEAREST_MIPMAP_NEAREST; + break; + } + } + // Depth format redirected to BGRA8 resample stage. Do not filter to avoid bits leaking. - m_fs_sampler_states[i].set_parameteri(GL_TEXTURE_MIN_FILTER, GL_NEAREST); + m_fs_sampler_states[i].set_parameteri(GL_TEXTURE_MIN_FILTER, adjusted_min_filter); m_fs_sampler_states[i].set_parameteri(GL_TEXTURE_MAG_FILTER, GL_NEAREST); } } @@ -469,92 +489,6 @@ void GLGSRender::load_texture_env() } void GLGSRender::bind_texture_env() -{ - // Bind textures and resolve external copy operations - gl::command_context cmd{ gl_state }; - - for (u32 textures_ref = current_fp_metadata.referenced_textures_mask, i = 0; textures_ref; textures_ref >>= 1, ++i) - { - if (!(textures_ref & 1)) - { - continue; - } - - gl::texture_view* view = nullptr; - auto sampler_state = static_cast(fs_sampler_state[i].get()); - - if (rsx::method_registers.fragment_textures[i].enabled() && - sampler_state->validate()) - { - if (view = sampler_state->image_handle; !view) [[unlikely]] - { - view = m_gl_texture_cache.create_temporary_subresource(cmd, sampler_state->external_subresource_desc); - } - } - - if (view) [[likely]] - { - view->bind(cmd, GL_FRAGMENT_TEXTURES_START + i); - - if (current_fragment_program.texture_state.redirected_textures & (1 << i)) - { - auto root_texture = static_cast(view->image()); - auto stencil_view = root_texture->get_view(rsx::default_remap_vector.with_encoding(gl::GL_REMAP_IDENTITY), gl::image_aspect::stencil); - stencil_view->bind(cmd, GL_STENCIL_MIRRORS_START + i); - } - } - else - { - const auto target = gl::get_target(current_fragment_program.get_texture_dimension(i)); - cmd->bind_texture(GL_FRAGMENT_TEXTURES_START + i, target, m_null_textures[target]->id()); - - if (current_fragment_program.texture_state.redirected_textures & (1 << i)) - { - cmd->bind_texture(GL_STENCIL_MIRRORS_START + i, target, m_null_textures[target]->id()); - } - } - } - - for (u32 textures_ref = current_vp_metadata.referenced_textures_mask, i = 0; textures_ref; textures_ref >>= 1, ++i) - { - if (!(textures_ref & 1)) - { - continue; - } - - auto sampler_state = static_cast(vs_sampler_state[i].get()); - gl::texture_view* view = nullptr; - - if (rsx::method_registers.vertex_textures[i].enabled() && - sampler_state->validate()) - { - if (view = sampler_state->image_handle; !view) - { - view = m_gl_texture_cache.create_temporary_subresource(cmd, sampler_state->external_subresource_desc); - } - } - - if (view) [[likely]] - { - view->bind(cmd, GL_VERTEX_TEXTURES_START + i); - } - else - { - cmd->bind_texture(GL_VERTEX_TEXTURES_START + i, GL_TEXTURE_2D, GL_NONE); - } - } - - if (current_fragment_program.ctrl & RSX_SHADER_CONTROL_EMULATE_DEPTH_COMPARE) - { - ensure(m_rtts.m_bound_depth_stencil.first, "Invalid FBO setup"); - gl::insert_texture_barrier(); - - auto view = m_rtts.m_bound_depth_stencil.second->get_view(rsx::default_remap_vector, gl::image_aspect::depth); - view->bind(cmd, GL_TEMP_IMAGE_SLOT(0)); - } -} - -void GLGSRender::bind_interpreter_texture_env() { // Bind textures and resolve external copy operations gl::command_context cmd{ gl_state }; @@ -580,13 +514,18 @@ void GLGSRender::bind_interpreter_texture_env() } } - if (!primary_view) + if (const bool is_redirected = (current_fragment_program.texture_state.redirected_textures & (1 << i)); + !primary_view) { const auto target = gl::get_target(current_fragment_program.get_texture_dimension(i)); primary_view = m_null_textures[target]->get_view(rsx::default_remap_vector); - stencil_mirror = primary_view; + + if (is_redirected) + { + stencil_mirror = primary_view; + } } - else if (current_fragment_program.texture_state.redirected_textures & (1 << i)) + else if (is_redirected) { auto root_texture = static_cast(primary_view->image()); stencil_mirror = root_texture->get_view(rsx::default_remap_vector.with_encoding(gl::GL_REMAP_IDENTITY), gl::image_aspect::stencil); @@ -639,6 +578,15 @@ void GLGSRender::bind_interpreter_texture_env() { m_shader_interpreter.flush_texture_bindings(); } + + if (current_fragment_program.ctrl & RSX_SHADER_CONTROL_EMULATE_DEPTH_COMPARE) + { + ensure(m_rtts.m_bound_depth_stencil.first, "Invalid FBO setup"); + gl::insert_texture_barrier(); + + auto view = m_rtts.m_bound_depth_stencil.second->get_view(rsx::default_remap_vector, gl::image_aspect::depth); + view->bind(cmd, GL_TEMP_IMAGE_SLOT(0)); + } } void GLGSRender::emit_geometry(u32 sub_index) @@ -729,7 +677,7 @@ void GLGSRender::emit_geometry(u32 sub_index) if (!upload_info.index_info) { - if (draw_call.is_trivial_instanced_draw) + if (draw_call.is_trivial_instanced_draw && backend_config.supports_hw_instanced_rendering) { glDrawArraysInstanced(draw_mode, 0, upload_info.vertex_draw_count, draw_call.pass_count()); } @@ -801,7 +749,7 @@ void GLGSRender::emit_geometry(u32 sub_index) m_index_ring_buffer->bind(); - if (draw_call.is_trivial_instanced_draw) + if (draw_call.is_trivial_instanced_draw && backend_config.supports_hw_instanced_rendering) { glDrawElementsInstanced(draw_mode, upload_info.vertex_draw_count, index_type, reinterpret_cast(u64{ index_offset }), draw_call.pass_count()); } @@ -928,7 +876,7 @@ void GLGSRender::end() { emit_geometry(subdraw++); - if (draw_call.is_trivial_instanced_draw) + if (draw_call.is_trivial_instanced_draw && backend_config.supports_hw_instanced_rendering) { // We already completed. End the draw. draw_call.end(); diff --git a/rpcs3/Emu/RSX/GL/GLFragmentProgram.cpp b/rpcs3/Emu/RSX/GL/GLFragmentProgram.cpp index ab63cb2ea1..c445a0532f 100644 --- a/rpcs3/Emu/RSX/GL/GLFragmentProgram.cpp +++ b/rpcs3/Emu/RSX/GL/GLFragmentProgram.cpp @@ -21,7 +21,7 @@ std::string GLFragmentDecompilerThread::getFunction(FUNCTION f) return glsl::getFunctionImpl(f); } -std::string GLFragmentDecompilerThread::compareFunction(COMPARE f, const std::string &Op0, const std::string &Op1) +std::string GLFragmentDecompilerThread::compareFunction(COMPARE f, std::string_view Op0, std::string_view Op1) { return glsl::compareFunctionImpl(f, Op0, Op1); } diff --git a/rpcs3/Emu/RSX/GL/GLFragmentProgram.h b/rpcs3/Emu/RSX/GL/GLFragmentProgram.h index e8fc2ed6f1..29e461793a 100644 --- a/rpcs3/Emu/RSX/GL/GLFragmentProgram.h +++ b/rpcs3/Emu/RSX/GL/GLFragmentProgram.h @@ -34,8 +34,8 @@ public: protected: std::string getFloatTypeName(usz elementCount) override; std::string getHalfTypeName(usz elementCount) override; - std::string getFunction(FUNCTION) override; - std::string compareFunction(COMPARE, const std::string&, const std::string&) override; + std::string getFunction(FUNCTION f) override; + std::string compareFunction(COMPARE f, std::string_view Op0, std::string_view Op1) override; void insertHeader(std::stringstream &OS) override; void insertInputs(std::stringstream &OS) override; diff --git a/rpcs3/Emu/RSX/GL/GLGSRender.cpp b/rpcs3/Emu/RSX/GL/GLGSRender.cpp index 15226ab485..d8d412911e 100644 --- a/rpcs3/Emu/RSX/GL/GLGSRender.cpp +++ b/rpcs3/Emu/RSX/GL/GLGSRender.cpp @@ -49,6 +49,7 @@ GLGSRender::GLGSRender(utils::serial* ar) noexcept : GSRender(ar) backend_config.supports_multidraw = true; backend_config.supports_normalized_barycentrics = true; + backend_config.supports_hw_instanced_rendering = true; if (g_cfg.video.antialiasing_level != msaa_level::none) { @@ -148,6 +149,9 @@ void GLGSRender::on_init_thread() rsx_log.success("GL VERSION: %s", reinterpret_cast(glGetString(GL_VERSION))); rsx_log.success("GLSL VERSION: %s", reinterpret_cast(glGetString(GL_SHADING_LANGUAGE_VERSION))); + // Update frame title after GL was initialized in order to show the proper GPU + m_frame->update_title(); + const auto& gl_caps = gl::get_driver_caps(); std::vector exception_reasons; @@ -176,6 +180,12 @@ void GLGSRender::on_init_thread() rsx_log.warning("Texture barriers are not supported by your GPU. Feedback loops will have undefined results."); } + if (!gl_caps.ARB_shader_storage_buffer_object_supported) + { + rsx_log.warning("[PERFORMANCE WARNING] SSBOs are not supported by your GPU. Some functionality such as hardware instancing will be unavailable."); + backend_config.supports_hw_instanced_rendering = false; + } + if (!gl_caps.ARB_bindless_texture_supported) { switch (shadermode) @@ -326,10 +336,16 @@ void GLGSRender::on_init_thread() m_vertex_layout_buffer->create(gl::buffer::target::uniform, 16 * 0x100000); m_raster_env_ring_buffer->create(gl::buffer::target::uniform, 16 * 0x100000); m_scratch_ring_buffer->create(gl::buffer::target::uniform, 16 * 0x100000); - m_instancing_ring_buffer->create(gl::buffer::target::ssbo, 128 * 0x100000); + + if (backend_config.supports_hw_instanced_rendering) + { + ensure(gl_caps.ARB_shader_storage_buffer_object_supported); + m_instancing_ring_buffer->create(gl::buffer::target::ssbo, 128 * 0x100000); + } if (shadermode == shader_mode::async_with_interpreter || shadermode == shader_mode::interpreter_only) { + ensure(gl_caps.ARB_shader_storage_buffer_object_supported); m_vertex_instructions_buffer->create(gl::buffer::target::ssbo, 16 * 0x100000); m_fragment_instructions_buffer->create(gl::buffer::target::ssbo, 16 * 0x100000); } @@ -919,7 +935,7 @@ void GLGSRender::load_program_env() const bool update_fragment_texture_env = m_graphics_state & rsx::pipeline_state::fragment_texture_state_dirty; const bool update_instruction_buffers = !!m_interpreter_state && m_shader_interpreter.is_interpreter(m_program); const bool update_raster_env = REGS(m_ctx)->polygon_stipple_enabled() && (m_graphics_state & rsx::pipeline_state::polygon_stipple_pattern_dirty); - const bool update_instancing_data = REGS(m_ctx)->current_draw_clause.is_trivial_instanced_draw; + const bool update_instancing_data = backend_config.supports_hw_instanced_rendering && REGS(m_ctx)->current_draw_clause.is_trivial_instanced_draw; if (manually_flush_ring_buffers) { @@ -1307,11 +1323,11 @@ void GLGSRender::do_local_task(rsx::FIFO::state state) { std::lock_guard lock(queue_guard); - work_queue.remove_if([](auto &q) { return q.received; }); + work_queue.remove_if([](auto &q) { return q.received.load(); }); for (auto& q : work_queue) { - if (q.processed) continue; + if (q.processed.load()) continue; gl::command_context cmd{ gl_state }; q.result = m_gl_texture_cache.flush_all(cmd, q.section_data); diff --git a/rpcs3/Emu/RSX/GL/GLGSRender.h b/rpcs3/Emu/RSX/GL/GLGSRender.h index 330838f804..357635c1e7 100644 --- a/rpcs3/Emu/RSX/GL/GLGSRender.h +++ b/rpcs3/Emu/RSX/GL/GLGSRender.h @@ -6,6 +6,7 @@ #include "GLOverlays.h" #include "GLShaderInterpreter.h" #include "Emu/RSX/rsx_cache.h" +#include "util/asm.hpp" #include #include @@ -45,16 +46,16 @@ namespace gl u32 address_to_flush = 0; gl::texture_cache::thrashed_set section_data; - volatile bool processed = false; + atomic_t processed = false; volatile bool result = false; - volatile bool received = false; + atomic_t received = false; void producer_wait() { - while (!processed) + utils::spin_wait(processed, [](auto v) { - std::this_thread::yield(); - } + return v; + }); received = true; } @@ -183,7 +184,6 @@ private: void load_texture_env(); void bind_texture_env(); - void bind_interpreter_texture_env(); gl::texture* get_present_source(gl::present_surface_info* info, const rsx::avconf& avconfig); diff --git a/rpcs3/Emu/RSX/GL/GLProcTable.h b/rpcs3/Emu/RSX/GL/GLProcTable.h index b4a9c1c289..f39610012d 100644 --- a/rpcs3/Emu/RSX/GL/GLProcTable.h +++ b/rpcs3/Emu/RSX/GL/GLProcTable.h @@ -248,7 +248,7 @@ OPENGL_PROC(PFNGLCOPYIMAGESUBDATAPROC, CopyImageSubData); // ARB_Buffer_Storage OPENGL_PROC(PFNGLNAMEDBUFFERSTORAGEPROC, NamedBufferStorage); -OPENGL_PROC(PFNGLNAMEDBUFFERSTORAGEEXTPROC, NamedBufferStorageEXT); +OPENGL_PROC(PFNGLBUFFERSTORAGEPROC, BufferStorage); // ARB_sync OPENGL_PROC(PFNGLFENCESYNCPROC, FenceSync); diff --git a/rpcs3/Emu/RSX/GL/GLShaderInterpreter.cpp b/rpcs3/Emu/RSX/GL/GLShaderInterpreter.cpp index aac9a9eabf..d5b49deb69 100644 --- a/rpcs3/Emu/RSX/GL/GLShaderInterpreter.cpp +++ b/rpcs3/Emu/RSX/GL/GLShaderInterpreter.cpp @@ -302,9 +302,14 @@ namespace gl GLFragmentDecompilerThread comp(shader_str, arr, frag, len); std::stringstream builder; - builder << - "#version 450\n" - "#extension GL_ARB_bindless_texture : require\n\n"; + builder << "#version 450\n"; + + if (compiler_options & COMPILER_OPT_ENABLE_TEXTURES) + { + builder << "#extension GL_ARB_bindless_texture : require\n"; + } + + builder << "\n"; ::glsl::insert_subheader_block(builder); comp.insertConstants(builder); diff --git a/rpcs3/Emu/RSX/GL/GLVertexProgram.cpp b/rpcs3/Emu/RSX/GL/GLVertexProgram.cpp index 6f5cb85df0..d275a55bcf 100644 --- a/rpcs3/Emu/RSX/GL/GLVertexProgram.cpp +++ b/rpcs3/Emu/RSX/GL/GLVertexProgram.cpp @@ -21,12 +21,12 @@ std::string GLVertexDecompilerThread::getFunction(FUNCTION f) return glsl::getFunctionImpl(f); } -std::string GLVertexDecompilerThread::compareFunction(COMPARE f, const std::string &Op0, const std::string &Op1, bool scalar) +std::string GLVertexDecompilerThread::compareFunction(COMPARE f, std::string_view Op0, std::string_view Op1, bool scalar) { return glsl::compareFunctionImpl(f, Op0, Op1, scalar); } -void GLVertexDecompilerThread::insertHeader(std::stringstream &OS) +void GLVertexDecompilerThread::insertHeader(std::stringstream& OS) { OS << "#version 430\n" @@ -162,7 +162,7 @@ void GLVertexDecompilerThread::insertOutputs(std::stringstream& OS, const std::v } } -void GLVertexDecompilerThread::insertMainStart(std::stringstream & OS) +void GLVertexDecompilerThread::insertMainStart(std::stringstream& OS) { const auto& dev_caps = gl::get_driver_caps(); @@ -236,7 +236,7 @@ void GLVertexDecompilerThread::insertMainStart(std::stringstream & OS) } } -void GLVertexDecompilerThread::insertMainEnd(std::stringstream & OS) +void GLVertexDecompilerThread::insertMainEnd(std::stringstream& OS) { OS << "}\n\n"; diff --git a/rpcs3/Emu/RSX/GL/GLVertexProgram.h b/rpcs3/Emu/RSX/GL/GLVertexProgram.h index 631b6ac245..2fb57b04c7 100644 --- a/rpcs3/Emu/RSX/GL/GLVertexProgram.h +++ b/rpcs3/Emu/RSX/GL/GLVertexProgram.h @@ -26,20 +26,20 @@ struct GLVertexDecompilerThread : public VertexProgramDecompiler protected: std::string getFloatTypeName(usz elementCount) override; std::string getIntTypeName(usz elementCount) override; - std::string getFunction(FUNCTION) override; - std::string compareFunction(COMPARE, const std::string&, const std::string&, bool scalar) override; + std::string getFunction(FUNCTION f) override; + std::string compareFunction(COMPARE f, std::string_view Op0, std::string_view Op1, bool scalar) override; - void insertHeader(std::stringstream &OS) override; - void insertInputs(std::stringstream &OS, const std::vector &inputs) override; - void insertConstants(std::stringstream &OS, const std::vector &constants) override; - void insertOutputs(std::stringstream &OS, const std::vector &outputs) override; - void insertMainStart(std::stringstream &OS) override; - void insertMainEnd(std::stringstream &OS) override; + void insertHeader(std::stringstream& OS) override; + void insertInputs(std::stringstream& OS, const std::vector& inputs) override; + void insertConstants(std::stringstream& OS, const std::vector& constants) override; + void insertOutputs(std::stringstream& OS, const std::vector& outputs) override; + void insertMainStart(std::stringstream& OS) override; + void insertMainEnd(std::stringstream& OS) override; const RSXVertexProgram &rsx_vertex_program; std::unordered_map input_locations; public: - GLVertexDecompilerThread(const RSXVertexProgram &prog, std::string& shader, ParamArray&) + GLVertexDecompilerThread(const RSXVertexProgram& prog, std::string& shader, ParamArray&) : VertexProgramDecompiler(prog) , m_shader(shader) , rsx_vertex_program(prog) diff --git a/rpcs3/Emu/RSX/GL/glutils/buffer_object.cpp b/rpcs3/Emu/RSX/GL/glutils/buffer_object.cpp index f3849718d0..d47848f393 100644 --- a/rpcs3/Emu/RSX/GL/glutils/buffer_object.cpp +++ b/rpcs3/Emu/RSX/GL/glutils/buffer_object.cpp @@ -1,6 +1,7 @@ #include "stdafx.h" #include "buffer_object.h" #include "common.h" +#include "ex.h" namespace gl { @@ -44,7 +45,7 @@ namespace gl flags |= GL_CLIENT_STORAGE_BIT; } - DSA_CALL2(NamedBufferStorage, m_id, size, data_, flags); + DSA_CALL_EX(NamedBufferStorage, m_id, static_cast(m_target), size, data_, flags); m_size = size; } else diff --git a/rpcs3/Emu/RSX/GL/glutils/capabilities.cpp b/rpcs3/Emu/RSX/GL/glutils/capabilities.cpp index a6999d6f9c..4ecb74d010 100644 --- a/rpcs3/Emu/RSX/GL/glutils/capabilities.cpp +++ b/rpcs3/Emu/RSX/GL/glutils/capabilities.cpp @@ -93,6 +93,8 @@ namespace gl CHECK_EXTENSION_SUPPORT(EXT_texture_compression_s3tc); + CHECK_EXTENSION_SUPPORT(ARB_shader_storage_buffer_object); + #undef CHECK_EXTENSION_SUPPORT // Set GLSL version @@ -165,4 +167,14 @@ namespace gl initialized = true; } + + const std::string get_device_name() + { + if (const char* renderer = reinterpret_cast(glGetString(GL_RENDERER))) + { + return renderer; + } + + return "OpenGL GPU"; + } } diff --git a/rpcs3/Emu/RSX/GL/glutils/capabilities.h b/rpcs3/Emu/RSX/GL/glutils/capabilities.h index cfe104ffbe..b68cd56923 100644 --- a/rpcs3/Emu/RSX/GL/glutils/capabilities.h +++ b/rpcs3/Emu/RSX/GL/glutils/capabilities.h @@ -43,11 +43,13 @@ namespace gl bool NV_fragment_shader_barycentric_supported = false; bool ARB_shader_texture_image_samples_supported = false; bool EXT_texture_compression_s3tc_supported = false; + bool ARB_shader_storage_buffer_object_supported = false; bool vendor_INTEL = false; // has broken GLSL compiler bool vendor_AMD = false; // has broken ARB_multidraw bool vendor_NVIDIA = false; // has NaN poisoning issues bool vendor_MESA = false; // requires CLIENT_STORAGE bit set for streaming buffers + bool subvendor_RADEONSI = false; bool subvendor_NOUVEAU = false; bool subvendor_ATI = false; // Pre-GCN cards (terascale, evergreen) @@ -56,4 +58,5 @@ namespace gl }; const capabilities& get_driver_caps(); + const std::string get_device_name(); } diff --git a/rpcs3/Emu/RSX/GL/glutils/common.h b/rpcs3/Emu/RSX/GL/glutils/common.h index 326a79b9e8..ef714a991c 100644 --- a/rpcs3/Emu/RSX/GL/glutils/common.h +++ b/rpcs3/Emu/RSX/GL/glutils/common.h @@ -25,19 +25,31 @@ #define GL_COMPUTE_BUFFER_SLOT(index) SSBO_SLOT(2 + index) #define GL_COMPUTE_IMAGE_SLOT(index) SSBO_SLOT(index) -//Function call wrapped in ARB_DSA vs EXT_DSA compat check +// Function call wrapped in ARB_DSA vs EXT_DSA compat check #define DSA_CALL(func, object_name, target, ...)\ if (::gl::get_driver_caps().ARB_direct_state_access_supported)\ gl##func(object_name, __VA_ARGS__);\ else\ gl##func##EXT(object_name, target, __VA_ARGS__); +#define DSA_CALL_EX(func, object_name, target, ...)\ + if (::gl::get_driver_caps().ARB_direct_state_access_supported)\ + gl##func(object_name, __VA_ARGS__);\ + else\ + gl##func##EX(object_name, target, __VA_ARGS__); + #define DSA_CALL2(func, ...)\ if (::gl::get_driver_caps().ARB_direct_state_access_supported)\ gl##func(__VA_ARGS__);\ else\ gl##func##EXT(__VA_ARGS__); +#define DSA_CALL2_EX(func, ...)\ + if (::gl::get_driver_caps().ARB_direct_state_access_supported)\ + gl##func(__VA_ARGS__);\ + else\ + gl##func##EX(__VA_ARGS__); + #define DSA_CALL2_RET(func, ...)\ (::gl::get_driver_caps().ARB_direct_state_access_supported) ?\ gl##func(__VA_ARGS__) :\ diff --git a/rpcs3/Emu/RSX/GL/glutils/ex.cpp b/rpcs3/Emu/RSX/GL/glutils/ex.cpp new file mode 100644 index 0000000000..d93ea508e8 --- /dev/null +++ b/rpcs3/Emu/RSX/GL/glutils/ex.cpp @@ -0,0 +1,19 @@ +#include "stdafx.h" +#include "ex.h" + +namespace gl::ex +{ + void glNamedBufferStorageEX(GLuint buffer, GLenum target, GLsizeiptr size, const void* data, GLbitfield flags) + { + GLuint restore = GL_NONE; + glGetIntegerv(target, utils::bless(&restore)); + + glBindBuffer(target, buffer); + glBufferStorage(target, size, data, flags); + + if (restore != buffer) + { + glBindBuffer(target, restore); + } + } +} diff --git a/rpcs3/Emu/RSX/GL/glutils/ex.h b/rpcs3/Emu/RSX/GL/glutils/ex.h new file mode 100644 index 0000000000..5464e50580 --- /dev/null +++ b/rpcs3/Emu/RSX/GL/glutils/ex.h @@ -0,0 +1,12 @@ +#pragma once + +#include "capabilities.h" + +namespace gl::ex +{ + // Fallback EX implementation of NamedBufferStorage. The official EXT_DSA variant was dropped from desktop spec as it was originally meant for GLES only. + // Target parameter reintroduced as an optimization to avoid side effects. + void glNamedBufferStorageEX(GLuint buffer, GLenum target, GLsizeiptr size, const void* data, GLbitfield flags); +} + +using namespace ::gl::ex; diff --git a/rpcs3/Emu/RSX/GL/glutils/ring_buffer.cpp b/rpcs3/Emu/RSX/GL/glutils/ring_buffer.cpp index ac0cc48afe..8ec0e29590 100644 --- a/rpcs3/Emu/RSX/GL/glutils/ring_buffer.cpp +++ b/rpcs3/Emu/RSX/GL/glutils/ring_buffer.cpp @@ -1,5 +1,6 @@ #include "stdafx.h" #include "ring_buffer.h" +#include "ex.h" namespace gl { @@ -17,7 +18,7 @@ namespace gl GLbitfield buffer_storage_flags = GL_MAP_WRITE_BIT | GL_MAP_PERSISTENT_BIT | GL_MAP_COHERENT_BIT; if (gl::get_driver_caps().vendor_MESA) buffer_storage_flags |= GL_CLIENT_STORAGE_BIT; - DSA_CALL2(NamedBufferStorage, m_id, size, data, buffer_storage_flags); + DSA_CALL_EX(NamedBufferStorage, m_id, static_cast(m_target), size, data, buffer_storage_flags); m_memory_mapping = DSA_CALL2_RET(MapNamedBufferRange, m_id, 0, size, GL_MAP_WRITE_BIT | GL_MAP_PERSISTENT_BIT | GL_MAP_COHERENT_BIT); ensure(m_memory_mapping != nullptr); @@ -206,7 +207,7 @@ namespace gl buffer::create(); save_binding_state save(current_target(), *this); - DSA_CALL2(NamedBufferStorage, m_id, size, data, GL_MAP_WRITE_BIT); + DSA_CALL_EX(NamedBufferStorage, m_id, static_cast(m_target), size, data, GL_MAP_WRITE_BIT); m_data_loc = 0; m_size = ::narrow(size); diff --git a/rpcs3/Emu/RSX/GL/glutils/sampler.cpp b/rpcs3/Emu/RSX/GL/glutils/sampler.cpp index 4b1b603fc6..005f6fa1d6 100644 --- a/rpcs3/Emu/RSX/GL/glutils/sampler.cpp +++ b/rpcs3/Emu/RSX/GL/glutils/sampler.cpp @@ -45,7 +45,7 @@ namespace gl return 1.0f; } - int tex_min_filter(rsx::texture_minify_filter min_filter) + GLenum tex_min_filter(rsx::texture_minify_filter min_filter) { switch (min_filter) { @@ -60,7 +60,7 @@ namespace gl fmt::throw_exception("Unknown min filter"); } - int tex_mag_filter(rsx::texture_magnify_filter mag_filter) + GLenum tex_mag_filter(rsx::texture_magnify_filter mag_filter) { switch (mag_filter) { diff --git a/rpcs3/Emu/RSX/GSFrameBase.h b/rpcs3/Emu/RSX/GSFrameBase.h index 69d2832c55..b5edbc19a4 100644 --- a/rpcs3/Emu/RSX/GSFrameBase.h +++ b/rpcs3/Emu/RSX/GSFrameBase.h @@ -33,4 +33,6 @@ public: virtual bool can_consume_frame() const = 0; virtual void present_frame(std::vector&& data, u32 pitch, u32 width, u32 height, bool is_bgra) const = 0; virtual void take_screenshot(std::vector&& sshot_data, u32 sshot_width, u32 sshot_height, bool is_bgra) = 0; + + virtual void update_title(double fps = 0.0) = 0; }; diff --git a/rpcs3/Emu/RSX/NV47/HW/nv406e.cpp b/rpcs3/Emu/RSX/NV47/HW/nv406e.cpp index 4307cc8289..3eaebb1528 100644 --- a/rpcs3/Emu/RSX/NV47/HW/nv406e.cpp +++ b/rpcs3/Emu/RSX/NV47/HW/nv406e.cpp @@ -29,6 +29,7 @@ namespace rsx RSX(ctx)->m_graphics_state |= rsx::pipeline_state::fragment_program_needs_rehash; const auto& sema = vm::_ref(addr); + const auto& atomic_sema = vm::_ref>(addr); if (sema == arg) { @@ -79,7 +80,17 @@ namespace rsx } } - RSX(ctx)->cpu_wait({}); + if (RSX(ctx)->external_interrupt_lock || + (RSX(ctx)->state & (cpu_flag::dbg_global_pause + cpu_flag::exit)) == cpu_flag::dbg_global_pause) + { + RSX(ctx)->cpu_wait({}); + continue; + } + + RSX(ctx)->on_semaphore_acquire_wait(); + + // Wait until the value changes or until 100us pass. + utils::spin_on_cacheline_once(atomic_sema, sema, 100); } RSX(ctx)->fifo_wake_delay(); diff --git a/rpcs3/Emu/RSX/Overlays/FriendsList/overlay_friends_list_dialog.cpp b/rpcs3/Emu/RSX/Overlays/FriendsList/overlay_friends_list_dialog.cpp index f96505ea3c..d925a9b542 100644 --- a/rpcs3/Emu/RSX/Overlays/FriendsList/overlay_friends_list_dialog.cpp +++ b/rpcs3/Emu/RSX/Overlays/FriendsList/overlay_friends_list_dialog.cpp @@ -14,7 +14,7 @@ namespace rsx dlg->callback_handler(ntype, username, status); } - friends_list_dialog::friends_list_entry::friends_list_entry(friends_list_dialog_page page, const std::string& username, const rpcn::friend_online_data& data) + friends_list_dialog::friends_list_entry::friends_list_entry(friends_list_dialog_page page, std::string_view username, const rpcn::friend_online_data& data) { std::unique_ptr image = std::make_unique(); image->set_size(160, 110); diff --git a/rpcs3/Emu/RSX/Overlays/FriendsList/overlay_friends_list_dialog.h b/rpcs3/Emu/RSX/Overlays/FriendsList/overlay_friends_list_dialog.h index 3d5ec9dbe2..5d07c349b8 100644 --- a/rpcs3/Emu/RSX/Overlays/FriendsList/overlay_friends_list_dialog.h +++ b/rpcs3/Emu/RSX/Overlays/FriendsList/overlay_friends_list_dialog.h @@ -26,7 +26,7 @@ namespace rsx std::unique_ptr icon_data; public: - friends_list_entry(friends_list_dialog_page page, const std::string& username, const rpcn::friend_online_data& data); + friends_list_entry(friends_list_dialog_page page, std::string_view username, const rpcn::friend_online_data& data); }; std::mutex m_list_mutex; diff --git a/rpcs3/Emu/RSX/Overlays/HomeMenu/overlay_home_menu_components.cpp b/rpcs3/Emu/RSX/Overlays/HomeMenu/overlay_home_menu_components.cpp index 94e70f4c63..c1b1585e99 100644 --- a/rpcs3/Emu/RSX/Overlays/HomeMenu/overlay_home_menu_components.cpp +++ b/rpcs3/Emu/RSX/Overlays/HomeMenu/overlay_home_menu_components.cpp @@ -5,7 +5,7 @@ namespace rsx { namespace overlays { - home_menu_entry::home_menu_entry(home_menu::fa_icon icon, const std::string& text, u16 width, text_align alignment) + home_menu_entry::home_menu_entry(home_menu::fa_icon icon, std::string_view text, u16 width, text_align alignment) { auto text_stack = std::make_unique(); auto padding = std::make_unique(); diff --git a/rpcs3/Emu/RSX/Overlays/HomeMenu/overlay_home_menu_components.h b/rpcs3/Emu/RSX/Overlays/HomeMenu/overlay_home_menu_components.h index 88e1c53324..5a065fc177 100644 --- a/rpcs3/Emu/RSX/Overlays/HomeMenu/overlay_home_menu_components.h +++ b/rpcs3/Emu/RSX/Overlays/HomeMenu/overlay_home_menu_components.h @@ -31,7 +31,7 @@ namespace rsx struct home_menu_entry : horizontal_layout { public: - home_menu_entry(home_menu::fa_icon icon, const std::string& text, u16 width, text_align alignment = text_align::center); + home_menu_entry(home_menu::fa_icon icon, std::string_view text, u16 width, text_align alignment = text_align::center); }; template diff --git a/rpcs3/Emu/RSX/Overlays/HomeMenu/overlay_home_menu_message_box.cpp b/rpcs3/Emu/RSX/Overlays/HomeMenu/overlay_home_menu_message_box.cpp index ed42f4f815..f906283987 100644 --- a/rpcs3/Emu/RSX/Overlays/HomeMenu/overlay_home_menu_message_box.cpp +++ b/rpcs3/Emu/RSX/Overlays/HomeMenu/overlay_home_menu_message_box.cpp @@ -53,7 +53,7 @@ namespace rsx return compiled_resources; } - void home_menu_message_box::show(const std::string& text, std::function on_accept, std::function on_cancel) + void home_menu_message_box::show(std::string_view text, std::function on_accept, std::function on_cancel) { m_on_accept = std::move(on_accept); m_on_cancel = std::move(on_cancel); diff --git a/rpcs3/Emu/RSX/Overlays/HomeMenu/overlay_home_menu_message_box.h b/rpcs3/Emu/RSX/Overlays/HomeMenu/overlay_home_menu_message_box.h index 4540484efc..3e04c26840 100644 --- a/rpcs3/Emu/RSX/Overlays/HomeMenu/overlay_home_menu_message_box.h +++ b/rpcs3/Emu/RSX/Overlays/HomeMenu/overlay_home_menu_message_box.h @@ -11,7 +11,7 @@ namespace rsx public: home_menu_message_box(s16 x, s16 y, u16 width, u16 height); compiled_resource& get_compiled() override; - void show(const std::string& text, std::function on_accept = nullptr, std::function on_cancel = nullptr); + void show(std::string_view text, std::function on_accept = nullptr, std::function on_cancel = nullptr); void hide(); page_navigation handle_button_press(pad_button button_press); diff --git a/rpcs3/Emu/RSX/Overlays/HomeMenu/overlay_home_menu_page.cpp b/rpcs3/Emu/RSX/Overlays/HomeMenu/overlay_home_menu_page.cpp index 224aace848..65346cbedb 100644 --- a/rpcs3/Emu/RSX/Overlays/HomeMenu/overlay_home_menu_page.cpp +++ b/rpcs3/Emu/RSX/Overlays/HomeMenu/overlay_home_menu_page.cpp @@ -155,7 +155,7 @@ namespace rsx refresh(); } - void home_menu_page::show_dialog(const std::string& text, std::function on_accept, std::function on_cancel) + void home_menu_page::show_dialog(std::string_view text, std::function on_accept, std::function on_cancel) { if (m_message_box && !m_message_box->visible) { diff --git a/rpcs3/Emu/RSX/Overlays/HomeMenu/overlay_home_menu_page.h b/rpcs3/Emu/RSX/Overlays/HomeMenu/overlay_home_menu_page.h index 67ee03f66d..0db5f3245e 100644 --- a/rpcs3/Emu/RSX/Overlays/HomeMenu/overlay_home_menu_page.h +++ b/rpcs3/Emu/RSX/Overlays/HomeMenu/overlay_home_menu_page.h @@ -26,7 +26,7 @@ namespace rsx struct home_menu_page : public list_view { public: - home_menu_page(s16 x, s16 y, u16 width, u16 height, bool use_separators, home_menu_page* parent, const std::string& text); + home_menu_page(s16 x, s16 y, u16 width, u16 height, bool use_separators, home_menu_page* parent, const std::string& title); void set_current_page(home_menu_page* page); home_menu_page* get_current_page(bool include_this); @@ -56,7 +56,7 @@ namespace rsx virtual void add_item(home_menu::fa_icon icon, std::string_view, std::function callback); virtual void add_item(std::unique_ptr& element, std::function callback); virtual void apply_layout(bool center_vertically = false); - void show_dialog(const std::string& text, std::function on_accept = nullptr, std::function on_cancel = nullptr); + void show_dialog(std::string_view text, std::function on_accept = nullptr, std::function on_cancel = nullptr); std::vector> m_pages; diff --git a/rpcs3/Emu/RSX/Overlays/Network/overlay_recvmessage_dialog.cpp b/rpcs3/Emu/RSX/Overlays/Network/overlay_recvmessage_dialog.cpp index 278510e8a9..01b0c53042 100644 --- a/rpcs3/Emu/RSX/Overlays/Network/overlay_recvmessage_dialog.cpp +++ b/rpcs3/Emu/RSX/Overlays/Network/overlay_recvmessage_dialog.cpp @@ -14,7 +14,7 @@ namespace rsx dlg->callback_handler(std::move(new_msg), msg_id); } - recvmessage_dialog::list_entry::list_entry(const std::string& name, const std::string& subj, const std::string& body) + recvmessage_dialog::list_entry::list_entry(std::string_view name, std::string_view subj, std::string_view body) { std::unique_ptr prefix_stack = std::make_unique(); std::unique_ptr text_stack = std::make_unique(); diff --git a/rpcs3/Emu/RSX/Overlays/Network/overlay_recvmessage_dialog.h b/rpcs3/Emu/RSX/Overlays/Network/overlay_recvmessage_dialog.h index 0c910e1311..eb107a4823 100644 --- a/rpcs3/Emu/RSX/Overlays/Network/overlay_recvmessage_dialog.h +++ b/rpcs3/Emu/RSX/Overlays/Network/overlay_recvmessage_dialog.h @@ -15,7 +15,7 @@ namespace rsx struct list_entry : horizontal_layout { public: - list_entry(const std::string& name, const std::string& subj, const std::string& body); + list_entry(std::string_view name, std::string_view subj, std::string_view body); }; shared_mutex m_mutex; diff --git a/rpcs3/Emu/RSX/Overlays/Network/overlay_sendmessage_dialog.cpp b/rpcs3/Emu/RSX/Overlays/Network/overlay_sendmessage_dialog.cpp index 5d2d5c1bef..19a1e752e2 100644 --- a/rpcs3/Emu/RSX/Overlays/Network/overlay_sendmessage_dialog.cpp +++ b/rpcs3/Emu/RSX/Overlays/Network/overlay_sendmessage_dialog.cpp @@ -18,7 +18,7 @@ namespace rsx dlg->callback_handler(ntype, username, status); } - sendmessage_dialog::list_entry::list_entry(const std::string& msg) + sendmessage_dialog::list_entry::list_entry(std::string_view msg) { std::unique_ptr text_stack = std::make_unique(); std::unique_ptr padding = std::make_unique(); @@ -336,7 +336,7 @@ namespace rsx return {}; } - void sendmessage_dialog::reload(const std::string& previous_selection) + void sendmessage_dialog::reload(std::string_view previous_selection) { if (m_list) { diff --git a/rpcs3/Emu/RSX/Overlays/Network/overlay_sendmessage_dialog.h b/rpcs3/Emu/RSX/Overlays/Network/overlay_sendmessage_dialog.h index 508d296fc4..ef506bb0d6 100644 --- a/rpcs3/Emu/RSX/Overlays/Network/overlay_sendmessage_dialog.h +++ b/rpcs3/Emu/RSX/Overlays/Network/overlay_sendmessage_dialog.h @@ -15,7 +15,7 @@ namespace rsx struct list_entry : horizontal_layout { public: - list_entry(const std::string& msg); + list_entry(std::string_view msg); }; shared_mutex m_mutex; @@ -29,7 +29,7 @@ namespace rsx animation_color_interpolate fade_animation; std::string get_current_selection() const; - void reload(const std::string& previous_selection); + void reload(std::string_view previous_selection); public: sendmessage_dialog(); diff --git a/rpcs3/Emu/RSX/Overlays/overlay_manager.cpp b/rpcs3/Emu/RSX/Overlays/overlay_manager.cpp index 70f933fbaa..d778dc1422 100644 --- a/rpcs3/Emu/RSX/Overlays/overlay_manager.cpp +++ b/rpcs3/Emu/RSX/Overlays/overlay_manager.cpp @@ -175,13 +175,19 @@ namespace rsx return; } + rsx_log.notice("display_manager::start_audio: path='%s'", audio_path); + m_audio_player = std::make_unique(audio_path); m_audio_player->set_active(true); } void display_manager::stop_audio() { - m_audio_player.reset(); + if (m_audio_player) + { + rsx_log.notice("display_manager::stop_audio"); + m_audio_player.reset(); + } } void display_manager::on_overlay_activated(const std::shared_ptr& /*item*/) diff --git a/rpcs3/Emu/RSX/Overlays/overlay_media_list_dialog.cpp b/rpcs3/Emu/RSX/Overlays/overlay_media_list_dialog.cpp index bedbf652c7..9cc5a5c1e3 100644 --- a/rpcs3/Emu/RSX/Overlays/overlay_media_list_dialog.cpp +++ b/rpcs3/Emu/RSX/Overlays/overlay_media_list_dialog.cpp @@ -221,7 +221,7 @@ namespace rsx return result; } - s32 media_list_dialog::show(std::shared_ptr root, media_entry& result, const std::string& title, u32 focused, bool enable_overlay) + s32 media_list_dialog::show(std::shared_ptr root, media_entry& result, std::string_view title, u32 focused, bool enable_overlay) { auto ref = g_fxo->get().get(uid); @@ -291,7 +291,7 @@ namespace rsx return return_code; } - void media_list_dialog::reload(const std::string& title, u32 focused) + void media_list_dialog::reload(std::string_view title, u32 focused) { ensure(m_media); diff --git a/rpcs3/Emu/RSX/Overlays/overlay_media_list_dialog.h b/rpcs3/Emu/RSX/Overlays/overlay_media_list_dialog.h index e2656d728f..110058e7cd 100644 --- a/rpcs3/Emu/RSX/Overlays/overlay_media_list_dialog.h +++ b/rpcs3/Emu/RSX/Overlays/overlay_media_list_dialog.h @@ -39,10 +39,10 @@ namespace rsx compiled_resource get_compiled() override; - s32 show(std::shared_ptr root, media_entry& result, const std::string& title, u32 focused, bool enable_overlay); + s32 show(std::shared_ptr root, media_entry& result, std::string_view title, u32 focused, bool enable_overlay); private: - void reload(const std::string& title, u32 focused); + void reload(std::string_view title, u32 focused); struct media_list_entry : horizontal_layout { diff --git a/rpcs3/Emu/RSX/Overlays/overlay_message.cpp b/rpcs3/Emu/RSX/Overlays/overlay_message.cpp index 1c49401d02..cfb642b02b 100644 --- a/rpcs3/Emu/RSX/Overlays/overlay_message.cpp +++ b/rpcs3/Emu/RSX/Overlays/overlay_message.cpp @@ -95,7 +95,7 @@ namespace rsx return m_text.text == text; } - void message_item::set_label_text(const std::string& text) + void message_item::set_label_text(std::string_view text) { m_text.set_text(text); m_is_compiled = false; @@ -338,12 +338,12 @@ namespace rsx return check_lists(location, check_list); } - bool message::message_exists(message_pin_location location, const std::string& msg, bool allow_refresh, bool compare_id) + bool message::message_exists(message_pin_location location, std::string_view msg, bool allow_refresh, bool compare_id) { return message_exists(location, utf8_to_u32string(msg), allow_refresh, compare_id); } - bool message::message_exists(message_pin_location location, const std::u32string& msg, bool allow_refresh, bool /*compare_id*/) + bool message::message_exists(message_pin_location location, std::u32string_view msg, bool allow_refresh, bool /*compare_id*/) { const auto check_list = [&](std::deque& list) { diff --git a/rpcs3/Emu/RSX/Overlays/overlay_message.h b/rpcs3/Emu/RSX/Overlays/overlay_message.h index e8cb1a3285..4e3caaee6d 100644 --- a/rpcs3/Emu/RSX/Overlays/overlay_message.h +++ b/rpcs3/Emu/RSX/Overlays/overlay_message.h @@ -33,7 +33,7 @@ namespace rsx bool id_matches(localized_string_id id) const; bool text_matches(std::u32string_view text) const; - void set_label_text(const std::string& text); + void set_label_text(std::string_view text); private: localized_string_id m_loc_id = localized_string_id::INVALID; @@ -126,8 +126,8 @@ namespace rsx // Stacking. Extends the lifetime of a message instead of inserting a duplicate bool check_lists(message_pin_location location, std::function& list)> check_list); bool message_exists(message_pin_location location, localized_string_id id, bool allow_refresh, bool compare_id); - bool message_exists(message_pin_location location, const std::string& msg, bool allow_refresh, bool compare_id); - bool message_exists(message_pin_location location, const std::u32string& msg, bool allow_refresh, bool compare_id); + bool message_exists(message_pin_location location, std::string_view msg, bool allow_refresh, bool compare_id); + bool message_exists(message_pin_location location, std::u32string_view msg, bool allow_refresh, bool compare_id); bool message_exists(message_pin_location location, const localized_string& container, bool allow_refresh, bool compare_id); }; diff --git a/rpcs3/Emu/RSX/Overlays/overlay_osk.cpp b/rpcs3/Emu/RSX/Overlays/overlay_osk.cpp index 428c68a541..fce759f18f 100644 --- a/rpcs3/Emu/RSX/Overlays/overlay_osk.cpp +++ b/rpcs3/Emu/RSX/Overlays/overlay_osk.cpp @@ -467,7 +467,7 @@ namespace rsx m_update = true; } - void osk_dialog::initialize_layout(const std::u32string& title, const std::u32string& initial_text) + void osk_dialog::initialize_layout(std::u32string_view title, std::u32string_view initial_text) { const auto scale_font = [this](overlay_element& elem) { @@ -1096,14 +1096,14 @@ namespace rsx on_text_changed(); } - void osk_dialog::on_shift(const std::u32string&) + void osk_dialog::on_shift(std::u32string_view /*str*/) { const u32 max = num_shift_layers_by_charset[m_selected_charset]; selected_z = (selected_z + 1) % max; m_update = true; } - void osk_dialog::on_layer(const std::u32string&) + void osk_dialog::on_layer(std::u32string_view /*str*/) { const u32 num_charsets = std::max(::size32(num_shift_layers_by_charset), 1); m_selected_charset = (m_selected_charset + 1) % num_charsets; @@ -1120,7 +1120,7 @@ namespace rsx m_update = true; } - void osk_dialog::on_space(const std::u32string&) + void osk_dialog::on_space(std::u32string_view /*str*/) { if (!(flags & CELL_OSKDIALOG_NO_SPACE)) { @@ -1132,19 +1132,19 @@ namespace rsx } } - void osk_dialog::on_backspace(const std::u32string&) + void osk_dialog::on_backspace(std::u32string_view /*str*/) { m_preview.erase(); on_text_changed(); } - void osk_dialog::on_delete(const std::u32string&) + void osk_dialog::on_delete(std::u32string_view /*str*/) { m_preview.del(); on_text_changed(); } - void osk_dialog::on_enter(const std::u32string&) + void osk_dialog::on_enter(std::u32string_view /*str*/) { if (!(flags & CELL_OSKDIALOG_NO_RETURN)) { @@ -1156,7 +1156,7 @@ namespace rsx } } - void osk_dialog::on_move_cursor(const std::u32string&, edit_text::direction dir) + void osk_dialog::on_move_cursor(std::u32string_view /*str*/, edit_text::direction dir) { m_preview.move_caret(dir); m_update = true; @@ -1371,11 +1371,11 @@ namespace rsx m_title.back_color.a = 0.7f; // Uses the dimmed color of the frame background } - const callback_t shift_cb = [this](const std::u32string& text){ on_shift(text); }; - const callback_t layer_cb = [this](const std::u32string& text){ on_layer(text); }; - const callback_t space_cb = [this](const std::u32string& text){ on_space(text); }; - const callback_t delete_cb = [this](const std::u32string& text){ on_backspace(text); }; - const callback_t enter_cb = [this](const std::u32string& text){ on_enter(text); }; + const callback_t shift_cb = [this](std::u32string_view text){ on_shift(text); }; + const callback_t layer_cb = [this](std::u32string_view text){ on_layer(text); }; + const callback_t space_cb = [this](std::u32string_view text){ on_space(text); }; + const callback_t delete_cb = [this](std::u32string_view text){ on_backspace(text); }; + const callback_t enter_cb = [this](std::u32string_view text){ on_enter(text); }; const auto is_supported = [&](u32 mode) -> bool { diff --git a/rpcs3/Emu/RSX/Overlays/overlay_osk.h b/rpcs3/Emu/RSX/Overlays/overlay_osk.h index 2d5fa294ab..b7015963c0 100644 --- a/rpcs3/Emu/RSX/Overlays/overlay_osk.h +++ b/rpcs3/Emu/RSX/Overlays/overlay_osk.h @@ -111,7 +111,7 @@ namespace rsx void SetText(const std::u16string& text) override; void Insert(const std::u16string& text) override; - void initialize_layout(const std::u32string& title, const std::u32string& initial_text); + void initialize_layout(std::u32string_view title, std::u32string_view initial_text); void add_panel(const osk_panel& panel); void step_panel(bool next_panel); void update_panel(); @@ -128,13 +128,13 @@ namespace rsx void on_text_changed(); void on_default_callback(const std::u32string& str); - void on_shift(const std::u32string&); - void on_layer(const std::u32string&); - void on_space(const std::u32string&); - void on_backspace(const std::u32string&); - void on_delete(const std::u32string&); - void on_enter(const std::u32string&); - void on_move_cursor(const std::u32string&, edit_text::direction dir); + void on_shift(std::u32string_view str); + void on_layer(std::u32string_view str); + void on_space(std::u32string_view str); + void on_backspace(std::u32string_view str); + void on_delete(std::u32string_view str); + void on_enter(std::u32string_view str); + void on_move_cursor(std::u32string_view str, edit_text::direction dir); std::u32string get_placeholder() const; diff --git a/rpcs3/Emu/RSX/Overlays/overlay_perf_metrics.cpp b/rpcs3/Emu/RSX/Overlays/overlay_perf_metrics.cpp index afaf7eb772..a3a53dc868 100644 --- a/rpcs3/Emu/RSX/Overlays/overlay_perf_metrics.cpp +++ b/rpcs3/Emu/RSX/Overlays/overlay_perf_metrics.cpp @@ -806,7 +806,7 @@ namespace rsx // Make sure min/max reflects the data being displayed, not the entire datapoints vector for (usz i = m_datapoints.size() - m_datapoint_count; i < m_datapoints.size(); i++) { - const f32& dp = m_datapoints[i]; + const f32 dp = m_datapoints[i]; if (dp < 0) continue; // Skip initial negative values. They don't count. diff --git a/rpcs3/Emu/RSX/Overlays/overlay_save_dialog.cpp b/rpcs3/Emu/RSX/Overlays/overlay_save_dialog.cpp index 2847db969c..cdeba03496 100644 --- a/rpcs3/Emu/RSX/Overlays/overlay_save_dialog.cpp +++ b/rpcs3/Emu/RSX/Overlays/overlay_save_dialog.cpp @@ -7,7 +7,7 @@ namespace rsx { namespace overlays { - save_dialog::save_dialog_entry::save_dialog_entry(const std::string& text1, const std::string& text2, const std::string& text3, u8 resource_id, const std::vector& icon_buf, const std::string& video_path) + save_dialog::save_dialog_entry::save_dialog_entry(std::string_view text1, std::string_view text2, std::string_view text3, u8 resource_id, const std::vector& icon_buf, const std::string& video_path) { const std::string audio_path; // no audio here std::unique_ptr image = resource_id != image_resource_id::raw_image diff --git a/rpcs3/Emu/RSX/Overlays/overlay_save_dialog.h b/rpcs3/Emu/RSX/Overlays/overlay_save_dialog.h index 0c65379153..378c3a70a3 100644 --- a/rpcs3/Emu/RSX/Overlays/overlay_save_dialog.h +++ b/rpcs3/Emu/RSX/Overlays/overlay_save_dialog.h @@ -14,7 +14,7 @@ namespace rsx struct save_dialog_entry : horizontal_layout { public: - save_dialog_entry(const std::string& text1, const std::string& text2, const std::string& text3, u8 resource_id, const std::vector& icon_buf, const std::string& video_path); + save_dialog_entry(std::string_view text1, std::string_view text2, std::string_view text3, u8 resource_id, const std::vector& icon_buf, const std::string& video_path); void set_selected(bool selected) override; private: diff --git a/rpcs3/Emu/RSX/Overlays/overlay_user_list_dialog.cpp b/rpcs3/Emu/RSX/Overlays/overlay_user_list_dialog.cpp index f534ce5587..6b98a667b9 100644 --- a/rpcs3/Emu/RSX/Overlays/overlay_user_list_dialog.cpp +++ b/rpcs3/Emu/RSX/Overlays/overlay_user_list_dialog.cpp @@ -9,7 +9,7 @@ namespace rsx { namespace overlays { - user_list_dialog::user_list_entry::user_list_entry(const std::string& username, const std::string& user_id, const std::string& avatar_path) + user_list_dialog::user_list_entry::user_list_entry(std::string_view username, std::string_view user_id, const std::string& avatar_path) { std::unique_ptr image = std::make_unique(); image->set_size(160, 110); @@ -174,7 +174,7 @@ namespace rsx return result; } - error_code user_list_dialog::show(const std::string& title, u32 focused, const std::vector& user_ids, bool enable_overlay, std::function on_close) + error_code user_list_dialog::show(std::string_view title, u32 focused, const std::vector& user_ids, bool enable_overlay, std::function on_close) { visible = false; diff --git a/rpcs3/Emu/RSX/Overlays/overlay_user_list_dialog.h b/rpcs3/Emu/RSX/Overlays/overlay_user_list_dialog.h index eb2498a032..06400c27ea 100644 --- a/rpcs3/Emu/RSX/Overlays/overlay_user_list_dialog.h +++ b/rpcs3/Emu/RSX/Overlays/overlay_user_list_dialog.h @@ -17,7 +17,7 @@ namespace rsx std::unique_ptr icon_data; public: - user_list_entry(const std::string& username, const std::string& user_id, const std::string& avatar_path); + user_list_entry(std::string_view username, std::string_view user_id, const std::string& avatar_path); }; std::vector m_entry_ids; @@ -35,7 +35,7 @@ namespace rsx compiled_resource get_compiled() override; - error_code show(const std::string& title, u32 focused, const std::vector& user_ids, bool enable_overlay, std::function on_close); + error_code show(std::string_view title, u32 focused, const std::vector& user_ids, bool enable_overlay, std::function on_close); }; } } diff --git a/rpcs3/Emu/RSX/Program/Assembler/FPOpcodes.h b/rpcs3/Emu/RSX/Program/Assembler/FPOpcodes.h index 636ac06082..ba3b6638d1 100644 --- a/rpcs3/Emu/RSX/Program/Assembler/FPOpcodes.h +++ b/rpcs3/Emu/RSX/Program/Assembler/FPOpcodes.h @@ -130,7 +130,7 @@ namespace rsx::assembler rsx::simple_array get_register_file_range(const RegisterRef& reg); // Compile a register file annotated blob to register references - std::vector compile_register_file(const std::array& file); + std::vector compile_register_file(const register_file_t& file); // Invert execution mask on an instruction void invert_conditional_execution_mask(Instruction* instruction); diff --git a/rpcs3/Emu/RSX/Program/Assembler/IR.h b/rpcs3/Emu/RSX/Program/Assembler/IR.h index 635aec7209..fc5281a623 100644 --- a/rpcs3/Emu/RSX/Program/Assembler/IR.h +++ b/rpcs3/Emu/RSX/Program/Assembler/IR.h @@ -105,14 +105,14 @@ namespace rsx::assembler FlowEdge* insert_succ(BasicBlock* b, EdgeType type = EdgeType::NONE) { FlowEdge e{ .type = type, .from = this, .to = b }; - succ.push_back(e); + succ.push_back(std::move(e)); return &succ.back(); } FlowEdge* insert_pred(BasicBlock* b, EdgeType type = EdgeType::NONE) { FlowEdge e{ .type = type, .from = b, .to = this }; - pred.push_back(e); + pred.push_back(std::move(e)); return &pred.back(); } diff --git a/rpcs3/Emu/RSX/Program/Assembler/Passes/FP/RegisterDependencyPass.cpp b/rpcs3/Emu/RSX/Program/Assembler/Passes/FP/RegisterDependencyPass.cpp index de9f978f51..9b3632504d 100644 --- a/rpcs3/Emu/RSX/Program/Assembler/Passes/FP/RegisterDependencyPass.cpp +++ b/rpcs3/Emu/RSX/Program/Assembler/Passes/FP/RegisterDependencyPass.cpp @@ -182,7 +182,7 @@ namespace rsx::assembler::FP instruction.bytecode[2] = src1.HEX; Register src_reg{ .id = static_cast(src_reg_id), .f16 = true }; - instruction.srcs.push_back({ .reg = src_reg, .mask = 0xF }); + instruction.srcs.push_back({ .reg = std::move(src_reg), .mask = 0xF }); instruction.dsts.push_back({ .reg{ .id = reg_id, .f16 = false }, .mask = (1u << ch) }); result.push_back(std::move(instruction)); } @@ -250,7 +250,7 @@ namespace rsx::assembler::FP instruction.bytecode[1] = src0.HEX; Register src_reg{ .id = static_cast(src_reg_id), .f16 = true }; - instruction.srcs.push_back({ .reg = src_reg, .mask = 0xF }); + instruction.srcs.push_back({ .reg = std::move(src_reg), .mask = 0xF }); instruction.dsts.push_back({ .reg{.id = reg.reg.id, .f16 = false }, .mask = dst.write_mask }); result.push_back(std::move(instruction)); } diff --git a/rpcs3/Emu/RSX/Program/FragmentProgramDecompiler.h b/rpcs3/Emu/RSX/Program/FragmentProgramDecompiler.h index 8e22a21a93..f3cfaa8730 100644 --- a/rpcs3/Emu/RSX/Program/FragmentProgramDecompiler.h +++ b/rpcs3/Emu/RSX/Program/FragmentProgramDecompiler.h @@ -111,33 +111,33 @@ protected: /** returns string calling function where arguments are passed via * $0 $1 $2 substring. */ - virtual std::string getFunction(FUNCTION) = 0; + virtual std::string getFunction(FUNCTION f) = 0; /** returns string calling comparison function on 2 args passed as strings. */ - virtual std::string compareFunction(COMPARE, const std::string &, const std::string &) = 0; + virtual std::string compareFunction(COMPARE f, std::string_view Op0, std::string_view Op1) = 0; /** Insert header of shader file (eg #version, "system constants"...) */ - virtual void insertHeader(std::stringstream &OS) = 0; + virtual void insertHeader(std::stringstream& OS) = 0; /** Insert global declaration of fragments inputs. */ - virtual void insertInputs(std::stringstream &OS) = 0; + virtual void insertInputs(std::stringstream& OS) = 0; /** insert global declaration of fragments outputs. */ - virtual void insertOutputs(std::stringstream &OS) = 0; + virtual void insertOutputs(std::stringstream& OS) = 0; /** insert declaration of shader constants. */ - virtual void insertConstants(std::stringstream &OS) = 0; + virtual void insertConstants(std::stringstream& OS) = 0; /** insert helper function definitions. */ - virtual void insertGlobalFunctions(std::stringstream &OS) = 0; + virtual void insertGlobalFunctions(std::stringstream& OS) = 0; /** insert beginning of main (signature, temporary declaration...) */ - virtual void insertMainStart(std::stringstream &OS) = 0; + virtual void insertMainStart(std::stringstream& OS) = 0; /** insert end of main function (return value, output copy...) */ - virtual void insertMainEnd(std::stringstream &OS) = 0; + virtual void insertMainEnd(std::stringstream& OS) = 0; public: enum : u16 @@ -201,7 +201,7 @@ public: device_props; ParamArray m_parr; - FragmentProgramDecompiler(const RSXFragmentProgram &prog, u32& size); + FragmentProgramDecompiler(const RSXFragmentProgram& prog, u32& size); FragmentProgramDecompiler(const FragmentProgramDecompiler&) = delete; FragmentProgramDecompiler(FragmentProgramDecompiler&&) = delete; std::string Decompile(); diff --git a/rpcs3/Emu/RSX/Program/GLSLCommon.cpp b/rpcs3/Emu/RSX/Program/GLSLCommon.cpp index 4c6d8625fa..761aa091ee 100644 --- a/rpcs3/Emu/RSX/Program/GLSLCommon.cpp +++ b/rpcs3/Emu/RSX/Program/GLSLCommon.cpp @@ -73,7 +73,7 @@ namespace glsl } } - std::string compareFunctionImpl(COMPARE f, const std::string &Op0, const std::string &Op1, bool scalar) + std::string compareFunctionImpl(COMPARE f, std::string_view Op0, std::string_view Op1, bool scalar) { if (scalar) { @@ -636,7 +636,7 @@ namespace glsl } } - varying_list.push_back({ reg_location, var_name, PT.type }); + varying_list.push_back({ reg_location, std::move(var_name), PT.type }); } } diff --git a/rpcs3/Emu/RSX/Program/GLSLCommon.h b/rpcs3/Emu/RSX/Program/GLSLCommon.h index b71dc8c80d..47fd730589 100644 --- a/rpcs3/Emu/RSX/Program/GLSLCommon.h +++ b/rpcs3/Emu/RSX/Program/GLSLCommon.h @@ -71,7 +71,7 @@ namespace glsl std::string getFloatTypeNameImpl(usz elementCount); std::string getHalfTypeNameImpl(usz elementCount); - std::string compareFunctionImpl(COMPARE f, const std::string &Op0, const std::string &Op1, bool scalar = false); + std::string compareFunctionImpl(COMPARE f, std::string_view Op0, std::string_view Op1, bool scalar = false); void insert_vertex_input_fetch(std::stringstream& OS, glsl_rules rules, bool glsl4_compliant=true); void insert_rop_init(std::ostream& OS); void insert_rop(std::ostream& OS, const shader_properties& props); diff --git a/rpcs3/Emu/RSX/Program/ShaderParam.h b/rpcs3/Emu/RSX/Program/ShaderParam.h index 6131f36ae9..bf78b8f499 100644 --- a/rpcs3/Emu/RSX/Program/ShaderParam.h +++ b/rpcs3/Emu/RSX/Program/ShaderParam.h @@ -130,7 +130,7 @@ struct ParamType { } - bool HasItem(const std::string& name) const + bool HasItem(std::string_view name) const { return std::any_of(items.cbegin(), items.cend(), [&name](const auto& item) { @@ -138,7 +138,7 @@ struct ParamType }); } - bool ReplaceOrInsert(const std::string& name, const ParamItem& item) + bool ReplaceOrInsert(std::string_view name, const ParamItem& item) { if (HasItem(name)) { @@ -168,7 +168,7 @@ struct ParamArray { std::vector params[PF_PARAM_COUNT]; - ParamType* SearchParam(const ParamFlag &flag, const std::string& type) + ParamType* SearchParam(const ParamFlag& flag, std::string_view type) { for (auto& param : params[flag]) { @@ -179,7 +179,7 @@ struct ParamArray return nullptr; } - bool HasParamTypeless(const ParamFlag flag, const std::string& name) const + bool HasParamTypeless(const ParamFlag flag, std::string_view name) const { const auto& p = params[flag]; return std::any_of(p.cbegin(), p.cend(), [&name](const auto& param) @@ -188,7 +188,7 @@ struct ParamArray }); } - bool HasParam(const ParamFlag flag, const std::string& type, const std::string& name) + bool HasParam(const ParamFlag flag, std::string_view type, std::string_view name) { const ParamType* t = SearchParam(flag, type); return t && t->HasItem(name); @@ -326,7 +326,7 @@ public: new_swizzle += swizzle[p.second]; } - swizzles.push_back(new_swizzle); + swizzles.push_back(std::move(new_swizzle)); return *this; } diff --git a/rpcs3/Emu/RSX/Program/VertexProgramDecompiler.h b/rpcs3/Emu/RSX/Program/VertexProgramDecompiler.h index 94b25f8076..adf6676921 100644 --- a/rpcs3/Emu/RSX/Program/VertexProgramDecompiler.h +++ b/rpcs3/Emu/RSX/Program/VertexProgramDecompiler.h @@ -99,35 +99,35 @@ protected: /** returns string calling function where arguments are passed via * $0 $1 $2 substring. */ - virtual std::string getFunction(FUNCTION) = 0; + virtual std::string getFunction(FUNCTION f) = 0; /** returns string calling comparison function on 2 args passed as strings. */ - virtual std::string compareFunction(COMPARE, const std::string &, const std::string &, bool scalar = false) = 0; + virtual std::string compareFunction(COMPARE f, std::string_view Op0, std::string_view Op1, bool scalar = false) = 0; /** Insert header of shader file (eg #version, "system constants"...) */ - virtual void insertHeader(std::stringstream &OS) = 0; + virtual void insertHeader(std::stringstream& OS) = 0; /** Insert vertex declaration. */ - virtual void insertInputs(std::stringstream &OS, const std::vector &inputs) = 0; + virtual void insertInputs(std::stringstream& OS, const std::vector& inputs) = 0; /** insert global declaration of vertex shader outputs. */ - virtual void insertConstants(std::stringstream &OS, const std::vector &constants) = 0; + virtual void insertConstants(std::stringstream& OS, const std::vector& constants) = 0; /** insert declaration of shader constants. */ - virtual void insertOutputs(std::stringstream &OS, const std::vector &outputs) = 0; + virtual void insertOutputs(std::stringstream& OS, const std::vector& outputs) = 0; /** insert beginning of main (signature, temporary declaration...) */ - virtual void insertMainStart(std::stringstream &OS) = 0; + virtual void insertMainStart(std::stringstream& OS) = 0; /** insert end of main function (return value, output copy...) */ - virtual void insertMainEnd(std::stringstream &OS) = 0; + virtual void insertMainEnd(std::stringstream& OS) = 0; public: struct diff --git a/rpcs3/Emu/RSX/RSXThread.cpp b/rpcs3/Emu/RSX/RSXThread.cpp index 714ec14863..a2435dcffe 100644 --- a/rpcs3/Emu/RSX/RSXThread.cpp +++ b/rpcs3/Emu/RSX/RSXThread.cpp @@ -765,7 +765,7 @@ namespace rsx ar(stereo_enabled, format, aspect, resolution_id, scanline_pitch, gamma, resolution_x, resolution_y, state, scan_mode); } - void thread::capture_frame(const std::string& name) + void thread::capture_frame(const std::string& name) const { frame_trace_data::draw_state draw_state{}; @@ -843,7 +843,7 @@ namespace rsx if (capture_current_frame) { - u32 element_count = rsx::method_registers.current_draw_clause.get_elements_count(); + const u32 element_count = rsx::method_registers.current_draw_clause.get_elements_count(); capture_frame(fmt::format("Draw %s %d", rsx::method_registers.current_draw_clause.primitive, element_count)); } } @@ -3222,7 +3222,7 @@ namespace rsx // capture first tile state with nop cmd rsx::frame_capture_data::replay_command replay_cmd; replay_cmd.rsx_command = std::make_pair(NV4097_NO_OPERATION, 0); - frame_capture.replay_commands.push_back(replay_cmd); + frame_capture.replay_commands.push_back(std::move(replay_cmd)); capture::capture_display_tile_state(this, frame_capture.replay_commands.back()); } else if (capture_current_frame) diff --git a/rpcs3/Emu/RSX/RSXThread.h b/rpcs3/Emu/RSX/RSXThread.h index 30ff3bd346..b8bc59ad88 100644 --- a/rpcs3/Emu/RSX/RSXThread.h +++ b/rpcs3/Emu/RSX/RSXThread.h @@ -86,6 +86,7 @@ namespace rsx bool supports_hw_msaa; // MSAA support bool supports_hw_a2one; // Alpha to one bool supports_hw_conditional_render; // Conditional render + bool supports_hw_instanced_rendering; // Instanced draws bool supports_passthrough_dma; // DMA passthrough bool supports_asynchronous_compute; // Async compute bool supports_host_gpu_labels; // Advanced host synchronization @@ -217,7 +218,7 @@ namespace rsx surface_scaling_config_t resolution_scaling_config{}; - void capture_frame(const std::string& name); + void capture_frame(const std::string& name) const; const backend_configuration& get_backend_config() const { return backend_config; } const draw_command_processor* draw_processor() const { return &m_draw_processor; } diff --git a/rpcs3/Emu/RSX/VK/VKCompute.cpp b/rpcs3/Emu/RSX/VK/VKCompute.cpp index 6803ce1f49..033e19ff07 100644 --- a/rpcs3/Emu/RSX/VK/VKCompute.cpp +++ b/rpcs3/Emu/RSX/VK/VKCompute.cpp @@ -13,20 +13,19 @@ namespace vk std::vector result; for (unsigned i = 0; i < ssbo_count; ++i) { - const auto input = glsl::program_input::make + result.push_back(glsl::program_input::make ( ::glsl::glsl_compute_program, "ssbo" + std::to_string(i), glsl::program_input_type::input_type_storage_buffer, 0, i - ); - result.push_back(input); + )); } if (use_push_constants && push_constants_size > 0) { - const auto input = glsl::program_input::make + result.push_back(glsl::program_input::make ( ::glsl::glsl_compute_program, "push_constants", @@ -34,8 +33,7 @@ namespace vk 0, 0, glsl::push_constant_ref{ .offset = 0, .size = push_constants_size } - ); - result.push_back(input); + )); } return result; diff --git a/rpcs3/Emu/RSX/VK/VKDraw.cpp b/rpcs3/Emu/RSX/VK/VKDraw.cpp index 8a6789213b..132f832416 100644 --- a/rpcs3/Emu/RSX/VK/VKDraw.cpp +++ b/rpcs3/Emu/RSX/VK/VKDraw.cpp @@ -457,6 +457,7 @@ void VKGSRender::load_texture_env() { mag_filter = VK_FILTER_NEAREST; min_filter.filter = VK_FILTER_NEAREST; + min_filter.mipmap_mode = VK_SAMPLER_MIPMAP_MODE_NEAREST; } if (min_filter.sample_mipmaps && mipmap_count > 1) diff --git a/rpcs3/Emu/RSX/VK/VKFragmentProgram.cpp b/rpcs3/Emu/RSX/VK/VKFragmentProgram.cpp index fda0690d33..29d7e6b0e4 100644 --- a/rpcs3/Emu/RSX/VK/VKFragmentProgram.cpp +++ b/rpcs3/Emu/RSX/VK/VKFragmentProgram.cpp @@ -21,7 +21,7 @@ std::string VKFragmentDecompilerThread::getFunction(FUNCTION f) return glsl::getFunctionImpl(f); } -std::string VKFragmentDecompilerThread::compareFunction(COMPARE f, const std::string &Op0, const std::string &Op1) +std::string VKFragmentDecompilerThread::compareFunction(COMPARE f, std::string_view Op0, std::string_view Op1) { return glsl::compareFunctionImpl(f, Op0, Op1); } @@ -253,14 +253,13 @@ void VKFragmentDecompilerThread::insertConstants(std::stringstream & OS) OS << "layout(set=" << vk::glsl::binding_set_index_fragment << ", binding=" << vk_prog->binding_table.frag_depth_input_location << ") uniform " << frag_depth_type << " frag_depth;\n"; - auto in = vk::glsl::program_input::make( + inputs.push_back(vk::glsl::program_input::make( glsl::glsl_fragment_program, "frag_depth", vk::glsl::input_type_texture, vk::glsl::binding_set_index_fragment, vk_prog->binding_table.frag_depth_input_location - ); - inputs.push_back(in); + )); } // Draw params are always provided by vertex program. Instead of pointer chasing, they're provided as varyings. diff --git a/rpcs3/Emu/RSX/VK/VKFragmentProgram.h b/rpcs3/Emu/RSX/VK/VKFragmentProgram.h index 0412561340..b17a67f1c5 100644 --- a/rpcs3/Emu/RSX/VK/VKFragmentProgram.h +++ b/rpcs3/Emu/RSX/VK/VKFragmentProgram.h @@ -37,8 +37,8 @@ public: protected: std::string getFloatTypeName(usz elementCount) override; std::string getHalfTypeName(usz elementCount) override; - std::string getFunction(FUNCTION) override; - std::string compareFunction(COMPARE, const std::string&, const std::string&) override; + std::string getFunction(FUNCTION f) override; + std::string compareFunction(COMPARE f, std::string_view Op0, std::string_view Op1) override; void insertHeader(std::stringstream &OS) override; void insertInputs(std::stringstream &OS) override; diff --git a/rpcs3/Emu/RSX/VK/VKGSRender.cpp b/rpcs3/Emu/RSX/VK/VKGSRender.cpp index d8ac9fccf1..e8acd2ec4c 100644 --- a/rpcs3/Emu/RSX/VK/VKGSRender.cpp +++ b/rpcs3/Emu/RSX/VK/VKGSRender.cpp @@ -640,6 +640,7 @@ VKGSRender::VKGSRender(utils::serial* ar) noexcept : GSRender(ar) } backend_config.supports_multidraw = true; + backend_config.supports_hw_instanced_rendering = true; // NVIDIA has broken attribute interpolation backend_config.supports_normalized_barycentrics = ( diff --git a/rpcs3/Emu/RSX/VK/VKGSRenderTypes.hpp b/rpcs3/Emu/RSX/VK/VKGSRenderTypes.hpp index 0a400c68d9..0b80a5d4e6 100644 --- a/rpcs3/Emu/RSX/VK/VKGSRenderTypes.hpp +++ b/rpcs3/Emu/RSX/VK/VKGSRenderTypes.hpp @@ -254,18 +254,18 @@ namespace vk void consumer_wait() const { - while (num_waiters.load() != 0) + utils::spin_wait(num_waiters, [](auto v) { - utils::pause(); - } + return v == 0; + }); } void producer_wait() const { - while (pending_state.load()) + utils::spin_wait(pending_state, [](auto v) { - std::this_thread::yield(); - } + return !v; + }); } }; diff --git a/rpcs3/Emu/RSX/VK/VKOverlays.cpp b/rpcs3/Emu/RSX/VK/VKOverlays.cpp index f879a62ddf..b3deb880df 100644 --- a/rpcs3/Emu/RSX/VK/VKOverlays.cpp +++ b/rpcs3/Emu/RSX/VK/VKOverlays.cpp @@ -67,23 +67,20 @@ namespace vk for (u32 n = 0; n < m_num_uniform_buffers; ++n, ++binding) { - const std::string name = std::string("static_data") + (n > 0 ? std::to_string(n) : ""); - const auto input = program_input::make(::glsl::program_domain::glsl_fragment_program, name, program_input_type::input_type_uniform_buffer, 0, 0); - fs_inputs.push_back(input); + std::string name = std::string("static_data") + (n > 0 ? std::to_string(n) : ""); + fs_inputs.push_back(program_input::make(::glsl::program_domain::glsl_fragment_program, std::move(name), program_input_type::input_type_uniform_buffer, 0, 0)); } for (u32 n = 0; n < m_num_usable_samplers; ++n, ++binding) { - const std::string name = "fs" + std::to_string(n); - const auto input = program_input::make(::glsl::program_domain::glsl_fragment_program, name, program_input_type::input_type_texture, 0, binding); - fs_inputs.push_back(input); + std::string name = "fs" + std::to_string(n); + fs_inputs.push_back(program_input::make(::glsl::program_domain::glsl_fragment_program, std::move(name), program_input_type::input_type_texture, 0, binding)); } for (u32 n = 0; n < m_num_input_attachments; ++n, ++binding) { - const std::string name = "sp" + std::to_string(n); - const auto input = program_input::make(::glsl::program_domain::glsl_fragment_program, name, program_input_type::input_type_texture, 0, binding); - fs_inputs.push_back(input); + std::string name = "sp" + std::to_string(n); + fs_inputs.push_back(program_input::make(::glsl::program_domain::glsl_fragment_program, std::move(name), program_input_type::input_type_texture, 0, binding)); } return fs_inputs; diff --git a/rpcs3/Emu/RSX/VK/VKProgramPipeline.cpp b/rpcs3/Emu/RSX/VK/VKProgramPipeline.cpp index 48874f55d0..7ae53c4c5e 100644 --- a/rpcs3/Emu/RSX/VK/VKProgramPipeline.cpp +++ b/rpcs3/Emu/RSX/VK/VKProgramPipeline.cpp @@ -282,7 +282,7 @@ namespace vk return *this; } - bool program::has_uniform(program_input_type type, const std::string& uniform_name) + bool program::has_uniform(program_input_type type, std::string_view uniform_name) { for (auto& set : m_sets) { @@ -296,7 +296,7 @@ namespace vk return false; } - std::pair program::get_uniform_location(::glsl::program_domain domain, program_input_type type, const std::string& uniform_name) + std::pair program::get_uniform_location(::glsl::program_domain domain, program_input_type type, std::string_view uniform_name) { for (unsigned i = 0; i < ::size32(m_sets); ++i) { @@ -632,7 +632,7 @@ namespace vk std::unordered_map descriptor_type_map; - auto descriptor_count = [](const std::string& name) -> u32 + auto descriptor_count = [](std::string_view name) -> u32 { const auto start = name.find_last_of("["); if (start == std::string::npos) @@ -643,8 +643,8 @@ namespace vk const auto end = name.find_last_of("]"); ensure(end != std::string::npos && start < end, "Invalid variable name"); - const std::string array_size = name.substr(start + 1, end - start - 1); - if (const auto count = std::atoi(array_size.c_str()); + const std::string_view array_size = name.substr(start + 1, end - start - 1); + if (const auto count = std::atoi(array_size.data()); count > 0) { return count; diff --git a/rpcs3/Emu/RSX/VK/VKProgramPipeline.h b/rpcs3/Emu/RSX/VK/VKProgramPipeline.h index dba619ec88..2beb2b90eb 100644 --- a/rpcs3/Emu/RSX/VK/VKProgramPipeline.h +++ b/rpcs3/Emu/RSX/VK/VKProgramPipeline.h @@ -89,6 +89,25 @@ namespace vk .name = name }; } + + static program_input make( + ::glsl::program_domain domain, + std::string&& name, + program_input_type type, + u32 set, + u32 location, + const bound_data_t& data = bound_buffer{}) + { + return program_input + { + .domain = domain, + .type = type, + .bound_data = data, + .set = set, + .location = location, + .name = std::move(name) + }; + } }; class shader @@ -202,13 +221,12 @@ namespace vk program& link(bool separate_stages); program& bind(const vk::command_buffer& cmd, VkPipelineBindPoint bind_point); - bool has_uniform(program_input_type type, const std::string &uniform_name); - std::pair get_uniform_location(::glsl::program_domain domain, program_input_type type, const std::string& uniform_name); + bool has_uniform(program_input_type type, std::string_view uniform_name); + std::pair get_uniform_location(::glsl::program_domain domain, program_input_type type, std::string_view uniform_name); void bind_uniform(const VkDescriptorImageInfoEx& image_descriptor, u32 set_id, u32 binding_point); void bind_uniform(const VkDescriptorBufferInfoEx& buffer_descriptor, u32 set_id, u32 binding_point); void bind_uniform(const VkDescriptorBufferViewEx& buffer_view, u32 set_id, u32 binding_point); - void bind_uniform(const VkDescriptorBufferViewEx& buffer_view, ::glsl::program_domain domain, program_input_type type, const std::string &binding_name); void bind_uniform_array(const std::span& image_descriptors,u32 set_id, u32 binding_point); diff --git a/rpcs3/Emu/RSX/VK/VKRenderPass.cpp b/rpcs3/Emu/RSX/VK/VKRenderPass.cpp index 0f6ef23458..9dc578ef66 100644 --- a/rpcs3/Emu/RSX/VK/VKRenderPass.cpp +++ b/rpcs3/Emu/RSX/VK/VKRenderPass.cpp @@ -301,7 +301,7 @@ namespace vk color_attachment_description.initialLayout = layout; color_attachment_description.finalLayout = layout; - attachments.push_back(color_attachment_description); + attachments.push_back(std::move(color_attachment_description)); attachment_references.push_back({ attachment_count++, layout }); } @@ -316,7 +316,7 @@ namespace vk depth_attachment_description.stencilStoreOp = VK_ATTACHMENT_STORE_OP_STORE; depth_attachment_description.initialLayout = dsv_layout; depth_attachment_description.finalLayout = dsv_layout; - attachments.push_back(depth_attachment_description); + attachments.push_back(std::move(depth_attachment_description)); attachment_references.push_back({ attachment_count, dsv_layout }); } diff --git a/rpcs3/Emu/RSX/VK/VKShaderInterpreter.cpp b/rpcs3/Emu/RSX/VK/VKShaderInterpreter.cpp index fef2851477..976f80086f 100644 --- a/rpcs3/Emu/RSX/VK/VKShaderInterpreter.cpp +++ b/rpcs3/Emu/RSX/VK/VKShaderInterpreter.cpp @@ -257,13 +257,14 @@ namespace vk // Declare local inputs auto vs_inputs = comp.get_inputs(); - vk::glsl::program_input in; - in.set = 0; - in.domain = ::glsl::glsl_vertex_program; - in.location = vertex_instruction_start; - in.type = glsl::input_type_storage_buffer; - in.name = "VertexInstructionBlock"; - vs_inputs.push_back(in); + vs_inputs.push_back(vk::glsl::program_input::make + ( + ::glsl::glsl_vertex_program, + "VertexInstructionBlock", + glsl::input_type_storage_buffer, + 0, + vertex_instruction_start + )); vk_prog->SetInputs(vs_inputs); @@ -426,12 +427,14 @@ namespace vk // Declare local inputs auto inputs = comp.get_inputs(); - vk::glsl::program_input in; - in.set = 1; - in.domain = ::glsl::glsl_fragment_program; - in.location = fragment_instruction_start; - in.type = glsl::input_type_storage_buffer; - in.name = "FragmentInstructionBlock"; + vk::glsl::program_input in = vk::glsl::program_input::make + ( + ::glsl::glsl_fragment_program, + "FragmentInstructionBlock", + glsl::input_type_storage_buffer, + 1, + fragment_instruction_start + ); inputs.push_back(in); if (compiler_options & COMPILER_OPT_ENABLE_TEXTURES) diff --git a/rpcs3/Emu/RSX/VK/VKTextureCache.cpp b/rpcs3/Emu/RSX/VK/VKTextureCache.cpp index 671529f87d..bf990010c8 100644 --- a/rpcs3/Emu/RSX/VK/VKTextureCache.cpp +++ b/rpcs3/Emu/RSX/VK/VKTextureCache.cpp @@ -1265,7 +1265,7 @@ namespace vk upload_command_flags |= source_is_gpu_resident; heap_align = width * bpp; - tmp.push_back(subres); + tmp.push_back(std::move(subres)); p_subresource_layout = &tmp; } } diff --git a/rpcs3/Emu/RSX/VK/VKVertexProgram.cpp b/rpcs3/Emu/RSX/VK/VKVertexProgram.cpp index 35e5f80265..7496679c40 100644 --- a/rpcs3/Emu/RSX/VK/VKVertexProgram.cpp +++ b/rpcs3/Emu/RSX/VK/VKVertexProgram.cpp @@ -21,7 +21,7 @@ std::string VKVertexDecompilerThread::getFunction(FUNCTION f) return glsl::getFunctionImpl(f); } -std::string VKVertexDecompilerThread::compareFunction(COMPARE f, const std::string &Op0, const std::string &Op1, bool scalar) +std::string VKVertexDecompilerThread::compareFunction(COMPARE f, std::string_view Op0, std::string_view Op1, bool scalar) { return glsl::compareFunctionImpl(f, Op0, Op1, scalar); } @@ -69,7 +69,7 @@ void VKVertexDecompilerThread::prepareBindingTable() } } -void VKVertexDecompilerThread::insertHeader(std::stringstream &OS) +void VKVertexDecompilerThread::insertHeader(std::stringstream& OS) { prepareBindingTable(); @@ -95,7 +95,7 @@ void VKVertexDecompilerThread::insertHeader(std::stringstream &OS) " vertex_context_t vertex_contexts[];\n" "};\n\n"; - const vk::glsl::program_input context_input + vk::glsl::program_input context_input { .domain = glsl::glsl_vertex_program, .type = vk::glsl::input_type_uniform_buffer, @@ -103,7 +103,7 @@ void VKVertexDecompilerThread::insertHeader(std::stringstream &OS) .location = vk_prog->binding_table.context_buffer_location, .name = "VertexContextBuffer" }; - inputs.push_back(context_input); + inputs.push_back(std::move(context_input)); if (m_device_props.emulate_conditional_rendering) { @@ -113,7 +113,7 @@ void VKVertexDecompilerThread::insertHeader(std::stringstream &OS) " uint cr_predicate_value;\n" "};\n\n"; - const vk::glsl::program_input predicate_input + vk::glsl::program_input predicate_input { .domain = glsl::glsl_vertex_program, .type = vk::glsl::input_type_storage_buffer, @@ -121,7 +121,7 @@ void VKVertexDecompilerThread::insertHeader(std::stringstream &OS) .location = vk_prog->binding_table.cr_pred_buffer_location, .name = "EXT_Conditional_Rendering" }; - inputs.push_back(predicate_input); + inputs.push_back(std::move(predicate_input)); } OS << @@ -130,7 +130,7 @@ void VKVertexDecompilerThread::insertHeader(std::stringstream &OS) " draw_parameters_t draw_parameters[];\n" "};\n\n"; - const vk::glsl::program_input layouts_input + vk::glsl::program_input layouts_input { .domain = glsl::glsl_vertex_program, .type = vk::glsl::input_type_storage_buffer, @@ -138,7 +138,7 @@ void VKVertexDecompilerThread::insertHeader(std::stringstream &OS) .location = vk_prog->binding_table.vertex_buffers_location + 2, .name = "DrawParametersBuffer" }; - inputs.push_back(layouts_input); + inputs.push_back(std::move(layouts_input)); OS << "layout(push_constant) uniform push_constants_block\n" @@ -146,7 +146,7 @@ void VKVertexDecompilerThread::insertHeader(std::stringstream &OS) " uint draw_parameters_offset;\n" "};\n\n"; - const vk::glsl::program_input push_constants + vk::glsl::program_input push_constants { .domain = glsl::glsl_vertex_program, .type = vk::glsl::input_type_push_constant, @@ -155,7 +155,7 @@ void VKVertexDecompilerThread::insertHeader(std::stringstream &OS) .location = umax, .name = "push_constants_block" }; - inputs.push_back(push_constants); + inputs.push_back(std::move(push_constants)); } void VKVertexDecompilerThread::insertInputs(std::stringstream& OS, const std::vector& /*inputs*/) @@ -171,19 +171,18 @@ void VKVertexDecompilerThread::insertInputs(std::stringstream& OS, const std::ve { OS << "layout(set=0, binding=" << location << ") uniform usamplerBuffer " << stream << ";\n"; - const vk::glsl::program_input input + this->inputs.push_back(vk::glsl::program_input { .domain = glsl::glsl_vertex_program, .type = vk::glsl::input_type_texel_buffer, .set = vk::glsl::binding_set_index_vertex, .location = location++, .name = stream - }; - this->inputs.push_back(input); + }); } } -void VKVertexDecompilerThread::insertConstants(std::stringstream & OS, const std::vector & constants) +void VKVertexDecompilerThread::insertConstants(std::stringstream& OS, const std::vector& constants) { vk::glsl::program_input in { @@ -331,7 +330,7 @@ void VKVertexDecompilerThread::insertFSExport(std::stringstream& OS) "}\n\n"; } -void VKVertexDecompilerThread::insertMainStart(std::stringstream & OS) +void VKVertexDecompilerThread::insertMainStart(std::stringstream& OS) { glsl::shader_properties properties2{}; properties2.domain = glsl::glsl_vertex_program; @@ -389,9 +388,9 @@ void VKVertexDecompilerThread::insertMainStart(std::stringstream & OS) OS << "{\n"; //Declare temporary registers, ignoring those mapped to outputs - for (const ParamType &PT : m_parr.params[PF_PARAM_NONE]) + for (const ParamType& PT : m_parr.params[PF_PARAM_NONE]) { - for (const ParamItem &PI : PT.items) + for (const ParamItem& PI : PT.items) { if (PI.name.starts_with("dst_reg")) continue; @@ -404,9 +403,9 @@ void VKVertexDecompilerThread::insertMainStart(std::stringstream & OS) } } - for (const ParamType &PT : m_parr.params[PF_PARAM_IN]) + for (const ParamType& PT : m_parr.params[PF_PARAM_IN]) { - for (const ParamItem &PI : PT.items) + for (const ParamItem& PI : PT.items) { OS << " vec4 " << PI.name << "= read_location(" << std::to_string(PI.location) << ");\n"; } @@ -415,7 +414,7 @@ void VKVertexDecompilerThread::insertMainStart(std::stringstream & OS) OS << "\nuint xform_constants_offset = get_draw_params().xform_constants_offset;\n\n"; } -void VKVertexDecompilerThread::insertMainEnd(std::stringstream & OS) +void VKVertexDecompilerThread::insertMainEnd(std::stringstream& OS) { OS << "}\n\n"; diff --git a/rpcs3/Emu/RSX/VK/VKVertexProgram.h b/rpcs3/Emu/RSX/VK/VKVertexProgram.h index c36f089a2e..e5aeee0c3d 100644 --- a/rpcs3/Emu/RSX/VK/VKVertexProgram.h +++ b/rpcs3/Emu/RSX/VK/VKVertexProgram.h @@ -25,21 +25,21 @@ struct VKVertexDecompilerThread : public VertexProgramDecompiler protected: std::string getFloatTypeName(usz elementCount) override; std::string getIntTypeName(usz elementCount) override; - std::string getFunction(FUNCTION) override; - std::string compareFunction(COMPARE, const std::string&, const std::string&, bool scalar) override; + std::string getFunction(FUNCTION f) override; + std::string compareFunction(COMPARE f, std::string_view Op0, std::string_view Op1, bool scalar) override; - void insertHeader(std::stringstream &OS) override; - void insertInputs(std::stringstream &OS, const std::vector &inputs) override; - void insertConstants(std::stringstream &OS, const std::vector &constants) override; - void insertOutputs(std::stringstream &OS, const std::vector &outputs) override; - void insertMainStart(std::stringstream &OS) override; - void insertMainEnd(std::stringstream &OS) override; + void insertHeader(std::stringstream& OS) override; + void insertInputs(std::stringstream& OS, const std::vector& inputs) override; + void insertConstants(std::stringstream& OS, const std::vector& constants) override; + void insertOutputs(std::stringstream& OS, const std::vector& outputs) override; + void insertMainStart(std::stringstream& OS) override; + void insertMainEnd(std::stringstream& OS) override; void prepareBindingTable(); const RSXVertexProgram &rsx_vertex_program; public: - VKVertexDecompilerThread(const RSXVertexProgram &prog, std::string& shader, ParamArray&, class VKVertexProgram &dst) + VKVertexDecompilerThread(const RSXVertexProgram& prog, std::string& shader, ParamArray&, class VKVertexProgram& dst) : VertexProgramDecompiler(prog) , m_shader(shader) , vk_prog(&dst) diff --git a/rpcs3/Emu/RSX/VK/vkutils/device.cpp b/rpcs3/Emu/RSX/VK/vkutils/device.cpp index 2c37298440..58fcc6e70a 100644 --- a/rpcs3/Emu/RSX/VK/vkutils/device.cpp +++ b/rpcs3/Emu/RSX/VK/vkutils/device.cpp @@ -1032,7 +1032,7 @@ namespace vk for (u32 i = 0; i < memory_properties.memoryTypeCount; i++) { - auto& type_info = memory_properties.memoryTypes[i]; + const auto& type_info = memory_properties.memoryTypes[i]; memory_heap_map[type_info.heapIndex].types.push_back({ i, type_info.propertyFlags, 0 }); } @@ -1040,9 +1040,9 @@ namespace vk { std::vector results; - for (auto& heap : memory_heap_map) + for (const auto& heap : memory_heap_map) { - for (auto &type : heap.types) + for (const auto& type : heap.types) { if (((type.flags & desired_flags) == desired_flags) && !(type.flags & excluded_flags)) { diff --git a/rpcs3/Emu/RSX/VK/vkutils/sync.cpp b/rpcs3/Emu/RSX/VK/vkutils/sync.cpp index 322e06460f..6d36283ca1 100644 --- a/rpcs3/Emu/RSX/VK/vkutils/sync.cpp +++ b/rpcs3/Emu/RSX/VK/vkutils/sync.cpp @@ -454,13 +454,13 @@ namespace vk dst_stages |= VK_PIPELINE_STAGE_TRANSFER_BIT; if (memory_barriers.empty()) { - const VkMemoryBarrier signal_barrier = + VkMemoryBarrier signal_barrier = { .sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER, .srcAccessMask = VK_ACCESS_MEMORY_READ_BIT | VK_ACCESS_MEMORY_WRITE_BIT, .dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT }; - memory_barriers.push_back(signal_barrier); + memory_barriers.push_back(std::move(signal_barrier)); } else { diff --git a/rpcs3/Emu/System.cpp b/rpcs3/Emu/System.cpp index fc44b8cabb..8f36b9a899 100644 --- a/rpcs3/Emu/System.cpp +++ b/rpcs3/Emu/System.cpp @@ -2212,11 +2212,11 @@ game_boot_result Emulator::Load(const std::string& title_id, bool is_disc_patch, for (auto&& entry : fs::dir{ins_dir}) { - const std::string pkg_file = ins_dir + entry.name; + std::string pkg_file = ins_dir + entry.name; if (!entry.is_directory && entry.name.ends_with(".PKG")) { - pkgs.push_back(pkg_file); + pkgs.push_back(std::move(pkg_file)); } } } @@ -2229,11 +2229,11 @@ game_boot_result Emulator::Load(const std::string& title_id, bool is_disc_patch, { if (entry.is_directory && entry.name.starts_with("PKG")) { - const std::string pkg_file = pkg_dir + entry.name + "/INSTALL.PKG"; + std::string pkg_file = pkg_dir + entry.name + "/INSTALL.PKG"; if (fs::is_file(pkg_file)) { - pkgs.push_back(pkg_file); + pkgs.push_back(std::move(pkg_file)); } } } @@ -2247,11 +2247,11 @@ game_boot_result Emulator::Load(const std::string& title_id, bool is_disc_patch, { if (entry.is_directory && entry.name[0] == 'D') { - const std::string pkg_file = extra_dir + entry.name + "/DATA000.PKG"; + std::string pkg_file = extra_dir + entry.name + "/DATA000.PKG"; if (fs::is_file(pkg_file)) { - pkgs.push_back(pkg_file); + pkgs.push_back(std::move(pkg_file)); } } } @@ -4276,10 +4276,12 @@ std::set Emulator::GetGameDirs() const return dirs; } -u32 Emulator::AddGamesFromDir(const std::string& path) +u32 Emulator::AddGamesFromDir(std::string path) { u32 games_added = 0; + fmt::trim_back(path, fs::delim); + m_games_config.set_save_on_dirty(false); // search for a game on the provided path first (game on ISO file or on folder type) @@ -4313,7 +4315,7 @@ u32 Emulator::AddGamesFromDir(const std::string& path) continue; } - const std::string dir_path = path + '/' + dir_entry.name; + const std::string dir_path = path + "/" + dir_entry.name; if (!dir_entry.is_directory && !is_iso_file(dir_path)) { @@ -4349,8 +4351,10 @@ u32 Emulator::AddGamesFromDir(const std::string& path) return games_added; } -game_boot_result Emulator::AddGame(const std::string& path) +game_boot_result Emulator::AddGame(std::string path) { + fmt::trim_back(path, fs::delim); + // Handle files directly if (!fs::is_dir(path) || fs::get_optical_raw_device(path)) { @@ -4396,8 +4400,10 @@ game_boot_result Emulator::AddGame(const std::string& path) return result; } -game_boot_result Emulator::AddGameToYml(const std::string& path) +game_boot_result Emulator::AddGameToYml(std::string path) { + fmt::trim_back(path, fs::delim); + // Detect boot location const auto is_invalid_path = [this](std::string_view path, std::string_view dir) -> game_boot_result { @@ -4843,7 +4849,7 @@ bool Emulator::IsValidSfb(const std::string& path) return false; } -void Emulator::SaveSettings(const std::string& settings, const std::string& title_id) +void Emulator::SaveSettings(std::string_view settings, const std::string& title_id) { std::string config_name; @@ -4864,7 +4870,7 @@ void Emulator::SaveSettings(const std::string& settings, const std::string& titl } else { - temp.file.write(settings.c_str(), settings.size()); + temp.file.write(settings.data(), settings.size()); if (!temp.commit()) { sys_log.error("Could not save config to %s (failed to commit) (error=%s)", config_name, fs::g_tls_error); @@ -4875,7 +4881,7 @@ void Emulator::SaveSettings(const std::string& settings, const std::string& titl if (config_name == g_cfg.name || title_id == Emu.GetTitleID()) { // Update current config - if (!g_cfg.from_string({settings.c_str(), settings.size()}, !Emu.IsStopped())) + if (!g_cfg.from_string(settings, !Emu.IsStopped())) { sys_log.fatal("Failed to update configuration"); } diff --git a/rpcs3/Emu/System.h b/rpcs3/Emu/System.h index e0efebc1d8..9a9b00c865 100644 --- a/rpcs3/Emu/System.h +++ b/rpcs3/Emu/System.h @@ -489,9 +489,9 @@ public: void ConfigurePPUCache() const; std::set GetGameDirs() const; - u32 AddGamesFromDir(const std::string& path); - game_boot_result AddGame(const std::string& path); - game_boot_result AddGameToYml(const std::string& path); + u32 AddGamesFromDir(std::string path); + game_boot_result AddGame(std::string path); + game_boot_result AddGameToYml(std::string path); u32 RemoveGamesFromDir(const std::string& games_dir, const std::vector& serials_to_remove_from_yml = {}, bool save_on_disk = true); u32 RemoveGames(const std::vector& title_id_list, bool save_on_disk = true); game_boot_result RemoveGameFromYml(const std::string& title_id); @@ -510,7 +510,7 @@ public: static bool IsVsh(); static bool IsValidSfb(const std::string& path); - static void SaveSettings(const std::string& settings, const std::string& title_id); + static void SaveSettings(std::string_view settings, const std::string& title_id); }; extern Emulator Emu; diff --git a/rpcs3/Emu/savestate_utils.cpp b/rpcs3/Emu/savestate_utils.cpp index 7c0cd7390c..ba68dceba2 100644 --- a/rpcs3/Emu/savestate_utils.cpp +++ b/rpcs3/Emu/savestate_utils.cpp @@ -606,7 +606,7 @@ namespace stx if ((saved ^ tag) & data_mask) { ensure(!ar.is_writing()); - fmt::throw_exception("serial_breathe_and_tag(%u): %s\nobject: '%s', next-object: '%s', expected/tag: 0x%x != 0x%x,", s_tls_call_count, ar, s_tls_object_name, name, tag, saved); + fmt::throw_exception("serial_breathe_and_tag(%u): %s\nobject: '%s', next-object: '%s', expected/tag: 0x%x != 0x%x\nLatest known RPCS3 build working for savestates: 0.0.41-19455", s_tls_call_count, ar, s_tls_object_name, name, tag, saved); } s_tls_object_name = name; diff --git a/rpcs3/Emu/system_config.h b/rpcs3/Emu/system_config.h index 89ee372a82..ea7de326f8 100644 --- a/rpcs3/Emu/system_config.h +++ b/rpcs3/Emu/system_config.h @@ -41,6 +41,7 @@ struct cfg_root : cfg::node cfg::_bool spu_accurate_reservations{ this, "Accurate SPU Reservations", true }; cfg::_bool accurate_cache_line_stores{ this, "Accurate Cache Line Stores", false }; cfg::_bool rsx_accurate_res_access{this, "Accurate RSX reservation access", false, true}; + cfg::_bool ppu_reservation_priority_over_spu{this, "PPU Reservation Priority Over SPUs", false, true}; struct fifo_setting : public cfg::_enum { diff --git a/rpcs3/Emu/system_utils.cpp b/rpcs3/Emu/system_utils.cpp index b1235dabf6..f95d65fd69 100644 --- a/rpcs3/Emu/system_utils.cpp +++ b/rpcs3/Emu/system_utils.cpp @@ -56,13 +56,13 @@ namespace rpcs3::utils was_silenced = silenced; } - u32 check_user(const std::string& user) + u32 check_user(std::string_view user) { u32 id = 0; if (user.size() == 8) { - std::from_chars(&user.front(), &user.back() + 1, id); + std::from_chars(user.data(), user.data() + user.size(), id); } return id; @@ -256,6 +256,36 @@ namespace rpcs3::utils return get_data_dir() + "redump/"; } + std::string get_psn_content_db_path() + { + return fs::get_config_dir(true) + "psn_content.dat"; + } + + std::string get_psn_content_db_download_url() + { + return "https://api.rpcs3.net/nointro/content/?api=v1"; + } + + std::string get_psn_dlc_db_path() + { + return fs::get_config_dir(true) + "psn_dlc.dat"; + } + + std::string get_psn_dlc_db_download_url() + { + return "https://api.rpcs3.net/nointro/dlc/?api=v1"; + } + + std::string get_psn_update_db_path() + { + return fs::get_config_dir(true) + "psn_update.dat"; + } + + std::string get_psn_update_db_download_url() + { + return "https://api.rpcs3.net/nointro/updates/?api=v1"; + } + std::string get_data_dir() { return fs::get_config_dir() + "data/"; @@ -553,7 +583,7 @@ namespace rpcs3::utils const bool check_disc = !disc_dir.empty(); const bool check_hdd0 = !hdd0_dir.empty() && !check_disc; - const auto find_content = [&](const std::string& name, const std::string& extension) -> std::string + const auto find_content = [&](std::string_view name, std::string_view extension) -> std::string { // Check localized content first for (bool localized : { true, false }) diff --git a/rpcs3/Emu/system_utils.hpp b/rpcs3/Emu/system_utils.hpp index 1545cbf829..ae6e8bf3d3 100644 --- a/rpcs3/Emu/system_utils.hpp +++ b/rpcs3/Emu/system_utils.hpp @@ -21,7 +21,7 @@ namespace rpcs3::utils void configure_logs(bool force_enable = false); - u32 check_user(const std::string& user); + u32 check_user(std::string_view user); bool install_pkg(const std::string& path); @@ -49,6 +49,12 @@ namespace rpcs3::utils std::string get_redump_db_path(); std::string get_redump_db_download_url(); std::string get_redump_key_dir(); + std::string get_psn_content_db_path(); + std::string get_psn_content_db_download_url(); + std::string get_psn_dlc_db_path(); + std::string get_psn_dlc_db_download_url(); + std::string get_psn_update_db_path(); + std::string get_psn_update_db_download_url(); std::string get_data_dir(); std::string get_icons_dir(); diff --git a/rpcs3/Emu/title.cpp b/rpcs3/Emu/title.cpp index ff614397cb..62306f0729 100644 --- a/rpcs3/Emu/title.cpp +++ b/rpcs3/Emu/title.cpp @@ -3,6 +3,11 @@ #include "rpcs3_version.h" #include "util/sysinfo.hpp" +#include "Emu/system_config.h" + +#if !(defined(__ANDROID__) || defined(__APPLE__)) +#include "Emu/RSX/GL/glutils/capabilities.h" +#endif namespace rpcs3 { @@ -40,6 +45,11 @@ namespace rpcs3 title_string += '%'; break; } + case 'A': + { + title_string += utils::get_architecture(); + break; + } case 'T': { title_string += title_data.title; @@ -68,7 +78,22 @@ namespace rpcs3 } case 'G': { - title_string += title_data.vulkan_adapter; + switch (g_cfg.video.renderer.get()) + { + case video_renderer::null: + title_string += "null"; + break; + case video_renderer::opengl: +#if !(defined(__ANDROID__) || defined(__APPLE__)) + title_string += gl::get_device_name(); +#else + title_string += "OpenGL GPU"; +#endif + break; + case video_renderer::vulkan: + title_string += title_data.vulkan_adapter; + break; + } break; } case 'C': diff --git a/rpcs3/Emu/vfs_config.cpp b/rpcs3/Emu/vfs_config.cpp index bd292f9a25..0958c01b28 100644 --- a/rpcs3/Emu/vfs_config.cpp +++ b/rpcs3/Emu/vfs_config.cpp @@ -11,9 +11,9 @@ std::string cfg_vfs::get(const cfg::string& _cfg, std::string_view emu_dir) cons return get(_cfg.to_string(), _cfg.def, emu_dir); } -std::string cfg_vfs::get(const std::string& _cfg, const std::string& def, std::string_view emu_dir) const +std::string cfg_vfs::get(std::string_view _cfg, std::string_view def, std::string_view emu_dir) const { - std::string path = _cfg; + std::string path = std::string(_cfg); // Fallback if (path.empty()) diff --git a/rpcs3/Emu/vfs_config.h b/rpcs3/Emu/vfs_config.h index eaba041344..feedf0d668 100644 --- a/rpcs3/Emu/vfs_config.h +++ b/rpcs3/Emu/vfs_config.h @@ -42,7 +42,7 @@ struct cfg_vfs : cfg::node cfg::device_info get_device(const cfg::device_entry& _cfg, std::string_view key, std::string_view emu_dir = {}) const; private: - std::string get(const std::string& _cfg, const std::string& def, std::string_view emu_dir) const; + std::string get(std::string_view _cfg, std::string_view def, std::string_view emu_dir) const; cfg::device_info get_device_info(const cfg::device_entry& _cfg, std::string_view key, std::string_view emu_dir = {}) const; }; diff --git a/rpcs3/GLGSRender.vcxproj b/rpcs3/GLGSRender.vcxproj index 8d6ba02dd9..c48f3845b9 100644 --- a/rpcs3/GLGSRender.vcxproj +++ b/rpcs3/GLGSRender.vcxproj @@ -66,6 +66,7 @@ + @@ -102,6 +103,7 @@ + diff --git a/rpcs3/GLGSRender.vcxproj.filters b/rpcs3/GLGSRender.vcxproj.filters index c4fe752d10..72aee7172c 100644 --- a/rpcs3/GLGSRender.vcxproj.filters +++ b/rpcs3/GLGSRender.vcxproj.filters @@ -49,6 +49,9 @@ + + glutils + @@ -125,6 +128,9 @@ glutils + + glutils + diff --git a/rpcs3/Icons/combo_config_bordered.png b/rpcs3/Icons/combo_config_bordered.png index e7c8b1b84a..6fb3fa919a 100644 Binary files a/rpcs3/Icons/combo_config_bordered.png and b/rpcs3/Icons/combo_config_bordered.png differ diff --git a/rpcs3/Icons/configure.png b/rpcs3/Icons/configure.png index c023e773df..4ac8730e97 100644 Binary files a/rpcs3/Icons/configure.png and b/rpcs3/Icons/configure.png differ diff --git a/rpcs3/Icons/controllers.png b/rpcs3/Icons/controllers.png index af31011fb6..9b15159c21 100644 Binary files a/rpcs3/Icons/controllers.png and b/rpcs3/Icons/controllers.png differ diff --git a/rpcs3/Icons/custom_config.png b/rpcs3/Icons/custom_config.png index 1dc0cf0390..9ccef16ad4 100644 Binary files a/rpcs3/Icons/custom_config.png and b/rpcs3/Icons/custom_config.png differ diff --git a/rpcs3/Icons/exit_fullscreen.png b/rpcs3/Icons/exit_fullscreen.png index d2f5a46070..acbf1fbfe2 100644 Binary files a/rpcs3/Icons/exit_fullscreen.png and b/rpcs3/Icons/exit_fullscreen.png differ diff --git a/rpcs3/Icons/fullscreen.png b/rpcs3/Icons/fullscreen.png index f75326efa3..e7bf0c9d9c 100644 Binary files a/rpcs3/Icons/fullscreen.png and b/rpcs3/Icons/fullscreen.png differ diff --git a/rpcs3/Icons/grid.png b/rpcs3/Icons/grid.png index 128e32adf9..7cf12acb0a 100644 Binary files a/rpcs3/Icons/grid.png and b/rpcs3/Icons/grid.png differ diff --git a/rpcs3/Icons/list.png b/rpcs3/Icons/list.png index 059afae886..2b411c29cf 100644 Binary files a/rpcs3/Icons/list.png and b/rpcs3/Icons/list.png differ diff --git a/rpcs3/Icons/open.png b/rpcs3/Icons/open.png index 354171345a..f33cb7b4b1 100644 Binary files a/rpcs3/Icons/open.png and b/rpcs3/Icons/open.png differ diff --git a/rpcs3/Icons/pause.png b/rpcs3/Icons/pause.png index e5a22b88ca..9b2df58036 100644 Binary files a/rpcs3/Icons/pause.png and b/rpcs3/Icons/pause.png differ diff --git a/rpcs3/Icons/play.png b/rpcs3/Icons/play.png index 8675cd3b69..5abba54de4 100644 Binary files a/rpcs3/Icons/play.png and b/rpcs3/Icons/play.png differ diff --git a/rpcs3/Icons/refresh.png b/rpcs3/Icons/refresh.png index 425e147ecc..a3f45b1ca2 100644 Binary files a/rpcs3/Icons/refresh.png and b/rpcs3/Icons/refresh.png differ diff --git a/rpcs3/Icons/restart.png b/rpcs3/Icons/restart.png index 6c231205ef..f4164c298d 100644 Binary files a/rpcs3/Icons/restart.png and b/rpcs3/Icons/restart.png differ diff --git a/rpcs3/Icons/rpcn.png b/rpcs3/Icons/rpcn.png index 1470fa7999..64ed61d263 100644 Binary files a/rpcs3/Icons/rpcn.png and b/rpcs3/Icons/rpcn.png differ diff --git a/rpcs3/Icons/stop.png b/rpcs3/Icons/stop.png index e9ed61ea2c..7ad63da242 100644 Binary files a/rpcs3/Icons/stop.png and b/rpcs3/Icons/stop.png differ diff --git a/rpcs3/Input/evdev_joystick_handler.cpp b/rpcs3/Input/evdev_joystick_handler.cpp index a8a957502a..5bb03c5852 100644 --- a/rpcs3/Input/evdev_joystick_handler.cpp +++ b/rpcs3/Input/evdev_joystick_handler.cpp @@ -371,7 +371,7 @@ PadHandlerBase::connection evdev_joystick_handler::get_next_button_press(const s const auto data = GetButtonValues(device); - const auto find_value = [&, this](const std::string& str) + const auto find_value = [&, this](std::string_view str) { const std::vector combos = cfg_pad::get_combos(str); @@ -1419,7 +1419,7 @@ bool evdev_joystick_handler::bindPadToDevice(std::shared_ptr pad) e_sensor.mirrored = sensor.mirrored.get(); e_sensor.shift = sensor.shift.get(); - const std::vector> combos = find_key_combos(motion_axis_list, sensor.axis); + const std::vector> combos = find_key_combos(motion_axis_list, sensor.axis.to_string()); if (!combos.empty() && !combos.front().empty()) e_sensor.code = *combos.front().begin(); // We should only have one key for each of our sensors return e_sensor; }; diff --git a/rpcs3/Input/keyboard_pad_handler.cpp b/rpcs3/Input/keyboard_pad_handler.cpp index e4cb6608b3..9ce274141a 100644 --- a/rpcs3/Input/keyboard_pad_handler.cpp +++ b/rpcs3/Input/keyboard_pad_handler.cpp @@ -906,7 +906,7 @@ std::string keyboard_pad_handler::GetKeyName(u32 keyCode) return QKeySequence(keyCode).toString(QKeySequence::NativeText).toStdString(); } -std::vector> keyboard_pad_handler::GetKeyCombos(const std::string& cfg_string) +std::vector> keyboard_pad_handler::GetKeyCombos(std::string_view cfg_string) { std::vector> res; diff --git a/rpcs3/Input/keyboard_pad_handler.h b/rpcs3/Input/keyboard_pad_handler.h index aadc51b29b..ec1a4416a3 100644 --- a/rpcs3/Input/keyboard_pad_handler.h +++ b/rpcs3/Input/keyboard_pad_handler.h @@ -94,7 +94,7 @@ public: static QStringList GetKeyNames(const QKeyEvent* keyEvent); static std::string GetKeyName(const QKeyEvent* keyEvent, bool with_modifiers); static std::string GetKeyName(u32 keyCode); - static std::vector> GetKeyCombos(const std::string& cfg_string); + static std::vector> GetKeyCombos(std::string_view cfg_string); static u32 GetKeyCode(const QString& keyName); static int native_scan_code_from_string(const std::string& key); diff --git a/rpcs3/Input/mm_joystick_handler.cpp b/rpcs3/Input/mm_joystick_handler.cpp index 710488a4f4..207e7461d3 100644 --- a/rpcs3/Input/mm_joystick_handler.cpp +++ b/rpcs3/Input/mm_joystick_handler.cpp @@ -226,7 +226,7 @@ pad_preview_values mm_joystick_handler::get_preview_values(const std::unordered_ if (buttons.size() != 10) return preview_values; - const auto get_key_value = [this, &data](const std::string& str) -> u16 + const auto get_key_value = [this, &data](std::string_view str) -> u16 { u16 value{}; diff --git a/rpcs3/Input/raw_mouse_handler.h b/rpcs3/Input/raw_mouse_handler.h index 9ab7945f20..d2846b2c20 100644 --- a/rpcs3/Input/raw_mouse_handler.h +++ b/rpcs3/Input/raw_mouse_handler.h @@ -74,12 +74,12 @@ public: const std::map& get_mice() const { return m_raw_mice; }; - void set_mouse_press_callback(std::function cb) + void set_mouse_press_callback(std::function cb) { m_mouse_press_callback = std::move(cb); } - void mouse_press_callback(const std::string& device_name, s32 button_code, bool pressed) + void mouse_press_callback(std::string_view device_name, s32 button_code, bool pressed) { if (m_mouse_press_callback) { @@ -87,12 +87,12 @@ public: } } - void set_key_press_callback(std::function cb) + void set_key_press_callback(std::function cb) { m_key_press_callback = std::move(cb); } - void key_press_callback(const std::string& device_name, s32 scan_code, bool pressed) + void key_press_callback(std::string_view device_name, s32 scan_code, bool pressed) { if (m_key_press_callback) { @@ -120,8 +120,8 @@ private: bool m_is_for_gui = false; std::map m_raw_mice; - std::function m_mouse_press_callback; - std::function m_key_press_callback; + std::function m_mouse_press_callback; + std::function m_key_press_callback; std::unique_ptr>> m_thread; }; diff --git a/rpcs3/Loader/ISO.cpp b/rpcs3/Loader/ISO.cpp index 96d196d670..4bd2050638 100644 --- a/rpcs3/Loader/ISO.cpp +++ b/rpcs3/Loader/ISO.cpp @@ -213,25 +213,32 @@ iso_type_status iso_file_decryption::get_key(const std::string& key_path, aes_co return iso_type_status::ERROR_OPENING_KEY; } - char key_str[32]; + std::array key_str {}; std::array key {}; - const u64 key_len = key_file.read(key_str, sizeof(key_str)); + const u64 key_len = key_file.read(key_str.data(), key_str.size()); - if (key_len == sizeof(key_str) || key_len == sizeof(key)) + if (key_len == key_str.size() || key_len == key.size()) { // If the key read from the key file is 16 bytes long instead of 32, consider the file as // binary (".key") and so not needing any further conversion from hex string to bytes - if (key_len == sizeof(key)) + if (key_len == key.size()) { - std::memcpy(key.data(), key_str, sizeof(key)); + std::memcpy(key.data(), key_str.data(), key.size()); } else { - hex_to_bytes(key.data(), std::string_view(key_str, key_len), static_cast(key_len)); + std::string error; + hex_to_bytes(key.data(), std::string_view(key_str.data(), key_str.size()), key_str.size(), &error); + + if (!error.empty()) + { + iso_log.error("get_key(%s): %s", key_path, error); + return iso_type_status::ERROR_PROCESSING_KEY; + } } - aes_context aes_dec; + aes_context aes_dec {}; // If "aes_ctx" not requested if (!aes_ctx) diff --git a/rpcs3/Loader/PSF.cpp b/rpcs3/Loader/PSF.cpp index 28c08d7b8c..2efa896109 100644 --- a/rpcs3/Loader/PSF.cpp +++ b/rpcs3/Loader/PSF.cpp @@ -396,24 +396,13 @@ namespace psf return found->second.as_integer(); } - bool check_registry(const registry& psf, std::function validate, std::source_location src_loc) + bool check_registry(const registry& psf, std::source_location src_loc) { bool psf_ok = true; for (const auto& [key, value] : psf) { - bool entry_ok = value.is_valid(); - - if (validate) - { - // Validate against a custom condition as well (forward error) - if (!validate(entry_ok, key, value)) - { - entry_ok = false; - } - } - - if (!entry_ok) + if (!value.is_valid()) { if (value.type() == format::string) { @@ -424,10 +413,7 @@ namespace psf // TODO: Better logging of other types psf_log.error("Entry %s is invalid.%s", key, value.as_string(), src_loc); } - } - if (!entry_ok) - { // Do not break, run over all entries in order to report all errors psf_ok = false; } diff --git a/rpcs3/Loader/PSF.h b/rpcs3/Loader/PSF.h index 9cc8b16c19..d4d93e713e 100644 --- a/rpcs3/Loader/PSF.h +++ b/rpcs3/Loader/PSF.h @@ -103,7 +103,7 @@ namespace psf // Get integer value or default value u32 get_integer(const registry& psf, std::string_view key, u32 def = 0); - bool check_registry(const registry& psf, std::function validate = {}, std::source_location src_loc = std::source_location::current()); + bool check_registry(const registry& psf, std::source_location src_loc = std::source_location::current()); // Assign new entry inline void assign(registry& psf, std::string_view key, entry&& _entry) diff --git a/rpcs3/Loader/TAR.cpp b/rpcs3/Loader/TAR.cpp index 7cd25c6f1f..5891f0e51e 100644 --- a/rpcs3/Loader/TAR.cpp +++ b/rpcs3/Loader/TAR.cpp @@ -565,11 +565,12 @@ void tar_object::save_directory(const std::string& target_path, utils::serial& a } }; - auto save_header = [&](const fs::stat_t& stat, const std::string& name) + auto save_header = [&](const fs::stat_t& stat, std::string_view name) { static_assert(sizeof(TARHeader) == 512); + ensure(src_dir_pos <= name.size()); - std::string_view saved_path{name.size() == src_dir_pos ? name.c_str() : &::at32(name, src_dir_pos), name.size() - src_dir_pos}; + std::string_view saved_path = name.size() == src_dir_pos ? std::string_view() : name.substr(src_dir_pos); if (is_null) { diff --git a/rpcs3/Loader/TROPUSR.cpp b/rpcs3/Loader/TROPUSR.cpp index dc47a643a1..b8549e7e1a 100644 --- a/rpcs3/Loader/TROPUSR.cpp +++ b/rpcs3/Loader/TROPUSR.cpp @@ -207,8 +207,8 @@ bool TROPUSRLoader::Generate(std::string_view filepath, std::string_view configp TROPUSREntry4 entry4 = { 4, u32{sizeof(TROPUSREntry4)} - 0x10, ::size32(m_table4), 0, trophy_id, trophy_grade, trophy_pid }; TROPUSREntry6 entry6 = { 6, u32{sizeof(TROPUSREntry6)} - 0x10, ::size32(m_table6), 0, trophy_id }; - m_table4.push_back(entry4); - m_table6.push_back(entry6); + m_table4.push_back(std::move(entry4)); + m_table6.push_back(std::move(entry6)); } } @@ -219,8 +219,8 @@ bool TROPUSRLoader::Generate(std::string_view filepath, std::string_view configp offset += m_table6.size() * sizeof(TROPUSREntry6); m_tableHeaders.clear(); - m_tableHeaders.push_back(table4header); - m_tableHeaders.push_back(table6header); + m_tableHeaders.push_back(std::move(table4header)); + m_tableHeaders.push_back(std::move(table6header)); std::memset(&m_header, 0, sizeof(m_header)); m_header.magic = TROPUSR_MAGIC; diff --git a/rpcs3/Loader/content_validation.cpp b/rpcs3/Loader/content_validation.cpp new file mode 100644 index 0000000000..773ee276be --- /dev/null +++ b/rpcs3/Loader/content_validation.cpp @@ -0,0 +1,187 @@ +#include "stdafx.h" + +#include "content_validation.h" +#include "ISO.h" + +#include "Emu/system_utils.hpp" +#include "Utilities/File.h" +#include "Utilities/rXml.h" +#include "Crypto/md5.h" +#include "Crypto/utils.h" + +LOG_CHANNEL(sys_log, "VALIDATION"); + +content_integrity_status content_validation::check_integrity(content_file_type file_type, std::string_view hash, std::string* game_name) +{ + // + // Check for Redump db + // + + std::string db_path; + + switch (file_type) + { + case content_file_type::ISO: + db_path = rpcs3::utils::get_redump_db_path(); + break; + case content_file_type::PSN_CONTENT: + db_path = rpcs3::utils::get_psn_content_db_path(); + break; + case content_file_type::PSN_DLC: + db_path = rpcs3::utils::get_psn_dlc_db_path(); + break; + case content_file_type::PSN_UPDATE: + db_path = rpcs3::utils::get_psn_update_db_path(); + break; + } + + fs::file db_file(db_path); + + // If no db file exists + if (!db_file) + { + // An empty hash is used to simply test the presence (without any logging) of the Redump db + if (!hash.empty()) + { + sys_log.error("check_integrity: Failed to open file: %s", db_path); + } + + return content_integrity_status::ERROR_OPENING_DB; + } + + if (hash.empty()) + { + return content_integrity_status::NO_MATCH; + } + + rXmlDocument db; + + if (!db.Read(db_file.to_string())) + { + sys_log.error("check_integrity: Failed to process file: %s", db_path); + return content_integrity_status::ERROR_PARSING_DB; + } + + // Close the file and work with the data loaded into the "db" document + db_file.close(); + + std::shared_ptr db_base = db.GetRoot(); + + if (!db_base) + { + sys_log.error("check_integrity: Failed to get 'root' node on file: %s", db_path); + return content_integrity_status::ERROR_PARSING_DB; + } + + if (db_base = db_base->GetChild(std::string_view("datafile")); !db_base) + { + sys_log.error("check_integrity: Failed to get 'datafile' node on file: %s", db_path); + return content_integrity_status::ERROR_PARSING_DB; + } + + // + // Check for a match on Redump db + // + + for (auto node = db_base->GetChildren(); node; node = node->GetNext()) + { + if (node->GetName() == "game") + { + for (auto child = node->GetChildren(); child; child = child->GetNext()) + { + // If a match is found, fill in "game_desc" (if requested) and return FOUND_MATCH + if (child->GetName() == "rom" && hash == child->GetAttribute(std::string_view("md5"))) + { + if (game_name) + { + *game_name = node->GetAttribute(std::string_view("name")); + } + + return content_integrity_status::FOUND_MATCH; + } + } + } + } + + // No match found + return content_integrity_status::NO_MATCH; +} + +bool content_validation::init_hash(const std::string& path) +{ + std::string new_path = path; + + fs::get_optical_raw_device(path, &new_path); + + iso_file file(new_path); + + // If no file exists + if (!file) + { + sys_log.error("init_hash: Failed to open file: %s", new_path); + m_status = content_hash_status::ABORTED; + return false; + } + + m_path = new_path; + m_name = new_path.find_last_of(fs::delim) != umax ? new_path.substr(new_path.find_last_of(fs::delim) + 1) : new_path; + m_size = file.size(); + m_bytes_read = 0; + m_status = content_hash_status::INITIALIZED; + return true; +} + +content_hash_status content_validation::calculate_hash(std::string& hash) +{ + if (m_status != content_hash_status::INITIALIZED) + { + sys_log.error("calculate_hash: MD5 hash calculation already performed: %s", m_path); + m_status = content_hash_status::ABORTED; + return m_status; + } + + iso_file file(m_path); + + // If no file exists + if (!file) + { + sys_log.error("calculate_hash: Failed to open file: %s", m_path); + m_status = content_hash_status::ABORTED; + return m_status; + } + + constexpr u64 block_size = 4096; + std::array buf; + u64 bytes_read; + mbedtls_md5_context md5_ctx; + unsigned char md5_hash[16]; + + mbedtls_md5_starts_ret(&md5_ctx); + + do + { + bytes_read = file.read(buf.data(), block_size); + mbedtls_md5_update_ret(&md5_ctx, buf.data(), bytes_read); + + m_bytes_read += bytes_read; + } while (bytes_read == block_size && m_status != content_hash_status::ABORTED); + + if (m_status == content_hash_status::ABORTED) + { + sys_log.warning("calculate_hash: MD5 hash calculation aborted by user: %s", m_path); + return m_status; + } + + if (mbedtls_md5_finish_ret(&md5_ctx, md5_hash) != 0) + { + sys_log.error("calculate_hash: Failed to calculate MD5 hash on file: %s", m_path); + m_status = content_hash_status::ABORTED; + return m_status; + } + + // Convert the MD5 hash to hex string + bytes_to_hex(hash, md5_hash, 16); + + m_status = content_hash_status::COMPLETED; + return m_status; +} diff --git a/rpcs3/Loader/content_validation.h b/rpcs3/Loader/content_validation.h new file mode 100644 index 0000000000..dc5d4ac5d9 --- /dev/null +++ b/rpcs3/Loader/content_validation.h @@ -0,0 +1,57 @@ +#pragma once + +#include "util/types.hpp" + +// Enum identifying the content file type +enum class content_file_type +{ + ISO, + PSN_CONTENT, + PSN_DLC, + PSN_UPDATE +}; + +// Enum returned by calculating hash +enum class content_hash_status +{ + INITIALIZED, + COMPLETED, + ABORTED +}; + +// Enum returned by checking integrity +enum class content_integrity_status +{ + NO_MATCH, + FOUND_MATCH, + ERROR_OPENING_DB, + ERROR_PARSING_DB +}; + +// Content validation class +class content_validation +{ +private: + std::string m_path; + std::string m_name; + u64 m_size = 0; + u64 m_bytes_read = 0; + u16 m_count = 0; // Set only by set_count() + content_hash_status m_status = content_hash_status::INITIALIZED; + +public: + static content_integrity_status check_integrity(content_file_type file_type, std::string_view hash, std::string* game_name = nullptr); + + const std::string& get_path() const { return m_path; } + const std::string& get_name() const { return m_name; } + u64 get_size() const { return m_size; } + u64 get_bytes_read() const { return m_bytes_read; } + u16 get_count() const { return m_count; } + content_hash_status get_status() const { return m_status; } + + void set_count(u16 count) { m_count = count; } + void abort_hash() { m_status = content_hash_status::ABORTED; } + + bool init_hash(const std::string& path); + content_hash_status calculate_hash(std::string& hash); +}; diff --git a/rpcs3/Loader/iso_cache.cpp b/rpcs3/Loader/iso_cache.cpp index a37927cf39..8360ebde7f 100644 --- a/rpcs3/Loader/iso_cache.cpp +++ b/rpcs3/Loader/iso_cache.cpp @@ -40,7 +40,7 @@ namespace namespace iso_cache { - bool load(const std::string& iso_path, const std::string& cache_key, iso_metadata_cache_entry& out_entry) + bool load(const std::string& iso_path, std::string_view cache_key, iso_metadata_cache_entry& out_entry) { fs::stat_t iso_stat{}; if (!fs::get_stat(iso_path, iso_stat) || iso_stat.is_directory) @@ -95,7 +95,7 @@ namespace iso_cache return true; } - void save(const std::string& iso_path, const std::string& cache_key, const iso_metadata_cache_entry& entry) + void save(std::string_view iso_path, std::string_view cache_key, const iso_metadata_cache_entry& entry) { const std::string stem = get_cache_stem(cache_key); const std::string dir = get_cache_dir(); diff --git a/rpcs3/Loader/iso_cache.h b/rpcs3/Loader/iso_cache.h index 88b080b1df..04f4c3a402 100644 --- a/rpcs3/Loader/iso_cache.h +++ b/rpcs3/Loader/iso_cache.h @@ -23,10 +23,10 @@ struct iso_metadata_cache_entry namespace iso_cache { // Returns false if no valid cache entry exists or mtime has changed. - bool load(const std::string& iso_path, const std::string& cache_key, iso_metadata_cache_entry& out_entry); + bool load(const std::string& iso_path, std::string_view cache_key, iso_metadata_cache_entry& out_entry); // Persists a populated cache entry to disk. - void save(const std::string& iso_path, const std::string& cache_key, const iso_metadata_cache_entry& entry); + void save(std::string_view iso_path, std::string_view cache_key, const iso_metadata_cache_entry& entry); bool load_index(const std::string& iso_path, std::vector& out_subdirs); void save_index(const std::string& iso_path, const std::vector& subdirs); diff --git a/rpcs3/Loader/iso_validation.cpp b/rpcs3/Loader/iso_validation.cpp deleted file mode 100644 index 31a82beaa7..0000000000 --- a/rpcs3/Loader/iso_validation.cpp +++ /dev/null @@ -1,162 +0,0 @@ -#include "stdafx.h" - -#include "iso_validation.h" -#include "ISO.h" - -#include "Emu/system_utils.hpp" -#include "Utilities/File.h" -#include "Utilities/rXml.h" -#include "Crypto/md5.h" -#include "Crypto/utils.h" - -LOG_CHANNEL(iso_log, "ISO"); - -iso_integrity_status iso_file_validation::check_integrity(const std::string& hash, std::string* game_name) -{ - // - // Check for Redump db - // - - const std::string db_path = rpcs3::utils::get_redump_db_path(); - fs::file db_file(db_path); - - // If no db file exists - if (!db_file) - { - // An empty hash is used to simply test the presence (without any logging) of the Redump db - if (!hash.empty()) - { - iso_log.error("check_integrity: Failed to open file: %s", db_path); - } - - return iso_integrity_status::ERROR_OPENING_DB; - } - - if (hash.empty()) - { - return iso_integrity_status::NO_MATCH; - } - - rXmlDocument db; - - if (!db.Read(db_file.to_string())) - { - iso_log.error("check_integrity: Failed to process file: %s", db_path); - return iso_integrity_status::ERROR_PARSING_DB; - } - - std::shared_ptr db_base = db.GetRoot(); - - if (!db_base) - { - iso_log.error("check_integrity: Failed to get 'root' node on file: %s", db_path); - return iso_integrity_status::ERROR_PARSING_DB; - } - - if (db_base = db_base->GetChild(std::string_view("datafile")); !db_base) - { - iso_log.error("check_integrity: Failed to get 'datafile' node on file: %s", db_path); - return iso_integrity_status::ERROR_PARSING_DB; - } - - // - // Check for a match on Redump db - // - - for (auto node = db_base->GetChildren(); node; node = node->GetNext()) - { - if (node->GetName() == "game") - { - if (const auto child = node->GetChild(std::string_view("rom"))) - { - // If a match is found, fill in "game_desc" (if requested) and return FOUND_MATCH - if (hash == child->GetAttribute(std::string_view("md5"))) - { - if (game_name) - { - *game_name = node->GetAttribute(std::string_view("name")); - } - - return iso_integrity_status::FOUND_MATCH; - } - } - } - } - - // No match found - return iso_integrity_status::NO_MATCH; -} - -bool iso_file_validation::init_hash(const std::string& path) -{ - fs::file iso_file(path); - - // If no ISO file exists - if (!iso_file) - { - iso_log.error("init_hash: Failed to open file: %s", path); - m_status = iso_hash_status::ABORTED; - return false; - } - - m_path = path; - m_size = iso_file.size(); - m_bytes_read = 0; - m_status = iso_hash_status::INITIALIZED; - return true; -} - -iso_hash_status iso_file_validation::calculate_hash(std::string& hash) -{ - if (m_status != iso_hash_status::INITIALIZED) - { - iso_log.error("calculate_hash: MD5 hash calculation already performed: %s", m_path); - m_status = iso_hash_status::ABORTED; - return m_status; - } - - fs::file iso_file(m_path); - - // If no ISO file exists - if (!iso_file) - { - iso_log.error("calculate_hash: Failed to open file: %s", m_path); - m_status = iso_hash_status::ABORTED; - return m_status; - } - - constexpr u64 block_size = ISO_SECTOR_SIZE * 2; - std::array buf; - u64 bytes_read; - mbedtls_md5_context md5_ctx; - unsigned char md5_hash[16]; - - mbedtls_md5_starts_ret(&md5_ctx); - - do - { - bytes_read = iso_file.read(buf.data(), block_size); - mbedtls_md5_update_ret(&md5_ctx, buf.data(), bytes_read); - - m_bytes_read += bytes_read; - } while (bytes_read == block_size && m_status != iso_hash_status::ABORTED); - - if (m_status == iso_hash_status::ABORTED) - { - iso_log.warning("calculate_hash: MD5 hash calculation aborted by user: %s", m_path); - return m_status; - } - - if (mbedtls_md5_finish_ret(&md5_ctx, md5_hash) != 0) - { - iso_log.error("calculate_hash: Failed to calculate MD5 hash on file: %s", m_path); - m_status = iso_hash_status::ABORTED; - return m_status; - } - - // Convert the MD5 hash to hex string - bytes_to_hex(hash, md5_hash, 16); - - m_status = iso_hash_status::COMPLETED; - return m_status; -} diff --git a/rpcs3/Loader/iso_validation.h b/rpcs3/Loader/iso_validation.h deleted file mode 100644 index 56ac659ce8..0000000000 --- a/rpcs3/Loader/iso_validation.h +++ /dev/null @@ -1,42 +0,0 @@ -#pragma once - -#include "util/types.hpp" - -// Enum returned by calculating hash -enum class iso_hash_status -{ - INITIALIZED, - COMPLETED, - ABORTED -}; - -// Enum returned by checking integrity -enum class iso_integrity_status -{ - NO_MATCH, - FOUND_MATCH, - ERROR_OPENING_DB, - ERROR_PARSING_DB -}; - -// ISO file validation class -class iso_file_validation -{ -private: - std::string m_path; - u64 m_size = 0; - u64 m_bytes_read = 0; - iso_hash_status m_status = iso_hash_status::INITIALIZED; - -public: - static iso_integrity_status check_integrity(const std::string& hash, std::string* game_name = nullptr); - - const std::string& get_path() const { return m_path; } - u64 get_size() const { return m_size; } - u64 get_bytes_read() const { return m_bytes_read; } - iso_hash_status get_status() const { return m_status; } - void abort_hash() { m_status = iso_hash_status::ABORTED; } - - bool init_hash(const std::string& path); - iso_hash_status calculate_hash(std::string& hash); -}; diff --git a/rpcs3/emucore.vcxproj b/rpcs3/emucore.vcxproj index cd4ee9a32e..770028936f 100644 --- a/rpcs3/emucore.vcxproj +++ b/rpcs3/emucore.vcxproj @@ -207,7 +207,7 @@ - + NotUsing @@ -782,8 +782,8 @@ + - diff --git a/rpcs3/emucore.vcxproj.filters b/rpcs3/emucore.vcxproj.filters index f944c85349..7280166c33 100644 --- a/rpcs3/emucore.vcxproj.filters +++ b/rpcs3/emucore.vcxproj.filters @@ -1414,6 +1414,9 @@ Loader + + Loader + Emu\GPU\RSX\Overlays @@ -1432,7 +1435,7 @@ Emu\GPU\RSX\Overlays - + Loader @@ -1462,9 +1465,6 @@ Emu\Cell\Modules - - Loader - Emu\Io @@ -2887,6 +2887,9 @@ Loader + + Loader + Emu\GPU\RSX\Overlays @@ -2911,7 +2914,7 @@ Emu\GPU\RSX\Common - + Loader @@ -2944,9 +2947,6 @@ Emu\Cell\Modules - - Loader - diff --git a/rpcs3/main_application.cpp b/rpcs3/main_application.cpp index 8d5c44c5e3..b998427e2d 100644 --- a/rpcs3/main_application.cpp +++ b/rpcs3/main_application.cpp @@ -394,7 +394,7 @@ EmuCallbacks main_application::CreateCallbacks() { font_dir += '/'; } - font_dirs.push_back(font_dir); + font_dirs.push_back(std::move(font_dir)); } return font_dirs; }; diff --git a/rpcs3/rpcs3.cpp b/rpcs3/rpcs3.cpp index 3bf6259a4a..2fa4e8cb6a 100644 --- a/rpcs3/rpcs3.cpp +++ b/rpcs3/rpcs3.cpp @@ -692,6 +692,10 @@ int run_rpcs3(int argc, char** argv) logs::stored_message ver{sys_log.always()}; ver.text = fmt::format("RPCS3 v%s", rpcs3::get_verbose_version()); + // Write Architecture + logs::stored_message arch{sys_log.always()}; + arch.text = fmt::format("Architecture: %s", utils::get_architecture()); + // Write System information logs::stored_message sys{sys_log.always()}; sys.text = utils::get_system_info(); @@ -708,7 +712,7 @@ int run_rpcs3(int argc, char** argv) logs::stored_message time{sys_log.always()}; time.text = fmt::format("Current Time: %s", std::chrono::system_clock::now()); - logs::set_init({std::move(ver), std::move(sys), std::move(os), std::move(qt), std::move(time)}); + logs::set_init({std::move(ver), std::move(arch), std::move(sys), std::move(os), std::move(qt), std::move(time)}); } #ifdef ARCH_ARM64 diff --git a/rpcs3/rpcs3.plist.in b/rpcs3/rpcs3.plist.in index 93a4f2c186..a56b8dca0e 100644 --- a/rpcs3/rpcs3.plist.in +++ b/rpcs3/rpcs3.plist.in @@ -31,7 +31,7 @@ LSApplicationCategoryType public.app-category.games LSMinimumSystemVersion - 14.4 + 15.0 NSCameraUsageDescription The camera will be used for PlayStation Eye emulation NSMicrophoneUsageDescription diff --git a/rpcs3/rpcs3.png b/rpcs3/rpcs3.png index fd8c4a82b7..aa294825c2 100644 Binary files a/rpcs3/rpcs3.png and b/rpcs3/rpcs3.png differ diff --git a/rpcs3/rpcs3.vcxproj b/rpcs3/rpcs3.vcxproj index 0aeb999181..c4338d2acb 100644 --- a/rpcs3/rpcs3.vcxproj +++ b/rpcs3/rpcs3.vcxproj @@ -344,7 +344,7 @@ true - + true @@ -656,7 +656,7 @@ true - + true @@ -929,7 +929,7 @@ - + @@ -1924,13 +1924,13 @@ .\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp "$(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" - + $(QTDIR)\bin\moc.exe;%(FullPath) - Moc%27ing iso_integrity.h... + Moc%27ing content_integrity.h... .\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp "$(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" $(QTDIR)\bin\moc.exe;%(FullPath) - Moc%27ing iso_integrity.h... + Moc%27ing content_integrity.h... .\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp "$(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" diff --git a/rpcs3/rpcs3.vcxproj.filters b/rpcs3/rpcs3.vcxproj.filters index 5358f6e372..b8af6799f1 100644 --- a/rpcs3/rpcs3.vcxproj.filters +++ b/rpcs3/rpcs3.vcxproj.filters @@ -1302,13 +1302,13 @@ Gui\settings - + Generated Files\Debug - + Generated Files\Release - + Gui\game list @@ -2083,7 +2083,7 @@ - + Gui\game list diff --git a/rpcs3/rpcs3qt/CMakeLists.txt b/rpcs3/rpcs3qt/CMakeLists.txt index 160aa4fb05..5dd092d074 100644 --- a/rpcs3/rpcs3qt/CMakeLists.txt +++ b/rpcs3/rpcs3qt/CMakeLists.txt @@ -13,6 +13,7 @@ add_library(rpcs3_ui STATIC config_adapter.cpp config_checker.cpp config_database.cpp + content_integrity.cpp curl_handle.cpp custom_dialog.cpp custom_table_widget_item.cpp @@ -51,7 +52,6 @@ add_library(rpcs3_ui STATIC input_dialog.cpp instruction_editor_dialog.cpp ipc_settings_dialog.cpp - iso_integrity.cpp kamen_rider_dialog.cpp kernel_explorer.cpp localized.cpp diff --git a/rpcs3/rpcs3qt/cheat_manager.cpp b/rpcs3/rpcs3qt/cheat_manager.cpp index fa8d0b4d58..90b38f2d49 100644 --- a/rpcs3/rpcs3qt/cheat_manager.cpp +++ b/rpcs3/rpcs3qt/cheat_manager.cpp @@ -195,7 +195,7 @@ bool cheat_engine::erase(const std::string& game, const u32 offset) return true; } -bool cheat_engine::resolve_script(u32& final_offset, const u32 offset, const std::string& red_script) +bool cheat_engine::resolve_script(u32& final_offset, const u32 offset, std::string_view red_script) { enum operand { @@ -290,8 +290,11 @@ bool cheat_engine::resolve_script(u32& final_offset, const u32 offset, const std cur_op = operand_sub; index++; break; - case ' ': index++; break; - default: log_cheat.fatal("invalid character in redirection script"); return false; + case ' ': index++; + break; + default: + log_cheat.fatal("invalid character in redirection script"); + return false; } } } diff --git a/rpcs3/rpcs3qt/cheat_manager.h b/rpcs3/rpcs3qt/cheat_manager.h index c09f47428f..a02aa8c696 100644 --- a/rpcs3/rpcs3qt/cheat_manager.h +++ b/rpcs3/rpcs3qt/cheat_manager.h @@ -30,7 +30,7 @@ public: void save() const; // Static functions to find/get/set values in ps3 memory - static bool resolve_script(u32& final_offset, const u32 offset, const std::string& red_script); + static bool resolve_script(u32& final_offset, const u32 offset, std::string_view red_script); template static std::vector search(const T value, const std::vector& to_filter); diff --git a/rpcs3/rpcs3qt/config_adapter.cpp b/rpcs3/rpcs3qt/config_adapter.cpp index ddc0cc839c..ec03eca95e 100644 --- a/rpcs3/rpcs3qt/config_adapter.cpp +++ b/rpcs3/rpcs3qt/config_adapter.cpp @@ -6,7 +6,7 @@ LOG_CHANNEL(cfg_log, "CFG"); // Helper methods to interact with YAML and the config settings. namespace cfg_adapter { - static cfg::_base& get_cfg(const cfg::_base& root, const std::string& name) + static cfg::_base& get_cfg(const cfg::_base& root, std::string_view name) { if (root.get_type() == cfg::type::node) { diff --git a/rpcs3/rpcs3qt/content_integrity.cpp b/rpcs3/rpcs3qt/content_integrity.cpp new file mode 100644 index 0000000000..97aabc88c9 --- /dev/null +++ b/rpcs3/rpcs3qt/content_integrity.cpp @@ -0,0 +1,134 @@ +#include "content_integrity.h" +#include "gui_settings.h" + +#include "Emu/system_utils.hpp" + +#include +#include + +LOG_CHANNEL(sys_log, "INTEGRITY"); + +content_integrity::content_integrity(QWidget* parent, content_file_type file_type) + : QObject(parent), m_file_type(file_type) +{ + switch (m_file_type) + { + case content_file_type::ISO: + m_file_path = QString::fromStdString(rpcs3::utils::get_redump_db_path()); + m_url = rpcs3::utils::get_redump_db_download_url(); + m_data_prefix = "redump"; + break; + case content_file_type::PSN_CONTENT: + m_file_path = QString::fromStdString(rpcs3::utils::get_psn_content_db_path()); + m_url = rpcs3::utils::get_psn_content_db_download_url(); + m_data_prefix = "nointro"; + break; + case content_file_type::PSN_DLC: + m_file_path = QString::fromStdString(rpcs3::utils::get_psn_dlc_db_path()); + m_url = rpcs3::utils::get_psn_dlc_db_download_url(); + m_data_prefix = "nointro"; + break; + case content_file_type::PSN_UPDATE: + m_file_path = QString::fromStdString(rpcs3::utils::get_psn_update_db_path()); + m_url = rpcs3::utils::get_psn_update_db_download_url(); + m_data_prefix = "nointro"; + break; + } + + m_downloader = new downloader(parent); + + connect(m_downloader, &downloader::signal_download_finished, this, &content_integrity::handle_download_finished); + connect(m_downloader, &downloader::signal_download_canceled, this, &content_integrity::handle_download_canceled); + connect(m_downloader, &downloader::signal_download_error, this, &content_integrity::handle_download_error); +} + +void content_integrity::download() +{ + sys_log.notice("Starting database download from: %s", m_url); + + m_downloader->start(m_url, true, true, true, tr("Downloading database")); +} + +void content_integrity::handle_download_finished(const QByteArray& content) +{ + sys_log.notice("Database download finished"); + + // Write database to file + if (QByteArray data = read_json(content, true); !data.isEmpty()) + { + QFile file(m_file_path); + + if (file.exists()) + { + sys_log.notice("Database file found: %s", m_file_path); + } + + if (!file.open(QIODevice::WriteOnly)) + { + sys_log.error("Failed to write database to file: %s", m_file_path); + return; + } + + file.write(data); + file.close(); + + sys_log.success("Database written to file: %s", m_file_path); + } +} + +void content_integrity::handle_download_canceled() +{ + sys_log.notice("Database download canceled"); +} + +void content_integrity::handle_download_error(const QString& error) +{ + sys_log.error("Database download failed: %s", error); +} + +QByteArray content_integrity::read_json(const QByteArray& data, bool after_download) +{ + QJsonParseError error{}; + const QJsonDocument json_document = QJsonDocument::fromJson(data, &error); + + if (!json_document.isObject()) + { + sys_log.error("Integrity database error - Invalid JSON: '%s'", error.errorString()); + return {}; + } + + const QJsonObject json_data = json_document.object(); + const int return_code = json_data["return_code"].toInt(-255); + + if (return_code < 0) + { + if (after_download) + { + std::string error_message; + + switch (return_code) + { + case -1: error_message = "Server Error - Internal Error"; break; + case -2: error_message = "Server Error - Maintenance Mode"; break; + case -255: error_message = "Server Error - Return code not found"; break; + default: error_message = "Server Error - Unknown Error"; break; + } + + sys_log.error("%s: return code %d", error_message, return_code); + } + else + { + sys_log.error("Integrity database error - Invalid: return code %d", return_code); + } + + return {}; + } + + if (!json_data[m_data_prefix.c_str()].isString()) + { + sys_log.error("Integrity database error - Unusable string"); + return {}; + } + + return QByteArray().fromStdString(json_data[m_data_prefix.c_str()].toString().toStdString()); +}; diff --git a/rpcs3/rpcs3qt/iso_integrity.h b/rpcs3/rpcs3qt/content_integrity.h similarity index 57% rename from rpcs3/rpcs3qt/iso_integrity.h rename to rpcs3/rpcs3qt/content_integrity.h index a4225770de..d7d51b9a55 100644 --- a/rpcs3/rpcs3qt/iso_integrity.h +++ b/rpcs3/rpcs3qt/content_integrity.h @@ -2,13 +2,15 @@ #include "downloader.h" -class iso_integrity : public QObject +#include "Loader/content_validation.h" + +class content_integrity : public QObject { Q_OBJECT public: - // Handles download for the ISO integrity database - iso_integrity(QWidget* parent); + // Handles download for the content integrity database + content_integrity(QWidget* parent, content_file_type file_type); // Downloads and writes the database to file void download(); @@ -21,6 +23,9 @@ private Q_SLOTS: private: QByteArray read_json(const QByteArray& data, bool after_download); - QString m_filepath; + content_file_type m_file_type; + QString m_file_path; + std::string m_url; + std::string m_data_prefix; downloader* m_downloader = nullptr; }; diff --git a/rpcs3/rpcs3qt/curl_handle.cpp b/rpcs3/rpcs3qt/curl_handle.cpp index cb91311d26..ff0c3e8018 100644 --- a/rpcs3/rpcs3qt/curl_handle.cpp +++ b/rpcs3/rpcs3qt/curl_handle.cpp @@ -1,6 +1,7 @@ #include "stdafx.h" #include "curl_handle.h" #include "util/logs.hpp" +#include "util/sysinfo.hpp" #ifdef _WIN32 #include "Utilities/StrUtil.h" @@ -13,10 +14,14 @@ namespace rpcs3::curl curl_handle::curl_handle() { - reset_error_buffer(); - m_curl = curl_easy_init(); + if (!m_curl) + { + network_log.error("curl_easy_init failed"); + return; + } + CURLcode err = curl_easy_setopt(m_curl, CURLOPT_ERRORBUFFER, m_error_buffer.data()); if (err != CURLE_OK) network_log.error("curl_easy_setopt(CURLOPT_ERRORBUFFER): %s", curl_easy_strerror(err)); @@ -30,11 +35,18 @@ curl_handle::curl_handle() err = curl_easy_setopt(m_curl, CURLOPT_SSL_OPTIONS, CURLSSLOPT_NATIVE_CA); if (err != CURLE_OK) network_log.error("curl_easy_setopt(CURLOPT_SSL_OPTIONS): %s", curl_easy_strerror(err)); #endif + + const std::string user_agent = utils::get_user_agent(); + err = curl_easy_setopt(m_curl, CURLOPT_USERAGENT, user_agent.c_str()); + if (err != CURLE_OK) network_log.error("curl_easy_setopt(CURLOPT_USERAGENT): %s", curl_easy_strerror(err)); } curl_handle::~curl_handle() { - curl_easy_cleanup(m_curl); + if (m_curl) + { + curl_easy_cleanup(m_curl); + } } CURL* curl_handle::get_curl() const @@ -44,7 +56,6 @@ CURL* curl_handle::get_curl() const void curl_handle::reset_error_buffer() { - ensure(m_error_buffer.size() == CURL_ERROR_SIZE); m_error_buffer[0] = 0; } @@ -52,7 +63,6 @@ std::string curl_handle::get_verbose_error(CURLcode code) const { if (m_uses_error_buffer) { - ensure(m_error_buffer.size() == CURL_ERROR_SIZE); if (m_error_buffer[0]) { return fmt::format("Curl error (%d): %s\nDetails: %s", static_cast(code), curl_easy_strerror(code), m_error_buffer.data()); diff --git a/rpcs3/rpcs3qt/curl_handle.h b/rpcs3/rpcs3qt/curl_handle.h index 090c198215..3415e9ee57 100644 --- a/rpcs3/rpcs3qt/curl_handle.h +++ b/rpcs3/rpcs3qt/curl_handle.h @@ -27,7 +27,7 @@ public: private: CURL* m_curl = nullptr; bool m_uses_error_buffer = false; - std::array m_error_buffer; + std::array m_error_buffer {}; }; } diff --git a/rpcs3/rpcs3qt/downloader.cpp b/rpcs3/rpcs3qt/downloader.cpp index c8e2b5896b..5a3c6b9819 100644 --- a/rpcs3/rpcs3qt/downloader.cpp +++ b/rpcs3/rpcs3qt/downloader.cpp @@ -29,7 +29,7 @@ int curl_xferinfo_cb(void* userdata, curl_off_t /*dltotal*/, curl_off_t /*dlnow* downloader::downloader(QWidget* parent) : QObject(parent) , m_parent(parent) - , m_curl(new rpcs3::curl::curl_handle()) + , m_curl(std::make_unique()) { } diff --git a/rpcs3/rpcs3qt/emu_settings_type.cpp b/rpcs3/rpcs3qt/emu_settings_type.cpp index 53260c2c39..e8de70f334 100644 --- a/rpcs3/rpcs3qt/emu_settings_type.cpp +++ b/rpcs3/rpcs3qt/emu_settings_type.cpp @@ -58,6 +58,7 @@ const std::map settings_location = { emu_settings_type::SPUProfiler, get_cfg_location(local_cfg.core.spu_prof) }, { emu_settings_type::DisableSpinOptimization, get_cfg_location(local_cfg.core.spu_getllar_spin_optimization_disabled) }, { emu_settings_type::EnabledSPUEventsBusyLoop, get_cfg_location(local_cfg.core.spu_reservation_busy_waiting_enabled) }, + { emu_settings_type::PPUReservationPriorityOverSPUs, get_cfg_location(local_cfg.core.ppu_reservation_priority_over_spu) }, // Graphics Tab { emu_settings_type::Renderer, get_cfg_location(local_cfg.video.renderer) }, diff --git a/rpcs3/rpcs3qt/emu_settings_type.h b/rpcs3/rpcs3qt/emu_settings_type.h index 81f7f5e725..0f3cd313a9 100644 --- a/rpcs3/rpcs3qt/emu_settings_type.h +++ b/rpcs3/rpcs3qt/emu_settings_type.h @@ -52,6 +52,7 @@ enum class emu_settings_type SPUProfiler, DisableSpinOptimization, EnabledSPUEventsBusyLoop, + PPUReservationPriorityOverSPUs, // Graphics Renderer, diff --git a/rpcs3/rpcs3qt/game_list.cpp b/rpcs3/rpcs3qt/game_list.cpp index 70089d650e..21f00f2256 100644 --- a/rpcs3/rpcs3qt/game_list.cpp +++ b/rpcs3/rpcs3qt/game_list.cpp @@ -12,6 +12,36 @@ game_list::game_list() : QTableWidget(), game_list_base() { Q_EMIT IconReady(game, item); }; + + // Activate video/music preview on selection change (keyboard/pad navigation) + connect(this, &QTableWidget::currentCellChanged, this, [this](int row, int /*column*/, int prev_row, int /*prev_column*/) + { + if (row == prev_row) + return; + + if (m_last_hover_item) + { + m_last_hover_item->set_active(false); + } + + movie_item* new_item = static_cast(item(row, 0)); + + if (new_item && isActiveWindow()) + { + new_item->set_active(true); + } + + m_last_hover_item = new_item; + }); +} + +void game_list::stop_movie() +{ + if (m_last_hover_item) + { + m_last_hover_item->set_active(false); + m_last_hover_item = nullptr; + } } void game_list::sync_header_actions(std::map& actions, std::function get_visibility) @@ -136,7 +166,7 @@ void game_list::mouseMoveEvent(QMouseEvent* event) { m_last_hover_item->set_active(false); } - if (new_item) + if (new_item && isActiveWindow()) { new_item->set_active(true); } diff --git a/rpcs3/rpcs3qt/game_list.h b/rpcs3/rpcs3qt/game_list.h index 6d5a3f5a3e..34e7cba059 100644 --- a/rpcs3/rpcs3qt/game_list.h +++ b/rpcs3/rpcs3qt/game_list.h @@ -23,6 +23,8 @@ class game_list : public QTableWidget, public game_list_base public: game_list(); + void stop_movie(); + void sync_header_actions(std::map& actions, std::function get_visibility); void create_header_actions(std::map& actions, std::function get_visibility, std::function set_visibility); diff --git a/rpcs3/rpcs3qt/game_list_actions.cpp b/rpcs3/rpcs3qt/game_list_actions.cpp index 5e0b77a802..97cb37e7ce 100644 --- a/rpcs3/rpcs3qt/game_list_actions.cpp +++ b/rpcs3/rpcs3qt/game_list_actions.cpp @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include @@ -367,67 +368,167 @@ void game_list_actions::ShowGameInfoDialog(const std::vector& games) QMessageBox::information(m_game_list_frame, tr("Game Info"), GetContentInfo(games).info); } -void game_list_actions::ShowGameIntegrityDialog(const game_info& game) +void game_list_actions::ShowGameIntegrityDialog(content_file_type file_type, const std::string& game_path) { if (m_game_integrity_future.isRunning()) // Still running the last request return; - // Initialize the validator (set also file size etc.) - m_iso_validator->init_hash(game->info.path); + QStringList path_list; + + switch (file_type) + { + case content_file_type::ISO: + path_list.push_back(QString::fromStdString(game_path)); + break; + default: // Auto-detect the file type + const QString path_last_pkg = m_gui_settings->GetValue(gui::fd_install_pkg).toString(); + + path_list = QFileDialog::getOpenFileNames(nullptr, tr("Select package or rap file to check"), + path_last_pkg, tr("All relevant (*.pkg *.PKG *.rap *.RAP *.edat *.EDAT);;Package files (*.pkg *.PKG);;Rap files (*.rap *.RAP);;Edat files (*.edat *.EDAT);;All files (*.*)")); + + if (path_list.isEmpty()) + { + return; + } + + break; + } + + // Tell the progress bar thread how many hash will be checked + m_game_validator->set_count(path_list.size()); // Game integrity check can take a while (in particular on non ssd/m.2 disks) // so run it on a concurrent thread avoiding to block the entire GUI - m_game_integrity_future = QtConcurrent::run([this]() + m_game_integrity_future = QtConcurrent::run([this, type = file_type, path_list]() { thread_base::set_name("Game Integrity"); - QString text; - std::string hash, game_name; - bool info_dialog = false; + content_file_type file_type = type; + QString text_result; + std::string db_id, hash, game_name; + bool info_dialog = true; - if (m_iso_validator->calculate_hash(hash) != iso_hash_status::COMPLETED) + for (int i = 0; i < path_list.size(); i++) { - text = "Hash calculation failed!\n\nIntegrity check aborted"; - } - else - { - text = "Integrity check completed!\n\n"; + bool use_fallback_db = false; // Set to "true" only for ".rap" and ".edat" - switch (m_iso_validator->check_integrity(hash, &game_name)) + if (file_type == content_file_type::ISO) { - case iso_integrity_status::NO_MATCH: - text += tr("Game check NOT PASSED\n\nNo match found on DB or game corrupted:\n - Hash: %0") - .arg(QString::fromStdString(hash)); - break; - case iso_integrity_status::FOUND_MATCH: - text += tr("Game check PASSED\n\nMatch found on DB:\n - Game: %0\n - Hash: %1") - .arg(QString::fromStdString(game_name)) - .arg(QString::fromStdString(hash)); - - info_dialog = true; - break; - default: - text += tr("Error parsing DB"); - break; + db_id = "REDUMP"; } - } - - Emu.CallFromMainThread([this, text, info_dialog]() - { - if (info_dialog) + else if (path_list[i].endsWith(".rap", Qt::CaseInsensitive) || path_list[i].endsWith(".edat", Qt::CaseInsensitive)) { - sys_log.success("%s", text.toStdString()); - QMessageBox::information(m_game_list_frame, tr("Game Integrity"), text); + // NOTE: This is the default type for any ".rap" and ".edat" due to it's not possible to detect the type by file parsing. + // If no match for ".rap" or ".edat" will be found on default "PSN Content" DB, we will try on "PSN DLC" DB + file_type = content_file_type::PSN_CONTENT; + db_id = "PSN CONTENT"; + use_fallback_db = true; } else { - sys_log.error("%s", text.toStdString()); - QMessageBox::critical(m_game_list_frame, tr("Game Integrity"), text); + const compat::package_info info = game_compatibility::GetPkgInfo(path_list[i], m_game_list_frame->GetGameCompatibility()); + + switch (info.type) + { + case compat::package_type::update: + file_type = content_file_type::PSN_UPDATE; + db_id = "PSN UPDATE"; + break; + case compat::package_type::dlc: + file_type = content_file_type::PSN_DLC; + db_id = "PSN DLC"; + break; + case compat::package_type::other: + file_type = content_file_type::PSN_CONTENT; + db_id = "PSN CONTENT"; + break; + } + } + + // Initialize the validator (set also file size etc.) + m_game_validator->init_hash(path_list[i].toStdString()); + + if (m_game_validator->calculate_hash(hash) == content_hash_status::COMPLETED) + { + content_integrity_status integrity_status = m_game_validator->check_integrity(file_type, hash, &game_name); + + // If no match for ".rap" or ".edat" is found on default "PSN Content" DB, try on "PSN DLC" DB + if (integrity_status == content_integrity_status::NO_MATCH && use_fallback_db) + { + db_id += " -> PSN DLC"; + integrity_status = m_game_validator->check_integrity(content_file_type::PSN_DLC, hash, &game_name); + } + + switch (integrity_status) + { + case content_integrity_status::NO_MATCH: + text_result += tr("Game check NOT PASSED\n\nNo match found on '%0' DB or game corrupted:\n - File: %1\n - Hash: %2") + .arg(QString::fromStdString(db_id)) + .arg(QString::fromStdString(m_game_validator->get_name())) + .arg(QString::fromStdString(hash)); + + info_dialog = false; + break; + case content_integrity_status::FOUND_MATCH: + text_result += tr("Game check PASSED\n\nMatch found on '%0' DB:\n - File: %1\n - Hash: %2\n - Game: %3") + .arg(QString::fromStdString(db_id)) + .arg(QString::fromStdString(m_game_validator->get_name())) + .arg(QString::fromStdString(hash)) + .arg(QString::fromStdString(game_name)); + break; + default: + text_result += tr("Error parsing '%0' DB or DB not existing:\n - File: %1\n - Hash: %2") + .arg(QString::fromStdString(db_id)) + .arg(QString::fromStdString(m_game_validator->get_name())) + .arg(QString::fromStdString(hash)); + + info_dialog = false; + break; + } + + if (i < path_list.size() - 1) // If it's not the last processed entry, add empty lines as separator + { + text_result += "\n\n\n"; + } + } + + if (m_game_validator->get_status() == content_hash_status::ABORTED) + { + break; + } + } + + QString text_dialog; + + if (m_game_validator->get_status() == content_hash_status::ABORTED) + { + text_dialog = tr("Hash calculation failed!\n\nIntegrity check aborted"); + info_dialog = false; + } + else + { + text_dialog = tr("Integrity check completed!\n\n%0").arg(text_result); + } + + // Tell the progress bar thread to terminate + m_game_validator->set_count(0); + + Emu.CallFromMainThread([this, text_dialog, info_dialog]() + { + if (info_dialog) + { + sys_log.success("%s", text_dialog.toStdString()); + QMessageBox::information(m_game_list_frame, tr("Game Integrity"), text_dialog); + } + else + { + sys_log.error("%s", text_dialog.toStdString()); + QMessageBox::critical(m_game_list_frame, tr("Game Integrity"), text_dialog); } }, nullptr, false); }); - progress_dialog* pdlg = new progress_dialog(tr("ISO File Hash Calculation"), tr("Calculating hash"), tr("Cancel"), + progress_dialog* pdlg = new progress_dialog(tr("File Hash Calculation"), "", tr("Cancel"), 0, 100, false, m_game_list_frame); pdlg->setAutoClose(false); @@ -436,21 +537,22 @@ void game_list_actions::ShowGameIntegrityDialog(const game_info& game) connect(pdlg, &progress_dialog::canceled, m_game_list_frame, [this]() { - m_iso_validator->abort_hash(); + m_game_validator->abort_hash(); }); QTimer* update_timer = new QTimer(m_game_list_frame); connect(update_timer, &QTimer::timeout, m_game_list_frame, [this, pdlg, update_timer]() { - if (m_iso_validator->get_status() == iso_hash_status::INITIALIZED) + if (m_game_validator->get_count()) { // Set progress in range 0-100 - const int progress = m_iso_validator->get_size() ? - (static_cast(m_iso_validator->get_bytes_read()) / m_iso_validator->get_size()) * 100 : + const int progress = m_game_validator->get_size() ? + (static_cast(m_game_validator->get_bytes_read()) / m_game_validator->get_size()) * 100 : 0; pdlg->setValue(progress); + pdlg->setLabelText(tr("Calculating hash: %0").arg(m_game_validator->get_name())); } else { @@ -503,7 +605,7 @@ void game_list_actions::ShowDiskUsageDialog() }); } -bool game_list_actions::IsGameRunning(const std::string& serial) +bool game_list_actions::IsGameRunning(std::string_view serial) { return !Emu.IsStopped(true) && (serial == Emu.GetTitleID() || (serial == "vsh.self" && Emu.IsVsh())); } diff --git a/rpcs3/rpcs3qt/game_list_actions.h b/rpcs3/rpcs3qt/game_list_actions.h index 0b9fa8f8ac..ff0b4a0be7 100644 --- a/rpcs3/rpcs3qt/game_list_actions.h +++ b/rpcs3/rpcs3qt/game_list_actions.h @@ -2,7 +2,7 @@ #include "gui_game_info.h" #include "shortcut_utils.h" -#include "Loader/iso_validation.h" +#include "Loader/content_validation.h" #include #include @@ -49,13 +49,13 @@ public: std::set removed_disc_list; // Filled in by RemoveContentList() }; - static bool IsGameRunning(const std::string& serial); + static bool IsGameRunning(std::string_view serial); void CreateShortcuts(const std::vector& games, const std::set& locations); void ShowRemoveGameDialog(const std::vector& games); void ShowGameInfoDialog(const std::vector& games); - void ShowGameIntegrityDialog(const game_info& game); + void ShowGameIntegrityDialog(content_file_type file_type, const std::string& game_path); void ShowDiskUsageDialog(); // NOTES: @@ -100,7 +100,7 @@ private: std::shared_ptr m_gui_settings; QFuture m_disk_usage_future; QFuture m_game_integrity_future; - std::shared_ptr m_iso_validator = std::make_shared(); + std::shared_ptr m_game_validator = std::make_shared(); // NOTE: // m_content_info is used by: diff --git a/rpcs3/rpcs3qt/game_list_context_menu.cpp b/rpcs3/rpcs3qt/game_list_context_menu.cpp index 402286e2f1..5d461d11d2 100644 --- a/rpcs3/rpcs3qt/game_list_context_menu.cpp +++ b/rpcs3/rpcs3qt/game_list_context_menu.cpp @@ -17,7 +17,7 @@ #include "Utilities/File.h" #include "Emu/system_utils.hpp" #include "Loader/ISO.h" -#include "Loader/iso_validation.h" +#include "Loader/content_validation.h" #include "QApplication" #include "QClipboard" @@ -339,7 +339,7 @@ void game_list_context_menu::show_single_selection_context_menu(const game_info& manage_game_menu->addSeparator(); // Remove game - QAction* remove_game = manage_game_menu->addAction(tr("&Remove %1").arg(gameinfo->localized_category)); + QAction* remove_game = manage_game_menu->addAction(tr("&Remove %0").arg(gameinfo->localized_category)); remove_game->setEnabled(!is_current_running_game); // Custom Images menu @@ -602,45 +602,36 @@ void game_list_context_menu::show_single_selection_context_menu(const game_info& addSeparator(); - // Check integrity + // Check disc game integrity if (QString::fromStdString(current_game.category) == cat::cat_disc_game) { + const bool raw_archive = is_iso_file(current_game.path); const iso_type_status iso_type = iso_file_decryption::check_type(current_game.path); // If it's an ISO file (e.g. even a decrypted ISO), always provide the entry on the context menu but disable // it if the ISO does not support integrity check (e.g. non Redump ISO) or no integrity DB is found. // That is to highlight a Redump ISO from a non Redump ISO - if (iso_type != iso_type_status::NOT_ISO) + if (raw_archive || iso_type != iso_type_status::NOT_ISO) { - const iso_integrity_status iso_integrity = iso_file_validation::check_integrity(""); - - QAction* check_integrity = addAction(tr("&Check ISO Integrity")); + QAction* check_iso_integrity = addAction(tr("&Check ISO Integrity")); // If it's a Redump ISO and the integrity DB exists - if (iso_type == iso_type_status::REDUMP_ISO && iso_integrity != iso_integrity_status::ERROR_OPENING_DB) + if ((raw_archive || iso_type == iso_type_status::REDUMP_ISO) && + content_validation::check_integrity(content_file_type::ISO, "") != content_integrity_status::ERROR_OPENING_DB) { - connect(check_integrity, &QAction::triggered, this, [this, gameinfo]() + connect(check_iso_integrity, &QAction::triggered, this, [this, gameinfo]() { - m_game_list_actions->ShowGameIntegrityDialog(gameinfo); + m_game_list_actions->ShowGameIntegrityDialog(content_file_type::ISO, gameinfo->info.path); }); } else { - check_integrity->setEnabled(false); + check_iso_integrity->setEnabled(false); } - - QAction* download_integrity = addAction(tr("&Download Integrity Database")); - connect(download_integrity, &QAction::triggered, m_game_list_frame, [this] - { - ensure(m_game_list_frame->GetIsoIntegrity())->download(); - }); } } QAction* check_compat = addAction(tr("&Check Game Compatibility")); - QAction* download_compat = addAction(tr("&Download Compatibility Database")); - QAction* download_config_db = addAction(tr("&Download Config Database")); - addSeparator(); // Disk usage @@ -734,14 +725,6 @@ void game_list_context_menu::show_single_selection_context_menu(const game_info& const QString link = "https://rpcs3.net/compatibility?g=" + serial; QDesktopServices::openUrl(QUrl(link)); }); - connect(download_compat, &QAction::triggered, m_game_list_frame, [this] - { - m_game_list_frame->GetGameCompatibility()->RequestCompatibility(true); - }); - connect(download_config_db, &QAction::triggered, m_game_list_frame, [this] - { - m_game_list_frame->GetConfigDatabase()->request_config_database(true); - }); connect(rename_title, &QAction::triggered, m_game_list_frame, [this, name, serial = QString::fromStdString(serial), global_pos] { const QString custom_title = m_persistent_settings->GetValue(gui::persistent::titles, serial, "").toString(); @@ -1001,20 +984,6 @@ void game_list_context_menu::show_multi_selection_context_menu(const std::vector addSeparator(); - QAction* download_compat = addAction(tr("&Download Compatibility Database")); - connect(download_compat, &QAction::triggered, m_game_list_frame, [this] - { - m_game_list_frame->GetGameCompatibility()->RequestCompatibility(true); - }); - - QAction* download_config_db = addAction(tr("&Download Config Database")); - connect(download_config_db, &QAction::triggered, m_game_list_frame, [this] - { - m_game_list_frame->GetConfigDatabase()->request_config_database(true); - }); - - addSeparator(); - // Disk usage QAction* disk_usage = addAction(tr("&Disk Usage")); connect(disk_usage, &QAction::triggered, this, [this]() diff --git a/rpcs3/rpcs3qt/game_list_frame.cpp b/rpcs3/rpcs3qt/game_list_frame.cpp index 5750e4220d..9dfd6a940e 100644 --- a/rpcs3/rpcs3qt/game_list_frame.cpp +++ b/rpcs3/rpcs3qt/game_list_frame.cpp @@ -75,7 +75,10 @@ game_list_frame::game_list_frame(std::shared_ptr gui_settings, std m_game_list->installEventFilter(this); m_game_list->verticalScrollBar()->installEventFilter(this); - m_iso_integrity = new iso_integrity(this); + m_iso_integrity = new content_integrity(this, content_file_type::ISO); + m_psn_content_integrity = new content_integrity(this, content_file_type::PSN_CONTENT); + m_psn_dlc_integrity = new content_integrity(this, content_file_type::PSN_DLC); + m_psn_update_integrity = new content_integrity(this, content_file_type::PSN_UPDATE); m_game_compat = new game_compatibility(this); m_config_db = new config_database(this); @@ -1234,6 +1237,12 @@ void game_list_frame::ShowCustomConfigIcon(const game_info& game) RepaintIcons(); } +void game_list_frame::stop_movie() +{ + if (m_game_list) m_game_list->stop_movie(); + if (m_game_grid) m_game_grid->stop_movie(); +} + void game_list_frame::ResizeIcons(int slider_pos) { m_icon_size_index = slider_pos; diff --git a/rpcs3/rpcs3qt/game_list_frame.h b/rpcs3/rpcs3qt/game_list_frame.h index 55569aa950..775258d02d 100644 --- a/rpcs3/rpcs3qt/game_list_frame.h +++ b/rpcs3/rpcs3qt/game_list_frame.h @@ -3,7 +3,7 @@ #include "game_list.h" #include "game_list_actions.h" #include "custom_dock_widget.h" -#include "iso_integrity.h" +#include "content_integrity.h" #include "Utilities/lockless.h" #include "Utilities/mutex.h" @@ -56,7 +56,10 @@ public: void SetShowHidden(bool show); - iso_integrity* GetIsoIntegrity() const { return m_iso_integrity; } + content_integrity* GetIsoIntegrity() const { return ensure(m_iso_integrity); } + content_integrity* GetPsnContentIntegrity() const { return ensure(m_psn_content_integrity); } + content_integrity* GetPsnDlcIntegrity() const { return ensure(m_psn_dlc_integrity); } + content_integrity* GetPsnUpdateIntegrity() const { return ensure(m_psn_update_integrity); } game_compatibility* GetGameCompatibility() const { return ensure(m_game_compat); } config_database* GetConfigDatabase() const { return ensure(m_config_db); } const std::vector& GetGameInfo() const { return m_game_data; } @@ -72,6 +75,8 @@ public: void ShowCustomConfigIcon(const game_info& game); + void stop_movie(); + // Enqueue slot for refreshed signal // Allowing for an individual container for each distinct use case (currently disabled and contains only one such entry) template @@ -156,7 +161,10 @@ private: // Game List game_list_table* m_game_list = nullptr; - iso_integrity* m_iso_integrity = nullptr; + content_integrity* m_iso_integrity = nullptr; + content_integrity* m_psn_content_integrity = nullptr; + content_integrity* m_psn_dlc_integrity = nullptr; + content_integrity* m_psn_update_integrity = nullptr; game_compatibility* m_game_compat = nullptr; config_database* m_config_db = nullptr; progress_dialog* m_progress_dialog = nullptr; diff --git a/rpcs3/rpcs3qt/game_list_grid.cpp b/rpcs3/rpcs3qt/game_list_grid.cpp index 04dd3a4e98..72cf5816a2 100644 --- a/rpcs3/rpcs3qt/game_list_grid.cpp +++ b/rpcs3/rpcs3qt/game_list_grid.cpp @@ -35,6 +35,17 @@ game_list_grid::game_list_grid() }); } +void game_list_grid::stop_movie() +{ + for (flow_widget_item* flow_item : items()) + { + if (game_list_grid_item* item = static_cast(flow_item)) + { + item->set_active(false); + } + } +} + void game_list_grid::clear_list() { clear(); @@ -145,6 +156,9 @@ void game_list_grid::populate( QApplication::processEvents(); select_items(selected_items); + + // Prevent playing unwanted movie + stop_movie(); } void game_list_grid::repaint_icons(std::vector& game_data, const QColor& icon_color, const QSize& icon_size, qreal device_pixel_ratio) diff --git a/rpcs3/rpcs3qt/game_list_grid.h b/rpcs3/rpcs3qt/game_list_grid.h index 0aebbe0547..e888e0ac08 100644 --- a/rpcs3/rpcs3qt/game_list_grid.h +++ b/rpcs3/rpcs3qt/game_list_grid.h @@ -12,6 +12,8 @@ class game_list_grid : public flow_widget, public game_list_base public: explicit game_list_grid(); + void stop_movie(); + void clear_list() override; void populate( diff --git a/rpcs3/rpcs3qt/game_list_grid_item.cpp b/rpcs3/rpcs3qt/game_list_grid_item.cpp index a3952a264a..1411bca8ba 100644 --- a/rpcs3/rpcs3qt/game_list_grid_item.cpp +++ b/rpcs3/rpcs3qt/game_list_grid_item.cpp @@ -74,7 +74,8 @@ bool game_list_grid_item::event(QEvent* event) switch (event->type()) { case QEvent::HoverEnter: - set_active(true); + if (isActiveWindow()) + set_active(true); break; case QEvent::HoverLeave: case QEvent::FocusOut: diff --git a/rpcs3/rpcs3qt/game_list_table.cpp b/rpcs3/rpcs3qt/game_list_table.cpp index ca157c5183..fc393e4667 100644 --- a/rpcs3/rpcs3qt/game_list_table.cpp +++ b/rpcs3/rpcs3qt/game_list_table.cpp @@ -424,6 +424,9 @@ void game_list_table::populate( } } Q_EMIT itemSelectionChanged(); + + // Prevent playing unwanted movie + stop_movie(); } void game_list_table::repaint_icons(std::vector& game_data, const QColor& icon_color, const QSize& icon_size, qreal device_pixel_ratio) diff --git a/rpcs3/rpcs3qt/gs_frame.cpp b/rpcs3/rpcs3qt/gs_frame.cpp index bcc148d9a6..5d65a2c93d 100644 --- a/rpcs3/rpcs3qt/gs_frame.cpp +++ b/rpcs3/rpcs3qt/gs_frame.cpp @@ -321,6 +321,14 @@ void gs_frame::handle_shortcut(gui::shortcuts::shortcut shortcut_key, const QKey } break; } + case gui::shortcuts::shortcut::gw_stop: + { + if (!Emu.IsStopped()) + { + Emu.GracefulShutdown(true, true); + } + break; + } case gui::shortcuts::shortcut::gw_restart: case gui::shortcuts::shortcut::gw_savestate_1: case gui::shortcuts::shortcut::gw_savestate_2: @@ -639,7 +647,7 @@ void gs_frame::hide_on_close() { // Make sure not to save the hidden state, which is useless to us. const Visibility current_visibility = visibility(); - m_gui_settings->SetValue(gui::gs_visibility, current_visibility == Visibility::Hidden ? m_visibility : current_visibility, false); + m_gui_settings->SetValue(gui::gs_visibility, gui::visibility_to_string(current_visibility == Visibility::Hidden ? m_visibility : current_visibility), false); m_gui_settings->SetValue(gui::gs_geometry, geometry(), true); if (!g_progr_text) @@ -710,10 +718,10 @@ void gs_frame::show() { setVisibility(FullScreen); } - else if (const QVariant var = m_gui_settings->GetValue(gui::gs_visibility); var.canConvert()) + else if (const QVariant var = m_gui_settings->GetValue(gui::gs_visibility); var.canConvert()) { // Restore saved visibility from last time. Make sure not to hide the window, or the user can't access it anymore. - if (const Visibility visibility = var.value(); visibility != Visibility::Hidden) + if (const Visibility visibility = gui::string_to_visibility(var.value()); visibility != Visibility::Hidden) { setVisibility(visibility); } @@ -822,17 +830,7 @@ void gs_frame::flip(draw_context_t /*context*/, bool /*skip_frame*/) if (fps_t.GetElapsedTimeInSec() >= 0.5) { - std::string new_title = Emu.GetFormattedTitle(m_frames / fps_t.GetElapsedTimeInSec()); - - if (new_title != m_window_title) - { - m_window_title = new_title; - - Emu.CallFromMainThread([this, title = std::move(new_title)]() - { - setTitle(QString::fromStdString(title)); - }); - } + update_title(m_frames / fps_t.GetElapsedTimeInSec()); m_frames = 0; fps_t.Start(); @@ -1122,6 +1120,21 @@ void gs_frame::take_screenshot(std::vector&& data, u32 sshot_width, u32 ssho .detach(); } +void gs_frame::update_title(double fps) +{ + std::string new_title = Emu.GetFormattedTitle(fps); + + if (new_title != m_window_title) + { + m_window_title = new_title; + + Emu.CallFromMainThread([this, title = std::move(new_title)]() + { + setTitle(QString::fromStdString(title)); + }); + } +} + void gs_frame::mouseDoubleClickEvent(QMouseEvent* ev) { if (m_disable_mouse) diff --git a/rpcs3/rpcs3qt/gs_frame.h b/rpcs3/rpcs3qt/gs_frame.h index 5e63a2d9c1..4c55ef4525 100644 --- a/rpcs3/rpcs3qt/gs_frame.h +++ b/rpcs3/rpcs3qt/gs_frame.h @@ -82,6 +82,8 @@ public: void present_frame(std::vector&& data, u32 pitch, u32 width, u32 height, bool is_bgra) const override; void take_screenshot(std::vector&& data, u32 sshot_width, u32 sshot_height, bool is_bgra) override; + void update_title(double fps = 0.0) override; + protected: video_renderer m_renderer; diff --git a/rpcs3/rpcs3qt/gui_settings.cpp b/rpcs3/rpcs3qt/gui_settings.cpp index 4d72b74a70..653ad5acad 100644 --- a/rpcs3/rpcs3qt/gui_settings.cpp +++ b/rpcs3/rpcs3qt/gui_settings.cpp @@ -102,6 +102,45 @@ namespace gui fmt::throw_exception("get_trophy_game_list_column_name: Invalid column"); } + + QString window_states_to_string(Qt::WindowStates states) + { + if (states & Qt::WindowFullScreen) return "FullScreen"; + if (states & Qt::WindowMaximized) return "Maximized"; + if (states & Qt::WindowMinimized) return "Minimized"; + return "Windowed"; + } + + Qt::WindowState string_to_window_states(const QString& state) + { + const QString lower = state.toLower(); // Allow for better user experience + if (lower == "fullscreen") return Qt::WindowFullScreen; + if (lower == "maximized") return Qt::WindowMaximized; + if (lower == "minimized") return Qt::WindowMinimized; + return Qt::WindowNoState; + } + + QString visibility_to_string(QWindow::Visibility visibility) + { + switch (visibility) + { + case QWindow::Visibility::Windowed: return "Windowed"; + case QWindow::Visibility::Minimized: return "Minimized"; + case QWindow::Visibility::Maximized: return "Maximized"; + case QWindow::Visibility::FullScreen: return "FullScreen"; + default: return "AutomaticVisibility"; + } + } + + QWindow::Visibility string_to_visibility(const QString& visibility) + { + const QString lower = visibility.toLower(); // Allow for better user experience + if (lower == "windowed") return QWindow::Visibility::Windowed; + if (lower == "minimized") return QWindow::Visibility::Minimized; + if (lower == "maximized") return QWindow::Visibility::Maximized; + if (lower == "fullscreen") return QWindow::Visibility::FullScreen; + return QWindow::Visibility::AutomaticVisibility; + } } gui_settings::gui_settings(QObject* parent) : settings(parent) diff --git a/rpcs3/rpcs3qt/gui_settings.h b/rpcs3/rpcs3qt/gui_settings.h index 41be2732ab..e54700be67 100644 --- a/rpcs3/rpcs3qt/gui_settings.h +++ b/rpcs3/rpcs3qt/gui_settings.h @@ -109,6 +109,12 @@ namespace gui return q_string_pair(path, title.simplified()); // simplified() forces single line text } + QString window_states_to_string(Qt::WindowStates states); + Qt::WindowState string_to_window_states(const QString& state); + + QString visibility_to_string(QWindow::Visibility visibility); + QWindow::Visibility string_to_visibility(const QString& visibility); + const QString Settings = "CurrentSettings"; const QString DefaultStylesheet = "default"; const QString NoStylesheet = "none"; @@ -180,6 +186,7 @@ namespace gui const gui_save mw_geometry = gui_save(main_window, "geometry", QByteArray()); const gui_save mw_windowState = gui_save(main_window, "windowState", QByteArray()); const gui_save mw_mwState = gui_save(main_window, "mwState", QByteArray()); + const gui_save mw_visibility = gui_save(main_window, "visibility", window_states_to_string(Qt::WindowNoState)); const gui_save cat_hdd_game = gui_save(game_list, "categoryVisibleHDDGame", true); const gui_save cat_disc_game = gui_save(game_list, "categoryVisibleDiscGame", true); @@ -272,7 +279,7 @@ namespace gui const gui_save gs_hideMouseIdle = gui_save(gs_frame, "hideMouseOnIdle", false); const gui_save gs_hideMouseIdleTime = gui_save(gs_frame, "hideMouseOnIdleTime", 2000); const gui_save gs_geometry = gui_save(gs_frame, "geometry", QRect()); - const gui_save gs_visibility = gui_save(gs_frame, "visibility", QWindow::Visibility::AutomaticVisibility); + const gui_save gs_visibility = gui_save(gs_frame, "visibility", visibility_to_string(QWindow::Visibility::AutomaticVisibility)); const gui_save ss_icon_color = gui_save(trophy, "icon_color", gl_icon_color); const gui_save ss_game_icon_size = gui_save(trophy, "game_icon_size", 25); diff --git a/rpcs3/rpcs3qt/infinity_dialog.cpp b/rpcs3/rpcs3qt/infinity_dialog.cpp index 7aa00e5819..0e37bb594f 100644 --- a/rpcs3/rpcs3qt/infinity_dialog.cpp +++ b/rpcs3/rpcs3qt/infinity_dialog.cpp @@ -626,7 +626,7 @@ bool figure_creator_dialog::create_blank_figure(u32 character, u8 series) std::array uid_data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x89, 0x44, 0x00, 0xC2}; for (u8 i = 0; i < 7; i++) { - u8 random = rand() % 255; + const u8 random = rand() % 255; sha1_calc.push_back(random); uid_data[i] = random; } @@ -659,7 +659,7 @@ bool figure_creator_dialog::create_blank_figure(u32 character, u8 series) figure_data[6] = 0x1C; } - u32 checksum = infinity_crc32(0, figure_data.data(), 12); + const u32 checksum = infinity_crc32(0, figure_data.data(), 12); for (s8 i = 0; i < 4; i++) { figure_data[12 + i] = u8((checksum >> (3 - i) * 8) & 0xFF); diff --git a/rpcs3/rpcs3qt/iso_integrity.cpp b/rpcs3/rpcs3qt/iso_integrity.cpp deleted file mode 100644 index ee0f433653..0000000000 --- a/rpcs3/rpcs3qt/iso_integrity.cpp +++ /dev/null @@ -1,113 +0,0 @@ -#include "iso_integrity.h" -#include "gui_settings.h" - -#include "Emu/system_utils.hpp" - -#include -#include - -LOG_CHANNEL(iso_log, "ISO"); - -iso_integrity::iso_integrity(QWidget* parent) - : QObject(parent) -{ - m_filepath = QString::fromStdString(rpcs3::utils::get_redump_db_path()); - m_downloader = new downloader(parent); - - connect(m_downloader, &downloader::signal_download_finished, this, &iso_integrity::handle_download_finished); - connect(m_downloader, &downloader::signal_download_canceled, this, &iso_integrity::handle_download_canceled); - connect(m_downloader, &downloader::signal_download_error, this, &iso_integrity::handle_download_error); -} - -void iso_integrity::download() -{ - const std::string url = rpcs3::utils::get_redump_db_download_url(); - - iso_log.notice("Starting database download from: %s", url); - - m_downloader->start(url, true, true, true, tr("Downloading database")); -} - -void iso_integrity::handle_download_finished(const QByteArray& content) -{ - iso_log.notice("Database download finished"); - - // Write database to file - if (QByteArray data = read_json(content, true); !data.isEmpty()) - { - QFile file(m_filepath); - - if (file.exists()) - { - iso_log.notice("Database file found: %s", m_filepath); - } - - if (!file.open(QIODevice::WriteOnly)) - { - iso_log.error("Failed to write database to file: %s", m_filepath); - return; - } - - file.write(data); - file.close(); - - iso_log.success("Database written to file: %s", m_filepath); - } -} - -void iso_integrity::handle_download_canceled() -{ - iso_log.notice("Database download canceled"); -} - -void iso_integrity::handle_download_error(const QString& error) -{ - iso_log.error("", error.toStdString().c_str()); -} - -QByteArray iso_integrity::read_json(const QByteArray& data, bool after_download) -{ - QJsonParseError error{}; - const QJsonDocument json_document = QJsonDocument::fromJson(data, &error); - - if (!json_document.isObject()) - { - iso_log.error("ISO Integrity database error - Invalid JSON: '%s'", error.errorString()); - return {}; - } - - const QJsonObject json_data = json_document.object(); - const int return_code = json_data["return_code"].toInt(-255); - - if (return_code < 0) - { - if (after_download) - { - std::string error_message; - - switch (return_code) - { - case -1: error_message = "Server Error - Internal Error"; break; - case -2: error_message = "Server Error - Maintenance Mode"; break; - case -255: error_message = "Server Error - Return code not found"; break; - default: error_message = "Server Error - Unknown Error"; break; - } - - iso_log.error("%s: return code %d", error_message, return_code); - } - else - { - iso_log.error("ISO Integrity database error - Invalid: return code %d", return_code); - } - - return {}; - } - - if (!json_data["redump"].isString()) - { - iso_log.error("ISO Integrity database error - Unusable Redump string"); - return {}; - } - - return QByteArray().fromStdString(json_data["redump"].toString().toStdString()); -}; diff --git a/rpcs3/rpcs3qt/main_window.cpp b/rpcs3/rpcs3qt/main_window.cpp index 3ea7d44508..4a1b33610e 100644 --- a/rpcs3/rpcs3qt/main_window.cpp +++ b/rpcs3/rpcs3qt/main_window.cpp @@ -1923,6 +1923,7 @@ void main_window::DecryptSPRXLibraries() void main_window::SaveWindowState() const { // Save gui settings + m_gui_settings->SetValue(gui::mw_visibility, gui::window_states_to_string(windowState()), false); m_gui_settings->SetValue(gui::mw_geometry, saveGeometry(), false); m_gui_settings->SetValue(gui::mw_windowState, saveState(), false); @@ -1999,14 +2000,7 @@ void main_window::RepaintToolBarIcons() ui->sysPauseAct->setIcon(m_icon_play); } - if (isFullScreen()) - { - ui->toolbar_fullscreen->setIcon(m_icon_fullscreen_off); - } - else - { - ui->toolbar_fullscreen->setIcon(m_icon_fullscreen_on); - } + ui->toolbar_fullscreen->setIcon(isFullScreen() ? m_icon_fullscreen_off : m_icon_fullscreen_on); const QColor& new_color = new_colors[QIcon::Normal]; ui->sizeSlider->setStyleSheet(ui->sizeSlider->styleSheet().append("QSlider::handle:horizontal{ background: rgba(%1, %2, %3, %4); }") @@ -3541,6 +3535,32 @@ void main_window::CreateConnects() #endif }); + connect(ui->downloadIntegrityDbAct, &QAction::triggered, this, [this]() + { + m_game_list_frame->GetIsoIntegrity()->download(); + m_game_list_frame->GetPsnContentIntegrity()->download(); + m_game_list_frame->GetPsnDlcIntegrity()->download(); + m_game_list_frame->GetPsnUpdateIntegrity()->download(); + }); + + connect(ui->downloadCompatDbAct, &QAction::triggered, this, [this]() + { + m_game_list_frame->GetGameCompatibility()->RequestCompatibility(true); + }); + + connect(ui->downloadConfigDbAct, &QAction::triggered, this, [this]() + { + m_game_list_frame->GetConfigDatabase()->request_config_database(true); + }); + + // Check integrity for the content categories based on .PKG, .RAP and .EDAT (e.g. HDD game, DLC, Update) + connect(ui->checkPackageIntegrityAct, &QAction::triggered, this, [this]() + { + // File type different than ISO as passed here (PSN_CONTENT) will be properly detected in + // ShowGameIntegrityDialog() based on the selected package file + m_game_list_frame->actions()->ShowGameIntegrityDialog(content_file_type::PSN_CONTENT, ""); + }); + connect(ui->welcomeAct, &QAction::triggered, this, [this]() { welcome_dialog* welcome = new welcome_dialog(m_gui_settings, true, this); @@ -3915,6 +3935,10 @@ void main_window::ConfigureGuiFromSettings() // Gamelist m_game_list_frame->LoadSettings(); + + // Restore saved visibility from last time. + setWindowState(gui::string_to_window_states(m_gui_settings->GetValue(gui::mw_visibility).toString())); + ui->toolbar_fullscreen->setIcon(isFullScreen() ? m_icon_fullscreen_off : m_icon_fullscreen_on); } void main_window::SetIconSizeActions(int idx) const @@ -4034,7 +4058,7 @@ void main_window::CreateFirmwareCache() } } -void main_window::mouseDoubleClickEvent(QMouseEvent *event) +void main_window::mouseDoubleClickEvent(QMouseEvent* event) { if (isFullScreen()) { @@ -4074,6 +4098,19 @@ void main_window::closeEvent(QCloseEvent* closeEvent) Emu.Quit(true); } +void main_window::changeEvent(QEvent* event) +{ + if (event->type() == QEvent::ActivationChange) + { + if (m_game_list_frame && !isActiveWindow()) + { + m_game_list_frame->stop_movie(); + } + } + + QMainWindow::changeEvent(event); +} + /** Add valid disc games to gamelist (games.yml) @param paths = dir paths to scan for game diff --git a/rpcs3/rpcs3qt/main_window.h b/rpcs3/rpcs3qt/main_window.h index 0dcf48200d..4f166728f7 100644 --- a/rpcs3/rpcs3qt/main_window.h +++ b/rpcs3/rpcs3qt/main_window.h @@ -125,8 +125,9 @@ private Q_SLOTS: void update_gui_pad_thread(); protected: - void closeEvent(QCloseEvent *event) override; - void mouseDoubleClickEvent(QMouseEvent *event) override; + void closeEvent(QCloseEvent* event) override; + void changeEvent(QEvent* event) override; + void mouseDoubleClickEvent(QMouseEvent* event) override; void dropEvent(QDropEvent* event) override; void dragEnterEvent(QDragEnterEvent* event) override; void dragMoveEvent(QDragMoveEvent* event) override; @@ -183,7 +184,7 @@ private: QActionGroup* m_category_visible_act_group = nullptr; // Dockable widget frames - QMainWindow *m_mw = nullptr; + QMainWindow* m_mw = nullptr; log_frame* m_log_frame = nullptr; debugger_frame* m_debugger_frame = nullptr; game_list_frame* m_game_list_frame = nullptr; diff --git a/rpcs3/rpcs3qt/main_window.ui b/rpcs3/rpcs3qt/main_window.ui index 7a7c965ce8..14f23f5a1a 100644 --- a/rpcs3/rpcs3qt/main_window.ui +++ b/rpcs3/rpcs3qt/main_window.ui @@ -315,6 +315,20 @@ + + + Databases + + + + + + + + Packages/Raps/Edats + + + @@ -328,6 +342,8 @@ + + @@ -850,6 +866,26 @@ Check for Updates + + + Download Integrity Database + + + + + Download Compatibility Database + + + + + Download Config Database + + + + + Check Integrity + + View Welcome Dialog diff --git a/rpcs3/rpcs3qt/midi_creator.cpp b/rpcs3/rpcs3qt/midi_creator.cpp index 587b1a04eb..c1baa66c1f 100644 --- a/rpcs3/rpcs3qt/midi_creator.cpp +++ b/rpcs3/rpcs3qt/midi_creator.cpp @@ -8,7 +8,7 @@ LOG_CHANNEL(cfg_log, "CFG"); -const auto midi_deleter = [](RtMidiWrapper* ptr) { if (ptr) rtmidi_in_free(ptr); }; +inline constexpr auto midi_deleter = [](RtMidiWrapper* ptr) { if (ptr) rtmidi_in_free(ptr); }; using midi_ptr = std::unique_ptr; std::mutex midi_creator::m_midi_init_mutex = {}; diff --git a/rpcs3/rpcs3qt/pad_settings_dialog.cpp b/rpcs3/rpcs3qt/pad_settings_dialog.cpp index d0334d3ae0..3cc15b7528 100644 --- a/rpcs3/rpcs3qt/pad_settings_dialog.cpp +++ b/rpcs3/rpcs3qt/pad_settings_dialog.cpp @@ -495,7 +495,7 @@ void pad_settings_dialog::InitButtons() return; } - const auto update_preview = [this](const std::string& pad_name, bool is_connected, int battery_level, int trigger_left, int trigger_right, int lx, int ly, int rx, int ry, const pad_capabilities& capabilities) + const auto update_preview = [this](std::string_view pad_name, bool is_connected, int battery_level, int trigger_left, int trigger_right, int lx, int ly, int rx, int ry, const pad_capabilities& capabilities) { SwitchPadInfo(pad_name, is_connected); @@ -801,7 +801,7 @@ void pad_settings_dialog::switch_pad_info(int index, pad_device_info info, bool } } -void pad_settings_dialog::SwitchPadInfo(const std::string& pad_name, bool is_connected) +void pad_settings_dialog::SwitchPadInfo(std::string_view pad_name, bool is_connected) { for (int i = 0; i < ui->chooseDevice->count(); i++) { diff --git a/rpcs3/rpcs3qt/pad_settings_dialog.h b/rpcs3/rpcs3qt/pad_settings_dialog.h index 4f20d2d0db..c917f82ffa 100644 --- a/rpcs3/rpcs3qt/pad_settings_dialog.h +++ b/rpcs3/rpcs3qt/pad_settings_dialog.h @@ -228,7 +228,7 @@ private: pad_device_info get_pad_info(QComboBox* combo, int index); void switch_pad_info(int index, pad_device_info info, bool is_connected); - void SwitchPadInfo(const std::string& name, bool is_connected); + void SwitchPadInfo(std::string_view name, bool is_connected); /** Enable/Disable Buttons while trying to remap an other */ void SwitchButtons(bool is_enabled); diff --git a/rpcs3/rpcs3qt/patch_manager_dialog.cpp b/rpcs3/rpcs3qt/patch_manager_dialog.cpp index de6db736e4..f86d4a9ebe 100644 --- a/rpcs3/rpcs3qt/patch_manager_dialog.cpp +++ b/rpcs3/rpcs3qt/patch_manager_dialog.cpp @@ -458,15 +458,14 @@ void patch_manager_dialog::filter_patches(const QString& term) const node_level level = static_cast(item->data(0, node_level_role).toInt()); // Hide nodes that aren't in the game list - if (m_show_owned_games_only) + if (m_show_owned_games_only && level == node_level::serial_level) { - if (level == node_level::serial_level) + const std::string serial = item->data(0, serial_role).toString().toStdString(); + if (serial != patch_key::all) { - const std::string serial = item->data(0, serial_role).toString().toStdString(); const std::string app_version = item->data(0, app_version_role).toString().toStdString(); - if (serial != patch_key::all && - (!m_owned_games.contains(serial) || (app_version != patch_key::all && !::at32(m_owned_games, serial).contains(app_version)))) + if (!m_owned_games.contains(serial) || (app_version != patch_key::all && !::at32(m_owned_games, serial).contains(app_version))) { item->setHidden(true); return 0; @@ -745,15 +744,15 @@ void patch_manager_dialog::handle_item_changed(QTreeWidgetItem* item, int /*colu if (const auto node = item->parent(); node && enabled) { const node_level level = static_cast(item->data(0, node_level_role).toInt()); - const std::string patch_group = item->data(0, patch_group_role).toString().toStdString(); + const QString patch_group = item->data(0, patch_group_role).toString(); - if (!patch_group.empty() && level == node_level::patch_level) + if (!patch_group.isEmpty() && level == node_level::patch_level) { for (int i = 0; i < node->childCount(); i++) { if (const auto other = node->child(i); other && other != item) { - const std::string other_patch_group = other->data(0, patch_group_role).toString().toStdString(); + const QString other_patch_group = other->data(0, patch_group_role).toString(); if (other_patch_group == patch_group) { @@ -844,7 +843,8 @@ void patch_manager_dialog::handle_config_value_changed(double value) for (const QString& q_key : q_config_values.keys()) { - if (const std::string s_key = q_key.toStdString(); key == q_key && patch.default_config_values.contains(s_key)) + if (key != q_key) continue; + if (const std::string s_key = q_key.toStdString(); patch.default_config_values.contains(s_key)) { config_values[s_key].value = value; } diff --git a/rpcs3/rpcs3qt/pkg_install_dialog.cpp b/rpcs3/rpcs3qt/pkg_install_dialog.cpp index c93b9dec01..df3250437f 100644 --- a/rpcs3/rpcs3qt/pkg_install_dialog.cpp +++ b/rpcs3/rpcs3qt/pkg_install_dialog.cpp @@ -297,13 +297,13 @@ std::vector pkg_install_dialog::get_paths_to_install() con const QListWidgetItem* item = m_dir_list->item(i); if (item && (m_dir_list->count() == 1 || item->checkState() == Qt::Checked)) { - compat::package_info info; + compat::package_info info {}; info.path = item->data(Roles::FullPathRole).toString(); info.title = item->data(Roles::TitleRole).toString(); info.title_id = item->data(Roles::TitleIdRole).toString(); info.changelog = item->data(Roles::ChangelogRole).toString(); info.version = item->data(Roles::VersionRole).toString(); - result.push_back(info); + result.push_back(std::move(info)); } } diff --git a/rpcs3/rpcs3qt/qt_music_handler.cpp b/rpcs3/rpcs3qt/qt_music_handler.cpp index f17e0e6d92..a7ce6b6417 100644 --- a/rpcs3/rpcs3qt/qt_music_handler.cpp +++ b/rpcs3/rpcs3qt/qt_music_handler.cpp @@ -66,15 +66,24 @@ void fmt_class_string::format(std::string& out, u64 qt_music_handler::qt_music_handler() { - music_log.notice("Constructing Qt music handler..."); + // Construct the QMediaPlayer on the GUI thread, the same thread on which it is destroyed + // (see ~qt_music_handler). cellMusic creates this handler from an emulated thread, but Qt's + // Windows multimedia backend balances its COM init/teardown per calling thread: constructing + // off the GUI thread and destroying on it left an unmatched CoUninitialize on the GUI thread, + // draining the OLE reference that Qt's startup OleInitialize set up until file drag&drop on + // the main window silently broke. Keeping both on the GUI thread keeps them balanced. + Emu.BlockingCallFromMainThread([this]() + { + music_log.notice("Constructing Qt music handler..."); - m_media_player = std::make_unique(); - m_media_player->setAudioOutput(new QAudioOutput(m_media_player.get())); + m_media_player = std::make_unique(); + m_media_player->setAudioOutput(new QAudioOutput(m_media_player.get())); - connect(m_media_player.get(), &QMediaPlayer::mediaStatusChanged, this, &qt_music_handler::handle_media_status); - connect(m_media_player.get(), &QMediaPlayer::playbackStateChanged, this, &qt_music_handler::handle_music_state); - connect(m_media_player.get(), &QMediaPlayer::errorOccurred, this, &qt_music_handler::handle_music_error); - connect(m_media_player->audioOutput(), &QAudioOutput::volumeChanged, this, &qt_music_handler::handle_volume_change); + connect(m_media_player.get(), &QMediaPlayer::mediaStatusChanged, this, &qt_music_handler::handle_media_status); + connect(m_media_player.get(), &QMediaPlayer::playbackStateChanged, this, &qt_music_handler::handle_music_state); + connect(m_media_player.get(), &QMediaPlayer::errorOccurred, this, &qt_music_handler::handle_music_error); + connect(m_media_player->audioOutput(), &QAudioOutput::volumeChanged, this, &qt_music_handler::handle_volume_change); + }); } qt_music_handler::~qt_music_handler() diff --git a/rpcs3/rpcs3qt/raw_mouse_settings_dialog.cpp b/rpcs3/rpcs3qt/raw_mouse_settings_dialog.cpp index c64391c021..174b227fc1 100644 --- a/rpcs3/rpcs3qt/raw_mouse_settings_dialog.cpp +++ b/rpcs3/rpcs3qt/raw_mouse_settings_dialog.cpp @@ -70,11 +70,11 @@ raw_mouse_settings_dialog::raw_mouse_settings_dialog(QWidget* parent) g_raw_mouse_handler = std::make_unique(); g_raw_mouse_handler->set_is_for_gui(true); g_raw_mouse_handler->Init(std::max(max_devices, ::size32(g_cfg_raw_mouse.players))); - g_raw_mouse_handler->set_mouse_press_callback([this](const std::string& device_name, s32 button_code, bool pressed) + g_raw_mouse_handler->set_mouse_press_callback([this](std::string_view device_name, s32 button_code, bool pressed) { mouse_press(device_name, button_code, pressed); }); - g_raw_mouse_handler->set_key_press_callback([this](const std::string& device_name, s32 scan_code, bool pressed) + g_raw_mouse_handler->set_key_press_callback([this](std::string_view device_name, s32 scan_code, bool pressed) { key_press(device_name, scan_code, pressed); }); @@ -350,7 +350,7 @@ void raw_mouse_settings_dialog::reset_config() } } -void raw_mouse_settings_dialog::mouse_press(const std::string& device_name, s32 button_code, bool pressed) +void raw_mouse_settings_dialog::mouse_press(std::string_view device_name, s32 button_code, bool pressed) { if (m_button_id < 0 || pressed) // Let's only react to mouse releases { @@ -378,7 +378,7 @@ void raw_mouse_settings_dialog::mouse_press(const std::string& device_name, s32 reactivate_buttons(); } -void raw_mouse_settings_dialog::key_press(const std::string& device_name, s32 scan_code, bool pressed) +void raw_mouse_settings_dialog::key_press(std::string_view device_name, s32 scan_code, bool pressed) { if (m_button_id < 0 || !pressed) // Let's only react to key presses { @@ -404,7 +404,7 @@ void raw_mouse_settings_dialog::key_press(const std::string& device_name, s32 sc reactivate_buttons(); } -void raw_mouse_settings_dialog::handle_device_change(const std::string& device_name) +void raw_mouse_settings_dialog::handle_device_change(std::string_view device_name) { if (is_device_active(device_name)) { @@ -419,7 +419,7 @@ void raw_mouse_settings_dialog::handle_device_change(const std::string& device_n } } -bool raw_mouse_settings_dialog::is_device_active(const std::string& device_name) +bool raw_mouse_settings_dialog::is_device_active(std::string_view device_name) { if (!g_raw_mouse_handler || device_name.empty()) { diff --git a/rpcs3/rpcs3qt/raw_mouse_settings_dialog.h b/rpcs3/rpcs3qt/raw_mouse_settings_dialog.h index 16aced5a77..a1c214eba7 100644 --- a/rpcs3/rpcs3qt/raw_mouse_settings_dialog.h +++ b/rpcs3/rpcs3qt/raw_mouse_settings_dialog.h @@ -30,10 +30,10 @@ private: void on_enumeration(); void reset_config(); void on_button_click(int id); - void mouse_press(const std::string& device_name, s32 button_code, bool pressed); - void key_press(const std::string& device_name, s32 scan_code, bool pressed); - void handle_device_change(const std::string& device_name); - bool is_device_active(const std::string& device_name); + void mouse_press(std::string_view device_name, s32 button_code, bool pressed); + void key_press(std::string_view device_name, s32 scan_code, bool pressed); + void handle_device_change(std::string_view device_name); + bool is_device_active(std::string_view device_name); std::string get_current_device_name(int player); void reactivate_buttons(); diff --git a/rpcs3/rpcs3qt/rpcn_settings_dialog.cpp b/rpcs3/rpcs3qt/rpcn_settings_dialog.cpp index cab71be3bc..f927291178 100644 --- a/rpcs3/rpcs3qt/rpcn_settings_dialog.cpp +++ b/rpcs3/rpcs3qt/rpcn_settings_dialog.cpp @@ -1353,7 +1353,7 @@ rpcn_friends_dialog::~rpcn_friends_dialog() } } -bool rpcn_friends_dialog::add_friend_with_error_dialog(const std::string& friend_username) +bool rpcn_friends_dialog::add_friend_with_error_dialog(std::string_view friend_username) { QString err_msg; const auto opt_error = m_rpcn->add_friend(friend_username); diff --git a/rpcs3/rpcs3qt/rpcn_settings_dialog.h b/rpcs3/rpcs3qt/rpcn_settings_dialog.h index c28828e410..a4fd8ec5e0 100644 --- a/rpcs3/rpcs3qt/rpcn_settings_dialog.h +++ b/rpcs3/rpcs3qt/rpcn_settings_dialog.h @@ -121,7 +121,7 @@ public: private: void add_update_list(QListWidget* list, const QString& name, const QIcon& icon, const QVariant& data); void remove_list(QListWidget* list, const QString& name); - bool add_friend_with_error_dialog(const std::string& friend_username); + bool add_friend_with_error_dialog(std::string_view friend_username); private Q_SLOTS: void add_update_friend(const QString& name, bool status); diff --git a/rpcs3/rpcs3qt/settings_dialog.cpp b/rpcs3/rpcs3qt/settings_dialog.cpp index c84cdc53c6..9bdae9511c 100644 --- a/rpcs3/rpcs3qt/settings_dialog.cpp +++ b/rpcs3/rpcs3qt/settings_dialog.cpp @@ -260,6 +260,9 @@ settings_dialog::settings_dialog(std::shared_ptr gui_settings, std m_emu_settings->EnhanceCheckBox(ui->spuLoopDetection, emu_settings_type::SPULoopDetection); SubscribeTooltip(ui->spuLoopDetection, tooltips.settings.spu_loop_detection); + m_emu_settings->EnhanceCheckBox(ui->ppuReservationPrority, emu_settings_type::PPUReservationPriorityOverSPUs); + SubscribeTooltip(ui->ppuReservationPrority, tooltips.settings.ppu_reservation_priority); + // Comboboxes m_emu_settings->EnhanceComboBox(ui->xfloatAccuracy, emu_settings_type::XFloatAccuracy); SubscribeTooltip(ui->gb_xfloat_accuracy, tooltips.settings.xfloat); @@ -2144,7 +2147,8 @@ settings_dialog::settings_dialog(std::shared_ptr gui_settings, std { "%R", tr("Renderer", "Game window title") }, { "%T", tr("Title", "Game window title") }, { "%t", tr("Title ID", "Game window title") }, - { "%V", tr("RPCS3 Version", "Game window title") } + { "%V", tr("RPCS3 Version", "Game window title") }, + { "%A", tr("Architecture", "Game window title") } }; QString glossary; diff --git a/rpcs3/rpcs3qt/settings_dialog.ui b/rpcs3/rpcs3qt/settings_dialog.ui index 46feb0c224..6f587470ff 100644 --- a/rpcs3/rpcs3qt/settings_dialog.ui +++ b/rpcs3/rpcs3qt/settings_dialog.ui @@ -2519,6 +2519,13 @@ + + + + PPU Reservation Priority + + + diff --git a/rpcs3/rpcs3qt/shortcut_settings.cpp b/rpcs3/rpcs3qt/shortcut_settings.cpp index 64feb94777..4bff7135b5 100644 --- a/rpcs3/rpcs3qt/shortcut_settings.cpp +++ b/rpcs3/rpcs3qt/shortcut_settings.cpp @@ -24,6 +24,7 @@ void fmt_class_string::format(std::string& out, u64 arg) case shortcut::gw_screenshot: return "gw_screenshot"; case shortcut::gw_toggle_recording: return "gw_toggle_recording"; case shortcut::gw_pause_play: return "gw_pause_play"; + case shortcut::gw_stop: return "gw_stop"; case shortcut::gw_savestate: return "gw_savestate"; case shortcut::gw_savestate_1: return "gw_savestate1"; case shortcut::gw_savestate_2: return "gw_savestate2"; @@ -63,7 +64,7 @@ void fmt_class_string::format(std::string& out, u64 arg) shortcut_settings::shortcut_settings() : shortcut_map({ { shortcut::mw_start, shortcut_info{ "main_window_start", tr("Start"), "Ctrl+E", shortcut_handler_id::main_window, false } }, - { shortcut::mw_stop, shortcut_info{ "main_window_stop", tr("Stop"), "Ctrl+S", shortcut_handler_id::main_window, false } }, + { shortcut::mw_stop, shortcut_info{ "main_window_stop", tr("Stop/Exit Game"), "Ctrl+S", shortcut_handler_id::main_window, false } }, { shortcut::mw_pause, shortcut_info{ "main_window_pause", tr("Pause"), "Ctrl+P", shortcut_handler_id::main_window, false } }, { shortcut::mw_restart, shortcut_info{ "main_window_restart", tr("Restart"), "Ctrl+R", shortcut_handler_id::main_window, false } }, { shortcut::mw_toggle_fullscreen, shortcut_info{ "main_window_toggle_fullscreen", tr("Toggle Fullscreen"), "Alt+Return", shortcut_handler_id::main_window, false } }, @@ -76,11 +77,12 @@ shortcut_settings::shortcut_settings() { shortcut::gw_toggle_recording, shortcut_info{ "game_window_toggle_recording", tr("Start/Stop Recording"), "F11", shortcut_handler_id::game_window, false } }, { shortcut::gw_screenshot, shortcut_info{ "game_window_screenshot", tr("Screenshot"), "F12", shortcut_handler_id::game_window, false } }, { shortcut::gw_pause_play, shortcut_info{ "game_window_pause_play", tr("Pause/Play"), "Ctrl+P", shortcut_handler_id::game_window, false } }, + { shortcut::gw_stop, shortcut_info{ "game_window_stop", tr("Stop/Exit Game"), "", shortcut_handler_id::game_window, false } }, { shortcut::gw_savestate, shortcut_info{ "game_window_savestate", tr("Savestate"), "Ctrl+S", shortcut_handler_id::game_window, false } }, - { shortcut::gw_savestate_1, shortcut_info{ "game_window_savestate_1", tr("Savestate"), "Alt+Ctrl+1", shortcut_handler_id::game_window, false } }, - { shortcut::gw_savestate_2, shortcut_info{ "game_window_savestate_2", tr("Savestate"), "Alt+Ctrl+2", shortcut_handler_id::game_window, false } }, - { shortcut::gw_savestate_3, shortcut_info{ "game_window_savestate_3", tr("Savestate"), "Alt+Ctrl+3", shortcut_handler_id::game_window, false } }, - { shortcut::gw_savestate_4, shortcut_info{ "game_window_savestate_4", tr("Savestate"), "Alt+Ctrl+4", shortcut_handler_id::game_window, false } }, + { shortcut::gw_savestate_1, shortcut_info{ "game_window_savestate_1", tr("Savestate 1"), "Alt+Ctrl+1", shortcut_handler_id::game_window, false } }, + { shortcut::gw_savestate_2, shortcut_info{ "game_window_savestate_2", tr("Savestate 2"), "Alt+Ctrl+2", shortcut_handler_id::game_window, false } }, + { shortcut::gw_savestate_3, shortcut_info{ "game_window_savestate_3", tr("Savestate 3"), "Alt+Ctrl+3", shortcut_handler_id::game_window, false } }, + { shortcut::gw_savestate_4, shortcut_info{ "game_window_savestate_4", tr("Savestate 4"), "Alt+Ctrl+4", shortcut_handler_id::game_window, false } }, { shortcut::gw_restart, shortcut_info{ "game_window_restart", tr("Restart"), "Ctrl+R", shortcut_handler_id::game_window, false } }, { shortcut::gw_rsx_capture, shortcut_info{ "game_window_rsx_capture", tr("RSX Capture"), "Alt+C", shortcut_handler_id::game_window, false } }, { shortcut::gw_frame_limit, shortcut_info{ "game_window_frame_limit", tr("Toggle Framelimit"), "Ctrl+F10", shortcut_handler_id::game_window, false } }, diff --git a/rpcs3/rpcs3qt/shortcut_settings.h b/rpcs3/rpcs3qt/shortcut_settings.h index be14ee1e30..43ea21549d 100644 --- a/rpcs3/rpcs3qt/shortcut_settings.h +++ b/rpcs3/rpcs3qt/shortcut_settings.h @@ -33,6 +33,7 @@ namespace gui gw_screenshot, gw_toggle_recording, gw_pause_play, + gw_stop, gw_savestate, gw_savestate_1, gw_savestate_2, diff --git a/rpcs3/rpcs3qt/shortcut_utils.cpp b/rpcs3/rpcs3qt/shortcut_utils.cpp index 2112dfe0e9..9b457a3362 100644 --- a/rpcs3/rpcs3qt/shortcut_utils.cpp +++ b/rpcs3/rpcs3qt/shortcut_utils.cpp @@ -109,7 +109,7 @@ namespace gui::utils return true; } - static std::string make_simple_name(const std::string& name) + static std::string make_simple_name(std::string_view name) { const std::string simple_name = QString::fromStdString(vfs::escape(name, true)).simplified().toStdString(); return simple_name; @@ -227,7 +227,7 @@ namespace gui::utils Microsoft::WRL::ComPtr pShellLink; Microsoft::WRL::ComPtr pPersistFile; - const auto cleanup = [&](bool return_value, const std::string& fail_reason) -> bool + const auto cleanup = [&](bool return_value, std::string_view fail_reason) -> bool { if (!return_value) sys_log.error("Failed to create shortcut: %s", fail_reason); CoUninitialize(); @@ -611,7 +611,7 @@ namespace gui::utils return result; } - static void remove_shortcuts(const std::string& simple_name, [[maybe_unused]] const std::string& serial) + static void remove_shortcuts(std::string_view simple_name, [[maybe_unused]] const std::string& serial) { if (simple_name.empty() || simple_name == "." || simple_name == "..") { diff --git a/rpcs3/rpcs3qt/steam_utils.cpp b/rpcs3/rpcs3qt/steam_utils.cpp index e5aedf9e20..036a7a4876 100644 --- a/rpcs3/rpcs3qt/steam_utils.cpp +++ b/rpcs3/rpcs3qt/steam_utils.cpp @@ -520,7 +520,7 @@ namespace gui::utils return !path.empty() && fs::is_dir(path); } - u32 steam_shortcut::crc32(const std::string& data) + u32 steam_shortcut::crc32(std::string_view data) { u32 crc = 0xFFFFFFFF; @@ -882,7 +882,7 @@ namespace gui::utils } #endif - std::string steam_shortcut::steamid64_to_32(const std::string& steam_id) + std::string steam_shortcut::steamid64_to_32(std::string_view steam_id) { u64 id = 0; if (!try_to_uint64(&id, steam_id, 0, umax)) @@ -1009,7 +1009,7 @@ namespace gui::utils usz user_count = 0; - const auto find_user_id = [&content, &user_count](const std::string& key, const std::string& comp) -> std::string + const auto find_user_id = [&content, &user_count](const std::string& key, std::string_view comp) -> std::string { user_count = 0; usz pos = 0; diff --git a/rpcs3/rpcs3qt/steam_utils.h b/rpcs3/rpcs3qt/steam_utils.h index deb7c0dad7..d3e011f19a 100644 --- a/rpcs3/rpcs3qt/steam_utils.h +++ b/rpcs3/rpcs3qt/steam_utils.h @@ -105,7 +105,7 @@ namespace gui::utils void update_steam_input_config(const std::string& user_dir); - static u32 crc32(const std::string& data); + static u32 crc32(std::string_view data); static u32 steam_appid(const std::string& exe, const std::string& name); static void append(std::string& s, const std::string& val); @@ -114,7 +114,7 @@ namespace gui::utils static std::string fix_slashes(const std::string& s); static std::string kv_string(const std::string& key, const std::string& value); static std::string kv_int(const std::string& key, u32 value); - static std::string steamid64_to_32(const std::string& steam_id); + static std::string steamid64_to_32(std::string_view steam_id); static std::string get_steam_path(); static std::string get_last_active_steam_user(const std::string& steam_path); diff --git a/rpcs3/rpcs3qt/tooltips.h b/rpcs3/rpcs3qt/tooltips.h index f0f7ded85b..4bc688db38 100644 --- a/rpcs3/rpcs3qt/tooltips.h +++ b/rpcs3/rpcs3qt/tooltips.h @@ -96,6 +96,7 @@ public: 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 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)"); + const QString ppu_reservation_priority = tr("Give PPUs an execution advantage when the SPUs are using reservations.\nThis setting is recommended only for a few game for improving performance.\nDO NOT use it without being advised to due to potential performance degradation."); // debug diff --git a/rpcs3/rpcs3qt/user_manager_dialog.cpp b/rpcs3/rpcs3qt/user_manager_dialog.cpp index 8c96992b2a..5da620effe 100644 --- a/rpcs3/rpcs3qt/user_manager_dialog.cpp +++ b/rpcs3/rpcs3qt/user_manager_dialog.cpp @@ -229,7 +229,7 @@ void user_manager_dialog::OnUserRemove() } } -void user_manager_dialog::GenerateUser(const std::string& user_id, const std::string& username) +void user_manager_dialog::GenerateUser(const std::string& user_id, std::string_view username) { ensure(rpcs3::utils::check_user(user_id) > 0); diff --git a/rpcs3/rpcs3qt/user_manager_dialog.h b/rpcs3/rpcs3qt/user_manager_dialog.h index 75d0ba23e1..fcd5a88fa3 100644 --- a/rpcs3/rpcs3qt/user_manager_dialog.h +++ b/rpcs3/rpcs3qt/user_manager_dialog.h @@ -35,7 +35,7 @@ protected: private: void Init(); void UpdateTable(bool mark_only = false); - static void GenerateUser(const std::string& user_id, const std::string& username); + static void GenerateUser(const std::string& user_id, std::string_view username); static bool ValidateUsername(const QString& text_to_validate); void ShowContextMenu(const QPoint &pos); diff --git a/rpcs3/tests/rpcs3_test.vcxproj b/rpcs3/tests/rpcs3_test.vcxproj index 0c2457bc96..fb96921efc 100644 --- a/rpcs3/tests/rpcs3_test.vcxproj +++ b/rpcs3/tests/rpcs3_test.vcxproj @@ -96,6 +96,7 @@ true + diff --git a/rpcs3/tests/test_rsx_fp_asm.cpp b/rpcs3/tests/test_rsx_fp_asm.cpp index 704df5a23d..c28707de44 100644 --- a/rpcs3/tests/test_rsx_fp_asm.cpp +++ b/rpcs3/tests/test_rsx_fp_asm.cpp @@ -51,7 +51,7 @@ namespace rsx::assembler return nullptr; }; - static FlowGraph CFG_from_source(const std::string& asm_) + static FlowGraph CFG_from_source(std::string_view asm_) { auto ir = FPIR::from_source(asm_); diff --git a/rpcs3/tests/test_spu_analyser.cpp b/rpcs3/tests/test_spu_analyser.cpp new file mode 100644 index 0000000000..57881edaaa --- /dev/null +++ b/rpcs3/tests/test_spu_analyser.cpp @@ -0,0 +1,244 @@ +#include + +#include +#include +#include +#include +#include + +#include "util/types.hpp" +#include "Emu/Cell/SPURecompiler.h" +#include "Emu/Cell/SPUThread.h" +#include "Emu/system_config.h" +#include "Emu/system_config_types.h" + +// Giga SPU analyser regression: a brsl whose return address is a stop-trap leaves +// a dangling target edge after block cleanup, which the reg-state walk must not +// dereference. The SPU program below is made-up data (not from any game). +namespace +{ + constexpr u32 SPU_STOP = 0x00000000u; // stop 0x0 — the no-return trap word + + // RI16: il rt, i16 (opcode 0x081) + constexpr u32 enc_il(u32 rt, u32 imm) + { + return (0x081u << 23) | ((imm & 0xffff) << 7) | (rt & 0x7f); + } + + // RI16: brsl rt, target (opcode 0x066). Branch-relative-and-set-link (call). + constexpr u32 enc_brsl(u32 pos, u32 target) + { + const u32 rel = ((target - pos) / 4) & 0xffff; + return (0x066u << 23) | (rel << 7) | 0u /* rt = $lr ($0) */; + } + + // RR: bi $0 (opcode 0x1a8) — branch indirect to $lr, i.e. function return. + constexpr u32 enc_bi_lr() + { + return 0x1a8u << 21; + } +} + +TEST(SpuAnalyserGiga, ReturnToStopTrapDoesNotRangeCheckFail) +{ + const auto saved = g_cfg.core.spu_block_size.get(); + g_cfg.core.spu_block_size.set(spu_block_size_type::giga); + + auto rec = spu_recompiler_base::make_asmjit_recompiler(); + ASSERT_TRUE(rec); + + std::array, SPU_LS_SIZE / 4> ls{}; + const auto w = [&](u32 addr, u32 instr) { ls[addr / 4] = instr; }; + + // entry@0x00 calls funcA@0x10; the call's return address (0x04) is a stop + // trap, sitting immediately before funcB@0x08 which funcA also calls — so the + // return-point block at 0x04 is created, then removed (stop word), orphaning it. + w(0x00, enc_brsl(0x00, 0x10)); // entry -> funcA ; return = 0x04 + w(0x04, SPU_STOP); // <- the trap that drives the bug + w(0x08, enc_il(3, 7)); // funcB@0x08 + w(0x0c, enc_bi_lr()); // funcB return + w(0x10, enc_brsl(0x10, 0x08)); // funcA@0x10 -> funcB ; return = 0x14 + w(0x14, enc_bi_lr()); // funcA return + + std::map> targets; + + // Pre-fix this aborts inside analyse() (Range check failed); reaching the + // assertions is the regression check. + const spu_program prog = rec->analyse(ls.data(), 0x00, &targets); + EXPECT_EQ(prog.entry_point, 0x00u); + EXPECT_FALSE(prog.data.empty()); + + g_cfg.core.spu_block_size.set(saved); +} + +// Encoders for the added coverage tests below. enc2_ prefix avoids clashing with +// the reused enc_il / enc_brsl / enc_bi_lr / SPU_STOP above; every word is +// MADE-UP data encoded from the SPU opcode tables. +namespace +{ + // RI16 (magn 2): opcode<<23 | i16<<7 | rt + constexpr u32 enc2_il(u32 rt, u32 imm) + { + return (0x081u << 23) | ((imm & 0xffff) << 7) | (rt & 0x7f); + } + + // RI18 (magn 4): opcode<<25 | i18<<7 | rt + constexpr u32 enc2_ila(u32 rt, u32 imm18) + { + return (0x021u << 25) | ((imm18 & 0x3ffff) << 7) | (rt & 0x7f); + } + + // br target - unconditional relative branch (single successor). + constexpr u32 enc2_br(u32 pos, u32 target) + { + const u32 rel = ((target - pos) / 4) & 0xffff; + return (0x064u << 23) | (rel << 7); + } + + // brnz rt, target - conditional relative branch (branch target + fall-through). + constexpr u32 enc2_brnz(u32 rt, u32 pos, u32 target) + { + const u32 rel = ((target - pos) / 4) & 0xffff; + return (0x042u << 23) | (rel << 7) | (rt & 0x7f); + } +} + +// FIX #1 (DISCRIMINATING): the exported target map must be the post-prune, +// self-consistent map. Cleanup removes the 0x04 STOP-trap return block +// (m_block_info[0x04/4] cleared); the prune drops the dead in-range edge +// 0x00 -> 0x04 and appends the SPU_LS_SIZE sentinel. The export was MOVED to run +// AFTER the prune, so callers no longer see 0x04. PASS with the fix; FAIL without +// it (pre-fix the export ran before the prune and leaked 0x04 into m_targets[0x00]). +TEST(SpuAnalyserGiga, OutTargetListHasNoDeadInRangeEdge) +{ + const auto saved = g_cfg.core.spu_block_size.get(); + g_cfg.core.spu_block_size.set(spu_block_size_type::giga); + + auto rec = spu_recompiler_base::make_asmjit_recompiler(); + ASSERT_TRUE(rec); + + std::array, SPU_LS_SIZE / 4> ls{}; + const auto w = [&](u32 addr, u32 instr) { ls[addr / 4] = instr; }; + + w(0x00, enc_brsl(0x00, 0x10)); // entry -> funcA ; return-point block = 0x04 + w(0x04, SPU_STOP); // STOP: kills the 0x04 return block + w(0x08, enc2_il(3, 7)); // funcB@0x08 + w(0x0c, enc_bi_lr()); // funcB return + w(0x10, enc_brsl(0x10, 0x08)); // funcA@0x10 -> funcB ; return-point = 0x14 + w(0x14, enc_bi_lr()); // funcA return + + std::map> targets; + const spu_program prog = rec->analyse(ls.data(), 0x00, &targets); + + EXPECT_EQ(prog.entry_point, 0x00u); + EXPECT_FALSE(prog.data.empty()); + + constexpr u32 kDeadBlock = 0x04u; + + for (const auto& [key, succ] : targets) + { + EXPECT_LT(key, static_cast(SPU_LS_SIZE)) + << "target-map key out of range: 0x" << std::hex << key; + EXPECT_NE(key, kDeadBlock) + << "removed block 0x04 must not be a key in the exported map"; + + for (u32 t : succ) + { + EXPECT_NE(t, kDeadBlock) + << "exported edge from 0x" << std::hex << key + << " still points at removed block 0x04 (export ran before prune)"; + + const bool in_range = (t < static_cast(SPU_LS_SIZE)); + const bool sentinel = (t == static_cast(SPU_LS_SIZE)); + EXPECT_TRUE(in_range || sentinel) + << "exported edge from 0x" << std::hex << key + << " has bad successor 0x" << t; + } + } + + g_cfg.core.spu_block_size.set(saved); +} + +// FIX #2 (POSITIVE walk - does NOT fire the guard): a giga function whose body is +// a multi-block loop, so the loop-body walk advances block_pc through cond_next +// fall-through hops. The guard is belt-and-suspenders and cannot trigger on valid +// input (the prune keeps dead edges out of block.targets). Asserts only that +// analyse() completes and returns a sane program. +TEST(SpuAnalyserGiga, MultiBlockLoopBodyWalkCompletes) +{ + const auto saved = g_cfg.core.spu_block_size.get(); + g_cfg.core.spu_block_size.set(spu_block_size_type::giga); + + auto rec = spu_recompiler_base::make_asmjit_recompiler(); + ASSERT_TRUE(rec); + + std::array, SPU_LS_SIZE / 4> ls{}; + const auto w = [&](u32 addr, u32 instr) { ls[addr / 4] = instr; }; + + w(0x00, enc2_il(3, 4)); // r3 = 4 (loop counter seed) + w(0x04, enc2_il(4, 1)); // r4 = 1 + w(0x08, enc2_brnz(3, 0x08, 0x10)); // if r3 != 0 -> 0x10, else fall to 0x0c + w(0x0c, enc_bi_lr()); // exit path: return + w(0x10, enc2_il(5, 9)); // body block B @0x10 + w(0x14, enc2_brnz(4, 0x14, 0x1c)); // -> 0x1c, else fall to 0x18 + w(0x18, enc2_il(6, 2)); // body block C @0x18 (fall-through chain) + w(0x1c, enc2_br(0x1c, 0x00)); // back-edge to loop head + + std::map> targets; + const spu_program prog = rec->analyse(ls.data(), 0x00, &targets); + + EXPECT_EQ(prog.entry_point, 0x00u); + EXPECT_FALSE(prog.data.empty()); + + for (const auto& [key, succ] : targets) + { + EXPECT_LT(key, static_cast(SPU_LS_SIZE)); + for (u32 t : succ) + { + EXPECT_TRUE(t < static_cast(SPU_LS_SIZE) + || t == static_cast(SPU_LS_SIZE)); + } + } + + g_cfg.core.spu_block_size.set(saved); +} + +// FIX #3 (POSITIVE, NON-DISCRIMINATING): the /41 -> /4 typo lives in the giga +// secondary "Fill more block info" re-decode loop and only corrupts internal +// bb.reg_const / bb.reg_val32 (-> stack_sub / func.good) - none of which reaches +// analyse()'s public outputs (spu_program / out_target_list), and BI/BISL targets +// come from the untouched first pass. So this can only exercise the re-decode loop +// over a multi-instruction block + assert completion + data round-trip; it PASSES +// under both /4 and /41. Discriminating coverage needs a test accessor on the +// internal block state (flagged in the PR notes). +TEST(SpuAnalyserGiga, ReDecodeConstPropCompletes) +{ + const auto saved = g_cfg.core.spu_block_size.get(); + g_cfg.core.spu_block_size.set(spu_block_size_type::giga); + + auto rec = spu_recompiler_base::make_asmjit_recompiler(); + ASSERT_TRUE(rec); + + std::array, SPU_LS_SIZE / 4> ls{}; + const auto w = [&](u32 addr, u32 instr) { ls[addr / 4] = instr; }; + + w(0x00, enc2_il(3, 0x1234)); // index 0 : const into r3 + w(0x04, enc2_ila(4, 0x208)); // index 1 : const into r4 (distinct word) + w(0x08, enc2_il(5, 0x22)); // index 2 : const into r5 + w(0x0c, enc2_ila(6, 0x108)); // index 3 : const into r6 + w(0x10, enc2_il(7, 0x33)); // index 4 : const into r7 + w(0x14, enc_bi_lr()); // return ($lr) + + std::map> targets; + const spu_program prog = rec->analyse(ls.data(), 0x00, &targets); + + EXPECT_EQ(prog.entry_point, 0x00u); + EXPECT_FALSE(prog.data.empty()); + + // data stores std::bit_cast(ls_word); compare underlying u32 forms. + ASSERT_GE(prog.data.size(), (0x10u / 4) + 1); + EXPECT_EQ(prog.data[0x00 / 4], std::bit_cast(be_t{enc2_il(3, 0x1234)})); + EXPECT_EQ(prog.data[0x10 / 4], std::bit_cast(be_t{enc2_il(7, 0x33)})); + + g_cfg.core.spu_block_size.set(saved); +} diff --git a/rpcs3/util/asm.hpp b/rpcs3/util/asm.hpp index 0aea4c10c2..da7326a09f 100644 --- a/rpcs3/util/asm.hpp +++ b/rpcs3/util/asm.hpp @@ -3,11 +3,14 @@ #include "util/types.hpp" #include "util/tsc.hpp" #include "util/atomic.hpp" +#include "util/sysinfo.hpp" #include +#include #ifdef ARCH_X64 #ifdef _MSC_VER #include +#include #else #include #include @@ -216,6 +219,200 @@ namespace utils while (get_tsc() < stop); } +#ifdef ARCH_X64 + inline u64 get_wait_cycles(u64 timeout_us, u64 tsc_freq) + { + constexpr u64 max_timeout = u64{umax}; + + if (!tsc_freq) + { + return 0; + } + + if (timeout_us == max_timeout) + { + return max_timeout; + } + + const u64 seconds = timeout_us / 1'000'000; + const u64 micros = timeout_us % 1'000'000; + + if (seconds > max_timeout / tsc_freq) + { + return max_timeout; + } + + const u64 sec_cycles = seconds * tsc_freq; + const u64 cycles_per_us = tsc_freq / 1'000'000; + + if (micros && cycles_per_us > max_timeout / micros) + { + return max_timeout; + } + + const u64 us_cycles = micros * cycles_per_us + (micros * (tsc_freq % 1'000'000)) / 1'000'000; + return sec_cycles > max_timeout - us_cycles ? max_timeout : sec_cycles + us_cycles; + } +#endif + + template +#if defined(ARCH_X64) && !defined(_MSC_VER) + __attribute__((target("waitpkg,mwaitx"))) +#endif + inline void spin_on_cacheline_once(const atomic_t& var, T old_value, u64 timeout_us) + { + const void* addr = &var.raw(); + +#if defined(ARCH_ARM64) + // WFE will wake from the periodic event stream, so the explicit timeout is ignored on ARM. + (void)timeout_us; + + using wait_type = std::remove_cvref_t; + using raw_type = std::conditional_t>>; + + static_assert(sizeof(wait_type) <= 8, "Unsupported atomic size for spin_on_cacheline_once"); + + raw_type value{}; + const auto* wait_addr = static_cast(addr); + + if constexpr (sizeof(raw_type) == 1) __asm__ volatile("ldaxrb %w0, %1" : "=r"(value) : "Q"(*wait_addr) : "memory"); + else if constexpr (sizeof(raw_type) == 2) __asm__ volatile("ldaxrh %w0, %1" : "=r"(value) : "Q"(*wait_addr) : "memory"); + else if constexpr (sizeof(raw_type) == 4) __asm__ volatile("ldaxr %w0, %1" : "=r"(value) : "Q"(*wait_addr) : "memory"); + else if constexpr (sizeof(raw_type) == 8) __asm__ volatile("ldaxr %x0, %1" : "=r"(value) : "Q"(*wait_addr) : "memory"); + + if (std::bit_cast(value) != old_value) + { + __asm__ volatile("clrex" ::: "memory"); + return; + } + + __asm__ volatile("wfe" ::: "memory"); + __asm__ volatile("clrex" ::: "memory"); +#elif defined(ARCH_X64) + static const bool use_umwait = has_waitpkg(); + static const bool use_waitx = has_waitx(); + + const u64 cycles = get_wait_cycles(timeout_us, get_tsc_freq()); + + if (use_umwait && cycles) + { + _umonitor(const_cast(addr)); + + if (var.load() != old_value) + { + return; + } + + constexpr u64 max_timeout = u64{umax}; + const u64 now = get_tsc(); + const u64 deadline = cycles > max_timeout - now ? max_timeout : now + cycles; + _umwait(0, deadline); + } + else if (use_waitx && cycles) + { + _mm_monitorx(const_cast(addr), 0, 0); + + if (var.load() != old_value) + { + return; + } + + constexpr u32 timer_enable = 2; + _mm_mwaitx(timer_enable, 0, cycles > u32{umax} ? u32{umax} : static_cast(cycles)); + } + else + { + std::this_thread::yield(); + } +#else + (void)addr; + (void)old_value; + (void)timeout_us; + + std::this_thread::yield(); +#endif + } + + template +#if defined(ARCH_X64) && !defined(_MSC_VER) + __attribute__((target("waitpkg,mwaitx"))) +#endif + inline void spin_wait(const atomic_t& var, Pred predicate) + { +#ifdef ARCH_X64 + static const bool use_umwait = has_waitpkg(); + static const bool use_waitx = has_waitx(); +#endif + + const auto read_mem = [&]() + { + return var.load(); + }; + + const void* addr = &var.raw(); + + while (true) + { + if (predicate(read_mem())) + { + return; + } + +#if defined(ARCH_ARM64) + using value_type = decltype(read_mem()); + using wait_type = std::remove_cvref_t; + + wait_type value{}; + const auto* wait_addr = static_cast(addr); + + if constexpr (sizeof(wait_type) == 1) __asm__ volatile("ldaxrb %w0, %1" : "=r"(value) : "Q"(*wait_addr) : "memory"); + else if constexpr (sizeof(wait_type) == 2) __asm__ volatile("ldaxrh %w0, %1" : "=r"(value) : "Q"(*wait_addr) : "memory"); + else if constexpr (sizeof(wait_type) == 4) __asm__ volatile("ldaxr %w0, %1" : "=r"(value) : "Q"(*wait_addr) : "memory"); + else if constexpr (sizeof(wait_type) == 8) __asm__ volatile("ldaxr %x0, %1" : "=r"(value) : "Q"(*wait_addr) : "memory"); + else static_assert(sizeof(wait_type) <= 8, "Unsupported atomic size for spin_wait"); + + if (predicate(static_cast(value))) + { + __asm__ volatile("clrex" ::: "memory"); + return; + } + + __asm__ volatile("wfe" ::: "memory"); + __asm__ volatile("clrex" ::: "memory"); + +#elif defined(ARCH_X64) + if (use_umwait) + { + _umonitor(const_cast(addr)); + if (predicate(read_mem())) + { + return; + } + + _umwait(0, ~0ULL); + } + else if (use_waitx) + { + _mm_monitorx(const_cast(addr), 0, 0); + if (predicate(read_mem())) + { + return; + } + + _mm_mwaitx(0, 0, 0); + } + else + { + pause(); + } +#else + pause(); +#endif + } + } + // Align to power of 2 template requires std::is_unsigned_v diff --git a/rpcs3/util/atomic.hpp b/rpcs3/util/atomic.hpp index f77aad7825..49d9663dec 100644 --- a/rpcs3/util/atomic.hpp +++ b/rpcs3/util/atomic.hpp @@ -1742,6 +1742,11 @@ public: return base::load() != 0; } + const uchar& raw() const + { + return base::raw(); + } + // Override implicit conversion from the parent type explicit operator uchar() const = delete; diff --git a/rpcs3/util/cpu_stats.cpp b/rpcs3/util/cpu_stats.cpp index 8203d1977b..0f4149e909 100644 --- a/rpcs3/util/cpu_stats.cpp +++ b/rpcs3/util/cpu_stats.cpp @@ -208,22 +208,23 @@ namespace utils content << proc_stat.rdbuf(); proc_stat.close(); - const std::vector lines = fmt::split(content.str(), {"\n"}); + const std::string content_str = content.str(); + const std::vector lines = fmt::split_sv(content_str, {"\n"}); if (lines.empty()) { perf_log.error("/proc/stat is empty"); return; } - for (const std::string& line : lines) + for (const std::string_view& line : lines) { - const std::vector tokens = fmt::split(line, {" "}); + const std::vector tokens = fmt::split_sv(line, {" "}); if (tokens.size() < 5) { return; } - const std::string& token = tokens[0]; + const std::string_view token = tokens[0]; if (!token.starts_with("cpu")) { return; diff --git a/rpcs3/util/logs.hpp b/rpcs3/util/logs.hpp index a2fcc863b2..459e35f167 100644 --- a/rpcs3/util/logs.hpp +++ b/rpcs3/util/logs.hpp @@ -175,10 +175,10 @@ namespace logs void silence(); // Log level control: register channel if necessary, set channel level - void set_level(const std::string&, level); + void set_level(const std::string& ch_name, level value); // Log level control: get channel level - level get_level(const std::string&); + level get_level(const std::string& ch_name); // Log level control: set specific channels to level::fatal void set_channel_levels(const std::map>& map); diff --git a/rpcs3/util/sysinfo.cpp b/rpcs3/util/sysinfo.cpp index fb6dd6d7f7..6092da71b5 100755 --- a/rpcs3/util/sysinfo.cpp +++ b/rpcs3/util/sysinfo.cpp @@ -3,6 +3,7 @@ #include "Utilities/File.h" #include "Emu/vfs_config.h" #include "Utilities/Thread.h" +#include "rpcs3_version.h" #if defined(ARCH_ARM64) #include "Emu/CPU/Backends/AArch64/AArch64Common.h" @@ -531,6 +532,17 @@ std::string utils::get_cpu_brand() #endif } +std::string_view utils::get_architecture() +{ +#if defined(ARCH_X64) + return "x64"sv; +#elif defined(ARCH_ARM64) + return "arm64"sv; +#else + return "unknown"sv; +#endif +} + std::string utils::get_system_info() { std::string result; @@ -765,7 +777,8 @@ utils::OS_version utils::get_OS_version() #else if (struct utsname details = {}; !uname(&details)) { - const std::vector version_list = fmt::split(details.release, { "." }); + const std::string_view release = details.release; + const std::vector version_list = fmt::split_sv(release, { "." }); const auto get_version_part = [&version_list](usz i) -> usz { if (version_list.size() <= i) return 0; @@ -784,14 +797,18 @@ utils::OS_version utils::get_OS_version() return res; } -std::string utils::get_OS_version_string() +std::string utils::get_OS_version_string(bool simple) { - std::string output; #ifdef _WIN32 OSVERSIONINFOW osvi{}; osvi.dwOSVersionInfoSize = sizeof(osvi); RtlGetVersion(&osvi); + if (simple) + { + return fmt::format("Windows %lu.%lu.%lu", osvi.dwMajorVersion, osvi.dwMinorVersion, osvi.dwBuildNumber); + } + const bool has_sp = osvi.szCSDVersion[0] != L'\0'; std::vector holder; if (has_sp) @@ -803,8 +820,7 @@ std::string utils::get_OS_version_string() holder.data(), len, nullptr, nullptr); } - fmt::append(output, - "Operating system: Windows, Major: %lu, Minor: %lu, Build: %lu, Service Pack: %s", + return fmt::format("Operating system: Windows, Major: %lu, Minor: %lu, Build: %lu, Service Pack: %s", osvi.dwMajorVersion, osvi.dwMinorVersion, osvi.dwBuildNumber, has_sp ? holder.data() : "none"); #elif defined (__APPLE__) @@ -812,22 +828,41 @@ std::string utils::get_OS_version_string() const int minor_version = Darwin_Version::getNSminorVersion(); const int patch_version = Darwin_Version::getNSpatchVersion(); - fmt::append(output, "Operating system: macOS, Version: %d.%d.%d", - major_version, minor_version, patch_version); + if (simple) + { + return fmt::format("macOS %d.%d.%d", major_version, minor_version, patch_version); + } + + return fmt::format("Operating system: macOS, Version: %d.%d.%d", major_version, minor_version, patch_version); #else struct utsname details = {}; if (!uname(&details)) { - fmt::append(output, "Operating system: POSIX, Name: %s, Release: %s, Version: %s", - details.sysname, details.release, details.version); + if (simple) + { + return fmt::format("%s %s", details.sysname, details.release); + } + + return fmt::format("Operating system: POSIX, Name: %s, Release: %s, Version: %s", details.sysname, details.release, details.version); } - else + + if (simple) { - fmt::append(output, "Operating system: POSIX, Unknown version! (Error: %d)", errno); + return "POSIX"; } + + return fmt::format("Operating system: POSIX, Unknown version! (Error: %d)", errno); #endif - return output; +} + +std::string utils::get_user_agent() +{ + const std::string user_agent = fmt::format("RPCS3/%s (%s; %s)", + rpcs3::get_version().to_string(true), + utils::get_OS_version_string(true), + utils::get_architecture()); + return user_agent; } int utils::get_maxfiles() diff --git a/rpcs3/util/sysinfo.hpp b/rpcs3/util/sysinfo.hpp index dfe781e17a..5d36f24adc 100755 --- a/rpcs3/util/sysinfo.hpp +++ b/rpcs3/util/sysinfo.hpp @@ -71,6 +71,8 @@ namespace utils #endif std::string get_cpu_brand(); + std::string_view get_architecture(); + std::string get_system_info(); std::string get_firmware_version(); @@ -87,7 +89,9 @@ namespace utils }; OS_version get_OS_version(); - std::string get_OS_version_string(); + std::string get_OS_version_string(bool simple = false); + + std::string get_user_agent(); int get_maxfiles();