diff --git a/.ci/build-freebsd.sh b/.ci/build-freebsd.sh deleted file mode 100755 index 8b8eef7f5..000000000 --- a/.ci/build-freebsd.sh +++ /dev/null @@ -1,27 +0,0 @@ -#!/bin/sh -ex - -# Pull all the submodules except llvm and opencv -# Note: Tried to use git submodule status, but it takes over 20 seconds -# shellcheck disable=SC2046 -git submodule -q update --init --depth 1 $(awk '/path/ && !/llvm/ && !/opencv/ { print $3 }' .gitmodules) - -CONFIGURE_ARGS=" - -DWITH_LLVM=ON - -DUSE_SDL=OFF - -DUSE_PRECOMPILED_HEADERS=OFF - -DUSE_NATIVE_INSTRUCTIONS=OFF - -DUSE_SYSTEM_FFMPEG=ON - -DUSE_SYSTEM_CURL=ON - -DUSE_SYSTEM_LIBPNG=ON - -DUSE_SYSTEM_OPENCV=ON -" - -# base Clang workaround (missing clang-scan-deps) -CONFIGURE_ARGS="$CONFIGURE_ARGS -DCMAKE_CXX_SCAN_FOR_MODULES=OFF" - -# shellcheck disable=SC2086 -cmake -B build -G Ninja $CONFIGURE_ARGS -cmake --build build - -ccache --show-stats -ccache --zero-stats diff --git a/.ci/build-linux-aarch64.sh b/.ci/build-linux-aarch64.sh deleted file mode 100755 index 0ee670ea3..000000000 --- a/.ci/build-linux-aarch64.sh +++ /dev/null @@ -1,50 +0,0 @@ -#!/bin/sh -ex - -git config --global --add safe.directory '*' - -# Pull all the submodules except llvm and opencv -# shellcheck disable=SC2046 -git submodule -q update --init $(awk '/path/ && !/llvm/ && !/opencv/ { print $3 }' .gitmodules) - -if [ "$COMPILER" = "gcc" ]; then - export CC=gcc-14 - export CXX=g++-14 -else - export CC=clang - export CXX=clang++ - export CFLAGS="$CFLAGS -fuse-ld=lld" -fi - -cmake -B build \ - -DCMAKE_INSTALL_PREFIX=/usr \ - -DCMAKE_C_FLAGS="$CFLAGS" \ - -DCMAKE_CXX_FLAGS="$CFLAGS" \ - -DUSE_NATIVE_INSTRUCTIONS=OFF \ - -DUSE_PRECOMPILED_HEADERS=OFF \ - -DUSE_SYSTEM_CURL=ON \ - -DUSE_SDL=OFF \ - -DUSE_SYSTEM_FFMPEG=OFF \ - -DUSE_SYSTEM_CURL=OFF \ - -DUSE_SYSTEM_OPENAL=OFF \ - -DUSE_SYSTEM_FFMPEG=OFF \ - -DUSE_DISCORD_RPC=ON \ - -DOpenGL_GL_PREFERENCE=LEGACY \ - -DSTATIC_LINK_LLVM=ON \ - -DBUILD_LLVM=on \ - -DWITH_RPCSX=off \ - -DWITH_RPCS3=on \ - -DWITH_RPCS3_QT_UI=on \ - -G Ninja - -cmake --build build; build_status=$?; - -shellcheck .ci/*.sh - -# If it compiled succesfully let's deploy. -# Azure and Cirrus publish PRs as artifacts only. -{ [ "$CI_HAS_ARTIFACTS" = "true" ]; -} && SHOULD_DEPLOY="true" || SHOULD_DEPLOY="false" - -if [ "$build_status" -eq 0 ] && [ "$SHOULD_DEPLOY" = "true" ]; then - .ci/deploy-linux.sh "aarch64" -fi diff --git a/.ci/build-linux.sh b/.ci/build-linux.sh deleted file mode 100755 index 54b5adc83..000000000 --- a/.ci/build-linux.sh +++ /dev/null @@ -1,51 +0,0 @@ -#!/bin/sh -ex - -git config --global --add safe.directory '*' - -# Pull all the submodules except llvm and opencv -# Note: Tried to use git submodule status, but it takes over 20 seconds -# shellcheck disable=SC2046 -git submodule -q update --init $(awk '/path/ && !/llvm/ && !/opencv/ { print $3 }' .gitmodules) - -if [ "$COMPILER" = "gcc" ]; then - # These are set in the dockerfile - export CC=gcc-14 - export CXX=g++-14 -else - export CC=clang - export CXX=clang++ - export LD=clang - export CFLAGS="$CFLAGS -fuse-ld=lld" -fi - -cmake -B build \ - -DCMAKE_INSTALL_PREFIX=/usr \ - -DCMAKE_C_FLAGS="$CFLAGS" \ - -DCMAKE_CXX_FLAGS="$CFLAGS" \ - -DUSE_NATIVE_INSTRUCTIONS=OFF \ - -DUSE_PRECOMPILED_HEADERS=OFF \ - -DUSE_SDL=OFF \ - -DUSE_SYSTEM_CURL=OFF \ - -DUSE_SYSTEM_OPENAL=OFF \ - -DUSE_SYSTEM_FFMPEG=OFF \ - -DUSE_DISCORD_RPC=ON \ - -DOpenGL_GL_PREFERENCE=LEGACY \ - -DSTATIC_LINK_LLVM=ON \ - -DBUILD_LLVM=on \ - -DWITH_RPCSX=off \ - -DWITH_RPCS3=on \ - -DWITH_RPCS3_QT_UI=on \ - -G Ninja - -cmake --build build; build_status=$?; - -shellcheck .ci/*.sh - -# If it compiled succesfully let's deploy. -# Azure and Cirrus publish PRs as artifacts only. -{ [ "$CI_HAS_ARTIFACTS" = "true" ]; -} && SHOULD_DEPLOY="true" || SHOULD_DEPLOY="false" - -if [ "$build_status" -eq 0 ] && [ "$SHOULD_DEPLOY" = "true" ]; then - .ci/deploy-linux.sh "x86_64" -fi diff --git a/.ci/build-mac-arm64.sh b/.ci/build-mac-arm64.sh deleted file mode 100644 index a9698a888..000000000 --- a/.ci/build-mac-arm64.sh +++ /dev/null @@ -1,160 +0,0 @@ -#!/bin/sh -ex - -# shellcheck disable=SC2086 -brew_arm64_install_packages() { - for pkg in "$@"; do - echo "Fetching bottle for $pkg (arm64)..." - bottle_path="$("$BREW_ARM64_PATH/bin/brew" --cache --bottle-tag=arm64_sonoma "$pkg")" - if [ ! -f "$bottle_path" ]; then - if ! "$BREW_ARM64_PATH/bin/brew" fetch --force --verbose --debug --bottle-tag=arm64_sonoma "$pkg"; then - echo "Failed to fetch bottle for $pkg" - return 1 - fi - bottle_path="$("$BREW_ARM64_PATH/bin/brew" --cache --bottle-tag=arm64_sonoma "$pkg")" - fi - - echo "Installing $pkg (arm64)..." - "$BREW_ARM64_PATH/bin/brew" install --force --force-bottle --ignore-dependencies "$bottle_path" || true - done -} - -export HOMEBREW_NO_AUTO_UPDATE=1 -export HOMEBREW_NO_INSTALLED_DEPENDENTS_CHECK=1 -export HOMEBREW_NO_INSTALL_CLEANUP=1 - -/usr/local/bin/brew update -sudo rm -rf /usr/local/Cellar/curl /usr/local/opt/curl -/usr/local/bin/brew install -f --overwrite curl -/usr/local/bin/brew uninstall -f --ignore-dependencies ffmpeg -/usr/local/bin/brew install -f --build-from-source ffmpeg@5 || true -/usr/local/bin/brew install -f --overwrite python || true -/usr/local/bin/brew link --overwrite python || true -/usr/local/bin/brew install -f --overwrite nasm ninja p7zip ccache pipenv #create-dmg -/usr/local/bin/brew link -f curl || true -/usr/local/bin/brew install llvm@$LLVM_COMPILER_VER glew cmake sdl3 vulkan-headers coreutils -/usr/local/bin/brew link -f llvm@$LLVM_COMPILER_VER ffmpeg@5 || true - -export BREW_ARM64_PATH="/opt/homebrew1" -sudo mkdir -p "$BREW_ARM64_PATH" -sudo chmod 777 "$BREW_ARM64_PATH" -curl -L https://github.com/Homebrew/brew/tarball/master | tar xz --strip 1 -C "$BREW_ARM64_PATH" - -#"$BREW_ARM64_PATH/bin/brew" update -# libvorbis requires Homebrew-installed curl, but we can't run it on x64, and we also need the aarch64 libs, so we swap the binary -brew_arm64_install_packages curl -mv /opt/homebrew1/opt/curl/bin/curl /opt/homebrew1/opt/curl/bin/curl.bak -ln -s /usr/local/opt/curl/bin/curl /opt/homebrew1/opt/curl/bin/curl - -brew_arm64_install_packages 0mq aom aribb24 ca-certificates cjson dav1d ffmpeg@5 fontconfig freetype freetype2 gettext glew gmp gnutls lame libbluray libidn2 libnettle libogg libpng librist libsodium libsoxr libtasn libtasn1 libunistring libvmaf libvorbis libvpx libx11 libxau libxcb libxdmcp llvm@$LLVM_COMPILER_VER mbedtls molten-vk nettle opencore-amr openjpeg openssl opus p11-kit pkg-config pkgconfig pzstd rav1e sdl3 snappy speex srt svt-av1 theora vulkan-headers webp x264 x265 xz z3 zeromq zmq zstd -"$BREW_ARM64_PATH/bin/brew" link -f ffmpeg@5 -ln -s "/opt/homebrew1/opt/llvm@$LLVM_COMPILER_VER/lib/unwind/libunwind.1.dylib" "/opt/homebrew1/opt/llvm@$LLVM_COMPILER_VER/lib/libunwind.1.dylib" - -# moltenvk based on commit for 1.2.11 release -wget https://raw.githubusercontent.com/Homebrew/homebrew-core/6bfc8950c696d1f952425e8af2a6248603dc0df9/Formula/m/molten-vk.rb -/usr/local/bin/brew install -f --overwrite ./molten-vk.rb -export CXX=clang++ -export CC=clang - -export BREW_PATH; -BREW_PATH="$(brew --prefix)" -export BREW_X64_PATH; -BREW_X64_PATH="$("/usr/local/bin/brew" --prefix)" -export BREW_BIN="/usr/local/bin" -export BREW_SBIN="/usr/local/sbin" -export CMAKE_EXTRA_OPTS='-DLLVM_TARGETS_TO_BUILD=arm64' - -export WORKDIR; -WORKDIR="$(pwd)" - -# Get Qt -if [ ! -d "/tmp/Qt/$QT_VER" ]; then - mkdir -p "/tmp/Qt" - git clone https://github.com/engnr/qt-downloader.git - cd qt-downloader - git checkout f52efee0f18668c6d6de2dec0234b8c4bc54c597 - # nested Qt 6.8.3 URL workaround - # sed -i '' "s/'qt{0}_{0}{1}{2}'.format(major, minor, patch)]))/'qt{0}_{0}{1}{2}'.format(major, minor, patch), 'qt{0}_{0}{1}{2}'.format(major, minor, patch)]))/g" qt-downloader - # sed -i '' "s/'{}\/{}\/qt{}_{}\/'/'{0}\/{1}\/qt{2}_{3}\/qt{2}_{3}\/'/g" qt-downloader - cd "/tmp/Qt" - "$BREW_X64_PATH/bin/pipenv" run pip3 install py7zr requests semantic_version lxml - mkdir -p "$QT_VER/macos" ; ln -s "macos" "$QT_VER/clang_64" - # sed -i '' 's/args\.version \/ derive_toolchain_dir(args) \/ //g' "$WORKDIR/qt-downloader/qt-downloader" # Qt 6.8.3 workaround - "$BREW_X64_PATH/bin/pipenv" run "$WORKDIR/qt-downloader/qt-downloader" macos desktop "$QT_VER" clang_64 --opensource --addons qtmultimedia qtimageformats # -o "$QT_VER/clang_64" -fi - -cd "$WORKDIR" -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_ARM64_PATH/opt/sdl3/lib/cmake/SDL3" - -export PATH="$BREW_X64_PATH/opt/llvm@$LLVM_COMPILER_VER/bin:$WORKDIR/qt-downloader/$QT_VER/clang_64/bin:$BREW_BIN:$BREW_SBIN:/usr/bin:/bin:/usr/sbin:/sbin:/opt/X11/bin:/Library/Apple/usr/bin:$PATH" -export LDFLAGS="-L$BREW_ARM64_PATH/lib $BREW_ARM64_PATH/opt/ffmpeg@5/lib/libavcodec.dylib $BREW_ARM64_PATH/opt/ffmpeg@5/lib/libavformat.dylib $BREW_ARM64_PATH/opt/ffmpeg@5/lib/libavutil.dylib $BREW_ARM64_PATH/opt/ffmpeg@5/lib/libswscale.dylib $BREW_ARM64_PATH/opt/ffmpeg@5/lib/libswresample.dylib $BREW_ARM64_PATH/opt/llvm@$LLVM_COMPILER_VER/lib/c++/libc++.1.dylib $BREW_ARM64_PATH/lib/libSDL3.dylib $BREW_ARM64_PATH/lib/libGLEW.dylib $BREW_ARM64_PATH/opt/llvm@$LLVM_COMPILER_VER/lib/libunwind.1.dylib -Wl,-rpath,$BREW_ARM64_PATH/lib" -export CPPFLAGS="-I$BREW_ARM64_PATH/include -I$BREW_X64_PATH/include -no-pie -D__MAC_OS_X_VERSION_MIN_REQUIRED=140000" -export CFLAGS="-D__MAC_OS_X_VERSION_MIN_REQUIRED=140000" -export LIBRARY_PATH="$BREW_ARM64_PATH/lib" -export LD_LIBRARY_PATH="$BREW_ARM64_PATH/lib" - -export VULKAN_SDK -VULKAN_SDK="$BREW_ARM64_PATH/opt/molten-vk" -ln -s "$VULKAN_SDK/lib/libMoltenVK.dylib" "$VULKAN_SDK/lib/libvulkan.dylib" || true -export VK_ICD_FILENAMES="$VULKAN_SDK/share/vulkan/icd.d/MoltenVK_icd.json" - -export LLVM_DIR -LLVM_DIR="$BREW_ARM64_PATH/opt/llvm@$LLVM_COMPILER_VER" -# exclude ffmpeg, LLVM, and sdl from submodule update -# shellcheck disable=SC2046 -git submodule -q update --init --depth=1 --jobs=8 $(awk '/path/ && !/ffmpeg/ && !/llvm/ && !/SDL/ { print $3 }' .gitmodules) - -# 3rdparty fixes -sed -i '' "s/extern const double NSAppKitVersionNumber;/const double NSAppKitVersionNumber = 1343;/g" 3rdparty/hidapi/hidapi/mac/hid.c - -rm -rf build -mkdir build && cd build || exit 1 - -export MACOSX_DEPLOYMENT_TARGET=14.0 - -"$BREW_X64_PATH/bin/cmake" .. \ - -DUSE_SDL=ON \ - -DUSE_DISCORD_RPC=ON \ - -DUSE_VULKAN=ON \ - -DUSE_ALSA=OFF \ - -DUSE_PULSE=OFF \ - -DUSE_AUDIOUNIT=ON \ - -DUSE_SYSTEM_FFMPEG=ON \ - -DLLVM_CCACHE_BUILD=OFF \ - -DLLVM_BUILD_RUNTIME=OFF \ - -DLLVM_BUILD_TOOLS=OFF \ - -DLLVM_INCLUDE_DOCS=OFF \ - -DLLVM_INCLUDE_EXAMPLES=OFF \ - -DLLVM_INCLUDE_TESTS=OFF \ - -DLLVM_INCLUDE_TOOLS=OFF \ - -DLLVM_INCLUDE_UTILS=OFF \ - -DLLVM_USE_PERF=OFF \ - -DLLVM_ENABLE_Z3_SOLVER=OFF \ - -DUSE_NATIVE_INSTRUCTIONS=OFF \ - -DUSE_SYSTEM_MVK=ON \ - -DUSE_SYSTEM_FAUDIO=OFF \ - -DUSE_SYSTEM_SDL=ON \ - -DUSE_SYSTEM_OPENCV=ON \ - $CMAKE_EXTRA_OPTS \ - -DLLVM_TARGET_ARCH=arm64 \ - -DCMAKE_OSX_ARCHITECTURES=arm64 \ - -DCMAKE_IGNORE_PATH="$BREW_X64_PATH/lib" \ - -DCMAKE_IGNORE_PREFIX_PATH=/usr/local/opt \ - -DCMAKE_SYSTEM_PROCESSOR=arm64 \ - -DCMAKE_TOOLCHAIN_FILE=buildfiles/cmake/TCDarwinARM64.cmake \ - -DCMAKE_CXX_FLAGS="-D__MAC_OS_X_VERSION_MIN_REQUIRED=140000" \ - -DCMAKE_POLICY_VERSION_MINIMUM=3.5 \ - -G Ninja - -"$BREW_PATH/bin/ninja"; build_status=$?; - -cd .. - -{ [ "$CI_HAS_ARTIFACTS" = "true" ]; -} && SHOULD_DEPLOY="true" || SHOULD_DEPLOY="false" - -if [ "$build_status" -eq 0 ] && [ "$SHOULD_DEPLOY" = "true" ]; then - .ci/deploy-mac-arm64.sh -fi diff --git a/.ci/build-mac.sh b/.ci/build-mac.sh deleted file mode 100644 index 287bd3949..000000000 --- a/.ci/build-mac.sh +++ /dev/null @@ -1,123 +0,0 @@ -#!/bin/sh -ex - -# shellcheck disable=SC2086 -export HOMEBREW_NO_AUTO_UPDATE=1 -export HOMEBREW_NO_INSTALLED_DEPENDENTS_CHECK=1 -brew unlink certifi -brew install -f --overwrite nasm ninja p7zip ccache pipenv #create-dmg - -#/usr/sbin/softwareupdate --install-rosetta --agree-to-license -arch -x86_64 /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" -arch -x86_64 /usr/local/bin/brew update -arch -x86_64 /usr/local/bin/brew install -f --overwrite python || arch -x86_64 /usr/local/bin/brew link --overwrite python -arch -x86_64 /usr/local/bin/brew uninstall -f --ignore-dependencies ffmpeg -arch -x86_64 /usr/local/bin/brew install -f --build-from-source ffmpeg@5 -arch -x86_64 /usr/local/bin/brew reinstall -f --build-from-source gnutls freetype -arch -x86_64 /usr/local/bin/brew install llvm@$LLVM_COMPILER_VER glew cmake sdl3 vulkan-headers coreutils -arch -x86_64 /usr/local/bin/brew link -f llvm@$LLVM_COMPILER_VER ffmpeg@5 - -# moltenvk based on commit for 1.2.11 release -wget https://raw.githubusercontent.com/Homebrew/homebrew-core/6bfc8950c696d1f952425e8af2a6248603dc0df9/Formula/m/molten-vk.rb -arch -x86_64 /usr/local/bin/brew install -f --overwrite ./molten-vk.rb -export CXX=clang++ -export CC=clang - -export BREW_PATH; -BREW_PATH="$(brew --prefix)" -export BREW_X64_PATH; -BREW_X64_PATH="$("/usr/local/bin/brew" --prefix)" -export BREW_BIN="/usr/local/bin" -export BREW_SBIN="/usr/local/sbin" -export CMAKE_EXTRA_OPTS='-DLLVM_TARGETS_TO_BUILD=X86' - -export WORKDIR; -WORKDIR="$(pwd)" - -# Get Qt -if [ ! -d "/tmp/Qt/$QT_VER" ]; then - mkdir -p "/tmp/Qt" - git clone https://github.com/engnr/qt-downloader.git - cd qt-downloader - git checkout f52efee0f18668c6d6de2dec0234b8c4bc54c597 - # nested Qt 6.8.3 URL workaround - # sed -i '' "s/'qt{0}_{0}{1}{2}'.format(major, minor, patch)]))/'qt{0}_{0}{1}{2}'.format(major, minor, patch), 'qt{0}_{0}{1}{2}'.format(major, minor, patch)]))/g" qt-downloader - # sed -i '' "s/'{}\/{}\/qt{}_{}\/'/'{0}\/{1}\/qt{2}_{3}\/qt{2}_{3}\/'/g" qt-downloader - cd "/tmp/Qt" - "$BREW_X64_PATH/bin/pipenv" run pip3 install py7zr requests semantic_version lxml - mkdir -p "$QT_VER/macos" ; ln -s "macos" "$QT_VER/clang_64" - # sed -i '' 's/args\.version \/ derive_toolchain_dir(args) \/ //g' "$WORKDIR/qt-downloader/qt-downloader" # Qt 6.8.3 workaround - "$BREW_X64_PATH/bin/pipenv" run "$WORKDIR/qt-downloader/qt-downloader" macos desktop "$QT_VER" clang_64 --opensource --addons qtmultimedia qtimageformats # -o "$QT_VER/clang_64" -fi - -cd "$WORKDIR" -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_X64_PATH/opt/sdl3/lib/cmake/SDL3" - -export PATH="$BREW_X64_PATH/opt/llvm@$LLVM_COMPILER_VER/bin:$WORKDIR/qt-downloader/$QT_VER/clang_64/bin:$BREW_BIN:$BREW_SBIN:/usr/bin:/bin:/usr/sbin:/sbin:/opt/X11/bin:/Library/Apple/usr/bin:$PATH" -export LDFLAGS="-L$BREW_X64_PATH/lib -Wl,-rpath,$BREW_X64_PATH/lib" -export CPPFLAGS="-I$BREW_X64_PATH/include -msse -msse2 -mcx16 -no-pie -D__MAC_OS_X_VERSION_MIN_REQUIRED=140000" -export CFLAGS="-D__MAC_OS_X_VERSION_MIN_REQUIRED=140000" -export LIBRARY_PATH="$BREW_X64_PATH/lib" -export LD_LIBRARY_PATH="$BREW_X64_PATH/lib" - -export VULKAN_SDK -VULKAN_SDK="$BREW_X64_PATH/opt/molten-vk" -ln -s "$VULKAN_SDK/lib/libMoltenVK.dylib" "$VULKAN_SDK/lib/libvulkan.dylib" -export VK_ICD_FILENAMES="$VULKAN_SDK/share/vulkan/icd.d/MoltenVK_icd.json" - -export LLVM_DIR -LLVM_DIR="BREW_X64_PATH/opt/llvm@$LLVM_COMPILER_VER" -# exclude ffmpeg, LLVM, opencv, and sdl from submodule update -# shellcheck disable=SC2046 -git submodule -q update --init --depth=1 --jobs=8 $(awk '/path/ && !/ffmpeg/ && !/llvm/ && !/opencv/ && !/SDL/ { print $3 }' .gitmodules) - -# 3rdparty fixes -sed -i '' "s/extern const double NSAppKitVersionNumber;/const double NSAppKitVersionNumber = 1343;/g" 3rdparty/hidapi/hidapi/mac/hid.c - -mkdir build && cd build || exit 1 - -export MACOSX_DEPLOYMENT_TARGET=14.0 - -"$BREW_X64_PATH/bin/cmake" .. \ - -DUSE_SDL=ON \ - -DUSE_DISCORD_RPC=ON \ - -DUSE_VULKAN=ON \ - -DUSE_ALSA=OFF \ - -DUSE_PULSE=OFF \ - -DUSE_AUDIOUNIT=ON \ - -DUSE_SYSTEM_FFMPEG=ON \ - -DLLVM_CCACHE_BUILD=OFF \ - -DLLVM_BUILD_RUNTIME=OFF \ - -DLLVM_BUILD_TOOLS=OFF \ - -DLLVM_INCLUDE_DOCS=OFF \ - -DLLVM_INCLUDE_EXAMPLES=OFF \ - -DLLVM_INCLUDE_TESTS=OFF \ - -DLLVM_INCLUDE_TOOLS=OFF \ - -DLLVM_INCLUDE_UTILS=OFF \ - -DLLVM_USE_PERF=OFF \ - -DLLVM_ENABLE_Z3_SOLVER=OFF \ - -DUSE_NATIVE_INSTRUCTIONS=OFF \ - -DUSE_SYSTEM_MVK=ON \ - -DUSE_SYSTEM_FAUDIO=OFF \ - -DUSE_SYSTEM_SDL=ON \ - -DUSE_SYSTEM_OPENCV=ON \ - $CMAKE_EXTRA_OPTS \ - -DLLVM_TARGET_ARCH=X86_64 \ - -DCMAKE_OSX_ARCHITECTURES=x86_64 \ - -DCMAKE_IGNORE_PATH="$BREW_PATH/lib" \ - -DCMAKE_CXX_FLAGS="-D__MAC_OS_X_VERSION_MIN_REQUIRED=140000" \ - -DCMAKE_POLICY_VERSION_MINIMUM=3.5 \ - -G Ninja - -"$BREW_PATH/bin/ninja"; build_status=$?; - -cd .. - -{ [ "$CI_HAS_ARTIFACTS" = "true" ]; -} && SHOULD_DEPLOY="true" || SHOULD_DEPLOY="false" - -if [ "$build_status" -eq 0 ] && [ "$SHOULD_DEPLOY" = "true" ]; then - .ci/deploy-mac.sh -fi diff --git a/.ci/deploy-linux.sh b/.ci/deploy-linux.sh deleted file mode 100755 index 48a94c0d4..000000000 --- a/.ci/deploy-linux.sh +++ /dev/null @@ -1,35 +0,0 @@ -#!/bin/sh -ex - -cd build || exit 1 - -CPU_ARCH="${1:-x86_64}" - -if [ "$DEPLOY_APPIMAGE" = "true" ]; then - DESTDIR=AppDir ninja install - - sudo curl -fsSLo /usr/bin/linuxdeploy "https://github.com/linuxdeploy/linuxdeploy/releases/download/continuous/linuxdeploy-$CPU_ARCH.AppImage" - sudo chmod a+x /usr/bin/linuxdeploy - sudo curl -fsSLo /usr/bin/linuxdeploy-plugin-qt "https://github.com/linuxdeploy/linuxdeploy-plugin-qt/releases/download/continuous/linuxdeploy-plugin-qt-$CPU_ARCH.AppImage" - sudo chmod a+x /usr/bin/linuxdeploy-plugin-qt - curl -fsSLo linuxdeploy-plugin-checkrt.sh https://github.com/darealshinji/linuxdeploy-plugin-checkrt/releases/download/continuous/linuxdeploy-plugin-checkrt.sh - chmod +x ./linuxdeploy-plugin-checkrt.sh - - export EXTRA_PLATFORM_PLUGINS="libqwayland-egl.so;libqwayland-generic.so" - export EXTRA_QT_PLUGINS="svg;wayland-decoration-client;wayland-graphics-integration-client;wayland-shell-integration;waylandcompositor" - - APPIMAGE_EXTRACT_AND_RUN=1 linuxdeploy --appdir AppDir --plugin qt --plugin checkrt - - # Remove libwayland-client because it has platform-dependent exports and breaks other OSes - rm -f ./AppDir/usr/lib/libwayland-client.so* - - # Remove libvulkan because it causes issues with gamescope - rm -f ./AppDir/usr/lib/libvulkan.so* - - # Remove git directory containing local commit history file - rm -rf ./AppDir/usr/share/rpcs3/git - - linuxdeploy --appimage-extract - ./squashfs-root/plugins/linuxdeploy-plugin-appimage/usr/bin/appimagetool AppDir -g - - mv ./*.AppImage ../RPCS3-Qt-UI.AppImage -fi diff --git a/.ci/deploy-mac-arm64.sh b/.ci/deploy-mac-arm64.sh deleted file mode 100644 index da1fbe916..000000000 --- a/.ci/deploy-mac-arm64.sh +++ /dev/null @@ -1,76 +0,0 @@ -#!/bin/sh -ex - -# shellcheck disable=SC2086 -cd build || exit 1 - -# Gather explicit version number and number of commits -COMM_TAG=$(awk '/version{.*}/ { printf("%d.%d.%d", $5, $6, $7) }' ../rpcs3/rpcs3_version.cpp) -COMM_COUNT=$(git rev-list --count HEAD) -COMM_HASH=$(git rev-parse --short=8 HEAD) - -AVVER="${COMM_TAG}-${COMM_COUNT}" - -# AVVER is used for GitHub releases, it is the version number. -echo "AVVER=$AVVER" >> ../.ci/ci-vars.env - -cd bin -mkdir "rpcs3.app/Contents/lib/" || true - -cp "$(realpath /opt/homebrew1/opt/llvm@$LLVM_COMPILER_VER/lib/c++/libc++abi.1.0.dylib)" "rpcs3.app/Contents/Frameworks/libc++abi.1.dylib" -cp "$(realpath /opt/homebrew1/lib/libsharpyuv.0.dylib)" "rpcs3.app/Contents/lib/libsharpyuv.0.dylib" -cp "$(realpath /opt/homebrew1/lib/libintl.8.dylib)" "rpcs3.app/Contents/lib/libintl.8.dylib" - -rm -rf "rpcs3.app/Contents/Frameworks/QtPdf.framework" \ -"rpcs3.app/Contents/Frameworks/QtQml.framework" \ -"rpcs3.app/Contents/Frameworks/QtQmlModels.framework" \ -"rpcs3.app/Contents/Frameworks/QtQuick.framework" \ -"rpcs3.app/Contents/Frameworks/QtVirtualKeyboard.framework" \ -"rpcs3.app/Contents/Plugins/platforminputcontexts" \ -"rpcs3.app/Contents/Plugins/virtualkeyboard" \ -"rpcs3.app/Contents/Resources/git" - -../../.ci/optimize-mac.sh rpcs3.app - -# Hack -install_name_tool \ --delete_rpath /opt/homebrew1/lib \ --delete_rpath /opt/homebrew/lib \ --delete_rpath /opt/homebrew1/opt/llvm@$LLVM_COMPILER_VER/lib \ --delete_rpath /usr/local/lib RPCS3.app/Contents/MacOS/rpcs3 -#-delete_rpath /opt/homebrew1/Cellar/sdl3/3.2.8/lib - -# Need to do this rename hack due to case insensitive filesystem -mv rpcs3.app RPCS3_.app -mv RPCS3_.app RPCS3.app - -# NOTE: "--deep" is deprecated -codesign --deep -fs - RPCS3.app - -echo "[InternetShortcut]" > Quickstart.url -echo "URL=https://rpcs3.net/quickstart" >> Quickstart.url -echo "IconIndex=0" >> Quickstart.url - -#DMG_FILEPATH="$BUILD_ARTIFACTSTAGINGDIRECTORY/rpcs3-v${COMM_TAG}-${COMM_COUNT}-${COMM_HASH}_macos_arm64.dmg" -#"$BREW_X64_PATH/bin/create-dmg" --volname RPCS3 \ -#--window-size 800 400 \ -#--icon-size 100 \ -#--icon rpcs3.app 200 190 \ -#--add-file Quickstart.url Quickstart.url 400 20 \ -#--hide-extension rpcs3.app \ -#--hide-extension Quickstart.url \ -#--app-drop-link 600 185 \ -#--skip-jenkins \ -#--format ULMO \ -#"$DMG_FILEPATH" \ -#RPCS3.app -#FILESIZE=$(stat -f %z "$DMG_FILEPATH") -#SHA256SUM=$(shasum -a 256 "$DMG_FILEPATH" | awk '{ print $1 }') - -ARCHIVE_FILEPATH="$BUILD_ARTIFACTSTAGINGDIRECTORY/rpcs3-v${COMM_TAG}-${COMM_COUNT}-${COMM_HASH}_macos_arm64.7z" -"$BREW_X64_PATH/bin/7z" a -mx9 "$ARCHIVE_FILEPATH" RPCS3.app Quickstart.url -FILESIZE=$(stat -f %z "$ARCHIVE_FILEPATH") -SHA256SUM=$(shasum -a 256 "$ARCHIVE_FILEPATH" | awk '{ print $1 }') - -cd .. -echo "${SHA256SUM};${FILESIZE}B" > "$RELEASE_MESSAGE" -cd bin diff --git a/.ci/deploy-mac.sh b/.ci/deploy-mac.sh deleted file mode 100644 index c29335874..000000000 --- a/.ci/deploy-mac.sh +++ /dev/null @@ -1,74 +0,0 @@ -#!/bin/sh -ex - -# shellcheck disable=SC2086 -cd build || exit 1 - -# Gather explicit version number and number of commits -COMM_TAG=$(awk '/version{.*}/ { printf("%d.%d.%d", $5, $6, $7) }' ../rpcs3/rpcs3_version.cpp) -COMM_COUNT=$(git rev-list --count HEAD) -COMM_HASH=$(git rev-parse --short=8 HEAD) - -AVVER="${COMM_TAG}-${COMM_COUNT}" - -# AVVER is used for GitHub releases, it is the version number. -echo "AVVER=$AVVER" >> ../.ci/ci-vars.env - -cd bin -mkdir "rpcs3.app/Contents/lib/" - -cp "/usr/local/opt/llvm@$LLVM_COMPILER_VER/lib/c++/libc++abi.1.0.dylib" "rpcs3.app/Contents/lib/libc++abi.1.dylib" -cp "$(realpath /usr/local/lib/libsharpyuv.0.dylib)" "rpcs3.app/Contents/lib/libsharpyuv.0.dylib" -cp "$(realpath /usr/local/lib/libintl.8.dylib)" "rpcs3.app/Contents/lib/libintl.8.dylib" - -rm -rf "rpcs3.app/Contents/Frameworks/QtPdf.framework" \ -"rpcs3.app/Contents/Frameworks/QtQml.framework" \ -"rpcs3.app/Contents/Frameworks/QtQmlModels.framework" \ -"rpcs3.app/Contents/Frameworks/QtQuick.framework" \ -"rpcs3.app/Contents/Frameworks/QtVirtualKeyboard.framework" \ -"rpcs3.app/Contents/Plugins/platforminputcontexts" \ -"rpcs3.app/Contents/Plugins/virtualkeyboard" \ -"rpcs3.app/Contents/Resources/git" - -../../.ci/optimize-mac.sh rpcs3.app - -# Need to do this rename hack due to case insensitive filesystem -mv rpcs3.app RPCS3_.app -mv RPCS3_.app RPCS3.app - -# Hack -install_name_tool \ --delete_rpath /usr/local/lib \ --delete_rpath /usr/local/opt/llvm@$LLVM_COMPILER_VER/lib RPCS3.app/Contents/MacOS/rpcs3 -#-delete_rpath /usr/local/Cellar/sdl3/3.2.8/lib - -# NOTE: "--deep" is deprecated -codesign --deep -fs - RPCS3.app - -echo "[InternetShortcut]" > Quickstart.url -echo "URL=https://rpcs3.net/quickstart" >> Quickstart.url -echo "IconIndex=0" >> Quickstart.url - -#DMG_FILEPATH="$BUILD_ARTIFACTSTAGINGDIRECTORY/rpcs3-v${COMM_TAG}-${COMM_COUNT}-${COMM_HASH}_macos.dmg" -#"$BREW_X64_PATH/bin/create-dmg" --volname RPCS3 \ -#--window-size 800 400 \ -#--icon-size 100 \ -#--icon rpcs3.app 200 190 \ -#--add-file Quickstart.url Quickstart.url 400 20 \ -#--hide-extension rpcs3.app \ -#--hide-extension Quickstart.url \ -#--app-drop-link 600 185 \ -#--skip-jenkins \ -#--format ULMO \ -#"$DMG_FILEPATH" \ -#RPCS3.app -#FILESIZE=$(stat -f %z "$DMG_FILEPATH") -#SHA256SUM=$(shasum -a 256 "$DMG_FILEPATH" | awk '{ print $1 }') - -ARCHIVE_FILEPATH="$BUILD_ARTIFACTSTAGINGDIRECTORY/rpcs3-v${COMM_TAG}-${COMM_COUNT}-${COMM_HASH}_macos.7z" -"$BREW_X64_PATH/bin/7z" a -mx9 "$ARCHIVE_FILEPATH" RPCS3.app Quickstart.url -FILESIZE=$(stat -f %z "$ARCHIVE_FILEPATH") -SHA256SUM=$(shasum -a 256 "$ARCHIVE_FILEPATH" | awk '{ print $1 }') - -cd .. -echo "${SHA256SUM};${FILESIZE}B" > "$RELEASE_MESSAGE" -cd bin diff --git a/.ci/deploy-windows.sh b/.ci/deploy-windows.sh deleted file mode 100755 index c4e88e8f2..000000000 --- a/.ci/deploy-windows.sh +++ /dev/null @@ -1,19 +0,0 @@ -#!/bin/sh -ex - -# First let's see print some info about our caches -"$(cygpath -u "$CCACHE_BIN_DIR")"/ccache.exe --show-stats -v - -# Remove unecessary files -rm -f ./rpcs3/bin/rpcs3.exp ./rpcs3/bin/rpcs3.lib ./rpcs3/bin/rpcs3.pdb ./rpcs3/bin/vc_redist.x64.exe -rm -rf ./rpcs3/bin/git - -# Prepare compatibility and SDL database for packaging -mkdir -p ./rpcs3/bin/config/input_configs -curl -fsSL 'https://raw.githubusercontent.com/gabomdq/SDL_GameControllerDB/master/gamecontrollerdb.txt' 1> ./rpcs3/bin/config/input_configs/gamecontrollerdb.txt -cp -rf ./build-msvc/bin/ ./rpcs3/bin/ - -# Package artifacts -7z a -m0=LZMA2 -mx9 "$BUILD" ./rpcs3/bin/* - -# Move files to publishing directory -cp -- "$BUILD" ./RPCS3-Qt-UI.7z diff --git a/.ci/docker.env b/.ci/docker.env deleted file mode 100644 index 2b36fb34c..000000000 --- a/.ci/docker.env +++ /dev/null @@ -1,15 +0,0 @@ -# Variables set by Azure Pipelines -CI_HAS_ARTIFACTS -BUILD_REASON -BUILD_SOURCEVERSION -BUILD_ARTIFACTSTAGINGDIRECTORY -BUILD_REPOSITORY_NAME -BUILD_SOURCEBRANCHNAME -APPDIR -ARTDIR -RELEASE_MESSAGE -# Variables for build matrix -COMPILER -DEPLOY_APPIMAGE -# Private variables -GITHUB_TOKEN diff --git a/.ci/export-azure-vars.sh b/.ci/export-azure-vars.sh deleted file mode 100755 index 033dd41cc..000000000 --- a/.ci/export-azure-vars.sh +++ /dev/null @@ -1,13 +0,0 @@ -#!/bin/sh -e - -# Export variables for later stages of the Azure pipeline -# Values done in this manner will appear as environment variables -# in later stages. - -# From pure-sh-bible -# Setting 'IFS' tells 'read' where to split the string. -while IFS='=' read -r key val; do - # Skip over lines containing comments. - [ "${key##\#*}" ] || continue - echo "##vso[task.setvariable variable=$key]$val" -done < ".ci/ci-vars.env" diff --git a/.ci/export-cirrus-vars.sh b/.ci/export-cirrus-vars.sh deleted file mode 100644 index 561e77e92..000000000 --- a/.ci/export-cirrus-vars.sh +++ /dev/null @@ -1,13 +0,0 @@ -#!/bin/sh -e - -# Export variables for later stages of the Cirrus pipeline -# Values done in this manner will appear as environment variables -# in later stages. - -# From pure-sh-bible -# Setting 'IFS' tells 'read' where to split the string. -while IFS='=' read -r key val; do - # Skip over lines containing comments. - [ "${key##\#*}" ] || continue - export "$key"="$val" -done < ".ci/ci-vars.env" diff --git a/.ci/generate-qt-ts.sh b/.ci/generate-qt-ts.sh deleted file mode 100755 index a9fc139a9..000000000 --- a/.ci/generate-qt-ts.sh +++ /dev/null @@ -1,13 +0,0 @@ -#!/bin/sh -ex - -mkdir -p ../translations - -LUPDATE_PATH=$(find /usr -name lupdate -type f 2>/dev/null | head -n 1) -if [ -z "$LUPDATE_PATH" ]; then - echo "Error: lupdate not found!" - exit 1 -else - echo "lupdate found at: $LUPDATE_PATH" - $LUPDATE_PATH -recursive . -ts ../translations/rpcs3_template.ts - sed -i 's|filename="\.\./|filename="./|g' ../translations/rpcs3_template.ts -fi \ No newline at end of file diff --git a/.ci/get_keys-windows.sh b/.ci/get_keys-windows.sh deleted file mode 100644 index 8c7ea9c14..000000000 --- a/.ci/get_keys-windows.sh +++ /dev/null @@ -1,4 +0,0 @@ -#!/bin/sh -ex - -curl -fLo "./llvm.lock" "https://github.com/RPCS3/llvm-mirror/releases/download/custom-build-win-${LLVM_VER}/llvmlibs_mt.7z.sha256" -curl -fLo "./glslang.lock" "https://github.com/RPCS3/glslang/releases/download/custom-build-win/glslanglibs_mt.7z.sha256" diff --git a/.ci/github-upload.sh b/.ci/github-upload.sh deleted file mode 100755 index 8cc89314d..000000000 --- a/.ci/github-upload.sh +++ /dev/null @@ -1,42 +0,0 @@ -#!/bin/sh -ex - -ARTIFACT_DIR="$BUILD_ARTIFACTSTAGINGDIRECTORY" -generate_post_data() -{ - body=$(cat GitHubReleaseMessage.txt) - cat <> release.json - -cat release.json -id=$(grep '"id"' release.json | cut -d ':' -f2 | head -n1 | awk '{$1=$1;print}') -id=${id%?} -echo "${id:?}" - -upload_file() -{ - curl -fsS \ - -H "Authorization: token ${RPCS3_TOKEN}" \ - -H "Accept: application/vnd.github.v3+json" \ - -H "Content-Type: application/octet-stream" \ - --data-binary @"$2"/"$3" \ - "https://uploads.github.com/repos/$UPLOAD_REPO_FULL_NAME/releases/$1/assets?name=$3" -} - -for file in "$ARTIFACT_DIR"/*; do - name=$(basename "$file") - upload_file "$id" "$ARTIFACT_DIR" "$name" -done diff --git a/.ci/install-freebsd.sh b/.ci/install-freebsd.sh deleted file mode 100755 index de10561df..000000000 --- a/.ci/install-freebsd.sh +++ /dev/null @@ -1,18 +0,0 @@ -#!/usr/bin/env -S su -m root -ex -# NOTE: this script is run under root permissions -# shellcheck shell=sh disable=SC2096 - -# RPCS3 often needs recent Qt and Vulkan-Headers -sed -i '' 's/quarterly/latest/' /etc/pkg/FreeBSD.conf - -export ASSUME_ALWAYS_YES=true -pkg info # debug - -# WITH_LLVM -pkg install "llvm$LLVM_COMPILER_VER" - -# Mandatory dependencies (qtX-base is pulled via qtX-multimedia) -pkg install git ccache cmake ninja "qt$QT_VER_MAIN-multimedia" "qt$QT_VER_MAIN-svg" glew openal-soft ffmpeg - -# Optional dependencies (libevdev is pulled by qtX-base) -pkg install pkgconf alsa-lib pulseaudio sdl3 evdev-proto vulkan-headers vulkan-loader diff --git a/.ci/optimize-mac.sh b/.ci/optimize-mac.sh deleted file mode 100644 index 5fea7877f..000000000 --- a/.ci/optimize-mac.sh +++ /dev/null @@ -1,21 +0,0 @@ -#!/bin/sh - -file_path=$(find "$1/Contents/MacOS" -type f -print0 | head -n 1) - -if [ -z "$file_path" ]; then - echo "No executable file found in $1/Contents/MacOS" >&2 - exit 1 -fi - - -target_architecture="$(lipo "$file_path" -archs)" - -if [ -z "$target_architecture" ]; then - exit 1 -fi - -# shellcheck disable=SC3045 -find "$1" -type f -print0 | while IFS= read -r -d '' file; do - echo Thinning "$file" -> "$target_architecture" - lipo "$file" -thin "$target_architecture" -output "$file" || true -done diff --git a/.ci/setup-windows.sh b/.ci/setup-windows.sh deleted file mode 100755 index d06a078ab..000000000 --- a/.ci/setup-windows.sh +++ /dev/null @@ -1,134 +0,0 @@ -#!/bin/sh -ex - -# These are Azure specific, so we wrap them for portability -REPO_NAME="$BUILD_REPOSITORY_NAME" -REPO_BRANCH="$SYSTEM_PULLREQUEST_SOURCEBRANCH" -PR_NUMBER="$SYSTEM_PULLREQUEST_PULLREQUESTID" - -# Resource/dependency URLs -# Qt mirrors can be volatile and slow, so we list 2 -#QT_HOST="http://mirrors.ocf.berkeley.edu/qt/" -QT_HOST="http://qt.mirror.constant.com/" -QT_URL_VER=$(echo "$QT_VER" | sed "s/\.//g") -QT_VER_MSVC_UP=$(echo "${QT_VER_MSVC}" | tr '[:lower:]' '[:upper:]') -QT_PREFIX="online/qtsdkrepository/windows_x86/desktop/qt${QT_VER_MAIN}_${QT_URL_VER}/qt${QT_VER_MAIN}_${QT_URL_VER}/qt.qt${QT_VER_MAIN}.${QT_URL_VER}." -QT_PREFIX_2="win64_${QT_VER_MSVC}_64/${QT_VER}-0-${QT_DATE}" -QT_SUFFIX="-Windows-Windows_11_23H2-${QT_VER_MSVC_UP}-Windows-Windows_11_23H2-X86_64.7z" -QT_BASE_URL="${QT_HOST}${QT_PREFIX}${QT_PREFIX_2}qtbase${QT_SUFFIX}" -QT_DECL_URL="${QT_HOST}${QT_PREFIX}${QT_PREFIX_2}qtdeclarative${QT_SUFFIX}" -QT_TOOL_URL="${QT_HOST}${QT_PREFIX}${QT_PREFIX_2}qttools${QT_SUFFIX}" -QT_MM_URL="${QT_HOST}${QT_PREFIX}addons.qtmultimedia.${QT_PREFIX_2}qtmultimedia${QT_SUFFIX}" -QT_SVG_URL="${QT_HOST}${QT_PREFIX}${QT_PREFIX_2}qtsvg${QT_SUFFIX}" -# LLVMLIBS_URL="https://github.com/RPCS3/llvm-mirror/releases/download/custom-build-win-${LLVM_VER}/llvmlibs_mt.7z" -# GLSLANG_URL='https://github.com/RPCS3/glslang/releases/latest/download/glslanglibs_mt.7z' -VULKAN_SDK_URL="https://www.dropbox.com/scl/fi/sjjh0fc4ld281pjbl2xzu/VulkanSDK-${VULKAN_VER}-Installer.exe?rlkey=f6wzc0lvms5vwkt2z3qabfv9d&dl=1" -CCACHE_URL="https://github.com/ccache/ccache/releases/download/v4.11.2/ccache-4.11.2-windows-x86_64.zip" - -DEP_URLS=" \ - $QT_BASE_URL \ - $QT_DECL_URL \ - $QT_TOOL_URL \ - $QT_MM_URL \ - $QT_SVG_URL \ - $LLVMLIBS_URL \ - $GLSLANG_URL \ - $VULKAN_SDK_URL\ - $CCACHE_URL" - -# Azure pipelines doesn't make a cache dir if it doesn't exist, so we do it manually -[ -d "$DEPS_CACHE_DIR" ] || mkdir "$DEPS_CACHE_DIR" - -# Pull all the submodules except llvm, since it is built separately and we just download that build -# Note: Tried to use git submodule status, but it takes over 20 seconds -# shellcheck disable=SC2046 -git submodule -q update --init --depth=1 --jobs=8 $(awk '/path/ && !/FAudio/ { print $3 }' .gitmodules) - -# Git bash doesn't have rev, so here it is -rev() -{ - echo "$1" | awk '{ for(i = length($0); i != 0; --i) { a = a substr($0, i, 1); } } END { print a }' -} - -# Usage: download_and_verify url checksum algo file -# Check to see if a file is already cached, and the checksum matches. If not, download it. -# Tries up to 3 times -download_and_verify() -{ - url="$1" - correctChecksum="$2" - algo="$3" - fileName="$4" - - for _ in 1 2 3; do - [ -e "$DEPS_CACHE_DIR/$fileName" ] || curl -fLo "$DEPS_CACHE_DIR/$fileName" "$url" - fileChecksum=$("${algo}sum" "$DEPS_CACHE_DIR/$fileName" | awk '{ print $1 }') - [ "$fileChecksum" = "$correctChecksum" ] && return 0 - done - - return 1; -} - -# Some dependencies install here -[ -d "./rpcs3/build/lib_ext/Release-x64" ] || mkdir -p "./rpcs3/build/lib_ext/Release-x64" - -for url in $DEP_URLS; do - # Get the filename from the URL and remove query strings (?arg=something). - fileName="$(rev "$(rev "$url" | cut -d'/' -f1)" | cut -d'?' -f1)" - [ -z "$fileName" ] && echo "Unable to parse url: $url" && exit 1 - - # shellcheck disable=SC1003 - case "$url" in - *qt*) checksum=$(curl -fL "${url}.sha1"); algo="sha1"; outDir="$QTDIR/" ;; - *llvm*) checksum=$(curl -fL "${url}.sha256"); algo="sha256"; outDir="./rpcs3/build/lib_ext/Release-x64" ;; - *glslang*) checksum=$(curl -fL "${url}.sha256"); algo="sha256"; outDir="./rpcs3/build/lib_ext/Release-x64" ;; - *ccache*) checksum=$CCACHE_SHA; algo="sha256"; outDir="$CCACHE_BIN_DIR" ;; - *Vulkan*) - # Vulkan setup needs to be run in batch environment - # Need to subshell this or else it doesn't wait - download_and_verify "$url" "$VULKAN_SDK_SHA" "sha256" "$fileName" - cp "$DEPS_CACHE_DIR/$fileName" . - _=$(echo "$fileName --accept-licenses --default-answer --confirm-command install" | cmd) - continue - ;; - *) echo "Unknown url resource: $url"; exit 1 ;; - esac - - download_and_verify "$url" "$checksum" "$algo" "$fileName" - 7z x -y "$DEPS_CACHE_DIR/$fileName" -aos -o"$outDir" -done - -# Setup ccache tool -[ -d "$CCACHE_DIR" ] || mkdir -p "$(cygpath -u "$CCACHE_DIR")" -CCACHE_SH_DIR=$(cygpath -u "$CCACHE_BIN_DIR") -mv "$CCACHE_SH_DIR"/ccache-*/* "$CCACHE_SH_DIR" -cp "$CCACHE_SH_DIR"/ccache.exe "$CCACHE_SH_DIR"/cl.exe - -# Gather explicit version number and number of commits -COMM_TAG=$(awk '/version{.*}/ { printf("%d.%d.%d", $5, $6, $7) }' ./rpcs3/rpcs3_version.cpp) -COMM_COUNT=$(git rev-list --count HEAD) -COMM_HASH=$(git rev-parse --short=8 HEAD) - -# Format the above into filenames -if [ -n "$PR_NUMBER" ]; then - AVVER="${COMM_TAG}-${COMM_HASH}" - BUILD_RAW="rpcs3-v${AVVER}_win64" - BUILD="${BUILD_RAW}.7z" -else - AVVER="${COMM_TAG}-${COMM_COUNT}" - BUILD_RAW="rpcs3-v${AVVER}-${COMM_HASH}_win64" - BUILD="${BUILD_RAW}.7z" -fi - -# BRANCH is used for experimental build warnings for pr builds, used in main_window.cpp. -# BUILD is the name of the release artifact -# BUILD_RAW is just filename -# AVVER is used for GitHub releases, it is the version number. -BRANCH="${REPO_NAME}/${REPO_BRANCH}" - -# SC2129 -{ - echo "BRANCH=$BRANCH" - echo "BUILD=$BUILD" - echo "BUILD_RAW=$BUILD_RAW" - echo "AVVER=$AVVER" -} >> .ci/ci-vars.env diff --git a/.github/workflows/rpcs3.yml b/.github/workflows/rpcs3.yml deleted file mode 100644 index d09e10357..000000000 --- a/.github/workflows/rpcs3.yml +++ /dev/null @@ -1,222 +0,0 @@ -name: Build RPCS3 Qt UI (Legacy) - -defaults: - run: - shell: bash -on: - push: - paths-ignore: - - "**/*.md" - pull_request: - paths-ignore: - - "**/*.md" - workflow_dispatch: - -concurrency: - group: ${{ github.ref }}-${{ github.event_name }} - cancel-in-progress: false - -env: - BUILD_REPOSITORY_NAME: ${{ github.repository }} - BUILD_SOURCEBRANCHNAME: ${{ github.ref_name }} - BUILD_SOURCEVERSION: ${{ github.sha }} - BUILD_ARTIFACTSTAGINGDIRECTORY: ${{ github.workspace }}/artifacts/ - -jobs: - Linux_Build: - strategy: - fail-fast: false - matrix: - include: - - os: ubuntu-24.04 - build_sh: ".ci/build-linux.sh" - compiler: clang - - os: ubuntu-24.04 - build_sh: ".ci/build-linux.sh" - compiler: gcc - - os: ubuntu-24.04-arm - build_sh: ".ci/build-linux-aarch64.sh" - compiler: clang - name: RPCS3 Qt UI (Legacy) for Linux ${{ matrix.os }} ${{ matrix.compiler }} - runs-on: ${{ matrix.os }} - env: - CCACHE_DIR: ${{ github.workspace }}/ccache - CI_HAS_ARTIFACTS: true - DEPLOY_APPIMAGE: true - APPDIR: "./appdir" - ARTDIR: "./artifacts" - COMPILER: ${{ matrix.compiler }} - RX_VERSION: "Unknown" - RX_SHA: "Unknown" - steps: - - name: Checkout repository - uses: actions/checkout@main - with: - fetch-depth: 0 - - - name: Setup Cache - uses: actions/cache@main - with: - path: ${{ env.CCACHE_DIR }} - key: ${{ runner.os }}-ccache-${{ matrix.compiler }}-${{ runner.arch }}-${{github.run_id}} - restore-keys: | - ${{ runner.os }}-ccache-${{ matrix.compiler }}-${{ runner.arch }}- - - - name: Setup dependencies - run: | - echo "Types: deb" | sudo tee -a /etc/apt/sources.list.d/ubuntu.sources - echo "URIs: ${{ matrix.os == 'ubuntu-24.04-arm' && 'http://ports.ubuntu.com/ubuntu-ports' || 'http://azure.archive.ubuntu.com/ubuntu/' }}" | sudo tee -a /etc/apt/sources.list.d/ubuntu.sources - echo "Suites: plucky plucky-updates plucky-security" | sudo tee -a /etc/apt/sources.list.d/ubuntu.sources - echo "Components: main restricted universe multiverse" | sudo tee -a /etc/apt/sources.list.d/ubuntu.sources - echo "Signed-By: /usr/share/keyrings/ubuntu-archive-keyring.gpg" | sudo tee -a /etc/apt/sources.list.d/ubuntu.sources - - sudo apt update - sudo apt install -y cmake build-essential libunwind-dev \ - libvulkan-dev vulkan-validationlayers \ - libsox-dev ninja-build libasound2-dev libglfw3-dev nasm libudev-dev \ - libpulse-dev libopenal-dev libglew-dev zlib1g-dev libedit-dev \ - libevdev-dev libjack-dev libsndio-dev libglvnd-dev \ - qt6-base-dev qt6-svg-dev qt6-base-private-dev qt6-multimedia-dev \ - clang lld gcc-14 g++-14 \ - - - name: Build - run: | - ${{ matrix.build_sh }} - - RX_VERSION=`cat .rx.version | awk -F'-' '{print $1}'` - RX_SHA=`cat .rx.version | awk -F'-' '{print $5}'` - - echo "RX_VERSION=$RX_VERSION" >> "${{ github.env }}" - echo "RX_SHA=$RX_SHA" >> "${{ github.env }}" - mv RPCS3-Qt-UI.AppImage RPCS3-Qt-UI-Linux-${{ runner.arch }}-${{ matrix.compiler }}.AppImage - - - name: Upload artifacts - uses: actions/upload-artifact@main - with: - name: RPCS3 Qt UI (Legacy) for Linux (${{ runner.arch }}, ${{ matrix.compiler }}) - path: RPCS3-Qt-UI-Linux-${{ runner.arch }}-${{ matrix.compiler }}.AppImage - compression-level: 0 - - - name: Deploy build - uses: softprops/action-gh-release@v2 - if: | - github.event_name != 'pull_request' && - github.ref == 'refs/heads/master' && - github.repository == 'RPCSX/rpcsx' - with: - prerelease: false - make_latest: true - repository: RPCSX/rpcsx-build - token: ${{ secrets.BUILD_TOKEN }} - tag_name: v${{ env.RX_VERSION }}-${{ env.RX_SHA }} - files: RPCS3-Qt-UI-Linux-${{ runner.arch }}-${{ matrix.compiler }}.AppImage - body: ${{ github.server_url }}/${{ github.repository }}/commit/${{ github.sha }} - - - Windows_Build: - name: RPCS3 Qt UI (Legacy) for Windows - runs-on: windows-2025 - env: - COMPILER: msvc - QT_VER_MAIN: '6' - QT_VER: '6.8.3' - QT_VER_MSVC: 'msvc2022' - QT_DATE: '202503201308' - LLVM_VER: '19.1.7' - VULKAN_VER: '1.3.268.0' - VULKAN_SDK_SHA: '8459ef49bd06b697115ddd3d97c9aec729e849cd775f5be70897718a9b3b9db5' - CCACHE_SHA: '1f39f3ad5aae3fe915e99ad1302633bc8f6718e58fa7c0de2b0ba7e080f0f08c' - CCACHE_BIN_DIR: 'C:\ccache_bin' - CCACHE_DIR: 'C:\ccache' - CCACHE_INODECACHE: 'true' - CCACHE_SLOPPINESS: 'time_macros' - DEPS_CACHE_DIR: ./dependency_cache - RX_VERSION: 'Unknown' - RX_SHA: 'Unknown' - - steps: - - name: Checkout repository - uses: actions/checkout@main - with: - fetch-depth: 0 - - - name: Setup env - run: | - echo "QTDIR=C:\Qt\${{ env.QT_VER }}\${{ env.QT_VER_MSVC }}_64" >> ${{ github.env }} - echo "VULKAN_SDK=C:\VulkanSDK\${{ env.VULKAN_VER }}" >> ${{ github.env }} - - # - name: Get Cache Keys - # run: .ci/get_keys-windows.sh - - # - name: Setup Build Ccache - # uses: actions/cache@main - # with: - # path: ${{ env.CCACHE_DIR }} - # key: "${{ runner.os }}-ccache-${{ env.COMPILER }}-${{github.run_id}}" - # restore-keys: ${{ runner.os }}-ccache-${{ env.COMPILER }}- - - # - name: Setup Dependencies Cache - # uses: actions/cache@main - # with: - # path: ${{ env.DEPS_CACHE_DIR }} - # key: "${{ runner.os }}-${{ env.COMPILER }}-${{ env.QT_VER }}-${{ env.VULKAN_SDK_SHA }}-${{ env.CCACHE_SHA }}-${{ hashFiles('llvm.lock') }}-${{ hashFiles('glslang.lock') }}" - # restore-keys: ${{ runner.os }}-${{ env.COMPILER }}- - - - name: Download and unpack dependencies - run: .ci/setup-windows.sh - - - name: Export Variables - run: | - while IFS='=' read -r key val; do - # Skip lines that are empty or start with '#' - [[ -z "$key" || "$key" =~ ^# ]] && continue - echo "$key=$val" >> "${{ github.env }}" - done < .ci/ci-vars.env - - # - name: Add msbuild to PATH - # uses: microsoft/setup-msbuild@main - - - name: Configure - run: | - cmake -B build-msvc -DCMAKE_POLICY_VERSION_MINIMUM=3.5 -DWITH_RPCSX=off -DWITH_RPCS3=on -DWITH_RPCS3_QT_UI=on -DCMAKE_CONFIGURATION_TYPES="Debug;Release" -DCMAKE_INSTALL_PREFIX="${sourceDir}/out/install/${presetName}" -DUSE_NATIVE_INSTRUCTIONS=on -DUSE_PRECOMPILED_HEADERS=on -DUSE_FAUDIO=off -DUSE_SYSTEM_CURL=off -DUSE_SYSTEM_ZLIB=off -DUSE_SYSTEM_OPENAL=off -DUSE_SYSTEM_OPENCV=off -DBUILD_LLVM=on -DSTATIC_LINK_LLVM=on - - - name: Export Version - run: | - RX_VERSION=`cat .rx.version | awk -F'-' '{print $1}'` - RX_SHA=`cat .rx.version | awk -F'-' '{print $5}'` - echo "RX_VERSION=$RX_VERSION" >> "${{ github.env }}" - echo "RX_SHA=$RX_SHA" >> "${{ github.env }}" - - - name: Build - run: | - cmake --build build-msvc --config Release - - - - name: Pack up build artifacts - run: | - mkdir -p "${{ env.BUILD_ARTIFACTSTAGINGDIRECTORY }}" - .ci/deploy-windows.sh - mv RPCS3-Qt-UI.7z RPCS3-Qt-UI-Windows-${{ runner.arch }}-${{ env.COMPILER }}.7z - - - name: Upload artifacts (7z) - uses: actions/upload-artifact@main - with: - name: RPCS3 Qt UI (Legacy) for Windows (MSVC) - path: RPCS3-Qt-UI-Windows-${{ runner.arch }}-${{ env.COMPILER }}.7z - compression-level: 0 - if-no-files-found: error - - - name: Deploy build - uses: softprops/action-gh-release@v2 - if: | - github.event_name != 'pull_request' && - github.ref == 'refs/heads/master' && - github.repository == 'RPCSX/rpcsx' - with: - prerelease: false - make_latest: true - repository: RPCSX/rpcsx-build - token: ${{ secrets.BUILD_TOKEN }} - tag_name: v${{ env.RX_VERSION }}-${{ env.RX_SHA }} - files: RPCS3-Qt-UI-Windows-${{ runner.arch }}-${{ env.COMPILER }}.7z - body: ${{ github.server_url }}/${{ github.repository }}/commit/${{ github.sha }} diff --git a/CMakeLists.txt b/CMakeLists.txt index 6f9ced89d..443462e6a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -131,9 +131,9 @@ function(target_base_address target address) endfunction() -option(WITH_RPCSX "Enable RPCSX" ON) -option(WITH_RPCS3 "Enable RPCS3" OFF) -option(WITH_RPCS3_QT_UI "Enable RPCS3 UI" OFF) +option(WITH_PS3 "Enable PS3 emulation support" OFF) +option(WITH_PS4 "Enable PS4 emulation support" ON) + option(WITHOUT_OPENGL "Disable OpenGL" OFF) option(WITHOUT_OPENGLEW "Disable OpenGLEW" OFF) option(WITHOUT_OPENAL "Disable OpenAL" OFF) @@ -159,8 +159,7 @@ option(USE_SYSTEM_OPENCV "Prefer system OpenCV instead of the builtin one" ON) option(HAS_MEMORY_BREAKPOINTS "Add support for memory breakpoints to the interpreter" OFF) option(USE_LTO "Use LTO for building" ON) - -if (NOT WITH_RPCS3) +if (NOT WITH_PS3) set(WITHOUT_OPENGL on) set(WITHOUT_OPENGLEW on) set(WITHOUT_OPENAL on) @@ -252,6 +251,8 @@ add_subdirectory(3rdparty EXCLUDE_FROM_ALL) add_subdirectory(rx EXCLUDE_FROM_ALL) include(3rdparty/llvm/CMakeLists.txt) +include(ConfigureCompiler) +include(CheckFunctionExists) if (NOT RX_TAG) set(RX_TAG 0) @@ -266,7 +267,7 @@ target_compile_definitions(rx PRIVATE RX_TAG_VERSION=${RX_TAG_VERSION} ) -if (WITH_RPCSX) +if (WITH_PS4) find_package(nlohmann_json CONFIG) add_subdirectory(tools) add_subdirectory(orbis-kernel) @@ -274,14 +275,7 @@ endif() add_subdirectory(rpcsx) -if (WITH_RPCS3) - include(ConfigureCompiler) - include(CheckFunctionExists) - +if (WITH_PS3) add_subdirectory(rpcs3) add_subdirectory(ps3fw) endif() - -if (NOT ANDROID AND WITH_RPCS3_QT_UI AND WITH_RPCS3) - add_subdirectory(rpcs3qt-legacy) -endif() diff --git a/android/CMakeLists.txt b/android/CMakeLists.txt index 9cd61d123..b834c0fe7 100644 --- a/android/CMakeLists.txt +++ b/android/CMakeLists.txt @@ -88,8 +88,8 @@ target_link_libraries(3rdparty_ffmpeg INTERFACE add_dependencies(3rdparty_ffmpeg ffmpeg-unpack) -set(WITH_RPCSX off) -set(WITH_RPCS3 on) +set(WITH_PS4 off) +set(WITH_PS3 on) set(USE_SYSTEM_LIBUSB off) set(USE_SYSTEM_CURL off) set(USE_DISCORD_RPC off) @@ -113,8 +113,6 @@ add_library(${CMAKE_PROJECT_NAME} SHARED src/rpcsx-android.cpp ) -target_include_directories(${CMAKE_PROJECT_NAME} PUBLIC rpcs3/rpcs3) - target_link_libraries(${CMAKE_PROJECT_NAME} rpcs3 rpcsx::fw::ps3 diff --git a/rpcs3qt-legacy/.clang-format b/rpcs3qt-legacy/.clang-format deleted file mode 100644 index 08b2119b3..000000000 --- a/rpcs3qt-legacy/.clang-format +++ /dev/null @@ -1,32 +0,0 @@ -Standard: c++20 -UseTab: AlignWithSpaces -TabWidth: 4 -IndentWidth: 4 -AccessModifierOffset: -4 -PointerAlignment: Left -NamespaceIndentation: All -ColumnLimit: 0 -BreakBeforeBraces: Allman -BreakConstructorInitializers: BeforeColon -BreakBeforeBinaryOperators: None -BreakBeforeTernaryOperators: false -AlwaysBreakTemplateDeclarations: Yes -AllowShortIfStatementsOnASingleLine: Never -AllowShortBlocksOnASingleLine: Never -AllowShortCaseLabelsOnASingleLine: true -AllowShortFunctionsOnASingleLine: Empty -AllowShortLoopsOnASingleLine: false -AllowShortLambdasOnASingleLine: Empty -Cpp11BracedListStyle: true -IndentCaseLabels: false -SortIncludes: false -ReflowComments: true -AlignConsecutiveAssignments: false -AlignTrailingComments: true -AlignAfterOpenBracket: DontAlign -ConstructorInitializerAllOnOneLineOrOnePerLine: false -BinPackArguments: true -BinPackParameters: true -AlwaysBreakAfterReturnType: None -KeepEmptyLinesAtTheStartOfBlocks: true -IndentWrappedFunctionNames: false diff --git a/rpcs3qt-legacy/CMakeLists.txt b/rpcs3qt-legacy/CMakeLists.txt deleted file mode 100644 index f6b868a3e..000000000 --- a/rpcs3qt-legacy/CMakeLists.txt +++ /dev/null @@ -1,281 +0,0 @@ -include(qt6.cmake) - -add_library(rpcs3_ui STATIC - about_dialog.cpp - auto_pause_settings_dialog.cpp - basic_mouse_settings_dialog.cpp - breakpoint_handler.cpp - breakpoint_list.cpp - call_stack_list.cpp - camera_settings_dialog.cpp - cg_disasm_window.cpp - cheat_manager.cpp - config_adapter.cpp - config_checker.cpp - curl_handle.cpp - custom_dialog.cpp - custom_table_widget_item.cpp - debugger_add_bp_window.cpp - debugger_frame.cpp - debugger_list.cpp - downloader.cpp - dimensions_dialog.cpp - _discord_utils.cpp - emu_settings.cpp - elf_memory_dumping_dialog.cpp - emulated_pad_settings_dialog.cpp - fatal_error_dialog.cpp - find_dialog.cpp - flow_layout.cpp - flow_widget.cpp - flow_widget_item.cpp - game_compatibility.cpp - game_list.cpp - game_list_base.cpp - game_list_delegate.cpp - game_list_frame.cpp - game_list_grid.cpp - game_list_grid_item.cpp - game_list_table.cpp - gui_application.cpp - gl_gs_frame.cpp - gs_frame.cpp - gui_game_info.cpp - gui_settings.cpp - infinity_dialog.cpp - input_dialog.cpp - instruction_editor_dialog.cpp - ipc_settings_dialog.cpp - kernel_explorer.cpp - localized.cpp - localized_emu.cpp - log_frame.cpp - log_viewer.cpp - main_window.cpp - memory_string_searcher.cpp - memory_viewer_panel.cpp - microphone_creator.cpp - midi_creator.cpp - movie_item.cpp - movie_item_base.cpp - msg_dialog_frame.cpp - osk_dialog_frame.cpp - pad_led_settings_dialog.cpp - pad_motion_settings_dialog.cpp - pad_settings_dialog.cpp - patch_creator_dialog.cpp - patch_manager_dialog.cpp - permissions.cpp - persistent_settings.cpp - pkg_install_dialog.cpp - progress_dialog.cpp - progress_indicator.cpp - ps_move_tracker_dialog.cpp - qt_camera_handler.cpp - qt_camera_video_sink.cpp - qt_music_handler.cpp - qt_utils.cpp - qt_video_source.cpp - raw_mouse_settings_dialog.cpp - register_editor_dialog.cpp - recvmessage_dialog_frame.cpp - render_creator.cpp - rpcn_settings_dialog.cpp - rsx_debugger.cpp - save_data_dialog.cpp - save_data_info_dialog.cpp - save_data_list_dialog.cpp - save_manager_dialog.cpp - savestate_manager_dialog.cpp - screenshot_item.cpp - screenshot_manager_dialog.cpp - screenshot_preview.cpp - sendmessage_dialog_frame.cpp - settings.cpp - settings_dialog.cpp - shortcut_utils.cpp - shortcut_dialog.cpp - shortcut_handler.cpp - shortcut_settings.cpp - skylander_dialog.cpp - syntax_highlighter.cpp - system_cmd_dialog.cpp - table_item_delegate.cpp - tooltips.cpp - trophy_manager_dialog.cpp - trophy_notification_frame.cpp - trophy_notification_helper.cpp - update_manager.cpp - user_account.cpp - user_manager_dialog.cpp - uuid.cpp - vfs_dialog.cpp - vfs_dialog_path_widget.cpp - vfs_dialog_tab.cpp - vfs_dialog_usb_input.cpp - vfs_dialog_usb_tab.cpp - vfs_tool_dialog.cpp - video_label.cpp - welcome_dialog.cpp - - about_dialog.ui - camera_settings_dialog.ui - main_window.ui - pad_led_settings_dialog.ui - pad_motion_settings_dialog.ui - pad_settings_dialog.ui - patch_creator_dialog.ui - patch_manager_dialog.ui - ps_move_tracker_dialog.ui - settings_dialog.ui - shortcut_dialog.ui - welcome_dialog.ui - - resources.qrc -) - -if(HAS_MEMORY_BREAKPOINTS) - target_compile_definitions(rpcs3_ui PRIVATE RPCS3_HAS_MEMORY_BREAKPOINTS) -endif() - -if(WIN32) - target_sources(rpcs3_ui PUBLIC windows.qrc) - target_compile_definitions(rpcs3_ui PRIVATE UNICODE _UNICODE) -endif() - -set_target_properties(rpcs3_ui - PROPERTIES - AUTOMOC ON - AUTOUIC ON - AUTORCC ON) - -# AUTOMOC brings Windows.h to the sources, which have some definitions conflicting with winsock2.h -# define WIN32_LEAN_AND_MEAN resolve the problem -# https://docs.microsoft.com/en-us/windows/win32/winsock/creating-a-basic-winsock-application -# https://docs.microsoft.com/en-us/windows/win32/winprog/using-the-windows-headers#faster-builds-with-smaller-header-files -target_compile_definitions(rpcs3_ui PUBLIC WIN32_LEAN_AND_MEAN NOMINMAX) - -target_link_libraries(rpcs3_ui - PUBLIC - 3rdparty::qt6 - 3rdparty::yaml-cpp - rpcs3 - rpcsx::fw::ps3::api -) - -if(TARGET OpenGL::EGL) - target_link_libraries(rpcs3_ui PUBLIC OpenGL::EGL) -endif() - - -if (NOT ANDROID) - if(WIN32) - add_executable(rpcs3qt-ui-legacy WIN32) - target_sources(rpcs3qt-ui-legacy PRIVATE rpcs3.rc) - target_compile_definitions(rpcs3qt-ui-legacy PUBLIC WIN32_LEAN_AND_MEAN NOMINMAX UNICODE _UNICODE) - elseif(APPLE) - add_executable(rpcs3qt-ui-legacy MACOSX_BUNDLE) - target_sources(rpcs3qt-ui-legacy PRIVATE rpcs3.icns update_helper.sh) - set_source_files_properties(update_helper.sh PROPERTIES MACOSX_PACKAGE_LOCATION Resources) - set_target_properties(rpcs3qt-ui-legacy - PROPERTIES - MACOSX_BUNDLE_INFO_PLIST "${CMAKE_CURRENT_SOURCE_DIR}/rpcs3.plist.in") - else() - add_executable(rpcs3qt-ui-legacy) - endif() - - set_target_properties(rpcs3qt-ui-legacy PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) - - target_sources(rpcs3qt-ui-legacy - PRIVATE - display_sleep_control.cpp - headless_application.cpp - main.cpp - main_application.cpp - - Input/basic_keyboard_handler.cpp - Input/basic_mouse_handler.cpp - Input/gui_pad_thread.cpp - Input/keyboard_pad_handler.cpp - ) - - set_target_properties(rpcs3qt-ui-legacy - PROPERTIES - AUTOMOC ON - AUTOUIC ON) - - target_link_libraries(rpcs3qt-ui-legacy - PRIVATE - rpcs3_emu - rpcs3_ui - 3rdparty::qt6 - rpcsx::fw::ps3 - rpcsx::fw::ps3::api) - - if(UNIX) - set(CMAKE_THREAD_PREFER_PTHREAD TRUE) - find_package(Threads REQUIRED) - target_link_libraries(rpcs3qt-ui-legacy PRIVATE Threads::Threads) - endif() - - if(WIN32) - target_link_libraries(rpcs3qt-ui-legacy PRIVATE bcrypt ws2_32 Iphlpapi Winmm Psapi gdi32 setupapi pdh) - else() - target_link_libraries(rpcs3qt-ui-legacy PRIVATE ${CMAKE_DL_LIBS}) - endif() - - # Copy icons to executable directory - if(APPLE) - if (CMAKE_BUILD_TYPE MATCHES "Debug" OR CMAKE_BUILD_TYPE MATCHES "RelWithDebInfo") - set(QT_DEPLOY_FLAGS "-no-strip") - else() - set(QT_DEPLOY_FLAGS "") - endif() - qt_finalize_target(rpcs3qt-ui-legacy) - add_custom_command(TARGET rpcs3qt-ui-legacy POST_BUILD - COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/rpcs3.icns $/../Resources/rpcs3.icns - COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_SOURCE_DIR}/rpcs3/bin/Icons $/../Resources/Icons - COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_SOURCE_DIR}/rpcs3/bin/GuiConfigs $/../Resources/GuiConfigs - COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_SOURCE_DIR}/rpcs3/bin/git $/../Resources/git - COMMAND "${MACDEPLOYQT_EXECUTABLE}" "${PROJECT_BINARY_DIR}/bin/rpcs3.app" "${QT_DEPLOY_FLAGS}") - elseif(UNIX) - add_custom_command(TARGET rpcs3qt-ui-legacy POST_BUILD - COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_SOURCE_DIR}/rpcs3/bin/Icons $/Icons - COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_SOURCE_DIR}/rpcs3/bin/GuiConfigs $/GuiConfigs - COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_SOURCE_DIR}/rpcs3/bin/git $/git) - elseif(WIN32) - add_custom_command(TARGET rpcs3qt-ui-legacy POST_BUILD - COMMAND ${CMAKE_COMMAND} -E copy $ $ - COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_SOURCE_DIR}/rpcs3/bin/Icons $/Icons - COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_SOURCE_DIR}/rpcs3/bin/GuiConfigs $/GuiConfigs - COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_SOURCE_DIR}/rpcs3/bin/git $/git - COMMAND "${WINDEPLOYQT_EXECUTABLE}" --no-compiler-runtime --no-opengl-sw --no-patchqt - --no-translations --no-system-d3d-compiler --no-system-dxc-compiler --no-ffmpeg --no-quick-import - --plugindir "$,$/plugins,$/share/qt6/plugins>" - --verbose 0 "$") - endif() - - # Unix installation - if(UNIX AND NOT APPLE) - # Install the binary - install(TARGETS rpcs3qt-ui-legacy RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) - # Install the application icon and menu item - install(FILES rpcs3.svg - DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/icons/hicolor/scalable/apps) - install(FILES rpcs3.png - DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/icons/hicolor/48x48/apps) - install(FILES rpcs3.desktop - DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/applications) - install(FILES rpcs3.metainfo.xml - DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/metainfo) - # Install other files - install(DIRECTORY ${CMAKE_SOURCE_DIR}/rpcs3/bin/Icons - DESTINATION ${CMAKE_INSTALL_DATADIR}/rpcs3) - install(DIRECTORY ${CMAKE_SOURCE_DIR}/rpcs3/bin/GuiConfigs - DESTINATION ${CMAKE_INSTALL_DATADIR}/rpcs3) - install(DIRECTORY ${CMAKE_SOURCE_DIR}/rpcs3/bin/git - DESTINATION ${CMAKE_INSTALL_DATADIR}/rpcs3) - install(DIRECTORY ${CMAKE_SOURCE_DIR}/rpcs3/bin/test - DESTINATION ${CMAKE_INSTALL_DATADIR}/rpcs3) - endif() -endif() diff --git a/rpcs3qt-legacy/Icons/DualShock_3.svg b/rpcs3qt-legacy/Icons/DualShock_3.svg deleted file mode 100644 index 4e66fe993..000000000 --- a/rpcs3qt-legacy/Icons/DualShock_3.svg +++ /dev/null @@ -1,36 +0,0 @@ - - - - - - - - - -SELECT - -START - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/rpcs3qt-legacy/Icons/combo_config_bordered.png b/rpcs3qt-legacy/Icons/combo_config_bordered.png deleted file mode 100644 index e7c8b1b84..000000000 Binary files a/rpcs3qt-legacy/Icons/combo_config_bordered.png and /dev/null differ diff --git a/rpcs3qt-legacy/Icons/configure.png b/rpcs3qt-legacy/Icons/configure.png deleted file mode 100644 index c023e773d..000000000 Binary files a/rpcs3qt-legacy/Icons/configure.png and /dev/null differ diff --git a/rpcs3qt-legacy/Icons/controllers.png b/rpcs3qt-legacy/Icons/controllers.png deleted file mode 100644 index af31011fb..000000000 Binary files a/rpcs3qt-legacy/Icons/controllers.png and /dev/null differ diff --git a/rpcs3qt-legacy/Icons/custom_config.png b/rpcs3qt-legacy/Icons/custom_config.png deleted file mode 100644 index 1dc0cf039..000000000 Binary files a/rpcs3qt-legacy/Icons/custom_config.png and /dev/null differ diff --git a/rpcs3qt-legacy/Icons/exit_fullscreen.png b/rpcs3qt-legacy/Icons/exit_fullscreen.png deleted file mode 100644 index d2f5a4607..000000000 Binary files a/rpcs3qt-legacy/Icons/exit_fullscreen.png and /dev/null differ diff --git a/rpcs3qt-legacy/Icons/fullscreen.png b/rpcs3qt-legacy/Icons/fullscreen.png deleted file mode 100644 index f75326efa..000000000 Binary files a/rpcs3qt-legacy/Icons/fullscreen.png and /dev/null differ diff --git a/rpcs3qt-legacy/Icons/grid.png b/rpcs3qt-legacy/Icons/grid.png deleted file mode 100644 index 128e32adf..000000000 Binary files a/rpcs3qt-legacy/Icons/grid.png and /dev/null differ diff --git a/rpcs3qt-legacy/Icons/list.png b/rpcs3qt-legacy/Icons/list.png deleted file mode 100644 index 059afae88..000000000 Binary files a/rpcs3qt-legacy/Icons/list.png and /dev/null differ diff --git a/rpcs3qt-legacy/Icons/open.png b/rpcs3qt-legacy/Icons/open.png deleted file mode 100644 index 354171345..000000000 Binary files a/rpcs3qt-legacy/Icons/open.png and /dev/null differ diff --git a/rpcs3qt-legacy/Icons/pause.png b/rpcs3qt-legacy/Icons/pause.png deleted file mode 100644 index e5a22b88c..000000000 Binary files a/rpcs3qt-legacy/Icons/pause.png and /dev/null differ diff --git a/rpcs3qt-legacy/Icons/play.png b/rpcs3qt-legacy/Icons/play.png deleted file mode 100644 index 8675cd3b6..000000000 Binary files a/rpcs3qt-legacy/Icons/play.png and /dev/null differ diff --git a/rpcs3qt-legacy/Icons/refresh.png b/rpcs3qt-legacy/Icons/refresh.png deleted file mode 100644 index 425e147ec..000000000 Binary files a/rpcs3qt-legacy/Icons/refresh.png and /dev/null differ diff --git a/rpcs3qt-legacy/Icons/restart.png b/rpcs3qt-legacy/Icons/restart.png deleted file mode 100644 index 6c231205e..000000000 Binary files a/rpcs3qt-legacy/Icons/restart.png and /dev/null differ diff --git a/rpcs3qt-legacy/Icons/stop.png b/rpcs3qt-legacy/Icons/stop.png deleted file mode 100644 index e9ed61ea2..000000000 Binary files a/rpcs3qt-legacy/Icons/stop.png and /dev/null differ diff --git a/rpcs3qt-legacy/Input/basic_keyboard_handler.cpp b/rpcs3qt-legacy/Input/basic_keyboard_handler.cpp deleted file mode 100644 index 8a7a5773c..000000000 --- a/rpcs3qt-legacy/Input/basic_keyboard_handler.cpp +++ /dev/null @@ -1,347 +0,0 @@ -#include "basic_keyboard_handler.h" - -#include - -#include "Emu/system_config.h" -#include "Emu/Io/interception.h" - -#ifdef _WIN32 -#include "windows.h" -#endif - -LOG_CHANNEL(input_log, "Input"); - -void basic_keyboard_handler::Init(keyboard_consumer& consumer, const u32 max_connect) -{ - KbInfo& info = consumer.GetInfo(); - std::vector& keyboards = consumer.GetKeyboards(); - - info = {}; - keyboards.clear(); - - for (u32 i = 0; i < max_connect; i++) - { - Keyboard kb{}; - kb.m_config.arrange = g_cfg.sys.keyboard_type; - - if (consumer.id() == keyboard_consumer::identifier::overlays) - { - // Enable key repeat - kb.m_key_repeat = true; - } - - LoadSettings(kb); - - keyboards.emplace_back(kb); - } - - info.max_connect = max_connect; - info.now_connect = std::min(::size32(keyboards), max_connect); - info.info = input::g_keyboards_intercepted ? CELL_KB_INFO_INTERCEPTED : 0; // Ownership of keyboard data: 0=Application, 1=System - info.status[0] = CELL_KB_STATUS_CONNECTED; // (TODO: Support for more keyboards) -} - -/* Sets the target window for the event handler, and also installs an event filter on the target. */ -void basic_keyboard_handler::SetTargetWindow(QWindow* target) -{ - if (target != nullptr) - { - m_target = target; - target->installEventFilter(this); - } - else - { - // If this is hit, it probably means that some refactoring occurs because currently a gsframe is created in Load. - // We still want events so filter from application instead since target is null. - QApplication::instance()->installEventFilter(this); - input_log.error("Trying to set keyboard handler to a null target window."); - } -} - -bool basic_keyboard_handler::eventFilter(QObject* watched, QEvent* event) -{ - if (!event) [[unlikely]] - { - return false; - } - - if (input::g_active_mouse_and_keyboard != input::active_mouse_and_keyboard::emulated) - { - if (!m_keys_released) - { - ReleaseAllKeys(); - } - return false; - } - - m_keys_released = false; - - // !m_target is for future proofing when gsrender isn't automatically initialized on load. - // !m_target->isVisible() is a hack since currently a guiless application will STILL inititialize a gsrender (providing a valid target) - if (!m_target || !m_target->isVisible() || watched == m_target) - { - switch (event->type()) - { - case QEvent::KeyPress: - { - keyPressEvent(static_cast(event)); - break; - } - case QEvent::KeyRelease: - { - keyReleaseEvent(static_cast(event)); - break; - } - case QEvent::FocusOut: - { - ReleaseAllKeys(); - break; - } - default: - { - break; - } - } - } - return false; -} - -void basic_keyboard_handler::keyPressEvent(QKeyEvent* keyEvent) -{ - if (!keyEvent) [[unlikely]] - { - return; - } - - if (m_consumers.empty()) - { - keyEvent->ignore(); - return; - } - - const int key = getUnmodifiedKey(keyEvent); - - if (key < 0 || !HandleKey(static_cast(key), keyEvent->nativeScanCode(), true, keyEvent->isAutoRepeat(), keyEvent->text().toStdU32String())) - { - keyEvent->ignore(); - } -} - -void basic_keyboard_handler::keyReleaseEvent(QKeyEvent* keyEvent) -{ - if (!keyEvent) [[unlikely]] - { - return; - } - - if (m_consumers.empty()) - { - keyEvent->ignore(); - return; - } - - const int key = getUnmodifiedKey(keyEvent); - - if (key < 0 || !HandleKey(static_cast(key), keyEvent->nativeScanCode(), false, keyEvent->isAutoRepeat(), keyEvent->text().toStdU32String())) - { - keyEvent->ignore(); - } -} - -// This should get the actual unmodified key without getting too crazy. -// key() only shows the modifiers and the modified key (e.g. no easy way of knowing that - was pressed in 'SHIFT+-' in order to get _) -s32 basic_keyboard_handler::getUnmodifiedKey(QKeyEvent* keyEvent) -{ - if (!keyEvent) [[unlikely]] - { - return -1; - } - - const int key = keyEvent->key(); - - if (key < 0) - { - return key; - } - - u32 raw_key = static_cast(key); - -#ifdef _WIN32 - if (keyEvent->modifiers() != Qt::NoModifier && !keyEvent->text().isEmpty()) - { - u32 mapped_key = static_cast(MapVirtualKeyA(static_cast(keyEvent->nativeVirtualKey()), MAPVK_VK_TO_CHAR)); - - if (raw_key != mapped_key) - { - if (mapped_key > 0x80000000) // diacritics - { - mapped_key -= 0x80000000; - } - raw_key = mapped_key; - } - } -#endif - - return static_cast(raw_key); -} - -void basic_keyboard_handler::LoadSettings(Keyboard& keyboard) -{ - std::vector buttons; - - // Meta Keys - buttons.emplace_back(Qt::Key_Control, CELL_KB_MKEY_L_CTRL); - buttons.emplace_back(Qt::Key_Shift, CELL_KB_MKEY_L_SHIFT); - buttons.emplace_back(Qt::Key_Alt, CELL_KB_MKEY_L_ALT); - buttons.emplace_back(Qt::Key_Meta, CELL_KB_MKEY_L_WIN); - // buttons.emplace_back(, CELL_KB_MKEY_R_CTRL); // There is no way to know if it's left or right in Qt at the moment - // buttons.emplace_back(, CELL_KB_MKEY_R_SHIFT); // There is no way to know if it's left or right in Qt at the moment - // buttons.emplace_back(, CELL_KB_MKEY_R_ALT); // There is no way to know if it's left or right in Qt at the moment - // buttons.emplace_back(, CELL_KB_MKEY_R_WIN); // There is no way to know if it's left or right in Qt at the moment - - buttons.emplace_back(Qt::Key_Super_L, CELL_KB_MKEY_L_WIN); // The super keys are supposed to be the windows keys, but they trigger the meta key instead. Let's assign the windows keys to both. - buttons.emplace_back(Qt::Key_Super_R, CELL_KB_MKEY_R_WIN); // The super keys are supposed to be the windows keys, but they trigger the meta key instead. Let's assign the windows keys to both. - - // CELL_KB_RAWDAT - // buttons.emplace_back(, CELL_KEYC_NO_EVENT); // Redundant, listed for completeness - // buttons.emplace_back(, CELL_KEYC_E_ROLLOVER); - // buttons.emplace_back(, CELL_KEYC_E_POSTFAIL); - // buttons.emplace_back(, CELL_KEYC_E_UNDEF); - buttons.emplace_back(Qt::Key_Escape, CELL_KEYC_ESCAPE); - buttons.emplace_back(Qt::Key_Kanji, CELL_KEYC_106_KANJI); - buttons.emplace_back(Qt::Key_CapsLock, CELL_KEYC_CAPS_LOCK); - buttons.emplace_back(Qt::Key_F1, CELL_KEYC_F1); - buttons.emplace_back(Qt::Key_F2, CELL_KEYC_F2); - buttons.emplace_back(Qt::Key_F3, CELL_KEYC_F3); - buttons.emplace_back(Qt::Key_F4, CELL_KEYC_F4); - buttons.emplace_back(Qt::Key_F5, CELL_KEYC_F5); - buttons.emplace_back(Qt::Key_F6, CELL_KEYC_F6); - buttons.emplace_back(Qt::Key_F7, CELL_KEYC_F7); - buttons.emplace_back(Qt::Key_F8, CELL_KEYC_F8); - buttons.emplace_back(Qt::Key_F9, CELL_KEYC_F9); - buttons.emplace_back(Qt::Key_F10, CELL_KEYC_F10); - buttons.emplace_back(Qt::Key_F11, CELL_KEYC_F11); - buttons.emplace_back(Qt::Key_F12, CELL_KEYC_F12); - buttons.emplace_back(Qt::Key_Print, CELL_KEYC_PRINTSCREEN); - buttons.emplace_back(Qt::Key_ScrollLock, CELL_KEYC_SCROLL_LOCK); - buttons.emplace_back(Qt::Key_Pause, CELL_KEYC_PAUSE); - buttons.emplace_back(Qt::Key_Insert, CELL_KEYC_INSERT); - buttons.emplace_back(Qt::Key_Home, CELL_KEYC_HOME); - buttons.emplace_back(Qt::Key_PageUp, CELL_KEYC_PAGE_UP); - buttons.emplace_back(Qt::Key_Delete, CELL_KEYC_DELETE); - buttons.emplace_back(Qt::Key_End, CELL_KEYC_END); - buttons.emplace_back(Qt::Key_PageDown, CELL_KEYC_PAGE_DOWN); - buttons.emplace_back(Qt::Key_Right, CELL_KEYC_RIGHT_ARROW); - buttons.emplace_back(Qt::Key_Left, CELL_KEYC_LEFT_ARROW); - buttons.emplace_back(Qt::Key_Down, CELL_KEYC_DOWN_ARROW); - buttons.emplace_back(Qt::Key_Up, CELL_KEYC_UP_ARROW); - // buttons.emplace_back(, CELL_KEYC_NUM_LOCK); - // buttons.emplace_back(, CELL_KEYC_APPLICATION); // This is probably the PS key on the PS3 keyboard - buttons.emplace_back(Qt::Key_Kana_Shift, CELL_KEYC_KANA); // maybe Key_Kana_Lock - buttons.emplace_back(Qt::Key_Henkan, CELL_KEYC_HENKAN); - buttons.emplace_back(Qt::Key_Muhenkan, CELL_KEYC_MUHENKAN); - - // CELL_KB_KEYPAD - buttons.emplace_back(Qt::Key_NumLock, CELL_KEYC_KPAD_NUMLOCK); - buttons.emplace_back(Qt::Key_division, CELL_KEYC_KPAD_SLASH); // should ideally be slash but that's occupied obviously - buttons.emplace_back(Qt::Key_multiply, CELL_KEYC_KPAD_ASTERISK); // should ideally be asterisk but that's occupied obviously - // buttons.emplace_back(Qt::Key_Minus, CELL_KEYC_KPAD_MINUS); // should ideally be minus but that's occupied obviously - buttons.emplace_back(Qt::Key_Plus, CELL_KEYC_KPAD_PLUS); - buttons.emplace_back(Qt::Key_Enter, CELL_KEYC_KPAD_ENTER); - // buttons.emplace_back(Qt::Key_1, CELL_KEYC_KPAD_1); - // buttons.emplace_back(Qt::Key_2, CELL_KEYC_KPAD_2); - // buttons.emplace_back(Qt::Key_3, CELL_KEYC_KPAD_3); - // buttons.emplace_back(Qt::Key_4, CELL_KEYC_KPAD_4); - // buttons.emplace_back(Qt::Key_5, CELL_KEYC_KPAD_5); - // buttons.emplace_back(Qt::Key_6, CELL_KEYC_KPAD_6); - // buttons.emplace_back(Qt::Key_7, CELL_KEYC_KPAD_7); - // buttons.emplace_back(Qt::Key_8, CELL_KEYC_KPAD_8); - // buttons.emplace_back(Qt::Key_9, CELL_KEYC_KPAD_9); - // buttons.emplace_back(Qt::Key_0, CELL_KEYC_KPAD_0); - // buttons.emplace_back(Qt::Key_Delete, CELL_KEYC_KPAD_PERIOD); - - // ASCII Printable characters - buttons.emplace_back(Qt::Key_A, CELL_KEYC_A); - buttons.emplace_back(Qt::Key_B, CELL_KEYC_B); - buttons.emplace_back(Qt::Key_C, CELL_KEYC_C); - buttons.emplace_back(Qt::Key_D, CELL_KEYC_D); - buttons.emplace_back(Qt::Key_E, CELL_KEYC_E); - buttons.emplace_back(Qt::Key_F, CELL_KEYC_F); - buttons.emplace_back(Qt::Key_G, CELL_KEYC_G); - buttons.emplace_back(Qt::Key_H, CELL_KEYC_H); - buttons.emplace_back(Qt::Key_I, CELL_KEYC_I); - buttons.emplace_back(Qt::Key_J, CELL_KEYC_J); - buttons.emplace_back(Qt::Key_K, CELL_KEYC_K); - buttons.emplace_back(Qt::Key_L, CELL_KEYC_L); - buttons.emplace_back(Qt::Key_M, CELL_KEYC_M); - buttons.emplace_back(Qt::Key_N, CELL_KEYC_N); - buttons.emplace_back(Qt::Key_O, CELL_KEYC_O); - buttons.emplace_back(Qt::Key_P, CELL_KEYC_P); - buttons.emplace_back(Qt::Key_Q, CELL_KEYC_Q); - buttons.emplace_back(Qt::Key_R, CELL_KEYC_R); - buttons.emplace_back(Qt::Key_S, CELL_KEYC_S); - buttons.emplace_back(Qt::Key_T, CELL_KEYC_T); - buttons.emplace_back(Qt::Key_U, CELL_KEYC_U); - buttons.emplace_back(Qt::Key_V, CELL_KEYC_V); - buttons.emplace_back(Qt::Key_W, CELL_KEYC_W); - buttons.emplace_back(Qt::Key_X, CELL_KEYC_X); - buttons.emplace_back(Qt::Key_Y, CELL_KEYC_Y); - buttons.emplace_back(Qt::Key_Z, CELL_KEYC_Z); - - buttons.emplace_back(Qt::Key_1, CELL_KEYC_1); - buttons.emplace_back(Qt::Key_2, CELL_KEYC_2); - buttons.emplace_back(Qt::Key_3, CELL_KEYC_3); - buttons.emplace_back(Qt::Key_4, CELL_KEYC_4); - buttons.emplace_back(Qt::Key_5, CELL_KEYC_5); - buttons.emplace_back(Qt::Key_6, CELL_KEYC_6); - buttons.emplace_back(Qt::Key_7, CELL_KEYC_7); - buttons.emplace_back(Qt::Key_8, CELL_KEYC_8); - buttons.emplace_back(Qt::Key_9, CELL_KEYC_9); - buttons.emplace_back(Qt::Key_0, CELL_KEYC_0); - - buttons.emplace_back(Qt::Key_Return, CELL_KEYC_ENTER); - buttons.emplace_back(Qt::Key_Backspace, CELL_KEYC_BS); - buttons.emplace_back(Qt::Key_Tab, CELL_KEYC_TAB); - buttons.emplace_back(Qt::Key_Space, CELL_KEYC_SPACE); - buttons.emplace_back(Qt::Key_Minus, CELL_KEYC_MINUS); - buttons.emplace_back(Qt::Key_Equal, CELL_KEYC_EQUAL_101); - buttons.emplace_back(Qt::Key_AsciiCircum, CELL_KEYC_ACCENT_CIRCONFLEX_106); - buttons.emplace_back(Qt::Key_At, CELL_KEYC_ATMARK_106); - buttons.emplace_back(Qt::Key_Semicolon, CELL_KEYC_SEMICOLON); - buttons.emplace_back(Qt::Key_Apostrophe, CELL_KEYC_QUOTATION_101); - buttons.emplace_back(Qt::Key_Colon, CELL_KEYC_COLON_106); - buttons.emplace_back(Qt::Key_Comma, CELL_KEYC_COMMA); - buttons.emplace_back(Qt::Key_Period, CELL_KEYC_PERIOD); - buttons.emplace_back(Qt::Key_Slash, CELL_KEYC_SLASH); - buttons.emplace_back(Qt::Key_yen, CELL_KEYC_YEN_106); - - // Some buttons share the same key code on different layouts - if (keyboard.m_config.arrange == CELL_KB_MAPPING_106) - { - buttons.emplace_back(Qt::Key_Backslash, CELL_KEYC_BACKSLASH_106); - buttons.emplace_back(Qt::Key_BracketLeft, CELL_KEYC_LEFT_BRACKET_106); - buttons.emplace_back(Qt::Key_BracketRight, CELL_KEYC_RIGHT_BRACKET_106); - } - else - { - buttons.emplace_back(Qt::Key_Backslash, CELL_KEYC_BACKSLASH_101); - buttons.emplace_back(Qt::Key_BracketLeft, CELL_KEYC_LEFT_BRACKET_101); - buttons.emplace_back(Qt::Key_BracketRight, CELL_KEYC_RIGHT_BRACKET_101); - } - - // Made up helper buttons - buttons.emplace_back(Qt::Key_Less, CELL_KEYC_LESS); - buttons.emplace_back(Qt::Key_NumberSign, CELL_KEYC_HASHTAG); - buttons.emplace_back(Qt::Key_ssharp, CELL_KEYC_SSHARP); - buttons.emplace_back(Qt::Key_QuoteLeft, CELL_KEYC_BACK_QUOTE); - buttons.emplace_back(Qt::Key_acute, CELL_KEYC_BACK_QUOTE); - - // Fill our map - for (const KbButton& button : buttons) - { - if (!keyboard.m_keys.try_emplace(button.m_keyCode, button).second) - { - input_log.error("basic_keyboard_handler failed to set key code %d", button.m_keyCode); - } - } -} diff --git a/rpcs3qt-legacy/Input/basic_keyboard_handler.h b/rpcs3qt-legacy/Input/basic_keyboard_handler.h deleted file mode 100644 index 284890025..000000000 --- a/rpcs3qt-legacy/Input/basic_keyboard_handler.h +++ /dev/null @@ -1,26 +0,0 @@ -#pragma once - -#include "util/types.hpp" -#include "Emu/Io/KeyboardHandler.h" - -#include -#include - -class basic_keyboard_handler final : public KeyboardHandlerBase, public QObject -{ - using KeyboardHandlerBase::KeyboardHandlerBase; - -public: - void Init(keyboard_consumer& consumer, const u32 max_connect) override; - - void SetTargetWindow(QWindow* target); - bool eventFilter(QObject* watched, QEvent* event) override; - void keyPressEvent(QKeyEvent* event); - void keyReleaseEvent(QKeyEvent* event); - static s32 getUnmodifiedKey(QKeyEvent* event); - -private: - void LoadSettings(Keyboard& keyboard); - - QWindow* m_target = nullptr; -}; diff --git a/rpcs3qt-legacy/Input/basic_mouse_handler.cpp b/rpcs3qt-legacy/Input/basic_mouse_handler.cpp deleted file mode 100644 index da0486260..000000000 --- a/rpcs3qt-legacy/Input/basic_mouse_handler.cpp +++ /dev/null @@ -1,280 +0,0 @@ -#include -#include - -#include "util/types.hpp" -#include "util/logs.hpp" - -#include "basic_mouse_handler.h" -#include "keyboard_pad_handler.h" -#include "../gs_frame.h" -#include "Emu/Io/interception.h" -#include "Emu/Io/mouse_config.h" - -mouse_config g_cfg_mouse; - -void basic_mouse_handler::Init(const u32 max_connect) -{ - if (m_info.max_connect > 0) - { - // Already initialized - return; - } - - if (!g_cfg_mouse.load()) - { - input_log.notice("basic_mouse_handler: Could not load basic mouse config. Using defaults."); - } - - reload_config(); - - m_mice.clear(); - m_mice.emplace_back(Mouse()); - - m_info = {}; - m_info.max_connect = max_connect; - m_info.now_connect = std::min(::size32(m_mice), max_connect); - m_info.info = input::g_mice_intercepted ? CELL_MOUSE_INFO_INTERCEPTED : 0; // Ownership of mouse data: 0=Application, 1=System - for (u32 i = 1; i < max_connect; i++) - { - m_info.status[i] = CELL_MOUSE_STATUS_DISCONNECTED; - m_info.mode[i] = CELL_MOUSE_INFO_TABLET_MOUSE_MODE; - m_info.tablet_is_supported[i] = CELL_MOUSE_INFO_TABLET_NOT_SUPPORTED; - } - m_info.status[0] = CELL_MOUSE_STATUS_CONNECTED; // (TODO: Support for more mice) - m_info.vendor_id[0] = 0x1234; - m_info.product_id[0] = 0x1234; - - type = mouse_handler::basic; -} - -void basic_mouse_handler::reload_config() -{ - input_log.notice("Basic mouse config=\n%s", g_cfg_mouse.to_string()); - - m_buttons[CELL_MOUSE_BUTTON_1] = get_mouse_button(g_cfg_mouse.mouse_button_1); - m_buttons[CELL_MOUSE_BUTTON_2] = get_mouse_button(g_cfg_mouse.mouse_button_2); - m_buttons[CELL_MOUSE_BUTTON_3] = get_mouse_button(g_cfg_mouse.mouse_button_3); - m_buttons[CELL_MOUSE_BUTTON_4] = get_mouse_button(g_cfg_mouse.mouse_button_4); - m_buttons[CELL_MOUSE_BUTTON_5] = get_mouse_button(g_cfg_mouse.mouse_button_5); - m_buttons[CELL_MOUSE_BUTTON_6] = get_mouse_button(g_cfg_mouse.mouse_button_6); - m_buttons[CELL_MOUSE_BUTTON_7] = get_mouse_button(g_cfg_mouse.mouse_button_7); - m_buttons[CELL_MOUSE_BUTTON_8] = get_mouse_button(g_cfg_mouse.mouse_button_8); -} - -/* Sets the target window for the event handler, and also installs an event filter on the target. */ -void basic_mouse_handler::SetTargetWindow(QWindow* target) -{ - if (target) - { - m_target = target; - target->installEventFilter(this); - } - else - { - // If this is hit, it probably means that some refactoring occurs because currently a gsframe is created in Load. - // We still want events so filter from application instead since target is null. - QApplication::instance()->installEventFilter(this); - input_log.error("Trying to set mouse handler to a null target window."); - } -} - -bool basic_mouse_handler::eventFilter(QObject* target, QEvent* ev) -{ - if (m_info.max_connect == 0) - { - // Not initialized - return false; - } - - if (!ev) [[unlikely]] - { - return false; - } - - if (input::g_active_mouse_and_keyboard != input::active_mouse_and_keyboard::emulated) - { - return false; - } - - // !m_target is for future proofing when gsrender isn't automatically initialized on load to ensure events still occur - // !m_target->isVisible() is a hack since currently a guiless application will STILL inititialize a gsrender (providing a valid target) - if (!m_target || !m_target->isVisible() || target == m_target) - { - if (g_cfg_mouse.reload_requested.exchange(false)) - { - reload_config(); - } - - switch (ev->type()) - { - case QEvent::MouseButtonPress: - MouseButton(static_cast(ev), true); - break; - case QEvent::MouseButtonRelease: - MouseButton(static_cast(ev), false); - break; - case QEvent::MouseMove: - MouseMove(static_cast(ev)); - break; - case QEvent::Wheel: - MouseScroll(static_cast(ev)); - break; - case QEvent::KeyPress: - Key(static_cast(ev), true); - break; - case QEvent::KeyRelease: - Key(static_cast(ev), false); - break; - case QEvent::Leave: - { - // Issue mouse move on leave. Otherwise we may not get any mouse event at the screen borders. - const QPoint window_pos = m_target->mapToGlobal(m_target->position()) / m_target->devicePixelRatio(); - const QPoint cursor_pos = QCursor::pos() - window_pos; - - if (cursor_pos.x() <= 0 || cursor_pos.x() >= m_target->width() || cursor_pos.y() <= 0 || cursor_pos.y() >= m_target->height()) - { - MouseMove(cursor_pos); - } - break; - } - default: - return false; - } - } - return false; -} - -void basic_mouse_handler::Key(QKeyEvent* event, bool pressed) -{ - if (!event) [[unlikely]] - { - return; - } - - const int key = event->key(); - if (const auto it = std::find_if(m_buttons.cbegin(), m_buttons.cend(), [key](const auto& entry) - { - return entry.second.code == key && entry.second.is_key; - }); - it != m_buttons.cend()) - { - MouseHandlerBase::Button(0, it->first, pressed); - } -} - -void basic_mouse_handler::MouseButton(QMouseEvent* event, bool pressed) -{ - if (!event) [[unlikely]] - { - return; - } - - const int button = event->button(); - if (const auto it = std::find_if(m_buttons.cbegin(), m_buttons.cend(), [button](const auto& entry) - { - return entry.second.code == button && !entry.second.is_key; - }); - it != m_buttons.cend()) - { - MouseHandlerBase::Button(0, it->first, pressed); - } -} - -void basic_mouse_handler::MouseScroll(QWheelEvent* event) -{ - if (!event) [[unlikely]] - { - return; - } - - const QPoint delta = event->angleDelta(); - const s8 x = std::clamp(delta.x() / 120, -128, 127); - const s8 y = std::clamp(delta.y() / 120, -128, 127); - MouseHandlerBase::Scroll(0, x, y); -} - -bool basic_mouse_handler::get_mouse_lock_state() const -{ - if (auto game_frame = dynamic_cast(m_target)) - return game_frame->get_mouse_lock_state(); - return false; -} - -basic_mouse_handler::mouse_button basic_mouse_handler::get_mouse_button(const cfg::string& button) -{ - const std::string name = button.to_string(); - const auto it = std::find_if(mouse_list.cbegin(), mouse_list.cend(), [&name](const auto& entry) - { - return entry.second == name; - }); - - if (it != mouse_list.cend()) - { - return mouse_button{ - .code = static_cast(it->first), - .is_key = false}; - } - - if (const u32 key = keyboard_pad_handler::GetKeyCode(QString::fromStdString(name))) - { - return mouse_button{ - .code = static_cast(key), - .is_key = true}; - } - - return mouse_button{ - .code = Qt::MouseButton::NoButton, - .is_key = false}; -} - -void basic_mouse_handler::MouseMove(QMouseEvent* event) -{ - if (!event) [[unlikely]] - { - return; - } - - if (is_time_for_update()) - { - MouseMove(event->pos()); - } -} - -void basic_mouse_handler::MouseMove(const QPoint& e_pos) -{ - if (!m_target) - return; - - // get the screen dimensions - const QSize screen = m_target->size(); - - if (m_target->isActive() && get_mouse_lock_state()) - { - // get the center of the screen in global coordinates - QPoint p_center = m_target->geometry().topLeft() + QPoint(screen.width() / 2, screen.height() / 2); - - // reset the mouse to the center for consistent results since edge movement won't be registered - QCursor::setPos(m_target->screen(), p_center); - - // convert the center into screen coordinates - p_center = m_target->mapFromGlobal(p_center); - - // current mouse position, starting at the center - static QPoint p_real(p_center); - - // get the delta of the mouse position to the screen center - const QPoint p_delta = e_pos - p_center; - - // update the current position without leaving the screen borders - p_real.setX(std::clamp(p_real.x() + p_delta.x(), 0, screen.width())); - p_real.setY(std::clamp(p_real.y() + p_delta.y(), 0, screen.height())); - - // pass the 'real' position and the current delta to the screen center - MouseHandlerBase::Move(0, p_real.x(), p_real.y(), screen.width(), screen.height(), true, p_delta.x(), p_delta.y()); - } - else - { - // pass the absolute position - MouseHandlerBase::Move(0, e_pos.x(), e_pos.y(), screen.width(), screen.height()); - } -} diff --git a/rpcs3qt-legacy/Input/basic_mouse_handler.h b/rpcs3qt-legacy/Input/basic_mouse_handler.h deleted file mode 100644 index ebc962e64..000000000 --- a/rpcs3qt-legacy/Input/basic_mouse_handler.h +++ /dev/null @@ -1,44 +0,0 @@ -#pragma once - -#include "util/types.hpp" -#include "Emu/Io/MouseHandler.h" - -#include -#include -#include - -namespace cfg -{ - class string; -} - -class basic_mouse_handler final : public MouseHandlerBase, public QObject -{ - using MouseHandlerBase::MouseHandlerBase; - -public: - void Init(const u32 max_connect) override; - - void SetTargetWindow(QWindow* target); - void Key(QKeyEvent* event, bool pressed); - void MouseButton(QMouseEvent* event, bool pressed); - void MouseScroll(QWheelEvent* event); - void MouseMove(QMouseEvent* event); - void MouseMove(const QPoint& e_pos); - - bool eventFilter(QObject* obj, QEvent* ev) override; - -private: - void reload_config(); - bool get_mouse_lock_state() const; - - struct mouse_button - { - int code = Qt::MouseButton::NoButton; - bool is_key = false; - }; - static mouse_button get_mouse_button(const cfg::string& button); - - QWindow* m_target = nullptr; - std::map m_buttons; -}; diff --git a/rpcs3qt-legacy/Input/gui_pad_thread.cpp b/rpcs3qt-legacy/Input/gui_pad_thread.cpp deleted file mode 100644 index 9c3da66ea..000000000 --- a/rpcs3qt-legacy/Input/gui_pad_thread.cpp +++ /dev/null @@ -1,815 +0,0 @@ -#include "stdafx.h" -#include "gui_pad_thread.h" -#include "Input/ds3_pad_handler.h" -#include "Input/ds4_pad_handler.h" -#include "Input/dualsense_pad_handler.h" -#include "Input/skateboard_pad_handler.h" -#ifdef _WIN32 -#include "Input/xinput_pad_handler.h" -#include "Input/mm_joystick_handler.h" -#elif HAVE_LIBEVDEV -#include "Input/evdev_joystick_handler.h" -#endif -#ifdef HAVE_SDL3 -#include "Input/sdl_pad_handler.h" -#endif -#include "Emu/Io/PadHandler.h" -#include "Emu/system_config.h" -#include "../gui_settings.h" - -#ifdef __linux__ -#include -#include -#include -#define CHECK_IOCTRL_RET(res) \ - if (res == -1) \ - { \ - gui_log.error("gui_pad_thread: ioctl failed (errno=%d=%s)", res, strerror(errno)); \ - } -#elif defined(__APPLE__) -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wold-style-cast" -#pragma GCC diagnostic ignored "-Wdeprecated-declarations" -#pragma GCC diagnostic ignored "-Wmissing-declarations" -#pragma GCC diagnostic ignored "-Wnullability-completeness" -#pragma GCC diagnostic ignored "-Wdeprecated-anon-enum-enum-conversion" -#include -#include -#pragma GCC diagnostic pop -#endif - -#include - -LOG_CHANNEL(gui_log, "GUI"); - -atomic_t gui_pad_thread::m_reset = false; - -gui_pad_thread::gui_pad_thread() -{ - m_thread = std::make_unique>>("Gui Pad Thread", [this]() - { - run(); - }); -} - -gui_pad_thread::~gui_pad_thread() -{ - if (m_thread) - { - auto& thread = *m_thread; - thread = thread_state::aborting; - thread(); - m_thread.reset(); - } - -#ifdef __linux__ - if (m_uinput_fd != 1) - { - gui_log.notice("gui_pad_thread: closing /dev/uinput"); - CHECK_IOCTRL_RET(ioctl(m_uinput_fd, UI_DEV_DESTROY)); - int res = close(m_uinput_fd); - if (res == -1) - { - gui_log.error("gui_pad_thread: Failed to close /dev/uinput (errno=%d=%s)", res, strerror(errno)); - } - m_uinput_fd = -1; - } -#endif -} - -void gui_pad_thread::update_settings(const std::shared_ptr& settings) -{ - ensure(!!settings); - - m_allow_global_input = settings->GetValue(gui::nav_global).toBool(); -} - -bool gui_pad_thread::init() -{ - m_handler.reset(); - m_pad.reset(); - - // Initialize last button states as pressed to avoid unwanted button presses when starting the thread. - m_last_button_state.fill(true); - m_timestamp = steady_clock::now(); - m_initial_timestamp = steady_clock::now(); - m_last_auto_repeat_button = pad_button::pad_button_max_enum; - - g_cfg_input_configs.load(); - - std::string active_config = g_cfg_input_configs.active_configs.get_value(""); - - if (active_config.empty()) - { - active_config = g_cfg_input_configs.active_configs.get_value(g_cfg_input_configs.global_key); - } - - gui_log.notice("gui_pad_thread: Using input configuration: '%s'", active_config); - - // Load in order to get the pad handlers - if (!g_cfg_input.load("", active_config)) - { - gui_log.notice("gui_pad_thread: Loaded empty pad config"); - } - - // Adjust to the different pad handlers - for (usz i = 0; i < g_cfg_input.player.size(); i++) - { - std::shared_ptr handler; - gui_pad_thread::InitPadConfig(g_cfg_input.player[i]->config, g_cfg_input.player[i]->handler, handler); - } - - // Reload with proper defaults - if (!g_cfg_input.load("", active_config)) - { - gui_log.notice("gui_pad_thread: Reloaded empty pad config"); - } - - gui_log.trace("gui_pad_thread: Using pad config:\n%s", g_cfg_input); - - for (u32 i = 0; i < CELL_PAD_MAX_PORT_NUM; i++) // max 7 pads - { - cfg_player* cfg = g_cfg_input.player[i]; - - const pad_handler handler_type = cfg->handler.get(); - std::shared_ptr cur_pad_handler = GetHandler(handler_type); - - if (!cur_pad_handler) - { - continue; - } - - cur_pad_handler->Init(); - - m_handler = cur_pad_handler; - m_pad = std::make_shared(handler_type, i, CELL_PAD_STATUS_DISCONNECTED, CELL_PAD_CAPABILITY_PS3_CONFORMITY | CELL_PAD_CAPABILITY_PRESS_MODE | CELL_PAD_CAPABILITY_ACTUATOR, CELL_PAD_DEV_TYPE_STANDARD); - - if (!cur_pad_handler->bindPadToDevice(m_pad)) - { - gui_log.error("gui_pad_thread: Failed to bind device '%s' to handler %s.", cfg->device.to_string(), handler_type); - } - - gui_log.notice("gui_pad_thread: Pad %d: device='%s', handler=%s, VID=0x%x, PID=0x%x, class_type=0x%x, class_profile=0x%x", - i, cfg->device.to_string(), m_pad->m_pad_handler, m_pad->m_vendor_id, m_pad->m_product_id, m_pad->m_class_type, m_pad->m_class_profile); - - if (handler_type != pad_handler::null) - { - input_log.notice("gui_pad_thread %d: config=\n%s", i, cfg->to_string()); - } - - // We only use one pad - break; - } - - if (!m_handler || !m_pad) - { - gui_log.notice("gui_pad_thread: No devices configured."); - return false; - } - -#ifdef __linux__ - gui_log.notice("gui_pad_thread: opening /dev/uinput"); - - m_uinput_fd = open("/dev/uinput", O_WRONLY | O_NONBLOCK); - if (m_uinput_fd == -1) - { - gui_log.error("gui_pad_thread: Failed to open /dev/uinput (errno=%d=%s)", m_uinput_fd, strerror(errno)); - return false; - } - - struct uinput_setup usetup{}; - usetup.id.bustype = BUS_USB; - usetup.id.vendor = 0x1234; - usetup.id.product = 0x1234; - std::strcpy(usetup.name, "RPCS3 GUI Input Device"); - - // The ioctls below will enable the device that is about to be created to pass events. - CHECK_IOCTRL_RET(ioctl(m_uinput_fd, UI_SET_EVBIT, EV_KEY)); - CHECK_IOCTRL_RET(ioctl(m_uinput_fd, UI_SET_KEYBIT, KEY_ESC)); - CHECK_IOCTRL_RET(ioctl(m_uinput_fd, UI_SET_KEYBIT, KEY_ENTER)); - CHECK_IOCTRL_RET(ioctl(m_uinput_fd, UI_SET_KEYBIT, KEY_BACKSPACE)); - CHECK_IOCTRL_RET(ioctl(m_uinput_fd, UI_SET_KEYBIT, KEY_TAB)); - CHECK_IOCTRL_RET(ioctl(m_uinput_fd, UI_SET_KEYBIT, KEY_LEFT)); - CHECK_IOCTRL_RET(ioctl(m_uinput_fd, UI_SET_KEYBIT, KEY_RIGHT)); - CHECK_IOCTRL_RET(ioctl(m_uinput_fd, UI_SET_KEYBIT, KEY_UP)); - CHECK_IOCTRL_RET(ioctl(m_uinput_fd, UI_SET_KEYBIT, KEY_DOWN)); - CHECK_IOCTRL_RET(ioctl(m_uinput_fd, UI_SET_KEYBIT, BTN_LEFT)); - CHECK_IOCTRL_RET(ioctl(m_uinput_fd, UI_SET_KEYBIT, BTN_RIGHT)); - CHECK_IOCTRL_RET(ioctl(m_uinput_fd, UI_SET_KEYBIT, BTN_MIDDLE)); - CHECK_IOCTRL_RET(ioctl(m_uinput_fd, UI_SET_EVBIT, EV_REL)); - CHECK_IOCTRL_RET(ioctl(m_uinput_fd, UI_SET_RELBIT, REL_X)); - CHECK_IOCTRL_RET(ioctl(m_uinput_fd, UI_SET_RELBIT, REL_Y)); - CHECK_IOCTRL_RET(ioctl(m_uinput_fd, UI_SET_RELBIT, REL_WHEEL)); - CHECK_IOCTRL_RET(ioctl(m_uinput_fd, UI_SET_RELBIT, REL_HWHEEL)); - CHECK_IOCTRL_RET(ioctl(m_uinput_fd, UI_DEV_SETUP, &usetup)); - CHECK_IOCTRL_RET(ioctl(m_uinput_fd, UI_DEV_CREATE)); -#endif - - return true; -} - -std::shared_ptr gui_pad_thread::GetHandler(pad_handler type) -{ - switch (type) - { - case pad_handler::null: - case pad_handler::keyboard: - case pad_handler::move: - // Makes no sense to use this if we are in the GUI anyway - return nullptr; - case pad_handler::ds3: - return std::make_shared(); - case pad_handler::ds4: - return std::make_shared(); - case pad_handler::dualsense: - return std::make_shared(); - case pad_handler::skateboard: - return std::make_shared(); -#ifdef _WIN32 - case pad_handler::xinput: - return std::make_shared(); - case pad_handler::mm: - return std::make_shared(); -#endif -#ifdef HAVE_SDL3 - case pad_handler::sdl: - return std::make_shared(); -#endif -#ifdef HAVE_LIBEVDEV - case pad_handler::evdev: - return std::make_shared(); -#endif - } - - return nullptr; -} - -void gui_pad_thread::InitPadConfig(cfg_pad& cfg, pad_handler type, std::shared_ptr& handler) -{ - if (!handler) - { - handler = GetHandler(type); - - if (handler) - { - handler->init_config(&cfg); - } - } -} - -void gui_pad_thread::run() -{ - gui_log.notice("gui_pad_thread: Pad thread started"); - - m_reset = true; - - while (thread_ctrl::state() != thread_state::aborting) - { - if (m_reset && m_reset.exchange(false)) - { - if (!init()) - { - gui_log.warning("gui_pad_thread: Pad thread stopped (init failed during reset)"); - return; - } - } - - // Only process input if there is an active window - if (m_handler && m_pad && (m_allow_global_input || QApplication::activeWindow())) - { - m_handler->process(); - - if (thread_ctrl::state() == thread_state::aborting) - { - break; - } - - process_input(); - } - - thread_ctrl::wait_for(10000); - } - - gui_log.notice("gui_pad_thread: Pad thread stopped"); -} - -void gui_pad_thread::process_input() -{ - if (!m_pad || !(m_pad->m_port_status & CELL_PAD_STATUS_CONNECTED)) - { - return; - } - - constexpr u64 ms_threshold = 500; - - const auto on_button_pressed = [this](pad_button button_id, bool pressed, u16 value) - { - if (button_id == m_mouse_boost_button) - { - m_boost_mouse = pressed; - return; - } - - u16 key = 0; - mouse_button btn = mouse_button::none; - mouse_wheel wheel = mouse_wheel::none; - float wheel_delta = 0.0f; - const float wheel_multiplier = pressed ? (m_boost_mouse ? 10.0f : 1.0f) : 0.0f; - const float move_multiplier = pressed ? (m_boost_mouse ? 40.0f : 20.0f) : 0.0f; - - switch (button_id) - { -#ifdef _WIN32 - case pad_button::dpad_up: key = VK_UP; break; - case pad_button::dpad_down: key = VK_DOWN; break; - case pad_button::dpad_left: key = VK_LEFT; break; - case pad_button::dpad_right: key = VK_RIGHT; break; - case pad_button::circle: key = VK_ESCAPE; break; - case pad_button::cross: key = VK_RETURN; break; - case pad_button::square: key = VK_BACK; break; - case pad_button::triangle: key = VK_TAB; break; -#elif defined(__linux__) - case pad_button::dpad_up: key = KEY_UP; break; - case pad_button::dpad_down: key = KEY_DOWN; break; - case pad_button::dpad_left: key = KEY_LEFT; break; - case pad_button::dpad_right: key = KEY_RIGHT; break; - case pad_button::circle: key = KEY_ESC; break; - case pad_button::cross: key = KEY_ENTER; break; - case pad_button::square: key = KEY_BACKSPACE; break; - case pad_button::triangle: key = KEY_TAB; break; -#elif defined(__APPLE__) - case pad_button::dpad_up: key = kVK_UpArrow; break; - case pad_button::dpad_down: key = kVK_DownArrow; break; - case pad_button::dpad_left: key = kVK_LeftArrow; break; - case pad_button::dpad_right: key = kVK_RightArrow; break; - case pad_button::circle: key = kVK_Escape; break; - case pad_button::cross: key = kVK_Return; break; - case pad_button::square: key = kVK_Delete; break; - case pad_button::triangle: key = kVK_Tab; break; -#endif - case pad_button::L1: btn = mouse_button::left; break; - case pad_button::R1: btn = mouse_button::right; break; - case pad_button::rs_up: - wheel = mouse_wheel::vertical; - wheel_delta = 10.0f * wheel_multiplier; - break; - case pad_button::rs_down: - wheel = mouse_wheel::vertical; - wheel_delta = -10.0f * wheel_multiplier; - break; - case pad_button::rs_left: - wheel = mouse_wheel::horizontal; - wheel_delta = -10.0f * wheel_multiplier; - break; - case pad_button::rs_right: - wheel = mouse_wheel::horizontal; - wheel_delta = 10.0f * wheel_multiplier; - break; - case pad_button::ls_up: m_mouse_delta_y -= (abs(value - 128) / 255.f) * move_multiplier; break; - case pad_button::ls_down: m_mouse_delta_y += (abs(value - 128) / 255.f) * move_multiplier; break; - case pad_button::ls_left: m_mouse_delta_x -= (abs(value - 128) / 255.f) * move_multiplier; break; - case pad_button::ls_right: m_mouse_delta_x += (abs(value - 128) / 255.f) * move_multiplier; break; - default: return; - } - - if (key) - { - send_key_event(key, pressed); - } - else if (btn != mouse_button::none) - { - send_mouse_button_event(btn, pressed); - } - else if (wheel != mouse_wheel::none && pressed) - { - send_mouse_wheel_event(wheel, wheel_delta); - } - }; - - const auto handle_button_press = [&](pad_button button_id, bool pressed, u16 value) - { - if (button_id >= pad_button::pad_button_max_enum) - { - return; - } - - bool& last_state = m_last_button_state[static_cast(button_id)]; - - if (pressed) - { - const bool is_auto_repeat_button = m_auto_repeat_buttons.contains(button_id); - const bool is_mouse_move_button = m_mouse_move_buttons.contains(button_id); - - if (!last_state) - { - if (button_id != m_mouse_boost_button && !is_mouse_move_button) - { - // The button was not pressed before, so this is a new button press. Reset auto-repeat. - m_timestamp = steady_clock::now(); - m_initial_timestamp = m_timestamp; - m_last_auto_repeat_button = is_auto_repeat_button ? button_id : pad_button::pad_button_max_enum; - } - - on_button_pressed(static_cast(button_id), true, value); - } - else if (is_auto_repeat_button) - { - if (m_last_auto_repeat_button == button_id && m_input_timer.GetMsSince(m_initial_timestamp) > ms_threshold && m_input_timer.GetMsSince(m_timestamp) > m_auto_repeat_buttons.at(button_id)) - { - // The auto-repeat button was pressed for at least the given threshold in ms and will trigger at an interval. - m_timestamp = steady_clock::now(); - on_button_pressed(static_cast(button_id), true, value); - } - else if (m_last_auto_repeat_button == pad_button::pad_button_max_enum) - { - // An auto-repeat button was already pressed before and will now start triggering again after the next threshold. - m_last_auto_repeat_button = button_id; - } - } - else if (is_mouse_move_button) - { - on_button_pressed(static_cast(button_id), pressed, value); - } - } - else if (last_state) - { - if (m_last_auto_repeat_button == button_id) - { - // We stopped pressing an auto-repeat button, so re-enable auto-repeat for other buttons. - m_last_auto_repeat_button = pad_button::pad_button_max_enum; - } - - on_button_pressed(static_cast(button_id), false, value); - } - - last_state = pressed; - }; - - for (const auto& button : m_pad->m_buttons) - { - pad_button button_id = pad_button::pad_button_max_enum; - if (button.m_offset == CELL_PAD_BTN_OFFSET_DIGITAL1) - { - switch (button.m_outKeyCode) - { - case CELL_PAD_CTRL_LEFT: - button_id = pad_button::dpad_left; - break; - case CELL_PAD_CTRL_RIGHT: - button_id = pad_button::dpad_right; - break; - case CELL_PAD_CTRL_DOWN: - button_id = pad_button::dpad_down; - break; - case CELL_PAD_CTRL_UP: - button_id = pad_button::dpad_up; - break; - case CELL_PAD_CTRL_L3: - button_id = pad_button::L3; - break; - case CELL_PAD_CTRL_R3: - button_id = pad_button::R3; - break; - case CELL_PAD_CTRL_SELECT: - button_id = pad_button::select; - break; - case CELL_PAD_CTRL_START: - button_id = pad_button::start; - break; - default: - break; - } - } - else if (button.m_offset == CELL_PAD_BTN_OFFSET_DIGITAL2) - { - switch (button.m_outKeyCode) - { - case CELL_PAD_CTRL_TRIANGLE: - button_id = pad_button::triangle; - break; - case CELL_PAD_CTRL_CIRCLE: - button_id = g_cfg.sys.enter_button_assignment == enter_button_assign::circle ? pad_button::cross : pad_button::circle; - break; - case CELL_PAD_CTRL_SQUARE: - button_id = pad_button::square; - break; - case CELL_PAD_CTRL_CROSS: - button_id = g_cfg.sys.enter_button_assignment == enter_button_assign::circle ? pad_button::circle : pad_button::cross; - break; - case CELL_PAD_CTRL_L1: - button_id = pad_button::L1; - break; - case CELL_PAD_CTRL_R1: - button_id = pad_button::R1; - break; - case CELL_PAD_CTRL_L2: - button_id = pad_button::L2; - break; - case CELL_PAD_CTRL_R2: - button_id = pad_button::R2; - break; - case CELL_PAD_CTRL_PS: - button_id = pad_button::ps; - break; - default: - break; - } - } - - handle_button_press(button_id, button.m_pressed, button.m_value); - } - - for (const AnalogStick& stick : m_pad->m_sticks) - { - pad_button button_id = pad_button::pad_button_max_enum; - pad_button release_id = pad_button::pad_button_max_enum; - - switch (stick.m_offset) - { - case CELL_PAD_BTN_OFFSET_ANALOG_LEFT_X: - button_id = (stick.m_value <= 128) ? pad_button::ls_left : pad_button::ls_right; - release_id = (stick.m_value > 128) ? pad_button::ls_left : pad_button::ls_right; - break; - case CELL_PAD_BTN_OFFSET_ANALOG_LEFT_Y: - button_id = (stick.m_value <= 128) ? pad_button::ls_up : pad_button::ls_down; - release_id = (stick.m_value > 128) ? pad_button::ls_up : pad_button::ls_down; - break; - case CELL_PAD_BTN_OFFSET_ANALOG_RIGHT_X: - button_id = (stick.m_value <= 128) ? pad_button::rs_left : pad_button::rs_right; - release_id = (stick.m_value > 128) ? pad_button::rs_left : pad_button::rs_right; - break; - case CELL_PAD_BTN_OFFSET_ANALOG_RIGHT_Y: - button_id = (stick.m_value <= 128) ? pad_button::rs_up : pad_button::rs_down; - release_id = (stick.m_value > 128) ? pad_button::rs_up : pad_button::rs_down; - break; - default: - break; - } - - bool pressed; - - if (m_mouse_move_buttons.contains(button_id)) - { - // Mouse move sticks are always pressed if they surpass a tiny deadzone. - constexpr int deadzone = 5; - pressed = std::abs(stick.m_value - 128) > deadzone; - } - else - { - // Let's say other sticks are only pressed if they are almost completely tilted. Otherwise navigation feels really wacky. - pressed = stick.m_value < 30 || stick.m_value > 225; - } - - // Release other direction on the same axis first - handle_button_press(release_id, false, stick.m_value); - - // Handle currently pressed stick direction - handle_button_press(button_id, pressed, stick.m_value); - } - - // Send mouse move event at the end to prevent redundant calls - - // Round to lower integer - const int delta_x = m_mouse_delta_x; - const int delta_y = m_mouse_delta_y; - - if (delta_x || delta_y) - { - // Remove integer part - m_mouse_delta_x -= delta_x; - m_mouse_delta_y -= delta_y; - - // Send event - send_mouse_move_event(delta_x, delta_y); - } -} - -#ifdef __linux__ -void gui_pad_thread::emit_event(int type, int code, int val) -{ - struct input_event ie{}; - ie.type = type; - ie.code = code; - ie.value = val; - - int res = write(m_uinput_fd, &ie, sizeof(ie)); - if (res == -1) - { - gui_log.error("gui_pad_thread::emit_event: write failed (errno=%d=%s)", res, strerror(errno)); - } -} -#endif - -void gui_pad_thread::send_key_event(u32 key, bool pressed) -{ - gui_log.trace("gui_pad_thread::send_key_event: key=%d, pressed=%d", key, pressed); - -#ifdef _WIN32 - INPUT input{}; - input.type = INPUT_KEYBOARD; - input.ki.wVk = key; - - if (!pressed) - { - input.ki.dwFlags = KEYEVENTF_KEYUP; - } - - if (SendInput(1, &input, sizeof(INPUT)) != 1) - { - gui_log.error("gui_pad_thread: SendInput() failed: %s", fmt::win_error{GetLastError(), nullptr}); - } -#elif defined(__linux__) - emit_event(EV_KEY, key, pressed ? 1 : 0); - emit_event(EV_SYN, SYN_REPORT, 0); -#elif defined(__APPLE__) - CGEventRef ev = CGEventCreateKeyboardEvent(NULL, static_cast(key), pressed); - if (!ev) - { - gui_log.error("gui_pad_thread: CGEventCreateKeyboardEvent() failed"); - return; - } - - CGEventPost(kCGHIDEventTap, ev); - CFRelease(ev); -#endif -} - -void gui_pad_thread::send_mouse_button_event(mouse_button btn, bool pressed) -{ - gui_log.trace("gui_pad_thread::send_mouse_button_event: btn=%d, pressed=%d", static_cast(btn), pressed); - -#ifdef _WIN32 - INPUT input{}; - input.type = INPUT_MOUSE; - - switch (btn) - { - case mouse_button::none: return; - case mouse_button::left: input.mi.dwFlags = pressed ? MOUSEEVENTF_LEFTDOWN : MOUSEEVENTF_LEFTUP; break; - case mouse_button::right: input.mi.dwFlags = pressed ? MOUSEEVENTF_RIGHTDOWN : MOUSEEVENTF_RIGHTUP; break; - case mouse_button::middle: input.mi.dwFlags = pressed ? MOUSEEVENTF_MIDDLEDOWN : MOUSEEVENTF_MIDDLEUP; break; - } - - if (SendInput(1, &input, sizeof(INPUT)) != 1) - { - gui_log.error("gui_pad_thread: SendInput() failed: %s", fmt::win_error{GetLastError(), nullptr}); - } -#elif defined(__linux__) - int key = 0; - - switch (btn) - { - case mouse_button::none: return; - case mouse_button::left: key = BTN_LEFT; break; - case mouse_button::right: key = BTN_RIGHT; break; - case mouse_button::middle: key = BTN_MIDDLE; break; - } - - emit_event(EV_KEY, key, pressed ? 1 : 0); - emit_event(EV_SYN, SYN_REPORT, 0); -#elif defined(__APPLE__) - CGEventType type{}; - CGMouseButton mouse_btn{}; - - switch (btn) - { - case mouse_button::none: return; - case mouse_button::left: - type = pressed ? kCGEventLeftMouseDown : kCGEventLeftMouseUp; - mouse_btn = kCGMouseButtonLeft; - break; - case mouse_button::right: - type = pressed ? kCGEventRightMouseDown : kCGEventRightMouseUp; - mouse_btn = kCGMouseButtonRight; - break; - case mouse_button::middle: - type = pressed ? kCGEventOtherMouseDown : kCGEventOtherMouseUp; - mouse_btn = kCGMouseButtonCenter; - break; - } - - CGEventRef ev = CGEventCreateMouseEvent(NULL, type, CGPointMake(m_mouse_abs_x, m_mouse_abs_y), mouse_btn); - if (!ev) - { - gui_log.error("gui_pad_thread: CGEventCreateMouseEvent() failed"); - return; - } - - CGEventPost(kCGHIDEventTap, ev); - CFRelease(ev); -#endif -} - -void gui_pad_thread::send_mouse_wheel_event(mouse_wheel wheel, int delta) -{ - gui_log.trace("gui_pad_thread::send_mouse_wheel_event: wheel=%d, delta=%d", static_cast(wheel), delta); - - if (!delta) - { - return; - } - -#ifdef _WIN32 - INPUT input{}; - input.type = INPUT_MOUSE; - input.mi.mouseData = delta; - - switch (wheel) - { - case mouse_wheel::none: return; - case mouse_wheel::vertical: input.mi.dwFlags = MOUSEEVENTF_WHEEL; break; - case mouse_wheel::horizontal: input.mi.dwFlags = MOUSEEVENTF_HWHEEL; break; - } - - if (SendInput(1, &input, sizeof(INPUT)) != 1) - { - gui_log.error("gui_pad_thread: SendInput() failed: %s", fmt::win_error{GetLastError(), nullptr}); - } -#elif defined(__linux__) - int axis = 0; - - switch (wheel) - { - case mouse_wheel::none: return; - case mouse_wheel::vertical: axis = REL_WHEEL; break; - case mouse_wheel::horizontal: axis = REL_HWHEEL; break; - } - - emit_event(EV_REL, axis, delta); - emit_event(EV_SYN, SYN_REPORT, 0); -#elif defined(__APPLE__) - int v_delta = 0; - int h_delta = 0; - - switch (wheel) - { - case mouse_wheel::none: return; - case mouse_wheel::vertical: v_delta = delta; break; - case mouse_wheel::horizontal: h_delta = delta; break; - } - - constexpr u32 wheel_count = 2; - CGEventRef ev = CGEventCreateScrollWheelEvent(NULL, kCGScrollEventUnitPixel, wheel_count, v_delta, h_delta); - if (!ev) - { - gui_log.error("gui_pad_thread: CGEventCreateScrollWheelEvent() failed"); - return; - } - - CGEventPost(kCGHIDEventTap, ev); - CFRelease(ev); -#endif -} - -void gui_pad_thread::send_mouse_move_event(int delta_x, int delta_y) -{ - gui_log.trace("gui_pad_thread::send_mouse_move_event: delta_x=%d, delta_y=%d", delta_x, delta_y); - - if (!delta_x && !delta_y) - { - return; - } - -#ifdef _WIN32 - INPUT input{}; - input.type = INPUT_MOUSE; - input.mi.dwFlags = MOUSEEVENTF_MOVE; - input.mi.dx = delta_x; - input.mi.dy = delta_y; - - if (SendInput(1, &input, sizeof(INPUT)) != 1) - { - gui_log.error("gui_pad_thread: SendInput() failed: %s", fmt::win_error{GetLastError(), nullptr}); - } -#elif defined(__linux__) - if (delta_x) - emit_event(EV_REL, REL_X, delta_x); - if (delta_y) - emit_event(EV_REL, REL_Y, delta_y); - emit_event(EV_SYN, SYN_REPORT, 0); -#elif defined(__APPLE__) - CGDirectDisplayID display = CGMainDisplayID(); - const usz width = CGDisplayPixelsWide(display); - const usz height = CGDisplayPixelsHigh(display); - const float mouse_abs_x = std::clamp(m_mouse_abs_x + delta_x, 0.0f, width - 1.0f); - const float mouse_abs_y = std::clamp(m_mouse_abs_y + delta_y, 0.0f, height - 1.0f); - - if (m_mouse_abs_x == mouse_abs_x && m_mouse_abs_y == mouse_abs_y) - { - return; - } - - m_mouse_abs_x = mouse_abs_x; - m_mouse_abs_y = mouse_abs_y; - - CGEventRef ev = CGEventCreateMouseEvent(NULL, kCGEventMouseMoved, CGPointMake(m_mouse_abs_x, m_mouse_abs_y), {}); - if (!ev) - { - gui_log.error("gui_pad_thread: CGEventCreateMouseEvent() failed"); - return; - } - - CGEventPost(kCGHIDEventTap, ev); - CFRelease(ev); -#endif -} diff --git a/rpcs3qt-legacy/Input/gui_pad_thread.h b/rpcs3qt-legacy/Input/gui_pad_thread.h deleted file mode 100644 index d4a8c169d..000000000 --- a/rpcs3qt-legacy/Input/gui_pad_thread.h +++ /dev/null @@ -1,101 +0,0 @@ -#pragma once - -#include "util/types.hpp" -#include "util/atomic.hpp" -#include "Emu/Io/pad_types.h" -#include "Emu/Io/pad_config.h" -#include "Emu/Io/pad_config_types.h" -#include "util/Timer.h" -#include "util/Thread.h" - -class PadHandlerBase; -class gui_settings; - -class gui_pad_thread -{ -public: - gui_pad_thread(); - virtual ~gui_pad_thread(); - - void update_settings(const std::shared_ptr& settings); - - static std::shared_ptr GetHandler(pad_handler type); - static void InitPadConfig(cfg_pad& cfg, pad_handler type, std::shared_ptr& handler); - - static void reset() - { - m_reset = true; - } - -protected: - bool init(); - void run(); - - void process_input(); - - enum class mouse_button - { - none, - left, - right, - middle - }; - - enum class mouse_wheel - { - none, - vertical, - horizontal - }; - - void send_key_event(u32 key, bool pressed); - void send_mouse_button_event(mouse_button btn, bool pressed); - void send_mouse_wheel_event(mouse_wheel wheel, int delta); - void send_mouse_move_event(int delta_x, int delta_y); - -#ifdef __linux__ - int m_uinput_fd = -1; - void emit_event(int type, int code, int val); -#endif - - std::shared_ptr m_handler; - std::shared_ptr m_pad; - - std::unique_ptr>> m_thread; - atomic_t m_allow_global_input = false; - static atomic_t m_reset; - - std::array(pad_button::pad_button_max_enum)> m_last_button_state{}; - - steady_clock::time_point m_timestamp; - steady_clock::time_point m_initial_timestamp; - Timer m_input_timer; - - static constexpr u64 auto_repeat_ms_interval_default = 200; - pad_button m_last_auto_repeat_button = pad_button::pad_button_max_enum; - std::map m_auto_repeat_buttons = { - {pad_button::dpad_up, auto_repeat_ms_interval_default}, - {pad_button::dpad_down, auto_repeat_ms_interval_default}, - {pad_button::dpad_left, auto_repeat_ms_interval_default}, - {pad_button::dpad_right, auto_repeat_ms_interval_default}, - {pad_button::rs_up, 1}, // used for wheel scrolling by default - {pad_button::rs_down, 1}, // used for wheel scrolling by default - {pad_button::rs_left, 1}, // used for wheel scrolling by default - {pad_button::rs_right, 1} // used for wheel scrolling by default - }; - - // Mouse movement should just work without delays - std::map m_mouse_move_buttons = { - {pad_button::ls_up, 1}, - {pad_button::ls_down, 1}, - {pad_button::ls_left, 1}, - {pad_button::ls_right, 1}}; - pad_button m_mouse_boost_button = pad_button::L2; - bool m_boost_mouse = false; - float m_mouse_delta_x = 0.0f; - float m_mouse_delta_y = 0.0f; -#ifdef __APPLE__ - float m_mouse_abs_x = 0.0f; - float m_mouse_abs_y = 0.0f; -#endif -}; diff --git a/rpcs3qt-legacy/Input/keyboard_pad_handler.cpp b/rpcs3qt-legacy/Input/keyboard_pad_handler.cpp deleted file mode 100644 index 56d88e594..000000000 --- a/rpcs3qt-legacy/Input/keyboard_pad_handler.cpp +++ /dev/null @@ -1,1311 +0,0 @@ -#include "keyboard_pad_handler.h" -#include "Input/pad_thread.h" -#include "Emu/Io/pad_config.h" -#include "Emu/Io/KeyboardHandler.h" -#include "Emu/Io/interception.h" -#include "Input/product_info.h" -#include "../gs_frame.h" - -#include - -bool keyboard_pad_handler::Init() -{ - const steady_clock::time_point now = steady_clock::now(); - m_last_mouse_move_left = now; - m_last_mouse_move_right = now; - m_last_mouse_move_up = now; - m_last_mouse_move_down = now; - return true; -} - -keyboard_pad_handler::keyboard_pad_handler() - : QObject(), PadHandlerBase(pad_handler::keyboard) -{ - init_configs(); - - // set capabilities - b_has_config = true; -} - -void keyboard_pad_handler::init_config(cfg_pad* cfg) -{ - if (!cfg) - return; - - // Set default button mapping - cfg->ls_left.def = GetKeyName(Qt::Key_A); - cfg->ls_down.def = GetKeyName(Qt::Key_S); - cfg->ls_right.def = GetKeyName(Qt::Key_D); - cfg->ls_up.def = GetKeyName(Qt::Key_W); - cfg->rs_left.def = GetKeyName(Qt::Key_Delete); - cfg->rs_down.def = GetKeyName(Qt::Key_End); - cfg->rs_right.def = GetKeyName(Qt::Key_PageDown); - cfg->rs_up.def = GetKeyName(Qt::Key_Home); - cfg->start.def = GetKeyName(Qt::Key_Return); - cfg->select.def = GetKeyName(Qt::Key_Space); - cfg->ps.def = GetKeyName(Qt::Key_Backspace); - cfg->square.def = GetKeyName(Qt::Key_Z); - cfg->cross.def = GetKeyName(Qt::Key_X); - cfg->circle.def = GetKeyName(Qt::Key_C); - cfg->triangle.def = GetKeyName(Qt::Key_V); - cfg->left.def = GetKeyName(Qt::Key_Left); - cfg->down.def = GetKeyName(Qt::Key_Down); - cfg->right.def = GetKeyName(Qt::Key_Right); - cfg->up.def = GetKeyName(Qt::Key_Up); - cfg->r1.def = GetKeyName(Qt::Key_E); - cfg->r2.def = GetKeyName(Qt::Key_T); - cfg->r3.def = GetKeyName(Qt::Key_G); - cfg->l1.def = GetKeyName(Qt::Key_Q); - cfg->l2.def = GetKeyName(Qt::Key_R); - cfg->l3.def = GetKeyName(Qt::Key_F); - - cfg->pressure_intensity_button.def = GetKeyName(Qt::NoButton); - cfg->analog_limiter_button.def = GetKeyName(Qt::NoButton); - - cfg->lstick_anti_deadzone.def = 0; - cfg->rstick_anti_deadzone.def = 0; - cfg->lstickdeadzone.def = 0; - cfg->rstickdeadzone.def = 0; - cfg->ltriggerthreshold.def = 0; - cfg->rtriggerthreshold.def = 0; - cfg->lpadsquircling.def = 8000; - cfg->rpadsquircling.def = 8000; - - // apply defaults - cfg->from_default(); -} - -void keyboard_pad_handler::Key(const u32 code, bool pressed, u16 value) -{ - if (!pad::g_enabled) - { - return; - } - - value = Clamp0To255(value); - - for (auto& pad : m_pads_internal) - { - const auto register_new_button_value = [&](std::map& pressed_keys) -> u16 - { - u16 actual_value = 0; - - // Make sure we keep this button pressed until all related keys are released. - if (pressed) - { - pressed_keys[code] = value; - } - else - { - pressed_keys.erase(code); - } - - // Get the max value of all pressed keys for this button - for (const auto& [key, val] : pressed_keys) - { - actual_value = std::max(actual_value, val); - } - - return actual_value; - }; - - // Find out if special buttons are pressed (introduced by RPCS3). - // Activate the buttons here if possible since keys don't auto-repeat. This ensures that they are already pressed in the following loop. - if (pad.m_pressure_intensity_button_index >= 0) - { - Button& pressure_intensity_button = pad.m_buttons[pad.m_pressure_intensity_button_index]; - - if (pressure_intensity_button.m_key_codes.contains(code)) - { - const u16 actual_value = register_new_button_value(pressure_intensity_button.m_pressed_keys); - - pressure_intensity_button.m_pressed = actual_value > 0; - pressure_intensity_button.m_value = actual_value; - } - } - - const bool adjust_pressure = pad.get_pressure_intensity_button_active(m_pressure_intensity_toggle_mode, pad.m_player_id); - const bool adjust_pressure_changed = pad.m_adjust_pressure_last != adjust_pressure; - - if (adjust_pressure_changed) - { - pad.m_adjust_pressure_last = adjust_pressure; - } - - if (pad.m_analog_limiter_button_index >= 0) - { - Button& analog_limiter_button = pad.m_buttons[pad.m_analog_limiter_button_index]; - - if (analog_limiter_button.m_key_codes.contains(code)) - { - const u16 actual_value = register_new_button_value(analog_limiter_button.m_pressed_keys); - - analog_limiter_button.m_pressed = actual_value > 0; - analog_limiter_button.m_value = actual_value; - } - } - - const bool analog_limiter_enabled = pad.get_analog_limiter_button_active(m_analog_limiter_toggle_mode, pad.m_player_id); - const bool analog_limiter_changed = pad.m_analog_limiter_enabled_last != analog_limiter_enabled; - const u32 l_stick_multiplier = analog_limiter_enabled ? m_l_stick_multiplier : 100; - const u32 r_stick_multiplier = analog_limiter_enabled ? m_r_stick_multiplier : 100; - - if (analog_limiter_changed) - { - pad.m_analog_limiter_enabled_last = analog_limiter_enabled; - } - - // Handle buttons - for (usz i = 0; i < pad.m_buttons.size(); i++) - { - // Ignore special buttons - if (static_cast(i) == pad.m_pressure_intensity_button_index || - static_cast(i) == pad.m_analog_limiter_button_index) - continue; - - Button& button = pad.m_buttons[i]; - - bool update_button = true; - - if (!button.m_key_codes.contains(code)) - { - // Handle pressure changes anyway - update_button = adjust_pressure_changed; - } - else - { - button.m_actual_value = register_new_button_value(button.m_pressed_keys); - - // to get the fastest response time possible we don't wanna use any lerp with factor 1 - if (button.m_analog) - { - update_button = m_analog_lerp_factor >= 1.0f; - } - else if (button.m_trigger) - { - update_button = m_trigger_lerp_factor >= 1.0f; - } - } - - if (!update_button) - continue; - - if (button.m_actual_value > 0) - { - // Modify pressure if necessary if the button was pressed - if (adjust_pressure) - { - button.m_value = pad.m_pressure_intensity; - } - else if (m_pressure_intensity_deadzone > 0) - { - button.m_value = NormalizeDirectedInput(button.m_actual_value, m_pressure_intensity_deadzone, 255); - } - else - { - button.m_value = button.m_actual_value; - } - - button.m_pressed = button.m_value > 0; - } - else - { - button.m_value = 0; - button.m_pressed = false; - } - } - - // Handle sticks - for (usz i = 0; i < pad.m_sticks.size(); i++) - { - AnalogStick& stick = pad.m_sticks[i]; - - const bool is_left_stick = i < 2; - - const bool is_max = stick.m_key_codes_max.contains(code); - const bool is_min = stick.m_key_codes_min.contains(code); - - if (!is_max && !is_min) - { - if (!analog_limiter_changed) - continue; - - // Update already pressed sticks - const bool is_min_pressed = !stick.m_pressed_keys_min.empty(); - const bool is_max_pressed = !stick.m_pressed_keys_max.empty(); - - const u32 stick_multiplier = is_left_stick ? l_stick_multiplier : r_stick_multiplier; - - const u16 actual_min_value = is_min_pressed ? MultipliedInput(255, stick_multiplier) : 255; - const u16 normalized_min_value = std::ceil(actual_min_value / 2.0); - - const u16 actual_max_value = is_max_pressed ? MultipliedInput(255, stick_multiplier) : 255; - const u16 normalized_max_value = std::ceil(actual_max_value / 2.0); - - m_stick_min[i] = is_min_pressed ? std::min(normalized_min_value, 128) : 0; - m_stick_max[i] = is_max_pressed ? std::min(128 + normalized_max_value, 255) : 128; - } - else - { - const u16 actual_value = pressed ? MultipliedInput(value, is_left_stick ? l_stick_multiplier : r_stick_multiplier) : value; - u16 normalized_value = std::ceil(actual_value / 2.0); - - const auto register_new_stick_value = [&](std::map& pressed_keys, bool is_max) - { - // Make sure we keep this stick pressed until all related keys are released. - if (pressed) - { - pressed_keys[code] = normalized_value; - } - else - { - pressed_keys.erase(code); - } - - // Get the min/max value of all pressed keys for this stick - for (const auto& [key, val] : pressed_keys) - { - normalized_value = is_max ? std::max(normalized_value, val) : std::min(normalized_value, val); - } - }; - - if (is_max) - { - register_new_stick_value(stick.m_pressed_keys_max, true); - - const bool is_max_pressed = !stick.m_pressed_keys_max.empty(); - - m_stick_max[i] = is_max_pressed ? std::min(128 + normalized_value, 255) : 128; - } - - if (is_min) - { - register_new_stick_value(stick.m_pressed_keys_min, false); - - const bool is_min_pressed = !stick.m_pressed_keys_min.empty(); - - m_stick_min[i] = is_min_pressed ? std::min(normalized_value, 128) : 0; - } - } - - m_stick_val[i] = m_stick_max[i] - m_stick_min[i]; - - const f32 stick_lerp_factor = is_left_stick ? m_l_stick_lerp_factor : m_r_stick_lerp_factor; - - // to get the fastest response time possible we don't wanna use any lerp with factor 1 - if (stick_lerp_factor >= 1.0f) - { - stick.m_value = m_stick_val[i]; - } - } - } -} - -void keyboard_pad_handler::release_all_keys() -{ - for (auto& pad : m_pads_internal) - { - for (Button& button : pad.m_buttons) - { - button.m_pressed = false; - button.m_value = 0; - button.m_actual_value = 0; - } - - for (usz i = 0; i < pad.m_sticks.size(); i++) - { - m_stick_min[i] = 0; - m_stick_max[i] = 128; - m_stick_val[i] = 128; - pad.m_sticks[i].m_value = 128; - } - } - - m_keys_released = true; -} - -bool keyboard_pad_handler::eventFilter(QObject* target, QEvent* ev) -{ - if (!ev) [[unlikely]] - { - return false; - } - - if (input::g_active_mouse_and_keyboard != input::active_mouse_and_keyboard::pad) - { - if (!m_keys_released) - { - release_all_keys(); - } - return false; - } - - m_keys_released = false; - - // !m_target is for future proofing when gsrender isn't automatically initialized on load. - // !m_target->isVisible() is a hack since currently a guiless application will STILL inititialize a gsrender (providing a valid target) - if (!m_target || !m_target->isVisible() || target == m_target) - { - switch (ev->type()) - { - case QEvent::KeyPress: - keyPressEvent(static_cast(ev)); - break; - case QEvent::KeyRelease: - keyReleaseEvent(static_cast(ev)); - break; - case QEvent::MouseButtonPress: - mousePressEvent(static_cast(ev)); - break; - case QEvent::MouseButtonRelease: - mouseReleaseEvent(static_cast(ev)); - break; - case QEvent::MouseMove: - mouseMoveEvent(static_cast(ev)); - break; - case QEvent::Wheel: - mouseWheelEvent(static_cast(ev)); - break; - case QEvent::FocusOut: - release_all_keys(); - break; - default: - break; - } - } - return false; -} - -/* Sets the target window for the event handler, and also installs an event filter on the target. */ -void keyboard_pad_handler::SetTargetWindow(QWindow* target) -{ - if (target != nullptr) - { - m_target = target; - target->installEventFilter(this); - } - else - { - QApplication::instance()->installEventFilter(this); - // If this is hit, it probably means that some refactoring occurs because currently a gsframe is created in Load. - // We still want events so filter from application instead since target is null. - input_log.error("Trying to set pad handler to a null target window."); - } -} - -void keyboard_pad_handler::processKeyEvent(QKeyEvent* event, bool pressed) -{ - if (!event) [[unlikely]] - { - return; - } - - if (event->isAutoRepeat()) - { - event->ignore(); - return; - } - - const auto handle_key = [this, pressed, event]() - { - QStringList list = GetKeyNames(event); - if (list.isEmpty()) - return; - - const bool is_num_key = list.removeAll("Num") > 0; - const QString name = QString::fromStdString(GetKeyName(event, true)); - - // TODO: Edge case: switching numlock keeps numpad keys pressed due to now different modifier - - // Handle every possible key combination, for example: ctrl+A -> {ctrl, A, ctrl+A} - for (const QString& keyname : list) - { - // skip the 'original keys' when handling numpad keys - if (is_num_key && !keyname.contains("Num")) - continue; - // skip held modifiers when handling another key - if (keyname != name && list.count() > 1 && (keyname == "Alt" || keyname == "AltGr" || keyname == "Ctrl" || keyname == "Meta" || keyname == "Shift")) - continue; - Key(GetKeyCode(keyname), pressed); - } - }; - - handle_key(); - event->ignore(); -} - -void keyboard_pad_handler::keyPressEvent(QKeyEvent* event) -{ - if (!event) [[unlikely]] - { - return; - } - - if (event->modifiers() & Qt::AltModifier) - { - switch (event->key()) - { - case Qt::Key_I: - m_deadzone_y = std::min(m_deadzone_y + 1, 255); - input_log.success("mouse move adjustment: deadzone y = %d", m_deadzone_y); - event->ignore(); - return; - case Qt::Key_U: - m_deadzone_y = std::max(0, m_deadzone_y - 1); - input_log.success("mouse move adjustment: deadzone y = %d", m_deadzone_y); - event->ignore(); - return; - case Qt::Key_Y: - m_deadzone_x = std::min(m_deadzone_x + 1, 255); - input_log.success("mouse move adjustment: deadzone x = %d", m_deadzone_x); - event->ignore(); - return; - case Qt::Key_T: - m_deadzone_x = std::max(0, m_deadzone_x - 1); - input_log.success("mouse move adjustment: deadzone x = %d", m_deadzone_x); - event->ignore(); - return; - case Qt::Key_K: - m_multi_y = std::min(m_multi_y + 0.1, 5.0); - input_log.success("mouse move adjustment: multiplier y = %d", static_cast(m_multi_y * 100)); - event->ignore(); - return; - case Qt::Key_J: - m_multi_y = std::max(0.0, m_multi_y - 0.1); - input_log.success("mouse move adjustment: multiplier y = %d", static_cast(m_multi_y * 100)); - event->ignore(); - return; - case Qt::Key_H: - m_multi_x = std::min(m_multi_x + 0.1, 5.0); - input_log.success("mouse move adjustment: multiplier x = %d", static_cast(m_multi_x * 100)); - event->ignore(); - return; - case Qt::Key_G: - m_multi_x = std::max(0.0, m_multi_x - 0.1); - input_log.success("mouse move adjustment: multiplier x = %d", static_cast(m_multi_x * 100)); - event->ignore(); - return; - default: - break; - } - } - processKeyEvent(event, true); -} - -void keyboard_pad_handler::keyReleaseEvent(QKeyEvent* event) -{ - processKeyEvent(event, false); -} - -void keyboard_pad_handler::mousePressEvent(QMouseEvent* event) -{ - if (!event) [[unlikely]] - { - return; - } - - Key(event->button(), true); - event->ignore(); -} - -void keyboard_pad_handler::mouseReleaseEvent(QMouseEvent* event) -{ - if (!event) [[unlikely]] - { - return; - } - - Key(event->button(), false, 0); - event->ignore(); -} - -bool keyboard_pad_handler::get_mouse_lock_state() const -{ - if (gs_frame* game_frame = dynamic_cast(m_target)) - return game_frame->get_mouse_lock_state(); - return false; -} - -void keyboard_pad_handler::mouseMoveEvent(QMouseEvent* event) -{ - if (!m_mouse_move_used || !event) - { - event->ignore(); - return; - } - - static int movement_x = 0; - static int movement_y = 0; - - if (m_target && m_target->isActive() && get_mouse_lock_state()) - { - // get the screen dimensions - const QSize screen = m_target->size(); - - // get the center of the screen in global coordinates - QPoint p_center = m_target->geometry().topLeft() + QPoint(screen.width() / 2, screen.height() / 2); - - // reset the mouse to the center for consistent results since edge movement won't be registered - QCursor::setPos(m_target->screen(), p_center); - - // convert the center into screen coordinates - p_center = m_target->mapFromGlobal(p_center); - - // get the delta of the mouse position to the screen center - const QPoint p_delta = event->pos() - p_center; - - if (m_mouse_movement_mode == mouse_movement_mode::relative) - { - movement_x = p_delta.x(); - movement_y = p_delta.y(); - } - else - { - // current mouse position, starting at the center - static QPoint p_real(p_center); - - // update the current position without leaving the screen borders - p_real.setX(std::clamp(p_real.x() + p_delta.x(), 0, screen.width())); - p_real.setY(std::clamp(p_real.y() + p_delta.y(), 0, screen.height())); - - // get the delta of the real mouse position to the screen center - const QPoint p_real_delta = p_real - p_center; - - movement_x = p_real_delta.x(); - movement_y = p_real_delta.y(); - } - } - else if (m_mouse_movement_mode == mouse_movement_mode::relative) - { - static int last_pos_x = 0; - static int last_pos_y = 0; - const QPoint e_pos = event->pos(); - - movement_x = e_pos.x() - last_pos_x; - movement_y = e_pos.y() - last_pos_y; - - last_pos_x = e_pos.x(); - last_pos_y = e_pos.y(); - } - else if (m_target && m_target->isActive()) - { - // get the screen dimensions - const QSize screen = m_target->size(); - - // get the center of the screen in global coordinates - QPoint p_center = m_target->geometry().topLeft() + QPoint(screen.width() / 2, screen.height() / 2); - - // convert the center into screen coordinates - p_center = m_target->mapFromGlobal(p_center); - - // get the delta of the mouse position to the screen center - const QPoint p_delta = event->pos() - p_center; - - movement_x = p_delta.x(); - movement_y = p_delta.y(); - } - - movement_x *= m_multi_x; - movement_y *= m_multi_y; - - int deadzone_x = 0; - int deadzone_y = 0; - - if (movement_x == 0 && movement_y != 0) - { - deadzone_y = m_deadzone_y; - } - else if (movement_y == 0 && movement_x != 0) - { - deadzone_x = m_deadzone_x; - } - else if (movement_x != 0 && movement_y != 0 && m_deadzone_x != 0 && m_deadzone_y != 0) - { - // Calculate the point on our deadzone ellipsis intersected with the line (0, 0)(movement_x, movement_y) - // Ellipsis: 1 = (x²/a²) + (y²/b²) ; where: a = m_deadzone_x and b = m_deadzone_y - // Line: y = mx + t ; where: t = 0 and m = (movement_y / movement_x) - // Combined: x = +-(a*b)/sqrt(a²m²+b²) ; where +- is always +, since we only want the magnitude - - const double a = m_deadzone_x; - const double b = m_deadzone_y; - const double m = static_cast(movement_y) / static_cast(movement_x); - - deadzone_x = a * b / std::sqrt(std::pow(a, 2) * std::pow(m, 2) + std::pow(b, 2)); - deadzone_y = std::abs(m * deadzone_x); - } - - if (movement_x < 0) - { - Key(mouse::move_right, false); - Key(mouse::move_left, true, std::min(deadzone_x + std::abs(movement_x), 255)); - m_last_mouse_move_left = steady_clock::now(); - } - else if (movement_x > 0) - { - Key(mouse::move_left, false); - Key(mouse::move_right, true, std::min(deadzone_x + movement_x, 255)); - m_last_mouse_move_right = steady_clock::now(); - } - - // in Qt mouse up is equivalent to movement_y < 0 - if (movement_y < 0) - { - Key(mouse::move_down, false); - Key(mouse::move_up, true, std::min(deadzone_y + std::abs(movement_y), 255)); - m_last_mouse_move_up = steady_clock::now(); - } - else if (movement_y > 0) - { - Key(mouse::move_up, false); - Key(mouse::move_down, true, std::min(deadzone_y + movement_y, 255)); - m_last_mouse_move_down = steady_clock::now(); - } - - event->ignore(); -} - -void keyboard_pad_handler::mouseWheelEvent(QWheelEvent* event) -{ - if (!m_mouse_wheel_used || !event) - { - return; - } - - const QPoint direction = event->angleDelta(); - - if (direction.isNull()) - { - // Scrolling started/ended event, no direction given - return; - } - - if (const int x = direction.x()) - { - const bool to_left = event->inverted() ? x < 0 : x > 0; - - if (to_left) - { - Key(mouse::wheel_left, true); - m_last_wheel_move_left = steady_clock::now(); - } - else - { - Key(mouse::wheel_right, true); - m_last_wheel_move_right = steady_clock::now(); - } - } - if (const int y = direction.y()) - { - const bool to_up = event->inverted() ? y < 0 : y > 0; - - if (to_up) - { - Key(mouse::wheel_up, true); - m_last_wheel_move_up = steady_clock::now(); - } - else - { - Key(mouse::wheel_down, true); - m_last_wheel_move_down = steady_clock::now(); - } - } -} - -std::vector keyboard_pad_handler::list_devices() -{ - std::vector list_devices; - list_devices.emplace_back(std::string(pad::keyboard_device_name), false); - return list_devices; -} - -std::string keyboard_pad_handler::GetMouseName(const QMouseEvent* event) -{ - return GetMouseName(event->button()); -} - -std::string keyboard_pad_handler::GetMouseName(u32 button) -{ - if (const auto it = mouse_list.find(button); it != mouse_list.cend()) - return it->second; - return "FAIL"; -} - -QStringList keyboard_pad_handler::GetKeyNames(const QKeyEvent* keyEvent) -{ - QStringList list; - - if (keyEvent->modifiers() & Qt::ShiftModifier) - { - list.append("Shift"); - list.append(QKeySequence(keyEvent->key() | Qt::ShiftModifier).toString(QKeySequence::NativeText)); - } - if (keyEvent->modifiers() & Qt::AltModifier) - { - list.append("Alt"); - list.append(QKeySequence(keyEvent->key() | Qt::AltModifier).toString(QKeySequence::NativeText)); - } - if (keyEvent->modifiers() & Qt::ControlModifier) - { - list.append("Ctrl"); - list.append(QKeySequence(keyEvent->key() | Qt::ControlModifier).toString(QKeySequence::NativeText)); - } - if (keyEvent->modifiers() & Qt::MetaModifier) - { - list.append("Meta"); - list.append(QKeySequence(keyEvent->key() | Qt::MetaModifier).toString(QKeySequence::NativeText)); - } - if (keyEvent->modifiers() & Qt::KeypadModifier) - { - list.append("Num"); // helper object, not used as actual key - list.append(QKeySequence(keyEvent->key() | Qt::KeypadModifier).toString(QKeySequence::NativeText)); - } - - // Handle special cases - if (const std::string name = native_scan_code_to_string(keyEvent->nativeScanCode()); !name.empty()) - { - list.append(QString::fromStdString(name)); - } - - switch (keyEvent->key()) - { - case Qt::Key_Alt: - list.append("Alt"); - break; - case Qt::Key_AltGr: - list.append("AltGr"); - break; - case Qt::Key_Shift: - list.append("Shift"); - break; - case Qt::Key_Control: - list.append("Ctrl"); - break; - case Qt::Key_Meta: - list.append("Meta"); - break; - default: - list.append(QKeySequence(keyEvent->key()).toString(QKeySequence::NativeText)); - break; - } - - list.removeDuplicates(); - return list; -} - -std::string keyboard_pad_handler::GetKeyName(const QKeyEvent* keyEvent, bool with_modifiers) -{ - // Handle special cases first - if (std::string name = native_scan_code_to_string(keyEvent->nativeScanCode()); !name.empty()) - { - return name; - } - - switch (keyEvent->key()) - { - case Qt::Key_Alt: return "Alt"; - case Qt::Key_AltGr: return "AltGr"; - case Qt::Key_Shift: return "Shift"; - case Qt::Key_Control: return "Ctrl"; - case Qt::Key_Meta: return "Meta"; - case Qt::Key_NumLock: return QKeySequence(keyEvent->key()).toString(QKeySequence::NativeText).toStdString(); -#ifdef __APPLE__ - // On macOS, the arrow keys are considered to be part of the keypad; - // since most Mac keyboards lack a keypad to begin with, - // we change them to regular arrows to avoid confusion - case Qt::Key_Left: return "←"; - case Qt::Key_Up: return "↑"; - case Qt::Key_Right: return "→"; - case Qt::Key_Down: return "↓"; -#endif - default: - break; - } - - if (with_modifiers) - { - return QKeySequence(keyEvent->key() | keyEvent->modifiers()).toString(QKeySequence::NativeText).toStdString(); - } - - return QKeySequence(keyEvent->key()).toString(QKeySequence::NativeText).toStdString(); -} - -std::string keyboard_pad_handler::GetKeyName(const u32& keyCode) -{ - return QKeySequence(keyCode).toString(QKeySequence::NativeText).toStdString(); -} - -std::set keyboard_pad_handler::GetKeyCodes(const cfg::string& cfg_string) -{ - std::set key_codes; - for (const std::string& key_name : cfg_pad::get_buttons(cfg_string)) - { - if (u32 code = GetKeyCode(QString::fromStdString(key_name)); code != Qt::NoButton) - { - key_codes.insert(code); - } - } - return key_codes; -} - -u32 keyboard_pad_handler::GetKeyCode(const QString& keyName) -{ - if (keyName.isEmpty()) - return Qt::NoButton; - if (const int native_scan_code = native_scan_code_from_string(keyName.toStdString()); native_scan_code >= 0) - return Qt::Key_unknown + native_scan_code; // Special cases that can't be expressed with Qt::Key - if (keyName == "Alt") - return Qt::Key_Alt; - if (keyName == "AltGr") - return Qt::Key_AltGr; - if (keyName == "Shift") - return Qt::Key_Shift; - if (keyName == "Ctrl") - return Qt::Key_Control; - if (keyName == "Meta") - return Qt::Key_Meta; -#ifdef __APPLE__ - // QKeySequence doesn't work properly for the arrow keys on macOS - if (keyName == "Num←") - return Qt::Key_Left; - if (keyName == "Num↑") - return Qt::Key_Up; - if (keyName == "Num→") - return Qt::Key_Right; - if (keyName == "Num↓") - return Qt::Key_Down; -#endif - - const QKeySequence seq(keyName); - u32 key_code = Qt::NoButton; - - if (seq.count() == 1 && seq[0].key() != Qt::Key_unknown) - key_code = seq[0].key(); - else - input_log.notice("GetKeyCode(%s): seq.count() = %d", keyName, seq.count()); - - return key_code; -} - -int keyboard_pad_handler::native_scan_code_from_string([[maybe_unused]] const std::string& key) -{ - // NOTE: Qt throws a Ctrl key at us when using Alt Gr first, so right Alt does not work at the moment - if (key == "Shift Left") - return native_key::shift_l; - if (key == "Shift Right") - return native_key::shift_r; - if (key == "Ctrl Left") - return native_key::ctrl_l; - if (key == "Ctrl Right") - return native_key::ctrl_r; - if (key == "Alt Left") - return native_key::alt_l; - if (key == "Alt Right") - return native_key::alt_r; - if (key == "Meta Left") - return native_key::meta_l; - if (key == "Meta Right") - return native_key::meta_r; -#ifdef _WIN32 - if (key == "Num+0" || key == "Num+Ins") - return 82; - if (key == "Num+1" || key == "Num+End") - return 79; - if (key == "Num+2" || key == "Num+Down") - return 80; - if (key == "Num+3" || key == "Num+PgDown") - return 81; - if (key == "Num+4" || key == "Num+Left") - return 75; - if (key == "Num+5" || key == "Num+Clear") - return 76; - if (key == "Num+6" || key == "Num+Right") - return 77; - if (key == "Num+7" || key == "Num+Home") - return 71; - if (key == "Num+8" || key == "Num+Up") - return 72; - if (key == "Num+9" || key == "Num+PgUp") - return 73; - if (key == "Num+," || key == "Num+Del") - return 83; - if (key == "Num+/") - return 309; - if (key == "Num+*") - return 55; - if (key == "Num+-") - return 74; - if (key == "Num++") - return 78; - if (key == "Num+Enter") - return 284; -#else - // TODO -#endif - return -1; -} - -std::string keyboard_pad_handler::native_scan_code_to_string(int native_scan_code) -{ - // NOTE: the other Qt function "nativeVirtualKey" does not distinguish between VK_SHIFT and VK_RSHIFT key in Qt at the moment - // NOTE: Qt throws a Ctrl key at us when using Alt Gr first, so right Alt does not work at the moment - // NOTE: for MacOs: nativeScanCode may not work - switch (native_scan_code) - { - case native_key::shift_l: return "Shift Left"; - case native_key::shift_r: return "Shift Right"; - case native_key::ctrl_l: return "Ctrl Left"; - case native_key::ctrl_r: return "Ctrl Right"; - case native_key::alt_l: return "Alt Left"; - case native_key::alt_r: return "Alt Right"; - case native_key::meta_l: return "Meta Left"; - case native_key::meta_r: return "Meta Right"; -#ifdef _WIN32 - case 82: return "Num+0"; // Also "Num+Ins" depending on numlock - case 79: return "Num+1"; // Also "Num+End" depending on numlock - case 80: return "Num+2"; // Also "Num+Down" depending on numlock - case 81: return "Num+3"; // Also "Num+PgDown" depending on numlock - case 75: return "Num+4"; // Also "Num+Left" depending on numlock - case 76: return "Num+5"; // Also "Num+Clear" depending on numlock - case 77: return "Num+6"; // Also "Num+Right" depending on numlock - case 71: return "Num+7"; // Also "Num+Home" depending on numlock - case 72: return "Num+8"; // Also "Num+Up" depending on numlock - case 73: return "Num+9"; // Also "Num+PgUp" depending on numlock - case 83: return "Num+,"; // Also "Num+Del" depending on numlock - case 309: return "Num+/"; - case 55: return "Num+*"; - case 74: return "Num+-"; - case 78: return "Num++"; - case 284: return "Num+Enter"; -#else - // TODO -#endif - default: return ""; - } -} - -bool keyboard_pad_handler::bindPadToDevice(std::shared_ptr pad) -{ - if (!pad || pad->m_player_id >= g_cfg_input.player.size()) - return false; - - const cfg_player* player_config = g_cfg_input.player[pad->m_player_id]; - if (!player_config || player_config->device.to_string() != pad::keyboard_device_name) - return false; - - m_pad_configs[pad->m_player_id].from_string(player_config->config.to_string()); - const cfg_pad* cfg = &m_pad_configs[pad->m_player_id]; - if (cfg == nullptr) - return false; - - m_mouse_movement_mode = cfg->mouse_move_mode; - m_mouse_move_used = false; - m_mouse_wheel_used = false; - m_deadzone_x = cfg->mouse_deadzone_x; - m_deadzone_y = cfg->mouse_deadzone_y; - m_multi_x = cfg->mouse_acceleration_x / 100.0; - m_multi_y = cfg->mouse_acceleration_y / 100.0; - m_l_stick_lerp_factor = cfg->l_stick_lerp_factor / 100.0f; - m_r_stick_lerp_factor = cfg->r_stick_lerp_factor / 100.0f; - m_analog_lerp_factor = cfg->analog_lerp_factor / 100.0f; - m_trigger_lerp_factor = cfg->trigger_lerp_factor / 100.0f; - m_l_stick_multiplier = cfg->lstickmultiplier; - m_r_stick_multiplier = cfg->rstickmultiplier; - m_analog_limiter_toggle_mode = cfg->analog_limiter_toggle_mode.get(); - m_pressure_intensity_toggle_mode = cfg->pressure_intensity_toggle_mode.get(); - m_pressure_intensity_deadzone = cfg->pressure_intensity_deadzone.get(); - - const auto find_keys = [this](const cfg::string& name) - { - std::set keys = FindKeyCodes(mouse_list, name, false); - for (const u32& key : GetKeyCodes(name)) - keys.insert(key); - - if (!keys.empty()) - { - if (!m_mouse_move_used && (keys.contains(mouse::move_left) || keys.contains(mouse::move_right) || keys.contains(mouse::move_up) || keys.contains(mouse::move_down))) - { - m_mouse_move_used = true; - } - else if (!m_mouse_wheel_used && (keys.contains(mouse::wheel_left) || keys.contains(mouse::wheel_right) || keys.contains(mouse::wheel_up) || keys.contains(mouse::wheel_down))) - { - m_mouse_wheel_used = true; - } - } - return keys; - }; - - u32 pclass_profile = 0x0; - - for (const input::product_info& product : input::get_products_by_class(cfg->device_class_type)) - { - if (product.vendor_id == cfg->vendor_id && product.product_id == cfg->product_id) - { - pclass_profile = product.pclass_profile; - } - } - - // Fixed assign change, default is both sensor and press off - pad->Init( - CELL_PAD_STATUS_DISCONNECTED, - CELL_PAD_CAPABILITY_PS3_CONFORMITY | CELL_PAD_CAPABILITY_PRESS_MODE | CELL_PAD_CAPABILITY_HP_ANALOG_STICK | CELL_PAD_CAPABILITY_ACTUATOR | CELL_PAD_CAPABILITY_SENSOR_MODE, - CELL_PAD_DEV_TYPE_STANDARD, - cfg->device_class_type, - pclass_profile, - cfg->vendor_id, - cfg->product_id, - cfg->pressure_intensity); - - if (b_has_pressure_intensity_button) - { - pad->m_buttons.emplace_back(special_button_offset, find_keys(cfg->pressure_intensity_button), special_button_value::pressure_intensity); - pad->m_pressure_intensity_button_index = static_cast(pad->m_buttons.size()) - 1; - } - - if (b_has_analog_limiter_button) - { - pad->m_buttons.emplace_back(special_button_offset, find_keys(cfg->analog_limiter_button), special_button_value::analog_limiter); - pad->m_analog_limiter_button_index = static_cast(pad->m_buttons.size()) - 1; - } - - pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL1, find_keys(cfg->left), CELL_PAD_CTRL_LEFT); - pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL1, find_keys(cfg->down), CELL_PAD_CTRL_DOWN); - pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL1, find_keys(cfg->right), CELL_PAD_CTRL_RIGHT); - pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL1, find_keys(cfg->up), CELL_PAD_CTRL_UP); - pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL1, find_keys(cfg->start), CELL_PAD_CTRL_START); - pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL1, find_keys(cfg->r3), CELL_PAD_CTRL_R3); - pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL1, find_keys(cfg->l3), CELL_PAD_CTRL_L3); - pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL1, find_keys(cfg->select), CELL_PAD_CTRL_SELECT); - pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL1, find_keys(cfg->ps), CELL_PAD_CTRL_PS); - pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL2, find_keys(cfg->square), CELL_PAD_CTRL_SQUARE); - pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL2, find_keys(cfg->cross), CELL_PAD_CTRL_CROSS); - pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL2, find_keys(cfg->circle), CELL_PAD_CTRL_CIRCLE); - pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL2, find_keys(cfg->triangle), CELL_PAD_CTRL_TRIANGLE); - pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL2, find_keys(cfg->r1), CELL_PAD_CTRL_R1); - pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL2, find_keys(cfg->l1), CELL_PAD_CTRL_L1); - pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL2, find_keys(cfg->r2), CELL_PAD_CTRL_R2); - pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_DIGITAL2, find_keys(cfg->l2), CELL_PAD_CTRL_L2); - - if (pad->m_class_type == CELL_PAD_PCLASS_TYPE_SKATEBOARD) - { - pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_PRESS_PIGGYBACK, find_keys(cfg->ir_nose), CELL_PAD_CTRL_PRESS_TRIANGLE); - pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_PRESS_PIGGYBACK, find_keys(cfg->ir_tail), CELL_PAD_CTRL_PRESS_CIRCLE); - pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_PRESS_PIGGYBACK, find_keys(cfg->ir_left), CELL_PAD_CTRL_PRESS_CROSS); - pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_PRESS_PIGGYBACK, find_keys(cfg->ir_right), CELL_PAD_CTRL_PRESS_SQUARE); - pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_PRESS_PIGGYBACK, find_keys(cfg->tilt_left), CELL_PAD_CTRL_PRESS_L1); - pad->m_buttons.emplace_back(CELL_PAD_BTN_OFFSET_PRESS_PIGGYBACK, find_keys(cfg->tilt_right), CELL_PAD_CTRL_PRESS_R1); - } - - pad->m_sticks[0] = AnalogStick(CELL_PAD_BTN_OFFSET_ANALOG_LEFT_X, find_keys(cfg->ls_left), find_keys(cfg->ls_right)); - pad->m_sticks[1] = AnalogStick(CELL_PAD_BTN_OFFSET_ANALOG_LEFT_Y, find_keys(cfg->ls_up), find_keys(cfg->ls_down)); - pad->m_sticks[2] = AnalogStick(CELL_PAD_BTN_OFFSET_ANALOG_RIGHT_X, find_keys(cfg->rs_left), find_keys(cfg->rs_right)); - pad->m_sticks[3] = AnalogStick(CELL_PAD_BTN_OFFSET_ANALOG_RIGHT_Y, find_keys(cfg->rs_up), find_keys(cfg->rs_down)); - - pad->m_sensors[0] = AnalogSensor(CELL_PAD_BTN_OFFSET_SENSOR_X, 0, 0, 0, DEFAULT_MOTION_X); - pad->m_sensors[1] = AnalogSensor(CELL_PAD_BTN_OFFSET_SENSOR_Y, 0, 0, 0, DEFAULT_MOTION_Y); - pad->m_sensors[2] = AnalogSensor(CELL_PAD_BTN_OFFSET_SENSOR_Z, 0, 0, 0, DEFAULT_MOTION_Z); - pad->m_sensors[3] = AnalogSensor(CELL_PAD_BTN_OFFSET_SENSOR_G, 0, 0, 0, DEFAULT_MOTION_G); - - pad->m_vibrateMotors[0] = VibrateMotor(true, 0); - pad->m_vibrateMotors[1] = VibrateMotor(false, 0); - - m_bindings.emplace_back(pad, nullptr, nullptr); - m_pads_internal.push_back(*pad); - - return true; -} - -void keyboard_pad_handler::process() -{ - constexpr double stick_interval = 10.0; - constexpr double button_interval = 10.0; - - const auto now = steady_clock::now(); - - const double elapsed_stick = std::chrono::duration_cast(now - m_stick_time).count() / 1000.0; - const double elapsed_button = std::chrono::duration_cast(now - m_button_time).count() / 1000.0; - - const bool update_sticks = elapsed_stick > stick_interval; - const bool update_buttons = elapsed_button > button_interval; - - if (update_sticks) - { - m_stick_time = now; - } - - if (update_buttons) - { - m_button_time = now; - } - - if (m_mouse_move_used && m_mouse_movement_mode == mouse_movement_mode::relative) - { - constexpr double mouse_interval = 30.0; - - const double elapsed_left = std::chrono::duration_cast(now - m_last_mouse_move_left).count() / 1000.0; - const double elapsed_right = std::chrono::duration_cast(now - m_last_mouse_move_right).count() / 1000.0; - const double elapsed_up = std::chrono::duration_cast(now - m_last_mouse_move_up).count() / 1000.0; - const double elapsed_down = std::chrono::duration_cast(now - m_last_mouse_move_down).count() / 1000.0; - - // roughly 1-2 frames to process the next mouse move - if (elapsed_left > mouse_interval) - { - Key(mouse::move_left, false); - m_last_mouse_move_left = now; - } - if (elapsed_right > mouse_interval) - { - Key(mouse::move_right, false); - m_last_mouse_move_right = now; - } - if (elapsed_up > mouse_interval) - { - Key(mouse::move_up, false); - m_last_mouse_move_up = now; - } - if (elapsed_down > mouse_interval) - { - Key(mouse::move_down, false); - m_last_mouse_move_down = now; - } - } - - const auto get_lerped = [](f32 v0, f32 v1, f32 lerp_factor) - { - // linear interpolation from the current value v0 to the desired value v1 - const f32 res = std::lerp(v0, v1, lerp_factor); - - // round to the correct direction to prevent sticky values on small factors - return (v0 <= v1) ? std::ceil(res) : std::floor(res); - }; - - for (uint i = 0; i < m_pads_internal.size(); i++) - { - auto& pad = m_pads_internal[i]; - - if (last_connection_status[i] == false) - { - ensure(m_bindings[i].pad); - m_bindings[i].pad->m_port_status |= CELL_PAD_STATUS_CONNECTED; - m_bindings[i].pad->m_port_status |= CELL_PAD_STATUS_ASSIGN_CHANGES; - last_connection_status[i] = true; - connected_devices++; - } - else - { - if (update_sticks) - { - for (usz j = 0; j < pad.m_sticks.size(); j++) - { - const f32 stick_lerp_factor = (j < 2) ? m_l_stick_lerp_factor : m_r_stick_lerp_factor; - - // we already applied the following values on keypress if we used factor 1 - if (stick_lerp_factor < 1.0f) - { - const f32 v0 = static_cast(pad.m_sticks[j].m_value); - const f32 v1 = static_cast(m_stick_val[j]); - const f32 res = get_lerped(v0, v1, stick_lerp_factor); - - pad.m_sticks[j].m_value = static_cast(res); - } - } - } - - if (update_buttons) - { - for (Button& button : pad.m_buttons) - { - if (button.m_analog) - { - // we already applied the following values on keypress if we used factor 1 - if (m_analog_lerp_factor < 1.0f) - { - const f32 v0 = static_cast(button.m_value); - const f32 v1 = static_cast(button.m_actual_value); - const f32 res = get_lerped(v0, v1, m_analog_lerp_factor); - - button.m_value = static_cast(res); - button.m_pressed = button.m_value > 0; - } - } - else if (button.m_trigger) - { - // we already applied the following values on keypress if we used factor 1 - if (m_trigger_lerp_factor < 1.0f) - { - const f32 v0 = static_cast(button.m_value); - const f32 v1 = static_cast(button.m_actual_value); - const f32 res = get_lerped(v0, v1, m_trigger_lerp_factor); - - button.m_value = static_cast(res); - button.m_pressed = button.m_value > 0; - } - } - } - } - } - } - - if (m_mouse_wheel_used) - { - // Releases the wheel buttons 0,1 sec after they've been triggered - // Next activation is set to distant future to avoid activating this on every proc - const auto update_threshold = now - std::chrono::milliseconds(100); - const auto distant_future = now + std::chrono::hours(24); - - if (update_threshold >= m_last_wheel_move_up) - { - Key(mouse::wheel_up, false); - m_last_wheel_move_up = distant_future; - } - if (update_threshold >= m_last_wheel_move_down) - { - Key(mouse::wheel_down, false); - m_last_wheel_move_down = distant_future; - } - if (update_threshold >= m_last_wheel_move_left) - { - Key(mouse::wheel_left, false); - m_last_wheel_move_left = distant_future; - } - if (update_threshold >= m_last_wheel_move_right) - { - Key(mouse::wheel_right, false); - m_last_wheel_move_right = distant_future; - } - } - - for (uint i = 0; i < m_bindings.size(); i++) - { - auto& pad = m_bindings[i].pad; - ensure(pad); - - const cfg_pad* cfg = &m_pad_configs[pad->m_player_id]; - ensure(cfg); - - const Pad& pad_internal = m_pads_internal[i]; - - // Normalize and apply pad squircling - // Copy sticks first. We don't want to modify the raw internal values - std::array squircled_sticks = pad_internal.m_sticks; - - // Apply squircling - if (cfg->lpadsquircling != 0) - { - u16& lx = squircled_sticks[0].m_value; - u16& ly = squircled_sticks[1].m_value; - - ConvertToSquirclePoint(lx, ly, cfg->lpadsquircling); - } - - if (cfg->rpadsquircling != 0) - { - u16& rx = squircled_sticks[2].m_value; - u16& ry = squircled_sticks[3].m_value; - - ConvertToSquirclePoint(rx, ry, cfg->rpadsquircling); - } - - pad->m_buttons = pad_internal.m_buttons; - pad->m_sticks = squircled_sticks; // Don't use std::move here. We assign values lockless, so std::move can lead to segfaults. - } -} diff --git a/rpcs3qt-legacy/Input/keyboard_pad_handler.h b/rpcs3qt-legacy/Input/keyboard_pad_handler.h deleted file mode 100644 index a00b22329..000000000 --- a/rpcs3qt-legacy/Input/keyboard_pad_handler.h +++ /dev/null @@ -1,154 +0,0 @@ -#pragma once - -#include "util/types.hpp" -#include "Emu/Io/PadHandler.h" - -#include -#include -#include -#include -#include - -enum mouse -{ - move_left = 0x05555550, - move_right = 0x05555551, - move_up = 0x05555552, - move_down = 0x05555553, - wheel_up = 0x05555554, - wheel_down = 0x05555555, - wheel_left = 0x05555556, - wheel_right = 0x05555557 -}; - -// Unique button names for the config files and our pad settings dialog -const std::unordered_map mouse_list = - { - {Qt::NoButton, ""}, - {Qt::LeftButton, "Mouse Left"}, - {Qt::RightButton, "Mouse Right"}, - {Qt::MiddleButton, "Mouse Middle"}, - {Qt::BackButton, "Mouse Back"}, - {Qt::ForwardButton, "Mouse Fwd"}, - {Qt::TaskButton, "Mouse Task"}, - {Qt::ExtraButton4, "Mouse 4"}, - {Qt::ExtraButton5, "Mouse 5"}, - {Qt::ExtraButton6, "Mouse 6"}, - {Qt::ExtraButton7, "Mouse 7"}, - {Qt::ExtraButton8, "Mouse 8"}, - {Qt::ExtraButton9, "Mouse 9"}, - {Qt::ExtraButton10, "Mouse 10"}, - {Qt::ExtraButton11, "Mouse 11"}, - {Qt::ExtraButton12, "Mouse 12"}, - {Qt::ExtraButton13, "Mouse 13"}, - {Qt::ExtraButton14, "Mouse 14"}, - {Qt::ExtraButton15, "Mouse 15"}, - {Qt::ExtraButton16, "Mouse 16"}, - {Qt::ExtraButton17, "Mouse 17"}, - {Qt::ExtraButton18, "Mouse 18"}, - {Qt::ExtraButton19, "Mouse 19"}, - {Qt::ExtraButton20, "Mouse 20"}, - {Qt::ExtraButton21, "Mouse 21"}, - {Qt::ExtraButton22, "Mouse 22"}, - {Qt::ExtraButton23, "Mouse 23"}, - {Qt::ExtraButton24, "Mouse 24"}, - - {mouse::move_left, "Mouse MLeft"}, - {mouse::move_right, "Mouse MRight"}, - {mouse::move_up, "Mouse MUp"}, - {mouse::move_down, "Mouse MDown"}, - - {mouse::wheel_up, "Wheel Up"}, - {mouse::wheel_down, "Wheel Down"}, - {mouse::wheel_left, "Wheel Left"}, - {mouse::wheel_right, "Wheel Right"}, -}; - -class keyboard_pad_handler final : public QObject, public PadHandlerBase -{ -public: - bool Init() override; - - keyboard_pad_handler(); - - void SetTargetWindow(QWindow* target); - void processKeyEvent(QKeyEvent* event, bool pressed); - void keyPressEvent(QKeyEvent* event); - void keyReleaseEvent(QKeyEvent* event); - void mousePressEvent(QMouseEvent* event); - void mouseReleaseEvent(QMouseEvent* event); - void mouseMoveEvent(QMouseEvent* event); - void mouseWheelEvent(QWheelEvent* event); - - bool eventFilter(QObject* target, QEvent* ev) override; - - void init_config(cfg_pad* cfg) override; - std::vector list_devices() override; - connection get_next_button_press(const std::string& /*padId*/, const pad_callback& /*callback*/, const pad_fail_callback& /*fail_callback*/, gui_call_type /*call_type*/, const std::vector& /*buttons*/) override - { - return connection::connected; - } - bool bindPadToDevice(std::shared_ptr pad) override; - void process() override; - - static std::string GetMouseName(const QMouseEvent* event); - static std::string GetMouseName(u32 button); - static QStringList GetKeyNames(const QKeyEvent* keyEvent); - static std::string GetKeyName(const QKeyEvent* keyEvent, bool with_modifiers); - static std::string GetKeyName(const u32& keyCode); - static std::set GetKeyCodes(const cfg::string& cfg_string); - static u32 GetKeyCode(const QString& keyName); - - static int native_scan_code_from_string(const std::string& key); - static std::string native_scan_code_to_string(int native_scan_code); - -protected: - void Key(const u32 code, bool pressed, u16 value = 255); - -private: - QWindow* m_target = nullptr; - mouse_movement_mode m_mouse_movement_mode = mouse_movement_mode::relative; - bool m_keys_released = false; - bool m_mouse_move_used = false; - bool m_mouse_wheel_used = false; - bool get_mouse_lock_state() const; - void release_all_keys(); - - std::vector m_pads_internal; // Accumulates input until the next poll. Only used for user input! - - // Button Movements - steady_clock::time_point m_button_time; - f32 m_analog_lerp_factor = 1.0f; - f32 m_trigger_lerp_factor = 1.0f; - bool m_analog_limiter_toggle_mode = false; - bool m_pressure_intensity_toggle_mode = false; - u32 m_pressure_intensity_deadzone = 0; - - // Stick Movements - steady_clock::time_point m_stick_time; - f32 m_l_stick_lerp_factor = 1.0f; - f32 m_r_stick_lerp_factor = 1.0f; - u32 m_l_stick_multiplier = 100; - u32 m_r_stick_multiplier = 100; - - static constexpr usz max_sticks = 4; - std::array m_stick_min{0, 0, 0, 0}; - std::array m_stick_max{128, 128, 128, 128}; - std::array m_stick_val{128, 128, 128, 128}; - - // Mouse Movements - steady_clock::time_point m_last_mouse_move_left; - steady_clock::time_point m_last_mouse_move_right; - steady_clock::time_point m_last_mouse_move_up; - steady_clock::time_point m_last_mouse_move_down; - int m_deadzone_x = 60; - int m_deadzone_y = 60; - double m_multi_x = 2; - double m_multi_y = 2.5; - - // Mousewheel - steady_clock::time_point m_last_wheel_move_up; - steady_clock::time_point m_last_wheel_move_down; - steady_clock::time_point m_last_wheel_move_left; - steady_clock::time_point m_last_wheel_move_right; -}; diff --git a/rpcs3qt-legacy/_discord_utils.cpp b/rpcs3qt-legacy/_discord_utils.cpp deleted file mode 100644 index 8c2d227f3..000000000 --- a/rpcs3qt-legacy/_discord_utils.cpp +++ /dev/null @@ -1,37 +0,0 @@ -#ifdef WITH_DISCORD_RPC -#include "_discord_utils.h" -#include "discord_rpc.h" - -#include -#include - -namespace discord -{ - void initialize(const std::string& application_id) - { - DiscordEventHandlers handlers = {}; - Discord_Initialize(application_id.c_str(), &handlers, 1, nullptr); - } - - void shutdown() - { - Discord_Shutdown(); - } - - void update_presence(const std::string& state, const std::string& details, bool reset_timer) - { - DiscordRichPresence discordPresence = {}; - discordPresence.details = details.c_str(); - discordPresence.state = state.c_str(); - discordPresence.largeImageKey = "rpcs3_logo"; - discordPresence.largeImageText = "RPCS3 is the world's first PlayStation 3 emulator."; - - if (reset_timer) - { - discordPresence.startTimestamp = std::time(nullptr); - } - - Discord_UpdatePresence(&discordPresence); - } -} // namespace discord -#endif diff --git a/rpcs3qt-legacy/_discord_utils.h b/rpcs3qt-legacy/_discord_utils.h deleted file mode 100644 index 638c208e3..000000000 --- a/rpcs3qt-legacy/_discord_utils.h +++ /dev/null @@ -1,15 +0,0 @@ -#pragma once - -#include - -namespace discord -{ - // Convenience function for initialization - void initialize(const std::string& application_id = "424004941485572097"); - - // Convenience function for shutdown - void shutdown(); - - // Convenience function for status updates. The default is set to idle. - void update_presence(const std::string& state = "", const std::string& details = "Idle", bool reset_timer = true); -} // namespace discord diff --git a/rpcs3qt-legacy/about_dialog.cpp b/rpcs3qt-legacy/about_dialog.cpp deleted file mode 100644 index 71952446a..000000000 --- a/rpcs3qt-legacy/about_dialog.cpp +++ /dev/null @@ -1,56 +0,0 @@ -#include "about_dialog.h" -#include "ui_about_dialog.h" - -#include "rpcs3_version.h" -#include "qt_utils.h" - -#include -#include -#include - -about_dialog::about_dialog(QWidget* parent) : QDialog(parent), ui(new Ui::about_dialog) -{ - ui->setupUi(this); - setAttribute(Qt::WA_DeleteOnClose); - - ui->close->setDefault(true); - ui->icon->load(QStringLiteral(":/rpcs3.svg")); - ui->version->setText(tr("RPCS3 Version: %1").arg(QString::fromStdString(rpcs3::get_verbose_version()))); - ui->description->setText(gui::utils::make_paragraph(tr( - "RPCS3 is an open-source Sony PlayStation 3 emulator and debugger.\n" - "It is written in C++ for Windows, Linux, FreeBSD and MacOS, funded with %0.\n" - "Our developers and contributors are always working hard to ensure this project is the best that it can be.\n" - "There are still plenty of implementations to make and optimizations to do.") - .arg(gui::utils::make_link(tr("Patreon"), "https://rpcs3.net/patreon")))); - - // Events - connect(ui->gitHub, &QPushButton::clicked, [] - { - QDesktopServices::openUrl(QUrl("https://www.github.com/RPCS3")); - }); - connect(ui->website, &QPushButton::clicked, [] - { - QDesktopServices::openUrl(QUrl("https://rpcs3.net")); - }); - connect(ui->forum, &QPushButton::clicked, [] - { - QDesktopServices::openUrl(QUrl("https://forums.rpcs3.net")); - }); - connect(ui->patreon, &QPushButton::clicked, [] - { - QDesktopServices::openUrl(QUrl("https://rpcs3.net/patreon")); - }); - connect(ui->discord, &QPushButton::clicked, [] - { - QDesktopServices::openUrl(QUrl("https://discord.me/RPCS3")); - }); - connect(ui->wiki, &QPushButton::clicked, [] - { - QDesktopServices::openUrl(QUrl("https://wiki.rpcs3.net/index.php?title=Main_Page")); - }); - connect(ui->close, &QPushButton::clicked, this, &QWidget::close); -} - -about_dialog::~about_dialog() -{ -} diff --git a/rpcs3qt-legacy/about_dialog.h b/rpcs3qt-legacy/about_dialog.h deleted file mode 100644 index 91872ec6c..000000000 --- a/rpcs3qt-legacy/about_dialog.h +++ /dev/null @@ -1,20 +0,0 @@ -#pragma once - -#include - -namespace Ui -{ - class about_dialog; -} - -class about_dialog : public QDialog -{ - Q_OBJECT - -public: - explicit about_dialog(QWidget* parent = nullptr); - ~about_dialog(); - -private: - std::unique_ptr ui; -}; diff --git a/rpcs3qt-legacy/about_dialog.ui b/rpcs3qt-legacy/about_dialog.ui deleted file mode 100644 index 67bb5762f..000000000 --- a/rpcs3qt-legacy/about_dialog.ui +++ /dev/null @@ -1,468 +0,0 @@ - - - about_dialog - - - - 0 - 0 - 805 - 555 - - - - About RPCS3 - - - - :/rpcs3.ico:/rpcs3.ico - - - - 6 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - - 0 - 0 - - - - - - - - 0 - 0 - - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - - 50 - 50 - - - - - - - - Qt::Orientation::Vertical - - - QSizePolicy::Policy::MinimumExpanding - - - - 20 - 0 - - - - - - - - - - - - - - Arial - 14 - false - - - - RPCS3 PlayStation 3 Emulator - - - Qt::AlignmentFlag::AlignLeading|Qt::AlignmentFlag::AlignLeft|Qt::AlignmentFlag::AlignTop - - - Qt::TextInteractionFlag::TextSelectableByMouse - - - - - - - - Arial - true - - - - Qt::TextFormat::RichText - - - Qt::AlignmentFlag::AlignLeading|Qt::AlignmentFlag::AlignLeft|Qt::AlignmentFlag::AlignTop - - - false - - - true - - - Qt::TextInteractionFlag::TextBrowserInteraction - - - - - - - - Arial - true - - - - - - - Qt::TextFormat::RichText - - - Qt::AlignmentFlag::AlignLeading|Qt::AlignmentFlag::AlignLeft|Qt::AlignmentFlag::AlignTop - - - true - - - Qt::TextInteractionFlag::TextBrowserInteraction - - - - - - - - - - - - - 0 - 0 - - - - - 0 - 200 - - - - QFrame::Shape::NoFrame - - - Qt::ScrollBarPolicy::ScrollBarAlwaysOn - - - QAbstractScrollArea::SizeAdjustPolicy::AdjustToContents - - - true - - - - - 0 - 0 - 793 - 2638 - - - - - QLayout::SizeConstraint::SetMinimumSize - - - 24 - - - - - 10 - - - - - - true - - - - Developers: - - - - - - - <p>¬DH<br>¬AlexAltea<br>¬Hykem<br>¬Oil<br>Nekotekina<br>¬Bigpet<br>¬gopalsr83<br>¬tambry<br>¬vlj<br>kd-11<br>¬jarveson<br>¬raven02<br>AniLeo<br>¬cornytrace<br>¬ssshadow<br>¬Numan<br>hcorion<br>Megamouse<br>¬flash-fire<br>DAGINATSUKO<br>GalCiv<br>eladash</p> - - - false - - - Qt::AlignmentFlag::AlignLeading|Qt::AlignmentFlag::AlignLeft|Qt::AlignmentFlag::AlignTop - - - Qt::TextInteractionFlag::TextSelectableByMouse - - - - - - - - - 10 - - - - - - true - - - - Contributors: - - - - - - - <p>BlackDaemon<br>elisha464<br>Aishou<br>krofna<br>xsacha<br>danilaml<br>unknownbrackets<br>Zangetsu38<br>lioncash<br>achurch<br>darkf<br>Syphurith<br>Blaypeg<br>Survanium90<br>georgemoralis<br>ikki84<br>scribam<br>TGE<br>velocity<br>Farseer<br>Dangles<br>ruipin<br>jbeich<br>CookiePLMonster<br>Whatcookie<br>rajkosto<br>Admiral Thrawn<br>FlexBy<br>Dunastique<br>Jonathan44062<br>yurinator557<br>Satan<br>HoldTheMourning<br>illusion0001</p> - - - Qt::AlignmentFlag::AlignLeading|Qt::AlignmentFlag::AlignLeft|Qt::AlignmentFlag::AlignTop - - - Qt::TextInteractionFlag::TextSelectableByMouse - - - - - - - - - 10 - - - - - - true - - - - Supporters: - - - - - - - <p>Howard Garrison<br>EXPotemkin<br>Marko V.<br>danhp<br>Jake (5315825)<br>Ian Reid<br>Tad Sherlock<br>Tyler Friesen<br>Folzar<br>Payton Williams<br>RedPill Australia<br>yanghong<br>Mohammed El-Serougi<br>Дима ~Ximer13~ Кулин<br>James Reed<br>BaroqueSonata<br>Bonzay0<br>Henrijs Kons<br>eoiz<br>Lena Stöffler<br>Michael Holder<br>Max Bosse<br>Tyler Whisinnand<br>Gato Harvey<br>cain4355<br>Thomas Peltier<br>Loli Co.<br>MapleLoonie<br>Travis McEwen<br>Scott Singratsomboune<br>T.E<br>Lukas Rieger<br>Dane Madsen<br>JMS<br>Jonatan R<br>Luke Johnson<br>Thomas Zaorski<br>MyOwnFan<br>Alexandros Mandravillis<br>Socker Bopper<br>Faris Leonhart<br>Fabien Net<br>Raves<br>Barrowsx<br>kilsuton<br>Max Mason<br>Ethan Condon<br>jfidone<br>iaDRM<br>Kazer2.0<br>Bryce Quintin<br>Yuri Kunde Schlesner<br>Abdulla Altayer<br>Nicolas Jallamion<br>Vorvek<br>Ian Faddis<br>Leon<br>Mohammad Taleb<br>Jokez<br>crashX<br>Raveskirza<br>Grant Deacon<br>michael<br>David Zech<br>Ben Manoochehri<br>Adnan Kovacevic<br>Mighty J<br>Sam Shan Jiang<br>TheAnig<br>Rodney Coleman<br>FiniteAce<br>Kian Soon Alex Chiam<br>yukkuri<br>Justin Chadwick<br>toxic Itzi<br>Templerror<br>Myles Wesley Carlson<br>Max Bosse<br>Ethan Clark<br>LupineDream<br>CheatCodesOfLife<br>Jan Zykmund<br>Francesco Cinquemani<br>Andylg<br>Julia H de Camargos<br>Suvodip Mitra<br>Goh<br>Dmitry<br>Steel Brain<br>VarieZ<br>William Swango<br>Matthew Messersmith<br>Duane Locsin<br>Shuddertrix<br>Loweys Litsman<br>Shuddertrix<br>Mason Ferrie<br>Richard Kaplan<br>Hugues Valois<br>richard(lath..ch@)<br>Johnathan (Virtuous John)<br>eoiz<br>Dany Huguenin<br>doobieashtray<br>dean(mag..94@)<br>Pommier Jean-Philippe<br>Douglas Alan Albino<br>Ryan Mull<br>Thor-Erling Engen<br>Nick Carpenter<br>curryking3<br>Jared Tracton<br>alex(koo..oh@)<br>Jason O'Brien<br>Skeletal Charizard<br>Ace00<br>Brandon Corujo<br>HyperBitHero<br>佳文 李<br>sorryboi - <br>Johnson Bui - <br>itachi1986 - <br>Mortano - <br>Xythera - <br>Albert Quinteros - <br>Uzair Sheikh - <br>Ethan Hoppins - <br>optic - <br>Quill Slyver - <br>Averie - <br>StevenCarson - <br>YuriNator557 - <br>Deanmaxx - <br>linkQatar G - <br>Jack Collie - <br>TAL BERKOVITZ - <br>cjtk - <br>Comexzone - <br>mapleglass - <br>Liquidbings - <br>Dormant_Hero@0230 - <br>Theodore Raney - <br>Morito - <br>Chaining Ten - <br>Xeropel - <br>Marko Gatzouris - <br>Steven (...weller@gmail.com) - <br>Real Gamer - <br>Spencer Robinson - <br>naps - <br>Matthew Stevens - <br>Orion Clark - <br>William K. Leung - <br>Trent M - <br>Viktor Ni - <br>Marc Tönsing - <br>Amogus - <br>Scott Davis - <br>秉軒 侯 - <br>Wiktor Tkaczyński - <br>Vekkar - <br>Jackson Abney - </p> - - - Qt::AlignmentFlag::AlignLeading|Qt::AlignmentFlag::AlignLeft|Qt::AlignmentFlag::AlignTop - - - Qt::TextInteractionFlag::TextSelectableByMouse - - - - - - - - - Qt::Orientation::Horizontal - - - QSizePolicy::Policy::MinimumExpanding - - - - 0 - 20 - - - - - - - - - - - - 9 - - - 9 - - - 9 - - - 9 - - - - - GitHub - - - - - - - Website - - - - - - - Forum - - - - - - - Discord - - - - - - - Wiki - - - - - - - Patreon - - - - - - - Qt::Orientation::Horizontal - - - - 40 - 20 - - - - - - - - Close - - - - - - - - - - QSvgWidget - QWidget -
qsvgwidget.h
- 1 -
-
- - - - -
diff --git a/rpcs3qt-legacy/auto_pause_settings_dialog.cpp b/rpcs3qt-legacy/auto_pause_settings_dialog.cpp deleted file mode 100644 index 2091381f9..000000000 --- a/rpcs3qt-legacy/auto_pause_settings_dialog.cpp +++ /dev/null @@ -1,299 +0,0 @@ -#include "auto_pause_settings_dialog.h" -#include "table_item_delegate.h" -#include "Emu/System.h" - -#include -#include -#include -#include -#include - -#include "util/logs.hpp" -#include "util/File.h" - -LOG_CHANNEL(autopause_log, "AutoPause"); - -auto_pause_settings_dialog::auto_pause_settings_dialog(QWidget* parent) : QDialog(parent) -{ - QLabel* description = new QLabel(tr("To use auto pause: enter the ID(s) of a function or a system call.\nRestart of the game is required to apply. You can enable/disable this in the settings."), this); - - m_pause_list = new QTableWidget(this); - m_pause_list->setColumnCount(2); - m_pause_list->setHorizontalHeaderLabels(QStringList() << tr("Call ID") << tr("Type")); - // m_pause_list->horizontalHeader()->setSectionResizeMode(QHeaderView::ResizeToContents); - m_pause_list->setSelectionBehavior(QAbstractItemView::SelectRows); - m_pause_list->setContextMenuPolicy(Qt::CustomContextMenu); - m_pause_list->setItemDelegate(new table_item_delegate(this)); - m_pause_list->setShowGrid(false); - - QPushButton* clearButton = new QPushButton(tr("Clear"), this); - QPushButton* reloadButton = new QPushButton(tr("Reload"), this); - QPushButton* saveButton = new QPushButton(tr("Save"), this); - QPushButton* cancelButton = new QPushButton(tr("Cancel"), this); - cancelButton->setDefault(true); - - QHBoxLayout* buttonsLayout = new QHBoxLayout(); - buttonsLayout->addWidget(clearButton); - buttonsLayout->addWidget(reloadButton); - buttonsLayout->addStretch(); - buttonsLayout->addWidget(saveButton); - buttonsLayout->addWidget(cancelButton); - - QVBoxLayout* mainLayout = new QVBoxLayout(this); - mainLayout->addWidget(description); - mainLayout->addWidget(m_pause_list); - mainLayout->addLayout(buttonsLayout); - setLayout(mainLayout); - - setMinimumSize(QSize(400, 360)); - setWindowTitle(tr("Auto Pause Manager")); - setObjectName("auto_pause_manager"); - - // Events - connect(m_pause_list, &QTableWidget::customContextMenuRequested, this, &auto_pause_settings_dialog::ShowContextMenu); - connect(clearButton, &QAbstractButton::clicked, [this]() - { - m_entries.clear(); - UpdateList(); - }); - connect(reloadButton, &QAbstractButton::clicked, [this]() - { - LoadEntries(); - UpdateList(); - }); - connect(saveButton, &QAbstractButton::clicked, [this]() - { - SaveEntries(); - autopause_log.success("File pause.bin was updated."); - }); - connect(cancelButton, &QAbstractButton::clicked, this, &QWidget::close); - - Emu.GracefulShutdown(false); - - LoadEntries(); - UpdateList(); - setFixedSize(sizeHint()); -} - -// Copied some from AutoPause. -void auto_pause_settings_dialog::LoadEntries() -{ - m_entries.clear(); - m_entries.reserve(16); - - const fs::file list(fs::get_config_dir() + "pause.bin"); - - if (list) - { - // System calls ID and Function calls ID are all u32 iirc. - u32 num; - const usz fmax = list.size(); - usz fcur = 0; - list.seek(0); - while (fcur <= fmax - sizeof(u32)) - { - list.read(&num, sizeof(u32)); - fcur += sizeof(u32); - if (num == 0xFFFFFFFF) - break; - - m_entries.emplace_back(num); - } - } -} - -// Copied some from AutoPause. -// Tip: This one doesn't check for the file is being read or not. -// This would always use a 0xFFFFFFFF as end of the pause.bin -void auto_pause_settings_dialog::SaveEntries() -{ - fs::file list(fs::get_config_dir() + "pause.bin", fs::rewrite); - // System calls ID and Function calls ID are all u32 iirc. - u32 num = 0; - list.seek(0); - for (usz i = 0; i < m_entries.size(); ++i) - { - if (num == 0xFFFFFFFF) - continue; - num = m_entries[i]; - list.write(&num, sizeof(u32)); - } - num = 0xFFFFFFFF; - list.write(&num, sizeof(u32)); -} - -void auto_pause_settings_dialog::UpdateList() -{ - const int entries_size = static_cast(m_entries.size()); - m_pause_list->clearContents(); - m_pause_list->setRowCount(entries_size); - for (int i = 0; i < entries_size; ++i) - { - QTableWidgetItem* callItem = new QTableWidgetItem; - QTableWidgetItem* typeItem = new QTableWidgetItem; - callItem->setFlags(callItem->flags() & ~Qt::ItemIsEditable); - typeItem->setFlags(typeItem->flags() & ~Qt::ItemIsEditable); - if (m_entries[i] != 0xFFFFFFFF) - { - callItem->setData(Qt::DisplayRole, QString::fromStdString(fmt::format("%08x", m_entries[i]))); - } - else - { - callItem->setData(Qt::DisplayRole, tr("Unset")); - } - - if (m_entries[i] < 1024) - { - typeItem->setData(Qt::DisplayRole, tr("System Call")); - } - else - { - typeItem->setData(Qt::DisplayRole, tr("Function Call")); - } - - m_pause_list->setItem(i, 0, callItem); - m_pause_list->setItem(i, 1, typeItem); - } -} - -void auto_pause_settings_dialog::ShowContextMenu(const QPoint& pos) -{ - const int row = m_pause_list->indexAt(pos).row(); - - QMenu myMenu; - - // Make Actions - QAction* add = myMenu.addAction(tr("&Add")); - QAction* remove = myMenu.addAction(tr("&Remove")); - myMenu.addSeparator(); - QAction* config = myMenu.addAction(tr("&Config")); - - if (row == -1) - { - remove->setEnabled(false); - config->setEnabled(false); - } - - auto OnEntryConfig = [this](int row, bool newEntry) - { - AutoPauseConfigDialog* config = new AutoPauseConfigDialog(this, this, newEntry, &m_entries[row]); - config->setModal(true); - config->exec(); - UpdateList(); - }; - - connect(add, &QAction::triggered, this, [=, this]() - { - m_entries.emplace_back(0xFFFFFFFF); - UpdateList(); - const int idx = static_cast(m_entries.size()) - 1; - m_pause_list->selectRow(idx); - OnEntryConfig(idx, true); - }); - connect(remove, &QAction::triggered, this, &auto_pause_settings_dialog::OnRemove); - connect(config, &QAction::triggered, this, [=, this]() - { - OnEntryConfig(row, false); - }); - - myMenu.exec(m_pause_list->viewport()->mapToGlobal(pos)); -} - -void auto_pause_settings_dialog::OnRemove() -{ - QModelIndexList selection = m_pause_list->selectionModel()->selectedRows(); - std::sort(selection.begin(), selection.end()); - for (int i = selection.count() - 1; i >= 0; i--) - { - m_entries.erase(m_entries.begin() + ::at32(selection, i).row()); - } - UpdateList(); -} - -void auto_pause_settings_dialog::keyPressEvent(QKeyEvent* event) -{ - if (event->isAutoRepeat()) - { - return; - } - - if (event->key() == Qt::Key_Delete) - { - OnRemove(); - } -} - -AutoPauseConfigDialog::AutoPauseConfigDialog(QWidget* parent, auto_pause_settings_dialog* apsd, bool newEntry, u32* entry) - : QDialog(parent), m_presult(entry), m_newEntry(newEntry), m_apsd(apsd) -{ - m_entry = *m_presult; - setMinimumSize(QSize(300, -1)); - - QPushButton* button_ok = new QPushButton(tr("&Ok"), this); - QPushButton* button_cancel = new QPushButton(tr("&Cancel"), this); - button_ok->setFixedWidth(50); - button_cancel->setFixedWidth(50); - - QLabel* description = new QLabel(tr("Specify ID of System Call or Function Call below. You need to use a Hexadecimal ID."), this); - description->setWordWrap(true); - - m_current_converted = new QLabel(tr("Currently it gets an id of \"Unset\"."), this); - m_current_converted->setWordWrap(true); - - m_id = new QLineEdit(this); - m_id->setText(QString::fromStdString(fmt::format("%08x", m_entry))); - m_id->setPlaceholderText("ffffffff"); - m_id->setFont(QFontDatabase::systemFont(QFontDatabase::FixedFont)); - m_id->setMaxLength(8); - m_id->setFixedWidth(65); - setWindowTitle("Auto Pause Setting: " + m_id->text()); - - connect(button_cancel, &QAbstractButton::clicked, this, &AutoPauseConfigDialog::OnCancel); - connect(button_ok, &QAbstractButton::clicked, this, &AutoPauseConfigDialog::OnOk); - connect(m_id, &QLineEdit::textChanged, this, &AutoPauseConfigDialog::OnUpdateValue); - - QHBoxLayout* configHBox = new QHBoxLayout(); - configHBox->addWidget(m_id); - configHBox->addWidget(button_ok); - configHBox->addWidget(button_cancel); - configHBox->setAlignment(Qt::AlignCenter); - - QVBoxLayout* mainLayout = new QVBoxLayout(this); - mainLayout->addWidget(description); - mainLayout->addLayout(configHBox); - mainLayout->addWidget(m_current_converted); - - setLayout(mainLayout); - setFixedSize(QSize(300, sizeHint().height())); - - OnUpdateValue(); -} - -void AutoPauseConfigDialog::OnOk() -{ - bool ok; - const ullong value = m_id->text().toULongLong(&ok, 16); - - m_entry = value; - *m_presult = m_entry; - - accept(); -} - -void AutoPauseConfigDialog::OnCancel() -{ - if (m_newEntry) - { - m_apsd->OnRemove(); - } - close(); -} - -void AutoPauseConfigDialog::OnUpdateValue() const -{ - bool ok; - const ullong value = m_id->text().toULongLong(&ok, 16); - const bool is_ok = ok && value <= u32{umax}; - - m_current_converted->setText(tr("Current value: %1 (%2)").arg(value, 8, 16).arg(is_ok ? tr("OK") : tr("Conversion failed"))); -} diff --git a/rpcs3qt-legacy/auto_pause_settings_dialog.h b/rpcs3qt-legacy/auto_pause_settings_dialog.h deleted file mode 100644 index 30e595e50..000000000 --- a/rpcs3qt-legacy/auto_pause_settings_dialog.h +++ /dev/null @@ -1,56 +0,0 @@ -#pragma once - -#include "util/types.hpp" - -#include -#include -#include -#include -#include - -class auto_pause_settings_dialog : public QDialog -{ - Q_OBJECT - - enum - { - id_add, - id_remove, - id_config, - }; - - std::vector m_entries; - QTableWidget* m_pause_list; - -public: - explicit auto_pause_settings_dialog(QWidget* parent); - void UpdateList(); - void LoadEntries(); - void SaveEntries(); - -public Q_SLOTS: - void OnRemove(); -private Q_SLOTS: - void ShowContextMenu(const QPoint& pos); - void keyPressEvent(QKeyEvent* event) override; -}; - -class AutoPauseConfigDialog : public QDialog -{ - Q_OBJECT - - u32 m_entry; - u32* m_presult; - bool m_newEntry; - QLineEdit* m_id; - QLabel* m_current_converted; - auto_pause_settings_dialog* m_apsd; - -public: - explicit AutoPauseConfigDialog(QWidget* parent, auto_pause_settings_dialog* apsd, bool newEntry, u32* entry); - -private Q_SLOTS: - void OnOk(); - void OnCancel(); - void OnUpdateValue() const; -}; diff --git a/rpcs3qt-legacy/basic_mouse_settings_dialog.cpp b/rpcs3qt-legacy/basic_mouse_settings_dialog.cpp deleted file mode 100644 index c89d461c9..000000000 --- a/rpcs3qt-legacy/basic_mouse_settings_dialog.cpp +++ /dev/null @@ -1,298 +0,0 @@ -#include "stdafx.h" -#include "basic_mouse_settings_dialog.h" -#include "localized_emu.h" -#include "Input/basic_mouse_handler.h" -#include "Input/keyboard_pad_handler.h" -#include "Emu/Io/mouse_config.h" -#include "util/asm.hpp" - -#include -#include -#include - -LOG_CHANNEL(cfg_log, "CFG"); - -enum button_role -{ - button_name = Qt::UserRole, - button_code -}; - -basic_mouse_settings_dialog::basic_mouse_settings_dialog(QWidget* parent) - : QDialog(parent) -{ - setObjectName("basic_mouse_settings_dialog"); - setWindowTitle(tr("Configure Basic Mouse Handler")); - setAttribute(Qt::WA_DeleteOnClose); - setAttribute(Qt::WA_StyledBackground); - setModal(true); - - QVBoxLayout* v_layout = new QVBoxLayout(this); - - m_button_box = new QDialogButtonBox(this); - m_button_box->setStandardButtons(QDialogButtonBox::Apply | QDialogButtonBox::Cancel | QDialogButtonBox::Save | QDialogButtonBox::RestoreDefaults); - - connect(m_button_box, &QDialogButtonBox::clicked, this, [this](QAbstractButton* button) - { - if (button == m_button_box->button(QDialogButtonBox::Apply)) - { - g_cfg_mouse.save(); - } - else if (button == m_button_box->button(QDialogButtonBox::Save)) - { - g_cfg_mouse.save(); - accept(); - } - else if (button == m_button_box->button(QDialogButtonBox::RestoreDefaults)) - { - if (QMessageBox::question(this, tr("Confirm Reset"), tr("Reset all settings?")) != QMessageBox::Yes) - return; - reset_config(); - } - else if (button == m_button_box->button(QDialogButtonBox::Cancel)) - { - // Restore config - if (!g_cfg_mouse.load()) - { - cfg_log.notice("Could not restore mouse config. Using defaults."); - } - reject(); - } - }); - - if (!g_cfg_mouse.load()) - { - cfg_log.notice("Could not load basic mouse config. Using defaults."); - } - - m_buttons = new QButtonGroup(this); - - connect(m_buttons, &QButtonGroup::idClicked, this, &basic_mouse_settings_dialog::on_button_click); - - connect(&m_remap_timer, &QTimer::timeout, this, [this]() - { - auto button = m_buttons->button(m_button_id); - - if (--m_seconds <= 0) - { - if (button) - { - if (const int button_id = m_buttons->id(button)) - { - const std::string name = g_cfg_mouse.get_button(button_id).to_string(); - button->setText(name.empty() ? QStringLiteral("-") : QString::fromStdString(name)); - } - } - reactivate_buttons(); - return; - } - if (button) - { - button->setText(tr("[ Waiting %1 ]").arg(m_seconds)); - } - }); - - const auto insert_button = [this](int id, QPushButton* button) - { - m_buttons->addButton(button, id); - button->installEventFilter(this); - }; - - constexpr u32 button_count = 8; - constexpr u32 max_items_per_column = 4; - int rows = button_count; - - for (u32 cols = 1; utils::aligned_div(button_count, cols) > max_items_per_column;) - { - rows = utils::aligned_div(button_count, ++cols); - } - - QWidget* widget = new QWidget(this); - QGridLayout* grid_layout = new QGridLayout(this); - - for (int i = 0, row = 0, col = 0; i < static_cast(button_count); i++, row++) - { - const int cell_code = get_mouse_button_code(i); - const QString translated_cell_button = localized_emu::translated_mouse_button(cell_code); - - QHBoxLayout* h_layout = new QHBoxLayout(this); - QGroupBox* gb = new QGroupBox(translated_cell_button, this); - QPushButton* pb = new QPushButton(this); - - insert_button(cell_code, pb); - - const std::string saved_btn = g_cfg_mouse.get_button(cell_code); - - pb->setText(saved_btn.empty() ? QStringLiteral("-") : QString::fromStdString(saved_btn)); - - if (row >= rows) - { - row = 0; - col++; - } - - m_push_buttons[cell_code] = pb; - h_layout->addWidget(pb); - gb->setLayout(h_layout); - grid_layout->addWidget(gb, row, col); - } - - widget->setLayout(grid_layout); - - v_layout->addWidget(widget); - v_layout->addWidget(m_button_box); - setLayout(v_layout); - - m_palette = m_push_buttons[CELL_MOUSE_BUTTON_1]->palette(); // save normal palette -} - -void basic_mouse_settings_dialog::reset_config() -{ - g_cfg_mouse.from_default(); - - for (auto& [cell_code, pb] : m_push_buttons) - { - if (!pb) - continue; - - const QString text = QString::fromStdString(g_cfg_mouse.get_button(cell_code).def); - pb->setText(text.isEmpty() ? QStringLiteral("-") : text); - } -} - -void basic_mouse_settings_dialog::on_button_click(int id) -{ - if (id < 0) - { - return; - } - - for (auto but : m_buttons->buttons()) - { - but->setEnabled(false); - but->setFocusPolicy(Qt::ClickFocus); - } - - m_button_box->setEnabled(false); - for (auto but : m_button_box->buttons()) - { - but->setFocusPolicy(Qt::ClickFocus); - } - - m_button_id = id; - if (auto button = m_buttons->button(m_button_id)) - { - button->setText(tr("[ Waiting %1 ]").arg(MAX_SECONDS)); - button->setPalette(QPalette(Qt::blue)); - button->grabMouse(); - } - - m_remap_timer.start(1000); -} - -void basic_mouse_settings_dialog::keyPressEvent(QKeyEvent* event) -{ - if (m_button_id < 0) - { - // We are not remapping a button, so pass the event to the base class. - QDialog::keyPressEvent(event); - return; - } - - const std::string name = keyboard_pad_handler::GetKeyName(event, false); - g_cfg_mouse.get_button(m_button_id).from_string(name); - - if (auto button = m_buttons->button(m_button_id)) - { - button->setText(QString::fromStdString(name)); - } - - reactivate_buttons(); -} - -void basic_mouse_settings_dialog::mouseReleaseEvent(QMouseEvent* event) -{ - if (m_button_id < 0) - { - // We are not remapping a button, so pass the event to the base class. - QDialog::mouseReleaseEvent(event); - return; - } - - const std::string name = keyboard_pad_handler::GetMouseName(event); - g_cfg_mouse.get_button(m_button_id).from_string(name); - - if (auto button = m_buttons->button(m_button_id)) - { - button->setText(QString::fromStdString(name)); - } - - reactivate_buttons(); -} - -bool basic_mouse_settings_dialog::eventFilter(QObject* object, QEvent* event) -{ - switch (event->type()) - { - case QEvent::MouseButtonRelease: - { - // On right click clear binding if we are not remapping pad button - if (m_button_id < 0) - { - QMouseEvent* mouse_event = static_cast(event); - if (const auto button = qobject_cast(object); button && button->isEnabled() && mouse_event->button() == Qt::RightButton) - { - if (const int button_id = m_buttons->id(button)) - { - button->setText(QStringLiteral("-")); - g_cfg_mouse.get_button(button_id).from_string(""); - return true; - } - } - } - - // Disabled buttons should not absorb mouseclicks - event->ignore(); - break; - } - default: - { - break; - } - } - - return QDialog::eventFilter(object, event); -} - -void basic_mouse_settings_dialog::reactivate_buttons() -{ - m_remap_timer.stop(); - m_seconds = MAX_SECONDS; - - if (m_button_id < 0) - { - return; - } - - if (auto button = m_buttons->button(m_button_id)) - { - button->setPalette(m_palette); - button->releaseMouse(); - } - - m_button_id = -1; - - // Enable all buttons - m_button_box->setEnabled(true); - - for (auto but : m_button_box->buttons()) - { - but->setFocusPolicy(Qt::StrongFocus); - } - - for (auto but : m_buttons->buttons()) - { - but->setEnabled(true); - but->setFocusPolicy(Qt::StrongFocus); - } -} diff --git a/rpcs3qt-legacy/basic_mouse_settings_dialog.h b/rpcs3qt-legacy/basic_mouse_settings_dialog.h deleted file mode 100644 index 3de502997..000000000 --- a/rpcs3qt-legacy/basic_mouse_settings_dialog.h +++ /dev/null @@ -1,43 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include -#include - -#include - -class basic_mouse_settings_dialog : public QDialog -{ - Q_OBJECT - -public: - basic_mouse_settings_dialog(QWidget* parent = nullptr); - -private: - void reset_config(); - - void on_button_click(int id); - void reactivate_buttons(); - - // Buttons - QDialogButtonBox* m_button_box = nullptr; - QButtonGroup* m_buttons = nullptr; - std::unordered_map m_push_buttons; - int m_button_id = -1; - - // Backup for standard button palette - QPalette m_palette; - - // Remap Timer - static constexpr int MAX_SECONDS = 5; - int m_seconds = MAX_SECONDS; - QTimer m_remap_timer; - -protected: - void keyPressEvent(QKeyEvent* event) override; - void mouseReleaseEvent(QMouseEvent* event) override; - bool eventFilter(QObject* object, QEvent* event) override; -}; diff --git a/rpcs3qt-legacy/breakpoint_handler.cpp b/rpcs3qt-legacy/breakpoint_handler.cpp deleted file mode 100644 index d2c4c44ba..000000000 --- a/rpcs3qt-legacy/breakpoint_handler.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include "breakpoint_handler.h" - -extern bool ppu_breakpoint(u32 loc, bool is_adding); - -bool breakpoint_handler::IsBreakOnBPM() const -{ - return m_break_on_bpm; -} - -void breakpoint_handler::SetBreakOnBPM(bool break_on_bpm) -{ - m_break_on_bpm = break_on_bpm; -} - -bool breakpoint_handler::HasBreakpoint(u32 loc, bs_t type) -{ - std::lock_guard lock(mutex_breakpoints); - - return m_breakpoints.contains(loc) && ((m_breakpoints.at(loc) & type) == type); -} - -bool breakpoint_handler::AddBreakpoint(u32 loc, bs_t type) -{ - std::lock_guard lock(mutex_breakpoints); - - if ((type & breakpoint_types::bp_exec) && !ppu_breakpoint(loc, true)) - { - return false; - } - - return m_breakpoints.insert({loc, type}).second; -} - -bool breakpoint_handler::RemoveBreakpoint(u32 loc) -{ - std::lock_guard lock(mutex_breakpoints); - - bs_t bp_type{}; - if (m_breakpoints.contains(loc)) - { - bp_type = m_breakpoints.at(loc); - } - - if (m_breakpoints.erase(loc) == 0) - { - return false; - } - - if (bp_type & breakpoint_types::bp_exec) - { - ensure(ppu_breakpoint(loc, false)); - } - return true; -} diff --git a/rpcs3qt-legacy/breakpoint_handler.h b/rpcs3qt-legacy/breakpoint_handler.h deleted file mode 100644 index 1d731b6e9..000000000 --- a/rpcs3qt-legacy/breakpoint_handler.h +++ /dev/null @@ -1,53 +0,0 @@ -#pragma once - -#include "util/types.hpp" -#include "util/bit_set.h" -#include -#include "util/mutex.h" - -enum class breakpoint_types -{ - bp_read = 0x1, - bp_write = 0x2, - bp_exec = 0x4, - __bitset_enum_max -}; - -/* - * This class acts as a layer between the UI and Emu for breakpoints. - */ -class breakpoint_handler -{ - -public: - breakpoint_handler() = default; - ~breakpoint_handler() = default; - - bool IsBreakOnBPM() const; - void SetBreakOnBPM(bool break_on_bpm); - - /** - * Returns true iff breakpoint exists at loc. - * TODO: Add arg for flags, gameid, and maybe even thread if it should be thread local breakpoint.... breakpoint struct is probably what'll happen - */ - bool HasBreakpoint(u32 loc, bs_t type); - - /** - * Returns true if added successfully. TODO: flags - */ - bool AddBreakpoint(u32 loc, bs_t type); - - /** - * Returns true if removed breakpoint at loc successfully. - */ - bool RemoveBreakpoint(u32 loc); - -private: - // TODO : generalize to hold multiple games and handle flags.Probably do : std::map>. - // Although, externally, they'll only be accessed by loc (I think) so a map of maps may also do? - shared_mutex mutex_breakpoints; - std::map> m_breakpoints; //! Holds all breakpoints. - bool m_break_on_bpm = false; -}; - -extern breakpoint_handler g_breakpoint_handler; diff --git a/rpcs3qt-legacy/breakpoint_list.cpp b/rpcs3qt-legacy/breakpoint_list.cpp deleted file mode 100644 index 12a4e44ef..000000000 --- a/rpcs3qt-legacy/breakpoint_list.cpp +++ /dev/null @@ -1,287 +0,0 @@ -#include "breakpoint_list.h" -#include "breakpoint_handler.h" - -#include "Emu/CPU/CPUDisAsm.h" -#include "Emu/Cell/PPUThread.h" -#include "Emu/Cell/SPUThread.h" -#include "debugger_add_bp_window.h" - -#include -#include -#include - -extern bool is_using_interpreter(thread_class t_class); - -breakpoint_list::breakpoint_list(QWidget* parent, breakpoint_handler* handler) : QListWidget(parent), m_ppu_breakpoint_handler(handler) -{ - setEditTriggers(QAbstractItemView::NoEditTriggers); - setContextMenuPolicy(Qt::CustomContextMenu); - setSelectionMode(QAbstractItemView::ExtendedSelection); - - connect(this, &QListWidget::itemDoubleClicked, this, &breakpoint_list::OnBreakpointListDoubleClicked); - connect(this, &QListWidget::customContextMenuRequested, this, &breakpoint_list::OnBreakpointListRightClicked); - - m_delete_action = new QAction(tr("&Delete"), this); - m_delete_action->setShortcut(Qt::Key_Delete); - m_delete_action->setShortcutContext(Qt::WidgetShortcut); - connect(m_delete_action, &QAction::triggered, this, &breakpoint_list::OnBreakpointListDelete); - addAction(m_delete_action); - - // Hide until used in order to allow as much space for registers panel as possible - hide(); -} - -/** - * It's unfortunate I need a method like this to sync these. Should ponder a cleaner way to do this. - */ -void breakpoint_list::UpdateCPUData(std::shared_ptr disasm) -{ - m_disasm = std::move(disasm); -} - -void breakpoint_list::ClearBreakpoints() -{ - while (count()) - { - auto* currentItem = takeItem(0); - const u32 loc = currentItem->data(Qt::UserRole).value(); - m_ppu_breakpoint_handler->RemoveBreakpoint(loc); - delete currentItem; - } - - hide(); -} - -void breakpoint_list::RemoveBreakpoint(u32 addr) -{ - m_ppu_breakpoint_handler->RemoveBreakpoint(addr); - - for (int i = 0; i < count(); i++) - { - QListWidgetItem* currentItem = item(i); - - if (currentItem->data(Qt::UserRole).value() == addr) - { - delete takeItem(i); - break; - } - } - - if (!count()) - { - hide(); - } -} - -bool breakpoint_list::AddBreakpoint(u32 pc, bs_t type) -{ - if (!m_ppu_breakpoint_handler->AddBreakpoint(pc, type)) - { - return false; - } - - QString breakpoint_item_text; - - if (type == breakpoint_types::bp_exec) - { - m_disasm->disasm(m_disasm->dump_pc = pc); - breakpoint_item_text = QString::fromStdString(m_disasm->last_opcode); - breakpoint_item_text.remove(10, 13); - } - else if (type == breakpoint_types::bp_read) - { - breakpoint_item_text = QString("BPMR: 0x%1").arg(pc, 8, 16, QChar('0')); - } - else if (type == breakpoint_types::bp_write) - { - breakpoint_item_text = QString("BPMW: 0x%1").arg(pc, 8, 16, QChar('0')); - } - else if (type == (breakpoint_types::bp_read + breakpoint_types::bp_write)) - { - breakpoint_item_text = QString("BPMRW: 0x%1").arg(pc, 8, 16, QChar('0')); - } - - QListWidgetItem* breakpoint_item = new QListWidgetItem(breakpoint_item_text); - breakpoint_item->setForeground(m_text_color_bp); - breakpoint_item->setBackground(m_color_bp); - breakpoint_item->setData(Qt::UserRole, pc); - addItem(breakpoint_item); - - show(); - - return true; -} - -/** - * If breakpoint exists, we remove it, else add new one. Yeah, it'd be nicer from a code logic to have it be set/reset. But, that logic has to happen somewhere anyhow. - */ -void breakpoint_list::HandleBreakpointRequest(u32 loc, bool only_add) -{ - const auto cpu = m_disasm ? m_disasm->get_cpu() : nullptr; - - if (!cpu || cpu->state & cpu_flag::exit) - { - return; - } - - if (!is_using_interpreter(cpu->get_class())) - { - QMessageBox::warning(this, tr("Interpreters-Only Feature!"), tr("Cannot set breakpoints on non-interpreter decoders.")); - return; - } - - switch (cpu->get_class()) - { - case thread_class::spu: - { - if (loc >= SPU_LS_SIZE || loc % 4) - { - QMessageBox::warning(this, tr("Invalid Memory For Breakpoints!"), tr("Cannot set breakpoints on non-SPU executable memory!")); - return; - } - - const auto spu = static_cast(cpu); - auto& list = spu->local_breakpoints; - const u32 pos_at = loc / 4; - const u32 pos_bit = 1u << (pos_at % 8); - - if (list[pos_at / 8].fetch_xor(pos_bit) & pos_bit) - { - if (std::none_of(list.begin(), list.end(), [](auto& val) - { - return val.load(); - })) - { - spu->has_active_local_bps = false; - } - } - else - { - if (!spu->has_active_local_bps.exchange(true)) - { - spu->state.atomic_op([](bs_t& flags) - { - if (flags & cpu_flag::pending) - { - flags += cpu_flag::pending_recheck; - } - else - { - flags += cpu_flag::pending; - } - }); - } - } - - return; - } - case thread_class::ppu: - break; - default: - QMessageBox::warning(this, tr("Unimplemented Breakpoints For Thread Type!"), tr("Cannot set breakpoints on a thread not an PPU/SPU currently, sorry.")); - return; - } - - if (!vm::check_addr(loc, vm::page_executable)) - { - QMessageBox::warning(this, tr("Invalid Memory For Breakpoints!"), tr("Cannot set breakpoints on non-executable memory!")); - return; - } - - if (m_ppu_breakpoint_handler->HasBreakpoint(loc, breakpoint_types::bp_exec)) - { - if (!only_add) - { - RemoveBreakpoint(loc); - } - } - else - { - if (!AddBreakpoint(loc, breakpoint_types::bp_exec)) - { - QMessageBox::warning(this, tr("Unknown error while setting breakpoint!"), tr("Failed to set breakpoints.")); - return; - } - } -} - -void breakpoint_list::OnBreakpointListDoubleClicked() -{ - if (QListWidgetItem* item = currentItem()) - { - const u32 address = item->data(Qt::UserRole).value(); - Q_EMIT RequestShowAddress(address); - } -} - -void breakpoint_list::OnBreakpointListRightClicked(const QPoint& pos) -{ - m_context_menu = new QMenu(); - - if (selectedItems().count() == 1) - { - QAction* rename_action = m_context_menu->addAction(tr("&Rename")); - connect(rename_action, &QAction::triggered, this, [this]() - { - QListWidgetItem* current_item = selectedItems().first(); - current_item->setFlags(current_item->flags() | Qt::ItemIsEditable); - editItem(current_item); - }); - m_context_menu->addSeparator(); - } - - if (selectedItems().count() >= 1) - { - m_context_menu->addAction(m_delete_action); - } - - QAction* m_addbp = new QAction(tr("Add Breakpoint"), this); - connect(m_addbp, &QAction::triggered, this, [this] - { - debugger_add_bp_window dlg(this, this); - dlg.exec(); - }); - m_context_menu->addAction(m_addbp); - -#ifdef RPCS3_HAS_MEMORY_BREAKPOINTS - QAction* m_tglbpmbreak = new QAction(m_ppu_breakpoint_handler->IsBreakOnBPM() ? tr("Disable BPM") : tr("Enable BPM"), this); - connect(m_tglbpmbreak, &QAction::triggered, [this] - { - m_ppu_breakpoint_handler->SetBreakOnBPM(!m_ppu_breakpoint_handler->IsBreakOnBPM()); - }); - m_context_menu->addAction(m_tglbpmbreak); -#endif - - m_context_menu->exec(viewport()->mapToGlobal(pos)); - m_context_menu->deleteLater(); - m_context_menu = nullptr; -} - -void breakpoint_list::OnBreakpointListDelete() -{ - for (int i = selectedItems().count() - 1; i >= 0; i--) - { - RemoveBreakpoint(::at32(selectedItems(), i)->data(Qt::UserRole).value()); - } - - if (m_context_menu) - { - m_context_menu->close(); - } -} - -void breakpoint_list::mouseDoubleClickEvent(QMouseEvent* ev) -{ - if (!ev) - return; - - // Qt's itemDoubleClicked signal doesn't distinguish between mouse buttons and there is no simple way to get the pressed button. - // So we have to ignore this event when another button is pressed. - if (ev->button() != Qt::LeftButton) - { - ev->ignore(); - return; - } - - QListWidget::mouseDoubleClickEvent(ev); -} diff --git a/rpcs3qt-legacy/breakpoint_list.h b/rpcs3qt-legacy/breakpoint_list.h deleted file mode 100644 index 4367f1ba7..000000000 --- a/rpcs3qt-legacy/breakpoint_list.h +++ /dev/null @@ -1,44 +0,0 @@ -#pragma once - -#include "util/types.hpp" - -#include - -#include "breakpoint_handler.h" - -class CPUDisAsm; -class cpu_thread; - -class breakpoint_list : public QListWidget -{ - Q_OBJECT - -public: - breakpoint_list(QWidget* parent, breakpoint_handler* handler); - void UpdateCPUData(std::shared_ptr disasm); - void ClearBreakpoints(); - void RemoveBreakpoint(u32 addr); - bool AddBreakpoint(u32 pc, bs_t type); - - QColor m_text_color_bp; - QColor m_color_bp; - -protected: - void mouseDoubleClickEvent(QMouseEvent* ev) override; - -Q_SIGNALS: - void RequestShowAddress(u32 addr, bool select_addr = true, bool force = false); -public Q_SLOTS: - void HandleBreakpointRequest(u32 loc, bool add_only); - -private Q_SLOTS: - void OnBreakpointListDoubleClicked(); - void OnBreakpointListRightClicked(const QPoint& pos); - void OnBreakpointListDelete(); - -private: - breakpoint_handler* m_ppu_breakpoint_handler = nullptr; - QMenu* m_context_menu = nullptr; - QAction* m_delete_action; - std::shared_ptr m_disasm = nullptr; -}; diff --git a/rpcs3qt-legacy/call_stack_list.cpp b/rpcs3qt-legacy/call_stack_list.cpp deleted file mode 100644 index c2b8f343b..000000000 --- a/rpcs3qt-legacy/call_stack_list.cpp +++ /dev/null @@ -1,70 +0,0 @@ -#include "call_stack_list.h" - -#include "util/StrFmt.h" - -#include -#include - -call_stack_list::call_stack_list(QWidget* parent) : QListWidget(parent) -{ - setEditTriggers(QAbstractItemView::NoEditTriggers); - setContextMenuPolicy(Qt::CustomContextMenu); - setSelectionMode(QAbstractItemView::ExtendedSelection); - - // connects - connect(this, &QListWidget::itemDoubleClicked, this, &call_stack_list::ShowItemAddress); - - // Hide until used in order to allow as much space for registers panel as possible - hide(); -} - -void call_stack_list::keyPressEvent(QKeyEvent* event) -{ - QListWidget::keyPressEvent(event); - event->ignore(); // Propagate the event to debugger_frame - - if (!event->modifiers() && event->key() == Qt::Key_Return) - { - ShowItemAddress(); - } -} - -void call_stack_list::HandleUpdate(const std::vector>& call_stack) -{ - clear(); - - for (const auto& addr : call_stack) - { - const QString text = QString::fromStdString(fmt::format("0x%08llx (sp=0x%08llx)", addr.first, addr.second)); - QListWidgetItem* call_stack_item = new QListWidgetItem(text); - call_stack_item->setData(Qt::UserRole, {addr.first}); - addItem(call_stack_item); - } - - setVisible(!call_stack.empty()); -} - -void call_stack_list::ShowItemAddress() -{ - if (QListWidgetItem* call_stack_item = currentItem()) - { - const u32 address = call_stack_item->data(Qt::UserRole).value(); - Q_EMIT RequestShowAddress(address); - } -} - -void call_stack_list::mouseDoubleClickEvent(QMouseEvent* ev) -{ - if (!ev) - return; - - // Qt's itemDoubleClicked signal doesn't distinguish between mouse buttons and there is no simple way to get the pressed button. - // So we have to ignore this event when another button is pressed. - if (ev->button() != Qt::LeftButton) - { - ev->ignore(); - return; - } - - QListWidget::mouseDoubleClickEvent(ev); -} diff --git a/rpcs3qt-legacy/call_stack_list.h b/rpcs3qt-legacy/call_stack_list.h deleted file mode 100644 index 48a71dbbb..000000000 --- a/rpcs3qt-legacy/call_stack_list.h +++ /dev/null @@ -1,31 +0,0 @@ -#pragma once - -#include "util/types.hpp" - -#include -#include - -class cpu_thread; -class CPUDisAsm; - -class call_stack_list : public QListWidget -{ - Q_OBJECT - -public: - explicit call_stack_list(QWidget* parent); - -protected: - void mouseDoubleClickEvent(QMouseEvent* ev) override; - -Q_SIGNALS: - void RequestShowAddress(u32 addr, bool select_addr = true, bool force = false); -public Q_SLOTS: - void HandleUpdate(const std::vector>& call_stack); - -private Q_SLOTS: - void ShowItemAddress(); - -private: - void keyPressEvent(QKeyEvent* event) override; -}; diff --git a/rpcs3qt-legacy/camera_settings_dialog.cpp b/rpcs3qt-legacy/camera_settings_dialog.cpp deleted file mode 100644 index 42a8b37f5..000000000 --- a/rpcs3qt-legacy/camera_settings_dialog.cpp +++ /dev/null @@ -1,284 +0,0 @@ -#include "stdafx.h" -#include "camera_settings_dialog.h" -#include "ui_camera_settings_dialog.h" -#include "permissions.h" -#include "Emu/Io/camera_config.h" - -#include -#include -#include -#include - -LOG_CHANNEL(camera_log, "Camera"); - -template <> -void fmt_class_string::format(std::string& out, u64 arg) -{ - format_enum(out, arg, [](QVideoFrameFormat::PixelFormat value) - { - switch (value) - { - case QVideoFrameFormat::Format_ARGB8888: return "ARGB8888"; - case QVideoFrameFormat::Format_ARGB8888_Premultiplied: return "ARGB8888_Premultiplied"; - case QVideoFrameFormat::Format_XRGB8888: return "XRGB8888"; - case QVideoFrameFormat::Format_BGRA8888: return "BGRA8888"; - case QVideoFrameFormat::Format_BGRA8888_Premultiplied: return "BGRA8888_Premultiplied"; - case QVideoFrameFormat::Format_BGRX8888: return "BGRX8888"; - case QVideoFrameFormat::Format_ABGR8888: return "ABGR8888"; - case QVideoFrameFormat::Format_XBGR8888: return "XBGR8888"; - case QVideoFrameFormat::Format_RGBA8888: return "RGBA8888"; - case QVideoFrameFormat::Format_RGBX8888: return "RGBX8888"; - case QVideoFrameFormat::Format_AYUV: return "AYUV"; - case QVideoFrameFormat::Format_AYUV_Premultiplied: return "AYUV_Premultiplied"; - case QVideoFrameFormat::Format_YUV420P: return "YUV420P"; - case QVideoFrameFormat::Format_YUV422P: return "YUV422P"; - case QVideoFrameFormat::Format_YV12: return "YV12"; - case QVideoFrameFormat::Format_UYVY: return "UYVY"; - case QVideoFrameFormat::Format_YUYV: return "YUYV"; - case QVideoFrameFormat::Format_NV12: return "NV12"; - case QVideoFrameFormat::Format_NV21: return "NV21"; - case QVideoFrameFormat::Format_IMC1: return "IMC1"; - case QVideoFrameFormat::Format_IMC2: return "IMC2"; - case QVideoFrameFormat::Format_IMC3: return "IMC3"; - case QVideoFrameFormat::Format_IMC4: return "IMC4"; - case QVideoFrameFormat::Format_Y8: return "Y8"; - case QVideoFrameFormat::Format_Y16: return "Y16"; - case QVideoFrameFormat::Format_P010: return "P010"; - case QVideoFrameFormat::Format_P016: return "P016"; - case QVideoFrameFormat::Format_SamplerExternalOES: return "SamplerExternalOES"; - case QVideoFrameFormat::Format_Jpeg: return "Jpeg"; - case QVideoFrameFormat::Format_SamplerRect: return "SamplerRect"; - default: return unknown; - } - }); -} - -Q_DECLARE_METATYPE(QCameraDevice); - -camera_settings_dialog::camera_settings_dialog(QWidget* parent) - : QDialog(parent), ui(new Ui::camera_settings_dialog) -{ - ui->setupUi(this); - - load_config(); - - for (const QCameraDevice& camera_info : QMediaDevices::videoInputs()) - { - if (camera_info.isNull()) - continue; - ui->combo_camera->addItem(camera_info.description(), QVariant::fromValue(camera_info)); - camera_log.notice("Found camera: '%s'", camera_info.description()); - } - - connect(ui->combo_camera, QOverload::of(&QComboBox::currentIndexChanged), this, &camera_settings_dialog::handle_camera_change); - connect(ui->combo_settings, QOverload::of(&QComboBox::currentIndexChanged), this, &camera_settings_dialog::handle_settings_change); - connect(ui->buttonBox, &QDialogButtonBox::clicked, [this](QAbstractButton* button) - { - if (button == ui->buttonBox->button(QDialogButtonBox::Save)) - { - save_config(); - accept(); - } - else if (button == ui->buttonBox->button(QDialogButtonBox::Apply)) - { - save_config(); - } - }); - - if (ui->combo_camera->count() == 0) - { - ui->combo_camera->setEnabled(false); - ui->combo_settings->setEnabled(false); - ui->buttonBox->button(QDialogButtonBox::Save)->setEnabled(false); - ui->buttonBox->button(QDialogButtonBox::Apply)->setEnabled(false); - } - else - { - // TODO: show camera ID somewhere - ui->combo_camera->setCurrentIndex(0); - } -} - -camera_settings_dialog::~camera_settings_dialog() -{ -} - -void camera_settings_dialog::handle_camera_change(int index) -{ - if (index < 0 || !ui->combo_camera->itemData(index).canConvert()) - { - ui->combo_settings->clear(); - return; - } - - const QCameraDevice camera_info = ui->combo_camera->itemData(index).value(); - - if (camera_info.isNull()) - { - ui->combo_settings->clear(); - return; - } - - m_camera.reset(new QCamera(camera_info)); - m_media_capture_session.reset(new QMediaCaptureSession(nullptr)); - m_media_capture_session->setCamera(m_camera.get()); - m_media_capture_session->setVideoSink(ui->videoWidget->videoSink()); - - if (!m_camera->isAvailable()) - { - ui->combo_settings->clear(); - QMessageBox::warning(this, tr("Camera not available"), tr("The selected camera is not available.\nIt might be blocked by another application.")); - return; - } - - ui->combo_settings->blockSignals(true); - ui->combo_settings->clear(); - - QList settings = camera_info.videoFormats(); - std::sort(settings.begin(), settings.end(), [](const QCameraFormat& l, const QCameraFormat& r) -> bool - { - if (l.resolution().width() > r.resolution().width()) - return true; - if (l.resolution().width() < r.resolution().width()) - return false; - if (l.resolution().height() > r.resolution().height()) - return true; - if (l.resolution().height() < r.resolution().height()) - return false; - if (l.minFrameRate() > r.minFrameRate()) - return true; - if (l.minFrameRate() < r.minFrameRate()) - return false; - if (l.maxFrameRate() > r.maxFrameRate()) - return true; - if (l.maxFrameRate() < r.maxFrameRate()) - return false; - if (l.pixelFormat() > r.pixelFormat()) - return true; - if (l.pixelFormat() < r.pixelFormat()) - return false; - return false; - }); - - for (const QCameraFormat& setting : settings) - { - if (setting.isNull()) - continue; - const QString description = tr("%0x%1, %2-%3 FPS, Format=%4") - .arg(setting.resolution().width()) - .arg(setting.resolution().height()) - .arg(setting.minFrameRate()) - .arg(setting.maxFrameRate()) - .arg(QString::fromStdString(fmt::format("%s", setting.pixelFormat()))); - ui->combo_settings->addItem(description, QVariant::fromValue(setting)); - } - ui->combo_settings->blockSignals(false); - - if (ui->combo_settings->count() == 0) - { - ui->combo_settings->setEnabled(false); - } - else - { - // Load selected settings from config file - int index = 0; - bool success = false; - const std::string key = camera_info.id().toStdString(); - cfg_camera::camera_setting cfg_setting = g_cfg_camera.get_camera_setting(key, success); - - if (success) - { - camera_log.notice("Found config entry for camera \"%s\"", key); - - // Select matching drowdown entry - const double epsilon = 0.001; - - for (int i = 0; i < ui->combo_settings->count(); i++) - { - const QCameraFormat tmp = ui->combo_settings->itemData(i).value(); - - if (tmp.resolution().width() == cfg_setting.width && - tmp.resolution().height() == cfg_setting.height && - tmp.minFrameRate() >= (cfg_setting.min_fps - epsilon) && - tmp.minFrameRate() <= (cfg_setting.min_fps + epsilon) && - tmp.maxFrameRate() >= (cfg_setting.max_fps - epsilon) && - tmp.maxFrameRate() <= (cfg_setting.max_fps + epsilon) && - tmp.pixelFormat() == static_cast(cfg_setting.format)) - { - index = i; - break; - } - } - } - - ui->combo_settings->setCurrentIndex(std::max(0, index)); - ui->combo_settings->setEnabled(true); - - // Update config to match user interface outcome - const QCameraFormat setting = ui->combo_settings->currentData().value(); - cfg_setting.width = setting.resolution().width(); - cfg_setting.height = setting.resolution().height(); - cfg_setting.min_fps = setting.minFrameRate(); - cfg_setting.max_fps = setting.maxFrameRate(); - cfg_setting.format = static_cast(setting.pixelFormat()); - g_cfg_camera.set_camera_setting(key, cfg_setting); - } -} - -void camera_settings_dialog::handle_settings_change(int index) -{ - if (!m_camera) - { - return; - } - - if (!m_camera->isAvailable()) - { - QMessageBox::warning(this, tr("Camera not available"), tr("The selected camera is not available.\nIt might be blocked by another application.")); - return; - } - - if (!gui::utils::check_camera_permission(this, [this, index]() - { - handle_settings_change(index); - }, - [this]() - { - QMessageBox::warning(this, tr("Camera permissions denied!"), tr("RPCS3 has no permissions to access cameras on this device.")); - })) - { - return; - } - - if (index >= 0 && ui->combo_settings->itemData(index).canConvert() && ui->combo_camera->currentData().canConvert()) - { - const QCameraFormat setting = ui->combo_settings->itemData(index).value(); - if (!setting.isNull()) - { - m_camera->setCameraFormat(setting); - } - - cfg_camera::camera_setting cfg_setting; - cfg_setting.width = setting.resolution().width(); - cfg_setting.height = setting.resolution().height(); - cfg_setting.min_fps = setting.minFrameRate(); - cfg_setting.max_fps = setting.maxFrameRate(); - cfg_setting.format = static_cast(setting.pixelFormat()); - g_cfg_camera.set_camera_setting(ui->combo_camera->currentData().value().id().toStdString(), cfg_setting); - } - - m_camera->start(); -} - -void camera_settings_dialog::load_config() -{ - if (!g_cfg_camera.load()) - { - camera_log.notice("Could not load camera config. Using defaults."); - } -} - -void camera_settings_dialog::save_config() -{ - g_cfg_camera.save(); -} diff --git a/rpcs3qt-legacy/camera_settings_dialog.h b/rpcs3qt-legacy/camera_settings_dialog.h deleted file mode 100644 index da18f64cc..000000000 --- a/rpcs3qt-legacy/camera_settings_dialog.h +++ /dev/null @@ -1,31 +0,0 @@ -#pragma once - -#include -#include -#include - -namespace Ui -{ - class camera_settings_dialog; -} - -class camera_settings_dialog : public QDialog -{ - Q_OBJECT - -public: - camera_settings_dialog(QWidget* parent = nullptr); - virtual ~camera_settings_dialog(); - -private Q_SLOTS: - void handle_camera_change(int index); - void handle_settings_change(int index); - -private: - void load_config(); - void save_config(); - - std::unique_ptr ui; - std::unique_ptr m_camera; - std::unique_ptr m_media_capture_session; -}; diff --git a/rpcs3qt-legacy/camera_settings_dialog.ui b/rpcs3qt-legacy/camera_settings_dialog.ui deleted file mode 100644 index 8afe262f2..000000000 --- a/rpcs3qt-legacy/camera_settings_dialog.ui +++ /dev/null @@ -1,130 +0,0 @@ - - - camera_settings_dialog - - - - 0 - 0 - 356 - 380 - - - - Camera Settings - - - - - - - - Camera - - - - - - No cameras found - - - - - - - - - - Settings - - - - - - No settings found - - - - - - - - - - - - Preview - - - - - - - 64 - 48 - - - - true - - - - - - - - - - Qt::Horizontal - - - QDialogButtonBox::Apply|QDialogButtonBox::Cancel|QDialogButtonBox::Save - - - - - - - - QVideoWidget - QWidget -
qvideowidget.h
- 1 -
-
- - - - buttonBox - accepted() - camera_settings_dialog - accept() - - - 248 - 254 - - - 157 - 274 - - - - - buttonBox - rejected() - camera_settings_dialog - reject() - - - 316 - 260 - - - 286 - 274 - - - - -
diff --git a/rpcs3qt-legacy/category.h b/rpcs3qt-legacy/category.h deleted file mode 100644 index 26771aa0e..000000000 --- a/rpcs3qt-legacy/category.h +++ /dev/null @@ -1,56 +0,0 @@ -#pragma once - -#include -#include - -enum Category -{ - Disc_Game, - HDD_Game, - PS1_Game, - PS2_Game, - PSP_Game, - Home, - Media, - Data, - OS, - Unknown_Cat, - Others, -}; - -namespace cat -{ - const QString cat_app_music = "AM"; - const QString cat_app_photo = "AP"; - const QString cat_app_store = "AS"; - const QString cat_app_tv = "AT"; - const QString cat_app_video = "AV"; - const QString cat_bc_video = "BV"; - const QString cat_web_tv = "WT"; - const QString cat_home = "HM"; - const QString cat_network = "CB"; - const QString cat_store_fe = "SF"; - const QString cat_disc_game = "DG"; - const QString cat_hdd_game = "HG"; - const QString cat_ps2_game = "2P"; - const QString cat_ps2_inst = "2G"; - const QString cat_ps1_game = "1P"; - const QString cat_psp_game = "PP"; - const QString cat_psp_mini = "MN"; - const QString cat_psp_rema = "PE"; - const QString cat_ps3_data = "GD"; - const QString cat_ps2_data = "2D"; - const QString cat_ps3_save = "SD"; - const QString cat_psp_save = "MS"; - - const QString cat_ps3_os = "/OS"; - - const QString cat_unknown = "Unknown"; - - const QStringList ps2_games = {cat_ps2_game, cat_ps2_inst}; - const QStringList psp_games = {cat_psp_game, cat_psp_mini, cat_psp_rema}; - const QStringList media = {cat_app_photo, cat_app_video, cat_bc_video, cat_app_music, cat_app_store, cat_app_tv, cat_web_tv}; - const QStringList data = {cat_ps3_data, cat_ps2_data, cat_ps3_save, cat_psp_save}; - const QStringList os = {cat_ps3_os}; - const QStringList others = {cat_network, cat_store_fe}; -} // namespace cat diff --git a/rpcs3qt-legacy/cg_disasm_window.cpp b/rpcs3qt-legacy/cg_disasm_window.cpp deleted file mode 100644 index 633fbbcf2..000000000 --- a/rpcs3qt-legacy/cg_disasm_window.cpp +++ /dev/null @@ -1,172 +0,0 @@ -#include "cg_disasm_window.h" -#include "gui_settings.h" -#include "syntax_highlighter.h" - -#include -#include -#include -#include -#include -#include - -#include "Emu/RSX/Program/CgBinaryProgram.h" - -LOG_CHANNEL(gui_log, "GUI"); - -cg_disasm_window::cg_disasm_window(std::shared_ptr gui_settings) - : m_gui_settings(std::move(gui_settings)) -{ - setWindowTitle(tr("Cg Disasm")); - setObjectName("cg_disasm"); - setAttribute(Qt::WA_DeleteOnClose); - setAttribute(Qt::WA_StyledBackground); - setAcceptDrops(true); - setMinimumSize(QSize(200, 150)); // seems fine on win 10 - resize(QSize(620, 395)); - - m_path_last = m_gui_settings->GetValue(gui::fd_cg_disasm).toString(); - - m_disasm_text = new QTextEdit(this); - m_disasm_text->setReadOnly(true); - m_disasm_text->setWordWrapMode(QTextOption::NoWrap); - m_disasm_text->setFont(QFontDatabase::systemFont(QFontDatabase::FixedFont)); - - m_glsl_text = new QTextEdit(this); - m_glsl_text->setReadOnly(true); - m_glsl_text->setWordWrapMode(QTextOption::NoWrap); - m_glsl_text->setFont(QFontDatabase::systemFont(QFontDatabase::FixedFont)); - - // m_disasm_text syntax highlighter - sh_asm = new AsmHighlighter(m_disasm_text->document()); - - // m_glsl_text syntax highlighter - sh_glsl = new GlslHighlighter(m_glsl_text->document()); - - QSplitter* splitter = new QSplitter(); - splitter->addWidget(m_disasm_text); - splitter->addWidget(m_glsl_text); - - QHBoxLayout* layout = new QHBoxLayout(); - layout->addWidget(splitter); - - setLayout(layout); - - m_disasm_text->setContextMenuPolicy(Qt::CustomContextMenu); - m_glsl_text->setContextMenuPolicy(Qt::CustomContextMenu); - connect(m_disasm_text, &QWidget::customContextMenuRequested, this, &cg_disasm_window::ShowContextMenu); - connect(m_glsl_text, &QWidget::customContextMenuRequested, this, &cg_disasm_window::ShowContextMenu); - - ShowDisasm(); -} - -void cg_disasm_window::ShowContextMenu(const QPoint& pos) -{ - QMenu menu; - QAction* clear = new QAction(tr("&Clear")); - QAction* open = new QAction(tr("Open &Cg binary program")); - - menu.addAction(open); - menu.addSeparator(); - menu.addAction(clear); - - connect(clear, &QAction::triggered, [this]() - { - m_disasm_text->clear(); - m_glsl_text->clear(); - }); - - connect(open, &QAction::triggered, [this]() - { - const QString file_path = QFileDialog::getOpenFileName(this, tr("Select Cg program object"), m_path_last, tr("Cg program objects (*.fpo;*.vpo);;")); - if (file_path.isEmpty()) - return; - m_path_last = file_path; - ShowDisasm(); - }); - - const auto obj = qobject_cast(sender()); - - QPoint origin; - - if (obj == m_disasm_text) - { - origin = m_disasm_text->viewport()->mapToGlobal(pos); - } - else if (obj == m_glsl_text) - { - origin = m_glsl_text->viewport()->mapToGlobal(pos); - } - else - { - origin = mapToGlobal(pos); - } - - menu.exec(origin); -} - -void cg_disasm_window::ShowDisasm() const -{ - if (QFileInfo(m_path_last).isFile()) - { - CgBinaryDisasm disasm(m_path_last.toStdString()); - disasm.BuildShaderBody(); - m_disasm_text->setText(QString::fromStdString(disasm.GetArbShader())); - m_glsl_text->setText(QString::fromStdString(disasm.GetGlslShader())); - m_gui_settings->SetValue(gui::fd_cg_disasm, m_path_last); - } - else if (!m_path_last.isEmpty()) - { - gui_log.error("CgDisasm: Failed to open %s", m_path_last); - } -} - -bool cg_disasm_window::IsValidFile(const QMimeData& md, bool save) -{ - const QList urls = md.urls(); - - if (urls.count() > 1) - { - return false; - } - - const QString suff = QFileInfo(urls[0].fileName()).suffix().toLower(); - - if (suff == "fpo" || suff == "vpo") - { - if (save) - { - m_path_last = urls[0].toLocalFile(); - } - return true; - } - return false; -} - -void cg_disasm_window::dropEvent(QDropEvent* ev) -{ - if (IsValidFile(*ev->mimeData(), true)) - { - ShowDisasm(); - } -} - -void cg_disasm_window::dragEnterEvent(QDragEnterEvent* ev) -{ - if (IsValidFile(*ev->mimeData())) - { - ev->accept(); - } -} - -void cg_disasm_window::dragMoveEvent(QDragMoveEvent* ev) -{ - if (IsValidFile(*ev->mimeData())) - { - ev->accept(); - } -} - -void cg_disasm_window::dragLeaveEvent(QDragLeaveEvent* ev) -{ - ev->accept(); -} diff --git a/rpcs3qt-legacy/cg_disasm_window.h b/rpcs3qt-legacy/cg_disasm_window.h deleted file mode 100644 index 46e86da15..000000000 --- a/rpcs3qt-legacy/cg_disasm_window.h +++ /dev/null @@ -1,40 +0,0 @@ -#pragma once - -#include -#include - -#include - -class AsmHighlighter; -class GlslHighlighter; -class gui_settings; - -class cg_disasm_window : public QWidget -{ - Q_OBJECT - -private Q_SLOTS: - void ShowContextMenu(const QPoint& pos); - -private: - void ShowDisasm() const; - bool IsValidFile(const QMimeData& md, bool save = false); - - QString m_path_last; - QTextEdit* m_disasm_text; - QTextEdit* m_glsl_text; - - std::shared_ptr m_gui_settings; - - AsmHighlighter* sh_asm; - GlslHighlighter* sh_glsl; - -public: - explicit cg_disasm_window(std::shared_ptr xSettings); - -protected: - void dropEvent(QDropEvent* ev) override; - void dragEnterEvent(QDragEnterEvent* ev) override; - void dragMoveEvent(QDragMoveEvent* ev) override; - void dragLeaveEvent(QDragLeaveEvent* ev) override; -}; diff --git a/rpcs3qt-legacy/cheat_manager.cpp b/rpcs3qt-legacy/cheat_manager.cpp deleted file mode 100644 index 2e6c756ba..000000000 --- a/rpcs3qt-legacy/cheat_manager.cpp +++ /dev/null @@ -1,1063 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include - -#include "cheat_manager.h" - -#include "Emu/System.h" -#include "Emu/Memory/vm.h" -#include "Emu/CPU/CPUThread.h" - -#include "Emu/IdManager.h" -#include "Emu/Cell/PPUAnalyser.h" -#include "Emu/Cell/PPUInterpreter.h" -#include "Emu/Cell/lv2/sys_sync.h" - -#include "util/yaml.hpp" -#include "util/asm.hpp" -#include "util/logs.hpp" -#include "util/to_endian.hpp" -#include "util/File.h" -#include "util/StrUtil.h" -#include "util/bin_patch.h" // get_patches_path() - -LOG_CHANNEL(log_cheat, "Cheat"); - -cheat_manager_dialog* cheat_manager_dialog::inst = nullptr; - -YAML::Emitter& operator<<(YAML::Emitter& out, const cheat_info& rhs) -{ - std::string type_formatted; - fmt::append(type_formatted, "%s", rhs.type); - - out << YAML::BeginSeq << rhs.description << type_formatted << rhs.red_script << YAML::EndSeq; - - return out; -} - -cheat_engine::cheat_engine() -{ - const std::string patches_path = patch_engine::get_patches_path(); - - if (!fs::create_path(patches_path)) - { - log_cheat.fatal("Failed to create path: %s (%s)", patches_path, fs::g_tls_error); - return; - } - - const std::string path = patches_path + m_cheats_filename; - - if (fs::file cheat_file{path, fs::read + fs::create}) - { - auto [yml_cheats, error] = yaml_load(cheat_file.to_string()); - - if (!error.empty()) - { - log_cheat.error("Error parsing %s: %s", path, error); - return; - } - - for (const auto& yml_cheat : yml_cheats) - { - const std::string& game_name = yml_cheat.first.Scalar(); - - for (const auto& yml_offset : yml_cheat.second) - { - const u32 offset = get_yaml_node_value(yml_offset.first, error); - if (!error.empty()) - { - log_cheat.error("Error parsing %s: node key %s is not a u32 offset", path, yml_offset.first.Scalar()); - return; - } - - cheat_info cheat = get_yaml_node_value(yml_offset.second, error); - if (!error.empty()) - { - log_cheat.error("Error parsing %s: node %s is not a cheat_info node", path, yml_offset.first.Scalar()); - return; - } - - cheat.game = game_name; - cheat.offset = offset; - cheats[game_name][offset] = std::move(cheat); - } - } - } - else - { - log_cheat.error("Error loading %s", path); - } -} - -void cheat_engine::save() const -{ - const std::string patches_path = patch_engine::get_patches_path(); - - if (!fs::create_path(patches_path)) - { - log_cheat.fatal("Failed to create path: %s (%s)", patches_path, fs::g_tls_error); - return; - } - - const std::string path = patches_path + m_cheats_filename; - - fs::file cheat_file(path, fs::rewrite); - if (!cheat_file) - return; - - YAML::Emitter out; - - out << YAML::BeginMap; - for (const auto& game_entry : cheats) - { - out << game_entry.first; - out << YAML::BeginMap; - for (const auto& offset_entry : game_entry.second) - { - out << YAML::Hex << offset_entry.first; - out << offset_entry.second; - } - out << YAML::EndMap; - } - out << YAML::EndMap; - - cheat_file.write(out.c_str(), out.size()); -} - -void cheat_engine::import_cheats_from_str(const std::string& str_cheats) -{ - auto cheats_vec = fmt::split(str_cheats, {"^^^"}); - - for (auto& cheat_line : cheats_vec) - { - cheat_info new_cheat; - if (new_cheat.from_str(cheat_line)) - cheats[new_cheat.game][new_cheat.offset] = new_cheat; - } -} - -std::string cheat_engine::export_cheats_to_str() const -{ - std::string cheats_str; - - for (const auto& game : cheats) - { - for (const auto& offset : ::at32(cheats, game.first)) - { - cheats_str += offset.second.to_str(); - cheats_str += "^^^"; - } - } - - return cheats_str; -} - -bool cheat_engine::exist(const std::string& game, const u32 offset) const -{ - return cheats.contains(game) && ::at32(cheats, game).contains(offset); -} - -void cheat_engine::add(const std::string& game, const std::string& description, const cheat_type type, const u32 offset, const std::string& red_script) -{ - cheats[game][offset] = cheat_info{game, description, type, offset, red_script}; -} - -cheat_info* cheat_engine::get(const std::string& game, const u32 offset) -{ - if (!exist(game, offset)) - return nullptr; - - return &cheats[game][offset]; -} - -bool cheat_engine::erase(const std::string& game, const u32 offset) -{ - if (!exist(game, offset)) - return false; - - cheats[game].erase(offset); - return true; -} - -bool cheat_engine::resolve_script(u32& final_offset, const u32 offset, const std::string& red_script) -{ - enum operand - { - operand_equal, - operand_add, - operand_sub - }; - - auto do_operation = [](const operand op, u32& param1, const u32 param2) -> u32 - { - switch (op) - { - case operand_equal: return param1 = param2; - case operand_add: return param1 += param2; - case operand_sub: return param1 -= param2; - } - - return ensure(0); - }; - - operand cur_op = operand_equal; - u32 index = 0; - - while (index < red_script.size()) - { - if (std::isdigit(static_cast(red_script[index]))) - { - std::string num_string; - for (; index < red_script.size(); index++) - { - if (!std::isdigit(static_cast(red_script[index]))) - break; - - num_string += red_script[index]; - } - - const u32 num_value = std::stoul(num_string); - do_operation(cur_op, final_offset, num_value); - } - else - { - switch (red_script[index]) - { - case '$': - { - do_operation(cur_op, final_offset, offset); - index++; - break; - } - case '[': - { - // find corresponding ] - s32 found_close = 1; - std::string sub_script; - for (index++; index < red_script.size(); index++) - { - if (found_close == 0) - break; - - if (red_script[index] == ']') - found_close--; - else if (red_script[index] == '[') - found_close++; - - if (found_close != 0) - sub_script += red_script[index]; - } - - if (found_close) - return false; - - // Resolves content of [] - u32 res_addr = 0; - if (!resolve_script(res_addr, offset, sub_script)) - return false; - - // Tries to get value at resolved address - bool success; - const u32 res_value = get_value(res_addr, success); - - if (!success) - return false; - - do_operation(cur_op, final_offset, res_value); - break; - } - case '+': - cur_op = operand_add; - index++; - break; - case '-': - cur_op = operand_sub; - index++; - break; - case ' ': index++; break; - default: log_cheat.fatal("invalid character in redirection script"); return false; - } - } - } - - return true; -} - -template -std::vector cheat_engine::search(const T value, const std::vector& to_filter) -{ - std::vector results; - - to_be_t value_swapped = value; - - if (Emu.IsStopped()) - return {}; - - cpu_thread::suspend_all(nullptr, {}, [&] - { - if (!to_filter.empty()) - { - for (const auto& off : to_filter) - { - if (vm::check_addr(off)) - { - if (*vm::get_super_ptr(off) == value_swapped) - results.push_back(off); - } - } - } - else - { - // Looks through mapped memory - for (u32 page_start = 0x10000; page_start < 0xF0000000; page_start += 4096) - { - if (vm::check_addr(page_start)) - { - // Assumes the values are aligned - for (u32 index = 0; index < 4096; index += sizeof(T)) - { - if (*vm::get_super_ptr(page_start + index) == value_swapped) - results.push_back(page_start + index); - } - } - } - } - }); - - return results; -} - -template -T cheat_engine::get_value(const u32 offset, bool& success) -{ - if (Emu.IsStopped()) - { - success = false; - return 0; - } - - return cpu_thread::suspend_all(nullptr, {}, [&]() -> T - { - if (!vm::check_addr(offset)) - { - success = false; - return 0; - } - - success = true; - return *vm::get_super_ptr(offset); - }); -} - -template -bool cheat_engine::set_value(const u32 offset, const T value) -{ - if (Emu.IsStopped()) - return false; - - if (!vm::check_addr(offset)) - { - return false; - } - - return cpu_thread::suspend_all(nullptr, {}, [&] - { - if (!vm::check_addr(offset)) - { - return false; - } - - *vm::get_super_ptr(offset) = value; - - const bool exec_code_at_start = vm::check_addr(offset, vm::page_executable); - const bool exec_code_at_end = [&]() - { - if constexpr (sizeof(T) == 1) - { - return exec_code_at_start; - } - else - { - return vm::check_addr(offset + sizeof(T) - 1, vm::page_executable); - } - }(); - - if (exec_code_at_end || exec_code_at_start) - { - extern void ppu_register_function_at(u32, u32, ppu_intrp_func_t); - - u32 addr = offset, size = sizeof(T); - - if (exec_code_at_end && exec_code_at_start) - { - size = utils::align(addr + size, 4) - (addr & -4); - addr &= -4; - } - else if (exec_code_at_end) - { - size -= utils::align(size - 4096 + (addr & 4095), 4); - addr = utils::align(addr, 4096); - } - else if (exec_code_at_start) - { - size = utils::align(4096 - (addr & 4095), 4); - addr &= -4; - } - - // Reinitialize executable code - ppu_register_function_at(addr, size, nullptr); - } - - return true; - }); -} - -bool cheat_engine::is_addr_safe(const u32 offset) -{ - if (Emu.IsStopped()) - return false; - - const auto ppum = g_fxo->try_get>(); - - if (!ppum) - { - log_cheat.fatal("Failed to get ppu_module"); - return false; - } - - std::vector> segs; - - for (const auto& seg : ppum->segs) - { - if ((seg.flags & 3)) - { - segs.emplace_back(seg.addr, seg.size); - } - } - - if (segs.empty()) - { - log_cheat.fatal("Couldn't find a +rw-x section"); - return false; - } - - for (const auto& seg : segs) - { - if (offset >= seg.first && offset < (seg.first + seg.second)) - return true; - } - - return false; -} - -u32 cheat_engine::reverse_lookup(const u32 addr, const u32 max_offset, const u32 max_depth, const u32 cur_depth) -{ - for (u32 index = 0; index <= max_offset; index += 4) - { - std::vector ptrs = search(addr - index, {}); - - log_cheat.fatal("Found %d pointer(s) for addr 0x%x [offset: %d cur_depth:%d]", ptrs.size(), addr, index, cur_depth); - - for (const auto& ptr : ptrs) - { - if (is_addr_safe(ptr)) - return ptr; - } - - // If depth has not been reached dig deeper - if (!ptrs.empty() && cur_depth < max_depth) - { - for (const auto& ptr : ptrs) - { - const u32 result = reverse_lookup(ptr, max_offset, max_depth, cur_depth + 1); - if (result) - return result; - } - } - } - - return 0; -} - -enum cheat_table_columns : int -{ - title = 0, - description, - type, - offset, - script -}; - -cheat_manager_dialog::cheat_manager_dialog(QWidget* parent) - : QDialog(parent) -{ - setWindowTitle(tr("Cheat Manager")); - setObjectName("cheat_manager"); - setMinimumSize(QSize(800, 400)); - - QVBoxLayout* main_layout = new QVBoxLayout(); - - tbl_cheats = new QTableWidget(this); - tbl_cheats->setSelectionMode(QAbstractItemView::SelectionMode::ExtendedSelection); - tbl_cheats->setSelectionBehavior(QAbstractItemView::SelectRows); - tbl_cheats->setContextMenuPolicy(Qt::CustomContextMenu); - tbl_cheats->setColumnCount(5); - tbl_cheats->setHorizontalHeaderLabels(QStringList() << tr("Game") << tr("Description") << tr("Type") << tr("Offset") << tr("Script")); - main_layout->addWidget(tbl_cheats); - - QHBoxLayout* btn_layout = new QHBoxLayout(); - QLabel* lbl_value_final = new QLabel(tr("Current Value:")); - edt_value_final = new QLineEdit(); - btn_apply = new QPushButton(tr("Apply"), this); - btn_apply->setEnabled(false); - btn_layout->addWidget(lbl_value_final); - btn_layout->addWidget(edt_value_final); - btn_layout->addWidget(btn_apply); - main_layout->addLayout(btn_layout); - - QGroupBox* grp_add_cheat = new QGroupBox(tr("Cheat Search")); - QVBoxLayout* grp_add_cheat_layout = new QVBoxLayout(); - QHBoxLayout* grp_add_cheat_sub_layout = new QHBoxLayout(); - QPushButton* btn_new_search = new QPushButton(tr("New Search")); - btn_new_search->setEnabled(false); - btn_filter_results = new QPushButton(tr("Filter Results")); - btn_filter_results->setEnabled(false); - edt_cheat_search_value = new QLineEdit(); - cbx_cheat_search_type = new QComboBox(); - - for (u64 i = 0; i < cheat_type_max; i++) - { - const QString item_text = get_localized_cheat_type(static_cast(i)); - cbx_cheat_search_type->addItem(item_text); - } - cbx_cheat_search_type->setCurrentIndex(static_cast(cheat_type::signed_32_cheat)); - grp_add_cheat_sub_layout->addWidget(btn_new_search); - grp_add_cheat_sub_layout->addWidget(btn_filter_results); - grp_add_cheat_sub_layout->addWidget(edt_cheat_search_value); - grp_add_cheat_sub_layout->addWidget(cbx_cheat_search_type); - grp_add_cheat_layout->addLayout(grp_add_cheat_sub_layout); - lst_search = new QListWidget(this); - lst_search->setSelectionMode(QAbstractItemView::SelectionMode::SingleSelection); - lst_search->setSelectionBehavior(QAbstractItemView::SelectRows); - lst_search->setContextMenuPolicy(Qt::CustomContextMenu); - grp_add_cheat_layout->addWidget(lst_search); - grp_add_cheat->setLayout(grp_add_cheat_layout); - main_layout->addWidget(grp_add_cheat); - - setLayout(main_layout); - - // Edit/Manage UI - connect(tbl_cheats, &QTableWidget::itemClicked, [this](QTableWidgetItem* item) - { - if (!item) - return; - - const int row = item->row(); - - if (row == -1) - return; - - cheat_info* cheat = g_cheat.get(tbl_cheats->item(row, cheat_table_columns::title)->text().toStdString(), tbl_cheats->item(row, cheat_table_columns::offset)->data(Qt::UserRole).toUInt()); - if (!cheat) - { - log_cheat.fatal("Failed to retrieve cheat selected from internal cheat_engine"); - return; - } - - u32 final_offset; - if (!cheat->red_script.empty()) - { - final_offset = 0; - if (!cheat_engine::resolve_script(final_offset, cheat->offset, cheat->red_script)) - { - btn_apply->setEnabled(false); - edt_value_final->setText(tr("Failed to resolve redirection script")); - return; - } - } - else - { - final_offset = cheat->offset; - } - - if (Emu.IsStopped()) - { - btn_apply->setEnabled(false); - edt_value_final->setText(tr("This Application is not running")); - return; - } - - bool success = false; - u64 result_value{}; - f64 result_value_f{}; - - switch (cheat->type) - { - case cheat_type::unsigned_8_cheat: result_value = cheat_engine::get_value(final_offset, success); break; - case cheat_type::unsigned_16_cheat: result_value = cheat_engine::get_value(final_offset, success); break; - case cheat_type::unsigned_32_cheat: result_value = cheat_engine::get_value(final_offset, success); break; - case cheat_type::unsigned_64_cheat: result_value = cheat_engine::get_value(final_offset, success); break; - case cheat_type::signed_8_cheat: result_value = cheat_engine::get_value(final_offset, success); break; - case cheat_type::signed_16_cheat: result_value = cheat_engine::get_value(final_offset, success); break; - case cheat_type::signed_32_cheat: result_value = cheat_engine::get_value(final_offset, success); break; - case cheat_type::signed_64_cheat: result_value = cheat_engine::get_value(final_offset, success); break; - case cheat_type::float_32_cheat: result_value_f = cheat_engine::get_value(final_offset, success); break; - default: log_cheat.fatal("Unsupported cheat type"); return; - } - - if (success) - { - if (cheat->type >= cheat_type::signed_8_cheat && cheat->type <= cheat_type::signed_64_cheat) - edt_value_final->setText(tr("%1").arg(static_cast(result_value))); - else if (cheat->type == cheat_type::float_32_cheat) - edt_value_final->setText(tr("%1").arg(result_value_f)); - else - edt_value_final->setText(tr("%1").arg(result_value)); - } - else - { - edt_value_final->setText(tr("Failed to get the value from memory")); - } - - btn_apply->setEnabled(success); - }); - - connect(tbl_cheats, &QTableWidget::cellChanged, [this](int row, int column) - { - QTableWidgetItem* item = tbl_cheats->item(row, column); - if (!item) - { - return; - } - - if (column != cheat_table_columns::description && column != cheat_table_columns::script) - { - log_cheat.fatal("A column other than description and script was edited"); - return; - } - - cheat_info* cheat = g_cheat.get(tbl_cheats->item(row, cheat_table_columns::title)->text().toStdString(), tbl_cheats->item(row, cheat_table_columns::offset)->data(Qt::UserRole).toUInt()); - if (!cheat) - { - log_cheat.fatal("Failed to retrieve cheat edited from internal cheat_engine"); - return; - } - - switch (column) - { - case cheat_table_columns::description: cheat->description = item->text().toStdString(); break; - case cheat_table_columns::script: cheat->red_script = item->text().toStdString(); break; - default: break; - } - - g_cheat.save(); - }); - - connect(tbl_cheats, &QTableWidget::customContextMenuRequested, [this](const QPoint& loc) - { - const QPoint globalPos = tbl_cheats->mapToGlobal(loc); - QMenu* menu = new QMenu(); - QAction* delete_cheats = new QAction(tr("Delete"), menu); - QAction* import_cheats = new QAction(tr("Import Cheats")); - QAction* export_cheats = new QAction(tr("Export Cheats")); - QAction* reverse_cheat = new QAction(tr("Reverse-Lookup Cheat")); - - connect(delete_cheats, &QAction::triggered, [this]() - { - const auto selected = tbl_cheats->selectedItems(); - - std::set rows; - - for (const auto& sel : selected) - { - const int row = sel->row(); - - if (rows.count(row)) - continue; - - g_cheat.erase(tbl_cheats->item(row, cheat_table_columns::title)->text().toStdString(), tbl_cheats->item(row, cheat_table_columns::offset)->data(Qt::UserRole).toUInt()); - rows.insert(row); - } - - update_cheat_list(); - }); - - connect(import_cheats, &QAction::triggered, [this]() - { - QClipboard* clipboard = QGuiApplication::clipboard(); - g_cheat.import_cheats_from_str(clipboard->text().toStdString()); - update_cheat_list(); - }); - - connect(export_cheats, &QAction::triggered, [this]() - { - const auto selected = tbl_cheats->selectedItems(); - - std::set rows; - std::string export_string; - - for (const auto& sel : selected) - { - const int row = sel->row(); - - if (rows.count(row)) - continue; - - cheat_info* cheat = g_cheat.get(tbl_cheats->item(row, cheat_table_columns::title)->text().toStdString(), tbl_cheats->item(row, cheat_table_columns::offset)->data(Qt::UserRole).toUInt()); - if (cheat) - export_string += cheat->to_str() + "^^^"; - - rows.insert(row); - } - - QClipboard* clipboard = QGuiApplication::clipboard(); - clipboard->setText(QString::fromStdString(export_string)); - }); - - connect(reverse_cheat, &QAction::triggered, [this]() - { - QTableWidgetItem* item = tbl_cheats->item(tbl_cheats->currentRow(), cheat_table_columns::offset); - if (item) - { - const u32 offset = item->data(Qt::UserRole).toUInt(); - const u32 result = cheat_engine::reverse_lookup(offset, 32, 12); - - log_cheat.fatal("Result is 0x%x", result); - } - }); - - menu->addAction(delete_cheats); - menu->addSeparator(); - // menu->addAction(reverse_cheat); - // menu->addSeparator(); - menu->addAction(import_cheats); - menu->addAction(export_cheats); - menu->exec(globalPos); - }); - - connect(btn_apply, &QPushButton::clicked, [this](bool /*checked*/) - { - const int row = tbl_cheats->currentRow(); - cheat_info* cheat = g_cheat.get(tbl_cheats->item(row, cheat_table_columns::title)->text().toStdString(), tbl_cheats->item(row, cheat_table_columns::offset)->data(Qt::UserRole).toUInt()); - - if (!cheat) - { - log_cheat.fatal("Failed to retrieve cheat selected from internal cheat_engine"); - return; - } - - std::pair results; - - u32 final_offset; - if (!cheat->red_script.empty()) - { - final_offset = 0; - if (!g_cheat.resolve_script(final_offset, cheat->offset, cheat->red_script)) - { - btn_apply->setEnabled(false); - edt_value_final->setText(tr("Failed to resolve redirection script")); - } - } - else - { - final_offset = cheat->offset; - } - - // TODO: better way to do this? - switch (static_cast(cbx_cheat_search_type->currentIndex())) - { - case cheat_type::unsigned_8_cheat: results = convert_and_set(final_offset); break; - case cheat_type::unsigned_16_cheat: results = convert_and_set(final_offset); break; - case cheat_type::unsigned_32_cheat: results = convert_and_set(final_offset); break; - case cheat_type::unsigned_64_cheat: results = convert_and_set(final_offset); break; - case cheat_type::signed_8_cheat: results = convert_and_set(final_offset); break; - case cheat_type::signed_16_cheat: results = convert_and_set(final_offset); break; - case cheat_type::signed_32_cheat: results = convert_and_set(final_offset); break; - case cheat_type::signed_64_cheat: results = convert_and_set(final_offset); break; - case cheat_type::float_32_cheat: results = convert_and_set(final_offset); break; - default: log_cheat.fatal("Unsupported cheat type"); return; - } - - if (!results.first) - { - QMessageBox::warning(this, tr("Error converting value"), tr("Couldn't convert the value you typed to the integer type of that cheat"), QMessageBox::Ok); - return; - } - - if (!results.second) - { - QMessageBox::warning(this, tr("Error applying value"), tr("Couldn't patch memory"), QMessageBox::Ok); - return; - } - }); - - // Search UI - connect(btn_new_search, &QPushButton::clicked, [this](bool /*checked*/) - { - offsets_found.clear(); - do_the_search(); - }); - - connect(edt_cheat_search_value, &QLineEdit::textChanged, this, [btn_new_search, this](const QString& text) - { - if (btn_new_search) - { - btn_new_search->setEnabled(!text.isEmpty()); - } - if (btn_filter_results) - { - btn_filter_results->setEnabled(!text.isEmpty() && !offsets_found.empty()); - } - }); - - connect(btn_filter_results, &QPushButton::clicked, [this](bool /*checked*/) - { - do_the_search(); - }); - - connect(lst_search, &QListWidget::customContextMenuRequested, [this](const QPoint& loc) - { - const QPoint globalPos = lst_search->mapToGlobal(loc); - const int current_row = lst_search->currentRow(); - QListWidgetItem* item = lst_search->item(current_row); - - // Skip if the item was a placeholder - if (!item || item->data(Qt::UserRole).toBool()) - return; - - QMenu* menu = new QMenu(); - - QAction* add_to_cheat_list = new QAction(tr("Add to cheat list"), menu); - - const u32 offset = offsets_found[current_row]; - const cheat_type type = static_cast(cbx_cheat_search_type->currentIndex()); - - connect(add_to_cheat_list, &QAction::triggered, [name = Emu.GetTitle(), offset, type, this]() - { - if (g_cheat.exist(name, offset)) - { - if (QMessageBox::question(this, tr("Cheat already exists"), tr("Do you want to overwrite the existing cheat?"), QMessageBox::Ok | QMessageBox::Cancel) != QMessageBox::Ok) - return; - } - - std::string comment; - if (!cheat_engine::is_addr_safe(offset)) - comment = "Unsafe"; - - g_cheat.add(name, comment, type, offset, ""); - update_cheat_list(); - }); - - menu->addAction(add_to_cheat_list); - menu->exec(globalPos); - }); - - update_cheat_list(); -} - -cheat_manager_dialog::~cheat_manager_dialog() -{ - inst = nullptr; -} - -cheat_manager_dialog* cheat_manager_dialog::get_dlg(QWidget* parent) -{ - if (inst == nullptr) - inst = new cheat_manager_dialog(parent); - - return inst; -} - -template -T cheat_manager_dialog::convert_from_QString(const QString& str, bool& success) -{ - if constexpr (std::is_same_v) - { - const u16 result_16 = str.toUShort(&success); - - if (result_16 > 0xFF) - success = false; - - return static_cast(result_16); - } - - if constexpr (std::is_same_v) - return str.toUShort(&success); - - if constexpr (std::is_same_v) - return str.toUInt(&success); - - if constexpr (std::is_same_v) - return str.toULongLong(&success); - - if constexpr (std::is_same_v) - { - const s16 result_16 = str.toShort(&success); - if (result_16 < -128 || result_16 > 127) - success = false; - - return static_cast(result_16); - } - - if constexpr (std::is_same_v) - return str.toShort(&success); - - if constexpr (std::is_same_v) - return str.toInt(&success); - - if constexpr (std::is_same_v) - return str.toLongLong(&success); - - if constexpr (std::is_same_v) - return str.toFloat(&success); - - return {}; -} - -template -bool cheat_manager_dialog::convert_and_search() -{ - bool res_conv = false; - const QString to_search = edt_cheat_search_value->text(); - - const T value = convert_from_QString(to_search, res_conv); - - if (!res_conv) - return false; - - offsets_found = cheat_engine::search(value, offsets_found); - return true; -} - -template -std::pair cheat_manager_dialog::convert_and_set(u32 offset) -{ - bool res_conv = false; - const QString to_set = edt_value_final->text(); - - const T value = convert_from_QString(to_set, res_conv); - - if (!res_conv) - return {false, false}; - - return {true, cheat_engine::set_value(offset, value)}; -} - -void cheat_manager_dialog::do_the_search() -{ - bool res_conv = false; - - // TODO: better way to do this? - switch (static_cast(cbx_cheat_search_type->currentIndex())) - { - case cheat_type::unsigned_8_cheat: res_conv = convert_and_search(); break; - case cheat_type::unsigned_16_cheat: res_conv = convert_and_search(); break; - case cheat_type::unsigned_32_cheat: res_conv = convert_and_search(); break; - case cheat_type::unsigned_64_cheat: res_conv = convert_and_search(); break; - case cheat_type::signed_8_cheat: res_conv = convert_and_search(); break; - case cheat_type::signed_16_cheat: res_conv = convert_and_search(); break; - case cheat_type::signed_32_cheat: res_conv = convert_and_search(); break; - case cheat_type::signed_64_cheat: res_conv = convert_and_search(); break; - case cheat_type::float_32_cheat: res_conv = convert_and_search(); break; - default: log_cheat.fatal("Unsupported cheat type"); break; - } - - if (!res_conv) - { - QMessageBox::warning(this, tr("Error converting value"), tr("Couldn't convert the search value you typed to the integer type you selected"), QMessageBox::Ok); - return; - } - - lst_search->clear(); - - const usz size = offsets_found.size(); - - if (size == 0) - { - QListWidgetItem* item = new QListWidgetItem(tr("Nothing found")); - item->setData(Qt::UserRole, true); - lst_search->insertItem(0, item); - } - else if (size > 10000) - { - // Only show entries below a fixed amount. Too many entries can take forever to render and fill up memory quickly. - QListWidgetItem* item = new QListWidgetItem(tr("Too many entries to display (%0)").arg(size)); - item->setData(Qt::UserRole, true); - lst_search->insertItem(0, item); - } - else - { - for (u32 row = 0; row < size; row++) - { - lst_search->insertItem(row, QString("0x%0").arg(offsets_found[row], 1, 16).toUpper()); - } - } - - btn_filter_results->setEnabled(!offsets_found.empty() && edt_cheat_search_value && !edt_cheat_search_value->text().isEmpty()); -} - -void cheat_manager_dialog::update_cheat_list() -{ - usz num_rows = 0; - for (const auto& name : g_cheat.cheats) - num_rows += name.second.size(); - - tbl_cheats->setRowCount(::narrow(num_rows)); - - u32 row = 0; - { - const QSignalBlocker blocker(tbl_cheats); - for (const auto& game : g_cheat.cheats) - { - for (const auto& offset : game.second) - { - QTableWidgetItem* item_game = new QTableWidgetItem(QString::fromStdString(offset.second.game)); - item_game->setFlags(item_game->flags() & ~Qt::ItemIsEditable); - tbl_cheats->setItem(row, cheat_table_columns::title, item_game); - - tbl_cheats->setItem(row, cheat_table_columns::description, new QTableWidgetItem(QString::fromStdString(offset.second.description))); - - std::string type_formatted; - fmt::append(type_formatted, "%s", offset.second.type); - QTableWidgetItem* item_type = new QTableWidgetItem(QString::fromStdString(type_formatted)); - item_type->setFlags(item_type->flags() & ~Qt::ItemIsEditable); - tbl_cheats->setItem(row, cheat_table_columns::type, item_type); - - QTableWidgetItem* item_offset = new QTableWidgetItem(QString("0x%0").arg(offset.second.offset, 1, 16).toUpper()); - item_offset->setData(Qt::UserRole, QVariant(offset.second.offset)); - item_offset->setFlags(item_offset->flags() & ~Qt::ItemIsEditable); - tbl_cheats->setItem(row, cheat_table_columns::offset, item_offset); - - tbl_cheats->setItem(row, cheat_table_columns::script, new QTableWidgetItem(QString::fromStdString(offset.second.red_script))); - - row++; - } - } - } - - g_cheat.save(); -} - -QString cheat_manager_dialog::get_localized_cheat_type(cheat_type type) -{ - switch (type) - { - case cheat_type::unsigned_8_cheat: return tr("Unsigned 8 bits"); - case cheat_type::unsigned_16_cheat: return tr("Unsigned 16 bits"); - case cheat_type::unsigned_32_cheat: return tr("Unsigned 32 bits"); - case cheat_type::unsigned_64_cheat: return tr("Unsigned 64 bits"); - case cheat_type::signed_8_cheat: return tr("Signed 8 bits"); - case cheat_type::signed_16_cheat: return tr("Signed 16 bits"); - case cheat_type::signed_32_cheat: return tr("Signed 32 bits"); - case cheat_type::signed_64_cheat: return tr("Signed 64 bits"); - case cheat_type::float_32_cheat: return tr("Float 32 bits"); - case cheat_type::max: break; - } - std::string type_formatted; - fmt::append(type_formatted, "%s", type); - return QString::fromStdString(type_formatted); -} diff --git a/rpcs3qt-legacy/cheat_manager.h b/rpcs3qt-legacy/cheat_manager.h deleted file mode 100644 index a6447787c..000000000 --- a/rpcs3qt-legacy/cheat_manager.h +++ /dev/null @@ -1,96 +0,0 @@ -#pragma once - -#include "util/types.hpp" - -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include "util/cheat_info.h" - -class cheat_engine -{ -public: - cheat_engine(); - - bool exist(const std::string& game, const u32 offset) const; - void add(const std::string& game, const std::string& description, const cheat_type type, const u32 offset, const std::string& red_script); - cheat_info* get(const std::string& game, const u32 offset); - bool erase(const std::string& game, const u32 offset); - - void import_cheats_from_str(const std::string& str_cheats); - std::string export_cheats_to_str() const; - 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); - - template - static std::vector search(const T value, const std::vector& to_filter); - - template - static T get_value(const u32 offset, bool& success); - template - static bool set_value(const u32 offset, const T value); - - static bool is_addr_safe(const u32 offset); - static u32 reverse_lookup(const u32 addr, const u32 max_offset, const u32 max_depth, const u32 cur_depth = 0); - - std::map> cheats; - -private: - const std::string m_cheats_filename = "cheats.yml"; -}; - -class cheat_manager_dialog : public QDialog -{ - Q_OBJECT -public: - cheat_manager_dialog(QWidget* parent = nullptr); - ~cheat_manager_dialog(); - static cheat_manager_dialog* get_dlg(QWidget* parent = nullptr); - - cheat_manager_dialog(cheat_manager_dialog const&) = delete; - void operator=(cheat_manager_dialog const&) = delete; - -protected: - void update_cheat_list(); - void do_the_search(); - - template - T convert_from_QString(const QString& str, bool& success); - - template - bool convert_and_search(); - template - std::pair convert_and_set(u32 offset); - -protected: - QTableWidget* tbl_cheats = nullptr; - QListWidget* lst_search = nullptr; - - QLineEdit* edt_value_final = nullptr; - QPushButton* btn_apply = nullptr; - - QLineEdit* edt_cheat_search_value = nullptr; - QComboBox* cbx_cheat_search_type = nullptr; - - QPushButton* btn_filter_results = nullptr; - - u32 current_offset{}; - std::vector offsets_found; - - cheat_engine g_cheat; - -private: - static cheat_manager_dialog* inst; - - QString get_localized_cheat_type(cheat_type type); -}; diff --git a/rpcs3qt-legacy/config_adapter.cpp b/rpcs3qt-legacy/config_adapter.cpp deleted file mode 100644 index 516c2fc3e..000000000 --- a/rpcs3qt-legacy/config_adapter.cpp +++ /dev/null @@ -1,71 +0,0 @@ -#include "config_adapter.h" -#include "Emu/system_config.h" - -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) - { - if (root.get_type() == cfg::type::node) - { - for (const auto& node : static_cast(root).get_nodes()) - { - if (node->get_name() == name) - { - return *node; - } - } - } - - fmt::throw_exception("Node not found: %s", name); - } - - static cfg::_base& get_cfg(cfg::_base& root, const cfg_location::const_iterator begin, const cfg_location::const_iterator end) - { - return begin == end ? root : get_cfg(get_cfg(root, *begin), begin + 1, end); - } - - YAML::Node get_node(const YAML::Node& node, const cfg_location::const_iterator begin, const cfg_location::const_iterator end) - { - if (begin == end) - { - return node; - } - - if (!node || !node.IsMap()) - { - cfg_log.fatal("Node error. A cfg_location does not match its cfg::node (location: %s)", get_yaml_node_location(node)); - return YAML::Node(); - } - - return get_node(node[*begin], begin + 1, end); // TODO - } - - YAML::Node get_node(const YAML::Node& node, const cfg_location& location) - { - return get_node(node, location.cbegin(), location.cend()); - } - - std::vector get_options(const cfg_location& location) - { - return cfg_adapter::get_cfg(g_cfg, location.cbegin(), location.cend()).to_list(); - } - - static bool get_is_dynamic(const cfg_location& location) - { - return cfg_adapter::get_cfg(g_cfg, location.cbegin(), location.cend()).get_is_dynamic(); - } - - bool get_is_dynamic(emu_settings_type type) - { - return get_is_dynamic(::at32(settings_location, type)); - } - - std::string get_setting_name(emu_settings_type type) - { - const cfg_location& loc = ::at32(settings_location, type); - return ::at32(loc, loc.size() - 1); - } -} // namespace cfg_adapter diff --git a/rpcs3qt-legacy/config_adapter.h b/rpcs3qt-legacy/config_adapter.h deleted file mode 100644 index f34e1e661..000000000 --- a/rpcs3qt-legacy/config_adapter.h +++ /dev/null @@ -1,22 +0,0 @@ -#pragma once - -#include "emu_settings_type.h" -#include "util/yaml.hpp" - -// Helper methods to interact with YAML and the config settings. -namespace cfg_adapter -{ - YAML::Node get_node(const YAML::Node& node, const cfg_location::const_iterator begin, const cfg_location::const_iterator end); - - /** Syntactic sugar to get a setting at a given config location. */ - YAML::Node get_node(const YAML::Node& node, const cfg_location& location); - - /** Returns possible options for values for some particular setting.*/ - std::vector get_options(const cfg_location& location); - - /** Returns dynamic property for some particular setting.*/ - bool get_is_dynamic(emu_settings_type type); - - /** Returns the string for a given setting.*/ - std::string get_setting_name(emu_settings_type type); -} // namespace cfg_adapter diff --git a/rpcs3qt-legacy/config_checker.cpp b/rpcs3qt-legacy/config_checker.cpp deleted file mode 100644 index accab6366..000000000 --- a/rpcs3qt-legacy/config_checker.cpp +++ /dev/null @@ -1,214 +0,0 @@ -#include "stdafx.h" -#include "config_checker.h" -#include "Emu/system_config.h" - -#include -#include -#include -#include -#include -#include - -LOG_CHANNEL(gui_log, "GUI"); - -config_checker::config_checker(QWidget* parent, const QString& content, bool is_log) : QDialog(parent) -{ - setObjectName("config_checker"); - setAttribute(Qt::WA_DeleteOnClose); - - QVBoxLayout* layout = new QVBoxLayout(); - QLabel* label = new QLabel(this); - layout->addWidget(label); - - QString result; - - if (check_config(content, result, is_log)) - { - setWindowTitle(tr("Interesting!")); - - if (result.isEmpty()) - { - label->setText(tr("Found config.\nIt seems to match the default config.")); - } - else - { - label->setText(tr("Found config.\nSome settings seem to deviate from the default config:")); - - QTextEdit* text_box = new QTextEdit(); - text_box->setReadOnly(true); - text_box->setHtml(result); - layout->addWidget(text_box); - - resize(400, 600); - } - } - else - { - setWindowTitle(tr("Ooops!")); - label->setText(result); - } - - QDialogButtonBox* box = new QDialogButtonBox(QDialogButtonBox::Close); - connect(box, &QDialogButtonBox::rejected, this, &QDialog::reject); - layout->addWidget(box); - - setLayout(layout); -} - -bool config_checker::check_config(QString content, QString& result, bool is_log) -{ - cfg_root config{}; - - if (is_log) - { - const QString start_token = "SYS: Used configuration:\n"; - const QString end_token = "\n·"; - - qsizetype start = content.indexOf(start_token); - qsizetype end = -1; - - if (start >= 0) - { - start += start_token.size(); - end = content.indexOf(end_token, start); - } - - if (end < 0) - { - result = tr("Cannot find any config!"); - return false; - } - - content = content.mid(start, end - start); - } - - if (!config.from_string(content.toStdString())) - { - gui_log.error("log_viewer: Failed to parse config:\n%s", content); - result = tr("Cannot find any config!"); - return false; - } - - std::function print_diff_recursive; - print_diff_recursive = [&print_diff_recursive](const cfg::_base* base, std::string& diff, int indentation) -> void - { - if (!base) - { - return; - } - - const auto indent = [](std::string& str, int indentation) - { - for (int i = 0; i < indentation * 2; i++) - { - str += " "; - } - }; - - switch (base->get_type()) - { - case cfg::type::node: - { - if (const auto& node = static_cast(base)) - { - std::string diff_tmp; - - for (const auto& n : node->get_nodes()) - { - print_diff_recursive(n, diff_tmp, indentation + 1); - } - - if (!diff_tmp.empty()) - { - indent(diff, indentation); - - if (!base->get_name().empty()) - { - fmt::append(diff, "%s:
", base->get_name()); - } - - fmt::append(diff, "%s", diff_tmp); - } - } - break; - } - case cfg::type::_bool: - case cfg::type::_enum: - case cfg::type::_int: - case cfg::type::uint: - case cfg::type::string: - { - const std::string val = base->to_string(); - const std::string def = base->def_to_string(); - - if (val != def) - { - indent(diff, indentation); - - if (def.empty()) - { - fmt::append(diff, "%s: %s
", base->get_name(), val); - } - else - { - fmt::append(diff, "%s: %s default: %s
", base->get_name(), val, def); - } - } - break; - } - case cfg::type::set: - { - if (const auto& node = static_cast(base)) - { - const std::vector set_entries = node->to_list(); - - if (!set_entries.empty()) - { - indent(diff, indentation); - fmt::append(diff, "%s:
", base->get_name()); - - for (const std::string& entry : set_entries) - { - indent(diff, indentation + 1); - fmt::append(diff, "- %s
", entry); - } - } - } - break; - } - case cfg::type::log: - { - if (const auto& node = static_cast(base)) - { - const auto& log_entries = node->get_map(); - - if (!log_entries.empty()) - { - indent(diff, indentation); - fmt::append(diff, "%s:
", base->get_name()); - - for (const auto& entry : log_entries) - { - indent(diff, indentation + 1); - fmt::append(diff, "%s: %s
", entry.first, entry.second); - } - } - } - break; - } - case cfg::type::map: - case cfg::type::node_map: - case cfg::type::device: - { - // Ignored - break; - } - } - }; - - std::string diff; - print_diff_recursive(&config, diff, 0); - result = QString::fromStdString(diff); - - return true; -} diff --git a/rpcs3qt-legacy/config_checker.h b/rpcs3qt-legacy/config_checker.h deleted file mode 100644 index 900fb3035..000000000 --- a/rpcs3qt-legacy/config_checker.h +++ /dev/null @@ -1,13 +0,0 @@ -#pragma once - -#include - -class config_checker : public QDialog -{ - Q_OBJECT - -public: - config_checker(QWidget* parent, const QString& path, bool is_log); - - bool check_config(QString content, QString& result, bool is_log); -}; diff --git a/rpcs3qt-legacy/curl_handle.cpp b/rpcs3qt-legacy/curl_handle.cpp deleted file mode 100644 index dbb393915..000000000 --- a/rpcs3qt-legacy/curl_handle.cpp +++ /dev/null @@ -1,85 +0,0 @@ -#include "stdafx.h" -#include "curl_handle.h" -#include "util/logs.hpp" - -#ifdef _WIN32 -#include "util/StrUtil.h" -#endif - -LOG_CHANNEL(network_log, "NET"); - -namespace rpcs3::curl -{ - - curl_handle::curl_handle() - { - reset_error_buffer(); - - m_curl = curl_easy_init(); - - 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)); - - m_uses_error_buffer = err == CURLE_OK; - - err = curl_easy_setopt(m_curl, CURLOPT_VERBOSE, g_curl_verbose); - if (err != CURLE_OK) - network_log.error("curl_easy_setopt(CURLOPT_VERBOSE, %d): %s", g_curl_verbose, curl_easy_strerror(err)); - -#ifdef _WIN32 - // Tell curl to use the native CA store for certificate verification - 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 - } - - curl_handle::~curl_handle() - { - curl_easy_cleanup(m_curl); - } - - CURL* curl_handle::get_curl() const - { - return m_curl; - } - - void curl_handle::reset_error_buffer() - { - ensure(m_error_buffer.size() == CURL_ERROR_SIZE); - m_error_buffer[0] = 0; - } - - 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()); - } - } - - return fmt::format("Curl error (%d): %s", static_cast(code), curl_easy_strerror(code)); - } - -} // namespace rpcs3::curl - -#ifdef _WIN32 -// Functions exported from our user_settings.h in WolfSSL, implemented in RPCS3 -extern "C" -{ - - FILE* wolfSSL_fopen_utf8(const char* name, const char* mode) - { - return _wfopen(utf8_to_wchar(name).c_str(), utf8_to_wchar(mode).c_str()); - } - - int wolfSSL_stat_utf8(const char* path, struct _stat* buffer) - { - return _wstat(utf8_to_wchar(path).c_str(), buffer); - } -} -#endif diff --git a/rpcs3qt-legacy/curl_handle.h b/rpcs3qt-legacy/curl_handle.h deleted file mode 100644 index c4abbf146..000000000 --- a/rpcs3qt-legacy/curl_handle.h +++ /dev/null @@ -1,36 +0,0 @@ -#pragma once - -#include - -#ifndef CURL_STATICLIB -#define CURL_STATICLIB -#endif -#include - -namespace rpcs3::curl -{ - inline bool g_curl_verbose = false; - - class curl_handle - { - public: - explicit curl_handle(); - ~curl_handle(); - - CURL* get_curl() const; - - operator CURL*() const - { - return get_curl(); - } - - void reset_error_buffer(); - std::string get_verbose_error(CURLcode code) const; - - private: - CURL* m_curl = nullptr; - bool m_uses_error_buffer = false; - std::array m_error_buffer; - }; - -} // namespace rpcs3::curl diff --git a/rpcs3qt-legacy/custom_dialog.cpp b/rpcs3qt-legacy/custom_dialog.cpp deleted file mode 100644 index add3c328a..000000000 --- a/rpcs3qt-legacy/custom_dialog.cpp +++ /dev/null @@ -1,36 +0,0 @@ -#include "custom_dialog.h" - -custom_dialog::custom_dialog(bool disableCancel, QWidget* parent) - : QDialog(parent), m_disable_cancel(disableCancel) -{ - if (m_disable_cancel) - { - setWindowFlags(windowFlags() & ~Qt::WindowCloseButtonHint); - } -} - -void custom_dialog::keyPressEvent(QKeyEvent* event) -{ - // this won't work with Alt+F4, the window still closes - if (m_disable_cancel && event->key() == Qt::Key_Escape) - { - event->ignore(); - } - else - { - QDialog::keyPressEvent(event); - } -} - -void custom_dialog::closeEvent(QCloseEvent* event) -{ - // spontaneous: don't close on external system level events like Alt+F4 - if (m_disable_cancel && event->spontaneous()) - { - event->ignore(); - } - else - { - QDialog::closeEvent(event); - } -} diff --git a/rpcs3qt-legacy/custom_dialog.h b/rpcs3qt-legacy/custom_dialog.h deleted file mode 100644 index 48531384d..000000000 --- a/rpcs3qt-legacy/custom_dialog.h +++ /dev/null @@ -1,17 +0,0 @@ -#pragma once - -#include -#include - -class custom_dialog : public QDialog -{ - Q_OBJECT - -public: - explicit custom_dialog(bool disableCancel, QWidget* parent = nullptr); - bool m_disable_cancel; - -private: - void keyPressEvent(QKeyEvent* event) override; - void closeEvent(QCloseEvent* event) override; -}; diff --git a/rpcs3qt-legacy/custom_dock_widget.h b/rpcs3qt-legacy/custom_dock_widget.h deleted file mode 100644 index 4899e63d6..000000000 --- a/rpcs3qt-legacy/custom_dock_widget.h +++ /dev/null @@ -1,67 +0,0 @@ -#pragma once - -#include -#include -#include - -class custom_dock_widget : public QDockWidget -{ -private: - std::shared_ptr m_title_bar_widget; - bool m_is_title_bar_visible = true; - -public: - explicit custom_dock_widget(const QString& title, QWidget* parent = Q_NULLPTR, Qt::WindowFlags flags = Qt::WindowFlags()) - : QDockWidget(title, parent, flags) - { - m_title_bar_widget.reset(titleBarWidget()); - - connect(this, &QDockWidget::topLevelChanged, [this](bool /* topLevel*/) - { - SetTitleBarVisible(m_is_title_bar_visible); - style()->unpolish(this); - style()->polish(this); - }); - } - - void SetTitleBarVisible(bool visible) - { - if (visible || isFloating()) - { - if (m_title_bar_widget.get() != titleBarWidget()) - { - setTitleBarWidget(m_title_bar_widget.get()); - QMargins margins = widget()->contentsMargins(); - margins.setTop(0); - widget()->setContentsMargins(margins); - } - } - else - { - setTitleBarWidget(new QWidget()); - QMargins margins = widget()->contentsMargins(); - margins.setTop(1); - widget()->setContentsMargins(margins); - } - - m_is_title_bar_visible = visible; - } - -protected: - void paintEvent(QPaintEvent* event) override - { - // We need to repaint the dock widgets as plain widgets in floating mode. - // Source: https://stackoverflow.com/questions/10272091/cannot-add-a-background-image-to-a-qdockwidget - if (isFloating()) - { - QStyleOption opt; - opt.initFrom(this); - QPainter p(this); - style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this); - return; - } - - // Use inherited method for docked mode because otherwise the dock would lose the title etc. - QDockWidget::paintEvent(event); - } -}; diff --git a/rpcs3qt-legacy/custom_table_widget_item.cpp b/rpcs3qt-legacy/custom_table_widget_item.cpp deleted file mode 100644 index 4c17da7a4..000000000 --- a/rpcs3qt-legacy/custom_table_widget_item.cpp +++ /dev/null @@ -1,72 +0,0 @@ -#include "custom_table_widget_item.h" -#include "util/StrFmt.h" - -#include - -custom_table_widget_item::custom_table_widget_item(const std::string& text, int sort_role, const QVariant& sort_value) - : movie_item(QString::fromStdString(text).simplified()) // simplified() forces single line text -{ - if (sort_role != Qt::DisplayRole) - { - setData(sort_role, sort_value, true); - } -} - -custom_table_widget_item::custom_table_widget_item(const QString& text, int sort_role, const QVariant& sort_value) - : movie_item(text.simplified()) // simplified() forces single line text -{ - if (sort_role != Qt::DisplayRole) - { - setData(sort_role, sort_value, true); - } -} - -bool custom_table_widget_item::operator<(const QTableWidgetItem& other) const -{ - if (m_sort_role == Qt::DisplayRole) - { - return QTableWidgetItem::operator<(other); - } - - const QVariant data_l = data(m_sort_role); - const QVariant data_r = other.data(m_sort_role); - const int type_l = data_l.metaType().id(); - const int type_r = data_r.metaType().id(); - - ensure(type_l == type_r); - - switch (type_l) - { - case QMetaType::Type::Bool: - case QMetaType::Type::Int: - return data_l.toInt() < data_r.toInt(); - case QMetaType::Type::UInt: - return data_l.toUInt() < data_r.toUInt(); - case QMetaType::Type::LongLong: - return data_l.toLongLong() < data_r.toLongLong(); - case QMetaType::Type::ULongLong: - return data_l.toULongLong() < data_r.toULongLong(); - case QMetaType::Type::Double: - return data_l.toDouble() < data_r.toDouble(); - case QMetaType::Type::QDate: - return data_l.toDate() < data_r.toDate(); - case QMetaType::Type::QTime: - return data_l.toTime() < data_r.toTime(); - case QMetaType::Type::QDateTime: - return data_l.toDateTime() < data_r.toDateTime(); - case QMetaType::Type::Char: - case QMetaType::Type::QString: - return data_l.toString() < data_r.toString(); - default: - fmt::throw_exception("Unimplemented type %s", QMetaType(type_l).name()); - } -} - -void custom_table_widget_item::setData(int role, const QVariant& value, bool assign_sort_role) -{ - if (assign_sort_role) - { - m_sort_role = role; - } - QTableWidgetItem::setData(role, value); -} diff --git a/rpcs3qt-legacy/custom_table_widget_item.h b/rpcs3qt-legacy/custom_table_widget_item.h deleted file mode 100644 index 7e87e35f5..000000000 --- a/rpcs3qt-legacy/custom_table_widget_item.h +++ /dev/null @@ -1,20 +0,0 @@ -#pragma once - -#include "movie_item.h" - -class custom_table_widget_item : public movie_item -{ -private: - int m_sort_role = Qt::DisplayRole; - -public: - using QTableWidgetItem::setData; - - custom_table_widget_item() = default; - custom_table_widget_item(const std::string& text, int sort_role = Qt::DisplayRole, const QVariant& sort_value = 0); - custom_table_widget_item(const QString& text, int sort_role = Qt::DisplayRole, const QVariant& sort_value = 0); - - bool operator<(const QTableWidgetItem& other) const override; - - void setData(int role, const QVariant& value, bool assign_sort_role); -}; diff --git a/rpcs3qt-legacy/debugger_add_bp_window.cpp b/rpcs3qt-legacy/debugger_add_bp_window.cpp deleted file mode 100644 index 710757fac..000000000 --- a/rpcs3qt-legacy/debugger_add_bp_window.cpp +++ /dev/null @@ -1,116 +0,0 @@ -#include "debugger_add_bp_window.h" -#include "util/StrFmt.h" -#include "util/StrUtil.h" -#include "breakpoint_handler.h" -#include "util/types.hpp" - -#include -#include -#include -#include -#include -#include -#include - -debugger_add_bp_window::debugger_add_bp_window(breakpoint_list* bp_list, QWidget* parent) - : QDialog(parent) -{ - ensure(bp_list); - - setWindowTitle(tr("Add a breakpoint")); - setModal(true); - - QVBoxLayout* vbox_panel = new QVBoxLayout(); - - QHBoxLayout* hbox_top = new QHBoxLayout(); - QLabel* l_address = new QLabel(tr("Address")); - QLineEdit* t_address = new QLineEdit(); - t_address->setPlaceholderText(tr("Address here")); - t_address->setFocus(); - - hbox_top->addWidget(l_address); - hbox_top->addWidget(t_address); - vbox_panel->addLayout(hbox_top); - - QHBoxLayout* hbox_bot = new QHBoxLayout(); - QComboBox* co_bptype = new QComboBox(this); - QStringList qstr_breakpoint_types; - - qstr_breakpoint_types -#ifdef RPCS3_HAS_MEMORY_BREAKPOINTS - << tr("Memory Read") - << tr("Memory Write") - << tr("Memory Read&Write") -#endif - << tr("Execution"); - - co_bptype->addItems(qstr_breakpoint_types); - - hbox_bot->addWidget(co_bptype); - vbox_panel->addLayout(hbox_bot); - - QHBoxLayout* hbox_buttons = new QHBoxLayout(); - QDialogButtonBox* button_box = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); - button_box->button(QDialogButtonBox::Ok)->setText(tr("Add")); - - hbox_buttons->addWidget(button_box); - vbox_panel->addLayout(hbox_buttons); - - setLayout(vbox_panel); - - connect(button_box, &QDialogButtonBox::accepted, this, [=, this] - { - const std::string str_address = t_address->text().toStdString(); - - if (str_address.empty()) - { - QMessageBox::warning(this, tr("Add BP error"), tr("Address is empty!")); - return; - } - - // We always want hex - const std::string parsed_string = (!str_address.starts_with("0x") && !str_address.starts_with("0X")) ? fmt::format("0x%s", str_address) : str_address; - u64 parsed_address = 0; - - // We don't accept 0 - if (!try_to_uint64(&parsed_address, parsed_string, 1, 0xFF'FF'FF'FF)) - { - QMessageBox::warning(this, tr("Add BP error"), tr("Address is invalid!")); - return; - } - - const u32 address = static_cast(parsed_address); - bs_t bp_t{}; - -#ifdef RPCS3_HAS_MEMORY_BREAKPOINTS - switch (co_bptype->currentIndex()) - { - case 0: - bp_t = breakpoint_types::bp_read; - break; - case 1: - bp_t = breakpoint_types::bp_write; - break; - case 2: - bp_t = breakpoint_types::bp_read + breakpoint_types::bp_write; - break; - case 3: - bp_t = breakpoint_types::bp_exec; - break; - default: - break; - } -#else - bp_t = breakpoint_types::bp_exec; -#endif - - if (bp_t) - bp_list->AddBreakpoint(address, bp_t); - - QDialog::accept(); - }); - - connect(button_box, &QDialogButtonBox::rejected, this, &QDialog::reject); - - move(QCursor::pos()); -} diff --git a/rpcs3qt-legacy/debugger_add_bp_window.h b/rpcs3qt-legacy/debugger_add_bp_window.h deleted file mode 100644 index c57c6873d..000000000 --- a/rpcs3qt-legacy/debugger_add_bp_window.h +++ /dev/null @@ -1,13 +0,0 @@ -#pragma once - -#include "breakpoint_list.h" - -#include - -class debugger_add_bp_window : public QDialog -{ - Q_OBJECT - -public: - explicit debugger_add_bp_window(breakpoint_list* bp_list, QWidget* parent = nullptr); -}; diff --git a/rpcs3qt-legacy/debugger_frame.cpp b/rpcs3qt-legacy/debugger_frame.cpp deleted file mode 100644 index 4305a6e75..000000000 --- a/rpcs3qt-legacy/debugger_frame.cpp +++ /dev/null @@ -1,1740 +0,0 @@ -#include "debugger_frame.h" -#include "register_editor_dialog.h" -#include "instruction_editor_dialog.h" -#include "memory_viewer_panel.h" -#include "elf_memory_dumping_dialog.h" -#include "gui_settings.h" -#include "debugger_list.h" -#include "breakpoint_list.h" -#include "breakpoint_handler.h" -#include "call_stack_list.h" -#include "input_dialog.h" -#include "qt_utils.h" - -#include "Emu/System.h" -#include "Emu/IdManager.h" -#include "Emu/RSX/RSXThread.h" -#include "Emu/RSX/RSXDisAsm.h" -#include "Emu/Cell/PPUAnalyser.h" -#include "Emu/Cell/PPUDisAsm.h" -#include "Emu/Cell/PPUThread.h" -#include "Emu/Cell/SPUDisAsm.h" -#include "Emu/Cell/SPUThread.h" -#include "Emu/CPU/CPUThread.h" -#include "Emu/CPU/CPUDisAsm.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "debugger_add_bp_window.h" -#include "util/asm.hpp" - -constexpr auto qstr = QString::fromStdString; - -constexpr auto s_pause_flags = cpu_flag::dbg_pause + cpu_flag::dbg_global_pause; - -extern atomic_t g_debugger_pause_all_threads_on_bp; - -extern const ppu_decoder g_ppu_itype; -#ifdef RPCS3_HAS_MEMORY_BREAKPOINTS -breakpoint_handler g_breakpoint_handler = breakpoint_handler(); -#endif - -extern bool is_using_interpreter(thread_class t_class); - -extern std::shared_ptr make_disasm(const cpu_thread* cpu, shared_ptr handle); - -debugger_frame::debugger_frame(std::shared_ptr gui_settings, QWidget* parent) - : custom_dock_widget(tr("Debugger [Press F1 for Help]"), parent), m_gui_settings(std::move(gui_settings)) -{ - setContentsMargins(0, 0, 0, 0); - - m_update = new QTimer(this); - connect(m_update, &QTimer::timeout, this, &debugger_frame::UpdateUI); - - m_mono = QFontDatabase::systemFont(QFontDatabase::FixedFont); - m_mono.setPointSize(9); - - QVBoxLayout* vbox_p_main = new QVBoxLayout(); - vbox_p_main->setContentsMargins(5, 5, 5, 5); - - QHBoxLayout* hbox_b_main = new QHBoxLayout(); - hbox_b_main->setContentsMargins(0, 0, 0, 0); - -#ifdef RPCS3_HAS_MEMORY_BREAKPOINTS - m_ppu_breakpoint_handler = &g_breakpoint_handler; -#else - m_ppu_breakpoint_handler = new breakpoint_handler(); -#endif - - m_breakpoint_list = new breakpoint_list(this, m_ppu_breakpoint_handler); - - m_debugger_list = new debugger_list(this, m_gui_settings, m_ppu_breakpoint_handler); - m_debugger_list->installEventFilter(this); - - m_call_stack_list = new call_stack_list(this); - - m_choice_units = new QComboBox(this); - m_choice_units->setSizeAdjustPolicy(QComboBox::AdjustToContents); - m_choice_units->setMaxVisibleItems(30); - m_choice_units->setMaximumWidth(500); - m_choice_units->setEditable(true); - m_choice_units->setInsertPolicy(QComboBox::NoInsert); - m_choice_units->lineEdit()->setPlaceholderText(tr("Choose a thread")); - m_choice_units->completer()->setCompletionMode(QCompleter::PopupCompletion); - m_choice_units->completer()->setMaxVisibleItems(30); - m_choice_units->completer()->setFilterMode(Qt::MatchContains); - m_choice_units->installEventFilter(this); - - m_go_to_addr = new QPushButton(tr("Go To Address"), this); - m_go_to_pc = new QPushButton(tr("Go To PC"), this); - m_btn_step = new QPushButton(tr("Step"), this); - m_btn_step_over = new QPushButton(tr("Step Over"), this); - m_btn_add_bp = new QPushButton(tr("Add BP"), this); - m_btn_run = new QPushButton(RunString, this); - - EnableButtons(false); - - ChangeColors(); - - hbox_b_main->addWidget(m_go_to_addr); - hbox_b_main->addWidget(m_go_to_pc); - hbox_b_main->addWidget(m_btn_step); - hbox_b_main->addWidget(m_btn_step_over); - hbox_b_main->addWidget(m_btn_add_bp); - hbox_b_main->addWidget(m_btn_run); - hbox_b_main->addWidget(m_choice_units); - hbox_b_main->addStretch(); - - // Misc state - m_misc_state = new QPlainTextEdit(this); - m_misc_state->setLineWrapMode(QPlainTextEdit::NoWrap); - m_misc_state->setTextInteractionFlags(Qt::TextSelectableByMouse | Qt::TextSelectableByKeyboard); - - // Registers - m_regs = new QPlainTextEdit(this); - m_regs->setLineWrapMode(QPlainTextEdit::NoWrap); - m_regs->setTextInteractionFlags(Qt::TextSelectableByMouse | Qt::TextSelectableByKeyboard); - - m_debugger_list->setFont(m_mono); - m_misc_state->setFont(m_mono); - m_regs->setFont(m_mono); - m_call_stack_list->setFont(m_mono); - - m_right_splitter = new QSplitter(this); - m_right_splitter->setOrientation(Qt::Vertical); - m_right_splitter->addWidget(m_misc_state); - m_right_splitter->addWidget(m_regs); - m_right_splitter->addWidget(m_call_stack_list); - m_right_splitter->addWidget(m_breakpoint_list); - - // Set relative sizes for widgets - m_right_splitter->setStretchFactor(0, 2); // misc state - m_right_splitter->setStretchFactor(1, 8); // registers - m_right_splitter->setStretchFactor(2, 3); // call stack - m_right_splitter->setStretchFactor(3, 1); // breakpoint list - - m_splitter = new QSplitter(this); - m_splitter->addWidget(m_debugger_list); - m_splitter->addWidget(m_right_splitter); - - QHBoxLayout* hbox_w_list = new QHBoxLayout(); - hbox_w_list->addWidget(m_splitter); - - vbox_p_main->addLayout(hbox_b_main); - vbox_p_main->addLayout(hbox_w_list); - - QWidget* body = new QWidget(this); - body->setLayout(vbox_p_main); - setWidget(body); - - connect(m_go_to_addr, &QAbstractButton::clicked, this, &debugger_frame::ShowGotoAddressDialog); - connect(m_go_to_pc, &QAbstractButton::clicked, this, [this]() - { - ShowPC(true); - }); - - connect(m_btn_step, &QAbstractButton::clicked, this, &debugger_frame::DoStep); - connect(m_btn_step_over, &QAbstractButton::clicked, [this]() - { - DoStep(true); - }); - - connect(m_btn_add_bp, &QAbstractButton::clicked, this, [this] - { - debugger_add_bp_window dlg(m_breakpoint_list, this); - dlg.exec(); - }); - - connect(m_btn_run, &QAbstractButton::clicked, this, &debugger_frame::RunBtnPress); - - connect(m_choice_units->lineEdit(), &QLineEdit::editingFinished, [&] - { - m_choice_units->clearFocus(); - }); - - connect(m_choice_units, QOverload::of(&QComboBox::currentIndexChanged), this, [&]() - { - m_is_spu_disasm_mode = false; - OnSelectUnit(); - }); - connect(this, &QDockWidget::visibilityChanged, this, &debugger_frame::EnableUpdateTimer); - - connect(m_debugger_list, &debugger_list::BreakpointRequested, m_breakpoint_list, &breakpoint_list::HandleBreakpointRequest); - connect(m_breakpoint_list, &breakpoint_list::RequestShowAddress, m_debugger_list, &debugger_list::ShowAddress); - - connect(this, &debugger_frame::CallStackUpdateRequested, m_call_stack_list, &call_stack_list::HandleUpdate); - connect(m_call_stack_list, &call_stack_list::RequestShowAddress, m_debugger_list, &debugger_list::ShowAddress); - - m_debugger_list->RefreshView(); - - m_choice_units->clear(); - m_choice_units->addItem(NoThreadString); -} - -void debugger_frame::SaveSettings() const -{ - m_gui_settings->SetValue(gui::d_splitterState, m_splitter->saveState()); -} - -void debugger_frame::ChangeColors() const -{ - if (m_debugger_list) - { - const QColor color = gui::utils::get_foreground_color(); - - m_debugger_list->m_color_bp = m_breakpoint_list->m_color_bp = gui::utils::get_label_color("debugger_frame_breakpoint", Qt::yellow, Qt::darkYellow, QPalette::Window); - m_debugger_list->m_color_pc = gui::utils::get_label_color("debugger_frame_pc", Qt::green, Qt::darkGreen, QPalette::Window); - m_debugger_list->m_text_color_bp = m_breakpoint_list->m_text_color_bp = gui::utils::get_label_color("debugger_frame_breakpoint", color, color); - m_debugger_list->m_text_color_pc = gui::utils::get_label_color("debugger_frame_pc", color, color); - } -} - -bool debugger_frame::eventFilter(QObject* object, QEvent* event) -{ - // There's no overlap between keys so returning true wouldn't matter. - if (object == m_debugger_list && event->type() == QEvent::KeyPress) - { - keyPressEvent(static_cast(event)); - event->accept(); // Restore accepted state - return false; - } - - if (object == m_choice_units && event->type() == QEvent::FocusOut) - { - if (int index = m_choice_units->currentIndex(); index >= 0) - { - // Restore item text automatically on focus-out after search - m_choice_units->setCurrentText(m_choice_units->itemText(index)); - } - } - - return false; -} - -void debugger_frame::closeEvent(QCloseEvent* event) -{ - SaveSettings(); - - QDockWidget::closeEvent(event); - Q_EMIT DebugFrameClosed(); -} - -void debugger_frame::showEvent(QShowEvent* event) -{ - // resize splitter widgets - if (!m_splitter->restoreState(m_gui_settings->GetValue(gui::d_splitterState).toByteArray())) - { - const int width_right = width() / 3; - const int width_left = width() - width_right; - m_splitter->setSizes({width_left, width_right}); - } - - QDockWidget::showEvent(event); -} - -void debugger_frame::hideEvent(QHideEvent* event) -{ - // save splitter state or it will resume its initial state on next show - m_gui_settings->SetValue(gui::d_splitterState, m_splitter->saveState()); - QDockWidget::hideEvent(event); -} - -void debugger_frame::open_breakpoints_settings() -{ - QDialog* dlg = new QDialog(this); - dlg->setWindowTitle(tr("Breakpoint Settings")); - dlg->setModal(true); - - QCheckBox* check_box = new QCheckBox(tr("Pause All Threads On Hit"), dlg); - check_box->setCheckable(true); - check_box->setChecked(g_debugger_pause_all_threads_on_bp.load()); - check_box->setToolTip(tr("When set: a breakpoint hit will pause the emulation instead of the current thread." - "\nApplies on all breakpoints in all threads regardless if set before or after changing this setting.")); - - connect(check_box, &QCheckBox::clicked, dlg, [](bool checked) - { - g_debugger_pause_all_threads_on_bp = checked; - }); - - QPushButton* button_ok = new QPushButton(tr("OK"), dlg); - connect(button_ok, &QAbstractButton::clicked, dlg, &QDialog::accept); - - QHBoxLayout* hbox_layout = new QHBoxLayout(dlg); - hbox_layout->addWidget(check_box); - hbox_layout->addWidget(button_ok); - dlg->setLayout(hbox_layout); - dlg->setAttribute(Qt::WA_DeleteOnClose); - dlg->open(); -} - -void debugger_frame::keyPressEvent(QKeyEvent* event) -{ - if (!isActiveWindow()) - { - event->ignore(); - return; - } - - const auto cpu = get_cpu(); - const int row = m_debugger_list->currentRow(); - - switch (event->key()) - { - case Qt::Key_F1: - { - if (event->isAutoRepeat()) - { - event->ignore(); - return; - } - - QDialog* dlg = new QDialog(this); - dlg->setAttribute(Qt::WA_DeleteOnClose); - dlg->setWindowTitle(tr("Debugger Guide & Shortcuts")); - - QLabel* l = new QLabel(tr( - "Keys Ctrl+G: Go to typed address." - "\nKeys Ctrl+B: Open breakpoints settings." - "\nKeys Ctrl+C: Copy instruction contents." - "\nKeys Ctrl+F: Find thread." - "\nKeys Alt+S: Capture SPU images of selected SPU or generalized form when used from PPU." - "\nKeys Alt+S: Launch a memory viewer pointed to the current RSX semaphores location when used from RSX." - "\nKeys Alt+R: Load last saved SPU state capture." - "\nKeys Alt+F5: Show the SPU disassmebler dialog." - "\nKey D: SPU MFC commands logger, MFC debug setting must be enabled." - "\nKey D: Also PPU calling history logger, interpreter and non-zero call history size must be used." - "\nKey E: Instruction Editor: click on the instruction you want to modify, then press E." - "\nKey F: Dedicated floating point mode switch for SPU threads." - "\nKey R: Registers Editor for selected thread." - "\nKey N: Show next instruction the thread will execute after marked instruction, does nothing if target is not predictable." - "\nKey M: Show the Memory Viewer with initial address pointing to the marked instruction." - "\nKey I: Show RSX method detail." - "\nKey F10: Perform step-over on instructions. (skip function calls)" - "\nKey F11: Perform single-stepping on instructions." - "\nKey F1: Show this help dialog." - "\nKey Up: Scroll one instruction upwards. (address is decremented)" - "\nKey Down: Scroll one instruction downwards. (address is incremented)" - "\nKey Page-Up: Scroll upwards with steps count equal to the viewed instruction count." - "\nKey Page-Down: Scroll downwards with steps count equal to the viewed instruction count." - "\nDouble-click: Set breakpoints.")); - - gui::utils::set_font_size(*l, 9); - - QVBoxLayout* layout = new QVBoxLayout(); - layout->addWidget(l); - dlg->setLayout(layout); - dlg->setFixedSize(dlg->sizeHint()); - dlg->move(QCursor::pos()); - dlg->open(); - return; - } - default: break; - } - - if (event->modifiers() == Qt::ControlModifier) - { - switch (const auto key = event->key()) - { - case Qt::Key_PageUp: - case Qt::Key_PageDown: - { - if (event->isAutoRepeat()) - { - event->ignore(); - break; - } - - const int count = m_choice_units->count(); - const int cur_index = m_choice_units->currentIndex(); - - if (count && cur_index >= 0) - { - // Wrap around - // Adding count so the result would not be negative, that would alter the remainder operation - m_choice_units->setCurrentIndex((cur_index + count + (key == Qt::Key_PageUp ? -1 : 1)) % count); - } - - return; - } - case Qt::Key_F: - { - m_choice_units->clearEditText(); - m_choice_units->setFocus(); - return; - } - default: break; - } - } - - if (!cpu) - { - event->ignore(); - return; - } - - const u32 address_limits = (cpu->get_class() == thread_class::spu ? 0x3fffc : ~3); - const u32 pc = (m_debugger_list->m_pc & address_limits); - const u32 selected = (m_debugger_list->m_showing_selected_instruction ? m_debugger_list->m_selected_instruction : cpu->get_pc()) & address_limits; - - const auto modifiers = event->modifiers(); - - if (modifiers == Qt::ControlModifier) - { - if (event->isAutoRepeat()) - { - event->ignore(); - return; - } - - switch (event->key()) - { - case Qt::Key_G: - { - ShowGotoAddressDialog(); - return; - } - case Qt::Key_B: - { - open_breakpoints_settings(); - return; - } - default: break; - } - } - else - { - switch (event->key()) - { - case Qt::Key_D: - { - if (event->isAutoRepeat()) - { - break; - } - - auto get_max_allowed = [&](QString title, QString description, u32 limit) -> u32 - { - input_dialog dlg(4, "", title, description.arg(limit), QString::number(limit), this); - - QFont mono = QFontDatabase::systemFont(QFontDatabase::FixedFont); - mono.setPointSize(8); - dlg.set_input_font(mono, false); - dlg.set_clear_button_enabled(false); - dlg.set_button_enabled(QDialogButtonBox::StandardButton::Ok, false); - dlg.set_validator(new QRegularExpressionValidator(QRegularExpression("^[1-9][0-9]*$"), &dlg)); - - u32 max = 0; - - connect(&dlg, &input_dialog::text_changed, [&](const QString& changed) - { - bool ok = false; - const u32 dummy = changed.toUInt(&ok, 10); - ok = ok && dummy && dummy <= limit; - dlg.set_button_enabled(QDialogButtonBox::StandardButton::Ok, ok); - - if (ok) - { - max = dummy; - } - }); - - if (dlg.exec() != QDialog::Accepted) - { - max = 0; - } - - return max; - }; - - auto copy_overlapping_list = [&](u64& index, u64 max, const std::vector& in, std::vector& out, bool& emptied) - { - max = std::min(max, in.size()); - - const u64 current_pos = index % in.size(); - const u64 last_elements = std::min(current_pos, max); - const u64 overlapped_old_elements = std::min(index, max) - last_elements; - - out.resize(overlapped_old_elements + last_elements); - - // Save list contents (only the relavant parts) - std::copy(in.end() - overlapped_old_elements, in.end(), out.begin()); - std::copy_n(in.begin() + current_pos - last_elements, last_elements, out.begin() + overlapped_old_elements); - - // Check if max elements to log is larger/equal to current list size - if ((emptied = index && max >= index)) - { - // Empty list when possible (further calls' history logging will not log any call before this) - index = 0; - } - }; - - if (cpu->get_class() == thread_class::spu && g_cfg.core.mfc_debug) - { - const u32 max = get_max_allowed(tr("Max MFC cmds logged"), tr("Decimal only, max allowed is %0."), spu_thread::max_mfc_dump_idx); - - // Preallocate in order to save execution time when inside suspend_all. - std::vector copy(max); - - bool emptied = false; - - cpu_thread::suspend_all(nullptr, {}, [&] - { - const auto spu = static_cast(cpu); - copy_overlapping_list(spu->mfc_dump_idx, max, spu->mfc_history, copy, emptied); - }); - - std::string ret; - - u32 i = 0; - for (auto it = copy.rbegin(); it != copy.rend(); it++, i++) - { - auto& dump = *it; - - const u32 pc = std::exchange(dump.cmd.eah, 0); - fmt::append(ret, "\n(%d) PC 0x%05x: [%s] (%s)", i, pc, dump.cmd, spu_block_hash{dump.block_hash}); - - if (dump.cmd.cmd == MFC_PUTLLC_CMD) - { - fmt::append(ret, " %s", dump.cmd.tag == MFC_PUTLLC_SUCCESS ? "(passed)" : "(failed)"); - } - - auto load = [&](usz index) - { - be_t data{}; - std::memcpy(&data, dump.data + index * sizeof(data), sizeof(data)); - return data; - }; - - for (usz i = 0; i < utils::aligned_div(std::min(dump.cmd.size, 128), 4); i += 4) - { - fmt::append(ret, "\n[0x%02x] %08x %08x %08x %08x", i * sizeof(be_t), load(i + 0), load(i + 1), load(i + 2), load(i + 3)); - } - } - - if (ret.empty()) - { - ret = "No MFC commands have been logged"; - } - - if (emptied) - { - ret += "\nPrevious MFC history has been emptied!"; - } - - spu_log.success("SPU MFC dump of '%s': %s", cpu->get_name(), ret); - } - else if (cpu->get_class() == thread_class::ppu && g_cfg.core.ppu_call_history) - { - const u32 max = get_max_allowed(tr("Max PPU calls logged"), tr("Decimal only, max allowed is %0."), ppu_thread::call_history_max_size); - - // Preallocate in order to save execution time when inside suspend_all. - std::vector copy(max); - std::vector sys_copy(ppu_thread::syscall_history_max_size); - - std::array emptied{}; - - cpu_thread::suspend_all(nullptr, {}, [&] - { - auto& list = static_cast(cpu)->call_history; - auto& sys_list = static_cast(cpu)->syscall_history; - copy_overlapping_list(list.index, max, list.data, copy, emptied[0]); - copy_overlapping_list(sys_list.index, max, sys_list.data, sys_copy, emptied[1]); - }); - - std::string ret; - - PPUDisAsm dis_asm(cpu_disasm_mode::normal, vm::g_sudo_addr); - u32 i = 0; - - for (auto it = copy.rbegin(); it != copy.rend(); it++, i++) - { - dis_asm.disasm(*it); - fmt::append(ret, "\n(%u) 0x%08x: %s", i, *it, dis_asm.last_opcode); - } - - i = 0; - for (auto it = sys_copy.rbegin(); it != sys_copy.rend(); it++, i++) - { - fmt::append(ret, "\n(%u) 0x%08x: %s, 0x%x, r3=0x%x, r4=0x%x, r5=0x%x, r6=0x%x", i, it->cia, it->func_name, it->error, it->args[0], it->args[1], it->args[2], it->args[3]); - } - - if (ret.empty()) - { - ret = "No PPU calls have been logged"; - } - - if (emptied[0]) - { - ret += "\nPrevious call history has been emptied!"; - } - - if (emptied[1]) - { - ret += "\nPrevious HLE call history has been emptied!"; - } - - ppu_log.success("PPU calling history dump of '%s': %s", cpu->get_name(), ret); - } - - return; - } - case Qt::Key_E: - { - if (event->isAutoRepeat()) - { - break; - } - - if (cpu->get_class() == thread_class::ppu || cpu->get_class() == thread_class::spu) - { - if (!m_inst_editor) - { - m_inst_editor = new instruction_editor_dialog(this, selected, m_disasm.get(), make_check_cpu(cpu)); - connect(m_inst_editor, &QDialog::finished, this, [this]() - { - m_inst_editor = nullptr; - }); - m_inst_editor->show(); - } - } - - return; - } - case Qt::Key_F: - { - if (event->isAutoRepeat()) - { - break; - } - - if (cpu->get_class() == thread_class::ppu) - { - static_cast(cpu)->debugger_mode.atomic_op([](ppu_debugger_mode& mode) - { - mode = static_cast((static_cast(mode) + 1) % static_cast(ppu_debugger_mode::max_mode)); - }); - - return; - } - if (cpu->get_class() == thread_class::spu) - { - static_cast(cpu)->debugger_mode.atomic_op([](spu_debugger_mode& mode) - { - mode = static_cast((static_cast(mode) + 1) % static_cast(spu_debugger_mode::max_mode)); - }); - - return; - } - - break; - } - case Qt::Key_R: - { - if (event->isAutoRepeat()) - { - break; - } - - if (cpu->get_class() == thread_class::ppu || cpu->get_class() == thread_class::spu) - { - if (cpu->get_class() == thread_class::spu && modifiers & Qt::AltModifier) - { - static_cast(cpu)->try_load_debug_capture(); - return; - } - - if (!m_reg_editor) - { - m_reg_editor = new register_editor_dialog(this, m_disasm.get(), make_check_cpu(cpu)); - connect(m_reg_editor, &QDialog::finished, this, [this]() - { - m_reg_editor = nullptr; - }); - m_reg_editor->show(); - } - } - - return; - } - case Qt::Key_S: - { - if (event->isAutoRepeat()) - { - break; - } - - if (modifiers & Qt::AltModifier) - { - if (cpu->get_class() == thread_class::rsx) - { - if (u32 addr = static_cast(cpu)->label_addr) - { - // Memory viewer pointing to RSX semaphores - idm::make(this, m_disasm, addr, make_check_cpu(nullptr)); - } - - return; - } - - if (cpu->get_class() == thread_class::ppu) - { - new elf_memory_dumping_dialog(pc, m_gui_settings, this); - return; - } - - if (cpu->get_class() != thread_class::spu) - { - return; - } - - if (!cpu->state.all_of(cpu_flag::wait + cpu_flag::dbg_pause)) - { - QMessageBox::warning(this, QObject::tr("Pause the SPU Thread!"), QObject::tr("Cannot perform SPU capture due to the thread needing manual pausing!")); - return; - } - - static_cast(cpu)->capture_state(); - return; - } - - break; - } - case Qt::Key_N: - { - // Next instruction according to code flow - // Known branch targets are selected over next PC for conditional branches - // Indirect branches (unknown targets, such as function return) do not proceed to any instruction - std::array res{umax, umax}; - - const u32 selected = (m_debugger_list->m_showing_selected_instruction ? m_debugger_list->m_selected_instruction : cpu->get_pc()) & address_limits; - - switch (cpu->get_class()) - { - case thread_class::spu: - { - res = op_branch_targets(selected, spu_opcode_t{static_cast(cpu)->_ref(selected)}); - break; - } - case thread_class::ppu: - { - be_t op{}; - - if (vm::check_addr(selected, vm::page_executable) && vm::try_access(selected, &op, 4, false)) - res = op_branch_targets(selected, op); - - break; - } - default: break; - } - - if (auto it = std::find_if(res.rbegin(), res.rend(), FN(x != umax)); it != res.rend()) - m_debugger_list->ShowAddress(*it - std::max(row, 0) * 4, true); - - return; - } - case Qt::Key_M: - { - if (event->isAutoRepeat()) - { - break; - } - - if (m_disasm && cpu->get_class() == thread_class::spu) - { - // Save shared pointer to shared memory handle, ensure the destructor will not be called until the SPUDisAsm is destroyed - static_cast(m_disasm.get())->set_shm(static_cast(cpu)->shm); - } - - // Memory viewer - idm::make(this, m_disasm, pc, make_check_cpu(cpu)); - return; - } - case Qt::Key_F10: - { - DoStep(true); - return; - } - case Qt::Key_F11: - { - DoStep(false); - return; - } - case Qt::Key_F5: - { - if (modifiers & Qt::AltModifier) - { - OnSelectSPUDisassembler(); - return; - } - - break; - } - default: break; - } - } - - event->ignore(); -} - -cpu_thread* debugger_frame::get_cpu() -{ - if (m_emu_state == system_state::stopped) - { - m_rsx = nullptr; - m_cpu.reset(); - return nullptr; - } - - // Wait flag is raised by the thread itself, acknowledging exit - if (m_cpu) - { - if (m_cpu->state.all_of(cpu_flag::wait + cpu_flag::exit)) - { - m_cpu.reset(); - return nullptr; - } - - return m_cpu.get(); - } - - // m_rsx is raw pointer, when emulation is stopped it won't be cleared - // Therefore need to do invalidation checks manually - - if (m_rsx) - { - if (g_fxo->try_get() != m_rsx || !m_rsx->ctrl || m_rsx->state.all_of(cpu_flag::wait + cpu_flag::exit)) - { - m_rsx = nullptr; - return nullptr; - } - } - - return m_rsx; -} - -std::function debugger_frame::make_check_cpu(cpu_thread* cpu, bool unlocked) -{ - constexpr cpu_thread* null_cpu = nullptr; - - if (Emu.IsStopped()) - { - return []() - { - return null_cpu; - }; - } - - const auto type = cpu ? cpu->get_class() : thread_class::general; - - shared_ptr shared; - - if (g_fxo->is_init>>() && g_fxo->is_init>>()) - { - if (unlocked) - { - if (type == thread_class::ppu) - { - shared = idm::get_unlocked>(cpu->id); - } - else if (type == thread_class::spu) - { - shared = idm::get_unlocked>(cpu->id); - } - } - else - { - if (type == thread_class::ppu) - { - shared = idm::get_unlocked>(cpu->id); - } - else if (type == thread_class::spu) - { - shared = idm::get_unlocked>(cpu->id); - } - } - } - - if (type == thread_class::rsx) - { - if (g_fxo->try_get() != cpu) - { - return []() - { - return null_cpu; - }; - } - } - else if (!shared || shared.get() != cpu) - { - return []() - { - return null_cpu; - }; - } - - return [cpu, type, shared = std::move(shared), emulation_id = Emu.GetEmulationIdentifier()]() mutable -> cpu_thread* - { - if (emulation_id != Emu.GetEmulationIdentifier() || Emu.IsStopped()) - { - // Invalidate all data after Emu.Kill() - shared.reset(); - cpu = nullptr; - return nullptr; - } - - if (type == thread_class::ppu || type == thread_class::spu) - { - // SPU and PPU - if (!shared || shared->state.all_of(cpu_flag::exit + cpu_flag::wait)) - { - shared.reset(); - return nullptr; - } - - return shared.get(); - } - - // RSX - const auto rsx = g_fxo->try_get(); - - if (cpu) - { - if (rsx != cpu || !rsx->ctrl || rsx->state.all_of(cpu_flag::wait + cpu_flag::exit)) - { - cpu = nullptr; - return nullptr; - } - } - - return cpu; - }; -} - -void debugger_frame::UpdateUI() -{ - const auto cpu = get_cpu(); - - // Refresh at a high rate during initialization (looks weird otherwise) - if (m_ui_update_ctr % (cpu || m_ui_update_ctr < 200 || m_debugger_list->m_dirty_flag ? 5 : 50) == 0) - { - // If no change to instruction position happened, update instruction list at 20hz - ShowPC(); - } - - if (m_ui_update_ctr % 20 == 0 && !m_thread_list_pending_update) - { - // Update threads list at 5hz (low priority) - UpdateUnitList(); - } - - if (!cpu) - { - if (m_last_pc != umax || !m_last_query_state.empty()) - { - if (m_ui_update_ctr % 20 && !m_thread_list_pending_update) - { - // Update threads list (thread exited) - UpdateUnitList(); - } - - ShowPC(); - m_last_query_state.clear(); - m_last_pc = -1; - DoUpdate(); - } - } - else if (m_ui_update_ctr % 5 == 0 || m_ui_update_ctr < m_ui_fast_update_permission_deadline) - { - const auto cia = cpu->get_pc(); - const auto size_context = cpu->get_class() == thread_class::ppu ? sizeof(ppu_thread) : - cpu->get_class() == thread_class::spu ? sizeof(spu_thread) : - sizeof(cpu_thread); - - if (m_last_pc != cia || m_last_query_state.size() != size_context || std::memcmp(m_last_query_state.data(), static_cast(cpu), size_context)) - { - // Copy thread data - m_last_query_state.resize(size_context); - std::memcpy(m_last_query_state.data(), static_cast(cpu), size_context); - - m_last_pc = cia; - DoUpdate(); - - const bool paused = !!(cpu->state & s_pause_flags); - - if (paused) - { - m_btn_run->setText(RunString); - } - else - { - m_btn_run->setText(PauseString); - } - - if (m_ui_update_ctr % 5) - { - // Call if it hasn't been called before - ShowPC(); - } - - if (is_using_interpreter(cpu->get_class())) - { - m_btn_step->setEnabled(paused); - m_btn_step_over->setEnabled(paused); - } - } - } - - m_ui_update_ctr++; -} - -void debugger_frame::UpdateUnitList() -{ - const u64 emulation_id = static_cast>(Emu.GetEmulationIdentifier()); - const u64 threads_created = cpu_thread::g_threads_created; - const u64 threads_deleted = cpu_thread::g_threads_deleted; - const system_state emu_state = Emu.GetStatus(); - - std::unique_lock lock{id_manager::g_mutex, std::defer_lock}; - - if (emulation_id == m_emulation_id && threads_created == m_threads_created && threads_deleted == m_threads_deleted && emu_state == m_emu_state) - { - // Nothing to do - m_thread_list_pending_update = false; - return; - } - - const cpu_thread* old_cpu_ptr = get_cpu(); - - if (!lock.try_lock()) - { - m_thread_list_pending_update = true; - QTimer::singleShot(5, [this]() - { - UpdateUnitList(); - }); - return; - } - - std::vector>> cpu_list; - cpu_list.reserve(threads_created >= threads_deleted ? 0 : threads_created - threads_deleted); - - usz reselected_index = umax; - - const auto on_select = [&](u32 id, cpu_thread& cpu) - { - std::function func_cpu = make_check_cpu(std::addressof(cpu), true); - - // Space at the end is to pad a gap on the right - cpu_list.emplace_back(qstr((id >> 24 == 0x55 ? "RSX[0x55555555]" : cpu.get_name()) + ' '), std::move(func_cpu)); - - if (old_cpu_ptr == std::addressof(cpu)) - { - reselected_index = cpu_list.size() - 1; - } - }; - - if (emu_state != system_state::stopped) - { - if (g_fxo->is_init>>()) - { - idm::select>(on_select, idm::unlocked); - } - - if (g_fxo->is_init>>()) - { - idm::select>(on_select, idm::unlocked); - } - - if (const auto render = g_fxo->try_get(); render && render->ctrl) - { - on_select(render->id, *render); - } - } - - lock.unlock(); - - m_emulation_id = emulation_id; - m_threads_created = threads_created; - m_threads_deleted = threads_deleted; - m_emu_state = emu_state; - m_thread_list_pending_update = false; - - { - const QSignalBlocker blocker(m_choice_units); - - m_threads_info.clear(); - m_choice_units->clear(); - - m_threads_info.emplace_back(make_check_cpu(nullptr)); - m_choice_units->addItem(NoThreadString); - - for (auto&& [thread_name, func_cpu] : cpu_list) - { - m_threads_info.emplace_back(std::move(func_cpu)); - m_choice_units->addItem(std::move(thread_name)); - } - - if (reselected_index != umax) - { - // Include no-thread at index 0 - m_choice_units->setCurrentIndex(::narrow(reselected_index + 1)); - } - } - - // Close dialogs which are tied to the specific thread selected - if (reselected_index == umax) - { - if (m_reg_editor) - m_reg_editor->close(); - if (m_inst_editor) - m_inst_editor->close(); - if (m_goto_dialog) - m_goto_dialog->close(); - } - - if (emu_state == system_state::stopped) - { - ClearBreakpoints(); - ClearCallStack(); - } - - OnSelectUnit(); - - m_choice_units->update(); -} - -void debugger_frame::OnSelectUnit() -{ - if (m_is_spu_disasm_mode) - { - return; - } - - cpu_thread* selected = nullptr; - - if (m_emu_state != system_state::stopped) - { - if (int index = m_choice_units->currentIndex(); index >= 0 && index + 0u < m_threads_info.size()) - { - selected = ::at32(m_threads_info, index)(); - } - - if (selected && m_cpu.get() == selected) - { - // They match, nothing to do. - return; - } - - if (selected && m_rsx == selected) - { - return; - } - - if (!selected && !m_rsx && !m_cpu) - { - return; - } - } - - m_disasm.reset(); - m_cpu.reset(); - m_rsx = nullptr; - m_spu_disasm_memory.reset(); - - if (selected) - { - const u32 cpu_id = selected->id; - - switch (cpu_id >> 24) - { - case 1: - { - m_cpu = idm::get_unlocked>(cpu_id); - - if (selected == m_cpu.get()) - { - m_disasm = make_disasm(selected, m_cpu); - } - - break; - } - case 2: - { - m_cpu = idm::get_unlocked>(cpu_id); - - if (selected == m_cpu.get()) - { - m_disasm = make_disasm(selected, m_cpu); - } - - break; - } - case 0x55: - { - m_rsx = static_cast(selected); - - if (get_cpu()) - { - m_disasm = make_disasm(m_rsx, null_ptr); - } - - break; - } - default: break; - } - } - - if (!m_disasm) - { - m_cpu.reset(); - m_rsx = nullptr; - } - - EnableButtons(true); - - m_debugger_list->UpdateCPUData(m_disasm); - m_breakpoint_list->UpdateCPUData(m_disasm); - - ShowPC(true); - DoUpdate(); - UpdateUI(); -} - -void debugger_frame::OnSelectSPUDisassembler() -{ - if (m_spu_disasm_dialog) - { - m_spu_disasm_dialog->move(QCursor::pos()); - m_spu_disasm_dialog->show(); - m_spu_disasm_dialog->setFocus(); - return; - } - - m_spu_disasm_dialog = new QDialog(this); - m_spu_disasm_dialog->setWindowTitle(tr("SPU Disassembler Properties")); - - // Panels - QVBoxLayout* vbox_panel(new QVBoxLayout()); - QHBoxLayout* hbox_expression_input_panel = new QHBoxLayout(); - QHBoxLayout* hbox_button_panel(new QHBoxLayout()); - - // Address expression input - QLineEdit* source_eal(new QLineEdit(m_spu_disasm_dialog)); - QLineEdit* start_pc(new QLineEdit(m_spu_disasm_dialog)); - source_eal->setFont(m_mono); - source_eal->setMaxLength(12); - source_eal->setValidator(new QRegularExpressionValidator(QRegularExpression("^(0[xX])?0*[a-fA-F0-9]{0,8}$"), this)); - start_pc->setFont(m_mono); - start_pc->setMaxLength(7); - start_pc->setValidator(new QRegularExpressionValidator(QRegularExpression("^(0[xX])?0*[a-fA-F0-9]{0,5}$"), this)); - - // Ok/Cancel - QPushButton* button_ok = new QPushButton(tr("OK")); - QPushButton* button_cancel = new QPushButton(tr("Cancel")); - - hbox_expression_input_panel->addWidget(new QLabel(tr("Source Address: "))); - hbox_expression_input_panel->addWidget(source_eal); - hbox_expression_input_panel->addSpacing(10); - hbox_expression_input_panel->addWidget(new QLabel(tr("Load PC: "))); - hbox_expression_input_panel->addWidget(start_pc); - - hbox_button_panel->addWidget(button_ok); - hbox_button_panel->addWidget(button_cancel); - - vbox_panel->addLayout(hbox_expression_input_panel); - vbox_panel->addSpacing(8); - vbox_panel->addLayout(hbox_button_panel); - - m_spu_disasm_dialog->setLayout(vbox_panel); - - const QFont font = source_eal->font(); - - source_eal->setPlaceholderText(QString::fromStdString(fmt::format("0x%08x", 0))); - start_pc->setPlaceholderText(QString::fromStdString(fmt::format("0x%05x", 0))); - - source_eal->setFixedWidth(gui::utils::get_label_width(source_eal->placeholderText(), &font) + 5); - start_pc->setFixedWidth(gui::utils::get_label_width(start_pc->placeholderText(), &font) + 5); - - if (m_spu_disasm_origin_eal) - { - source_eal->setText(QString::fromStdString(fmt::format("0x%08x", m_spu_disasm_origin_eal))); - start_pc->setText(QString::fromStdString(fmt::format("0x%05x", m_spu_disasm_pc))); - } - - connect(button_ok, &QAbstractButton::clicked, m_spu_disasm_dialog, &QDialog::accept); - connect(button_cancel, &QAbstractButton::clicked, m_spu_disasm_dialog, &QDialog::reject); - - m_spu_disasm_dialog->move(QCursor::pos()); - m_spu_disasm_dialog->setAttribute(Qt::WA_DeleteOnClose); - - connect(m_spu_disasm_dialog, &QDialog::finished, this, [this, source_eal, start_pc](int result) - { - m_spu_disasm_dialog = nullptr; - - if (result != QDialog::Accepted) - { - return; - } - const u64 spu_base = EvaluateExpression(start_pc->text()); - - if (spu_base > SPU_LS_SIZE - 4 || spu_base % 4) - { - return; - } - - const u64 spu_addr = EvaluateExpression(source_eal->text()); - - if (spu_addr == umax || !spu_addr) - { - return; - } - - // Try to load as much memory as possible until SPU local memory ends - // Because I don't think there is a need for a size argument - // The user probably does not know the exact size of the SPU code either - u32 spu_size = SPU_LS_SIZE - spu_base; - - for (u32 passed = spu_base; passed < SPU_LS_SIZE; passed += 4096) - { - if (!vm::check_addr(spu_addr + passed)) - { - if (passed == spu_base) - { - return; - } - - spu_size = passed - spu_base - (spu_addr + passed) % 4096; - break; - } - - if (4096 > ~(spu_addr + passed)) - { - // For overflow - spu_size = std::min(SPU_LS_SIZE, 0 - spu_addr); - break; - } - } - - m_disasm.reset(); - m_cpu.reset(); - m_rsx = nullptr; - - m_spu_disasm_memory = std::make_shared(SPU_LS_SIZE); - m_spu_disasm_memory->map_self(); - m_is_spu_disasm_mode = true; - - std::memset(m_spu_disasm_memory->get(), 0, spu_base); - std::memcpy(m_spu_disasm_memory->get() + spu_base, vm::get_super_ptr(spu_addr), spu_size); - std::memset(m_spu_disasm_memory->get() + spu_base + spu_size, 0, SPU_LS_SIZE - (spu_base + spu_size)); - - m_spu_disasm_pc = spu_base; - m_spu_disasm_origin_eal = spu_addr; - - m_disasm = std::make_shared(cpu_disasm_mode::interpreter, m_spu_disasm_memory->get()); - - EnableButtons(true); - - m_debugger_list->UpdateCPUData(m_disasm); - m_breakpoint_list->UpdateCPUData(m_disasm); - ShowPC(true); - DoUpdate(); - UpdateUI(); - }); - - m_spu_disasm_dialog->show(); -} - -void debugger_frame::DoUpdate() -{ - // Check if we need to disable a step over bp - if (const auto cpu0 = get_cpu(); cpu0 && m_last_step_over_breakpoint != umax && cpu0->get_pc() == m_last_step_over_breakpoint) - { - m_ppu_breakpoint_handler->RemoveBreakpoint(m_last_step_over_breakpoint); - m_last_step_over_breakpoint = -1; - } - - WritePanels(); -} - -void debugger_frame::WritePanels() -{ - const auto cpu = get_cpu(); - - if (!cpu) - { - m_misc_state->clear(); - m_regs->clear(); - ClearCallStack(); - return; - } - - int loc = m_misc_state->verticalScrollBar()->value(); - int hloc = m_misc_state->horizontalScrollBar()->value(); - m_misc_state->clear(); - m_misc_state->setPlainText(qstr(cpu->dump_misc())); - m_misc_state->verticalScrollBar()->setValue(loc); - m_misc_state->horizontalScrollBar()->setValue(hloc); - - loc = m_regs->verticalScrollBar()->value(); - hloc = m_regs->horizontalScrollBar()->value(); - m_regs->clear(); - m_last_reg_state.clear(); - cpu->dump_regs(m_last_reg_state, m_dump_reg_func_data); - m_regs->setPlainText(qstr(m_last_reg_state)); - m_regs->verticalScrollBar()->setValue(loc); - m_regs->horizontalScrollBar()->setValue(hloc); - - Q_EMIT CallStackUpdateRequested(cpu->dump_callstack_list()); -} - -void debugger_frame::ShowGotoAddressDialog() -{ - if (m_goto_dialog) - { - m_goto_dialog->move(QCursor::pos()); - m_goto_dialog->show(); - m_goto_dialog->setFocus(); - return; - } - - m_goto_dialog = new QDialog(this); - m_goto_dialog->setWindowTitle(tr("Go To Address")); - - // Panels - QVBoxLayout* vbox_panel(new QVBoxLayout()); - QHBoxLayout* hbox_expression_input_panel = new QHBoxLayout(); - QHBoxLayout* hbox_button_panel(new QHBoxLayout()); - - // Address expression input - QLineEdit* expression_input(new QLineEdit(m_goto_dialog)); - expression_input->setFont(m_mono); - expression_input->setMaxLength(18); - - if (const auto thread = get_cpu(); !thread || thread->get_class() != thread_class::spu) - { - expression_input->setValidator(new QRegularExpressionValidator(QRegularExpression("^(0[xX])?0*[a-fA-F0-9]{0,8}$"), this)); - } - else - { - expression_input->setValidator(new QRegularExpressionValidator(QRegularExpression("^(0[xX])?0*[a-fA-F0-9]{0,5}$"), this)); - } - - // Ok/Cancel - QPushButton* button_ok = new QPushButton(tr("OK")); - QPushButton* button_cancel = new QPushButton(tr("Cancel")); - - hbox_expression_input_panel->addWidget(expression_input); - - hbox_button_panel->addWidget(button_ok); - hbox_button_panel->addWidget(button_cancel); - - vbox_panel->addLayout(hbox_expression_input_panel); - vbox_panel->addSpacing(8); - vbox_panel->addLayout(hbox_button_panel); - - m_goto_dialog->setLayout(vbox_panel); - - const auto cpu_check = make_check_cpu(get_cpu()); - const auto cpu = cpu_check(); - const QFont font = expression_input->font(); - - // -1 from get_pc() turns into 0 - const u32 pc = cpu ? utils::align(cpu->get_pc(), 4) : 0; - expression_input->setPlaceholderText(QString("0x%1").arg(pc, 16, 16, QChar('0'))); - expression_input->setFixedWidth(gui::utils::get_label_width(expression_input->placeholderText(), &font) + 5); - - connect(button_ok, &QAbstractButton::clicked, m_goto_dialog, &QDialog::accept); - connect(button_cancel, &QAbstractButton::clicked, m_goto_dialog, &QDialog::reject); - - m_goto_dialog->move(QCursor::pos()); - m_goto_dialog->setAttribute(Qt::WA_DeleteOnClose); - - connect(m_goto_dialog, &QDialog::finished, this, [this, cpu, cpu_check, expression_input](int result) - { - // Check if the thread has not been destroyed and is still the focused since - // This also works if no thread is selected and has been selected before - if (result == QDialog::Accepted && cpu == get_cpu() && cpu == cpu_check()) - { - PerformGoToRequest(expression_input->text()); - } - - m_goto_dialog = nullptr; - }); - - m_goto_dialog->show(); -} - -void debugger_frame::PerformGoToRequest(const QString& text_argument) -{ - const bool asterisk_prefixed = text_argument.startsWith(QChar('*')); - const u64 address = EvaluateExpression(asterisk_prefixed ? text_argument.sliced(1, text_argument.size() - 1) : text_argument); - - if (address != umax) - { - // Try to read from OPD entry if prefixed by asterisk - if (asterisk_prefixed) - { - if (auto cpu = get_cpu()) - { - if (cpu->try_get()) - { - const vm::ptr func_ptr = vm::cast(static_cast(address)); - - be_t code_addr{}; - - if (func_ptr.try_read(code_addr)) - { - m_debugger_list->ShowAddress(code_addr, true); - } - } - } - - return; - } - - m_debugger_list->ShowAddress(static_cast(address), true); - } -} - -void debugger_frame::PerformGoToThreadRequest(const QString& text_argument) -{ - const u64 thread_id = EvaluateExpression(text_argument); - - if (thread_id != umax) - { - for (usz i = 0; i < m_threads_info.size(); i++) - { - if (cpu_thread* ptr = m_threads_info[i](); ptr && ptr->id == thread_id) - { - // Success - m_choice_units->setCurrentIndex(::narrow(i)); - return; - } - } - } -} - -void debugger_frame::PerformAddBreakpointRequest(u32 addr) -{ - m_debugger_list->BreakpointRequested(addr, true); -} - -u64 debugger_frame::EvaluateExpression(const QString& expression) -{ - bool ok = false; - - // Parse expression (or at least used to, was nuked to remove the need for QtJsEngine) - const QString fixed_expression = QRegularExpression(QRegularExpression::anchoredPattern("a .*|^[A-Fa-f0-9]+$")).match(expression).hasMatch() ? "0x" + expression : expression; - const u64 res = static_cast(fixed_expression.toULong(&ok, 16)); - - if (ok) - return res; - if (const auto thread = get_cpu(); thread && expression.isEmpty()) - return thread->get_pc(); - return umax; -} - -void debugger_frame::ClearBreakpoints() const -{ - m_breakpoint_list->ClearBreakpoints(); -} - -void debugger_frame::ClearCallStack() -{ - Q_EMIT CallStackUpdateRequested({}); -} - -void debugger_frame::ShowPC(bool user_requested) -{ - const auto cpu0 = get_cpu(); - - const u32 pc = (cpu0 ? cpu0->get_pc() : (m_is_spu_disasm_mode ? m_spu_disasm_pc : 0)); - - if (user_requested) - { - m_debugger_list->EnableThreadFollowing(); - } - - m_debugger_list->ShowAddress(pc, false); -} - -void debugger_frame::DoStep(bool step_over) -{ - if (const auto cpu = get_cpu()) - { - bool should_step_over = step_over && cpu->get_class() == thread_class::ppu; - - // If stepping over, lay at the same spot and wait for the thread to finish the call - // If not, fixate on the current pointed instruction - m_debugger_list->EnableThreadFollowing(!should_step_over); - - if (should_step_over) - { - m_debugger_list->ShowAddress(cpu->get_pc() + 4, false); - } - - if (step_over && cpu->get_class() == thread_class::rsx) - { - const bool was_paused = cpu->is_paused(); - static_cast(cpu)->pause_on_draw = true; - - if (was_paused) - { - RunBtnPress(); - m_debugger_list->EnableThreadFollowing(true); - } - - return; - } - - if (const auto _state = +cpu->state; _state & s_pause_flags && _state & cpu_flag::wait && !(_state & cpu_flag::dbg_step)) - { - if (should_step_over && cpu->get_class() == thread_class::ppu) - { - const u32 current_instruction_pc = cpu->get_pc(); - - vm::ptr inst_ptr = vm::cast(current_instruction_pc); - be_t result{}; - - if (inst_ptr.try_read(result)) - { - ppu_opcode_t ppu_op{result}; - const ppu_itype::type itype = g_ppu_itype.decode(ppu_op.opcode); - - should_step_over = (itype & ppu_itype::branch && ppu_op.lk); - } - } - - if (should_step_over) - { - const u32 current_instruction_pc = cpu->get_pc(); - - // Set breakpoint on next instruction - const u32 next_instruction_pc = current_instruction_pc + 4; - m_ppu_breakpoint_handler->AddBreakpoint(next_instruction_pc, breakpoint_types::bp_exec); - - // Undefine previous step over breakpoint if it hasn't been already - // This can happen when the user steps over a branch that doesn't return to itself - if (m_last_step_over_breakpoint != umax) - { - m_ppu_breakpoint_handler->RemoveBreakpoint(next_instruction_pc); - } - - m_last_step_over_breakpoint = next_instruction_pc; - } - - cpu->state.atomic_op([&](bs_t& state) - { - state -= s_pause_flags; - - if (!should_step_over) - { - if (u32* ptr = cpu->get_pc2()) - { - state += cpu_flag::dbg_step; - *ptr = cpu->get_pc(); - } - } - }); - - cpu->state.notify_one(); - } - } - - // Tighten up, put the debugger on a wary watch over any thread info changes if there aren't any - // This allows responsive debugger interaction - m_ui_fast_update_permission_deadline = m_ui_update_ctr + 5; -} - -void debugger_frame::EnableUpdateTimer(bool enable) const -{ - if (m_update->isActive() == enable) - { - return; - } - - enable ? m_update->start(10) : m_update->stop(); -} - -void debugger_frame::RunBtnPress() -{ - if (const auto cpu = get_cpu()) - { - // If paused, unpause. - // If not paused, add dbg_pause. - const auto old = cpu->state.fetch_op([](bs_t& state) - { - if (state & s_pause_flags) - { - state -= s_pause_flags; - } - else - { - state += cpu_flag::dbg_pause; - } - }); - - // Notify only if no pause flags are set after this change - if (old & s_pause_flags) - { - if (g_debugger_pause_all_threads_on_bp && Emu.IsPaused() && (old & s_pause_flags) == s_pause_flags) - { - // Resume all threads were paused by this breakpoint - Emu.Resume(); - } - - cpu->state.notify_one(); - m_debugger_list->EnableThreadFollowing(); - } - } - - // Tighten up, put the debugger on a wary watch over any thread info changes if there aren't any - // This allows responsive debugger interaction - m_ui_fast_update_permission_deadline = m_ui_update_ctr + 5; -} - -void debugger_frame::EnableButtons(bool enable) -{ - const auto cpu = get_cpu(); - - if (!cpu) - enable = false; - - const bool step = enable && is_using_interpreter(cpu->get_class()); - - m_go_to_addr->setEnabled(enable); - m_go_to_pc->setEnabled(enable); - m_btn_add_bp->setEnabled(enable); - m_btn_step->setEnabled(step); - m_btn_step_over->setEnabled(step); - m_btn_run->setEnabled(enable); -} diff --git a/rpcs3qt-legacy/debugger_frame.h b/rpcs3qt-legacy/debugger_frame.h deleted file mode 100644 index 96a3ece2f..000000000 --- a/rpcs3qt-legacy/debugger_frame.h +++ /dev/null @@ -1,146 +0,0 @@ -#pragma once - -#include "util/types.hpp" -#include "util/shared_ptr.hpp" - -#include "custom_dock_widget.h" - -#include -#include -#include -#include - -#include -#include -#include -#include - -class CPUDisAsm; -class cpu_thread; -class gui_settings; -class debugger_list; -class breakpoint_list; -class breakpoint_handler; -class call_stack_list; - -namespace rsx -{ - class thread; -} - -namespace utils -{ - class shm; -} - -enum class system_state : u32; - -class instruction_editor_dialog; -class register_editor_dialog; - -class debugger_frame : public custom_dock_widget -{ - Q_OBJECT - - const QString NoThreadString = tr("No Thread"); - const QString RunString = tr("Run"); - const QString PauseString = tr("Pause"); - - debugger_list* m_debugger_list = nullptr; - QSplitter* m_right_splitter = nullptr; - QFont m_mono; - QPlainTextEdit* m_misc_state = nullptr; - QPlainTextEdit* m_regs = nullptr; - QPushButton* m_go_to_addr = nullptr; - QPushButton* m_go_to_pc = nullptr; - QPushButton* m_btn_step = nullptr; - QPushButton* m_btn_step_over = nullptr; - QPushButton* m_btn_add_bp = nullptr; - QPushButton* m_btn_run = nullptr; - - QComboBox* m_choice_units = nullptr; - QTimer* m_update = nullptr; - QSplitter* m_splitter = nullptr; - - u64 m_threads_created = -1; - u64 m_threads_deleted = -1; - system_state m_emu_state{}; - u64 m_emulation_id{}; - u32 m_last_pc = -1; - std::vector m_last_query_state; - std::string m_last_reg_state; - std::any m_dump_reg_func_data; - std::vector> m_threads_info; - u32 m_last_step_over_breakpoint = -1; - u64 m_ui_update_ctr = 0; - u64 m_ui_fast_update_permission_deadline = 0; - bool m_thread_list_pending_update = false; - - std::shared_ptr m_disasm; // Only shared to allow base/derived functionality - shared_ptr m_cpu; - rsx::thread* m_rsx = nullptr; - std::shared_ptr m_spu_disasm_memory; - u32 m_spu_disasm_origin_eal = 0; - u32 m_spu_disasm_pc = 0; - bool m_is_spu_disasm_mode = false; - - breakpoint_list* m_breakpoint_list = nullptr; - breakpoint_handler* m_ppu_breakpoint_handler = nullptr; - call_stack_list* m_call_stack_list = nullptr; - instruction_editor_dialog* m_inst_editor = nullptr; - register_editor_dialog* m_reg_editor = nullptr; - QDialog* m_goto_dialog = nullptr; - QDialog* m_spu_disasm_dialog = nullptr; - - std::shared_ptr m_gui_settings; - - cpu_thread* get_cpu(); - std::function make_check_cpu(cpu_thread* cpu, bool unlocked = false); - void open_breakpoints_settings(); - -public: - explicit debugger_frame(std::shared_ptr settings, QWidget* parent = nullptr); - - void SaveSettings() const; - void ChangeColors() const; - - void UpdateUI(); - void UpdateUnitList(); - - void DoUpdate(); - void WritePanels(); - void EnableButtons(bool enable); - void ShowGotoAddressDialog(); - void PerformGoToRequest(const QString& text_argument); - void PerformGoToThreadRequest(const QString& text_argument); - void PerformAddBreakpointRequest(u32 addr); - u64 EvaluateExpression(const QString& expression); - void ClearBreakpoints() const; // Fallthrough method into breakpoint_list. - void ClearCallStack(); - - /** Needed so key press events work when other objects are selected in debugger_frame. */ - bool eventFilter(QObject* object, QEvent* event) override; - -protected: - /** Override inherited method from Qt to allow signalling when close happened.*/ - void closeEvent(QCloseEvent* event) override; - void showEvent(QShowEvent* event) override; - void hideEvent(QHideEvent* event) override; - void keyPressEvent(QKeyEvent* event) override; - -Q_SIGNALS: - void DebugFrameClosed(); - void CallStackUpdateRequested(const std::vector>& call_stack); - -public Q_SLOTS: - void DoStep(bool step_over = false); - -private Q_SLOTS: - void OnSelectUnit(); - void OnSelectSPUDisassembler(); - void ShowPC(bool user_requested = false); - void EnableUpdateTimer(bool enable) const; - void RunBtnPress(); -}; - -Q_DECLARE_METATYPE(u32) diff --git a/rpcs3qt-legacy/debugger_list.cpp b/rpcs3qt-legacy/debugger_list.cpp deleted file mode 100644 index 5012d27a4..000000000 --- a/rpcs3qt-legacy/debugger_list.cpp +++ /dev/null @@ -1,497 +0,0 @@ -#include "debugger_list.h" -#include "gui_settings.h" -#include "qt_utils.h" -#include "breakpoint_handler.h" - -#include "Emu/Cell/SPUThread.h" -#include "Emu/CPU/CPUDisAsm.h" -#include "Emu/CPU/CPUThread.h" -#include "Emu/RSX/RSXDisAsm.h" -#include "Emu/RSX/RSXThread.h" -#include "Emu/System.h" - -#include "util/asm.hpp" - -#include -#include -#include -#include - -#include - -constexpr auto qstr = QString::fromStdString; - -debugger_list::debugger_list(QWidget* parent, std::shared_ptr gui_settings, breakpoint_handler* handler) - : QListWidget(parent), m_gui_settings(std::move(gui_settings)), m_ppu_breakpoint_handler(handler) -{ - setWindowTitle(tr("ASM")); - - for (uint i = 0; i < m_item_count; ++i) - { - insertItem(i, new QListWidgetItem("")); - } - - setSizeAdjustPolicy(QListWidget::AdjustToContents); - setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); - - connect(this, &QListWidget::currentRowChanged, this, [this](int row) - { - if (row < 0) - { - m_selected_instruction = -1; - m_showing_selected_instruction = false; - return; - } - - u32 pc = m_start_addr; - - const auto cpu = m_disasm && !Emu.IsStopped() ? m_disasm->get_cpu() : nullptr; - - for (; cpu && cpu->get_class() == thread_class::rsx && row; row--) - { - // If scrolling forwards (downwards), we can skip entire commands - pc += std::max(m_disasm->disasm(pc), 4); - } - - m_selected_instruction = pc + row * 4; - }); -} - -void debugger_list::UpdateCPUData(std::shared_ptr disasm) -{ - if ((!m_disasm) != (!disasm) || (m_disasm && disasm->get_cpu() != m_disasm->get_cpu())) - { - m_selected_instruction = -1; - m_showing_selected_instruction = false; - } - - m_disasm = std::move(disasm); -} - -u32 debugger_list::GetStartAddress(u32 address) -{ - const auto cpu = m_disasm && !Emu.IsStopped() ? m_disasm->get_cpu() : nullptr; - - const u32 steps = m_item_count / 3; - const u32 inst_count_jump_on_step = std::min(steps, 4); - - const bool is_spu = IsSpu(); - const u32 address_mask = (is_spu ? 0x3fffc : ~3); - - u32 result = address & address_mask; - - if (cpu && cpu->get_class() == thread_class::rsx) - { - if (auto [count, res] = static_cast(cpu)->try_get_pc_of_x_cmds_backwards(steps, address); count == steps) - { - result = res; - } - } - else - { - result = (address - (steps * 4)) & address_mask; - } - - u32 upper_bound = (m_start_addr + (steps * 4)) & address_mask; - - if (cpu && cpu->get_class() == thread_class::rsx) - { - if (auto [count, res] = static_cast(cpu)->try_get_pc_of_x_cmds_backwards(0 - steps, m_start_addr); count == steps) - { - upper_bound = res; - } - } - - bool goto_addr = false; - - if (upper_bound > m_start_addr) - { - goto_addr = address < m_start_addr || address >= upper_bound; - } - else - { - // Overflowing bounds case - goto_addr = address < m_start_addr && address >= upper_bound; - } - - if (goto_addr) - { - m_pc = address; - - if (address > upper_bound && address - upper_bound < inst_count_jump_on_step * 4) - { - m_start_addr = result + inst_count_jump_on_step * 4; - } - else - { - m_start_addr = result; - } - } - - return m_start_addr; -} - -bool debugger_list::IsSpu() const -{ - const auto cpu = m_disasm && !Emu.IsStopped() ? m_disasm->get_cpu() : nullptr; - - return (cpu && cpu->get_class() == thread_class::spu) || (m_disasm && !cpu); -} - -void debugger_list::ShowAddress(u32 addr, bool select_addr, bool direct) -{ - const auto cpu = m_disasm && !Emu.IsStopped() ? m_disasm->get_cpu() : nullptr; - - const decltype(spu_thread::local_breakpoints)* spu_bps_list{}; - - if (cpu && cpu->get_class() == thread_class::spu) - { - spu_bps_list = &static_cast(cpu)->local_breakpoints; - } - - auto IsBreakpoint = [&](u32 pc) - { - switch (cpu ? cpu->get_class() : thread_class::general) - { - case thread_class::ppu: - { - return m_ppu_breakpoint_handler->HasBreakpoint(pc, breakpoint_types::bp_exec); - } - case thread_class::spu: - { - const u32 pos_at = pc / 4; - const u32 pos_bit = 1u << (pos_at % 8); - - return !!((*spu_bps_list)[pos_at / 8] & pos_bit); - } - default: return false; - } - }; - - if (select_addr || direct) - { - // The user wants to survey a specific memory location, do not interfere from this point forth - m_follow_thread = false; - } - - m_dirty_flag = false; - - u32 pc = m_start_addr; - - if (!direct && (m_follow_thread || select_addr)) - { - pc = GetStartAddress(addr); - } - - const auto& default_foreground = palette().color(foregroundRole()); - const auto& default_background = palette().color(backgroundRole()); - - m_showing_selected_instruction = false; - - if (select_addr) - { - m_selected_instruction = addr; - } - - for (uint i = 0; i < m_item_count; ++i) - { - if (auto list_item = item(i); list_item->isSelected()) - { - list_item->setSelected(false); - } - } - - if (!m_disasm || (cpu && cpu->state.all_of(cpu_flag::exit + cpu_flag::wait))) - { - for (uint i = 0; i < m_item_count; ++i) - { - QListWidgetItem* list_item = item(i); - list_item->setText(qstr(fmt::format(" [%08x] ?? ?? ?? ??:", 0))); - list_item->setForeground(default_foreground); - list_item->setBackground(default_background); - } - } - else - { - const bool is_spu = IsSpu(); - const u32 address_limits = (is_spu ? 0x3fffc : ~3); - const u32 current_pc = (cpu ? cpu->get_pc() : 0); - m_start_addr &= address_limits; - pc = m_start_addr; - - for (uint i = 0, count = 4; i < m_item_count; ++i, pc = (pc + count) & address_limits) - { - QListWidgetItem* list_item = item(i); - - if (pc == current_pc) - { - list_item->setForeground(m_text_color_pc); - list_item->setBackground(m_color_pc); - } - else if (pc == m_selected_instruction) - { - // setSelected may invoke a resize event which causes stack overflow, terminate recursion - if (!list_item->isSelected()) - { - list_item->setSelected(true); - } - - m_showing_selected_instruction = true; - } - else if (IsBreakpoint(pc)) - { - list_item->setForeground(m_text_color_bp); - list_item->setBackground(m_color_bp); - } - else - { - list_item->setForeground(default_foreground); - list_item->setBackground(default_background); - } - - if (cpu && cpu->get_class() == thread_class::ppu && !vm::check_addr(pc, 0)) - { - list_item->setText((IsBreakpoint(pc) ? ">> " : " ") + qstr(fmt::format("[%08x] ?? ?? ?? ??:", pc))); - count = 4; - continue; - } - - if (cpu && cpu->get_class() == thread_class::ppu && !vm::check_addr(pc, vm::page_executable)) - { - const u32 data = *vm::get_super_ptr>(pc); - list_item->setText((IsBreakpoint(pc) ? ">> " : " ") + qstr(fmt::format("[%08x] %02x %02x %02x %02x:", pc, - static_cast(data >> 24), - static_cast(data >> 16), - static_cast(data >> 8), - static_cast(data >> 0)))); - count = 4; - continue; - } - - count = m_disasm->disasm(pc); - - if (!count) - { - list_item->setText((IsBreakpoint(pc) ? ">> " : " ") + qstr(fmt::format("[%08x] ??? ?? ??", pc))); - count = 4; - continue; - } - - list_item->setText((IsBreakpoint(pc) ? ">> " : " ") + qstr(m_disasm->last_opcode)); - } - } - - setLineWidth(-1); -} - -void debugger_list::RefreshView() -{ - const bool old = std::exchange(m_follow_thread, false); - ShowAddress(0, false); - m_follow_thread = old; -} - -void debugger_list::EnableThreadFollowing(bool enable) -{ - m_follow_thread = enable; -} - -void debugger_list::scroll(s32 steps) -{ - const auto cpu = m_disasm && !Emu.IsStopped() ? m_disasm->get_cpu() : nullptr; - - for (; cpu && cpu->get_class() == thread_class::rsx && steps > 0; steps--) - { - // If scrolling forwards (downwards), we can skip entire commands - m_start_addr += std::max(m_disasm->disasm(m_start_addr), 4); - } - - if (cpu && cpu->get_class() == thread_class::rsx && steps < 0) - { - // If scrolling backwards (upwards), try to obtain the start of commands tail - if (auto [count, res] = static_cast(cpu)->try_get_pc_of_x_cmds_backwards(-steps, m_start_addr); count == 0u - steps) - { - steps = 0; - m_start_addr = res; - } - } - - EnableThreadFollowing(false); - m_start_addr += steps * 4; - ShowAddress(0, false, true); -} - -void debugger_list::keyPressEvent(QKeyEvent* event) -{ - const auto cpu = m_disasm && !Emu.IsStopped() ? m_disasm->get_cpu() : nullptr; - - // Always accept event (so it would not bubble upwards, debugger_frame already sees it) - struct accept_event_t - { - QKeyEvent* event; - - ~accept_event_t() noexcept - { - event->accept(); - } - } accept_event{event}; - - if (!isActiveWindow()) - { - QListWidget::keyPressEvent(event); - return; - } - - if (event->modifiers()) - { - QListWidget::keyPressEvent(event); - return; - } - - switch (event->key()) - { - case Qt::Key_PageUp: scroll(0 - m_item_count); return; - case Qt::Key_PageDown: scroll(m_item_count); return; - case Qt::Key_Up: scroll(-1); return; - case Qt::Key_Down: scroll(1); return; - case Qt::Key_I: - { - if (event->isAutoRepeat()) - { - QListWidget::keyPressEvent(event); - return; - } - - if (cpu && cpu->get_class() == thread_class::rsx) - { - create_rsx_command_detail(m_showing_selected_instruction ? m_selected_instruction : m_pc); - return; - } - - break; - } - default: break; - } - - QListWidget::keyPressEvent(event); -} - -void debugger_list::showEvent(QShowEvent* event) -{ - if (m_cmd_detail) - m_cmd_detail->show(); - QListWidget::showEvent(event); -} - -void debugger_list::hideEvent(QHideEvent* event) -{ - if (m_cmd_detail) - m_cmd_detail->hide(); - QListWidget::hideEvent(event); -} - -void debugger_list::create_rsx_command_detail(u32 pc) -{ - RSXDisAsm rsx_dis = static_cast(*m_disasm); - rsx_dis.change_mode(cpu_disasm_mode::list); - - // Either invalid or not a method - if (rsx_dis.disasm(pc) <= 4) - return; - - if (m_cmd_detail) - { - // Edit the existing dialog - m_detail_label->setText(QString::fromStdString(rsx_dis.last_opcode)); - m_cmd_detail->setFixedSize(m_cmd_detail->sizeHint()); - return; - } - - m_cmd_detail = new QDialog(this); - m_cmd_detail->setWindowTitle(tr("RSX Command Detail")); - - m_detail_label = new QLabel(QString::fromStdString(rsx_dis.last_opcode), this); - m_detail_label->setFont(font()); - gui::utils::set_font_size(*m_detail_label, 10); - m_detail_label->setTextInteractionFlags(Qt::TextSelectableByMouse | Qt::TextSelectableByKeyboard); - - QVBoxLayout* layout = new QVBoxLayout(this); - layout->addWidget(m_detail_label); - m_cmd_detail->setLayout(layout); - m_cmd_detail->setFixedSize(m_cmd_detail->sizeHint()); - m_cmd_detail->show(); - - connect(m_cmd_detail, &QDialog::finished, [this](int) - { - // Cleanup - std::exchange(m_cmd_detail, nullptr)->deleteLater(); - }); -} - -void debugger_list::mouseDoubleClickEvent(QMouseEvent* event) -{ - if (event->button() == Qt::LeftButton) - { - int i = currentRow(); - if (i < 0) - return; - - u32 pc = m_start_addr; - - const auto cpu = m_disasm && !Emu.IsStopped() ? m_disasm->get_cpu() : nullptr; - - for (; cpu && cpu->get_class() == thread_class::rsx && i; i--) - { - // If scrolling forwards (downwards), we can skip entire commands - pc += std::max(m_disasm->disasm(pc), 4); - } - - pc += i * 4; - m_selected_instruction = pc; - - // Let debugger_frame know about breakpoint. - // Other option is to add to breakpoint manager directly and have a signal there instead. - // Either the flow goes from debugger_list->breakpoint_manager->debugger_frame, or it goes debugger_list->debugger_frame, and I felt this was easier to read for now. - Q_EMIT BreakpointRequested(pc); - } -} - -void debugger_list::wheelEvent(QWheelEvent* event) -{ - const QPoint numSteps = event->angleDelta() / 8 / 15; // http://doc.qt.io/qt-5/qwheelevent.html#pixelDelta - const int value = numSteps.y(); - const auto direction = (event->modifiers() == Qt::ControlModifier); - - scroll(direction ? value : -value); -} - -void debugger_list::resizeEvent(QResizeEvent* event) -{ - QListWidget::resizeEvent(event); - - if (count() < 1 || visualItemRect(item(0)).height() < 1) - { - return; - } - - const u32 old_size = m_item_count; - - // It is fine if the QWidgetList is a tad bit larger than the frame - m_item_count = utils::aligned_div(rect().height() - frameWidth() * 2, visualItemRect(item(0)).height()); - - if (old_size <= m_item_count) - { - for (u32 i = old_size; i < m_item_count; ++i) - { - insertItem(i, new QListWidgetItem("")); - m_dirty_flag = true; - } - } - else - { - for (u32 i = old_size - 1; i >= m_item_count; --i) - { - delete takeItem(i); - } - } -} diff --git a/rpcs3qt-legacy/debugger_list.h b/rpcs3qt-legacy/debugger_list.h deleted file mode 100644 index d3589e013..000000000 --- a/rpcs3qt-legacy/debugger_list.h +++ /dev/null @@ -1,67 +0,0 @@ -#pragma once - -#include "util/types.hpp" - -#include - -#include - -class breakpoint_handler; -class CPUDisAsm; -class cpu_thread; -class gui_settings; -class QLabel; - -class debugger_list : public QListWidget -{ - Q_OBJECT - -public: - u32 m_pc = 0; - u32 m_start_addr = 0; - u32 m_item_count = 30; - u32 m_selected_instruction = -1; - bool m_follow_thread = true; // If true, follow the selected thread to wherever it goes in code - bool m_showing_selected_instruction = false; - bool m_dirty_flag = false; - QColor m_color_bp; - QColor m_color_pc; - QColor m_text_color_bp; - QColor m_text_color_pc; - -Q_SIGNALS: - void BreakpointRequested(u32 loc, bool only_add = false); - -public: - debugger_list(QWidget* parent, std::shared_ptr settings, breakpoint_handler* handler); - void UpdateCPUData(std::shared_ptr disasm); - void EnableThreadFollowing(bool enable = true); -public Q_SLOTS: - void ShowAddress(u32 addr, bool select_addr = true, bool direct = false); - void RefreshView(); - -protected: - void keyPressEvent(QKeyEvent* event) override; - void mouseDoubleClickEvent(QMouseEvent* event) override; - void wheelEvent(QWheelEvent* event) override; - void resizeEvent(QResizeEvent* event) override; - void showEvent(QShowEvent* event) override; - void hideEvent(QHideEvent* event) override; - void scroll(s32 steps); - void create_rsx_command_detail(u32 pc); - -private: - /** - * It really upsetted me I had to copy this code to make debugger_list/frame not circularly dependent. - */ - u32 GetStartAddress(u32 address); - bool IsSpu() const; - - std::shared_ptr m_gui_settings; - - breakpoint_handler* m_ppu_breakpoint_handler = nullptr; - cpu_thread* m_cpu = nullptr; - std::shared_ptr m_disasm; - QDialog* m_cmd_detail = nullptr; - QLabel* m_detail_label = nullptr; -}; diff --git a/rpcs3qt-legacy/dimensions_dialog.cpp b/rpcs3qt-legacy/dimensions_dialog.cpp deleted file mode 100644 index f59483988..000000000 --- a/rpcs3qt-legacy/dimensions_dialog.cpp +++ /dev/null @@ -1,779 +0,0 @@ -#include "stdafx.h" -#include "util/File.h" -#include "dimensions_dialog.h" -#include "Emu/Io/Dimensions.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -dimensions_dialog* dimensions_dialog::inst = nullptr; -std::array, 7> figure_slots = {}; -static QString s_last_figure_path; - -LOG_CHANNEL(dimensions_log, "dimensions"); - -const std::map list_minifigs = { - {0, "Blank Tag"}, - {1, "Batman"}, - {2, "Gandalf"}, - {3, "Wyldstyle"}, - {4, "Aquaman"}, - {5, "Bad Cop"}, - {6, "Bane"}, - {7, "Bart Simpson"}, - {8, "Benny"}, - {9, "Chell"}, - {10, "Cole"}, - {11, "Cragger"}, - {12, "Cyborg"}, - {13, "Cyberman"}, - {14, "Doc Brown"}, - {15, "The Doctor"}, - {16, "Emmet"}, - {17, "Eris"}, - {18, "Gimli"}, - {19, "Gollum"}, - {20, "Harley Quinn"}, - {21, "Homer Simpson"}, - {22, "Jay"}, - {23, "Joker"}, - {24, "Kai"}, - {25, "ACU Trooper"}, - {26, "Gamer Kid"}, - {27, "Krusty the Clown"}, - {28, "Laval"}, - {29, "Legolas"}, - {30, "Lloyd"}, - {31, "Marty McFly"}, - {32, "Nya"}, - {33, "Owen Grady"}, - {34, "Peter Venkman"}, - {35, "Slimer"}, - {36, "Scooby-Doo"}, - {37, "Sensei Wu"}, - {38, "Shaggy"}, - {39, "Stay Puft"}, - {40, "Superman"}, - {41, "Unikitty"}, - {42, "Wicked Witch of the West"}, - {43, "Wonder Woman"}, - {44, "Zane"}, - {45, "Green Arrow"}, - {46, "Supergirl"}, - {47, "Abby Yates"}, - {48, "Finn the Human"}, - {49, "Ethan Hunt"}, - {50, "Lumpy Space Princess"}, - {51, "Jake the Dog"}, - {52, "Harry Potter"}, - {53, "Lord Voldemort"}, - {54, "Michael Knight"}, - {55, "B.A. Baracus"}, - {56, "Newt Scamander"}, - {57, "Sonic the Hedgehog"}, - {58, "Future Update (unreleased)"}, - {59, "Gizmo"}, - {60, "Stripe"}, - {61, "E.T."}, - {62, "Tina Goldstein"}, - {63, "Marceline the Vampire Queen"}, - {64, "Batgirl"}, - {65, "Robin"}, - {66, "Sloth"}, - {67, "Hermione Granger"}, - {68, "Chase McCain"}, - {69, "Excalibur Batman"}, - {70, "Raven"}, - {71, "Beast Boy"}, - {72, "Betelgeuse"}, - {73, "Lord Vortech (unreleased)"}, - {74, "Blossom"}, - {75, "Bubbles"}, - {76, "Buttercup"}, - {77, "Starfire"}, - {78, "World 15 (unreleased)"}, - {79, "World 16 (unreleased)"}, - {80, "World 17 (unreleased)"}, - {81, "World 18 (unreleased)"}, - {82, "World 19 (unreleased)"}, - {83, "World 20 (unreleased)"}, - {768, "Unknown 768"}, - {769, "Supergirl Red Lantern"}, - {770, "Unknown 770"}}; - -const std::map list_tokens = { - {1000, "Police Car"}, - {1001, "Aerial Squad Car"}, - {1002, "Missile Striker"}, - {1003, "Gravity Sprinter"}, - {1004, "Street Shredder"}, - {1005, "Sky Clobberer"}, - {1006, "Batmobile"}, - {1007, "Batblaster"}, - {1008, "Sonic Batray"}, - {1009, "Benny's Spaceship"}, - {1010, "Lasercraft"}, - {1011, "The Annihilator"}, - {1012, "DeLorean Time Machine"}, - {1013, "Electric Time Machine"}, - {1014, "Ultra Time Machine"}, - {1015, "Hoverboard"}, - {1016, "Cyclone Board"}, - {1017, "Ultimate Hoverjet"}, - {1018, "Eagle Interceptor"}, - {1019, "Eagle Sky Blazer"}, - {1020, "Eagle Swoop Diver"}, - {1021, "Swamp Skimmer"}, - {1022, "Cragger's Fireship"}, - {1023, "Croc Command Sub"}, - {1024, "Cyber-Guard"}, - {1025, "Cyber-Wrecker"}, - {1026, "Laser Robot Walker"}, - {1027, "K-9"}, - {1028, "K-9 Ruff Rover"}, - {1029, "K-9 Laser Cutter"}, - {1030, "TARDIS"}, - {1031, "Laser-Pulse TARDIS"}, - {1032, "Energy-Burst TARDIS"}, - {1033, "Emmet's Excavator"}, - {1034, "Destroy Dozer"}, - {1035, "Destruct-o-Mech"}, - {1036, "Winged Monkey"}, - {1037, "Battle Monkey"}, - {1038, "Commander Monkey"}, - {1039, "Axe Chariot"}, - {1040, "Axe Hurler"}, - {1041, "Soaring Chariot"}, - {1042, "Shelob the Great"}, - {1043, "8-Legged Stalker"}, - {1044, "Poison Slinger"}, - {1045, "Homer's Car"}, - {1047, "The SubmaHomer"}, - {1046, "The Homercraft"}, - {1048, "Taunt-o-Vision"}, - {1050, "The MechaHomer"}, - {1049, "Blast Cam"}, - {1051, "Velociraptor"}, - {1053, "Venom Raptor"}, - {1052, "Spike Attack Raptor"}, - {1054, "Gyrosphere"}, - {1055, "Sonic Beam Gyrosphere"}, - {1056, " Gyrosphere"}, - {1057, "Clown Bike"}, - {1058, "Cannon Bike"}, - {1059, "Anti-Gravity Rocket Bike"}, - {1060, "Mighty Lion Rider"}, - {1061, "Lion Blazer"}, - {1062, "Fire Lion"}, - {1063, "Arrow Launcher"}, - {1064, "Seeking Shooter"}, - {1065, "Triple Ballista"}, - {1066, "Mystery Machine"}, - {1067, "Mystery Tow & Go"}, - {1068, "Mystery Monster"}, - {1069, "Boulder Bomber"}, - {1070, "Boulder Blaster"}, - {1071, "Cyclone Jet"}, - {1072, "Storm Fighter"}, - {1073, "Lightning Jet"}, - {1074, "Electro-Shooter"}, - {1075, "Blade Bike"}, - {1076, "Flight Fire Bike"}, - {1077, "Blades of Fire"}, - {1078, "Samurai Mech"}, - {1079, "Samurai Shooter"}, - {1080, "Soaring Samurai Mech"}, - {1081, "Companion Cube"}, - {1082, "Laser Deflector"}, - {1083, "Gold Heart Emitter"}, - {1084, "Sentry Turret"}, - {1085, "Turret Striker"}, - {1086, "Flight Turret Carrier"}, - {1087, "Scooby Snack"}, - {1088, "Scooby Fire Snack"}, - {1089, "Scooby Ghost Snack"}, - {1090, "Cloud Cuckoo Car"}, - {1091, "X-Stream Soaker"}, - {1092, "Rainbow Cannon"}, - {1093, "Invisible Jet"}, - {1094, "Laser Shooter"}, - {1095, "Torpedo Bomber"}, - {1096, "NinjaCopter"}, - {1097, "Glaciator"}, - {1098, "Freeze Fighter"}, - {1099, "Travelling Time Train"}, - {1100, "Flight Time Train"}, - {1101, "Missile Blast Time Train"}, - {1102, "Aqua Watercraft"}, - {1103, "Seven Seas Speeder"}, - {1104, "Trident of Fire"}, - {1105, "Drill Driver"}, - {1106, "Bane Dig 'n' Drill"}, - {1107, "Bane Drill 'n' Blast"}, - {1108, "Quinn Mobile"}, - {1109, "Quinn Ultra Racer"}, - {1110, "Missile Launcher"}, - {1111, "The Joker's Chopper"}, - {1112, "Mischievous Missile Blaster"}, - {1113, "Lock 'n' Laser Jet"}, - {1114, "Hover Pod"}, - {1115, "Krypton Striker"}, - {1116, "Super Stealth Pod"}, - {1117, "Dalek"}, - {1118, "Fire 'n' Ride Dalek"}, - {1119, "Silver Shooter Dalek"}, - {1120, "Ecto-1"}, - {1121, "Ecto-1 Blaster"}, - {1122, "Ecto-1 Water Diver"}, - {1123, "Ghost Trap"}, - {1124, "Ghost Stun 'n' Trap"}, - {1125, "Proton Zapper"}, - {1126, "Unknown"}, - {1127, "Unknown"}, - {1128, "Unknown"}, - {1129, "Unknown"}, - {1130, "Unknown"}, - {1131, "Unknown"}, - {1132, "Lloyd's Golden Dragon"}, - {1133, "Sword Projector Dragon"}, - {1134, "Unknown"}, - {1135, "Unknown"}, - {1136, "Unknown"}, - {1137, "Unknown"}, - {1138, "Unknown"}, - {1139, "Unknown"}, - {1140, "Unknown"}, - {1141, "Unknown"}, - {1142, "Unknown"}, - {1143, "Unknown"}, - {1144, "Mega Flight Dragon"}, - {1145, "Unknown"}, - {1146, "Unknown"}, - {1147, "Unknown"}, - {1148, "Unknown"}, - {1149, "Unknown"}, - {1150, "Unknown"}, - {1151, "Unknown"}, - {1152, "Unknown"}, - {1153, "Unknown"}, - {1154, "Unknown"}, - {1155, "Flying White Dragon"}, - {1156, "Golden Fire Dragon"}, - {1157, "Ultra Destruction Dragon"}, - {1158, "Arcade Machine"}, - {1159, "8-Bit Shooter"}, - {1160, "The Pixelator"}, - {1161, "G-6155 Spy Hunter"}, - {1162, "Interdiver"}, - {1163, "Aerial Spyhunter"}, - {1164, "Slime Shooter"}, - {1165, "Slime Exploder"}, - {1166, "Slime Streamer"}, - {1167, "Terror Dog"}, - {1168, "Terror Dog Destroyer"}, - {1169, "Soaring Terror Dog"}, - {1170, "Ancient Psychic Tandem War Elephant"}, - {1171, "Cosmic Squid"}, - {1172, "Psychic Submarine"}, - {1173, "BMO"}, - {1174, "DOGMO"}, - {1175, "SNAKEMO"}, - {1176, "Jakemobile"}, - {1177, "Snail Dude Jake"}, - {1178, "Hover Jake"}, - {1179, "Lumpy Car"}, - {1181, "Lumpy Land Whale"}, - {1180, "Lumpy Truck"}, - {1182, "Lunatic Amp"}, - {1183, "Shadow Scorpion"}, - {1184, "Heavy Metal Monster"}, - {1185, "B.A.'s Van"}, - {1186, "Fool Smasher"}, - {1187, "Pain Plane"}, - {1188, "Phone Home"}, - {1189, "Mobile Uplink"}, - {1190, "Super-Charged Satellite"}, - {1191, "Niffler"}, - {1192, "Sinister Scorpion"}, - {1193, "Vicious Vulture"}, - {1194, "Swooping Evil"}, - {1195, "Brutal Bloom"}, - {1196, "Crawling Creeper"}, - {1197, "Ecto-1 (2016)"}, - {1198, "Ectozer"}, - {1199, "PerfEcto"}, - {1200, "Flash 'n' Finish"}, - {1201, "Rampage Record Player"}, - {1202, "Stripe's Throne"}, - {1203, "R.C. Racer"}, - {1204, "Gadget-O-Matic"}, - {1205, "Scarlet Scorpion"}, - {1206, "Hogwarts Express"}, - {1208, "Steam Warrior"}, - {1207, "Soaring Steam Plane"}, - {1209, "Enchanted Car"}, - {1210, "Shark Sub"}, - {1211, "Monstrous Mouth"}, - {1212, "IMF Scrambler"}, - {1213, "Shock Cycle"}, - {1214, "IMF Covert Jet"}, - {1215, "IMF Sports Car"}, - {1216, "IMF Tank"}, - {1217, "IMF Splorer"}, - {1218, "Sonic Speedster"}, - {1219, "Blue Typhoon"}, - {1220, "Moto Bug"}, - {1221, "The Tornado"}, - {1222, "Crabmeat"}, - {1223, "Eggcatcher"}, - {1224, "K.I.T.T."}, - {1225, "Goliath Armored Semi"}, - {1226, "K.I.T.T. Jet"}, - {1227, "Police Helicopter"}, - {1228, "Police Hovercraft"}, - {1229, "Police Plane"}, - {1230, "Bionic Steed"}, - {1231, "Bat-Raptor"}, - {1232, "Ultrabat"}, - {1233, "Batwing"}, - {1234, "The Black Thunder"}, - {1235, "Bat-Tank"}, - {1236, "Skeleton Organ"}, - {1237, "Skeleton Jukebox"}, - {1238, "Skele-Turkey"}, - {1239, "One-Eyed Willy's Pirate Ship"}, - {1240, "Fanged Fortune"}, - {1241, "Inferno Cannon"}, - {1242, "Buckbeak"}, - {1243, "Giant Owl"}, - {1244, "Fierce Falcon"}, - {1245, "Saturn's Sandworm"}, - {1247, "Haunted Vacuum"}, - {1246, "Spooky Spider"}, - {1248, "PPG Smartphone"}, - {1249, "PPG Hotline"}, - {1250, "Powerpuff Mag-Net"}, - {1253, "Mega Blast Bot"}, - {1251, "Ka-Pow Cannon"}, - {1252, "Slammin' Guitar"}, - {1254, "Octi"}, - {1255, "Super Skunk"}, - {1256, "Sonic Squid"}, - {1257, "T-Car"}, - {1258, "T-Forklift"}, - {1259, "T-Plane"}, - {1260, "Spellbook of Azarath"}, - {1261, "Raven Wings"}, - {1262, "Giant Hand"}, - {1263, "Titan Robot"}, - {1264, "T-Rocket"}, - {1265, "Robot Retriever"}}; - -minifig_creator_dialog::minifig_creator_dialog(QWidget* parent) - : QDialog(parent) -{ - setWindowTitle(tr("Figure Creator")); - setObjectName("figure_creator"); - setMinimumSize(QSize(500, 150)); - - QVBoxLayout* vbox_panel = new QVBoxLayout(); - - QComboBox* combo_figlist = new QComboBox(); - QStringList filterlist; - - for (const auto& [figure, figure_name] : list_minifigs) - { - QString name = QString::fromStdString(figure_name); - combo_figlist->addItem(name, QVariant(figure)); - filterlist << std::move(name); - } - - combo_figlist->addItem(tr("--Unknown--"), QVariant(0xFFFF)); - combo_figlist->setEditable(true); - combo_figlist->setInsertPolicy(QComboBox::NoInsert); - combo_figlist->model()->sort(0, Qt::AscendingOrder); - - QCompleter* co_compl = new QCompleter(filterlist, this); - co_compl->setCaseSensitivity(Qt::CaseInsensitive); - co_compl->setCompletionMode(QCompleter::PopupCompletion); - co_compl->setFilterMode(Qt::MatchContains); - combo_figlist->setCompleter(co_compl); - - vbox_panel->addWidget(combo_figlist); - - QFrame* line = new QFrame(); - line->setFrameShape(QFrame::HLine); - line->setFrameShadow(QFrame::Sunken); - vbox_panel->addWidget(line); - - QHBoxLayout* hbox_number = new QHBoxLayout(); - QLabel* label_number = new QLabel(tr("Figure Number:")); - QLineEdit* edit_number = new QLineEdit(QString::fromStdString(std::to_string(0))); - QRegularExpressionValidator* rxv = new QRegularExpressionValidator(QRegularExpression("\\d*"), this); - edit_number->setValidator(rxv); - hbox_number->addWidget(label_number); - hbox_number->addWidget(edit_number); - vbox_panel->addLayout(hbox_number); - - QHBoxLayout* hbox_buttons = new QHBoxLayout(); - QPushButton* btn_create = new QPushButton(tr("Create"), this); - QPushButton* btn_cancel = new QPushButton(tr("Cancel"), this); - hbox_buttons->addStretch(); - hbox_buttons->addWidget(btn_create); - hbox_buttons->addWidget(btn_cancel); - vbox_panel->addLayout(hbox_buttons); - - setLayout(vbox_panel); - - connect(combo_figlist, QOverload::of(&QComboBox::currentIndexChanged), [=](int index) - { - const u16 fig_info = combo_figlist->itemData(index).toUInt(); - if (fig_info != 0xFFFF) - { - edit_number->setText(QString::number(fig_info)); - } - }); - - connect(btn_create, &QAbstractButton::clicked, this, [=, this]() - { - bool ok_num = false; - const u16 fig_num = edit_number->text().toUInt(&ok_num) & 0xFFFF; - if (!ok_num) - { - QMessageBox::warning(this, tr("Error converting value"), tr("Figure number entered is invalid!"), QMessageBox::Ok); - return; - } - const auto found_figure = list_minifigs.find(fig_num); - if (found_figure != list_minifigs.cend()) - { - s_last_figure_path += QString::fromStdString(found_figure->second + ".bin"); - } - else - { - s_last_figure_path += QString("Unknown(%1).bin").arg(fig_num); - } - - m_file_path = QFileDialog::getSaveFileName(this, tr("Create Figure File"), s_last_figure_path, tr("Dimensions Figure (*.bin);;")); - if (m_file_path.isEmpty()) - { - return; - } - - fs::file dim_file(m_file_path.toStdString(), fs::read + fs::write + fs::create); - if (!dim_file) - { - QMessageBox::warning(this, tr("Failed to create minifig file!"), tr("Failed to create minifig file:\n%1").arg(m_file_path), QMessageBox::Ok); - return; - } - - std::array file_data{}; - g_dimensionstoypad.create_blank_character(file_data, fig_num); - - dim_file.write(file_data.data(), file_data.size()); - dim_file.close(); - - s_last_figure_path = QFileInfo(m_file_path).absolutePath() + "/"; - accept(); - }); - - connect(btn_cancel, &QAbstractButton::clicked, this, &QDialog::reject); - - connect(co_compl, QOverload::of(&QCompleter::activated), [=](const QString& text) - { - combo_figlist->setCurrentIndex(combo_figlist->findText(text)); - }); -} - -QString minifig_creator_dialog::get_file_path() const -{ - return m_file_path; -} - -minifig_move_dialog::minifig_move_dialog(QWidget* parent, u8 old_index) - : QDialog(parent) -{ - setWindowTitle(tr("Figure Mover")); - setObjectName("figure_mover"); - setMinimumSize(QSize(500, 150)); - - auto* grid_panel = new QGridLayout(); - - add_minifig_position(grid_panel, 0, 0, 0, old_index); - grid_panel->addWidget(new QLabel(tr("")), 0, 1); - add_minifig_position(grid_panel, 1, 0, 2, old_index); - grid_panel->addWidget(new QLabel(tr(""), this), 0, 3); - add_minifig_position(grid_panel, 2, 0, 4, old_index); - - add_minifig_position(grid_panel, 3, 1, 0, old_index); - add_minifig_position(grid_panel, 4, 1, 1, old_index); - grid_panel->addWidget(new QLabel(tr("")), 1, 2); - add_minifig_position(grid_panel, 5, 1, 3, old_index); - add_minifig_position(grid_panel, 6, 1, 4, old_index); - - setLayout(grid_panel); -} - -void minifig_move_dialog::add_minifig_position(QGridLayout* grid_panel, u8 index, u8 row, u8 column, u8 old_index) -{ - ensure(index < figure_slots.size()); - - auto* vbox_panel = new QVBoxLayout(); - - if (figure_slots[index]) - { - const auto found_figure = list_minifigs.find(figure_slots[index].value()); - if (found_figure != list_minifigs.cend()) - { - vbox_panel->addWidget(new QLabel(tr(found_figure->second.c_str()))); - } - } - else - { - vbox_panel->addWidget(new QLabel(tr("None"))); - } - auto* btn_move = new QPushButton(tr("Move Here"), this); - if (old_index == index) - { - btn_move->setText(tr("Pick up and Place")); - } - vbox_panel->addWidget(btn_move); - connect(btn_move, &QAbstractButton::clicked, this, [this, index] - { - m_index = index; - m_pad = index == 1 ? 1 : - index == 0 || index == 3 || index == 4 ? 2 : - 3; - accept(); - }); - - grid_panel->addLayout(vbox_panel, row, column); -} - -u8 minifig_move_dialog::get_new_pad() const -{ - return m_pad; -} - -u8 minifig_move_dialog::get_new_index() const -{ - return m_index; -} - -dimensions_dialog::dimensions_dialog(QWidget* parent) - : QDialog(parent) -{ - setWindowTitle(tr("Dimensions Manager")); - setObjectName("dimensions_manager"); - setAttribute(Qt::WA_DeleteOnClose); - setMinimumSize(QSize(800, 200)); - - QVBoxLayout* vbox_panel = new QVBoxLayout(); - QVBoxLayout* vbox_group = new QVBoxLayout(); - QHBoxLayout* hbox_group_1 = new QHBoxLayout(); - QHBoxLayout* hbox_group_2 = new QHBoxLayout(); - - QGroupBox* group_figures = new QGroupBox(tr("Active Dimensions Figures:")); - - add_minifig_slot(hbox_group_1, 2, 0); - hbox_group_1->addSpacerItem(new QSpacerItem(50, 0, QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding)); - add_minifig_slot(hbox_group_1, 1, 1); - hbox_group_1->addSpacerItem(new QSpacerItem(50, 0, QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding)); - add_minifig_slot(hbox_group_1, 3, 2); - - add_minifig_slot(hbox_group_2, 2, 3); - add_minifig_slot(hbox_group_2, 2, 4); - hbox_group_2->addSpacerItem(new QSpacerItem(50, 0, QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding)); - add_minifig_slot(hbox_group_2, 3, 5); - add_minifig_slot(hbox_group_2, 3, 6); - - vbox_group->addLayout(hbox_group_1); - vbox_group->addSpacerItem(new QSpacerItem(0, 20, QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding)); - vbox_group->addLayout(hbox_group_2); - group_figures->setLayout(vbox_group); - vbox_panel->addWidget(group_figures); - setLayout(vbox_panel); -} - -dimensions_dialog::~dimensions_dialog() -{ - inst = nullptr; -} - -dimensions_dialog* dimensions_dialog::get_dlg(QWidget* parent) -{ - if (inst == nullptr) - inst = new dimensions_dialog(parent); - - return inst; -} - -void dimensions_dialog::add_minifig_slot(QHBoxLayout* layout, u8 pad, u8 index) -{ - ensure(index < figure_slots.size()); - - QVBoxLayout* vbox_layout = new QVBoxLayout(); - - QHBoxLayout* hbox_name_move = new QHBoxLayout(); - QHBoxLayout* hbox_actions = new QHBoxLayout(); - - QPushButton* clear_btn = new QPushButton(tr("Clear")); - QPushButton* create_btn = new QPushButton(tr("Create")); - QPushButton* load_btn = new QPushButton(tr("Load")); - QPushButton* move_btn = new QPushButton(tr("Move")); - - m_edit_figures[index] = new QLineEdit(); - m_edit_figures[index]->setEnabled(false); - if (figure_slots[index]) - { - const auto found_figure = list_minifigs.find(figure_slots[index].value()); - if (found_figure != list_minifigs.cend()) - { - m_edit_figures[index]->setText(QString::fromStdString(found_figure->second)); - } - else - { - m_edit_figures[index]->setText(tr("Unknown Figure")); - } - } - else - { - m_edit_figures[index]->setText(tr("None")); - } - - connect(clear_btn, &QAbstractButton::clicked, this, [this, pad, index] - { - clear_figure(pad, index); - }); - connect(create_btn, &QAbstractButton::clicked, this, [this, pad, index] - { - create_figure(pad, index); - }); - connect(load_btn, &QAbstractButton::clicked, this, [this, pad, index] - { - load_figure(pad, index); - }); - connect(move_btn, &QAbstractButton::clicked, this, [this, pad, index] - { - if (figure_slots[index]) - { - move_figure(pad, index); - } - }); - - hbox_name_move->addWidget(m_edit_figures[index]); - hbox_name_move->addWidget(move_btn); - hbox_actions->addWidget(clear_btn); - hbox_actions->addWidget(create_btn); - hbox_actions->addWidget(load_btn); - - vbox_layout->addLayout(hbox_name_move); - vbox_layout->addLayout(hbox_actions); - - layout->addLayout(vbox_layout); -} - -void dimensions_dialog::clear_figure(u8 pad, u8 index) -{ - ensure(index < figure_slots.size()); - - if (figure_slots[index]) - { - g_dimensionstoypad.remove_figure(pad, index, true, true); - figure_slots[index] = std::nullopt; - m_edit_figures[index]->setText(tr("None")); - } -} - -void dimensions_dialog::create_figure(u8 pad, u8 index) -{ - ensure(index < figure_slots.size()); - minifig_creator_dialog create_dlg(this); - if (create_dlg.exec() == Accepted) - { - load_figure_path(pad, index, create_dlg.get_file_path()); - } -} - -void dimensions_dialog::load_figure(u8 pad, u8 index) -{ - ensure(index < figure_slots.size()); - const QString file_path = QFileDialog::getOpenFileName(this, tr("Select Dimensions File"), s_last_figure_path, tr("Dimensions Figure (*.bin);;")); - if (file_path.isEmpty()) - { - return; - } - - s_last_figure_path = QFileInfo(file_path).absolutePath() + "/"; - - load_figure_path(pad, index, file_path); -} - -void dimensions_dialog::move_figure(u8 pad, u8 index) -{ - ensure(index < figure_slots.size()); - minifig_move_dialog move_dlg(this, index); - g_dimensionstoypad.temp_remove(index); - if (move_dlg.exec() == Accepted) - { - g_dimensionstoypad.move_figure(move_dlg.get_new_pad(), move_dlg.get_new_index(), pad, index); - if (index != move_dlg.get_new_index()) - { - figure_slots[move_dlg.get_new_index()] = figure_slots[index]; - m_edit_figures[move_dlg.get_new_index()]->setText(m_edit_figures[index]->text()); - figure_slots[index] = std::nullopt; - m_edit_figures[index]->setText(tr("None")); - } - } - else - { - g_dimensionstoypad.cancel_remove(index); - } -} - -void dimensions_dialog::load_figure_path(u8 pad, u8 index, const QString& path) -{ - fs::file dim_file(path.toStdString(), fs::read + fs::write + fs::lock); - if (!dim_file) - { - QMessageBox::warning(this, tr("Failed to open the figure file!"), tr("Failed to open the figure file(%1)!\nFile may already be in use on the base.").arg(path), QMessageBox::Ok); - return; - } - - std::array data; - if (dim_file.read(data.data(), data.size()) != data.size()) - { - QMessageBox::warning(this, tr("Failed to read the figure file!"), tr("Failed to read the figure file(%1)!\nFile was too small.").arg(path), QMessageBox::Ok); - return; - } - - clear_figure(pad, index); - - const u32 fig_num = g_dimensionstoypad.load_figure(data, std::move(dim_file), pad, index, true); - - figure_slots[index] = fig_num; - const auto name = list_minifigs.find(fig_num); - if (name != list_minifigs.cend()) - { - m_edit_figures[index]->setText(QString::fromStdString(name->second)); - } - else - { - const auto blank_name = list_tokens.find(fig_num); - if (blank_name != list_tokens.cend()) - { - m_edit_figures[index]->setText(QString::fromStdString(blank_name->second)); - } - else - { - m_edit_figures[index]->setText("Blank Tag"); - } - } -} diff --git a/rpcs3qt-legacy/dimensions_dialog.h b/rpcs3qt-legacy/dimensions_dialog.h deleted file mode 100644 index ceaeaef31..000000000 --- a/rpcs3qt-legacy/dimensions_dialog.h +++ /dev/null @@ -1,63 +0,0 @@ -#pragma once - -#include "util/types.hpp" - -#include -#include -#include - -class minifig_creator_dialog : public QDialog -{ - Q_OBJECT - -public: - explicit minifig_creator_dialog(QWidget* parent); - QString get_file_path() const; - -protected: - QString m_file_path; -}; - -class minifig_move_dialog : public QDialog -{ - Q_OBJECT - -public: - explicit minifig_move_dialog(QWidget* parent, u8 old_index); - u8 get_new_pad() const; - u8 get_new_index() const; - -protected: - u8 m_pad = 0; - u8 m_index = 0; - -private: - void add_minifig_position(QGridLayout* grid_panel, u8 index, u8 row, u8 column, u8 old_index); -}; - -class dimensions_dialog : public QDialog -{ - Q_OBJECT - -public: - explicit dimensions_dialog(QWidget* parent); - ~dimensions_dialog(); - static dimensions_dialog* get_dlg(QWidget* parent); - - dimensions_dialog(dimensions_dialog const&) = delete; - void operator=(dimensions_dialog const&) = delete; - -protected: - void clear_figure(u8 pad, u8 index); - void create_figure(u8 pad, u8 index); - void load_figure(u8 pad, u8 index); - void move_figure(u8 pad, u8 index); - void load_figure_path(u8 pad, u8 index, const QString& path); - -protected: - std::array m_edit_figures{}; - -private: - void add_minifig_slot(QHBoxLayout* layout, u8 pad, u8 index); - static dimensions_dialog* inst; -}; diff --git a/rpcs3qt-legacy/display_sleep_control.cpp b/rpcs3qt-legacy/display_sleep_control.cpp deleted file mode 100644 index 4716be20e..000000000 --- a/rpcs3qt-legacy/display_sleep_control.cpp +++ /dev/null @@ -1,96 +0,0 @@ -#include "display_sleep_control.h" - -#ifdef _WIN32 -#include - -#elif defined(__APPLE__) -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wold-style-cast" -#include -#pragma GCC diagnostic pop - -static IOPMAssertionID s_pm_assertion = kIOPMNullAssertionID; - -#elif defined(HAVE_QTDBUS) -#include -#include -#include -#include -#include "util/types.hpp" -static u32 s_dbus_cookie = 0; -#endif - -bool display_sleep_control_supported() -{ -#if defined(_WIN32) || defined(__APPLE__) - return true; -#elif defined(HAVE_QTDBUS) - for (const char* service : {"org.freedesktop.ScreenSaver", "org.mate.ScreenSaver"}) - { - QDBusInterface interface(service, "/ScreenSaver", service, QDBusConnection::sessionBus()); - if (interface.isValid()) - { - return true; - } - } - return false; -#else - return false; -#endif -} - -void enable_display_sleep(bool enabled) -{ - if (!display_sleep_control_supported()) - { - return; - } - -#ifdef _WIN32 - SetThreadExecutionState(enabled ? ES_CONTINUOUS : (ES_CONTINUOUS | ES_SYSTEM_REQUIRED | ES_DISPLAY_REQUIRED)); -#elif defined(__APPLE__) - if (enabled && s_pm_assertion != kIOPMNullAssertionID) - { - IOPMAssertionRelease(s_pm_assertion); - s_pm_assertion = kIOPMNullAssertionID; - } - else if (!enabled) - { -#pragma GCC diagnostic push -// Necessary as some of those values are macro using old casts -#pragma GCC diagnostic ignored "-Wold-style-cast" - IOPMAssertionCreateWithName(kIOPMAssertionTypePreventUserIdleDisplaySleep, kIOPMAssertionLevelOn, CFSTR("Game running"), &s_pm_assertion); -#pragma GCC diagnostic pop - } -#elif defined(HAVE_QTDBUS) - if (enabled && s_dbus_cookie != 0) - { - for (const char* service : {"org.freedesktop.ScreenSaver", "org.mate.ScreenSaver"}) - { - QDBusInterface interface(service, "/ScreenSaver", service, QDBusConnection::sessionBus()); - if (interface.isValid()) - { - interface.call("UnInhibit", s_dbus_cookie); - break; - } - } - s_dbus_cookie = 0; - } - else if (!enabled) - { - for (const char* service : {"org.freedesktop.ScreenSaver", "org.mate.ScreenSaver"}) - { - QDBusInterface interface(service, "/ScreenSaver", service, QDBusConnection::sessionBus()); - if (interface.isValid()) - { - QDBusReply reply = interface.call("Inhibit", "rpcs3", "Game running"); - if (reply.isValid()) - { - s_dbus_cookie = reply.value(); - } - break; - } - } - } -#endif -} diff --git a/rpcs3qt-legacy/display_sleep_control.h b/rpcs3qt-legacy/display_sleep_control.h deleted file mode 100644 index 66d246a46..000000000 --- a/rpcs3qt-legacy/display_sleep_control.h +++ /dev/null @@ -1,4 +0,0 @@ -#pragma once - -bool display_sleep_control_supported(); -void enable_display_sleep(bool enabled); diff --git a/rpcs3qt-legacy/downloader.cpp b/rpcs3qt-legacy/downloader.cpp deleted file mode 100644 index 999a78e08..000000000 --- a/rpcs3qt-legacy/downloader.cpp +++ /dev/null @@ -1,204 +0,0 @@ -#include -#include - -#include "downloader.h" -#include "curl_handle.h" -#include "progress_dialog.h" - -#include "util/logs.hpp" - -LOG_CHANNEL(network_log, "NET"); - -usz curl_write_cb_compat(char* ptr, usz /*size*/, usz nmemb, void* userdata) -{ - downloader* download = static_cast(userdata); - return download->update_buffer(ptr, nmemb); -} - -downloader::downloader(QWidget* parent) - : QObject(parent), m_parent(parent), m_curl(new rpcs3::curl::curl_handle()) -{ -} - -downloader::~downloader() -{ - if (m_thread && m_thread->isRunning()) - { - m_curl_abort = true; - m_thread->wait(); - } -} - -void downloader::start(const std::string& url, bool follow_location, bool show_progress_dialog, const QString& progress_dialog_title, bool keep_progress_dialog_open, int expected_size) -{ - network_log.notice("Starting download from URL: %s", url); - - if (m_thread) - { - if (m_thread->isRunning()) - { - m_curl_abort = true; - m_thread->wait(); - } - m_thread->deleteLater(); - } - - m_keep_progress_dialog_open = keep_progress_dialog_open; - m_curl_buf.clear(); - m_curl_abort = false; - - CURLcode err = curl_easy_setopt(m_curl->get_curl(), CURLOPT_URL, url.c_str()); - if (err != CURLE_OK) - network_log.error("curl_easy_setopt(CURLOPT_URL, %s) error: %s", url, curl_easy_strerror(err)); - - err = curl_easy_setopt(m_curl->get_curl(), CURLOPT_WRITEFUNCTION, curl_write_cb_compat); - if (err != CURLE_OK) - network_log.error("curl_easy_setopt(CURLOPT_WRITEFUNCTION, curl_write_cb_compat) error: %s", curl_easy_strerror(err)); - - err = curl_easy_setopt(m_curl->get_curl(), CURLOPT_WRITEDATA, this); - if (err != CURLE_OK) - network_log.error("curl_easy_setopt(CURLOPT_WRITEDATA) error: %s", curl_easy_strerror(err)); - - err = curl_easy_setopt(m_curl->get_curl(), CURLOPT_FOLLOWLOCATION, follow_location ? 1 : 0); - if (err != CURLE_OK) - network_log.error("curl_easy_setopt(CURLOPT_FOLLOWLOCATION, %d) error: %s", follow_location, curl_easy_strerror(err)); - - m_thread = QThread::create([this] - { - // Reset error buffer before we call curl_easy_perform - m_curl->reset_error_buffer(); - - const CURLcode result = curl_easy_perform(m_curl->get_curl()); - m_curl_success = result == CURLE_OK; - - if (!m_curl_success && !m_curl_abort) - { - const std::string error = fmt::format("curl_easy_perform(): %s", m_curl->get_verbose_error(result)); - network_log.error("%s", error); - Q_EMIT signal_download_error(QString::fromStdString(error)); - } - }); - - connect(m_thread, &QThread::finished, this, [this]() - { - if (m_curl_abort) - { - network_log.notice("Download aborted"); - return; - } - - if (!m_keep_progress_dialog_open || !m_curl_success) - { - close_progress_dialog(); - } - - if (m_curl_success) - { - network_log.notice("Download finished"); - Q_EMIT signal_download_finished(m_curl_buf); - } - }); - - // The downloader's signals are expected to be disconnected and customized before start is called. - // Therefore we need to (re)connect its signal(s) here and not in the constructor. - connect(this, &downloader::signal_buffer_update, this, &downloader::handle_buffer_update); - - if (show_progress_dialog) - { - const int maximum = expected_size > 0 ? expected_size : 100; - - if (m_progress_dialog) - { - m_progress_dialog->setWindowTitle(progress_dialog_title); - m_progress_dialog->setAutoClose(!m_keep_progress_dialog_open); - m_progress_dialog->SetRange(0, maximum); - } - else - { - m_progress_dialog = new progress_dialog(progress_dialog_title, tr("Please wait..."), tr("Abort"), 0, maximum, true, m_parent); - m_progress_dialog->setAutoReset(false); - m_progress_dialog->setAutoClose(!m_keep_progress_dialog_open); - m_progress_dialog->show(); - - // Handle abort - connect(m_progress_dialog, &QProgressDialog::canceled, this, [this]() - { - m_curl_abort = true; - m_progress_dialog = nullptr; // The progress dialog deletes itself on close - Q_EMIT signal_download_canceled(); - }); - connect(m_progress_dialog, &QProgressDialog::finished, this, [this]() - { - m_progress_dialog = nullptr; // The progress dialog deletes itself on close - }); - } - } - - m_thread->setObjectName("Download Thread"); - m_thread->setParent(this); - m_thread->start(); -} - -void downloader::update_progress_dialog(const QString& title) const -{ - if (m_progress_dialog) - { - m_progress_dialog->setWindowTitle(title); - } -} - -void downloader::close_progress_dialog() -{ - if (m_progress_dialog) - { - m_progress_dialog->accept(); - m_progress_dialog = nullptr; - } -} - -progress_dialog* downloader::get_progress_dialog() const -{ - return m_progress_dialog; -} - -usz downloader::update_buffer(char* data, usz size) -{ - if (m_curl_abort) - { - return 0; - } - - const auto old_size = m_curl_buf.size(); - const auto new_size = old_size + size; - m_curl_buf.resize(static_cast(new_size)); - memcpy(m_curl_buf.data() + old_size, data, size); - - int max = 0; - - if (m_actual_download_size < 0) - { - if (curl_easy_getinfo(m_curl->get_curl(), CURLINFO_CONTENT_LENGTH_DOWNLOAD_T, &m_actual_download_size) == CURLE_OK && m_actual_download_size > 0) - { - max = static_cast(m_actual_download_size); - } - } - - Q_EMIT signal_buffer_update(static_cast(new_size), max); - - return size; -} - -void downloader::handle_buffer_update(int size, int max) const -{ - if (m_curl_abort) - { - return; - } - - if (m_progress_dialog) - { - m_progress_dialog->SetRange(0, max > 0 ? max : m_progress_dialog->maximum()); - m_progress_dialog->SetValue(size); - QApplication::processEvents(); - } -} diff --git a/rpcs3qt-legacy/downloader.h b/rpcs3qt-legacy/downloader.h deleted file mode 100644 index 5e96b3476..000000000 --- a/rpcs3qt-legacy/downloader.h +++ /dev/null @@ -1,55 +0,0 @@ -#pragma once - -#include -#include "util/atomic.hpp" - -namespace rpcs3 -{ - namespace curl - { - class curl_handle; - } -} // namespace rpcs3 - -class progress_dialog; - -class downloader : public QObject -{ - Q_OBJECT - -public: - explicit downloader(QWidget* parent = nullptr); - ~downloader(); - - void start(const std::string& url, bool follow_location, bool show_progress_dialog, const QString& progress_dialog_title = "", bool keep_progress_dialog_open = false, int expected_size = -1); - usz update_buffer(char* data, usz size); - - void update_progress_dialog(const QString& title) const; - void close_progress_dialog(); - - progress_dialog* get_progress_dialog() const; - -private Q_SLOTS: - void handle_buffer_update(int size, int max) const; - -Q_SIGNALS: - void signal_download_error(const QString& error); - void signal_download_finished(const QByteArray& data); - void signal_download_canceled(); - void signal_buffer_update(int size, int max); - -private: - QWidget* m_parent = nullptr; - - std::unique_ptr m_curl; - QByteArray m_curl_buf; - atomic_t m_curl_abort = false; - atomic_t m_curl_success = false; - double m_actual_download_size = -1.0; - - progress_dialog* m_progress_dialog = nullptr; - atomic_t m_keep_progress_dialog_open = false; - QString m_progress_dialog_title; - - QThread* m_thread = nullptr; -}; diff --git a/rpcs3qt-legacy/elf_memory_dumping_dialog.cpp b/rpcs3qt-legacy/elf_memory_dumping_dialog.cpp deleted file mode 100644 index f15cf5998..000000000 --- a/rpcs3qt-legacy/elf_memory_dumping_dialog.cpp +++ /dev/null @@ -1,222 +0,0 @@ -#include "elf_memory_dumping_dialog.h" -#include "Emu/Cell/SPUThread.h" - -#include "qt_utils.h" - -#include -#include -#include -#include -#include -#include -#include -#include - -LOG_CHANNEL(gui_log, "GUI"); - -Q_DECLARE_METATYPE(spu_memory_segment_dump_data); - -elf_memory_dumping_dialog::elf_memory_dumping_dialog(u32 ppu_debugger_addr, std::shared_ptr _gui_settings, QWidget* parent) - : QDialog(parent), m_gui_settings(std::move(_gui_settings)) -{ - setWindowTitle(tr("SPU ELF Dumper")); - setAttribute(Qt::WA_DeleteOnClose); - - m_seg_list = new QListWidget(); - - // Font - const int pSize = 10; - QFont mono = QFontDatabase::systemFont(QFontDatabase::FixedFont); - mono.setPointSize(pSize); - - m_seg_list->setMinimumWidth(gui::utils::get_label_width(tr("PPU Address: 0x00000000, LS Address: 0x00000, Segment Size: 0x00000, Flags: 0x0"))); - - // Address expression input - auto make_hex_edit = [this, mono](u32 max_digits) - { - QLineEdit* le = new QLineEdit(); - le->setFont(mono); - le->setMaxLength(max_digits + 2); - le->setPlaceholderText("0x" + QStringLiteral("0").repeated(max_digits)); - le->setValidator(new QRegularExpressionValidator(QRegularExpression(QStringLiteral("^(0[xX])?0*[a-fA-F0-9]{0,%1}$").arg(max_digits)), this)); - return le; - }; - - m_segment_size_input = make_hex_edit(5); - m_ppu_address_input = make_hex_edit(8); - m_ls_address_input = make_hex_edit(5); - m_segment_flags_input = make_hex_edit(1); - m_segment_flags_input->setText("0x7"); // READ WRITE EXEC - m_ppu_address_input->setText(QStringLiteral("0x%1").arg(ppu_debugger_addr & -0x10000, 1, 16)); // SPU code segments are usually 128 bytes aligned, let's make it even 64k so the user would have to type himself the lower part to avoid human errors. - - QPushButton* add_segment_button = new QPushButton(QStringLiteral("+")); - add_segment_button->setToolTip(tr("Add new segment")); - add_segment_button->setFixedWidth(add_segment_button->sizeHint().height()); // Make button square - connect(add_segment_button, &QAbstractButton::clicked, this, &elf_memory_dumping_dialog::add_new_segment); - - QPushButton* remove_segment_button = new QPushButton(QStringLiteral("-")); - remove_segment_button->setToolTip(tr("Remove segment")); - remove_segment_button->setFixedWidth(remove_segment_button->sizeHint().height()); // Make button square - remove_segment_button->setEnabled(false); - connect(remove_segment_button, &QAbstractButton::clicked, this, &elf_memory_dumping_dialog::remove_segment); - - QPushButton* save_to_file = new QPushButton(tr("Save To ELF")); - save_to_file->setToolTip(tr("Save To An ELF file")); - connect(save_to_file, &QAbstractButton::clicked, this, &elf_memory_dumping_dialog::save_to_file); - - QHBoxLayout* hbox_input = new QHBoxLayout; - hbox_input->addWidget(new QLabel(tr("Segment Size:"))); - hbox_input->addWidget(m_segment_size_input); - hbox_input->addSpacing(5); - - hbox_input->addWidget(new QLabel(tr("PPU Address:"))); - hbox_input->addWidget(m_ppu_address_input); - hbox_input->addSpacing(5); - hbox_input->addWidget(new QLabel(tr("LS Address:"))); - hbox_input->addWidget(m_ls_address_input); - hbox_input->addSpacing(5); - hbox_input->addWidget(new QLabel(tr("Flags:"))); - hbox_input->addWidget(m_segment_flags_input); - - QHBoxLayout* hbox_save_and_edit = new QHBoxLayout; - hbox_save_and_edit->addStretch(2); - hbox_save_and_edit->addWidget(add_segment_button); - hbox_save_and_edit->addSpacing(4); - hbox_save_and_edit->addWidget(remove_segment_button); - hbox_save_and_edit->addSpacing(4); - hbox_save_and_edit->addWidget(save_to_file); - - QVBoxLayout* vbox = new QVBoxLayout(); - vbox->addLayout(hbox_input); - vbox->addSpacing(5); - vbox->addWidget(m_seg_list); - vbox->addSpacing(5); - vbox->addLayout(hbox_save_and_edit); - - setLayout(vbox); - - connect(m_seg_list, &QListWidget::currentRowChanged, this, [this, remove_segment_button](int row) - { - remove_segment_button->setEnabled(row >= 0 && m_seg_list->item(row)); - }); - - show(); -} - -void elf_memory_dumping_dialog::add_new_segment() -{ - QStringList errors; - auto interpret = [&](QString text, QString error_field) -> u32 - { - bool ok = false; - - // Parse expression (or at least used to, was nuked to remove the need for QtJsEngine) - const QString fixed_expression = QRegularExpression(QRegularExpression::anchoredPattern("a .*|^[A-Fa-f0-9]+$")).match(text).hasMatch() ? "0x" + text : text; - const u32 res = static_cast(fixed_expression.toULong(&ok, 16)); - - if (!ok) - { - errors << error_field; - return umax; - } - - return res; - }; - - spu_memory_segment_dump_data data{}; - data.segment_size = interpret(m_segment_size_input->text(), tr("Segment Size")); - data.src_addr = vm::get_super_ptr(interpret(m_ppu_address_input->text(), tr("PPU Address"))); - data.ls_addr = interpret(m_ls_address_input->text(), tr("LS Address")); - data.flags = interpret(m_segment_flags_input->text(), tr("Segment Flags")); - - if (!errors.isEmpty()) - { - QMessageBox::warning(this, tr("Failed To Add Segment"), tr("Segment parameters are incorrect:\n%1").arg(errors.join('\n'))); - return; - } - - if (data.segment_size % 4) - { - QMessageBox::warning(this, tr("Failed To Add Segment"), tr("SPU segment size must be 4 bytes aligned.")); - return; - } - - if (data.segment_size + data.ls_addr > SPU_LS_SIZE || data.segment_size == 0 || data.segment_size % 4) - { - QMessageBox::warning(this, tr("Failed To Add Segment"), tr("SPU segment range is invalid.")); - return; - } - - if (!vm::check_addr(vm::try_get_addr(data.src_addr).first, vm::page_readable, data.segment_size)) - { - QMessageBox::warning(this, tr("Failed To Add Segment"), tr("PPU address range is not accessible.")); - return; - } - - for (int i = 0; i < m_seg_list->count(); ++i) - { - ensure(m_seg_list->item(i)->data(Qt::UserRole).canConvert()); - const auto seg_stored = m_seg_list->item(i)->data(Qt::UserRole).value(); - - const auto stored_max = seg_stored.src_addr + seg_stored.segment_size; - const auto data_max = data.src_addr + data.segment_size; - - if (seg_stored.src_addr < data_max && data.src_addr < stored_max) - { - QMessageBox::warning(this, tr("Failed To Add Segment"), tr("SPU segment overlaps with previous SPU segment(s)\n")); - return; - } - } - - auto item = new QListWidgetItem(tr("PPU Address: 0x%0, LS Address: 0x%1, Segment Size: 0x%2, Flags: 0x%3").arg(+vm::try_get_addr(data.src_addr).first, 5, 16).arg(data.ls_addr, 2, 16).arg(data.segment_size, 2, 16).arg(data.flags, 2, 16), m_seg_list); - item->setData(Qt::UserRole, QVariant::fromValue(data)); - m_seg_list->setCurrentItem(item); -} - -void elf_memory_dumping_dialog::remove_segment() -{ - const int row = m_seg_list->currentRow(); - if (row >= 0) - { - QListWidgetItem* item = m_seg_list->takeItem(row); - delete item; - } -} - -void elf_memory_dumping_dialog::save_to_file() -{ - std::vector segs; - segs.reserve(m_seg_list->count()); - - for (int i = 0; i < m_seg_list->count(); ++i) - { - ensure(m_seg_list->item(i)->data(Qt::UserRole).canConvert()); - const auto seg_stored = m_seg_list->item(i)->data(Qt::UserRole).value(); - segs.emplace_back(seg_stored); - } - - if (segs.empty()) - { - return; - } - - const QString path_last_elf = m_gui_settings->GetValue(gui::fd_save_elf).toString(); - - const QString qpath = QFileDialog::getSaveFileName(this, tr("Capture"), path_last_elf, "SPU ELF (*.elf)"); - const std::string path = qpath.toStdString(); - - if (!path.empty()) - { - const auto result = spu_thread::capture_memory_as_elf({segs.data(), segs.size()}).save(); - - if (!result.empty() && fs::write_file(path, fs::rewrite, result)) - { - gui_log.success("Saved ELF at %s", path); - m_gui_settings->SetValue(gui::fd_save_elf, qpath); - } - else - { - QMessageBox::warning(this, tr("Save Failure"), tr("Failed to save SPU ELF.")); - } - } -} diff --git a/rpcs3qt-legacy/elf_memory_dumping_dialog.h b/rpcs3qt-legacy/elf_memory_dumping_dialog.h deleted file mode 100644 index 3c6d7fe59..000000000 --- a/rpcs3qt-legacy/elf_memory_dumping_dialog.h +++ /dev/null @@ -1,33 +0,0 @@ -#pragma once - -#include "util/types.hpp" -#include "gui_settings.h" - -#include -#include -#include - -#include - -class elf_memory_dumping_dialog : public QDialog -{ - Q_OBJECT - -public: - explicit elf_memory_dumping_dialog(u32 ppu_debugger_pc, std::shared_ptr _gui_settings, QWidget* parent = nullptr); - -protected: - void add_new_segment(); - void remove_segment(); - void save_to_file(); - - std::shared_ptr m_gui_settings; - - // UI variables needed in higher scope - QListWidget* m_seg_list = nullptr; - - QLineEdit* m_ls_address_input = nullptr; - QLineEdit* m_segment_size_input = nullptr; - QLineEdit* m_ppu_address_input = nullptr; - QLineEdit* m_segment_flags_input = nullptr; -}; diff --git a/rpcs3qt-legacy/emu_settings.cpp b/rpcs3qt-legacy/emu_settings.cpp deleted file mode 100644 index 459262c4b..000000000 --- a/rpcs3qt-legacy/emu_settings.cpp +++ /dev/null @@ -1,1468 +0,0 @@ -#include "emu_settings.h" -#include "config_adapter.h" - -#include -#include -#include -#include - -#include "Emu/System.h" -#include "Emu/system_config.h" -#include "Emu/system_utils.hpp" -#include "rpcsx/fw/ps3/cellSysutil.h" -#include "Emu/Io/Keyboard.h" - -#include "util/yaml.hpp" -#include "util/File.h" -#include "util/Config.h" - -LOG_CHANNEL(cfg_log, "CFG"); - -extern std::string g_cfg_defaults; //! Default settings grabbed from util/Config.h - -// Emit sorted YAML -namespace -{ - static NEVER_INLINE void emit_data(YAML::Emitter& out, const YAML::Node& node) - { - // TODO - out << node; - } - - // Incrementally load YAML - static NEVER_INLINE void operator+=(YAML::Node& left, const YAML::Node& node) - { - if (node && !node.IsNull()) - { - if (node.IsMap()) - { - for (const auto& pair : node) - { - if (pair.first.IsScalar()) - { - auto&& lhs = left[pair.first.Scalar()]; - lhs += pair.second; - } - else - { - // Exotic case (TODO: probably doesn't work) - auto&& lhs = left[YAML::Clone(pair.first)]; - lhs += pair.second; - } - } - } - else if (node.IsScalar() || node.IsSequence()) - { - // Scalars and sequences are replaced completely, but this may change in future. - // This logic may be overwritten by custom demands of every specific cfg:: node. - left = node; - } - } - } -} // namespace - -emu_settings::emu_settings() - : QObject() -{ -} - -bool emu_settings::Init() -{ - m_render_creator = new render_creator(this); - - if (m_render_creator->abort_requested) - { - return false; - } - - // Make Vulkan default setting if it is supported - if (m_render_creator->Vulkan.supported && !m_render_creator->Vulkan.adapters.empty()) - { - const std::string adapter = ::at32(m_render_creator->Vulkan.adapters, 0).toStdString(); - cfg_log.notice("Setting the default renderer to Vulkan. Default GPU: '%s'", adapter); - Emu.SetDefaultRenderer(video_renderer::vulkan); - Emu.SetDefaultGraphicsAdapter(adapter); - } - - return true; -} - -void emu_settings::LoadSettings(const std::string& title_id, bool create_config_from_global) -{ - m_title_id = title_id; - - // Create config path if necessary - fs::create_path(title_id.empty() ? fs::get_config_dir(true) : rpcs3::utils::get_custom_config_dir()); - - // Load default config - auto [default_config, default_error] = yaml_load(g_cfg_defaults); - - if (default_error.empty()) - { - m_default_settings = default_config; - m_current_settings = YAML::Clone(default_config); - } - else - { - cfg_log.fatal("Failed to load default config:\n%s", default_error); - QMessageBox::critical(nullptr, tr("Config Error"), tr("Failed to load default config:\n%0").arg(QString::fromStdString(default_error)), QMessageBox::Ok); - } - - if (create_config_from_global) - { - // Add global config - const std::string global_config_path = fs::get_config_dir(true) + "config.yml"; - fs::g_tls_error = fs::error::ok; - fs::file config(global_config_path, fs::read + fs::create); - auto [global_config, global_error] = yaml_load(config ? config.to_string() : ""); - - if (config && global_error.empty()) - { - m_current_settings += global_config; - } - else - { - config.close(); - cfg_log.fatal("Failed to load global config %s:\n%s (%s)", global_config_path, global_error, fs::g_tls_error); - QMessageBox::critical(nullptr, tr("Config Error"), tr("Failed to load global config:\nFile: %0\nError: %1").arg(QString::fromStdString(global_config_path)).arg(QString::fromStdString(global_error)), QMessageBox::Ok); - } - } - - // Add game config - if (!title_id.empty()) - { - // Remove obsolete settings of the global config before adding the custom settings. - // Otherwise we'll always trigger the "obsolete settings dialog" when editing custom configs. - ValidateSettings(true); - - std::string custom_config_path; - - if (std::string config_path = rpcs3::utils::get_custom_config_path(m_title_id); fs::is_file(config_path)) - { - custom_config_path = std::move(config_path); - } - - if (!custom_config_path.empty()) - { - if (fs::file config{custom_config_path}) - { - auto [custom_config, custom_error] = yaml_load(config.to_string()); - config.close(); - - if (custom_error.empty()) - { - m_current_settings += custom_config; - } - else - { - cfg_log.fatal("Failed to load custom config %s:\n%s", custom_config_path, custom_error); - QMessageBox::critical(nullptr, tr("Config Error"), tr("Failed to load custom config:\nFile: %0\nError: %1").arg(QString::fromStdString(custom_config_path)).arg(QString::fromStdString(custom_error)), QMessageBox::Ok); - } - } - else if (fs::g_tls_error != fs::error::noent) - { - cfg_log.fatal("Failed to load custom config %s (file error: %s)", custom_config_path, fs::g_tls_error); - QMessageBox::critical(nullptr, tr("Config Error"), tr("Failed to load custom config:\nFile: %0\nError: %1").arg(QString::fromStdString(custom_config_path)).arg(QString::fromStdString(fmt::format("%s", fs::g_tls_error))), QMessageBox::Ok); - } - } - } -} - -bool emu_settings::ValidateSettings(bool cleanup) -{ - bool is_clean = true; - - std::function&, cfg::_base*)> search_level; - search_level = [&search_level, &is_clean, &cleanup, this](int level, YAML::Node& yml_node, std::vector& keys, cfg::_base* cfg_base) - { - if (!yml_node || !yml_node.IsMap()) - { - return; - } - - const int next_level = level + 1; - - for (const auto& yml_entry : yml_node) - { - const std::string& key = yml_entry.first.Scalar(); - cfg::_base* cfg_node = nullptr; - - keys.resize(next_level); - keys[level] = key; - - if (cfg_base && cfg_base->get_type() == cfg::type::node) - { - for (const auto& node : static_cast(cfg_base)->get_nodes()) - { - if (node->get_name() == keys[level]) - { - cfg_node = node; - break; - } - } - } - - if (cfg_node) - { - // Ignore every node in Log subsection - if (level == 0 && cfg_node->get_name() == "Log") - { - continue; - } - - YAML::Node next_node = yml_node[key]; - search_level(next_level, next_node, keys, cfg_node); - } - else - { - const auto get_full_key = [&keys](const std::string& separator) -> std::string - { - std::string full_key; - for (usz i = 0; i < keys.size(); i++) - { - full_key += keys[i]; - if (i < keys.size() - 1) - full_key += separator; - } - return full_key; - }; - - is_clean = false; - - if (cleanup) - { - if (!yml_node.remove(key)) - { - cfg_log.error("Could not remove config entry: %s", get_full_key(": ")); - is_clean = true; // abort - return; - } - - // Let's only remove one entry at a time. I got some weird issues when doing all at once. - return; - } - else - { - cfg_log.warning("Unknown config entry found: %s", get_full_key(": ")); - } - } - } - }; - - std::unique_ptr root = std::make_unique(); - std::vector keys; - - do - { - is_clean = true; - search_level(0, m_current_settings, keys, root.get()); - } while (cleanup && !is_clean); - - return is_clean; -} - -void emu_settings::RestoreDefaults() -{ - m_current_settings = YAML::Clone(m_default_settings); - Q_EMIT RestoreDefaultsSignal(); -} - -void emu_settings::SaveSettings() const -{ - YAML::Emitter out; - emit_data(out, m_current_settings); - Emulator::SaveSettings(out.c_str(), m_title_id); -} - -void emu_settings::EnhanceComboBox(QComboBox* combobox, emu_settings_type type, bool is_ranged, bool use_max, int max, bool sorted, bool strict) -{ - if (!combobox) - { - cfg_log.fatal("EnhanceComboBox '%s' was used with an invalid object", cfg_adapter::get_setting_name(type)); - return; - } - - if (is_ranged) - { - if (sorted) - { - cfg_log.warning("EnhanceCombobox '%s': ignoring sorting request on ranged combo box", cfg_adapter::get_setting_name(type)); - } - - const QStringList range = GetQStringSettingOptions(type); - ensure(!range.empty()); - - const int max_item = use_max ? max : range.last().toInt(); - - for (int i = range.first().toInt(); i <= max_item; i++) - { - combobox->addItem(QString::number(i), i); - } - } - else - { - const QStringList settings = GetQStringSettingOptions(type); - - for (int i = 0; i < settings.count(); i++) - { - const QString localized_setting = GetLocalizedSetting(settings[i], type, combobox->count(), strict); - combobox->addItem(localized_setting, QVariant({settings[i], i})); - } - - if (sorted) - { - combobox->model()->sort(0, Qt::AscendingOrder); - } - } - - // Since the QComboBox has localised strings, we can't just findText / findData, so we need to manually iterate through it to find our index - const auto find_index = [](QComboBox* combobox, const QString& value) - { - if (!combobox) - { - return -1; - } - - for (int i = 0; i < combobox->count(); i++) - { - const QVariantList var_list = combobox->itemData(i).toList(); - if (var_list.size() != 2 || !var_list[0].canConvert()) - { - fmt::throw_exception("Invalid data found in combobox entry %d (text='%s', listsize=%d, itemcount=%d)", i, combobox->itemText(i), var_list.size(), combobox->count()); - } - - if (value == var_list[0].toString()) - { - return i; - } - } - return -1; - }; - - const std::string def = GetSettingDefault(type); - const std::string selected = GetSetting(type); - const QString selected_q = QString::fromStdString(selected); - int index; - - if (is_ranged) - { - index = combobox->findData(selected_q); - } - else - { - index = find_index(combobox, selected_q); - } - - if (index == -1) - { - cfg_log.error("EnhanceComboBox '%s' tried to set an invalid value: %s. Setting to default: %s", cfg_adapter::get_setting_name(type), selected, def); - - if (is_ranged) - { - index = combobox->findData(QString::fromStdString(def)); - } - else - { - index = find_index(combobox, QString::fromStdString(def)); - } - - m_broken_types.insert(type); - } - - combobox->setCurrentIndex(index); - - connect(combobox, QOverload::of(&QComboBox::currentIndexChanged), combobox, [this, is_ranged, combobox, type](int index) - { - if (index < 0) - return; - - if (is_ranged) - { - SetSetting(type, combobox->itemData(index).toString().toStdString()); - } - else - { - const QVariantList var_list = combobox->itemData(index).toList(); - if (var_list.size() != 2 || !var_list[0].canConvert()) - { - fmt::throw_exception("Invalid data found in combobox entry %d (text='%s', listsize=%d, itemcount=%d)", index, combobox->itemText(index), var_list.size(), combobox->count()); - } - SetSetting(type, var_list[0].toString().toStdString()); - } - }); - - connect(this, &emu_settings::RestoreDefaultsSignal, combobox, [def, combobox, is_ranged, find_index]() - { - if (is_ranged) - { - combobox->setCurrentIndex(combobox->findData(QString::fromStdString(def))); - } - else - { - combobox->setCurrentIndex(find_index(combobox, QString::fromStdString(def))); - } - }); -} - -void emu_settings::EnhanceCheckBox(QCheckBox* checkbox, emu_settings_type type) -{ - if (!checkbox) - { - cfg_log.fatal("EnhanceCheckBox '%s' was used with an invalid object", cfg_adapter::get_setting_name(type)); - return; - } - - std::string def = GetSettingDefault(type); - std::transform(def.begin(), def.end(), def.begin(), ::tolower); - - if (def != "true" && def != "false") - { - cfg_log.fatal("EnhanceCheckBox '%s' was used with an invalid emu_settings_type", cfg_adapter::get_setting_name(type)); - return; - } - - std::string selected = GetSetting(type); - std::transform(selected.begin(), selected.end(), selected.begin(), ::tolower); - - if (selected == "true") - { - checkbox->setChecked(true); - } - else if (selected != "false") - { - cfg_log.error("EnhanceCheckBox '%s' tried to set an invalid value: %s. Setting to default: %s", cfg_adapter::get_setting_name(type), selected, def); - checkbox->setChecked(def == "true"); - m_broken_types.insert(type); - } - - connect(checkbox, &QCheckBox::checkStateChanged, this, [type, this](Qt::CheckState val) - { - const std::string str = val != Qt::Unchecked ? "true" : "false"; - SetSetting(type, str); - }); - - connect(this, &emu_settings::RestoreDefaultsSignal, checkbox, [def, checkbox]() - { - checkbox->setChecked(def == "true"); - }); -} - -void emu_settings::EnhanceDateTimeEdit(QDateTimeEdit* date_time_edit, emu_settings_type type, const QString& format, bool use_calendar, bool as_offset_from_now, int offset_update_time) -{ - if (!date_time_edit) - { - cfg_log.fatal("EnhanceDateTimeEdit '%s' was used with an invalid object", cfg_adapter::get_setting_name(type)); - return; - } - - date_time_edit->setDisplayFormat(format); - date_time_edit->setCalendarPopup(use_calendar); - - if (as_offset_from_now) - { - // If using offset from now, then we disable the keyboard tracking to reduce the numebr of events that occur (since for each event we will lose focus) - date_time_edit->setKeyboardTracking(false); - - const QStringList range = GetQStringSettingOptions(type); - ensure(!range.empty()); - - bool ok_def = false, ok_min = false, ok_max = false; - const s64 def = QString::fromStdString(GetSettingDefault(type)).toLongLong(&ok_def); - const s64 min = range.first().toLongLong(&ok_min); - const s64 max = range.last().toLongLong(&ok_max); - if (!ok_def || !ok_min || !ok_max) - { - cfg_log.fatal("EnhanceDateTimeEdit '%s' was used with an invalid emu_settings_type", cfg_adapter::get_setting_name(type)); - return; - } - - bool ok_sel = false; - s64 val = QString::fromStdString(GetSetting(type)).toLongLong(&ok_sel); - if (!ok_sel || val < min || val > max) - { - cfg_log.error("EnhanceDateTimeEdit '%s' tried to set an invalid value: %d. Setting to default: %d. Allowed range: [%d, %d]", cfg_adapter::get_setting_name(type), val, def, min, max); - val = def; - m_broken_types.insert(type); - SetSetting(type, std::to_string(def)); - } - - // we'll capture the DateTime once, and apply the min/max and offset against it here. - const QDateTime now = QDateTime::currentDateTime(); - - // we set the allowed limits - date_time_edit->setDateTimeRange(now.addSecs(min), now.addSecs(max)); - - // we add the offset, and set the control to have this datetime value - const QDateTime date_time = now.addSecs(val); - date_time_edit->setDateTime(date_time); - - // if we have an invalid update time then we won't run the update timer - if (offset_update_time > 0) - { - QTimer* console_time_update = new QTimer(date_time_edit); - connect(console_time_update, &QTimer::timeout, date_time_edit, [this, date_time_edit, min, max]() - { - if (!date_time_edit->hasFocus() && (!date_time_edit->calendarPopup() || !date_time_edit->calendarWidget()->hasFocus())) - { - const QDateTime now = QDateTime::currentDateTime(); - const s64 offset = QString::fromStdString(GetSetting(emu_settings_type::ConsoleTimeOffset)).toLongLong(); - date_time_edit->setDateTime(now.addSecs(offset)); - date_time_edit->setDateTimeRange(now.addSecs(min), now.addSecs(max)); - } - }); - - console_time_update->start(offset_update_time); - } - - connect(this, &emu_settings::RestoreDefaultsSignal, date_time_edit, [def, date_time_edit]() - { - date_time_edit->setDateTime(QDateTime::currentDateTime().addSecs(def)); - }); - } - else - { - const QStringList range = GetQStringSettingOptions(type); - ensure(!range.empty()); - - QString str = QString::fromStdString(GetSettingDefault(type)); - const QDateTime def = QDateTime::fromString(str, Qt::ISODate); - const QDateTime min = QDateTime::fromString(range.first(), Qt::ISODate); - const QDateTime max = QDateTime::fromString(range.last(), Qt::ISODate); - if (!def.isValid() || !min.isValid() || !max.isValid()) - { - cfg_log.fatal("EnhanceDateTimeEdit '%s' was used with an invalid emu_settings_type", cfg_adapter::get_setting_name(type)); - return; - } - - str = QString::fromStdString(GetSetting(type)); - QDateTime val = QDateTime::fromString(str, Qt::ISODate); - if (!val.isValid() || val < min || val > max) - { - cfg_log.error("EnhanceDateTimeEdit '%s' tried to set an invalid value: %s. Setting to default: %s Allowed range: [%s, %s]", - cfg_adapter::get_setting_name(type), val.toString(Qt::ISODate), def.toString(Qt::ISODate), min.toString(Qt::ISODate), max.toString(Qt::ISODate)); - val = def; - m_broken_types.insert(type); - SetSetting(type, def.toString(Qt::ISODate).toStdString()); - } - - // we set the allowed limits - date_time_edit->setDateTimeRange(min, max); - - // set the date_time value to the control - date_time_edit->setDateTime(val); - - connect(this, &emu_settings::RestoreDefaultsSignal, date_time_edit, [def, date_time_edit]() - { - date_time_edit->setDateTime(def); - }); - } - - connect(date_time_edit, &QDateTimeEdit::dateTimeChanged, this, [date_time_edit, type, as_offset_from_now, this](const QDateTime& datetime) - { - if (as_offset_from_now) - { - // offset will be applied in seconds - const s64 offset = QDateTime::currentDateTime().secsTo(datetime); - SetSetting(type, std::to_string(offset)); - - // HACK: We are only looking at whether the control has focus to prevent the time from updating dynamically, so we - // clear the focus, so that this dynamic updating isn't suppressed. - if (date_time_edit) - { - date_time_edit->clearFocus(); - } - } - else - { - // date time will be written straight into settings - SetSetting(type, datetime.toString(Qt::ISODate).toStdString()); - } - }); -} - -void emu_settings::EnhanceSlider(QSlider* slider, emu_settings_type type) -{ - if (!slider) - { - cfg_log.fatal("EnhanceSlider '%s' was used with an invalid object", cfg_adapter::get_setting_name(type)); - return; - } - - const QStringList range = GetQStringSettingOptions(type); - ensure(!range.empty()); - - bool ok_def, ok_sel, ok_min, ok_max; - - const int def = QString::fromStdString(GetSettingDefault(type)).toInt(&ok_def); - const int min = range.first().toInt(&ok_min); - const int max = range.last().toInt(&ok_max); - - if (!ok_def || !ok_min || !ok_max) - { - cfg_log.fatal("EnhanceSlider '%s' was used with an invalid emu_settings_type", cfg_adapter::get_setting_name(type)); - return; - } - - const QString selected = QString::fromStdString(GetSetting(type)); - int val = selected.toInt(&ok_sel); - - if (!ok_sel || val < min || val > max) - { - cfg_log.error("EnhanceSlider '%s' tried to set an invalid value: %d. Setting to default: %d. Allowed range: [%d, %d]", cfg_adapter::get_setting_name(type), val, def, min, max); - val = def; - m_broken_types.insert(type); - } - - slider->setRange(min, max); - slider->setValue(val); - - connect(slider, &QSlider::valueChanged, this, [type, this](int value) - { - SetSetting(type, QString::number(value).toStdString()); - }); - - connect(this, &emu_settings::RestoreDefaultsSignal, slider, [def, slider]() - { - slider->setValue(def); - }); -} - -void emu_settings::EnhanceSpinBox(QSpinBox* spinbox, emu_settings_type type, const QString& prefix, const QString& suffix) -{ - if (!spinbox) - { - cfg_log.fatal("EnhanceSpinBox '%s' was used with an invalid object", cfg_adapter::get_setting_name(type)); - return; - } - - const QStringList range = GetQStringSettingOptions(type); - ensure(!range.empty()); - - bool ok_def, ok_sel, ok_min, ok_max; - - const int def = QString::fromStdString(GetSettingDefault(type)).toInt(&ok_def); - const int min = range.first().toInt(&ok_min); - const int max = range.last().toInt(&ok_max); - - if (!ok_def || !ok_min || !ok_max) - { - cfg_log.fatal("EnhanceSpinBox '%s' was used with an invalid type", cfg_adapter::get_setting_name(type)); - return; - } - - const std::string selected = GetSetting(type); - int val = QString::fromStdString(selected).toInt(&ok_sel); - - if (!ok_sel || val < min || val > max) - { - cfg_log.error("EnhanceSpinBox '%s' tried to set an invalid value: %d. Setting to default: %d. Allowed range: [%d, %d]", cfg_adapter::get_setting_name(type), selected, def, min, max); - val = def; - m_broken_types.insert(type); - } - - spinbox->setPrefix(prefix); - spinbox->setSuffix(suffix); - spinbox->setRange(min, max); - spinbox->setValue(val); - - connect(spinbox, &QSpinBox::textChanged, this, [type, spinbox, this](const QString& /* text*/) - { - if (!spinbox) - return; - SetSetting(type, spinbox->cleanText().toStdString()); - }); - - connect(this, &emu_settings::RestoreDefaultsSignal, spinbox, [def, spinbox]() - { - spinbox->setValue(def); - }); -} - -void emu_settings::EnhanceDoubleSpinBox(QDoubleSpinBox* spinbox, emu_settings_type type, const QString& prefix, const QString& suffix) -{ - if (!spinbox) - { - cfg_log.fatal("EnhanceDoubleSpinBox '%s' was used with an invalid object", cfg_adapter::get_setting_name(type)); - return; - } - - const std::vector range = GetSettingOptions(type); - ensure(!range.empty()); - - const std::string def_s = GetSettingDefault(type); - const std::string val_s = GetSetting(type); - const std::string& min_s = range.front(); - const std::string& max_s = range.back(); - - // cfg::_float range is in s32 - constexpr s32 min_value = ::std::numeric_limits::min(); - constexpr s32 max_value = ::std::numeric_limits::max(); - - f64 val, def, min, max; - const bool ok_sel = try_to_float(&val, val_s, min_value, max_value); - const bool ok_def = try_to_float(&def, def_s, min_value, max_value); - const bool ok_min = try_to_float(&min, min_s, min_value, max_value); - const bool ok_max = try_to_float(&max, max_s, min_value, max_value); - - if (!ok_def || !ok_min || !ok_max) - { - cfg_log.fatal("EnhanceDoubleSpinBox '%s' was used with an invalid type. (val='%s', def='%s', min_s='%s', max_s='%s')", cfg_adapter::get_setting_name(type), val_s, def_s, min_s, max_s); - return; - } - - if (!ok_sel || val < min || val > max) - { - cfg_log.error("EnhanceDoubleSpinBox '%s' tried to set an invalid value: %f. Setting to default: %f. Allowed range: [%f, %f]", cfg_adapter::get_setting_name(type), val, def, min, max); - val = def; - m_broken_types.insert(type); - } - - spinbox->setPrefix(prefix); - spinbox->setSuffix(suffix); - spinbox->setRange(min, max); - spinbox->setValue(val); - - connect(spinbox, &QDoubleSpinBox::textChanged, this, [type, spinbox, this](const QString& /* text*/) - { - if (!spinbox) - return; - SetSetting(type, spinbox->cleanText().toStdString()); - }); - - connect(this, &emu_settings::RestoreDefaultsSignal, spinbox, [def, spinbox]() - { - spinbox->setValue(def); - }); -} - -void emu_settings::EnhanceLineEdit(QLineEdit* edit, emu_settings_type type) -{ - if (!edit) - { - cfg_log.fatal("EnhanceEdit '%s' was used with an invalid object", cfg_adapter::get_setting_name(type)); - return; - } - - const std::string set_text = GetSetting(type); - edit->setText(QString::fromStdString(set_text)); - - connect(edit, &QLineEdit::textChanged, this, [type, this](const QString& text) - { - const QString trimmed = text.trimmed(); - if (trimmed.size() != text.size()) - { - cfg_log.warning("EnhanceLineEdit '%s' input was trimmed", cfg_adapter::get_setting_name(type)); - } - SetSetting(type, trimmed.toStdString()); - }); - - connect(this, &emu_settings::RestoreDefaultsSignal, edit, [this, edit, type]() - { - edit->setText(QString::fromStdString(GetSettingDefault(type))); - }); -} - -void emu_settings::EnhanceRadioButton(QButtonGroup* button_group, emu_settings_type type) -{ - if (!button_group) - { - cfg_log.fatal("EnhanceRadioButton '%s' was used with an invalid object", cfg_adapter::get_setting_name(type)); - return; - } - - const QString selected = QString::fromStdString(GetSetting(type)); - const QString def = QString::fromStdString(GetSettingDefault(type)); - const QStringList options = GetQStringSettingOptions(type); - - if (button_group->buttons().count() < options.size()) - { - cfg_log.fatal("EnhanceRadioButton '%s': wrong button count", cfg_adapter::get_setting_name(type)); - return; - } - - bool found = false; - int def_pos = -1; - - for (int i = 0; i < options.count(); i++) - { - const QString& option = options[i]; - const QString localized_setting = GetLocalizedSetting(option, type, i, true); - - QAbstractButton* button = button_group->button(i); - button->setText(localized_setting); - - if (!found && option == selected) - { - found = true; - button->setChecked(true); - } - - if (def_pos == -1 && option == def) - { - def_pos = i; - } - - connect(button, &QAbstractButton::toggled, this, [this, type, val = option.toStdString()](bool checked) - { - if (checked) - { - SetSetting(type, val); - } - }); - } - - if (!found) - { - ensure(def_pos >= 0); - - cfg_log.error("EnhanceRadioButton '%s' tried to set an invalid value: %s. Setting to default: %s.", cfg_adapter::get_setting_name(type), selected, def); - m_broken_types.insert(type); - - // Select the default option on invalid setting string - button_group->button(def_pos)->setChecked(true); - } - - connect(this, &emu_settings::RestoreDefaultsSignal, button_group, [button_group, def_pos]() - { - if (button_group && button_group->button(def_pos)) - { - button_group->button(def_pos)->setChecked(true); - } - }); -} - -std::vector emu_settings::GetLibrariesControl() -{ - return m_current_settings["Core"]["Libraries Control"].as, std::initializer_list>({}); -} - -void emu_settings::SaveSelectedLibraries(const std::vector& libs) -{ - m_current_settings["Core"]["Libraries Control"] = libs; -} - -std::vector emu_settings::GetSettingOptions(emu_settings_type type) -{ - return cfg_adapter::get_options(::at32(settings_location, type)); -} - -QStringList emu_settings::GetQStringSettingOptions(emu_settings_type type) -{ - QStringList values; - for (const std::string& value : cfg_adapter::get_options(::at32(settings_location, type))) - { - values.append(QString::fromStdString(value)); - } - return values; -} - -std::string emu_settings::GetSettingDefault(emu_settings_type type) const -{ - if (const auto node = cfg_adapter::get_node(m_default_settings, ::at32(settings_location, type)); node && node.IsScalar()) - { - return node.Scalar(); - } - - cfg_log.fatal("GetSettingDefault(type=%d) could not retrieve the requested node", static_cast(type)); - return ""; -} - -std::string emu_settings::GetSetting(emu_settings_type type) const -{ - if (const auto node = cfg_adapter::get_node(m_current_settings, ::at32(settings_location, type)); node && node.IsScalar()) - { - return node.Scalar(); - } - - cfg_log.fatal("GetSetting(type=%d) could not retrieve the requested node", static_cast(type)); - return ""; -} - -void emu_settings::SetSetting(emu_settings_type type, const std::string& val) const -{ - cfg_adapter::get_node(m_current_settings, ::at32(settings_location, type)) = val; -} - -emu_settings_type emu_settings::FindSettingsType(const cfg::_base* node) const -{ - // Add key and value to static map on first use - static std::map id_to_type; - static std::mutex mtx; - std::lock_guard lock(mtx); - - if (!node) [[unlikely]] - { - // Provoke error. Don't use ensure or we will get a nullptr deref warning in VS - return ::at32(id_to_type, umax); - } - - std::vector node_location; - if (!id_to_type.contains(node->get_id())) - { - for (const cfg::_base* n = node; n; n = n->get_parent()) - { - if (!n->get_name().empty()) - { - node_location.push_back(n->get_name()); - } - } - - std::reverse(node_location.begin(), node_location.end()); - - for (const auto& [type, loc] : settings_location) - { - if (node_location.size() != loc.size()) - { - continue; - } - - bool is_match = true; - for (usz i = 0; i < node_location.size(); i++) - { - if (node_location[i] != loc[i]) - { - is_match = false; - break; - } - } - - if (is_match && !id_to_type.try_emplace(node->get_id(), type).second) - { - cfg_log.error("'%s' already exists", loc.back()); - } - } - } - - if (!id_to_type.contains(node->get_id())) - { - fmt::throw_exception("Node '%s' not represented in emu_settings_type", node->get_name()); - } - - return ::at32(id_to_type, node->get_id()); -} - -void emu_settings::OpenCorrectionDialog(QWidget* parent) -{ - if (!m_broken_types.empty() && QMessageBox::question(parent, tr("Fix invalid settings?"), - tr( - "Your config file contained one or more unrecognized values for settings.\n" - "Their default value will be used until they are corrected.\n" - "Consider that a correction might render them invalid for other versions of RPCS3.\n" - "\n" - "Do you wish to let the program correct them for you?\n" - "This change will only be final when you save the config."), - QMessageBox::Yes | QMessageBox::No, QMessageBox::No) == QMessageBox::Yes) - { - for (const auto& type : m_broken_types) - { - const std::string def = GetSettingDefault(type); - const std::string old = GetSetting(type); - SetSetting(type, def); - cfg_log.success("The config entry '%s' was corrected from '%s' to '%s'", cfg_adapter::get_setting_name(type), old, def); - } - - m_broken_types.clear(); - cfg_log.success("You need to save the settings in order to make these changes permanent!"); - } -} - -QString emu_settings::GetLocalizedSetting(const QString& original, emu_settings_type type, int index, bool strict) const -{ - switch (type) - { - case emu_settings_type::SPUBlockSize: - switch (static_cast(index)) - { - case spu_block_size_type::safe: return tr("Safe", "SPU block size"); - case spu_block_size_type::mega: return tr("Mega", "SPU block size"); - case spu_block_size_type::giga: return tr("Giga", "SPU block size"); - } - break; - case emu_settings_type::ThreadSchedulerMode: - switch (static_cast(index)) - { - case thread_scheduler_mode::old: return tr("RPCS3 Scheduler", "Thread Scheduler Mode"); - case thread_scheduler_mode::alt: return tr("RPCS3 Alternative Scheduler", "Thread Scheduler Mode"); - case thread_scheduler_mode::os: return tr("Operating System", "Thread Scheduler Mode"); - } - break; - case emu_settings_type::EnableTSX: - switch (static_cast(index)) - { - case tsx_usage::disabled: return tr("Disabled", "Enable TSX"); - case tsx_usage::enabled: return tr("Enabled", "Enable TSX"); - case tsx_usage::forced: return tr("Forced", "Enable TSX"); - } - break; - case emu_settings_type::Renderer: - switch (static_cast(index)) - { - case video_renderer::null: return tr("Disable Video Output", "Video renderer"); - case video_renderer::opengl: return tr("OpenGL", "Video renderer"); - case video_renderer::vulkan: return tr("Vulkan", "Video renderer"); - } - break; - case emu_settings_type::ShaderMode: - switch (static_cast(index)) - { - case shader_mode::recompiler: return tr("Legacy (single threaded)", "Shader Mode"); - case shader_mode::async_recompiler: return tr("Async (multi threaded)", "Shader Mode"); - case shader_mode::async_with_interpreter: return tr("Async with Shader Interpreter", "Shader Mode"); - case shader_mode::interpreter_only: return tr("Shader Interpreter only", "Shader Mode"); - } - break; - case emu_settings_type::Resolution: - switch (static_cast(index)) - { - case video_resolution::_1080p: return tr("1080p", "Resolution"); - case video_resolution::_1080i: return tr("1080i", "Resolution"); - case video_resolution::_720p: return tr("720p", "Resolution"); - case video_resolution::_480p: return tr("480p", "Resolution"); - case video_resolution::_480i: return tr("480i", "Resolution"); - case video_resolution::_576p: return tr("576p", "Resolution"); - case video_resolution::_576i: return tr("576i", "Resolution"); - case video_resolution::_1600x1080p: return tr("1600x1080p", "Resolution"); - case video_resolution::_1440x1080p: return tr("1440x1080p", "Resolution"); - case video_resolution::_1280x1080p: return tr("1280x1080p", "Resolution"); - case video_resolution::_960x1080p: return tr("960x1080p", "Resolution"); - } - break; - case emu_settings_type::FrameLimit: - switch (static_cast(index)) - { - case frame_limit_type::none: return tr("Off", "Frame limit"); - case frame_limit_type::_30: return tr("30", "Frame limit"); - case frame_limit_type::_50: return tr("50", "Frame limit"); - case frame_limit_type::_60: return tr("60", "Frame limit"); - case frame_limit_type::_120: return tr("120", "Frame limit"); - case frame_limit_type::display_rate: return tr("Display", "Frame limit"); - case frame_limit_type::_auto: return tr("Auto", "Frame limit"); - case frame_limit_type::_ps3: return tr("PS3 Native", "Frame limit"); - case frame_limit_type::infinite: return tr("Infinite", "Frame limit"); - } - break; - case emu_settings_type::MSAA: - switch (static_cast(index)) - { - case msaa_level::none: return tr("Disabled", "MSAA"); - case msaa_level::_auto: return tr("Auto", "MSAA"); - } - break; - case emu_settings_type::ShaderPrecisionQuality: - switch (static_cast(index)) - { - case gpu_preset_level::_auto: return tr("Auto", "Shader Precision"); - case gpu_preset_level::ultra: return tr("Ultra", "Shader Precision"); - case gpu_preset_level::high: return tr("High", "Shader Precision"); - case gpu_preset_level::low: return tr("Low", "Shader Precision"); - } - break; - case emu_settings_type::OutputScalingMode: - switch (static_cast(index)) - { - case output_scaling_mode::nearest: return tr("Nearest", "Output Scaling Mode"); - case output_scaling_mode::bilinear: return tr("Bilinear", "Output Scaling Mode"); - case output_scaling_mode::fsr: return tr("FidelityFX Super Resolution 1", "Output Scaling Mode"); - } - break; - case emu_settings_type::AudioRenderer: - switch (static_cast(index)) - { - case audio_renderer::null: return tr("Disable Audio Output", "Audio renderer"); -#ifdef _WIN32 - case audio_renderer::xaudio: return tr("XAudio2", "Audio renderer"); -#endif - case audio_renderer::cubeb: return tr("Cubeb", "Audio renderer"); -#ifdef HAVE_FAUDIO - case audio_renderer::faudio: return tr("FAudio", "Audio renderer"); -#endif - } - break; - case emu_settings_type::MicrophoneType: - switch (static_cast(index)) - { - case microphone_handler::null: return tr("Disabled", "Microphone handler"); - case microphone_handler::standard: return tr("Standard", "Microphone handler"); - case microphone_handler::singstar: return tr("SingStar", "Microphone handler"); - case microphone_handler::real_singstar: return tr("Real SingStar", "Microphone handler"); - case microphone_handler::rocksmith: return tr("Rocksmith", "Microphone handler"); - } - break; - case emu_settings_type::KeyboardHandler: - switch (static_cast(index)) - { - case keyboard_handler::null: return tr("Null", "Keyboard handler"); - case keyboard_handler::basic: return tr("Basic", "Keyboard handler"); - } - break; - case emu_settings_type::MouseHandler: - switch (static_cast(index)) - { - case mouse_handler::null: return tr("Null", "Mouse handler"); - case mouse_handler::basic: return tr("Basic", "Mouse handler"); - case mouse_handler::raw: return tr("Raw", "Mouse handler"); - } - break; - case emu_settings_type::CameraType: - switch (static_cast(index)) - { - case fake_camera_type::unknown: return tr("Unknown", "Camera type"); - case fake_camera_type::eyetoy: return tr("EyeToy", "Camera type"); - case fake_camera_type::eyetoy2: return tr("PS Eye", "Camera type"); - case fake_camera_type::uvc1_1: return tr("UVC 1.1", "Camera type"); - } - break; - case emu_settings_type::CameraFlip: - switch (static_cast(index)) - { - case camera_flip::none: return tr("No", "Camera flip"); - case camera_flip::horizontal: return tr("Flip horizontally", "Camera flip"); - case camera_flip::vertical: return tr("Flip vertically", "Camera flip"); - case camera_flip::both: return tr("Flip both axes", "Camera flip"); - } - break; - case emu_settings_type::Camera: - switch (static_cast(index)) - { - case camera_handler::null: return tr("Null", "Camera handler"); - case camera_handler::fake: return tr("Fake", "Camera handler"); - case camera_handler::qt: return tr("Qt", "Camera handler"); - } - break; - case emu_settings_type::MusicHandler: - switch (static_cast(index)) - { - case music_handler::null: return tr("Null", "Music handler"); - case music_handler::qt: return tr("Qt", "Music handler"); - } - break; - case emu_settings_type::PadHandlerMode: - switch (static_cast(index)) - { - case pad_handler_mode::single_threaded: return tr("Single-threaded", "Pad handler mode"); - case pad_handler_mode::multi_threaded: return tr("Multi-threaded", "Pad handler mode"); - } - break; - case emu_settings_type::Move: - switch (static_cast(index)) - { - case move_handler::null: return tr("Null", "Move handler"); - case move_handler::real: return tr("Real", "Move handler"); - case move_handler::fake: return tr("Fake", "Move handler"); - case move_handler::mouse: return tr("Mouse", "Move handler"); - case move_handler::raw_mouse: return tr("Raw Mouse", "Move handler"); -#ifdef HAVE_LIBEVDEV - case move_handler::gun: return tr("Gun", "Gun handler"); -#endif - } - break; - case emu_settings_type::Buzz: - switch (static_cast(index)) - { - case buzz_handler::null: return tr("Null (use real Buzzers)", "Buzz handler"); - case buzz_handler::one_controller: return tr("1 controller (1-4 players)", "Buzz handler"); - case buzz_handler::two_controllers: return tr("2 controllers (5-7 players)", "Buzz handler"); - } - break; - case emu_settings_type::Turntable: - switch (static_cast(index)) - { - case turntable_handler::null: return tr("Null", "Turntable handler"); - case turntable_handler::one_controller: return tr("1 controller", "Turntable handler"); - case turntable_handler::two_controllers: return tr("2 controllers", "Turntable handler"); - } - break; - case emu_settings_type::GHLtar: - switch (static_cast(index)) - { - case ghltar_handler::null: return tr("Null", "GHLtar handler"); - case ghltar_handler::one_controller: return tr("1 controller", "GHLtar handler"); - case ghltar_handler::two_controllers: return tr("2 controllers", "GHLtar handler"); - } - break; - case emu_settings_type::InternetStatus: - switch (static_cast(index)) - { - case np_internet_status::disabled: return tr("Disconnected", "Internet Status"); - case np_internet_status::enabled: return tr("Connected", "Internet Status"); - } - break; - case emu_settings_type::PSNStatus: - switch (static_cast(index)) - { - case np_psn_status::disabled: return tr("Disconnected", "PSN Status"); - case np_psn_status::psn_fake: return tr("Simulated", "PSN Status"); - case np_psn_status::psn_rpcn: return tr("RPCN", "PSN Status"); - } - break; - case emu_settings_type::SleepTimersAccuracy: - switch (static_cast(index)) - { - case sleep_timers_accuracy_level::_as_host: return tr("As Host", "Sleep timers accuracy"); - case sleep_timers_accuracy_level::_usleep: return tr("Usleep Only", "Sleep timers accuracy"); - case sleep_timers_accuracy_level::_all_timers: return tr("All Timers", "Sleep timers accuracy"); - } - break; - case emu_settings_type::FIFOAccuracy: - switch (static_cast(index)) - { - case rsx_fifo_mode::fast: return tr("Fast", "RSX FIFO Accuracy"); - case rsx_fifo_mode::atomic: return tr("Atomic", "RSX FIFO Accuracy"); - case rsx_fifo_mode::atomic_ordered: return tr("Ordered & Atomic", "RSX FIFO Accuracy"); - case rsx_fifo_mode::as_ps3: return tr("PS3", "RSX FIFO Accuracy"); - } - break; - case emu_settings_type::PerfOverlayDetailLevel: - switch (static_cast(index)) - { - case detail_level::none: return tr("None", "Detail Level"); - case detail_level::minimal: return tr("Minimal", "Detail Level"); - case detail_level::low: return tr("Low", "Detail Level"); - case detail_level::medium: return tr("Medium", "Detail Level"); - case detail_level::high: return tr("High", "Detail Level"); - } - break; - case emu_settings_type::PerfOverlayFramerateDetailLevel: - case emu_settings_type::PerfOverlayFrametimeDetailLevel: - switch (static_cast(index)) - { - case perf_graph_detail_level::minimal: return tr("Minimal", "Perf Graph Detail Level"); - case perf_graph_detail_level::show_min_max: return tr("Show Min And Max", "Perf Graph Detail Level"); - case perf_graph_detail_level::show_one_percent_avg: return tr("Show 1% Low And Average", "Perf Graph Detail Level"); - case perf_graph_detail_level::show_all: return tr("Show All", "Perf Graph Detail Level"); - } - break; - case emu_settings_type::PerfOverlayPosition: - switch (static_cast(index)) - { - case screen_quadrant::top_left: return tr("Top Left", "Performance overlay position"); - case screen_quadrant::top_right: return tr("Top Right", "Performance overlay position"); - case screen_quadrant::bottom_left: return tr("Bottom Left", "Performance overlay position"); - case screen_quadrant::bottom_right: return tr("Bottom Right", "Performance overlay position"); - } - break; - case emu_settings_type::PPUDecoder: - switch (static_cast(index)) - { - case ppu_decoder_type::_static: return tr("Interpreter (Legacy)", "PPU decoder"); - case ppu_decoder_type::llvm_legacy: return tr("LLVM Recompiler (Legacy)", "PPU decoder"); - case ppu_decoder_type::interpreter: return tr("Interpreter", "PPU decoder"); - } - break; - case emu_settings_type::SPUDecoder: - switch (static_cast(index)) - { - case spu_decoder_type::_static: return tr("Interpreter (static)", "SPU decoder"); - case spu_decoder_type::dynamic: return tr("Interpreter (dynamic)", "SPU decoder"); - case spu_decoder_type::asmjit: return tr("Recompiler (ASMJIT)", "SPU decoder"); - case spu_decoder_type::llvm: return tr("Recompiler (LLVM)", "SPU decoder"); - } - break; - case emu_settings_type::EnterButtonAssignment: - switch (static_cast(index)) - { - case enter_button_assign::circle: return tr("Enter with circle", "Enter button assignment"); - case enter_button_assign::cross: return tr("Enter with cross", "Enter button assignment"); - } - break; - case emu_settings_type::AudioFormat: - switch (static_cast(index)) - { - case audio_format::stereo: return tr("Stereo", "Audio format"); - case audio_format::surround_5_1: return tr("Surround 5.1", "Audio format"); - case audio_format::surround_7_1: return tr("Surround 7.1", "Audio format"); - case audio_format::manual: return tr("Manual", "Audio format"); - case audio_format::automatic: return tr("Automatic", "Audio format"); - } - break; - case emu_settings_type::AudioFormats: - switch (static_cast(index)) - { - case audio_format_flag::lpcm_2_48khz: return tr("Linear PCM 2 Ch. 48 kHz", "Audio format flag"); - case audio_format_flag::lpcm_5_1_48khz: return tr("Linear PCM 5.1 Ch. 48 kHz", "Audio format flag"); - case audio_format_flag::lpcm_7_1_48khz: return tr("Linear PCM 7.1 Ch. 48 kHz", "Audio format flag"); - case audio_format_flag::ac3: return tr("Dolby Digital 5.1 Ch.", "Audio format flag"); - case audio_format_flag::dts: return tr("DTS 5.1 Ch.", "Audio format flag"); - } - break; - case emu_settings_type::AudioProvider: - switch (static_cast(index)) - { - case audio_provider::none: return tr("None", "Audio Provider"); - case audio_provider::cell_audio: return tr("CellAudio", "Audio Provider"); - case audio_provider::rsxaudio: return tr("RSXAudio", "Audio Provider"); - } - break; - case emu_settings_type::AudioAvport: - switch (static_cast(index)) - { - case audio_avport::hdmi_0: return tr("HDMI 0", "Audio Avport"); - case audio_avport::hdmi_1: return tr("HDMI 1", "Audio Avport"); - case audio_avport::avmulti: return tr("AV multiout", "Audio Avport"); - case audio_avport::spdif_0: return tr("SPDIF 0", "Audio Avport"); - case audio_avport::spdif_1: return tr("SPDIF 1", "Audio Avport"); - } - break; - case emu_settings_type::AudioChannelLayout: - switch (static_cast(index)) - { - case audio_channel_layout::automatic: return tr("Auto", "Audio Channel Layout"); - case audio_channel_layout::mono: return tr("Mono", "Audio Channel Layout"); - case audio_channel_layout::stereo: return tr("Stereo", "Audio Channel Layout"); - case audio_channel_layout::stereo_lfe: return tr("Stereo LFE", "Audio Channel Layout"); - case audio_channel_layout::quadraphonic: return tr("Quadraphonic", "Audio Channel Layout"); - case audio_channel_layout::quadraphonic_lfe: return tr("Quadraphonic LFE", "Audio Channel Layout"); - case audio_channel_layout::surround_5_1: return tr("Surround 5.1", "Audio Channel Layout"); - case audio_channel_layout::surround_7_1: return tr("Surround 7.1", "Audio Channel Layout"); - } - break; - case emu_settings_type::LicenseArea: - switch (static_cast(index)) - { - case CellSysutilLicenseArea::CELL_SYSUTIL_LICENSE_AREA_J: return tr("Japan", "License Area"); - case CellSysutilLicenseArea::CELL_SYSUTIL_LICENSE_AREA_A: return tr("America", "License Area"); - case CellSysutilLicenseArea::CELL_SYSUTIL_LICENSE_AREA_E: return tr("Europe, Oceania, Middle East, Russia", "License Area"); - case CellSysutilLicenseArea::CELL_SYSUTIL_LICENSE_AREA_H: return tr("Southeast Asia", "License Area"); - case CellSysutilLicenseArea::CELL_SYSUTIL_LICENSE_AREA_K: return tr("Korea", "License Area"); - case CellSysutilLicenseArea::CELL_SYSUTIL_LICENSE_AREA_C: return tr("China", "License Area"); - case CellSysutilLicenseArea::CELL_SYSUTIL_LICENSE_AREA_OTHER: return tr("Other", "License Area"); - } - break; - case emu_settings_type::VulkanAsyncSchedulerDriver: - switch (static_cast(index)) - { - case vk_gpu_scheduler_mode::safe: return tr("Safe", "Asynchronous Queue Scheduler"); - case vk_gpu_scheduler_mode::fast: return tr("Fast", "Asynchronous Queue Scheduler"); - } - break; - case emu_settings_type::Language: - switch (static_cast(index)) - { - case CELL_SYSUTIL_LANG_JAPANESE: return tr("Japanese", "System Language"); - case CELL_SYSUTIL_LANG_ENGLISH_US: return tr("English (US)", "System Language"); - case CELL_SYSUTIL_LANG_FRENCH: return tr("French", "System Language"); - case CELL_SYSUTIL_LANG_SPANISH: return tr("Spanish", "System Language"); - case CELL_SYSUTIL_LANG_GERMAN: return tr("German", "System Language"); - case CELL_SYSUTIL_LANG_ITALIAN: return tr("Italian", "System Language"); - case CELL_SYSUTIL_LANG_DUTCH: return tr("Dutch", "System Language"); - case CELL_SYSUTIL_LANG_PORTUGUESE_PT: return tr("Portuguese (Portugal)", "System Language"); - case CELL_SYSUTIL_LANG_RUSSIAN: return tr("Russian", "System Language"); - case CELL_SYSUTIL_LANG_KOREAN: return tr("Korean", "System Language"); - case CELL_SYSUTIL_LANG_CHINESE_T: return tr("Chinese (Traditional)", "System Language"); - case CELL_SYSUTIL_LANG_CHINESE_S: return tr("Chinese (Simplified)", "System Language"); - case CELL_SYSUTIL_LANG_FINNISH: return tr("Finnish", "System Language"); - case CELL_SYSUTIL_LANG_SWEDISH: return tr("Swedish", "System Language"); - case CELL_SYSUTIL_LANG_DANISH: return tr("Danish", "System Language"); - case CELL_SYSUTIL_LANG_NORWEGIAN: return tr("Norwegian", "System Language"); - case CELL_SYSUTIL_LANG_POLISH: return tr("Polish", "System Language"); - case CELL_SYSUTIL_LANG_ENGLISH_GB: return tr("English (UK)", "System Language"); - case CELL_SYSUTIL_LANG_PORTUGUESE_BR: return tr("Portuguese (Brazil)", "System Language"); - case CELL_SYSUTIL_LANG_TURKISH: return tr("Turkish", "System Language"); - default: - break; - } - break; - case emu_settings_type::KeyboardType: - switch (static_cast(index)) - { - case CELL_KB_MAPPING_101: return tr("English keyboard (US standard)", "Keyboard Type"); - case CELL_KB_MAPPING_106: return tr("Japanese keyboard", "Keyboard Type"); - case CELL_KB_MAPPING_106_KANA: return tr("Japanese keyboard (Kana state)", "Keyboard Type"); - case CELL_KB_MAPPING_GERMAN_GERMANY: return tr("German keyboard", "Keyboard Type"); - case CELL_KB_MAPPING_SPANISH_SPAIN: return tr("Spanish keyboard", "Keyboard Type"); - case CELL_KB_MAPPING_FRENCH_FRANCE: return tr("French keyboard", "Keyboard Type"); - case CELL_KB_MAPPING_ITALIAN_ITALY: return tr("Italian keyboard", "Keyboard Type"); - case CELL_KB_MAPPING_DUTCH_NETHERLANDS: return tr("Dutch keyboard", "Keyboard Type"); - case CELL_KB_MAPPING_PORTUGUESE_PORTUGAL: return tr("Portuguese keyboard (Portugal)", "Keyboard Type"); - case CELL_KB_MAPPING_RUSSIAN_RUSSIA: return tr("Russian keyboard", "Keyboard Type"); - case CELL_KB_MAPPING_ENGLISH_UK: return tr("English keyboard (UK standard)", "Keyboard Type"); - case CELL_KB_MAPPING_KOREAN_KOREA: return tr("Korean keyboard", "Keyboard Type"); - case CELL_KB_MAPPING_NORWEGIAN_NORWAY: return tr("Norwegian keyboard", "Keyboard Type"); - case CELL_KB_MAPPING_FINNISH_FINLAND: return tr("Finnish keyboard", "Keyboard Type"); - case CELL_KB_MAPPING_DANISH_DENMARK: return tr("Danish keyboard", "Keyboard Type"); - case CELL_KB_MAPPING_SWEDISH_SWEDEN: return tr("Swedish keyboard", "Keyboard Type"); - case CELL_KB_MAPPING_CHINESE_TRADITIONAL: return tr("Chinese keyboard (Traditional)", "Keyboard Type"); - case CELL_KB_MAPPING_CHINESE_SIMPLIFIED: return tr("Chinese keyboard (Simplified)", "Keyboard Type"); - case CELL_KB_MAPPING_SWISS_FRENCH_SWITZERLAND: return tr("French keyboard (Switzerland)", "Keyboard Type"); - case CELL_KB_MAPPING_SWISS_GERMAN_SWITZERLAND: return tr("German keyboard (Switzerland)", "Keyboard Type"); - case CELL_KB_MAPPING_CANADIAN_FRENCH_CANADA: return tr("French keyboard (Canada)", "Keyboard Type"); - case CELL_KB_MAPPING_BELGIAN_BELGIUM: return tr("French keyboard (Belgium)", "Keyboard Type"); - case CELL_KB_MAPPING_POLISH_POLAND: return tr("Polish keyboard", "Keyboard Type"); - case CELL_KB_MAPPING_PORTUGUESE_BRAZIL: return tr("Portuguese keyboard (Brazil)", "Keyboard Type"); - case CELL_KB_MAPPING_TURKISH_TURKEY: return tr("Turkish keyboard", "Keyboard Type"); - } - break; - case emu_settings_type::ExclusiveFullscreenMode: - switch (static_cast(index)) - { - case vk_exclusive_fs_mode::unspecified: return tr("Automatic (Default)", "Exclusive Fullscreen Mode"); - case vk_exclusive_fs_mode::disable: return tr("Prefer borderless fullscreen", "Exclusive Fullscreen Mode"); - case vk_exclusive_fs_mode::enable: return tr("Prefer exclusive fullscreen", "Exclusive Fullscreen Mode"); - } - break; - case emu_settings_type::StereoRenderMode: - switch (static_cast(index)) - { - case stereo_render_mode_options::disabled: return tr("Disabled", "3D Display Mode"); - case stereo_render_mode_options::side_by_side: return tr("Side-by-side", "3D Display Mode"); - case stereo_render_mode_options::over_under: return tr("Over-under", "3D Display Mode"); - case stereo_render_mode_options::interlaced: return tr("Interlaced", "3D Display Mode"); - case stereo_render_mode_options::anaglyph_red_green: return tr("Anaglyph Red-Green", "3D Display Mode"); - case stereo_render_mode_options::anaglyph_red_blue: return tr("Anaglyph Red-Blue", "3D Display Mode"); - case stereo_render_mode_options::anaglyph_red_cyan: return tr("Anaglyph Red-Cyan", "3D Display Mode"); - case stereo_render_mode_options::anaglyph_magenta_cyan: return tr("Anaglyph Magenta-Cyan", "3D Display Mode"); - case stereo_render_mode_options::anaglyph_trioscopic: return tr("Anaglyph Green-Magenta (Trioscopic)", "3D Display Mode"); - case stereo_render_mode_options::anaglyph_amber_blue: return tr("Anaglyph Amber-Blue (ColorCode 3D)", "3D Display Mode"); - } - break; - case emu_settings_type::MidiDevices: - switch (static_cast(index)) - { - case midi_device_type::guitar: return tr("Guitar (17 frets)", "Midi Device Type"); - case midi_device_type::guitar_22fret: return tr("Guitar (22 frets)", "Midi Device Type"); - case midi_device_type::keyboard: return tr("Keyboard", "Midi Device Type"); - case midi_device_type::drums: return tr("Drums", "Midi Device Type"); - } - break; - case emu_settings_type::XFloatAccuracy: - switch (static_cast(index)) - { - case xfloat_accuracy::accurate: return tr("Accurate XFloat"); - case xfloat_accuracy::approximate: return tr("Approximate XFloat"); - case xfloat_accuracy::relaxed: return tr("Relaxed XFloat"); - case xfloat_accuracy::inaccurate: return tr("Inaccurate XFloat"); - } - break; - default: - break; - } - - // if (strict) - // { - // std::string type_string; - // if (const auto it = settings_location.find(type); it != settings_location.cend()) - // { - // for (const char* loc : it->second) - // { - // if (!type_string.empty()) - // type_string += ": "; - // type_string += loc; - // } - // } - // fmt::throw_exception("Missing translation for emu setting (original=%s, type='%s'=%d, index=%d)", original, type_string.empty() ? "?" : type_string, static_cast(type), index); - // } - - return original; -} - -std::string emu_settings::GetLocalizedSetting(const std::string& original, emu_settings_type type, int index, bool strict) const -{ - return GetLocalizedSetting(QString::fromStdString(original), type, index, strict).toStdString(); -} - -std::string emu_settings::GetLocalizedSetting(const cfg::_base* node, u32 index) const -{ - const emu_settings_type type = FindSettingsType(node); - const std::vector settings = GetSettingOptions(type); - return GetLocalizedSetting(::at32(settings, index), type, index, true); -} diff --git a/rpcs3qt-legacy/emu_settings.h b/rpcs3qt-legacy/emu_settings.h deleted file mode 100644 index 8ecb25afd..000000000 --- a/rpcs3qt-legacy/emu_settings.h +++ /dev/null @@ -1,130 +0,0 @@ -#pragma once - -#include "util/types.hpp" -#include "util/yaml.hpp" - -#include "microphone_creator.h" -#include "midi_creator.h" -#include "render_creator.h" -#include "emu_settings_type.h" - -#include -#include -#include -#include -#include -#include - -#include -#include - -namespace cfg -{ - class _base; -} - -constexpr auto qstr = QString::fromStdString; - -class emu_settings : public QObject -{ - /** A settings class for Emulator specific settings. This class is a refactored version of the wx version. It is much nicer - * - */ - Q_OBJECT -public: - std::set m_broken_types; // list of broken settings - - /** Creates a settings object which reads in the config.yml file at rpcs3/bin/%path%/config.yml - * Settings are only written when SaveSettings is called. - */ - emu_settings(); - - bool Init(); - - /** Connects a combo box with the target settings type*/ - void EnhanceComboBox(QComboBox* combobox, emu_settings_type type, bool is_ranged = false, bool use_max = false, int max = 0, bool sorted = false, bool strict = true); - - /** Connects a check box with the target settings type*/ - void EnhanceCheckBox(QCheckBox* checkbox, emu_settings_type type); - - /** Connects a date time edit box with the target settings type*/ - void EnhanceDateTimeEdit(QDateTimeEdit* date_time_edit, emu_settings_type type, const QString& format, bool use_calendar, bool as_offset_from_now, int offset_update_time = 0); - - /** Connects a slider with the target settings type*/ - void EnhanceSlider(QSlider* slider, emu_settings_type type); - - /** Connects an integer spin box with the target settings type*/ - void EnhanceSpinBox(QSpinBox* spinbox, emu_settings_type type, const QString& prefix = "", const QString& suffix = ""); - - /** Connects a double spin box with the target settings type*/ - void EnhanceDoubleSpinBox(QDoubleSpinBox* spinbox, emu_settings_type type, const QString& prefix = "", const QString& suffix = ""); - - /** Connects a line edit with the target settings type*/ - void EnhanceLineEdit(QLineEdit* edit, emu_settings_type type); - - /** Connects a button group with the target settings type*/ - void EnhanceRadioButton(QButtonGroup* button_group, emu_settings_type type); - - std::vector GetLibrariesControl(); - void SaveSelectedLibraries(const std::vector& libs); - - /** Returns the valid options for a given setting.*/ - static std::vector GetSettingOptions(emu_settings_type type); - - /** Returns the valid options for a given setting.*/ - static QStringList GetQStringSettingOptions(emu_settings_type type); - - /** Returns the default value of the setting type.*/ - std::string GetSettingDefault(emu_settings_type type) const; - - /** Returns the value of the setting type.*/ - std::string GetSetting(emu_settings_type type) const; - - /** Sets the setting type to a given value.*/ - void SetSetting(emu_settings_type type, const std::string& val) const; - - /** Try to find the settings type for a given string.*/ - emu_settings_type FindSettingsType(const cfg::_base* node) const; - - /** Gets all the renderer info for gpu settings.*/ - render_creator* m_render_creator = nullptr; - - /** Gets a list of all the microphones available.*/ - microphone_creator m_microphone_creator; - - /** Gets a list of all the midi devices available.*/ - midi_creator m_midi_creator; - - /** Loads the settings from path.*/ - void LoadSettings(const std::string& title_id = "", bool create_config_from_global = true); - - /** Fixes all registered invalid settings after asking the user for permission.*/ - void OpenCorrectionDialog(QWidget* parent = Q_NULLPTR); - - /** Get a localized and therefore freely adjustable version of the string used in config.yml.*/ - QString GetLocalizedSetting(const QString& original, emu_settings_type type, int index, bool strict) const; - - /** Get a localized and therefore freely adjustable version of the string used in config.yml.*/ - std::string GetLocalizedSetting(const std::string& original, emu_settings_type type, int index, bool strict) const; - - /** Get a localized and therefore freely adjustable version of the string used in config.yml.*/ - std::string GetLocalizedSetting(const cfg::_base* node, u32 index) const; - - /** Validates the settings and logs unused entries or cleans up the yaml*/ - bool ValidateSettings(bool cleanup); - - /** Resets the current settings to the global default. This includes all connected widgets. */ - void RestoreDefaults(); - -Q_SIGNALS: - void RestoreDefaultsSignal(); - -public Q_SLOTS: - /** Writes the unsaved settings to file. Used in settings dialog on accept.*/ - void SaveSettings() const; - -private: - YAML::Node m_default_settings; // The default settings as a YAML node. - YAML::Node m_current_settings; // The current settings as a YAML node. - std::string m_title_id; -}; diff --git a/rpcs3qt-legacy/emu_settings_type.h b/rpcs3qt-legacy/emu_settings_type.h deleted file mode 100644 index 22895042f..000000000 --- a/rpcs3qt-legacy/emu_settings_type.h +++ /dev/null @@ -1,414 +0,0 @@ -#pragma once - -#include -#include - -// Node location -using cfg_location = std::vector; - -enum class emu_settings_type -{ - // Core - PPUDecoder, - SPUDecoder, - HookStaticFuncs, - ThreadSchedulerMode, - SPULoopDetection, - PreferredSPUThreads, - PPUDebug, - SPUDebug, - MFCDebug, - MaxLLVMThreads, - LLVMPrecompilation, - EnableTSX, - AccurateSpuDMA, - AccurateClineStores, - AccurateRSXAccess, - FIFOAccuracy, - XFloatAccuracy, - AccuratePPU128Loop, - MFCCommandsShuffling, - NumPPUThreads, - SetDAZandFTZ, - SPUBlockSize, - SPUCache, - DebugConsoleMode, - SilenceAllLogs, - SuspendEmulationSavestateMode, - CompatibleEmulationSavestateMode, - StartSavestatePaused, - MaxSPURSThreads, - SleepTimersAccuracy, - ClocksScale, - PerformanceReport, - FullWidthAVX512, - PPUNJFixup, - AccurateDFMA, - AccuratePPUSAT, - AccuratePPUNJ, - FixupPPUVNAN, - AccuratePPUVNAN, - AccuratePPUFPCC, - MaxPreemptCount, - SPUProfiler, - DisableSpinOptimization, - - // Graphics - Renderer, - Resolution, - AspectRatio, - FrameLimit, - MSAA, - LogShaderPrograms, - WriteDepthBuffer, - WriteColorBuffers, - ReadColorBuffers, - ReadDepthBuffer, - HandleRSXTiledMemory, - VSync, - DebugOutput, - DebugOverlay, - RenderdocCompatibility, - GPUTextureScaling, - StretchToDisplayArea, - VulkanAdapter, - ForceHighpZ, - StrictRenderingMode, - DisableVertexCache, - DisableOcclusionQueries, - DisableVideoOutput, - DisableFIFOReordering, - StrictTextureFlushing, - ShaderPrecisionQuality, - StereoRenderMode, - AnisotropicFilterOverride, - TextureLodBias, - ResolutionScale, - MinimumScalableDimension, - FsrSharpeningStrength, - ExclusiveFullscreenMode, - ForceCPUBlitEmulation, - DisableOnDiskShaderCache, - DisableVulkanMemAllocator, - ShaderMode, - ShaderCompilerNumThreads, - MultithreadedRSX, - VBlankRate, - VBlankNTSCFixup, - RelaxedZCULL, - PreciseZCULL, - DriverWakeUpDelay, - VulkanAsyncTextureUploads, - VulkanAsyncSchedulerDriver, - AllowHostGPULabels, - DisableMSLFastMath, - OutputScalingMode, - ForceHwMSAAResolve, - DisableAsyncHostMM, - - // Performance Overlay - PerfOverlayEnabled, - PerfOverlayFramerateGraphEnabled, - PerfOverlayFrametimeGraphEnabled, - PerfOverlayFramerateDatapoints, - PerfOverlayFrametimeDatapoints, - PerfOverlayDetailLevel, - PerfOverlayFramerateDetailLevel, - PerfOverlayFrametimeDetailLevel, - PerfOverlayPosition, - PerfOverlayUpdateInterval, - PerfOverlayFontSize, - PerfOverlayOpacity, - PerfOverlayMarginX, - PerfOverlayMarginY, - PerfOverlayCenterX, - PerfOverlayCenterY, - - // Shader Loading Dialog - ShaderLoadBgEnabled, - ShaderLoadBgDarkening, - ShaderLoadBgBlur, - - // Audio - AudioRenderer, - DumpToFile, - ConvertTo16Bit, - AudioFormat, - AudioFormats, - AudioProvider, - AudioAvport, - AudioDevice, - AudioChannelLayout, - MasterVolume, - EnableBuffering, - AudioBufferDuration, - EnableTimeStretching, - TimeStretchingThreshold, - MicrophoneType, - MicrophoneDevices, - MusicHandler, - - // Input / Output - BackgroundInput, - ShowMoveCursor, - LockOvlIptToP1, - PadHandlerMode, - PadConnection, - KeyboardHandler, - MouseHandler, - Camera, - CameraType, - CameraFlip, - CameraID, - Move, - Buzz, - Turntable, - GHLtar, - MidiDevices, - SDLMappings, - IoDebugOverlay, - - // Misc - ExitRPCS3OnFinish, - StartOnBoot, - PauseOnFocusLoss, - StartGameFullscreen, - PreventDisplaySleep, - ShowTrophyPopups, - ShowRpcnPopups, - UseNativeInterface, - ShowShaderCompilationHint, - ShowPPUCompilationHint, - ShowAutosaveAutoloadHint, - ShowPressureIntensityToggleHint, - ShowAnalogLimiterToggleHint, - ShowMouseAndKeyboardToggleHint, - WindowTitleFormat, - PauseDuringHomeMenu, - - // Network - InternetStatus, - DNSAddress, - IpSwapList, - PSNStatus, - BindAddress, - EnableUpnp, - PSNCountry, - - // System - LicenseArea, - Language, - KeyboardType, - EnterButtonAssignment, - EnableHostRoot, - EmptyHdd0Tmp, - LimitCacheSize, - MaximumCacheSize, - ConsoleTimeOffset, -}; - -/** A helper map that keeps track of where a given setting type is located*/ -inline static const std::map settings_location = - { - // Core Tab - {emu_settings_type::PPUDecoder, {"Core", "PPU Decoder"}}, - {emu_settings_type::SPUDecoder, {"Core", "SPU Decoder"}}, - {emu_settings_type::HookStaticFuncs, {"Core", "Hook static functions"}}, - {emu_settings_type::ThreadSchedulerMode, {"Core", "Thread Scheduler Mode"}}, - {emu_settings_type::SPULoopDetection, {"Core", "SPU loop detection"}}, - {emu_settings_type::PreferredSPUThreads, {"Core", "Preferred SPU Threads"}}, - {emu_settings_type::PPUDebug, {"Core", "PPU Debug"}}, - {emu_settings_type::SPUDebug, {"Core", "SPU Debug"}}, - {emu_settings_type::MFCDebug, {"Core", "MFC Debug"}}, - {emu_settings_type::MaxLLVMThreads, {"Core", "Max LLVM Compile Threads"}}, - {emu_settings_type::LLVMPrecompilation, {"Core", "LLVM Precompilation"}}, - {emu_settings_type::EnableTSX, {"Core", "Enable TSX"}}, - {emu_settings_type::AccurateSpuDMA, {"Core", "Accurate SPU DMA"}}, - {emu_settings_type::AccurateClineStores, {"Core", "Accurate Cache Line Stores"}}, - {emu_settings_type::AccurateRSXAccess, {"Core", "Accurate RSX reservation access"}}, - {emu_settings_type::FIFOAccuracy, {"Core", "RSX FIFO Accuracy"}}, - {emu_settings_type::XFloatAccuracy, {"Core", "XFloat Accuracy"}}, - {emu_settings_type::MFCCommandsShuffling, {"Core", "MFC Commands Shuffling Limit"}}, - {emu_settings_type::SetDAZandFTZ, {"Core", "Set DAZ and FTZ"}}, - {emu_settings_type::SPUBlockSize, {"Core", "SPU Block Size"}}, - {emu_settings_type::SPUCache, {"Core", "SPU Cache"}}, - {emu_settings_type::DebugConsoleMode, {"Core", "Debug Console Mode"}}, - {emu_settings_type::MaxSPURSThreads, {"Core", "Max SPURS Threads"}}, - {emu_settings_type::SleepTimersAccuracy, {"Core", "Sleep Timers Accuracy"}}, - {emu_settings_type::ClocksScale, {"Core", "Clocks scale"}}, - {emu_settings_type::AccuratePPU128Loop, {"Core", "Accurate PPU 128-byte Reservation Op Max Length"}}, - {emu_settings_type::PerformanceReport, {"Core", "Enable Performance Report"}}, - {emu_settings_type::FullWidthAVX512, {"Core", "Full Width AVX-512"}}, - {emu_settings_type::NumPPUThreads, {"Core", "PPU Threads"}}, - {emu_settings_type::PPUNJFixup, {"Core", "PPU LLVM Java Mode Handling"}}, - {emu_settings_type::AccurateDFMA, {"Core", "Use Accurate DFMA"}}, - {emu_settings_type::AccuratePPUSAT, {"Core", "PPU Set Saturation Bit"}}, - {emu_settings_type::AccuratePPUNJ, {"Core", "PPU Accurate Non-Java Mode"}}, - {emu_settings_type::FixupPPUVNAN, {"Core", "PPU Fixup Vector NaN Values"}}, - {emu_settings_type::AccuratePPUVNAN, {"Core", "PPU Accurate Vector NaN Values"}}, - {emu_settings_type::AccuratePPUFPCC, {"Core", "PPU Set FPCC Bits"}}, - {emu_settings_type::MaxPreemptCount, {"Core", "Max CPU Preempt Count"}}, - {emu_settings_type::SPUProfiler, {"Core", "SPU Profiler"}}, - {emu_settings_type::DisableSpinOptimization, {"Core", "Disable SPU GETLLAR Spin Optimization"}}, - - // Graphics Tab - {emu_settings_type::Renderer, {"Video", "Renderer"}}, - {emu_settings_type::Resolution, {"Video", "Resolution"}}, - {emu_settings_type::AspectRatio, {"Video", "Aspect ratio"}}, - {emu_settings_type::FrameLimit, {"Video", "Frame limit"}}, - {emu_settings_type::MSAA, {"Video", "MSAA"}}, - {emu_settings_type::LogShaderPrograms, {"Video", "Log shader programs"}}, - {emu_settings_type::WriteDepthBuffer, {"Video", "Write Depth Buffer"}}, - {emu_settings_type::WriteColorBuffers, {"Video", "Write Color Buffers"}}, - {emu_settings_type::ReadColorBuffers, {"Video", "Read Color Buffers"}}, - {emu_settings_type::ReadDepthBuffer, {"Video", "Read Depth Buffer"}}, - {emu_settings_type::HandleRSXTiledMemory, {"Video", "Handle RSX Memory Tiling"}}, - {emu_settings_type::VSync, {"Video", "VSync"}}, - {emu_settings_type::DebugOutput, {"Video", "Debug output"}}, - {emu_settings_type::DebugOverlay, {"Video", "Debug overlay"}}, - {emu_settings_type::RenderdocCompatibility, {"Video", "Renderdoc Compatibility Mode"}}, - {emu_settings_type::GPUTextureScaling, {"Video", "Use GPU texture scaling"}}, - {emu_settings_type::StretchToDisplayArea, {"Video", "Stretch To Display Area"}}, - {emu_settings_type::ForceHighpZ, {"Video", "Force High Precision Z buffer"}}, - {emu_settings_type::StrictRenderingMode, {"Video", "Strict Rendering Mode"}}, - {emu_settings_type::DisableVertexCache, {"Video", "Disable Vertex Cache"}}, - {emu_settings_type::DisableOcclusionQueries, {"Video", "Disable ZCull Occlusion Queries"}}, - {emu_settings_type::DisableVideoOutput, {"Video", "Disable Video Output"}}, - {emu_settings_type::DisableFIFOReordering, {"Video", "Disable FIFO Reordering"}}, - {emu_settings_type::StereoRenderMode, {"Video", "3D Display Mode"}}, - {emu_settings_type::StrictTextureFlushing, {"Video", "Strict Texture Flushing"}}, - {emu_settings_type::ForceCPUBlitEmulation, {"Video", "Force CPU Blit"}}, - {emu_settings_type::DisableOnDiskShaderCache, {"Video", "Disable On-Disk Shader Cache"}}, - {emu_settings_type::DisableVulkanMemAllocator, {"Video", "Disable Vulkan Memory Allocator"}}, - {emu_settings_type::ShaderMode, {"Video", "Shader Mode"}}, - {emu_settings_type::ShaderCompilerNumThreads, {"Video", "Shader Compiler Threads"}}, - {emu_settings_type::ShaderPrecisionQuality, {"Video", "Shader Precision"}}, - {emu_settings_type::MultithreadedRSX, {"Video", "Multithreaded RSX"}}, - {emu_settings_type::RelaxedZCULL, {"Video", "Relaxed ZCULL Sync"}}, - {emu_settings_type::PreciseZCULL, {"Video", "Accurate ZCULL stats"}}, - {emu_settings_type::AnisotropicFilterOverride, {"Video", "Anisotropic Filter Override"}}, - {emu_settings_type::TextureLodBias, {"Video", "Texture LOD Bias Addend"}}, - {emu_settings_type::ResolutionScale, {"Video", "Resolution Scale"}}, - {emu_settings_type::MinimumScalableDimension, {"Video", "Minimum Scalable Dimension"}}, - {emu_settings_type::VulkanAdapter, {"Video", "Vulkan", "Adapter"}}, - {emu_settings_type::VBlankRate, {"Video", "Vblank Rate"}}, - {emu_settings_type::VBlankNTSCFixup, {"Video", "Vblank NTSC Fixup"}}, - {emu_settings_type::DriverWakeUpDelay, {"Video", "Driver Wake-Up Delay"}}, - {emu_settings_type::AllowHostGPULabels, {"Video", "Allow Host GPU Labels"}}, - {emu_settings_type::DisableMSLFastMath, {"Video", "Disable MSL Fast Math"}}, - {emu_settings_type::OutputScalingMode, {"Video", "Output Scaling Mode"}}, - {emu_settings_type::ForceHwMSAAResolve, {"Video", "Force Hardware MSAA Resolve"}}, - {emu_settings_type::DisableAsyncHostMM, {"Video", "Disable Asynchronous Memory Manager"}}, - - // Vulkan - {emu_settings_type::VulkanAsyncTextureUploads, {"Video", "Vulkan", "Asynchronous Texture Streaming 2"}}, - {emu_settings_type::VulkanAsyncSchedulerDriver, {"Video", "Vulkan", "Asynchronous Queue Scheduler"}}, - {emu_settings_type::FsrSharpeningStrength, {"Video", "Vulkan", "FidelityFX CAS Sharpening Intensity"}}, - {emu_settings_type::ExclusiveFullscreenMode, {"Video", "Vulkan", "Exclusive Fullscreen Mode"}}, - - // Performance Overlay - {emu_settings_type::PerfOverlayEnabled, {"Video", "Performance Overlay", "Enabled"}}, - {emu_settings_type::PerfOverlayFramerateGraphEnabled, {"Video", "Performance Overlay", "Enable Framerate Graph"}}, - {emu_settings_type::PerfOverlayFrametimeGraphEnabled, {"Video", "Performance Overlay", "Enable Frametime Graph"}}, - {emu_settings_type::PerfOverlayFramerateDatapoints, {"Video", "Performance Overlay", "Framerate datapoints"}}, - {emu_settings_type::PerfOverlayFrametimeDatapoints, {"Video", "Performance Overlay", "Frametime datapoints"}}, - {emu_settings_type::PerfOverlayDetailLevel, {"Video", "Performance Overlay", "Detail level"}}, - {emu_settings_type::PerfOverlayFramerateDetailLevel, {"Video", "Performance Overlay", "Framerate graph detail level"}}, - {emu_settings_type::PerfOverlayFrametimeDetailLevel, {"Video", "Performance Overlay", "Frametime graph detail level"}}, - {emu_settings_type::PerfOverlayPosition, {"Video", "Performance Overlay", "Position"}}, - {emu_settings_type::PerfOverlayUpdateInterval, {"Video", "Performance Overlay", "Metrics update interval (ms)"}}, - {emu_settings_type::PerfOverlayFontSize, {"Video", "Performance Overlay", "Font size (px)"}}, - {emu_settings_type::PerfOverlayOpacity, {"Video", "Performance Overlay", "Opacity (%)"}}, - {emu_settings_type::PerfOverlayMarginX, {"Video", "Performance Overlay", "Horizontal Margin (px)"}}, - {emu_settings_type::PerfOverlayMarginY, {"Video", "Performance Overlay", "Vertical Margin (px)"}}, - {emu_settings_type::PerfOverlayCenterX, {"Video", "Performance Overlay", "Center Horizontally"}}, - {emu_settings_type::PerfOverlayCenterY, {"Video", "Performance Overlay", "Center Vertically"}}, - - // Shader Loading Dialog - {emu_settings_type::ShaderLoadBgEnabled, {"Video", "Shader Loading Dialog", "Allow custom background"}}, - {emu_settings_type::ShaderLoadBgDarkening, {"Video", "Shader Loading Dialog", "Darkening effect strength"}}, - {emu_settings_type::ShaderLoadBgBlur, {"Video", "Shader Loading Dialog", "Blur effect strength"}}, - - // Audio - {emu_settings_type::AudioRenderer, {"Audio", "Renderer"}}, - {emu_settings_type::DumpToFile, {"Audio", "Dump to file"}}, - {emu_settings_type::ConvertTo16Bit, {"Audio", "Convert to 16 bit"}}, - {emu_settings_type::AudioFormat, {"Audio", "Audio Format"}}, - {emu_settings_type::AudioFormats, {"Audio", "Audio Formats"}}, - {emu_settings_type::AudioProvider, {"Audio", "Audio Provider"}}, - {emu_settings_type::AudioAvport, {"Audio", "RSXAudio Avport"}}, - {emu_settings_type::AudioDevice, {"Audio", "Audio Device"}}, - {emu_settings_type::AudioChannelLayout, {"Audio", "Audio Channel Layout"}}, - {emu_settings_type::MasterVolume, {"Audio", "Master Volume"}}, - {emu_settings_type::EnableBuffering, {"Audio", "Enable Buffering"}}, - {emu_settings_type::AudioBufferDuration, {"Audio", "Desired Audio Buffer Duration"}}, - {emu_settings_type::EnableTimeStretching, {"Audio", "Enable Time Stretching"}}, - {emu_settings_type::TimeStretchingThreshold, {"Audio", "Time Stretching Threshold"}}, - {emu_settings_type::MicrophoneType, {"Audio", "Microphone Type"}}, - {emu_settings_type::MicrophoneDevices, {"Audio", "Microphone Devices"}}, - {emu_settings_type::MusicHandler, {"Audio", "Music Handler"}}, - - // Input / Output - {emu_settings_type::BackgroundInput, {"Input/Output", "Background input enabled"}}, - {emu_settings_type::ShowMoveCursor, {"Input/Output", "Show move cursor"}}, - {emu_settings_type::LockOvlIptToP1, {"Input/Output", "Lock overlay input to player one"}}, - {emu_settings_type::PadHandlerMode, {"Input/Output", "Pad handler mode"}}, - {emu_settings_type::PadConnection, {"Input/Output", "Keep pads connected"}}, - {emu_settings_type::KeyboardHandler, {"Input/Output", "Keyboard"}}, - {emu_settings_type::MouseHandler, {"Input/Output", "Mouse"}}, - {emu_settings_type::Camera, {"Input/Output", "Camera"}}, - {emu_settings_type::CameraType, {"Input/Output", "Camera type"}}, - {emu_settings_type::CameraFlip, {"Input/Output", "Camera flip"}}, - {emu_settings_type::CameraID, {"Input/Output", "Camera ID"}}, - {emu_settings_type::Move, {"Input/Output", "Move"}}, - {emu_settings_type::Buzz, {"Input/Output", "Buzz emulated controller"}}, - {emu_settings_type::Turntable, {"Input/Output", "Turntable emulated controller"}}, - {emu_settings_type::GHLtar, {"Input/Output", "GHLtar emulated controller"}}, - {emu_settings_type::MidiDevices, {"Input/Output", "Emulated Midi devices"}}, - {emu_settings_type::SDLMappings, {"Input/Output", "Load SDL GameController Mappings"}}, - {emu_settings_type::IoDebugOverlay, {"Input/Output", "IO Debug overlay"}}, - - // Misc - {emu_settings_type::ExitRPCS3OnFinish, {"Miscellaneous", "Exit RPCS3 when process finishes"}}, - {emu_settings_type::StartOnBoot, {"Miscellaneous", "Automatically start games after boot"}}, - {emu_settings_type::PauseOnFocusLoss, {"Miscellaneous", "Pause emulation on RPCS3 focus loss"}}, - {emu_settings_type::StartGameFullscreen, {"Miscellaneous", "Start games in fullscreen mode"}}, - {emu_settings_type::PreventDisplaySleep, {"Miscellaneous", "Prevent display sleep while running games"}}, - {emu_settings_type::ShowTrophyPopups, {"Miscellaneous", "Show trophy popups"}}, - {emu_settings_type::ShowRpcnPopups, {"Miscellaneous", "Show RPCN popups"}}, - {emu_settings_type::UseNativeInterface, {"Miscellaneous", "Use native user interface"}}, - {emu_settings_type::ShowShaderCompilationHint, {"Miscellaneous", "Show shader compilation hint"}}, - {emu_settings_type::ShowPPUCompilationHint, {"Miscellaneous", "Show PPU compilation hint"}}, - {emu_settings_type::ShowAutosaveAutoloadHint, {"Miscellaneous", "Show autosave/autoload hint"}}, - {emu_settings_type::ShowPressureIntensityToggleHint, {"Miscellaneous", "Show pressure intensity toggle hint"}}, - {emu_settings_type::ShowAnalogLimiterToggleHint, {"Miscellaneous", "Show analog limiter toggle hint"}}, - {emu_settings_type::ShowMouseAndKeyboardToggleHint, {"Miscellaneous", "Show mouse and keyboard toggle hint"}}, - {emu_settings_type::SilenceAllLogs, {"Miscellaneous", "Silence All Logs"}}, - {emu_settings_type::WindowTitleFormat, {"Miscellaneous", "Window Title Format"}}, - {emu_settings_type::PauseDuringHomeMenu, {"Miscellaneous", "Pause Emulation During Home Menu"}}, - - // Networking - {emu_settings_type::InternetStatus, {"Net", "Internet enabled"}}, - {emu_settings_type::DNSAddress, {"Net", "DNS address"}}, - {emu_settings_type::IpSwapList, {"Net", "IP swap list"}}, - {emu_settings_type::PSNStatus, {"Net", "PSN status"}}, - {emu_settings_type::BindAddress, {"Net", "Bind address"}}, - {emu_settings_type::EnableUpnp, {"Net", "UPNP Enabled"}}, - {emu_settings_type::PSNCountry, {"Net", "PSN Country"}}, - - // System - {emu_settings_type::LicenseArea, {"System", "License Area"}}, - {emu_settings_type::Language, {"System", "Language"}}, - {emu_settings_type::KeyboardType, {"System", "Keyboard Type"}}, - {emu_settings_type::EnterButtonAssignment, {"System", "Enter button assignment"}}, - {emu_settings_type::EnableHostRoot, {"VFS", "Enable /host_root/"}}, - {emu_settings_type::EmptyHdd0Tmp, {"VFS", "Empty /dev_hdd0/tmp/"}}, - {emu_settings_type::LimitCacheSize, {"VFS", "Limit disk cache size"}}, - {emu_settings_type::MaximumCacheSize, {"VFS", "Disk cache maximum size (MB)"}}, - {emu_settings_type::ConsoleTimeOffset, {"System", "Console time offset (s)"}}, - - // Savestates - {emu_settings_type::SuspendEmulationSavestateMode, {"Savestate", "Suspend Emulation Savestate Mode"}}, - {emu_settings_type::CompatibleEmulationSavestateMode, {"Savestate", "Compatible Savestate Mode"}}, - {emu_settings_type::StartSavestatePaused, {"Savestate", "Start Paused"}}, -}; diff --git a/rpcs3qt-legacy/emulated_pad_settings_dialog.cpp b/rpcs3qt-legacy/emulated_pad_settings_dialog.cpp deleted file mode 100644 index 4992298d2..000000000 --- a/rpcs3qt-legacy/emulated_pad_settings_dialog.cpp +++ /dev/null @@ -1,609 +0,0 @@ -#include "stdafx.h" -#include "emulated_pad_settings_dialog.h" -#include "localized_emu.h" -#include "Input/raw_mouse_config.h" -#include "Emu/Io/mouse_config.h" -#include "Emu/Io/buzz_config.h" -#include "Emu/Io/gem_config.h" -#include "Emu/Io/ghltar_config.h" -#include "Emu/Io/guncon3_config.h" -#include "Emu/Io/topshotelite_config.h" -#include "Emu/Io/topshotfearmaster_config.h" -#include "Emu/Io/turntable_config.h" -#include "Emu/Io/usio_config.h" -#include "util/asm.hpp" - -#include -#include -#include -#include -#include -#include - -enum button_role -{ - button = Qt::UserRole, - emulated_button -}; - -emulated_pad_settings_dialog::emulated_pad_settings_dialog(pad_type type, QWidget* parent) - : QDialog(parent), m_type(type) -{ - setObjectName("emulated_pad_settings_dialog"); - setAttribute(Qt::WA_DeleteOnClose); - setAttribute(Qt::WA_StyledBackground); - setModal(true); - - QVBoxLayout* v_layout = new QVBoxLayout(this); - - QTabWidget* tabs = new QTabWidget(); - tabs->setUsesScrollButtons(false); - - QDialogButtonBox* buttons = new QDialogButtonBox(this); - buttons->setStandardButtons(QDialogButtonBox::Apply | QDialogButtonBox::Cancel | QDialogButtonBox::Save | QDialogButtonBox::RestoreDefaults); - - connect(buttons, &QDialogButtonBox::clicked, this, [this, buttons](QAbstractButton* button) - { - if (button == buttons->button(QDialogButtonBox::Apply)) - { - save_config(); - } - else if (button == buttons->button(QDialogButtonBox::Save)) - { - save_config(); - accept(); - } - else if (button == buttons->button(QDialogButtonBox::RestoreDefaults)) - { - if (QMessageBox::question(this, tr("Confirm Reset"), tr("Reset all buttons of all players?")) != QMessageBox::Yes) - return; - reset_config(); - } - else if (button == buttons->button(QDialogButtonBox::Cancel)) - { - // Restore config - load_config(); - reject(); - } - }); - - load_config(); - - switch (m_type) - { - case emulated_pad_settings_dialog::pad_type::buzz: - setWindowTitle(tr("Configure Emulated Buzz")); - add_tabs(tabs); - break; - case emulated_pad_settings_dialog::pad_type::turntable: - setWindowTitle(tr("Configure Emulated Turntable")); - add_tabs(tabs); - break; - case emulated_pad_settings_dialog::pad_type::ghltar: - setWindowTitle(tr("Configure Emulated GHLtar")); - add_tabs(tabs); - break; - case emulated_pad_settings_dialog::pad_type::usio: - setWindowTitle(tr("Configure Emulated USIO")); - add_tabs(tabs); - break; - case emulated_pad_settings_dialog::pad_type::gem: - setWindowTitle(tr("Configure Emulated PS Move (Real)")); - add_tabs(tabs); - break; - case emulated_pad_settings_dialog::pad_type::ds3gem: - setWindowTitle(tr("Configure Emulated PS Move (Fake)")); - add_tabs(tabs); - break; - case emulated_pad_settings_dialog::pad_type::mousegem: - setWindowTitle(tr("Configure Emulated PS Move (Mouse)")); - add_tabs(tabs); - break; - case emulated_pad_settings_dialog::pad_type::guncon3: - setWindowTitle(tr("Configure Emulated GunCon 3")); - add_tabs(tabs); - break; - case emulated_pad_settings_dialog::pad_type::topshotelite: - setWindowTitle(tr("Configure Emulated Top Shot Elite")); - add_tabs(tabs); - break; - case emulated_pad_settings_dialog::pad_type::topshotfearmaster: - setWindowTitle(tr("Configure Emulated Top Shot Fearmaster")); - add_tabs(tabs); - break; - } - - v_layout->addWidget(tabs); - v_layout->addWidget(buttons); - setLayout(v_layout); -} - -template -void emulated_pad_settings_dialog::add_tabs(QTabWidget* tabs) -{ - ensure(!!tabs); - - std::set ignored_values; - - const auto remove_value = [&ignored_values](int value) - { - ignored_values.insert(static_cast(value)); - }; - - usz players = 0; - switch (m_type) - { - case pad_type::buzz: - players = g_cfg_buzz.players.size(); - break; - case pad_type::turntable: - players = g_cfg_turntable.players.size(); - break; - case pad_type::ghltar: - players = g_cfg_ghltar.players.size(); - break; - case pad_type::usio: - players = g_cfg_usio.players.size(); - break; - case pad_type::gem: - players = g_cfg_gem_real.players.size(); - - // Ignore combo, x and y axis - remove_value(static_cast(gem_btn::x_axis)); - remove_value(static_cast(gem_btn::y_axis)); - for (int i = static_cast(gem_btn::combo_begin); i <= static_cast(gem_btn::combo_end); i++) - { - remove_value(i); - } - break; - case pad_type::ds3gem: - players = g_cfg_gem_fake.players.size(); - - // Ignore combo - for (int i = static_cast(gem_btn::combo_begin); i <= static_cast(gem_btn::combo_end); i++) - { - remove_value(i); - } - break; - case pad_type::mousegem: - players = g_cfg_gem_mouse.players.size(); - - // Ignore x and y axis - remove_value(static_cast(gem_btn::x_axis)); - remove_value(static_cast(gem_btn::y_axis)); - break; - case pad_type::guncon3: - players = g_cfg_guncon3.players.size(); - break; - case pad_type::topshotelite: - players = g_cfg_topshotelite.players.size(); - break; - case pad_type::topshotfearmaster: - players = g_cfg_topshotfearmaster.players.size(); - break; - } - - constexpr u32 max_items_per_column = 6; - const int count = static_cast(T::count) - static_cast(ignored_values.size()); - int rows = count; - - for (u32 cols = 1; utils::aligned_div(static_cast(count), cols) > max_items_per_column;) - { - rows = utils::aligned_div(static_cast(count), ++cols); - } - - m_combos.resize(players); - - const bool show_mouse_legend = m_type == pad_type::mousegem; - - if (show_mouse_legend) - { - if (!g_cfg_mouse.load()) - { - cfg_log.notice("Could not restore mouse config. Using defaults."); - } - - if (!g_cfg_raw_mouse.load()) - { - cfg_log.notice("Could not restore raw mouse config. Using defaults."); - } - } - - for (usz player = 0; player < players; player++) - { - // Create grid with all buttons - QGridLayout* grid_layout = new QGridLayout(this); - - for (int i = 0, row = 0, col = 0; i < static_cast(T::count); i++) - { - if (ignored_values.contains(i)) - continue; - - const T id = static_cast(i); - const QString name = QString::fromStdString(fmt::format("%s", id)); - - QHBoxLayout* h_layout = new QHBoxLayout(this); - QGroupBox* gb = new QGroupBox(name, this); - QComboBox* combo = new QComboBox; - - if constexpr (std::is_same_v) - { - const gem_btn btn = static_cast(i); - if (btn >= gem_btn::combo_begin && btn <= gem_btn::combo_end) - { - gb->setToolTip(tr("Press the \"Combo\" button in combination with any of the other combo buttons to trigger their related PS Move button.\n" - "This can be useful if your device does not have enough regular buttons.")); - } - } - - // Add empty value - combo->addItem(""); - const int index = combo->findText(""); - combo->setItemData(index, static_cast(pad_button::pad_button_max_enum), button_role::button); - combo->setItemData(index, i, button_role::emulated_button); - - if (m_type != pad_type::mousegem) - { - for (int p = 0; p < static_cast(pad_button::pad_button_max_enum); p++) - { - const QString translated = localized_emu::translated_pad_button(static_cast(p)); - combo->addItem(translated); - const int index = combo->findText(translated); - combo->setItemData(index, p, button_role::button); - combo->setItemData(index, i, button_role::emulated_button); - } - } - - if (std::is_same_v || std::is_same_v || std::is_same_v || m_type == pad_type::mousegem) - { - for (int p = static_cast(pad_button::mouse_button_1); p <= static_cast(pad_button::mouse_button_8); p++) - { - const QString translated = localized_emu::translated_pad_button(static_cast(p)); - combo->addItem(translated); - const int index = combo->findText(translated); - combo->setItemData(index, p, button_role::button); - combo->setItemData(index, i, button_role::emulated_button); - } - } - - pad_button saved_btn_id = pad_button::pad_button_max_enum; - switch (m_type) - { - case pad_type::buzz: - saved_btn_id = ::at32(g_cfg_buzz.players, player)->get_pad_button(static_cast(id)); - break; - case pad_type::turntable: - saved_btn_id = ::at32(g_cfg_turntable.players, player)->get_pad_button(static_cast(id)); - break; - case pad_type::ghltar: - saved_btn_id = ::at32(g_cfg_ghltar.players, player)->get_pad_button(static_cast(id)); - break; - case pad_type::usio: - saved_btn_id = ::at32(g_cfg_usio.players, player)->get_pad_button(static_cast(id)); - break; - case pad_type::gem: - saved_btn_id = ::at32(g_cfg_gem_real.players, player)->get_pad_button(static_cast(id)); - break; - case pad_type::ds3gem: - saved_btn_id = ::at32(g_cfg_gem_fake.players, player)->get_pad_button(static_cast(id)); - break; - case pad_type::mousegem: - saved_btn_id = ::at32(g_cfg_gem_mouse.players, player)->get_pad_button(static_cast(id)); - break; - case pad_type::guncon3: - saved_btn_id = ::at32(g_cfg_guncon3.players, player)->get_pad_button(static_cast(id)); - break; - case pad_type::topshotelite: - saved_btn_id = ::at32(g_cfg_topshotelite.players, player)->get_pad_button(static_cast(id)); - break; - case pad_type::topshotfearmaster: - saved_btn_id = ::at32(g_cfg_topshotfearmaster.players, player)->get_pad_button(static_cast(id)); - break; - } - - combo->setCurrentIndex(combo->findData(static_cast(saved_btn_id))); - - connect(combo, QOverload::of(&QComboBox::currentIndexChanged), this, [this, player, id, combo](int index) - { - if (index < 0 || !combo) - return; - - const QVariant data = combo->itemData(index, button_role::button); - if (!data.isValid() || !data.canConvert()) - return; - - const pad_button btn_id = static_cast(data.toInt()); - - switch (m_type) - { - case pad_type::buzz: - ::at32(g_cfg_buzz.players, player)->set_button(static_cast(id), btn_id); - break; - case pad_type::turntable: - ::at32(g_cfg_turntable.players, player)->set_button(static_cast(id), btn_id); - break; - case pad_type::ghltar: - ::at32(g_cfg_ghltar.players, player)->set_button(static_cast(id), btn_id); - break; - case pad_type::usio: - ::at32(g_cfg_usio.players, player)->set_button(static_cast(id), btn_id); - break; - case pad_type::gem: - ::at32(g_cfg_gem_real.players, player)->set_button(static_cast(id), btn_id); - break; - case pad_type::ds3gem: - ::at32(g_cfg_gem_fake.players, player)->set_button(static_cast(id), btn_id); - break; - case pad_type::mousegem: - ::at32(g_cfg_gem_mouse.players, player)->set_button(static_cast(id), btn_id); - break; - case pad_type::guncon3: - ::at32(g_cfg_guncon3.players, player)->set_button(static_cast(id), btn_id); - break; - case pad_type::topshotelite: - ::at32(g_cfg_topshotelite.players, player)->set_button(static_cast(id), btn_id); - break; - case pad_type::topshotfearmaster: - ::at32(g_cfg_topshotfearmaster.players, player)->set_button(static_cast(id), btn_id); - break; - } - }); - - if (row >= rows) - { - row = 0; - col++; - } - - ::at32(m_combos, player).push_back(combo); - h_layout->addWidget(combo); - gb->setLayout(h_layout); - grid_layout->addWidget(gb, row, col); - - row++; - } - - QVBoxLayout* v_layout = new QVBoxLayout(this); - - // Create a legend of the current mouse settings - if (show_mouse_legend) - { - QHBoxLayout* legend_layout = new QHBoxLayout(this); - if (player == 0) - { - std::string basic_mouse_settings; - fmt::append(basic_mouse_settings, "1: %s\n", g_cfg_mouse.mouse_button_1.to_string()); - fmt::append(basic_mouse_settings, "2: %s\n", g_cfg_mouse.mouse_button_2.to_string()); - fmt::append(basic_mouse_settings, "3: %s\n", g_cfg_mouse.mouse_button_3.to_string()); - fmt::append(basic_mouse_settings, "4: %s\n", g_cfg_mouse.mouse_button_4.to_string()); - fmt::append(basic_mouse_settings, "5: %s\n", g_cfg_mouse.mouse_button_5.to_string()); - fmt::append(basic_mouse_settings, "6: %s\n", g_cfg_mouse.mouse_button_6.to_string()); - fmt::append(basic_mouse_settings, "7: %s\n", g_cfg_mouse.mouse_button_7.to_string()); - fmt::append(basic_mouse_settings, "8: %s", g_cfg_mouse.mouse_button_8.to_string()); - - QGroupBox* gb_legend_basic = new QGroupBox(tr("Current Basic Mouse Config"), this); - QVBoxLayout* gb_legend_basic_layout = new QVBoxLayout(this); - gb_legend_basic_layout->addWidget(new QLabel(QString::fromStdString(basic_mouse_settings), this)); - gb_legend_basic->setLayout(gb_legend_basic_layout); - legend_layout->addWidget(gb_legend_basic); - } - { - std::string raw_mouse_settings; - const auto& raw_cfg = *ensure(::at32(g_cfg_raw_mouse.players, player)); - fmt::append(raw_mouse_settings, "1: %s\n", raw_mouse_config::get_button_name(raw_cfg.mouse_button_1.to_string())); - fmt::append(raw_mouse_settings, "2: %s\n", raw_mouse_config::get_button_name(raw_cfg.mouse_button_2.to_string())); - fmt::append(raw_mouse_settings, "3: %s\n", raw_mouse_config::get_button_name(raw_cfg.mouse_button_3.to_string())); - fmt::append(raw_mouse_settings, "4: %s\n", raw_mouse_config::get_button_name(raw_cfg.mouse_button_4.to_string())); - fmt::append(raw_mouse_settings, "5: %s\n", raw_mouse_config::get_button_name(raw_cfg.mouse_button_5.to_string())); - fmt::append(raw_mouse_settings, "6: %s\n", raw_mouse_config::get_button_name(raw_cfg.mouse_button_6.to_string())); - fmt::append(raw_mouse_settings, "7: %s\n", raw_mouse_config::get_button_name(raw_cfg.mouse_button_7.to_string())); - fmt::append(raw_mouse_settings, "8: %s", raw_mouse_config::get_button_name(raw_cfg.mouse_button_8.to_string())); - - QGroupBox* gb_legend_raw = new QGroupBox(tr("Current Raw Mouse Config"), this); - QVBoxLayout* gb_legend_raw_layout = new QVBoxLayout(this); - gb_legend_raw_layout->addWidget(new QLabel(QString::fromStdString(raw_mouse_settings), this)); - gb_legend_raw->setLayout(gb_legend_raw_layout); - legend_layout->addWidget(gb_legend_raw); - } - v_layout->addLayout(legend_layout); - } - - v_layout->addLayout(grid_layout); - - QWidget* widget = new QWidget(this); - widget->setLayout(v_layout); - - tabs->addTab(widget, tr("Player %0").arg(player + 1)); - } -} - -void emulated_pad_settings_dialog::load_config() -{ - switch (m_type) - { - case emulated_pad_settings_dialog::pad_type::buzz: - if (!g_cfg_buzz.load()) - { - cfg_log.notice("Could not load buzz config. Using defaults."); - } - break; - case emulated_pad_settings_dialog::pad_type::turntable: - if (!g_cfg_turntable.load()) - { - cfg_log.notice("Could not load turntable config. Using defaults."); - } - break; - case emulated_pad_settings_dialog::pad_type::ghltar: - if (!g_cfg_ghltar.load()) - { - cfg_log.notice("Could not load ghltar config. Using defaults."); - } - break; - case emulated_pad_settings_dialog::pad_type::usio: - if (!g_cfg_usio.load()) - { - cfg_log.notice("Could not load usio config. Using defaults."); - } - break; - case emulated_pad_settings_dialog::pad_type::gem: - if (!g_cfg_gem_real.load()) - { - cfg_log.notice("Could not load gem config. Using defaults."); - } - break; - case emulated_pad_settings_dialog::pad_type::ds3gem: - if (!g_cfg_gem_fake.load()) - { - cfg_log.notice("Could not load fake gem config. Using defaults."); - } - break; - case emulated_pad_settings_dialog::pad_type::mousegem: - if (!g_cfg_gem_mouse.load()) - { - cfg_log.notice("Could not load mouse gem config. Using defaults."); - } - break; - case emulated_pad_settings_dialog::pad_type::guncon3: - if (!g_cfg_guncon3.load()) - { - cfg_log.notice("Could not load guncon3 config. Using defaults."); - } - break; - case emulated_pad_settings_dialog::pad_type::topshotelite: - if (!g_cfg_topshotelite.load()) - { - cfg_log.notice("Could not load topshotelite config. Using defaults."); - } - break; - case emulated_pad_settings_dialog::pad_type::topshotfearmaster: - if (!g_cfg_topshotfearmaster.load()) - { - cfg_log.notice("Could not load topshotfearmaster config. Using defaults."); - } - break; - } -} - -void emulated_pad_settings_dialog::save_config() -{ - switch (m_type) - { - case emulated_pad_settings_dialog::pad_type::buzz: - g_cfg_buzz.save(); - break; - case emulated_pad_settings_dialog::pad_type::turntable: - g_cfg_turntable.save(); - break; - case emulated_pad_settings_dialog::pad_type::ghltar: - g_cfg_ghltar.save(); - break; - case emulated_pad_settings_dialog::pad_type::usio: - g_cfg_usio.save(); - break; - case emulated_pad_settings_dialog::pad_type::gem: - g_cfg_gem_real.save(); - break; - case emulated_pad_settings_dialog::pad_type::ds3gem: - g_cfg_gem_fake.save(); - break; - case emulated_pad_settings_dialog::pad_type::mousegem: - g_cfg_gem_mouse.save(); - break; - case emulated_pad_settings_dialog::pad_type::guncon3: - g_cfg_guncon3.save(); - break; - case emulated_pad_settings_dialog::pad_type::topshotelite: - g_cfg_topshotelite.save(); - break; - case emulated_pad_settings_dialog::pad_type::topshotfearmaster: - g_cfg_topshotfearmaster.save(); - break; - } -} - -void emulated_pad_settings_dialog::reset_config() -{ - switch (m_type) - { - case emulated_pad_settings_dialog::pad_type::buzz: - g_cfg_buzz.from_default(); - break; - case emulated_pad_settings_dialog::pad_type::turntable: - g_cfg_turntable.from_default(); - break; - case emulated_pad_settings_dialog::pad_type::ghltar: - g_cfg_ghltar.from_default(); - break; - case emulated_pad_settings_dialog::pad_type::usio: - g_cfg_usio.from_default(); - break; - case emulated_pad_settings_dialog::pad_type::gem: - g_cfg_gem_real.from_default(); - break; - case emulated_pad_settings_dialog::pad_type::ds3gem: - g_cfg_gem_fake.from_default(); - break; - case emulated_pad_settings_dialog::pad_type::mousegem: - g_cfg_gem_mouse.from_default(); - break; - case emulated_pad_settings_dialog::pad_type::guncon3: - g_cfg_guncon3.from_default(); - break; - case emulated_pad_settings_dialog::pad_type::topshotelite: - g_cfg_topshotelite.from_default(); - break; - case emulated_pad_settings_dialog::pad_type::topshotfearmaster: - g_cfg_topshotfearmaster.from_default(); - break; - } - - for (usz player = 0; player < m_combos.size(); player++) - { - for (QComboBox* combo : m_combos.at(player)) - { - if (!combo) - continue; - - const QVariant data = combo->itemData(0, button_role::emulated_button); - if (!data.isValid() || !data.canConvert()) - continue; - - pad_button def_btn_id = pad_button::pad_button_max_enum; - switch (m_type) - { - case pad_type::buzz: - def_btn_id = ::at32(g_cfg_buzz.players, player)->default_pad_button(static_cast(data.toInt())); - break; - case pad_type::turntable: - def_btn_id = ::at32(g_cfg_turntable.players, player)->default_pad_button(static_cast(data.toInt())); - break; - case pad_type::ghltar: - def_btn_id = ::at32(g_cfg_ghltar.players, player)->default_pad_button(static_cast(data.toInt())); - break; - case pad_type::usio: - def_btn_id = ::at32(g_cfg_usio.players, player)->default_pad_button(static_cast(data.toInt())); - break; - case pad_type::gem: - def_btn_id = ::at32(g_cfg_gem_real.players, player)->default_pad_button(static_cast(data.toInt())); - break; - case pad_type::ds3gem: - def_btn_id = ::at32(g_cfg_gem_fake.players, player)->default_pad_button(static_cast(data.toInt())); - break; - case pad_type::mousegem: - def_btn_id = ::at32(g_cfg_gem_mouse.players, player)->default_pad_button(static_cast(data.toInt())); - break; - case pad_type::guncon3: - def_btn_id = ::at32(g_cfg_guncon3.players, player)->default_pad_button(static_cast(data.toInt())); - break; - case pad_type::topshotelite: - def_btn_id = ::at32(g_cfg_topshotelite.players, player)->default_pad_button(static_cast(data.toInt())); - break; - case pad_type::topshotfearmaster: - def_btn_id = ::at32(g_cfg_topshotfearmaster.players, player)->default_pad_button(static_cast(data.toInt())); - break; - } - - combo->setCurrentIndex(combo->findData(static_cast(def_btn_id))); - } - } -} diff --git a/rpcs3qt-legacy/emulated_pad_settings_dialog.h b/rpcs3qt-legacy/emulated_pad_settings_dialog.h deleted file mode 100644 index 490dee3ae..000000000 --- a/rpcs3qt-legacy/emulated_pad_settings_dialog.h +++ /dev/null @@ -1,41 +0,0 @@ -#pragma once - -#include -#include -#include - -#include - -class emulated_pad_settings_dialog : public QDialog -{ - Q_OBJECT - -public: - enum class pad_type - { - buzz, - turntable, - ghltar, - usio, - gem, - ds3gem, - mousegem, - guncon3, - topshotelite, - topshotfearmaster, - }; - - emulated_pad_settings_dialog(pad_type type, QWidget* parent = nullptr); - -private: - template - void add_tabs(QTabWidget* tabs); - - void load_config(); - void save_config(); - void reset_config(); - - pad_type m_type; - - std::vector> m_combos; -}; diff --git a/rpcs3qt-legacy/fatal_error_dialog.cpp b/rpcs3qt-legacy/fatal_error_dialog.cpp deleted file mode 100644 index de6cffbd9..000000000 --- a/rpcs3qt-legacy/fatal_error_dialog.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include "fatal_error_dialog.h" - -#include -#include -#include - -const QString document_with_help_text = R"( - -

- %1
- %2
- https://github.com/RPCS3/rpcs3/wiki/How-to-ask-for-Support
- %3
-

-)"; - -const QString document_without_help_text = R"( - -

- %1
-

-)"; - -fatal_error_dialog::fatal_error_dialog(std::string_view text, bool is_html, bool include_help_text) : QMessageBox() -{ - const QString qstr = QString::fromUtf8(text.data(), text.size()); - const QString msg = is_html ? qstr : Qt::convertFromPlainText(qstr); - - QString document_body; - if (include_help_text) [[likely]] - { - document_body = document_with_help_text - .arg(msg) - .arg(tr("HOW TO REPORT ERRORS:")) - .arg(tr("Please, don't send incorrect reports. Thanks for understanding.")); - } - else - { - document_body = document_without_help_text.arg(msg); - } - -#ifndef __APPLE__ - setWindowIcon(QIcon(":/rpcs3.ico")); -#endif - setWindowTitle(tr("RPCS3: Fatal Error")); - setIcon(QMessageBox::Icon::Critical); - setTextFormat(Qt::TextFormat::RichText); - setText(document_body); - layout()->setSizeConstraint(QLayout::SetFixedSize); -} diff --git a/rpcs3qt-legacy/fatal_error_dialog.h b/rpcs3qt-legacy/fatal_error_dialog.h deleted file mode 100644 index 913daf284..000000000 --- a/rpcs3qt-legacy/fatal_error_dialog.h +++ /dev/null @@ -1,13 +0,0 @@ -#pragma once - -#include - -#include - -class fatal_error_dialog : public QMessageBox -{ - Q_OBJECT - -public: - explicit fatal_error_dialog(std::string_view text, bool is_html, bool include_help_text); -}; diff --git a/rpcs3qt-legacy/find_dialog.cpp b/rpcs3qt-legacy/find_dialog.cpp deleted file mode 100644 index e88b74266..000000000 --- a/rpcs3qt-legacy/find_dialog.cpp +++ /dev/null @@ -1,117 +0,0 @@ -#include "find_dialog.h" - -#include - -find_dialog::find_dialog(QPlainTextEdit* edit, QWidget* parent, Qt::WindowFlags f) : QDialog(parent, f), m_text_edit(edit) -{ - setWindowTitle(tr("Find string")); - - m_find_bar = new QLineEdit(); - m_find_bar->setPlaceholderText(tr("Search...")); - - m_label_count_lines = new QLabel(tr("Counted in lines: -")); - m_label_count_total = new QLabel(tr("Counted in total: -")); - - m_find_first = new QPushButton(tr("First")); - m_find_last = new QPushButton(tr("Last")); - m_find_next = new QPushButton(tr("Next")); - m_find_previous = new QPushButton(tr("Previous")); - - QHBoxLayout* count_layout = new QHBoxLayout(); - count_layout->addWidget(m_label_count_lines); - count_layout->addWidget(m_label_count_total); - - QHBoxLayout* button_layout = new QHBoxLayout(); - button_layout->addWidget(m_find_first); - button_layout->addWidget(m_find_last); - button_layout->addWidget(m_find_previous); - button_layout->addWidget(m_find_next); - - QVBoxLayout* layout = new QVBoxLayout(); - layout->addWidget(m_find_bar); - layout->addLayout(count_layout); - layout->addLayout(button_layout); - setLayout(layout); - - connect(m_find_first, &QPushButton::clicked, this, &find_dialog::find_first); - connect(m_find_last, &QPushButton::clicked, this, &find_dialog::find_last); - connect(m_find_next, &QPushButton::clicked, this, &find_dialog::find_next); - connect(m_find_previous, &QPushButton::clicked, this, &find_dialog::find_previous); - - m_find_next->setDefault(true); - - show(); -} - -int find_dialog::count_all() -{ - m_count_lines = 0; - m_count_total = 0; - - if (!m_text_edit || m_find_bar->text().isEmpty()) - { - show_count(); - return 0; - } - - const QTextCursor old_cursor = m_text_edit->textCursor(); - m_text_edit->moveCursor(QTextCursor::Start); - - int old_line_index = -1; - - while (m_text_edit->find(m_find_bar->text())) - { - m_count_total++; - const int new_line_index = m_text_edit->textCursor().blockNumber(); - - if (new_line_index != old_line_index) - { - m_count_lines++; - old_line_index = new_line_index; - } - } - - m_text_edit->setTextCursor(old_cursor); - show_count(); - return m_count_total; -} - -void find_dialog::find_first() -{ - if (count_all() <= 0) - return; - - m_text_edit->moveCursor(QTextCursor::Start); - m_text_edit->find(m_find_bar->text()); -} - -void find_dialog::find_last() -{ - if (count_all() <= 0) - return; - - m_text_edit->moveCursor(QTextCursor::End); - m_text_edit->find(m_find_bar->text(), QTextDocument::FindBackward); -} - -void find_dialog::find_next() -{ - if (count_all() <= 0) - return; - - m_text_edit->find(m_find_bar->text()); -} - -void find_dialog::find_previous() -{ - if (count_all() <= 0) - return; - - m_text_edit->find(m_find_bar->text(), QTextDocument::FindBackward); -} - -void find_dialog::show_count() const -{ - m_label_count_lines->setText(tr("Counted in lines: %0").arg(m_count_lines)); - m_label_count_total->setText(tr("Counted in total: %0").arg(m_count_total)); -} diff --git a/rpcs3qt-legacy/find_dialog.h b/rpcs3qt-legacy/find_dialog.h deleted file mode 100644 index cbf0cf582..000000000 --- a/rpcs3qt-legacy/find_dialog.h +++ /dev/null @@ -1,35 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include - -class find_dialog : public QDialog -{ - Q_OBJECT - -public: - find_dialog(QPlainTextEdit* edit, QWidget* parent = Q_NULLPTR, Qt::WindowFlags f = Qt::WindowFlags()); - -private: - int m_count_lines = 0; - int m_count_total = 0; - QLabel* m_label_count_lines; - QLabel* m_label_count_total; - QPlainTextEdit* m_text_edit; - QLineEdit* m_find_bar; - QPushButton* m_find_first; - QPushButton* m_find_last; - QPushButton* m_find_next; - QPushButton* m_find_previous; - -private Q_SLOTS: - int count_all(); - void find_first(); - void find_last(); - void find_next(); - void find_previous(); - void show_count() const; -}; diff --git a/rpcs3qt-legacy/flow_layout.cpp b/rpcs3qt-legacy/flow_layout.cpp deleted file mode 100644 index c70793bcb..000000000 --- a/rpcs3qt-legacy/flow_layout.cpp +++ /dev/null @@ -1,288 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the examples of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:BSD$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** BSD License Usage -** Alternatively, you may use this file under the terms of the BSD license -** as follows: -** -** "Redistribution and use in source and binary forms, with or without -** modification, are permitted provided that the following conditions are -** met: -** * Redistributions of source code must retain the above copyright -** notice, this list of conditions and the following disclaimer. -** * Redistributions in binary form must reproduce the above copyright -** notice, this list of conditions and the following disclaimer in -** the documentation and/or other materials provided with the -** distribution. -** * Neither the name of The Qt Company Ltd nor the names of its -** contributors may be used to endorse or promote products derived -** from this software without specific prior written permission. -** -** -** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include -#include "flow_layout.h" - -flow_layout::flow_layout(QWidget* parent, int margin, bool dynamic_spacing, int hSpacing, int vSpacing) - : QLayout(parent), m_dynamic_spacing(dynamic_spacing), m_h_space_initial(hSpacing), m_v_space_initial(vSpacing), m_h_space(hSpacing), m_v_space(vSpacing) -{ - setContentsMargins(margin, margin, margin, margin); -} - -flow_layout::flow_layout(int margin, bool dynamic_spacing, int hSpacing, int vSpacing) - : m_dynamic_spacing(dynamic_spacing), m_h_space_initial(hSpacing), m_v_space_initial(vSpacing), m_h_space(hSpacing), m_v_space(vSpacing) -{ - setContentsMargins(margin, margin, margin, margin); -} - -flow_layout::~flow_layout() -{ - clear(); -} - -void flow_layout::clear() -{ - // We can't use a ranged loop here, since deleting the widget will call takeAt on the layout. So let's also use takeAt. - while (QLayoutItem* item = takeAt(0)) - { - if (item) - { - delete item->widget(); - delete item; - } - } - m_item_list.clear(); - m_positions.clear(); -} - -void flow_layout::addItem(QLayoutItem* item) -{ - m_positions.append(position{.row = -1, .col = -1}); - m_item_list.append(item); -} - -int flow_layout::horizontalSpacing() const -{ - if (m_h_space >= 0) - { - return m_h_space; - } - - return smartSpacing(QStyle::PM_LayoutHorizontalSpacing); -} - -int flow_layout::verticalSpacing() const -{ - if (m_v_space >= 0) - { - return m_v_space; - } - - return smartSpacing(QStyle::PM_LayoutVerticalSpacing); -} - -int flow_layout::count() const -{ - return m_item_list.size(); -} - -QLayoutItem* flow_layout::itemAt(int index) const -{ - return m_item_list.value(index); -} - -QLayoutItem* flow_layout::takeAt(int index) -{ - if (index >= 0 && index < m_item_list.size()) - { - m_positions.takeAt(index); - return m_item_list.takeAt(index); - } - - return nullptr; -} - -Qt::Orientations flow_layout::expandingDirections() const -{ - return {}; -} - -bool flow_layout::hasHeightForWidth() const -{ - return true; -} - -int flow_layout::heightForWidth(int width) const -{ - return doLayout(QRect(0, 0, width, 0), true); -} - -void flow_layout::setGeometry(const QRect& rect) -{ - QLayout::setGeometry(rect); - doLayout(rect, false); -} - -QSize flow_layout::sizeHint() const -{ - return minimumSize(); -} - -QSize flow_layout::minimumSize() const -{ - QSize size; - for (const QLayoutItem* item : m_item_list) - { - if (item) - { - size = size.expandedTo(item->minimumSize()); - } - } - - const QMargins margins = contentsMargins(); - size += QSize(margins.left() + margins.right(), margins.top() + margins.bottom()); - return size; -} - -int flow_layout::doLayout(const QRect& rect, bool testOnly) const -{ - int left, top, right, bottom; - getContentsMargins(&left, &top, &right, &bottom); - const QRect effectiveRect = rect.adjusted(+left, +top, -right, -bottom); - int x = effectiveRect.x(); - int y = effectiveRect.y(); - int lineHeight = 0; - int row_count = 0; - int col_count = 0; - - if (m_dynamic_spacing) - { - const int available_width = effectiveRect.width(); - const int min_spacing = smartSpacing(QStyle::PM_LayoutHorizontalSpacing); - bool fits_into_width = true; - int width = 0; - int index = 0; - - for (; index < m_item_list.size(); index++) - { - if (QLayoutItem* item = m_item_list.at(index)) - { - const int new_width = width + item->sizeHint().width() + (width > 0 ? min_spacing : 0); - - if (new_width > effectiveRect.width()) - { - fits_into_width = false; - break; - } - - width = new_width; - } - } - - // Try to evenly distribute the items across the width - m_h_space = (index == 0) ? -1 : ((available_width - width) / index); - - if (fits_into_width) - { - // Make sure there aren't huge gaps between the items - m_h_space = smartSpacing(QStyle::PM_LayoutHorizontalSpacing); - } - } - else - { - m_h_space = m_h_space_initial; - } - - for (int i = 0, row = 0, col = 0; i < m_item_list.size(); i++) - { - QLayoutItem* item = m_item_list.at(i); - if (!item) - continue; - - const QWidget* wid = item->widget(); - if (!wid) - continue; - - int spaceX = horizontalSpacing(); - if (spaceX == -1) - spaceX = wid->style()->layoutSpacing(QSizePolicy::PushButton, QSizePolicy::PushButton, Qt::Horizontal); - - int spaceY = verticalSpacing(); - if (spaceY == -1) - spaceY = wid->style()->layoutSpacing(QSizePolicy::PushButton, QSizePolicy::PushButton, Qt::Vertical); - - int nextX = x + item->sizeHint().width() + spaceX; - if (nextX - spaceX > effectiveRect.right() && lineHeight > 0) - { - x = effectiveRect.x(); - y = y + lineHeight + spaceY; - nextX = x + item->sizeHint().width() + spaceX; - lineHeight = 0; - col = 0; - row++; - } - - position& pos = m_positions[i]; - pos.row = row; - pos.col = col++; - - row_count = std::max(row_count, pos.row + 1); - col_count = std::max(col_count, pos.col + 1); - - if (!testOnly) - item->setGeometry(QRect(QPoint(x, y), item->sizeHint())); - - x = nextX; - lineHeight = qMax(lineHeight, item->sizeHint().height()); - } - - m_rows = row_count; - m_cols = col_count; - - return y + lineHeight - rect.y() + bottom; -} - -int flow_layout::smartSpacing(QStyle::PixelMetric pm) const -{ - const QObject* parent = this->parent(); - if (!parent) - { - return -1; - } - - if (parent->isWidgetType()) - { - const QWidget* pw = static_cast(parent); - return pw->style()->pixelMetric(pm, nullptr, pw); - } - - return static_cast(parent)->spacing(); -} diff --git a/rpcs3qt-legacy/flow_layout.h b/rpcs3qt-legacy/flow_layout.h deleted file mode 100644 index f120a097c..000000000 --- a/rpcs3qt-legacy/flow_layout.h +++ /dev/null @@ -1,115 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the examples of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:BSD$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** BSD License Usage -** Alternatively, you may use this file under the terms of the BSD license -** as follows: -** -** "Redistribution and use in source and binary forms, with or without -** modification, are permitted provided that the following conditions are -** met: -** * Redistributions of source code must retain the above copyright -** notice, this list of conditions and the following disclaimer. -** * Redistributions in binary form must reproduce the above copyright -** notice, this list of conditions and the following disclaimer in -** the documentation and/or other materials provided with the -** distribution. -** * Neither the name of The Qt Company Ltd nor the names of its -** contributors may be used to endorse or promote products derived -** from this software without specific prior written permission. -** -** -** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#pragma once - -#include -#include -#include - -class flow_layout : public QLayout -{ -public: - struct position - { - int row{}; - int col{}; - }; - - explicit flow_layout(QWidget* parent, int margin = -1, bool dynamic_spacing = false, int hSpacing = -1, int vSpacing = -1); - explicit flow_layout(int margin = -1, bool dynamic_spacing = false, int hSpacing = -1, int vSpacing = -1); - ~flow_layout(); - - void clear(); - const QList& item_list() const - { - return m_item_list; - } - const QList& positions() const - { - return m_positions; - } - int rows() const - { - return m_rows; - } - int cols() const - { - return m_cols; - } - - void addItem(QLayoutItem* item) override; - Qt::Orientations expandingDirections() const override; - bool hasHeightForWidth() const override; - int heightForWidth(int) const override; - int count() const override; - QLayoutItem* itemAt(int index) const override; - QSize minimumSize() const override; - void setGeometry(const QRect& rect) override; - QSize sizeHint() const override; - QLayoutItem* takeAt(int index) override; - -private: - int horizontalSpacing() const; - int verticalSpacing() const; - int doLayout(const QRect& rect, bool testOnly) const; - int smartSpacing(QStyle::PixelMetric pm) const; - - QList m_item_list; - bool m_dynamic_spacing{}; - int m_h_space_initial{-1}; - int m_v_space_initial{-1}; - mutable int m_h_space{-1}; - mutable int m_v_space{-1}; - - mutable QList m_positions; - mutable int m_rows{}; - mutable int m_cols{}; -}; diff --git a/rpcs3qt-legacy/flow_widget.cpp b/rpcs3qt-legacy/flow_widget.cpp deleted file mode 100644 index 971d9e076..000000000 --- a/rpcs3qt-legacy/flow_widget.cpp +++ /dev/null @@ -1,257 +0,0 @@ -#include "stdafx.h" -#include "flow_widget.h" - -#include -#include -#include -#include - -flow_widget::flow_widget(QWidget* parent) - : QWidget(parent) -{ - m_flow_layout = new flow_layout(); - - QWidget* widget = new QWidget(this); - widget->setLayout(m_flow_layout); - widget->setObjectName("flow_widget_content"); - widget->setFocusProxy(this); - - m_scroll_area = new QScrollArea(this); - m_scroll_area->setWidget(widget); - m_scroll_area->setWidgetResizable(true); - - QVBoxLayout* layout = new QVBoxLayout(this); - layout->addWidget(m_scroll_area); - layout->setContentsMargins(0, 0, 0, 0); - setLayout(layout); -} - -flow_widget::~flow_widget() -{ -} - -void flow_widget::add_widget(flow_widget_item* widget) -{ - if (widget) - { - m_widgets.push_back(widget); - m_flow_layout->addWidget(widget); - - connect(widget, &flow_widget_item::navigate, this, &flow_widget::on_navigate); - connect(widget, &flow_widget_item::focused, this, &flow_widget::on_item_focus); - } -} - -void flow_widget::clear() -{ - m_widgets.clear(); - m_flow_layout->clear(); -} - -flow_widget_item* flow_widget::selected_item() const -{ - if (m_selected_index >= 0 && static_cast(m_selected_index) < m_widgets.size()) - { - return ::at32(m_widgets, m_selected_index); - } - - return nullptr; -} - -void flow_widget::paintEvent(QPaintEvent* /*event*/) -{ - // Needed for stylesheets to apply to QWidgets - QStyleOption option; - option.initFrom(this); - QPainter painter(this); - style()->drawPrimitive(QStyle::PE_Widget, &option, &painter, this); -} - -s64 flow_widget::find_item(const flow_layout::position& pos) -{ - if (pos.row < 0 || pos.col < 0) - { - return -1; - } - - const auto& positions = m_flow_layout->positions(); - - for (s64 i = 0; i < positions.size(); i++) - { - if (const auto& other = ::at32(positions, i); other.row == pos.row && other.col == pos.col) - { - return i; - } - } - - return -1; -} - -flow_layout::position flow_widget::find_item(flow_widget_item* item) -{ - if (item) - { - const auto& item_list = m_flow_layout->item_list(); - const auto& positions = m_flow_layout->positions(); - ensure(item_list.size() == positions.size()); - - for (s64 i = 0; i < item_list.size(); i++) - { - if (const auto& layout_item = ::at32(item_list, i); layout_item && layout_item->widget() == item) - { - return ::at32(positions, i); - } - } - } - - return flow_layout::position{.row = -1, .col = -1}; -} - -flow_layout::position flow_widget::find_next_item(flow_layout::position current_pos, flow_navigation value) -{ - if (current_pos.row >= 0 && current_pos.col >= 0 && m_flow_layout->rows() > 0 && m_flow_layout->cols() > 0) - { - switch (value) - { - case flow_navigation::up: - // Go up one row. - if (current_pos.row > 0) - { - current_pos.row--; - } - break; - case flow_navigation::down: - // Go down one row. Beware of last row which might have less columns. - for (const auto& pos : m_flow_layout->positions()) - { - ; - if (pos.col != current_pos.col) - continue; - if (pos.row == current_pos.row + 1) - { - current_pos.row = pos.row; - break; - } - } - break; - case flow_navigation::left: - // Go left one column. - if (current_pos.col > 0) - { - current_pos.col--; - } - break; - case flow_navigation::right: - // Go right one column. Beware of last row which might have less columns. - for (const auto& pos : m_flow_layout->positions()) - { - if (pos.row > current_pos.row) - break; - if (pos.row < current_pos.row) - continue; - if (pos.col == current_pos.col + 1) - { - current_pos.col = pos.col; - break; - } - } - break; - case flow_navigation::home: - // Go to leftmost column. - current_pos.col = 0; - break; - case flow_navigation::end: - // Go to last column. Beware of last row which might have less columns. - for (const auto& pos : m_flow_layout->positions()) - { - if (pos.row > current_pos.row) - break; - if (pos.row < current_pos.row) - continue; - current_pos.col = std::max(current_pos.col, pos.col); - } - break; - case flow_navigation::page_up: - // Go to top row. - current_pos.row = 0; - break; - case flow_navigation::page_down: - // Go to bottom row. Beware of last row which might have less columns. - for (const auto& pos : m_flow_layout->positions()) - { - if (pos.col != current_pos.col) - continue; - current_pos.row = std::max(current_pos.row, pos.row); - } - break; - } - } - - return current_pos; -} - -void flow_widget::select_item(flow_widget_item* item) -{ - const flow_layout::position selected_pos = find_item(item); - const s64 selected_index = find_item(selected_pos); - - if (selected_index < 0 || static_cast(selected_index) >= items().size()) - { - m_selected_index = -1; - return; - } - - m_selected_index = selected_index; - Q_EMIT ItemSelectionChanged(m_selected_index); - - for (usz i = 0; i < items().size(); i++) - { - if (flow_widget_item* item = items().at(i)) - { - // We need to polish the widgets in order to re-apply any stylesheet changes for the selected property. - item->selected = m_selected_index >= 0 && i == static_cast(m_selected_index); - item->polish_style(); - } - } - - // Make sure we see the focused widget - m_scroll_area->ensureWidgetVisible(::at32(items(), m_selected_index)); -} - -void flow_widget::on_item_focus() -{ - select_item(static_cast(QObject::sender())); -} - -void flow_widget::on_navigate(flow_navigation value) -{ - const flow_layout::position selected_pos = find_next_item(find_item(static_cast(QObject::sender())), value); - const s64 selected_index = find_item(selected_pos); - if (selected_index < 0 || static_cast(selected_index) >= items().size()) - { - return; - } - - if (flow_widget_item* item = items().at(selected_index)) - { - item->setFocus(); - } - - m_selected_index = selected_index; -} - -void flow_widget::mouseDoubleClickEvent(QMouseEvent* ev) -{ - if (!ev) - return; - - // Qt's itemDoubleClicked signal doesn't distinguish between mouse buttons and there is no simple way to get the pressed button. - // So we have to ignore this event when another button is pressed. - if (ev->button() != Qt::LeftButton) - { - ev->ignore(); - return; - } - - QWidget::mouseDoubleClickEvent(ev); -} diff --git a/rpcs3qt-legacy/flow_widget.h b/rpcs3qt-legacy/flow_widget.h deleted file mode 100644 index 34f167d52..000000000 --- a/rpcs3qt-legacy/flow_widget.h +++ /dev/null @@ -1,54 +0,0 @@ -#pragma once - -#include "util/types.hpp" -#include "flow_widget_item.h" -#include "flow_layout.h" - -#include -#include -#include - -class flow_widget : public QWidget -{ - Q_OBJECT - -public: - flow_widget(QWidget* parent); - virtual ~flow_widget(); - - void add_widget(flow_widget_item* widget); - void clear(); - - std::vector& items() - { - return m_widgets; - } - flow_widget_item* selected_item() const; - QScrollArea* scroll_area() const - { - return m_scroll_area; - } - - void paintEvent(QPaintEvent* event) override; - -Q_SIGNALS: - void ItemSelectionChanged(int index); - -private Q_SLOTS: - void on_item_focus(); - void on_navigate(flow_navigation value); - -protected: - void select_item(flow_widget_item* item); - void mouseDoubleClickEvent(QMouseEvent* event) override; - -private: - s64 find_item(const flow_layout::position& pos); - flow_layout::position find_item(flow_widget_item* item); - flow_layout::position find_next_item(flow_layout::position current_pos, flow_navigation value); - - flow_layout* m_flow_layout{}; - QScrollArea* m_scroll_area{}; - std::vector m_widgets; - s64 m_selected_index = -1; -}; diff --git a/rpcs3qt-legacy/flow_widget_item.cpp b/rpcs3qt-legacy/flow_widget_item.cpp deleted file mode 100644 index 8b766d302..000000000 --- a/rpcs3qt-legacy/flow_widget_item.cpp +++ /dev/null @@ -1,125 +0,0 @@ -#include "flow_widget_item.h" - -#include -#include -#include - -flow_widget_item::flow_widget_item(QWidget* parent) : QWidget(parent) -{ - setAttribute(Qt::WA_Hover); // We need to enable the hover attribute to ensure that hover events are handled. -} - -flow_widget_item::~flow_widget_item() -{ -} - -void flow_widget_item::polish_style() -{ - style()->unpolish(this); - style()->polish(this); -} - -void flow_widget_item::paintEvent(QPaintEvent* /*event*/) -{ - // Needed for stylesheets to apply to QWidgets - QStyleOption option; - option.initFrom(this); - QPainter painter(this); - style()->drawPrimitive(QStyle::PE_Widget, &option, &painter, this); - - if (!got_visible && cb_on_first_visibility) - { - if (QWidget* widget = static_cast(parent())) - { - if (widget->visibleRegion().intersects(geometry())) - { - got_visible = true; - cb_on_first_visibility(); - } - } - } -} - -void flow_widget_item::focusInEvent(QFocusEvent* event) -{ - QWidget::focusInEvent(event); - - // We need to polish the widgets in order to re-apply any stylesheet changes for the focus property. - polish_style(); - - Q_EMIT focused(); -} - -void flow_widget_item::focusOutEvent(QFocusEvent* event) -{ - QWidget::focusOutEvent(event); - - // We need to polish the widgets in order to re-apply any stylesheet changes for the focus property. - polish_style(); -} - -void flow_widget_item::keyPressEvent(QKeyEvent* event) -{ - if (!event) - { - return; - } - - switch (event->key()) - { - case Qt::Key_Left: - Q_EMIT navigate(flow_navigation::left); - return; - case Qt::Key_Right: - Q_EMIT navigate(flow_navigation::right); - return; - case Qt::Key_Up: - Q_EMIT navigate(flow_navigation::up); - return; - case Qt::Key_Down: - Q_EMIT navigate(flow_navigation::down); - return; - case Qt::Key_Home: - Q_EMIT navigate(flow_navigation::home); - return; - case Qt::Key_End: - Q_EMIT navigate(flow_navigation::end); - return; - case Qt::Key_PageUp: - Q_EMIT navigate(flow_navigation::page_up); - return; - case Qt::Key_PageDown: - Q_EMIT navigate(flow_navigation::page_down); - return; - default: - break; - } - - QWidget::keyPressEvent(event); -} - -bool flow_widget_item::event(QEvent* event) -{ - bool hover_changed = false; - - switch (event->type()) - { - case QEvent::HoverEnter: - hover_changed = setProperty("hover", "true"); - break; - case QEvent::HoverLeave: - hover_changed = setProperty("hover", "false"); - break; - default: - break; - } - - if (hover_changed) - { - // We need to polish the widgets in order to re-apply any stylesheet changes for the custom hover property. - // :hover does not work if we add descendants in the qss, so we need to use a custom property. - polish_style(); - } - - return QWidget::event(event); -} diff --git a/rpcs3qt-legacy/flow_widget_item.h b/rpcs3qt-legacy/flow_widget_item.h deleted file mode 100644 index 6b7078e1a..000000000 --- a/rpcs3qt-legacy/flow_widget_item.h +++ /dev/null @@ -1,49 +0,0 @@ -#pragma once - -#include -#include - -#include - -enum class flow_navigation -{ - up, - down, - left, - right, - home, - end, - page_up, - page_down -}; - -class flow_widget_item : public QWidget -{ - Q_OBJECT - - Q_PROPERTY(bool hover MEMBER m_hover) // Stylesheet workaround for descendants with parent pseudo state - Q_PROPERTY(bool selected MEMBER selected) // Stylesheet workaround for descendants with parent pseudo state - -public: - flow_widget_item(QWidget* parent); - virtual ~flow_widget_item(); - - virtual void polish_style(); - - void paintEvent(QPaintEvent* event) override; - void focusInEvent(QFocusEvent* event) override; - void focusOutEvent(QFocusEvent* event) override; - void keyPressEvent(QKeyEvent* event) override; - bool event(QEvent* event) override; - - bool got_visible{}; - bool selected{}; - std::function cb_on_first_visibility{}; - -protected: - bool m_hover{}; - -Q_SIGNALS: - void navigate(flow_navigation value); - void focused(); -}; diff --git a/rpcs3qt-legacy/frame_icon.xpm b/rpcs3qt-legacy/frame_icon.xpm deleted file mode 100644 index 08ac79c27..000000000 --- a/rpcs3qt-legacy/frame_icon.xpm +++ /dev/null @@ -1,139 +0,0 @@ -/* XPM */ -static char * frame_icon_xpm[] = { -"128 128 8 1", -" c None", -". c #0064E7", -"+ c #0065E8", -"@ c #0063E7", -"# c #0065E7", -"$ c #0064E8", -"% c #0065E6", -"& c #0064E6", -" ", -" ", -" ", -" ", -" ", -" ", -" ", -" ", -" ", -" ", -" ", -" ", -" ", -" ", -" ", -" ", -" ", -" ", -" ", -" ", -" ", -" ", -" ", -" ..........................................................................................................+ ", -" ............................................................................................................. ", -" ............................................................................................................... ", -" ................................................................................................................@ ", -" .................................................................................................................. ", -" .................................................................................................................. ", -" ................................................................................................................... ", -" ...................................................................................................................@ ", -" .................................................................................................................... ", -" .................................................................................................................... ", -" ....................................................................................................................@ ", -" ..................................................................................................................... ", -" ..................................................................................................................... ", -" ..................... ", -" .................... ", -" ................... ", -" ................... ", -" .................. ", -" .................. ", -" #................. ", -" .................. ", -" $................. ", -" %................. ", -" .................. ", -" &................. ", -" .................. ", -" $................. ", -" .................. ", -" .................. ", -" $.................. ", -" ..................& ", -" $..................# ", -" #&.................... ", -" .................................................................................................................... ", -" ...................................................................................................................# ", -" ................................................................................................................... ", -" .................................................................................................................. ", -" ................................................................................................................ ", -" ................................................................................................................. ", -" ..................................................................................................................$ ", -" ...................................................................................................................@ ", -" .................................................................................................................... ", -" .................................................................................................................... ", -" ....................................................................................................................+ ", -" ....................................................................................................................@ ", -" ..................................................................................................................... ", -" ..................... ", -" &................... ", -" ................... ", -" .................. ", -" .................. ", -" @................. ", -" ................. ", -" ................. ", -" ................. ", -" ................. ", -" ................. ", -" ................. ", -" ................. ", -" ................. ", -" .................. ", -" .................. ", -" .................. ", -" &.................. ", -" ................... ", -" ...................$ ", -" .................................................................................................................... ", -" &................................................................................................................... ", -" ...................................................................................................................& ", -" @.................................................................................................................. ", -" &.................................................................................................................# ", -" .................................................................................................................. ", -" ................................................................................................................. ", -" +............................................................................................................... ", -" ............................................................................................................... ", -" .............................................................................................................. ", -" @..........................................................................................................$ ", -" .......................................................................................................... ", -" .....................................................................................................#@ ", -" @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ ", -" ", -" ", -" ", -" ", -" ", -" ", -" ", -" ", -" ", -" ", -" ", -" ", -" ", -" ", -" ", -" ", -" ", -" ", -" ", -" ", -" ", -" ", -" ", -" ", -" "}; diff --git a/rpcs3qt-legacy/game_compatibility.cpp b/rpcs3qt-legacy/game_compatibility.cpp deleted file mode 100644 index b6089878a..000000000 --- a/rpcs3qt-legacy/game_compatibility.cpp +++ /dev/null @@ -1,347 +0,0 @@ -#include "game_compatibility.h" -#include "gui_settings.h" -#include "downloader.h" -#include "localized.h" - -#include "Crypto/unpkg.h" -#include "Loader/PSF.h" - -#include -#include -#include -#include - -LOG_CHANNEL(compat_log, "Compat"); - -constexpr auto qstr = QString::fromStdString; - -game_compatibility::game_compatibility(std::shared_ptr gui_settings, QWidget* parent) - : QObject(parent), m_gui_settings(std::move(gui_settings)) -{ - m_filepath = m_gui_settings->GetSettingsDir() + "/compat_database.dat"; - m_downloader = new downloader(parent); - RequestCompatibility(); - - connect(m_downloader, &downloader::signal_download_error, this, &game_compatibility::handle_download_error); - connect(m_downloader, &downloader::signal_download_finished, this, &game_compatibility::handle_download_finished); - connect(m_downloader, &downloader::signal_download_canceled, this, &game_compatibility::handle_download_canceled); -} - -void game_compatibility::handle_download_error(const QString& error) -{ - Q_EMIT DownloadError(error); -} - -void game_compatibility::handle_download_finished(const QByteArray& content) -{ - compat_log.notice("Database download finished"); - - // Create new map from database and write database to file if database was valid - if (ReadJSON(QJsonDocument::fromJson(content).object(), true)) - { - // Write database to file - QFile file(m_filepath); - - if (file.exists()) - { - compat_log.notice("Database file found: %s", m_filepath); - } - - if (!file.open(QIODevice::WriteOnly)) - { - compat_log.error("Database Error - Could not write database to file: %s", m_filepath); - return; - } - - file.write(content); - file.close(); - - compat_log.success("Wrote database to file: %s", m_filepath); - } - - // We have a new database in map, therefore refresh gamelist to new state - Q_EMIT DownloadFinished(); -} - -void game_compatibility::handle_download_canceled() -{ - Q_EMIT DownloadCanceled(); -} - -bool game_compatibility::ReadJSON(const QJsonObject& json_data, bool after_download) -{ - const int return_code = json_data["return_code"].toInt(); - - 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; - default: - error_message = "Server Error - Unknown Error"; - break; - } - compat_log.error("%s: return code %d", error_message, return_code); - Q_EMIT DownloadError(qstr(error_message) + " " + QString::number(return_code)); - } - else - { - compat_log.error("Database Error - Invalid: return code %d", return_code); - } - return false; - } - - if (!json_data["results"].isObject()) - { - compat_log.error("Database Error - No Results found"); - return false; - } - - m_compat_database.clear(); - - QJsonObject json_results = json_data["results"].toObject(); - - // Retrieve status data for every valid entry - for (const auto& key : json_results.keys()) - { - if (!json_results[key].isObject()) - { - compat_log.error("Database Error - Unusable object %s", key); - continue; - } - - QJsonObject json_result = json_results[key].toObject(); - - // Retrieve compatibility information from json - compat::status status = ::at32(Status_Data, json_result.value("status").toString("NoResult")); - - // Add date if possible - status.date = json_result.value("date").toString(); - - // Add latest version if possible - status.latest_version = json_result.value("update").toString(); - - // Add patchsets if possible - if (const QJsonValue patchsets_value = json_result.value("patchsets"); patchsets_value.isArray()) - { - for (const QJsonValue& patch_set : patchsets_value.toArray()) - { - compat::pkg_patchset set; - set.tag_id = patch_set["tag_id"].toString().toStdString(); - set.popup = patch_set["popup"].toBool(); - set.signoff = patch_set["signoff"].toBool(); - set.popup_delay = patch_set["popup_delay"].toInt(); - set.min_system_ver = patch_set["min_system_ver"].toString().toStdString(); - - if (const QJsonValue packages_value = patch_set["packages"]; packages_value.isArray()) - { - for (const QJsonValue& package : packages_value.toArray()) - { - compat::pkg_package pkg; - pkg.version = package["version"].toString().toStdString(); - pkg.size = package["size"].toInt(); - pkg.sha1sum = package["sha1sum"].toString().toStdString(); - pkg.ps3_system_ver = package["ps3_system_ver"].toString().toStdString(); - pkg.drm_type = package["drm_type"].toString().toStdString(); - - if (const QJsonValue changelogs_value = package["changelogs"]; changelogs_value.isArray()) - { - for (const QJsonValue& changelog : changelogs_value.toArray()) - { - compat::pkg_changelog chl; - chl.type = changelog["type"].toString().toStdString(); - chl.content = changelog["content"].toString().toStdString(); - - pkg.changelogs.push_back(std::move(chl)); - } - } - - if (const QJsonValue titles_value = package["titles"]; titles_value.isArray()) - { - for (const QJsonValue& title : titles_value.toArray()) - { - compat::pkg_title ttl; - ttl.type = title["type"].toString().toStdString(); - ttl.title = title["title"].toString().toStdString(); - - pkg.titles.push_back(std::move(ttl)); - } - } - - set.packages.push_back(std::move(pkg)); - } - } - - status.patch_sets.push_back(std::move(set)); - } - } - - // Add status to map - m_compat_database.emplace(key.toStdString(), std::move(status)); - } - - return true; -} - -void game_compatibility::RequestCompatibility(bool online) -{ - if (!online) - { - // Retrieve database from file - QFile file(m_filepath); - - if (!file.exists()) - { - compat_log.notice("Database file not found: %s", m_filepath); - return; - } - - if (!file.open(QIODevice::ReadOnly)) - { - compat_log.error("Database Error - Could not read database from file: %s", m_filepath); - return; - } - - const QByteArray content = file.readAll(); - file.close(); - - compat_log.notice("Finished reading database from file: %s", m_filepath); - - // Create new map from database - ReadJSON(QJsonDocument::fromJson(content).object(), online); - - return; - } - - const std::string url = "https://rpcs3.net/compatibility?api=v1&export"; - compat_log.notice("Beginning compatibility database download from: %s", url); - - m_downloader->start(url, true, true, tr("Downloading Database")); - - // We want to retrieve a new database, therefore refresh gamelist and indicate that - Q_EMIT DownloadStarted(); -} - -compat::status game_compatibility::GetCompatibility(const std::string& title_id) -{ - if (m_compat_database.empty()) - { - return ::at32(Status_Data, "NoData"); - } - - if (const auto it = m_compat_database.find(title_id); it != m_compat_database.cend()) - { - return it->second; - } - - return ::at32(Status_Data, "NoResult"); -} - -compat::status game_compatibility::GetStatusData(const QString& status) const -{ - return ::at32(Status_Data, status); -} - -compat::package_info game_compatibility::GetPkgInfo(const QString& pkg_path, game_compatibility* compat) -{ - compat::package_info info; - - const package_reader reader(pkg_path.toStdString()); - if (!reader.is_valid()) - { - info.is_valid = false; - return info; - } - - const psf::registry& psf = reader.get_psf(); - - // TODO: localization of title and changelog - const std::string title_key = "TITLE"; - const std::string changelog_key = "paramhip"; - - info.path = pkg_path; - info.title = qstr(std::string(psf::get_string(psf, title_key))); // Let's read this from the psf first - info.title_id = qstr(std::string(psf::get_string(psf, "TITLE_ID"))); - info.category = qstr(std::string(psf::get_string(psf, "CATEGORY"))); - info.version = qstr(std::string(psf::get_string(psf, "APP_VER"))); - - if (!info.category.isEmpty()) - { - const Localized localized; - - if (const auto boot_cat = localized.category.cat_boot.find(info.category); boot_cat != localized.category.cat_boot.end()) - { - info.local_cat = boot_cat->second; - } - else if (const auto data_cat = localized.category.cat_data.find(info.category); data_cat != localized.category.cat_data.end()) - { - info.local_cat = data_cat->second; - } - - if (info.category == "GD") - { - // For now let's assume that PS3 Game Data packages are always updates or DLC. - // Update packages always seem to have an APP_VER, so let's say it's a DLC otherwise. - // Ideally this would simply be the package content type, but I am too lazy to implement this right now. - if (info.version.isEmpty()) - { - info.type = compat::package_type::dlc; - } - else - { - info.type = compat::package_type::update; - } - } - } - - if (info.version.isEmpty()) - { - // Fallback to VERSION - info.version = qstr(std::string(psf::get_string(psf, "VERSION"))); - } - - if (compat) - { - compat::status stat = compat->GetCompatibility(info.title_id.toStdString()); - if (!stat.patch_sets.empty()) - { - // We currently only handle the first patch set - for (const auto& package : stat.patch_sets.front().packages) - { - if (info.version.toStdString() == package.version) - { - if (const std::string localized_title = package.get_title(title_key); !localized_title.empty()) - { - info.title = qstr(localized_title); - } - - if (const std::string localized_changelog = package.get_changelog(changelog_key); !localized_changelog.empty()) - { - info.changelog = qstr(localized_changelog); - } - - // This should be an update since it was found in a patch set - info.type = compat::package_type::update; - - break; - } - } - } - } - - if (info.title.isEmpty()) - { - const QFileInfo file_info(pkg_path); - info.title = file_info.fileName(); - } - - return info; -} diff --git a/rpcs3qt-legacy/game_compatibility.h b/rpcs3qt-legacy/game_compatibility.h deleted file mode 100644 index 5bc97514c..000000000 --- a/rpcs3qt-legacy/game_compatibility.h +++ /dev/null @@ -1,177 +0,0 @@ -#pragma once - -#include - -#include - -class downloader; -class gui_settings; - -namespace compat -{ - /** Represents the "title" json object */ - struct pkg_title - { - std::string type; // TITLE or TITLE_08 etc. (system languages) - std::string title; // The Last of Arse - }; - - /** Represents the "changelog" json object */ - struct pkg_changelog - { - std::string type; // paramhip or paramhip_08 etc. (system languages) - std::string content; // "This system software update improves system performance." - }; - - /** Represents the "package" json object */ - struct pkg_package - { - std::string version; // 01.04 - int size = 0; - std::string sha1sum; // a5c83b88394ea3ae99974caedd38690981a80f3e - std::string ps3_system_ver; // 04.4000 - std::string drm_type; // local or mbind etc. - std::vector changelogs; - std::vector titles; - - std::string get_changelog(const std::string& type) const - { - if (const auto it = std::find_if(changelogs.cbegin(), changelogs.cend(), [type](const pkg_changelog& cl) - { - return cl.type == type; - }); - it != changelogs.cend()) - { - return it->content; - } - if (const auto it = std::find_if(changelogs.cbegin(), changelogs.cend(), [](const pkg_changelog& cl) - { - return cl.type == "paramhip"; - }); - it != changelogs.cend()) - { - return it->content; - } - return ""; - } - - std::string get_title(const std::string& type) const - { - if (const auto it = std::find_if(titles.cbegin(), titles.cend(), [type](const pkg_title& t) - { - return t.type == type; - }); - it != titles.cend()) - { - return it->title; - } - if (const auto it = std::find_if(titles.cbegin(), titles.cend(), [](const pkg_title& t) - { - return t.type == "TITLE"; - }); - it != titles.end()) - { - return it->title; - } - return ""; - } - }; - - /** Represents the "patchset" json object */ - struct pkg_patchset - { - std::string tag_id; // BLES01269_T7 - bool popup = false; - bool signoff = false; - int popup_delay = 0; - std::string min_system_ver; // 03.60 - std::vector packages; - }; - - /** Represents the json object that contains an app's information and some additional info that is used in the GUI */ - struct status - { - int index = 0; - QString date; - QString color; - QString text; - QString tooltip; - QString latest_version; - std::vector patch_sets; - }; - - // The type of the package. In the future this should signify the proper PKG_CONTENT_TYPE. - enum class package_type - { - update, - dlc, - other - }; - - /** Concicely represents a specific pkg's localized information for use in the GUI */ - struct package_info - { - bool is_valid = true; - - QString path; // File path - QString title_id; // TEST12345 - QString title; // Localized - QString changelog; // Localized, may be empty - QString version; // May be empty - QString category; // HG, DG, GD etc. - QString local_cat; // Localized category - - package_type type = package_type::other; // The type of package (Update, DLC or other) - }; -} // namespace compat - -class game_compatibility : public QObject -{ - Q_OBJECT - -private: - const std::map Status_Data = - { - {"Playable", {0, "", "#1ebc61", tr("Playable"), tr("Games that can be properly played from start to finish")}}, - {"Ingame", {1, "", "#f9b32f", tr("Ingame"), tr("Games that either can't be finished, have serious glitches or have insufficient performance")}}, - {"Intro", {2, "", "#e08a1e", tr("Intro"), tr("Games that display image but don't make it past the menus")}}, - {"Loadable", {3, "", "#e74c3c", tr("Loadable"), tr("Games that display a black screen with a framerate on the window's title")}}, - {"Nothing", {4, "", "#455556", tr("Nothing"), tr("Games that don't initialize properly, not loading at all and/or crashing the emulator")}}, - {"NoResult", {5, "", "", tr("No results found"), tr("There is no entry for this game or application in the compatibility database yet.")}}, - {"NoData", {6, "", "", tr("Database missing"), tr("Right click here and download the current database.\nMake sure you are connected to the internet.")}}, - {"Download", {7, "", "", tr("Retrieving..."), tr("Downloading the compatibility database. Please wait...")}}}; - std::shared_ptr m_gui_settings; - QString m_filepath; - downloader* m_downloader = nullptr; - std::map m_compat_database; - - /** Creates new map from the database */ - bool ReadJSON(const QJsonObject& json_data, bool after_download); - -public: - /** Handles reads, writes and downloads for the compatibility database */ - game_compatibility(std::shared_ptr settings, QWidget* parent); - - /** Reads database. If online set to true: Downloads and writes the database to file */ - void RequestCompatibility(bool online = false); - - /** Returns the compatibility status for the requested title */ - compat::status GetCompatibility(const std::string& title_id); - - /** Returns the data for the requested status */ - compat::status GetStatusData(const QString& status) const; - - /** Returns package information like title, version, changelog etc. */ - static compat::package_info GetPkgInfo(const QString& pkg_path, game_compatibility* compat); - -Q_SIGNALS: - void DownloadStarted(); - void DownloadFinished(); - void DownloadCanceled(); - void DownloadError(const QString& error); - -private Q_SLOTS: - void handle_download_error(const QString& error); - void handle_download_finished(const QByteArray& content); - void handle_download_canceled(); -}; diff --git a/rpcs3qt-legacy/game_list.cpp b/rpcs3qt-legacy/game_list.cpp deleted file mode 100644 index 674fb62c2..000000000 --- a/rpcs3qt-legacy/game_list.cpp +++ /dev/null @@ -1,194 +0,0 @@ -#include "stdafx.h" -#include "game_list.h" -#include "movie_item.h" - -#include -#include -#include - -game_list::game_list() : QTableWidget(), game_list_base() -{ - m_icon_ready_callback = [this](const movie_item_base* item) - { - Q_EMIT IconReady(item); - }; -} - -void game_list::sync_header_actions(QList& actions, std::function get_visibility) -{ - ensure(get_visibility); - - bool is_dirty = false; - - for (int col = 0; col < actions.count(); ++col) - { - const bool is_hidden = !get_visibility(col); - actions[col]->setChecked(!is_hidden); - - if (isColumnHidden(col) != is_hidden) - { - setColumnHidden(col, is_hidden); - is_dirty = true; - } - } - - if (is_dirty) - { - fix_narrow_columns(); - } -} - -void game_list::create_header_actions(QList& actions, std::function get_visibility, std::function set_visibility) -{ - ensure(get_visibility); - ensure(set_visibility); - - horizontalHeader()->setContextMenuPolicy(Qt::CustomContextMenu); - - connect(horizontalHeader(), &QHeaderView::customContextMenuRequested, this, [this, &actions](const QPoint& pos) - { - QMenu* configure = new QMenu(this); - configure->addActions(actions); - configure->exec(horizontalHeader()->viewport()->mapToGlobal(pos)); - }); - - for (int col = 0; col < actions.count(); ++col) - { - actions[col]->setCheckable(true); - - connect(actions[col], &QAction::triggered, this, [this, &actions, get_visibility, set_visibility, col](bool checked) - { - if (!checked) // be sure to have at least one column left so you can call the context menu at all time - { - int c = 0; - for (int i = 0; i < actions.count(); ++i) - { - if (get_visibility(i) && ++c > 1) - break; - } - if (c < 2) - { - actions[col]->setChecked(true); // re-enable the checkbox if we don't change the actual state - return; - } - } - - setColumnHidden(col, !checked); // Negate because it's a set col hidden and we have menu say show. - set_visibility(col, checked); - - if (checked) // handle hidden columns that have zero width after showing them (stuck between others) - { - fix_narrow_columns(); - } - }); - } - - sync_header_actions(actions, get_visibility); -} - -void game_list::clear_list() -{ - m_last_hover_item = nullptr; - clearSelection(); - clearContents(); -} - -void game_list::fix_narrow_columns() -{ - QApplication::processEvents(); - - // handle columns (other than the icon column) that have zero width after showing them (stuck between others) - for (int col = 1; col < columnCount(); ++col) - { - if (isColumnHidden(col)) - { - continue; - } - - if (columnWidth(col) <= horizontalHeader()->minimumSectionSize()) - { - setColumnWidth(col, horizontalHeader()->minimumSectionSize()); - } - } -} - -void game_list::mousePressEvent(QMouseEvent* event) -{ - if (QTableWidgetItem* item = itemAt(event->pos()); !item || !item->data(Qt::UserRole).isValid()) - { - clearSelection(); - setCurrentItem(nullptr); // Needed for currentItemChanged - } - QTableWidget::mousePressEvent(event); -} - -void game_list::mouseMoveEvent(QMouseEvent* event) -{ - movie_item* new_item = static_cast(itemAt(event->pos())); - - if (new_item != m_last_hover_item) - { - if (m_last_hover_item) - { - m_last_hover_item->set_active(false); - } - if (new_item) - { - new_item->set_active(true); - } - } - - m_last_hover_item = new_item; - - QTableWidget::mouseMoveEvent(event); -} - -void game_list::mouseDoubleClickEvent(QMouseEvent* ev) -{ - if (!ev) - return; - - // Qt's itemDoubleClicked signal doesn't distinguish between mouse buttons and there is no simple way to get the pressed button. - // So we have to ignore this event when another button is pressed. - if (ev->button() != Qt::LeftButton) - { - ev->ignore(); - return; - } - - QTableWidget::mouseDoubleClickEvent(ev); -} - -void game_list::keyPressEvent(QKeyEvent* event) -{ - const auto modifiers = event->modifiers(); - - if (modifiers == Qt::ControlModifier && event->key() == Qt::Key_F && !event->isAutoRepeat()) - { - Q_EMIT FocusToSearchBar(); - return; - } - - QTableWidget::keyPressEvent(event); -} - -void game_list::leaveEvent(QEvent* event) -{ - if (m_last_hover_item) - { - m_last_hover_item->set_active(false); - m_last_hover_item = nullptr; - } - - QTableWidget::leaveEvent(event); -} - -void game_list::FocusAndSelectFirstEntryIfNoneIs() -{ - if (QTableWidgetItem* item = itemAt(0, 0); item && selectedIndexes().isEmpty()) - { - setCurrentItem(item); - } - - setFocus(); -} diff --git a/rpcs3qt-legacy/game_list.h b/rpcs3qt-legacy/game_list.h deleted file mode 100644 index 104047153..000000000 --- a/rpcs3qt-legacy/game_list.h +++ /dev/null @@ -1,49 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include - -#include "game_list_base.h" - -#include - -class movie_item; - -/* - class used in order to get deselection and hover change - if you know a simpler way, tell @Megamouse -*/ -class game_list : public QTableWidget, public game_list_base -{ - Q_OBJECT - -public: - game_list(); - - void sync_header_actions(QList& actions, std::function get_visibility); - void create_header_actions(QList& actions, std::function get_visibility, std::function set_visibility); - - void clear_list() override; // Use this instead of clearContents - - /** Fix columns with width smaller than the minimal section size */ - void fix_narrow_columns(); - -public Q_SLOTS: - void FocusAndSelectFirstEntryIfNoneIs(); - -Q_SIGNALS: - void FocusToSearchBar(); - void IconReady(const movie_item_base* item); - -protected: - movie_item* m_last_hover_item = nullptr; - - void mousePressEvent(QMouseEvent* event) override; - void mouseMoveEvent(QMouseEvent* event) override; - void mouseDoubleClickEvent(QMouseEvent* event) override; - void keyPressEvent(QKeyEvent* event) override; - void leaveEvent(QEvent* event) override; -}; diff --git a/rpcs3qt-legacy/game_list_base.cpp b/rpcs3qt-legacy/game_list_base.cpp deleted file mode 100644 index dec6926da..000000000 --- a/rpcs3qt-legacy/game_list_base.cpp +++ /dev/null @@ -1,216 +0,0 @@ -#include "stdafx.h" -#include "game_list_base.h" - -#include -#include - -#include -#include - -LOG_CHANNEL(game_list_log, "GameList"); - -game_list_base::game_list_base() -{ -} - -void game_list_base::repaint_icons(std::vector& game_data, const QColor& icon_color, const QSize& icon_size, qreal device_pixel_ratio) -{ - m_icon_size = icon_size; - m_icon_color = icon_color; - - QPixmap placeholder(icon_size * device_pixel_ratio); - placeholder.setDevicePixelRatio(device_pixel_ratio); - placeholder.fill(Qt::transparent); - - for (game_info& game : game_data) - { - game->pxmap = placeholder; - - if (movie_item_base* item = game->item) - { - item->set_icon_load_func([this, game, device_pixel_ratio, cancel = item->icon_loading_aborted()](int) - { - IconLoadFunction(game, device_pixel_ratio, cancel); - }); - - item->image_change_callback(); - } - } -} - -void game_list_base::IconLoadFunction(game_info game, qreal device_pixel_ratio, std::shared_ptr> cancel) -{ - if (cancel && cancel->load()) - { - return; - } - - static std::unordered_set warn_once_list; - static shared_mutex s_mtx; - - if (game->icon.isNull() && (game->info.icon_path.empty() || !game->icon.load(QString::fromStdString(game->info.icon_path)))) - { - if (game_list_log.warning) - { - bool logged = false; - { - std::lock_guard lock(s_mtx); - logged = !warn_once_list.emplace(game->info.icon_path).second; - } - - if (!logged) - { - game_list_log.warning("Could not load image from path %s", QDir(QString::fromStdString(game->info.icon_path)).absolutePath()); - } - } - } - - if (!game->item || (cancel && cancel->load())) - { - return; - } - - const QColor color = GetGridCompatibilityColor(game->compat.color); - { - std::lock_guard lock(game->item->pixmap_mutex); - game->pxmap = PaintedPixmap(game->icon, device_pixel_ratio, game->has_custom_config, game->has_custom_pad_config, color); - } - - if (!cancel || !cancel->load()) - { - if (m_icon_ready_callback) - m_icon_ready_callback(game->item); - } -} - -QPixmap game_list_base::PaintedPixmap(const QPixmap& icon, qreal device_pixel_ratio, bool paint_config_icon, bool paint_pad_config_icon, const QColor& compatibility_color) const -{ - QSize canvas_size(320, 176); - QSize icon_size(icon.size()); - QPoint target_pos; - - if (!icon.isNull()) - { - // Let's upscale the original icon to at least fit into the outer rect of the size of PS3's ICON0.PNG - if (icon_size.width() < 320 || icon_size.height() < 176) - { - icon_size.scale(320, 176, Qt::KeepAspectRatio); - } - - canvas_size = icon_size; - - // Calculate the centered size and position of the icon on our canvas. - if (icon_size.width() != 320 || icon_size.height() != 176) - { - ensure(icon_size.height() > 0); - constexpr double target_ratio = 320.0 / 176.0; // aspect ratio 20:11 - - if ((icon_size.width() / static_cast(icon_size.height())) > target_ratio) - { - canvas_size.setHeight(std::ceil(icon_size.width() / target_ratio)); - } - else - { - canvas_size.setWidth(std::ceil(icon_size.height() * target_ratio)); - } - - target_pos.setX(std::max(0, (canvas_size.width() - icon_size.width()) / 2.0)); - target_pos.setY(std::max(0, (canvas_size.height() - icon_size.height()) / 2.0)); - } - } - - // Create a canvas large enough to fit our entire scaled icon - QPixmap canvas(canvas_size * device_pixel_ratio); - canvas.setDevicePixelRatio(device_pixel_ratio); - canvas.fill(m_icon_color); - - // Create a painter for our canvas - QPainter painter(&canvas); - painter.setRenderHint(QPainter::SmoothPixmapTransform); - - // Draw the icon onto our canvas - if (!icon.isNull()) - { - painter.drawPixmap(target_pos.x(), target_pos.y(), icon_size.width(), icon_size.height(), icon); - } - - // Draw config icons if necessary - if (!m_is_list_layout && (paint_config_icon || paint_pad_config_icon)) - { - const int width = canvas_size.width() * 0.2; - const QPoint origin = QPoint(canvas_size.width() - width, 0); - QString icon_path; - - if (paint_config_icon && paint_pad_config_icon) - { - icon_path = ":/Icons/combo_config_bordered.png"; - } - else if (paint_config_icon) - { - icon_path = ":/Icons/custom_config.png"; - } - else if (paint_pad_config_icon) - { - icon_path = ":/Icons/controllers.png"; - } - - QPixmap custom_config_icon(icon_path); - custom_config_icon.setDevicePixelRatio(device_pixel_ratio); - painter.drawPixmap(origin, custom_config_icon.scaled(QSize(width, width) * device_pixel_ratio, Qt::KeepAspectRatio, Qt::TransformationMode::SmoothTransformation)); - } - - // Draw game compatibility icons if necessary - if (compatibility_color.isValid()) - { - const int size = canvas_size.height() * 0.2; - const int spacing = canvas_size.height() * 0.05; - QColor copyColor = QColor(compatibility_color); - copyColor.setAlpha(215); // ~85% opacity - painter.setRenderHint(QPainter::Antialiasing); - painter.setBrush(QBrush(copyColor)); - painter.setPen(QPen(Qt::black, std::max(canvas_size.width() / 320, canvas_size.height() / 176))); - painter.drawEllipse(spacing, spacing, size, size); - } - - // Finish the painting - painter.end(); - - // Scale and return our final image - return canvas.scaled(m_icon_size * device_pixel_ratio, Qt::KeepAspectRatio, Qt::TransformationMode::SmoothTransformation); -} - -QColor game_list_base::GetGridCompatibilityColor(const QString& string) const -{ - if (m_draw_compat_status_to_grid && !m_is_list_layout) - { - return QColor(string); - } - return QColor(); -} - -QIcon game_list_base::GetCustomConfigIcon(const game_info& game) -{ - if (!game) - return {}; - - static const QIcon icon_combo_config_bordered(":/Icons/combo_config_bordered.png"); - static const QIcon icon_custom_config(":/Icons/custom_config.png"); - static const QIcon icon_controllers(":/Icons/controllers.png"); - - if (game->has_custom_config && game->has_custom_pad_config) - { - return icon_combo_config_bordered; - } - - if (game->has_custom_config) - { - return icon_custom_config; - } - - if (game->has_custom_pad_config) - { - return icon_controllers; - } - - return {}; -} diff --git a/rpcs3qt-legacy/game_list_base.h b/rpcs3qt-legacy/game_list_base.h deleted file mode 100644 index 56fe8f822..000000000 --- a/rpcs3qt-legacy/game_list_base.h +++ /dev/null @@ -1,49 +0,0 @@ -#pragma once - -#include "gui_game_info.h" - -#include -#include - -class game_list_base -{ -public: - game_list_base(); - - virtual void clear_list() {}; - virtual void populate( - [[maybe_unused]] const std::vector& game_data, - [[maybe_unused]] const std::map& notes_map, - [[maybe_unused]] const std::map& title_map, - [[maybe_unused]] const std::string& selected_item_id, - [[maybe_unused]] bool play_hover_movies) {}; - - void set_icon_size(QSize size) - { - m_icon_size = std::move(size); - } - void set_icon_color(QColor color) - { - m_icon_color = std::move(color); - } - void set_draw_compat_status_to_grid(bool enabled) - { - m_draw_compat_status_to_grid = enabled; - } - - virtual void repaint_icons(std::vector& game_data, const QColor& icon_color, const QSize& icon_size, qreal device_pixel_ratio); - - /** Sets the custom config icon. */ - static QIcon GetCustomConfigIcon(const game_info& game); - -protected: - void IconLoadFunction(game_info game, qreal device_pixel_ratio, std::shared_ptr> cancel); - QPixmap PaintedPixmap(const QPixmap& icon, qreal device_pixel_ratio, bool paint_config_icon = false, bool paint_pad_config_icon = false, const QColor& compatibility_color = {}) const; - QColor GetGridCompatibilityColor(const QString& string) const; - - std::function m_icon_ready_callback{}; - bool m_draw_compat_status_to_grid{}; - bool m_is_list_layout{}; - QSize m_icon_size{}; - QColor m_icon_color{}; -}; diff --git a/rpcs3qt-legacy/game_list_delegate.cpp b/rpcs3qt-legacy/game_list_delegate.cpp deleted file mode 100644 index 1498ccc4b..000000000 --- a/rpcs3qt-legacy/game_list_delegate.cpp +++ /dev/null @@ -1,48 +0,0 @@ -#include "game_list_delegate.h" -#include "movie_item.h" -#include "gui_settings.h" - -#include - -game_list_delegate::game_list_delegate(QObject* parent) - : table_item_delegate(parent, true) -{ -} - -void game_list_delegate::paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const -{ - table_item_delegate::paint(painter, option, index); - - // Find out if the icon or size items are visible - if (index.column() == static_cast(gui::game_list_columns::dir_size) || (m_has_icons && index.column() == static_cast(gui::game_list_columns::icon))) - { - if (const QTableWidget* table = static_cast(parent())) - { - // We need to remove the headers from our calculation. The visualItemRect starts at 0,0 while the visibleRegion doesn't. - QRegion visible_region = table->visibleRegion(); - visible_region.translate(-table->verticalHeader()->width(), -table->horizontalHeader()->height()); - - if (const QTableWidgetItem* current_item = table->item(index.row(), index.column()); - current_item && visible_region.intersects(table->visualItemRect(current_item))) - { - if (movie_item* item = static_cast(table->item(index.row(), static_cast(gui::game_list_columns::icon)))) - { - if (index.column() == static_cast(gui::game_list_columns::dir_size)) - { - if (!item->size_on_disk_loading()) - { - item->call_size_calc_func(); - } - } - else if (m_has_icons && index.column() == static_cast(gui::game_list_columns::icon)) - { - if (!item->icon_loading()) - { - item->call_icon_load_func(index.row()); - } - } - } - } - } - } -} diff --git a/rpcs3qt-legacy/game_list_delegate.h b/rpcs3qt-legacy/game_list_delegate.h deleted file mode 100644 index 60d818625..000000000 --- a/rpcs3qt-legacy/game_list_delegate.h +++ /dev/null @@ -1,11 +0,0 @@ -#pragma once - -#include "table_item_delegate.h" - -class game_list_delegate : public table_item_delegate -{ -public: - explicit game_list_delegate(QObject* parent); - - void paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const override; -}; diff --git a/rpcs3qt-legacy/game_list_frame.cpp b/rpcs3qt-legacy/game_list_frame.cpp deleted file mode 100644 index 61e945e93..000000000 --- a/rpcs3qt-legacy/game_list_frame.cpp +++ /dev/null @@ -1,3070 +0,0 @@ -#include "game_list_frame.h" -#include "qt_utils.h" -#include "settings_dialog.h" -#include "pad_settings_dialog.h" -#include "input_dialog.h" -#include "localized.h" -#include "progress_dialog.h" -#include "persistent_settings.h" -#include "emu_settings.h" -#include "gui_settings.h" -#include "gui_application.h" -#include "game_list_table.h" -#include "game_list_grid.h" -#include "game_list_grid_item.h" -#include "patch_manager_dialog.h" - -#include "Emu/System.h" -#include "Emu/vfs_config.h" -#include "Emu/system_utils.hpp" -#include "Loader/PSF.h" -#include "util/types.hpp" -#include "util/File.h" -#include "util/sysinfo.hpp" -#include "Input/pad_thread.h" - -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -LOG_CHANNEL(game_list_log, "GameList"); -LOG_CHANNEL(sys_log, "SYS"); - -extern atomic_t g_system_progress_canceled; - -std::string get_savestate_file(std::string_view title_id, std::string_view boot_pat, s64 abs_id, s64 rel_id); - -game_list_frame::game_list_frame(std::shared_ptr gui_settings, std::shared_ptr emu_settings, std::shared_ptr persistent_settings, QWidget* parent) - : custom_dock_widget(tr("Game List"), parent), m_gui_settings(std::move(gui_settings)), m_emu_settings(std::move(emu_settings)), m_persistent_settings(std::move(persistent_settings)) -{ - m_icon_size = gui::gl_icon_size_min; // ensure a valid size - m_is_list_layout = m_gui_settings->GetValue(gui::gl_listMode).toBool(); - m_margin_factor = m_gui_settings->GetValue(gui::gl_marginFactor).toReal(); - m_text_factor = m_gui_settings->GetValue(gui::gl_textFactor).toReal(); - m_icon_color = m_gui_settings->GetValue(gui::gl_iconColor).value(); - m_col_sort_order = m_gui_settings->GetValue(gui::gl_sortAsc).toBool() ? Qt::AscendingOrder : Qt::DescendingOrder; - m_sort_column = m_gui_settings->GetValue(gui::gl_sortCol).toInt(); - m_hidden_list = gui::utils::list_to_set(m_gui_settings->GetValue(gui::gl_hidden_list).toStringList()); - - m_old_layout_is_list = m_is_list_layout; - - // Save factors for first setup - m_gui_settings->SetValue(gui::gl_iconColor, m_icon_color, false); - m_gui_settings->SetValue(gui::gl_marginFactor, m_margin_factor, false); - m_gui_settings->SetValue(gui::gl_textFactor, m_text_factor, true); - - m_game_dock = new QMainWindow(this); - m_game_dock->setWindowFlags(Qt::Widget); - setWidget(m_game_dock); - - m_game_grid = new game_list_grid(); - m_game_grid->installEventFilter(this); - m_game_grid->scroll_area()->verticalScrollBar()->installEventFilter(this); - - m_game_list = new game_list_table(this, m_persistent_settings); - m_game_list->installEventFilter(this); - m_game_list->verticalScrollBar()->installEventFilter(this); - - m_game_compat = new game_compatibility(m_gui_settings, this); - - m_central_widget = new QStackedWidget(this); - m_central_widget->addWidget(m_game_list); - m_central_widget->addWidget(m_game_grid); - - if (m_is_list_layout) - { - m_central_widget->setCurrentWidget(m_game_list); - } - else - { - m_central_widget->setCurrentWidget(m_game_grid); - } - - m_game_dock->setCentralWidget(m_central_widget); - - // Actions regarding showing/hiding columns - auto add_column = [this](gui::game_list_columns col, const QString& header_text, const QString& action_text) - { - m_game_list->setHorizontalHeaderItem(static_cast(col), new QTableWidgetItem(header_text)); - m_columnActs.append(new QAction(action_text, this)); - }; - - add_column(gui::game_list_columns::icon, tr("Icon"), tr("Show Icons")); - add_column(gui::game_list_columns::name, tr("Name"), tr("Show Names")); - add_column(gui::game_list_columns::serial, tr("Serial"), tr("Show Serials")); - add_column(gui::game_list_columns::firmware, tr("Firmware"), tr("Show Firmwares")); - add_column(gui::game_list_columns::version, tr("Version"), tr("Show Versions")); - add_column(gui::game_list_columns::category, tr("Category"), tr("Show Categories")); - add_column(gui::game_list_columns::path, tr("Path"), tr("Show Paths")); - add_column(gui::game_list_columns::move, tr("PlayStation Move"), tr("Show PlayStation Move")); - add_column(gui::game_list_columns::resolution, tr("Supported Resolutions"), tr("Show Supported Resolutions")); - add_column(gui::game_list_columns::sound, tr("Sound Formats"), tr("Show Sound Formats")); - add_column(gui::game_list_columns::parental, tr("Parental Level"), tr("Show Parental Levels")); - add_column(gui::game_list_columns::last_play, tr("Last Played"), tr("Show Last Played")); - add_column(gui::game_list_columns::playtime, tr("Time Played"), tr("Show Time Played")); - add_column(gui::game_list_columns::compat, tr("Compatibility"), tr("Show Compatibility")); - add_column(gui::game_list_columns::dir_size, tr("Space On Disk"), tr("Show Space On Disk")); - - m_progress_dialog = new progress_dialog(tr("Loading games"), tr("Loading games, please wait..."), tr("Cancel"), 0, 0, false, this, Qt::Dialog | Qt::WindowTitleHint | Qt::CustomizeWindowHint); - m_progress_dialog->setMinimumDuration(200); // Only show the progress dialog after some time has passed - - // Events - connect(m_progress_dialog, &QProgressDialog::canceled, this, [this]() - { - gui::utils::stop_future_watcher(m_parsing_watcher, true); - gui::utils::stop_future_watcher(m_refresh_watcher, true); - - m_path_entries.clear(); - m_path_list.clear(); - m_serials.clear(); - m_game_data.clear(); - m_notes.clear(); - m_games.pop_all(); - }); - connect(&m_parsing_watcher, &QFutureWatcher::finished, this, &game_list_frame::OnParsingFinished); - connect(&m_parsing_watcher, &QFutureWatcher::canceled, this, [this]() - { - WaitAndAbortSizeCalcThreads(); - WaitAndAbortRepaintThreads(); - - m_path_entries.clear(); - m_path_list.clear(); - m_game_data.clear(); - m_serials.clear(); - m_games.pop_all(); - }); - connect(&m_refresh_watcher, &QFutureWatcher::finished, this, &game_list_frame::OnRefreshFinished); - connect(&m_refresh_watcher, &QFutureWatcher::canceled, this, [this]() - { - WaitAndAbortSizeCalcThreads(); - WaitAndAbortRepaintThreads(); - - m_path_entries.clear(); - m_path_list.clear(); - m_game_data.clear(); - m_serials.clear(); - m_games.pop_all(); - - if (m_progress_dialog) - { - m_progress_dialog->accept(); - } - }); - connect(&m_refresh_watcher, &QFutureWatcher::progressRangeChanged, this, [this](int minimum, int maximum) - { - if (m_progress_dialog) - { - m_progress_dialog->SetRange(minimum, maximum); - } - }); - connect(&m_refresh_watcher, &QFutureWatcher::progressValueChanged, this, [this](int value) - { - if (m_progress_dialog) - { - m_progress_dialog->SetValue(value); - } - }); - - connect(m_game_list, &QTableWidget::customContextMenuRequested, this, &game_list_frame::ShowContextMenu); - connect(m_game_list, &QTableWidget::itemSelectionChanged, this, &game_list_frame::ItemSelectionChangedSlot); - connect(m_game_list, &QTableWidget::itemDoubleClicked, this, QOverload::of(&game_list_frame::doubleClickedSlot)); - - connect(m_game_list->horizontalHeader(), &QHeaderView::sectionClicked, this, &game_list_frame::OnColClicked); - - connect(m_game_grid, &QWidget::customContextMenuRequested, this, &game_list_frame::ShowContextMenu); - connect(m_game_grid, &game_list_grid::ItemSelectionChanged, this, &game_list_frame::NotifyGameSelection); - connect(m_game_grid, &game_list_grid::ItemDoubleClicked, this, QOverload::of(&game_list_frame::doubleClickedSlot)); - - connect(m_game_compat, &game_compatibility::DownloadStarted, this, [this]() - { - for (const auto& game : m_game_data) - { - game->compat = m_game_compat->GetStatusData("Download"); - } - Refresh(); - }); - connect(m_game_compat, &game_compatibility::DownloadFinished, this, &game_list_frame::OnCompatFinished); - connect(m_game_compat, &game_compatibility::DownloadCanceled, this, &game_list_frame::OnCompatFinished); - connect(m_game_compat, &game_compatibility::DownloadError, this, [this](const QString& error) - { - OnCompatFinished(); - QMessageBox::warning(this, tr("Warning!"), tr("Failed to retrieve the online compatibility database!\nFalling back to local database.\n\n%0").arg(error)); - }); - - connect(m_game_list, &game_list::FocusToSearchBar, this, &game_list_frame::FocusToSearchBar); - connect(m_game_grid, &game_list_grid::FocusToSearchBar, this, &game_list_frame::FocusToSearchBar); - - m_game_list->create_header_actions(m_columnActs, [this](int col) - { - return m_gui_settings->GetGamelistColVisibility(static_cast(col)); - }, - [this](int col, bool visible) - { - m_gui_settings->SetGamelistColVisibility(static_cast(col), visible); - }); -} - -void game_list_frame::LoadSettings() -{ - m_col_sort_order = m_gui_settings->GetValue(gui::gl_sortAsc).toBool() ? Qt::AscendingOrder : Qt::DescendingOrder; - m_sort_column = m_gui_settings->GetValue(gui::gl_sortCol).toInt(); - m_category_filters = m_gui_settings->GetGameListCategoryFilters(true); - m_grid_category_filters = m_gui_settings->GetGameListCategoryFilters(false); - m_draw_compat_status_to_grid = m_gui_settings->GetValue(gui::gl_draw_compat).toBool(); - m_prefer_game_data_icons = m_gui_settings->GetValue(gui::gl_pref_gd_icon).toBool(); - m_show_custom_icons = m_gui_settings->GetValue(gui::gl_custom_icon).toBool(); - m_play_hover_movies = m_gui_settings->GetValue(gui::gl_hover_gifs).toBool(); - - m_game_list->sync_header_actions(m_columnActs, [this](int col) - { - return m_gui_settings->GetGamelistColVisibility(static_cast(col)); - }); -} - -game_list_frame::~game_list_frame() -{ - WaitAndAbortSizeCalcThreads(); - WaitAndAbortRepaintThreads(); - gui::utils::stop_future_watcher(m_parsing_watcher, true); - gui::utils::stop_future_watcher(m_refresh_watcher, true); -} - -void game_list_frame::OnColClicked(int col) -{ - if (col == static_cast(gui::game_list_columns::icon)) - return; // Don't "sort" icons. - - if (col == m_sort_column) - { - m_col_sort_order = (m_col_sort_order == Qt::AscendingOrder) ? Qt::DescendingOrder : Qt::AscendingOrder; - } - else - { - m_col_sort_order = Qt::AscendingOrder; - } - m_sort_column = col; - - m_gui_settings->SetValue(gui::gl_sortAsc, m_col_sort_order == Qt::AscendingOrder, false); - m_gui_settings->SetValue(gui::gl_sortCol, col, true); - - m_game_list->sort(m_game_data.size(), m_sort_column, m_col_sort_order); -} - -// Get visibility of entries -bool game_list_frame::IsEntryVisible(const game_info& game, bool search_fallback) const -{ - const auto matches_category = [&]() - { - if (m_is_list_layout) - { - return m_category_filters.contains(qstr(game->info.category)); - } - - return m_grid_category_filters.contains(qstr(game->info.category)); - }; - - const QString serial = qstr(game->info.serial); - const bool is_visible = m_show_hidden || !m_hidden_list.contains(serial); - return is_visible && matches_category() && SearchMatchesApp(qstr(game->info.name), serial, search_fallback); -} - -bool game_list_frame::RemoveContentPath(const std::string& path, const std::string& desc) -{ - if (!fs::exists(path)) - { - return true; - } - - if (fs::is_dir(path)) - { - if (fs::remove_all(path)) - { - game_list_log.notice("Removed '%s' directory: '%s'", desc, path); - } - else - { - game_list_log.error("Could not remove '%s' directory: '%s' (%s)", desc, path, fs::g_tls_error); - - return false; - } - } - else // If file - { - if (fs::remove_file(path)) - { - game_list_log.notice("Removed '%s' file: '%s'", desc, path); - } - else - { - game_list_log.error("Could not remove '%s' file: '%s' (%s)", desc, path, fs::g_tls_error); - - return false; - } - } - - return true; -} - -u32 game_list_frame::RemoveContentPathList(const std::vector& path_list, const std::string& desc) -{ - u32 paths_removed = 0; - - for (const std::string& path : path_list) - { - if (RemoveContentPath(path, desc)) - { - paths_removed++; - } - } - - return paths_removed; -} - -bool game_list_frame::RemoveContentBySerial(const std::string& base_dir, const std::string& serial, const std::string& desc) -{ - bool success = true; - - for (const auto& entry : fs::dir(base_dir)) - { - // Search for any path starting with serial (e.g. BCES01118_BCES01118) - if (!entry.name.starts_with(serial)) - { - continue; - } - - if (!RemoveContentPath(base_dir + entry.name, desc)) - { - success = false; // Mark as failed if there is at least one failure - } - } - - return success; -} - -std::vector game_list_frame::GetDirListBySerial(const std::string& base_dir, const std::string& serial) -{ - std::vector dir_list; - - for (const auto& entry : fs::dir(base_dir)) - { - // Check for sub folder starting with serial (e.g. BCES01118_BCES01118) - if (entry.is_directory && entry.name.starts_with(serial)) - { - dir_list.push_back(base_dir + entry.name); - } - } - - return dir_list; -} - -std::string game_list_frame::GetCacheDirBySerial(const std::string& serial) -{ - return rpcs3::utils::get_cache_dir() + (serial == "vsh.self" ? "vsh" : serial); -} - -std::string game_list_frame::GetDataDirBySerial(const std::string& serial) -{ - return fs::get_config_dir() + "data/" + serial; -} - -void game_list_frame::push_path(const std::string& path, std::vector& legit_paths) -{ - { - std::lock_guard lock(m_path_mutex); - if (!m_path_list.insert(path).second) - { - return; - } - } - legit_paths.push_back(path); -} - -void game_list_frame::Refresh(const bool from_drive, const std::vector& serials_to_remove_from_yml, const bool scroll_after) -{ - if (from_drive) - { - WaitAndAbortSizeCalcThreads(); - } - WaitAndAbortRepaintThreads(); - gui::utils::stop_future_watcher(m_parsing_watcher, from_drive); - gui::utils::stop_future_watcher(m_refresh_watcher, from_drive); - - if (m_progress_dialog && m_progress_dialog->isVisible()) - { - m_progress_dialog->SetValue(m_progress_dialog->maximum()); - m_progress_dialog->accept(); - } - - if (from_drive) - { - m_path_entries.clear(); - m_path_list.clear(); - m_serials.clear(); - m_game_data.clear(); - m_notes.clear(); - m_games.pop_all(); - - if (m_progress_dialog) - { - m_progress_dialog->SetValue(0); - } - - const std::string games_dir = rpcs3::utils::get_games_dir(); - - // List of serials (title id) to remove in "games.yml" file (if any) - std::vector serials_to_remove = serials_to_remove_from_yml; // Initialize the list with the specified serials (if any) - - // Scan game list to detect the titles belonging to auto-detection "games" folder - for (const auto& [serial, path] : Emu.GetGamesConfig().get_games()) // Loop on game list file - { - // NOTE: Used starts_with(games_dir) instead of Emu.IsPathInsideDir(path, games_dir) due the latter would check also the existence of the paths - // - if (path.starts_with(games_dir)) // If game path belongs to auto-detection "games" folder, add the serial to the removal list - { - serials_to_remove.push_back(serial); - } - } - - // Remove the specified and detected serials (title id) only from the game list in memory (not yet in "games.yml" file) - Emu.RemoveGames(serials_to_remove, false); - - // Scan auto-detection "games" folder adding the detected titles to the game list plus flushing also all the other changes in "games.yml" file - if (const u32 games_added = Emu.AddGamesFromDir(games_dir); games_added != 0) - { - game_list_log.notice("Refresh added %d new entries found in %s", games_added, games_dir); - } - - const std::string _hdd = Emu.GetCallbacks().resolve_path(rpcs3::utils::get_hdd0_dir()) + '/'; - - m_parsing_watcher.setFuture(QtConcurrent::map(m_parsing_threads, [this, _hdd](int index) - { - if (index > 0) - { - game_list_log.error("Unexpected thread index: %d", index); - return; - } - - const auto add_dir = [this](const std::string& path, bool is_disc) - { - for (const auto& entry : fs::dir(path)) - { - if (m_parsing_watcher.isCanceled()) - { - break; - } - - if (!entry.is_directory || entry.name == "." || entry.name == "..") - { - continue; - } - - std::lock_guard lock(m_path_mutex); - m_path_entries.emplace_back(path_entry{path + entry.name, is_disc, false}); - } - }; - - const std::string hdd0_game = _hdd + "game/"; - - add_dir(hdd0_game, false); - add_dir(_hdd + "disc/", true); // Deprecated - - for (const auto& [serial, path] : Emu.GetGamesConfig().get_games()) - { - if (m_parsing_watcher.isCanceled()) - { - break; - } - - std::string game_dir = path; - game_dir.resize(game_dir.find_last_not_of('/') + 1); - - if (game_dir.empty() || path.starts_with(hdd0_game)) - { - continue; - } - - // Don't use the C00 subdirectory in our game list - if (game_dir.ends_with("/C00") || game_dir.ends_with("\\C00")) - { - game_dir = game_dir.substr(0, game_dir.size() - 4); - } - - std::lock_guard lock(m_path_mutex); - m_path_entries.emplace_back(path_entry{game_dir, false, true}); - } - })); - - return; - } - - // Fill Game List / Game Grid - - const std::string selected_item = CurrentSelectionPath(); - - // Release old data - for (const auto& game : m_game_data) - { - game->item = nullptr; - } - - // Get list of matching apps - std::vector matching_apps; - - for (const auto& app : m_game_data) - { - if (IsEntryVisible(app)) - { - matching_apps.push_back(app); - } - } - - // Fallback is not needed when at least one entry is visible - if (matching_apps.empty()) - { - for (const auto& app : m_game_data) - { - if (IsEntryVisible(app, true)) - { - matching_apps.push_back(app); - } - } - } - - if (m_is_list_layout) - { - m_game_grid->clear_list(); - const int scroll_position = m_game_list->verticalScrollBar()->value(); - m_game_list->populate(matching_apps, m_notes, m_titles, selected_item, m_play_hover_movies); - m_game_list->sort(m_game_data.size(), m_sort_column, m_col_sort_order); - RepaintIcons(); - - if (scroll_after) - { - m_game_list->scrollTo(m_game_list->currentIndex(), QAbstractItemView::PositionAtCenter); - } - else - { - m_game_list->verticalScrollBar()->setValue(scroll_position); - } - } - else - { - m_game_list->clear_list(); - m_game_grid->populate(matching_apps, m_notes, m_titles, selected_item, m_play_hover_movies); - RepaintIcons(); - } -} - -void game_list_frame::OnParsingFinished() -{ - const Localized localized; - const std::string dev_flash = g_cfg_vfs.get_dev_flash(); - const std::string _hdd = rpcs3::utils::get_hdd0_dir(); - - m_path_entries.emplace_back(path_entry{dev_flash + "vsh/module/vsh.self", false, false}); - - // Remove duplicates - sort(m_path_entries.begin(), m_path_entries.end(), [](const path_entry& l, const path_entry& r) - { - return l.path < r.path; - }); - m_path_entries.erase(unique(m_path_entries.begin(), m_path_entries.end(), [](const path_entry& l, const path_entry& r) - { - return l.path == r.path; - }), - m_path_entries.end()); - - const s32 language_index = gui_application::get_language_id(); - const std::string game_icon_path = fs::get_config_dir() + "/Icons/game_icons/"; - const std::string localized_title = fmt::format("TITLE_%02d", language_index); - const std::string localized_icon = fmt::format("ICON0_%02d.PNG", language_index); - const std::string localized_movie = fmt::format("ICON1_%02d.PAM", language_index); - - const auto add_game = [this, localized_title, localized_icon, localized_movie, dev_flash, cat_unknown_localized = localized.category.unknown.toStdString(), cat_unknown = cat::cat_unknown.toStdString(), game_icon_path, _hdd, play_hover_movies = m_play_hover_movies, show_custom_icons = m_show_custom_icons](const std::string& dir_or_elf) - { - gui_game_info game{}; - game.info.path = dir_or_elf; - - const Localized thread_localized; - - const std::string sfo_dir = rpcs3::utils::get_sfo_dir_from_game_path(dir_or_elf); - const psf::registry psf = psf::load_object(sfo_dir + "/PARAM.SFO"); - const std::string_view title_id = psf::get_string(psf, "TITLE_ID", ""); - - if (title_id.empty()) - { - if (!fs::is_file(dir_or_elf)) - { - // Do not care about invalid entries - return; - } - - game.info.serial = dir_or_elf.substr(dir_or_elf.find_last_of(fs::delim) + 1); - game.info.category = cat::cat_ps3_os.toStdString(); // Key for operating system executables - game.info.version = utils::get_firmware_version(); - game.info.app_ver = game.info.version; - game.info.fw = game.info.version; - game.info.bootable = 1; - game.info.icon_path = dev_flash + "vsh/resource/explore/icon/icon_home.png"; - - if (dir_or_elf.starts_with(dev_flash)) - { - std::string path_vfs = dir_or_elf.substr(dev_flash.size()); - - if (const usz pos = path_vfs.find_first_not_of(fs::delim); pos != umax && pos != 0) - { - path_vfs = path_vfs.substr(pos); - } - - if (const auto it = thread_localized.title.titles.find(path_vfs); it != thread_localized.title.titles.cend()) - { - game.info.name = it->second.toStdString(); - } - } - - if (game.info.name.empty()) - { - game.info.name = game.info.serial; - } - } - else - { - std::string_view name = psf::get_string(psf, localized_title); - if (name.empty()) - name = psf::get_string(psf, "TITLE", cat_unknown_localized); - - game.info.serial = std::string(title_id); - game.info.name = std::string(name); - game.info.app_ver = std::string(psf::get_string(psf, "APP_VER", cat_unknown_localized)); - game.info.version = std::string(psf::get_string(psf, "VERSION", cat_unknown_localized)); - game.info.category = std::string(psf::get_string(psf, "CATEGORY", cat_unknown)); - game.info.fw = std::string(psf::get_string(psf, "PS3_SYSTEM_VER", cat_unknown_localized)); - game.info.parental_lvl = psf::get_integer(psf, "PARENTAL_LEVEL", 0); - game.info.resolution = psf::get_integer(psf, "RESOLUTION", 0); - game.info.sound_format = psf::get_integer(psf, "SOUND_FORMAT", 0); - game.info.bootable = psf::get_integer(psf, "BOOTABLE", 0); - game.info.attr = psf::get_integer(psf, "ATTRIBUTE", 0); - } - - if (show_custom_icons) - { - if (std::string icon_path = game_icon_path + game.info.serial + "/ICON0.PNG"; fs::is_file(icon_path)) - { - game.info.icon_path = std::move(icon_path); - game.has_custom_icon = true; - } - } - - if (game.info.icon_path.empty()) - { - if (std::string icon_path = sfo_dir + "/" + localized_icon; fs::is_file(icon_path)) - { - game.info.icon_path = std::move(icon_path); - } - else - { - game.info.icon_path = sfo_dir + "/ICON0.PNG"; - } - } - - if (std::string movie_path = game_icon_path + game.info.serial + "/hover.gif"; fs::is_file(movie_path)) - { - game.info.movie_path = std::move(movie_path); - game.has_hover_gif = true; - } - else if (std::string movie_path = sfo_dir + "/" + localized_movie; fs::is_file(movie_path)) - { - game.info.movie_path = std::move(movie_path); - game.has_hover_pam = true; - } - else if (std::string movie_path = sfo_dir + "/ICON1.PAM"; fs::is_file(movie_path)) - { - game.info.movie_path = std::move(movie_path); - game.has_hover_pam = true; - } - - const QString serial = QString::fromStdString(game.info.serial); - - m_games_mutex.lock(); - - // Read persistent_settings values - const QString last_played = m_persistent_settings->GetValue(gui::persistent::last_played, serial, "").toString(); - const quint64 playtime = m_persistent_settings->GetValue(gui::persistent::playtime, serial, 0).toULongLong(); - - // Set persistent_settings values if values exist - if (!last_played.isEmpty()) - { - m_persistent_settings->SetLastPlayed(serial, last_played, false); // No need to sync here. It would slow down the refresh anyway. - } - if (playtime > 0) - { - m_persistent_settings->SetPlaytime(serial, playtime, false); // No need to sync here. It would slow down the refresh anyway. - } - - m_serials.insert(serial); - - if (QString note = m_persistent_settings->GetValue(gui::persistent::notes, serial, "").toString(); !note.isEmpty()) - { - m_notes.insert_or_assign(serial, std::move(note)); - } - - if (QString title = m_persistent_settings->GetValue(gui::persistent::titles, serial, "").toString().simplified(); !title.isEmpty()) - { - m_titles.insert_or_assign(serial, std::move(title)); - } - - m_games_mutex.unlock(); - - QString qt_cat = QString::fromStdString(game.info.category); - - if (const auto boot_cat = thread_localized.category.cat_boot.find(qt_cat); boot_cat != thread_localized.category.cat_boot.cend()) - { - qt_cat = boot_cat->second; - } - else if (const auto data_cat = thread_localized.category.cat_data.find(qt_cat); data_cat != thread_localized.category.cat_data.cend()) - { - qt_cat = data_cat->second; - } - else if (game.info.category == cat_unknown) - { - qt_cat = thread_localized.category.unknown; - } - else - { - qt_cat = thread_localized.category.other; - } - - game.localized_category = std::move(qt_cat); - game.compat = m_game_compat->GetCompatibility(game.info.serial); - game.has_custom_config = fs::is_file(rpcs3::utils::get_custom_config_path(game.info.serial)); - game.has_custom_pad_config = fs::is_file(rpcs3::utils::get_custom_input_config_path(game.info.serial)); - - m_games.push(std::make_shared(std::move(game))); - }; - - const auto add_disc_dir = [this](const std::string& path, std::vector& legit_paths) - { - for (const auto& entry : fs::dir(path)) - { - if (m_refresh_watcher.isCanceled()) - { - break; - } - - if (!entry.is_directory || entry.name == "." || entry.name == "..") - { - continue; - } - - if (entry.name == "PS3_GAME" || std::regex_match(entry.name, std::regex("^PS3_GM[[:digit:]]{2}$"))) - { - push_path(path + "/" + entry.name, legit_paths); - } - } - }; - - m_refresh_watcher.setFuture(QtConcurrent::map(m_path_entries, [this, _hdd, add_disc_dir, add_game](const path_entry& entry) - { - std::vector legit_paths; - - if (entry.is_from_yml) - { - if (fs::is_file(entry.path + "/PARAM.SFO")) - { - push_path(entry.path, legit_paths); - } - else if (fs::is_file(entry.path + "/PS3_DISC.SFB")) - { - // Check if a path loaded from games.yml is already registered in add_dir(_hdd + "disc/"); - if (entry.path.starts_with(_hdd)) - { - std::string_view frag = std::string_view(entry.path).substr(_hdd.size()); - - if (frag.starts_with("disc/")) - { - // Our path starts from _hdd + 'disc/' - frag.remove_prefix(5); - - // Check if the remaining part is the only path component - if (frag.find_first_of('/') + 1 == 0) - { - game_list_log.trace("Removed duplicate: %s", entry.path); - - if (static std::unordered_set warn_once_list; warn_once_list.emplace(entry.path).second) - { - game_list_log.todo("Game at '%s' is using deprecated directory '/dev_hdd0/disc/'.\nConsider moving into '%s'.", entry.path, rpcs3::utils::get_games_dir()); - } - - return; - } - } - } - - add_disc_dir(entry.path, legit_paths); - } - else - { - game_list_log.trace("Invalid game path registered: %s", entry.path); - return; - } - } - else if (fs::is_file(entry.path + "/PS3_DISC.SFB")) - { - if (!entry.is_disc) - { - game_list_log.error("Invalid game path found in %s", entry.path); - return; - } - - add_disc_dir(entry.path, legit_paths); - } - else - { - if (entry.is_disc) - { - game_list_log.error("Invalid disc path found in %s", entry.path); - return; - } - - push_path(entry.path, legit_paths); - } - - for (const std::string& path : legit_paths) - { - add_game(path); - } - })); -} - -void game_list_frame::OnRefreshFinished() -{ - WaitAndAbortSizeCalcThreads(); - WaitAndAbortRepaintThreads(); - - for (auto&& g : m_games.pop_all()) - { - m_game_data.push_back(g); - } - - const Localized localized; - const std::string cat_unknown_localized = localized.category.unknown.toStdString(); - const s32 language_index = gui_application::get_language_id(); - const std::string localized_icon = fmt::format("ICON0_%02d.PNG", language_index); - const std::string localized_movie = fmt::format("ICON1_%02d.PAM", language_index); - - // Try to update the app version for disc games if there is a patch - // Also try to find updated game icons and movies - for (const game_info& entry : m_game_data) - { - if (entry->info.category != "DG") - continue; - - for (const auto& other : m_game_data) - { - if (other->info.category == "DG") - continue; - if (entry->info.serial != other->info.serial) - continue; - - // The patch is game data and must have the same serial and an app version - if (other->info.app_ver != cat_unknown_localized) - { - // Update the app version if it's higher than the disc's version (old games may not have an app version) - if (entry->info.app_ver == cat_unknown_localized || rpcs3::utils::version_is_bigger(other->info.app_ver, entry->info.app_ver, entry->info.serial, false)) - { - entry->info.app_ver = other->info.app_ver; - } - // Update the firmware version if possible and if it's higher than the disc's version - if (other->info.fw != cat_unknown_localized && rpcs3::utils::version_is_bigger(other->info.fw, entry->info.fw, entry->info.serial, true)) - { - entry->info.fw = other->info.fw; - } - // Update the parental level if possible and if it's higher than the disc's level - if (other->info.parental_lvl != 0 && other->info.parental_lvl > entry->info.parental_lvl) - { - entry->info.parental_lvl = other->info.parental_lvl; - } - } - - // Let's fetch the game data icon if preferred or if the path was empty for some reason - if ((m_prefer_game_data_icons && !entry->has_custom_icon) || entry->info.icon_path.empty()) - { - if (std::string icon_path = other->info.path + "/" + localized_icon; fs::is_file(icon_path)) - { - entry->info.icon_path = std::move(icon_path); - } - else if (std::string icon_path = other->info.path + "/ICON0.PNG"; fs::is_file(icon_path)) - { - entry->info.icon_path = std::move(icon_path); - } - } - - // Let's fetch the game data movie if preferred or if the path was empty - if (m_prefer_game_data_icons || entry->info.movie_path.empty()) - { - if (std::string movie_path = other->info.path + "/" + localized_movie; fs::is_file(movie_path)) - { - entry->info.movie_path = std::move(movie_path); - } - else if (std::string movie_path = other->info.path + "/ICON1.PAM"; fs::is_file(movie_path)) - { - entry->info.movie_path = std::move(movie_path); - } - } - } - } - - // Sort by name at the very least. - std::sort(m_game_data.begin(), m_game_data.end(), [&](const game_info& game1, const game_info& game2) - { - const QString serial1 = QString::fromStdString(game1->info.serial); - const QString serial2 = QString::fromStdString(game2->info.serial); - const QString& title1 = m_titles.contains(serial1) ? m_titles.at(serial1) : QString::fromStdString(game1->info.name); - const QString& title2 = m_titles.contains(serial2) ? m_titles.at(serial2) : QString::fromStdString(game2->info.name); - return title1.toLower() < title2.toLower(); - }); - - // clean up hidden games list - m_hidden_list.intersect(m_serials); - m_gui_settings->SetValue(gui::gl_hidden_list, QStringList(m_hidden_list.values())); - m_serials.clear(); - m_path_list.clear(); - m_path_entries.clear(); - - Refresh(); - - if (!std::exchange(m_initial_refresh_done, true)) - { - m_game_list->restore_layout(m_gui_settings->GetValue(gui::gl_state).toByteArray()); - m_game_list->sync_header_actions(m_columnActs, [this](int col) - { - return m_gui_settings->GetGamelistColVisibility(static_cast(col)); - }); - } - - // Emit signal and remove slots - Q_EMIT Refreshed(); - m_refresh_funcs_manage_type.reset(); - m_refresh_funcs_manage_type.emplace(); -} - -void game_list_frame::OnCompatFinished() -{ - for (const auto& game : m_game_data) - { - game->compat = m_game_compat->GetCompatibility(game->info.serial); - } - Refresh(); -} - -void game_list_frame::ToggleCategoryFilter(const QStringList& categories, bool show) -{ - QStringList& filters = m_is_list_layout ? m_category_filters : m_grid_category_filters; - - if (show) - { - filters.append(categories); - } - else - { - for (const QString& cat : categories) - { - filters.removeAll(cat); - } - } - - Refresh(); -} - -void game_list_frame::SaveSettings() -{ - for (int col = 0; col < m_columnActs.count(); ++col) - { - m_gui_settings->SetGamelistColVisibility(static_cast(col), m_columnActs[col]->isChecked()); - } - m_gui_settings->SetValue(gui::gl_sortCol, m_sort_column, false); - m_gui_settings->SetValue(gui::gl_sortAsc, m_col_sort_order == Qt::AscendingOrder, false); - m_gui_settings->SetValue(gui::gl_state, m_game_list->horizontalHeader()->saveState(), true); -} - -void game_list_frame::doubleClickedSlot(QTableWidgetItem* item) -{ - if (!item) - { - return; - } - - doubleClickedSlot(GetGameInfoByMode(item)); -} - -void game_list_frame::doubleClickedSlot(const game_info& game) -{ - if (!game) - { - return; - } - - sys_log.notice("Booting from gamelist per doubleclick..."); - Q_EMIT RequestBoot(game); -} - -void game_list_frame::ItemSelectionChangedSlot() -{ - game_info game = nullptr; - - if (m_is_list_layout) - { - if (const auto item = m_game_list->item(m_game_list->currentRow(), static_cast(gui::game_list_columns::icon)); item && item->isSelected()) - { - game = GetGameInfoByMode(item); - } - } - - Q_EMIT NotifyGameSelection(game); -} - -void game_list_frame::CreateShortcuts(const std::vector& games, const std::set& locations) -{ - if (games.empty()) - { - game_list_log.notice("Skip creating shortcuts. No games selected."); - return; - } - - if (locations.empty()) - { - game_list_log.error("Failed to create shortcuts. No locations selected."); - return; - } - - bool success = true; - - for (const game_info& gameinfo : games) - { - std::string gameid_token_value; - - const std::string dev_flash = g_cfg_vfs.get_dev_flash(); - - if (gameinfo->info.category == "DG" && !fs::is_file(rpcs3::utils::get_hdd0_dir() + "/game/" + gameinfo->info.serial + "/USRDIR/EBOOT.BIN")) - { - const usz ps3_game_dir_pos = fs::get_parent_dir(gameinfo->info.path).size(); - std::string relative_boot_dir = gameinfo->info.path.substr(ps3_game_dir_pos); - - if (usz char_pos = relative_boot_dir.find_first_not_of(fs::delim); char_pos != umax) - { - relative_boot_dir = relative_boot_dir.substr(char_pos); - } - else - { - relative_boot_dir.clear(); - } - - if (!relative_boot_dir.empty()) - { - if (relative_boot_dir != "PS3_GAME") - { - gameid_token_value = gameinfo->info.serial + "/" + relative_boot_dir; - } - else - { - gameid_token_value = gameinfo->info.serial; - } - } - } - else - { - gameid_token_value = gameinfo->info.serial; - } - -#ifdef __linux__ - const std::string target_cli_args = gameinfo->info.path.starts_with(dev_flash) ? fmt::format("--no-gui \"%%%%RPCS3_VFS%%%%:dev_flash/%s\"", gameinfo->info.path.substr(dev_flash.size())) : fmt::format("--no-gui \"%%%%RPCS3_GAMEID%%%%:%s\"", gameid_token_value); -#else - const std::string target_cli_args = gameinfo->info.path.starts_with(dev_flash) ? fmt::format("--no-gui \"%%RPCS3_VFS%%:dev_flash/%s\"", gameinfo->info.path.substr(dev_flash.size())) : fmt::format("--no-gui \"%%RPCS3_GAMEID%%:%s\"", gameid_token_value); -#endif - const std::string target_icon_dir = fmt::format("%sIcons/game_icons/%s/", fs::get_config_dir(), gameinfo->info.serial); - - if (!fs::create_path(target_icon_dir)) - { - game_list_log.error("Failed to create shortcut path %s (%s)", qstr(gameinfo->info.name).simplified(), target_icon_dir, fs::g_tls_error); - success = false; - continue; - } - - for (const gui::utils::shortcut_location& location : locations) - { - std::string destination; - - switch (location) - { - case gui::utils::shortcut_location::desktop: - destination = "desktop"; - break; - case gui::utils::shortcut_location::applications: - destination = "application menu"; - break; -#ifdef _WIN32 - case gui::utils::shortcut_location::rpcs3_shortcuts: - destination = "/games/shortcuts/"; - break; -#endif - } - - if (!gameid_token_value.empty() && gui::utils::create_shortcut(gameinfo->info.name, gameinfo->info.serial, target_cli_args, gameinfo->info.name, gameinfo->info.icon_path, target_icon_dir, location)) - { - game_list_log.success("Created %s shortcut for %s", destination, qstr(gameinfo->info.name).simplified()); - } - else - { - game_list_log.error("Failed to create %s shortcut for %s", destination, qstr(gameinfo->info.name).simplified()); - success = false; - } - } - } - -#ifdef _WIN32 - if (locations.size() == 1 && locations.contains(gui::utils::shortcut_location::rpcs3_shortcuts)) - { - return; - } -#endif - - if (success) - { - QMessageBox::information(this, tr("Success!"), tr("Successfully created shortcut(s).")); - } - else - { - QMessageBox::warning(this, tr("Warning!"), tr("Failed to create one or more shortcuts!")); - } -} - -void game_list_frame::ShowContextMenu(const QPoint& pos) -{ - QPoint global_pos; - game_info gameinfo; - - if (m_is_list_layout) - { - QTableWidgetItem* item = m_game_list->item(m_game_list->indexAt(pos).row(), static_cast(gui::game_list_columns::icon)); - global_pos = m_game_list->viewport()->mapToGlobal(pos); - gameinfo = GetGameInfoFromItem(item); - } - else if (game_list_grid_item* item = static_cast(m_game_grid->selected_item())) - { - gameinfo = item->game(); - global_pos = m_game_grid->mapToGlobal(pos); - } - - if (!gameinfo) - { - return; - } - - GameInfo current_game = gameinfo->info; - const QString serial = qstr(current_game.serial); - const QString name = qstr(current_game.name).simplified(); - - const std::string cache_base_dir = GetCacheDirBySerial(current_game.serial); - const std::string config_data_base_dir = GetDataDirBySerial(current_game.serial); - - // Make Actions - QMenu menu; - - static const auto is_game_running = [](const std::string& serial) - { - return !Emu.IsStopped(true) && (serial == Emu.GetTitleID() || (serial == "vsh.self" && Emu.IsVsh())); - }; - - const bool is_current_running_game = is_game_running(current_game.serial); - - QAction* boot = new QAction(gameinfo->has_custom_config ? (is_current_running_game ? tr("&Reboot with global configuration") : tr("&Boot with global configuration")) : (is_current_running_game ? tr("&Reboot") : tr("&Boot"))); - - QFont font = boot->font(); - font.setBold(true); - - if (gameinfo->has_custom_config) - { - QAction* boot_custom = menu.addAction(is_current_running_game ? tr("&Reboot with custom configuration") : tr("&Boot with custom configuration")); - boot_custom->setFont(font); - connect(boot_custom, &QAction::triggered, [this, gameinfo] - { - sys_log.notice("Booting from gamelist per context menu..."); - Q_EMIT RequestBoot(gameinfo); - }); - } - else - { - boot->setFont(font); - } - - menu.addAction(boot); - - { - QAction* boot_default = menu.addAction(is_current_running_game ? tr("&Reboot with default configuration") : tr("&Boot with default configuration")); - - connect(boot_default, &QAction::triggered, [this, gameinfo] - { - sys_log.notice("Booting from gamelist per context menu..."); - Q_EMIT RequestBoot(gameinfo, cfg_mode::default_config); - }); - - QAction* boot_manual = menu.addAction(is_current_running_game ? tr("&Reboot with manually selected configuration") : tr("&Boot with manually selected configuration")); - - connect(boot_manual, &QAction::triggered, [this, gameinfo] - { - if (const std::string file_path = QFileDialog::getOpenFileName(this, "Select Config File", "", tr("Config Files (*.yml);;All files (*.*)")).toStdString(); !file_path.empty()) - { - sys_log.notice("Booting from gamelist per context menu..."); - Q_EMIT RequestBoot(gameinfo, cfg_mode::custom_selection, file_path); - } - else - { - sys_log.notice("Manual config selection aborted."); - } - }); - } - - extern bool is_savestate_compatible(const std::string& filepath); - - if (const std::string sstate = get_savestate_file(current_game.serial, current_game.path, 0, 0); is_savestate_compatible(sstate)) - { - QAction* boot_state = menu.addAction(is_current_running_game ? tr("&Reboot with savestate") : tr("&Boot with savestate")); - connect(boot_state, &QAction::triggered, [this, gameinfo, sstate] - { - sys_log.notice("Booting savestate from gamelist per context menu..."); - Q_EMIT RequestBoot(gameinfo, cfg_mode::custom, "", sstate); - }); - } - - menu.addSeparator(); - - QAction* configure = menu.addAction(gameinfo->has_custom_config ? tr("&Change Custom Configuration") : tr("&Create Custom Configuration From Global Settings")); - QAction* create_game_default_config = gameinfo->has_custom_config ? nullptr : menu.addAction(tr("&Create Custom Configuration From Default Settings")); - QAction* pad_configure = menu.addAction(gameinfo->has_custom_pad_config ? tr("&Change Custom Gamepad Configuration") : tr("&Create Custom Gamepad Configuration")); - QAction* configure_patches = menu.addAction(tr("&Manage Game Patches")); - - menu.addSeparator(); - - QAction* create_cpu_cache = menu.addAction(tr("&Create LLVM Cache")); - - // Remove menu - QMenu* remove_menu = menu.addMenu(tr("&Remove")); - - if (gameinfo->has_custom_config) - { - QAction* remove_custom_config = remove_menu->addAction(tr("&Remove Custom Configuration")); - connect(remove_custom_config, &QAction::triggered, [this, current_game, gameinfo]() - { - if (RemoveCustomConfiguration(current_game.serial, gameinfo, true)) - { - ShowCustomConfigIcon(gameinfo); - } - }); - } - if (gameinfo->has_custom_pad_config) - { - QAction* remove_custom_pad_config = remove_menu->addAction(tr("&Remove Custom Gamepad Configuration")); - connect(remove_custom_pad_config, &QAction::triggered, [this, current_game, gameinfo]() - { - if (RemoveCustomPadConfiguration(current_game.serial, gameinfo, true)) - { - ShowCustomConfigIcon(gameinfo); - } - }); - } - - const bool has_cache_dir = fs::is_dir(cache_base_dir); - - if (has_cache_dir) - { - remove_menu->addSeparator(); - - QAction* remove_shaders_cache = remove_menu->addAction(tr("&Remove Shaders Cache")); - remove_shaders_cache->setEnabled(!is_current_running_game); - connect(remove_shaders_cache, &QAction::triggered, [this, cache_base_dir]() - { - RemoveShadersCache(cache_base_dir, true); - }); - QAction* remove_ppu_cache = remove_menu->addAction(tr("&Remove PPU Cache")); - remove_ppu_cache->setEnabled(!is_current_running_game); - connect(remove_ppu_cache, &QAction::triggered, [this, cache_base_dir]() - { - RemovePPUCache(cache_base_dir, true); - }); - QAction* remove_spu_cache = remove_menu->addAction(tr("&Remove SPU Cache")); - remove_spu_cache->setEnabled(!is_current_running_game); - connect(remove_spu_cache, &QAction::triggered, [this, cache_base_dir]() - { - RemoveSPUCache(cache_base_dir, true); - }); - } - - const std::string hdd1_cache_base_dir = rpcs3::utils::get_hdd1_dir() + "caches/"; - const bool has_hdd1_cache_dir = !GetDirListBySerial(hdd1_cache_base_dir, current_game.serial).empty(); - - if (has_hdd1_cache_dir) - { - QAction* remove_hdd1_cache = remove_menu->addAction(tr("&Remove HDD1 Cache")); - remove_hdd1_cache->setEnabled(!is_current_running_game); - connect(remove_hdd1_cache, &QAction::triggered, [this, hdd1_cache_base_dir, serial = current_game.serial]() - { - RemoveHDD1Cache(hdd1_cache_base_dir, serial, true); - }); - } - - if (has_cache_dir || has_hdd1_cache_dir) - { - QAction* remove_all_caches = remove_menu->addAction(tr("&Remove All Caches")); - remove_all_caches->setEnabled(!is_current_running_game); - connect(remove_all_caches, &QAction::triggered, [this, current_game, cache_base_dir, hdd1_cache_base_dir]() - { - if (is_game_running(current_game.serial)) - return; - - if (QMessageBox::question(this, tr("Confirm Removal"), tr("Remove all caches?")) != QMessageBox::Yes) - return; - - RemoveContentPath(cache_base_dir, "cache"); - RemoveHDD1Cache(hdd1_cache_base_dir, current_game.serial); - }); - } - - const std::string savestate_dir = fs::get_config_dir() + "savestates/" + current_game.serial; - - if (fs::is_dir(savestate_dir)) - { - remove_menu->addSeparator(); - - QAction* remove_savestate = remove_menu->addAction(tr("&Remove Savestates")); - remove_savestate->setEnabled(!is_current_running_game); - connect(remove_savestate, &QAction::triggered, [this, current_game, savestate_dir]() - { - if (is_game_running(current_game.serial)) - return; - - if (QMessageBox::question(this, tr("Confirm Removal"), tr("Remove savestates?")) != QMessageBox::Yes) - return; - - RemoveContentPath(savestate_dir, "savestate"); - }); - } - - // Disable the Remove menu if empty - remove_menu->setEnabled(!remove_menu->isEmpty()); - - menu.addSeparator(); - - // Manage Game menu - QMenu* manage_game_menu = menu.addMenu(tr("&Manage Game")); - - // Create game shortcuts - QAction* create_desktop_shortcut = manage_game_menu->addAction(tr("&Create Desktop Shortcut")); - connect(create_desktop_shortcut, &QAction::triggered, this, [this, gameinfo]() - { - CreateShortcuts({gameinfo}, {gui::utils::shortcut_location::desktop}); - }); -#ifdef _WIN32 - QAction* create_start_menu_shortcut = manage_game_menu->addAction(tr("&Create Start Menu Shortcut")); -#elif defined(__APPLE__) - QAction* create_start_menu_shortcut = manage_game_menu->addAction(tr("&Create Launchpad Shortcut")); -#else - QAction* create_start_menu_shortcut = manage_game_menu->addAction(tr("&Create Application Menu Shortcut")); -#endif - connect(create_start_menu_shortcut, &QAction::triggered, this, [this, gameinfo]() - { - CreateShortcuts({gameinfo}, {gui::utils::shortcut_location::applications}); - }); - - manage_game_menu->addSeparator(); - - // Hide/rename game in game list - QAction* hide_serial = manage_game_menu->addAction(tr("&Hide From Game List")); - hide_serial->setCheckable(true); - hide_serial->setChecked(m_hidden_list.contains(serial)); - QAction* rename_title = manage_game_menu->addAction(tr("&Rename In Game List")); - - // Edit tooltip notes/reset time played - QAction* edit_notes = manage_game_menu->addAction(tr("&Edit Tooltip Notes")); - QAction* reset_time_played = manage_game_menu->addAction(tr("&Reset Time Played")); - - manage_game_menu->addSeparator(); - - // Remove game - QAction* remove_game = manage_game_menu->addAction(tr("&Remove %1").arg(gameinfo->localized_category)); - remove_game->setEnabled(!is_current_running_game); - - // Custom Images menu - QMenu* icon_menu = menu.addMenu(tr("&Custom Images")); - const std::array custom_icon_actions = - { - icon_menu->addAction(tr("&Import Custom Icon")), - icon_menu->addAction(tr("&Replace Custom Icon")), - icon_menu->addAction(tr("&Remove Custom Icon"))}; - icon_menu->addSeparator(); - const std::array custom_gif_actions = - { - icon_menu->addAction(tr("&Import Hover Gif")), - icon_menu->addAction(tr("&Replace Hover Gif")), - icon_menu->addAction(tr("&Remove Hover Gif"))}; - icon_menu->addSeparator(); - const std::array custom_shader_icon_actions = - { - icon_menu->addAction(tr("&Import Custom Shader Loading Background")), - icon_menu->addAction(tr("&Replace Custom Shader Loading Background")), - icon_menu->addAction(tr("&Remove Custom Shader Loading Background"))}; - - if (const std::string custom_icon_dir_path = fs::get_config_dir() + "/Icons/game_icons/" + current_game.serial; - fs::create_path(custom_icon_dir_path)) - { - enum class icon_action - { - add, - replace, - remove - }; - enum class icon_type - { - game_list, - hover_gif, - shader_load - }; - - const auto handle_icon = [this, serial](const QString& game_icon_path, const QString& suffix, icon_action action, icon_type type) - { - QString icon_path; - - if (action != icon_action::remove) - { - QString msg; - switch (type) - { - case icon_type::game_list: - msg = tr("Select Custom Icon"); - break; - case icon_type::hover_gif: - msg = tr("Select Custom Hover Gif"); - break; - case icon_type::shader_load: - msg = tr("Select Custom Shader Loading Background"); - break; - } - icon_path = QFileDialog::getOpenFileName(this, msg, "", tr("%0 (*.%0);;All files (*.*)").arg(suffix)); - } - if (action == icon_action::remove || !icon_path.isEmpty()) - { - bool refresh = false; - - QString msg; - switch (type) - { - case icon_type::game_list: - msg = tr("Remove Custom Icon of %0?").arg(serial); - break; - case icon_type::hover_gif: - msg = tr("Remove Custom Hover Gif of %0?").arg(serial); - break; - case icon_type::shader_load: - msg = tr("Remove Custom Shader Loading Background of %0?").arg(serial); - break; - } - - if (action == icon_action::replace || (action == icon_action::remove && - QMessageBox::question(this, tr("Confirm Removal"), msg) == QMessageBox::Yes)) - { - if (QFile file(game_icon_path); file.exists() && !file.remove()) - { - game_list_log.error("Could not remove old file: '%s'", game_icon_path, file.errorString()); - QMessageBox::warning(this, tr("Warning!"), tr("Failed to remove the old file!")); - return; - } - - game_list_log.success("Removed file: '%s'", game_icon_path); - if (action == icon_action::remove) - { - refresh = true; - } - } - - if (action != icon_action::remove) - { - if (!QFile::copy(icon_path, game_icon_path)) - { - game_list_log.error("Could not import file '%s' to '%s'.", icon_path, game_icon_path); - QMessageBox::warning(this, tr("Warning!"), tr("Failed to import the new file!")); - } - else - { - game_list_log.success("Imported file '%s' to '%s'", icon_path, game_icon_path); - refresh = true; - } - } - - if (refresh) - { - Refresh(true); - } - } - }; - - const std::vector&>> icon_map = - { - {icon_type::game_list, "/ICON0.PNG", "png", custom_icon_actions}, - {icon_type::hover_gif, "/hover.gif", "gif", custom_gif_actions}, - {icon_type::shader_load, "/PIC1.PNG", "png", custom_shader_icon_actions}, - }; - - for (const auto& [type, icon_name, suffix, actions] : icon_map) - { - const QString icon_path = qstr(custom_icon_dir_path) + icon_name; - - if (QFile::exists(icon_path)) - { - actions[static_cast(icon_action::add)]->setVisible(false); - connect(actions[static_cast(icon_action::replace)], &QAction::triggered, this, [handle_icon, icon_path, t = type, s = suffix] - { - handle_icon(icon_path, s, icon_action::replace, t); - }); - connect(actions[static_cast(icon_action::remove)], &QAction::triggered, this, [handle_icon, icon_path, t = type, s = suffix] - { - handle_icon(icon_path, s, icon_action::remove, t); - }); - } - else - { - connect(actions[static_cast(icon_action::add)], &QAction::triggered, this, [handle_icon, icon_path, t = type, s = suffix] - { - handle_icon(icon_path, s, icon_action::add, t); - }); - actions[static_cast(icon_action::replace)]->setVisible(false); - actions[static_cast(icon_action::remove)]->setEnabled(false); - } - } - } - else - { - game_list_log.error("Could not create path '%s'", custom_icon_dir_path); - icon_menu->setEnabled(false); - } - - menu.addSeparator(); - - // Open Folder menu - QMenu* open_folder_menu = menu.addMenu(tr("&Open Folder")); - - const bool is_disc_game = qstr(current_game.category) == cat::cat_disc_game; - const std::string captures_dir = fs::get_config_dir() + "/captures/"; - const std::string recordings_dir = fs::get_config_dir() + "/recordings/" + current_game.serial; - const std::string screenshots_dir = fs::get_config_dir() + "/screenshots/" + current_game.serial; - std::vector data_dir_list; - - if (is_disc_game) - { - QAction* open_disc_game_folder = open_folder_menu->addAction(tr("&Open Disc Game Folder")); - connect(open_disc_game_folder, &QAction::triggered, [current_game]() - { - gui::utils::open_dir(current_game.path); - }); - - data_dir_list = GetDirListBySerial(rpcs3::utils::get_hdd0_dir() + "game/", current_game.serial); // It could be absent for a disc game - } - else - { - data_dir_list.push_back(current_game.path); - } - - if (!data_dir_list.empty()) // "true" if data path is present (it could be absent for a disc game) - { - QAction* open_data_folder = open_folder_menu->addAction(tr("&Open %0 Folder").arg(is_disc_game ? tr("Game Data") : gameinfo->localized_category)); - connect(open_data_folder, &QAction::triggered, [data_dir_list]() - { - for (const std::string& data_dir : data_dir_list) - { - gui::utils::open_dir(data_dir); - } - }); - } - - if (gameinfo->has_custom_config) - { - QAction* open_config_dir = open_folder_menu->addAction(tr("&Open Custom Config Folder")); - connect(open_config_dir, &QAction::triggered, [current_game]() - { - const std::string config_path = rpcs3::utils::get_custom_config_path(current_game.serial); - - if (fs::is_file(config_path)) - gui::utils::open_dir(config_path); - }); - } - - // This is a debug feature, let's hide it by reusing debug tab protection - if (m_gui_settings->GetValue(gui::m_showDebugTab).toBool() && has_cache_dir) - { - QAction* open_cache_folder = open_folder_menu->addAction(tr("&Open Cache Folder")); - connect(open_cache_folder, &QAction::triggered, [cache_base_dir]() - { - gui::utils::open_dir(cache_base_dir); - }); - } - - if (fs::is_dir(config_data_base_dir)) - { - QAction* open_config_data_dir = open_folder_menu->addAction(tr("&Open Config Data Folder")); - connect(open_config_data_dir, &QAction::triggered, [config_data_base_dir]() - { - gui::utils::open_dir(config_data_base_dir); - }); - } - - if (fs::is_dir(savestate_dir)) - { - QAction* open_savestate_dir = open_folder_menu->addAction(tr("&Open Savestate Folder")); - connect(open_savestate_dir, &QAction::triggered, [savestate_dir]() - { - gui::utils::open_dir(savestate_dir); - }); - } - - QAction* open_captures_dir = open_folder_menu->addAction(tr("&Open Captures Folder")); - connect(open_captures_dir, &QAction::triggered, [captures_dir]() - { - gui::utils::open_dir(captures_dir); - }); - - if (fs::is_dir(recordings_dir)) - { - QAction* open_recordings_dir = open_folder_menu->addAction(tr("&Open Recordings Folder")); - connect(open_recordings_dir, &QAction::triggered, [recordings_dir]() - { - gui::utils::open_dir(recordings_dir); - }); - } - - if (fs::is_dir(screenshots_dir)) - { - QAction* open_screenshots_dir = open_folder_menu->addAction(tr("&Open Screenshots Folder")); - connect(open_screenshots_dir, &QAction::triggered, [screenshots_dir]() - { - gui::utils::open_dir(screenshots_dir); - }); - } - - // Copy Info menu - QMenu* info_menu = menu.addMenu(tr("&Copy Info")); - QAction* copy_info = info_menu->addAction(tr("&Copy Name + Serial")); - QAction* copy_name = info_menu->addAction(tr("&Copy Name")); - QAction* copy_serial = info_menu->addAction(tr("&Copy Serial")); - - menu.addSeparator(); - - QAction* check_compat = menu.addAction(tr("&Check Game Compatibility")); - QAction* download_compat = menu.addAction(tr("&Download Compatibility Database")); - - connect(boot, &QAction::triggered, this, [this, gameinfo]() - { - sys_log.notice("Booting from gamelist per context menu..."); - Q_EMIT RequestBoot(gameinfo, cfg_mode::global); - }); - - auto configure_l = [this, current_game, gameinfo](bool create_cfg_from_global_cfg) - { - settings_dialog dlg(m_gui_settings, m_emu_settings, 0, this, ¤t_game, create_cfg_from_global_cfg); - - connect(&dlg, &settings_dialog::EmuSettingsApplied, [this, gameinfo]() - { - if (!gameinfo->has_custom_config) - { - gameinfo->has_custom_config = true; - ShowCustomConfigIcon(gameinfo); - } - Q_EMIT NotifyEmuSettingsChange(); - }); - - dlg.exec(); - }; - - if (create_game_default_config) - { - connect(configure, &QAction::triggered, this, [configure_l]() - { - configure_l(true); - }); - connect(create_game_default_config, &QAction::triggered, this, [configure_l = std::move(configure_l)]() - { - configure_l(false); - }); - } - else - { - connect(configure, &QAction::triggered, this, [configure_l = std::move(configure_l)]() - { - configure_l(true); - }); - } - - connect(pad_configure, &QAction::triggered, this, [this, current_game, gameinfo]() - { - pad_settings_dialog dlg(m_gui_settings, this, ¤t_game); - - if (dlg.exec() == QDialog::Accepted && !gameinfo->has_custom_pad_config) - { - gameinfo->has_custom_pad_config = true; - ShowCustomConfigIcon(gameinfo); - } - }); - connect(hide_serial, &QAction::triggered, this, [serial, this](bool checked) - { - if (checked) - m_hidden_list.insert(serial); - else - m_hidden_list.remove(serial); - - m_gui_settings->SetValue(gui::gl_hidden_list, QStringList(m_hidden_list.values())); - Refresh(); - }); - connect(create_cpu_cache, &QAction::triggered, this, [gameinfo, this] - { - if (m_gui_settings->GetBootConfirmation(this)) - { - CreateCPUCaches(gameinfo); - } - }); - connect(remove_game, &QAction::triggered, this, [this, current_game, gameinfo, cache_base_dir, hdd1_cache_base_dir, name] - { - if (is_game_running(current_game.serial)) - { - QMessageBox::critical(this, tr("Cannot Remove Game"), tr("The PS3 application is still running, it cannot be removed!")); - return; - } - - const bool is_disc_game = qstr(current_game.category) == cat::cat_disc_game; - const bool is_in_games_dir = is_disc_game && Emu.IsPathInsideDir(current_game.path, rpcs3::utils::get_games_dir()); - std::vector data_dir_list; - - if (is_disc_game) - { - data_dir_list = GetDirListBySerial(rpcs3::utils::get_hdd0_dir() + "game/", current_game.serial); - } - else - { - data_dir_list.push_back(current_game.path); - } - - const bool has_data_dir = !data_dir_list.empty(); // "true" if data path is present (it could be absent for a disc game) - QString text = tr("%0 - %1\n").arg(qstr(current_game.serial)).arg(name); - - if (is_disc_game) - { - text += tr("\nDisc Game Info:\nPath: %0\n").arg(qstr(current_game.path)); - - if (current_game.size_on_disk != umax) // If size was properly detected - { - text += tr("Size: %0\n").arg(gui::utils::format_byte_size(current_game.size_on_disk)); - } - } - - if (has_data_dir) - { - u64 total_data_size = 0; - - text += tr("\n%0 Info:\n").arg(is_disc_game ? tr("Game Data") : gameinfo->localized_category); - - for (const std::string& data_dir : data_dir_list) - { - text += tr("Path: %0\n").arg(qstr(data_dir)); - - if (const u64 data_size = fs::get_dir_size(data_dir, 1); data_size != umax) // If size was properly detected - { - total_data_size += data_size; - text += tr("Size: %0\n").arg(gui::utils::format_byte_size(data_size)); - } - } - - if (data_dir_list.size() > 1) - { - text += tr("Total size: %0\n").arg(gui::utils::format_byte_size(total_data_size)); - } - } - - if (fs::device_stat stat{}; fs::statfs(rpcs3::utils::get_hdd0_dir(), stat)) // Retrieve disk space info on data path's drive - { - text += tr("\nCurrent free disk space: %0\n").arg(gui::utils::format_byte_size(stat.avail_free)); - } - - if (has_data_dir) - { - text += tr("\nPermanently remove %0 and selected (optional) contents from drive?\n").arg(is_disc_game ? tr("Game Data") : gameinfo->localized_category); - } - else - { - text += tr("\nPermanently remove selected (optional) contents from drive?\n"); - } - - QMessageBox mb(QMessageBox::Question, tr("Confirm %0 Removal").arg(gameinfo->localized_category), text, QMessageBox::Yes | QMessageBox::No, this); - QCheckBox* disc = new QCheckBox(tr("Remove title from game list (Disc Game path is not removed!)")); - QCheckBox* caches = new QCheckBox(tr("Remove caches and custom configs")); - QCheckBox* icons = new QCheckBox(tr("Remove icons and shortcuts")); - QCheckBox* savestate = new QCheckBox(tr("Remove savestates")); - QCheckBox* captures = new QCheckBox(tr("Remove captures")); - QCheckBox* recordings = new QCheckBox(tr("Remove recordings")); - QCheckBox* screenshots = new QCheckBox(tr("Remove screenshots")); - - if (is_disc_game) - { - if (is_in_games_dir) - { - disc->setToolTip(tr("Title located under auto-detection \"games\" folder cannot be removed")); - disc->setDisabled(true); - } - else - { - disc->setChecked(true); - } - } - else - { - disc->setVisible(false); - } - - caches->setChecked(true); - icons->setChecked(true); - mb.setCheckBox(disc); - - QGridLayout* grid = qobject_cast(mb.layout()); - int row, column, rowSpan, columnSpan; - - grid->getItemPosition(grid->indexOf(disc), &row, &column, &rowSpan, &columnSpan); - grid->addWidget(caches, row + 3, column, rowSpan, columnSpan); - grid->addWidget(icons, row + 4, column, rowSpan, columnSpan); - grid->addWidget(savestate, row + 5, column, rowSpan, columnSpan); - grid->addWidget(captures, row + 6, column, rowSpan, columnSpan); - grid->addWidget(recordings, row + 7, column, rowSpan, columnSpan); - grid->addWidget(screenshots, row + 8, column, rowSpan, columnSpan); - - if (mb.exec() == QMessageBox::Yes) - { - const bool remove_caches = caches->isChecked(); - - // Remove data path in "dev_hdd0/game" folder (if any) - if (has_data_dir && RemoveContentPathList(data_dir_list, gameinfo->localized_category.toStdString()) != data_dir_list.size()) - { - QMessageBox::critical(this, tr("Failure!"), remove_caches ? tr("Failed to remove %0 from drive!\nPath: %1\nCaches and custom configs have been left intact.").arg(name).arg(qstr(data_dir_list[0])) : tr("Failed to remove %0 from drive!\nPath: %1").arg(name).arg(qstr(data_dir_list[0]))); - - return; - } - - // Remove lock file in "dev_hdd0/game/$locks" folder (if any) - RemoveContentBySerial(rpcs3::utils::get_hdd0_dir() + "game/$locks/", current_game.serial, "lock"); - - // Remove caches in "cache" and "dev_hdd1/caches" folders (if any) and custom configs in "config/custom_config" folder (if any) - if (remove_caches) - { - RemoveContentPath(cache_base_dir, "cache"); - RemoveHDD1Cache(hdd1_cache_base_dir, current_game.serial); - - RemoveCustomConfiguration(current_game.serial); - RemoveCustomPadConfiguration(current_game.serial); - } - - // Remove icons in "Icons/game_icons" folder, shortcuts in "games/shortcuts" folder and from desktop/start menu - if (icons->isChecked()) - { - RemoveContentBySerial(fs::get_config_dir() + "Icons/game_icons/", current_game.serial, "icons"); - RemoveContentBySerial(fs::get_config_dir() + "games/shortcuts/", name.toStdString() + ".lnk", "link"); - // TODO: Remove shortcuts from desktop/start menu - } - - if (savestate->isChecked()) - { - RemoveContentBySerial(fs::get_config_dir() + "savestates/", current_game.serial, "savestate"); - } - - if (captures->isChecked()) - { - RemoveContentBySerial(fs::get_config_dir() + "captures/", current_game.serial, "captures"); - } - - if (recordings->isChecked()) - { - RemoveContentBySerial(fs::get_config_dir() + "recordings/", current_game.serial, "recordings"); - } - - if (screenshots->isChecked()) - { - RemoveContentBySerial(fs::get_config_dir() + "screenshots/", current_game.serial, "screenshots"); - } - - m_game_data.erase(std::remove(m_game_data.begin(), m_game_data.end(), gameinfo), m_game_data.end()); - game_list_log.success("Removed %s - %s", gameinfo->localized_category, current_game.name); - - std::vector serials_to_remove_from_yml{}; - - // Prepare list of serials (title id) to remove in "games.yml" file (if any) - if (is_disc_game && disc->isChecked()) - { - serials_to_remove_from_yml.push_back(current_game.serial); - } - - // Finally, refresh the game list. - // Hidden list in "GuiConfigs/CurrentSettings.ini" file is also properly updated (title removed) if needed - Refresh(true, serials_to_remove_from_yml); - } - }); - connect(configure_patches, &QAction::triggered, this, [this, gameinfo]() - { - patch_manager_dialog patch_manager(m_gui_settings, m_game_data, gameinfo->info.serial, gameinfo->GetGameVersion(), this); - patch_manager.exec(); - }); - connect(check_compat, &QAction::triggered, this, [serial] - { - const QString link = "https://rpcs3.net/compatibility?g=" + serial; - QDesktopServices::openUrl(QUrl(link)); - }); - connect(download_compat, &QAction::triggered, this, [this] - { - m_game_compat->RequestCompatibility(true); - }); - connect(rename_title, &QAction::triggered, this, [this, name, serial, global_pos] - { - const QString custom_title = m_persistent_settings->GetValue(gui::persistent::titles, serial, "").toString(); - const QString old_title = custom_title.isEmpty() ? name : custom_title; - - input_dialog dlg(128, old_title, tr("Rename Title"), tr("%0\n%1\n\nYou can clear the line in order to use the original title.").arg(name).arg(serial), name, this); - dlg.move(global_pos); - - if (dlg.exec() == QDialog::Accepted) - { - const QString new_title = dlg.get_input_text().simplified(); - - if (new_title.isEmpty() || new_title == name) - { - m_titles.erase(serial); - m_persistent_settings->RemoveValue(gui::persistent::titles, serial); - } - else - { - m_titles.insert_or_assign(serial, new_title); - m_persistent_settings->SetValue(gui::persistent::titles, serial, new_title); - } - Refresh(true); // full refresh in order to reliably sort the list - } - }); - connect(edit_notes, &QAction::triggered, this, [this, name, serial] - { - bool accepted; - const QString old_notes = m_persistent_settings->GetValue(gui::persistent::notes, serial, "").toString(); - const QString new_notes = QInputDialog::getMultiLineText(this, tr("Edit Tooltip Notes"), tr("%0\n%1").arg(name).arg(serial), old_notes, &accepted); - - if (accepted) - { - if (new_notes.simplified().isEmpty()) - { - m_notes.erase(serial); - m_persistent_settings->RemoveValue(gui::persistent::notes, serial); - } - else - { - m_notes.insert_or_assign(serial, new_notes); - m_persistent_settings->SetValue(gui::persistent::notes, serial, new_notes); - } - Refresh(); - } - }); - connect(reset_time_played, &QAction::triggered, this, [this, name, serial] - { - if (QMessageBox::question(this, tr("Confirm Reset"), tr("Reset time played?\n\n%0 [%1]").arg(name).arg(serial)) == QMessageBox::Yes) - { - m_persistent_settings->SetPlaytime(serial, 0, false); - m_persistent_settings->SetLastPlayed(serial, 0, true); - Refresh(); - } - }); - connect(copy_info, &QAction::triggered, this, [name, serial] - { - QApplication::clipboard()->setText(name % QStringLiteral(" [") % serial % QStringLiteral("]")); - }); - connect(copy_name, &QAction::triggered, this, [name] - { - QApplication::clipboard()->setText(name); - }); - connect(copy_serial, &QAction::triggered, this, [serial] - { - QApplication::clipboard()->setText(serial); - }); - - // Disable options depending on software category - const QString category = qstr(current_game.category); - - if (category == cat::cat_ps3_os) - { - remove_game->setEnabled(false); - } - else if (category != cat::cat_disc_game && category != cat::cat_hdd_game) - { - check_compat->setEnabled(false); - } - - menu.exec(global_pos); -} - -bool game_list_frame::CreateCPUCaches(const std::string& path, const std::string& serial) -{ - Emu.GracefulShutdown(false); - Emu.SetForceBoot(true); - - if (const auto error = Emu.BootGame(fs::is_file(path) ? fs::get_parent_dir(path) : path, serial, true); error != game_boot_result::no_errors) - { - game_list_log.error("Could not create LLVM caches for %s, error: %s", path, error); - return false; - } - - game_list_log.warning("Creating LLVM Caches for %s", path); - return true; -} - -bool game_list_frame::CreateCPUCaches(const game_info& game) -{ - return game && CreateCPUCaches(game->info.path, game->info.serial); -} - -bool game_list_frame::RemoveCustomConfiguration(const std::string& title_id, const game_info& game, bool is_interactive) -{ - const std::string path = rpcs3::utils::get_custom_config_path(title_id); - - if (!fs::is_file(path)) - return true; - - if (is_interactive && QMessageBox::question(this, tr("Confirm Removal"), tr("Remove custom game configuration?")) != QMessageBox::Yes) - return true; - - bool result = true; - - if (fs::is_file(path)) - { - if (fs::remove_file(path)) - { - if (game) - { - game->has_custom_config = false; - } - game_list_log.success("Removed configuration file: %s", path); - } - else - { - game_list_log.fatal("Failed to remove configuration file: %s\nError: %s", path, fs::g_tls_error); - result = false; - } - } - - if (is_interactive && !result) - { - QMessageBox::warning(this, tr("Warning!"), tr("Failed to remove configuration file!")); - } - - return result; -} - -bool game_list_frame::RemoveCustomPadConfiguration(const std::string& title_id, const game_info& game, bool is_interactive) -{ - if (title_id.empty()) - return true; - - const std::string config_dir = rpcs3::utils::get_input_config_dir(title_id); - - if (!fs::is_dir(config_dir)) - return true; - - if (is_interactive && QMessageBox::question(this, tr("Confirm Removal"), (!Emu.IsStopped(true) && Emu.GetTitleID() == title_id) ? tr("Remove custom pad configuration?\nYour configuration will revert to the global pad settings.") : tr("Remove custom pad configuration?")) != QMessageBox::Yes) - return true; - - g_cfg_input_configs.load(); - g_cfg_input_configs.active_configs.erase(title_id); - g_cfg_input_configs.save(); - game_list_log.notice("Removed active input configuration entry for key '%s'", title_id); - - if (QDir(qstr(config_dir)).removeRecursively()) - { - if (game) - { - game->has_custom_pad_config = false; - } - if (!Emu.IsStopped(true) && Emu.GetTitleID() == title_id) - { - pad::set_enabled(false); - pad::reset(title_id); - pad::set_enabled(true); - } - game_list_log.notice("Removed pad configuration directory: %s", config_dir); - return true; - } - - if (is_interactive) - { - QMessageBox::warning(this, tr("Warning!"), tr("Failed to completely remove pad configuration directory!")); - game_list_log.fatal("Failed to completely remove pad configuration directory: %s\nError: %s", config_dir, fs::g_tls_error); - } - return false; -} - -bool game_list_frame::RemoveShadersCache(const std::string& base_dir, bool is_interactive) -{ - if (!fs::is_dir(base_dir)) - return true; - - if (is_interactive && QMessageBox::question(this, tr("Confirm Removal"), tr("Remove shaders cache?")) != QMessageBox::Yes) - return true; - - u32 caches_removed = 0; - u32 caches_total = 0; - - const QStringList filter{QStringLiteral("shaders_cache")}; - const QString q_base_dir = qstr(base_dir); - - QDirIterator dir_iter(q_base_dir, filter, QDir::Dirs | QDir::NoDotAndDotDot, QDirIterator::Subdirectories); - - while (dir_iter.hasNext()) - { - const QString filepath = dir_iter.next(); - - if (QDir(filepath).removeRecursively()) - { - ++caches_removed; - game_list_log.notice("Removed shaders cache dir: %s", filepath); - } - else - { - game_list_log.warning("Could not completely remove shaders cache dir: %s", filepath); - } - - ++caches_total; - } - - const bool success = caches_total == caches_removed; - - if (success) - game_list_log.success("Removed shaders cache in %s", base_dir); - else - game_list_log.fatal("Only %d/%d shaders cache dirs could be removed in %s", caches_removed, caches_total, base_dir); - - if (QDir(q_base_dir).isEmpty()) - { - if (fs::remove_dir(base_dir)) - game_list_log.notice("Removed empty shader cache directory: %s", base_dir); - else - game_list_log.error("Could not remove empty shader cache directory: '%s' (%s)", base_dir, fs::g_tls_error); - } - - return success; -} - -bool game_list_frame::RemovePPUCache(const std::string& base_dir, bool is_interactive) -{ - if (!fs::is_dir(base_dir)) - return true; - - if (is_interactive && QMessageBox::question(this, tr("Confirm Removal"), tr("Remove PPU cache?")) != QMessageBox::Yes) - return true; - - u32 files_removed = 0; - u32 files_total = 0; - - const QStringList filter{QStringLiteral("v*.obj"), QStringLiteral("v*.obj.gz")}; - const QString q_base_dir = qstr(base_dir); - - QDirIterator dir_iter(q_base_dir, filter, QDir::Files | QDir::NoDotAndDotDot, QDirIterator::Subdirectories); - - while (dir_iter.hasNext()) - { - const QString filepath = dir_iter.next(); - - if (QFile::remove(filepath)) - { - ++files_removed; - game_list_log.notice("Removed PPU cache file: %s", filepath); - } - else - { - game_list_log.warning("Could not remove PPU cache file: %s", filepath); - } - - ++files_total; - } - - const bool success = files_total == files_removed; - - if (success) - game_list_log.success("Removed PPU cache in %s", base_dir); - else - game_list_log.fatal("Only %d/%d PPU cache files could be removed in %s", files_removed, files_total, base_dir); - - if (QDir(q_base_dir).isEmpty()) - { - if (fs::remove_dir(base_dir)) - game_list_log.notice("Removed empty PPU cache directory: %s", base_dir); - else - game_list_log.error("Could not remove empty PPU cache directory: '%s' (%s)", base_dir, fs::g_tls_error); - } - - return success; -} - -bool game_list_frame::RemoveSPUCache(const std::string& base_dir, bool is_interactive) -{ - if (!fs::is_dir(base_dir)) - return true; - - if (is_interactive && QMessageBox::question(this, tr("Confirm Removal"), tr("Remove SPU cache?")) != QMessageBox::Yes) - return true; - - u32 files_removed = 0; - u32 files_total = 0; - - const QStringList filter{QStringLiteral("spu*.dat"), QStringLiteral("spu*.dat.gz"), QStringLiteral("spu*.obj"), QStringLiteral("spu*.obj.gz")}; - const QString q_base_dir = qstr(base_dir); - - QDirIterator dir_iter(q_base_dir, filter, QDir::Files | QDir::NoDotAndDotDot, QDirIterator::Subdirectories); - - while (dir_iter.hasNext()) - { - const QString filepath = dir_iter.next(); - - if (QFile::remove(filepath)) - { - ++files_removed; - game_list_log.notice("Removed SPU cache file: %s", filepath); - } - else - { - game_list_log.warning("Could not remove SPU cache file: %s", filepath); - } - - ++files_total; - } - - const bool success = files_total == files_removed; - - if (success) - game_list_log.success("Removed SPU cache in %s", base_dir); - else - game_list_log.fatal("Only %d/%d SPU cache files could be removed in %s", files_removed, files_total, base_dir); - - if (QDir(q_base_dir).isEmpty()) - { - if (fs::remove_dir(base_dir)) - game_list_log.notice("Removed empty SPU cache directory: %s", base_dir); - else - game_list_log.error("Could not remove empty SPU cache directory: '%s' (%s)", base_dir, fs::g_tls_error); - } - - return success; -} - -void game_list_frame::RemoveHDD1Cache(const std::string& base_dir, const std::string& title_id, bool is_interactive) -{ - if (!fs::is_dir(base_dir)) - return; - - if (is_interactive && QMessageBox::question(this, tr("Confirm Removal"), tr("Remove HDD1 cache?")) != QMessageBox::Yes) - return; - - u32 dirs_removed = 0; - u32 dirs_total = 0; - - const QString q_base_dir = qstr(base_dir); - - const QStringList filter{qstr(title_id + "_*")}; - - QDirIterator dir_iter(q_base_dir, filter, QDir::Dirs | QDir::NoDotAndDotDot); - - while (dir_iter.hasNext()) - { - const QString filepath = dir_iter.next(); - - if (fs::remove_all(filepath.toStdString())) - { - ++dirs_removed; - game_list_log.notice("Removed HDD1 cache directory: %s", filepath); - } - else - { - game_list_log.warning("Could not remove HDD1 cache directory: %s", filepath); - } - - ++dirs_total; - } - - const bool success = dirs_removed == dirs_total; - - if (success) - game_list_log.success("Removed HDD1 cache in %s (%s)", base_dir, title_id); - else - game_list_log.fatal("Only %d/%d HDD1 cache directories could be removed in %s (%s)", dirs_removed, dirs_total, base_dir, title_id); -} - -void game_list_frame::BatchActionBySerials(progress_dialog* pdlg, const std::set& serials, QString progressLabel, std::function action, std::function cancel_log, bool refresh_on_finish, bool can_be_concurrent, std::function should_wait_cb) -{ - // Concurrent tasks should not wait (at least not in current implementation) - ensure(!should_wait_cb || !can_be_concurrent); - - g_system_progress_canceled = false; - - const std::shared_ptr> iterate_over_serial = std::make_shared>(); - - const std::shared_ptr> index = std::make_shared>(0); - - const int serials_size = ::narrow(serials.size()); - - *iterate_over_serial = [=, this, index_ptr = index](int index) - { - if (index == serials_size) - { - return false; - } - - const std::string& serial = *std::next(serials.begin(), index); - - if (pdlg->wasCanceled() || g_system_progress_canceled.exchange(false)) - { - if (cancel_log) - { - cancel_log(index, serials_size); - } - return false; - } - - if (action(serial)) - { - const int done = index_ptr->load(); - pdlg->setLabelText(progressLabel.arg(done + 1).arg(serials_size)); - pdlg->SetValue(done + 1); - } - - (*index_ptr)++; - return true; - }; - - if (can_be_concurrent) - { - // Unused currently - - QList indices; - - for (int i = 0; i < serials_size; i++) - { - indices.append(i); - } - - QFutureWatcher* future_watcher = new QFutureWatcher(this); - - future_watcher->setFuture(QtConcurrent::map(std::move(indices), *iterate_over_serial)); - - connect(future_watcher, &QFutureWatcher::finished, this, [=, this]() - { - pdlg->setLabelText(progressLabel.arg(*index).arg(serials_size)); - pdlg->setCancelButtonText(tr("OK")); - QApplication::beep(); - - if (refresh_on_finish && index) - { - Refresh(true); - } - - future_watcher->deleteLater(); - }); - - return; - } - - const std::shared_ptr> periodic_func = std::make_shared>(); - - *periodic_func = [=, this]() - { - if (should_wait_cb && should_wait_cb()) - { - // Conditions are not met for execution - // Check again later - QTimer::singleShot(5, this, *periodic_func); - return; - } - - if ((*iterate_over_serial)(*index)) - { - QTimer::singleShot(1, this, *periodic_func); - return; - } - - pdlg->setLabelText(progressLabel.arg(*index).arg(serials_size)); - pdlg->setCancelButtonText(tr("OK")); - connect(pdlg, &progress_dialog::canceled, this, [pdlg]() - { - pdlg->deleteLater(); - }); - QApplication::beep(); - - if (refresh_on_finish && index) - { - Refresh(true); - } - }; - - // Invoked on the next event loop processing iteration - QTimer::singleShot(1, this, *periodic_func); -} - -void game_list_frame::BatchCreateCPUCaches(const std::vector& game_data) -{ - std::set serials; - - if (game_data.empty()) - { - serials.emplace("vsh.self"); - } - - for (const auto& game : (game_data.empty() ? m_game_data : game_data)) - { - serials.emplace(game->info.serial); - } - - const usz total = serials.size(); - - if (total == 0) - { - QMessageBox::information(this, tr("LLVM Cache Batch Creation"), tr("No titles found"), QMessageBox::Ok); - return; - } - - if (!m_gui_settings->GetBootConfirmation(this)) - { - return; - } - - const QString main_label = tr("Creating all LLVM caches"); - - progress_dialog* pdlg = new progress_dialog(tr("LLVM Cache Batch Creation"), main_label, tr("Cancel"), 0, ::narrow(total), false, this); - pdlg->setAutoClose(false); - pdlg->setAutoReset(false); - pdlg->open(); - - connect(pdlg, &progress_dialog::canceled, this, []() - { - if (!Emu.IsStopped()) - { - Emu.GracefulShutdown(false, true); - } - }); - - BatchActionBySerials(pdlg, serials, tr("%0\nProgress: %1/%2 caches compiled").arg(main_label), [&, game_data](const std::string& serial) - { - if (Emu.IsStopped(true)) - { - const auto it = std::find_if(m_game_data.begin(), m_game_data.end(), FN(x->info.serial == serial)); - - if (it != m_game_data.end()) - { - return CreateCPUCaches((*it)->info.path, serial); - } - } - - return false; - }, - [this](u32, u32) - { - game_list_log.notice("LLVM Cache Batch Creation was canceled"); - }, - false, false, []() - { - return !Emu.IsStopped(true); - }); -} - -void game_list_frame::BatchRemovePPUCaches() -{ - if (Emu.GetStatus(false) != system_state::stopped) - { - return; - } - - std::set serials; - serials.emplace("vsh.self"); - - for (const auto& game : m_game_data) - { - serials.emplace(game->info.serial); - } - - const u32 total = ::size32(serials); - - if (total == 0) - { - QMessageBox::information(this, tr("PPU Cache Batch Removal"), tr("No files found"), QMessageBox::Ok); - return; - } - - progress_dialog* pdlg = new progress_dialog(tr("PPU Cache Batch Removal"), tr("Removing all PPU caches"), tr("Cancel"), 0, total, false, this); - pdlg->setAutoClose(false); - pdlg->setAutoReset(false); - pdlg->open(); - - BatchActionBySerials(pdlg, serials, tr("%0/%1 caches cleared"), [this](const std::string& serial) - { - return Emu.IsStopped(true) && RemovePPUCache(GetCacheDirBySerial(serial)); - }, - [this](u32, u32) - { - game_list_log.notice("PPU Cache Batch Removal was canceled"); - }, - false); -} - -void game_list_frame::BatchRemoveSPUCaches() -{ - if (Emu.GetStatus(false) != system_state::stopped) - { - return; - } - - std::set serials; - serials.emplace("vsh.self"); - - for (const auto& game : m_game_data) - { - serials.emplace(game->info.serial); - } - - const u32 total = ::size32(serials); - - if (total == 0) - { - QMessageBox::information(this, tr("SPU Cache Batch Removal"), tr("No files found"), QMessageBox::Ok); - return; - } - - progress_dialog* pdlg = new progress_dialog(tr("SPU Cache Batch Removal"), tr("Removing all SPU caches"), tr("Cancel"), 0, total, false, this); - pdlg->setAutoClose(false); - pdlg->setAutoReset(false); - pdlg->open(); - - BatchActionBySerials(pdlg, serials, tr("%0/%1 caches cleared"), [this](const std::string& serial) - { - return Emu.IsStopped(true) && RemoveSPUCache(GetCacheDirBySerial(serial)); - }, - [this](u32 removed, u32 total) - { - game_list_log.notice("SPU Cache Batch Removal was canceled. %d/%d folders cleared", removed, total); - }, - false); -} - -void game_list_frame::BatchRemoveCustomConfigurations() -{ - std::set serials; - for (const auto& game : m_game_data) - { - if (game->has_custom_config && !serials.count(game->info.serial)) - { - serials.emplace(game->info.serial); - } - } - - const u32 total = ::size32(serials); - - if (total == 0) - { - QMessageBox::information(this, tr("Custom Configuration Batch Removal"), tr("No files found"), QMessageBox::Ok); - return; - } - - progress_dialog* pdlg = new progress_dialog(tr("Custom Configuration Batch Removal"), tr("Removing all custom configurations"), tr("Cancel"), 0, total, false, this); - pdlg->setAutoClose(false); - pdlg->setAutoReset(false); - pdlg->open(); - - BatchActionBySerials(pdlg, serials, tr("%0/%1 custom configurations cleared"), [this](const std::string& serial) - { - return Emu.IsStopped(true) && RemoveCustomConfiguration(serial); - }, - [this](u32 removed, u32 total) - { - game_list_log.notice("Custom Configuration Batch Removal was canceled. %d/%d custom configurations cleared", removed, total); - }, - true); -} - -void game_list_frame::BatchRemoveCustomPadConfigurations() -{ - std::set serials; - for (const auto& game : m_game_data) - { - if (game->has_custom_pad_config && !serials.count(game->info.serial)) - { - serials.emplace(game->info.serial); - } - } - const u32 total = ::size32(serials); - - if (total == 0) - { - QMessageBox::information(this, tr("Custom Pad Configuration Batch Removal"), tr("No files found"), QMessageBox::Ok); - return; - } - - progress_dialog* pdlg = new progress_dialog(tr("Custom Pad Configuration Batch Removal"), tr("Removing all custom pad configurations"), tr("Cancel"), 0, total, false, this); - pdlg->setAutoClose(false); - pdlg->setAutoReset(false); - pdlg->open(); - - BatchActionBySerials(pdlg, serials, tr("%0/%1 custom pad configurations cleared"), [this](const std::string& serial) - { - return Emu.IsStopped(true) && RemoveCustomPadConfiguration(serial); - }, - [this](u32 removed, u32 total) - { - game_list_log.notice("Custom Pad Configuration Batch Removal was canceled. %d/%d custom pad configurations cleared", removed, total); - }, - true); -} - -void game_list_frame::BatchRemoveShaderCaches() -{ - if (Emu.GetStatus(false) != system_state::stopped) - { - return; - } - - std::set serials; - serials.emplace("vsh.self"); - - for (const auto& game : m_game_data) - { - serials.emplace(game->info.serial); - } - - const u32 total = ::size32(serials); - - if (total == 0) - { - QMessageBox::information(this, tr("Shader Cache Batch Removal"), tr("No files found"), QMessageBox::Ok); - return; - } - - progress_dialog* pdlg = new progress_dialog(tr("Shader Cache Batch Removal"), tr("Removing all shader caches"), tr("Cancel"), 0, total, false, this); - pdlg->setAutoClose(false); - pdlg->setAutoReset(false); - pdlg->open(); - - BatchActionBySerials(pdlg, serials, tr("%0/%1 shader caches cleared"), [this](const std::string& serial) - { - return Emu.IsStopped(true) && RemoveShadersCache(GetCacheDirBySerial(serial)); - }, - [this](u32 removed, u32 total) - { - game_list_log.notice("Shader Cache Batch Removal was canceled. %d/%d cleared", removed, total); - }, - false); -} - -void game_list_frame::ShowCustomConfigIcon(const game_info& game) -{ - if (!game) - { - return; - } - - const std::string serial = game->info.serial; - const bool has_custom_config = game->has_custom_config; - const bool has_custom_pad_config = game->has_custom_pad_config; - - for (const auto& other_game : m_game_data) - { - if (other_game->info.serial == serial) - { - other_game->has_custom_config = has_custom_config; - other_game->has_custom_pad_config = has_custom_pad_config; - } - } - - m_game_list->set_custom_config_icon(game); - - RepaintIcons(); -} - -void game_list_frame::ResizeIcons(const int& slider_pos) -{ - m_icon_size_index = slider_pos; - m_icon_size = gui_settings::SizeFromSlider(slider_pos); - - RepaintIcons(); -} - -void game_list_frame::RepaintIcons(const bool& from_settings) -{ - gui::utils::stop_future_watcher(m_parsing_watcher, false); - gui::utils::stop_future_watcher(m_refresh_watcher, false); - WaitAndAbortRepaintThreads(); - - if (from_settings) - { - if (m_gui_settings->GetValue(gui::m_enableUIColors).toBool()) - { - m_icon_color = m_gui_settings->GetValue(gui::gl_iconColor).value(); - } - else - { - m_icon_color = gui::utils::get_label_color("gamelist_icon_background_color", Qt::transparent, Qt::transparent); - } - } - - if (m_is_list_layout) - { - m_game_list->repaint_icons(m_game_data, m_icon_color, m_icon_size, devicePixelRatioF()); - } - else - { - m_game_grid->set_draw_compat_status_to_grid(m_draw_compat_status_to_grid); - m_game_grid->repaint_icons(m_game_data, m_icon_color, m_icon_size, devicePixelRatioF()); - } -} - -void game_list_frame::SetShowHidden(bool show) -{ - m_show_hidden = show; -} - -void game_list_frame::SetListMode(const bool& is_list) -{ - m_old_layout_is_list = m_is_list_layout; - m_is_list_layout = is_list; - - m_gui_settings->SetValue(gui::gl_listMode, is_list); - - Refresh(); - - if (m_is_list_layout) - { - m_central_widget->setCurrentWidget(m_game_list); - } - else - { - m_central_widget->setCurrentWidget(m_game_grid); - } -} - -void game_list_frame::SetSearchText(const QString& text) -{ - m_search_text = text; - Refresh(); -} - -void game_list_frame::FocusAndSelectFirstEntryIfNoneIs() -{ - if (m_is_list_layout) - { - if (m_game_list) - { - m_game_list->FocusAndSelectFirstEntryIfNoneIs(); - } - } - else - { - if (m_game_grid) - { - m_game_grid->FocusAndSelectFirstEntryIfNoneIs(); - } - } -} - -void game_list_frame::closeEvent(QCloseEvent* event) -{ - SaveSettings(); - - QDockWidget::closeEvent(event); - Q_EMIT GameListFrameClosed(); -} - -bool game_list_frame::eventFilter(QObject* object, QEvent* event) -{ - // Zoom gamelist/gamegrid - if (event->type() == QEvent::Wheel && (object == m_game_list->verticalScrollBar() || object == m_game_grid->scroll_area()->verticalScrollBar())) - { - QWheelEvent* wheel_event = static_cast(event); - - if (wheel_event->modifiers() & Qt::ControlModifier) - { - const QPoint num_steps = wheel_event->angleDelta() / 8 / 15; // http://doc.qt.io/qt-5/qwheelevent.html#pixelDelta - const int value = num_steps.y(); - Q_EMIT RequestIconSizeChange(value); - return true; - } - } - else if (event->type() == QEvent::KeyPress && (object == m_game_list || object == m_game_grid)) - { - QKeyEvent* key_event = static_cast(event); - - if (key_event->modifiers() & Qt::ControlModifier) - { - if (key_event->key() == Qt::Key_Plus) - { - Q_EMIT RequestIconSizeChange(1); - return true; - } - if (key_event->key() == Qt::Key_Minus) - { - Q_EMIT RequestIconSizeChange(-1); - return true; - } - } - else if (!key_event->isAutoRepeat()) - { - if (key_event->key() == Qt::Key_Enter || key_event->key() == Qt::Key_Return) - { - game_info gameinfo{}; - - if (object == m_game_list) - { - QTableWidgetItem* item = m_game_list->item(m_game_list->currentRow(), static_cast(gui::game_list_columns::icon)); - - if (!item || !item->isSelected()) - return false; - - gameinfo = GetGameInfoFromItem(item); - } - else if (game_list_grid_item* item = static_cast(m_game_grid->selected_item())) - { - gameinfo = item->game(); - } - - if (!gameinfo) - return false; - - sys_log.notice("Booting from gamelist by pressing %s...", key_event->key() == Qt::Key_Enter ? "Enter" : "Return"); - Q_EMIT RequestBoot(gameinfo); - - return true; - } - } - } - - return QDockWidget::eventFilter(object, event); -} - -/** - * Returns false if the game should be hidden because it doesn't match search term in toolbar. - */ -bool game_list_frame::SearchMatchesApp(const QString& name, const QString& serial, bool fallback) const -{ - if (!m_search_text.isEmpty()) - { - QString search_text = m_search_text.toLower(); - QString title_name; - - if (const auto it = m_titles.find(serial); it != m_titles.cend()) - { - title_name = it->second.toLower(); - } - else - { - title_name = name.toLower(); - } - - // Ignore trademarks when no search results have been yielded by unmodified search - static const QRegularExpression s_ignored_on_fallback(reinterpret_cast(u8"[:\\-®©™]+")); - - if (fallback) - { - search_text = search_text.simplified(); - title_name = title_name.simplified(); - - QString title_name_replaced_trademarks_with_spaces = title_name; - QString title_name_simplified = title_name; - - search_text.remove(s_ignored_on_fallback); - title_name.remove(s_ignored_on_fallback); - title_name_replaced_trademarks_with_spaces.replace(s_ignored_on_fallback, " "); - - // Before simplify to allow spaces in the beginning and end where ignored characters may have been - if (title_name_replaced_trademarks_with_spaces.contains(search_text)) - { - return true; - } - - title_name_replaced_trademarks_with_spaces = title_name_replaced_trademarks_with_spaces.simplified(); - - if (title_name_replaced_trademarks_with_spaces.contains(search_text)) - { - return true; - } - - // Initials-only search - if (search_text.size() >= 2 && search_text.count(QRegularExpression(QStringLiteral("[a-z0-9]"))) >= 2 && !search_text.contains(QRegularExpression(QStringLiteral("[^a-z0-9 ]")))) - { - QString initials = QStringLiteral("\\b"); - - for (auto it = search_text.begin(); it != search_text.end(); it++) - { - if (it->isSpace()) - { - continue; - } - - initials += *it; - initials += QStringLiteral("\\w*\\b "); - } - - initials += QChar('?'); - - if (title_name_replaced_trademarks_with_spaces.contains(QRegularExpression(initials))) - { - return true; - } - } - } - - return title_name.contains(search_text) || serial.toLower().contains(search_text); - } - return true; -} - -std::string game_list_frame::CurrentSelectionPath() -{ - std::string selection; - - game_info game{}; - - if (m_old_layout_is_list) - { - if (!m_game_list->selectedItems().isEmpty()) - { - if (QTableWidgetItem* item = m_game_list->item(m_game_list->currentRow(), 0)) - { - if (const QVariant var = item->data(gui::game_role); var.canConvert()) - { - game = var.value(); - } - } - } - } - else if (m_game_grid) - { - if (game_list_grid_item* item = static_cast(m_game_grid->selected_item())) - { - game = item->game(); - } - } - - if (game) - { - selection = game->info.path + game->info.icon_path; - } - - m_old_layout_is_list = m_is_list_layout; - - return selection; -} - -game_info game_list_frame::GetGameInfoByMode(const QTableWidgetItem* item) const -{ - if (!item) - { - return nullptr; - } - - if (m_is_list_layout) - { - return GetGameInfoFromItem(m_game_list->item(item->row(), static_cast(gui::game_list_columns::icon))); - } - - return GetGameInfoFromItem(item); -} - -game_info game_list_frame::GetGameInfoFromItem(const QTableWidgetItem* item) -{ - if (!item) - { - return nullptr; - } - - const QVariant var = item->data(gui::game_role); - if (!var.canConvert()) - { - return nullptr; - } - - return var.value(); -} - -void game_list_frame::SetShowCompatibilityInGrid(bool show) -{ - m_draw_compat_status_to_grid = show; - RepaintIcons(); - m_gui_settings->SetValue(gui::gl_draw_compat, show); -} - -void game_list_frame::SetPreferGameDataIcons(bool enabled) -{ - if (m_prefer_game_data_icons != enabled) - { - m_prefer_game_data_icons = enabled; - m_gui_settings->SetValue(gui::gl_pref_gd_icon, enabled); - Refresh(true); - } -} - -void game_list_frame::SetShowCustomIcons(bool show) -{ - if (m_show_custom_icons != show) - { - m_show_custom_icons = show; - m_gui_settings->SetValue(gui::gl_custom_icon, show); - Refresh(true); - } -} - -void game_list_frame::SetPlayHoverGifs(bool play) -{ - if (m_play_hover_movies != play) - { - m_play_hover_movies = play; - m_gui_settings->SetValue(gui::gl_hover_gifs, play); - Refresh(true); - } -} - -const std::vector& game_list_frame::GetGameInfo() const -{ - return m_game_data; -} - -void game_list_frame::WaitAndAbortRepaintThreads() -{ - for (const game_info& game : m_game_data) - { - if (game && game->item) - { - game->item->wait_for_icon_loading(true); - } - } -} - -void game_list_frame::WaitAndAbortSizeCalcThreads() -{ - for (const game_info& game : m_game_data) - { - if (game && game->item) - { - game->item->wait_for_size_on_disk_loading(true); - } - } -} diff --git a/rpcs3qt-legacy/game_list_frame.h b/rpcs3qt-legacy/game_list_frame.h deleted file mode 100644 index 5326a0989..000000000 --- a/rpcs3qt-legacy/game_list_frame.h +++ /dev/null @@ -1,225 +0,0 @@ -#pragma once - -#include "game_list.h" -#include "custom_dock_widget.h" -#include "shortcut_utils.h" -#include "util/lockless.h" -#include "util/mutex.h" -#include "util/auto_typemap.hpp" -#include "Emu/config_mode.h" - -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -class game_list_table; -class game_list_grid; -class gui_settings; -class emu_settings; -class persistent_settings; -class progress_dialog; - -class game_list_frame : public custom_dock_widget -{ - Q_OBJECT - -public: - explicit game_list_frame(std::shared_ptr gui_settings, std::shared_ptr emu_settings, std::shared_ptr persistent_settings, QWidget* parent = nullptr); - ~game_list_frame(); - - /** Refresh the gamelist with/without loading game data from files. Public so that main frame can refresh after vfs or install */ - void Refresh(const bool from_drive = false, const std::vector& serials_to_remove_from_yml = {}, const bool scroll_after = true); - - /** Adds/removes categories that should be shown on gamelist. Public so that main frame menu actions can apply them */ - void ToggleCategoryFilter(const QStringList& categories, bool show); - - /** Loads from settings. Public so that main frame can easily reset these settings if needed. */ - void LoadSettings(); - - /** Saves settings. Public so that main frame can save this when a caching of column widths is needed for settings backup */ - void SaveSettings(); - - /** Resize Gamelist Icons to size given by slider position */ - void ResizeIcons(const int& slider_pos); - - /** Repaint Gamelist Icons with new background color */ - void RepaintIcons(const bool& from_settings = false); - - void SetShowHidden(bool show); - - game_compatibility* GetGameCompatibility() const - { - return m_game_compat; - } - - const std::vector& GetGameInfo() const; - - void CreateShortcuts(const std::vector& games, const std::set& locations); - - bool IsEntryVisible(const game_info& game, bool search_fallback = false) const; - -public Q_SLOTS: - void BatchCreateCPUCaches(const std::vector& game_data = {}); - void BatchRemovePPUCaches(); - void BatchRemoveSPUCaches(); - void BatchRemoveCustomConfigurations(); - void BatchRemoveCustomPadConfigurations(); - void BatchRemoveShaderCaches(); - void SetListMode(const bool& is_list); - void SetSearchText(const QString& text); - void SetShowCompatibilityInGrid(bool show); - void SetPreferGameDataIcons(bool enabled); - void SetShowCustomIcons(bool show); - void SetPlayHoverGifs(bool play); - void FocusAndSelectFirstEntryIfNoneIs(); - -private Q_SLOTS: - void OnParsingFinished(); - void OnRefreshFinished(); - void OnCompatFinished(); - void OnColClicked(int col); - void ShowContextMenu(const QPoint& pos); - void doubleClickedSlot(QTableWidgetItem* item); - void doubleClickedSlot(const game_info& game); - void ItemSelectionChangedSlot(); -Q_SIGNALS: - void GameListFrameClosed(); - void NotifyGameSelection(const game_info& game); - void RequestBoot(const game_info& game, cfg_mode config_mode = cfg_mode::custom, const std::string& config_path = "", const std::string& savestate = ""); - void RequestIconSizeChange(const int& val); - void NotifyEmuSettingsChange(); - void FocusToSearchBar(); - void Refreshed(); - -public: - template - struct GameIdsTable - { - // List of game paths an operation has been done on for the use of the slot function - std::set m_done_paths; - }; - - // Enqueue slot for refreshed signal - // Allowing for an individual container for each distinct use case (currently disabled and contains only one such entry) - template - void AddRefreshedSlot(Func&& func) - { - // NOTE: Remove assert when the need for individual containers arises - static_assert(std::is_void_v); - - connect(this, &game_list_frame::Refreshed, this, [this, func = std::move(func)]() mutable - { - func(m_refresh_funcs_manage_type->get>().m_done_paths); - }, - Qt::SingleShotConnection); - } - -protected: - /** Override inherited method from Qt to allow signalling when close happened.*/ - void closeEvent(QCloseEvent* event) override; - bool eventFilter(QObject* object, QEvent* event) override; - -private: - void push_path(const std::string& path, std::vector& legit_paths); - - void ShowCustomConfigIcon(const game_info& game); - bool SearchMatchesApp(const QString& name, const QString& serial, bool fallback = false) const; - - bool RemoveCustomConfiguration(const std::string& title_id, const game_info& game = nullptr, bool is_interactive = false); - bool RemoveCustomPadConfiguration(const std::string& title_id, const game_info& game = nullptr, bool is_interactive = false); - bool RemoveShadersCache(const std::string& base_dir, bool is_interactive = false); - bool RemovePPUCache(const std::string& base_dir, bool is_interactive = false); - bool RemoveSPUCache(const std::string& base_dir, bool is_interactive = false); - void RemoveHDD1Cache(const std::string& base_dir, const std::string& title_id, bool is_interactive = false); - static bool CreateCPUCaches(const std::string& path, const std::string& serial = {}); - static bool CreateCPUCaches(const game_info& game); - - static bool RemoveContentPath(const std::string& path, const std::string& desc); - static u32 RemoveContentPathList(const std::vector& path_list, const std::string& desc); - static bool RemoveContentBySerial(const std::string& base_dir, const std::string& serial, const std::string& desc); - static std::vector GetDirListBySerial(const std::string& base_dir, const std::string& serial); - void BatchActionBySerials(progress_dialog* pdlg, const std::set& serials, QString progressLabel, std::function action, std::function cancel_log, bool refresh_on_finish, bool can_be_concurrent = false, std::function should_wait_cb = {}); - static std::string GetCacheDirBySerial(const std::string& serial); - static std::string GetDataDirBySerial(const std::string& serial); - std::string CurrentSelectionPath(); - - game_info GetGameInfoByMode(const QTableWidgetItem* item) const; - static game_info GetGameInfoFromItem(const QTableWidgetItem* item); - - void WaitAndAbortRepaintThreads(); - void WaitAndAbortSizeCalcThreads(); - - // Which widget we are displaying depends on if we are in grid or list mode. - QMainWindow* m_game_dock = nullptr; - QStackedWidget* m_central_widget = nullptr; - - // Game Grid - game_list_grid* m_game_grid = nullptr; - - // Game List - game_list_table* m_game_list = nullptr; - game_compatibility* m_game_compat = nullptr; - progress_dialog* m_progress_dialog = nullptr; - QList m_columnActs; - Qt::SortOrder m_col_sort_order{}; - int m_sort_column{}; - bool m_initial_refresh_done = false; - std::map m_notes; - std::map m_titles; - - // Categories - QStringList m_category_filters; - QStringList m_grid_category_filters; - - // List Mode - bool m_is_list_layout = true; - bool m_old_layout_is_list = true; - - // Data - std::shared_ptr m_gui_settings; - std::shared_ptr m_emu_settings; - std::shared_ptr m_persistent_settings; - std::vector m_game_data; - struct path_entry - { - std::string path; - bool is_disc{}; - bool is_from_yml{}; - }; - std::vector m_path_entries; - shared_mutex m_path_mutex; - std::set m_path_list; - QSet m_serials; - QMutex m_games_mutex; - lf_queue m_games; - const std::array m_parsing_threads{0}; - QFutureWatcher m_parsing_watcher; - QFutureWatcher m_refresh_watcher; - QSet m_hidden_list; - bool m_show_hidden{false}; - - // Search - QString m_search_text; - - // Icon Size - int m_icon_size_index = 0; - - // Icons - QColor m_icon_color; - QSize m_icon_size; - qreal m_margin_factor; - qreal m_text_factor; - bool m_draw_compat_status_to_grid = false; - bool m_prefer_game_data_icons = false; - bool m_show_custom_icons = true; - bool m_play_hover_movies = true; - std::optional> m_refresh_funcs_manage_type{std::in_place}; -}; diff --git a/rpcs3qt-legacy/game_list_grid.cpp b/rpcs3qt-legacy/game_list_grid.cpp deleted file mode 100644 index c43855989..000000000 --- a/rpcs3qt-legacy/game_list_grid.cpp +++ /dev/null @@ -1,217 +0,0 @@ -#include "stdafx.h" -#include "game_list_grid.h" -#include "game_list_grid_item.h" -#include "gui_settings.h" -#include "qt_utils.h" -#include "util/File.h" - -#include -#include - -game_list_grid::game_list_grid() - : flow_widget(nullptr), game_list_base() -{ - setObjectName("game_list_grid"); - setContextMenuPolicy(Qt::CustomContextMenu); - - m_icon_ready_callback = [this](const movie_item_base* item) - { - Q_EMIT IconReady(item); - }; - - connect(this, &game_list_grid::IconReady, this, [this](const movie_item_base* item) - { - if (item) - item->image_change_callback(); - }, - Qt::QueuedConnection); // The default 'AutoConnection' doesn't seem to work in this specific case... - - connect(this, &flow_widget::ItemSelectionChanged, this, [this](int index) - { - if (game_list_grid_item* item = static_cast(::at32(items(), index))) - { - Q_EMIT ItemSelectionChanged(item->game()); - } - }); -} - -void game_list_grid::clear_list() -{ - clear(); -} - -void game_list_grid::populate( - const std::vector& game_data, - const std::map& notes_map, - const std::map& title_map, - const std::string& selected_item_id, - bool play_hover_movies) -{ - clear_list(); - - game_list_grid_item* selected_item = nullptr; - - blockSignals(true); - - const auto get_title = [&title_map](const QString& serial, const std::string& name) -> QString - { - if (const auto it = title_map.find(serial); it != title_map.cend()) - { - return it->second.simplified(); - } - - return QString::fromStdString(name).simplified(); - }; - - for (const auto& game : game_data) - { - const QString serial = QString::fromStdString(game->info.serial); - const QString title = get_title(serial, game->info.name); - - game_list_grid_item* item = new game_list_grid_item(this, game, title); - item->installEventFilter(this); - item->setFocusPolicy(Qt::StrongFocus); - - game->item = item; - - if (const auto it = notes_map.find(serial); it != notes_map.cend() && !it->second.isEmpty()) - { - item->setToolTip(tr("%0 [%1]\n\nNotes:\n%2").arg(title).arg(serial).arg(it->second)); - } - else - { - item->setToolTip(tr("%0 [%1]").arg(title).arg(serial)); - } - - item->set_image_change_callback([this, item, game](const QVideoFrame& frame) - { - if (!item || !game) - { - return; - } - - if (const QPixmap pixmap = item->get_movie_image(frame); item->get_active() && !pixmap.isNull()) - { - item->set_icon(gui::utils::get_centered_pixmap(pixmap, m_icon_size, 0, 0, 1.0, Qt::FastTransformation)); - } - else - { - std::lock_guard lock(item->pixmap_mutex); - - item->set_icon(game->pxmap); - - if (!game->has_hover_gif && !game->has_hover_pam) - { - game->pxmap = {}; - } - - item->stop_movie(); - } - }); - - if (play_hover_movies && (game->has_hover_gif || game->has_hover_pam)) - { - item->set_video_path(game->info.movie_path); - } - - if (selected_item_id == game->info.path + game->info.icon_path) - { - selected_item = item; - } - - add_widget(item); - } - - blockSignals(false); - - // Update layout before setting focus on the selected item - show(); - - QApplication::processEvents(); - - select_item(selected_item); -} - -void game_list_grid::repaint_icons(std::vector& game_data, const QColor& icon_color, const QSize& icon_size, qreal device_pixel_ratio) -{ - m_icon_size = icon_size; - m_icon_color = icon_color; - - QPixmap placeholder(icon_size * device_pixel_ratio); - placeholder.setDevicePixelRatio(device_pixel_ratio); - placeholder.fill(Qt::transparent); - - const bool show_title = m_icon_size.width() > (gui::gl_icon_size_medium.width() + gui::gl_icon_size_small.width()) / 2; - - for (game_info& game : game_data) - { - if (game_list_grid_item* item = static_cast(game->item)) - { - if (item->icon_loading()) - { - // We already have an icon. Simply set the icon size to let the label scale itself in a quick and dirty fashion. - item->set_icon_size(m_icon_size); - } - else - { - // We don't have an icon. Set a placeholder to initialize the layout. - game->pxmap = placeholder; - item->image_change_callback(); - } - - item->set_icon_load_func([this, game, device_pixel_ratio, cancel = item->icon_loading_aborted()](int) - { - IconLoadFunction(game, device_pixel_ratio, cancel); - }); - - item->adjust_size(); - item->show_title(show_title); - item->got_visible = false; - } - } -} - -void game_list_grid::FocusAndSelectFirstEntryIfNoneIs() -{ - if (!items().empty()) - { - items().front()->setFocus(); - } -} - -bool game_list_grid::eventFilter(QObject* watched, QEvent* event) -{ - if (!event) - { - return false; - } - - if (event->type() == QEvent::MouseButtonDblClick && static_cast(event)->button() == Qt::LeftButton) - { - if (game_list_grid_item* item = static_cast(watched)) - { - Q_EMIT ItemDoubleClicked(item->game()); - return true; - } - } - - return false; -} - -void game_list_grid::keyPressEvent(QKeyEvent* event) -{ - if (!event) - { - return; - } - - const auto modifiers = event->modifiers(); - - if (modifiers == Qt::ControlModifier && event->key() == Qt::Key_F && !event->isAutoRepeat()) - { - Q_EMIT FocusToSearchBar(); - return; - } - - flow_widget::keyPressEvent(event); -} diff --git a/rpcs3qt-legacy/game_list_grid.h b/rpcs3qt-legacy/game_list_grid.h deleted file mode 100644 index aaa1e267d..000000000 --- a/rpcs3qt-legacy/game_list_grid.h +++ /dev/null @@ -1,37 +0,0 @@ -#pragma once - -#include "game_list_base.h" -#include "flow_widget.h" - -#include - -class game_list_grid : public flow_widget, public game_list_base -{ - Q_OBJECT - -public: - explicit game_list_grid(); - - void clear_list() override; - - void populate( - const std::vector& game_data, - const std::map& notes_map, - const std::map& title_map, - const std::string& selected_item_id, - bool play_hover_movies) override; - - void repaint_icons(std::vector& game_data, const QColor& icon_color, const QSize& icon_size, qreal device_pixel_ratio) override; - - bool eventFilter(QObject* watched, QEvent* event) override; - void keyPressEvent(QKeyEvent* event) override; - -public Q_SLOTS: - void FocusAndSelectFirstEntryIfNoneIs(); - -Q_SIGNALS: - void FocusToSearchBar(); - void ItemDoubleClicked(const game_info& game); - void ItemSelectionChanged(const game_info& game); - void IconReady(const movie_item_base* item); -}; diff --git a/rpcs3qt-legacy/game_list_grid_item.cpp b/rpcs3qt-legacy/game_list_grid_item.cpp deleted file mode 100644 index 928a5fa26..000000000 --- a/rpcs3qt-legacy/game_list_grid_item.cpp +++ /dev/null @@ -1,87 +0,0 @@ -#include "game_list_grid_item.h" - -#include -#include - -game_list_grid_item::game_list_grid_item(QWidget* parent, game_info game, const QString& title) - : flow_widget_item(parent), movie_item_base(), m_game(std::move(game)) -{ - setObjectName("game_list_grid_item"); - setAttribute(Qt::WA_Hover); // We need to enable the hover attribute to ensure that hover events are handled. - - cb_on_first_visibility = [this]() - { - if (!icon_loading()) - { - call_icon_load_func(0); - } - }; - - m_icon_label = new QLabel(this); - m_icon_label->setObjectName("game_list_grid_item_icon_label"); - m_icon_label->setAttribute(Qt::WA_TranslucentBackground); - m_icon_label->setScaledContents(true); - - m_title_label = new QLabel(title, this); - m_title_label->setObjectName("game_list_grid_item_title_label"); - m_title_label->setAlignment(Qt::AlignVCenter | Qt::AlignHCenter); - m_title_label->setWordWrap(true); - m_title_label->setVisible(false); - - QVBoxLayout* layout = new QVBoxLayout(this); - layout->addWidget(m_icon_label, 1); - layout->addWidget(m_title_label, 0); - - setLayout(layout); -} - -void game_list_grid_item::set_icon_size(const QSize& size) -{ - m_icon_size = size; -} - -void game_list_grid_item::set_icon(const QPixmap& pixmap) -{ - m_icon_size = pixmap.size() / devicePixelRatioF(); - m_icon_label->setPixmap(pixmap); -} - -void game_list_grid_item::adjust_size() -{ - m_icon_label->setMinimumSize(m_icon_size); - m_icon_label->setMaximumSize(m_icon_size); - m_title_label->setMaximumWidth(m_icon_size.width()); -} - -void game_list_grid_item::show_title(bool visible) -{ - if (m_title_label) - { - m_title_label->setVisible(visible); - } -} - -void game_list_grid_item::polish_style() -{ - flow_widget_item::polish_style(); - - m_title_label->style()->unpolish(m_title_label); - m_title_label->style()->polish(m_title_label); -} - -bool game_list_grid_item::event(QEvent* event) -{ - switch (event->type()) - { - case QEvent::HoverEnter: - set_active(true); - break; - case QEvent::HoverLeave: - set_active(false); - break; - default: - break; - } - - return flow_widget_item::event(event); -} diff --git a/rpcs3qt-legacy/game_list_grid_item.h b/rpcs3qt-legacy/game_list_grid_item.h deleted file mode 100644 index 773854823..000000000 --- a/rpcs3qt-legacy/game_list_grid_item.h +++ /dev/null @@ -1,36 +0,0 @@ -#pragma once - -#include "flow_widget_item.h" -#include "movie_item_base.h" -#include "game_list_base.h" - -#include - -class game_list_grid_item : public flow_widget_item, public movie_item_base -{ - Q_OBJECT - -public: - game_list_grid_item(QWidget* parent, game_info game, const QString& title); - - void set_icon_size(const QSize& size); - void set_icon(const QPixmap& pixmap); - void adjust_size(); - - const game_info& game() const - { - return m_game; - } - - void show_title(bool visible); - - void polish_style() override; - - bool event(QEvent* event) override; - -private: - QSize m_icon_size{}; - QLabel* m_icon_label{}; - QLabel* m_title_label{}; - game_info m_game{}; -}; diff --git a/rpcs3qt-legacy/game_list_table.cpp b/rpcs3qt-legacy/game_list_table.cpp deleted file mode 100644 index 3e639537a..000000000 --- a/rpcs3qt-legacy/game_list_table.cpp +++ /dev/null @@ -1,398 +0,0 @@ -#include "stdafx.h" -#include "game_list_table.h" -#include "game_list_delegate.h" -#include "game_list_frame.h" -#include "gui_settings.h" -#include "localized.h" -#include "custom_table_widget_item.h" -#include "persistent_settings.h" -#include "qt_utils.h" - -#include "Emu/vfs_config.h" -#include "util/StrUtil.h" - -#include -#include -#include - -game_list_table::game_list_table(game_list_frame* frame, std::shared_ptr persistent_settings) - : game_list(), m_game_list_frame(frame), m_persistent_settings(std::move(persistent_settings)) -{ - m_is_list_layout = true; - - setShowGrid(false); - setItemDelegate(new game_list_delegate(this)); - setEditTriggers(QAbstractItemView::NoEditTriggers); - setSelectionBehavior(QAbstractItemView::SelectRows); - setSelectionMode(QAbstractItemView::SingleSelection); - setVerticalScrollMode(QAbstractItemView::ScrollPerPixel); - setHorizontalScrollMode(QAbstractItemView::ScrollPerPixel); - verticalScrollBar()->setSingleStep(20); - horizontalScrollBar()->setSingleStep(20); - verticalHeader()->setSectionResizeMode(QHeaderView::Fixed); - verticalHeader()->setVisible(false); - horizontalHeader()->setHighlightSections(false); - horizontalHeader()->setSortIndicatorShown(true); - horizontalHeader()->setStretchLastSection(true); - horizontalHeader()->setDefaultSectionSize(150); - horizontalHeader()->setDefaultAlignment(Qt::AlignLeft); - setContextMenuPolicy(Qt::CustomContextMenu); - setAlternatingRowColors(true); - setColumnCount(static_cast(gui::game_list_columns::count)); - setMouseTracking(true); - - connect(this, &game_list_table::size_on_disk_ready, this, [this](const game_info& game) - { - if (!game || !game->item) - return; - if (QTableWidgetItem* size_item = item(static_cast(game->item)->row(), static_cast(gui::game_list_columns::dir_size))) - { - const u64& game_size = game->info.size_on_disk; - size_item->setText(game_size != umax ? gui::utils::format_byte_size(game_size) : tr("Unknown")); - size_item->setData(Qt::UserRole, QVariant::fromValue(game_size)); - } - }); - - connect(this, &game_list::IconReady, this, [this](const movie_item_base* item) - { - if (item) - item->image_change_callback(); - }); -} - -void game_list_table::restore_layout(const QByteArray& state) -{ - // Resize to fit and get the ideal icon column width - resize_columns_to_contents(); - const int icon_column_width = columnWidth(static_cast(gui::game_list_columns::icon)); - - // Restore header layout from last session - if (!horizontalHeader()->restoreState(state) && rowCount()) - { - // Nothing to do - } - - // Make sure no columns are squished - fix_narrow_columns(); - - // Make sure that the icon column is large enough for the actual items. - // This is important if the list appeared as empty when closing the software before. - horizontalHeader()->resizeSection(static_cast(gui::game_list_columns::icon), icon_column_width); - - // Save new header state - horizontalHeader()->restoreState(horizontalHeader()->saveState()); -} - -void game_list_table::resize_columns_to_contents(int spacing) -{ - verticalHeader()->resizeSections(QHeaderView::ResizeMode::ResizeToContents); - horizontalHeader()->resizeSections(QHeaderView::ResizeMode::ResizeToContents); - - // Make non-icon columns slighty bigger for better visuals - for (int i = 1; i < columnCount(); i++) - { - if (isColumnHidden(i)) - { - continue; - } - - const int size = horizontalHeader()->sectionSize(i) + spacing; - horizontalHeader()->resizeSection(i, size); - } -} - -void game_list_table::adjust_icon_column() -{ - // Fixate vertical header and row height - verticalHeader()->setMinimumSectionSize(m_icon_size.height()); - verticalHeader()->setMaximumSectionSize(m_icon_size.height()); - - // Resize the icon column - resizeColumnToContents(static_cast(gui::game_list_columns::icon)); - - // Shorten the last section to remove horizontal scrollbar if possible - resizeColumnToContents(static_cast(gui::game_list_columns::count) - 1); -} - -void game_list_table::sort(usz game_count, int sort_column, Qt::SortOrder col_sort_order) -{ - // Back-up old header sizes to handle unwanted column resize in case of zero search results - const int old_row_count = rowCount(); - const usz old_game_count = game_count; - - std::vector column_widths(columnCount()); - for (int i = 0; i < columnCount(); i++) - { - column_widths[i] = columnWidth(i); - } - - // Sorting resizes hidden columns, so unhide them as a workaround - std::vector columns_to_hide; - - for (int i = 0; i < columnCount(); i++) - { - if (isColumnHidden(i)) - { - setColumnHidden(i, false); - columns_to_hide.push_back(i); - } - } - - // Sort the list by column and sort order - sortByColumn(sort_column, col_sort_order); - - // Hide columns again - for (int col : columns_to_hide) - { - setColumnHidden(col, true); - } - - // Don't resize the columns if no game is shown to preserve the header settings - if (!rowCount()) - { - for (int i = 0; i < columnCount(); i++) - { - setColumnWidth(i, column_widths[i]); - } - - horizontalHeader()->setSectionResizeMode(static_cast(gui::game_list_columns::icon), QHeaderView::Fixed); - return; - } - - // Fixate vertical header and row height - verticalHeader()->setMinimumSectionSize(m_icon_size.height()); - verticalHeader()->setMaximumSectionSize(m_icon_size.height()); - resizeRowsToContents(); - - // Resize columns if the game list was empty before - if (!old_row_count && !old_game_count) - { - resize_columns_to_contents(); - } - else - { - resizeColumnToContents(static_cast(gui::game_list_columns::icon)); - } - - // Fixate icon column - horizontalHeader()->setSectionResizeMode(static_cast(gui::game_list_columns::icon), QHeaderView::Fixed); - - // Shorten the last section to remove horizontal scrollbar if possible - resizeColumnToContents(static_cast(gui::game_list_columns::count) - 1); -} - -void game_list_table::set_custom_config_icon(const game_info& game) -{ - if (!game) - { - return; - } - - const QString serial = QString::fromStdString(game->info.serial); - - for (int row = 0; row < rowCount(); ++row) - { - if (QTableWidgetItem* title_item = item(row, static_cast(gui::game_list_columns::name))) - { - if (const QTableWidgetItem* serial_item = item(row, static_cast(gui::game_list_columns::serial)); serial_item && serial_item->text() == serial) - { - title_item->setIcon(game_list_base::GetCustomConfigIcon(game)); - } - } - } -} - -void game_list_table::populate( - const std::vector& game_data, - const std::map& notes_map, - const std::map& title_map, - const std::string& selected_item_id, - bool play_hover_movies) -{ - clear_list(); - - setRowCount(::narrow(game_data.size())); - - // Default locale. Uses current Qt application language. - const QLocale locale{}; - const Localized localized; - - const std::string dev_flash = g_cfg_vfs.get_dev_flash(); - - int row = 0; - int index = -1; - int selected_row = -1; - - const auto get_title = [&title_map](const QString& serial, const std::string& name) -> QString - { - if (const auto it = title_map.find(serial); it != title_map.cend()) - { - return it->second; - } - - return QString::fromStdString(name); - }; - - for (const auto& game : game_data) - { - index++; - - const QString serial = QString::fromStdString(game->info.serial); - const QString title = get_title(serial, game->info.name); - - // Icon - custom_table_widget_item* icon_item = new custom_table_widget_item; - game->item = icon_item; - - icon_item->set_image_change_callback([this, icon_item, game](const QVideoFrame& frame) - { - if (!icon_item || !game) - { - return; - } - - if (const QPixmap pixmap = icon_item->get_movie_image(frame); icon_item->get_active() && !pixmap.isNull()) - { - icon_item->setData(Qt::DecorationRole, pixmap.scaled(m_icon_size, Qt::KeepAspectRatio)); - } - else - { - std::lock_guard lock(icon_item->pixmap_mutex); - - icon_item->setData(Qt::DecorationRole, game->pxmap); - - if (!game->has_hover_gif && !game->has_hover_pam) - { - game->pxmap = {}; - } - - icon_item->stop_movie(); - } - }); - - icon_item->set_size_calc_func([this, game, cancel = icon_item->size_on_disk_loading_aborted(), dev_flash]() - { - if (game && game->info.size_on_disk == umax && (!cancel || !cancel->load())) - { - if (game->info.path.starts_with(dev_flash)) - { - // Do not report size of apps inside /dev_flash (it does not make sense to do so) - game->info.size_on_disk = 0; - } - else - { - game->info.size_on_disk = fs::get_dir_size(game->info.path, 1, cancel.get()); - } - - if (!cancel || !cancel->load()) - { - Q_EMIT size_on_disk_ready(game); - return; - } - } - }); - - if (play_hover_movies && (game->has_hover_gif || game->has_hover_pam)) - { - icon_item->set_video_path(game->info.movie_path); - } - - icon_item->setData(Qt::UserRole, index, true); - icon_item->setData(gui::custom_roles::game_role, QVariant::fromValue(game)); - - // Title - custom_table_widget_item* title_item = new custom_table_widget_item(title); - title_item->setIcon(game_list_base::GetCustomConfigIcon(game)); - - // Serial - custom_table_widget_item* serial_item = new custom_table_widget_item(game->info.serial); - - if (const auto it = notes_map.find(serial); it != notes_map.cend() && !it->second.isEmpty()) - { - const QString tool_tip = tr("%0 [%1]\n\nNotes:\n%2").arg(title).arg(serial).arg(it->second); - title_item->setToolTip(tool_tip); - serial_item->setToolTip(tool_tip); - } - - // Move Support (http://www.psdevwiki.com/ps3/PARAM.SFO#ATTRIBUTE) - const bool supports_move = game->info.attr & 0x800000; - - // Compatibility - custom_table_widget_item* compat_item = new custom_table_widget_item; - compat_item->setText(game->compat.text % (game->compat.date.isEmpty() ? QStringLiteral("") : " (" % game->compat.date % ")")); - compat_item->setData(Qt::UserRole, game->compat.index, true); - compat_item->setToolTip(game->compat.tooltip); - if (!game->compat.color.isEmpty()) - { - compat_item->setData(Qt::DecorationRole, gui::utils::circle_pixmap(game->compat.color, devicePixelRatioF() * 2)); - } - - // Version - QString app_version = QString::fromStdString(game->GetGameVersion()); - - if (game->info.bootable && !game->compat.latest_version.isEmpty()) - { - f64 top_ver = 0.0, app_ver = 0.0; - const bool unknown = app_version == localized.category.unknown; - const bool ok_app = !unknown && try_to_float(&app_ver, app_version.toStdString(), ::std::numeric_limits::min(), ::std::numeric_limits::max()); - const bool ok_top = !unknown && try_to_float(&top_ver, game->compat.latest_version.toStdString(), ::std::numeric_limits::min(), ::std::numeric_limits::max()); - - // If the app is bootable and the compat database contains info about the latest patch version: - // add a hint for available software updates if the app version is unknown or lower than the latest version. - if (unknown || (ok_top && ok_app && top_ver > app_ver)) - { - app_version = tr("%0 (Update available: %1)").arg(app_version, game->compat.latest_version); - } - } - - // Playtimes - const quint64 elapsed_ms = m_persistent_settings->GetPlaytime(serial); - - // Last played (support outdated values) - QDateTime last_played; - const QString last_played_str = m_persistent_settings->GetLastPlayed(serial); - - if (!last_played_str.isEmpty()) - { - last_played = QDateTime::fromString(last_played_str, gui::persistent::last_played_date_format); - - if (!last_played.isValid()) - { - last_played = QDateTime::fromString(last_played_str, gui::persistent::last_played_date_format_old); - } - } - - const u64 game_size = game->info.size_on_disk; - - setItem(row, static_cast(gui::game_list_columns::icon), icon_item); - setItem(row, static_cast(gui::game_list_columns::name), title_item); - setItem(row, static_cast(gui::game_list_columns::serial), serial_item); - setItem(row, static_cast(gui::game_list_columns::firmware), new custom_table_widget_item(game->info.fw)); - setItem(row, static_cast(gui::game_list_columns::version), new custom_table_widget_item(app_version)); - setItem(row, static_cast(gui::game_list_columns::category), new custom_table_widget_item(game->localized_category)); - setItem(row, static_cast(gui::game_list_columns::path), new custom_table_widget_item(game->info.path)); - setItem(row, static_cast(gui::game_list_columns::move), new custom_table_widget_item((supports_move ? tr("Supported") : tr("Not Supported")).toStdString(), Qt::UserRole, !supports_move)); - setItem(row, static_cast(gui::game_list_columns::resolution), new custom_table_widget_item(Localized::GetStringFromU32(game->info.resolution, localized.resolution.mode, true))); - setItem(row, static_cast(gui::game_list_columns::sound), new custom_table_widget_item(Localized::GetStringFromU32(game->info.sound_format, localized.sound.format, true))); - setItem(row, static_cast(gui::game_list_columns::parental), new custom_table_widget_item(Localized::GetStringFromU32(game->info.parental_lvl, localized.parental.level), Qt::UserRole, game->info.parental_lvl)); - setItem(row, static_cast(gui::game_list_columns::last_play), new custom_table_widget_item(locale.toString(last_played, last_played >= QDateTime::currentDateTime().addDays(-7) ? gui::persistent::last_played_date_with_time_of_day_format : gui::persistent::last_played_date_format_new), Qt::UserRole, last_played)); - setItem(row, static_cast(gui::game_list_columns::playtime), new custom_table_widget_item(elapsed_ms == 0 ? tr("Never played") : localized.GetVerboseTimeByMs(elapsed_ms), Qt::UserRole, elapsed_ms)); - setItem(row, static_cast(gui::game_list_columns::compat), compat_item); - setItem(row, static_cast(gui::game_list_columns::dir_size), new custom_table_widget_item(game_size != umax ? gui::utils::format_byte_size(game_size) : tr("Unknown"), Qt::UserRole, QVariant::fromValue(game_size))); - - if (selected_item_id == game->info.path + game->info.icon_path) - { - selected_row = row; - } - - row++; - } - - selectRow(selected_row); -} - -void game_list_table::repaint_icons(std::vector& game_data, const QColor& icon_color, const QSize& icon_size, qreal device_pixel_ratio) -{ - game_list_base::repaint_icons(game_data, icon_color, icon_size, device_pixel_ratio); - adjust_icon_column(); -} diff --git a/rpcs3qt-legacy/game_list_table.h b/rpcs3qt-legacy/game_list_table.h deleted file mode 100644 index e3d693034..000000000 --- a/rpcs3qt-legacy/game_list_table.h +++ /dev/null @@ -1,42 +0,0 @@ -#pragma once - -#include "game_list.h" - -class persistent_settings; -class game_list_frame; - -class game_list_table : public game_list -{ - Q_OBJECT - -public: - game_list_table(game_list_frame* frame, std::shared_ptr persistent_settings); - - /** Restores the initial layout of the table */ - void restore_layout(const QByteArray& state); - - /** Resizes the columns to their contents and adds a small spacing */ - void resize_columns_to_contents(int spacing = 20); - - void adjust_icon_column(); - - void sort(usz game_count, int sort_column, Qt::SortOrder col_sort_order); - - void set_custom_config_icon(const game_info& game); - - void populate( - const std::vector& game_data, - const std::map& notes_map, - const std::map& title_map, - const std::string& selected_item_id, - bool play_hover_movies) override; - - void repaint_icons(std::vector& game_data, const QColor& icon_color, const QSize& icon_size, qreal device_pixel_ratio) override; - -Q_SIGNALS: - void size_on_disk_ready(const game_info& game); - -private: - game_list_frame* m_game_list_frame{}; - std::shared_ptr m_persistent_settings; -}; diff --git a/rpcs3qt-legacy/gl_gs_frame.cpp b/rpcs3qt-legacy/gl_gs_frame.cpp deleted file mode 100644 index ee6d901e4..000000000 --- a/rpcs3qt-legacy/gl_gs_frame.cpp +++ /dev/null @@ -1,149 +0,0 @@ -#include "gl_gs_frame.h" - -#include "Emu/System.h" -#include "Emu/system_config.h" - -#include -#include - -gl_gs_frame::gl_gs_frame(QScreen* screen, const QRect& geometry, const QIcon& appIcon, std::shared_ptr gui_settings, bool force_fullscreen) - : gs_frame(screen, geometry, appIcon, std::move(gui_settings), force_fullscreen) -{ - setSurfaceType(QSurface::OpenGLSurface); - - m_format.setRenderableType(QSurfaceFormat::OpenGL); - m_format.setMajorVersion(4); - m_format.setMinorVersion(3); - m_format.setProfile(QSurfaceFormat::CoreProfile); - m_format.setAlphaBufferSize(0); - m_format.setDepthBufferSize(0); - m_format.setSwapBehavior(QSurfaceFormat::SwapBehavior::DoubleBuffer); - m_format.setSwapInterval(0); - if (g_cfg.video.debug_output) - { - m_format.setOption(QSurfaceFormat::FormatOption::DebugContext); - } - setFormat(m_format); - create(); - show(); -} - -void gl_gs_frame::reset() -{ - m_primary_context = nullptr; -} - -draw_context_t gl_gs_frame::make_context() -{ - auto context = new GLContext(); - context->handle = new QOpenGLContext(); - - if (m_primary_context) - { - QOffscreenSurface* surface = nullptr; - - // Workaround for the Qt warning: "Attempting to create QWindow-based QOffscreenSurface outside the gui thread. Expect failures." - Emu.BlockingCallFromMainThread([&]() - { - surface = new QOffscreenSurface(); - surface->setFormat(m_format); - surface->create(); - }); - - // Share resources with the first created context - context->handle->setShareContext(m_primary_context->handle); - context->surface = surface; - context->owner = true; - } - else - { - // This is the first created context, all others will share resources with this one - m_primary_context = context; - context->surface = this; - context->owner = false; - } - - context->handle->setFormat(m_format); - - if (!context->handle->create()) - { - fmt::throw_exception("Failed to create OpenGL context"); - } - - return context; -} - -void gl_gs_frame::set_current(draw_context_t ctx) -{ - if (!ctx) - { - fmt::throw_exception("Null context handle passed to set_current"); - } - - const auto context = static_cast(ctx); - - if (!context->handle->makeCurrent(context->surface)) - { - if (!context->owner) - { - create(); - } - else if (!context->handle->isValid()) - { - if (!context->handle->create()) - { - fmt::throw_exception("Failed to create OpenGL context"); - } - } - - if (!context->handle->makeCurrent(context->surface)) - { - fmt::throw_exception("Could not bind OpenGL context"); - } - } -} - -void gl_gs_frame::delete_context(draw_context_t ctx) -{ - const auto gl_ctx = static_cast(ctx); - - gl_ctx->handle->doneCurrent(); - -#ifdef _MSC_VER - // AMD driver crashes when executing wglDeleteContext - // Catch with SEH - __try - { - delete gl_ctx->handle; - } - __except (GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION ? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH) - { - rsx_log.fatal("Your graphics driver just crashed whilst cleaning up. All consumed VRAM should have been released, but you may want to restart the emulator just in case"); - } -#else - delete gl_ctx->handle; -#endif - - if (gl_ctx->owner) - { - delete gl_ctx->surface; - } - - delete gl_ctx; -} - -void gl_gs_frame::flip(draw_context_t context, bool skip_frame) -{ - gs_frame::flip(context); - - // Do not swap buffers if frame skip is active - if (skip_frame) - return; - - const auto gl_ctx = static_cast(context); - - if (auto window = dynamic_cast(gl_ctx->surface); window && window->isExposed()) - { - gl_ctx->handle->swapBuffers(gl_ctx->surface); - } -} diff --git a/rpcs3qt-legacy/gl_gs_frame.h b/rpcs3qt-legacy/gl_gs_frame.h deleted file mode 100644 index 223ac3cfd..000000000 --- a/rpcs3qt-legacy/gl_gs_frame.h +++ /dev/null @@ -1,28 +0,0 @@ -#pragma once - -#include "gs_frame.h" - -#include - -struct GLContext -{ - QSurface* surface = nullptr; - QOpenGLContext* handle = nullptr; - bool owner = false; -}; - -class gl_gs_frame : public gs_frame -{ -private: - QSurfaceFormat m_format; - GLContext* m_primary_context = nullptr; - -public: - explicit gl_gs_frame(QScreen* screen, const QRect& geometry, const QIcon& appIcon, std::shared_ptr gui_settings, bool force_fullscreen); - - void reset() override; - draw_context_t make_context() override; - void set_current(draw_context_t ctx) override; - void delete_context(draw_context_t ctx) override; - void flip(draw_context_t context, bool skip_frame = false) override; -}; diff --git a/rpcs3qt-legacy/gs_frame.cpp b/rpcs3qt-legacy/gs_frame.cpp deleted file mode 100644 index 79f958ccf..000000000 --- a/rpcs3qt-legacy/gs_frame.cpp +++ /dev/null @@ -1,1210 +0,0 @@ -#include "gs_frame.h" -#include "gui_settings.h" - -#include "util/Config.h" -#include "util/Timer.h" -#include "util/date_time.h" -#include "util/File.h" -#include "util/video_provider.h" -#include "Emu/System.h" -#include "Emu/system_config.h" -#include "Emu/system_progress.hpp" -#include "Emu/IdManager.h" -#include "Emu/Audio/audio_utils.h" -#include "rpcsx/fw/ps3/cellScreenshot.h" -#include "rpcsx/fw/ps3/cellAudio.h" -#include "Emu/Cell/lv2/sys_rsxaudio.h" -#include "Emu/RSX/rsx_utils.h" -#include "Emu/RSX/Overlays/overlay_message.h" -#include "Emu/Io/interception.h" -#include "Emu/Io/recording_config.h" - -#include -#include -#include -#include -#include -#include - -#include -#include - -#include "png.h" - -#ifdef _WIN32 -#include -#elif defined(__APPLE__) -// nothing -#else -#ifdef HAVE_WAYLAND -#include -#include -#endif -#ifdef HAVE_X11 -#include -#endif -#endif - -LOG_CHANNEL(screenshot_log, "SCREENSHOT"); -LOG_CHANNEL(mark_log, "MARK"); -LOG_CHANNEL(gui_log, "GUI"); - -extern atomic_t g_user_asked_for_recording; -extern atomic_t g_user_asked_for_screenshot; -extern atomic_t g_user_asked_for_frame_capture; -extern atomic_t g_disable_frame_limit; -extern atomic_t g_game_window_focused; -extern atomic_t g_recording_mode; - -namespace pad -{ - extern atomic_t g_home_menu_requested; -} - -gs_frame::gs_frame(QScreen* screen, const QRect& geometry, const QIcon& appIcon, std::shared_ptr gui_settings, bool force_fullscreen) - : QWindow(), m_initial_geometry(geometry), m_gui_settings(std::move(gui_settings)), m_start_games_fullscreen(force_fullscreen), m_renderer(g_cfg.video.renderer) -{ - m_window_title = Emu.GetFormattedTitle(0); - - if (!g_cfg_recording.load()) - { - gui_log.notice("Could not load recording config. Using defaults."); - } - - g_fxo->need(); - m_video_encoder = std::make_shared(); - - if (!appIcon.isNull()) - { - setIcon(appIcon); - } - -#ifdef __APPLE__ - // Needed for MoltenVK to work properly on MacOS - if (g_cfg.video.renderer == video_renderer::vulkan) - setSurfaceType(QSurface::VulkanSurface); -#endif - - // NOTE: You cannot safely create a wayland window that has hidden initial status and perform any changes on the window while it is still hidden. - // Doing this will create a surface with deferred commands that require a buffer. When binding to your session, this may assert in your compositor due to protocol restrictions. - Visibility startup_visibility = Hidden; -#ifndef _WIN32 - if (const char* session_type = ::getenv("XDG_SESSION_TYPE")) - { - if (!strcasecmp(session_type, "wayland")) - { - // Start windowed. This is a featureless rectangle on-screen with no window decorations. - // It does not even resemble a window until the WM attaches later on. - // Fullscreen could technically work with some fiddling, but easily breaks depending on geometry input. - startup_visibility = Windowed; - } - } -#endif - - setMinimumWidth(160); - setMinimumHeight(90); - setScreen(screen); - setGeometry(geometry); - setTitle(QString::fromStdString(m_window_title)); - - if (g_cfg.video.renderer != video_renderer::opengl) - { - // Do not display the window before OpenGL is configured! - // This works fine in windows and X11 but wayland-egl will crash later. - setVisibility(startup_visibility); - create(); - } - - load_gui_settings(); - - // Change cursor when in fullscreen. - connect(this, &QWindow::visibilityChanged, this, [this](QWindow::Visibility visibility) - { - handle_cursor(visibility, true, false, true); - }); - - // Change cursor when this window gets or loses focus. - connect(this, &QWindow::activeChanged, this, [this]() - { - g_game_window_focused = isActive(); - handle_cursor(visibility(), false, true, true); - }); - - // Configure the mouse hide on idle timer - connect(&m_mousehide_timer, &QTimer::timeout, this, &gs_frame::mouse_hide_timeout); - m_mousehide_timer.setSingleShot(true); - - m_progress_indicator = std::make_unique(0, 100); -} - -gs_frame::~gs_frame() -{ - g_user_asked_for_screenshot = false; - pad::g_home_menu_requested = false; - - // Save active screen to gui settings - const QScreen* current_screen = screen(); - const QList screens = QGuiApplication::screens(); - int screen_index = 0; - - for (int i = 0; i < screens.count(); i++) - { - if (current_screen == ::at32(screens, i)) - { - screen_index = i; - break; - } - } - - m_gui_settings->SetValue(gui::gs_screen, screen_index); -} - -void gs_frame::load_gui_settings() -{ - m_disable_mouse = m_gui_settings->GetValue(gui::gs_disableMouse).toBool(); - m_disable_kb_hotkeys = m_gui_settings->GetValue(gui::gs_disableKbHotkeys).toBool(); - m_show_mouse_in_fullscreen = m_gui_settings->GetValue(gui::gs_showMouseFs).toBool(); - m_lock_mouse_in_fullscreen = m_gui_settings->GetValue(gui::gs_lockMouseFs).toBool(); - m_hide_mouse_after_idletime = m_gui_settings->GetValue(gui::gs_hideMouseIdle).toBool(); - m_hide_mouse_idletime = m_gui_settings->GetValue(gui::gs_hideMouseIdleTime).toUInt(); - - if (m_disable_kb_hotkeys) - { - if (m_shortcut_handler) - { - m_shortcut_handler->deleteLater(); - m_shortcut_handler = nullptr; - } - } - else if (!m_shortcut_handler) - { - m_shortcut_handler = new shortcut_handler(gui::shortcuts::shortcut_handler_id::game_window, this, m_gui_settings); - connect(m_shortcut_handler, &shortcut_handler::shortcut_activated, this, &gs_frame::handle_shortcut); - } -} - -void gs_frame::update_shortcuts() -{ - if (m_shortcut_handler) - { - m_shortcut_handler->update(); - } -} - -void gs_frame::paintEvent(QPaintEvent* event) -{ - Q_UNUSED(event) -} - -void gs_frame::showEvent(QShowEvent* event) -{ - // We have to calculate new window positions, since the frame is only known once the window was created. - // We will try to find the originally requested dimensions if possible by moving the frame. - - // NOTES: The parameter m_initial_geometry is not necessarily equal to our actual geometry() at this point. - // That's why we use m_initial_geometry instead of the frameGeometry() in some places. - // All of these values, including the screen geometry, can also be negative numbers. - - const QRect available_geometry = screen()->availableGeometry(); // The available screen geometry - const QRect inner_geometry = geometry(); // The current inner geometry - const QRect outer_geometry = frameGeometry(); // The current outer geometry - - // Calculate the left and top frame borders (this will include window handles) - const int left_border = inner_geometry.left() - outer_geometry.left(); - const int top_border = inner_geometry.top() - outer_geometry.top(); - - // Calculate the initially expected frame origin - const QPoint expected_pos(m_initial_geometry.left() - left_border, - m_initial_geometry.top() - top_border); - - // Make sure that the expected position is inside the screen (check left and top borders) - QPoint pos(std::max(expected_pos.x(), available_geometry.left()), - std::max(expected_pos.y(), available_geometry.top())); - - // Find the maximum position that still ensures that the frame is completely visible inside the screen (check right and bottom borders) - QPoint max_pos(available_geometry.left() + available_geometry.width() - frameGeometry().width(), - available_geometry.top() + available_geometry.height() - frameGeometry().height()); - - // Make sure that the "maximum" position is inside the screen (check left and top borders) - max_pos.setX(std::max(max_pos.x(), available_geometry.left())); - max_pos.setY(std::max(max_pos.y(), available_geometry.top())); - - // Adjust the expected position accordingly - pos.setX(std::min(pos.x(), max_pos.x())); - pos.setY(std::min(pos.y(), max_pos.y())); - - // Set the new position - setFramePosition(pos); - - QWindow::showEvent(event); -} - -void gs_frame::handle_shortcut(gui::shortcuts::shortcut shortcut_key, const QKeySequence& key_sequence) -{ - gui_log.notice("Game window registered shortcut: %s (%s)", shortcut_key, key_sequence.toString()); - - if (m_disable_kb_hotkeys) - { - return; - } - - switch (shortcut_key) - { - case gui::shortcuts::shortcut::gw_toggle_fullscreen: - { - toggle_fullscreen(); - break; - } - case gui::shortcuts::shortcut::gw_exit_fullscreen: - { - if (visibility() == FullScreen) - { - toggle_fullscreen(); - } - break; - } - case gui::shortcuts::shortcut::gw_log_mark: - { - static int count = 0; - mark_log.success("Made forced mark %d in log", ++count); - break; - } - case gui::shortcuts::shortcut::gw_mouse_lock: - { - toggle_mouselock(); - break; - } - case gui::shortcuts::shortcut::gw_screenshot: - { - g_user_asked_for_screenshot = true; - break; - } - case gui::shortcuts::shortcut::gw_toggle_recording: - { - toggle_recording(); - break; - } - case gui::shortcuts::shortcut::gw_pause_play: - { - switch (Emu.GetStatus()) - { - case system_state::ready: - { - Emu.Run(true); - return; - } - case system_state::paused: - { - Emu.Resume(); - return; - } - default: - { - Emu.Pause(); - return; - } - } - break; - } - case gui::shortcuts::shortcut::gw_restart: - { - if (Emu.IsStopped()) - { - Emu.Restart(); - return; - } - - extern bool boot_last_savestate(bool testing); - boot_last_savestate(false); - break; - } - case gui::shortcuts::shortcut::gw_savestate: - { - if (!g_cfg.savestate.suspend_emu) - { - Emu.after_kill_callback = []() - { - Emu.Restart(); - }; - - // Make sure we keep the game window opened - Emu.SetContinuousMode(true); - } - - Emu.Kill(false, true); - return; - } - case gui::shortcuts::shortcut::gw_rsx_capture: - { - g_user_asked_for_frame_capture = true; - break; - } - case gui::shortcuts::shortcut::gw_frame_limit: - { - g_disable_frame_limit = !g_disable_frame_limit; - gui_log.warning("%s boost mode", g_disable_frame_limit.load() ? "Enabled" : "Disabled"); - break; - } - case gui::shortcuts::shortcut::gw_toggle_mouse_and_keyboard: - { - input::toggle_mouse_and_keyboard(); - break; - } - case gui::shortcuts::shortcut::gw_home_menu: - { - pad::g_home_menu_requested = true; - break; - } - case gui::shortcuts::shortcut::gw_mute_unmute: - { - audio::toggle_mute(); - break; - } - case gui::shortcuts::shortcut::gw_volume_up: - { - audio::change_volume(5); - break; - } - case gui::shortcuts::shortcut::gw_volume_down: - { - audio::change_volume(-5); - break; - } - default: - { - break; - } - } -} - -void gs_frame::toggle_fullscreen() -{ - Emu.CallFromMainThread([this]() - { - if (visibility() == FullScreen) - { - // Change to the last recorded visibility. Sanitize it just in case. - if (m_last_visibility != Visibility::Maximized && m_last_visibility != Visibility::Windowed) - { - m_last_visibility = Visibility::Windowed; - } - setVisibility(m_last_visibility); - } - else - { - // Backup visibility for exiting fullscreen mode later. Don't do this in the visibilityChanged slot, - // since entering fullscreen from maximized will first change the visibility to windowed. - m_last_visibility = visibility(); - setVisibility(FullScreen); - } - }); -} - -void gs_frame::toggle_recording() -{ - utils::video_provider& video_provider = g_fxo->get(); - - if (g_recording_mode == recording_mode::cell) - { - gui_log.warning("A video recorder is already in use by cell. Regular recording can not proceed."); - m_video_encoder->stop(); - return; - } - - if (g_recording_mode.exchange(recording_mode::stopped) == recording_mode::rpcs3) - { - m_video_encoder->stop(); - - if (!video_provider.set_video_sink(nullptr, recording_mode::rpcs3)) - { - gui_log.warning("The video provider could not release the video sink. A sink with higher priority must have been set."); - } - - // Play a sound - if (const std::string sound_path = fs::get_config_dir() + "sounds/snd_recording.wav"; fs::is_file(sound_path)) - { - Emu.GetCallbacks().play_sound(sound_path); - } - else - { - QApplication::beep(); - } - - ensure(m_video_encoder->path().starts_with(fs::get_config_dir())); - const std::string shortpath = m_video_encoder->path().substr(fs::get_config_dir().size() - 1); // -1 for / - rsx::overlays::queue_message(tr("Recording saved: %0").arg(QString::fromStdString(shortpath)).toStdString()); - } - else - { - m_video_encoder->stop(); - - const std::string& id = Emu.GetTitleID(); - std::string video_path = fs::get_config_dir() + "recordings/"; - if (!id.empty()) - { - video_path += id + "/"; - } - - if (!fs::create_path(video_path) && fs::g_tls_error != fs::error::exist) - { - screenshot_log.error("Failed to create recordings path \"%s\" : %s", video_path, fs::g_tls_error); - return; - } - - if (!id.empty()) - { - video_path += id + "_"; - } - - video_path += "recording_" + date_time::current_time_narrow<'_'>() + ".mp4"; - - utils::video_encoder::frame_format output_format{}; - output_format.av_pixel_format = static_cast(g_cfg_recording.video.pixel_format.get()); - output_format.width = g_cfg_recording.video.width; - output_format.height = g_cfg_recording.video.height; - output_format.pitch = g_cfg_recording.video.width * 4; - - m_video_encoder->use_internal_audio = true; - m_video_encoder->use_internal_video = true; - m_video_encoder->set_path(video_path); - m_video_encoder->set_framerate(g_cfg_recording.video.framerate); - m_video_encoder->set_video_bitrate(g_cfg_recording.video.video_bps); - m_video_encoder->set_video_codec(g_cfg_recording.video.video_codec); - m_video_encoder->set_max_b_frames(g_cfg_recording.video.max_b_frames); - m_video_encoder->set_gop_size(g_cfg_recording.video.gop_size); - m_video_encoder->set_output_format(output_format); - - switch (g_cfg.audio.provider) - { - case audio_provider::none: - { - // Disable audio recording - m_video_encoder->use_internal_audio = false; - break; - } - case audio_provider::cell_audio: - { - const cell_audio_config& cfg = g_fxo->get().cfg; - m_video_encoder->set_sample_rate(cfg.audio_sampling_rate); - m_video_encoder->set_audio_channels(cfg.audio_channels); - break; - } - case audio_provider::rsxaudio: - { - const auto& rsx_audio = g_fxo->get(); - m_video_encoder->set_sample_rate(rsx_audio.get_sample_rate()); - m_video_encoder->set_audio_channels(rsx_audio.get_channel_count()); - break; - } - } - - m_video_encoder->set_audio_bitrate(g_cfg_recording.audio.audio_bps); - m_video_encoder->set_audio_codec(g_cfg_recording.audio.audio_codec); - m_video_encoder->encode(); - - if (m_video_encoder->has_error) - { - rsx::overlays::queue_message(tr("Recording not possible").toStdString()); - m_video_encoder->stop(); - return; - } - - if (!video_provider.set_video_sink(m_video_encoder, recording_mode::rpcs3)) - { - gui_log.warning("The video provider could not set the video sink. A sink with higher priority must have been set."); - rsx::overlays::queue_message(tr("Recording not possible").toStdString()); - m_video_encoder->stop(); - return; - } - - video_provider.set_pause_time_us(0); - - g_recording_mode = recording_mode::rpcs3; - - rsx::overlays::queue_message(tr("Recording started").toStdString()); - } -} - -void gs_frame::toggle_mouselock() -{ - // first we toggle the value - m_mouse_hide_and_lock = !m_mouse_hide_and_lock; - - // and update the cursor - handle_cursor(visibility(), false, false, true); -} - -void gs_frame::update_cursor() -{ - bool show_mouse; - - if (!isActive()) - { - // Show the mouse by default if this is not the active window - show_mouse = true; - } - else if (m_hide_mouse_after_idletime && !m_mousehide_timer.isActive()) - { - // Hide the mouse if the idle timeout was reached (which means that the timer isn't running) - show_mouse = false; - } - else if (visibility() == QWindow::Visibility::FullScreen) - { - // Fullscreen: Show or hide the mouse depending on the settings. - show_mouse = m_show_mouse_in_fullscreen; - } - else - { - // Windowed: Hide the mouse if it was locked by the user - show_mouse = !m_mouse_hide_and_lock; - } - - // Update Cursor if necessary - if (show_mouse != m_show_mouse.exchange(show_mouse)) - { - setCursor(m_show_mouse ? Qt::ArrowCursor : Qt::BlankCursor); - } -} - -bool gs_frame::get_mouse_lock_state() -{ - handle_cursor(visibility(), false, false, true); - - return isActive() && m_mouse_hide_and_lock; -} - -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 ? Visibility::AutomaticVisibility : current_visibility, false); - m_gui_settings->SetValue(gui::gs_geometry, geometry(), true); - - if (!g_progr_text) - { - // Hide the dialog before stopping if no progress bar is being shown. - // Otherwise users might think that the game softlocked if stopping takes too long. - QWindow::hide(); - } -} - -void gs_frame::close() -{ - if (m_is_closing.exchange(true)) - { - gui_log.notice("Closing game window (ignored, already closing)"); - return; - } - - gui_log.notice("Closing game window"); - - if (m_ignore_stop_events) - { - return; - } - - Emu.CallFromMainThread([this]() - { - // Hide window if necessary - hide_on_close(); - - if (!Emu.IsStopped()) - { - // Notify progress dialog cancellation - g_system_progress_canceled = true; - - // Blocking shutdown request. Obsolete, but I'm keeping it here as last resort. - Emu.after_kill_callback = [this]() - { - deleteLater(); - }; - Emu.GracefulShutdown(true); - } - else - { - deleteLater(); - } - }); -} - -void gs_frame::reset() -{ -} - -bool gs_frame::shown() -{ - return QWindow::isVisible(); -} - -void gs_frame::hide() -{ - Emu.CallFromMainThread([this]() - { - QWindow::hide(); - }); -} - -void gs_frame::show() -{ - Emu.CallFromMainThread([this]() - { - QWindow::show(); - - if (g_cfg.misc.start_fullscreen || m_start_games_fullscreen) - { - setVisibility(FullScreen); - } - 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) - { - setVisibility(visibility); - } - } - }); -} - -display_handle_t gs_frame::handle() const -{ -#ifdef _WIN32 - return reinterpret_cast(this->winId()); -#elif defined(__APPLE__) - return reinterpret_cast(this->winId()); // NSView -#else -#ifdef HAVE_WAYLAND - QPlatformNativeInterface* native = QGuiApplication::platformNativeInterface(); - struct wl_display* wl_dpy = static_cast( - native->nativeResourceForWindow("display", NULL)); - struct wl_surface* wl_surf = static_cast( - native->nativeResourceForWindow("surface", const_cast(static_cast(this)))); - if (wl_dpy != nullptr && wl_surf != nullptr) - { - return std::make_pair(wl_dpy, wl_surf); - } - else -#endif -#ifdef HAVE_X11 - { - return std::make_pair(XOpenDisplay(0), static_cast(this->winId())); - } -#else - fmt::throw_exception("Vulkan X11 support disabled at compile-time."); -#endif -#endif -} - -draw_context_t gs_frame::make_context() -{ - return nullptr; -} - -void gs_frame::set_current(draw_context_t context) -{ - Q_UNUSED(context) -} - -void gs_frame::delete_context(draw_context_t context) -{ - Q_UNUSED(context) -} - -int gs_frame::client_width() -{ -#ifdef _WIN32 - RECT rect; - if (GetClientRect(reinterpret_cast(winId()), &rect)) - { - return rect.right - rect.left; - } -#endif // _WIN32 - return width() * devicePixelRatio(); -} - -int gs_frame::client_height() -{ -#ifdef _WIN32 - RECT rect; - if (GetClientRect(reinterpret_cast(winId()), &rect)) - { - return rect.bottom - rect.top; - } -#endif // _WIN32 - return height() * devicePixelRatio(); -} - -f64 gs_frame::client_display_rate() -{ - f64 rate = 20.; // Minimum is 20 - - Emu.BlockingCallFromMainThread([this, &rate]() - { - const QList screens = QGuiApplication::screens(); - - for (int i = 0; i < screens.count(); i++) - { - rate = std::fmax(rate, ::at32(screens, i)->refreshRate()); - } - }); - - return rate; -} - -void gs_frame::flip(draw_context_t, bool /*skip_frame*/) -{ - static Timer fps_t; - - if (!m_flip_showed_frame) - { - // Show on first flip - m_flip_showed_frame = true; - show(); - fps_t.Start(); - } - - ++m_frames; - - 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)); - }); - } - - m_frames = 0; - fps_t.Start(); - } - - if (g_user_asked_for_recording.exchange(false)) - { - Emu.CallFromMainThread([this]() - { - toggle_recording(); - }); - } -} - -bool gs_frame::can_consume_frame() const -{ - utils::video_provider& video_provider = g_fxo->get(); - return video_provider.can_consume_frame(); -} - -void gs_frame::present_frame(std::vector& data, u32 pitch, u32 width, u32 height, bool is_bgra) const -{ - utils::video_provider& video_provider = g_fxo->get(); - video_provider.present_frame(data, pitch, width, height, is_bgra); -} - -void gs_frame::take_screenshot(std::vector&& data, u32 sshot_width, u32 sshot_height, bool is_bgra) -{ - std::thread( - [sshot_width, sshot_height, is_bgra](std::vector sshot_data) - { - thread_base::set_name("Screenshot"); - - screenshot_log.notice("Taking screenshot (%dx%d)", sshot_width, sshot_height); - - const std::string& id = Emu.GetTitleID(); - std::string screen_path = fs::get_config_dir() + "screenshots/"; - if (!id.empty()) - { - screen_path += id + "/"; - } - - if (!fs::create_path(screen_path) && fs::g_tls_error != fs::error::exist) - { - screenshot_log.error("Failed to create screenshot path \"%s\" : %s", screen_path, fs::g_tls_error); - return; - } - - std::string filename = screen_path; - if (!id.empty()) - { - filename += id + "_"; - } - - filename += "screenshot_" + date_time::current_time_narrow<'_'>() + ".png"; - - fs::file sshot_file(filename, fs::open_mode::create + fs::open_mode::write + fs::open_mode::excl); - if (!sshot_file) - { - screenshot_log.error("Failed to save screenshot \"%s\" : %s", filename, fs::g_tls_error); - return; - } - - std::vector sshot_data_alpha(sshot_data.size()); - const u32* sshot_ptr = reinterpret_cast(sshot_data.data()); - u32* alpha_ptr = reinterpret_cast(sshot_data_alpha.data()); - - if (is_bgra) [[likely]] - { - for (usz index = 0; index < sshot_data.size() / sizeof(u32); index++) - { - alpha_ptr[index] = ((sshot_ptr[index] & 0xFF) << 16) | (sshot_ptr[index] & 0xFF00) | ((sshot_ptr[index] & 0xFF0000) >> 16) | 0xFF000000; - } - } - else - { - for (usz index = 0; index < sshot_data.size() / sizeof(u32); index++) - { - alpha_ptr[index] = sshot_ptr[index] | 0xFF000000; - } - } - - screenshot_info manager; - { - auto& s = g_fxo->get(); - std::lock_guard lock(s.mutex); - manager = s; - } - - struct scoped_png_ptrs - { - png_structp write_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr); - png_infop info_ptr = png_create_info_struct(write_ptr); - - ~scoped_png_ptrs() - { - png_free_data(write_ptr, info_ptr, PNG_FREE_ALL, -1); - png_destroy_write_struct(&write_ptr, &info_ptr); - } - }; - - png_text text[6] = {}; - int num_text = 0; - - const QDateTime date_time = QDateTime::currentDateTime(); - const std::string creation_time = date_time.toString("yyyy:MM:dd hh:mm:ss").toStdString(); - const std::string photo_title = manager.get_photo_title(); - const std::string game_title = manager.get_game_title(); - const std::string game_comment = manager.get_game_comment(); - const std::string& title_id = Emu.GetTitleID(); - - // Write tEXt chunk - text[num_text].compression = PNG_TEXT_COMPRESSION_NONE; - text[num_text].key = const_cast("Creation Time"); - text[num_text].text = const_cast(creation_time.c_str()); - ++num_text; - text[num_text].compression = PNG_TEXT_COMPRESSION_NONE; - text[num_text].key = const_cast("Source"); - text[num_text].text = const_cast("RPCS3"); // Originally PlayStation(R)3 - ++num_text; - text[num_text].compression = PNG_TEXT_COMPRESSION_NONE; - text[num_text].key = const_cast("Title ID"); - text[num_text].text = const_cast(title_id.c_str()); - ++num_text; - - // Write tTXt chunk (they probably meant zTXt) - text[num_text].compression = PNG_TEXT_COMPRESSION_zTXt; - text[num_text].key = const_cast("Title"); - text[num_text].text = const_cast(photo_title.c_str()); - ++num_text; - text[num_text].compression = PNG_TEXT_COMPRESSION_zTXt; - text[num_text].key = const_cast("Game Title"); - text[num_text].text = const_cast(game_title.c_str()); - ++num_text; - text[num_text].compression = PNG_TEXT_COMPRESSION_zTXt; - text[num_text].key = const_cast("Comment"); - text[num_text].text = const_cast(game_comment.c_str()); - - // Create image from data - QImage img(sshot_data_alpha.data(), sshot_width, sshot_height, sshot_width * 4, QImage::Format_RGBA8888); - - // Scale image if necessary - const auto& avconf = g_fxo->get(); - auto new_size = avconf.aspect_convert_dimensions(size2u{u32(img.width()), u32(img.height())}); - - if (new_size.width != static_cast(img.width()) || new_size.height != static_cast(img.height())) - { - img = img.scaled(QSize(new_size.width, new_size.height), Qt::AspectRatioMode::IgnoreAspectRatio, Qt::TransformationMode::SmoothTransformation); - img.convertTo(QImage::Format_RGBA8888); // The current Qt version changes the image format during smooth scaling, so we have to change it back. - } - - // Create row pointers for libpng - std::vector rows(img.height()); - for (int y = 0; y < img.height(); y++) - rows[y] = img.scanLine(y); - - std::vector encoded_png; - - const auto write_png = [&]() - { - const scoped_png_ptrs ptrs; - png_set_IHDR(ptrs.write_ptr, ptrs.info_ptr, img.width(), img.height(), 8, PNG_COLOR_TYPE_RGBA, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT); - png_set_text(ptrs.write_ptr, ptrs.info_ptr, text, 6); - png_set_rows(ptrs.write_ptr, ptrs.info_ptr, &rows[0]); - png_set_write_fn(ptrs.write_ptr, &encoded_png, [](png_structp png_ptr, png_bytep data, png_size_t length) - { - std::vector* p = static_cast*>(png_get_io_ptr(png_ptr)); - p->insert(p->end(), data, data + length); - }, - nullptr); - png_write_png(ptrs.write_ptr, ptrs.info_ptr, PNG_TRANSFORM_IDENTITY, nullptr); - }; - - write_png(); - sshot_file.write(encoded_png.data(), encoded_png.size()); - - screenshot_log.success("Successfully saved screenshot to %s", filename); - - if (manager.is_enabled) - { - const std::string cell_sshot_overlay_path = manager.get_overlay_path(); - if (fs::is_file(cell_sshot_overlay_path)) - { - screenshot_log.notice("Adding overlay to cell screenshot from %s", cell_sshot_overlay_path); - - QImage overlay_img; - - if (!overlay_img.load(QString::fromStdString(cell_sshot_overlay_path))) - { - screenshot_log.error("Failed to read cell screenshot overlay '%s' : %s", cell_sshot_overlay_path, fs::g_tls_error); - return; - } - - // Games choose the overlay file and the offset based on the current video resolution. - // We need to scale the overlay if our resolution scaling causes the image to have a different size. - - // Scale the resolution first (as seen before with the image) - new_size = avconf.aspect_convert_dimensions(size2u{avconf.resolution_x, avconf.resolution_y}); - - if (new_size.width != static_cast(img.width()) || new_size.height != static_cast(img.height())) - { - const int scale = rsx::get_resolution_scale_percent(); - const int x = (scale * manager.overlay_offset_x) / 100; - const int y = (scale * manager.overlay_offset_y) / 100; - const int width = (scale * overlay_img.width()) / 100; - const int height = (scale * overlay_img.height()) / 100; - - screenshot_log.notice("Scaling overlay from %dx%d at offset (%d,%d) to %dx%d at offset (%d,%d)", - overlay_img.width(), overlay_img.height(), manager.overlay_offset_x, manager.overlay_offset_y, width, height, x, y); - - manager.overlay_offset_x = x; - manager.overlay_offset_y = y; - overlay_img = overlay_img.scaled(QSize(width, height), Qt::AspectRatioMode::IgnoreAspectRatio, Qt::TransformationMode::SmoothTransformation); - } - - if (manager.overlay_offset_x < static_cast(img.width()) && - manager.overlay_offset_y < static_cast(img.height()) && - manager.overlay_offset_x + overlay_img.width() > 0 && - manager.overlay_offset_y + overlay_img.height() > 0) - { - QImage screenshot_img(rows[0], img.width(), img.height(), QImage::Format_RGBA8888); - QPainter painter(&screenshot_img); - painter.drawImage(manager.overlay_offset_x, manager.overlay_offset_y, overlay_img); - painter.end(); - - std::memcpy(rows[0], screenshot_img.constBits(), screenshot_img.sizeInBytes()); - - screenshot_log.success("Applied screenshot overlay '%s'", cell_sshot_overlay_path); - } - } - - const std::string cell_sshot_filename = manager.get_screenshot_path(date_time.toString("yyyy/MM/dd").toStdString()); - const std::string cell_sshot_dir = fs::get_parent_dir(cell_sshot_filename); - - screenshot_log.notice("Saving cell screenshot to %s", cell_sshot_filename); - - if (!fs::create_path(cell_sshot_dir) && fs::g_tls_error != fs::error::exist) - { - screenshot_log.error("Failed to create cell screenshot dir \"%s\" : %s", cell_sshot_dir, fs::g_tls_error); - return; - } - - fs::file cell_sshot_file(cell_sshot_filename, fs::open_mode::create + fs::open_mode::write + fs::open_mode::excl); - if (!cell_sshot_file) - { - screenshot_log.error("Failed to save cell screenshot \"%s\" : %s", cell_sshot_filename, fs::g_tls_error); - return; - } - - encoded_png.clear(); - write_png(); - cell_sshot_file.write(encoded_png.data(), encoded_png.size()); - - screenshot_log.success("Successfully saved cell screenshot to %s", cell_sshot_filename); - } - - // Play a sound - Emu.CallFromMainThread([]() - { - if (const std::string sound_path = fs::get_config_dir() + "sounds/snd_screenshot.wav"; fs::is_file(sound_path)) - { - Emu.GetCallbacks().play_sound(sound_path); - } - else - { - QApplication::beep(); - } - }); - - ensure(filename.starts_with(fs::get_config_dir())); - const std::string shortpath = filename.substr(fs::get_config_dir().size() - 1); // -1 for / - rsx::overlays::queue_message(tr("Screenshot saved: %0").arg(QString::fromStdString(shortpath)).toStdString()); - - return; - }, - std::move(data)) - .detach(); -} - -void gs_frame::mouseDoubleClickEvent(QMouseEvent* ev) -{ - if (m_disable_mouse) - { - return; - } - - switch (g_cfg.io.move) - { - case move_handler::mouse: - case move_handler::raw_mouse: -#ifdef HAVE_LIBEVDEV - case move_handler::gun: -#endif - return; - default: - break; - } - - if (ev->button() == Qt::LeftButton) - { - toggle_fullscreen(); - } -} - -void gs_frame::handle_cursor(QWindow::Visibility visibility, bool visibility_changed, bool active_changed, bool start_idle_timer) -{ - // Let's reload the gui settings when the visibility or the active window changes. - if (visibility_changed || active_changed) - { - load_gui_settings(); - - // Update the mouse lock state if the visibility changed. - if (visibility_changed) - { - // In fullscreen we default to hiding and locking. In windowed mode we do not want the lock by default. - m_mouse_hide_and_lock = (visibility == QWindow::Visibility::FullScreen) && m_lock_mouse_in_fullscreen; - } - } - - // Update the mouse hide timer - if (m_hide_mouse_after_idletime && start_idle_timer) - { - m_mousehide_timer.start(m_hide_mouse_idletime); - } - else - { - m_mousehide_timer.stop(); - } - - // Update the cursor visibility - update_cursor(); -} - -void gs_frame::mouse_hide_timeout() -{ - // Our idle timeout occurred, so we update the cursor - if (m_hide_mouse_after_idletime && m_show_mouse) - { - handle_cursor(visibility(), false, false, false); - } -} - -bool gs_frame::event(QEvent* ev) -{ - if (ev->type() == QEvent::Close) - { - if (m_gui_settings->GetValue(gui::ib_confirm_exit).toBool()) - { - if (visibility() == FullScreen) - { - toggle_fullscreen(); - } - - int result = QMessageBox::Yes; - m_gui_settings->ShowConfirmationBox(tr("Exit Game?"), - tr("Do you really want to exit the game?

Any unsaved progress will be lost!
"), - gui::ib_confirm_exit, &result, nullptr); - - if (result != QMessageBox::Yes) - { - return true; - } - } - - gui_log.notice("Game window close event issued"); - - if (m_ignore_stop_events) - { - return QWindow::event(ev); - } - - if (Emu.IsStopped()) - { - // This should be unreachable, but never say never. Properly close the window anyway. - close(); - } - else - { - // Notify progress dialog cancellation - g_system_progress_canceled = true; - - // Issue async shutdown - Emu.GracefulShutdown(true, true); - - // Hide window if necessary - hide_on_close(); - - // Do not propagate the close event. It will be closed by the rsx_thread. - return true; - } - } - else if (ev->type() == QEvent::MouseMove && (!m_show_mouse || m_mousehide_timer.isActive())) - { - // This will make the cursor visible again if it was hidden by the mouse idle timeout - handle_cursor(visibility(), false, false, true); - } - return QWindow::event(ev); -} - -void gs_frame::progress_reset(bool reset_limit) -{ - m_progress_indicator->reset(); - - if (reset_limit) - { - progress_set_limit(100); - } -} - -void gs_frame::progress_set_value(int value) -{ - m_progress_indicator->set_value(value); -} - -void gs_frame::progress_increment(int delta) -{ - if (delta != 0) - { - m_progress_indicator->set_value(m_progress_indicator->value() + delta); - } -} - -void gs_frame::progress_set_limit(int limit) -{ - m_progress_indicator->set_range(0, limit); -} - -bool gs_frame::has_alpha() -{ - return format().hasAlpha(); -} diff --git a/rpcs3qt-legacy/gs_frame.h b/rpcs3qt-legacy/gs_frame.h deleted file mode 100644 index 7ff47877a..000000000 --- a/rpcs3qt-legacy/gs_frame.h +++ /dev/null @@ -1,125 +0,0 @@ -#pragma once - -#include "shortcut_handler.h" -#include "progress_indicator.h" -#include "util/types.hpp" -#include "util/atomic.hpp" -#include "util/media_utils.h" -#include "Emu/RSX/GSFrameBase.h" - -#include -#include -#include - -#include -#include - -class gui_settings; -enum class video_renderer; - -class gs_frame : public QWindow, public GSFrameBase -{ - Q_OBJECT - -private: - // taskbar progress - std::unique_ptr m_progress_indicator; - - shortcut_handler* m_shortcut_handler = nullptr; - - QRect m_initial_geometry; - - std::shared_ptr m_gui_settings; - QTimer m_mousehide_timer; - - u64 m_frames = 0; - std::string m_window_title; - QWindow::Visibility m_last_visibility = Visibility::Windowed; - atomic_t m_is_closing = false; - atomic_t m_show_mouse = true; - bool m_disable_mouse = false; - bool m_disable_kb_hotkeys = false; - bool m_mouse_hide_and_lock = false; - bool m_show_mouse_in_fullscreen = false; - bool m_lock_mouse_in_fullscreen = true; - bool m_hide_mouse_after_idletime = false; - u32 m_hide_mouse_idletime = 2000; // ms - bool m_flip_showed_frame = false; - bool m_start_games_fullscreen = false; - bool m_ignore_stop_events = false; - - std::shared_ptr m_video_encoder{}; - -public: - explicit gs_frame(QScreen* screen, const QRect& geometry, const QIcon& appIcon, std::shared_ptr gui_settings, bool force_fullscreen); - ~gs_frame(); - - video_renderer renderer() const - { - return m_renderer; - }; - - void ignore_stop_events() - { - m_ignore_stop_events = true; - } - - draw_context_t make_context() override; - void set_current(draw_context_t context) override; - void delete_context(draw_context_t context) override; - void toggle_fullscreen() override; - - void update_shortcuts(); - - // taskbar progress - void progress_reset(bool reset_limit = false); - void progress_set_value(int value); - void progress_increment(int delta); - void progress_set_limit(int limit); - - /* - Returns true if the mouse is locked inside the game window. - Also conveniently updates the cursor visibility, because using it from a mouse handler indicates mouse emulation. - */ - bool get_mouse_lock_state(); - - bool can_consume_frame() const override; - 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; - -protected: - video_renderer m_renderer; - - void paintEvent(QPaintEvent* event) override; - void showEvent(QShowEvent* event) override; - - void close() override; - void reset() override; - - bool shown() override; - void hide() override; - void show() override; - void mouseDoubleClickEvent(QMouseEvent* ev) override; - - display_handle_t handle() const override; - - void flip(draw_context_t context, bool skip_frame = false) override; - int client_width() override; - int client_height() override; - f64 client_display_rate() override; - bool has_alpha() override; - - bool event(QEvent* ev) override; - -private: - void load_gui_settings(); - void hide_on_close(); - void toggle_recording(); - void toggle_mouselock(); - void update_cursor(); - void handle_cursor(QWindow::Visibility visibility, bool visibility_changed, bool active_changed, bool start_idle_timer); - -private Q_SLOTS: - void mouse_hide_timeout(); - void handle_shortcut(gui::shortcuts::shortcut shortcut_key, const QKeySequence& key_sequence); -}; diff --git a/rpcs3qt-legacy/gui_application.cpp b/rpcs3qt-legacy/gui_application.cpp deleted file mode 100644 index 43362f861..000000000 --- a/rpcs3qt-legacy/gui_application.cpp +++ /dev/null @@ -1,1477 +0,0 @@ -#include "stdafx.h" -#include "gui_application.h" - -#include "qt_utils.h" -#include "permissions.h" -#include "welcome_dialog.h" -#include "main_window.h" -#include "emu_settings.h" -#include "gui_settings.h" -#include "persistent_settings.h" -#include "gs_frame.h" -#include "gl_gs_frame.h" -#include "localized_emu.h" -#include "qt_camera_handler.h" -#include "qt_music_handler.h" -#include "rpcs3_version.h" -#include "display_sleep_control.h" - -#ifdef WITH_DISCORD_RPC -#include "_discord_utils.h" -#endif - -#include "Emu/Audio/audio_utils.h" -#include "rpcsx/fw/ps3/cellSysutil.h" -#include "Emu/Io/Null/null_camera_handler.h" -#include "Emu/Io/Null/null_music_handler.h" -#include "Emu/vfs_config.h" -#include "util/init_mutex.hpp" -#include "util/console.h" - -#include "Input/keyboard_pad_handler.h" -#include "Emu/Io/Null/NullPadHandler.h" -#include "Input/ds3_pad_handler.h" -#include "Input/ds4_pad_handler.h" -#include "Input/dualsense_pad_handler.h" -#include "Input/skateboard_pad_handler.h" -#include "Input/ps_move_handler.h" -#include "Input/virtual_pad_handler.h" -#ifdef _WIN32 -#include "Input/xinput_pad_handler.h" -#include "Input/mm_joystick_handler.h" -#endif - -#ifdef HAVE_SDL3 -#include "Input/sdl_pad_handler.h" -#endif - -#ifdef HAVE_LIBEVDEV -#include "Input/evdev_joystick_handler.h" -#endif - -#include "qt_video_source.h" -#include "trophy_notification_helper.h" -#include "save_data_dialog.h" -#include "msg_dialog_frame.h" -#include "osk_dialog_frame.h" -#include "recvmessage_dialog_frame.h" -#include "sendmessage_dialog_frame.h" -#include "stylesheets.h" -#include "progress_dialog.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include "Emu/RSX/Null/NullGSRender.h" -#include "Emu/RSX/GL/GLGSRender.h" - -#if defined(HAVE_VULKAN) -#include "Emu/RSX/VK/VKGSRender.h" -#endif - -#ifdef _WIN32 -#include -#include - -#include "Emu/Cell/lv2/sys_usbd.h" -#endif - -LOG_CHANNEL(gui_log, "GUI"); - -std::unique_ptr g_raw_mouse_handler; - -s32 gui_application::m_language_id = static_cast(CELL_SYSUTIL_LANG_ENGLISH_US); - -[[noreturn]] void report_fatal_error(std::string_view text, bool is_html = false, bool include_help_text = true); - -gui_application::gui_application(int& argc, char** argv) : QApplication(argc, argv) -{ - std::setlocale(LC_NUMERIC, "C"); // On linux Qt changes to system locale while initializing QCoreApplication -} - -gui_application::~gui_application() -{ -#ifdef WITH_DISCORD_RPC - discord::shutdown(); -#endif -#ifdef _WIN32 - unregister_device_notification(); -#endif -} - -bool gui_application::Init() -{ -#ifndef __APPLE__ - setWindowIcon(QIcon(":/rpcs3.ico")); -#endif - - if (!rpcs3::is_release_build() && !rpcs3::is_local_build()) - { - const std::string_view branch_name = rpcs3::get_full_branch(); - gui_log.warning("Experimental Build Warning! Build origin: %s", branch_name); - - QMessageBox msg; - msg.setWindowModality(Qt::WindowModal); - msg.setWindowTitle(tr("Experimental Build Warning")); - msg.setIcon(QMessageBox::Critical); - msg.setTextFormat(Qt::RichText); - msg.setStandardButtons(QMessageBox::Yes | QMessageBox::No); - msg.setDefaultButton(QMessageBox::No); - msg.setText(gui::utils::make_paragraph(tr( - "Please understand that this build is not an official RPCS3 release.\n" - "This build contains changes that may break games, or even damage your data.\n" - "We recommend to download and use the official build from the %0.\n" - "\n" - "Build origin: %1\n" - "Do you wish to use this build anyway?") - .arg(gui::utils::make_link(tr("RPCS3 website"), "https://rpcs3.net/download")) - .arg(Qt::convertFromPlainText(branch_name.data())))); - msg.layout()->setSizeConstraint(QLayout::SetFixedSize); - - if (msg.exec() == QMessageBox::No) - { - return false; - } - } - - m_emu_settings = std::make_shared(); - m_gui_settings = std::make_shared(); - m_persistent_settings = std::make_shared(); - - if (!m_emu_settings->Init()) - { - return false; - } - - if (m_gui_settings->GetValue(gui::m_attachCommandLine).toBool()) - { - utils::attach_console(utils::console_stream::std_err, true); - } - else - { - m_gui_settings->SetValue(gui::m_attachCommandLine, false); - } - - // The user might be set by cli arg. If not, set another user. - if (m_active_user.empty()) - { - // Get active user with standard user as fallback - m_active_user = m_persistent_settings->GetCurrentUser("00000001").toStdString(); - } - - // Force init the emulator - InitializeEmulator(m_active_user, m_show_gui); - - // Create the main window - if (m_show_gui) - { - m_main_window = new main_window(m_gui_settings, m_emu_settings, m_persistent_settings, nullptr); - - const auto codes = GetAvailableLanguageCodes(); - const auto language = m_gui_settings->GetValue(gui::loc_language).toString(); - const auto index = codes.indexOf(language); - - LoadLanguage(index < 0 ? QLocale(QLocale::English).bcp47Name() : ::at32(codes, index)); - } - - // Create callbacks from the emulator, which reference the handlers. - InitializeCallbacks(); - - // Create connects to propagate events throughout Gui. - InitializeConnects(); - - if (m_gui_settings->GetValue(gui::ib_show_welcome).toBool()) - { - welcome_dialog* welcome = new welcome_dialog(m_gui_settings, false); - - if (welcome->exec() == QDialog::Rejected) - { - // If the agreement on RPCS3's usage conditions was not accepted by the user, ask the main window to gracefully terminate - return false; - } - } - - // Check maxfiles - if (utils::get_maxfiles() < 4096) - { - QMessageBox::warning(nullptr, - tr("Warning"), - tr("The current limit of maximum file descriptors is too low.\n" - "Some games will crash.\n" - "\n" - "Please increase the limit before running RPCS3.")); - } - - if (m_main_window && !m_main_window->Init(m_with_cli_boot)) - { - return false; - } - -#ifdef WITH_DISCORD_RPC - // Discord Rich Presence Integration - if (m_gui_settings->GetValue(gui::m_richPresence).toBool()) - { - discord::initialize(); - } -#endif - - // Install native event filter -#ifdef _WIN32 // Currently only needed for raw mouse input on windows - installNativeEventFilter(&m_native_event_filter); - - if (m_main_window) - { - register_device_notification(m_main_window->winId()); - } -#endif - - return true; -} - -void gui_application::SwitchTranslator(QTranslator& translator, const QString& filename, const QString& language_code) -{ - // remove the old translator - removeTranslator(&translator); - - const QString lang_path = QLibraryInfo::path(QLibraryInfo::TranslationsPath) + QStringLiteral("/"); - const QString file_path = lang_path + filename; - - if (QFileInfo(file_path).isFile()) - { - // load the new translator - if (translator.load(file_path)) - { - installTranslator(&translator); - } - } - else if (QString default_code = QLocale(QLocale::English).bcp47Name(); language_code != default_code) - { - // show error, but ignore default case "en", since it is handled in source code - gui_log.error("No translation file found in: %s", file_path); - - // reset current language to default "en" - set_language_code(std::move(default_code)); - } -} - -void gui_application::LoadLanguage(const QString& language_code) -{ - if (m_language_code == language_code) - { - return; - } - - set_language_code(language_code); - - const QLocale locale = QLocale(language_code); - const QString locale_name = QLocale::languageToString(locale.language()); - - QLocale::setDefault(locale); - - // Idk if this is overruled by the QLocale default, so I'll change it here just to be sure. - // As per QT recommendations to avoid conflicts for POSIX functions - std::setlocale(LC_NUMERIC, "C"); - - SwitchTranslator(m_translator, QStringLiteral("rpcs3_%1.qm").arg(language_code), language_code); - - if (m_main_window) - { - const QString default_code = QLocale(QLocale::English).bcp47Name(); - QStringList language_codes = GetAvailableLanguageCodes(); - - if (!language_codes.contains(default_code)) - { - language_codes.prepend(default_code); - } - - m_main_window->RetranslateUI(language_codes, m_language_code); - } - - m_gui_settings->SetValue(gui::loc_language, m_language_code); - - gui_log.notice("Current language changed to %s (%s)", locale_name, language_code); -} - -QStringList gui_application::GetAvailableLanguageCodes() -{ - QStringList language_codes; - - const QString language_path = QLibraryInfo::path(QLibraryInfo::TranslationsPath); - - if (QFileInfo(language_path).isDir()) - { - const QDir dir(language_path); - const QStringList filenames = dir.entryList(QStringList("rpcs3_*.qm")); - - for (const QString& filename : filenames) - { - QString language_code = filename; // "rpcs3_en.qm" - language_code.truncate(language_code.lastIndexOf('.')); // "rpcs3_en" - language_code.remove(0, language_code.indexOf('_') + 1); // "en" - - if (language_codes.contains(language_code)) - { - gui_log.error("Found duplicate language '%s' (%s)", language_code, filename); - } - else - { - language_codes << language_code; - } - } - } - - return language_codes; -} - -void gui_application::set_language_code(QString language_code) -{ - m_language_code = language_code; - - // Transform language code to lowercase and use '-' - language_code = language_code.toLower().replace("_", "-"); - - // Try to find the CELL language ID for this language code - static const std::map language_ids = { - {"ja", CELL_SYSUTIL_LANG_JAPANESE}, - {"en", CELL_SYSUTIL_LANG_ENGLISH_US}, - {"en-us", CELL_SYSUTIL_LANG_ENGLISH_US}, - {"en-gb", CELL_SYSUTIL_LANG_ENGLISH_GB}, - {"fr", CELL_SYSUTIL_LANG_FRENCH}, - {"es", CELL_SYSUTIL_LANG_SPANISH}, - {"de", CELL_SYSUTIL_LANG_GERMAN}, - {"it", CELL_SYSUTIL_LANG_ITALIAN}, - {"nl", CELL_SYSUTIL_LANG_DUTCH}, - {"pt", CELL_SYSUTIL_LANG_PORTUGUESE_PT}, - {"pt-pt", CELL_SYSUTIL_LANG_PORTUGUESE_PT}, - {"pt-br", CELL_SYSUTIL_LANG_PORTUGUESE_BR}, - {"ru", CELL_SYSUTIL_LANG_RUSSIAN}, - {"ko", CELL_SYSUTIL_LANG_KOREAN}, - {"zh", CELL_SYSUTIL_LANG_CHINESE_T}, - {"zh-hant", CELL_SYSUTIL_LANG_CHINESE_T}, - {"zh-hans", CELL_SYSUTIL_LANG_CHINESE_S}, - {"fi", CELL_SYSUTIL_LANG_FINNISH}, - {"sv", CELL_SYSUTIL_LANG_SWEDISH}, - {"da", CELL_SYSUTIL_LANG_DANISH}, - {"no", CELL_SYSUTIL_LANG_NORWEGIAN}, - {"nn", CELL_SYSUTIL_LANG_NORWEGIAN}, - {"nb", CELL_SYSUTIL_LANG_NORWEGIAN}, - {"pl", CELL_SYSUTIL_LANG_POLISH}, - {"tr", CELL_SYSUTIL_LANG_TURKISH}, - }; - - // Check direct match first - const auto it = language_ids.find(language_code); - if (it != language_ids.cend()) - { - m_language_id = static_cast(it->second); - return; - } - - // Try to find closest match - for (const auto& [code, id] : language_ids) - { - if (language_code.startsWith(code)) - { - m_language_id = static_cast(id); - return; - } - } - - // Fallback to English (US) - m_language_id = static_cast(CELL_SYSUTIL_LANG_ENGLISH_US); -} - -s32 gui_application::get_language_id() -{ - return m_language_id; -} - -void gui_application::InitializeConnects() -{ - connect(&m_timer, &QTimer::timeout, this, &gui_application::UpdatePlaytime); - connect(this, &gui_application::OnEmulatorRun, this, &gui_application::StartPlaytime); - connect(this, &gui_application::OnEmulatorStop, this, &gui_application::StopPlaytime); - connect(this, &gui_application::OnEmulatorPause, this, &gui_application::StopPlaytime); - connect(this, &gui_application::OnEmulatorResume, this, &gui_application::StartPlaytime); - connect(this, &QGuiApplication::applicationStateChanged, this, &gui_application::OnAppStateChanged); - - if (m_main_window) - { - connect(m_main_window, &main_window::RequestLanguageChange, this, &gui_application::LoadLanguage); - connect(m_main_window, &main_window::RequestGlobalStylesheetChange, this, &gui_application::OnChangeStyleSheetRequest); - connect(m_main_window, &main_window::NotifyEmuSettingsChange, this, [this]() - { - OnEmuSettingsChange(); - }); - connect(m_main_window, &main_window::NotifyShortcutHandlers, this, &gui_application::OnShortcutChange); - - connect(this, &gui_application::OnEmulatorRun, m_main_window, &main_window::OnEmuRun); - connect(this, &gui_application::OnEmulatorStop, m_main_window, &main_window::OnEmuStop); - connect(this, &gui_application::OnEmulatorPause, m_main_window, &main_window::OnEmuPause); - connect(this, &gui_application::OnEmulatorResume, m_main_window, &main_window::OnEmuResume); - connect(this, &gui_application::OnEmulatorReady, m_main_window, &main_window::OnEmuReady); - connect(this, &gui_application::OnEnableDiscEject, m_main_window, &main_window::OnEnableDiscEject); - connect(this, &gui_application::OnEnableDiscInsert, m_main_window, &main_window::OnEnableDiscInsert); - - connect(QGuiApplication::styleHints(), &QStyleHints::colorSchemeChanged, this, [this]() - { - OnChangeStyleSheetRequest(); - }); - } - -#ifdef WITH_DISCORD_RPC - connect(this, &gui_application::OnEmulatorRun, [this](bool /*start_playtime*/) - { - // Discord Rich Presence Integration - if (m_gui_settings->GetValue(gui::m_richPresence).toBool()) - { - discord::update_presence(Emu.GetTitleID(), Emu.GetTitle()); - } - }); - connect(this, &gui_application::OnEmulatorStop, [this]() - { - // Discord Rich Presence Integration - if (m_gui_settings->GetValue(gui::m_richPresence).toBool()) - { - discord::update_presence(m_gui_settings->GetValue(gui::m_discordState).toString().toStdString()); - } - }); -#endif - - qRegisterMetaType>("std::function"); - connect(this, &gui_application::RequestCallFromMainThread, this, &gui_application::CallFromMainThread); -} - -std::unique_ptr gui_application::get_gs_frame() -{ - // Load AppIcon - const QIcon app_icon = m_main_window ? m_main_window->GetAppIcon() : gui::utils::get_app_icon_from_path(Emu.GetBoot(), Emu.GetTitleID()); - - if (m_game_window) - { - // Check if the continuous mode is enabled. We reset the mode after each use in order to ensure that it is only used when explicitly needed. - const bool continuous_mode_enabled = Emu.ContinuousModeEnabled(true); - - // Make sure we run the same config - const bool is_same_renderer = m_game_window->renderer() == g_cfg.video.renderer; - - if (is_same_renderer && (Emu.IsChildProcess() || continuous_mode_enabled)) - { - gui_log.notice("gui_application: Re-using old game window (IsChildProcess=%d, ContinuousModeEnabled=%d)", Emu.IsChildProcess(), continuous_mode_enabled); - - if (!app_icon.isNull()) - { - m_game_window->setIcon(app_icon); - } - return std::unique_ptr(m_game_window); - } - - // Clean-up old game window. This should only happen if the renderer changed or there was an unexpected error during boot. - Emu.GetCallbacks().close_gs_frame(); - } - - gui_log.notice("gui_application: Creating new game window"); - - extern const std::unordered_map, value_hash> g_video_out_resolution_map; - - auto [w, h] = ::at32(g_video_out_resolution_map, g_cfg.video.resolution); - - const bool resize_game_window = m_gui_settings->GetValue(gui::gs_resize).toBool(); - - if (resize_game_window) - { - if (m_gui_settings->GetValue(gui::gs_resize_manual).toBool()) - { - w = m_gui_settings->GetValue(gui::gs_width).toInt(); - h = m_gui_settings->GetValue(gui::gs_height).toInt(); - } - else - { - const qreal device_pixel_ratio = devicePixelRatio(); - w /= device_pixel_ratio; - h /= device_pixel_ratio; - } - } - - QScreen* screen = nullptr; - QRect base_geometry{}; - - // Use screen index set by CLI argument - int screen_index = m_game_screen_index; - const int last_screen_index = m_gui_settings->GetValue(gui::gs_screen).toInt(); - - // Use last used screen if no CLI index was set - if (screen_index < 0) - { - screen_index = last_screen_index; - } - - // Try to find the specified screen - if (screen_index >= 0) - { - const QList available_screens = screens(); - - if (screen_index < available_screens.count()) - { - screen = ::at32(available_screens, screen_index); - - if (screen) - { - base_geometry = screen->geometry(); - } - } - - if (!screen) - { - gui_log.error("The selected game screen with index %d is not available (available screens: %d)", screen_index, available_screens.count()); - } - } - - // Fallback to the screen of the main window. Use the primary screen as last resort. - if (!screen) - { - screen = m_main_window ? m_main_window->screen() : primaryScreen(); - base_geometry = m_main_window ? m_main_window->frameGeometry() : primaryScreen()->geometry(); - } - - // Use saved geometry if possible. Ignore this if the last used screen is different than the requested screen. - QRect frame_geometry = screen_index != last_screen_index ? QRect{} : m_gui_settings->GetValue(gui::gs_geometry).value(); - - if (frame_geometry.isNull() || frame_geometry.isEmpty()) - { - // Center above main window or inside screen if the saved geometry is invalid - frame_geometry = gui::utils::create_centered_window_geometry(screen, base_geometry, w, h); - } - else if (resize_game_window) - { - // Apply size override to our saved geometry if needed - frame_geometry.setSize(QSize(w, h)); - } - - gs_frame* frame = nullptr; - - switch (g_cfg.video.renderer.get()) - { - case video_renderer::opengl: - { - frame = new gl_gs_frame(screen, frame_geometry, app_icon, m_gui_settings, m_start_games_fullscreen); - break; - } - case video_renderer::null: - case video_renderer::vulkan: - { - frame = new gs_frame(screen, frame_geometry, app_icon, m_gui_settings, m_start_games_fullscreen); - break; - } - } - - m_game_window = frame; - ensure(m_game_window); - -#ifdef _WIN32 - if (!m_show_gui) - { - register_device_notification(m_game_window->winId()); - } -#endif - - connect(m_game_window, &gs_frame::destroyed, this, [this]() - { - gui_log.notice("gui_application: Deleting old game window"); - m_game_window = nullptr; - -#ifdef _WIN32 - if (!m_show_gui) - { - unregister_device_notification(); - } -#endif - }); - - return std::unique_ptr(frame); -} - -/** RPCS3 emulator has functions it desires to call from the GUI at times. Initialize them in here. */ -void gui_application::InitializeCallbacks() -{ - EmuCallbacks callbacks = CreateCallbacks(); - - callbacks.try_to_quit = [this](bool force_quit, std::function on_exit) -> bool - { - // Close rpcs3 if closed in no-gui mode - if (force_quit || !m_main_window) - { - if (on_exit) - { - on_exit(); - } - - if (m_main_window) - { - // Close main window in order to save its window state - m_main_window->close(); - } - quit(); - return true; - } - - return false; - }; - callbacks.call_from_main_thread = [this](std::function func, atomic_t* wake_up) - { - RequestCallFromMainThread(std::move(func), wake_up); - }; - - callbacks.init_gs_render = [](utils::serial* ar) - { - switch (g_cfg.video.renderer.get()) - { - case video_renderer::null: - { - g_fxo->init>(ar); - break; - } - case video_renderer::opengl: - { -#if not defined(__APPLE__) - g_fxo->init>(ar); -#endif - break; - } - case video_renderer::vulkan: - { -#if defined(HAVE_VULKAN) - g_fxo->init>(ar); -#endif - break; - } - } - }; - - callbacks.get_camera_handler = []() -> std::shared_ptr - { - switch (g_cfg.io.camera.get()) - { - case camera_handler::null: - case camera_handler::fake: - { - return std::make_shared(); - } - case camera_handler::qt: - { - return std::make_shared(); - } - } - return nullptr; - }; - - callbacks.get_music_handler = - []() -> std::shared_ptr - { - switch (g_cfg.audio.music.get()) - { - case music_handler::null: - { - return std::make_shared(); - } - case music_handler::qt: - { - return std::make_shared(); - } - } - return nullptr; - }; - - callbacks.create_pad_handler = [](pad_handler type, void* thread, void* window) -> std::shared_ptr - { - switch (type) - { - case pad_handler::null: - break; - case pad_handler::keyboard: - { - auto result = std::make_shared(); - result->moveToThread(static_cast(thread)); - result->SetTargetWindow(static_cast(window)); - return result; - } - case pad_handler::ds3: - return std::make_shared(); - case pad_handler::ds4: - return std::make_shared(); - case pad_handler::dualsense: - return std::make_shared(); - case pad_handler::skateboard: - return std::make_shared(); - case pad_handler::move: - return std::make_shared(); -#ifdef _WIN32 - case pad_handler::xinput: - return std::make_shared(); - case pad_handler::mm: - return std::make_shared(); -#endif -#ifdef HAVE_SDL3 - case pad_handler::sdl: - return std::make_shared(); -#endif -#ifdef HAVE_LIBEVDEV - case pad_handler::evdev: - return std::make_shared(); -#endif - case pad_handler::virtual_pad: - return std::make_shared(); - } - - return std::make_shared(); - }; - - callbacks.close_gs_frame = [this]() - { - if (m_game_window) - { - gui_log.warning("gui_application: Closing old game window"); - m_game_window->ignore_stop_events(); - delete m_game_window; - m_game_window = nullptr; - } - }; - callbacks.get_gs_frame = [this]() -> std::unique_ptr - { - return get_gs_frame(); - }; - callbacks.get_msg_dialog = [this]() -> std::shared_ptr - { - return m_show_gui ? std::make_shared() : nullptr; - }; - callbacks.get_osk_dialog = [this]() -> std::shared_ptr - { - return m_show_gui ? std::make_shared() : nullptr; - }; - callbacks.get_save_dialog = []() -> std::unique_ptr - { - return std::make_unique(); - }; - callbacks.get_sendmessage_dialog = [this]() -> std::shared_ptr - { - return std::make_shared(); - }; - callbacks.get_recvmessage_dialog = [this]() -> std::shared_ptr - { - return std::make_shared(); - }; - callbacks.get_trophy_notification_dialog = [this]() -> std::unique_ptr - { - return std::make_unique(m_game_window); - }; - - callbacks.on_run = [this](bool start_playtime) - { - OnEmulatorRun(start_playtime); - }; - callbacks.on_pause = [this]() - { - OnEmulatorPause(); - }; - callbacks.on_resume = [this]() - { - OnEmulatorResume(true); - }; - callbacks.on_stop = [this]() - { - OnEmulatorStop(); - }; - callbacks.on_ready = [this]() - { - OnEmulatorReady(); - }; - - callbacks.enable_disc_eject = [this](bool enabled) - { - Emu.CallFromMainThread([this, enabled]() - { - OnEnableDiscEject(enabled); - }); - }; - callbacks.enable_disc_insert = [this](bool enabled) - { - Emu.CallFromMainThread([this, enabled]() - { - OnEnableDiscInsert(enabled); - }); - }; - - callbacks.on_missing_fw = [this]() - { - if (m_main_window) - { - m_main_window->OnMissingFw(); - } - }; - - callbacks.handle_taskbar_progress = [this](s32 type, s32 value) - { - if (m_game_window) - { - switch (type) - { - case 0: m_game_window->progress_reset(value); break; - case 1: m_game_window->progress_increment(value); break; - case 2: m_game_window->progress_set_limit(value); break; - case 3: m_game_window->progress_set_value(value); break; - default: gui_log.fatal("Unknown type in handle_taskbar_progress(type=%d, value=%d)", type, value); break; - } - } - }; - - callbacks.get_localized_string = [](localized_string_id id, const char* args) -> std::string - { - return localized_emu::get_string(id, args); - }; - - callbacks.get_localized_u32string = [](localized_string_id id, const char* args) -> std::u32string - { - return localized_emu::get_u32string(id, args); - }; - - callbacks.get_localized_setting = [this](const cfg::_base* node, u32 enum_index) -> std::string - { - ensure(!!m_emu_settings); - return m_emu_settings->GetLocalizedSetting(node, enum_index); - }; - - callbacks.play_sound = [this](const std::string& path) - { - Emu.CallFromMainThread([this, path]() - { - if (fs::is_file(path)) - { - // Allow to play 3 sound effects at the same time - while (m_sound_effects.size() >= 3) - { - m_sound_effects.pop_front(); - } - - // Create a new sound effect. Re-using the same object seems to be broken for some users starting with Qt 6.6.3. - std::unique_ptr sound_effect = std::make_unique(); - sound_effect->setSource(QUrl::fromLocalFile(QString::fromStdString(path))); - sound_effect->setVolume(audio::get_volume()); - sound_effect->play(); - - m_sound_effects.push_back(std::move(sound_effect)); - } - }); - }; - - if (m_show_gui) // If this is false, we already have a fallback in the main_application. - { - callbacks.on_install_pkgs = [this](const std::vector& pkgs) - { - ensure(m_main_window); - ensure(!pkgs.empty()); - QStringList pkg_list; - for (const std::string& pkg : pkgs) - { - pkg_list << QString::fromStdString(pkg); - } - return m_main_window->InstallPackages(pkg_list, true); - }; - } - - callbacks.on_emulation_stop_no_response = [this](std::shared_ptr> closed_successfully, int seconds_waiting_already) - { - const std::string terminate_message = tr("Stopping emulator took too long." - "\nSome thread has probably deadlocked. Aborting.") - .toStdString(); - - if (!closed_successfully) - { - report_fatal_error(terminate_message); - } - - Emu.CallFromMainThread([this, closed_successfully, seconds_waiting_already, terminate_message] - { - const auto seconds = std::make_shared(seconds_waiting_already); - - QMessageBox* mb = new QMessageBox(); - mb->setWindowTitle(tr("PS3 Game/Application Is Unresponsive")); - mb->setIcon(QMessageBox::Critical); - mb->setStandardButtons(QMessageBox::Yes | QMessageBox::No); - mb->setDefaultButton(QMessageBox::No); - mb->button(QMessageBox::Yes)->setText(tr("Terminate RPCS3")); - mb->button(QMessageBox::No)->setText(tr("Keep Waiting")); - - QString text_base = tr("Waiting for %0 second(s) already to stop emulation without success." - "\nKeep waiting or terminate RPCS3 unsafely at your own risk?"); - - mb->setText(text_base.arg(10)); - mb->layout()->setSizeConstraint(QLayout::SetFixedSize); - mb->setAttribute(Qt::WA_DeleteOnClose); - - QTimer* update_timer = new QTimer(mb); - - connect(update_timer, &QTimer::timeout, [mb, seconds, text_base, closed_successfully]() - { - *seconds += 1; - mb->setText(text_base.arg(*seconds)); - - if (*closed_successfully) - { - mb->reject(); - } - }); - - connect(mb, &QDialog::accepted, mb, [closed_successfully, terminate_message] - { - if (!*closed_successfully) - { - report_fatal_error(terminate_message); - } - }); - - mb->open(); - update_timer->start(1000); - }); - }; - - callbacks.on_save_state_progress = [this](std::shared_ptr> closed_successfully, stx::shared_ptr ar_ptr, stx::atomic_ptr* code_location, std::shared_ptr init_mtx) - { - Emu.CallFromMainThread([this, closed_successfully, ar_ptr, code_location, init_mtx] - { - const auto half_seconds = std::make_shared(1); - - progress_dialog* pdlg = new progress_dialog(tr("Creating Save-State / Do Not Close RPCS3"), tr("Please wait..."), tr("Hide Progress"), 0, 100, true, m_main_window); - pdlg->setAutoReset(false); - pdlg->setAutoClose(true); - pdlg->show(); - - QString text_base = tr("%0 written, %1 second(s) passed%2"); - - pdlg->setLabelText(text_base.arg("0B").arg(1).arg("")); - pdlg->setAttribute(Qt::WA_DeleteOnClose); - - QTimer* update_timer = new QTimer(pdlg); - - connect(update_timer, &QTimer::timeout, [pdlg, ar_ptr, half_seconds, text_base, closed_successfully, code_location, init_mtx, old_written = usz{0}, repeat_count = u32{0}]() mutable - { - std::string verbose_message; - usz bytes_written = 0; - - while (true) - { - auto mtx = static_cast(init_mtx.get()); - auto init = mtx->access(); - - if (!init) - { - // Try to wait for the abort process to complete - auto fake_reset = mtx->reset(); - if (!fake_reset) - { - // End of emulation termination - pdlg->reject(); - return; - } - - fake_reset.set_init(); - - // Now ar_ptr contains a null file descriptor - continue; - } - - if (auto str_ptr = code_location->load()) - { - verbose_message = "\n" + *str_ptr; - } - - bytes_written = ar_ptr->is_writing() ? std::max(ar_ptr->get_size(), old_written) : old_written; - break; - } - - *half_seconds += 1; - - if (old_written == bytes_written) - { - if (repeat_count == 60) - { - if (verbose_message.empty()) - { - verbose_message += "\n"; - } - else - { - verbose_message += ". "; - } - - verbose_message += tr("If Stuck, Report To Developers").toStdString(); - } - else - { - repeat_count++; - } - } - else - { - repeat_count = 0; - } - - old_written = bytes_written; - - pdlg->setLabelText(text_base.arg(gui::utils::format_byte_size(bytes_written)).arg(*half_seconds / 2).arg(QString::fromStdString(verbose_message))); - - // 300MB -> 50%, 600MB -> 75%, 1200MB -> 87.5% etc - const int percent = std::clamp(static_cast(100. - 100. / std::pow(2., std::fmax(0.01, bytes_written * 1. / (300 * 1024 * 1024)))), 2, 100); - - // Add a third of the remaining progress when the keyword is found - pdlg->setValue(verbose_message.find("Finalizing") != umax ? 100 - ((100 - percent) * 2 / 3) : percent); - - if (*closed_successfully) - { - pdlg->reject(); - } - }); - - pdlg->open(); - update_timer->start(500); - }); - }; - - callbacks.add_breakpoint = [this](u32 addr) - { - Emu.BlockingCallFromMainThread([this, addr]() - { - m_main_window->OnAddBreakpoint(addr); - }); - }; - - callbacks.display_sleep_control_supported = []() - { - return display_sleep_control_supported(); - }; - callbacks.enable_display_sleep = [](bool enabled) - { - enable_display_sleep(enabled); - }; - - callbacks.check_microphone_permissions = []() - { - Emu.BlockingCallFromMainThread([]() - { - gui::utils::check_microphone_permission(); - }); - }; - - callbacks.make_video_source = []() - { - return std::make_unique(); - }; - - Emu.SetCallbacks(std::move(callbacks)); -} - -void gui_application::StartPlaytime(bool start_playtime = true) -{ - if (!start_playtime) - { - return; - } - - const QString serial = QString::fromStdString(Emu.GetTitleID()); - if (serial.isEmpty()) - { - return; - } - - m_persistent_settings->SetLastPlayed(serial, QDateTime::currentDateTime().toString(gui::persistent::last_played_date_format), true); - m_timer_playtime.start(); - m_timer.start(10000); // Update every 10 seconds in case the emulation crashes -} - -void gui_application::UpdatePlaytime() -{ - if (!m_timer_playtime.isValid()) - { - m_timer.stop(); - return; - } - - const QString serial = QString::fromStdString(Emu.GetTitleID()); - if (serial.isEmpty()) - { - m_timer_playtime.invalidate(); - m_timer.stop(); - return; - } - - m_persistent_settings->AddPlaytime(serial, m_timer_playtime.restart(), false); - m_persistent_settings->SetLastPlayed(serial, QDateTime::currentDateTime().toString(gui::persistent::last_played_date_format), true); -} - -void gui_application::StopPlaytime() -{ - m_timer.stop(); - - if (!m_timer_playtime.isValid()) - return; - - const QString serial = QString::fromStdString(Emu.GetTitleID()); - if (serial.isEmpty()) - { - m_timer_playtime.invalidate(); - return; - } - - m_persistent_settings->AddPlaytime(serial, m_timer_playtime.restart(), false); - m_persistent_settings->SetLastPlayed(serial, QDateTime::currentDateTime().toString(gui::persistent::last_played_date_format), true); - m_timer_playtime.invalidate(); -} - -/* - * Handle a request to change the stylesheet based on the current entry in the settings. - */ -void gui_application::OnChangeStyleSheetRequest() -{ - // skip stylesheets on first repaint if a style was set from command line - if (m_use_cli_style && gui::stylesheet.isEmpty()) - { - gui::stylesheet = styleSheet().isEmpty() ? "/* style set by command line arg */" : styleSheet(); - - if (m_main_window) - { - m_main_window->RepaintGui(); - } - - return; - } - - // Remove old fonts - QFontDatabase::removeAllApplicationFonts(); - - const QString stylesheet_name = m_gui_settings->GetValue(gui::m_currentStylesheet).toString(); - - // Determine default style - if (m_default_style.isEmpty()) - { -#ifdef _WIN32 - // On windows, the custom stylesheets don't seem to work properly unless we use the windowsvista style as default - if (QStyleFactory::keys().contains("windowsvista")) - { - m_default_style = "windowsvista"; - gui_log.notice("Using '%s' as default style", m_default_style); - } -#endif - - // Use the initial style as default style - if (const QStyle* style = m_default_style.isEmpty() ? QApplication::style() : nullptr) - { - m_default_style = style->name(); - gui_log.notice("Determined '%s' as default style", m_default_style); - } - - // Fallback to the first style, which is supposed to be the default style according to the Qt docs. - if (m_default_style.isEmpty()) - { - if (const QStringList styles = QStyleFactory::keys(); !styles.empty()) - { - m_default_style = styles.front(); - gui_log.notice("Determined '%s' as default style (first style available)", m_default_style); - } - } - } - - // Reset style to default before doing anything else, or we will get unexpected effects in custom stylesheets. - if (QStyle* style = QStyleFactory::create(m_default_style)) - { - setStyle(style); - } - - const auto match_native_style = [&stylesheet_name]() -> QString - { - // Search for "native (