diff --git a/.github/linux-appimage-qt.sh b/.github/linux-appimage-qt.sh
deleted file mode 100755
index 776eed61e..000000000
--- a/.github/linux-appimage-qt.sh
+++ /dev/null
@@ -1,33 +0,0 @@
-# SPDX-FileCopyrightText: 2024 shadPS4 Emulator Project
-# SPDX-License-Identifier: GPL-2.0-or-later
-
-#!/bin/bash
-
-if [[ -z $GITHUB_WORKSPACE ]]; then
- GITHUB_WORKSPACE="${PWD%/*}"
-fi
-
-export Qt6_DIR="/usr/lib/qt6"
-export PATH="$Qt6_DIR/bin:$PATH"
-export EXTRA_QT_PLUGINS="waylandcompositor"
-export EXTRA_PLATFORM_PLUGINS="libqwayland-egl.so;libqwayland-generic.so"
-
-# Prepare Tools for building the AppImage
-wget -q https://github.com/linuxdeploy/linuxdeploy/releases/download/continuous/linuxdeploy-x86_64.AppImage
-wget -q https://github.com/linuxdeploy/linuxdeploy-plugin-qt/releases/download/continuous/linuxdeploy-plugin-qt-x86_64.AppImage
-wget -q https://github.com/linuxdeploy/linuxdeploy-plugin-checkrt/releases/download/continuous/linuxdeploy-plugin-checkrt-x86_64.sh
-
-chmod a+x linuxdeploy-x86_64.AppImage
-chmod a+x linuxdeploy-plugin-qt-x86_64.AppImage
-chmod a+x linuxdeploy-plugin-checkrt-x86_64.sh
-
-# Build AppImage
-./linuxdeploy-x86_64.AppImage --appdir AppDir
-./linuxdeploy-plugin-checkrt-x86_64.sh --appdir AppDir
-
-cp -a "$GITHUB_WORKSPACE/build/translations" AppDir/usr/bin
-
-./linuxdeploy-x86_64.AppImage --appdir AppDir -d "$GITHUB_WORKSPACE"/dist/net.shadps4.shadPS4.desktop -e "$GITHUB_WORKSPACE"/build/shadps4 -i "$GITHUB_WORKSPACE"/src/images/net.shadps4.shadPS4.svg --plugin qt
-rm AppDir/usr/plugins/multimedia/libgstreamermediaplugin.so
-./linuxdeploy-x86_64.AppImage --appdir AppDir --output appimage
-mv shadPS4-x86_64.AppImage Shadps4-qt.AppImage
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 5e9fbd342..d1fab6354 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -95,64 +95,6 @@ jobs:
name: shadps4-win64-sdl-${{ needs.get-info.outputs.date }}-${{ needs.get-info.outputs.shorthash }}
path: ${{github.workspace}}/build/shadPS4.exe
- windows-qt:
- runs-on: windows-2025
- needs: get-info
- steps:
- - uses: actions/checkout@v5
- with:
- submodules: recursive
-
- - name: Setup Qt
- uses: jurplel/install-qt-action@v4
- with:
- version: 6.9.3
- host: windows
- target: desktop
- arch: win64_msvc2022_64
- archives: qtbase qttools
- modules: qtmultimedia
-
- - name: Cache CMake Configuration
- uses: actions/cache@v4
- env:
- cache-name: ${{ runner.os }}-qt-ninja-cache-cmake-configuration
- with:
- path: |
- ${{github.workspace}}/build
- key: ${{ env.cache-name }}-${{ hashFiles('**/CMakeLists.txt', 'cmake/**') }}
- restore-keys: |
- ${{ env.cache-name }}-
-
- - name: Cache CMake Build
- uses: hendrikmuhs/ccache-action@v1.2.19
- env:
- cache-name: ${{ runner.os }}-qt-cache-cmake-build
- with:
- append-timestamp: false
- key: ${{ env.cache-name }}-${{ hashFiles('**/CMakeLists.txt', 'cmake/**') }}
-
- - name: Configure CMake
- run: cmake --fresh -G Ninja -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DENABLE_QT_GUI=ON -DENABLE_UPDATER=ON -DCMAKE_INTERPROCEDURAL_OPTIMIZATION_RELEASE=ON -DCMAKE_C_COMPILER=clang-cl -DCMAKE_CXX_COMPILER=clang-cl -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache
-
- - name: Build
- run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} --parallel $env:NUMBER_OF_PROCESSORS
-
- - name: Deploy and Package
- run: |
- mkdir upload
- mkdir upload/qtplugins
- move build/shadPS4.exe upload
- cp dist/qt.conf upload/qt.conf
- windeployqt --plugindir upload/qtplugins --no-compiler-runtime --no-system-d3d-compiler --no-system-dxc-compiler --dir upload upload/shadPS4.exe
- Compress-Archive -Path upload/* -DestinationPath shadps4-win64-qt-${{ needs.get-info.outputs.date }}-${{ needs.get-info.outputs.shorthash }}.zip
-
- - name: Upload Windows Qt artifact
- uses: actions/upload-artifact@v4
- with:
- name: shadps4-win64-qt-${{ needs.get-info.outputs.date }}-${{ needs.get-info.outputs.shorthash }}
- path: upload/
-
macos-sdl:
runs-on: macos-15
needs: get-info
@@ -204,67 +146,6 @@ jobs:
name: shadps4-macos-sdl-${{ needs.get-info.outputs.date }}-${{ needs.get-info.outputs.shorthash }}
path: upload/
- macos-qt:
- runs-on: macos-15
- needs: get-info
- steps:
- - uses: actions/checkout@v5
- with:
- submodules: recursive
-
- - name: Setup latest Xcode
- uses: maxim-lobanov/setup-xcode@v1
- with:
- xcode-version: latest
-
- - name: Setup Qt
- uses: jurplel/install-qt-action@v4
- with:
- version: 6.9.3
- host: mac
- target: desktop
- arch: clang_64
- archives: qtbase qttools
- modules: qtmultimedia
-
- - name: Cache CMake Configuration
- uses: actions/cache@v4
- env:
- cache-name: ${{ runner.os }}-qt-cache-cmake-configuration
- with:
- path: |
- ${{github.workspace}}/build
- key: ${{ env.cache-name }}-${{ hashFiles('**/CMakeLists.txt', 'cmake/**') }}
- restore-keys: |
- ${{ env.cache-name }}-
-
- - name: Cache CMake Build
- uses: hendrikmuhs/ccache-action@v1.2.19
- env:
- cache-name: ${{runner.os}}-qt-cache-cmake-build
- with:
- append-timestamp: false
- create-symlink: true
- key: ${{env.cache-name}}-${{ hashFiles('**/CMakeLists.txt', 'cmake/**') }}
- variant: sccache
-
- - name: Configure CMake
- run: cmake --fresh -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DCMAKE_OSX_ARCHITECTURES=x86_64 -DENABLE_QT_GUI=ON -DENABLE_UPDATER=ON -DCMAKE_INTERPROCEDURAL_OPTIMIZATION_RELEASE=ON -DCMAKE_C_COMPILER_LAUNCHER=sccache -DCMAKE_CXX_COMPILER_LAUNCHER=sccache
-
- - name: Build
- run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} --parallel $(sysctl -n hw.ncpu)
-
- - name: Package and Upload macOS Qt artifact
- run: |
- mkdir upload
- mv ${{github.workspace}}/build/shadps4.app upload
- macdeployqt upload/shadps4.app
- tar cf shadps4-macos-qt.tar.gz -C upload .
- - uses: actions/upload-artifact@v4
- with:
- name: shadps4-macos-qt-${{ needs.get-info.outputs.date }}-${{ needs.get-info.outputs.shorthash }}
- path: shadps4-macos-qt.tar.gz
-
linux-sdl:
runs-on: ubuntu-24.04
needs: get-info
@@ -326,58 +207,6 @@ jobs:
name: shadps4-linux-sdl-${{ needs.get-info.outputs.date }}-${{ needs.get-info.outputs.shorthash }}
path: Shadps4-sdl.AppImage
- linux-qt:
- runs-on: ubuntu-24.04
- needs: get-info
- steps:
- - uses: actions/checkout@v5
- with:
- submodules: recursive
-
- - name: Add LLVM repository
- run: |
- wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | sudo apt-key add -
- sudo add-apt-repository 'deb http://apt.llvm.org/noble/ llvm-toolchain-noble-19 main'
-
- - name: Install dependencies
- run: sudo apt-get update && sudo apt install -y libx11-dev libxext-dev libwayland-dev libdecor-0-dev libxkbcommon-dev libglfw3-dev libgles2-mesa-dev libfuse2 clang-19 mold build-essential qt6-base-dev qt6-tools-dev qt6-multimedia-dev libasound2-dev libpulse-dev libopenal-dev libudev-dev
-
- - name: Cache CMake Configuration
- uses: actions/cache@v4
- env:
- cache-name: ${{ runner.os }}-qt-cache-cmake-configuration
- with:
- path: |
- ${{github.workspace}}/build
- key: ${{ env.cache-name }}-${{ hashFiles('**/CMakeLists.txt', 'cmake/**') }}
- restore-keys: |
- ${{ env.cache-name }}-
-
- - name: Cache CMake Build
- uses: hendrikmuhs/ccache-action@v1.2.19
- env:
- cache-name: ${{ runner.os }}-qt-cache-cmake-build
- with:
- append-timestamp: false
- key: ${{ env.cache-name }}-${{ hashFiles('**/CMakeLists.txt', 'cmake/**') }}
-
- - name: Configure CMake
- run: cmake --fresh -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DCMAKE_INTERPROCEDURAL_OPTIMIZATION_RELEASE=ON -DCMAKE_C_COMPILER=clang-19 -DCMAKE_CXX_COMPILER=clang++-19 -DCMAKE_EXE_LINKER_FLAGS="-fuse-ld=mold" -DCMAKE_SHARED_LINKER_FLAGS="-fuse-ld=mold" -DENABLE_QT_GUI=ON -DENABLE_UPDATER=ON -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache
-
- - name: Build
- run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} --parallel $(nproc)
-
- - name: Run AppImage packaging script
- run: ./.github/linux-appimage-qt.sh
-
- - name: Package and Upload Linux Qt artifact
- run: |
- tar cf shadps4-linux-qt.tar.gz -C ${{github.workspace}}/build shadps4
- - uses: actions/upload-artifact@v4
- with:
- name: shadps4-linux-qt-${{ needs.get-info.outputs.date }}-${{ needs.get-info.outputs.shorthash }}
- path: Shadps4-qt.AppImage
-
linux-sdl-gcc:
runs-on: ubuntu-24.04
needs: get-info
@@ -414,45 +243,9 @@ jobs:
- name: Build
run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} --parallel $(nproc)
- linux-qt-gcc:
- runs-on: ubuntu-24.04
- needs: get-info
- steps:
- - uses: actions/checkout@v5
- with:
- submodules: recursive
-
- - name: Install dependencies
- run: sudo apt-get update && sudo apt install -y libx11-dev libxext-dev libwayland-dev libdecor-0-dev libxkbcommon-dev libglfw3-dev libgles2-mesa-dev libfuse2 gcc-14 mold build-essential qt6-base-dev qt6-tools-dev qt6-multimedia-dev libasound2-dev libpulse-dev libopenal-dev libudev-dev
-
- - name: Cache CMake Configuration
- uses: actions/cache@v4
- env:
- cache-name: ${{ runner.os }}-qt-gcc-cache-cmake-configuration
- with:
- path: |
- ${{github.workspace}}/build
- key: ${{ env.cache-name }}-${{ hashFiles('**/CMakeLists.txt', 'cmake/**') }}
- restore-keys: |
- ${{ env.cache-name }}-
-
- - name: Cache CMake Build
- uses: hendrikmuhs/ccache-action@v1.2.19
- env:
- cache-name: ${{ runner.os }}-qt-gcc-cache-cmake-build
- with:
- append-timestamp: false
- key: ${{ env.cache-name }}-${{ hashFiles('**/CMakeLists.txt', 'cmake/**') }}
-
- - name: Configure CMake
- run: cmake --fresh -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DCMAKE_INTERPROCEDURAL_OPTIMIZATION_RELEASE=ON -DCMAKE_C_COMPILER=gcc-14 -DCMAKE_CXX_COMPILER=g++-14 -DCMAKE_EXE_LINKER_FLAGS="-fuse-ld=mold" -DCMAKE_SHARED_LINKER_FLAGS="-fuse-ld=mold" -DENABLE_QT_GUI=ON -DENABLE_UPDATER=ON -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache
-
- - name: Build
- run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} --parallel $(nproc)
-
pre-release:
if: github.ref == 'refs/heads/main' && github.repository == 'shadps4-emu/shadPS4' && github.event_name == 'push'
- needs: [get-info, windows-sdl, windows-qt, macos-sdl, macos-qt, linux-sdl, linux-qt]
+ needs: [get-info, windows-sdl, macos-sdl, linux-sdl]
runs-on: ubuntu-latest
steps:
- name: Download all artifacts
diff --git a/.gitmodules b/.gitmodules
index 0deb96309..38aed89a0 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -110,3 +110,6 @@
path = externals/MoltenVK
url = https://github.com/KhronosGroup/MoltenVK.git
shallow = true
+[submodule "externals/json"]
+ path = externals/json
+ url = https://github.com/nlohmann/json.git
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 5d88a9aa4..14ad3e3fc 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -31,7 +31,6 @@ if(UNIX AND NOT APPLE)
endif()
endif()
-option(ENABLE_QT_GUI "Enable the Qt GUI. If not selected then the emulator uses a minimal SDL-based UI instead" OFF)
option(ENABLE_DISCORD_RPC "Enable the Discord RPC integration" ON)
option(ENABLE_UPDATER "Enables the options to updater" ON)
@@ -220,10 +219,6 @@ if(NOT (GIT_REMOTE_URL_LOWER MATCHES "shadps4-emu/shadps4" AND (GIT_BRANCH STREQ
set(ENABLE_UPDATER OFF)
endif()
-if(WIN32 AND ENABLE_QT_GUI AND NOT CMAKE_PREFIX_PATH)
- include("${CMAKE_CURRENT_SOURCE_DIR}/cmake/DetectQtInstallation.cmake")
-endif ()
-
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake")
find_package(Boost 1.84.0 CONFIG)
find_package(FFmpeg 5.1.2 MODULE)
@@ -262,30 +257,6 @@ endif()
add_subdirectory(externals)
include_directories(src)
-if(ENABLE_QT_GUI)
- find_package(Qt6 REQUIRED COMPONENTS Widgets Concurrent LinguistTools Network Multimedia)
- qt_standard_project_setup()
- set(CMAKE_AUTORCC ON)
- set(CMAKE_AUTOMOC ON)
- set(CMAKE_AUTOUIC ON)
-
- set(QT_TRANSLATIONS "${PROJECT_SOURCE_DIR}/src/qt_gui/translations")
- file(GLOB_RECURSE TRANSLATIONS_TS ${QT_TRANSLATIONS}/*.ts)
-
- set_source_files_properties(${TRANSLATIONS_TS} PROPERTIES OUTPUT_LOCATION "${CMAKE_CURRENT_BINARY_DIR}/translations")
- qt_add_translation(TRANSLATIONS_QM ${TRANSLATIONS_TS})
-
- set(TRANSLATIONS_QRC ${CMAKE_CURRENT_BINARY_DIR}/translations/translations.qrc)
- file(WRITE ${TRANSLATIONS_QRC} "\n")
- foreach (QM ${TRANSLATIONS_QM})
- get_filename_component(QM_FILE ${QM} NAME)
- file(APPEND ${TRANSLATIONS_QRC} "${QM_FILE}\n")
- endforeach (QM)
- file(APPEND ${TRANSLATIONS_QRC} "")
-
- qt_add_resources(TRANSLATIONS ${TRANSLATIONS_QRC})
-endif()
-
set(AJM_LIB src/core/libraries/ajm/ajm.cpp
src/core/libraries/ajm/ajm.h
src/core/libraries/ajm/ajm_at9.cpp
@@ -1066,112 +1037,26 @@ set(EMULATOR src/emulator.cpp
src/sdl_window.cpp
)
-# The above is shared in SDL and Qt version (TODO share them all)
-
-if(ENABLE_QT_GUI)
-qt_add_resources(RESOURCE_FILES src/shadps4.qrc)
-
-if (ENABLE_UPDATER)
- set(UPDATER src/qt_gui/check_update.cpp
- src/qt_gui/check_update.h
- )
-endif()
-
-set(QT_GUI src/qt_gui/about_dialog.cpp
- src/qt_gui/about_dialog.h
- src/qt_gui/about_dialog.ui
- src/qt_gui/background_music_player.cpp
- src/qt_gui/background_music_player.h
- src/qt_gui/cheats_patches.cpp
- src/qt_gui/cheats_patches.h
- src/qt_gui/compatibility_info.cpp
- src/qt_gui/compatibility_info.h
- src/qt_gui/control_settings.cpp
- src/qt_gui/control_settings.h
- src/qt_gui/control_settings.ui
- src/qt_gui/kbm_gui.cpp
- src/qt_gui/kbm_gui.h
- src/qt_gui/kbm_gui.ui
- src/qt_gui/main_window_ui.h
- src/qt_gui/main_window.cpp
- src/qt_gui/main_window.h
- src/qt_gui/gui_context_menus.h
- src/qt_gui/game_list_utils.h
- src/qt_gui/game_info.cpp
- src/qt_gui/game_info.h
- src/qt_gui/game_list_frame.cpp
- src/qt_gui/game_list_frame.h
- src/qt_gui/game_grid_frame.cpp
- src/qt_gui/game_grid_frame.h
- src/qt_gui/game_install_dialog.cpp
- src/qt_gui/game_install_dialog.h
- src/qt_gui/trophy_viewer.cpp
- src/qt_gui/trophy_viewer.h
- src/qt_gui/elf_viewer.cpp
- src/qt_gui/elf_viewer.h
- src/qt_gui/kbm_config_dialog.cpp
- src/qt_gui/kbm_config_dialog.h
- src/qt_gui/kbm_help_dialog.cpp
- src/qt_gui/kbm_help_dialog.h
- src/qt_gui/main_window_themes.cpp
- src/qt_gui/main_window_themes.h
- src/qt_gui/log_presets_dialog.cpp
- src/qt_gui/log_presets_dialog.h
- src/qt_gui/settings_dialog.cpp
- src/qt_gui/settings_dialog.h
- src/qt_gui/settings_dialog.ui
- src/qt_gui/main.cpp
- src/qt_gui/gui_settings.cpp
- src/qt_gui/gui_settings.h
- src/qt_gui/settings.cpp
- src/qt_gui/settings.h
- src/qt_gui/sdl_event_wrapper.cpp
- src/qt_gui/sdl_event_wrapper.h
- src/qt_gui/hotkeys.h
- src/qt_gui/hotkeys.cpp
- src/qt_gui/hotkeys.ui
- ${EMULATOR}
- ${RESOURCE_FILES}
- ${TRANSLATIONS}
- ${UPDATER}
+add_executable(shadps4
+ ${AUDIO_CORE}
+ ${IMGUI}
+ ${INPUT}
+ ${COMMON}
+ ${CORE}
+ ${SHADER_RECOMPILER}
+ ${VIDEO_CORE}
+ ${EMULATOR}
+ src/main.cpp
+ src/emulator.cpp
+ src/emulator.h
+ src/sdl_window.h
+ src/sdl_window.cpp
)
-endif()
-
-if (ENABLE_QT_GUI)
- qt_add_executable(shadps4
- ${AUDIO_CORE}
- ${IMGUI}
- ${INPUT}
- ${QT_GUI}
- ${COMMON}
- ${CORE}
- ${SHADER_RECOMPILER}
- ${VIDEO_CORE}
- ${EMULATOR}
- src/images/shadPS4.icns
- )
-else()
- add_executable(shadps4
- ${AUDIO_CORE}
- ${IMGUI}
- ${INPUT}
- ${COMMON}
- ${CORE}
- ${SHADER_RECOMPILER}
- ${VIDEO_CORE}
- ${EMULATOR}
- src/main.cpp
- src/emulator.cpp
- src/emulator.h
- src/sdl_window.h
- src/sdl_window.cpp
- )
-endif()
create_target_directory_groups(shadps4)
target_link_libraries(shadps4 PRIVATE magic_enum::magic_enum fmt::fmt toml11::toml11 tsl::robin_map xbyak::xbyak Tracy::TracyClient RenderDoc::API FFmpeg::ffmpeg Dear_ImGui gcn half::half ZLIB::ZLIB PNG::PNG)
-target_link_libraries(shadps4 PRIVATE Boost::headers GPUOpen::VulkanMemoryAllocator LibAtrac9 sirit Vulkan::Headers xxHash::xxhash Zydis::Zydis glslang::glslang SDL3::SDL3 pugixml::pugixml stb::headers libusb::usb lfreist-hwinfo::hwinfo)
+target_link_libraries(shadps4 PRIVATE Boost::headers GPUOpen::VulkanMemoryAllocator LibAtrac9 sirit Vulkan::Headers xxHash::xxhash Zydis::Zydis glslang::glslang SDL3::SDL3 pugixml::pugixml stb::headers libusb::usb lfreist-hwinfo::hwinfo nlohmann_json::nlohmann_json)
target_compile_definitions(shadps4 PRIVATE IMGUI_USER_CONFIG="imgui/imgui_config.h")
target_compile_definitions(Dear_ImGui PRIVATE IMGUI_USER_CONFIG="${PROJECT_SOURCE_DIR}/src/imgui/imgui_config.h")
@@ -1191,20 +1076,9 @@ endif()
if (APPLE)
# Include MoltenVK, along with an ICD file so it can be found by the system Vulkan loader if used for loading layers.
- if (ENABLE_QT_GUI)
- set(MVK_BUNDLE_PATH "Resources/vulkan/icd.d")
- set_property(TARGET shadps4 APPEND PROPERTY BUILD_RPATH "@executable_path/../${MVK_BUNDLE_PATH}")
- set(MVK_DST ${CMAKE_CURRENT_BINARY_DIR}/shadps4.app/Contents/${MVK_BUNDLE_PATH})
-
- add_custom_command(
- OUTPUT ${MVK_DST}
- COMMAND ${CMAKE_COMMAND} -E make_directory ${MVK_DST})
- else()
- set_property(TARGET shadps4 APPEND PROPERTY BUILD_RPATH "@executable_path")
- set(MVK_DST ${CMAKE_CURRENT_BINARY_DIR})
- endif()
-
- set(MVK_DYLIB_SRC ${CMAKE_CURRENT_BINARY_DIR}/externals/MoltenVK/MoltenVK/libMoltenVK.dylib)
+ set_property(TARGET shadps4 APPEND PROPERTY BUILD_RPATH "@executable_path")
+ set(MVK_DST ${CMAKE_CURRENT_BINARY_DIR})
+ set(MVK_DYLIB_SRC ${CMAKE_CURRENT_BINARY_DIR}/externals/MoltenVK/libMoltenVK.dylib)
set(MVK_DYLIB_DST ${MVK_DST}/libMoltenVK.dylib)
set(MVK_ICD_SRC ${CMAKE_CURRENT_SOURCE_DIR}/externals/MoltenVK/MoltenVK/icd/MoltenVK_icd.json)
set(MVK_ICD_DST ${MVK_DST}/MoltenVK_icd.json)
@@ -1230,14 +1104,6 @@ if (APPLE)
target_link_libraries(shadps4 PRIVATE date::date-tz epoll-shim)
endif()
-if (ENABLE_QT_GUI)
- target_link_libraries(shadps4 PRIVATE Qt6::Widgets Qt6::Concurrent Qt6::Network Qt6::Multimedia)
- add_definitions(-DENABLE_QT_GUI)
- if (ENABLE_UPDATER)
- add_definitions(-DENABLE_UPDATER)
- endif()
-endif()
-
if (WIN32)
target_link_libraries(shadps4 PRIVATE mincore wepoll)
@@ -1315,25 +1181,6 @@ add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/src/imgui/renderer)
add_dependencies(shadps4 ImGui_Resources)
target_include_directories(shadps4 PRIVATE ${IMGUI_RESOURCES_INCLUDE})
-if (ENABLE_QT_GUI)
- set_target_properties(shadps4 PROPERTIES
-# WIN32_EXECUTABLE ON
- MACOSX_BUNDLE ON
- MACOSX_BUNDLE_INFO_PLIST "${CMAKE_CURRENT_SOURCE_DIR}/dist/MacOSBundleInfo.plist.in"
- MACOSX_BUNDLE_ICON_FILE "shadPS4.icns"
- MACOSX_BUNDLE_SHORT_VERSION_STRING "${APP_VERSION}"
- )
-
- set_source_files_properties(src/images/shadPS4.icns PROPERTIES
- MACOSX_PACKAGE_LOCATION Resources)
-endif()
-
-if (UNIX AND NOT APPLE)
- if (ENABLE_QT_GUI)
- find_package(OpenSSL REQUIRED)
- target_link_libraries(shadps4 PRIVATE ${OPENSSL_LIBRARIES})
- endif()
-endif()
# Discord RPC
if (ENABLE_DISCORD_RPC)
@@ -1342,10 +1189,3 @@ endif()
# Install rules
install(TARGETS shadps4 BUNDLE DESTINATION .)
-
-if (ENABLE_QT_GUI AND CMAKE_SYSTEM_NAME STREQUAL "Linux")
- install(FILES "dist/net.shadps4.shadPS4.desktop" DESTINATION "share/applications")
- install(FILES "dist/net.shadps4.shadPS4.metainfo.xml" DESTINATION "share/metainfo")
- install(FILES ".github/shadps4.png" DESTINATION "share/icons/hicolor/512x512/apps" RENAME "net.shadps4.shadPS4.png")
- install(FILES "src/images/net.shadps4.shadPS4.svg" DESTINATION "share/icons/hicolor/scalable/apps")
-endif()
diff --git a/CMakePresets.json b/CMakePresets.json
index bd1aba36e..c34007a34 100644
--- a/CMakePresets.json
+++ b/CMakePresets.json
@@ -15,15 +15,6 @@
"CMAKE_BUILD_TYPE": "Debug"
}
},
- {
- "name": "x64-Clang-Debug-Qt",
- "displayName": "Clang x64 Debug with Qt",
- "inherits": ["x64-Clang-Base"],
- "cacheVariables": {
- "CMAKE_BUILD_TYPE": "Debug",
- "ENABLE_QT_GUI": "ON"
- }
- },
{
"name": "x64-Clang-Release",
"displayName": "Clang x64 Release",
@@ -32,15 +23,6 @@
"CMAKE_BUILD_TYPE": "Release"
}
},
- {
- "name": "x64-Clang-Release-Qt",
- "displayName": "Clang x64 Release with Qt",
- "inherits": ["x64-Clang-Base"],
- "cacheVariables": {
- "CMAKE_BUILD_TYPE": "Release",
- "ENABLE_QT_GUI": "ON"
- }
- },
{
"name": "x64-Clang-RelWithDebInfo",
"displayName": "Clang x64 RelWithDebInfo",
@@ -48,15 +30,6 @@
"cacheVariables": {
"CMAKE_BUILD_TYPE": "RelWithDebInfo"
}
- },
- {
- "name": "x64-Clang-RelWithDebInfo-Qt",
- "displayName": "Clang x64 RelWithDebInfo with Qt",
- "inherits": ["x64-Clang-Base"],
- "cacheVariables": {
- "CMAKE_BUILD_TYPE": "RelWithDebInfo",
- "ENABLE_QT_GUI": "ON"
- }
}
]
}
\ No newline at end of file
diff --git a/CMakeSettings.json b/CMakeSettings.json
index e1ed36887..bb522fcfc 100644
--- a/CMakeSettings.json
+++ b/CMakeSettings.json
@@ -12,18 +12,6 @@
"inheritEnvironments": [ "clang_cl_x64_x64" ],
"intelliSenseMode": "windows-clang-x64"
},
- {
- "name": "x64-Clang-Release-Qt",
- "generator": "Ninja",
- "configurationType": "Release",
- "buildRoot": "${projectDir}\\Build\\${name}",
- "installRoot": "${projectDir}\\Install\\${name}",
- "cmakeCommandArgs": "-DENABLE_QT_GUI=ON",
- "buildCommandArgs": "",
- "ctestCommandArgs": "",
- "inheritEnvironments": [ "clang_cl_x64_x64" ],
- "intelliSenseMode": "windows-clang-x64"
- },
{
"name": "x64-Clang-Debug",
"generator": "Ninja",
@@ -36,18 +24,6 @@
"inheritEnvironments": [ "clang_cl_x64_x64" ],
"intelliSenseMode": "windows-clang-x64"
},
- {
- "name": "x64-Clang-Debug-Qt",
- "generator": "Ninja",
- "configurationType": "Debug",
- "buildRoot": "${projectDir}\\Build\\${name}",
- "installRoot": "${projectDir}\\Install\\${name}",
- "cmakeCommandArgs": "-DENABLE_QT_GUI=ON",
- "buildCommandArgs": "",
- "ctestCommandArgs": "",
- "inheritEnvironments": [ "clang_cl_x64_x64" ],
- "intelliSenseMode": "windows-clang-x64"
- },
{
"name": "x64-Clang-RelWithDebInfo",
"generator": "Ninja",
@@ -59,18 +35,6 @@
"ctestCommandArgs": "",
"inheritEnvironments": [ "clang_cl_x64_x64" ],
"intelliSenseMode": "windows-clang-x64"
- },
- {
- "name": "x64-Clang-RelWithDebInfo-Qt",
- "generator": "Ninja",
- "configurationType": "RelWithDebInfo",
- "buildRoot": "${projectDir}\\Build\\${name}",
- "installRoot": "${projectDir}\\Install\\${name}",
- "cmakeCommandArgs": "-DENABLE_QT_GUI=ON",
- "buildCommandArgs": "",
- "ctestCommandArgs": "",
- "inheritEnvironments": [ "clang_cl_x64_x64" ],
- "intelliSenseMode": "windows-clang-x64"
}
]
}
\ No newline at end of file
diff --git a/README.md b/README.md
index e08b47a55..dbb890f87 100644
--- a/README.md
+++ b/README.md
@@ -36,6 +36,9 @@ SPDX-License-Identifier: GPL-2.0-or-later
**shadPS4** is an early **PlayStation 4** emulator for **Windows**, **Linux** and **macOS** written in C++.
+> [!IMPORTANT]
+> This is the emulator core, which does not include a GUI. If you just want to use the emulator as an end user, download the [**QtLauncher**](https://github.com/shadps4-emu/shadps4-qtlauncher/releases) instead.
+
If you encounter problems or have doubts, do not hesitate to look at the [**Quickstart**](https://github.com/shadps4-emu/shadPS4/wiki/I.-Quick-start-%5BUsers%5D).\
To verify that a game works, you can look at [**shadPS4 Game Compatibility**](https://github.com/shadps4-compatibility/shadps4-game-compatibility).\
To discuss shadPS4 development, suggest ideas or to ask for help, join our [**Discord server**](https://discord.gg/bFJxfftGW6).\
@@ -55,9 +58,6 @@ This project began for fun. Given our limited free time, it may take some time b
# Building
-> [!IMPORTANT]
-> If you want to use shadPS4 to play your games, you don't have to follow the build instructions, you can simply download the emulator from either the [**release tab**](https://github.com/shadps4-emu/shadPS4/releases) or the [**action tab**](https://github.com/shadps4-emu/shadPS4/actions).
-
## Windows
Check the build instructions for [**Windows**](https://github.com/shadps4-emu/shadPS4/blob/main/documents/building-windows.md).
@@ -73,6 +73,22 @@ Check the build instructions for [**macOS**](https://github.com/shadps4-emu/shad
> [!IMPORTANT]
> macOS users need at least macOS 15.4 to run shadPS4. Due to GPU issues there are currently heavy bugs on Intel Macs.
+# Usage examples
+
+> [!IMPORTANT]
+> For a user-friendly GUI, download the [**QtLauncher**](https://github.com/shadps4-emu/shadps4-qtlauncher/releases).
+
+To get the list of all available commands and also a more detailed description of what each command does, please refer to the `--help` flag's output.
+
+Below is a list of commonly used command patterns:
+```sh
+shadPS4 CUSA00001 # Searches for a game folder called CUSA00001 in the list of game install folders, and boots it.
+shadPS4 --fullscreen true --config-clean CUSA00001 # the game argument is always the last one,
+shadPS4 -g CUSA00001 --fullscreen true --config-clean # ...unless manually specified otherwise.
+shadPS4 /path/to/game.elf # Boots a PS4 ELF file directly. Useful if you want to boot an executable that is not named eboot.bin.
+shadPS4 CUSA00001 -- -flag1 -flag2 # Passes '-flag1' and '-flag2' to the game executable in argv.
+```
+
# Debugging and reporting issues
For more information on how to test, debug and report issues with the emulator or games, read the [**Debugging documentation**](https://github.com/shadps4-emu/shadPS4/blob/main/documents/Debugging/Debugging.md).
@@ -160,15 +176,6 @@ Logo is done by [**Xphalnos**](https://github.com/Xphalnos)
If you want to contribute, please read the [**CONTRIBUTING.md**](https://github.com/shadps4-emu/shadPS4/blob/main/CONTRIBUTING.md) file.\
Open a PR and we'll check it :)
-# Translations
-
-If you want to translate shadPS4 to your language we use [**Crowdin**](https://crowdin.com/project/shadps4-emulator).
-# Contributors
-
-
-
-
-
# Special Thanks
diff --git a/REUSE.toml b/REUSE.toml
index 1245f7b2e..18200ab28 100644
--- a/REUSE.toml
+++ b/REUSE.toml
@@ -74,7 +74,6 @@ path = [
"src/images/trophy.wav",
"src/images/hotkey.png",
"src/images/game_settings.png",
- "src/shadps4.qrc",
"src/shadps4.rc",
]
precedence = "aggregate"
@@ -130,9 +129,4 @@ SPDX-License-Identifier = "MIT"
[[annotations]]
path = "src/video_core/host_shaders/fsr/*"
SPDX-FileCopyrightText = "Copyright (c) 2021 Advanced Micro Devices, Inc. All rights reserved."
-SPDX-License-Identifier = "MIT"
-
-[[annotations]]
-path = "dist/qt.conf"
-SPDX-FileCopyrightText = "shadPS4 Emulator Project"
-SPDX-License-Identifier = "GPL-2.0-or-later"
+SPDX-License-Identifier = "MIT"
\ No newline at end of file
diff --git a/cmake/DetectQtInstallation.cmake b/cmake/DetectQtInstallation.cmake
deleted file mode 100644
index 650cc9745..000000000
--- a/cmake/DetectQtInstallation.cmake
+++ /dev/null
@@ -1,28 +0,0 @@
-# SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
-# SPDX-License-Identifier: GPL-2.0-or-later
-
-set(highest_version "0")
-set(CANDIDATE_DRIVES A B C D E F G H I J K L M N O P Q R S T U V W X Y Z)
-
-foreach(drive ${CANDIDATE_DRIVES})
- file(GLOB kits LIST_DIRECTORIES true CONFIGURE_DEPENDS "${drive}:/Qt/*/msvc*_64")
- foreach(kit IN LISTS kits)
- get_filename_component(version_dir "${kit}" DIRECTORY)
- get_filename_component(kit_version "${version_dir}" NAME)
-
- message(STATUS "DetectQtInstallation.cmake: Detected Qt: ${kit}")
-
- if (kit_version VERSION_GREATER highest_version)
- set(highest_version "${kit_version}")
- set(QT_PREFIX "${kit}")
-
- endif()
- endforeach()
-endforeach()
-
-if(QT_PREFIX)
- set(CMAKE_PREFIX_PATH "${QT_PREFIX}" CACHE PATH "Qt prefix auto‑detected" FORCE)
- message(STATUS "DetectQtInstallation.cmake: Choose newest Qt: ${QT_PREFIX}")
-else()
- message(STATUS "DetectQtInstallation.cmake: No Qt‑Directory found in :/Qt – please set CMAKE_PREFIX_PATH manually")
-endif()
diff --git a/dist/qt.conf b/dist/qt.conf
deleted file mode 100644
index 0bb15d2e0..000000000
--- a/dist/qt.conf
+++ /dev/null
@@ -1,2 +0,0 @@
-[Paths]
-plugins = "./qtplugins"
\ No newline at end of file
diff --git a/documents/building-linux.md b/documents/building-linux.md
index 00d73280e..49aacdfc9 100644
--- a/documents/building-linux.md
+++ b/documents/building-linux.md
@@ -17,8 +17,7 @@ First and foremost, Clang 18 is the **recommended compiler** as it is used for o
sudo apt install build-essential clang git cmake libasound2-dev \
libpulse-dev libopenal-dev libssl-dev zlib1g-dev libedit-dev \
libudev-dev libevdev-dev libsdl2-dev libjack-dev libsndio-dev \
- qt6-base-dev qt6-tools-dev qt6-multimedia-dev libvulkan-dev \
- vulkan-validationlayers libpng-dev
+ libvulkan-dev vulkan-validationlayers libpng-dev
```
#### Fedora
@@ -27,8 +26,6 @@ sudo apt install build-essential clang git cmake libasound2-dev \
sudo dnf install clang git cmake libatomic alsa-lib-devel \
pipewire-jack-audio-connection-kit-devel openal-soft-devel \
openssl-devel libevdev-devel libudev-devel libXext-devel \
- qt6-qtbase-devel qt6-qtbase-private-devel \
- qt6-qtmultimedia-devel qt6-qtsvg-devel qt6-qttools-devel \
vulkan-devel vulkan-validation-layers libpng-devel libuuid-devel
```
@@ -36,8 +33,7 @@ sudo dnf install clang git cmake libatomic alsa-lib-devel \
```bash
sudo pacman -S base-devel clang git cmake sndio jack2 openal \
- qt6-base qt6-declarative qt6-multimedia qt6-tools sdl2 \
- vulkan-validation-layers libpng
+ sdl2 vulkan-validation-layers libpng
```
**Note**: The `shadps4-git` AUR package is not maintained by any of the developers, and it uses the default compiler, which is often set to GCC. Use at your own discretion.
@@ -48,9 +44,7 @@ sudo pacman -S base-devel clang git cmake sndio jack2 openal \
sudo zypper install clang git cmake libasound2 libpulse-devel \
libsndio7 libjack-devel openal-soft-devel libopenssl-devel \
zlib-devel libedit-devel systemd-devel libevdev-devel \
- qt6-base-devel qt6-multimedia-devel qt6-svg-devel \
- qt6-linguist-devel qt6-gui-private-devel vulkan-devel \
- vulkan-validationlayers libpng-devel
+ vulkan-devel vulkan-validationlayers libpng-devel
```
#### NixOS
@@ -90,12 +84,12 @@ There are 3 options you can choose from. Option 1 is **highly recommended**.
1. Generate the build directory in the shadPS4 directory.
```bash
-cmake -S . -B build/ -DENABLE_QT_GUI=ON -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++
+cmake -S . -B build/ -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++
```
-To disable the Qt GUI, remove the `-DENABLE_QT_GUI=ON` flag. To change the build type (for debugging), add `-DCMAKE_BUILD_TYPE=Debug`.
+To change the build type (for debugging), add `-DCMAKE_BUILD_TYPE=Debug`.
-2. Use CMake to build the project:
+1. Use CMake to build the project:
```bash
cmake --build ./build --parallel$(nproc)
@@ -103,18 +97,12 @@ cmake --build ./build --parallel$(nproc)
If your computer freezes during this step, this could be caused by excessive system resource usage. In that case, remove `--parallel$(nproc)`.
-Now run the emulator. If Qt was enabled at configure time:
+Now run the emulator to get the list of options:
```bash
./build/shadps4
```
-Otherwise, specify the path to your game's boot file:
-
-```bash
-./build/shadps4 /"PATH"/"TO"/"GAME"/"FOLDER"/eboot.bin
-```
-
You can also specify the Game ID as an argument for which game to boot, as long as the folder containing the games is specified in config.toml (example: Bloodborne (US) is CUSA00900).
#### Option 2: Configuring with cmake-gui
@@ -142,10 +130,6 @@ Go to Settings, filter by `@ext:ms-vscode.cmake-tools configure` and disable thi

-If you wish to build with the Qt GUI, add `-DENABLE_QT_GUI=ON` to the configure arguments:
-
-
-
On the CMake tab, change the options as you wish, but make sure that it looks similar to or exactly like this:

diff --git a/documents/building-macos.md b/documents/building-macos.md
index 9a1a021ee..6189356f0 100644
--- a/documents/building-macos.md
+++ b/documents/building-macos.md
@@ -24,21 +24,6 @@ eval $(/opt/homebrew/bin/brew shellenv)
brew install clang-format cmake
```
-Next, install x86_64 Qt. You can skip these steps and move on to **Cloning and compiling** if you do not intend to build the Qt GUI.
-
-**If you are on an ARM Mac:**
-```
-# Installs x86_64 Homebrew to /usr/local
-arch -x86_64 /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
-# Installs libraries.
-arch -x86_64 /usr/local/bin/brew install qt@6
-```
-
-**If you are on an x86_64 Mac:**
-```
-brew install qt@6
-```
-
### Cloning and compiling:
Clone the repository recursively:
@@ -52,8 +37,6 @@ Generate the build directory in the shadPS4 directory:
cmake -S . -B build/ -DCMAKE_OSX_ARCHITECTURES=x86_64
```
-If you want to build the Qt GUI, add `-DENABLE_QT_GUI=ON` to the end of this command as well.
-
Enter the directory:
```
cd build/
diff --git a/documents/building-windows.md b/documents/building-windows.md
index 195028da5..aa7213abc 100644
--- a/documents/building-windows.md
+++ b/documents/building-windows.md
@@ -22,23 +22,6 @@ Once you are within the installer:
2. Go to "Individual Components" tab then search and select both `C++ Clang Compiler for Windows` and `MSBuild support for LLVM`
3. Continue the installation
-### (Prerequisite) Download [**Qt**](https://doc.qt.io/qt-6/get-and-install-qt.html)
-
-Beware, this requires you to create a Qt account. If you do not want to do this, please follow the MSYS2/MinGW compilation method instead.
-
-1. Under the current, non beta version of Qt, select the option `MSVC 2022 64-bit` or similar, as well as `QT Multimedia`.
- If you are on Windows on ARM / Qualcomm Snapdragon Elite X, select `MSVC 2022 ARM64` instead.
-
- Go through the installation normally. If you know what you are doing, you may unselect individual components that eat up too much disk space.
-
-2. Download and install [Qt Visual Studio Tools](https://marketplace.visualstudio.com/items?itemName=TheQtCompany.QtVisualStudioTools2022)
-
-Once you are finished, you will have to configure Qt within Visual Studio:
-
-1. Tools -> Options -> Qt -> Versions
-2. Add a new Qt version and navigate it to the correct folder. Should look like so: `C:\Qt\\msvc2022_64`
-3. Enable the default checkmark on the new version you just created.
-
### (Prerequisite) Download [**Git for Windows**](https://git-scm.com/download/win)
Go through the Git for Windows installation as normal
@@ -79,13 +62,10 @@ Normal x86-based computers, follow:
1. Open "MSYS2 MINGW64" from your new applications
2. Run `pacman -Syu`, let it complete;
3. Run `pacman -S --needed git mingw-w64-x86_64-binutils mingw-w64-x86_64-clang mingw-w64-x86_64-cmake mingw-w64-x86_64-rapidjson mingw-w64-x86_64-ninja mingw-w64-x86_64-ffmpeg`
- 1. Optional (Qt only): run `pacman -S --needed mingw-w64-x86_64-qt6-base mingw-w64-x86_64-qt6-tools mingw-w64-x86_64-qt6-multimedia`
4. Run `git clone --depth 1 --recursive https://github.com/shadps4-emu/shadPS4`
5. Run `cd shadPS4`
6. Run `cmake -S . -B build -DCMAKE_C_COMPILER="clang.exe" -DCMAKE_CXX_COMPILER="clang++.exe" -DCMAKE_CXX_FLAGS="-O2 -march=native"`
- 1. Optional (Qt only): add `-DENABLE_QT_GUI=ON`
7. Run `cmake --build build`
- 1. Optional (Qt only): run `windeployqt6 build/shadps4.exe`
8. To run the finished product, run `./build/shadPS4.exe`
ARM64-based computers, follow:
@@ -97,9 +77,7 @@ ARM64-based computers, follow:
4. Run `git clone --depth 1 --recursive https://github.com/shadps4-emu/shadPS4`
5. Run `cd shadPS4`
6. Run `cmake -S . -B build -DCMAKE_C_COMPILER="clang.exe" -DCMAKE_CXX_COMPILER="clang++.exe" -DCMAKE_CXX_FLAGS="-O2 -march=native"`
- 1. Optional (Qt only): add `-DENABLE_QT_GUI=ON`
7. Run `cmake --build build`
- 1. Optional (Qt only): run `windeployqt6 build/shadps4.exe`
8. To run the finished product, run `./build/shadPS4.exe`
## Note on MSYS2 builds
diff --git a/externals/CMakeLists.txt b/externals/CMakeLists.txt
index 2df833426..a26f58d69 100644
--- a/externals/CMakeLists.txt
+++ b/externals/CMakeLists.txt
@@ -245,3 +245,7 @@ endif()
if (WIN32)
add_subdirectory(ext-wepoll)
endif()
+
+#nlohmann json
+set(JSON_BuildTests OFF CACHE INTERNAL "")
+add_subdirectory(json)
diff --git a/externals/json b/externals/json
new file mode 160000
index 000000000..55f93686c
--- /dev/null
+++ b/externals/json
@@ -0,0 +1 @@
+Subproject commit 55f93686c01528224f448c19128836e7df245f72
diff --git a/shell.nix b/shell.nix
index 4eee9fdea..50336d1b2 100644
--- a/shell.nix
+++ b/shell.nix
@@ -17,7 +17,6 @@ pkgs.mkShell {
pkgs.alsa-lib
pkgs.libpulseaudio
pkgs.openal
- pkgs.openssl
pkgs.zlib
pkgs.libedit
pkgs.udev
@@ -25,9 +24,6 @@ pkgs.mkShell {
pkgs.SDL2
pkgs.jack2
pkgs.sndio
- pkgs.qt6.qtbase
- pkgs.qt6.qttools
- pkgs.qt6.qtmultimedia
pkgs.vulkan-headers
pkgs.vulkan-utility-libraries
@@ -44,16 +40,12 @@ pkgs.mkShell {
pkgs.xorg.xcbutilwm
pkgs.sdl3
pkgs.stb
- pkgs.qt6.qtwayland
pkgs.wayland-protocols
pkgs.libpng
];
shellHook = ''
echo "Entering shadPS4 dev shell"
- export QT_QPA_PLATFORM="wayland"
- export QT_PLUGIN_PATH="${pkgs.qt6.qtwayland}/lib/qt-6/plugins:${pkgs.qt6.qtbase}/lib/qt-6/plugins"
- export QML2_IMPORT_PATH="${pkgs.qt6.qtbase}/lib/qt-6/qml"
export CMAKE_PREFIX_PATH="${pkgs.vulkan-headers}:$CMAKE_PREFIX_PATH"
# OpenGL
diff --git a/src/common/config.cpp b/src/common/config.cpp
index ee9a12874..539c4242e 100644
--- a/src/common/config.cpp
+++ b/src/common/config.cpp
@@ -139,13 +139,10 @@ static ConfigEntry trophyNotificationDuration(6.0);
static ConfigEntry logFilter("");
static ConfigEntry logType("sync");
static ConfigEntry userName("shadPS4");
-static ConfigEntry chooseHomeTab("General");
static ConfigEntry isShowSplash(false);
static ConfigEntry isSideTrophy("right");
static ConfigEntry isConnectedToNetwork(false);
static bool enableDiscordRPC = false;
-static bool checkCompatibilityOnStartup = false;
-static bool compatibilityData = false;
static std::filesystem::path sys_modules_path = {};
// Input
@@ -203,7 +200,6 @@ static ConfigEntry isFpsColor(true);
static ConfigEntry logEnabled(true);
// GUI
-static bool load_game_size = true;
static std::vector settings_install_dirs = {};
std::vector install_dirs_enabled = {};
std::filesystem::path settings_addon_install_dir = {};
@@ -218,9 +214,19 @@ static string trophyKey = "";
// Config version, used to determine if a user's config file is outdated.
static string config_version = Common::g_scm_rev;
-// These two entries aren't stored in the config
+// These entries aren't stored in the config
static bool overrideControllerColor = false;
static int controllerCustomColorRGB[3] = {0, 0, 255};
+static bool isGameRunning = false;
+static bool load_auto_patches = true;
+
+bool getGameRunning() {
+ return isGameRunning;
+}
+
+void setGameRunning(bool running) {
+ isGameRunning = running;
+}
std::filesystem::path getSysModulesPath() {
if (sys_modules_path.empty()) {
@@ -278,10 +284,6 @@ void setTrophyKey(string key) {
trophyKey = key;
}
-bool GetLoadGameSizeEnabled() {
- return load_game_size;
-}
-
std::filesystem::path GetSaveDataPath() {
if (save_data_path.empty()) {
return Common::FS::GetUserPath(Common::FS::PathType::UserDir) / "savedata";
@@ -293,10 +295,6 @@ void setVolumeSlider(int volumeValue, bool is_game_specific) {
volumeSlider.set(volumeValue, is_game_specific);
}
-void setLoadGameSizeEnabled(bool enable) {
- load_game_size = enable;
-}
-
bool isNeoModeConsole() {
return isNeo.get();
}
@@ -309,8 +307,10 @@ int getExtraDmemInMbytes() {
return extraDmemInMbytes.get();
}
-void setExtraDmemInMbytes(int value) {
- extraDmemInMbytes.base_value = 0;
+void setExtraDmemInMbytes(int value, bool is_game_specific) {
+ // Disable setting in global config
+ is_game_specific ? extraDmemInMbytes.game_specific_value = value
+ : extraDmemInMbytes.base_value = 0;
}
bool getIsFullscreen() {
@@ -389,10 +389,6 @@ string getUserName() {
return userName.get();
}
-string getChooseHomeTab() {
- return chooseHomeTab.get();
-}
-
bool getUseSpecialPad() {
return useSpecialPad.get();
}
@@ -508,14 +504,6 @@ void setVkGuestMarkersEnabled(bool enable, bool is_game_specific) {
vkGuestMarkers.set(enable, is_game_specific);
}
-bool getCompatibilityEnabled() {
- return compatibilityData;
-}
-
-bool getCheckCompatibilityOnStartup() {
- return checkCompatibilityOnStartup;
-}
-
bool getIsConnectedToNetwork() {
return isConnectedToNetwork.get();
}
@@ -600,6 +588,14 @@ void setVkSyncValidation(bool enable, bool is_game_specific) {
vkValidationSync.set(enable, is_game_specific);
}
+void setVkCoreValidation(bool enable, bool is_game_specific) {
+ vkValidationCore.set(enable, is_game_specific);
+}
+
+void setVkGpuValidation(bool enable, bool is_game_specific) {
+ vkValidationGpu.set(enable, is_game_specific);
+}
+
void setRdocEnabled(bool enable, bool is_game_specific) {
rdocEnable.set(enable, is_game_specific);
}
@@ -680,10 +676,6 @@ void setUserName(const string& name, bool is_game_specific) {
userName.set(name, is_game_specific);
}
-void setChooseHomeTab(const string& type, bool is_game_specific) {
- chooseHomeTab.set(type, is_game_specific);
-}
-
void setUseSpecialPad(bool use) {
useSpecialPad.base_value = use;
}
@@ -696,14 +688,6 @@ void setIsMotionControlsEnabled(bool use, bool is_game_specific) {
isMotionControlsEnabled.set(use, is_game_specific);
}
-void setCompatibilityEnabled(bool use) {
- compatibilityData = use;
-}
-
-void setCheckCompatibilityOnStartup(bool use) {
- checkCompatibilityOnStartup = use;
-}
-
bool addGameInstallDir(const std::filesystem::path& dir, bool enabled) {
for (const auto& install_dir : settings_install_dirs) {
if (install_dir.path == dir) {
@@ -833,6 +817,13 @@ void setRcasAttenuation(int value, bool is_game_specific) {
rcasAttenuation.set(value, is_game_specific);
}
+bool getLoadAutoPatches() {
+ return load_auto_patches;
+}
+void setLoadAutoPatches(bool enable) {
+ load_auto_patches = enable;
+}
+
void load(const std::filesystem::path& path, bool is_game_specific) {
// If the configuration file does not exist, create it and return, unless it is game specific
std::error_code error;
@@ -874,12 +865,8 @@ void load(const std::filesystem::path& path, bool is_game_specific) {
userName.setFromToml(general, "userName", is_game_specific);
isShowSplash.setFromToml(general, "showSplash", is_game_specific);
isSideTrophy.setFromToml(general, "sideTrophy", is_game_specific);
- compatibilityData = toml::find_or(general, "compatibilityEnabled", compatibilityData);
- checkCompatibilityOnStartup = toml::find_or(general, "checkCompatibilityOnStartup",
- checkCompatibilityOnStartup);
isConnectedToNetwork.setFromToml(general, "isConnectedToNetwork", is_game_specific);
- chooseHomeTab.setFromToml(general, "chooseHomeTab", is_game_specific);
defaultControllerID.setFromToml(general, "defaultControllerID", is_game_specific);
sys_modules_path = toml::find_fs_path_or(general, "sysModulesPath", sys_modules_path);
}
@@ -957,8 +944,6 @@ void load(const std::filesystem::path& path, bool is_game_specific) {
if (data.contains("GUI")) {
const toml::value& gui = data.at("GUI");
- load_game_size = toml::find_or(gui, "loadGameSizeEnabled", load_game_size);
-
const auto install_dir_array =
toml::find_or>(gui, "installDirs", {});
@@ -1062,7 +1047,6 @@ void save(const std::filesystem::path& path, bool is_game_specific) {
logFilter.setTomlValue(data, "General", "logFilter", is_game_specific);
logType.setTomlValue(data, "General", "logType", is_game_specific);
userName.setTomlValue(data, "General", "userName", is_game_specific);
- chooseHomeTab.setTomlValue(data, "General", "chooseHomeTab", is_game_specific);
isShowSplash.setTomlValue(data, "General", "showSplash", is_game_specific);
isSideTrophy.setTomlValue(data, "General", "sideTrophy", is_game_specific);
isNeo.setTomlValue(data, "General", "isPS4Pro", is_game_specific);
@@ -1104,6 +1088,8 @@ void save(const std::filesystem::path& path, bool is_game_specific) {
gpuId.setTomlValue(data, "Vulkan", "gpuId", is_game_specific);
vkValidation.setTomlValue(data, "Vulkan", "validation", is_game_specific);
vkValidationSync.setTomlValue(data, "Vulkan", "validation_sync", is_game_specific);
+ vkValidationCore.setTomlValue(data, "Vulkan", "validation_core", is_game_specific);
+ vkValidationGpu.setTomlValue(data, "Vulkan", "validation_gpu", is_game_specific);
vkCrashDiagnostic.setTomlValue(data, "Vulkan", "crashDiagnostic", is_game_specific);
vkHostMarkers.setTomlValue(data, "Vulkan", "hostMarkers", is_game_specific);
vkGuestMarkers.setTomlValue(data, "Vulkan", "guestMarkers", is_game_specific);
@@ -1149,13 +1135,10 @@ void save(const std::filesystem::path& path, bool is_game_specific) {
// Non game-specific entries
data["General"]["enableDiscordRPC"] = enableDiscordRPC;
- data["General"]["compatibilityEnabled"] = compatibilityData;
- data["General"]["checkCompatibilityOnStartup"] = checkCompatibilityOnStartup;
data["General"]["sysModulesPath"] = string{fmt::UTF(sys_modules_path.u8string()).data};
data["GUI"]["installDirs"] = install_dirs;
data["GUI"]["installDirsEnabled"] = install_dirs_enabled;
data["GUI"]["saveDataPath"] = string{fmt::UTF(save_data_path.u8string()).data};
- data["GUI"]["loadGameSizeEnabled"] = load_game_size;
data["GUI"]["addonInstallDir"] =
string{fmt::UTF(settings_addon_install_dir.u8string()).data};
data["Debug"]["ConfigVersion"] = config_version;
@@ -1169,8 +1152,6 @@ void save(const std::filesystem::path& path, bool is_game_specific) {
data["GPU"]["internalScreenWidth"] = internalScreenWidth.base_value;
data["GPU"]["internalScreenHeight"] = internalScreenHeight.base_value;
data["GPU"]["patchShaders"] = shouldPatchShaders.base_value;
- data["Vulkan"]["validation_core"] = vkValidationCore.base_value;
- data["Vulkan"]["validation_gpu"] = vkValidationGpu.base_value;
data["Debug"]["FPSColor"] = isFpsColor.base_value;
}
@@ -1205,7 +1186,6 @@ void setDefaultValues(bool is_game_specific) {
logFilter.set("", is_game_specific);
logType.set("sync", is_game_specific);
userName.set("shadPS4", is_game_specific);
- chooseHomeTab.set("General", is_game_specific);
isShowSplash.set(false, is_game_specific);
isSideTrophy.set("right", is_game_specific);
@@ -1258,8 +1238,6 @@ void setDefaultValues(bool is_game_specific) {
// General
enableDiscordRPC = false;
- compatibilityData = false;
- checkCompatibilityOnStartup = false;
// Input
useSpecialPad.base_value = false;
@@ -1278,9 +1256,6 @@ void setDefaultValues(bool is_game_specific) {
internalScreenWidth.base_value = 1280;
internalScreenHeight.base_value = 720;
- // GUI
- load_game_size = true;
-
// Debug
isFpsColor.base_value = true;
}
diff --git a/src/common/config.h b/src/common/config.h
index 2760117f7..43bdf83f3 100644
--- a/src/common/config.h
+++ b/src/common/config.h
@@ -27,6 +27,8 @@ void load(const std::filesystem::path& path, bool is_game_specific = false);
void save(const std::filesystem::path& path, bool is_game_specific = false);
void resetGameSpecificValue(std::string entry);
+bool getGameRunning();
+void setGameRunning(bool running);
int getVolumeSlider();
void setVolumeSlider(int volumeValue, bool is_game_specific = false);
std::string getTrophyKey();
@@ -79,6 +81,10 @@ bool vkValidationEnabled();
void setVkValidation(bool enable, bool is_game_specific = false);
bool vkValidationSyncEnabled();
void setVkSyncValidation(bool enable, bool is_game_specific = false);
+bool vkValidationGpuEnabled();
+void setVkGpuValidation(bool enable, bool is_game_specific = false);
+bool vkValidationCoreEnabled();
+void setVkCoreValidation(bool enable, bool is_game_specific = false);
bool getVkCrashDiagnosticEnabled();
void setVkCrashDiagnosticEnabled(bool enable, bool is_game_specific = false);
bool getVkHostMarkersEnabled();
@@ -97,11 +103,10 @@ double getTrophyNotificationDuration();
void setTrophyNotificationDuration(double newTrophyNotificationDuration,
bool is_game_specific = false);
int getCursorHideTimeout();
-void setCursorHideTimeout(int newcursorHideTimeout);
std::string getMainOutputDevice();
-void setMainOutputDevice(std::string device);
+void setMainOutputDevice(std::string device, bool is_game_specific = false);
std::string getPadSpkOutputDevice();
-void setPadSpkOutputDevice(std::string device);
+void setPadSpkOutputDevice(std::string device, bool is_game_specific = false);
std::string getMicDevice();
void setCursorHideTimeout(int newcursorHideTimeout, bool is_game_specific = false);
void setMicDevice(std::string device, bool is_game_specific = false);
@@ -122,10 +127,8 @@ void setNeoMode(bool enable, bool is_game_specific = false);
bool isDevKitConsole();
void setDevKitConsole(bool enable, bool is_game_specific = false);
-bool vkValidationCoreEnabled(); // no set
-bool vkValidationGpuEnabled(); // no set
int getExtraDmemInMbytes();
-void setExtraDmemInMbytes(int value);
+void setExtraDmemInMbytes(int value, bool is_game_specific = false);
bool getIsMotionControlsEnabled();
void setIsMotionControlsEnabled(bool use, bool is_game_specific = false);
std::string getDefaultControllerID();
@@ -143,18 +146,14 @@ void setRcasAttenuation(int value, bool is_game_specific = false);
bool getIsConnectedToNetwork();
void setConnectedToNetwork(bool enable, bool is_game_specific = false);
void setUserName(const std::string& name, bool is_game_specific = false);
-void setChooseHomeTab(const std::string& type, bool is_game_specific = false);
std::filesystem::path getSysModulesPath();
void setSysModulesPath(const std::filesystem::path& path);
+bool getLoadAutoPatches();
+void setLoadAutoPatches(bool enable);
// TODO
-bool GetLoadGameSizeEnabled();
std::filesystem::path GetSaveDataPath();
-void setLoadGameSizeEnabled(bool enable);
-bool getCompatibilityEnabled();
-bool getCheckCompatibilityOnStartup();
std::string getUserName();
-std::string getChooseHomeTab();
bool GetUseUnifiedInputConfig();
void SetUseUnifiedInputConfig(bool use);
bool GetOverrideControllerColor();
@@ -164,8 +163,6 @@ void SetControllerCustomColor(int r, int b, int g);
void setGameInstallDirs(const std::vector& dirs_config);
void setAllGameInstallDirs(const std::vector& dirs_config);
void setSaveDataPath(const std::filesystem::path& path);
-void setCompatibilityEnabled(bool use);
-void setCheckCompatibilityOnStartup(bool use);
// Gui
bool addGameInstallDir(const std::filesystem::path& dir, bool enabled = true);
void removeGameInstallDir(const std::filesystem::path& dir);
diff --git a/src/common/memory_patcher.cpp b/src/common/memory_patcher.cpp
index 123fcdc9c..a7755c1f1 100644
--- a/src/common/memory_patcher.cpp
+++ b/src/common/memory_patcher.cpp
@@ -3,20 +3,12 @@
#include
#include
+#include
#include
#include
+#include
#include
-#ifdef ENABLE_QT_GUI
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#endif
+#include "common/config.h"
#include "common/elf_info.h"
#include "common/logging/log.h"
#include "common/path_util.h"
@@ -28,7 +20,7 @@ namespace MemoryPatcher {
EXPORT uintptr_t g_eboot_address;
uint64_t g_eboot_image_size;
std::string g_game_serial;
-std::string patchFile;
+std::string patch_file;
bool patches_applied = false;
std::vector pending_patches;
@@ -122,223 +114,55 @@ std::string convertValueToHex(const std::string type, const std::string valueStr
void ApplyPendingPatches();
-void OnGameLoaded() {
- if (!patchFile.empty()) {
- std::filesystem::path patchDir = Common::FS::GetUserPath(Common::FS::PathType::PatchesDir);
+void ApplyPatchesFromXML(std::filesystem::path path) {
+ pugi::xml_document doc;
+ pugi::xml_parse_result result = doc.load_file(path.c_str());
- auto filePath = (patchDir / patchFile).native();
+ auto* param_sfo = Common::Singleton::Instance();
+ auto app_version = param_sfo->GetString("APP_VER").value_or("Unknown version");
- pugi::xml_document doc;
- pugi::xml_parse_result result = doc.load_file(filePath.c_str());
+ if (result) {
+ auto patchXML = doc.child("Patch");
+ for (pugi::xml_node_iterator it = patchXML.children().begin();
+ it != patchXML.children().end(); ++it) {
- auto* param_sfo = Common::Singleton::Instance();
- auto app_version = param_sfo->GetString("APP_VER").value_or("Unknown version");
+ if (std::string(it->name()) == "Metadata") {
+ if (std::string(it->attribute("isEnabled").value()) == "true") {
+ std::string currentPatchName = it->attribute("Name").value();
+ std::string metadataAppVer = it->attribute("AppVer").value();
+ bool versionMatches = metadataAppVer == app_version;
- if (result) {
- auto patchXML = doc.child("Patch");
- for (pugi::xml_node_iterator it = patchXML.children().begin();
- it != patchXML.children().end(); ++it) {
+ auto patchList = it->first_child();
+ for (pugi::xml_node_iterator patchLineIt = patchList.children().begin();
+ patchLineIt != patchList.children().end(); ++patchLineIt) {
+
+ std::string type = patchLineIt->attribute("Type").value();
+ if (!versionMatches && type != "mask" && type != "mask_jump32")
+ continue;
- if (std::string(it->name()) == "Metadata") {
- if (std::string(it->attribute("isEnabled").value()) == "true") {
std::string currentPatchName = it->attribute("Name").value();
- std::string metadataAppVer = it->attribute("AppVer").value();
- bool versionMatches = metadataAppVer == app_version;
- auto patchList = it->first_child();
for (pugi::xml_node_iterator patchLineIt = patchList.children().begin();
patchLineIt != patchList.children().end(); ++patchLineIt) {
std::string type = patchLineIt->attribute("Type").value();
- if (!versionMatches && type != "mask" && type != "mask_jump32")
- continue;
-
- std::string currentPatchName = it->attribute("Name").value();
-
- for (pugi::xml_node_iterator patchLineIt = patchList.children().begin();
- patchLineIt != patchList.children().end(); ++patchLineIt) {
-
- std::string type = patchLineIt->attribute("Type").value();
- std::string address = patchLineIt->attribute("Address").value();
- std::string patchValue = patchLineIt->attribute("Value").value();
- std::string maskOffsetStr =
- patchLineIt->attribute("Offset").value();
- std::string targetStr = "";
- std::string sizeStr = "";
- if (type == "mask_jump32") {
- targetStr = patchLineIt->attribute("Target").value();
- sizeStr = patchLineIt->attribute("Size").value();
- } else {
- patchValue = convertValueToHex(type, patchValue);
- }
-
- bool littleEndian = false;
-
- if (type == "bytes16" || type == "bytes32" || type == "bytes64") {
- littleEndian = true;
- }
-
- MemoryPatcher::PatchMask patchMask = MemoryPatcher::PatchMask::None;
- int maskOffsetValue = 0;
-
- if (type == "mask")
- patchMask = MemoryPatcher::PatchMask::Mask;
-
- if (type == "mask_jump32")
- patchMask = MemoryPatcher::PatchMask::Mask_Jump32;
-
- if ((type == "mask" || type == "mask_jump32") &&
- !maskOffsetStr.empty()) {
- maskOffsetValue = std::stoi(maskOffsetStr, 0, 10);
- }
-
- MemoryPatcher::PatchMemory(currentPatchName, address, patchValue,
- targetStr, sizeStr, false, littleEndian,
- patchMask, maskOffsetValue);
- }
- }
- }
- }
- }
- } else {
- LOG_ERROR(Loader, "couldnt patch parse xml : {}", result.description());
- }
- }
- ApplyPendingPatches();
-
-#ifdef ENABLE_QT_GUI
- // We use the QT headers for the xml and json parsing, this define is only true on QT builds
- QString patchDir;
- Common::FS::PathToQString(patchDir, Common::FS::GetUserPath(Common::FS::PathType::PatchesDir));
- QDir dir(patchDir);
- QStringList folders = dir.entryList(QDir::Dirs | QDir::NoDotAndDotDot);
-
- for (const QString& folder : folders) {
- QString filesJsonPath = patchDir + "/" + folder + "/files.json";
-
- QFile jsonFile(filesJsonPath);
- if (!jsonFile.open(QIODevice::ReadOnly)) {
- LOG_ERROR(Loader, "Unable to open files.json for reading in repository {}",
- folder.toStdString());
- continue;
- }
-
- QByteArray jsonData = jsonFile.readAll();
- jsonFile.close();
-
- QJsonDocument jsonDoc = QJsonDocument::fromJson(jsonData);
- QJsonObject jsonObject = jsonDoc.object();
-
- QString selectedFileName;
- QString serial = QString::fromStdString(g_game_serial);
-
- for (auto it = jsonObject.constBegin(); it != jsonObject.constEnd(); ++it) {
- QString filePath = it.key();
- QJsonArray idsArray = it.value().toArray();
-
- if (idsArray.contains(QJsonValue(serial))) {
- selectedFileName = filePath;
- break;
- }
- }
-
- if (selectedFileName.isEmpty()) {
- LOG_ERROR(Loader, "No patch file found for the current serial in repository {}",
- folder.toStdString());
- continue;
- }
-
- QString filePath = patchDir + "/" + folder + "/" + selectedFileName;
- QFile file(filePath);
- if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
- LOG_ERROR(Loader, "Unable to open the file for reading.");
- continue;
- }
-
- QByteArray xmlData = file.readAll();
- file.close();
-
- QString newXmlData;
-
- QXmlStreamReader xmlReader(xmlData);
-
- bool isEnabled = false;
- std::string currentPatchName;
-
- auto* param_sfo = Common::Singleton::Instance();
- auto app_version = param_sfo->GetString("APP_VER").value_or("Unknown version");
- bool versionMatches = true;
-
- while (!xmlReader.atEnd()) {
- xmlReader.readNext();
-
- if (xmlReader.isStartElement()) {
- QJsonArray patchLines;
- if (xmlReader.name() == QStringLiteral("Metadata")) {
- QString name = xmlReader.attributes().value("Name").toString();
- currentPatchName = name.toStdString();
-
- QString appVer = xmlReader.attributes().value("AppVer").toString();
-
- // Check and update the isEnabled attribute
- isEnabled = false;
- for (const QXmlStreamAttribute& attr : xmlReader.attributes()) {
- if (attr.name() == QStringLiteral("isEnabled")) {
- isEnabled = (attr.value().toString() == "true");
- }
- }
- versionMatches = (appVer.toStdString() == app_version);
-
- } else if (xmlReader.name() == QStringLiteral("PatchList")) {
- QJsonArray linesArray;
- while (!xmlReader.atEnd() &&
- !(xmlReader.tokenType() == QXmlStreamReader::EndElement &&
- xmlReader.name() == QStringLiteral("PatchList"))) {
- xmlReader.readNext();
- if (xmlReader.tokenType() == QXmlStreamReader::StartElement &&
- xmlReader.name() == QStringLiteral("Line")) {
- QXmlStreamAttributes attributes = xmlReader.attributes();
- QJsonObject lineObject;
- lineObject["Type"] = attributes.value("Type").toString();
- lineObject["Address"] = attributes.value("Address").toString();
- lineObject["Value"] = attributes.value("Value").toString();
- lineObject["Offset"] = attributes.value("Offset").toString();
- if (lineObject["Type"].toString() == "mask_jump32") {
- lineObject["Target"] = attributes.value("Target").toString();
- lineObject["Size"] = attributes.value("Size").toString();
- }
- linesArray.append(lineObject);
- }
- }
-
- patchLines = linesArray;
- if (isEnabled) {
- foreach (const QJsonValue& value, patchLines) {
- QJsonObject lineObject = value.toObject();
- QString type = lineObject["Type"].toString();
-
- if ((type != "mask" && type != "mask_jump32") && !versionMatches)
- continue;
-
- QString address = lineObject["Address"].toString();
- QString patchValue = lineObject["Value"].toString();
- QString maskOffsetStr = lineObject["Offset"].toString();
-
- QString targetStr;
- QString sizeStr;
-
+ std::string address = patchLineIt->attribute("Address").value();
+ std::string patchValue = patchLineIt->attribute("Value").value();
+ std::string maskOffsetStr = patchLineIt->attribute("Offset").value();
+ std::string targetStr = "";
+ std::string sizeStr = "";
if (type == "mask_jump32") {
- targetStr = lineObject["Target"].toString();
- sizeStr = lineObject["Size"].toString();
+ targetStr = patchLineIt->attribute("Target").value();
+ sizeStr = patchLineIt->attribute("Size").value();
} else {
- patchValue = QString::fromStdString(convertValueToHex(
- type.toStdString(), patchValue.toStdString()));
+ patchValue = convertValueToHex(type, patchValue);
}
bool littleEndian = false;
- if (type == "bytes16" || type == "bytes32" || type == "bytes64")
+ if (type == "bytes16" || type == "bytes32" || type == "bytes64") {
littleEndian = true;
+ }
MemoryPatcher::PatchMask patchMask = MemoryPatcher::PatchMask::None;
int maskOffsetValue = 0;
@@ -350,28 +174,53 @@ void OnGameLoaded() {
patchMask = MemoryPatcher::PatchMask::Mask_Jump32;
if ((type == "mask" || type == "mask_jump32") &&
- !maskOffsetStr.toStdString().empty()) {
- maskOffsetValue = std::stoi(maskOffsetStr.toStdString(), 0, 10);
+ !maskOffsetStr.empty()) {
+ maskOffsetValue = std::stoi(maskOffsetStr, 0, 10);
}
- MemoryPatcher::PatchMemory(
- currentPatchName, address.toStdString(), patchValue.toStdString(),
- targetStr.toStdString(), sizeStr.toStdString(), false, littleEndian,
- patchMask, maskOffsetValue);
+ MemoryPatcher::PatchMemory(currentPatchName, address, patchValue,
+ targetStr, sizeStr, false, littleEndian,
+ patchMask, maskOffsetValue);
}
}
}
}
}
-
- if (xmlReader.hasError()) {
- LOG_ERROR(Loader, "Failed to parse XML for {}", g_game_serial);
- } else {
- LOG_INFO(Loader, "Patches loaded successfully, repository: {}", folder.toStdString());
- }
- ApplyPendingPatches();
+ } else {
+ LOG_ERROR(Loader, "Could not parse patch XML: {}", result.description());
}
-#endif
+}
+
+void OnGameLoaded() {
+ std::filesystem::path patch_dir = Common::FS::GetUserPath(Common::FS::PathType::PatchesDir);
+ if (!patch_file.empty()) {
+
+ auto file_path = (patch_dir / patch_file).native();
+ if (std::filesystem::exists(patch_file)) {
+ ApplyPatchesFromXML(patch_file);
+ } else {
+ ApplyPatchesFromXML(file_path);
+ }
+ } else if (Config::getLoadAutoPatches()) {
+ for (auto const& repo : std::filesystem::directory_iterator(patch_dir)) {
+ if (!repo.is_directory()) {
+ continue;
+ }
+ std::ifstream json_file{repo.path() / "files.json"};
+ nlohmann::json available_patches = nlohmann::json::parse(json_file);
+ std::filesystem::path game_patch_file;
+ for (auto const& [filename, serials] : available_patches.items()) {
+ if (std::find(serials.begin(), serials.end(), g_game_serial) != serials.end()) {
+ game_patch_file = repo.path() / filename;
+ break;
+ }
+ }
+ if (std::filesystem::exists(game_patch_file)) {
+ ApplyPatchesFromXML(game_patch_file);
+ }
+ }
+ }
+ ApplyPendingPatches();
}
void AddPatchToQueue(patchInfo patchToAdd) {
diff --git a/src/common/memory_patcher.h b/src/common/memory_patcher.h
index 93306f540..5a67beec5 100644
--- a/src/common/memory_patcher.h
+++ b/src/common/memory_patcher.h
@@ -17,7 +17,7 @@ namespace MemoryPatcher {
extern EXPORT uintptr_t g_eboot_address;
extern uint64_t g_eboot_image_size;
extern std::string g_game_serial;
-extern std::string patchFile;
+extern std::string patch_file;
enum PatchMask : uint8_t {
None,
diff --git a/src/common/path_util.cpp b/src/common/path_util.cpp
index f368980bf..bd0aff040 100644
--- a/src/common/path_util.cpp
+++ b/src/common/path_util.cpp
@@ -25,10 +25,6 @@
#endif
#endif
-#ifdef ENABLE_QT_GUI
-#include
-#endif
-
namespace Common::FS {
namespace fs = std::filesystem;
@@ -88,13 +84,6 @@ static std::optional GetBundleParentDirectory() {
#endif
static auto UserPaths = [] {
-#if defined(__APPLE__) && defined(ENABLE_QT_GUI)
- // Set the current path to the directory containing the app bundle.
- if (const auto bundle_dir = GetBundleParentDirectory()) {
- std::filesystem::current_path(*bundle_dir);
- }
-#endif
-
// Try the portable user directory first.
auto user_dir = std::filesystem::current_path() / PORTABLE_DIR;
if (!std::filesystem::exists(user_dir)) {
@@ -229,22 +218,4 @@ std::optional FindGameByID(const fs::path& dir, const std::string& gam
return std::nullopt;
}
-#ifdef ENABLE_QT_GUI
-void PathToQString(QString& result, const std::filesystem::path& path) {
-#ifdef _WIN32
- result = QString::fromStdWString(path.wstring());
-#else
- result = QString::fromStdString(path.string());
-#endif
-}
-
-std::filesystem::path PathFromQString(const QString& path) {
-#ifdef _WIN32
- return std::filesystem::path(path.toStdWString());
-#else
- return std::filesystem::path(path.toStdString());
-#endif
-}
-#endif
-
} // namespace Common::FS
diff --git a/src/common/path_util.h b/src/common/path_util.h
index af40b3974..0a0234eba 100644
--- a/src/common/path_util.h
+++ b/src/common/path_util.h
@@ -7,10 +7,6 @@
#include
#include
-#ifdef ENABLE_QT_GUI
-class QString; // to avoid including in this header
-#endif
-
namespace Common::FS {
enum class PathType {
@@ -99,25 +95,6 @@ constexpr auto LOG_FILE = "shad_log.txt";
*/
void SetUserPath(PathType user_path, const std::filesystem::path& new_path);
-#ifdef ENABLE_QT_GUI
-/**
- * Converts an std::filesystem::path to a QString.
- * The native underlying string of a path is wstring on Windows and string on POSIX.
- *
- * @param result The resulting QString
- * @param path The path to convert
- */
-void PathToQString(QString& result, const std::filesystem::path& path);
-
-/**
- * Converts a QString to an std::filesystem::path.
- * The native underlying string of a path is wstring on Windows and string on POSIX.
- *
- * @param path The path to convert
- */
-[[nodiscard]] std::filesystem::path PathFromQString(const QString& path);
-#endif
-
/**
* Recursively searches for a game directory by its ID.
* Limits search depth to prevent excessive filesystem traversal.
diff --git a/src/core/ipc/ipc.cpp b/src/core/ipc/ipc.cpp
index da5e0d667..0f416982d 100644
--- a/src/core/ipc/ipc.cpp
+++ b/src/core/ipc/ipc.cpp
@@ -70,6 +70,8 @@ void IPC::Init() {
return;
}
+ Config::setLoadAutoPatches(false);
+
input_thread = std::jthread([this] {
Common::SetCurrentThreadName("IPC Read thread");
this->InputLoop();
diff --git a/src/emulator.cpp b/src/emulator.cpp
index 625027c11..f9e1f7959 100644
--- a/src/emulator.cpp
+++ b/src/emulator.cpp
@@ -15,10 +15,6 @@
#include "common/logging/backend.h"
#include "common/logging/log.h"
#include "core/ipc/ipc.h"
-#ifdef ENABLE_QT_GUI
-#include
-#endif
-#include "common/assert.h"
#ifdef ENABLE_DISCORD_RPC
#include "common/discord_rpc_handler.h"
#endif
@@ -341,8 +337,6 @@ void Emulator::Run(std::filesystem::path file, std::vector args,
}
#endif
- // Start the timer (Play Time)
-#ifdef ENABLE_QT_GUI
if (!id.empty()) {
start_time = std::chrono::steady_clock::now();
@@ -354,7 +348,6 @@ void Emulator::Run(std::filesystem::path file, std::vector args,
}
}).detach();
}
-#endif
args.insert(args.begin(), eboot_name.generic_string());
linker->Execute(args);
@@ -364,9 +357,7 @@ void Emulator::Run(std::filesystem::path file, std::vector args,
window->WaitEvent();
}
-#ifdef ENABLE_QT_GUI
UpdatePlayTime(id);
-#endif
std::quick_exit(0);
}
@@ -389,9 +380,9 @@ void Emulator::Restart(std::filesystem::path eboot_path,
args.push_back("--ignore-game-patch");
}
- if (!MemoryPatcher::patchFile.empty()) {
+ if (!MemoryPatcher::patch_file.empty()) {
args.push_back("--patch");
- args.push_back(MemoryPatcher::patchFile);
+ args.push_back(MemoryPatcher::patch_file);
}
args.push_back("--wait-for-pid");
diff --git a/src/main.cpp b/src/main.cpp
index 4ba094314..32b583e10 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -86,13 +86,14 @@ int main(int argc, char* argv[]) {
{"-p",
[&](int& i) {
if (i + 1 < argc) {
- MemoryPatcher::patchFile = argv[++i];
+ MemoryPatcher::patch_file = argv[++i];
} else {
std::cerr << "Error: Missing argument for -p/--patch\n";
exit(1);
}
}},
{"--patch", [&](int& i) { arg_map["-p"](i); }},
+
{"-i", [&](int&) { Core::FileSys::MntPoints::ignore_game_patches = true; }},
{"--ignore-game-patch", [&](int& i) { arg_map["-i"](i); }},
{"-f",
diff --git a/src/qt_gui/about_dialog.cpp b/src/qt_gui/about_dialog.cpp
deleted file mode 100644
index 627a0c052..000000000
--- a/src/qt_gui/about_dialog.cpp
+++ /dev/null
@@ -1,195 +0,0 @@
-// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include "about_dialog.h"
-#include "main_window_themes.h"
-#include "ui_about_dialog.h"
-
-AboutDialog::AboutDialog(std::shared_ptr gui_settings, QWidget* parent)
- : QDialog(parent), ui(new Ui::AboutDialog), m_gui_settings(std::move(gui_settings)) {
- ui->setupUi(this);
- preloadImages();
-
- ui->image_1->setAttribute(Qt::WA_Hover, true);
- ui->image_2->setAttribute(Qt::WA_Hover, true);
- ui->image_3->setAttribute(Qt::WA_Hover, true);
- ui->image_4->setAttribute(Qt::WA_Hover, true);
- ui->image_5->setAttribute(Qt::WA_Hover, true);
-
- ui->image_1->installEventFilter(this);
- ui->image_2->installEventFilter(this);
- ui->image_3->installEventFilter(this);
- ui->image_4->installEventFilter(this);
- ui->image_5->installEventFilter(this);
-}
-
-AboutDialog::~AboutDialog() {
- delete ui;
-}
-
-void AboutDialog::preloadImages() {
- originalImages[0] = ui->image_1->pixmap().copy();
- originalImages[1] = ui->image_2->pixmap().copy();
- originalImages[2] = ui->image_3->pixmap().copy();
- originalImages[3] = ui->image_4->pixmap().copy();
- originalImages[4] = ui->image_5->pixmap().copy();
-
- for (int i = 0; i < 5; ++i) {
- QImage image = originalImages[i].toImage();
- for (int y = 0; y < image.height(); ++y) {
- for (int x = 0; x < image.width(); ++x) {
- QColor color = image.pixelColor(x, y);
- color.setRed(255 - color.red());
- color.setGreen(255 - color.green());
- color.setBlue(255 - color.blue());
- image.setPixelColor(x, y, color);
- }
- }
- invertedImages[i] = QPixmap::fromImage(image);
- }
- updateImagesForCurrentTheme();
-}
-
-void AboutDialog::updateImagesForCurrentTheme() {
- Theme currentTheme = static_cast(m_gui_settings->GetValue(gui::gen_theme).toInt());
- bool isDarkTheme = (currentTheme == Theme::Dark || currentTheme == Theme::Green ||
- currentTheme == Theme::Blue || currentTheme == Theme::Violet);
- if (isDarkTheme) {
- ui->image_1->setPixmap(invertedImages[0]);
- ui->image_2->setPixmap(invertedImages[1]);
- ui->image_3->setPixmap(invertedImages[2]);
- ui->image_4->setPixmap(invertedImages[3]);
- ui->image_5->setPixmap(invertedImages[4]);
- } else {
- ui->image_1->setPixmap(originalImages[0]);
- ui->image_2->setPixmap(originalImages[1]);
- ui->image_3->setPixmap(originalImages[2]);
- ui->image_4->setPixmap(originalImages[3]);
- ui->image_5->setPixmap(originalImages[4]);
- }
-}
-
-bool AboutDialog::eventFilter(QObject* obj, QEvent* event) {
- if (event->type() == QEvent::Enter) {
- if (obj == ui->image_1) {
- if (isDarkTheme()) {
- ui->image_1->setPixmap(originalImages[0]);
- } else {
- ui->image_1->setPixmap(invertedImages[0]);
- }
- applyHoverEffect(ui->image_1);
- } else if (obj == ui->image_2) {
- if (isDarkTheme()) {
- ui->image_2->setPixmap(originalImages[1]);
- } else {
- ui->image_2->setPixmap(invertedImages[1]);
- }
- applyHoverEffect(ui->image_2);
- } else if (obj == ui->image_3) {
- if (isDarkTheme()) {
- ui->image_3->setPixmap(originalImages[2]);
- } else {
- ui->image_3->setPixmap(invertedImages[2]);
- }
- applyHoverEffect(ui->image_3);
- } else if (obj == ui->image_4) {
- if (isDarkTheme()) {
- ui->image_4->setPixmap(originalImages[3]);
- } else {
- ui->image_4->setPixmap(invertedImages[3]);
- }
- applyHoverEffect(ui->image_4);
- } else if (obj == ui->image_5) {
- if (isDarkTheme()) {
- ui->image_5->setPixmap(originalImages[4]);
- } else {
- ui->image_5->setPixmap(invertedImages[4]);
- }
- applyHoverEffect(ui->image_5);
- }
- } else if (event->type() == QEvent::Leave) {
- if (obj == ui->image_1) {
- if (isDarkTheme()) {
- ui->image_1->setPixmap(invertedImages[0]);
- } else {
- ui->image_1->setPixmap(originalImages[0]);
- }
- removeHoverEffect(ui->image_1);
- } else if (obj == ui->image_2) {
- if (isDarkTheme()) {
- ui->image_2->setPixmap(invertedImages[1]);
- } else {
- ui->image_2->setPixmap(originalImages[1]);
- }
- removeHoverEffect(ui->image_2);
- } else if (obj == ui->image_3) {
- if (isDarkTheme()) {
- ui->image_3->setPixmap(invertedImages[2]);
- } else {
- ui->image_3->setPixmap(originalImages[2]);
- }
- removeHoverEffect(ui->image_3);
- } else if (obj == ui->image_4) {
- if (isDarkTheme()) {
- ui->image_4->setPixmap(invertedImages[3]);
- } else {
- ui->image_4->setPixmap(originalImages[3]);
- }
- removeHoverEffect(ui->image_4);
- } else if (obj == ui->image_5) {
- if (isDarkTheme()) {
- ui->image_5->setPixmap(invertedImages[4]);
- } else {
- ui->image_5->setPixmap(originalImages[4]);
- }
- removeHoverEffect(ui->image_5);
- }
- } else if (event->type() == QEvent::MouseButtonPress) {
- if (obj == ui->image_1) {
- QDesktopServices::openUrl(QUrl("https://github.com/shadps4-emu/shadPS4"));
- } else if (obj == ui->image_2) {
- QDesktopServices::openUrl(QUrl("https://discord.gg/bFJxfftGW6"));
- } else if (obj == ui->image_3) {
- QDesktopServices::openUrl(QUrl("https://www.youtube.com/@shadPS4/videos"));
- } else if (obj == ui->image_4) {
- QDesktopServices::openUrl(QUrl("https://ko-fi.com/shadps4"));
- } else if (obj == ui->image_5) {
- QDesktopServices::openUrl(QUrl("https://shadps4.net"));
- }
- return true;
- }
- return QDialog::eventFilter(obj, event);
-}
-
-void AboutDialog::applyHoverEffect(QLabel* label) {
- QColor shadowColor = isDarkTheme() ? QColor(0, 0, 0) : QColor(169, 169, 169);
- QGraphicsDropShadowEffect* shadow = new QGraphicsDropShadowEffect;
- shadow->setBlurRadius(5);
- shadow->setXOffset(2);
- shadow->setYOffset(2);
- shadow->setColor(shadowColor);
- label->setGraphicsEffect(shadow);
-}
-
-void AboutDialog::removeHoverEffect(QLabel* label) {
- QColor shadowColor = isDarkTheme() ? QColor(50, 50, 50) : QColor(169, 169, 169);
- QGraphicsDropShadowEffect* shadow = new QGraphicsDropShadowEffect;
- shadow->setBlurRadius(3);
- shadow->setXOffset(0);
- shadow->setYOffset(0);
- shadow->setColor(shadowColor);
- label->setGraphicsEffect(shadow);
-}
-
-bool AboutDialog::isDarkTheme() const {
- Theme currentTheme = static_cast(m_gui_settings->GetValue(gui::gen_theme).toInt());
- return currentTheme == Theme::Dark || currentTheme == Theme::Green ||
- currentTheme == Theme::Blue || currentTheme == Theme::Violet;
-}
diff --git a/src/qt_gui/about_dialog.h b/src/qt_gui/about_dialog.h
deleted file mode 100644
index b74cdfd1a..000000000
--- a/src/qt_gui/about_dialog.h
+++ /dev/null
@@ -1,38 +0,0 @@
-// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-#pragma once
-
-#include
-#include
-#include
-#include
-#include
-#include "gui_settings.h"
-
-namespace Ui {
-class AboutDialog;
-}
-
-class AboutDialog : public QDialog {
- Q_OBJECT
-
-public:
- explicit AboutDialog(std::shared_ptr gui_settings, QWidget* parent = nullptr);
- ~AboutDialog();
- bool eventFilter(QObject* obj, QEvent* event);
-
-private:
- Ui::AboutDialog* ui;
-
- void preloadImages();
- void updateImagesForCurrentTheme();
- void applyHoverEffect(QLabel* label);
- void removeHoverEffect(QLabel* label);
-
- bool isDarkTheme() const;
-
- QPixmap originalImages[5];
- QPixmap invertedImages[5];
- std::shared_ptr m_gui_settings;
-};
diff --git a/src/qt_gui/about_dialog.ui b/src/qt_gui/about_dialog.ui
deleted file mode 100644
index 0e9ef222c..000000000
--- a/src/qt_gui/about_dialog.ui
+++ /dev/null
@@ -1,233 +0,0 @@
-
-
-
- AboutDialog
-
-
-
- 0
- 0
- 780
- 310
-
-
-
- About shadPS4
-
-
-
- :/images/shadps4.ico:/images/shadps4.ico
-
-
-
-
- 15
- 15
- 271
- 271
-
-
-
- QFrame::NoFrame
-
-
-
-
-
- :/images/shadps4.png
-
-
- true
-
-
-
-
-
- 310
- 15
- 171
- 41
-
-
-
-
- 24
- true
-
-
-
- shadPS4
-
-
-
-
-
- 310
- 60
- 451
- 70
-
-
-
-
- 14
-
-
-
- shadPS4 is an experimental open-source emulator for the PlayStation 4.
-
-
- true
-
-
-
-
-
- 310
- 130
- 451
- 70
-
-
-
-
- 14
-
-
-
- This software should not be used to play games you have not legally obtained.
-
-
- true
-
-
-
-
-
- 310
- 210
- 80
- 80
-
-
-
- ArrowCursor
-
-
- QFrame::NoFrame
-
-
-
-
-
- :/images/github.png
-
-
- true
-
-
-
-
-
- 400
- 210
- 80
- 80
-
-
-
- ArrowCursor
-
-
- QFrame::NoFrame
-
-
-
-
-
- :/images/discord.png
-
-
- true
-
-
-
-
-
- 490
- 210
- 80
- 80
-
-
-
- ArrowCursor
-
-
- QFrame::NoFrame
-
-
-
-
-
- :/images/youtube.png
-
-
- true
-
-
-
-
-
- 580
- 210
- 80
- 80
-
-
-
- ArrowCursor
-
-
- QFrame::NoFrame
-
-
-
-
-
- :/images/ko-fi.png
-
-
- true
-
-
-
-
-
- 670
- 210
- 80
- 80
-
-
-
- ArrowCursor
-
-
- QFrame::NoFrame
-
-
-
-
-
- :/images/website.png
-
-
- true
-
-
-
-
-
-
diff --git a/src/qt_gui/background_music_player.cpp b/src/qt_gui/background_music_player.cpp
deleted file mode 100644
index e2b7177b3..000000000
--- a/src/qt_gui/background_music_player.cpp
+++ /dev/null
@@ -1,44 +0,0 @@
-// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-#include "background_music_player.h"
-
-BackgroundMusicPlayer::BackgroundMusicPlayer(QObject* parent) : QObject(parent) {
- m_mediaPlayer = new QMediaPlayer(this);
- m_audioOutput = new QAudioOutput(this);
- m_mediaPlayer->setAudioOutput(m_audioOutput);
-}
-
-void BackgroundMusicPlayer::setVolume(int volume) {
- float linearVolume = QAudio::convertVolume(volume / 100.0f, QAudio::LogarithmicVolumeScale,
- QAudio::LinearVolumeScale);
- m_audioOutput->setVolume(linearVolume);
-}
-
-void BackgroundMusicPlayer::playMusic(const QString& snd0path, bool loops) {
- if (snd0path.isEmpty()) {
- stopMusic();
- return;
- }
- const auto newMusic = QUrl::fromLocalFile(snd0path);
- if (m_mediaPlayer->playbackState() == QMediaPlayer::PlayingState &&
- m_currentMusic == newMusic) {
- // already playing the correct music
- return;
- }
-
- if (loops) {
- m_mediaPlayer->setLoops(QMediaPlayer::Infinite);
- } else {
- m_mediaPlayer->setLoops(1);
- }
-
- m_currentMusic = newMusic;
- m_mediaPlayer->setSource(newMusic);
- m_mediaPlayer->play();
-}
-
-void BackgroundMusicPlayer::stopMusic() {
- m_mediaPlayer->stop();
- m_mediaPlayer->setSource(QUrl(""));
-}
diff --git a/src/qt_gui/background_music_player.h b/src/qt_gui/background_music_player.h
deleted file mode 100644
index 078710a01..000000000
--- a/src/qt_gui/background_music_player.h
+++ /dev/null
@@ -1,29 +0,0 @@
-// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-#pragma once
-
-#include
-#include
-#include
-
-class BackgroundMusicPlayer : public QObject {
- Q_OBJECT
-
-public:
- static BackgroundMusicPlayer& getInstance() {
- static BackgroundMusicPlayer instance;
- return instance;
- }
-
- void setVolume(int volume);
- void playMusic(const QString& snd0path, bool loops = true);
- void stopMusic();
-
-private:
- BackgroundMusicPlayer(QObject* parent = nullptr);
-
- QMediaPlayer* m_mediaPlayer;
- QAudioOutput* m_audioOutput;
- QUrl m_currentMusic;
-};
diff --git a/src/qt_gui/cheats_patches.cpp b/src/qt_gui/cheats_patches.cpp
deleted file mode 100644
index 1a2e3e19f..000000000
--- a/src/qt_gui/cheats_patches.cpp
+++ /dev/null
@@ -1,1351 +0,0 @@
-// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-
-#include "cheats_patches.h"
-#include "common/config.h"
-#include "common/logging/log.h"
-#include "common/memory_patcher.h"
-#include "common/path_util.h"
-#include "core/module.h"
-
-CheatsPatches::CheatsPatches(const QString& gameName, const QString& gameSerial,
- const QString& gameVersion, const QString& gameSize,
- const QPixmap& gameImage, QWidget* parent)
- : QWidget(parent), m_gameName(gameName), m_gameSerial(gameSerial), m_gameVersion(gameVersion),
- m_gameSize(gameSize), m_gameImage(gameImage), manager(new QNetworkAccessManager(this)) {
- setupUI();
- resize(500, 400);
- setWindowTitle(tr("Cheats / Patches for ") + m_gameName);
-}
-
-CheatsPatches::~CheatsPatches() {}
-
-void CheatsPatches::setupUI() {
-
- // clang-format off
- defaultTextEdit_MSG = tr("Cheats/Patches are experimental.\\nUse with caution.\\n\\nDownload cheats individually by selecting the repository and clicking the download button.\\nIn the Patches tab, you can download all patches at once, choose which ones you want to use, and save your selection.\\n\\nSince we do not develop the Cheats/Patches,\\nplease report issues to the cheat author.\\n\\nCreated a new cheat? Visit:\\n").replace("\\n", "\n")+"https://github.com/shadps4-emu/ps4_cheats";
- CheatsNotFound_MSG = tr("No Cheats found for this game in this version of the selected repository,try another repository or a different version of the game.");
- CheatsDownloadedSuccessfully_MSG = tr("You have successfully downloaded the cheats for this version of the game from the selected repository. You can try downloading from another repository, if it is available it will also be possible to use it by selecting the file from the list.");
- DownloadComplete_MSG = tr("Patches Downloaded Successfully! All Patches available for all games have been downloaded, there is no need to download them individually for each game as happens in Cheats. If the patch does not appear, it may be that it does not exist for the specific serial and version of the game.");
- // clang-format on
-
- QString CHEATS_DIR_QString;
- Common::FS::PathToQString(CHEATS_DIR_QString,
- Common::FS::GetUserPath(Common::FS::PathType::CheatsDir));
- QString PATCHS_DIR_QString;
- Common::FS::PathToQString(PATCHS_DIR_QString,
- Common::FS::GetUserPath(Common::FS::PathType::PatchesDir));
- QString NameCheatJson = m_gameSerial + "_" + m_gameVersion + ".json";
- m_cheatFilePath = CHEATS_DIR_QString + "/" + NameCheatJson;
-
- QHBoxLayout* mainLayout = new QHBoxLayout(this);
-
- // Create the game info group box
- QGroupBox* gameInfoGroupBox = new QGroupBox();
- QVBoxLayout* gameInfoLayout = new QVBoxLayout(gameInfoGroupBox);
- gameInfoLayout->setAlignment(Qt::AlignTop);
-
- QLabel* gameImageLabel = new QLabel();
- if (!m_gameImage.isNull()) {
- gameImageLabel->setPixmap(
- m_gameImage.scaled(275, 275, Qt::KeepAspectRatio, Qt::SmoothTransformation));
- } else {
- gameImageLabel->setText(tr("No Image Available"));
- }
- gameImageLabel->setAlignment(Qt::AlignCenter);
- gameInfoLayout->addWidget(gameImageLabel, 0, Qt::AlignCenter);
-
- QLabel* gameNameLabel = new QLabel(m_gameName);
- gameNameLabel->setAlignment(Qt::AlignLeft);
- gameNameLabel->setWordWrap(true);
- gameInfoLayout->addWidget(gameNameLabel);
-
- QLabel* gameSerialLabel = new QLabel(tr("Serial: ") + m_gameSerial);
- gameSerialLabel->setAlignment(Qt::AlignLeft);
- gameInfoLayout->addWidget(gameSerialLabel);
-
- QLabel* gameVersionLabel = new QLabel(tr("Version: ") + m_gameVersion);
- gameVersionLabel->setAlignment(Qt::AlignLeft);
- gameInfoLayout->addWidget(gameVersionLabel);
-
- if (Config::GetLoadGameSizeEnabled()) {
- QLabel* gameSizeLabel = new QLabel(tr("Size: ") + m_gameSize);
- gameSizeLabel->setAlignment(Qt::AlignLeft);
- gameInfoLayout->addWidget(gameSizeLabel);
- }
-
- // Add a text area for instructions and 'Patch' descriptions
- instructionsTextEdit = new QTextEdit();
- instructionsTextEdit->setText(defaultTextEdit_MSG);
- instructionsTextEdit->setReadOnly(true);
- instructionsTextEdit->setFixedHeight(290);
- gameInfoLayout->addWidget(instructionsTextEdit);
-
- // Create the tab widget
- QTabWidget* tabWidget = new QTabWidget();
- QWidget* cheatsTab = new QWidget();
- QWidget* patchesTab = new QWidget();
-
- // Layouts for the tabs
- QVBoxLayout* cheatsLayout = new QVBoxLayout();
- QVBoxLayout* patchesLayout = new QVBoxLayout();
-
- // Setup the cheats tab
- QGroupBox* cheatsGroupBox = new QGroupBox();
- rightLayout = new QVBoxLayout(cheatsGroupBox);
- rightLayout->setAlignment(Qt::AlignTop);
-
- cheatsGroupBox->setLayout(rightLayout);
- QScrollArea* scrollArea = new QScrollArea();
- scrollArea->setWidgetResizable(true);
- scrollArea->setWidget(cheatsGroupBox);
- scrollArea->setMinimumHeight(490);
- cheatsLayout->addWidget(scrollArea);
-
- // QListView
- listView_selectFile = new QListView();
- listView_selectFile->setSelectionMode(QAbstractItemView::SingleSelection);
- listView_selectFile->setEditTriggers(QAbstractItemView::NoEditTriggers);
-
- // Add QListView to layout
- QVBoxLayout* fileListLayout = new QVBoxLayout();
- fileListLayout->addWidget(new QLabel(tr("Select Cheat File:")));
- fileListLayout->addWidget(listView_selectFile);
- cheatsLayout->addLayout(fileListLayout, 2);
-
- // Call the method to fill the list of cheat files
- populateFileListCheats();
-
- QLabel* repositoryLabel = new QLabel(tr("Repository:"));
- repositoryLabel->setAlignment(Qt::AlignLeft);
- repositoryLabel->setAlignment(Qt::AlignVCenter);
-
- // Add a combo box and a download button
- QHBoxLayout* controlLayout = new QHBoxLayout();
- controlLayout->addWidget(repositoryLabel);
- controlLayout->setAlignment(Qt::AlignLeft);
- QComboBox* downloadComboBox = new QComboBox();
-
- downloadComboBox->addItem("GoldHEN", "GoldHEN");
- downloadComboBox->addItem("shadPS4", "shadPS4");
-
- controlLayout->addWidget(downloadComboBox);
-
- QPushButton* downloadButton = new QPushButton(tr("Download Cheats"));
- connect(downloadButton, &QPushButton::clicked, [this, downloadComboBox]() {
- QString source = downloadComboBox->currentData().toString();
- downloadCheats(source, m_gameSerial, m_gameVersion, true);
- });
-
- QPushButton* deleteCheatButton = new QPushButton(tr("Delete File"));
- connect(deleteCheatButton, &QPushButton::clicked, [this, CHEATS_DIR_QString]() {
- QStringListModel* model = qobject_cast(listView_selectFile->model());
- if (!model) {
- return;
- }
- QItemSelectionModel* selectionModel = listView_selectFile->selectionModel();
- if (!selectionModel) {
- return;
- }
- QModelIndexList selectedIndexes = selectionModel->selectedIndexes();
- if (selectedIndexes.isEmpty()) {
- QMessageBox::warning(
- this, tr("Delete File"),
- tr("No files selected.") + "\n" +
- tr("You can delete the cheats you don't want after downloading them."));
- return;
- }
- QModelIndex selectedIndex = selectedIndexes.first();
- QString selectedFileName = model->data(selectedIndex).toString();
-
- int ret = QMessageBox::warning(
- this, tr("Delete File"),
- QString(tr("Do you want to delete the selected file?\\n%1").replace("\\n", "\n"))
- .arg(selectedFileName),
- QMessageBox::Yes | QMessageBox::No);
-
- if (ret == QMessageBox::Yes) {
- QString filePath = CHEATS_DIR_QString + "/" + selectedFileName;
- QFile::remove(filePath);
- populateFileListCheats();
- }
- });
-
- QPushButton* closeButton = new QPushButton(tr("Close"));
- connect(closeButton, &QPushButton::clicked, [this]() { QWidget::close(); });
-
- controlLayout->addWidget(downloadButton);
- controlLayout->addWidget(deleteCheatButton);
- controlLayout->addWidget(closeButton);
-
- cheatsLayout->addLayout(controlLayout);
- cheatsTab->setLayout(cheatsLayout);
-
- // Setup the patches tab
- QGroupBox* patchesGroupBox = new QGroupBox();
- patchesGroupBoxLayout = new QVBoxLayout(patchesGroupBox);
- patchesGroupBoxLayout->setAlignment(Qt::AlignTop);
- patchesGroupBox->setLayout(patchesGroupBoxLayout);
-
- QScrollArea* patchesScrollArea = new QScrollArea();
- patchesScrollArea->setWidgetResizable(true);
- patchesScrollArea->setWidget(patchesGroupBox);
- patchesScrollArea->setMinimumHeight(490);
- patchesLayout->addWidget(patchesScrollArea);
-
- // List of files in patchesListView
- patchesListView = new QListView();
- patchesListView->setSelectionMode(QAbstractItemView::SingleSelection);
- patchesListView->setEditTriggers(QAbstractItemView::NoEditTriggers);
-
- // Add new label "Select Patch File:" above the QListView
- QVBoxLayout* patchFileListLayout = new QVBoxLayout();
- patchFileListLayout->addWidget(new QLabel(tr("Select Patch File:")));
- patchFileListLayout->addWidget(patchesListView);
- patchesLayout->addLayout(patchFileListLayout, 2);
-
- QStringListModel* patchesModel = new QStringListModel();
- patchesListView->setModel(patchesModel);
-
- QHBoxLayout* patchesControlLayout = new QHBoxLayout();
-
- QLabel* patchesRepositoryLabel = new QLabel(tr("Repository:"));
- patchesRepositoryLabel->setAlignment(Qt::AlignLeft);
- patchesRepositoryLabel->setAlignment(Qt::AlignVCenter);
- patchesControlLayout->addWidget(patchesRepositoryLabel);
-
- // Add the combo box with options
- patchesComboBox = new QComboBox();
- patchesComboBox->addItem("shadPS4", "shadPS4");
- patchesComboBox->addItem("GoldHEN", "GoldHEN");
- patchesControlLayout->addWidget(patchesComboBox);
-
- QPushButton* patchesButton = new QPushButton(tr("Download Patches"));
- connect(patchesButton, &QPushButton::clicked, [this]() {
- QString selectedOption = patchesComboBox->currentData().toString();
- downloadPatches(selectedOption, true);
- });
- patchesControlLayout->addWidget(patchesButton);
-
- QPushButton* deletePatchButton = new QPushButton(tr("Delete File"));
- connect(deletePatchButton, &QPushButton::clicked, [this, PATCHS_DIR_QString]() {
- QStringListModel* model = qobject_cast(patchesListView->model());
- if (!model) {
- return;
- }
- QItemSelectionModel* selectionModel = patchesListView->selectionModel();
- if (!selectionModel) {
- return;
- }
- QModelIndexList selectedIndexes = selectionModel->selectedIndexes();
- if (selectedIndexes.isEmpty()) {
- QMessageBox::warning(this, tr("Delete File"), tr("No files selected."));
- return;
- }
- QModelIndex selectedIndex = selectedIndexes.first();
- QString selectedFileName = model->data(selectedIndex).toString();
-
- int ret = QMessageBox::warning(
- this, tr("Delete File"),
- QString(tr("Do you want to delete the selected file?\\n%1").replace("\\n", "\n"))
- .arg(selectedFileName),
- QMessageBox::Yes | QMessageBox::No);
-
- if (ret == QMessageBox::Yes) {
- QString fileName = selectedFileName.split('|').first().trimmed();
- QString directoryName = selectedFileName.split('|').last().trimmed();
- QString filePath = PATCHS_DIR_QString + "/" + directoryName + "/" + fileName;
-
- QFile::remove(filePath);
- createFilesJson(directoryName);
- populateFileListPatches();
- }
- });
-
- QPushButton* saveButton = new QPushButton(tr("Save"));
- connect(saveButton, &QPushButton::clicked, this, &CheatsPatches::onSaveButtonClicked);
-
- patchesControlLayout->addWidget(deletePatchButton);
- patchesControlLayout->addWidget(saveButton);
-
- patchesLayout->addLayout(patchesControlLayout);
- patchesTab->setLayout(patchesLayout);
-
- tabWidget->addTab(cheatsTab, tr("Cheats"));
- tabWidget->addTab(patchesTab, tr("Patches"));
-
- connect(tabWidget, &QTabWidget::currentChanged, this, [this](int index) {
- if (index == 1) {
- populateFileListPatches();
- }
- });
-
- mainLayout->addWidget(gameInfoGroupBox, 1);
- mainLayout->addWidget(tabWidget, 3);
-
- manager = new QNetworkAccessManager(this);
-
- setLayout(mainLayout);
-}
-
-void CheatsPatches::onSaveButtonClicked() {
- // Get the name of the selected folder in the patchesListView
- QString selectedPatchName;
- QModelIndexList selectedIndexes = patchesListView->selectionModel()->selectedIndexes();
- if (selectedIndexes.isEmpty()) {
- QMessageBox::warning(this, tr("Error"), tr("No patch selected."));
- return;
- }
- selectedPatchName = patchesListView->model()->data(selectedIndexes.first()).toString();
- int separatorIndex = selectedPatchName.indexOf(" | ");
- selectedPatchName = selectedPatchName.mid(separatorIndex + 3);
-
- QString patchDir;
- Common::FS::PathToQString(patchDir, Common::FS::GetUserPath(Common::FS::PathType::PatchesDir));
- patchDir += "/" + selectedPatchName;
-
- QString filesJsonPath = patchDir + "/files.json";
- QFile jsonFile(filesJsonPath);
- if (!jsonFile.open(QIODevice::ReadOnly)) {
- QMessageBox::critical(this, tr("Error"), tr("Unable to open files.json for reading."));
- return;
- }
-
- QByteArray jsonData = jsonFile.readAll();
- jsonFile.close();
-
- QJsonDocument jsonDoc = QJsonDocument::fromJson(jsonData);
- QJsonObject jsonObject = jsonDoc.object();
-
- QString selectedFileName;
- QString serial = m_gameSerial;
-
- for (auto it = jsonObject.constBegin(); it != jsonObject.constEnd(); ++it) {
- QString filePath = it.key();
- QJsonArray idsArray = it.value().toArray();
-
- if (idsArray.contains(QJsonValue(serial))) {
- selectedFileName = filePath;
- break;
- }
- }
-
- if (selectedFileName.isEmpty()) {
- QMessageBox::critical(this, tr("Error"), tr("No patch file found for the current serial."));
- return;
- }
-
- QString filePath = patchDir + "/" + selectedFileName;
- QFile file(filePath);
- if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
- QMessageBox::critical(this, tr("Error"), tr("Unable to open the file for reading."));
- return;
- }
-
- QByteArray xmlData = file.readAll();
- file.close();
-
- QString newXmlData;
- QXmlStreamWriter xmlWriter(&newXmlData);
- xmlWriter.setAutoFormatting(true);
- xmlWriter.writeStartDocument();
-
- QXmlStreamReader xmlReader(xmlData);
-
- while (!xmlReader.atEnd()) {
- xmlReader.readNext();
-
- if (xmlReader.isStartElement()) {
- if (xmlReader.name() == QStringLiteral("Metadata")) {
- xmlWriter.writeStartElement(xmlReader.name().toString());
-
- QString name = xmlReader.attributes().value("Name").toString();
- QString version = xmlReader.attributes().value("AppVer").toString();
-
- bool versionMatch = version == m_gameVersion;
- bool isEnabled = false;
- bool foundPatchInfo = false;
-
- // Check and update the isEnabled attribute
- for (const QXmlStreamAttribute& attr : xmlReader.attributes()) {
- if (attr.name() == QStringLiteral("isEnabled"))
- continue;
- xmlWriter.writeAttribute(attr.name().toString(), attr.value().toString());
- }
-
- auto it = m_patchInfos.find(name);
- if (it != m_patchInfos.end()) {
- QCheckBox* checkBox = findCheckBoxByName(it->name);
- if (checkBox) {
- foundPatchInfo = true;
- isEnabled = checkBox->isChecked();
- }
- }
- if (!foundPatchInfo) {
- auto maskIt = m_patchInfos.find(name + " (any version)");
- if (maskIt != m_patchInfos.end()) {
- QCheckBox* checkBox = findCheckBoxByName(maskIt->name);
- if (checkBox) {
- versionMatch = true;
- foundPatchInfo = true;
- isEnabled = checkBox->isChecked();
- }
- }
- }
- if (foundPatchInfo) {
- xmlWriter.writeAttribute("isEnabled",
- (isEnabled && versionMatch) ? "true" : "false");
- }
- } else {
- xmlWriter.writeStartElement(xmlReader.name().toString());
- for (const QXmlStreamAttribute& attr : xmlReader.attributes()) {
- xmlWriter.writeAttribute(attr.name().toString(), attr.value().toString());
- }
- }
- } else if (xmlReader.isEndElement()) {
- xmlWriter.writeEndElement();
- } else if (xmlReader.isCharacters() && !xmlReader.isWhitespace()) {
- xmlWriter.writeCharacters(xmlReader.text().toString());
- }
- }
-
- xmlWriter.writeEndDocument();
-
- if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
- QMessageBox::critical(this, tr("Error"), tr("Unable to open the file for writing."));
- return;
- }
-
- QTextStream textStream(&file);
- textStream << newXmlData;
- file.close();
-
- if (xmlReader.hasError()) {
- QMessageBox::critical(this, tr("Error"),
- tr("Failed to parse XML: ") + "\n" + xmlReader.errorString());
- } else {
- QMessageBox::information(this, tr("Success"), tr("Options saved successfully."));
- }
-
- QWidget::close();
-}
-
-QCheckBox* CheatsPatches::findCheckBoxByName(const QString& name) {
- for (int i = 0; i < patchesGroupBoxLayout->count(); ++i) {
- QLayoutItem* item = patchesGroupBoxLayout->itemAt(i);
- if (item) {
- QWidget* widget = item->widget();
- QCheckBox* checkBox = qobject_cast(widget);
- if (checkBox) {
- const auto patchName = checkBox->property("patchName");
- if (patchName.isValid() && patchName.toString().toStdString().find(
- name.toStdString()) != std::string::npos) {
- return checkBox;
- }
- }
- }
- }
- return nullptr;
-}
-
-void CheatsPatches::downloadCheats(const QString& source, const QString& gameSerial,
- const QString& gameVersion, const bool showMessageBox) {
- QDir dir(Common::FS::GetUserPath(Common::FS::PathType::CheatsDir));
- if (!dir.exists()) {
- dir.mkpath(".");
- }
-
- QString url;
- if (source == "GoldHEN") {
- url = "https://raw.githubusercontent.com/GoldHEN/GoldHEN_Cheat_Repository/main/json.txt";
- } else if (source == "shadPS4") {
- url = "https://raw.githubusercontent.com/shadps4-emu/ps4_cheats/main/CHEATS_JSON.txt";
- } else {
- QMessageBox::warning(this, tr("Invalid Source"),
- QString(tr("The selected source is invalid.") + "\n%1").arg(source));
- return;
- }
-
- QNetworkRequest request(url);
- QNetworkReply* reply = manager->get(request);
-
- connect(reply, &QNetworkReply::finished, [=, this]() {
- if (reply->error() == QNetworkReply::NoError) {
- QByteArray jsonData = reply->readAll();
- bool foundFiles = false;
-
- if (source == "GoldHEN" || source == "shadPS4") {
- QString textContent(jsonData);
- QRegularExpression regex(
- QString("%1_%2[^=]*\\.json").arg(gameSerial).arg(gameVersion));
- QRegularExpressionMatchIterator matches = regex.globalMatch(textContent);
- QString baseUrl;
-
- if (source == "GoldHEN") {
- baseUrl = "https://raw.githubusercontent.com/GoldHEN/GoldHEN_Cheat_Repository/"
- "main/json/";
- } else {
- baseUrl = "https://raw.githubusercontent.com/shadps4-emu/ps4_cheats/"
- "main/CHEATS/";
- }
-
- while (matches.hasNext()) {
- QRegularExpressionMatch match = matches.next();
- QString fileName = match.captured(0);
-
- if (!fileName.isEmpty()) {
- QString newFileName = fileName;
- int dotIndex = newFileName.lastIndexOf('.');
- if (dotIndex != -1) {
-
- if (source == "GoldHEN") {
- newFileName.insert(dotIndex, "_GoldHEN");
- } else {
- newFileName.insert(dotIndex, "_shadPS4");
- }
- }
- QString fileUrl = baseUrl + fileName;
- QString localFilePath = dir.filePath(newFileName);
-
- if (QFile::exists(localFilePath) && showMessageBox) {
- QMessageBox::StandardButton reply;
- reply = QMessageBox::question(
- this, tr("File Exists"),
- tr("File already exists. Do you want to replace it?") + "\n" +
- newFileName,
- QMessageBox::Yes | QMessageBox::No);
- if (reply == QMessageBox::No) {
- continue;
- }
- }
- QNetworkRequest fileRequest(fileUrl);
- QNetworkReply* fileReply = manager->get(fileRequest);
-
- connect(fileReply, &QNetworkReply::finished, [=, this]() {
- if (fileReply->error() == QNetworkReply::NoError) {
- QByteArray fileData = fileReply->readAll();
- QFile localFile(localFilePath);
- if (localFile.open(QIODevice::WriteOnly)) {
- localFile.write(fileData);
- localFile.close();
- } else {
- QMessageBox::warning(
- this, tr("Error"),
- QString(tr("Failed to save file:") + "\n%1")
- .arg(localFilePath));
- }
- } else {
- QMessageBox::warning(this, tr("Error"),
- QString(tr("Failed to download file:") +
- "%1\n\n" + tr("Error") + ":%2")
- .arg(fileUrl)
- .arg(fileReply->errorString()));
- }
- fileReply->deleteLater();
- });
-
- foundFiles = true;
- }
- }
- if (!foundFiles && showMessageBox) {
- QMessageBox::warning(this, tr("Cheats Not Found"), CheatsNotFound_MSG);
- }
- }
- if (foundFiles && showMessageBox) {
- QMessageBox::information(this, tr("Cheats Downloaded Successfully"),
- CheatsDownloadedSuccessfully_MSG);
- populateFileListCheats();
- }
-
- } else {
- if (showMessageBox) {
- QMessageBox::warning(this, tr("Cheats Not Found"), CheatsNotFound_MSG);
- }
- }
- reply->deleteLater();
- emit downloadFinished();
- });
-
- // connect(reply, &QNetworkReply::errorOccurred, [=](QNetworkReply::NetworkError code) {
- // if (showMessageBox)
- // QMessageBox::warning(this, "Download Error",
- // QString("Error in response: %1").arg(reply->errorString()));
- // });
-}
-
-void CheatsPatches::populateFileListPatches() {
- QLayoutItem* item;
- while ((item = patchesGroupBoxLayout->takeAt(0)) != nullptr) {
- delete item->widget();
- delete item;
- }
- m_patchInfos.clear();
-
- QString patchesDir;
- Common::FS::PathToQString(patchesDir,
- Common::FS::GetUserPath(Common::FS::PathType::PatchesDir));
- QDir dir(patchesDir);
-
- QStringList folders = dir.entryList(QDir::Dirs | QDir::NoDotAndDotDot);
- QStringList matchingFiles;
- QString shadPS4entry = "";
-
- foreach (const QString& folder, folders) {
- QString folderPath = dir.filePath(folder);
- QDir subDir(folderPath);
-
- QString filesJsonPath = subDir.filePath("files.json");
- QFile file(filesJsonPath);
-
- if (file.open(QIODevice::ReadOnly)) {
- QByteArray fileData = file.readAll();
- file.close();
-
- QJsonDocument jsonDoc(QJsonDocument::fromJson(fileData));
- QJsonObject jsonObj = jsonDoc.object();
-
- for (auto it = jsonObj.constBegin(); it != jsonObj.constEnd(); ++it) {
- QString fileName = it.key();
- QJsonArray serials = it.value().toArray();
-
- if (serials.contains(QJsonValue(m_gameSerial))) {
- QString fileEntry = fileName + " | " + folder;
- if (!matchingFiles.contains(fileEntry)) {
- if (folder == "shadPS4") {
- shadPS4entry = fileEntry;
- }
- matchingFiles << fileEntry;
- }
- }
- }
- }
- }
- QStringListModel* model = new QStringListModel(matchingFiles, this);
- if (shadPS4entry != "") {
- QModelIndexList matches = model->match(model->index(0, 0), Qt::DisplayRole, shadPS4entry, 1,
- Qt::MatchExactly | Qt::MatchCaseSensitive);
- QModelIndex shadPS4Index = matches.first();
- model->moveRow(QModelIndex(), shadPS4Index.row(), QModelIndex(), 0);
- }
- patchesListView->setModel(model);
-
- connect(
- patchesListView->selectionModel(), &QItemSelectionModel::selectionChanged, this, [this]() {
- QModelIndexList selectedIndexes = patchesListView->selectionModel()->selectedIndexes();
- if (!selectedIndexes.isEmpty()) {
- QString selectedText = selectedIndexes.first().data().toString();
- addPatchesToLayout(selectedText);
- }
- });
-
- if (!matchingFiles.isEmpty()) {
- QModelIndex firstIndex = model->index(0, 0);
- patchesListView->selectionModel()->select(firstIndex, QItemSelectionModel::Select |
- QItemSelectionModel::Rows);
- patchesListView->setCurrentIndex(firstIndex);
- }
-}
-
-void CheatsPatches::downloadPatches(const QString repository, const bool showMessageBox) {
- QString url;
- if (repository == "shadPS4") {
- url = "https://api.github.com/repos/shadps4-emu/ps4_cheats/contents/PATCHES";
- }
- if (repository == "GoldHEN") {
- url = "https://api.github.com/repos/illusion0001/PS4-PS5-Game-Patch/contents/patches/xml";
- }
- QNetworkAccessManager* manager = new QNetworkAccessManager(this);
- QNetworkRequest request(url);
- request.setRawHeader("Accept", "application/vnd.github.v3+json");
- QNetworkReply* reply = manager->get(request);
-
- connect(reply, &QNetworkReply::finished, [=, this]() {
- if (reply->error() == QNetworkReply::NoError) {
- QByteArray jsonData = reply->readAll();
- reply->deleteLater();
-
- QJsonDocument jsonDoc = QJsonDocument::fromJson(jsonData);
- QJsonArray itemsArray = jsonDoc.array();
-
- if (itemsArray.isEmpty()) {
- if (showMessageBox) {
- QMessageBox::warning(this, tr("Error"),
- tr("Failed to parse JSON data from HTML."));
- }
- return;
- }
-
- QDir dir(Common::FS::GetUserPath(Common::FS::PathType::PatchesDir));
- QString fullPath = dir.filePath(repository);
- if (!dir.exists(fullPath)) {
- dir.mkpath(fullPath);
- }
- dir.setPath(fullPath);
-
- foreach (const QJsonValue& value, itemsArray) {
- QJsonObject fileObj = value.toObject();
- QString fileName = fileObj["name"].toString();
- QString filePath = fileObj["path"].toString();
- QString downloadUrl = fileObj["download_url"].toString();
-
- if (fileName.endsWith(".xml")) {
- QNetworkRequest fileRequest(downloadUrl);
- QNetworkReply* fileReply = manager->get(fileRequest);
-
- connect(fileReply, &QNetworkReply::finished, [=, this]() {
- if (fileReply->error() == QNetworkReply::NoError) {
- QByteArray fileData = fileReply->readAll();
- QFile localFile(dir.filePath(fileName));
- if (localFile.open(QIODevice::WriteOnly)) {
- localFile.write(fileData);
- localFile.close();
- } else {
- if (showMessageBox) {
- QMessageBox::warning(
- this, tr("Error"),
- QString(tr("Failed to save:") + "\n%1").arg(fileName));
- }
- }
- } else {
- if (showMessageBox) {
- QMessageBox::warning(
- this, tr("Error"),
- QString(tr("Failed to download:") + "\n%1").arg(downloadUrl));
- }
- }
- fileReply->deleteLater();
- });
- }
- }
- if (showMessageBox) {
- QMessageBox::information(this, tr("Download Complete"),
- QString(DownloadComplete_MSG));
- }
- // Create the files.json file with the identification of which file to open
- createFilesJson(repository);
- populateFileListPatches();
- compatibleVersionNotice(repository);
- } else {
- if (showMessageBox) {
- QMessageBox::warning(this, tr("Error"),
- QString(tr("Failed to retrieve HTML page.") + "\n%1")
- .arg(reply->errorString()));
- }
- }
- emit downloadFinished();
- });
-}
-
-void CheatsPatches::compatibleVersionNotice(const QString repository) {
- QDir patchesDir(Common::FS::GetUserPath(Common::FS::PathType::PatchesDir));
- QDir dir = patchesDir.filePath(repository);
-
- QStringList xmlFiles = dir.entryList(QStringList() << "*.xml", QDir::Files);
- QStringList incompatMessages;
-
- foreach (const QString& xmlFile, xmlFiles) {
- QFile file(dir.filePath(xmlFile));
- if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
- QMessageBox::warning(this, tr("Error"),
- QString(tr("Failed to open file:") + "\n%1").arg(xmlFile));
- continue;
- }
-
- QXmlStreamReader xmlReader(&file);
- QSet appVersionsSet;
- bool foundMatchingID = false;
-
- while (!xmlReader.atEnd() && !xmlReader.hasError()) {
- QXmlStreamReader::TokenType token = xmlReader.readNext();
- if (token == QXmlStreamReader::StartElement) {
- if (xmlReader.name() == QStringLiteral("ID")) {
- QString id = xmlReader.readElementText();
- if (id == m_gameSerial) {
- foundMatchingID = true;
- }
- } else if (xmlReader.name() == QStringLiteral("Metadata")) {
- if (foundMatchingID) {
- QString appVer = xmlReader.attributes().value("AppVer").toString();
- if (!appVer.isEmpty()) {
- appVersionsSet.insert(appVer);
- }
- }
- }
- }
- }
-
- if (xmlReader.hasError()) {
- QMessageBox::warning(this, tr("Error"),
- QString(tr("XML ERROR:") + "\n%1").arg(xmlReader.errorString()));
- }
-
- if (!foundMatchingID) {
- continue;
- }
-
- for (const QString& appVer : appVersionsSet) {
- if (appVer == QLatin1String("mask") || appVer == m_gameVersion) {
- return;
- }
- }
-
- if (!appVersionsSet.isEmpty()) {
- QStringList versionsList;
- for (const QString& v : appVersionsSet) {
- versionsList << v;
- }
- QString versions = versionsList.join(", ");
- QString message =
- QString(tr("The game is in version: %1")).arg(m_gameVersion) + "\n" +
- QString(tr("The downloaded patch only works on version: %1")).arg(versions) +
- QString("\n" + tr("You may need to update your game."));
- incompatMessages << message;
- }
- }
-
- if (!incompatMessages.isEmpty()) {
- QString finalMsg = incompatMessages.join("\n\n---\n\n");
- QMessageBox::information(this, tr("Incompatibility Notice"), finalMsg);
- }
-}
-
-void CheatsPatches::createFilesJson(const QString& repository) {
-
- QDir dir(Common::FS::GetUserPath(Common::FS::PathType::PatchesDir));
- QString fullPath = dir.filePath(repository);
- if (!dir.exists(fullPath)) {
- dir.mkpath(fullPath);
- }
- dir.setPath(fullPath);
-
- QJsonObject filesObject;
- QStringList xmlFiles = dir.entryList(QStringList() << "*.xml", QDir::Files);
-
- foreach (const QString& xmlFile, xmlFiles) {
- QFile file(dir.filePath(xmlFile));
- if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
- QMessageBox::warning(this, tr("Error"),
- QString(tr("Failed to open file:") + "\n%1").arg(xmlFile));
- continue;
- }
-
- QXmlStreamReader xmlReader(&file);
- QJsonArray titleIdsArray;
-
- while (!xmlReader.atEnd() && !xmlReader.hasError()) {
- QXmlStreamReader::TokenType token = xmlReader.readNext();
- if (token == QXmlStreamReader::StartElement) {
- if (xmlReader.name() == QStringLiteral("ID")) {
- titleIdsArray.append(xmlReader.readElementText());
- }
- }
- }
-
- if (xmlReader.hasError()) {
- QMessageBox::warning(this, tr("Error"),
- QString(tr("XML ERROR:") + "\n%1").arg(xmlReader.errorString()));
- }
- filesObject[xmlFile] = titleIdsArray;
- }
-
- QFile jsonFile(dir.absolutePath() + "/files.json");
- if (!jsonFile.open(QIODevice::WriteOnly)) {
- QMessageBox::warning(this, tr("Error"), tr("Failed to open files.json for writing"));
- return;
- }
-
- QJsonDocument jsonDoc(filesObject);
- jsonFile.write(jsonDoc.toJson());
- jsonFile.close();
-}
-
-void CheatsPatches::clearListCheats() {
- QLayoutItem* item;
- while ((item = rightLayout->takeAt(0)) != nullptr) {
- QWidget* widget = item->widget();
- if (widget) {
- delete widget;
- } else {
- QLayout* layout = item->layout();
- if (layout) {
- QLayoutItem* innerItem;
- while ((innerItem = layout->takeAt(0)) != nullptr) {
- QWidget* innerWidget = innerItem->widget();
- if (innerWidget) {
- delete innerWidget;
- }
- delete innerItem;
- }
- delete layout;
- }
- }
- }
- m_cheats.clear();
- m_cheatCheckBoxes.clear();
-}
-
-void CheatsPatches::addCheatsToLayout(const QJsonArray& modsArray, const QJsonArray& creditsArray) {
- clearListCheats();
- int maxWidthButton = 0;
-
- for (const QJsonValue& modValue : modsArray) {
- QJsonObject modObject = modValue.toObject();
- QString modName = modObject["name"].toString();
- QString modType = modObject["type"].toString();
-
- Cheat cheat;
- cheat.name = modName;
- cheat.type = modType;
-
- QJsonArray memoryArray = modObject["memory"].toArray();
- for (const QJsonValue& memoryValue : memoryArray) {
- QJsonObject memoryObject = memoryValue.toObject();
- MemoryMod memoryMod;
- memoryMod.offset = memoryObject["offset"].toString();
- memoryMod.on = memoryObject["on"].toString();
- memoryMod.off = memoryObject["off"].toString();
- cheat.memoryMods.append(memoryMod);
- }
-
- // Check for the presence of 'hint' field
- cheat.hasHint = modObject.contains("hint");
-
- m_cheats[modName] = cheat;
-
- if (modType == "checkbox") {
- QCheckBox* cheatCheckBox = new QCheckBox(modName);
- rightLayout->addWidget(cheatCheckBox);
- m_cheatCheckBoxes.append(cheatCheckBox);
- connect(cheatCheckBox, &QCheckBox::toggled,
- [this, modName](bool checked) { applyCheat(modName, checked); });
- } else if (modType == "button") {
- QPushButton* cheatButton = new QPushButton(modName);
- cheatButton->adjustSize();
- int buttonWidth = cheatButton->sizeHint().width();
- if (buttonWidth > maxWidthButton) {
- maxWidthButton = buttonWidth;
- }
-
- // Create a horizontal layout for buttons
- QHBoxLayout* buttonLayout = new QHBoxLayout();
- buttonLayout->setContentsMargins(0, 0, 0, 0);
- buttonLayout->addWidget(cheatButton);
- buttonLayout->addStretch();
-
- rightLayout->addLayout(buttonLayout);
- connect(cheatButton, &QPushButton::clicked,
- [this, modName]() { applyCheat(modName, true); });
- }
- }
-
- // Set minimum and fixed size for all buttons + 20
- for (int i = 0; i < rightLayout->count(); ++i) {
- QLayoutItem* layoutItem = rightLayout->itemAt(i);
- QWidget* widget = layoutItem->widget();
- if (widget) {
- QPushButton* button = qobject_cast(widget);
- if (button) {
- button->setMinimumWidth(maxWidthButton);
- button->setFixedWidth(maxWidthButton + 20);
- }
- } else {
- QLayout* layout = layoutItem->layout();
- if (layout) {
- for (int j = 0; j < layout->count(); ++j) {
- QLayoutItem* innerItem = layout->itemAt(j);
- QWidget* innerWidget = innerItem->widget();
- if (innerWidget) {
- QPushButton* button = qobject_cast(innerWidget);
- if (button) {
- button->setMinimumWidth(maxWidthButton);
- button->setFixedWidth(maxWidthButton + 20);
- }
- }
- }
- }
- }
- }
-
- // Set credits label
- QLabel* creditsLabel = new QLabel();
- QString creditsText = tr("Author: ");
- if (!creditsArray.isEmpty()) {
- QStringList authors;
- for (const QJsonValue& credit : creditsArray) {
- authors << credit.toString();
- }
- creditsText += authors.join(", ");
- }
- creditsLabel->setText(creditsText);
- creditsLabel->setAlignment(Qt::AlignLeft);
- rightLayout->addWidget(creditsLabel);
-}
-
-void CheatsPatches::populateFileListCheats() {
- clearListCheats();
-
- QString cheatsDir;
- Common::FS::PathToQString(cheatsDir, Common::FS::GetUserPath(Common::FS::PathType::CheatsDir));
-
- QString fullGameVersion = m_gameVersion;
- QString modifiedGameVersion = m_gameVersion.mid(1);
-
- QString patternWithFirstChar = m_gameSerial + "_" + fullGameVersion + "*.json";
- QString patternWithoutFirstChar = m_gameSerial + "_" + modifiedGameVersion + "*.json";
-
- QDir dir(cheatsDir);
- QStringList filters;
- filters << patternWithFirstChar << patternWithoutFirstChar;
- dir.setNameFilters(filters);
-
- QFileInfoList fileList = dir.entryInfoList(QDir::Files);
- QStringList fileNames;
-
- for (const QFileInfo& fileInfo : fileList) {
- fileNames << fileInfo.fileName();
- }
-
- QStringListModel* model = new QStringListModel(fileNames, this);
- listView_selectFile->setModel(model);
-
- connect(listView_selectFile->selectionModel(), &QItemSelectionModel::selectionChanged, this,
- [this]() {
- QModelIndexList selectedIndexes =
- listView_selectFile->selectionModel()->selectedIndexes();
- if (!selectedIndexes.isEmpty()) {
-
- QString selectedFileName = selectedIndexes.first().data().toString();
- QString cheatsDir;
- Common::FS::PathToQString(
- cheatsDir, Common::FS::GetUserPath(Common::FS::PathType::CheatsDir));
-
- QFile file(cheatsDir + "/" + selectedFileName);
- if (file.open(QIODevice::ReadOnly)) {
- QByteArray jsonData = file.readAll();
- QJsonDocument jsonDoc = QJsonDocument::fromJson(jsonData);
- QJsonObject jsonObject = jsonDoc.object();
- QJsonArray modsArray = jsonObject["mods"].toArray();
- QJsonArray creditsArray = jsonObject["credits"].toArray();
- addCheatsToLayout(modsArray, creditsArray);
- }
- }
- });
-
- if (!fileNames.isEmpty()) {
- QModelIndex firstIndex = model->index(0, 0);
- listView_selectFile->selectionModel()->select(firstIndex, QItemSelectionModel::Select |
- QItemSelectionModel::Rows);
- listView_selectFile->setCurrentIndex(firstIndex);
- }
-}
-
-void CheatsPatches::addPatchesToLayout(const QString& filePath) {
- if (filePath == "") {
- return;
- }
- QString folderPath = filePath.section(" | ", 1, 1);
-
- // Clear existing layout items
- QLayoutItem* item;
- while ((item = patchesGroupBoxLayout->takeAt(0)) != nullptr) {
- delete item->widget();
- delete item;
- }
- m_patchInfos.clear();
-
- QDir dir(Common::FS::GetUserPath(Common::FS::PathType::PatchesDir));
- QString fullPath = dir.filePath(folderPath);
-
- if (!dir.exists(fullPath)) {
- QMessageBox::warning(this, tr("Error"),
- QString(tr("Directory does not exist:") + "\n%1").arg(fullPath));
- return;
- }
- dir.setPath(fullPath);
-
- QString filesJsonPath = dir.filePath("files.json");
-
- QFile jsonFile(filesJsonPath);
- if (!jsonFile.open(QIODevice::ReadOnly)) {
- QMessageBox::warning(this, tr("Error"), tr("Failed to open files.json for reading."));
- return;
- }
-
- QByteArray jsonData = jsonFile.readAll();
- jsonFile.close();
-
- QJsonDocument jsonDoc = QJsonDocument::fromJson(jsonData);
- QJsonObject jsonObject = jsonDoc.object();
-
- bool patchAdded = false;
-
- // Iterate over each entry in the JSON file
- for (auto it = jsonObject.constBegin(); it != jsonObject.constEnd(); ++it) {
- QString xmlFileName = it.key();
- QJsonArray idsArray = it.value().toArray();
-
- // Check if the serial is in the ID list
- if (idsArray.contains(QJsonValue(m_gameSerial))) {
- QString xmlFilePath = dir.filePath(xmlFileName);
- QFile xmlFile(xmlFilePath);
-
- if (!xmlFile.open(QIODevice::ReadOnly)) {
- QMessageBox::warning(
- this, tr("Error"),
- QString(tr("Failed to open file:") + "\n%1").arg(xmlFile.fileName()));
- continue;
- }
- QXmlStreamReader xmlReader(&xmlFile);
- QString patchName;
- QString patchAuthor;
- QString patchNote;
- QJsonArray patchLines;
- bool isEnabled = false;
-
- while (!xmlReader.atEnd() && !xmlReader.hasError()) {
- xmlReader.readNext();
-
- if (xmlReader.tokenType() == QXmlStreamReader::StartElement) {
- if (xmlReader.name() == QStringLiteral("Metadata")) {
- QXmlStreamAttributes attributes = xmlReader.attributes();
- QString appVer = attributes.value("AppVer").toString();
- if (appVer == m_gameVersion) {
- patchName = attributes.value("Name").toString();
- patchAuthor = attributes.value("Author").toString();
- patchNote = attributes.value("Note").toString();
- isEnabled =
- attributes.value("isEnabled").toString() == QStringLiteral("true");
- }
- if (appVer == "mask") {
- patchName = attributes.value("Name").toString() + " (any version)";
- patchAuthor = attributes.value("Author").toString();
- patchNote = attributes.value("Note").toString();
- isEnabled =
- attributes.value("isEnabled").toString() == QStringLiteral("true");
- }
- } else if (xmlReader.name() == QStringLiteral("PatchList")) {
- QJsonArray linesArray;
- while (!xmlReader.atEnd() &&
- !(xmlReader.tokenType() == QXmlStreamReader::EndElement &&
- xmlReader.name() == QStringLiteral("PatchList"))) {
- xmlReader.readNext();
- if (xmlReader.tokenType() == QXmlStreamReader::StartElement &&
- xmlReader.name() == QStringLiteral("Line")) {
- QXmlStreamAttributes attributes = xmlReader.attributes();
- QJsonObject lineObject;
- lineObject["Type"] = attributes.value("Type").toString();
- lineObject["Address"] = attributes.value("Address").toString();
- lineObject["Value"] = attributes.value("Value").toString();
- linesArray.append(lineObject);
- }
- }
- patchLines = linesArray;
- }
- }
-
- if (!patchName.isEmpty() && !patchLines.isEmpty()) {
- QCheckBox* patchCheckBox = new QCheckBox(patchName);
- patchCheckBox->setProperty("patchName", patchName);
- patchCheckBox->setChecked(isEnabled);
- patchesGroupBoxLayout->addWidget(patchCheckBox);
-
- PatchInfo patchInfo;
- patchInfo.name = patchName;
- patchInfo.author = patchAuthor;
- patchInfo.note = patchNote;
- patchInfo.linesArray = patchLines;
- patchInfo.serial = m_gameSerial;
- m_patchInfos[patchName] = patchInfo;
-
- patchCheckBox->installEventFilter(this);
-
- connect(patchCheckBox, &QCheckBox::toggled,
- [this, patchName](bool checked) { applyPatch(patchName, checked); });
-
- patchName.clear();
- patchAuthor.clear();
- patchNote.clear();
- patchLines = QJsonArray();
- patchAdded = true;
- }
- }
- xmlFile.close();
- }
- }
-
- // Remove the item from the list view if no patches were added
- // (the game has patches, but not for the current version)
- if (!patchAdded) {
- QStringListModel* model = qobject_cast(patchesListView->model());
- if (model) {
- QStringList items = model->stringList();
- int index = items.indexOf(filePath);
- if (index != -1) {
- items.removeAt(index);
- model->setStringList(items);
- }
- }
- }
-}
-
-void CheatsPatches::updateNoteTextEdit(const QString& patchName) {
- if (m_patchInfos.contains(patchName)) {
- const PatchInfo& patchInfo = m_patchInfos[patchName];
- QString text = QString(tr("Name:") + " %1\n" + tr("Author: ") + "%2\n\n%3")
- .arg(patchInfo.name)
- .arg(patchInfo.author)
- .arg(patchInfo.note);
-
- foreach (const QJsonValue& value, patchInfo.linesArray) {
- QJsonObject lineObject = value.toObject();
- QString type = lineObject["Type"].toString();
- QString address = lineObject["Address"].toString();
- QString patchValue = lineObject["Value"].toString();
- }
- text.replace("\\n", "\n");
- instructionsTextEdit->setText(text);
- }
-}
-
-bool showErrorMessage = true;
-void CheatsPatches::uncheckAllCheatCheckBoxes() {
- for (auto& cheatCheckBox : m_cheatCheckBoxes) {
- cheatCheckBox->setChecked(false);
- }
- showErrorMessage = true;
-}
-
-void CheatsPatches::applyCheat(const QString& modName, bool enabled) {
- if (!m_cheats.contains(modName))
- return;
-
- if (MemoryPatcher::g_eboot_address == 0 && enabled) {
- QMessageBox::critical(this, tr("Error"),
- tr("Can't apply cheats before the game is started"));
- uncheckAllCheatCheckBoxes();
- return;
- }
-
- Cheat cheat = m_cheats[modName];
-
- for (const MemoryMod& memoryMod : cheat.memoryMods) {
- QString value = enabled ? memoryMod.on : memoryMod.off;
-
- std::string modNameStr = modName.toStdString();
- std::string offsetStr = memoryMod.offset.toStdString();
- std::string valueStr = value.toStdString();
-
- if (MemoryPatcher::g_eboot_address == 0)
- return;
-
- // Determine if the hint field is present
- bool isHintPresent = m_cheats[modName].hasHint;
- MemoryPatcher::PatchMemory(modNameStr, offsetStr, valueStr, "", "", !isHintPresent, false);
- }
-}
-
-void CheatsPatches::applyPatch(const QString& patchName, bool enabled) {
- if (!enabled)
- return;
- if (m_patchInfos.contains(patchName)) {
- const PatchInfo& patchInfo = m_patchInfos[patchName];
-
- foreach (const QJsonValue& value, patchInfo.linesArray) {
- QJsonObject lineObject = value.toObject();
- QString type = lineObject["Type"].toString();
- QString address = lineObject["Address"].toString();
- QString patchValue = lineObject["Value"].toString();
- QString maskOffsetStr = lineObject["Offset"].toString();
-
- patchValue = QString::fromStdString(
- MemoryPatcher::convertValueToHex(type.toStdString(), patchValue.toStdString()));
-
- bool littleEndian = false;
-
- if (type == "bytes16" || type == "bytes32" || type == "bytes64") {
- littleEndian = true;
- }
-
- MemoryPatcher::PatchMask patchMask = MemoryPatcher::PatchMask::None;
- int maskOffsetValue = 0;
-
- if (type == "mask")
- patchMask = MemoryPatcher::PatchMask::Mask;
-
- if (type == "mask_jump32")
- patchMask = MemoryPatcher::PatchMask::Mask_Jump32;
-
- if ((type == "mask" || type == "mask_jump32") && !maskOffsetStr.toStdString().empty()) {
- maskOffsetValue = std::stoi(maskOffsetStr.toStdString(), 0, 10);
- }
-
- if (MemoryPatcher::g_eboot_address == 0) {
- MemoryPatcher::patchInfo addingPatch;
- addingPatch.gameSerial = patchInfo.serial.toStdString();
- addingPatch.modNameStr = patchName.toStdString();
- addingPatch.offsetStr = address.toStdString();
- addingPatch.valueStr = patchValue.toStdString();
- addingPatch.isOffset = false;
- addingPatch.littleEndian = littleEndian;
- addingPatch.patchMask = patchMask;
- addingPatch.maskOffset = maskOffsetValue;
-
- MemoryPatcher::AddPatchToQueue(addingPatch);
- continue;
- }
- MemoryPatcher::PatchMemory(patchName.toStdString(), address.toStdString(),
- patchValue.toStdString(), "", "", false, littleEndian,
- patchMask);
- }
- }
-}
-
-bool CheatsPatches::eventFilter(QObject* obj, QEvent* event) {
- if (event->type() == QEvent::HoverEnter || event->type() == QEvent::HoverLeave) {
- QCheckBox* checkBox = qobject_cast(obj);
- if (checkBox) {
- bool hovered = (event->type() == QEvent::HoverEnter);
- onPatchCheckBoxHovered(checkBox, hovered);
- return true;
- }
- }
- // Pass the event on to base class
- return QWidget::eventFilter(obj, event);
-}
-
-void CheatsPatches::onPatchCheckBoxHovered(QCheckBox* checkBox, bool hovered) {
- if (hovered) {
- const auto patchName = checkBox->property("patchName");
- if (patchName.isValid()) {
- updateNoteTextEdit(patchName.toString());
- }
- } else {
- instructionsTextEdit->setText(defaultTextEdit_MSG);
- }
-}
diff --git a/src/qt_gui/cheats_patches.h b/src/qt_gui/cheats_patches.h
deleted file mode 100644
index 0f793b774..000000000
--- a/src/qt_gui/cheats_patches.h
+++ /dev/null
@@ -1,120 +0,0 @@
-// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-#ifndef CHEATS_PATCHES_H
-#define CHEATS_PATCHES_H
-
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-
-class CheatsPatches : public QWidget {
- Q_OBJECT
-
-public:
- CheatsPatches(const QString& gameName, const QString& gameSerial, const QString& gameVersion,
- const QString& gameSize, const QPixmap& gameImage, QWidget* parent = nullptr);
- ~CheatsPatches();
-
- void downloadCheats(const QString& source, const QString& m_gameSerial,
- const QString& m_gameVersion, bool showMessageBox);
- void downloadPatches(const QString repository, const bool showMessageBox);
- void createFilesJson(const QString& repository);
- void clearListCheats();
- void compatibleVersionNotice(const QString repository);
-
-signals:
- void downloadFinished();
-
-private:
- // UI Setup and Event Handlers
- void setupUI();
- void onSaveButtonClicked();
- QCheckBox* findCheckBoxByName(const QString& name);
- bool eventFilter(QObject* obj, QEvent* event);
- void onPatchCheckBoxHovered(QCheckBox* checkBox, bool hovered);
-
- // Cheat and Patch Management
- void populateFileListCheats();
- void populateFileListPatches();
-
- void addCheatsToLayout(const QJsonArray& modsArray, const QJsonArray& creditsArray);
- void addPatchesToLayout(const QString& serial);
-
- void applyCheat(const QString& modName, bool enabled);
- void applyPatch(const QString& patchName, bool enabled);
-
- void uncheckAllCheatCheckBoxes();
- void updateNoteTextEdit(const QString& patchName);
-
- // Network Manager
- QNetworkAccessManager* manager;
-
- // Patch Info Structures
- struct MemoryMod {
- QString offset;
- QString on;
- QString off;
- };
-
- struct Cheat {
- QString name;
- QString type;
- bool hasHint;
- QVector memoryMods;
- };
-
- struct PatchInfo {
- QString name;
- QString author;
- QString note;
- QJsonArray linesArray;
- QString serial;
- };
-
- // Members
- QString m_gameName;
- QString m_gameSerial;
- QString m_gameVersion;
- QString m_gameSize;
- QPixmap m_gameImage;
- QString m_cheatFilePath;
- QMap m_cheats;
- QMap m_patchInfos;
- QVector m_cheatCheckBoxes;
-
- // UI Elements
- QVBoxLayout* rightLayout;
- QVBoxLayout* patchesGroupBoxLayout;
- QGroupBox* patchesGroupBox;
- QVBoxLayout* patchesLayout;
- QTextEdit* instructionsTextEdit;
- QListView* listView_selectFile;
- QItemSelectionModel* selectionModel;
- QComboBox* patchesComboBox;
- QListView* patchesListView;
-
- // Strings
- QString defaultTextEdit_MSG;
- QString CheatsNotFound_MSG;
- QString CheatsDownloadedSuccessfully_MSG;
- QString DownloadComplete_MSG;
-};
-
-#endif // CHEATS_PATCHES_H
\ No newline at end of file
diff --git a/src/qt_gui/check_update.cpp b/src/qt_gui/check_update.cpp
deleted file mode 100644
index 75d2715b2..000000000
--- a/src/qt_gui/check_update.cpp
+++ /dev/null
@@ -1,633 +0,0 @@
-// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include "check_update.h"
-
-using namespace Common::FS;
-
-CheckUpdate::CheckUpdate(std::shared_ptr gui_settings, const bool showMessage,
- QWidget* parent)
- : QDialog(parent), m_gui_settings(std::move(gui_settings)),
- networkManager(new QNetworkAccessManager(this)) {
- setWindowTitle(tr("Auto Updater"));
- setFixedSize(0, 0);
- CheckForUpdates(showMessage);
-}
-
-CheckUpdate::~CheckUpdate() {}
-
-void CheckUpdate::CheckForUpdates(const bool showMessage) {
- QString updateChannel;
- QUrl url;
-
- bool checkName = true;
- while (checkName) {
- updateChannel = m_gui_settings->GetValue(gui::gen_updateChannel).toString();
- if (updateChannel == "Nightly") {
- url = QUrl("https://api.github.com/repos/shadps4-emu/shadPS4/releases");
- checkName = false;
- } else if (updateChannel == "Release") {
- url = QUrl("https://api.github.com/repos/shadps4-emu/shadPS4/releases/latest");
- checkName = false;
- } else {
- if (Common::g_is_release) {
- m_gui_settings->SetValue(gui::gen_updateChannel, "Release");
- } else {
- m_gui_settings->SetValue(gui::gen_updateChannel, "Nightly");
- }
- }
- }
-
- QNetworkRequest request(url);
- QNetworkReply* reply = networkManager->get(request);
-
- connect(reply, &QNetworkReply::finished, this, [this, reply, showMessage, updateChannel]() {
- if (reply->error() != QNetworkReply::NoError) {
- if (reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt() == 403) {
- QString response = reply->readAll();
- if (response.startsWith("{\"message\":\"API rate limit exceeded for")) {
- QMessageBox::warning(
- this, tr("Auto Updater"),
- // clang-format off
-tr("The Auto Updater allows up to 60 update checks per hour.\\nYou have reached this limit. Please try again later.").replace("\\n", "\n"));
- // clang-format on
- } else {
- QMessageBox::warning(
- this, tr("Error"),
- QString(tr("Network error:") + "\n" + reply->errorString()));
- }
- } else {
- QMessageBox::warning(this, tr("Error"),
- QString(tr("Network error:") + "\n" + reply->errorString()));
- }
- reply->deleteLater();
- return;
- }
-
- QByteArray response = reply->readAll();
- QJsonDocument jsonDoc(QJsonDocument::fromJson(response));
-
- if (jsonDoc.isNull()) {
- QMessageBox::warning(this, tr("Error"), tr("Failed to parse update information."));
- reply->deleteLater();
- return;
- }
-
- QString downloadUrl;
- QString latestVersion;
- QString latestRev;
- QString latestDate;
- QString platformString;
-
-#ifdef Q_OS_WIN
- platformString = "win64-qt";
-#elif defined(Q_OS_LINUX)
- platformString = "linux-qt";
-#elif defined(Q_OS_MAC)
- platformString = "macos-qt";
-#endif
-
- QJsonObject jsonObj;
- if (updateChannel == "Nightly") {
- QJsonArray jsonArray = jsonDoc.array();
- for (const QJsonValue& value : jsonArray) {
- jsonObj = value.toObject();
- if (jsonObj.contains("prerelease") && jsonObj["prerelease"].toBool()) {
- break;
- }
- }
- if (!jsonObj.isEmpty()) {
- latestVersion = jsonObj["tag_name"].toString();
- } else {
- QMessageBox::warning(this, tr("Error"), tr("No pre-releases found."));
- reply->deleteLater();
- return;
- }
- } else {
- jsonObj = jsonDoc.object();
- if (jsonObj.contains("tag_name")) {
- latestVersion = jsonObj["tag_name"].toString();
- } else {
- QMessageBox::warning(this, tr("Error"), tr("Invalid release data."));
- reply->deleteLater();
- return;
- }
- }
-
- latestRev = latestVersion.right(40);
- latestDate = jsonObj["published_at"].toString();
-
- QJsonArray assets = jsonObj["assets"].toArray();
- bool found = false;
-
- for (const QJsonValue& assetValue : assets) {
- QJsonObject assetObj = assetValue.toObject();
- if (assetObj["name"].toString().contains(platformString)) {
- downloadUrl = assetObj["browser_download_url"].toString();
- found = true;
- break;
- }
- }
-
- if (!found) {
- QMessageBox::warning(
- this, tr("Auto Updater"),
- // clang-format off
-tr("Notice:
Starting from version 0.12.0, the Qt version of the emulator will no longer receive direct updates.
However, the Qt interface remains available through the new official launcher:
Qt Launcher - based on the original shadPS4 source code.
We recommend switching to this launcher to continue receiving updates."));
- // clang-format on
-
- reply->deleteLater();
- return;
- }
-
- QString currentRev = (updateChannel == "Nightly")
- ? QString::fromStdString(Common::g_scm_rev)
- : "v." + QString::fromStdString(Common::g_version);
- QString currentDate = Common::g_scm_date;
-
- QDateTime dateTime = QDateTime::fromString(latestDate, Qt::ISODate);
- latestDate = dateTime.isValid() ? dateTime.toString("yyyy-MM-dd HH:mm:ss") : "Unknown date";
-
- if (latestRev == currentRev) {
- if (showMessage) {
- QMessageBox::information(this, tr("Auto Updater"),
- tr("Your version is already up to date!"));
- }
- close();
- return;
- } else {
- setupUI(downloadUrl, latestDate, latestRev, currentDate, currentRev);
- }
- reply->deleteLater();
- });
-}
-
-void CheckUpdate::setupUI(const QString& downloadUrl, const QString& latestDate,
- const QString& latestRev, const QString& currentDate,
- const QString& currentRev) {
- QVBoxLayout* layout = new QVBoxLayout(this);
- QHBoxLayout* titleLayout = new QHBoxLayout();
-
- QLabel* imageLabel = new QLabel(this);
- QPixmap pixmap(":/images/shadps4.png");
- imageLabel->setPixmap(pixmap);
- imageLabel->setScaledContents(true);
- imageLabel->setFixedSize(50, 50);
-
- QLabel* titleLabel = new QLabel("" + tr("Update Available") + "
", this);
- titleLayout->addWidget(imageLabel);
- titleLayout->addWidget(titleLabel);
- layout->addLayout(titleLayout);
-
- QString updateChannel = m_gui_settings->GetValue(gui::gen_updateChannel).toString();
-
- QString updateText =
- QString("" + tr("Update Channel") + ": " + updateChannel +
- "
"
- "
"
- "| " +
- tr("Current Version") +
- ": | "
- "%1 | "
- "(%2) | "
- "
"
- "| " +
- tr("Latest Version") +
- ": | "
- "%3 | "
- "(%4) | "
- "
")
- .arg(updateChannel == "Nightly" ? currentRev.left(7) : currentRev.left(8), currentDate,
- updateChannel == "Nightly" ? latestRev.left(7) : latestRev.left(8), latestDate);
-
- QLabel* updateLabel = new QLabel(updateText, this);
- layout->addWidget(updateLabel);
-
- // Setup bottom layout with action buttons
- autoUpdateCheckBox = new QCheckBox(tr("Check for Updates at Startup"), this);
- layout->addWidget(autoUpdateCheckBox);
-
- QHBoxLayout* updatePromptLayout = new QHBoxLayout();
- QLabel* updatePromptLabel = new QLabel(tr("Do you want to update?"), this);
- updatePromptLayout->addWidget(updatePromptLabel);
-
- yesButton = new QPushButton(tr("Update"), this);
- noButton = new QPushButton(tr("No"), this);
- yesButton->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Preferred);
- noButton->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Preferred);
-
- QSpacerItem* spacer = new QSpacerItem(40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum);
- updatePromptLayout->addItem(spacer);
- updatePromptLayout->addWidget(yesButton);
- updatePromptLayout->addWidget(noButton);
-
- layout->addLayout(updatePromptLayout);
-
- // Don't show changelog button if:
- // The current version is a pre-release and the version to be downloaded is a release.
- bool current_isWIP = currentRev.endsWith("WIP", Qt::CaseInsensitive);
- bool latest_isWIP = latestRev.endsWith("WIP", Qt::CaseInsensitive);
- if (current_isWIP && !latest_isWIP) {
- } else {
- QTextBrowser* textField = new QTextBrowser(this);
- textField->setReadOnly(true);
- textField->setFixedWidth(500);
- textField->setFixedHeight(200);
- textField->setVisible(false);
- layout->addWidget(textField);
-
- QPushButton* toggleButton = new QPushButton(tr("Show Changelog"), this);
- layout->addWidget(toggleButton);
-
- connect(toggleButton, &QPushButton::clicked,
- [this, textField, toggleButton, currentRev, latestRev, downloadUrl, latestDate,
- currentDate]() {
- if (!textField->isVisible()) {
- requestChangelog(currentRev, latestRev, downloadUrl, latestDate,
- currentDate);
- textField->setVisible(true);
- toggleButton->setText(tr("Hide Changelog"));
- adjustSize();
- textField->setFixedWidth(textField->width() + 20);
- } else {
- textField->setVisible(false);
- toggleButton->setText(tr("Show Changelog"));
- adjustSize();
- }
- });
-
- if (m_gui_settings->GetValue(gui::gen_showChangeLog).toBool()) {
- requestChangelog(currentRev, latestRev, downloadUrl, latestDate, currentDate);
- textField->setVisible(true);
- toggleButton->setText(tr("Hide Changelog"));
- adjustSize();
- textField->setFixedWidth(textField->width() + 20);
- }
- }
-
- connect(yesButton, &QPushButton::clicked, this, [this, downloadUrl]() {
- yesButton->setEnabled(false);
- noButton->setEnabled(false);
- DownloadUpdate(downloadUrl);
- });
-
- connect(noButton, &QPushButton::clicked, this, [this]() { close(); });
-
- autoUpdateCheckBox->setChecked(m_gui_settings->GetValue(gui::gen_checkForUpdates).toBool());
-#if (QT_VERSION < QT_VERSION_CHECK(6, 7, 0))
- connect(autoUpdateCheckBox, &QCheckBox::stateChanged, this, [this](int state) {
-#else
- connect(autoUpdateCheckBox, &QCheckBox::checkStateChanged, this, [this](Qt::CheckState state) {
-#endif
- const auto user_dir = Common::FS::GetUserPath(Common::FS::PathType::UserDir);
- m_gui_settings->SetValue(gui::gen_checkForUpdates, (state == Qt::Checked));
- Config::save(user_dir / "config.toml");
- });
-
- setLayout(layout);
-}
-
-void CheckUpdate::requestChangelog(const QString& currentRev, const QString& latestRev,
- const QString& downloadUrl, const QString& latestDate,
- const QString& currentDate) {
- QString compareUrlString =
- QString("https://api.github.com/repos/shadps4-emu/shadPS4/compare/%1...%2")
- .arg(currentRev)
- .arg(latestRev);
-
- QUrl compareUrl(compareUrlString);
- QNetworkRequest compareRequest(compareUrl);
- QNetworkReply* compareReply = networkManager->get(compareRequest);
-
- connect(compareReply, &QNetworkReply::finished, this,
- [this, compareReply, downloadUrl, latestDate, latestRev, currentDate, currentRev]() {
- if (compareReply->error() != QNetworkReply::NoError) {
- QMessageBox::warning(
- this, tr("Error"),
- QString(tr("Network error:") + "\n%1").arg(compareReply->errorString()));
- compareReply->deleteLater();
- return;
- }
-
- QByteArray compareResponse = compareReply->readAll();
- QJsonDocument compareJsonDoc(QJsonDocument::fromJson(compareResponse));
- QJsonObject compareJsonObj = compareJsonDoc.object();
- QJsonArray commits = compareJsonObj["commits"].toArray();
-
- QString changes;
- for (const QJsonValue& commitValue : commits) {
- QJsonObject commitObj = commitValue.toObject();
- QString message = commitObj["commit"].toObject()["message"].toString();
-
- // Remove texts after first line break, if any, to make it cleaner
- int newlineIndex = message.indexOf('\n');
- if (newlineIndex != -1) {
- message = message.left(newlineIndex);
- }
- if (!changes.isEmpty()) {
- changes += "
";
- }
- changes += " • " + message;
- }
-
- // Update the text field with the changelog
- QTextBrowser* textField = findChild();
- if (textField) {
- QRegularExpression re("\\(\\#(\\d+)\\)");
- QString newChanges;
- int lastIndex = 0;
- QRegularExpressionMatchIterator i = re.globalMatch(changes);
- while (i.hasNext()) {
- QRegularExpressionMatch match = i.next();
- newChanges += changes.mid(lastIndex, match.capturedStart() - lastIndex);
- QString num = match.captured(1);
- newChanges +=
- QString(
- "(#%1)")
- .arg(num);
- lastIndex = match.capturedEnd();
- }
-
- newChanges += changes.mid(lastIndex);
- changes = newChanges;
-
- textField->setOpenExternalLinks(true);
- textField->setHtml("" + tr("Changes") + ":
" + changes);
- }
-
- compareReply->deleteLater();
- });
-}
-
-void CheckUpdate::DownloadUpdate(const QString& url) {
- QProgressBar* progressBar = new QProgressBar(this);
- progressBar->setRange(0, 100);
- progressBar->setTextVisible(true);
- progressBar->setValue(0);
-
- layout()->addWidget(progressBar);
-
- QNetworkRequest request(url);
- QNetworkReply* reply = networkManager->get(request);
-
- connect(reply, &QNetworkReply::downloadProgress, this,
- [progressBar](qint64 bytesReceived, qint64 bytesTotal) {
- if (bytesTotal > 0) {
- int percentage = static_cast((bytesReceived * 100) / bytesTotal);
- progressBar->setValue(percentage);
- }
- });
-
- connect(reply, &QNetworkReply::finished, this, [this, reply, progressBar, url]() {
- progressBar->setValue(100);
- if (reply->error() != QNetworkReply::NoError) {
- QMessageBox::warning(this, tr("Error"),
- tr("Network error occurred while trying to access the URL") +
- ":\n" + url + "\n" + reply->errorString());
- reply->deleteLater();
- progressBar->deleteLater();
- return;
- }
-
- QString userPath;
- Common::FS::PathToQString(userPath, Common::FS::GetUserPath(Common::FS::PathType::UserDir));
-#ifdef Q_OS_WIN
- QString tempDownloadPath =
- QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) +
- "/Temp/temp_download_update";
-#else
- QString tempDownloadPath = userPath + "/temp_download_update";
-#endif
- QDir dir(tempDownloadPath);
- if (!dir.exists()) {
- dir.mkpath(".");
- }
-
- QString downloadPath = tempDownloadPath + "/temp_download_update.zip";
- QFile file(downloadPath);
- if (file.open(QIODevice::WriteOnly)) {
- file.write(reply->readAll());
- file.close();
- QMessageBox::information(this, tr("Download Complete"),
- tr("The update has been downloaded, press OK to install."));
- Install();
- } else {
- QMessageBox::warning(
- this, tr("Error"),
- QString(tr("Failed to save the update file at") + ":\n" + downloadPath));
- }
-
- reply->deleteLater();
- progressBar->deleteLater();
- });
-}
-
-void CheckUpdate::Install() {
- QString userPath;
- Common::FS::PathToQString(userPath, Common::FS::GetUserPath(Common::FS::PathType::UserDir));
-
- QString rootPath;
- Common::FS::PathToQString(rootPath, std::filesystem::current_path());
-
- QString tempDirPath = userPath + "/temp_download_update";
- QString startingUpdate = tr("Starting Update...");
-
- QString binaryStartingUpdate;
- for (QChar c : startingUpdate) {
- binaryStartingUpdate.append(QString::number(c.unicode(), 2).rightJustified(16, '0'));
- }
-
- QString scriptContent;
- QString scriptFileName;
- QStringList arguments;
- QString processCommand;
-
-#ifdef Q_OS_WIN
- // On windows, overwrite tempDirPath with AppData/Roaming/shadps4/Temp folder
- // due to PowerShell Expand-Archive not being able to handle correctly
- // paths in square brackets (ie: ./[shadps4])
- tempDirPath = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) +
- "/Temp/temp_download_update";
-
- // Windows Batch Script
- scriptFileName = tempDirPath + "/update.ps1";
- scriptContent = QStringLiteral(
- "Set-ExecutionPolicy Bypass -Scope Process -Force\n"
- "$binaryStartingUpdate = '%1'\n"
- "$chars = @()\n"
- "for ($i = 0; $i -lt $binaryStartingUpdate.Length; $i += 16) {\n"
- " $chars += [char]([convert]::ToInt32($binaryStartingUpdate.Substring($i, 16), 2))\n"
- "}\n"
- "$startingUpdate = -join $chars\n"
- "Write-Output $startingUpdate\n"
- "Expand-Archive -Path '%2\\temp_download_update.zip' -DestinationPath '%2' -Force\n"
- "Start-Sleep -Seconds 3\n"
- "Copy-Item -Recurse -Force '%2\\*' '%3\\'\n"
- "Start-Sleep -Seconds 2\n"
- "Remove-Item -Force -LiteralPath '%3\\update.ps1'\n"
- "Remove-Item -Force -LiteralPath '%3\\temp_download_update.zip'\n"
- "Remove-Item -Recurse -Force '%2'\n"
- "Start-Process -FilePath '%3\\shadps4.exe' "
- "-WorkingDirectory ([WildcardPattern]::Escape('%3'))\n");
- arguments << "-ExecutionPolicy"
- << "Bypass"
- << "-File" << scriptFileName;
- processCommand = "powershell.exe";
-
-#elif defined(Q_OS_LINUX)
- // Linux Shell Script
- scriptFileName = tempDirPath + "/update.sh";
- scriptContent = QStringLiteral(
- "#!/bin/bash\n"
- "check_unzip() {\n"
- " if ! command -v unzip &> /dev/null && ! command -v 7z &> /dev/null; then\n"
- " echo \"Neither 'unzip' nor '7z' is installed.\"\n"
- " read -p \"Would you like to install 'unzip'? (y/n): \" response\n"
- " if [[ \"$response\" == \"y\" || \"$response\" == \"Y\" ]]; then\n"
- " if [[ -f /etc/os-release ]]; then\n"
- " . /etc/os-release\n"
- " case \"$ID\" in\n"
- " ubuntu|debian)\n"
- " sudo apt-get install unzip -y\n"
- " ;;\n"
- " fedora|redhat)\n"
- " sudo dnf install unzip -y\n"
- " ;;\n"
- " *)\n"
- " echo \"Unsupported distribution for automatic installation.\"\n"
- " exit 1\n"
- " ;;\n"
- " esac\n"
- " else\n"
- " echo \"Could not identify the distribution.\"\n"
- " exit 1\n"
- " fi\n"
- " else\n"
- " echo \"At least one of 'unzip' or '7z' is required to continue. The process "
- "will be terminated.\"\n"
- " exit 1\n"
- " fi\n"
- " fi\n"
- "}\n"
- "extract_file() {\n"
- " if command -v unzip &> /dev/null; then\n"
- " unzip -o \"%2/temp_download_update.zip\" -d \"%2/\"\n"
- " elif command -v 7z &> /dev/null; then\n"
- " 7z x \"%2/temp_download_update.zip\" -o\"%2/\" -y\n"
- " else\n"
- " echo \"No suitable extraction tool found.\"\n"
- " exit 1\n"
- " fi\n"
- "}\n"
- "main() {\n"
- " check_unzip\n"
- " echo \"%1\"\n"
- " sleep 2\n"
- " extract_file\n"
- " sleep 2\n"
- " if pgrep -f \"Shadps4-qt.AppImage\" > /dev/null; then\n"
- " pkill -f \"Shadps4-qt.AppImage\"\n"
- " sleep 2\n"
- " fi\n"
- " cp -r \"%2/\"* \"%3/\"\n"
- " sleep 2\n"
- " rm \"%3/update.sh\"\n"
- " rm \"%3/temp_download_update.zip\"\n"
- " chmod +x \"%3/Shadps4-qt.AppImage\"\n"
- " rm -r \"%2\"\n"
- " cd \"%3\" && ./Shadps4-qt.AppImage\n"
- "}\n"
- "main\n");
- arguments << scriptFileName;
- processCommand = "bash";
-
-#elif defined(Q_OS_MAC)
- // macOS Shell Script
- scriptFileName = tempDirPath + "/update.sh";
- scriptContent = QStringLiteral(
- "#!/bin/bash\n"
- "check_tools() {\n"
- " if ! command -v unzip &> /dev/null && ! command -v tar &> /dev/null; then\n"
- " echo \"Neither 'unzip' nor 'tar' is installed.\"\n"
- " read -p \"Would you like to install 'unzip'? (y/n): \" response\n"
- " if [[ \"$response\" == \"y\" || \"$response\" == \"Y\" ]]; then\n"
- " echo \"Please install 'unzip' using Homebrew or another package manager.\"\n"
- " exit 1\n"
- " else\n"
- " echo \"At least one of 'unzip' or 'tar' is required to continue. The process "
- "will be terminated.\"\n"
- " exit 1\n"
- " fi\n"
- " fi\n"
- "}\n"
- "check_tools\n"
- "echo \"%1\"\n"
- "sleep 2\n"
- "unzip -o \"%2/temp_download_update.zip\" -d \"%2/\"\n"
- "sleep 2\n"
- "tar -xzf \"%2/shadps4-macos-qt.tar.gz\" -C \"%3\"\n"
- "sleep 2\n"
- "rm \"%3/update.sh\"\n"
- "chmod +x \"%3/shadps4.app/Contents/MacOS/shadps4\"\n"
- "open \"%3/shadps4.app\"\n"
- "rm -r \"%2\"\n");
-
- arguments << scriptFileName;
- processCommand = "bash";
-
-#else
- QMessageBox::warning(this, tr("Error"), "Unsupported operating system.");
- return;
-#endif
-
- QFile scriptFile(scriptFileName);
- if (scriptFile.open(QIODevice::WriteOnly | QIODevice::Text)) {
- QTextStream out(&scriptFile);
- scriptFile.write("\xEF\xBB\xBF");
-#ifdef Q_OS_WIN
- out << scriptContent.arg(binaryStartingUpdate).arg(tempDirPath).arg(rootPath);
-#endif
-#if defined(Q_OS_LINUX) || defined(Q_OS_MAC)
- out << scriptContent.arg(startingUpdate).arg(tempDirPath).arg(rootPath);
-#endif
- scriptFile.close();
-
-// Make the script executable on Unix-like systems
-#if defined(Q_OS_LINUX) || defined(Q_OS_MAC)
- scriptFile.setPermissions(QFileDevice::ExeOwner | QFileDevice::ReadOwner |
- QFileDevice::WriteOwner);
-#endif
-
- QProcess::startDetached(processCommand, arguments);
-
- exit(EXIT_SUCCESS);
- } else {
- QMessageBox::warning(
- this, tr("Error"),
- QString(tr("Failed to create the update script file") + ":\n" + scriptFileName));
- }
-}
diff --git a/src/qt_gui/check_update.h b/src/qt_gui/check_update.h
deleted file mode 100644
index 139059c41..000000000
--- a/src/qt_gui/check_update.h
+++ /dev/null
@@ -1,43 +0,0 @@
-// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-#ifndef CHECKUPDATE_H
-#define CHECKUPDATE_H
-
-#include
-#include
-#include
-#include
-#include "gui_settings.h"
-
-class CheckUpdate : public QDialog {
- Q_OBJECT
-
-public:
- explicit CheckUpdate(std::shared_ptr gui_settings, const bool showMessage,
- QWidget* parent = nullptr);
- ~CheckUpdate();
-
-private slots:
- void CheckForUpdates(const bool showMessage);
- void DownloadUpdate(const QString& url);
- void Install();
-
-private:
- void setupUI(const QString& downloadUrl, const QString& latestDate, const QString& latestRev,
- const QString& currentDate, const QString& currentRev);
-
- void requestChangelog(const QString& currentRev, const QString& latestRev,
- const QString& downloadUrl, const QString& latestDate,
- const QString& currentDate);
-
- QCheckBox* autoUpdateCheckBox;
- QPushButton* yesButton;
- QPushButton* noButton;
- QString updateDownloadUrl;
-
- QNetworkAccessManager* networkManager;
- std::shared_ptr m_gui_settings;
-};
-
-#endif // CHECKUPDATE_H
diff --git a/src/qt_gui/compatibility_info.cpp b/src/qt_gui/compatibility_info.cpp
deleted file mode 100644
index eecd1bd47..000000000
--- a/src/qt_gui/compatibility_info.cpp
+++ /dev/null
@@ -1,216 +0,0 @@
-// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-#include
-#include
-#include
-#include
-
-#include "common/path_util.h"
-#include "compatibility_info.h"
-
-CompatibilityInfoClass::CompatibilityInfoClass()
- : m_network_manager(new QNetworkAccessManager(this)) {
- QStringList file_paths;
- std::filesystem::path compatibility_file_path =
- Common::FS::GetUserPath(Common::FS::PathType::MetaDataDir) / "compatibility_data.json";
- Common::FS::PathToQString(m_compatibility_filename, compatibility_file_path);
-};
-CompatibilityInfoClass::~CompatibilityInfoClass() = default;
-
-void CompatibilityInfoClass::UpdateCompatibilityDatabase(QWidget* parent, bool forced) {
- if (!forced && LoadCompatibilityFile())
- return;
-
- QUrl url("https://github.com/shadps4-compatibility/shadps4-game-compatibility/releases/latest/"
- "download/compatibility_data.json");
- QNetworkRequest request(url);
- QNetworkReply* reply = m_network_manager->get(request);
-
- QProgressDialog dialog(tr("Fetching compatibility data, please wait"), tr("Cancel"), 0, 100,
- parent);
- dialog.setWindowTitle(tr("Loading..."));
- dialog.setWindowModality(Qt::WindowModal);
- dialog.setMinimumDuration(0);
- dialog.setValue(0);
-
- connect(reply, &QNetworkReply::downloadProgress,
- [&dialog](qint64 bytesReceived, qint64 bytesTotal) {
- if (bytesTotal > 0) {
- dialog.setMaximum(bytesTotal);
- dialog.setValue(bytesReceived);
- }
- });
-
- connect(&dialog, &QProgressDialog::canceled, reply, &QNetworkReply::abort);
-
- QEventLoop loop;
- connect(reply, &QNetworkReply::finished, &loop, &QEventLoop::quit);
- loop.exec();
-
- if (reply->error() != QNetworkReply::NoError) {
- reply->deleteLater();
- QMessageBox::critical(parent, tr("Error"),
- tr("Unable to update compatibility data! Try again later."));
- // Try loading compatibility_file.json again
- if (!forced)
- LoadCompatibilityFile();
- return;
- }
-
- QFile compatibility_file(m_compatibility_filename);
- if (!compatibility_file.open(QIODevice::WriteOnly | QIODevice::Truncate | QIODevice::Text)) {
- QMessageBox::critical(parent, tr("Error"),
- tr("Unable to open compatibility_data.json for writing."));
- reply->deleteLater();
- return;
- }
-
- // Writes the received data to the file.
- QByteArray json_data = reply->readAll();
- compatibility_file.write(json_data);
- compatibility_file.close();
- reply->deleteLater();
-
- LoadCompatibilityFile();
-}
-
-CompatibilityEntry CompatibilityInfoClass::GetCompatibilityInfo(const std::string& serial) {
- QString title_id = QString::fromStdString(serial);
- if (m_compatibility_database.contains(title_id)) {
- QJsonObject compatibility_obj = m_compatibility_database[title_id].toObject();
-
- // Set current_os automatically
- QString current_os;
-#ifdef Q_OS_WIN
- current_os = "os-windows";
-#elif defined(Q_OS_MAC)
- current_os = "os-macOS";
-#elif defined(Q_OS_LINUX)
- current_os = "os-linux";
-#else
- current_os = "os-unknown";
-#endif
- // Check if the game is compatible with the current operating system
- if (compatibility_obj.contains(current_os)) {
- QJsonObject compatibility_entry_obj = compatibility_obj[current_os].toObject();
- CompatibilityEntry compatibility_entry{
- LabelToCompatStatus.at(compatibility_entry_obj["status"].toString()),
- compatibility_entry_obj["version"].toString(),
- QDateTime::fromString(compatibility_entry_obj["last_tested"].toString(),
- Qt::ISODate),
- compatibility_entry_obj["url"].toString(),
- compatibility_entry_obj["issue_number"].toString()};
- return compatibility_entry;
- } else {
- // If there is no entry for the current operating system, return "Unknown"
- return CompatibilityEntry{CompatibilityStatus::Unknown, "",
- QDateTime::currentDateTime(), "", 0};
- }
- }
-
- // If title not found, return "Unknown"
- return CompatibilityEntry{CompatibilityStatus::Unknown, "", QDateTime::currentDateTime(), "",
- 0};
-}
-
-bool CompatibilityInfoClass::LoadCompatibilityFile() {
- // Returns true if compatibility is loaded succescfully
- QFileInfo check_file(m_compatibility_filename);
- const auto modified_delta = QDateTime::currentDateTime() - check_file.lastModified();
- if (!check_file.exists() || !check_file.isFile() ||
- std::chrono::duration_cast(modified_delta).count() > 60) {
- return false;
- }
-
- QFile compatibility_file(m_compatibility_filename);
- if (!compatibility_file.open(QIODevice::ReadOnly)) {
- compatibility_file.close();
- return false;
- }
- QByteArray json_data = compatibility_file.readAll();
- compatibility_file.close();
-
- QJsonDocument json_doc = QJsonDocument::fromJson(json_data);
- if (json_doc.isEmpty() || json_doc.isNull()) {
- return false;
- }
-
- m_compatibility_database = json_doc.object();
- return true;
-}
-
-void CompatibilityInfoClass::ExtractCompatibilityInfo(QByteArray response) {
- QJsonDocument json_doc(QJsonDocument::fromJson(response));
-
- if (json_doc.isNull()) {
- return;
- }
-
- QJsonArray json_arr;
-
- json_arr = json_doc.array();
-
- for (const auto& issue_ref : std::as_const(json_arr)) {
- QJsonObject issue_obj = issue_ref.toObject();
- QString title_id;
- QRegularExpression title_id_regex("CUSA[0-9]{5}");
- QRegularExpressionMatch title_id_match =
- title_id_regex.match(issue_obj["title"].toString());
- QString current_os = "os-unknown";
- QString compatibility_status = "status-unknown";
- if (issue_obj.contains("labels") && title_id_match.hasMatch()) {
- title_id = title_id_match.captured(0);
- const QJsonArray& label_array = issue_obj["labels"].toArray();
- for (const auto& elem : label_array) {
- QString label = elem.toObject()["name"].toString();
- if (LabelToOSType.contains(label)) {
- current_os = label;
- continue;
- }
- if (LabelToCompatStatus.contains(label)) {
- compatibility_status = label;
- continue;
- }
- }
-
- // QJson does not support editing nested objects directly..
-
- QJsonObject compatibility_obj = m_compatibility_database[title_id].toObject();
-
- QJsonObject compatibility_data{
- {{"status", compatibility_status},
- {"last_tested", issue_obj["updated_at"]},
- {"version", issue_obj["milestone"].isNull()
- ? "unknown"
- : issue_obj["milestone"].toObject()["title"].toString()},
- {"url", issue_obj["html_url"]},
- {"issue_number", issue_obj["number"]}}};
-
- compatibility_obj[current_os] = compatibility_data;
-
- m_compatibility_database[title_id] = compatibility_obj;
- }
- }
-
- return;
-}
-
-const QString CompatibilityInfoClass::GetCompatStatusString(const CompatibilityStatus status) {
- switch (status) {
- case CompatibilityStatus::Unknown:
- return tr("Unknown");
- case CompatibilityStatus::Nothing:
- return tr("Nothing");
- case CompatibilityStatus::Boots:
- return tr("Boots");
- case CompatibilityStatus::Menus:
- return tr("Menus");
- case CompatibilityStatus::Ingame:
- return tr("Ingame");
- case CompatibilityStatus::Playable:
- return tr("Playable");
- default:
- return tr("Unknown");
- }
-}
diff --git a/src/qt_gui/compatibility_info.h b/src/qt_gui/compatibility_info.h
deleted file mode 100644
index 7e70e998b..000000000
--- a/src/qt_gui/compatibility_info.h
+++ /dev/null
@@ -1,88 +0,0 @@
-// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-#pragma once
-
-#include
-#include
-#include
-#include
-
-#include "common/config.h"
-#include "core/file_format/psf.h"
-
-enum class CompatibilityStatus {
- Unknown,
- Nothing,
- Boots,
- Menus,
- Ingame,
- Playable,
-};
-
-// Prioritize different compatibility reports based on user's platform
-enum class OSType {
-#ifdef Q_OS_WIN
- Win32 = 0,
- Unknown,
- Linux,
- macOS,
-#elif defined(Q_OS_LINUX)
- Linux = 0,
- Unknown,
- Win32,
- macOS,
-#elif defined(Q_OS_MAC)
- macOS = 0,
- Unknown,
- Linux,
- Win32,
-#endif
- // Fake enum to allow for iteration
- Last
-};
-
-struct CompatibilityEntry {
- CompatibilityStatus status;
- QString version;
- QDateTime last_tested;
- QString url;
- QString issue_number;
-};
-
-class CompatibilityInfoClass : public QObject {
- Q_OBJECT
-public:
- // Please think of a better alternative
- inline static const std::unordered_map LabelToCompatStatus = {
- {QStringLiteral("status-unknown"), CompatibilityStatus::Unknown},
- {QStringLiteral("status-nothing"), CompatibilityStatus::Nothing},
- {QStringLiteral("status-boots"), CompatibilityStatus::Boots},
- {QStringLiteral("status-menus"), CompatibilityStatus::Menus},
- {QStringLiteral("status-ingame"), CompatibilityStatus::Ingame},
- {QStringLiteral("status-playable"), CompatibilityStatus::Playable}};
- inline static const std::unordered_map LabelToOSType = {
- {QStringLiteral("os-linux"), OSType::Linux},
- {QStringLiteral("os-macOS"), OSType::macOS},
- {QStringLiteral("os-windows"), OSType::Win32},
- };
-
- inline static const std::unordered_map OSTypeToString = {
- {OSType::Linux, QStringLiteral("os-linux")},
- {OSType::macOS, QStringLiteral("os-macOS")},
- {OSType::Win32, QStringLiteral("os-windows")},
- {OSType::Unknown, QStringLiteral("os-unknown")}};
-
- CompatibilityInfoClass();
- ~CompatibilityInfoClass();
- void UpdateCompatibilityDatabase(QWidget* parent = nullptr, bool forced = false);
- bool LoadCompatibilityFile();
- CompatibilityEntry GetCompatibilityInfo(const std::string& serial);
- const QString GetCompatStatusString(const CompatibilityStatus status);
- void ExtractCompatibilityInfo(QByteArray response);
-
-private:
- QNetworkAccessManager* m_network_manager;
- QString m_compatibility_filename;
- QJsonObject m_compatibility_database;
-};
\ No newline at end of file
diff --git a/src/qt_gui/control_settings.cpp b/src/qt_gui/control_settings.cpp
deleted file mode 100644
index 21ed50af6..000000000
--- a/src/qt_gui/control_settings.cpp
+++ /dev/null
@@ -1,1038 +0,0 @@
-// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-#include
-#include
-#include
-#include
-#include "common/logging/log.h"
-#include "common/path_util.h"
-#include "control_settings.h"
-#include "input/input_handler.h"
-#include "ui_control_settings.h"
-
-ControlSettings::ControlSettings(std::shared_ptr game_info_get, bool isGameRunning,
- std::string GameRunningSerial, QWidget* parent)
- : QDialog(parent), m_game_info(game_info_get), GameRunning(isGameRunning),
- RunningGameSerial(GameRunningSerial), ui(new Ui::ControlSettings) {
-
- ui->setupUi(this);
-
- if (!GameRunning) {
- SDL_InitSubSystem(SDL_INIT_GAMEPAD);
- SDL_InitSubSystem(SDL_INIT_EVENTS);
- } else {
- SDL_SetHint(SDL_HINT_JOYSTICK_ALLOW_BACKGROUND_EVENTS, "1");
- }
-
- AddBoxItems();
- SetUIValuestoMappings();
- UpdateLightbarColor();
- CheckGamePad();
- installEventFilter(this);
-
- ButtonsList = {ui->CrossButton,
- ui->CircleButton,
- ui->TriangleButton,
- ui->SquareButton,
- ui->L1Button,
- ui->R1Button,
- ui->L2Button,
- ui->R2Button,
- ui->L3Button,
- ui->R3Button,
- ui->OptionsButton,
- ui->TouchpadLeftButton,
- ui->TouchpadCenterButton,
- ui->TouchpadRightButton,
- ui->DpadUpButton,
- ui->DpadDownButton,
- ui->DpadLeftButton,
- ui->DpadRightButton};
-
- AxisList = {ui->LStickUpButton, ui->LStickDownButton, ui->LStickLeftButton,
- ui->LStickRightButton, ui->RStickUpButton, ui->RStickDownButton,
- ui->RStickLeftButton, ui->RStickRightButton};
-
- for (auto& button : ButtonsList) {
- connect(button, &QPushButton::clicked, this,
- [this, &button]() { StartTimer(button, true); });
- }
-
- for (auto& button : AxisList) {
- connect(button, &QPushButton::clicked, this,
- [this, &button]() { StartTimer(button, false); });
- }
-
- connect(ui->buttonBox, &QDialogButtonBox::clicked, this, [this](QAbstractButton* button) {
- if (button == ui->buttonBox->button(QDialogButtonBox::Save)) {
- SaveControllerConfig(true);
- } else if (button == ui->buttonBox->button(QDialogButtonBox::RestoreDefaults)) {
- SetDefault();
- } else if (button == ui->buttonBox->button(QDialogButtonBox::Apply)) {
- SaveControllerConfig(false);
- }
- });
-
- ui->buttonBox->button(QDialogButtonBox::Save)->setText(tr("Save"));
- ui->buttonBox->button(QDialogButtonBox::Apply)->setText(tr("Apply"));
- ui->buttonBox->button(QDialogButtonBox::RestoreDefaults)->setText(tr("Restore Defaults"));
- ui->buttonBox->button(QDialogButtonBox::Cancel)->setText(tr("Cancel"));
-
- ui->PerGameCheckBox->setChecked(!Config::GetUseUnifiedInputConfig());
-
- connect(ui->buttonBox, &QDialogButtonBox::rejected, this, &QWidget::close);
-
- connect(ui->ProfileComboBox, &QComboBox::currentTextChanged, this, [this] {
- GetGameTitle();
- SetUIValuestoMappings();
- });
-
- connect(ui->LeftDeadzoneSlider, &QSlider::valueChanged, this,
- [this](int value) { ui->LeftDeadzoneValue->setText(QString::number(value)); });
- connect(ui->RightDeadzoneSlider, &QSlider::valueChanged, this,
- [this](int value) { ui->RightDeadzoneValue->setText(QString::number(value)); });
-
- connect(ui->RSlider, &QSlider::valueChanged, this, [this](int value) {
- QString RedValue = QString("%1").arg(value, 3, 10, QChar('0'));
- ui->RLabel->setText(RedValue);
- UpdateLightbarColor();
- });
-
- connect(ui->GSlider, &QSlider::valueChanged, this, [this](int value) {
- QString GreenValue = QString("%1").arg(value, 3, 10, QChar('0'));
- ui->GLabel->setText(GreenValue);
- UpdateLightbarColor();
- });
-
- connect(ui->BSlider, &QSlider::valueChanged, this, [this](int value) {
- QString BlueValue = QString("%1").arg(value, 3, 10, QChar('0'));
- ui->BLabel->setText(BlueValue);
- UpdateLightbarColor();
- });
-
- connect(this, &ControlSettings::PushGamepadEvent, this,
- [this]() { CheckMapping(MappingButton); });
- connect(this, &ControlSettings::AxisChanged, this,
- [this]() { ConnectAxisInputs(MappingButton); });
- connect(ui->ActiveGamepadBox, &QComboBox::currentIndexChanged, this,
- &ControlSettings::ActiveControllerChanged);
-
- connect(ui->DefaultGamepadButton, &QPushButton::clicked, this, [this]() {
- ui->DefaultGamepadName->setText(ui->ActiveGamepadBox->currentText());
- std::string GUID =
- GamepadSelect::GetGUIDString(gamepads, ui->ActiveGamepadBox->currentIndex());
- ui->DefaultGamepadLabel->setText(tr("ID: ") + QString::fromStdString(GUID).right(16));
- Config::setDefaultControllerID(GUID);
- Config::save(Common::FS::GetUserPath(Common::FS::PathType::UserDir) / "config.toml");
- QMessageBox::information(this, tr("Default Controller Selected"),
- tr("Active controller set as default"));
- });
-
- connect(ui->RemoveDefaultGamepadButton, &QPushButton::clicked, this, [this]() {
- ui->DefaultGamepadName->setText(tr("No default selected"));
- ui->DefaultGamepadLabel->setText(tr("n/a"));
- Config::setDefaultControllerID("");
- Config::save(Common::FS::GetUserPath(Common::FS::PathType::UserDir) / "config.toml");
- QMessageBox::information(this, tr("Default Controller Removed"),
- tr("Default controller setting removed"));
- });
-
- RemapWrapper = SdlEventWrapper::Wrapper::GetInstance();
- SdlEventWrapper::Wrapper::wrapperActive = true;
- QObject::connect(RemapWrapper, &SdlEventWrapper::Wrapper::SDLEvent, this,
- &ControlSettings::processSDLEvents);
-
- if (!GameRunning) {
- Polling = QtConcurrent::run(&ControlSettings::pollSDLEvents, this);
- }
-}
-
-void ControlSettings::SaveControllerConfig(bool CloseOnSave) {
- QList list;
- list << ui->RStickUpButton << ui->RStickRightButton << ui->LStickUpButton
- << ui->LStickRightButton;
- int count_axis_left_x = 0, count_axis_left_y = 0, count_axis_right_x = 0,
- count_axis_right_y = 0;
- for (const auto& i : list) {
- if (i->text() == "axis_left_x") {
- count_axis_left_x = count_axis_left_x + 1;
- } else if (i->text() == "axis_left_y") {
- count_axis_left_y = count_axis_left_y + 1;
- } else if (i->text() == "axis_right_x") {
- count_axis_right_x = count_axis_right_x + 1;
- } else if (i->text() == "axis_right_y") {
- count_axis_right_y = count_axis_right_y + 1;
- }
- }
-
- if (count_axis_left_x > 1 | count_axis_left_y > 1 | count_axis_right_x > 1 |
- count_axis_right_y > 1) {
- QMessageBox::information(this, tr("Unable to Save"),
- tr("Cannot bind axis values more than once"));
- return;
- }
-
- std::string config_id;
- config_id = (ui->ProfileComboBox->currentText() == tr("Common Config"))
- ? "default"
- : ui->ProfileComboBox->currentText().toStdString();
- const auto config_file = Config::GetFoolproofInputConfigFile(config_id);
-
- int lineCount = 0;
- std::string line;
- std::vector lines, inputs;
- std::string output_string = "", input_string = "";
- std::fstream file(config_file);
-
- while (std::getline(file, line)) {
- lineCount++;
-
- std::size_t comment_pos = line.find('#');
- if (comment_pos != std::string::npos) {
- if (!line.contains("Range of deadzones"))
- lines.push_back(line);
- continue;
- }
-
- std::size_t equal_pos = line.find('=');
- if (equal_pos == std::string::npos) {
- lines.push_back(line);
- continue;
- }
-
- output_string = line.substr(0, equal_pos - 1);
- input_string = line.substr(equal_pos + 2);
-
- if (output_string.contains("hotkey")) {
- lines.push_back(line);
- continue;
- }
-
- bool controllerInputdetected = false;
- for (std::string input : ControllerInputs) {
- // Needed to avoid detecting backspace while detecting back
- if (input_string.contains(input) && !input_string.contains("backspace")) {
- controllerInputdetected = true;
- break;
- }
- }
-
- if (controllerInputdetected || output_string == "analog_deadzone" ||
- output_string == "override_controller_color") {
- line.erase();
- continue;
- }
- lines.push_back(line);
- }
-
- file.close();
-
- // Lambda to reduce repetitive code for mapping buttons to config lines
- auto add_mapping = [&](const QString& buttonText, const std::string& output_name) {
- input_string = buttonText.toStdString();
- output_string = output_name;
- if (input_string != "unmapped") {
- lines.push_back(output_string + " = " + input_string);
- inputs.push_back(input_string);
- }
- };
-
- add_mapping(ui->CrossButton->text(), "cross");
- add_mapping(ui->CircleButton->text(), "circle");
- add_mapping(ui->SquareButton->text(), "square");
- add_mapping(ui->TriangleButton->text(), "triangle");
-
- lines.push_back("");
-
- add_mapping(ui->L1Button->text(), "l1");
- add_mapping(ui->R1Button->text(), "r1");
- add_mapping(ui->L2Button->text(), "l2");
- add_mapping(ui->R2Button->text(), "r2");
- add_mapping(ui->L3Button->text(), "l3");
- add_mapping(ui->R3Button->text(), "r3");
-
- lines.push_back("");
-
- add_mapping(ui->TouchpadLeftButton->text(), "touchpad_left");
- add_mapping(ui->TouchpadCenterButton->text(), "touchpad_center");
- add_mapping(ui->TouchpadRightButton->text(), "touchpad_right");
- add_mapping(ui->OptionsButton->text(), "options");
-
- lines.push_back("");
-
- add_mapping(ui->DpadUpButton->text(), "pad_up");
- add_mapping(ui->DpadDownButton->text(), "pad_down");
- add_mapping(ui->DpadLeftButton->text(), "pad_left");
- add_mapping(ui->DpadRightButton->text(), "pad_right");
-
- lines.push_back("");
-
- output_string = "axis_left_x";
- input_string = ui->LStickRightButton->text().toStdString();
- lines.push_back(output_string + " = " + input_string);
-
- output_string = "axis_left_y";
- input_string = ui->LStickUpButton->text().toStdString();
- lines.push_back(output_string + " = " + input_string);
-
- output_string = "axis_right_x";
- input_string = ui->RStickRightButton->text().toStdString();
- lines.push_back(output_string + " = " + input_string);
-
- output_string = "axis_right_y";
- input_string = ui->RStickUpButton->text().toStdString();
- lines.push_back(output_string + " = " + input_string);
-
- lines.push_back("");
- lines.push_back("# Range of deadzones: 1 (almost none) to 127 (max)");
-
- std::string deadzonevalue = std::to_string(ui->LeftDeadzoneSlider->value());
- lines.push_back("analog_deadzone = leftjoystick, " + deadzonevalue + ", 127");
-
- deadzonevalue = std::to_string(ui->RightDeadzoneSlider->value());
- lines.push_back("analog_deadzone = rightjoystick, " + deadzonevalue + ", 127");
-
- lines.push_back("");
- std::string OverrideLB = ui->LightbarCheckBox->isChecked() ? "true" : "false";
- std::string LightBarR = std::to_string(ui->RSlider->value());
- std::string LightBarG = std::to_string(ui->GSlider->value());
- std::string LightBarB = std::to_string(ui->BSlider->value());
- lines.push_back("override_controller_color = " + OverrideLB + ", " + LightBarR + ", " +
- LightBarG + ", " + LightBarB);
-
- // Prevent duplicate inputs that break the input engine
- bool duplicateFound = false;
- QSet duplicateMappings;
-
- for (auto it = inputs.begin(); it != inputs.end(); ++it) {
- if (std::find(it + 1, inputs.end(), *it) != inputs.end()) {
- duplicateFound = true;
- duplicateMappings.insert(QString::fromStdString(*it));
- }
- }
-
- if (duplicateFound) {
- QStringList duplicatesList;
- for (const QString mapping : duplicateMappings) {
- for (const auto& button : ButtonsList) {
- if (button->text() == mapping)
- duplicatesList.append(button->objectName() + " - " + mapping);
- }
- }
- QMessageBox::information(
- this, tr("Unable to Save"),
- // clang-format off
- QString(tr("Cannot bind any unique input more than once. Duplicate inputs mapped to the following buttons:\n\n%1").arg(duplicatesList.join("\n"))));
- // clang-format on
- return;
- }
-
- std::vector save;
- bool CurrentLineEmpty = false, LastLineEmpty = false;
- for (auto const& line : lines) {
- LastLineEmpty = CurrentLineEmpty ? true : false;
- CurrentLineEmpty = line.empty() ? true : false;
- if (!CurrentLineEmpty || !LastLineEmpty)
- save.push_back(line);
- }
-
- std::ofstream output_file(config_file);
- for (auto const& line : save) {
- output_file << line << '\n';
- }
- output_file.close();
-
- Config::SetUseUnifiedInputConfig(!ui->PerGameCheckBox->isChecked());
- Config::SetOverrideControllerColor(ui->LightbarCheckBox->isChecked());
- Config::SetControllerCustomColor(ui->RSlider->value(), ui->GSlider->value(),
- ui->BSlider->value());
- Config::save(Common::FS::GetUserPath(Common::FS::PathType::UserDir) / "config.toml");
-
- if (GameRunning) {
- Config::GetUseUnifiedInputConfig() ? Input::ParseInputConfig("default")
- : Input::ParseInputConfig(RunningGameSerial);
- }
-
- if (CloseOnSave)
- QWidget::close();
-}
-
-void ControlSettings::SetDefault() {
- ui->CrossButton->setText("cross");
- ui->CircleButton->setText("circle");
- ui->SquareButton->setText("square");
- ui->TriangleButton->setText("triangle");
- ui->DpadUpButton->setText("pad_up");
- ui->DpadDownButton->setText("pad_down");
- ui->DpadLeftButton->setText("pad_left");
- ui->DpadRightButton->setText("pad_right");
- ui->L3Button->setText("l3");
- ui->R3Button->setText("r3");
- ui->L1Button->setText("l1");
- ui->R1Button->setText("r1");
- ui->L2Button->setText("l2");
- ui->R2Button->setText("r2");
- ui->OptionsButton->setText("options");
- ui->TouchpadLeftButton->setText("back");
- ui->TouchpadCenterButton->setText("unmapped");
- ui->TouchpadRightButton->setText("unmapped");
-
- ui->LStickUpButton->setText("axis_left_y");
- ui->LStickDownButton->setText("axis_left_y");
- ui->LStickLeftButton->setText("axis_left_x");
- ui->LStickRightButton->setText("axis_left_x");
- ui->RStickUpButton->setText("axis_right_y");
- ui->RStickDownButton->setText("axis_right_y");
- ui->RStickLeftButton->setText("axis_right_x");
- ui->RStickRightButton->setText("axis_right_x");
-
- ui->LeftDeadzoneSlider->setValue(2);
- ui->RightDeadzoneSlider->setValue(2);
-
- ui->RSlider->setValue(0);
- ui->GSlider->setValue(0);
- ui->BSlider->setValue(255);
- ui->LightbarCheckBox->setChecked(false);
- ui->PerGameCheckBox->setChecked(false);
-}
-
-void ControlSettings::AddBoxItems() {
- ui->ProfileComboBox->addItem(tr("Common Config"));
- for (int i = 0; i < m_game_info->m_games.size(); i++) {
- ui->ProfileComboBox->addItem(QString::fromStdString(m_game_info->m_games[i].serial));
- }
- ui->ProfileComboBox->setCurrentText(tr("Common Config"));
- ui->TitleLabel->setText(tr("Common Config"));
-}
-
-void ControlSettings::SetUIValuestoMappings() {
- std::string config_id;
- config_id = (ui->ProfileComboBox->currentText() == tr("Common Config"))
- ? "default"
- : ui->ProfileComboBox->currentText().toStdString();
-
- const auto config_file = Config::GetFoolproofInputConfigFile(config_id);
- std::ifstream file(config_file);
-
- bool CrossExists = false, CircleExists = false, SquareExists = false, TriangleExists = false,
- L1Exists = false, L2Exists = false, L3Exists = false, R1Exists = false, R2Exists = false,
- R3Exists = false, DPadUpExists = false, DPadDownExists = false, DPadLeftExists = false,
- DPadRightExists = false, OptionsExists = false, TouchpadLeftExists = false,
- TouchpadCenterExists = false, TouchpadRightExists = false, LStickXExists = false,
- LStickYExists = false, RStickXExists = false, RStickYExists = false;
- int lineCount = 0;
- std::string line = "";
- while (std::getline(file, line)) {
- lineCount++;
-
- line.erase(std::remove(line.begin(), line.end(), ' '), line.end());
- if (line.empty())
- continue;
-
- std::size_t comment_pos = line.find('#');
- if (comment_pos != std::string::npos)
- line = line.substr(0, comment_pos);
-
- std::size_t equal_pos = line.find('=');
- if (equal_pos == std::string::npos)
- continue;
-
- std::string output_string = line.substr(0, equal_pos);
- std::string input_string = line.substr(equal_pos + 1);
-
- bool controllerInputdetected = false;
- for (std::string input : ControllerInputs) {
- // Needed to avoid detecting backspace while detecting back
- if (input_string.contains(input) && !input_string.contains("backspace")) {
- controllerInputdetected = true;
- break;
- }
- }
-
- if (controllerInputdetected) {
- if (output_string == "cross") {
- ui->CrossButton->setText(QString::fromStdString(input_string));
- CrossExists = true;
- } else if (output_string == "circle") {
- ui->CircleButton->setText(QString::fromStdString(input_string));
- CircleExists = true;
- } else if (output_string == "square") {
- ui->SquareButton->setText(QString::fromStdString(input_string));
- SquareExists = true;
- } else if (output_string == "triangle") {
- ui->TriangleButton->setText(QString::fromStdString(input_string));
- TriangleExists = true;
- } else if (output_string == "l1") {
- ui->L1Button->setText(QString::fromStdString(input_string));
- L1Exists = true;
- } else if (output_string == "l2") {
- ui->L2Button->setText(QString::fromStdString(input_string));
- L2Exists = true;
- } else if (output_string == "r1") {
- ui->R1Button->setText(QString::fromStdString(input_string));
- R1Exists = true;
- } else if (output_string == "r2") {
- ui->R2Button->setText(QString::fromStdString(input_string));
- R2Exists = true;
- } else if (output_string == "l3") {
- ui->L3Button->setText(QString::fromStdString(input_string));
- L3Exists = true;
- } else if (output_string == "r3") {
- ui->R3Button->setText(QString::fromStdString(input_string));
- R3Exists = true;
- } else if (output_string == "pad_up") {
- ui->DpadUpButton->setText(QString::fromStdString(input_string));
- DPadUpExists = true;
- } else if (output_string == "pad_down") {
- ui->DpadDownButton->setText(QString::fromStdString(input_string));
- DPadDownExists = true;
- } else if (output_string == "pad_left") {
- ui->DpadLeftButton->setText(QString::fromStdString(input_string));
- DPadLeftExists = true;
- } else if (output_string == "pad_right") {
- ui->DpadRightButton->setText(QString::fromStdString(input_string));
- DPadRightExists = true;
- } else if (output_string == "options") {
- ui->OptionsButton->setText(QString::fromStdString(input_string));
- OptionsExists = true;
- } else if (output_string == "touchpad_left") {
- ui->TouchpadLeftButton->setText(QString::fromStdString(input_string));
- TouchpadLeftExists = true;
- } else if (output_string == "touchpad_center") {
- ui->TouchpadCenterButton->setText(QString::fromStdString(input_string));
- TouchpadCenterExists = true;
- } else if (output_string == "touchpad_right") {
- ui->TouchpadRightButton->setText(QString::fromStdString(input_string));
- TouchpadRightExists = true;
- } else if (output_string == "axis_left_x") {
- ui->LStickRightButton->setText(QString::fromStdString(input_string));
- ui->LStickLeftButton->setText(QString::fromStdString(input_string));
- LStickXExists = true;
- } else if (output_string == "axis_left_y") {
- ui->LStickUpButton->setText(QString::fromStdString(input_string));
- ui->LStickDownButton->setText(QString::fromStdString(input_string));
- LStickYExists = true;
- } else if (output_string == "axis_right_x") {
- ui->RStickRightButton->setText(QString::fromStdString(input_string));
- ui->RStickLeftButton->setText(QString::fromStdString(input_string));
- RStickXExists = true;
- } else if (output_string == "axis_right_y") {
- ui->RStickUpButton->setText(QString::fromStdString(input_string));
- ui->RStickDownButton->setText(QString::fromStdString(input_string));
- RStickYExists = true;
- }
- }
-
- if (input_string.contains("leftjoystick")) {
- std::size_t comma_pos = line.find(',');
- if (comma_pos != std::string::npos) {
- int deadzonevalue = std::stoi(line.substr(comma_pos + 1));
- ui->LeftDeadzoneSlider->setValue(deadzonevalue);
- ui->LeftDeadzoneValue->setText(QString::number(deadzonevalue));
- } else {
- ui->LeftDeadzoneSlider->setValue(2);
- ui->LeftDeadzoneValue->setText("2");
- }
- }
-
- if (input_string.contains("rightjoystick")) {
- std::size_t comma_pos = line.find(',');
- if (comma_pos != std::string::npos) {
- int deadzonevalue = std::stoi(line.substr(comma_pos + 1));
- ui->RightDeadzoneSlider->setValue(deadzonevalue);
- ui->RightDeadzoneValue->setText(QString::number(deadzonevalue));
- } else {
- ui->RightDeadzoneSlider->setValue(2);
- ui->RightDeadzoneValue->setText("2");
- }
- }
-
- if (output_string == "override_controller_color") {
- std::size_t comma_pos = line.find(',');
- if (comma_pos != std::string::npos) {
- std::string overridestring = line.substr(equal_pos + 1, comma_pos);
- bool override = overridestring.contains("true") ? true : false;
- ui->LightbarCheckBox->setChecked(override);
-
- std::string lightbarstring = line.substr(comma_pos + 1);
- std::size_t comma_pos2 = lightbarstring.find(',');
- if (comma_pos2 != std::string::npos) {
- std::string Rstring = lightbarstring.substr(0, comma_pos2);
- ui->RSlider->setValue(std::stoi(Rstring));
- QString RedValue = QString("%1").arg(std::stoi(Rstring), 3, 10, QChar('0'));
- ui->RLabel->setText(RedValue);
- }
-
- std::string GBstring = lightbarstring.substr(comma_pos2 + 1);
- std::size_t comma_pos3 = GBstring.find(',');
- if (comma_pos3 != std::string::npos) {
- std::string Gstring = GBstring.substr(0, comma_pos3);
- ui->GSlider->setValue(std::stoi(Gstring));
- QString GreenValue = QString("%1").arg(std::stoi(Gstring), 3, 10, QChar('0'));
- ui->GLabel->setText(GreenValue);
-
- std::string Bstring = GBstring.substr(comma_pos3 + 1);
- ui->BSlider->setValue(std::stoi(Bstring));
- QString BlueValue = QString("%1").arg(std::stoi(Bstring), 3, 10, QChar('0'));
- ui->BLabel->setText(BlueValue);
- }
- }
- }
- }
- file.close();
-
- // If an entry does not exist in the config file, we assume the user wants it unmapped
- if (!CrossExists)
- ui->CrossButton->setText("unmapped");
- if (!CircleExists)
- ui->CircleButton->setText("unmapped");
- if (!SquareExists)
- ui->SquareButton->setText("unmapped");
- if (!TriangleExists)
- ui->TriangleButton->setText("unmapped");
- if (!L1Exists)
- ui->L1Button->setText("unmapped");
- if (!L2Exists)
- ui->L2Button->setText("unmapped");
- if (!L3Exists)
- ui->L3Button->setText("unmapped");
- if (!R1Exists)
- ui->R1Button->setText("unmapped");
- if (!R2Exists)
- ui->R2Button->setText("unmapped");
- if (!R3Exists)
- ui->R3Button->setText("unmapped");
- if (!DPadUpExists)
- ui->DpadUpButton->setText("unmapped");
- if (!DPadDownExists)
- ui->DpadDownButton->setText("unmapped");
- if (!DPadLeftExists)
- ui->DpadLeftButton->setText("unmapped");
- if (!DPadRightExists)
- ui->DpadRightButton->setText("unmapped");
- if (!TouchpadLeftExists)
- ui->TouchpadLeftButton->setText("unmapped");
- if (!TouchpadCenterExists)
- ui->TouchpadCenterButton->setText("unmapped");
- if (!TouchpadRightExists)
- ui->TouchpadRightButton->setText("unmapped");
- if (!OptionsExists)
- ui->OptionsButton->setText("unmapped");
-
- if (!LStickXExists) {
- ui->LStickRightButton->setText("unmapped");
- ui->LStickLeftButton->setText("unmapped");
- }
- if (!LStickYExists) {
- ui->LStickUpButton->setText("unmapped");
- ui->LStickDownButton->setText("unmapped");
- }
- if (!RStickXExists) {
- ui->RStickRightButton->setText("unmapped");
- ui->RStickLeftButton->setText("unmapped");
- }
- if (!RStickYExists) {
- ui->RStickUpButton->setText("unmapped");
- ui->RStickDownButton->setText("unmapped");
- }
-}
-
-void ControlSettings::GetGameTitle() {
- if (ui->ProfileComboBox->currentText() == tr("Common Config")) {
- ui->TitleLabel->setText(tr("Common Config"));
- } else {
- for (int i = 0; i < m_game_info->m_games.size(); i++) {
- if (m_game_info->m_games[i].serial ==
- ui->ProfileComboBox->currentText().toStdString()) {
- ui->TitleLabel->setText(QString::fromStdString(m_game_info->m_games[i].name));
- }
- }
- }
-}
-
-void ControlSettings::UpdateLightbarColor() {
- ui->LightbarColorFrame->setStyleSheet("");
- QString RValue = QString::number(ui->RSlider->value());
- QString GValue = QString::number(ui->GSlider->value());
- QString BValue = QString::number(ui->BSlider->value());
- QString colorstring = "background-color: rgb(" + RValue + "," + GValue + "," + BValue + ")";
- ui->LightbarColorFrame->setStyleSheet(colorstring);
-}
-
-void ControlSettings::ActiveControllerChanged(int value) {
- GamepadSelect::SetSelectedGamepad(GamepadSelect::GetGUIDString(gamepads, value));
- QString GUID = QString::fromStdString(GamepadSelect::GetSelectedGamepad()).right(16);
- ui->ActiveGamepadLabel->setText("ID: " + GUID);
-
- if (gamepad) {
- SDL_CloseGamepad(gamepad);
- gamepad = nullptr;
- }
-
- gamepad = SDL_OpenGamepad(gamepads[value]);
-
- if (!gamepad) {
- LOG_ERROR(Input, "Failed to open gamepad: {}", SDL_GetError());
- }
-}
-
-void ControlSettings::CheckGamePad() {
- if (gamepad) {
- SDL_CloseGamepad(gamepad);
- gamepad = nullptr;
- }
-
- gamepads = SDL_GetGamepads(&gamepad_count);
-
- if (!gamepads) {
- LOG_ERROR(Input, "Cannot get gamepad list: {}", SDL_GetError());
- }
-
- QString defaultGUID = "";
- int defaultIndex =
- GamepadSelect::GetIndexfromGUID(gamepads, gamepad_count, Config::getDefaultControllerID());
- int activeIndex = GamepadSelect::GetIndexfromGUID(gamepads, gamepad_count,
- GamepadSelect::GetSelectedGamepad());
-
- if (!GameRunning) {
- if (activeIndex != -1) {
- gamepad = SDL_OpenGamepad(gamepads[activeIndex]);
- } else if (defaultIndex != -1) {
- gamepad = SDL_OpenGamepad(gamepads[defaultIndex]);
- } else {
- LOG_INFO(Input, "Got {} gamepads. Opening the first one.", gamepad_count);
- gamepad = SDL_OpenGamepad(gamepads[0]);
- }
-
- if (!gamepad) {
- LOG_ERROR(Input, "Failed to open gamepad: {}", SDL_GetError());
- }
- }
-
- if (!gamepads || gamepad_count == 0) {
- ui->ActiveGamepadBox->addItem("No gamepads detected");
- ui->ActiveGamepadBox->setCurrentIndex(0);
- return;
- } else {
- for (int i = 0; i < gamepad_count; i++) {
- QString name = SDL_GetGamepadNameForID(gamepads[i]);
- ui->ActiveGamepadBox->addItem(QString("%1: %2").arg(QString::number(i + 1), name));
- }
- }
-
- if (defaultIndex != -1) {
- defaultGUID =
- QString::fromStdString(GamepadSelect::GetGUIDString(gamepads, defaultIndex)).right(16);
- ui->DefaultGamepadName->setText(SDL_GetGamepadNameForID(gamepads[defaultIndex]));
- ui->DefaultGamepadLabel->setText(tr("ID: ") + defaultGUID);
- } else {
- ui->DefaultGamepadName->setText("Default controller not connected");
- ui->DefaultGamepadLabel->setText(tr("n/a"));
- }
-
- if (activeIndex != -1) {
- QString GUID =
- QString::fromStdString(GamepadSelect::GetGUIDString(gamepads, activeIndex)).right(16);
- ui->ActiveGamepadLabel->setText(tr("ID: ") + GUID);
- ui->ActiveGamepadBox->setCurrentIndex(activeIndex);
- } else if (defaultIndex != -1) {
- ui->ActiveGamepadLabel->setText(defaultGUID);
- ui->ActiveGamepadBox->setCurrentIndex(defaultIndex);
- } else {
- QString GUID = QString::fromStdString(GamepadSelect::GetGUIDString(gamepads, 0)).right(16);
- ui->ActiveGamepadLabel->setText("ID: " + GUID);
- ui->ActiveGamepadBox->setCurrentIndex(0);
- }
-}
-
-void ControlSettings::DisableMappingButtons() {
- for (const auto& i : ButtonsList) {
- i->setEnabled(false);
- }
-
- for (const auto& i : AxisList) {
- i->setEnabled(false);
- }
-
- ui->buttonBox->button(QDialogButtonBox::Save)->setEnabled(false);
- ui->buttonBox->button(QDialogButtonBox::Apply)->setEnabled(false);
- ui->buttonBox->button(QDialogButtonBox::RestoreDefaults)->setEnabled(false);
-}
-
-void ControlSettings::EnableMappingButtons() {
- for (const auto& i : ButtonsList) {
- i->setEnabled(true);
- }
-
- for (const auto& i : AxisList) {
- i->setEnabled(true);
- }
-
- ui->buttonBox->button(QDialogButtonBox::Save)->setEnabled(true);
- ui->buttonBox->button(QDialogButtonBox::Apply)->setEnabled(true);
- ui->buttonBox->button(QDialogButtonBox::RestoreDefaults)->setEnabled(true);
-}
-
-void ControlSettings::ConnectAxisInputs(QPushButton*& button) {
- QString input = button->text();
- if (button == ui->LStickUpButton) {
- ui->LStickDownButton->setText(input);
- } else if (button == ui->LStickDownButton) {
- ui->LStickUpButton->setText(input);
- } else if (button == ui->LStickLeftButton) {
- ui->LStickRightButton->setText(input);
- } else if (button == ui->LStickRightButton) {
- ui->LStickLeftButton->setText(input);
- } else if (button == ui->RStickUpButton) {
- ui->RStickDownButton->setText(input);
- } else if (button == ui->RStickDownButton) {
- ui->RStickUpButton->setText(input);
- } else if (button == ui->RStickLeftButton) {
- ui->RStickRightButton->setText(input);
- } else if (button == ui->RStickRightButton) {
- ui->RStickLeftButton->setText(input);
- }
-}
-
-void ControlSettings::StartTimer(QPushButton*& button, bool isButton) {
- MappingTimer = 3;
- isButton ? EnableButtonMapping = true : EnableAxisMapping = true;
- MappingCompleted = false;
- mapping = button->text();
- DisableMappingButtons();
-
- EnableButtonMapping
- ? button->setText(tr("Press a button") + " [" + QString::number(MappingTimer) + "]")
- : button->setText(tr("Move analog stick") + " [" + QString::number(MappingTimer) + "]");
-
- timer = new QTimer(this);
- MappingButton = button;
- timer->start(1000);
- connect(timer, &QTimer::timeout, this, [this]() { CheckMapping(MappingButton); });
-}
-
-void ControlSettings::CheckMapping(QPushButton*& button) {
- MappingTimer -= 1;
- EnableButtonMapping
- ? button->setText(tr("Press a button") + " [" + QString::number(MappingTimer) + "]")
- : button->setText(tr("Move analog stick") + " [" + QString::number(MappingTimer) + "]");
-
- if (pressedButtons.size() > 0) {
- QStringList keyStrings;
-
- for (const QString& buttonAction : pressedButtons) {
- keyStrings << buttonAction;
- }
-
- QString combo = keyStrings.join(",");
- SetMapping(combo);
- MappingButton->setText(combo);
- pressedButtons.clear();
- }
-
- if (MappingCompleted || MappingTimer <= 0) {
- button->setText(mapping);
- EnableButtonMapping = false;
- EnableAxisMapping = false;
- L2Pressed = false;
- R2Pressed = false;
- EnableMappingButtons();
- timer->stop();
- }
-}
-
-void ControlSettings::SetMapping(QString input) {
- mapping = input;
- MappingCompleted = true;
- if (EnableAxisMapping) {
- emit PushGamepadEvent();
- emit AxisChanged();
- }
-}
-
-// use QT events instead of SDL to override default event closing the window with escape
-bool ControlSettings::eventFilter(QObject* obj, QEvent* event) {
- if (event->type() == QEvent::KeyPress && EnableButtonMapping) {
- QKeyEvent* keyEvent = static_cast(event);
- if (keyEvent->key() == Qt::Key_Escape) {
- SetMapping("unmapped");
- return true;
- }
- }
- return QDialog::eventFilter(obj, event);
-}
-
-void ControlSettings::processSDLEvents(int Type, int Input, int Value) {
- if (EnableButtonMapping) {
-
- if (pressedButtons.size() >= 3) {
- return;
- }
-
- if (Type == SDL_EVENT_GAMEPAD_BUTTON_DOWN) {
- switch (Input) {
- case SDL_GAMEPAD_BUTTON_SOUTH:
- pressedButtons.insert(5, "cross");
- break;
- case SDL_GAMEPAD_BUTTON_EAST:
- pressedButtons.insert(6, "circle");
- break;
- case SDL_GAMEPAD_BUTTON_NORTH:
- pressedButtons.insert(7, "triangle");
- break;
- case SDL_GAMEPAD_BUTTON_WEST:
- pressedButtons.insert(8, "square");
- break;
- case SDL_GAMEPAD_BUTTON_LEFT_SHOULDER:
- pressedButtons.insert(3, "l1");
- break;
- case SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER:
- pressedButtons.insert(4, "r1");
- break;
- case SDL_GAMEPAD_BUTTON_LEFT_STICK:
- pressedButtons.insert(9, "l3");
- break;
- case SDL_GAMEPAD_BUTTON_RIGHT_STICK:
- pressedButtons.insert(10, "r3");
- break;
- case SDL_GAMEPAD_BUTTON_DPAD_UP:
- pressedButtons.insert(13, "pad_up");
- break;
- case SDL_GAMEPAD_BUTTON_DPAD_DOWN:
- pressedButtons.insert(14, "pad_down");
- break;
- case SDL_GAMEPAD_BUTTON_DPAD_LEFT:
- pressedButtons.insert(15, "pad_left");
- break;
- case SDL_GAMEPAD_BUTTON_DPAD_RIGHT:
- pressedButtons.insert(16, "pad_right");
- break;
- case SDL_GAMEPAD_BUTTON_BACK:
- pressedButtons.insert(11, "back");
- break;
- case SDL_GAMEPAD_BUTTON_LEFT_PADDLE1:
- pressedButtons.insert(17, "lpaddle_high");
- break;
- case SDL_GAMEPAD_BUTTON_RIGHT_PADDLE1:
- pressedButtons.insert(18, "rpaddle_high");
- break;
- case SDL_GAMEPAD_BUTTON_LEFT_PADDLE2:
- pressedButtons.insert(19, "lpaddle_low");
- break;
- case SDL_GAMEPAD_BUTTON_RIGHT_PADDLE2:
- pressedButtons.insert(20, "rpaddle_low");
- break;
- case SDL_GAMEPAD_BUTTON_START:
- pressedButtons.insert(12, "options");
- break;
- default:
- break;
- }
- }
-
- if (Type == SDL_EVENT_GAMEPAD_AXIS_MOTION) {
- // SDL trigger axis values range from 0 to 32000, set mapping on half movement
- // Set zone for trigger release signal arbitrarily at 5000
- switch (Input) {
- case SDL_GAMEPAD_AXIS_LEFT_TRIGGER:
- if (Value > 16000) {
- pressedButtons.insert(1, "l2");
- L2Pressed = true;
- } else if (Value < 5000) {
- if (L2Pressed && !R2Pressed)
- emit PushGamepadEvent();
- }
- break;
- case SDL_GAMEPAD_AXIS_RIGHT_TRIGGER:
- if (Value > 16000) {
- pressedButtons.insert(2, "r2");
- R2Pressed = true;
- } else if (Value < 5000) {
- if (R2Pressed && !L2Pressed)
- emit PushGamepadEvent();
- }
- break;
- default:
- break;
- }
- }
-
- if (Type == SDL_EVENT_GAMEPAD_BUTTON_UP)
- emit PushGamepadEvent();
-
- } else if (EnableAxisMapping) {
- if (Type == SDL_EVENT_GAMEPAD_AXIS_MOTION) {
- // SDL stick axis values range from -32000 to 32000, set mapping on half movement
- if (Value > 16000 || Value < -16000) {
- switch (Input) {
- case SDL_GAMEPAD_AXIS_LEFTX:
- SetMapping("axis_left_x");
- break;
- case SDL_GAMEPAD_AXIS_LEFTY:
- SetMapping("axis_left_y");
- break;
- case SDL_GAMEPAD_AXIS_RIGHTX:
- SetMapping("axis_right_x");
- break;
- case SDL_GAMEPAD_AXIS_RIGHTY:
- SetMapping("axis_right_y");
- break;
- default:
- break;
- }
- }
- }
- }
-
- if (Type == SDL_EVENT_GAMEPAD_ADDED || SDL_EVENT_GAMEPAD_REMOVED) {
- ui->ActiveGamepadBox->clear();
- CheckGamePad();
- }
-}
-
-void ControlSettings::pollSDLEvents() {
- SDL_Event event;
- while (SdlEventWrapper::Wrapper::wrapperActive) {
-
- if (!SDL_WaitEvent(&event)) {
- return;
- }
-
- if (event.type == SDL_EVENT_QUIT) {
- return;
- }
-
- SdlEventWrapper::Wrapper::GetInstance()->Wrapper::ProcessEvent(&event);
- }
-}
-
-void ControlSettings::Cleanup() {
- SdlEventWrapper::Wrapper::wrapperActive = false;
- if (gamepad) {
- SDL_CloseGamepad(gamepad);
- gamepad = nullptr;
- }
-
- SDL_free(gamepads);
-
- if (!GameRunning) {
- SDL_Event quitLoop{};
- quitLoop.type = SDL_EVENT_QUIT;
- SDL_PushEvent(&quitLoop);
- Polling.waitForFinished();
-
- SDL_QuitSubSystem(SDL_INIT_GAMEPAD);
- SDL_QuitSubSystem(SDL_INIT_EVENTS);
- SDL_Quit();
- } else {
- if (!Config::getBackgroundControllerInput()) {
- SDL_SetHint(SDL_HINT_JOYSTICK_ALLOW_BACKGROUND_EVENTS, "0");
- }
- SDL_Event checkGamepad{};
- checkGamepad.type = SDL_EVENT_CHANGE_CONTROLLER;
- SDL_PushEvent(&checkGamepad);
- }
-}
-
-ControlSettings::~ControlSettings() {}
diff --git a/src/qt_gui/control_settings.h b/src/qt_gui/control_settings.h
deleted file mode 100644
index 1fa6337bb..000000000
--- a/src/qt_gui/control_settings.h
+++ /dev/null
@@ -1,87 +0,0 @@
-// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
-// SPDX-License-Identifier: GPL-2.0-or-later
-#pragma once
-#include
-#include
-#include
-#include "game_info.h"
-#include "sdl_event_wrapper.h"
-
-namespace Ui {
-class ControlSettings;
-}
-
-class ControlSettings : public QDialog {
- Q_OBJECT
-public:
- explicit ControlSettings(std::shared_ptr game_info_get, bool GameRunning,
- std::string GameRunningSerial, QWidget* parent = nullptr);
- ~ControlSettings();
-
-signals:
- void PushGamepadEvent();
- void AxisChanged();
-
-private Q_SLOTS:
- void SaveControllerConfig(bool CloseOnSave);
- void SetDefault();
- void UpdateLightbarColor();
- void CheckMapping(QPushButton*& button);
- void StartTimer(QPushButton*& button, bool isButton);
- void ConnectAxisInputs(QPushButton*& button);
- void ActiveControllerChanged(int value);
-
-private:
- std::unique_ptr ui;
- std::shared_ptr m_game_info;
-
- bool eventFilter(QObject* obj, QEvent* event) override;
- void AddBoxItems();
- void SetUIValuestoMappings();
- void GetGameTitle();
- void CheckGamePad();
- void processSDLEvents(int Type, int Input, int Value);
- void pollSDLEvents();
- void SetMapping(QString input);
- void DisableMappingButtons();
- void EnableMappingButtons();
- void Cleanup();
-
- // use QMap instead of QSet to maintain order of inserted strings
- QMap pressedButtons;
- QList ButtonsList;
- QList AxisList;
-
- std::string RunningGameSerial;
- bool GameRunning;
- bool L2Pressed = false;
- bool R2Pressed = false;
- bool EnableButtonMapping = false;
- bool EnableAxisMapping = false;
- bool MappingCompleted = false;
- QString mapping;
- int MappingTimer;
- int gamepad_count;
- QTimer* timer;
- QPushButton* MappingButton;
- SDL_Gamepad* gamepad = nullptr;
- SDL_JoystickID* gamepads;
- SdlEventWrapper::Wrapper* RemapWrapper;
- QFuture Polling;
-
- const std::vector ControllerInputs = {
- "cross", "circle", "square", "triangle", "l1",
- "r1", "l2", "r2", "l3",
-
- "r3", "options", "pad_up",
-
- "pad_down",
-
- "pad_left", "pad_right", "axis_left_x", "axis_left_y", "axis_right_x",
- "axis_right_y", "back"};
-
-protected:
- void closeEvent(QCloseEvent* event) override {
- Cleanup();
- }
-};
diff --git a/src/qt_gui/control_settings.ui b/src/qt_gui/control_settings.ui
deleted file mode 100644
index bdb2146bc..000000000
--- a/src/qt_gui/control_settings.ui
+++ /dev/null
@@ -1,1816 +0,0 @@
-
-
-
- ControlSettings
-
-
- Qt::WindowModality::WindowModal
-
-
-
- 0
- 0
- 1124
- 847
-
-
-
- Configure Controls
-
-
-
- :/rpcs3.ico:/rpcs3.ico
-
-
- -
-
-
- true
-
-
-
-
- 0
- 0
- 1104
- 797
-
-
-
-
-
- 0
- 0
- 1101
- 791
-
-
-
-
-
-
-
- 5
-
-
-
-
-
- true
-
-
-
- 0
- 0
-
-
-
-
- 16777215
- 16777215
-
-
-
- D-Pad
-
-
-
- 6
-
-
- 5
-
-
- 5
-
-
- 5
-
-
- 5
-
-
-
-
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 0
-
-
-
-
-
-
- 152
- 0
-
-
-
-
- 0
- 16777215
-
-
-
- Up
-
-
- Qt::AlignmentFlag::AlignCenter
-
-
-
-
-
-
- unmapped
-
-
-
-
-
-
-
-
-
- -
-
-
-
-
-
- Left
-
-
- Qt::AlignmentFlag::AlignCenter
-
-
-
- 5
-
-
- 5
-
-
- 5
-
-
- 5
-
-
-
-
-
- unmapped
-
-
-
-
-
-
- -
-
-
- Right
-
-
- Qt::AlignmentFlag::AlignCenter
-
-
-
- 5
-
-
- 5
-
-
- 5
-
-
- 5
-
-
-
-
-
- unmapped
-
-
-
-
-
-
-
-
- -
-
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 0
-
-
-
-
-
-
- 152
- 0
-
-
-
-
- 124
- 16777215
-
-
-
- Down
-
-
- Qt::AlignmentFlag::AlignCenter
-
-
-
-
-
-
- unmapped
-
-
-
-
-
-
-
-
-
-
-
-
- -
-
-
- L1 and L2
-
-
-
-
-
-
-
- 0
- 0
-
-
-
- L1
-
-
- Qt::AlignmentFlag::AlignCenter
-
-
-
- 5
-
-
- 5
-
-
- 5
-
-
- 5
-
-
-
-
-
- unmapped
-
-
-
-
-
-
- -
-
-
- L2
-
-
- Qt::AlignmentFlag::AlignCenter
-
-
-
- 5
-
-
- 5
-
-
- 5
-
-
- 5
-
-
-
-
-
- unmapped
-
-
-
-
-
-
-
-
-
- -
-
-
- Qt::Orientation::Vertical
-
-
-
- 20
- 40
-
-
-
-
- -
-
-
-
- 0
- 0
-
-
-
- Left Stick Deadzone (def:2 max:127)
-
-
-
-
-
-
-
- 0
- 0
-
-
-
- Left Deadzone
-
-
- Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter
-
-
-
- -
-
-
-
- 0
- 0
-
-
-
- 1
-
-
- 127
-
-
- Qt::Orientation::Horizontal
-
-
-
-
-
-
- -
-
-
-
- 0
- 0
-
-
-
-
- 0
- 0
-
-
-
- Left Stick
-
-
-
- 5
-
-
- 5
-
-
- 5
-
-
- 5
-
-
-
-
-
-
- 16777215
- 2121
-
-
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 0
-
-
-
-
-
-
- 0
- 0
-
-
-
-
- 152
- 16777215
-
-
-
- Up
-
-
- Qt::AlignmentFlag::AlignCenter
-
-
-
-
-
-
- unmapped
-
-
-
-
-
-
-
-
-
- -
-
-
-
-
-
- Left
-
-
- Qt::AlignmentFlag::AlignCenter
-
-
-
- 5
-
-
- 5
-
-
- 5
-
-
- 5
-
-
-
-
-
- unmapped
-
-
-
-
-
-
- -
-
-
-
- 179
- 16777215
-
-
-
- Right
-
-
- Qt::AlignmentFlag::AlignCenter
-
-
-
- 5
-
-
- 5
-
-
- 5
-
-
- 5
-
-
-
-
-
- unmapped
-
-
-
-
-
-
-
-
- -
-
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 0
-
-
-
-
-
-
- 152
- 0
-
-
-
-
- 124
- 21212
-
-
-
- Down
-
-
- Qt::AlignmentFlag::AlignCenter
-
-
-
-
-
-
- unmapped
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- -
-
-
- 0
-
-
-
-
-
-
- 0
- 0
-
-
-
-
- 12
- true
-
-
-
- Config Selection
-
-
- Qt::AlignmentFlag::AlignCenter
-
-
-
-
-
-
-
-
-
-
- 9
- false
-
-
-
-
-
-
- -1
-
-
- Common Config
-
-
-
- -
-
-
-
- 10
- true
-
-
-
- Common Config
-
-
- Qt::AlignmentFlag::AlignCenter
-
-
- true
-
-
-
-
-
- -
-
-
-
- 0
- 0
-
-
-
-
- 9
- false
-
-
-
- Use per-game configs
-
-
-
- -
-
-
-
- 9
- true
-
-
-
- Active Gamepad
-
-
-
-
-
-
-
- 9
- false
-
-
-
-
- -
-
-
-
- 9
- false
-
-
-
- Gamepad ID
-
-
- Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter
-
-
-
-
-
-
- -
-
-
-
- 9
- true
-
-
-
- Default Gamepad
-
-
-
-
-
-
- No default selected
-
-
-
- -
-
-
-
- 9
- false
-
-
-
- n/a
-
-
- Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter
-
-
-
-
-
-
- -
-
-
-
-
-
-
- 9
- true
-
-
-
- Set Active Gamepad as Default
-
-
-
- -
-
-
-
- 9
- true
-
-
-
- Remove Default Gamepad
-
-
-
-
-
-
-
-
- -
-
-
-
- 0
- 200
-
-
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 0
-
-
-
-
-
-
- 415
- 256
-
-
-
- :/images/ps4_controller.png
-
-
- true
-
-
- Qt::AlignmentFlag::AlignBottom|Qt::AlignmentFlag::AlignHCenter
-
-
-
-
-
-
- -
-
-
- 10
-
-
- QLayout::SizeConstraint::SetDefaultConstraint
-
-
-
-
-
-
-
-
-
- 0
- 0
-
-
-
- L3
-
-
- Qt::AlignmentFlag::AlignCenter
-
-
-
- 5
-
-
- 5
-
-
- 5
-
-
- 5
-
-
-
-
-
- unmapped
-
-
-
-
-
-
- -
-
-
- Options
-
-
- Qt::AlignmentFlag::AlignCenter
-
-
-
- 5
-
-
- 5
-
-
- 5
-
-
- 5
-
-
-
-
-
- unmapped
-
-
-
-
-
-
- -
-
-
-
- 0
- 0
-
-
-
- R3
-
-
- Qt::AlignmentFlag::AlignCenter
-
-
-
- 5
-
-
- 5
-
-
- 5
-
-
- 5
-
-
-
-
-
- unmapped
-
-
-
-
-
-
-
-
- -
-
-
-
-
-
- Touchpad Left
-
-
- Qt::AlignmentFlag::AlignCenter
-
-
-
-
-
-
- unmapped
-
-
-
-
-
-
- -
-
-
- Touchpad Center
-
-
- Qt::AlignmentFlag::AlignCenter
-
-
-
-
-
-
- unmapped
-
-
-
-
-
-
- -
-
-
- Touchpad Right
-
-
- Qt::AlignmentFlag::AlignCenter
-
-
-
-
-
-
- unmapped
-
-
-
-
-
-
-
-
-
-
- -
-
-
-
-
-
-
-
-
-
- false
-
-
-
- Color Adjustment
-
-
-
- 6
-
-
- 6
-
-
-
-
-
- 6
-
-
-
-
-
-
- false
-
-
-
- RED
-
-
-
- -
-
-
-
- 0
- 0
-
-
-
- 255
-
-
- Qt::Orientation::Horizontal
-
-
-
- -
-
-
-
- 20
- 16777215
-
-
-
- 000
-
-
-
-
-
- -
-
-
-
-
-
-
- false
-
-
-
- GREEN
-
-
-
- -
-
-
-
- 0
- 0
-
-
-
- 255
-
-
- Qt::Orientation::Horizontal
-
-
-
- -
-
-
-
- 20
- 16777215
-
-
-
- 000
-
-
-
-
-
- -
-
-
-
-
-
-
- false
-
-
-
- BLUE
-
-
-
- -
-
-
-
- 0
- 0
-
-
-
- 255
-
-
- 255
-
-
- Qt::Orientation::Horizontal
-
-
-
- -
-
-
-
- 20
- 16777215
-
-
-
- 255
-
-
-
-
-
-
-
-
-
-
- -
-
-
-
-
-
-
- false
-
-
-
- Override Lightbar Color
-
-
-
-
-
-
-
- false
-
-
-
- Override Color
-
-
-
- -
-
-
- QFrame::Shape::StyledPanel
-
-
- QFrame::Shadow::Raised
-
-
-
-
-
-
-
-
-
-
-
-
- -
-
-
- 5
-
-
-
-
-
-
- 0
- 0
-
-
-
- Face Buttons
-
-
-
- 5
-
-
- 5
-
-
- 5
-
-
- 5
-
-
-
-
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 0
-
-
-
-
-
-
- 0
- 0
-
-
-
-
- 152
- 0
-
-
-
-
- 0
- 16777215
-
-
-
- Triangle
-
-
- Qt::AlignmentFlag::AlignCenter
-
-
-
-
-
-
- unmapped
-
-
-
-
-
-
-
-
-
- -
-
-
-
-
-
- Square
-
-
- Qt::AlignmentFlag::AlignCenter
-
-
-
- 5
-
-
- 5
-
-
- 5
-
-
- 5
-
-
-
-
-
- unmapped
-
-
-
-
-
-
- -
-
-
- Circle
-
-
- Qt::AlignmentFlag::AlignCenter
-
-
-
- 5
-
-
- 5
-
-
- 5
-
-
- 5
-
-
-
-
-
- unmapped
-
-
-
-
-
-
-
-
- -
-
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 0
-
-
-
-
-
-
- 152
- 0
-
-
-
-
- 124
- 16777215
-
-
-
- Cross
-
-
- Qt::AlignmentFlag::AlignCenter
-
-
-
-
-
-
- unmapped
-
-
-
-
-
-
-
-
-
-
-
-
- -
-
-
- R1 and R2
-
-
-
-
-
-
-
- 0
- 0
-
-
-
- R1
-
-
- Qt::AlignmentFlag::AlignCenter
-
-
-
- 5
-
-
- 5
-
-
- 5
-
-
- 5
-
-
-
-
-
- unmapped
-
-
-
-
-
-
- -
-
-
- R2
-
-
- Qt::AlignmentFlag::AlignCenter
-
-
-
- 5
-
-
- 5
-
-
- 5
-
-
- 5
-
-
-
-
-
- unmapped
-
-
-
-
-
-
-
-
-
- -
-
-
- Qt::Orientation::Vertical
-
-
- QSizePolicy::Policy::Expanding
-
-
-
- 20
- 40
-
-
-
-
- -
-
-
-
- 0
- 0
-
-
-
- Right Stick Deadzone (def:2, max:127)
-
-
-
-
-
-
-
- 0
- 0
-
-
-
- Right Deadzone
-
-
- Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter
-
-
-
- -
-
-
-
- 0
- 0
-
-
-
- 1
-
-
- 127
-
-
- Qt::Orientation::Horizontal
-
-
-
-
-
-
- -
-
-
-
- 0
- 0
-
-
-
-
- 0
- 0
-
-
-
- Right Stick
-
-
-
- 5
-
-
- 5
-
-
- 5
-
-
- 5
-
-
-
-
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 0
-
-
-
-
-
-
- 0
- 0
-
-
-
-
- 0
- 0
-
-
-
-
- 152
- 1231321
-
-
-
- Up
-
-
- Qt::AlignmentFlag::AlignCenter
-
-
-
-
-
-
- unmapped
-
-
-
-
-
-
-
-
-
- -
-
-
-
-
-
- Left
-
-
- Qt::AlignmentFlag::AlignCenter
-
-
-
- 5
-
-
- 5
-
-
- 5
-
-
- 5
-
-
-
-
-
- unmapped
-
-
-
-
-
-
- -
-
-
- Right
-
-
- Qt::AlignmentFlag::AlignCenter
-
-
-
- 5
-
-
- 5
-
-
- 5
-
-
- 5
-
-
-
-
-
- unmapped
-
-
-
-
-
-
-
-
- -
-
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 0
-
-
-
-
-
-
- 152
- 0
-
-
-
-
- 124
- 2121
-
-
-
- Down
-
-
- Qt::AlignmentFlag::AlignCenter
-
-
-
-
-
-
- unmapped
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- -
-
-
- QDialogButtonBox::StandardButton::Apply|QDialogButtonBox::StandardButton::Cancel|QDialogButtonBox::StandardButton::RestoreDefaults|QDialogButtonBox::StandardButton::Save
-
-
- false
-
-
-
-
-
-
-
-
-
-
diff --git a/src/qt_gui/elf_viewer.cpp b/src/qt_gui/elf_viewer.cpp
deleted file mode 100644
index 8d472755b..000000000
--- a/src/qt_gui/elf_viewer.cpp
+++ /dev/null
@@ -1,98 +0,0 @@
-// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-#include "elf_viewer.h"
-
-ElfViewer::ElfViewer(std::shared_ptr gui_settings, QWidget* parent)
- : QTableWidget(parent), m_gui_settings(std::move(gui_settings)) {
-
- list = gui_settings::Var2List(m_gui_settings->GetValue(gui::gen_elfDirs));
- for (const auto& str : list) {
- dir_list.append(str);
- }
-
- CheckElfFolders();
-
- this->setShowGrid(false);
- this->setEditTriggers(QAbstractItemView::NoEditTriggers);
- this->setSelectionBehavior(QAbstractItemView::SelectRows);
- this->setSelectionMode(QAbstractItemView::SingleSelection);
- this->setVerticalScrollMode(QAbstractItemView::ScrollPerPixel);
- this->setHorizontalScrollMode(QAbstractItemView::ScrollPerPixel);
- this->verticalScrollBar()->installEventFilter(this);
- this->verticalScrollBar()->setSingleStep(20);
- this->horizontalScrollBar()->setSingleStep(20);
- this->verticalHeader()->setVisible(false);
- this->horizontalHeader()->setContextMenuPolicy(Qt::CustomContextMenu);
- this->horizontalHeader()->setHighlightSections(false);
- this->horizontalHeader()->setSortIndicatorShown(true);
- this->horizontalHeader()->setStretchLastSection(true);
- this->setContextMenuPolicy(Qt::CustomContextMenu);
- this->setColumnCount(2);
- this->setColumnWidth(0, 250);
- this->setColumnWidth(1, 400);
- this->verticalHeader()->setSectionResizeMode(QHeaderView::ResizeToContents);
- this->setStyleSheet("QTableWidget { background-color: #D3D3D3; }");
- OpenElfFiles();
- QStringList headers;
- headers << "Name"
- << "Path";
- this->setHorizontalHeaderLabels(headers);
- this->horizontalHeader()->setSortIndicatorShown(true);
- this->horizontalHeader()->setSectionResizeMode(0, QHeaderView::ResizeToContents);
-}
-
-void ElfViewer::OpenElfFolder() {
- QString folderPath =
- QFileDialog::getExistingDirectory(this, tr("Open Folder"), QDir::homePath());
- if (!dir_list.contains(folderPath)) {
- dir_list.append(folderPath);
- QDir directory(folderPath);
- QFileInfoList fileInfoList = directory.entryInfoList(QDir::Files);
- for (const QFileInfo& fileInfo : fileInfoList) {
- QString file_ext = fileInfo.suffix();
- if (fileInfo.isFile() && (file_ext == "bin" || file_ext == "elf")) {
- m_elf_list.append(fileInfo.absoluteFilePath());
- }
- }
- std::ranges::sort(m_elf_list);
- OpenElfFiles();
- list.clear();
- for (auto dir : dir_list) {
- list.push_back(dir);
- }
- m_gui_settings->SetValue(gui::gen_elfDirs, gui_settings::List2Var(list));
- } else {
- // qDebug() << "Folder selection canceled.";
- }
-}
-
-void ElfViewer::CheckElfFolders() {
- m_elf_list.clear();
- for (const QString& dir : dir_list) {
- QDir directory(dir);
- QFileInfoList fileInfoList = directory.entryInfoList(QDir::Files);
- for (const QFileInfo& fileInfo : fileInfoList) {
- QString file_ext = fileInfo.suffix();
- if (fileInfo.isFile() && (file_ext == "bin" || file_ext == "elf")) {
- m_elf_list.append(fileInfo.absoluteFilePath());
- }
- }
- }
- std::sort(m_elf_list.begin(), m_elf_list.end());
-}
-
-void ElfViewer::OpenElfFiles() {
- this->clearContents();
- this->setRowCount(m_elf_list.size());
- for (int i = 0; auto elf : m_elf_list) {
- QTableWidgetItem* item = new QTableWidgetItem();
- QFileInfo fileInfo(m_elf_list[i]);
- QString fileName = fileInfo.baseName();
- SetTableItem(this, i, 0, fileName);
- item = new QTableWidgetItem();
- SetTableItem(this, i, 1, m_elf_list[i]);
- i++;
- }
- this->resizeColumnsToContents();
-}
\ No newline at end of file
diff --git a/src/qt_gui/elf_viewer.h b/src/qt_gui/elf_viewer.h
deleted file mode 100644
index 6256abf31..000000000
--- a/src/qt_gui/elf_viewer.h
+++ /dev/null
@@ -1,53 +0,0 @@
-// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-#pragma once
-
-#include
-
-#include "core/loader/elf.h"
-#include "game_list_frame.h"
-
-class ElfViewer : public QTableWidget {
- Q_OBJECT
-public:
- explicit ElfViewer(std::shared_ptr gui_settings, QWidget* parent = nullptr);
- QStringList m_elf_list;
-
-private:
- void CheckElfFolders();
- void OpenElfFiles();
-
- Core::Loader::Elf m_elf_file;
- QStringList dir_list;
- QStringList elf_headers_list;
- QList list;
- std::shared_ptr m_gui_settings;
-
- void SetTableItem(QTableWidget* game_list, int row, int column, QString itemStr) {
- QTableWidgetItem* item = new QTableWidgetItem();
- QWidget* widget = new QWidget(this);
- QVBoxLayout* layout = new QVBoxLayout(widget);
- QLabel* label = new QLabel(itemStr, widget);
-
- label->setStyleSheet("color: white; font-size: 15px; font-weight: bold;");
-
- // Create shadow effect
- QGraphicsDropShadowEffect* shadowEffect = new QGraphicsDropShadowEffect();
- shadowEffect->setBlurRadius(5); // Set the blur radius of the shadow
- shadowEffect->setColor(QColor(0, 0, 0, 160)); // Set the color and opacity of the shadow
- shadowEffect->setOffset(2, 2); // Set the offset of the shadow
-
- label->setGraphicsEffect(shadowEffect); // Apply shadow effect to the QLabel
-
- layout->addWidget(label);
- if (column != 8 && column != 1)
- layout->setAlignment(Qt::AlignCenter);
- widget->setLayout(layout);
- game_list->setItem(row, column, item);
- game_list->setCellWidget(row, column, widget);
- }
-
-public slots:
- void OpenElfFolder();
-};
diff --git a/src/qt_gui/game_grid_frame.cpp b/src/qt_gui/game_grid_frame.cpp
deleted file mode 100644
index 7608e1150..000000000
--- a/src/qt_gui/game_grid_frame.cpp
+++ /dev/null
@@ -1,297 +0,0 @@
-// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-#include "common/path_util.h"
-#include "game_grid_frame.h"
-#include "qt_gui/compatibility_info.h"
-
-GameGridFrame::GameGridFrame(std::shared_ptr gui_settings,
- std::shared_ptr game_info_get,
- std::shared_ptr compat_info_get,
- QWidget* parent)
- : QTableWidget(parent), m_gui_settings(std::move(gui_settings)), m_game_info(game_info_get),
- m_compat_info(compat_info_get) {
- icon_size = m_gui_settings->GetValue(gui::gg_icon_size).toInt();
- windowWidth = parent->width();
- this->setShowGrid(false);
- this->setEditTriggers(QAbstractItemView::NoEditTriggers);
- this->setSelectionBehavior(QAbstractItemView::SelectItems);
- this->setSelectionMode(QAbstractItemView::SingleSelection);
- this->setVerticalScrollMode(QAbstractItemView::ScrollPerPixel);
- this->setHorizontalScrollMode(QAbstractItemView::ScrollPerPixel);
- this->verticalScrollBar()->installEventFilter(this);
- this->verticalScrollBar()->setSingleStep(20);
- this->horizontalScrollBar()->setSingleStep(20);
- this->horizontalHeader()->setVisible(false);
- this->verticalHeader()->setVisible(false);
- this->setContextMenuPolicy(Qt::CustomContextMenu);
- PopulateGameGrid(m_game_info->m_games, false);
-
- connect(this, &QTableWidget::currentCellChanged, this, &GameGridFrame::onCurrentCellChanged);
-
- connect(this->verticalScrollBar(), &QScrollBar::valueChanged, this,
- &GameGridFrame::RefreshGridBackgroundImage);
- connect(this->horizontalScrollBar(), &QScrollBar::valueChanged, this,
- &GameGridFrame::RefreshGridBackgroundImage);
- connect(this, &QTableWidget::customContextMenuRequested, this, [=, this](const QPoint& pos) {
- m_gui_context_menus.RequestGameMenu(pos, m_game_info->m_games, m_compat_info,
- m_gui_settings, this, false);
- PopulateGameGrid(m_game_info->m_games, false);
- });
-}
-
-void GameGridFrame::onCurrentCellChanged(int currentRow, int currentColumn, int previousRow,
- int previousColumn) {
- // Early exit for invalid indices
- if (currentRow < 0 || currentColumn < 0) {
- cellClicked = false;
- validCellSelected = false;
- BackgroundMusicPlayer::getInstance().stopMusic();
- return;
- }
-
- crtRow = currentRow;
- crtColumn = currentColumn;
- columnCnt = this->columnCount();
-
- // Prevent integer overflow
- if (columnCnt <= 0 || crtRow > (std::numeric_limits::max() / columnCnt)) {
- cellClicked = false;
- validCellSelected = false;
- BackgroundMusicPlayer::getInstance().stopMusic();
- return;
- }
-
- auto itemID = (crtRow * columnCnt) + currentColumn;
- if (itemID < 0 || itemID > m_game_info->m_games.count() - 1) {
- cellClicked = false;
- validCellSelected = false;
- BackgroundMusicPlayer::getInstance().stopMusic();
- return;
- }
-
- cellClicked = true;
- validCellSelected = true;
- SetGridBackgroundImage(crtRow, crtColumn);
- auto snd0Path = QString::fromStdString(m_game_info->m_games[itemID].snd0_path.string());
- PlayBackgroundMusic(snd0Path);
-}
-
-void GameGridFrame::PlayBackgroundMusic(QString path) {
- if (path.isEmpty() || !m_gui_settings->GetValue(gui::gl_playBackgroundMusic).toBool()) {
- BackgroundMusicPlayer::getInstance().stopMusic();
- return;
- }
- BackgroundMusicPlayer::getInstance().playMusic(path);
-}
-
-void GameGridFrame::PopulateGameGrid(QVector m_games_search, bool fromSearch) {
- this->crtRow = -1;
- this->crtColumn = -1;
- QVector m_games_;
- this->clearContents();
- if (fromSearch) {
- SortByFavorite(&m_games_search);
- m_games_ = m_games_search;
- } else {
- SortByFavorite(&(m_game_info->m_games));
- m_games_ = m_game_info->m_games;
- }
- m_games_shared = std::make_shared>(m_games_);
- icon_size =
- m_gui_settings->GetValue(gui::gg_icon_size).toInt(); // update icon size for resize event.
-
- int gamesPerRow = windowWidth / (icon_size + 20); // 2 x cell widget border size.
- int row = 0;
- int gameCounter = 0;
- int rowCount = m_games_.size() / gamesPerRow;
- if (m_games_.size() % gamesPerRow != 0) {
- rowCount += 1; // Add an extra row for the remainder
- }
-
- int column = 0;
- this->setColumnCount(gamesPerRow);
- this->setRowCount(rowCount);
- for (int i = 0; i < m_games_.size(); i++) {
- QWidget* widget = new QWidget();
- QVBoxLayout* layout = new QVBoxLayout();
-
- QWidget* image_container = new QWidget();
- image_container->setFixedSize(icon_size, icon_size);
-
- QLabel* image_label = new QLabel(image_container);
- QImage icon = m_games_[gameCounter].icon.scaled(
- QSize(icon_size, icon_size), Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
- image_label->setFixedSize(icon.width(), icon.height());
- image_label->setPixmap(QPixmap::fromImage(icon));
- image_label->move(0, 0);
- SetFavoriteIcon(image_container, m_games_, gameCounter);
- SetGameConfigIcon(image_container, m_games_, gameCounter);
-
- QLabel* name_label = new QLabel(QString::fromStdString(m_games_[gameCounter].serial));
- name_label->setAlignment(Qt::AlignHCenter);
- layout->addWidget(image_container);
- layout->addWidget(name_label);
-
- // Resizing of font-size.
- float fontSize = (m_gui_settings->GetValue(gui::gg_icon_size).toInt() / 5.5f);
- QString styleSheet =
- QString("color: white; font-weight: bold; font-size: %1px;").arg(fontSize);
- name_label->setStyleSheet(styleSheet);
-
- QGraphicsDropShadowEffect* shadowEffect = new QGraphicsDropShadowEffect();
- shadowEffect->setBlurRadius(5); // Set the blur radius of the shadow
- shadowEffect->setColor(QColor(0, 0, 0, 160)); // Set the color and opacity of the shadow
- shadowEffect->setOffset(2, 2); // Set the offset of the shadow
-
- name_label->setGraphicsEffect(shadowEffect);
- widget->setLayout(layout);
- QString tooltipText = QString::fromStdString(m_games_[gameCounter].name + " (" +
- m_games_[gameCounter].version + ", " +
- m_games_[gameCounter].region + ")");
- widget->setToolTip(tooltipText);
- QString tooltipStyle = QString("QToolTip {"
- "background-color: #ffffff;"
- "color: #000000;"
- "border: 1px solid #000000;"
- "padding: 2px;"
- "font-size: 12px; }");
- widget->setStyleSheet(tooltipStyle);
- this->setCellWidget(row, column, widget);
-
- column++;
- if (column == gamesPerRow) {
- column = 0;
- row++;
- }
-
- gameCounter++;
- if (gameCounter >= m_games_.size()) {
- break;
- }
- }
- m_games_.clear();
- this->resizeRowsToContents();
- this->resizeColumnsToContents();
-}
-
-void GameGridFrame::SetGridBackgroundImage(int row, int column) {
- int itemID = (row * this->columnCount()) + column;
- QWidget* item = this->cellWidget(row, column);
- if (!item) {
- // handle case where no item was clicked
- return;
- }
-
- // If background images are hidden, clear the background image
- if (!m_gui_settings->GetValue(gui::gl_showBackgroundImage).toBool()) {
- backgroundImage = QImage();
- m_last_opacity = -1; // Reset opacity tracking when disabled
- m_current_game_path.clear(); // Reset current game path
- RefreshGridBackgroundImage();
- return;
- }
-
- const auto& game = (*m_games_shared)[itemID];
- const int opacity = m_gui_settings->GetValue(gui::gl_backgroundImageOpacity).toInt();
-
- // Recompute if opacity changed or we switched to a different game
- if (opacity != m_last_opacity || game.pic_path != m_current_game_path) {
- QImage original_image(QString::fromStdString(game.pic_path.string()));
- if (!original_image.isNull()) {
- backgroundImage = m_game_list_utils.ChangeImageOpacity(
- original_image, original_image.rect(), opacity / 100.0f);
- m_last_opacity = opacity;
- m_current_game_path = game.pic_path;
- }
- }
-
- RefreshGridBackgroundImage();
-}
-
-void GameGridFrame::RefreshGridBackgroundImage() {
- QPalette palette;
- if (!backgroundImage.isNull() &&
- m_gui_settings->GetValue(gui::gl_showBackgroundImage).toBool()) {
- QSize widgetSize = size();
- QPixmap scaledPixmap =
- QPixmap::fromImage(backgroundImage)
- .scaled(widgetSize, Qt::KeepAspectRatioByExpanding, Qt::SmoothTransformation);
- int x = (widgetSize.width() - scaledPixmap.width()) / 2;
- int y = (widgetSize.height() - scaledPixmap.height()) / 2;
- QPixmap finalPixmap(widgetSize);
- finalPixmap.fill(Qt::transparent);
- QPainter painter(&finalPixmap);
- painter.drawPixmap(x, y, scaledPixmap);
- palette.setBrush(QPalette::Base, QBrush(finalPixmap));
- }
- QColor transparentColor = QColor(135, 206, 235, 40);
- palette.setColor(QPalette::Highlight, transparentColor);
- this->setPalette(palette);
-}
-
-void GameGridFrame::resizeEvent(QResizeEvent* event) {
- QTableWidget::resizeEvent(event);
- RefreshGridBackgroundImage();
-}
-
-bool GameGridFrame::IsValidCellSelected() {
- return validCellSelected;
-}
-
-void GameGridFrame::SetFavoriteIcon(QWidget* parentWidget, QVector m_games_,
- int gameCounter) {
- QString serialStr = QString::fromStdString(m_games_[gameCounter].serial);
- QList list = gui_settings::Var2List(m_gui_settings->GetValue(gui::favorites_list));
- bool isFavorite = list.contains(serialStr);
-
- QLabel* label = new QLabel(parentWidget);
- label->setPixmap(QPixmap(":images/favorite_icon.png")
- .scaled(icon_size / 3.8, icon_size / 3.8, Qt::KeepAspectRatio,
- Qt::SmoothTransformation));
- label->move(icon_size - icon_size / 4, 2);
- label->raise();
- label->setVisible(isFavorite);
- label->setObjectName("favoriteIcon");
-}
-
-void GameGridFrame::SetGameConfigIcon(QWidget* parentWidget, QVector m_games_,
- int gameCounter) {
- std::string serialStr = m_games_[gameCounter].serial;
-
- bool hasGameConfig = std::filesystem::exists(
- Common::FS::GetUserPath(Common::FS::PathType::CustomConfigs) / (serialStr + ".toml"));
-
- QLabel* label = new QLabel(parentWidget);
- label->setPixmap(QPixmap(":images/game_settings.png")
- .scaled(icon_size / 3.8, icon_size / 3.8, Qt::KeepAspectRatio,
- Qt::SmoothTransformation));
- label->move(2, 2);
- label->raise();
- label->setVisible(hasGameConfig);
- label->setObjectName("gameConfigIcon");
-}
-
-void GameGridFrame::SortByFavorite(QVector* game_list) {
- std::sort(game_list->begin(), game_list->end(), [this](const GameInfo& a, const GameInfo& b) {
- return this->CompareWithFavorite(a, b);
- });
-}
-
-bool GameGridFrame::CompareWithFavorite(GameInfo a, GameInfo b) {
- std::string serial_a = a.serial;
- std::string serial_b = b.serial;
- QString serialStr_a = QString::fromStdString(a.serial);
- QString serialStr_b = QString::fromStdString(b.serial);
- QList list = gui_settings::Var2List(m_gui_settings->GetValue(gui::favorites_list));
- bool isFavorite_a = list.contains(serialStr_a);
- bool isFavorite_b = list.contains(serialStr_b);
- if (isFavorite_a != isFavorite_b) {
- return isFavorite_a;
- } else {
- std::string name_a = a.name, name_b = b.name;
- std::transform(name_a.begin(), name_a.end(), name_a.begin(), ::tolower);
- std::transform(name_b.begin(), name_b.end(), name_b.begin(), ::tolower);
- return name_a < name_b;
- }
-}
diff --git a/src/qt_gui/game_grid_frame.h b/src/qt_gui/game_grid_frame.h
deleted file mode 100644
index 4da5b9147..000000000
--- a/src/qt_gui/game_grid_frame.h
+++ /dev/null
@@ -1,61 +0,0 @@
-// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-#pragma once
-
-#include
-#include
-
-#include "background_music_player.h"
-#include "common/config.h"
-#include "game_info.h"
-#include "game_list_utils.h"
-#include "gui_context_menus.h"
-#include "gui_settings.h"
-#include "qt_gui/compatibility_info.h"
-
-class GameGridFrame : public QTableWidget {
- Q_OBJECT
-
-Q_SIGNALS:
- void GameGridFrameClosed();
-
-public Q_SLOTS:
- void SetGridBackgroundImage(int row, int column);
- void RefreshGridBackgroundImage();
- void resizeEvent(QResizeEvent* event);
- void PlayBackgroundMusic(QString path);
- void onCurrentCellChanged(int currentRow, int currentColumn, int previousRow,
- int previousColumn);
-
-private:
- QImage backgroundImage;
- GameListUtils m_game_list_utils;
- GuiContextMenus m_gui_context_menus;
- std::shared_ptr m_game_info;
- std::shared_ptr m_compat_info;
- std::shared_ptr> m_games_shared;
- bool validCellSelected = false;
- int m_last_opacity = -1; // Track last opacity to avoid unnecessary recomputation
- std::filesystem::path m_current_game_path; // Track current game path to detect changes
- std::shared_ptr m_gui_settings;
- void SetFavoriteIcon(QWidget* parentWidget, QVector m_games_, int gameCounter);
- void SetGameConfigIcon(QWidget* parentWidget, QVector m_games_, int gameCounter);
- bool CompareWithFavorite(GameInfo a, GameInfo b);
-
-public:
- explicit GameGridFrame(std::shared_ptr gui_settings,
- std::shared_ptr game_info_get,
- std::shared_ptr compat_info_get,
- QWidget* parent = nullptr);
- void PopulateGameGrid(QVector m_games, bool fromSearch);
- bool IsValidCellSelected();
- void SortByFavorite(QVector* game_list);
-
- bool cellClicked = false;
- int icon_size;
- int windowWidth;
- int crtRow;
- int crtColumn;
- int columnCnt;
-};
diff --git a/src/qt_gui/game_info.cpp b/src/qt_gui/game_info.cpp
deleted file mode 100644
index 19b6adc1e..000000000
--- a/src/qt_gui/game_info.cpp
+++ /dev/null
@@ -1,73 +0,0 @@
-// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-#include
-
-#include "common/path_util.h"
-#include "compatibility_info.h"
-#include "game_info.h"
-
-// Maximum depth to search for games in subdirectories
-const int max_recursion_depth = 5;
-
-void ScanDirectoryRecursively(const QString& dir, QStringList& filePaths, int current_depth = 0) {
- // Stop recursion if we've reached the maximum depth
- if (current_depth >= max_recursion_depth) {
- return;
- }
-
- QDir directory(dir);
- QFileInfoList entries = directory.entryInfoList(QDir::Dirs | QDir::NoDotAndDotDot);
-
- for (const auto& entry : entries) {
- if (entry.fileName().endsWith("-UPDATE") || entry.fileName().endsWith("-patch")) {
- continue;
- }
-
- // Check if this directory contains a PS4 game (has sce_sys/param.sfo)
- if (QFile::exists(entry.filePath() + "/sce_sys/param.sfo")) {
- filePaths.append(entry.absoluteFilePath());
- } else {
- // If not a game directory, recursively scan it with increased depth
- ScanDirectoryRecursively(entry.absoluteFilePath(), filePaths, current_depth + 1);
- }
- }
-}
-
-GameInfoClass::GameInfoClass() = default;
-GameInfoClass::~GameInfoClass() = default;
-
-void GameInfoClass::GetGameInfo(QWidget* parent) {
- QStringList filePaths;
- for (const auto& installLoc : Config::getGameInstallDirs()) {
- QString installDir;
- Common::FS::PathToQString(installDir, installLoc);
- ScanDirectoryRecursively(installDir, filePaths, 0);
- }
-
- m_games = QtConcurrent::mapped(filePaths, [&](const QString& path) {
- return readGameInfo(Common::FS::PathFromQString(path));
- }).results();
-
- // used to retrieve values after performing a search
- m_games_backup = m_games;
-
- // Progress bar, please be patient :)
- QProgressDialog dialog(tr("Loading game list, please wait :3"), tr("Cancel"), 0, 0, parent);
- dialog.setWindowTitle(tr("Loading..."));
-
- QFutureWatcher futureWatcher;
- GameListUtils game_util;
- bool finished = false;
- futureWatcher.setFuture(QtConcurrent::map(m_games, game_util.GetFolderSize));
- connect(&futureWatcher, &QFutureWatcher::finished, [&]() {
- dialog.reset();
- std::sort(m_games.begin(), m_games.end(), CompareStrings);
- });
- connect(&dialog, &QProgressDialog::canceled, &futureWatcher, &QFutureWatcher::cancel);
- dialog.setRange(0, m_games.size());
- connect(&futureWatcher, &QFutureWatcher::progressValueChanged, &dialog,
- &QProgressDialog::setValue);
-
- dialog.exec();
-}
diff --git a/src/qt_gui/game_info.h b/src/qt_gui/game_info.h
deleted file mode 100644
index df8f4937c..000000000
--- a/src/qt_gui/game_info.h
+++ /dev/null
@@ -1,100 +0,0 @@
-// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-#pragma once
-
-#include
-#include
-
-#include "common/config.h"
-#include "core/file_format/psf.h"
-#include "game_list_utils.h"
-
-class GameInfoClass : public QObject {
- Q_OBJECT
-public:
- GameInfoClass();
- ~GameInfoClass();
- void GetGameInfo(QWidget* parent = nullptr);
- QVector m_games;
- QVector m_games_backup;
-
- static void SceUpdateChecker(const std::string sceItem, std::filesystem::path& gameItem,
- std::filesystem::path& update_folder,
- std::filesystem::path& patch_folder,
- std::filesystem::path& game_folder) {
- if (std::filesystem::exists(update_folder / "sce_sys" / sceItem)) {
- gameItem = update_folder / "sce_sys" / sceItem;
- } else if (std::filesystem::exists(patch_folder / "sce_sys" / sceItem)) {
- gameItem = patch_folder / "sce_sys" / sceItem;
- } else {
- gameItem = game_folder / "sce_sys" / sceItem;
- }
- }
-
- static bool CompareStrings(GameInfo& a, GameInfo& b) {
- std::string name_a = a.name, name_b = b.name;
- std::transform(name_a.begin(), name_a.end(), name_a.begin(), ::tolower);
- std::transform(name_b.begin(), name_b.end(), name_b.begin(), ::tolower);
- return name_a < name_b;
- }
-
- static GameInfo readGameInfo(const std::filesystem::path& filePath) {
- GameInfo game;
- game.path = filePath;
- std::filesystem::path param_sfo_path;
- std::filesystem::path game_update_path = filePath;
- game_update_path += "-UPDATE";
- std::filesystem::path game_patch_path = filePath;
- game_patch_path += "-patch";
- SceUpdateChecker("param.sfo", param_sfo_path, game_update_path, game_patch_path, game.path);
-
- PSF psf;
- if (psf.Open(param_sfo_path)) {
- SceUpdateChecker("icon0.png", game.icon_path, game_update_path, game_patch_path,
- game.path);
- QString iconpath;
- Common::FS::PathToQString(iconpath, game.icon_path);
- game.icon = QImage(iconpath);
- SceUpdateChecker("pic1.png", game.pic_path, game_update_path, game_patch_path,
- game.path);
- SceUpdateChecker("snd0.at9", game.snd0_path, game_update_path, game_patch_path,
- game.path);
-
- if (const auto title = psf.GetString("TITLE"); title.has_value()) {
- game.name = *title;
- }
- if (const auto title_id = psf.GetString("TITLE_ID"); title_id.has_value()) {
- game.serial = *title_id;
- }
- if (const auto content_id = psf.GetString("CONTENT_ID");
- content_id.has_value() && !content_id->empty()) {
- game.region = GameListUtils::GetRegion(content_id->at(0)).toStdString();
- }
- if (const auto fw_int_opt = psf.GetInteger("SYSTEM_VER"); fw_int_opt.has_value()) {
- auto fw_int = *fw_int_opt;
- if (fw_int == 0) {
- game.fw = "0.00";
- } else {
- QString fw = QString::number(fw_int, 16);
- QString fw_ = fw.length() > 7
- ? QString::number(fw_int, 16).left(3).insert(2, '.')
- : fw.left(3).insert(1, '.');
- game.fw = fw_.toStdString();
- }
- }
- if (auto app_ver = psf.GetString("APP_VER"); app_ver.has_value()) {
- game.version = *app_ver;
- }
- if (const auto play_time = psf.GetString("PLAY_TIME"); play_time.has_value()) {
- game.play_time = *play_time;
- }
- if (const auto save_dir = psf.GetString("INSTALL_DIR_SAVEDATA"); save_dir.has_value()) {
- game.save_dir = *save_dir;
- } else {
- game.save_dir = game.serial;
- }
- }
- return game;
- }
-};
diff --git a/src/qt_gui/game_install_dialog.cpp b/src/qt_gui/game_install_dialog.cpp
deleted file mode 100644
index e53c58315..000000000
--- a/src/qt_gui/game_install_dialog.cpp
+++ /dev/null
@@ -1,134 +0,0 @@
-// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-
-#include "game_install_dialog.h"
-
-GameInstallDialog::GameInstallDialog() : m_gamesDirectory(nullptr) {
- auto layout = new QVBoxLayout(this);
-
- layout->addWidget(SetupGamesDirectory());
- layout->addWidget(SetupAddonsDirectory());
- layout->addStretch();
- layout->addWidget(SetupDialogActions());
-
- setWindowTitle(tr("shadPS4 - Choose directory"));
- setWindowIcon(QIcon(":images/shadps4.ico"));
-}
-
-GameInstallDialog::~GameInstallDialog() {}
-
-void GameInstallDialog::BrowseGamesDirectory() {
- auto path = QFileDialog::getExistingDirectory(this, tr("Directory to install games"));
-
- if (!path.isEmpty()) {
- m_gamesDirectory->setText(QDir::toNativeSeparators(path));
- }
-}
-
-void GameInstallDialog::BrowseAddonsDirectory() {
- auto path = QFileDialog::getExistingDirectory(this, tr("Directory to install DLC"));
-
- if (!path.isEmpty()) {
- m_addonsDirectory->setText(QDir::toNativeSeparators(path));
- }
-}
-
-QWidget* GameInstallDialog::SetupGamesDirectory() {
- auto group = new QGroupBox(tr("Directory to install games"));
- auto layout = new QHBoxLayout(group);
-
- // Input.
- m_gamesDirectory = new QLineEdit();
- QString install_dir;
- std::filesystem::path install_path =
- Config::getGameInstallDirs().empty() ? "" : Config::getGameInstallDirs().front();
- Common::FS::PathToQString(install_dir, install_path);
- m_gamesDirectory->setText(install_dir);
- m_gamesDirectory->setMinimumWidth(400);
-
- layout->addWidget(m_gamesDirectory);
-
- // Browse button.
- auto browse = new QPushButton(tr("Browse"));
-
- connect(browse, &QPushButton::clicked, this, &GameInstallDialog::BrowseGamesDirectory);
-
- layout->addWidget(browse);
-
- return group;
-}
-
-QWidget* GameInstallDialog::SetupAddonsDirectory() {
- auto group = new QGroupBox(tr("Directory to install DLC"));
- auto layout = new QHBoxLayout(group);
-
- // Input.
- m_addonsDirectory = new QLineEdit();
- QString install_dir;
- Common::FS::PathToQString(install_dir, Config::getAddonInstallDir());
- m_addonsDirectory->setText(install_dir);
- m_addonsDirectory->setMinimumWidth(400);
-
- layout->addWidget(m_addonsDirectory);
-
- // Browse button.
- auto browse = new QPushButton(tr("Browse"));
-
- connect(browse, &QPushButton::clicked, this, &GameInstallDialog::BrowseAddonsDirectory);
-
- layout->addWidget(browse);
-
- return group;
-}
-
-QWidget* GameInstallDialog::SetupDialogActions() {
- auto actions = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
-
- connect(actions, &QDialogButtonBox::accepted, this, &GameInstallDialog::Save);
- connect(actions, &QDialogButtonBox::rejected, this, &GameInstallDialog::reject);
-
- return actions;
-}
-
-void GameInstallDialog::Save() {
- // Check games directory.
- auto gamesDirectory = m_gamesDirectory->text();
- auto addonsDirectory = m_addonsDirectory->text();
-
- if (gamesDirectory.isEmpty() || !QDir(gamesDirectory).exists() ||
- !QDir::isAbsolutePath(gamesDirectory)) {
- QMessageBox::critical(this, tr("Error"),
- "The value for location to install games is not valid.");
- return;
- }
-
- if (addonsDirectory.isEmpty() || !QDir::isAbsolutePath(addonsDirectory)) {
- QMessageBox::critical(this, tr("Error"),
- "The value for location to install DLC is not valid.");
- return;
- }
- QDir addonsDir(addonsDirectory);
- if (!addonsDir.exists()) {
- if (!addonsDir.mkpath(".")) {
- QMessageBox::critical(this, tr("Error"),
- "The DLC install location could not be created.");
- return;
- }
- }
- Config::addGameInstallDir(Common::FS::PathFromQString(gamesDirectory));
- Config::setAddonInstallDir(Common::FS::PathFromQString(addonsDirectory));
- const auto config_dir = Common::FS::GetUserPath(Common::FS::PathType::UserDir);
- Config::save(config_dir / "config.toml");
- accept();
-}
diff --git a/src/qt_gui/game_install_dialog.h b/src/qt_gui/game_install_dialog.h
deleted file mode 100644
index 938f0e1f3..000000000
--- a/src/qt_gui/game_install_dialog.h
+++ /dev/null
@@ -1,32 +0,0 @@
-// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-#pragma once
-
-#include
-
-#include "common/config.h"
-#include "common/path_util.h"
-
-class QLineEdit;
-
-class GameInstallDialog final : public QDialog {
- Q_OBJECT
-public:
- GameInstallDialog();
- ~GameInstallDialog();
-
-private slots:
- void BrowseGamesDirectory();
- void BrowseAddonsDirectory();
-
-private:
- QWidget* SetupGamesDirectory();
- QWidget* SetupAddonsDirectory();
- QWidget* SetupDialogActions();
- void Save();
-
-private:
- QLineEdit* m_gamesDirectory;
- QLineEdit* m_addonsDirectory;
-};
\ No newline at end of file
diff --git a/src/qt_gui/game_list_frame.cpp b/src/qt_gui/game_list_frame.cpp
deleted file mode 100644
index bdbb3e81f..000000000
--- a/src/qt_gui/game_list_frame.cpp
+++ /dev/null
@@ -1,514 +0,0 @@
-// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-#include
-#include "common/config.h"
-#include "common/logging/log.h"
-#include "common/path_util.h"
-#include "common/string_util.h"
-#include "game_list_frame.h"
-#include "game_list_utils.h"
-
-GameListFrame::GameListFrame(std::shared_ptr gui_settings,
- std::shared_ptr game_info_get,
- std::shared_ptr compat_info_get,
- QWidget* parent)
- : QTableWidget(parent), m_gui_settings(std::move(gui_settings)), m_game_info(game_info_get),
- m_compat_info(compat_info_get) {
- icon_size = m_gui_settings->GetValue(gui::gl_icon_size).toInt();
- last_favorite = "";
- this->setShowGrid(false);
- this->setEditTriggers(QAbstractItemView::NoEditTriggers);
- this->setSelectionBehavior(QAbstractItemView::SelectRows);
- this->setSelectionMode(QAbstractItemView::SingleSelection);
- this->setVerticalScrollMode(QAbstractItemView::ScrollPerPixel);
- this->setHorizontalScrollMode(QAbstractItemView::ScrollPerPixel);
- this->verticalScrollBar()->installEventFilter(this);
- this->verticalScrollBar()->setSingleStep(20);
- this->horizontalScrollBar()->setSingleStep(20);
- this->verticalHeader()->setSectionResizeMode(QHeaderView::ResizeToContents);
- this->verticalHeader()->setVisible(false);
- this->horizontalHeader()->setContextMenuPolicy(Qt::CustomContextMenu);
- this->horizontalHeader()->setHighlightSections(false);
- this->horizontalHeader()->setSortIndicatorShown(true);
- this->setContextMenuPolicy(Qt::CustomContextMenu);
- this->setColumnCount(11);
- this->setColumnWidth(1, 300); // Name
- this->setColumnWidth(2, 140); // Compatibility
- this->setColumnWidth(3, 120); // Serial
- this->setColumnWidth(4, 90); // Region
- this->setColumnWidth(5, 90); // Firmware
- this->setColumnWidth(6, 90); // Size
- this->setColumnWidth(7, 90); // Version
- this->setColumnWidth(8, 120); // Play Time
- this->setColumnWidth(10, 90); // Favorite
- QStringList headers;
- headers << tr("Icon") << tr("Name") << tr("Compatibility") << tr("Serial") << tr("Region")
- << tr("Firmware") << tr("Size") << tr("Version") << tr("Play Time") << tr("Path")
- << tr("Favorite");
- this->setHorizontalHeaderLabels(headers);
- this->horizontalHeader()->setSortIndicatorShown(true);
- this->horizontalHeader()->setSectionResizeMode(0, QHeaderView::ResizeToContents);
- this->horizontalHeader()->setSectionResizeMode(3, QHeaderView::Fixed);
- this->horizontalHeader()->setSectionResizeMode(4, QHeaderView::Fixed);
- this->horizontalHeader()->setSectionResizeMode(9, QHeaderView::Stretch);
- this->horizontalHeader()->setSectionResizeMode(10, QHeaderView::Fixed);
- PopulateGameList();
-
- connect(this, &QTableWidget::currentCellChanged, this, &GameListFrame::onCurrentCellChanged);
- connect(this->verticalScrollBar(), &QScrollBar::valueChanged, this,
- &GameListFrame::RefreshListBackgroundImage);
- connect(this->horizontalScrollBar(), &QScrollBar::valueChanged, this,
- &GameListFrame::RefreshListBackgroundImage);
-
- this->horizontalHeader()->setSortIndicatorShown(true);
- this->horizontalHeader()->setSectionsClickable(true);
- QObject::connect(
- this->horizontalHeader(), &QHeaderView::sectionClicked, this, [this](int columnIndex) {
- if (ListSortedAsc) {
- SortNameDescending(columnIndex);
- this->horizontalHeader()->setSortIndicator(columnIndex, Qt::DescendingOrder);
- ListSortedAsc = false;
- sortColumn = columnIndex;
- } else {
- SortNameAscending(columnIndex);
- this->horizontalHeader()->setSortIndicator(columnIndex, Qt::AscendingOrder);
- ListSortedAsc = true;
- sortColumn = columnIndex;
- }
- this->clearContents();
- PopulateGameList(false);
- });
-
- connect(this, &QTableWidget::customContextMenuRequested, this, [=, this](const QPoint& pos) {
- int changedFavorite = m_gui_context_menus.RequestGameMenu(
- pos, m_game_info->m_games, m_compat_info, m_gui_settings, this, true);
- PopulateGameList(false);
- });
-
- connect(this, &QTableWidget::cellClicked, this, [=, this](int row, int column) {
- if (column == 2 && m_game_info->m_games[row].compatibility.issue_number != "") {
- auto url_issues =
- "https://github.com/shadps4-compatibility/shadps4-game-compatibility/issues/";
- QDesktopServices::openUrl(
- QUrl(url_issues + m_game_info->m_games[row].compatibility.issue_number));
- } else if (column == 10) {
- last_favorite = m_game_info->m_games[row].serial;
- QString serialStr = QString::fromStdString(last_favorite);
- QList list =
- gui_settings::Var2List(m_gui_settings->GetValue(gui::favorites_list));
- bool isFavorite = list.contains(serialStr);
- if (isFavorite) {
- list.removeOne(serialStr);
- } else {
- list.append(serialStr);
- }
- m_gui_settings->SetValue(gui::favorites_list, gui_settings::List2Var(list));
- PopulateGameList(false);
- }
- });
-}
-
-void GameListFrame::onCurrentCellChanged(int currentRow, int currentColumn, int previousRow,
- int previousColumn) {
- QTableWidgetItem* item = this->item(currentRow, currentColumn);
- if (!item) {
- return;
- }
- m_current_item = item; // Store current item
- SetListBackgroundImage(item);
- PlayBackgroundMusic(item);
-}
-
-void GameListFrame::PlayBackgroundMusic(QTableWidgetItem* item) {
- if (!item || !m_gui_settings->GetValue(gui::gl_playBackgroundMusic).toBool()) {
- BackgroundMusicPlayer::getInstance().stopMusic();
- return;
- }
- QString snd0path;
- Common::FS::PathToQString(snd0path, m_game_info->m_games[item->row()].snd0_path);
- BackgroundMusicPlayer::getInstance().playMusic(snd0path);
-}
-
-void GameListFrame::PopulateGameList(bool isInitialPopulation) {
- this->m_current_item = nullptr;
- // Do not show status column if it is not enabled
- this->setColumnHidden(2, !Config::getCompatibilityEnabled());
- this->setColumnHidden(6, !Config::GetLoadGameSizeEnabled());
-
- this->setRowCount(m_game_info->m_games.size());
- ResizeIcons(icon_size);
-
- ApplyLastSorting(isInitialPopulation);
-
- for (int i = 0; i < m_game_info->m_games.size(); i++) {
- SetTableItem(i, 1, QString::fromStdString(m_game_info->m_games[i].name));
- if (std::filesystem::exists(Common::FS::GetUserPath(Common::FS::PathType::CustomConfigs) /
- (m_game_info->m_games[i].serial + ".toml"))) {
- QTableWidgetItem* name_item = item(i, 1);
- name_item->setIcon(QIcon(":images/game_settings.png"));
- }
-
- SetTableItem(i, 3, QString::fromStdString(m_game_info->m_games[i].serial));
- SetRegionFlag(i, 4, QString::fromStdString(m_game_info->m_games[i].region));
- SetTableItem(i, 5, QString::fromStdString(m_game_info->m_games[i].fw));
- SetTableItem(i, 6, QString::fromStdString(m_game_info->m_games[i].size));
- SetTableItem(i, 7, QString::fromStdString(m_game_info->m_games[i].version));
- SetFavoriteIcon(i, 10);
-
- if (m_game_info->m_games[i].serial == last_favorite && !isInitialPopulation) {
- this->setCurrentCell(i, 10);
- }
-
- m_game_info->m_games[i].compatibility =
- m_compat_info->GetCompatibilityInfo(m_game_info->m_games[i].serial);
- SetCompatibilityItem(i, 2, m_game_info->m_games[i].compatibility);
-
- QString playTime = GetPlayTime(m_game_info->m_games[i].serial);
- if (playTime.isEmpty()) {
- m_game_info->m_games[i].play_time = "0:00:00";
- SetTableItem(i, 8, tr("Never Played"));
- } else {
- QStringList timeParts = playTime.split(':');
- int hours = timeParts[0].toInt();
- int minutes = timeParts[1].toInt();
- int seconds = timeParts[2].toInt();
-
- QString formattedPlayTime;
- if (hours > 0) {
- formattedPlayTime += QString("%1").arg(hours) + tr("h");
- }
- if (minutes > 0) {
- formattedPlayTime += QString("%1").arg(minutes) + tr("m");
- }
-
- formattedPlayTime = formattedPlayTime.trimmed();
- m_game_info->m_games[i].play_time = playTime.toStdString();
- if (formattedPlayTime.isEmpty()) {
- SetTableItem(i, 8, QString("%1").arg(seconds) + tr("s"));
- } else {
- SetTableItem(i, 8, formattedPlayTime);
- }
- }
-
- QString path;
- Common::FS::PathToQString(path, m_game_info->m_games[i].path);
- SetTableItem(i, 9, path);
- }
-}
-
-void GameListFrame::SetListBackgroundImage(QTableWidgetItem* item) {
- if (!item) {
- // handle case where no item was clicked
- return;
- }
-
- // If background images are hidden, clear the background image
- if (!m_gui_settings->GetValue(gui::gl_showBackgroundImage).toBool()) {
- backgroundImage = QImage();
- m_last_opacity = -1; // Reset opacity tracking when disabled
- m_current_game_path.clear(); // Reset current game path
- RefreshListBackgroundImage();
- return;
- }
-
- const auto& game = m_game_info->m_games[item->row()];
- const int opacity = m_gui_settings->GetValue(gui::gl_backgroundImageOpacity).toInt();
-
- // Recompute if opacity changed or we switched to a different game
- if (opacity != m_last_opacity || game.pic_path != m_current_game_path) {
- auto image_path = game.pic_path.u8string();
- QImage original_image(QString::fromStdString({image_path.begin(), image_path.end()}));
- if (!original_image.isNull()) {
- backgroundImage = m_game_list_utils.ChangeImageOpacity(
- original_image, original_image.rect(), opacity / 100.0f);
- m_last_opacity = opacity;
- m_current_game_path = game.pic_path;
- }
- }
-
- RefreshListBackgroundImage();
-}
-
-void GameListFrame::RefreshListBackgroundImage() {
- QPalette palette;
- if (!backgroundImage.isNull() &&
- m_gui_settings->GetValue(gui::gl_showBackgroundImage).toBool()) {
- QSize widgetSize = size();
- QPixmap scaledPixmap =
- QPixmap::fromImage(backgroundImage)
- .scaled(widgetSize, Qt::KeepAspectRatioByExpanding, Qt::SmoothTransformation);
- int x = (widgetSize.width() - scaledPixmap.width()) / 2;
- int y = (widgetSize.height() - scaledPixmap.height()) / 2;
- QPixmap finalPixmap(widgetSize);
- finalPixmap.fill(Qt::transparent);
- QPainter painter(&finalPixmap);
- painter.drawPixmap(x, y, scaledPixmap);
- palette.setBrush(QPalette::Base, QBrush(finalPixmap));
- }
- QColor transparentColor = QColor(135, 206, 235, 40);
- palette.setColor(QPalette::Highlight, transparentColor);
- this->setPalette(palette);
-}
-
-void GameListFrame::resizeEvent(QResizeEvent* event) {
- QTableWidget::resizeEvent(event);
- RefreshListBackgroundImage();
-}
-
-bool GameListFrame::CompareWithFavorite(GameInfo a, GameInfo b, int columnIndex, bool ascending) {
- std::string serial_a = a.serial;
- std::string serial_b = b.serial;
- QString serialStr_a = QString::fromStdString(a.serial);
- QString serialStr_b = QString::fromStdString(b.serial);
- QList list = gui_settings::Var2List(m_gui_settings->GetValue(gui::favorites_list));
- bool isFavorite_a = list.contains(serialStr_a);
- bool isFavorite_b = list.contains(serialStr_b);
- if (isFavorite_a != isFavorite_b) {
- return isFavorite_a;
- } else if (ascending) {
- return CompareStringsAscending(a, b, columnIndex);
- } else {
- return CompareStringsDescending(a, b, columnIndex);
- }
-}
-
-void GameListFrame::SortNameAscending(int columnIndex) {
- std::sort(m_game_info->m_games.begin(), m_game_info->m_games.end(),
- [this, columnIndex](const GameInfo& a, const GameInfo& b) {
- return this->CompareWithFavorite(a, b, columnIndex, true);
- });
-}
-
-void GameListFrame::SortNameDescending(int columnIndex) {
- std::sort(m_game_info->m_games.begin(), m_game_info->m_games.end(),
- [this, columnIndex](const GameInfo& a, const GameInfo& b) {
- return this->CompareWithFavorite(a, b, columnIndex, false);
- });
-}
-
-void GameListFrame::ApplyLastSorting(bool isInitialPopulation) {
- if (isInitialPopulation) {
- SortNameAscending(1); // Column 1 = Name
- ResizeIcons(icon_size);
- } else if (ListSortedAsc) {
- SortNameAscending(sortColumn);
- ResizeIcons(icon_size);
- } else {
- SortNameDescending(sortColumn);
- ResizeIcons(icon_size);
- }
-}
-
-void GameListFrame::ResizeIcons(int iconSize) {
- for (int index = 0; auto& game : m_game_info->m_games) {
- QImage scaledPixmap = game.icon.scaled(QSize(iconSize, iconSize), Qt::KeepAspectRatio,
- Qt::SmoothTransformation);
- QTableWidgetItem* iconItem = new QTableWidgetItem();
- this->verticalHeader()->resizeSection(index, scaledPixmap.height());
- this->horizontalHeader()->resizeSection(0, scaledPixmap.width());
- iconItem->setData(Qt::DecorationRole, scaledPixmap);
- this->setItem(index, 0, iconItem);
- index++;
- }
- this->horizontalHeader()->setSectionResizeMode(8, QHeaderView::ResizeToContents);
-}
-
-void GameListFrame::SetCompatibilityItem(int row, int column, CompatibilityEntry entry) {
- QTableWidgetItem* item = new QTableWidgetItem();
- QWidget* widget = new QWidget(this);
- QGridLayout* layout = new QGridLayout(widget);
-
- widget->setStyleSheet("QToolTip {background-color: black; color: white;}");
-
- QColor color;
- QString status_explanation;
-
- switch (entry.status) {
- case CompatibilityStatus::Unknown:
- color = QStringLiteral("#000000");
- status_explanation = tr("Compatibility is untested");
- break;
- case CompatibilityStatus::Nothing:
- color = QStringLiteral("#212121");
- status_explanation = tr("Game does not initialize properly / crashes the emulator");
- break;
- case CompatibilityStatus::Boots:
- color = QStringLiteral("#828282");
- status_explanation = tr("Game boots, but only displays a blank screen");
- break;
- case CompatibilityStatus::Menus:
- color = QStringLiteral("#FF0000");
- status_explanation = tr("Game displays an image but does not go past the menu");
- break;
- case CompatibilityStatus::Ingame:
- color = QStringLiteral("#F2D624");
- status_explanation = tr("Game has game-breaking glitches or unplayable performance");
- break;
- case CompatibilityStatus::Playable:
- color = QStringLiteral("#47D35C");
- status_explanation =
- tr("Game can be completed with playable performance and no major glitches");
- break;
- }
-
- QString tooltip_string;
-
- if (entry.status == CompatibilityStatus::Unknown) {
- tooltip_string = status_explanation;
- } else {
- tooltip_string =
- " " + tr("Click to see details on github") + "" + "
" +
- tr("Last updated") +
- QString(": %1 (%2)").arg(entry.last_tested.toString("yyyy-MM-dd"), entry.version) +
- "
" + status_explanation + "
";
- }
-
- QPixmap circle_pixmap(16, 16);
- circle_pixmap.fill(Qt::transparent);
- QPainter painter(&circle_pixmap);
- painter.setRenderHint(QPainter::Antialiasing);
- painter.setPen(color);
- painter.setBrush(color);
- painter.drawEllipse({circle_pixmap.width() / 2.0, circle_pixmap.height() / 2.0}, 6.0, 6.0);
-
- QLabel* dotLabel = new QLabel("", widget);
- dotLabel->setPixmap(circle_pixmap);
-
- QLabel* label = new QLabel(m_compat_info->GetCompatStatusString(entry.status), widget);
- this->horizontalHeader()->setSectionResizeMode(2, QHeaderView::ResizeToContents);
-
- label->setStyleSheet("color: white; font-size: 16px; font-weight: bold;");
-
- // Create shadow effect
- QGraphicsDropShadowEffect* shadowEffect = new QGraphicsDropShadowEffect();
- shadowEffect->setBlurRadius(5); // Set the blur radius of the shadow
- shadowEffect->setColor(QColor(0, 0, 0, 160)); // Set the color and opacity of the shadow
- shadowEffect->setOffset(2, 2); // Set the offset of the shadow
-
- label->setGraphicsEffect(shadowEffect); // Apply shadow effect to the QLabel
-
- layout->addWidget(dotLabel, 0, 0, -1, 1);
- layout->addWidget(label, 0, 1, 1, 1);
- layout->setAlignment(Qt::AlignLeft);
- widget->setLayout(layout);
- widget->setToolTip(tooltip_string);
- this->setItem(row, column, item);
- this->setCellWidget(row, column, widget);
-
- return;
-}
-
-void GameListFrame::SetTableItem(int row, int column, QString itemStr) {
- QTableWidgetItem* item = new QTableWidgetItem();
- QWidget* widget = new QWidget(this);
- QVBoxLayout* layout = new QVBoxLayout(widget);
- QLabel* label = new QLabel(itemStr, widget);
-
- label->setStyleSheet("color: white; font-size: 16px; font-weight: bold;");
-
- // Create shadow effect
- QGraphicsDropShadowEffect* shadowEffect = new QGraphicsDropShadowEffect();
- shadowEffect->setBlurRadius(5); // Set the blur radius of the shadow
- shadowEffect->setColor(QColor(0, 0, 0, 160)); // Set the color and opacity of the shadow
- shadowEffect->setOffset(2, 2); // Set the offset of the shadow
-
- label->setGraphicsEffect(shadowEffect); // Apply shadow effect to the QLabel
-
- layout->addWidget(label);
- if (column != 8 && column != 1)
- layout->setAlignment(Qt::AlignCenter);
- widget->setLayout(layout);
- this->setItem(row, column, item);
- this->setCellWidget(row, column, widget);
-}
-
-void GameListFrame::SetRegionFlag(int row, int column, QString itemStr) {
- QTableWidgetItem* item = new QTableWidgetItem();
- QImage scaledPixmap;
- if (itemStr == "Japan") {
- scaledPixmap = QImage(":images/flag_jp.png");
- } else if (itemStr == "Europe") {
- scaledPixmap = QImage(":images/flag_eu.png");
- } else if (itemStr == "USA") {
- scaledPixmap = QImage(":images/flag_us.png");
- } else if (itemStr == "Asia") {
- scaledPixmap = QImage(":images/flag_china.png");
- } else if (itemStr == "World") {
- scaledPixmap = QImage(":images/flag_world.png");
- } else {
- scaledPixmap = QImage(":images/flag_unk.png");
- }
- QWidget* widget = new QWidget(this);
- QVBoxLayout* layout = new QVBoxLayout(widget);
- QLabel* label = new QLabel(widget);
- label->setPixmap(QPixmap::fromImage(scaledPixmap));
- layout->setAlignment(Qt::AlignCenter);
- layout->addWidget(label);
- widget->setLayout(layout);
- this->setItem(row, column, item);
- this->setCellWidget(row, column, widget);
-}
-
-void GameListFrame::SetFavoriteIcon(int row, int column) {
-
- QString serialStr = QString::fromStdString(m_game_info->m_games[row].serial);
- QList list = gui_settings::Var2List(m_gui_settings->GetValue(gui::favorites_list));
- bool isFavorite = list.contains(serialStr);
-
- QTableWidgetItem* item = new QTableWidgetItem();
- QImage scaledPixmap = QImage(":images/favorite_icon.png");
-
- scaledPixmap = scaledPixmap.scaledToHeight(this->columnWidth(column) / 2.5);
- scaledPixmap = scaledPixmap.scaledToWidth(this->columnWidth(column) / 2.5);
- QWidget* widget = new QWidget(this);
- QVBoxLayout* layout = new QVBoxLayout(widget);
- QLabel* label = new QLabel(widget);
- label->setPixmap(QPixmap::fromImage(scaledPixmap));
- label->setObjectName("favoriteIcon");
- label->setVisible(isFavorite);
-
- layout->setAlignment(Qt::AlignCenter);
- layout->addWidget(label);
- widget->setLayout(layout);
- this->setItem(row, column, item);
- this->setCellWidget(row, column, widget);
-
- if (column > 0) {
- this->horizontalHeader()->setSectionResizeMode(column - 1, QHeaderView::Stretch);
- }
-}
-
-QString GameListFrame::GetPlayTime(const std::string& serial) {
- QString playTime;
- const auto user_dir = Common::FS::GetUserPath(Common::FS::PathType::UserDir);
- QString filePath = QString::fromStdString((user_dir / "play_time.txt").string());
-
- QFile file(filePath);
- if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
- return playTime;
- }
-
- while (!file.atEnd()) {
- QByteArray line = file.readLine();
- QString lineStr = QString::fromUtf8(line).trimmed();
-
- QStringList parts = lineStr.split(' ');
- if (parts.size() >= 2) {
- QString fileSerial = parts[0];
- QString time = parts[1];
-
- if (fileSerial == QString::fromStdString(serial)) {
- playTime = time;
- break;
- }
- }
- }
-
- file.close();
- return playTime;
-}
-
-QTableWidgetItem* GameListFrame::GetCurrentItem() {
- return m_current_item;
-}
diff --git a/src/qt_gui/game_list_frame.h b/src/qt_gui/game_list_frame.h
deleted file mode 100644
index d1e065864..000000000
--- a/src/qt_gui/game_list_frame.h
+++ /dev/null
@@ -1,139 +0,0 @@
-// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-#pragma once
-
-#include // std::transform
-#include // std::tolower
-
-#include
-#include
-#include
-#include
-#include
-
-#include "background_music_player.h"
-#include "compatibility_info.h"
-#include "game_info.h"
-#include "game_list_utils.h"
-#include "gui_context_menus.h"
-#include "gui_settings.h"
-
-class GameListFrame : public QTableWidget {
- Q_OBJECT
-public:
- explicit GameListFrame(std::shared_ptr gui_settings,
- std::shared_ptr game_info_get,
- std::shared_ptr compat_info_get,
- QWidget* parent = nullptr);
-Q_SIGNALS:
- void GameListFrameClosed();
-
-public Q_SLOTS:
- void SetListBackgroundImage(QTableWidgetItem* item);
- void RefreshListBackgroundImage();
- void resizeEvent(QResizeEvent* event);
- void SortNameAscending(int columnIndex);
- void SortNameDescending(int columnIndex);
- void PlayBackgroundMusic(QTableWidgetItem* item);
- void onCurrentCellChanged(int currentRow, int currentColumn, int previousRow,
- int previousColumn);
-
-private:
- void SetTableItem(int row, int column, QString itemStr);
- void SetRegionFlag(int row, int column, QString itemStr);
- void SetFavoriteIcon(int row, int column);
- void SetCompatibilityItem(int row, int column, CompatibilityEntry entry);
- QString GetPlayTime(const std::string& serial);
- QList m_columnActs;
- GameInfoClass* game_inf_get = nullptr;
- bool ListSortedAsc = true;
- int sortColumn = 1;
- QTableWidgetItem* m_current_item = nullptr;
- int m_last_opacity = -1; // Track last opacity to avoid unnecessary recomputation
- std::filesystem::path m_current_game_path; // Track current game path to detect changes
- std::shared_ptr m_gui_settings;
-
-public:
- void PopulateGameList(bool isInitialPopulation = true);
- void ResizeIcons(int iconSize);
- void ApplyLastSorting(bool isInitialPopulation);
- QTableWidgetItem* GetCurrentItem();
- QImage backgroundImage;
- GameListUtils m_game_list_utils;
- GuiContextMenus m_gui_context_menus;
- std::shared_ptr m_game_info;
- std::shared_ptr m_compat_info;
-
- int icon_size;
- std::string last_favorite;
-
- static float parseAsFloat(const std::string& str, const int& offset) {
- return std::stof(str.substr(0, str.size() - offset));
- }
-
- static float parseSizeMB(const std::string& size) {
- float num = parseAsFloat(size, 3);
- return (size[size.size() - 2] == 'G') ? num * 1024 : num;
- }
-
- static bool CompareStringsAscending(GameInfo a, GameInfo b, int columnIndex) {
- switch (columnIndex) {
- case 1: {
- std::string name_a = a.name, name_b = b.name;
- std::transform(name_a.begin(), name_a.end(), name_a.begin(), ::tolower);
- std::transform(name_b.begin(), name_b.end(), name_b.begin(), ::tolower);
- return name_a < name_b;
- }
- case 2:
- return a.compatibility.status < b.compatibility.status;
- case 3:
- return a.serial.substr(4) < b.serial.substr(4);
- case 4:
- return a.region < b.region;
- case 5:
- return parseAsFloat(a.fw, 0) < parseAsFloat(b.fw, 0);
- case 6:
- return parseSizeMB(b.size) < parseSizeMB(a.size);
- case 7:
- return a.version < b.version;
- case 8:
- return a.play_time < b.play_time;
- case 9:
- return a.path < b.path;
- default:
- return false;
- }
- }
-
- static bool CompareStringsDescending(GameInfo a, GameInfo b, int columnIndex) {
- switch (columnIndex) {
- case 1: {
- std::string name_a = a.name, name_b = b.name;
- std::transform(name_a.begin(), name_a.end(), name_a.begin(), ::tolower);
- std::transform(name_b.begin(), name_b.end(), name_b.begin(), ::tolower);
- return name_a > name_b;
- }
- case 2:
- return a.compatibility.status > b.compatibility.status;
- case 3:
- return a.serial.substr(4) > b.serial.substr(4);
- case 4:
- return a.region > b.region;
- case 5:
- return parseAsFloat(a.fw, 0) > parseAsFloat(b.fw, 0);
- case 6:
- return parseSizeMB(b.size) > parseSizeMB(a.size);
- case 7:
- return a.version > b.version;
- case 8:
- return a.play_time > b.play_time;
- case 9:
- return a.path > b.path;
- default:
- return false;
- }
- }
-
- bool CompareWithFavorite(GameInfo a, GameInfo b, int columnIndex, bool ascending);
-};
diff --git a/src/qt_gui/game_list_utils.h b/src/qt_gui/game_list_utils.h
deleted file mode 100644
index e19cf364e..000000000
--- a/src/qt_gui/game_list_utils.h
+++ /dev/null
@@ -1,235 +0,0 @@
-// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-#pragma once
-
-#include
-#include
-#include
-#include
-#include
-#include "common/path_util.h"
-#include "compatibility_info.h"
-
-struct GameInfo {
- std::filesystem::path path; // root path of game directory
- // (normally directory that contains eboot.bin)
- std::filesystem::path icon_path; // path of icon0.png
- std::filesystem::path pic_path; // path of pic1.png
- std::filesystem::path snd0_path; // path of snd0.at9
- QImage icon;
- std::string size;
- // variables extracted from param.sfo
- std::string name = "Unknown";
- std::string serial = "Unknown";
- std::string version = "Unknown";
- std::string region = "Unknown";
- std::string fw = "Unknown";
- std::string save_dir = "Unknown";
-
- std::string play_time = "Unknown";
- CompatibilityEntry compatibility = CompatibilityEntry{CompatibilityStatus::Unknown};
-};
-
-class GameListUtils : public QObject {
- Q_OBJECT
-public:
- static QString FormatSize(qint64 size) {
- static const QStringList suffixes = {tr("B"), tr("KB"), tr("MB"), tr("GB"), tr("TB")};
- int suffixIndex = 0;
-
- double gameSize = static_cast(size);
- while (gameSize >= 1024 && suffixIndex < suffixes.size() - 1) {
- gameSize /= 1024;
- ++suffixIndex;
- }
-
- // Format the size with a specified precision
- QString sizeString;
- if (gameSize < 10.0) {
- sizeString = QString::number(gameSize, 'f', 2);
- } else if (gameSize < 100.0) {
- sizeString = QString::number(gameSize, 'f', 1);
- } else {
- sizeString = QString::number(gameSize, 'f', 0);
- }
-
- return sizeString + " " + suffixes[suffixIndex];
- }
-
- static void GetFolderSize(GameInfo& game) {
- QString dirPath;
- Common::FS::PathToQString(dirPath, game.path);
- QDir dir(dirPath);
- QDirIterator it(dir.absolutePath(), QDirIterator::Subdirectories);
- qint64 total = 0;
-
- if (!Config::GetLoadGameSizeEnabled()) {
- game.size = FormatSize(0).toStdString();
- return;
- }
-
- // Cache path
- QDir cacheDir =
- QDir(Common::FS::GetUserPath(Common::FS::PathType::MetaDataDir) / game.serial);
- if (!cacheDir.exists()) {
- cacheDir.mkpath(".");
- }
- QFile size_cache_file(cacheDir.absoluteFilePath("size_cache.txt"));
- QFileInfo cacheInfo(size_cache_file);
- QFileInfo dirInfo(dirPath);
-
- // Check if cache file exists and is valid
- if (size_cache_file.exists() && cacheInfo.lastModified() >= dirInfo.lastModified()) {
- if (size_cache_file.open(QIODevice::ReadOnly | QIODevice::Text)) {
- QTextStream in(&size_cache_file);
- QString cachedSize = in.readLine();
- size_cache_file.close();
-
- if (!cachedSize.isEmpty()) {
- game.size = cachedSize.toStdString();
- return;
- }
- }
- }
-
- // Cache is invalid or does not exist; calculate size
- while (it.hasNext()) {
- it.next();
- total += it.fileInfo().size();
- }
-
- game.size = FormatSize(total).toStdString();
-
- // Save new cache
- if (size_cache_file.open(QIODevice::WriteOnly | QIODevice::Text)) {
- QTextStream out(&size_cache_file);
- out << QString::fromStdString(game.size) << "\n";
- size_cache_file.close();
- }
- }
-
- static QString GetRegion(char region) {
- switch (region) {
- case 'U':
- return "USA";
- case 'E':
- return "Europe";
- case 'J':
- return "Japan";
- case 'H':
- return "Asia";
- case 'I':
- return "World";
- default:
- return "Unknown";
- }
- }
-
- static QString GetAppType(int type) {
- switch (type) {
- case 0:
- return "Not Specified";
- case 1:
- return "FULL APP";
- case 2:
- return "UPGRADABLE";
- case 3:
- return "DEMO";
- case 4:
- return "FREEMIUM";
- default:
- return "Unknown";
- }
- }
-
- QImage BlurImage(const QImage& image, const QRect& rect, int radius) {
- int tab[] = {14, 10, 8, 6, 5, 5, 4, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2};
- int alpha = (radius < 1) ? 16 : (radius > 17) ? 1 : tab[radius - 1];
-
- QImage result = image.convertToFormat(QImage::Format_ARGB32);
- int r1 = rect.top();
- int r2 = rect.bottom();
- int c1 = rect.left();
- int c2 = rect.right();
-
- int bpl = result.bytesPerLine();
- int rgba[4];
- unsigned char* p;
-
- int i1 = 0;
- int i2 = 3;
-
- for (int col = c1; col <= c2; col++) {
- p = result.scanLine(r1) + col * 4;
- for (int i = i1; i <= i2; i++)
- rgba[i] = p[i] << 4;
-
- p += bpl;
- for (int j = r1; j < r2; j++, p += bpl)
- for (int i = i1; i <= i2; i++)
- p[i] = (rgba[i] += ((p[i] << 4) - rgba[i]) * alpha / 16) >> 4;
- }
-
- for (int row = r1; row <= r2; row++) {
- p = result.scanLine(row) + c1 * 4;
- for (int i = i1; i <= i2; i++)
- rgba[i] = p[i] << 4;
-
- p += 4;
- for (int j = c1; j < c2; j++, p += 4)
- for (int i = i1; i <= i2; i++)
- p[i] = (rgba[i] += ((p[i] << 4) - rgba[i]) * alpha / 16) >> 4;
- }
-
- for (int col = c1; col <= c2; col++) {
- p = result.scanLine(r2) + col * 4;
- for (int i = i1; i <= i2; i++)
- rgba[i] = p[i] << 4;
-
- p -= bpl;
- for (int j = r1; j < r2; j++, p -= bpl)
- for (int i = i1; i <= i2; i++)
- p[i] = (rgba[i] += ((p[i] << 4) - rgba[i]) * alpha / 16) >> 4;
- }
-
- for (int row = r1; row <= r2; row++) {
- p = result.scanLine(row) + c2 * 4;
- for (int i = i1; i <= i2; i++)
- rgba[i] = p[i] << 4;
-
- p -= 4;
- for (int j = c1; j < c2; j++, p -= 4)
- for (int i = i1; i <= i2; i++)
- p[i] = (rgba[i] += ((p[i] << 4) - rgba[i]) * alpha / 16) >> 4;
- }
-
- return result;
- }
-
- // Opacity is a float between 0 and 1
- static QImage ChangeImageOpacity(const QImage& image, const QRect& rect, float opacity) {
- // Convert to ARGB32 format to ensure alpha channel support
- QImage result = image.convertToFormat(QImage::Format_ARGB32);
-
- // Ensure opacity is between 0 and 1
- opacity = std::clamp(opacity, 0.0f, 1.0f);
-
- // Convert opacity to integer alpha value (0-255)
- int alpha = static_cast(opacity * 255);
-
- // Process only the specified rectangle area
- for (int y = rect.top(); y <= rect.bottom(); ++y) {
- QRgb* line = reinterpret_cast(result.scanLine(y));
- for (int x = rect.left(); x <= rect.right(); ++x) {
- // Get current pixel
- QRgb pixel = line[x];
- // Keep RGB values, but modify alpha while preserving relative transparency
- int newAlpha = (qAlpha(pixel) * alpha) / 255;
- line[x] = qRgba(qRed(pixel), qGreen(pixel), qBlue(pixel), newAlpha);
- }
- }
-
- return result;
- }
-};
diff --git a/src/qt_gui/gui_context_menus.h b/src/qt_gui/gui_context_menus.h
deleted file mode 100644
index 17053790b..000000000
--- a/src/qt_gui/gui_context_menus.h
+++ /dev/null
@@ -1,755 +0,0 @@
-// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-#pragma once
-
-#include
-#include
-#include
-#include
-#include
-
-#include
-#include "cheats_patches.h"
-#include "common/config.h"
-#include "common/path_util.h"
-#include "common/scm_rev.h"
-#include "compatibility_info.h"
-#include "game_info.h"
-#include "gui_settings.h"
-#include "settings_dialog.h"
-#include "trophy_viewer.h"
-
-#ifdef Q_OS_WIN
-#include
-#include
-#include
-#include
-#include
-#include
-#endif
-
-class GuiContextMenus : public QObject {
- Q_OBJECT
-public:
- int RequestGameMenu(const QPoint& pos, QVector& m_games,
- std::shared_ptr m_compat_info,
- std::shared_ptr settings, QTableWidget* widget, bool isList) {
- QPoint global_pos = widget->viewport()->mapToGlobal(pos);
- std::shared_ptr m_gui_settings = std::move(settings);
- int itemID = 0;
- int changedFavorite = 0;
- if (isList) {
- itemID = widget->currentRow();
- } else {
- itemID = widget->currentRow() * widget->columnCount() + widget->currentColumn();
- }
-
- // Do not show the menu if no item is selected
- if (itemID < 0 || itemID >= m_games.size()) {
- return changedFavorite;
- }
-
- // Setup menu.
- QMenu menu(widget);
-
- // "Open Folder..." submenu
- QMenu* openFolderMenu = new QMenu(tr("Open Folder..."), widget);
- QAction* openGameFolder = new QAction(tr("Open Game Folder"), widget);
- QAction* openUpdateFolder = new QAction(tr("Open Update Folder"), widget);
- QAction* openSaveDataFolder = new QAction(tr("Open Save Data Folder"), widget);
- QAction* openLogFolder = new QAction(tr("Open Log Folder"), widget);
-
- openFolderMenu->addAction(openGameFolder);
- openFolderMenu->addAction(openUpdateFolder);
- openFolderMenu->addAction(openSaveDataFolder);
- openFolderMenu->addAction(openLogFolder);
-
- menu.addMenu(openFolderMenu);
-
- QMenu* gameConfigMenu = new QMenu(tr("Game-specific Settings..."), widget);
- QAction gameConfigConfigure(tr("Configure Game-specific Settings"), widget);
- QAction gameConfigCreate(tr("Create Game-specific Settings from Global Settings"), widget);
- QAction gameConfigDelete(tr("Delete Game-specific Settings"), widget);
-
- if (std::filesystem::exists(Common::FS::GetUserPath(Common::FS::PathType::CustomConfigs) /
- (m_games[itemID].serial + ".toml"))) {
- gameConfigMenu->addAction(&gameConfigConfigure);
- } else {
- gameConfigMenu->addAction(&gameConfigCreate);
- }
-
- if (std::filesystem::exists(Common::FS::GetUserPath(Common::FS::PathType::CustomConfigs) /
- (m_games[itemID].serial + ".toml")))
- gameConfigMenu->addAction(&gameConfigDelete);
-
- menu.addMenu(gameConfigMenu);
-
- QString serialStr = QString::fromStdString(m_games[itemID].serial);
- QList list = gui_settings::Var2List(m_gui_settings->GetValue(gui::favorites_list));
- bool isFavorite = list.contains(serialStr);
- QAction* toggleFavorite;
-
- if (isFavorite) {
- toggleFavorite = new QAction(tr("Remove from Favorites"), widget);
- } else {
- toggleFavorite = new QAction(tr("Add to Favorites"), widget);
- }
-
- QAction createShortcut(tr("Create Shortcut"), widget);
- QAction openCheats(tr("Cheats / Patches"), widget);
- QAction openSfoViewer(tr("SFO Viewer"), widget);
- QAction openTrophyViewer(tr("Trophy Viewer"), widget);
-
- menu.addAction(toggleFavorite);
- menu.addAction(&createShortcut);
- menu.addAction(&openCheats);
- menu.addAction(&openSfoViewer);
- menu.addAction(&openTrophyViewer);
-
- // "Copy" submenu.
- QMenu* copyMenu = new QMenu(tr("Copy info..."), widget);
- QAction* copyName = new QAction(tr("Copy Name"), widget);
- QAction* copySerial = new QAction(tr("Copy Serial"), widget);
- QAction* copyVersion = new QAction(tr("Copy Version"), widget);
- QAction* copySize = new QAction(tr("Copy Size"), widget);
- QAction* copyNameAll = new QAction(tr("Copy All"), widget);
-
- copyMenu->addAction(copyName);
- copyMenu->addAction(copySerial);
- copyMenu->addAction(copyVersion);
- if (Config::GetLoadGameSizeEnabled()) {
- copyMenu->addAction(copySize);
- }
- copyMenu->addAction(copyNameAll);
-
- menu.addMenu(copyMenu);
-
- // "Delete..." submenu.
- QMenu* deleteMenu = new QMenu(tr("Delete..."), widget);
- QAction* deleteGame = new QAction(tr("Delete Game"), widget);
- QAction* deleteUpdate = new QAction(tr("Delete Update"), widget);
- QAction* deleteSaveData = new QAction(tr("Delete Save Data"), widget);
- QAction* deleteDLC = new QAction(tr("Delete DLC"), widget);
- QAction* deleteTrophy = new QAction(tr("Delete Trophy"), widget);
-
- deleteMenu->addAction(deleteGame);
- deleteMenu->addAction(deleteUpdate);
- deleteMenu->addAction(deleteSaveData);
- deleteMenu->addAction(deleteDLC);
- deleteMenu->addAction(deleteTrophy);
-
- menu.addMenu(deleteMenu);
-
- // Compatibility submenu.
- QMenu* compatibilityMenu = new QMenu(tr("Compatibility..."), widget);
- QAction* updateCompatibility = new QAction(tr("Update Database"), widget);
- QAction* viewCompatibilityReport = new QAction(tr("View Report"), widget);
- QAction* submitCompatibilityReport = new QAction(tr("Submit a Report"), widget);
-
- compatibilityMenu->addAction(updateCompatibility);
- compatibilityMenu->addAction(viewCompatibilityReport);
- if (Common::g_is_release) {
- compatibilityMenu->addAction(submitCompatibilityReport);
- }
-
- menu.addMenu(compatibilityMenu);
-
- compatibilityMenu->setEnabled(Config::getCompatibilityEnabled());
- viewCompatibilityReport->setEnabled(m_games[itemID].compatibility.status !=
- CompatibilityStatus::Unknown);
-
- // Show menu.
- auto selected = menu.exec(global_pos);
- if (!selected) {
- return changedFavorite;
- }
-
- if (selected == openGameFolder) {
- QString folderPath;
- Common::FS::PathToQString(folderPath, m_games[itemID].path);
- QDesktopServices::openUrl(QUrl::fromLocalFile(folderPath));
- }
-
- if (selected == openUpdateFolder) {
- QString open_update_path;
- Common::FS::PathToQString(open_update_path, m_games[itemID].path);
- open_update_path += "-UPDATE";
- if (std::filesystem::exists(Common::FS::PathFromQString(open_update_path))) {
- QDesktopServices::openUrl(QUrl::fromLocalFile(open_update_path));
- } else {
- Common::FS::PathToQString(open_update_path, m_games[itemID].path);
- open_update_path += "-patch";
- if (std::filesystem::exists(Common::FS::PathFromQString(open_update_path))) {
- QDesktopServices::openUrl(QUrl::fromLocalFile(open_update_path));
- } else {
- QMessageBox::critical(nullptr, tr("Error"),
- QString(tr("This game has no update folder to open!")));
- }
- }
- }
-
- if (selected == openSaveDataFolder) {
- QString saveDataPath;
- Common::FS::PathToQString(saveDataPath,
- Config::GetSaveDataPath() / "1" / m_games[itemID].save_dir);
- QDir(saveDataPath).mkpath(saveDataPath);
- QDesktopServices::openUrl(QUrl::fromLocalFile(saveDataPath));
- }
-
- if (selected == openLogFolder) {
- QString logPath;
- Common::FS::PathToQString(logPath,
- Common::FS::GetUserPath(Common::FS::PathType::LogDir));
- if (!Config::getSeparateLogFilesEnabled()) {
- QDesktopServices::openUrl(QUrl::fromLocalFile(logPath));
- } else {
- QString fileName = QString::fromStdString(m_games[itemID].serial) + ".log";
- QString filePath = logPath + "/" + fileName;
- QStringList arguments;
- if (QFile::exists(filePath)) {
-#ifdef Q_OS_WIN
- arguments << "/select," << filePath.replace("/", "\\");
- QProcess::startDetached("explorer", arguments);
-
-#elif defined(Q_OS_MAC)
- arguments << "-R" << filePath;
- QProcess::startDetached("open", arguments);
-
-#elif defined(Q_OS_LINUX)
- QStringList arguments;
- arguments << "--select" << filePath;
- if (!QProcess::startDetached("nautilus", arguments)) {
- // Failed to open Nautilus to select file
- arguments.clear();
- arguments << logPath;
- if (!QProcess::startDetached("xdg-open", arguments)) {
- // Failed to open directory on Linux
- }
- }
-#else
- QDesktopServices::openUrl(QUrl::fromLocalFile(logPath));
-#endif
- } else {
- QMessageBox msgBox;
- msgBox.setIcon(QMessageBox::Information);
- msgBox.setText(tr("No log file found for this game!"));
-
- QPushButton* okButton = msgBox.addButton(QMessageBox::Ok);
- QPushButton* openFolderButton =
- msgBox.addButton(tr("Open Log Folder"), QMessageBox::ActionRole);
-
- msgBox.exec();
-
- if (msgBox.clickedButton() == openFolderButton) {
- QDesktopServices::openUrl(QUrl::fromLocalFile(logPath));
- }
- }
- }
- }
-
- if (selected == &openSfoViewer) {
- PSF psf;
- QString gameName = QString::fromStdString(m_games[itemID].name);
- std::filesystem::path game_folder_path = m_games[itemID].path;
- std::filesystem::path game_update_path = game_folder_path;
- game_update_path += "-UPDATE";
- if (std::filesystem::exists(game_update_path)) {
- game_folder_path = game_update_path;
- } else {
- game_update_path = game_folder_path;
- game_update_path += "-patch";
- if (std::filesystem::exists(game_update_path)) {
- game_folder_path = game_update_path;
- }
- }
- if (psf.Open(game_folder_path / "sce_sys" / "param.sfo")) {
- int rows = psf.GetEntries().size();
- QTableWidget* tableWidget = new QTableWidget(rows, 2);
- tableWidget->setAttribute(Qt::WA_DeleteOnClose);
- connect(widget->parent(), &QWidget::destroyed, tableWidget,
- [tableWidget]() { tableWidget->deleteLater(); });
-
- tableWidget->verticalHeader()->setVisible(false); // Hide vertical header
- int row = 0;
-
- for (const auto& entry : psf.GetEntries()) {
- QTableWidgetItem* keyItem =
- new QTableWidgetItem(QString::fromStdString(entry.key));
- QTableWidgetItem* valueItem;
- switch (entry.param_fmt) {
- case PSFEntryFmt::Binary: {
- const auto bin = psf.GetBinary(entry.key);
- if (!bin.has_value()) {
- valueItem = new QTableWidgetItem(QString("Unknown"));
- } else {
- std::string text;
- text.reserve(bin->size() * 2);
- for (const auto& c : *bin) {
- static constexpr char hex[] = "0123456789ABCDEF";
- text.push_back(hex[c >> 4 & 0xF]);
- text.push_back(hex[c & 0xF]);
- }
- valueItem = new QTableWidgetItem(QString::fromStdString(text));
- }
- } break;
- case PSFEntryFmt::Text: {
- auto text = psf.GetString(entry.key);
- if (!text.has_value()) {
- valueItem = new QTableWidgetItem(QString("Unknown"));
- } else {
- valueItem =
- new QTableWidgetItem(QString::fromStdString(std::string{*text}));
- }
- } break;
- case PSFEntryFmt::Integer: {
- auto integer = psf.GetInteger(entry.key);
- if (!integer.has_value()) {
- valueItem = new QTableWidgetItem(QString("Unknown"));
- } else {
- valueItem =
- new QTableWidgetItem(QString("0x") + QString::number(*integer, 16));
- }
- } break;
- }
-
- tableWidget->setItem(row, 0, keyItem);
- tableWidget->setItem(row, 1, valueItem);
- keyItem->setFlags(keyItem->flags() & ~Qt::ItemIsEditable);
- valueItem->setFlags(valueItem->flags() & ~Qt::ItemIsEditable);
- row++;
- }
- tableWidget->resizeColumnsToContents();
- tableWidget->resizeRowsToContents();
-
- int width = tableWidget->horizontalHeader()->sectionSize(0) +
- tableWidget->horizontalHeader()->sectionSize(1) + 2;
- int height = (rows + 1) * (tableWidget->rowHeight(0));
- tableWidget->setFixedSize(width, height);
- tableWidget->sortItems(0, Qt::AscendingOrder);
- tableWidget->horizontalHeader()->setVisible(false);
-
- tableWidget->horizontalHeader()->setSectionResizeMode(QHeaderView::Fixed);
- tableWidget->setWindowTitle(tr("SFO Viewer for ") + gameName);
- tableWidget->show();
- }
- }
-
- if (selected == toggleFavorite) {
- if (isFavorite) {
- list.removeOne(serialStr);
- } else {
- list.append(serialStr);
- }
- m_gui_settings->SetValue(gui::favorites_list, gui_settings::List2Var(list));
- changedFavorite = 1;
- }
-
- if (selected == &openCheats) {
- QString gameName = QString::fromStdString(m_games[itemID].name);
- QString gameSerial = QString::fromStdString(m_games[itemID].serial);
- QString gameVersion = QString::fromStdString(m_games[itemID].version);
- QString gameSize = QString::fromStdString(m_games[itemID].size);
- QString iconPath;
- Common::FS::PathToQString(iconPath, m_games[itemID].icon_path);
- QPixmap gameImage(iconPath);
- CheatsPatches* cheatsPatches =
- new CheatsPatches(gameName, gameSerial, gameVersion, gameSize, gameImage);
- cheatsPatches->show();
- connect(widget->parent(), &QWidget::destroyed, cheatsPatches,
- [cheatsPatches]() { cheatsPatches->deleteLater(); });
- }
-
- if (selected == &openTrophyViewer) {
- QString trophyPath, gameTrpPath;
- Common::FS::PathToQString(trophyPath, m_games[itemID].serial);
- Common::FS::PathToQString(gameTrpPath, m_games[itemID].path);
- auto game_update_path = Common::FS::PathFromQString(gameTrpPath);
- game_update_path += "-UPDATE";
- if (std::filesystem::exists(game_update_path)) {
- Common::FS::PathToQString(gameTrpPath, game_update_path);
- } else {
- game_update_path = Common::FS::PathFromQString(gameTrpPath);
- game_update_path += "-patch";
- if (std::filesystem::exists(game_update_path)) {
- Common::FS::PathToQString(gameTrpPath, game_update_path);
- }
- }
-
- // Array with all games and their trophy information
- QVector allTrophyGames;
- for (const auto& game : m_games) {
- TrophyGameInfo gameInfo;
- gameInfo.name = QString::fromStdString(game.name);
- Common::FS::PathToQString(gameInfo.trophyPath, game.serial);
- Common::FS::PathToQString(gameInfo.gameTrpPath, game.path);
-
- auto update_path = Common::FS::PathFromQString(gameInfo.gameTrpPath);
- update_path += "-UPDATE";
- if (std::filesystem::exists(update_path)) {
- Common::FS::PathToQString(gameInfo.gameTrpPath, update_path);
- } else {
- update_path = Common::FS::PathFromQString(gameInfo.gameTrpPath);
- update_path += "-patch";
- if (std::filesystem::exists(update_path)) {
- Common::FS::PathToQString(gameInfo.gameTrpPath, update_path);
- }
- }
-
- allTrophyGames.append(gameInfo);
- }
-
- QString gameName = QString::fromStdString(m_games[itemID].name);
- TrophyViewer* trophyViewer =
- new TrophyViewer(m_gui_settings, trophyPath, gameTrpPath, gameName, allTrophyGames);
- trophyViewer->show();
- connect(widget->parent(), &QWidget::destroyed, trophyViewer,
- [trophyViewer]() { trophyViewer->deleteLater(); });
- }
-
- if (selected == &gameConfigConfigure || selected == &gameConfigCreate) {
- auto settingsWindow = new SettingsDialog(m_gui_settings, m_compat_info, widget, false,
- true, serialStr.toStdString());
- settingsWindow->exec();
- }
-
- if (selected == &gameConfigDelete) {
- if (QMessageBox::Yes == QMessageBox::question(widget, tr("Confirm deletion"),
- tr("Delete game-specific settings?"),
- QMessageBox::Yes | QMessageBox::No)) {
- std::filesystem::remove(
- Common::FS::GetUserPath(Common::FS::PathType::CustomConfigs) /
- (m_games[itemID].serial + ".toml"));
- }
- }
-
- if (selected == &createShortcut) {
- QString targetPath;
- Common::FS::PathToQString(targetPath, m_games[itemID].path);
- QString ebootPath = targetPath + "/eboot.bin";
-
- // Get the full path to the icon
- QString iconPath;
- Common::FS::PathToQString(iconPath, m_games[itemID].icon_path);
- QFileInfo iconFileInfo(iconPath);
- QString icoPath = iconFileInfo.absolutePath() + "/" + iconFileInfo.baseName() + ".ico";
-
- // Path to shortcut/link
- QString linkPath;
-
- // Path to the shadps4.exe executable
- QString exePath;
-#ifdef Q_OS_WIN
- linkPath = QStandardPaths::writableLocation(QStandardPaths::DesktopLocation) + "/" +
- QString::fromStdString(m_games[itemID].name)
- .remove(QRegularExpression("[\\\\/:*?\"<>|]")) +
- ".lnk";
-
- exePath = QCoreApplication::applicationFilePath().replace("\\", "/");
-
-#else
- linkPath = QStandardPaths::writableLocation(QStandardPaths::DesktopLocation) + "/" +
- QString::fromStdString(m_games[itemID].name)
- .remove(QRegularExpression("[\\\\/:*?\"<>|]")) +
- ".desktop";
-#endif
-
- // Convert the icon to .ico if necessary
- if (iconFileInfo.suffix().toLower() == "png") {
- // Convert icon from PNG to ICO
- if (convertPngToIco(iconPath, icoPath)) {
-
-#ifdef Q_OS_WIN
- if (createShortcutWin(linkPath, ebootPath, icoPath, exePath)) {
-#else
- if (createShortcutLinux(linkPath, m_games[itemID].name, ebootPath, iconPath)) {
-#endif
- QMessageBox::information(
- nullptr, tr("Shortcut creation"),
- QString(tr("Shortcut created successfully!") + "\n%1").arg(linkPath));
- } else {
- QMessageBox::critical(
- nullptr, tr("Error"),
- QString(tr("Error creating shortcut!") + "\n%1").arg(linkPath));
- }
- } else {
- QMessageBox::critical(nullptr, tr("Error"), tr("Failed to convert icon."));
- }
- } else {
- // If the icon is already in ICO format, we just create the shortcut
-#ifdef Q_OS_WIN
- if (createShortcutWin(linkPath, ebootPath, iconPath, exePath)) {
-#else
- if (createShortcutLinux(linkPath, m_games[itemID].name, ebootPath, iconPath)) {
-#endif
- QMessageBox::information(
- nullptr, tr("Shortcut creation"),
- QString(tr("Shortcut created successfully!") + "\n%1").arg(linkPath));
- } else {
- QMessageBox::critical(
- nullptr, tr("Error"),
- QString(tr("Error creating shortcut!") + "\n%1").arg(linkPath));
- }
- }
- }
-
- // Handle the "Copy" actions
- if (selected == copyName) {
- QClipboard* clipboard = QGuiApplication::clipboard();
- clipboard->setText(QString::fromStdString(m_games[itemID].name));
- }
-
- if (selected == copySerial) {
- QClipboard* clipboard = QGuiApplication::clipboard();
- clipboard->setText(QString::fromStdString(m_games[itemID].serial));
- }
-
- if (selected == copyVersion) {
- QClipboard* clipboard = QGuiApplication::clipboard();
- clipboard->setText(QString::fromStdString(m_games[itemID].version));
- }
-
- if (selected == copySize) {
- QClipboard* clipboard = QGuiApplication::clipboard();
- clipboard->setText(QString::fromStdString(m_games[itemID].size));
- }
-
- if (selected == copyNameAll) {
- QString GameSizeEnabled;
- if (Config::GetLoadGameSizeEnabled()) {
- GameSizeEnabled = " | Size:" + QString::fromStdString(m_games[itemID].size);
- }
-
- QClipboard* clipboard = QGuiApplication::clipboard();
- QString combinedText = QString("Name:%1 | Serial:%2 | Version:%3%4")
- .arg(QString::fromStdString(m_games[itemID].name))
- .arg(QString::fromStdString(m_games[itemID].serial))
- .arg(QString::fromStdString(m_games[itemID].version))
- .arg(GameSizeEnabled);
-
- clipboard->setText(combinedText);
- }
-
- if (selected == deleteGame || selected == deleteUpdate || selected == deleteDLC ||
- selected == deleteSaveData || selected == deleteTrophy) {
- bool error = false;
- QString folder_path, game_update_path, dlc_path, save_data_path, trophy_data_path;
- Common::FS::PathToQString(folder_path, m_games[itemID].path);
- game_update_path = folder_path + "-UPDATE";
- if (!std::filesystem::exists(Common::FS::PathFromQString(game_update_path))) {
- game_update_path = folder_path + "-patch";
- }
- Common::FS::PathToQString(
- dlc_path, Config::getAddonInstallDir() /
- Common::FS::PathFromQString(folder_path).parent_path().filename());
- Common::FS::PathToQString(save_data_path,
- Config::GetSaveDataPath() / "1" / m_games[itemID].save_dir);
-
- Common::FS::PathToQString(trophy_data_path,
- Common::FS::GetUserPath(Common::FS::PathType::MetaDataDir) /
- m_games[itemID].serial / "TrophyFiles");
-
- QString message_type;
-
- if (selected == deleteGame) {
- BackgroundMusicPlayer::getInstance().stopMusic();
- message_type = tr("Game");
- } else if (selected == deleteUpdate) {
- if (!std::filesystem::exists(Common::FS::PathFromQString(game_update_path))) {
- QMessageBox::critical(nullptr, tr("Error"),
- QString(tr("This game has no update to delete!")));
- error = true;
- } else {
- folder_path = game_update_path;
- message_type = tr("Update");
- }
- } else if (selected == deleteDLC) {
- if (!std::filesystem::exists(Common::FS::PathFromQString(dlc_path))) {
- QMessageBox::critical(nullptr, tr("Error"),
- QString(tr("This game has no DLC to delete!")));
- error = true;
- } else {
- folder_path = dlc_path;
- message_type = tr("DLC");
- }
- } else if (selected == deleteSaveData) {
- if (!std::filesystem::exists(Common::FS::PathFromQString(save_data_path))) {
- QMessageBox::critical(nullptr, tr("Error"),
- QString(tr("This game has no save data to delete!")));
- error = true;
- } else {
- folder_path = save_data_path;
- message_type = tr("Save Data");
- }
- } else if (selected == deleteTrophy) {
- if (!std::filesystem::exists(Common::FS::PathFromQString(trophy_data_path))) {
- QMessageBox::critical(
- nullptr, tr("Error"),
- QString(tr("This game has no saved trophies to delete!")));
- error = true;
- } else {
- folder_path = trophy_data_path;
- message_type = tr("Trophy");
- }
- }
- if (!error) {
- QString gameName = QString::fromStdString(m_games[itemID].name);
- QDir dir(folder_path);
- QMessageBox::StandardButton reply = QMessageBox::question(
- nullptr, QString(tr("Delete %1")).arg(message_type),
- QString(tr("Are you sure you want to delete %1's %2 directory?"))
- .arg(gameName, message_type),
- QMessageBox::Yes | QMessageBox::No);
- if (reply == QMessageBox::Yes) {
- dir.removeRecursively();
- if (selected == deleteGame) {
- widget->removeRow(itemID);
- m_games.removeAt(itemID);
- }
- }
- }
- }
-
- if (selected == updateCompatibility) {
- m_compat_info->UpdateCompatibilityDatabase(widget, true);
- }
-
- if (selected == viewCompatibilityReport) {
- if (m_games[itemID].compatibility.issue_number != "") {
- auto url_issues =
- "https://github.com/shadps4-compatibility/shadps4-game-compatibility/issues/";
- QDesktopServices::openUrl(
- QUrl(url_issues + m_games[itemID].compatibility.issue_number));
- }
- }
-
- if (selected == submitCompatibilityReport) {
- if (m_games[itemID].compatibility.issue_number == "") {
- QUrl url = QUrl("https://github.com/shadps4-compatibility/"
- "shadps4-game-compatibility/issues/new");
- QUrlQuery query;
- query.addQueryItem("template", QString("game_compatibility.yml"));
- query.addQueryItem(
- "title", QString("%1 - %2").arg(QString::fromStdString(m_games[itemID].serial),
- QString::fromStdString(m_games[itemID].name)));
- query.addQueryItem("game-name", QString::fromStdString(m_games[itemID].name));
- query.addQueryItem("game-serial", QString::fromStdString(m_games[itemID].serial));
- query.addQueryItem("game-version", QString::fromStdString(m_games[itemID].version));
- query.addQueryItem("emulator-version", QString(Common::g_version));
- url.setQuery(query);
-
- QDesktopServices::openUrl(url);
- } else {
- auto url_issues =
- "https://github.com/shadps4-compatibility/shadps4-game-compatibility/issues/";
- QDesktopServices::openUrl(
- QUrl(url_issues + m_games[itemID].compatibility.issue_number));
- }
- }
- return changedFavorite;
- }
-
- int GetRowIndex(QTreeWidget* treeWidget, QTreeWidgetItem* item) {
- int row = 0;
- for (int i = 0; i < treeWidget->topLevelItemCount(); i++) { // check top level/parent items
- QTreeWidgetItem* currentItem = treeWidget->topLevelItem(i);
- if (currentItem == item) {
- return row;
- }
- row++;
-
- if (currentItem->childCount() > 0) { // check child items
- for (int j = 0; j < currentItem->childCount(); j++) {
- QTreeWidgetItem* childItem = currentItem->child(j);
- if (childItem == item) {
- return row;
- }
- row++;
- }
- }
- }
- return -1;
- }
-
-private:
- bool convertPngToIco(const QString& pngFilePath, const QString& icoFilePath) {
- // Load the PNG image
- QImage image(pngFilePath);
- if (image.isNull()) {
- return false;
- }
-
- // Scale the image to the default icon size (256x256 pixels)
- QImage scaledImage =
- image.scaled(QSize(256, 256), Qt::KeepAspectRatio, Qt::SmoothTransformation);
-
- // Convert the image to QPixmap
- QPixmap pixmap = QPixmap::fromImage(scaledImage);
-
- // Save the pixmap as an ICO file
- if (pixmap.save(icoFilePath, "ICO")) {
- return true;
- } else {
- return false;
- }
- }
-
-#ifdef Q_OS_WIN
- bool createShortcutWin(const QString& linkPath, const QString& targetPath,
- const QString& iconPath, const QString& exePath) {
- CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED);
-
- // Create the ShellLink object
- Microsoft::WRL::ComPtr pShellLink;
- HRESULT hres = CoCreateInstance(CLSID_ShellLink, nullptr, CLSCTX_INPROC_SERVER,
- IID_PPV_ARGS(&pShellLink));
- if (SUCCEEDED(hres)) {
- // Defines the path to the program executable
- pShellLink->SetPath((LPCWSTR)exePath.utf16());
-
- // Sets the home directory ("Start in")
- pShellLink->SetWorkingDirectory((LPCWSTR)QFileInfo(exePath).absolutePath().utf16());
-
- // Set arguments, eboot.bin file location
- QString arguments = QString("-g \"%1\"").arg(targetPath);
- pShellLink->SetArguments((LPCWSTR)arguments.utf16());
-
- // Set the icon for the shortcut
- pShellLink->SetIconLocation((LPCWSTR)iconPath.utf16(), 0);
-
- // Save the shortcut
- Microsoft::WRL::ComPtr pPersistFile;
- hres = pShellLink.As(&pPersistFile);
- if (SUCCEEDED(hres)) {
- hres = pPersistFile->Save((LPCWSTR)linkPath.utf16(), TRUE);
- }
- }
-
- CoUninitialize();
-
- return SUCCEEDED(hres);
- }
-#else
- bool createShortcutLinux(const QString& linkPath, const std::string& name,
- const QString& targetPath, const QString& iconPath) {
- QFile shortcutFile(linkPath);
- if (!shortcutFile.open(QIODevice::WriteOnly | QIODevice::Text)) {
- QMessageBox::critical(nullptr, "Error",
- QString("Error creating shortcut!\n %1").arg(linkPath));
- return false;
- }
-
- QTextStream out(&shortcutFile);
- out << "[Desktop Entry]\n";
- out << "Version=1.0\n";
- out << "Name=" << QString::fromStdString(name) << "\n";
- out << "Exec=" << QCoreApplication::applicationFilePath() << " \"" << targetPath << "\"\n";
- out << "Icon=" << iconPath << "\n";
- out << "Terminal=false\n";
- out << "Type=Application\n";
- shortcutFile.close();
-
- return true;
- }
-#endif
-};
diff --git a/src/qt_gui/gui_settings.cpp b/src/qt_gui/gui_settings.cpp
deleted file mode 100644
index 4de6b7f19..000000000
--- a/src/qt_gui/gui_settings.cpp
+++ /dev/null
@@ -1,9 +0,0 @@
-// SPDX-FileCopyrightText: Copyright 2025 shadPS4 Emulator Project
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-#include "gui_settings.h"
-
-gui_settings::gui_settings(QObject* parent) : settings(parent) {
- m_settings = std::make_unique(ComputeSettingsDir() + "qt_ui.ini",
- QSettings::Format::IniFormat, parent);
-}
diff --git a/src/qt_gui/gui_settings.h b/src/qt_gui/gui_settings.h
deleted file mode 100644
index 4d2a1b7f7..000000000
--- a/src/qt_gui/gui_settings.h
+++ /dev/null
@@ -1,58 +0,0 @@
-// SPDX-FileCopyrightText: Copyright 2025 shadPS4 Emulator Project
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-#pragma once
-
-#include
-#include "settings.h"
-
-namespace gui {
-// categories
-const QString general_settings = "general_settings";
-const QString main_window = "main_window";
-const QString game_list = "game_list";
-const QString game_grid = "game_grid";
-const QString favorites = "favorites";
-
-// general
-const gui_value gen_checkForUpdates = gui_value(general_settings, "checkForUpdates", false);
-const gui_value gen_showChangeLog = gui_value(general_settings, "showChangeLog", false);
-const gui_value gen_updateChannel = gui_value(general_settings, "updateChannel", "Release");
-const gui_value gen_recentFiles =
- gui_value(main_window, "recentFiles", QVariant::fromValue(QList()));
-const gui_value gen_guiLanguage = gui_value(general_settings, "guiLanguage", "en_US");
-const gui_value gen_elfDirs =
- gui_value(main_window, "elfDirs", QVariant::fromValue(QList()));
-const gui_value gen_theme = gui_value(general_settings, "theme", 0);
-
-// main window settings
-const gui_value mw_geometry = gui_value(main_window, "geometry", QByteArray());
-const gui_value mw_showLabelsUnderIcons = gui_value(main_window, "showLabelsUnderIcons", true);
-
-// game list settings
-const gui_value gl_mode = gui_value(game_list, "tableMode", 0);
-const gui_value gl_icon_size = gui_value(game_list, "icon_size", 36);
-const gui_value gl_slider_pos = gui_value(game_list, "slider_pos", 0);
-const gui_value gl_showBackgroundImage = gui_value(game_list, "showBackgroundImage", true);
-const gui_value gl_backgroundImageOpacity = gui_value(game_list, "backgroundImageOpacity", 50);
-const gui_value gl_playBackgroundMusic = gui_value(game_list, "playBackgroundMusic", true);
-const gui_value gl_backgroundMusicVolume = gui_value(game_list, "backgroundMusicVolume", 50);
-const gui_value gl_VolumeSlider = gui_value(game_list, "volumeSlider", 100);
-
-// game grid settings
-const gui_value gg_icon_size = gui_value(game_grid, "icon_size", 69);
-const gui_value gg_slider_pos = gui_value(game_grid, "slider_pos", 0);
-
-// favorites list
-const gui_value favorites_list =
- gui_value(favorites, "favoritesList", QVariant::fromValue(QList()));
-
-} // namespace gui
-
-class gui_settings : public settings {
- Q_OBJECT
-
-public:
- explicit gui_settings(QObject* parent = nullptr);
- ~gui_settings() override = default;
-};
diff --git a/src/qt_gui/hotkeys.cpp b/src/qt_gui/hotkeys.cpp
deleted file mode 100644
index 5fbd1e6d5..000000000
--- a/src/qt_gui/hotkeys.cpp
+++ /dev/null
@@ -1,930 +0,0 @@
-// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-#include
-#include
-#include
-#include
-#include
-
-#include "common/config.h"
-#include "common/logging/log.h"
-#include "common/path_util.h"
-#include "hotkeys.h"
-#include "input/controller.h"
-#include "input/input_handler.h"
-#include "sdl_event_wrapper.h"
-#include "ui_hotkeys.h"
-
-Hotkeys::Hotkeys(bool isGameRunning, QWidget* parent)
- : QDialog(parent), GameRunning(isGameRunning), ui(new Ui::Hotkeys) {
-
- ui->setupUi(this);
-
- if (!GameRunning) {
- SDL_InitSubSystem(SDL_INIT_GAMEPAD);
- SDL_InitSubSystem(SDL_INIT_EVENTS);
- } else {
- SDL_SetHint(SDL_HINT_JOYSTICK_ALLOW_BACKGROUND_EVENTS, "1");
- }
-
- LoadHotkeys();
- CheckGamePad();
- installEventFilter(this);
-
- PadButtonsList = {ui->fpsButtonPad, ui->quitButtonPad, ui->fullscreenButtonPad,
- ui->pauseButtonPad, ui->reloadButtonPad};
-
- KBButtonsList = {ui->fpsButtonKB, ui->quitButtonKB, ui->fullscreenButtonKB,
- ui->pauseButtonKB, ui->reloadButtonKB, ui->renderdocButton,
- ui->mouseJoystickButton, ui->mouseGyroButton};
-
- connect(ui->buttonBox, &QDialogButtonBox::clicked, this, [this](QAbstractButton* button) {
- if (button == ui->buttonBox->button(QDialogButtonBox::Save)) {
- SaveHotkeys(true);
- } else if (button == ui->buttonBox->button(QDialogButtonBox::Apply)) {
- SaveHotkeys(false);
- } else if (button == ui->buttonBox->button(QDialogButtonBox::RestoreDefaults)) {
- SetDefault();
- } else if (button == ui->buttonBox->button(QDialogButtonBox::Cancel)) {
- QWidget::close();
- }
- });
-
- connect(ui->buttonBox, &QDialogButtonBox::rejected, this, &QWidget::close);
-
- ui->buttonBox->button(QDialogButtonBox::Save)->setText(tr("Save"));
- ui->buttonBox->button(QDialogButtonBox::Apply)->setText(tr("Apply"));
- ui->buttonBox->button(QDialogButtonBox::Cancel)->setText(tr("Cancel"));
- ui->buttonBox->button(QDialogButtonBox::RestoreDefaults)->setText(tr("Restore Defaults"));
-
- for (auto& button : PadButtonsList) {
- connect(button, &QPushButton::clicked, this,
- [this, &button]() { StartTimer(button, true); });
- }
-
- for (auto& button : KBButtonsList) {
- connect(button, &QPushButton::clicked, this,
- [this, &button]() { StartTimer(button, false); });
- }
-
- SdlEventWrapper::Wrapper::wrapperActive = true;
- QObject::connect(SdlEventWrapper::Wrapper::GetInstance(), &SdlEventWrapper::Wrapper::SDLEvent,
- this, &Hotkeys::processSDLEvents);
-
- if (!GameRunning) {
- Polling = QtConcurrent::run(&Hotkeys::pollSDLEvents, this);
- }
-}
-
-void Hotkeys::DisableMappingButtons() {
- for (auto& i : PadButtonsList) {
- i->setEnabled(false);
- }
-
- for (auto& i : KBButtonsList) {
- i->setEnabled(false);
- }
-
- ui->buttonBox->button(QDialogButtonBox::Save)->setEnabled(false);
- ui->buttonBox->button(QDialogButtonBox::Apply)->setEnabled(false);
-}
-
-void Hotkeys::EnableMappingButtons() {
- for (auto& i : PadButtonsList) {
- i->setEnabled(true);
- }
-
- for (auto& i : KBButtonsList) {
- i->setEnabled(true);
- }
-
- ui->buttonBox->button(QDialogButtonBox::Save)->setEnabled(true);
- ui->buttonBox->button(QDialogButtonBox::Apply)->setEnabled(true);
-}
-
-void Hotkeys::SetDefault() {
-
- PadButtonsList = {ui->fpsButtonPad, ui->quitButtonPad, ui->fullscreenButtonPad,
- ui->pauseButtonPad, ui->reloadButtonPad};
-
- KBButtonsList = {ui->fpsButtonKB, ui->quitButtonKB, ui->fullscreenButtonKB,
- ui->pauseButtonKB, ui->reloadButtonKB, ui->renderdocButton,
- ui->mouseJoystickButton, ui->mouseGyroButton};
-
- ui->fpsButtonPad->setText("unmapped");
- ui->quitButtonPad->setText("unmapped");
- ui->fullscreenButtonPad->setText("unmapped");
- ui->pauseButtonPad->setText("unmapped");
- ui->reloadButtonPad->setText("unmapped");
-
- ui->fpsButtonKB->setText("f10");
- ui->quitButtonKB->setText("lctrl, lshift, end");
- ui->fullscreenButtonKB->setText("f11");
- ui->pauseButtonKB->setText("f9");
- ui->reloadButtonKB->setText("f8");
-
- ui->renderdocButton->setText("f12");
- ui->mouseJoystickButton->setText("f7");
- ui->mouseGyroButton->setText("f6");
-}
-
-void Hotkeys::SaveHotkeys(bool CloseOnSave) {
- std::vector lines, inputs;
-
- auto add_mapping = [&](const QString& buttonText, const std::string& output_name) {
- if (buttonText.toStdString() != "unmapped") {
- lines.push_back(output_name + " = " + buttonText.toStdString());
- inputs.push_back(buttonText.toStdString());
- }
- };
-
- lines.push_back("# Anything put here will be loaded for all games,");
- lines.push_back("# alongside the game's config or default.ini depending on your preference.");
- lines.push_back("");
-
- add_mapping(ui->fullscreenButtonPad->text(), "hotkey_fullscreen");
- add_mapping(ui->fullscreenButtonKB->text(), "hotkey_fullscreen");
- lines.push_back("");
-
- add_mapping(ui->pauseButtonPad->text(), "hotkey_pause");
- add_mapping(ui->pauseButtonKB->text(), "hotkey_pause");
- lines.push_back("");
-
- add_mapping(ui->fpsButtonPad->text(), "hotkey_show_fps");
- add_mapping(ui->fpsButtonKB->text(), "hotkey_show_fps");
- lines.push_back("");
-
- add_mapping(ui->quitButtonPad->text(), "hotkey_quit");
- add_mapping(ui->quitButtonKB->text(), "hotkey_quit");
- lines.push_back("");
-
- add_mapping(ui->reloadButtonPad->text(), "hotkey_reload_inputs");
- add_mapping(ui->reloadButtonKB->text(), "hotkey_reload_inputs");
- lines.push_back("");
-
- add_mapping(ui->renderdocButton->text(), "hotkey_renderdoc_capture");
- add_mapping(ui->mouseJoystickButton->text(), "hotkey_toggle_mouse_to_joystick");
- add_mapping(ui->mouseGyroButton->text(), "hotkey_toggle_mouse_to_gyro");
-
- auto hotkey_file = Config::GetFoolproofInputConfigFile("global");
- std::fstream file(hotkey_file);
- int lineCount = 0;
- std::string line;
- while (std::getline(file, line)) {
- lineCount++;
-
- std::size_t comment_pos = line.find('#');
- if (comment_pos != std::string::npos) {
- if (!line.contains("Anything put here will be loaded for all games") &&
- !line.contains("alongside the game's config or default.ini depending on your"))
- lines.push_back(line);
- continue;
- }
-
- std::size_t equal_pos = line.find('=');
- if (equal_pos == std::string::npos) {
- lines.push_back(line);
- continue;
- }
-
- if (!line.contains("hotkey")) {
- lines.push_back(line);
- }
- }
- file.close();
-
- // Prevent duplicate inputs that break the input engine
- bool duplicateFound = false;
- QSet duplicateMappings;
-
- for (auto it = inputs.begin(); it != inputs.end(); ++it) {
- if (std::find(it + 1, inputs.end(), *it) != inputs.end()) {
- duplicateFound = true;
- duplicateMappings.insert(QString::fromStdString(*it));
- }
- }
-
- if (duplicateFound) {
- QStringList duplicatesList;
- for (const QString& mapping : duplicateMappings) {
- for (auto& button : PadButtonsList) {
- if (button->text() == mapping)
- duplicatesList.append(button->objectName() + " - " + mapping);
- }
-
- for (auto& button : KBButtonsList) {
- if (button->text() == mapping)
- duplicatesList.append(button->objectName() + " - " + mapping);
- }
- }
- QMessageBox::information(
- this, tr("Unable to Save"),
- // clang-format off
- QString(tr("Cannot bind any unique input more than once. Duplicate inputs mapped to the following buttons:\n\n%1").arg(duplicatesList.join("\n"))));
- // clang-format on
- return;
- }
-
- std::vector save;
- bool CurrentLineEmpty = false, LastLineEmpty = false;
- for (auto const& line : lines) {
- LastLineEmpty = CurrentLineEmpty ? true : false;
- CurrentLineEmpty = line.empty() ? true : false;
- if (!CurrentLineEmpty || !LastLineEmpty)
- save.push_back(line);
- }
-
- std::ofstream output_file(hotkey_file);
- for (auto const& line : save) {
- output_file << line << '\n';
- }
- output_file.close();
-
- // this also parses global hotkeys
- if (GameRunning)
- Input::ParseInputConfig("default");
-
- if (CloseOnSave)
- QWidget::close();
-}
-
-void Hotkeys::LoadHotkeys() {
- auto hotkey_file = Config::GetFoolproofInputConfigFile("global");
- std::ifstream file(hotkey_file);
- int lineCount = 0;
- std::string line = "";
-
- while (std::getline(file, line)) {
- lineCount++;
-
- std::size_t equal_pos = line.find('=');
- if (equal_pos == std::string::npos)
- continue;
-
- std::string output_string = line.substr(0, equal_pos);
- std::string input_string = line.substr(equal_pos + 2);
-
- bool controllerInputDetected = false;
- for (const std::string& input : ControllerInputs) {
- // Needed to avoid detecting backspace while detecting back
- if (input_string.contains(input) && !input_string.contains("backspace")) {
- controllerInputDetected = true;
- break;
- }
- }
-
- if (output_string.contains("hotkey_fullscreen")) {
- controllerInputDetected
- ? ui->fullscreenButtonPad->setText(QString::fromStdString(input_string))
- : ui->fullscreenButtonKB->setText(QString::fromStdString(input_string));
- } else if (output_string.contains("hotkey_show_fps")) {
- controllerInputDetected
- ? ui->fpsButtonPad->setText(QString::fromStdString(input_string))
- : ui->fpsButtonKB->setText(QString::fromStdString(input_string));
- } else if (output_string.contains("hotkey_pause")) {
- controllerInputDetected
- ? ui->pauseButtonPad->setText(QString::fromStdString(input_string))
- : ui->pauseButtonKB->setText(QString::fromStdString(input_string));
- } else if (output_string.contains("hotkey_quit")) {
- controllerInputDetected
- ? ui->quitButtonPad->setText(QString::fromStdString(input_string))
- : ui->quitButtonKB->setText(QString::fromStdString(input_string));
- } else if (output_string.contains("hotkey_reload_inputs")) {
- controllerInputDetected
- ? ui->reloadButtonPad->setText(QString::fromStdString(input_string))
- : ui->reloadButtonKB->setText(QString::fromStdString(input_string));
- } else if (output_string.contains("hotkey_renderdoc_capture")) {
- ui->renderdocButton->setText(QString::fromStdString(input_string));
- } else if (output_string.contains("hotkey_toggle_mouse_to_joystick")) {
- ui->mouseJoystickButton->setText(QString::fromStdString(input_string));
- } else if (output_string.contains("hotkey_toggle_mouse_to_gyro")) {
- ui->mouseGyroButton->setText(QString::fromStdString(input_string));
- }
- }
-
- file.close();
-}
-
-void Hotkeys::CheckGamePad() {
- if (h_gamepad) {
- SDL_CloseGamepad(h_gamepad);
- h_gamepad = nullptr;
- }
-
- h_gamepads = SDL_GetGamepads(&gamepad_count);
-
- if (!h_gamepads) {
- LOG_ERROR(Input, "Cannot get gamepad list: {}", SDL_GetError());
- return;
- }
-
- int defaultIndex = GamepadSelect::GetIndexfromGUID(h_gamepads, gamepad_count,
- Config::getDefaultControllerID());
- int activeIndex = GamepadSelect::GetIndexfromGUID(h_gamepads, gamepad_count,
- GamepadSelect::GetSelectedGamepad());
-
- if (!GameRunning) {
- if (activeIndex != -1) {
- h_gamepad = SDL_OpenGamepad(h_gamepads[activeIndex]);
- } else if (defaultIndex != -1) {
- h_gamepad = SDL_OpenGamepad(h_gamepads[defaultIndex]);
- } else {
- LOG_INFO(Input, "Got {} gamepads. Opening the first one.", gamepad_count);
- h_gamepad = SDL_OpenGamepad(h_gamepads[0]);
- }
-
- if (!h_gamepad) {
- LOG_ERROR(Input, "Failed to open gamepad: {}", SDL_GetError());
- }
- }
-}
-
-void Hotkeys::StartTimer(QPushButton*& button, bool isButton) {
- MappingTimer = 3;
- MappingCompleted = false;
- mapping = button->text();
- DisableMappingButtons();
-
- isButton ? EnablePadMapping = true : EnableKBMapping = true;
- button->setText(tr("Waiting for inputs") + " [" + QString::number(MappingTimer) + "]");
-
- timer = new QTimer(this);
- MappingButton = button;
- timer->start(1000);
- connect(timer, &QTimer::timeout, this, [this]() { CheckMapping(MappingButton); });
-}
-
-void Hotkeys::CheckMapping(QPushButton*& button) {
- MappingTimer -= 1;
- button->setText(tr("Waiting for inputs") + " [" + QString::number(MappingTimer) + "]");
-
- if (pressedButtons.size() > 0) {
- QStringList keyStrings;
-
- for (QString& buttonAction : pressedButtons) {
- keyStrings << buttonAction;
- }
-
- QString combo = keyStrings.join(", ");
- SetMapping(combo);
- MappingButton->setText(combo);
- pressedButtons.clear();
- }
-
- if (MappingCompleted || MappingTimer <= 0) {
- button->setText(mapping);
- EnablePadMapping = false;
- EnableKBMapping = false;
- L2Pressed = false;
- R2Pressed = false;
- EnableMappingButtons();
- timer->stop();
- }
-}
-
-void Hotkeys::SetMapping(QString input) {
- mapping = input;
- MappingCompleted = true;
-}
-
-// use QT events instead of SDL for KB since SDL doesn't allow background KB events
-bool Hotkeys::eventFilter(QObject* obj, QEvent* event) {
- if (event->type() == QEvent::KeyPress) {
- QKeyEvent* keyEvent = static_cast(event);
- if (keyEvent->key() == Qt::Key_Escape) {
- if (EnableKBMapping || EnablePadMapping) {
- SetMapping("unmapped");
- CheckMapping(MappingButton);
- }
- return true;
- } else {
- if (EnableKBMapping) {
-
- if (pressedButtons.size() >= 3) {
- return true;
- }
-
- switch (keyEvent->key()) {
- // modifiers
- case Qt::Key_Shift:
- if (keyEvent->nativeScanCode() == LSHIFT_KEY) {
- pressedButtons.insert(1, "lshift");
- } else {
- pressedButtons.insert(2, "rshift");
- }
- break;
- case Qt::Key_Alt:
- if (keyEvent->nativeScanCode() == LALT_KEY) {
- pressedButtons.insert(3, "lalt");
- } else {
- pressedButtons.insert(4, "ralt");
- }
- break;
- case Qt::Key_Control:
- if (keyEvent->nativeScanCode() == LCTRL_KEY) {
- pressedButtons.insert(5, "lctrl");
- } else {
- pressedButtons.insert(6, "rctrl");
- }
- break;
- case Qt::Key_Meta:
-#ifdef _WIN32
- pressedButtons.insert(7, "lwin");
-#else
- pressedButtons.insert(7, "lmeta");
-#endif
- break;
-
- // f keys
-
- case Qt::Key_F1:
- pressedButtons.insert(8, "f1");
- break;
- case Qt::Key_F2:
- pressedButtons.insert(9, "f2");
- break;
- case Qt::Key_F3:
- pressedButtons.insert(10, "f3");
- break;
- case Qt::Key_F4:
- pressedButtons.insert(11, "f4");
- break;
- case Qt::Key_F5:
- pressedButtons.insert(12, "f5");
- break;
- case Qt::Key_F6:
- pressedButtons.insert(13, "f6");
- break;
- case Qt::Key_F7:
- pressedButtons.insert(14, "f7");
- break;
- case Qt::Key_F8:
- pressedButtons.insert(15, "f8");
- break;
- case Qt::Key_F9:
- pressedButtons.insert(16, "f9");
- break;
- case Qt::Key_F10:
- pressedButtons.insert(17, "f10");
- break;
- case Qt::Key_F11:
- pressedButtons.insert(18, "f11");
- break;
- case Qt::Key_F12:
- pressedButtons.insert(19, "f12");
- break;
-
- // alphanumeric
- case Qt::Key_A:
- pressedButtons.insert(20, "a");
- break;
- case Qt::Key_B:
- pressedButtons.insert(21, "b");
- break;
- case Qt::Key_C:
- pressedButtons.insert(22, "c");
- break;
- case Qt::Key_D:
- pressedButtons.insert(23, "d");
- break;
- case Qt::Key_E:
- pressedButtons.insert(24, "e");
- break;
- case Qt::Key_F:
- pressedButtons.insert(25, "f");
- break;
- case Qt::Key_G:
- pressedButtons.insert(26, "g");
- break;
- case Qt::Key_H:
- pressedButtons.insert(27, "h");
- break;
- case Qt::Key_I:
- pressedButtons.insert(28, "i");
- break;
- case Qt::Key_J:
- pressedButtons.insert(29, "j");
- break;
- case Qt::Key_K:
- pressedButtons.insert(30, "k");
- break;
- case Qt::Key_L:
- pressedButtons.insert(31, "l");
- break;
- case Qt::Key_M:
- pressedButtons.insert(32, "m");
- break;
- case Qt::Key_N:
- pressedButtons.insert(33, "n");
- break;
- case Qt::Key_O:
- pressedButtons.insert(34, "o");
- break;
- case Qt::Key_P:
- pressedButtons.insert(35, "p");
- break;
- case Qt::Key_Q:
- pressedButtons.insert(36, "q");
- break;
- case Qt::Key_R:
- pressedButtons.insert(37, "r");
- break;
- case Qt::Key_S:
- pressedButtons.insert(38, "s");
- break;
- case Qt::Key_T:
- pressedButtons.insert(39, "t");
- break;
- case Qt::Key_U:
- pressedButtons.insert(40, "u");
- break;
- case Qt::Key_V:
- pressedButtons.insert(41, "v");
- break;
- case Qt::Key_W:
- pressedButtons.insert(42, "w");
- break;
- case Qt::Key_X:
- pressedButtons.insert(43, "x");
- break;
- case Qt::Key_Y:
- pressedButtons.insert(44, "y");
- break;
- case Qt::Key_Z:
- pressedButtons.insert(45, "z");
- break;
- case Qt::Key_0:
- QApplication::keyboardModifiers() & Qt::KeypadModifier
- ? pressedButtons.insert(46, "kp0")
- : pressedButtons.insert(56, "0");
- break;
- case Qt::Key_1:
- QApplication::keyboardModifiers() & Qt::KeypadModifier
- ? pressedButtons.insert(47, "kp1")
- : pressedButtons.insert(57, "1");
- break;
- case Qt::Key_2:
- QApplication::keyboardModifiers() & Qt::KeypadModifier
- ? pressedButtons.insert(48, "kp2")
- : pressedButtons.insert(58, "2");
- break;
- case Qt::Key_3:
- QApplication::keyboardModifiers() & Qt::KeypadModifier
- ? pressedButtons.insert(49, "kp3")
- : pressedButtons.insert(59, "3");
- break;
- case Qt::Key_4:
- QApplication::keyboardModifiers() & Qt::KeypadModifier
- ? pressedButtons.insert(50, "kp4")
- : pressedButtons.insert(60, "4");
- break;
- case Qt::Key_5:
- QApplication::keyboardModifiers() & Qt::KeypadModifier
- ? pressedButtons.insert(51, "kp5")
- : pressedButtons.insert(61, "5");
- break;
- case Qt::Key_6:
- QApplication::keyboardModifiers() & Qt::KeypadModifier
- ? pressedButtons.insert(52, "kp6")
- : pressedButtons.insert(62, "6");
- break;
- case Qt::Key_7:
- QApplication::keyboardModifiers() & Qt::KeypadModifier
- ? pressedButtons.insert(53, "kp7")
- : pressedButtons.insert(63, "7");
- break;
- case Qt::Key_8:
- QApplication::keyboardModifiers() & Qt::KeypadModifier
- ? pressedButtons.insert(54, "kp8")
- : pressedButtons.insert(64, "8");
- break;
- case Qt::Key_9:
- QApplication::keyboardModifiers() & Qt::KeypadModifier
- ? pressedButtons.insert(55, "kp9")
- : pressedButtons.insert(65, "9");
- break;
-
- // symbols
- case Qt::Key_Asterisk:
- QApplication::keyboardModifiers() & Qt::KeypadModifier
- ? pressedButtons.insert(66, "kpasterisk")
- : pressedButtons.insert(74, "asterisk");
- break;
- case Qt::Key_Minus:
- QApplication::keyboardModifiers() & Qt::KeypadModifier
- ? pressedButtons.insert(67, "kpminus")
- : pressedButtons.insert(75, "minus");
- break;
- case Qt::Key_Equal:
- QApplication::keyboardModifiers() & Qt::KeypadModifier
- ? pressedButtons.insert(68, "kpequals")
- : pressedButtons.insert(76, "equals");
- break;
- case Qt::Key_Plus:
- QApplication::keyboardModifiers() & Qt::KeypadModifier
- ? pressedButtons.insert(69, "kpplus")
- : pressedButtons.insert(77, "plus");
- break;
- case Qt::Key_Slash:
- QApplication::keyboardModifiers() & Qt::KeypadModifier
- ? pressedButtons.insert(70, "kpslash")
- : pressedButtons.insert(78, "slash");
- break;
- case Qt::Key_Comma:
- QApplication::keyboardModifiers() & Qt::KeypadModifier
- ? pressedButtons.insert(71, "kpcomma")
- : pressedButtons.insert(79, "comma");
- break;
- case Qt::Key_Period:
- QApplication::keyboardModifiers() & Qt::KeypadModifier
- ? pressedButtons.insert(72, "kpperiod")
- : pressedButtons.insert(80, "period");
- break;
- case Qt::Key_Enter:
- QApplication::keyboardModifiers() & Qt::KeypadModifier
- ? pressedButtons.insert(73, "kpenter")
- : pressedButtons.insert(81, "enter");
- break;
- case Qt::Key_QuoteLeft:
- pressedButtons.insert(82, "grave");
- break;
- case Qt::Key_AsciiTilde:
- pressedButtons.insert(83, "tilde");
- break;
- case Qt::Key_Exclam:
- pressedButtons.insert(84, "exclamation");
- break;
- case Qt::Key_At:
- pressedButtons.insert(85, "at");
- break;
- case Qt::Key_NumberSign:
- pressedButtons.insert(86, "hash");
- break;
- case Qt::Key_Dollar:
- pressedButtons.insert(87, "dollar");
- break;
- case Qt::Key_Percent:
- pressedButtons.insert(88, "percent");
- break;
- case Qt::Key_AsciiCircum:
- pressedButtons.insert(89, "caret");
- break;
- case Qt::Key_Ampersand:
- pressedButtons.insert(90, "ampersand");
- break;
- case Qt::Key_ParenLeft:
- pressedButtons.insert(91, "lparen");
- break;
- case Qt::Key_ParenRight:
- pressedButtons.insert(92, "rparen");
- break;
- case Qt::Key_BracketLeft:
- pressedButtons.insert(93, "lbracket");
- break;
- case Qt::Key_BracketRight:
- pressedButtons.insert(94, "rbracket");
- break;
- case Qt::Key_BraceLeft:
- pressedButtons.insert(95, "lbrace");
- break;
- case Qt::Key_BraceRight:
- pressedButtons.insert(96, "rbrace");
- break;
- case Qt::Key_Underscore:
- pressedButtons.insert(97, "underscore");
- break;
- case Qt::Key_Backslash:
- pressedButtons.insert(98, "backslash");
- break;
- case Qt::Key_Bar:
- pressedButtons.insert(99, "pipe");
- break;
- case Qt::Key_Semicolon:
- pressedButtons.insert(100, "semicolon");
- break;
- case Qt::Key_Colon:
- pressedButtons.insert(101, "colon");
- break;
- case Qt::Key_Apostrophe:
- pressedButtons.insert(102, "apostrophe");
- break;
- case Qt::Key_QuoteDbl:
- pressedButtons.insert(103, "quote");
- break;
- case Qt::Key_Less:
- pressedButtons.insert(104, "less");
- break;
- case Qt::Key_Greater:
- pressedButtons.insert(105, "greater");
- break;
- case Qt::Key_Question:
- pressedButtons.insert(106, "question");
- break;
-
- // special keys
- case Qt::Key_Print:
- pressedButtons.insert(107, "printscreen");
- break;
- case Qt::Key_ScrollLock:
- pressedButtons.insert(108, "scrolllock");
- break;
- case Qt::Key_Pause:
- pressedButtons.insert(109, "pausebreak");
- break;
- case Qt::Key_Backspace:
- pressedButtons.insert(110, "backspace");
- break;
- case Qt::Key_Insert:
- pressedButtons.insert(111, "insert");
- break;
- case Qt::Key_Delete:
- pressedButtons.insert(112, "delete");
- break;
- case Qt::Key_Home:
- pressedButtons.insert(113, "home");
- break;
- case Qt::Key_End:
- pressedButtons.insert(114, "end");
- break;
- case Qt::Key_PageUp:
- pressedButtons.insert(115, "pgup");
- break;
- case Qt::Key_PageDown:
- pressedButtons.insert(116, "pgdown");
- break;
- case Qt::Key_Tab:
- pressedButtons.insert(117, "tab");
- break;
- case Qt::Key_CapsLock:
- pressedButtons.insert(118, "capslock");
- break;
- case Qt::Key_Return:
- pressedButtons.insert(119, "enter");
- break;
- case Qt::Key_Space:
- pressedButtons.insert(120, "space");
- break;
- case Qt::Key_Up:
- pressedButtons.insert(121, "up");
- break;
- case Qt::Key_Down:
- pressedButtons.insert(122, "down");
- break;
- case Qt::Key_Left:
- pressedButtons.insert(123, "left");
- break;
- case Qt::Key_Right:
- pressedButtons.insert(124, "right");
- break;
- default:
- break;
- }
- return true;
- }
- }
- }
-
- if (event->type() == QEvent::KeyPress && EnableKBMapping) {
- CheckMapping(MappingButton);
- return true;
- }
-
- return QDialog::eventFilter(obj, event);
-}
-
-void Hotkeys::processSDLEvents(int Type, int Input, int Value) {
- if (EnablePadMapping) {
-
- if (pressedButtons.size() >= 3) {
- return;
- }
-
- if (Type == SDL_EVENT_GAMEPAD_BUTTON_DOWN) {
- switch (Input) {
- case SDL_GAMEPAD_BUTTON_SOUTH:
- pressedButtons.insert(5, "cross");
- break;
- case SDL_GAMEPAD_BUTTON_EAST:
- pressedButtons.insert(6, "circle");
- break;
- case SDL_GAMEPAD_BUTTON_NORTH:
- pressedButtons.insert(7, "triangle");
- break;
- case SDL_GAMEPAD_BUTTON_WEST:
- pressedButtons.insert(8, "square");
- break;
- case SDL_GAMEPAD_BUTTON_LEFT_SHOULDER:
- pressedButtons.insert(3, "l1");
- break;
- case SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER:
- pressedButtons.insert(4, "r1");
- break;
- case SDL_GAMEPAD_BUTTON_LEFT_STICK:
- pressedButtons.insert(9, "l3");
- break;
- case SDL_GAMEPAD_BUTTON_RIGHT_STICK:
- pressedButtons.insert(10, "r3");
- break;
- case SDL_GAMEPAD_BUTTON_DPAD_UP:
- pressedButtons.insert(13, "pad_up");
- break;
- case SDL_GAMEPAD_BUTTON_DPAD_DOWN:
- pressedButtons.insert(14, "pad_down");
- break;
- case SDL_GAMEPAD_BUTTON_DPAD_LEFT:
- pressedButtons.insert(15, "pad_left");
- break;
- case SDL_GAMEPAD_BUTTON_DPAD_RIGHT:
- pressedButtons.insert(16, "pad_right");
- break;
- case SDL_GAMEPAD_BUTTON_BACK:
- pressedButtons.insert(11, "back");
- break;
- case SDL_GAMEPAD_BUTTON_START:
- pressedButtons.insert(12, "options");
- break;
- default:
- break;
- }
- }
-
- if (Type == SDL_EVENT_GAMEPAD_AXIS_MOTION) {
- // SDL trigger axis values range from 0 to 32000, set mapping on half movement
- // Set zone for trigger release signal arbitrarily at 5000
- switch (Input) {
- case SDL_GAMEPAD_AXIS_LEFT_TRIGGER:
- if (Value > 16000) {
- pressedButtons.insert(1, "l2");
- L2Pressed = true;
- } else if (Value < 5000) {
- if (L2Pressed && !R2Pressed)
- CheckMapping(MappingButton);
- }
- break;
- case SDL_GAMEPAD_AXIS_RIGHT_TRIGGER:
- if (Value > 16000) {
- pressedButtons.insert(2, "r2");
- R2Pressed = true;
- } else if (Value < 5000) {
- if (R2Pressed && !L2Pressed)
- CheckMapping(MappingButton);
- }
- break;
- default:
- break;
- }
- }
-
- if (Type == SDL_EVENT_GAMEPAD_BUTTON_UP)
- CheckMapping(MappingButton);
- }
-
- if (Type == SDL_EVENT_GAMEPAD_ADDED || SDL_EVENT_GAMEPAD_REMOVED) {
- CheckGamePad();
- }
-}
-
-void Hotkeys::pollSDLEvents() {
- SDL_Event event;
- while (SdlEventWrapper::Wrapper::wrapperActive) {
-
- if (!SDL_WaitEvent(&event)) {
- return;
- }
-
- if (event.type == SDL_EVENT_QUIT) {
- return;
- }
-
- SdlEventWrapper::Wrapper::GetInstance()->Wrapper::ProcessEvent(&event);
- }
-}
-
-void Hotkeys::Cleanup() {
- SdlEventWrapper::Wrapper::wrapperActive = false;
- if (h_gamepad) {
- SDL_CloseGamepad(h_gamepad);
- h_gamepad = nullptr;
- }
-
- if (h_gamepads)
- SDL_free(h_gamepads);
-
- if (!GameRunning) {
- SDL_Event quitLoop{};
- quitLoop.type = SDL_EVENT_QUIT;
- SDL_PushEvent(&quitLoop);
- Polling.waitForFinished();
-
- SDL_QuitSubSystem(SDL_INIT_GAMEPAD);
- SDL_QuitSubSystem(SDL_INIT_EVENTS);
- SDL_Quit();
- } else {
- if (!Config::getBackgroundControllerInput()) {
- SDL_SetHint(SDL_HINT_JOYSTICK_ALLOW_BACKGROUND_EVENTS, "0");
- }
- }
-}
-
-Hotkeys::~Hotkeys() {}
diff --git a/src/qt_gui/hotkeys.h b/src/qt_gui/hotkeys.h
deleted file mode 100644
index 23f8f0261..000000000
--- a/src/qt_gui/hotkeys.h
+++ /dev/null
@@ -1,88 +0,0 @@
-// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-#include
-#include
-#include
-#include
-
-#ifdef _WIN32
-#define LCTRL_KEY 29
-#define LALT_KEY 56
-#define LSHIFT_KEY 42
-#else
-#define LCTRL_KEY 37
-#define LALT_KEY 64
-#define LSHIFT_KEY 50
-#endif
-
-namespace Ui {
-class Hotkeys;
-}
-
-class Hotkeys : public QDialog {
- Q_OBJECT
-
-public:
- explicit Hotkeys(bool GameRunning, QWidget* parent = nullptr);
- ~Hotkeys();
-
-private Q_SLOTS:
- void processSDLEvents(int Type, int Input, int Value);
- void StartTimer(QPushButton*& button, bool isPad);
- void SaveHotkeys(bool CloseOnSave);
- void SetDefault();
-
-private:
- bool eventFilter(QObject* obj, QEvent* event) override;
- void CheckMapping(QPushButton*& button);
- void DisableMappingButtons();
- void EnableMappingButtons();
- void LoadHotkeys();
- void pollSDLEvents();
- void CheckGamePad();
- void SetMapping(QString input);
- void Cleanup();
-
- bool GameRunning;
- bool EnablePadMapping = false;
- bool EnableKBMapping = false;
- bool MappingCompleted = false;
- bool L2Pressed = false;
- bool R2Pressed = false;
- int MappingTimer;
- int gamepad_count;
- QString mapping;
- QTimer* timer;
- QPushButton* MappingButton;
- SDL_Gamepad* h_gamepad = nullptr;
- SDL_JoystickID* h_gamepads;
-
- // use QMap instead of QSet to maintain order of inserted strings
- QMap pressedButtons;
- QList PadButtonsList;
- QList KBButtonsList;
- QFuture Polling;
-
- Ui::Hotkeys* ui;
-
- const std::vector ControllerInputs = {
- "cross", "circle", "square", "triangle", "l1",
- "r1", "l2", "r2", "l3",
-
- "r3", "options", "pad_up",
-
- "pad_down",
-
- "pad_left", "pad_right", "axis_left_x", "axis_left_y", "axis_right_x",
- "axis_right_y", "back"};
-
-protected:
- void closeEvent(QCloseEvent* event) override {
- Cleanup();
- }
-
- void accept() override {
- // Blank override to prevent quitting when save button pressed
- }
-};
diff --git a/src/qt_gui/hotkeys.ui b/src/qt_gui/hotkeys.ui
deleted file mode 100644
index 7b4d01be4..000000000
--- a/src/qt_gui/hotkeys.ui
+++ /dev/null
@@ -1,549 +0,0 @@
-
-
-
- Hotkeys
-
-
- Qt::WindowModality::WindowModal
-
-
-
- 0
- 0
- 987
- 549
-
-
-
- Customize Hotkeys
-
-
-
-
- 3
- 2
- 981
- 541
-
-
-
- true
-
-
-
-
- 0
- 0
- 979
- 539
-
-
-
-
-
- 3
- 7
- 971
- 532
-
-
-
- -
-
-
-
- 0
- 0
-
-
-
-
- 19
- true
-
-
-
- Controller Hotkeys
-
-
- Qt::AlignmentFlag::AlignCenter
-
-
-
- -
-
-
-
-
-
-
- 0
- 0
-
-
-
- Show FPS Counter
-
-
-
-
-
-
- Qt::FocusPolicy::NoFocus
-
-
- unmapped
-
-
-
-
-
-
- -
-
-
-
- 0
- 0
-
-
-
- Quit Emulation
-
-
-
-
-
-
- Qt::FocusPolicy::NoFocus
-
-
- unmapped
-
-
-
-
-
-
- -
-
-
-
- 0
- 0
-
-
-
- Reload Button Mappings
-
-
-
-
-
-
- unmapped
-
-
-
-
-
-
-
-
- -
-
-
-
-
-
-
- 0
- 0
-
-
-
- Toggle Fullscreen
-
-
-
-
-
-
- Qt::FocusPolicy::NoFocus
-
-
- unmapped
-
-
-
-
-
-
- -
-
-
-
- 0
- 0
-
-
-
- Toggle Pause
-
-
-
-
-
-
- Qt::FocusPolicy::NoFocus
-
-
- unmapped
-
-
-
-
-
-
- -
-
-
- Qt::Orientation::Horizontal
-
-
-
- 40
- 20
-
-
-
-
-
-
- -
-
-
-
- 0
- 0
-
-
-
-
- 19
- true
-
-
-
- Keyboard Hotkeys
-
-
- Qt::AlignmentFlag::AlignCenter
-
-
-
- -
-
-
-
-
-
-
- 0
- 0
-
-
-
- Show FPS Counter
-
-
-
-
-
-
- Qt::FocusPolicy::NoFocus
-
-
- unmapped
-
-
-
-
-
-
- -
-
-
-
- 0
- 0
-
-
-
- Quit Emulation
-
-
-
-
-
-
- Qt::FocusPolicy::NoFocus
-
-
- unmapped
-
-
-
-
-
-
- -
-
-
-
- 0
- 0
-
-
-
- Reload Button Mappings
-
-
-
-
-
-
- Qt::FocusPolicy::NoFocus
-
-
- unmapped
-
-
-
-
-
-
-
-
- -
-
-
-
-
-
-
- 0
- 0
-
-
-
- Toggle Fullscreen
-
-
-
-
-
-
- Qt::FocusPolicy::NoFocus
-
-
- unmapped
-
-
-
-
-
-
- -
-
-
-
- 0
- 0
-
-
-
- Toggle Pause
-
-
-
-
-
-
- Qt::FocusPolicy::NoFocus
-
-
- unmapped
-
-
-
-
-
-
- -
-
-
-
- 0
- 0
-
-
-
- Renderdoc Capture (for debugging only)
-
-
-
-
-
-
- Qt::FocusPolicy::NoFocus
-
-
- unmapped
-
-
-
-
-
-
-
-
- -
-
-
-
-
-
-
- 0
- 0
-
-
-
- Toggle Mouse to Joystick Emulation
-
-
-
-
-
-
- Qt::FocusPolicy::NoFocus
-
-
- unmapped
-
-
-
-
-
-
- -
-
-
-
- 0
- 0
-
-
-
- Toggle Mouse to Gyro Emulation
-
-
-
-
-
-
- Qt::FocusPolicy::NoFocus
-
-
- unmapped
-
-
-
-
-
-
- -
-
-
- Qt::Orientation::Horizontal
-
-
-
- 40
- 20
-
-
-
-
-
-
- -
-
-
-
- 0
- 0
-
-
-
-
- 12
- true
-
-
-
- Tip: Up to three simultaneous inputs can be assigned for each hotkey
-
-
- Qt::AlignmentFlag::AlignCenter
-
-
-
- -
-
-
- Qt::Orientation::Horizontal
-
-
-
- 40
- 20
-
-
-
-
- -
-
-
- Qt::Orientation::Horizontal
-
-
- QDialogButtonBox::StandardButton::Apply|QDialogButtonBox::StandardButton::Cancel|QDialogButtonBox::StandardButton::RestoreDefaults|QDialogButtonBox::StandardButton::Save
-
-
-
-
-
-
-
-
-
-
-
- buttonBox
- accepted()
- Hotkeys
- accept()
-
-
- 248
- 254
-
-
- 157
- 274
-
-
-
-
- buttonBox
- rejected()
- Hotkeys
- reject()
-
-
- 316
- 260
-
-
- 286
- 274
-
-
-
-
-
diff --git a/src/qt_gui/kbm_config_dialog.cpp b/src/qt_gui/kbm_config_dialog.cpp
deleted file mode 100644
index 89c551128..000000000
--- a/src/qt_gui/kbm_config_dialog.cpp
+++ /dev/null
@@ -1,239 +0,0 @@
-// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-#include "kbm_config_dialog.h"
-#include "kbm_help_dialog.h"
-
-#include
-#include
-#include
-#include "common/config.h"
-#include "common/path_util.h"
-#include "game_info.h"
-#include "sdl_window.h"
-
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-
-QString previous_game = "default";
-bool isHelpOpen = false;
-HelpDialog* helpDialog;
-
-EditorDialog::EditorDialog(QWidget* parent) : QDialog(parent) {
-
- setWindowTitle(tr("Edit Keyboard + Mouse and Controller input bindings"));
- resize(600, 400);
-
- // Create the editor widget
- editor = new QPlainTextEdit(this);
- editorFont.setPointSize(10); // Set default text size
- editor->setFont(editorFont); // Apply font to the editor
-
- // Create the game selection combo box
- gameComboBox = new QComboBox(this);
- gameComboBox->addItem("default"); // Add default option
- // Load all installed games
- loadInstalledGames();
-
- QCheckBox* unifiedInputCheckBox = new QCheckBox(tr("Use Per-Game configs"), this);
- unifiedInputCheckBox->setChecked(!Config::GetUseUnifiedInputConfig());
-
- // Connect checkbox signal
- connect(unifiedInputCheckBox, &QCheckBox::toggled, this, [](bool checked) {
- Config::SetUseUnifiedInputConfig(!checked);
- Config::save(Common::FS::GetUserPath(Common::FS::PathType::UserDir) / "config.toml");
- });
- // Create Save, Cancel, and Help buttons
- QPushButton* saveButton = new QPushButton("Save", this);
- QPushButton* cancelButton = new QPushButton("Cancel", this);
- QPushButton* helpButton = new QPushButton("Help", this);
- QPushButton* defaultButton = new QPushButton("Default", this);
-
- // Layout for the game selection and buttons
- QHBoxLayout* topLayout = new QHBoxLayout();
- topLayout->addWidget(unifiedInputCheckBox);
- topLayout->addWidget(gameComboBox);
- topLayout->addStretch();
- topLayout->addWidget(saveButton);
- topLayout->addWidget(cancelButton);
- topLayout->addWidget(defaultButton);
- topLayout->addWidget(helpButton);
-
- // Main layout with editor and buttons
- QVBoxLayout* layout = new QVBoxLayout(this);
- layout->addLayout(topLayout);
- layout->addWidget(editor);
-
- // Load the default config file content into the editor
- loadFile(gameComboBox->currentText());
-
- // Connect button and combo box signals
- connect(saveButton, &QPushButton::clicked, this, &EditorDialog::onSaveClicked);
- connect(cancelButton, &QPushButton::clicked, this, &EditorDialog::onCancelClicked);
- connect(helpButton, &QPushButton::clicked, this, &EditorDialog::onHelpClicked);
- connect(defaultButton, &QPushButton::clicked, this, &EditorDialog::onResetToDefaultClicked);
- connect(gameComboBox, &QComboBox::currentTextChanged, this,
- &EditorDialog::onGameSelectionChanged);
-}
-
-void EditorDialog::loadFile(QString game) {
-
- const auto config_file = Config::GetFoolproofInputConfigFile(game.toStdString());
- QFile file(config_file);
-
- if (file.open(QIODevice::ReadOnly | QIODevice::Text)) {
- QTextStream in(&file);
- editor->setPlainText(in.readAll());
- originalConfig = editor->toPlainText();
- file.close();
- } else {
- QMessageBox::warning(this, tr("Error"), tr("Could not open the file for reading"));
- }
-}
-
-void EditorDialog::saveFile(QString game) {
-
- const auto config_file = Config::GetFoolproofInputConfigFile(game.toStdString());
- QFile file(config_file);
-
- if (file.open(QIODevice::WriteOnly | QIODevice::Text)) {
- QTextStream out(&file);
- out << editor->toPlainText();
- file.close();
- } else {
- QMessageBox::warning(this, tr("Error"), tr("Could not open the file for writing"));
- }
-}
-
-// Override the close event to show the save confirmation dialog only if changes were made
-void EditorDialog::closeEvent(QCloseEvent* event) {
- if (isHelpOpen) {
- helpDialog->close();
- isHelpOpen = false;
- // at this point I might have to add this flag and the help dialog to the class itself
- }
- if (hasUnsavedChanges()) {
- QMessageBox::StandardButton reply;
- reply = QMessageBox::question(this, tr("Save Changes"), tr("Do you want to save changes?"),
- QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel);
-
- if (reply == QMessageBox::Yes) {
- saveFile(gameComboBox->currentText());
- event->accept(); // Close the dialog
- } else if (reply == QMessageBox::No) {
- event->accept(); // Close the dialog without saving
- } else {
- event->ignore(); // Cancel the close event
- }
- } else {
- event->accept(); // No changes, close the dialog without prompting
- }
-}
-void EditorDialog::keyPressEvent(QKeyEvent* event) {
- if (event->key() == Qt::Key_Escape) {
- if (isHelpOpen) {
- helpDialog->close();
- isHelpOpen = false;
- }
- close(); // Trigger the close action, same as pressing the close button
- } else {
- QDialog::keyPressEvent(event); // Call the base class implementation for other keys
- }
-}
-
-void EditorDialog::onSaveClicked() {
- if (isHelpOpen) {
- helpDialog->close();
- isHelpOpen = false;
- }
- saveFile(gameComboBox->currentText());
- reject(); // Close the dialog
-}
-
-void EditorDialog::onCancelClicked() {
- if (isHelpOpen) {
- helpDialog->close();
- isHelpOpen = false;
- }
- reject(); // Close the dialog
-}
-
-void EditorDialog::onHelpClicked() {
- if (!isHelpOpen) {
- helpDialog = new HelpDialog(&isHelpOpen, this);
- helpDialog->setWindowTitle(tr("Help"));
- helpDialog->setAttribute(Qt::WA_DeleteOnClose); // Clean up on close
- // Get the position and size of the Config window
- QRect configGeometry = this->geometry();
- int helpX = configGeometry.x() + configGeometry.width() + 10; // 10 pixels offset
- int helpY = configGeometry.y();
- // Move the Help dialog to the right side of the Config window
- helpDialog->move(helpX, helpY);
- helpDialog->show();
- isHelpOpen = true;
- } else {
- helpDialog->close();
- isHelpOpen = false;
- }
-}
-
-void EditorDialog::onResetToDefaultClicked() {
- bool default_default = gameComboBox->currentText() == "default";
- QString prompt =
- default_default
- ? tr("Do you want to reset your custom default config to the original default config?")
- : tr("Do you want to reset this config to your custom default config?");
- QMessageBox::StandardButton reply = QMessageBox::question(this, tr("Reset to Default"), prompt,
- QMessageBox::Yes | QMessageBox::No);
-
- if (reply == QMessageBox::Yes) {
- if (default_default) {
- const auto default_file = Config::GetFoolproofInputConfigFile("default");
- std::filesystem::remove(default_file);
- }
- const auto config_file = Config::GetFoolproofInputConfigFile("default");
- QFile file(config_file);
-
- if (file.open(QIODevice::ReadOnly | QIODevice::Text)) {
- QTextStream in(&file);
- editor->setPlainText(in.readAll());
- file.close();
- } else {
- QMessageBox::warning(this, tr("Error"), tr("Could not open the file for reading"));
- }
- // saveFile(gameComboBox->currentText());
- }
-}
-
-bool EditorDialog::hasUnsavedChanges() {
- // Compare the current content with the original content to check if there are unsaved changes
- return editor->toPlainText() != originalConfig;
-}
-void EditorDialog::loadInstalledGames() {
- previous_game = "default";
- QStringList filePaths;
- for (const auto& installLoc : Config::getGameInstallDirs()) {
- QString installDir;
- Common::FS::PathToQString(installDir, installLoc);
- QDir parentFolder(installDir);
- QFileInfoList fileList = parentFolder.entryInfoList(QDir::Dirs | QDir::NoDotAndDotDot);
- for (const auto& fileInfo : fileList) {
- if (fileInfo.isDir() && (!fileInfo.filePath().endsWith("-UPDATE") ||
- !fileInfo.filePath().endsWith("-patch"))) {
- gameComboBox->addItem(fileInfo.fileName()); // Add game name to combo box
- }
- }
- }
-}
-void EditorDialog::onGameSelectionChanged(const QString& game) {
- saveFile(previous_game);
- loadFile(gameComboBox->currentText()); // Reload file based on the selected game
- previous_game = gameComboBox->currentText();
-}
diff --git a/src/qt_gui/kbm_config_dialog.h b/src/qt_gui/kbm_config_dialog.h
deleted file mode 100644
index cc334b082..000000000
--- a/src/qt_gui/kbm_config_dialog.h
+++ /dev/null
@@ -1,38 +0,0 @@
-// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-#pragma once
-
-#include
-#include
-#include
-#include "string"
-
-class EditorDialog : public QDialog {
-Q_OBJECT // Necessary for using Qt's meta-object system (signals/slots)
- public : explicit EditorDialog(QWidget* parent = nullptr); // Constructor
-
-protected:
- void closeEvent(QCloseEvent* event) override; // Override close event
- void keyPressEvent(QKeyEvent* event) override;
-
-private:
- QPlainTextEdit* editor; // Editor widget for the config file
- QFont editorFont; // To handle the text size
- QString originalConfig; // Starting config string
- std::string gameId;
-
- QComboBox* gameComboBox; // Combo box for selecting game configurations
-
- void loadFile(QString game); // Function to load the config file
- void saveFile(QString game); // Function to save the config file
- void loadInstalledGames(); // Helper to populate gameComboBox
- bool hasUnsavedChanges(); // Checks for unsaved changes
-
-private slots:
- void onSaveClicked(); // Save button slot
- void onCancelClicked(); // Slot for handling cancel button
- void onHelpClicked(); // Slot for handling help button
- void onResetToDefaultClicked();
- void onGameSelectionChanged(const QString& game); // Slot for game selection changes
-};
diff --git a/src/qt_gui/kbm_gui.cpp b/src/qt_gui/kbm_gui.cpp
deleted file mode 100644
index a7e5fc343..000000000
--- a/src/qt_gui/kbm_gui.cpp
+++ /dev/null
@@ -1,1061 +0,0 @@
-// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-
-#include "common/path_util.h"
-#include "input/input_handler.h"
-#include "kbm_config_dialog.h"
-#include "kbm_gui.h"
-#include "kbm_help_dialog.h"
-#include "ui_kbm_gui.h"
-
-HelpDialog* HelpWindow;
-KBMSettings::KBMSettings(std::shared_ptr game_info_get, bool isGameRunning,
- std::string GameRunningSerial, QWidget* parent)
- : QDialog(parent), m_game_info(game_info_get), GameRunning(isGameRunning),
- RunningGameSerial(GameRunningSerial), ui(new Ui::KBMSettings) {
-
- ui->setupUi(this);
- ui->PerGameCheckBox->setChecked(!Config::GetUseUnifiedInputConfig());
- ui->TextEditorButton->setFocus();
- this->setFocusPolicy(Qt::StrongFocus);
-
- ui->MouseJoystickBox->addItem("none");
- ui->MouseJoystickBox->addItem("right");
- ui->MouseJoystickBox->addItem("left");
-
- ui->ProfileComboBox->addItem(tr("Common Config"));
- for (int i = 0; i < m_game_info->m_games.size(); i++) {
- ui->ProfileComboBox->addItem(QString::fromStdString(m_game_info->m_games[i].serial));
- }
-
- ButtonsList = {ui->CrossButton,
- ui->CircleButton,
- ui->TriangleButton,
- ui->SquareButton,
- ui->L1Button,
- ui->R1Button,
- ui->L2Button,
- ui->R2Button,
- ui->L3Button,
- ui->R3Button,
- ui->OptionsButton,
- ui->TouchpadLeftButton,
- ui->TouchpadCenterButton,
- ui->TouchpadRightButton,
- ui->DpadUpButton,
- ui->DpadDownButton,
- ui->DpadLeftButton,
- ui->DpadRightButton,
- ui->LStickUpButton,
- ui->LStickDownButton,
- ui->LStickLeftButton,
- ui->LStickRightButton,
- ui->RStickUpButton,
- ui->RStickDownButton,
- ui->RStickLeftButton,
- ui->RStickRightButton,
- ui->LHalfButton,
- ui->RHalfButton};
-
- ButtonConnects();
- SetUIValuestoMappings("default");
- installEventFilter(this);
-
- ui->ProfileComboBox->setCurrentText(tr("Common Config"));
- ui->TitleLabel->setText(tr("Common Config"));
- config_id = "default";
-
- connect(ui->buttonBox, &QDialogButtonBox::clicked, this, [this](QAbstractButton* button) {
- if (button == ui->buttonBox->button(QDialogButtonBox::Save)) {
- if (HelpWindowOpen) {
- HelpWindow->close();
- HelpWindowOpen = false;
- }
- SaveKBMConfig(true);
- } else if (button == ui->buttonBox->button(QDialogButtonBox::RestoreDefaults)) {
- SetDefault();
- } else if (button == ui->buttonBox->button(QDialogButtonBox::Apply)) {
- SaveKBMConfig(false);
- }
- });
-
- ui->buttonBox->button(QDialogButtonBox::Save)->setText(tr("Save"));
- ui->buttonBox->button(QDialogButtonBox::Apply)->setText(tr("Apply"));
- ui->buttonBox->button(QDialogButtonBox::RestoreDefaults)->setText(tr("Restore Defaults"));
- ui->buttonBox->button(QDialogButtonBox::Cancel)->setText(tr("Cancel"));
-
- connect(ui->HelpButton, &QPushButton::clicked, this, &KBMSettings::onHelpClicked);
- connect(ui->TextEditorButton, &QPushButton::clicked, this, [this]() {
- auto kbmWindow = new EditorDialog(this);
- kbmWindow->exec();
- SetUIValuestoMappings(config_id);
- });
-
- connect(ui->buttonBox, &QDialogButtonBox::rejected, this, [this] {
- QWidget::close();
- if (HelpWindowOpen) {
- HelpWindow->close();
- HelpWindowOpen = false;
- }
- });
-
- connect(ui->ProfileComboBox, &QComboBox::currentTextChanged, this, [this] {
- GetGameTitle();
- SetUIValuestoMappings(config_id);
- });
-
- connect(ui->CopyCommonButton, &QPushButton::clicked, this, [this] {
- if (ui->ProfileComboBox->currentText() == tr("Common Config")) {
- QMessageBox::information(this, tr("Common Config Selected"),
- // clang-format off
-tr("This button copies mappings from the Common Config to the currently selected profile, and cannot be used when the currently selected profile is the Common Config."));
- // clang-format on
- } else {
- QMessageBox::StandardButton reply =
- QMessageBox::question(this, tr("Copy values from Common Config"),
- // clang-format off
-tr("Do you want to overwrite existing mappings with the mappings from the Common Config?"),
- // clang-format on
- QMessageBox::Yes | QMessageBox::No);
- if (reply == QMessageBox::Yes) {
- SetUIValuestoMappings("default");
- }
- }
- });
-
- connect(ui->DeadzoneOffsetSlider, &QSlider::valueChanged, this, [this](int value) {
- QString DOSValue = QString::number(value / 100.0, 'f', 2);
- ui->DeadzoneOffsetLabel->setText(DOSValue);
- });
-
- connect(ui->SpeedMultiplierSlider, &QSlider::valueChanged, this, [this](int value) {
- QString SMSValue = QString::number(value / 10.0, 'f', 1);
- ui->SpeedMultiplierLabel->setText(SMSValue);
- });
-
- connect(ui->SpeedOffsetSlider, &QSlider::valueChanged, this, [this](int value) {
- QString SOSValue = QString::number(value / 1000.0, 'f', 3);
- ui->SpeedOffsetLabel->setText(SOSValue);
- });
-
- connect(this, &KBMSettings::PushKBMEvent, this, [this]() { CheckMapping(MappingButton); });
-}
-
-void KBMSettings::ButtonConnects() {
- for (auto& button : ButtonsList) {
- connect(button, &QPushButton::clicked, this, [this, &button]() { StartTimer(button); });
- }
-}
-
-void KBMSettings::DisableMappingButtons() {
- for (const auto& i : ButtonsList) {
- i->setEnabled(false);
- }
-
- ui->buttonBox->button(QDialogButtonBox::Save)->setEnabled(false);
- ui->buttonBox->button(QDialogButtonBox::Apply)->setEnabled(false);
- ui->buttonBox->button(QDialogButtonBox::RestoreDefaults)->setEnabled(false);
-}
-
-void KBMSettings::EnableMappingButtons() {
- for (const auto& i : ButtonsList) {
- i->setEnabled(true);
- }
-
- ui->buttonBox->button(QDialogButtonBox::Save)->setEnabled(true);
- ui->buttonBox->button(QDialogButtonBox::Apply)->setEnabled(true);
- ui->buttonBox->button(QDialogButtonBox::RestoreDefaults)->setEnabled(true);
-}
-
-void KBMSettings::SaveKBMConfig(bool close_on_save) {
- std::string output_string = "", input_string = "";
- std::vector lines, inputs;
-
- // Comment lines for config file
- lines.push_back("#Feeling lost? Check out the Help section!");
- lines.push_back("");
- lines.push_back("#Keyboard bindings");
- lines.push_back("");
-
- // Lambda to reduce repetitive code for mapping buttons to config lines
- auto add_mapping = [&](const QString& buttonText, const std::string& output_name) {
- input_string = buttonText.toStdString();
- output_string = output_name;
- lines.push_back(output_string + " = " + input_string);
- if (input_string != "unmapped") {
- inputs.push_back(input_string);
- }
- };
-
- add_mapping(ui->CrossButton->text(), "cross");
- add_mapping(ui->CircleButton->text(), "circle");
- add_mapping(ui->TriangleButton->text(), "triangle");
- add_mapping(ui->SquareButton->text(), "square");
-
- lines.push_back("");
-
- add_mapping(ui->DpadUpButton->text(), "pad_up");
- add_mapping(ui->DpadDownButton->text(), "pad_down");
- add_mapping(ui->DpadLeftButton->text(), "pad_left");
- add_mapping(ui->DpadRightButton->text(), "pad_right");
-
- lines.push_back("");
-
- add_mapping(ui->L1Button->text(), "l1");
- add_mapping(ui->R1Button->text(), "r1");
- add_mapping(ui->L2Button->text(), "l2");
- add_mapping(ui->R2Button->text(), "r2");
- add_mapping(ui->L3Button->text(), "l3");
- add_mapping(ui->R3Button->text(), "r3");
-
- lines.push_back("");
-
- add_mapping(ui->TouchpadLeftButton->text(), "touchpad_left");
- add_mapping(ui->TouchpadCenterButton->text(), "touchpad_center");
- add_mapping(ui->TouchpadRightButton->text(), "touchpad_right");
- add_mapping(ui->OptionsButton->text(), "options");
-
- lines.push_back("");
-
- add_mapping(ui->LStickUpButton->text(), "axis_left_y_minus");
- add_mapping(ui->LStickDownButton->text(), "axis_left_y_plus");
- add_mapping(ui->LStickLeftButton->text(), "axis_left_x_minus");
- add_mapping(ui->LStickRightButton->text(), "axis_left_x_plus");
-
- lines.push_back("");
-
- add_mapping(ui->RStickUpButton->text(), "axis_right_y_minus");
- add_mapping(ui->RStickDownButton->text(), "axis_right_y_plus");
- add_mapping(ui->RStickLeftButton->text(), "axis_right_x_minus");
- add_mapping(ui->RStickRightButton->text(), "axis_right_x_plus");
-
- lines.push_back("");
-
- lines.push_back("mouse_to_joystick = " + ui->MouseJoystickBox->currentText().toStdString());
- add_mapping(ui->LHalfButton->text(), "leftjoystick_halfmode");
- add_mapping(ui->RHalfButton->text(), "rightjoystick_halfmode");
-
- std::string DOString = std::format("{:.2f}", (ui->DeadzoneOffsetSlider->value() / 100.f));
- std::string SMString = std::format("{:.1f}", (ui->SpeedMultiplierSlider->value() / 10.f));
- std::string SOString = std::format("{:.3f}", (ui->SpeedOffsetSlider->value() / 1000.f));
- input_string = DOString + ", " + SMString + ", " + SOString;
- output_string = "mouse_movement_params";
- lines.push_back(output_string + " = " + input_string);
-
- lines.push_back("");
- const auto config_file = Config::GetFoolproofInputConfigFile(config_id);
- std::fstream file(config_file);
- int lineCount = 0;
- std::string line;
- while (std::getline(file, line)) {
- lineCount++;
-
- if (line.empty()) {
- lines.push_back(line);
- continue;
- }
-
- std::size_t comment_pos = line.find('#');
- if (comment_pos != std::string::npos) {
- if (!line.contains("Keyboard bindings") && !line.contains("Feeling lost") &&
- !line.contains("Alternatives for users"))
- lines.push_back(line);
- continue;
- }
-
- std::size_t equal_pos = line.find('=');
- if (equal_pos == std::string::npos) {
- lines.push_back(line);
- continue;
- }
-
- output_string = line.substr(0, equal_pos - 1);
- input_string = line.substr(equal_pos + 2);
-
- bool controllerInputdetected = false;
- for (std::string input : ControllerInputs) {
- // Needed to avoid detecting backspace while detecting back
- if (input_string.contains(input) && !input_string.contains("backspace")) {
- controllerInputdetected = true;
- break;
- }
- }
-
- if (controllerInputdetected || output_string == "analog_deadzone" ||
- output_string == "override_controller_color" || output_string.contains("hotkey")) {
- lines.push_back(line);
- }
- }
- file.close();
-
- // Prevent duplicate inputs for KBM as this breaks the engine
- bool duplicateFound = false;
- QSet duplicateMappings;
-
- for (auto it = inputs.begin(); it != inputs.end(); ++it) {
- if (std::find(it + 1, inputs.end(), *it) != inputs.end()) {
- duplicateFound = true;
- duplicateMappings.insert(QString::fromStdString(*it));
- }
- }
-
- if (duplicateFound) {
- QStringList duplicatesList;
- for (const QString mapping : duplicateMappings) {
- for (const auto& button : ButtonsList) {
- if (button->text() == mapping)
- duplicatesList.append(button->objectName() + " - " + mapping);
- }
- }
- QMessageBox::information(
- this, tr("Unable to Save"),
- // clang-format off
-QString(tr("Cannot bind any unique input more than once. Duplicate inputs mapped to the following buttons:\n\n%1").arg(duplicatesList.join("\n"))));
- // clang-format on
- return;
- }
-
- std::vector save;
- bool CurrentLineEmpty = false, LastLineEmpty = false;
- for (auto const& line : lines) {
- LastLineEmpty = CurrentLineEmpty ? true : false;
- CurrentLineEmpty = line.empty() ? true : false;
- if (!CurrentLineEmpty || !LastLineEmpty)
- save.push_back(line);
- }
-
- std::ofstream output_file(config_file);
- for (auto const& line : save) {
- output_file << line << '\n';
- }
- output_file.close();
-
- Config::SetUseUnifiedInputConfig(!ui->PerGameCheckBox->isChecked());
- Config::save(Common::FS::GetUserPath(Common::FS::PathType::UserDir) / "config.toml");
-
- if (GameRunning) {
- Config::GetUseUnifiedInputConfig() ? Input::ParseInputConfig("default")
- : Input::ParseInputConfig(RunningGameSerial);
- }
-
- if (close_on_save)
- QWidget::close();
-}
-
-void KBMSettings::SetDefault() {
- ui->CrossButton->setText("kp2");
- ui->CircleButton->setText("kp6");
- ui->TriangleButton->setText("kp8");
- ui->SquareButton->setText("kp4");
-
- ui->L1Button->setText("q");
- ui->L2Button->setText("e");
- ui->L3Button->setText("x");
- ui->R1Button->setText("u");
- ui->R2Button->setText("o");
- ui->R3Button->setText("m");
-
- ui->TouchpadLeftButton->setText("space");
- ui->TouchpadCenterButton->setText("unmapped");
- ui->TouchpadRightButton->setText("unmapped");
- ui->OptionsButton->setText("enter");
-
- ui->DpadUpButton->setText("up");
- ui->DpadDownButton->setText("down");
- ui->DpadLeftButton->setText("left");
- ui->DpadRightButton->setText("right");
-
- ui->LStickUpButton->setText("w");
- ui->LStickDownButton->setText("s");
- ui->LStickLeftButton->setText("a");
- ui->LStickRightButton->setText("d");
-
- ui->RStickUpButton->setText("i");
- ui->RStickDownButton->setText("k");
- ui->RStickLeftButton->setText("j");
- ui->RStickRightButton->setText("l");
-
- ui->LHalfButton->setText("unmapped");
- ui->RHalfButton->setText("unmapped");
-
- ui->MouseJoystickBox->setCurrentText("none");
- ui->DeadzoneOffsetSlider->setValue(50);
- ui->SpeedMultiplierSlider->setValue(10);
- ui->SpeedOffsetSlider->setValue(125);
-}
-
-void KBMSettings::SetUIValuestoMappings(std::string config_id) {
- const auto config_file = Config::GetFoolproofInputConfigFile(config_id);
- std::ifstream file(config_file);
-
- int lineCount = 0;
- std::string line = "";
- while (std::getline(file, line)) {
- lineCount++;
-
- std::size_t comment_pos = line.find('#');
- if (comment_pos != std::string::npos)
- line = line.substr(0, comment_pos);
-
- std::size_t equal_pos = line.find('=');
- if (equal_pos == std::string::npos)
- continue;
-
- std::string output_string = line.substr(0, equal_pos - 1);
- std::string input_string = line.substr(equal_pos + 2);
-
- bool controllerInputdetected = false;
- for (std::string input : ControllerInputs) {
- // Needed to avoid detecting backspace while detecting back
- if (input_string.contains(input) && !input_string.contains("backspace")) {
- controllerInputdetected = true;
- break;
- }
- }
-
- if (!controllerInputdetected) {
- if (output_string == "cross") {
- ui->CrossButton->setText(QString::fromStdString(input_string));
- } else if (output_string == "circle") {
- ui->CircleButton->setText(QString::fromStdString(input_string));
- } else if (output_string == "square") {
- ui->SquareButton->setText(QString::fromStdString(input_string));
- } else if (output_string == "triangle") {
- ui->TriangleButton->setText(QString::fromStdString(input_string));
- } else if (output_string == "l1") {
- ui->L1Button->setText(QString::fromStdString(input_string));
- } else if (output_string == "l2") {
- ui->L2Button->setText(QString::fromStdString(input_string));
- } else if (output_string == "r1") {
- ui->R1Button->setText(QString::fromStdString(input_string));
- } else if (output_string == "r2") {
- ui->R2Button->setText(QString::fromStdString(input_string));
- } else if (output_string == "l3") {
- ui->L3Button->setText(QString::fromStdString(input_string));
- } else if (output_string == "r3") {
- ui->R3Button->setText(QString::fromStdString(input_string));
- } else if (output_string == "pad_up") {
- ui->DpadUpButton->setText(QString::fromStdString(input_string));
- } else if (output_string == "pad_down") {
- ui->DpadDownButton->setText(QString::fromStdString(input_string));
- } else if (output_string == "pad_left") {
- ui->DpadLeftButton->setText(QString::fromStdString(input_string));
- } else if (output_string == "pad_right") {
- ui->DpadRightButton->setText(QString::fromStdString(input_string));
- } else if (output_string == "options") {
- ui->OptionsButton->setText(QString::fromStdString(input_string));
- } else if (output_string == "touchpad_left") {
- ui->TouchpadLeftButton->setText(QString::fromStdString(input_string));
- } else if (output_string == "touchpad_center") {
- ui->TouchpadCenterButton->setText(QString::fromStdString(input_string));
- } else if (output_string == "touchpad_right") {
- ui->TouchpadRightButton->setText(QString::fromStdString(input_string));
- } else if (output_string == "axis_left_x_minus") {
- ui->LStickLeftButton->setText(QString::fromStdString(input_string));
- } else if (output_string == "axis_left_x_plus") {
- ui->LStickRightButton->setText(QString::fromStdString(input_string));
- } else if (output_string == "axis_left_y_minus") {
- ui->LStickUpButton->setText(QString::fromStdString(input_string));
- } else if (output_string == "axis_left_y_plus") {
- ui->LStickDownButton->setText(QString::fromStdString(input_string));
- } else if (output_string == "axis_right_x_minus") {
- ui->RStickLeftButton->setText(QString::fromStdString(input_string));
- } else if (output_string == "axis_right_x_plus") {
- ui->RStickRightButton->setText(QString::fromStdString(input_string));
- } else if (output_string == "axis_right_y_minus") {
- ui->RStickUpButton->setText(QString::fromStdString(input_string));
- } else if (output_string == "axis_right_y_plus") {
- ui->RStickDownButton->setText(QString::fromStdString(input_string));
- } else if (output_string == "mouse_to_joystick") {
- ui->MouseJoystickBox->setCurrentText(QString::fromStdString(input_string));
- } else if (output_string == "leftjoystick_halfmode") {
- ui->LHalfButton->setText(QString::fromStdString(input_string));
- } else if (output_string == "rightjoystick_halfmode") {
- ui->RHalfButton->setText(QString::fromStdString(input_string));
- } else if (output_string.contains("mouse_movement_params")) {
- std::size_t comma_pos = line.find(',');
- if (comma_pos != std::string::npos) {
- std::string DOstring = line.substr(equal_pos + 1, comma_pos);
- float DOffsetValue = std::stof(DOstring) * 100.0;
- int DOffsetInt = static_cast(DOffsetValue);
- ui->DeadzoneOffsetSlider->setValue(DOffsetInt);
- QString LabelValue = QString::number(DOffsetInt / 100.0, 'f', 2);
- ui->DeadzoneOffsetLabel->setText(LabelValue);
-
- std::string SMSOstring = line.substr(comma_pos + 1);
- std::size_t comma_pos2 = SMSOstring.find(',');
- if (comma_pos2 != std::string::npos) {
- std::string SMstring = SMSOstring.substr(0, comma_pos2);
- float SpeedMultValue = std::clamp(std::stof(SMstring) * 10.0f, 1.0f, 50.0f);
- int SpeedMultInt = static_cast(SpeedMultValue);
- ui->SpeedMultiplierSlider->setValue(SpeedMultInt);
- LabelValue = QString::number(SpeedMultInt / 10.0, 'f', 1);
- ui->SpeedMultiplierLabel->setText(LabelValue);
-
- std::string SOstring = SMSOstring.substr(comma_pos2 + 1);
- float SOffsetValue = std::stof(SOstring) * 1000.0;
- int SOffsetInt = static_cast(SOffsetValue);
- ui->SpeedOffsetSlider->setValue(SOffsetInt);
- LabelValue = QString::number(SOffsetInt / 1000.0, 'f', 3);
- ui->SpeedOffsetLabel->setText(LabelValue);
- }
- }
- }
- }
- }
- file.close();
-}
-
-void KBMSettings::GetGameTitle() {
- if (ui->ProfileComboBox->currentText() == tr("Common Config")) {
- ui->TitleLabel->setText(tr("Common Config"));
- } else {
- for (int i = 0; i < m_game_info->m_games.size(); i++) {
- if (m_game_info->m_games[i].serial ==
- ui->ProfileComboBox->currentText().toStdString()) {
- ui->TitleLabel->setText(QString::fromStdString(m_game_info->m_games[i].name));
- }
- }
- }
- config_id = (ui->ProfileComboBox->currentText() == tr("Common Config"))
- ? "default"
- : ui->ProfileComboBox->currentText().toStdString();
-}
-
-void KBMSettings::onHelpClicked() {
- if (!HelpWindowOpen) {
- HelpWindow = new HelpDialog(&HelpWindowOpen, this);
- HelpWindow->setWindowTitle(tr("Help"));
- HelpWindow->setAttribute(Qt::WA_DeleteOnClose); // Clean up on close
- HelpWindow->show();
- HelpWindowOpen = true;
- } else {
- HelpWindow->close();
- HelpWindowOpen = false;
- }
-}
-
-void KBMSettings::StartTimer(QPushButton*& button) {
- MappingTimer = 3;
- EnableMapping = true;
- MappingCompleted = false;
- mapping = button->text();
-
- DisableMappingButtons();
- button->setText(tr("Press a key") + " [" + QString::number(MappingTimer) + "]");
- timer = new QTimer(this);
- MappingButton = button;
- connect(timer, &QTimer::timeout, this, [this]() { CheckMapping(MappingButton); });
- timer->start(1000);
-}
-
-void KBMSettings::CheckMapping(QPushButton*& button) {
- MappingTimer -= 1;
- button->setText(tr("Press a key") + " [" + QString::number(MappingTimer) + "]");
-
- if (pressedKeys.size() > 0) {
- QStringList keyStrings;
-
- for (const QString& buttonAction : pressedKeys) {
- keyStrings << buttonAction;
- }
-
- QString combo = keyStrings.join(",");
- SetMapping(combo);
- MappingCompleted = true;
- EnableMapping = false;
-
- MappingButton->setText(combo);
- pressedKeys.clear();
- timer->stop();
- }
- if (MappingCompleted) {
- EnableMapping = false;
- EnableMappingButtons();
- timer->stop();
-
- button->setText(mapping);
- }
-
- if (MappingTimer <= 0) {
- button->setText(mapping);
- EnableMapping = false;
- EnableMappingButtons();
- timer->stop();
- }
-}
-
-void KBMSettings::SetMapping(QString input) {
- mapping = input;
- MappingCompleted = true;
-}
-
-// Helper lambda to get the modified button text based on the current keyboard modifiers
-auto GetModifiedButton = [](Qt::KeyboardModifiers modifier, const std::string& m_button,
- const std::string& n_button) -> QString {
- if (QApplication::keyboardModifiers() & modifier) {
- return QString::fromStdString(m_button);
- } else {
- return QString::fromStdString(n_button);
- }
-};
-
-bool KBMSettings::eventFilter(QObject* obj, QEvent* event) {
- if (event->type() == QEvent::Close) {
- if (HelpWindowOpen) {
- HelpWindow->close();
- HelpWindowOpen = false;
- }
- }
-
- if (EnableMapping) {
- if (event->type() == QEvent::KeyPress) {
- QKeyEvent* keyEvent = static_cast(event);
-
- if (keyEvent->isAutoRepeat())
- return true;
-
- if (pressedKeys.size() >= 3) {
- return true;
- }
-
- switch (keyEvent->key()) {
- // modifiers
- case Qt::Key_Shift:
- keyEvent->nativeScanCode() == LSHIFT_KEY ? pressedKeys.insert(1, "lshift")
- : pressedKeys.insert(2, "rshift");
- break;
- case Qt::Key_Alt:
- keyEvent->nativeScanCode() == LALT_KEY ? pressedKeys.insert(3, "lalt")
- : pressedKeys.insert(4, "ralt");
- break;
- case Qt::Key_Control:
- keyEvent->nativeScanCode() == LCTRL_KEY ? pressedKeys.insert(5, "lctrl")
- : pressedKeys.insert(6, "rctrl");
- break;
- case Qt::Key_Meta:
-#ifdef _WIN32
- pressedKeys.insert(7, "lwin");
-#else
- pressedKeys.insert(7, "lmeta");
-#endif
- break;
-
- // alphanumeric
- case Qt::Key_A:
- pressedKeys.insert(20, "a");
- break;
- case Qt::Key_B:
- pressedKeys.insert(21, "b");
- break;
- case Qt::Key_C:
- pressedKeys.insert(22, "c");
- break;
- case Qt::Key_D:
- pressedKeys.insert(23, "d");
- break;
- case Qt::Key_E:
- pressedKeys.insert(24, "e");
- break;
- case Qt::Key_F:
- pressedKeys.insert(25, "f");
- break;
- case Qt::Key_G:
- pressedKeys.insert(26, "g");
- break;
- case Qt::Key_H:
- pressedKeys.insert(27, "h");
- break;
- case Qt::Key_I:
- pressedKeys.insert(28, "i");
- break;
- case Qt::Key_J:
- pressedKeys.insert(29, "j");
- break;
- case Qt::Key_K:
- pressedKeys.insert(30, "k");
- break;
- case Qt::Key_L:
- pressedKeys.insert(31, "l");
- break;
- case Qt::Key_M:
- pressedKeys.insert(32, "m");
- break;
- case Qt::Key_N:
- pressedKeys.insert(33, "n");
- break;
- case Qt::Key_O:
- pressedKeys.insert(34, "o");
- break;
- case Qt::Key_P:
- pressedKeys.insert(35, "p");
- break;
- case Qt::Key_Q:
- pressedKeys.insert(36, "q");
- break;
- case Qt::Key_R:
- pressedKeys.insert(37, "r");
- break;
- case Qt::Key_S:
- pressedKeys.insert(38, "s");
- break;
- case Qt::Key_T:
- pressedKeys.insert(39, "t");
- break;
- case Qt::Key_U:
- pressedKeys.insert(40, "u");
- break;
- case Qt::Key_V:
- pressedKeys.insert(41, "v");
- break;
- case Qt::Key_W:
- pressedKeys.insert(42, "w");
- break;
- case Qt::Key_X:
- pressedKeys.insert(43, "x");
- break;
- case Qt::Key_Y:
- pressedKeys.insert(44, "y");
- break;
- case Qt::Key_Z:
- pressedKeys.insert(45, "z");
- break;
- case Qt::Key_0:
- QApplication::keyboardModifiers() & Qt::KeypadModifier
- ? pressedKeys.insert(46, "kp0")
- : pressedKeys.insert(56, "0");
- break;
- case Qt::Key_1:
- QApplication::keyboardModifiers() & Qt::KeypadModifier
- ? pressedKeys.insert(47, "kp1")
- : pressedKeys.insert(57, "1");
- break;
- case Qt::Key_2:
- QApplication::keyboardModifiers() & Qt::KeypadModifier
- ? pressedKeys.insert(48, "kp2")
- : pressedKeys.insert(58, "2");
- break;
- case Qt::Key_3:
- QApplication::keyboardModifiers() & Qt::KeypadModifier
- ? pressedKeys.insert(49, "kp3")
- : pressedKeys.insert(59, "3");
- break;
- case Qt::Key_4:
- QApplication::keyboardModifiers() & Qt::KeypadModifier
- ? pressedKeys.insert(50, "kp4")
- : pressedKeys.insert(60, "4");
- break;
- case Qt::Key_5:
- QApplication::keyboardModifiers() & Qt::KeypadModifier
- ? pressedKeys.insert(51, "kp5")
- : pressedKeys.insert(61, "5");
- break;
- case Qt::Key_6:
- QApplication::keyboardModifiers() & Qt::KeypadModifier
- ? pressedKeys.insert(52, "kp6")
- : pressedKeys.insert(62, "6");
- break;
- case Qt::Key_7:
- QApplication::keyboardModifiers() & Qt::KeypadModifier
- ? pressedKeys.insert(53, "kp7")
- : pressedKeys.insert(63, "7");
- break;
- case Qt::Key_8:
- QApplication::keyboardModifiers() & Qt::KeypadModifier
- ? pressedKeys.insert(54, "kp8")
- : pressedKeys.insert(64, "8");
- break;
- case Qt::Key_9:
- QApplication::keyboardModifiers() & Qt::KeypadModifier
- ? pressedKeys.insert(55, "kp9")
- : pressedKeys.insert(65, "9");
- break;
-
- // symbols
- case Qt::Key_Asterisk:
- QApplication::keyboardModifiers() & Qt::KeypadModifier
- ? pressedKeys.insert(66, "kpasterisk")
- : pressedKeys.insert(74, "asterisk");
- break;
- case Qt::Key_Minus:
- QApplication::keyboardModifiers() & Qt::KeypadModifier
- ? pressedKeys.insert(67, "kpminus")
- : pressedKeys.insert(75, "minus");
- break;
- case Qt::Key_Equal:
- QApplication::keyboardModifiers() & Qt::KeypadModifier
- ? pressedKeys.insert(68, "kpequals")
- : pressedKeys.insert(76, "equals");
- break;
- case Qt::Key_Plus:
- QApplication::keyboardModifiers() & Qt::KeypadModifier
- ? pressedKeys.insert(69, "kpplus")
- : pressedKeys.insert(77, "plus");
- break;
- case Qt::Key_Slash:
- QApplication::keyboardModifiers() & Qt::KeypadModifier
- ? pressedKeys.insert(70, "kpslash")
- : pressedKeys.insert(78, "slash");
- break;
- case Qt::Key_Comma:
- QApplication::keyboardModifiers() & Qt::KeypadModifier
- ? pressedKeys.insert(71, "kpcomma")
- : pressedKeys.insert(79, "comma");
- break;
- case Qt::Key_Period:
- QApplication::keyboardModifiers() & Qt::KeypadModifier
- ? pressedKeys.insert(72, "kpperiod")
- : pressedKeys.insert(80, "period");
- break;
- case Qt::Key_Enter:
- QApplication::keyboardModifiers() & Qt::KeypadModifier
- ? pressedKeys.insert(73, "kpenter")
- : pressedKeys.insert(81, "enter");
- break;
- case Qt::Key_QuoteLeft:
- pressedKeys.insert(82, "grave");
- break;
- case Qt::Key_AsciiTilde:
- pressedKeys.insert(83, "tilde");
- break;
- case Qt::Key_Exclam:
- pressedKeys.insert(84, "exclamation");
- break;
- case Qt::Key_At:
- pressedKeys.insert(85, "at");
- break;
- case Qt::Key_NumberSign:
- pressedKeys.insert(86, "hash");
- break;
- case Qt::Key_Dollar:
- pressedKeys.insert(87, "dollar");
- break;
- case Qt::Key_Percent:
- pressedKeys.insert(88, "percent");
- break;
- case Qt::Key_AsciiCircum:
- pressedKeys.insert(89, "caret");
- break;
- case Qt::Key_Ampersand:
- pressedKeys.insert(90, "ampersand");
- break;
- case Qt::Key_ParenLeft:
- pressedKeys.insert(91, "lparen");
- break;
- case Qt::Key_ParenRight:
- pressedKeys.insert(92, "rparen");
- break;
- case Qt::Key_BracketLeft:
- pressedKeys.insert(93, "lbracket");
- break;
- case Qt::Key_BracketRight:
- pressedKeys.insert(94, "rbracket");
- break;
- case Qt::Key_BraceLeft:
- pressedKeys.insert(95, "lbrace");
- break;
- case Qt::Key_BraceRight:
- pressedKeys.insert(96, "rbrace");
- break;
- case Qt::Key_Underscore:
- pressedKeys.insert(97, "underscore");
- break;
- case Qt::Key_Backslash:
- pressedKeys.insert(98, "backslash");
- break;
- case Qt::Key_Bar:
- pressedKeys.insert(99, "pipe");
- break;
- case Qt::Key_Semicolon:
- pressedKeys.insert(100, "semicolon");
- break;
- case Qt::Key_Colon:
- pressedKeys.insert(101, "colon");
- break;
- case Qt::Key_Apostrophe:
- pressedKeys.insert(102, "apostrophe");
- break;
- case Qt::Key_QuoteDbl:
- pressedKeys.insert(103, "quote");
- break;
- case Qt::Key_Less:
- pressedKeys.insert(104, "less");
- break;
- case Qt::Key_Greater:
- pressedKeys.insert(105, "greater");
- break;
- case Qt::Key_Question:
- pressedKeys.insert(106, "question");
- break;
-
- // special keys
- case Qt::Key_Print:
- pressedKeys.insert(107, "printscreen");
- break;
- case Qt::Key_ScrollLock:
- pressedKeys.insert(108, "scrolllock");
- break;
- case Qt::Key_Pause:
- pressedKeys.insert(109, "pausebreak");
- break;
- case Qt::Key_Backspace:
- pressedKeys.insert(110, "backspace");
- break;
- case Qt::Key_Insert:
- pressedKeys.insert(111, "insert");
- break;
- case Qt::Key_Delete:
- pressedKeys.insert(112, "delete");
- break;
- case Qt::Key_Home:
- pressedKeys.insert(113, "home");
- break;
- case Qt::Key_End:
- pressedKeys.insert(114, "end");
- break;
- case Qt::Key_PageUp:
- pressedKeys.insert(115, "pgup");
- break;
- case Qt::Key_PageDown:
- pressedKeys.insert(116, "pgdown");
- break;
- case Qt::Key_Tab:
- pressedKeys.insert(117, "tab");
- break;
- case Qt::Key_CapsLock:
- pressedKeys.insert(118, "capslock");
- break;
- case Qt::Key_Return:
- pressedKeys.insert(119, "enter");
- break;
- case Qt::Key_Space:
- pressedKeys.insert(120, "space");
- break;
- case Qt::Key_Up:
- pressedKeys.insert(121, "up");
- break;
- case Qt::Key_Down:
- pressedKeys.insert(122, "down");
- break;
- case Qt::Key_Left:
- pressedKeys.insert(123, "left");
- break;
- case Qt::Key_Right:
- pressedKeys.insert(124, "right");
- break;
-
- // cancel mapping
- case Qt::Key_Escape:
- SetMapping("unmapped");
- break;
- default:
- break;
- }
- return true;
- }
-
- if (event->type() == QEvent::MouseButtonPress) {
- QMouseEvent* mouseEvent = static_cast(event);
- if (pressedKeys.size() < 3) {
- switch (mouseEvent->button()) {
- case Qt::LeftButton:
- pressedKeys.insert(125, "leftbutton");
- break;
- case Qt::RightButton:
- pressedKeys.insert(127, "rightbutton");
- break;
- case Qt::MiddleButton:
- pressedKeys.insert(126, "middlebutton");
- break;
- case Qt::XButton1:
- pressedKeys.insert(128, "sidebuttonback");
- break;
- case Qt::XButton2:
- pressedKeys.insert(129, "sidebuttonforward");
- break;
-
- // default case
- default:
- break;
- // bottom text
- }
- return true;
- }
- }
-
- const QList AxisList = {
- ui->LStickUpButton, ui->LStickDownButton, ui->LStickLeftButton, ui->LStickRightButton,
- ui->RStickUpButton, ui->LStickDownButton, ui->LStickLeftButton, ui->RStickRightButton};
-
- if (event->type() == QEvent::Wheel) {
- QWheelEvent* wheelEvent = static_cast(event);
- if (pressedKeys.size() < 3) {
- if (wheelEvent->angleDelta().y() > 5) {
- if (std::find(AxisList.begin(), AxisList.end(), MappingButton) ==
- AxisList.end()) {
- pressedKeys.insert(130, "mousewheelup");
- if (QApplication::keyboardModifiers() == Qt::NoModifier)
- emit PushKBMEvent();
- } else {
- QMessageBox::information(
- this, tr("Cannot set mapping"),
- tr("Mousewheel cannot be mapped to stick outputs"));
- }
- } else if (wheelEvent->angleDelta().y() < -5) {
- if (std::find(AxisList.begin(), AxisList.end(), MappingButton) ==
- AxisList.end()) {
- pressedKeys.insert(131, "mousewheeldown");
- if (QApplication::keyboardModifiers() == Qt::NoModifier)
- emit PushKBMEvent();
- } else {
- QMessageBox::information(
- this, tr("Cannot set mapping"),
- tr("Mousewheel cannot be mapped to stick outputs"));
- }
- }
- if (wheelEvent->angleDelta().x() > 5) {
- if (std::find(AxisList.begin(), AxisList.end(), MappingButton) ==
- AxisList.end()) {
- // QT changes scrolling to horizontal for all widgets with the alt modifier
- QApplication::keyboardModifiers() & Qt::AltModifier
- ? pressedKeys.insert(130, "mousewheelup")
- : pressedKeys.insert(133, "mousewheelright");
- if (QApplication::keyboardModifiers() == Qt::NoModifier)
- emit PushKBMEvent();
- } else {
- QMessageBox::information(
- this, tr("Cannot set mapping"),
- tr("Mousewheel cannot be mapped to stick outputs"));
- }
- } else if (wheelEvent->angleDelta().x() < -5) {
- if (std::find(AxisList.begin(), AxisList.end(), MappingButton) ==
- AxisList.end()) {
- QApplication::keyboardModifiers() & Qt::AltModifier
- ? pressedKeys.insert(131, "mousewheeldown")
- : pressedKeys.insert(132, "mousewheelleft");
- if (QApplication::keyboardModifiers() == Qt::NoModifier)
- emit PushKBMEvent();
- } else {
- QMessageBox::information(
- this, tr("Cannot set mapping"),
- tr("Mousewheel cannot be mapped to stick outputs"));
- }
- }
- }
- }
-
- if (event->type() == QEvent::KeyRelease || event->type() == QEvent::MouseButtonRelease)
- emit PushKBMEvent();
- }
- return QDialog::eventFilter(obj, event);
-}
-
-KBMSettings::~KBMSettings() {}
diff --git a/src/qt_gui/kbm_gui.h b/src/qt_gui/kbm_gui.h
deleted file mode 100644
index e16b83d56..000000000
--- a/src/qt_gui/kbm_gui.h
+++ /dev/null
@@ -1,75 +0,0 @@
-// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-#include
-#include "game_info.h"
-
-// macros > declaring constants
-// also, we were only using one counterpart
-#ifdef _WIN32
-#define LCTRL_KEY 29
-#define LALT_KEY 56
-#define LSHIFT_KEY 42
-#else
-#define LCTRL_KEY 37
-#define LALT_KEY 64
-#define LSHIFT_KEY 50
-#endif
-
-namespace Ui {
-class KBMSettings;
-}
-
-class KBMSettings : public QDialog {
- Q_OBJECT
-public:
- explicit KBMSettings(std::shared_ptr game_info_get, bool GameRunning,
- std::string GameRunningSerial, QWidget* parent = nullptr);
- ~KBMSettings();
-
-signals:
- void PushKBMEvent();
-
-private Q_SLOTS:
- void SaveKBMConfig(bool CloseOnSave);
- void SetDefault();
- void CheckMapping(QPushButton*& button);
- void StartTimer(QPushButton*& button);
- void onHelpClicked();
-
-private:
- std::unique_ptr ui;
- std::shared_ptr m_game_info;
-
- bool eventFilter(QObject* obj, QEvent* event) override;
- void ButtonConnects();
- void SetUIValuestoMappings(std::string config_id);
- void GetGameTitle();
- void DisableMappingButtons();
- void EnableMappingButtons();
- void SetMapping(QString input);
- void Cleanup();
-
- std::string RunningGameSerial;
- QMap pressedKeys;
- bool GameRunning;
- bool EnableMapping = false;
- bool MappingCompleted = false;
- bool HelpWindowOpen = false;
- QString mapping;
- int MappingTimer;
- QTimer* timer;
- QPushButton* MappingButton;
- QList ButtonsList;
- std::string config_id;
- const std::vector ControllerInputs = {
- "cross", "circle", "square", "triangle", "l1",
- "r1", "l2", "r2", "l3",
-
- "r3", "options", "pad_up",
-
- "pad_down",
-
- "pad_left", "pad_right", "axis_left_x", "axis_left_y", "axis_right_x",
- "axis_right_y", "back"};
-};
diff --git a/src/qt_gui/kbm_gui.ui b/src/qt_gui/kbm_gui.ui
deleted file mode 100644
index 67aa34ce7..000000000
--- a/src/qt_gui/kbm_gui.ui
+++ /dev/null
@@ -1,2118 +0,0 @@
-
-
-
- KBMSettings
-
-
- Qt::WindowModality::WindowModal
-
-
-
- 0
- 0
- 1235
- 842
-
-
-
-
- 0
- 0
-
-
-
- Qt::FocusPolicy::StrongFocus
-
-
- Configure Controls
-
-
- true
-
-
- -
-
-
- Qt::FocusPolicy::NoFocus
-
-
- true
-
-
-
-
- 0
- 0
- 1215
- 792
-
-
-
-
-
- 0
- 0
- 1211
- 800
-
-
-
-
-
-
-
- 5
-
-
-
-
-
- true
-
-
-
- 0
- 0
-
-
-
-
- 16777215
- 16777215
-
-
-
- D-Pad
-
-
-
- 6
-
-
- 5
-
-
- 5
-
-
- 5
-
-
- 5
-
-
-
-
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 0
-
-
-
-
-
-
- 160
- 0
-
-
-
-
- 0
- 16777215
-
-
-
- Up
-
-
- Qt::AlignmentFlag::AlignCenter
-
-
-
-
-
-
- Qt::FocusPolicy::NoFocus
-
-
- unmapped
-
-
-
-
-
-
-
-
-
- -
-
-
-
-
-
-
- 160
- 0
-
-
-
- Left
-
-
- Qt::AlignmentFlag::AlignCenter
-
-
-
- 5
-
-
- 5
-
-
- 5
-
-
- 5
-
-
-
-
-
- Qt::FocusPolicy::NoFocus
-
-
- unmapped
-
-
-
-
-
-
- -
-
-
-
- 160
- 0
-
-
-
- Right
-
-
- Qt::AlignmentFlag::AlignCenter
-
-
-
- 5
-
-
- 5
-
-
- 5
-
-
- 5
-
-
-
-
-
- Qt::FocusPolicy::NoFocus
-
-
- unmapped
-
-
-
-
-
-
-
-
- -
-
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 0
-
-
-
-
-
-
- 160
- 0
-
-
-
-
- 124
- 16777215
-
-
-
- Down
-
-
- Qt::AlignmentFlag::AlignCenter
-
-
-
-
-
-
- Qt::FocusPolicy::NoFocus
-
-
- unmapped
-
-
-
-
-
-
-
-
-
-
-
-
- -
-
-
- Qt::Orientation::Vertical
-
-
- QSizePolicy::Policy::Maximum
-
-
-
- 20
- 40
-
-
-
-
- -
-
-
-
- 0
- 0
-
-
-
-
- 344
- 16777215
-
-
-
- Left Analog Halfmode
-
-
-
-
-
-
- Qt::FocusPolicy::NoFocus
-
-
- unmapped
-
-
-
- -
-
-
-
- true
-
-
-
- hold to move left stick at half-speed
-
-
-
-
-
-
- -
-
-
-
- 0
- 0
-
-
-
-
- 0
- 0
-
-
-
- Left Stick
-
-
-
- 5
-
-
- 5
-
-
- 5
-
-
- 5
-
-
-
-
-
-
- 16777215
- 2121
-
-
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 0
-
-
-
-
-
-
- 160
- 0
-
-
-
-
- 124
- 16777215
-
-
-
- Up
-
-
- Qt::AlignmentFlag::AlignCenter
-
-
-
-
-
-
- Qt::FocusPolicy::NoFocus
-
-
- unmapped
-
-
-
-
-
-
-
-
-
- -
-
-
-
-
-
-
- 160
- 0
-
-
-
- Left
-
-
- Qt::AlignmentFlag::AlignCenter
-
-
-
- 5
-
-
- 5
-
-
- 5
-
-
- 5
-
-
-
-
-
- Qt::FocusPolicy::NoFocus
-
-
- unmapped
-
-
-
-
-
-
- -
-
-
-
- 160
- 0
-
-
-
-
- 179
- 16777215
-
-
-
- Right
-
-
- Qt::AlignmentFlag::AlignCenter
-
-
-
- 5
-
-
- 5
-
-
- 5
-
-
- 5
-
-
-
-
-
- Qt::FocusPolicy::NoFocus
-
-
- unmapped
-
-
-
-
-
-
-
-
- -
-
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 0
-
-
-
-
-
-
- 160
- 0
-
-
-
-
- 124
- 21212
-
-
-
- Down
-
-
- Qt::AlignmentFlag::AlignCenter
-
-
-
-
-
-
- Qt::FocusPolicy::NoFocus
-
-
- unmapped
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- -
-
-
- 0
-
-
-
-
-
-
- 0
- 0
-
-
-
-
- 0
- 0
-
-
-
-
- 12
- true
-
-
-
- Config Selection
-
-
- Qt::AlignmentFlag::AlignCenter
-
-
-
-
-
-
-
-
-
-
- 9
- false
-
-
-
- Qt::FocusPolicy::NoFocus
-
-
-
-
-
- -1
-
-
- Common Config
-
-
-
- -
-
-
-
- 10
- true
-
-
-
- Common Config
-
-
- Qt::AlignmentFlag::AlignCenter
-
-
- true
-
-
-
-
-
- -
-
-
-
-
-
-
- 0
- 0
-
-
-
-
- 9
- false
-
-
-
- Qt::FocusPolicy::NoFocus
-
-
- Use per-game configs
-
-
-
- -
-
-
-
- 9
- false
-
-
-
- Qt::FocusPolicy::NoFocus
-
-
- Copy from Common Config
-
-
-
-
-
-
-
-
- -
-
-
- 0
-
-
-
-
-
-
-
-
-
- 0
- 0
-
-
-
-
- 160
- 0
-
-
-
- L1
-
-
- Qt::AlignmentFlag::AlignCenter
-
-
-
- 5
-
-
- 5
-
-
- 5
-
-
- 5
-
-
-
-
-
-
- 0
- 0
-
-
-
- Qt::FocusPolicy::NoFocus
-
-
- unmapped
-
-
-
-
-
-
- -
-
-
-
- 0
- 0
-
-
-
-
- 160
- 0
-
-
-
- L2
-
-
- Qt::AlignmentFlag::AlignCenter
-
-
-
- 5
-
-
- 5
-
-
- 5
-
-
- 5
-
-
-
-
-
-
- 0
- 0
-
-
-
- Qt::FocusPolicy::NoFocus
-
-
- unmapped
-
-
-
-
-
-
-
-
- -
-
-
-
-
-
-
- 0
- 0
-
-
-
-
- 0
- 0
-
-
-
-
- true
-
-
-
-
-
-
-
-
-
-
-
- 0
- 0
-
-
-
-
- 0
- 24
-
-
-
- Qt::FocusPolicy::NoFocus
-
-
- Text Editor
-
-
-
- -
-
-
-
- 0
- 0
-
-
-
-
- 0
- 24
-
-
-
- Qt::FocusPolicy::NoFocus
-
-
- Help
-
-
-
-
-
-
- -
-
-
-
- 0
- 0
-
-
-
-
- 160
- 0
-
-
-
- Options
-
-
- Qt::AlignmentFlag::AlignCenter
-
-
-
- 5
-
-
- 5
-
-
- 5
-
-
- 5
-
-
-
-
-
-
- 0
- 0
-
-
-
- Qt::FocusPolicy::NoFocus
-
-
- unmapped
-
-
-
-
-
-
-
-
- -
-
-
-
-
-
-
- 0
- 0
-
-
-
-
- 160
- 0
-
-
-
- R1
-
-
- Qt::AlignmentFlag::AlignCenter
-
-
-
- 5
-
-
- 5
-
-
- 5
-
-
- 5
-
-
-
-
-
-
- 0
- 0
-
-
-
- Qt::FocusPolicy::NoFocus
-
-
- unmapped
-
-
-
-
-
-
- -
-
-
-
- 0
- 0
-
-
-
-
- 160
- 0
-
-
-
- R2
-
-
- Qt::AlignmentFlag::AlignCenter
-
-
-
- 5
-
-
- 5
-
-
- 5
-
-
- 5
-
-
-
-
-
-
- 0
- 0
-
-
-
- Qt::FocusPolicy::NoFocus
-
-
- unmapped
-
-
-
-
-
-
-
-
-
-
- -
-
-
-
- 0
- 200
-
-
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 0
-
-
-
-
-
-
- 424
- 250
-
-
-
- :/images/KBM.png
-
-
- true
-
-
- Qt::AlignmentFlag::AlignBottom|Qt::AlignmentFlag::AlignHCenter
-
-
-
-
-
-
- -
-
-
- 0
-
-
- QLayout::SizeConstraint::SetDefaultConstraint
-
-
-
-
-
-
-
-
-
- 0
- 0
-
-
-
-
- 160
- 0
-
-
-
- L3
-
-
- Qt::AlignmentFlag::AlignCenter
-
-
-
- 5
-
-
- 5
-
-
- 5
-
-
- 5
-
-
-
-
-
-
- 0
- 0
-
-
-
- Qt::FocusPolicy::NoFocus
-
-
- unmapped
-
-
-
-
-
-
- -
-
-
- Touchpad Left
-
-
- Qt::AlignmentFlag::AlignCenter
-
-
-
-
-
-
- unmapped
-
-
-
-
-
-
-
-
- -
-
-
-
-
-
-
- 0
- 0
-
-
-
-
- 0
- 0
-
-
-
- Mouse to Joystick
-
-
- Qt::AlignmentFlag::AlignCenter
-
-
-
-
-
-
- Qt::FocusPolicy::NoFocus
-
-
-
- -
-
-
-
- true
-
-
-
- *press F7 ingame to activate
-
-
- true
-
-
-
-
-
-
- -
-
-
- Touchpad Center
-
-
- Qt::AlignmentFlag::AlignCenter
-
-
-
-
-
-
- unmapped
-
-
-
-
-
-
-
-
- -
-
-
-
-
-
-
- 0
- 0
-
-
-
-
- 160
- 0
-
-
-
- R3
-
-
- Qt::AlignmentFlag::AlignCenter
-
-
-
- 5
-
-
- 5
-
-
- 5
-
-
- 5
-
-
-
-
-
-
- 0
- 0
-
-
-
- Qt::FocusPolicy::NoFocus
-
-
- unmapped
-
-
-
-
-
-
- -
-
-
-
- 0
- 0
-
-
-
-
- 160
- 0
-
-
-
- Touchpad Right
-
-
- Qt::AlignmentFlag::AlignCenter
-
-
-
-
-
-
-
- 0
- 0
-
-
-
- Qt::FocusPolicy::NoFocus
-
-
- unmapped
-
-
-
-
-
-
-
-
-
-
- -
-
-
-
-
-
-
-
-
-
- 0
- 0
-
-
-
-
- false
-
-
-
- Mouse Movement Parameters
-
-
-
-
-
-
-
-
-
-
- false
-
-
-
- Deadzone Offset (def 0.50):
-
-
-
- -
-
-
-
- 0
- 0
-
-
-
- Qt::FocusPolicy::NoFocus
-
-
- 100
-
-
- 50
-
-
- Qt::Orientation::Horizontal
-
-
-
- -
-
-
-
- 0
- 0
-
-
-
-
- 30
- 16777215
-
-
-
- 0.50
-
-
-
-
-
- -
-
-
-
-
-
-
- false
-
-
-
- Speed Multiplier (def 1.0):
-
-
-
- -
-
-
-
- 0
- 0
-
-
-
- Qt::FocusPolicy::NoFocus
-
-
- 1
-
-
- 50
-
-
- 5
-
-
- 10
-
-
- Qt::Orientation::Horizontal
-
-
-
- -
-
-
-
- 0
- 0
-
-
-
-
- 30
- 16777215
-
-
-
- 1.0
-
-
-
-
-
- -
-
-
-
-
-
-
- false
-
-
-
- Speed Offset (def 0.125):
-
-
-
- -
-
-
-
- 0
- 0
-
-
-
- Qt::FocusPolicy::NoFocus
-
-
- 1000
-
-
- 100
-
-
- 125
-
-
- Qt::Orientation::Horizontal
-
-
-
- -
-
-
-
- 30
- 16777215
-
-
-
- 0.125
-
-
-
-
-
- -
-
-
-
- 0
- 0
-
-
-
-
- true
- false
-
-
-
- note: click Help Button/Special Keybindings for more information
-
-
-
-
-
-
-
-
-
-
-
-
- -
-
-
- 5
-
-
-
-
-
-
- 0
- 0
-
-
-
- Face Buttons
-
-
-
- 5
-
-
- 5
-
-
- 5
-
-
- 5
-
-
-
-
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 0
-
-
-
-
-
-
- 0
- 0
-
-
-
-
- 160
- 0
-
-
-
-
- 0
- 16777215
-
-
-
- Triangle
-
-
- Qt::AlignmentFlag::AlignCenter
-
-
-
-
-
-
- Qt::FocusPolicy::NoFocus
-
-
- unmapped
-
-
-
-
-
-
-
-
-
- -
-
-
-
-
-
-
- 160
- 0
-
-
-
- Square
-
-
- Qt::AlignmentFlag::AlignCenter
-
-
-
- 5
-
-
- 5
-
-
- 5
-
-
- 5
-
-
-
-
-
- Qt::FocusPolicy::NoFocus
-
-
- unmapped
-
-
-
-
-
-
- -
-
-
-
- 160
- 0
-
-
-
- Circle
-
-
- Qt::AlignmentFlag::AlignCenter
-
-
-
- 5
-
-
- 5
-
-
- 5
-
-
- 5
-
-
-
-
-
- Qt::FocusPolicy::NoFocus
-
-
- unmapped
-
-
-
-
-
-
-
-
- -
-
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 0
-
-
-
-
-
-
- 160
- 0
-
-
-
-
- 124
- 16777215
-
-
-
- Cross
-
-
- Qt::AlignmentFlag::AlignCenter
-
-
-
-
-
-
- Qt::FocusPolicy::NoFocus
-
-
- unmapped
-
-
-
-
-
-
-
-
-
-
-
-
- -
-
-
- Qt::Orientation::Vertical
-
-
- QSizePolicy::Policy::Maximum
-
-
-
- 20
- 40
-
-
-
-
- -
-
-
-
- 0
- 0
-
-
-
-
- 344
- 16777215
-
-
-
- Right Analog Halfmode
-
-
-
-
-
-
- Qt::FocusPolicy::NoFocus
-
-
- unmapped
-
-
-
- -
-
-
-
- true
-
-
-
- hold to move right stick at half-speed
-
-
-
-
-
-
- -
-
-
-
- 0
- 0
-
-
-
-
- 0
- 0
-
-
-
- Right Stick
-
-
-
- 5
-
-
- 5
-
-
- 5
-
-
- 5
-
-
-
-
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 0
-
-
-
-
-
-
- 0
- 0
-
-
-
-
- 160
- 0
-
-
-
-
- 124
- 1231321
-
-
-
- Up
-
-
- Qt::AlignmentFlag::AlignCenter
-
-
-
-
-
-
-
- 0
- 0
-
-
-
- Qt::FocusPolicy::NoFocus
-
-
- unmapped
-
-
-
-
-
-
-
-
-
- -
-
-
-
-
-
-
- 160
- 0
-
-
-
- Left
-
-
- Qt::AlignmentFlag::AlignCenter
-
-
-
- 5
-
-
- 5
-
-
- 5
-
-
- 5
-
-
-
-
-
- Qt::FocusPolicy::NoFocus
-
-
- unmapped
-
-
-
-
-
-
- -
-
-
-
- 160
- 0
-
-
-
- Right
-
-
- Qt::AlignmentFlag::AlignCenter
-
-
-
- 5
-
-
- 5
-
-
- 5
-
-
- 5
-
-
-
-
-
- Qt::FocusPolicy::NoFocus
-
-
- unmapped
-
-
-
-
-
-
-
-
- -
-
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 0
-
-
-
-
-
-
- 160
- 0
-
-
-
-
- 124
- 2121
-
-
-
- Down
-
-
- Qt::AlignmentFlag::AlignCenter
-
-
-
-
-
-
- Qt::FocusPolicy::NoFocus
-
-
- unmapped
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- -
-
-
- QDialogButtonBox::StandardButton::Apply|QDialogButtonBox::StandardButton::Cancel|QDialogButtonBox::StandardButton::RestoreDefaults|QDialogButtonBox::StandardButton::Save
-
-
- false
-
-
-
-
-
-
-
-
-
-
diff --git a/src/qt_gui/kbm_help_dialog.cpp b/src/qt_gui/kbm_help_dialog.cpp
deleted file mode 100644
index 1c28f6895..000000000
--- a/src/qt_gui/kbm_help_dialog.cpp
+++ /dev/null
@@ -1,306 +0,0 @@
-// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-#include "kbm_help_dialog.h"
-
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-
-ExpandableSection::ExpandableSection(const QString& title, const QString& content,
- QWidget* parent = nullptr)
- : QWidget(parent) {
- QVBoxLayout* layout = new QVBoxLayout(this);
-
- // Button to toggle visibility of content
- toggleButton = new QPushButton(title);
- layout->addWidget(toggleButton);
-
- // QTextBrowser for content (initially hidden)
- contentBrowser = new QTextBrowser();
- contentBrowser->setPlainText(content);
- contentBrowser->setVisible(false);
-
- // Remove scrollbars from QTextBrowser
- contentBrowser->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
- contentBrowser->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
-
- // Set size policy to allow vertical stretching only
- contentBrowser->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum);
-
- // Calculate and set initial height based on content
- updateContentHeight();
-
- layout->addWidget(contentBrowser);
-
- // Connect button click to toggle visibility
- connect(toggleButton, &QPushButton::clicked, [this]() {
- contentBrowser->setVisible(!contentBrowser->isVisible());
- if (contentBrowser->isVisible()) {
- updateContentHeight(); // Update height when expanding
- }
- emit expandedChanged(); // Notify for layout adjustments
- });
-
- // Connect to update height if content changes
- connect(contentBrowser->document(), &QTextDocument::contentsChanged, this,
- &ExpandableSection::updateContentHeight);
-
- // Minimal layout settings for spacing
- layout->setSpacing(2);
- layout->setContentsMargins(0, 0, 0, 0);
-}
-
-void HelpDialog::closeEvent(QCloseEvent* event) {
- *help_open_ptr = false;
- close();
-}
-void HelpDialog::reject() {
- *help_open_ptr = false;
- close();
-}
-
-HelpDialog::HelpDialog(bool* open_flag, QWidget* parent) : QDialog(parent) {
- help_open_ptr = open_flag;
- // Main layout for the help dialog
- QVBoxLayout* mainLayout = new QVBoxLayout(this);
-
- // Container widget for the scroll area
- QWidget* containerWidget = new QWidget;
- QVBoxLayout* containerLayout = new QVBoxLayout(containerWidget);
-
- // Add expandable sections to container layout
- auto* quickstartSection = new ExpandableSection(tr("Quickstart"), quickstart());
- auto* syntaxSection = new ExpandableSection(tr("Syntax"), syntax());
- auto* bindingsSection = new ExpandableSection(tr("Keybindings"), bindings());
- auto* specialSection = new ExpandableSection(tr("Special Bindings"), special());
- auto* faqSection = new ExpandableSection(tr("FAQ"), faq());
-
- containerLayout->addWidget(quickstartSection);
- containerLayout->addWidget(syntaxSection);
- containerLayout->addWidget(bindingsSection);
- containerLayout->addWidget(specialSection);
- containerLayout->addWidget(faqSection);
- containerLayout->addStretch(1);
-
- // Scroll area wrapping the container
- QScrollArea* scrollArea = new QScrollArea;
- scrollArea->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
- scrollArea->setWidgetResizable(true);
- scrollArea->setWidget(containerWidget);
-
- // Add the scroll area to the main dialog layout
- mainLayout->addWidget(scrollArea);
- setLayout(mainLayout);
-
- // Minimum size for the dialog
- setMinimumSize(500, 400);
-
- // Re-adjust dialog layout when any section expands/collapses
- connect(quickstartSection, &ExpandableSection::expandedChanged, this, &HelpDialog::adjustSize);
- connect(faqSection, &ExpandableSection::expandedChanged, this, &HelpDialog::adjustSize);
- connect(syntaxSection, &ExpandableSection::expandedChanged, this, &HelpDialog::adjustSize);
- connect(specialSection, &ExpandableSection::expandedChanged, this, &HelpDialog::adjustSize);
- connect(bindingsSection, &ExpandableSection::expandedChanged, this, &HelpDialog::adjustSize);
-}
-
-// Helper functions that store the text contents for each tab inb the HelpDialog menu
-QString HelpDialog::quickstart() {
- return R"(
-In this section, you will find information about the project and its features, as well as help setting up your ideal setup.
-To view the config file's syntax, check out the Syntax tab, for keybind names, visit Normal Keybinds and Special Bindings, and if you are here to view emulator-wide keybinds, you can find it in the FAQ section.
-)";
-}
-
-QString HelpDialog::syntax() {
- return R"(
-Below is the file format for mouse, keyboard, and controller inputs:
-
-Rules:
-- You can bind up to 3 inputs to one button.
-- Adding '#' at the beginning of a line creates a comment.
-- Extra whitespace doesn't affect input.
-