diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml deleted file mode 100644 index 0aa9a7f2..00000000 --- a/.github/workflows/build.yml +++ /dev/null @@ -1,302 +0,0 @@ -name: Build Cemu - -on: - workflow_call: - inputs: - next_version_major: - required: false - type: string - next_version_minor: - required: false - type: string - -env: - VCPKG_ROOT: "${{github.workspace}}/dependencies/vcpkg" - # No nuget binary cache: this fork does not publish packages, and the one it - # had was 537 vcpkg artifacts nothing here reads - the libretro core build in - # libretro.yml does not use it. - VCPKG_BINARY_SOURCES: 'clear' - VCPKG_FORCE_DOWNLOADED_BINARIES: true - -jobs: - build-ubuntu: - continue-on-error: true - strategy: - fail-fast: false - matrix: - include: - - os: ubuntu-22.04 - arch: x64 - - os: ubuntu-22.04-arm - arch: arm - - name: build-ubuntu-${{ matrix.arch }} - runs-on: ${{ matrix.os }} - steps: - - name: "Checkout repo" - uses: actions/checkout@v6 - with: - submodules: "recursive" - fetch-depth: 0 - - - name: Setup release mode parameters - run: | - echo "BUILD_MODE=release" >> $GITHUB_ENV - echo "BUILD_FLAGS=" >> $GITHUB_ENV - echo "Build mode is release" - - - name: Setup build flags for version - if: ${{ inputs.next_version_major != '' }} - run: | - echo "[INFO] Version ${{ inputs.next_version_major }}.${{ inputs.next_version_minor }}" - echo "BUILD_FLAGS=${{ env.BUILD_FLAGS }} -DEMULATOR_VERSION_MAJOR=${{ inputs.next_version_major }} -DEMULATOR_VERSION_MINOR=${{ inputs.next_version_minor }}" >> $GITHUB_ENV - - - name: "Install system dependencies" - run: | - sudo apt update -qq - sudo apt install -y clang-15 cmake freeglut3-dev libgcrypt20-dev libglm-dev libgtk-3-dev libpulse-dev libsecret-1-dev libsystemd-dev libudev-dev nasm ninja-build libbluetooth-dev - - - name: "Bootstrap vcpkg" - run: | - bash ./dependencies/vcpkg/bootstrap-vcpkg.sh - - - name: 'Setup NuGet Credentials for vcpkg' - shell: 'bash' - run: | - mono `./dependencies/vcpkg/vcpkg fetch nuget | tail -n 1` \ - sources add \ - -source "https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json" \ - -storepasswordincleartext \ - -name "GitHub" \ - -username "${{ github.repository_owner }}" \ - -password "${{ secrets.GITHUB_TOKEN }}" - mono `./dependencies/vcpkg/vcpkg fetch nuget | tail -n 1` \ - setapikey "${{ secrets.GITHUB_TOKEN }}" \ - -source "https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json" - - - name: "cmake" - run: | - cmake -S . -B build ${{ env.BUILD_FLAGS }} -DCMAKE_BUILD_TYPE=${{ env.BUILD_MODE }} -DCMAKE_C_COMPILER=/usr/bin/clang-15 -DCMAKE_CXX_COMPILER=/usr/bin/clang++-15 -G Ninja -DCMAKE_MAKE_PROGRAM=/usr/bin/ninja - - - name: "Build Cemu" - run: | - cmake --build build - - - name: Prepare artifact - run: mv bin/Cemu_release bin/Cemu - - - name: Upload artifact - uses: actions/upload-artifact@v6 - with: - name: cemu-bin-linux-${{ matrix.arch }} - path: ./bin/Cemu - - build-appimage: - strategy: - fail-fast: false - matrix: - include: - - os: ubuntu-22.04 - arch: x64 - - os: ubuntu-22.04-arm - arch: arm - - name: build-appimage-${{ matrix.arch }} - runs-on: ${{ matrix.os }} - needs: build-ubuntu - steps: - - name: Checkout Upstream Repo - uses: actions/checkout@v6 - - - uses: actions/download-artifact@v8 - with: - name: cemu-bin-linux-${{ matrix.arch }} - path: bin - - - name: "Install system dependencies" - run: | - sudo apt update -qq - sudo apt install -y clang-15 cmake freeglut3-dev libgcrypt20-dev libglm-dev libgtk-3-dev libpulse-dev libsecret-1-dev libsystemd-dev nasm ninja-build appstream libbluetooth-dev - - - name: "Build AppImage" - run: | - export LD_LIBRARY_PATH="/usr/local/lib:$LD_LIBRARY_PATH" - export DEPLOY_GTK_VERSION=3 - dist/linux/appimage.sh ${{ runner.arch }} - - - name: Upload artifact - uses: actions/upload-artifact@v6 - with: - name: cemu-appimage-${{ matrix.arch }} - path: artifacts - - build-windows: - runs-on: windows-2022 - steps: - - name: "Checkout repo" - uses: actions/checkout@v6 - with: - submodules: "recursive" - - - name: Setup release mode parameters - run: | - echo "BUILD_MODE=release" | Out-File -FilePath $Env:GITHUB_ENV -Encoding utf8 -Append - echo "BUILD_FLAGS=" | Out-File -FilePath $Env:GITHUB_ENV -Encoding utf8 -Append - echo "Build mode is release" - - - name: Setup build flags for version - if: ${{ inputs.next_version_major != '' }} - run: | - echo "[INFO] Version ${{ inputs.next_version_major }}.${{ inputs.next_version_minor }}" - echo "BUILD_FLAGS=${{ env.BUILD_FLAGS }} -DEMULATOR_VERSION_MAJOR=${{ inputs.next_version_major }} -DEMULATOR_VERSION_MINOR=${{ inputs.next_version_minor }}" | Out-File -FilePath $Env:GITHUB_ENV -Encoding utf8 -Append - - - name: "Setup cmake" - uses: jwlawson/actions-setup-cmake@v2 - with: - cmake-version: '3.29.0' - - - name: "Bootstrap vcpkg" - run: | - ./dependencies/vcpkg/bootstrap-vcpkg.bat - - - name: 'Setup NuGet Credentials for vcpkg' - shell: 'bash' - run: | - `./dependencies/vcpkg/vcpkg.exe fetch nuget | tail -n 1` \ - sources add \ - -source "https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json" \ - -storepasswordincleartext \ - -name "GitHub" \ - -username "${{ github.repository_owner }}" \ - -password "${{ secrets.GITHUB_TOKEN }}" - `./dependencies/vcpkg/vcpkg.exe fetch nuget | tail -n 1` \ - setapikey "${{ secrets.GITHUB_TOKEN }}" \ - -source "https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json" - - - name: "cmake" - run: | - mkdir -p build - cd build - echo "[INFO] BUILD_FLAGS: ${{ env.BUILD_FLAGS }}" - echo "[INFO] BUILD_MODE: ${{ env.BUILD_MODE }}" - cmake .. ${{ env.BUILD_FLAGS }} -DCMAKE_BUILD_TYPE=${{ env.BUILD_MODE }} -DVCPKG_INSTALL_OPTIONS="--clean-after-build" - - - name: "Build Cemu" - run: | - cd build - cmake --build . --config ${{ env.BUILD_MODE }} - - - name: Prepare artifact - run: Rename-Item bin/Cemu_release.exe Cemu.exe - - - name: Build NSIS Installer - shell: cmd - run: | - cd src\resource - makensis /DPRODUCT_VERSION=${{ inputs.next_version_major }}.${{ inputs.next_version_minor }} installer.nsi - - - name: Upload artifact - uses: actions/upload-artifact@v6 - with: - name: cemu-bin-windows-x64 - path: ./bin/Cemu.exe - - - name: Upload NSIS Installer - uses: actions/upload-artifact@v6 - with: - name: cemu-installer-windows-x64 - path: ./src/resource/cemu-${{ inputs.next_version_major }}.${{ inputs.next_version_minor }}-windows-x64-installer.exe - - build-macos: - runs-on: macos-14 - strategy: - fail-fast: false - matrix: - arch: [x86_64, arm64] - steps: - - name: "Checkout repo" - uses: actions/checkout@v6 - with: - submodules: "recursive" - - - name: Setup release mode parameters - run: | - echo "BUILD_MODE=release" >> $GITHUB_ENV - echo "BUILD_FLAGS=" >> $GITHUB_ENV - echo "Build mode is release" - - - name: Setup build flags for version - if: ${{ inputs.next_version_major != '' }} - run: | - echo "[INFO] Version ${{ inputs.next_version_major }}.${{ inputs.next_version_minor }}" - echo "BUILD_FLAGS=${{ env.BUILD_FLAGS }} -DEMULATOR_VERSION_MAJOR=${{ inputs.next_version_major }} -DEMULATOR_VERSION_MINOR=${{ inputs.next_version_minor }}" >> $GITHUB_ENV - - - name: "Install system dependencies" - run: | - # Removing azure/bicep and aws to silence warnings in Github Actions - brew uninstall bicep - brew untap aws/tap azure/bicep - brew update - brew install nasm automake libtool - - - name: "Install molten-vk" - run: | - curl -L -O https://github.com/KhronosGroup/MoltenVK/releases/download/v1.4.1/MoltenVK-macos-privateapi.tar - tar xf MoltenVK-macos-privateapi.tar - sudo mkdir -p /usr/local/lib - sudo cp MoltenVK/MoltenVK/dynamic/dylib/macOS/libMoltenVK.dylib /usr/local/lib - - - name: "Setup cmake" - uses: jwlawson/actions-setup-cmake@v2 - with: - cmake-version: '3.29.0' - - - name: "Bootstrap vcpkg" - run: | - bash ./dependencies/vcpkg/bootstrap-vcpkg.sh - - - name: 'Setup NuGet Credentials for vcpkg' - shell: 'bash' - run: | - mono `./dependencies/vcpkg/vcpkg fetch nuget | tail -n 1` \ - sources add \ - -source "https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json" \ - -storepasswordincleartext \ - -name "GitHub" \ - -username "${{ github.repository_owner }}" \ - -password "${{ secrets.GITHUB_TOKEN }}" - mono `./dependencies/vcpkg/vcpkg fetch nuget | tail -n 1` \ - setapikey "${{ secrets.GITHUB_TOKEN }}" \ - -source "https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json" - - - name: "cmake" - run: | - mkdir build - cd build - cmake .. ${{ env.BUILD_FLAGS }} \ - -DCMAKE_BUILD_TYPE=${{ env.BUILD_MODE }} \ - -DCMAKE_OSX_ARCHITECTURES=${{ matrix.arch }} \ - -DMACOS_BUNDLE=ON \ - -G Ninja - - - name: "Build Cemu" - run: | - cmake --build build - - - name: Prepare artifact - run: | - mkdir bin/Cemu_app - mv bin/Cemu_release.app bin/Cemu_app/Cemu.app - mv bin/Cemu_app/Cemu.app/Contents/MacOS/Cemu_release bin/Cemu_app/Cemu.app/Contents/MacOS/Cemu - sed -i '' 's/Cemu_release/Cemu/g' bin/Cemu_app/Cemu.app/Contents/Info.plist - codesign --entitlements ${{github.workspace}}/src/resource/cemu.macos.entitlements --force --deep --preserve-metadata=entitlements,requirements,flags,runtime --sign - --timestamp --options runtime bin/Cemu_app/Cemu.app/Contents/MacOS/Cemu - ln -s /Applications bin/Cemu_app/Applications - hdiutil create ./bin/tmp.dmg -ov -volname "Cemu" -fs HFS+ -srcfolder "./bin/Cemu_app" - hdiutil convert ./bin/tmp.dmg -format UDZO -o bin/Cemu.dmg - rm bin/tmp.dmg - - - name: Upload artifact - uses: actions/upload-artifact@v6 - with: - name: cemu-bin-macos-${{ matrix.arch }} - path: ./bin/Cemu.dmg diff --git a/.github/workflows/build_check.yml b/.github/workflows/build_check.yml deleted file mode 100644 index 5d24b0c6..00000000 --- a/.github/workflows/build_check.yml +++ /dev/null @@ -1,18 +0,0 @@ -name: Build check -on: - pull_request: - paths-ignore: - - "*.md" - types: - - opened - - synchronize - - reopened - push: - paths-ignore: - - "*.md" - branches: - - main - -jobs: - build: - uses: ./.github/workflows/build.yml diff --git a/.github/workflows/deploy_release.yml b/.github/workflows/deploy_release.yml deleted file mode 100644 index caa4a57a..00000000 --- a/.github/workflows/deploy_release.yml +++ /dev/null @@ -1,164 +0,0 @@ -name: Deploy release -on: - workflow_dispatch: - inputs: - changelog0: - description: 'Enter the changelog lines for this release. Each line is a feature / bullet point. Do not use dash.' - required: true - type: string - changelog1: - description: 'Feature 2' - required: false - type: string - changelog2: - description: 'Feature 3' - required: false - type: string - changelog3: - description: 'Feature 4' - required: false - type: string - changelog4: - description: 'Feature 5' - required: false - type: string - changelog5: - description: 'Feature 6' - required: false - type: string - changelog6: - description: 'Feature 7' - required: false - type: string - changelog7: - description: 'Feature 8' - required: false - type: string - changelog8: - description: 'Feature 9' - required: false - type: string - changelog9: - description: 'Feature 10' - required: false - type: string - -jobs: - calculate-version: - name: Calculate Version - uses: ./.github/workflows/determine_release_version.yml - call-release-build: - uses: ./.github/workflows/build.yml - needs: calculate-version - with: - next_version_major: ${{ needs.calculate-version.outputs.next_version_major }} - next_version_minor: ${{ needs.calculate-version.outputs.next_version_minor }} - deploy: - name: Deploy release - runs-on: ubuntu-22.04 - needs: [call-release-build, calculate-version] - steps: - - uses: actions/checkout@v6 - with: - fetch-depth: 0 - - - name: Generate changelog - id: generate_changelog - run: | - CHANGELOG="" - if [ -n "${{ github.event.inputs.changelog0 }}" ]; then CHANGELOG="$CHANGELOG- ${{ github.event.inputs.changelog0 }}\n"; fi - if [ -n "${{ github.event.inputs.changelog1 }}" ]; then CHANGELOG="$CHANGELOG- ${{ github.event.inputs.changelog1 }}\n"; fi - if [ -n "${{ github.event.inputs.changelog2 }}" ]; then CHANGELOG="$CHANGELOG- ${{ github.event.inputs.changelog2 }}\n"; fi - if [ -n "${{ github.event.inputs.changelog3 }}" ]; then CHANGELOG="$CHANGELOG- ${{ github.event.inputs.changelog3 }}\n"; fi - if [ -n "${{ github.event.inputs.changelog4 }}" ]; then CHANGELOG="$CHANGELOG- ${{ github.event.inputs.changelog4 }}\n"; fi - if [ -n "${{ github.event.inputs.changelog5 }}" ]; then CHANGELOG="$CHANGELOG- ${{ github.event.inputs.changelog5 }}\n"; fi - if [ -n "${{ github.event.inputs.changelog6 }}" ]; then CHANGELOG="$CHANGELOG- ${{ github.event.inputs.changelog6 }}\n"; fi - if [ -n "${{ github.event.inputs.changelog7 }}" ]; then CHANGELOG="$CHANGELOG- ${{ github.event.inputs.changelog7 }}\n"; fi - if [ -n "${{ github.event.inputs.changelog8 }}" ]; then CHANGELOG="$CHANGELOG- ${{ github.event.inputs.changelog8 }}\n"; fi - if [ -n "${{ github.event.inputs.changelog9 }}" ]; then CHANGELOG="$CHANGELOG- ${{ github.event.inputs.changelog9 }}\n"; fi - echo -e "$CHANGELOG" - echo "RELEASE_BODY=$CHANGELOG" >> $GITHUB_ENV - - uses: actions/download-artifact@v8 - with: - name: cemu-bin-linux-x64 - path: cemu-bin-linux-x64 - - - uses: actions/download-artifact@v8 - with: - name: cemu-appimage-x64 - path: cemu-appimage-x64 - - - uses: actions/download-artifact@v8 - with: - name: cemu-bin-windows-x64 - path: cemu-bin-windows-x64 - - - uses: actions/download-artifact@v8 - with: - name: cemu-installer-windows-x64 - path: cemu-installer-windows-x64 - - - uses: actions/download-artifact@v8 - with: - pattern: cemu-bin-macos* - path: cemu-macos - - - name: Initialize - run: | - mkdir upload - sudo apt install zip - - - name: Set version dependent vars - run: | - echo "Version: ${{ needs.calculate-version.outputs.next_version }}" - echo "CEMU_FOLDER_NAME=Cemu_${{ needs.calculate-version.outputs.next_version }}" - echo "CEMU_VERSION=${{ needs.calculate-version.outputs.next_version }}" - echo "CEMU_FOLDER_NAME=Cemu_${{ needs.calculate-version.outputs.next_version }}" >> $GITHUB_ENV - echo "CEMU_VERSION=${{ needs.calculate-version.outputs.next_version }}" >> $GITHUB_ENV - - - name: Create release from windows-bin - run: | - ls ./ - ls ./bin/ - cp -R ./bin ./${{ env.CEMU_FOLDER_NAME }} - mv cemu-bin-windows-x64/Cemu.exe ./${{ env.CEMU_FOLDER_NAME }}/Cemu.exe - zip -9 -r upload/cemu-${{ env.CEMU_VERSION }}-windows-x64.zip ${{ env.CEMU_FOLDER_NAME }} - rm -r ./${{ env.CEMU_FOLDER_NAME }} - - - name: Create release from windows-installer - run: cp cemu-installer-windows-x64/cemu-${{ env.CEMU_VERSION }}-windows-x64-installer.exe upload/cemu-${{ env.CEMU_VERSION }}-windows-x64-installer.exe - - - name: Create appimage - run: | - VERSION=${{ env.CEMU_VERSION }} - echo "Cemu Version is $VERSION" - ls cemu-appimage-x64 - mv cemu-appimage-x64/Cemu-*-x86_64.AppImage upload/Cemu-$VERSION-x86_64.AppImage - - - name: Create release from linux-bin - run: | - ls ./ - ls ./bin/ - cp -R ./bin ./${{ env.CEMU_FOLDER_NAME }} - mv cemu-bin-linux-x64/Cemu ./${{ env.CEMU_FOLDER_NAME }}/Cemu - zip -9 -r upload/cemu-${{ env.CEMU_VERSION }}-ubuntu-22.04-x64.zip ${{ env.CEMU_FOLDER_NAME }} - rm -r ./${{ env.CEMU_FOLDER_NAME }} - - - name: Create release from macos-bin - run: | - cd cemu-macos - for bin_dir in cemu-bin-macos-*; do - arch="${bin_dir##cemu-bin-macos-}" - cp $bin_dir/Cemu.dmg ../upload/cemu-${{ env.CEMU_VERSION }}-macos-12-$arch.dmg - done - - - name: Create release - run: | - wget -O ghr.tar.gz https://github.com/tcnksm/ghr/releases/download/v0.15.0/ghr_v0.15.0_linux_amd64.tar.gz - tar xvzf ghr.tar.gz; rm ghr.tar.gz - echo "[INFO] Release tag: v${{ env.CEMU_VERSION }}" - CHANGELOG_UNESCAPED=$(printf "%s\n" "${{ env.RELEASE_BODY }}" | sed 's/\\n/\n/g') - RELEASE_BODY=$(printf "%s\n%s" \ - "**Changelog:**" \ - "$CHANGELOG_UNESCAPED") - ghr_v0.15.0_linux_amd64/ghr -draft -t ${{ secrets.GITHUB_TOKEN }} -n "Cemu ${{ env.CEMU_VERSION }}" -b "$RELEASE_BODY" "v${{ env.CEMU_VERSION }}" ./upload diff --git a/.github/workflows/determine_release_version.yml b/.github/workflows/determine_release_version.yml deleted file mode 100644 index a2efcd17..00000000 --- a/.github/workflows/determine_release_version.yml +++ /dev/null @@ -1,74 +0,0 @@ -name: Calculate Next Version from release history - -on: - workflow_dispatch: - workflow_call: - outputs: - next_version: - description: "The next semantic version" - value: ${{ jobs.calculate-version.outputs.next_version }} - next_version_major: - description: "The next semantic version (major)" - value: ${{ jobs.calculate-version.outputs.next_version_major }} - next_version_minor: - description: "The next semantic version (minor)" - value: ${{ jobs.calculate-version.outputs.next_version_minor }} - -jobs: - calculate-version: - runs-on: ubuntu-latest - outputs: - next_version: ${{ steps.calculate_next_version.outputs.next_version }} - next_version_major: ${{ steps.calculate_next_version.outputs.next_version_major }} - next_version_minor: ${{ steps.calculate_next_version.outputs.next_version_minor }} - steps: - - name: Checkout code - uses: actions/checkout@v6 - - - name: Get all releases - id: get_all_releases - run: | - # Fetch all releases and check for API errors - RESPONSE=$(curl -s -o response.json -w "%{http_code}" "https://api.github.com/repos/${{ github.repository }}/releases?per_page=100") - if [ "$RESPONSE" -ne 200 ]; then - echo "Failed to fetch releases. HTTP status: $RESPONSE" - cat response.json - exit 1 - fi - - # Extract and sort tags - ALL_TAGS=$(jq -r '.[].tag_name' response.json | grep -E '^v[0-9]+\.[0-9]+(-[0-9]+)?$' | sed 's/-.*//' | sort -V | tail -n 1) - - # Exit if no tags were found - if [ -z "$ALL_TAGS" ]; then - echo "No valid tags found." - exit 1 - fi - - echo "::set-output name=tag::$ALL_TAGS" - # echo "tag=$ALL_TAGS" >> $GITHUB_STATE - - - name: Calculate next semver minor - id: calculate_next_version - run: | - LATEST_VERSION=${{ steps.get_all_releases.outputs.tag }} - - # strip 'v' prefix and split into major.minor - LATEST_VERSION=${LATEST_VERSION//v/} - IFS='.' read -r -a VERSION_PARTS <<< "$LATEST_VERSION" - - MAJOR=${VERSION_PARTS[0]} - MINOR=${VERSION_PARTS[1]} - - # increment the minor version - MINOR=$((MINOR + 1)) - - NEXT_VERSION="${MAJOR}.${MINOR}" - - echo "Major: $MAJOR" - echo "Minor: $MINOR" - - echo "Next version: $NEXT_VERSION" - echo "::set-output name=next_version::$NEXT_VERSION" - echo "::set-output name=next_version_major::$MAJOR" - echo "::set-output name=next_version_minor::$MINOR" \ No newline at end of file diff --git a/.github/workflows/generate_pot.yml b/.github/workflows/generate_pot.yml deleted file mode 100644 index f8f1864c..00000000 --- a/.github/workflows/generate_pot.yml +++ /dev/null @@ -1,43 +0,0 @@ -name: Generate translation template - -on: - pull_request: - paths-ignore: - - "*.md" - types: - - opened - - synchronize - - reopened - push: - paths-ignore: - - "*.md" - branches: - - main - -jobs: - generate-pot: - runs-on: ubuntu-latest - steps: - - name: "Checkout repo" - uses: actions/checkout@v6 - - - name: "Install gettext" - run: | - sudo apt update -qq - sudo apt install -y gettext - - - name: "Generate POT file using xgettext" - run: > - find src -name *.cpp -o -name *.hpp -o -name *.h | - xargs xgettext --from-code=utf-8 -w 100 - --keyword="_" --keyword="wxTRANSLATE" --keyword="wxPLURAL:1,2" - --keyword="_tr" --keyword="TR_NOOP" - --check=space-ellipsis --omit-header - -o cemu.pot - - - name: Upload artifact - uses: actions/upload-artifact@v6 - with: - name: POT file - path: ./cemu.pot - if-no-files-found: error \ No newline at end of file diff --git a/.github/workflows/libretro.yml b/.github/workflows/libretro.yml index 79be11fb..bad1c20a 100644 --- a/.github/workflows/libretro.yml +++ b/.github/workflows/libretro.yml @@ -101,7 +101,7 @@ jobs: - name: "Install molten-vk" # The Vulkan renderer links against the loader; on macOS that is - # MoltenVK. Same release the standalone macOS build in build.yml uses. + # MoltenVK, the release upstream's macOS build uses. run: | curl -L -O https://github.com/KhronosGroup/MoltenVK/releases/download/v1.4.1/MoltenVK-macos-privateapi.tar tar xf MoltenVK-macos-privateapi.tar diff --git a/CMakeSettings.json b/CMakeSettings.json deleted file mode 100644 index 0927e98b..00000000 --- a/CMakeSettings.json +++ /dev/null @@ -1,28 +0,0 @@ -{ - "configurations": [ - { - "name": "RelWithDebInfo", - "configurationType": "RelWithDebInfo", - "generator": "Ninja", - "inheritEnvironments": [ "msvc_x64_x64" ], - "buildRoot": "${projectDir}\\out\\build\\${name}", - "installRoot": "${projectDir}\\out\\install\\${name}" - }, - { - "name": "Release", - "configurationType": "Release", - "generator": "Ninja", - "inheritEnvironments": [ "msvc_x64_x64" ], - "buildRoot": "${projectDir}\\out\\build\\${name}", - "installRoot": "${projectDir}\\out\\install\\${name}" - }, - { - "name": "Debug", - "configurationType": "Debug", - "generator": "Ninja", - "inheritEnvironments": [ "msvc_x64_x64" ], - "buildRoot": "${projectDir}\\out\\build\\${name}", - "installRoot": "${projectDir}\\out\\install\\${name}" - } - ] -} \ No newline at end of file diff --git a/bin/resources/ar/‏‏cemu.mo b/bin/resources/ar/‏‏cemu.mo deleted file mode 100644 index 4062628b..00000000 Binary files a/bin/resources/ar/‏‏cemu.mo and /dev/null differ diff --git a/bin/resources/ca/cemu.mo b/bin/resources/ca/cemu.mo deleted file mode 100644 index 55640215..00000000 Binary files a/bin/resources/ca/cemu.mo and /dev/null differ diff --git a/bin/resources/de/cemu.mo b/bin/resources/de/cemu.mo deleted file mode 100644 index cd9edd3c..00000000 Binary files a/bin/resources/de/cemu.mo and /dev/null differ diff --git a/bin/resources/es/cemu.mo b/bin/resources/es/cemu.mo deleted file mode 100644 index a43d4a1d..00000000 Binary files a/bin/resources/es/cemu.mo and /dev/null differ diff --git a/bin/resources/fr/cemu.mo b/bin/resources/fr/cemu.mo deleted file mode 100644 index f3f3b498..00000000 Binary files a/bin/resources/fr/cemu.mo and /dev/null differ diff --git a/bin/resources/he/cemu.mo b/bin/resources/he/cemu.mo deleted file mode 100644 index 072e48c6..00000000 Binary files a/bin/resources/he/cemu.mo and /dev/null differ diff --git a/bin/resources/hu/cemu.mo b/bin/resources/hu/cemu.mo deleted file mode 100644 index 51b00d08..00000000 Binary files a/bin/resources/hu/cemu.mo and /dev/null differ diff --git a/bin/resources/it/cemu.mo b/bin/resources/it/cemu.mo deleted file mode 100644 index 20d5bb93..00000000 Binary files a/bin/resources/it/cemu.mo and /dev/null differ diff --git a/bin/resources/ja/cemu.mo b/bin/resources/ja/cemu.mo deleted file mode 100644 index ff2fadd8..00000000 Binary files a/bin/resources/ja/cemu.mo and /dev/null differ diff --git a/bin/resources/ko/cemu.mo b/bin/resources/ko/cemu.mo deleted file mode 100644 index 5ea5e1da..00000000 Binary files a/bin/resources/ko/cemu.mo and /dev/null differ diff --git a/bin/resources/nb/cemu.mo b/bin/resources/nb/cemu.mo deleted file mode 100644 index 21149d82..00000000 Binary files a/bin/resources/nb/cemu.mo and /dev/null differ diff --git a/bin/resources/nl/cemu.mo b/bin/resources/nl/cemu.mo deleted file mode 100644 index f9fcc226..00000000 Binary files a/bin/resources/nl/cemu.mo and /dev/null differ diff --git a/bin/resources/pl/cemu.mo b/bin/resources/pl/cemu.mo deleted file mode 100644 index 2d86b139..00000000 Binary files a/bin/resources/pl/cemu.mo and /dev/null differ diff --git a/bin/resources/pt/cemu.mo b/bin/resources/pt/cemu.mo deleted file mode 100644 index 8ff9b167..00000000 Binary files a/bin/resources/pt/cemu.mo and /dev/null differ diff --git a/bin/resources/ru/cemu.mo b/bin/resources/ru/cemu.mo deleted file mode 100644 index eb8f372f..00000000 Binary files a/bin/resources/ru/cemu.mo and /dev/null differ diff --git a/bin/resources/sv/cemu.mo b/bin/resources/sv/cemu.mo deleted file mode 100644 index c8fd68ee..00000000 Binary files a/bin/resources/sv/cemu.mo and /dev/null differ diff --git a/bin/resources/tr/cemu.mo b/bin/resources/tr/cemu.mo deleted file mode 100644 index 5b9b47ba..00000000 Binary files a/bin/resources/tr/cemu.mo and /dev/null differ diff --git a/bin/resources/uk/cemu.mo b/bin/resources/uk/cemu.mo deleted file mode 100644 index c8f6d461..00000000 Binary files a/bin/resources/uk/cemu.mo and /dev/null differ diff --git a/bin/resources/zh/cemu.mo b/bin/resources/zh/cemu.mo deleted file mode 100644 index 3e636971..00000000 Binary files a/bin/resources/zh/cemu.mo and /dev/null differ diff --git a/dist/linux/appimage.sh b/dist/linux/appimage.sh deleted file mode 100755 index fbadd733..00000000 --- a/dist/linux/appimage.sh +++ /dev/null @@ -1,59 +0,0 @@ -#!/bin/bash - -if [ "$1" == 'X64' ]; then CPU_ARCH="x86_64"; else CPU_ARCH="aarch64"; fi - -if [[ -z "${GITHUB_WORKSPACE}" ]]; then - export GITHUB_WORKSPACE="." -fi - -curl -sSfLO "https://github.com/linuxdeploy/linuxdeploy/releases/download/continuous/linuxdeploy-"${CPU_ARCH}".AppImage" -chmod a+x linuxdeploy*.AppImage -curl -sSfL https://github.com"$(curl https://github.com/probonopd/go-appimage/releases/expanded_assets/continuous | grep "mkappimage-.*-"${CPU_ARCH}".AppImage" | head -n 1 | cut -d '"' -f 2)" -o mkappimage.AppImage -chmod a+x mkappimage.AppImage -curl -sSfLO "https://raw.githubusercontent.com/linuxdeploy/linuxdeploy-plugin-gtk/master/linuxdeploy-plugin-gtk.sh" -chmod a+x linuxdeploy-plugin-gtk.sh -curl -sSfLO "https://github.com/darealshinji/linuxdeploy-plugin-checkrt/releases/download/continuous/linuxdeploy-plugin-checkrt.sh" -chmod a+x linuxdeploy-plugin-checkrt.sh - -if [[ ! -e /usr/lib/"${CPU_ARCH}"-linux-gnu ]]; then - sed -i 's#lib\/"${CPU_ARCH}"-linux-gnu#lib64#g' linuxdeploy-plugin-gtk.sh -fi - -mkdir -p AppDir/usr/bin -mkdir -p AppDir/usr/share/Cemu -mkdir -p AppDir/usr/share/applications -mkdir -p AppDir/usr/share/icons/hicolor/128x128/apps -mkdir -p AppDir/usr/share/metainfo -mkdir -p AppDir/usr/lib - -cp dist/linux/info.cemu.Cemu.{desktop,png} AppDir/ -cp dist/linux/info.cemu.Cemu.metainfo.xml AppDir/usr/share/metainfo/info.cemu.Cemu.appdata.xml - -cp -r bin/* AppDir/usr/share/Cemu - -mv AppDir/usr/share/Cemu/Cemu AppDir/usr/bin/ -chmod +x AppDir/usr/bin/Cemu - -cp /usr/lib/"${CPU_ARCH}"-linux-gnu/{libsepol.so.1,libffi.so.7,libpcre.so.3,libGLU.so.1,libthai.so.0} AppDir/usr/lib - -export UPD_INFO="gh-releases-zsync|cemu-project|Cemu|ci|Cemu.AppImage.zsync" -export NO_STRIP=1 -./linuxdeploy-"${CPU_ARCH}".AppImage --appimage-extract-and-run \ - --appdir="${GITHUB_WORKSPACE}"/AppDir/ \ - -d "${GITHUB_WORKSPACE}"/AppDir/info.cemu.Cemu.desktop \ - -i "${GITHUB_WORKSPACE}"/AppDir/info.cemu.Cemu.png \ - -e "${GITHUB_WORKSPACE}"/AppDir/usr/bin/Cemu \ - --plugin gtk \ - --plugin checkrt - -if ! GITVERSION="$(git rev-parse --short HEAD 2>/dev/null)"; then - GITVERSION=experimental -fi -echo "Cemu Version Cemu-${GITVERSION}" - -rm AppDir/usr/lib/libwayland-client.so.0 -echo -e "export LC_ALL=C\nexport FONTCONFIG_PATH=/etc/fonts" >> AppDir/apprun-hooks/linuxdeploy-plugin-gtk.sh -VERSION="${GITVERSION}" ./mkappimage.AppImage --appimage-extract-and-run "${GITHUB_WORKSPACE}"/AppDir - -mkdir -p "${GITHUB_WORKSPACE}"/artifacts/ -mv Cemu-"${GITVERSION}"-"${CPU_ARCH}".AppImage "${GITHUB_WORKSPACE}"/artifacts/ diff --git a/dist/linux/info.cemu.Cemu.desktop b/dist/linux/info.cemu.Cemu.desktop deleted file mode 100644 index 6eeb0120..00000000 --- a/dist/linux/info.cemu.Cemu.desktop +++ /dev/null @@ -1,27 +0,0 @@ -[Desktop Entry] -Name=Cemu -Type=Application -Terminal=false -Icon=info.cemu.Cemu -Exec=Cemu -GenericName=Wii U Emulator -GenericName[fi]=Wii U -emulaattori -GenericName[el]=Πρόγραμμα προσομοίωσης Wii U -GenericName[es]=Emulador de Wii U -GenericName[pt_BR]=Emulador de Wii U -GenericName[fr]=Émulateur de Wii U -GenericName[it]=Emulatore Wii U -Comment=Software to emulate Wii U games and applications on PC -Comment[fi]=Ohjelmisto Wii U -pelien ja sovellusten emulointiin PC:lla -Comment[de]=Software zum emulieren von Wii U Spielen und Anwendungen auf dem PC -Comment[ru]=Программа для эмуляции игр и приложений Wii U на PC -Comment[fr]=Application pour émuler des jeux et des applications Wii U sur PC -Comment[nl]=Applicatie om Wii U spellen en applicaties te emuleren op PC -Comment[el]=Πρόγραμμα προσομοίωσης παιχνιδιών και εφαρμογών Wii U στον υπολογιστή -Comment[es]=Software para emular juegos y aplicaciones de Wii U en PC -Comment[pt_BR]=Software para emular jogos e aplicativos de Wii U no PC -Comment[it]=Software per emulare giochi e applicazioni per Wii U su PC -Categories=Game;Emulator; -Keywords=Nintendo; -MimeType=application/x-wii-u-rom; -StartupWMClass=Cemu diff --git a/dist/linux/info.cemu.Cemu.metainfo.xml b/dist/linux/info.cemu.Cemu.metainfo.xml deleted file mode 100644 index ef59427f..00000000 --- a/dist/linux/info.cemu.Cemu.metainfo.xml +++ /dev/null @@ -1,84 +0,0 @@ - - - - info.cemu.Cemu - Cemu - Nintendo Wii U Emulator - Nintendo Wii U Emulator - Émulateur Nintendo Wii U - Nintendo Wii U Emulator - Εξομοιωτής Nintendo Wii U - Emulador de Nintendo Wii U - Emulador Nintendo Wii U - Emulatore Nintendo Wii U - Nintendo Wii U Emulaattori - Cemu Project - info.cemu.Cemu.desktop - CC0-1.0 - MPL-2.0 - -

Cemu is a Nintendo Wii U emulator that is able to run most Wii U games and homebrew in a playable state. Created by Exzap, and written in C/C++.

-

Cemu ist ein Nintendo Wii U-Emulator, der die meisten Wii U Spiele und Homebrew in einem spielbaren Zustand ausführen kann. Erstellt von Exzap, und geschrieben in C/C++.

-

Cemu est un émulateur de Wii U capable de lancer la plupart des jeux Wii U et des homebrews en interface de jeu. Crée par Exzap, et développé en C/C++.

-

Cemu is een Nintendo Wii U emulator die de meeste Wii U en Homebrew games speelbaar kan runnen. Het project is begonnen door Exzap, en het is geschreven in C/C++.

-

Το Cemu είναι ένας προσομοιωτής της κονσόλας Nintendo Wii U που προσομοιώνει επίσημα παιχνίδια, καθώς και μη επίσημα προγράμματα ("homebrew") του Wii U. Δημιουργήθηκε από τον Exzap σε C/C++.

-

Cemu es un emulador de Nintendo Wii U que es capaz de ejecutar la mayoría de los juegos de Wii U y homebrew en un estado jugable. Creado por Exzap, y escrito en C y C++.

-

Cemu é um emulador de Nintendo Wii U que é capaz de executar a maioría dos jogos de Wii U e homebrew em um estado jogavel. Criado por Exzap, e escrito em C e C++.

-

Cemu è un emulatore del Nintendo Wii U in grado di eseguire in stato giocabile la maggior parte dei giochi e homebrew per Wii U. Creato da Exzap, e scritto in C/C++.

-

Cemu on Wii U -emulaattori, joka pystyy toistamaan useimpia Wii U -pelejä ja homebrew'ta pelikelvollisesti. Luonut Exzap, kirjoitettu C:llä/C++:lla.

-

This emulator aims at providing both high-accuracy and performance, and is actively being developed with new features and fixes to increase compatibility, convenience and usability.

-

Dieser Emulator zielt darauf ab, sowohl hohe Genauigkeit als auch Leistung zu bieten, und wird aktiv mit neuen Funktionen und Korrekturen weiterentwickelt, um Kompatibilität, Komfort und Benutzerfreundlichkeit zu verbessern.

-

Cet émulateur vise à la fois à offrir fidélité et performance, il est activement développé avec des nouvelles fonctionnalités et des correctifs pour augmenter la compatibilité, la commodité et la facilité d'utilisation.

-

De emulator richt zich op integriteit en snelheid, en wordt continu verder ontwikkeld met nieuwe toevoegingen en fixes om de compatibiliteit, het gemak en de gebruiksvriendelijkheid te verbeteren.

-

Ο προσομοιωτής αυτός στοχεύει τόσο στην ακρίβεια, όσο και στην ταχύτητα, και βελτιώνεται συνεχώς με νέες δυνατότητες και διορθώσεις που τον καθιστούν πιο βολικό, εύκολο στην χρήση και συμβατό με περισσότερα παιχνίδια.

-

Este emulador tiene como objetivo proporcionar tanto alta precisión como rendimiento y se desarrolla activamente con nuevas características y correcciones para mejorar la compatibilidad, la comodidad y la usabilidad.

-

Esse emulador visa proporcionar tanto alta precisão como rendimento e está sendo ativamente desenvolvido com novas características e correções para melhorar a compatibilidade, a comodidade e a usabilidade.

-

Questo emulatore ha l'obiettivo di fornire sia alta precisione che alte prestazioni, ed è in continuo sviluppo con nuove funzionalità e correzioni per aumentare la compatibilità, la comodità e l'usabilità.

-

Tämä emulaattori pyrkii tarjoamaan korkeaa tarkkuutta sekä suorituskykyä, ja sitä kehitetään aktiivisesti uusilla ominaisuuksilla ja korjauksilla, jotka parantavat yhteensopivuutta, mukavuutta ja käytettävyyttä..

-

It was written from scratch and development on the project began roughly early 2015.

-

Er wird seit Anfang 2015 entwickelt.

-

Il a été écrit à partir de zéro et son développement a débuté vers le début de l'année 2015.

-

Ontwikkeling van Cemu begon ongeveer in het voorjaar van 2015.

-

Αρχισε απο το τίποτα και βρίσκεται υπό ανάπτυξη από το 2015.

-

Fue escrito desde cero y el desarrollo del proyecto comenzó aproximadamente a principios de 2015.

-

Foi escrito do zero e o desenvolvimento do projeto começou aproximadamente a princípio de 2015.

-

È stato scritto da zero e lo sviluppo del progetto è cominciato intorno all'inizio del 2015.

-

Se kirjoitettiin tyhjästä ja sen kehitys alkoi suunnilleen vuoden 2015 alussa.

- -
- - - https://upload.wikimedia.org/wikipedia/commons/5/58/Cemu_Screenshot.png - - - - - https://github.com/cemu-project/Cemu/releases/tag/v2.0 - - - https://cemu.info - https://github.com/cemu-project/Cemu/issues - https://cemu.info/faq.html - https://wiki.cemu.info - - - Game - Emulator - - - 8192 - - - pointing - keyboard - gamepad - - - - cemu - application/x-wii-u-rom - - - nintendo - -
diff --git a/dist/linux/info.cemu.Cemu.png b/dist/linux/info.cemu.Cemu.png deleted file mode 100644 index 6f655072..00000000 Binary files a/dist/linux/info.cemu.Cemu.png and /dev/null differ diff --git a/dist/windows/Cemu.manifest b/dist/windows/Cemu.manifest deleted file mode 100644 index 5ff952b1..00000000 --- a/dist/windows/Cemu.manifest +++ /dev/null @@ -1,16 +0,0 @@ - - - - - - - - - - - - - True/PM - - - \ No newline at end of file diff --git a/src/gui/wxgui/AudioDebuggerWindow.cpp b/src/gui/wxgui/AudioDebuggerWindow.cpp deleted file mode 100644 index eb30974f..00000000 --- a/src/gui/wxgui/AudioDebuggerWindow.cpp +++ /dev/null @@ -1,305 +0,0 @@ -#include "wxgui.h" -#include "AudioDebuggerWindow.h" - -#include "Cafe/OS/libs/snd_core/ax.h" -#include "Cafe/OS/libs/snd_core/ax_internal.h" - -enum -{ - // options - REFRESH_ID, - CLOSE_ID, - VOICELIST_ID, - REFRESH_TIMER_ID, -}; - -wxBEGIN_EVENT_TABLE(AudioDebuggerWindow, wxFrame) -EVT_BUTTON(CLOSE_ID, AudioDebuggerWindow::OnCloseButton) -EVT_BUTTON(REFRESH_ID, AudioDebuggerWindow::OnRefreshButton) -EVT_TIMER(REFRESH_TIMER_ID, AudioDebuggerWindow::OnRefreshTimer) - -EVT_CLOSE(AudioDebuggerWindow::OnClose) -wxEND_EVENT_TABLE() - -AudioDebuggerWindow::AudioDebuggerWindow(wxFrame& parent) - : wxFrame(&parent, wxID_ANY, _("AX voice viewer"), wxDefaultPosition, wxSize(1126, 580), wxCLOSE_BOX | wxCLIP_CHILDREN | wxCAPTION | wxRESIZE_BORDER) -{ - wxPanel* mainPane = new wxPanel(this); - wxBoxSizer* sizer = new wxBoxSizer(wxVERTICAL); - - voiceListbox = new wxListCtrl(mainPane, VOICELIST_ID, wxPoint(0, 0), wxSize(1126, 570), wxLC_REPORT); - voiceListbox->SetFont(wxFont(8, wxFONTFAMILY_MODERN, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL, false, "Courier New")); - // add columns - wxListItem col0; - col0.SetId(0); - col0.SetText("idx"); - col0.SetWidth(40); - voiceListbox->InsertColumn(0, col0); - wxListItem col1; - col1.SetId(1); - col1.SetText("state"); - col1.SetWidth(48); - voiceListbox->InsertColumn(1, col1); - //wxListItem col2; - // format - col1.SetId(2); - col1.SetText("fmt"); - col1.SetWidth(52); - voiceListbox->InsertColumn(2, col1); - // sample base addr - col1.SetId(3); - col1.SetText("base"); - col1.SetWidth(70); - voiceListbox->InsertColumn(3, col1); - // current offset - col1.SetId(4); - col1.SetText("current"); - col1.SetWidth(70); - voiceListbox->InsertColumn(4, col1); - // loop offset - col1.SetId(5); - col1.SetText("loop"); - col1.SetWidth(70); - voiceListbox->InsertColumn(5, col1); - // end offset - col1.SetId(6); - col1.SetText("end"); - col1.SetWidth(70); - voiceListbox->InsertColumn(6, col1); - // volume - col1.SetId(7); - col1.SetText("vol"); - col1.SetWidth(46); - voiceListbox->InsertColumn(7, col1); - // volume delta - col1.SetId(8); - col1.SetText("volD"); - col1.SetWidth(46); - voiceListbox->InsertColumn(8, col1); - // src - col1.SetId(9); - col1.SetText("src"); - col1.SetWidth(70); - voiceListbox->InsertColumn(9, col1); - // low-pass filter coef a0 - col1.SetId(10); - col1.SetText("lpa0"); - col1.SetWidth(46); - voiceListbox->InsertColumn(10, col1); - // low-pass filter coef b0 - col1.SetId(11); - col1.SetText("lpb0"); - col1.SetWidth(46); - voiceListbox->InsertColumn(11, col1); - // biquad filter coef b0 - col1.SetId(12); - col1.SetText("bqb0"); - col1.SetWidth(46); - voiceListbox->InsertColumn(12, col1); - // biquad filter coef b0 - col1.SetId(13); - col1.SetText("bqb1"); - col1.SetWidth(46); - voiceListbox->InsertColumn(13, col1); - // biquad filter coef b0 - col1.SetId(14); - col1.SetText("bqb2"); - col1.SetWidth(46); - voiceListbox->InsertColumn(14, col1); - // biquad filter coef a0 - col1.SetId(15); - col1.SetText("bqa1"); - col1.SetWidth(46); - voiceListbox->InsertColumn(15, col1); - // biquad filter coef a1 - col1.SetId(16); - col1.SetText("bqa2"); - col1.SetWidth(46); - voiceListbox->InsertColumn(16, col1); - // device mix - col1.SetId(17); - col1.SetText("deviceMix"); - col1.SetWidth(186); - voiceListbox->InsertColumn(17, col1); - - sizer->Add(voiceListbox, 1, wxEXPAND | wxBOTTOM, 0); - - voiceListbox->Bind(wxEVT_RIGHT_DOWN, &AudioDebuggerWindow::OnVoiceListRightClick, this); - - mainPane->SetSizer(sizer); - - // add empty entries - for (sint32 i = 0; i < snd_core::AX_MAX_VOICES; i++) - { - wxListItem item; - item.SetId(i); - item.SetText(wxString::Format("%d", snd_core::AX_MAX_VOICES-i-1)); - voiceListbox->InsertItem(item); - } - RefreshVoiceList(); - - // start refresh timer - static const int INTERVAL = 100; // milliseconds - refreshTimer = new wxTimer(this, REFRESH_TIMER_ID); - refreshTimer->Start(INTERVAL); -} - -void AudioDebuggerWindow::OnRefreshTimer(wxTimerEvent& event) -{ - RefreshVoiceList(); -} - -void AudioDebuggerWindow::OnCloseButton(wxCommandEvent& event) -{ - Close(); -} - -void AudioDebuggerWindow::OnRefreshButton(wxCommandEvent& event) -{ - RefreshVoiceList(); -} - -void AudioDebuggerWindow::OnClose(wxCloseEvent& event) -{ - Close(); -} - -#define _r(__idx) _swapEndianU32(threadItrBE->context.gpr[__idx]) - -void AudioDebuggerWindow::RefreshVoiceList_sndgeneric() -{ - if (snd_core::__AXVPBInternalVoiceArray == nullptr || snd_core::__AXVPBArrayPtr == nullptr) - return; - - snd_core::AXVPB tempVoiceArray[snd_core::AX_MAX_VOICES]; - memcpy(tempVoiceArray, snd_core::__AXVPBArrayPtr, sizeof(snd_core::AXVPB)*snd_core::AX_MAX_VOICES); - - voiceListbox->Freeze(); - - for (sint32 i = 0; i < snd_core::AX_MAX_VOICES; i++) - { - sint32 voiceIndex = snd_core::AX_MAX_VOICES - 1 - i; - snd_core::AXVPBInternal_t* internal = snd_core::__AXVPBInternalVoiceArray + voiceIndex; - // index - voiceListbox->SetItem(i, 0, wxString::Format("%d", (sint32)tempVoiceArray[voiceIndex].index)); - // state - uint16 playbackState = _swapEndianU16(internal->playbackState); - voiceListbox->SetItem(i, 1, playbackState ? "on" : "off"); - // if voice index is invalid then stop updating here to prevent crashes - if (voiceIndex < 0 || playbackState == 0) - { - voiceListbox->SetItem(i, 0, ""); - voiceListbox->SetItem(i, 1, ""); - voiceListbox->SetItem(i, 2, ""); - voiceListbox->SetItem(i, 3, ""); - voiceListbox->SetItem(i, 4, ""); - voiceListbox->SetItem(i, 5, ""); - voiceListbox->SetItem(i, 6, ""); - voiceListbox->SetItem(i, 7, ""); - voiceListbox->SetItem(i, 8, ""); - voiceListbox->SetItem(i, 9, ""); - voiceListbox->SetItem(i, 10, ""); - voiceListbox->SetItem(i, 11, ""); - voiceListbox->SetItem(i, 12, ""); - voiceListbox->SetItem(i, 13, ""); - voiceListbox->SetItem(i, 14, ""); - voiceListbox->SetItem(i, 15, ""); - voiceListbox->SetItem(i, 16, ""); - voiceListbox->SetItem(i, 17, ""); - continue; - } - // format - wxString formatLabel; - if (tempVoiceArray[voiceIndex].offsets.format == _swapEndianU16(snd_core::AX_FORMAT_ADPCM)) - formatLabel = "adpcm"; - else if (tempVoiceArray[voiceIndex].offsets.format == _swapEndianU16(snd_core::AX_FORMAT_PCM16)) - formatLabel = "pcm16"; - else if (tempVoiceArray[voiceIndex].offsets.format == _swapEndianU16(snd_core::AX_FORMAT_PCM8)) - formatLabel = "pcm8"; - else - formatLabel = "ukn"; - voiceListbox->SetItem(i, 2, formatLabel); - // update offsets - snd_core::AXPBOFFSET_t tempOffsets; - snd_core::AXGetVoiceOffsets(tempVoiceArray + voiceIndex, &tempOffsets); - // sample base - voiceListbox->SetItem(i, 3, wxString::Format("%08x", _swapEndianU32(tempOffsets.samples))); - // current offset - voiceListbox->SetItem(i, 4, wxString::Format("%08x", _swapEndianU32(tempOffsets.currentOffset))); - // loop offset - voiceListbox->SetItem(i, 5, tempOffsets.loopFlag ? wxString::Format("%08x", _swapEndianU32(tempOffsets.loopOffset)) : ""); - // end offset - voiceListbox->SetItem(i, 6, wxString::Format("%08x", _swapEndianU32(tempOffsets.endOffset))); - // volume - voiceListbox->SetItem(i, 7, wxString::Format("%04x", (uint16)internal->veVolume)); - // volume delta - voiceListbox->SetItem(i, 8, wxString::Format("%04x", (uint16)internal->veDelta)); - // src - voiceListbox->SetItem(i, 9, wxString::Format("%04x%04x", _swapEndianU16(internal->src.ratioHigh), _swapEndianU16(internal->src.ratioLow))); - // lpf - if (internal->lpf.on) - { - voiceListbox->SetItem(i, 10, wxString::Format("%04x", _swapEndianU16(internal->lpf.a0))); - voiceListbox->SetItem(i, 11, wxString::Format("%04x", _swapEndianU16(internal->lpf.b0))); - } - else - { - voiceListbox->SetItem(i, 10, ""); - voiceListbox->SetItem(i, 11, ""); - } - // biquad - if (internal->biquad.on) - { - voiceListbox->SetItem(i, 12, wxString::Format("%04x", _swapEndianU16(internal->biquad.b0))); - voiceListbox->SetItem(i, 13, wxString::Format("%04x", _swapEndianU16(internal->biquad.b1))); - voiceListbox->SetItem(i, 14, wxString::Format("%04x", _swapEndianU16(internal->biquad.b2))); - voiceListbox->SetItem(i, 15, wxString::Format("%04x", _swapEndianU16(internal->biquad.a1))); - voiceListbox->SetItem(i, 16, wxString::Format("%04x", _swapEndianU16(internal->biquad.a2))); - } - else - { - voiceListbox->SetItem(i, 12, ""); - voiceListbox->SetItem(i, 13, ""); - voiceListbox->SetItem(i, 14, ""); - voiceListbox->SetItem(i, 15, ""); - voiceListbox->SetItem(i, 16, ""); - } - wxString label; - // device mix - for (uint32 f = 0; f < snd_core::AX_TV_CHANNEL_COUNT*snd_core::AX_MAX_NUM_BUS; f++) - { - sint32 busIndex = f% snd_core::AX_MAX_NUM_BUS; - sint32 channelIndex = f / snd_core::AX_MAX_NUM_BUS; - - //debug_printf("DeviceMix TV Voice %08x b%02d/c%02d vol %04x delta %04x\n", hCPU->gpr[3], busIndex, channelIndex, _swapEndianU16(mixArrayBE[f].vol), _swapEndianU16(mixArrayBE[f].volDelta)); - uint32 mixVol = internal->deviceMixTV[channelIndex * 4 + busIndex].vol; - mixVol = (mixVol + 0x0FFF) >> (12); - label += wxString::Format("%x", mixVol); - //ax.voiceInternal[voiceIndex].deviceMixTVChannel[channelIndex].bus[busIndex].vol = _swapEndianU16(mixArrayBE[f].vol); - } - voiceListbox->SetItem(i, 17, label); - } - voiceListbox->Thaw(); -} - -void AudioDebuggerWindow::RefreshVoiceList() -{ - RefreshVoiceList_sndgeneric(); -} - -void AudioDebuggerWindow::OnVoiceListPopupClick(wxCommandEvent &evt) -{ - -} - -void AudioDebuggerWindow::OnVoiceListRightClick(wxMouseEvent& event) -{ - -} - -void AudioDebuggerWindow::Close() -{ - // this->MakeModal(false); - refreshTimer->Stop(); - this->Destroy(); -} diff --git a/src/gui/wxgui/AudioDebuggerWindow.h b/src/gui/wxgui/AudioDebuggerWindow.h deleted file mode 100644 index 896d4999..00000000 --- a/src/gui/wxgui/AudioDebuggerWindow.h +++ /dev/null @@ -1,30 +0,0 @@ -#pragma once - -#include - -class wxListCtrl; - -class AudioDebuggerWindow : public wxFrame -{ -public: - AudioDebuggerWindow(wxFrame& parent); - - void OnCloseButton(wxCommandEvent& event); - void OnRefreshButton(wxCommandEvent& event); - void OnClose(wxCloseEvent& event); - void RefreshVoiceList_sndgeneric(); - void RefreshVoiceList(); - void OnRefreshTimer(wxTimerEvent& event); - void OnVoiceListPopupClick(wxCommandEvent &evt); - void OnVoiceListRightClick(wxMouseEvent& event); - - void Close(); - -private: - wxListCtrl* voiceListbox; - wxTimer* refreshTimer; - - wxDECLARE_EVENT_TABLE(); - - -}; diff --git a/src/gui/wxgui/CMakeLists.txt b/src/gui/wxgui/CMakeLists.txt deleted file mode 100644 index 079a78cd..00000000 --- a/src/gui/wxgui/CMakeLists.txt +++ /dev/null @@ -1,198 +0,0 @@ -add_library(CemuWxGui STATIC - canvas/IRenderCanvas.h - CemuApp.cpp - CemuApp.h - CemuUpdateWindow.cpp - CemuUpdateWindow.h - ChecksumTool.cpp - ChecksumTool.h - components/TextList.cpp - components/TextList.h - components/wxDownloadManagerList.cpp - components/wxDownloadManagerList.h - components/wxGameList.cpp - components/wxGameList.h - components/wxInputDraw.cpp - components/wxInputDraw.h - components/wxLogCtrl.cpp - components/wxLogCtrl.h - components/wxTitleManagerList.cpp - components/wxTitleManagerList.h - debugger/BreakpointWindow.cpp - debugger/BreakpointWindow.h - debugger/DebuggerWindow2.cpp - debugger/DebuggerWindow2.h - debugger/DisasmCtrl.cpp - debugger/DisasmCtrl.h - debugger/DumpCtrl.cpp - debugger/DumpCtrl.h - debugger/DumpWindow.cpp - debugger/DumpWindow.h - debugger/ModuleWindow.cpp - debugger/ModuleWindow.h - debugger/RegisterWindow.cpp - debugger/RegisterWindow.h - debugger/SymbolCtrl.cpp - debugger/SymbolCtrl.h - debugger/SymbolWindow.cpp - debugger/SymbolWindow.h - dialogs/CreateAccount/wxCreateAccountDialog.cpp - dialogs/CreateAccount/wxCreateAccountDialog.h - dialogs/SaveImport/SaveImportWindow.cpp - dialogs/SaveImport/SaveImportWindow.h - dialogs/SaveImport/SaveTransfer.cpp - dialogs/SaveImport/SaveTransfer.h - AudioDebuggerWindow.cpp - AudioDebuggerWindow.h - DownloadGraphicPacksWindow.cpp - DownloadGraphicPacksWindow.h - DownloadCustomGraphicPackWindow.cpp - DownloadCustomGraphicPackWindow.h - GameProfileWindow.cpp - GameProfileWindow.h - GameUpdateWindow.cpp - GameUpdateWindow.h - GeneralSettings2.cpp - GeneralSettings2.h - GettingStartedDialog.cpp - GettingStartedDialog.h - GraphicPacksWindow2.cpp - GraphicPacksWindow2.h - wxWindowSystem.cpp - helpers/wxControlObject.h - helpers/wxCustomData.h - helpers/wxCustomEvents.cpp - helpers/wxCustomEvents.h - helpers/wxHelpers.cpp - helpers/wxHelpers.h - helpers/wxLogEvent.h - helpers/wxWayland.cpp - helpers/wxWayland.h - input/HotkeySettings.cpp - input/HotkeySettings.h - input/InputAPIAddWindow.cpp - input/InputAPIAddWindow.h - input/InputSettings2.cpp - input/InputSettings2.h - input/panels/ClassicControllerInputPanel.cpp - input/panels/ClassicControllerInputPanel.h - input/panels/InputPanel.cpp - input/panels/InputPanel.h - input/panels/ProControllerInputPanel.cpp - input/panels/ProControllerInputPanel.h - input/panels/VPADInputPanel.cpp - input/panels/VPADInputPanel.h - input/panels/WiimoteInputPanel.cpp - input/panels/WiimoteInputPanel.h - input/settings/DefaultControllerSettings.cpp - input/settings/DefaultControllerSettings.h - input/settings/WiimoteControllerSettings.cpp - input/settings/WiimoteControllerSettings.h - LoggingWindow.cpp - LoggingWindow.h - MainWindow.cpp - MainWindow.h - MemorySearcherTool.cpp - MemorySearcherTool.h - PadViewFrame.cpp - PadViewFrame.h - TitleManager.cpp - TitleManager.h - wxCemuConfig.cpp - wxCemuConfig.h - EmulatedUSBDevices/EmulatedUSBDeviceFrame.cpp - EmulatedUSBDevices/EmulatedUSBDeviceFrame.h - windows/PPCThreadsViewer - windows/PPCThreadsViewer/DebugPPCThreadsWindow.cpp - windows/PPCThreadsViewer/DebugPPCThreadsWindow.h - windows/TextureRelationViewer - windows/TextureRelationViewer/TextureRelationWindow.cpp - windows/TextureRelationViewer/TextureRelationWindow.h - wxcomponents/checktree.cpp - wxcomponents/checktree.h - wxgui.h - wxHelper.h -) - -if (ENABLE_OPENGL) - target_sources(CemuWxGui PRIVATE - canvas/OpenGLCanvas.cpp - canvas/OpenGLCanvas.h - ) -endif() - -if (ENABLE_VULKAN) - target_sources(CemuWxGui PRIVATE - canvas/VulkanCanvas.cpp - canvas/VulkanCanvas.h - ) -endif() - -if (ENABLE_METAL) - target_sources(CemuWxGui PRIVATE - canvas/MetalCanvas.cpp - canvas/MetalCanvas.h - ) -endif() - -if (ENABLE_BLUEZ) - target_sources(CemuWxGui PRIVATE - input/PairingDialog.cpp - input/PairingDialog.h - ) -endif() - -set_property(TARGET CemuWxGui PROPERTY MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>") - -cemu_use_precompiled_header(CemuWxGui) - - -target_include_directories(CemuWxGui PUBLIC "../") -# PUBLIC because rapidjson/document.h is included in ChecksumTool.h -target_include_directories(CemuWxGui PUBLIC ${RAPIDJSON_INCLUDE_DIRS}) - -target_link_libraries(CemuWxGui PRIVATE - CemuCommon - CemuResource - libzip::zip - ZArchive::zarchive - CemuComponents - pugixml::pugixml - CemuCafe - PUBLIC - CURL::libcurl -) - -if(ENABLE_WXWIDGETS AND UNIX AND NOT APPLE) - # PUBLIC because gdk/gdkkeysyms.h is included in guiWrapper.h - target_link_libraries(CemuWxGui PUBLIC GTK3::gtk) - if (ENABLE_WAYLAND) - target_link_libraries(CemuWxGui PRIVATE Wayland::Client CemuWaylandProtocols) - endif() -endif() - -if(ENABLE_CUBEB) - target_link_libraries(CemuWxGui PRIVATE cubeb::cubeb) -endif() - -if(ENABLE_SDL) - target_link_libraries(CemuWxGui PRIVATE SDL3::SDL3) -endif() - -if(UNIX AND NOT APPLE) - if(ENABLE_FERAL_GAMEMODE) - target_link_libraries(CemuWxGui PRIVATE gamemode) - endif() -endif() -if (ENABLE_WXWIDGETS) - # PUBLIC because wx/app.h is included in CemuApp.h - target_link_libraries(CemuWxGui PUBLIC wxWidgets::wxWidgets) -endif() - -if(WIN32) - target_link_libraries(CemuWxGui PRIVATE bthprops) -endif() - -if(ALLOW_PORTABLE) - target_compile_definitions(CemuWxGui PRIVATE CEMU_ALLOW_PORTABLE) -endif () diff --git a/src/gui/wxgui/CemuApp.cpp b/src/gui/wxgui/CemuApp.cpp deleted file mode 100644 index 98a3fc40..00000000 --- a/src/gui/wxgui/CemuApp.cpp +++ /dev/null @@ -1,678 +0,0 @@ -#include "wxgui/CemuApp.h" -#include "wxCemuConfig.h" -#include "wxgui/MainWindow.h" -#include "wxgui/wxgui.h" -#include "config/CemuConfig.h" -#ifdef ENABLE_VULKAN -#include "Cafe/HW/Latte/Renderer/Vulkan/VulkanAPI.h" -#endif -#include "Cafe/HW/Latte/Core/LatteOverlay.h" -#include "config/ActiveSettings.h" -#include "config/LaunchSettings.h" -#include "wxgui/GettingStartedDialog.h" -#include "input/InputManager.h" -#include "input/api/SDL/SDLControllerProvider.h" -#include "wxgui/helpers/wxHelpers.h" -#include "Cemu/ncrypto/ncrypto.h" -#include "wxgui/input/HotkeySettings.h" -#include "wxgui/debugger/DebuggerWindow2.h" -#include - -#if ( BOOST_OS_LINUX || BOOST_OS_BSD ) && HAS_WAYLAND -#include "wxgui/helpers/wxWayland.h" -#endif -#if __WXGTK__ -#include -#endif - -#include -#include -#include -#include -#include -#include "wxHelper.h" - -#include "Cafe/TitleList/TitleList.h" -#include "Cafe/TitleList/SaveList.h" -#include "Cafe/CafeSystem.h" - -wxIMPLEMENT_APP_NO_MAIN(CemuApp); - -// defined in wxWindowSystem.cpp -extern WindowSystem::WindowInfo g_window_info; -extern std::shared_mutex g_mutex; - -// forward declarations from main.cpp -void UnitTests(); -void CemuCommonInit(); - -void HandlePostUpdate(); -// Translation strings to extract for gettext: -void unused_translation_dummy() -{ - void(_("Browse")); - void(_("Select a file")); - void(_("Select a directory")); - - void(_("Japanese")); - void(_("English")); - void(_("French")); - void(_("German")); - void(_("Italian")); - void(_("Spanish")); - void(_("Chinese")); - void(_("Korean")); - void(_("Dutch")); - void(_("Portugese")); - void(_("Russian")); - void(_("Taiwanese")); - void(_("unknown")); -} - -#if BOOST_OS_WINDOWS -#include -fs::path GetAppDataRoamingPath() -{ - PWSTR path = nullptr; - HRESULT result = SHGetKnownFolderPath(FOLDERID_RoamingAppData, 0, nullptr, &path); - if (FAILED(result)) - { - CoTaskMemFree(path); - return {}; - } - std::string appDataPath = boost::nowide::narrow(path); - CoTaskMemFree(path); - return _utf8ToPath(appDataPath); -} -#endif - -#if BOOST_OS_WINDOWS -void CemuApp::DeterminePaths(std::set& failedWriteAccess) // for Windows -{ - std::error_code ec; - bool isPortable = false; - fs::path user_data_path, config_path, cache_path, data_path; - auto standardPaths = wxStandardPaths::Get(); - fs::path exePath(wxHelper::MakeFSPath(standardPaths.GetExecutablePath())); - fs::path portablePath = exePath.parent_path() / "portable"; - data_path = exePath.parent_path(); // the data path is always the same as the exe path -#ifdef CEMU_ALLOW_PORTABLE - if (fs::is_directory(portablePath, ec)) - { - isPortable = true; - user_data_path = config_path = cache_path = portablePath; - } - else -#endif - { - fs::path roamingPath = GetAppDataRoamingPath() / "Cemu"; - user_data_path = config_path = cache_path = roamingPath; - } - // on Windows Cemu used to be portable by default prior to 2.0-89 - // to remain backwards compatible with old installations we check for settings.xml in the Cemu directory - // if it exists, we use the exe path as the portable directory - if(!isPortable) // lower priority than portable directory - { - if (fs::exists(exePath.parent_path() / "settings.xml", ec)) - { - isPortable = true; - user_data_path = config_path = cache_path = exePath.parent_path(); - } - } - ActiveSettings::SetPaths(isPortable, exePath, user_data_path, config_path, cache_path, data_path, failedWriteAccess); -} -#endif - -#if BOOST_OS_LINUX || BOOST_OS_BSD -void CemuApp::DeterminePaths(std::set& failedWriteAccess) // for Linux -{ - std::error_code ec; - bool isPortable = false; - fs::path user_data_path, config_path, cache_path, data_path; - auto standardPaths = wxStandardPaths::Get(); - fs::path exePath(wxHelper::MakeFSPath(standardPaths.GetExecutablePath())); - fs::path portablePath = exePath.parent_path() / "portable"; - // GetExecutablePath returns the AppImage's temporary mount location - wxString appImagePath; - if (wxGetEnv("APPIMAGE", &appImagePath)) - { - exePath = wxHelper::MakeFSPath(appImagePath); - portablePath = exePath.parent_path() / "portable"; - } -#ifdef CEMU_ALLOW_PORTABLE - if (fs::is_directory(portablePath, ec)) - { - isPortable = true; - user_data_path = config_path = cache_path = portablePath; - // in portable mode assume the data directories (resources, gameProfiles/default/) are next to the executable - data_path = exePath.parent_path(); - } - else -#endif - { - SetAppName("Cemu"); - wxString appName = GetAppName(); - standardPaths.SetFileLayout(wxStandardPaths::FileLayout::FileLayout_XDG); - auto getEnvDir = [&](const wxString& varName, const wxString& defaultValue) - { - wxString dir; - if (!wxGetEnv(varName, &dir) || dir.empty()) - return defaultValue; - return dir; - }; - wxString homeDir = wxFileName::GetHomeDir(); - user_data_path = (getEnvDir(wxS("XDG_DATA_HOME"), homeDir + wxS("/.local/share")) + "/" + appName).ToStdString(); - config_path = (getEnvDir(wxS("XDG_CONFIG_HOME"), homeDir + wxS("/.config")) + "/" + appName).ToStdString(); - data_path = standardPaths.GetDataDir().ToStdString(); - cache_path = standardPaths.GetUserDir(wxStandardPaths::Dir::Dir_Cache).ToStdString(); - cache_path /= appName.ToStdString(); - } - ActiveSettings::SetPaths(isPortable, exePath, user_data_path, config_path, cache_path, data_path, failedWriteAccess); -} -#endif - -#if BOOST_OS_MACOS -void CemuApp::DeterminePaths(std::set& failedWriteAccess) // for MacOS -{ - std::error_code ec; - bool isPortable = false; - fs::path user_data_path, config_path, cache_path, data_path; - auto standardPaths = wxStandardPaths::Get(); - fs::path exePath(wxHelper::MakeFSPath(standardPaths.GetExecutablePath())); - // If run from an app bundle, use its parent directory - fs::path appPath = exePath.parent_path().parent_path().parent_path(); - fs::path portablePath = appPath.extension() == ".app" ? appPath.parent_path() / "portable" : exePath.parent_path() / "portable"; -#ifdef CEMU_ALLOW_PORTABLE - if (fs::is_directory(portablePath, ec)) - { - isPortable = true; - user_data_path = config_path = cache_path = portablePath; - data_path = exePath.parent_path(); - } - else -#endif - { - SetAppName("Cemu"); - wxString appName = GetAppName(); - user_data_path = config_path = standardPaths.GetUserDataDir().ToStdString(); - data_path = standardPaths.GetDataDir().ToStdString(); - cache_path = standardPaths.GetUserDir(wxStandardPaths::Dir::Dir_Cache).ToStdString(); - cache_path /= appName.ToStdString(); - } - ActiveSettings::SetPaths(isPortable, exePath, user_data_path, config_path, cache_path, data_path, failedWriteAccess); -} -#endif - -// create default MLC files or quit if it fails -void CemuApp::InitializeNewMLCOrFail(fs::path mlc) -{ - if( CemuApp::CreateDefaultMLCFiles(mlc) ) - return; // all good - cemu_assert_debug(!ActiveSettings::IsCustomMlcPath()); // should not be possible? - - if(ActiveSettings::IsCommandLineMlcPath() || ActiveSettings::IsCustomMlcPath()) - { - // tell user that the custom path is not writable - wxMessageBox(formatWxString(_("Cemu failed to write to the custom mlc directory.\nThe path is:\n{}"), wxHelper::FromPath(mlc)), _("Error"), wxOK | wxCENTRE | wxICON_ERROR); - exit(0); - } - wxMessageBox(formatWxString(_("Cemu failed to write to the mlc directory.\nThe path is:\n{}"), wxHelper::FromPath(mlc)), _("Error"), wxOK | wxCENTRE | wxICON_ERROR); - exit(0); -} - -void CemuApp::InitializeExistingMLCOrFail(fs::path mlc) -{ - if(CreateDefaultMLCFiles(mlc)) - return; // all good - // failed to write mlc files - if(ActiveSettings::IsCommandLineMlcPath() || ActiveSettings::IsCustomMlcPath()) - { - // tell user that the custom path is not writable - // if it's a command line path then just quit. Otherwise ask if user wants to reset the path - if(ActiveSettings::IsCommandLineMlcPath()) - { - wxMessageBox(formatWxString(_("Cemu failed to write to the custom mlc directory.\nThe path is:\n{}"), wxHelper::FromPath(mlc)), _("Error"), wxOK | wxCENTRE | wxICON_ERROR); - exit(0); - } - // ask user if they want to reset the path - const wxString message = formatWxString(_("Cemu failed to write to the custom mlc directory.\n\nThe path is:\n{}\n\nCemu cannot start without a valid mlc path. Do you want to reset the path? You can later change it again in the General Settings."), - _pathToUtf8(mlc)); - wxMessageDialog dialog(nullptr, message, _("Error"), wxCENTRE | wxYES_NO | wxICON_WARNING); - dialog.SetYesNoLabels(_("Reset path"), _("Exit")); - const auto dialogResult = dialog.ShowModal(); - if (dialogResult == wxID_NO) - exit(0); - else // reset path - { - GetConfig().mlc_path = ""; - GetConfigHandle().Save(); - } - } - else - { - // default path is not writeable. Just let the user know and quit. Unsure if it would be a good idea to ask the user to choose an alternative path instead - wxMessageBox(formatWxString(_("Cemu failed to write to the default mlc directory.\nThe path is:\n{}"), wxHelper::FromPath(mlc)), _("Error"), wxOK | wxCENTRE | wxICON_ERROR); - exit(0); - } -} - -std::string TranslationCallback(std::string_view msgId) -{ - return wxGetTranslation(wxString::FromUTF8(msgId)).utf8_string(); -} - -bool CemuApp::OnInit() -{ -#if __WXGTK__ - GTKSuppressDiagnostics(G_LOG_LEVEL_MASK & ~G_LOG_FLAG_FATAL); -#endif - std::set failedWriteAccess; - DeterminePaths(failedWriteAccess); - // make sure default cemu directories exist - CreateDefaultCemuFiles(); - - GetConfigHandle().SetFilename(ActiveSettings::GetConfigPath("settings.xml").generic_wstring()); - - auto& config = GetWxGUIConfig(); - - std::error_code ec; - bool isFirstStart = !fs::exists(ActiveSettings::GetConfigPath("settings.xml"), ec); - - NetworkConfig::LoadOnce(); - if (!isFirstStart) - { - GetConfigHandle().Load(); - sint32 language = config.language.GetValue(); - LocalizeUI(static_cast(language == wxLANGUAGE_DEFAULT ? wxLocale::GetSystemLanguage() : language)); - } - else - { - LocalizeUI(static_cast(wxLocale::GetSystemLanguage())); - } - - SetTranslationCallback(TranslationCallback); -#if __WXMSW__ - auto& wxGuiConfig = GetWxGUIConfig(); - if (wxGuiConfig.msw_theme.GetValue() == static_cast(MSWThemeOption::kAuto)) - { - MSWEnableDarkMode(DarkMode_Auto); - } - else if (wxGuiConfig.msw_theme.GetValue() == static_cast(MSWThemeOption::kDark)) - { - MSWEnableDarkMode(DarkMode_Always); - } - - // extend tooltip duration to the maximum possible value - wxToolTip::SetDelay(-1); - wxToolTip::SetAutoPop(MAKELPARAM(std::numeric_limits::max(),0)); -#endif - - for (auto&& path : failedWriteAccess) - { - wxMessageBox(formatWxString(_("Cemu can't write to {}!"), wxHelper::FromPath(path)), - _("Warning"), wxOK | wxCENTRE | wxICON_EXCLAMATION, nullptr); - } - - if (isFirstStart) - { - // show the getting started dialog - GettingStartedDialog dia(nullptr); - dia.ShowModal(); - // make sure config is created. Gfx pack UI and input UI may create it earlier already, but we still want to update it - GetConfigHandle().Save(); - // create mlc, on failure the user can quit here. So do this after the Getting Started dialog - InitializeNewMLCOrFail(ActiveSettings::GetMlcPath()); - } - else - { - // check if mlc is valid and recreate default files - InitializeExistingMLCOrFail(ActiveSettings::GetMlcPath()); - } - - ActiveSettings::Init(); // this is a bit of a misnomer, right now this call only loads certs for online play. In the future we should move the logic to a more appropriate place - HandlePostUpdate(); - - LatteOverlay_init(); - // run a couple of tests if in non-release mode -#ifdef CEMU_DEBUG_ASSERT - UnitTests(); -#endif - -#if BOOST_OS_MACOS - SDLControllerProvider::InitSDL(); -#endif - CemuCommonInit(); - -#if BOOST_OS_MACOS - m_sdlEventPumpTimer = new wxTimer(this); - Bind(wxEVT_TIMER, &CemuApp::OnSDLEventPumpTimer, this); - m_sdlEventPumpTimer->Start(5, wxTIMER_CONTINUOUS); -#endif - - wxInitAllImageHandlers(); - - // fill colour db - wxTheColourDatabase->AddColour("ERROR", wxColour(0xCC, 0, 0)); - wxTheColourDatabase->AddColour("SUCCESS", wxColour(0, 0xbb, 0)); - -#if BOOST_OS_WINDOWS - const auto parent_path = GetParentProcess(); - if(parent_path.has_filename()) - { - const auto filename = parent_path.filename().generic_string(); - if (boost::icontains(filename, "WiiU_USB_Helper")) - __fastfail(0); - } -#endif - #ifdef ENABLE_VULKAN - InitializeGlobalVulkan(); - #endif - - Bind(wxEVT_ACTIVATE_APP, &CemuApp::ActivateApp, this); - - m_mainFrame = new MainWindow(); - - std::unique_lock lock(g_mutex); - g_window_info.app_active = true; - - HotkeySettings::Init(m_mainFrame); - - SetTopWindow(m_mainFrame); - m_mainFrame->Show(); - -#if ( BOOST_OS_LINUX || BOOST_OS_BSD ) && HAS_WAYLAND - if (wxWlIsWaylandWindow(m_mainFrame)) - wxWlSetAppId(m_mainFrame, "info.cemu.Cemu"); -#endif - - return true; -} - -int CemuApp::OnExit() -{ -#if BOOST_OS_MACOS - if (m_sdlEventPumpTimer) - { - m_sdlEventPumpTimer->Stop(); - Unbind(wxEVT_TIMER, &CemuApp::OnSDLEventPumpTimer, this); - delete m_sdlEventPumpTimer; - m_sdlEventPumpTimer = nullptr; - } -#endif - wxApp::OnExit(); - wxTheClipboard->Flush(); - InputManager::instance().Shutdown(); - int retValue = 0; - if (auto r = CafeSystem::GetForegroundTitleReturnStatus(); (LaunchSettings::GetLoadFile() || LaunchSettings::GetLoadTitleID()) && r) - retValue = *r; -#if BOOST_OS_MACOS - SDLControllerProvider::ShutdownSDL(); -#endif - // handle restart if requested - if (m_restartExecutable.has_value() && !m_restartExecutable->empty() && fs::exists(*m_restartExecutable)) - { - fs::path restartPath = *m_restartExecutable; -#if BOOST_OS_WINDOWS - PROCESS_INFORMATION pi{}; - STARTUPINFOW si{}; - si.cb = sizeof(si); - std::wstring cmdline; - cmdline = L"\"" + boost::nowide::widen(_pathToUtf8(restartPath)) + L"\""; - CreateProcessW(nullptr, (wchar_t*)cmdline.c_str(), nullptr, nullptr, FALSE, 0, nullptr, nullptr, &si, &pi); -#elif BOOST_OS_LINUX - std::string appPath = _pathToUtf8(restartPath); - execlp(appPath.c_str(), appPath.c_str(), (char *)NULL); -#elif BOOST_OS_MACOS - std::string appPath = _pathToUtf8(restartPath); - execlp(appPath.c_str(), appPath.c_str(), (char *)NULL); -#endif - } -#if BOOST_OS_WINDOWS - ExitProcess(retValue); -#else - _Exit(retValue); -#endif -} - -#if BOOST_OS_MACOS -void CemuApp::OnSDLEventPumpTimer(wxTimerEvent& event) -{ - // this callback is only used on macOS where SDL event functions need to be called on the main thread - // on other platforms SDLControllerProvider creates a separate thread for SDL event polling - SDLControllerProvider::PumpSDLEvents(); -} -#endif - -#if BOOST_OS_WINDOWS -void DumpThreadStackTrace(); -#endif - -void CemuApp::OnAssertFailure(const wxChar* file, int line, const wxChar* func, const wxChar* cond, const wxChar* msg) -{ - cemuLog_createLogFile(false); - cemuLog_log(LogType::Force, "Encountered wxWidgets assert!"); - cemuLog_log(LogType::Force, "File: {0} Line: {1}", wxString(file).utf8_string(), line); - cemuLog_log(LogType::Force, "Func: {0} Cond: {1}", wxString(func).utf8_string(), wxString(cond).utf8_string()); - cemuLog_log(LogType::Force, "Message: {}", wxString(msg).utf8_string()); - -#if BOOST_OS_WINDOWS - DumpThreadStackTrace(); -#endif - cemu_assert_debug(false); -} - -int CemuApp::FilterEvent(wxEvent& event) -{ - if(event.GetEventType() == wxEVT_KEY_DOWN) - { - const auto& key_event = (wxKeyEvent&)event; - g_window_info.set_keystate(fix_raw_keycode(key_event.GetRawKeyCode(), key_event.GetRawKeyFlags()), true); - } - else if(event.GetEventType() == wxEVT_KEY_UP) - { - const auto& key_event = (wxKeyEvent&)event; - g_window_info.set_keystate(fix_raw_keycode(key_event.GetRawKeyCode(), key_event.GetRawKeyFlags()), false); - } - else if(event.GetEventType() == wxEVT_ACTIVATE_APP) - { - const auto& activate_event = (wxActivateEvent&)event; - if(!activate_event.GetActive()) - g_window_info.set_keystatesup(); - } - - // track if debugger window or its child windows are focused - if (s_debuggerWindow && (event.GetEventType() == wxEVT_SET_FOCUS || event.GetEventType() == wxEVT_ACTIVATE)) - { - wxWindow* target_window = wxDynamicCast(event.GetEventObject(), wxWindow); - - if (target_window && event.GetEventType() == wxEVT_ACTIVATE && !((wxActivateEvent&)event).GetActive()) - target_window = nullptr; - - if (target_window) - { - g_window_info.debugger_focused = false; - wxWindow* window_it = target_window; - while (window_it) - { - if (window_it == s_debuggerWindow) g_window_info.debugger_focused = true; - window_it = window_it->GetParent(); - } - } - } - else if (!s_debuggerWindow) - { - g_window_info.debugger_focused = false; - } - - return wxApp::FilterEvent(event); -} - -std::vector CemuApp::GetLanguages() const { - std::vector availableLanguages(m_availableTranslations); - availableLanguages.insert(availableLanguages.begin(), wxLocale::GetLanguageInfo(wxLANGUAGE_ENGLISH)); - return availableLanguages; -} - -void CemuApp::LocalizeUI(wxLanguage languageToUse) -{ - std::unique_ptr translationsMgr(new wxTranslations()); - m_availableTranslations = GetAvailableTranslationLanguages(translationsMgr.get()); - - bool isTranslationAvailable = std::any_of(m_availableTranslations.begin(), m_availableTranslations.end(), - [languageToUse](const wxLanguageInfo* info) { return info->Language == languageToUse; }); - if (languageToUse == wxLANGUAGE_DEFAULT || isTranslationAvailable) - { - translationsMgr->SetLanguage(static_cast(languageToUse)); - translationsMgr->AddCatalog("cemu"); - - if (translationsMgr->IsLoaded("cemu") && wxLocale::IsAvailable(languageToUse)) - { - m_locale.Init(languageToUse); - } - // This must be run after wxLocale::Init, as the latter sets up its own wxTranslations instance which we want to override - wxTranslations::Set(translationsMgr.release()); - } -} - -std::vector CemuApp::GetAvailableTranslationLanguages(wxTranslations* translationsMgr) -{ - wxFileTranslationsLoader::AddCatalogLookupPathPrefix(wxHelper::FromPath(ActiveSettings::GetDataPath("resources"))); - std::vector languages; - for (const auto& langName : translationsMgr->GetAvailableTranslations("cemu")) - { - const auto* langInfo = wxLocale::FindLanguageInfo(langName); - if (langInfo) - languages.emplace_back(langInfo); - } - return languages; -} - -bool CemuApp::CheckMLCPath(const fs::path& mlc) -{ - std::error_code ec; - if (!fs::exists(mlc, ec)) - return false; - if (!fs::exists(mlc / "usr", ec) || !fs::exists(mlc / "sys", ec)) - return false; - return true; -} - -bool CemuApp::CreateDefaultMLCFiles(const fs::path& mlc) -{ - auto CreateDirectoriesIfNotExist = [](const fs::path& path) - { - std::error_code ec; - if (!fs::exists(path, ec)) - return fs::create_directories(path, ec); - return true; - }; - // list of directories to create - const fs::path directories[] = { - mlc, - mlc / "sys", - mlc / "usr", - mlc / "usr/title/00050000", // base - mlc / "usr/title/0005000c", // dlc - mlc / "usr/title/0005000e", // update - mlc / "usr/save/00050010/1004a000/user/common/db", // Mii Maker save folders {0x500101004A000, 0x500101004A100, 0x500101004A200} - mlc / "usr/save/00050010/1004a100/user/common/db", - mlc / "usr/save/00050010/1004a200/user/common/db", - mlc / "sys/title/0005001b/1005c000/content" // lang files - }; - for(auto& path : directories) - { - if(!CreateDirectoriesIfNotExist(path)) - return false; - } - // create sys/usr folder in mlc01 - try - { - const auto langDir = fs::path(mlc).append("sys/title/0005001b/1005c000/content"); - auto langFile = fs::path(langDir).append("language.txt"); - if (!fs::exists(langFile)) - { - std::ofstream file(langFile); - if (file.is_open()) - { - const char* langStrings[] = { "ja","en","fr","de","it","es","zh","ko","nl","pt","ru","zh" }; - for (const char* lang : langStrings) - file << fmt::format(R"("{}",)", lang) << std::endl; - - file.flush(); - file.close(); - } - } - - auto countryFile = fs::path(langDir).append("country.txt"); - if (!fs::exists(countryFile)) - { - std::ofstream file(countryFile); - for (sint32 i = 0; i < NCrypto::GetCountryCount(); i++) - { - const char* countryCode = NCrypto::GetCountryAsString(i); - if (boost::iequals(countryCode, "NN")) - file << "NULL," << std::endl; - else - file << fmt::format(R"("{}",)", countryCode) << std::endl; - } - file.flush(); - file.close(); - } - // create a dummy file in the mlc folder to check if it's writable - const auto dummyFile = fs::path(mlc).append("writetestdummy"); - std::ofstream file(dummyFile); - if (!file.is_open()) - return false; - file.close(); - fs::remove(dummyFile); - } - catch (const std::exception& ex) - { - return false; - } - return true; -} - -void CemuApp::CreateDefaultCemuFiles() -{ - // cemu directories - try - { - const auto controllerProfileFolder = ActiveSettings::GetConfigPath("controllerProfiles"); - if (!fs::exists(controllerProfileFolder)) - fs::create_directories(controllerProfileFolder); - - const auto memorySearcherFolder = ActiveSettings::GetUserDataPath("memorySearcher"); - if (!fs::exists(memorySearcherFolder)) - fs::create_directories(memorySearcherFolder); - } - catch (const std::exception& ex) - { - wxString errorMsg = formatWxString(_("Couldn't create a required cemu directory or file!\n\nError: {0}"), ex.what()); - -#if BOOST_OS_WINDOWS - const DWORD lastError = GetLastError(); - if (lastError != ERROR_SUCCESS) - errorMsg << fmt::format("\n\n{}", GetSystemErrorMessage(lastError)); -#endif - - wxMessageBox(errorMsg, _("Error"), wxOK | wxCENTRE | wxICON_ERROR); - exit(0); - } -} - -void CemuApp::ActivateApp(wxActivateEvent& event) -{ - g_window_info.app_active = event.GetActive(); - event.Skip(); -} - -void CemuApp::RequestRestart(fs::path executablePath) -{ - m_restartExecutable = executablePath; - CallAfter([this, executablePath = std::move(executablePath)]() mutable - { - if (m_mainFrame && !m_mainFrame->IsBeingDeleted()) - m_mainFrame->Close(true); - }); -} diff --git a/src/gui/wxgui/CemuApp.h b/src/gui/wxgui/CemuApp.h deleted file mode 100644 index 978123dd..00000000 --- a/src/gui/wxgui/CemuApp.h +++ /dev/null @@ -1,47 +0,0 @@ -#pragma once - -#include - -class MainWindow; -class wxTimer; -class wxTimerEvent; - -class CemuApp : public wxApp -{ -public: - bool OnInit() override; - int OnExit() override; - - void OnAssertFailure(const wxChar* file, int line, const wxChar* func, const wxChar* cond, const wxChar* msg) override; - int FilterEvent(wxEvent& event) override; - - std::vector GetLanguages() const; - - static bool CheckMLCPath(const fs::path& mlc); - static bool CreateDefaultMLCFiles(const fs::path& mlc); - static void CreateDefaultCemuFiles(); - - static void InitializeNewMLCOrFail(fs::path mlc); - static void InitializeExistingMLCOrFail(fs::path mlc); - - void RequestRestart(fs::path executablePath); -private: - void LocalizeUI(wxLanguage languageToUse); - - void DeterminePaths(std::set& failedWriteAccess); - - void ActivateApp(wxActivateEvent& event); - static std::vector GetAvailableTranslationLanguages(wxTranslations* translationsMgr); - - MainWindow* m_mainFrame = nullptr; -#if BOOST_OS_MACOS - void OnSDLEventPumpTimer(wxTimerEvent& event); - wxTimer* m_sdlEventPumpTimer = nullptr; -#endif - - wxLocale m_locale; - std::vector m_availableTranslations; - std::optional m_restartExecutable; -}; - -wxDECLARE_APP(CemuApp); diff --git a/src/gui/wxgui/CemuUpdateWindow.cpp b/src/gui/wxgui/CemuUpdateWindow.cpp deleted file mode 100644 index 2b1183c0..00000000 --- a/src/gui/wxgui/CemuUpdateWindow.cpp +++ /dev/null @@ -1,861 +0,0 @@ -#include "wxgui/CemuUpdateWindow.h" - -#include "Common/version.h" -#include "util/helpers/helpers.h" -#include "util/helpers/SystemException.h" -#include "util/helpers/ZipArchive.h" -#include "config/ActiveSettings.h" -#include "Common/FileStream.h" -#include "wxCemuConfig.h" -#include "CemuApp.h" - -#include -#include -#include -#include -#include - -#ifndef BOOST_OS_WINDOWS -#include -#include -#endif - -#include -#include -#include - - -wxDECLARE_EVENT(wxEVT_RESULT, wxCommandEvent); -wxDEFINE_EVENT(wxEVT_RESULT, wxCommandEvent); - -wxDECLARE_EVENT(wxEVT_PROGRESS, wxCommandEvent); -wxDEFINE_EVENT(wxEVT_PROGRESS, wxCommandEvent); - -CemuUpdateWindow::CemuUpdateWindow(wxWindow* parent) - : wxDialog(parent, wxID_ANY, _("Cemu update"), wxDefaultPosition, wxDefaultSize, - wxCAPTION | wxMINIMIZE_BOX | wxSYSTEM_MENU | wxTAB_TRAVERSAL | wxCLOSE_BOX) -{ - auto* sizer = new wxBoxSizer(wxVERTICAL); - m_gauge = new wxGauge(this, wxID_ANY, 100, wxDefaultPosition, wxSize(500, 20), wxGA_HORIZONTAL); - m_gauge->SetValue(0); - sizer->Add(m_gauge, 0, wxALL | wxEXPAND, 5); - - auto* rows = new wxFlexGridSizer(0, 2, 0, 0); - rows->AddGrowableCol(1); - - m_text = new wxStaticText(this, wxID_ANY, _("Checking for latest version...")); - rows->Add(m_text, 0, wxALL | wxALIGN_CENTER_VERTICAL, 5); - - { - auto* right_side = new wxBoxSizer(wxHORIZONTAL); - - m_updateButton = new wxButton(this, wxID_ANY, _("Update")); - m_updateButton->Bind(wxEVT_BUTTON, &CemuUpdateWindow::OnUpdateButton, this); - right_side->Add(m_updateButton, 0, wxALL, 5); - - m_cancelButton = new wxButton(this, wxID_ANY, _("Cancel")); - m_cancelButton->Bind(wxEVT_BUTTON, &CemuUpdateWindow::OnCancelButton, this); - right_side->Add(m_cancelButton, 0, wxALL, 5); - - rows->Add(right_side, 1, wxALIGN_RIGHT, 5); - } - - m_changelog = new wxHyperlinkCtrl(this, wxID_ANY, _("Changelog"), wxEmptyString); - rows->Add(m_changelog, 0, wxLEFT | wxBOTTOM | wxRIGHT | wxEXPAND, 5); - - sizer->Add(rows, 0, wxALL | wxEXPAND, 5); - - SetSizerAndFit(sizer); - Centre(wxBOTH); - - Bind(wxEVT_CLOSE_WINDOW, &CemuUpdateWindow::OnClose, this); - Bind(wxEVT_RESULT, &CemuUpdateWindow::OnResult, this); - Bind(wxEVT_PROGRESS, &CemuUpdateWindow::OnGaugeUpdate, this); - m_thread = std::thread(&CemuUpdateWindow::WorkerThread, this); - - m_updateButton->Hide(); - m_changelog->Hide(); -} - -CemuUpdateWindow::~CemuUpdateWindow() -{ - m_order = WorkerOrder::Exit; - if (m_thread.joinable()) - m_thread.join(); -} - -size_t CemuUpdateWindow::WriteStringCallback(char* ptr, size_t size, size_t nmemb, void* userdata) -{ - ((std::string*)userdata)->append(ptr, size * nmemb); - return size * nmemb; -}; - -static std::string CurlUrlEscape(CURL* curl, const std::string& input) -{ - char* escapedStr = curl_easy_escape(curl, input.c_str(), input.size()); - std::string r(escapedStr); - curl_free(escapedStr); - return r; -} - -static std::string CurlUrlUnescape(CURL* curl, std::string_view input) -{ - int decodedLen = 0; - char* decoded = curl_easy_unescape(curl, input.data(), input.size(), &decodedLen); - std::string r(decoded, decodedLen); - curl_free(decoded); - return r; -} - -static std::string GenerateSecureRandomString() -{ - std::array bytes{}; - if (RAND_bytes(bytes.data(), bytes.size()) != 1) - return ""; - std::string result; - result.reserve(21); - for (unsigned char byte : bytes) - { - result.push_back('a' + (byte % 26)); - } - return result; -} - -static bool HexToBytes(const std::string& hex, std::vector& dataOut) -{ - if (hex.size() % 2) - return false; - dataOut.clear(); - dataOut.reserve(hex.size() / 2); - for (size_t i = 0; i < hex.size(); i += 2) - { - unsigned int x; - auto [p, ec] = std::from_chars(hex.data() + i, hex.data() + i + 2, x, 16); - if (ec != std::errc{} || p != hex.data() + i + 2) - return false; - dataOut.push_back(static_cast(x)); - } - return true; -} - -static bool VerifyUpdateResponseSignature(std::string_view message, std::string signatureAsHex) -{ - std::vector signatureData; - if (!HexToBytes(signatureAsHex, signatureData)) - return false; - EVP_PKEY* key = EVP_PKEY_new_raw_public_key(EVP_PKEY_ED25519, nullptr, (const unsigned char*)"\xa0\x73\xda\x45\x12\x11\xd6\xe0\x2d\x7b\x1a\xb3\x69\xfd\x6c\xc5\x99\xd3\xd3\x1e\xe7\x9d\x57\x89\x2a\xe6\xe8\x40\x54\x7c\xc5\xbd", 32); - if (!key) - return false; - EVP_MD_CTX* context = EVP_MD_CTX_new(); - if (!context) - { - EVP_PKEY_free(key); - return false; - } - bool valid = EVP_DigestVerifyInit(context, nullptr, nullptr, nullptr, key) == 1 && - EVP_DigestVerify(context,signatureData.data(),signatureData.size(), reinterpret_cast(message.data()), message.size()) == 1; - EVP_MD_CTX_free(context); - EVP_PKEY_free(key); - return valid; -} - -// returns empty string for platforms which cant auto-update or dont have support for it yet -static std::string GetPlatformUpdateIdentifier() -{ - std::string identifier; -#if BOOST_OS_LINUX - const char* appImagePath = std::getenv("APPIMAGE"); - if (appImagePath && *appImagePath) - identifier.append("linux_appimage"); - else - return ""; -#elif BOOST_OS_WINDOWS - identifier.append("windows"); -#elif BOOST_OS_MACOS - identifier.append("macos_bundle"); -#elif BOOST_OS_BSD - return ""; // BSD users must update from source -#else - return ""; -#endif -#if defined(__aarch64__) - identifier.append("_aarch64"); -#elif defined(ARCH_X86_64) - identifier.append("_x86_64"); -#else - return ""; -#endif - return identifier; -} - -// returns true if update is available and sets output parameters -bool CemuUpdateWindow::QueryUpdateInfo(std::string& downloadUrlOut, std::string& changelogUrlOut, std::array& fileSha256Out) -{ - std::string rdString = GenerateSecureRandomString(); - if (rdString.empty()) - return false; - std::string buffer; - std::string urlStr("https://cemu.info/api2/version.php?v="); - auto* curl = curl_easy_init(); - urlStr.append(CurlUrlEscape(curl, BUILD_VERSION_STRING)); - - std::string platformIdentifier = GetPlatformUpdateIdentifier(); - if (platformIdentifier.empty()) - return false; - - urlStr.append("&platform="); - urlStr.append(platformIdentifier); - - const auto& config = GetWxGUIConfig(); - urlStr.append("&s="); - urlStr.append(rdString); - if(config.receive_untested_updates) - urlStr.append("&allowNewUpdates=1"); - - curl_easy_setopt(curl, CURLOPT_URL, urlStr.c_str()); - curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1); - curl_easy_setopt(curl, CURLOPT_WRITEDATA, &buffer); - curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteStringCallback); - curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 1L); - curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 2L); - curl_easy_setopt(curl, CURLOPT_SSL_OPTIONS, CURLSSLOPT_NATIVE_CA); - - bool result = false; - CURLcode cr = curl_easy_perform(curl); - if (cr == CURLE_OK) - { - long http_code = 0; - curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_code); - if (http_code != 0 && http_code != 200) - { - cemuLog_log(LogType::Force, "Update check failed (http code: {})", http_code); - cemu_assert_debug(false); - return false; - } - - std::vector tokens; - const boost::char_separator sep{ "|" }; - for (const auto& token : boost::tokenizer(buffer, sep)) - tokens.emplace_back(token); - - if (tokens.size() >= 5 && tokens[0] == "UPDATE") - { - // first token: "UPDATE" - // second token: Download URL - // third token: Changelog URL - // fourth token: SHA256 - // fifth token: Signature - // we allow more tokens in case we ever want to add extra information for future releases - downloadUrlOut = CurlUrlUnescape(curl, tokens[1]); - changelogUrlOut = CurlUrlUnescape(curl, tokens[2]); - if (!downloadUrlOut.empty() && !changelogUrlOut.empty()) - result = true; - // check signature - std::string signedMessage = platformIdentifier.append("|").append(tokens[0]).append("|").append(tokens[1]).append("|").append(tokens[2]).append("|").append(tokens[3]).append("|").append(rdString); - bool isValidSignature = VerifyUpdateResponseSignature(signedMessage, tokens[4]); - if (!isValidSignature) - { - cemuLog_log(LogType::Force, "Failed server signature check"); - return false; - } - std::vector fileShaVec; - if (!HexToBytes(tokens[3], fileShaVec)) - return false; - if (fileShaVec.size() != 32) - return false; - std::memcpy(fileSha256Out.data(), fileShaVec.data(), 32); - } - } - else - { - cemuLog_log(LogType::Force, "Update check failed with CURL error {}", (int)cr); - cemu_assert_debug(false); - } - curl_easy_cleanup(curl); - return result; -} - -std::future CemuUpdateWindow::IsUpdateAvailableAsync() -{ - return std::async(std::launch::async, CheckVersion); -} - -bool CemuUpdateWindow::CheckVersion() -{ - std::string downloadUrl, changelogUrl; - std::array fileSha256; - return QueryUpdateInfo(downloadUrl, changelogUrl, fileSha256); -} - - -int CemuUpdateWindow::ProgressCallback(void* clientp, curl_off_t dltotal, curl_off_t dlnow, curl_off_t ultotal, curl_off_t ulnow) -{ - auto* thisptr = (CemuUpdateWindow*)clientp; - auto* event = new wxCommandEvent(wxEVT_PROGRESS); - event->SetInt((int)dlnow); - wxQueueEvent(thisptr, event); - return 0; -} - -void CemuUpdateWindow::SubmitWorkerResult(Result newWorkerState) -{ - auto* event = new wxCommandEvent(wxEVT_RESULT); - event->SetInt((int)newWorkerState); - wxQueueEvent(this, event); -} - -static std::atomic s_cemuUpdateCancelRequest{false}; - -bool CemuUpdateWindow::DownloadCemuUpdateFile(const std::string& url, const fs::path& filename) -{ - if (!fs::exists(filename.parent_path())) - fs::create_directories(filename.parent_path()); - auto* curl = curl_easy_init(); - curl_easy_setopt(curl, CURLOPT_URL, url.c_str()); - curl_easy_setopt(curl, CURLOPT_NOBODY, 1); - curl_easy_setopt(curl, CURLOPT_USERAGENT, BUILD_VERSION_WITH_NAME_STRING); - curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 1L); - curl_easy_setopt(curl, CURLOPT_SSL_OPTIONS, CURLSSLOPT_NATIVE_CA); - curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1); - auto r = curl_easy_perform(curl); - if (r != CURLE_OK) - { - cemuLog_log(LogType::Force, "Cemu update download failed with error {}", r); - curl_easy_cleanup(curl); - return false; - } - long http_code = 0; - curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_code); - if (http_code != 0 && http_code != 200) - { - cemuLog_log(LogType::Force, "Unable to download cemu update file from {} (http error: {})", url, http_code); - curl_easy_cleanup(curl); - return false; - } - - curl_off_t update_size; - if (curl_easy_getinfo(curl, CURLINFO_CONTENT_LENGTH_DOWNLOAD_T, &update_size) == CURLE_OK) - m_gaugeMaxValue = (int)update_size; - - std::vector fileBuffer; - - s_cemuUpdateCancelRequest = false; - auto _curlWriteData = +[](void* ptr, size_t size, size_t nmemb, void* ctx) -> size_t - { - if (s_cemuUpdateCancelRequest) - return 0; - std::vector* fileBufferVec = (std::vector*)ctx; - const size_t writeSize = size * nmemb; - fileBufferVec->insert(fileBufferVec->end(), (uint8*)ptr, (uint8*)ptr + writeSize); - return writeSize; - }; - - curl_easy_setopt(curl, CURLOPT_NOBODY, 0); - curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, _curlWriteData); - curl_easy_setopt(curl, CURLOPT_WRITEDATA, &fileBuffer); - curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0); - curl_easy_setopt(curl, CURLOPT_XFERINFOFUNCTION, ProgressCallback); - curl_easy_setopt(curl, CURLOPT_XFERINFODATA, this); - - auto curl_result = std::async(std::launch::async, [](CURL* curl, long* http_code) - { - const auto r = curl_easy_perform(curl); - return r; - }, curl, &http_code); - while (true) - { - auto r = curl_result.wait_for(std::chrono::milliseconds(100)); - if (r == std::future_status::ready) - break; - if (m_order == WorkerOrder::Exit && !s_cemuUpdateCancelRequest) - s_cemuUpdateCancelRequest = true; - } - curl_easy_cleanup(curl); - CURLcode curlResult = curl_result.get(); - if (curlResult == CURLE_ABORTED_BY_CALLBACK) - { - return false; - } - else if (curlResult != CURLE_OK) - { - return false; - } - // store file to temp location - std::error_code ec; - fs::remove(filename, ec); - if (!FileStream::WriteFileAtomic(filename, fileBuffer)) - { - cemuLog_log(LogType::Force, "Failed to store update file"); - return false; - } - // read back file and verify the hash - auto diskFileData = FileStream::LoadIntoMemory(filename); - if (!diskFileData) - { - cemuLog_log(LogType::Force, "Failed to read back update file"); - return false; - } - // verify the file hash - std::array hash; - EVP_Q_digest(nullptr, "SHA256", nullptr, diskFileData->data(), diskFileData->size(), hash.data(), nullptr); - if (hash != m_fileSHA256) - { - cemuLog_log(LogType::Force, "File hash of downloaded update does not match"); - fs::remove(filename, ec); - return false; - } - SubmitWorkerResult(Result::UpdateDownloaded); - return true; -} - -bool CemuUpdateWindow::ExtractZipUpdate(const fs::path& zipname, const fs::path& targetpath) -{ - ZipArchive archive(zipname); - if (!archive.IsValid()) - { - cemuLog_log(LogType::Force, "Cannot open zip file: {}", _pathToUtf8(zipname)); - return false; - } - const size_t count = archive.GetNumEntries(); - m_gaugeMaxValue = count; - // the update files are in a directory called "Cemu_". Find the name of the directory - std::string cemuPathPrefix; - for (size_t i=0; i fileData; - if (!archive.ExtractIntoMemoryByIndex(i, fileData)) - { - cemuLog_log(LogType::Force, "Failed to extract file {}", entry.fullPath); - return false; - } - // make sure the folder exists - const fs::path fname = targetpath / entryPath; - try - { - fs::create_directories(fname.parent_path()); - } - catch (const std::exception& ex) - { - SystemException sys(ex); - cemuLog_log(LogType::Force, "can't create folder for update file \"{}\": {}", entry.fullPath, sys.what()); - return false; - } - } - } - // check if there is enough disk space (to keep this simple we assume 100MB, generally releases should be below that) - std::error_code ec; - const fs::space_info targetSpace = fs::space(ActiveSettings::GetExecutablePath().parent_path(), ec); - if (targetSpace.free <= 100 * 1024 * 1024) - { - cemuLog_log(LogType::Force, "Not enough disk space available for update"); - return false; - } - // actually extract and replace files - for (size_t i = 0; i < count; i++) - { - ZipArchive::ZipFileEntry entry; - if (archive.GetFileEntryByIndex(i, entry)) - { - if (!entry.fullPath.starts_with(cemuPathPrefxWithSlash)) - continue; - const fs::path entryPath = _utf8ToPath(entry.fullPath.substr(cemuPathPrefxWithSlash.length())); - // read into memory - std::vector fileData; - if (!archive.ExtractIntoMemoryByIndex(i, fileData)) - { - cemu_assert(false); // extraction tested earlier, it should not fail here - } - const fs::path fname = targetpath / entryPath; - if ( !FileStream::WriteFileAtomic(fname, fileData, true) ) - { - cemuLog_log(LogType::Force, "Failed to update {}", _pathToUtf8(fname)); - // continue regardless - } - if ((i / 10) * 10 == i) - { - auto* event = new wxCommandEvent(wxEVT_PROGRESS); - event->SetInt(static_cast(i)); - wxQueueEvent(this, event); - } - } - } - - auto* event = new wxCommandEvent(wxEVT_PROGRESS); - event->SetInt(m_gaugeMaxValue); - wxQueueEvent(this, event); - - return true; -} - -#if BOOST_OS_WINDOWS -bool CemuUpdateWindow::WorkerThread_Windows() -{ - const auto tmppath = fs::temp_directory_path() / L"cemu_update"; - std::error_code ec; - if (exists(tmppath)) - remove_all(tmppath, ec); - const auto updateSrcFile = tmppath / L"update.zip"; - if (!DownloadCemuUpdateFile(m_downloadUrl, updateSrcFile)) - return false; - if (m_order == WorkerOrder::Exit) - return false; - // extract and replace files - if (!ExtractZipUpdate(updateSrcFile, ActiveSettings::GetExecutablePath().parent_path())) - { - cemuLog_log(LogType::Force, "Extracting Cemu zip failed"); - return false; - } - SubmitWorkerResult(Result::ExtractSuccess); - // set relaunch path - fs::path newExePath = ActiveSettings::GetExecutablePath(); - newExePath = newExePath.parent_path().append("Cemu.exe"); - m_restartFile = newExePath; - return true; -} -#endif - -#if BOOST_OS_LINUX -bool CemuUpdateWindow::WorkerThread_AppImage() -{ - const auto tmppath = fs::temp_directory_path() / L"cemu_update"; - std::error_code ec; - if (exists(tmppath, ec)) - remove_all(tmppath, ec); - const auto updateSrcFile = tmppath / L"Cemu.AppImage"; - if (!DownloadCemuUpdateFile(m_downloadUrl, updateSrcFile)) - return false; - if (m_order == WorkerOrder::Exit) - return false; - const char* appimage_path = std::getenv("APPIMAGE"); - auto backupExecutable = fs::path(appimage_path); - backupExecutable.replace_extension( _utf8ToPath(_pathToUtf8(backupExecutable.extension()).append(".backup"))); - const char* filePath = updateSrcFile.c_str(); - mode_t permissions = S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH; - fs::rename(appimage_path, backupExecutable, ec); - if (ec) - { - cemuLog_log(LogType::Force, "Failed to rename current .AppImage for update replacement"); - return false; - } - m_restartFile = appimage_path; - chmod(filePath, permissions); - wxString wxAppPath = wxString::FromUTF8(appimage_path); - wxCopyFile (wxString::FromUTF8(_pathToUtf8(updateSrcFile)), wxAppPath); - return true; -} -#endif - -#if BOOST_OS_MACOS -bool CemuUpdateWindow::WorkerThread_MacBundle() -{ - const fs::path tempPath = fs::temp_directory_path() / "cemu_update"; - - std::error_code ec; - fs::remove_all(tempPath, ec); - fs::create_directories(tempPath, ec); - if (ec) - return false; - - const fs::path dmgPath = tempPath / "cemu.dmg"; - if (!DownloadCemuUpdateFile(m_downloadUrl, dmgPath)) - return false; - - if (m_order == WorkerOrder::Exit) - return false; - - // .../Cemu.app/Contents/MacOS/Cemu - const fs::path executable = ActiveSettings::GetExecutablePath(); - const fs::path appBundle = executable.parent_path().parent_path().parent_path(); - if (appBundle.extension() != ".app") - { - cemuLog_log(LogType::Force, "Cemu is not running from a macOS app bundle"); - return false; - } - - const fs::path scriptPath = tempPath / "update.sh"; - const fs::path mountPath = tempPath / "mount"; - - auto ShellQuote = [](std::string_view value) - { - std::string result = "'"; - for (const char c : value) - { - if (c == '\'') - result += "'\\''"; - else - result += c; - } - result += "'"; - return result; - }; - - std::string dmg = ShellQuote(_pathToUtf8(dmgPath)); - std::string app = ShellQuote(_pathToUtf8(appBundle)); - std::string mount = ShellQuote(_pathToUtf8(mountPath)); - - std::string script; - - script += "#!/bin/sh\n"; - script += "DMG_PATH=" + ShellQuote(_pathToUtf8(dmgPath)) + "\n"; - script += "APP_BUNDLE=" + ShellQuote(_pathToUtf8(appBundle)) + "\n"; - script += "MOUNT_PATH=" + ShellQuote(_pathToUtf8(mountPath)) + "\n"; - - script += R"SH( -set -eu - -STAGED_BUNDLE="${APP_BUNDLE}.update" -BACKUP_BUNDLE="${APP_BUNDLE}.backup" -MOUNTED=0 - -finish() -{ - STATUS=$? - trap - EXIT HUP INT TERM - - if [ "$MOUNTED" -eq 1 ]; then - /usr/bin/hdiutil detach "$MOUNT_PATH" >/dev/null 2>&1 || true - fi - - if [ -d "$APP_BUNDLE" ]; then - /usr/bin/open -n "$APP_BUNDLE" || true - elif [ -x "$BACKUP_BUNDLE/Contents/MacOS/Cemu" ]; then - "$BACKUP_BUNDLE/Contents/MacOS/Cemu" & - fi - - exit "$STATUS" -} - -trap finish EXIT HUP INT TERM - -/bin/rm -rf "$MOUNT_PATH" "$STAGED_BUNDLE" -/bin/mkdir -p "$MOUNT_PATH" - -/usr/bin/hdiutil attach \ - -nobrowse \ - -readonly \ - -mountpoint "$MOUNT_PATH" \ - "$DMG_PATH" - -MOUNTED=1 - -/usr/bin/ditto \ - "$MOUNT_PATH/Cemu.app" \ - "$STAGED_BUNDLE" - -/usr/bin/codesign \ - --verify \ - --strict \ - "$STAGED_BUNDLE/Contents/MacOS/Cemu" - -/usr/bin/hdiutil detach "$MOUNT_PATH" -MOUNTED=0 - -/bin/rm -rf "$BACKUP_BUNDLE" -/bin/mv "$APP_BUNDLE" "$BACKUP_BUNDLE" - -if ! /bin/mv "$STAGED_BUNDLE" "$APP_BUNDLE"; then - /bin/mv "$BACKUP_BUNDLE" "$APP_BUNDLE" || true - exit 1 -fi -)SH"; - - std::vector scriptData(script.begin(), script.end()); - if (!FileStream::WriteFileAtomic(scriptPath, scriptData)) - { - cemuLog_log(LogType::Force, "Failed to create macOS updater script"); - return false; - } - if (chmod(scriptPath.c_str(), S_IRUSR | S_IWUSR | S_IXUSR) != 0) - { - cemuLog_log(LogType::Force, "Failed to make updater script executable"); - return false; - } - // run the script on exit - m_restartFile = scriptPath; - return true; -} -#endif - -void CemuUpdateWindow::WorkerThread() -{ - // start writing log.txt if not created yet - cemuLog_createLogFile(false); - - while (true) - { - std::unique_lock lock(m_mutex); - WorkerOrder nextWorkOrder = WorkerOrder::Idle; - while (true) - { - nextWorkOrder = m_order.exchange(WorkerOrder::Idle); - if (nextWorkOrder != WorkerOrder::Idle) - break; - m_condition.wait_for(lock, std::chrono::milliseconds(125)); - } - if (nextWorkOrder == WorkerOrder::Exit) - break; - if (nextWorkOrder == WorkerOrder::CheckVersion) - { - auto* event = new wxCommandEvent(wxEVT_RESULT); - if (QueryUpdateInfo(m_downloadUrl, m_changelogUrl, m_fileSHA256)) - event->SetInt((int)Result::UpdateAvailable); - else - event->SetInt((int)Result::NoUpdateAvailable); - - wxQueueEvent(this, event); - } - else if (nextWorkOrder == WorkerOrder::UpdateVersion) - { - // download update - const std::string url = m_downloadUrl; - bool r = false; -#if BOOST_OS_WINDOWS - r = WorkerThread_Windows(); -#elif BOOST_OS_LINUX - r = WorkerThread_AppImage(); -#elif BOOST_OS_BSD - // dummy placeholder on BSD for now -#elif BOOST_OS_MACOS - r = WorkerThread_MacBundle(); -#endif - // update done - if (r) - { - // report success - auto* event = new wxCommandEvent(wxEVT_PROGRESS); - event->SetInt(m_gaugeMaxValue); - wxQueueEvent(this, event); - - auto* result_event = new wxCommandEvent(wxEVT_RESULT); - result_event->SetInt((int)Result::Success); - wxQueueEvent(this, result_event); - } - else if (m_order != WorkerOrder::Exit) - { - SubmitWorkerResult(Result::UpdateDownloadError); - } - } - } -} - -void CemuUpdateWindow::OnClose(wxCloseEvent& event) -{ - event.Skip(); - if (m_restartRequired) - { - wxGetApp().RequestRestart(m_restartFile); - } -} - - -void CemuUpdateWindow::OnResult(wxCommandEvent& event) -{ - switch ((Result)event.GetInt()) - { - case Result::NoUpdateAvailable: - m_cancelButton->SetLabel(_("Exit")); - m_text->SetLabel(_("No update available!")); - m_gauge->SetValue(100); - break; - case Result::UpdateAvailable: - { - if (!m_changelogUrl.empty()) - { - m_changelog->SetURL(m_changelogUrl); - m_changelog->Show(); - } - else - m_changelog->Hide(); - - m_updateButton->Show(); - - m_text->SetLabel(_("Update available!")); - m_cancelButton->SetLabel(_("Exit")); - break; - } - case Result::UpdateDownloaded: - m_text->SetLabel(_("Extracting update...")); - m_gauge->SetValue(0); - break; - case Result::UpdateDownloadError: - m_updateButton->Enable(); - m_cancelButton->Enable(); - m_text->SetLabel(_("Couldn't download the update!")); - break; - case Result::ExtractSuccess: - m_text->SetLabel(_("Applying update...")); - m_gauge->SetValue(0); - m_cancelButton->Disable(); - break; - case Result::ExtractError: - m_updateButton->Enable(); - m_cancelButton->Enable(); - m_text->SetLabel(_("Extracting failed!")); - break; - case Result::Success: - m_cancelButton->Enable(); - m_updateButton->Hide(); - - m_text->SetLabel(_("Success")); - m_cancelButton->SetLabel(_("Restart")); - m_restartRequired = true; - break; - default:; - } -} - -void CemuUpdateWindow::OnGaugeUpdate(wxCommandEvent& event) -{ - const int total_size = m_gaugeMaxValue > 0 ? m_gaugeMaxValue : 10000000; - m_gauge->SetValue((event.GetInt() * 100) / total_size); -} - -void CemuUpdateWindow::OnUpdateButton(const wxCommandEvent& event) -{ - std::unique_lock lock(m_mutex); - m_order = WorkerOrder::UpdateVersion; - - m_condition.notify_all(); - - m_updateButton->Disable(); - - m_text->SetLabel(_("Downloading update...")); -} - -void CemuUpdateWindow::OnCancelButton(const wxCommandEvent& event) -{ - Close(); -} diff --git a/src/gui/wxgui/CemuUpdateWindow.h b/src/gui/wxgui/CemuUpdateWindow.h deleted file mode 100644 index 4ab8c269..00000000 --- a/src/gui/wxgui/CemuUpdateWindow.h +++ /dev/null @@ -1,84 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include -#include - -#include - -class CemuUpdateWindow : public wxDialog -{ -public: - CemuUpdateWindow(wxWindow* parent); - ~CemuUpdateWindow(); - - static std::future IsUpdateAvailableAsync(); - -private: - wxStaticText* m_text; - wxGauge* m_gauge; - wxButton* m_cancelButton, *m_updateButton; - wxHyperlinkCtrl* m_changelog; - - void OnUpdateButton(const wxCommandEvent& event); - void OnCancelButton(const wxCommandEvent& event); - void OnClose(wxCloseEvent& event); - void OnResult(wxCommandEvent& event); - void OnGaugeUpdate(wxCommandEvent& event); - - static size_t WriteStringCallback(char* ptr, size_t size, size_t nmemb, void* userdata); - static bool QueryUpdateInfo(std::string& downloadUrlOut, std::string& changelogUrlOut, std::array& fileSha256Out); - static bool CheckVersion(); - - static int ProgressCallback(void* clientp, curl_off_t dltotal, curl_off_t dlnow, curl_off_t ultotal, curl_off_t ulnow); - bool DownloadCemuUpdateFile(const std::string& url, const fs::path& filename); - bool ExtractZipUpdate(const fs::path& zipname, const fs::path& targetpath); - - enum class WorkerOrder - { - Idle, - InProgress, // work command received - Exit, - CheckVersion, - UpdateVersion, - }; - enum class Result - { - NoUpdateAvailable, - UpdateAvailable, - UpdateDownloaded, - UpdateDownloadError, - ExtractSuccess, - ExtractError, - Success, - Error - }; - std::mutex m_mutex; - std::condition_variable m_condition; - std::atomic m_order = WorkerOrder::CheckVersion; - void WorkerThread(); - - void SubmitWorkerResult(Result newWorkerState); - -#if BOOST_OS_WINDOWS - bool WorkerThread_Windows(); -#endif -#if BOOST_OS_LINUX - bool WorkerThread_AppImage(); -#endif -#if BOOST_OS_MACOS - bool WorkerThread_MacBundle(); -#endif - - - std::string m_downloadUrl, m_changelogUrl; - std::array m_fileSHA256; - int m_gaugeMaxValue = 0; - - std::thread m_thread; - fs::path m_restartFile; - bool m_restartRequired = false; -}; diff --git a/src/gui/wxgui/ChecksumTool.cpp b/src/gui/wxgui/ChecksumTool.cpp deleted file mode 100644 index 8f9698a6..00000000 --- a/src/gui/wxgui/ChecksumTool.cpp +++ /dev/null @@ -1,792 +0,0 @@ -#include "wxgui/ChecksumTool.h" - -#include "Cafe/TitleList/GameInfo.h" -#include "wxgui/helpers/wxCustomEvents.h" -#include "util/helpers/helpers.h" -#include "wxgui/helpers/wxHelpers.h" -#include "wxgui/wxHelper.h" -#include "Cafe/Filesystem/WUD/wud.h" - -#include -#include - -#include /* EVP_Digest */ -#include /* SHA256_DIGEST_LENGTH */ -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "config/ActiveSettings.h" -#include "Cafe/TitleList/TitleList.h" - -const char kSchema[] = R"( -{ - "$schema": "http://json-schema.org/draft-04/schema#", - "type": "object", - "properties": { - "title_id": { - "type": "string" - }, - "region": { - "type": "integer" - }, - "version": { - "type": "integer" - }, - "wud_hash": { - "type": "string" - }, - "files": { - "type": "array", - "items": { - "type": "object", - "properties": { - "file": { - "type": "string" - }, - "hash": { - "type": "string" - } - }, - "required": [ - "file", - "hash" - ] - } - } - }, - "required": [ - "title_id", - "region", - "version", - "files" - ] -})"; - - -ChecksumTool::ChecksumTool(wxWindow* parent, wxTitleManagerList::TitleEntry& entry) - : wxDialog(parent, wxID_ANY, - formatWxString(_("Title checksum of {:08x}-{:08x}"), (uint32) (entry.title_id >> 32), (uint32) (entry.title_id & 0xFFFFFFFF)), - wxDefaultPosition, wxDefaultSize, wxCAPTION | wxFRAME_TOOL_WINDOW | wxSYSTEM_MENU | wxTAB_TRAVERSAL | wxCLOSE_BOX), m_entry(entry) -{ - - m_info = CafeTitleList::GetTitleInfoByUID(m_entry.location_uid); - if (!m_info.IsValid()) - throw std::runtime_error("Invalid title"); - - // only request online update once - static bool s_once = false; - if (!s_once) - { - s_once = true; - m_online_ready = std::async(std::launch::async, &ChecksumTool::LoadOnlineData, this); - } - else - m_enable_verify_button = 1; - - auto* sizer = new wxBoxSizer(wxVERTICAL); - { - auto* box_sizer = new wxStaticBoxSizer(new wxStaticBox(this, wxID_ANY, _("Verifying integrity of game files...")), wxVERTICAL); - auto* box = box_sizer->GetStaticBox(); - - m_progress = new wxGauge(box, wxID_ANY, 100, wxDefaultPosition, wxDefaultSize, wxGA_HORIZONTAL | wxGA_SMOOTH); - m_progress->SetMinSize({ 400, -1 }); - m_progress->SetValue(0); - box_sizer->Add(m_progress, 0, wxALL | wxEXPAND, 5); - - m_status = new wxStaticText(box, wxID_ANY, wxEmptyString); - m_status->Wrap(-1); - box_sizer->Add(m_status, 0, wxLEFT | wxRIGHT | wxBOTTOM | wxEXPAND, 5); - - sizer->Add(box_sizer, 0, wxEXPAND | wxALL, 5); - } - - { - auto* box_sizer = new wxStaticBoxSizer(new wxStaticBox(this, wxID_ANY, _("Control")), wxHORIZONTAL); - auto* box = box_sizer->GetStaticBox(); - - m_verify_online = new wxButton(box, wxID_ANY, _("Verify online")); - m_verify_online->SetToolTip(_("Verifies the checksum online")); - m_verify_online->Disable(); - m_verify_online->Bind(wxEVT_BUTTON, &ChecksumTool::OnVerifyOnline, this); - m_verify_online->Bind(wxEVT_ENABLE, [this](wxCommandEvent&) - { - ++m_enable_verify_button; - if (m_enable_verify_button >= 2) - { - // only enable if we have a file for it - const auto title_id_str = fmt::format("{:016x}", m_json_entry.title_id); - const auto default_file = fmt::format("{}_v{}.json", title_id_str, m_info.GetAppTitleVersion()); - - const auto checksum_path = ActiveSettings::GetUserDataPath("resources/checksums/{}", default_file); - if (exists(checksum_path)) - m_verify_online->Enable(); - } - }); - box_sizer->Add(m_verify_online, 0, wxALL | wxEXPAND, 5); - - m_verify_local = new wxButton(box, wxID_ANY, _("Verify with local file")); - m_verify_local->SetToolTip(_("Verifies the checksum with a local JSON file you can select")); - m_verify_local->Disable(); - m_verify_local->Bind(wxEVT_BUTTON, &ChecksumTool::OnVerifyLocal, this); - box_sizer->Add(m_verify_local, 0, wxALL | wxEXPAND, 5); - - m_export_button = new wxButton(box, wxID_ANY, _("Export")); - m_export_button->SetToolTip(_("Export the title checksum data to a local JSON file")); - m_export_button->Disable(); - m_export_button->Bind(wxEVT_BUTTON, &ChecksumTool::OnExportChecksums, this); - box_sizer->Add(m_export_button, 0, wxALL | wxEXPAND, 5); - - sizer->Add(box_sizer, 0, wxEXPAND | wxALL, 5); - } - - this->Bind(wxEVT_SET_GAUGE_VALUE, &ChecksumTool::OnSetGaugevalue, this); - - m_worker = std::thread(&ChecksumTool::DoWork, this); - - this->SetSizerAndFit(sizer); - this->Centre(wxBOTH); -} - -ChecksumTool::~ChecksumTool() -{ - m_running = false; - if (m_worker.joinable()) - m_worker.join(); -} - -std::size_t WriteCallback(const char* in, std::size_t size, std::size_t num, std::string* out) -{ - const std::size_t totalBytes(size * num); - out->append(in, totalBytes); - return totalBytes; -} - -void ChecksumTool::LoadOnlineData() const -{ - try - { - bool updated_required = true; - - std::string latest_commit; - - const auto checksum_path = ActiveSettings::GetUserDataPath("resources/checksums"); - if (exists(checksum_path)) - { - std::string current_commit; - // check for current version - std::ifstream file(checksum_path / "commit.txt"); - if (file.is_open()) - { - std::getline(file, current_commit); - file.close(); - } - - // check latest version - /* - https://api.github.com/repos/cemu-project/title-checksums/branches/master - https://api.github.com/repos/cemu-project/title-checksums/commits?per_page=1 - */ - std::string data; - auto* curl = curl_easy_init(); - curl_easy_setopt(curl, CURLOPT_URL, "https://api.github.com/repos/cemu-project/title-checksums/commits/master"); - curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback); - curl_easy_setopt(curl, CURLOPT_WRITEDATA, &data); - curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 1); - curl_easy_setopt(curl, CURLOPT_SSL_OPTIONS, CURLSSLOPT_NATIVE_CA); - curl_easy_setopt(curl, CURLOPT_USERAGENT, BUILD_VERSION_WITH_NAME_STRING); - - curl_easy_perform(curl); - - long http_code = 0; - curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_code); - curl_easy_cleanup(curl); - if (http_code == 200 && !data.empty()) - { - rapidjson::Document doc; - doc.Parse(data.c_str(), data.size()); - if (!doc.HasParseError() && doc.HasMember("sha")) - { - latest_commit = doc["sha"].GetString(); - if (boost::iequals(current_commit, latest_commit)) - { - updated_required = false; - } - } - } - } - else - { - // create directory since not available yet - fs::create_directories(checksum_path); - } - - if (updated_required) - { - std::string data; - auto* curl = curl_easy_init(); - curl_easy_setopt(curl, CURLOPT_URL, "https://github.com/cemu-project/title-checksums/archive/master.zip"); - curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback); - curl_easy_setopt(curl, CURLOPT_WRITEDATA, &data); - curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 1); - curl_easy_setopt(curl, CURLOPT_SSL_OPTIONS, CURLSSLOPT_NATIVE_CA); - curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1); - curl_easy_setopt(curl, CURLOPT_USERAGENT, BUILD_VERSION_WITH_NAME_STRING); - - curl_easy_perform(curl); - - long http_code = 0; - curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_code); - curl_easy_cleanup(curl); - if (http_code == 200 && !data.empty()) - { - // init zip source - zip_error_t error; - zip_error_init(&error); - zip_source_t* src; - if ((src = zip_source_buffer_create(data.data(), data.size(), 0, &error)) == nullptr) - { - zip_error_fini(&error); - return; - } - - auto* za = zip_open_from_source(src, ZIP_RDONLY, &error); - if (!za) - { - zip_source_free(src); - zip_error_fini(&error); - wxQueueEvent(m_verify_online, new wxCommandEvent(wxEVT_ENABLE)); - return; - } - - const auto numEntries = zip_get_num_entries(za, 0); - for (sint64 i = 0; i < numEntries; i++) - { - zip_stat_t sb = { 0 }; - if (zip_stat_index(za, i, 0, &sb) != 0) - continue; - - if (std::strstr(sb.name, "../") != nullptr || - std::strstr(sb.name, "..\\") != nullptr) - continue; // bad path - - if (boost::equals(sb.name, "title-checksums-master/")) - continue; - - // title-checksums-master/ - const auto path = checksum_path / &sb.name[sizeof("title-checksums-master")]; - - size_t sbNameLen = strlen(sb.name); - if (sbNameLen == 0) - continue; - - if (sb.name[sbNameLen - 1] == '/') - { - std::error_code ec; - fs::create_directories(path, ec); - continue; - } - - if (sb.size == 0) - continue; - - if (sb.size > (1024 * 1024 * 128)) - continue; // skip unusually huge files - - zip_file_t* zipFile = zip_fopen_index(za, i, 0); - if (zipFile == nullptr) - continue; - - std::vector buffer(sb.size); - if (zip_fread(zipFile, buffer.data(), sb.size) == sb.size) - { - std::ofstream file(path); - if (file.is_open()) - { - file.write(buffer.data(), sb.size); - file.flush(); - file.close(); - } - } - - zip_fclose(zipFile); - } - - zip_discard(za); - zip_error_fini(&error); - - std::ofstream file(checksum_path / "commit.txt"); - if (file.is_open()) - { - file << latest_commit; - file.close(); - } - } - } - } - catch(const std::exception& ex) - { - cemuLog_log(LogType::Force, "error on updating json checksum data: {}", ex.what()); - } - - wxQueueEvent(m_verify_online, new wxCommandEvent(wxEVT_ENABLE)); -} - -void ChecksumTool::OnSetGaugevalue(wxSetGaugeValue& event) -{ - event.GetGauge()->SetValue(event.GetValue()); - event.GetTextCtrl()->SetLabelText(event.GetText()); - - // no error - if(event.GetInt() == 0 && event.GetValue() == 100) - { - m_export_button->Enable(); - m_verify_local->Enable(); - wxPostEvent(m_verify_online, wxCommandEvent(wxEVT_ENABLE)); - } -} - -void ChecksumTool::OnExportChecksums(wxCommandEvent& event) -{ - // TODO: merge if json already exists - wxDirDialog dialog(this, _("Export checksum entry"), "", wxDD_DEFAULT_STYLE | wxDD_DIR_MUST_EXIST); - if (dialog.ShowModal() != wxID_OK || dialog.GetPath().IsEmpty()) - return; - - rapidjson::Document doc; - doc.SetObject(); - auto& a = doc.GetAllocator(); - /* - title_id - region - version - wud_hash - files: - [ - {file, hash} - ] - */ - - auto title_id_str = fmt::format("{:016x}", m_json_entry.title_id); - doc.AddMember("title_id", rapidjson::StringRef(title_id_str.c_str(), title_id_str.size()), a); - doc.AddMember("region", (int)m_info.GetMetaRegion(), a); - doc.AddMember("version", m_info.GetAppTitleVersion(), a); - if (!m_json_entry.wud_hash.empty()) - doc.AddMember("wud_hash", rapidjson::StringRef(m_json_entry.wud_hash.c_str(), m_json_entry.wud_hash.size()), a); - - rapidjson::Value entry_array(rapidjson::kArrayType); - - rapidjson::Value file_array(rapidjson::kArrayType); - for(const auto& file : m_json_entry.file_hashes) - { - rapidjson::Value file_entry; - file_entry.SetObject(); - - file_entry.AddMember("file", rapidjson::StringRef(file.first.c_str(), file.first.size()), a); - file_entry.AddMember("hash", rapidjson::StringRef(file.second.c_str(), file.second.size()), a); - - file_array.PushBack(file_entry, a); - } - - doc.AddMember("files", file_array, a); - - std::filesystem::path target_file{ dialog.GetPath().c_str().AsInternal() }; - target_file /= fmt::format("{}_v{}.json", title_id_str, m_info.GetAppTitleVersion()); - - std::ofstream file(target_file); - if(file.is_open()) - { - rapidjson::OStreamWrapper osw(file); - rapidjson::PrettyWriter writer(osw); - //rapidjson::GenericSchemaValidator > validator(schema, writer); - doc.Accept(writer); - wxMessageBox(_("Export successful"), wxMessageBoxCaptionStr, wxOK | wxCENTRE, this); - } - else - { - wxMessageBox(formatWxString(_("Can't write to file: {}"), target_file.string()), _("Error"), wxOK | wxCENTRE | wxICON_ERROR, this); - } -} - -void ChecksumTool::VerifyJsonEntry(const rapidjson::Document& doc) -{ - rapidjson::Document sdoc; - sdoc.Parse(kSchema, std::size(kSchema)); - wxASSERT(!sdoc.HasParseError()); - rapidjson::SchemaDocument schema(sdoc); - rapidjson::SchemaValidator validator(schema); - if (!doc.Accept(validator)) - { - //// validation error: - //rapidjson::StringBuffer sb; - //validator.GetInvalidSchemaPointer().StringifyUriFragment(sb); - //printf("Invalid schema: %s\n", sb.GetString()); - //printf("Invalid keyword: %s\n", validator.GetInvalidSchemaKeyword()); - //sb.Clear(); - //validator.GetInvalidDocumentPointer().StringifyUriFragment(sb); - //printf("Invalid document: %s\n", sb.GetString()); - ///* - //Invalid schema: # - //Invalid keyword: required - //Invalid document: # - // */ - - wxMessageBox(_("JSON file doesn't satisfy needed schema"), _("Error"), wxOK | wxCENTRE | wxICON_ERROR, this); - return; - } - - try - { - JsonEntry test_entry{}; - test_entry.title_id = ConvertString(doc["title_id"].GetString(), 16); - test_entry.region = (CafeConsoleRegion)doc["region"].GetInt(); - test_entry.version = doc["version"].GetInt(); - if (doc.HasMember("wud_hash")) - test_entry.wud_hash = doc["wud_hash"].GetString(); - - for (const auto& v : doc["files"].GetArray()) - { - std::filesystem::path genericFilePath(v["file"].GetString(), std::filesystem::path::generic_format); // convert path to generic form (forward slashes) - test_entry.file_hashes[genericFilePath.generic_string()] = v["hash"].GetString(); - } - - if (m_json_entry.title_id != test_entry.title_id) - { - wxMessageBox(formatWxString(_("The file you are comparing with is for a different title.")), _("Error"), wxOK | wxCENTRE | wxICON_ERROR, this); - return; - } - if (m_json_entry.version != test_entry.version) - { - wxMessageBox(formatWxString(_("Wrong version: {}"), test_entry.version), _("Error"), wxOK | wxCENTRE | wxICON_ERROR, this); - return; - } - if (m_json_entry.region != test_entry.region) - { - wxMessageBox(formatWxString(_("Wrong region: {}"), test_entry.region), _("Error"), wxOK | wxCENTRE | wxICON_ERROR, this); - return; - } - if (!m_json_entry.wud_hash.empty()) - { - if (test_entry.wud_hash.empty()) - { - wxMessageBox(_("The verification data doesn't include a WUD hash!"), _("Error"), wxOK | wxCENTRE | wxICON_WARNING, this); - return; - } - if(!boost::iequals(test_entry.wud_hash, m_json_entry.wud_hash)) - { - wxMessageBox(formatWxString(_("Your game image is invalid!\n\nYour hash:\n{}\n\nExpected hash:\n{}"), m_json_entry.wud_hash, test_entry.wud_hash), _("Error"), wxOK | wxCENTRE | wxICON_ERROR, this); - return; - } - } - else - { - std::map> invalid_hashes; - std::vector missing_files; - const auto writeMismatchInfoToLog = [this, &missing_files, &invalid_hashes]() - { - wxFileDialog dialog(this, _("Select a file to export the errors"), wxEmptyString, wxEmptyString, "Error list (*.txt)|*.txt", wxFD_SAVE | wxFD_OVERWRITE_PROMPT); - if (dialog.ShowModal() != wxID_OK || dialog.GetPath().IsEmpty()) - return; - - const std::string path = dialog.GetPath().utf8_string(); - std::ofstream file(path); - if (file.is_open()) - { - if (!missing_files.empty()) - { - file << "The following files are missing:\n"; - for (const auto& f : missing_files) - file << "\t" << f << "\n"; - - file << "\n"; - } - - if (!invalid_hashes.empty()) - { - file << "The following files have an invalid hash (name | current hash | expected hash):\n"; - for (const auto& f : invalid_hashes) - file << "\t" << f.first << " | " << f.second.first << " | " << f.second.second << "\n"; - } - file.flush(); - file.close(); - - wxLaunchDefaultBrowser(formatWxString("file:{}", path)); - } - else - wxMessageBox(_("Can't open file to write!"), _("Error"), wxOK | wxCENTRE | wxICON_ERROR, this); - }; - - for (const auto& f : test_entry.file_hashes) - { - const auto it = m_json_entry.file_hashes.find(f.first); - if (it == m_json_entry.file_hashes.cend()) - { - missing_files.emplace_back(f.first); - } - else if (!boost::iequals(f.second, it->second)) - { - invalid_hashes[f.first] = std::make_pair(it->second, f.second); - } - } - - // files are missing but rest is okay - if ((!missing_files.empty() || !invalid_hashes.empty()) && (missing_files.size() + invalid_hashes.size()) < 30) - { - // the list of missing + invalid hashes is short enough that we can print it to the message box - std::stringstream str; - if (missing_files.size() > 0) - { - str << _("The following files are missing:").ToUTF8().data() << "\n"; - for (const auto& v : missing_files) - str << v << "\n"; - if(invalid_hashes.size() > 0) - str << "\n"; - } - if (invalid_hashes.size() > 0) - { - str << _("The following files are damaged:").ToUTF8().data() << "\n"; - for (const auto& v : invalid_hashes) - str << v.first << "\n"; - } - - wxMessageBox(str.str(), _("Error"), wxOK | wxCENTRE | wxICON_ERROR, this); - return; - } - else if (missing_files.empty() && !invalid_hashes.empty()) - { - const int result = wxMessageBox(formatWxString( - _("{} files have an invalid hash!\nDo you want to export a list of them to a file?"), - invalid_hashes.size()), _("Error"), wxYES_NO | wxCENTRE | wxICON_ERROR, this); - if (result == wxYES) - { - writeMismatchInfoToLog(); - } - return; - } - else if (!missing_files.empty() && !invalid_hashes.empty()) - { - const int result = wxMessageBox(formatWxString( - _("Multiple issues with your game files have been found!\nDo you want to export them to a file?"), - invalid_hashes.size()), _("Error"), wxYES_NO | wxCENTRE | wxICON_ERROR, this); - if (result == wxYES) - { - writeMismatchInfoToLog(); - } - return; - } - } - wxMessageBox(_("Your game files are valid"), _("Success"), wxOK | wxCENTRE, this); - } - catch (const std::exception& ex) - { - wxMessageBox(formatWxString(_("JSON parse error: {}"), ex.what()), _("Error"), wxOK | wxCENTRE | wxICON_ERROR, this); - } - -} - -void ChecksumTool::OnVerifyOnline(wxCommandEvent& event) -{ - const auto title_id_str = fmt::format("{:016x}", m_json_entry.title_id); - const auto default_file = fmt::format("{}_v{}.json", title_id_str, m_info.GetAppTitleVersion()); - - const auto checksum_path = ActiveSettings::GetUserDataPath("resources/checksums/{}", default_file); - if(!exists(checksum_path)) - return; - - std::ifstream file(checksum_path); - if (!file.is_open()) - { - wxMessageBox(_("Can't open file!"), _("Error"), wxOK | wxCENTRE | wxICON_ERROR, this); - return; - } - - rapidjson::IStreamWrapper str(file); - rapidjson::Document d; - d.ParseStream(str); - if (d.HasParseError()) - { - wxMessageBox(_("Can't parse JSON file!"), _("Error"), wxOK | wxCENTRE | wxICON_ERROR, this); - return; - } - - VerifyJsonEntry(d); -} - -void ChecksumTool::OnVerifyLocal(wxCommandEvent& event) -{ - const auto title_id_str = fmt::format("{:016x}", m_json_entry.title_id); - const auto default_file = fmt::format("{}_v{}.json", title_id_str, m_info.GetAppTitleVersion()); - wxFileDialog file_dialog(this, _("Open checksum entry"), "", default_file.c_str(),"JSON files (*.json)|*.json", wxFD_OPEN | wxFD_FILE_MUST_EXIST); - if (file_dialog.ShowModal() != wxID_OK || file_dialog.GetPath().IsEmpty()) - return; - - std::filesystem::path filename{ file_dialog.GetPath().c_str().AsInternal() }; - std::ifstream file(filename); - if(!file.is_open()) - { - wxMessageBox(_("Can't open file!"), _("Error"), wxOK | wxCENTRE | wxICON_ERROR, this); - return; - } - - rapidjson::IStreamWrapper str(file); - rapidjson::Document d; - d.ParseStream(str); - if (d.HasParseError()) - { - wxMessageBox(_("Can't parse JSON file!"), _("Error"), wxOK | wxCENTRE | wxICON_ERROR, this); - return; - } - - VerifyJsonEntry(d); -} - -static void _fscGetAllFiles(std::set& allFilesOut, const std::string& fscBasePath, const std::string& relativePath) -{ - sint32 fscStatus; - FSCVirtualFile* fsc = fsc_openDirIterator((fscBasePath + relativePath).c_str(), &fscStatus); - cemu_assert(fsc); - FSCDirEntry dirEntry; - while (fsc_nextDir(fsc, &dirEntry)) - { - if (dirEntry.isDirectory) - { - _fscGetAllFiles(allFilesOut, fscBasePath, std::string(relativePath).append(dirEntry.GetPath()).append("/")); - } - else - { - allFilesOut.emplace(std::string(relativePath).append(dirEntry.GetPath())); - } - } - delete fsc; -} - -void ChecksumTool::DoWork() -{ - m_json_entry.title_id = m_info.GetAppTitleId(); - m_json_entry.region = m_info.GetMetaRegion(); - m_json_entry.version = m_info.GetAppTitleVersion(); - - static_assert(SHA256_DIGEST_LENGTH == 32); - - std::array checksum{}; - - switch (m_info.GetFormat()) - { - case TitleInfo::TitleDataFormat::WUD: - { - const auto path = m_entry.path.string(); - wxQueueEvent(this, new wxSetGaugeValue(1, m_progress, m_status, formatWxString(_("Reading game image: {}"), path))); - - wud_t* wud = wud_open(m_info.GetPath()); - if (!wud) - throw std::runtime_error("can't open game image"); - - const auto wud_size = wud_getWUDSize(wud); - std::vector buffer(1024 * 1024 * 8); - - EVP_MD_CTX *sha256 = EVP_MD_CTX_new(); - EVP_DigestInit(sha256, EVP_sha256()); - - uint32 read = 0; - size_t offset = 0; - auto size = wud_size; - do - { - if (!m_running.load(std::memory_order_relaxed)) - { - wud_close(wud); - return; - } - - read = wud_readData(wud, buffer.data(), std::min(buffer.size(), (size_t)wud_size - offset), offset); - offset += read; - size -= read; - - EVP_DigestUpdate(sha256, buffer.data(), read); - - wxQueueEvent(this, new wxSetGaugeValue((int)((offset * 90) / wud_size), m_progress, m_status, formatWxString(_("Reading game image: {0}/{1} kB"), offset / 1024, wud_size / 1024))); - } while (read != 0 && size > 0); - wud_close(wud); - - wxQueueEvent(this, new wxSetGaugeValue(90, m_progress, m_status, formatWxString(_("Generating checksum of game image: {}"), path))); - - if (!m_running.load(std::memory_order_relaxed)) - return; - - EVP_DigestFinal_ex(sha256, checksum.data(), NULL); - EVP_MD_CTX_free(sha256); - - std::stringstream str; - for (const auto& b : checksum) - { - str << fmt::format("{:02X}", b); - } - - m_json_entry.wud_hash = str.str(); - - wxQueueEvent(this, new wxSetGaugeValue(100, m_progress, m_status, formatWxString(_("Generated checksum of game image: {}"), path))); - break; - } - default: - // we hash the individual files for all formats except WUD/WUX - std::string temporaryMountPath = TitleInfo::GetUniqueTempMountingPath(); - m_info.Mount(temporaryMountPath.c_str(), "", FSC_PRIORITY_BASE); - wxQueueEvent(this, new wxSetGaugeValue(1, m_progress, m_status, _("Grabbing game files"))); - - // get list of all files - std::set files; - _fscGetAllFiles(files, temporaryMountPath, ""); - - const size_t file_count = files.size(); - size_t counter = 0; - for (const auto& filename : files) - { - auto fileData = fsc_extractFile((temporaryMountPath + "/" + filename).c_str()); - if (!fileData) - { - cemuLog_log(LogType::Force, "Failed to open {}", filename); - continue; - } - - SHA256(fileData->data(), fileData->size(), checksum.data()); - - std::stringstream str; - for (const auto& b : checksum) - { - str << fmt::format("{:02X}", b); - } - - // store relative path and hash - m_json_entry.file_hashes[filename] = str.str(); - - ++counter; - wxQueueEvent(this, new wxSetGaugeValue((int)((counter * 100) / file_count), m_progress, m_status, formatWxString(_("Hashing game file: {}/{}"), counter, file_count))); - - if (!m_running.load(std::memory_order_relaxed)) - { - m_info.Unmount(temporaryMountPath.c_str()); - return; - } - } - m_info.Unmount(temporaryMountPath.c_str()); - - wxQueueEvent(this, new wxSetGaugeValue(100, m_progress, m_status, formatWxString(_("Generated checksum of {} game files"), file_count))); - break; - } -} diff --git a/src/gui/wxgui/ChecksumTool.h b/src/gui/wxgui/ChecksumTool.h deleted file mode 100644 index ae0ea760..00000000 --- a/src/gui/wxgui/ChecksumTool.h +++ /dev/null @@ -1,51 +0,0 @@ -#pragma once -#include -#include "wxgui/components/wxTitleManagerList.h" -#include "Cafe/TitleList/TitleInfo.h" -#include - -class wxSetGaugeValue; - -class ChecksumTool : public wxDialog -{ -public: - ChecksumTool(wxWindow* parent, wxTitleManagerList::TitleEntry& entry); - ~ChecksumTool(); - -private: - std::future m_online_ready; - void LoadOnlineData() const; - void VerifyJsonEntry(const rapidjson::Document& doc); - - void OnSetGaugevalue(wxSetGaugeValue& event); - void OnExportChecksums(wxCommandEvent& event); - void OnVerifyOnline(wxCommandEvent& event); - void OnVerifyLocal(wxCommandEvent& event); - - void DoWork(); - std::atomic_bool m_running = true; - std::thread m_worker; - - class wxGauge* m_progress; - class wxStaticText* m_status; - class wxButton *m_verify_online, *m_verify_local, *m_export_button; - int m_enable_verify_button = 0; - - TitleInfo m_info; - wxTitleManagerList::TitleEntry m_entry; - wxColour m_default_color; - - struct JsonEntry - { - uint64 title_id; - uint32 version; - CafeConsoleRegion region; - - std::string wud_hash; - std::map file_hashes; - }; - JsonEntry m_json_entry; - inline static std::mutex s_mutex{}; - inline static std::vector s_entries{}; - -}; diff --git a/src/gui/wxgui/DownloadCustomGraphicPackWindow.cpp b/src/gui/wxgui/DownloadCustomGraphicPackWindow.cpp deleted file mode 100644 index f261061d..00000000 --- a/src/gui/wxgui/DownloadCustomGraphicPackWindow.cpp +++ /dev/null @@ -1,418 +0,0 @@ -#include "wxgui/DownloadCustomGraphicPackWindow.h" -#include "Cafe/CafeSystem.h" -#include "config/ActiveSettings.h" - -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -static size_t curlDownloadFile_writeData(void *ptr, size_t size, size_t nmemb, DownloadCustomGraphicPackWindow::curlDownloadFileState_t* downloadState) -{ - const size_t writeSize = size * nmemb; - const size_t currentSize = downloadState->fileData.size(); - const size_t newSize = currentSize + writeSize; - auto* bytePtr = static_cast(ptr); - downloadState->fileData.insert(downloadState->fileData.end(), bytePtr, bytePtr + writeSize); - return writeSize; -} - -static int progress_callback(DownloadCustomGraphicPackWindow::curlDownloadFileState_t* downloadState, curl_off_t dltotal, curl_off_t dlnow, curl_off_t ultotal, curl_off_t ulnow) -{ - if (downloadState->isCanceled) - return 1; - - if (dltotal > 1.0) - downloadState->progress = dlnow / dltotal; - else - downloadState->progress = 0.0; - return 0; -} - -static bool curlDownloadFile(const char *url, DownloadCustomGraphicPackWindow::curlDownloadFileState_t* downloadState) -{ - CURL* curl = curl_easy_init(); - if (curl == nullptr) - return false; - - downloadState->progress = 0.0; - curl_easy_setopt(curl, CURLOPT_URL, url); - curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, curlDownloadFile_writeData); - curl_easy_setopt(curl, CURLOPT_WRITEDATA, downloadState); - curl_easy_setopt(curl, CURLOPT_XFERINFOFUNCTION, progress_callback); - curl_easy_setopt(curl, CURLOPT_PROGRESSDATA, downloadState); - curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0); - curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); - curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 10L); - curl_easy_setopt(curl, CURLOPT_LOW_SPEED_LIMIT, 30L); - curl_easy_setopt(curl, CURLOPT_LOW_SPEED_TIME, 15L); - curl_easy_setopt(curl, CURLOPT_USERAGENT, BUILD_VERSION_WITH_NAME_STRING); - downloadState->fileData.resize(0); - const CURLcode res = curl_easy_perform(curl); - curl_easy_cleanup(curl); - return res == CURLE_OK; -} - -DownloadCustomGraphicPackWindow::DownloadCustomGraphicPackWindow(wxWindow* parent) - : wxDialog(parent, wxID_ANY, _("Download Graphic Pack from URL"), wxDefaultPosition, wxDefaultSize, wxCAPTION | wxMINIMIZE_BOX | wxSYSTEM_MENU | wxTAB_TRAVERSAL | wxCLOSE_BOX), - m_stage(StageDone), m_currentStage(StageDone) -{ - auto* sizer = new wxBoxSizer(wxVERTICAL); - - m_urlField = new wxTextCtrl(this, wxID_ANY, wxEmptyString); - m_urlField->SetHint(_("Enter download URL...")); - sizer->Add(m_urlField, 0, wxALL | wxEXPAND, 5); - - m_processBar = new wxGauge(this, wxID_ANY, 100, wxDefaultPosition, wxSize(500, 20), wxGA_HORIZONTAL); - m_processBar->SetValue(0); - m_processBar->SetRange(100); - sizer->Add(m_processBar, 0, wxALL | wxEXPAND, 5); - - auto* buttonSizer = new wxBoxSizer(wxHORIZONTAL); - - m_statusText = new wxStaticText(this, wxID_ANY, _("Ready...")); - buttonSizer->Add(m_statusText, 0, wxALIGN_CENTER_VERTICAL | wxALL, 5); - - buttonSizer->AddStretchSpacer(1); - - auto* m_closeButton = new wxButton(this, wxID_ANY, _("Close")); - m_closeButton->Bind(wxEVT_BUTTON, &DownloadCustomGraphicPackWindow::OnCancelButton, this); - buttonSizer->Add(m_closeButton, 0, wxALIGN_CENTER_VERTICAL | wxALL, 5); - - m_downloadButton = new wxButton(this, wxID_ANY, _("Download")); - m_downloadButton->Bind(wxEVT_BUTTON, &DownloadCustomGraphicPackWindow::OnDownloadButton, this); - buttonSizer->Add(m_downloadButton, 0, wxALIGN_CENTER_VERTICAL | wxALL, 5); - - sizer->Add(buttonSizer, 0, wxEXPAND | wxALL, 5); - - this->SetSizer(sizer); - this->Centre(wxBOTH); - - wxWindowBase::Layout(); - wxWindowBase::Fit(); - - m_timer = new wxTimer(this); - this->Bind(wxEVT_TIMER, &DownloadCustomGraphicPackWindow::OnUpdate, this); - this->Bind(wxEVT_CLOSE_WINDOW, &DownloadCustomGraphicPackWindow::OnClose, this); - m_timer->Start(100); - - m_downloadState = std::make_unique(); -} - -DownloadCustomGraphicPackWindow::~DownloadCustomGraphicPackWindow() -{ - if (m_downloadState) - m_downloadState->isCanceled = true; - - m_timer->Stop(); - if (m_thread.joinable()) - m_thread.join(); -} - -int DownloadCustomGraphicPackWindow::ShowModal() -{ - wxDialog::ShowModal(); - return wxID_OK; -} - -void DownloadCustomGraphicPackWindow::OnClose(wxCloseEvent& event) -{ - if (m_downloadState) - { - m_downloadState->isCanceled = true; - } - - m_timer->Stop(); - if (m_thread.joinable()) - m_thread.join(); - - event.Skip(); -} - -void DownloadCustomGraphicPackWindow::OnUpdate(const wxTimerEvent& event) -{ - if (m_currentStage >= StageDone) - { - m_downloadButton->Enable(); - m_urlField->Enable(); - } - else - { - m_downloadButton->Disable(); - m_urlField->Disable(); - } - - if (m_currentStage == StageDownloading) - { - const sint32 processedSize = (sint32)(m_downloadState->progress * 100.0f); - if (m_processBar->GetValue() != processedSize) - m_processBar->SetValue(processedSize); - } - else if (m_currentStage == StageExtracting) - { - const sint32 processedSize = (sint32)(m_extractionProgress * 100.0f); - if (m_processBar->GetValue() != processedSize) - m_processBar->SetValue(processedSize); - } - - if (m_currentStage != m_stage) - { - wxString status = "..."; - const wxColour* colour = wxWHITE; - - switch (m_stage) - { - case StageDownloading: - status = "Downloading..."; - colour = wxWHITE; - break; - case StageVerifying: - status = "Verifying..."; - colour = wxWHITE; - break; - case StageExtracting: - status = "Extracting..."; - colour = wxWHITE; - break; - case StageDone: - status = "Done!"; - colour = wxGREEN; - m_processBar->SetValue(100.0); - break; - case StageErrConnectFailed: - if (m_urlField->GetValue().empty()) - { - status = "Please enter a valid URL."; - colour = wxWHITE; - } - else - { - status = "ERROR: Connection failed."; - colour = wxRED; - } - m_processBar->SetValue(0.0); - break; - case StageErrInvalidPack: - status = "ERROR: Invalid pack."; - colour = wxRED; - m_processBar->SetValue(0.0); - break; - case StageErrSourceFailed: - status = "ERROR: Failed to create ZIP source."; - colour = wxRED; - m_processBar->SetValue(0.0); - break; - case StageErrOpenFailed: - status = "ERROR: Failed to open downloaded ZIP."; - colour = wxRED; - m_processBar->SetValue(0.0); - break; - case StageErrConflict: - status = "ERROR: File conflict. Pack already installed?"; - colour = wxRED; - m_processBar->SetValue(0.0); - break; - } - - m_currentStage = m_stage; - m_statusText->SetLabel(status); - m_statusText->SetForegroundColour(*colour); - } -} - -void DownloadCustomGraphicPackWindow::OnCancelButton(const wxCommandEvent& event) -{ - Close(); -} - -void DownloadCustomGraphicPackWindow::OnDownloadButton(const wxCommandEvent& event) -{ - m_downloadButton->Disable(); - m_urlField->Disable(); - - if (m_thread.joinable()) - m_thread.join(); - - m_thread = std::thread(&DownloadCustomGraphicPackWindow::UpdateThread, this); -} - -void DownloadCustomGraphicPackWindow::UpdateThread() -{ - m_stage = StageDownloading; - if (curlDownloadFile(m_urlField->GetValue(), m_downloadState.get()) == false) - { - m_stage = StageErrConnectFailed; - return; - } - - m_extractionProgress = 0.0; - m_stage = StageExtracting; - - zip_source_t *src; - zip_t *za; - zip_error_t error; - - // init zip source - zip_error_init(&error); - if ((src = zip_source_buffer_create(m_downloadState->fileData.data(), m_downloadState->fileData.size(), 0, &error)) == NULL) - { - zip_error_fini(&error); - m_stage = StageErrSourceFailed; - return; - } - - // open zip from source - if ((za = zip_open_from_source(src, 0, &error)) == NULL) - { - zip_source_free(src); - zip_error_fini(&error); - m_stage = StageErrOpenFailed; - return; - } - - auto path = ActiveSettings::GetUserDataPath("graphicPacks/customGraphicPacks"); - fs::create_directories(path); - - // check if zip root directly contains a rules.txt - zip_stat_t zs; - zip_stat_init(&zs); - bool hasRootRulesTxt = (zip_stat(za, "rules.txt", 0, &zs) == 0); - - fs::path extractionRoot = path; - - if (hasRootRulesTxt) - { - // make a folder and extract into that - wxString urlStr = m_urlField->GetValue(); - std::string folderName = "NewCustomPack"; // fallback - - int lastSlash = urlStr.Find('/', true); - wxString fileNameBase = (lastSlash != wxNOT_FOUND) ? urlStr.Mid(lastSlash + 1) : urlStr; - - int lastDot = fileNameBase.Find('.', true); - if (lastDot != wxNOT_FOUND) - { - fileNameBase = fileNameBase.Left(lastDot); - } - - fileNameBase.Trim(true).Trim(false); - if (!fileNameBase.IsEmpty()) - { - folderName = fileNameBase.ToStdString(); - } - - extractionRoot = path / folderName; - fs::create_directories(extractionRoot); - } - else - { - // not a gfxpack - m_stage = StageErrInvalidPack; - zip_close(za); - return; - } - - // extract - bool err = false; - zip_int64_t numEntries = zip_get_num_entries(za, 0); - for (zip_int64_t i = 0; i < numEntries; i++) - { - m_extractionProgress = (double)i / (double)numEntries; - - if (m_downloadState->isCanceled) - { - err = true; - } - - if (err) - { - break; - } - - zip_stat_t sb = { 0 }; - zip_stat_init(&sb); - if (zip_stat_index(za, i, 0, &sb) != 0) - { - assert_dbg(); - continue; - } - - std::string fileName = sb.name; - if (std::strstr(sb.name, "../") != nullptr || - std::strstr(sb.name, "..\\") != nullptr || - (!fileName.empty() && (fileName[0] == '/' || fileName[0] == '\\'))) - { - // bad path, CommunityGraphicPacks silently continues - // but this is a low-trust environment, so we halt - m_stage = StageErrInvalidPack; - err = true; - break; - } - - fs::path targetDest = extractionRoot / fileName; - - if (!fileName.empty() && fileName.back() == '/') - { - fs::create_directories(targetDest); - continue; - } - - if (fs::exists(targetDest) && fs::is_regular_file(targetDest)) - { - m_stage = StageErrConflict; - err = true; - break; - } - - fs::create_directories(targetDest.parent_path()); - - zip_file_t* zf = zip_fopen_index(za, i, 0); - if (!zf) - { - continue; - } - - if (sb.size > 1000000000) // 1GB suspicious limit per file - { - m_stage = StageErrInvalidPack; - err = true; - break; - } - - std::vector fileBuffer(sb.size); - - if (zip_fread(zf, fileBuffer.data(), sb.size) == sb.size) - { - std::unique_ptr fs(FileStream::createFile2(targetDest)); - if (fs) - { - fs->writeData(fileBuffer.data(), sb.size); - } - } - else - { - err = true; - m_stage = StageErrInvalidPack; - } - - zip_fclose(zf); - } - - zip_discard(za); - zip_error_fini(&error); - - if (!err) - { - m_stage = StageDone; - } -} diff --git a/src/gui/wxgui/DownloadCustomGraphicPackWindow.h b/src/gui/wxgui/DownloadCustomGraphicPackWindow.h deleted file mode 100644 index 586a8644..00000000 --- a/src/gui/wxgui/DownloadCustomGraphicPackWindow.h +++ /dev/null @@ -1,66 +0,0 @@ -#pragma once - -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -struct curlDownloadFileState_t; - -class DownloadCustomGraphicPackWindow : public wxDialog -{ -public: - DownloadCustomGraphicPackWindow(wxWindow* parent); - ~DownloadCustomGraphicPackWindow() override; - - int ShowModal() override; - void OnClose(wxCloseEvent& event); - void OnUpdate(const wxTimerEvent& event); - void OnCancelButton(const wxCommandEvent& event); - void OnDownloadButton(const wxCommandEvent& event); - -public: - struct curlDownloadFileState_t - { - std::vector fileData; - std::atomic progress = 0.0; - std::atomic isCanceled = false; - }; - -private: - void UpdateThread(); - - enum DownloadStage_t - { - StageDownloading, - StageExtracting, - StageVerifying, - StageDone, - - // Error stages - StageErrConnectFailed, - StageErrSourceFailed, - StageErrInvalidPack, - StageErrOpenFailed, - StageErrConflict, - }; - - std::unique_ptr m_downloadState; - std::atomic m_stage; - DownloadStage_t m_currentStage; - std::atomic m_extractionProgress; - - wxTimer* m_timer; - wxTextCtrl* m_urlField; - wxGauge* m_processBar; - wxStaticText* m_statusText; - wxButton* m_downloadButton; - std::thread m_thread; -}; diff --git a/src/gui/wxgui/DownloadGraphicPacksWindow.cpp b/src/gui/wxgui/DownloadGraphicPacksWindow.cpp deleted file mode 100644 index b2f8b849..00000000 --- a/src/gui/wxgui/DownloadGraphicPacksWindow.cpp +++ /dev/null @@ -1,404 +0,0 @@ -#include "wxgui/wxgui.h" -#include "wxgui/DownloadGraphicPacksWindow.h" - -#include -#include -#include -#include -#include - -#include "config/ActiveSettings.h" -#include "Common/FileStream.h" - -#include "Cafe/CafeSystem.h" - -struct DownloadGraphicPacksWindow::curlDownloadFileState_t -{ - std::vector fileData; - double progress; -}; - -size_t DownloadGraphicPacksWindow::curlDownloadFile_writeData(void *ptr, size_t size, size_t nmemb, curlDownloadFileState_t* downloadState) -{ - const size_t writeSize = size * nmemb; - const size_t currentSize = downloadState->fileData.size(); - const size_t newSize = currentSize + writeSize; - downloadState->fileData.resize(newSize); - memcpy(downloadState->fileData.data() + currentSize, ptr, writeSize); - return writeSize; -} - -int DownloadGraphicPacksWindow::progress_callback(curlDownloadFileState_t* downloadState, curl_off_t dltotal, curl_off_t dlnow, curl_off_t ultotal, curl_off_t ulnow) -{ - if (dltotal > 1.0) - downloadState->progress = dlnow / dltotal; - else - downloadState->progress = 0.0; - return 0; -} - -bool DownloadGraphicPacksWindow::curlDownloadFile(const char *url, curlDownloadFileState_t* downloadState) -{ - CURL* curl = curl_easy_init(); - if (curl == nullptr) - return false; - - downloadState->progress = 0.0; - curl_easy_setopt(curl, CURLOPT_URL, url); - curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, curlDownloadFile_writeData); - curl_easy_setopt(curl, CURLOPT_WRITEDATA, downloadState); - curl_easy_setopt(curl, CURLOPT_XFERINFOFUNCTION, progress_callback); - curl_easy_setopt(curl, CURLOPT_PROGRESSDATA, downloadState); - curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0); - curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, true); - curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 2); - curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 1); - curl_easy_setopt(curl, CURLOPT_SSL_OPTIONS, CURLSSLOPT_NATIVE_CA); - - curl_easy_setopt(curl, CURLOPT_USERAGENT, BUILD_VERSION_WITH_NAME_STRING); - downloadState->fileData.resize(0); - const CURLcode res = curl_easy_perform(curl); - curl_easy_cleanup(curl); - return res == CURLE_OK; -} - -// returns true if the version matches -bool checkGraphicPackDownloadedVersion(const char* nameVersion, bool& hasVersionFile) -{ - hasVersionFile = false; - const auto path = ActiveSettings::GetUserDataPath("graphicPacks/downloadedGraphicPacks/version.txt"); - std::unique_ptr file(FileStream::openFile2(path)); - - std::string versionInFile; - if (file && file->readLine(versionInFile)) - { - return boost::iequals(versionInFile, nameVersion); - } - return false; -} - -void createGraphicPackDownloadedVersionFile(const char* nameVersion) -{ - const auto path = ActiveSettings::GetUserDataPath("graphicPacks/downloadedGraphicPacks/version.txt"); - std::unique_ptr file(FileStream::createFile2(path)); - if (file) - file->writeString(nameVersion); - else - { - cemuLog_log(LogType::Force, "Failed to write graphic pack version.txt"); - } -} - -void deleteDownloadedGraphicPacks() -{ - const auto path = ActiveSettings::GetUserDataPath("graphicPacks/downloadedGraphicPacks"); - std::error_code er; - if (!fs::exists(path, er)) - return; - try - { - for (auto& p : fs::directory_iterator(path)) - { - fs::remove_all(p.path(), er); - } - } - catch (std::filesystem::filesystem_error& e) - { - cemuLog_log(LogType::Force, "Error in deleteDownloadedGraphicPacks():"); - cemuLog_log(LogType::Force, e.what()); - } -} - -void DownloadGraphicPacksWindow::UpdateThread() -{ - // get github url - std::string githubAPIUrl; - curlDownloadFileState_t tempDownloadState; - std::string queryUrl("https://cemu.info/api2/query_graphicpack_url.php?"); - char temp[64]; - sprintf(temp, "version=%d.%d.%d", EMULATOR_VERSION_MAJOR, EMULATOR_VERSION_MINOR, EMULATOR_VERSION_PATCH); - queryUrl.append(temp); - queryUrl.append("&"); - sprintf(temp, "t=%u", (uint32)std::chrono::seconds(std::time(NULL)).count()); // add a dynamic part to the url to bypass overly aggressive caching (like some proxies do) - queryUrl.append(temp); - if (curlDownloadFile(queryUrl.c_str(), &tempDownloadState)) - { - // convert downloaded data to url string - githubAPIUrl.assign(tempDownloadState.fileData.cbegin(), tempDownloadState.fileData.cend()); - } - if (githubAPIUrl.empty()) - { - // cemu api request failed, use hardcoded github url - cemuLog_log(LogType::Force, "Graphic pack update request failed or returned invalid URL. Using default repository URL instead"); - githubAPIUrl = "https://api.github.com/repos/cemu-project/cemu_graphic_packs/releases/latest"; - } - // github API request - if (curlDownloadFile(githubAPIUrl.c_str(), &tempDownloadState) == false) - { - wxMessageBox( _("Error"), _(L"Failed to connect to server"), wxOK | wxCENTRE | wxICON_ERROR, this); - m_threadState = ThreadError; - return; - } - // parse json result - rapidjson::Document d; - d.Parse((const char*)tempDownloadState.fileData.data(), tempDownloadState.fileData.size()); - if (d.HasParseError()) - { - m_threadState = ThreadError; - return; - } - auto& jsonName = d["name"]; - if (jsonName.IsString() == false) - { - m_threadState = ThreadError; - return; - } - const char* assetName = jsonName.GetString(); // name includes version - if( d.IsObject() == false) - { - m_threadState = ThreadError; - return; - } - auto& jsonAssets = d["assets"]; - if (jsonAssets.IsArray() == false || jsonAssets.GetArray().Size() == 0) - { - m_threadState = ThreadError; - return; - } - auto& jsonAsset0 = jsonAssets.GetArray()[0]; - if (jsonAsset0.IsObject() == false) - { - m_threadState = ThreadError; - return; - } - auto& jsonDownloadUrl = jsonAsset0["browser_download_url"]; - if (jsonDownloadUrl.IsString() == false) - { - m_threadState = ThreadError; - return; - } - const char* browserDownloadUrl = jsonDownloadUrl.GetString(); - // check version - bool hasVersionFile = false; - if (checkGraphicPackDownloadedVersion(assetName, hasVersionFile)) - { - // already up to date - wxMessageBox(_("No updates available."), _("Graphic packs"), wxOK | wxCENTRE, this); - m_threadState = ThreadFinished; - return; - } - if (hasVersionFile) - { - // if a version file already exists (and graphic packs are installed) ask the user if he really wants to update - if (wxMessageBox(_("Updated graphic packs are available. Do you want to download and install them?"), _("Graphic packs"), wxYES_NO, this) != wxYES) - { - // cancel update - m_threadState = ThreadFinished; - return; - } - } - // download zip - m_stage = StageDownloading; - if (curlDownloadFile(browserDownloadUrl, m_downloadState.get()) == false) - { - wxMessageBox(_("Error"), _(L"Failed to connect to server"), wxOK | wxCENTRE | wxICON_ERROR, this); - m_threadState = ThreadError; - return; - } - - m_extractionProgress = 0.0; - m_stage = StageExtracting; - - zip_source_t *src; - zip_t *za; - zip_error_t error; - - // init zip source - zip_error_init(&error); - if ((src = zip_source_buffer_create(m_downloadState->fileData.data(), m_downloadState->fileData.size(), 0, &error)) == NULL) - { - zip_error_fini(&error); - m_threadState = ThreadError; - return; - } - - // open zip from source - if ((za = zip_open_from_source(src, 0, &error)) == NULL) - { - zip_source_free(src); - zip_error_fini(&error); - m_threadState = ThreadError; - return; - } - - auto path = ActiveSettings::GetUserDataPath("graphicPacks/downloadedGraphicPacks"); - std::error_code er; - //fs::remove_all(path, er); -> Don't delete the whole folder and recreate it immediately afterwards because sometimes it just fails - deleteDownloadedGraphicPacks(); - fs::create_directories(path, er); // make sure downloadedGraphicPacks folder exists - - sint32 numEntries = zip_get_num_entries(za, 0); - for (sint32 i = 0; i < numEntries; i++) - { - m_extractionProgress = (double)i / (double)numEntries; - zip_stat_t sb = { 0 }; - if (zip_stat_index(za, i, 0, &sb) != 0) - { - assert_dbg(); - } - - if(std::strstr(sb.name, "../") != nullptr || - std::strstr(sb.name, "..\\") != nullptr) - continue; // bad path - - path = ActiveSettings::GetUserDataPath("graphicPacks/downloadedGraphicPacks/{}", sb.name); - - size_t sbNameLen = strlen(sb.name); - if(sbNameLen == 0) - continue; - if (sb.name[sbNameLen - 1] == '/') - { - fs::create_directories(path, er); - continue; - } - if(sb.size == 0) - continue; - if (sb.size > (1024 * 1024 * 128)) - continue; // skip unusually huge files - - zip_file_t* zipFile = zip_fopen_index(za, i, 0); - if (zipFile == nullptr) - continue; - - uint8* fileBuffer = new uint8[sb.size]; - - if (zip_fread(zipFile, fileBuffer, sb.size) == sb.size) - { - FileStream* fs = FileStream::createFile2(path); - if (fs) - { - fs->writeData(fileBuffer, sb.size); - delete fs; - } - } - - delete [] fileBuffer; - zip_fclose(zipFile); - } - - zip_discard(za); - zip_error_fini(&error); - createGraphicPackDownloadedVersionFile(assetName); - m_threadState = ThreadFinished; -} - -DownloadGraphicPacksWindow::DownloadGraphicPacksWindow(wxWindow* parent) - : wxDialog(parent, wxID_ANY, _("Checking version..."), wxDefaultPosition, wxDefaultSize, wxCAPTION | wxMINIMIZE_BOX | wxSYSTEM_MENU | wxTAB_TRAVERSAL | wxCLOSE_BOX), - m_threadState(ThreadRunning), m_stage(StageCheckVersion), m_currentStage(StageCheckVersion) -{ - auto* sizer = new wxBoxSizer(wxVERTICAL); - - m_processBar = new wxGauge(this, wxID_ANY, 100, wxDefaultPosition, wxSize(500, 20), wxGA_HORIZONTAL); - m_processBar->SetValue(0); - m_processBar->SetRange(100); - sizer->Add(m_processBar, 0, wxALL | wxEXPAND, 5); - - auto* m_cancelButton = new wxButton(this, wxID_ANY, _("Cancel")); - m_cancelButton->Bind(wxEVT_BUTTON, &DownloadGraphicPacksWindow::OnCancelButton, this); - sizer->Add(m_cancelButton, 0, wxALIGN_RIGHT | wxALL, 5); - - this->SetSizer(sizer); - this->Centre(wxBOTH); - - wxWindowBase::Layout(); - wxWindowBase::Fit(); - - m_timer = new wxTimer(this); - this->Bind(wxEVT_TIMER, &DownloadGraphicPacksWindow::OnUpdate, this); - this->Bind(wxEVT_CLOSE_WINDOW, &DownloadGraphicPacksWindow::OnClose, this); - m_timer->Start(250); - - - m_downloadState = std::make_unique(); -} - -DownloadGraphicPacksWindow::~DownloadGraphicPacksWindow() -{ - m_timer->Stop(); - if (m_thread.joinable()) - m_thread.join(); -} - -const std::string& DownloadGraphicPacksWindow::GetException() const -{ - return m_threadException; -} - -int DownloadGraphicPacksWindow::ShowModal() -{ - if(CafeSystem::IsTitleRunning()) - { - wxMessageBox(_("Graphic packs cannot be updated while a game is running."), _("Graphic packs"), 5, this); - return wxID_CANCEL; - } - m_thread = std::thread(&DownloadGraphicPacksWindow::UpdateThread, this); - wxDialog::ShowModal(); - return m_threadState == ThreadCanceled ? wxID_CANCEL : wxID_OK; -} - -void DownloadGraphicPacksWindow::OnClose(wxCloseEvent& event) -{ - if (m_threadState == ThreadRunning) - { - //wxMessageDialog dialog(this, _("Do you really want to cancel the update process?\n\nCanceling the process will delete the applied update."), _("Info"), wxCENTRE | wxYES_NO); - //if (dialog.ShowModal() != wxID_YES) - // return; - - m_threadState = ThreadCanceled; - } - - m_timer->Stop(); - if (m_thread.joinable()) - m_thread.join(); - - event.Skip(); -} - -void DownloadGraphicPacksWindow::OnUpdate(const wxTimerEvent& event) -{ - if (m_threadState != ThreadRunning) - { - Close(); - return; - } - if (m_currentStage != m_stage) - { - if (m_stage == StageDownloading) - { - this->SetTitle(_("Downloading graphic packs...")); - } - else if (m_stage == StageExtracting) - { - this->SetTitle(_("Extracting...")); - } - m_currentStage = m_stage; - } - - if (m_currentStage == StageDownloading) - { - const sint32 processedSize = (sint32)(m_downloadState->progress * 100.0f); - if (m_processBar->GetValue() != processedSize) - m_processBar->SetValue(processedSize); - } - else if (m_currentStage == StageExtracting) - { - const sint32 processedSize = (sint32)(m_extractionProgress * 100.0f); - if (m_processBar->GetValue() != processedSize) - m_processBar->SetValue(processedSize); - } -} - -void DownloadGraphicPacksWindow::OnCancelButton(const wxCommandEvent& event) -{ - Close(); -} diff --git a/src/gui/wxgui/DownloadGraphicPacksWindow.h b/src/gui/wxgui/DownloadGraphicPacksWindow.h deleted file mode 100644 index 73101f98..00000000 --- a/src/gui/wxgui/DownloadGraphicPacksWindow.h +++ /dev/null @@ -1,62 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include - -#include -#include -#include - - -class DownloadGraphicPacksWindow : public wxDialog -{ -public: - DownloadGraphicPacksWindow(wxWindow* parent); - ~DownloadGraphicPacksWindow(); - - const std::string& GetException() const; - - int ShowModal() override; - void OnClose(wxCloseEvent& event); - - void OnUpdate(const wxTimerEvent& event); - void OnCancelButton(const wxCommandEvent& event); - -private: - void UpdateThread(); - - enum ThreadState_t - { - ThreadRunning, - ThreadCanceled, - ThreadError, - ThreadFinished, - }; - - enum DownloadStage_t - { - StageCheckVersion, - StageDownloading, - StageExtracting - }; - - std::atomic m_threadState; - std::atomic m_stage; - std::atomic m_extractionProgress; - std::string m_threadException; - std::thread m_thread; - - DownloadStage_t m_currentStage; - wxGauge* m_processBar; - wxTimer* m_timer; - - struct curlDownloadFileState_t; - std::unique_ptr m_downloadState; - - static size_t curlDownloadFile_writeData(void* ptr, size_t size, size_t nmemb, curlDownloadFileState_t* downloadState); - static int progress_callback(curlDownloadFileState_t* downloadState, curl_off_t dltotal, curl_off_t dlnow, curl_off_t ultotal, curl_off_t ulnow); - static bool curlDownloadFile(const char* url, curlDownloadFileState_t* downloadState); -}; diff --git a/src/gui/wxgui/EmulatedUSBDevices/EmulatedUSBDeviceFrame.cpp b/src/gui/wxgui/EmulatedUSBDevices/EmulatedUSBDeviceFrame.cpp deleted file mode 100644 index c1d4b81b..00000000 --- a/src/gui/wxgui/EmulatedUSBDevices/EmulatedUSBDeviceFrame.cpp +++ /dev/null @@ -1,842 +0,0 @@ -#include "EmulatedUSBDeviceFrame.h" - -#include - -#include "config/CemuConfig.h" -#include "wxgui/helpers/wxHelpers.h" -#include "wxgui/wxHelper.h" -#include "util/helpers/helpers.h" - -#include "Cafe/OS/libs/nsyshid/nsyshid.h" -#include "Cafe/OS/libs/nsyshid/Dimensions.h" - -#include "Common/FileStream.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "resource/embedded/resources.h" - -EmulatedUSBDeviceFrame::EmulatedUSBDeviceFrame(wxWindow* parent) - : wxFrame(parent, wxID_ANY, _("Emulated USB Devices"), wxDefaultPosition, - wxDefaultSize, wxDEFAULT_FRAME_STYLE | wxTAB_TRAVERSAL) -{ - SetIcon(wxICON(X_BOX)); - - auto& config = GetConfig(); - - auto* sizer = new wxBoxSizer(wxVERTICAL); - auto* notebook = new wxNotebook(this, wxID_ANY); - - notebook->AddPage(AddSkylanderPage(notebook), _("Skylanders Portal")); - notebook->AddPage(AddInfinityPage(notebook), _("Infinity Base")); - notebook->AddPage(AddDimensionsPage(notebook), _("Dimensions Toypad")); - - sizer->Add(notebook, 1, wxEXPAND | wxALL, 2); - - SetSizerAndFit(sizer); - Layout(); - Centre(wxBOTH); -} - -EmulatedUSBDeviceFrame::~EmulatedUSBDeviceFrame() {} - -wxPanel* EmulatedUSBDeviceFrame::AddSkylanderPage(wxNotebook* notebook) -{ - auto* panel = new wxPanel(notebook); - auto* panelSizer = new wxBoxSizer(wxVERTICAL); - auto* box = new wxStaticBox(panel, wxID_ANY, _("Skylanders Manager")); - auto* boxSizer = new wxStaticBoxSizer(box, wxVERTICAL); - - auto* row = new wxBoxSizer(wxHORIZONTAL); - - m_emulatePortal = - new wxCheckBox(box, wxID_ANY, _("Emulate Skylander Portal")); - m_emulatePortal->SetValue( - GetConfig().emulated_usb_devices.emulate_skylander_portal); - m_emulatePortal->Bind(wxEVT_CHECKBOX, [this](wxCommandEvent&) { - GetConfig().emulated_usb_devices.emulate_skylander_portal = - m_emulatePortal->IsChecked(); - GetConfigHandle().Save(); - }); - row->Add(m_emulatePortal, 1, wxEXPAND | wxALL, 2); - boxSizer->Add(row, 1, wxEXPAND | wxALL, 2); - for (int i = 0; i < nsyshid::MAX_SKYLANDERS; i++) - { - boxSizer->Add(AddSkylanderRow(i, box), 1, wxEXPAND | wxALL, 2); - } - panelSizer->Add(boxSizer, 1, wxEXPAND | wxALL, 2); - panel->SetSizerAndFit(panelSizer); - - return panel; -} - -wxPanel* EmulatedUSBDeviceFrame::AddInfinityPage(wxNotebook* notebook) -{ - auto* panel = new wxPanel(notebook); - auto* panelSizer = new wxBoxSizer(wxVERTICAL); - auto* box = new wxStaticBox(panel, wxID_ANY, _("Infinity Manager")); - auto* boxSizer = new wxStaticBoxSizer(box, wxVERTICAL); - - auto* row = new wxBoxSizer(wxHORIZONTAL); - - m_emulateBase = - new wxCheckBox(box, wxID_ANY, _("Emulate Infinity Base")); - m_emulateBase->SetValue( - GetConfig().emulated_usb_devices.emulate_infinity_base); - m_emulateBase->Bind(wxEVT_CHECKBOX, [this](wxCommandEvent&) { - GetConfig().emulated_usb_devices.emulate_infinity_base = - m_emulateBase->IsChecked(); - GetConfigHandle().Save(); - }); - row->Add(m_emulateBase, 1, wxEXPAND | wxALL, 2); - boxSizer->Add(row, 1, wxEXPAND | wxALL, 2); - boxSizer->Add(AddInfinityRow("Play Set/Power Disc", 0, box), 1, wxEXPAND | wxALL, 2); - boxSizer->Add(AddInfinityRow("Power Disc Two", 1, box), 1, wxEXPAND | wxALL, 2); - boxSizer->Add(AddInfinityRow("Power Disc Three", 2, box), 1, wxEXPAND | wxALL, 2); - boxSizer->Add(AddInfinityRow("Player One", 3, box), 1, wxEXPAND | wxALL, 2); - boxSizer->Add(AddInfinityRow("Player One Ability One", 4, box), 1, wxEXPAND | wxALL, 2); - boxSizer->Add(AddInfinityRow("Player One Ability Two", 5, box), 1, wxEXPAND | wxALL, 2); - boxSizer->Add(AddInfinityRow("Player Two", 6, box), 1, wxEXPAND | wxALL, 2); - boxSizer->Add(AddInfinityRow("Player Two Ability One", 7, box), 1, wxEXPAND | wxALL, 2); - boxSizer->Add(AddInfinityRow("Player Two Ability Two", 8, box), 1, wxEXPAND | wxALL, 2); - - panelSizer->Add(boxSizer, 1, wxEXPAND | wxALL, 2); - panel->SetSizerAndFit(panelSizer); - - return panel; -} - -wxPanel* EmulatedUSBDeviceFrame::AddDimensionsPage(wxNotebook* notebook) -{ - auto* panel = new wxPanel(notebook); - auto* panel_sizer = new wxBoxSizer(wxVERTICAL); - auto* box = new wxStaticBox(panel, wxID_ANY, _("Dimensions Manager")); - auto* box_sizer = new wxStaticBoxSizer(box, wxVERTICAL); - - auto* row = new wxBoxSizer(wxHORIZONTAL); - - m_emulateToypad = - new wxCheckBox(box, wxID_ANY, _("Emulate Dimensions Toypad")); - m_emulateToypad->SetValue( - GetConfig().emulated_usb_devices.emulate_dimensions_toypad); - m_emulateToypad->Bind(wxEVT_CHECKBOX, [this](wxCommandEvent&) { - GetConfig().emulated_usb_devices.emulate_dimensions_toypad = - m_emulateToypad->IsChecked(); - GetConfigHandle().Save(); - }); - row->Add(m_emulateToypad, 1, wxEXPAND | wxALL, 2); - box_sizer->Add(row, 1, wxEXPAND | wxALL, 2); - auto* top_row = new wxBoxSizer(wxHORIZONTAL); - auto* bottom_row = new wxBoxSizer(wxHORIZONTAL); - - top_row->Add(AddDimensionPanel(2, 0, box), 1, wxEXPAND | wxALL, 2); - top_row->Add(0, 0, 1, wxEXPAND | wxLEFT | wxRIGHT, 2); - top_row->Add(AddDimensionPanel(1, 1, box), 1, wxEXPAND | wxALL, 2); - top_row->Add(0, 0, 1, wxEXPAND | wxLEFT | wxRIGHT, 2); - top_row->Add(AddDimensionPanel(3, 2, box), 1, wxEXPAND | wxALL, 2); - - bottom_row->Add(AddDimensionPanel(2, 3, box), 1, wxEXPAND | wxALL, 2); - bottom_row->Add(AddDimensionPanel(2, 4, box), 1, wxEXPAND | wxALL, 2); - bottom_row->Add(0, 0, 1, wxEXPAND | wxLEFT | wxRIGHT, 0); - bottom_row->Add(AddDimensionPanel(3, 5, box), 1, wxEXPAND | wxALL, 2); - bottom_row->Add(AddDimensionPanel(3, 6, box), 1, wxEXPAND | wxALL, 2); - - box_sizer->Add(top_row, 1, wxEXPAND | wxALL, 2); - box_sizer->Add(bottom_row, 1, wxEXPAND | wxALL, 2); - panel_sizer->Add(box_sizer, 1, wxEXPAND | wxALL, 2); - panel->SetSizerAndFit(panel_sizer); - - return panel; -} - -wxBoxSizer* EmulatedUSBDeviceFrame::AddSkylanderRow(uint8 rowNumber, wxStaticBox* box) -{ - auto* row = new wxBoxSizer(wxHORIZONTAL); - - row->Add(new wxStaticText(box, wxID_ANY, - fmt::format("{} {}", _("Skylander").ToStdString(), - rowNumber + 1)), - 1, wxEXPAND | wxALL, 2); - m_skylanderSlots[rowNumber] = - new wxTextCtrl(box, wxID_ANY, _("None"), wxDefaultPosition, wxDefaultSize, - wxTE_READONLY); - m_skylanderSlots[rowNumber]->SetMinSize(wxSize(150, -1)); - m_skylanderSlots[rowNumber]->Disable(); - row->Add(m_skylanderSlots[rowNumber], 1, wxEXPAND | wxALL, 2); - auto* loadButton = new wxButton(box, wxID_ANY, _("Load")); - loadButton->Bind(wxEVT_BUTTON, [rowNumber, this](wxCommandEvent&) { - LoadSkylander(rowNumber); - }); - auto* createButton = new wxButton(box, wxID_ANY, _("Create")); - createButton->Bind(wxEVT_BUTTON, [rowNumber, this](wxCommandEvent&) { - CreateSkylander(rowNumber); - }); - auto* clearButton = new wxButton(box, wxID_ANY, _("Clear")); - clearButton->Bind(wxEVT_BUTTON, [rowNumber, this](wxCommandEvent&) { - ClearSkylander(rowNumber); - }); - row->Add(loadButton, 1, wxEXPAND | wxALL, 2); - row->Add(createButton, 1, wxEXPAND | wxALL, 2); - row->Add(clearButton, 1, wxEXPAND | wxALL, 2); - - return row; -} - -wxBoxSizer* EmulatedUSBDeviceFrame::AddInfinityRow(wxString name, uint8 rowNumber, wxStaticBox* box) -{ - auto* row = new wxBoxSizer(wxHORIZONTAL); - - row->Add(new wxStaticText(box, wxID_ANY, name), 1, wxEXPAND | wxALL, 2); - m_infinitySlots[rowNumber] = - new wxTextCtrl(box, wxID_ANY, _("None"), wxDefaultPosition, wxDefaultSize, - wxTE_READONLY); - m_infinitySlots[rowNumber]->SetMinSize(wxSize(150, -1)); - m_infinitySlots[rowNumber]->Disable(); - row->Add(m_infinitySlots[rowNumber], 1, wxALL | wxEXPAND, 5); - auto* loadButton = new wxButton(box, wxID_ANY, _("Load")); - loadButton->Bind(wxEVT_BUTTON, [rowNumber, this](wxCommandEvent&) { - LoadFigure(rowNumber); - }); - auto* createButton = new wxButton(box, wxID_ANY, _("Create")); - createButton->Bind(wxEVT_BUTTON, [rowNumber, this](wxCommandEvent&) { - CreateFigure(rowNumber); - }); - auto* clearButton = new wxButton(box, wxID_ANY, _("Clear")); - clearButton->Bind(wxEVT_BUTTON, [rowNumber, this](wxCommandEvent&) { - ClearFigure(rowNumber); - }); - row->Add(loadButton, 1, wxEXPAND | wxALL, 2); - row->Add(createButton, 1, wxEXPAND | wxALL, 2); - row->Add(clearButton, 1, wxEXPAND | wxALL, 2); - - return row; -} - -wxBoxSizer* EmulatedUSBDeviceFrame::AddDimensionPanel(uint8 pad, uint8 index, wxStaticBox* box) -{ - auto* panel = new wxBoxSizer(wxVERTICAL); - - auto* combo_row = new wxBoxSizer(wxHORIZONTAL); - m_dimensionSlots[index] = new wxTextCtrl(box, wxID_ANY, _("None"), wxDefaultPosition, wxDefaultSize, - wxTE_READONLY); - combo_row->Add(m_dimensionSlots[index], 1, wxEXPAND | wxALL, 2); - auto* move_button = new wxButton(box, wxID_ANY, _("Move")); - move_button->Bind(wxEVT_BUTTON, [pad, index, this](wxCommandEvent&) { - MoveMinifig(pad, index); - }); - - combo_row->Add(move_button, 1, wxEXPAND | wxALL, 2); - - auto* button_row = new wxBoxSizer(wxHORIZONTAL); - auto* load_button = new wxButton(box, wxID_ANY, _("Load")); - load_button->Bind(wxEVT_BUTTON, [pad, index, this](wxCommandEvent&) { - LoadMinifig(pad, index); - }); - auto* clear_button = new wxButton(box, wxID_ANY, _("Clear")); - clear_button->Bind(wxEVT_BUTTON, [pad, index, this](wxCommandEvent&) { - ClearMinifig(pad, index); - }); - auto* create_button = new wxButton(box, wxID_ANY, _("Create")); - create_button->Bind(wxEVT_BUTTON, [pad, index, this](wxCommandEvent&) { - CreateMinifig(pad, index); - }); - button_row->Add(clear_button, 1, wxEXPAND | wxALL, 2); - button_row->Add(create_button, 1, wxEXPAND | wxALL, 2); - button_row->Add(load_button, 1, wxEXPAND | wxALL, 2); - - panel->Add(combo_row, 1, wxEXPAND | wxALL, 2); - panel->Add(button_row, 1, wxEXPAND | wxALL, 2); - - return panel; -} - -void EmulatedUSBDeviceFrame::LoadSkylander(uint8 slot) -{ - wxFileDialog openFileDialog(this, _("Open Skylander dump"), "", "", - "Skylander files (*.sky;*.bin;*.dump;*.dmp)|*.sky;*.bin;*.dump;*.dmp", - wxFD_OPEN | wxFD_FILE_MUST_EXIST); - if (openFileDialog.ShowModal() != wxID_OK || openFileDialog.GetPath().empty()) - return; - - LoadSkylanderPath(slot, openFileDialog.GetPath()); -} - -void EmulatedUSBDeviceFrame::LoadSkylanderPath(uint8 slot, wxString path) -{ - std::unique_ptr skyFile(FileStream::openFile2(_utf8ToPath(path.utf8_string()), true)); - if (!skyFile) - { - wxMessageDialog open_error(this, "Error Opening File: " + path); - open_error.ShowModal(); - return; - } - - std::array fileData; - if (skyFile->readData(fileData.data(), fileData.size()) != fileData.size()) - { - wxMessageDialog open_error(this, "Failed to read file! File was too small"); - open_error.ShowModal(); - return; - } - ClearSkylander(slot); - - uint16 skyId = uint16(fileData[0x11]) << 8 | uint16(fileData[0x10]); - uint16 skyVar = uint16(fileData[0x1D]) << 8 | uint16(fileData[0x1C]); - - uint8 portalSlot = nsyshid::g_skyportal.LoadSkylander(fileData.data(), - std::move(skyFile)); - m_skySlots[slot] = std::tuple(portalSlot, skyId, skyVar); - UpdateSkylanderEdits(); -} - -void EmulatedUSBDeviceFrame::CreateSkylander(uint8 slot) -{ - CreateSkylanderDialog create_dlg(this, slot); - create_dlg.ShowModal(); - if (create_dlg.GetReturnCode() == 1) - { - LoadSkylanderPath(slot, create_dlg.GetFilePath()); - } -} - -void EmulatedUSBDeviceFrame::ClearSkylander(uint8 slot) -{ - if (auto slotInfos = m_skySlots[slot]) - { - auto [curSlot, id, var] = slotInfos.value(); - nsyshid::g_skyportal.RemoveSkylander(curSlot); - m_skySlots[slot] = {}; - UpdateSkylanderEdits(); - } -} - -CreateSkylanderDialog::CreateSkylanderDialog(wxWindow* parent, uint8 slot) - : wxDialog(parent, wxID_ANY, _("Skylander Figure Creator"), wxDefaultPosition, wxSize(500, 150)) -{ - auto* sizer = new wxBoxSizer(wxVERTICAL); - - auto* comboRow = new wxBoxSizer(wxHORIZONTAL); - - auto* comboBox = new wxComboBox(this, wxID_ANY); - comboBox->Append("---Select---", reinterpret_cast(0xFFFFFFFF)); - wxArrayString filterlist; - for (const auto& it : nsyshid::g_skyportal.GetListSkylanders()) - { - const uint32 variant = uint32(uint32(it.first.first) << 16) | uint32(it.first.second); - comboBox->Append(it.second, reinterpret_cast(variant)); - filterlist.Add(it.second); - } - comboBox->SetSelection(0); - bool enabled = comboBox->AutoComplete(filterlist); - comboRow->Add(comboBox, 1, wxEXPAND | wxALL, 2); - - auto* idVarRow = new wxBoxSizer(wxHORIZONTAL); - - wxIntegerValidator validator; - - auto* labelId = new wxStaticText(this, wxID_ANY, "ID:"); - auto* labelVar = new wxStaticText(this, wxID_ANY, "Variant:"); - auto* editId = new wxTextCtrl(this, wxID_ANY, _("0"), wxDefaultPosition, wxDefaultSize, 0, validator); - auto* editVar = new wxTextCtrl(this, wxID_ANY, _("0"), wxDefaultPosition, wxDefaultSize, 0, validator); - - idVarRow->Add(labelId, 1, wxALL, 5); - idVarRow->Add(editId, 1, wxALL, 5); - idVarRow->Add(labelVar, 1, wxALL, 5); - idVarRow->Add(editVar, 1, wxALL, 5); - - auto* buttonRow = new wxBoxSizer(wxHORIZONTAL); - - auto* createButton = new wxButton(this, wxID_ANY, _("Create")); - createButton->Bind(wxEVT_BUTTON, [editId, editVar, this](wxCommandEvent&) { - long longSkyId; - if (!editId->GetValue().ToLong(&longSkyId) || longSkyId > 0xFFFF) - { - wxMessageDialog idError(this, "Error Converting ID!", "ID Entered is Invalid"); - idError.ShowModal(); - return; - } - long longSkyVar; - if (!editVar->GetValue().ToLong(&longSkyVar) || longSkyVar > 0xFFFF) - { - wxMessageDialog idError(this, "Error Converting Variant!", "Variant Entered is Invalid"); - idError.ShowModal(); - return; - } - uint16 skyId = longSkyId & 0xFFFF; - uint16 skyVar = longSkyVar & 0xFFFF; - wxString predefName = nsyshid::g_skyportal.FindSkylander(skyId, skyVar) + ".sky"; - wxFileDialog - saveFileDialog(this, _("Create Skylander file"), "", predefName, - "SKY files (*.sky)|*.sky", wxFD_SAVE | wxFD_OVERWRITE_PROMPT); - - if (saveFileDialog.ShowModal() == wxID_CANCEL) - return; - - m_filePath = saveFileDialog.GetPath(); - - if (!nsyshid::g_skyportal.CreateSkylander(_utf8ToPath(m_filePath.utf8_string()), skyId, skyVar)) - { - wxMessageDialog errorMessage(this, "Failed to create file"); - errorMessage.ShowModal(); - this->EndModal(0); - return; - } - - this->EndModal(1); - }); - auto* cancelButton = new wxButton(this, wxID_ANY, _("Cancel")); - cancelButton->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) { - this->EndModal(0); - }); - - comboBox->Bind(wxEVT_COMBOBOX, [comboBox, editId, editVar, this](wxCommandEvent&) { - const uint64 sky_info = reinterpret_cast(comboBox->GetClientData(comboBox->GetSelection())); - if (sky_info != 0xFFFFFFFF) - { - const uint16 skyId = sky_info >> 16; - const uint16 skyVar = sky_info & 0xFFFF; - - editId->SetValue(wxString::Format(wxT("%i"), skyId)); - editVar->SetValue(wxString::Format(wxT("%i"), skyVar)); - } - }); - - buttonRow->Add(createButton, 1, wxALL, 5); - buttonRow->Add(cancelButton, 1, wxALL, 5); - - sizer->Add(comboRow, 1, wxEXPAND | wxALL, 2); - sizer->Add(idVarRow, 1, wxEXPAND | wxALL, 2); - sizer->Add(buttonRow, 1, wxEXPAND | wxALL, 2); - - this->SetSizer(sizer); - this->Centre(wxBOTH); -} - -wxString CreateSkylanderDialog::GetFilePath() const -{ - return m_filePath; -} - -void EmulatedUSBDeviceFrame::UpdateSkylanderEdits() -{ - for (auto i = 0; i < nsyshid::MAX_SKYLANDERS; i++) - { - std::string displayString; - if (auto sd = m_skySlots[i]) - { - auto [portalSlot, skyId, skyVar] = sd.value(); - displayString = nsyshid::g_skyportal.FindSkylander(skyId, skyVar); - } - else - { - displayString = "None"; - } - - m_skylanderSlots[i]->ChangeValue(displayString); - } -} - -void EmulatedUSBDeviceFrame::LoadFigure(uint8 slot) -{ - wxFileDialog openFileDialog(this, _("Open Infinity Figure dump"), "", "", - "BIN files (*.bin)|*.bin", - wxFD_OPEN | wxFD_FILE_MUST_EXIST); - if (openFileDialog.ShowModal() != wxID_OK || openFileDialog.GetPath().empty()) - { - wxMessageDialog errorMessage(this, "File Okay Error"); - errorMessage.ShowModal(); - return; - } - - LoadFigurePath(slot, openFileDialog.GetPath()); -} - -void EmulatedUSBDeviceFrame::LoadFigurePath(uint8 slot, wxString path) -{ - std::unique_ptr infFile(FileStream::openFile2(_utf8ToPath(path.utf8_string()), true)); - if (!infFile) - { - wxMessageDialog errorMessage(this, "File Open Error"); - errorMessage.ShowModal(); - return; - } - - std::array fileData; - if (infFile->readData(fileData.data(), fileData.size()) != fileData.size()) - { - wxMessageDialog open_error(this, "Failed to read file! File was too small"); - open_error.ShowModal(); - return; - } - ClearFigure(slot); - - uint32 number = nsyshid::g_infinitybase.LoadFigure(fileData, std::move(infFile), slot); - m_infinitySlots[slot]->ChangeValue(nsyshid::g_infinitybase.FindFigure(number).second); -} - -void EmulatedUSBDeviceFrame::CreateFigure(uint8 slot) -{ - cemuLog_log(LogType::Force, "Create Figure: {}", slot); - CreateInfinityFigureDialog create_dlg(this, slot); - create_dlg.ShowModal(); - if (create_dlg.GetReturnCode() == 1) - { - LoadFigurePath(slot, create_dlg.GetFilePath()); - } -} - -void EmulatedUSBDeviceFrame::ClearFigure(uint8 slot) -{ - m_infinitySlots[slot]->ChangeValue("None"); - nsyshid::g_infinitybase.RemoveFigure(slot); -} - -CreateInfinityFigureDialog::CreateInfinityFigureDialog(wxWindow* parent, uint8 slot) - : wxDialog(parent, wxID_ANY, _("Infinity Figure Creator"), wxDefaultPosition, wxSize(500, 150)) -{ - auto* sizer = new wxBoxSizer(wxVERTICAL); - - auto* comboRow = new wxBoxSizer(wxHORIZONTAL); - - auto* comboBox = new wxComboBox(this, wxID_ANY); - comboBox->Append("---Select---", reinterpret_cast(0xFFFFFF)); - wxArrayString filterlist; - for (const auto& it : nsyshid::g_infinitybase.GetFigureList()) - { - const uint32 figure = it.first; - if ((slot == 0 && - ((figure > 0x1E8480 && figure < 0x2DC6BF) || (figure > 0x3D0900 && figure < 0x4C4B3F))) || - ((slot == 1 || slot == 2) && (figure > 0x3D0900 && figure < 0x4C4B3F)) || - ((slot == 3 || slot == 6) && figure < 0x1E847F) || - ((slot == 4 || slot == 5 || slot == 7 || slot == 8) && - (figure > 0x2DC6C0 && figure < 0x3D08FF))) - { - comboBox->Append(it.second.second, reinterpret_cast(figure)); - filterlist.Add(it.second.second); - } - } - comboBox->SetSelection(0); - bool enabled = comboBox->AutoComplete(filterlist); - comboRow->Add(comboBox, 1, wxEXPAND | wxALL, 2); - - auto* figNumRow = new wxBoxSizer(wxHORIZONTAL); - - wxIntegerValidator validator; - - auto* labelFigNum = new wxStaticText(this, wxID_ANY, "Figure Number:"); - auto* editFigNum = new wxTextCtrl(this, wxID_ANY, _("0"), wxDefaultPosition, wxDefaultSize, 0, validator); - - figNumRow->Add(labelFigNum, 1, wxALL, 5); - figNumRow->Add(editFigNum, 1, wxALL, 5); - - auto* buttonRow = new wxBoxSizer(wxHORIZONTAL); - - auto* createButton = new wxButton(this, wxID_ANY, _("Create")); - createButton->Bind(wxEVT_BUTTON, [editFigNum, this](wxCommandEvent&) { - long longFigNum; - if (!editFigNum->GetValue().ToLong(&longFigNum)) - { - wxMessageDialog idError(this, "Error Converting Figure Number!", "Number Entered is Invalid"); - idError.ShowModal(); - this->EndModal(0); - return; - } - uint32 figNum = longFigNum & 0xFFFFFFFF; - auto figure = nsyshid::g_infinitybase.FindFigure(figNum); - wxString predefName = figure.second + ".bin"; - wxFileDialog - saveFileDialog(this, _("Create Infinity Figure file"), "", predefName, - "BIN files (*.bin)|*.bin", wxFD_SAVE | wxFD_OVERWRITE_PROMPT); - - if (saveFileDialog.ShowModal() == wxID_CANCEL) - { - this->EndModal(0); - return; - } - - m_filePath = saveFileDialog.GetPath(); - - nsyshid::g_infinitybase.CreateFigure(_utf8ToPath(m_filePath.utf8_string()), figNum, figure.first); - - this->EndModal(1); - }); - auto* cancelButton = new wxButton(this, wxID_ANY, _("Cancel")); - cancelButton->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) { - this->EndModal(0); - }); - - comboBox->Bind(wxEVT_COMBOBOX, [comboBox, editFigNum, this](wxCommandEvent&) { - const uint64 fig_info = reinterpret_cast(comboBox->GetClientData(comboBox->GetSelection())); - if (fig_info != 0xFFFFFF) - { - const uint32 figNum = fig_info & 0xFFFFFFFF; - - editFigNum->SetValue(wxString::Format(wxT("%i"), figNum)); - } - }); - - buttonRow->Add(createButton, 1, wxALL, 5); - buttonRow->Add(cancelButton, 1, wxALL, 5); - - sizer->Add(comboRow, 1, wxEXPAND | wxALL, 2); - sizer->Add(figNumRow, 1, wxEXPAND | wxALL, 2); - sizer->Add(buttonRow, 1, wxEXPAND | wxALL, 2); - - this->SetSizer(sizer); - this->Centre(wxBOTH); -} - -wxString CreateInfinityFigureDialog::GetFilePath() const -{ - return m_filePath; -} - -void EmulatedUSBDeviceFrame::LoadMinifig(uint8 pad, uint8 index) -{ - wxFileDialog openFileDialog(this, _("Load Dimensions Figure"), "", "", - "Dimensions files (*.bin)|*.bin", - wxFD_OPEN | wxFD_FILE_MUST_EXIST); - if (openFileDialog.ShowModal() != wxID_OK || openFileDialog.GetPath().empty()) - return; - - LoadMinifigPath(openFileDialog.GetPath(), pad, index); -} - -void EmulatedUSBDeviceFrame::LoadMinifigPath(wxString path_name, uint8 pad, uint8 index) -{ - std::unique_ptr dim_file(FileStream::openFile2(_utf8ToPath(path_name.utf8_string()), true)); - if (!dim_file) - { - wxMessageDialog errorMessage(this, "Failed to open minifig file"); - errorMessage.ShowModal(); - return; - } - - std::array file_data; - - if (dim_file->readData(file_data.data(), file_data.size()) != file_data.size()) - { - wxMessageDialog errorMessage(this, "Failed to read minifig file data"); - errorMessage.ShowModal(); - return; - } - - ClearMinifig(pad, index); - - uint32 id = nsyshid::g_dimensionstoypad.LoadFigure(file_data, std::move(dim_file), pad, index); - m_dimensionSlots[index]->ChangeValue(nsyshid::g_dimensionstoypad.FindFigure(id)); - m_dimSlots[index] = id; -} - -void EmulatedUSBDeviceFrame::ClearMinifig(uint8 pad, uint8 index) -{ - nsyshid::g_dimensionstoypad.RemoveFigure(pad, index, true); - m_dimensionSlots[index]->ChangeValue("None"); - m_dimSlots[index] = std::nullopt; -} - -void EmulatedUSBDeviceFrame::CreateMinifig(uint8 pad, uint8 index) -{ - CreateDimensionFigureDialog create_dlg(this); - create_dlg.ShowModal(); - if (create_dlg.GetReturnCode() == 1) - { - LoadMinifigPath(create_dlg.GetFilePath(), pad, index); - } -} - -void EmulatedUSBDeviceFrame::MoveMinifig(uint8 pad, uint8 index) -{ - if (!m_dimSlots[index]) - return; - - MoveDimensionFigureDialog move_dlg(this, index); - nsyshid::g_dimensionstoypad.TempRemove(index); - move_dlg.ShowModal(); - if (move_dlg.GetReturnCode() == 1) - { - nsyshid::g_dimensionstoypad.MoveFigure(move_dlg.GetNewPad(), move_dlg.GetNewIndex(), pad, index); - if (index != move_dlg.GetNewIndex()) - { - m_dimSlots[move_dlg.GetNewIndex()] = m_dimSlots[index]; - m_dimensionSlots[move_dlg.GetNewIndex()]->ChangeValue(m_dimensionSlots[index]->GetValue()); - m_dimSlots[index] = std::nullopt; - m_dimensionSlots[index]->ChangeValue("None"); - } - } - else - { - nsyshid::g_dimensionstoypad.CancelRemove(index); - } -} - -CreateDimensionFigureDialog::CreateDimensionFigureDialog(wxWindow* parent) - : wxDialog(parent, wxID_ANY, _("Dimensions Figure Creator"), wxDefaultPosition, wxSize(500, 200)) -{ - auto* sizer = new wxBoxSizer(wxVERTICAL); - - auto* comboRow = new wxBoxSizer(wxHORIZONTAL); - - auto* comboBox = new wxComboBox(this, wxID_ANY); - comboBox->Append("---Select---", reinterpret_cast(0xFFFFFFFF)); - wxArrayString filterlist; - for (const auto& it : nsyshid::g_dimensionstoypad.GetListMinifigs()) - { - const uint32 figure = it.first; - comboBox->Append(it.second, reinterpret_cast(figure)); - filterlist.Add(it.second); - } - comboBox->SetSelection(0); - bool enabled = comboBox->AutoComplete(filterlist); - comboRow->Add(comboBox, 1, wxEXPAND | wxALL, 2); - - auto* figNumRow = new wxBoxSizer(wxHORIZONTAL); - - wxIntegerValidator validator; - - auto* labelFigNum = new wxStaticText(this, wxID_ANY, "Figure Number:"); - auto* editFigNum = new wxTextCtrl(this, wxID_ANY, _("0"), wxDefaultPosition, wxDefaultSize, 0, validator); - - figNumRow->Add(labelFigNum, 1, wxALL, 5); - figNumRow->Add(editFigNum, 1, wxALL, 5); - - auto* buttonRow = new wxBoxSizer(wxHORIZONTAL); - - auto* createButton = new wxButton(this, wxID_ANY, _("Create")); - createButton->Bind(wxEVT_BUTTON, [editFigNum, this](wxCommandEvent&) { - long longFigNum; - if (!editFigNum->GetValue().ToLong(&longFigNum) || longFigNum > 0xFFFF) - { - wxMessageDialog idError(this, "Error Converting Figure Number!", "Number Entered is Invalid"); - idError.ShowModal(); - this->EndModal(0); - return; - } - uint16 figNum = longFigNum & 0xFFFF; - auto figure = nsyshid::g_dimensionstoypad.FindFigure(figNum); - wxString predefName = figure + ".bin"; - wxFileDialog - saveFileDialog(this, _("Create Dimensions Figure file"), "", predefName, - "BIN files (*.bin)|*.bin", wxFD_SAVE | wxFD_OVERWRITE_PROMPT); - - if (saveFileDialog.ShowModal() == wxID_CANCEL) - { - this->EndModal(0); - return; - } - - m_filePath = saveFileDialog.GetPath(); - - nsyshid::g_dimensionstoypad.CreateFigure(_utf8ToPath(m_filePath.utf8_string()), figNum); - - this->EndModal(1); - }); - auto* cancelButton = new wxButton(this, wxID_ANY, _("Cancel")); - cancelButton->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) { - this->EndModal(0); - }); - - comboBox->Bind(wxEVT_COMBOBOX, [comboBox, editFigNum, this](wxCommandEvent&) { - const uint64 fig_info = reinterpret_cast(comboBox->GetClientData(comboBox->GetSelection())); - if (fig_info != 0xFFFF) - { - const uint16 figNum = fig_info & 0xFFFF; - - editFigNum->SetValue(wxString::Format(wxT("%i"), figNum)); - } - }); - - buttonRow->Add(createButton, 1, wxALL, 5); - buttonRow->Add(cancelButton, 1, wxALL, 5); - - sizer->Add(comboRow, 1, wxEXPAND | wxALL, 2); - sizer->Add(figNumRow, 1, wxEXPAND | wxALL, 2); - sizer->Add(buttonRow, 1, wxEXPAND | wxALL, 2); - - this->SetSizer(sizer); - this->Centre(wxBOTH); -} - -wxString CreateDimensionFigureDialog::GetFilePath() const -{ - return m_filePath; -} - -MoveDimensionFigureDialog::MoveDimensionFigureDialog(EmulatedUSBDeviceFrame* parent, uint8 currentIndex) - : wxDialog(parent, wxID_ANY, _("Dimensions Figure Mover"), wxDefaultPosition, wxSize(700, 300)) -{ - auto* sizer = new wxGridSizer(2, 5, 10, 10); - - std::array, 7> ids = parent->GetCurrentMinifigs(); - - sizer->Add(AddMinifigSlot(2, 0, currentIndex, ids[0]), 1, wxALL, 5); - sizer->Add(new wxStaticText(this, wxID_ANY, ""), 1, wxALL, 5); - sizer->Add(AddMinifigSlot(1, 1, currentIndex, ids[1]), 1, wxALL, 5); - sizer->Add(new wxStaticText(this, wxID_ANY, ""), 1, wxALL, 5); - sizer->Add(AddMinifigSlot(3, 2, currentIndex, ids[2]), 1, wxALL, 5); - - sizer->Add(AddMinifigSlot(2, 3, currentIndex, ids[3]), 1, wxALL, 5); - sizer->Add(AddMinifigSlot(2, 4, currentIndex, ids[4]), 1, wxALL, 5); - sizer->Add(new wxStaticText(this, wxID_ANY, ""), 1, wxALL, 5); - sizer->Add(AddMinifigSlot(3, 5, currentIndex, ids[5]), 1, wxALL, 5); - sizer->Add(AddMinifigSlot(3, 6, currentIndex, ids[6]), 1, wxALL, 5); - - this->SetSizer(sizer); - this->Centre(wxBOTH); -} - -wxBoxSizer* MoveDimensionFigureDialog::AddMinifigSlot(uint8 pad, uint8 index, uint8 currentIndex, std::optional currentId) -{ - auto* panel = new wxBoxSizer(wxVERTICAL); - - auto* label = new wxStaticText(this, wxID_ANY, "None"); - if (currentId) - label->SetLabel(nsyshid::g_dimensionstoypad.FindFigure(currentId.value())); - - auto* moveButton = new wxButton(this, wxID_ANY, _("Move Here")); - if (index == currentIndex) - moveButton->SetLabelText("Pick up and Place"); - - moveButton->Bind(wxEVT_BUTTON, [pad, index, this](wxCommandEvent&) { - m_newPad = pad; - m_newIndex = index; - this->EndModal(1); - }); - - panel->Add(label, 1, wxALL, 5); - panel->Add(moveButton, 1, wxALL, 5); - - return panel; -} - -uint8 MoveDimensionFigureDialog::GetNewPad() const -{ - return m_newPad; -} - -uint8 MoveDimensionFigureDialog::GetNewIndex() const -{ - return m_newIndex; -} - -std::array, 7> EmulatedUSBDeviceFrame::GetCurrentMinifigs() -{ - return m_dimSlots; -} diff --git a/src/gui/wxgui/EmulatedUSBDevices/EmulatedUSBDeviceFrame.h b/src/gui/wxgui/EmulatedUSBDevices/EmulatedUSBDeviceFrame.h deleted file mode 100644 index 78c70a4a..00000000 --- a/src/gui/wxgui/EmulatedUSBDevices/EmulatedUSBDeviceFrame.h +++ /dev/null @@ -1,102 +0,0 @@ -#pragma once - -#include - -#include -#include - -#include "Cafe/OS/libs/nsyshid/Infinity.h" -#include "Cafe/OS/libs/nsyshid/Skylander.h" - -class wxBoxSizer; -class wxCheckBox; -class wxFlexGridSizer; -class wxNotebook; -class wxPanel; -class wxStaticBox; -class wxString; -class wxTextCtrl; - -class EmulatedUSBDeviceFrame : public wxFrame -{ - public: - EmulatedUSBDeviceFrame(wxWindow* parent); - ~EmulatedUSBDeviceFrame(); - std::array, 7> GetCurrentMinifigs(); - - private: - wxCheckBox* m_emulatePortal; - wxCheckBox* m_emulateBase; - wxCheckBox* m_emulateToypad; - std::array m_skylanderSlots; - std::array m_infinitySlots; - std::array m_dimensionSlots; - std::array>, nsyshid::MAX_SKYLANDERS> m_skySlots; - std::array, 7> m_dimSlots; - - wxPanel* AddSkylanderPage(wxNotebook* notebook); - wxPanel* AddInfinityPage(wxNotebook* notebook); - wxPanel* AddDimensionsPage(wxNotebook* notebook); - wxBoxSizer* AddSkylanderRow(uint8 row_number, wxStaticBox* box); - wxBoxSizer* AddInfinityRow(wxString name, uint8 row_number, wxStaticBox* box); - wxBoxSizer* AddDimensionPanel(uint8 pad, uint8 index, wxStaticBox* box); - void LoadSkylander(uint8 slot); - void LoadSkylanderPath(uint8 slot, wxString path); - void CreateSkylander(uint8 slot); - void ClearSkylander(uint8 slot); - void UpdateSkylanderEdits(); - void LoadFigure(uint8 slot); - void LoadFigurePath(uint8 slot, wxString path); - void CreateFigure(uint8 slot); - void ClearFigure(uint8 slot); - void LoadMinifig(uint8 pad, uint8 index); - void LoadMinifigPath(wxString path_name, uint8 pad, uint8 index); - void CreateMinifig(uint8 pad, uint8 index); - void ClearMinifig(uint8 pad, uint8 index); - void MoveMinifig(uint8 pad, uint8 index); -}; - -class CreateSkylanderDialog : public wxDialog -{ - public: - explicit CreateSkylanderDialog(wxWindow* parent, uint8 slot); - wxString GetFilePath() const; - - protected: - wxString m_filePath; -}; - -class CreateInfinityFigureDialog : public wxDialog -{ - public: - explicit CreateInfinityFigureDialog(wxWindow* parent, uint8 slot); - wxString GetFilePath() const; - - protected: - wxString m_filePath; -}; - -class CreateDimensionFigureDialog : public wxDialog -{ - public: - explicit CreateDimensionFigureDialog(wxWindow* parent); - wxString GetFilePath() const; - - protected: - wxString m_filePath; -}; - -class MoveDimensionFigureDialog : public wxDialog -{ - public: - explicit MoveDimensionFigureDialog(EmulatedUSBDeviceFrame* parent, uint8 currentIndex); - uint8 GetNewPad() const; - uint8 GetNewIndex() const; - - protected: - uint8 m_newIndex = 0; - uint8 m_newPad = 0; - - private: - wxBoxSizer* AddMinifigSlot(uint8 pad, uint8 index, uint8 oldIndex, std::optional currentId); -}; \ No newline at end of file diff --git a/src/gui/wxgui/GameProfileWindow.cpp b/src/gui/wxgui/GameProfileWindow.cpp deleted file mode 100644 index 5962133e..00000000 --- a/src/gui/wxgui/GameProfileWindow.cpp +++ /dev/null @@ -1,405 +0,0 @@ -#include "wxgui/GameProfileWindow.h" - -#include -#include -#include -#include -#include -#include -#include - -#include "wxgui/helpers/wxHelpers.h" -#include "input/InputManager.h" - -#if BOOST_OS_LINUX || BOOST_OS_MACOS || BOOST_OS_BSD -#include "resource/embedded/resources.h" -#endif - -GameProfileWindow::GameProfileWindow(wxWindow* parent, uint64_t title_id) - : wxFrame(parent, wxID_ANY, _("Edit game profile"), wxDefaultPosition, wxSize{ 390, 350 }, wxCLOSE_BOX | wxCLIP_CHILDREN | wxCAPTION | wxRESIZE_BORDER | wxTAB_TRAVERSAL | wxSYSTEM_MENU), m_title_id(title_id) -{ - SetIcon(wxICON(X_GAME_PROFILE)); - - m_game_profile.Reset(); - m_game_profile.Load(title_id); - - this->SetSizeHints(wxDefaultSize, wxDefaultSize); - - auto* main_sizer = new wxBoxSizer(wxVERTICAL); - - auto* m_notebook = new wxNotebook(this, wxID_ANY); - // general - { - auto* panel = new wxPanel(m_notebook, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL); - auto* sizer = new wxBoxSizer(wxVERTICAL); - { - auto* box_sizer = new wxStaticBoxSizer(new wxStaticBox(panel, wxID_ANY, _("General")), wxVERTICAL); - auto* box = box_sizer->GetStaticBox(); - - m_load_libs = new wxCheckBox(box, wxID_ANY, _("Load shared libraries")); - m_load_libs->SetToolTip(_("EXPERT OPTION\nThis option will load libraries from the cafeLibs directory")); - box_sizer->Add(m_load_libs, 0, wxALL, 5); - - m_start_with_padview = new wxCheckBox(box, wxID_ANY, _("Launch with gamepad view")); - m_start_with_padview->SetToolTip(_("Games will be launched with gamepad view toggled as default. The view can be toggled with CTRL + TAB")); - box_sizer->Add(m_start_with_padview, 0, wxALL, 5); - - sizer->Add(box_sizer, 0, wxEXPAND, 5); - } - // cpu - { - auto* box_sizer = new wxStaticBoxSizer(new wxStaticBox(panel, wxID_ANY, _("CPU")), wxVERTICAL); - auto* box = box_sizer->GetStaticBox(); - - auto* first_row = new wxFlexGridSizer(0, 2, 0, 0); - first_row->SetFlexibleDirection(wxBOTH); - first_row->SetNonFlexibleGrowMode(wxFLEX_GROWMODE_SPECIFIED); - - first_row->Add(new wxStaticText(box, wxID_ANY, _("Mode")), 0, wxALIGN_CENTER_VERTICAL | wxALL, 5); - - wxString cpu_modes[] = { _("Single-core interpreter"), _("Single-core recompiler"), _("Multi-core recompiler"), _("Auto (recommended)") }; - const sint32 m_cpu_modeNChoices = std::size(cpu_modes); - m_cpu_mode = new wxChoice(box, wxID_ANY, wxDefaultPosition, wxDefaultSize, m_cpu_modeNChoices, cpu_modes, 0); - m_cpu_mode->SetToolTip(_("Set the CPU emulation mode")); - first_row->Add(m_cpu_mode, 0, wxALL, 5); - - first_row->Add(new wxStaticText(box, wxID_ANY, _("Thread quantum")), 0, wxALIGN_CENTER_VERTICAL | wxALL, 5); - - auto* quantum_sizer = new wxFlexGridSizer(0, 2, 0, 0); - quantum_sizer->SetFlexibleDirection(wxBOTH); - quantum_sizer->SetNonFlexibleGrowMode(wxFLEX_GROWMODE_SPECIFIED); - - wxString quantum_values[] = { "20000", "45000", "60000", "80000" ,"100000" }; - m_thread_quantum = new wxChoice(box, wxID_ANY, wxDefaultPosition, wxDefaultSize, std::size(quantum_values), quantum_values); - m_thread_quantum->SetMinSize(wxSize(85, -1)); - m_thread_quantum->SetToolTip(_("EXPERT OPTION\nSet the maximum thread slice runtime (in virtual cycles)")); - quantum_sizer->Add(m_thread_quantum, 0, wxALL, 5); - - quantum_sizer->Add(new wxStaticText(box, wxID_ANY, _("cycles")), 0, wxALIGN_CENTER_VERTICAL | wxALL, 5); - - first_row->Add(quantum_sizer, 0, wxEXPAND, 5); - - box_sizer->Add(first_row, 0, wxEXPAND, 5); - - - sizer->Add(box_sizer, 0, wxEXPAND, 5); - } - - panel->SetSizer(sizer); - panel->Layout(); - sizer->Fit(panel); - m_notebook->AddPage(panel, _("General"), true); - } - - // graphic - { - auto* panel = new wxPanel(m_notebook, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL); - auto* sizer = new wxBoxSizer(wxVERTICAL); - - //m_extended_texture_readback = new wxCheckBox(panel, wxID_ANY, _("Extended texture readback")); - //m_extended_texture_readback->SetToolTip(_("Improves emulation accuracy of CPU to GPU memory access at the cost of performance. Required for some games.")); - //sizer->Add(m_extended_texture_readback, 0, wxALL, 5); - - auto* first_row = new wxFlexGridSizer(0, 2, 0, 0); - first_row->SetNonFlexibleGrowMode(wxFLEX_GROWMODE_SPECIFIED); - - /*first_row->Add(new wxStaticText(panel, wxID_ANY, _("Precompiled shaders")), 0, wxALIGN_CENTER_VERTICAL | wxALL, 5); - wxString precompiled_modes[] = { _("auto"), _("enable"), _("disable") }; - const sint32 precompiled_count = std::size(precompiled_modes); - m_precompiled = new wxChoice(panel, wxID_ANY, wxDefaultPosition, wxDefaultSize, precompiled_count, precompiled_modes, 0); - m_precompiled->SetToolTip(_("Precompiled shaders can speed up the load time on the shader loading screen.\nAuto will enable it for AMD/Intel but disable it for NVIDIA GPUs as a workaround for a driver bug.\n\nRecommended: Auto")); - first_row->Add(m_precompiled, 0, wxALL, 5);*/ - - first_row->Add(new wxStaticText(panel, wxID_ANY, _("Graphics API")), 0, wxALIGN_CENTER_VERTICAL | wxALL, 5); - - wxString gapi_values[] = { "", "OpenGL", "Vulkan", -#ifdef ENABLE_METAL - "Metal" -#endif - }; - m_graphic_api = new wxChoice(panel, wxID_ANY, wxDefaultPosition, wxDefaultSize, (int)std::size(gapi_values), gapi_values); - first_row->Add(m_graphic_api, 0, wxALL, 5); - - first_row->Add(new wxStaticText(panel, wxID_ANY, _("Shader multiplication accuracy")), 0, wxALIGN_CENTER_VERTICAL | wxALL, 5); - - wxString mul_values[] = { _("false"), _("true")}; - m_shader_mul_accuracy = new wxChoice(panel, wxID_ANY, wxDefaultPosition, wxDefaultSize, (int)std::size(mul_values), mul_values); - m_shader_mul_accuracy->SetToolTip(_("EXPERT OPTION\nControls the accuracy of floating point multiplication in shaders.\n\nRecommended: true")); - first_row->Add(m_shader_mul_accuracy, 0, wxALL, 5); - -#ifdef ENABLE_METAL - first_row->Add(new wxStaticText(panel, wxID_ANY, _("Shader fast math")), 0, wxALIGN_CENTER_VERTICAL | wxALL, 5); - - wxString math_values[] = { _("false"), _("true") }; - m_shader_fast_math = new wxChoice(panel, wxID_ANY, wxDefaultPosition, wxDefaultSize, (int)std::size(math_values), math_values); - m_shader_fast_math->SetToolTip(_("EXPERT OPTION\nEnables fast math for all shaders. May (rarely) cause graphical bugs.\n\nMetal only\n\nRecommended: true")); - first_row->Add(m_shader_fast_math, 0, wxALL, 5); - - first_row->Add(new wxStaticText(panel, wxID_ANY, _("Metal buffer cache mode")), 0, wxALIGN_CENTER_VERTICAL | wxALL, 5); - - wxString cache_values[] = { _("auto"), _("device private"), _("device shared"), _("host") }; - m_metal_buffer_cache_mode = new wxChoice(panel, wxID_ANY, wxDefaultPosition, wxDefaultSize, (int)std::size(cache_values), cache_values); - m_metal_buffer_cache_mode->SetToolTip(_("EXPERT OPTION\nDecides how the buffer cache memory will be managed.\n\nMetal only\n\nRecommended: auto")); - first_row->Add(m_metal_buffer_cache_mode, 0, wxALL, 5); - - first_row->Add(new wxStaticText(panel, wxID_ANY, _("Position invariance")), 0, wxALIGN_CENTER_VERTICAL | wxALL, 5); - - wxString pos_values[] = { _("auto"), _("false"), _("true") }; - m_position_invariance = new wxChoice(panel, wxID_ANY, wxDefaultPosition, wxDefaultSize, (int)std::size(pos_values), pos_values); - m_position_invariance->SetToolTip(_("EXPERT OPTION\nDisables most optimizations for vertex positions. May fix polygon cutouts or flickering in some games.\n\nMetal only\n\nRecommended: auto")); - first_row->Add(m_position_invariance, 0, wxALL, 5); -#endif - - /*first_row->Add(new wxStaticText(panel, wxID_ANY, _("GPU buffer cache accuracy")), 0, wxALIGN_CENTER_VERTICAL | wxALL, 5); - wxString accuarcy_values[] = { _("high"), _("medium"), _("low") }; - m_cache_accuracy = new wxChoice(panel, wxID_ANY, wxDefaultPosition, wxDefaultSize, (int)std::size(accuarcy_values), accuarcy_values); - m_cache_accuracy->SetToolTip(_("Lower value results in higher performance, but may cause graphical issues")); - first_row->Add(m_cache_accuracy, 0, wxALL, 5);*/ - - sizer->Add(first_row, 0, wxEXPAND, 5); - - - panel->SetSizer(sizer); - panel->Layout(); - sizer->Fit(panel); - m_notebook->AddPage(panel, _("Graphic"), false); - } - - //// audio - //{ - // auto panel = new wxPanel(m_notebook, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL); - // wxBoxSizer* sizer = new wxBoxSizer(wxVERTICAL); - - // m_disable_audio = new wxCheckBox(panel, wxID_ANY, _("Disable Audio"), wxDefaultPosition, wxDefaultSize, wxCHK_3STATE | wxCHK_ALLOW_3RD_STATE_FOR_USER); - // sizer->Add(m_disable_audio, 0, wxALL, 5); - - - // panel->SetSizer(sizer); - // panel->Layout(); - // sizer->Fit(panel); - // m_notebook->AddPage(panel, _("Audio"), false); - //} - - // controller - { - auto panel = new wxPanel(m_notebook, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL); - wxBoxSizer* sizer; - sizer = new wxBoxSizer(wxVERTICAL); - - wxFlexGridSizer* profile_sizer; - profile_sizer = new wxFlexGridSizer(0, 2, 0, 0); - profile_sizer->SetFlexibleDirection(wxBOTH); - profile_sizer->SetNonFlexibleGrowMode(wxFLEX_GROWMODE_SPECIFIED); - - for (int i = 0; i < 8; ++i) - { - profile_sizer->Add(new wxStaticText(panel, wxID_ANY, formatWxString(_("Controller {}"), i + 1)), 0, wxALIGN_CENTER_VERTICAL | wxALL, 5); - - m_controller_profile[i] = new wxComboBox(panel, wxID_ANY,"", wxDefaultPosition, wxDefaultSize, 0, nullptr, wxCB_DROPDOWN| wxCB_READONLY); - m_controller_profile[i]->SetMinSize(wxSize(250, -1)); - m_controller_profile[i]->Bind(wxEVT_COMBOBOX_DROPDOWN, &GameProfileWindow::OnControllerProfileDropdown, this); - m_controller_profile[i]->SetToolTip(_("Forces a given controller profile")); - profile_sizer->Add(m_controller_profile[i], 0, wxALL, 5); - } - - sizer->Add(profile_sizer, 0, wxEXPAND, 5); - - panel->SetSizer(sizer); - panel->Layout(); - sizer->Fit(panel); - m_notebook->AddPage(panel, _("Controller"), false); - } - - main_sizer->Add(m_notebook, 1, wxEXPAND | wxALL, 5); - - - this->SetSizer(main_sizer); - this->Layout(); - - this->Centre(wxBOTH); - - ApplyProfile(); -} - -GameProfileWindow::~GameProfileWindow() -{ - SaveProfile(); -} - -void GameProfileWindow::OnStreamoutSizeChange(wxCommandEvent& event) -{ - wxSlider* slider = wxDynamicCast(event.GetEventObject(), wxSlider); - wxASSERT(slider); - wxStaticText* text = wxDynamicCast(slider->GetClientData(), wxStaticText); - wxASSERT(text); - text->SetLabelText(fmt::format("{} MB", slider->GetValue())); - event.Skip(); -} - -void GameProfileWindow::OnControllerProfileDropdown(wxCommandEvent& event) -{ - wxComboBox* cb = wxDynamicCast(event.GetEventObject(), wxComboBox); - wxASSERT(cb); - - wxWindowUpdateLocker lock(cb); - - const auto selected_value = cb->GetStringSelection(); - cb->Clear(); - cb->Append(wxEmptyString); - - auto profiles = InputManager::get_profiles(); - for (const auto& profile : profiles) - { - cb->Append(wxString::FromUTF8(profile)); - } - - cb->SetStringSelection(selected_value); -} - -void GameProfileWindow::SetProfileInt(gameProfileIntegerOption_t& option, wxCheckBox* checkbox, sint32 value) const -{ - const auto state = checkbox->GetValue(); - if (state) - { - option.isPresent = true; - option.value = value; - } - else - option.isPresent = false; -} - -void GameProfileWindow::ApplyProfile() -{ - if(m_game_profile.m_gameName) - this->SetTitle(_("Edit game profile") + " - " + m_game_profile.m_gameName.value()); - - // general - m_load_libs->SetValue(m_game_profile.m_loadSharedLibraries.value()); - m_start_with_padview->SetValue(m_game_profile.m_startWithPadView); - - // cpu - // wxString cpu_modes[] = { _("Singlecore-Interpreter"), _("Singlecore-Recompiler"), _("Triplecore-Recompiler"), _("Auto (recommended)") }; - switch(m_game_profile.m_cpuMode.value()) - { - case CPUMode::SinglecoreInterpreter: m_cpu_mode->SetSelection(0); break; - case CPUMode::SinglecoreRecompiler: m_cpu_mode->SetSelection(1); break; - case CPUMode::DualcoreRecompiler: m_cpu_mode->SetSelection(2); break; - case CPUMode::MulticoreRecompiler: m_cpu_mode->SetSelection(2); break; - default: m_cpu_mode->SetSelection(3); - } - - m_thread_quantum->SetStringSelection(fmt::format("{}", m_game_profile.m_threadQuantum)); - - // gpu - if (!m_game_profile.m_graphics_api.has_value()) - m_graphic_api->SetSelection(0); // selecting "" - else - m_graphic_api->SetSelection(1 + m_game_profile.m_graphics_api.value()); // "", OpenGL, Vulkan, Metal - m_shader_mul_accuracy->SetSelection((int)m_game_profile.m_accurateShaderMul); -#ifdef ENABLE_METAL - m_shader_fast_math->SetSelection((int)m_game_profile.m_shaderFastMath); - m_metal_buffer_cache_mode->SetSelection((int)m_game_profile.m_metalBufferCacheMode); - m_position_invariance->SetSelection((int)m_game_profile.m_positionInvariance); -#endif - - //// audio - //m_disable_audio->Set3StateValue(GetCheckboxState(m_game_profile.disableAudio)); - - // controller - auto profiles = InputManager::get_profiles(); - - for (const auto& cb : m_controller_profile) - { - cb->Clear(); - for (const auto& profile : profiles) - { - cb->Append(wxString::FromUTF8(profile)); - } - } - - for (int i = 0; i < InputManager::kMaxController; ++i) - { - const bool has_value = m_game_profile.m_controllerProfile[i].has_value(); - if (has_value) - { - const auto& v = m_game_profile.m_controllerProfile[i].value(); - m_controller_profile[i]->SetStringSelection(wxString::FromUTF8(v)); - } - - else - m_controller_profile[i]->SetSelection(wxNOT_FOUND); - } -} - -void GameProfileWindow::SaveProfile() -{ - // update game profile struct - m_game_profile.Reset(); - // general - m_game_profile.m_loadSharedLibraries = m_load_libs->GetValue(); - m_game_profile.m_startWithPadView = m_start_with_padview->GetValue(); - - // cpu - switch(m_cpu_mode->GetSelection()) - { - case 0: m_game_profile.m_cpuMode = CPUMode::SinglecoreInterpreter; break; - case 1: m_game_profile.m_cpuMode = CPUMode::SinglecoreRecompiler; break; - case 2: m_game_profile.m_cpuMode = CPUMode::MulticoreRecompiler; break; - default: - m_game_profile.m_cpuMode = CPUMode::Auto; - } - - - const wxString thread_quantum = m_thread_quantum->GetStringSelection(); - if (!thread_quantum.empty()) - { - m_game_profile.m_threadQuantum = ConvertString(thread_quantum.ToStdString()); - m_game_profile.m_threadQuantum = std::min(m_game_profile.m_threadQuantum, 536870912); - m_game_profile.m_threadQuantum = std::max(m_game_profile.m_threadQuantum, 5000); - } - - // gpu - m_game_profile.m_accurateShaderMul = (AccurateShaderMulOption)m_shader_mul_accuracy->GetSelection(); - if (m_game_profile.m_accurateShaderMul != AccurateShaderMulOption::False && m_game_profile.m_accurateShaderMul != AccurateShaderMulOption::True) - m_game_profile.m_accurateShaderMul = AccurateShaderMulOption::True; // force a legal value -#ifdef ENABLE_METAL - m_game_profile.m_shaderFastMath = (bool)m_shader_fast_math->GetSelection(); - m_game_profile.m_metalBufferCacheMode = (MetalBufferCacheMode)m_metal_buffer_cache_mode->GetSelection(); - m_game_profile.m_positionInvariance = (PositionInvariance)m_position_invariance->GetSelection(); -#endif - - if (m_graphic_api->GetSelection() == 0) - m_game_profile.m_graphics_api = {}; - else - m_game_profile.m_graphics_api = (GraphicAPI)(m_graphic_api->GetSelection() - 1); // "", OpenGL, Vulkan, Metal - - // controller - for (int i = 0; i < 8; ++i) - { - if(m_controller_profile[i]->GetSelection() == wxNOT_FOUND) - { - m_game_profile.m_controllerProfile[i].reset(); - continue; - } - - const wxString profile_name = m_controller_profile[i]->GetStringSelection(); - if (profile_name.empty()) - m_game_profile.m_controllerProfile[i].reset(); - else - m_game_profile.m_controllerProfile[i] = profile_name.ToUTF8(); - } - - // update game profile file - m_game_profile.Save(m_title_id); -} - -void GameProfileWindow::SetSliderValue(wxSlider* slider, sint32 new_value) const -{ - wxASSERT(slider); - slider->SetValue(new_value); - - wxCommandEvent slider_event(wxEVT_SLIDER, slider->GetId()); - slider_event.SetEventObject(slider); - slider_event.SetClientData((void*)IsFrozen()); - wxPostEvent(slider->GetEventHandler(), slider_event); -} diff --git a/src/gui/wxgui/GameProfileWindow.h b/src/gui/wxgui/GameProfileWindow.h deleted file mode 100644 index fe6150dc..00000000 --- a/src/gui/wxgui/GameProfileWindow.h +++ /dev/null @@ -1,55 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include - -#include "Cafe/GameProfile/GameProfile.h" - -class GameProfileWindow : public wxFrame -{ -public: - GameProfileWindow(wxWindow* parent, uint64_t title_id); - ~GameProfileWindow(); - -private: - uint64_t m_title_id; - GameProfile m_game_profile; - - void OnStreamoutSizeChange(wxCommandEvent& event); - void OnControllerProfileDropdown(wxCommandEvent& event); - - void SetSliderValue(wxSlider* slider, sint32 new_value) const; - void SetProfileInt(gameProfileIntegerOption_t& option, wxCheckBox* checkbox, sint32 value) const; - - void ApplyProfile(); - void SaveProfile(); - - // general - wxCheckBox* m_load_libs, *m_start_with_padview; - - // cpu - wxChoice *m_cpu_mode; - wxChoice* m_thread_quantum; - - // gpu - //wxCheckBox* m_extended_texture_readback; - //wxChoice* m_precompiled; - wxChoice* m_graphic_api; - - wxChoice* m_shader_mul_accuracy; -#ifdef ENABLE_METAL - wxChoice* m_shader_fast_math; - wxChoice* m_metal_buffer_cache_mode; - wxChoice* m_position_invariance; -#endif - //wxChoice* m_cache_accuracy; - - // audio - //wxCheckBox* m_disable_audio; - - // controller - wxComboBox* m_controller_profile[8]; -}; diff --git a/src/gui/wxgui/GameUpdateWindow.cpp b/src/gui/wxgui/GameUpdateWindow.cpp deleted file mode 100644 index 253a7610..00000000 --- a/src/gui/wxgui/GameUpdateWindow.cpp +++ /dev/null @@ -1,339 +0,0 @@ -#include "wxgui/wxgui.h" -#include "wxgui/GameUpdateWindow.h" -#include "util/helpers/helpers.h" -#include "util/helpers/SystemException.h" -#include "Cafe/TitleList/GameInfo.h" -#include "config/ActiveSettings.h" -#include "wxgui/helpers/wxHelpers.h" - -wxString _GetTitleIdTypeStr(TitleId titleId) -{ - TitleIdParser tip(titleId); - switch (tip.GetType()) - { - case TitleIdParser::TITLE_TYPE::AOC: - return _("DLC"); - case TitleIdParser::TITLE_TYPE::BASE_TITLE: - return _("Base game"); - case TitleIdParser::TITLE_TYPE::BASE_TITLE_DEMO: - return _("Demo"); - case TitleIdParser::TITLE_TYPE::SYSTEM_TITLE: - case TitleIdParser::TITLE_TYPE::SYSTEM_OVERLAY_TITLE: - return _("System title"); - case TitleIdParser::TITLE_TYPE::SYSTEM_DATA: - return _("System data title"); - case TitleIdParser::TITLE_TYPE::BASE_TITLE_UPDATE: - return _("Update"); - default: - break; - } - return "Unknown"; -} - -bool GameUpdateWindow::ParseUpdate(const fs::path& metaPath) -{ - m_title_info = TitleInfo(metaPath); - if (!m_title_info.IsValid()) - return false; - fs::path target_location = ActiveSettings::GetMlcPath(m_title_info.GetInstallPath()); - std::error_code ec; - if (fs::exists(target_location, ec)) - { - try - { - const TitleInfo tmp(target_location); - if (!tmp.IsValid()) - { - // does not exist / is not valid. We allow to overwrite it - } - else - { - TitleIdParser tip(m_title_info.GetAppTitleId()); - TitleIdParser tipOther(tmp.GetAppTitleId()); - - if (tip.GetType() != tipOther.GetType()) - { - auto typeStrToInstall = _GetTitleIdTypeStr(m_title_info.GetAppTitleId()); - auto typeStrCurrentlyInstalled = _GetTitleIdTypeStr(tmp.GetAppTitleId()); - - auto wxMsg = _("It seems that there is already a title installed at the target location but it has a different type.\nCurrently installed: \'{}\' Installing: \'{}\'\n\nThis can happen for titles which were installed with very old Cemu versions.\nDo you still want to continue with the installation? It will replace the currently installed title."); - wxMessageDialog dialog(this, formatWxString(wxMsg, typeStrCurrentlyInstalled, typeStrToInstall), _("Warning"), wxCENTRE | wxYES_NO | wxICON_EXCLAMATION); - if (dialog.ShowModal() != wxID_YES) - return false; - } - else if (tmp.GetAppTitleVersion() == m_title_info.GetAppTitleVersion()) - { - wxMessageDialog dialog(this, _("It seems that the selected title is already installed, do you want to reinstall it?"), _("Warning"), wxCENTRE | wxYES_NO); - if (dialog.ShowModal() != wxID_YES) - return false; - } - else if (tmp.GetAppTitleVersion() > m_title_info.GetAppTitleVersion()) - { - wxMessageDialog dialog(this, _("It seems that a newer version is already installed, do you still want to install the older version?"), _("Warning"), wxCENTRE | wxYES_NO); - if (dialog.ShowModal() != wxID_YES) - return false; - } - } - - // temp rename until done - m_backup_folder = target_location; - m_backup_folder.replace_extension(".backup"); - - std::error_code ec; - while (fs::exists(m_backup_folder, ec) || ec) - { - fs::remove_all(m_backup_folder, ec); - - if (ec) - { - const auto error_msg = formatWxString(_("Error when trying to move former title installation:\n{}"), GetSystemErrorMessage(ec)); - wxMessageBox(error_msg, _("Error"), wxOK | wxCENTRE, this); - return false; - } - - // wait so filesystem doesnt - std::this_thread::sleep_for(std::chrono::milliseconds(100)); - } - - fs::rename(target_location, m_backup_folder); - } - catch (const std::exception& ex) - { - cemuLog_log(LogType::Force, "GameUpdateWindow::ParseUpdate exist-error: {} at {}", ex.what(), _pathToUtf8(target_location)); - } - } - - m_target_path = target_location; - - fs::path source(metaPath); - - m_source_paths = - { - fs::path(source).append("content"), - fs::path(source).append("code"), - fs::path(source).append("meta") - }; - - m_required_size = 0; - for (auto& path : m_source_paths) - { - for (const fs::directory_entry& f : fs::recursive_directory_iterator(path)) - { - if (is_regular_file(f.path())) - m_required_size += file_size(f.path()); - } - } - - const fs::space_info targetSpace = fs::space(ActiveSettings::GetMlcPath()); - if (targetSpace.free <= m_required_size) - { - auto string = formatWxString(_("Not enough space available.\nRequired: {0} MB\nAvailable: {1} MB"), (m_required_size / 1024 / 1024), (targetSpace.free / 1024 / 1024)); - throw std::runtime_error(string.utf8_string()); - } - - return true; -} - -GameUpdateWindow::GameUpdateWindow(wxWindow& parent, const fs::path& filePath) - : wxDialog(&parent, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxCAPTION | wxMINIMIZE_BOX | wxSYSTEM_MENU | wxTAB_TRAVERSAL | wxCLOSE_BOX), - m_thread_state(ThreadRunning) -{ - try - { - #if BOOST_OS_WINDOWS - SetLastError(0); - #endif - if(!ParseUpdate(filePath)) - throw AbortException(); - } - catch (const std::runtime_error& ex) - { - throw SystemException(ex); - } - - auto sizer = new wxBoxSizer(wxVERTICAL); - - TitleIdParser tip(GetTitleId()); - - if (tip.GetType() == TitleIdParser::TITLE_TYPE::AOC) - SetTitle(_("Installing DLC...")); - else if (tip.GetType() == TitleIdParser::TITLE_TYPE::BASE_TITLE_UPDATE) - SetTitle(_("Installing update...")); - else if (tip.IsSystemTitle()) - SetTitle(_("Installing system title...")); - else - SetTitle(_("Installing title...")); - - m_processBar = new wxGauge(this, wxID_ANY, 100, wxDefaultPosition, wxSize(500, 20), wxGA_HORIZONTAL); - m_processBar->SetValue(0); - m_processBar->SetRange((sint32)(m_required_size / 1000)); - sizer->Add(m_processBar, 0, wxALL | wxEXPAND, 5); - - wxButton* m_cancelButton = new wxButton(this, wxID_ANY, _("Cancel")); - m_cancelButton->Bind(wxEVT_BUTTON, &GameUpdateWindow::OnCancelButton, this); - sizer->Add(m_cancelButton, 0, wxALIGN_RIGHT | wxALL, 5); - - this->SetSizer(sizer); - this->Centre(wxBOTH); - - wxWindowBase::Layout(); - wxWindowBase::Fit(); - - m_timer = new wxTimer(this); - this->Bind(wxEVT_TIMER, &GameUpdateWindow::OnUpdate, this); - this->Bind(wxEVT_CLOSE_WINDOW, &GameUpdateWindow::OnClose, this); - m_timer->Start(250); - - m_thread_state = ThreadRunning; - m_thread = std::thread(&GameUpdateWindow::ThreadWork, this); -} - -void GameUpdateWindow::ThreadWork() -{ - fs::directory_entry currentDirEntry; - try - { - // create base directories - for (auto& path : m_source_paths) - { - if (!path.has_stem()) - continue; - - fs::path targetDir = fs::path(m_target_path) / path.stem(); - create_directories(targetDir); - } - - for (auto& path : m_source_paths) - { - if (m_thread_state == ThreadCanceled) - break; - - if (!path.has_parent_path()) - continue; - - const auto len = path.parent_path().string().size() + 1; - for (const fs::directory_entry& f : fs::recursive_directory_iterator {path}) - { - if (m_thread_state == ThreadCanceled) - break; - - currentDirEntry = f; - fs::path relative(f.path().string().substr(len)); - fs::path target = fs::path(m_target_path) / relative; - if (is_directory(f)) - { - create_directories(target); - continue; - } - - copy(f, target, fs::copy_options::overwrite_existing); - if (is_regular_file(f.path())) - { - m_processed_size += file_size(f.path()); - } - } - } - } - catch (const std::exception& ex) - { - std::stringstream error_msg; - error_msg << GetSystemErrorMessage(ex); - - if(currentDirEntry != fs::directory_entry{}) - error_msg << fmt::format("\n{}\n{}",_("Current file:").utf8_string(), _pathToUtf8(currentDirEntry.path())); - - m_thread_exception = error_msg.str(); - m_thread_state = ThreadCanceled; - } - - if (m_thread_state == ThreadCanceled) - { - if(fs::exists(m_target_path)) - fs::remove_all(m_target_path); - } - else - m_thread_state = ThreadFinished; -} - -GameUpdateWindow::~GameUpdateWindow() -{ - m_timer->Stop(); - if (m_thread.joinable()) - m_thread.join(); -} - -int GameUpdateWindow::ShowModal() -{ - wxDialog::ShowModal(); - return m_thread_state == ThreadCanceled ? wxID_CANCEL : wxID_OK; -} - -void GameUpdateWindow::OnClose(wxCloseEvent& event) -{ - if (m_thread_state == ThreadRunning) - { - wxMessageDialog dialog(this, _("Do you really want to cancel the installation process?\n\nCanceling the process will delete the applied files."), _("Info"), wxCENTRE | wxYES_NO); - if (dialog.ShowModal() != wxID_YES) - return; - - m_thread_state = ThreadCanceled; - } - - m_timer->Stop(); - if (m_thread.joinable()) - m_thread.join(); - - if(!m_backup_folder.empty()) - { - if(m_thread_state == ThreadCanceled) - { - // restore backup - try - { - if(fs::exists(m_target_path)) - fs::remove_all(m_target_path); - - fs::rename(m_backup_folder, m_target_path); - } - catch (const std::exception& ex) - { - cemuLog_logDebug(LogType::Force, "can't restore update backup: {}",ex.what()); - } - } - else - { - // delete backup - try - { - if(fs::exists(m_backup_folder)) - fs::remove_all(m_backup_folder); - } - catch (const std::exception& ex) - { - cemuLog_logDebug(LogType::Force, "can't delete update backup: {}",ex.what()); - } - } - - m_backup_folder.clear(); - } - - event.Skip(); -} - -void GameUpdateWindow::OnUpdate(const wxTimerEvent& event) -{ - if (m_thread_state != ThreadRunning) - { - Close(); - return; - } - - const auto processedSize = (sint32)(m_processed_size / 1000); - if (m_processBar->GetValue() != processedSize) - m_processBar->SetValue(processedSize); -} - -void GameUpdateWindow::OnCancelButton(const wxCommandEvent& event) -{ - Close(); -} diff --git a/src/gui/wxgui/GameUpdateWindow.h b/src/gui/wxgui/GameUpdateWindow.h deleted file mode 100644 index 508e6465..00000000 --- a/src/gui/wxgui/GameUpdateWindow.h +++ /dev/null @@ -1,67 +0,0 @@ -#pragma once - -#include "Cafe/TitleList/GameInfo.h" - -#include -#include -#include - -#include -#include -#include -#include - -// thrown if users doesn't wish to reinstall update/dlc -class AbortException : public std::exception {}; - -class GameUpdateWindow : public wxDialog -{ -public: - - GameUpdateWindow(wxWindow& parent, const fs::path& metaPath); - ~GameUpdateWindow(); - - uint64 GetTitleId() const { return m_title_info.GetAppTitleId(); } - bool HasException() const { return !m_thread_exception.empty(); } - //bool IsDLC() const { return m_game_info->IsDLC(); } - //bool IsUpdate() const { return m_game_info->IsUpdate(); } - const std::string& GetExceptionMessage() const { return m_thread_exception; } - const std::string GetGameName() const { return m_title_info.GetMetaTitleName(); } - uint32 GetTargetVersion() const { return m_title_info.GetAppTitleVersion(); } - fs::path GetTargetPath() const { return fs::path(m_target_path); } - - int ShowModal() override; - void OnClose(wxCloseEvent& event); - - void OnUpdate(const wxTimerEvent& event); - void OnCancelButton(const wxCommandEvent& event); - - //uint64 GetUpdateTitleId() const { return m_title_info->GetUpdateTitleId(); } - //uint64 GetDLCTitleId() const { return m_game_info->GetDLCTitleId(); } - -private: - //std::unique_ptr m_game_info; - TitleInfo m_title_info; - enum ThreadState_t - { - ThreadRunning, - ThreadCanceled, - ThreadFinished, - }; - - uint64_t m_required_size; - fs::path m_target_path; - std::array m_source_paths; - bool ParseUpdate(const fs::path& metaPath); - - std::atomic m_processed_size = 0; - std::atomic m_thread_state; - std::string m_thread_exception; - std::thread m_thread; - void ThreadWork(); - - fs::path m_backup_folder; // for prev update data - - wxGauge* m_processBar; - wxTimer* m_timer; -}; diff --git a/src/gui/wxgui/GeneralSettings2.cpp b/src/gui/wxgui/GeneralSettings2.cpp deleted file mode 100644 index 0dd8c768..00000000 --- a/src/gui/wxgui/GeneralSettings2.cpp +++ /dev/null @@ -1,2509 +0,0 @@ -#include "wxCemuConfig.h" -#include "wxgui/wxgui.h" -#include "wxgui/GeneralSettings2.h" -#include "wxgui/CemuApp.h" -#include "wxgui/helpers/wxControlObject.h" - -#include "util/helpers/helpers.h" - -#include "Cafe/OS/libs/snd_core/ax.h" - -#include -#include -#include -#include -#include -#include - -#include "config/CemuConfig.h" -#include "config/NetworkSettings.h" - -#include "audio/IAudioAPI.h" -#if HAS_DIRECTX_AUDIO -#include "audio/DirectSoundAPI.h" -#include "audio/XAudio27API.h" -#endif -#include "audio/CubebAPI.h" - -#include "audio/IAudioInputAPI.h" - -#ifdef ENABLE_VULKAN -#include "Cafe/HW/Latte/Renderer/Vulkan/VulkanAPI.h" -#include "Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.h" -#endif -#ifdef ENABLE_METAL -#include "Cafe/HW/Latte/Renderer/Metal/MetalRenderer.h" -#endif -#include "Cafe/Account/Account.h" - -#include -#include "util/helpers/SystemException.h" -#include "wxgui/dialogs/CreateAccount/wxCreateAccountDialog.h" - -#if BOOST_OS_WINDOWS -#include -#endif - -#include "config/LaunchSettings.h" -#include "config/ActiveSettings.h" -#include "wxgui/helpers/wxHelpers.h" - -#include "resource/embedded/resources.h" - -#include "Cafe/CafeSystem.h" -#include "Cemu/ncrypto/ncrypto.h" -#include "Cafe/TitleList/TitleList.h" -#include "wxHelper.h" - -#include "util/ScreenSaver/ScreenSaver.h" - -const wxString kDirectSound("DirectSound"); -const wxString kXAudio27("XAudio2.7"); -const wxString kXAudio2("XAudio2"); -const wxString kCubeb("Cubeb"); - -const wxString kPropertyPersistentId("PersistentId"); -const wxString kPropertyMiiName("MiiName"); -const wxString kPropertyBirthday("Birthday"); -const wxString kPropertyGender("Gender"); -const wxString kPropertyEmail("Email"); -const wxString kPropertyCountry("Country"); - -wxDEFINE_EVENT(wxEVT_ACCOUNTLIST_REFRESH, wxCommandEvent); - -class wxDeviceDescription : public wxClientData -{ -public: - wxDeviceDescription(const IAudioAPI::DeviceDescriptionPtr& description) : m_description(description) {} - const IAudioAPI::DeviceDescriptionPtr& GetDescription() const { return m_description; } -private: - IAudioAPI::DeviceDescriptionPtr m_description; -}; - -class wxInputDeviceDescription : public wxClientData -{ -public: - wxInputDeviceDescription(const IAudioInputAPI::DeviceDescriptionPtr& description) : m_description(description) {} - const IAudioInputAPI::DeviceDescriptionPtr& GetDescription() const { return m_description; } -private: - IAudioInputAPI::DeviceDescriptionPtr m_description; -}; - -#ifdef ENABLE_VULKAN -class wxVulkanUUID : public wxClientData -{ -public: - wxVulkanUUID(const VulkanRenderer::DeviceInfo& info) - : m_device_info(info) {} - const VulkanRenderer::DeviceInfo& GetDeviceInfo() const { return m_device_info; } - -private: - VulkanRenderer::DeviceInfo m_device_info; -}; -#endif - -#ifdef ENABLE_METAL -class wxMetalUUID : public wxClientData -{ -public: - wxMetalUUID(const MetalRenderer::DeviceInfo& info) - : m_device_info(info) {} - const MetalRenderer::DeviceInfo& GetDeviceInfo() const { return m_device_info; } - -private: - MetalRenderer::DeviceInfo m_device_info; -}; -#endif - -class wxAccountData : public wxClientData -{ -public: - wxAccountData(const Account& account) - : m_account(account) {} - - Account& GetAccount() { return m_account; } - const Account& GetAccount() const { return m_account; } - -private: - Account m_account; -}; - -wxPanel* GeneralSettings2::AddGeneralPage(wxNotebook* notebook) -{ - auto* panel = new wxPanel(notebook); - auto* general_panel_sizer = new wxBoxSizer(wxVERTICAL); - - { - auto* box = new wxStaticBox(panel, wxID_ANY, _("Interface")); - auto* box_sizer = new wxStaticBoxSizer(box, wxVERTICAL); - - { - auto* first_row = new wxFlexGridSizer(0, 2, 0, 0); - first_row->SetFlexibleDirection(wxBOTH); - first_row->SetNonFlexibleGrowMode(wxFLEX_GROWMODE_SPECIFIED); - - first_row->Add(new wxStaticText(box, wxID_ANY, _("Language")), 0, wxALIGN_CENTER_VERTICAL | wxALL, 5); - - wxString language_choices[] = { _("Default") }; - m_language = new wxChoice(box, wxID_ANY, wxDefaultPosition, wxDefaultSize, std::size(language_choices), language_choices); - m_language->SetSelection(0); - m_language->SetToolTip(_("Changes the interface language of Cemu\nAvailable languages are stored in the translation directory\nA restart will be required after changing the language")); - for (const auto& language : wxGetApp().GetLanguages()) - { - m_language->Append(language->DescriptionNative); - } - - first_row->Add(m_language, 0, wxALL | wxEXPAND, 5); - - box_sizer->Add(first_row, 1, wxEXPAND, 5); - } - -#if BOOST_OS_WINDOWS - { - auto* second_row = new wxFlexGridSizer(0, 2, 0, 0); - second_row->SetFlexibleDirection(wxBOTH); - second_row->SetNonFlexibleGrowMode(wxFLEX_GROWMODE_SPECIFIED); - - second_row->Add(new wxStaticText(box, wxID_ANY, _("Theme")), 0, wxALIGN_CENTER_VERTICAL | wxALL, 5); - - m_msw_theme = new wxChoice(box, wxID_ANY); - m_msw_theme->SetToolTip(_("Changes the Windows theme used by Cemu\nThis only works on Windows 10 and later\nA restart will be required for any changes to take effect")); - - m_msw_theme->AppendString(_("Follow Windows theme")); - m_msw_theme->AppendString(_("Light Theme")); - m_msw_theme->AppendString(_("Dark Theme")); - m_msw_theme->SetSelection(0); - - second_row->Add(m_msw_theme, 0, wxALL, 5); - - box_sizer->Add(second_row, 0, wxEXPAND, 5); - } -#endif - - { - auto* third_row = new wxFlexGridSizer(0, 3, 0, 0); - third_row->SetFlexibleDirection(wxBOTH); - third_row->SetNonFlexibleGrowMode(wxFLEX_GROWMODE_SPECIFIED); - - sint32 checkboxCount = 0; - auto CountRowElement = [&]() - { - checkboxCount++; - if(checkboxCount != 2) - return; - third_row->AddSpacer(10); - checkboxCount = 0; - }; - - auto InsertEmptyRow = [&]() - { - while(checkboxCount != 0) - CountRowElement(); - third_row->AddSpacer(10); - third_row->AddSpacer(10); - third_row->AddSpacer(10); - }; - - const int topflag = wxALIGN_CENTER_VERTICAL | wxALL; - m_save_window_position_size = new wxCheckBox(box, wxID_ANY, _("Remember main window position")); - m_save_window_position_size->SetToolTip(_("Restores the last known window position and size when starting Cemu")); - third_row->Add(m_save_window_position_size, 0, topflag, 5); - CountRowElement(); - //third_row->AddSpacer(10); - m_save_padwindow_position_size = new wxCheckBox(box, wxID_ANY, _("Remember pad window position")); - m_save_padwindow_position_size->SetToolTip(_("Restores the last known pad window position and size when opening it")); - third_row->Add(m_save_padwindow_position_size, 0, topflag, 5); - CountRowElement(); - - const int botflag = wxALIGN_CENTER_VERTICAL | wxLEFT | wxRIGHT | wxBOTTOM; - m_discord_presence = new wxCheckBox(box, wxID_ANY, _("Discord Presence")); - m_discord_presence->SetToolTip(_("Enables the Discord Rich Presence feature\nYou will also need to enable it in the Discord settings itself!")); - third_row->Add(m_discord_presence, 0, botflag, 5); - CountRowElement(); -#ifndef ENABLE_DISCORD_RPC - m_discord_presence->Disable(); -#endif - // third_row->AddSpacer(10); - m_fullscreen_menubar = new wxCheckBox(box, wxID_ANY, _("Fullscreen menu bar")); - m_fullscreen_menubar->SetToolTip(_("Displays the menu bar when Cemu is running in fullscreen mode and the mouse cursor is moved to the top")); - third_row->Add(m_fullscreen_menubar, 0, botflag, 5); - CountRowElement(); - - m_save_screenshot = new wxCheckBox(box, wxID_ANY, _("Save screenshot")); - m_save_screenshot->SetToolTip(_("Pressing the screenshot key will save a screenshot directly to the screenshots folder instead of to the clipboard")); - third_row->Add(m_save_screenshot, 0, botflag, 5); - CountRowElement(); - - m_disable_screensaver = new wxCheckBox(box, wxID_ANY, _("Disable screen saver")); - m_disable_screensaver->SetToolTip(_("Prevents the system from activating the screen saver or going to sleep while running a game.")); - third_row->Add(m_disable_screensaver, 0, botflag, 5); - CountRowElement(); - - // enable/disable feral interactive gamemode -#if BOOST_OS_LINUX && defined(ENABLE_FERAL_GAMEMODE) - m_feral_gamemode = new wxCheckBox(box, wxID_ANY, _("Enable Feral GameMode")); - m_feral_gamemode->SetToolTip(_("Use FeralInteractive GameMode if installed.")); - third_row->Add(m_feral_gamemode, 0, botflag, 5); - CountRowElement(); -#endif - - // temporary workaround because feature crashes on macOS -#if BOOST_OS_MACOS - m_disable_screensaver->Enable(false); -#endif - m_play_boot_sound = new wxCheckBox(box, wxID_ANY, _("Enable intro sound")); - m_play_boot_sound->SetToolTip(_("Play bootSound file while compiling shaders/pipelines.")); - third_row->Add(m_play_boot_sound, 0, botflag, 5); - CountRowElement(); - - m_auto_update = new wxCheckBox(box, wxID_ANY, _("Automatically check for updates")); - m_auto_update->SetToolTip(_("Automatically checks for new cemu versions on startup")); - third_row->Add(m_auto_update, 0, botflag, 5); - CountRowElement(); - - m_receive_untested_releases = new wxCheckBox(box, wxID_ANY, _("Receive untested updates")); - m_receive_untested_releases->SetToolTip(_("When checking for updates, include brand new and untested releases. These may contain bugs!")); - third_row->Add(m_receive_untested_releases, 0, botflag, 5); -#if BOOST_OS_LINUX - if (!std::getenv("APPIMAGE")) { - m_auto_update->Disable(); - } -#elif BOOST_OS_BSD // BSD users must update from source so disable auto updates - m_auto_update->Disable(); -#endif - - box_sizer->Add(third_row, 0, wxEXPAND, 5); - } - - general_panel_sizer->Add(box_sizer, 0, wxEXPAND | wxALL, 5); - } - - { - auto* outerMlcBox = new wxStaticBox(panel, wxID_ANY, _("Custom MLC path")); - - auto* box_sizer_mlc = new wxStaticBoxSizer(outerMlcBox, wxVERTICAL); - box_sizer_mlc->Add(new wxStaticText(box_sizer_mlc->GetStaticBox(), wxID_ANY, _("You can configure a custom path for the emulated internal Wii U storage (MLC).\nThis is where Cemu stores saves, accounts and other Wii U system files.")), 0, wxALL, 5); - - auto* mlcPathLineSizer = new wxBoxSizer(wxHORIZONTAL); - - m_mlc_path = new wxTextCtrl(outerMlcBox, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxTE_READONLY); - m_mlc_path->SetMinSize(wxSize(150, -1)); - m_mlc_path->SetToolTip(_("The mlc directory contains your save games and installed game update/dlc data")); - - mlcPathLineSizer->Add(m_mlc_path, 1, wxALL | wxEXPAND, 5); - - auto* changePath = new wxButton(outerMlcBox, wxID_ANY, "Change"); - changePath->Bind(wxEVT_BUTTON, &GeneralSettings2::OnMLCPathSelect, this); - mlcPathLineSizer->Add(changePath, 0, wxALL, 5); - if (LaunchSettings::GetMLCPath().has_value()) - changePath->Disable(); - - auto* clearPath = new wxButton(outerMlcBox, wxID_ANY, "Clear custom path"); - clearPath->Bind(wxEVT_BUTTON, &GeneralSettings2::OnMLCPathClear, this); - mlcPathLineSizer->Add(clearPath, 0, wxALL, 5); - if (LaunchSettings::GetMLCPath().has_value() || !ActiveSettings::IsCustomMlcPath()) - clearPath->Disable(); - - box_sizer_mlc->Add(mlcPathLineSizer, 0, wxEXPAND, 5); - general_panel_sizer->Add(box_sizer_mlc, 0, wxEXPAND | wxALL, 5); - } - - { - auto* general_gamepath_box = new wxStaticBox(panel, wxID_ANY, _("Game Paths")); - auto* general_gamepath_sizer = new wxStaticBoxSizer(general_gamepath_box, wxVERTICAL); - - m_game_paths = new wxListBox(general_gamepath_box, wxID_ANY); - m_game_paths->SetMinSize(wxSize(150, 70)); - m_game_paths->SetToolTip(_("Add the root directory of your game(s). It will scan all directories in it for games")); - general_gamepath_sizer->Add(m_game_paths, 1, wxALL | wxEXPAND, 5); - - auto* general_gamepath_buttons = new wxFlexGridSizer(0, 2, 0, 0); - general_gamepath_buttons->SetFlexibleDirection(wxBOTH); - general_gamepath_buttons->SetNonFlexibleGrowMode(wxFLEX_GROWMODE_SPECIFIED); - - auto* general_gamepath_add_button = new wxButton(general_gamepath_box, wxID_ANY, _("Add")); - general_gamepath_add_button->Bind(wxEVT_BUTTON, &GeneralSettings2::OnAddPathClicked, this); - general_gamepath_add_button->SetToolTip(_("Adds a game path to scan for games displayed in the game list\nIf you have unpacked games, make sure to select the root folder of a game")); - general_gamepath_buttons->Add(general_gamepath_add_button, 0, wxALL, 5); - - auto* general_gamepath_remove_button = new wxButton(general_gamepath_box, wxID_ANY, _("Remove")); - general_gamepath_remove_button->Bind(wxEVT_BUTTON, &GeneralSettings2::OnRemovePathClicked, this); - general_gamepath_remove_button->SetToolTip(_("Removes the currently selected game path from the game list")); - general_gamepath_buttons->Add(general_gamepath_remove_button, 0, wxALL, 5); - - general_gamepath_sizer->Add(general_gamepath_buttons, 0, wxEXPAND, 5); - general_panel_sizer->Add(general_gamepath_sizer, 1, wxEXPAND | wxALL, 5); - } - - panel->SetSizerAndFit(general_panel_sizer); - - return panel; -} - -wxPanel* GeneralSettings2::AddGraphicsPage(wxNotebook* notebook) -{ - // Graphics page - auto graphics_panel = new wxPanel(notebook); - auto graphics_panel_sizer = new wxBoxSizer(wxVERTICAL); - - { - auto box = new wxStaticBox(graphics_panel, wxID_ANY, _("General")); - auto box_sizer = new wxStaticBoxSizer(box, wxVERTICAL); - - auto row = new wxFlexGridSizer(0, 2, 0, 0); - row->SetFlexibleDirection(wxBOTH); - row->SetNonFlexibleGrowMode(wxFLEX_GROWMODE_SPECIFIED); - - row->Add(new wxStaticText(box, wxID_ANY, _("Graphics API")), 0, wxALIGN_CENTER_VERTICAL | wxALL, 5); - - sint32 api_size = 0; - wxString choices[size_t(GraphicAPI::COUNT)]; - -#ifdef ENABLE_OPENGL - choices[api_size++] = "OpenGL"; - m_api_map.push_back(GraphicAPI::kOpenGL); -#endif -#ifdef ENABLE_VULKAN - if (g_vulkan_available) - { - choices[api_size++] = "Vulkan"; - m_api_map.push_back(GraphicAPI::kVulkan); - } -#endif -#ifdef ENABLE_METAL - choices[api_size++] = "Metal"; - m_api_map.push_back(GraphicAPI::kMetal); -#endif - wxASSERT(api_size > 0); - - m_graphic_api = new wxChoice(box, wxID_ANY, wxDefaultPosition, wxDefaultSize, api_size, choices); - m_graphic_api->SetSelection(0); - if (api_size > 1) - m_graphic_api->SetToolTip(_("Select one of the available graphic back ends")); - row->Add(m_graphic_api, 0, wxALL, 5); - m_graphic_api->Bind(wxEVT_CHOICE, &GeneralSettings2::OnGraphicAPISelected, this); - - row->Add(new wxStaticText(box, wxID_ANY, _("Graphics Device")), 0, wxALIGN_CENTER_VERTICAL | wxALL, 5); - m_graphic_device = new wxChoice(box, wxID_ANY, wxDefaultPosition, { 230, -1 }, api_size, choices); - m_graphic_device->SetSelection(0); - m_graphic_device->SetToolTip(_("Select the used graphic device")); - row->Add(m_graphic_device, 0, wxALL, 5); - - row->Add(new wxStaticText(box, wxID_ANY, _("VSync")), 0, wxALIGN_CENTER_VERTICAL | wxALL, 5); - m_vsync = new wxChoice(box, wxID_ANY, wxDefaultPosition, { 230, -1 }); - m_vsync->SetToolTip(_("Controls the vsync state")); - m_vsync->Bind(wxEVT_CHOICE, [](wxCommandEvent& event) { - GetConfig().vsync = event.GetSelection(); - }); - row->Add(m_vsync, 0, wxALL, 5); - - box_sizer->Add(row, 0, wxEXPAND, 5); - - auto* graphic_misc_row = new wxFlexGridSizer(0, 2, 0, 0); - - m_async_compile = new wxCheckBox(box, wxID_ANY, _("Async shader compile")); - m_async_compile->SetToolTip(_("Enables async shader and pipeline compilation. Reduces stutter at the cost of objects not rendering for a short time.\nVulkan only")); - graphic_misc_row->Add(m_async_compile, 0, wxALL, 5); - - m_gx2drawdone_sync = new wxCheckBox(box, wxID_ANY, _("Full sync at GX2DrawDone()")); - m_gx2drawdone_sync->SetToolTip(_("If synchronization is requested by the game, the emulated CPU will wait for the GPU to finish all operations.\nThis is more accurate behavior, but may cause lower performance")); - graphic_misc_row->Add(m_gx2drawdone_sync, 0, wxALL, 5); - -#ifdef ENABLE_METAL - m_force_mesh_shaders = new wxCheckBox(box, wxID_ANY, _("Force mesh shaders")); - m_force_mesh_shaders->SetToolTip(_("Force mesh shaders on all GPUs that support them. Mesh shaders are disabled by default on Intel GPUs due to potential stability issues.\nMetal only")); - graphic_misc_row->Add(m_force_mesh_shaders, 0, wxALL, 5); -#endif - - box_sizer->Add(graphic_misc_row, 1, wxEXPAND, 5); - graphics_panel_sizer->Add(box_sizer, 0, wxEXPAND | wxALL, 5); - } - - { - auto box = new wxStaticBox(graphics_panel, wxID_ANY, _("Gamma settings")); - auto box_sizer = new wxStaticBoxSizer(box, wxVERTICAL); - auto row = new wxFlexGridSizer(0, 2, 0, 0); - row->SetFlexibleDirection(wxBOTH); - row->SetNonFlexibleGrowMode(wxFLEX_GROWMODE_SPECIFIED); - - auto targetGammaLabel = new wxStaticText(box, wxID_ANY, _("Target Gamma")); - row->Add(targetGammaLabel, 0, wxALIGN_CENTER_VERTICAL | wxALL, 5); - m_overrideGammaValue = new wxSpinCtrlDouble(box, wxID_ANY, "2.2f", wxDefaultPosition, {230, -1}, wxSP_ARROW_KEYS, 0.1f, 4.0f, 2.2f, 0.1f); - row->Add(m_overrideGammaValue, 0, wxALL, 5); - auto targetGammaTooltip = _("The display gamma to reproduce\nIf you are unsure, set this to 2.2"); - targetGammaLabel->SetToolTip(targetGammaTooltip); - m_overrideGammaValue->SetToolTip(targetGammaTooltip); - m_overrideGammaValue->Bind(wxEVT_SPINCTRLDOUBLE, [](wxSpinDoubleEvent& event) { - GetConfig().overrideGammaValue = event.GetValue(); - }); - - - auto displayGammaLabel = new wxStaticText(box, wxID_ANY, _("Display Gamma")); - row->Add(displayGammaLabel, 0, wxALIGN_CENTER_VERTICAL | wxALL, 5); - - wxBoxSizer* srgbCheckBoxSizer = new wxBoxSizer(wxHORIZONTAL); - row->Add(srgbCheckBoxSizer); - m_userDisplayGamma = new wxSpinCtrlDouble(box, wxID_ANY, "2.2f", wxDefaultPosition, {230, -1}, wxSP_ARROW_KEYS, 0.1f, 4.0f, 2.2f, 0.1f); - - auto displayGammaTooltip = _("The gamma of your monitor\nIf you are unsure, set this to 2.2"); - m_userDisplayGamma->SetToolTip(displayGammaTooltip); - displayGammaLabel->SetToolTip(displayGammaTooltip); - m_userDisplayGamma->Bind(wxEVT_SPINCTRLDOUBLE, [](wxSpinDoubleEvent& event) { - GetConfig().userDisplayGamma = event.GetValue(); - }); - - m_userDisplayisSRGB = new wxCheckBox(box, wxID_ANY, "sRGB"); - m_userDisplayisSRGB->SetToolTip(_("Select this if Cemu is being displayed using a piecewise sRGB gamma curve.\n" - "This is typically not the case so you can probably leave this unchecked.\n" - "Exceptions include HDR displays (with HDR enabled), calibrated SDR displays with Windows 11's Auto Color Management enabled, " - "or when using a display profile with a VCGT tag that targets piecewise sRGB.\n" - "When this box is selected Cemu will compensate for the piecewise curve to approximate the pure gamma curve of a TV.\n" - "Colors will be more accurate, especially in dark scenes, but this may result in banding or crushed shadows, " - "so it is best if you display Cemu with pure gamma and do not use this setting.")); - m_userDisplayisSRGB->Bind(wxEVT_CHECKBOX, &GeneralSettings2::OnUserDisplaySRGBSelected, this); - - srgbCheckBoxSizer->Add(m_userDisplayGamma, 0, wxALL, 5); - srgbCheckBoxSizer->Add(m_userDisplayisSRGB, 0, wxALL | wxALIGN_CENTER_VERTICAL, 5); - - row->Add(new wxStaticText(box, wxID_ANY, _("Override Gamma")), 0, wxALIGN_CENTER_VERTICAL | wxALL, 5); - m_overrideGamma = new wxCheckBox(box, wxID_ANY, "", wxDefaultPosition, {230, -1}); - m_overrideGamma->SetToolTip(_("Ignore title's gamma preference")); - m_overrideGamma->Bind(wxEVT_CHECKBOX, [](wxCommandEvent& event) { - GetConfig().overrideAppGammaPreference = event.IsChecked(); - }); - row->Add(m_overrideGamma, 0, wxALL, 5); - - box_sizer->Add(row, 0, wxEXPAND, 5); - graphics_panel_sizer->Add(box_sizer, 0, wxEXPAND | wxALL, 5); - } - - { - wxString choices[] = { _("Bilinear"), _("Bicubic"), _("Hermite"), _("Nearest Neighbor") }; - m_upscale_filter = new wxRadioBox(graphics_panel, wxID_ANY, _("Upscale filter"), wxDefaultPosition, wxDefaultSize, std::size(choices), choices, 5, wxRA_SPECIFY_COLS); - m_upscale_filter->SetToolTip(_("Upscaling filters are used when the game resolution is smaller than the window size")); - m_upscale_filter->Bind(wxEVT_RADIOBOX, [](wxCommandEvent& event) { - GetConfig().upscale_filter = event.GetSelection(); - }); - graphics_panel_sizer->Add(m_upscale_filter, 0, wxALL | wxEXPAND, 5); - - m_downscale_filter = new wxRadioBox(graphics_panel, wxID_ANY, _("Downscale filter"), wxDefaultPosition, wxDefaultSize, std::size(choices), choices, 5, wxRA_SPECIFY_COLS); - m_downscale_filter->SetToolTip(_("Downscaling filters are used when the game resolution is bigger than the window size")); - m_downscale_filter->Bind(wxEVT_RADIOBOX, [](wxCommandEvent& event) { - GetConfig().downscale_filter = event.GetSelection(); - }); - graphics_panel_sizer->Add(m_downscale_filter, 0, wxALL | wxEXPAND, 5); - } - - { - wxString choices[] = { _("Keep aspect ratio"), _("Stretch") }; - m_fullscreen_scaling = new wxRadioBox(graphics_panel, wxID_ANY, _("Fullscreen scaling"), wxDefaultPosition, wxDefaultSize, std::size(choices), choices, 5, wxRA_SPECIFY_COLS); - m_fullscreen_scaling->SetToolTip(_("Controls the output aspect ratio when it doesn't match the ratio of the game")); - m_fullscreen_scaling->Bind(wxEVT_RADIOBOX, [](wxCommandEvent& event) { - GetConfig().fullscreen_scaling = event.GetSelection(); - }); - graphics_panel_sizer->Add(m_fullscreen_scaling, 0, wxALL | wxEXPAND, 5); - } - - graphics_panel->SetSizerAndFit(graphics_panel_sizer); - return graphics_panel; -} - -wxPanel* GeneralSettings2::AddAudioPage(wxNotebook* notebook) -{ - auto audio_panel = new wxPanel(notebook, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL); - auto audio_panel_sizer = new wxBoxSizer(wxVERTICAL); - - { - auto box = new wxStaticBox(audio_panel, wxID_ANY, _("General")); - auto box_sizer = new wxStaticBoxSizer(box, wxVERTICAL); - - auto audio_general_row = new wxFlexGridSizer(0, 3, 0, 0); - audio_general_row->SetFlexibleDirection(wxBOTH); - audio_general_row->SetNonFlexibleGrowMode(wxFLEX_GROWMODE_SPECIFIED); - - audio_general_row->Add(new wxStaticText(box, wxID_ANY, _("API")), 0, wxALIGN_CENTER_VERTICAL | wxALL, 5); - - m_audio_api = new wxChoice(box, wxID_ANY); - if (IAudioAPI::IsAudioAPIAvailable(IAudioAPI::DirectSound)) - m_audio_api->Append(kDirectSound); - if (IAudioAPI::IsAudioAPIAvailable(IAudioAPI::XAudio27)) - m_audio_api->Append(kXAudio27); - if (IAudioAPI::IsAudioAPIAvailable(IAudioAPI::XAudio2)) - m_audio_api->Append(kXAudio2); - if (IAudioAPI::IsAudioAPIAvailable(IAudioAPI::Cubeb)) - m_audio_api->Append(kCubeb); - - m_audio_api->SetSelection(0); - m_audio_api->SetToolTip(_("Select one of the available audio back ends")); - audio_general_row->Add(m_audio_api, 0, wxALL, 5); - - m_audio_api->Bind(wxEVT_CHOICE, &GeneralSettings2::OnAudioAPISelected, this); - - audio_general_row->AddSpacer(0); - - audio_general_row->Add(new wxStaticText(box, wxID_ANY, _("Latency")), 0, wxALIGN_CENTER_VERTICAL | wxALL, 5); - m_audio_latency = new wxSlider(box, wxID_ANY, 2, 0, IAudioAPI::kBlockCount - 1, wxDefaultPosition, wxDefaultSize, wxSL_HORIZONTAL); - m_audio_latency->SetToolTip(_("Controls the amount of buffered audio data\nHigher values will create a delay in audio playback, but may avoid audio problems when emulation is too slow")); - audio_general_row->Add(m_audio_latency, 0, wxEXPAND | wxALL, 5); - auto latency_text = new wxStaticText(box, wxID_ANY, "24ms"); - audio_general_row->Add(latency_text, 0, wxALIGN_CENTER_VERTICAL | wxALL | wxALIGN_RIGHT, 5); - m_audio_latency->Bind(wxEVT_SLIDER, &GeneralSettings2::OnLatencySliderChanged, this, wxID_ANY, wxID_ANY, new wxControlObject(latency_text)); - m_audio_latency->Bind(wxEVT_SLIDER, &GeneralSettings2::OnAudioLatencyChanged, this); - - box_sizer->Add(audio_general_row, 1, wxEXPAND, 5); - audio_panel_sizer->Add(box_sizer, 0, wxEXPAND | wxALL, 5); - } - - const wxString audio_channel_choices[] = { _("Mono"), _("Stereo") , _("Surround") }; - { - auto box = new wxStaticBox(audio_panel, wxID_ANY, _("TV")); - auto box_sizer = new wxStaticBoxSizer(box, wxVERTICAL); - - auto audio_tv_row = new wxFlexGridSizer(0, 3, 0, 0); - audio_tv_row->SetFlexibleDirection(wxBOTH); - audio_tv_row->SetNonFlexibleGrowMode(wxFLEX_GROWMODE_SPECIFIED); - - audio_tv_row->Add(new wxStaticText(box, wxID_ANY, _("Device")), 0, wxALIGN_CENTER_VERTICAL | wxALL, 5); - m_tv_device = new wxChoice(box, wxID_ANY); - m_tv_device->SetMinSize(wxSize(300, -1)); - m_tv_device->SetToolTip(_("Select the active audio output device for Wii U TV")); - audio_tv_row->Add(m_tv_device, 0, wxEXPAND | wxALL, 5); - audio_tv_row->AddSpacer(0); - - m_tv_device->Bind(wxEVT_CHOICE, &GeneralSettings2::OnAudioDeviceSelected, this); - - audio_tv_row->Add(new wxStaticText(box, wxID_ANY, _("Channels")), 0, wxALIGN_CENTER_VERTICAL | wxALL, 5); - - m_tv_channels = new wxChoice(box, wxID_ANY, wxDefaultPosition, wxDefaultSize, std::size(audio_channel_choices), audio_channel_choices); - - m_tv_channels->SetSelection(1); // set default to stereo - m_tv_channels->Bind(wxEVT_CHOICE, &GeneralSettings2::OnAudioChannelsSelected, this); - audio_tv_row->Add(m_tv_channels, 0, wxEXPAND | wxALL, 5); - audio_tv_row->AddSpacer(0); - - audio_tv_row->Add(new wxStaticText(box, wxID_ANY, _("Volume")), 0, wxALIGN_CENTER_VERTICAL | wxALL, 5); - m_tv_volume = new wxSlider(box, wxID_ANY, 100, 0, 100, wxDefaultPosition, wxDefaultSize, wxSL_HORIZONTAL); - audio_tv_row->Add(m_tv_volume, 0, wxEXPAND | wxALL, 5); - auto audio_tv_volume_text = new wxStaticText(box, wxID_ANY, "100%"); - audio_tv_row->Add(audio_tv_volume_text, 0, wxALIGN_CENTER_VERTICAL | wxALL | wxALIGN_RIGHT, 5); - - m_tv_volume->Bind(wxEVT_SLIDER, &GeneralSettings2::OnSliderChangedPercent, this, wxID_ANY, wxID_ANY, new wxControlObject(audio_tv_volume_text)); - m_tv_volume->Bind(wxEVT_SLIDER, &GeneralSettings2::OnVolumeChanged, this); - - box_sizer->Add(audio_tv_row, 1, wxEXPAND, 5); - audio_panel_sizer->Add(box_sizer, 0, wxEXPAND | wxALL, 5); - } - - { - auto box = new wxStaticBox(audio_panel, wxID_ANY, _("Gamepad")); - auto box_sizer = new wxStaticBoxSizer(box, wxVERTICAL); - - auto audio_pad_row = new wxFlexGridSizer(0, 3, 0, 0); - audio_pad_row->SetFlexibleDirection(wxBOTH); - audio_pad_row->SetNonFlexibleGrowMode(wxFLEX_GROWMODE_SPECIFIED); - - audio_pad_row->Add(new wxStaticText(box, wxID_ANY, _("Device")), 0, wxALIGN_CENTER_VERTICAL | wxALL, 5); - m_pad_device = new wxChoice(box, wxID_ANY); - m_pad_device->SetMinSize(wxSize(300, -1)); - m_pad_device->SetToolTip(_("Select the active audio output device for Wii U GamePad")); - audio_pad_row->Add(m_pad_device, 0, wxEXPAND | wxALL, 5); - audio_pad_row->AddSpacer(0); - - m_pad_device->Bind(wxEVT_CHOICE, &GeneralSettings2::OnAudioDeviceSelected, this); - - const wxString audio_channel_drc_choices[] = { _("Stereo") }; // stereo for now only - - audio_pad_row->Add(new wxStaticText(box, wxID_ANY, _("Channels")), 0, wxALIGN_CENTER_VERTICAL | wxALL, 5); - m_pad_channels = new wxChoice(box, wxID_ANY, wxDefaultPosition, wxDefaultSize, std::size(audio_channel_drc_choices), audio_channel_drc_choices); - - m_pad_channels->SetSelection(0); // set default to stereo - - m_pad_channels->Bind(wxEVT_CHOICE, &GeneralSettings2::OnAudioChannelsSelected, this); - audio_pad_row->Add(m_pad_channels, 0, wxEXPAND | wxALL, 5); - audio_pad_row->AddSpacer(0); - - audio_pad_row->Add(new wxStaticText(box, wxID_ANY, _("Volume")), 0, wxALIGN_CENTER_VERTICAL | wxALL, 5); - m_pad_volume = new wxSlider(box, wxID_ANY, 100, 0, 100); - audio_pad_row->Add(m_pad_volume, 0, wxEXPAND | wxALL, 5); - auto audio_pad_volume_text = new wxStaticText(box, wxID_ANY, "100%"); - audio_pad_row->Add(audio_pad_volume_text, 0, wxALIGN_CENTER_VERTICAL | wxALL | wxALIGN_RIGHT, 5); - - m_pad_volume->Bind(wxEVT_SLIDER, &GeneralSettings2::OnSliderChangedPercent, this, wxID_ANY, wxID_ANY, new wxControlObject(audio_pad_volume_text)); - m_pad_volume->Bind(wxEVT_SLIDER, &GeneralSettings2::OnVolumeChanged, this); - - box_sizer->Add(audio_pad_row, 1, wxEXPAND, 5); - audio_panel_sizer->Add(box_sizer, 0, wxEXPAND | wxALL, 5); - } - - { - auto box = new wxStaticBox(audio_panel, wxID_ANY, _("Microphone (Experimental)")); - auto box_sizer = new wxStaticBoxSizer(box, wxVERTICAL); - - auto audio_input_row = new wxFlexGridSizer(0, 3, 0, 0); - audio_input_row->SetFlexibleDirection(wxBOTH); - audio_input_row->SetNonFlexibleGrowMode(wxFLEX_GROWMODE_SPECIFIED); - - audio_input_row->Add(new wxStaticText(box, wxID_ANY, _("Device")), 0, wxALIGN_CENTER_VERTICAL | wxALL, 5); - m_input_device = new wxChoice(box, wxID_ANY); - m_input_device->SetMinSize(wxSize(300, -1)); - m_input_device->SetToolTip(_("Select the active audio input device for Wii U GamePad")); - audio_input_row->Add(m_input_device, 0, wxEXPAND | wxALL, 5); - audio_input_row->AddSpacer(0); - - m_input_device->Bind(wxEVT_CHOICE, &GeneralSettings2::OnAudioDeviceSelected, this); - - const wxString audio_channel_drc_choices[] = { _("Mono") }; // mono for now only - - audio_input_row->Add(new wxStaticText(box, wxID_ANY, _("Channels")), 0, wxALIGN_CENTER_VERTICAL | wxALL, 5); - m_input_channels = new wxChoice(box, wxID_ANY, wxDefaultPosition, wxDefaultSize, std::size(audio_channel_drc_choices), audio_channel_drc_choices); - - m_input_channels->SetSelection(0); // set default to stereo - - m_input_channels->Bind(wxEVT_CHOICE, &GeneralSettings2::OnAudioChannelsSelected, this); - audio_input_row->Add(m_input_channels, 0, wxEXPAND | wxALL, 5); - audio_input_row->AddSpacer(0); - - audio_input_row->Add(new wxStaticText(box, wxID_ANY, _("Volume")), 0, wxALIGN_CENTER_VERTICAL | wxALL, 5); - m_input_volume = new wxSlider(box, wxID_ANY, 100, 0, 100); - audio_input_row->Add(m_input_volume, 0, wxEXPAND | wxALL, 5); - auto audio_input_volume_text = new wxStaticText(box, wxID_ANY, "100%"); - audio_input_row->Add(audio_input_volume_text, 0, wxALIGN_CENTER_VERTICAL | wxALL | wxALIGN_RIGHT, 5); - - m_input_volume->Bind(wxEVT_SLIDER, &GeneralSettings2::OnSliderChangedPercent, this, wxID_ANY, wxID_ANY, new wxControlObject(audio_input_volume_text)); - m_input_volume->Bind(wxEVT_SLIDER, &GeneralSettings2::OnVolumeChanged, this); - - box_sizer->Add(audio_input_row, 1, wxEXPAND, 5); - audio_panel_sizer->Add(box_sizer, 0, wxEXPAND | wxALL, 5); - } - - { - auto box = new wxStaticBox(audio_panel, wxID_ANY, _("Trap Team Portal")); - auto box_sizer = new wxStaticBoxSizer(box, wxVERTICAL); - - auto portal_audio_row = new wxFlexGridSizer(0, 3, 0, 0); - portal_audio_row->SetFlexibleDirection(wxBOTH); - portal_audio_row->SetNonFlexibleGrowMode(wxFLEX_GROWMODE_SPECIFIED); - - portal_audio_row->Add(new wxStaticText(box, wxID_ANY, _("Device")), 0, wxALIGN_CENTER_VERTICAL | wxALL, 5); - m_portal_device = new wxChoice(box, wxID_ANY, wxDefaultPosition); - m_portal_device->SetMinSize(wxSize(300, -1)); - m_portal_device->SetToolTip(_("Select the active audio output device for Wii U GamePad")); - portal_audio_row->Add(m_portal_device, 0, wxEXPAND | wxALL, 5); - portal_audio_row->AddSpacer(0); - - m_portal_device->Bind(wxEVT_CHOICE, &GeneralSettings2::OnAudioDeviceSelected, this); - - portal_audio_row->Add(new wxStaticText(box, wxID_ANY, _("Volume")), 0, wxALIGN_CENTER_VERTICAL | wxALL, 5); - m_portal_volume = new wxSlider(box, wxID_ANY, 100, 0, 100); - portal_audio_row->Add(m_portal_volume, 0, wxEXPAND | wxALL, 5); - auto audio_pad_volume_text = new wxStaticText(box, wxID_ANY, "100%"); - portal_audio_row->Add(audio_pad_volume_text, 0, wxALIGN_CENTER_VERTICAL | wxALL | wxALIGN_RIGHT, 5); - - m_portal_volume->Bind(wxEVT_SLIDER, &GeneralSettings2::OnSliderChangedPercent, this, wxID_ANY, wxID_ANY, new wxControlObject(audio_pad_volume_text)); - m_portal_volume->Bind(wxEVT_SLIDER, &GeneralSettings2::OnVolumeChanged, this); - - box_sizer->Add(portal_audio_row, 1, wxEXPAND, 5); - audio_panel_sizer->Add(box_sizer, 0, wxEXPAND | wxALL, 5); - } - - audio_panel->SetSizerAndFit(audio_panel_sizer); - return audio_panel; -} - -wxPanel* GeneralSettings2::AddOverlayPage(wxNotebook* notebook) -{ - auto* panel = new wxPanel(notebook, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL); - auto* panel_sizer = new wxBoxSizer(wxVERTICAL); - - const wxString positions[]{ _("Disabled"), _("Top left"), _("Top center"), _("Top right"), _("Bottom left"), _("Bottom center"), _("Bottom right") }; - const wxString text_scale[]{ "50%", "75%", "100%", "125%", "150%", "175%", "200%", "225%", "250%", "275%", "300%" }; - { - auto box = new wxStaticBox(panel, wxID_ANY, _("Overlay")); - auto box_sizer = new wxStaticBoxSizer(box, wxVERTICAL); - - auto position_row = new wxFlexGridSizer(1, 0, 0, 0); - position_row->SetFlexibleDirection(wxBOTH); - position_row->SetNonFlexibleGrowMode(wxFLEX_GROWMODE_SPECIFIED); - { - position_row->Add(new wxStaticText(box, wxID_ANY, _("Position")), 0, wxALL | wxALIGN_CENTER_VERTICAL, 5); - m_overlay_position = new wxChoice(box, wxID_ANY, wxDefaultPosition, wxDefaultSize, std::size(positions), positions); - m_overlay_position->SetSelection(0); - m_overlay_position->SetToolTip(_("Controls the overlay which displays technical information while playing")); - position_row->Add(m_overlay_position, 0, wxALL, 5); - - position_row->AddSpacer(25); - - position_row->Add(new wxStaticText(box, wxID_ANY, _("Text Color")), 0, wxALL | wxALIGN_CENTRE_VERTICAL, 5); - m_overlay_font_color = new wxColourPickerCtrl(box, wxID_ANY, *wxWHITE, wxDefaultPosition, wxDefaultSize, wxCLRP_SHOW_ALPHA); - m_overlay_font_color->SetToolTip(_("Sets the text color of the overlay")); - position_row->Add(m_overlay_font_color, 0, wxEXPAND | wxALL, 5); - - position_row->AddSpacer(25); - - position_row->Add(new wxStaticText(box, wxID_ANY, _("Scale")), 0, wxALL | wxALIGN_CENTRE_VERTICAL, 5); - m_overlay_scale = new wxChoice(box, wxID_ANY, wxDefaultPosition, wxDefaultSize, std::size(text_scale), text_scale); - m_overlay_scale->SetToolTip(_("Sets the scale of the overlay text")); - position_row->Add(m_overlay_scale, 0, wxEXPAND | wxALL, 5); - } - box_sizer->Add(position_row, 0, wxEXPAND, 5); - - auto settings2_row = new wxFlexGridSizer(0, 4, 2, 0); - { - m_overlay_fps = new wxCheckBox(box, wxID_ANY, _("FPS")); - m_overlay_fps->SetToolTip(_("The number of frames per second. Average over last 5 seconds")); - settings2_row->Add(m_overlay_fps, 0, wxALL, 5); - - m_overlay_drawcalls = new wxCheckBox(box, wxID_ANY, _("Draw calls per frame")); - m_overlay_drawcalls->SetToolTip(_("The number of draw calls per frame. Average over last 5 seconds")); - settings2_row->Add(m_overlay_drawcalls, 0, wxALL, 5); - - m_overlay_cpu = new wxCheckBox(box, wxID_ANY, _("CPU usage")); - m_overlay_cpu->SetToolTip(_("CPU usage of Cemu in percent")); - settings2_row->Add(m_overlay_cpu, 0, wxALL, 5); - - m_overlay_cpu_per_core = new wxCheckBox(box, wxID_ANY, _("CPU per core usage")); - m_overlay_cpu_per_core->SetToolTip(_("Total cpu usage in percent for each core")); - settings2_row->Add(m_overlay_cpu_per_core, 0, wxALL, 5); - - m_overlay_ram = new wxCheckBox(box, wxID_ANY, _("RAM usage")); - m_overlay_ram->SetToolTip(_("Cemu RAM usage in MB")); - settings2_row->Add(m_overlay_ram, 0, wxALL, 5); - - m_overlay_vram = new wxCheckBox(box, wxID_ANY, _("VRAM usage")); -#if BOOST_OS_WINDOWS - using RtlGetVersion_t = LONG(WINAPI*)(PRTL_OSVERSIONINFOW lpVersionInformation); - const auto pRtlGetVersion = (RtlGetVersion_t)GetProcAddress(GetModuleHandleA("ntdll.dll"), "RtlGetVersion"); - //if(IsWindows8Point1OrGreater()) requires manifest - RTL_OSVERSIONINFOW info{}; - // Windows 8.1 6.3* - if (pRtlGetVersion && pRtlGetVersion(&info) == 0 && ((info.dwMajorVersion == 6 && info.dwMinorVersion >= 3) || info.dwMajorVersion > 6)) - m_overlay_vram->SetToolTip(_("The VRAM usage of Cemu in MB")); - else - { - m_overlay_vram->SetToolTip(_("This option requires Win8.1+")); - m_overlay_vram->Disable(); - } -#else - m_overlay_vram->SetToolTip(_("The VRAM usage of Cemu in MB")); -#endif - - settings2_row->Add(m_overlay_vram, 0, wxALL, 5); - - m_overlay_debug = new wxCheckBox(box, wxID_ANY, _("Debug")); - m_overlay_debug->SetToolTip(_("Displays internal debug information (Vulkan only)")); - settings2_row->Add(m_overlay_debug, 0, wxALL, 5); - } - box_sizer->Add(settings2_row, 0, wxEXPAND, 5); - - panel_sizer->Add(box_sizer, 0, wxEXPAND | wxALL, 5); - } - - { - auto box = new wxStaticBox(panel, wxID_ANY, _("Notifications")); - auto box_sizer = new wxStaticBoxSizer(box, wxVERTICAL); - - auto position_row = new wxFlexGridSizer(1, 0, 0, 0); - position_row->SetFlexibleDirection(wxBOTH); - position_row->SetNonFlexibleGrowMode(wxFLEX_GROWMODE_SPECIFIED); - { - position_row->Add(new wxStaticText(box, wxID_ANY, _("Position")), 0, wxALL | wxALIGN_CENTER_VERTICAL, 5); - - m_notification_position = new wxChoice(box, wxID_ANY, wxDefaultPosition, wxDefaultSize, std::size(positions), positions); - m_notification_position->SetSelection(0); - m_notification_position->SetToolTip(_("Controls the notification position while playing")); - position_row->Add(m_notification_position, 0, wxALL, 5); - - position_row->AddSpacer(25); - - position_row->Add(new wxStaticText(box, wxID_ANY, _("Text Color")), 0, wxALL | wxALIGN_CENTRE_VERTICAL, 5); - m_notification_font_color = new wxColourPickerCtrl(box, wxID_ANY, *wxWHITE, wxDefaultPosition, wxDefaultSize, wxCLRP_SHOW_ALPHA); - m_notification_font_color->SetToolTip(_("Sets the text color of notifications")); - position_row->Add(m_notification_font_color, 0, wxEXPAND | wxALL, 5); - - position_row->Add(new wxStaticText(box, wxID_ANY, _("Scale")), 0, wxALL | wxALIGN_CENTRE_VERTICAL, 5); - m_notification_scale = new wxChoice(box, wxID_ANY, wxDefaultPosition, wxDefaultSize, std::size(text_scale), text_scale); - m_notification_scale->SetToolTip(_("Sets the scale of the notification text")); - position_row->Add(m_notification_scale, 0, wxEXPAND | wxALL, 5); - } - box_sizer->Add(position_row, 0, wxEXPAND, 5); - - auto settings1_row = new wxFlexGridSizer(1, 0, 2, 0); - { - m_controller_profile_name = new wxCheckBox(box, wxID_ANY, _("Controller profiles")); - m_controller_profile_name->SetToolTip(_("Displays the active controller profile when starting a game")); - settings1_row->Add(m_controller_profile_name, 0, wxALL, 5); - - m_controller_low_battery = new wxCheckBox(box, wxID_ANY, _("Low battery")); - m_controller_low_battery->SetToolTip(_("Shows a notification when a low controller battery has been detected")); - settings1_row->Add(m_controller_low_battery, 0, wxALL, 5); - - m_shader_compiling = new wxCheckBox(box, wxID_ANY, _("Shader compiler")); - m_shader_compiling->SetToolTip(_("Shows a notification after shaders have been compiled")); - settings1_row->Add(m_shader_compiling, 0, wxALL, 5); - - m_friends_data = new wxCheckBox(box, wxID_ANY, _("Friend list")); - m_friends_data->SetToolTip(_("Shows friend list related data if online")); - settings1_row->Add(m_friends_data, 0, wxALL, 5); - } - box_sizer->Add(settings1_row, 0, wxEXPAND, 5); - - - panel_sizer->Add(box_sizer, 0, wxEXPAND | wxALL, 5); - } - - panel->SetSizerAndFit(panel_sizer); - - return panel; -} - -wxPanel* GeneralSettings2::AddAccountPage(wxNotebook* notebook) -{ - auto* online_panel = new wxPanel(notebook); - auto* online_panel_sizer = new wxBoxSizer(wxVERTICAL); - - { - auto* box = new wxStaticBox(online_panel, wxID_ANY, _("Account settings")); - auto* box_sizer = new wxStaticBoxSizer(box, wxVERTICAL); - - auto* content = new wxFlexGridSizer(0, 4, 0, 0); - content->SetFlexibleDirection(wxBOTH); - content->SetNonFlexibleGrowMode(wxFLEX_GROWMODE_SPECIFIED); - content->AddGrowableCol(1, 1); - content->AddGrowableCol(2, 0); - content->AddGrowableCol(3, 0); - - content->Add(new wxStaticText(box, wxID_ANY, _("Active account")), 1, wxALIGN_CENTER_VERTICAL | wxALL, 5); - - m_active_account = new wxChoice(box, wxID_ANY); - m_active_account->SetMinSize({ 250, -1 }); - content->Add(m_active_account, 0, wxEXPAND | wxALL, 5); - m_active_account->Bind(wxEVT_CHOICE, &GeneralSettings2::OnActiveAccountChanged, this); - - m_create_account = new wxButton(box, wxID_ANY, _("Create")); - content->Add(m_create_account, 0, wxEXPAND | wxALL | wxALIGN_RIGHT, 5); - m_create_account->Bind(wxEVT_BUTTON, &GeneralSettings2::OnAccountCreate, this); - - m_delete_account = new wxButton(box, wxID_ANY, _("Delete")); - content->Add(m_delete_account, 0, wxEXPAND | wxALL | wxALIGN_RIGHT, 5); - m_delete_account->Bind(wxEVT_BUTTON, &GeneralSettings2::OnAccountDelete, this); - - box_sizer->Add(content, 1, wxEXPAND, 5); - - online_panel_sizer->Add(box_sizer, 0, wxEXPAND | wxALL, 5); - - if (CafeSystem::IsTitleRunning()) - { - m_active_account->Enable(false); - m_create_account->Enable(false); - m_delete_account->Enable(false); - } - } - - - { - wxString choices[] = { _("Offline"), _("Nintendo"), _("Pretendo"), _("Custom") }; - m_active_service = new wxRadioBox(online_panel, wxID_ANY, _("Network Service"), wxDefaultPosition, wxDefaultSize, std::size(choices), choices, 4, wxRA_SPECIFY_COLS); - if (!NetworkConfig::XMLExists()) - m_active_service->Enable(3, false); - - m_active_service->SetItemToolTip(0, _("Online functionality disabled for this account")); - m_active_service->SetItemToolTip(1, _("Connect to the official Nintendo Network Service")); - m_active_service->SetItemToolTip(2, _("Connect to the Pretendo Network Service")); - m_active_service->SetItemToolTip(3, _("Connect to a custom Network Service (configured via network_services.xml)")); - - m_active_service->Bind(wxEVT_RADIOBOX, &GeneralSettings2::OnAccountServiceChanged,this); - online_panel_sizer->Add(m_active_service, 0, wxEXPAND | wxALL, 5); - - if (CafeSystem::IsTitleRunning()) - { - m_active_service->Enable(false); - } - } - - { - auto* box = new wxStaticBox(online_panel, wxID_ANY, _("Online play requirements")); - auto* box_sizer = new wxStaticBoxSizer(box, wxVERTICAL); - - auto* row = new wxFlexGridSizer(0, 2, 0, 0); - row->SetFlexibleDirection(wxBOTH); - row->SetNonFlexibleGrowMode(wxFLEX_GROWMODE_SPECIFIED); - - const wxImage tmp = wxBITMAP_PNG_FROM_DATA(PNG_ERROR).ConvertToImage(); - m_validate_online = new wxBitmapButton(box, wxID_ANY, tmp.Scale(16, 16)); - m_validate_online->Bind(wxEVT_BUTTON, &GeneralSettings2::OnShowOnlineValidator, this); - row->Add(m_validate_online, 0, wxEXPAND | wxALL, 5); - - m_online_status = new wxStaticText(box, wxID_ANY, _("No account selected")); - row->Add(m_online_status, 1, wxALL | wxALIGN_CENTRE_VERTICAL, 5); - - box_sizer->Add(row, 1, wxEXPAND, 5); - - auto* tutorial_link = new wxHyperlinkCtrl(box, wxID_ANY, _("Online play tutorial"), "https://cemu.info/online-guide"); - box_sizer->Add(tutorial_link, 0, wxALL, 5); - - - online_panel_sizer->Add(box_sizer, 0, wxEXPAND | wxALL, 5); - } - - { - m_account_information = new wxCollapsiblePane(online_panel, wxID_ANY, _("Account information")); - auto win = m_account_information->GetPane(); - - auto content = new wxBoxSizer(wxVERTICAL); - - m_account_grid = new wxPropertyGrid(win, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxPG_HIDE_MARGIN | wxPG_STATIC_SPLITTER); - m_account_grid->SetExtraStyle(wxPG_EX_HELP_AS_TOOLTIPS); - m_account_grid->SetMinSize({ 300, -1 }); - //m_account_grid->Append(new wxPropertyCategory("Main")); - - auto* persistent_id_gprop = m_account_grid->Append(new wxStringProperty("PersistentId", kPropertyPersistentId)); - persistent_id_gprop->SetHelpString(_("The persistent id is the internal folder name used for your saves")); - m_account_grid->SetPropertyReadOnly(persistent_id_gprop); - - m_account_grid->Append(new wxStringProperty(_("Mii name"), kPropertyMiiName))->SetHelpString(_("The mii name is the profile name")); - m_account_grid->Append(new wxStringProperty(_("Birthday"), kPropertyBirthday)); - - wxPGChoices gender; - gender.Add(_("Female"), 0); - gender.Add(_("Male"), 1); - m_account_grid->Append(new wxEnumProperty(_("Gender"), kPropertyGender, gender)); - - m_account_grid->Append(new wxStringProperty(_("Email"), kPropertyEmail)); - - wxPGChoices countries; - for (int i = 0; i < NCrypto::GetCountryCount(); ++i) - { - const auto country = NCrypto::GetCountryAsString(i); - if (country && (i == 0 || !boost::equals(country, "NN"))) - { - countries.Add(country, i); - } - } - m_account_grid->Append(new wxEnumProperty(_("Country"), kPropertyCountry, countries)); - - m_account_grid->Bind(wxEVT_PG_CHANGED, &GeneralSettings2::OnAccountSettingsChanged, this); - - content->Add(m_account_grid, 1, wxEXPAND | wxALL, 5); - - win->SetSizer(content); - content->SetSizeHints(win); - - online_panel_sizer->Add(m_account_information, 0, wxEXPAND | wxALL, 5); - } - - online_panel->SetSizerAndFit(online_panel_sizer); - return online_panel; -} - -wxPanel* GeneralSettings2::AddDebugPage(wxNotebook* notebook) -{ - auto* panel = new wxPanel(notebook, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL); - auto* debug_panel_sizer = new wxBoxSizer(wxVERTICAL); - - { - auto* debug_row = new wxFlexGridSizer(0, 2, 0, 0); - debug_row->SetFlexibleDirection(wxBOTH); - debug_row->SetNonFlexibleGrowMode(wxFLEX_GROWMODE_SPECIFIED); - - debug_row->Add(new wxStaticText(panel, wxID_ANY, _("Crash dump")), 0, wxALIGN_CENTER_VERTICAL | wxALL, 5); - -#if BOOST_OS_WINDOWS - wxString dump_choices[] = {_("Disabled"), _("Lite"), _("Full")}; -#elif BOOST_OS_UNIX - wxString dump_choices[] = {_("Disabled"), _("Enabled")}; -#endif - m_crash_dump = new wxChoice(panel, wxID_ANY, wxDefaultPosition, wxDefaultSize, std::size(dump_choices), dump_choices); - m_crash_dump->SetSelection(0); -#if BOOST_OS_WINDOWS - m_crash_dump->SetToolTip(_("Creates a dump when Cemu crashes\nOnly enable when requested by a developer!\nThe Full option will create a very large dump file (includes a full RAM dump of the Cemu process)")); -#elif BOOST_OS_UNIX - m_crash_dump->SetToolTip(_("Creates a core dump when Cemu crashes\nOnly enable when requested by a developer!")); -#endif - debug_row->Add(m_crash_dump, 0, wxALL | wxEXPAND, 5); - debug_panel_sizer->Add(debug_row, 0, wxALL | wxEXPAND, 5); - } - - { - auto* debug_row = new wxFlexGridSizer(0, 2, 0, 0); - debug_row->SetFlexibleDirection(wxBOTH); - debug_row->SetNonFlexibleGrowMode(wxFLEX_GROWMODE_SPECIFIED); - - debug_row->Add(new wxStaticText(panel, wxID_ANY, _("GDB Stub port")), 0, wxALIGN_CENTER_VERTICAL | wxALL, 5); - - m_gdb_port = new wxSpinCtrl(panel, wxID_ANY, "1337", wxDefaultPosition, wxDefaultSize, 0, 1000, 65535); - m_gdb_port->SetToolTip(_("Changes the port that the GDB stub will use, which you can use by either starting Cemu with the --enable-gdbstub option or by enabling it the Debug tab.")); - - debug_row->Add(m_gdb_port, 0, wxALL | wxEXPAND, 5); - debug_panel_sizer->Add(debug_row, 0, wxALL | wxEXPAND, 5); - } - -#ifdef ENABLE_METAL - { - auto* debug_row = new wxFlexGridSizer(0, 2, 0, 0); - debug_row->SetFlexibleDirection(wxBOTH); - debug_row->SetNonFlexibleGrowMode(wxFLEX_GROWMODE_SPECIFIED); - - debug_row->Add(new wxStaticText(panel, wxID_ANY, _("GPU capture save directory"), wxDefaultPosition, wxDefaultSize, 0), 0, wxALIGN_CENTER_VERTICAL | wxALL, 5); - - m_gpu_capture_dir = new wxTextCtrl(panel, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxTE_DONTWRAP); - m_gpu_capture_dir->SetMinSize(wxSize(150, -1)); - m_gpu_capture_dir->SetToolTip(_("Cemu will save the GPU captures done by selecting Debug -> GPU capture (Metal) in the menu bar in this directory. If a debugger with support for GPU captures (like Xcode) is attached, the capture will be opened in that debugger instead. If such debugger is not attached, METAL_CAPTURE_ENABLED must be set to 1 as an environment variable.")); - - debug_row->Add(m_gpu_capture_dir, 0, wxALL | wxEXPAND, 5); - debug_panel_sizer->Add(debug_row, 0, wxALL | wxEXPAND, 5); - } - - { - auto* debug_row = new wxFlexGridSizer(0, 2, 0, 0); - debug_row->SetFlexibleDirection(wxBOTH); - debug_row->SetNonFlexibleGrowMode(wxFLEX_GROWMODE_SPECIFIED); - - m_framebuffer_fetch = new wxCheckBox(panel, wxID_ANY, _("Framebuffer fetch")); - m_framebuffer_fetch->SetToolTip(_("Enable framebuffer fetch for eligible textures on supported devices.\nMetal only")); - - debug_row->Add(m_framebuffer_fetch, 0, wxALL | wxEXPAND, 5); - debug_panel_sizer->Add(debug_row, 0, wxALL | wxEXPAND, 5); - } -#endif - - panel->SetSizerAndFit(debug_panel_sizer); - - return panel; -} - -GeneralSettings2::GeneralSettings2(wxWindow* parent, bool game_launched) - : wxDialog(parent, wxID_ANY, _("General settings"), wxDefaultPosition, wxDefaultSize, wxCLOSE_BOX | wxCLIP_CHILDREN | wxCAPTION | wxRESIZE_BORDER), m_game_launched(game_launched) -{ - SetIcon(wxICON(X_SETTINGS)); - - auto* sizer = new wxBoxSizer(wxVERTICAL); - auto* notebook = new wxNotebook(this, wxID_ANY); - - notebook->AddPage(AddGeneralPage(notebook), _("General")); - notebook->AddPage(AddGraphicsPage(notebook), _("Graphics")); - notebook->AddPage(AddAudioPage(notebook), _("Audio")); - notebook->AddPage(AddOverlayPage(notebook), _("Overlay")); - notebook->AddPage(AddAccountPage(notebook), _("Account")); - notebook->AddPage(AddDebugPage(notebook), _("Debug")); - - Bind(wxEVT_CLOSE_WINDOW, &GeneralSettings2::OnClose, this); - - // - - sizer->Add(notebook, 1, wxEXPAND | wxALL, 5); - - SetSizerAndFit(sizer); - Layout(); - Centre(wxBOTH); - - // - - UpdateOnlineAccounts(); - UpdateAudioDeviceList(); - - ApplyConfig(); - HandleGraphicsApiSelection(); - - DisableSettings(game_launched); -} - -uint32 GeneralSettings2::GetSelectedAccountPersistentId() -{ - const auto active_account = m_active_account->GetSelection(); - if (active_account == wxNOT_FOUND) - return GetConfig().account.m_persistent_id.GetInitValue(); - return dynamic_cast(m_active_account->GetClientObject(active_account))->GetAccount().GetPersistentId(); -} - -void GeneralSettings2::StoreConfig() -{ - auto& app = wxGetApp(); - auto& config = GetConfig(); - auto& wxGuiConfig = GetWxGUIConfig(); - - wxGuiConfig.use_discord_presence = m_discord_presence->IsChecked(); - wxGuiConfig.fullscreen_menubar = m_fullscreen_menubar->IsChecked(); - wxGuiConfig.check_update = m_auto_update->IsChecked(); - wxGuiConfig.save_screenshot = m_save_screenshot->IsChecked(); - wxGuiConfig.receive_untested_updates = m_receive_untested_releases->IsChecked(); -#if BOOST_OS_LINUX && defined(ENABLE_FERAL_GAMEMODE) - wxGuiConfig.feral_gamemode = m_feral_gamemode->IsChecked(); -#endif -#if BOOST_OS_WINDOWS - wxGuiConfig.msw_theme = m_msw_theme->GetSelection(); -#endif - config.play_boot_sound = m_play_boot_sound->IsChecked(); - config.disable_screensaver = m_disable_screensaver->IsChecked(); - // toggle while a game is running - if (CafeSystem::IsTitleRunning()) - { - ScreenSaver::SetInhibit(config.disable_screensaver); - } - - // -1 is default wx widget value -> set to dummy 0 so mainwindow and padwindow will update it - wxGuiConfig.window_position = m_save_window_position_size->IsChecked() ? Vector2i{ 0,0 } : Vector2i{-1,-1}; - wxGuiConfig.window_size = m_save_window_position_size->IsChecked() ? Vector2i{ 0,0 } : Vector2i{-1,-1}; - wxGuiConfig.pad_position = m_save_padwindow_position_size->IsChecked() ? Vector2i{ 0,0 } : Vector2i{-1,-1}; - wxGuiConfig.pad_size = m_save_padwindow_position_size->IsChecked() ? Vector2i{ 0,0 } : Vector2i{-1,-1}; - - config.game_paths.clear(); - for (auto& path : m_game_paths->GetStrings()) - config.game_paths.emplace_back(path.utf8_string()); - - auto selection = m_language->GetSelection(); - if (selection == 0) - wxGuiConfig.language = wxLANGUAGE_DEFAULT; - else - { - const auto language = m_language->GetStringSelection(); - for (const auto& lang : app.GetLanguages()) - { - if (lang->DescriptionNative == language) - { - wxGuiConfig.language = lang->Language; - break; - } - } - } - - // audio - if (m_audio_api->GetStringSelection() == kDirectSound) - config.audio_api = IAudioAPI::DirectSound; - else if (m_audio_api->GetStringSelection() == kXAudio27) - config.audio_api = IAudioAPI::XAudio27; - else if (m_audio_api->GetStringSelection() == kXAudio2) - config.audio_api = IAudioAPI::XAudio2; - else if (m_audio_api->GetStringSelection() == kCubeb) - config.audio_api = IAudioAPI::Cubeb; - - config.audio_delay = m_audio_latency->GetValue(); - config.tv_channels = (AudioChannels)m_tv_channels->GetSelection(); - //config.pad_channels = (AudioChannels)m_pad_channels->GetSelection(); - config.pad_channels = kStereo; // (AudioChannels)m_pad_channels->GetSelection(); - //config.input_channels = (AudioChannels)m_input_channels->GetSelection(); - config.input_channels = kMono; // (AudioChannels)m_input_channels->GetSelection(); - - config.tv_volume = m_tv_volume->GetValue(); - config.pad_volume = m_pad_volume->GetValue(); - config.input_volume = m_input_volume->GetValue(); - config.portal_volume = m_portal_volume->GetValue(); - - config.tv_device.clear(); - const auto tv_device = m_tv_device->GetSelection(); - if (tv_device != wxNOT_FOUND && tv_device != 0 && m_tv_device->HasClientObjectData()) - { - const auto* device_description = (wxDeviceDescription*)m_tv_device->GetClientObject(tv_device); - if(device_description) - config.tv_device = device_description->GetDescription()->GetIdentifier(); - } - - config.pad_device.clear(); - const auto pad_device = m_pad_device->GetSelection(); - if (pad_device != wxNOT_FOUND && pad_device != 0 && m_pad_device->HasClientObjectData()) - { - const auto* device_description = (wxDeviceDescription*)m_pad_device->GetClientObject(pad_device); - if (device_description) - config.pad_device = device_description->GetDescription()->GetIdentifier(); - } - - config.input_device = L""; - const auto input_device = m_input_device->GetSelection(); - if (input_device != wxNOT_FOUND && input_device != 0 && m_input_device->HasClientObjectData()) - { - const auto* device_description = (wxDeviceDescription*)m_input_device->GetClientObject(input_device); - if (device_description) - config.input_device = device_description->GetDescription()->GetIdentifier(); - } - - config.portal_device.clear(); - const auto portal_device = m_portal_device->GetSelection(); - if (portal_device != wxNOT_FOUND && portal_device != 0 && m_portal_device->HasClientObjectData()) - { - const auto* device_description = (wxDeviceDescription*)m_portal_device->GetClientObject(portal_device); - if (device_description) - config.portal_device = device_description->GetDescription()->GetIdentifier(); - } - - // graphics - config.graphic_api = m_api_map[m_graphic_api->GetSelection()]; - - if (!CafeSystem::IsTitleRunning()) - { - selection = m_graphic_device->GetSelection(); -#ifdef ENABLE_VULKAN - if (config.graphic_api == GraphicAPI::kVulkan) - { - if (selection != wxNOT_FOUND) - { - const auto* info = (wxVulkanUUID*)m_graphic_device->GetClientObject(selection); - if (info) - config.vk_graphic_device_uuid = info->GetDeviceInfo().uuid; - else - config.vk_graphic_device_uuid = {}; - } - else - config.vk_graphic_device_uuid = {}; - } -#endif -#ifdef ENABLE_METAL - if (config.graphic_api == GraphicAPI::kMetal) - { - if (selection != wxNOT_FOUND) - { - const auto* info = (wxMetalUUID*)m_graphic_device->GetClientObject(selection); - if (info) - config.mtl_graphic_device_uuid = info->GetDeviceInfo().uuid; - else - config.mtl_graphic_device_uuid = {}; - } - else - config.mtl_graphic_device_uuid = {}; - } -#endif - } - - - config.gx2drawdone_sync = m_gx2drawdone_sync->IsChecked(); -#ifdef ENABLE_METAL - config.force_mesh_shaders = m_force_mesh_shaders->IsChecked(); -#endif - config.async_compile = m_async_compile->IsChecked(); - - config.overlay.position = (ScreenPosition)m_overlay_position->GetSelection(); wxASSERT((int)config.overlay.position <= (int)ScreenPosition::kBottomRight); - config.overlay.text_color = m_overlay_font_color->GetColour().GetRGBA(); - config.overlay.text_scale = m_overlay_scale->GetSelection() * 25 + 50; - - config.overlay.fps = m_overlay_fps->GetValue(); - config.overlay.drawcalls = m_overlay_drawcalls->GetValue(); - config.overlay.cpu_usage = m_overlay_cpu->GetValue(); - config.overlay.cpu_per_core_usage = m_overlay_cpu_per_core->GetValue(); - config.overlay.ram_usage = m_overlay_ram->GetValue(); - config.overlay.vram_usage = m_overlay_vram->GetValue(); - config.overlay.debug = m_overlay_debug->GetValue(); - - config.notification.position = (ScreenPosition)m_notification_position->GetSelection(); wxASSERT((int)config.notification.position <= (int)ScreenPosition::kBottomRight); - config.notification.text_color = m_notification_font_color->GetColour().GetRGBA(); - config.notification.text_scale = m_notification_scale->GetSelection() * 25 + 50; - config.notification.controller_profiles = m_controller_profile_name->GetValue(); - config.notification.controller_battery = m_controller_low_battery->GetValue(); - config.notification.shader_compiling = m_shader_compiling->GetValue(); - config.notification.friends = m_friends_data->GetValue(); - - // account - config.account.m_persistent_id = GetSelectedAccountPersistentId(); - - // debug - config.crash_dump = (CrashDump)m_crash_dump->GetSelection(); - config.gdb_port = m_gdb_port->GetValue(); -#ifdef ENABLE_METAL - config.gpu_capture_dir = m_gpu_capture_dir->GetValue().utf8_string(); - config.framebuffer_fetch = m_framebuffer_fetch->IsChecked(); -#endif - - GetConfigHandle().Save(); -} - -GeneralSettings2::~GeneralSettings2() -{ - Unbind(wxEVT_CLOSE_WINDOW, &GeneralSettings2::OnClose, this); -} - -void GeneralSettings2::OnClose(wxCloseEvent& event) -{ - StoreConfig(); - if (m_has_account_change) - { - wxCommandEvent refresh_event(wxEVT_ACCOUNTLIST_REFRESH); - GetParent()->ProcessWindowEvent(refresh_event); - } - event.Skip(); -} - -void GeneralSettings2::ValidateConfig() -{ - GetConfigHandle().Load(); - - auto& data = GetConfigHandle().data(); - // todo - //data.fullscreen_scaling = min(max(data.fullscreen_scaling,)) -} - -void GeneralSettings2::DisableSettings(bool game_launched) -{ - if (game_launched) - { - m_graphic_api->Disable(); - m_graphic_api->SetToolTip(_("Graphics API cannot be changed while a title is running")); - m_graphic_device->Disable(); - m_graphic_device->SetToolTip(_("Graphics Device cannot be changed while a title is running")); - } -} - -void GeneralSettings2::OnAudioLatencyChanged(wxCommandEvent& event) -{ - IAudioAPI::s_audioDelay = event.GetInt(); - event.Skip(); -} - -void GeneralSettings2::OnVolumeChanged(wxCommandEvent& event) -{ - - if(event.GetEventObject() == m_input_volume) - { - std::shared_lock lock(g_audioInputMutex); - if (g_inputAudio) - g_inputAudio->SetVolume(event.GetInt()); - } - else - { - std::shared_lock lock(g_audioMutex); - if(event.GetEventObject() == m_pad_volume) - { - if (g_padAudio) - g_padAudio->SetVolume(event.GetInt()); - - g_padVolume = event.GetInt(); - } - else if (event.GetEventObject() == m_tv_volume) - { - if (g_tvAudio) - g_tvAudio->SetVolume(event.GetInt()); - } - else - { - if(g_portalAudio) - g_portalAudio->SetVolume(event.GetInt()); - } - } - - - event.Skip(); -} - -void GeneralSettings2::OnInputVolumeChanged(wxCommandEvent& event) -{ - std::shared_lock lock(g_audioMutex); - if (g_padAudio) - g_padAudio->SetInputVolume(event.GetInt()); - - event.Skip(); -} - -void GeneralSettings2::OnSliderChangedPercent(wxCommandEvent& event) -{ - const auto slider = dynamic_cast(event.GetEventObject()); - wxASSERT(slider); - - const auto control = dynamic_cast(event.GetEventUserData()); - wxASSERT(control); - - auto slider_text = control->GetControl(); - wxASSERT(slider_text); - - const auto value = event.GetInt(); - slider->SetValue(value); - slider_text->SetLabel(wxString::Format("%d%%", value)); - - event.Skip(); -} - -void GeneralSettings2::OnLatencySliderChanged(wxCommandEvent& event) -{ - const auto slider = dynamic_cast(event.GetEventObject()); - wxASSERT(slider); - - const auto control = dynamic_cast(event.GetEventUserData()); - wxASSERT(control); - - auto slider_text = control->GetControl(); - wxASSERT(slider_text); - - const auto value = event.GetInt(); - slider->SetValue(value); - slider_text->SetLabel(wxString::Format("%dms", value * 12)); - - event.Skip(); -} - -void GeneralSettings2::UpdateAudioDeviceList() -{ - m_tv_device->Clear(); - m_pad_device->Clear(); - m_input_device->Clear(); - m_portal_device->Clear(); - - m_tv_device->Append(_("Disabled")); - m_pad_device->Append(_("Disabled")); - m_input_device->Append(_("Disabled")); - m_portal_device->Append(_("Disabled")); - - const auto audio_api = (IAudioAPI::AudioAPI)GetConfig().audio_api; - const auto devices = IAudioAPI::GetDevices(audio_api); - for (auto& device : devices) - { - m_tv_device->Append(device->GetName(), new wxDeviceDescription(device)); - m_pad_device->Append(device->GetName(), new wxDeviceDescription(device)); - m_portal_device->Append(device->GetName(), new wxDeviceDescription(device)); - } - - const auto input_audio_api = IAudioInputAPI::Cubeb; //(IAudioAPI::AudioAPI)GetConfig().input_audio_api; - const auto input_devices = IAudioInputAPI::GetDevices(input_audio_api); - - for (auto& device : input_devices) - { - m_input_device->Append(device->GetName(), new wxInputDeviceDescription(device)); - } - - if(m_tv_device->GetCount() > 1) - m_tv_device->SetSelection(1); - else - m_tv_device->SetSelection(0); - - m_pad_device->SetSelection(0); - - m_input_device->SetSelection(0); - - m_portal_device->SetSelection(0); - - // todo reset global instance of audio device -} - -void GeneralSettings2::ResetAccountInformation() -{ - m_account_grid->SetSplitterPosition(100); - m_active_account->SetSelection(0); - - for(auto it = m_account_grid->GetIterator(); !it.AtEnd(); ++it) - { - (*it)->SetValueToUnspecified(); - } - - // refresh pane size - m_account_information->InvalidateBestSize(); - #if BOOST_OS_WINDOWS - m_account_information->OnStateChange(GetBestSize()); - #endif -} - -void GeneralSettings2::OnAccountCreate(wxCommandEvent& event) -{ - wxASSERT(Account::HasFreeAccountSlots()); - - wxCreateAccountDialog dialog(this); - if (dialog.ShowModal() == wxID_CANCEL) - return; - - Account account(dialog.GetPersistentId(), dialog.GetMiiName().ToStdWstring()); - account.Save(); - Account::RefreshAccounts(); - - const int index = m_active_account->Append(account.ToString(), new wxAccountData(account)); - - // update ui - m_active_account->SetSelection(index); - UpdateAccountInformation(); - - m_create_account->Enable(m_active_account->GetCount() < 0xC); - m_delete_account->Enable(m_active_account->GetCount() > 1); - - // send main window event - wxASSERT(GetParent()); - wxCommandEvent refresh_event(wxEVT_ACCOUNTLIST_REFRESH); - GetParent()->ProcessWindowEvent(refresh_event); -} - -void GeneralSettings2::OnAccountDelete(wxCommandEvent& event) -{ - if(m_active_account->GetCount() == 1) - { - wxMessageBox(_("Can't delete the only account!"), _("Error"), wxOK | wxCENTRE | wxICON_ERROR, this); - return; - } - - const auto selection = m_active_account->GetSelection(); - wxASSERT(selection != wxNOT_FOUND); - auto* obj = dynamic_cast(m_active_account->GetClientObject(selection)); - wxASSERT(obj); - auto& account = obj->GetAccount(); - - const std::wstring format_str = _("Are you sure you want to delete the account {} with id {:x}?").ToStdWstring(); - const std::wstring msg = fmt::format(fmt::runtime(format_str), - std::wstring{ account.GetMiiName() }, account.GetPersistentId()); - - const int answer = wxMessageBox(msg, _("Confirmation"), wxYES_NO | wxCENTRE | wxICON_QUESTION, this); - if (answer == wxNO) - return; - - // todo: ask if saves should be deleted too? - - const fs::path path = account.GetFileName(); - try - { - fs::remove_all(path.parent_path()); - m_active_account->Delete(selection); - m_active_account->SetSelection(0); - Account::RefreshAccounts(); - UpdateAccountInformation(); - - m_create_account->Enable(m_active_account->GetCount() < 0xC); - m_delete_account->Enable(m_active_account->GetCount() > 1); - } - catch(const std::exception& ex) - { - SystemException sys(ex); - cemuLog_log(LogType::Force, sys.what()); - } - -} - -void GeneralSettings2::OnAccountSettingsChanged(wxPropertyGridEvent& event) -{ - wxPGProperty* property = event.GetProperty(); - if (!property) - return; - - const wxAny value = property->GetValue(); - if (value.IsNull()) - return; - - const auto selection = m_active_account->GetSelection(); - wxASSERT(selection != wxNOT_FOUND); - auto* obj = dynamic_cast(m_active_account->GetClientObject(selection)); - wxASSERT(obj); - auto& account = obj->GetAccount(); - - // TODO make id changeable to free ids + current it? - bool refresh_accounts = false; - if (property->GetName() == kPropertyMiiName) - { - std::wstring new_name = value.As().ToStdWstring(); - if (new_name.empty()) - new_name = L"default"; - - account.SetMiiName(new_name); - refresh_accounts = true; - } - else if (property->GetName() == kPropertyBirthday) - { - const std::string birthday = value.As().ToStdString(); - const boost::char_separator sep{ "-" }; - - std::vector tokens; - for (const auto& token : boost::tokenizer(birthday, sep)) - { - tokens.emplace_back(token); - } - - if (tokens.size() == 3) - { - account.SetBirthYear(ConvertString(tokens[0])); - account.SetBirthMonth(ConvertString(tokens[1])); - account.SetBirthDay(ConvertString(tokens[2])); - } - } - else if (property->GetName() == kPropertyGender) - { - account.SetGender(value.As()); - } - else if (property->GetName() == kPropertyEmail) - { - account.SetEmail(value.As().ToStdString()); - - } - else if (property->GetName() == kPropertyCountry) - { - account.SetCountry(value.As()); - } - else - cemu_assert_debug(false); - - account.Save(); - Account::RefreshAccounts(); // refresh internal account list - UpdateAccountInformation(); // refresh on invalid values - - if(refresh_accounts) - { - wxCommandEvent refresh_event(wxEVT_ACCOUNTLIST_REFRESH); - GetParent()->ProcessWindowEvent(refresh_event); - } -} - -void GeneralSettings2::UpdateAccountInformation() -{ - m_account_grid->SetSplitterPosition(100); - - const auto selection = m_active_account->GetSelection(); - if(selection == wxNOT_FOUND) - { - m_validate_online->SetBitmap(wxBITMAP_PNG_FROM_DATA(PNG_ERROR).ConvertToImage().Scale(16, 16)); - m_validate_online->SetWindowStyleFlag(m_validate_online->GetWindowStyleFlag() & ~wxBORDER_NONE); - ResetAccountInformation(); - m_online_status->SetLabel(_("No account selected")); - return; - } - - const auto* obj = dynamic_cast(m_active_account->GetClientObject(selection)); - wxASSERT(obj); - const auto& account = obj->GetAccount(); - - m_active_account->SetString(selection, account.ToString()); - - m_account_grid->GetProperty(kPropertyPersistentId)->SetValueFromString(fmt::format("{:x}", account.GetPersistentId())); - m_account_grid->GetProperty(kPropertyMiiName)->SetValueFromString(std::wstring{ account.GetMiiName() }); - m_account_grid->GetProperty(kPropertyBirthday)->SetValueFromString(fmt::format("{:04d}-{:02d}-{:02d}", account.GetBirthYear(), account.GetBirthMonth(), account.GetBirthDay())); - - const auto gender_property = m_account_grid->GetProperty(kPropertyGender); // gender 2 can be also female? - gender_property->SetChoiceSelection(std::min(gender_property->GetChoices().GetCount() - 1, (uint32)account.GetGender())); - - m_account_grid->GetProperty(kPropertyEmail)->SetValueFromString(std::string{ account.GetEmail() }); - - auto* country_property = dynamic_cast(m_account_grid->GetProperty(kPropertyCountry)); - wxASSERT(country_property); - int index = (country_property)->GetIndexForValue(account.GetCountry()); - if (index == wxNOT_FOUND) - index = 0; - country_property->SetChoiceSelection(index); - - const bool online_fully_valid = account.IsValidOnlineAccount() && ActiveSettings::HasRequiredOnlineFiles(); - if (ActiveSettings::HasRequiredOnlineFiles()) - { - if(account.IsValidOnlineAccount()) - m_online_status->SetLabel(_("Selected account is a valid online account")); - else - m_online_status->SetLabel(_("Selected account is not linked to a NNID or PNID")); - } - else - { - if(NCrypto::OTP_IsPresent() != NCrypto::SEEPROM_IsPresent()) - m_online_status->SetLabel(_("OTP.bin or SEEPROM.bin is missing")); - else if(NCrypto::OTP_IsPresent() && NCrypto::SEEPROM_IsPresent()) - m_online_status->SetLabel(_("OTP and SEEPROM present but no certificate files were found")); - else - m_online_status->SetLabel(_("Online play is not set up. Follow the guide below to get started")); - } - - if(online_fully_valid) - { - m_validate_online->SetBitmap(wxBITMAP_PNG_FROM_DATA(PNG_CHECK_YES).ConvertToImage().Scale(16, 16)); - m_validate_online->SetWindowStyleFlag(m_validate_online->GetWindowStyleFlag() | wxBORDER_NONE); - } - else - { - m_validate_online->SetBitmap(wxBITMAP_PNG_FROM_DATA(PNG_ERROR).ConvertToImage().Scale(16, 16)); - m_validate_online->SetWindowStyleFlag(m_validate_online->GetWindowStyleFlag() & ~wxBORDER_NONE); - } - - // enable/disable network service field depending on online requirements - m_active_service->Enable(online_fully_valid && !CafeSystem::IsTitleRunning()); - if(online_fully_valid) - { - NetworkService service = GetConfig().GetAccountNetworkService(account.GetPersistentId()); - m_active_service->SetSelection(static_cast(service)); - // set the config option here for the selected service - // this will guarantee that it's actually written to settings.xml - // allowing us to eventually get rid of the legacy option in the (far) future - GetConfig().SetAccountSelectedService(account.GetPersistentId(), service); - } - else - { - m_active_service->SetSelection(0); // force offline - } - wxString tmp = _("Network service"); - tmp.append(" ("); - tmp.append(wxString::FromUTF8(boost::nowide::narrow(account.GetMiiName()))); - tmp.append(")"); - m_active_service->SetLabel(tmp); - - // refresh pane size - m_account_grid->InvalidateBestSize(); - //m_account_grid->GetParent()->FitInside(); - //m_account_information->OnStateChange(GetBestSize()); idk.. -} - -void GeneralSettings2::UpdateOnlineAccounts() -{ - m_active_account->Clear(); - for(const auto& account : Account::GetAccounts()) - { - m_active_account->Append(fmt::format(L"{} ({:x})", std::wstring{ account.GetMiiName() }, account.GetPersistentId()), - new wxAccountData(account)); - } - - m_active_account->SetSelection(0); - m_create_account->Enable(m_active_account->GetCount() < 0xC); - m_delete_account->Enable(m_active_account->GetCount() > 1); - UpdateAccountInformation(); -} - -void GeneralSettings2::HandleGraphicsApiSelection() -{ - int selection = m_vsync->GetSelection(); - if(selection == wxNOT_FOUND) - selection = GetConfig().vsync; - - m_vsync->Clear(); - - auto api = m_api_map[m_graphic_api->GetSelection()]; - switch (api) - { -#ifdef ENABLE_OPENGL - case GraphicAPI::kOpenGL: - { - // OpenGL - m_vsync->AppendString(_("Off")); - m_vsync->AppendString(_("On")); - if (selection == 0) - m_vsync->Select(0); - else - m_vsync->Select(1); - - m_graphic_device->Clear(); - m_graphic_device->Disable(); - - m_gx2drawdone_sync->Enable(); - m_async_compile->Disable(); -#ifdef ENABLE_METAL - m_force_mesh_shaders->Disable(); -#endif - break; - } -#endif -#ifdef ENABLE_VULKAN - case GraphicAPI::kVulkan: - { - // Vulkan - m_gx2drawdone_sync->Disable(); - m_async_compile->Enable(); -#ifdef ENABLE_METAL - m_force_mesh_shaders->Disable(); -#endif - - m_vsync->AppendString(_("Off")); - m_vsync->AppendString(_("Double buffering")); - m_vsync->AppendString(_("Triple buffering")); -#if BOOST_OS_WINDOWS - m_vsync->AppendString(_("Match emulated display (Experimental)")); -#endif - - m_vsync->Select(selection); - - m_graphic_device->Enable(); - m_graphic_device->Clear(); - if (!CafeSystem::IsTitleRunning()) - { - auto devices = VulkanRenderer::GetDevices(); - if(!devices.empty()) - { - for(const auto& device : devices) - { - m_graphic_device->Append(device.name, new wxVulkanUUID(device)); - } - m_graphic_device->SetSelection(0); - - const auto& config = GetConfig(); - for(size_t i = 0; i < devices.size(); ++i) - { - if(config.vk_graphic_device_uuid == devices[i].uuid) - { - m_graphic_device->SetSelection(i); - break; - } - } - } - } - else - { - cemu_assert(g_renderer != nullptr); - m_graphic_device->Append(g_renderer->GetDeviceName()); - m_graphic_device->SetSelection(0); - } - break; - } - #endif -#ifdef ENABLE_METAL - case GraphicAPI::kMetal: - { - // Metal - m_gx2drawdone_sync->Disable(); - m_async_compile->Enable(); - m_force_mesh_shaders->Enable(); - - m_vsync->AppendString(_("Off")); - m_vsync->AppendString(_("On")); - - m_vsync->Select(selection); - - m_graphic_device->Enable(); - m_graphic_device->Clear(); - - if (!CafeSystem::IsTitleRunning()) - { - auto devices = MetalRenderer::GetDevices(); - if(!devices.empty()) - { - for (const auto& device : devices) - { - m_graphic_device->Append(device.name, new wxMetalUUID(device)); - } - m_graphic_device->SetSelection(0); - - const auto& config = GetConfig(); - for (size_t i = 0; i < devices.size(); ++i) - { - if (config.mtl_graphic_device_uuid == devices[i].uuid) - { - m_graphic_device->SetSelection(i); - break; - } - } - } - } - else - { - cemu_assert(g_renderer != nullptr); - m_graphic_device->Append(g_renderer->GetDeviceName()); - m_graphic_device->SetSelection(0); - } - break; - } -#endif - } -} - -void GeneralSettings2::ApplyConfig() -{ - ValidateConfig(); - auto& wxGUIconfig = GetWxGUIConfig(); - auto& config = GetConfig(); - - if (LaunchSettings::GetMLCPath().has_value()) - m_mlc_path->SetValue(wxHelper::FromPath(LaunchSettings::GetMLCPath().value())); - else - m_mlc_path->SetValue(wxString::FromUTF8(config.mlc_path.GetValue())); - - m_save_window_position_size->SetValue(wxGUIconfig.window_position != Vector2i{-1,-1}); - m_save_padwindow_position_size->SetValue(wxGUIconfig.pad_position != Vector2i{-1,-1}); - - m_discord_presence->SetValue(wxGUIconfig.use_discord_presence); - m_fullscreen_menubar->SetValue(wxGUIconfig.fullscreen_menubar); - - m_auto_update->SetValue(wxGUIconfig.check_update); - m_receive_untested_releases->SetValue(wxGUIconfig.receive_untested_updates); - m_save_screenshot->SetValue(wxGUIconfig.save_screenshot); - - m_disable_screensaver->SetValue(config.disable_screensaver); - m_play_boot_sound->SetValue(config.play_boot_sound); -#if BOOST_OS_WINDOWS - m_msw_theme->SetSelection(wxGUIconfig.msw_theme); -#endif -#if BOOST_OS_LINUX && defined(ENABLE_FERAL_GAMEMODE) - m_feral_gamemode->SetValue(wxGUIconfig.feral_gamemode); -#endif - // temporary workaround because feature crashes on macOS -#if BOOST_OS_MACOS - m_disable_screensaver->SetValue(false); -#endif - - m_game_paths->Clear(); - for (auto& path : config.game_paths) - { - m_game_paths->Append(wxString::FromUTF8(path)); - } - - const auto& app = wxGetApp(); - for (const auto& language : app.GetLanguages()) - { - if (wxGUIconfig.language == language->Language) - { - m_language->SetStringSelection(language->DescriptionNative); - break; - } - } - - // graphics - for (int i = 0; i < (int)m_api_map.size(); ++i) - { - if (m_api_map[i] == config.graphic_api) - { - m_graphic_api->SetSelection(i); - break; - } - } - m_vsync->SetSelection(config.vsync); - m_overrideGamma->SetValue(config.overrideAppGammaPreference); - m_overrideGammaValue->SetValue(config.overrideGammaValue); - m_userDisplayisSRGB->SetValue(config.userDisplayGamma == 0.0f); - m_userDisplayGamma->SetValue(config.userDisplayGamma); - if(m_userDisplayisSRGB->GetValue()) - { - m_userDisplayGamma->Disable(); - m_userDisplayGamma->SetValue(2.2f); - } - m_async_compile->SetValue(config.async_compile); - m_gx2drawdone_sync->SetValue(config.gx2drawdone_sync); -#ifdef ENABLE_METAL - m_force_mesh_shaders->SetValue(config.force_mesh_shaders); -#endif - m_upscale_filter->SetSelection(config.upscale_filter); - m_downscale_filter->SetSelection(config.downscale_filter); - m_fullscreen_scaling->SetSelection(config.fullscreen_scaling); - - wxASSERT((uint32)config.overlay.position < m_overlay_position->GetCount()); - m_overlay_position->SetSelection((int)config.overlay.position); - m_overlay_font_color->SetColour(wxColour((unsigned long)config.overlay.text_color)); - - uint32 selection = (config.overlay.text_scale - 50) / 25; - wxASSERT(selection < m_overlay_scale->GetCount()); - m_overlay_scale->SetSelection(selection); - - m_overlay_fps->SetValue(config.overlay.fps); - m_overlay_drawcalls->SetValue(config.overlay.drawcalls); - m_overlay_cpu->SetValue(config.overlay.cpu_usage); - m_overlay_cpu_per_core->SetValue(config.overlay.cpu_per_core_usage); - m_overlay_ram->SetValue(config.overlay.ram_usage); - m_overlay_vram->SetValue(config.overlay.vram_usage); - m_overlay_debug->SetValue(config.overlay.debug); - - wxASSERT((uint32)config.notification.position < m_notification_position->GetCount()); - m_notification_position->SetSelection((int)config.notification.position); - m_notification_font_color->SetColour(wxColour((unsigned long)config.notification.text_color)); - - selection = (config.notification.text_scale - 50) / 25; - wxASSERT(selection < m_notification_scale->GetCount()); - m_notification_scale->SetSelection(selection); - - m_controller_profile_name->SetValue(config.notification.controller_profiles); - m_controller_low_battery->SetValue(config.notification.controller_battery); - m_shader_compiling->SetValue(config.notification.shader_compiling); - m_friends_data->SetValue(config.notification.friends); - - // audio - if(config.audio_api == IAudioAPI::DirectSound) - m_audio_api->SetStringSelection(kDirectSound); - else if(config.audio_api == IAudioAPI::XAudio27) - m_audio_api->SetStringSelection(kXAudio27); - else if(config.audio_api == IAudioAPI::XAudio2) - m_audio_api->SetStringSelection(kXAudio2); - else if(config.audio_api == IAudioAPI::Cubeb) - m_audio_api->SetStringSelection(kCubeb); - - SendSliderEvent(m_audio_latency, config.audio_delay); - - m_tv_channels->SetSelection(config.tv_channels); - //m_pad_channels->SetSelection(config.pad_channels); - m_pad_channels->SetSelection(0); - //m_input_channels->SetSelection(config.pad_channels); - m_input_channels->SetSelection(0); - - SendSliderEvent(m_tv_volume, config.tv_volume); - - if (!config.tv_device.empty() && m_tv_device->HasClientObjectData()) - { - for(uint32 i = 0; i < m_tv_device->GetCount(); ++i) - { - const auto device_description = (wxDeviceDescription*)m_tv_device->GetClientObject(i); - if (device_description && config.tv_device == device_description->GetDescription()->GetIdentifier()) - { - m_tv_device->SetSelection(i); - break; - } - } - } - else - m_tv_device->SetSelection(0); - - SendSliderEvent(m_pad_volume, config.pad_volume); - if (!config.pad_device.empty() && m_pad_device->HasClientObjectData()) - { - for (uint32 i = 0; i < m_pad_device->GetCount(); ++i) - { - const auto device_description = (wxDeviceDescription*)m_pad_device->GetClientObject(i); - if (device_description && config.pad_device == device_description->GetDescription()->GetIdentifier()) - { - m_pad_device->SetSelection(i); - break; - } - } - } - else - m_pad_device->SetSelection(0); - - SendSliderEvent(m_input_volume, config.input_volume); - if (!config.input_device.empty() && m_input_device->HasClientObjectData()) - { - for (uint32 i = 0; i < m_input_device->GetCount(); ++i) - { - const auto device_description = (wxInputDeviceDescription*)m_input_device->GetClientObject(i); - if (device_description && config.input_device == device_description->GetDescription()->GetIdentifier()) - { - m_input_device->SetSelection(i); - break; - } - } - } - else - m_input_device->SetSelection(0); - - SendSliderEvent(m_portal_volume, config.portal_volume); - if (!config.portal_device.empty() && m_portal_device->HasClientObjectData()) - { - for (uint32 i = 0; i < m_portal_device->GetCount(); ++i) - { - const auto device_description = (wxDeviceDescription*)m_portal_device->GetClientObject(i); - if (device_description && config.portal_device == device_description->GetDescription()->GetIdentifier()) - { - m_portal_device->SetSelection(i); - break; - } - } - } - else - m_portal_device->SetSelection(0); - - // account - UpdateOnlineAccounts(); - m_active_account->SetSelection(0); - for(uint32 i = 0; i < m_active_account->GetCount(); ++i) - { - const auto* obj = dynamic_cast(m_active_account->GetClientObject(i)); - wxASSERT(obj); - if(obj->GetAccount().GetPersistentId() == ActiveSettings::GetPersistentId()) - { - m_active_account->SetSelection(i); - break; - } - } - m_active_service->SetSelection((int)config.GetAccountNetworkService(ActiveSettings::GetPersistentId())); - - UpdateAccountInformation(); - - // debug - m_crash_dump->SetSelection((int)config.crash_dump.GetValue()); - m_gdb_port->SetValue(config.gdb_port.GetValue()); -#ifdef ENABLE_METAL - m_gpu_capture_dir->SetValue(wxString::FromUTF8(config.gpu_capture_dir.GetValue())); - m_framebuffer_fetch->SetValue(config.framebuffer_fetch); -#endif -} - -void GeneralSettings2::OnAudioAPISelected(wxCommandEvent& event) -{ - IAudioAPI::AudioAPI api; - if (m_audio_api->GetStringSelection() == kDirectSound) - api = IAudioAPI::DirectSound; - else if (m_audio_api->GetStringSelection() == kXAudio27) - api = IAudioAPI::XAudio27; - else if (m_audio_api->GetStringSelection() == kXAudio2) - api = IAudioAPI::XAudio2; - else if (m_audio_api->GetStringSelection() == kCubeb) - api = IAudioAPI::Cubeb; - else - { - wxFAIL_MSG("invalid audio api selected!"); - return; - } - - GetConfig().audio_api = api; - UpdateAudioDeviceList(); - OnAudioDeviceSelected(event); -} - -#define AX_FRAMES_PER_GROUP 4 - -void GeneralSettings2::UpdateAudioDevice() -{ - auto& config = GetConfig(); - - std::unique_lock lock(g_audioMutex); - std::unique_lock inputLock(g_audioInputMutex); - - // tv audio device - { - const auto selection = m_tv_device->GetSelection(); - if (selection == wxNOT_FOUND) - { - cemu_assert_debug(false); - return; - } - - g_tvAudio.reset(); - - if (m_tv_device->HasClientObjectData()) - { - const auto description = (wxDeviceDescription*)m_tv_device->GetClientObject(selection); - if (description) - { - sint32 channels; - if (m_game_launched && g_tvAudio) - channels = g_tvAudio->GetChannels(); - else - channels = CemuConfig::AudioChannelsToNChannels(config.tv_channels); - - try - { - g_tvAudio = IAudioAPI::CreateDevice((IAudioAPI::AudioAPI)config.audio_api, description->GetDescription(), 48000, channels, snd_core::AX_SAMPLES_PER_3MS_48KHZ * AX_FRAMES_PER_GROUP, 16); - g_tvAudio->SetVolume(m_tv_volume->GetValue()); - } - catch (std::runtime_error& ex) - { - cemuLog_log(LogType::Force, "can't initialize tv audio: {}", ex.what()); - } - } - } - } - - // pad audio device - { - const auto selection = m_pad_device->GetSelection(); - if (selection == wxNOT_FOUND) - { - cemu_assert_debug(false); - return; - } - - g_padAudio.reset(); - - if (m_pad_device->HasClientObjectData()) - { - const auto description = (wxDeviceDescription*)m_pad_device->GetClientObject(selection); - if (description) - { - sint32 channels; - if (m_game_launched && g_padAudio) - channels = g_padAudio->GetChannels(); - else - channels = CemuConfig::AudioChannelsToNChannels(config.pad_channels); - - g_padVolume = m_pad_volume->GetValue(); - - try - { - g_padAudio = IAudioAPI::CreateDevice((IAudioAPI::AudioAPI)config.audio_api, description->GetDescription(), 48000, channels, snd_core::AX_SAMPLES_PER_3MS_48KHZ * AX_FRAMES_PER_GROUP, 16); - g_padAudio->SetVolume(m_pad_volume->GetValue()); - } - catch (std::runtime_error& ex) - { - cemuLog_log(LogType::Force, "can't initialize pad audio: {}", ex.what()); - } - } - } - } - - // input audio device - { - const auto selection = m_input_device->GetSelection(); - if (selection == wxNOT_FOUND) - { - cemu_assert_debug(false); - return; - } - - g_inputAudio.reset(); - - if (m_input_device->HasClientObjectData()) - { - const auto description = (wxInputDeviceDescription*)m_input_device->GetClientObject(selection); - if (description) - { - sint32 channels; - if (m_game_launched && g_inputAudio) - channels = g_inputAudio->GetChannels(); - else - channels = CemuConfig::AudioChannelsToNChannels(config.input_channels); - - try - { - g_inputAudio = IAudioInputAPI::CreateDevice(IAudioInputAPI::AudioInputAPI::Cubeb, description->GetDescription(), 32000, channels, snd_core::AX_SAMPLES_PER_3MS_32KHZ, 16); - g_inputAudio->SetVolume(m_input_volume->GetValue()); - } - catch (std::runtime_error& ex) - { - cemuLog_log(LogType::Force, "can't initialize pad audio: {}", ex.what()); - } - } - } - } - - // skylander portal audio device - { - const auto selection = m_portal_device->GetSelection(); - if (selection == wxNOT_FOUND) - { - cemu_assert_debug(false); - return; - } - - g_portalAudio.reset(); - - if (m_portal_device->HasClientObjectData()) - { - const auto description = (wxDeviceDescription*)m_portal_device->GetClientObject(selection); - if (description) - { - sint32 channels; - if (m_game_launched && g_portalAudio) - channels = g_portalAudio->GetChannels(); - else - channels = 1; - - try - { - g_portalAudio = IAudioAPI::CreateDevice((IAudioAPI::AudioAPI)config.audio_api, description->GetDescription(), 8000, 1, 32, 16); - g_portalAudio->SetVolume(m_portal_volume->GetValue()); - } - catch (std::runtime_error& ex) - { - cemuLog_log(LogType::Force, "can't initialize portal audio: {}", ex.what()); - } - } - } - - } -} - -void GeneralSettings2::OnAudioDeviceSelected(wxCommandEvent& event) -{ - UpdateAudioDevice(); -} - -void GeneralSettings2::OnAudioChannelsSelected(wxCommandEvent& event) -{ - const auto obj = wxDynamicCast(event.GetEventObject(), wxChoice); - wxASSERT(obj); - if (obj->GetSelection() == wxNOT_FOUND) - return; - - auto& config = GetConfig(); - if (obj == m_tv_channels) - { - if (config.tv_channels == (AudioChannels)obj->GetSelection()) - return; - - config.tv_channels = (AudioChannels)obj->GetSelection(); - } - else if (obj == m_pad_channels) - { - if (config.pad_channels == (AudioChannels)obj->GetSelection()) - return; - - config.pad_channels = (AudioChannels)obj->GetSelection(); - } - else - cemu_assert_debug(false); - - if(m_game_launched) - wxMessageBox(_("You have to restart the game in order to apply the new settings."), _("Information"), wxOK | wxCENTRE, this); - else - UpdateAudioDevice(); -} - -void GeneralSettings2::OnGraphicAPISelected(wxCommandEvent& event) -{ - cemu_assert_debug(!CafeSystem::IsTitleRunning()); - HandleGraphicsApiSelection(); -} - -void GeneralSettings2::OnUserDisplaySRGBSelected(wxCommandEvent& event) -{ - m_userDisplayGamma->SetValue(2.2f); - if(event.IsChecked()) - m_userDisplayGamma->Disable(); - else - m_userDisplayGamma->Enable(); - auto& config = GetConfig(); - config.userDisplayGamma = m_userDisplayGamma->GetValue() * !event.IsChecked(); -} - -void GeneralSettings2::OnAddPathClicked(wxCommandEvent& event) -{ - wxDirDialog path_dialog(this, _("Select a directory containing games."), wxEmptyString, wxDD_DEFAULT_STYLE | wxDD_DIR_MUST_EXIST); - if (path_dialog.ShowModal() != wxID_OK || path_dialog.GetPath().empty()) - return; - - const auto path = path_dialog.GetPath(); - // test if already included - for (auto& s : m_game_paths->GetStrings()) - { - if (s == path) - return; - } - - m_game_paths->Append(path); - m_reload_gamelist = true; - - // trigger title list rescan with new path configuration - CafeTitleList::ClearScanPaths(); - for (auto& it : m_game_paths->GetStrings()) - CafeTitleList::AddScanPath(wxHelper::MakeFSPath(it)); - CafeTitleList::Refresh(); -} - -void GeneralSettings2::OnRemovePathClicked(wxCommandEvent& event) -{ - const auto selection = m_game_paths->GetSelection(); - if (selection == wxNOT_FOUND) - return; - - m_game_paths->Delete(selection); - m_reload_gamelist = true; - // trigger title list rescan with new path configuration - CafeTitleList::ClearScanPaths(); - for (auto& it : m_game_paths->GetStrings()) - CafeTitleList::AddScanPath(wxHelper::MakeFSPath(it)); - CafeTitleList::Refresh(); -} - -void GeneralSettings2::OnActiveAccountChanged(wxCommandEvent& event) -{ - UpdateAccountInformation(); - m_has_account_change = true; -} - -void GeneralSettings2::OnAccountServiceChanged(wxCommandEvent& event) -{ - auto& config = GetConfig(); - uint32 peristentId = GetSelectedAccountPersistentId(); - config.SetAccountSelectedService(peristentId, static_cast(m_active_service->GetSelection())); - UpdateAccountInformation(); -} - -void GeneralSettings2::OnMLCPathSelect(wxCommandEvent& event) -{ - if(CafeSystem::IsTitleRunning()) - { - wxMessageBox(_("Can't change MLC path while a game is running!"), _("Error"), wxOK | wxCENTRE | wxICON_ERROR, this); - return; - } - // show directory dialog - wxDirDialog path_dialog(this, _("Select MLC directory"), wxEmptyString, wxDD_DEFAULT_STYLE | wxDD_DIR_MUST_EXIST); - if (path_dialog.ShowModal() != wxID_OK || path_dialog.GetPath().empty()) - return; - // check if the choosen MLC path is an already initialized MLC location - fs::path newMlc = wxHelper::MakeFSPath(path_dialog.GetPath()); - if(CemuApp::CheckMLCPath(newMlc)) - { - // ask user if they are sure they want to use this folder and let them know that accounts and saves wont transfer - wxString message = _("Note that changing the MLC location will not transfer any accounts or save files. Are you sure you want to change the path?"); - wxMessageDialog dialog(this, message, _("Warning"), wxYES_NO | wxCENTRE | wxICON_WARNING); - if(dialog.ShowModal() == wxID_NO) - return; - if( !CemuApp::CreateDefaultMLCFiles(newMlc) ) // creating also acts as a check for read+write access - { - wxMessageBox(_("Failed to create default MLC files in the selected directory. The MLC path has not been changed"), _("Error"), wxOK | wxCENTRE | wxICON_ERROR, this); - return; - } - } - else - { - // ask user if they want to create a new mlc structure at the choosen location - wxString message = _("The selected directory does not contain the expected MLC structure. Do you want to create a new MLC structure in this directory?\nNote that changing the MLC location will not transfer any accounts or save files."); - wxMessageDialog dialog(this, message, _("Warning"), wxYES_NO | wxCENTRE | wxICON_WARNING); - if( !CemuApp::CreateDefaultMLCFiles(newMlc) ) - { - wxMessageBox(_("Failed to create default MLC files in the selected directory. The MLC path has not been changed"), _("Error"), wxOK | wxCENTRE | wxICON_ERROR, this); - return; - } - } - // update MLC path and store any other modified settings - GetConfig().SetMLCPath(newMlc); - StoreConfig(); - wxMessageBox(_("Cemu needs to be restarted for the changes to take effect."), _("Information"), wxOK | wxCENTRE | wxICON_INFORMATION, this); - // close settings and then cemu - wxCloseEvent closeEvent(wxEVT_CLOSE_WINDOW); - wxPostEvent(this, closeEvent); - wxPostEvent(GetParent(), closeEvent); -} - -void GeneralSettings2::OnMLCPathClear(wxCommandEvent& event) -{ - if(CafeSystem::IsTitleRunning()) - { - wxMessageBox(_("Can't change MLC path while a game is running!"), _("Error"), wxOK | wxCENTRE | wxICON_ERROR, this); - return; - } - wxString message = _("Note that changing the MLC location will not transfer any accounts or save files. Are you sure you want to change the path?"); - wxMessageDialog dialog(this, message, _("Warning"), wxYES_NO | wxCENTRE | wxICON_WARNING); - if(dialog.ShowModal() == wxID_NO) - return; - GetConfig().SetMLCPath(""); - StoreConfig(); - GetConfigHandle().Save(); - wxMessageBox(_("Cemu needs to be restarted for the changes to take effect."), _("Information"), wxOK | wxCENTRE | wxICON_INFORMATION, this); - // close settings and then cemu - wxCloseEvent closeEvent(wxEVT_CLOSE_WINDOW); - wxPostEvent(this, closeEvent); - wxPostEvent(GetParent(), closeEvent); -} - -void GeneralSettings2::OnShowOnlineValidator(wxCommandEvent& event) -{ - const auto selection = m_active_account->GetSelection(); - if (selection == wxNOT_FOUND) - return; - - const auto* obj = dynamic_cast(m_active_account->GetClientObject(selection)); - wxASSERT(obj); - const auto& account = obj->GetAccount(); - - const auto validator = account.ValidateOnlineFiles(); - if (validator) // everything valid? shouldn't happen - return; - - wxString err; - err << _("The following error(s) have been found:") << '\n'; - - if (validator.otp == OnlineValidator::FileState::Missing) - err << _("otp.bin missing in Cemu directory") << '\n'; - else if(validator.otp == OnlineValidator::FileState::Corrupted) - err << _("otp.bin is invalid") << '\n'; - - if (validator.seeprom == OnlineValidator::FileState::Missing) - err << _("seeprom.bin missing in Cemu directory") << '\n'; - else if(validator.seeprom == OnlineValidator::FileState::Corrupted) - err << _("seeprom.bin is invalid") << '\n'; - - if(!validator.missing_files.empty()) - { - err << _("Missing certificate and key files:") << '\n'; - - int counter = 0; - for (const auto& f : validator.missing_files) - { - err << f << '\n'; - - ++counter; - if(counter > 10) - { - err << "..." << '\n'; - break; - } - } - - err << '\n'; - } - - if (!validator.valid_account) - { - err << _("The currently selected account is not a valid or dumped online account:") << '\n'; - err << GetOnlineAccountErrorMessage(validator.account_error); - } - - wxMessageBox(err, _("Online Status"), wxOK | wxCENTRE | wxICON_INFORMATION); -} - -wxString GeneralSettings2::GetOnlineAccountErrorMessage(OnlineAccountError error) -{ - switch (error) - { - case OnlineAccountError::kNoAccountId: - return _("AccountId missing (The account is not connected to a NNID/PNID)"); - case OnlineAccountError::kNoPasswordCached: - return _("IsPasswordCacheEnabled is set to false (The remember password option on your Wii U must be enabled for this account before dumping it)"); - case OnlineAccountError::kPasswordCacheEmpty: - return _("AccountPasswordCache is empty (The remember password option on your Wii U must be enabled for this account before dumping it)"); - case OnlineAccountError::kNoPrincipalId: - return _("PrincipalId missing"); - default: - return "no error"; - } -} diff --git a/src/gui/wxgui/GeneralSettings2.h b/src/gui/wxgui/GeneralSettings2.h deleted file mode 100644 index 2f7e3b68..00000000 --- a/src/gui/wxgui/GeneralSettings2.h +++ /dev/null @@ -1,143 +0,0 @@ -#pragma once -#include "config/CemuConfig.h" -#include -#include -#include - -class wxCheckBox; -class wxChoice; -class wxColourPickerCtrl; -class wxListBox; -class wxNotebook; -class wxRadioBox; -class wxSlider; -class wxSpinCtrl; -class wxSpinCtrlDouble; -class wxStaticText; - -wxDECLARE_EVENT(wxEVT_ACCOUNTLIST_REFRESH, wxCommandEvent); - -class GeneralSettings2 : public wxDialog -{ -public: - GeneralSettings2(wxWindow* parent, bool game_launched); - ~GeneralSettings2(); - - [[nodiscard]] bool ShouldReloadGamelist() const { return m_reload_gamelist; } - [[nodiscard]] bool MLCModified() const { return m_mlc_modified; } - void OnClose(wxCloseEvent& event); - -private: - void ValidateConfig(); - void StoreConfig(); - void DisableSettings(bool game_launched); - - bool m_reload_gamelist = false; - bool m_mlc_modified = false; - bool m_game_launched; - - bool m_has_account_change = false; // keep track of dirty state of accounts - std::vector m_api_map; // map from dropdown index to GraphicsAPISetting, used in HandleGraphicsApiSelection - - - wxPanel* AddGeneralPage(wxNotebook* notebook); - wxPanel* AddGraphicsPage(wxNotebook* notebook); - wxPanel* AddAudioPage(wxNotebook* notebook); - wxPanel* AddOverlayPage(wxNotebook* notebook); - wxPanel* AddAccountPage(wxNotebook* notebook); - wxPanel* AddDebugPage(wxNotebook* notebook); - - // General - wxChoice * m_language; - wxCheckBox* m_save_window_position_size; - wxCheckBox* m_save_padwindow_position_size; - wxCheckBox* m_discord_presence, *m_fullscreen_menubar; - wxCheckBox* m_auto_update, *m_receive_untested_releases, *m_save_screenshot; - wxCheckBox* m_disable_screensaver; - wxCheckBox* m_play_boot_sound; -#if BOOST_OS_WINDOWS - wxChoice* m_msw_theme; -#endif -#if BOOST_OS_LINUX && defined(ENABLE_FERAL_GAMEMODE) - wxCheckBox* m_feral_gamemode; -#endif - wxListBox* m_game_paths; - wxTextCtrl* m_mlc_path; - - // Graphics - wxChoice* m_graphic_api, * m_graphic_device; - wxChoice* m_vsync; - wxCheckBox* m_overrideGamma; - wxSpinCtrlDouble* m_overrideGammaValue; - wxSpinCtrlDouble* m_userDisplayGamma; - wxCheckBox* m_userDisplayisSRGB; - - wxCheckBox *m_async_compile, *m_gx2drawdone_sync; -#ifdef ENABLE_METAL - wxCheckBox *m_force_mesh_shaders; -#endif - wxRadioBox* m_upscale_filter, *m_downscale_filter, *m_fullscreen_scaling; - wxChoice* m_overlay_position, *m_notification_position, *m_overlay_scale, *m_notification_scale; - wxCheckBox* m_controller_profile_name, *m_controller_low_battery, *m_shader_compiling, *m_friends_data; - wxCheckBox *m_overlay_fps, *m_overlay_drawcalls, *m_overlay_cpu, *m_overlay_cpu_per_core,*m_overlay_ram, *m_overlay_vram, *m_overlay_debug; - wxColourPickerCtrl *m_overlay_font_color, *m_notification_font_color; - - // Audio - wxChoice* m_audio_api; - wxSlider *m_audio_latency; - wxSlider *m_tv_volume, *m_pad_volume, *m_input_volume, *m_portal_volume; - wxChoice *m_tv_channels, *m_pad_channels, *m_input_channels; - wxChoice *m_tv_device, *m_pad_device, *m_input_device, *m_portal_device; - - // Account - wxButton* m_create_account, * m_delete_account; - wxChoice* m_active_account; - wxRadioBox* m_active_service; - wxCollapsiblePane* m_account_information; - wxPropertyGrid* m_account_grid; - wxBitmapButton* m_validate_online; - wxStaticText* m_online_status; - - // Debug - wxChoice* m_crash_dump; - wxSpinCtrl* m_gdb_port; -#ifdef ENABLE_METAL - wxTextCtrl* m_gpu_capture_dir; - wxCheckBox* m_framebuffer_fetch; -#endif - - void OnAccountCreate(wxCommandEvent& event); - void OnAccountDelete(wxCommandEvent& event); - void OnAccountSettingsChanged(wxPropertyGridEvent& event); - void OnAudioLatencyChanged(wxCommandEvent& event); - void OnVolumeChanged(wxCommandEvent& event); - void OnInputVolumeChanged(wxCommandEvent& event); - void OnSliderChangedPercent(wxCommandEvent& event); - void OnLatencySliderChanged(wxCommandEvent& event); - void OnAudioAPISelected(wxCommandEvent& event); - void OnAudioDeviceSelected(wxCommandEvent& event); - void OnAudioChannelsSelected(wxCommandEvent& event); - void OnGraphicAPISelected(wxCommandEvent& event); - void OnUserDisplaySRGBSelected(wxCommandEvent& event); - void OnAddPathClicked(wxCommandEvent& event); - void OnRemovePathClicked(wxCommandEvent& event); - void OnActiveAccountChanged(wxCommandEvent& event); - void OnMLCPathSelect(wxCommandEvent& event); - void OnMLCPathClear(wxCommandEvent& event); - void OnShowOnlineValidator(wxCommandEvent& event); - void OnAccountServiceChanged(wxCommandEvent& event); - static wxString GetOnlineAccountErrorMessage(OnlineAccountError error); - - uint32 GetSelectedAccountPersistentId(); - - // updates cemu audio devices - void UpdateAudioDevice(); - // refreshes audio device list for dropdown - void UpdateAudioDeviceList(); - - void ResetAccountInformation(); - void UpdateAccountInformation(); - void UpdateOnlineAccounts(); - void HandleGraphicsApiSelection(); - void ApplyConfig(); -}; diff --git a/src/gui/wxgui/GettingStartedDialog.cpp b/src/gui/wxgui/GettingStartedDialog.cpp deleted file mode 100644 index df3c940e..00000000 --- a/src/gui/wxgui/GettingStartedDialog.cpp +++ /dev/null @@ -1,283 +0,0 @@ -#include "wxgui/GettingStartedDialog.h" - -#include -#include -#include -#include -#include -#include - -#include "config/ActiveSettings.h" -#include "wxCemuConfig.h" -#include "wxgui/CemuApp.h" -#include "wxgui/DownloadGraphicPacksWindow.h" -#include "wxgui/GraphicPacksWindow2.h" -#include "wxgui/input/InputSettings2.h" -#include "config/CemuConfig.h" - -#include "Cafe/TitleList/TitleList.h" - -#if BOOST_OS_LINUX || BOOST_OS_MACOS || BOOST_OS_BSD -#include "resource/embedded/resources.h" -#endif - -#include "wxHelper.h" - -wxDEFINE_EVENT(EVT_REFRESH_FIRST_PAGE, wxCommandEvent); // used to refresh the first page after the language change - -wxPanel* GettingStartedDialog::CreatePage1() -{ - auto* mainPanel = new wxPanel(m_notebook, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL); - auto* page1_sizer = new wxBoxSizer(wxVERTICAL); - { - auto* sizer = new wxBoxSizer(wxHORIZONTAL); - sizer->Add(new wxStaticBitmap(mainPanel, wxID_ANY, wxICON(M_WND_ICON128)), 0, wxALIGN_CENTER_VERTICAL | wxALL, 5); - m_page1.staticText11 = new wxStaticText(mainPanel, wxID_ANY, _("It looks like you're starting Cemu for the first time.\nThis quick setup assistant will help you get the best experience")); - m_page1.staticText11->Wrap(-1); - sizer->Add(m_page1.staticText11, 0, wxALL, 5); - page1_sizer->Add(sizer, 0, wxALL | wxEXPAND, 5); - } - - if(ActiveSettings::IsPortableMode()) - { - m_page1.portableModeInfoText = new wxStaticText(mainPanel, wxID_ANY, _("Cemu is running in portable mode")); - m_page1.portableModeInfoText->Show(true); - page1_sizer->Add(m_page1.portableModeInfoText, 0, wxALL, 5); - - } - - // language selection -#if 0 - { - m_page1.languageBoxSizer = new wxStaticBoxSizer(wxVERTICAL, mainPanel, _("Language")); - m_page1.languageText = new wxStaticText(m_page1.languageBoxSizer->GetStaticBox(), wxID_ANY, _("Select the language you want to use in Cemu")); - m_page1.languageBoxSizer->Add(m_page1.languageText, 0, wxALL, 5); - - wxString language_choices[] = { _("Default") }; - wxChoice* m_language = new wxChoice(m_page1.languageBoxSizer->GetStaticBox(), wxID_ANY, wxDefaultPosition, wxDefaultSize, std::size(language_choices), language_choices); - m_language->SetSelection(0); - - for (const auto& language : wxGetApp().GetLanguages()) - { - m_language->Append(language->DescriptionNative); - } - - m_language->SetSelection(0); - m_page1.languageBoxSizer->Add(m_language, 0, wxALL | wxEXPAND, 5); - - page1_sizer->Add(m_page1.languageBoxSizer, 0, wxALL | wxEXPAND, 5); - - m_language->Bind(wxEVT_CHOICE, [this, m_language](const auto&) - { - const auto language = m_language->GetStringSelection(); - auto selection = m_language->GetSelection(); - if (selection == 0) - GetConfig().language = wxLANGUAGE_DEFAULT; - else - { - auto& app = wxGetApp(); - const auto language = m_language->GetStringSelection(); - for (const auto& lang : app.GetLanguages()) - { - if (lang->DescriptionNative == language) - { - app.LocalizeUI(static_cast(lang->Language)); - wxCommandEvent event(EVT_REFRESH_FIRST_PAGE); - wxPostEvent(this, event); - break; - } - } - } - }); - } -#endif - - { - m_page1.gamePathBoxSizer = new wxStaticBoxSizer(wxVERTICAL, mainPanel, _("Game paths")); - m_page1.gamePathText = new wxStaticText(m_page1.gamePathBoxSizer->GetStaticBox(), wxID_ANY, _("The game path is scanned by Cemu to automatically locate your games, game updates and DLCs. We recommend creating a dedicated directory in which\nyou place all your Wii U game files. Additional paths can be set later in Cemu's general settings. All common Wii U game formats are supported by Cemu.")); - m_page1.gamePathBoxSizer->Add(m_page1.gamePathText, 0, wxALL, 5); - - auto* game_path_sizer = new wxBoxSizer(wxHORIZONTAL); - - m_page1.gamePathText2 = new wxStaticText(m_page1.gamePathBoxSizer->GetStaticBox(), wxID_ANY, _("Game path")); - game_path_sizer->Add(m_page1.gamePathText2, 0, wxALIGN_CENTER_VERTICAL | wxALL, 5); - - m_page1.gamePathPicker = new wxDirPickerCtrl(m_page1.gamePathBoxSizer->GetStaticBox(), wxID_ANY, wxEmptyString, _("Select a folder")); - game_path_sizer->Add(m_page1.gamePathPicker, 1, wxALL, 5); - - m_page1.gamePathBoxSizer->Add(game_path_sizer, 0, wxEXPAND, 5); - - page1_sizer->Add(m_page1.gamePathBoxSizer, 0, wxALL | wxEXPAND, 5); - } - - { - auto* sizer = new wxStaticBoxSizer(wxVERTICAL, mainPanel, _("Graphic packs && mods")); - - sizer->Add(new wxStaticText(sizer->GetStaticBox(), wxID_ANY, _("Graphic packs improve games by offering the ability to change resolution, increase FPS, tweak visuals or add gameplay modifications.\nGet started by opening the graphic packs configuration window.\n")), 0, wxALL, 5); - - auto* download_gp = new wxButton(sizer->GetStaticBox(), wxID_ANY, _("Download and configure graphic packs")); - download_gp->Bind(wxEVT_BUTTON, &GettingStartedDialog::OnConfigureGPs, this); - sizer->Add(download_gp, 0, wxALIGN_CENTER | wxALL, 5); - - page1_sizer->Add(sizer, 0, wxALL | wxEXPAND, 5); - } - - { - auto* sizer = new wxFlexGridSizer(0, 1, 0, 0); - sizer->AddGrowableCol(0); - sizer->AddGrowableRow(0); - sizer->SetFlexibleDirection(wxBOTH); - sizer->SetNonFlexibleGrowMode(wxFLEX_GROWMODE_ALL); - - auto* next = new wxButton(mainPanel, wxID_ANY, _("Next")); - next->Bind(wxEVT_BUTTON, [this](const auto&){m_notebook->SetSelection(1); }); - sizer->Add(next, 0, wxALIGN_BOTTOM | wxALIGN_RIGHT | wxALL, 5); - - page1_sizer->Add(sizer, 1, wxEXPAND, 5); - } - - mainPanel->SetSizer(page1_sizer); - return mainPanel; -} - -wxPanel* GettingStartedDialog::CreatePage2() -{ - auto* result = new wxPanel(m_notebook, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL); - auto* page2_sizer = new wxBoxSizer(wxVERTICAL); - - { - auto* sizer = new wxStaticBoxSizer(new wxStaticBox(result, wxID_ANY, _("Input settings")), wxVERTICAL); - - sizer->Add(new wxStaticText(sizer->GetStaticBox(), wxID_ANY, _("You can configure one controller for each player.\nWe advise you to always use GamePad as emulated input for the first player, since many games expect the GamePad to be present.\nIt is also required for touch functionality.\nThe default global hotkeys are:\nCTRL - show pad screen\nCTRL + TAB - toggle pad screen\nALT + ENTER - toggle fullscreen\nESC - leave fullscreen\n\nIf you're having trouble configuring your controller, make sure to have it in idle state and press calibrate.\nAlso don't set the axis deadzone too low.")), 0, wxALL, 5); - - auto* input_button = new wxButton(sizer->GetStaticBox(), wxID_ANY, _("Configure input")); - input_button->Bind(wxEVT_BUTTON, &GettingStartedDialog::OnInputSettings, this); - sizer->Add(input_button, 0, wxALIGN_CENTER | wxALL, 5); - - page2_sizer->Add(sizer, 0, wxALL | wxEXPAND, 5); - } - - { - auto* sizer = new wxStaticBoxSizer(new wxStaticBox(result, wxID_ANY, _("Additional options")), wxVERTICAL); - - auto* option_sizer = new wxFlexGridSizer(0, 2, 0, 0); - option_sizer->SetFlexibleDirection(wxBOTH); - option_sizer->SetNonFlexibleGrowMode(wxFLEX_GROWMODE_SPECIFIED); - - m_page2.fullscreenCheckbox = new wxCheckBox(sizer->GetStaticBox(), wxID_ANY, _("Start games with fullscreen")); - option_sizer->Add(m_page2.fullscreenCheckbox, 0, wxALL, 5); - - m_page2.separateCheckbox = new wxCheckBox(sizer->GetStaticBox(), wxID_ANY, _("Open separate pad screen")); - option_sizer->Add(m_page2.separateCheckbox, 0, wxALL, 5); - - m_page2.updateCheckbox = new wxCheckBox(sizer->GetStaticBox(), wxID_ANY, _("Automatically check for updates")); - option_sizer->Add(m_page2.updateCheckbox, 0, wxALL, 5); -#if BOOST_OS_LINUX - if (!std::getenv("APPIMAGE")) { - m_page2.updateCheckbox->Disable(); - } -#endif - sizer->Add(option_sizer, 1, wxEXPAND, 5); - page2_sizer->Add(sizer, 0, wxALL | wxEXPAND, 5); - } - - { - auto* sizer = new wxFlexGridSizer(0, 3, 0, 0); - sizer->AddGrowableCol(1); - sizer->AddGrowableRow(0); - sizer->SetFlexibleDirection(wxBOTH); - sizer->SetNonFlexibleGrowMode(wxFLEX_GROWMODE_ALL); - - auto* previous = new wxButton(result, wxID_ANY, _("Previous")); - previous->Bind(wxEVT_BUTTON, [this](const auto&) {m_notebook->SetSelection(0); }); - sizer->Add(previous, 0, wxALIGN_BOTTOM | wxALIGN_RIGHT | wxALL, 5); - - auto* close = new wxButton(result, wxID_ANY, _("Close")); - close->Bind(wxEVT_BUTTON, [this](const auto&){ Close(); }); - sizer->Add(close, 1, wxALIGN_BOTTOM | wxALIGN_RIGHT | wxALL, 5); - - page2_sizer->Add(sizer, 1, wxEXPAND | wxLEFT, 5); - } - - result->SetSizer(page2_sizer); - return result; -} - -void GettingStartedDialog::ApplySettings() -{ - auto& config = GetWxGUIConfig(); - m_page2.fullscreenCheckbox->SetValue(config.fullscreen.GetValue()); - m_page2.updateCheckbox->SetValue(config.check_update.GetValue()); - m_page2.separateCheckbox->SetValue(config.pad_open.GetValue()); -} - -void GettingStartedDialog::UpdateWindowSize() -{ - for (auto it = m_notebook->GetChildren().GetFirst(); it; it = it->GetNext()) - { - it->GetData()->Layout(); - } - m_notebook->Layout(); - Layout(); - Fit(); -} - -void GettingStartedDialog::OnClose(wxCloseEvent& event) -{ - event.Skip(); - - auto& wxGUIConfig = GetWxGUIConfig(); - wxGUIConfig.fullscreen = m_page2.fullscreenCheckbox->GetValue(); - wxGUIConfig.check_update = m_page2.updateCheckbox->GetValue(); - wxGUIConfig.pad_open = m_page2.separateCheckbox->GetValue(); - - auto& config = GetConfig(); - const fs::path gamePath = wxHelper::MakeFSPath(m_page1.gamePathPicker->GetPath()); - std::error_code ec; - if (!gamePath.empty() && fs::exists(gamePath, ec)) - { - const auto it = std::find(config.game_paths.cbegin(), config.game_paths.cend(), gamePath); - if (it == config.game_paths.cend()) - { - config.game_paths.emplace_back(_pathToUtf8(gamePath)); - } - } -} - -GettingStartedDialog::GettingStartedDialog(wxWindow* parent) - : wxDialog(parent, wxID_ANY, _("Getting started"), wxDefaultPosition, { 740,530 }, wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER) -{ - auto* sizer = new wxBoxSizer(wxVERTICAL); - - m_notebook = new wxSimplebook(this, wxID_ANY); - - auto* m_page1 = CreatePage1(); - m_notebook->AddPage(m_page1, wxEmptyString); - - auto* m_page2 = CreatePage2(); - m_notebook->AddPage(m_page2, wxEmptyString); - - sizer->Add(m_notebook, 1, wxEXPAND | wxALL, 5); - - this->SetSizer(sizer); - this->Centre(wxBOTH); - this->Bind(wxEVT_CLOSE_WINDOW, &GettingStartedDialog::OnClose, this); - - ApplySettings(); - UpdateWindowSize(); -} - -void GettingStartedDialog::OnConfigureGPs(wxCommandEvent& event) -{ - DownloadGraphicPacksWindow dialog(this); - dialog.ShowModal(); - GraphicPacksWindow2::RefreshGraphicPacks(); - GraphicPacksWindow2 window(this, 0); - window.ShowModal(); -} - -void GettingStartedDialog::OnInputSettings(wxCommandEvent& event) -{ - InputSettings2 dialog(this); - dialog.ShowModal(); -} diff --git a/src/gui/wxgui/GettingStartedDialog.h b/src/gui/wxgui/GettingStartedDialog.h deleted file mode 100644 index 9dfd69b4..00000000 --- a/src/gui/wxgui/GettingStartedDialog.h +++ /dev/null @@ -1,48 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include -#include -#include - -class GettingStartedDialog : public wxDialog -{ -public: - GettingStartedDialog(wxWindow* parent = nullptr); - -private: - wxPanel* CreatePage1(); - wxPanel* CreatePage2(); - void ApplySettings(); - void UpdateWindowSize(); - - void OnClose(wxCloseEvent& event); - void OnConfigureGPs(wxCommandEvent& event); - void OnInputSettings(wxCommandEvent& event); - - wxSimplebook* m_notebook; - - struct - { - // header - wxStaticText* staticText11{}; - wxStaticText* portableModeInfoText{}; - - // game path box - wxStaticBoxSizer* gamePathBoxSizer{}; - wxStaticText* gamePathText{}; - wxStaticText* gamePathText2{}; - wxDirPickerCtrl* gamePathPicker{}; - }m_page1; - - struct - { - wxCheckBox* fullscreenCheckbox; - wxCheckBox* separateCheckbox; - wxCheckBox* updateCheckbox; - }m_page2; -}; - diff --git a/src/gui/wxgui/GraphicPacksWindow2.cpp b/src/gui/wxgui/GraphicPacksWindow2.cpp deleted file mode 100644 index 21e25b07..00000000 --- a/src/gui/wxgui/GraphicPacksWindow2.cpp +++ /dev/null @@ -1,741 +0,0 @@ -#include "helpers/wxHelpers.h" -#include "wxgui/wxgui.h" -#include "wxgui/GraphicPacksWindow2.h" -#include "wxgui/DownloadGraphicPacksWindow.h" -#include "wxgui/DownloadCustomGraphicPackWindow.h" -#include "Cafe/GraphicPack/GraphicPack2.h" -#include "config/CemuConfig.h" -#include "config/ActiveSettings.h" - -#include "Cafe/HW/Latte/Core/LatteAsyncCommands.h" - -#include "Cafe/CafeSystem.h" -#include "Cafe/TitleList/TitleList.h" - -#include "wxHelper.h" - -#if BOOST_OS_LINUX || BOOST_OS_MACOS || BOOST_OS_BSD -#include "resource/embedded/resources.h" -#endif - -// main.cpp -class wxGraphicPackData : public wxTreeItemData -{ -public: - wxGraphicPackData(GraphicPackPtr pack) - : m_pack(std::move(pack)) { } - - const GraphicPackPtr& GetGraphicPack() const { return m_pack; } - -private: - GraphicPackPtr m_pack; -}; - -void GraphicPacksWindow2::FillGraphicPackList() const -{ - wxWindowUpdateLocker lock(m_graphic_pack_tree); - - m_graphic_pack_tree->DeleteAllItems(); - auto graphic_packs = GraphicPack2::GetGraphicPacks(); - - const auto root = m_graphic_pack_tree->AddRoot("Root"); - - const bool has_filter = !m_filter.empty(); - - for(auto& p : graphic_packs) - { - if (!p->IsUniversal()) - { - // filter graphic packs by given title id - if (m_filter_installed_games && !m_installed_games.empty()) - { - bool found = false; - for (uint64 titleId : p->GetTitleIds()) - { - if (std::find(m_installed_games.cbegin(), m_installed_games.cend(), titleId) != m_installed_games.cend()) - { - found = true; - break; - } - } - - if (!found) - continue; - } - - // filter graphic packs by given title id - if(has_filter) - { - bool found = false; - - if (boost::icontains(p->GetVirtualPath(), m_filter)) - found = true; - else - { - for (uint64 titleId : p->GetTitleIds()) - { - if (boost::icontains(fmt::format("{:x}", titleId), m_filter)) - { - found = true; - break; - } - } - } - - if (!found) - continue; - } - } - - const auto& path = p->GetVirtualPath(); - auto tokens = TokenizeView(path, '/'); - auto node = root; - for(size_t i=0; iAppendItem(parent_node, wxString::FromUTF8(token)); - } - } - else - { - // last token - // if a node with same name already exists, add a number suffix - for (sint32 s = 0; s < 999; s++) - { - wxString nodeName = wxString::FromUTF8(token); - if (s > 0) - nodeName.append(formatWxString(" #{}", s + 1)); - - node = FindTreeItem(parent_node, nodeName); - if (!node.IsOk()) - { - node = m_graphic_pack_tree->AppendItem(parent_node, nodeName); - break; - } - } - } - } - - if(node.IsOk() && node != root) - { - m_graphic_pack_tree->SetItemData(node, new wxGraphicPackData(p)); - bool canEnable = true; - - if (p->GetVersion() == 3) - { - auto tmp_text = m_graphic_pack_tree->GetItemText(node); - m_graphic_pack_tree->SetItemText(node, tmp_text + " (may not be compatible with Vulkan)"); - } - else if (p->GetVersion() != 3 && p->GetVersion() != 4 && p->GetVersion() != 5 && p->GetVersion() != 6 && p->GetVersion() != GraphicPack2::GFXPACK_VERSION_7 && p->GetVersion() != GraphicPack2::GFXPACK_VERSION_8) - { - auto tmp_text = m_graphic_pack_tree->GetItemText(node); - m_graphic_pack_tree->SetItemText(node, tmp_text + " (Unsupported version)"); - m_graphic_pack_tree->SetItemTextColour(node, m_incompatible_colour); - canEnable = false; - } - else if (p->IsActivated()) - m_graphic_pack_tree->SetItemTextColour(node, m_activated_colour); - - m_graphic_pack_tree->MakeCheckable(node, p->IsEnabled()); - if (!canEnable) - m_graphic_pack_tree->DisableCheckBox(node); - } - } - - m_graphic_pack_tree->Sort(root, true); - - if (!m_filter.empty()) - { - size_t counter = 0; - ExpandChildren({ root }, counter); - } -} - - -void GraphicPacksWindow2::GetChildren(const wxTreeItemId& id, std::vector& children) const -{ - wxTreeItemIdValue cookie; - wxTreeItemId child = m_graphic_pack_tree->GetFirstChild(id, cookie); - while (child.IsOk()) - { - children.emplace_back(child); - child = m_graphic_pack_tree->GetNextChild(child, cookie); - } -} - -void GraphicPacksWindow2::ExpandChildren(const std::vector& ids, size_t& counter) const -{ - std::vector children; - for (const auto& id : ids) - GetChildren(id, children); - - counter += children.size(); - if (counter >= 30 || children.empty()) - return; - - for (const auto& id : ids) - { - if(id != m_graphic_pack_tree->GetRootItem() && m_graphic_pack_tree->HasChildren(id)) - m_graphic_pack_tree->Expand(id); - } - - ExpandChildren(children, counter); -} - -void GraphicPacksWindow2::RefreshGraphicPacks() -{ - GraphicPack2::ClearGraphicPacks(); - GraphicPack2::LoadAll(); -} - -GraphicPacksWindow2::GraphicPacksWindow2(wxWindow* parent, uint64_t title_id_filter) - : wxDialog(parent, wxID_ANY, _("Graphic packs"), wxDefaultPosition, wxSize(1000,670), wxCLOSE_BOX | wxCLIP_CHILDREN | wxCAPTION | wxRESIZE_BORDER), - m_installed_games(CafeTitleList::GetAllTitleIds()) -{ - if (title_id_filter != 0) - m_filter = fmt::format("{:x}", title_id_filter); - - m_filter_installed_games = !m_installed_games.empty(); - - SetIcon(wxICON(X_BOX)); - SetMinSize(wxSize(500, 400)); - auto main_sizer = new wxBoxSizer(wxVERTICAL); - - m_splitter_window = new wxSplitterWindow(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxSP_3D); - m_splitter_window->Bind(wxEVT_SIZE, &GraphicPacksWindow2::OnSizeChanged, this); - m_splitter_window->Bind(wxEVT_SPLITTER_SASH_POS_CHANGED, &GraphicPacksWindow2::SashPositionChanged, this); - - // left side - auto left_panel = new wxPanel(m_splitter_window); - { - auto sizer = new wxBoxSizer(wxVERTICAL); - - wxFlexGridSizer* filter_row = new wxFlexGridSizer(0, 3, 0, 0); - filter_row->AddGrowableCol(1); - filter_row->SetFlexibleDirection(wxBOTH); - filter_row->SetNonFlexibleGrowMode(wxFLEX_GROWMODE_SPECIFIED); - - const auto text = new wxStaticText(left_panel, wxID_ANY, _("Filter")); - text->Wrap(-1); - filter_row->Add(text, 0, wxALIGN_CENTER_VERTICAL | wxALL, 5); - - m_filter_text = new wxTextCtrl(left_panel, wxID_ANY, wxString::FromUTF8(m_filter)); - filter_row->Add(m_filter_text, 0, wxALL | wxEXPAND, 5); - m_filter_text->Bind(wxEVT_COMMAND_TEXT_UPDATED, &GraphicPacksWindow2::OnFilterUpdate, this); - - m_installed_games_only = new wxCheckBox(left_panel, wxID_ANY, _("Installed games")); - m_installed_games_only->SetValue(m_filter_installed_games); - filter_row->Add(m_installed_games_only, 0, wxALL | wxEXPAND, 5); - m_installed_games_only->Bind(wxEVT_CHECKBOX, &GraphicPacksWindow2::OnInstalledGamesChanged, this); - if (m_installed_games.empty()) - m_installed_games_only->Disable(); - - sizer->Add(filter_row, 0, wxEXPAND, 5); - - m_graphic_pack_tree = new wxCheckTree(left_panel, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTR_DEFAULT_STYLE | wxTR_HIDE_ROOT); - m_graphic_pack_tree->Bind(wxEVT_TREE_SEL_CHANGED, &GraphicPacksWindow2::OnTreeSelectionChanged, this); - m_graphic_pack_tree->Bind(wxEVT_CHECKTREE_CHOICE, &GraphicPacksWindow2::OnTreeChoiceChanged, this); - //m_graphic_pack_tree->SetMinSize(wxSize(600, 400)); - sizer->Add(m_graphic_pack_tree, 1, wxEXPAND | wxALL, 5); - - left_panel->SetSizerAndFit(sizer); - } - - // right side - m_right_panel = new wxPanel(m_splitter_window, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL | wxNO_BORDER | wxFULL_REPAINT_ON_RESIZE); - { - auto* sizer = new wxBoxSizer(wxVERTICAL); - { - m_gp_options = new wxScrolled(m_right_panel, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL | wxNO_BORDER | wxVSCROLL); - m_gp_options->SetScrollRate(-1, 10); - - auto* inner_sizer = new wxBoxSizer(wxVERTICAL); - { - auto* box = new wxStaticBox(m_gp_options, wxID_ANY, _("Graphic pack")); - auto* box_sizer = new wxStaticBoxSizer(box, wxVERTICAL); - - m_graphic_pack_name = new wxStaticText(box, wxID_ANY, wxEmptyString); - box_sizer->Add(m_graphic_pack_name, 1, wxEXPAND | wxALL, 5); - - inner_sizer->Add(box_sizer, 0, wxEXPAND | wxALL, 5); - } - - { - auto* box = new wxStaticBox(m_gp_options, wxID_ANY, _("Description")); - auto* box_sizer = new wxStaticBoxSizer(box, wxVERTICAL); - - m_graphic_pack_description = new wxStaticText(box, wxID_ANY, wxEmptyString); - box_sizer->Add(m_graphic_pack_description, 1, wxEXPAND | wxALL, 5); - - inner_sizer->Add(box_sizer, 0, wxEXPAND | wxALL, 5); - } - - m_preset_sizer = new wxBoxSizer(wxVERTICAL); - inner_sizer->Add(m_preset_sizer, 0, wxEXPAND, 0); - - { - auto* box = new wxStaticBox(m_gp_options, wxID_ANY, _("Control")); - auto* box_sizer = new wxStaticBoxSizer(box, wxHORIZONTAL); - - m_reload_shaders = new wxButton(box, wxID_ANY, _("Reload edited shaders")); - m_reload_shaders->Bind(wxEVT_BUTTON, &GraphicPacksWindow2::OnReloadShaders, this); - m_reload_shaders->Disable(); - box_sizer->Add(m_reload_shaders, 0, wxEXPAND | wxALL, 5); - - inner_sizer->Add(box_sizer, 0, wxEXPAND | wxALL, 5); - } - - inner_sizer->Add(new wxStaticText(m_gp_options, wxID_ANY, wxEmptyString), 1, wxALL | wxEXPAND, 5); - - m_gp_options->SetSizerAndFit(inner_sizer); - m_gp_options->Hide(); - - sizer->Add(m_gp_options, 1, wxEXPAND | wxRESERVE_SPACE_EVEN_IF_HIDDEN); - } - - - sizer->Add(new wxStaticLine(m_right_panel, wxID_ANY), 0, wxLEFT|wxRIGHT | wxEXPAND, 3); - - auto* row = new wxBoxSizer(wxHORIZONTAL); - - m_update_graphicPacks = new wxButton(m_right_panel, wxID_ANY, _("Download latest community graphic packs")); - m_update_graphicPacks->Bind(wxEVT_BUTTON, &GraphicPacksWindow2::OnCheckForUpdates, this); - row->Add(m_update_graphicPacks, 0, wxALL | wxALIGN_CENTER_VERTICAL, 5); - - m_download_from_url = new wxHyperlinkCtrl(m_right_panel, wxID_ANY, _("Or download pack from URL..."), _("")); - m_download_from_url->Bind(wxEVT_HYPERLINK, &GraphicPacksWindow2::OnClickCustomDownload, this); - row->Add(m_download_from_url, 0, wxALL | wxALIGN_CENTER_VERTICAL, 5); - - sizer->Add(row, 0, wxALL | wxEXPAND, 5); - - m_right_panel->SetSizerAndFit(sizer); - } - - m_splitter_window->SetMinimumPaneSize(50); - m_splitter_window->SplitVertically(left_panel, m_right_panel, (sint32)(m_ratio * m_splitter_window->GetParent()->GetSize().GetWidth())); - main_sizer->Add(m_splitter_window, 1, wxEXPAND, 5); - - - m_info_bar = new wxInfoBar(this); - m_info_bar->SetShowHideEffects(wxSHOW_EFFECT_BLEND, wxSHOW_EFFECT_BLEND); - m_info_bar->SetEffectDuration(500); - main_sizer->Add(m_info_bar, 0, wxALL | wxEXPAND, 5); - - SetSizer(main_sizer); - - UpdateTitleRunning(CafeSystem::IsTitleRunning()); - FillGraphicPackList(); -} - -void GraphicPacksWindow2::SaveStateToConfig() -{ - auto& data = GetConfigHandle().data(); - data.graphic_pack_entries.clear(); - - for (const auto& gp : GraphicPack2::GetGraphicPacks()) - { - auto filename = _utf8ToPath(gp->GetNormalizedPathString()); - if (gp->IsEnabled()) - { - data.graphic_pack_entries.try_emplace(filename); - auto& it = data.graphic_pack_entries[filename]; - // otherwise store all selected presets - for (const auto& preset : gp->GetActivePresets()) - it.try_emplace(preset->category, preset->name); - } - else if(gp->IsDefaultEnabled()) - { - // save that its disabled - data.graphic_pack_entries.try_emplace(filename); - auto& it = data.graphic_pack_entries[filename]; - it.try_emplace("_disabled", "false"); - } - } - - GetConfigHandle().Save(); -} - -GraphicPacksWindow2::~GraphicPacksWindow2() -{ - m_graphic_pack_tree->Unbind(wxEVT_CHECKTREE_CHOICE, &GraphicPacksWindow2::OnTreeSelectionChanged, this); - m_graphic_pack_tree->Unbind(wxEVT_CHECKTREE_CHOICE, &GraphicPacksWindow2::OnTreeChoiceChanged, this); - // m_active_preset->Unbind(wxEVT_CHOICE, &GraphicPacksWindow2::OnActivePresetChanged, this); - m_reload_shaders->Unbind(wxEVT_BUTTON, &GraphicPacksWindow2::OnReloadShaders, this); - - SaveStateToConfig(); -} - -wxTreeItemId GraphicPacksWindow2::FindTreeItem(const wxTreeItemId& root, const wxString& text) const -{ - wxTreeItemIdValue cookie; - for(auto item = m_graphic_pack_tree->GetFirstChild(root, cookie); item.IsOk(); item = m_graphic_pack_tree->GetNextSibling(item)) - { - if (m_graphic_pack_tree->GetItemText(item) == text) - return item; - } - - return {}; -} - -void GraphicPacksWindow2::LoadPresetSelections(const GraphicPackPtr& gp) -{ - std::vector order; - auto presets = gp->GetCategorizedPresets(order); - for(const auto& category : order) - { - const auto& entry = presets[category]; - - // test if any preset is visible and update its status - if (std::none_of(entry.cbegin(), entry.cend(), [gp](const auto& p) - { - return p->visible; - })) - { - continue; - } - - wxString categoryWxStr = wxString::FromUTF8(category); - wxString label(category.empty() ? _("Active preset") : categoryWxStr); - auto* box = new wxStaticBox(m_preset_sizer->GetContainingWindow(), wxID_ANY, label); - auto* box_sizer = new wxStaticBoxSizer(box, wxVERTICAL); - - auto* preset = new wxChoice(box, wxID_ANY); - preset->SetClientObject(new wxStringClientData(categoryWxStr)); - preset->Bind(wxEVT_CHOICE, &GraphicPacksWindow2::OnActivePresetChanged, this); - - std::optional active_preset; - for (auto& pentry : entry) - { - if (!pentry->visible) - continue; - - preset->AppendString(wxString::FromUTF8(pentry->name)); - if (pentry->active) - active_preset = pentry->name; - } - - if (active_preset) - preset->SetStringSelection(wxString::FromUTF8(active_preset.value())); - else if (preset->GetCount() > 0) - preset->SetSelection(0); - - box_sizer->Add(preset, 1, wxEXPAND | wxALL, 5); - - m_preset_sizer->Add(box_sizer, 0, wxEXPAND | wxALL, 5); - } -} - -void GraphicPacksWindow2::OnTreeSelectionChanged(wxTreeEvent& event) -{ - wxWindowUpdateLocker lock(this); - - bool item_deselected = m_graphic_pack_tree->GetSelection() == m_graphic_pack_tree->GetRootItem(); - if (item_deselected) - return; - - const auto selection = m_graphic_pack_tree->GetSelection(); - if (selection.IsOk()) - { - const auto data = dynamic_cast(m_graphic_pack_tree->GetItemData(selection)); - if (data) - { - if(!m_gp_options->IsShown()) - m_gp_options->Show(); - - const auto& gp = data->GetGraphicPack(); - if (gp != m_shown_graphic_pack) - { - m_preset_sizer->Clear(true); - m_gp_name = gp->GetName(); - m_graphic_pack_name->SetLabel(wxString::FromUTF8(m_gp_name)); - - if (gp->GetDescription().empty()) - m_gp_description = _("This graphic pack has no description").utf8_string(); - else - m_gp_description = gp->GetDescription(); - - m_graphic_pack_description->SetLabel(wxString::FromUTF8(m_gp_description)); - - LoadPresetSelections(gp); - - m_reload_shaders->Enable(gp->HasShaders()); - - m_shown_graphic_pack = gp; - - m_graphic_pack_name->Wrap(m_graphic_pack_name->GetParent()->GetClientSize().GetWidth() - 20); - m_graphic_pack_name->GetGrandParent()->Layout(); - - m_graphic_pack_description->Wrap(m_graphic_pack_description->GetParent()->GetClientSize().GetWidth() - 20); - m_graphic_pack_description->GetGrandParent()->Layout(); - - m_right_panel->FitInside(); - m_right_panel->Layout(); - } - return; - } - } - - m_preset_sizer->Clear(true); - m_graphic_pack_name->SetLabel(wxEmptyString); - m_graphic_pack_description->SetLabel(wxEmptyString); - - m_reload_shaders->Disable(); - - m_shown_graphic_pack.reset(); - - m_gp_options->Hide(); - m_right_panel->FitInside(); - m_right_panel->Layout(); -} - -void GraphicPacksWindow2::OnTreeChoiceChanged(wxTreeEvent& event) -{ - auto item = event.GetItem(); - if (!item.IsOk()) - return; - - const bool state = event.GetExtraLong() != 0; - - const auto data = dynamic_cast(m_graphic_pack_tree->GetItemData(item)); - if (!data) - return; - - auto& graphic_pack = data->GetGraphicPack(); - graphic_pack->SetEnabled(state); - - bool requiresRestart = graphic_pack->RequiresRestart(true, false); - bool isRunning = CafeSystem::IsTitleRunning() && graphic_pack->ContainsTitleId(CafeSystem::GetForegroundTitleId()); - if (isRunning) - { - if (state) - { - GraphicPack2::ActivateGraphicPack(graphic_pack); - if (!requiresRestart) - { - ReloadPack(graphic_pack); - m_graphic_pack_tree->SetItemTextColour(item, m_activated_colour); - } - } - else - { - if (!requiresRestart) - { - DeleteShadersFromRuntimeCache(graphic_pack); - m_graphic_pack_tree->SetItemTextColour(item, m_default_colour); - } - GraphicPack2::DeactivateGraphicPack(graphic_pack); - } - } - - if (!m_info_bar->IsShown() && (isRunning && requiresRestart)) - m_info_bar->ShowMessage(_("Restart of Cemu required for changes to take effect")); - - // also change selection to activated gp - m_graphic_pack_tree->SelectItem(item); -} - -// In some environments with GTK (e.g. a flatpak app with org.freedesktop.Platform 22.08 runtime), -// destroying the event source inside the handler crashes the app. -// As a workaround to that, the wxWindow that needs to be destroyed is hidden and then -// destroyed at a later time, outside the handler. -void GraphicPacksWindow2::ClearPresets() -{ - size_t item_count = m_preset_sizer->GetItemCount(); - std::vector sizers; - sizers.reserve(item_count); - for (size_t i = 0; i < item_count; i++) - sizers.push_back(m_preset_sizer->GetItem(i)->GetSizer()); - - for (auto&& sizer : sizers) - { - auto static_box_sizer = dynamic_cast(sizer); - if (static_box_sizer) - { - wxStaticBox* parent_window = static_box_sizer->GetStaticBox(); - if (parent_window) - { - m_preset_sizer->Detach(sizer); - parent_window->Hide(); - CallAfter([=]() - { - parent_window->DestroyChildren(); - delete static_box_sizer; - }); - } - } - } -} - -void GraphicPacksWindow2::OnActivePresetChanged(wxCommandEvent& event) -{ - if (!m_shown_graphic_pack) - return; - - const auto obj = wxDynamicCast(event.GetEventObject(), wxChoice); - wxASSERT(obj); - const auto string_data = dynamic_cast(obj->GetClientObject()); - wxASSERT(string_data); - const auto preset = obj->GetStringSelection().utf8_string(); - if(m_shown_graphic_pack->SetActivePreset(string_data->GetData().utf8_string(), preset)) - { - wxWindowUpdateLocker lock(this); - ClearPresets(); - LoadPresetSelections(m_shown_graphic_pack); - //m_preset_sizer->GetContainingWindow()->Layout(); - //m_right_panel->FitInside(); - m_right_panel->FitInside(); - m_right_panel->Layout(); - } - - if (!m_shown_graphic_pack->RequiresRestart(false, true)) - ReloadPack(m_shown_graphic_pack); - else if (!m_info_bar->IsShown()) - m_info_bar->ShowMessage(_("Restart of Cemu required for changes to take effect")); -} - -void GraphicPacksWindow2::OnReloadShaders(wxCommandEvent& event) -{ - if (m_shown_graphic_pack) - ReloadPack(m_shown_graphic_pack); -} - -void GraphicPacksWindow2::OnClickCustomDownload(wxCommandEvent& event) -{ - DownloadCustomGraphicPackWindow frame(this); - if (frame.ShowModal() == wxID_OK) - { - RefreshGraphicPacks(); - FillGraphicPackList(); - } -} - -void GraphicPacksWindow2::OnCheckForUpdates(wxCommandEvent& event) -{ - DownloadGraphicPacksWindow frame(this); - SaveStateToConfig(); - const int updateResult = frame.ShowModal(); - if (updateResult == wxID_OK) - { - if (!CafeSystem::IsTitleRunning()) - { - // remember virtual paths of all the enabled packs - std::map previouslyEnabledPacks; - for(auto& it : GraphicPack2::GetGraphicPacks()) - { - if(it->IsEnabled()) - previouslyEnabledPacks.emplace(it->GetNormalizedPathString(), it->GetVirtualPath()); - } - // reload graphic packs - RefreshGraphicPacks(); - FillGraphicPackList(); - // remove packs which are still present - for(auto& it : GraphicPack2::GetGraphicPacks()) - previouslyEnabledPacks.erase(it->GetNormalizedPathString()); - if(!previouslyEnabledPacks.empty()) - { - std::string lost_packs; - for(auto& it : previouslyEnabledPacks) - { - lost_packs.append(it.second); - lost_packs.push_back('\n'); - } - wxString message = _("This update removed or renamed the following graphic packs:"); - message << "\n \n" << wxString::FromUTF8(lost_packs) << " \n" << _("You may need to set them up again."); - wxMessageBox(message, _("Warning"), wxOK | wxCENTRE | wxICON_INFORMATION, this); - } - } - } -} - -void GraphicPacksWindow2::OnSizeChanged(wxSizeEvent& event) -{ - const auto obj = (wxSplitterWindow*)event.GetEventObject(); - wxASSERT(obj); - - const auto width = std::max(obj->GetMinimumPaneSize(), obj->GetParent()->GetClientSize().GetWidth()); - obj->SetSashPosition((sint32)(m_ratio * width)); - - if (!m_gp_name.empty()) - m_graphic_pack_name->SetLabel(wxString::FromUTF8(m_gp_name)); - - if (!m_gp_description.empty()) - m_graphic_pack_description->SetLabel(wxString::FromUTF8(m_gp_description)); - - m_graphic_pack_name->Wrap(m_graphic_pack_name->GetParent()->GetClientSize().GetWidth() - 10); - m_graphic_pack_description->Wrap(m_graphic_pack_description->GetParent()->GetClientSize().GetWidth() - 10); - - event.Skip(); -} - -void GraphicPacksWindow2::SashPositionChanged(wxEvent& event) -{ - const auto obj = (wxSplitterWindow*)event.GetEventObject(); - wxASSERT(obj); - - const auto width = std::max(obj->GetMinimumPaneSize(), obj->GetParent()->GetClientSize().GetWidth()); - m_ratio = (float)obj->GetSashPosition() / width; - event.Skip(); -} - -void GraphicPacksWindow2::OnFilterUpdate(wxEvent& event) -{ - m_filter = m_filter_text->GetValue().utf8_string(); - FillGraphicPackList(); - event.Skip(); -} - -void GraphicPacksWindow2::OnInstalledGamesChanged(wxCommandEvent& event) -{ - m_filter_installed_games = m_installed_games_only->GetValue(); - FillGraphicPackList(); - event.Skip(); -} - -void GraphicPacksWindow2::UpdateTitleRunning(bool running) -{ - m_update_graphicPacks->Enable(!running); - m_download_from_url->Enable(!running); - if(running) - { - m_download_from_url->SetToolTip(_("Graphic packs cannot be updated while a game is running.")); - m_update_graphicPacks->SetToolTip(_("Graphic packs cannot be updated while a game is running.")); - } - else - { - m_update_graphicPacks->SetToolTip(nullptr); - m_download_from_url->SetToolTip(nullptr); - } -} - -void GraphicPacksWindow2::ReloadPack(const GraphicPackPtr& graphic_pack) const -{ - if (graphic_pack->HasShaders() || graphic_pack->HasPatches() || graphic_pack->HasCustomVSyncFrequency()) - { - if (graphic_pack->Reload()) - { - DeleteShadersFromRuntimeCache(graphic_pack); - } - } -} - -void GraphicPacksWindow2::DeleteShadersFromRuntimeCache(const GraphicPackPtr& graphic_pack) const -{ - for (const auto& shader : graphic_pack->GetCustomShaders()) - { - LatteConst::ShaderType shaderType; - if (shader.type == GraphicPack2::GP_SHADER_TYPE::VERTEX) - shaderType = LatteConst::ShaderType::Vertex; - else if (shader.type == GraphicPack2::GP_SHADER_TYPE::GEOMETRY) - shaderType = LatteConst::ShaderType::Geometry; - else if (shader.type == GraphicPack2::GP_SHADER_TYPE::PIXEL) - shaderType = LatteConst::ShaderType::Pixel; - LatteAsyncCommands_queueDeleteShader(shader.shader_base_hash, shader.shader_aux_hash, shaderType); - } -} - diff --git a/src/gui/wxgui/GraphicPacksWindow2.h b/src/gui/wxgui/GraphicPacksWindow2.h deleted file mode 100644 index bf8647de..00000000 --- a/src/gui/wxgui/GraphicPacksWindow2.h +++ /dev/null @@ -1,80 +0,0 @@ -#pragma once - -#include -#include -#include -#include - -#include "wxcomponents/checktree.h" - -#include "Cafe/GraphicPack/GraphicPack2.h" - -class wxSplitterWindow; -class wxPanel; -class wxButton; -class wxChoice; - -class GraphicPacksWindow2 : public wxDialog -{ -public: - GraphicPacksWindow2(wxWindow* parent, uint64_t title_id_filter); - ~GraphicPacksWindow2(); - - static void RefreshGraphicPacks(); - void UpdateTitleRunning(bool running); - -private: - std::string m_filter; - bool m_filter_installed_games; - std::vector m_installed_games; - - void ClearPresets(); - void FillGraphicPackList() const; - void GetChildren(const wxTreeItemId& id, std::vector& children) const; - void ExpandChildren(const std::vector& ids, size_t& counter) const; - - wxSplitterWindow * m_splitter_window; - - wxPanel* m_right_panel; - wxScrolled* m_gp_options; - - wxCheckTree * m_graphic_pack_tree; - wxTextCtrl* m_filter_text; - wxCheckBox* m_installed_games_only; - - wxStaticText* m_graphic_pack_name, *m_graphic_pack_description; - wxBoxSizer* m_preset_sizer; - std::vector m_active_preset; - wxButton* m_reload_shaders; - wxHyperlinkCtrl* m_download_from_url; - wxButton* m_update_graphicPacks; - wxInfoBar* m_info_bar; - - GraphicPackPtr m_shown_graphic_pack; - std::string m_gp_name, m_gp_description; - - float m_ratio = 0.55f; - wxColour m_default_colour = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT); - wxColour m_activated_colour = wxSystemSettings::SelectLightDark(wxColour(0x00, 0xCC, 0x00), wxColour(0x42, 0xB3, 0x42)); - wxColour m_incompatible_colour = wxSystemSettings::SelectLightDark(wxColour(0xCC, 0x00, 0x00), wxColour(0xDE, 0x49, 0x49)); - - wxTreeItemId FindTreeItem(const wxTreeItemId& root, const wxString& text) const; - void LoadPresetSelections(const GraphicPackPtr& gp); - - void OnTreeSelectionChanged(wxTreeEvent& event); - void OnTreeChoiceChanged(wxTreeEvent& event); - void OnActivePresetChanged(wxCommandEvent& event); - void OnReloadShaders(wxCommandEvent& event); - void OnClickCustomDownload(wxCommandEvent& event); - void OnCheckForUpdates(wxCommandEvent& event); - void OnSizeChanged(wxSizeEvent& event); - void SashPositionChanged(wxEvent& event); - void OnFilterUpdate(wxEvent& event); - void OnInstalledGamesChanged(wxCommandEvent& event); - - void SaveStateToConfig(); - - void ReloadPack(const GraphicPackPtr& graphic_pack) const; - void DeleteShadersFromRuntimeCache(const GraphicPackPtr& graphic_pack) const; - -}; \ No newline at end of file diff --git a/src/gui/wxgui/LoggingWindow.cpp b/src/gui/wxgui/LoggingWindow.cpp deleted file mode 100644 index 6c6ca6c3..00000000 --- a/src/gui/wxgui/LoggingWindow.cpp +++ /dev/null @@ -1,85 +0,0 @@ -#include "wxgui/LoggingWindow.h" - -#include "Cemu/Logging/CemuLogging.h" -#include "wxgui/helpers/wxLogEvent.h" - -#include -#include -#include - -wxDEFINE_EVENT(EVT_LOG, wxLogEvent); - -LoggingWindow::LoggingWindow(wxFrame* parent) - : wxFrame(parent, wxID_ANY, _("Logging window"), wxDefaultPosition, wxSize(800, 600), wxDEFAULT_FRAME_STYLE | wxTAB_TRAVERSAL) -{ - auto* sizer = new wxBoxSizer( wxVERTICAL ); - { - auto filter_row = new wxBoxSizer( wxHORIZONTAL ); - - filter_row->Add(new wxStaticText( this, wxID_ANY, _("Filter")), 0, wxALIGN_CENTER_VERTICAL|wxALL, 5 ); - - wxString choices[] = {"Unsupported APIs calls", "Coreinit Logging", "Coreinit File-Access", "Coreinit Thread-Synchronization", "Coreinit Memory", "Coreinit MP", "Coreinit Thread", "nn::nfp", "GX2", "Audio", "Input", "Socket", "Save", "H264", "Graphic pack patches", "Texture cache", "Texture readback", "OpenGL debug output", "Vulkan validation layer", "Metal debug output"}; - m_filter = new wxComboBox(this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, std::size(choices), choices); - m_filter->Bind(wxEVT_COMBOBOX, &LoggingWindow::OnFilterChange, this); - m_filter->Bind(wxEVT_TEXT, &LoggingWindow::OnFilterChange, this); - filter_row->Add(m_filter, 1, wxALL, 5 ); - - m_filter_message = new wxCheckBox(this, wxID_ANY, _("Filter messages")); - m_filter_message->Bind(wxEVT_CHECKBOX, &LoggingWindow::OnFilterMessageChange, this); - filter_row->Add(m_filter_message, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5 ); - - sizer->Add( filter_row, 0, wxEXPAND, 5 ); - } - - m_log_list = new wxLogCtrl(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxScrolledWindowStyle, true); - sizer->Add( m_log_list, 1, wxALL | wxEXPAND, 5 ); - - this->SetSizer( sizer ); - this->Layout(); - - this->Bind(EVT_LOG, &LoggingWindow::OnLogMessage, this); - - cemuLog_setCallbacks(this); -} - -LoggingWindow::~LoggingWindow() -{ - this->Unbind(EVT_LOG, &LoggingWindow::OnLogMessage, this); - - cemuLog_clearCallbacks(); -} - -void LoggingWindow::Log(std::string_view filter, std::string_view message) -{ - wxLogEvent event(std::string {filter}, std::string{ message }); - OnLogMessage(event); - - //const auto log_event = new wxLogEvent(filter, message); - //wxQueueEvent(s_instance, log_event); -} - -void LoggingWindow::Log(std::string_view filter, std::wstring_view message) -{ - wxLogEvent event(std::string {filter}, std::wstring{ message }); - OnLogMessage(event); - - //const auto log_event = new wxLogEvent(filter, message); - //wxQueueEvent(s_instance, log_event); -} - -void LoggingWindow::OnLogMessage(wxLogEvent& event) -{ - m_log_list->PushEntry(event.GetFilter(), event.GetMessage()); -} - -void LoggingWindow::OnFilterChange(wxCommandEvent& event) -{ - m_log_list->SetActiveFilter(m_filter->GetValue().utf8_string()); - event.Skip(); -} - -void LoggingWindow::OnFilterMessageChange(wxCommandEvent& event) -{ - m_log_list->SetFilterMessage(m_filter_message->GetValue()); - event.Skip(); -} diff --git a/src/gui/wxgui/LoggingWindow.h b/src/gui/wxgui/LoggingWindow.h deleted file mode 100644 index d1ba2689..00000000 --- a/src/gui/wxgui/LoggingWindow.h +++ /dev/null @@ -1,29 +0,0 @@ -#pragma once - -#include "Cemu/Logging/CemuLogging.h" - -#include -#include -#include -#include "wxgui/components/wxLogCtrl.h" - -class wxLogEvent; - -class LoggingWindow : public wxFrame, public LoggingCallbacks -{ - public: - LoggingWindow(wxFrame* parent); - ~LoggingWindow(); - - void Log(std::string_view filter, std::string_view message) override; - void Log(std::string_view filter, std::wstring_view message) override; - - private: - void OnLogMessage(wxLogEvent& event); - void OnFilterChange(wxCommandEvent& event); - void OnFilterMessageChange(wxCommandEvent& event); - - wxComboBox* m_filter; - wxLogCtrl* m_log_list; - wxCheckBox* m_filter_message; -}; diff --git a/src/gui/wxgui/MainWindow.cpp b/src/gui/wxgui/MainWindow.cpp deleted file mode 100644 index db4dfbe9..00000000 --- a/src/gui/wxgui/MainWindow.cpp +++ /dev/null @@ -1,2516 +0,0 @@ -#include "MainWindow.h" - -// subwindows -#include "TitleManager.h" -#include "GeneralSettings2.h" -#include "GameUpdateWindow.h" -#include "CemuUpdateWindow.h" -#include "GraphicPacksWindow2.h" -#include "AudioDebuggerWindow.h" -#include "input/InputSettings2.h" -#include "input/HotkeySettings.h" -#include "debugger/DebuggerWindow2.h" -#include "EmulatedUSBDevices/EmulatedUSBDeviceFrame.h" -#include "windows/PPCThreadsViewer/DebugPPCThreadsWindow.h" -#include "windows/TextureRelationViewer/TextureRelationWindow.h" - -//wxgui + misc. -#include "wxgui.h" -#include "wxCemuConfig.h" -#include "interface/WindowSystem.h" -#include "wxHelper.h" -#include "helpers/wxHelpers.h" -#include "PadViewFrame.h" - -#if BOOST_OS_LINUX || BOOST_OS_MACOS || BOOST_OS_BSD -#include "resource/embedded/resources.h" -#endif - -// settings -#include "config/CemuConfig.h" -#include "config/LaunchSettings.h" -#include "config/ActiveSettings.h" - -// External functionality headers -#include "input/InputManager.h" -#include "Cafe/TitleList/TitleList.h" -#include "Cemu/DiscordPresence/DiscordPresence.h" -#include "util/ScreenSaver/ScreenSaver.h" -#include "util/helpers/SystemException.h" -#include "Cafe/HW/Latte/Renderer/Vulkan/VsyncDriver.h" -#if BOOST_OS_LINUX && defined(ENABLE_FERAL_GAMEMODE) -#include -#endif -#if ( BOOST_OS_LINUX || BOOST_OS_BSD ) && HAS_WAYLAND -#include "helpers/wxWayland.h" -#endif - -// Renderer Canvasses -#ifdef ENABLE_OPENGL -#include "canvas/OpenGLCanvas.h" -#endif -#ifdef ENABLE_VULKAN -#include "canvas/VulkanCanvas.h" -#endif -#ifdef ENABLE_METAL -#include "canvas/MetalCanvas.h" -#include "Cafe/HW/Latte/Renderer/Metal/MetalRenderer.h" -#endif - -//Cafe libs -#include "Cafe/OS/libs/nfc/nfc.h" -#include "Cafe/OS/libs/swkbd/swkbd.h" - -#include "Cafe/HW/Latte/Renderer/Renderer.h" // For renderer API checks - -extern WindowSystem::WindowInfo g_window_info; -extern std::shared_mutex g_mutex; - -enum -{ - // ui elements - MAINFRAME_GAMELIST_ID = 20000, //wxID_HIGHEST + 1, - // file - MAINFRAME_MENU_ID_FILE_LOAD = 20100, - MAINFRAME_MENU_ID_FILE_INSTALL_UPDATE, - MAINFRAME_MENU_ID_FILE_OPEN_CEMU_FOLDER, - MAINFRAME_MENU_ID_FILE_OPEN_MLC_FOLDER, - MAINFRAME_MENU_ID_FILE_OPEN_SHADERCACHE_FOLDER, - MAINFRAME_MENU_ID_FILE_CLEAR_SPOTPASS_CACHE, - MAINFRAME_MENU_ID_FILE_EXIT, - MAINFRAME_MENU_ID_FILE_END_EMULATION, - MAINFRAME_MENU_ID_FILE_RECENT_0, - MAINFRAME_MENU_ID_FILE_RECENT_LAST = MAINFRAME_MENU_ID_FILE_RECENT_0 + 15, - // options - MAINFRAME_MENU_ID_OPTIONS_FULLSCREEN = 20200, - MAINFRAME_MENU_ID_OPTIONS_SECOND_WINDOW_PADVIEW, - MAINFRAME_MENU_ID_OPTIONS_GRAPHIC, - MAINFRAME_MENU_ID_OPTIONS_GRAPHIC_PACKS2, - MAINFRAME_MENU_ID_OPTIONS_GENERAL, - MAINFRAME_MENU_ID_OPTIONS_GENERAL2, - MAINFRAME_MENU_ID_OPTIONS_AUDIO, - MAINFRAME_MENU_ID_OPTIONS_INPUT, - MAINFRAME_MENU_ID_OPTIONS_HOTKEY, - MAINFRAME_MENU_ID_OPTIONS_MAC_SETTINGS, - // options -> account - MAINFRAME_MENU_ID_OPTIONS_ACCOUNT_1 = 20350, - MAINFRAME_MENU_ID_OPTIONS_ACCOUNT_12 = 20350 + 11, - - // options -> system language - MAINFRAME_MENU_ID_OPTIONS_LANGUAGE_JAPANESE = 20500, - MAINFRAME_MENU_ID_OPTIONS_LANGUAGE_ENGLISH, - MAINFRAME_MENU_ID_OPTIONS_LANGUAGE_FRENCH, - MAINFRAME_MENU_ID_OPTIONS_LANGUAGE_GERMAN, - MAINFRAME_MENU_ID_OPTIONS_LANGUAGE_ITALIAN, - MAINFRAME_MENU_ID_OPTIONS_LANGUAGE_SPANISH, - MAINFRAME_MENU_ID_OPTIONS_LANGUAGE_CHINESE, - MAINFRAME_MENU_ID_OPTIONS_LANGUAGE_KOREAN, - MAINFRAME_MENU_ID_OPTIONS_LANGUAGE_DUTCH, - MAINFRAME_MENU_ID_OPTIONS_LANGUAGE_PORTUGUESE, - MAINFRAME_MENU_ID_OPTIONS_LANGUAGE_RUSSIAN, - MAINFRAME_MENU_ID_OPTIONS_LANGUAGE_TAIWANESE, - // tools - MAINFRAME_MENU_ID_TOOLS_MEMORY_SEARCHER = 20600, - MAINFRAME_MENU_ID_TOOLS_TITLE_MANAGER, - MAINFRAME_MENU_ID_TOOLS_DOWNLOAD_MANAGER, - MAINFRAME_MENU_ID_TOOLS_EMULATED_USB_DEVICES, - // cpu - // cpu->timer speed - MAINFRAME_MENU_ID_TIMER_SPEED_1X = 20700, - MAINFRAME_MENU_ID_TIMER_SPEED_2X = 20701, - MAINFRAME_MENU_ID_TIMER_SPEED_4X = 20702, - MAINFRAME_MENU_ID_TIMER_SPEED_8X = 20703, - MAINFRAME_MENU_ID_TIMER_SPEED_05X = 20704, - MAINFRAME_MENU_ID_TIMER_SPEED_025X = 20705, - MAINFRAME_MENU_ID_TIMER_SPEED_0125X = 20706, - - // nfc->Touch NFC file - MAINFRAME_MENU_ID_NFC_TOUCH_NFC_FILE = 21000, - MAINFRAME_MENU_ID_NFC_RECENT_0, - MAINFRAME_MENU_ID_NFC_RECENT_LAST = MAINFRAME_MENU_ID_NFC_RECENT_0 + 15, - // debug - MAINFRAME_MENU_ID_DEBUG_RENDER_UPSIDE_DOWN = 21100, - MAINFRAME_MENU_ID_DEBUG_VIEW_LOGGING_WINDOW, - MAINFRAME_MENU_ID_DEBUG_TOGGLE_GDB_STUB, - MAINFRAME_MENU_ID_DEBUG_VIEW_PPC_THREADS, - MAINFRAME_MENU_ID_DEBUG_VIEW_PPC_DEBUGGER, - MAINFRAME_MENU_ID_DEBUG_VIEW_AUDIO_DEBUGGER, - MAINFRAME_MENU_ID_DEBUG_VIEW_TEXTURE_RELATIONS, - MAINFRAME_MENU_ID_DEBUG_AUDIO_AUX_ONLY, - MAINFRAME_MENU_ID_DEBUG_VK_ACCURATE_BARRIERS, - MAINFRAME_MENU_ID_DEBUG_GPU_CAPTURE, - - // debug->logging - MAINFRAME_MENU_ID_DEBUG_LOGGING_MESSAGE = 21499, - MAINFRAME_MENU_ID_DEBUG_LOGGING0 = 21500, - MAINFRAME_MENU_ID_DEBUG_ADVANCED_PPC_INFO = 21599, - // debug->dump - MAINFRAME_MENU_ID_DEBUG_DUMP_TEXTURES = 21600, - MAINFRAME_MENU_ID_DEBUG_DUMP_SHADERS, - MAINFRAME_MENU_ID_DEBUG_DUMP_RECOMPILER_FUNCTIONS, - MAINFRAME_MENU_ID_DEBUG_DUMP_RAM, - MAINFRAME_MENU_ID_DEBUG_DUMP_RPX, - MAINFRAME_MENU_ID_DEBUG_DUMP_FST, - MAINFRAME_MENU_ID_DEBUG_DUMP_CURL_REQUESTS, - // help - MAINFRAME_MENU_ID_HELP_ABOUT = 21700, - MAINFRAME_MENU_ID_HELP_UPDATE, - // custom - MAINFRAME_ID_TIMER1 = 21800, -}; - -wxDEFINE_EVENT(wxEVT_SET_WINDOW_TITLE, wxCommandEvent); -wxDEFINE_EVENT(wxEVT_REQUEST_GAMELIST_REFRESH, wxCommandEvent); -wxDEFINE_EVENT(wxEVT_LAUNCH_GAME, wxLaunchGameEvent); -wxDEFINE_EVENT(wxEVT_REQUEST_RECREATE_CANVAS, wxCommandEvent); -wxDEFINE_EVENT(wxEVT_REQUEST_GAME_EXIT, wxCommandEvent); - -wxBEGIN_EVENT_TABLE(MainWindow, wxFrame) -EVT_TIMER(MAINFRAME_ID_TIMER1, MainWindow::OnTimer) -EVT_CLOSE(MainWindow::OnClose) -EVT_SIZE(MainWindow::OnSizeEvent) -EVT_DPI_CHANGED(MainWindow::OnDPIChangedEvent) -EVT_MOVE(MainWindow::OnMove) -// file menu -EVT_MENU(MAINFRAME_MENU_ID_FILE_LOAD, MainWindow::OnFileMenu) -EVT_MENU(MAINFRAME_MENU_ID_FILE_INSTALL_UPDATE, MainWindow::OnInstallUpdate) -EVT_MENU(MAINFRAME_MENU_ID_FILE_OPEN_CEMU_FOLDER, MainWindow::OnOpenFolder) -EVT_MENU(MAINFRAME_MENU_ID_FILE_OPEN_MLC_FOLDER, MainWindow::OnOpenFolder) -EVT_MENU(MAINFRAME_MENU_ID_FILE_OPEN_SHADERCACHE_FOLDER, MainWindow::OnOpenFolder) -EVT_MENU(MAINFRAME_MENU_ID_FILE_CLEAR_SPOTPASS_CACHE, MainWindow::OnClearSpotPassCache) -EVT_MENU(MAINFRAME_MENU_ID_FILE_EXIT, MainWindow::OnFileExit) -EVT_MENU(MAINFRAME_MENU_ID_FILE_END_EMULATION, MainWindow::OnFileMenu) -EVT_MENU_RANGE(MAINFRAME_MENU_ID_FILE_RECENT_0 + 0, MAINFRAME_MENU_ID_FILE_RECENT_LAST, MainWindow::OnFileMenu) -// options -> region menu -EVT_MENU_RANGE(MAINFRAME_MENU_ID_OPTIONS_ACCOUNT_1, MAINFRAME_MENU_ID_OPTIONS_ACCOUNT_12, MainWindow::OnAccountSelect) -EVT_MENU_RANGE(MAINFRAME_MENU_ID_OPTIONS_LANGUAGE_JAPANESE, MAINFRAME_MENU_ID_OPTIONS_LANGUAGE_TAIWANESE, MainWindow::OnConsoleLanguage) -// options menu -EVT_MENU(MAINFRAME_MENU_ID_OPTIONS_FULLSCREEN, MainWindow::OnOptionsInput) -EVT_MENU(MAINFRAME_MENU_ID_OPTIONS_SECOND_WINDOW_PADVIEW, MainWindow::OnOptionsInput) -EVT_MENU(MAINFRAME_MENU_ID_OPTIONS_GRAPHIC, MainWindow::OnOptionsInput) -EVT_MENU(MAINFRAME_MENU_ID_OPTIONS_GRAPHIC_PACKS2, MainWindow::OnOptionsInput) -EVT_MENU(MAINFRAME_MENU_ID_OPTIONS_GENERAL, MainWindow::OnOptionsInput) -EVT_MENU(MAINFRAME_MENU_ID_OPTIONS_GENERAL2, MainWindow::OnOptionsInput) -EVT_MENU(MAINFRAME_MENU_ID_OPTIONS_AUDIO, MainWindow::OnOptionsInput) -EVT_MENU(MAINFRAME_MENU_ID_OPTIONS_INPUT, MainWindow::OnOptionsInput) -EVT_MENU(MAINFRAME_MENU_ID_OPTIONS_HOTKEY, MainWindow::OnOptionsInput) -EVT_MENU(MAINFRAME_MENU_ID_OPTIONS_MAC_SETTINGS, MainWindow::OnOptionsInput) -// tools menu -EVT_MENU(MAINFRAME_MENU_ID_TOOLS_MEMORY_SEARCHER, MainWindow::OnToolsInput) -EVT_MENU(MAINFRAME_MENU_ID_TOOLS_TITLE_MANAGER, MainWindow::OnToolsInput) -EVT_MENU(MAINFRAME_MENU_ID_TOOLS_DOWNLOAD_MANAGER, MainWindow::OnToolsInput) -EVT_MENU(MAINFRAME_MENU_ID_TOOLS_EMULATED_USB_DEVICES, MainWindow::OnToolsInput) -// cpu menu -EVT_MENU(MAINFRAME_MENU_ID_TIMER_SPEED_8X, MainWindow::OnDebugSetting) -EVT_MENU(MAINFRAME_MENU_ID_TIMER_SPEED_4X, MainWindow::OnDebugSetting) -EVT_MENU(MAINFRAME_MENU_ID_TIMER_SPEED_2X, MainWindow::OnDebugSetting) -EVT_MENU(MAINFRAME_MENU_ID_TIMER_SPEED_1X, MainWindow::OnDebugSetting) -EVT_MENU(MAINFRAME_MENU_ID_TIMER_SPEED_05X, MainWindow::OnDebugSetting) -EVT_MENU(MAINFRAME_MENU_ID_TIMER_SPEED_025X, MainWindow::OnDebugSetting) -EVT_MENU(MAINFRAME_MENU_ID_TIMER_SPEED_0125X, MainWindow::OnDebugSetting) -// nfc menu -EVT_MENU(MAINFRAME_MENU_ID_NFC_TOUCH_NFC_FILE, MainWindow::OnNFCMenu) -EVT_MENU_RANGE(MAINFRAME_MENU_ID_NFC_RECENT_0 + 0, MAINFRAME_MENU_ID_NFC_RECENT_LAST, MainWindow::OnNFCMenu) -// debug -> logging menu -EVT_MENU_RANGE(MAINFRAME_MENU_ID_DEBUG_LOGGING0 + 0, MAINFRAME_MENU_ID_DEBUG_LOGGING0 + 98, MainWindow::OnDebugLoggingToggleFlagGeneric) -EVT_MENU(MAINFRAME_MENU_ID_DEBUG_ADVANCED_PPC_INFO, MainWindow::OnPPCInfoToggle) -// debug -> dump menu -EVT_MENU(MAINFRAME_MENU_ID_DEBUG_DUMP_TEXTURES, MainWindow::OnDebugDumpGeneric) -EVT_MENU(MAINFRAME_MENU_ID_DEBUG_DUMP_SHADERS, MainWindow::OnDebugDumpGeneric) -EVT_MENU(MAINFRAME_MENU_ID_DEBUG_DUMP_RECOMPILER_FUNCTIONS, MainWindow::OnDebugDumpGeneric) -EVT_MENU(MAINFRAME_MENU_ID_DEBUG_DUMP_CURL_REQUESTS, MainWindow::OnDebugSetting) -// debug -> Other options -EVT_MENU(MAINFRAME_MENU_ID_DEBUG_RENDER_UPSIDE_DOWN, MainWindow::OnDebugSetting) -EVT_MENU(MAINFRAME_MENU_ID_DEBUG_AUDIO_AUX_ONLY, MainWindow::OnDebugSetting) -EVT_MENU(MAINFRAME_MENU_ID_DEBUG_VK_ACCURATE_BARRIERS, MainWindow::OnDebugSetting) -EVT_MENU(MAINFRAME_MENU_ID_DEBUG_GPU_CAPTURE, MainWindow::OnDebugSetting) -EVT_MENU(MAINFRAME_MENU_ID_DEBUG_DUMP_RAM, MainWindow::OnDebugSetting) -EVT_MENU(MAINFRAME_MENU_ID_DEBUG_DUMP_RPX, MainWindow::OnDebugSetting) -EVT_MENU(MAINFRAME_MENU_ID_DEBUG_DUMP_FST, MainWindow::OnDebugSetting) -// debug -> View ... -EVT_MENU(MAINFRAME_MENU_ID_DEBUG_VIEW_LOGGING_WINDOW, MainWindow::OnLoggingWindow) -EVT_MENU(MAINFRAME_MENU_ID_DEBUG_TOGGLE_GDB_STUB, MainWindow::OnGDBStubToggle) -EVT_MENU(MAINFRAME_MENU_ID_DEBUG_VIEW_PPC_THREADS, MainWindow::OnDebugViewPPCThreads) -EVT_MENU(MAINFRAME_MENU_ID_DEBUG_VIEW_PPC_DEBUGGER, MainWindow::OnDebugViewPPCDebugger) -EVT_MENU(MAINFRAME_MENU_ID_DEBUG_VIEW_AUDIO_DEBUGGER, MainWindow::OnDebugViewAudioDebugger) -EVT_MENU(MAINFRAME_MENU_ID_DEBUG_VIEW_TEXTURE_RELATIONS, MainWindow::OnDebugViewTextureRelations) -// help menu -EVT_MENU(MAINFRAME_MENU_ID_HELP_ABOUT, MainWindow::OnHelpAbout) -EVT_MENU(MAINFRAME_MENU_ID_HELP_UPDATE, MainWindow::OnHelpUpdate) -// misc -EVT_COMMAND(wxID_ANY, wxEVT_REQUEST_GAMELIST_REFRESH, MainWindow::OnRequestGameListRefresh) - -EVT_COMMAND(wxID_ANY, wxEVT_GAMELIST_BEGIN_UPDATE, MainWindow::OnGameListBeginUpdate) -EVT_COMMAND(wxID_ANY, wxEVT_GAMELIST_END_UPDATE, MainWindow::OnGameListEndUpdate) -EVT_COMMAND(wxID_ANY, wxEVT_ACCOUNTLIST_REFRESH, MainWindow::OnAccountListRefresh) -EVT_COMMAND(wxID_ANY, wxEVT_SET_WINDOW_TITLE, MainWindow::OnSetWindowTitle) -EVT_COMMAND(wxID_ANY, wxEVT_REQUEST_RECREATE_CANVAS, MainWindow::OnRequestRecreateCanvas) -EVT_COMMAND(wxID_ANY, wxEVT_REQUEST_GAME_EXIT, MainWindow::OnRequestGameExit) - -wxEND_EVENT_TABLE() - -class wxGameDropTarget : public wxFileDropTarget -{ -public: - wxGameDropTarget(MainWindow* window) : m_window(window) {} - bool OnDropFiles(wxCoord x, wxCoord y, const wxArrayString& filenames) override - { - if(!m_window->IsGameLaunched() && filenames.GetCount() == 1) - return m_window->FileLoad(_utf8ToPath(filenames[0].utf8_string()), wxLaunchGameEvent::INITIATED_BY::DRAG_AND_DROP); - - return false; - } - -private: - MainWindow* m_window; -}; - -class wxAmiiboDropTarget : public wxFileDropTarget -{ -public: - wxAmiiboDropTarget(MainWindow* window) : m_window(window) {} - bool OnDropFiles(wxCoord x, wxCoord y, const wxArrayString& filenames) override - { - if (!m_window->IsGameLaunched() || filenames.GetCount() != 1) - return false; - uint32 nfcError; - std::string path = filenames[0].utf8_string(); - if (nfc::TouchTagFromFile(_utf8ToPath(path), &nfcError)) - { - GetWxGUIConfig().AddRecentNfcFile(path); - m_window->UpdateNFCMenu(); - return true; - } - else - { - if (nfcError == NFC_TOUCH_TAG_ERROR_NO_ACCESS) - wxMessageBox(_("Cannot open file"), _("Error"), wxOK | wxCENTRE | wxICON_ERROR); - else if (nfcError == NFC_TOUCH_TAG_ERROR_INVALID_FILE_FORMAT) - wxMessageBox(_("Not a valid NFC file"), _("Error"), wxOK | wxCENTRE | wxICON_ERROR); - return false; - } - } - -private: - MainWindow* m_window; -}; - -MainWindow::MainWindow() - : wxFrame(nullptr, wxID_ANY, GetInitialWindowTitle(), wxDefaultPosition, wxSize(1280, 720), wxMINIMIZE_BOX | wxMAXIMIZE_BOX | wxSYSTEM_MENU | wxCAPTION | wxCLOSE_BOX | wxCLIP_CHILDREN | wxRESIZE_BORDER) -{ -#ifdef __WXMAC__ - // Not necessary to set wxApp::s_macExitMenuItemId as automatically handled - wxApp::s_macAboutMenuItemId = MAINFRAME_MENU_ID_HELP_ABOUT; - wxApp::s_macPreferencesMenuItemId = MAINFRAME_MENU_ID_OPTIONS_MAC_SETTINGS; -#endif - g_window_info.window_main = initHandleContextFromWxWidgetsWindow(this); - g_mainFrame = this; - CafeSystem::SetImplementation(this); - - RecreateMenu(); - SetClientSize(1280, 720); - SetIcon(wxICON(M_WND_ICON128)); - -#if BOOST_OS_MACOS - this->EnableFullScreenView(true); -#endif - -#if BOOST_OS_WINDOWS - HICON hWindowIcon = (HICON)LoadImageA(NULL, "M_WND_ICON16", IMAGE_ICON, 16, 16, LR_LOADFROMFILE); - SendMessage(this->GetHWND(), WM_SETICON, ICON_SMALL, (LPARAM)hWindowIcon); -#endif - - auto* main_sizer = new wxBoxSizer(wxVERTICAL); - auto load_file = LaunchSettings::GetLoadFile(); - auto load_title_id = LaunchSettings::GetLoadTitleID(); - bool quick_launch = false; - - if (load_file) - { - MainWindow::RequestLaunchGame(load_file.value(), wxLaunchGameEvent::INITIATED_BY::COMMAND_LINE); - quick_launch = true; - } - else if (load_title_id) - { - TitleInfo info; - TitleId baseId; - if (CafeTitleList::FindBaseTitleId(load_title_id.value(), baseId) && CafeTitleList::GetFirstByTitleId(baseId, info)) - { - MainWindow::RequestLaunchGame(info.GetPath(), wxLaunchGameEvent::INITIATED_BY::COMMAND_LINE); - quick_launch = true; - } - else - { - wxString errorMsg = fmt::format("Title ID {:016x} not found", load_title_id.value()); - wxMessageBox(errorMsg, _("Error"), wxOK | wxCENTRE | wxICON_ERROR); - - } - } - SetSizer(main_sizer); - if (!quick_launch) - { - CreateGameListAndStatusBar(); - } - else - { - // launching game via -g or -t option. Don't set up or load game list - m_game_list = nullptr; - m_info_bar = nullptr; - } - SetSizer(main_sizer); - - m_last_mouse_move_time = std::chrono::steady_clock::now(); - - m_timer = new wxTimer(this, MAINFRAME_ID_TIMER1); - m_timer->Start(500); - - LoadSettings(); - - #ifdef ENABLE_DISCORD_RPC - if (GetWxGUIConfig().use_discord_presence) - m_discord = std::make_unique(); - #endif - - Bind(wxEVT_OPEN_GRAPHIC_PACK, &MainWindow::OnGraphicWindowOpen, this); - Bind(wxEVT_LAUNCH_GAME, &MainWindow::OnLaunchFromFile, this); - - if (LaunchSettings::GDBStubEnabled()) - { - g_gdbstub = std::make_unique(GetConfig().gdb_port); - } - - if (LaunchSettings::OpenDebuggerEnabled()) - OpenPPCDebugger(); -} - -MainWindow::~MainWindow() -{ - if (m_padView) - { - m_padView->Destroy(); - m_padView = nullptr; - } - - m_timer->Stop(); - - std::unique_lock lock(g_mutex); - g_mainFrame = nullptr; -} - -void MainWindow::CreateGameListAndStatusBar() -{ - if(m_main_panel) - return; // already displayed - m_main_panel = new wxPanel(this); - auto* sizer = new wxBoxSizer(wxVERTICAL); - // game list - m_game_list = new wxGameList(m_main_panel, MAINFRAME_GAMELIST_ID); - m_game_list->Bind(wxEVT_OPEN_SETTINGS, [this](auto&) {OpenSettings(); }); - m_game_list->SetDropTarget(new wxGameDropTarget(this)); - sizer->Add(m_game_list, 1, wxEXPAND); - - // info, warning bar - m_info_bar = new wxInfoBar(m_main_panel); - m_info_bar->SetShowHideEffects(wxSHOW_EFFECT_BLEND, wxSHOW_EFFECT_BLEND); - m_info_bar->SetEffectDuration(500); - sizer->Add(m_info_bar, 0, wxALL | wxEXPAND, 5); - - m_main_panel->SetSizer(sizer); - - auto* main_sizer = this->GetSizer(); - main_sizer->Add(m_main_panel, 1, wxEXPAND, 0, nullptr); -} - -void MainWindow::DestroyGameListAndStatusBar() -{ - if(!m_main_panel) - return; - m_main_panel->Destroy(); - m_main_panel = nullptr; - m_game_list = nullptr; - m_info_bar = nullptr; -} - -wxString MainWindow::GetInitialWindowTitle() -{ - return BUILD_VERSION_WITH_NAME_STRING; -} - -void MainWindow::OnClose(wxCloseEvent& event) -{ - if (m_debugger_window) - { - m_debugger_window->CleanupForDestroy(); - m_debugger_window->Destroy(); - m_debugger_window = nullptr; - } - - if(m_game_list) - m_game_list->OnClose(event); - - if (!IsMaximized() && !WindowSystem::IsFullScreen()) - m_restored_size = GetSize(); - - SaveSettings(); - m_timer->Stop(); - - event.Skip(); - - CafeSystem::Shutdown(); - DestroyCanvas(); -} - -bool MainWindow::InstallUpdate(const fs::path& metaFilePath) -{ - try - { - GameUpdateWindow frame(*this, metaFilePath); - const int updateResult = frame.ShowModal(); - - if (updateResult == wxID_OK) - { - CafeTitleList::AddTitleFromPath(frame.GetTargetPath()); // this will also send a notification to the game list which will update the entry - wxMessageBox(_("Title installed!"), _("Success")); - return true; - } - else - { - if (frame.GetExceptionMessage().empty()) - wxMessageBox(_("Title installation has been canceled!")); - else - { - throw std::runtime_error(frame.GetExceptionMessage()); - } - } - } - catch(const AbortException&) - { - // ignored - } - catch (const std::exception& ex) - { - wxMessageBox(ex.what(), _("Update error")); - } - return false; -} - -bool MainWindow::FileLoad(const fs::path launchPath, wxLaunchGameEvent::INITIATED_BY initiatedBy) -{ - TitleInfo launchTitle{ launchPath }; - if (launchTitle.IsValid()) - { - // the title might not be in the TitleList, so we add it as a temporary entry - CafeTitleList::AddTitleFromPath(launchPath); - // title is valid, launch from TitleId - TitleId baseTitleId; - if (!CafeTitleList::FindBaseTitleId(launchTitle.GetAppTitleId(), baseTitleId)) - { - wxString t = _("Unable to launch game because the base files were not found."); - wxMessageBox(t, _("Error"), wxOK | wxCENTRE | wxICON_ERROR); - return false; - } - CafeSystem::PREPARE_STATUS_CODE r = CafeSystem::PrepareForegroundTitle(baseTitleId); - if (r == CafeSystem::PREPARE_STATUS_CODE::INVALID_RPX) - { - cemu_assert_debug(false); - return false; - } - else if (r == CafeSystem::PREPARE_STATUS_CODE::UNABLE_TO_MOUNT) - { - wxString t = _("Unable to mount title.\nMake sure the configured game paths are still valid and refresh the game list.\n\nFile which failed to load:\n"); - t.append(_pathToUtf8(launchPath)); - wxMessageBox(t, _("Error"), wxOK | wxCENTRE | wxICON_ERROR); - return false; - } - else if (r != CafeSystem::PREPARE_STATUS_CODE::SUCCESS) - { - wxString t = _("Failed to launch game."); - t.append(_pathToUtf8(launchPath)); - wxMessageBox(t, _("Error"), wxOK | wxCENTRE | wxICON_ERROR); - return false; - } - } - else //if (launchTitle.GetFormat() == TitleInfo::TitleDataFormat::INVALID_STRUCTURE ) - { - // title is invalid, if it's an RPX/ELF we can launch it directly - // otherwise it's an error - CafeTitleFileType fileType = DetermineCafeSystemFileType(launchPath); - if (fileType == CafeTitleFileType::RPX || fileType == CafeTitleFileType::ELF) - { - CafeSystem::PREPARE_STATUS_CODE r = CafeSystem::PrepareForegroundTitleFromStandaloneRPX(launchPath); - if (r != CafeSystem::PREPARE_STATUS_CODE::SUCCESS) - { - cemu_assert_debug(false); // todo - wxString t = _("Failed to launch executable. Path: "); - t.append(_pathToUtf8(launchPath)); - wxMessageBox(t, _("Error"), wxOK | wxCENTRE | wxICON_ERROR); - return false; - } - } - else if (initiatedBy == wxLaunchGameEvent::INITIATED_BY::GAME_LIST) - { - wxString t = _("Unable to launch title.\nMake sure the configured game paths are still valid and refresh the game list.\n\nPath which failed to load:\n"); - t.append(_pathToUtf8(launchPath)); - wxMessageBox(t, _("Error"), wxOK | wxCENTRE | wxICON_ERROR); - return false; - } - else - { - wxString t = _("Unable to launch game\nPath:\n"); - t.append(_pathToUtf8(launchPath)); - if(launchTitle.GetInvalidReason() == TitleInfo::InvalidReason::NO_DISC_KEY) - { - t.append("\n\n"); - t.append(_("Could not decrypt title. Make sure that keys.txt contains the correct disc key for this title.")); - } - if(launchTitle.GetInvalidReason() == TitleInfo::InvalidReason::NO_TITLE_TIK) - { - t.append("\n\n"); - t.append(_("Could not decrypt title because title.tik is missing.")); - } - wxMessageBox(t, _("Error"), wxOK | wxCENTRE | wxICON_ERROR); - return false; - } - } - - if(launchTitle.IsValid()) - GetWxGUIConfig().AddRecentlyLaunchedFile(_pathToUtf8(launchTitle.GetPath())); - else - GetWxGUIConfig().AddRecentlyLaunchedFile(_pathToUtf8(launchPath)); - - wxWindowUpdateLocker lock(this); - - DestroyGameListAndStatusBar(); - - m_game_launched = true; - m_loadMenuItem->Enable(false); - m_installUpdateMenuItem->Enable(false); - m_memorySearcherMenuItem->Enable(true); - - m_launched_game_name = CafeSystem::GetForegroundTitleName(); - #ifdef ENABLE_DISCORD_RPC - if (m_discord) - m_discord->UpdatePresence(DiscordPresence::Playing, m_launched_game_name); - #endif - - if (GetConfig().disable_screensaver) - ScreenSaver::SetInhibit(true); - - if (FullscreenEnabled()) - SetFullScreen(true); - - //GameMode support -#if BOOST_OS_LINUX && defined(ENABLE_FERAL_GAMEMODE) - if(GetWxGUIConfig().feral_gamemode) - { - // attempt to start gamemode - if(gamemode_request_start() < 0) - { - // GameMode failed to start - cemuLog_log(LogType::Force, "Could not start GameMode"); - } - else - { - cemuLog_log(LogType::Force, "GameMode has been started."); - } - } -#endif - - CreateCanvas(); - CafeSystem::LaunchForegroundTitle(); - RecreateMenu(); - UpdateChildWindowTitleRunningState(); - - return true; -} - -void MainWindow::OnLaunchFromFile(wxLaunchGameEvent& event) -{ - if (event.GetPath().empty()) - return; - FileLoad(event.GetPath(), event.GetInitiatedBy()); -} - -void MainWindow::OnFileMenu(wxCommandEvent& event) -{ - const auto menuId = event.GetId(); - if (menuId == MAINFRAME_MENU_ID_FILE_LOAD) - { - const auto wildcard = formatWxString( - "{}|*.wud;*.wux;*.wua;*.wuhb;*.iso;*.rpx;*.elf;title.tmd" - "|{}|*.wud;*.wux;*.iso" - "|{}|title.tmd" - "|{}|*.wua" - "|{}|*.wuhb" - "|{}|*.rpx;*.elf" - "|{}|*", - _("All Wii U files (*.wud, *.wux, *.wua, *.wuhb, *.iso, *.rpx, *.elf)"), - _("Wii U image (*.wud, *.wux, *.iso, *.wad)"), - _("Wii U NUS content"), - _("Wii U archive (*.wua)"), - _("Wii U homebrew bundle (*.wuhb)"), - _("Wii U executable (*.rpx, *.elf)"), - _("All files (*.*)") - ); - - wxFileDialog openFileDialog(this, _("Open file to launch"), wxEmptyString, wxEmptyString, wildcard, wxFD_OPEN | wxFD_FILE_MUST_EXIST); - - if (openFileDialog.ShowModal() == wxID_CANCEL || openFileDialog.GetPath().IsEmpty()) - return; - - const wxString wxStrFilePath = openFileDialog.GetPath(); - FileLoad(_utf8ToPath(wxStrFilePath.utf8_string()), wxLaunchGameEvent::INITIATED_BY::MENU); - } - else if (menuId >= MAINFRAME_MENU_ID_FILE_RECENT_0 && menuId <= MAINFRAME_MENU_ID_FILE_RECENT_LAST) - { - const auto& config = GetWxGUIConfig(); - const size_t index = menuId - MAINFRAME_MENU_ID_FILE_RECENT_0; - if (index < config.recent_launch_files.size()) - { - fs::path path = _utf8ToPath(config.recent_launch_files[index]); - if (!path.empty()) - FileLoad(path, wxLaunchGameEvent::INITIATED_BY::MENU); - } - } - else if (menuId == MAINFRAME_MENU_ID_FILE_END_EMULATION) - { - EndEmulation(); - } -} - -void MainWindow::OnOpenFolder(wxCommandEvent& event) -{ - const auto id = event.GetId(); - if(id == MAINFRAME_MENU_ID_FILE_OPEN_CEMU_FOLDER) - wxLaunchDefaultApplication(wxHelper::FromPath(ActiveSettings::GetUserDataPath())); - else if(id == MAINFRAME_MENU_ID_FILE_OPEN_MLC_FOLDER) - wxLaunchDefaultApplication(wxHelper::FromPath(ActiveSettings::GetMlcPath())); - else if (id == MAINFRAME_MENU_ID_FILE_OPEN_SHADERCACHE_FOLDER) - wxLaunchDefaultApplication(wxHelper::FromPath(ActiveSettings::GetCachePath("shaderCache"))); -} - -void MainWindow::OnClearSpotPassCache(wxCommandEvent& event) -{ - fs::path bossPath = ActiveSettings::GetMlcPath("usr/boss"); - fs::remove_all(bossPath); - // recreate usr/boss/ - fs::create_directory(bossPath); - wxMessageBox(_("SpotPass cache cleared")); -} - -void MainWindow::OnInstallUpdate(wxCommandEvent& event) -{ - while (true) - { - wxDirDialog openDirDialog(nullptr, _("Select folder of title to install"), "", wxDD_DEFAULT_STYLE | wxDD_DIR_MUST_EXIST, wxDefaultPosition, wxDefaultSize, _("Select the folder that stores your update, DLC or base game files")); - int modalChoice = openDirDialog.ShowModal(); - if (modalChoice == wxID_CANCEL || openDirDialog.GetPath().IsEmpty()) - break; - if (modalChoice == wxID_OK) - { - #if BOOST_OS_LINUX || BOOST_OS_MACOS || BOOST_OS_BSD - fs::path dirPath((const char*)(openDirDialog.GetPath().fn_str())); - #else - fs::path dirPath(openDirDialog.GetPath().fn_str()); - #endif - - if ((dirPath.filename() == "code" || dirPath.filename() == "content" || dirPath.filename() == "meta") && dirPath.has_parent_path()) - { - if (!fs::exists(dirPath.parent_path() / "code") || !fs::exists(dirPath.parent_path() / "content") || !fs::exists(dirPath.parent_path() / "meta")) - { - wxMessageBox(formatWxString(_("The (parent) folder of the title you selected is missing at least one of the required subfolders (\"code\", \"content\" and \"meta\")\nMake sure that the files are complete."), dirPath.filename().string())); - continue; - } - else - dirPath = dirPath.parent_path(); - } - - if (!fs::exists(dirPath)) - wxMessageBox(_("The folder you have selected cannot be found on your system.")); - else if (!fs::exists(dirPath / "meta" / "meta.xml")) - wxMessageBox(_("Unable to find the /meta/meta.xml file inside the selected folder.")); - else - { - InstallUpdate(dirPath); - return; - } - } - } -} - -void MainWindow::OnNFCMenu(wxCommandEvent& event) -{ - if (event.GetId() == MAINFRAME_MENU_ID_NFC_TOUCH_NFC_FILE) - { - wxFileDialog - openFileDialog(this, _("Open file to load"), "", "", - "All NFC files (bin, dat, nfc)|*.bin;*.dat;*.nfc|All files (*.*)|*", wxFD_OPEN | wxFD_FILE_MUST_EXIST); // TRANSLATE - if (openFileDialog.ShowModal() == wxID_CANCEL || openFileDialog.GetPath().IsEmpty()) - return; - wxString wxStrFilePath = openFileDialog.GetPath(); - uint32 nfcError; - if (nfc::TouchTagFromFile(_utf8ToPath(wxStrFilePath.utf8_string()), &nfcError) == false) - { - if (nfcError == NFC_TOUCH_TAG_ERROR_NO_ACCESS) - wxMessageBox(_("Cannot open file")); - else if (nfcError == NFC_TOUCH_TAG_ERROR_INVALID_FILE_FORMAT) - wxMessageBox(_("Not a valid NFC file")); - } - else - { - GetWxGUIConfig().AddRecentNfcFile(wxStrFilePath.utf8_string()); - UpdateNFCMenu(); - } - } - else if (event.GetId() >= MAINFRAME_MENU_ID_NFC_RECENT_0 && event.GetId() <= MAINFRAME_MENU_ID_NFC_RECENT_LAST) - { - const size_t index = event.GetId() - MAINFRAME_MENU_ID_NFC_RECENT_0; - auto& config = GetWxGUIConfig(); - if (index < config.recent_nfc_files.size()) - { - const auto& path = config.recent_nfc_files[index]; - if (!path.empty()) - { - uint32 nfcError = 0; - if (nfc::TouchTagFromFile(_utf8ToPath(path), &nfcError) == false) - { - if (nfcError == NFC_TOUCH_TAG_ERROR_NO_ACCESS) - wxMessageBox(_("Cannot open file")); - else if (nfcError == NFC_TOUCH_TAG_ERROR_INVALID_FILE_FORMAT) - wxMessageBox(_("Not a valid NFC file")); - } - else - { - config.AddRecentNfcFile(path); - UpdateNFCMenu(); - } - } - } - } -} - -void MainWindow::OnFileExit(wxCommandEvent& event) -{ - Close(); -} - -void MainWindow::TogglePadView() -{ - const auto& config = GetWxGUIConfig(); - if (config.pad_open) - { - if (m_padView) - return; - - m_padView = new PadViewFrame(this); - - m_padView->Bind(wxEVT_CLOSE_WINDOW, &MainWindow::OnPadClose, this); - - m_padView->Show(true); - -#if ( BOOST_OS_LINUX || BOOST_OS_BSD ) && HAS_WAYLAND - if (wxWlIsWaylandWindow(m_padView)) - wxWlSetAppId(m_padView, "info.cemu.Cemu"); -#endif - - m_padView->Initialize(); - if (m_game_launched) - m_padView->InitializeRenderCanvas(); - } - else if (m_padView) - { - m_padView->Destroy(); - m_padView = nullptr; - } -} - -#if BOOST_OS_WINDOWS - -#ifndef DBT_DEVNODES_CHANGED -#define DBT_DEVNODES_CHANGED (0x0007) -#endif -WXLRESULT MainWindow::MSWWindowProc(WXUINT nMsg, WXWPARAM wParam, WXLPARAM lParam) -{ - if (nMsg == WM_DEVICECHANGE) - { - if (wParam == DBT_DEVNODES_CHANGED) - { - InputManager::instance().on_device_changed(); - } - } - - return wxFrame::MSWWindowProc(nMsg, wParam, lParam); -} -#endif - -void MainWindow::OpenSettings() -{ - auto& config = GetWxGUIConfig(); - const auto language = config.language; - - GeneralSettings2 frame(this, m_game_launched); - frame.ShowModal(); - const bool paths_modified = frame.ShouldReloadGamelist(); - const bool mlc_modified = frame.MLCModified(); - - if (paths_modified) - m_game_list->ReloadGameEntries(); - else - SaveSettings(); - - #ifdef ENABLE_DISCORD_RPC - if (config.use_discord_presence) - { - if (!m_discord) - { - m_discord = std::make_unique(); - if (!m_launched_game_name.empty()) - m_discord->UpdatePresence(DiscordPresence::Playing, m_launched_game_name); - } - } - else - m_discord.reset(); - #endif - - if(config.check_update && !m_game_launched) - m_update_available = CemuUpdateWindow::IsUpdateAvailableAsync(); - - if (mlc_modified) - RecreateMenu(); - - if (!config.fullscreen_menubar && IsFullScreen()) - SetMenuVisible(false); - - if (language != config.language) - wxMessageBox(_("Cemu must be restarted to apply the selected UI language."), _("Information"), wxOK | wxCENTRE, this); // TODO: change language to newly selected one -} - -void MainWindow::OnOptionsInput(wxCommandEvent& event) -{ - switch (event.GetId()) - { - case MAINFRAME_MENU_ID_OPTIONS_FULLSCREEN: - { - const bool state = m_fullscreenMenuItem->IsChecked(); - SetFullScreen(state); - break; - } - case MAINFRAME_MENU_ID_OPTIONS_SECOND_WINDOW_PADVIEW: - { - GetWxGUIConfig().pad_open = !GetWxGUIConfig().pad_open; - g_wxConfig.Save(); - - TogglePadView(); - break; - } - case MAINFRAME_MENU_ID_OPTIONS_GRAPHIC_PACKS2: - { - if (m_graphic_pack_window) - return; - - uint64 titleId = 0; - if (CafeSystem::IsTitleRunning()) - titleId = CafeSystem::GetForegroundTitleId(); - - m_graphic_pack_window = new GraphicPacksWindow2(this, titleId); - m_graphic_pack_window->Bind(wxEVT_CLOSE_WINDOW, &MainWindow::OnGraphicWindowClose, this); - m_graphic_pack_window->Show(true); - - break; - } - - case MAINFRAME_MENU_ID_OPTIONS_MAC_SETTINGS: - case MAINFRAME_MENU_ID_OPTIONS_GENERAL2: - { - OpenSettings(); - break; - } - case MAINFRAME_MENU_ID_OPTIONS_INPUT: - { - auto* frame = new InputSettings2(this); - frame->ShowModal(); - frame->Destroy(); - break; - } - - case MAINFRAME_MENU_ID_OPTIONS_HOTKEY: - { - auto* frame = new HotkeySettings(this); - frame->Show(); - break; - } - } -} - -void MainWindow::OnAccountSelect(wxCommandEvent& event) -{ - const int index = event.GetId() - MAINFRAME_MENU_ID_OPTIONS_ACCOUNT_1; - const auto& accounts = Account::GetAccounts(); - wxASSERT(index >= 0 && index < (int)accounts.size()); - auto& config = GetConfig(); - config.account.m_persistent_id = accounts[index].GetPersistentId(); - // config.account.online_enabled.value = false; // reset online for safety - GetConfigHandle().Save(); -} - -void MainWindow::OnConsoleLanguage(wxCommandEvent& event) -{ - switch (event.GetId()) - { - case MAINFRAME_MENU_ID_OPTIONS_LANGUAGE_JAPANESE: - GetConfig().console_language = CafeConsoleLanguage::JA; - break; - case MAINFRAME_MENU_ID_OPTIONS_LANGUAGE_ENGLISH: - GetConfig().console_language = CafeConsoleLanguage::EN; - break; - case MAINFRAME_MENU_ID_OPTIONS_LANGUAGE_FRENCH: - GetConfig().console_language = CafeConsoleLanguage::FR; - break; - case MAINFRAME_MENU_ID_OPTIONS_LANGUAGE_GERMAN: - GetConfig().console_language = CafeConsoleLanguage::DE; - break; - case MAINFRAME_MENU_ID_OPTIONS_LANGUAGE_ITALIAN: - GetConfig().console_language = CafeConsoleLanguage::IT; - break; - case MAINFRAME_MENU_ID_OPTIONS_LANGUAGE_SPANISH: - GetConfig().console_language = CafeConsoleLanguage::ES; - break; - case MAINFRAME_MENU_ID_OPTIONS_LANGUAGE_CHINESE: - GetConfig().console_language = CafeConsoleLanguage::ZH; - break; - case MAINFRAME_MENU_ID_OPTIONS_LANGUAGE_KOREAN: - GetConfig().console_language = CafeConsoleLanguage::KO; - break; - case MAINFRAME_MENU_ID_OPTIONS_LANGUAGE_DUTCH: - GetConfig().console_language = CafeConsoleLanguage::NL; - break; - case MAINFRAME_MENU_ID_OPTIONS_LANGUAGE_PORTUGUESE: - GetConfig().console_language = CafeConsoleLanguage::PT; - break; - case MAINFRAME_MENU_ID_OPTIONS_LANGUAGE_RUSSIAN: - GetConfig().console_language = CafeConsoleLanguage::RU; - break; - case MAINFRAME_MENU_ID_OPTIONS_LANGUAGE_TAIWANESE: - GetConfig().console_language = CafeConsoleLanguage::TW; - break; - default: - cemu_assert_debug(false); - } - if (m_game_list) - { - m_game_list->DeleteCachedStrings(); - m_game_list->ReloadGameEntries(); - } - GetConfigHandle().Save(); -} - -//void MainWindow::OnCPUMode(wxCommandEvent& event) -//{ -// if (event.GetId() == MAINFRAME_MENU_ID_CPU_MODE_SINGLECORE_INTERPRETER) -// GetConfig().cpu_mode = CPUMode::SinglecoreInterpreter; -// else if (event.GetId() == MAINFRAME_MENU_ID_CPU_MODE_SINGLECORE_RECOMPILER) -// GetConfig().cpu_mode = CPUMode::SinglecoreRecompiler; -// else if (event.GetId() == MAINFRAME_MENU_ID_CPU_MODE_DUALCORE_RECOMPILER) -// GetConfig().cpu_mode = CPUMode::DualcoreRecompiler; -// else if (event.GetId() == MAINFRAME_MENU_ID_CPU_MODE_TRIPLECORE_RECOMPILER) -// GetConfig().cpu_mode = CPUMode::TriplecoreRecompiler; -// else -// cemu_assert_debug(false); -// -// GetConfigHandle().Save(); -//} - -void MainWindow::OnDebugSetting(wxCommandEvent& event) -{ - if (event.GetId() == MAINFRAME_MENU_ID_DEBUG_RENDER_UPSIDE_DOWN) - GetConfig().render_upside_down = event.IsChecked(); - else if (event.GetId() == MAINFRAME_MENU_ID_DEBUG_VK_ACCURATE_BARRIERS) - { - GetConfig().vk_accurate_barriers = event.IsChecked(); - if(!GetConfig().vk_accurate_barriers) - wxMessageBox(_("Warning: Disabling the accurate barriers option will lead to flickering graphics but may improve performance. It is highly recommended to leave it turned on."), _("Accurate barriers are off"), wxOK); - } -#ifdef ENABLE_METAL - else if (event.GetId() == MAINFRAME_MENU_ID_DEBUG_GPU_CAPTURE) - { - cemu_assert_debug(g_renderer->GetType() == RendererAPI::Metal); - static_cast(g_renderer.get())->CaptureFrame(); - } -#endif - else if (event.GetId() == MAINFRAME_MENU_ID_DEBUG_AUDIO_AUX_ONLY) - ActiveSettings::EnableAudioOnlyAux(event.IsChecked()); - else if (event.GetId() == MAINFRAME_MENU_ID_DEBUG_DUMP_RAM) - memory_createDump(); - else if (event.GetId() == MAINFRAME_MENU_ID_DEBUG_DUMP_RPX) - CafeSystem::DumpCurrentRPX(); - else if (event.GetId() == MAINFRAME_MENU_ID_DEBUG_DUMP_FST) - { - /* int msgBoxAnswer = wxMessageBox(_("All files from the currently running game will be dumped to /dump/. This process can take a few minutes."), - _("Dump WUD"), wxOK | wxCANCEL | wxICON_WARNING); - if (msgBoxAnswer == wxOK) - { - volumeFST_dump(bootGame_getMountedWUD()); - wxMessageBox(_("Dump complete")); - }*/ - } - else if (event.GetId() == MAINFRAME_MENU_ID_DEBUG_DUMP_CURL_REQUESTS) - { - // toggle debug -> dump -> curl requests - const bool state = event.IsChecked(); - ActiveSettings::EnableDumpLibcurlRequests(state); - if (state) - { - try - { - const fs::path path(ActiveSettings::GetUserDataPath()); - fs::create_directories(path / "dump" / "curl"); - } - catch (const std::exception& ex) - { - SystemException sys(ex); - cemuLog_log(LogType::Force, "error when creating dump curl folder: {}", sys.what()); - ActiveSettings::EnableDumpLibcurlRequests(false); - } - } - } - else if (event.GetId() == MAINFRAME_MENU_ID_TIMER_SPEED_8X) - ActiveSettings::SetTimerShiftFactor(0); - else if (event.GetId() == MAINFRAME_MENU_ID_TIMER_SPEED_4X) - ActiveSettings::SetTimerShiftFactor(1); - else if (event.GetId() == MAINFRAME_MENU_ID_TIMER_SPEED_2X) - ActiveSettings::SetTimerShiftFactor(2); - else if (event.GetId() == MAINFRAME_MENU_ID_TIMER_SPEED_1X) - ActiveSettings::SetTimerShiftFactor(3); - else if (event.GetId() == MAINFRAME_MENU_ID_TIMER_SPEED_05X) - ActiveSettings::SetTimerShiftFactor(4); - else if (event.GetId() == MAINFRAME_MENU_ID_TIMER_SPEED_025X) - ActiveSettings::SetTimerShiftFactor(5); - else if (event.GetId() == MAINFRAME_MENU_ID_TIMER_SPEED_0125X) - ActiveSettings::SetTimerShiftFactor(6); - else - cemu_assert_debug(false); - - GetConfigHandle().Save(); -} - -void MainWindow::OnDebugLoggingToggleFlagGeneric(wxCommandEvent& event) -{ - sint32 loggingIdBase = MAINFRAME_MENU_ID_DEBUG_LOGGING0; - - sint32 id = event.GetId(); - if (id >= loggingIdBase && id < (MAINFRAME_MENU_ID_DEBUG_LOGGING0 + 64)) - { - bool isEnable = event.IsChecked(); - LogType loggingType = static_cast(id - loggingIdBase); - if (isEnable) - GetConfig().log_flag = GetConfig().log_flag.GetValue() | cemuLog_getFlag(loggingType); - else - GetConfig().log_flag = GetConfig().log_flag.GetValue() & ~cemuLog_getFlag(loggingType); - cemuLog_setActiveLoggingFlags(GetConfig().log_flag.GetValue()); - GetConfigHandle().Save(); - } -} - -void MainWindow::OnPPCInfoToggle(wxCommandEvent& event) -{ - GetConfig().advanced_ppc_logging = !GetConfig().advanced_ppc_logging.GetValue(); - GetConfigHandle().Save(); -} - -void MainWindow::OnDebugDumpGeneric(wxCommandEvent& event) -{ - std::string dumpSubpath; - std::function setDumpState; - switch(event.GetId()) - { - case MAINFRAME_MENU_ID_DEBUG_DUMP_TEXTURES: - dumpSubpath = "dump/textures"; - setDumpState = ActiveSettings::EnableDumpTextures; - break; - case MAINFRAME_MENU_ID_DEBUG_DUMP_SHADERS: - dumpSubpath = "dump/shaders"; - setDumpState = ActiveSettings::EnableDumpShaders; - break; - case MAINFRAME_MENU_ID_DEBUG_DUMP_RECOMPILER_FUNCTIONS: - dumpSubpath = "dump/recompiler"; - setDumpState = ActiveSettings::EnableDumpRecompilerFunctions; - break; - default: - UNREACHABLE; - } - const bool value = event.IsChecked(); - setDumpState(value); - if (value) - { - try - { - fs::create_directories(ActiveSettings::GetUserDataPath(dumpSubpath)); - } - catch (const std::exception & ex) - { - SystemException sys(ex); - cemuLog_log(LogType::Force, "can't create folder {} in user data folder: {}", dumpSubpath, ex.what()); - setDumpState(false); - } - } -} - -void MainWindow::OnLoggingWindow(wxCommandEvent& event) -{ - if(m_logging_window) - return; - - m_logging_window = new LoggingWindow(this); - m_logging_window->Bind(wxEVT_CLOSE_WINDOW, - [this](wxCloseEvent& event) { - m_logging_window = nullptr; - event.Skip(); - }); - m_logging_window->Show(true); -} - -void MainWindow::OnGDBStubToggle(wxCommandEvent& event) -{ - if (g_gdbstub) - { - g_gdbstub.release(); - return; - } - - const auto& config = GetConfig(); - g_gdbstub = std::make_unique(config.gdb_port); -} - -void MainWindow::OnDebugViewPPCThreads(wxCommandEvent& event) -{ - auto frame = new DebugPPCThreadsWindow(*this); - frame->Show(true); -} - -void MainWindow::OnDebugViewPPCDebugger(wxCommandEvent& event) -{ - if (m_debugger_window && m_debugger_window->IsShown()) - { - m_debugger_window->Close(); - m_debugger_window = nullptr; - return; - } - - OpenPPCDebugger(); -} - -void MainWindow::OpenPPCDebugger() -{ - if (m_debugger_window) - return; - - auto rect = GetDesktopRect(); - /* - sint32 new_width = max(rect.GetWidth() * 0.70, rect.GetWidth() - 850); - this->SetSize(new_width, 480);*/ - - this->SetSize(800, 450 + 50); - this->CenterOnScreen(); - - auto pos = this->GetPosition(); - pos.y = std::min(pos.y + 200, rect.GetHeight() - 400); - this->SetPosition(pos); - - m_debugger_window = new DebuggerWindow2(*this, rect); - m_debugger_window->Bind(wxEVT_CLOSE_WINDOW, &MainWindow::OnDebuggerClose, this); - m_debugger_window->Show(true); -} - -void MainWindow::OnDebugViewAudioDebugger(wxCommandEvent& event) -{ - auto frame = new AudioDebuggerWindow(*this); - frame->Show(true); -} - -void MainWindow::OnDebugViewTextureRelations(wxCommandEvent& event) -{ - openTextureViewer(*this); -} - -void MainWindow::ShowCursor(bool state) -{ - #if BOOST_OS_WINDOWS - CURSORINFO info{}; - info.cbSize = sizeof(CURSORINFO); - GetCursorInfo(&info); - const bool visible = info.flags == CURSOR_SHOWING; - - if (state == visible) - return; - - int counter = 0; - if(state) - { - do - { - counter = ::ShowCursor(TRUE); - } while (counter < 0); - } - else - { - do - { - counter = ::ShowCursor(FALSE); - } while (counter >= 0); - } - #else - if (state) - { - wxSetCursor(wxNullCursor); // restore system default cursor - } - else - { - wxSetCursor(wxCursor(wxCURSOR_BLANK)); - } - #endif -} - -uintptr_t MainWindow::GetRenderCanvasHWND() -{ - // deprecated. We can use the global cross-platform window info structs now - #if BOOST_OS_WINDOWS - if (!m_render_canvas) - return 0; - return (uintptr_t)m_render_canvas->GetHWND(); - #else - return 0; - #endif -} - -wxRect MainWindow::GetDesktopRect() -{ - const auto pos = GetPosition(); - const auto middle = pos.x + GetSize().GetWidth() / 2; - - const auto displayCount = wxDisplay::GetCount(); - for (uint32 i = 0; i < displayCount; ++i) - { - wxDisplay display(i); - if (!display.IsOk()) - continue; - - const auto geo = display.GetGeometry(); - if (geo.x <= middle && middle <= geo.x + geo.width) - return geo; - } - return { 0,0,800,600 }; -} - -void MainWindow::LoadSettings() -{ - GetConfigHandle().Load(); - const auto& config = GetWxGUIConfig(); - - if(config.check_update) - m_update_available = CemuUpdateWindow::IsUpdateAvailableAsync(); - - if (config.window_position != Vector2i{ -1,-1 }) - this->SetPosition({ config.window_position.x, config.window_position.y }); - - if (config.window_size.x > 0 && config.window_size.y > 0) - { - this->SetSize({ config.window_size.x, config.window_size.y }); - - if (config.window_maximized) - this->Maximize(); - } - - if (config.pad_position != Vector2i{ -1,-1 }) - { - g_window_info.restored_pad_x = config.pad_position.x; - g_window_info.restored_pad_y = config.pad_position.y; - } - - if (config.pad_size != Vector2i{ -1,-1 }) - { - g_window_info.restored_pad_width = config.pad_size.x; - g_window_info.restored_pad_height = config.pad_size.y; - - g_window_info.pad_maximized = config.pad_maximized; - } - - this->TogglePadView(); - - if(m_game_list) - m_game_list->LoadConfig(); -} - -void MainWindow::SaveSettings() -{ - auto lock = GetConfigHandle().Lock(); - auto& config = GetWxGUIConfig(); - - if (config.window_position != Vector2i{ -1,-1 }) - { - config.window_position.x = m_restored_position.x; - config.window_position.y = m_restored_position.y; - } - if (config.window_size != Vector2i{ -1,-1 }) - { - config.window_size.x = m_restored_size.x; - config.window_size.y = m_restored_size.y; - config.window_maximized = IsMaximized(); - } - else - { - config.window_maximized = false; - } - - config.pad_open = m_padView != nullptr; - - if (config.pad_position != Vector2i{ -1,-1 } && g_window_info.restored_pad_x != -1) - { - config.pad_position.x = g_window_info.restored_pad_x; - config.pad_position.y = g_window_info.restored_pad_y; - } - if (config.pad_size != Vector2i{ -1,-1 } && g_window_info.restored_pad_width != -1) - { - config.pad_size.x = g_window_info.restored_pad_width; - config.pad_size.y = g_window_info.restored_pad_height; - config.pad_maximized = g_window_info.pad_maximized; - } - else - { - config.pad_maximized = false; - } - - if(m_game_list) - m_game_list->SaveConfig(); - - g_wxConfig.Save(); -} - -void MainWindow::OnMouseMove(wxMouseEvent& event) -{ - event.Skip(); - - m_last_mouse_move_time = std::chrono::steady_clock::now(); - m_mouse_position = wxGetMousePosition(); - ShowCursor(true); - - auto& instance = InputManager::instance(); - std::unique_lock lock(instance.m_main_mouse.m_mutex); - auto physPos = ToPhys(event.GetPosition()); - instance.m_main_mouse.position = { physPos.x, physPos.y }; - lock.unlock(); - - if (!IsFullScreen()) - return; - - const auto& config = GetWxGUIConfig(); - // if mouse goes to upper screen then show our menu in fullscreen mode - if (config.fullscreen_menubar) - SetMenuVisible(event.GetPosition().y < 50); -} - -void MainWindow::OnMouseLeft(wxMouseEvent& event) -{ - auto& instance = InputManager::instance(); - - std::scoped_lock lock(instance.m_main_mouse.m_mutex); - instance.m_main_mouse.left_down = event.ButtonDown(wxMOUSE_BTN_LEFT); - auto physPos = ToPhys(event.GetPosition()); - instance.m_main_mouse.position = { physPos.x, physPos.y }; - if (event.ButtonDown(wxMOUSE_BTN_LEFT)) - instance.m_main_mouse.left_down_toggle = true; - - event.Skip(); -} - -void MainWindow::OnMouseRight(wxMouseEvent& event) -{ - auto& instance = InputManager::instance(); - - std::scoped_lock lock(instance.m_main_mouse.m_mutex); - instance.m_main_mouse.right_down = event.ButtonDown(wxMOUSE_BTN_RIGHT); - auto physPos = ToPhys(event.GetPosition()); - instance.m_main_mouse.position = { physPos.x, physPos.y }; - if(event.ButtonDown(wxMOUSE_BTN_RIGHT)) - instance.m_main_mouse.right_down_toggle = true; - - event.Skip(); -} - -void MainWindow::OnGameListBeginUpdate(wxCommandEvent& event) -{ - if (m_game_list->IsShown()) - m_info_bar->ShowMessage(_("Updating game list...")); -} - -void MainWindow::OnGameListEndUpdate(wxCommandEvent& event) -{ - m_info_bar->Dismiss(); -} - -void MainWindow::OnAccountListRefresh(wxCommandEvent& event) -{ - RecreateMenu(); -} - -void MainWindow::OnRequestGameListRefresh(wxCommandEvent& event) -{ - m_game_list->ReloadGameEntries(); -} - -void MainWindow::OnSetWindowTitle(wxCommandEvent& event) -{ - this->SetTitle(event.GetString()); -} - -void MainWindow::OnKeyUp(wxKeyEvent& event) -{ - event.Skip(); - - if (swkbd_hasKeyboardInputHook()) - return; - - HotkeySettings::CaptureInput(event); -} - -void MainWindow::OnKeyDown(wxKeyEvent& event) -{ -#if defined(__APPLE__) - // On macOS, allow Cmd+Q to quit the application - if (event.CmdDown() && event.GetKeyCode() == 'Q') - { - Close(true); - } -#else - // On Windows/Linux, only Alt+F4 is allowed for quitting - if (event.AltDown() && event.GetKeyCode() == WXK_F4) - { - Close(true); - } -#endif - else - { - event.Skip(); - } -} - -void MainWindow::OnChar(wxKeyEvent& event) -{ - if (swkbd_hasKeyboardInputHook()) - swkbd_keyInput(event.GetUnicodeKey()); - - // event.Skip(); -} - -void MainWindow::OnToolsInput(wxCommandEvent& event) -{ - const auto id = event.GetId(); - switch (id) - { - case MAINFRAME_MENU_ID_TOOLS_MEMORY_SEARCHER: - { - if (m_toolWindow) - m_toolWindow->SetFocus(); - else - { - m_toolWindow = new MemorySearcherTool(this); - m_toolWindow->Bind(wxEVT_CLOSE_WINDOW, &MainWindow::OnMemorySearcherClose, this); - m_toolWindow->Show(true); - } - break; - } - case MAINFRAME_MENU_ID_TOOLS_TITLE_MANAGER: - case MAINFRAME_MENU_ID_TOOLS_DOWNLOAD_MANAGER: - { - const auto default_tab = id == MAINFRAME_MENU_ID_TOOLS_TITLE_MANAGER ? TitleManagerPage::TitleManager : TitleManagerPage::DownloadManager; - - if (m_title_manager) - m_title_manager->SetFocusAndTab(default_tab); - else - { - m_title_manager = new TitleManager(this, default_tab); - m_title_manager->Bind(wxEVT_CLOSE_WINDOW, [this](wxCloseEvent& event) - { - m_title_manager = nullptr; - event.Skip(); - }); - m_title_manager->Show(); - } - break; - } - case MAINFRAME_MENU_ID_TOOLS_EMULATED_USB_DEVICES: - { - if (m_usb_devices) - { - m_usb_devices->Show(true); - m_usb_devices->Raise(); - m_usb_devices->SetFocus(); - } - else - { - m_usb_devices = new EmulatedUSBDeviceFrame(this); - m_usb_devices->Bind(wxEVT_CLOSE_WINDOW, [this](wxCloseEvent& event) - { - if (event.CanVeto()) { - m_usb_devices->Show(false); - event.Veto(); - } - }); - m_usb_devices->Show(true); - } - break; - } - break; - } -} - -void MainWindow::OnGesturePan(wxPanGestureEvent& event) -{ - auto& instance = InputManager::instance(); - std::scoped_lock lock(instance.m_main_touch.m_mutex); - auto physPos = ToPhys(event.GetPosition()); - instance.m_main_touch.position = { physPos.x, physPos.y }; - instance.m_main_touch.left_down = event.IsGestureStart() || !event.IsGestureEnd(); - if (event.IsGestureStart() || !event.IsGestureEnd()) - instance.m_main_touch.left_down_toggle = true; - - - event.Skip(); -} - -void MainWindow::OnGameLoaded() -{ - if (m_debugger_window) - m_debugger_window->OnGameLoaded(); -} - -void MainWindow::AsyncSetTitle(std::string_view windowTitle) -{ - wxCommandEvent set_title_event(wxEVT_SET_WINDOW_TITLE); - set_title_event.SetString(wxString::FromUTF8(windowTitle)); - g_mainFrame->QueueEvent(set_title_event.Clone()); -} - -void MainWindow::CreateCanvas() -{ - // create panel for canvas - m_game_panel = new wxPanel(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL | wxNO_BORDER | wxWANTS_CHARS); - auto* sizer = new wxBoxSizer(wxVERTICAL); - - // shouldn't be needed, but who knows - m_game_panel->Bind(wxEVT_KEY_UP, &MainWindow::OnKeyUp, this); - m_game_panel->Bind(wxEVT_CHAR, &MainWindow::OnChar, this); - - m_game_panel->SetSizer(sizer); - this->GetSizer()->Add(m_game_panel, 1, wxEXPAND); - - // create canvas - #ifdef ENABLE_OPENGL - if (ActiveSettings::GetGraphicsAPI() == kOpenGL) - m_render_canvas = GLCanvas_Create(m_game_panel, wxSize(1280, 720), true); - #endif - #ifdef ENABLE_VULKAN - if (ActiveSettings::GetGraphicsAPI() == kVulkan) - m_render_canvas = new VulkanCanvas(m_game_panel, wxSize(1280, 720), true); - #endif - #ifdef ENABLE_METAL - if (ActiveSettings::GetGraphicsAPI() == kMetal) - m_render_canvas = new MetalCanvas(m_game_panel, wxSize(1280, 720), true); - #endif - if (!m_render_canvas) - cemu_assert(false && "Failed to create canvas or invalid graphics API selected"); - cemu_assert(m_render_canvas != nullptr); - - // mouse events - m_render_canvas->Bind(wxEVT_MOTION, &MainWindow::OnMouseMove, this); - m_render_canvas->Bind(wxEVT_MOUSEWHEEL, &MainWindow::OnMouseWheel, this); - m_render_canvas->Bind(wxEVT_LEFT_DOWN, &MainWindow::OnMouseLeft, this); - m_render_canvas->Bind(wxEVT_LEFT_UP, &MainWindow::OnMouseLeft, this); - m_render_canvas->Bind(wxEVT_RIGHT_DOWN, &MainWindow::OnMouseRight, this); - m_render_canvas->Bind(wxEVT_RIGHT_UP, &MainWindow::OnMouseRight, this); - - m_render_canvas->Bind(wxEVT_GESTURE_PAN, &MainWindow::OnGesturePan, this); - - // key events - m_render_canvas->Bind(wxEVT_KEY_UP, &MainWindow::OnKeyUp, this); - m_render_canvas->Bind(wxEVT_KEY_DOWN, &MainWindow::OnKeyDown, this); - m_render_canvas->Bind(wxEVT_CHAR, &MainWindow::OnChar, this); - - m_render_canvas->SetDropTarget(new wxAmiiboDropTarget(this)); - m_game_panel->GetSizer()->Add(m_render_canvas, 1, wxEXPAND, 0, nullptr); - - GetSizer()->Layout(); - m_render_canvas->SetFocus(); - - if (m_padView) - m_padView->InitializeRenderCanvas(); -} - -void MainWindow::DestroyCanvas() -{ - if (m_padView) - { - m_padView->DestroyCanvas(); - } - if (m_render_canvas) - { - m_render_canvas->Destroy(); - m_render_canvas = nullptr; - } - if(m_game_panel) - { - m_game_panel->Destroy(); - m_game_panel = nullptr; - } -} - -void MainWindow::OnSizeEvent(wxSizeEvent& event) -{ - if (!IsMaximized() && !WindowSystem::IsFullScreen()) - m_restored_size = GetSize(); - - const wxSize client_size = GetClientSize(); - g_window_info.width = client_size.GetWidth(); - g_window_info.height = client_size.GetHeight(); - g_window_info.phys_width = ToPhys(client_size.GetWidth()); - g_window_info.phys_height = ToPhys(client_size.GetHeight()); - g_window_info.dpi_scale = GetDPIScaleFactor(); - - if (m_debugger_window && m_debugger_window->IsShown()) - m_debugger_window->OnParentMove(GetPosition(), event.GetSize()); - - event.Skip(); - - #ifdef ENABLE_VULKAN - VsyncDriver_notifyWindowPosChanged(); - #endif -} - -void MainWindow::OnDPIChangedEvent(wxDPIChangedEvent& event) -{ - event.Skip(); - const wxSize client_size = GetClientSize(); - g_window_info.width = client_size.GetWidth(); - g_window_info.height = client_size.GetHeight(); - g_window_info.phys_width = ToPhys(client_size.GetWidth()); - g_window_info.phys_height = ToPhys(client_size.GetHeight()); - g_window_info.dpi_scale = GetDPIScaleFactor(); -} - -void MainWindow::OnMove(wxMoveEvent& event) -{ - if (!IsMaximized() && !WindowSystem::IsFullScreen()) - m_restored_position = GetPosition(); - - if (m_debugger_window && m_debugger_window->IsShown()) - m_debugger_window->OnParentMove(GetPosition(), GetSize()); - #ifdef ENABLE_VULKAN - VsyncDriver_notifyWindowPosChanged(); - #endif -} - -void MainWindow::OnDebuggerClose(wxCloseEvent& event) -{ - m_debugger_window = nullptr; - event.Skip(); -} - -void MainWindow::OnPadClose(wxCloseEvent& event) -{ - auto& config = GetWxGUIConfig(); - config.pad_open = false; - if (config.pad_position != Vector2i{ -1,-1 }) - m_padView->GetPosition(&config.pad_position.x, &config.pad_position.y); - - if (config.pad_size != Vector2i{ -1,-1 }) - m_padView->GetSize(&config.pad_size.x, &config.pad_size.y); - - g_wxConfig.Save(); - - // already deleted by wxwidget - m_padView = nullptr; - - if (m_padViewMenuItem) - m_padViewMenuItem->Check(false); - - event.Skip(); -} - -void MainWindow::OnMemorySearcherClose(wxCloseEvent& event) -{ - m_toolWindow = nullptr; - event.Skip(); -} - -void MainWindow::OnMouseWheel(wxMouseEvent& event) -{ - const float delta = event.GetWheelRotation(); // in 120 steps -> max reached ~480 (?) - auto& instance = InputManager::instance(); - instance.m_mouse_wheel = (delta / 120.0f); - - event.Skip(); -} - -void MainWindow::SetFullScreen(bool state) -{ - // only update config entry if we dont't have launch parameters - if (!LaunchSettings::FullscreenEnabled().has_value()) - { - GetWxGUIConfig().fullscreen = state; - g_wxConfig.Save(); - } - if (state && !m_game_launched) - return; - g_window_info.is_fullscreen = state; - m_fullscreenMenuItem->Check(state); - - this->ShowFullScreen(state); - - if (state) - m_menu_visible = false; // menu gets always disabled by wxFULLSCREEN_NOMENUBAR - else - SetMenuVisible(true); -} - -void MainWindow::EndEmulation() // unfinished - memory leaks and crashes after repeated use (after 3x usually) -{ - CafeSystem::ShutdownTitle(); - DestroyCanvas(); - m_game_launched = false; - m_launched_game_name.clear(); - #ifdef ENABLE_DISCORD_RPC - if (m_discord) - m_discord->UpdatePresence(DiscordPresence::Idling, ""); - #endif - - if (GetConfig().disable_screensaver) - ScreenSaver::SetInhibit(false); - - // close memory searcher if created - if (m_toolWindow) - { - m_toolWindow->Close(); - m_toolWindow = nullptr; - m_memorySearcherMenuItem->Enable(false); - } - - RecreateMenu(); - CreateGameListAndStatusBar(); - DoLayout(); - UpdateChildWindowTitleRunningState(); -} - -void MainWindow::SetMenuVisible(bool state) -{ - if (m_menu_visible == state) - return; - -#if !BOOST_OS_MACOS // on macOS hiding the menu seems to cause issues (see #609) - SetMenuBar(state ? m_menuBar : nullptr); -#endif - m_menu_visible = state; -} - -void MainWindow::UpdateNFCMenu() -{ - if (m_nfcMenuSeparator0) - { - m_nfcMenu->Remove(m_nfcMenuSeparator0->GetId()); - m_nfcMenuSeparator0 = nullptr; - } - // remove recent files list - for (sint32 i = 0; i < wxCemuConfig::kMaxRecentEntries; i++) - { - if (m_nfcMenu->FindChildItem(MAINFRAME_MENU_ID_NFC_RECENT_0 + i) == nullptr) - continue; - m_nfcMenu->Remove(MAINFRAME_MENU_ID_NFC_RECENT_0 + i); - } - // add entries - const auto& config = GetWxGUIConfig(); - sint32 recentFileIndex = 0; - for (size_t i = 0; i < config.recent_nfc_files.size(); i++) - { - const auto& entry = config.recent_nfc_files[i]; - if (entry.empty()) - continue; - - if (!fs::exists(_utf8ToPath(entry))) - continue; - - if (recentFileIndex == 0) - m_nfcMenuSeparator0 = m_nfcMenu->AppendSeparator(); - - m_nfcMenu->Append(MAINFRAME_MENU_ID_NFC_RECENT_0 + i, formatWxString("{}. {}", recentFileIndex, entry)); - - recentFileIndex++; - if (recentFileIndex >= 12) - break; - } -} - -bool MainWindow::IsMenuHidden() const -{ - return m_menu_visible; -} - -void MainWindow::OnTimer(wxTimerEvent& event) -{ - if(m_update_available.valid() && future_is_ready(m_update_available)) - { - if(m_update_available.get()) - { - wxMessageDialog dialog(this, _("There's a new update available.\nDo you want to update?"), _("Update notification"), wxCENTRE | wxYES_NO); - if(dialog.ShowModal() == wxID_YES) - { - CemuUpdateWindow update_window(this); - update_window.ShowModal(); - } - } - - m_update_available = {}; - } - - if (!IsFullScreen() || m_menu_visible) - return; - - const auto mouse_position = wxGetMousePosition(); - if(m_mouse_position != mouse_position) - { - m_last_mouse_move_time = std::chrono::steady_clock::now(); - m_mouse_position = mouse_position; - ShowCursor(true); - return; - } - - auto diff = std::chrono::duration_cast(std::chrono::steady_clock::now() - m_last_mouse_move_time); - if (diff.count() > 3000) - { - ShowCursor(false); - } - -} - -#define BUILD_DATE __DATE__ " " __TIME__ - -class CemuAboutDialog : public wxDialog -{ -public: - CemuAboutDialog(wxWindow* parent = NULL) - : wxDialog(NULL, wxID_ANY, _("About Cemu"), wxDefaultPosition, wxSize(500, 700)) - { - Create(parent); - } - - void Create(wxWindow* parent = NULL) - { - SetIcon(wxICON(M_WND_ICON128)); - - wxScrolledWindow* scrolledWindow = new wxScrolledWindow(this); - - wxBoxSizer* mainSizer = new wxBoxSizer(wxVERTICAL); - - m_scrolledSizer = new wxBoxSizer(wxVERTICAL); - - AddHeaderInfo(scrolledWindow, m_scrolledSizer); - m_scrolledSizer->AddSpacer(5); - AddLibInfo(scrolledWindow, m_scrolledSizer); - m_scrolledSizer->AddSpacer(5); - AddThanks(scrolledWindow, m_scrolledSizer); - - scrolledWindow->SetSizer(m_scrolledSizer); - scrolledWindow->FitInside(); - scrolledWindow->SetScrollRate(25, 25); - mainSizer->Add(scrolledWindow, wxSizerFlags(1).Expand().Border(wxLEFT, 10)); - - SetSizer(mainSizer); - CentreOnParent(); - } - - void AddHeaderInfo(wxWindow* parent, wxSizer* sizer) - { - auto versionString = formatWxString(_("Cemu\nVersion {0}\nCompiled on {1}\nOriginal authors: {2}"), BUILD_VERSION_STRING, BUILD_DATE, "Exzap, Petergov"); - - sizer->Add(new wxStaticText(parent, wxID_ANY, versionString), wxSizerFlags().Border(wxALL, 3).Border(wxTOP, 10)); - sizer->Add(new wxHyperlinkCtrl(parent, wxID_ANY, "https://cemu.info", "https://cemu.info", wxDefaultPosition, wxDefaultSize, (wxHL_CONTEXTMENU|wxNO_BORDER|wxHL_ALIGN_LEFT)), wxSizerFlags().Expand().Border(wxTOP | wxBOTTOM, 3)); - - sizer->AddSpacer(3); - sizer->Add(new wxStaticLine(parent), wxSizerFlags().Expand().Border(wxRIGHT, 4)); - sizer->AddSpacer(5); - - wxString extraInfo(_("Cemu is a Wii U emulator.\n\nWii and Wii U are trademarks of Nintendo.\nCemu is not affiliated with Nintendo.")); - sizer->Add(new wxStaticText(parent, wxID_ANY, extraInfo), wxSizerFlags()); - } - - void AddLibInfo(wxWindow* parent, wxSizer* sizer) - { - sizer->AddSpacer(3); - sizer->Add(new wxStaticLine(parent), wxSizerFlags().Expand().Border(wxRIGHT, 4)); - sizer->AddSpacer(3); - - sizer->Add(new wxStaticText(parent, wxID_ANY, _("Used libraries and utilities:")), wxSizerFlags().Expand().Border(wxTOP | wxBOTTOM, 2)); - // zLib - { - wxSizer* lineSizer = new wxBoxSizer(wxHORIZONTAL); - lineSizer->Add(new wxStaticText(parent, wxID_ANY, "zLib (")); - lineSizer->Add(new wxHyperlinkCtrl(parent, wxID_ANY, "https://www.zlib.net", "https://www.zlib.net", wxDefaultPosition, wxDefaultSize, (wxHL_CONTEXTMENU|wxNO_BORDER|wxHL_ALIGN_LEFT))); - lineSizer->Add(new wxStaticText(parent, wxID_ANY, ")")); - sizer->Add(lineSizer); - } - // wxWidgets - { - wxSizer* lineSizer = new wxBoxSizer(wxHORIZONTAL); - lineSizer->Add(new wxStaticText(parent, wxID_ANY, "wxWidgets (")); - lineSizer->Add(new wxHyperlinkCtrl(parent, wxID_ANY, "https://www.wxwidgets.org/", "https://www.wxwidgets.org/", wxDefaultPosition, wxDefaultSize, (wxHL_CONTEXTMENU|wxNO_BORDER|wxHL_ALIGN_LEFT))); - lineSizer->Add(new wxStaticText(parent, wxID_ANY, ")")); - sizer->Add(lineSizer); - } - // OpenSSL - { - wxSizer* lineSizer = new wxBoxSizer(wxHORIZONTAL); - lineSizer->Add(new wxStaticText(parent, wxID_ANY, "OpenSSL (")); - lineSizer->Add(new wxHyperlinkCtrl(parent, wxID_ANY, "https://www.openssl.org/", "https://www.openssl.org/", wxDefaultPosition, wxDefaultSize, (wxHL_CONTEXTMENU|wxNO_BORDER|wxHL_ALIGN_LEFT))); - lineSizer->Add(new wxStaticText(parent, wxID_ANY, ")")); - sizer->Add(lineSizer); - } - // libcurl - { - wxSizer* lineSizer = new wxBoxSizer(wxHORIZONTAL); - lineSizer->Add(new wxStaticText(parent, wxID_ANY, "libcurl (")); - lineSizer->Add(new wxHyperlinkCtrl(parent, wxID_ANY, "https://curl.haxx.se/libcurl/", "https://curl.haxx.se/libcurl/", wxDefaultPosition, wxDefaultSize, (wxHL_CONTEXTMENU|wxNO_BORDER|wxHL_ALIGN_LEFT))); - lineSizer->Add(new wxStaticText(parent, wxID_ANY, ")")); - sizer->Add(lineSizer); - } - // imgui - { - wxSizer* lineSizer = new wxBoxSizer(wxHORIZONTAL); - lineSizer->Add(new wxStaticText(parent, wxID_ANY, "imgui (")); - lineSizer->Add(new wxHyperlinkCtrl(parent, wxID_ANY, "https://github.com/ocornut/imgui", "https://github.com/ocornut/imgui", wxDefaultPosition, wxDefaultSize, (wxHL_CONTEXTMENU|wxNO_BORDER|wxHL_ALIGN_LEFT))); - lineSizer->Add(new wxStaticText(parent, wxID_ANY, ")")); - sizer->Add(lineSizer); - } - // fontawesome - { - wxSizer* lineSizer = new wxBoxSizer(wxHORIZONTAL); - lineSizer->Add(new wxStaticText(parent, wxID_ANY, "fontawesome (")); - lineSizer->Add(new wxHyperlinkCtrl(parent, wxID_ANY, "https://github.com/FortAwesome/Font-Awesome", "https://github.com/FortAwesome/Font-Awesome", wxDefaultPosition, wxDefaultSize, (wxHL_CONTEXTMENU|wxNO_BORDER|wxHL_ALIGN_LEFT))); - lineSizer->Add(new wxStaticText(parent, wxID_ANY, ")")); - sizer->Add(lineSizer); - } - // boost - { - wxSizer* lineSizer = new wxBoxSizer(wxHORIZONTAL); - lineSizer->Add(new wxStaticText(parent, wxID_ANY, "boost (")); - lineSizer->Add(new wxHyperlinkCtrl(parent, wxID_ANY, "https://www.boost.org", "https://www.boost.org", wxDefaultPosition, wxDefaultSize, (wxHL_CONTEXTMENU|wxNO_BORDER|wxHL_ALIGN_LEFT))); - lineSizer->Add(new wxStaticText(parent, wxID_ANY, ")")); - sizer->Add(lineSizer); - } - // libusb - { - wxSizer* lineSizer = new wxBoxSizer(wxHORIZONTAL); - lineSizer->Add(new wxStaticText(parent, wxID_ANY, "libusb (")); - lineSizer->Add(new wxHyperlinkCtrl(parent, wxID_ANY, "https://libusb.info", "https://libusb.info", wxDefaultPosition, wxDefaultSize, (wxHL_CONTEXTMENU|wxNO_BORDER|wxHL_ALIGN_LEFT))); - lineSizer->Add(new wxStaticText(parent, wxID_ANY, ")")); - sizer->Add(lineSizer); - } -#if BOOST_OS_MACOS - // MoltenVK - { - wxSizer* lineSizer = new wxBoxSizer(wxHORIZONTAL); - lineSizer->Add(new wxStaticText(parent, -1, "MoltenVK (")); - lineSizer->Add(new wxHyperlinkCtrl(parent, -1, "https://github.com/KhronosGroup/MoltenVK", "https://github.com/KhronosGroup/MoltenVK", wxDefaultPosition, wxDefaultSize, (wxHL_CONTEXTMENU|wxNO_BORDER|wxHL_ALIGN_LEFT))); - lineSizer->Add(new wxStaticText(parent, -1, ")")); - sizer->Add(lineSizer); - } -#endif - // icons - { - wxSizer* lineSizer = new wxBoxSizer(wxHORIZONTAL); - lineSizer->Add(new wxStaticText(parent, wxID_ANY, "icons from ")); - lineSizer->Add(new wxHyperlinkCtrl(parent, wxID_ANY, "https://icons8.com", "https://icons8.com", wxDefaultPosition, wxDefaultSize, (wxHL_CONTEXTMENU|wxNO_BORDER|wxHL_ALIGN_LEFT))); - sizer->Add(lineSizer); - } - // Lato font (are we still using it?) - { - wxSizer* lineSizer = new wxBoxSizer(wxHORIZONTAL); - lineSizer->Add(new wxStaticText(parent, wxID_ANY, "\"Lato\" font by tyPoland Lukasz Dziedzic (OFL, V1.1)", wxDefaultPosition, wxDefaultSize, (wxHL_CONTEXTMENU|wxNO_BORDER|wxHL_ALIGN_LEFT))); - sizer->Add(lineSizer); - } - // SDL - { - wxSizer* lineSizer = new wxBoxSizer(wxHORIZONTAL); - lineSizer->Add(new wxStaticText(parent, wxID_ANY, "SDL (")); - lineSizer->Add(new wxHyperlinkCtrl(parent, wxID_ANY, "https://github.com/libsdl-org/SDL", "https://github.com/libsdl-org/SDL", wxDefaultPosition, wxDefaultSize, (wxHL_CONTEXTMENU|wxNO_BORDER|wxHL_ALIGN_LEFT))); - lineSizer->Add(new wxStaticText(parent, wxID_ANY, ")")); - sizer->Add(lineSizer); - } - // IH264 - { - wxSizer* lineSizer = new wxBoxSizer(wxHORIZONTAL); - lineSizer->Add(new wxStaticText(parent, wxID_ANY, "Modified ih264 from Android project (")); - lineSizer->Add(new wxHyperlinkCtrl(parent, wxID_ANY, "Source", "https://cemu.info/oss/ih264d.zip", wxDefaultPosition, wxDefaultSize, (wxHL_CONTEXTMENU|wxNO_BORDER|wxHL_ALIGN_LEFT))); - lineSizer->Add(new wxStaticText(parent, wxID_ANY, " ")); - wxHyperlinkCtrl* noticeLink = new wxHyperlinkCtrl(parent, wxID_ANY, "NOTICE", "", wxDefaultPosition, wxDefaultSize, (wxHL_CONTEXTMENU|wxNO_BORDER|wxHL_ALIGN_LEFT)); - noticeLink->Bind(wxEVT_LEFT_DOWN, [](wxMouseEvent& event) - { - fs::path tempPath = fs::temp_directory_path(); - tempPath.append("NOTICE_IH264.txt"); - FileStream* fs = FileStream::createFile2(tempPath); - if (!fs) - return; - fs->writeString( - "/******************************************************************************\r\n" - " *\r\n" - " * Copyright (C) 2015 The Android Open Source Project\r\n" - " *\r\n" - " * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n" - " * you may not use this file except in compliance with the License.\r\n" - " * You may obtain a copy of the License at:" - " *\r\n" - " * http://www.apache.org/licenses/LICENSE-2.0\r\n" - " *\r\n" - " * Unless required by applicable law or agreed to in writing, software\r\n" - " * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n" - " * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n" - " * See the License for the specific language governing permissions and\r\n" - " * limitations under the License.\r\n" - " *\r\n" - " *****************************************************************************\r\n" - " * Originally developed and contributed by Ittiam Systems Pvt. Ltd, Bangalore\r\n" - "*/\r\n" - "/*****************************************************************************/\r\n" - ); - delete fs; - wxLaunchDefaultBrowser(formatWxString("file:{}", _pathToUtf8(tempPath))); - }); - lineSizer->Add(noticeLink); - lineSizer->Add(new wxStaticText(parent, wxID_ANY, ")")); - sizer->Add(lineSizer); - } - } - - void AddThanks(wxWindow* parent, wxSizer* sizer) - { - sizer->AddSpacer(3); - sizer->Add(new wxStaticLine(parent), wxSizerFlags().Expand().Border(wxRIGHT, 4)); - sizer->AddSpacer(3); - - wxGridSizer* gridSizer = new wxGridSizer(1, 2, 0, 0); - - sizer->AddSpacer(2); - - sizer->Add(new wxStaticText(parent, wxID_ANY, _("Thanks to our Patreon supporters:")), wxSizerFlags().Expand().Border(wxTOP | wxBOTTOM, 2)); - - std::vector patreonSupporterNames{ "Maufeat", "lvlv", "F34R", "John Godgames", "Jameel Lewis", "skooks", "Cheesy", "Barrowsx", "Mored1984", "madmat007" - , "Kuhnnl", "Owen M", "lucianobugalu", "KimoMaka", "nick palma aka renaissance18", "TheGiantBros", "SpiGAndromeda" - , "Chimech0", "Nicolás Pino", "Pezzatti", "Barry Wallace", "REGNR8 Productions", "Lagia", "Freestyler316", "Dentora" - , "tactics", "Merola.C", "Ceigyx", "Mata", "BobSchneeder45", "fenixDG", "jjalapeno55", "FissionMetroid101", "Jetta88" - , "nesxdie", "Mikah", "PornfoxVR.com", "Hunter4everosa", "Bbzx", "Salim Sanehi", "FalloutpunkX", "NashOH-CL", "RaheemWala" - , "Faris Leonhart", "MahvZero", "PlaguedGuardian", "Stuffie", "CaptainLester", "Qtech", "Zaurexus", "Leonidas", "Artifesto" - , "Alca259", "SirWestofAsh", "Loli Co.", "The Technical Revolutionary", "MegaYama", "mitori", "Seymordius", "Adrian Josh Cruz", "Manuel Hoenings", "Just A Jabb" - , "pgantonio", "CannonXIII", "Lonewolf00708", "AlexsDesign.com", "NoskLo", "MrSirHaku", "xElite_V AKA William H. Johnson", "Zalnor", "Pig", "James \"SE4LS\"", "DairyOrange", "Horoko Lawrence", "bloodmc", "Officer Jenny", "Quasar", "Postposterous", "Jake Jackson", "Kaydax", "CthePredatorG" - , "Hengi", "Pyrochaser", "luma.x3"}; - - wxString nameListLeft, nameListRight; - for (size_t i = 0; i < patreonSupporterNames.size(); i++) - { - const char* name = patreonSupporterNames[i]; - wxString& nameList = ((i % 2) == 0) ? nameListLeft : nameListRight; - if (i >= 2) - nameList.append("\n"); - nameList.append(wxString::FromUTF8(name)); - } - - gridSizer->Add(new wxStaticText(parent, wxID_ANY, nameListLeft), wxSizerFlags()); - gridSizer->Add(new wxStaticText(parent, wxID_ANY, nameListRight), wxSizerFlags()); - - sizer->AddSpacer(4); - - sizer->Add(gridSizer, 1, wxEXPAND); - - sizer->AddSpacer(2); - sizer->Add(new wxStaticText(parent, wxID_ANY, _("Special thanks:")), wxSizerFlags().Expand().Border(wxTOP, 2)); - sizer->Add(new wxStaticText(parent, wxID_ANY, "espes - Also try XQEMU!\nWaltzz92"), wxSizerFlags().Expand().Border(wxTOP, 1)); - } - -protected: - wxSizer* m_scrolledSizer; -}; - -void MainWindow::OnHelpAbout(wxCommandEvent& event) -{ - CemuAboutDialog dlgAbout(this); - dlgAbout.ShowModal(); -} - -void MainWindow::OnHelpUpdate(wxCommandEvent& event) -{ - CemuUpdateWindow test(this); - test.ShowModal(); -} - -void MainWindow::RecreateMenu() -{ - if (m_menuBar) - { - SetMenuBar(nullptr); - m_menuBar->Destroy(); - m_menuBar = nullptr; - } - - auto& guiConfig = GetWxGUIConfig(); - - m_menuBar = new wxMenuBar(); - // file submenu - m_fileMenu = new wxMenu(); - - if (!m_game_launched) - { - m_loadMenuItem = m_fileMenu->Append(MAINFRAME_MENU_ID_FILE_LOAD, _("&Load...")); - m_installUpdateMenuItem = m_fileMenu->Append(MAINFRAME_MENU_ID_FILE_INSTALL_UPDATE, _("&Install game title, update or DLC...")); - - wxMenu* recentMenu = new wxMenu(); - sint32 recentFileIndex = 1; - m_fileMenuSeparator0 = nullptr; - m_fileMenuSeparator1 = nullptr; - for (size_t i = 0; i < guiConfig.recent_launch_files.size(); i++) - { - const std::string& pathStr = guiConfig.recent_launch_files[i]; - if (pathStr.empty()) - continue; - recentMenu->Append(MAINFRAME_MENU_ID_FILE_RECENT_0 + i, formatWxString("{}. {}", recentFileIndex, pathStr)); - recentFileIndex++; - - if (recentFileIndex >= 10) - break; - } - if (recentFileIndex == 0) - { - wxMenuItem* placeholder = recentMenu->Append(wxID_NONE, _("(No recent files)")); - placeholder->Enable(false); - } - - m_fileMenu->AppendSeparator(); - m_fileMenu->AppendSubMenu(recentMenu, _("Recent files")); - m_fileMenu->AppendSeparator(); - } - else - { -#ifdef CEMU_DEBUG_ASSERT - m_fileMenu->Append(MAINFRAME_MENU_ID_FILE_END_EMULATION, _("Close game")); - m_fileMenuSeparator1 = m_fileMenu->AppendSeparator(); -#endif - } - - m_fileMenu->Append(MAINFRAME_MENU_ID_FILE_OPEN_CEMU_FOLDER, _("Open Cemu folder")); - m_fileMenu->Append(MAINFRAME_MENU_ID_FILE_OPEN_MLC_FOLDER, _("Open MLC folder")); - m_fileMenu->Append(MAINFRAME_MENU_ID_FILE_OPEN_SHADERCACHE_FOLDER, _("Open &shader cache folder")); - m_fileMenu->AppendSeparator(); - m_fileMenu->Append(MAINFRAME_MENU_ID_FILE_CLEAR_SPOTPASS_CACHE, _("Clear Spot&Pass cache")); - if (m_game_launched) - m_fileMenu->Enable(MAINFRAME_MENU_ID_FILE_CLEAR_SPOTPASS_CACHE, false); - m_fileMenu->AppendSeparator(); - m_exitMenuItem = m_fileMenu->Append(MAINFRAME_MENU_ID_FILE_EXIT, _("&Exit")); - m_menuBar->Append(m_fileMenu, _("&File")); - // options->account submenu - m_optionsAccountMenu = new wxMenu(); - const auto account_id = ActiveSettings::GetPersistentId(); - int index = 0; - for(const auto& account : Account::GetAccounts()) - { - wxMenuItem* item = m_optionsAccountMenu->AppendRadioItem(MAINFRAME_MENU_ID_OPTIONS_ACCOUNT_1 + index, account.ToString()); - item->Check(account_id == account.GetPersistentId()); - if (m_game_launched || LaunchSettings::GetPersistentId().has_value()) - item->Enable(false); - - ++index; - } - - auto& config = GetConfig(); - auto& wxConfig = GetWxGUIConfig(); - // options->console language submenu - wxMenu* optionsConsoleLanguageMenu = new wxMenu(); - optionsConsoleLanguageMenu->AppendRadioItem(MAINFRAME_MENU_ID_OPTIONS_LANGUAGE_ENGLISH, _("&English"))->Check(config.console_language == CafeConsoleLanguage::EN); - optionsConsoleLanguageMenu->AppendRadioItem(MAINFRAME_MENU_ID_OPTIONS_LANGUAGE_JAPANESE, _("&Japanese"))->Check(config.console_language == CafeConsoleLanguage::JA); - optionsConsoleLanguageMenu->AppendRadioItem(MAINFRAME_MENU_ID_OPTIONS_LANGUAGE_FRENCH, _("&French"))->Check(config.console_language == CafeConsoleLanguage::FR); - optionsConsoleLanguageMenu->AppendRadioItem(MAINFRAME_MENU_ID_OPTIONS_LANGUAGE_GERMAN, _("&German"))->Check(config.console_language == CafeConsoleLanguage::DE); - optionsConsoleLanguageMenu->AppendRadioItem(MAINFRAME_MENU_ID_OPTIONS_LANGUAGE_ITALIAN, _("&Italian"))->Check(config.console_language == CafeConsoleLanguage::IT); - optionsConsoleLanguageMenu->AppendRadioItem(MAINFRAME_MENU_ID_OPTIONS_LANGUAGE_SPANISH, _("&Spanish"))->Check(config.console_language == CafeConsoleLanguage::ES); - optionsConsoleLanguageMenu->AppendRadioItem(MAINFRAME_MENU_ID_OPTIONS_LANGUAGE_CHINESE, _("&Chinese"))->Check(config.console_language == CafeConsoleLanguage::ZH); - optionsConsoleLanguageMenu->AppendRadioItem(MAINFRAME_MENU_ID_OPTIONS_LANGUAGE_KOREAN, _("&Korean"))->Check(config.console_language == CafeConsoleLanguage::KO); - optionsConsoleLanguageMenu->AppendRadioItem(MAINFRAME_MENU_ID_OPTIONS_LANGUAGE_DUTCH, _("&Dutch"))->Check(config.console_language == CafeConsoleLanguage::NL); - optionsConsoleLanguageMenu->AppendRadioItem(MAINFRAME_MENU_ID_OPTIONS_LANGUAGE_PORTUGUESE, _("&Portuguese"))->Check(config.console_language == CafeConsoleLanguage::PT); - optionsConsoleLanguageMenu->AppendRadioItem(MAINFRAME_MENU_ID_OPTIONS_LANGUAGE_RUSSIAN, _("&Russian"))->Check(config.console_language == CafeConsoleLanguage::RU); - optionsConsoleLanguageMenu->AppendRadioItem(MAINFRAME_MENU_ID_OPTIONS_LANGUAGE_TAIWANESE, _("&Taiwanese"))->Check(config.console_language == CafeConsoleLanguage::TW); - if(IsGameLaunched()) - { - auto items = optionsConsoleLanguageMenu->GetMenuItems(); - for (auto& item : items) - { - item->Enable(false); - } - } - - // options submenu - wxMenu* optionsMenu = new wxMenu(); - m_fullscreenMenuItem = optionsMenu->AppendCheckItem(MAINFRAME_MENU_ID_OPTIONS_FULLSCREEN, _("&Fullscreen")); - m_fullscreenMenuItem->Check(FullscreenEnabled()); - - optionsMenu->Append(MAINFRAME_MENU_ID_OPTIONS_GRAPHIC_PACKS2, _("&Graphic packs")); - m_padViewMenuItem = optionsMenu->AppendCheckItem(MAINFRAME_MENU_ID_OPTIONS_SECOND_WINDOW_PADVIEW, _("&Separate GamePad view")); - m_padViewMenuItem->Check(wxConfig.pad_open); - optionsMenu->AppendSeparator(); - #if BOOST_OS_MACOS - optionsMenu->Append(MAINFRAME_MENU_ID_OPTIONS_MAC_SETTINGS, _("&Settings..." "\tCtrl-,")); - #endif - optionsMenu->Append(MAINFRAME_MENU_ID_OPTIONS_GENERAL2, _("&General settings")); - optionsMenu->Append(MAINFRAME_MENU_ID_OPTIONS_INPUT, _("&Input settings")); - optionsMenu->Append(MAINFRAME_MENU_ID_OPTIONS_HOTKEY, _("&Hotkey settings")); - - optionsMenu->AppendSeparator(); - optionsMenu->AppendSubMenu(m_optionsAccountMenu, _("&Active account")); - optionsMenu->AppendSubMenu(optionsConsoleLanguageMenu, _("&Console language")); - m_menuBar->Append(optionsMenu, _("&Options")); - - // tools submenu - wxMenu* toolsMenu = new wxMenu(); - m_memorySearcherMenuItem = toolsMenu->Append(MAINFRAME_MENU_ID_TOOLS_MEMORY_SEARCHER, _("&Memory searcher")); - m_memorySearcherMenuItem->Enable(false); - toolsMenu->Append(MAINFRAME_MENU_ID_TOOLS_TITLE_MANAGER, _("&Title Manager")); - toolsMenu->Append(MAINFRAME_MENU_ID_TOOLS_DOWNLOAD_MANAGER, _("&Download Manager")); - toolsMenu->Append(MAINFRAME_MENU_ID_TOOLS_EMULATED_USB_DEVICES, _("&Emulated USB Devices")); - - m_menuBar->Append(toolsMenu, _("&Tools")); - - // cpu timer speed menu - wxMenu* timerSpeedMenu = new wxMenu(); - timerSpeedMenu->AppendRadioItem(MAINFRAME_MENU_ID_TIMER_SPEED_1X, _("&1x speed"))->Check(ActiveSettings::GetTimerShiftFactor() == 3); - timerSpeedMenu->AppendRadioItem(MAINFRAME_MENU_ID_TIMER_SPEED_2X, _("&2x speed"))->Check(ActiveSettings::GetTimerShiftFactor() == 2); - timerSpeedMenu->AppendRadioItem(MAINFRAME_MENU_ID_TIMER_SPEED_4X, _("&4x speed"))->Check(ActiveSettings::GetTimerShiftFactor() == 1); - timerSpeedMenu->AppendRadioItem(MAINFRAME_MENU_ID_TIMER_SPEED_8X, _("&8x speed"))->Check(ActiveSettings::GetTimerShiftFactor() == 0); - timerSpeedMenu->AppendRadioItem(MAINFRAME_MENU_ID_TIMER_SPEED_05X, _("&0.5x speed"))->Check(ActiveSettings::GetTimerShiftFactor() == 4); - timerSpeedMenu->AppendRadioItem(MAINFRAME_MENU_ID_TIMER_SPEED_025X, _("&0.25x speed"))->Check(ActiveSettings::GetTimerShiftFactor() == 5); - timerSpeedMenu->AppendRadioItem(MAINFRAME_MENU_ID_TIMER_SPEED_0125X, _("&0.125x speed"))->Check(ActiveSettings::GetTimerShiftFactor() == 6); - - // cpu submenu - wxMenu* cpuMenu = new wxMenu(); - cpuMenu->AppendSubMenu(timerSpeedMenu, _("&Timer speed")); - m_menuBar->Append(cpuMenu, _("&CPU")); - - // nfc submenu - wxMenu* nfcMenu = new wxMenu(); - m_nfcMenu = nfcMenu; - nfcMenu->Append(MAINFRAME_MENU_ID_NFC_TOUCH_NFC_FILE, _("&Scan NFC tag/amiibo from file"))->Enable(false); - m_menuBar->Append(nfcMenu, _("&NFC")); - m_nfcMenuSeparator0 = nullptr; - // debug->logging submenu - wxMenu* debugLoggingMenu = new wxMenu(); - - debugLoggingMenu->AppendCheckItem(MAINFRAME_MENU_ID_DEBUG_LOGGING0 + stdx::to_underlying(LogType::UnsupportedAPI), _("&Unsupported API calls"))->Check(cemuLog_isLoggingEnabled(LogType::UnsupportedAPI)); - debugLoggingMenu->AppendCheckItem(MAINFRAME_MENU_ID_DEBUG_LOGGING0 + stdx::to_underlying(LogType::APIErrors), _("&Invalid API usage"))->Check(cemuLog_isLoggingEnabled(LogType::APIErrors)); - debugLoggingMenu->AppendCheckItem(MAINFRAME_MENU_ID_DEBUG_LOGGING0 + stdx::to_underlying(LogType::CoreinitLogging), _("&Coreinit Logging (OSReport/OSConsole)"))->Check(cemuLog_isLoggingEnabled(LogType::CoreinitLogging)); - debugLoggingMenu->AppendSeparator(); - - wxMenu* logCosModulesMenu = new wxMenu(); - logCosModulesMenu->AppendCheckItem(MAINFRAME_MENU_ID_DEBUG_LOGGING_MESSAGE, _("&Options below are for experts. Leave off if unsure"))->Enable(false); - logCosModulesMenu->AppendSeparator(); - logCosModulesMenu->AppendCheckItem(MAINFRAME_MENU_ID_DEBUG_LOGGING0 + stdx::to_underlying(LogType::CoreinitFile), _("coreinit File-Access API"))->Check(cemuLog_isLoggingEnabled(LogType::CoreinitFile)); - logCosModulesMenu->AppendCheckItem(MAINFRAME_MENU_ID_DEBUG_LOGGING0 + stdx::to_underlying(LogType::CoreinitThreadSync), _("coreinit Thread-Synchronization API"))->Check(cemuLog_isLoggingEnabled(LogType::CoreinitThreadSync)); - logCosModulesMenu->AppendCheckItem(MAINFRAME_MENU_ID_DEBUG_LOGGING0 + stdx::to_underlying(LogType::CoreinitMem), _("coreinit Memory API"))->Check(cemuLog_isLoggingEnabled(LogType::CoreinitMem)); - logCosModulesMenu->AppendCheckItem(MAINFRAME_MENU_ID_DEBUG_LOGGING0 + stdx::to_underlying(LogType::CoreinitMP), _("coreinit MP API"))->Check(cemuLog_isLoggingEnabled(LogType::CoreinitMP)); - logCosModulesMenu->AppendCheckItem(MAINFRAME_MENU_ID_DEBUG_LOGGING0 + stdx::to_underlying(LogType::CoreinitThread), _("coreinit Thread API"))->Check(cemuLog_isLoggingEnabled(LogType::CoreinitThread)); - logCosModulesMenu->AppendCheckItem(MAINFRAME_MENU_ID_DEBUG_LOGGING0 + stdx::to_underlying(LogType::Save), _("nn_save API"))->Check(cemuLog_isLoggingEnabled(LogType::Save)); - logCosModulesMenu->AppendCheckItem(MAINFRAME_MENU_ID_DEBUG_LOGGING0 + stdx::to_underlying(LogType::NN_NFP), _("nn_nfp API"))->Check(cemuLog_isLoggingEnabled(LogType::NN_NFP)); - logCosModulesMenu->AppendCheckItem(MAINFRAME_MENU_ID_DEBUG_LOGGING0 + stdx::to_underlying(LogType::NN_FP), _("nn_fp API"))->Check(cemuLog_isLoggingEnabled(LogType::NN_FP)); - logCosModulesMenu->AppendCheckItem(MAINFRAME_MENU_ID_DEBUG_LOGGING0 + stdx::to_underlying(LogType::PRUDP), _("nn_fp PRUDP"))->Check(cemuLog_isLoggingEnabled(LogType::PRUDP)); - logCosModulesMenu->AppendCheckItem(MAINFRAME_MENU_ID_DEBUG_LOGGING0 + stdx::to_underlying(LogType::NN_BOSS), _("nn_boss API"))->Check(cemuLog_isLoggingEnabled(LogType::NN_BOSS)); - logCosModulesMenu->AppendCheckItem(MAINFRAME_MENU_ID_DEBUG_LOGGING0 + stdx::to_underlying(LogType::NFC), _("nfc API"))->Check(cemuLog_isLoggingEnabled(LogType::NFC)); - logCosModulesMenu->AppendCheckItem(MAINFRAME_MENU_ID_DEBUG_LOGGING0 + stdx::to_underlying(LogType::NTAG), _("ntag API"))->Check(cemuLog_isLoggingEnabled(LogType::NTAG)); - logCosModulesMenu->AppendCheckItem(MAINFRAME_MENU_ID_DEBUG_LOGGING0 + stdx::to_underlying(LogType::Socket), _("nsysnet API"))->Check(cemuLog_isLoggingEnabled(LogType::Socket)); - logCosModulesMenu->AppendCheckItem(MAINFRAME_MENU_ID_DEBUG_LOGGING0 + stdx::to_underlying(LogType::H264), _("h264 API"))->Check(cemuLog_isLoggingEnabled(LogType::H264)); - logCosModulesMenu->AppendCheckItem(MAINFRAME_MENU_ID_DEBUG_LOGGING0 + stdx::to_underlying(LogType::GX2), _("gx2 API"))->Check(cemuLog_isLoggingEnabled(LogType::GX2)); - logCosModulesMenu->AppendCheckItem(MAINFRAME_MENU_ID_DEBUG_LOGGING0 + stdx::to_underlying(LogType::SWKBD), _("swkbd API"))->Check(cemuLog_isLoggingEnabled(LogType::SWKBD)); - logCosModulesMenu->AppendCheckItem(MAINFRAME_MENU_ID_DEBUG_LOGGING0 + stdx::to_underlying(LogType::SoundAPI), _("Audio API"))->Check(cemuLog_isLoggingEnabled(LogType::SoundAPI)); - logCosModulesMenu->AppendCheckItem(MAINFRAME_MENU_ID_DEBUG_LOGGING0 + stdx::to_underlying(LogType::InputAPI), _("Input API"))->Check(cemuLog_isLoggingEnabled(LogType::InputAPI)); - - debugLoggingMenu->AppendSubMenu(logCosModulesMenu, _("&CafeOS modules logging")); - debugLoggingMenu->AppendSeparator(); - debugLoggingMenu->AppendCheckItem(MAINFRAME_MENU_ID_DEBUG_LOGGING0 + stdx::to_underlying(LogType::Patches), _("&Graphic pack patches"))->Check(cemuLog_isLoggingEnabled(LogType::Patches)); - debugLoggingMenu->AppendCheckItem(MAINFRAME_MENU_ID_DEBUG_LOGGING0 + stdx::to_underlying(LogType::TextureCache), _("&Texture cache warnings"))->Check(cemuLog_isLoggingEnabled(LogType::TextureCache)); - debugLoggingMenu->AppendCheckItem(MAINFRAME_MENU_ID_DEBUG_LOGGING0 + stdx::to_underlying(LogType::TextureReadback), _("&Texture readback"))->Check(cemuLog_isLoggingEnabled(LogType::TextureReadback)); - debugLoggingMenu->AppendSeparator(); - debugLoggingMenu->AppendCheckItem(MAINFRAME_MENU_ID_DEBUG_LOGGING0 + stdx::to_underlying(LogType::OpenGLLogging), _("&OpenGL debug output"))->Check(cemuLog_isLoggingEnabled(LogType::OpenGLLogging)); - debugLoggingMenu->AppendCheckItem(MAINFRAME_MENU_ID_DEBUG_LOGGING0 + stdx::to_underlying(LogType::VulkanValidation), _("&Vulkan validation layer (slow)"))->Check(cemuLog_isLoggingEnabled(LogType::VulkanValidation)); - debugLoggingMenu->AppendCheckItem(MAINFRAME_MENU_ID_DEBUG_ADVANCED_PPC_INFO, _("&Log PPC context for API"))->Check(cemuLog_advancedPPCLoggingEnabled()); - m_loggingSubmenu = debugLoggingMenu; - // debug->dump submenu - wxMenu* debugDumpMenu = new wxMenu; - debugDumpMenu->AppendCheckItem(MAINFRAME_MENU_ID_DEBUG_DUMP_TEXTURES, _("&Textures"))->Check(ActiveSettings::DumpTexturesEnabled()); - debugDumpMenu->AppendCheckItem(MAINFRAME_MENU_ID_DEBUG_DUMP_SHADERS, _("&Shaders"))->Check(ActiveSettings::DumpShadersEnabled()); - debugDumpMenu->AppendCheckItem(MAINFRAME_MENU_ID_DEBUG_DUMP_RECOMPILER_FUNCTIONS, _("&Recompiled functions"))->Check(ActiveSettings::DumpRecompilerFunctionsEnabled()); - debugDumpMenu->AppendCheckItem(MAINFRAME_MENU_ID_DEBUG_DUMP_CURL_REQUESTS, _("&nlibcurl HTTP/HTTPS requests")); - // debug submenu - wxMenu* debugMenu = new wxMenu(); - m_debugMenu = debugMenu; - debugMenu->AppendSubMenu(debugLoggingMenu, _("&Logging")); - debugMenu->AppendSubMenu(debugDumpMenu, _("&Dump")); - debugMenu->AppendSeparator(); - - auto upsidedownItem = debugMenu->AppendCheckItem(MAINFRAME_MENU_ID_DEBUG_RENDER_UPSIDE_DOWN, _("&Render upside-down")); - upsidedownItem->Check(ActiveSettings::RenderUpsideDownEnabled()); - if(LaunchSettings::RenderUpsideDownEnabled().has_value()) - upsidedownItem->Enable(false); - - auto accurateBarriers = debugMenu->AppendCheckItem(MAINFRAME_MENU_ID_DEBUG_VK_ACCURATE_BARRIERS, _("&Accurate barriers (Vulkan)")); - accurateBarriers->Check(GetConfig().vk_accurate_barriers); - -#ifdef ENABLE_METAL - auto gpuCapture = debugMenu->Append(MAINFRAME_MENU_ID_DEBUG_GPU_CAPTURE, _("&GPU capture (Metal)")); - gpuCapture->Enable(m_game_launched && g_renderer->GetType() == RendererAPI::Metal); -#endif - - debugMenu->AppendSeparator(); - -#ifdef CEMU_DEBUG_ASSERT - auto audioAuxOnly = debugMenu->AppendCheckItem(MAINFRAME_MENU_ID_DEBUG_AUDIO_AUX_ONLY, _("&Audio AUX only")); - audioAuxOnly->Check(ActiveSettings::AudioOutputOnlyAux()); -#endif - - debugMenu->Append(MAINFRAME_MENU_ID_DEBUG_VIEW_LOGGING_WINDOW, _("&Open logging window")); - m_gdbstub_toggle = debugMenu->AppendCheckItem(MAINFRAME_MENU_ID_DEBUG_TOGGLE_GDB_STUB, _("&Launch with GDB stub")); - m_gdbstub_toggle->Check(g_gdbstub != nullptr); - m_gdbstub_toggle->Enable(!m_game_launched); - - debugMenu->Append(MAINFRAME_MENU_ID_DEBUG_VIEW_PPC_THREADS, _("&View PPC threads")); - debugMenu->Append(MAINFRAME_MENU_ID_DEBUG_VIEW_PPC_DEBUGGER, _("&View PPC debugger")); - debugMenu->Append(MAINFRAME_MENU_ID_DEBUG_VIEW_AUDIO_DEBUGGER, _("&View audio debugger")); - debugMenu->Append(MAINFRAME_MENU_ID_DEBUG_VIEW_TEXTURE_RELATIONS, _("&View texture cache info")); - debugMenu->Append(MAINFRAME_MENU_ID_DEBUG_DUMP_RAM, _("&Dump current RAM"))->Enable(m_game_launched); - debugMenu->Append(MAINFRAME_MENU_ID_DEBUG_DUMP_RPX, _("&Dump current RPX"))->Enable(m_game_launched); - // debugMenu->Append(MAINFRAME_MENU_ID_DEBUG_DUMP_FST, _("&Dump WUD filesystem"))->Enable(false); - - m_menuBar->Append(debugMenu, _("&Debug")); - // help menu - wxMenu* helpMenu = new wxMenu(); - m_check_update_menu = helpMenu->Append(MAINFRAME_MENU_ID_HELP_UPDATE, _("&Check for updates")); -#if BOOST_OS_LINUX - if (!std::getenv("APPIMAGE")) { - m_check_update_menu->Enable(false); - } -#elif BOOST_OS_BSD // BSD users must update from source so disable update checks - m_check_update_menu->Enable(false); -#endif - helpMenu->AppendSeparator(); - helpMenu->Append(MAINFRAME_MENU_ID_HELP_ABOUT, _("&About Cemu")); - - m_menuBar->Append(helpMenu, _("&Help")); - - SetMenuBar(m_menuBar); - m_menu_visible = true; - - if (m_game_launched) - { - if (m_check_update_menu) - m_check_update_menu->Enable(false); - - m_memorySearcherMenuItem->Enable(true); - m_nfcMenu->Enable(MAINFRAME_MENU_ID_NFC_TOUCH_NFC_FILE, true); - - // these options cant be toggled after the renderer backend is initialized: - m_loggingSubmenu->Enable(MAINFRAME_MENU_ID_DEBUG_LOGGING0 + stdx::to_underlying(LogType::OpenGLLogging), false); - m_loggingSubmenu->Enable(MAINFRAME_MENU_ID_DEBUG_LOGGING0 + stdx::to_underlying(LogType::VulkanValidation), false); - - UpdateNFCMenu(); - } - - // hide new menu in fullscreen - if (IsFullScreen()) - SetMenuVisible(false); -} - -void MainWindow::UpdateChildWindowTitleRunningState() -{ - const bool running = CafeSystem::IsTitleRunning(); - - if(m_graphic_pack_window) - m_graphic_pack_window->UpdateTitleRunning(running); -} - -void MainWindow::RestoreSettingsAfterGameExited() -{ - RecreateMenu(); -} - -void MainWindow::UpdateSettingsAfterGameLaunch() -{ - m_update_available = {}; - RecreateMenu(); -} - -void MainWindow::OnGraphicWindowClose(wxCloseEvent& event) -{ - m_graphic_pack_window->Destroy(); - m_graphic_pack_window = nullptr; -} - -void MainWindow::OnGraphicWindowOpen(wxTitleIdEvent& event) -{ - if (m_graphic_pack_window) - return; - m_graphic_pack_window = new GraphicPacksWindow2(this, event.GetTitleId()); - m_graphic_pack_window->Bind(wxEVT_CLOSE_WINDOW, &MainWindow::OnGraphicWindowClose, this); - m_graphic_pack_window->Show(true); -} - -void MainWindow::RequestGameListRefresh() -{ - auto* evt = new wxCommandEvent(wxEVT_REQUEST_GAMELIST_REFRESH); - wxQueueEvent(g_mainFrame, evt); -} - -void MainWindow::RequestLaunchGame(fs::path filePath, wxLaunchGameEvent::INITIATED_BY initiatedBy) -{ - wxLaunchGameEvent evt(filePath, initiatedBy); - wxPostEvent(g_mainFrame, evt); -} - -void MainWindow::OnRequestRecreateCanvas(wxCommandEvent& event) -{ - CounterSemaphore* sem = (CounterSemaphore*)event.GetClientData(); - DestroyCanvas(); - CreateCanvas(); - sem->increment(); -} - -void MainWindow::CafeRecreateCanvas() -{ - CounterSemaphore sem; - auto* evt = new wxCommandEvent(wxEVT_REQUEST_RECREATE_CANVAS); - evt->SetClientData((void*)&sem); - wxQueueEvent(g_mainFrame, evt); - sem.decrementWithWait(); -} - -void MainWindow::CafePPCProcessExit() -{ - // this is called from the emulated PPC thread, so queue an event instead of handling it directly - wxQueueEvent(g_mainFrame, new wxCommandEvent(wxEVT_REQUEST_GAME_EXIT)); -} - -void MainWindow::OnRequestGameExit(wxCommandEvent& event) -{ - // if the title was launched via the command line we close Cemu and use the foreground title's exit status as Cemu's process return code (via CemuApp:OnExit) - // this is useful for homebrew testing setups that use Cemu via CLI - // otherwise the title was launched from the game list, so we just stop it and return to the game list instead of closing Cemu - if (LaunchSettings::GetLoadFile() || LaunchSettings::GetLoadTitleID()) - { - Close(); - } - else - { - EndEmulation(); - } -} - -bool MainWindow::FullscreenEnabled() const -{ - return LaunchSettings::FullscreenEnabled().value_or(GetWxGUIConfig().fullscreen); -} diff --git a/src/gui/wxgui/MainWindow.h b/src/gui/wxgui/MainWindow.h deleted file mode 100644 index 483b67fa..00000000 --- a/src/gui/wxgui/MainWindow.h +++ /dev/null @@ -1,248 +0,0 @@ -#pragma once - -#include -#include -#include - -#include "wxgui/PadViewFrame.h" -#include "wxgui/MemorySearcherTool.h" - -#include "config/XMLConfig.h" - -#include "wxgui/LoggingWindow.h" -#include "wxgui/components/wxGameList.h" - -#include -#include "Cafe/HW/Espresso/Debugger/GDBStub.h" -#include "Cafe/CafeSystem.h" - -class DebuggerWindow2; -struct GameEntry; -class DiscordPresence; -class TitleManager; -class GraphicPacksWindow2; -class EmulatedUSBDeviceFrame; -class wxLaunchGameEvent; - -wxDECLARE_EVENT(wxEVT_LAUNCH_GAME, wxLaunchGameEvent); -wxDECLARE_EVENT(wxEVT_SET_WINDOW_TITLE, wxCommandEvent); - -class wxLaunchGameEvent : public wxCommandEvent -{ -public: - enum class INITIATED_BY - { - MENU, // via file menu - DRAG_AND_DROP, - GAME_LIST, - TITLE_MANAGER, - COMMAND_LINE, // -g parameter - }; - - wxLaunchGameEvent(fs::path path, INITIATED_BY initiatedBy) - : wxCommandEvent(wxEVT_LAUNCH_GAME), m_launchPath(path), m_initiatedBy(initiatedBy) {} - - [[nodiscard]] fs::path GetPath() const { return m_launchPath; } - [[nodiscard]] INITIATED_BY GetInitiatedBy() const { return m_initiatedBy; } - - wxEvent* Clone() const { return new wxLaunchGameEvent(*this); } - -private: - fs::path m_launchPath; - INITIATED_BY m_initiatedBy; -}; - -class MainWindow : public wxFrame, public CafeSystem::SystemImplementation -{ - friend class CemuApp; - -public: - MainWindow(); - ~MainWindow(); - - void CreateGameListAndStatusBar(); - void DestroyGameListAndStatusBar(); - - void UpdateSettingsAfterGameLaunch(); - void RestoreSettingsAfterGameExited(); - - bool FileLoad(const fs::path launchPath, wxLaunchGameEvent::INITIATED_BY initiatedBy); - - [[nodiscard]] bool IsGameLaunched() const { return m_game_launched; } - - void SetFullScreen(bool state); - void EndEmulation(); - void SetMenuVisible(bool state); - void UpdateNFCMenu(); - bool IsMenuHidden() const; - void TogglePadView(); - void OpenPPCDebugger(); - -#if BOOST_OS_WINDOWS - WXLRESULT MSWWindowProc(WXUINT nMsg, WXWPARAM wParam, WXLPARAM lParam) override; -#endif - void OpenSettings(); - - PadViewFrame* GetPadView() const { return m_padView; } - - void OnSizeEvent(wxSizeEvent& event); - void OnDPIChangedEvent(wxDPIChangedEvent& event); - void OnMove(wxMoveEvent& event); - - void OnDebuggerClose(wxCloseEvent& event); - void OnPadClose(wxCloseEvent& event); - void OnMemorySearcherClose(wxCloseEvent& event); - void OnMouseWheel(wxMouseEvent& event); - void OnClose(wxCloseEvent& event); - void OnFileMenu(wxCommandEvent& event); - void OnOpenFolder(wxCommandEvent& event); - void OnClearSpotPassCache(wxCommandEvent& event); - void OnLaunchFromFile(wxLaunchGameEvent& event); - void OnInstallUpdate(wxCommandEvent& event); - void OnFileExit(wxCommandEvent& event); - void OnNFCMenu(wxCommandEvent& event); - void OnOptionsInput(wxCommandEvent& event); - void OnAccountSelect(wxCommandEvent& event); - void OnConsoleLanguage(wxCommandEvent& event); - void OnHelpAbout(wxCommandEvent& event); - void OnHelpUpdate(wxCommandEvent& event); - void OnDebugSetting(wxCommandEvent& event); - void OnDebugLoggingToggleFlagGeneric(wxCommandEvent& event); - void OnPPCInfoToggle(wxCommandEvent& event); - void OnDebugDumpGeneric(wxCommandEvent& event); - void OnLoggingWindow(wxCommandEvent& event); - void OnGDBStubToggle(wxCommandEvent& event); - void OnDebugViewPPCThreads(wxCommandEvent& event); - void OnDebugViewPPCDebugger(wxCommandEvent& event); - void OnDebugViewAudioDebugger(wxCommandEvent& event); - void OnDebugViewTextureRelations(wxCommandEvent& event); - void OnMouseMove(wxMouseEvent& event); - void OnMouseLeft(wxMouseEvent& event); - void OnMouseRight(wxMouseEvent& event); - void OnGameListBeginUpdate(wxCommandEvent& event); - void OnGameListEndUpdate(wxCommandEvent& event); - void OnAccountListRefresh(wxCommandEvent& event); - void OnRequestGameListRefresh(wxCommandEvent& event); - void OnSetWindowTitle(wxCommandEvent& event); - - void OnKeyUp(wxKeyEvent& event); - void OnKeyDown(wxKeyEvent& event); - void OnChar(wxKeyEvent& event); - - void OnToolsInput(wxCommandEvent& event); - void OnGesturePan(wxPanGestureEvent& event); - - void OnGameLoaded(); - - void AsyncSetTitle(std::string_view windowTitle); - - void CreateCanvas(); - void DestroyCanvas(); - - static void ShowCursor(bool state); - - uintptr_t GetRenderCanvasHWND(); - - static void RequestGameListRefresh(); - static void RequestLaunchGame(fs::path filePath, wxLaunchGameEvent::INITIATED_BY initiatedBy); - -private: - bool FullscreenEnabled() const; - void RecreateMenu(); - void UpdateChildWindowTitleRunningState(); - static wxString GetInitialWindowTitle(); - - bool InstallUpdate(const fs::path& metaFilePath); - - void OnTimer(wxTimerEvent& event); - - // CafeSystem implementation - void CafeRecreateCanvas() override; - void CafePPCProcessExit() override; - - void OnRequestRestart(wxCommandEvent& event); - void OnRequestRecreateCanvas(wxCommandEvent& event); - void OnRequestGameExit(wxCommandEvent& event); - - wxRect GetDesktopRect(); - - MemorySearcherTool* m_toolWindow = nullptr; - TitleManager* m_title_manager = nullptr; - EmulatedUSBDeviceFrame* m_usb_devices = nullptr; - PadViewFrame* m_padView = nullptr; - GraphicPacksWindow2* m_graphic_pack_window = nullptr; - - wxTimer* m_timer; - wxPoint m_mouse_position{}; - std::chrono::steady_clock::time_point m_last_mouse_move_time; - wxSize m_restored_size; - wxPoint m_restored_position; - - bool m_menu_visible = false; - bool m_game_launched = false; - - #ifdef ENABLE_DISCORD_RPC - std::unique_ptr m_discord; - #endif - - std::string m_launched_game_name; - - wxMenuItem* m_gdbstub_toggle{}; - DebuggerWindow2* m_debugger_window = nullptr; - LoggingWindow* m_logging_window = nullptr; - - std::future m_update_available; - - wxMenuItem* m_check_update_menu{}; - void LoadSettings(); - void SaveSettings(); - - void OnGraphicWindowClose(wxCloseEvent& event); - void OnGraphicWindowOpen(wxTitleIdEvent& event); - - // panels - wxPanel* m_main_panel{}, * m_game_panel{}; - - // rendering - wxWindow* m_render_canvas{}; - - // gamelist - wxGameList* m_game_list{}; - wxInfoBar* m_info_bar{}; - - // menu - wxMenuBar* m_menuBar{}; - - // file - wxMenu* m_fileMenu{}; - wxMenuItem* m_fileMenuSeparator0{}; - wxMenuItem* m_fileMenuSeparator1{}; - wxMenuItem* m_loadMenuItem{}; - wxMenuItem* m_installUpdateMenuItem{}; - wxMenuItem* m_exitMenuItem{}; - - // options - wxMenu* m_optionsAccountMenu{}; - - wxMenuItem* m_fullscreenMenuItem{}; - wxMenuItem* m_padViewMenuItem{}; - - // tools - wxMenuItem* m_memorySearcherMenuItem{}; - - // cpu - wxMenu* m_cpuTimerSubmenu{}; - - // nfc - wxMenu* m_nfcMenu{}; - wxMenuItem* m_nfcMenuSeparator0{}; - - // debug - wxMenu* m_debugMenu{}; - wxMenu* m_loggingSubmenu{}; - wxMenuItem* m_asyncCompile{}; - -wxDECLARE_EVENT_TABLE(); -}; - -extern MainWindow* g_mainFrame; diff --git a/src/gui/wxgui/MemorySearcherTool.cpp b/src/gui/wxgui/MemorySearcherTool.cpp deleted file mode 100644 index 5cdfacc4..00000000 --- a/src/gui/wxgui/MemorySearcherTool.cpp +++ /dev/null @@ -1,742 +0,0 @@ -#include "wxgui/wxgui.h" - -#include "wxgui/MemorySearcherTool.h" - -#include -#include -#include -#include - -#include "config/ActiveSettings.h" -#include "wxgui/helpers/wxHelpers.h" -#include "Common/FileStream.h" -#include "util/IniParser/IniParser.h" -#include "util/helpers/StringHelpers.h" -#include "Cafe/CafeSystem.h" - -enum -{ - COMBOBOX_DATATYPE = wxID_HIGHEST + 1, - TEXT_VALUE, - BUTTON_START, - BUTTON_FILTER, - LIST_RESULTS, - LIST_ENTRYTABLE, - TIMER_REFRESH, - LIST_ENTRY_ADD, - LIST_ENTRY_REMOVE, -}; - -wxDEFINE_EVENT(wxEVT_SEARCH_FINISHED, wxCommandEvent); - -wxBEGIN_EVENT_TABLE(MemorySearcherTool, wxFrame) -EVT_CLOSE(MemorySearcherTool::OnClose) -EVT_BUTTON(BUTTON_START, MemorySearcherTool::OnSearch) -EVT_BUTTON(BUTTON_FILTER, MemorySearcherTool::OnFilter) -EVT_TIMER(TIMER_REFRESH, MemorySearcherTool::OnTimerTick) -wxEND_EVENT_TABLE() - -constexpr auto kMaxResultCount = 5000; - -const wxString kDatatypeFloat = "float"; -const wxString kDatatypeDouble = "double"; -const wxString kDatatypeString = "string"; -const wxString kDatatypeInt8 = "int8"; -const wxString kDatatypeInt16 = "int16"; -const wxString kDatatypeInt32 = "int32"; -const wxString kDatatypeInt64 = "int64"; -const wxString kDataTypeNames[] = {kDatatypeFloat,kDatatypeDouble,/*DATATYPE_STRING,*/kDatatypeInt8,kDatatypeInt16,kDatatypeInt32,kDatatypeInt64}; - -MemorySearcherTool::MemorySearcherTool(wxFrame* parent) - : wxFrame(parent, wxID_ANY, _("Memory Searcher"), wxDefaultPosition, wxSize(600, 540), wxDEFAULT_FRAME_STYLE | wxTAB_TRAVERSAL) -{ - this->SetSizeHints(wxDefaultSize, wxDefaultSize); - this->wxTopLevelWindowBase::SetMinSize(wxSize(600, 540)); - - auto* sizer = new wxBoxSizer(wxVERTICAL); - - auto* row1 = new wxFlexGridSizer(0, 4, 0, 0); - row1->AddGrowableCol(1); - - m_cbDataType = new wxComboBox(this, COMBOBOX_DATATYPE, kDatatypeFloat, wxDefaultPosition, wxDefaultSize, std::size(kDataTypeNames), kDataTypeNames, wxCB_READONLY); - m_textValue = new wxTextCtrl(this, TEXT_VALUE); - m_buttonStart = new wxButton(this, BUTTON_START, _("Search")); - m_buttonFilter = new wxButton(this, BUTTON_FILTER, _("Filter")); - m_buttonFilter->Disable(); - - row1->Add(m_cbDataType, 0, wxALL, 5); - row1->Add(m_textValue, 0, wxALL | wxEXPAND, 5); - row1->Add(m_buttonStart, 0, wxALL, 5); - row1->Add(m_buttonFilter, 0, wxALL, 5); - - sizer->Add(row1, 0, wxEXPAND, 5); - - auto* row2 = new wxFlexGridSizer(0, 1, 0, 0); - row2->AddGrowableCol(0); - - m_gauge = new wxGauge(this, wxID_ANY, 100, wxDefaultPosition, wxDefaultSize, wxGA_HORIZONTAL); - m_gauge->SetValue(0); - m_gauge->Enable(false); - - m_textEntryTable = new wxStaticText(this, wxID_ANY, _("Results")); - m_listResults = new wxListView(this, LIST_RESULTS, wxDefaultPosition, wxDefaultSize, wxLC_REPORT | wxLC_SORT_ASCENDING); - m_listResults->Bind(wxEVT_LEFT_DCLICK, &MemorySearcherTool::OnResultListClick, this); - { - wxListItem col0; - col0.SetId(0); - col0.SetText(_("Address")); - col0.SetWidth(100); - m_listResults->InsertColumn(0, col0); - wxListItem col1; - col1.SetId(1); - col1.SetText(_("Value")); - col1.SetWidth(250); - m_listResults->InsertColumn(1, col1); - } - - auto textEntryTable = new wxStaticText(this, wxID_ANY, _("Stored Entries")); - m_listEntryTable = new wxDataViewListCtrl(this, LIST_ENTRYTABLE, wxDefaultPosition, wxSize(420, 200), wxDV_HORIZ_RULES); - m_listEntryTable->Bind(wxEVT_DATAVIEW_ITEM_CONTEXT_MENU, &MemorySearcherTool::OnEntryListRightClick, this); - m_listEntryTable->Bind(wxEVT_COMMAND_DATAVIEW_ITEM_EDITING_DONE, &MemorySearcherTool::OnItemEdited, this); - { - m_listEntryTable->AppendTextColumn(_("Description"), wxDATAVIEW_CELL_EDITABLE, 150, wxALIGN_LEFT, wxDATAVIEW_COL_SORTABLE); - m_listEntryTable->AppendTextColumn(_("Address"), wxDATAVIEW_CELL_INERT, 100, wxALIGN_LEFT, wxDATAVIEW_COL_SORTABLE); - m_listEntryTable->AppendTextColumn(_("Type")); - m_listEntryTable->AppendTextColumn(_("Value"), wxDATAVIEW_CELL_EDITABLE); - m_listEntryTable->AppendToggleColumn(_("Freeze"), wxDATAVIEW_CELL_ACTIVATABLE, 50, wxALIGN_LEFT, 0); - } - - row2->AddGrowableRow(3); - row2->AddGrowableRow(5); - - row2->Add(m_gauge, 0, wxALL | wxEXPAND, 5); - row2->Add(0, 10, 1, wxEXPAND, 5); - row2->Add(m_textEntryTable, 0, wxALL, 5); - row2->Add(m_listResults, 1, wxALL | wxEXPAND, 5); - row2->Add(textEntryTable, 0, wxALL, 5); - row2->Add(m_listEntryTable, 1, wxALL | wxEXPAND, 5); - - sizer->Add(row2, 1, wxEXPAND, 5); - - // load stored entries - Load(); - - this->Bind(wxEVT_SEARCH_FINISHED, &MemorySearcherTool::OnSearchFinished, this); - this->Bind(wxEVT_SET_GAUGE_VALUE, &MemorySearcherTool::OnUpdateGauge, this); - - m_refresh_timer = new wxTimer(this, TIMER_REFRESH); - m_refresh_timer->Start(250); - - this->SetSizer(sizer); - this->wxWindowBase::Layout(); - - this->Centre(wxBOTH); -} - -MemorySearcherTool::~MemorySearcherTool() -{ - m_refresh_timer->Stop(); - - m_running = false; - if (m_worker.joinable()) - m_worker.join(); -} - -void MemorySearcherTool::OnTimerTick(wxTimerEvent& event) -{ - RefreshResultList(); - RefreshStashList(); -} - -void MemorySearcherTool::OnClose(wxCloseEvent& event) -{ - Save(); - event.Skip(); -} - -void MemorySearcherTool::OnSearchFinished(wxCommandEvent&) -{ - FillResultList(); - m_search_running = false; - m_buttonStart->Enable(); - m_buttonFilter->Enable(); -} - -void MemorySearcherTool::OnUpdateGauge(wxSetGaugeValue& event) -{ - auto* gauge = event.GetGauge(); - const auto value = event.GetValue(); - if (event.GetRange() != 0) - { - gauge->SetRange(event.GetRange()); - gauge->SetValue(value); - return; - } - - debug_printf("update gauge: %d + %d = %d (/%d)\n", gauge->GetValue(), value, gauge->GetValue() + value, gauge->GetRange()); - gauge->SetValue(gauge->GetValue() + value); -} - -void MemorySearcherTool::OnSearch(wxCommandEvent&) -{ - if (m_clear_state) - { - Reset(); - return; - } - - if (m_search_running) - return; - - if (m_textValue->IsEmpty()) - return; - - SetSearchDataType(); - - if (!VerifySearchValue()) - { - wxMessageBox(_("Your entered value is not valid for the selected datatype."), _("Error"), wxICON_ERROR); - return; - } - m_buttonStart->Disable(); - m_buttonFilter->Disable(); - m_cbDataType->Disable(); - m_buttonStart->SetLabelText(_("Clear")); - m_clear_state = true; - - if (m_worker.joinable()) - m_worker.join(); - - m_worker = std::thread([this]() - { - m_search_jobs.clear(); - uint32 total_size = 0; - for (const auto& itr : memory_getMMURanges()) - { - if (!m_running) - return; - - if (!itr->isMapped()) - continue; - - void* ptr = itr->getPtr(); - const uint32 size = itr->getSize(); - - total_size += (size / kGaugeStep); - m_search_jobs.emplace_back(std::async(std::launch::async, [this, ptr, size]() { return SearchValues(m_searchDataType, ptr, size); })); - } - - wxQueueEvent(this, new wxSetGaugeValue(0, total_size, m_gauge)); - - ListType_t tmp; - for (auto& it : m_search_jobs) - { - const auto result = it.get(); - tmp.insert(tmp.end(), result.cbegin(), result.cend()); - } - - std::unique_lock lock(m_mutex); - m_searchBuffer.swap(tmp); - lock.unlock(); - - wxQueueEvent(this, new wxCommandEvent(wxEVT_SEARCH_FINISHED)); - }); -} - -void MemorySearcherTool::OnFilter(wxCommandEvent& event) -{ - m_buttonStart->Disable(); - m_buttonFilter->Disable(); - - if (m_worker.joinable()) - m_worker.join(); - - m_worker = std::thread([this]() - { - const auto count = (uint32)m_searchBuffer.size(); - wxQueueEvent(this, new wxSetGaugeValue(0, count, m_gauge)); - - auto tmp = FilterValues(m_searchDataType); - - std::unique_lock lock(m_mutex); - m_searchBuffer.swap(tmp); - lock.unlock(); - - wxQueueEvent(this, new wxCommandEvent(wxEVT_SEARCH_FINISHED)); - }); - - m_gauge->SetValue(0); -} - -void MemorySearcherTool::Load() -{ - const auto memorySearcherPath = ActiveSettings::GetUserDataPath("memorySearcher/{:016x}.ini", CafeSystem::GetForegroundTitleId()); - auto memSearcherIniContents = FileStream::LoadIntoMemory(memorySearcherPath); - if (!memSearcherIniContents) - return; - - IniParser iniParser(*memSearcherIniContents, _pathToUtf8(memorySearcherPath)); - while (iniParser.NextSection()) - { - auto option_description = iniParser.FindOption("description"); - auto option_address = iniParser.FindOption("address"); - auto option_type = iniParser.FindOption("type"); - auto option_value = iniParser.FindOption("value"); - - if (!option_description || !option_address || !option_type || !option_value) - continue; - - try - { - const auto addr = StringHelpers::ToInt64(*option_address); - if (!IsAddressValid(addr)) - continue; - } - catch (const std::invalid_argument&) - { - continue; - } - - bool found = false; - for (const auto& entry : kDataTypeNames) - { - if (boost::iequals(entry.ToStdString(), *option_type)) - { - found = true; - break; - } - } - - if (!found && !boost::iequals(kDatatypeString.ToStdString(), *option_type)) - continue; - - wxVector data; - data.push_back(std::string(*option_description).c_str()); - data.push_back(std::string(*option_address).c_str()); - data.push_back(std::string(*option_type).c_str()); - data.push_back(std::string(*option_value).c_str()); - data.push_back(!option_value->empty()); - m_listEntryTable->AppendItem(data); - } -} - -void MemorySearcherTool::Save() -{ - const auto memorySearcherPath = ActiveSettings::GetUserDataPath("memorySearcher/{:016x}.ini", CafeSystem::GetForegroundTitleId()); - FileStream* fs = FileStream::createFile2(memorySearcherPath); - if (fs) - { - for (int i = 0; i < m_listEntryTable->GetItemCount(); ++i) - { - fs->writeLine("[Entry]"); - - std::string tmp = "description=" + m_listEntryTable->GetTextValue(i, 0).ToStdString(); - fs->writeLine(tmp.c_str()); - - tmp = "address=" + m_listEntryTable->GetTextValue(i, 1).ToStdString(); - fs->writeLine(tmp.c_str()); - - tmp = "type=" + m_listEntryTable->GetTextValue(i, 2).ToStdString(); - fs->writeLine(tmp.c_str()); - - // only save value when FREEZE is active - if (m_listEntryTable->GetToggleValue(i, 4)) - { - tmp = "value=" + m_listEntryTable->GetTextValue(i, 3).ToStdString(); - } - else - { - tmp = "value="; - } - fs->writeLine(tmp.c_str()); - - fs->writeLine(""); - } - delete fs; - } -} - -bool MemorySearcherTool::IsAddressValid(uint32 addr) -{ - for (const auto& itr : memory_getMMURanges()) - { - if (!itr->isMapped()) - continue; - - void* ptr = itr->getPtr(); - const uint32 size = itr->getSize(); - - MEMPTR start((uint8*)ptr); - MEMPTR end((uint8*)ptr + size); - if (start.GetMPTR() <= addr && addr < end.GetMPTR()) - return true; - } - - return false; -} - -void MemorySearcherTool::OnEntryListRightClick(wxDataViewEvent& event) -{ - //void *data = reinterpret_cast(event.GetItem().GetData()); - wxMenu mnu; - //mnu.SetClientData(data); - mnu.Append(LIST_ENTRY_ADD, _("&Add new entry"))->Enable(false); - mnu.Append(LIST_ENTRY_REMOVE, _("&Remove entry")); - mnu.Bind(wxEVT_COMMAND_MENU_SELECTED, &MemorySearcherTool::OnPopupClick, this); - PopupMenu(&mnu); -} - -void MemorySearcherTool::OnResultListClick(wxMouseEvent& event) -{ - for (long selectedIndex = m_listResults->GetFirstSelected(); selectedIndex != wxNOT_FOUND; selectedIndex = m_listResults->GetNextSelected(selectedIndex)) - { - long address = m_listResults->GetItemData(selectedIndex); - auto currValue = m_listResults->GetItemText(selectedIndex, 1); - - wxString addressString = wxString::Format("0x%08lx", address); - - // description, address, type, value, freeze - wxVector data; - data.push_back(""); - data.push_back(addressString); - data.push_back(m_cbDataType->GetValue()); - data.push_back(currValue); - data.push_back(false); - m_listEntryTable->AppendItem(data); - } -} - -void MemorySearcherTool::Reset() -{ - m_searchBuffer.clear(); - m_buttonStart->SetLabelText(_("Search")); - m_textEntryTable->SetLabelText(_("Results")); - m_buttonFilter->Disable(); - m_cbDataType->Enable(); - m_textValue->SetValue(""); - m_listResults->DeleteAllItems(); - m_clear_state = false; -} - -bool MemorySearcherTool::VerifySearchValue() const -{ - const auto inputString = m_textValue->GetValue().ToStdString(); - - switch (m_searchDataType) - { - case SearchDataType_String: - return true; - case SearchDataType_Float: - { - float value; - return ConvertStringToType(inputString, value); - } - case SearchDataType_Double: - { - double value; - return ConvertStringToType(inputString, value); - } - case SearchDataType_Int8: - { - sint8 value; - return ConvertStringToType(inputString, value); - } - case SearchDataType_Int16: - { - sint16 value; - return ConvertStringToType(inputString, value); - } - case SearchDataType_Int32: - { - sint32 value; - return ConvertStringToType(inputString, value); - } - case SearchDataType_Int64: - { - sint64 value; - return ConvertStringToType(inputString, value); - } - default: - return false; - } -} - -void MemorySearcherTool::FillResultList() -{ - auto text = formatWxString(_("Results ({0})"), m_searchBuffer.size()); - m_textEntryTable->SetLabelText(text); - - m_listResults->DeleteAllItems(); - - if (m_searchBuffer.empty() || m_searchBuffer.size() > kMaxResultCount) - return; - - for (const auto& address : m_searchBuffer) - { - const auto index = m_listResults->InsertItem(0, fmt::format("{:#08x}", address.GetMPTR())); - m_listResults->SetItemData(index, address.GetMPTR()); - m_listResults->SetItem(index, 1, m_textValue->GetValue()); - } -} - -void MemorySearcherTool::RefreshResultList() -{ - std::unique_lock lock(m_mutex); - if (m_searchBuffer.empty() || m_searchBuffer.size() > kMaxResultCount) - return; - - for (int i = 0; i < m_listResults->GetItemCount(); ++i) - { - const auto addr = m_listResults->GetItemData(i); - switch (m_searchDataType) - { - case SearchDataType_String: - { - // TODO Peter - break; - } - case SearchDataType_Float: - { - const auto value = memory_read(addr); - m_listResults->SetItem(i, 1, fmt::format("{}", value)); - break; - } - case SearchDataType_Double: - { - const auto value = memory_read(addr); - m_listResults->SetItem(i, 1, fmt::format("{}", value)); - break; - } - case SearchDataType_Int8: - { - const auto value = memory_read(addr); - m_listResults->SetItem(i, 1, fmt::format("{}", value)); - break; - } - case SearchDataType_Int16: - { - const auto value = memory_read(addr); - m_listResults->SetItem(i, 1, fmt::format("{}", value)); - break; - } - case SearchDataType_Int32: - { - const auto value = memory_read(addr); - m_listResults->SetItem(i, 1, fmt::format("{}", value)); - break; - } - case SearchDataType_Int64: - { - const auto value = memory_read(addr); - m_listResults->SetItem(i, 1, fmt::format("{}", value)); - break; - } - } - } -} - -void MemorySearcherTool::RefreshStashList() -{ - for (int i = 0; i < m_listEntryTable->GetItemCount(); ++i) - { - auto freeze = m_listEntryTable->GetToggleValue(i, 4); - - auto addressText = m_listEntryTable->GetTextValue(i, 1).ToStdString(); - auto type = m_listEntryTable->GetTextValue(i, 2); - - auto address = std::stol(addressText, nullptr, 16); - - // if freeze is activated, set the value instead of refreshing it - if (freeze) - { - wxVariant var; - m_listEntryTable->GetValue(var, i, 3); - if (type == kDatatypeFloat) - { - auto value = (float)var.GetDouble(); - memory_writeFloat(address, value); - } - else if (type == kDatatypeDouble) - { - auto value = var.GetDouble(); - memory_writeDouble(address, value); - } - else if (type == kDatatypeInt8) - { - auto value = var.GetInteger(); - memory_writeU8(address, value); - } - else if (type == kDatatypeInt16) - { - auto value = var.GetInteger(); - memory_writeU16(address, value); - } - else if (type == kDatatypeInt32) - { - auto value = var.GetInteger(); - memory_writeU32(address, value); - } - else if (type == kDatatypeInt64) - { - auto valueText = var.GetString().ToStdString(); - auto value = std::stoull(valueText); - memory_writeU64(address, value); - } - else if (type == kDatatypeString) - { - auto valueText = var.GetString().ToStdString(); - for (int i = 0; i < valueText.size(); ++i) - { - memory_writeU8(address + i, valueText[i]); - } - memory_writeU8(address + valueText.size(), 0x00); - } - } - else - { - if (type == kDatatypeFloat) - { - auto value = memory_readFloat(address); - m_listEntryTable->SetValue(fmt::format("{}", value), i, 3); - } - else if (type == kDatatypeDouble) - { - auto value = memory_readDouble(address); - m_listEntryTable->SetValue(fmt::format("{}", value), i, 3); - } - else if (type == kDatatypeInt8) - { - auto value = (sint8)memory_readU8(address); - m_listEntryTable->SetValue(fmt::format("{}", (int)value), i, 3); - } - else if (type == kDatatypeInt16) - { - auto value = (sint16)memory_readU16(address); - m_listEntryTable->SetValue(fmt::format("{}", value), i, 3); - } - else if (type == kDatatypeInt32) - { - auto value = (sint32)memory_readU32(address); - m_listEntryTable->SetValue(fmt::format("{}", value), i, 3); - } - else if (type == kDatatypeInt64) - { - auto value = (sint64)memory_readU64(address); - m_listEntryTable->SetValue(fmt::format("{}", value), i, 3); - } - else if (type == kDatatypeString) - { - // TODO Peter - } - } - } -} - -void MemorySearcherTool::SetSearchDataType() -{ - const auto type = m_cbDataType->GetStringSelection(); - if (type == kDatatypeFloat) - m_searchDataType = SearchDataType_Float; - else if (type == kDatatypeDouble) - m_searchDataType = SearchDataType_Double; - else if (type == kDatatypeInt8) - m_searchDataType = SearchDataType_Int8; - else if (type == kDatatypeInt16) - m_searchDataType = SearchDataType_Int16; - else if (type == kDatatypeInt32) - m_searchDataType = SearchDataType_Int32; - else if (type == kDatatypeInt64) - m_searchDataType = SearchDataType_Int64; - else if (type == kDatatypeString) - m_searchDataType = SearchDataType_String; - else - m_searchDataType = SearchDataType_None; -} - -template<> -bool MemorySearcherTool::ConvertStringToType(const std::string& inValue, sint8& outValue) const -{ - sint16 tmp; - std::istringstream iss(inValue); - iss >> std::noskipws >> tmp; - if (iss && iss.eof()) - { - if (SCHAR_MIN <= tmp && tmp <= SCHAR_MAX) - { - outValue = tmp; - return true; - } - } - return false; -} - -void MemorySearcherTool::OnPopupClick(wxCommandEvent& event) -{ - if (event.GetId() == LIST_ENTRY_REMOVE) - { - const int row = m_listEntryTable->GetSelectedRow(); - if (row == wxNOT_FOUND) - return; - - m_listEntryTable->DeleteItem(row); - } -} - -void MemorySearcherTool::OnItemEdited(wxDataViewEvent& event) -{ - auto column = event.GetColumn(); - // Edit description - if (column == 0) { } - // Edit value - else if (column == 3) - { - auto row = m_listEntryTable->GetSelectedRow(); - if (row == wxNOT_FOUND) - return; - - auto addressText = m_listEntryTable->GetTextValue(row, 1).ToStdString(); - uint32 address = std::stoul(addressText, nullptr, 16); - - auto type = m_listEntryTable->GetTextValue(row, 2); - if (type == kDatatypeFloat) - { - auto value = (float)event.GetValue().GetDouble(); - memory_writeFloat(address, value); - } - else if (type == kDatatypeDouble) - { - auto value = event.GetValue().GetDouble(); - memory_writeDouble(address, value); - } - else if (type == kDatatypeInt8) - { - auto value = event.GetValue().GetInteger(); - memory_writeU8(address, value); - } - else if (type == kDatatypeInt16) - { - auto value = event.GetValue().GetInteger(); - memory_writeU16(address, value); - } - else if (type == kDatatypeInt32) - { - auto value = event.GetValue().GetInteger(); - memory_writeU32(address, value); - } - else if (type == kDatatypeInt64) - { - auto valueText = event.GetValue().GetString().ToStdString(); - auto value = std::stoull(valueText); - memory_writeU64(address, value); - } - else if (type == kDatatypeString) - { - auto valueText = event.GetValue().GetString().ToStdString(); - for (int i = 0; i < valueText.size(); ++i) - { - memory_writeU8(address + i, valueText[i]); - } - memory_writeU8(address + valueText.size(), 0x00); - } - } -} diff --git a/src/gui/wxgui/MemorySearcherTool.h b/src/gui/wxgui/MemorySearcherTool.h deleted file mode 100644 index cb669d1c..00000000 --- a/src/gui/wxgui/MemorySearcherTool.h +++ /dev/null @@ -1,264 +0,0 @@ -#pragma once - -#include -#include -#include -#include - -#include -#include - -#include "Cafe/HW/MMU/MMU.h" -#include "util/helpers/helpers.h" -#include "wxgui/helpers/wxCustomEvents.h" - -class wxComboBox; -class wxDataViewEvent; -class wxDataViewListCtrl; -class wxStaticText; -class wxTimer; -class wxTimerEvent; - -enum SearchDataType -{ - SearchDataType_None, - SearchDataType_String, - SearchDataType_Float, - SearchDataType_Double, - SearchDataType_Int8, - SearchDataType_Int16, - SearchDataType_Int32, - SearchDataType_Int64, -}; - -struct TableEntry_t -{ - uint32 address; - const char description[32]; - SearchDataType type; - bool freeze; - uint16 dataLen; - uint8* data; -}; - -class MemorySearcherTool : public wxFrame -{ -public: - MemorySearcherTool(wxFrame* parent); - virtual ~MemorySearcherTool(); - - void OnTimerTick(wxTimerEvent& event); - void OnClose(wxCloseEvent&); - void OnSearch(wxCommandEvent&); - void OnSearchFinished(wxCommandEvent& event); - void OnUpdateGauge(wxSetGaugeValue& event); - void OnFilter(wxCommandEvent& event); - void OnResultListClick(wxMouseEvent& event); - void OnEntryListRightClick(wxDataViewEvent& event); - //void OnEntryListRightClick2(wxContextMenuEvent& event); - void OnPopupClick(wxCommandEvent& event); - - void OnItemEdited(wxDataViewEvent& event); - -private: - void Reset(); - bool VerifySearchValue() const; - void FillResultList(); - void RefreshResultList(); - void RefreshStashList(); - void SetSearchDataType(); - - void Load(); - void Save(); - - static bool IsAddressValid(uint32 addr); - - template - bool ConvertStringToType(const std::string& inValue, T& outValue) const - { - std::istringstream iss(inValue); - iss >> std::noskipws >> outValue; - return iss && iss.eof(); - } - - using ListType_t = std::vector>; - ListType_t SearchValues(SearchDataType type, void* ptr, uint32 size) - { - /*if (type == SearchDataType_String) - return SearchValues((const char* )ptr, size); - else */if (type == SearchDataType_Float) - return SearchValues((float*)ptr, size); - else if (type == SearchDataType_Double) - return SearchValues((double*)ptr, size); - else if (type == SearchDataType_Int8) - return SearchValues((sint8*)ptr, size); - else if (type == SearchDataType_Int16) - return SearchValues((sint16*)ptr, size); - else if (type == SearchDataType_Int32) - return SearchValues((sint32*)ptr, size); - else if (type == SearchDataType_Int64) - return SearchValues((sint64*)ptr, size); - return {}; - } - - ListType_t FilterValues(SearchDataType type) - { - /*if (type == SearchDataType_String) - return FilterValues(); - else */if (type == SearchDataType_Float) - return FilterValues(); - else if (type == SearchDataType_Double) - return FilterValues(); - else if (type == SearchDataType_Int8) - return FilterValues(); - else if (type == SearchDataType_Int16) - return FilterValues(); - else if (type == SearchDataType_Int32) - return FilterValues(); - else if (type == SearchDataType_Int64) - return FilterValues(); - return {}; - } - - constexpr static int kGaugeStep = 0x10000; - - template - ListType_t SearchValues(T* ptr, uint32 size) - { - const auto search_value = ConvertString(m_textValue->GetValue().ToStdString()); - - const auto* end = (T*)((uint8*)ptr + size - sizeof(T)); - - uint32 counter = 0; - std::vector> result; - for (; ptr < end; ++ptr) - { - if (!m_running) - return result; - - const auto tmp = (betype*)ptr; - if (equals(search_value, tmp->value())) - result.emplace_back(ptr); - - counter += sizeof(T); - if(counter >= kGaugeStep) - { - wxQueueEvent(this, new wxSetGaugeValue(1, m_gauge)); - counter -= kGaugeStep; - } - } - - return result; - } - - template - ListType_t FilterValues() - { - const auto search_value = ConvertString(m_textValue->GetValue().ToStdString()); - - ListType_t newSearchBuffer; - newSearchBuffer.reserve(m_searchBuffer.size()); - - for (const auto& it : m_searchBuffer) - { - if (!m_running) - return newSearchBuffer; - - const auto tmp = (betype*)it.GetPtr(); - if (equals(search_value , tmp->value())) - newSearchBuffer.emplace_back(it); - - wxQueueEvent(this, new wxSetGaugeValue(1, m_gauge)); - } - - return newSearchBuffer; - } - -wxDECLARE_EVENT_TABLE(); - - SearchDataType m_searchDataType = SearchDataType_None; - wxComboBox* m_cbDataType; - wxTextCtrl* m_textValue; - wxButton *m_buttonStart, *m_buttonFilter; - wxListView* m_listResults; - wxDataViewListCtrl* m_listEntryTable; - wxStaticText* m_textEntryTable; - wxGauge* m_gauge; - wxTimer* m_refresh_timer; - - ListType_t m_searchBuffer; - std::vector m_tableEntries; - - std::mutex m_mutex; - std::thread m_worker; - - std::atomic_bool m_running = true; - std::atomic_bool m_search_running = false; - std::vector> m_search_jobs; - - bool m_clear_state = false; -}; - -template<> -bool MemorySearcherTool::ConvertStringToType(const std::string& inValue, sint8& outValue) const; - -// -//template -//void MemorySearcherTool::FilterValues() -//{ -// auto s1 = m_textValue->GetValue(); -// auto s2 = s1.c_str(); -// auto stringValue = s2.AsChar(); -// -// T filterValue; -// ConvertStringToType(stringValue, filterValue); -// -// std::vector newSearchBuffer; -// newSearchBuffer.reserve(m_searchBuffer.size()); -// -// uint32 gaugeValue = 0; -// uint32 count = m_searchBuffer.size(); -// uint32 counter = 0; -// -// for (auto it = m_searchBuffer.begin(); it != m_searchBuffer.end(); ++it) -// { -// if (m_running) -// return; -// -// T value = memory_read(*it); -// if constexpr (std::is_same::value) -// { -// //float value1 = filterValue * 0.95f; -// float diff = filterValue - value; -// diff = fabs(diff); -// if(diff < value*0.05f) -// { -// newSearchBuffer.push_back(*it); -// } -// -// } -// else -// { -// if (value == filterValue) -// { -// newSearchBuffer.push_back(*it); -// } -// } -// -// ++counter; -// uint32 newValue = counter * 100 / count; -// if (newValue > gaugeValue + GAUGE_STEP_SIZE) -// { -// gaugeValue = newValue; -// m_gaugeValue = newValue; -// } -// } -// -// m_mutex.lock(); -// m_searchBuffer = newSearchBuffer; -// m_gaugeValue = 100; -// m_mutex.unlock(); -// m_isSearchFinished = true; -//} -// - diff --git a/src/gui/wxgui/PadViewFrame.cpp b/src/gui/wxgui/PadViewFrame.cpp deleted file mode 100644 index ca213849..00000000 --- a/src/gui/wxgui/PadViewFrame.cpp +++ /dev/null @@ -1,242 +0,0 @@ -#include "interface/WindowSystem.h" -#include "wxgui/wxgui.h" -#include "wxgui/PadViewFrame.h" - -#include - -#include "config/ActiveSettings.h" -#include "Cafe/OS/libs/swkbd/swkbd.h" -#ifdef ENABLE_OPENGL -#include "wxgui/canvas/OpenGLCanvas.h" -#endif -#ifdef ENABLE_VULKAN -#include "wxgui/canvas/VulkanCanvas.h" -#endif -#ifdef ENABLE_METAL -#include "wxgui/canvas/MetalCanvas.h" -#endif -#include "config/CemuConfig.h" -#include "wxgui/MainWindow.h" -#include "wxgui/helpers/wxHelpers.h" -#include "input/InputManager.h" - -#if BOOST_OS_LINUX || BOOST_OS_MACOS || BOOST_OS_BSD -#include "resource/embedded/resources.h" -#endif -#include "wxHelper.h" - -extern WindowSystem::WindowInfo g_window_info; - -#define PAD_MIN_WIDTH 320 -#define PAD_MIN_HEIGHT 180 - -PadViewFrame::PadViewFrame(wxFrame* parent) - : wxFrame(nullptr, wxID_ANY, _("GamePad View"), wxDefaultPosition, wxDefaultSize, wxMINIMIZE_BOX | wxMAXIMIZE_BOX | wxSYSTEM_MENU | wxCAPTION | wxCLIP_CHILDREN | wxRESIZE_BORDER | wxCLOSE_BOX | wxWANTS_CHARS) -{ - g_window_info.window_pad = initHandleContextFromWxWidgetsWindow(this); - - SetIcon(wxICON(M_WND_ICON128)); - wxWindow::EnableTouchEvents(wxTOUCH_PAN_GESTURES); - - SetMinClientSize({ PAD_MIN_WIDTH, PAD_MIN_HEIGHT }); - - SetPosition({ g_window_info.restored_pad_x, g_window_info.restored_pad_y }); - if (g_window_info.restored_pad_width >= PAD_MIN_WIDTH && g_window_info.restored_pad_height >= PAD_MIN_HEIGHT) - SetClientSize({ g_window_info.restored_pad_width, g_window_info.restored_pad_height }); - else - SetClientSize(wxSize(854, 480)); - - if (g_window_info.pad_maximized) - Maximize(); - - Bind(wxEVT_SIZE, &PadViewFrame::OnSizeEvent, this); - Bind(wxEVT_DPI_CHANGED, &PadViewFrame::OnDPIChangedEvent, this); - Bind(wxEVT_MOVE, &PadViewFrame::OnMoveEvent, this); - Bind(wxEVT_MOTION, &PadViewFrame::OnMouseMove, this); - - Bind(wxEVT_SET_WINDOW_TITLE, &PadViewFrame::OnSetWindowTitle, this); - - g_window_info.pad_open = true; -} - -PadViewFrame::~PadViewFrame() -{ - g_window_info.pad_open = false; -} - -bool PadViewFrame::Initialize() -{ - const wxSize client_size = GetClientSize(); - g_window_info.pad_width = client_size.GetWidth(); - g_window_info.pad_height = client_size.GetHeight(); - g_window_info.phys_pad_width = ToPhys(client_size.GetWidth()); - g_window_info.phys_pad_height = ToPhys(client_size.GetHeight()); - - return true; -} - -void PadViewFrame::InitializeRenderCanvas() -{ - auto sizer = new wxBoxSizer(wxVERTICAL); - { - #ifdef ENABLE_VULKAN - if (ActiveSettings::GetGraphicsAPI() == kVulkan) - m_render_canvas = new VulkanCanvas(this, wxSize(854, 480), false); - #endif - #ifdef ENABLE_OPENGL - if (ActiveSettings::GetGraphicsAPI() == kOpenGL) - m_render_canvas = GLCanvas_Create(this, wxSize(854, 480), false); - #endif - #ifdef ENABLE_METAL - if (ActiveSettings::GetGraphicsAPI() == kMetal) - m_render_canvas = new MetalCanvas(this, wxSize(854, 480), false); - #endif - sizer->Add(m_render_canvas, 1, wxEXPAND, 0, nullptr); - } - cemu_assert(m_render_canvas != nullptr); - SetSizer(sizer); - Layout(); - - m_render_canvas->Bind(wxEVT_KEY_UP, &PadViewFrame::OnKeyUp, this); - m_render_canvas->Bind(wxEVT_CHAR, &PadViewFrame::OnChar, this); - - m_render_canvas->Bind(wxEVT_MOTION, &PadViewFrame::OnMouseMove, this); - m_render_canvas->Bind(wxEVT_LEFT_DOWN, &PadViewFrame::OnMouseLeft, this); - m_render_canvas->Bind(wxEVT_LEFT_UP, &PadViewFrame::OnMouseLeft, this); - m_render_canvas->Bind(wxEVT_RIGHT_DOWN, &PadViewFrame::OnMouseRight, this); - m_render_canvas->Bind(wxEVT_RIGHT_UP, &PadViewFrame::OnMouseRight, this); - - m_render_canvas->Bind(wxEVT_GESTURE_PAN, &PadViewFrame::OnGesturePan, this); - - m_render_canvas->SetFocus(); - SendSizeEvent(); -} - -void PadViewFrame::DestroyCanvas() -{ - if(!m_render_canvas) - return; - m_render_canvas->Destroy(); - m_render_canvas = nullptr; -} - -void PadViewFrame::OnSizeEvent(wxSizeEvent& event) -{ - if (!IsMaximized() && !IsFullScreen()) - { - g_window_info.restored_pad_width = GetSize().x; - g_window_info.restored_pad_height = GetSize().y; - } - g_window_info.pad_maximized = IsMaximized() && !IsFullScreen(); - - const wxSize client_size = GetClientSize(); - g_window_info.pad_width = client_size.GetWidth(); - g_window_info.pad_height = client_size.GetHeight(); - g_window_info.phys_pad_width = ToPhys(client_size.GetWidth()); - g_window_info.phys_pad_height = ToPhys(client_size.GetHeight()); - g_window_info.pad_dpi_scale = GetDPIScaleFactor(); - - event.Skip(); -} - -void PadViewFrame::OnDPIChangedEvent(wxDPIChangedEvent& event) -{ - event.Skip(); - const wxSize client_size = GetClientSize(); - g_window_info.pad_width = client_size.GetWidth(); - g_window_info.pad_height = client_size.GetHeight(); - g_window_info.phys_pad_width = ToPhys(client_size.GetWidth()); - g_window_info.phys_pad_height = ToPhys(client_size.GetHeight()); - g_window_info.pad_dpi_scale = GetDPIScaleFactor(); -} - -void PadViewFrame::OnMoveEvent(wxMoveEvent& event) -{ - if (!IsMaximized() && !IsFullScreen()) - { - g_window_info.restored_pad_x = GetPosition().x; - g_window_info.restored_pad_y = GetPosition().y; - } -} - -void PadViewFrame::OnKeyUp(wxKeyEvent& event) -{ - event.Skip(); - - if (swkbd_hasKeyboardInputHook()) - return; - - const auto code = event.GetKeyCode(); - if (code == WXK_ESCAPE) - ShowFullScreen(false); - else if (code == WXK_RETURN && event.AltDown() || code == WXK_F11) - ShowFullScreen(!IsFullScreen()); -} - -void PadViewFrame::OnGesturePan(wxPanGestureEvent& event) -{ - auto& instance = InputManager::instance(); - - std::scoped_lock lock(instance.m_pad_touch.m_mutex); - auto physPos = ToPhys(event.GetPosition()); - instance.m_pad_touch.position = { physPos.x, physPos.y }; - instance.m_pad_touch.left_down = event.IsGestureStart() || !event.IsGestureEnd(); - if (event.IsGestureStart() || !event.IsGestureEnd()) - instance.m_pad_touch.left_down_toggle = true; -} - -void PadViewFrame::OnChar(wxKeyEvent& event) -{ - if (swkbd_hasKeyboardInputHook()) - swkbd_keyInput(event.GetUnicodeKey()); - - event.Skip(); -} - -void PadViewFrame::OnMouseMove(wxMouseEvent& event) -{ - auto& instance = InputManager::instance(); - - std::scoped_lock lock(instance.m_pad_touch.m_mutex); - auto physPos = ToPhys(event.GetPosition()); - instance.m_pad_mouse.position = { physPos.x, physPos.y }; - - event.Skip(); -} - -void PadViewFrame::OnMouseLeft(wxMouseEvent& event) -{ - auto& instance = InputManager::instance(); - - std::scoped_lock lock(instance.m_pad_mouse.m_mutex); - instance.m_pad_mouse.left_down = event.ButtonDown(wxMOUSE_BTN_LEFT); - auto physPos = ToPhys(event.GetPosition()); - instance.m_pad_mouse.position = { physPos.x, physPos.y }; - if (event.ButtonDown(wxMOUSE_BTN_LEFT)) - instance.m_pad_mouse.left_down_toggle = true; - -} - -void PadViewFrame::OnMouseRight(wxMouseEvent& event) -{ - auto& instance = InputManager::instance(); - - std::scoped_lock lock(instance.m_pad_mouse.m_mutex); - instance.m_pad_mouse.right_down = event.ButtonDown(wxMOUSE_BTN_LEFT); - auto physPos = ToPhys(event.GetPosition()); - instance.m_pad_mouse.position = { physPos.x, physPos.y }; - if (event.ButtonDown(wxMOUSE_BTN_RIGHT)) - instance.m_pad_mouse.right_down_toggle = true; -} - -void PadViewFrame::OnSetWindowTitle(wxCommandEvent& event) -{ - this->SetTitle(event.GetString()); -} - -void PadViewFrame::AsyncSetTitle(std::string_view windowTitle) -{ - wxCommandEvent set_title_event(wxEVT_SET_WINDOW_TITLE); - set_title_event.SetString(wxString::FromUTF8(windowTitle)); - QueueEvent(set_title_event.Clone()); -} diff --git a/src/gui/wxgui/PadViewFrame.h b/src/gui/wxgui/PadViewFrame.h deleted file mode 100644 index 7e343d7e..00000000 --- a/src/gui/wxgui/PadViewFrame.h +++ /dev/null @@ -1,38 +0,0 @@ -#pragma once - -#include - -#define WM_CREATE_PAD (WM_USER+1) -#define WM_DESTROY_PAD (WM_USER+2) - -wxDECLARE_EVENT(EVT_PAD_CLOSE, wxCommandEvent); -wxDECLARE_EVENT(EVT_SET_WINDOW_TITLE, wxCommandEvent); - -class PadViewFrame : public wxFrame -{ -public: - PadViewFrame(wxFrame* parent); - ~PadViewFrame(); - - bool Initialize(); - void InitializeRenderCanvas(); - void DestroyCanvas(); - - void OnKeyUp(wxKeyEvent& event); - void OnChar(wxKeyEvent& event); - - void AsyncSetTitle(std::string_view windowTitle); - -private: - - void OnMouseMove(wxMouseEvent& event); - void OnMouseLeft(wxMouseEvent& event); - void OnMouseRight(wxMouseEvent& event); - void OnSizeEvent(wxSizeEvent& event); - void OnDPIChangedEvent(wxDPIChangedEvent& event); - void OnMoveEvent(wxMoveEvent& event); - void OnGesturePan(wxPanGestureEvent& event); - void OnSetWindowTitle(wxCommandEvent& event); - - wxWindow* m_render_canvas = nullptr; -}; diff --git a/src/gui/wxgui/TitleManager.cpp b/src/gui/wxgui/TitleManager.cpp deleted file mode 100644 index a4e191c9..00000000 --- a/src/gui/wxgui/TitleManager.cpp +++ /dev/null @@ -1,907 +0,0 @@ -#include "wxgui/TitleManager.h" - -#include "wxgui/helpers/wxCustomEvents.h" -#include "wxgui/helpers/wxCustomData.h" -#include "Cafe/TitleList/GameInfo.h" -#include "util/helpers/helpers.h" -#include "wxgui/helpers/wxHelpers.h" -#include "wxgui/wxHelper.h" -#include "wxgui/components/wxTitleManagerList.h" -#include "wxgui/components/wxDownloadManagerList.h" -#include "wxgui/GameUpdateWindow.h" -#include "wxgui/dialogs/SaveImport/SaveTransfer.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#include "Cafe/IOSU/legacy/iosu_crypto.h" -#include "config/ActiveSettings.h" -#include "wxgui/dialogs/SaveImport/SaveImportWindow.h" -#include "Cafe/Account/Account.h" -#include "Cemu/Tools/DownloadManager/DownloadManager.h" -#include "wxgui/CemuApp.h" -#include "Cafe/TitleList/TitleList.h" -#include "Cafe/TitleList/SaveList.h" -#include "resource/embedded/resources.h" - -wxDEFINE_EVENT(wxEVT_TITLE_FOUND, wxCommandEvent); -wxDEFINE_EVENT(wxEVT_TITLE_SEARCH_COMPLETE, wxCommandEvent); -wxDEFINE_EVENT(wxEVT_DL_TITLE_UPDATE, wxCommandEvent); -wxDEFINE_EVENT(wxEVT_DL_DISCONNECT_COMPLETE, wxCommandEvent); - -wxPanel* TitleManager::CreateTitleManagerPage() -{ - auto* panel = new wxPanel(m_notebook); - auto* sizer = new wxBoxSizer(wxVERTICAL); - { - auto* row = new wxFlexGridSizer(0, 4, 0, 0); - row->AddGrowableCol(1); - - row->Add(new wxStaticText(panel, wxID_ANY, _("Filter")), 0, wxALIGN_CENTER_VERTICAL | wxALL, 5); - - m_filter = new wxTextCtrl(panel, wxID_ANY); - m_filter->Bind(wxEVT_TEXT, &TitleManager::OnFilterChanged, this); - row->Add(m_filter, 1, wxALL | wxEXPAND, 5); - - const wxImage refresh = wxHelper::LoadThemedBitmapFromPNG(PNG_REFRESH_png, sizeof(PNG_REFRESH_png), wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT)).ConvertToImage(); - m_refresh_button = new wxBitmapButton(panel, wxID_ANY, refresh.Scale(16, 16)); - m_refresh_button->Disable(); - m_refresh_button->Bind(wxEVT_BUTTON, &TitleManager::OnRefreshButton, this); - m_refresh_button->SetToolTip(_("Refresh")); - row->Add(m_refresh_button, 0, wxALIGN_CENTER_VERTICAL | wxALL, 5); - - const wxBitmap help_bitmap = wxHelper::LoadThemedBitmapFromPNG(PNG_HELP_png, sizeof(PNG_HELP_png), wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT)); - auto* help_button = new wxStaticBitmap(panel, wxID_ANY, help_bitmap); - help_button->SetToolTip(formatWxString(_("The following prefixes are supported:\n{0}\n{1}\n{2}\n{3}\n{4}"), - "titleid:", "name:", "type:", "version:", "region:")); - row->Add(help_button, 0, wxALIGN_CENTER_VERTICAL | wxALL, 5); - - sizer->Add(row, 0, wxEXPAND, 5); - } - - sizer->Add(new wxStaticLine(panel, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL), 0, wxEXPAND | wxALL, 5); - - m_title_list = new wxTitleManagerList(panel); - m_title_list->SetSizeHints(800, 600); - m_title_list->Bind(wxEVT_LIST_ITEM_SELECTED, &TitleManager::OnTitleSelected, this); - sizer->Add(m_title_list, 1, wxALL | wxEXPAND, 5); - - sizer->Add(new wxStaticLine(panel, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL), 0, wxEXPAND | wxALL, 5); - - { - auto* row = new wxFlexGridSizer(0, 3, 0, 0); - row->AddGrowableCol(2); - - auto* install_button = new wxButton(panel, wxID_ANY, _("Install title")); - install_button->Bind(wxEVT_BUTTON, &TitleManager::OnInstallTitle, this); - row->Add(install_button, 0, wxALIGN_CENTER_VERTICAL | wxALL, 5); - - row->Add(new wxStaticLine(panel, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_VERTICAL), 0, wxEXPAND | wxALL, 5); - - { - m_save_panel = new wxPanel(panel); - auto* save_sizer = new wxFlexGridSizer(0, 7, 0, 0); - - save_sizer->Add(new wxStaticText(m_save_panel, wxID_ANY, _("Account")), 0, wxALIGN_CENTER_VERTICAL | wxALL, 5); - - m_save_account_list = new wxChoice(m_save_panel, wxID_ANY); - m_save_account_list->SetMinSize({ 170, -1 }); - m_save_account_list->Bind(wxEVT_CHOICE, &TitleManager::OnSaveAccountSelected, this); - save_sizer->Add(m_save_account_list, 0, wxALIGN_CENTER_VERTICAL | wxALL, 5); - - auto* save_open = new wxButton(m_save_panel, wxID_ANY, _("Open directory")); - save_open->Bind(wxEVT_BUTTON, &TitleManager::OnSaveOpenDirectory, this); - save_open->SetToolTip(_("Open the directory of the save entry")); - save_open->Disable(); - save_sizer->Add(save_open, 0, wxALIGN_CENTER_VERTICAL | wxALL, 5); - - auto* save_transfer = new wxButton(m_save_panel, wxID_ANY, _("Transfer")); - save_transfer->Bind(wxEVT_BUTTON, &TitleManager::OnSaveTransfer, this); - save_transfer->SetToolTip(_("Transfers the save entry to another persistent account id")); - save_transfer->Disable(); - save_sizer->Add(save_transfer, 0, wxALIGN_CENTER_VERTICAL | wxALL, 5); - - auto* save_delete = new wxButton(m_save_panel, wxID_ANY, _("Delete")); - save_delete->Bind(wxEVT_BUTTON, &TitleManager::OnSaveDelete, this); - save_delete->SetToolTip(_("Permanently delete the save entry")); - save_delete->Disable(); - save_sizer->Add(save_delete, 0, wxALIGN_CENTER_VERTICAL | wxALL, 5); - - m_save_import = new wxButton(m_save_panel, wxID_ANY, _("Import")); - m_save_import->Bind(wxEVT_BUTTON, &TitleManager::OnSaveImport, this); - m_save_import->SetToolTip(_("Imports a zipped save entry")); - save_sizer->Add(m_save_import, 0, wxALIGN_CENTER_VERTICAL | wxLEFT | wxTOP | wxBOTTOM, 5); - - auto* export_bttn = new wxButton(m_save_panel, wxID_ANY, _("Export")); - export_bttn->Bind(wxEVT_BUTTON, &TitleManager::OnSaveExport, this); - export_bttn->SetToolTip(_("Exports the selected save entry as zip file")); - export_bttn->Disable(); - save_sizer->Add(export_bttn, 0, wxALIGN_CENTER_VERTICAL | wxRIGHT | wxTOP | wxBOTTOM, 5); - - m_save_panel->SetSizerAndFit(save_sizer); - row->Add(m_save_panel, 1, wxRESERVE_SPACE_EVEN_IF_HIDDEN | wxALIGN_CENTER_VERTICAL, 0); - m_save_panel->Hide(); // hide by default - } - - sizer->Add(row, 0, wxEXPAND, 5); - } - - panel->SetSizerAndFit(sizer); - return panel; -} - -wxPanel* TitleManager::CreateDownloadManagerPage() -{ - auto* panel = new wxPanel(m_notebook); - auto* sizer = new wxBoxSizer(wxVERTICAL); - { - auto* row = new wxBoxSizer(wxHORIZONTAL); - -#if DOWNLOADMGR_HAS_ACCOUNT_DROPDOWN - m_account = new wxChoice(panel, wxID_ANY); - m_account->SetMinSize({ 250,-1 }); - auto accounts = Account::GetAccounts(); - if (!accounts.empty()) - { - const auto id = GetConfig().account.m_persistent_id.GetValue(); - for (const auto& a : accounts) - { - m_account->Append(a.ToString(), (void*)static_cast(a.GetPersistentId())); - if(a.GetPersistentId() == id) - { - m_account->SetSelection(m_account->GetCount() - 1); - } - } - } - - row->Add(m_account, 0, wxALL, 5); -#endif - - m_connect = new wxButton(panel, wxID_ANY, _("Connect")); - m_connect->Bind(wxEVT_BUTTON, &TitleManager::OnConnect, this); - row->Add(m_connect, 0, wxALL, 5); - - sizer->Add(row, 0, wxEXPAND, 5); - } - -#if DOWNLOADMGR_HAS_ACCOUNT_DROPDOWN - m_status_text = new wxStaticText(panel, wxID_ANY, _("Select an account and press Connect")); -#else - if(!NCrypto::HasDataForConsoleCert()) - { - m_status_text = new wxStaticText(panel, wxID_ANY, _("Valid online files are required to download eShop titles. For more information, go to the Account tab in the General Settings.")); - m_connect->Enable(false); - } - else - m_status_text = new wxStaticText(panel, wxID_ANY, _("Click on Connect to load the list of downloadable titles")); -#endif - this->Bind(wxEVT_SET_TEXT, &TitleManager::OnSetStatusText, this); - sizer->Add(m_status_text, 0, wxALL, 5); - - sizer->Add(new wxStaticLine(panel, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL), 0, wxEXPAND | wxALL, 5); - - { - auto* row = new wxFlexGridSizer(0, 3, 0, 0); - m_show_titles = new wxCheckBox(panel, wxID_ANY, _("Show available titles")); - m_show_titles->SetValue(true); - m_show_titles->Enable(false); - row->Add(m_show_titles, 0, wxALL, 5); - m_show_titles->Bind(wxEVT_CHECKBOX, &TitleManager::OnDlFilterCheckbox, this); - - m_show_updates = new wxCheckBox(panel, wxID_ANY, _("Show available updates")); - m_show_updates->SetValue(true); - m_show_updates->Enable(false); - row->Add(m_show_updates, 0, wxALL, 5); - m_show_updates->Bind(wxEVT_CHECKBOX, &TitleManager::OnDlFilterCheckbox, this); - - m_show_installed = new wxCheckBox(panel, wxID_ANY, _("Show installed")); - m_show_installed->SetValue(true); - m_show_installed->Enable(false); - row->Add(m_show_installed, 0, wxALL, 5); - m_show_installed->Bind(wxEVT_CHECKBOX, &TitleManager::OnDlFilterCheckbox, this); - - sizer->Add(row, 0, wxEXPAND, 5); - } - - m_download_list = new wxDownloadManagerList(panel); - m_download_list->SetSizeHints(800, 600); - m_download_list->Bind(wxEVT_LIST_ITEM_SELECTED, &TitleManager::OnTitleSelected, this); - sizer->Add(m_download_list, 1, wxALL | wxEXPAND, 5); - - panel->SetSizerAndFit(sizer); - return panel; -} - -TitleManager::TitleManager(wxWindow* parent, TitleManagerPage default_page) - : wxFrame(parent, wxID_ANY, _("Title Manager"), wxDefaultPosition, wxDefaultSize, wxDEFAULT_FRAME_STYLE | wxTAB_TRAVERSAL) -{ - SetIcon(wxICON(X_BOX)); - - auto* sizer = new wxBoxSizer(wxVERTICAL); - m_notebook = new wxNotebook(this, wxID_ANY); - - m_notebook->AddPage(CreateTitleManagerPage(), _("Title Manager"), default_page == TitleManagerPage::TitleManager); - m_notebook->AddPage(CreateDownloadManagerPage(), _("Download Manager"), default_page == TitleManagerPage::DownloadManager); - - sizer->Add(m_notebook, 1, wxEXPAND); - - m_status_bar = CreateStatusBar(2, wxSTB_SIZEGRIP); - m_status_bar->SetStatusText(_("Searching for titles...")); - - this->SetSizerAndFit(sizer); - this->Centre(wxBOTH); - - this->Bind(wxEVT_SET_STATUS_BAR_TEXT, &TitleManager::OnSetStatusBarText, this); - this->Bind(wxEVT_TITLE_FOUND, &TitleManager::OnTitleFound, this); - this->Bind(wxEVT_TITLE_SEARCH_COMPLETE, &TitleManager::OnTitleSearchComplete, this); - this->Bind(wxEVT_DL_TITLE_UPDATE, &TitleManager::OnDownloadableTitleUpdate, this); - this->Bind(wxEVT_DL_DISCONNECT_COMPLETE, &TitleManager::OnDisconnect, this); - - // TODO typing on title list should change filter text and filter! - - // if download manager is already active then restore state - DownloadManager* dlMgr = DownloadManager::GetInstance(false); - if (dlMgr && dlMgr->IsConnected()) - { - dlMgr->setUserData(this); - dlMgr->registerCallbacks( - TitleManager::Callback_ConnectStatusUpdate, - TitleManager::Callback_AddDownloadableTitle, - TitleManager::Callback_RemoveDownloadableTitle); - SetConnected(true); - } - - m_callbackId = CafeTitleList::RegisterCallback([](CafeTitleListCallbackEvent* evt, void* ctx) { ((TitleManager*)ctx)->HandleTitleListCallback(evt); }, this); -} - -TitleManager::~TitleManager() -{ - CafeTitleList::UnregisterCallback(m_callbackId); - - // unregister callbacks for download manager - DownloadManager* dlMgr = DownloadManager::GetInstance(false); - if (dlMgr) - { - dlMgr->setUserData(nullptr); - dlMgr->registerCallbacks( - nullptr, - nullptr, - nullptr); - // if download manager is still downloading / installing then show a warning - if (dlMgr->hasActiveDownloads()) - { - static bool s_showedBGDownloadWarning = false; - if (!s_showedBGDownloadWarning) - { - wxMessageBox(_("Currently active downloads will continue in the background."), _("Information"), wxOK | wxCENTRE, this); - s_showedBGDownloadWarning = true; - } - } - } - - m_running = false; -} - -void TitleManager::SetFocusAndTab(TitleManagerPage page) -{ - m_notebook->SetSelection((int)page); - this->SetFocus(); -} - -void TitleManager::SetDownloadStatusText(const wxString& text) -{ - auto* evt = new wxCommandEvent(wxEVT_SET_TEXT); - evt->SetEventObject(m_status_text); - evt->SetString(text); - wxQueueEvent(this, evt); -} - -void TitleManager::HandleTitleListCallback(CafeTitleListCallbackEvent* evt) -{ - if (evt->eventType == CafeTitleListCallbackEvent::TYPE::SCAN_FINISHED) - { - auto* evt = new wxCommandEvent(wxEVT_TITLE_SEARCH_COMPLETE); - wxQueueEvent(this, evt); - } -} - -void TitleManager::OnTitleFound(wxCommandEvent& event) -{ - auto* obj = dynamic_cast(event.GetClientObject()); - wxASSERT(obj); - m_title_list->AddTitle(obj); -} - -void TitleManager::OnTitleSearchComplete(wxCommandEvent& event) -{ - m_isScanning = false; - if (m_connectRequested) - { - InitiateConnect(); - m_connectRequested = false; - } - // update status bar text - m_title_list->SortEntries(-1); - m_status_bar->SetStatusText(formatWxString(_("Found {0} games, {1} updates, {2} DLCs and {3} save entries"), - m_title_list->GetCountByType(wxTitleManagerList::EntryType::Base) + m_title_list->GetCountByType(wxTitleManagerList::EntryType::System), - m_title_list->GetCountByType(wxTitleManagerList::EntryType::Update), - m_title_list->GetCountByType(wxTitleManagerList::EntryType::Dlc), - m_title_list->GetCountByType(wxTitleManagerList::EntryType::Save) - )); - - // re-filter if any filter is set - const auto filter = m_filter->GetValue(); - if (!filter.IsEmpty()) - m_title_list->Filter(m_filter->GetValue()); - - m_title_list->AutosizeColumns(); - m_refresh_button->Enable(); -} - -void TitleManager::OnSetStatusBarText(wxSetStatusBarTextEvent& event) -{ - m_status_bar->SetStatusText(event.GetText(), event.GetNumber()); -} - -void TitleManager::OnFilterChanged(wxCommandEvent& event) -{ - event.Skip(); - m_title_list->Filter(m_filter->GetValue()); -} - -void TitleManager::OnRefreshButton(wxCommandEvent& event) -{ - m_refresh_button->Disable(); - m_isScanning = true; - // m_title_list->ClearItems(); -> Dont clear. Refresh() triggers incremental updates via notifications - m_status_bar->SetStatusText(_("Searching for titles...")); - CafeTitleList::Refresh(); -} - -void TitleManager::OnInstallTitle(wxCommandEvent& event) -{ - wxFileDialog openFileDialog(this, _("Select title to install"), "", "", "meta.xml|meta.xml", wxFD_OPEN | wxFD_FILE_MUST_EXIST); - if (openFileDialog.ShowModal() == wxID_CANCEL || openFileDialog.GetPath().IsEmpty()) - return; - - fs::path filePath(openFileDialog.GetPath().wc_string()); - try - { - filePath = filePath.parent_path(); - filePath = filePath.parent_path(); - GameUpdateWindow frame(*this, filePath); - const int updateResult = frame.ShowModal(); - - if (updateResult == wxID_OK) - { - CafeTitleList::AddTitleFromPath(frame.GetTargetPath()); - } - else - { - if (frame.GetExceptionMessage().empty()) - wxMessageBox(_("Update installation has been canceled!")); - else - { - throw std::runtime_error(frame.GetExceptionMessage()); - } - } - } - catch (const AbortException&) - { - // ignored - } - catch (const std::exception& ex) - { - wxMessageBox(ex.what(), _("Update error")); - } -} - -static void PopulateSavePersistentIds(wxTitleManagerList::TitleEntry& entry) -{ - if (!entry.persistent_ids.empty()) - return; - cemu_assert(entry.type == wxTitleManagerList::EntryType::Save); - SaveInfo saveInfo = CafeSaveList::GetSaveByTitleId(entry.title_id); - if (!saveInfo.IsValid()) - return; - fs::path savePath = saveInfo.GetPath(); - savePath /= "user"; - std::error_code ec; - for (auto it : fs::directory_iterator(savePath, ec)) - { - if(!it.is_directory(ec)) - continue; - if(fs::is_empty(it.path())) - continue; - std::string dirName = it.path().filename().string(); - if(!std::regex_match(dirName, std::regex("[0-9a-fA-F]{8}"))) - continue; - entry.persistent_ids.emplace_back(ConvertString(dirName, 16)); - } -} - -void TitleManager::OnTitleSelected(wxListEvent& event) -{ - event.Skip(); - - const auto entry = m_title_list->GetSelectedTitleEntry(); - if(entry.has_value() && entry->type == wxTitleManagerList::EntryType::Save) - { - m_save_panel->Show(); - m_save_account_list->Clear(); - - PopulateSavePersistentIds(*entry); - - // an account must be selected before any of the control buttons can be used - for(auto&& v : m_save_panel->GetChildren()) - { - if (dynamic_cast(v) && v->GetId() != m_save_import->GetId()) // import is always enabled - v->Disable(); - } - - const auto& accounts = Account::GetAccounts(); - for (const auto& id : entry->persistent_ids) - { - const auto it = std::find_if(accounts.cbegin(), accounts.cend(), [id](const auto& acc) { return acc.GetPersistentId() == id; }); - if(it != accounts.cend()) - { - m_save_account_list->Append(fmt::format("{:x} ({})", id, - boost::nowide::narrow(it->GetMiiName().data(), it->GetMiiName().size())), - (void*)(uintptr_t)id); - } - else - m_save_account_list->Append(fmt::format("{:x}", id), (void*)(uintptr_t)id); - } - } - else - { - m_save_panel->Hide(); - } -} - -void TitleManager::OnSaveOpenDirectory(wxCommandEvent& event) -{ - const auto selection= m_save_account_list->GetSelection(); - if (selection == wxNOT_FOUND) - return; - - const auto entry = m_title_list->GetSelectedTitleEntry(); - if (!entry.has_value()) - return; - - const auto persistent_id = (uint32)(uintptr_t)m_save_account_list->GetClientData(selection); - - const fs::path target = ActiveSettings::GetMlcPath("usr/save/{:08x}/{:08x}/user/{:08x}", (uint32)(entry->title_id >> 32), (uint32)(entry->title_id & 0xFFFFFFFF), persistent_id); - if (!fs::exists(target) || !fs::is_directory(target)) - return; - - wxLaunchDefaultApplication(wxHelper::FromPath(target)); -} - -void TitleManager::OnSaveDelete(wxCommandEvent& event) -{ - const auto selection_index = m_save_account_list->GetSelection(); - if (selection_index == wxNOT_FOUND) - return; - - const auto selection = m_save_account_list->GetStringSelection(); - if (selection.IsEmpty()) - return; - - const auto msg = formatWxString(_("Are you really sure that you want to delete the save entry for {}"), selection); - const auto result = wxMessageBox(msg, _("Warning"), wxYES_NO | wxCENTRE | wxICON_EXCLAMATION, this); - if (result == wxNO) - return; - - auto entry = m_title_list->GetSelectedTitleEntry(); - if (!entry.has_value()) - return; - - const auto persistent_id = (uint32)(uintptr_t)m_save_account_list->GetClientData(selection_index); - - const fs::path target = ActiveSettings::GetMlcPath("usr/save/{:08x}/{:08x}/user/{:08x}", (uint32)(entry->title_id >> 32), (uint32)(entry->title_id & 0xFFFFFFFF), persistent_id); - if (!fs::exists(target) || !fs::is_directory(target)) - return; - - // edit meta saveinfo.xml - bool meta_file_edited = false; - const fs::path saveinfo = ActiveSettings::GetMlcPath("usr/save/{:08x}/{:08x}/meta/saveinfo.xml", (uint32)(entry->title_id >> 32), (uint32)(entry->title_id & 0xFFFFFFFF)); - if (fs::exists(saveinfo) || fs::is_regular_file(saveinfo)) - { - pugi::xml_document doc; - if (doc.load_file(saveinfo.c_str())) - { - auto info_node = doc.child("info"); - if(info_node) - { - auto persistend_id_string = fmt::format(L"{:08x}", persistent_id); - const auto delete_entry = info_node.find_child([&persistend_id_string](const pugi::xml_node& node) - { - return boost::iequals(node.attribute("persistentId").as_string(), persistend_id_string); - }); - if (delete_entry) - { - info_node.remove_child(delete_entry); - meta_file_edited = doc.save_file(saveinfo.c_str()); - } - } - } - } - - if (!meta_file_edited) - cemuLog_log(LogType::Force, "TitleManager::OnSaveDelete: couldn't delete save entry in saveinfo.xml: {}", _pathToUtf8(saveinfo)); - - // remove from title entry - auto& persistent_ids = entry->persistent_ids; - persistent_ids.erase(std::remove(persistent_ids.begin(), persistent_ids.end(), persistent_id), persistent_ids.end()); - - std::error_code ec; - fs::remove_all(target, ec); - if (ec) - { - const auto error_msg = formatWxString(_("Error when trying to delete the save directory:\n{}"), GetSystemErrorMessage(ec)); - wxMessageBox(error_msg, _("Error"), wxOK | wxCENTRE, this); - return; - } - - m_save_account_list->Delete(selection_index); -} - - -void TitleManager::OnSaveTransfer(wxCommandEvent& event) -{ - const auto selection_index = m_save_account_list->GetSelection(); - if (selection_index == wxNOT_FOUND) - return; - - const auto selection = m_save_account_list->GetStringSelection(); - if (selection.IsEmpty()) - return; - - auto entry = m_title_list->GetSelectedTitleEntry(); - if (!entry.has_value()) - return; - - const auto persistent_id = (uint32)(uintptr_t)m_save_account_list->GetClientData(selection_index); - - SaveTransfer transfer(this, entry->title_id, selection, persistent_id); - if (transfer.ShowModal() == wxCANCEL) - return; - - // remove old id entry - auto& persistent_ids = entry->persistent_ids; - persistent_ids.erase(std::remove(persistent_ids.begin(), persistent_ids.end(), persistent_id), persistent_ids.end()); - - // add new id if not added yet - const auto new_id = transfer.GetTargetPersistentId(); - if (new_id != 0 && std::find(persistent_ids.cbegin(), persistent_ids.cend(), new_id) == persistent_ids.cend()) - { - persistent_ids.emplace_back(new_id); - - const auto& account = Account::GetAccount(new_id); - if(account.GetPersistentId() == new_id) - m_save_account_list->Append(fmt::format("{:x} ({})", new_id, - boost::nowide::narrow(account.GetMiiName().data(), account.GetMiiName().size())), - (void*)(uintptr_t)new_id); - else - m_save_account_list->Append(fmt::format("{:x}", new_id), (void*)(uintptr_t)new_id); - } - - m_save_account_list->Delete(selection_index); -} - -void TitleManager::OnSaveAccountSelected(wxCommandEvent& event) -{ - event.Skip(); - for (auto&& v : m_save_panel->GetChildren()) - { - if (!v->IsEnabled()) - v->Enable(); - } -} - -void TitleManager::OnSaveExport(wxCommandEvent& event) -{ - const auto selection_index = m_save_account_list->GetSelection(); - if (selection_index == wxNOT_FOUND) - return; - - const auto selection = m_save_account_list->GetStringSelection(); - if (selection.IsEmpty()) - return; - - const auto entry = m_title_list->GetSelectedTitleEntry(); - if (!entry.has_value()) - return; - - const auto persistent_id = (uint32)(uintptr_t)m_save_account_list->GetClientData(selection_index); - - wxFileDialog path_dialog(this, _("Select a target file to export the save entry"), wxHelper::FromPath(entry->path), wxEmptyString, - fmt::format("{}|*.zip", _("Exported save entry (*.zip)")), wxFD_SAVE | wxFD_OVERWRITE_PROMPT); - if (path_dialog.ShowModal() != wxID_OK || path_dialog.GetPath().IsEmpty()) - return; - - const auto path = path_dialog.GetPath(); - int ze; - auto* zip = zip_open(path.ToUTF8().data(), ZIP_CREATE | ZIP_TRUNCATE, &ze); - if (!zip) - { - zip_error_t ziperror; - zip_error_init_with_code(&ziperror, ze); - const auto error_msg = formatWxString(_("Error when creating the zip for the save entry:\n{}"), zip_error_strerror(&ziperror)); - wxMessageBox(error_msg, _("Error"), wxOK | wxCENTRE | wxICON_ERROR, this); - return; - } - - // grab all files to zip - const auto savedir = fs::path(entry->path).append(fmt::format("user/{:08x}", persistent_id)); - const auto savedir_str = savedir.generic_u8string(); - for(const auto& it : fs::recursive_directory_iterator(savedir)) - { - if (it.path() == "." || it.path() == "..") - continue; - - const auto entryname = it.path().generic_u8string(); - if(fs::is_directory(it.path())) - { - if(zip_dir_add(zip, (const char*)entryname.substr(savedir_str.size() + 1).c_str(), ZIP_FL_ENC_UTF_8) < 0 ) - { - const auto error_msg = formatWxString(_("Error when trying to add a directory to the zip:\n{}"), zip_strerror(zip)); - wxMessageBox(error_msg, _("Error"), wxOK | wxCENTRE | wxICON_ERROR, this); - } - } - else - { - auto* source = zip_source_file(zip, (const char*)entryname.c_str(), 0, 0); - if(!source) - { - const auto error_msg = formatWxString(_("Error when trying to add a file to the zip:\n{}"), zip_strerror(zip)); - wxMessageBox(error_msg, _("Error"), wxOK | wxCENTRE | wxICON_ERROR, this); - } - - if (zip_file_add(zip, (const char*)entryname.substr(savedir_str.size() + 1).c_str(), source, ZIP_FL_ENC_UTF_8) < 0) - { - const auto error_msg = formatWxString(_("Error when trying to add a file to the zip:\n{}"), zip_strerror(zip)); - wxMessageBox(error_msg, _("Error"), wxOK | wxCENTRE | wxICON_ERROR, this); - - zip_source_free(source); - } - } - } - - // add own metainfo like file and store title id for verification later - std::string metacontent = fmt::format("titleId = {:#016x}", entry->title_id); - auto* metabuff = zip_source_buffer(zip, metacontent.data(), metacontent.size(), 0); - if(zip_file_add(zip, "cemu_meta", metabuff, ZIP_FL_ENC_UTF_8) < 0) - { - const auto error_msg = formatWxString(_("Error when trying to add cemu_meta file to the zip:\n{}"), zip_strerror(zip)); - wxMessageBox(error_msg, _("Error"), wxOK | wxCENTRE | wxICON_ERROR, this); - - zip_source_free(metabuff); - } - - zip_close(zip); -} - -void TitleManager::OnSaveImport(wxCommandEvent& event) -{ - auto entry = m_title_list->GetSelectedTitleEntry(); - if (!entry.has_value()) - return; - - SaveImportWindow save_import(this, entry->title_id); - if (save_import.ShowModal() == wxCANCEL) - return; - - // add new id if not added yet - auto& persistent_ids = entry->persistent_ids; - const auto new_id = save_import.GetTargetPersistentId(); - if (new_id != 0 && std::find(persistent_ids.cbegin(), persistent_ids.cend(), new_id) == persistent_ids.cend()) - { - persistent_ids.emplace_back(new_id); - - const auto& account = Account::GetAccount(new_id); - if (account.GetPersistentId() == new_id) - m_save_account_list->Append(fmt::format("{:x} ({})", new_id, - boost::nowide::narrow(account.GetMiiName().data(), account.GetMiiName().size())), - (void*)(uintptr_t)new_id); - else - m_save_account_list->Append(fmt::format("{:x}", new_id), (void*)(uintptr_t)new_id); - } - -} - -void TitleManager::InitiateConnect() -{ - // init connection to download manager if queued -#if DOWNLOADMGR_HAS_ACCOUNT_DROPDOWN - uint32 persistentId = (uint32)(uintptr_t)m_account->GetClientData(m_account->GetSelection()); - auto& account = Account::GetAccount(persistentId); -#endif - DownloadManager* dlMgr = DownloadManager::GetInstance(); - dlMgr->reset(); - m_download_list->SetCurrentDownloadMgr(dlMgr); - - std::string deviceCertBase64 = NCrypto::CertECC::GetDeviceCertificate().encodeToBase64(); - - if (!NCrypto::SEEPROM_IsPresent()) - { - SetDownloadStatusText(_("Dumped online files not found")); - return; - } - - SetDownloadStatusText(_("Connecting...")); - // begin async connect - dlMgr->setUserData(this); - dlMgr->registerCallbacks( - TitleManager::Callback_ConnectStatusUpdate, - TitleManager::Callback_AddDownloadableTitle, - TitleManager::Callback_RemoveDownloadableTitle); - std::string accountName; - std::array accountPassword; - std::string accountCountry; -#if DOWNLOADMGR_HAS_ACCOUNT_DROPDOWN - accountName = account.GetAccountId(); - accountPassword = account.GetAccountPasswordCache(); - accountCountry.assign(NCrypto::GetCountryAsString(account.GetCountry())); -#endif - dlMgr->connect(accountName, accountPassword, NCrypto::SEEPROM_GetRegion(), accountCountry, NCrypto::GetDeviceId(), NCrypto::GetSerial(), deviceCertBase64); -} - -void TitleManager::OnConnect(wxCommandEvent& event) -{ - if (!m_isScanning) - { - InitiateConnect(); - SetConnected(true); - } - else - { - SetDownloadStatusText(_("Getting installed title information...")); - SetConnected(true); - m_connectRequested = true; - } -} - -void TitleManager::OnDlFilterCheckbox(wxCommandEvent& event) -{ - m_download_list->Filter2(m_show_titles->GetValue(), m_show_updates->GetValue(), m_show_installed->GetValue()); - m_download_list->SortEntries(); -} - -void TitleManager::OnSetStatusText(wxCommandEvent& event) -{ - auto* text = wxDynamicCast(event.GetEventObject(), wxControl); - wxASSERT(text); - text->SetLabel(event.GetString()); -} - -void TitleManager::OnDownloadableTitleUpdate(wxCommandEvent& event) -{ - auto* obj = dynamic_cast(event.GetClientObject()); - auto entry = obj->GetData(); - m_download_list->AddOrUpdateTitle(obj); -} - -void TitleManager::OnDisconnect(wxCommandEvent& event) -{ - SetConnected(false); -} - -void TitleManager::SetConnected(bool state) -{ -#if DOWNLOADMGR_HAS_ACCOUNT_DROPDOWN - m_account->Enable(!state); -#endif - m_connect->Enable(!state); - - m_show_titles->Enable(state); - m_show_updates->Enable(state); - m_show_installed->Enable(state); - m_download_list->Enable(state); -} - -void TitleManager::Callback_ConnectStatusUpdate(std::string statusText, DLMGR_STATUS_CODE statusCode) -{ - TitleManager* titleManager = static_cast(DownloadManager::GetInstance()->getUserData()); - titleManager->SetDownloadStatusText(wxString::FromUTF8(statusText)); - if (statusCode == DLMGR_STATUS_CODE::FAILED) - { - auto* evt = new wxCommandEvent(wxEVT_DL_DISCONNECT_COMPLETE); - wxQueueEvent(titleManager, evt); // this will set SetConnected() to false - return; - } -} - -void TitleManager::Callback_AddDownloadableTitle(const DlMgrTitleReport& titleInfo) -{ - TitleManager* titleManager = static_cast(DownloadManager::GetInstance()->getUserData()); - wxDownloadManagerList::EntryType type = wxDownloadManagerList::EntryType::Base; - if (((titleInfo.titleId >> 32) & 0xF) == 0xE) - type = wxDownloadManagerList::EntryType::Update; - else if (((titleInfo.titleId >> 32) & 0xF) == 0xC) - type = wxDownloadManagerList::EntryType::DLC; - if (titleInfo.status == DlMgrTitleReport::STATUS::INSTALLABLE || titleInfo.status == DlMgrTitleReport::STATUS::INSTALLABLE_UNFINISHED || titleInfo.status == DlMgrTitleReport::STATUS::INSTALLABLE_UPDATE) - { - // installable title - wxDownloadManagerList::TitleEntry titleEntry(type, false, titleInfo.titleId, titleInfo.version, titleInfo.isPaused); - titleEntry.name = wxString::FromUTF8(titleInfo.name); - titleEntry.status = wxDownloadManagerList::TitleDownloadStatus::Available; - titleEntry.progress = 0; - if (titleInfo.status == DlMgrTitleReport::STATUS::INSTALLABLE_UNFINISHED) - titleEntry.progress = 1; - if (titleInfo.status == DlMgrTitleReport::STATUS::INSTALLABLE_UPDATE) - titleEntry.progress = 2; - auto* evt = new wxCommandEvent(wxEVT_DL_TITLE_UPDATE); - evt->SetClientObject(new wxCustomData(titleEntry)); - wxQueueEvent(titleManager, evt); - } - else - { - // package - wxDownloadManagerList::TitleEntry titleEntry(type, true, titleInfo.titleId, titleInfo.version, titleInfo.isPaused); - titleEntry.name = wxString::FromUTF8(titleInfo.name); - titleEntry.progress = titleInfo.progress; - titleEntry.progressMax = titleInfo.progressMax; - switch (titleInfo.status) - { - case DlMgrTitleReport::STATUS::INITIALIZING: - titleEntry.status = wxDownloadManagerList::TitleDownloadStatus::Initializing; - break; - case DlMgrTitleReport::STATUS::QUEUED: - titleEntry.status = wxDownloadManagerList::TitleDownloadStatus::Queued; - break; - case DlMgrTitleReport::STATUS::CHECKING: - titleEntry.status = wxDownloadManagerList::TitleDownloadStatus::Checking; - break; - case DlMgrTitleReport::STATUS::DOWNLOADING: - titleEntry.status = wxDownloadManagerList::TitleDownloadStatus::Downloading; - titleEntry.progress = titleInfo.progress; - break; - case DlMgrTitleReport::STATUS::VERIFYING: - titleEntry.status = wxDownloadManagerList::TitleDownloadStatus::Verifying; - break; - case DlMgrTitleReport::STATUS::INSTALLING: - titleEntry.status = wxDownloadManagerList::TitleDownloadStatus::Installing; - break; - case DlMgrTitleReport::STATUS::INSTALLED: - titleEntry.status = wxDownloadManagerList::TitleDownloadStatus::Installed; - break; - case DlMgrTitleReport::STATUS::HAS_ERROR: - titleEntry.status = wxDownloadManagerList::TitleDownloadStatus::Error; - titleEntry.errorMsg = titleInfo.errorMsg; - break; - default: - titleEntry.status = wxDownloadManagerList::TitleDownloadStatus::None; - break; - } - - auto* evt = new wxCommandEvent(wxEVT_DL_TITLE_UPDATE); - evt->SetClientObject(new wxCustomData(titleEntry)); - wxQueueEvent(titleManager, evt); - } -} - -void TitleManager::Callback_RemoveDownloadableTitle(uint64 titleId, uint16 version) -{ - TitleManager* titleManager = static_cast(DownloadManager::GetInstance()->getUserData()); - assert_dbg(); -} diff --git a/src/gui/wxgui/TitleManager.h b/src/gui/wxgui/TitleManager.h deleted file mode 100644 index ae284040..00000000 --- a/src/gui/wxgui/TitleManager.h +++ /dev/null @@ -1,104 +0,0 @@ -#pragma once - -#include -#include - -#include -#include - -#include "Cemu/Tools/DownloadManager/DownloadManager.h" - -#define DOWNLOADMGR_HAS_ACCOUNT_DROPDOWN 0 - -class wxCheckBox; -class wxStaticText; -class wxListEvent; -class wxSetStatusBarTextEvent; -class wxTitleManagerList; -class wxDownloadManagerList; -class wxTextCtrl; -class wxStatusBar; -class wxImageList; -class wxBitmapButton; -class wxPanel; -class wxChoice; -class wxNotebook; - -enum class TitleManagerPage -{ - TitleManager = 0, - DownloadManager = 1 -}; - -enum class DLMGR_STATUS_CODE; - -class TitleManager : public wxFrame -{ -public: - TitleManager(wxWindow* parent, TitleManagerPage default_page = TitleManagerPage::TitleManager); - ~TitleManager(); - - void SetFocusAndTab(TitleManagerPage page); - - void SetDownloadStatusText(const wxString& text); - -private: - wxPanel* CreateTitleManagerPage(); - wxPanel* CreateDownloadManagerPage(); - - // title manager - void OnTitleFound(wxCommandEvent& event); - void OnTitleSearchComplete(wxCommandEvent& event); - void OnSetStatusBarText(wxSetStatusBarTextEvent& event); - void OnFilterChanged(wxCommandEvent& event); - void OnRefreshButton(wxCommandEvent& event); - void OnInstallTitle(wxCommandEvent& event); - void OnTitleSelected(wxListEvent& event); - void OnSaveOpenDirectory(wxCommandEvent& event); - void OnSaveDelete(wxCommandEvent& event); - void OnSaveTransfer(wxCommandEvent& event); - void OnSaveAccountSelected(wxCommandEvent& event); - void OnSaveExport(wxCommandEvent& event); - void OnSaveImport(wxCommandEvent& event); - - void HandleTitleListCallback(struct CafeTitleListCallbackEvent* evt); - - wxNotebook* m_notebook; - - uint32 m_callbackId; - - // title manager - wxTextCtrl* m_filter; - wxTitleManagerList* m_title_list; - wxStatusBar* m_status_bar; - wxBitmapButton* m_refresh_button; - wxPanel* m_save_panel; - wxChoice* m_save_account_list; - wxButton* m_save_import; - - bool m_isScanning{ true }; // set when CafeTitleList is scanning - - std::atomic_bool m_running = true; - - // download manager - void InitiateConnect(); - void OnConnect(wxCommandEvent& event); - void OnSetStatusText(wxCommandEvent& event); - void OnDownloadableTitleUpdate(wxCommandEvent& event); - void OnDisconnect(wxCommandEvent& event); - - void OnDlFilterCheckbox(wxCommandEvent& event); - - void SetConnected(bool state); - - static void Callback_ConnectStatusUpdate(std::string statusText, DLMGR_STATUS_CODE statusCode); - static void Callback_AddDownloadableTitle(const struct DlMgrTitleReport& titleInfo); - static void Callback_RemoveDownloadableTitle(uint64 titleId, uint16 version); - - wxChoice* m_account; - wxButton* m_connect; - wxStaticText* m_status_text; - wxCheckBox *m_show_titles, *m_show_updates, *m_show_installed; - wxDownloadManagerList* m_download_list; - bool m_connectRequested{false}; // connect was clicked before m_foundTitles was available -}; diff --git a/src/gui/wxgui/canvas/IRenderCanvas.h b/src/gui/wxgui/canvas/IRenderCanvas.h deleted file mode 100644 index 125ae954..00000000 --- a/src/gui/wxgui/canvas/IRenderCanvas.h +++ /dev/null @@ -1,14 +0,0 @@ -#pragma once - -#include - -// base class for all render interfaces -class IRenderCanvas -{ -public: - IRenderCanvas(bool is_main_window) - : m_is_main_window(is_main_window) {} - -protected: - bool m_is_main_window; -}; \ No newline at end of file diff --git a/src/gui/wxgui/canvas/MetalCanvas.cpp b/src/gui/wxgui/canvas/MetalCanvas.cpp deleted file mode 100644 index ee58abcd..00000000 --- a/src/gui/wxgui/canvas/MetalCanvas.cpp +++ /dev/null @@ -1,61 +0,0 @@ -#include "wxgui/canvas/MetalCanvas.h" -#include "Cafe/HW/Latte/Renderer/Metal/MetalRenderer.h" - -#include -#include - -MetalCanvas::MetalCanvas(wxWindow* parent, const wxSize& size, bool is_main_window) - : IRenderCanvas(is_main_window), wxWindow(parent, wxID_ANY, wxDefaultPosition, size, wxNO_FULL_REPAINT_ON_RESIZE | wxWANTS_CHARS) -{ - Bind(wxEVT_PAINT, &MetalCanvas::OnPaint, this); - Bind(wxEVT_SIZE, &MetalCanvas::OnResize, this); - - auto& canvas = is_main_window ? WindowSystem::GetWindowInfo().canvas_main : WindowSystem::GetWindowInfo().canvas_pad; - canvas = initHandleContextFromWxWidgetsWindow(this); - - try - { - if (is_main_window) - g_renderer = std::make_unique(); - - auto metal_renderer = MetalRenderer::GetInstance(); - metal_renderer->InitializeLayer({size.x, size.y}, is_main_window); - } - catch(const std::exception& ex) - { - cemuLog_log(LogType::Force, "Error when initializing Metal renderer: {}", ex.what()); - auto msg = formatWxString(_("Error when initializing Metal renderer:\n{}"), ex.what()); - wxMessageDialog dialog(this, msg, _("Error"), wxOK | wxCENTRE | wxICON_ERROR); - dialog.ShowModal(); - exit(0); - } - - wxWindow::EnableTouchEvents(wxTOUCH_PAN_GESTURES); -} - -MetalCanvas::~MetalCanvas() -{ - Unbind(wxEVT_PAINT, &MetalCanvas::OnPaint, this); - Unbind(wxEVT_SIZE, &MetalCanvas::OnResize, this); - - MetalRenderer* mtlr = (MetalRenderer*)g_renderer.get(); - if (mtlr) - mtlr->ShutdownLayer(m_is_main_window); -} - -void MetalCanvas::OnPaint(wxPaintEvent& event) -{ -} - -void MetalCanvas::OnResize(wxSizeEvent& event) -{ - const wxSize size = GetSize(); - if (size.GetWidth() == 0 || size.GetHeight() == 0) - return; - - const wxRect refreshRect(size); - RefreshRect(refreshRect, false); - - auto metal_renderer = MetalRenderer::GetInstance(); - metal_renderer->ResizeLayer({size.x, size.y}, m_is_main_window); -} diff --git a/src/gui/wxgui/canvas/MetalCanvas.h b/src/gui/wxgui/canvas/MetalCanvas.h deleted file mode 100644 index a0a3a616..00000000 --- a/src/gui/wxgui/canvas/MetalCanvas.h +++ /dev/null @@ -1,19 +0,0 @@ -#pragma once - -#include "wxgui/canvas/IRenderCanvas.h" - -#include - -#include - -class MetalCanvas : public IRenderCanvas, public wxWindow -{ -public: - MetalCanvas(wxWindow* parent, const wxSize& size, bool is_main_window); - ~MetalCanvas(); - -private: - - void OnPaint(wxPaintEvent& event); - void OnResize(wxSizeEvent& event); -}; diff --git a/src/gui/wxgui/canvas/OpenGLCanvas.cpp b/src/gui/wxgui/canvas/OpenGLCanvas.cpp deleted file mode 100644 index d80d696e..00000000 --- a/src/gui/wxgui/canvas/OpenGLCanvas.cpp +++ /dev/null @@ -1,179 +0,0 @@ -#include "wxgui/canvas/OpenGLCanvas.h" - -#include "wxgui/canvas/IRenderCanvas.h" - -#include "Cafe/HW/Latte/Renderer/OpenGL/OpenGLRenderer.h" - -#include "config/CemuConfig.h" - -#include "Common/GLInclude/GLInclude.h" -#include // this includes GL/gl.h, avoid using this in a header because it would contaminate our own OpenGL definitions (GLInclude) - -static const int g_gl_attribute_list[] = -{ - WX_GL_RGBA, - WX_GL_DOUBLEBUFFER, - WX_GL_DEPTH_SIZE, 16, - - WX_GL_MIN_RED, 8, - WX_GL_MIN_GREEN, 8, - WX_GL_MIN_BLUE, 8, - WX_GL_MIN_ALPHA, 8, - - WX_GL_STENCIL_SIZE, 8, - - //WX_GL_MAJOR_VERSION, 4, - //WX_GL_MINOR_VERSION, 1, - //wx_GL_COMPAT_PROFILE, - - 0, // end of list -}; - -class OpenGLCanvas; - -class GLCanvasManager : public OpenGLCanvasCallbacks -{ - public: - GLCanvasManager() - { - SetOpenGLCanvasCallbacks(this); - } - - ~GLCanvasManager() - { - ClearOpenGLCanvasCallbacks(); - } - - void SetTVView(OpenGLCanvas* canvas) - { - m_tvView = canvas; - } - - void SetPadView(OpenGLCanvas* canvas) - { - m_padView = canvas; - } - - void SetGLContext(wxGLContext* context) - { - m_glContext = context; - } - - void DeleteGLContext() - { - if (m_tvView == nullptr && m_padView == nullptr && m_glContext) - { - delete m_glContext; - m_glContext = nullptr; - } - } - - bool HasPadViewOpen() const - { - return m_padView != nullptr; - } - - bool MakeCurrent(bool padView); - - void SwapBuffers(bool swapTV, bool swapDRC); - - private: - wxGLContext* m_glContext = nullptr; - OpenGLCanvas* m_tvView = nullptr; - OpenGLCanvas* m_padView = nullptr; -} s_glCanvasManager; - -class OpenGLCanvas : public IRenderCanvas, public wxGLCanvas -{ -public: - OpenGLCanvas(wxWindow* parent, const wxSize& size, bool is_main_window) - : IRenderCanvas(is_main_window), wxGLCanvas(parent, wxID_ANY, g_gl_attribute_list, wxDefaultPosition, size, wxFULL_REPAINT_ON_RESIZE | wxWANTS_CHARS) - { - if (m_is_main_window) - { - s_glCanvasManager.SetTVView(this); - s_glCanvasManager.SetGLContext(new wxGLContext(this)); - - g_renderer = std::make_unique(); - } - else - { - s_glCanvasManager.SetPadView(this); - } - - wxWindow::EnableTouchEvents(wxTOUCH_PAN_GESTURES); - } - - ~OpenGLCanvas() override - { - // todo - if this is the main window, make sure the renderer has been shut down - - if (m_is_main_window) - s_glCanvasManager.SetTVView(nullptr); - else - s_glCanvasManager.SetPadView(nullptr); - - s_glCanvasManager.DeleteGLContext(); - } - - void UpdateVSyncState() - { - int configValue = GetConfig().vsync.GetValue(); - configValue = configValue > 0 ? 1 : 0; - if(m_activeVSyncState != configValue) - { -#if BOOST_OS_WINDOWS - if(wglSwapIntervalEXT) - wglSwapIntervalEXT(configValue); // 1 = enabled, 0 = disabled -#elif BOOST_OS_LINUX || BOOST_OS_BSD - if (eglSwapInterval) - { - if (eglSwapInterval(eglGetCurrentDisplay(), configValue) == EGL_FALSE) - { - cemuLog_log(LogType::Force, "Failed to set vsync using EGL"); - } - } -#else - cemuLog_log(LogType::Force, "OpenGL vsync not implemented"); -#endif - m_activeVSyncState = configValue; - } - } - -private: - int m_activeVSyncState = -1; - //wxGLContext* m_context = nullptr; -}; - -wxWindow* GLCanvas_Create(wxWindow* parent, const wxSize& size, bool is_main_window) -{ - return new OpenGLCanvas(parent, size, is_main_window); -} - -void GLCanvasManager::SwapBuffers(bool swapTV, bool swapDRC) -{ - if (swapTV && m_tvView) - { - MakeCurrent(false); - m_tvView->SwapBuffers(); - m_tvView->UpdateVSyncState(); - } - - if (swapDRC && m_padView) - { - MakeCurrent(true); - m_padView->SwapBuffers(); - m_padView->UpdateVSyncState(); - } - - MakeCurrent(false); -} - -bool GLCanvasManager::MakeCurrent(bool padView) -{ - OpenGLCanvas* canvas = padView ? m_padView : m_tvView; - if (!canvas) - return false; - m_glContext->SetCurrent(*canvas); - return true; -} diff --git a/src/gui/wxgui/canvas/OpenGLCanvas.h b/src/gui/wxgui/canvas/OpenGLCanvas.h deleted file mode 100644 index 167058fe..00000000 --- a/src/gui/wxgui/canvas/OpenGLCanvas.h +++ /dev/null @@ -1,4 +0,0 @@ -#pragma once -#include - -wxWindow* GLCanvas_Create(wxWindow* parent, const wxSize& size, bool is_main_window); \ No newline at end of file diff --git a/src/gui/wxgui/canvas/VulkanCanvas.cpp b/src/gui/wxgui/canvas/VulkanCanvas.cpp deleted file mode 100644 index 5b0a0a33..00000000 --- a/src/gui/wxgui/canvas/VulkanCanvas.cpp +++ /dev/null @@ -1,85 +0,0 @@ -#include "wxgui/canvas/VulkanCanvas.h" -#include "Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.h" - -#if ( BOOST_OS_LINUX || BOOST_OS_BSD ) && HAS_WAYLAND -#include "wxgui/helpers/wxWayland.h" -#endif - -#include -#include - -VulkanCanvas::VulkanCanvas(wxWindow* parent, const wxSize& size, bool is_main_window) - : IRenderCanvas(is_main_window), wxWindow(parent, wxID_ANY, wxDefaultPosition, size, wxNO_FULL_REPAINT_ON_RESIZE | wxWANTS_CHARS) -{ - Bind(wxEVT_PAINT, &VulkanCanvas::OnPaint, this); - Bind(wxEVT_SIZE, &VulkanCanvas::OnResize, this); -#if __WXMSW__ - MSWDisableComposited(); -#endif - - auto& canvas = is_main_window ? WindowSystem::GetWindowInfo().canvas_main : WindowSystem::GetWindowInfo().canvas_pad; - canvas = initHandleContextFromWxWidgetsWindow(this); - #if ( BOOST_OS_LINUX || BOOST_OS_BSD ) && HAS_WAYLAND - if (canvas.backend == WindowSystem::WindowHandleInfo::Backend::Wayland) - { - m_subsurface = std::make_unique(this); - canvas.surface = m_subsurface->getSurface(); - } - #endif - - cemu_assert(g_vulkan_available); - - try - { - if (is_main_window) - g_renderer = std::make_unique(); - - auto vulkan_renderer = VulkanRenderer::GetInstance(); - vulkan_renderer->InitializeSurface({size.x, size.y}, is_main_window); - } - catch(const std::exception& ex) - { - cemuLog_log(LogType::Force, "Error when initializing Vulkan renderer: {}", ex.what()); - auto msg = formatWxString(_("Error when initializing Vulkan renderer:\n{}"), ex.what()); - wxMessageDialog dialog(this, msg, _("Error"), wxOK | wxCENTRE | wxICON_ERROR); - dialog.ShowModal(); - _exit(0); - } - - wxWindow::EnableTouchEvents(wxTOUCH_PAN_GESTURES); -} - -VulkanCanvas::~VulkanCanvas() -{ - Unbind(wxEVT_PAINT, &VulkanCanvas::OnPaint, this); - Unbind(wxEVT_SIZE, &VulkanCanvas::OnResize, this); - - if(!m_is_main_window) - { - VulkanRenderer* vkr = (VulkanRenderer*)g_renderer.get(); - if(vkr) - vkr->StopUsingPadAndWait(); - } -} - -void VulkanCanvas::OnPaint(wxPaintEvent& event) -{ -} - -void VulkanCanvas::OnResize(wxSizeEvent& event) -{ - const wxSize size = GetSize(); - if (size.GetWidth() == 0 || size.GetHeight() == 0) - return; - -#if ( BOOST_OS_LINUX || BOOST_OS_BSD ) && HAS_WAYLAND - if(m_subsurface) - { - auto sRect = GetScreenRect(); - m_subsurface->setSize(sRect.GetX(), sRect.GetY(), sRect.GetWidth(), sRect.GetHeight()); - } -#endif - - const wxRect refreshRect(size); - RefreshRect(refreshRect, false); -} diff --git a/src/gui/wxgui/canvas/VulkanCanvas.h b/src/gui/wxgui/canvas/VulkanCanvas.h deleted file mode 100644 index 731638f3..00000000 --- a/src/gui/wxgui/canvas/VulkanCanvas.h +++ /dev/null @@ -1,23 +0,0 @@ -#pragma once - -#include "wxgui/canvas/IRenderCanvas.h" - -#include - -#include "Cafe/HW/Latte/Renderer/Vulkan/VulkanAPI.h" -#include - -class VulkanCanvas : public IRenderCanvas, public wxWindow -{ -#if ( BOOST_OS_LINUX || BOOST_OS_BSD ) && HAS_WAYLAND - std::unique_ptr m_subsurface; -#endif -public: - VulkanCanvas(wxWindow* parent, const wxSize& size, bool is_main_window); - ~VulkanCanvas(); - -private: - - void OnPaint(wxPaintEvent& event); - void OnResize(wxSizeEvent& event); -}; diff --git a/src/gui/wxgui/components/TextList.cpp b/src/gui/wxgui/components/TextList.cpp deleted file mode 100644 index f5e3f133..00000000 --- a/src/gui/wxgui/components/TextList.cpp +++ /dev/null @@ -1,320 +0,0 @@ -#include "wxgui/wxgui.h" -#include "TextList.h" - -#include "debugger/DisasmCtrl.h" - -#include -#include - -TextList::~TextList() -{ - m_tooltip_timer->Stop(); - - this->Unbind(wxEVT_MOTION, &TextList::OnMouseMoveEvent, this); - this->Unbind(wxEVT_KEY_DOWN, &TextList::OnKeyDownEvent, this); - this->Unbind(wxEVT_KEY_UP, &TextList::OnKeyUpEvent, this); - this->Unbind(wxEVT_PAINT, &TextList::OnPaintEvent, this); - this->Unbind(wxEVT_LEFT_DOWN, &TextList::OnMouseDownEvent, this); - this->Unbind(wxEVT_LEFT_UP, &TextList::OnMouseUpEvent, this); - this->Unbind(wxEVT_LEFT_DCLICK, &TextList::OnMouseDClickEvent, this); - this->Unbind(wxEVT_CONTEXT_MENU, &TextList::OnContextMenu, this); - this->Unbind(wxEVT_ERASE_BACKGROUND, &TextList::OnEraseBackground, this); -} - -TextList::TextList(wxWindow* parent, wxWindowID id, const wxPoint& pos, const wxSize& size, long style) - : wxControl(parent, id, pos, size, style), wxScrollHelper(this) -{ - m_font = wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT); - this->wxWindowBase::SetBackgroundStyle(wxBG_STYLE_PAINT); - - wxInfoDC dc(this); - this->DoPrepareReadOnlyDC(dc); - dc.SetFont(m_font); - m_line_height = dc.GetCharHeight(); - m_char_width = dc.GetCharWidth(); - - m_yScrollPixelsPerLine = m_line_height; - - this->ShowScrollbars(wxSHOW_SB_DEFAULT, wxSHOW_SB_DEFAULT); - - m_tooltip = new wxToolTip(wxEmptyString); - - this->Bind(wxEVT_MOTION, &TextList::OnMouseMoveEvent, this); - this->Bind(wxEVT_KEY_DOWN, &TextList::OnKeyDownEvent, this); - this->Bind(wxEVT_KEY_UP, &TextList::OnKeyUpEvent, this); - this->Bind(wxEVT_PAINT, &TextList::OnPaintEvent, this); - this->Bind(wxEVT_LEFT_DOWN, &TextList::OnMouseDownEvent, this); - this->Bind(wxEVT_LEFT_UP, &TextList::OnMouseUpEvent, this); - this->Bind(wxEVT_LEFT_DCLICK, &TextList::OnMouseDClickEvent, this); - this->Bind(wxEVT_CONTEXT_MENU, &TextList::OnContextMenu, this); - this->Bind(wxEVT_ERASE_BACKGROUND, &TextList::OnEraseBackground, this); - - m_tooltip_window = new wxPanel(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxNO_BORDER); - m_tooltip_window->SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_INFOBK)); - m_tooltip_window->Hide(); - - m_tooltip_timer = new wxTimer(this); - this->Bind(wxEVT_TIMER, &TextList::OnTooltipTimer, this); -} - -void TextList::RefreshControl(const wxRect* update_region) -{ - if(update_region) - Refresh(true, update_region); - else - { - wxRect region = GetClientRect(); - update_region = ®ion; - Refresh(true, update_region); - } -} - -void TextList::RefreshLine(uint32 line) -{ - wxRect update_region = GetClientRect(); - update_region.y = (sint32)line * m_line_height; - update_region.height = m_line_height; - CalcScrolledPosition(0, update_region.y, nullptr, &update_region.y); - // debug_printf("update: <%x, %x>\n", update_region.y, update_region.height); - Refresh(true, &update_region); -} - -wxSize TextList::DoGetVirtualSize() const -{ - return {wxDefaultCoord, (int)m_element_count * m_line_height}; -} - -void TextList::DoSetSize(int x, int y, int width, int height, int sizeFlags) -{ - wxControl::DoSetSize(x, y, width, height, sizeFlags); - - m_elements_visible = (height + m_line_height - 1) / m_line_height; - Refresh(); -} - -void TextList::SetScrollPos(int orient, int pos, bool refresh) -{ - wxControl::SetScrollPos(orient, pos, refresh); -} - -void TextList::DrawLineBackground(wxDC& dc, uint32 line, const wxColour& colour, uint32 lines) const -{ - wxRect rect; - rect.x = GetPosition().x; - rect.y = line * m_line_height; - rect.width = GetSize().x; - rect.height = m_line_height * lines; - - dc.SetBrush(colour); - dc.DrawRectangle(rect); -} - -void TextList::DrawLineBackground(wxDC& dc, const wxPoint& position, const wxColour& colour, uint32 lines) const -{ - wxRect rect; - rect.x = position.x; - rect.y = position.y; - rect.width = this->GetSize().x; - rect.height = m_line_height * lines; - - dc.SetBrush(colour); - dc.DrawRectangle(rect); -} - -bool TextList::SetElementCount(size_t element_count) -{ - if (m_element_count == element_count) - return false; - - if (element_count > 0x7FFFFFFF) - element_count = 0x7FFFFFFF; - - m_element_count = element_count; - this->AdjustScrollbars(); - return true; -} - -uint32 TextList::GetElementCount() const -{ - return m_element_count; -} - -bool TextList::IsKeyDown(sint32 keycode) -{ - return m_key_states[keycode]; -} - -void TextList::WriteText(wxDC& dc, const wxString& text, wxPoint& position) const -{ - dc.ResetBoundingBox(); - dc.DrawText(text, position); - position.x += dc.MaxX() - dc.MinX(); -} - -void TextList::WriteText(wxDC& dc, const wxString& text, wxPoint& position, const wxColour& color) const -{ - dc.SetTextForeground(color); - WriteText(dc, text, position); -} - -void TextList::NextLine(wxPoint& position, const wxPoint* start_position) const -{ - position.y += m_line_height; - - if (start_position) - position.x = start_position->x; -} - -void TextList::OnMouseDown() {} -void TextList::OnMouseUp() {} - -bool TextList::OnShowTooltip(const wxPoint& position, uint32 line) -{ - return false; -} - -void TextList::OnMouseMoveEvent(wxMouseEvent& event) -{ - m_tooltip_timer->Stop(); - m_tooltip_timer->StartOnce(250); - - wxPoint position = event.GetPosition(); - CalcUnscrolledPosition(position.x, position.y, &position.x, &position.y); - - m_mouse_position = position; - - if(m_mouse_down) - m_selection.SetBottomRight(position); - - const sint32 line = position.y / m_line_height; - OnMouseMove(position, line); -} - -void TextList::OnKeyDownEvent(wxKeyEvent& event) -{ - const auto key_code = event.GetKeyCode(); - const auto it = m_key_states.find(key_code); - if(it == m_key_states.end() || !it->second) - { - m_key_states[key_code] = true; - OnKeyPressed(key_code, event.GetPosition()); - } - - event.Skip(); -} - -void TextList::OnKeyUpEvent(wxKeyEvent& event) -{ - m_key_states[event.GetKeyCode()] = false; -} - -void TextList::OnMouseDownEvent(wxMouseEvent& event) -{ - m_mouse_down = true; - - wxPoint position = event.GetPosition(); - CalcUnscrolledPosition(position.x, position.y, &position.x, &position.y); - m_selection.SetPosition(position); - m_selection.SetBottomRight(position); - - OnMouseDown(); - - event.Skip(); -} - -void TextList::OnMouseUpEvent(wxMouseEvent& event) -{ - m_mouse_down = false; - OnMouseUp(); - - event.Skip(); -} - -void TextList::OnMouseDClickEvent(wxMouseEvent& event) -{ - wxPoint position = event.GetPosition(); - CalcUnscrolledPosition(position.x, position.y, &position.x, &position.y); - m_selection.SetPosition(position); - m_selection.SetBottomRight(position); - - const uint32 line = position.y / m_line_height; - OnMouseDClick(position, line); -} - -void TextList::OnContextMenu(wxContextMenuEvent& event) -{ - wxPoint position = event.GetPosition(); - if (position == wxDefaultPosition) - return; - - wxPoint clientPosition = ScreenToClient(position); - - CalcUnscrolledPosition(clientPosition.x, clientPosition.y, &clientPosition.x, &clientPosition.y); - m_selection.SetPosition(clientPosition); - m_selection.SetBottomRight(clientPosition); - - const uint32 line = clientPosition.y / m_line_height; - OnContextMenu(clientPosition, line); -} - -void TextList::OnTooltipTimer(wxTimerEvent& event) -{ - m_tooltip_window->Hide(); - - const auto cursor_position = wxGetMousePosition(); - - const auto position = GetScreenPosition(); - if (cursor_position.x < position.x || cursor_position.y < position.y) - return; - - const auto size = GetSize(); - if (position.x + size.x < cursor_position.x || position.y + size.y < cursor_position.y) - return; - - const sint32 line = position.y / m_line_height; - if(OnShowTooltip(m_mouse_position, line)) - { - m_tooltip_window->SetPosition(wxPoint(m_mouse_position.x + 15, m_mouse_position.y + 15)); - m_tooltip_window->SendSizeEvent(); - m_tooltip_window->Show(); - } -} - -void TextList::OnPaintEvent(wxPaintEvent& event) -{ - wxAutoBufferedPaintDC dc(m_targetWindow); - - // get window position - auto position = GetPosition(); - - // get current real position - wxRect rect_update = GetUpdateRegion().GetBox(); - const auto count = (uint32)std::ceil((float)rect_update.GetHeight() / m_line_height); - - position.y = (rect_update.y / m_line_height) * m_line_height; - - // paint background - dc.SetFont(m_font); - const wxColour window_colour = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW); - dc.SetBrush(GetBackgroundColour()); - dc.SetPen(*wxTRANSPARENT_PEN); - dc.DrawRectangle(rect_update); - - //// paint selection - //if (!m_selected_text.eof()) - //{ - // dc.SetBrush(*wxBLUE_BRUSH); - // dc.SetPen(*wxBLUE_PEN); - // dc.DrawRectangle(m_selection); - //} - - sint32 start; - CalcUnscrolledPosition(rect_update.x, rect_update.y, nullptr, &start); - - start /= m_line_height; - m_scrolled_to_end = (start + count) >= m_element_count; - - OnDraw(dc, start, count, position); - - // removed Update() here since all text is white -} \ No newline at end of file diff --git a/src/gui/wxgui/components/TextList.h b/src/gui/wxgui/components/TextList.h deleted file mode 100644 index 9a722a21..00000000 --- a/src/gui/wxgui/components/TextList.h +++ /dev/null @@ -1,73 +0,0 @@ -#pragma once - -#include - -#include -#include - - -class TextList : public wxControl, public wxScrollHelper -{ -public: - virtual ~TextList(); - TextList(wxWindow* parent, wxWindowID id, - const wxPoint& pos = wxDefaultPosition, - const wxSize& size = wxDefaultSize, long style = 0); - - void RefreshControl(const wxRect* update_region = nullptr); - void RefreshLine(uint32 line); - - wxSize DoGetVirtualSize() const override; - void DoSetSize(int x, int y, int width, int height, int sizeFlags) override; - void SetScrollPos(int orient, int pos, bool refresh) override; - void DrawLineBackground(wxDC& dc, uint32 line, const wxColour& colour, uint32 lines = 1) const; - void DrawLineBackground(wxDC& dc, const wxPoint& position, const wxColour& colour, uint32 lines = 1) const; - - bool SetElementCount(size_t element_count); - uint32 GetElementCount() const; - bool IsKeyDown(sint32 keycode); - -protected: - void WriteText(wxDC& dc, const wxString& text, wxPoint& position) const; - void WriteText(wxDC& dc, const wxString& text, wxPoint& position, const wxColour& color) const; - void NextLine(wxPoint& position, const wxPoint* start_position = nullptr) const; - - virtual void OnDraw(wxDC& dc, sint32 start, sint32 count, const wxPoint& start_position) {}; - virtual void OnMouseMove(const wxPoint& position, uint32 line) {} - virtual void OnKeyPressed(sint32 key_code, const wxPoint& position) {} - virtual void OnMouseDown(); - virtual void OnMouseUp(); - virtual void OnMouseDClick(const wxPoint& position, uint32 line) {} - virtual void OnContextMenu(const wxPoint& position, uint32 line) {} - virtual bool OnShowTooltip(const wxPoint& position, uint32 line); - void OnEraseBackground(wxEraseEvent&) {} - - sint32 m_line_height; - sint32 m_char_width; - size_t m_elements_visible = 0; - size_t m_element_count = 0; - bool m_scrolled_to_end = true; - - std::wstringstream m_selected_text; - bool m_mouse_down = false; - wxRect m_selection; - wxPanel* m_tooltip_window; - -private: - void OnPaintEvent(wxPaintEvent& event); - void OnMouseMoveEvent(wxMouseEvent& event); - void OnKeyDownEvent(wxKeyEvent& event); - void OnKeyUpEvent(wxKeyEvent& event); - void OnMouseDownEvent(wxMouseEvent& event); - void OnMouseUpEvent(wxMouseEvent& event); - void OnMouseDClickEvent(wxMouseEvent& event); - void OnContextMenu(wxContextMenuEvent& event); - void OnTooltipTimer(wxTimerEvent& event); - - std::unordered_map m_key_states; - - wxFont m_font; - - wxTimer* m_tooltip_timer; - wxPoint m_mouse_position; -}; diff --git a/src/gui/wxgui/components/wxDownloadManagerList.cpp b/src/gui/wxgui/components/wxDownloadManagerList.cpp deleted file mode 100644 index e3919cff..00000000 --- a/src/gui/wxgui/components/wxDownloadManagerList.cpp +++ /dev/null @@ -1,813 +0,0 @@ -#include "wxgui/components/wxDownloadManagerList.h" - -#include "wxHelper.h" -#include "wxgui/helpers/wxHelpers.h" -#include "util/helpers/SystemException.h" -#include "Cafe/TitleList/GameInfo.h" -#include "wxgui/components/wxGameList.h" -#include "wxgui/helpers/wxCustomEvents.h" - -#include -#include -#include -#include -#include -#include -#include - -#include - -#include "config/ActiveSettings.h" -#include "wxgui/ChecksumTool.h" -#include "Cemu/Tools/DownloadManager/DownloadManager.h" -#include "Cafe/TitleList/TitleId.h" -#include "wxgui/MainWindow.h" - -wxDEFINE_EVENT(wxEVT_REMOVE_ENTRY, wxCommandEvent); - -wxDownloadManagerList::wxDownloadManagerList(wxWindow* parent, wxWindowID id) - : wxListView(parent, id, wxDefaultPosition, wxDefaultSize, wxLC_REPORT | wxLC_VIRTUAL) -{ - AddColumns(); - - // tooltip TODO: extract class mb wxPanelTooltip - m_tooltip_window = new wxPanel(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxNO_BORDER); - auto* tooltip_sizer = new wxBoxSizer(wxVERTICAL); - m_tooltip_text = new wxStaticText(m_tooltip_window, wxID_ANY, wxEmptyString); - tooltip_sizer->Add(m_tooltip_text , 0, wxALL, 5); - m_tooltip_window->SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_INFOBK)); - m_tooltip_window->SetSizerAndFit(tooltip_sizer); - m_tooltip_window->Hide(); - m_tooltip_timer = new wxTimer(this); - - Bind(wxEVT_LIST_COL_CLICK, &wxDownloadManagerList::OnColumnClick, this); - Bind(wxEVT_CONTEXT_MENU, &wxDownloadManagerList::OnContextMenu, this); - Bind(wxEVT_LIST_ITEM_SELECTED, &wxDownloadManagerList::OnItemSelected, this); - Bind(wxEVT_TIMER, &wxDownloadManagerList::OnTimer, this); - Bind(wxEVT_REMOVE_ITEM, &wxDownloadManagerList::OnRemoveItem, this); - Bind(wxEVT_REMOVE_ENTRY, &wxDownloadManagerList::OnRemoveEntry, this); - Bind(wxEVT_CLOSE_WINDOW, &wxDownloadManagerList::OnClose, this); - - ShowSortIndicator(ColumnName); -} - -boost::optional wxDownloadManagerList::GetSelectedTitleEntry() const -{ - const auto selection = GetFirstSelected(); - if (selection != wxNOT_FOUND) - { - const auto tmp = GetTitleEntry(selection); - if (tmp.has_value()) - return tmp.value(); - } - - return {}; -} - -boost::optional wxDownloadManagerList::GetSelectedTitleEntry() -{ - const auto selection = GetFirstSelected(); - if (selection != wxNOT_FOUND) - { - const auto tmp = GetTitleEntry(selection); - if (tmp.has_value()) - return tmp.value(); - } - - return {}; -} - -boost::optional wxDownloadManagerList::GetTitleEntry(uint64 titleId, uint16 titleVersion) -{ - for(const auto& v : m_data) - { - if (v->entry.titleId == titleId && v->entry.version == titleVersion) - return v->entry; - } - - return {}; -} - -void wxDownloadManagerList::AddColumns() -{ - wxListItem col0; - col0.SetId(ColumnTitleId); - col0.SetText(_("Title ID")); - col0.SetWidth(120); - InsertColumn(ColumnTitleId, col0); - - wxListItem col1; - col1.SetId(ColumnName); - col1.SetText(_("Name")); - col1.SetWidth(260); - InsertColumn(ColumnName, col1); - - wxListItem col2; - col2.SetId(ColumnVersion); - col2.SetText(_("Version")); - col2.SetWidth(55); - InsertColumn(ColumnVersion, col2); - - wxListItem col3; - col3.SetId(ColumnType); - col3.SetText(_("Type")); - col3.SetWidth(60); - InsertColumn(ColumnType, col3); - - wxListItem col4; - col4.SetId(ColumnProgress); - col4.SetText(_("Progress")); - col4.SetWidth(wxLIST_AUTOSIZE_USEHEADER); - InsertColumn(ColumnProgress, col4); - - wxListItem col5; - col5.SetId(ColumnStatus); - col5.SetText(_("Status")); - col5.SetWidth(240); - InsertColumn(ColumnStatus, col5); -} - -wxString wxDownloadManagerList::OnGetItemText(long item, long column) const -{ - if (item >= GetItemCount()) - return wxEmptyString; - - const auto entry = GetTitleEntry(item); - if (entry.has_value()) - return GetTitleEntryText(entry.value(), (ItemColumn)column); - - return wxEmptyString; -} - -wxItemAttr* wxDownloadManagerList::OnGetItemAttr(long item) const -{ - const auto entry = GetTitleEntry(item); - - const wxColour bgColour = GetBackgroundColour(); - const bool isDarkTheme = wxSystemSettings::GetAppearance().IsDark(); - - if (entry.has_value()) - { - auto& entryData = entry.value(); - if (entryData.status == TitleDownloadStatus::Downloading || - entryData.status == TitleDownloadStatus::Verifying || - entryData.status == TitleDownloadStatus::Installing) - { - const wxColour kActiveColor = isDarkTheme ? wxColour(80, 40, 40) : wxColour(0xFFE0E0); - static wxListItemAttr s_active_attr; - s_active_attr.SetBackgroundColour(kActiveColor); - s_active_attr.SetTextColour(GetTextColour()); - s_active_attr.SetFont(GetFont()); - return &s_active_attr; - } - else if (entryData.status == TitleDownloadStatus::Installed && entryData.isPackage) - { - const wxColour kActiveColor = isDarkTheme ? wxColour(40, 80, 40) : wxColour(0xE0FFE0); - static wxListItemAttr s_installed_attr; - s_installed_attr.SetBackgroundColour(kActiveColor); - s_installed_attr.SetTextColour(GetTextColour()); - s_installed_attr.SetFont(GetFont()); - return &s_installed_attr; - } - else if (entryData.status == TitleDownloadStatus::Error) - { - const wxColour kActiveColor = isDarkTheme ? wxColour(40, 40, 80) : wxColour(0xCCCCF2); - static wxListItemAttr s_error_attr; - s_error_attr.SetBackgroundColour(kActiveColor); - s_error_attr.SetTextColour(GetTextColour()); - s_error_attr.SetFont(GetFont()); - return &s_error_attr; - } - } - - wxColour bgColourSecondary = wxHelper::CalculateAccentColour(bgColour); - static wxListItemAttr s_coloured_attr; - s_coloured_attr.SetBackgroundColour(bgColourSecondary); - s_coloured_attr.SetTextColour(GetTextColour()); - s_coloured_attr.SetFont(GetFont()); - return item % 2 == 0 ? nullptr : &s_coloured_attr; -} - -boost::optional wxDownloadManagerList::GetTitleEntry(long item) -{ - long counter = 0; - for (const auto& data : m_sorted_data) - { - if (!data.get().visible) - continue; - - if (item != counter++) - continue; - - return data.get().entry; - } - - return {}; -} - -boost::optional wxDownloadManagerList::GetTitleEntry(long item) const -{ - long counter = 0; - for (const auto& data : m_sorted_data) - { - if (!data.get().visible) - continue; - - if (item != counter++) - continue; - - return data.get().entry; - } - - return {}; -} - -void wxDownloadManagerList::OnClose(wxCloseEvent& event) -{ - event.Skip(); - // wait until all tasks are complete - if (m_context_worker.valid()) - m_context_worker.get(); - g_mainFrame->RequestGameListRefresh(); // todo: add games instead of fully refreshing game list if a game is downloaded -} - -void wxDownloadManagerList::OnColumnClick(wxListEvent& event) -{ - const int column = event.GetColumn(); - - SortEntries(column); - event.Skip(); -} - -void wxDownloadManagerList::RemoveItem(long item) -{ - const int item_count = GetItemCount(); - - const ItemData* ref = nullptr; - long counter = 0; - for(auto it = m_sorted_data.begin(); it != m_sorted_data.end(); ++it) - { - if (!it->get().visible) - continue; - - if (item != counter++) - continue; - - ref = &(it->get()); - m_sorted_data.erase(it); - break; - } - - // shouldn't happen - if (ref == nullptr) - return; - - for(auto it = m_data.begin(); it != m_data.end(); ++it) - { - if (ref != (*it).get()) - continue; - - m_data.erase(it); - break; - } - - SetItemCount(std::max(0, item_count - 1)); - RefreshPage(); -} - -void wxDownloadManagerList::RemoveItem(const TitleEntry& entry) -{ - const int item_count = GetItemCount(); - - const TitleEntry* ref = &entry; - for (auto it = m_sorted_data.begin(); it != m_sorted_data.end(); ++it) - { - if (ref != &it->get().entry) - continue; - - m_sorted_data.erase(it); - break; - } - - for (auto it = m_data.begin(); it != m_data.end(); ++it) - { - if (ref != &(*it).get()->entry) - continue; - - m_data.erase(it); - break; - } - - SetItemCount(std::max(0, item_count - 1)); - RefreshPage(); -} - -void wxDownloadManagerList::OnItemSelected(wxListEvent& event) -{ - event.Skip(); - m_tooltip_timer->Stop(); - const auto selection = event.GetIndex(); - - if (selection == wxNOT_FOUND) - { - m_tooltip_window->Hide(); - return; - } - -} - -enum ContextMenuEntries -{ - kContextMenuRetry = wxID_HIGHEST + 1, - kContextMenuDownload, - kContextMenuPause, - kContextMenuResume, -}; -void wxDownloadManagerList::OnContextMenu(wxContextMenuEvent& event) -{ - // still doing work - if (m_context_worker.valid() && !future_is_ready(m_context_worker)) - return; - - wxMenu menu; - menu.Bind(wxEVT_COMMAND_MENU_SELECTED, &wxDownloadManagerList::OnContextMenuSelected, this); - - const auto selection = GetFirstSelected(); - if (selection == wxNOT_FOUND) - return; - - const auto entry = GetTitleEntry(selection); - if (!entry.has_value()) - return; - - if (entry->isPaused) - menu.Append(kContextMenuResume, _("&Resume")); - else if (entry->status == TitleDownloadStatus::Error) - menu.Append(kContextMenuRetry, _("&Retry")); - else if(entry->status == TitleDownloadStatus::Available) - menu.Append(kContextMenuDownload, _("&Download")); - //else if(entry->status == TitleDownloadStatus::Downloading || entry->status == TitleDownloadStatus::Initializing) - // menu.Append(kContextMenuPause, _("&Pause")); buggy! - - PopupMenu(&menu); - - // TODO: fix tooltip position -} - -void wxDownloadManagerList::SetCurrentDownloadMgr(DownloadManager* dlMgr) -{ - std::unique_lock _l(m_dlMgrMutex); - m_dlMgr = dlMgr; -} - -bool wxDownloadManagerList::StartDownloadEntry(const TitleEntry& entry) -{ - std::unique_lock _l(m_dlMgrMutex); - if (m_dlMgr) - m_dlMgr->initiateDownload(entry.titleId, entry.version); - return true; -} - -bool wxDownloadManagerList::RetryDownloadEntry(const TitleEntry& entry) -{ - StartDownloadEntry(entry); - return true; -} - -bool wxDownloadManagerList::PauseDownloadEntry(const TitleEntry& entry) -{ - std::unique_lock _l(m_dlMgrMutex); - if (m_dlMgr) - m_dlMgr->pauseDownload(entry.titleId, entry.version); - return true; -} - -void wxDownloadManagerList::OnContextMenuSelected(wxCommandEvent& event) -{ - // still doing work - if (m_context_worker.valid() && !future_is_ready(m_context_worker)) - return; - - const auto selection = GetFirstSelected(); - if (selection == wxNOT_FOUND) - return; - - const auto entry = GetTitleEntry(selection); - if (!entry.has_value()) - return; - - switch (event.GetId()) - { - case kContextMenuDownload: - StartDownloadEntry(entry.value()); - break; - case kContextMenuRetry: - RetryDownloadEntry(entry.value()); - break; - case kContextMenuResume: - StartDownloadEntry(entry.value()); - break; - case kContextMenuPause: - PauseDownloadEntry(entry.value()); - break; - } -} - -void wxDownloadManagerList::OnTimer(wxTimerEvent& event) -{ - if(event.GetTimer().GetId() != m_tooltip_timer->GetId()) - { - event.Skip(); - return; - } - - m_tooltip_window->Show(); -} - -void wxDownloadManagerList::OnRemoveItem(wxCommandEvent& event) -{ - RemoveItem(event.GetInt()); -} - -void wxDownloadManagerList::OnRemoveEntry(wxCommandEvent& event) -{ - wxASSERT(event.GetClientData() != nullptr); - RemoveItem(*(TitleEntry*)event.GetClientData()); -} - -wxString wxDownloadManagerList::GetTitleEntryText(const TitleEntry& entry, ItemColumn column) -{ - switch (column) - { - case ColumnTitleId: - return formatWxString("{:08x}-{:08x}", (uint32) (entry.titleId >> 32), (uint32) (entry.titleId & 0xFFFFFFFF)); - case ColumnName: - return entry.name; - case ColumnType: - return GetTranslatedTitleEntryType(entry.type); - case ColumnVersion: - { - // dont show version for base game unless it is not v0 - if (entry.type == EntryType::Base && entry.version == 0) - return ""; - if (entry.type == EntryType::DLC && entry.version == 0) - return ""; - return formatWxString("v{}", entry.version); - } - case ColumnProgress: - { - if (entry.status == TitleDownloadStatus::Downloading) - { - if (entry.progress >= 1000) - return "100%"; - return formatWxString("{:.1f}%", (float) entry.progress / 10.0f); // one decimal - } - else if (entry.status == TitleDownloadStatus::Installing || entry.status == TitleDownloadStatus::Checking || entry.status == TitleDownloadStatus::Verifying) - { - return formatWxString("{0}/{1}", entry.progress, entry.progressMax); // number of processed files/content files - } - return ""; - } - case ColumnStatus: - { - if (entry.isPaused) - return _("Paused"); - else if (entry.status == TitleDownloadStatus::Available) - { - if (entry.progress == 1) - return _("Not installed (Partially downloaded)"); - if (entry.progress == 2) - return _("Update available"); - return _("Not installed"); - } - else if (entry.status == TitleDownloadStatus::Initializing) - return _("Initializing"); - else if (entry.status == TitleDownloadStatus::Checking) - return _("Checking"); - else if (entry.status == TitleDownloadStatus::Queued) - return _("Queued"); - else if (entry.status == TitleDownloadStatus::Downloading) - return _("Downloading"); - else if (entry.status == TitleDownloadStatus::Verifying) - return _("Verifying"); - else if (entry.status == TitleDownloadStatus::Installing) - return _("Installing"); - else if (entry.status == TitleDownloadStatus::Installed) - return _("Installed"); - else if (entry.status == TitleDownloadStatus::Error) - { - wxString errorStatusMsg = _("Error:"); - errorStatusMsg.append(" "); - errorStatusMsg.append(entry.errorMsg); - return errorStatusMsg; - } - else - return "Unknown status"; - } - } - - return wxEmptyString; -} - -wxString wxDownloadManagerList::GetTranslatedTitleEntryType(EntryType type) -{ - switch (type) - { - case EntryType::Base: - return _("base"); - case EntryType::Update: - return _("update"); - case EntryType::DLC: - return _("DLC"); - default: - return std::to_string(static_cast>(type)); - } -} - -void wxDownloadManagerList::AddOrUpdateTitle(TitleEntryData_t* obj) -{ - const auto& data = obj->GetData(); - // if already in list only update - auto entry = GetTitleEntry(data.titleId, data.version); - if (entry.has_value()) - { - // update item - entry.value() = data; - RefreshPage(); // more efficient way to do this? - return; - } - - m_data.emplace_back(std::make_unique(true, data)); - m_sorted_data.emplace_back(*m_data[m_data.size() - 1]); - SetItemCount(m_data.size()); - - // reapply sort - Filter2(m_filterShowTitles, m_filterShowUpdates, m_filterShowInstalled); - SortEntries(); -} - -void wxDownloadManagerList::UpdateTitleStatusDepr(TitleEntryData_t* obj, const wxString& text) -{ - const auto& data = obj->GetData(); - const auto entry = GetTitleEntry(data.titleId, data.version); - // check if already added to list - if (!entry.has_value()) - return; - - // update gamelist text - for(size_t i = 0; i < m_sorted_data.size(); ++i) - { - if (m_sorted_data[i].get().entry == data) - { - SetItem(i, ColumnStatus, text); - return; - } - } - - cemuLog_logDebug(LogType::Force, "cant update title status of {:x}", data.titleId); -} - -int wxDownloadManagerList::AddImage(const wxImage& image) const -{ - return -1; // m_image_list->Add(image.Scale(kListIconWidth, kListIconWidth, wxIMAGE_QUALITY_BICUBIC)); -} - -// return < -bool wxDownloadManagerList::SortFunc(std::span sortColumnOrder, const Type_t& v1, const Type_t& v2) -{ - cemu_assert_debug(sortColumnOrder.size() == 5); - - // visible have always priority - if (!v1.get().visible && v2.get().visible) - return false; - else if (v1.get().visible && !v2.get().visible) - return true; - - const auto& entry1 = v1.get().entry; - const auto& entry2 = v2.get().entry; - - for (size_t i = 0; i < sortColumnOrder.size(); i++) - { - int sortByColumn = sortColumnOrder[i]; - if (sortByColumn == ColumnTitleId) - { - // ensure strong ordering -> use type since only one entry should be now (should be changed if every save for every user is displayed spearately?) - if (entry1.titleId != entry2.titleId) - return entry1.titleId < entry2.titleId; - } - else if (sortByColumn == ColumnName) - { - const int tmp = entry1.name.CmpNoCase(entry2.name); - if (tmp != 0) - return tmp < 0; - } - else if (sortByColumn == ColumnType) - { - if (std::underlying_type_t(entry1.type) != std::underlying_type_t(entry2.type)) - return std::underlying_type_t(entry1.type) < std::underlying_type_t(entry2.type); - } - else if (sortByColumn == ColumnVersion) - { - if (entry1.version != entry2.version) - return entry1.version < entry2.version; - } - else if (sortByColumn == ColumnProgress) - { - if (entry1.progress != entry2.progress) - return entry1.progress < entry2.progress; - } - else - { - cemu_assert_debug(false); - return (uintptr_t)&entry1 < (uintptr_t)&entry2; - } - - } - - return false; -} - -#include - -void wxDownloadManagerList::SortEntries(int column) -{ - boost::container::small_vector s_SortColumnOrder{ ColumnName, ColumnType, ColumnVersion, ColumnTitleId, ColumnProgress }; - - bool ascending; - if (column == -1) - { - column = GetSortIndicator(); - if (column == -1) - column = ColumnName; - ascending = IsAscendingSortIndicator(); - } - else - ascending = GetUpdatedAscendingSortIndicator(column); - - // prioritize column by moving it to first position in the column sort order list - s_SortColumnOrder.erase(std::remove(s_SortColumnOrder.begin(), s_SortColumnOrder.end(), column), s_SortColumnOrder.end()); - s_SortColumnOrder.insert(s_SortColumnOrder.begin(), column); - - std::sort(m_sorted_data.begin(), m_sorted_data.end(), - [this, &s_SortColumnOrder, ascending](const Type_t& v1, const Type_t& v2) -> bool { - return ascending ? SortFunc(s_SortColumnOrder, v1, v2) : SortFunc(s_SortColumnOrder, v2, v1); - }); - - ShowSortIndicator(column, ascending); - RefreshPage(); -} - -void wxDownloadManagerList::RefreshPage() -{ - long item_count = GetItemCount(); - - if (item_count > 0) - RefreshItems(GetTopItem(), std::min(item_count - 1, GetTopItem() + GetCountPerPage() + 1)); -} - -int wxDownloadManagerList::Filter(const wxString& filter, const wxString& prefix, ItemColumn column) -{ - if (prefix.empty()) - return -1; - - if (!filter.StartsWith(prefix)) - return -1; - - int counter = 0; - const auto tmp_filter = filter.substr(prefix.size() - 1).Trim(false); - for (auto&& data : m_data) - { - if (GetTitleEntryText(data->entry, column).Upper().Contains(tmp_filter)) - { - data->visible = true; - ++counter; - } - else - data->visible = false; - } - return counter; -} - -void wxDownloadManagerList::Filter(const wxString& filter) -{ - if(filter.empty()) - { - std::for_each(m_data.begin(), m_data.end(), [](ItemDataPtr& data) { data->visible = true; }); - SetItemCount(m_data.size()); - RefreshPage(); - return; - } - - const auto filter_upper = filter.Upper().Trim(false).Trim(true); - int counter = 0; - - if (const auto result = Filter(filter_upper, "TITLEID:", ColumnTitleId) != -1) - counter = result; - else if (const auto result = Filter(filter_upper, "NAME:", ColumnName) != -1) - counter = result; - else if (const auto result = Filter(filter_upper, "TYPE:", ColumnType) != -1) - counter = result; - //else if (const auto result = Filter(filter_upper, "REGION:", ColumnRegion) != -1) - // counter = result; - else if (const auto result = Filter(filter_upper, "VERSION:", ColumnVersion) != -1) - counter = result; - else if(filter_upper == "ERROR") - { - for (auto&& data : m_data) - { - bool visible = false; - //if (data->entry.error != TitleError::None) - // visible = true; - - data->visible = visible; - if (visible) - ++counter; - } - } - else - { - for (auto&& data : m_data) - { - bool visible = false; - if (data->entry.name.Upper().Contains(filter_upper)) - visible = true; - else if (GetTitleEntryText(data->entry, ColumnTitleId).Upper().Contains(filter_upper)) - visible = true; - else if (GetTitleEntryText(data->entry, ColumnType).Upper().Contains(filter_upper)) - visible = true; - - data->visible = visible; - if (visible) - ++counter; - } - } - - SetItemCount(counter); - RefreshPage(); -} - -void wxDownloadManagerList::Filter2(bool showTitles, bool showUpdates, bool showInstalled) -{ - m_filterShowTitles = showTitles; - m_filterShowUpdates = showUpdates; - m_filterShowInstalled = showInstalled; - if (showTitles && showUpdates && showInstalled) - { - std::for_each(m_data.begin(), m_data.end(), [](ItemDataPtr& data) { data->visible = true; }); - SetItemCount(m_data.size()); - RefreshPage(); - return; - } - - size_t counter = 0; - - for (auto&& data : m_data) - { - bool visible = false; - - TitleIdParser tParser(data->entry.titleId); - bool isInstalled = data->entry.status == TitleDownloadStatus::Installed; - if (tParser.IsBaseTitleUpdate()) - { - if (showUpdates && (showInstalled || !isInstalled)) - visible = true; - } - else - { - if (showTitles && (showInstalled || !isInstalled)) - visible = true; - } - - data->visible = visible; - if (visible) - ++counter; - } - - SetItemCount(counter); - RefreshPage(); -} - -size_t wxDownloadManagerList::GetCountByType(EntryType type) const -{ - size_t result = 0; - for(const auto& data : m_data) - { - if (data->entry.type == type) - ++result; - } - return result; -} - -void wxDownloadManagerList::ClearItems() -{ - m_sorted_data.clear(); - m_data.clear(); - SetItemCount(0); - RefreshPage(); -} - -void wxDownloadManagerList::AutosizeColumns() -{ - wxAutosizeColumns(this, ColumnTitleId, ColumnMAX - 1); -} - diff --git a/src/gui/wxgui/components/wxDownloadManagerList.h b/src/gui/wxgui/components/wxDownloadManagerList.h deleted file mode 100644 index e3988e13..00000000 --- a/src/gui/wxgui/components/wxDownloadManagerList.h +++ /dev/null @@ -1,152 +0,0 @@ -#pragma once - -#include "wxgui/helpers/wxCustomData.h" -#include "config/CemuConfig.h" - -#include - -#include // std::optional doesn't support optional reference inner types yet -#include -#include - -class wxDownloadManagerList : public wxListView -{ - friend class TitleManager; -public: - wxDownloadManagerList(wxWindow* parent, wxWindowID id = wxID_ANY); - - enum ItemColumn - { - ColumnTitleId = 0, - ColumnName, - ColumnVersion, - ColumnType, - ColumnProgress, - ColumnStatus, - - ColumnMAX, - }; - - enum class EntryType - { - Base, - Update, - DLC, - }; - - enum class TitleDownloadStatus - { - None, - Available, // available for download - Error, // error state - Queued, // queued for download - Initializing, // downloading/parsing TMD - Checking, // checking for previously downloaded files - Downloading, - Verifying, // verifying downloaded files - Installing, - Installed, - // error state? - }; - - void SortEntries(int column = -1); - void RefreshPage(); - void Filter(const wxString& filter); - void Filter2(bool showTitles, bool showUpdates, bool showInstalled); - [[nodiscard]] size_t GetCountByType(EntryType type) const; - void ClearItems(); - void AutosizeColumns(); - - struct TitleEntry - { - TitleEntry(const EntryType& type, bool isPackage, uint64 titleId, uint16 version, bool isPaused) - : type(type), isPackage(isPackage), titleId(titleId), version(version), isPaused(isPaused) {} - - EntryType type; - - bool isPaused{}; - int icon = -1; - bool isPackage; - uint64 titleId; - wxString name; - uint32_t version{ 0 }; - uint32 progress; // downloading: in 1/10th of a percent, installing: number of files - uint32 progressMax{ 0 }; - CafeConsoleRegion region; - - TitleDownloadStatus status = TitleDownloadStatus::None; - std::string errorMsg; - - bool operator==(const TitleEntry& e) const - { - return type == e.type && titleId == e.titleId && version == e.version; - } - bool operator!=(const TitleEntry& e) const { return !(*this == e); } - }; - boost::optional GetSelectedTitleEntry() const; - -private: - void AddColumns(); - int Filter(const wxString& filter, const wxString& prefix, ItemColumn column); - boost::optional GetSelectedTitleEntry(); - boost::optional GetTitleEntry(uint64 titleId, uint16 titleVersion); - - class wxPanel* m_tooltip_window; - class wxStaticText* m_tooltip_text; - class wxTimer* m_tooltip_timer; - - void OnClose(wxCloseEvent& event); - void OnColumnClick(wxListEvent& event); - void OnContextMenu(wxContextMenuEvent& event); - void OnItemSelected(wxListEvent& event); - void OnContextMenuSelected(wxCommandEvent& event); - void OnTimer(class wxTimerEvent& event); - void OnRemoveItem(wxCommandEvent& event); - void OnRemoveEntry(wxCommandEvent& event); - - using TitleEntryData_t = wxCustomData; - void AddOrUpdateTitle(TitleEntryData_t* obj); - void UpdateTitleStatusDepr(TitleEntryData_t* obj, const wxString& text); - int AddImage(const wxImage& image) const; - wxString OnGetItemText(long item, long column) const override; - wxItemAttr* OnGetItemAttr(long item) const override; - [[nodiscard]] boost::optional GetTitleEntry(long item) const; - [[nodiscard]] boost::optional GetTitleEntry(long item); - //[[nodiscard]] boost::optional GetTitleEntry(const fs::path& path) const; - //[[nodiscard]] boost::optional GetTitleEntry(const fs::path& path); - - void SetCurrentDownloadMgr(class DownloadManager* dlMgr); - - //bool FixEntry(TitleEntry& entry); - //bool VerifyEntryFiles(TitleEntry& entry); - bool StartDownloadEntry(const TitleEntry& entry); - bool RetryDownloadEntry(const TitleEntry& entry); - bool PauseDownloadEntry(const TitleEntry& entry); - - void RemoveItem(long item); - void RemoveItem(const TitleEntry& entry); - - struct ItemData - { - ItemData(bool visible, const TitleEntry& entry) - : visible(visible), entry(entry) {} - - bool visible; - TitleEntry entry; - }; - using ItemDataPtr = std::unique_ptr; - std::vector m_data; - std::vector> m_sorted_data; - - bool m_filterShowTitles = true; - bool m_filterShowUpdates = true; - bool m_filterShowInstalled = true; - DownloadManager* m_dlMgr{}; - std::mutex m_dlMgrMutex; - using Type_t = std::reference_wrapper; - bool SortFunc(std::span sortColumnOrder, const Type_t& v1, const Type_t& v2); - - static wxString GetTitleEntryText(const TitleEntry& entry, ItemColumn column); - static wxString GetTranslatedTitleEntryType(EntryType entryType); - std::future m_context_worker; -}; diff --git a/src/gui/wxgui/components/wxGameList.cpp b/src/gui/wxgui/components/wxGameList.cpp deleted file mode 100644 index 68116026..00000000 --- a/src/gui/wxgui/components/wxGameList.cpp +++ /dev/null @@ -1,1696 +0,0 @@ -#include "wxgui/components/wxGameList.h" - -#include "wxgui/helpers/wxCustomData.h" -#include "wxCemuConfig.h" -#include "util/helpers/helpers.h" -#include "wxgui/GameProfileWindow.h" - -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#include "config/ActiveSettings.h" -#include "config/LaunchSettings.h" -#include "Cafe/TitleList/GameInfo.h" -#include "Cafe/TitleList/TitleList.h" - -#include "wxgui/CemuApp.h" -#include "wxgui/helpers/wxHelpers.h" -#include "wxgui/MainWindow.h" - -#include "../wxHelper.h" - -#include "Cafe/IOSU/PDM/iosu_pdm.h" // for last played and play time - -#if BOOST_OS_WINDOWS -// for shortcut creation -#include -#include -#include -#include -#include -#include -#include -#include -#endif - -// public events -wxDEFINE_EVENT(wxEVT_OPEN_SETTINGS, wxCommandEvent); -wxDEFINE_EVENT(wxEVT_GAMELIST_BEGIN_UPDATE, wxCommandEvent); -wxDEFINE_EVENT(wxEVT_GAMELIST_END_UPDATE, wxCommandEvent); -wxDEFINE_EVENT(wxEVT_OPEN_GRAPHIC_PACK, wxTitleIdEvent); - -// internal events -wxDEFINE_EVENT(wxEVT_GAME_ENTRY_ADDED_OR_REMOVED, wxTitleIdEvent); - -constexpr uint64 kDefaultEntryData = 0x1337; - -void _stripPathFilename(fs::path& path) -{ - if (path.has_extension()) - path = path.parent_path(); -} - -std::vector _getCachesPaths(const TitleId& titleId) -{ - std::vector cachePaths{ - ActiveSettings::GetCachePath(L"shaderCache/driver/vk/{:016x}.bin", titleId), - ActiveSettings::GetCachePath(L"shaderCache/precompiled/{:016x}_spirv.bin", titleId), - ActiveSettings::GetCachePath(L"shaderCache/precompiled/{:016x}_gl.bin", titleId), - ActiveSettings::GetCachePath(L"shaderCache/precompiled/{:016x}_air.bin", titleId), - ActiveSettings::GetCachePath(L"shaderCache/transferable/{:016x}_shaders.bin", titleId), - ActiveSettings::GetCachePath(L"shaderCache/transferable/{:016x}_mtlshaders.bin", titleId), - ActiveSettings::GetCachePath(L"shaderCache/transferable/{:016x}_vkpipeline.bin", titleId), - ActiveSettings::GetCachePath(L"shaderCache/transferable/{:016x}_mtlpipeline.bin", titleId)}; - - cachePaths.erase(std::remove_if(cachePaths.begin(), cachePaths.end(), - [](const fs::path& cachePath) { - std::error_code ec; - return !fs::exists(cachePath, ec); - }), - cachePaths.end()); - - return cachePaths; -} - -// Convert PNG to Apple icon image format -bool writeICNS(const fs::path& pngPath, const fs::path& icnsPath) { - // Read PNG file - std::ifstream pngFile(pngPath, std::ios::binary); - if (!pngFile) - return false; - - // Get PNG size - pngFile.seekg(0, std::ios::end); - uint32 pngSize = static_cast(pngFile.tellg()); - pngFile.seekg(0, std::ios::beg); - - // Calculate total file size (header + size + type + data) - uint32 totalSize = 8 + 8 + pngSize; - - // Create output file - std::ofstream icnsFile(icnsPath, std::ios::binary); - if (!icnsFile) - return false; - - // Write ICNS header - icnsFile.put(0x69); // 'i' - icnsFile.put(0x63); // 'c' - icnsFile.put(0x6e); // 'n' - icnsFile.put(0x73); // 's' - - // Write total file size (big endian) - icnsFile.put((totalSize >> 24) & 0xFF); - icnsFile.put((totalSize >> 16) & 0xFF); - icnsFile.put((totalSize >> 8) & 0xFF); - icnsFile.put(totalSize & 0xFF); - - // Write icon type (ic07 = 128x128 PNG) - icnsFile.put(0x69); // 'i' - icnsFile.put(0x63); // 'c' - icnsFile.put(0x30); // '0' - icnsFile.put(0x37); // '7' - - // Write PNG size (big endian) - icnsFile.put((pngSize >> 24) & 0xFF); - icnsFile.put((pngSize >> 16) & 0xFF); - icnsFile.put((pngSize >> 8) & 0xFF); - icnsFile.put(pngSize & 0xFF); - - // Copy PNG data - icnsFile << pngFile.rdbuf(); - - return true; -} - -wxGameList::wxGameList(wxWindow* parent, wxWindowID id) - : wxListView(parent, id, wxDefaultPosition, wxDefaultSize, GetStyleFlags(Style::kList)), m_style(Style::kList) -{ - const auto& config = GetWxGUIConfig(); - - char transparent_bitmap[kIconWidth * kIconWidth * 4] = {}; - memset(transparent_bitmap, wxSystemSettings::GetAppearance().IsDark() ? 0xFF : 0x00, sizeof(transparent_bitmap)); - wxBitmap blank(transparent_bitmap, kIconWidth, kIconWidth); - - m_image_list_data.Add(blank); - wxListCtrl::SetImageList(&m_image_list_data, wxIMAGE_LIST_NORMAL); - - wxBitmap::Rescale(blank, {kListIconWidth, kListIconWidth}); - m_image_list_small_data.Add(blank); - wxListCtrl::SetImageList(&m_image_list_small_data, wxIMAGE_LIST_SMALL); - - InsertColumn(ColumnHiddenName, "", wxLIST_FORMAT_LEFT, 0); - if(config.show_icon_column) - InsertColumn(ColumnIcon, _("Icon"), wxLIST_FORMAT_LEFT, GetColumnDefaultWidth(ColumnIcon)); - else - InsertColumn(ColumnIcon, _("Icon"), wxLIST_FORMAT_LEFT, 0); - InsertColumn(ColumnName, _("Game"), wxLIST_FORMAT_LEFT, config.column_width.name); - InsertColumn(ColumnVersion, _("Version"), wxLIST_FORMAT_LEFT, config.column_width.version); - InsertColumn(ColumnDLC, _("DLC"), wxLIST_FORMAT_LEFT, config.column_width.dlc); - InsertColumn(ColumnGameTime, _("You've played"), wxLIST_FORMAT_LEFT, config.column_width.game_time); - InsertColumn(ColumnGameStarted, _("Last played"), wxLIST_FORMAT_LEFT, config.column_width.game_started); - InsertColumn(ColumnRegion, _("Region"), wxLIST_FORMAT_LEFT, config.column_width.region); - InsertColumn(ColumnTitleID, _("Title ID"), wxLIST_FORMAT_LEFT, config.column_width.title_id); - - m_tooltip_window = new wxPanel(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxNO_BORDER); - auto* tooltip_sizer = new wxBoxSizer(wxVERTICAL); - tooltip_sizer->Add(new wxStaticText(m_tooltip_window, wxID_ANY, _("This game entry seems to be either an update or the base game was merged with update data\nBroken game dumps cause various problems during emulation and may even stop working at all in future Cemu versions\nPlease make sure the base game is intact and install updates only with the File->Install Update/DLC option")), 0, wxALL, 5); - m_tooltip_window->SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_INFOBK)); - m_tooltip_window->SetSizerAndFit(tooltip_sizer); - m_tooltip_window->Hide(); - - m_tooltip_timer = new wxTimer(this); - - Bind(wxEVT_CLOSE_WINDOW, &wxGameList::OnClose, this); - Bind(wxEVT_MOTION, &wxGameList::OnMouseMove, this); - Bind(wxEVT_LIST_KEY_DOWN, &wxGameList::OnKeyDown, this); - Bind(wxEVT_CONTEXT_MENU, &wxGameList::OnContextMenu, this); - Bind(wxEVT_LIST_ITEM_ACTIVATED, &wxGameList::OnItemActivated, this); - Bind(wxEVT_GAME_ENTRY_ADDED_OR_REMOVED, &wxGameList::OnGameEntryUpdatedByTitleId, this); - Bind(wxEVT_TIMER, &wxGameList::OnTimer, this); - Bind(wxEVT_LEAVE_WINDOW, &wxGameList::OnLeaveWindow, this); - - Bind(wxEVT_LIST_COL_CLICK, &wxGameList::OnColumnClick, this); - Bind(wxEVT_LIST_COL_BEGIN_DRAG, &wxGameList::OnColumnBeginResize, this); - Bind(wxEVT_LIST_COL_END_DRAG, &wxGameList::OnColumnResize, this); - Bind(wxEVT_LIST_COL_RIGHT_CLICK, &wxGameList::OnColumnRightClick, this); - Bind(wxEVT_SIZE, &wxGameList::OnGameListSize, this); - m_bulkUpdateTimer.Bind(wxEVT_TIMER, &wxGameList::OnTimerBulkAddEntriesToGameList, this); - - m_callbackIdTitleList = CafeTitleList::RegisterCallback([](CafeTitleListCallbackEvent* evt, void* ctx) { ((wxGameList*)ctx)->HandleTitleListCallback(evt); }, this); - - // start async worker (for icon loading) - m_async_worker_active = true; - m_async_worker_thread = std::thread(&wxGameList::AsyncWorkerThread, this); - - ShowSortIndicator(ColumnName); -} - -wxGameList::~wxGameList() -{ - CafeTitleList::UnregisterCallback(m_callbackIdTitleList); - - m_tooltip_timer->Stop(); - - // shut down async worker - m_async_worker_active.store(false); - m_async_task_count.increment(); - m_async_worker_thread.join(); - - // clear image cache - m_icon_cache_mtx.lock(); - m_icon_cache.clear(); - m_icon_cache_mtx.unlock(); -} - -void wxGameList::LoadConfig() -{ - const auto& config = GetWxGUIConfig(); - SetStyle((Style)config.game_list_style, false); - - if (!config.game_list_column_order.empty()) - { - wxArrayInt order; - order.reserve(ColumnCounts); - - const auto order_string = std::string_view(config.game_list_column_order).substr(1); - - const boost::char_separator sep(","); - boost::tokenizer tokens(order_string.begin(), order_string.end(), sep); - for(const auto& token : tokens) - { - order.push_back(ConvertString(token, 10)); - } - -#ifdef wxHAS_LISTCTRL_COLUMN_ORDER - if(order.GetCount() == ColumnCounts) - SetColumnsOrder(order); -#endif - } -} - -void wxGameList::OnGameListSize(wxSizeEvent &event) -{ - event.Skip(); - - // when using a sizer-based layout, do not change the size of the wxComponent in its own wxSizeEvent handler to avoid some UI issues. - int last_col_index = 0; - for(int i = GetColumnCount() - 1; i > 0; i--) - { -#ifdef wxHAS_LISTCTRL_COLUMN_ORDER - if(GetColumnWidth(GetColumnIndexFromOrder(i)) > 0) - { - last_col_index = GetColumnIndexFromOrder(i); - break; - } -#else - if(GetColumnWidth(i) > 0) - { - last_col_index = i; - break; - } -#endif - } - wxListEvent column_resize_event(wxEVT_LIST_COL_END_DRAG); - column_resize_event.SetColumn(last_col_index); - wxPostEvent(this, column_resize_event); -} - -void wxGameList::AdjustLastColumnWidth() -{ - wxWindowUpdateLocker windowlock(this); - int last_col_index = 0; - int last_col_width = GetClientSize().GetWidth(); - for (int i = 1; i < GetColumnCount(); i++) - { -#ifdef wxHAS_LISTCTRL_COLUMN_ORDER - if (GetColumnWidth(GetColumnIndexFromOrder(i)) > 0) - { - last_col_index = GetColumnIndexFromOrder(i); - last_col_width -= GetColumnWidth(last_col_index); - } -#else - if (GetColumnWidth(i) > 0) - { - last_col_index = i; - last_col_width -= GetColumnWidth(i); - } -#endif - } - last_col_width += GetColumnWidth(last_col_index); - if (last_col_width < GetColumnDefaultWidth(last_col_index)) // keep a minimum width - last_col_width = GetColumnDefaultWidth(last_col_index); - SetColumnWidth(last_col_index, last_col_width); -} - -// todo: scale all columns using a ratio instead of hardcoding exact widths -int wxGameList::GetColumnDefaultWidth(int column) -{ - switch (column) - { - case ColumnIcon: - return kListIconWidth+2; - case ColumnName: - return DefaultColumnSize::name; - case ColumnVersion: - return DefaultColumnSize::version; - case ColumnDLC: - return DefaultColumnSize::dlc; - case ColumnGameTime: - return DefaultColumnSize::game_time; - case ColumnGameStarted: - return DefaultColumnSize::game_started; - case ColumnRegion: - return DefaultColumnSize::region; - case ColumnTitleID: - return DefaultColumnSize::title_id; - default: - return 80; - } -} - -void wxGameList::SaveConfig(bool flush) -{ - auto& config = GetWxGUIConfig(); - - config.game_list_style = (int)m_style; - #ifdef wxHAS_LISTCTRL_COLUMN_ORDER - config.game_list_column_order = fmt::format("{}", GetColumnsOrder()); - #endif - - if (flush) - GetConfigHandle().Save(); -} - -bool wxGameList::IsVisible(long item) const -{ - wxRect itemRect; - GetItemRect(item, itemRect); - const wxRect clientRect = GetClientRect(); - bool visible = clientRect.Intersects(itemRect); - return visible; -} - -void wxGameList::ReloadGameEntries() -{ - wxWindowUpdateLocker windowlock(this); - DeleteAllItems(); - // tell the game list to rescan - CafeTitleList::Refresh(); - // resend notifications for all known titles by re-registering the callback - CafeTitleList::UnregisterCallback(m_callbackIdTitleList); - m_callbackIdTitleList = CafeTitleList::RegisterCallback([](CafeTitleListCallbackEvent* evt, void* ctx) { ((wxGameList*)ctx)->HandleTitleListCallback(evt); }, this); -} - -long wxGameList::FindListItemByTitleId(uint64 title_id) const -{ - for (int i = 0; i < GetItemCount(); ++i) - { - const auto id = (uint64)GetItemData(i); - if (id == title_id) - return i; - } - - return wxNOT_FOUND; -} - -// get title name with cache -std::string wxGameList::GetNameByTitleId(uint64 titleId) -{ - auto it = m_name_cache.find(titleId); - if (it != m_name_cache.end()) - return it->second; - TitleInfo titleInfo; - if (!CafeTitleList::GetFirstByTitleId(titleId, titleInfo)) - return "Unknown title"; - std::string name; - if (!GetConfig().GetGameListCustomName(titleId, name)) - name = titleInfo.GetMetaTitleName(); - m_name_cache.emplace(titleId, name); - return name; -} - -void wxGameList::SetStyle(Style style, bool save) -{ - if (m_style == style) - return; - - wxWindowUpdateLocker updatelock(this); - - m_style = style; - SetWindowStyleFlag(GetStyleFlags(m_style)); - - uint64 selected_title_id = 0; - auto selection = GetFirstSelected(); - if (selection != wxNOT_FOUND) - { - selected_title_id = (uint64)GetItemData(selection); - selection = wxNOT_FOUND; - } - - switch(style) - { - case Style::kIcons: - wxListCtrl::SetImageList(&m_image_list_data, wxIMAGE_LIST_NORMAL); - break; - case Style::kSmallIcons: - wxListCtrl::SetImageList(&m_image_list_small_data, wxIMAGE_LIST_NORMAL); - break; - case Style::kList: - wxListCtrl::SetImageList(&m_image_list_small_data, wxIMAGE_LIST_SMALL); - break; - } - - ReloadGameEntries(); - SortEntries(); - UpdateItemColors(); - - if(selection != wxNOT_FOUND) - { - Select(selection); - Focus(selection); - } - - if(save) - { - GetWxGUIConfig().game_list_style = (int)m_style; - GetConfigHandle().Save(); - } - - if (style == Style::kList) - ApplyGameListColumnWidths(); -} - -long wxGameList::GetStyleFlags(Style style) const -{ - switch (style) - { - case Style::kList: - return (wxLC_SINGLE_SEL | wxLC_VRULES | wxLC_REPORT); - case Style::kIcons: - return (wxLC_SINGLE_SEL | wxLC_ICON); - case Style::kSmallIcons: - return (wxLC_SINGLE_SEL | wxLC_ICON); - default: - wxASSERT(false); - return (wxLC_SINGLE_SEL | wxLC_REPORT); - } -} - -void wxGameList::UpdateItemColors(sint32 startIndex) -{ - wxWindowUpdateLocker lock(this); - - for (int i = startIndex; i < GetItemCount(); ++i) - { - const uint64 titleId = GetItemData(i); - if (GetConfig().IsGameListFavorite(titleId)) - { - SetItemBackgroundColour(i, kFavoriteColor); - SetItemTextColour(i, wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT)); - } - else if ((i % 2) != 0) - { - SetItemBackgroundColour(i, kPrimaryColor); - SetItemTextColour(i, wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT)); - } - else - { - SetItemBackgroundColour(i, kAlternateColor); - SetItemTextColour(i, wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT)); - } - } -} - -static inline int order_to_int(const std::weak_ordering &wo) -{ - // no easy conversion seems to exist in C++20 - if (wo == std::weak_ordering::less) - return -1; - else if (wo == std::weak_ordering::greater) - return 1; - return 0; -} - -std::weak_ordering wxGameList::SortComparator(uint64 titleId1, uint64 titleId2, SortData* sortData) -{ - auto titleLastPlayed = [](uint64_t id) - { - iosu::pdm::GameListStat playTimeStat{}; - iosu::pdm::GetStatForGamelist(id, playTimeStat); - return playTimeStat; - }; - - auto titlePlayMinutes = [](uint64_t id) - { - iosu::pdm::GameListStat playTimeStat; - if (!iosu::pdm::GetStatForGamelist(id, playTimeStat)) - return 0u; - return playTimeStat.numMinutesPlayed; - }; - - auto titleRegion = [](uint64_t id) - { - return CafeTitleList::GetGameInfo(id).GetRegion(); - }; - - switch(sortData->column) - { - default: - case ColumnName: - { - const auto isFavoriteA = GetConfig().IsGameListFavorite(titleId1); - const auto isFavoriteB = GetConfig().IsGameListFavorite(titleId2); - const auto nameA = GetNameByTitleId(titleId1); - const auto nameB = GetNameByTitleId(titleId2); - return std::tie(isFavoriteB, nameA) <=> std::tie(isFavoriteA, nameB); - } - case ColumnGameStarted: - return titleLastPlayed(titleId1).last_played <=> titleLastPlayed(titleId2).last_played; - case ColumnGameTime: - return titlePlayMinutes(titleId1) <=> titlePlayMinutes(titleId2); - case ColumnRegion: - return titleRegion(titleId1) <=> titleRegion(titleId2); - case ColumnTitleID: - return titleId1 <=> titleId2; - } - // unreachable - cemu_assert_debug(false); - return std::weak_ordering::less; -} - -int wxGameList::SortFunction(wxIntPtr item1, wxIntPtr item2, wxIntPtr sortData) -{ - const auto sort_data = (SortData*)sortData; - return sort_data->dir * order_to_int(sort_data->thisptr->SortComparator((uint64)item1, (uint64)item2, sort_data)); -} - -void wxGameList::SortEntries(int column) -{ - bool ascending; - if (column == -1) - { - column = GetSortIndicator(); - if (column == -1) - column = ColumnName; - ascending = IsAscendingSortIndicator(); - } - else - ascending = GetUpdatedAscendingSortIndicator(column); - - switch (column) - { - case ColumnName: - case ColumnGameTime: - case ColumnGameStarted: - case ColumnRegion: - case ColumnTitleID: - { - SortData data{this, ItemColumns{column}, ascending ? 1 : -1}; - SortItems(SortFunction, (wxIntPtr)&data); - ShowSortIndicator(column, ascending); - break; - } - } -} - -void wxGameList::OnKeyDown(wxListEvent& event) -{ - event.Skip(); - if (m_style != Style::kList) - return; - - const auto keycode = event.GetKeyCode(); - if (keycode == WXK_LEFT) - { - const auto item_count = GetItemCount(); - if (item_count > 0) - { - auto selection = (int)GetFirstSelected(); - if (selection == wxNOT_FOUND) - selection = 0; - else - selection = std::max(0, selection - GetCountPerPage()); - - Select(selection); - Focus(selection); - } - } - else if (keycode == WXK_RIGHT) - { - const auto item_count = GetItemCount(); - if (item_count > 0) - { - auto selection = (int)GetFirstSelected(); - if (selection == wxNOT_FOUND) - selection = 0; - - selection = std::min(item_count - 1, selection + GetCountPerPage()); - - Select(selection); - Focus(selection); - } - } -} - -enum ContextMenuEntries -{ - kContextMenuRefreshGames = wxID_HIGHEST + 1, - - kContextMenuStart, - kWikiPage, - kContextMenuFavorite, - kContextMenuEditName, - - kContextMenuGameFolder, - kContextMenuSaveFolder, - kContextMenuUpdateFolder, - kContextMenuDLCFolder, - kContextMenuEditGraphicPacks, - kContextMenuEditGameProfile, - - kContextMenuRemoveCache, - - kContextMenuStyleList, - kContextMenuStyleIcon, - kContextMenuStyleIconSmall, - - kContextMenuCreateShortcut, - - kContextMenuCopyTitleName, - kContextMenuCopyTitleId, - kContextMenuCopyTitleImage -}; - -void wxGameList::OnContextMenu(wxContextMenuEvent& event) -{ - auto& config = GetConfig(); - - wxMenu menu; - menu.Bind(wxEVT_COMMAND_MENU_SELECTED, &wxGameList::OnContextMenuSelected, this); - - const auto selection = GetFirstSelected(); - if (selection != wxNOT_FOUND) - { - const auto title_id = (uint64)GetItemData(selection); - GameInfo2 gameInfo = CafeTitleList::GetGameInfo(title_id); - if (gameInfo.IsValid()) - { - menu.SetClientData((void*)title_id); - - menu.Append(kContextMenuStart, _("&Start")); - - bool isFavorite = GetConfig().IsGameListFavorite(title_id); - std::error_code ec; - - menu.AppendSeparator(); - menu.AppendCheckItem(kContextMenuFavorite, _("&Favorite"))->Check(isFavorite); - menu.Append(kContextMenuEditName, _("&Edit name")); - - menu.AppendSeparator(); - menu.Append(kWikiPage, _("&Wiki page")); - menu.Append(kContextMenuGameFolder, _("&Game directory")); - menu.Append(kContextMenuSaveFolder, _("&Save directory"))->Enable(fs::is_directory(gameInfo.GetSaveFolder(), ec)); - menu.Append(kContextMenuUpdateFolder, _("&Update directory"))->Enable(gameInfo.HasUpdate()); - menu.Append(kContextMenuDLCFolder, _("&DLC directory"))->Enable(gameInfo.HasAOC()); - - menu.AppendSeparator(); - menu.Append(kContextMenuRemoveCache, _("&Remove shader caches"))->Enable(!_getCachesPaths(gameInfo.GetBaseTitleId()).empty()); - - menu.AppendSeparator(); - menu.Append(kContextMenuEditGraphicPacks, _("&Edit graphic packs")); - menu.Append(kContextMenuEditGameProfile, _("&Edit game profile")); - - menu.AppendSeparator(); - menu.Append(kContextMenuCreateShortcut, _("&Create shortcut")); - menu.AppendSeparator(); - menu.Append(kContextMenuCopyTitleName, _("&Copy Title Name")); - menu.Append(kContextMenuCopyTitleId, _("&Copy Title ID")); - menu.Append(kContextMenuCopyTitleImage, _("&Copy Title Image")); - menu.AppendSeparator(); - } - } - - menu.Append(kContextMenuRefreshGames, _("&Refresh game list"))->Enable(!CafeTitleList::IsScanning()); - menu.AppendSeparator(); - menu.AppendRadioItem(kContextMenuStyleList, _("Style: &List"))->Check(m_style == Style::kList); - menu.AppendRadioItem(kContextMenuStyleIcon, _("Style: &Icons"))->Check(m_style == Style::kIcons); - menu.AppendRadioItem(kContextMenuStyleIconSmall, _("Style: &Small Icons"))->Check(m_style == Style::kSmallIcons); - PopupMenu(&menu); -} - -void wxGameList::OnContextMenuSelected(wxCommandEvent& event) -{ - const auto title_id = (uint64)((wxMenu*)event.GetEventObject())->GetClientData(); - if (title_id) - { - GameInfo2 gameInfo = CafeTitleList::GetGameInfo(title_id); - if (gameInfo.IsValid()) - { - switch (event.GetId()) - { - case kContextMenuStart: - { - MainWindow::RequestLaunchGame(gameInfo.GetBase().GetPath(), wxLaunchGameEvent::INITIATED_BY::GAME_LIST); - break; - } - case kContextMenuFavorite: - GetConfig().SetGameListFavorite(title_id, !GetConfig().IsGameListFavorite(title_id)); - SortEntries(); - UpdateItemColors(); - SaveConfig(true); - break; - case kContextMenuEditName: - { - std::string customName = ""; - if (!GetConfig().GetGameListCustomName(title_id, customName)) - customName.clear(); - wxTextEntryDialog dialog(this, wxEmptyString, _("Enter a custom game title"), wxString::FromUTF8(customName)); - if(dialog.ShowModal() == wxID_OK) - { - const auto custom_name = dialog.GetValue(); - GetConfig().SetGameListCustomName(title_id, custom_name.utf8_string()); - m_name_cache.clear(); - GetConfigHandle().Save(); - // update list entry - for (int i = 0; i < GetItemCount(); ++i) - { - const auto id = (uint64)GetItemData(i); - if (id == title_id) - { - if (m_style == Style::kList) - SetItem(i, ColumnName, wxString::FromUTF8(GetNameByTitleId(title_id))); - break; - } - } - SortEntries(); - UpdateItemColors(); - } - break; - } - case kContextMenuGameFolder: - { - fs::path path(gameInfo.GetBase().GetPath()); - _stripPathFilename(path); - wxLaunchDefaultApplication(wxHelper::FromPath(path)); - break; - } - case kWikiPage: - { - // https://wiki.cemu.info/wiki/GameIDs - // WUP-P-ALZP - if (gameInfo.GetBase().ParseXmlInfo()) - { - std::string productCode = gameInfo.GetBase().GetMetaInfo()->GetProductCode(); - const auto tokens = TokenizeView(productCode, '-'); - wxASSERT(!tokens.empty()); - const std::string company_code = gameInfo.GetBase().GetMetaInfo()->GetCompanyCode(); - wxASSERT(company_code.size() >= 2); - wxLaunchDefaultBrowser(formatWxString("https://wiki.cemu.info/wiki/{}{}", *tokens.rbegin(), company_code.substr(company_code.size() - 2))); - } - break; - } - - case kContextMenuSaveFolder: - { - wxLaunchDefaultApplication(wxHelper::FromPath(gameInfo.GetSaveFolder())); - break; - } - case kContextMenuUpdateFolder: - { - fs::path path(gameInfo.GetUpdate().GetPath()); - _stripPathFilename(path); - wxLaunchDefaultApplication(wxHelper::FromPath(path)); - break; - } - case kContextMenuDLCFolder: - { - fs::path path(gameInfo.GetAOC().front().GetPath()); - _stripPathFilename(path); - wxLaunchDefaultApplication(wxHelper::FromPath(path)); - break; - } - case kContextMenuRemoveCache: - { - RemoveCache(_getCachesPaths(gameInfo.GetBaseTitleId()), gameInfo.GetTitleName()); - break; - } - case kContextMenuEditGraphicPacks: - { - wxTitleIdEvent open_event(wxEVT_OPEN_GRAPHIC_PACK, title_id); - ProcessWindowEvent(open_event); - break; - } - case kContextMenuEditGameProfile: - { - (new GameProfileWindow(GetParent(), title_id))->Show(); - break; - } - case kContextMenuCreateShortcut: - { - CreateShortcut(gameInfo); - break; - } - case kContextMenuCopyTitleName: - { - if (wxClipboard::Get()->Open()) - { - wxClipboard::Get()->SetData(new wxTextDataObject(wxString::FromUTF8(gameInfo.GetTitleName()))); - wxClipboard::Get()->Close(); - } - break; - } - case kContextMenuCopyTitleId: - { - if (wxClipboard::Get()->Open()) - { - wxClipboard::Get()->SetData(new wxTextDataObject(fmt::format("{:016x}", gameInfo.GetBaseTitleId()))); - wxClipboard::Get()->Close(); - } - break; - } - case kContextMenuCopyTitleImage: - { - if (wxClipboard::Get()->Open()) - { - int icon_large; - int icon_small; - if (!QueryIconForTitle(title_id, icon_large, icon_small)) - break; - auto icon = m_image_list_data.GetIcon(icon_large); - wxClipboard::Get()->SetData(new wxBitmapDataObject(icon)); - wxClipboard::Get()->Close(); - } - break; - } - } - } - } - - switch (event.GetId()) - { - case kContextMenuRefreshGames: - ReloadGameEntries(); - break; - case kContextMenuStyleList: - SetStyle(Style::kList); - break; - case kContextMenuStyleIcon: - SetStyle(Style::kIcons); - break; - case kContextMenuStyleIconSmall: - SetStyle(Style::kSmallIcons); - break; - } -} - -void wxGameList::OnColumnClick(wxListEvent& event) -{ - const int column = event.GetColumn(); - SortEntries(column); - UpdateItemColors(); - event.Skip(); -} - -void wxGameList::OnColumnRightClick(wxListEvent& event) -{ - enum ItemIds - { - ResetWidth = wxID_HIGHEST + 1, - ResetOrder, - - ShowIcon, - ShowName, - ShowVersion, - ShowDlc, - ShowGameTime, - ShowLastPlayed, - ShowRegion, - ShowTitleId - }; - const int column = event.GetColumn(); - wxMenu menu; - menu.SetClientObject(new wxCustomData(column)); - - menu.Append(ResetWidth, _("Reset &width")); - menu.Append(ResetOrder, _("Reset &order")) ; - - menu.AppendSeparator(); - menu.AppendCheckItem(ShowIcon, _("Show &icon"))->Check(GetColumnWidth(ColumnIcon) > 0); - menu.AppendCheckItem(ShowName, _("Show &name"))->Check(GetColumnWidth(ColumnName) > 0); - menu.AppendCheckItem(ShowVersion, _("Show &version"))->Check(GetColumnWidth(ColumnVersion) > 0); - menu.AppendCheckItem(ShowDlc, _("Show &dlc"))->Check(GetColumnWidth(ColumnDLC) > 0); - menu.AppendCheckItem(ShowGameTime, _("Show &game time"))->Check(GetColumnWidth(ColumnGameTime) > 0); - menu.AppendCheckItem(ShowLastPlayed, _("Show &last played"))->Check(GetColumnWidth(ColumnGameStarted) > 0); - menu.AppendCheckItem(ShowRegion, _("Show ®ion"))->Check(GetColumnWidth(ColumnRegion) > 0); - menu.AppendCheckItem(ShowTitleId, _("Show &title ID"))->Check(GetColumnWidth(ColumnTitleID) > 0); - - menu.Bind(wxEVT_COMMAND_MENU_SELECTED, - [this](wxCommandEvent& event) { - event.Skip(); - - const auto menu = dynamic_cast(event.GetEventObject()); - const int column = dynamic_cast*>(menu->GetClientObject())->GetData(); - auto& config = GetWxGUIConfig(); - - switch (event.GetId()) - { - case ShowIcon: - config.show_icon_column = menu->IsChecked(ShowIcon); - break; - case ShowName: - config.column_width.name = menu->IsChecked(ShowName) ? DefaultColumnSize::name : 0; - break; - case ShowVersion: - config.column_width.version = menu->IsChecked(ShowVersion) ? DefaultColumnSize::version : 0; - break; - case ShowDlc: - config.column_width.dlc = menu->IsChecked(ShowDlc) ? DefaultColumnSize::dlc : 0; - break; - case ShowGameTime: - config.column_width.game_time = menu->IsChecked(ShowGameTime) ? DefaultColumnSize::game_time : 0; - break; - case ShowLastPlayed: - config.column_width.game_started = menu->IsChecked(ShowLastPlayed) ? DefaultColumnSize::game_started : 0; - break; - case ShowRegion: - config.column_width.region = menu->IsChecked(ShowRegion) ? DefaultColumnSize::region : 0; - break; - case ShowTitleId: - config.column_width.title_id = menu->IsChecked(ShowTitleId) ? DefaultColumnSize::title_id : 0; - break; - case ResetWidth: - { - switch (column) - { - case ColumnIcon: - break; - case ColumnName: - config.column_width.name = DefaultColumnSize::name; - break; - case ColumnVersion: - config.column_width.version = DefaultColumnSize::version; - break; - case ColumnDLC: - config.column_width.dlc = DefaultColumnSize::dlc; - break; - case ColumnGameTime: - config.column_width.game_time = DefaultColumnSize::game_time; - break; - case ColumnGameStarted: - config.column_width.game_started = DefaultColumnSize::game_started; - break; - case ColumnRegion: - config.column_width.region = DefaultColumnSize::region; - break; - case ColumnTitleID: - config.column_width.title_id = DefaultColumnSize::title_id; - default: - return; - } - - break; - } - case ResetOrder: - { - config.game_list_column_order.clear(); - wxArrayInt order(ColumnCounts); - std::iota(order.begin(), order.end(), 0); - #ifdef wxHAS_LISTCTRL_COLUMN_ORDER - SetColumnsOrder(order); - #endif - //ApplyGameListColumnWidths(); - //Refresh(); - //return; - } - } - - GetConfigHandle().Save(); - ApplyGameListColumnWidths(); - }); - - PopupMenu(&menu); - event.Skip(); -} - -void wxGameList::ApplyGameListColumnWidths() -{ - const auto& config = GetWxGUIConfig(); - wxWindowUpdateLocker lock(this); - if(config.show_icon_column) - SetColumnWidth(ColumnIcon, kListIconWidth+2); - else - SetColumnWidth(ColumnIcon, 0); - SetColumnWidth(ColumnName, config.column_width.name); - SetColumnWidth(ColumnVersion, config.column_width.version); - SetColumnWidth(ColumnDLC, config.column_width.dlc); - SetColumnWidth(ColumnGameTime, config.column_width.game_time); - SetColumnWidth(ColumnGameStarted, config.column_width.game_started); - SetColumnWidth(ColumnRegion, config.column_width.region); - SetColumnWidth(ColumnTitleID, config.column_width.title_id); - - AdjustLastColumnWidth(); -} - -void wxGameList::OnColumnBeginResize(wxListEvent& event) -{ - const int column = event.GetColumn(); - const int width = GetColumnWidth(column); - int last_col_index = 0; - for(int i = GetColumnCount() - 1; i > 0; i--) - { -#ifdef wxHAS_LISTCTRL_COLUMN_ORDER - if(GetColumnWidth(GetColumnIndexFromOrder(i)) > 0) - { - last_col_index = GetColumnIndexFromOrder(i); - break; - } -#else - if(GetColumnWidth(i) > 0) - { - last_col_index = i; - break; - } -#endif - } - if (width == 0 || column == ColumnIcon || column == last_col_index) // dont resize hidden name, icon, and last column - event.Veto(); - else - event.Skip(); -} - -void wxGameList::OnColumnResize(wxListEvent& event) -{ - event.Skip(); - - if(m_style != Style::kList) - return; - - const int column = event.GetColumn(); - const int width = GetColumnWidth(column); - - auto& config = GetWxGUIConfig(); - switch (column) - { - case ColumnName: - config.column_width.name = width; - break; - case ColumnVersion: - config.column_width.version = width; - break; - case ColumnDLC: - config.column_width.dlc = width; - break; - case ColumnGameTime: - config.column_width.game_time = width; - break; - case ColumnGameStarted: - config.column_width.game_started = width; - break; - case ColumnRegion: - config.column_width.region = width; - break; - default: - break; - } - - GetConfigHandle().Save(); - AdjustLastColumnWidth(); -} - -void wxGameList::OnClose(wxCloseEvent& event) -{ - event.Skip(); - m_exit = true; -} - -int wxGameList::FindInsertPosition(TitleId titleId, bool& entryAlreadyExists) -{ - entryAlreadyExists = false; - SortData data{this, ItemColumns(GetSortIndicator()), IsAscendingSortIndicator() ? 1 : -1}; - const auto itemCount = GetItemCount(); - - if (itemCount == 0) - return 0; - sint32 low = 0; - sint32 high = itemCount; - while (low < high) - { - sint32 mid = low + (high - low) / 2; - auto cmp = data.dir * order_to_int(SortComparator(titleId, (uint64)GetItemData(mid), &data)); - if (cmp <= 0) - { - if (cmp == 0) - { - entryAlreadyExists = true; - return mid; - } - high = mid; - } - else - { - low = mid + 1; - } - } - return low; -} - -void wxGameList::OnTimerBulkAddEntriesToGameList(wxTimerEvent& event) -{ - std::vector titleIdsToUpdate; - std::swap(titleIdsToUpdate, m_bulkTitlesToAdd); - - wxWindowUpdateLocker lock(this); - bool hasAnyNewEntry = false; - for (auto& titleId : titleIdsToUpdate) - { - GameInfo2 gameInfo = CafeTitleList::GetGameInfo(titleId); - if (!gameInfo.IsValid() || gameInfo.IsSystemDataTitle()) - { - // entry no longer exists or is not a valid game - // we dont need to remove list entries here because all delete operations should trigger a full list refresh - continue; - } - TitleId baseTitleId = gameInfo.GetBaseTitleId(); - bool isNewEntry = false; - - int icon = -1; /* 0 is the default empty icon */ - int icon_small = -1; /* 0 is the default empty icon */ - QueryIconForTitle(baseTitleId, icon, icon_small); - - bool entryAlreadyExists = false; - auto index = FindInsertPosition(baseTitleId, entryAlreadyExists); - if(!entryAlreadyExists) - { - // entry doesn't exist - index = InsertItem(index, wxString::FromUTF8(GetNameByTitleId(baseTitleId))); - SetItemPtrData(index, baseTitleId); - isNewEntry = true; - hasAnyNewEntry = true; - } - - if (m_style == Style::kList) - { - SetItemColumnImage(index, ColumnIcon, icon_small); - - SetItem(index, ColumnName, wxString::FromUTF8(GetNameByTitleId(baseTitleId))); - - SetItem(index, ColumnVersion, fmt::format("{}", gameInfo.GetVersion())); - - if(gameInfo.HasAOC()) - SetItem(index, ColumnDLC, fmt::format("{}", gameInfo.GetAOCVersion())); - else - SetItem(index, ColumnDLC, wxString()); - - if (isNewEntry) - { - iosu::pdm::GameListStat playTimeStat; - if (iosu::pdm::GetStatForGamelist(baseTitleId, playTimeStat)) - { - // time played - uint32 minutesPlayed = playTimeStat.numMinutesPlayed; - if (minutesPlayed == 0) - SetItem(index, ColumnGameTime, wxEmptyString); - else if (minutesPlayed < 60) - SetItem(index, ColumnGameTime, formatWxString(wxPLURAL("{} minute", "{} minutes", minutesPlayed), minutesPlayed)); - else - { - uint32 hours = minutesPlayed / 60; - uint32 minutes = minutesPlayed % 60; - wxString hoursText = formatWxString(wxPLURAL("{} hour", "{} hours", hours), hours); - wxString minutesText = formatWxString(wxPLURAL("{} minute", "{} minutes", minutes), minutes); - SetItem(index, ColumnGameTime, hoursText + " " + minutesText); - } - - // last played - if (playTimeStat.last_played.year != 0) - { - const wxDateTime tmp((wxDateTime::wxDateTime_t)playTimeStat.last_played.day, (wxDateTime::Month)playTimeStat.last_played.month, (wxDateTime::wxDateTime_t)playTimeStat.last_played.year, 0, 0, 0, 0); - SetItem(index, ColumnGameStarted, tmp.FormatDate()); - } - else - SetItem(index, ColumnGameStarted, _("never")); - } - else - { - SetItem(index, ColumnGameTime, wxEmptyString); - SetItem(index, ColumnGameStarted, _("never")); - } - } - const auto region_text = fmt::format("{}", gameInfo.GetRegion()); - SetItem(index, ColumnRegion, wxGetTranslation(region_text)); - SetItem(index, ColumnTitleID, fmt::format("{:016x}", baseTitleId)); - } - else if (m_style == Style::kIcons) - { - SetItemImage(index, icon); - } - else if (m_style == Style::kSmallIcons) - { - SetItemImage(index, icon_small); - } - } - if (hasAnyNewEntry) - UpdateItemColors(); -} - -void wxGameList::OnGameEntryUpdatedByTitleId(wxTitleIdEvent& event) -{ - if (m_bulkTitlesToAdd.size() < 100) - m_bulkUpdateTimer.StartOnce(100); // if timer is started already this will delay it - const auto titleId = event.GetTitleId(); - m_bulkTitlesToAdd.emplace_back(titleId); -} - -void wxGameList::OnItemActivated(wxListEvent& event) -{ - event.Skip(); - - const auto selection = event.GetIndex(); - if (selection == wxNOT_FOUND) - return; - - const auto item_data = (uint64)GetItemData(selection); - if(item_data == kDefaultEntryData) - { - const wxCommandEvent open_settings_event(wxEVT_OPEN_SETTINGS); - wxPostEvent(this, open_settings_event); - return; - } - - TitleInfo titleInfo; - if (!CafeTitleList::GetFirstByTitleId(item_data, titleInfo)) - return; - if (!titleInfo.IsValid()) - return; - - MainWindow::RequestLaunchGame(titleInfo.GetPath(), wxLaunchGameEvent::INITIATED_BY::GAME_LIST); -} - -void wxGameList::OnTimer(wxTimerEvent& event) -{ - const auto& obj = event.GetTimer().GetId(); - if(obj == m_tooltip_timer->GetId()) - { - m_tooltip_window->Hide(); - - auto flag = wxLIST_HITTEST_ONITEM; - const auto item = this->HitTest(m_mouse_position, flag); - if(item != wxNOT_FOUND ) - { - //const auto title_id = (uint64_t)GetItemData(item); - //auto entry = GetGameEntry(title_id); - //if (entry && entry->is_update) - //{ - // m_tooltip_window->SetPosition(wxPoint(m_mouse_position.x + 15, m_mouse_position.y + 15)); - // m_tooltip_window->SendSizeEvent(); - // m_tooltip_window->Show(); - //} - } - } - -} - -void wxGameList::OnMouseMove(wxMouseEvent& event) -{ - m_tooltip_timer->Stop(); - m_tooltip_timer->StartOnce(250); - m_mouse_position = event.GetPosition(); -} - -void wxGameList::OnLeaveWindow(wxMouseEvent& event) -{ - m_tooltip_timer->Stop(); - m_tooltip_window->Hide(); -} - -void wxGameList::HandleTitleListCallback(CafeTitleListCallbackEvent* evt) -{ - if (evt->eventType == CafeTitleListCallbackEvent::TYPE::TITLE_DISCOVERED || - evt->eventType == CafeTitleListCallbackEvent::TYPE::TITLE_REMOVED) - { - wxQueueEvent(this, new wxTitleIdEvent(wxEVT_GAME_ENTRY_ADDED_OR_REMOVED, evt->titleInfo->GetAppTitleId())); - } -} - -void wxGameList::RemoveCache(const std::vector& cachePaths, const std::string& titleName) -{ - wxMessageDialog dialog(this, formatWxString(_("Remove the shader caches for {}?"), titleName), _("Remove shader caches"), wxCENTRE | wxYES_NO | wxICON_EXCLAMATION); - dialog.SetYesNoLabels(_("Yes"), _("No")); - - const auto dialogResult = dialog.ShowModal(); - if (dialogResult != wxID_YES) - return; - std::vector errs; - for (const fs::path& cachePath : cachePaths) - { - if (std::error_code ec; !fs::remove(cachePath, ec)) - errs.emplace_back(fmt::format("{} : {}", cachePath.string(), ec.message())); - } - if (errs.empty()) - wxMessageDialog(this, _("The shader caches were removed!"), _("Shader caches removed"), wxCENTRE | wxOK | wxICON_INFORMATION).ShowModal(); - else - wxMessageDialog(this, formatWxString(_("Failed to remove the shader caches:\n{}"), fmt::join(errs, "\n")), _("Error"), wxCENTRE | wxOK | wxICON_ERROR).ShowModal(); -} - -void wxGameList::AsyncWorkerThread() -{ - SetThreadName("GameListWorker"); - while (m_async_worker_active) - { - m_async_task_count.decrementWithWait(); - // get next titleId to load (if any) - m_async_worker_mutex.lock(); - bool hasJob = !m_icon_load_queue.empty(); - TitleId titleId = 0; - if (hasJob) - { - titleId = m_icon_load_queue.front(); - m_icon_load_queue.erase(m_icon_load_queue.begin()); - } - m_async_worker_mutex.unlock(); - if (!hasJob) - continue; - if(m_icon_loaded.find(titleId) != m_icon_loaded.end()) - continue; - m_icon_loaded.emplace(titleId); - // load and process icon - TitleInfo titleInfo; - if( !CafeTitleList::GetFirstByTitleId(titleId, titleInfo) ) - continue; - std::string tempMountPath = TitleInfo::GetUniqueTempMountingPath(); - if(!titleInfo.Mount(tempMountPath, "", FSC_PRIORITY_BASE)) - continue; - auto tgaData = fsc_extractFile((tempMountPath + "/meta/iconTex.tga").c_str()); - // try iconTex.tga.gz - if (!tgaData) - { - tgaData = fsc_extractFile((tempMountPath + "/meta/iconTex.tga.gz").c_str()); - if (tgaData) - { - auto decompressed = zlibDecompress(*tgaData, 70*1024); - std::swap(tgaData, decompressed); - } - } - bool iconSuccessfullyLoaded = false; - if (tgaData && tgaData->size() > 16) - { - wxMemoryInputStream tmp_stream(tgaData->data(), tgaData->size()); - const wxImage image(tmp_stream); - // todo - is wxImageList thread safe? - int icon = m_image_list_data.Add(image.Scale(kIconWidth, kIconWidth, wxIMAGE_QUALITY_BICUBIC)); - int icon_small = m_image_list_small_data.Add(image.Scale(kListIconWidth, kListIconWidth, wxIMAGE_QUALITY_BICUBIC)); - // store in cache - m_icon_cache_mtx.lock(); - m_icon_cache.try_emplace(titleId, icon, icon_small); - m_icon_cache_mtx.unlock(); - iconSuccessfullyLoaded = true; - } - else - { - cemuLog_log(LogType::Force, "Failed to load icon for title {:016x}", titleId); - } - titleInfo.Unmount(tempMountPath); - // notify UI about loaded icon - if(iconSuccessfullyLoaded) - wxQueueEvent(this, new wxTitleIdEvent(wxEVT_GAME_ENTRY_ADDED_OR_REMOVED, titleId)); - } -} - -void wxGameList::RequestLoadIconAsync(TitleId titleId) -{ - m_async_worker_mutex.lock(); - m_icon_load_queue.push_back(titleId); - m_async_worker_mutex.unlock(); - m_async_task_count.increment(); -} - -// returns icons if cached, otherwise an async request to load them is made -bool wxGameList::QueryIconForTitle(TitleId titleId, int& icon, int& iconSmall) -{ - m_icon_cache_mtx.lock(); - auto it = m_icon_cache.find(titleId); - if (it == m_icon_cache.end()) - { - m_icon_cache_mtx.unlock(); - RequestLoadIconAsync(titleId); - return false; - } - icon = it->second.first; - iconSmall = it->second.second; - m_icon_cache_mtx.unlock(); - return true; -} - -void wxGameList::DeleteCachedStrings() -{ - m_name_cache.clear(); -} - -#if BOOST_OS_LINUX || BOOST_OS_BSD -void wxGameList::CreateShortcut(GameInfo2& gameInfo) -{ - const auto titleId = gameInfo.GetBaseTitleId(); - const auto titleName = wxString::FromUTF8(gameInfo.GetTitleName()); - auto exePath = ActiveSettings::GetExecutablePath(); - const char* flatpakId = getenv("FLATPAK_ID"); - - const wxString desktopEntryName = wxString::Format("%s.desktop", titleName); - wxFileDialog entryDialog(this, _("Choose desktop entry location"), "~/.local/share/applications", desktopEntryName, - "Desktop file (*.desktop)|*.desktop", wxFD_SAVE | wxFD_CHANGE_DIR | wxFD_OVERWRITE_PROMPT); - const auto result = entryDialog.ShowModal(); - if (result == wxID_CANCEL) - return; - const auto output_path = entryDialog.GetPath(); - - std::optional iconPath; - // Obtain and convert icon - [&]() - { - int iconIdx, smallIconIdx; - - if (!QueryIconForTitle(titleId, iconIdx, smallIconIdx)) - { - cemuLog_log(LogType::Force, "Icon hasn't loaded"); - return; - } - const fs::path outIconDir = ActiveSettings::GetUserDataPath("icons"); - - if (!fs::exists(outIconDir) && !fs::create_directories(outIconDir)) - { - cemuLog_log(LogType::Force, "Failed to create icon directory"); - return; - } - - iconPath = outIconDir / fmt::format("{:016x}.png", gameInfo.GetBaseTitleId()); - wxFileOutputStream pngFileStream(_pathToUtf8(iconPath.value())); - - const auto icon = m_image_list_data.GetIcon(iconIdx); - wxBitmap bitmap{icon}; - wxImage image = bitmap.ConvertToImage(); - wxPNGHandler pngHandler; - if (!pngHandler.SaveFile(&image, pngFileStream, false)) - { - iconPath = std::nullopt; - cemuLog_log(LogType::Force, "Icon failed to save"); - } - }(); - - std::string desktopExecEntry = flatpakId ? fmt::format("/usr/bin/flatpak run {0} --title-id {1:016x}", flatpakId, titleId) - : fmt::format("{0:?} --title-id {1:016x}", _pathToUtf8(exePath), titleId); - - // 'Icon' accepts spaces in file name, does not accept quoted file paths - // 'Exec' does not accept non-escaped spaces, and can accept quoted file paths - auto desktopEntryString = fmt::format( - "[Desktop Entry]\n" - "Name={0}\n" - "Comment=Play {0} on Cemu\n" - "Exec={1}\n" - "Icon={2}\n" - "Terminal=false\n" - "Type=Application\n" - "Categories=Game;\n", - titleName.utf8_string(), - desktopExecEntry, - _pathToUtf8(iconPath.value_or(""))); - - if (flatpakId) - desktopEntryString += fmt::format("X-Flatpak={}\n", flatpakId); - - std::ofstream outputStream(output_path.utf8_string()); - if (!outputStream.good()) - { - auto errorMsg = formatWxString(_("Failed to save desktop entry to {}"), output_path.utf8_string()); - wxMessageBox(errorMsg, _("Error"), wxOK | wxCENTRE | wxICON_ERROR); - return; - } - outputStream << desktopEntryString; -} -#elif BOOST_OS_MACOS -void wxGameList::CreateShortcut(GameInfo2& gameInfo) -{ - const auto titleId = gameInfo.GetBaseTitleId(); - const auto titleName = wxString::FromUTF8(gameInfo.GetTitleName()); - auto exePath = ActiveSettings::GetExecutablePath(); - - const wxString appName = wxString::Format("%s.app", titleName); - wxFileDialog entryDialog(this, _("Choose shortcut location"), "~/Applications", appName, - "Application (*.app)|*.app", wxFD_SAVE | wxFD_CHANGE_DIR | wxFD_OVERWRITE_PROMPT); - const auto result = entryDialog.ShowModal(); - if (result == wxID_CANCEL) - return; - const auto output_path = entryDialog.GetPath(); - // Create .app folder - const fs::path appPath = output_path.utf8_string(); - if (!fs::create_directories(appPath)) - { - cemuLog_log(LogType::Force, "Failed to create app directory"); - return; - } - const fs::path infoPath = appPath / "Contents/Info.plist"; - const fs::path scriptPath = appPath / "Contents/MacOS/run.sh"; - const fs::path icnsPath = appPath / "Contents/Resources/shortcut.icns"; - if (!(fs::create_directories(scriptPath.parent_path()) && fs::create_directories(icnsPath.parent_path()))) - { - cemuLog_log(LogType::Force, "Failed to create app shortcut directories"); - return; - } - - std::optional iconPath; - // Obtain and convert icon - [&]() - { - int iconIdx, smallIconIdx; - - if (!QueryIconForTitle(titleId, iconIdx, smallIconIdx)) - { - cemuLog_log(LogType::Force, "Icon hasn't loaded"); - return; - } - const fs::path outIconDir = fs::temp_directory_path(); - - if (!fs::exists(outIconDir) && !fs::create_directories(outIconDir)) - { - cemuLog_log(LogType::Force, "Failed to create icon directory"); - return; - } - - iconPath = outIconDir / fmt::format("{:016x}.png", gameInfo.GetBaseTitleId()); - wxFileOutputStream pngFileStream(_pathToUtf8(iconPath.value())); - - const auto icon = m_image_list_data.GetIcon(iconIdx); - wxBitmap bitmap{icon}; - wxImage image = bitmap.ConvertToImage(); - wxPNGHandler pngHandler; - if (!pngHandler.SaveFile(&image, pngFileStream, false)) - { - iconPath = std::nullopt; - cemuLog_log(LogType::Force, "Icon failed to save"); - } - }(); - - std::string runCommand = fmt::format("#!/bin/zsh\n\n{0:?} --title-id {1:016x}", _pathToUtf8(exePath), titleId); - const std::string infoPlist = fmt::format( - "\n" - "\n" - "\n" - "\n" - " CFBundleDisplayName\n" - " {0}\n" - " CFBundleExecutable\n" - " run.sh\n" - " CFBundleIconFile\n" - " shortcut.icns\n" - " CFBundleName\n" - " {0}\n" - " CFBundlePackageType\n" - " APPL\n" - " CFBundleSignature\n" - " \?\?\?\?\n" - " LSApplicationCategoryType\n" - " public.app-category.games\n" - " CFBundleShortVersionString\n" - " {1}\n" - " CFBundleVersion\n" - " {1}\n" - "\n" - "\n", - gameInfo.GetTitleName(), - std::to_string(gameInfo.GetVersion()) - ); - // write Info.plist to infoPath - std::ofstream infoStream(infoPath); - std::ofstream scriptStream(scriptPath); - if (!infoStream.good() || !scriptStream.good()) - { - auto errorMsg = formatWxString(_("Failed to save app shortcut to {}"), output_path.utf8_string()); - wxMessageBox(errorMsg, _("Error"), wxOK | wxCENTRE | wxICON_ERROR); - return; - } - infoStream << infoPlist; - scriptStream << runCommand; - scriptStream.close(); - - // Set execute permissions for script - fs::permissions( - scriptPath, - fs::perms::owner_exec | fs::perms::group_exec | fs::perms::others_exec, - fs::perm_options::add - ); - - // Return if iconPath is empty - if (!iconPath) - { - cemuLog_log(LogType::Force, "Icon not found"); - return; - } - - // Convert icon to icns, only works for 128x128 PNG - // Alternatively, can run the command "sips -s format icns {iconPath} --out '{icnsPath}'" - // using std::system() to handle images of any size - if (!writeICNS(*iconPath, icnsPath)) - { - cemuLog_log(LogType::Force, "Failed to convert icon to icns"); - return; - } - - // Remove temp file - fs::remove(*iconPath); -} -#elif BOOST_OS_WINDOWS -void wxGameList::CreateShortcut(GameInfo2& gameInfo) -{ - const auto titleId = gameInfo.GetBaseTitleId(); - const auto titleName = wxString::FromUTF8(gameInfo.GetTitleName()); - auto exePath = ActiveSettings::GetExecutablePath(); - - // Get '%APPDATA%\Microsoft\Windows\Start Menu\Programs' path - PWSTR userShortcutFolder; - SHGetKnownFolderPath(FOLDERID_Programs, 0, NULL, &userShortcutFolder); - const wxString shortcutName = wxString::Format("%s.lnk", titleName); - wxFileDialog shortcutDialog(this, _("Choose shortcut location"), userShortcutFolder, shortcutName, - "Shortcut (*.lnk)|*.lnk", wxFD_SAVE | wxFD_CHANGE_DIR | wxFD_OVERWRITE_PROMPT); - - CoTaskMemFree(userShortcutFolder); - - const auto result = shortcutDialog.ShowModal(); - if (result == wxID_CANCEL) - return; - const auto outputPath = shortcutDialog.GetPath(); - - std::optional icon_path = std::nullopt; - { - int iconIdx; - int smallIconIdx; - if (!QueryIconForTitle(titleId, iconIdx, smallIconIdx)) - { - cemuLog_log(LogType::Force, "Icon hasn't loaded"); - return; - } - const auto icon = m_image_list_data.GetIcon(iconIdx); - const auto folder = ActiveSettings::GetUserDataPath("icons"); - if (!fs::exists(folder) && !fs::create_directories(folder)) - { - cemuLog_log(LogType::Force, "Failed to create icon directory"); - return; - } - wxBitmap bitmap{icon}; - - icon_path = folder / fmt::format("{:016x}.ico", titleId); - auto stream = wxFileOutputStream(icon_path->wstring()); - auto image = bitmap.ConvertToImage(); - wxICOHandler icohandler{}; - if (!icohandler.SaveFile(&image, stream, false)) - { - icon_path = std::nullopt; - cemuLog_log(LogType::Force, "Icon failed to save"); - } - } - - Microsoft::WRL::ComPtr shellLink; - HRESULT hres = CoCreateInstance(__uuidof(ShellLink), nullptr, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&shellLink)); - if (SUCCEEDED(hres)) - { - const auto description = wxString::Format("Play %s on Cemu", titleName); - const auto args = wxString::Format("-t %016llx", titleId); - - shellLink->SetPath(exePath.wstring().c_str()); - shellLink->SetDescription(description.wc_str()); - shellLink->SetArguments(args.wc_str()); - shellLink->SetWorkingDirectory(exePath.parent_path().wstring().c_str()); - - if (icon_path) - shellLink->SetIconLocation(icon_path->wstring().c_str(), 0); - else - shellLink->SetIconLocation(exePath.wstring().c_str(), 0); - - Microsoft::WRL::ComPtr shellLinkFile; - // save the shortcut - hres = shellLink.As(&shellLinkFile); - if (SUCCEEDED(hres)) - { - hres = shellLinkFile->Save(outputPath.wc_str(), TRUE); - } - } - if (FAILED(hres)) - { - auto errorMsg = formatWxString(_("Failed to save shortcut to {}"), outputPath); - wxMessageBox(errorMsg, _("Error"), wxOK | wxCENTRE | wxICON_ERROR); - } -} -#endif diff --git a/src/gui/wxgui/components/wxGameList.h b/src/gui/wxgui/components/wxGameList.h deleted file mode 100644 index 388a9df6..00000000 --- a/src/gui/wxgui/components/wxGameList.h +++ /dev/null @@ -1,166 +0,0 @@ -#pragma once - -#include "config/CemuConfig.h" -#include "Cafe/TitleList/TitleId.h" - -#include -#include -#include -#include -#include - -#include "wxHelper.h" -#include "util/helpers/Semaphore.h" - -class wxTitleIdEvent : public wxCommandEvent -{ -public: - wxTitleIdEvent(int type, uint64 title_id) - : wxCommandEvent(type), m_title_id(title_id) {} - - [[nodiscard]] uint64 GetTitleId() const { return m_title_id; } - -private: - uint64 m_title_id; -}; - -wxDECLARE_EVENT(wxEVT_OPEN_SETTINGS, wxCommandEvent); -wxDECLARE_EVENT(wxEVT_OPEN_GRAPHIC_PACK, wxTitleIdEvent); -wxDECLARE_EVENT(wxEVT_GAMELIST_BEGIN_UPDATE, wxCommandEvent); -wxDECLARE_EVENT(wxEVT_GAMELIST_END_UPDATE, wxCommandEvent); - -class wxGameList : public wxListView -{ - friend class MainWindow; -public: - enum class Style - { - kList, - kIcons, - kSmallIcons - }; - - wxGameList(wxWindow* parent, wxWindowID id = wxID_ANY); - ~wxGameList(); - - void SetStyle(Style style, bool save = true); - - void LoadConfig(); - void SaveConfig(bool flush = false); - bool IsVisible(long item) const; // only available in wxwidgets 3.1.3 - - void ReloadGameEntries(); - void DeleteCachedStrings(); - - void CreateShortcut(GameInfo2& gameInfo); - - long FindListItemByTitleId(uint64 title_id) const; - - void OnClose(wxCloseEvent& event); - -private: - std::atomic_bool m_exit = false; - Style m_style; - long GetStyleFlags(Style style) const; - - const wxColour kUpdateColor{ wxSystemSettings::SelectLightDark(wxColour(195, 57, 57), wxColour(84, 29, 29)) }; - const wxColour kFavoriteColor{ wxSystemSettings::SelectLightDark(wxColour(253, 246, 211), wxColour(82, 84, 48)) }; - const wxColour kPrimaryColor = GetBackgroundColour(); - const wxColour kAlternateColor = wxHelper::CalculateAccentColour(kPrimaryColor); - void UpdateItemColors(sint32 startIndex = 0); - - enum ItemColumns : int - { - ColumnHiddenName = 0, - ColumnIcon, - ColumnName, - ColumnVersion, - ColumnDLC, - ColumnGameTime, - ColumnGameStarted, - ColumnRegion, - ColumnTitleID, - //ColumnFavorite, - ColumnCounts, - }; - - void SortEntries(int column = -1); - struct SortData - { - wxGameList* thisptr; - ItemColumns column; - int dir; - }; - - int FindInsertPosition(TitleId titleId, bool& entryAlreadyExists); - std::weak_ordering SortComparator(uint64 titleId1, uint64 titleId2, SortData* sortData); - static int SortFunction(wxIntPtr item1, wxIntPtr item2, wxIntPtr sortData); - - wxTimer* m_tooltip_timer; - wxToolTip* m_tool_tip; - wxPanel* m_tooltip_window; - wxPoint m_mouse_position; - - std::mutex m_async_worker_mutex; - std::thread m_async_worker_thread; - CounterSemaphore m_async_task_count; - std::atomic_bool m_async_worker_active{false}; - - std::vector m_icon_load_queue; - - uint64 m_callbackIdTitleList; - - std::string GetNameByTitleId(uint64 titleId); - - void HandleTitleListCallback(struct CafeTitleListCallbackEvent* evt); - - void RemoveCache(const std::vector& cachePath, const std::string& titleName); - - void AsyncWorkerThread(); - void RequestLoadIconAsync(TitleId titleId); - bool QueryIconForTitle(TitleId titleId, int& icon, int& iconSmall); - - inline static constexpr int kListIconWidth = 64; - inline static constexpr int kIconWidth = 128; - wxImageList m_image_list_data = wxImageList(kIconWidth, kIconWidth, false, 1); - wxImageList m_image_list_small_data = wxImageList(kListIconWidth, kListIconWidth, false, 1); - - std::mutex m_icon_cache_mtx; - std::set m_icon_loaded; - std::map> m_icon_cache; // pair contains icon and small icon - - std::map m_name_cache; - - // bulk update handling - std::vector m_bulkTitlesToAdd; - wxTimer m_bulkUpdateTimer; - - // list mode - void CreateListColumns(); - - void OnColumnClick(wxListEvent& event); - void OnColumnRightClick(wxListEvent& event); - void ApplyGameListColumnWidths(); - void OnColumnBeginResize(wxListEvent& event); - void OnColumnResize(wxListEvent& event); - void OnColumnDrag(wxListEvent& event); - - // generic events - void OnKeyDown(wxListEvent& event); - void OnContextMenu(wxContextMenuEvent& event); - void OnContextMenuSelected(wxCommandEvent& event); - void OnGameEntryUpdatedByTitleId(wxTitleIdEvent& event); - void OnItemActivated(wxListEvent& event); - void OnTimer(wxTimerEvent& event); - void OnMouseMove(wxMouseEvent& event); - void OnLeaveWindow(wxMouseEvent& event); - - void OnGameListSize(wxSizeEvent& event); - void AdjustLastColumnWidth(); - int GetColumnDefaultWidth(int column); - - void OnTimerBulkAddEntriesToGameList(wxTimerEvent& event); - - static inline std::once_flag s_launch_file_once; -}; - diff --git a/src/gui/wxgui/components/wxInputDraw.cpp b/src/gui/wxgui/components/wxInputDraw.cpp deleted file mode 100644 index d12237c8..00000000 --- a/src/gui/wxgui/components/wxInputDraw.cpp +++ /dev/null @@ -1,121 +0,0 @@ -#include "wxgui/components/wxInputDraw.h" - -#include -#include - -wxInputDraw::wxInputDraw(wxWindow* parent, wxWindowID id, const wxPoint& pos, const wxSize& size) - : wxWindow(parent, id, pos, size, 0, wxPanelNameStr) -{ - SetBackgroundStyle(wxBG_STYLE_PAINT); - Bind(wxEVT_PAINT, &wxInputDraw::OnPaintEvent, this); -} - -wxInputDraw::~wxInputDraw() { Unbind(wxEVT_PAINT, &wxInputDraw::OnPaintEvent, this); } - -void wxInputDraw::OnPaintEvent(wxPaintEvent& event) -{ - wxAutoBufferedPaintDC dc(this); - OnRender(dc); -} - -void wxInputDraw::OnRender(wxDC& dc) -{ - dc.Clear(); - - glm::vec2 position = m_position; - - wxPen black = wxPen(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT)); - wxPen red = *wxRED_PEN; - wxPen grey = wxPen(wxSystemSettings::GetColour(wxSYS_COLOUR_GRAYTEXT)); - wxPen green = wxSystemSettings::SelectLightDark(0x336600, 0x99FF99); - - wxBrush black_brush = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT); - wxBrush red_brush = *wxRED_BRUSH; - wxBrush grey_brush = wxSystemSettings::GetColour(wxSYS_COLOUR_GRAYTEXT); - wxBrush green_brush = wxSystemSettings::SelectLightDark(0x336600, 0x99FF99); - - if(!IsEnabled()) - { - position = {}; - black.SetColour(black.GetColour()); - red.SetColour(wxSystemSettings::GetColour(wxSYS_COLOUR_GRAYTEXT)); - grey.SetColour(wxSystemSettings::GetColour(wxSYS_COLOUR_GRAYTEXT).MakeDisabled()); - - black_brush = wxSystemSettings::GetColour(wxSYS_COLOUR_GRAYTEXT); - red_brush = wxSystemSettings::GetColour(wxSYS_COLOUR_GRAYTEXT).MakeDisabled(); - grey_brush = wxSystemSettings::GetColour(wxSYS_COLOUR_GRAYTEXT).MakeDisabled(); - } - - dc.SetBackgroundMode(wxSOLID); - dc.SetBackground(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW)); - dc.SetPen(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW)); - dc.SetBrush(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW)); - dc.Clear(); - - const auto size = GetSize(); - const auto min_size = (float)std::min(size.GetWidth(), size.GetHeight()) - 1.0f; - const Vector2f middle{min_size / 2.0f, min_size / 2.0f}; - - // border - const wxRect border{0, 0, (int)min_size, (int)min_size}; - dc.SetPen(wxSystemSettings::GetColour(wxSYS_COLOUR_GRAYTEXT)); - dc.DrawRectangle(border); - - dc.SetPen(IsEnabled() ? green.GetColour() : grey.GetColour()); - dc.DrawCircle((int)middle.x, (int)middle.y, (int)middle.x); - - if (m_deadzone > 0) - { - dc.SetPen(grey); - dc.SetBrush(grey_brush); - const auto deadzone_size = m_deadzone * min_size / 2.0f; - dc.DrawCircle( - static_cast(middle.x), - static_cast(middle.x), - (int)deadzone_size); - - if (length(position) >= m_deadzone) - { - dc.SetPen(red); - dc.SetBrush(red_brush); - - if (std::abs(1.0f - length(position)) < 0.05f) - { - dc.SetPen(green); - dc.SetBrush(green_brush); - } - } - else - { - dc.SetPen(black); - dc.SetBrush(black_brush); - } - } - else - { - dc.SetPen(red); - dc.SetBrush(red_brush); - } - - // draw axis - const wxPoint pos - { - static_cast(middle.x + position.x * middle.x), - static_cast(middle.y - position.y * middle.y) - }; - dc.DrawCircle(pos.x, pos.y, 2); - - dc.SetBrush(*wxTRANSPARENT_BRUSH); -} - -void wxInputDraw::SetAxisValue(const glm::vec2& position) -{ - m_position = position; - Refresh(); -} - -void wxInputDraw::SetDeadzone(float deadzone) -{ - m_deadzone = deadzone; - Refresh(); -} diff --git a/src/gui/wxgui/components/wxInputDraw.h b/src/gui/wxgui/components/wxInputDraw.h deleted file mode 100644 index 477dd075..00000000 --- a/src/gui/wxgui/components/wxInputDraw.h +++ /dev/null @@ -1,24 +0,0 @@ -#pragma once - -#include "input/api/ControllerState.h" -#include "util/math/vector2.h" - -#include - - -class wxInputDraw : public wxWindow -{ -public: - wxInputDraw(wxWindow *parent, wxWindowID id, const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxDefaultSize); - ~wxInputDraw(); - - virtual void OnRender(wxDC& dc); - void SetAxisValue(const glm::vec2& position); - void SetDeadzone(float deadzone); - -private: - void OnPaintEvent(wxPaintEvent& event); - - glm::vec2 m_position{}; - float m_deadzone{0}; -}; diff --git a/src/gui/wxgui/components/wxLogCtrl.cpp b/src/gui/wxgui/components/wxLogCtrl.cpp deleted file mode 100644 index 46ab9ad5..00000000 --- a/src/gui/wxgui/components/wxLogCtrl.cpp +++ /dev/null @@ -1,161 +0,0 @@ -#include "wxgui/components/wxLogCtrl.h" - -#include - -wxDEFINE_EVENT(EVT_ON_LIST_UPDATED, wxEvent); - -wxLogCtrl::wxLogCtrl(wxWindow* parent, wxWindowID id, const wxPoint& pos, const wxSize& size, long style, bool alternateRowColours) - : TextList(parent, id, pos, size, style) -{ - m_timer = new wxTimer(this); - this->Bind(wxEVT_TIMER, &wxLogCtrl::OnTimer, this); - this->Bind(EVT_ON_LIST_UPDATED, &wxLogCtrl::OnActiveListUpdated, this); - m_timer->Start(250); - - m_alternateRowColoursEnabled = alternateRowColours; -} - -wxLogCtrl::~wxLogCtrl() -{ - this->Unbind(wxEVT_TIMER, &wxLogCtrl::OnTimer, this); - this->Unbind(EVT_ON_LIST_UPDATED, &wxLogCtrl::OnActiveListUpdated, this); - - if(m_timer) - m_timer->Stop(); - - if(m_update_worker.joinable()) - m_update_worker.join(); -} - -void wxLogCtrl::SetActiveFilter(const std::string& active_filter) -{ - if(m_active_filter == active_filter) - return; - - m_active_filter = active_filter; - - if(m_update_worker.joinable()) - m_update_worker.join(); - - m_update_worker = std::thread(&wxLogCtrl::UpdateActiveEntries, this); -} - -const std::string& wxLogCtrl::GetActiveFilter() const -{ - return m_active_filter; -} - -void wxLogCtrl::SetFilterMessage(bool state) -{ - if(m_filter_messages == state) - return; - - m_filter_messages = state; - - if(m_update_worker.joinable()) - m_update_worker.join(); - - m_update_worker = std::thread(&wxLogCtrl::UpdateActiveEntries, this); -} - -bool wxLogCtrl::GetFilterMessage() const -{ - return m_filter_messages; -} - -void wxLogCtrl::PushEntry(const wxString& filter, const wxString& message) -{ - std::unique_lock lock(m_mutex); - m_log_entries.emplace_back(filter, message); - ListIt_t it = m_log_entries.back(); - lock.unlock(); - - if(m_active_filter.empty() || filter == m_active_filter || (m_filter_messages && boost::icontains(message.ToStdString(), m_active_filter))) - { - std::unique_lock active_lock(m_active_mutex); - m_active_entries.emplace_back(std::cref(it)); - const auto entry_count = m_active_entries.size(); - active_lock.unlock(); - - if(entry_count <= m_elements_visible) - { - RefreshLine(entry_count - 1); - } - } -} - -void wxLogCtrl::OnDraw(wxDC& dc, sint32 start, sint32 count, const wxPoint& start_position) -{ - wxPoint position = start_position; - - std::scoped_lock lock(m_active_mutex); - auto it = std::next(m_active_entries.cbegin(), start); - if(it == m_active_entries.cend()) - return; - - for (sint32 i = 0; i <= count && it != m_active_entries.cend(); ++i, ++it) - { - wxColour background_colour = GetBackgroundColour(); - if((start + i) % 2 != 0 && m_alternateRowColoursEnabled) - background_colour = GetAlternateRowColour(); - - DrawLineBackground(dc, position, background_colour); - - dc.SetTextForeground(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT)); - dc.DrawText(it->get().second, position); - - NextLine(position, &start_position); - } -} - -void wxLogCtrl::OnTimer(wxTimerEvent& event) -{ - std::unique_lock lock(m_active_mutex); - const auto count = m_active_entries.size(); - if(count == m_element_count) - return; - - lock.unlock(); - SetElementCount(count); - - if(m_scrolled_to_end) - { - Scroll(0, count); - RefreshControl(); - } -} - -void wxLogCtrl::OnActiveListUpdated(wxEvent& event) -{ - std::unique_lock lock(m_active_mutex); - const auto count = m_active_entries.size(); - lock.unlock(); - - SetElementCount(count); - RefreshControl(); -} - -void wxLogCtrl::UpdateActiveEntries() -{ - { - std::scoped_lock lock(m_mutex, m_active_mutex); - m_active_entries.clear(); - if(m_active_filter.empty()) - { - for (const auto& it : m_log_entries) - m_active_entries.emplace_back(it); - } - else - { - for (const auto& it : m_log_entries) - { - if(it.first == m_active_filter || - (m_filter_messages && boost::icontains(it.second.ToStdString(), m_active_filter)) ) - m_active_entries.emplace_back(it); - } - } - } - - wxQueueEvent(this, new wxCommandEvent(EVT_ON_LIST_UPDATED)); -} - diff --git a/src/gui/wxgui/components/wxLogCtrl.h b/src/gui/wxgui/components/wxLogCtrl.h deleted file mode 100644 index e4593b8c..00000000 --- a/src/gui/wxgui/components/wxLogCtrl.h +++ /dev/null @@ -1,52 +0,0 @@ -#pragma once -#include "TextList.h" - -class wxLogCtrl : public TextList -{ -public: - wxLogCtrl(wxWindow* parent, wxWindowID id, const wxPoint& pos, const wxSize& size, long style, bool alternateRowColours = false); - ~wxLogCtrl(); - - void SetActiveFilter(const std::string& active_filter); - [[nodiscard]] const std::string& GetActiveFilter() const; - void SetFilterMessage(bool state); - [[nodiscard]] bool GetFilterMessage() const; - - void PushEntry(const wxString& filter, const wxString& message); - -protected: - void OnDraw(wxDC& dc, sint32 start, sint32 count, const wxPoint& start_position); - -private: - void OnTimer(wxTimerEvent& event); - void OnActiveListUpdated(wxEvent& event); - - wxTimer* m_timer; - - bool m_alternateRowColoursEnabled = false; - wxColour m_alternateRowColour; - - wxColour GetAlternateRowColour() - { - if (m_alternateRowColour.IsOk()) - return m_alternateRowColour; - - // Depending on the background, alternate row colour should be a bit - // darker or brighter. - const wxColour bgColour = GetBackgroundColour(); - int alpha = bgColour.GetRGB() > 0x808080 ? 97 : 110; - m_alternateRowColour = bgColour.ChangeLightness(alpha); - return m_alternateRowColour; - } - - std::string m_active_filter; - std::thread m_update_worker; - bool m_filter_messages = false; - - std::mutex m_mutex, m_active_mutex; - using ListEntry_t = std::pair; - using ListIt_t = std::list::const_reference; - std::list m_log_entries; - std::list> m_active_entries; - void UpdateActiveEntries(); -}; \ No newline at end of file diff --git a/src/gui/wxgui/components/wxProgressDialogManager.h b/src/gui/wxgui/components/wxProgressDialogManager.h deleted file mode 100644 index 93b6c316..00000000 --- a/src/gui/wxgui/components/wxProgressDialogManager.h +++ /dev/null @@ -1,118 +0,0 @@ -#pragma once - -#include -#include -#include -#include "util/helpers/Semaphore.h" - -wxDEFINE_EVENT(wxEVT_CREATE_PROGRESS_DIALOG, wxThreadEvent); -wxDEFINE_EVENT(wxEVT_DESTROY_PROGRESS_DIALOG, wxThreadEvent); -wxDEFINE_EVENT(wxEVT_UPDATE_PROGRESS_DIALOG, wxThreadEvent); - -// wrapper for wxGenericProgressDialog which can be used from any thread -class wxProgressDialogManager : public wxEvtHandler -{ -public: - wxProgressDialogManager(wxWindow* parent) : m_parent(parent), m_dialog(nullptr) - { - Bind(wxEVT_CREATE_PROGRESS_DIALOG, &wxProgressDialogManager::OnCreateProgressDialog, this); - Bind(wxEVT_DESTROY_PROGRESS_DIALOG, &wxProgressDialogManager::OnDestroyProgressDialog, this); - Bind(wxEVT_UPDATE_PROGRESS_DIALOG, &wxProgressDialogManager::OnUpdateProgressDialog, this); - } - - ~wxProgressDialogManager() - { - if (m_dialog) - Destroy(); - } - - void Create(const wxString& title, const wxString& message, int maximum, int style = wxPD_APP_MODAL | wxPD_ELAPSED_TIME | wxPD_REMAINING_TIME) - { - m_instanceSemaphore.increment(); - m_isCancelled = false; - m_isSkipped = false; - wxThreadEvent event(wxEVT_CREATE_PROGRESS_DIALOG); - event.SetString(title); - event.SetInt(maximum); - event.SetExtraLong(style); - wxQueueEvent(this, event.Clone()); - } - - void Destroy() - { - wxThreadEvent event(wxEVT_DESTROY_PROGRESS_DIALOG); - wxQueueEvent(this, event.Clone()); - m_instanceSemaphore.waitUntilZero(); // wait until destruction is complete - } - - // this also updates the cancel and skip state - void Update(int value, const wxString& newmsg = wxEmptyString) - { - wxThreadEvent event(wxEVT_UPDATE_PROGRESS_DIALOG); - event.SetInt(value); - event.SetString(newmsg); - wxQueueEvent(this, event.Clone()); - } - - bool IsCancelled() const - { - return m_isCancelled; - } - - bool IsSkipped() const - { - return m_isSkipped; - } - - bool IsCancelledOrSkipped() const - { - return m_isCancelled || m_isSkipped; - } - -private: - void OnCreateProgressDialog(wxThreadEvent& event) - { - if (m_dialog) - { - m_dialog->Destroy(); - m_instanceSemaphore.waitUntilZero(); - } - m_maximum = event.GetInt(); - m_dialog = new wxGenericProgressDialog(event.GetString(), "Please wait...", m_maximum, m_parent, event.GetExtraLong()); - } - - void OnDestroyProgressDialog(wxThreadEvent& event) - { - if (m_dialog) - { - m_dialog->Destroy(); - m_dialog = nullptr; - m_instanceSemaphore.decrement(); - } - } - - void OnUpdateProgressDialog(wxThreadEvent& event) - { - if (m_dialog) - { - // make sure that progress is never >= maximum - // because wxGenericProgressDialog seems to become crashy on destruction otherwise - int progress = event.GetInt(); - if(progress >= m_maximum) - progress = m_maximum - 1; - bool wasSkipped = false; - bool r = m_dialog->Update(progress, event.GetString(), &wasSkipped); - if(!r) - m_isCancelled = true; - if(wasSkipped) - m_isSkipped = true; - } - } - - wxWindow* m_parent; - wxGenericProgressDialog* m_dialog; - bool m_isCancelled{false}; - bool m_isSkipped{false}; - int m_maximum{0}; - CounterSemaphore m_instanceSemaphore; // used to synchronize destruction of the dialog -}; \ No newline at end of file diff --git a/src/gui/wxgui/components/wxTitleManagerList.cpp b/src/gui/wxgui/components/wxTitleManagerList.cpp deleted file mode 100644 index abca965c..00000000 --- a/src/gui/wxgui/components/wxTitleManagerList.cpp +++ /dev/null @@ -1,1333 +0,0 @@ -#include "wxgui/components/wxTitleManagerList.h" -#include "wxgui/helpers/wxHelpers.h" -#include "util/helpers/SystemException.h" -#include "Cafe/TitleList/GameInfo.h" -#include "Cafe/TitleList/TitleInfo.h" -#include "Cafe/TitleList/TitleList.h" -#include "wxgui/components/wxGameList.h" -#include "wxgui/helpers/wxCustomEvents.h" -#include "wxgui/helpers/wxHelpers.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "../wxHelper.h" - -#include - -#include "config/ActiveSettings.h" -#include "wxgui/ChecksumTool.h" -#include "wxgui/MainWindow.h" -#include "Cafe/TitleList/TitleId.h" -#include "Cafe/TitleList/SaveList.h" -#include "Cafe/TitleList/TitleList.h" - -#include -#include - -#include "Common/FileStream.h" - -wxDEFINE_EVENT(wxEVT_TITLE_FOUND, wxCommandEvent); -wxDEFINE_EVENT(wxEVT_TITLE_REMOVED, wxCommandEvent); -wxDEFINE_EVENT(wxEVT_REMOVE_ENTRY, wxCommandEvent); - -wxTitleManagerList::wxTitleManagerList(wxWindow* parent, wxWindowID id) - : wxListView(parent, id, wxDefaultPosition, wxDefaultSize, wxLC_REPORT | wxLC_VIRTUAL) -{ - AddColumns(); - - // tooltip TODO: extract class mb wxPanelTooltip - m_tooltip_window = new wxPanel(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxNO_BORDER); - auto* tooltip_sizer = new wxBoxSizer(wxVERTICAL); - m_tooltip_text = new wxStaticText(m_tooltip_window, wxID_ANY, wxEmptyString); - tooltip_sizer->Add(m_tooltip_text , 0, wxALL, 5); - m_tooltip_window->SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_INFOBK)); - m_tooltip_window->SetSizerAndFit(tooltip_sizer); - m_tooltip_window->Hide(); - m_tooltip_timer = new wxTimer(this); - - Bind(wxEVT_LIST_COL_CLICK, &wxTitleManagerList::OnColumnClick, this); - Bind(wxEVT_CONTEXT_MENU, &wxTitleManagerList::OnContextMenu, this); - Bind(wxEVT_LIST_ITEM_SELECTED, &wxTitleManagerList::OnItemSelected, this); - Bind(wxEVT_TIMER, &wxTitleManagerList::OnTimer, this); - Bind(wxEVT_REMOVE_ITEM, &wxTitleManagerList::OnRemoveItem, this); - Bind(wxEVT_REMOVE_ENTRY, &wxTitleManagerList::OnRemoveEntry, this); - Bind(wxEVT_TITLE_FOUND, &wxTitleManagerList::OnTitleDiscovered, this); - Bind(wxEVT_TITLE_REMOVED, &wxTitleManagerList::OnTitleRemoved, this); - Bind(wxEVT_CLOSE_WINDOW, &wxTitleManagerList::OnClose, this); - - m_callbackIdTitleList = CafeTitleList::RegisterCallback([](CafeTitleListCallbackEvent* evt, void* ctx) { ((wxTitleManagerList*)ctx)->HandleTitleListCallback(evt); }, this); - m_callbackIdSaveList = CafeSaveList::RegisterCallback([](CafeSaveListCallbackEvent* evt, void* ctx) { ((wxTitleManagerList*)ctx)->HandleSaveListCallback(evt); }, this); - - ShowSortIndicator(ColumnTitleId); -} - -wxTitleManagerList::~wxTitleManagerList() -{ - CafeSaveList::UnregisterCallback(m_callbackIdSaveList); - CafeTitleList::UnregisterCallback(m_callbackIdTitleList); -} - -boost::optional wxTitleManagerList::GetSelectedTitleEntry() const -{ - const auto selection = GetFirstSelected(); - if (selection != wxNOT_FOUND) - { - const auto tmp = GetTitleEntry(selection); - if (tmp.has_value()) - return tmp.value(); - } - - return {}; -} - -boost::optional wxTitleManagerList::GetSelectedTitleEntry() -{ - const auto selection = GetFirstSelected(); - if (selection != wxNOT_FOUND) - { - const auto tmp = GetTitleEntry(selection); - if (tmp.has_value()) - return tmp.value(); - } - - return {}; -} - -//boost::optional wxTitleManagerList::GetTitleEntry(EntryType type, uint64 titleid) -//{ -// for(const auto& v : m_data) -// { -// if (v->entry.title_id == titleid && v->entry.type == type) -// return v->entry; -// } -// -// return {}; -//} - -boost::optional wxTitleManagerList::GetTitleEntryByUID(uint64 uid) -{ - for (const auto& v : m_data) - { - if (v->entry.location_uid == uid) - return v->entry; - } - return {}; -} - -void wxTitleManagerList::AddColumns() -{ - wxListItem col0; - col0.SetId(ColumnTitleId); - col0.SetText(_("Title ID")); - col0.SetWidth(120); - InsertColumn(ColumnTitleId, col0); - - wxListItem col1; - col1.SetId(ColumnName); - col1.SetText(_("Name")); - col1.SetWidth(435); - InsertColumn(ColumnName, col1); - - wxListItem col2; - col2.SetId(ColumnType); - col2.SetText(_("Type")); - col2.SetWidth(65); - InsertColumn(ColumnType, col2); - - wxListItem col3; - col3.SetId(ColumnVersion); - col3.SetText(_("Version")); - col3.SetWidth(40); - InsertColumn(ColumnVersion, col3); - - wxListItem col4; - col4.SetId(ColumnRegion); - col4.SetText(_("Region")); - col4.SetWidth(60); - InsertColumn(ColumnRegion, col4); - - wxListItem col5; - col5.SetId(ColumnFormat); - col5.SetText(_("Format")); - col5.SetWidth(63); - InsertColumn(ColumnFormat, col5); - - wxListItem col6; - col6.SetId(ColumnLocation); - col6.SetText(_("Location")); - col6.SetWidth(63); - InsertColumn(ColumnLocation, col6); -} - -wxString wxTitleManagerList::OnGetItemText(long item, long column) const -{ - if (item >= GetItemCount()) - return wxEmptyString; - - const auto entry = GetTitleEntry(item); - if (entry.has_value()) - return GetTitleEntryText(entry.value(), (ItemColumn)column); - - return wxEmptyString; -} - -wxItemAttr* wxTitleManagerList::OnGetItemAttr(long item) const -{ - static wxColour bgColourPrimary = GetBackgroundColour(); - static wxColour bgColourSecondary = wxHelper::CalculateAccentColour(bgColourPrimary); - static wxListItemAttr s_primary_attr(GetTextColour(), bgColourPrimary, GetFont()); - static wxListItemAttr s_secondary_attr(GetTextColour(), bgColourSecondary, GetFont()); - return item % 2 == 0 ? &s_primary_attr : &s_secondary_attr; -} - -boost::optional wxTitleManagerList::GetTitleEntry(long item) -{ - long counter = 0; - for (const auto& data : m_sorted_data) - { - if (!data.get().visible) - continue; - - if (item != counter++) - continue; - - return data.get().entry; - } - - return {}; -} - -boost::optional wxTitleManagerList::GetTitleEntry(const fs::path& path) const -{ - const auto tmp = _pathToUtf8(path); - for (const auto& data : m_data) - { - if (boost::iequals(_pathToUtf8(data->entry.path), tmp)) - return data->entry; - } - - return {}; -} -boost::optional wxTitleManagerList::GetTitleEntry(const fs::path& path) -{ - const auto tmp = _pathToUtf8(path); - for (const auto& data : m_data) - { - if (boost::iequals(_pathToUtf8(data->entry.path), tmp)) - return data->entry; - } - - return {}; -} - -boost::optional wxTitleManagerList::GetTitleEntry(long item) const -{ - long counter = 0; - for (const auto& data : m_sorted_data) - { - if (!data.get().visible) - continue; - - if (item != counter++) - continue; - - return data.get().entry; - } - - return {}; -} - -void wxTitleManagerList::OnConvertToCompressedFormat(uint64 titleId, uint64 rightClickedUID) -{ - TitleInfo titleInfo_base; - TitleInfo titleInfo_update; - TitleInfo titleInfo_aoc; - - titleId = TitleIdParser::MakeBaseTitleId(titleId); // if the titleId of a separate update is selected, this converts it back to the base titleId - TitleIdParser titleIdParser(titleId); - bool hasBaseTitleId = titleIdParser.GetType() != TitleIdParser::TITLE_TYPE::AOC; - bool hasUpdateTitleId = titleIdParser.CanHaveSeparateUpdateTitleId(); - TitleId updateTitleId = hasUpdateTitleId ? titleIdParser.GetSeparateUpdateTitleId() : 0; - - // todo - AOC titleIds might differ from the base/update game in other bits than the type. We have to use the meta data from the base/update to match aoc to the base title id - // for now we just assume they match - TitleId aocTitleId; - if (hasBaseTitleId) - aocTitleId = (titleId & (uint64)~0xFF00000000) | (uint64)0xC00000000; - else - aocTitleId = titleId; - - // find base and update - for (const auto& data : m_data) - { - if (hasBaseTitleId && data->entry.title_id == titleId) - { - if (!titleInfo_base.IsValid()) - { - titleInfo_base = CafeTitleList::GetTitleInfoByUID(data->entry.location_uid); - if(data->entry.location_uid == rightClickedUID) - break; // prefer the users selection - } - } - } - for (const auto& data : m_data) - { - if (hasUpdateTitleId && data->entry.title_id == updateTitleId) - { - if (!titleInfo_update.IsValid()) - { - titleInfo_update = CafeTitleList::GetTitleInfoByUID(data->entry.location_uid); - if(data->entry.location_uid == rightClickedUID) - break; - } - else - { - // if multiple updates are present use the newest one - if (titleInfo_update.GetAppTitleVersion() < data->entry.version) - titleInfo_update = CafeTitleList::GetTitleInfoByUID(data->entry.location_uid); - if(data->entry.location_uid == rightClickedUID) - break; - } - } - } - // find AOC - for (const auto& data : m_data) - { - if (data->entry.title_id == aocTitleId) - { - titleInfo_aoc = CafeTitleList::GetTitleInfoByUID(data->entry.location_uid); - if(data->entry.location_uid == rightClickedUID) - break; - } - } - - wxString msg = _("The following content will be converted to a compressed Wii U archive file (.wua):"); - msg.append("\n \n"); - - if (titleInfo_base.IsValid()) - msg.append(formatWxString(_("Base game:\n{}"), titleInfo_base.GetPrintPath())); - else - msg.append(_("Base game:\nNot installed")); - - msg.append("\n\n"); - - if (titleInfo_update.IsValid()) - msg.append(formatWxString(_("Update:\n{}"), titleInfo_update.GetPrintPath())); - else - msg.append(_("Update:\nNot installed")); - - msg.append("\n\n"); - - if (titleInfo_aoc.IsValid()) - msg.append(formatWxString(_("DLC:\n{}"), titleInfo_aoc.GetPrintPath())); - else - msg.append(_("DLC:\nNot installed")); - - const int answer = wxMessageBox(msg, _("Confirmation"), wxOK | wxCANCEL | wxCENTRE | wxICON_QUESTION, this); - if (answer != wxOK) - return; - std::vector titlesToConvert; - if (titleInfo_base.IsValid()) - titlesToConvert.emplace_back(&titleInfo_base); - if (titleInfo_update.IsValid()) - titlesToConvert.emplace_back(&titleInfo_update); - if (titleInfo_aoc.IsValid()) - titlesToConvert.emplace_back(&titleInfo_aoc); - if (titlesToConvert.empty()) - return; - // get short name - CafeConsoleLanguage languageId = CafeConsoleLanguage::EN; // todo - use user's locale - std::string shortName; - if (titleInfo_base.IsValid()) - shortName = titleInfo_base.GetMetaInfo()->GetShortName(languageId); - else if (titleInfo_update.IsValid()) - shortName = titleInfo_update.GetMetaInfo()->GetShortName(languageId); - else if (titleInfo_aoc.IsValid()) - shortName = titleInfo_aoc.GetMetaInfo()->GetShortName(languageId); - - if (!shortName.empty()) - { - boost::replace_all(shortName, ":", ""); - } - // for the default output directory we use the first game path configured by the user - std::string defaultDir = ""; - if (!GetConfig().game_paths.empty()) - defaultDir = GetConfig().game_paths.front(); - // get the short name, which we will use as a suggested default file name - std::string defaultFileName = std::move(shortName); - boost::replace_all(defaultFileName, "/", ""); - boost::replace_all(defaultFileName, "\\", ""); - - CafeConsoleRegion region = CafeConsoleRegion::Auto; - if (titleInfo_base.IsValid() && titleInfo_base.HasValidXmlInfo()) - region = titleInfo_base.GetMetaInfo()->GetRegion(); - else if (titleInfo_update.IsValid() && titleInfo_update.HasValidXmlInfo()) - region = titleInfo_update.GetMetaInfo()->GetRegion(); - - if (region == CafeConsoleRegion::JPN) - defaultFileName.append(" (JP)"); - else if (region == CafeConsoleRegion::EUR) - defaultFileName.append(" (EU)"); - else if (region == CafeConsoleRegion::USA) - defaultFileName.append(" (US)"); - if (titleInfo_update.IsValid()) - { - defaultFileName.append(fmt::format(" (v{})", titleInfo_update.GetAppTitleVersion())); - } - defaultFileName.append(".wua"); - - // ask the user to provide a path for the output file - wxFileDialog saveFileDialog(this, _("Save Wii U game archive file"), defaultDir, wxString::FromUTF8(defaultFileName), - "WUA files (*.wua)|*.wua", wxFD_SAVE | wxFD_OVERWRITE_PROMPT); - if (saveFileDialog.ShowModal() == wxID_CANCEL || saveFileDialog.GetPath().IsEmpty()) - return; - fs::path outputPath(wxHelper::MakeFSPath(saveFileDialog.GetPath())); - fs::path outputPathTmp(wxHelper::MakeFSPath(saveFileDialog.GetPath().append("__tmp"))); - struct ZArchiveWriterContext - { - static void NewOutputFile(const int32_t partIndex, void* _ctx) - { - ZArchiveWriterContext* ctx = (ZArchiveWriterContext*)_ctx; - ctx->fs = FileStream::createFile2(ctx->outputPath); - if (!ctx->fs) - ctx->isValid = false; - } - - static void WriteOutputData(const void* data, size_t length, void* _ctx) - { - ZArchiveWriterContext* ctx = (ZArchiveWriterContext*)_ctx; - if (ctx->fs) - ctx->fs->writeData(data, length); - } - - bool RecursivelyCountFiles(const std::string& fscPath) - { - sint32 fscStatus; - std::unique_ptr vfDir(fsc_openDirIterator(fscPath.c_str(), &fscStatus)); - if (!vfDir) - return false; - if (cancelled) - return false; - FSCDirEntry dirEntry; - while (fsc_nextDir(vfDir.get(), &dirEntry)) - { - if (dirEntry.isFile) - { - totalInputFileSize += (uint64)dirEntry.fileSize; - totalFileCount++; - } - else if (dirEntry.isDirectory) - { - if (!RecursivelyCountFiles(fmt::format("{}{}/", fscPath, dirEntry.path))) - { - return false; - } - } - else - cemu_assert_unimplemented(); - } - return true; - } - - bool RecursivelyAddFiles(std::string archivePath, std::string fscPath) - { - sint32 fscStatus; - std::unique_ptr vfDir(fsc_openDirIterator(fscPath.c_str(), &fscStatus)); - if (!vfDir) - return false; - if (cancelled) - return false; - zaWriter->MakeDir(archivePath.c_str(), false); - FSCDirEntry dirEntry; - while (fsc_nextDir(vfDir.get(), &dirEntry)) - { - if (dirEntry.isFile) - { - zaWriter->StartNewFile((archivePath + dirEntry.path).c_str()); - std::unique_ptr vFile(fsc_open((fscPath + dirEntry.path).c_str(), FSC_ACCESS_FLAG::OPEN_FILE | FSC_ACCESS_FLAG::READ_PERMISSION, &fscStatus)); - if (!vFile) - return false; - transferBuffer.resize(32 * 1024); // 32KB - uint32 readBytes; - while (true) - { - readBytes = vFile->fscReadData(transferBuffer.data(), transferBuffer.size()); - if(readBytes == 0) - break; - zaWriter->AppendData(transferBuffer.data(), readBytes); - if (cancelled) - return false; - transferredInputBytes += readBytes; - } - currentFileIndex++; - } - else if (dirEntry.isDirectory) - { - if (!RecursivelyAddFiles(fmt::format("{}{}/", archivePath, dirEntry.path), fmt::format("{}{}/", fscPath, dirEntry.path))) - return false; - } - else - cemu_assert_unimplemented(); - } - return true; - } - - bool LoadTitleMetaAndCountFiles(TitleInfo* titleInfo) - { - std::string temporaryMountPath = TitleInfo::GetUniqueTempMountingPath(); - titleInfo->Mount(temporaryMountPath.c_str(), "", FSC_PRIORITY_BASE); - bool r = RecursivelyCountFiles(temporaryMountPath); - titleInfo->Unmount(temporaryMountPath.c_str()); - return r; - } - - bool StoreTitle(TitleInfo* titleInfo) - { - std::string temporaryMountPath = TitleInfo::GetUniqueTempMountingPath(); - titleInfo->Mount(temporaryMountPath.c_str(), "", FSC_PRIORITY_BASE); - bool r = RecursivelyAddFiles(fmt::format("{:016x}_v{}/", titleInfo->GetAppTitleId(), titleInfo->GetAppTitleVersion()), temporaryMountPath); - titleInfo->Unmount(temporaryMountPath.c_str()); - return r; - } - - bool AddTitles(TitleInfo** titles, size_t count) - { - currentFileIndex = 0; - totalFileCount = 0; - // count files - for (size_t i = 0; i < count; i++) - { - if (!LoadTitleMetaAndCountFiles(titles[i])) - return false; - if (cancelled) - return false; - } - // store files - for (size_t i = 0; i < count; i++) - { - if (!StoreTitle(titles[i])) - return false; - } - return true; - } - - ~ZArchiveWriterContext() - { - delete fs; - delete zaWriter; - }; - - fs::path outputPath; - FileStream* fs; - ZArchiveWriter* zaWriter{}; - bool isValid{false}; - std::vector transferBuffer; - std::atomic_bool cancelled{false}; - // progress - std::atomic_uint32_t totalFileCount{}; - std::atomic_uint32_t currentFileIndex{}; - std::atomic_uint64_t totalInputFileSize{}; - std::atomic_uint64_t transferredInputBytes{}; - }writerContext; - - // mount and store - writerContext.isValid = true; - writerContext.outputPath = outputPathTmp; - writerContext.zaWriter = new ZArchiveWriter(&ZArchiveWriterContext::NewOutputFile, &ZArchiveWriterContext::WriteOutputData, &writerContext); - if (!writerContext.isValid) - { - // failed to create file - wxMessageBox(_("Unable to create file"), _("Error"), wxOK | wxCENTRE | wxICON_ERROR, this); - return; - } - // open progress dialog - wxGenericProgressDialog progressDialog("Converting to .wua", - _("Counting files..."), - 100, // range - this, // parent - wxPD_CAN_ABORT - ); - progressDialog.Show(); - - auto asyncWorker = std::async(std::launch::async, &ZArchiveWriterContext::AddTitles, &writerContext, titlesToConvert.data(), titlesToConvert.size()); - while (!future_is_ready(asyncWorker)) - { - if (writerContext.cancelled) - { - progressDialog.Update(0, _("Stopping...")); - } - else if (writerContext.currentFileIndex != 0) - { - uint64 numSizeCompleted = writerContext.transferredInputBytes; - uint64 numSizeTotal = writerContext.totalInputFileSize; - uint32 pct = (uint32)(numSizeCompleted * (uint64)100 / numSizeTotal); - pct = std::min(pct, (uint32)100); - if (pct >= 100) - pct = 99; // never set it to 100 as progress == total will make .Update() call ShowModal() and lock up this loop - std::string textSuffix = fmt::format(" ({}MiB/{}MiB)", numSizeCompleted / 1024 / 1024, numSizeTotal / 1024 / 1024); - progressDialog.Update(pct, _("Converting files...") + textSuffix); - } - else - { - progressDialog.Update(0, _("Collecting list of files...") + fmt::format(" ({})", writerContext.totalFileCount.load())); - } - if (progressDialog.WasCancelled()) - writerContext.cancelled.store(true); - std::this_thread::sleep_for(std::chrono::milliseconds(100)); - } - progressDialog.Update(100-1, _("Finalizing...")); - bool r = asyncWorker.get(); - if (!r) - { - delete writerContext.fs; - writerContext.fs = nullptr; - std::error_code ec; - fs::remove(outputPathTmp, ec); - return; - } - writerContext.zaWriter->Finalize(); - delete writerContext.fs; - writerContext.fs = nullptr; - // verify the created WUA file - ZArchiveReader* zreader = ZArchiveReader::OpenFromFile(outputPathTmp); - if (!zreader) - { - wxMessageBox(_("Conversion failed\n"), _("Error"), wxOK | wxCENTRE | wxICON_ERROR, this); - std::error_code ec; - fs::remove(outputPathTmp, ec); - return; - } - // todo - do a quick verification here - delete zreader; - // finish - progressDialog.Hide(); - fs::rename(outputPathTmp, outputPath); - - // ask user if they want to delete the original titles - // todo - - - CafeTitleList::Refresh(); - wxMessageBox(_("Conversion finished\n"), _("Complete"), wxOK | wxCENTRE | wxICON_INFORMATION, this); -} - -void wxTitleManagerList::OnClose(wxCloseEvent& event) -{ - event.Skip(); - // wait until all tasks are complete - if (m_context_worker.valid()) - m_context_worker.get(); -} - -void wxTitleManagerList::OnColumnClick(wxListEvent& event) -{ - const int column = event.GetColumn(); - SortEntries(column); - event.Skip(); -} - -void wxTitleManagerList::RemoveItem(long item) -{ - const int item_count = GetItemCount(); - - const ItemData* ref = nullptr; - long counter = 0; - for(auto it = m_sorted_data.begin(); it != m_sorted_data.end(); ++it) - { - if (!it->get().visible) - continue; - - if (item != counter++) - continue; - - ref = &(it->get()); - m_sorted_data.erase(it); - break; - } - - // shouldn't happen - if (ref == nullptr) - return; - - for(auto it = m_data.begin(); it != m_data.end(); ++it) - { - if (ref != (*it).get()) - continue; - - m_data.erase(it); - break; - } - - SetItemCount(std::max(0, item_count - 1)); - RefreshPage(); -} - -void wxTitleManagerList::RemoveItem(const TitleEntry& entry) -{ - const int item_count = GetItemCount(); - - const TitleEntry* ref = &entry; - for (auto it = m_sorted_data.begin(); it != m_sorted_data.end(); ++it) - { - if (ref != &it->get().entry) - continue; - - m_sorted_data.erase(it); - break; - } - - for (auto it = m_data.begin(); it != m_data.end(); ++it) - { - if (ref != &(*it).get()->entry) - continue; - - m_data.erase(it); - break; - } - - SetItemCount(std::max(0, item_count - 1)); - RefreshPage(); -} - -void wxTitleManagerList::OnItemSelected(wxListEvent& event) -{ - event.Skip(); - m_tooltip_timer->Stop(); - const auto selection = event.GetIndex(); - - if (selection == wxNOT_FOUND) - { - m_tooltip_window->Hide(); - return; - } - - const auto entry = GetTitleEntry(selection); - if (!entry.has_value()) - { - m_tooltip_window->Hide(); - return; - } - - m_tooltip_window->Hide(); - return; - - //const auto mouse_position = wxGetMousePosition() - GetScreenPosition(); - //m_tooltip_window->SetPosition(wxPoint(mouse_position.x + 15, mouse_position.y + 15)); - - //wxString msg; - //switch(entry->error) - //{ - //case TitleError::WrongBaseLocation: - // msg = _("This base game is installed at the wrong location."); - // break; - //case TitleError::WrongUpdateLocation: - // msg = _("This update is installed at the wrong location."); - // break; - //case TitleError::WrongDlcLocation: - // msg = _("This DLC is installed at the wrong location."); - // break; - //default: - // return;; - //} - - //m_tooltip_text->SetLabel(formatWxString("{}\n{}", msg, _("You can use the context menu to fix it."))); - //m_tooltip_window->Fit(); - //m_tooltip_timer->StartOnce(250); -} - -enum ContextMenuEntries -{ - kContextMenuOpenDirectory = wxID_HIGHEST + 1, - kContextMenuDelete, - kContextMenuLaunch, - kContextMenuVerifyGameFiles, - kContextMenuConvertToWUA, -}; -void wxTitleManagerList::OnContextMenu(wxContextMenuEvent& event) -{ - // still doing work - if (m_context_worker.valid() && !future_is_ready(m_context_worker)) - return; - - wxMenu menu; - menu.Bind(wxEVT_COMMAND_MENU_SELECTED, &wxTitleManagerList::OnContextMenuSelected, this); - - const auto selection = GetFirstSelected(); - if (selection == wxNOT_FOUND) - return; - - const auto entry = GetTitleEntry(selection); - if (!entry.has_value()) - return; - - if(entry->type == EntryType::Base) - menu.Append(kContextMenuLaunch, _("&Launch title")); - - menu.Append(kContextMenuOpenDirectory, _("&Open directory")); - if (entry->type != EntryType::Save) - menu.Append(kContextMenuVerifyGameFiles, _("&Verify integrity of game files")); - - menu.AppendSeparator(); - - if (entry->type != EntryType::Save && entry->format != EntryFormat::WUA) - { - menu.Append(kContextMenuConvertToWUA, _("Convert to compressed Wii U archive (.wua)")); - - menu.AppendSeparator(); - } - menu.Append(kContextMenuDelete, _("&Delete")); - - PopupMenu(&menu); - - // TODO: fix tooltip position -} - -bool wxTitleManagerList::DeleteEntry(long index, const TitleEntry& entry) -{ - wxDTorFunc reset_text(wxQueueEvent, this, new wxSetStatusBarTextEvent(wxEmptyString, 1)); - wxQueueEvent(this, new wxSetStatusBarTextEvent("Deleting entry...", 1)); - - wxString msg; - const bool is_directory = fs::is_directory(entry.path); - if(is_directory) - msg = formatWxString(_("Are you really sure that you want to delete the following folder:\n{}"), _pathToUtf8(entry.path)); - else - msg = formatWxString(_("Are you really sure that you want to delete the following file:\n{}"), _pathToUtf8(entry.path)); - - const auto result = wxMessageBox(msg, _("Warning"), wxYES_NO | wxCENTRE | wxICON_EXCLAMATION, this); - if (result == wxNO) - return false; - - std::error_code ec; - if (is_directory) - { - if (entry.type != EntryType::Save) - { - // delete content, meta, code folders first - const auto content = entry.path / "content"; - fs::remove_all(content, ec); - - const auto meta = entry.path / "meta"; - fs::remove_all(meta, ec); - - const auto code = entry.path / "code"; - fs::remove_all(code, ec); - } - else - { - // delete meta, user folders first - const auto meta = entry.path / "meta"; - fs::remove_all(meta, ec); - - const auto user = entry.path / "user"; - fs::remove_all(user, ec); - } - - - // check if folder is empty - if(fs::is_empty(entry.path, ec)) - fs::remove_all(entry.path, ec); - } - else // simply remove file - fs::remove(entry.path, ec); - - if(ec) - { - const auto error_msg = formatWxString(_("Error when trying to delete the entry:\n{}"), GetSystemErrorMessage(ec)); - wxMessageBox(error_msg, _("Error"), wxOK|wxCENTRE, this); - return false; - } - - // thread safe request to delete entry - const auto evt = new wxCommandEvent(wxEVT_REMOVE_ITEM); - evt->SetInt(index); - wxQueueEvent(this, evt); - return true; -} - -void wxTitleManagerList::OnContextMenuSelected(wxCommandEvent& event) -{ - // still doing work - if (m_context_worker.valid() && !future_is_ready(m_context_worker)) - return; - - const auto selection = GetFirstSelected(); - if (selection == wxNOT_FOUND) - return; - - const auto entry = GetTitleEntry(selection); - if (!entry.has_value()) - return; - - switch (event.GetId()) - { - case kContextMenuOpenDirectory: - { - const auto path = fs::is_directory(entry->path) ? entry->path : entry->path.parent_path(); - wxLaunchDefaultApplication(wxHelper::FromPath(path)); - } - break; - case kContextMenuDelete: - m_context_worker = std::async(std::launch::async, &wxTitleManagerList::DeleteEntry, this, selection, entry.value()); - break; - case kContextMenuLaunch: - { - try - { - MainWindow::RequestLaunchGame(entry->path, wxLaunchGameEvent::INITIATED_BY::TITLE_MANAGER); - Close(); - } - catch (const std::exception& ex) - { - cemuLog_log(LogType::Force, "wxTitleManagerList::OnContextMenuSelected: can't launch title: {}", ex.what()); - } - } - break; - case kContextMenuVerifyGameFiles: - (new ChecksumTool(this, entry.value()))->Show(); - break; - case kContextMenuConvertToWUA: - - OnConvertToCompressedFormat(entry.value().title_id, entry.value().location_uid); - break; - } -} - -void wxTitleManagerList::OnTimer(wxTimerEvent& event) -{ - if(event.GetTimer().GetId() != m_tooltip_timer->GetId()) - { - event.Skip(); - return; - } - - m_tooltip_window->Show(); -} - -void wxTitleManagerList::OnRemoveItem(wxCommandEvent& event) -{ - RemoveItem(event.GetInt()); -} - -void wxTitleManagerList::OnRemoveEntry(wxCommandEvent& event) -{ - wxASSERT(event.GetClientData() != nullptr); - RemoveItem(*(TitleEntry*)event.GetClientData()); -} - -wxString wxTitleManagerList::GetTitleEntryText(const TitleEntry& entry, ItemColumn column) -{ - switch (column) - { - case ColumnTitleId: - return formatWxString("{:08x}-{:08x}", (uint32) (entry.title_id >> 32), (uint32) (entry.title_id & 0xFFFFFFFF)); - case ColumnName: - return entry.name; - case ColumnType: - return GetTranslatedTitleEntryType(entry.type); - case ColumnVersion: - return formatWxString("{}", entry.version); - case ColumnRegion: - return wxGetTranslation(fmt::format("{}", entry.region)); - case ColumnFormat: - { - if (entry.type == EntryType::Save) - return _("Save folder"); - switch (entry.format) - { - case wxTitleManagerList::EntryFormat::Folder: - return _("Folder"); - case wxTitleManagerList::EntryFormat::WUD: - return _("WUD"); - case wxTitleManagerList::EntryFormat::NUS: - return _("NUS"); - case wxTitleManagerList::EntryFormat::WUA: - return _("WUA"); - case wxTitleManagerList::EntryFormat::WUHB: - return _("WUHB"); - } - return ""; - } - case ColumnLocation: - { - const auto relative_mlc_path = _pathToUtf8(entry.path.lexically_relative(ActiveSettings::GetMlcPath())); - if (relative_mlc_path.starts_with("usr") || relative_mlc_path.starts_with("sys")) - return _("MLC"); - else - return _("Game Paths"); - } - default: - UNREACHABLE; - } - - return wxEmptyString; -} - -wxString wxTitleManagerList::GetTranslatedTitleEntryType(EntryType type) -{ - switch (type) - { - case EntryType::Base: - return _("base"); - case EntryType::Update: - return _("update"); - case EntryType::Dlc: - return _("DLC"); - case EntryType::Save: - return _("save"); - case EntryType::System: - return _("system"); - default: - return std::to_string(static_cast>(type)); - } -} - -void wxTitleManagerList::HandleTitleListCallback(CafeTitleListCallbackEvent* evt) -{ - if (evt->eventType != CafeTitleListCallbackEvent::TYPE::TITLE_DISCOVERED && - evt->eventType != CafeTitleListCallbackEvent::TYPE::TITLE_REMOVED) - return; - - auto& titleInfo = *evt->titleInfo; - wxTitleManagerList::EntryType entryType; - switch (titleInfo.GetTitleType()) - { - case TitleIdParser::TITLE_TYPE::BASE_TITLE_UPDATE: - entryType = EntryType::Update; - break; - case TitleIdParser::TITLE_TYPE::AOC: - entryType = EntryType::Dlc; - break; - case TitleIdParser::TITLE_TYPE::SYSTEM_DATA: - case TitleIdParser::TITLE_TYPE::SYSTEM_OVERLAY_TITLE: - case TitleIdParser::TITLE_TYPE::SYSTEM_TITLE: - entryType = EntryType::System; - break; - default: - entryType = EntryType::Base; - } - - wxTitleManagerList::EntryFormat entryFormat; - switch (titleInfo.GetFormat()) - { - case TitleInfo::TitleDataFormat::WUD: - entryFormat = EntryFormat::WUD; - break; - case TitleInfo::TitleDataFormat::NUS: - entryFormat = EntryFormat::NUS; - break; - case TitleInfo::TitleDataFormat::WIIU_ARCHIVE: - entryFormat = EntryFormat::WUA; - break; - case TitleInfo::TitleDataFormat::WUHB: - entryFormat = EntryFormat::WUHB; - break; - case TitleInfo::TitleDataFormat::HOST_FS: - default: - entryFormat = EntryFormat::Folder; - break; - } - - if (evt->eventType == CafeTitleListCallbackEvent::TYPE::TITLE_DISCOVERED) - { - if (titleInfo.IsCached()) - return; // the title list only displays non-cached entries - wxTitleManagerList::TitleEntry entry(entryType, entryFormat, titleInfo.GetPath()); - - ParsedMetaXml* metaInfo = titleInfo.GetMetaInfo(); - if(titleInfo.IsSystemDataTitle()) - return; // dont show system data titles for now - entry.location_uid = titleInfo.GetUID(); - entry.title_id = titleInfo.GetAppTitleId(); - std::string name = metaInfo->GetLongName(GetConfig().console_language.GetValue()); - const auto nl = name.find(L'\n'); - if (nl != std::string::npos) - name.replace(nl, 1, " - "); - entry.name = wxString::FromUTF8(name); - entry.version = titleInfo.GetAppTitleVersion(); - entry.region = metaInfo->GetRegion(); - - auto* cmdEvt = new wxCommandEvent(wxEVT_TITLE_FOUND); - cmdEvt->SetClientObject(new wxCustomData(entry)); - wxQueueEvent(this, cmdEvt); - } - else if (evt->eventType == CafeTitleListCallbackEvent::TYPE::TITLE_REMOVED) - { - wxTitleManagerList::TitleEntry entry(entryType, entryFormat, titleInfo.GetPath()); - entry.location_uid = titleInfo.GetUID(); - entry.title_id = titleInfo.GetAppTitleId(); - - auto* cmdEvt = new wxCommandEvent(wxEVT_TITLE_REMOVED); - cmdEvt->SetClientObject(new wxCustomData(entry)); - wxQueueEvent(this, cmdEvt); - } -} - -void wxTitleManagerList::HandleSaveListCallback(struct CafeSaveListCallbackEvent* evt) -{ - if (evt->eventType == CafeSaveListCallbackEvent::TYPE::SAVE_DISCOVERED) - { - ParsedMetaXml* metaInfo = evt->saveInfo->GetMetaInfo(); - if (!metaInfo) - return; - auto& saveInfo = *evt->saveInfo; - wxTitleManagerList::TitleEntry entry(EntryType::Save, EntryFormat::Folder, saveInfo.GetPath()); - entry.location_uid = std::hash() ( metaInfo->GetTitleId() ); - entry.title_id = metaInfo->GetTitleId(); - std::string name = metaInfo->GetLongName(GetConfig().console_language.GetValue()); - const auto nl = name.find(L'\n'); - if (nl != std::string::npos) - name.replace(nl, 1, " - "); - entry.name = wxString::FromUTF8(name); - entry.version = metaInfo->GetTitleVersion(); - entry.region = metaInfo->GetRegion(); - - auto* cmdEvt = new wxCommandEvent(wxEVT_TITLE_FOUND); - cmdEvt->SetClientObject(new wxCustomData(entry)); - wxQueueEvent(this, cmdEvt); - } -} - -void wxTitleManagerList::OnTitleDiscovered(wxCommandEvent& event) -{ - auto* obj = dynamic_cast(event.GetClientObject()); - wxASSERT(obj); - AddTitle(obj); -} - -void wxTitleManagerList::OnTitleRemoved(wxCommandEvent& event) -{ - auto* obj = dynamic_cast(event.GetClientObject()); - wxASSERT(obj); - for (auto& itr : m_data) - { - if (itr.get()->entry.location_uid == obj->get().location_uid) - { - RemoveItem(itr.get()->entry); - break; - } - } -} - -void wxTitleManagerList::AddTitle(TitleEntryData_t* obj) -{ - const auto& data = obj->GetData(); - if (GetTitleEntryByUID(data.location_uid).has_value()) - return; // already in list - m_data.emplace_back(std::make_unique(true, data)); - m_sorted_data.emplace_back(*m_data[m_data.size() - 1]); - SetItemCount(m_data.size()); -} - -int wxTitleManagerList::AddImage(const wxImage& image) const -{ - return -1; // m_image_list->Add(image.Scale(kListIconWidth, kListIconWidth, wxIMAGE_QUALITY_BICUBIC)); -} - -// return < -bool wxTitleManagerList::SortFunc(int column, const Type_t& v1, const Type_t& v2) -{ - // last sort option - if (column == -1) - return v1.get().entry.path.compare(v2.get().entry.path) < 0; - - // visible have always priority - if (!v1.get().visible && v2.get().visible) - return false; - else if (v1.get().visible && !v2.get().visible) - return true; - - const auto& entry1 = v1.get().entry; - const auto& entry2 = v2.get().entry; - - // check column: title id -> type -> path - if (column == ColumnTitleId) - { - // ensure strong ordering -> use type since only one entry should be now (should be changed if every save for every user is displayed separately?) - if (entry1.title_id == entry2.title_id) - return SortFunc(ColumnType, v1, v2); - - return entry1.title_id < entry2.title_id; - } - else if (column == ColumnName) - { - const int tmp = entry1.name.CmpNoCase(entry2.name); - if(tmp == 0) - return SortFunc(ColumnTitleId, v1, v2); - - return tmp < 0; - } - else if (column == ColumnType) - { - if(entry1.type == entry2.type) - return SortFunc(-1, v1, v2); - - return std::underlying_type_t(entry1.type) < std::underlying_type_t(entry2.type); - } - else if (column == ColumnVersion) - { - if(entry1.version == entry2.version) - return SortFunc(ColumnTitleId, v1, v2); - - return entry1.version < entry2.version; - } - else if (column == ColumnRegion) - { - if(entry1.region == entry2.region) - return SortFunc(ColumnTitleId, v1, v2); - - return std::underlying_type_t(entry1.region) < std::underlying_type_t(entry2.region); - } - else if (column == ColumnFormat) - { - if(entry1.format == entry2.format) - return SortFunc(ColumnType, v1, v2); - - return std::underlying_type_t(entry1.format) < std::underlying_type_t(entry2.format); - } - - return false; -} -void wxTitleManagerList::SortEntries(int column) -{ - bool ascending; - if (column == -1) - { - column = GetSortIndicator(); - if (column == -1) - column = ColumnTitleId; - ascending = IsAscendingSortIndicator(); - } - else - ascending = GetUpdatedAscendingSortIndicator(column); - - if (column != ColumnTitleId && column != ColumnName && column != ColumnType && column != ColumnVersion && column != ColumnRegion && column != ColumnFormat) - return; - - std::sort(m_sorted_data.begin(), m_sorted_data.end(), - [this, column, ascending](const Type_t& v1, const Type_t& v2) -> bool { - return ascending ? SortFunc(column, v1, v2) : SortFunc(column, v2, v1); - }); - - ShowSortIndicator(column, ascending); - RefreshPage(); -} - -void wxTitleManagerList::RefreshPage() -{ - long item_count = GetItemCount(); - - if (item_count > 0) - RefreshItems(GetTopItem(), std::min(item_count - 1, GetTopItem() + GetCountPerPage() + 1)); -} - -int wxTitleManagerList::Filter(const wxString& filter, const wxString& prefix, ItemColumn column) -{ - if (prefix.empty()) - return -1; - - if (!filter.StartsWith(prefix)) - return -1; - - int counter = 0; - const auto tmp_filter = filter.substr(prefix.size() - 1).Trim(false); - for (auto&& data : m_data) - { - if (GetTitleEntryText(data->entry, column).Upper().Contains(tmp_filter)) - { - data->visible = true; - ++counter; - } - else - data->visible = false; - } - return counter; -} - -void wxTitleManagerList::Filter(const wxString& filter) -{ - if(filter.empty()) - { - std::for_each(m_data.begin(), m_data.end(), [](ItemDataPtr& data) { data->visible = true; }); - SetItemCount(m_data.size()); - RefreshPage(); - return; - } - - const auto filter_upper = filter.Upper().Trim(false).Trim(true); - int counter = 0; - - if (const auto result = Filter(filter_upper, "TITLEID:", ColumnTitleId) != -1) - counter = result; - else if (const auto result = Filter(filter_upper, "NAME:", ColumnName) != -1) - counter = result; - else if (const auto result = Filter(filter_upper, "TYPE:", ColumnType) != -1) - counter = result; - else if (const auto result = Filter(filter_upper, "REGION:", ColumnRegion) != -1) - counter = result; - else if (const auto result = Filter(filter_upper, "VERSION:", ColumnVersion) != -1) - counter = result; - else if (const auto result = Filter(filter_upper, "FORMAT:", ColumnFormat) != -1) - counter = result; - else if(filter_upper == "ERROR") - { - for (auto&& data : m_data) - { - bool visible = true; - data->visible = visible; - if (visible) - ++counter; - } - } - else - { - for (auto&& data : m_data) - { - bool visible = false; - if (data->entry.name.Upper().Contains(filter_upper)) - visible = true; - else if (GetTitleEntryText(data->entry, ColumnTitleId).Upper().Contains(filter_upper)) - visible = true; - else if (GetTitleEntryText(data->entry, ColumnType).Upper().Contains(filter_upper)) - visible = true; - - data->visible = visible; - if (visible) - ++counter; - } - } - - SetItemCount(counter); - RefreshPage(); -} - -size_t wxTitleManagerList::GetCountByType(EntryType type) const -{ - size_t result = 0; - for(const auto& data : m_data) - { - if (data->entry.type == type) - ++result; - } - return result; -} - -void wxTitleManagerList::ClearItems() -{ - m_sorted_data.clear(); - m_data.clear(); - SetItemCount(0); - RefreshPage(); -} - -void wxTitleManagerList::AutosizeColumns() -{ - wxAutosizeColumns(this, ColumnTitleId, ColumnMAX - 1); -} diff --git a/src/gui/wxgui/components/wxTitleManagerList.h b/src/gui/wxgui/components/wxTitleManagerList.h deleted file mode 100644 index 5781e3bf..00000000 --- a/src/gui/wxgui/components/wxTitleManagerList.h +++ /dev/null @@ -1,139 +0,0 @@ -#pragma once - -#include "wxgui/helpers/wxCustomData.h" -#include "config/CemuConfig.h" - -#include - -#include // std::optional doesn't support optional reference inner types yet -#include -#include - -class wxTitleManagerList : public wxListView -{ - friend class TitleManager; -public: - wxTitleManagerList(wxWindow* parent, wxWindowID id = wxID_ANY); - ~wxTitleManagerList(); - - enum ItemColumn - { - ColumnTitleId = 0, - ColumnName, - ColumnType, - ColumnVersion, - ColumnRegion, - ColumnFormat, - ColumnLocation, - - ColumnMAX, - }; - - enum class EntryType - { - Base, - Update, - Dlc, - Save, - System, - }; - - enum class EntryFormat - { - Folder, - WUD, - NUS, - WUA, - WUHB, - }; - - // sort by column, if -1 will sort by last column or default (=titleid) - void SortEntries(int column); - void RefreshPage(); - void Filter(const wxString& filter); - [[nodiscard]] size_t GetCountByType(EntryType type) const; - void ClearItems(); - void AutosizeColumns(); - - struct TitleEntry - { - TitleEntry(const EntryType& type, const EntryFormat& format, fs::path path) - : type(type), format(format), path(std::move(path)) {} - - EntryType type; - EntryFormat format; - fs::path path; - - int icon = -1; - uint64 location_uid; - uint64 title_id; - wxString name; - uint32_t version = 0; - CafeConsoleRegion region; - - std::vector persistent_ids; // only used for save - }; - boost::optional GetSelectedTitleEntry() const; - -private: - void AddColumns(); - int Filter(const wxString& filter, const wxString& prefix, ItemColumn column); - boost::optional GetSelectedTitleEntry(); - boost::optional GetTitleEntryByUID(uint64 uid); - - class wxPanel* m_tooltip_window; - class wxStaticText* m_tooltip_text; - class wxTimer* m_tooltip_timer; - - void OnClose(wxCloseEvent& event); - void OnColumnClick(wxListEvent& event); - void OnContextMenu(wxContextMenuEvent& event); - void OnItemSelected(wxListEvent& event); - void OnContextMenuSelected(wxCommandEvent& event); - void OnTimer(class wxTimerEvent& event); - void OnRemoveItem(wxCommandEvent& event); - void OnRemoveEntry(wxCommandEvent& event); - - void OnTitleDiscovered(wxCommandEvent& event); - void OnTitleRemoved(wxCommandEvent& event); - void HandleTitleListCallback(struct CafeTitleListCallbackEvent* evt); - void HandleSaveListCallback(struct CafeSaveListCallbackEvent* evt); - - using TitleEntryData_t = wxCustomData; - void AddTitle(TitleEntryData_t* obj); - int AddImage(const wxImage& image) const; - wxString OnGetItemText(long item, long column) const override; - wxItemAttr* OnGetItemAttr(long item) const override; - [[nodiscard]] boost::optional GetTitleEntry(long item) const; - [[nodiscard]] boost::optional GetTitleEntry(long item); - [[nodiscard]] boost::optional GetTitleEntry(const fs::path& path) const; - [[nodiscard]] boost::optional GetTitleEntry(const fs::path& path); - - void OnConvertToCompressedFormat(uint64 titleId, uint64 rightClickedUID); - bool DeleteEntry(long index, const TitleEntry& entry); - - void RemoveItem(long item); - void RemoveItem(const TitleEntry& entry); - - struct ItemData - { - ItemData(bool visible, const TitleEntry& entry) - : visible(visible), entry(entry) {} - - bool visible; - TitleEntry entry; - }; - using ItemDataPtr = std::unique_ptr; - std::vector m_data; - std::vector> m_sorted_data; - - using Type_t = std::reference_wrapper; - bool SortFunc(int column, const Type_t& v1, const Type_t& v2); - - static wxString GetTitleEntryText(const TitleEntry& entry, ItemColumn column); - static wxString GetTranslatedTitleEntryType(EntryType entryType); - std::future m_context_worker; - - uint64 m_callbackIdTitleList; - uint64 m_callbackIdSaveList; -}; diff --git a/src/gui/wxgui/debugger/BreakpointWindow.cpp b/src/gui/wxgui/debugger/BreakpointWindow.cpp deleted file mode 100644 index 8dc449d9..00000000 --- a/src/gui/wxgui/debugger/BreakpointWindow.cpp +++ /dev/null @@ -1,321 +0,0 @@ -#include "wxgui/wxgui.h" -#include "wxgui/debugger/BreakpointWindow.h" - -#include - -#include "wxgui/debugger/DebuggerWindow2.h" -#include "Cafe/HW/Espresso/Debugger/Debugger.h" - -#include "Cemu/ExpressionParser/ExpressionParser.h" - -enum -{ - MENU_ID_CREATE_CODE_BP_EXECUTION = 1, - MENU_ID_CREATE_CODE_BP_LOGGING, - MENU_ID_CREATE_MEM_BP_READ, - MENU_ID_CREATE_MEM_BP_WRITE, - MENU_ID_DELETE_BP, -}; - -enum ItemColumns -{ - ColumnEnabled = 0, - ColumnAddress, - ColumnType, - ColumnComment, -}; - -BreakpointWindow::BreakpointWindow(DebuggerWindow2& parent, const wxPoint& main_position, const wxSize& main_size) - : wxFrame(&parent, wxID_ANY, _("Breakpoints"), wxDefaultPosition, wxSize(420, 250), wxSYSTEM_MENU | wxCAPTION | wxCLIP_CHILDREN | wxRESIZE_BORDER | wxFRAME_FLOAT_ON_PARENT) -{ - this->SetSizeHints(wxDefaultSize, wxDefaultSize); - - wxBoxSizer* main_sizer = new wxBoxSizer(wxVERTICAL); - - m_breakpoints = new wxListView(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLC_REPORT | wxLC_SINGLE_SEL); - m_breakpoints->EnableCheckBoxes(true); - - wxListItem col0; - col0.SetId(ColumnEnabled); - col0.SetText(_("On")); - col0.SetWidth(32); - m_breakpoints->InsertColumn(ColumnEnabled, col0); - - wxListItem col1; - col1.SetId(ColumnAddress); - col1.SetText(_("Address")); - col1.SetWidth(75); - m_breakpoints->InsertColumn(ColumnAddress, col1); - - wxListItem col2; - col2.SetId(ColumnType); - col2.SetText(_("Type")); - col2.SetWidth(42); - m_breakpoints->InsertColumn(ColumnType, col2); - - wxListItem col3; - col3.SetId(ColumnComment); - col3.SetText(_("Comment")); - col3.SetWidth(250); - m_breakpoints->InsertColumn(ColumnComment, col3); - - main_sizer->Add(m_breakpoints, 1, wxEXPAND); - - this->SetSizer(main_sizer); - this->wxWindowBase::Layout(); - - this->Centre(wxBOTH); - - if (parent.GetConfig().data().pin_to_main) - OnMainMove(main_position, main_size); - - m_breakpoints->Bind(wxEVT_LIST_ITEM_CHECKED, &BreakpointWindow::OnBreakpointToggled, this); - m_breakpoints->Bind(wxEVT_LIST_ITEM_UNCHECKED, &BreakpointWindow::OnBreakpointToggled, this); - m_breakpoints->Bind(wxEVT_LEFT_DCLICK, &BreakpointWindow::OnLeftDClick, this); - m_breakpoints->Bind(wxEVT_RIGHT_DOWN, &BreakpointWindow::OnRightDown, this); - - OnUpdateView(); -} - -BreakpointWindow::~BreakpointWindow() -{ - m_breakpoints->Unbind(wxEVT_LIST_ITEM_CHECKED, &BreakpointWindow::OnBreakpointToggled, this); - m_breakpoints->Unbind(wxEVT_LIST_ITEM_UNCHECKED, &BreakpointWindow::OnBreakpointToggled, this); - m_breakpoints->Unbind(wxEVT_LEFT_DCLICK, &BreakpointWindow::OnLeftDClick, this); - m_breakpoints->Unbind(wxEVT_RIGHT_DOWN, &BreakpointWindow::OnRightDown, this); -} - -void BreakpointWindow::OnMainMove(const wxPoint& main_position, const wxSize& main_size) -{ - wxSize size(420, 250); - this->SetSize(size); - - wxPoint position = main_position; - position.x -= 420; - position.y += main_size.y - 250; - this->SetPosition(position); -} - -void BreakpointWindow::OnUpdateView() -{ - Freeze(); - - m_breakpoints->DeleteAllItems(); - std::vector& breakpoints = debugger_lockBreakpoints(); - - uint32 i = 0; - for (const auto bpBase : breakpoints) - { - DebuggerBreakpoint* bp = bpBase; - while (bp) - { - wxListItem item = {}; - item.SetId(i++); - - const auto index = m_breakpoints->InsertItem(item); - m_breakpoints->SetItemPtrData(index, (uintptr_t)bp->id); - m_breakpoints->SetItem(index, ColumnAddress, wxString::Format("%08x", bp->address)); - const char* typeName = "UKN"; - if (bp->bpType == DEBUGGER_BP_T_NORMAL) - typeName = "X"; - else if (bp->bpType == DEBUGGER_BP_T_LOGGING) - typeName = "LOG"; - else if (bp->bpType == DEBUGGER_BP_T_ONE_SHOT) - typeName = "XS"; - else if (bp->bpType == DEBUGGER_BP_T_MEMORY_READ) - typeName = "R"; - else if (bp->bpType == DEBUGGER_BP_T_MEMORY_WRITE) - typeName = "W"; - - m_breakpoints->SetItem(index, ColumnType, typeName); - m_breakpoints->SetItem(index, ColumnComment, bp->comment); - m_breakpoints->CheckItem(index, bp->enabled); - - bp = bp->next; - } - } - debugger_unlockBreakpoints(); - Thaw(); -} - -void BreakpointWindow::OnGameLoaded() -{ - OnUpdateView(); -} - -void BreakpointWindow::OnBreakpointToggled(wxListEvent& event) -{ - const sint32 index = event.GetIndex(); - uint64 bpId = (uint64)event.GetData(); - debugger_lockBreakpoints(); - DebuggerBreakpoint* bp = debugger_getBreakpointById(bpId); - if (bp) - { - const bool state = m_breakpoints->IsItemChecked(index); - debugger_toggleBreakpoint(bp->address, state, bp); - } - debugger_unlockBreakpoints(); -} - -void BreakpointWindow::OnLeftDClick(wxMouseEvent& event) -{ - const auto position = event.GetPosition(); - int flags = 0; - const long index = m_breakpoints->HitTest(position, flags); - - if (index == wxNOT_FOUND) - return; - - sint32 x = position.x; - const auto enabled_width = m_breakpoints->GetColumnWidth(ColumnEnabled); - if (x <= enabled_width) - return; - - x -= enabled_width; - const auto address_width = m_breakpoints->GetColumnWidth(ColumnAddress); - if (x <= address_width) - { - const auto item = m_breakpoints->GetItemText(index, ColumnAddress); - const auto address = std::stoul(item.ToStdString(), nullptr, 16); - g_debuggerDispatcher.MoveToAddressInDisassembly(address); - return; - } - - x -= address_width; - const auto type_width = m_breakpoints->GetColumnWidth(ColumnType); - if (x <= type_width) - return; - - x -= type_width; - const auto comment_width = m_breakpoints->GetColumnWidth(ColumnComment); - if (x <= comment_width) - { - std::vector& breakpoints = debugger_lockBreakpoints(); - if (index >= breakpoints.size()) - { - debugger_unlockBreakpoints(); - return; - } - DebuggerBreakpoint* bp = breakpoints[index]; - auto bpId = bp->id; - - const auto item = m_breakpoints->GetItemText(index, ColumnAddress); - const auto address = std::stoul(item.ToStdString(), nullptr, 16); - - bool isLoggingBP = bp->bpType == DEBUGGER_BP_T_LOGGING; - const wxString dialogTitle = isLoggingBP ? _("Enter a new logging message") : _("Enter a new comment"); - const wxString dialogMessage = isLoggingBP ? _("Set logging message when code at address %08x is ran.\nUse placeholders like {r3} or {f3} to log register values") : _("Set comment for breakpoint at address %08x"); - const wxString existingComment = bp->comment; - debugger_unlockBreakpoints(); - wxTextEntryDialog set_comment_dialog(this, dialogMessage, dialogTitle, existingComment); - if (set_comment_dialog.ShowModal() == wxID_OK) - { - if (isLoggingBP) - { - debugger_lockBreakpoints(); - DebuggerBreakpoint* bp = debugger_getBreakpointById(bpId); - if (bp) - { - bp->comment = set_comment_dialog.GetValue().ToStdWstring(); - m_breakpoints->SetItem(index, ColumnComment, set_comment_dialog.GetValue()); - } - debugger_unlockBreakpoints(); - } - } - } -} - -void BreakpointWindow::OnRightDown(wxMouseEvent& event) -{ - int flags = 0; - const long index = m_breakpoints->HitTest(event.GetPosition(), flags); - if (index == wxNOT_FOUND || index < 0 || index >= m_breakpoints->GetItemCount()) - { - wxMenu menu; - menu.Append(MENU_ID_CREATE_CODE_BP_EXECUTION, _("Create execution breakpoint")); - menu.Append(MENU_ID_CREATE_CODE_BP_LOGGING, _("Create logging breakpoint")); - menu.Append(MENU_ID_CREATE_MEM_BP_READ, _("Create memory breakpoint (read)")); - menu.Append(MENU_ID_CREATE_MEM_BP_WRITE, _("Create memory breakpoint (write)")); - - menu.Bind(wxEVT_COMMAND_MENU_SELECTED, &BreakpointWindow::OnContextMenuClick, this); - PopupMenu(&menu); - } - else - { - m_breakpoints->Focus(index); - m_breakpoints->Select(index); - - wxMenu menu; - menu.Append(MENU_ID_DELETE_BP, _("Delete breakpoint")); - - menu.Bind(wxEVT_COMMAND_MENU_SELECTED, &BreakpointWindow::OnContextMenuClickSelected, this); - PopupMenu(&menu); - } -} - -void BreakpointWindow::OnContextMenuClickSelected(wxCommandEvent& evt) -{ - auto& breakpoints = debugger_lockBreakpoints(); - long selectedIndex = m_breakpoints->GetFirstSelected(); - if (selectedIndex == wxNOT_FOUND || selectedIndex < 0 || selectedIndex >= (long)breakpoints.size()) - { - debugger_unlockBreakpoints(); - return; - } - BreakpointId bpId = (uint64)m_breakpoints->GetItemData(selectedIndex); - DebuggerBreakpoint* bp = debugger_getBreakpointById(bpId); - if (!bp) - { - debugger_unlockBreakpoints(); - return; - } - if (evt.GetId() == MENU_ID_DELETE_BP) - { - debugger_deleteBreakpoint(bpId); - wxCommandEvent evt(wxEVT_BREAKPOINT_CHANGE); - wxPostEvent(this->m_parent, evt); - } - debugger_unlockBreakpoints(); -} - -void BreakpointWindow::OnContextMenuClick(wxCommandEvent& evt) -{ - wxTextEntryDialog goto_dialog(this, _("Enter a memory address"), _("Set breakpoint"), wxEmptyString); - if (goto_dialog.ShowModal() == wxID_OK) - { - ExpressionParser parser; - - auto value = goto_dialog.GetValue().ToStdString(); - std::transform(value.begin(), value.end(), value.begin(), tolower); - - uint32_t newBreakpointAddress = 0; - try - { - debugger_addParserSymbols(parser); - newBreakpointAddress = parser.IsConstantExpression("0x"+value) ? (uint32)parser.Evaluate("0x"+value) : (uint32)parser.Evaluate(value); - } - catch (const std::exception& ex) - { - wxMessageBox(ex.what(), _("Error"), wxOK | wxCENTRE | wxICON_ERROR, this); - return; - } - - switch (evt.GetId()) - { - case MENU_ID_CREATE_CODE_BP_EXECUTION: - debugger_createCodeBreakpoint(newBreakpointAddress, DEBUGGER_BP_T_NORMAL); - break; - case MENU_ID_CREATE_CODE_BP_LOGGING: - debugger_createCodeBreakpoint(newBreakpointAddress, DEBUGGER_BP_T_LOGGING); - break; - case MENU_ID_CREATE_MEM_BP_READ: - debugger_createMemoryBreakpoint(newBreakpointAddress, true, false); - break; - case MENU_ID_CREATE_MEM_BP_WRITE: - debugger_createMemoryBreakpoint(newBreakpointAddress, false, true); - break; - } - - this->OnUpdateView(); - } -} diff --git a/src/gui/wxgui/debugger/BreakpointWindow.h b/src/gui/wxgui/debugger/BreakpointWindow.h deleted file mode 100644 index 330247e0..00000000 --- a/src/gui/wxgui/debugger/BreakpointWindow.h +++ /dev/null @@ -1,28 +0,0 @@ -#pragma once - -#include - -class DebuggerWindow2; -class wxListEvent; -class wxListView; - -class BreakpointWindow : public wxFrame -{ -public: - BreakpointWindow(DebuggerWindow2& parent, const wxPoint& main_position, const wxSize& main_size); - virtual ~BreakpointWindow(); - - void OnMainMove(const wxPoint& position, const wxSize& main_size); - void OnUpdateView(); - void OnGameLoaded(); - -private: - void OnBreakpointToggled(wxListEvent& event); - void OnLeftDClick(wxMouseEvent& event); - void OnRightDown(wxMouseEvent& event); - - void OnContextMenuClick(wxCommandEvent& evt); - void OnContextMenuClickSelected(wxCommandEvent& evt); - - wxListView* m_breakpoints; -}; diff --git a/src/gui/wxgui/debugger/DebuggerWindow2.cpp b/src/gui/wxgui/debugger/DebuggerWindow2.cpp deleted file mode 100644 index b3bb5105..00000000 --- a/src/gui/wxgui/debugger/DebuggerWindow2.cpp +++ /dev/null @@ -1,936 +0,0 @@ -#include "wxgui/wxgui.h" -#include "wxgui/debugger/DebuggerWindow2.h" - -#include "wxHelper.h" - -#include "Common/FileStream.h" -#include "config/ActiveSettings.h" -#include "Cafe/HW/MMU/MMU.h" -#include "Cafe/OS/RPL/rpl_structs.h" -#include "Cafe/OS/RPL/rpl_debug_symbols.h" -#include "Cafe/OS/RPL/rpl_symbol_storage.h" - -#include "wxgui/debugger/RegisterWindow.h" -#include "wxgui/debugger/DumpWindow.h" - -#include "Cafe/HW/Espresso/Debugger/Debugger.h" - -#include "Cafe/OS/RPL/rpl.h" -#include "wxgui/debugger/DisasmCtrl.h" -#include "wxgui/debugger/SymbolWindow.h" -#include "wxgui/debugger/BreakpointWindow.h" -#include "wxgui/debugger/ModuleWindow.h" -#include "util/helpers/helpers.h" -#include "util/helpers/StringHelpers.h" -#include "Cafe/HW/Espresso/Recompiler/PPCRecompiler.h" -#include "Cemu/Logging/CemuLogging.h" - -#include "resource/embedded/resources.h" - -enum -{ - // file - MENU_ID_FILE_EXIT = wxID_HIGHEST + 8000, - // settings - MENU_ID_OPTIONS_PIN_TO_MAINWINDOW, - MENU_ID_OPTIONS_BREAK_ON_START, - MENU_ID_OPTIONS_LOG_MEMORY_BREAKPOINTS, - MENU_ID_OPTIONS_SWITCH_CPU_MODE, - // window - MENU_ID_WINDOW_REGISTERS, - MENU_ID_WINDOW_DUMP, - MENU_ID_WINDOW_STACK, - MENU_ID_WINDOW_BREAKPOINTS, - MENU_ID_WINDOW_MODULE, - MENU_ID_WINDOW_SYMBOL, - - // tool - TOOL_ID_GOTO, - TOOL_ID_BP, - TOOL_ID_PAUSE, - TOOL_ID_STEP_OVER, - TOOL_ID_STEP_INTO, -}; - -wxDEFINE_EVENT(wxEVT_DEBUGGER_CLOSE, wxCloseEvent); -wxDEFINE_EVENT(wxEVT_UPDATE_VIEW, wxCommandEvent); -wxDEFINE_EVENT(wxEVT_BREAKPOINT_CHANGE, wxCommandEvent); -wxDEFINE_EVENT(wxEVT_BREAKPOINT_HIT, wxCommandEvent); -wxDEFINE_EVENT(wxEVT_MOVE_TO_DISASM_ADDR, wxCommandEvent); -wxDEFINE_EVENT(wxEVT_RUN, wxCommandEvent); -wxDEFINE_EVENT(wxEVT_NOTIFY_MODULE_LOADED, wxCommandEvent); -wxDEFINE_EVENT(wxEVT_NOTIFY_MODULE_UNLOADED, wxCommandEvent); -wxDEFINE_EVENT(wxEVT_NOTIFY_GRAPHIC_PACKS_MODIFIED, wxCommandEvent); - -wxBEGIN_EVENT_TABLE(DebuggerWindow2, wxFrame) - EVT_SHOW(DebuggerWindow2::OnShow) - EVT_CLOSE(DebuggerWindow2::OnClose) - EVT_COMMAND(wxID_ANY, wxEVT_UPDATE_VIEW, DebuggerWindow2::OnUpdateView) - EVT_COMMAND(wxID_ANY, wxEVT_BREAKPOINT_CHANGE, DebuggerWindow2::OnBreakpointChange) - EVT_COMMAND(wxID_ANY, wxEVT_MOVE_TO_DISASM_ADDR, DebuggerWindow2::OnMoveToDisasmAddr) - EVT_COMMAND(wxID_ANY, wxEVT_COMMAND_TOOL_CLICKED, DebuggerWindow2::OnToolClicked) - EVT_COMMAND(wxID_ANY, wxEVT_BREAKPOINT_HIT, DebuggerWindow2::OnBreakpointHit) - EVT_COMMAND(wxID_ANY, wxEVT_RUN, DebuggerWindow2::OnRunProgram) - EVT_COMMAND(wxID_ANY, wxEVT_NOTIFY_MODULE_LOADED, DebuggerWindow2::OnNotifyModuleLoaded) - EVT_COMMAND(wxID_ANY, wxEVT_NOTIFY_MODULE_UNLOADED, DebuggerWindow2::OnNotifyModuleUnloaded) - EVT_COMMAND(wxID_ANY, wxEVT_NOTIFY_GRAPHIC_PACKS_MODIFIED, DebuggerWindow2::OnNotifyGraphicPacksModified) - EVT_COMMAND(wxID_ANY, wxEVT_DISASMCTRL_NOTIFY_GOTO_ADDRESS, DebuggerWindow2::OnDisasmCtrlGotoAddress) - // file menu - EVT_MENU(MENU_ID_FILE_EXIT, DebuggerWindow2::OnExit) - // window - EVT_MENU_RANGE(MENU_ID_WINDOW_REGISTERS, MENU_ID_WINDOW_MODULE, DebuggerWindow2::OnWindowMenu) -wxEND_EVENT_TABLE() - -DebuggerModuleInfo::DebuggerModuleInfo(RPLModule* module) -{ - moduleName = module->moduleName; - patchCRC = module->patchCRC; - textArea.base = module->regionMappingBase_text.GetMPTR(); - textArea.origBase = module->regionOrigAddr_text; - textArea.size = module->regionSize_text; - dataArea.base = module->regionMappingBase_data; - dataArea.origBase = module->regionOrigAddr_data; - dataArea.size = module->regionSize_data; -} - -struct DebuggerModuleInfoNotify : public wxClientData -{ - DebuggerModuleInfo moduleInfo; - - DebuggerModuleInfoNotify(RPLModule* module) : moduleInfo(module) {}; -}; - -DebuggerWindow2* s_debuggerWindow; - -static bool TryParseMapAddress(std::string_view token, uint32& value) -{ - trim(token, "\t\n\v\f\r ,:;()[]"); - if (const auto colonIndex = token.find_last_of(':'); colonIndex != std::string_view::npos) - token = token.substr(colonIndex + 1); - trim(token, "\t\n\v\f\r ,:;()[]"); - if (!token.empty() && (token.back() == 'h' || token.back() == 'H')) - token.remove_suffix(1); - if (token.size() > 2 && token[0] == '0' && (token[1] == 'x' || token[1] == 'X')) - token.remove_prefix(2); - if (token.empty()) - return false; - - uint64 parsedValue = 0; - const auto result = std::from_chars(token.data(), token.data() + token.size(), parsedValue, 16); - if (result.ec != std::errc() || result.ptr != (token.data() + token.size()) || parsedValue > std::numeric_limits::max()) - return false; - - value = static_cast(parsedValue); - return true; -} - -static bool TryParseMapSymbolLine(std::string_view rawLine, uint32& address, std::string& symbolName) -{ - auto line = rawLine; - trim(line); - if (line.empty()) - return false; - - const auto addressEnd = line.find_first_of(" \t"); - if (addressEnd == std::string_view::npos) - return false; - if (!TryParseMapAddress(line.substr(0, addressEnd), address)) - return false; - - line = line.substr(addressEnd + 1); - trim(line); - if (line.empty()) - return false; - - const auto symbolEnd = line.find_first_of(" \t"); - auto symbolToken = symbolEnd == std::string_view::npos ? line : line.substr(0, symbolEnd); - trim(symbolToken); - if (symbolToken.empty()) - return false; - - symbolName.assign(symbolToken); - return true; -} - -static std::optional GetRelocatedMapAddress(const DebuggerModuleInfo& moduleInfo, uint32 mapAddress) -{ - if (moduleInfo.textArea.ContainsOriginalAddress(mapAddress)) - return moduleInfo.textArea.base + (mapAddress - moduleInfo.textArea.origBase); - if (moduleInfo.dataArea.ContainsOriginalAddress(mapAddress)) - return moduleInfo.dataArea.base + (mapAddress - moduleInfo.dataArea.origBase); - return std::nullopt; -} - -void DebuggerConfig::Load(XMLConfigParser& parser) -{ - pin_to_main = parser.get("PinToMainWindow", true); - break_on_start = parser.get("break_on_start", true); - log_memory_breakpoints = parser.get("log_memory_breakpoints", false); - - auto window_parser = parser.get("Windows"); - show_register = window_parser.get("Registers", true); - show_dump = window_parser.get("MemoryDump", true); - show_stack = window_parser.get("Stack", true); - show_breakpoints = window_parser.get("Breakpoints", true); - show_modules = window_parser.get("Modules", true); - show_symbols = window_parser.get("Symbols", true); -} - -void DebuggerConfig::Save(XMLConfigParser& parser) -{ - parser.set("PinToMainWindow", pin_to_main); - parser.set("break_on_start", break_on_start); - parser.set("log_memory_breakpoints", log_memory_breakpoints); - - auto window_parser = parser.set("Windows"); - window_parser.set("Registers", show_register); - window_parser.set("MemoryDump", show_dump); - window_parser.set("Stack", show_stack); - window_parser.set("Breakpoints", show_breakpoints); - window_parser.set("Modules", show_modules); - window_parser.set("Symbols", show_symbols); -} - -void DebuggerModuleStorage::Load(XMLConfigParser& parser) -{ - auto breakpoints_parser = parser.get("Breakpoints"); - for (auto element = breakpoints_parser.get("Entry"); element.valid(); element = breakpoints_parser.get("Entry", element)) - { - const auto address_string = element.get("Address", ""); - if (*address_string == '\0') - continue; - - uint32 relative_address = std::strtoul(address_string, nullptr, 16); - if (relative_address == 0) - continue; - - auto type = element.get("Type", 0); - auto enabled = element.get("Enabled", true); - const auto comment = element.get("Comment", ""); - - // calculate absolute address - uint32 module_base_address = (type == DEBUGGER_BP_T_NORMAL || type == DEBUGGER_BP_T_LOGGING) ? this->moduleInfo.textArea.base : this->moduleInfo.dataArea.base; - uint32 address = module_base_address + relative_address; - - // don't change anything if there's already a breakpoint - if (debugger_getFirstBP(address) != nullptr) - continue; - - // register breakpoints in debugger - if (type == DEBUGGER_BP_T_NORMAL) - debugger_createCodeBreakpoint(address, DEBUGGER_BP_T_NORMAL); - else if (type == DEBUGGER_BP_T_LOGGING) - debugger_createCodeBreakpoint(address, DEBUGGER_BP_T_LOGGING); - else if (type == DEBUGGER_BP_T_MEMORY_READ) - debugger_createMemoryBreakpoint(address, true, false); - else if (type == DEBUGGER_BP_T_MEMORY_WRITE) - debugger_createMemoryBreakpoint(address, false, true); - else - continue; - - DebuggerBreakpoint* debugBreakpoint = debugger_getFirstBP(address); - - if (!enabled) - debugger_toggleBreakpoint(address, false, debugBreakpoint); - - debugBreakpoint->comment = boost::nowide::widen(comment); - } - - auto comments_parser = parser.get("Comments"); - for (XMLConfigParser element = comments_parser.get("Entry"); element.valid(); element = comments_parser.get("Entry", element)) - { - const auto address_string = element.get("Address", ""); - if (*address_string == '\0') - continue; - - uint32 address = std::strtoul(address_string, nullptr, 16); - if (address == 0) - continue; - - const auto comment = element.get("Comment", ""); - if (*comment == '\0') - continue; - - rplDebugSymbol_createComment(address, boost::nowide::widen(comment).c_str()); - } -} - -void DebuggerModuleStorage::Save(XMLConfigParser& parser) -{ - auto breakpoints_parser = parser.set("Breakpoints"); - auto& breakpoints = debugger_lockBreakpoints(); - for (const auto& bp : breakpoints) - { - // check breakpoint type - if (bp->dbType != DEBUGGER_BP_T_DEBUGGER) - continue; - - // check whether the breakpoint is part of the current module being saved - uint32 relativeAddress; - if (moduleInfo.textArea.ContainsAddress(bp->address)) - relativeAddress = bp->address - moduleInfo.textArea.base; - else if (moduleInfo.dataArea.ContainsAddress(bp->address)) - relativeAddress = bp->address - moduleInfo.dataArea.base; - else - continue; - - auto entry = breakpoints_parser.set("Entry"); - entry.set("Address", fmt::format("{:#10x}", relativeAddress)); - entry.set("Comment", boost::nowide::narrow(bp->comment).c_str()); - entry.set("Type", bp->bpType); - entry.set("Enabled", bp->enabled); - - if (this->delete_breakpoints_after_saving) - debugger_deleteBreakpoint(bp->id); - this->delete_breakpoints_after_saving = false; - } - debugger_unlockBreakpoints(); - - auto comments_parser = parser.set("Comments"); - for (const auto& comment_entry : rplDebugSymbol_getSymbols()) - { - // check comment type - const auto commentAddress = comment_entry.first; - const auto comment = static_cast(comment_entry.second); - if (!comment || comment->type != RplDebugSymbolComment) - continue; - - // check whether the comment is part of the current module being saved - uint32 relativeAddress; - if (moduleInfo.textArea.ContainsAddress(commentAddress)) - relativeAddress = commentAddress - moduleInfo.textArea.base; - else - continue; - - auto entry = comments_parser.set("Entry"); - entry.set("Address", fmt::format("{:#10x}", relativeAddress)); - entry.set("Comment", boost::nowide::narrow(comment->comment).c_str()); - } -} - -void DebuggerWindow2::CreateToolBar() -{ - m_toolbar = wxFrame::CreateToolBar(wxTB_HORIZONTAL, wxID_ANY); - m_toolbar->SetToolBitmapSize(wxSize(16, 16)); - - wxBitmap goto_bitmap = wxHelper::LoadThemedBitmapFromPNG(DEBUGGER_GOTO_png, sizeof(DEBUGGER_GOTO_png), wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT)); - m_toolbar->AddTool(TOOL_ID_GOTO, wxEmptyString, goto_bitmap, wxNullBitmap, wxITEM_NORMAL, _("GoTo (CTRL + G)"), "test", NULL); - m_toolbar->AddSeparator(); - - wxBitmap bp_bitmap = wxHelper::LoadThemedBitmapFromPNG(DEBUGGER_BP_RED_png, sizeof(DEBUGGER_BP_RED_png), wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT)); - m_toolbar->AddTool(TOOL_ID_BP, wxEmptyString, bp_bitmap, wxNullBitmap, wxITEM_NORMAL, _("Toggle Breakpoint (F9)"), wxEmptyString, NULL); - m_toolbar->AddSeparator(); - - m_pause = wxHelper::LoadThemedBitmapFromPNG(DEBUGGER_PAUSE_png, sizeof(DEBUGGER_PAUSE_png), wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT)); - m_run = wxHelper::LoadThemedBitmapFromPNG(DEBUGGER_PLAY_png, sizeof(DEBUGGER_PLAY_png), wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT)); - m_toolbar->AddTool(TOOL_ID_PAUSE, wxEmptyString, m_pause, wxNullBitmap, wxITEM_NORMAL, _("Break (F5)"), wxEmptyString, NULL); - - wxBitmap step_into_bitmap = wxHelper::LoadThemedBitmapFromPNG(DEBUGGER_STEP_INTO_png, sizeof(DEBUGGER_STEP_INTO_png), wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT)); - wxBitmap step_over_bitmap = wxHelper::LoadThemedBitmapFromPNG(DEBUGGER_STEP_OVER_png, sizeof(DEBUGGER_STEP_OVER_png), wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT)); - m_toolbar->AddTool(TOOL_ID_STEP_INTO, wxEmptyString, step_into_bitmap, wxNullBitmap, wxITEM_NORMAL, _("Step Into (F11)"), wxEmptyString, NULL); - m_toolbar->AddTool(TOOL_ID_STEP_OVER, wxEmptyString, step_over_bitmap, wxNullBitmap, wxITEM_NORMAL, _("Step Over (F10)"), wxEmptyString, NULL); - m_toolbar->AddSeparator(); - - m_toolbar->Realize(); - - m_toolbar->EnableTool(TOOL_ID_STEP_INTO, false); - m_toolbar->EnableTool(TOOL_ID_STEP_OVER, false); - -} - -void DebuggerWindow2::LoadModuleStorage(const DebuggerModuleInfo& moduleInfo) -{ - auto path = GetModuleStoragePath(moduleInfo.moduleName, moduleInfo.patchCRC); - bool already_loaded = std::any_of(m_modulesStorage.begin(), m_modulesStorage.end(), [path](const std::unique_ptr& debug) { return debug->GetFilename() == path; }); - if (!path.empty() && !already_loaded) - { - auto& moduleStorage = m_modulesStorage.emplace_back(new XMLDebuggerModuleConfig(path, { moduleInfo, false })); - moduleStorage->Load(); - LoadModuleMap(moduleStorage->data()); - } -} - -void DebuggerWindow2::SaveModuleStorage(const DebuggerModuleInfo& moduleInfo, bool deleteModuleStorage) -{ - auto path = GetModuleStoragePath(moduleInfo.moduleName, moduleInfo.patchCRC); - for (auto it = m_modulesStorage.begin(); it != m_modulesStorage.end(); ++it) - { - auto& moduleStorage = *it; - if (moduleStorage->data().moduleInfo.moduleName == moduleInfo.moduleName && moduleStorage->data().moduleInfo.patchCRC == moduleInfo.patchCRC) - { - moduleStorage->data().delete_breakpoints_after_saving = deleteModuleStorage; // make sure breakpoints are deleted when the storage is removed - moduleStorage->Save(path); - if (deleteModuleStorage) - { - UnloadModuleMap(moduleStorage->data()); - m_modulesStorage.erase(it); - } - return; - } - } -} - -void DebuggerWindow2::LoadModuleMap(DebuggerModuleStorage& moduleStorage) -{ - UnloadModuleMap(moduleStorage); - - const auto mapPath = GetModuleMapPath(moduleStorage.moduleInfo.moduleName, moduleStorage.moduleInfo.patchCRC); - if (mapPath.empty()) - return; - - std::unique_ptr fileStream(FileStream::openFile(mapPath.c_str())); - if (!fileStream) - return; - - const auto fileSize = fileStream->GetSize(); - if (fileSize == 0 || fileSize > std::numeric_limits::max()) - return; - - const auto readSize = static_cast(fileSize); - std::vector fileData(readSize); - if (fileStream->readData(fileData.data(), readSize) != readSize) - return; - - std::string_view fileView(fileData.data(), fileData.size()); - for (const auto& line : StringHelpers::StringLineIterator(fileView)) - { - uint32 mapAddress = 0; - std::string symbolName; - if (TryParseMapSymbolLine(line, mapAddress, symbolName)) - { - const auto relocatedAddress = GetRelocatedMapAddress(moduleStorage.moduleInfo, mapAddress); - if (relocatedAddress) - { - moduleStorage.loaded_map_symbols.emplace_back(rplSymbolStorage_store(moduleStorage.moduleInfo.moduleName.c_str(), symbolName.c_str(), *relocatedAddress, RPL_STORED_SYMBOL_MAP)); - } - } - } -} - -void DebuggerWindow2::UnloadModuleMap(DebuggerModuleStorage& moduleStorage) -{ - for (auto* symbol : moduleStorage.loaded_map_symbols) - { - if (symbol) - rplSymbolStorage_remove(symbol); - } - moduleStorage.loaded_map_symbols.clear(); -} - -DebuggerWindow2::DebuggerWindow2(wxFrame& parent, const wxRect& display_size) - : wxFrame(&parent, wxID_ANY, _("PPC Debugger"), wxDefaultPosition, wxSize(1280, 300), wxMINIMIZE_BOX | wxMAXIMIZE_BOX | wxSYSTEM_MENU | wxCAPTION | wxCLOSE_BOX | wxCLIP_CHILDREN | wxRESIZE_BORDER | wxFRAME_FLOAT_ON_PARENT), - m_module_address(0) -{ - g_debuggerDispatcher.SetDebuggerCallbacks(this); - - const auto file = ActiveSettings::GetConfigPath("debugger/config.xml"); - m_config.SetFilename(file.generic_wstring()); - m_config.Load(); - - debugger_setOptionBreakOnEntry(m_config.data().break_on_start); - debugger_setOptionLogOnlyMemoryBreakpoints(m_config.data().log_memory_breakpoints); - - m_main_position = parent.GetPosition(); - m_main_size = parent.GetSize(); - - auto y = std::max(300, (display_size.GetHeight() - 500 - 300) * 0.8); - this->SetSize(1280, y); - - CreateMenuBar(); - CreateToolBar(); - - wxBoxSizer* main_sizer = new wxBoxSizer(wxVERTICAL); - - // load configs and module storage for already loaded modules - const auto module_list = RPLLoader_GetModuleList(); - for (sint32 i = 0; i < module_list.size(); i++) - { - const auto module = module_list[i]; - DebuggerModuleInfo moduleInfo(module); - LoadModuleStorage(moduleInfo); - } - - wxString label_text = _("> no modules loaded"); - if (module_list.size() != 0) - { - RPLModule* currentModule = RPLLoader_FindModuleByCodeAddr(MEMORY_CODEAREA_ADDR); - if (currentModule) - label_text = wxString::Format("> %s", currentModule->moduleName.c_str()); - else - label_text = _("> unknown module"); - } - - m_module_label = new wxStaticText(this, wxID_ANY, label_text); - m_module_label->SetForegroundColour(wxColour(0xFFbf52fe)); - main_sizer->Add(m_module_label, 0, wxEXPAND | wxALL, 5); - - m_disasm_ctrl = new DisasmCtrl(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxScrolledWindowStyle); - main_sizer->Add(m_disasm_ctrl, 1, wxEXPAND); - - this->SetSizer(main_sizer); - this->wxWindowBase::Layout(); - - m_register_window = new RegisterWindow(*this, m_main_position, m_main_size); - m_dump_window = new DumpWindow(*this, m_main_position, m_main_size); - m_breakpoint_window = new BreakpointWindow(*this, m_main_position, m_main_size); - m_module_window = new ModuleWindow(*this, m_main_position, m_main_size); - m_symbol_window = new SymbolWindow(*this, m_main_position, m_main_size); - - const bool value = m_config.data().pin_to_main; - m_config.data().pin_to_main = true; - OnParentMove(m_main_position, m_main_size); - m_config.data().pin_to_main = value; - - s_debuggerWindow = this; -} - -DebuggerWindow2::~DebuggerWindow2() -{ - g_debuggerDispatcher.ClearDebuggerCallbacks(); - - s_debuggerWindow = nullptr; - - CleanupForDestroy(); - - if (m_register_window && m_register_window->IsShown()) - m_register_window->Close(true); - - if (m_dump_window && m_dump_window->IsShown()) - m_dump_window->Close(true); - - if (m_breakpoint_window && m_breakpoint_window->IsShown()) - m_breakpoint_window->Close(true); - - if (m_module_window && m_module_window->IsShown()) - m_module_window->Close(true); - - if (m_symbol_window && m_symbol_window->IsShown()) - m_symbol_window->Close(true); - - // reenable recompiler if force interpreter option was enabled - if (m_forceInterpreter) - { - PPCRecompiler_Enable(); - m_forceInterpreter = false; - cemuLog_log(LogType::Force, "Debugger: Switched CPU mode to recompiler"); - } -} - -// note: Can be called twice in some circumstances where early cleanup is needed. Will also always be called from destructor -void DebuggerWindow2::CleanupForDestroy() -{ - debugger_setOptionBreakOnEntry(false); - // save module storage - while (!m_modulesStorage.empty()) - { - SaveModuleStorage(m_modulesStorage[0]->data().moduleInfo, true); // deleting the module storage will also delete breakpoints belonging to the module's data - } - m_config.Save(); -} - -void DebuggerWindow2::OnClose(wxCloseEvent& event) -{ - debugger_setOptionBreakOnEntry(false); - - const wxCloseEvent parentEvent(wxEVT_DEBUGGER_CLOSE); - wxPostEvent(m_parent, parentEvent); - - event.Skip(); -} - -void DebuggerWindow2::OnMoveToDisasmAddr(wxCommandEvent& event) -{ - MPTR address = (uint32)(uint64)event.GetClientData(); - if (!address) - return; - UpdateModuleLabel(address); - m_disasm_ctrl->CenterOffset(address); -} - -void DebuggerWindow2::OnDisasmCtrlGotoAddress(wxCommandEvent& event) -{ - uint32 address = static_cast(event.GetExtraLong()); - UpdateModuleLabel(address); -} - -void DebuggerWindow2::OnParentMove(const wxPoint& main_position, const wxSize& main_size) -{ - m_main_position = main_position; - m_main_size = main_size; - - if (!m_config.data().pin_to_main) - return; - - wxSize size(m_main_size.x, GetSize().GetHeight()); - SetSize(size); - - wxPoint position = m_main_position; - position.y -= size.y; - SetPosition(position); - - m_register_window->OnMainMove(main_position, main_size); - m_dump_window->OnMainMove(main_position, main_size); - m_breakpoint_window->OnMainMove(main_position, main_size); - m_module_window->OnMainMove(main_position, main_size); - m_symbol_window->OnMainMove(main_position, main_size); -} - -void DebuggerWindow2::OnNotifyModuleLoaded(wxCommandEvent& event) -{ - DebuggerModuleInfoNotify* moduleNotif = static_cast(event.GetClientObject()); - LoadModuleStorage(moduleNotif->moduleInfo); - m_module_window->OnGameLoaded(); - m_symbol_window->OnGameLoaded(); - m_disasm_ctrl->Init(); -} - -void DebuggerWindow2::OnNotifyModuleUnloaded(wxCommandEvent& event) -{ - DebuggerModuleInfoNotify* moduleNotif = static_cast(event.GetClientObject()); - SaveModuleStorage(moduleNotif->moduleInfo, true); - m_module_window->OnGameLoaded(); - m_symbol_window->OnGameLoaded(); - m_disasm_ctrl->Init(); -} - -void DebuggerWindow2::OnNotifyGraphicPacksModified(wxCommandEvent& event) -{ - m_module_window->OnGameLoaded(); - m_symbol_window->OnGameLoaded(); - m_disasm_ctrl->Init(); -} - -void DebuggerWindow2::OnGameLoaded() -{ - m_disasm_ctrl->Init(); - - m_dump_window->OnGameLoaded(); - m_module_window->OnGameLoaded(); - m_breakpoint_window->OnGameLoaded(); - m_symbol_window->OnGameLoaded(); - - RPLModule* current_rpl_module = RPLLoader_FindModuleByCodeAddr(MEMORY_CODEAREA_ADDR); - if(current_rpl_module) - m_module_label->SetLabel(wxString::Format("> %s", current_rpl_module->moduleName.c_str())); - - this->SendSizeEvent(); -} - -XMLDebuggerConfig& DebuggerWindow2::GetConfig() -{ - return m_config; -} - -bool DebuggerWindow2::Show(bool show) -{ - const bool result = wxFrame::Show(show); - - if (show) - { - m_register_window->Show(m_config.data().show_register); - m_dump_window->Show(m_config.data().show_dump); - m_breakpoint_window->Show(m_config.data().show_breakpoints); - m_module_window->Show(m_config.data().show_modules); - m_symbol_window->Show(m_config.data().show_symbols); - } - else - { - m_register_window->Show(false); - m_dump_window->Show(false); - m_breakpoint_window->Show(false); - m_module_window->Show(false); - m_symbol_window->Show(false); - } - - return result; -} - -std::wstring DebuggerWindow2::GetModuleStoragePath(std::string module_name, uint32_t crc_hash) const -{ - if (module_name.empty() || crc_hash == 0) return {}; - return ActiveSettings::GetConfigPath("debugger/{}_{:#10x}.xml", module_name, crc_hash).generic_wstring(); -} - -std::wstring DebuggerWindow2::GetModuleMapPath(std::string module_name, uint32_t crc_hash) const -{ - if (module_name.empty() || crc_hash == 0) return {}; - return ActiveSettings::GetConfigPath("debugger/{}_{:#10x}.map", module_name, crc_hash).generic_wstring(); -} - -void DebuggerWindow2::OnBreakpointHit(wxCommandEvent& event) -{ - PPCInterpreter_t* hCPU = debugger_lockDebugSession(); - if (!hCPU) - return; - MPTR ip = hCPU->instructionPointer; - debugger_unlockDebugSession(hCPU); - - UpdateModuleLabel(ip); - - m_toolbar->SetToolShortHelp(TOOL_ID_PAUSE, _("Run (F5)")); - m_toolbar->SetToolNormalBitmap(TOOL_ID_PAUSE, m_run); - - m_toolbar->EnableTool(TOOL_ID_STEP_INTO, true); - m_toolbar->EnableTool(TOOL_ID_STEP_OVER, true); - - m_disasm_ctrl->CenterOffset(ip); -} - -void DebuggerWindow2::OnRunProgram(wxCommandEvent& event) -{ - m_toolbar->SetToolShortHelp(TOOL_ID_PAUSE, _("Break (F5)")); - m_toolbar->SetToolNormalBitmap(TOOL_ID_PAUSE, m_pause); - - m_toolbar->EnableTool(TOOL_ID_STEP_INTO, false); - m_toolbar->EnableTool(TOOL_ID_STEP_OVER, false); -} - -void DebuggerWindow2::OnToolClicked(wxCommandEvent& event) -{ - switch(event.GetId()) - { - case TOOL_ID_GOTO: - m_disasm_ctrl->GoToAddressDialog(); - break; - case TOOL_ID_PAUSE: - if (debugger_isTrapped()) - debugger_stepCommand(DebuggerStepCommand::Run); - else - debugger_requestBreak(); - break; - case TOOL_ID_STEP_INTO: - if (debugger_isTrapped()) - debugger_stepCommand(DebuggerStepCommand::StepInto); - break; - case TOOL_ID_STEP_OVER: - if (debugger_isTrapped()) - debugger_stepCommand(DebuggerStepCommand::StepOver); - break; - } -} - -void DebuggerWindow2::OnBreakpointChange(wxCommandEvent& event) -{ - m_breakpoint_window->OnUpdateView(); - m_disasm_ctrl->RefreshControl(); - UpdateModuleLabel(); -} - -void DebuggerWindow2::OnOptionsInput(wxCommandEvent& event) -{ - switch (event.GetId()) - { - case MENU_ID_OPTIONS_PIN_TO_MAINWINDOW: - { - const bool value = !m_config.data().pin_to_main; - m_config.data().pin_to_main = value; - if (value) - OnParentMove(m_main_position, m_main_size); - - break; - } - case MENU_ID_OPTIONS_BREAK_ON_START: - { - const bool value = !m_config.data().break_on_start; - m_config.data().break_on_start = value; - debugger_setOptionBreakOnEntry(value); - break; - } - case MENU_ID_OPTIONS_LOG_MEMORY_BREAKPOINTS: - { - const bool value = !m_config.data().log_memory_breakpoints; - m_config.data().log_memory_breakpoints = value; - debugger_setOptionLogOnlyMemoryBreakpoints(value); - break; - } - case MENU_ID_OPTIONS_SWITCH_CPU_MODE: - { - if (m_forceInterpreter) - { - PPCRecompiler_Enable(); - m_forceInterpreter = false; - cemuLog_log(LogType::Force, "Debugger: Switched CPU mode to recompiler"); - } - else - { - PPCRecompiler_Disable(); - m_forceInterpreter = true; - cemuLog_log(LogType::Force, "Debugger: Switched CPU mode to interpreter"); - } - break; - } - default: - return; - } - - m_config.Save(); -} - -void DebuggerWindow2::OnWindowMenu(wxCommandEvent& event) -{ - switch (event.GetId()) - { - case MENU_ID_WINDOW_REGISTERS: - { - const bool value = !m_config.data().show_register; - m_config.data().show_register = value; - m_register_window->Show(value); - break; - } - case MENU_ID_WINDOW_DUMP: - { - const bool value = !m_config.data().show_dump; - m_config.data().show_dump = value; - m_dump_window->Show(value); - break; - } - case MENU_ID_WINDOW_BREAKPOINTS: - { - const bool value = !m_config.data().show_breakpoints; - m_config.data().show_breakpoints = value; - m_breakpoint_window->Show(value); - break; - } - case MENU_ID_WINDOW_MODULE: - { - const bool value = !m_config.data().show_modules; - m_config.data().show_modules = value; - m_module_window->Show(value); - break; - } - case MENU_ID_WINDOW_SYMBOL: - { - const bool value = !m_config.data().show_symbols; - m_config.data().show_symbols = value; - m_symbol_window->Show(value); - break; - } - default: - return; - } - - m_config.Save(); -} - -void DebuggerWindow2::OnUpdateView(wxCommandEvent& event) -{ - UpdateModuleLabel(); - m_disasm_ctrl->OnUpdateView(); - m_register_window->OnUpdateView(); - m_breakpoint_window->OnUpdateView(); -} - -void DebuggerWindow2::OnExit(wxCommandEvent& event) -{ - this->Close(); -} - -void DebuggerWindow2::OnShow(wxShowEvent& event) -{ - m_register_window->Show(); - m_dump_window->Show(); - m_breakpoint_window->Show(); - m_module_window->Show(); - m_symbol_window->Show(); - event.Skip(); -} - -void DebuggerWindow2::CreateMenuBar() -{ - auto menu_bar = new wxMenuBar; - - // file - wxMenu* file_menu = new wxMenu; - file_menu->Append(MENU_ID_FILE_EXIT, _("&Exit")); - file_menu->Bind(wxEVT_MENU, &DebuggerWindow2::OnExit, this); - - menu_bar->Append(file_menu, _("&File")); - - // options - wxMenu* options_menu = new wxMenu; - options_menu->Append(MENU_ID_OPTIONS_PIN_TO_MAINWINDOW, _("&Pin to main window"), wxEmptyString, wxITEM_CHECK)->Check(m_config.data().pin_to_main); - options_menu->Append(MENU_ID_OPTIONS_BREAK_ON_START, _("Break on &entry point"), wxEmptyString, wxITEM_CHECK)->Check(m_config.data().break_on_start); - options_menu->Append(MENU_ID_OPTIONS_LOG_MEMORY_BREAKPOINTS, _("Log only memory breakpoints"), wxEmptyString, wxITEM_CHECK)->Check(m_config.data().log_memory_breakpoints); - options_menu->Append(MENU_ID_OPTIONS_SWITCH_CPU_MODE, _("Switch to &interpreter CPU mode"), wxEmptyString, wxITEM_CHECK); - menu_bar->Append(options_menu, _("&Options")); - - // window - wxMenu* window_menu = new wxMenu; - window_menu->Append(MENU_ID_WINDOW_REGISTERS, _("&Registers"), wxEmptyString, wxITEM_CHECK)->Check(m_config.data().show_register); - window_menu->Append(MENU_ID_WINDOW_DUMP, _("&Memory Dump"), wxEmptyString, wxITEM_CHECK)->Check(m_config.data().show_dump); - window_menu->Append(MENU_ID_WINDOW_BREAKPOINTS, _("&Breakpoints"), wxEmptyString, wxITEM_CHECK)->Check(m_config.data().show_breakpoints); - window_menu->Append(MENU_ID_WINDOW_MODULE, _("Module&list"), wxEmptyString, wxITEM_CHECK)->Check(m_config.data().show_modules); - window_menu->Append(MENU_ID_WINDOW_SYMBOL, _("&Symbols"), wxEmptyString, wxITEM_CHECK)->Check(m_config.data().show_symbols); - - menu_bar->Append(window_menu, _("&Window")); - - SetMenuBar(menu_bar); - - options_menu->Bind(wxEVT_MENU, &DebuggerWindow2::OnOptionsInput, this); - window_menu->Bind(wxEVT_MENU, &DebuggerWindow2::OnWindowMenu, this); -} - -void DebuggerWindow2::UpdateModuleLabel(uint32 address) -{ - if (address == 0) - address = m_disasm_ctrl->GetViewBaseAddress(); - - RPLModule* module = RPLLoader_FindModuleByCodeAddr(address); - if (module && m_module_address != module->regionMappingBase_text.GetMPTR()) - { - m_module_label->SetLabel(wxString::Format("> %s", module->moduleName.c_str())); - m_module_address = module->regionMappingBase_text.GetMPTR(); - } - else if (address >= mmuRange_CODECAVE.getBase() && address < mmuRange_CODECAVE.getEnd()) - { - m_module_label->SetLabel(wxString::Format("> %s", "Cemu codecave")); - m_module_address = mmuRange_CODECAVE.getBase(); - } -} - -void DebuggerWindow2::UpdateViewThreadsafe() -{ - auto* evt = new wxCommandEvent(wxEVT_UPDATE_VIEW); - wxQueueEvent(this, evt); -} - -void DebuggerWindow2::NotifyDebugBreakpointHit() -{ - auto* evt = new wxCommandEvent(wxEVT_BREAKPOINT_HIT); - wxQueueEvent(this, evt); -} - -void DebuggerWindow2::NotifyRun() -{ - auto* evt = new wxCommandEvent(wxEVT_RUN); - wxQueueEvent(this, evt); -} - -void DebuggerWindow2::MoveToAddressInDisassembly(MPTR address) -{ - auto* evt = new wxCommandEvent(wxEVT_MOVE_TO_DISASM_ADDR); - evt->SetClientData((void*)(uintptr_t)address); - wxQueueEvent(this, evt); -} - -void DebuggerWindow2::NotifyGraphicPacksModified() -{ - auto* evt = new wxCommandEvent(wxEVT_NOTIFY_GRAPHIC_PACKS_MODIFIED); - wxQueueEvent(this, evt); -} - -void DebuggerWindow2::NotifyModuleLoaded(RPLModule* module) -{ - DebuggerModuleInfoNotify* notifData = new DebuggerModuleInfoNotify(module); - auto* evt = new wxCommandEvent(wxEVT_NOTIFY_MODULE_LOADED); - evt->SetClientObject(notifData); - wxQueueEvent(this, evt); -} - -void DebuggerWindow2::NotifyModuleUnloaded(RPLModule* module) -{ - DebuggerModuleInfoNotify* notifData = new DebuggerModuleInfoNotify(module); - auto* evt = new wxCommandEvent(wxEVT_NOTIFY_MODULE_UNLOADED); - evt->SetClientObject(notifData); - wxQueueEvent(this, evt); -} diff --git a/src/gui/wxgui/debugger/DebuggerWindow2.h b/src/gui/wxgui/debugger/DebuggerWindow2.h deleted file mode 100644 index 83cc5edb..00000000 --- a/src/gui/wxgui/debugger/DebuggerWindow2.h +++ /dev/null @@ -1,173 +0,0 @@ -#pragma once - -#include "wxgui/debugger/DisasmCtrl.h" -#include "config/XMLConfig.h" -#include "Cafe/HW/Espresso/Debugger/Debugger.h" -#include "Cafe/OS/RPL/rpl.h" -#include "Cafe/HW/Espresso/Debugger/GDBStub.h" - -#include -#include -#include - -class BreakpointWindow; -class RegisterWindow; -class DumpWindow; -class ModuleWindow; -class SymbolWindow; -class wxStaticText; -struct RPLStoredSymbol; - -wxDECLARE_EVENT(wxEVT_UPDATE_VIEW, wxCommandEvent); -wxDECLARE_EVENT(wxEVT_BREAKPOINT_HIT, wxCommandEvent); -wxDECLARE_EVENT(wxEVT_RUN, wxCommandEvent); -wxDECLARE_EVENT(wxEVT_BREAKPOINT_CHANGE, wxCommandEvent); -wxDECLARE_EVENT(wxEVT_MOVE_TO_DISASM_ADDR, wxCommandEvent); -wxDECLARE_EVENT(wxEVT_NOTIFY_MODULE_LOADED, wxCommandEvent); -wxDECLARE_EVENT(wxEVT_NOTIFY_MODULE_UNLOADED, wxCommandEvent); -wxDECLARE_EVENT(wxEVT_NOTIFY_GRAPHIC_PACKS_MODIFIED, wxCommandEvent); - -extern class DebuggerWindow2* s_debuggerWindow; - -struct DebuggerConfig -{ - DebuggerConfig() - : pin_to_main(true), break_on_start(true), log_memory_breakpoints(false), show_register(true), show_dump(true), show_stack(true), show_breakpoints(true), show_modules(true), show_symbols(true) {} - - bool pin_to_main; - bool break_on_start; - bool log_memory_breakpoints; - - bool show_register; - bool show_dump; - bool show_stack; - bool show_breakpoints; - bool show_modules; - bool show_symbols; - - void Load(XMLConfigParser& parser); - void Save(XMLConfigParser& parser); -}; -typedef XMLDataConfig XMLDebuggerConfig; - -struct DebuggerModuleInfo -{ - struct ModuleArea - { - MPTR base; - MPTR origBase; - uint32 size; - - bool ContainsAddress(MPTR addr) const - { - return addr >= base && (addr < (base+size)); - } - - bool ContainsOriginalAddress(MPTR addr) const - { - return addr >= origBase && (addr < (origBase + size)); - } - }; - - std::string moduleName; - uint32 patchCRC; - ModuleArea textArea; - ModuleArea dataArea; - - DebuggerModuleInfo(RPLModule* module); -}; - -struct DebuggerModuleStorage -{ - DebuggerModuleInfo moduleInfo; - bool delete_breakpoints_after_saving; - std::vector loaded_map_symbols; - - void Load(XMLConfigParser& parser); - void Save(XMLConfigParser& parser); -}; -typedef XMLDataConfig XMLDebuggerModuleConfig; - -static wxBitmap LoadThemedBitmapFromPNG(const uint8* data, size_t size, const wxColour& tint) -{ - wxMemoryInputStream strm(data, size); - wxImage img(strm, wxBITMAP_TYPE_PNG); - img.Replace(0x00, 0x00, 0x00, tint.Red(), tint.Green(), tint.Blue()); - return wxBitmap(img); -} - -class DebuggerWindow2 : public wxFrame, public DebuggerCallbacks -{ -public: - void CreateToolBar(); - void LoadModuleStorage(const struct DebuggerModuleInfo& moduleInfo); - void SaveModuleStorage(const struct DebuggerModuleInfo& moduleInfo, bool deleteModuleStorage); - DebuggerWindow2(wxFrame& parent, const wxRect& display_size); - ~DebuggerWindow2(); - void CleanupForDestroy(); - - void OnParentMove(const wxPoint& position, const wxSize& size); - void OnGameLoaded(); - - XMLDebuggerConfig& GetConfig(); - - bool Show(bool show = true) override; - std::wstring GetModuleStoragePath(std::string module_name, uint32_t crc_hash) const; - -private: - void OnBreakpointHit(wxCommandEvent& event); - void OnRunProgram(wxCommandEvent& event); - void OnToolClicked(wxCommandEvent& event); - void OnBreakpointChange(wxCommandEvent& event); - void OnOptionsInput(wxCommandEvent& event); - void OnWindowMenu(wxCommandEvent& event); - void OnUpdateView(wxCommandEvent& event); - void OnExit(wxCommandEvent& event); - void OnShow(wxShowEvent& event); - void OnClose(wxCloseEvent& event); - void OnMoveToDisasmAddr(wxCommandEvent& event); - void OnNotifyModuleLoaded(wxCommandEvent& event); - void OnNotifyModuleUnloaded(wxCommandEvent& event); - void OnNotifyGraphicPacksModified(wxCommandEvent& event); - // events from DisasmCtrl - void OnDisasmCtrlGotoAddress(wxCommandEvent& event); - - void CreateMenuBar(); - void UpdateModuleLabel(uint32 address = 0); - void LoadModuleMap(DebuggerModuleStorage& moduleStorage); - void UnloadModuleMap(DebuggerModuleStorage& moduleStorage); - std::wstring GetModuleMapPath(std::string module_name, uint32_t crc_hash) const; - - void UpdateViewThreadsafe() override; - void NotifyDebugBreakpointHit() override; - void NotifyRun() override; - void MoveToAddressInDisassembly(MPTR address) override; - void NotifyGraphicPacksModified() override; - void NotifyModuleLoaded(struct RPLModule* module) override; - void NotifyModuleUnloaded(struct RPLModule* module) override; - - XMLDebuggerConfig m_config; - std::vector> m_modulesStorage; - - wxPoint m_main_position; - wxSize m_main_size; - - RegisterWindow* m_register_window; - DumpWindow* m_dump_window; - BreakpointWindow* m_breakpoint_window; - ModuleWindow* m_module_window; - SymbolWindow* m_symbol_window; - - DisasmCtrl* m_disasm_ctrl; - - wxToolBar* m_toolbar; - wxBitmap m_run, m_pause; - - uint32 m_module_address; - wxStaticText* m_module_label; - - bool m_forceInterpreter{false}; - -wxDECLARE_EVENT_TABLE(); -}; - diff --git a/src/gui/wxgui/debugger/DisasmCtrl.cpp b/src/gui/wxgui/debugger/DisasmCtrl.cpp deleted file mode 100644 index 913df026..00000000 --- a/src/gui/wxgui/debugger/DisasmCtrl.cpp +++ /dev/null @@ -1,895 +0,0 @@ -#include "wxgui/wxgui.h" -#include "wxgui/debugger/DisasmCtrl.h" - -#include "wxHelper.h" -#include "Cafe/OS/RPL/rpl_structs.h" -#include "Cafe/OS/RPL/rpl.h" -#include "Cafe/OS/RPL/rpl_symbol_storage.h" -#include "Cafe/OS/RPL/rpl_debug_symbols.h" -#include "Cemu/PPCAssembler/ppcAssembler.h" -#include "Cafe/HW/Espresso/Debugger/Debugger.h" -#include "wxgui/debugger/DebuggerWindow2.h" -#include "util/helpers/helpers.h" - -#include "Cemu/ExpressionParser/ExpressionParser.h" -#include "Cafe/HW/Espresso/Debugger/DebugSymbolStorage.h" - -#include - -wxDEFINE_EVENT(wxEVT_DISASMCTRL_NOTIFY_GOTO_ADDRESS, wxCommandEvent); - -#define MAX_SYMBOL_LEN (120) - - -constexpr uint8 arrowRightPNG[] = -{ - 0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A, 0x00, 0x00, 0x00, 0x0D, - 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x0B, - 0x08, 0x03, 0x00, 0x00, 0x00, 0x41, 0x3C, 0xFD, 0x0B, 0x00, 0x00, 0x00, - 0x01, 0x73, 0x52, 0x47, 0x42, 0x00, 0xAE, 0xCE, 0x1C, 0xE9, 0x00, 0x00, - 0x00, 0x04, 0x67, 0x41, 0x4D, 0x41, 0x00, 0x00, 0xB1, 0x8F, 0x0B, 0xFC, - 0x61, 0x05, 0x00, 0x00, 0x00, 0x06, 0x50, 0x4C, 0x54, 0x45, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0xA5, 0x67, 0xB9, 0xCF, 0x00, 0x00, 0x00, 0x02, - 0x74, 0x52, 0x4E, 0x53, 0xFF, 0x00, 0xE5, 0xB7, 0x30, 0x4A, 0x00, 0x00, - 0x00, 0x09, 0x70, 0x48, 0x59, 0x73, 0x00, 0x00, 0x0E, 0xC3, 0x00, 0x00, - 0x0E, 0xC3, 0x01, 0xC7, 0x6F, 0xA8, 0x64, 0x00, 0x00, 0x00, 0x2C, 0x49, - 0x44, 0x41, 0x54, 0x18, 0x57, 0x63, 0x60, 0x84, 0x03, 0x08, 0x13, 0x59, - 0x00, 0xCC, 0x46, 0x11, 0x00, 0x71, 0x80, 0x24, 0x32, 0xC0, 0x10, 0x60, - 0xC0, 0x10, 0xC0, 0x00, 0x58, 0xCC, 0x80, 0xD8, 0x00, 0x02, 0x60, 0x3E, - 0x7E, 0x77, 0x00, 0x31, 0x23, 0x23, 0x00, 0x21, 0x95, 0x00, 0x5B, 0x20, - 0x73, 0x8D, 0x0B, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4E, 0x44, 0xAE, - 0x42, 0x60, 0x82 -}; -std::optional g_ipArrowBitmap; - - -static wxColour theme_textForeground; -static wxColour theme_textForegroundMuted; -static wxColour theme_background; - -// background colors for lines in the disassembly view -static wxColour theme_lineBreakpointAndCurrentInstruction; -static wxColour theme_lineCurrentInstruction; -static wxColour theme_lineBreakpointSet; -static wxColour theme_lineLoggingBreakpointSet; -static wxColour theme_lineLastGotoAddress; - -// text colors for addresses in the disassembly view -static wxColour theme_syntaxGPR; -static wxColour theme_syntaxFPR; -static wxColour theme_syntaxSPR; -static wxColour theme_syntaxCR; -static wxColour theme_syntaxIMM; -static wxColour theme_syntaxIMMOffset; -static wxColour theme_syntaxCallIMM; -static wxColour theme_syntaxPseudoOrUnknown; -static wxColour theme_syntaxSymbol; - -static wxColour theme_opCode; -static wxColour theme_typeData; -static wxColour theme_patchedOpCode; -static wxColour theme_patchedData; - - -static void InitSyntaxColors() -{ - theme_textForeground = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT); - theme_textForegroundMuted = wxSystemSettings::SelectLightDark(wxSystemSettings::GetColour(wxSYS_COLOUR_GRAYTEXT), wxColour(140, 142, 145)); - theme_background = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW); - - // line background highlights - theme_lineBreakpointSet = wxSystemSettings::SelectLightDark(wxColour(0xFF,0x80,0x80), wxColour(0x84,0x21,0x21)); // red for a non-current breakpoint - theme_lineBreakpointAndCurrentInstruction = wxSystemSettings::SelectLightDark(wxColour(0xD2,0x7E,0xD2), wxColour(0x91,0x44,0xA4)); // pink for a current breakpoint - theme_lineCurrentInstruction = wxSystemSettings::SelectLightDark(wxColour(0xFF,0xA0,0x80), wxColour(0x87,0x53,0x1A)); // light orange - theme_lineLoggingBreakpointSet = wxSystemSettings::SelectLightDark(wxColour(0xAB,0xED,0xEE), wxColour(0x25,0x5D,0x6D)); // light blue - theme_lineLastGotoAddress = wxSystemSettings::SelectLightDark(wxColour(0xE0,0xE0,0xE0), wxColour(0x40,0x40,0x40)); // very light gray to indicate the line that was last jumped to - - // disassembly syntax colors - theme_syntaxGPR = wxSystemSettings::SelectLightDark(wxColour(0x66,0x00,0x00), wxColour(0xE0,0x6C,0x75)); - theme_syntaxFPR = wxSystemSettings::SelectLightDark(wxColour(0x66,0x66,0x00), wxColour(0xD1,0x9A,0x66)); - theme_syntaxSPR = wxSystemSettings::SelectLightDark(wxColour(0x00,0x66,0x66), wxColour(0x56,0xB6,0xC2)); - theme_syntaxCR = wxSystemSettings::SelectLightDark(wxColour(0x00,0x66,0x66), wxColour(0x56,0xB6,0xC2)); - theme_syntaxIMM = wxSystemSettings::SelectLightDark(wxColour(0x00,0x66,0x00), wxColour(0x9D,0xDE,0x6F)); - theme_syntaxIMMOffset = wxSystemSettings::SelectLightDark(wxColour(0x00,0x66,0x00), wxColour(0x9D,0xDE,0x6F)); - theme_syntaxCallIMM = wxSystemSettings::SelectLightDark(wxColour(0x00,0x00,0x88), wxColour(0x61,0xAF,0xEF)); - theme_syntaxPseudoOrUnknown = wxSystemSettings::SelectLightDark(wxColour(0xA0,0xA0,0xA0), wxColour(0x5C,0x63,0x70)); - theme_syntaxSymbol = wxSystemSettings::SelectLightDark(wxColour(0xA0,0x00,0x00), wxColour(0xE0,0x6C,0x75)); - - // opcode & data highlighting - theme_opCode = wxSystemSettings::SelectLightDark(wxColour(0xFF,0x00,0x40), wxColour(0xFF,0x70,0x7B)); - theme_patchedOpCode = wxSystemSettings::SelectLightDark(wxColour(0xFF,0x20,0x20), wxColour(0xCC,0x52,0xF5)); - theme_typeData = wxSystemSettings::SelectLightDark(wxColour(0xFF,0x20,0x20), wxColour(0xCE,0x91,0x78)); - theme_patchedData = wxSystemSettings::SelectLightDark(wxColour(0xFF,0x20,0x20), wxColour(0xF4,0x43,0x36)); - - // theme the current instruction pointer arrow - g_ipArrowBitmap = wxHelper::LoadThemedBitmapFromPNG(arrowRightPNG, sizeof(arrowRightPNG), wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT)); -} - - -#define OFFSET_ADDRESS (60) -#define OFFSET_ADDRESS_RELATIVE (90) -#define OFFSET_DISASSEMBLY (300) - -#define OFFSET_DISASSEMBLY_OPERAND (80) - -DisasmCtrl::DisasmCtrl(wxWindow* parent, const wxWindowID& id, const wxPoint& pos, const wxSize& size, long style) - : TextList(parent, id, pos, size, style), m_mouse_line(-1), m_mouse_line_drawn(-1), m_active_line(-1) -{ - Init(); - - if (!g_ipArrowBitmap.has_value()) - { - InitSyntaxColors(); - } - - auto tooltip_sizer = new wxBoxSizer(wxVERTICAL); - tooltip_sizer->Add(new wxStaticText(m_tooltip_window, wxID_ANY, wxEmptyString), 0, wxALL, 5); - m_tooltip_window->SetSizer(tooltip_sizer); - - Bind(wxEVT_MENU, &DisasmCtrl::OnContextMenuEntryClicked, this, IDContextMenu_ToggleBreakpoint, IDContextMenu_Last); -} - -void DisasmCtrl::Init() -{ - SelectCodeRegion(mmuRange_TEXT_AREA.getBase()); -} - -void DisasmCtrl::SelectCodeRegion(uint32 newAddress) -{ - if (newAddress >= mmuRange_TEXT_AREA.getBase() && newAddress < mmuRange_TEXT_AREA.getEnd()) - { - currentCodeRegionStart = MEMORY_CODEAREA_ADDR; - currentCodeRegionEnd = RPLLoader_GetMaxCodeOffset(); - currentCodeRegionEnd = std::max(currentCodeRegionEnd, currentCodeRegionStart + 0x1000); - } - MMURange* mmuRange = memory_getMMURangeByAddress(newAddress); - if (mmuRange) - { - currentCodeRegionStart = mmuRange->getBase(); - currentCodeRegionEnd = mmuRange->getEnd(); - } - else - { - currentCodeRegionStart = 0; - currentCodeRegionEnd = 0; - } - - // update line tracking - sint32 element_count = currentCodeRegionEnd - currentCodeRegionStart; - // if (element_count <= 0x00010000) - // element_count = 0x00010000; - - if (this->SetElementCount(element_count / 4)) - { - Scroll(0, 0); - RefreshControl(); - } -} - -void DisasmCtrl::DrawDisassemblyLine(wxDC& dc, const wxPoint& linePosition, MPTR virtualAddress, RPLModule* rplModule) -{ - wxPoint position = linePosition; - - bool hasPatch = debugger_hasPatch(virtualAddress); - - PPCDisassembledInstruction disasmInstr; - - const DebuggerBreakpoint* bp = debugger_getFirstBP(virtualAddress); - while (bp) - { - if (bp->isExecuteBP() && bp->enabled) - break; - bp = bp->next; - } - - uint32 opcode; - - if (bp) - opcode = bp->originalOpcodeValue; - else - opcode = memory_readU32(virtualAddress); - - ppcAssembler_disassemble(virtualAddress, opcode, &disasmInstr); - - bool hasActiveBP = false; - PPCInterpreter_t* sessionCpu = debugger_lockDebugSession(); - if (sessionCpu) - { - hasActiveBP = sessionCpu->instructionPointer == virtualAddress; - debugger_unlockDebugSession(sessionCpu); - } - - // write virtual address - wxColour background_colour; - if (hasActiveBP && bp != nullptr) - background_colour = theme_lineBreakpointAndCurrentInstruction; - else if (hasActiveBP) - background_colour = theme_lineCurrentInstruction; - else if (bp != nullptr) - background_colour = bp->bpType == DEBUGGER_BP_T_NORMAL ? theme_lineBreakpointSet : theme_lineLoggingBreakpointSet; - else if(virtualAddress == m_lastGotoTarget) - background_colour = theme_lineLastGotoAddress; - else - background_colour = theme_background; - - DrawLineBackground(dc, position, background_colour); - - dc.SetTextForeground(theme_textForeground); - dc.DrawText(wxString::Format("%08x", virtualAddress), position); - position.x += OFFSET_ADDRESS; - - dc.SetTextForeground(theme_textForegroundMuted); - if (rplModule) - dc.DrawText(wxString::Format("+0x%-8x", virtualAddress - rplModule->regionMappingBase_text.GetMPTR()), position); - else - dc.DrawText("???", position); - - position.x += OFFSET_ADDRESS_RELATIVE; - - // draw arrow to clearly indicate instruction pointer - if(hasActiveBP) - dc.DrawBitmap(*g_ipArrowBitmap, wxPoint(position.x - 24, position.y + 2), false); - - // handle data symbols - auto debugSymbolDataType = DebugSymbolStorage::GetDataType(virtualAddress); - if (debugSymbolDataType == DEBUG_SYMBOL_TYPE::FLOAT) - { - dc.SetTextForeground(hasPatch ? theme_patchedData : theme_typeData); - dc.DrawText(fmt::format(".float"), position); - - position.x += OFFSET_DISASSEMBLY_OPERAND; - dc.SetTextForeground(hasPatch ? theme_patchedData : theme_syntaxIMM); - dc.DrawText(fmt::format("{}", memory_readFloat(virtualAddress)), position); - - return; - } - else if (debugSymbolDataType == DEBUG_SYMBOL_TYPE::U32) - { - dc.SetTextForeground(hasPatch ? theme_patchedData : theme_typeData); - dc.DrawText(fmt::format(".uint"), position); - - position.x += OFFSET_DISASSEMBLY_OPERAND; - dc.SetTextForeground(hasPatch ? theme_patchedData : theme_syntaxIMM); - dc.DrawText(fmt::format("{}", memory_readU32(virtualAddress)), position); - - return; - } - - sint32 start_width = position.x; - dc.SetTextForeground(hasPatch ? theme_patchedOpCode : theme_opCode); - std::string opName = ppcAssembler_getInstructionName(disasmInstr.ppcAsmCode); - std::transform(opName.begin(), opName.end(), opName.begin(), tolower); - dc.DrawText(wxString::Format("%-12s", opName.c_str()), position); - position.x += OFFSET_DISASSEMBLY_OPERAND; - - bool isRLWINM = disasmInstr.ppcAsmCode == PPCASM_OP_RLWINM || disasmInstr.ppcAsmCode == PPCASM_OP_RLWINM_ || - disasmInstr.ppcAsmCode == PPCASM_OP_CLRLWI || disasmInstr.ppcAsmCode == PPCASM_OP_CLRLWI_ || - disasmInstr.ppcAsmCode == PPCASM_OP_CLRRWI || disasmInstr.ppcAsmCode == PPCASM_OP_CLRRWI_ || - disasmInstr.ppcAsmCode == PPCASM_OP_EXTLWI || disasmInstr.ppcAsmCode == PPCASM_OP_EXTLWI_ || - disasmInstr.ppcAsmCode == PPCASM_OP_EXTRWI || disasmInstr.ppcAsmCode == PPCASM_OP_EXTRWI_ || - disasmInstr.ppcAsmCode == PPCASM_OP_SLWI || disasmInstr.ppcAsmCode == PPCASM_OP_SLWI_ || - disasmInstr.ppcAsmCode == PPCASM_OP_SRWI || disasmInstr.ppcAsmCode == PPCASM_OP_SRWI_ || - disasmInstr.ppcAsmCode == PPCASM_OP_ROTRWI || disasmInstr.ppcAsmCode == PPCASM_OP_ROTRWI_ || - disasmInstr.ppcAsmCode == PPCASM_OP_ROTLWI || disasmInstr.ppcAsmCode == PPCASM_OP_ROTLWI_; - bool forceDecDisplay = isRLWINM; - - if (disasmInstr.ppcAsmCode == PPCASM_OP_UKN) - { - // show raw bytes - WriteText(dc, wxString::Format("%02x %02x %02x %02x", (opcode >> 24) & 0xFF, (opcode >> 16) & 0xFF, (opcode >> 8) & 0xFF, (opcode >> 0) & 0xFF), position, theme_syntaxPseudoOrUnknown); - } - - bool is_first_operand = true; - for (sint32 o = 0; o < PPCASM_OPERAND_COUNT; o++) - { - if (((disasmInstr.operandMask >> o) & 1) == 0) - continue; - - if (!is_first_operand) - WriteText(dc, ", ", position, theme_textForeground); - - is_first_operand = false; - switch (disasmInstr.operand[o].type) - { - case PPCASM_OPERAND_TYPE_GPR: - WriteText(dc, wxString::Format("r%d", disasmInstr.operand[o].registerIndex), position, theme_syntaxGPR); - break; - - case PPCASM_OPERAND_TYPE_FPR: - WriteText(dc, wxString::Format("f%d", disasmInstr.operand[o].registerIndex), position, theme_syntaxFPR); - break; - - case PPCASM_OPERAND_TYPE_SPR: - WriteText(dc, wxString::Format("spr%d", disasmInstr.operand[o].registerIndex), position, theme_syntaxSPR); - break; - - case PPCASM_OPERAND_TYPE_CR: - WriteText(dc, wxString::Format("cr%d", disasmInstr.operand[o].registerIndex), position, theme_syntaxCR); - break; - - case PPCASM_OPERAND_TYPE_IMM: - { - wxString string; - if (disasmInstr.operand[o].isSignedImm) - { - sint32 sImm = disasmInstr.operand[o].immS32; - if (disasmInstr.operand[o].immWidth == 16 && (sImm & 0x8000)) - sImm |= (sint32)0xFFFF0000; - - if ((sImm > -10 && sImm < 10) || forceDecDisplay) - string = wxString::Format("%d", sImm); - else - { - if (sImm < 0) - string = wxString::Format("-0x%x", -sImm); - else - string = wxString::Format("0x%x", sImm); - } - } - else - { - uint32 uImm = disasmInstr.operand[o].immS32; - if ((uImm >= 0 && uImm < 10) || forceDecDisplay) - string = wxString::Format("%u", uImm); - else - string = wxString::Format("0x%x", uImm); - } - - WriteText(dc, string, position, theme_syntaxIMM); - break; - } - case PPCASM_OPERAND_TYPE_PSQMODE: - { - if (disasmInstr.operand[o].immS32) - WriteText(dc, "single", position, theme_syntaxIMM); - else - WriteText(dc, "paired", position, theme_syntaxIMM); - break; - } - case PPCASM_OPERAND_TYPE_CIMM: - { - wxString string; - // use symbol for function calls if available - uint32 callDest = disasmInstr.operand[o].immU32; - RPLStoredSymbol* storedSymbol = nullptr; - if (disasmInstr.ppcAsmCode == PPCASM_OP_BL || disasmInstr.ppcAsmCode == PPCASM_OP_BLA) - storedSymbol = rplSymbolStorage_getByAddress(callDest); - - if (storedSymbol) - { - // if symbol is within same module then don't display libname prefix - RPLModule* rplModuleCurrent = RPLLoader_FindModuleByCodeAddr(virtualAddress); // cache this - if (rplModuleCurrent && callDest >= rplModuleCurrent->regionMappingBase_text.GetMPTR() && callDest < (rplModuleCurrent->regionMappingBase_text.GetMPTR() + rplModuleCurrent->regionSize_text)) - string = wxString((char*)storedSymbol->symbolName); - else - string = wxString::Format("%s.%s", (char*)storedSymbol->libName, (char*)storedSymbol->symbolName); - - // truncate name after 36 characters - if (string.Length() >= 36) - { - string.Truncate(34); - string.Append(".."); - } - } - else - { - string = wxString::Format("0x%08x", disasmInstr.operand[o].immU32); - } - - WriteText(dc, string, position, theme_syntaxCallIMM); - - if (disasmInstr.ppcAsmCode != PPCASM_OP_BL) - { - wxString x = wxString(" "); - if (callDest <= virtualAddress) - x.Append(wxUniChar(0x2191)); // arrow up - else - x.Append(wxUniChar(0x2193)); // arrow down - - WriteText(dc, x, position, theme_textForeground); - } - - break; - } - case PPCASM_OPERAND_TYPE_MEM: - { - // offset - wxString string; - if (disasmInstr.operand[o].isSignedImm && disasmInstr.operand[o].immS32 >= 0) - string = wxString::Format("+0x%x", disasmInstr.operand[o].immS32); - else - string = wxString::Format("-0x%x", -disasmInstr.operand[o].immS32); - - WriteText(dc, string, position, theme_syntaxIMMOffset); - WriteText(dc, "(", position, theme_textForeground); - - // register - WriteText(dc, wxString::Format("r%d", disasmInstr.operand[o].registerIndex), position, theme_syntaxGPR); - WriteText(dc, ")", position, theme_textForeground); - break; - } - default: - // TODO - WriteText(dc, "", position, wxColour(0xFF444444)); - } - } - - position.x = start_width + OFFSET_DISASSEMBLY; - const auto comment = static_cast(rplDebugSymbol_getForAddress(virtualAddress)); - if (comment && comment->type == RplDebugSymbolComment) - WriteText(dc, comment->comment, position, theme_textForeground); - else if (isRLWINM) - { - sint32 rS, rA, SH, MB, ME; - rS = (opcode >> 21) & 0x1f; - rA = (opcode >> 16) & 0x1f; - SH = (opcode >> 11) & 0x1f; - MB = (opcode >> 6) & 0x1f; - ME = (opcode >> 1) & 0x1f; - - uint32 mask = ppcAssembler_generateMaskRLW(MB, ME); - - wxString string; - if (SH == 0) - string = wxString::Format("r%d=r%d&0x%x", rA, rS, mask); - else if ((0xFFFFFFFF << (uint32)disasmInstr.operand[2].immS32) == mask) - string = wxString::Format("r%d=r%d<<%d", rA, rS, SH); - else if ((0xFFFFFFFF >> (32 - SH) == mask)) - string = wxString::Format("r%d=r%d>>%d", rA, rS, 32 - SH); - else - string = wxString::Format("r%d=(r%d<<<%d)&0x%x", rA, rS, SH, mask); - WriteText(dc, string, position, theme_textForegroundMuted); - } - else if (disasmInstr.ppcAsmCode == PPCASM_OP_SUBF || disasmInstr.ppcAsmCode == PPCASM_OP_SUBF_) - { - sint32 rD, rA, rB; - rD = (opcode >> 21) & 0x1f; - rA = (opcode >> 16) & 0x1f; - rB = (opcode >> 11) & 0x1f; - - wxString string; - string = wxString::Format("r%d=r%d-r%d", rD, rB, rA); - WriteText(dc, string, position, theme_textForegroundMuted); - } -} - -void DisasmCtrl::DrawLabelName(wxDC& dc, const wxPoint& linePosition, MPTR virtualAddress, RPLStoredSymbol* storedSymbol) -{ - wxString symbol_string = wxString::Format("%s:", (char*)storedSymbol->symbolName); - if (symbol_string.Length() > MAX_SYMBOL_LEN) - { - symbol_string.Truncate(MAX_SYMBOL_LEN - 3); - symbol_string.Append("..:"); - } - wxPoint tmpPos(linePosition); - WriteText(dc, symbol_string, tmpPos, theme_syntaxSymbol); -} - -void DisasmCtrl::OnDraw(wxDC& dc, sint32 start, sint32 count, const wxPoint& start_position) -{ - wxPoint position(0, 0); - - RPLModule* current_rpl_module = RPLLoader_FindModuleByCodeAddr(GetViewBaseAddress()); - - if (currentCodeRegionStart == currentCodeRegionEnd) - return; - - sint32 viewFirstLine = GetViewStart().y; - sint32 lineOffset = start - viewFirstLine; - - cemu_assert_debug(lineOffset >= 0); - - sint32 instructionIndex = 0; - sint32 numLinesToUpdate = lineOffset + count; - numLinesToUpdate = std::min(numLinesToUpdate, (sint32)m_elements_visible); - - if(m_lineToAddress.size() != m_elements_visible) - m_lineToAddress.resize(m_elements_visible); - - sint32 lineIndex = 0; - while(lineIndex < numLinesToUpdate) - { - const uint32 virtualAddress = GetViewBaseAddress() + instructionIndex * 4; - instructionIndex++; - - if (virtualAddress < currentCodeRegionStart || - virtualAddress >= currentCodeRegionEnd) - { - NextLine(position, &start_position); - m_lineToAddress[lineIndex] = std::nullopt; - lineIndex++; - continue; - } - - // check if valid memory address - if (!memory_isAddressRangeAccessible(virtualAddress, 4)) - { - NextLine(position, &start_position); - m_lineToAddress[lineIndex] = std::nullopt; - lineIndex++; - continue; - } - - // draw symbol as label - RPLStoredSymbol* storedSymbol = rplSymbolStorage_getByAddress(virtualAddress); - if (storedSymbol) - { - if(lineIndex >= lineOffset) - DrawLabelName(dc, position, virtualAddress, storedSymbol); - m_lineToAddress[lineIndex] = virtualAddress; - lineIndex++; - if (lineIndex >= numLinesToUpdate) - break; - NextLine(position, &start_position); - } - m_lineToAddress[lineIndex] = virtualAddress; - if (lineIndex >= lineOffset) - DrawDisassemblyLine(dc, position, virtualAddress, current_rpl_module); - NextLine(position, &start_position); - lineIndex++; - } - - // draw vertical separator lines: offset | disassembly | comment - dc.SetPen(*wxLIGHT_GREY_PEN); - - wxPoint line_from = start_position; - line_from.x = OFFSET_ADDRESS + OFFSET_ADDRESS_RELATIVE - 5; - wxPoint line_to = line_from; - line_to.y += (count + 1) * m_line_height; - dc.DrawLine(line_from, line_to); - - line_from.x += OFFSET_DISASSEMBLY; - line_to.x += OFFSET_DISASSEMBLY; - dc.DrawLine(line_from, line_to); -} - -void DisasmCtrl::OnMouseMove(const wxPoint& start_position, uint32 line) -{ - /*m_mouse_line = line; - if (m_mouse_line_drawn != -1) - RefreshLine(m_mouse_line_drawn); - if (m_mouse_line != -1) - RefreshLine(m_mouse_line);*/ - - wxPoint position = start_position; - - // address - if (position.x <= OFFSET_ADDRESS) - { - return; - } - - position.x -= OFFSET_ADDRESS; - - // relative offset - if (position.x <= OFFSET_ADDRESS_RELATIVE) - { - return; - } - - position.x -= OFFSET_ADDRESS_RELATIVE; - - // disassembly code - if (position.x <= OFFSET_DISASSEMBLY) - { - if(m_mouse_down) - { - - } - - if (position.x <= OFFSET_DISASSEMBLY_OPERAND) - { - return; - } - - position.x -= OFFSET_DISASSEMBLY_OPERAND; - } -} - -void DisasmCtrl::OnKeyPressed(sint32 key_code, const wxPoint& position) -{ - auto optVirtualAddress = LinePixelPosToAddress(position.y); - switch (key_code) - { - case WXK_F9: - { - if (optVirtualAddress) - { - debugger_toggleExecuteBreakpoint(*optVirtualAddress); - - wxCommandEvent evt(wxEVT_BREAKPOINT_CHANGE); - wxPostEvent(this->m_parent, evt); - } - return; - } - case 'G': - { - if(IsKeyDown(WXK_CONTROL)) - { - GoToAddressDialog(); - return; - } - } - } - - // debugger currently in break state - PPCInterpreter_t* debugCpu = debugger_lockDebugSession(); - if (debugCpu) - { - switch (key_code) - { - case WXK_F5: - { - debugger_stepCommand(DebuggerStepCommand::Run); - break; - } - case WXK_F10: - { - debugger_stepCommand(DebuggerStepCommand::StepOver); - break; - } - case WXK_F11: - { - debugger_stepCommand(DebuggerStepCommand::StepInto); - break; - } - } - debugger_unlockDebugSession(debugCpu); - } - else - { - if (key_code == WXK_F5) - { - debugger_requestBreak(); - } - } -} - -void DisasmCtrl::OnMouseDClick(const wxPoint& position, uint32 line) -{ - wxPoint pos = position; - auto optVirtualAddress = LinePixelPosToAddress(position.y - GetViewStart().y * m_line_height); - if (!optVirtualAddress) - return; - MPTR virtualAddress = *optVirtualAddress; - - // address - if (pos.x <= OFFSET_ADDRESS + OFFSET_ADDRESS_RELATIVE) - { - // address + address relative - debugger_toggleExecuteBreakpoint(virtualAddress); - RefreshLine(line); - wxCommandEvent evt(wxEVT_BREAKPOINT_CHANGE); - wxPostEvent(this->m_parent, evt); - return; - } - else if (pos.x <= OFFSET_ADDRESS + OFFSET_ADDRESS_RELATIVE + OFFSET_DISASSEMBLY) - { - // double-clicked on disassembly (operation and operand data) - wxString currentInstruction = wxEmptyString; - wxTextEntryDialog set_value_dialog(this, _("Enter a new instruction."), wxString::Format(_("Overwrite instruction at address %08x"), virtualAddress), currentInstruction); - if (set_value_dialog.ShowModal() == wxID_OK) - { - PPCAssemblerInOut ctx = { 0 }; - ctx.virtualAddress = virtualAddress; - if (ppcAssembler_assembleSingleInstruction(set_value_dialog.GetValue().c_str(), &ctx)) - { - debugger_createPatch(virtualAddress, { ctx.outputData.data(), ctx.outputData.size() }); - RefreshLine(line); - } - } - return; - } - else - { - // comment - const auto comment = static_cast(rplDebugSymbol_getForAddress(virtualAddress)); - - wxString old_comment = wxEmptyString; - if (comment && comment->type == RplDebugSymbolComment) - old_comment = comment->comment; - - wxTextEntryDialog set_value_dialog(this, _("Enter a new comment."), wxString::Format(_("Create comment at address %08x"), virtualAddress), old_comment); - if (set_value_dialog.ShowModal() == wxID_OK) - { - rplDebugSymbol_createComment(virtualAddress, set_value_dialog.GetValue().wc_str()); - RefreshLine(line); - } - return; - } -} - -void DisasmCtrl::CopyToClipboard(std::string text) -{ - if (wxClipboard::Get()->Open()) - { - wxClipboard::Get()->SetData(new wxTextDataObject(text)); - wxClipboard::Get()->Close(); - } -} - -static uint32 GetUnrelocatedAddress(MPTR address) -{ - RPLModule* rplModule = RPLLoader_FindModuleByCodeAddr(address); - if (!rplModule) - return 0; - if (address >= rplModule->regionMappingBase_text.GetMPTR() && address < (rplModule->regionMappingBase_text.GetMPTR() + rplModule->regionSize_text)) - return 0x02000000 + (address - rplModule->regionMappingBase_text.GetMPTR()); - return 0; -} - -void DisasmCtrl::OnContextMenu(const wxPoint& position, uint32 line) -{ - auto optVirtualAddress = LinePixelPosToAddress(position.y - GetViewStart().y * m_line_height); - if (!optVirtualAddress) - return; - MPTR virtualAddress = *optVirtualAddress; - m_contextMenuAddress = virtualAddress; - // show dialog - wxMenu menu; - menu.Append(IDContextMenu_ToggleBreakpoint, _("Toggle breakpoint")); - menu.Append(IDContextMenu_ToggleLoggingBreakpoint, _("Toggle logging point")); - if(debugger_hasPatch(virtualAddress)) - menu.Append(IDContextMenu_RestoreOriginalInstructions, _("Restore original instructions")); - menu.AppendSeparator(); - menu.Append(IDContextMenu_CopyAddress, _("Copy address")); - uint32 unrelocatedAddress = GetUnrelocatedAddress(virtualAddress); - if (unrelocatedAddress && unrelocatedAddress != virtualAddress) - menu.Append(IDContextMenu_CopyUnrelocatedAddress, _("Copy virtual address (for IDA/Ghidra)")); - PopupMenu(&menu); -} - -void DisasmCtrl::OnContextMenuEntryClicked(wxCommandEvent& event) -{ - switch(event.GetId()) - { - case IDContextMenu_ToggleBreakpoint: - { - debugger_toggleExecuteBreakpoint(m_contextMenuAddress); - wxCommandEvent evt(wxEVT_BREAKPOINT_CHANGE); - wxPostEvent(this->m_parent, evt); - break; - } - case IDContextMenu_ToggleLoggingBreakpoint: - { - debugger_toggleLoggingBreakpoint(m_contextMenuAddress); - wxCommandEvent evt(wxEVT_BREAKPOINT_CHANGE); - wxPostEvent(this->m_parent, evt); - break; - } - case IDContextMenu_RestoreOriginalInstructions: - { - debugger_removePatch(m_contextMenuAddress); - wxCommandEvent evt(wxEVT_BREAKPOINT_CHANGE); // This also refreshes the disassembly view - wxPostEvent(this->m_parent, evt); - break; - } - case IDContextMenu_CopyAddress: - { - CopyToClipboard(fmt::format("{:#10x}", m_contextMenuAddress)); - break; - } - case IDContextMenu_CopyUnrelocatedAddress: - { - uint32 unrelocatedAddress = GetUnrelocatedAddress(m_contextMenuAddress); - CopyToClipboard(fmt::format("{:#10x}", unrelocatedAddress)); - break; - } - default: - UNREACHABLE; - } -} - -bool DisasmCtrl::OnShowTooltip(const wxPoint& position, uint32 line) -{ - return false; -} - -void DisasmCtrl::ScrollWindow(int dx, int dy, const wxRect* prect) -{ - // scroll and repaint everything - RefreshControl(nullptr); - TextList::ScrollWindow(dx, dy, nullptr); -} - -wxSize DisasmCtrl::DoGetBestSize() const -{ - return TextList::DoGetBestSize(); -} - -void DisasmCtrl::OnUpdateView() -{ - RefreshControl(); -} - -uint32 DisasmCtrl::GetViewBaseAddress() const -{ - if (GetViewStart().y < 0) - return MPTR_NULL; - return currentCodeRegionStart + GetViewStart().y * 4; -} - -std::optional DisasmCtrl::LinePixelPosToAddress(sint32 posY) -{ - if (posY < 0) - return std::nullopt; - - sint32 lineIndex = posY / m_line_height; - if (lineIndex >= m_lineToAddress.size()) - return std::nullopt; - - return m_lineToAddress[lineIndex]; -} - -uint32 DisasmCtrl::AddressToScrollPos(uint32 offset) const -{ - return (offset - currentCodeRegionStart) / 4; -} - -void DisasmCtrl::CenterOffset(uint32 offset) -{ - if (offset < currentCodeRegionStart || offset >= currentCodeRegionEnd) - SelectCodeRegion(offset); - - const sint32 line = AddressToScrollPos(offset); - if (line < 0 || line >= (sint32)m_element_count) - return; - - if (m_active_line != -1) - RefreshLine(m_active_line); - - DoScroll(0, std::max(0, line - (sint32)(m_elements_visible / 2))); - - m_active_line = line; - RefreshLine(m_active_line); -} - -void DisasmCtrl::GoToAddressDialog() -{ - wxTextEntryDialog goto_dialog(this, _("Enter a target address."), _("GoTo address"), wxEmptyString); - if (goto_dialog.ShowModal() == wxID_OK) - { - ExpressionParser parser; - - auto value = goto_dialog.GetValue().ToStdString(); - std::transform(value.begin(), value.end(), value.begin(), tolower); - - // trim any leading spaces - while(!value.empty() && value[0] == ' ') - value.erase(value.begin()); - - debugger_addParserSymbols(parser); - - // try to parse expression as hex value first (it should interpret 1234 as 0x1234, not 1234) - if (parser.IsConstantExpression("0x"+value)) - { - const auto result = (uint32)parser.Evaluate("0x"+value); - m_lastGotoTarget = result; - CenterOffset(result); - wxCommandEvent evt(wxEVT_DISASMCTRL_NOTIFY_GOTO_ADDRESS); - evt.SetExtraLong(static_cast(result)); - wxPostEvent(GetParent(), evt); - } - else if (parser.IsConstantExpression(value)) - { - const auto result = (uint32)parser.Evaluate(value); - m_lastGotoTarget = result; - CenterOffset(result); - wxCommandEvent evt(wxEVT_DISASMCTRL_NOTIFY_GOTO_ADDRESS); - evt.SetExtraLong(static_cast(result)); - wxPostEvent(GetParent(), evt); - } - else - { - try - { - // if not a constant expression (i.e. relying on unknown variables), then evaluating will throw an exception with a detailed error message - const auto _ = (uint32)parser.Evaluate(value); - } - catch (const std::exception& ex) - { - wxMessageBox(ex.what(), _("Error"), wxOK | wxCENTRE | wxICON_ERROR, this); - } - } - } -} diff --git a/src/gui/wxgui/debugger/DisasmCtrl.h b/src/gui/wxgui/debugger/DisasmCtrl.h deleted file mode 100644 index 61f7afa6..00000000 --- a/src/gui/wxgui/debugger/DisasmCtrl.h +++ /dev/null @@ -1,60 +0,0 @@ -#pragma once -#include "wxgui/components/TextList.h" - -wxDECLARE_EVENT(wxEVT_DISASMCTRL_NOTIFY_GOTO_ADDRESS, wxCommandEvent); // Notify parent that goto address operation completed. Event contains the address that was jumped to. - -class DisasmCtrl : public TextList -{ - enum - { - IDContextMenu_ToggleBreakpoint = wxID_HIGHEST + 1, - IDContextMenu_ToggleLoggingBreakpoint, - IDContextMenu_RestoreOriginalInstructions, - IDContextMenu_CopyAddress, - IDContextMenu_CopyUnrelocatedAddress, - IDContextMenu_Last - }; -public: - - DisasmCtrl(wxWindow* parent, const wxWindowID& id, const wxPoint& pos, const wxSize& size, long style); - - void Init(); - wxSize DoGetBestSize() const override; - - void OnUpdateView(); - - uint32 GetViewBaseAddress() const; - std::optional LinePixelPosToAddress(sint32 posY); - - uint32 AddressToScrollPos(uint32 offset) const; - void CenterOffset(uint32 offset); - void GoToAddressDialog(); - -protected: - void OnDraw(wxDC& dc, sint32 start, sint32 count, const wxPoint& start_position) override; - void OnMouseMove(const wxPoint& position, uint32 line) override; - void OnKeyPressed(sint32 key_code, const wxPoint& position) override; - void OnMouseDClick(const wxPoint& position, uint32 line) override; - void OnContextMenu(const wxPoint& position, uint32 line) override; - void OnContextMenuEntryClicked(wxCommandEvent& event); - bool OnShowTooltip(const wxPoint& position, uint32 line) override; - void ScrollWindow(int dx, int dy, const wxRect* prect) override; - - void DrawDisassemblyLine(wxDC& dc, const wxPoint& linePosition, MPTR virtualAddress, struct RPLModule* rplModule); - void DrawLabelName(wxDC& dc, const wxPoint& linePosition, MPTR virtualAddress, struct RPLStoredSymbol* storedSymbol); - - void SelectCodeRegion(uint32 newAddress); - -private: - void CopyToClipboard(std::string text); - - sint32 m_mouse_line, m_mouse_line_drawn; - sint32 m_active_line; - uint32 m_lastGotoTarget{}; - uint32 m_contextMenuAddress{}; - // code region info - uint32 currentCodeRegionStart; - uint32 currentCodeRegionEnd; - // line to address mapping - std::vector> m_lineToAddress; -}; diff --git a/src/gui/wxgui/debugger/DumpCtrl.cpp b/src/gui/wxgui/debugger/DumpCtrl.cpp deleted file mode 100644 index 8bfb2dff..00000000 --- a/src/gui/wxgui/debugger/DumpCtrl.cpp +++ /dev/null @@ -1,441 +0,0 @@ -#include "wxgui/wxgui.h" -#include "wxgui/debugger/DumpCtrl.h" -#include "Cafe/OS/RPL/rpl.h" -#include "Cafe/OS/RPL/rpl_structs.h" -#include "Cafe/HW/Espresso/Debugger/Debugger.h" -#include "util/helpers/helpers.h" - -#include "Cemu/ExpressionParser/ExpressionParser.h" - - -#define OFFSET_ADDRESS (60) -#define OFFSET_ADDRESS_RELATIVE (90) -#define OFFSET_MEMORY (450) - -enum { - ID_WRITE_U8 = wxID_HIGHEST + 1, - ID_WRITE_U16, - ID_WRITE_U32, - ID_WRITE_FLOAT, - ID_WRITE_STRING -}; - -DumpCtrl::DumpCtrl(wxWindow* parent, const wxWindowID& id, const wxPoint& pos, const wxSize& size, long style) - : TextList(parent, id, pos, size, style) -{ - MMURange* range = memory_getMMURangeByAddress(0x10000000); - if (range) - { - m_memoryRegion.baseAddress = range->getBase(); - m_memoryRegion.size = range->getSize(); - Init(); - } - else - { - m_memoryRegion.baseAddress = 0x10000000; - m_memoryRegion.size = 0x1000; - Init(); - } - - Bind(wxEVT_MENU, &DumpCtrl::OnMenuSelected, this); -} - -void DumpCtrl::Init() -{ - uint32 element_count = m_memoryRegion.size; - this->SetElementCount(element_count / 0x10); - Scroll(0, 0); - RefreshControl(); -} - -void DumpCtrl::OnDraw(wxDC& dc, sint32 start, sint32 count, const wxPoint& start_position) -{ - wxPoint position = start_position; - uint32 endAddr = m_memoryRegion.baseAddress + m_memoryRegion.size; - RPLModule* currentCodeRPL = RPLLoader_FindModuleByCodeAddr(LineToOffset(start)); - RPLModule* currentDataRPL = RPLLoader_FindModuleByDataAddr(LineToOffset(start)); - for (sint32 i = 0; i <= count; i++) - { - const uint32 virtual_address = LineToOffset(start + i); - - dc.SetTextForeground(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT)); - dc.DrawText(wxString::Format("%08x", virtual_address), position); - position.x += OFFSET_ADDRESS; - - dc.SetTextForeground(wxSystemSettings::GetColour(wxSYS_COLOUR_GRAYTEXT)); - if (currentCodeRPL) - { - dc.DrawText(wxString::Format("+0x%-8x", virtual_address - currentCodeRPL->regionMappingBase_text.GetMPTR()), position); - } - else if (currentDataRPL) - { - dc.DrawText(wxString::Format("+0x%-8x", virtual_address - currentDataRPL->regionMappingBase_data), position); - } - else - { - dc.DrawText("???", position); - } - - position.x += OFFSET_ADDRESS_RELATIVE; - - sint32 start_width = position.x; - - if (!memory_isAddressRangeAccessible(virtual_address, 0x10)) - { - for (sint32 f=0; f<0x10; f++) - { - wxPoint p(position); - WriteText(dc, wxString::Format("?? "), p); - position.x += (m_char_width * 3); - } - } - else - { - std::array data; - memory_readBytes(virtual_address, data); - for (auto b : data) - { - wxPoint p(position); - WriteText(dc, wxString::Format("%02x ", b), p); - position.x += (m_char_width * 3); - } - position.x = start_width = OFFSET_MEMORY; - dc.SetTextForeground(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT)); - for (auto b : data) - { - if (isprint(b)) - dc.DrawText(wxString::Format("%c ", b), position); - else - dc.DrawText(".", position); - - position.x += m_char_width; - } - } - - // display goto indicator - if (m_lastGotoOffset >= virtual_address && m_lastGotoOffset < (virtual_address + 16)) - { - sint32 indicatorX = start_position.x + OFFSET_ADDRESS + OFFSET_ADDRESS_RELATIVE + (m_lastGotoOffset - virtual_address) * m_char_width * 3; - wxPoint line1(start_position.x + indicatorX - 2, position.y); - wxPoint line2(line1.x, line1.y + m_line_height); - dc.SetPen(*wxRED_PEN); - dc.DrawLine(line1, line2); - dc.DrawLine(line1, wxPoint(line1.x + 3, line1.y)); - dc.DrawLine(line2, wxPoint(line2.x + 3, line2.y)); - } - - NextLine(position, &start_position); - } - - // draw vertical separator lines for 4 byte blocks - sint32 cursorOffsetHexBytes = start_position.x + OFFSET_ADDRESS + OFFSET_ADDRESS_RELATIVE; - wxPoint line_from( - cursorOffsetHexBytes + m_char_width * (3 * 4 - 1) + m_char_width / 2, - start_position.y - ); - wxPoint line_to(line_from.x, line_from.y + m_line_height * (count + 1)); - dc.SetPen(wxSystemSettings::GetColour(wxSYS_COLOUR_INACTIVECAPTIONTEXT)); - for (sint32 i = 0; i < 3; i++) - { - dc.DrawLine(line_from, line_to); - line_from.x += m_char_width * (3 * 4); - line_to.x += m_char_width * (3 * 4); - } -} - -void DumpCtrl::OnMouseMove(const wxPoint& start_position, uint32 line) -{ - wxPoint position = start_position; - - // address - if (position.x <= OFFSET_ADDRESS) - { - return; - } - - position.x -= OFFSET_ADDRESS; - - // relative offset - if (position.x <= OFFSET_ADDRESS_RELATIVE) - { - return; - } - - position.x -= OFFSET_ADDRESS_RELATIVE; - - // byte code - if (position.x <= OFFSET_MEMORY) - { - - } - - // string view - position.x -= OFFSET_MEMORY; -} - -uint32 DumpCtrl::PositionToAddress(const wxPoint& position, uint32 line) -{ - wxPoint pos = position; - - if (pos.x <= OFFSET_ADDRESS + OFFSET_ADDRESS_RELATIVE) - return MPTR_NULL; - - pos.x -= OFFSET_ADDRESS + OFFSET_ADDRESS_RELATIVE; - - if (pos.x > OFFSET_MEMORY) - return MPTR_NULL; - - const uint32 byteIndex = (pos.x / m_char_width) / 3; - return LineToOffset(line) + byteIndex; -} - -void DumpCtrl::OnMouseDClick(const wxPoint& position, uint32 line) -{ - uint32 address = PositionToAddress(position, line); - - if (address == MPTR_NULL) - return; - - if (!memory_isAddressRangeAccessible(address, 1)) - return; - - if (WriteNumericDialog(address)) - { - wxRect updateRect(0, line * m_line_height, GetSize().x, m_line_height); - RefreshControl(&updateRect); - } -} - -void DumpCtrl::GoToAddressDialog() -{ - wxTextEntryDialog goto_dialog(this, _("Enter a target address."), _("GoTo address"), wxEmptyString); - if (goto_dialog.ShowModal() == wxID_OK) - { - ExpressionParser parser; - - auto value = goto_dialog.GetValue().ToStdString(); - std::transform(value.begin(), value.end(), value.begin(), tolower); - - debugger_addParserSymbols(parser); - - // try to parse expression as hex value first (it should interpret 1234 as 0x1234, not 1234) - if (parser.IsConstantExpression("0x"+value)) - { - const auto result = (uint32)parser.Evaluate("0x"+value); - m_lastGotoOffset = result; - CenterOffset(result); - } - else if (parser.IsConstantExpression(value)) - { - const auto result = (uint32)parser.Evaluate(value); - m_lastGotoOffset = result; - CenterOffset(result); - } - else - { - try - { - const auto _ = (uint32)parser.Evaluate(value); - } - catch (const std::exception& ex) - { - wxMessageBox(ex.what(), _("Error"), wxOK | wxCENTRE | wxICON_ERROR, this); - } - } - } -} - -void DumpCtrl::CenterOffset(uint32 offset) -{ - // check if the offset is valid - if (!memory_isAddressRangeAccessible(offset, 1)) - return; - // set region and line - MMURange* range = memory_getMMURangeByAddress(offset); - if (m_memoryRegion.baseAddress != range->getBase() || m_memoryRegion.size != range->getSize()) - { - m_memoryRegion.baseAddress = range->getBase(); - m_memoryRegion.size = range->getSize(); - Init(); - } - - const sint32 line = OffsetToLine(offset); - if (line < 0 || line >= (sint32)m_element_count) - return; - - DoScroll(0, std::max(0, line - ((sint32)m_elements_visible / 2))); - - RefreshControl(); - //RefreshLine(line); -} - -uint32 DumpCtrl::LineToOffset(uint32 line) -{ - return m_memoryRegion.baseAddress + line * 0x10; -} - -uint32 DumpCtrl::OffsetToLine(uint32 offset) -{ - return (offset - m_memoryRegion.baseAddress) / 0x10; -} - -void DumpCtrl::OnKeyPressed(sint32 key_code, const wxPoint& position) -{ - switch (key_code) - { - case 'G': - { - if (IsKeyDown(WXK_CONTROL)) - { - GoToAddressDialog(); - return; - } - } - } -} - -wxSize DumpCtrl::DoGetBestSize() const -{ - return TextList::DoGetBestSize(); -} - -void DumpCtrl::OnContextMenu(const wxPoint& position, uint32 line) -{ - const uint32 address = PositionToAddress(position, line); - - if (!memory_isAddressRangeAccessible(address, 1)) - return; - - m_writerContextAddress = address; - m_writerContextLine = line; - - wxMenu menu; - - menu.Append(ID_WRITE_U8, _("Write Byte")); - menu.Append(ID_WRITE_U16, _("Write Int16")); - menu.Append(ID_WRITE_U32, _("Write Int32")); - menu.Append(ID_WRITE_FLOAT, _("Write Float")); - menu.Append(ID_WRITE_STRING, _("Write String")); - - PopupMenu(&menu); -} - -void DumpCtrl::OnMenuSelected(wxCommandEvent& event) -{ - bool update = false; - switch (event.GetId()) - { - case ID_WRITE_U8: - update = WriteNumericDialog(m_writerContextAddress); - break; - case ID_WRITE_U16: - update = WriteNumericDialog(m_writerContextAddress); - break; - case ID_WRITE_U32: - update = WriteNumericDialog(m_writerContextAddress); - break; - case ID_WRITE_FLOAT: - update = WriteNumericDialog(m_writerContextAddress); - break; - case ID_WRITE_STRING: - update = WriteString(m_writerContextAddress); - break; - } - - if (update) - { - wxRect updateRect(0, m_writerContextLine * m_line_height, GetSize().x, m_line_height); - RefreshControl(&updateRect); - } -} - -template -bool DumpCtrl::WriteNumericDialog(uint32 address) -{ - static_assert( - std::is_same::value || - std::is_same::value || - std::is_same::value || - std::is_same::value, - "Unsupported type" - ); - - T value; - const char* dataType; - wxString label; - - if constexpr (std::is_same::value) - { - value = memory_readU8(address); - dataType = "byte"; - label = wxString::Format("0x%02x", value); - } - else if constexpr (std::is_same::value) - { - value = memory_readU16(address); - dataType = "int16"; - label = wxString::Format("0x%04x", value); - } - else if constexpr (std::is_same::value) - { - value = memory_readU32(address); - dataType = "int32"; - label = wxString::Format("0x%08x", value); - } - else if constexpr (std::is_same::value) - { - value = memory_readFloat(address); - dataType = "float"; - label = wxString::Format("%f", value); - } - - wxTextEntryDialog dialog( - this, - _("Enter a new value."), - wxString::Format(_("Write %s at address 0x%08x"), dataType, address), - label - ); - - if (dialog.ShowModal() != wxID_OK) - return false; - - - const T newValue = ConvertString(dialog.GetValue().ToStdString()); - - if constexpr (std::is_same::value) - memory_writeU8(address, newValue); - else if constexpr (std::is_same::value) - memory_writeU16(address, newValue); - else if constexpr (std::is_same::value) - memory_writeU32(address, newValue); - else if constexpr (std::is_same::value) - memory_writeFloat(address, newValue); - - return true; -} - -bool DumpCtrl::WriteString(uint32 address) -{ - wxTextEntryDialog dialog( - this, - _("Enter string"), - wxString::Format(_("Write string at address 0x%08x"), address), - "" - ); - - if (dialog.ShowModal() != wxID_OK) - return false; - - std::string text = dialog.GetValue().ToStdString(); - - if (text.empty()) - return false; - - for (size_t i = 0; i < text.size(); i++) - memory_writeU8(address + i, static_cast(text[i])); - - // null-terminator - memory_writeU8(address + text.size(), 0); - - return true; -} diff --git a/src/gui/wxgui/debugger/DumpCtrl.h b/src/gui/wxgui/debugger/DumpCtrl.h deleted file mode 100644 index b63bbc85..00000000 --- a/src/gui/wxgui/debugger/DumpCtrl.h +++ /dev/null @@ -1,38 +0,0 @@ -#pragma once -#include "wxgui/components/TextList.h" - -class DumpCtrl : public TextList -{ -public: - DumpCtrl(wxWindow* parent, const wxWindowID& id, const wxPoint& pos, const wxSize& size, long style); - - void Init(); - wxSize DoGetBestSize() const override; - -protected: - void GoToAddressDialog(); - void CenterOffset(uint32 offset); - uint32 LineToOffset(uint32 line); - uint32 OffsetToLine(uint32 offset); - uint32 PositionToAddress(const wxPoint& position, uint32 line); - - void OnDraw(wxDC& dc, sint32 start, sint32 count, const wxPoint& start_position) override; - void OnMouseMove(const wxPoint& position, uint32 line) override; - void OnMouseDClick(const wxPoint& position, uint32 line) override; - void OnKeyPressed(sint32 key_code, const wxPoint& position) override; - void OnContextMenu(const wxPoint& position, uint32 line) override; - void OnMenuSelected(wxCommandEvent& event); - - template - bool WriteNumericDialog(uint32 address); - bool WriteString(uint32 address); -private: - struct - { - uint32 baseAddress; - uint32 size; - }m_memoryRegion; - uint32 m_lastGotoOffset{0}; - uint32 m_writerContextAddress {0}; - uint32 m_writerContextLine {0}; -}; diff --git a/src/gui/wxgui/debugger/DumpWindow.cpp b/src/gui/wxgui/debugger/DumpWindow.cpp deleted file mode 100644 index f152e8f6..00000000 --- a/src/gui/wxgui/debugger/DumpWindow.cpp +++ /dev/null @@ -1,46 +0,0 @@ -#include "wxgui/wxgui.h" -#include "wxgui/debugger/DumpWindow.h" - -#include "wxgui/debugger/DebuggerWindow2.h" -#include "Cafe/HW/Espresso/Debugger/Debugger.h" -#include "wxgui/debugger/DumpCtrl.h" - -enum -{ - // REGISTER - REGISTER_ADDRESS_R0 = wxID_HIGHEST + 8200, - REGISTER_LABEL_R0 = REGISTER_ADDRESS_R0 + 32, - REGISTER_LABEL_FPR0_0 = REGISTER_LABEL_R0 + 32, - REGISTER_LABEL_FPR1_0 = REGISTER_LABEL_R0 + 32, -}; - -DumpWindow::DumpWindow(DebuggerWindow2& parent, const wxPoint& main_position, const wxSize& main_size) - : wxFrame(&parent, wxID_ANY, _("Memory Dump"), wxDefaultPosition, wxSize(600, 250), wxSYSTEM_MENU | wxCAPTION | wxCLIP_CHILDREN | wxRESIZE_BORDER | wxFRAME_FLOAT_ON_PARENT) -{ - wxBoxSizer* main_sizer = new wxBoxSizer(wxVERTICAL); - m_dump_ctrl = new DumpCtrl(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxScrolledWindowStyle); - main_sizer->Add(m_dump_ctrl, 1, wxEXPAND); - - this->SetSizer(main_sizer); - this->wxWindowBase::Layout(); - - this->Centre(wxBOTH); - - if (parent.GetConfig().data().pin_to_main) - OnMainMove(main_position, main_size); -} - -void DumpWindow::OnMainMove(const wxPoint& main_position, const wxSize& main_size) -{ - wxSize size(600, 250); - this->SetSize(size); - - wxPoint position = main_position; - position.y += main_size.GetHeight(); - this->SetPosition(position); -} - -void DumpWindow::OnGameLoaded() -{ - m_dump_ctrl->Init(); -} diff --git a/src/gui/wxgui/debugger/DumpWindow.h b/src/gui/wxgui/debugger/DumpWindow.h deleted file mode 100644 index e45f3489..00000000 --- a/src/gui/wxgui/debugger/DumpWindow.h +++ /dev/null @@ -1,18 +0,0 @@ -#pragma once - -#include "wxgui/debugger/DumpCtrl.h" - -class DebuggerWindow2; - -class DumpWindow : public wxFrame -{ -public: - DumpWindow(DebuggerWindow2& parent, const wxPoint& main_position, const wxSize& main_size); - - void OnMainMove(const wxPoint& position, const wxSize& main_size); - void OnGameLoaded(); - -private: - wxScrolledWindow* m_scrolled_window; - DumpCtrl* m_dump_ctrl; -}; \ No newline at end of file diff --git a/src/gui/wxgui/debugger/ModuleWindow.cpp b/src/gui/wxgui/debugger/ModuleWindow.cpp deleted file mode 100644 index 77a36d09..00000000 --- a/src/gui/wxgui/debugger/ModuleWindow.cpp +++ /dev/null @@ -1,132 +0,0 @@ -#include "wxgui/wxgui.h" -#include "wxgui/debugger/ModuleWindow.h" - -#include - -#include "wxgui/debugger/DebuggerWindow2.h" -#include "Cafe/HW/Espresso/Debugger/Debugger.h" - -#include "Cafe/OS/RPL/rpl.h" -#include "Cafe/OS/RPL/rpl_structs.h" - -#include "Cafe/GraphicPack/GraphicPack2.h" - -enum ItemColumns -{ - ColumnName = 0, - ColumnAddress, - ColumnSize, -}; - -ModuleWindow::ModuleWindow(DebuggerWindow2& parent, const wxPoint& main_position, const wxSize& main_size) - : wxFrame(&parent, wxID_ANY, _("Modules"), wxDefaultPosition, wxSize(420, 250), wxSYSTEM_MENU | wxCAPTION | wxCLIP_CHILDREN | wxRESIZE_BORDER | wxFRAME_FLOAT_ON_PARENT) -{ - this->SetSizeHints(wxDefaultSize, wxDefaultSize); - - wxBoxSizer* main_sizer = new wxBoxSizer(wxVERTICAL); - - m_modules = new wxListView(this, wxID_ANY); - - wxListItem col0; - col0.SetId(ColumnName); - col0.SetText(_("Name")); - col0.SetWidth(125); - m_modules->InsertColumn(ColumnName, col0); - - wxListItem col1; - col1.SetId(ColumnAddress); - col1.SetText(_("Address")); - col1.SetWidth(75); - col1.SetAlign(wxLIST_FORMAT_RIGHT); - m_modules->InsertColumn(ColumnAddress, col1); - - wxListItem col2; - col2.SetId(ColumnSize); - col2.SetText(_("Size")); - col2.SetWidth(75); - col2.SetAlign(wxLIST_FORMAT_RIGHT); - m_modules->InsertColumn(ColumnSize, col2); - - main_sizer->Add(m_modules, 1, wxEXPAND); - - this->SetSizer(main_sizer); - this->wxWindowBase::Layout(); - - this->Centre(wxBOTH); - - if (parent.GetConfig().data().pin_to_main) - OnMainMove(main_position, main_size); - - m_modules->Bind(wxEVT_LEFT_DCLICK, &ModuleWindow::OnLeftDClick, this); - - OnGameLoaded(); -} - -void ModuleWindow::OnMainMove(const wxPoint& main_position, const wxSize& main_size) -{ - wxSize size(420, 250); - this->SetSize(size); - - wxPoint position = main_position; - position.x -= 420; - position.y += main_size.y; - this->SetPosition(position); -} - - -void ModuleWindow::OnGameLoaded() -{ - Freeze(); - - m_modules->DeleteAllItems(); - - const auto module_list = RPLLoader_GetModuleList(); - for (size_t i = 0; i < module_list.size(); i++) - { - const auto module = module_list[i]; - if (module) - { - wxListItem item; - item.SetId(i); - item.SetText(module->moduleName); - - const auto index = m_modules->InsertItem(item); - m_modules->SetItem(index, ColumnAddress, wxString::Format("%08x", module->regionMappingBase_text.GetMPTR())); - m_modules->SetItem(index, ColumnSize, wxString::Format("%x", module->regionSize_text)); - } - } - - sint32 patch_count = 0; - for (auto& gfx_pack : GraphicPack2::GetGraphicPacks()) - { - for (auto& patch_group : gfx_pack->GetPatchGroups()) - { - if (patch_group->isApplied()) - { - wxListItem item; - item.SetId(module_list.size() + patch_count); - item.SetText(std::string(patch_group->getName())); - - const auto index = m_modules->InsertItem(item); - m_modules->SetItem(index, ColumnAddress, wxString::Format("%08x", patch_group->getCodeCaveBase())); - m_modules->SetItem(index, ColumnSize, wxString::Format("%x", patch_group->getCodeCaveSize())); - patch_count++; - } - } - } - - Thaw(); -} - - -void ModuleWindow::OnLeftDClick(wxMouseEvent& event) -{ - long selected = m_modules->GetFirstSelected(); - if (selected == wxNOT_FOUND) - return; - const auto text = m_modules->GetItemText(selected, ColumnAddress); - const auto address = std::stoul(text.ToStdString(), nullptr, 16); - if (address == 0) - return; - debugger_jumpToAddressInDisasm(address); -} diff --git a/src/gui/wxgui/debugger/ModuleWindow.h b/src/gui/wxgui/debugger/ModuleWindow.h deleted file mode 100644 index 76bb0c67..00000000 --- a/src/gui/wxgui/debugger/ModuleWindow.h +++ /dev/null @@ -1,20 +0,0 @@ -#pragma once - -#include - -class DebuggerWindow2; -class wxListView; - -class ModuleWindow : public wxFrame -{ -public: - ModuleWindow(DebuggerWindow2& parent, const wxPoint& main_position, const wxSize& main_size); - - void OnMainMove(const wxPoint& position, const wxSize& main_size); - void OnGameLoaded(); - -private: - void OnLeftDClick(wxMouseEvent& event); - - wxListView* m_modules; -}; diff --git a/src/gui/wxgui/debugger/RegisterWindow.cpp b/src/gui/wxgui/debugger/RegisterWindow.cpp deleted file mode 100644 index 1e6605b1..00000000 --- a/src/gui/wxgui/debugger/RegisterWindow.cpp +++ /dev/null @@ -1,485 +0,0 @@ -#include "wxgui/wxgui.h" -#include "wxgui/debugger/RegisterWindow.h" - -#include - -#include "wxgui/debugger/DebuggerWindow2.h" -#include "Cafe/HW/Espresso/Debugger/Debugger.h" -#include "Cafe/OS/RPL/rpl.h" -#include "Cafe/OS/RPL/rpl_structs.h" -#include "Cafe/HW/Espresso/EspressoISA.h" -#include "util/helpers/helpers.h" - -enum -{ - // REGISTER - kRegisterValueR0 = wxID_HIGHEST + 8400, - kRegisterLabelR0 = kRegisterValueR0 + 32, - kRegisterLabelLR = kRegisterLabelR0 + 32, - kRegisterValueLR = kRegisterLabelLR + 1, - kRegisterValueFPR0_0 = kRegisterValueLR + 32, - kRegisterValueFPR1_0 = kRegisterValueFPR0_0 + 32, - kRegisterValueCR0 = kRegisterValueFPR1_0 + 32, - kContextMenuZero, - kContextMenuInc, - kContextMenuDec, - kContextMenuCopy, - kContextMenuGotoDisasm, - kContextMenuGotoDump, -}; - -RegisterWindow::RegisterWindow(DebuggerWindow2& parent, const wxPoint& main_position, const wxSize& main_size) - : wxFrame(&parent, wxID_ANY, _("Registers"), wxDefaultPosition, wxSize(400, 975), wxSYSTEM_MENU | wxCAPTION | wxCLIP_CHILDREN | wxRESIZE_BORDER | wxFRAME_FLOAT_ON_PARENT), - m_prev_snapshot({}), m_show_double_values(true), m_context_ctrl(nullptr) -{ - SetSizeHints(wxDefaultSize, wxDefaultSize); - SetMaxSize({ 400, 975 }); - this->SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW)); - - wxBoxSizer* main_sizer = new wxBoxSizer(wxVERTICAL); - - auto scrolled_win = new wxScrolledWindow(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxVSCROLL); - wxBoxSizer* sizer = new wxBoxSizer(wxVERTICAL); - - auto gpr_sizer = new wxFlexGridSizer(0, 3, 0, 0); - - // GPRs - for(sint32 i = 0; i < 32; ++i) - { - gpr_sizer->Add(new wxStaticText(scrolled_win, wxID_ANY, wxString::Format("R%d", i)), 0, wxLEFT, 5); - - auto value = new wxTextCtrl(scrolled_win, kRegisterValueR0 + i, wxString::Format("%08x", 0), wxDefaultPosition, wxDefaultSize, wxTE_READONLY | wxNO_BORDER | wxTE_RICH2); - value->SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW)); - value->SetForegroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT)); - value->Bind(wxEVT_LEFT_DCLICK, &RegisterWindow::OnMouseDClickEvent, this); - //value->Bind(wxEVT_CONTEXT_MENU, &RegisterWindow::OnValueContextMenu, this); - gpr_sizer->Add(value, 0, wxLEFT|wxRIGHT, 5); - - auto label = new wxTextCtrl(scrolled_win, kRegisterLabelR0 + i, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxTE_READONLY | wxNO_BORDER | wxTE_RICH2); - label->SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW)); - label->SetForegroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT)); - label->SetMinSize(wxSize(500, -1)); - gpr_sizer->Add(label, 0, wxEXPAND); - } - - { - // LR - gpr_sizer->Add(new wxStaticText(scrolled_win, wxID_ANY, wxString::Format("LR")), 0, wxLEFT, 5); - auto value = new wxTextCtrl(scrolled_win, kRegisterValueLR, wxString::Format("%08x", 0), wxDefaultPosition, wxDefaultSize, wxTE_READONLY | wxNO_BORDER | wxTE_RICH2); - value->SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW)); - value->SetForegroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT)); - value->Bind(wxEVT_LEFT_DCLICK, &RegisterWindow::OnMouseDClickEvent, this); - //value->Bind(wxEVT_CONTEXT_MENU, &RegisterWindow::OnValueContextMenu, this); - gpr_sizer->Add(value, 0, wxLEFT | wxRIGHT, 5); - auto label = new wxTextCtrl(scrolled_win, kRegisterLabelLR, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxTE_READONLY | wxNO_BORDER | wxTE_RICH2); - label->SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW)); - label->SetForegroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT)); - label->SetMinSize(wxSize(500, -1)); - gpr_sizer->Add(label, 0, wxEXPAND); - } - - sizer->Add(gpr_sizer, 1, wxEXPAND); - - auto button = new wxButton(scrolled_win, wxID_ANY, _("FP view mode")); - button->Bind(wxEVT_BUTTON, &RegisterWindow::OnFPViewModePress, this); - sizer->Add(button, 0, wxALL, 5); - - auto fp_sizer = new wxFlexGridSizer(0, 3, 0, 0); - for (sint32 i = 0; i < 32; ++i) - { - fp_sizer->Add(new wxStaticText(scrolled_win, wxID_ANY, wxString::Format("FP%d", i)), 0, wxLEFT, 5); - - auto value0 = new wxTextCtrl(scrolled_win, kRegisterValueFPR0_0 + i, wxString::Format("%lf", 0.0), wxDefaultPosition, wxDefaultSize, wxTE_READONLY | wxNO_BORDER | wxTE_RICH2); - value0->SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW)); - value0->SetForegroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT)); - value0->Bind(wxEVT_LEFT_DCLICK, &RegisterWindow::OnMouseDClickEvent, this); - fp_sizer->Add(value0, 0, wxLEFT | wxRIGHT, 5); - - auto value1 = new wxTextCtrl(scrolled_win, kRegisterValueFPR1_0 + i, wxString::Format("%lf", 0.0), wxDefaultPosition, wxDefaultSize, wxTE_READONLY | wxNO_BORDER | wxTE_RICH2); - value1->SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW)); - value1->SetForegroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT)); - value1->Bind(wxEVT_LEFT_DCLICK, &RegisterWindow::OnMouseDClickEvent, this); - fp_sizer->Add(value1, 0, wxLEFT | wxRIGHT, 5); - } - - sizer->Add(fp_sizer, 0, wxEXPAND); - auto cr_sizer = new wxFlexGridSizer(0, 2, 0, 0); - - // CRs - for (sint32 i = 0; i < 8; ++i) - { - cr_sizer->Add(new wxStaticText(scrolled_win, wxID_ANY, wxString::Format("CR%d", i)), 0, wxLEFT, 5); - auto value = new wxTextCtrl(scrolled_win, kRegisterValueCR0 + i, "-", wxDefaultPosition, wxDefaultSize, wxTE_READONLY | wxNO_BORDER | wxTE_RICH2); - value->SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW)); - value->SetForegroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT)); - //value->Bind(wxEVT_CONTEXT_MENU, &RegisterWindow::OnValueContextMenu, this); - cr_sizer->Add(value, 0, wxRIGHT, 5); - } - - sizer->Add(cr_sizer, 0, wxEXPAND); - scrolled_win->SetSizerAndFit(sizer); - scrolled_win->SetScrollRate(-1, 10); - - main_sizer->Add(scrolled_win, 1, wxEXPAND); - SetSizer(main_sizer); - Layout(); - - if (parent.GetConfig().data().pin_to_main) - OnMainMove(main_position, main_size); - - //Bind(wxEVT_COMMAND_MENU_SELECTED, &RegisterWindow::OnValueContextMenuSelected, this, kContextMenuZero, kContextMenuGotoDump); -} - - -void RegisterWindow::OnMainMove(const wxPoint& main_position, const wxSize& main_size) -{ - wxSize size(400, 255 + main_size.y + 250); - this->SetSize(size); - - wxPoint position = main_position; - position.x += main_size.x; - position.y -= 255; - this->SetPosition(position); -} - -void RegisterWindow::UpdateIntegerRegister(wxTextCtrl* label, wxTextCtrl* value, uint32 registerValue, bool hasChanged) -{ - //const auto value = dynamic_cast(GetWindowChild(kRegisterValueR0 + i)); - //wxASSERT(value); - - //const bool has_changed = register_value != m_prev_snapshot.gpr[i]; - if (hasChanged) - value->SetForegroundColour(m_changed_color); - else if (value->GetForegroundColour() != wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT)) - value->SetForegroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT)); - - value->ChangeValue(wxString::Format("%08x", registerValue)); - - //const auto label = dynamic_cast(GetWindowChild(kRegisterLabelR0 + i)); - //wxASSERT(label); - label->SetForegroundColour(hasChanged ? m_changed_color : wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT)); - - // check if address is a string - if (registerValue >= MEMORY_DATA_AREA_ADDR && registerValue < (MEMORY_DATA_AREA_ADDR + MEMORY_DATA_AREA_SIZE)) - { - bool is_valid_string = true; - std::stringstream buffer; - - uint32 string_offset = registerValue; - while (string_offset < (MEMORY_DATA_AREA_ADDR + MEMORY_DATA_AREA_SIZE)) - { - const uint8 c = memory_readU8(string_offset++); - if (isprint(c)) - buffer << c; - else if (c == '\0') - { - buffer << c; - break; - } - else - { - is_valid_string = false; - break; - } - } - - if (is_valid_string && buffer.tellp() > 1) - { - label->ChangeValue(wxString::Format("\"%s\"", buffer.str().c_str())); - return; - } - - // check for widestring - is_valid_string = true; - string_offset = registerValue; - std::wstringstream wbuffer; - while (string_offset < (MEMORY_DATA_AREA_ADDR + MEMORY_DATA_AREA_SIZE - 1)) - { - const uint16 c = memory_readU16(string_offset); - string_offset += 2; - - if (iswprint(c)) - wbuffer << c; - else if (c == L'\0') - { - wbuffer << c; - break; - } - else - { - is_valid_string = false; - break; - } - } - - if (is_valid_string && buffer.tellp() > 1) - { - label->ChangeValue(wxString::Format(L"ws\"%s\"", wbuffer.str().c_str())); - return; - } - } - - // check if address is a code offset - RPLModule* code_module = RPLLoader_FindModuleByCodeAddr(registerValue); - if (code_module) - { - label->ChangeValue(wxString::Format("<%s> + %x", code_module->moduleName.c_str(), registerValue - code_module->regionMappingBase_text.GetMPTR())); - return; - } - - label->ChangeValue(wxEmptyString); -} - -void RegisterWindow::OnUpdateView() -{ - PPCInterpreter_t* hCPU = debugger_lockDebugSession(); - if (!hCPU) - return; - PPCSnapshot snapshot = debugger_getSnapshotFromSession(hCPU); - debugger_unlockDebugSession(hCPU); - for (int i = 0; i < 32; ++i) - { - const uint32 registerValue = snapshot.gpr[i]; - const bool hasChanged = registerValue != m_prev_snapshot.gpr[i]; - const auto value = dynamic_cast(FindWindow(kRegisterValueR0 + i)); - wxASSERT(value); - const auto label = dynamic_cast(FindWindow(kRegisterLabelR0 + i)); - wxASSERT(label); - - UpdateIntegerRegister(label, value, registerValue, hasChanged); - } - - // update LR - { - const uint32 registerValue = snapshot.spr_lr; - const bool hasChanged = registerValue != m_prev_snapshot.spr_lr; - const auto value = dynamic_cast(FindWindow(kRegisterValueLR)); - wxASSERT(value); - const auto label = dynamic_cast(FindWindow(kRegisterLabelLR)); - wxASSERT(label); - UpdateIntegerRegister(label, value, registerValue, hasChanged); - } - - for (int i = 0; i < 32; ++i) - { - const uint64_t register_value = snapshot.fpr[i].fp0int; - - const auto value = dynamic_cast(FindWindow(kRegisterValueFPR0_0 + i)); - wxASSERT(value); - - const bool has_changed = register_value != m_prev_snapshot.fpr[i].fp0int; - if (has_changed) - value->SetForegroundColour(m_changed_color); - else if (value->GetForegroundColour() != wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT)) - value->SetForegroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT)); - else - continue; - - if(m_show_double_values) - value->ChangeValue(wxString::Format("%lf", snapshot.fpr[i].fp0)); - else - value->ChangeValue(wxString::Format("%016llx", register_value)); - } - - for (int i = 0; i < 32; ++i) - { - const uint64_t register_value = snapshot.fpr[i].fp1int; - - const auto value = dynamic_cast(FindWindow(kRegisterValueFPR1_0 + i)); - wxASSERT(value); - - const bool has_changed = register_value != m_prev_snapshot.fpr[i].fp1int; - if (has_changed) - value->SetForegroundColour(m_changed_color); - else if (value->GetForegroundColour() != wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT)) - value->SetForegroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT)); - else - continue; - - if (m_show_double_values) - value->ChangeValue(wxString::Format("%lf", snapshot.fpr[i].fp1)); - else - value->ChangeValue(wxString::Format("%016llx", register_value)); - } - - // update CRs - for (int i = 0; i < 8; ++i) - { - const auto value = dynamic_cast(FindWindow(kRegisterValueCR0 + i)); - wxASSERT(value); - - auto cr_bits_ptr = snapshot.cr + i * 4; - auto cr_bits_ptr_cmp = m_prev_snapshot.cr + i * 4; - - const bool has_changed = !std::equal(cr_bits_ptr, cr_bits_ptr + 4, cr_bits_ptr_cmp); - if (has_changed) - value->SetForegroundColour(m_changed_color); - else if (value->GetForegroundColour() != wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT)) - value->SetForegroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT)); - else - continue; - - std::vector joinArray = {}; - if (cr_bits_ptr[Espresso::CR_BIT_INDEX_LT] != 0) - joinArray.emplace_back("LT"); - if (cr_bits_ptr[Espresso::CR_BIT_INDEX_GT] != 0) - joinArray.emplace_back("GT"); - if (cr_bits_ptr[Espresso::CR_BIT_INDEX_EQ] != 0) - joinArray.emplace_back("EQ"); - if (cr_bits_ptr[Espresso::CR_BIT_INDEX_SO] != 0) - joinArray.emplace_back("SO"); - - if (joinArray.empty()) - value->ChangeValue("-"); - else - value->ChangeValue(fmt::format("{}", fmt::join(joinArray, ", "))); - } - - m_prev_snapshot = snapshot; -} - -void RegisterWindow::OnMouseDClickEvent(wxMouseEvent& event) -{ - PPCInterpreter_t* debugSession = debugger_lockDebugSession(); - if (!debugSession) - { - event.Skip(); - return; - } - PPCSnapshot ppcSnapshot = debugger_getSnapshotFromSession(debugSession); - debugger_unlockDebugSession(debugSession); - debugSession = nullptr; - - const auto id = event.GetId(); - if(kRegisterValueR0 <= id && id < kRegisterValueR0 + 32) - { - const uint32 register_index = id - kRegisterValueR0; - const uint32 register_value = ppcSnapshot.gpr[register_index]; - wxTextEntryDialog set_value_dialog(this, _("Enter a new value."), wxString::Format(_("Set R%d value"), register_index), wxString::Format("0x%08x", register_value)); - if (set_value_dialog.ShowModal() == wxID_OK) - { - const uint32 value = ConvertString(set_value_dialog.GetValue().ToStdString()); - - if (debugSession = debugger_lockDebugSession(); debugSession) - { - debugSession->gpr[register_index] = value; - debugger_unlockDebugSession(debugSession); - } - - OnUpdateView(); - } - return; - } - - if (kRegisterValueFPR0_0 <= id && id < kRegisterValueFPR0_0 + 32) - { - const uint32 register_index = id - kRegisterValueFPR0_0; - const double register_value = ppcSnapshot.fpr[register_index].fp0; - wxTextEntryDialog set_value_dialog(this, _("Enter a new value."), wxString::Format(_("Set FP0_%d value"), register_index), wxString::Format("%lf", register_value)); - if (set_value_dialog.ShowModal() == wxID_OK) - { - const double value = ConvertString(set_value_dialog.GetValue().ToStdString()); - - if (debugSession = debugger_lockDebugSession(); debugSession) - { - debugSession->fpr[register_index].fp0 = value; - debugger_unlockDebugSession(debugSession); - } - - OnUpdateView(); - } - - return; - } - - if (kRegisterValueFPR1_0 <= id && id < kRegisterValueFPR1_0 + 32) - { - const uint32 register_index = id - kRegisterValueFPR1_0; - const double register_value = ppcSnapshot.fpr[register_index].fp1; - wxTextEntryDialog set_value_dialog(this, _("Enter a new value."), wxString::Format(_("Set FP1_%d value"), register_index), wxString::Format("%lf", register_value)); - if (set_value_dialog.ShowModal() == wxID_OK) - { - const double value = ConvertString(set_value_dialog.GetValue().ToStdString()); - - if (debugSession = debugger_lockDebugSession(); debugSession) - { - debugSession->fpr[register_index].fp1 = value; - debugger_unlockDebugSession(debugSession); - } - - OnUpdateView(); - } - return; - } - - event.Skip(); -} - -void RegisterWindow::OnFPViewModePress(wxCommandEvent& event) -{ - m_show_double_values = !m_show_double_values; - - PPCInterpreter_t* debugSession = debugger_lockDebugSession(); - if (!debugSession) - return; - PPCSnapshot ppcSnapshot = debugger_getSnapshotFromSession(debugSession); - debugger_unlockDebugSession(debugSession); - - for (int i = 0; i < 32; ++i) - { - const auto value0 = dynamic_cast(FindWindow(kRegisterValueFPR0_0 + i)); - const auto value1 = dynamic_cast(FindWindow(kRegisterValueFPR1_0 + i)); - wxASSERT(value0); - wxASSERT(value1); - - if (m_show_double_values) - { - value0->ChangeValue(wxString::Format("%lf", ppcSnapshot.fpr[i].fp0)); - value1->ChangeValue(wxString::Format("%lf", ppcSnapshot.fpr[i].fp1)); - } - else - { - value0->ChangeValue(wxString::Format("%016llx", ppcSnapshot.fpr[i].fp0int)); - value1->ChangeValue(wxString::Format("%016llx", ppcSnapshot.fpr[i].fp1int)); - } - } -} - -void RegisterWindow::OnValueContextMenu(wxContextMenuEvent& event) -{ - wxMenu menu; - - menu.Append(kContextMenuZero, _("&Zero")); - menu.Append(kContextMenuInc, _("&Increment")); - menu.Append(kContextMenuDec, _("&Decrement")); - menu.AppendSeparator(); - menu.Append(kContextMenuCopy, _("&Copy")); - menu.AppendSeparator(); - menu.Append(kContextMenuGotoDisasm, _("&Goto Disasm")); - menu.Append(kContextMenuGotoDump, _("G&oto Dump")); - - m_context_ctrl = dynamic_cast(event.GetEventObject()); - - PopupMenu(&menu); -} - -void RegisterWindow::OnValueContextMenuSelected(wxCommandEvent& event) -{ - wxASSERT(m_context_ctrl); - - switch (event.GetId()) - { - case kContextMenuZero: - break; - case kContextMenuInc: - break; - case kContextMenuDec: - break; - case kContextMenuCopy: - break; - case kContextMenuGotoDisasm: - break; - case kContextMenuGotoDump: - break; - } -} diff --git a/src/gui/wxgui/debugger/RegisterWindow.h b/src/gui/wxgui/debugger/RegisterWindow.h deleted file mode 100644 index 9cb50840..00000000 --- a/src/gui/wxgui/debugger/RegisterWindow.h +++ /dev/null @@ -1,29 +0,0 @@ -#pragma once -#include "Cafe/HW/Espresso/Debugger/Debugger.h" -#include - -class DebuggerWindow2; -class wxTextCtrl; - -class RegisterWindow : public wxFrame -{ -public: - RegisterWindow(DebuggerWindow2& parent, const wxPoint& main_position, const wxSize& main_size); - - void OnMainMove(const wxPoint& position, const wxSize& main_size); - void OnUpdateView(); - -private: - void OnMouseDClickEvent(wxMouseEvent& event); - void OnFPViewModePress(wxCommandEvent& event); - void OnValueContextMenu(wxContextMenuEvent& event); - void OnValueContextMenuSelected(wxCommandEvent& event); - - void UpdateIntegerRegister(wxTextCtrl* label, wxTextCtrl* value, uint32 registerValue, bool hasChanged); - - PPCSnapshot m_prev_snapshot; - bool m_show_double_values; - wxColour m_changed_color = {0xFF0000FF}; - - wxTextCtrl* m_context_ctrl; -}; diff --git a/src/gui/wxgui/debugger/SymbolCtrl.cpp b/src/gui/wxgui/debugger/SymbolCtrl.cpp deleted file mode 100644 index 8675259c..00000000 --- a/src/gui/wxgui/debugger/SymbolCtrl.cpp +++ /dev/null @@ -1,138 +0,0 @@ -#include "wxgui/debugger/SymbolCtrl.h" -#include "Cafe/OS/RPL/rpl_symbol_storage.h" -#include "Cafe/HW/Espresso/Debugger/Debugger.h" -#include -#include - -enum ItemColumns -{ - ColumnName = 0, - ColumnAddress, - ColumnModule, -}; - -SymbolListCtrl::SymbolListCtrl(wxWindow* parent, const wxWindowID& id, const wxPoint& pos, const wxSize& size) : wxListView(parent, id, pos, size, wxLC_REPORT | wxLC_VIRTUAL) -{ - wxListItem col0; - col0.SetId(ColumnName); - col0.SetText(_("Name")); - col0.SetWidth(200); - InsertColumn(ColumnName, col0); - - wxListItem col1; - col1.SetId(ColumnAddress); - col1.SetText(_("Address")); - col1.SetWidth(75); - col1.SetAlign(wxLIST_FORMAT_RIGHT); - InsertColumn(ColumnAddress, col1); - - wxListItem col2; - col2.SetId(ColumnModule); - col2.SetText(_("Module")); - col2.SetWidth(75); - col2.SetAlign(wxLIST_FORMAT_RIGHT); - InsertColumn(ColumnModule, col2); - - Bind(wxEVT_LIST_ITEM_ACTIVATED, &SymbolListCtrl::OnLeftDClick, this); - Bind(wxEVT_LIST_ITEM_RIGHT_CLICK, &SymbolListCtrl::OnRightClick, this); - - m_list_filter.Clear(); - - OnGameLoaded(); -} - -void SymbolListCtrl::OnGameLoaded() -{ - m_data.clear(); - m_visible_items.clear(); - const auto symbol_map = rplSymbolStorage_lockSymbolMap(); - for (auto const& [address, symbol_info] : symbol_map) - { - if (symbol_info == nullptr || symbol_info->symbolName == nullptr) - continue; - - wxString libNameWX = wxString::FromAscii((const char*)symbol_info->libName); - wxString symbolNameWX = wxString::FromAscii((const char*)symbol_info->symbolName); - wxString searchNameWX = libNameWX + symbolNameWX; - searchNameWX.MakeLower(); - - m_data.try_emplace( - address, - symbolNameWX, - libNameWX, - searchNameWX - ); - } - rplSymbolStorage_unlockSymbolMap(); - - RebuildVisibleItems(); -} - -wxString SymbolListCtrl::OnGetItemText(long item, long column) const -{ - if (item < 0 || item >= static_cast(m_visible_items.size())) - return wxEmptyString; - - const auto& [address, symbol] = *m_visible_items[item]; - if (column == ColumnName) - return symbol.name; - if (column == ColumnAddress) - return wxString::Format("%08x", address); - if (column == ColumnModule) - return symbol.libName; - - return wxEmptyString; -} - - -void SymbolListCtrl::OnLeftDClick(wxListEvent& event) -{ - long selected = GetFirstSelected(); - if (selected == wxNOT_FOUND || selected >= static_cast(m_visible_items.size())) - return; - const auto address = m_visible_items[selected]->first; - if (address == 0) - return; - debugger_jumpToAddressInDisasm(address); -} - -void SymbolListCtrl::OnRightClick(wxListEvent& event) -{ - long selected = GetFirstSelected(); - if (selected == wxNOT_FOUND || selected >= static_cast(m_visible_items.size())) - return; - if (wxClipboard::Get()->Open()) - { - wxClipboard::Get()->SetData(new wxTextDataObject(wxString::Format("0x%08x", m_visible_items[selected]->first))); - wxClipboard::Get()->Close(); - } -} - -void SymbolListCtrl::RebuildVisibleItems() -{ - m_visible_items.clear(); - m_visible_items.reserve(m_data.size()); - const bool has_filter = !m_list_filter.IsEmpty(); - for (auto it = m_data.cbegin(); it != m_data.cend(); ++it) - { - if (has_filter && !it->second.searchName.Contains(m_list_filter)) - continue; - - m_visible_items.emplace_back(it); - } - - SetItemCount(static_cast(m_visible_items.size())); - if (!m_visible_items.empty()) - { - const auto top_item = std::min(GetTopItem(), static_cast(m_visible_items.size() - 1)); - RefreshItems(top_item, std::min(static_cast(m_visible_items.size() - 1), top_item + GetCountPerPage() + 1)); - } - else - Refresh(); -} - -void SymbolListCtrl::ChangeListFilter(wxString filter) -{ - m_list_filter = filter.MakeLower(); - RebuildVisibleItems(); -} diff --git a/src/gui/wxgui/debugger/SymbolCtrl.h b/src/gui/wxgui/debugger/SymbolCtrl.h deleted file mode 100644 index 938361c0..00000000 --- a/src/gui/wxgui/debugger/SymbolCtrl.h +++ /dev/null @@ -1,32 +0,0 @@ -#pragma once - -#include - -class SymbolListCtrl : public wxListView -{ -public: - SymbolListCtrl(wxWindow* parent, const wxWindowID& id, const wxPoint& pos, const wxSize& size); - void OnGameLoaded(); - - void ChangeListFilter(wxString filter); - -private: - struct SymbolItem { - SymbolItem() = default; - SymbolItem(const wxString& name, const wxString& libName, const wxString& searchName) : name(name), libName(libName), searchName(searchName) {} - - wxString name; - wxString libName; - wxString searchName; - }; - using SymbolMap = std::map; - - SymbolMap m_data; - wxString m_list_filter; - std::vector m_visible_items; - - wxString OnGetItemText(long item, long column) const override; - void RebuildVisibleItems(); - void OnLeftDClick(wxListEvent& event); - void OnRightClick(wxListEvent& event); -}; diff --git a/src/gui/wxgui/debugger/SymbolWindow.cpp b/src/gui/wxgui/debugger/SymbolWindow.cpp deleted file mode 100644 index 663e599d..00000000 --- a/src/gui/wxgui/debugger/SymbolWindow.cpp +++ /dev/null @@ -1,52 +0,0 @@ -#include "wxgui/wxgui.h" -#include "wxgui/debugger/SymbolWindow.h" -#include "wxgui/debugger/DebuggerWindow2.h" -#include "Cafe/HW/Espresso/Debugger/Debugger.h" -#include "Cafe/OS/RPL/rpl_symbol_storage.h" - -enum ItemColumns -{ - ColumnName = 0, - ColumnAddress, - ColumnModule, -}; - -SymbolWindow::SymbolWindow(DebuggerWindow2& parent, const wxPoint& main_position, const wxSize& main_size) - : wxFrame(&parent, wxID_ANY, _("Symbols"), wxDefaultPosition, wxSize(600, 250), wxSYSTEM_MENU | wxCAPTION | wxCLIP_CHILDREN | wxRESIZE_BORDER | wxFRAME_FLOAT_ON_PARENT) -{ - wxBoxSizer* main_sizer = new wxBoxSizer(wxVERTICAL); - m_symbol_ctrl = new SymbolListCtrl(this, wxID_ANY, wxDefaultPosition, wxDefaultSize); - main_sizer->Add(m_symbol_ctrl, 1, wxEXPAND); - - m_filter = new wxTextCtrl(this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxTE_NO_VSCROLL); - m_filter->Bind(wxEVT_TEXT, &SymbolWindow::OnFilterChanged, this); - main_sizer->Add(m_filter, 0, wxALL | wxEXPAND, 5); - - this->SetSizer(main_sizer); - this->wxWindowBase::Layout(); - - this->Centre(wxHORIZONTAL); - - if (parent.GetConfig().data().pin_to_main) - OnMainMove(main_position, main_size); -} - -void SymbolWindow::OnMainMove(const wxPoint& main_position, const wxSize& main_size) -{ - wxSize size(420, 250); - this->SetSize(size); - - wxPoint position = main_position; - position.x -= 420; - this->SetPosition(position); -} - -void SymbolWindow::OnGameLoaded() -{ - m_symbol_ctrl->OnGameLoaded(); -} - -void SymbolWindow::OnFilterChanged(wxCommandEvent& event) -{ - m_symbol_ctrl->ChangeListFilter(m_filter->GetValue()); -} diff --git a/src/gui/wxgui/debugger/SymbolWindow.h b/src/gui/wxgui/debugger/SymbolWindow.h deleted file mode 100644 index 505328fa..00000000 --- a/src/gui/wxgui/debugger/SymbolWindow.h +++ /dev/null @@ -1,23 +0,0 @@ -#pragma once - -#include "wxgui/debugger/SymbolCtrl.h" -#include - -class DebuggerWindow2; - -class SymbolWindow : public wxFrame -{ -public: - SymbolWindow(DebuggerWindow2& parent, const wxPoint& main_position, const wxSize& main_size); - - void OnMainMove(const wxPoint& position, const wxSize& main_size); - void OnGameLoaded(); - - void OnLeftDClick(wxListEvent& event); - -private: - wxTextCtrl* m_filter; - SymbolListCtrl* m_symbol_ctrl; - - void OnFilterChanged(wxCommandEvent& event); -}; diff --git a/src/gui/wxgui/dialogs/CreateAccount/wxCreateAccountDialog.cpp b/src/gui/wxgui/dialogs/CreateAccount/wxCreateAccountDialog.cpp deleted file mode 100644 index dbe17be4..00000000 --- a/src/gui/wxgui/dialogs/CreateAccount/wxCreateAccountDialog.cpp +++ /dev/null @@ -1,101 +0,0 @@ -#include "wxgui/dialogs/CreateAccount/wxCreateAccountDialog.h" -#include "Cafe/Account/Account.h" - -#include -#include -#include -#include -#include -#include -#include -#include "util/helpers/helpers.h" - -wxCreateAccountDialog::wxCreateAccountDialog(wxWindow* parent) - : wxDialog(parent, wxID_ANY, _("Create new account")) -{ - auto* main_sizer = new wxFlexGridSizer(0, 2, 0, 0); - main_sizer->AddGrowableCol(1); - main_sizer->SetFlexibleDirection(wxBOTH); - main_sizer->SetNonFlexibleGrowMode(wxFLEX_GROWMODE_SPECIFIED); - - main_sizer->Add(new wxStaticText(this, wxID_ANY, "PersistentId"), 0, wxALIGN_CENTER_VERTICAL | wxALL, 5); - - m_persistent_id = new wxTextCtrl(this, wxID_ANY, fmt::format("{:x}", Account::GetNextPersistentId())); - m_persistent_id->SetToolTip(_("The persistent id is the internal folder name used for your saves. Only change this if you are importing saves from a Wii U with a specific id")); - main_sizer->Add(m_persistent_id, 1, wxALL | wxEXPAND, 5); - - main_sizer->Add(new wxStaticText(this, wxID_ANY, _("Mii name")), 0, wxALIGN_CENTER_VERTICAL | wxALL, 5); - - m_mii_name = new wxTextCtrl(this, wxID_ANY); - m_mii_name->SetFocus(); - m_mii_name->SetMaxLength(10); - main_sizer->Add(m_mii_name, 1, wxALL | wxEXPAND, 5); - - main_sizer->Add(0, 0, 1, wxEXPAND, 5); - { - auto* button_sizer = new wxBoxSizer(wxHORIZONTAL); - - m_ok_button = new wxButton(this, wxID_ANY, _("OK")); - m_ok_button->Bind(wxEVT_BUTTON, &wxCreateAccountDialog::OnOK, this); - button_sizer->Add(m_ok_button, 0, wxALL, 5); - - m_cancel_buton = new wxButton(this, wxID_ANY, _("Cancel")); - m_cancel_buton->Bind(wxEVT_BUTTON, &wxCreateAccountDialog::OnCancel, this); - button_sizer->Add(m_cancel_buton, 0, wxALL, 5); - - main_sizer->Add(button_sizer, 1, wxALIGN_RIGHT, 5); - } - - this->SetSizerAndFit(main_sizer); - this->wxWindowBase::Layout(); -} - -uint32 wxCreateAccountDialog::GetPersistentId() const -{ - const std::string id_string = m_persistent_id->GetValue().ToStdString(); - return ConvertString(id_string, 16); -} - -wxString wxCreateAccountDialog::GetMiiName() const -{ - return m_mii_name->GetValue(); -} - -void wxCreateAccountDialog::OnOK(wxCommandEvent& event) -{ - if(m_persistent_id->IsEmpty()) - { - wxMessageBox(_("No persistent id entered!"), _("Error"), wxOK | wxCENTRE | wxICON_ERROR, this); - return; - } - - const auto id = GetPersistentId(); - if(id < Account::kMinPersistendId) - { - wxMessageBox(formatWxString(_("The persistent id must be greater than {:x}!"), Account::kMinPersistendId), - _("Error"), wxOK | wxCENTRE | wxICON_ERROR, this); - return; - } - - const auto& account = Account::GetAccount(id); - if(account.GetPersistentId() == id) - { - const std::wstring msg = fmt::format(fmt::runtime(_("The persistent id {:x} is already in use by account {}!").ToStdWstring()), - account.GetPersistentId(), std::wstring{ account.GetMiiName() }); - wxMessageBox(msg, _("Error"), wxOK | wxCENTRE | wxICON_ERROR, this); - return; - } - - if(m_mii_name->IsEmpty()) - { - wxMessageBox(_("Account name may not be empty!"), _("Error"), wxOK | wxCENTRE | wxICON_ERROR, this); - return; - } - - EndModal(wxID_OK); -} - -void wxCreateAccountDialog::OnCancel(wxCommandEvent& event) -{ - EndModal(wxID_CANCEL); -} diff --git a/src/gui/wxgui/dialogs/CreateAccount/wxCreateAccountDialog.h b/src/gui/wxgui/dialogs/CreateAccount/wxCreateAccountDialog.h deleted file mode 100644 index a61cf548..00000000 --- a/src/gui/wxgui/dialogs/CreateAccount/wxCreateAccountDialog.h +++ /dev/null @@ -1,18 +0,0 @@ -#pragma once -#include - -class wxCreateAccountDialog : public wxDialog -{ -public: - wxCreateAccountDialog(wxWindow* parent); - - [[nodiscard]] uint32 GetPersistentId() const; - [[nodiscard]] wxString GetMiiName() const; -private: - class wxTextCtrl* m_persistent_id; - class wxTextCtrl* m_mii_name; - class wxButton* m_ok_button, * m_cancel_buton; - - void OnOK(wxCommandEvent& event); - void OnCancel(wxCommandEvent& event); -}; \ No newline at end of file diff --git a/src/gui/wxgui/dialogs/SaveImport/SaveImportWindow.cpp b/src/gui/wxgui/dialogs/SaveImport/SaveImportWindow.cpp deleted file mode 100644 index be463377..00000000 --- a/src/gui/wxgui/dialogs/SaveImport/SaveImportWindow.cpp +++ /dev/null @@ -1,330 +0,0 @@ -#include "SaveImportWindow.h" - -#include "pugixml.hpp" -#include -#include -#include -#include -#include -#include -#include -#include -#include "zip.h" - -#include "Cafe/Account/Account.h" -#include "config/ActiveSettings.h" -#include "Cafe/OS/libs/coreinit/coreinit_Time.h" -#include "util/helpers/helpers.h" -#include "Cafe/HW/Espresso/PPCState.h" -#include "wxgui/helpers/wxHelpers.h" - -SaveImportWindow::SaveImportWindow(wxWindow* parent, uint64 title_id) - : wxDialog(parent, wxID_ANY, _("Import save entry"), wxDefaultPosition, wxDefaultSize, wxCAPTION | wxFRAME_TOOL_WINDOW | wxSYSTEM_MENU | wxTAB_TRAVERSAL | wxCLOSE_BOX), - m_title_id(title_id) -{ - auto* sizer = new wxBoxSizer(wxVERTICAL); - - { - auto* row1 = new wxFlexGridSizer(0, 2, 0, 0); - row1->AddGrowableCol(1); - - row1->Add(new wxStaticText(this, wxID_ANY, _("Source")), 0, wxALIGN_CENTER_VERTICAL | wxALL, 5); - m_source_selection = new wxFilePickerCtrl(this, wxID_ANY, wxEmptyString, - _("Select a zipped save file"), - formatWxString("{}|*.zip", _("Save entry (*.zip)"))); - m_source_selection->SetMinSize({ 270, -1 }); - row1->Add(m_source_selection, 1, wxALL | wxEXPAND, 5); - - row1->Add(new wxStaticText(this, wxID_ANY, _("Target")), 0, wxALIGN_CENTER_VERTICAL | wxALL, 5); - - const auto& accounts = Account::GetAccounts(); - m_target_selection = new wxComboBox(this, wxID_ANY); - for (const auto& account : accounts) - { - m_target_selection->Append(fmt::format("{:x} ({})", account.GetPersistentId(), - boost::nowide::narrow(account.GetMiiName().data(), account.GetMiiName().size())), - (void*)(uintptr_t)account.GetPersistentId()); - } - row1->Add(m_target_selection, 1, wxALL | wxEXPAND, 5); - - sizer->Add(row1, 0, wxEXPAND, 5); - } - - { - auto* row2 = new wxFlexGridSizer(0, 2, 0, 0); - row2->AddGrowableCol(1); - row2->SetFlexibleDirection(wxBOTH); - row2->SetNonFlexibleGrowMode(wxFLEX_GROWMODE_SPECIFIED); - - auto* ok_button = new wxButton(this, wxID_ANY, _("OK")); - ok_button->Bind(wxEVT_BUTTON, &SaveImportWindow::OnImport, this); - row2->Add(ok_button, 0, wxALL, 5); - - auto* cancel_button = new wxButton(this, wxID_ANY, _("Cancel")); - cancel_button->Bind(wxEVT_BUTTON, [this](auto&) - { - m_return_code = wxCANCEL; - Close(); - }); - row2->Add(cancel_button, 0, wxALIGN_RIGHT | wxALL, 5); - - sizer->Add(row2, 1, wxEXPAND, 5); - } - - this->SetSizerAndFit(sizer); - this->Centre(wxBOTH); -} - -void SaveImportWindow::EndModal(int retCode) -{ - wxDialog::EndModal(retCode); - SetReturnCode(m_return_code); -} - -void SaveImportWindow::OnImport(wxCommandEvent& event) -{ - const auto source_path = m_source_selection->GetPath(); - if (source_path.empty()) - return; - - // try to find cemu_meta file to verify targetid - const std::string zipfile = source_path.ToUTF8().data(); - - int ziperr; - auto* zip = zip_open(zipfile.c_str(), ZIP_RDONLY, &ziperr); - if (zip) - { - const sint32 numEntries = zip_get_num_entries(zip, 0); - for (sint32 i = 0; i < numEntries; i++) - { - zip_stat_t sb{}; - if (zip_stat_index(zip, i, 0, &sb) != 0) - continue; - - if (!boost::equals(sb.name, "cemu_meta")) - continue; - - auto* zip_file = zip_fopen_index(zip, i, 0); - if (zip_file == nullptr) - continue; - - auto buffer = std::make_unique(sb.size); - if (zip_fread(zip_file, buffer.get(), sb.size) == sb.size) - { - std::string_view str{ buffer.get(), sb.size }; - // titleId = {:#016x} - if(boost::starts_with(str, "titleId = ")) - { - const uint64_t titleId = ConvertString(str.substr(sizeof("titleId = ") + 1), 16); - if(titleId != 0 && titleId != m_title_id) - { - const auto msg = formatWxString(_("You are trying to import a savegame for a different title than your currently selected one: {:016x} vs {:016x}\nAre you sure that you want to continue?"), titleId, m_title_id); - const auto res = wxMessageBox(msg, _("Error"), wxYES_NO | wxCENTRE | wxICON_WARNING, this); - if(res == wxNO) - { - m_return_code = wxCANCEL; - Close(); - return; - } - } - } - } - - zip_fclose(zip_file); - - break; - } - - zip_close(zip); - } - - // unzip to tmp folder -> using target path directly now - std::error_code ec; - //auto tmp_source = fs::temp_directory_path(ec); - //if(ec) - //{ - // const auto error_msg = formatWxString(_("Error when getting the temp directory path:\n{}"), GetSystemErrorMessage(ec)); - // wxMessageBox(error_msg, _("Error"), wxOK | wxCENTRE | wxICON_ERROR, this); - // return; - //} - - uint32 target_id = 0; - const auto selection = m_target_selection->GetCurrentSelection(); - if (selection != wxNOT_FOUND) - target_id = (uint32)(uintptr_t)m_target_selection->GetClientData(selection); - - if (target_id == 0) - { - target_id = ConvertString(m_target_selection->GetValue().ToStdString(), 16); - if (target_id < Account::kMinPersistendId) - { - const auto msg = formatWxString(_("The given account id is not valid!\nIt must be a hex number bigger or equal than {:08x}"), Account::kMinPersistendId); - wxMessageBox(msg, _("Error"), wxOK | wxCENTRE | wxICON_ERROR, this); - return; - } - - } - - fs::path target_path = ActiveSettings::GetMlcPath("usr/save/{:08x}/{:08x}/user/{:08x}", (uint32)(m_title_id >> 32), (uint32)(m_title_id & 0xFFFFFFFF), target_id); - if (fs::exists(target_path)) - { - if (!fs::is_directory(target_path)) - { - const auto msg = formatWxString(_("There's already a file at the target directory:\n{}"), _pathToUtf8(target_path)); - wxMessageBox(msg, _("Error"), wxOK | wxCENTRE | wxICON_ERROR, this); - m_return_code = wxCANCEL; - Close(); - return; - } - - const auto msg = _("There's already a save game available for the target account, do you want to overwrite it?\nThis will delete the existing save files for the account and replace them."); - const auto result = wxMessageBox(msg, _("Error"), wxYES_NO | wxCENTRE | wxICON_WARNING, this); - if (result == wxNO) - { - m_return_code = wxCANCEL; - Close(); - return; - } - - std::error_code ec; - while (fs::exists(target_path, ec) || ec) - { - fs::remove_all(target_path, ec); - - if (ec) - { - const auto error_msg = formatWxString(_("Error when trying to delete the former save game:\n{}"), GetSystemErrorMessage(ec)); - wxMessageBox(error_msg, _("Error"), wxOK | wxCENTRE, this); - return; - } - - std::this_thread::sleep_for(std::chrono::milliseconds(100)); - } - } - - // extract zip file - - //tmp_source.append("Cemu").append(fmt::format("{}", rand())); - - //tmp_source = getMlcPath(L"usr/save"); - //tmp_source /= fmt::format("{:08x}/{:08x}/user/{:08x}_tmp", (uint32)(m_title_id >> 32), (uint32)(m_title_id & 0xFFFFFFFF), target_id); - auto tmp_source = target_path; - - fs::create_directories(tmp_source, ec); - if (ec) - { - const auto error_msg = formatWxString(_("Error when creating the extraction path:\n{}"), GetSystemErrorMessage(ec)); - wxMessageBox(error_msg, _("Error"), wxOK | wxCENTRE | wxICON_ERROR, this); - return; - } - - zip = zip_open(zipfile.c_str(), ZIP_RDONLY, &ziperr); - if (!zip) - { - const auto error_msg = formatWxString(_("Error when opening the import zip file:\n{}"), GetSystemErrorMessage(ec)); - wxMessageBox(error_msg, _("Error"), wxOK | wxCENTRE | wxICON_ERROR, this); - return; - } - - sint32 numEntries = zip_get_num_entries(zip, 0); - for (sint32 i = 0; i < numEntries; i++) - { - zip_stat_t sb{}; - if (zip_stat_index(zip, i, 0, &sb) != 0) - { - cemuLog_log(LogType::Force, "zip stat index failed on {} entry", i); - continue; - } - - if (boost::starts_with(sb.name, "../") || boost::starts_with(sb.name, "..\\")) - continue; // bad path - - if (boost::equals(sb.name, "cemu_meta")) - continue; - - size_t sb_name_len = strlen(sb.name); - if (sb_name_len == 0) - continue; - - const auto path = fs::path(tmp_source).append(sb.name); - if (sb.name[sb_name_len - 1] == '/') - { - fs::create_directories(path, ec); - if (ec) - cemuLog_log(LogType::Force, "can't create directory {}: {}", sb.name, ec.message()); - - continue; - } - - if (sb.size == 0) - continue; - - if (sb.size > (1024 * 1024 * 128)) - continue; // skip unusually huge files - - auto* zip_file = zip_fopen_index(zip, i, 0); - if (zip_file == nullptr) - continue; - - auto buffer = std::make_unique(sb.size); - if (zip_fread(zip_file, buffer.get(), sb.size) == sb.size) - { - std::ofstream file(path, std::ios::out | std::ios::binary); - if (file.is_open()) - file.write(buffer.get(), sb.size); - } - - zip_fclose(zip_file); - } - - zip_close(zip); - // extracted all files to tmp_source - - // edit meta saveinfo.xml - fs::path saveinfo = ActiveSettings::GetMlcPath("usr/save/{:08x}/{:08x}/meta/saveinfo.xml", (uint32)(m_title_id >> 32), (uint32)(m_title_id & 0xFFFFFFFF)); - if (fs::exists(saveinfo) || fs::is_regular_file(saveinfo)) - { - pugi::xml_document doc; - if (doc.load_file(saveinfo.c_str())) - { - auto info_node = doc.child("info"); - if (info_node) - { - // delete old node if available - auto old_persistend_id_string = fmt::format("{:08x}", target_id); - const auto node_entry = info_node.find_child([&old_persistend_id_string](const pugi::xml_node& node) - { - return boost::iequals(node.attribute("persistentId").as_string(), old_persistend_id_string); - }); - // add save-entry node if not available yet - if (!node_entry) - { - // add new node - auto new_persistend_id_string = fmt::format("{:08x}", target_id); - auto new_node = info_node.append_child("account"); - new_node.append_attribute("persistentId").set_value(new_persistend_id_string.c_str()); - auto timestamp = new_node.append_child("timestamp"); - timestamp.text().set(fmt::format("{:016x}", coreinit::OSGetTime() / ESPRESSO_TIMER_CLOCK).c_str()); // TODO time not initialized yet? - - if(!doc.save_file(saveinfo.c_str())) - cemuLog_log(LogType::Force, "couldn't insert save entry in saveinfo.xml: {}", _pathToUtf8(saveinfo)); - } - } - } - } // todo create saveinfo if doesnt exist - - - /*wxMessageBox(fmt::format("{}\n{}", tmp_source.generic_u8string(), target_path.generic_u8string()), _("Error"), wxOK | wxCENTRE, this); - - fs::rename(tmp_source, target_path, ec); - if (ec) - { - const auto error_msg = formatWxString(_("Error when trying to move the extracted save game:\n{}"), GetSystemErrorMessage(ec)); - wxMessageBox(error_msg, _("Error"), wxOK | wxCENTRE, this); - return; - }*/ - - m_target_id = target_id; - m_return_code = wxOK; - Close(); -} diff --git a/src/gui/wxgui/dialogs/SaveImport/SaveImportWindow.h b/src/gui/wxgui/dialogs/SaveImport/SaveImportWindow.h deleted file mode 100644 index 209df80a..00000000 --- a/src/gui/wxgui/dialogs/SaveImport/SaveImportWindow.h +++ /dev/null @@ -1,25 +0,0 @@ -#pragma once - -#include - -class SaveImportWindow : public wxDialog -{ -public: - SaveImportWindow(wxWindow* parent, uint64 title_id); - - void EndModal(int retCode) override; - - uint32 GetTargetPersistentId() const { return m_target_id; } -private: - void OnImport(wxCommandEvent& event); - - class wxFilePickerCtrl* m_source_selection; - class wxComboBox* m_target_selection; - - uint32 m_target_id = 0; - const uint64 m_title_id; - const fs::path m_source_file; - int m_return_code = wxCANCEL; -}; - - diff --git a/src/gui/wxgui/dialogs/SaveImport/SaveTransfer.cpp b/src/gui/wxgui/dialogs/SaveImport/SaveTransfer.cpp deleted file mode 100644 index 9e6ed6a7..00000000 --- a/src/gui/wxgui/dialogs/SaveImport/SaveTransfer.cpp +++ /dev/null @@ -1,198 +0,0 @@ -#include "SaveTransfer.h" - -#include "pugixml.hpp" -#include -#include -#include -#include -#include -#include -#include - -#include "Cafe/Account/Account.h" -#include "config/ActiveSettings.h" -#include "util/helpers/helpers.h" -#include "wxgui/helpers/wxHelpers.h" - -SaveTransfer::SaveTransfer(wxWindow* parent, uint64 title_id, const wxString& source_account, uint32 source_id) -: wxDialog(parent, wxID_ANY, _("Save transfer"), wxDefaultPosition, wxDefaultSize, wxCAPTION | wxFRAME_TOOL_WINDOW | wxSYSTEM_MENU | wxTAB_TRAVERSAL | wxCLOSE_BOX), - m_title_id(title_id), m_source_id(source_id) -{ - auto* sizer = new wxBoxSizer(wxVERTICAL); - - { - auto* row1 = new wxFlexGridSizer(0, 2, 0, 0); - row1->AddGrowableCol(1); - - row1->Add(new wxStaticText(this, wxID_ANY, _("Source")), 0, wxALIGN_CENTER_VERTICAL | wxALL, 5); - auto* source_choice = new wxChoice(this, wxID_ANY); - source_choice->SetMinSize({170, -1}); - source_choice->Append(source_account); - source_choice->SetSelection(0); - row1->Add(source_choice, 1, wxALL | wxEXPAND, 5); - - row1->Add(new wxStaticText(this, wxID_ANY, _("Target")), 0, wxALIGN_CENTER_VERTICAL | wxALL, 5); - - const auto& accounts = Account::GetAccounts(); - m_target_selection = new wxComboBox(this, wxID_ANY); - for (const auto& account : accounts) - { - if (account.GetPersistentId() == m_source_id) - continue; - - m_target_selection->Append(fmt::format("{:x} ({})", account.GetPersistentId(), - boost::nowide::narrow(account.GetMiiName().data(), account.GetMiiName().size())), - (void*)(uintptr_t)account.GetPersistentId()); - } - row1->Add(m_target_selection, 1, wxALL | wxEXPAND, 5); - - sizer->Add(row1, 0, wxEXPAND, 5); - } - - { - auto* row2 = new wxFlexGridSizer(0, 2, 0, 0); - row2->AddGrowableCol(1); - row2->SetFlexibleDirection(wxBOTH); - row2->SetNonFlexibleGrowMode(wxFLEX_GROWMODE_SPECIFIED); - - auto* ok_button = new wxButton(this, wxID_ANY, _("OK")); - ok_button->Bind(wxEVT_BUTTON, &SaveTransfer::OnTransfer, this); - row2->Add(ok_button, 0, wxALL, 5); - - auto* cancel_button = new wxButton(this, wxID_ANY, _("Cancel")); - cancel_button->Bind(wxEVT_BUTTON, [this](auto&) - { - m_return_code = wxCANCEL; - Close(); - }); - row2->Add(cancel_button, 0, wxALIGN_RIGHT | wxALL, 5); - - sizer->Add(row2, 1, wxEXPAND, 5); - } - - this->SetSizerAndFit(sizer); - this->Centre(wxBOTH); -} - -void SaveTransfer::EndModal(int retCode) -{ - wxDialog::EndModal(retCode); - SetReturnCode(m_return_code); -} - -void SaveTransfer::OnTransfer(wxCommandEvent& event) -{ - uint32 target_id = 0; - const auto selection = m_target_selection->GetCurrentSelection(); - if(selection != wxNOT_FOUND) - target_id = (uint32)(uintptr_t)m_target_selection->GetClientData(selection); - - if (target_id == 0) - { - target_id = ConvertString(m_target_selection->GetValue().ToStdString(), 16); - if(target_id < Account::kMinPersistendId) - { - const auto msg = formatWxString(_("The given account id is not valid!\nIt must be a hex number bigger or equal than {:08x}"), Account::kMinPersistendId); - wxMessageBox(msg, _("Error"), wxOK | wxCENTRE | wxICON_ERROR, this); - return; - } - - } - - const fs::path source_path = ActiveSettings::GetMlcPath("usr/save/{:08x}/{:08x}/user/{:08x}", (uint32)(m_title_id >> 32), (uint32)(m_title_id & 0xFFFFFFFF), m_source_id); - if (!fs::exists(source_path) || !fs::is_directory(source_path)) - return; - - const fs::path target_path = ActiveSettings::GetMlcPath("usr/save/{:08x}/{:08x}/user/{:08x}", (uint32)(m_title_id >> 32), (uint32)(m_title_id & 0xFFFFFFFF), target_id); - if (fs::exists(target_path)) - { - if(!fs::is_directory(target_path)) - { - const auto msg = formatWxString(_("There's already a file at the target directory:\n{}"), _pathToUtf8(target_path)); - wxMessageBox(msg, _("Error"), wxOK | wxCENTRE | wxICON_ERROR, this); - m_return_code = wxCANCEL; - Close(); - return; - } - - const auto msg = _("There's already a save game available for the target account, do you want to overwrite it?\nThis will delete the existing save files for the account and replace them."); - const auto result = wxMessageBox(msg, _("Error"), wxYES_NO | wxCENTRE | wxICON_WARNING, this); - if(result == wxNO) - { - m_return_code = wxCANCEL; - Close(); - return; - } - - std::error_code ec; - while (fs::exists(target_path, ec) || ec) - { - fs::remove_all(target_path, ec); - - if (ec) - { - const auto error_msg = formatWxString(_("Error when trying to delete the former save game:\n{}"), GetSystemErrorMessage(ec)); - wxMessageBox(error_msg, _("Error"), wxOK | wxCENTRE, this); - return; - } - - // wait so filesystem doesnt - std::this_thread::sleep_for(std::chrono::milliseconds(100)); - } - } - - // edit meta saveinfo.xml - bool meta_file_edited = false; - const fs::path saveinfo = ActiveSettings::GetMlcPath("usr/save/{:08x}/{:08x}/meta/saveinfo.xml", (uint32)(m_title_id >> 32), (uint32)(m_title_id & 0xFFFFFFFF)); - if (fs::exists(saveinfo) || fs::is_regular_file(saveinfo)) - { - pugi::xml_document doc; - if (doc.load_file(saveinfo.c_str())) - { - auto info_node = doc.child("info"); - if (info_node) - { - // delete old node if available - auto old_persistend_id_string = fmt::format("{:08x}", target_id); - const auto delete_entry = info_node.find_child([&old_persistend_id_string](const pugi::xml_node& node) - { - return boost::iequals(node.attribute("persistentId").as_string(), old_persistend_id_string); - }); - if (delete_entry) - info_node.remove_child(delete_entry); - - - // move new node - auto new_persistend_id_string = fmt::format("{:08x}", m_source_id); - auto move_entry = info_node.find_child([&new_persistend_id_string](const pugi::xml_node& node) - { - return boost::iequals(node.attribute("persistentId").as_string(), new_persistend_id_string); - }); - if(move_entry) - move_entry.attribute("persistentId").set_value(old_persistend_id_string.c_str()); - else - { - // TODO: create if not found! (shouldnt happen though) - } - - meta_file_edited = doc.save_file(saveinfo.c_str()); - } - } - } - - if (!meta_file_edited) - cemuLog_log(LogType::Force, "SaveTransfer::OnTransfer: couldn't update save entry in saveinfo.xml: {}", _pathToUtf8(saveinfo)); - - std::error_code ec; - fs::rename(source_path, target_path, ec); - if (ec) - { - const auto error_msg = formatWxString(_("Error when trying to move the save game:\n{}"), GetSystemErrorMessage(ec)); - wxMessageBox(error_msg, _("Error"), wxOK | wxCENTRE, this); - return; - } - - m_target_id = target_id; - m_return_code = wxOK; - Close(); -} diff --git a/src/gui/wxgui/dialogs/SaveImport/SaveTransfer.h b/src/gui/wxgui/dialogs/SaveImport/SaveTransfer.h deleted file mode 100644 index 6f208fb8..00000000 --- a/src/gui/wxgui/dialogs/SaveImport/SaveTransfer.h +++ /dev/null @@ -1,24 +0,0 @@ -#pragma once - -#include - -class wxComboBox; - -class SaveTransfer : public wxDialog -{ -public: - SaveTransfer(wxWindow* parent, uint64 title_id, const wxString& source_account, uint32 source_id); - - void EndModal(int retCode) override; - - uint32 GetTargetPersistentId() const { return m_target_id; } -private: - void OnTransfer(wxCommandEvent& event); - - wxComboBox* m_target_selection; - - uint32 m_target_id = 0; - const uint64 m_title_id; - const uint32 m_source_id; - int m_return_code = wxCANCEL; -}; diff --git a/src/gui/wxgui/helpers/wxControlObject.h b/src/gui/wxgui/helpers/wxControlObject.h deleted file mode 100644 index 5726001f..00000000 --- a/src/gui/wxgui/helpers/wxControlObject.h +++ /dev/null @@ -1,44 +0,0 @@ -#pragma once - -#include - -class wxControlObject : public wxObject -{ -public: - wxControlObject(wxControl* control) - : m_control(control) {} - - template - T* GetControl() const - { - static_assert(std::is_base_of_v, "T must inherit from wxControl"); - return dynamic_cast(m_control); - } - - wxControl* GetControl() const { return m_control; } - -private: - wxControl* m_control; -}; - -class wxControlObjects : public wxObject -{ -public: - wxControlObjects(std::vector controls) - : m_controls(std::move(controls)) {} - - template - T* GetControl(int index) const - { - static_assert(std::is_base_of_v, "T must inherit from wxControl"); - if (index < 0 || index >= m_controls.size()) - return nullptr; - - return dynamic_cast(m_controls[index]); - } - - const std::vector& GetControls() const { return m_controls; } - -private: - std::vector m_controls; -}; diff --git a/src/gui/wxgui/helpers/wxCustomData.h b/src/gui/wxgui/helpers/wxCustomData.h deleted file mode 100644 index bd7b0184..00000000 --- a/src/gui/wxgui/helpers/wxCustomData.h +++ /dev/null @@ -1,49 +0,0 @@ -#pragma once - -#include - -template -class wxCustomData : public wxClientData -{ -public: - wxCustomData() - : m_data({}) {} - - wxCustomData(T data) - : m_data(std::move(data)) { } - - const T& GetData() const { return m_data; } - - /// - /// obtains ownership of the data - /// - /// returns the stored data - T get() { return std::move(m_data); } - - /// - /// access to the data without obtaining ownership - /// - /// returns a reference to the stored data - T& ref() { return m_data; } - - bool operator==(const T& o) const { return m_data == o.m_data; } - bool operator!=(const T& o) const { return !(*this == o); } - -private: - T m_data; -}; - -//template -//class wxCustomObject : public wxObject -//{ -//public: -// wxCustomObject(T data) -// : m_data(std::move(data)) { } -// -// const T& GetData() const { return m_data; } -// -// bool operator==(const T& o) const { return m_data == o.m_data; } -// bool operator!=(const T& o) const { return !(*this == o); } -//private: -// T m_data; -//}; diff --git a/src/gui/wxgui/helpers/wxCustomEvents.cpp b/src/gui/wxgui/helpers/wxCustomEvents.cpp deleted file mode 100644 index 589b7c80..00000000 --- a/src/gui/wxgui/helpers/wxCustomEvents.cpp +++ /dev/null @@ -1,7 +0,0 @@ -#include "wxgui/helpers/wxCustomEvents.h" - -wxDEFINE_EVENT(wxEVT_SET_STATUS_BAR_TEXT, wxSetStatusBarTextEvent); -wxDEFINE_EVENT(wxEVT_SET_GAUGE_VALUE, wxSetGaugeValue); -wxDEFINE_EVENT(wxEVT_REMOVE_ITEM, wxCommandEvent); -wxDEFINE_EVENT(wxEVT_SET_TEXT, wxCommandEvent); -wxDEFINE_EVENT(wxEVT_ENABLE, wxCommandEvent); \ No newline at end of file diff --git a/src/gui/wxgui/helpers/wxCustomEvents.h b/src/gui/wxgui/helpers/wxCustomEvents.h deleted file mode 100644 index 1eb213be..00000000 --- a/src/gui/wxgui/helpers/wxCustomEvents.h +++ /dev/null @@ -1,74 +0,0 @@ -#pragma once - -#include -#include - -class wxStatusBar; -class wxControl; - -wxDECLARE_EVENT(wxEVT_REMOVE_ITEM, wxCommandEvent); -wxDECLARE_EVENT(wxEVT_SET_TEXT, wxCommandEvent); -wxDECLARE_EVENT(wxEVT_ENABLE, wxCommandEvent); - -class wxSetStatusBarTextEvent; -wxDECLARE_EVENT(wxEVT_SET_STATUS_BAR_TEXT, wxSetStatusBarTextEvent); -class wxSetStatusBarTextEvent : public wxCommandEvent -{ -public: - wxSetStatusBarTextEvent(const wxString& text, int number = 0, wxEventType type = wxEVT_SET_STATUS_BAR_TEXT, int id = 0) - : wxCommandEvent(type, id), m_text(text), m_number(number) {} - - wxSetStatusBarTextEvent(const wxSetStatusBarTextEvent& event) - : wxCommandEvent(event), m_text(event.GetText()), m_number(event.GetNumber()) {} - - - [[nodiscard]] const wxString& GetText() const { return m_text; } - [[nodiscard]] int GetNumber() const { return m_number; } - - wxEvent* Clone() const override - { - return new wxSetStatusBarTextEvent(*this); - } - -private: - const wxString m_text; - const int m_number; -}; -class wxSetGaugeValue; -wxDECLARE_EVENT(wxEVT_SET_GAUGE_VALUE, wxSetGaugeValue); -class wxSetGaugeValue : public wxCommandEvent -{ -public: - wxSetGaugeValue(int value, wxGauge* gauge, wxControl* text_ctrl = nullptr, const wxString& text = wxEmptyString, wxEventType type = wxEVT_SET_GAUGE_VALUE, int id = 0) - : wxCommandEvent(type, id), m_gauge_value(value), m_gauge_range(0), m_text_ctrl(text_ctrl), m_text(text) - { - SetEventObject(gauge); - } - - wxSetGaugeValue(int value, int range, wxGauge* gauge, wxControl* text_ctrl = nullptr, const wxString& text = wxEmptyString, wxEventType type = wxEVT_SET_GAUGE_VALUE, int id = 0) - : wxCommandEvent(type, id), m_gauge_value(value), m_gauge_range(range), m_text_ctrl(text_ctrl), m_text(text) - { - SetEventObject(gauge); - } - - wxSetGaugeValue(const wxSetGaugeValue& event) - : wxCommandEvent(event), m_gauge_value(event.GetValue()), m_gauge_range(event.GetRange()), m_text_ctrl(event.GetTextCtrl()), m_text(event.GetText()) - {} - - [[nodiscard]] int GetValue() const { return m_gauge_value; } - [[nodiscard]] int GetRange() const { return m_gauge_range; } - [[nodiscard]] wxGauge* GetGauge() const { return (wxGauge*)GetEventObject(); } - [[nodiscard]] const wxString& GetText() const { return m_text; } - [[nodiscard]] wxControl* GetTextCtrl() const { return m_text_ctrl; } - - wxEvent* Clone() const override - { - return new wxSetGaugeValue(*this); - } - -private: - const int m_gauge_value, m_gauge_range; - wxControl* m_text_ctrl; - const wxString m_text; -}; - diff --git a/src/gui/wxgui/helpers/wxHelpers.cpp b/src/gui/wxgui/helpers/wxHelpers.cpp deleted file mode 100644 index 0d734372..00000000 --- a/src/gui/wxgui/helpers/wxHelpers.cpp +++ /dev/null @@ -1,116 +0,0 @@ -#include "wxgui/helpers/wxHelpers.h" - -#include -#include -#include -#include - -#if BOOST_OS_LINUX || BOOST_OS_BSD -#include -#include -#include -#include -#ifdef HAS_WAYLAND -#include -#endif -#endif - -#include "wxgui/helpers/wxControlObject.h" - -void wxAutosizeColumn(wxListCtrlBase* ctrl, int col) -{ - ctrl->SetColumnWidth(col, wxLIST_AUTOSIZE_USEHEADER); - int wh = ctrl->GetColumnWidth(col); - ctrl->SetColumnWidth(col, wxLIST_AUTOSIZE); - int wc = ctrl->GetColumnWidth(col); - if (wh > wc) - ctrl->SetColumnWidth(col, wxLIST_AUTOSIZE_USEHEADER); -} - -void wxAutosizeColumns(wxListCtrlBase* ctrl, int col_start, int col_end) -{ - wxWindowUpdateLocker lock(ctrl); - for (int i = col_start; i <= col_end; ++i) - wxAutosizeColumn(ctrl, i); -} - -void update_slider_text(wxCommandEvent& event, const wxFormatString& format /*= "%d%%"*/) -{ - const auto slider = dynamic_cast(event.GetEventObject()); - wxASSERT(slider); - - auto slider_text = dynamic_cast(event.GetEventUserData())->GetControl(); - wxASSERT(slider_text); - - slider_text->SetLabel(wxString::Format(format, slider->GetValue())); -} - -uint32 fix_raw_keycode(uint32 keycode, uint32 raw_flags) -{ -#if BOOST_OS_WINDOWS - const auto flags = (HIWORD(raw_flags) & 0xFFF); - if(keycode == VK_SHIFT) - { - if(flags == 0x2A) - return 160; - else if (flags == 0x36) - return 161; - } - else if (keycode == VK_CONTROL) - { - if (flags == 0x1d) - return 162; - else if (flags == 0x11d) - return 163; - } - else if (keycode == VK_MENU) - { - if ((flags & 0xFF) == 0x38) - return 164; - else if ((flags & 0xFF) == 0x38) - return 165; - } -#endif - - return keycode; -} - -WindowSystem::WindowHandleInfo initHandleContextFromWxWidgetsWindow(wxWindow* wxw) -{ - WindowSystem::WindowHandleInfo handleInfo; -#if BOOST_OS_WINDOWS - handleInfo.backend = WindowSystem::WindowHandleInfo::Backend::Windows; - handleInfo.surface = reinterpret_cast(wxw->GetHWND()); -#elif BOOST_OS_LINUX || BOOST_OS_BSD - GtkWidget* gtkWidget = (GtkWidget*)wxw->GetHandle(); // returns GtkWidget - gtk_widget_realize(gtkWidget); - GdkWindow* gdkWindow = gtk_widget_get_window(gtkWidget); - GdkDisplay* gdkDisplay = gdk_window_get_display(gdkWindow); - if (GDK_IS_X11_WINDOW(gdkWindow)) - { - handleInfo.backend = WindowSystem::WindowHandleInfo::Backend::X11; - handleInfo.surface = reinterpret_cast(gdk_x11_window_get_xid(gdkWindow)); - handleInfo.display = gdk_x11_display_get_xdisplay(gdkDisplay); - if (!handleInfo.display) - { - cemuLog_log(LogType::Force, "Unable to get xlib display"); - } - } -#ifdef HAS_WAYLAND - else if (GDK_IS_WAYLAND_WINDOW(gdkWindow)) - { - handleInfo.backend = WindowSystem::WindowHandleInfo::Backend::Wayland; - handleInfo.surface = gdk_wayland_window_get_wl_surface(gdkWindow); - handleInfo.display = gdk_wayland_display_get_wl_display(gdkDisplay); - } -#endif - else - { - cemuLog_log(LogType::Force, "Unsuported GTK backend"); - } -#elif BOOST_OS_MACOS - handleInfo.backend = WindowSystem::WindowHandleInfo::Backend::Cocoa; - handleInfo.surface = reinterpret_cast(wxw->GetHandle()); -#endif - return handleInfo; -} diff --git a/src/gui/wxgui/helpers/wxHelpers.h b/src/gui/wxgui/helpers/wxHelpers.h deleted file mode 100644 index fa42338b..00000000 --- a/src/gui/wxgui/helpers/wxHelpers.h +++ /dev/null @@ -1,108 +0,0 @@ -#pragma once - -#include "interface/WindowSystem.h" -#include -#include -#include - -template <> -struct fmt::formatter : formatter -{ - template - auto format(const wxString& str, FormatContext& ctx) const - { - return formatter::format(str.ToStdString(), ctx); - } -}; - -class wxTempEnable -{ -public: - wxTempEnable(wxControl* control, bool state = true) - : m_control(control), m_state(state) - { - m_control->Enable(m_state); - } - - wxTempEnable(const wxTempEnable&) = delete; - wxTempEnable(wxTempEnable&&) noexcept = default; - - ~wxTempEnable() - { - if(m_control) - m_control->Enable(!m_state); - } - -private: - wxControl* m_control; - const bool m_state; -}; - -class wxTempDisable : wxTempEnable -{ -public: - wxTempDisable(wxControl* control) - : wxTempEnable(control, false) {} -}; - -template -wxString formatWxString(const wxString& format, TArgs&&...args) -{ - return wxString::FromUTF8(fmt::format(fmt::runtime(format.utf8_string()), std::forward(args)...)); -} - -// executes a function when destroying the obj -template -class wxDTorFunc -{ -public: - wxDTorFunc(TFunction&& func, TArgs&&... args) - { - auto bound = std::bind(std::forward(func), std::forward(args)...); - // m_function = [bound] { bound(); }; - m_function = [bound{ std::move(bound) }]{ bound(); }; - - } - ~wxDTorFunc() - { - m_function(); - } -private: - std::function m_function; -}; - -void wxAutosizeColumn(wxListCtrlBase* ctrl, int col); -void wxAutosizeColumns(wxListCtrlBase* ctrl, int col_start, int col_end); - -template -T get_next_sibling(const T element) -{ - static_assert(std::is_pointer_v && std::is_base_of_v>, "element must be a pointer and inherit from wxControl"); - for (auto sibling = element->GetNextSibling(); sibling; sibling = sibling->GetNextSibling()) - { - if (auto result = dynamic_cast(sibling)) - return result; - } - - return nullptr; -} - -template -T get_prev_sibling(const T element) -{ - static_assert(std::is_pointer_v && std::is_base_of_v>, "element must be a pointer and inherit from wxControl"); - for (auto sibling = element->GetPrevSibling(); sibling; sibling = sibling->GetPrevSibling()) - { - auto result = dynamic_cast(sibling); - if (result) - return result; - } - - return nullptr; -} - -void update_slider_text(wxCommandEvent& event, const wxFormatString& format = "%d%%"); - -uint32 fix_raw_keycode(uint32 keycode, uint32 raw_flags); - -WindowSystem::WindowHandleInfo initHandleContextFromWxWidgetsWindow(wxWindow* wxw); diff --git a/src/gui/wxgui/helpers/wxLogEvent.h b/src/gui/wxgui/helpers/wxLogEvent.h deleted file mode 100644 index 23f6533d..00000000 --- a/src/gui/wxgui/helpers/wxLogEvent.h +++ /dev/null @@ -1,32 +0,0 @@ -#pragma once - -#include - -class wxLogEvent; -wxDECLARE_EVENT(EVT_LOG, wxLogEvent); - -class wxLogEvent : public wxCommandEvent -{ -public: - wxLogEvent(const wxString& filter, const wxString& message) - : wxCommandEvent(EVT_LOG), m_filter(filter), m_message(message) { } - - wxLogEvent(const wxLogEvent& event) - : wxCommandEvent(event), m_filter(event.GetFilter()), m_message(event.GetMessage()) { } - - wxEvent* Clone() const { return new wxLogEvent(*this); } - - [[nodiscard]] const wxString& GetFilter() const - { - return m_filter; - } - - [[nodiscard]] const wxString& GetMessage() const - { - return m_message; - } - -private: - wxString m_filter; - wxString m_message; -}; diff --git a/src/gui/wxgui/helpers/wxWayland.cpp b/src/gui/wxgui/helpers/wxWayland.cpp deleted file mode 100644 index 6e7ff789..00000000 --- a/src/gui/wxgui/helpers/wxWayland.cpp +++ /dev/null @@ -1,24 +0,0 @@ -#include "wxgui/helpers/wxWayland.h" - -#if ( BOOST_OS_LINUX || BOOST_OS_BSD ) && HAS_WAYLAND - -#include - -bool wxWlIsWaylandWindow(wxWindow* window) -{ - GtkWidget* gtkWindow = static_cast(window->GetHandle()); - GdkWindow* gdkWindow = gtk_widget_get_window(gtkWindow); - return GDK_IS_WAYLAND_WINDOW(gdkWindow); -} - -void wxWlSetAppId(wxFrame* frame, const char* applicationId) -{ - GtkWidget* gtkWindow = static_cast(frame->GetHandle()); - gtk_widget_realize(gtkWindow); - GdkWindow* gdkWindow = gtk_widget_get_window(gtkWindow); - static auto gdk_wl_set_app_id = reinterpret_cast(dlsym(nullptr, "gdk_wayland_window_set_application_id")); - if (gdk_wl_set_app_id) - gdk_wl_set_app_id(gdkWindow, applicationId); -} - -#endif // BOOST_OS_LINUX && HAS_WAYLAND diff --git a/src/gui/wxgui/helpers/wxWayland.h b/src/gui/wxgui/helpers/wxWayland.h deleted file mode 100644 index 0f162787..00000000 --- a/src/gui/wxgui/helpers/wxWayland.h +++ /dev/null @@ -1,93 +0,0 @@ -#pragma once - -#if ( BOOST_OS_LINUX || BOOST_OS_BSD ) && HAS_WAYLAND - -#include -#include -#include -#include -#include -#include "wayland-viewporter-client-protocol.h" - -class wxWlSubsurface -{ - static void registry_add_object(void* data, struct wl_registry* registry, uint32_t name, const char* interface, uint32_t version) - { - auto wlSubsurface = static_cast(data); - - if (!strcmp(interface, wl_subcompositor_interface.name)) - { - wlSubsurface->m_subcompositor = static_cast(wl_registry_bind(registry, name, &wl_subcompositor_interface, 1)); - } - else if (!strcmp(interface, wp_viewporter_interface.name)) - { - wlSubsurface->m_viewporter = static_cast(wl_registry_bind(registry, name, &wp_viewporter_interface, 1)); - } - } - static void registry_remove_object(void* /*data*/, struct wl_registry* /*registry*/, uint32_t /*name*/) {} - - wl_registry_listener m_registry_listener = {®istry_add_object, ®istry_remove_object}; - wl_subcompositor* m_subcompositor; - wl_surface* m_surface; - wl_subsurface* m_subsurface; - wp_viewporter* m_viewporter = NULL; - wp_viewport* m_viewport = NULL; - int32_t m_xPos = 0; - int32_t m_yPos = 0; - int32_t m_width = 0; - int32_t m_height = 0; - -public: - wxWlSubsurface(wxWindow* window) - { - GtkWidget* widget = static_cast(window->GetHandle()); - gtk_widget_realize(widget); - GdkWindow* gdkWindow = gtk_widget_get_window(widget); - GdkDisplay* gdkDisplay = gdk_window_get_display(gdkWindow); - wl_display* display = gdk_wayland_display_get_wl_display(gdkDisplay); - wl_surface* surface = gdk_wayland_window_get_wl_surface(gdkWindow); - wl_compositor* compositor = gdk_wayland_display_get_wl_compositor(gdkDisplay); - wl_registry* registry = wl_display_get_registry(display); - wl_registry_add_listener(registry, &m_registry_listener, this); - wl_display_roundtrip(display); - m_surface = wl_compositor_create_surface(compositor); - if (m_viewporter) - m_viewport = wp_viewporter_get_viewport(m_viewporter, m_surface); - wl_region* region = wl_compositor_create_region(compositor); - wl_surface_set_input_region(m_surface, region); - m_subsurface = wl_subcompositor_get_subsurface(m_subcompositor, m_surface, surface); - wl_subsurface_set_desync(m_subsurface); - auto sRect = window->GetScreenRect(); - setSize(sRect.x, sRect.y, sRect.width, sRect.height); - wl_region_destroy(region); - } - - wl_surface* getSurface() const { return m_surface; } - - void setSize(int32_t xPos, int32_t yPos, int32_t width, int32_t height) - { - if (xPos != m_xPos || m_yPos != yPos || m_width != width || m_height != height) - { - m_xPos = xPos; - m_yPos = yPos; - m_height = height; - m_width = width; - wl_subsurface_set_position(m_subsurface, m_xPos, m_yPos); - if (m_viewport) - wp_viewport_set_destination(m_viewport, m_width, m_height); - wl_surface_commit(m_surface); - } - } - - ~wxWlSubsurface() - { - wl_subsurface_destroy(m_subsurface); - wl_surface_destroy(m_surface); - wl_subcompositor_destroy(m_subcompositor); - } -}; - -bool wxWlIsWaylandWindow(wxWindow* window); -void wxWlSetAppId(wxFrame* frame, const char* application_id); - -#endif // BOOST_OS_LINUX && HAS_WAYLAND diff --git a/src/gui/wxgui/input/HotkeySettings.cpp b/src/gui/wxgui/input/HotkeySettings.cpp deleted file mode 100644 index d84ffa1b..00000000 --- a/src/gui/wxgui/input/HotkeySettings.cpp +++ /dev/null @@ -1,522 +0,0 @@ -#include "wxgui/input/HotkeySettings.h" -#include "Cafe/HW/Latte/Renderer/Renderer.h" -#include "interface/WindowSystem.h" -#include -#include "input/InputManager.h" -#include "HotkeySettings.h" -#include "MainWindow.h" - -#include -#include - -#if BOOST_OS_WINDOWS -#include -#endif - -#if BOOST_OS_LINUX || BOOST_OS_MACOS || BOOST_OS_BSD -#include "resource/embedded/resources.h" -#endif - -std::optional GenerateScreenshotFilename(bool isDRC) -{ - fs::path screendir = ActiveSettings::GetUserDataPath("screenshots"); - // build screenshot name with format Screenshot_YYYY-MM-DD_HH-MM-SS[_GamePad].png - // if the file already exists add a suffix counter (_2.png, _3.png etc) - std::time_t time_t = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now()); - std::tm* tm = std::localtime(&time_t); - - std::string screenshotFileName = fmt::format("Screenshot_{:04}-{:02}-{:02}_{:02}-{:02}-{:02}", tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec); - if (isDRC) - screenshotFileName.append("_GamePad"); - - fs::path screenshotPath; - for (sint32 i = 0; i < 999; i++) - { - screenshotPath = screendir; - if (i == 0) - screenshotPath.append(fmt::format("{}.png", screenshotFileName)); - else - screenshotPath.append(fmt::format("{}_{}.png", screenshotFileName, i + 1)); - - std::error_code ec; - bool exists = fs::exists(screenshotPath, ec); - - if (!ec && !exists) - return screenshotPath; - } - return std::nullopt; -} - -bool SaveScreenshotToFile(const fs::path& imagePath, const wxImage& image) -{ - std::error_code ec; - fs::create_directories(imagePath.parent_path(), ec); - if (ec) - return false; - - // suspend wxWidgets logging for the lifetime this object, to prevent a message box if wxImage::SaveFile fails - wxLogNull _logNo; - return image.SaveFile(imagePath.wstring()); -} - -bool SaveScreenshotToClipboard(const wxImage& image) -{ - static std::mutex s_clipboardMutex; - bool success = false; - - s_clipboardMutex.lock(); - if (wxTheClipboard->Open()) - { - wxTheClipboard->SetData(new wxImageDataObject(image)); - wxTheClipboard->Close(); - success = true; - } - s_clipboardMutex.unlock(); - - return success; -} - -std::optional SaveScreenshot(std::vector data, int width, int height, bool mainWindow) -{ -#if BOOST_OS_WINDOWS - // on Windows wxWidgets uses OLE API for the clipboard - // to make this work we need to call OleInitialize() on the same thread - OleInitialize(nullptr); -#endif - bool save_screenshot = GetWxGUIConfig().save_screenshot; - wxImage image(width, height, data.data(), true); - if (mainWindow) - { - if (SaveScreenshotToClipboard(image)) - { - if (!save_screenshot) - return _tr("Screenshot saved to clipboard"); - } - else - { - return _tr("Failed to open clipboard"); - } - } - if (save_screenshot) - { - auto imagePath = GenerateScreenshotFilename(mainWindow); - if (imagePath.has_value() && SaveScreenshotToFile(imagePath.value(), image)) - { - if (mainWindow) - return _tr("Screenshot saved"); - } - else - { - return _tr("Failed to save screenshot to file"); - } - } - return std::nullopt; -} - -extern WindowSystem::WindowInfo g_window_info; - -std::unordered_map> HotkeySettings::s_cfgHotkeyToFuncMap; - -struct HotkeyEntry -{ - std::unique_ptr name; - std::unique_ptr keyInput; - std::unique_ptr controllerInput; - sHotkeyCfg& hotkey; - - HotkeyEntry(wxStaticText* name, wxButton* keyInput, wxButton* controllerInput, sHotkeyCfg& hotkey) - : name(name), keyInput(keyInput), controllerInput(controllerInput), hotkey(hotkey) - { - keyInput->SetClientData(&hotkey); - controllerInput->SetClientData(&hotkey); - } -}; - -HotkeySettings::HotkeySettings(wxWindow* parent) - : wxFrame(parent, wxID_ANY, _("Hotkey Settings")) -{ - SetIcon(wxICON(X_HOTKEY_SETTINGS)); - - m_sizer = new wxFlexGridSizer(0, 3, 5, 5); - m_sizer->AddGrowableCol(1); - m_sizer->AddGrowableCol(2); - - m_panel = new wxPanel(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxBORDER_THEME); - m_panel->SetSizer(m_sizer); - - Center(); - - SetActiveController(); - - CreateColumnHeaders(); - - /* global modifier */ - CreateHotkeyRow(_tr("Hotkey modifier"), s_cfgHotkeys.modifiers); - m_hotkeys.at(0).keyInput->Hide(); - - /* hotkeys */ - CreateHotkeyRow(_tr("Toggle fullscreen"), s_cfgHotkeys.toggleFullscreen); - CreateHotkeyRow(_tr("Take screenshot"), s_cfgHotkeys.takeScreenshot); - CreateHotkeyRow(_tr("Toggle fast-forward"), s_cfgHotkeys.toggleFastForward); -#ifdef CEMU_DEBUG_ASSERT - CreateHotkeyRow(_tr("End emulation"), s_cfgHotkeys.endEmulation); -#endif - CreateHotkeyRow(_tr("Exit application"), s_cfgHotkeys.exitApplication); - - m_controllerTimer = new wxTimer(this); - Bind(wxEVT_TIMER, &HotkeySettings::OnControllerTimer, this); - - m_sizer->SetSizeHints(this); -} - -HotkeySettings::~HotkeySettings() -{ - m_controllerTimer->Stop(); - if (m_needToSave) - { - GetConfigHandle().Save(); - } -} - -void HotkeySettings::Init(MainWindow* mainWindowFrame) -{ - s_cfgHotkeyToFuncMap.insert({ - {&s_cfgHotkeys.toggleFullscreen, [](void) { - s_mainWindow->SetFullScreen(!s_mainWindow->IsFullScreen()); - }}, - {&s_cfgHotkeys.toggleFullscreenAlt, [](void) { - s_mainWindow->SetFullScreen(!s_mainWindow->IsFullScreen()); - }}, - {&s_cfgHotkeys.exitFullscreen, [](void) { - s_mainWindow->SetFullScreen(false); - }}, - {&s_cfgHotkeys.takeScreenshot, [](void) { - if (g_renderer) - g_renderer->RequestScreenshot(SaveScreenshot); - }}, - {&s_cfgHotkeys.toggleFastForward, [](void) { - ActiveSettings::SetTimerShiftFactor((ActiveSettings::GetTimerShiftFactor() < 3) ? 3 : 1); - }}, - {&s_cfgHotkeys.exitApplication, [](void) { - auto closeEvent = new wxCloseEvent{wxEVT_CLOSE_WINDOW, s_mainWindow->GetId()}; - closeEvent->SetCanVeto(false); - wxQueueEvent(s_mainWindow, closeEvent); - }}, -#ifdef CEMU_DEBUG_ASSERT - {&s_cfgHotkeys.endEmulation, [](void) { - wxTheApp->CallAfter([]() { - s_mainWindow->EndEmulation(); - }); - }}, -#endif - }); - - s_keyboardHotkeyToFuncMap.reserve(s_cfgHotkeyToFuncMap.size()); - for (const auto& [cfgHotkey, func] : s_cfgHotkeyToFuncMap) - { - auto keyboardHotkey = cfgHotkey->keyboard.raw; - if (keyboardHotkey > sHotkeyCfg::keyboardNone) - { - s_keyboardHotkeyToFuncMap[keyboardHotkey] = func; - } - auto controllerHotkey = cfgHotkey->controller; - if (controllerHotkey > sHotkeyCfg::controllerNone) - { - s_controllerHotkeyToFuncMap[controllerHotkey] = func; - } - } - s_mainWindow = mainWindowFrame; -} - -void HotkeySettings::CreateColumnHeaders(void) -{ - auto* emptySpace = new wxStaticText(m_panel, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxALIGN_CENTER_HORIZONTAL); - auto* keyboard = new wxStaticText(m_panel, wxID_ANY, _tr("Keyboard"), wxDefaultPosition, wxDefaultSize, wxALIGN_CENTER_HORIZONTAL); - auto* controller = new wxStaticText(m_panel, wxID_ANY, _tr("Controller"), wxDefaultPosition, wxDefaultSize, wxALIGN_CENTER_HORIZONTAL); - keyboard->SetFont(keyboard->GetFont().Bold().Larger()); - controller->SetFont(controller->GetFont().Bold().Larger()); - - keyboard->SetMinSize(m_minButtonSize); - controller->SetMinSize(m_minButtonSize); - - auto flags = wxSizerFlags().Expand().Border(wxTOP, 10); - m_sizer->Add(emptySpace, flags); - m_sizer->Add(keyboard, flags); - m_sizer->Add(controller, flags); -} - -void HotkeySettings::CreateHotkeyRow(const wxString& label, sHotkeyCfg& cfgHotkey) -{ - auto* name = new wxStaticText(m_panel, wxID_ANY, label); - auto* keyInput = new wxButton(m_panel, wxID_ANY, To_wxString(cfgHotkey.keyboard), wxDefaultPosition, wxDefaultSize, wxWANTS_CHARS | wxBU_EXACTFIT); - auto* controllerInput = new wxButton(m_panel, wxID_ANY, To_wxString(cfgHotkey.controller), wxDefaultPosition, wxDefaultSize, wxWANTS_CHARS | wxBU_EXACTFIT); - - /* for starting input */ - keyInput->Bind(wxEVT_BUTTON, &HotkeySettings::OnKeyboardHotkeyInputLeftClick, this); - controllerInput->Bind(wxEVT_BUTTON, &HotkeySettings::OnControllerHotkeyInputLeftClick, this); - - /* for cancelling and clearing input */ - keyInput->Bind(wxEVT_RIGHT_UP, &HotkeySettings::OnKeyboardHotkeyInputRightClick, this); - controllerInput->Bind(wxEVT_RIGHT_UP, &HotkeySettings::OnControllerHotkeyInputRightClick, this); - - keyInput->SetMinSize(m_minButtonSize); - controllerInput->SetMinSize(m_minButtonSize); - - const wxColour inputButtonColor = GetBackgroundColour(); - keyInput->SetBackgroundColour(inputButtonColor); - controllerInput->SetBackgroundColour(inputButtonColor); - - auto flags = wxSizerFlags(1).Expand().Border(wxALL, 5).CenterVertical(); - m_sizer->Add(name, flags); - m_sizer->Add(keyInput, flags); - m_sizer->Add(controllerInput, flags); - - m_hotkeys.emplace_back(name, keyInput, controllerInput, cfgHotkey); -} - -void HotkeySettings::OnControllerTimer(wxTimerEvent& event) -{ - if (m_activeController.expired()) - { - m_controllerTimer->Stop(); - return; - } - auto& controller = *m_activeController.lock(); - auto buttons = controller.update_state().buttons; - if (!buttons.IsIdle()) - { - for (const auto& newHotkey : buttons.GetButtonList()) - { - m_controllerTimer->Stop(); - auto* inputButton = static_cast(m_controllerTimer->GetClientData()); - auto& cfgHotkey = *static_cast(inputButton->GetClientData()); - const auto oldHotkey = cfgHotkey.controller; - const bool isModifier = (&cfgHotkey == &s_cfgHotkeys.modifiers); - /* ignore same hotkeys and block duplicate hotkeys */ - if ((newHotkey != oldHotkey) && (isModifier || (newHotkey != s_cfgHotkeys.modifiers.controller)) && - (s_controllerHotkeyToFuncMap.find(newHotkey) == s_controllerHotkeyToFuncMap.end())) - { - m_needToSave |= true; - cfgHotkey.controller = newHotkey; - /* don't bind modifier to map */ - if (!isModifier) - { - s_controllerHotkeyToFuncMap.erase(oldHotkey); - s_controllerHotkeyToFuncMap[newHotkey] = s_cfgHotkeyToFuncMap.at(&cfgHotkey); - } - } - FinalizeInput(inputButton); - return; - } - } -} - -void HotkeySettings::OnKeyboardHotkeyInputLeftClick(wxCommandEvent& event) -{ - auto* inputButton = static_cast(event.GetEventObject()); - if (m_activeInputButton) - { - /* ignore multiple clicks of the same button */ - if (inputButton == m_activeInputButton) return; - RestoreInputButton(); - } - inputButton->Bind(wxEVT_KEY_UP, &HotkeySettings::OnKeyUp, this); - inputButton->SetLabelText(m_editModeHotkeyText); - m_activeInputButton = inputButton; -} - -void HotkeySettings::OnControllerHotkeyInputLeftClick(wxCommandEvent& event) -{ - auto* inputButton = static_cast(event.GetEventObject()); - if (m_activeInputButton) - { - /* ignore multiple clicks of the same button */ - if (inputButton == m_activeInputButton) return; - RestoreInputButton(); - } - m_controllerTimer->Stop(); - if (!SetActiveController()) - { - return; - } - inputButton->SetLabelText(m_editModeHotkeyText); - m_controllerTimer->SetClientData(inputButton); - m_controllerTimer->Start(25); - m_activeInputButton = inputButton; -} - -void HotkeySettings::OnKeyboardHotkeyInputRightClick(wxMouseEvent& event) -{ - if (m_activeInputButton) - { - RestoreInputButton(); - return; - } - auto* inputButton = static_cast(event.GetEventObject()); - auto& cfgHotkey = *static_cast(inputButton->GetClientData()); - uKeyboardHotkey newHotkey{sHotkeyCfg::keyboardNone}; - if (cfgHotkey.keyboard.raw != newHotkey.raw) - { - m_needToSave |= true; - s_keyboardHotkeyToFuncMap.erase(cfgHotkey.keyboard.raw); - cfgHotkey.keyboard = newHotkey; - FinalizeInput(inputButton); - } -} - -void HotkeySettings::OnControllerHotkeyInputRightClick(wxMouseEvent& event) -{ - if (m_activeInputButton) - { - RestoreInputButton(); - return; - } - auto* inputButton = static_cast(event.GetEventObject()); - auto& cfgHotkey = *static_cast(inputButton->GetClientData()); - ControllerHotkey_t newHotkey{sHotkeyCfg::controllerNone}; - if (cfgHotkey.controller != newHotkey) - { - m_needToSave |= true; - s_controllerHotkeyToFuncMap.erase(cfgHotkey.controller); - cfgHotkey.controller = newHotkey; - FinalizeInput(inputButton); - } -} - -bool HotkeySettings::SetActiveController(void) -{ - auto emulatedController = InputManager::instance().get_controller(0); - if (emulatedController.use_count() <= 1) - { - return false; - } - const auto& controllers = emulatedController->get_controllers(); - if (controllers.empty()) - { - return false; - } - m_activeController = controllers.at(0); - return true; -} - -void HotkeySettings::OnKeyUp(wxKeyEvent& event) -{ - auto* inputButton = static_cast(event.GetEventObject()); - auto& cfgHotkey = *static_cast(inputButton->GetClientData()); - if (auto keycode = event.GetKeyCode(); IsValidKeycodeUp(keycode)) - { - auto oldHotkey = cfgHotkey.keyboard; - uKeyboardHotkey newHotkey{}; - newHotkey.key = keycode; - newHotkey.alt = event.AltDown(); - newHotkey.ctrl = event.ControlDown(); - newHotkey.shift = event.ShiftDown(); - if ((newHotkey.raw != oldHotkey.raw) && - (s_keyboardHotkeyToFuncMap.find(newHotkey.raw) == s_keyboardHotkeyToFuncMap.end())) - { - m_needToSave |= true; - cfgHotkey.keyboard = newHotkey; - s_keyboardHotkeyToFuncMap.erase(oldHotkey.raw); - s_keyboardHotkeyToFuncMap[newHotkey.raw] = s_cfgHotkeyToFuncMap.at(&cfgHotkey); - } - } - FinalizeInput(inputButton); -} - -template -void HotkeySettings::FinalizeInput(wxButton* inputButton) -{ - auto& cfgHotkey = *static_cast(inputButton->GetClientData()); - if constexpr (std::is_same_v) - { - inputButton->Unbind(wxEVT_KEY_UP, &HotkeySettings::OnKeyUp, this); - inputButton->SetLabelText(To_wxString(cfgHotkey.keyboard)); - } - else if constexpr (std::is_same_v) - { - inputButton->SetLabelText(To_wxString(cfgHotkey.controller)); - } - m_activeInputButton = nullptr; -} - -template -void HotkeySettings::RestoreInputButton(void) -{ - FinalizeInput(m_activeInputButton); -} - -bool HotkeySettings::IsValidKeycodeUp(int keycode) -{ - switch (keycode) - { - case WXK_NONE: - case WXK_ESCAPE: - case WXK_ALT: - case WXK_CONTROL: - case WXK_SHIFT: - return false; - default: - return true; - } -} - -wxString HotkeySettings::To_wxString(uKeyboardHotkey hotkey) -{ - if (hotkey.raw == sHotkeyCfg::keyboardNone) { - return m_disabledHotkeyText; - } - wxString ret{}; - if (hotkey.alt) - { - ret.append(_("Alt + ")); - } - if (hotkey.ctrl) - { - ret.append(_("Ctrl + ")); - } - if (hotkey.shift) - { - ret.append(_("Shift + ")); - } - ret.append(wxAcceleratorEntry(0, hotkey.key).ToString()); - return ret; -} - -wxString HotkeySettings::To_wxString(ControllerHotkey_t hotkey) -{ - if ((hotkey == sHotkeyCfg::controllerNone) || m_activeController.expired()) { - return m_disabledHotkeyText; - } - return m_activeController.lock()->get_button_name(hotkey); -} - -void HotkeySettings::CaptureInput(wxKeyEvent& event) -{ - uKeyboardHotkey hotkey{}; - hotkey.key = event.GetKeyCode(); - hotkey.alt = event.AltDown(); - hotkey.ctrl = event.ControlDown(); - hotkey.shift = event.ShiftDown(); - const auto it = s_keyboardHotkeyToFuncMap.find(hotkey.raw); - if (it != s_keyboardHotkeyToFuncMap.end()) - it->second(); -} - -void HotkeySettings::CaptureInput(const ControllerState& currentState, const ControllerState& lastState) -{ - const auto& modifier = s_cfgHotkeys.modifiers.controller; - if ((modifier >= 0) && currentState.buttons.GetButtonState(modifier)) - { - for (const auto& buttonId : currentState.buttons.GetButtonList()) - { - const auto it = s_controllerHotkeyToFuncMap.find(buttonId); - if (it == s_controllerHotkeyToFuncMap.end()) - continue; - /* only capture clicks */ - if (lastState.buttons.GetButtonState(buttonId)) - break; - it->second(); - break; - } - } -} diff --git a/src/gui/wxgui/input/HotkeySettings.h b/src/gui/wxgui/input/HotkeySettings.h deleted file mode 100644 index 4ed1e396..00000000 --- a/src/gui/wxgui/input/HotkeySettings.h +++ /dev/null @@ -1,60 +0,0 @@ -#pragma once - -#include -#include "wxCemuConfig.h" -#include "input/api/Controller.h" - -class HotkeyEntry; - -class HotkeySettings : public wxFrame -{ -public: - static void Init(class MainWindow* mainWindow); - - static void CaptureInput(wxKeyEvent& event); - static void CaptureInput(const ControllerState& currentState, const ControllerState& lastState); - - HotkeySettings(wxWindow* parent); - ~HotkeySettings(); - -private: - inline static class MainWindow* s_mainWindow = nullptr; - static std::unordered_map> s_cfgHotkeyToFuncMap; - inline static std::unordered_map> s_keyboardHotkeyToFuncMap{}; - inline static std::unordered_map> s_controllerHotkeyToFuncMap{}; - inline static auto& s_cfgHotkeys = GetWxGUIConfig().hotkeys; - - wxPanel* m_panel; - wxFlexGridSizer* m_sizer; - wxButton* m_activeInputButton{ nullptr }; - wxTimer* m_controllerTimer{ nullptr }; - const wxSize m_minButtonSize{ 250, 45 }; - const wxString m_disabledHotkeyText{ _("----") }; - const wxString m_editModeHotkeyText{ "" }; - - std::vector m_hotkeys; - std::weak_ptr m_activeController{}; - bool m_needToSave = false; - - /* helpers */ - void CreateColumnHeaders(void); - void CreateHotkeyRow(const wxString& label, sHotkeyCfg& cfgHotkey); - wxString To_wxString(uKeyboardHotkey hotkey); - wxString To_wxString(ControllerHotkey_t hotkey); - bool IsValidKeycodeUp(int keycode); - bool SetActiveController(void); - - template - void FinalizeInput(wxButton* inputButton); - - template - void RestoreInputButton(void); - - /* events */ - void OnKeyboardHotkeyInputLeftClick(wxCommandEvent& event); - void OnControllerHotkeyInputLeftClick(wxCommandEvent& event); - void OnKeyboardHotkeyInputRightClick(wxMouseEvent& event); - void OnControllerHotkeyInputRightClick(wxMouseEvent& event); - void OnKeyUp(wxKeyEvent& event); - void OnControllerTimer(wxTimerEvent& event); -}; diff --git a/src/gui/wxgui/input/InputAPIAddWindow.cpp b/src/gui/wxgui/input/InputAPIAddWindow.cpp deleted file mode 100644 index b10a2c75..00000000 --- a/src/gui/wxgui/input/InputAPIAddWindow.cpp +++ /dev/null @@ -1,326 +0,0 @@ -#include "wxgui/input/InputAPIAddWindow.h" - -#include "input/InputManager.h" -#include "wxgui/helpers/wxCustomData.h" -#include "wxgui/helpers/wxHelpers.h" -#include "input/api/Controller.h" - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "input/ControllerFactory.h" - -wxDEFINE_EVENT(wxControllersRefreshed, wxCommandEvent); - -using wxTypeData = wxCustomData; -using wxControllerData = wxCustomData; - -InputAPIAddWindow::InputAPIAddWindow(wxWindow* parent, const wxPoint& position, - const std::vector& controllers) - : wxDialog(parent, wxID_ANY, "Add input API", position, wxDefaultSize, wxCAPTION), m_controllers(controllers) -{ - this->SetSizeHints(wxDefaultSize, wxDefaultSize); - - auto* sizer = new wxBoxSizer(wxVERTICAL); - - { - auto* api_row = new wxFlexGridSizer(2); - - // API - api_row->Add(new wxStaticText(this, wxID_ANY, _("API")), 0, wxALIGN_CENTER_VERTICAL | wxALL, 5); - - m_input_api = new wxChoice(this, wxID_ANY); - auto& providers = InputManager::instance().get_api_providers(); - for (const auto& p : providers) - { - if (p.empty()) - continue; - - const auto provider = *p.begin(); - m_input_api->Append(wxString::FromUTF8(provider->api_name()), new wxTypeData(provider->api())); - } - - m_input_api->Bind(wxEVT_CHOICE, &InputAPIAddWindow::on_api_selected, this); - api_row->Add(m_input_api, 1, wxALL | wxEXPAND, 5); - - // Controller - api_row->Add(new wxStaticText(this, wxID_ANY, _("Controller")), 0, wxALIGN_CENTER_VERTICAL | wxALL, 5); - - m_controller_list = new wxComboBox(this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0, nullptr, - wxCB_READONLY); - m_controller_list->Bind(wxEVT_COMBOBOX_DROPDOWN, &InputAPIAddWindow::on_controller_dropdown, this); - m_controller_list->Bind(wxEVT_COMBOBOX, &InputAPIAddWindow::on_controller_selected, this); - m_controller_list->SetMinSize(wxSize(240, -1)); - m_controller_list->Disable(); - api_row->Add(m_controller_list, 1, wxALL | wxEXPAND, 5); - - sizer->Add(api_row, 0, wxEXPAND, 5); - } - - sizer->Add(new wxStaticLine(this), 0, wxEXPAND); - - { - auto* end_row = new wxBoxSizer(wxHORIZONTAL); - - m_ok_button = new wxButton(this, wxID_ANY, _("Add")); - m_ok_button->Bind(wxEVT_BUTTON, &InputAPIAddWindow::on_add_button, this); - m_ok_button->Disable(); - end_row->Add(m_ok_button, 0, wxALL, 5); - - auto* cancel_button = new wxButton(this, wxID_ANY, _("Cancel")); - cancel_button->Bind(wxEVT_BUTTON, &InputAPIAddWindow::on_close_button, this); - end_row->Add(cancel_button, 0, wxALL, 5); - - sizer->Add(end_row, 0, wxEXPAND, 5); - } - - { - // optional settings - m_settings_panel = new wxPanel(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL); - auto* panel_sizer = new wxBoxSizer(wxVERTICAL); - panel_sizer->Add(new wxStaticLine(m_settings_panel), 0, wxEXPAND, 0); - - { - auto* row = new wxBoxSizer(wxHORIZONTAL); - // we only have dsu settings atm, so add elements now - row->Add(new wxStaticText(m_settings_panel, wxID_ANY, _("IP")), 0, wxALIGN_CENTER_VERTICAL | wxALL, 5); - m_ip = new wxTextCtrl(m_settings_panel, wxID_ANY, "127.0.0.1"); - row->Add(m_ip, 0, wxALL, 5); - - row->Add(new wxStaticText(m_settings_panel, wxID_ANY, _("Port")), 0, wxALIGN_CENTER_VERTICAL | wxALL, 5); - m_port = new wxTextCtrl(m_settings_panel, wxID_ANY, "26760"); - row->Add(m_port, 0, wxALL, 5); - - panel_sizer->Add(row, 0, wxEXPAND); - } - - m_settings_panel->SetSizer(panel_sizer); - m_settings_panel->Layout(); - m_settings_panel->Hide(); - - sizer->Add(m_settings_panel, 1, wxEXPAND); - } - - this->SetSizer(sizer); - this->Layout(); - sizer->Fit(this); - - this->Bind(wxControllersRefreshed, &InputAPIAddWindow::on_controllers_refreshed, this); -} - -InputAPIAddWindow::~InputAPIAddWindow() -{ - discard_thread_result(); -} - -void InputAPIAddWindow::on_add_button(wxCommandEvent& event) -{ - const auto selection = m_input_api->GetSelection(); - if (selection == wxNOT_FOUND) - { - cemu_assert_debug(false); - EndModal(wxID_CANCEL); - return; - } - - for (const auto& c : m_controllers) - { - if (*c == *m_controller) - { - wxMessageBox(_("The controller is already added!"), _("Error"), wxOK | wxCENTRE | wxICON_ERROR, this); - return; - } - } - - m_type = static_cast(m_input_api->GetClientObject(selection))->get(); - EndModal(wxID_OK); -} - -void InputAPIAddWindow::on_close_button(wxCommandEvent& event) -{ - EndModal(wxID_CANCEL); -} - -bool InputAPIAddWindow::has_custom_settings() const -{ - const auto selection = m_input_api->GetStringSelection(); - return selection == wxString::FromUTF8(to_string(InputAPI::DSUClient)); -} - -std::unique_ptr InputAPIAddWindow::get_settings() const -{ - if (!has_custom_settings()) - return {}; - - return std::make_unique(m_ip->GetValue().ToStdString(), - ConvertString(m_port->GetValue().ToStdString())); -} - -void InputAPIAddWindow::on_api_selected(wxCommandEvent& event) -{ - discard_thread_result(); - - if (m_input_api->GetSelection() == wxNOT_FOUND) - return; - - m_controller_list->Enable(); - m_controller_list->SetSelection(wxNOT_FOUND); - - const auto selection = m_input_api->GetStringSelection(); - // keyboard is a special case, as theres only one device supported atm - if (selection == wxString::FromUTF8(to_string(InputAPI::Keyboard))) - { - const auto controllers = InputManager::instance().get_api_provider(InputAPI::Keyboard)->get_controllers(); - if (!controllers.empty()) - { - m_controller = controllers[0]; - m_ok_button->Enable(); - - m_controller_list->Clear(); - const auto display_name = controllers[0]->display_name(); - const auto index = m_controller_list->Append(display_name, new wxCustomData(controllers[0])); - m_controller_list->SetSelection(index); - } - } - else - { -#if BOOST_OS_LINUX || BOOST_OS_BSD - // We rely on the wxEVT_COMBOBOX_DROPDOWN event to trigger filling the controller list, - // but on wxGTK the dropdown button cannot be clicked if the list is empty - // so as a quick and dirty workaround we fill the list here - wxCommandEvent tmpCmdEvt; - on_controller_dropdown(tmpCmdEvt); -#endif - } - - const auto show_settings = has_custom_settings(); - // dsu has special settings for ip/port - if (show_settings != m_settings_panel->IsShown()) - { - wxWindowUpdateLocker locker(this); - m_settings_panel->Show(show_settings); - Layout(); - Fit(); - } -} - -void InputAPIAddWindow::on_controller_dropdown(wxCommandEvent& event) -{ - if (m_search_running) - return; - - int selection = m_input_api->GetSelection(); - if (selection == wxNOT_FOUND) - return; - - const auto type = static_cast(m_input_api->GetClientObject(selection))->get(); - auto settings = get_settings(); - - ControllerProviderPtr provider; - if (settings) - provider = InputManager::instance().get_api_provider(type, *settings); - else - provider = InputManager::instance().get_api_provider(type); - - if (!provider) - return; - - std::string selected_uuid; - selection = m_controller_list->GetSelection(); - if (selection != wxNOT_FOUND) - { - // TODO selected_uuid - } - - m_search_running = true; - - wxWindowUpdateLocker lock(m_controller_list); - m_controller_list->Clear(); - - m_controller_list->Append(_("Searching for controllers..."), (wxClientData*)nullptr); - m_controller_list->SetSelection(wxNOT_FOUND); - - m_search_thread_data = std::make_unique(); - std::thread([this, provider, selected_uuid](std::shared_ptr data) - { - auto available_controllers = provider->get_controllers(); - - { - std::lock_guard lock{data->mutex}; - if(!data->discardResult) - { - wxCommandEvent event(wxControllersRefreshed); - event.SetEventObject(m_controller_list); - event.SetClientObject(new wxCustomData(std::move(available_controllers))); - event.SetInt(provider->api()); - event.SetString(selected_uuid); - wxPostEvent(this, event); - m_search_running = false; - } - } - }, m_search_thread_data).detach(); -} - -void InputAPIAddWindow::on_controller_selected(wxCommandEvent& event) -{ - if (m_search_running) - { - return; - } - - const auto selection = m_controller_list->GetSelection(); - if (selection == wxNOT_FOUND) - { - return; - } - - if (auto* controller = (wxControllerData*)m_controller_list->GetClientObject(selection)) - { - m_controller = controller->ref(); - m_ok_button->Enable(); - } -} - -void InputAPIAddWindow::on_controllers_refreshed(wxCommandEvent& event) -{ - const auto type = event.GetInt(); - wxASSERT(0 <= type && type < InputAPI::MAX); - - auto* controllers = dynamic_cast(event.GetEventObject()); - wxASSERT(controllers); - - const auto available_controllers = static_cast>>*>(event. - GetClientObject())->get(); - const auto selected_uuid = event.GetString().ToStdString(); - bool item_selected = false; - - wxWindowUpdateLocker lock(controllers); - controllers->Clear(); - for (const auto& c : available_controllers) - { - const auto display_name = c->display_name(); - const auto uuid = c->uuid(); - const auto index = controllers->Append(display_name, new wxCustomData(c)); - if (!item_selected && selected_uuid == uuid) - { - controllers->SetSelection(index); - item_selected = true; - } - } -} - -void InputAPIAddWindow::discard_thread_result() -{ - m_search_running = false; - if(m_search_thread_data) - { - std::lock_guard lock{m_search_thread_data->mutex}; - m_search_thread_data->discardResult = true; - } -} diff --git a/src/gui/wxgui/input/InputAPIAddWindow.h b/src/gui/wxgui/input/InputAPIAddWindow.h deleted file mode 100644 index 1e17a8db..00000000 --- a/src/gui/wxgui/input/InputAPIAddWindow.h +++ /dev/null @@ -1,62 +0,0 @@ -#pragma once - -#include "input/api/InputAPI.h" - -#include -#include -#include - -#include "wxgui/helpers/wxCustomData.h" -#include "input/api/Controller.h" - -class wxComboBox; -class wxChoice; -class wxTextCtrl; - -using wxAPIType = wxCustomData; - -class InputAPIAddWindow : public wxDialog -{ -public: - InputAPIAddWindow(wxWindow* parent, const wxPoint& position, const std::vector& controllers); - ~InputAPIAddWindow(); - - bool is_valid() const { return m_type.has_value() && m_controller != nullptr; } - InputAPI::Type get_type() const { return m_type.value(); } - std::shared_ptr get_controller() const { return m_controller; } - - bool has_custom_settings() const; - std::unique_ptr get_settings() const; - -private: - void on_add_button(wxCommandEvent& event); - void on_close_button(wxCommandEvent& event); - - - void on_api_selected(wxCommandEvent& event); - - void on_controller_dropdown(wxCommandEvent& event); - void on_controller_selected(wxCommandEvent& event); - void on_controllers_refreshed(wxCommandEvent& event); - - void discard_thread_result(); - - wxChoice* m_input_api; - wxComboBox* m_controller_list; - wxButton* m_ok_button; - - wxPanel* m_settings_panel; - wxTextCtrl* m_ip, * m_port; - - std::optional m_type; - std::shared_ptr m_controller; - - std::vector m_controllers; - std::atomic_bool m_search_running = false; - struct AsyncThreadData - { - std::atomic_bool discardResult = false; - std::mutex mutex; - }; - std::shared_ptr m_search_thread_data; -}; diff --git a/src/gui/wxgui/input/InputSettings2.cpp b/src/gui/wxgui/input/InputSettings2.cpp deleted file mode 100644 index d77e1430..00000000 --- a/src/gui/wxgui/input/InputSettings2.cpp +++ /dev/null @@ -1,976 +0,0 @@ -#include "wxgui/input/InputSettings2.h" - -#include - -#include "input/InputManager.h" -#include "wxgui/helpers/wxHelpers.h" -#include "wxgui/helpers/wxControlObject.h" -#include "wxgui/helpers/wxCustomData.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "config/ActiveSettings.h" -#include "wxgui/input/InputAPIAddWindow.h" -#include "input/ControllerFactory.h" - -#ifdef HAS_BLUEZ -#include "wxgui/input/PairingDialog.h" -#endif - -#include "wxgui/input/panels/VPADInputPanel.h" -#include "wxgui/input/panels/ProControllerInputPanel.h" - -#include "wxgui/input/settings/DefaultControllerSettings.h" -#include "wxgui/input/panels/ClassicControllerInputPanel.h" -#include "wxgui/input/panels/WiimoteInputPanel.h" -#include "wxgui/input/settings/WiimoteControllerSettings.h" -#include "util/EventService.h" - -#include "resource/embedded/resources.h" - -bool g_inputConfigWindowHasFocus = false; - -using wxTypeData = wxCustomData; -using wxControllerData = wxCustomData; - -struct ControllerPage -{ - EmulatedControllerPtr m_controller; - - // profiles - wxComboBox* m_profiles; - wxButton* m_profile_load, * m_profile_save, * m_profile_delete; - wxStaticText* m_profile_status; - - // emulated controller - wxComboBox* m_emulated_controller; - - // controller api - wxComboBox* m_controllers; - wxButton* m_controller_api_add, *m_controller_api_remove; - - wxButton* m_controller_settings, * m_controller_calibrate, *m_controller_clear; - wxBitmapButton* m_controller_connected; - - // panel - std::array m_panels{}; -}; -using wxControllerPageData = wxCustomData; - - -InputSettings2::InputSettings2(wxWindow* parent) - : wxDialog(parent, wxID_ANY, _("Input settings")) -{ - this->SetSizeHints(wxDefaultSize, wxDefaultSize); - - g_inputConfigWindowHasFocus = true; - - m_connected = wxHelper::LoadThemedBitmapFromPNG(INPUT_CONNECTED_png, sizeof(INPUT_CONNECTED_png), wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT)); - m_disconnected = wxHelper::LoadThemedBitmapFromPNG(INPUT_DISCONNECTED_png, sizeof(INPUT_DISCONNECTED_png), wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT)); - m_low_battery = wxHelper::LoadThemedBitmapFromPNG(INPUT_LOW_BATTERY_png, sizeof(INPUT_LOW_BATTERY_png), wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT)); - - auto* sizer = new wxBoxSizer(wxVERTICAL); - - m_notebook = new wxNotebook(this, wxID_ANY); - for(size_t i = 0; i < InputManager::kMaxController; ++i) - { - auto* page = new wxPanel(m_notebook, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL); - page->SetClientObject(nullptr); // force internal type to client object - m_notebook->AddPage(page, formatWxString(_("Controller {}"), i + 1)); - } - - m_notebook->Bind(wxEVT_NOTEBOOK_PAGE_CHANGED, &InputSettings2::on_controller_page_changed, this); - sizer->Add(m_notebook, 1, wxEXPAND); - - m_notebook->SetSelection(0); - auto* first_page = initialize_page(0); - - // init first/default page for fitting size - auto* page_data = (wxControllerPageData*)first_page->GetClientObject(); - auto* panel = new VPADInputPanel(first_page); - page_data->ref().m_panels[EmulatedController::Type::VPAD] = panel; - - auto* first_page_sizer = dynamic_cast(first_page->GetSizer()); - auto* panel_sizer = first_page_sizer->FindItemAtPosition(wxGBPosition(7, 0))->GetSizer(); - panel_sizer->Add(panel, 0, wxEXPAND); - - panel->Show(); - first_page->Layout(); - - SetSizer(sizer); - Layout(); - Fit(); - - panel->Hide(); - - update_state(); - - Bind(wxEVT_TIMER, &InputSettings2::on_timer, this); - - m_timer = new wxTimer(this); - m_timer->Start(25); - - m_controller_changed = EventService::instance().connect(&InputSettings2::on_controller_changed, this); -} - -InputSettings2::~InputSettings2() -{ - m_controller_changed.disconnect(); - - g_inputConfigWindowHasFocus = false; - m_timer->Stop(); - InputManager::instance().save(); -} - - -wxWindow* InputSettings2::initialize_page(size_t index) -{ - auto* page = m_notebook->GetPage(index); - if (page->GetClientObject()) // already initialized - return page; - - page->Bind(wxEVT_LEFT_UP, &InputSettings2::on_left_click, this); - - ControllerPage page_data{}; - const auto emulated_controller = InputManager::instance().get_controller(index); - page_data.m_controller = emulated_controller; - - wxWindowUpdateLocker lock(page); - auto* sizer = new wxGridBagSizer(); - - { - // profile - sizer->Add(new wxStaticText(page, wxID_ANY, _("Profile")), wxGBPosition(0, 0), wxDefaultSpan, wxALIGN_CENTER_VERTICAL | wxALL, 5); - auto* profiles = new wxComboBox(page, wxID_ANY, kDefaultProfileName); - sizer->Add(profiles, wxGBPosition(0, 1), wxDefaultSpan, wxALIGN_CENTER_VERTICAL | wxALL | wxEXPAND, 5); - -#if BOOST_OS_LINUX || BOOST_OS_BSD - // We rely on the wxEVT_COMBOBOX_DROPDOWN event to trigger filling the profile list, - // but on wxGTK the dropdown button cannot be clicked if the list is empty - // so as a quick and dirty workaround we fill the list here - wxCommandEvent tmpCmdEvt; - tmpCmdEvt.SetEventObject(profiles); - on_profile_dropdown(tmpCmdEvt); -#endif - - if (emulated_controller && emulated_controller->has_profile_name()) - { - profiles->SetValue(emulated_controller->get_profile_name()); - } - - auto* load_bttn = new wxButton(page, wxID_ANY, _("Load")); - load_bttn->Disable(); - sizer->Add(load_bttn, wxGBPosition(0, 2), wxDefaultSpan, wxALIGN_CENTER_VERTICAL | wxALL, 5); - - auto* save_bttn = new wxButton(page, wxID_ANY, _("Save")); - save_bttn->Disable(); - sizer->Add(save_bttn, wxGBPosition(0, 3), wxDefaultSpan, wxALIGN_CENTER_VERTICAL | wxALL, 5); - - auto* delete_bttn = new wxButton(page, wxID_ANY, _("Delete")); - delete_bttn->Disable(); - sizer->Add(delete_bttn, wxGBPosition(0, 4), wxDefaultSpan, wxALIGN_CENTER_VERTICAL | wxALL, 5); - - auto* profile_status = new wxStaticText(page, wxID_ANY, _("controller set by gameprofile. changes won't be saved permanently!")); - profile_status->SetMinSize(wxSize(200, -1)); - profile_status->Wrap(200); - sizer->Add(profile_status, wxGBPosition(0, 5), wxDefaultSpan, wxALIGN_CENTER_VERTICAL | wxALL | wxRESERVE_SPACE_EVEN_IF_HIDDEN, 5); - - if(InputManager::instance().is_gameprofile_set(index)) - { - profile_status->SetForegroundColour(wxTheColourDatabase->Find("ERROR")); - } - else - { - profile_status->Hide(); - } - - load_bttn->Bind(wxEVT_BUTTON, &InputSettings2::on_profile_load, this); - save_bttn->Bind(wxEVT_BUTTON, &InputSettings2::on_profile_save, this); - delete_bttn->Bind(wxEVT_BUTTON, &InputSettings2::on_profile_delete, this); - - profiles->Bind(wxEVT_COMBOBOX_DROPDOWN, &InputSettings2::on_profile_dropdown, this); - profiles->Bind(wxEVT_TEXT, &InputSettings2::on_profile_text_changed, this); - - page_data.m_profiles = profiles; - page_data.m_profile_load = load_bttn; - page_data.m_profile_save = save_bttn; - page_data.m_profile_delete = delete_bttn; - page_data.m_profile_status = profile_status; - } - - sizer->Add(new wxStaticLine(page), wxGBPosition(1, 0), wxGBSpan(1, 6), wxEXPAND); - - { - // emulated controller - sizer->Add(new wxStaticText(page, wxID_ANY, _("Emulated controller")), wxGBPosition(2, 0), wxDefaultSpan, wxALIGN_CENTER_VERTICAL | wxALL, 5); - - auto* econtroller_box = new wxComboBox(page, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0, nullptr, wxCB_READONLY); - econtroller_box->SetMinSize(wxSize(200, -1)); - econtroller_box->Bind(wxEVT_COMBOBOX_DROPDOWN, &InputSettings2::on_emulated_controller_dropdown, this); - econtroller_box->Bind(wxEVT_COMBOBOX, &InputSettings2::on_emulated_controller_selected, this); - - econtroller_box->AppendString(_("Disabled")); - econtroller_box->SetSelection(0); - - sizer->Add(econtroller_box, wxGBPosition(2, 1), wxDefaultSpan, wxALIGN_CENTER_VERTICAL | wxALL | wxEXPAND, 5); - page_data.m_emulated_controller = econtroller_box; - } - - sizer->Add(new wxStaticLine(page), wxGBPosition(3, 0), wxGBSpan(1, 6), wxEXPAND); - - { - // controller api - sizer->Add(new wxStaticText(page, wxID_ANY, _("Controller")), wxGBPosition(4, 0), wxDefaultSpan, wxALIGN_CENTER_VERTICAL | wxALL, 5); - - auto* controllers = new wxComboBox(page, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0, nullptr, wxCB_READONLY); - controllers->Bind(wxEVT_COMBOBOX, &InputSettings2::on_controller_selected, this); - controllers->Bind(wxEVT_COMBOBOX_DROPDOWN, &InputSettings2::on_controller_dropdown, this); - controllers->SetMinSize(wxSize(300, -1)); - - page_data.m_controllers = controllers; - - sizer->Add(controllers, wxGBPosition(4, 1), wxDefaultSpan, wxALIGN_CENTER_VERTICAL | wxALL | wxEXPAND, 5); - - { - // add/remove buttons - auto* bttn_sizer = new wxBoxSizer(wxHORIZONTAL); - - auto* add_api = new wxButton(page, wxID_ANY, " + ", wxDefaultPosition, wxDefaultSize, wxBU_EXACTFIT); - add_api->Bind(wxEVT_BUTTON, &InputSettings2::on_controller_add, this); - bttn_sizer->Add(add_api, 0, wxALL, 5); - - auto* remove_api = new wxButton(page, wxID_ANY, " - ", wxDefaultPosition, wxDefaultSize, wxBU_EXACTFIT); - remove_api->Bind(wxEVT_BUTTON, &InputSettings2::on_controller_remove, this); - bttn_sizer->Add(remove_api, 0, wxALL, 5); - - sizer->Add(bttn_sizer, wxGBPosition(4, 2), wxDefaultSpan, wxEXPAND, 5); - - page_data.m_controller_api_add = add_api; - page_data.m_controller_api_remove = remove_api; - } - -#ifdef HAS_BLUEZ - auto* pairingDialog = new wxButton(page, wxID_ANY, _("Pair Wii/Wii U Controller")); - pairingDialog->Bind(wxEVT_BUTTON, [this](wxEvent&) { - PairingDialog pairing_dialog(this); - pairing_dialog.ShowModal(); - }); - sizer->Add(pairingDialog, wxGBPosition(5, 0), wxDefaultSpan, wxALIGN_CENTER_VERTICAL | wxALL, 5); -#endif - - // controller - auto* controller_bttns = new wxBoxSizer(wxHORIZONTAL); - auto* settings = new wxButton(page, wxID_ANY, _("Settings")); - settings->Bind(wxEVT_BUTTON, &InputSettings2::on_controller_settings, this); - settings->Disable(); - controller_bttns->Add(settings, 0, wxALL, 5); - - auto* calibrate = new wxButton(page, wxID_ANY, _("Calibrate")); - calibrate->Bind(wxEVT_BUTTON, &InputSettings2::on_controller_calibrate, this); - calibrate->Disable(); - controller_bttns->Add(calibrate, 0, wxALL, 5); - - auto* clear = new wxButton(page, wxID_ANY, _("Clear")); - clear->Bind(wxEVT_BUTTON, &InputSettings2::on_controller_clear, this); - controller_bttns->Add(clear, 0, wxALL, 5); - - sizer->Add(controller_bttns, wxGBPosition(5, 1), wxDefaultSpan, wxALIGN_CENTER_VERTICAL); - - auto* connected_button = new wxBitmapButton(page, wxID_ANY, m_disconnected); - connected_button->Bind(wxEVT_BUTTON, &InputSettings2::on_controller_connect, this); - connected_button->SetToolTip(_("Test if the controller is connected")); - sizer->Add(connected_button, wxGBPosition(5, 2), wxDefaultSpan, wxALIGN_CENTER_VERTICAL | wxALL, 5); // TODO replace with icon - - page_data.m_controller_settings = settings; - page_data.m_controller_calibrate = calibrate; - page_data.m_controller_clear = clear; - page_data.m_controller_connected = connected_button; - - } - - sizer->Add(new wxStaticLine(page), wxGBPosition(6, 0), wxGBSpan(1, 6), wxEXPAND); - - auto* panel_sizer = new wxBoxSizer(wxVERTICAL); - sizer->Add(panel_sizer, wxGBPosition(7, 0), wxGBSpan(1, 6), wxEXPAND | wxALL, 5); - - page->SetSizer(sizer); - page->Layout(); - - page->SetClientObject(new wxCustomData(page_data)); - - return page; -} - -std::shared_ptr InputSettings2::get_active_controller() const -{ - auto& page_data = get_current_page_data(); - - const auto selection = page_data.m_controllers->GetSelection(); - if(selection != wxNOT_FOUND) - { - if(auto* controller = (wxControllerData*)page_data.m_controllers->GetClientObject(selection)) - return controller->ref(); - } - - return {}; -} - -bool InputSettings2::has_settings(InputAPI::Type type) -{ - switch(type) - { - case InputAPI::Keyboard: - return false; - default: - return true; - } -} - -void InputSettings2::update_state() -{ - auto* page = m_notebook->GetCurrentPage(); - wxWindowUpdateLocker lock(page); - - auto* page_data_ptr = (wxControllerPageData*)page->GetClientObject(); - wxASSERT(page_data_ptr); - auto& page_data = page_data_ptr->ref(); - - page_data.m_profile_status->Hide(); - - EmulatedControllerPtr emulated_controller = page_data.m_controller; - auto has_controllers = false; - - // update emulated - if(emulated_controller) - { - has_controllers = !emulated_controller->get_controllers().empty(); - - const auto emulated_type = emulated_controller->type(); - int index = page_data.m_emulated_controller->Append(wxString::FromUTF8(emulated_controller->type_to_string(emulated_type))); - page_data.m_emulated_controller->SetSelection(index); - - const auto controller_selection = page_data.m_controllers->GetStringSelection(); - page_data.m_controllers->Clear(); - if (has_controllers) - { - for (const auto& c : emulated_controller->get_controllers()) - { - page_data.m_controllers->Append(fmt::format("{} [{}]", c->display_name(), c->api_name()), new wxCustomData(c)); - } - - if (page_data.m_controllers->GetCount() > 0) - { - page_data.m_controllers->SetSelection(0); - if (!controller_selection.empty()) - page_data.m_controllers->SetStringSelection(controller_selection); - } - } - } - else - { - page_data.m_emulated_controller->SetValue(_("Disabled")); - } - - ControllerPtr controller; - if (page_data.m_controllers->GetSelection() != wxNOT_FOUND) - { - if (const auto data = (wxControllerData*)page_data.m_controllers->GetClientObject(page_data.m_controllers->GetSelection())) - controller = data->ref(); - } - - if (controller && controller->is_connected()) - page_data.m_controller_connected->SetBitmap(m_connected); - else - page_data.m_controller_connected->SetBitmap(m_disconnected); - - // update controller - page_data.m_controller_calibrate->Enable(has_controllers); - page_data.m_controller_api_remove->Enable(has_controllers); - page_data.m_controller_settings->Enable(controller && has_settings(controller->api())); - - // update settings - // update panel - // test if we need to update to correct panel - std::optional active_api{}; - for(auto i = 0; i < EmulatedController::Type::MAX; ++i) - { - if(page_data.m_panels[i] && page_data.m_panels[i]->IsShown()) - { - active_api = (EmulatedController::Type)i; - break; - } - } - - // disabled and no emulated controller - if (!active_api && !emulated_controller) - return; - - // enabled correct panel for active controller - if (active_api && emulated_controller && emulated_controller->type() == active_api.value()) - { - // same controller type panel already shown, refresh content of panels - for (auto* panel : page_data.m_panels) - { - if (panel) - panel->load_controller(page_data.m_controller); - } - return; - } - - // hide all panels - for (auto* panel : page_data.m_panels) - { - if (panel) - panel->Hide(); - } - - // show required panel - if (emulated_controller) - { - auto* sizer = dynamic_cast(page->GetSizer()); - wxASSERT(sizer); - - const auto type = page_data.m_controller->type(); - InputPanel* panel = page_data.m_panels[type]; - if (!panel) - { - switch (type) - { - case EmulatedController::Type::VPAD: - panel = new VPADInputPanel(page); - break; - case EmulatedController::Pro: - panel = new ProControllerInputPanel(page); - break; - case EmulatedController::Classic: - panel = new ClassicControllerInputPanel(page); - break; - case EmulatedController::Wiimote: - panel = new WiimoteInputPanel(page); - break; - default: - cemu_assert_debug(false); - return; - } - - page_data.m_panels[type] = panel; - - auto* panel_sizer = sizer->FindItemAtPosition(wxGBPosition(7, 0))->GetSizer(); - wxASSERT(panel_sizer); - panel_sizer->Add(panel, 0, wxEXPAND); - } - - panel->load_controller(page_data.m_controller); - if (has_controllers) - panel->set_selected_controller(emulated_controller, emulated_controller->get_controllers()[0]); - - panel->Show(); - page->wxWindowBase::Layout(); - page->wxWindow::Update(); - } -} - -void InputSettings2::on_controller_changed() -{ - for(auto i = 0 ; i < m_notebook->GetPageCount(); ++i) - { - auto* page = m_notebook->GetPage(i); - if (!page) - continue; - - auto* page_data_ptr = (wxControllerPageData*)page->GetClientObject(); - if (!page_data_ptr) - continue; - - const auto& page_data = page_data_ptr->ref(); - if (page_data.m_controllers->GetSelection() != wxNOT_FOUND) - { - if (const auto data = (wxControllerData*)page_data.m_controllers->GetClientObject(page_data.m_controllers->GetSelection())) - { - if (const auto controller = data->ref()) - { - if (controller->connect()) - page_data.m_controller_connected->SetBitmap(m_connected); - else - page_data.m_controller_connected->SetBitmap(m_disconnected); - } - } - } - } -} - -void InputSettings2::on_notebook_page_changed(wxBookCtrlEvent& event) -{ - initialize_page(event.GetSelection()); - update_state(); - event.Skip(); -} - -void InputSettings2::on_timer(wxTimerEvent&) -{ - auto& page_data = get_current_page_data(); - if (!page_data.m_controller) { - return; - } - - auto* panel = page_data.m_panels[page_data.m_controller->type()]; - if (!panel) - return; - - auto controller = get_active_controller(); - if (controller) { - panel->on_timer(page_data.m_controller, controller); - } -} - -void InputSettings2::on_left_click(wxMouseEvent& event) -{ - event.Skip(); - - auto& page_data = get_current_page_data(); - if (!page_data.m_controller) { - return; - } - - auto* panel = page_data.m_panels[page_data.m_controller->type()]; - if (!panel) - return; - - panel->on_left_click(event); -} - -void InputSettings2::on_profile_dropdown(wxCommandEvent& event) -{ - auto* profile_names = dynamic_cast(event.GetEventObject()); - wxASSERT(profile_names); - wxWindowUpdateLocker lock(profile_names); - - const auto selected_value = profile_names->GetStringSelection(); - profile_names->Clear(); - - for(const auto& profile : InputManager::get_profiles()) - { - profile_names->Append(wxString::FromUTF8(profile)); - } - - profile_names->SetStringSelection(selected_value); -} - -void InputSettings2::on_profile_text_changed(wxCommandEvent& event) -{ - auto* profile_names = dynamic_cast(event.GetEventObject()); - wxASSERT(profile_names); - - auto& page_data = get_current_page_data(); - - // load_bttn, save_bttn, delete_bttn, profile_status - const auto text = event.GetString(); - const bool valid_name = InputManager::is_valid_profilename(text.utf8_string()); - const bool name_exists = profile_names->FindString(text) != wxNOT_FOUND; - - page_data.m_profile_load->Enable(name_exists); - page_data.m_profile_save->Enable(valid_name); - page_data.m_profile_delete->Enable(name_exists); - page_data.m_profile_status->Hide(); -} - -void InputSettings2::on_profile_load(wxCommandEvent& event) -{ - auto& page_data = get_current_page_data(); - - auto* profile_names = page_data.m_profiles; - auto* text = page_data.m_profile_status; - - const auto selection = profile_names->GetValue().utf8_string(); - text->Show(); - if (selection.empty() || !InputManager::is_valid_profilename(selection)) - { - text->SetLabelText(_("invalid profile name")); - text->SetForegroundColour(wxTheColourDatabase->Find("ERROR")); - text->Refresh(); - return; - } - - const auto page_index = m_notebook->GetSelection(); - if (InputManager::instance().load(page_index, selection)) - { - text->SetLabelText(_("profile loaded")); - text->SetForegroundColour(wxTheColourDatabase->Find("SUCCESS")); - } - else - { - text->SetLabelText(_("couldn't load profile")); - text->SetForegroundColour(wxTheColourDatabase->Find("ERROR")); - } - - text->Refresh(); - - // update controller info - page_data.m_controller = InputManager::instance().get_controller(page_index); - update_state(); -} - -void InputSettings2::on_profile_save(wxCommandEvent& event) -{ - auto& page_data = get_current_page_data(); - - auto* profile_names = page_data.m_profiles; - auto* text = page_data.m_profile_status; - - const auto selection = profile_names->GetValue().utf8_string(); - text->Show(); - if (selection.empty() || !InputManager::is_valid_profilename(selection)) - { - text->SetLabelText(_("invalid profile name")); - text->SetForegroundColour(wxTheColourDatabase->Find("ERROR")); - text->Refresh(); - return; - } - - if(InputManager::instance().save(m_notebook->GetSelection(), selection)) - { - text->SetLabelText(_("profile saved")); - text->SetForegroundColour(wxTheColourDatabase->Find("SUCCESS")); - } - else - { - text->SetLabelText(_("couldn't save profile")); - text->SetForegroundColour(wxTheColourDatabase->Find("ERROR")); - } - - text->Refresh(); -} - -void InputSettings2::on_profile_delete(wxCommandEvent& event) -{ - auto& page_data = get_current_page_data(); - - auto* profile_names = page_data.m_profiles; - auto* text = page_data.m_profile_status; - - const auto selection = profile_names->GetStringSelection().utf8_string(); - - text->Show(); - if (selection.empty() || !InputManager::is_valid_profilename(selection)) - { - text->SetLabelText(_("invalid profile name")); - text->SetForegroundColour(wxTheColourDatabase->Find("ERROR")); - text->Refresh(); - return; - } - try - { - const fs::path old_path = ActiveSettings::GetConfigPath("controllerProfiles/{}.txt", selection); - fs::remove(old_path); - - const fs::path path = ActiveSettings::GetConfigPath("controllerProfiles/{}.xml", selection); - fs::remove(path); - - profile_names->ChangeValue(kDefaultProfileName); - text->SetLabelText(_("profile deleted")); - text->SetForegroundColour(wxTheColourDatabase->Find("SUCCESS")); - - page_data.m_profile_load->Disable(); - page_data.m_profile_save->Disable(); - page_data.m_profile_delete->Disable(); - } - catch (const std::exception&) - { - text->SetLabelText(_("can't delete profile")); - text->SetForegroundColour(wxTheColourDatabase->Find("ERROR")); - } - text->Refresh(); -} - - -void InputSettings2::on_controller_page_changed(wxBookCtrlEvent& event) -{ - initialize_page(event.GetSelection()); - update_state(); - event.Skip(); -} - -void InputSettings2::on_emulated_controller_selected(wxCommandEvent& event) -{ - const auto page_index = m_notebook->GetSelection(); - auto& page_data = get_current_page_data(); - - const auto selection = event.GetSelection(); - if(selection == 0) // disabled selected - { - page_data.m_controller = {}; - InputManager::instance().delete_controller(page_index, true); - } - else - { - try - { - const auto type = EmulatedController::type_from_string(event.GetString().utf8_string()); - // same has already been selected - if (page_data.m_controller && page_data.m_controller->type() == type) - return; - - // set new controller - const auto new_controller = InputManager::instance().set_controller(page_index, type); - page_data.m_controller = new_controller; - - // append controllers if some were already added before - if (new_controller->get_controllers().empty()) - { - // test if we had no emulated controller before but still assigned controllers we want to transfer now - for (uint32 i = 0; i < page_data.m_controllers->GetCount(); ++i) - { - if (auto* controller = (wxControllerData*)page_data.m_controllers->GetClientObject(i)) - { - new_controller->add_controller(controller->ref()); - } - } - } - - // set default mappings if any controllers available - for(const auto& c: new_controller->get_controllers()) - { - new_controller->set_default_mapping(c); - } - } - catch (const std::exception&) - { - cemu_assert_debug(false); - } - - } - - update_state(); -} - -void InputSettings2::on_emulated_controller_dropdown(wxCommandEvent& event) -{ - auto* emulated_controllers = dynamic_cast(event.GetEventObject()); - wxASSERT(emulated_controllers); - - wxWindowUpdateLocker lock(emulated_controllers); - - bool is_gamepad_selected = false; - bool is_wpad_selected = false; - const auto selected = emulated_controllers->GetSelection(); - const auto selected_value = emulated_controllers->GetStringSelection(); - if(selected != wxNOT_FOUND) - { - is_gamepad_selected = selected_value == wxString::FromUTF8(EmulatedController::type_to_string(EmulatedController::Type::VPAD)); - is_wpad_selected = !is_gamepad_selected && selected != 0; - } - - const auto [vpad_count, wpad_count] = InputManager::instance().get_controller_count(); - - emulated_controllers->Clear(); - emulated_controllers->AppendString(_("Disabled")); - - if (vpad_count < InputManager::kMaxVPADControllers || is_gamepad_selected) - emulated_controllers->Append(wxString::FromUTF8(EmulatedController::type_to_string(EmulatedController::Type::VPAD))); - - if (wpad_count < InputManager::kMaxWPADControllers || is_wpad_selected) - { - emulated_controllers->AppendString(wxString::FromUTF8(EmulatedController::type_to_string(EmulatedController::Type::Pro))); - emulated_controllers->AppendString(wxString::FromUTF8(EmulatedController::type_to_string(EmulatedController::Type::Classic))); - emulated_controllers->AppendString(wxString::FromUTF8(EmulatedController::type_to_string(EmulatedController::Type::Wiimote))); - } - - emulated_controllers->SetStringSelection(selected_value); -} - -void InputSettings2::on_controller_selected(wxCommandEvent& event) -{ - auto& page_data = get_current_page_data(); - - const auto enabled = event.GetSelection() != wxNOT_FOUND; - page_data.m_controller_api_remove->Enable(enabled); - // page_data->ref().m_controller_list->Clear(); - if(enabled) - { - // get selected controller if any todo - if (auto* controller = (wxControllerData*)page_data.m_controllers->GetClientObject(event.GetSelection())) - { - page_data.m_controller_settings->Enable(has_settings(controller->ref()->api())); - - if(page_data.m_controller) - { - page_data.m_panels[page_data.m_controller->type()]->set_selected_controller(page_data.m_controller, controller->ref()); - } - } - - } -} - -void InputSettings2::on_controller_dropdown(wxCommandEvent& event) -{ - if(auto* controllers = dynamic_cast(event.GetEventObject())) - { - if(controllers->GetCount()== 0) - { - on_controller_add(event); - controllers->SetSelection(0); - } - } -} - -ControllerPage& InputSettings2::get_current_page_data() const -{ - auto* page = m_notebook->GetCurrentPage(); - auto* page_data_ptr = (wxControllerPageData*)page->GetClientObject(); - wxASSERT(page_data_ptr); - return page_data_ptr->ref(); -} - -void InputSettings2::on_controller_connect(wxCommandEvent& event) -{ - auto& page_data = get_current_page_data(); - - if (page_data.m_controllers->GetSelection() != wxNOT_FOUND) - { - if (const auto data = (wxControllerData*)page_data.m_controllers->GetClientObject(page_data.m_controllers->GetSelection())) - { - if(const auto controller = data->ref()) - { - if(controller->connect()) - page_data.m_controller_connected->SetBitmap(m_connected); - else - page_data.m_controller_connected->SetBitmap(m_disconnected); - } - } - } -} - -void InputSettings2::on_controller_add(wxCommandEvent& event) -{ - auto& page_data = get_current_page_data(); - - std::vector controllers; - controllers.reserve(page_data.m_controllers->GetCount()); - for(uint32 i = 0; i < page_data.m_controllers->GetCount(); ++i) - { - if (auto* controller = (wxControllerData*)page_data.m_controllers->GetClientObject(i)) - controllers.emplace_back(controller->ref()); - } - - InputAPIAddWindow wnd(this, wxGetMousePosition() + wxSize(5, 5), controllers); - if (wnd.ShowModal() != wxID_OK) - return; - - wxASSERT(wnd.is_valid()); - - const auto controller = wnd.get_controller(); - - const auto api_type = wnd.get_type(); - controller->connect(); - const int index = page_data.m_controllers->Append(fmt::format("{} [{}]", controller->display_name(), to_string(api_type)), new wxCustomData(controller)); - - page_data.m_controllers->Select(index); - - if(page_data.m_controller) - { - page_data.m_controller->add_controller(controller); - - const auto type = page_data.m_controller->type(); - // if first controller and we got no mappings, add default mappings - if(page_data.m_controller->set_default_mapping(controller)) - page_data.m_panels[type]->load_controller(page_data.m_controller); - - page_data.m_panels[type]->set_selected_controller(page_data.m_controller, controller); - } - - update_state(); -} - -void InputSettings2::on_controller_remove(wxCommandEvent& event) -{ - auto& page_data = get_current_page_data(); - - auto* api_box = page_data.m_controllers; - int selection = api_box->GetSelection(); - if (selection == wxNOT_FOUND) - return; - - if (page_data.m_controller) { - if (auto* controller = (wxControllerData*)page_data.m_controllers->GetClientObject(selection)) - { - page_data.m_controller->remove_controller(controller->ref()); - page_data.m_panels[page_data.m_controller->type()]->load_controller(page_data.m_controller); - } - } - - page_data.m_panels[page_data.m_controller->type()]->reset_colours(); - - api_box->Delete(selection); - api_box->Refresh(); - - update_state(); - - if (api_box->GetCount() > 0) - { - selection = selection > 0 ? (selection - 1) : 0; - api_box->SetSelection(selection); - } - - update_state(); -} - -void InputSettings2::on_controller_calibrate(wxCommandEvent& event) -{ - if(const auto controller = get_active_controller()) - controller->calibrate(); -} - -void InputSettings2::on_controller_clear(wxCommandEvent& event) -{ - auto& page_data = get_current_page_data(); - if (page_data.m_controller) { - const auto type = page_data.m_controller->type(); - - page_data.m_panels[type]->reset_configuration(); - page_data.m_controller->clear_mappings(); - } -} - -void InputSettings2::on_controller_settings(wxCommandEvent& event) -{ - auto controller = get_active_controller(); - if (!controller) - return; - - switch(controller->api()) - { - - case InputAPI::DirectInput: - case InputAPI::XInput: - case InputAPI::GameCube: - case InputAPI::WGIGamepad: - case InputAPI::WGIRawController: - case InputAPI::SDLController: - case InputAPI::DSUClient: - { - DefaultControllerSettings wnd(this, wxGetMousePosition() + wxSize(5, 5), controller); - wnd.ShowModal(); - break; - } - - case InputAPI::Keyboard: break; - - #ifdef SUPPORTS_WIIMOTE - case InputAPI::Wiimote: { - const auto wiimote = std::dynamic_pointer_cast(controller); - wxASSERT(wiimote); - WiimoteControllerSettings wnd(this, wxGetMousePosition() + wxSize(5, 5), wiimote); - wnd.ShowModal(); - break; - } - #endif - } -} diff --git a/src/gui/wxgui/input/InputSettings2.h b/src/gui/wxgui/input/InputSettings2.h deleted file mode 100644 index 1a3c8bb1..00000000 --- a/src/gui/wxgui/input/InputSettings2.h +++ /dev/null @@ -1,73 +0,0 @@ -#pragma once - -#include -#include -#include - -#include "input/api/InputAPI.h" - -#include - -struct ControllerPage; -class ControllerBase; - -class InputSettings2 : public wxDialog -{ -public: - InputSettings2(wxWindow* parent); - ~InputSettings2(); - -private: - const wxString kDefaultProfileName = _(""); - - wxNotebook* m_notebook; - wxTimer* m_timer; - - wxBitmap m_connected, m_disconnected, m_low_battery; - - wxWindow* initialize_page(size_t index); - - // currently selected controller from active tab - std::shared_ptr get_active_controller() const; - - bool has_settings(InputAPI::Type type); - ControllerPage& get_current_page_data() const; - void update_state(); - - boost::signals2::connection m_controller_changed; - void on_controller_changed(); - - // events - void on_notebook_page_changed(wxBookCtrlEvent& event); - void on_timer(wxTimerEvent& event); - - void on_left_click(wxMouseEvent& event); - - void on_controller_page_changed(wxBookCtrlEvent& event); - - void on_profile_dropdown(wxCommandEvent& event); - void on_profile_text_changed(wxCommandEvent& event); - - void on_profile_load(wxCommandEvent& event); - void on_profile_save(wxCommandEvent& event); - void on_profile_delete(wxCommandEvent& event); - - void on_emulated_controller_selected(wxCommandEvent& event); - void on_emulated_controller_dropdown(wxCommandEvent& event); - - void on_controller_selected(wxCommandEvent& event); - void on_controller_dropdown(wxCommandEvent& event); - void on_controller_connect(wxCommandEvent& event); - - void on_controller_add(wxCommandEvent& event); - void on_controller_remove(wxCommandEvent& event); - - void on_controller_calibrate(wxCommandEvent& event); - void on_controller_clear(wxCommandEvent& event); - void on_controller_settings(wxCommandEvent& event); - - // void on_controller_dropdown(wxCommandEvent& event); - // void on_controllers_refreshed(wxCommandEvent& event); - -}; - diff --git a/src/gui/wxgui/input/PairingDialog.cpp b/src/gui/wxgui/input/PairingDialog.cpp deleted file mode 100644 index 9bf2870f..00000000 --- a/src/gui/wxgui/input/PairingDialog.cpp +++ /dev/null @@ -1,300 +0,0 @@ -#include "wxgui/wxgui.h" -#include "PairingDialog.h" - -#if BOOST_OS_WINDOWS -#include -#endif -#if BOOST_OS_LINUX -#include -#include -#include -#include -#endif - -wxDECLARE_EVENT(wxEVT_PROGRESS_PAIR, wxCommandEvent); -wxDEFINE_EVENT(wxEVT_PROGRESS_PAIR, wxCommandEvent); - -PairingDialog::PairingDialog(wxWindow* parent) - : wxDialog(parent, wxID_ANY, _("Pairing..."), wxDefaultPosition, wxDefaultSize, wxCAPTION | wxMINIMIZE_BOX | wxSYSTEM_MENU | wxTAB_TRAVERSAL | wxCLOSE_BOX) -{ - auto* sizer = new wxBoxSizer(wxVERTICAL); - m_gauge = new wxGauge(this, wxID_ANY, 100, wxDefaultPosition, wxSize(350, 20), wxGA_HORIZONTAL); - m_gauge->SetValue(0); - sizer->Add(m_gauge, 0, wxALL | wxEXPAND, 5); - - auto* rows = new wxFlexGridSizer(0, 2, 0, 0); - rows->AddGrowableCol(1); - - m_text = new wxStaticText(this, wxID_ANY, _("Searching for controllers...")); - rows->Add(m_text, 0, wxALL | wxALIGN_CENTER_VERTICAL, 5); - - { - auto* right_side = new wxBoxSizer(wxHORIZONTAL); - - m_cancelButton = new wxButton(this, wxID_ANY, _("Cancel")); - m_cancelButton->Bind(wxEVT_BUTTON, &PairingDialog::OnCancelButton, this); - right_side->Add(m_cancelButton, 0, wxALL, 5); - - rows->Add(right_side, 1, wxALIGN_RIGHT, 5); - } - - sizer->Add(rows, 0, wxALL | wxEXPAND, 5); - - SetSizerAndFit(sizer); - Centre(wxBOTH); - - Bind(wxEVT_CLOSE_WINDOW, &PairingDialog::OnClose, this); - Bind(wxEVT_PROGRESS_PAIR, &PairingDialog::OnGaugeUpdate, this); - - m_thread = std::thread(&PairingDialog::WorkerThread, this); -} - -PairingDialog::~PairingDialog() -{ - Unbind(wxEVT_CLOSE_WINDOW, &PairingDialog::OnClose, this); -} - -void PairingDialog::OnClose(wxCloseEvent& event) -{ - event.Skip(); - - m_threadShouldQuit = true; - if (m_thread.joinable()) - m_thread.join(); -} - -void PairingDialog::OnCancelButton(const wxCommandEvent& event) -{ - Close(); -} - -void PairingDialog::OnGaugeUpdate(wxCommandEvent& event) -{ - PairingState state = (PairingState)event.GetInt(); - - switch (state) - { - case PairingState::Pairing: - { - m_text->SetLabel(_("Found controller. Pairing...")); - m_gauge->SetValue(50); - break; - } - - case PairingState::Finished: - { - m_text->SetLabel(_("Successfully paired the controller.")); - m_gauge->SetValue(100); - m_cancelButton->SetLabel(_("Close")); - break; - } - - case PairingState::NoBluetoothAvailable: - { - m_text->SetLabel(_("Failed to find a suitable Bluetooth radio.")); - m_gauge->SetValue(0); - m_cancelButton->SetLabel(_("Close")); - break; - } - - case PairingState::SearchFailed: - { - m_text->SetLabel(_("Failed to find controllers.")); - m_gauge->SetValue(0); - m_cancelButton->SetLabel(_("Close")); - break; - } - - case PairingState::PairingFailed: - { - m_text->SetLabel(_("Failed to pair with the found controller.")); - m_gauge->SetValue(0); - m_cancelButton->SetLabel(_("Close")); - break; - } - - case PairingState::BluetoothUnusable: - { - m_text->SetLabel(_("Please use your system's Bluetooth manager instead.")); - m_gauge->SetValue(0); - m_cancelButton->SetLabel(_("Close")); - break; - } - - default: - { - break; - } - } -} - -#if BOOST_OS_WINDOWS -void PairingDialog::WorkerThread() -{ - const std::wstring wiimoteName = L"Nintendo RVL-CNT-01"; - const std::wstring wiiUProControllerName = L"Nintendo RVL-CNT-01-UC"; - - const GUID bthHidGuid = {0x00001124, 0x0000, 0x1000, {0x80, 0x00, 0x00, 0x80, 0x5F, 0x9B, 0x34, 0xFB}}; - - const BLUETOOTH_FIND_RADIO_PARAMS radioFindParams = - { - .dwSize = sizeof(BLUETOOTH_FIND_RADIO_PARAMS)}; - - HANDLE radio = INVALID_HANDLE_VALUE; - HBLUETOOTH_RADIO_FIND radioFind = BluetoothFindFirstRadio(&radioFindParams, &radio); - if (radioFind == nullptr) - { - UpdateCallback(PairingState::NoBluetoothAvailable); - return; - } - - BluetoothFindRadioClose(radioFind); - - BLUETOOTH_RADIO_INFO radioInfo = - { - .dwSize = sizeof(BLUETOOTH_RADIO_INFO)}; - - DWORD result = BluetoothGetRadioInfo(radio, &radioInfo); - if (result != ERROR_SUCCESS) - { - UpdateCallback(PairingState::NoBluetoothAvailable); - return; - } - - const BLUETOOTH_DEVICE_SEARCH_PARAMS searchParams = - { - .dwSize = sizeof(BLUETOOTH_DEVICE_SEARCH_PARAMS), - - .fReturnAuthenticated = FALSE, - .fReturnRemembered = FALSE, - .fReturnUnknown = TRUE, - .fReturnConnected = FALSE, - - .fIssueInquiry = TRUE, - .cTimeoutMultiplier = 5, - - .hRadio = radio}; - - BLUETOOTH_DEVICE_INFO info = - { - .dwSize = sizeof(BLUETOOTH_DEVICE_INFO)}; - - while (!m_threadShouldQuit) - { - HBLUETOOTH_DEVICE_FIND deviceFind = BluetoothFindFirstDevice(&searchParams, &info); - if (deviceFind == nullptr) - { - UpdateCallback(PairingState::SearchFailed); - return; - } - - while (!m_threadShouldQuit) - { - if (info.szName == wiimoteName || info.szName == wiiUProControllerName) - { - BluetoothFindDeviceClose(deviceFind); - - UpdateCallback(PairingState::Pairing); - - wchar_t passwd[6] = {radioInfo.address.rgBytes[0], radioInfo.address.rgBytes[1], radioInfo.address.rgBytes[2], radioInfo.address.rgBytes[3], radioInfo.address.rgBytes[4], radioInfo.address.rgBytes[5]}; - DWORD bthResult = BluetoothAuthenticateDevice(nullptr, radio, &info, passwd, 6); - if (bthResult != ERROR_SUCCESS) - { - UpdateCallback(PairingState::PairingFailed); - return; - } - - bthResult = BluetoothSetServiceState(radio, &info, &bthHidGuid, BLUETOOTH_SERVICE_ENABLE); - if (bthResult != ERROR_SUCCESS) - { - UpdateCallback(PairingState::PairingFailed); - return; - } - - UpdateCallback(PairingState::Finished); - return; - } - - BOOL nextDevResult = BluetoothFindNextDevice(deviceFind, &info); - if (nextDevResult == FALSE) - { - break; - } - } - - BluetoothFindDeviceClose(deviceFind); - } -} -#elif defined(HAS_BLUEZ) -void PairingDialog::WorkerThread() -{ - constexpr static uint8_t LIAC_LAP[] = {0x00, 0x8b, 0x9e}; - - constexpr static auto isWiimoteName = [](std::string_view name) { - return name == "Nintendo RVL-CNT-01" || name == "Nintendo RVL-CNT-01-TR"; - }; - - // Get default BT device - const auto hostId = hci_get_route(nullptr); - if (hostId < 0) - { - UpdateCallback(PairingState::NoBluetoothAvailable); - return; - } - - // Search for device - inquiry_info* infos = nullptr; - m_cancelButton->Disable(); - const auto respCount = hci_inquiry(hostId, 7, 4, LIAC_LAP, &infos, IREQ_CACHE_FLUSH); - m_cancelButton->Enable(); - if (respCount <= 0) - { - UpdateCallback(PairingState::SearchFailed); - return; - } - stdx::scope_exit infoFree([&]() { bt_free(infos);}); - - if (m_threadShouldQuit) - return; - - // Open dev to read name - const auto hostDev = hci_open_dev(hostId); - stdx::scope_exit devClose([&]() { hci_close_dev(hostDev);}); - - char nameBuffer[HCI_MAX_NAME_LENGTH] = {}; - - bool foundADevice = false; - // Get device name and compare. Would use product and vendor id from SDP, but many third-party Wiimotes don't store them - for (const auto& devInfo : std::span(infos, respCount)) - { - const auto& addr = devInfo.bdaddr; - const auto err = hci_read_remote_name(hostDev, &addr, HCI_MAX_NAME_LENGTH, nameBuffer, - 2000); - if (m_threadShouldQuit) - return; - if (err || !isWiimoteName(nameBuffer)) - continue; - - L2CapWiimote::AddCandidateAddress(addr); - foundADevice = true; - const auto& b = addr.b; - cemuLog_log(LogType::Force, "Pairing Dialog: Found '{}' with address '{:02x}:{:02x}:{:02x}:{:02x}:{:02x}:{:02x}'", - nameBuffer, b[5], b[4], b[3], b[2], b[1], b[0]); - } - if (foundADevice) - UpdateCallback(PairingState::Finished); - else - UpdateCallback(PairingState::SearchFailed); -} -#else -void PairingDialog::WorkerThread() -{ - UpdateCallback(PairingState::BluetoothUnusable); -} -#endif -void PairingDialog::UpdateCallback(PairingState state) -{ - auto* event = new wxCommandEvent(wxEVT_PROGRESS_PAIR); - event->SetInt((int)state); - wxQueueEvent(this, event); -} \ No newline at end of file diff --git a/src/gui/wxgui/input/PairingDialog.h b/src/gui/wxgui/input/PairingDialog.h deleted file mode 100644 index 02cab4fc..00000000 --- a/src/gui/wxgui/input/PairingDialog.h +++ /dev/null @@ -1,38 +0,0 @@ -#pragma once - -#include -#include -#include -#include - -class PairingDialog : public wxDialog -{ -public: - PairingDialog(wxWindow* parent); - ~PairingDialog(); - -private: - enum class PairingState - { - Pairing, - Finished, - NoBluetoothAvailable, - SearchFailed, - PairingFailed, - BluetoothUnusable - }; - - void OnClose(wxCloseEvent& event); - void OnCancelButton(const wxCommandEvent& event); - void OnGaugeUpdate(wxCommandEvent& event); - - void WorkerThread(); - void UpdateCallback(PairingState state); - - wxStaticText* m_text; - wxGauge* m_gauge; - wxButton* m_cancelButton; - - std::thread m_thread; - bool m_threadShouldQuit = false; -}; diff --git a/src/gui/wxgui/input/panels/ClassicControllerInputPanel.cpp b/src/gui/wxgui/input/panels/ClassicControllerInputPanel.cpp deleted file mode 100644 index b1b5205c..00000000 --- a/src/gui/wxgui/input/panels/ClassicControllerInputPanel.cpp +++ /dev/null @@ -1,118 +0,0 @@ -#include "wxgui/input/panels/ClassicControllerInputPanel.h" - -#include -#include -#include -#include -#include - -#include "wxgui/helpers/wxControlObject.h" -#include "input/emulated/ClassicController.h" -#include "wxgui/helpers/wxHelpers.h" -#include "wxgui/components/wxInputDraw.h" - -constexpr ClassicController::ButtonId g_kFirstColumnItems[] = { ClassicController::kButtonId_A, ClassicController::kButtonId_B, ClassicController::kButtonId_X, ClassicController::kButtonId_Y, ClassicController::kButtonId_L, ClassicController::kButtonId_R, ClassicController::kButtonId_ZL, ClassicController::kButtonId_ZR, ClassicController::kButtonId_Plus, ClassicController::kButtonId_Minus }; -constexpr ClassicController::ButtonId g_kSecondColumnItems[] = { ClassicController::kButtonId_StickL_Up, ClassicController::kButtonId_StickL_Down, ClassicController::kButtonId_StickL_Left, ClassicController::kButtonId_StickL_Right }; -constexpr ClassicController::ButtonId g_kThirdColumnItems[] = { ClassicController::kButtonId_StickR_Up, ClassicController::kButtonId_StickR_Down, ClassicController::kButtonId_StickR_Left, ClassicController::kButtonId_StickR_Right }; -constexpr ClassicController::ButtonId g_kFourthRowItems[] = { ClassicController::kButtonId_Up, ClassicController::kButtonId_Down, ClassicController::kButtonId_Left, ClassicController::kButtonId_Right }; - - -ClassicControllerInputPanel::ClassicControllerInputPanel(wxWindow* parent) - : InputPanel(parent) -{ - auto bold_font = GetFont(); - bold_font.MakeBold(); - - auto* main_sizer = new wxGridBagSizer(); - sint32 row = 0; - sint32 column = 0; - for (const auto& id : g_kFirstColumnItems) - { - row++; - add_button_row(main_sizer, row, column, id); - } - - ////////////////////////////////////////////////////////////////// - - main_sizer->Add(new wxStaticLine(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxVERTICAL), wxGBPosition(0, column + 2), wxGBSpan(11, 1), wxALL | wxEXPAND, 5); - - row = 0; - column += 3; - - auto text = new wxStaticText(this, wxID_ANY, _("Left Axis")); - text->SetFont(bold_font); - main_sizer->Add(text, wxGBPosition(row, column), wxGBSpan(1, 3), wxALL | wxEXPAND, 5); - - for (const auto& id : g_kSecondColumnItems) - { - row++; - add_button_row(main_sizer, row, column, id); - } - - row++; - - // input drawer - m_left_draw = new wxInputDraw(this, wxID_ANY, wxDefaultPosition, { 60, 60 }); - main_sizer->Add(m_left_draw, wxGBPosition(row, column + 1), wxGBSpan(2, 1), wxTOP | wxBOTTOM | wxEXPAND | wxALIGN_CENTER, 5); - - main_sizer->Add(new wxStaticLine(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxVERTICAL), wxGBPosition(0, column + 3), wxGBSpan(11, 1), wxALL | wxEXPAND, 5); - - ////////////////////////////////////////////////////////////////// - - row = 0; - column += 4; - - text = new wxStaticText(this, wxID_ANY, _("Right Axis")); - text->SetFont(bold_font); - main_sizer->Add(text, wxGBPosition(row, column), wxGBSpan(1, 3), wxALL | wxEXPAND, 5); - - for (const auto& id : g_kThirdColumnItems) - { - row++; - add_button_row(main_sizer, row, column, id); - } - - row++; - - // input drawer - m_right_draw = new wxInputDraw(this, wxID_ANY, wxDefaultPosition, { 60, 60 }); - main_sizer->Add(m_right_draw, wxGBPosition(row, column + 1), wxGBSpan(2, 1), wxTOP | wxBOTTOM | wxEXPAND | wxALIGN_CENTER, 5); - - main_sizer->Add(new wxStaticLine(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxVERTICAL), wxGBPosition(0, column + 3), wxGBSpan(11, 1), wxALL | wxEXPAND, 5); - - ////////////////////////////////////////////////////////////////// - - row = 0; - column += 4; - - text = new wxStaticText(this, wxID_ANY, _("D-pad")); - text->SetFont(bold_font); - main_sizer->Add(text, wxGBPosition(row, column), wxGBSpan(1, 3), wxALL | wxEXPAND, 5); - - for (auto id : g_kFourthRowItems) - { - row++; - add_button_row(main_sizer, row, column, id); - } - - ////////////////////////////////////////////////////////////////// - - SetSizer(main_sizer); - Layout(); -} - -void ClassicControllerInputPanel::add_button_row(wxGridBagSizer *sizer, sint32 row, sint32 column, const ClassicController::ButtonId &button_id) { - sizer->Add( - new wxStaticText(this, wxID_ANY, wxGetTranslation(wxString::FromUTF8(ClassicController::get_button_name(button_id)))), - wxGBPosition(row, column), - wxDefaultSpan, - wxALL | wxALIGN_CENTER_VERTICAL, 5); - - auto* text_ctrl = new wxTextCtrl(this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL | wxTE_PROCESS_ENTER | wxTE_PROCESS_TAB); - text_ctrl->SetClientData(reinterpret_cast(button_id)); - text_ctrl->SetMinSize(wxSize(150, -1)); - text_ctrl->SetEditable(false); - text_ctrl->SetBackgroundColour(kKeyColourNormalMode); - bind_hotkey_events(text_ctrl); - sizer->Add(text_ctrl, wxGBPosition(row, column + 1), wxDefaultSpan, wxALL | wxEXPAND, 5); -} diff --git a/src/gui/wxgui/input/panels/ClassicControllerInputPanel.h b/src/gui/wxgui/input/panels/ClassicControllerInputPanel.h deleted file mode 100644 index 8be8c52e..00000000 --- a/src/gui/wxgui/input/panels/ClassicControllerInputPanel.h +++ /dev/null @@ -1,19 +0,0 @@ -#pragma once - -#include -#include "input/emulated/ClassicController.h" -#include "wxgui/input/panels/InputPanel.h" - -class wxInputDraw; - -class ClassicControllerInputPanel : public InputPanel -{ -public: - ClassicControllerInputPanel(wxWindow* parent); - -private: - wxInputDraw* m_left_draw, * m_right_draw; - - void add_button_row(wxGridBagSizer *sizer, sint32 row, sint32 column, const ClassicController::ButtonId &button_id); -}; - diff --git a/src/gui/wxgui/input/panels/InputPanel.cpp b/src/gui/wxgui/input/panels/InputPanel.cpp deleted file mode 100644 index e460f0c6..00000000 --- a/src/gui/wxgui/input/panels/InputPanel.cpp +++ /dev/null @@ -1,285 +0,0 @@ -#include "wxgui/input/panels/InputPanel.h" - -#include -#include - -#include "wxgui/helpers/wxHelpers.h" - -InputPanel::InputPanel(wxWindow* parent) - : wxPanel(parent, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL | wxNO_BORDER | wxWANTS_CHARS) -{ - Bind(wxEVT_LEFT_UP, &InputPanel::on_left_click, this); -} - -void InputPanel::on_timer(const EmulatedControllerPtr& emulated_controller, const ControllerPtr& controller) -{ - const auto& state = controller->update_state(); - - if(m_focused_element == wxID_NONE) - { - return; - } - - const auto element = dynamic_cast(FindWindow(m_focused_element)); - wxASSERT(element); - - const auto mapping = reinterpret_cast(element->GetClientData()); - - // reset mapping - if(std::exchange(m_right_down, false) || WindowSystem::IsKeyDown(WindowSystem::PlatformKeyCodes::ESCAPE)) - { - element->SetBackgroundColour(kKeyColourNormalMode); - m_color_backup[element->GetId()] = kKeyColourNormalMode; - - emulated_controller->delete_mapping(mapping); - if(element->IsEmpty()) - reset_focused_element(); - else - element->SetValue(wxEmptyString); - - return; - } - - static bool s_was_idle = true; - if (state.buttons.IsIdle()) - { - s_was_idle = true; - return; - } - - if (!s_was_idle) - { - return; - } - s_was_idle = false; - for(const auto& id : state.buttons.GetButtonList()) - { - if (controller->has_axis()) - { - // test if one axis direction is pressed more than the other - if ((id == kAxisXP || id == kAxisXN) && (state.buttons.GetButtonState(kAxisYP) || state.buttons.GetButtonState(kAxisYN))) - { - if (std::abs(state.axis.y) > std::abs(state.axis.x)) - continue; - } - else if ((id == kAxisYP || id == kAxisYN) && (state.buttons.GetButtonState(kAxisXP) || state.buttons.GetButtonState(kAxisXN))) - { - if (std::abs(state.axis.x) > std::abs(state.axis.y)) - continue; - } - else if ((id == kRotationXP || id == kRotationXN) && (state.buttons.GetButtonState(kRotationYP) || state.buttons.GetButtonState(kRotationYN))) - { - if (std::abs(state.rotation.y) > std::abs(state.rotation.x)) - continue; - } - else if ((id == kRotationYP || id == kRotationYN) && (state.buttons.GetButtonState(kRotationXP) || state.buttons.GetButtonState(kRotationXN))) - { - if (std::abs(state.rotation.x) > std::abs(state.rotation.y)) - continue; - } - else if ((id == kTriggerXP || id == kTriggerXN) && (state.buttons.GetButtonState(kTriggerYP) || state.buttons.GetButtonState(kTriggerYN))) - { - if (std::abs(state.trigger.y) > std::abs(state.trigger.x)) - continue; - } - else if ((id == kTriggerYP || id == kTriggerYN) && (state.buttons.GetButtonState(kTriggerXP) || state.buttons.GetButtonState(kTriggerXN))) - { - if (std::abs(state.trigger.x) > std::abs(state.trigger.y)) - continue; - } - - // ignore too low button values on configuration - if (id >= kButtonAxisStart) - { - if (controller->get_axis_value(id) < 0.33f) { - cemuLog_logDebug(LogType::Force, "skipping since value too low {}", controller->get_axis_value(id)); - s_was_idle = true; - return; - } - } - } - - emulated_controller->set_mapping(mapping, controller, id); - element->SetValue(controller->get_button_name(id)); - element->SetBackgroundColour(kKeyColourNormalMode); - m_color_backup[element->GetId()] = kKeyColourNormalMode; - break; - } - - if (const auto sibling = get_next_sibling(element)) - sibling->SetFocus(); - else // last element reached - { - reset_focused_element(); - this->SetFocusIgnoringChildren(); - } -} - -void InputPanel::reset_configuration() -{ - m_color_backup.clear(); - - wxWindowUpdateLocker lock(this); - for (const auto& c : GetChildren()) - { - if (auto* text = dynamic_cast(c)) - { - text->SetValue(wxEmptyString); - text->SetBackgroundColour(kKeyColourNormalMode); - text->Refresh(); - } - } -} - -void InputPanel::reset_colours() -{ - m_color_backup.clear(); - - wxWindowUpdateLocker lock(this); - for (const auto& c : GetChildren()) - { - if (auto* text = dynamic_cast(c)) - { - text->SetBackgroundColour(kKeyColourNormalMode); - text->Refresh(); - } - } -} - - -void InputPanel::load_controller(const EmulatedControllerPtr& controller) -{ - reset_configuration(); - if(!controller) - { - return; - } - - if(controller->get_controllers().empty()) - { - return; - } - - wxWindowUpdateLocker lock(this); - for (auto* child : this->GetChildren()) - { - const auto text = dynamic_cast(child); - if (text == nullptr) - continue; - - - const auto mapping = reinterpret_cast(text->GetClientData()); - if (mapping <= 0) - continue; - - auto button_name = controller->get_mapping_name(mapping); - text->ChangeValue(button_name); - } -} - -void InputPanel::set_selected_controller(const EmulatedControllerPtr& emulated_controller, const ControllerPtr& controller) -{ - wxWindowUpdateLocker lock(this); - for (auto* child : this->GetChildren()) - { - const auto text = dynamic_cast(child); - if (text == nullptr) - continue; - - if (text->GetId() == m_focused_element) - continue; - - const auto mapping = reinterpret_cast(text->GetClientData()); - if (mapping <= 0) - continue; - - const auto mapping_controller = emulated_controller->get_mapping_controller(mapping); - if (!mapping_controller) - continue; - - text->SetBackgroundColour(*mapping_controller == *controller ? kKeyColourNormalMode : kKeyColourActiveMode); - text->Refresh(); - } -} - -void InputPanel::bind_hotkey_events(wxTextCtrl* text_ctrl) -{ - text_ctrl->Bind(wxEVT_SET_FOCUS, &InputPanel::on_edit_key_focus, this); - text_ctrl->Bind(wxEVT_KILL_FOCUS, &InputPanel::on_edit_key_kill_focus, this); - text_ctrl->Bind(wxEVT_RIGHT_DOWN, &InputPanel::on_right_click, this); -#if BOOST_OS_LINUX - // Bind to a no-op lambda to disable arrow keys navigation - text_ctrl->Bind(wxEVT_KEY_DOWN, [](wxKeyEvent &) {}); -#endif -} - -void InputPanel::on_left_click(wxMouseEvent& event) -{ - if (m_focused_element == wxID_NONE) - return; - - const auto focuses_element = FindWindow(m_focused_element); - wxASSERT(focuses_element); - - wxFocusEvent focus(wxEVT_KILL_FOCUS, m_focused_element); - focus.SetWindow(focuses_element); - focuses_element->GetEventHandler()->ProcessEvent(focus); - - this->SetFocusIgnoringChildren(); -} - -void InputPanel::on_edit_key_focus(wxFocusEvent& event) -{ - auto* text = dynamic_cast(event.GetEventObject()); - wxASSERT(text); - - m_color_backup[text->GetId()] = text->GetBackgroundColour(); - - text->SetBackgroundColour(kKeyColourEditMode); - #if BOOST_OS_WINDOWS - text->HideNativeCaret(); - #endif - text->Refresh(); - - m_focused_element = text->GetId(); - event.Skip(); -} - -void InputPanel::on_edit_key_kill_focus(wxFocusEvent& event) -{ - reset_focused_element(); - event.Skip(); -} - -void InputPanel::on_right_click(wxMouseEvent& event) -{ - m_right_down = true; - if(m_focused_element == wxID_NONE) - { - auto* text = dynamic_cast(event.GetEventObject()); - wxASSERT(text); - text->SetFocus(); - } -} - -bool InputPanel::reset_focused_element() -{ - if (m_focused_element == wxID_NONE) - return false; - - auto* prev_element = dynamic_cast(FindWindow(m_focused_element)); - wxASSERT(prev_element); - - if(m_color_backup.find(prev_element->GetId()) != m_color_backup.cend()) - prev_element->SetBackgroundColour(m_color_backup[prev_element->GetId()]); - else - prev_element->SetBackgroundColour(kKeyColourNormalMode); - -#if BOOST_OS_WINDOWS - prev_element->HideNativeCaret(); -#endif - prev_element->Refresh(); - - m_focused_element = wxID_NONE; - return true; -} diff --git a/src/gui/wxgui/input/panels/InputPanel.h b/src/gui/wxgui/input/panels/InputPanel.h deleted file mode 100644 index 818708eb..00000000 --- a/src/gui/wxgui/input/panels/InputPanel.h +++ /dev/null @@ -1,48 +0,0 @@ -#pragma once - -#include -#include - -#include "input/emulated/EmulatedController.h" -#include "input/api/Controller.h" - -#include "wxgui/wxHelper.h" - -class ControllerBase; -class wxTextCtrl; -class wxComboBox; - -class InputPanel : public wxPanel -{ -public: - const wxColour kKeyColourNormalMode = GetBackgroundColour(); - const wxColour kKeyColourEditMode = wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHT); - const wxColour kKeyColourActiveMode = wxHelper::CalculateAccentColour(GetBackgroundColour()); - - InputPanel(wxWindow* parent); - - ControllerPtr get_active_controller() const; - - virtual void on_timer(const EmulatedControllerPtr& emulated_controller, const ControllerPtr& controller); - void on_left_click(wxMouseEvent& event); - - void reset_configuration(); - virtual void load_controller(const EmulatedControllerPtr& controller); - - void set_selected_controller(const EmulatedControllerPtr& emulated_controller, const ControllerPtr& controller_base); - void reset_colours(); - -protected: - void bind_hotkey_events(wxTextCtrl* text_ctrl); - - void on_edit_key_focus(wxFocusEvent& event); - void on_edit_key_kill_focus(wxFocusEvent& event); - void on_right_click(wxMouseEvent& event); - - bool reset_focused_element(); - - bool m_right_down = false; - int m_focused_element = wxID_NONE; - std::unordered_map m_color_backup; -}; - diff --git a/src/gui/wxgui/input/panels/ProControllerInputPanel.cpp b/src/gui/wxgui/input/panels/ProControllerInputPanel.cpp deleted file mode 100644 index 8b8ac741..00000000 --- a/src/gui/wxgui/input/panels/ProControllerInputPanel.cpp +++ /dev/null @@ -1,131 +0,0 @@ -#include "wxgui/input/panels/ProControllerInputPanel.h" - -#include -#include -#include -#include - -#include "input/emulated/ProController.h" -#include "wxgui/helpers/wxHelpers.h" -#include "wxgui/components/wxInputDraw.h" - -const ProController::ButtonId g_kFirstColumnItems[] = { ProController::kButtonId_A, ProController::kButtonId_B, ProController::kButtonId_X, ProController::kButtonId_Y, ProController::kButtonId_L, ProController::kButtonId_R, ProController::kButtonId_ZL, ProController::kButtonId_ZR, ProController::kButtonId_Plus, ProController::kButtonId_Minus }; -const ProController::ButtonId g_kSecondColumnItems[] = { ProController::kButtonId_StickL, ProController::kButtonId_StickL_Up, ProController::kButtonId_StickL_Down, ProController::kButtonId_StickL_Left, ProController::kButtonId_StickL_Right }; -const ProController::ButtonId g_kThirdColumnItems[] = { ProController::kButtonId_StickR, ProController::kButtonId_StickR_Up, ProController::kButtonId_StickR_Down, ProController::kButtonId_StickR_Left, ProController::kButtonId_StickR_Right }; -const ProController::ButtonId g_kFourthRowItems[] = { ProController::kButtonId_Up, ProController::kButtonId_Down, ProController::kButtonId_Left, ProController::kButtonId_Right }; - - -ProControllerInputPanel::ProControllerInputPanel(wxWindow* parent) - : InputPanel(parent) -{ - auto bold_font = GetFont(); - bold_font.MakeBold(); - - auto main_sizer = new wxGridBagSizer(); - - sint32 row = 0; - sint32 column = 0; - for (auto id : g_kFirstColumnItems) - { - row++; - add_button_row(main_sizer, row, column, id); - } - - ////////////////////////////////////////////////////////////////// - - main_sizer->Add(new wxStaticLine(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxVERTICAL), wxGBPosition(0, column + 2), wxGBSpan(11, 1), wxALL | wxEXPAND, 5); - - row = 0; - column += 3; - - auto text = new wxStaticText(this, wxID_ANY, _("Left Axis")); - text->SetFont(bold_font); - main_sizer->Add(text, wxGBPosition(row, column), wxGBSpan(1, 3), wxALL | wxEXPAND, 5); - - for (auto id : g_kSecondColumnItems) - { - row++; - add_button_row(main_sizer, row, column, id); - } - - row++; - - // input drawer - m_left_draw = new wxInputDraw(this, wxID_ANY, wxDefaultPosition, { 60, 60 }); - main_sizer->Add(m_left_draw, wxGBPosition(row, column + 1), wxGBSpan(2, 1), wxTOP | wxBOTTOM | wxEXPAND | wxALIGN_CENTER, 5); - - main_sizer->Add(new wxStaticLine(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxVERTICAL), wxGBPosition(0, column + 3), wxGBSpan(11, 1), wxALL | wxEXPAND, 5); - - ////////////////////////////////////////////////////////////////// - - row = 0; - column += 4; - - text = new wxStaticText(this, wxID_ANY, _("Right Axis")); - text->SetFont(bold_font); - main_sizer->Add(text, wxGBPosition(row, column), wxGBSpan(1, 3), wxALL | wxEXPAND, 5); - - for (auto id : g_kThirdColumnItems) - { - row++; - add_button_row(main_sizer, row, column, id); - } - - row++; - - // input drawer - m_right_draw = new wxInputDraw(this, wxID_ANY, wxDefaultPosition, { 60, 60 }); - main_sizer->Add(m_right_draw, wxGBPosition(row, column + 1), wxGBSpan(2, 1), wxTOP | wxBOTTOM | wxEXPAND | wxALIGN_CENTER, 5); - - main_sizer->Add(new wxStaticLine(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxVERTICAL), wxGBPosition(0, column + 3), wxGBSpan(11, 1), wxALL | wxEXPAND, 5); - - ////////////////////////////////////////////////////////////////// - - row = 0; - column += 4; - - text = new wxStaticText(this, wxID_ANY, _("D-pad")); - text->SetFont(bold_font); - main_sizer->Add(text, wxGBPosition(row, column), wxGBSpan(1, 3), wxALL | wxEXPAND, 5); - - for (auto id : g_kFourthRowItems) - { - row++; - add_button_row(main_sizer, row, column, id); - } - - ////////////////////////////////////////////////////////////////// - - SetSizerAndFit(main_sizer); -} - -void ProControllerInputPanel::add_button_row(wxGridBagSizer *sizer, sint32 row, sint32 column, const ProController::ButtonId &button_id) { - sizer->Add( - new wxStaticText(this, wxID_ANY, wxGetTranslation(wxString::FromUTF8(ProController::get_button_name(button_id)))), - wxGBPosition(row, column), - wxDefaultSpan, - wxALL | wxALIGN_CENTER_VERTICAL, 5); - - auto text_ctrl = new wxTextCtrl(this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL | wxTE_PROCESS_ENTER | wxTE_PROCESS_TAB); - text_ctrl->SetClientData((void*)button_id); - text_ctrl->SetMinSize(wxSize(150, -1)); - text_ctrl->SetEditable(false); - text_ctrl->SetBackgroundColour(kKeyColourNormalMode); - bind_hotkey_events(text_ctrl); - sizer->Add(text_ctrl, wxGBPosition(row, column + 1), wxDefaultSpan, wxALL | wxEXPAND, 5); -} - -void ProControllerInputPanel::on_timer(const EmulatedControllerPtr& emulated_controller, - const ControllerPtr& controller_base) -{ - InputPanel::on_timer(emulated_controller, controller_base); - - if (emulated_controller) - { - const auto axis = emulated_controller->get_axis(); - const auto rotation = emulated_controller->get_rotation(); - - m_left_draw->SetAxisValue(axis); - m_right_draw->SetAxisValue(rotation); - } -} diff --git a/src/gui/wxgui/input/panels/ProControllerInputPanel.h b/src/gui/wxgui/input/panels/ProControllerInputPanel.h deleted file mode 100644 index cd82a957..00000000 --- a/src/gui/wxgui/input/panels/ProControllerInputPanel.h +++ /dev/null @@ -1,19 +0,0 @@ -#pragma once - -#include -#include "input/emulated/ProController.h" -#include "wxgui/input/panels/InputPanel.h" -#include "wxgui/components/wxInputDraw.h" - -class ProControllerInputPanel : public InputPanel -{ -public: - ProControllerInputPanel(wxWindow* parent); - - void on_timer(const EmulatedControllerPtr& emulated_controller, const ControllerPtr& controller) override; - -private: - wxInputDraw* m_left_draw, * m_right_draw; - - void add_button_row(wxGridBagSizer *sizer, sint32 row, sint32 column, const ProController::ButtonId &button_id); -}; diff --git a/src/gui/wxgui/input/panels/VPADInputPanel.cpp b/src/gui/wxgui/input/panels/VPADInputPanel.cpp deleted file mode 100644 index 676d70eb..00000000 --- a/src/gui/wxgui/input/panels/VPADInputPanel.cpp +++ /dev/null @@ -1,206 +0,0 @@ -#include "wxgui/input/panels/VPADInputPanel.h" - -#include -#include -#include -#include -#include -#include - - -#include "wxgui/helpers/wxControlObject.h" -#include "wxgui/helpers/wxHelpers.h" -#include "wxgui/components/wxInputDraw.h" -#include "input/emulated/VPADController.h" - -constexpr VPADController::ButtonId g_kFirstColumnItems[] = -{ - VPADController::kButtonId_A, VPADController::kButtonId_B, VPADController::kButtonId_X, VPADController::kButtonId_Y, - VPADController::kButtonId_L, VPADController::kButtonId_R, VPADController::kButtonId_ZL, VPADController::kButtonId_ZR, - VPADController::kButtonId_Plus, VPADController::kButtonId_Minus -}; - -constexpr VPADController::ButtonId g_kSecondColumnItems[] = -{ - VPADController::kButtonId_StickL, VPADController::kButtonId_StickL_Up, VPADController::kButtonId_StickL_Down, VPADController::kButtonId_StickL_Left, VPADController::kButtonId_StickL_Right -}; - -constexpr VPADController::ButtonId g_kThirdColumnItems[] = -{ - VPADController::kButtonId_StickR, VPADController::kButtonId_StickR_Up, VPADController::kButtonId_StickR_Down, VPADController::kButtonId_StickR_Left, VPADController::kButtonId_StickR_Right -}; - -constexpr VPADController::ButtonId g_kFourthRowItems[] = -{ - VPADController::kButtonId_Up, VPADController::kButtonId_Down, VPADController::kButtonId_Left, VPADController::kButtonId_Right -}; - - -VPADInputPanel::VPADInputPanel(wxWindow* parent) - : InputPanel(parent) -{ - auto bold_font = GetFont(); - bold_font.MakeBold(); - - auto* main_sizer = new wxGridBagSizer(); - - sint32 row = 0; - sint32 column = 0; - for (const auto& id : g_kFirstColumnItems) - { - row++; - add_button_row(main_sizer, row, column, id); - } - - ////////////////////////////////////////////////////////////////// - - main_sizer->Add(new wxStaticLine(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxVERTICAL), wxGBPosition(0, column + 2), wxGBSpan(11, 1), wxALL | wxEXPAND, 5); - - row = 0; - column += 3; - - auto text = new wxStaticText(this, wxID_ANY, _("Left Axis")); - text->SetFont(bold_font); - main_sizer->Add(text, wxGBPosition(row, column), wxGBSpan(1, 3), wxALL | wxEXPAND, 5); - - for (const auto& id : g_kSecondColumnItems) - { - row++; - add_button_row(main_sizer, row, column, id); - } - - row++; - - // input drawer - m_left_draw = new wxInputDraw(this, wxID_ANY, wxDefaultPosition, { 60, 60 }); - main_sizer->Add(m_left_draw, wxGBPosition(row, column + 1), wxGBSpan(2, 1), wxTOP | wxBOTTOM | wxEXPAND | wxALIGN_CENTER, 5); - - main_sizer->Add(new wxStaticLine(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxVERTICAL), wxGBPosition(0, column + 3), wxGBSpan(11, 1), wxALL | wxEXPAND, 5); - - ////////////////////////////////////////////////////////////////// - - row = 0; - column += 4; - - text = new wxStaticText(this, wxID_ANY, _("Right Axis")); - text->SetFont(bold_font); - main_sizer->Add(text, wxGBPosition(row, column), wxGBSpan(1, 3), wxALL | wxEXPAND, 5); - - for (const auto& id : g_kThirdColumnItems) - { - row++; - add_button_row(main_sizer, row, column, id); - } - - row++; - - // input drawer - m_right_draw = new wxInputDraw(this, wxID_ANY, wxDefaultPosition, { 60, 60 }); - main_sizer->Add(m_right_draw, wxGBPosition(row, column + 1), wxGBSpan(2, 1), wxTOP | wxBOTTOM | wxEXPAND | wxALIGN_CENTER, 5); - - // Volume - row = 10; - - text = new wxStaticText(this, wxID_ANY, _("Volume")); - text->Disable(); - main_sizer->Add(text, wxGBPosition(row, column), wxDefaultSpan, wxALL, 5); - - auto*m_volume = new wxSlider(this, wxID_ANY, 0, 0, 100); - m_volume->Disable(); - main_sizer->Add(m_volume, wxGBPosition(row, column + 1), wxDefaultSpan, wxTOP | wxBOTTOM | wxEXPAND, 5); - - const auto volume_text = new wxStaticText(this, wxID_ANY, wxString::Format("%d%%", 0)); - volume_text->Disable(); - main_sizer->Add(volume_text, wxGBPosition(row, column + 2), wxDefaultSpan, wxALL, 5); - m_volume->Bind(wxEVT_SLIDER, &VPADInputPanel::OnVolumeChange, this, wxID_ANY, wxID_ANY, new wxControlObject(volume_text)); - - main_sizer->Add(new wxStaticLine(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxVERTICAL), wxGBPosition(0, column + 3), wxGBSpan(11, 1), wxALL | wxEXPAND, 5); - - ////////////////////////////////////////////////////////////////// - - row = 0; - column += 4; - - text = new wxStaticText(this, wxID_ANY, _("D-pad")); - text->SetFont(bold_font); - main_sizer->Add(text, wxGBPosition(row, column), wxGBSpan(1, 3), wxALL | wxEXPAND, 5); - - for (const auto& id : g_kFourthRowItems) - { - row++; - add_button_row(main_sizer, row, column, id); - } - - // Blow Mic - row = 8; - add_button_row(main_sizer, row, column, VPADController::kButtonId_Mic, _("blow mic")); - row++; - - add_button_row(main_sizer, row, column, VPADController::kButtonId_Screen, _("show screen")); - row++; - - auto toggleScreenText = new wxStaticText(this, wxID_ANY, _("toggle screen")); - main_sizer->Add(toggleScreenText, - wxGBPosition(row, column), - wxDefaultSpan, - wxALL | wxALIGN_CENTER_VERTICAL, 5); - m_togglePadViewCheckBox = new wxCheckBox(this, wxID_ANY, {}, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL); - wxString toggleScreenTT = _("Makes the \"show screen\" button toggle between the TV and gamepad screens"); - m_togglePadViewCheckBox->SetToolTip(toggleScreenTT); - toggleScreenText->SetToolTip(toggleScreenTT); - main_sizer->Add(m_togglePadViewCheckBox, wxGBPosition(row,column+1), wxDefaultSpan, wxALL | wxEXPAND, 5); - - ////////////////////////////////////////////////////////////////// - - SetSizer(main_sizer); - Layout(); -} - -void VPADInputPanel::add_button_row(wxGridBagSizer *sizer, sint32 row, sint32 column, const VPADController::ButtonId &button_id) { - add_button_row(sizer, row, column, button_id, wxGetTranslation(wxString::FromUTF8(VPADController::get_button_name(button_id)))); -} - -void VPADInputPanel::add_button_row(wxGridBagSizer *sizer, sint32 row, sint32 column, - const VPADController::ButtonId &button_id, const wxString &label) { - sizer->Add( - new wxStaticText(this, wxID_ANY, label), - wxGBPosition(row, column), - wxDefaultSpan, - wxALL | wxALIGN_CENTER_VERTICAL, 5); - - auto* text_ctrl = new wxTextCtrl(this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL | wxTE_PROCESS_ENTER | wxTE_PROCESS_TAB); - text_ctrl->SetClientData((void*)button_id); - text_ctrl->SetMinSize(wxSize(150, -1)); - text_ctrl->SetEditable(false); - text_ctrl->SetBackgroundColour(kKeyColourNormalMode); - bind_hotkey_events(text_ctrl); - sizer->Add(text_ctrl, wxGBPosition(row, column + 1), wxDefaultSpan, wxALL | wxEXPAND, 5); -} - -void VPADInputPanel::on_timer(const EmulatedControllerPtr& emulated_controller, const ControllerPtr& controller_base) -{ - InputPanel::on_timer(emulated_controller, controller_base); - - static_cast(emulated_controller.get())->set_screen_toggle(m_togglePadViewCheckBox->GetValue()); - - if(emulated_controller) - { - const auto axis = emulated_controller->get_axis(); - const auto rotation = emulated_controller->get_rotation(); - - m_left_draw->SetAxisValue(axis); - m_right_draw->SetAxisValue(rotation); - } -} - -void VPADInputPanel::OnVolumeChange(wxCommandEvent& event) -{ - -} -void VPADInputPanel::load_controller(const EmulatedControllerPtr& controller) -{ - InputPanel::load_controller(controller); - - const bool isToggle = static_cast(controller.get())->is_screen_active_toggle(); - m_togglePadViewCheckBox->SetValue(isToggle); -} diff --git a/src/gui/wxgui/input/panels/VPADInputPanel.h b/src/gui/wxgui/input/panels/VPADInputPanel.h deleted file mode 100644 index 97b3ad1b..00000000 --- a/src/gui/wxgui/input/panels/VPADInputPanel.h +++ /dev/null @@ -1,25 +0,0 @@ -#pragma once - -#include -#include "wxgui/input/panels/InputPanel.h" - -class wxInputDraw; -class wxCheckBox; - -class VPADInputPanel : public InputPanel -{ -public: - VPADInputPanel(wxWindow* parent); - - void on_timer(const EmulatedControllerPtr& emulated_controller, const ControllerPtr& controller) override; - virtual void load_controller(const EmulatedControllerPtr& controller) override; - -private: - void OnVolumeChange(wxCommandEvent& event); - - wxInputDraw* m_left_draw, * m_right_draw; - wxCheckBox* m_togglePadViewCheckBox; - - void add_button_row(wxGridBagSizer *sizer, sint32 row, sint32 column, const VPADController::ButtonId &button_id); - void add_button_row(wxGridBagSizer *sizer, sint32 row, sint32 column, const VPADController::ButtonId &button_id, const wxString &label); -}; diff --git a/src/gui/wxgui/input/panels/WiimoteInputPanel.cpp b/src/gui/wxgui/input/panels/WiimoteInputPanel.cpp deleted file mode 100644 index da94852c..00000000 --- a/src/gui/wxgui/input/panels/WiimoteInputPanel.cpp +++ /dev/null @@ -1,260 +0,0 @@ -#include "wxgui/input/panels/WiimoteInputPanel.h" - -#include -#include -#include -#include -#include -#include -#include - -#include "wxgui/helpers/wxControlObject.h" -#include "input/emulated/WiimoteController.h" -#include "wxgui/helpers/wxHelpers.h" -#include "wxgui/components/wxInputDraw.h" - -constexpr WiimoteController::ButtonId g_kFirstColumnItems[] = -{ - WiimoteController::kButtonId_A, WiimoteController::kButtonId_B, WiimoteController::kButtonId_1, WiimoteController::kButtonId_2, WiimoteController::kButtonId_Plus, WiimoteController::kButtonId_Minus, WiimoteController::kButtonId_Home -}; - -constexpr WiimoteController::ButtonId g_kSecondColumnItems[] = -{ - WiimoteController::kButtonId_Up, WiimoteController::kButtonId_Down, WiimoteController::kButtonId_Left, WiimoteController::kButtonId_Right -}; - -constexpr WiimoteController::ButtonId g_kThirdColumnItems[] = -{ - WiimoteController::kButtonId_Nunchuck_C, WiimoteController::kButtonId_Nunchuck_Z, - WiimoteController::kButtonId_None, - WiimoteController::kButtonId_Nunchuck_Up,WiimoteController::kButtonId_Nunchuck_Down,WiimoteController::kButtonId_Nunchuck_Left,WiimoteController::kButtonId_Nunchuck_Right -}; - -WiimoteInputPanel::WiimoteInputPanel(wxWindow* parent) - : InputPanel(parent) -{ - auto bold_font = GetFont(); - bold_font.MakeBold(); - - auto* main_sizer = new wxBoxSizer(wxVERTICAL); - auto* horiz_main_sizer = new wxBoxSizer(wxHORIZONTAL); - - auto* extensions_sizer = new wxBoxSizer(wxHORIZONTAL); - horiz_main_sizer->Add(extensions_sizer, wxSizerFlags(0).Align(wxALIGN_CENTER_VERTICAL)); - - extensions_sizer->Add(new wxStaticText(this, wxID_ANY, _("Extensions:"))); - extensions_sizer->AddSpacer(10); - - m_motion_plus = new wxCheckBox(this, wxID_ANY, _("MotionPlus")); - m_motion_plus->Bind(wxEVT_CHECKBOX, &WiimoteInputPanel::on_extension_change, this); - extensions_sizer->Add(m_motion_plus); - - m_nunchuck = new wxCheckBox(this, wxID_ANY, _("Nunchuck")); - m_nunchuck->Bind(wxEVT_CHECKBOX, &WiimoteInputPanel::on_extension_change, this); - extensions_sizer->Add(m_nunchuck); - - m_classic = new wxCheckBox(this, wxID_ANY, _("Classic")); - m_classic->Bind(wxEVT_CHECKBOX, &WiimoteInputPanel::on_extension_change, this); - m_classic->Hide(); - extensions_sizer->Add(m_classic); - - main_sizer->Add(horiz_main_sizer, 0, wxEXPAND | wxALL, 5); - main_sizer->Add(new wxStaticLine(this), 0, wxLEFT | wxRIGHT | wxTOP | wxEXPAND, 5); - - m_item_sizer = new wxGridBagSizer(); - - sint32 row = 0; - sint32 column = 0; - for (const auto& id : g_kFirstColumnItems) - { - row++; - add_button_row(row, column, id); - } - - m_item_sizer->Add(new wxStaticLine(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxVERTICAL), wxGBPosition(0, column + 2), wxGBSpan(11, 1), wxLEFT | wxRIGHT | wxBOTTOM | wxEXPAND, 5); - - ////////////////////////////////////////////////////////////////// - - row = 0; - column += 3; - - auto text = new wxStaticText(this, wxID_ANY, _("D-pad")); - text->SetFont(bold_font); - m_item_sizer->Add(text, wxGBPosition(row, column), wxGBSpan(1, 3), wxALL | wxEXPAND, 5); - - for (const auto& id : g_kSecondColumnItems) - { - row++; - add_button_row(row, column, id); - } - - row = 8; - // Volume - text = new wxStaticText(this, wxID_ANY, _("Volume")); - text->Disable(); - m_item_sizer->Add(text, wxGBPosition(row, column), wxDefaultSpan, wxALL, 5); - - m_volume = new wxSlider(this, wxID_ANY, 0, 0, 100); - m_volume->Disable(); - m_item_sizer->Add(m_volume, wxGBPosition(row, column + 1), wxDefaultSpan, wxTOP | wxBOTTOM | wxEXPAND, 5); - - const auto volume_text = new wxStaticText(this, wxID_ANY, wxString::Format("%d%%", 0)); - volume_text->Disable(); - m_item_sizer->Add(volume_text, wxGBPosition(row, column + 2), wxDefaultSpan, wxALL, 5); - m_volume->Bind(wxEVT_SLIDER, &WiimoteInputPanel::on_volume_change, this, wxID_ANY, wxID_ANY, new wxControlObject(volume_text)); - row++; - - m_item_sizer->Add(new wxStaticLine(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxVERTICAL), wxGBPosition(0, column + 3), wxGBSpan(11, 1), wxLEFT | wxRIGHT | wxBOTTOM | wxEXPAND, 5); - - ////////////////////////////////////////////////////////////////// - - row = 0; - column += 4; - - text = new wxStaticText(this, wxID_ANY, _("Nunchuck")); - text->SetFont(bold_font); - m_item_sizer->Add(text, wxGBPosition(row, column), wxGBSpan(1, 3), wxALL | wxEXPAND, 5); - - for (const auto& id : g_kThirdColumnItems) - { - row++; - if (id == WiimoteController::kButtonId_None) - continue; - - m_item_sizer->Add( - new wxStaticText(this, wxID_ANY, wxGetTranslation(wxString::FromUTF8(WiimoteController::get_button_name(id)))), - wxGBPosition(row, column), - wxDefaultSpan, - wxALL | wxALIGN_CENTER_VERTICAL, 5); - - auto* text_ctrl = new wxTextCtrl(this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL | wxTE_PROCESS_ENTER | wxTE_PROCESS_TAB); - text_ctrl->SetClientData((void*)id); - text_ctrl->SetMinSize(wxSize(150, -1)); - text_ctrl->SetEditable(false); - text_ctrl->SetBackgroundColour(kKeyColourNormalMode); - bind_hotkey_events(text_ctrl); - text_ctrl->Enable(m_nunchuck->GetValue()); - m_item_sizer->Add(text_ctrl, wxGBPosition(row, column + 1), wxDefaultSpan, wxALL | wxEXPAND, 5); - - m_nunchuck_items.push_back(text_ctrl); - } - - - // input drawer - m_draw = new wxInputDraw(this, wxID_ANY, wxDefaultPosition, { 60, 60 }); - m_draw->Enable(m_nunchuck->GetValue()); - m_item_sizer->Add(5, 0, wxGBPosition(3, column + 3), wxDefaultSpan, wxTOP | wxBOTTOM | wxEXPAND | wxALIGN_CENTER, 5); - m_item_sizer->Add(m_draw, wxGBPosition(3, column + 4), wxGBSpan(4, 1), wxTOP | wxBOTTOM | wxEXPAND | wxALIGN_CENTER, 5); - - m_nunchuck_items.push_back(m_draw); - - ////////////////////////////////////////////////////////////////// - - main_sizer->Add(m_item_sizer, 1, wxEXPAND | wxLEFT | wxRIGHT | wxTOP, 5); - - SetSizer(main_sizer); - Layout(); -} - -void WiimoteInputPanel::add_button_row(sint32 row, sint32 column, const WiimoteController::ButtonId &button_id) { - m_item_sizer->Add( - new wxStaticText(this, wxID_ANY, wxGetTranslation(wxString::FromUTF8(WiimoteController::get_button_name(button_id)))), - wxGBPosition(row, column), - wxDefaultSpan, - wxALL | wxALIGN_CENTER_VERTICAL, 5); - - auto* text_ctrl = new wxTextCtrl(this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL | wxTE_PROCESS_ENTER | wxTE_PROCESS_TAB); - text_ctrl->SetClientData((void*)button_id); - text_ctrl->SetMinSize(wxSize(150, -1)); - text_ctrl->SetEditable(false); - text_ctrl->SetBackgroundColour(kKeyColourNormalMode); - bind_hotkey_events(text_ctrl); - m_item_sizer->Add(text_ctrl, wxGBPosition(row, column + 1), wxDefaultSpan, wxALL | wxEXPAND, 5); -} - -void WiimoteInputPanel::set_active_device_type(WPADDeviceType type) -{ - m_device_type = type; - - m_motion_plus->SetValue(type == kWAPDevMPLS || type == kWAPDevMPLSFreeStyle || type == kWAPDevMPLSClassic); - switch(type) - { - case kWAPDevFreestyle: - case kWAPDevMPLSFreeStyle: - m_nunchuck->SetValue(true); - m_classic->SetValue(false); - for (const auto& item : m_nunchuck_items) - { - item->Enable(true); - } - break; - - case kWAPDevClassic: - case kWAPDevMPLSClassic: - m_nunchuck->SetValue(false); - m_classic->SetValue(true); - for (const auto& item : m_nunchuck_items) - { - item->Enable(false); - } - break; - - default: - m_nunchuck->SetValue(false); - m_classic->SetValue(false); - for (const auto& item : m_nunchuck_items) - { - item->Enable(false); - } - } -} - -void WiimoteInputPanel::on_volume_change(wxCommandEvent& event) -{ -} - -void WiimoteInputPanel::on_extension_change(wxCommandEvent& event) -{ - if(m_motion_plus->GetValue() && m_nunchuck->GetValue()) - set_active_device_type(kWAPDevMPLSFreeStyle); - else if(m_motion_plus->GetValue() && m_classic->GetValue()) - set_active_device_type(kWAPDevMPLSClassic); - else if (m_motion_plus->GetValue()) - set_active_device_type(kWAPDevMPLS); - else if (m_nunchuck->GetValue()) - set_active_device_type(kWAPDevFreestyle); - else if (m_classic->GetValue()) - set_active_device_type(kWAPDevClassic); - else - set_active_device_type(kWAPDevCore); -} - -void WiimoteInputPanel::on_timer(const EmulatedControllerPtr& emulated_controller, const ControllerPtr& controller) -{ - if (emulated_controller) - { - const auto wiimote = std::dynamic_pointer_cast(emulated_controller); - wxASSERT(wiimote); - - wiimote->set_device_type(m_device_type); - } - - InputPanel::on_timer(emulated_controller, controller); - - if (emulated_controller) - { - const auto axis = emulated_controller->get_axis(); - m_draw->SetAxisValue(axis); - } -} - -void WiimoteInputPanel::load_controller(const EmulatedControllerPtr& emulated_controller) -{ - InputPanel::load_controller(emulated_controller); - - if (emulated_controller) { - const auto wiimote = std::dynamic_pointer_cast(emulated_controller); - wxASSERT(wiimote); - set_active_device_type(wiimote->get_device_type()); - } -} diff --git a/src/gui/wxgui/input/panels/WiimoteInputPanel.h b/src/gui/wxgui/input/panels/WiimoteInputPanel.h deleted file mode 100644 index 946f7cd5..00000000 --- a/src/gui/wxgui/input/panels/WiimoteInputPanel.h +++ /dev/null @@ -1,44 +0,0 @@ -#pragma once - -#include "wxgui/input/panels/InputPanel.h" -#include "input/emulated/WiimoteController.h" -#include - -class wxCheckBox; -class wxGridBagSizer; -class wxInputDraw; - -class WiimoteInputPanel : public InputPanel -{ -public: - WiimoteInputPanel(wxWindow* parent); - - void on_timer(const EmulatedControllerPtr& emulated_controller, const ControllerPtr& controller) override; - - void load_controller(const EmulatedControllerPtr& emulated_controller) override; - -private: - wxInputDraw* m_draw; - - WPADDeviceType m_device_type = kWAPDevCore; - void set_active_device_type(WPADDeviceType type); - - void on_volume_change(wxCommandEvent& event); - void on_extension_change(wxCommandEvent& event); - void on_pair_button(wxCommandEvent& event); - - wxGridBagSizer* m_item_sizer; - - wxCheckBox* m_nunchuck, * m_classic; - wxCheckBox* m_motion_plus; - - wxSlider* m_volume; - - std::vector m_nunchuck_items; - - void add_button_row(sint32 row, sint32 column, const WiimoteController::ButtonId &button_id); -}; - - - - diff --git a/src/gui/wxgui/input/settings/DefaultControllerSettings.cpp b/src/gui/wxgui/input/settings/DefaultControllerSettings.cpp deleted file mode 100644 index c10c15ce..00000000 --- a/src/gui/wxgui/input/settings/DefaultControllerSettings.cpp +++ /dev/null @@ -1,318 +0,0 @@ -#include "wxgui/input/settings/DefaultControllerSettings.h" - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "wxgui/helpers/wxControlObject.h" -#include "wxgui/helpers/wxHelpers.h" -#include "wxgui/components/wxInputDraw.h" -#include "wxgui/input/InputAPIAddWindow.h" - - -DefaultControllerSettings::DefaultControllerSettings(wxWindow* parent, const wxPoint& position, std::shared_ptr controller) - : wxDialog(parent, wxID_ANY, _("Controller settings"), position, wxDefaultSize, - wxDEFAULT_DIALOG_STYLE), m_controller(std::move(controller)) -{ - m_settings = m_controller->get_settings(); - m_rumble_backup = m_settings.rumble; - - this->SetSizeHints(wxDefaultSize, wxDefaultSize); - - auto* sizer = new wxBoxSizer(wxVERTICAL); - - // options - { - auto* box = new wxStaticBox(this, wxID_ANY, _("Options")); - auto* box_sizer = new wxStaticBoxSizer(box, wxVERTICAL); - - // Motion - m_use_motion = new wxCheckBox(box, wxID_ANY, _("Use motion")); - m_use_motion->SetValue(m_settings.motion); - m_use_motion->Enable(m_controller->has_motion()); - box_sizer->Add(m_use_motion, 0, wxEXPAND | wxALL, 5); - - // Vibration - auto* rumbleSizer = new wxBoxSizer(wxHORIZONTAL); - - const auto rumble = (int)(m_settings.rumble * 100); - rumbleSizer->Add(new wxStaticText(box, wxID_ANY, _("Rumble")), 0, wxALL | wxALIGN_CENTER_VERTICAL, 5); - m_rumble = new wxSlider(box, wxID_ANY, rumble, 0, 100); - rumbleSizer->Add(m_rumble, 1, wxALL | wxEXPAND, 5); - - const auto text = new wxStaticText(box, wxID_ANY, wxString::Format("%d%%", rumble)); - rumbleSizer->Add(text, 0, wxALL | wxALIGN_CENTER_VERTICAL, 5); - m_rumble->Bind(wxEVT_SLIDER, &DefaultControllerSettings::on_rumble_change, this, wxID_ANY, wxID_ANY, new wxControlObject(text)); - - box_sizer->Add(rumbleSizer); - - sizer->Add(box_sizer, 1, wxALL|wxEXPAND, 5); - } - - auto* row_sizer = new wxBoxSizer(wxHORIZONTAL); - // Axis - { - auto* box = new wxStaticBox(this, wxID_ANY, _("Axis")); - auto* box_sizer = new wxStaticBoxSizer(box, wxVERTICAL); - - { - auto* content_sizer = new wxFlexGridSizer(0, 3, 0, 0); - - // Deadzone - const auto deadzone = (int)(m_settings.axis.deadzone * 100); - content_sizer->Add(new wxStaticText(box, wxID_ANY, _("Deadzone")), 0, wxALL | wxALIGN_CENTER_VERTICAL, 5); - m_axis_deadzone = new wxSlider(box, wxID_ANY, deadzone, 0, 100); - content_sizer->Add(m_axis_deadzone, 1, wxALL | wxEXPAND, 5); - - const auto deadzone_text = new wxStaticText(box, wxID_ANY, wxString::Format("%d%%", deadzone)); - content_sizer->Add(deadzone_text, 0, wxALL | wxALIGN_CENTER_VERTICAL, 5); - - m_axis_deadzone->Bind(wxEVT_SLIDER, &DefaultControllerSettings::on_deadzone_change, this, wxID_ANY, wxID_ANY, - new wxControlObject(deadzone_text)); - - - // Range - const auto range = (int)(m_settings.axis.range * 100); - content_sizer->Add(new wxStaticText(box, wxID_ANY, _("Range")), 0, wxALL | wxALIGN_CENTER_VERTICAL, 5); - m_axis_range = new wxSlider(box, wxID_ANY, range, 50, 200); - content_sizer->Add(m_axis_range, 1, wxALL | wxEXPAND, 5); - - const auto range_text = new wxStaticText(box, wxID_ANY, wxString::Format("%d%%", range)); - content_sizer->Add(range_text, 0, wxALL | wxALIGN_CENTER_VERTICAL, 5); - - m_axis_range->Bind(wxEVT_SLIDER, &DefaultControllerSettings::on_range_change, this, wxID_ANY, wxID_ANY, - new wxControlObject(range_text)); - - content_sizer->AddSpacer(1); - m_axis_draw = new wxInputDraw(box, wxID_ANY, wxDefaultPosition, { 60, 60 }); - content_sizer->Add(m_axis_draw, 0, wxTOP | wxBOTTOM | wxALIGN_CENTER, 5); - - box_sizer->Add(content_sizer, 1, wxEXPAND, 0); - } - - row_sizer->Add(box_sizer, 0, wxALL | wxEXPAND, 5); - } - - // Rotation - { - auto* box = new wxStaticBox(this, wxID_ANY, _("Rotation")); - auto* box_sizer = new wxStaticBoxSizer(box, wxVERTICAL); - - { - auto* content_sizer = new wxFlexGridSizer(0, 3, 0, 0); - - // Deadzone - const auto deadzone = (int)(m_settings.rotation.deadzone * 100); - content_sizer->Add(new wxStaticText(box, wxID_ANY, _("Deadzone")), 0, wxALL | wxALIGN_CENTER_VERTICAL, 5); - m_rotation_deadzone = new wxSlider(box, wxID_ANY, deadzone, 0, 100); - content_sizer->Add(m_rotation_deadzone, 1, wxALL | wxEXPAND, 5); - - const auto deadzone_text = new wxStaticText(box, wxID_ANY, wxString::Format("%d%%", deadzone)); - content_sizer->Add(deadzone_text, 0, wxALL | wxALIGN_CENTER_VERTICAL, 5); - - m_rotation_deadzone->Bind(wxEVT_SLIDER, &DefaultControllerSettings::on_deadzone_change, this, wxID_ANY, wxID_ANY, - new wxControlObject(deadzone_text)); - - - // Range - const auto range = (int)(m_settings.rotation.range * 100); - content_sizer->Add(new wxStaticText(box, wxID_ANY, _("Range")), 0, wxALL | wxALIGN_CENTER_VERTICAL, 5); - m_rotation_range = new wxSlider(box, wxID_ANY, range, 50, 200); - content_sizer->Add(m_rotation_range, 1, wxALL | wxEXPAND, 5); - - const auto range_text = new wxStaticText(box, wxID_ANY, wxString::Format("%d%%", range)); - content_sizer->Add(range_text, 0, wxALL | wxALIGN_CENTER_VERTICAL, 5); - - m_rotation_range->Bind(wxEVT_SLIDER, &DefaultControllerSettings::on_range_change, this, wxID_ANY, wxID_ANY, - new wxControlObject(range_text)); - - content_sizer->AddSpacer(1); - m_rotation_draw = new wxInputDraw(box, wxID_ANY, wxDefaultPosition, { 60, 60 }); - content_sizer->Add(m_rotation_draw, 0, wxTOP | wxBOTTOM | wxALIGN_CENTER, 5); - - box_sizer->Add(content_sizer, 1, wxEXPAND, 0); - } - - row_sizer->Add(box_sizer, 0, wxALL | wxEXPAND, 5); - } - - // Trigger - { - auto* box = new wxStaticBox(this, wxID_ANY, _("Trigger")); - auto* box_sizer = new wxStaticBoxSizer(box, wxVERTICAL); - - { - auto* content_sizer = new wxFlexGridSizer(0, 3, 0, 0); - - // Deadzone - const auto deadzone = (int)(m_settings.trigger.deadzone * 100); - content_sizer->Add(new wxStaticText(box, wxID_ANY, _("Deadzone")), 0, wxALL | wxALIGN_CENTER_VERTICAL, 5); - m_trigger_deadzone = new wxSlider(box, wxID_ANY, deadzone, 0, 100); - content_sizer->Add(m_trigger_deadzone, 1, wxALL | wxEXPAND, 5); - - const auto deadzone_text = new wxStaticText(box, wxID_ANY, wxString::Format("%d%%", deadzone)); - content_sizer->Add(deadzone_text, 0, wxALL | wxALIGN_CENTER_VERTICAL, 5); - - m_trigger_deadzone->Bind(wxEVT_SLIDER, &DefaultControllerSettings::on_deadzone_change, this, wxID_ANY, wxID_ANY, - new wxControlObject(deadzone_text)); - - - // Range - const auto range = (int)(m_settings.trigger.range * 100); - content_sizer->Add(new wxStaticText(box, wxID_ANY, _("Range")), 0, wxALL | wxALIGN_CENTER_VERTICAL, 5); - m_trigger_range = new wxSlider(box, wxID_ANY, range, 50, 200); - content_sizer->Add(m_trigger_range, 1, wxALL | wxEXPAND, 5); - - const auto range_text = new wxStaticText(box, wxID_ANY, wxString::Format("%d%%", range)); - content_sizer->Add(range_text, 0, wxALL | wxALIGN_CENTER_VERTICAL, 5); - - m_trigger_range->Bind(wxEVT_SLIDER, &DefaultControllerSettings::on_range_change, this, wxID_ANY, wxID_ANY, - new wxControlObject(range_text)); - - content_sizer->AddSpacer(1); - m_trigger_draw = new wxInputDraw(box, wxID_ANY, wxDefaultPosition, { 60, 60 }); - content_sizer->Add(m_trigger_draw, 0, wxTOP | wxBOTTOM | wxALIGN_CENTER, 5); - - box_sizer->Add(content_sizer, 1, wxEXPAND, 0); - } - - row_sizer->Add(box_sizer, 0, wxALL | wxEXPAND, 5); - } - sizer->Add(row_sizer); - - { - auto* control_sizer = new wxFlexGridSizer(0, 4, 0, 0); - control_sizer->AddGrowableCol(3); - - auto* ok_button = new wxButton(this, wxID_ANY, _("OK")); - ok_button->Bind(wxEVT_BUTTON, [this](auto&) { update_settings(); EndModal(wxID_OK); }); - control_sizer->Add(ok_button, 0, wxALL, 5); - - control_sizer->Add(0, 0, 0, wxEXPAND, 5); - - auto* cancel_button = new wxButton(this, wxID_ANY, _("Cancel")); - cancel_button->Bind(wxEVT_BUTTON, [this](auto&) { EndModal(wxID_CANCEL); }); - control_sizer->Add(cancel_button, 0, wxALL, 5); - - auto* calibrate_button = new wxButton(this, wxID_ANY, _("Calibrate")); - calibrate_button->Bind(wxEVT_BUTTON, [this](auto&) { m_controller->calibrate(); }); - control_sizer->Add(calibrate_button, 0, wxALL | wxALIGN_RIGHT, 5); - - sizer->Add(control_sizer, 0, wxEXPAND, 5); - } - - - - this->SetSizer(sizer); - this->Layout(); - this->Fit(); - - this->Bind(wxEVT_CLOSE_WINDOW, &DefaultControllerSettings::on_close, this); - - m_timer = new wxTimer(this); - Bind(wxEVT_TIMER, &DefaultControllerSettings::on_timer, this); - m_timer->Start(100); -} - -DefaultControllerSettings::~DefaultControllerSettings() -{ - m_controller->stop_rumble(); - m_timer->Stop(); -} - -void DefaultControllerSettings::update_settings() -{ - // update settings - m_controller->set_settings(m_settings); - if (m_use_motion) - m_controller->set_use_motion(m_use_motion->GetValue()); -} - -void DefaultControllerSettings::on_timer(wxTimerEvent& event) -{ - if (m_rumble_time.has_value() && std::chrono::duration_cast(std::chrono::steady_clock::now() - m_rumble_time.value()).count() > 500 ) - { - m_controller->stop_rumble(); - m_rumble_time = {}; - } - - const auto& default_state = m_controller->get_default_state(); - - auto state = m_controller->raw_state(); - m_controller->apply_axis_setting(state.axis, default_state.axis, m_settings.axis); - m_controller->apply_axis_setting(state.rotation, default_state.rotation, m_settings.rotation); - m_controller->apply_axis_setting(state.trigger, default_state.trigger, m_settings.trigger); - - if (m_controller->api() == InputAPI::SDLController) - { - state.axis.y *= -1; - state.rotation.y *= -1; - } - - m_axis_draw->SetDeadzone(m_settings.axis.deadzone); - m_axis_draw->SetAxisValue(state.axis); - - m_rotation_draw->SetDeadzone(m_settings.rotation.deadzone); - m_rotation_draw->SetAxisValue(state.rotation); - - m_trigger_draw->SetDeadzone(m_settings.trigger.deadzone); - m_trigger_draw->SetAxisValue(state.trigger); -} - -void DefaultControllerSettings::on_close(wxCloseEvent& event) -{ - if (this->GetReturnCode() == 0 || this->GetReturnCode() == wxID_OK) - update_settings(); - - event.Skip(); -} - -void DefaultControllerSettings::on_deadzone_change(wxCommandEvent& event) -{ - update_slider_text(event); - const auto new_value = (float)event.GetInt() / 100.0f; - - if (event.GetEventObject() == m_axis_deadzone) - m_settings.axis.deadzone = new_value; - else if (event.GetEventObject() == m_rotation_deadzone) - m_settings.rotation.deadzone = new_value; - else if (event.GetEventObject() == m_trigger_deadzone) - m_settings.trigger.deadzone = new_value; -} - -void DefaultControllerSettings::on_range_change(wxCommandEvent& event) -{ - update_slider_text(event); - - const auto new_value = (float)event.GetInt() / 100.0f; - - if (event.GetEventObject() == m_axis_range) - m_settings.axis.range = new_value; - else if (event.GetEventObject() == m_rotation_range) - m_settings.rotation.range = new_value; - else if (event.GetEventObject() == m_trigger_range) - m_settings.trigger.range = new_value; -} - -void DefaultControllerSettings::on_rumble_change(wxCommandEvent& event) -{ - update_slider_text(event); - - const auto rumble_value = event.GetInt(); - m_settings.rumble = (float)rumble_value / 100.0f; - - m_controller->set_rumble(m_settings.rumble); - if (rumble_value != 0) - m_controller->start_rumble(); - else - m_controller->stop_rumble(); - - m_controller->set_rumble(m_rumble_backup); - - m_rumble_time = std::chrono::steady_clock::now(); -} diff --git a/src/gui/wxgui/input/settings/DefaultControllerSettings.h b/src/gui/wxgui/input/settings/DefaultControllerSettings.h deleted file mode 100644 index a5cb76ef..00000000 --- a/src/gui/wxgui/input/settings/DefaultControllerSettings.h +++ /dev/null @@ -1,42 +0,0 @@ -#pragma once - -#include -#include -#include - -#include "input/api/Controller.h" - -class wxCheckBox; -class wxInputDraw; - -class DefaultControllerSettings : public wxDialog -{ -public: - DefaultControllerSettings(wxWindow* parent, const wxPoint& position, ControllerPtr controller); - ~DefaultControllerSettings(); - -private: - void update_settings(); - - ControllerPtr m_controller; - ControllerBase::Settings m_settings; - float m_rumble_backup; - - wxTimer* m_timer; - std::optional m_rumble_time{}; - - wxSlider* m_axis_deadzone, *m_axis_range; - wxSlider* m_rotation_deadzone, *m_rotation_range; - wxSlider* m_trigger_deadzone, *m_trigger_range; - wxSlider* m_rumble; - - wxCheckBox* m_use_motion = nullptr; - - wxInputDraw* m_axis_draw, * m_rotation_draw, *m_trigger_draw; - - void on_timer(wxTimerEvent& event); - void on_close(wxCloseEvent& event); - void on_deadzone_change(wxCommandEvent& event); - void on_range_change(wxCommandEvent& event); - void on_rumble_change(wxCommandEvent& event); -}; diff --git a/src/gui/wxgui/input/settings/WiimoteControllerSettings.cpp b/src/gui/wxgui/input/settings/WiimoteControllerSettings.cpp deleted file mode 100644 index 320a895a..00000000 --- a/src/gui/wxgui/input/settings/WiimoteControllerSettings.cpp +++ /dev/null @@ -1,369 +0,0 @@ -#include "wxgui/input/settings/WiimoteControllerSettings.h" - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "wxgui/helpers/wxControlObject.h" -#include "wxgui/helpers/wxHelpers.h" -#include "wxgui/components/wxInputDraw.h" -#include "wxgui/input/InputAPIAddWindow.h" - -#ifdef SUPPORTS_WIIMOTE - -WiimoteControllerSettings::WiimoteControllerSettings(wxWindow* parent, const wxPoint& position, std::shared_ptr controller) - : wxDialog(parent, wxID_ANY, _("Controller settings"), position, wxDefaultSize, - wxDEFAULT_DIALOG_STYLE), m_controller(std::move(controller)) -{ - m_settings = m_controller->get_settings(); - m_rumble_backup = m_settings.rumble; - m_packet_delay_backup = m_controller->get_packet_delay(); - - this->SetSizeHints(wxDefaultSize, wxDefaultSize); - - auto* sizer = new wxBoxSizer(wxVERTICAL); - - // extension info - { - auto* box = new wxStaticBox(this, wxID_ANY, _("Connected extension")); - auto* box_sizer = new wxStaticBoxSizer(box, wxVERTICAL); - - m_extension_text = new wxStaticText(box, wxID_ANY, _("None")); - box_sizer->Add(m_extension_text, 0, wxALL, 5); - - sizer->Add(box_sizer, 0, wxALL | wxEXPAND, 5); - } - - // options - { - auto* box = new wxStaticBox(this, wxID_ANY, _("Options")); - auto* box_sizer = new wxStaticBoxSizer(box, wxVERTICAL); - - { - auto* row_sizer = new wxBoxSizer(wxHORIZONTAL); - // Rumble - m_rumble = new wxCheckBox(box, wxID_ANY, _("Rumble")); - m_rumble->SetValue(m_settings.rumble > 0); - row_sizer->Add(m_rumble, 0, wxALL, 5); - - m_rumble->Bind(wxEVT_CHECKBOX, &WiimoteControllerSettings::on_rumble_change, this); - - // Motion - m_use_motion = new wxCheckBox(box, wxID_ANY, _("Use motion")); - m_use_motion->SetValue(m_settings.motion); - m_use_motion->Enable(m_controller->has_motion()); - row_sizer->Add(m_use_motion, 0, wxALL, 5); - - box_sizer->Add(row_sizer); - } - { - auto* row_sizer = new wxBoxSizer(wxHORIZONTAL); - - // Delay - row_sizer->Add(new wxStaticText(box, wxID_ANY, _("Packet delay")), 0, wxALL | wxALIGN_CENTER_VERTICAL, 5); - m_package_delay = new wxSlider(box, wxID_ANY, m_packet_delay_backup, 1, 100); - row_sizer->Add(m_package_delay, 1, wxALL | wxEXPAND, 5); - - const auto range_text = new wxStaticText(box, wxID_ANY, wxString::Format("%dms", m_packet_delay_backup)); - row_sizer->Add(range_text, 0, wxALL | wxALIGN_CENTER_VERTICAL, 5); - - m_package_delay->Bind(wxEVT_SLIDER, &WiimoteControllerSettings::on_delay_change, this, wxID_ANY, wxID_ANY, - new wxControlObject(range_text)); - - box_sizer->Add(row_sizer); - } - - sizer->Add(box_sizer, 0, wxALL | wxEXPAND, 5); - } - - // Nunchuck - { - m_nunchuck_settings = new wxStaticBox(this, wxID_ANY, _("Nunchuck")); - auto* box_sizer = new wxStaticBoxSizer(m_nunchuck_settings, wxVERTICAL); - - { - auto* content_sizer = new wxFlexGridSizer(0, 3, 0, 0); - - // Deadzone - const auto deadzone = (int)(m_settings.axis.deadzone * 100); - content_sizer->Add(new wxStaticText(m_nunchuck_settings, wxID_ANY, _("Deadzone")), 0, wxALL | wxALIGN_CENTER_VERTICAL, 5); - m_nunchuck_deadzone = new wxSlider(m_nunchuck_settings, wxID_ANY, deadzone, 0, 100); - content_sizer->Add(m_nunchuck_deadzone, 1, wxALL | wxEXPAND, 5); - - const auto deadzone_text = new wxStaticText(m_nunchuck_settings, wxID_ANY, wxString::Format("%d%%", deadzone)); - content_sizer->Add(deadzone_text, 0, wxALL | wxALIGN_CENTER_VERTICAL, 5); - - m_nunchuck_deadzone->Bind(wxEVT_SLIDER, &WiimoteControllerSettings::on_slider_change, this, wxID_ANY, wxID_ANY, - new wxControlObject(deadzone_text)); - - - // Range - const auto range = (int)(m_settings.axis.range * 100); - content_sizer->Add(new wxStaticText(m_nunchuck_settings, wxID_ANY, _("Range")), 0, wxALL | wxALIGN_CENTER_VERTICAL, 5); - m_nunchuck_range = new wxSlider(m_nunchuck_settings, wxID_ANY, range, 50, 200); - content_sizer->Add(m_nunchuck_range, 1, wxALL | wxEXPAND, 5); - - const auto range_text = new wxStaticText(m_nunchuck_settings, wxID_ANY, wxString::Format("%d%%", range)); - content_sizer->Add(range_text, 0, wxALL | wxALIGN_CENTER_VERTICAL, 5); - - m_nunchuck_range->Bind(wxEVT_SLIDER, &WiimoteControllerSettings::on_slider_change, this, wxID_ANY, wxID_ANY, - new wxControlObject(range_text)); - - content_sizer->AddSpacer(1); - m_nunchuck_draw = new wxInputDraw(m_nunchuck_settings, wxID_ANY, wxDefaultPosition, { 60, 60 }); - content_sizer->Add(m_nunchuck_draw, 0, wxTOP | wxBOTTOM | wxALIGN_CENTER, 5); - - box_sizer->Add(content_sizer, 1, wxEXPAND, 0); - } - - sizer->Add(box_sizer, 0, wxALL | wxEXPAND, 5); - } - - // Classic - { - m_classic_settings = new wxStaticBox(this, wxID_ANY, _("Classic")); - auto* box_sizer = new wxStaticBoxSizer(m_classic_settings, wxVERTICAL); - - { - auto* content_sizer = new wxFlexGridSizer(0, 6, 0, 0); - - // Deadzone - { - // Axis - const auto deadzone = (int)(m_settings.axis.deadzone * 100); - content_sizer->Add(new wxStaticText(m_classic_settings, wxID_ANY, _("Deadzone")), 0, wxALL | wxALIGN_CENTER_VERTICAL, 5); - m_classic_axis_deadzone = new wxSlider(m_classic_settings, wxID_ANY, deadzone, 0, 100); - content_sizer->Add(m_classic_axis_deadzone, 1, wxALL | wxEXPAND, 5); - - const auto deadzone_text = new wxStaticText(m_classic_settings, wxID_ANY, wxString::Format("%d%%", deadzone)); - content_sizer->Add(deadzone_text, 0, wxALL | wxALIGN_CENTER_VERTICAL, 5); - - m_classic_axis_deadzone->Bind(wxEVT_SLIDER, &WiimoteControllerSettings::on_slider_change, this, wxID_ANY, wxID_ANY, - new wxControlObject(deadzone_text)); - } - { - // Range - const auto deadzone = (int)(m_settings.rotation.deadzone * 100); - content_sizer->Add(new wxStaticText(m_classic_settings, wxID_ANY, _("Deadzone")), 0, wxALL | wxALIGN_CENTER_VERTICAL, 5); - m_classic_rotation_deadzone = new wxSlider(m_classic_settings, wxID_ANY, deadzone, 0, 100); - content_sizer->Add(m_classic_rotation_deadzone, 1, wxALL | wxEXPAND, 5); - - const auto deadzone_text = new wxStaticText(m_classic_settings, wxID_ANY, wxString::Format("%d%%", deadzone)); - content_sizer->Add(deadzone_text, 0, wxALL | wxALIGN_CENTER_VERTICAL, 5); - - m_classic_rotation_deadzone->Bind(wxEVT_SLIDER, &WiimoteControllerSettings::on_slider_change, this, wxID_ANY, wxID_ANY, - new wxControlObject(deadzone_text)); - } - - // Range - { - // Axis - const auto range = (int)(m_settings.axis.range * 100); - content_sizer->Add(new wxStaticText(m_classic_settings, wxID_ANY, _("Range")), 0, wxALL | wxALIGN_CENTER_VERTICAL, 5); - m_classic_axis_range = new wxSlider(m_classic_settings, wxID_ANY, range, 50, 200); - content_sizer->Add(m_classic_axis_range, 1, wxALL | wxEXPAND, 5); - - const auto range_text = new wxStaticText(m_classic_settings, wxID_ANY, wxString::Format("%d%%", range)); - content_sizer->Add(range_text, 0, wxALL | wxALIGN_CENTER_VERTICAL, 5); - - m_classic_axis_range->Bind(wxEVT_SLIDER, &WiimoteControllerSettings::on_slider_change, this, wxID_ANY, wxID_ANY, - new wxControlObject(range_text)); - } - { - // Rotation - const auto range = (int)(m_settings.rotation.range * 100); - content_sizer->Add(new wxStaticText(m_classic_settings, wxID_ANY, _("Range")), 0, wxALL | wxALIGN_CENTER_VERTICAL, 5); - m_classic_rotation_range = new wxSlider(m_classic_settings, wxID_ANY, range, 50, 200); - content_sizer->Add(m_classic_rotation_range, 1, wxALL | wxEXPAND, 5); - - const auto range_text = new wxStaticText(m_classic_settings, wxID_ANY, wxString::Format("%d%%", range)); - content_sizer->Add(range_text, 0, wxALL | wxALIGN_CENTER_VERTICAL, 5); - - m_classic_rotation_range->Bind(wxEVT_SLIDER, &WiimoteControllerSettings::on_slider_change, this, wxID_ANY, wxID_ANY, - new wxControlObject(range_text)); - } - - content_sizer->AddSpacer(1); - m_classic_axis_draw = new wxInputDraw(m_classic_settings, wxID_ANY, wxDefaultPosition, { 60, 60 }); - content_sizer->Add(m_classic_axis_draw, 0, wxTOP | wxBOTTOM | wxALIGN_CENTER, 5); - content_sizer->AddSpacer(1); - - content_sizer->AddSpacer(1); - m_classic_rotation_draw = new wxInputDraw(m_classic_settings, wxID_ANY, wxDefaultPosition, { 60, 60 }); - content_sizer->Add(m_classic_rotation_draw, 0, wxTOP | wxBOTTOM | wxALIGN_CENTER, 5); - - box_sizer->Add(content_sizer, 1, wxEXPAND, 0); - } - - sizer->Add(box_sizer, 0, wxALL | wxEXPAND, 5); - } - - { - auto* control_sizer = new wxFlexGridSizer(0, 4, 0, 0); - control_sizer->AddGrowableCol(3); - - auto* ok_button = new wxButton(this, wxID_ANY, _("OK")); - ok_button->Bind(wxEVT_BUTTON, [this](auto&) { update_settings(); EndModal(wxID_OK); }); - control_sizer->Add(ok_button, 0, wxALL, 5); - - control_sizer->Add(0, 0, 0, wxEXPAND, 5); - - auto* cancel_button = new wxButton(this, wxID_ANY, _("Cancel")); - cancel_button->Bind(wxEVT_BUTTON, [this](auto&) { EndModal(wxID_CANCEL); }); - control_sizer->Add(cancel_button, 0, wxALL, 5); - - auto* calibrate_button = new wxButton(this, wxID_ANY, _("Calibrate")); - calibrate_button->Bind(wxEVT_BUTTON, [this](auto&) { m_controller->calibrate(); }); - control_sizer->Add(calibrate_button, 0, wxALL | wxALIGN_RIGHT, 5); - - sizer->Add(control_sizer, 0, wxEXPAND, 5); - } - - - - this->SetSizer(sizer); - this->Layout(); - this->Fit(); - - this->Bind(wxEVT_CLOSE_WINDOW, &WiimoteControllerSettings::on_close, this); - - m_timer = new wxTimer(this); - Bind(wxEVT_TIMER, &WiimoteControllerSettings::on_timer, this); - m_timer->Start(100); -} - -WiimoteControllerSettings::~WiimoteControllerSettings() -{ - m_controller->stop_rumble(); - m_timer->Stop(); -} - -void WiimoteControllerSettings::update_settings() -{ - // update settings - m_controller->set_settings(m_settings); - if (m_use_motion) - m_controller->set_use_motion(m_use_motion->GetValue()); - - m_controller->set_packet_delay(m_package_delay->GetValue()); -} - -void WiimoteControllerSettings::on_timer(wxTimerEvent& event) -{ - if (m_rumble_time.has_value() && std::chrono::duration_cast(std::chrono::steady_clock::now() - m_rumble_time.value()).count() > 500) - { - m_controller->stop_rumble(); - m_rumble_time = {}; - } - - const auto& default_state = m_controller->get_default_state(); - - auto state = m_controller->raw_state(); - m_controller->apply_axis_setting(state.axis, default_state.axis, m_settings.axis); - m_controller->apply_axis_setting(state.rotation, default_state.rotation, m_settings.rotation); - m_controller->apply_axis_setting(state.trigger, default_state.trigger, m_settings.trigger); - - if (m_nunchuck_settings->IsEnabled()) - { - m_nunchuck_draw->SetDeadzone(m_settings.axis.deadzone); - m_nunchuck_draw->SetAxisValue(state.axis); - } - - if (m_classic_settings->IsEnabled()) - { - m_classic_axis_draw->SetDeadzone(m_settings.axis.deadzone); - m_classic_axis_draw->SetAxisValue(state.axis); - - m_classic_rotation_draw->SetDeadzone(m_settings.rotation.deadzone); - m_classic_rotation_draw->SetAxisValue(state.rotation); - } - - wxString label; - switch (m_controller->get_extension()) - { - case NativeWiimoteController::Nunchuck: - label = _("Nunchuck"); - m_nunchuck_settings->Enable(); - m_classic_settings->Disable(); - break; - case NativeWiimoteController::Classic: - label = _("Classic"); - m_nunchuck_settings->Disable(); - m_classic_settings->Enable(); - break; - default: - m_nunchuck_settings->Disable(); - m_classic_settings->Disable(); - } - - if(m_controller->is_mpls_attached()) - { - const bool empty = label.empty(); - if (!empty) - label.Append(" ("); - - label.Append(_("MotionPlus")); - - if (!empty) - label.Append(")"); - } - - if(label.empty()) - { - label = _("None"); - } - - m_extension_text->SetLabelText(label); -} - -void WiimoteControllerSettings::on_close(wxCloseEvent& event) -{ - if (this->GetReturnCode() == 0 || this->GetReturnCode() == wxID_OK) - update_settings(); - - event.Skip(); -} - -void WiimoteControllerSettings::on_slider_change(wxCommandEvent& event) -{ - update_slider_text(event); - const auto new_value = (float)event.GetInt() / 100.0f; - - auto* obj = event.GetEventObject(); - if (obj == m_nunchuck_deadzone || obj == m_classic_axis_deadzone) - m_settings.axis.deadzone = new_value; - else if (obj == m_nunchuck_range || obj == m_classic_axis_range) - m_settings.axis.range = new_value; - else if (obj == m_classic_rotation_deadzone) - m_settings.rotation.deadzone = new_value; - else if (obj == m_classic_rotation_range) - m_settings.rotation.range = new_value; -} - -void WiimoteControllerSettings::on_rumble_change(wxCommandEvent& event) -{ - const auto rumble_value = m_rumble->GetValue(); - m_settings.rumble = rumble_value ? 1.0f : 0.0f; - - m_controller->set_rumble(m_settings.rumble); - if (rumble_value) - m_controller->start_rumble(); - else - m_controller->stop_rumble(); - - m_controller->set_rumble(m_rumble_backup); - - m_rumble_time = std::chrono::steady_clock::now(); -} - -void WiimoteControllerSettings::on_delay_change(wxCommandEvent& event) -{ - update_slider_text(event, "%dms"); -} - -#endif \ No newline at end of file diff --git a/src/gui/wxgui/input/settings/WiimoteControllerSettings.h b/src/gui/wxgui/input/settings/WiimoteControllerSettings.h deleted file mode 100644 index d5214efe..00000000 --- a/src/gui/wxgui/input/settings/WiimoteControllerSettings.h +++ /dev/null @@ -1,61 +0,0 @@ -#pragma once - -#ifdef SUPPORTS_WIIMOTE - -#include -#include -#include - -#include "input/api/Controller.h" -#include "input/api/Wiimote/NativeWiimoteController.h" - -class wxStaticBox; -class wxStaticText; -class wxCheckBox; -class wxInputDraw; - -class WiimoteControllerSettings : public wxDialog -{ -public: - WiimoteControllerSettings(wxWindow* parent, const wxPoint& position, std::shared_ptr controller); - ~WiimoteControllerSettings(); - -private: - void update_settings(); - - std::shared_ptr m_controller; - ControllerBase::Settings m_settings; - float m_rumble_backup; - uint32 m_packet_delay_backup; - - wxTimer* m_timer; - std::optional m_rumble_time{}; - - wxStaticText* m_extension_text; - - wxSlider* m_package_delay; - wxCheckBox* m_rumble = nullptr; - wxCheckBox* m_use_motion = nullptr; - - wxStaticBox* m_nunchuck_settings; - wxSlider* m_nunchuck_deadzone, * m_nunchuck_range; - wxInputDraw* m_nunchuck_draw; - - wxStaticBox* m_classic_settings; - wxSlider* m_classic_axis_deadzone, * m_classic_axis_range; - wxInputDraw* m_classic_axis_draw; - wxSlider* m_classic_rotation_deadzone, * m_classic_rotation_range; - wxInputDraw* m_classic_rotation_draw; - - - - - - void on_timer(wxTimerEvent& event); - void on_close(wxCloseEvent& event); - void on_slider_change(wxCommandEvent& event); - void on_rumble_change(wxCommandEvent& event); - void on_delay_change(wxCommandEvent& event); -}; - -#endif \ No newline at end of file diff --git a/src/gui/wxgui/windows/PPCThreadsViewer/DebugPPCThreadsWindow.cpp b/src/gui/wxgui/windows/PPCThreadsViewer/DebugPPCThreadsWindow.cpp deleted file mode 100644 index 5f7da298..00000000 --- a/src/gui/wxgui/windows/PPCThreadsViewer/DebugPPCThreadsWindow.cpp +++ /dev/null @@ -1,466 +0,0 @@ -#include "wxgui/wxgui.h" -#include "Cafe/OS/libs/coreinit/coreinit_Thread.h" -#include "Cafe/OS/libs/coreinit/coreinit_Scheduler.h" -#include "DebugPPCThreadsWindow.h" -#include "Cafe/OS/RPL/rpl.h" -#include "Cafe/OS/RPL/rpl_symbol_storage.h" - -#include "wxgui/components/wxProgressDialogManager.h" - -#include -#include -#include - -enum -{ - // options - REFRESH_ID = wxID_HIGHEST + 1, - AUTO_REFRESH_ID, - CLOSE_ID, - GPLIST_ID, - - // list context menu options - THREADLIST_MENU_BOOST_PRIO_1, - THREADLIST_MENU_BOOST_PRIO_5, - THREADLIST_MENU_DECREASE_PRIO_1, - THREADLIST_MENU_DECREASE_PRIO_5, - THREADLIST_MENU_SUSPEND, - THREADLIST_MENU_RESUME, - THREADLIST_MENU_DUMP_STACK_TRACE, - THREADLIST_MENU_PROFILE_THREAD, -}; - -wxBEGIN_EVENT_TABLE(DebugPPCThreadsWindow, wxFrame) - EVT_BUTTON(CLOSE_ID, DebugPPCThreadsWindow::OnCloseButton) - EVT_BUTTON(REFRESH_ID, DebugPPCThreadsWindow::OnRefreshButton) - EVT_CLOSE(DebugPPCThreadsWindow::OnClose) - wxEND_EVENT_TABLE() - - DebugPPCThreadsWindow::DebugPPCThreadsWindow(wxFrame& parent) - : wxFrame(&parent, wxID_ANY, _("PPC threads"), wxDefaultPosition, wxSize(930, 280), - wxCLOSE_BOX | wxCLIP_CHILDREN | wxCAPTION | wxRESIZE_BORDER) -{ - auto* sizer = new wxBoxSizer(wxVERTICAL); - m_thread_list = new wxListView(this, GPLIST_ID, wxPoint(0, 0), wxSize(930, 240), wxLC_REPORT); - - m_thread_list->SetFont(wxFont(8, wxFONTFAMILY_MODERN, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL, false, "Courier New")); // wxSystemSettings::GetFont(wxSYS_OEM_FIXED_FONT)); - - // add columns - wxListItem col0; - col0.SetId(0); - col0.SetText("Address"); - col0.SetWidth(75); - m_thread_list->InsertColumn(0, col0); - wxListItem col1; - col1.SetId(1); - col1.SetText("Entry"); - col1.SetWidth(75); - m_thread_list->InsertColumn(1, col1); - wxListItem col2; - col2.SetId(2); - col2.SetText("Stack"); - col2.SetWidth(145); - m_thread_list->InsertColumn(2, col2); - wxListItem col3; - col3.SetId(3); - col3.SetText("PC"); - col3.SetWidth(120); - m_thread_list->InsertColumn(3, col3); - wxListItem colLR; - colLR.SetId(4); - colLR.SetText("LR"); - colLR.SetWidth(75); - m_thread_list->InsertColumn(4, colLR); - wxListItem col4; - col4.SetId(5); - col4.SetText("State"); - col4.SetWidth(90); - m_thread_list->InsertColumn(5, col4); - wxListItem col5; - col5.SetId(6); - col5.SetText("Affinity"); - col5.SetWidth(70); - m_thread_list->InsertColumn(6, col5); - wxListItem colPriority; - colPriority.SetId(7); - colPriority.SetText("Priority"); - colPriority.SetWidth(80); - m_thread_list->InsertColumn(7, colPriority); - wxListItem col6; - col6.SetId(8); - col6.SetText("SliceStart"); - col6.SetWidth(110); - m_thread_list->InsertColumn(8, col6); - wxListItem col7; - col7.SetId(9); - col7.SetText("SumWakeTime"); - col7.SetWidth(110); - m_thread_list->InsertColumn(9, col7); - wxListItem col8; - col8.SetId(10); - col8.SetText("ThreadName"); - col8.SetWidth(180); - m_thread_list->InsertColumn(10, col8); - wxListItem col9; - col9.SetId(11); - col9.SetText("GPR"); - col9.SetWidth(180); - m_thread_list->InsertColumn(11, col9); - wxListItem col10; - col10.SetId(12); - col10.SetText("Extra info"); - col10.SetWidth(180); - m_thread_list->InsertColumn(12, col10); - - sizer->Add(m_thread_list, 1, wxEXPAND | wxALL, 5); - - auto* row = new wxBoxSizer(wxHORIZONTAL); - wxButton* button = new wxButton(this, REFRESH_ID, _("Refresh"), wxPoint(0, 0), wxSize(80, 26)); - row->Add(button, 0, wxALL, 5); - - m_auto_refresh = new wxCheckBox(this, AUTO_REFRESH_ID, _("Auto refresh")); - m_auto_refresh->SetValue(true); - row->Add(m_auto_refresh, 0, wxEXPAND | wxALL, 5); - - sizer->Add(row, 0, wxEXPAND | wxALL, 5); - - m_thread_list->Bind(wxEVT_RIGHT_DOWN, &DebugPPCThreadsWindow::OnThreadListRightClick, this); - - SetSizer(sizer); - - RefreshThreadList(); - - m_timer = new wxTimer(this); - this->Bind(wxEVT_TIMER, &DebugPPCThreadsWindow::OnTimer, this); - m_timer->Start(250); -} - -DebugPPCThreadsWindow::~DebugPPCThreadsWindow() -{ - m_timer->Stop(); -} - -void DebugPPCThreadsWindow::OnCloseButton(wxCommandEvent& event) -{ - Close(); -} - -void DebugPPCThreadsWindow::OnRefreshButton(wxCommandEvent& event) -{ - RefreshThreadList(); -} - -void DebugPPCThreadsWindow::OnClose(wxCloseEvent& event) -{ - Close(); -} - -void DebugPPCThreadsWindow::OnTimer(wxTimerEvent& event) -{ - if (m_auto_refresh->IsChecked()) - RefreshThreadList(); -} - -#define _r(__idx) _swapEndianU32(cafeThread->context.gpr[__idx]) - -void DebugPPCThreadsWindow::RefreshThreadList() -{ - wxWindowUpdateLocker lock(m_thread_list); - - long selected_thread = 0; - const int selection = m_thread_list->GetFirstSelected(); - if (selection != wxNOT_FOUND) - selected_thread = m_thread_list->GetItemData(selection); - - const int scrollPos = m_thread_list->GetScrollPos(0); - m_thread_list->DeleteAllItems(); - - if (activeThreadCount > 0) - { - __OSLockScheduler(); - srwlock_activeThreadList.LockWrite(); - for (sint32 i = 0; i < activeThreadCount; i++) - { - MPTR threadItrMPTR = activeThread[i]; - OSThread_t* cafeThread = (OSThread_t*)memory_getPointerFromVirtualOffset(threadItrMPTR); - - wxListItem item; - item.SetId(i); - item.SetText(wxString::Format("%08X", threadItrMPTR)); - m_thread_list->InsertItem(item); - m_thread_list->SetItemData(item, (long)threadItrMPTR); - // entry point - m_thread_list->SetItem(i, 1, wxString::Format("%08X", cafeThread->entrypoint.GetMPTR())); - // stack base (low) - m_thread_list->SetItem(i, 2, wxString::Format("%08X - %08X", cafeThread->stackEnd.GetMPTR(), cafeThread->stackBase.GetMPTR())); - // pc - RPLStoredSymbol* symbol = rplSymbolStorage_getByAddress(cafeThread->context.srr0); - wxString pcLabel; - if (symbol) - pcLabel = wxString::Format("%s (0x%08x)", (const char*)symbol->symbolName, cafeThread->context.srr0); - else - pcLabel = wxString::Format("%08X", cafeThread->context.srr0); - m_thread_list->SetItem(i, 3, pcLabel); - // lr - m_thread_list->SetItem(i, 4, wxString::Format("%08X", _swapEndianU32(cafeThread->context.lr))); - // state - OSThread_t::THREAD_STATE threadState = cafeThread->state; - wxString threadStateStr = "UNDEFINED"; - if (cafeThread->suspendCounter != 0) - threadStateStr = "SUSPENDED"; - else if (threadState == OSThread_t::THREAD_STATE::STATE_NONE) - threadStateStr = "NONE"; - else if (threadState == OSThread_t::THREAD_STATE::STATE_READY) - threadStateStr = "READY"; - else if (threadState == OSThread_t::THREAD_STATE::STATE_RUNNING) - threadStateStr = "RUNNING"; - else if (threadState == OSThread_t::THREAD_STATE::STATE_WAITING) - threadStateStr = "WAITING"; - else if (threadState == OSThread_t::THREAD_STATE::STATE_MORIBUND) - threadStateStr = "MORIBUND"; - m_thread_list->SetItem(i, 5, threadStateStr); - // affinity - uint8 affinity = cafeThread->attr & 7; - uint8 affinityReal = cafeThread->context.affinity; - wxString affinityLabel; - if (affinity != affinityReal) - affinityLabel = wxString::Format("(!) %d%d%d real: %d%d%d", (affinity >> 0) & 1, (affinity >> 1) & 1, (affinity >> 2) & 1, (affinityReal >> 0) & 1, (affinityReal >> 1) & 1, (affinityReal >> 2) & 1); - else - affinityLabel = wxString::Format("%d%d%d", (affinity >> 0) & 1, (affinity >> 1) & 1, (affinity >> 2) & 1); - m_thread_list->SetItem(i, 6, affinityLabel); - // priority - sint32 effectivePriority = cafeThread->effectivePriority; - m_thread_list->SetItem(i, 7, wxString::Format("%d", effectivePriority)); - // last awake in cycles - uint64 lastWakeUpTime = cafeThread->wakeUpTime; - m_thread_list->SetItem(i, 8, wxString::Format("%" PRIu64, lastWakeUpTime)); - // awake time in cycles - uint64 awakeTime = cafeThread->totalCycles; - m_thread_list->SetItem(i, 9, wxString::Format("%" PRIu64, awakeTime)); - // thread name - const char* threadName = "NULL"; - if (!cafeThread->threadName.IsNull()) - threadName = cafeThread->threadName.GetPtr(); - m_thread_list->SetItem(i, 10, threadName); - // GPR - m_thread_list->SetItem(i, 11, wxString::Format("r3 %08x r4 %08x r5 %08x r6 %08x r7 %08x", _r(3), _r(4), _r(5), _r(6), _r(7))); - // waiting condition / extra info - coreinit::OSMutex* mutex = cafeThread->waitingForMutex; - wxString extraInfoLabel; - if (mutex) - extraInfoLabel = wxString::Format("Mutex 0x%08x (Held by thread 0x%08X Lock-Count: %d)", memory_getVirtualOffsetFromPointer(mutex), mutex->owner.GetMPTR(), (uint32)mutex->lockCount); - - // OSSetThreadCancelState - if (cafeThread->requestFlags & OSThread_t::REQUEST_FLAG_CANCEL) - extraInfoLabel += "[Cancel requested]"; - - m_thread_list->SetItem(i, 12, extraInfoLabel); - - if (selected_thread != 0 && selected_thread == (long)threadItrMPTR) - { - m_thread_list->Select(i); - m_thread_list->Focus(i); - } - } - srwlock_activeThreadList.UnlockWrite(); - __OSUnlockScheduler(); - } - - m_thread_list->SetScrollPos(0, scrollPos, true); -} - -void DebugPPCThreadsWindow::DumpStackTrace(OSThread_t* thread) -{ - cemuLog_log(LogType::Force, "Dumping stack trace for thread {0:08x} LR: {1:08x}", memory_getVirtualOffsetFromPointer(thread), _swapEndianU32(thread->context.lr)); - DebugLogStackTrace(thread, _swapEndianU32(thread->context.gpr[1])); -} - -void DebugPPCThreadsWindow::PresentProfileResults(OSThread_t* thread, const std::unordered_map& samples) -{ - std::vector> sortedSamples; - // count samples - uint32 totalSampleCount = 0; - for (auto& sample : samples) - totalSampleCount += sample.second; - cemuLog_log(LogType::Force, "--- Thread {:08x} profile results with {:} samples captured ---", - MEMPTR(thread).GetMPTR(), totalSampleCount); - cemuLog_log(LogType::Force, "Exclusive time, grouped by function:"); - // print samples grouped by function - sortedSamples.clear(); - for (auto& sample : samples) - { - RPLStoredSymbol* symbol = rplSymbolStorage_getByClosestAddress(sample.first); - VAddr sampleAddr = sample.first; - if (symbol) - sampleAddr = symbol->address; - auto it = std::find_if(sortedSamples.begin(), sortedSamples.end(), - [sampleAddr](const std::pair& a) { return a.first == sampleAddr; }); - if (it != sortedSamples.end()) - it->second += sample.second; - else - sortedSamples.push_back(std::make_pair(sampleAddr, sample.second)); - } - std::sort(sortedSamples.begin(), sortedSamples.end(), - [](const std::pair& a, const std::pair& b) { return a.second > b.second; }); - for (auto& sample : sortedSamples) - { - if (sample.second < 3) - continue; - VAddr sampleAddr = sample.first; - RPLStoredSymbol* symbol = rplSymbolStorage_getByClosestAddress(sample.first); - std::string strName; - if (symbol) - { - strName = fmt::format("{}.{}+0x{:x}", (const char*)symbol->libName, (const char*)symbol->symbolName, - sampleAddr - symbol->address); - } - else - strName = "Unknown"; - cemuLog_log(LogType::Force, "[{:08x}] {:8.2f}% (Samples: {:5}) Symbol: {}", sample.first, - (double)(sample.second * 100) / (double)totalSampleCount, sample.second, strName); - } -} - -void DebugPPCThreadsWindow::ProfileThreadWorker(OSThread_t* thread) -{ - wxProgressDialogManager progressDialog(this); - progressDialog.Create(_("Profiling thread"), - _("Capturing samples..."), - 1000, // range - wxPD_CAN_SKIP); - - std::unordered_map samples; - // loop for one minute - uint64 startTime = std::chrono::duration_cast( - std::chrono::system_clock::now().time_since_epoch()) - .count(); - uint32 totalSampleCount = 0; - while (true) - { - // suspend thread - coreinit::OSSuspendThread(thread); - // wait until thread is not running anymore - __OSLockScheduler(); - while (coreinit::OSIsThreadRunningNoLock(thread)) - { - __OSUnlockScheduler(); - std::this_thread::sleep_for(std::chrono::milliseconds(1)); - __OSLockScheduler(); - } - uint32 sampleIP = thread->context.srr0; - __OSUnlockScheduler(); - coreinit::OSResumeThread(thread); - // count sample - samples[sampleIP]++; - totalSampleCount++; - if ((totalSampleCount % 50) == 0) - { - wxString msg = formatWxString(_("Capturing samples... ({:})\nResults will be written to log.txt\n"), totalSampleCount); - if (totalSampleCount < 30000) - msg.Append(_("Click Skip button for early results with lower accuracy")); - else - msg.Append(_("Click Skip button to finish")); - progressDialog.Update(totalSampleCount * 1000 / 30000, msg); - if (progressDialog.IsCancelledOrSkipped()) - break; - } - std::this_thread::sleep_for(std::chrono::milliseconds(1)); - } - PresentProfileResults(thread, samples); - progressDialog.Destroy(); -} - -void DebugPPCThreadsWindow::ProfileThread(OSThread_t* thread) -{ - std::thread profileThread(&DebugPPCThreadsWindow::ProfileThreadWorker, this, thread); - profileThread.detach(); -} - -void DebugPPCThreadsWindow::OnThreadListPopupClick(wxCommandEvent& evt) -{ - MPTR threadMPTR = (MPTR)(size_t)static_cast(evt.GetEventObject())->GetClientData(); - OSThread_t* osThread = (OSThread_t*)memory_getPointerFromVirtualOffset(threadMPTR); - __OSLockScheduler(); - if (!coreinit::__OSIsThreadActive(osThread)) - { - __OSUnlockScheduler(); - return; - } - __OSUnlockScheduler(); - // handle command - switch (evt.GetId()) - { - case THREADLIST_MENU_BOOST_PRIO_5: - osThread->basePriority = osThread->basePriority - 5; - break; - case THREADLIST_MENU_BOOST_PRIO_1: - osThread->basePriority = osThread->basePriority - 1; - break; - case THREADLIST_MENU_DECREASE_PRIO_5: - osThread->basePriority = osThread->basePriority + 5; - break; - case THREADLIST_MENU_DECREASE_PRIO_1: - osThread->basePriority = osThread->basePriority + 1; - break; - case THREADLIST_MENU_SUSPEND: - coreinit::OSSuspendThread(osThread); - break; - case THREADLIST_MENU_RESUME: - coreinit::OSResumeThread(osThread); - break; - case THREADLIST_MENU_DUMP_STACK_TRACE: - DumpStackTrace(osThread); - break; - case THREADLIST_MENU_PROFILE_THREAD: - ProfileThread(osThread); - break; - } - coreinit::__OSUpdateThreadEffectivePriority(osThread); - // update thread list - RefreshThreadList(); -} - -void DebugPPCThreadsWindow::OnThreadListRightClick(wxMouseEvent& event) -{ - // Get the item index - int hitTestFlag; - int itemIndex = m_thread_list->HitTest(event.GetPosition(), hitTestFlag); - if (itemIndex == wxNOT_FOUND) - return; - // select item - m_thread_list->Focus(itemIndex); - long sel = m_thread_list->GetFirstSelected(); - if (sel != wxNOT_FOUND) - m_thread_list->Select(sel, false); - m_thread_list->Select(itemIndex); - // check if thread is still on the list of active threads - MPTR threadMPTR = (MPTR)m_thread_list->GetItemData(itemIndex); - __OSLockScheduler(); - if (!coreinit::__OSIsThreadActive(MEMPTR(threadMPTR))) - { - __OSUnlockScheduler(); - return; - } - __OSUnlockScheduler(); - // create menu entry - wxMenu menu; - menu.SetClientData((void*)(size_t)threadMPTR); - menu.Append(THREADLIST_MENU_BOOST_PRIO_5, _("Boost priority (-5)")); - menu.Append(THREADLIST_MENU_BOOST_PRIO_1, _("Boost priority (-1)")); - menu.AppendSeparator(); - menu.Append(THREADLIST_MENU_DECREASE_PRIO_5, _("Decrease priority (+5)")); - menu.Append(THREADLIST_MENU_DECREASE_PRIO_1, _("Decrease priority (+1)")); - menu.AppendSeparator(); - menu.Append(THREADLIST_MENU_RESUME, _("Resume")); - menu.Append(THREADLIST_MENU_SUSPEND, _("Suspend")); - menu.AppendSeparator(); - menu.Append(THREADLIST_MENU_DUMP_STACK_TRACE, _("Write stack trace to log")); - menu.Append(THREADLIST_MENU_PROFILE_THREAD, _("Profile thread")); - menu.Bind(wxEVT_COMMAND_MENU_SELECTED, &DebugPPCThreadsWindow::OnThreadListPopupClick, this); - PopupMenu(&menu); -} - -void DebugPPCThreadsWindow::Close() -{ - this->Destroy(); -} diff --git a/src/gui/wxgui/windows/PPCThreadsViewer/DebugPPCThreadsWindow.h b/src/gui/wxgui/windows/PPCThreadsViewer/DebugPPCThreadsWindow.h deleted file mode 100644 index 5b502e32..00000000 --- a/src/gui/wxgui/windows/PPCThreadsViewer/DebugPPCThreadsWindow.h +++ /dev/null @@ -1,37 +0,0 @@ -#pragma once - -#include - -class wxListView; - -class DebugPPCThreadsWindow: public wxFrame -{ -public: - DebugPPCThreadsWindow(wxFrame& parent); - ~DebugPPCThreadsWindow(); - - void OnCloseButton(wxCommandEvent& event); - void OnRefreshButton(wxCommandEvent& event); - void OnClose(wxCloseEvent& event); - void RefreshThreadList(); - void OnThreadListPopupClick(wxCommandEvent &evt); - void OnThreadListRightClick(wxMouseEvent& event); - - void Close(); - -private: - void ProfileThread(struct OSThread_t* thread); - void ProfileThreadWorker(OSThread_t* thread); - void PresentProfileResults(OSThread_t* thread, const std::unordered_map& samples); - void DumpStackTrace(struct OSThread_t* thread); - - wxListView* m_thread_list; - wxCheckBox* m_auto_refresh; - wxTimer* m_timer; - - void OnTimer(wxTimerEvent& event); - - wxDECLARE_EVENT_TABLE(); - - -}; diff --git a/src/gui/wxgui/windows/TextureRelationViewer/TextureRelationWindow.cpp b/src/gui/wxgui/windows/TextureRelationViewer/TextureRelationWindow.cpp deleted file mode 100644 index b49adb14..00000000 --- a/src/gui/wxgui/windows/TextureRelationViewer/TextureRelationWindow.cpp +++ /dev/null @@ -1,377 +0,0 @@ -#include "wxgui/wxgui.h" -#include "wxHelper.h" -#include "TextureRelationWindow.h" -#include "Cafe/HW/Latte/Core/LatteTexture.h" - -enum -{ - // options - REFRESH_ID, - CLOSE_ID, - TEX_LIST_A_ID, - TEX_LIST_B_ID, - CHECKBOX_SHOW_ONLY_ACTIVE, - CHECKBOX_SHOW_VIEWS, -}; - -wxBEGIN_EVENT_TABLE(TextureRelationViewerWindow, wxFrame) -EVT_BUTTON(CLOSE_ID, TextureRelationViewerWindow::OnCloseButton) -EVT_BUTTON(REFRESH_ID, TextureRelationViewerWindow::OnRefreshButton) -EVT_CHECKBOX(CHECKBOX_SHOW_ONLY_ACTIVE, TextureRelationViewerWindow::OnCheckbox) -EVT_CHECKBOX(CHECKBOX_SHOW_VIEWS, TextureRelationViewerWindow::OnCheckbox) - -EVT_CLOSE(TextureRelationViewerWindow::OnClose) -wxEND_EVENT_TABLE() - -wxListCtrl* textureRelationListA; -bool isTextureViewerOpen = false; - -void openTextureViewer(wxFrame& parentFrame) -{ - if (isTextureViewerOpen) - return; - auto frame = new TextureRelationViewerWindow(parentFrame); - frame->Show(true); -} - -TextureRelationViewerWindow::TextureRelationViewerWindow(wxFrame& parent) - : wxFrame(&parent, wxID_ANY, _("Texture cache"), wxDefaultPosition, wxSize(1000, 480), wxCLOSE_BOX | wxCLIP_CHILDREN | wxCAPTION | wxRESIZE_BORDER) -{ - isTextureViewerOpen = true; - - this->showOnlyActive = false; - this->showTextureViews = true; - - wxPanel* mainPane = new wxPanel(this); - wxBoxSizer* sizer = new wxBoxSizer(wxVERTICAL); - textureRelationListA = new wxListCtrl(mainPane, TEX_LIST_A_ID, wxPoint(0, 0), wxSize(1008, 440), wxLC_REPORT); - - textureRelationListA->SetFont(wxFont(8, wxFONTFAMILY_MODERN, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL, false, "Courier New"));//wxSystemSettings::GetFont(wxSYS_OEM_FIXED_FONT)); - - // add columns - wxListItem colType; - sint32 columnIndex = 0; - colType.SetId(columnIndex); columnIndex++; - colType.SetText("Type"); - colType.SetWidth(85); - textureRelationListA->InsertColumn(columnIndex-1, colType); - wxListItem colPhysAddr; - colPhysAddr.SetId(columnIndex); columnIndex++; - colPhysAddr.SetText("PhysAddr"); - colPhysAddr.SetWidth(80); - textureRelationListA->InsertColumn(columnIndex-1, colPhysAddr); - wxListItem colPhysMipAddr; - colPhysMipAddr.SetId(columnIndex); columnIndex++; - colPhysMipAddr.SetText("MipPAddr"); - colPhysMipAddr.SetWidth(80); - textureRelationListA->InsertColumn(columnIndex-1, colPhysMipAddr); - wxListItem colDim; - colDim.SetId(columnIndex); columnIndex++; - colDim.SetText("Dim"); - colDim.SetWidth(80); - textureRelationListA->InsertColumn(columnIndex-1, colDim); - wxListItem colResolution; - colResolution.SetId(columnIndex); columnIndex++; - colResolution.SetText("Resolution"); - colResolution.SetWidth(110); - textureRelationListA->InsertColumn(columnIndex-1, colResolution); - wxListItem colFormat; - colFormat.SetId(columnIndex); columnIndex++; - colFormat.SetText("Format"); - colFormat.SetWidth(70); - textureRelationListA->InsertColumn(columnIndex-1, colFormat); - wxListItem colPitch; - colPitch.SetId(columnIndex); columnIndex++; - colPitch.SetText("Pitch"); - colPitch.SetWidth(80); - textureRelationListA->InsertColumn(columnIndex-1, colPitch); - wxListItem colTilemode; - colTilemode.SetId(columnIndex); columnIndex++; - colTilemode.SetText("Tilemode"); - colTilemode.SetWidth(80); - textureRelationListA->InsertColumn(columnIndex-1, colTilemode); - wxListItem colSliceRange; - colSliceRange.SetId(columnIndex); columnIndex++; - colSliceRange.SetText("SliceRange"); - colSliceRange.SetWidth(90); - textureRelationListA->InsertColumn(columnIndex-1, colSliceRange); - wxListItem colMipRange; - colMipRange.SetId(columnIndex); columnIndex++; - colMipRange.SetText("MipRange"); - colMipRange.SetWidth(90); - textureRelationListA->InsertColumn(columnIndex-1, colMipRange); - wxListItem colAge; - colAge.SetId(columnIndex); columnIndex++; - colAge.SetText("Last access"); - colAge.SetWidth(90); - textureRelationListA->InsertColumn(columnIndex - 1, colAge); - wxListItem colOverwriteRes; - colOverwriteRes.SetId(columnIndex); columnIndex++; - colOverwriteRes.SetText("OverwriteRes"); - colOverwriteRes.SetWidth(110); - textureRelationListA->InsertColumn(columnIndex - 1, colOverwriteRes); - - wxBoxSizer* sizerBottom = new wxBoxSizer(wxHORIZONTAL); - - sizer->Add(textureRelationListA, 1, wxEXPAND | wxBOTTOM, 0); - wxButton* button = new wxButton(mainPane, REFRESH_ID, _("Refresh")); - sizerBottom->Add(button, 0, wxALIGN_CENTER_VERTICAL | wxBOTTOM | wxTOP | wxLEFT, 10); - - wxCheckBox* checkboxShowOnlyActive = new wxCheckBox(mainPane, CHECKBOX_SHOW_ONLY_ACTIVE, _("Show only active")); - sizerBottom->Add(checkboxShowOnlyActive, 0, wxALIGN_CENTER_VERTICAL | wxBOTTOM | wxTOP | wxLEFT, 10); - - wxCheckBox* checkboxShowViews = new wxCheckBox(mainPane, CHECKBOX_SHOW_VIEWS, _("Show views")); - sizerBottom->Add(checkboxShowViews, 0, wxALIGN_CENTER_VERTICAL | wxBOTTOM | wxTOP | wxLEFT, 10); - checkboxShowViews->SetValue(true); - - textureRelationListA->Bind(wxEVT_RIGHT_DOWN, &TextureRelationViewerWindow::OnTextureListRightClick, this); - - sizer->Add( - sizerBottom, - 0, // vertically unstretchable - wxALIGN_LEFT); - mainPane->SetSizer(sizer); - - RefreshTextureList(); -} - -TextureRelationViewerWindow::~TextureRelationViewerWindow() -{ - isTextureViewerOpen = false; -} - -void TextureRelationViewerWindow::OnCloseButton(wxCommandEvent& event) -{ - Close(); -} - -void TextureRelationViewerWindow::OnRefreshButton(wxCommandEvent& event) -{ - RefreshTextureList(); -} - -void TextureRelationViewerWindow::OnCheckbox(wxCommandEvent& event) -{ - if (event.GetId() == CHECKBOX_SHOW_ONLY_ACTIVE) - { - showOnlyActive = event.IsChecked(); - RefreshTextureList(); - } - else if (event.GetId() == CHECKBOX_SHOW_VIEWS) - { - showTextureViews = event.IsChecked(); - RefreshTextureList(); - } -} - -void TextureRelationViewerWindow::OnClose(wxCloseEvent& event) -{ - Close(); -} - -void TextureRelationViewerWindow::_setTextureRelationListItemTexture(wxListCtrl* uiList, sint32 rowIndex, struct LatteTextureInformation* texInfo) -{ - wxString typeLabel; - // count number of alternative views for base view - sint32 alternativeViewCount = texInfo->alternativeViewCount; - if (texInfo->isUpdatedOnGPU) - typeLabel = "TEXTURE*"; - else - typeLabel = "TEXTURE"; - if (alternativeViewCount > 0) - { - typeLabel += wxString::Format("(%d)", alternativeViewCount + 1); - } - wxListItem item; - item.SetId(rowIndex); - item.SetText(typeLabel); - item.SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW)); - uiList->InsertItem(item); - - sint32 columnIndex = 1; - // phys address - uiList->SetItem(rowIndex, columnIndex, wxString::Format("%08X", texInfo->physAddress)); - columnIndex++; - // phys mip address - uiList->SetItem(rowIndex, columnIndex, wxString::Format("%08X", texInfo->physMipAddress)); - columnIndex++; - // dim - wxString dimLabel; - if (texInfo->dim == Latte::E_DIM::DIM_2D) - dimLabel = "2D"; - else if (texInfo->dim == Latte::E_DIM::DIM_2D_ARRAY) - dimLabel = "2D_ARRAY"; - else if (texInfo->dim == Latte::E_DIM::DIM_3D) - dimLabel = "3D"; - else if (texInfo->dim == Latte::E_DIM::DIM_CUBEMAP) - dimLabel = "CUBEMAP"; - else if (texInfo->dim == Latte::E_DIM::DIM_1D) - dimLabel = "1D"; - else if (texInfo->dim == Latte::E_DIM::DIM_2D_MSAA) - dimLabel = "2D_MSAA"; - else if (texInfo->dim == Latte::E_DIM::DIM_2D_ARRAY_MSAA) - dimLabel = "2D_MS_ARRAY"; - else - dimLabel = "UKN"; - uiList->SetItem(rowIndex, columnIndex, dimLabel); - columnIndex++; - // resolution - wxString resolutionLabel; - if (texInfo->depth == 1) - resolutionLabel = wxString::Format("%dx%d", texInfo->width, texInfo->height); - else - resolutionLabel = wxString::Format("%dx%dx%d", texInfo->width, texInfo->height, texInfo->depth); - uiList->SetItem(rowIndex, columnIndex, resolutionLabel); - columnIndex++; - // format - wxString formatLabel; - if(texInfo->isDepth) - formatLabel = wxString::Format("%04x(d)", (uint32)texInfo->format); - else - formatLabel = wxString::Format("%04x", (uint32)texInfo->format); - uiList->SetItem(rowIndex, columnIndex, formatLabel); - columnIndex++; - // pitch - uiList->SetItem(rowIndex, columnIndex, wxString::Format("%d", texInfo->pitch)); - columnIndex++; - // tilemode - uiList->SetItem(rowIndex, columnIndex, wxString::Format("%d", (int)texInfo->tileMode)); - columnIndex++; - // sliceRange - uiList->SetItem(rowIndex, columnIndex, ""); - columnIndex++; - // mipRange - uiList->SetItem(rowIndex, columnIndex, texInfo->mipLevels == 1 ? "1 mip" : wxString::Format("%d mips", texInfo->mipLevels)); - columnIndex++; - // last access - uiList->SetItem(rowIndex, columnIndex, wxString::Format("%uus", (GetTickCount() - texInfo->lastAccessTick + 499) / 1000)); - columnIndex++; - // overwrite resolution - wxString overwriteResLabel; - if (texInfo->overwriteInfo.hasResolutionOverwrite) - { - if(texInfo->overwriteInfo.depth != 1 || texInfo->depth != 1) - overwriteResLabel = wxString::Format("%dx%dx%d", texInfo->overwriteInfo.width, texInfo->overwriteInfo.height, texInfo->overwriteInfo.depth); - else - overwriteResLabel = wxString::Format("%dx%d", texInfo->overwriteInfo.width, texInfo->overwriteInfo.height); - } - uiList->SetItem(rowIndex, columnIndex, overwriteResLabel); - columnIndex++; -} - -void TextureRelationViewerWindow::_setTextureRelationListItemView(wxListCtrl* uiList, sint32 rowIndex, struct LatteTextureInformation* texInfo, struct LatteTextureViewInformation* viewInfo) -{ - // count number of alternative views - sint32 alternativeViewCount = 0; // todo - // set type string - // find and handle highlight entry - wxListItem item; - item.SetId(rowIndex); - item.SetText(alternativeViewCount == 0 ? "> VIEW" : wxString::Format("> VIEW(%d)", alternativeViewCount+1)); - item.SetBackgroundColour(wxHelper::CalculateAccentColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW))); - uiList->InsertItem(item); - //uiList->SetItemPtrData(item, (wxUIntPtr)viewInfo); - sint32 columnIndex = 1; - // phys address - uiList->SetItem(rowIndex, columnIndex, ""); - columnIndex++; - // phys mip address - uiList->SetItem(rowIndex, columnIndex, ""); - columnIndex++; - // dim - wxString dimLabel; - if (viewInfo->dim == Latte::E_DIM::DIM_2D) - dimLabel = "2D"; - else if (viewInfo->dim == Latte::E_DIM::DIM_2D_ARRAY) - dimLabel = "2D_ARRAY"; - else if (viewInfo->dim == Latte::E_DIM::DIM_3D) - dimLabel = "3D"; - else if (viewInfo->dim == Latte::E_DIM::DIM_CUBEMAP) - dimLabel = "CUBEMAP"; - else if (viewInfo->dim == Latte::E_DIM::DIM_1D) - dimLabel = "1D"; - else if (viewInfo->dim == Latte::E_DIM::DIM_2D_MSAA) - dimLabel = "2D_MSAA"; - else if (viewInfo->dim == Latte::E_DIM::DIM_2D_ARRAY_MSAA) - dimLabel = "2D_ARRAY_MSAA"; - else - dimLabel = "UKN"; - uiList->SetItem(rowIndex, columnIndex, dimLabel); - columnIndex++; - // resolution - uiList->SetItem(rowIndex, columnIndex, ""); - columnIndex++; - // format - uiList->SetItem(rowIndex, columnIndex, wxString::Format("%04x", (uint32)viewInfo->format)); - columnIndex++; - // pitch - uiList->SetItem(rowIndex, columnIndex, ""); - columnIndex++; - // tilemode - uiList->SetItem(rowIndex, columnIndex, ""); - columnIndex++; - // sliceRange - uiList->SetItem(rowIndex, columnIndex, wxString::Format("%d-%d", viewInfo->firstSlice, viewInfo->firstSlice+ viewInfo->numSlice-1)); - columnIndex++; - // mipRange - uiList->SetItem(rowIndex, columnIndex, wxString::Format("%d-%d", viewInfo->firstMip, viewInfo->firstMip + viewInfo->numMip - 1)); - columnIndex++; - // last access - uiList->SetItem(rowIndex, columnIndex, ""); - columnIndex++; -} - -void TextureRelationViewerWindow::RefreshTextureList() -{ - int scrollPos = textureRelationListA->GetScrollPos(wxVERTICAL); - textureRelationListA->DeleteAllItems(); - - std::vector texCache = LatteTexture_QueryCacheInfo(); - - // sort by physAddr in ascending order - for (sint32 i1 = 0; i1 < texCache.size(); i1++) - { - for (sint32 i2 = i1+1; i2 < texCache.size(); i2++) - { - if (texCache[i1].physAddress > texCache[i2].physAddress) - { - std::swap(texCache[i1], texCache[i2]); - } - } - } - - textureRelationListA->Freeze(); - - sint32 rowIndex = 0; - uint32 currentTick = GetTickCount(); - for (auto& tex : texCache) - { - uint32 timeSinceLastAccess = currentTick - tex.lastAccessTick; - if (showOnlyActive && timeSinceLastAccess > 3000) - continue; // hide textures which haven't been updated in more than 3 seconds - - _setTextureRelationListItemTexture(textureRelationListA, rowIndex, &tex); - rowIndex++; - if (showTextureViews) - { - for (auto& view : tex.views) - { - _setTextureRelationListItemView(textureRelationListA, rowIndex, &tex, &view); - rowIndex++; - } - } - } - textureRelationListA->Thaw(); - long itemCount = textureRelationListA->GetItemCount(); - if (itemCount > 0) - textureRelationListA->EnsureVisible(std::min(itemCount - 1, scrollPos + textureRelationListA->GetCountPerPage() - 1)); -} - -void TextureRelationViewerWindow::OnTextureListRightClick(wxMouseEvent& event) -{ -} - -void TextureRelationViewerWindow::Close() -{ - this->Destroy(); -} diff --git a/src/gui/wxgui/windows/TextureRelationViewer/TextureRelationWindow.h b/src/gui/wxgui/windows/TextureRelationViewer/TextureRelationWindow.h deleted file mode 100644 index f429a0ed..00000000 --- a/src/gui/wxgui/windows/TextureRelationViewer/TextureRelationWindow.h +++ /dev/null @@ -1,33 +0,0 @@ -#pragma once - -#include - -class wxListCtrl; - -class TextureRelationViewerWindow : public wxFrame -{ -public: - TextureRelationViewerWindow(wxFrame& parent); - ~TextureRelationViewerWindow(); - - void OnCloseButton(wxCommandEvent& event); - void OnRefreshButton(wxCommandEvent& event); - void OnCheckbox(wxCommandEvent& event); - void OnClose(wxCloseEvent& event); - void RefreshTextureList(); - void OnTextureListRightClick(wxMouseEvent& event); - - void Close(); - -private: - - wxDECLARE_EVENT_TABLE(); - - void _setTextureRelationListItemTexture(wxListCtrl* uiList, sint32 rowIndex, struct LatteTextureInformation* texInfo); - void _setTextureRelationListItemView(wxListCtrl* uiList, sint32 rowIndex, struct LatteTextureInformation* texInfo, struct LatteTextureViewInformation* viewInfo); - - bool showOnlyActive; - bool showTextureViews; -}; - -void openTextureViewer(wxFrame& parentFrame); diff --git a/src/gui/wxgui/wxCemuConfig.cpp b/src/gui/wxgui/wxCemuConfig.cpp deleted file mode 100644 index 3795c3cf..00000000 --- a/src/gui/wxgui/wxCemuConfig.cpp +++ /dev/null @@ -1,190 +0,0 @@ -#include "wxCemuConfig.h" -#include "Common/precompiled.h" -#include "config/CemuConfig.h" -#include "config/XMLConfig.h" -#include "util/helpers/helpers.h" -#include - -XMLWxCemuConfig_t g_wxConfig(&GetConfigHandle); - -void wxCemuConfig::AddRecentlyLaunchedFile(std::string_view file) -{ - recent_launch_files.insert(recent_launch_files.begin(), std::string(file)); - RemoveDuplicatesKeepOrder(recent_launch_files); - while (recent_launch_files.size() > kMaxRecentEntries) - recent_launch_files.pop_back(); -} - -void wxCemuConfig::AddRecentNfcFile(std::string_view file) -{ - recent_nfc_files.insert(recent_nfc_files.begin(), std::string(file)); - RemoveDuplicatesKeepOrder(recent_nfc_files); - while (recent_nfc_files.size() > kMaxRecentEntries) - recent_nfc_files.pop_back(); -} - -void wxCemuConfig::Load(XMLConfigParser& parser) -{ - language = parser.get("language", wxLANGUAGE_DEFAULT); - msw_theme = parser.get("msw_theme", msw_theme); - use_discord_presence = parser.get("use_discord_presence", true); - fullscreen_menubar = parser.get("fullscreen_menubar", false); - feral_gamemode = parser.get("feral_gamemode", false); - check_update = parser.get("check_update", check_update); - receive_untested_updates = parser.get("receive_untested_updates", receive_untested_updates); - save_screenshot = parser.get("save_screenshot", save_screenshot); - did_show_vulkan_warning = parser.get("vk_warning", did_show_vulkan_warning); - did_show_graphic_pack_download = parser.get("gp_download", did_show_graphic_pack_download); - did_show_macos_disclaimer = parser.get("macos_disclaimer", did_show_macos_disclaimer); - fullscreen = parser.get("fullscreen", fullscreen); - - window_position.x = parser.get("window_position").get("x", -1); - window_position.y = parser.get("window_position").get("y", -1); - - window_size.x = parser.get("window_size").get("x", -1); - window_size.y = parser.get("window_size").get("y", -1); - window_maximized = parser.get("window_maximized", false); - - pad_open = parser.get("open_pad", false); - pad_position.x = parser.get("pad_position").get("x", -1); - pad_position.y = parser.get("pad_position").get("y", -1); - - pad_size.x = parser.get("pad_size").get("x", -1); - pad_size.y = parser.get("pad_size").get("y", -1); - pad_maximized = parser.get("pad_maximized", false); - - auto gamelist = parser.get("GameList"); - game_list_style = gamelist.get("style", 0); - game_list_column_order = gamelist.get("order", ""); - - show_icon_column = parser.get("show_icon_column", true); - - // return default width if value in config file out of range - auto loadColumnSize = [&gamelist](const char* name, uint32 defaultWidth) { - sint64 val = gamelist.get(name, DefaultColumnSize::name); - if (val < 0 || val > (sint64)std::numeric_limits::max) - return defaultWidth; - return static_cast(val); - }; - column_width.name = loadColumnSize("name_width", DefaultColumnSize::name); - column_width.version = loadColumnSize("version_width", DefaultColumnSize::version); - column_width.dlc = loadColumnSize("dlc_width", DefaultColumnSize::dlc); - column_width.game_time = loadColumnSize("game_time_width", DefaultColumnSize::game_time); - column_width.game_started = loadColumnSize("game_started_width", DefaultColumnSize::game_started); - column_width.region = loadColumnSize("region_width", DefaultColumnSize::region); - column_width.title_id = loadColumnSize("title_id", DefaultColumnSize::title_id); - - recent_launch_files.clear(); - auto launch_parser = parser.get("RecentLaunchFiles"); - for (auto element = launch_parser.get("Entry"); element.valid(); element = launch_parser.get("Entry", element)) - { - const std::string path = element.value(""); - if (path.empty()) - continue; - - try - { - recent_launch_files.emplace_back(path); - } catch (const std::exception&) - { - cemuLog_log(LogType::Force, "config load error: can't load recently launched game file: {}", path); - } - } - - recent_nfc_files.clear(); - auto nfc_parser = parser.get("RecentNFCFiles"); - for (auto element = nfc_parser.get("Entry"); element.valid(); element = nfc_parser.get("Entry", element)) - { - const std::string path = element.value(""); - if (path.empty()) - continue; - try - { - recent_nfc_files.emplace_back(path); - } catch (const std::exception&) - { - cemuLog_log(LogType::Force, "config load error: can't load recently launched nfc file: {}", path); - } - } - - // hotkeys - auto xml_hotkeys = parser.get("Hotkeys"); - hotkeys.modifiers = xml_hotkeys.get("modifiers", sHotkeyCfg{}); - hotkeys.exitFullscreen = xml_hotkeys.get("ExitFullscreen", sHotkeyCfg{uKeyboardHotkey{WXK_ESCAPE}}); - hotkeys.toggleFullscreen = xml_hotkeys.get("ToggleFullscreen", sHotkeyCfg{uKeyboardHotkey{WXK_F11}}); - hotkeys.toggleFullscreenAlt = xml_hotkeys.get("ToggleFullscreenAlt", sHotkeyCfg{uKeyboardHotkey{WXK_CONTROL_M, true}}); // ALT+ENTER - hotkeys.takeScreenshot = xml_hotkeys.get("TakeScreenshot", sHotkeyCfg{uKeyboardHotkey{WXK_F12}}); - hotkeys.toggleFastForward = xml_hotkeys.get("ToggleFastForward", sHotkeyCfg{}); - hotkeys.exitApplication = xml_hotkeys.get("ExitApplication", sHotkeyCfg{}); -#ifdef CEMU_DEBUG_ASSERT - hotkeys.endEmulation = xml_hotkeys.get("EndEmulation", sHotkeyCfg{uKeyboardHotkey{WXK_F5}}); -#endif -} - -void wxCemuConfig::Save(XMLConfigParser& config) -{ - // general settings - config.set("language", language); - config.set("msw_theme", msw_theme); - config.set("use_discord_presence", use_discord_presence); - config.set("fullscreen_menubar", fullscreen_menubar); - config.set("feral_gamemode", feral_gamemode); - config.set("check_update", check_update); - config.set("receive_untested_updates", receive_untested_updates); - config.set("save_screenshot", save_screenshot); - config.set("vk_warning", did_show_vulkan_warning); - config.set("gp_download", did_show_graphic_pack_download); - config.set("macos_disclaimer", did_show_macos_disclaimer); - config.set("fullscreen", fullscreen); - - auto wpos = config.set("window_position"); - wpos.set("x", window_position.x); - wpos.set("y", window_position.y); - auto wsize = config.set("window_size"); - wsize.set("x", window_size.x); - wsize.set("y", window_size.y); - config.set("window_maximized", window_maximized); - - config.set("open_pad", pad_open); - auto ppos = config.set("pad_position"); - ppos.set("x", pad_position.x); - ppos.set("y", pad_position.y); - auto psize = config.set("pad_size"); - psize.set("x", pad_size.x); - psize.set("y", pad_size.y); - config.set("pad_maximized", pad_maximized); - config.set("show_icon_column", show_icon_column); - - auto gamelist = config.set("GameList"); - gamelist.set("style", game_list_style); - gamelist.set("order", game_list_column_order); - gamelist.set("name_width", column_width.name); - gamelist.set("version_width", column_width.version); - gamelist.set("dlc_width", column_width.dlc); - gamelist.set("game_time_width", column_width.game_time); - gamelist.set("game_started_width", column_width.game_started); - gamelist.set("region_width", column_width.region); - gamelist.set("title_id", column_width.title_id); - - auto launch_files_parser = config.set("RecentLaunchFiles"); - for (const auto& entry : recent_launch_files) - { - launch_files_parser.set("Entry", entry.c_str()); - } - - auto nfc_files_parser = config.set("RecentNFCFiles"); - for (const auto& entry : recent_nfc_files) - { - nfc_files_parser.set("Entry", entry.c_str()); - } - - // hotkeys - auto xml_hotkeys = config.set("Hotkeys"); - xml_hotkeys.set("modifiers", hotkeys.modifiers); - xml_hotkeys.set("ExitFullscreen", hotkeys.exitFullscreen); - xml_hotkeys.set("ToggleFullscreen", hotkeys.toggleFullscreen); - xml_hotkeys.set("ToggleFullscreenAlt", hotkeys.toggleFullscreenAlt); - xml_hotkeys.set("TakeScreenshot", hotkeys.takeScreenshot); - xml_hotkeys.set("ToggleFastForward", hotkeys.toggleFastForward); - xml_hotkeys.set("ExitApplication", hotkeys.exitApplication); -} diff --git a/src/gui/wxgui/wxCemuConfig.h b/src/gui/wxgui/wxCemuConfig.h deleted file mode 100644 index 9fede16c..00000000 --- a/src/gui/wxgui/wxCemuConfig.h +++ /dev/null @@ -1,154 +0,0 @@ -#pragma once - -#include "config/XMLConfig.h" -#include "util/math/vector2.h" - -#include "wxgui.h" - -namespace DefaultColumnSize -{ - enum : uint32 - { - name = 500u, - version = 60u, - dlc = 50u, - game_time = 140u, - game_started = 160u, - region = 80u, - title_id = 160u - }; -}; - -enum class MSWThemeOption : int -{ - kAuto = 0, - kLight = 1, - kDark = 2, -}; -ENABLE_ENUM_ITERATORS(MSWThemeOption, MSWThemeOption::kAuto, MSWThemeOption::kDark); - -typedef union -{ - struct - { - uint16 key : 13; // enough bits for all keycodes - uint16 alt : 1; - uint16 ctrl : 1; - uint16 shift : 1; - }; - uint16 raw; -} uKeyboardHotkey; - -typedef sint16 ControllerHotkey_t; - -struct sHotkeyCfg -{ - static constexpr uint8 keyboardNone{WXK_NONE}; - static constexpr sint8 controllerNone{-1}; // no enums to work with, but buttons start from 0 - - uKeyboardHotkey keyboard{keyboardNone}; - ControllerHotkey_t controller{controllerNone}; - - /* for defaults */ - sHotkeyCfg(const uKeyboardHotkey& keyboard = {keyboardNone}, const ControllerHotkey_t& controller = {controllerNone}) : keyboard(keyboard), controller(controller) {}; - - /* for reading from xml */ - sHotkeyCfg(const char* xml_values) - { - std::istringstream iss(xml_values); - iss >> keyboard.raw >> controller; - } -}; - -template<> -struct fmt::formatter : formatter -{ - template - auto format(const sHotkeyCfg c, FormatContext& ctx) const - { - std::string xml_values = fmt::format("{} {}", c.keyboard.raw, c.controller); - return formatter::format(xml_values, ctx); - } -}; - -struct wxCemuConfig -{ - ConfigValue language{wxLANGUAGE_DEFAULT}; - ConfigValue msw_theme { static_cast(MSWThemeOption::kAuto) }; - ConfigValue use_discord_presence{true}; - ConfigValue fullscreen{ false }; - ConfigValue fullscreen_menubar{false}; - ConfigValue feral_gamemode{false}; - - // max 15 entries - static constexpr size_t kMaxRecentEntries = 15; - std::vector recent_launch_files; - std::vector recent_nfc_files; - - Vector2i window_position{-1, -1}; - Vector2i window_size{-1, -1}; - ConfigValue window_maximized; - - ConfigValue pad_open; - Vector2i pad_position{-1, -1}; - Vector2i pad_size{-1, -1}; - ConfigValue pad_maximized; - - ConfigValue check_update{true}; - ConfigValue receive_untested_updates{false}; - ConfigValue save_screenshot{true}; - - ConfigValue did_show_vulkan_warning{false}; - ConfigValue did_show_graphic_pack_download{false}; // no longer used but we keep the config value around in case people downgrade Cemu. Despite the name this was used for the Getting Started dialog - ConfigValue did_show_macos_disclaimer{false}; // no longer used - - ConfigValue show_icon_column{true}; - - int game_list_style = 0; - std::string game_list_column_order; - struct - { - uint32 name = DefaultColumnSize::name; - uint32 version = DefaultColumnSize::version; - uint32 dlc = DefaultColumnSize::dlc; - uint32 game_time = DefaultColumnSize::game_time; - uint32 game_started = DefaultColumnSize::game_started; - uint32 region = DefaultColumnSize::region; - uint32 title_id = 0; - } column_width{}; - - // hotkeys - struct - { - sHotkeyCfg modifiers; - sHotkeyCfg toggleFullscreen; - sHotkeyCfg toggleFullscreenAlt; - sHotkeyCfg exitFullscreen; - sHotkeyCfg takeScreenshot; - sHotkeyCfg toggleFastForward; - sHotkeyCfg exitApplication; -#ifdef CEMU_DEBUG_ASSERT - sHotkeyCfg endEmulation; -#endif - } hotkeys{}; - - void AddRecentlyLaunchedFile(std::string_view file); - void AddRecentNfcFile(std::string_view file); - - void Load(XMLConfigParser& parser); - void Save(XMLConfigParser& parser); -}; - -typedef XMLChildConfig XMLWxCemuConfig_t; - -extern XMLWxCemuConfig_t g_wxConfig; - -inline XMLWxCemuConfig_t& GetWxGuiConfigHandle() -{ - return g_wxConfig; -} - -inline wxCemuConfig& GetWxGUIConfig() -{ - return GetWxGuiConfigHandle().Data(); -} diff --git a/src/gui/wxgui/wxHelper.h b/src/gui/wxgui/wxHelper.h deleted file mode 100644 index 9bbaddac..00000000 --- a/src/gui/wxgui/wxHelper.h +++ /dev/null @@ -1,39 +0,0 @@ -#pragma once -#include -#include -#include - -namespace wxHelper -{ - inline fs::path MakeFSPath(const wxString& str) - { - auto tmpUtf8 = str.ToUTF8(); - auto sv = std::basic_string_view((const char8_t*)tmpUtf8.data(), tmpUtf8.length()); - return fs::path(sv); - } - - inline wxString FromPath(const fs::path& path) - { - std::string str = _pathToUtf8(path); - return wxString::FromUTF8(str); - } - - inline wxColour CalculateAccentColour(const wxColour& bgColour) - { - const uint32 bgLightness = (bgColour.GetRed() + bgColour.GetGreen() + bgColour.GetBlue()) / 3; - const bool isDarkTheme = bgLightness < 128; - wxColour bgColourSecondary = bgColour.ChangeLightness(isDarkTheme ? 110 : 90); // color for even rows - // for very light themes we'll use a blue tint to match the older Windows Cemu look - if (bgLightness > 250) - bgColourSecondary = wxColour(bgColour.Red() - 13, bgColour.Green() - 6, bgColour.Blue() - 2); - return bgColourSecondary; - } - - static wxBitmap LoadThemedBitmapFromPNG(const uint8* data, size_t size, const wxColour& tint) - { - wxMemoryInputStream strm(data, size); - wxImage img(strm, wxBITMAP_TYPE_PNG); - img.Replace(0x00, 0x00, 0x00, tint.Red(), tint.Green(), tint.Blue()); - return wxBitmap(img); - } -}; diff --git a/src/gui/wxgui/wxWindowSystem.cpp b/src/gui/wxgui/wxWindowSystem.cpp deleted file mode 100644 index bb730db8..00000000 --- a/src/gui/wxgui/wxWindowSystem.cpp +++ /dev/null @@ -1,352 +0,0 @@ -#include "input/HotkeySettings.h" -#include "interface/WindowSystem.h" - -#include "helpers/wxHelpers.h" - -#if BOOST_OS_LINUX || BOOST_OS_BSD -#include -#include -#include -#include -#include -#ifdef HAS_WAYLAND -#include -#endif -#endif - -#if BOOST_OS_MACOS -#include -#endif - -#include "wxgui/wxgui.h" -#include "wxgui/CemuApp.h" -#include "wxgui/MainWindow.h" -#include "Cafe/HW/Latte/Core/Latte.h" -#include "config/ActiveSettings.h" -#include "config/NetworkSettings.h" -#include "config/CemuConfig.h" -#include "Cafe/HW/Latte/Renderer/Renderer.h" -#include "Cafe/CafeSystem.h" - -WindowSystem::WindowInfo g_window_info{}; - -std::shared_mutex g_mutex; -MainWindow* g_mainFrame = nullptr; - -#if BOOST_OS_WINDOWS -void _wxLaunch() -{ - SetThreadName("MainThread_UI"); - wxEntry(); -} -#endif - -void WindowSystem::Create() -{ - SetThreadName("cemu"); -#if BOOST_OS_WINDOWS - // on Windows wxWidgets there is a bug where wxDirDialog->ShowModal will deadlock in Windows internals somehow - // moving the UI thread off the main thread fixes this - std::thread t = std::thread(_wxLaunch); - t.join(); -#else - int argc = 0; - char* argv[1]{}; - wxEntry(argc, argv); -#endif -} - -void WindowSystem::ShowErrorDialog(std::string_view message, std::string_view title, std::optional /*errorId*/) -{ - wxString caption; - if (title.empty()) - caption = wxASCII_STR(wxMessageBoxCaptionStr); - else - caption = wxString::FromUTF8(title); - wxMessageBox(wxString::FromUTF8(message), caption, wxOK | wxCENTRE | wxICON_ERROR); -} - -WindowSystem::WindowInfo& WindowSystem::GetWindowInfo() -{ - return g_window_info; -} - -void WindowSystem::UpdateWindowTitles(bool isIdle, bool isLoading, double fps) -{ - std::string windowText; - windowText = BUILD_VERSION_WITH_NAME_STRING; - - if (isIdle) - { - if (g_mainFrame) - g_mainFrame->AsyncSetTitle(windowText); - return; - } - if (isLoading) - { - windowText.append(" - Loading..."); - if (g_mainFrame) - g_mainFrame->AsyncSetTitle(windowText); - return; - } - - const char* renderer = ""; - if (g_renderer) - { - switch (g_renderer->GetType()) - { - case RendererAPI::OpenGL: - renderer = "[OpenGL]"; - break; - case RendererAPI::Vulkan: - renderer = "[Vulkan]"; - break; - case RendererAPI::Metal: - renderer = "[Metal]"; - break; - default: break; - } - } - - // get GPU vendor/mode - const char* graphicMode = "[Generic]"; - if (LatteGPUState.glVendor == GLVENDOR_AMD) - graphicMode = "[AMD GPU]"; - else if (LatteGPUState.glVendor == GLVENDOR_INTEL) - graphicMode = "[Intel GPU]"; - else if (LatteGPUState.glVendor == GLVENDOR_NVIDIA) - graphicMode = "[NVIDIA GPU]"; - else if (LatteGPUState.glVendor == GLVENDOR_APPLE) - graphicMode = "[Apple GPU]"; - - const uint64 titleId = CafeSystem::GetForegroundTitleId(); - windowText.append(fmt::format(" - FPS: {:.2f} {} {} [TitleId: {:08x}-{:08x}]", (double)fps, renderer, graphicMode, (uint32)(titleId >> 32), (uint32)(titleId & 0xFFFFFFFF))); - - if (ActiveSettings::IsOnlineEnabled()) - { - if (ActiveSettings::GetNetworkService() == NetworkService::Nintendo) - windowText.append(" [Online]"); - else if (ActiveSettings::GetNetworkService() == NetworkService::Pretendo) - windowText.append(" [Online-Pretendo]"); - else if (ActiveSettings::GetNetworkService() == NetworkService::Custom) - windowText.append(" [Online-" + GetNetworkConfig().networkname.GetValue() + "]"); - } - windowText.append(" "); - windowText.append(CafeSystem::GetForegroundTitleName()); - // append region - CafeConsoleRegion region = CafeSystem::GetForegroundTitleRegion(); - uint16 titleVersion = CafeSystem::GetForegroundTitleVersion(); - if (region == CafeConsoleRegion::JPN) - windowText.append(fmt::format(" [JP v{}]", titleVersion)); - else if (region == CafeConsoleRegion::USA) - windowText.append(fmt::format(" [US v{}]", titleVersion)); - else if (region == CafeConsoleRegion::EUR) - windowText.append(fmt::format(" [EU v{}]", titleVersion)); - else - windowText.append(fmt::format(" [v{}]", titleVersion)); - - std::shared_lock lock(g_mutex); - if (g_mainFrame) - { - g_mainFrame->AsyncSetTitle(windowText); - auto* pad = g_mainFrame->GetPadView(); - if (pad) - pad->AsyncSetTitle(fmt::format("{} - FPS: {:.02f}", _("GamePad View").utf8_string(), fps)); - } -} - -void WindowSystem::GetWindowSize(int& w, int& h) -{ - w = g_window_info.width; - h = g_window_info.height; -} - -void WindowSystem::GetPadWindowSize(int& w, int& h) -{ - if (g_window_info.pad_open) - { - w = g_window_info.pad_width; - h = g_window_info.pad_height; - } - else - { - w = 0; - h = 0; - } -} - -void WindowSystem::GetWindowPhysSize(int& w, int& h) -{ - w = g_window_info.phys_width; - h = g_window_info.phys_height; -} - -void WindowSystem::GetPadWindowPhysSize(int& w, int& h) -{ - if (g_window_info.pad_open) - { - w = g_window_info.phys_pad_width; - h = g_window_info.phys_pad_height; - } - else - { - w = 0; - h = 0; - } -} - -double WindowSystem::GetWindowDPIScale() -{ - return g_window_info.dpi_scale; -} - -double WindowSystem::GetPadDPIScale() -{ - return g_window_info.pad_open ? g_window_info.pad_dpi_scale.load() : 1.0; -} - -bool WindowSystem::IsPadWindowOpen() -{ - return g_window_info.pad_open; -} - -bool WindowSystem::IsKeyDown(uint32 key) -{ - return g_window_info.get_keystate(key); -} - -bool WindowSystem::IsKeyDown(PlatformKeyCodes platformKey) -{ - uint32 key = 0; - - switch (platformKey) - { -#if BOOST_OS_WINDOWS - case PlatformKeyCodes::LCONTROL: - key = VK_LCONTROL; - break; - case PlatformKeyCodes::RCONTROL: - key = VK_RCONTROL; - break; - case PlatformKeyCodes::TAB: - key = VK_TAB; - break; - case PlatformKeyCodes::ESCAPE: - key = VK_ESCAPE; - break; -#elif BOOST_OS_LINUX || BOOST_OS_BSD - case PlatformKeyCodes::LCONTROL: - key = GDK_KEY_Control_L; - break; - case PlatformKeyCodes::RCONTROL: - key = GDK_KEY_Control_R; - break; - case PlatformKeyCodes::TAB: - key = GDK_KEY_Tab; - break; - case PlatformKeyCodes::ESCAPE: - key = GDK_KEY_Escape; - break; -#elif BOOST_OS_MACOS - case PlatformKeyCodes::LCONTROL: - key = kVK_Control; - break; - case PlatformKeyCodes::RCONTROL: - key = kVK_RightControl; - break; - case PlatformKeyCodes::TAB: - key = kVK_Tab; - break; - case PlatformKeyCodes::ESCAPE: - key = kVK_Escape; - break; -#endif - default: - return false; - } - - return WindowSystem::IsKeyDown(key); -} - -std::string WindowSystem::GetKeyCodeName(uint32 button) -{ -#if BOOST_OS_WINDOWS - LONG scan_code = MapVirtualKeyA((UINT)button, MAPVK_VK_TO_VSC_EX); - if (HIBYTE(scan_code)) - scan_code |= 0x100; - - // because MapVirtualKey strips the extended bit for some keys - switch (button) - { - case VK_LEFT: - case VK_UP: - case VK_RIGHT: - case VK_DOWN: // arrow keys - case VK_PRIOR: - case VK_NEXT: // page up and page down - case VK_END: - case VK_HOME: - case VK_INSERT: - case VK_DELETE: - case VK_DIVIDE: // numpad slash - case VK_NUMLOCK: - { - scan_code |= 0x100; // set extended bit - break; - } - } - - scan_code <<= 16; - - char key_name[128]; - if (GetKeyNameTextA(scan_code, key_name, std::size(key_name)) != 0) - return key_name; - else - return fmt::format("key_{}", button); -#elif BOOST_OS_LINUX || BOOST_OS_BSD - return gdk_keyval_name(button); -#else - return fmt::format("key_{}", button); -#endif -} - -bool WindowSystem::InputConfigWindowHasFocus() -{ - return g_inputConfigWindowHasFocus; -} - -void WindowSystem::NotifyGameLoaded() -{ - std::shared_lock lock(g_mutex); - if (g_mainFrame) - { - g_mainFrame->OnGameLoaded(); - g_mainFrame->UpdateSettingsAfterGameLaunch(); - } -} - -void WindowSystem::NotifyGameExited() -{ - std::shared_lock lock(g_mutex); - if (g_mainFrame) - g_mainFrame->RestoreSettingsAfterGameExited(); -} - -void WindowSystem::RefreshGameList() -{ - std::shared_lock lock(g_mutex); - if (g_mainFrame) - { - g_mainFrame->RequestGameListRefresh(); - } -} - -void WindowSystem::CaptureInput(const ControllerState& currentState, const ControllerState& lastState) -{ - HotkeySettings::CaptureInput(currentState, lastState); -} - -bool WindowSystem::IsFullScreen() -{ - return g_window_info.is_fullscreen; -} diff --git a/src/gui/wxgui/wxcomponents/checktree.cpp b/src/gui/wxgui/wxcomponents/checktree.cpp deleted file mode 100644 index d7d56b69..00000000 --- a/src/gui/wxgui/wxcomponents/checktree.cpp +++ /dev/null @@ -1,613 +0,0 @@ -#include "wxgui/wxcomponents/checktree.h" - -#include -#include -#include -#include - -wxDEFINE_EVENT(wxEVT_CHECKTREE_FOCUS, wxTreeEvent); -wxDEFINE_EVENT(wxEVT_CHECKTREE_CHOICE, wxTreeEvent); - -//IMPLEMENT_DYNAMIC_CLASS(wxCheckTree, wxTreeCtrl) - -bool on_check_or_label(int flags) -{ - return flags & (wxTREE_HITTEST_ONITEMSTATEICON | wxTREE_HITTEST_ONITEMLABEL) ? true : false; -} - -bool on_check(int flags) -{ - return flags & (wxTREE_HITTEST_ONITEMSTATEICON) ? true : false; -} - -bool on_label(int flags) -{ - return flags & (wxTREE_HITTEST_ONITEMLABEL) ? true : false; -} - -void unhighlight(wxTreeCtrl* m_treeCtrl1, wxTreeItemId& id) -{ - if (!id.IsOk()) - return; - - const int state = m_treeCtrl1->GetItemState(id); - if (wxCheckTree::UNCHECKED <= state && state < wxCheckTree::UNCHECKED_DISABLED) - { - m_treeCtrl1->SetItemState(id, wxCheckTree::UNCHECKED); - } - else if (wxCheckTree::CHECKED <= state && state < wxCheckTree::CHECKED_DISABLED) - { - m_treeCtrl1->SetItemState(id, wxCheckTree::CHECKED); - } -} - - -void mohighlight(wxTreeCtrl* m_treeCtrl1, wxTreeItemId& id, bool toggle) -{ - if (!id.IsOk()) - return; - - const int i = m_treeCtrl1->GetItemState(id); - if (i < 0) - return; - - bool is_checked = false; - if (wxCheckTree::UNCHECKED <= i && i < wxCheckTree::UNCHECKED_DISABLED) - { - m_treeCtrl1->SetItemState(id, toggle ? wxCheckTree::CHECKED_MOUSE_OVER : wxCheckTree::UNCHECKED_MOUSE_OVER); - is_checked = true; - } - else if (wxCheckTree::CHECKED <= i && i < wxCheckTree::CHECKED_DISABLED) - { - m_treeCtrl1->SetItemState(id, toggle ? wxCheckTree::UNCHECKED_MOUSE_OVER : wxCheckTree::CHECKED_MOUSE_OVER); - is_checked = false; - } - - if (toggle) - { - wxTreeEvent event2(wxEVT_CHECKTREE_CHOICE, m_treeCtrl1, id); - event2.SetExtraLong(is_checked ? 1 : 0); - m_treeCtrl1->ProcessWindowEvent(event2); - } -} - -void ldhighlight(wxTreeCtrl* m_treeCtrl1, wxTreeItemId& id) -{ - if (!id.IsOk()) - return; - - const int i = m_treeCtrl1->GetItemState(id); - if (wxCheckTree::UNCHECKED <= i && i < wxCheckTree::UNCHECKED_DISABLED) - { - m_treeCtrl1->SetItemState(id, wxCheckTree::UNCHECKED_LEFT_DOWN); - } - else if (wxCheckTree::CHECKED <= i && i < wxCheckTree::CHECKED_DISABLED) - { - m_treeCtrl1->SetItemState(id, wxCheckTree::CHECKED_LEFT_DOWN); - } -} - -wxIMPLEMENT_CLASS(wxCheckTree, wxTreeCtrl); - -wxCheckTree::wxCheckTree() -{ - Init(); -} - -wxCheckTree::wxCheckTree(wxWindow* parent, const wxWindowID id, const wxPoint& pos, const wxSize& size, long style) - : wxTreeCtrl(parent, id, pos, size, style) -{ - Init(); -} - - -void wxCheckTree::Init() -{ - int width = wxRendererNative::Get().GetCheckBoxSize(this).GetWidth(); - int height = wxRendererNative::Get().GetCheckBoxSize(this).GetHeight(); - - auto states = new wxImageList(width, height, true); - - wxBitmap unchecked_bmp(width, height); - wxBitmap unchecked_mouse_over_bmp(width, height); - wxBitmap unchecked_left_down_bmp(width, height); - wxBitmap unchecked_disabled_bmp(width, height); - wxBitmap checked_bmp(width, height); - wxBitmap checked_mouse_over_bmp(width, height); - wxBitmap checked_left_down_bmp(width, height); - wxBitmap checked_disabled_bmp(width, height); - - wxMemoryDC renderer_dc; - - // Unchecked - renderer_dc.SelectObject(unchecked_bmp); - renderer_dc.SetBackground(*wxTheBrushList->FindOrCreateBrush(GetBackgroundColour())); - renderer_dc.Clear(); - wxRendererNative::Get().DrawCheckBox(this, renderer_dc, wxRect(0, 0, width, height), wxCONTROL_NONE); - - // Unchecked Mouse Over - renderer_dc.SelectObject(unchecked_mouse_over_bmp); - renderer_dc.SetBackground(*wxTheBrushList->FindOrCreateBrush(GetBackgroundColour())); - renderer_dc.Clear(); - wxRendererNative::Get().DrawCheckBox(this, renderer_dc, wxRect(0, 0, width, height), wxCONTROL_CURRENT); - - // Unchecked and Disabled - renderer_dc.SelectObject(unchecked_disabled_bmp); - renderer_dc.SetBackground(*wxTheBrushList->FindOrCreateBrush(GetBackgroundColour())); - renderer_dc.Clear(); - wxRendererNative::Get().DrawCheckBox(this, renderer_dc, wxRect(0, 0, width, height), wxCONTROL_DISABLED); - - // Unchecked Left Down - renderer_dc.SelectObject(unchecked_left_down_bmp); - renderer_dc.SetBackground(*wxTheBrushList->FindOrCreateBrush(GetBackgroundColour())); - renderer_dc.Clear(); - wxRendererNative::Get().DrawCheckBox(this, renderer_dc, wxRect(0, 0, width, height), wxCONTROL_CURRENT | wxCONTROL_PRESSED); - - // Checked - renderer_dc.SelectObject(checked_bmp); - renderer_dc.SetBackground(*wxTheBrushList->FindOrCreateBrush(GetBackgroundColour())); - renderer_dc.Clear(); - wxRendererNative::Get().DrawCheckBox(this, renderer_dc, wxRect(0, 0, width, height), wxCONTROL_CHECKED); - - // Checked Mouse Over - renderer_dc.SelectObject(checked_mouse_over_bmp); - renderer_dc.SetBackground(*wxTheBrushList->FindOrCreateBrush(GetBackgroundColour())); - renderer_dc.Clear(); - wxRendererNative::Get().DrawCheckBox(this, renderer_dc, wxRect(0, 0, width, height), wxCONTROL_CHECKED | wxCONTROL_CURRENT); - - // Checked Left Down - renderer_dc.SelectObject(checked_left_down_bmp); - renderer_dc.SetBackground(*wxTheBrushList->FindOrCreateBrush(GetBackgroundColour())); - renderer_dc.Clear(); - wxRendererNative::Get().DrawCheckBox(this, renderer_dc, wxRect(0, 0, width, height), wxCONTROL_CHECKED | wxCONTROL_CURRENT | wxCONTROL_PRESSED); - - // Checked and Disabled - renderer_dc.SelectObject(checked_disabled_bmp); - renderer_dc.SetBackground(*wxTheBrushList->FindOrCreateBrush(GetBackgroundColour())); - renderer_dc.Clear(); - wxRendererNative::Get().DrawCheckBox(this, renderer_dc, wxRect(0, 0, width, height), wxCONTROL_CHECKED | wxCONTROL_DISABLED); - - renderer_dc.SelectObject(wxNullBitmap); - - states->Add(unchecked_bmp); - states->Add(unchecked_mouse_over_bmp); - states->Add(unchecked_left_down_bmp); - states->Add(unchecked_disabled_bmp); - states->Add(checked_bmp); - states->Add(checked_mouse_over_bmp); - states->Add(checked_left_down_bmp); - states->Add(checked_disabled_bmp); - - AssignStateImageList(states); - - Connect(wxEVT_TREE_SEL_CHANGING, wxTreeEventHandler( wxCheckTree::On_Tree_Sel_Changed ), nullptr, this); - - Connect(wxEVT_CHAR, wxKeyEventHandler( wxCheckTree::On_Char ), nullptr, this); - Connect(wxEVT_KEY_DOWN, wxKeyEventHandler( wxCheckTree::On_KeyDown ), nullptr, this); - Connect(wxEVT_KEY_UP, wxKeyEventHandler( wxCheckTree::On_KeyUp ), nullptr, this); - - Connect(wxEVT_ENTER_WINDOW, wxMouseEventHandler( wxCheckTree::On_Mouse_Enter_Tree ), nullptr, this); - Connect(wxEVT_LEAVE_WINDOW, wxMouseEventHandler( wxCheckTree::On_Mouse_Leave_Tree ), nullptr, this); - Connect(wxEVT_LEFT_DCLICK, wxMouseEventHandler( wxCheckTree::On_Left_DClick ), nullptr, this); - Connect(wxEVT_LEFT_DOWN, wxMouseEventHandler( wxCheckTree::On_Left_Down ), nullptr, this); - Connect(wxEVT_LEFT_UP, wxMouseEventHandler( wxCheckTree::On_Left_Up ), nullptr, this); - Connect(wxEVT_MOTION, wxMouseEventHandler( wxCheckTree::On_Mouse_Motion ), nullptr, this); - Connect(wxEVT_MOUSEWHEEL, wxMouseEventHandler( wxCheckTree::On_Mouse_Wheel ), nullptr, this); - - Connect(wxEVT_SET_FOCUS, wxFocusEventHandler( wxCheckTree::On_Tree_Focus_Set ), nullptr, this); - Connect(wxEVT_KILL_FOCUS, wxFocusEventHandler( wxCheckTree::On_Tree_Focus_Lost ), nullptr, this); -} - -void wxCheckTree::Sort(const wxTreeItemId& node, bool recursive) -{ - if (recursive) - { - wxTreeItemIdValue cookie; - for(auto it = GetFirstChild(node, cookie); it.IsOk(); it = GetNextChild(it, cookie)) - { - Sort(it, true); - } - } - - if(GetChildrenCount(node, false) > 0) - this->SortChildren(node); -} - -int wxCheckTree::OnCompareItems(const wxTreeItemId& item1, const wxTreeItemId& item2) -{ - const bool check1 = GetChildrenCount(item1, false) == 0; - const bool check2 = GetChildrenCount(item2, false) == 0; - - if (!check1 && check2) - return -1; - - if (check1 && !check2) - return 1; - - return GetItemText(item1).Lower().compare(GetItemText(item2).Lower()); -} - - -void wxCheckTree::SetItemTextColour(const wxTreeItemId& item, const wxColour& col) -{ - const auto it = m_colors.find(item); - if (it == m_colors.end()) - m_colors.emplace(std::pair(item, col)); - else - m_colors[item] = col; - - wxTreeCtrl::SetItemTextColour(item, col); -} - -bool wxCheckTree::EnableCheckBox(const wxTreeItemId& item, bool enable) -{ - if (!item.IsOk()) - return false; - - const int state = GetItemState(item); - - if (state < 0 || state > CHECKED_DISABLED) - return false; - - if (enable) - { - if (state == UNCHECKED_DISABLED) - SetItemState(item, UNCHECKED); - else if (state == CHECKED_DISABLED) - SetItemState(item, CHECKED); - - const auto it = m_colors.find(item); - if (it != m_colors.end()) - { - SetItemTextColour(item, it->second); - m_colors.erase(it); - } - - return true; - } - if (state == UNCHECKED_DISABLED || state == CHECKED_DISABLED) - { - //don't disable a second time or we'll lose the - //text color information. - return true; - } - - if (state == UNCHECKED || state == UNCHECKED_MOUSE_OVER || state == UNCHECKED_LEFT_DOWN) - SetItemState(item, UNCHECKED_DISABLED); - else if (state == CHECKED || state == CHECKED_MOUSE_OVER || state == CHECKED_LEFT_DOWN) - SetItemState(item, CHECKED_DISABLED); - - const wxColour col = GetItemTextColour(item); - SetItemTextColour(item, wxColour(161, 161, 146)); - m_colors[item] = col; - return true; -} - -bool wxCheckTree::DisableCheckBox(const wxTreeItemId& item) -{ - return EnableCheckBox(item, false); -} - -void wxCheckTree::MakeCheckable(const wxTreeItemId& item, bool state) -{ - if (!item.IsOk()) - return; - - const int i = GetItemState(item); - if (i < 0 || i > CHECKED_DISABLED) - SetItemState(item, state ? CHECKED : UNCHECKED); -} - -bool wxCheckTree::IsCheckable(const wxTreeItemId& item) -{ - if (!item.IsOk()) - return false; - - const int i = GetItemState(item); - return i >= 0 && i <= CHECKED_DISABLED; -} - -void wxCheckTree::Check(const wxTreeItemId& item, bool state) -{ - if (!item.IsOk()) - return; - - const int old_state = GetItemState(item); - if (UNCHECKED <= old_state && old_state <= CHECKED_DISABLED) - { - const bool enable = !(old_state == UNCHECKED_DISABLED || old_state == CHECKED_DISABLED); - int check = enable ? CHECKED : CHECKED_DISABLED; - int uncheck = enable ? UNCHECKED : UNCHECKED_DISABLED; - const int new_state = state ? check : uncheck; - - if (new_state != old_state) - { - SetItemState(item, new_state); - } - } -} - -void wxCheckTree::Uncheck(const wxTreeItemId& item) -{ - Check(item, false); -} - -void wxCheckTree::On_Tree_Sel_Changed(wxTreeEvent& event) -{ - wxTreeItemId id = event.GetItem(); - - unhighlight(this, last_kf); - mohighlight(this, id, false); - last_kf = id; - - event.Skip(); -} - -void wxCheckTree::On_Char(wxKeyEvent& event) -{ - if (!GetSelection().IsOk() && GetCount() > 0) - { - //If there is no selection, any keypress should just select the first item - wxTreeItemIdValue cookie; - const auto new_item = HasFlag(wxTR_HIDE_ROOT) ? GetFirstChild(GetRootItem(), cookie) : GetRootItem(); - SelectItem(new_item); - last_kf = new_item; - return; - } - - event.Skip(); -} - -void wxCheckTree::On_KeyDown(wxKeyEvent& event) -{ - if (event.GetKeyCode() == WXK_SPACE) - { - last_kf = this->GetSelection(); - ldhighlight(this, last_kf); - } - - event.Skip(); -} - -void wxCheckTree::On_KeyUp(wxKeyEvent& event) -{ - if (event.GetKeyCode() == WXK_SPACE) - { - //last_kf = this->GetSelection(); - mohighlight(this, last_kf, true); - } - else if (event.GetKeyCode() == WXK_ESCAPE) - { - unhighlight(this, last_kf); - last_kf = {}; - Unselect(); - } - - event.Skip(); -} - - -void wxCheckTree::On_Mouse_Enter_Tree(wxMouseEvent& event) -{ - if (event.LeftIsDown()) - { - mouse_entered_tree_with_left_down = true; - } -} - -void wxCheckTree::On_Mouse_Leave_Tree(wxMouseEvent& event) -{ - unhighlight(this, last_mouse_over); - unhighlight(this, last_left_down); - last_mouse_over = {}; - last_left_down = {}; -} - -void wxCheckTree::On_Left_DClick(wxMouseEvent& event) -{ - int flags; - - HitTest(event.GetPosition(), flags); - - //double clicks on buttons can be annoying, so we'll ignore those - //but all other double clicks will just have 1 more click added to them - - //without this, the check boxes are not as responsive as they should be - if (!(flags & wxTREE_HITTEST_ONITEMBUTTON)) - { - On_Left_Down(event); - On_Left_Up(event); - } -} - -void wxCheckTree::On_Left_Down(wxMouseEvent& event) -{ - int flags; - wxTreeItemId id = HitTest(event.GetPosition(), flags); - if (!id.IsOk()) - return; - - int i = GetItemState(id); - - if (id.IsOk() && i >= 0 && on_check(flags)) - { - last_left_down = id; - ldhighlight(this, id); - } - else if (on_label(flags)) - event.Skip(); -} - -void wxCheckTree::On_Left_Up(wxMouseEvent& event) -{ - SetFocus(); - - int flags; - wxTreeItemId id = HitTest(event.GetPosition(), flags); - if (!id.IsOk()) - return; - - int i = GetItemState(id); - - if (mouse_entered_tree_with_left_down) - { - mouse_entered_tree_with_left_down = false; - - if (i >= 0 && on_check(flags)) - { - mohighlight(this, id, false); - last_mouse_over = id; - } - } - else if (id.IsOk()) - { - if (flags & wxTREE_HITTEST_ONITEMBUTTON && ItemHasChildren(id)) - { - if (IsExpanded(id)) - { - Collapse(id); - } - else - { - Expand(id); - } - } - else if (i >= 0 && on_check(flags)) - { - if (id != last_left_down) - { - unhighlight(this, last_left_down); - mohighlight(this, id, false); - } - else - { - mohighlight(this, id, true); - } - - last_left_down = wxTreeItemId(); - last_mouse_over = id; - } - else if (on_label(flags)) - { - event.Skip(); - } - else - { - unhighlight(this, last_left_down); - unhighlight(this, last_mouse_over); - last_left_down = wxTreeItemId(); - last_mouse_over = wxTreeItemId(); - } - } - else - { - //id is not ok - unhighlight(this, last_left_down); - unhighlight(this, last_mouse_over); - last_left_down = wxTreeItemId(); - last_mouse_over = wxTreeItemId(); - } -} - -void wxCheckTree::On_Mouse_Motion(wxMouseEvent& event) -{ - if (mouse_entered_tree_with_left_down) - { - //just ignore everything until the left button is released - return; - } - - int flags; - wxTreeItemId id = HitTest(event.GetPosition(), flags); - - if (!id.IsOk()) - { - unhighlight(this, last_mouse_over); - last_mouse_over = {}; - } - else if (event.LeftIsDown() && last_left_down.IsOk()) - { - //to match the behavior of ordinary check boxes, - //if we've moved to a new item while holding the mouse button down - //we want to set the item where the left down click occured to have - //mouse over highlight. And if we return to the box where the - //left down occured, we want to return it to the having the left down highlight - - //I don't understand why this is the behavior - //of ordinary check boxes, but I'm goin to match it anyway. - - if (id == last_left_down) - { - const auto state = GetItemState(last_left_down); - if (state != UNCHECKED_LEFT_DOWN && state != CHECKED_LEFT_DOWN) - { - ldhighlight(this, last_left_down); - } - } - else - mohighlight(this, last_left_down, false); - } - else - { - //4 cases 1 we're still on the same item, but we've moved off the state icon or label - // 2 we're still on the same item and on the state icon or label - do nothing - // 3 we're on a new item but not on its state icon or label (or the new item has no state) - // 4 we're on a new item, it has a state icon, and we're on the state icon or label - - const int state = GetItemState(id); - if (id == last_mouse_over) - { - if (state < 0 || !on_check(flags)) - { - unhighlight(this, last_mouse_over); - last_mouse_over = {}; - } - } - else - { - if (state < 0 || !on_check(flags)) - { - unhighlight(this, last_mouse_over); - last_mouse_over = {}; - } - else - { - unhighlight(this, last_mouse_over); - mohighlight(this, id, false); - last_mouse_over = id; - } - } - } -} - -void wxCheckTree::On_Mouse_Wheel(wxMouseEvent& event) -{ - event.Skip(); -} - -void wxCheckTree::On_Tree_Focus_Set(wxFocusEvent& event) -{ - //event.Skip(); - - //skipping this event will set the last selected item - //to be highlighted and I want the tree items to only be - //highlighted by keyboard actions. -} - -void wxCheckTree::On_Tree_Focus_Lost(wxFocusEvent& event) -{ - unhighlight(this, last_kf); - Unselect(); - event.Skip(); -} - -void wxCheckTree::SetFocusFromKbd() -{ - if (last_kf.IsOk()) - SelectItem(last_kf); - - wxTreeEvent event2(wxEVT_CHECKTREE_FOCUS, this, wxTreeItemId()); - ProcessWindowEvent(event2); - - wxWindow::SetFocusFromKbd(); -} diff --git a/src/gui/wxgui/wxcomponents/checktree.h b/src/gui/wxgui/wxcomponents/checktree.h deleted file mode 100644 index eb620b8b..00000000 --- a/src/gui/wxgui/wxcomponents/checktree.h +++ /dev/null @@ -1,105 +0,0 @@ -#ifndef checktree_H_INCLUDED -#define checktree_H_INCLUDED - -// credits to: https://forums.wxwidgets.org/viewtopic.php?t=39582 - -#include "wx/treectrl.h" -#include - -#define WXMAKINGDLL_CHECKTREE - -// dll export macros -#ifdef WXMAKINGDLL_CHECKTREE - #define WXDLLIMPEXP_CHECKTREE WXEXPORT -#elif defined(WXUSING_CHECKTREE_SOURCE) - #define WXDLLIMPEXP_CHECKTREE -#elif defined(WXUSINGDLL) - #define WXDLLIMPEXP_CHECKTREE WXIMPORT -#else // not making nor using DLL - #define WXDLLIMPEXP_CHECKTREE -#endif - -#include -// wxDECLARE_CLASS, wxIMPLEMENT_CLASS for sorting? - -class WXDLLIMPEXP_CHECKTREE wxCheckTree : public wxTreeCtrl -{ - wxDECLARE_ABSTRACT_CLASS(wxCheckTree); - - public: - wxCheckTree(); - wxCheckTree(wxWindow *parent, const wxWindowID id, - const wxPoint& pos, const wxSize& size, - long style); - - - void Init(); - void Sort(const wxTreeItemId& node, bool recursive); - - int OnCompareItems(const wxTreeItemId& item1, const wxTreeItemId& item2) override; - - //methods overriden from base class: - void SetFocusFromKbd() override; - void SetItemTextColour(const wxTreeItemId &item, const wxColour &col) override; - - //interaction with the check boxes: - bool EnableCheckBox(const wxTreeItemId &item, bool enable = true ); - bool DisableCheckBox(const wxTreeItemId &item); - void Check(const wxTreeItemId &item,bool state=true); - void Uncheck(const wxTreeItemId &item); - void MakeCheckable(const wxTreeItemId &item, bool state = false); - bool IsCheckable(const wxTreeItemId& item); - - enum - { - UNCHECKED, - UNCHECKED_MOUSE_OVER, - UNCHECKED_LEFT_DOWN, - UNCHECKED_DISABLED, - CHECKED, - CHECKED_MOUSE_OVER, - CHECKED_LEFT_DOWN, - CHECKED_DISABLED - }; - - private: - //event handlers - void On_Tree_Sel_Changed( wxTreeEvent& event ); - - void On_Char( wxKeyEvent& event ); - void On_KeyDown( wxKeyEvent& event ); - void On_KeyUp( wxKeyEvent& event ); - - void On_Mouse_Enter_Tree( wxMouseEvent& event ); - void On_Mouse_Leave_Tree( wxMouseEvent& event ); - void On_Left_DClick( wxMouseEvent& event ); - void On_Left_Down( wxMouseEvent& event ); - void On_Left_Up( wxMouseEvent& event ); - void On_Mouse_Motion( wxMouseEvent& event ); - void On_Mouse_Wheel( wxMouseEvent& event ); - - void On_Tree_Focus_Set( wxFocusEvent& event ); - void On_Tree_Focus_Lost( wxFocusEvent& event ); - - //private data: - std::map m_colors; - - bool mouse_entered_tree_with_left_down = false; - - wxTreeItemId last_mouse_over{}; - wxTreeItemId last_left_down{}; - wxTreeItemId last_kf{}; - - - // NB: due to an ugly wxMSW hack you _must_ use DECLARE_DYNAMIC_CLASS() - // if you want your overloaded OnCompareItems() to be called. - // OTOH, if you don't want it you may omit the next line - this will - // make default (alphabetical) sorting much faster under wxMSW. - //DECLARE_DYNAMIC_CLASS(wxCheckTree) -}; - -wxDECLARE_EXPORTED_EVENT(WXDLLIMPEXP_CHECKTREE, wxEVT_CHECKTREE_CHOICE, wxTreeEvent); -wxDECLARE_EXPORTED_EVENT(WXDLLIMPEXP_CHECKTREE, wxEVT_CHECKTREE_FOCUS, wxTreeEvent); - - -#endif // checktree_H_INCLUDED diff --git a/src/gui/wxgui/wxgui.h b/src/gui/wxgui/wxgui.h deleted file mode 100644 index bb4352d1..00000000 --- a/src/gui/wxgui/wxgui.h +++ /dev/null @@ -1,47 +0,0 @@ -#pragma once - -#define wxNO_UNSAFE_WXSTRING_CONV 1 - -#include -#ifndef WX_PRECOMP -#include -#endif - -#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 -#include -#include - -extern bool g_inputConfigWindowHasFocus; - -inline bool SendSliderEvent(wxSlider* slider, int new_value) -{ - wxCommandEvent cevent(wxEVT_SLIDER, slider->GetId()); - cevent.SetInt(new_value); - cevent.SetEventObject(slider); - return slider->HandleWindowEvent(cevent); -} diff --git a/src/resource/MacOSXBundleInfo.plist.in b/src/resource/MacOSXBundleInfo.plist.in deleted file mode 100644 index ccd1c922..00000000 --- a/src/resource/MacOSXBundleInfo.plist.in +++ /dev/null @@ -1,69 +0,0 @@ - - - - - CFBundleDevelopmentRegion - English - CFBundleExecutable - ${MACOSX_BUNDLE_EXECUTABLE_NAME} - CFBundleIconFile - ${MACOSX_BUNDLE_ICON_FILE} - CFBundleIdentifier - ${MACOSX_BUNDLE_GUI_IDENTIFIER} - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - ${MACOSX_BUNDLE_BUNDLE_NAME} - CFBundlePackageType - APPL - CFBundleShortVersionString - ${MACOSX_BUNDLE_SHORT_VERSION_STRING} - CFBundleSignature - ???? - CFBundleVersion - ${MACOSX_BUNDLE_BUNDLE_VERSION} - CSResourcesFileMapped - - NSHumanReadableCopyright - ${MACOSX_BUNDLE_COPYRIGHT} - LSApplicationCategoryType - ${MACOSX_BUNDLE_CATEGORY} - LSMinimumSystemVersion - ${MACOSX_MINIMUM_SYSTEM_VERSION} - CFBundleLocalizations - - ca - de - en - es - fr - he - hu - it - ja - ko - nb - nl - pl - pt - ru - sv - tr - uk - zh - - CFBundleDocumentTypes - - - CFBundleTypeExtensions - - ${MACOSX_BUNDLE_TYPE_EXTENSION} - - CFBundleTypeName - Wii U File - CFBundleTypeRole - Viewer - - - - diff --git a/src/resource/cemu.icns b/src/resource/cemu.icns deleted file mode 100644 index c6d8c5b5..00000000 Binary files a/src/resource/cemu.icns and /dev/null differ diff --git a/src/resource/cemu.macos.entitlements b/src/resource/cemu.macos.entitlements deleted file mode 100644 index 0017e1a9..00000000 --- a/src/resource/cemu.macos.entitlements +++ /dev/null @@ -1,13 +0,0 @@ - - - - com.apple.security.cs.disable-library-validation - - com.apple.security.get-task-allow - - com.apple.security.cs.allow-jit - - com.apple.security.cs.allow-unsigned-executable-memory - - - diff --git a/src/resource/cemu.rc b/src/resource/cemu.rc deleted file mode 100644 index e6332032..00000000 --- a/src/resource/cemu.rc +++ /dev/null @@ -1,129 +0,0 @@ -// Microsoft Visual C++ generated resource script. -// -#include "resource/resource.h" - -#define APSTUDIO_READONLY_SYMBOLS -///////////////////////////////////////////////////////////////////////////// -// -// Generated from the TEXTINCLUDE 2 resource. -// -#include "winres.h" - -///////////////////////////////////////////////////////////////////////////// -#undef APSTUDIO_READONLY_SYMBOLS - -///////////////////////////////////////////////////////////////////////////// -// Neutral (Default) (unknown sub-lang: 0x8) resources - -#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ZZZ) -LANGUAGE LANG_NEUTRAL, 0x8 - -///////////////////////////////////////////////////////////////////////////// -// -// Icon -// - -// Icon with lowest ID value placed first to ensure application icon -// remains consistent on all systems. -M_WND_ICON128 ICON "resource\\logo_icon.ico" - -M_WND_ICON16 ICON "resource\\logo_icon16.ico" - -X_INPUT_CONTROLLER ICON "resource\\input\\icons8-game-controller-24.ico" - -X_BOX ICON "resource\\icons8_cardboard_box_filled.ico" - -X_SETTINGS ICON "resource\\icons8_automatic_26_xQK_icon.ico" - -X_GAME_PROFILE ICON "resource\\icons8-compose-filled-50.ico" - -X_HOTKEY_SETTINGS ICON "resource\\icons8_hotkeys.ico" - -#ifdef APSTUDIO_INVOKED -///////////////////////////////////////////////////////////////////////////// -// -// TEXTINCLUDE -// - -1 TEXTINCLUDE -BEGIN - "resource.h\0" -END - -2 TEXTINCLUDE -BEGIN - "#include ""afxres.h""\r\n" - "\0" -END - -3 TEXTINCLUDE -BEGIN - "\r\n" - "\0" -END - -#endif // APSTUDIO_INVOKED - - -///////////////////////////////////////////////////////////////////////////// -// -// Version -// - -#define xstr(s) str(s) -#define str(s) #s - -VS_VERSION_INFO VERSIONINFO -FILEVERSION EMULATOR_VERSION_MAJOR, EMULATOR_VERSION_MINOR, EMULATOR_VERSION_PATCH, 0 -PRODUCTVERSION EMULATOR_VERSION_MAJOR, EMULATOR_VERSION_MINOR, EMULATOR_VERSION_PATCH, 0 - FILEFLAGSMASK 0x3fL -#ifdef _DEBUG - FILEFLAGS 0x1L -#else - FILEFLAGS 0x0L -#endif - FILEOS 0x0L - FILETYPE 0x1L - FILESUBTYPE 0x0L -BEGIN - BLOCK "StringFileInfo" - BEGIN - BLOCK "040904b0" - BEGIN - VALUE "FileDescription", "Cemu Wii U emulator" - VALUE "InternalName", "Cemu" - VALUE "LegalCopyright", "Team Cemu" - VALUE "OriginalFilename", "Cemu.exe" - VALUE "ProductName", "Cemu" - VALUE "ProductVersion", xstr(EMULATOR_VERSION_MAJOR) "." xstr(EMULATOR_VERSION_MINOR) "." xstr(EMULATOR_VERSION_PATCH) EMULATOR_VERSION_SUFFIX "\0" - END - END - BLOCK "VarFileInfo" - BEGIN - VALUE "Translation", 0x409, 1200 - END -END - - -///////////////////////////////////////////////////////////////////////////// -// -// RCDATA -// - -IDR_FONTAWESOME RCDATA "resource\\fontawesome-webfont.ttf" - -#endif // Neutral (Default) (unknown sub-lang: 0x8) resources -///////////////////////////////////////////////////////////////////////////// - - - -#ifndef APSTUDIO_INVOKED -///////////////////////////////////////////////////////////////////////////// -// -// Generated from the TEXTINCLUDE 3 resource. -// - - -///////////////////////////////////////////////////////////////////////////// -#endif // not APSTUDIO_INVOKED - diff --git a/src/tools/ShaderCacheMerger.cpp b/src/tools/ShaderCacheMerger.cpp deleted file mode 100644 index 7a2727dd..00000000 --- a/src/tools/ShaderCacheMerger.cpp +++ /dev/null @@ -1,145 +0,0 @@ -#include "Cemu/FileCache/FileCache.h" -#include "Cafe/HW/Latte/Core/LatteShaderCache.h" -#include -#include - -void MergeShaderCacheFile(std::string fileName) -{ - // parse titleId from fileName - uint64 titleId = 0; - if (sscanf(fileName.c_str(), "%" SCNx64, &titleId) != 1) - return; - - const std::string mainPath = "shaderCache/transferable/" + fileName; - const std::string mergeSourcePath = "shaderCache/transferable/merge/" + fileName; - if (!fs::exists(mainPath) || !fs::exists(mergeSourcePath)) - return; - // open both caches - FileCache* mainCache = FileCache::Open(boost::nowide::widen(mainPath)); - FileCache* sourceCache = FileCache::Open(boost::nowide::widen(mergeSourcePath)); - if (!mainCache && !sourceCache) - { - printf("Failed to open cache files for %s\n", fileName.c_str()); - if (mainCache) - delete mainCache; - if (sourceCache) - delete sourceCache; - return; - } - // begin merging - printf("Merging shaders %" PRIx64 "...", titleId); - uint32 numMergedEntries = 0; // number of files added to the main cache file - for (sint32 i = 0; i < sourceCache->GetFileCount(); i++) - { - uint64 name1, name2; - std::vector fileData; - if (!sourceCache->GetFileByIndex(i, &name1, &name2, fileData)) - continue; - std::vector existingfileData; - if (mainCache->HasFile({ name1, name2 })) - continue; - mainCache->AddFile({ name1, name2 }, fileData.data(), (sint32)fileData.size()); - numMergedEntries++; - } - printf(" -> Added %d new shaders for a total of %d\n", numMergedEntries, mainCache->GetFileCount()); - - delete mainCache; - delete sourceCache; - - fs::remove(mergeSourcePath); -} - -void MergePipelineCacheFile(std::string fileName) -{ - // parse titleId from fileName - uint64 titleId = 0; - if (sscanf(fileName.c_str(), "%" SCNx64, &titleId) != 1) - return; - - const std::string mainPath = "shaderCache/transferable/" + fileName; - const std::string mergeSourcePath = "shaderCache/transferable/merge/" + fileName; - if (!fs::exists(mainPath) || !fs::exists(mergeSourcePath)) - return; - // open both caches - const uint32 cacheFileVersion = 1; - FileCache* mainCache = FileCache::Open(boost::nowide::widen(mainPath)); - FileCache* sourceCache = FileCache::Open(boost::nowide::widen(mergeSourcePath)); - if (!mainCache && !sourceCache) - { - printf("Failed to open cache files for %s\n", fileName.c_str()); - if (mainCache) - delete mainCache; - if (sourceCache) - delete sourceCache; - return; - } - // begin merging - printf("Merging pipelines %" PRIx64 "...", titleId); - uint32 numMergedEntries = 0; // number of files added to the main cache file - for (sint32 i = 0; i < sourceCache->GetFileCount(); i++) - { - uint64 name1, name2; - std::vector fileData; - if (!sourceCache->GetFileByIndex(i, &name1, &name2, fileData)) - continue; - std::vector existingfileData; - if (mainCache->HasFile({ name1, name2 })) - continue; - mainCache->AddFile({ name1, name2 }, fileData.data(), (sint32)fileData.size()); - numMergedEntries++; - } - printf(" -> Added %d new pipelines for a total of %d\n", numMergedEntries, mainCache->GetFileCount()); - - delete mainCache; - delete sourceCache; - - fs::remove(mergeSourcePath); -} - -void MergeShaderAndPipelineCacheFiles() -{ - printf("Scanning for shader cache files to merge...\n"); - for (const auto& it : fs::directory_iterator("shaderCache/transferable/")) - { - if (fs::is_directory(it)) - continue; - auto filename = it.path().filename().generic_string(); - if (std::regex_match(filename, std::regex("^[0-9a-fA-F]{16}(?:_shaders.bin)"))) - MergeShaderCacheFile(filename); - if (std::regex_match(filename, std::regex("^[0-9a-fA-F]{16}(?:_mtlshaders.bin)"))) - MergeShaderCacheFile(filename); - } - printf("\nScanning for pipeline cache files to merge...\n"); - for (const auto& it : fs::directory_iterator("shaderCache/transferable/")) - { - if (fs::is_directory(it)) - continue; - auto filename = it.path().filename().generic_string(); - if (std::regex_match(filename, std::regex("^[0-9a-fA-F]{16}(?:_vkpipeline.bin)"))) - MergePipelineCacheFile(filename); - if (std::regex_match(filename, std::regex("^[0-9a-fA-F]{16}(?:_mtlpipeline.bin)"))) - MergePipelineCacheFile(filename); - } -} - -void ToolShaderCacheMerger() -{ - printf("****************************************************\n"); - printf("****************************************************\n"); - printf("Shader and pipeline cache merging tool\n"); - printf("This tool will merge any shader caches placed in:\n"); - printf("shaderCache/transferable/merge/\n"); - printf("into the files of the same name in:\n"); - printf("shaderCache/transferable/\n"); - printf("****************************************************\n"); - printf("****************************************************\n"); - printf("\n"); - - MergeShaderAndPipelineCacheFiles(); - - - printf("done!\n"); - while (true) - Sleep(1000); - exit(0); -}