mirror of
https://github.com/PCSX2/pcsx2.git
synced 2025-12-16 04:08:48 +00:00
Compare commits
16 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
748f232976 | ||
|
|
a33612cf7d | ||
|
|
5c123f3183 | ||
|
|
d30a7fb991 | ||
|
|
d4f661c27c | ||
|
|
6d05d0220d | ||
|
|
7fab935c2d | ||
|
|
cd120c3cfd | ||
|
|
0180ec060b | ||
|
|
cf4412ecbe | ||
|
|
743b0b11d8 | ||
|
|
e8c2cfa843 | ||
|
|
764875ddbf | ||
|
|
92c7eaa383 | ||
|
|
4d2c1a82c9 | ||
|
|
eb50aaea35 |
@ -17,7 +17,7 @@ jobs:
|
|||||||
run: ./.github/workflows/scripts/common/update_base_translation.sh
|
run: ./.github/workflows/scripts/common/update_base_translation.sh
|
||||||
|
|
||||||
- name: Create Pull Request
|
- name: Create Pull Request
|
||||||
uses: peter-evans/create-pull-request@22a9089034f40e5a961c8808d113e2c98fb63676
|
uses: peter-evans/create-pull-request@98357b18bf14b5342f975ff684046ec3b2a07725
|
||||||
with:
|
with:
|
||||||
title: "Qt: Update Base Translation"
|
title: "Qt: Update Base Translation"
|
||||||
commit-message: "[ci skip] Qt: Update Base Translation."
|
commit-message: "[ci skip] Qt: Update Base Translation."
|
||||||
|
|||||||
@ -19,7 +19,7 @@ jobs:
|
|||||||
mv ./game_controller_db.txt ${{github.workspace}}/bin/resources/game_controller_db.txt
|
mv ./game_controller_db.txt ${{github.workspace}}/bin/resources/game_controller_db.txt
|
||||||
|
|
||||||
- name: Create Pull Request
|
- name: Create Pull Request
|
||||||
uses: peter-evans/create-pull-request@22a9089034f40e5a961c8808d113e2c98fb63676
|
uses: peter-evans/create-pull-request@98357b18bf14b5342f975ff684046ec3b2a07725
|
||||||
with:
|
with:
|
||||||
title: "PAD: Update to latest controller database"
|
title: "PAD: Update to latest controller database"
|
||||||
commit-message: "[ci skip] PAD: Update to latest controller database."
|
commit-message: "[ci skip] PAD: Update to latest controller database."
|
||||||
|
|||||||
2
.github/workflows/linux_build_flatpak.yml
vendored
2
.github/workflows/linux_build_flatpak.yml
vendored
@ -153,7 +153,7 @@ jobs:
|
|||||||
mv "./${{ steps.artifact-metadata.outputs.artifact-name }}.flatpak" "$GITHUB_WORKSPACE"/ci-artifacts/
|
mv "./${{ steps.artifact-metadata.outputs.artifact-name }}.flatpak" "$GITHUB_WORKSPACE"/ci-artifacts/
|
||||||
|
|
||||||
- name: Upload artifact
|
- name: Upload artifact
|
||||||
uses: actions/upload-artifact@v5
|
uses: actions/upload-artifact@v6
|
||||||
with:
|
with:
|
||||||
name: ${{ steps.artifact-metadata.outputs.artifact-name }}
|
name: ${{ steps.artifact-metadata.outputs.artifact-name }}
|
||||||
path: ci-artifacts
|
path: ci-artifacts
|
||||||
|
|||||||
6
.github/workflows/linux_build_qt.yml
vendored
6
.github/workflows/linux_build_qt.yml
vendored
@ -92,7 +92,7 @@ jobs:
|
|||||||
run: echo "timestamp=$(date -u "+%Y-%m-%d-%H;%M;%S")" >> $GITHUB_OUTPUT
|
run: echo "timestamp=$(date -u "+%Y-%m-%d-%H;%M;%S")" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
- name: ccache cache files
|
- name: ccache cache files
|
||||||
uses: actions/cache@v4
|
uses: actions/cache@v5
|
||||||
with:
|
with:
|
||||||
path: .ccache
|
path: .ccache
|
||||||
key: ${{ inputs.os }} ${{ inputs.platform }} ${{ inputs.compiler }} ${{ inputs.detail }} ccache ${{ steps.ccache_cache_timestamp.outputs.timestamp }}
|
key: ${{ inputs.os }} ${{ inputs.platform }} ${{ inputs.compiler }} ${{ inputs.detail }} ccache ${{ steps.ccache_cache_timestamp.outputs.timestamp }}
|
||||||
@ -114,7 +114,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Cache Dependencies
|
- name: Cache Dependencies
|
||||||
id: cache-deps
|
id: cache-deps
|
||||||
uses: actions/cache@v4
|
uses: actions/cache@v5
|
||||||
with:
|
with:
|
||||||
path: ~/deps
|
path: ~/deps
|
||||||
key: ${{ inputs.os }} ${{ inputs.platform }} deps ${{ hashFiles('.github/workflows/scripts/linux/build-dependencies-qt.sh', '.github/workflows/scripts/common/*.patch') }}
|
key: ${{ inputs.os }} ${{ inputs.platform }} deps ${{ hashFiles('.github/workflows/scripts/linux/build-dependencies-qt.sh', '.github/workflows/scripts/common/*.patch') }}
|
||||||
@ -174,7 +174,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Upload artifact
|
- name: Upload artifact
|
||||||
if: inputs.buildAppImage == true
|
if: inputs.buildAppImage == true
|
||||||
uses: actions/upload-artifact@v5
|
uses: actions/upload-artifact@v6
|
||||||
with:
|
with:
|
||||||
name: ${{ steps.artifact-metadata.outputs.artifact-name }}
|
name: ${{ steps.artifact-metadata.outputs.artifact-name }}
|
||||||
path: ci-artifacts
|
path: ci-artifacts
|
||||||
|
|||||||
6
.github/workflows/macos_build.yml
vendored
6
.github/workflows/macos_build.yml
vendored
@ -91,7 +91,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Cache Dependencies
|
- name: Cache Dependencies
|
||||||
id: cache-deps
|
id: cache-deps
|
||||||
uses: actions/cache@v4
|
uses: actions/cache@v5
|
||||||
with:
|
with:
|
||||||
path: ~/deps
|
path: ~/deps
|
||||||
key: ${{ inputs.os }} deps ${{ hashFiles('.github/workflows/scripts/macos/*', '.github/workflows/scripts/common/*.patch') }}
|
key: ${{ inputs.os }} deps ${{ hashFiles('.github/workflows/scripts/macos/*', '.github/workflows/scripts/common/*.patch') }}
|
||||||
@ -112,7 +112,7 @@ jobs:
|
|||||||
run: echo "timestamp=$(date -u "+%Y-%m-%d-%H;%M;%S")" >> $GITHUB_OUTPUT
|
run: echo "timestamp=$(date -u "+%Y-%m-%d-%H;%M;%S")" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
- name: Cache ccache cache
|
- name: Cache ccache cache
|
||||||
uses: actions/cache@v4
|
uses: actions/cache@v5
|
||||||
with:
|
with:
|
||||||
path: .ccache
|
path: .ccache
|
||||||
key: ${{ inputs.os }} ccache ${{ steps.ccache_cache_timestamp.outputs.timestamp }}
|
key: ${{ inputs.os }} ccache ${{ steps.ccache_cache_timestamp.outputs.timestamp }}
|
||||||
@ -197,7 +197,7 @@ jobs:
|
|||||||
cp "${{ steps.artifact-metadata.outputs.artifact-name }}.tar.xz" ci-artifacts/macOS.tar.xz
|
cp "${{ steps.artifact-metadata.outputs.artifact-name }}.tar.xz" ci-artifacts/macOS.tar.xz
|
||||||
|
|
||||||
- name: Upload Artifact
|
- name: Upload Artifact
|
||||||
uses: actions/upload-artifact@v5
|
uses: actions/upload-artifact@v6
|
||||||
with:
|
with:
|
||||||
name: ${{ steps.artifact-metadata.outputs.artifact-name }}
|
name: ${{ steps.artifact-metadata.outputs.artifact-name }}
|
||||||
path: "*.tar.xz"
|
path: "*.tar.xz"
|
||||||
|
|||||||
2
.github/workflows/release_cut_new.yml
vendored
2
.github/workflows/release_cut_new.yml
vendored
@ -168,7 +168,7 @@ jobs:
|
|||||||
- name: Prepare Artifact Folder
|
- name: Prepare Artifact Folder
|
||||||
run: mkdir ./ci-artifacts/
|
run: mkdir ./ci-artifacts/
|
||||||
|
|
||||||
- uses: actions/download-artifact@v6
|
- uses: actions/download-artifact@v7
|
||||||
name: Download all Artifacts
|
name: Download all Artifacts
|
||||||
with:
|
with:
|
||||||
path: ./ci-artifacts/
|
path: ./ci-artifacts/
|
||||||
|
|||||||
6
.github/workflows/windows_build_qt.yml
vendored
6
.github/workflows/windows_build_qt.yml
vendored
@ -115,7 +115,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Cache Dependencies
|
- name: Cache Dependencies
|
||||||
id: cache-deps
|
id: cache-deps
|
||||||
uses: actions/cache@v4
|
uses: actions/cache@v5
|
||||||
with:
|
with:
|
||||||
path: deps
|
path: deps
|
||||||
key: ${{ inputs.os }} ${{ inputs.platform }} deps ${{ hashFiles('.github/workflows/scripts/windows/build-dependencies.bat', '.github/workflows/scripts/common/*.patch') }}
|
key: ${{ inputs.os }} ${{ inputs.platform }} deps ${{ hashFiles('.github/workflows/scripts/windows/build-dependencies.bat', '.github/workflows/scripts/common/*.patch') }}
|
||||||
@ -154,7 +154,7 @@ jobs:
|
|||||||
cmake --build build --config Release --target unittests
|
cmake --build build --config Release --target unittests
|
||||||
|
|
||||||
- name: Upload artifact
|
- name: Upload artifact
|
||||||
uses: actions/upload-artifact@v5
|
uses: actions/upload-artifact@v6
|
||||||
with:
|
with:
|
||||||
name: ${{ steps.artifact-metadata.outputs.artifact-name }}
|
name: ${{ steps.artifact-metadata.outputs.artifact-name }}
|
||||||
path: |
|
path: |
|
||||||
@ -186,7 +186,7 @@ jobs:
|
|||||||
}
|
}
|
||||||
|
|
||||||
- name: Upload artifact - with symbols
|
- name: Upload artifact - with symbols
|
||||||
uses: actions/upload-artifact@v5
|
uses: actions/upload-artifact@v6
|
||||||
with:
|
with:
|
||||||
name: ${{ steps.artifact-metadata.outputs.artifact-name }}-symbols
|
name: ${{ steps.artifact-metadata.outputs.artifact-name }}-symbols
|
||||||
path: |
|
path: |
|
||||||
|
|||||||
@ -78,7 +78,7 @@
|
|||||||
03000000c82d00001230000000000000,8BitDo Ultimate,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,paddle1:b2,paddle2:b5,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,
|
03000000c82d00001230000000000000,8BitDo Ultimate,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,paddle1:b2,paddle2:b5,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,
|
||||||
03000000c82d00001260000000000000,8BitDo Ultimate 2,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,paddle1:b17,paddle2:b16,paddle3:b2,paddle4:b5,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,
|
03000000c82d00001260000000000000,8BitDo Ultimate 2,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,paddle1:b17,paddle2:b16,paddle3:b2,paddle4:b5,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,
|
||||||
03000000c82d00001b30000000000000,8BitDo Ultimate 2C,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,paddle1:b5,paddle2:b2,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,
|
03000000c82d00001b30000000000000,8BitDo Ultimate 2C,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,paddle1:b5,paddle2:b2,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,
|
||||||
03000000c82d00001c30000000000000,8BitDo Ultimate 2C,a:b0,b:b1,x:b3,y:b4,back:b10,guide:b12,start:b11,leftstick:b13,rightstick:b14,leftshoulder:b6,rightshoulder:b7,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a1,rightx:a3,righty:a4,lefttrigger:b8,righttrigger:b9,paddle1:b2,paddle2:b5,platform:Windows,
|
03000000c82d00001c30000000000000,8BitDo Ultimate 2C,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,paddle1:b2,paddle2:b5,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,
|
||||||
03000000c82d00001d30000000000000,8BitDo Ultimate 2C,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,paddle1:b5,paddle2:b2,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,
|
03000000c82d00001d30000000000000,8BitDo Ultimate 2C,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,paddle1:b5,paddle2:b2,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,
|
||||||
03000000c82d00001530000000000000,8BitDo Ultimate C,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,
|
03000000c82d00001530000000000000,8BitDo Ultimate C,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,
|
||||||
03000000c82d00001630000000000000,8BitDo Ultimate C,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,
|
03000000c82d00001630000000000000,8BitDo Ultimate C,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,
|
||||||
@ -985,6 +985,7 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
|
|||||||
030000007e0500000720000001000000,Joy-Con (R),+leftx:h0.2,+lefty:h0.4,-leftx:h0.8,-lefty:h0.1,a:b0,b:b1,back:b12,leftshoulder:b4,leftstick:b11,rightshoulder:b5,start:b9,x:b2,y:b3,platform:Mac OS X,
|
030000007e0500000720000001000000,Joy-Con (R),+leftx:h0.2,+lefty:h0.4,-leftx:h0.8,-lefty:h0.1,a:b0,b:b1,back:b12,leftshoulder:b4,leftstick:b11,rightshoulder:b5,start:b9,x:b2,y:b3,platform:Mac OS X,
|
||||||
03000000242f00002d00000007010000,JYS Adapter,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Mac OS X,
|
03000000242f00002d00000007010000,JYS Adapter,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Mac OS X,
|
||||||
030000006d04000019c2000000000000,Logitech Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,
|
030000006d04000019c2000000000000,Logitech Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,
|
||||||
|
030000006d04000019c2000000020000,Logitech Cordless RumblePad 2,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1~,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3~,start:b9,x:b0,y:b3,platform:Mac OS X,
|
||||||
030000006d04000016c2000000020000,Logitech Dual Action,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,
|
030000006d04000016c2000000020000,Logitech Dual Action,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,
|
||||||
030000006d04000016c2000000030000,Logitech Dual Action,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,
|
030000006d04000016c2000000030000,Logitech Dual Action,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,
|
||||||
030000006d04000016c2000014040000,Logitech Dual Action,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,
|
030000006d04000016c2000014040000,Logitech Dual Action,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,
|
||||||
@ -993,7 +994,6 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
|
|||||||
030000006d04000019c2000005030000,Logitech F710,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,
|
030000006d04000019c2000005030000,Logitech F710,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,
|
||||||
030000006d0400001fc2000000000000,Logitech F710,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X,
|
030000006d0400001fc2000000000000,Logitech F710,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X,
|
||||||
030000006d04000018c2000000010000,Logitech RumblePad 2,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1~,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3~,start:b9,x:b0,y:b3,platform:Mac OS X,
|
030000006d04000018c2000000010000,Logitech RumblePad 2,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1~,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3~,start:b9,x:b0,y:b3,platform:Mac OS X,
|
||||||
030000006d04000019c2000000020000,Logitech Cordless RumblePad 2,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1~,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3~,start:b9,x:b0,y:b3,platform:Mac OS X,
|
|
||||||
03000000380700005032000000010000,Mad Catz PS3 Fightpad Pro,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,
|
03000000380700005032000000010000,Mad Catz PS3 Fightpad Pro,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,
|
||||||
03000000380700008433000000010000,Mad Catz PS3 Fightstick TE S Plus,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,
|
03000000380700008433000000010000,Mad Catz PS3 Fightstick TE S Plus,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,
|
||||||
03000000380700005082000000010000,Mad Catz PS4 Fightpad Pro,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Mac OS X,
|
03000000380700005082000000010000,Mad Catz PS4 Fightpad Pro,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Mac OS X,
|
||||||
@ -1395,6 +1395,10 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
|
|||||||
03000000242e00008816000001010000,Hyperkin X91,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
|
03000000242e00008816000001010000,Hyperkin X91,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
|
||||||
03000000f00300008d03000011010000,HyperX Clutch,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,
|
03000000f00300008d03000011010000,HyperX Clutch,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,
|
||||||
03000000830500006020000010010000,iBuffalo Super Famicom Controller,a:b1,b:b0,back:b6,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,start:b7,x:b3,y:b2,platform:Linux,
|
03000000830500006020000010010000,iBuffalo Super Famicom Controller,a:b1,b:b0,back:b6,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,start:b7,x:b3,y:b2,platform:Linux,
|
||||||
|
03000000d80400004bea000011010000,icedragon.io STAC Dance Pad,a:b0,b:b1,x:b2,y:b3,back:b4,platform:Linux,
|
||||||
|
03000000d80400004aea000011010000,icedragon.io STAC Dance Pad,a:b0,b:b1,x:b2,y:b3,back:b4,platform:Linux,
|
||||||
|
030000008a2e0000d910000011010000,icedragon.io STAC2 Dance Pad,a:b0,b:b1,x:b2,y:b3,back:b4,platform:Linux,
|
||||||
|
030000008a2e0000e910000011010000,icedragon.io STAC2 Dance Pad,a:b8,b:b9,x:b10,y:b11,back:b12,platform:Linux,
|
||||||
030000008f0e00001330000001010000,iCode Retro Adapter,b:b3,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b9,lefttrigger:b10,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b1,start:b7,x:b2,y:b0,platform:Linux,
|
030000008f0e00001330000001010000,iCode Retro Adapter,b:b3,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b9,lefttrigger:b10,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b1,start:b7,x:b2,y:b0,platform:Linux,
|
||||||
050000006964726f69643a636f6e0000,idroidcon Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,
|
050000006964726f69643a636f6e0000,idroidcon Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,
|
||||||
03000000b50700001503000010010000,Impact,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b1,platform:Linux,
|
03000000b50700001503000010010000,Impact,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b1,platform:Linux,
|
||||||
@ -1591,6 +1595,7 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
|
|||||||
030000006d040000d2ca000011010000,Precision Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,
|
030000006d040000d2ca000011010000,Precision Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,
|
||||||
03000000250900000017000010010000,PS/SS/N64 Adapter,a:b1,b:b2,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,leftshoulder:b5,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b7,rightx:a2~,righty:a3,start:b8,platform:Linux,
|
03000000250900000017000010010000,PS/SS/N64 Adapter,a:b1,b:b2,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,leftshoulder:b5,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b7,rightx:a2~,righty:a3,start:b8,platform:Linux,
|
||||||
03000000ff1100004133000010010000,PS2 Controller,a:b2,b:b1,back:b8,leftshoulder:b6,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b5,start:b9,x:b3,y:b0,platform:Linux,
|
03000000ff1100004133000010010000,PS2 Controller,a:b2,b:b1,back:b8,leftshoulder:b6,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b5,start:b9,x:b3,y:b0,platform:Linux,
|
||||||
|
03000000120c0000160e000011010000,PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,
|
||||||
03000000341a00003608000011010000,PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,
|
03000000341a00003608000011010000,PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,
|
||||||
030000004c0500006802000010010000,PS3 Controller,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:b9,rightx:a2,righty:a3,start:b3,x:b15,y:b12,platform:Linux,
|
030000004c0500006802000010010000,PS3 Controller,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:b9,rightx:a2,righty:a3,start:b3,x:b15,y:b12,platform:Linux,
|
||||||
030000004c0500006802000010810000,PS3 Controller,a:b0,b:b1,back:b8,dpdown:b14,dpleft:b15,dpright:b16,dpup:b13,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux,
|
030000004c0500006802000010810000,PS3 Controller,a:b0,b:b1,back:b8,dpdown:b14,dpleft:b15,dpright:b16,dpup:b13,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux,
|
||||||
@ -1701,7 +1706,6 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
|
|||||||
03000000952e00004e43000011010000,Scuf Envision,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b8,lefttrigger:a3,leftx:a0,lefty:a1,misc1:b11,rightshoulder:b5,rightstick:b9,righttrigger:a4,rightx:a2,righty:a5,start:b7,x:b2,y:b3,platform:Linux,
|
03000000952e00004e43000011010000,Scuf Envision,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b8,lefttrigger:a3,leftx:a0,lefty:a1,misc1:b11,rightshoulder:b5,rightstick:b9,righttrigger:a4,rightx:a2,righty:a5,start:b7,x:b2,y:b3,platform:Linux,
|
||||||
03000000a30c00002500000011010000,Sega Genesis Mini 3B Controller,a:b2,b:b1,dpdown:+a4,dpleft:-a3,dpright:+a3,dpup:-a4,righttrigger:b5,start:b9,platform:Linux,
|
03000000a30c00002500000011010000,Sega Genesis Mini 3B Controller,a:b2,b:b1,dpdown:+a4,dpleft:-a3,dpright:+a3,dpup:-a4,righttrigger:b5,start:b9,platform:Linux,
|
||||||
03000000790000001100000011010000,Sega Saturn,a:b1,b:b2,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,lefttrigger:b7,rightshoulder:b5,righttrigger:b4,start:b9,x:b0,y:b3,platform:Linux,
|
03000000790000001100000011010000,Sega Saturn,a:b1,b:b2,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,lefttrigger:b7,rightshoulder:b5,righttrigger:b4,start:b9,x:b0,y:b3,platform:Linux,
|
||||||
03000000790000002201000011010000,Sega Saturn,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b5,rightshoulder:b6,righttrigger:b7,start:b9,x:b2,y:b3,platform:Linux,
|
|
||||||
03000000b40400000a01000000010000,Sega Saturn,a:b0,b:b1,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,lefttrigger:b7,rightshoulder:b5,righttrigger:b2,start:b8,x:b3,y:b4,platform:Linux,
|
03000000b40400000a01000000010000,Sega Saturn,a:b0,b:b1,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,lefttrigger:b7,rightshoulder:b5,righttrigger:b2,start:b8,x:b3,y:b4,platform:Linux,
|
||||||
03000000632500002305000010010000,ShanWan Gamepad,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux,
|
03000000632500002305000010010000,ShanWan Gamepad,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux,
|
||||||
03000000632500002605000010010000,ShanWan Gamepad,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,
|
03000000632500002605000010010000,ShanWan Gamepad,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,
|
||||||
@ -1858,4 +1862,5 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
|
|||||||
03000000120c0000100e000011010000,Zeroplus P4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,
|
03000000120c0000100e000011010000,Zeroplus P4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,
|
||||||
03000000120c0000101e000011010000,Zeroplus P4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,
|
03000000120c0000101e000011010000,Zeroplus P4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,
|
||||||
03000000120c0000182e000011010000,Zeroplus PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,
|
03000000120c0000182e000011010000,Zeroplus PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,
|
||||||
|
03000000790000002201000011010000,ZhiXu GuliKit D,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux,
|
||||||
|
|
||||||
|
|||||||
@ -28,7 +28,7 @@
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if defined(_WIN32)
|
#if defined(_WIN32)
|
||||||
#include "RedtapeWindows.h"
|
#include "common/RedtapeWindows.h"
|
||||||
#include <io.h>
|
#include <io.h>
|
||||||
#include <malloc.h>
|
#include <malloc.h>
|
||||||
#include <pathcch.h>
|
#include <pathcch.h>
|
||||||
|
|||||||
@ -1148,7 +1148,7 @@ bool BMPBufferLoader(RGBA8Image* image, const void* buffer, size_t buffer_size)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (width > 65536 || height > 65536)
|
if (width >= 65536 || height >= 65536)
|
||||||
{
|
{
|
||||||
Console.Error("BMP dimensions too large: %ux%u", width, height);
|
Console.Error("BMP dimensions too large: %ux%u", width, height);
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@ -220,6 +220,8 @@ void GameListWidget::initialize()
|
|||||||
m_sort_model->setSourceModel(m_model);
|
m_sort_model->setSourceModel(m_model);
|
||||||
|
|
||||||
m_ui.setupUi(this);
|
m_ui.setupUi(this);
|
||||||
|
m_ui.stack->installEventFilter(this);
|
||||||
|
m_ui.stack->setAutoFillBackground(false);
|
||||||
|
|
||||||
for (u32 type = 0; type < static_cast<u32>(GameList::EntryType::Count); type++)
|
for (u32 type = 0; type < static_cast<u32>(GameList::EntryType::Count); type++)
|
||||||
{
|
{
|
||||||
@ -353,6 +355,7 @@ void GameListWidget::setCustomBackground()
|
|||||||
m_background_movie = nullptr;
|
m_background_movie = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Get the path to the custom background
|
||||||
std::string path = Host::GetBaseStringSettingValue("UI", "GameListBackgroundPath");
|
std::string path = Host::GetBaseStringSettingValue("UI", "GameListBackgroundPath");
|
||||||
if (!Path::IsAbsolute(path))
|
if (!Path::IsAbsolute(path))
|
||||||
path = Path::Combine(EmuFolders::DataRoot, path);
|
path = Path::Combine(EmuFolders::DataRoot, path);
|
||||||
@ -360,27 +363,26 @@ void GameListWidget::setCustomBackground()
|
|||||||
// Only try to create background if path are valid
|
// Only try to create background if path are valid
|
||||||
if (!path.empty() && FileSystem::FileExists(path.c_str()))
|
if (!path.empty() && FileSystem::FileExists(path.c_str()))
|
||||||
{
|
{
|
||||||
QMovie* new_movie;
|
|
||||||
QString img_path = QString::fromStdString(path);
|
QString img_path = QString::fromStdString(path);
|
||||||
if (img_path.endsWith(".png", Qt::CaseInsensitive))
|
const QByteArray format = (img_path.endsWith(".png", Qt::CaseInsensitive)) ? QByteArray("apng") : QByteArray();
|
||||||
// Use apng plugin
|
m_background_movie = new QMovie(img_path, format, this);
|
||||||
new_movie = new QMovie(img_path, "apng", this);
|
if (!m_background_movie->isValid())
|
||||||
else
|
|
||||||
new_movie = new QMovie(img_path, QByteArray(), this);
|
|
||||||
|
|
||||||
if (new_movie->isValid())
|
|
||||||
m_background_movie = new_movie;
|
|
||||||
else
|
|
||||||
{
|
{
|
||||||
Console.Warning("Failed to load background movie from: %s", path.c_str());
|
Console.Warning("Failed to load background movie from: %s", path.c_str());
|
||||||
delete new_movie;
|
delete m_background_movie;
|
||||||
|
m_background_movie = nullptr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// If there is no valid background then reset fallback to default UI state
|
// If there is no valid background then reset fallback to default UI state
|
||||||
if (!m_background_movie)
|
if (!m_background_movie)
|
||||||
{
|
{
|
||||||
m_ui.stack->setPalette(QApplication::palette());
|
m_background_pixmap = QPixmap();
|
||||||
|
m_ui.stack->setAutoFillBackground(true);
|
||||||
|
m_table_view->viewport()->setAutoFillBackground(true);
|
||||||
|
m_list_view->viewport()->setAutoFillBackground(true);
|
||||||
|
|
||||||
|
m_ui.stack->update();
|
||||||
m_table_view->setAlternatingRowColors(true);
|
m_table_view->setAlternatingRowColors(true);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -390,7 +392,7 @@ void GameListWidget::setCustomBackground()
|
|||||||
const std::string ar_value = Host::GetBaseStringSettingValue("UI", "GameListBackgroundMode", InterfaceSettingsWidget::BACKGROUND_SCALE_NAMES[static_cast<u8>(QtUtils::ScalingMode::Fit)]);
|
const std::string ar_value = Host::GetBaseStringSettingValue("UI", "GameListBackgroundMode", InterfaceSettingsWidget::BACKGROUND_SCALE_NAMES[static_cast<u8>(QtUtils::ScalingMode::Fit)]);
|
||||||
for (u8 i = 0; i < static_cast<u8>(QtUtils::ScalingMode::MaxCount); i++)
|
for (u8 i = 0; i < static_cast<u8>(QtUtils::ScalingMode::MaxCount); i++)
|
||||||
{
|
{
|
||||||
if (!(InterfaceSettingsWidget::BACKGROUND_SCALE_NAMES[i] == nullptr))
|
if (InterfaceSettingsWidget::BACKGROUND_SCALE_NAMES[i] != nullptr)
|
||||||
{
|
{
|
||||||
if (ar_value == InterfaceSettingsWidget::BACKGROUND_SCALE_NAMES[i])
|
if (ar_value == InterfaceSettingsWidget::BACKGROUND_SCALE_NAMES[i])
|
||||||
{
|
{
|
||||||
@ -405,8 +407,13 @@ void GameListWidget::setCustomBackground()
|
|||||||
|
|
||||||
// Selected Custom background is valid, connect the signals and start animation in gamelist
|
// Selected Custom background is valid, connect the signals and start animation in gamelist
|
||||||
connect(m_background_movie, &QMovie::frameChanged, this, &GameListWidget::processBackgroundFrames, Qt::UniqueConnection);
|
connect(m_background_movie, &QMovie::frameChanged, this, &GameListWidget::processBackgroundFrames, Qt::UniqueConnection);
|
||||||
|
m_ui.stack->setAutoFillBackground(false);
|
||||||
|
|
||||||
|
m_table_view->viewport()->setAutoFillBackground(false);
|
||||||
|
m_list_view->viewport()->setAutoFillBackground(false);
|
||||||
updateCustomBackgroundState(true);
|
updateCustomBackgroundState(true);
|
||||||
m_table_view->setAlternatingRowColors(false);
|
m_table_view->setAlternatingRowColors(false);
|
||||||
|
processBackgroundFrames();
|
||||||
}
|
}
|
||||||
|
|
||||||
void GameListWidget::updateCustomBackgroundState(const bool force_start)
|
void GameListWidget::updateCustomBackgroundState(const bool force_start)
|
||||||
@ -422,7 +429,7 @@ void GameListWidget::updateCustomBackgroundState(const bool force_start)
|
|||||||
|
|
||||||
void GameListWidget::processBackgroundFrames()
|
void GameListWidget::processBackgroundFrames()
|
||||||
{
|
{
|
||||||
if (m_background_movie && m_background_movie->isValid())
|
if (m_background_movie && m_background_movie->isValid() && isVisible())
|
||||||
{
|
{
|
||||||
const int widget_width = m_ui.stack->width();
|
const int widget_width = m_ui.stack->width();
|
||||||
const int widget_height = m_ui.stack->height();
|
const int widget_height = m_ui.stack->height();
|
||||||
@ -435,9 +442,8 @@ void GameListWidget::processBackgroundFrames()
|
|||||||
|
|
||||||
QtUtils::resizeAndScalePixmap(&pm, widget_width, widget_height, dpr, m_background_scaling, m_background_opacity);
|
QtUtils::resizeAndScalePixmap(&pm, widget_width, widget_height, dpr, m_background_scaling, m_background_opacity);
|
||||||
|
|
||||||
QPalette bg_palette(m_ui.stack->palette());
|
m_background_pixmap = std::move(pm);
|
||||||
bg_palette.setBrush(QPalette::Base, pm);
|
m_ui.stack->update();
|
||||||
m_ui.stack->setPalette(bg_palette);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -725,6 +731,7 @@ bool GameListWidget::event(QEvent* event)
|
|||||||
if (event->type() == QEvent::DevicePixelRatioChange)
|
if (event->type() == QEvent::DevicePixelRatioChange)
|
||||||
{
|
{
|
||||||
m_model->setDevicePixelRatio(devicePixelRatioF());
|
m_model->setDevicePixelRatio(devicePixelRatioF());
|
||||||
|
processBackgroundFrames();
|
||||||
QWidget::event(event);
|
QWidget::event(event);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -732,6 +739,25 @@ bool GameListWidget::event(QEvent* event)
|
|||||||
return QWidget::event(event);
|
return QWidget::event(event);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool GameListWidget::eventFilter(QObject* watched, QEvent* event)
|
||||||
|
{
|
||||||
|
if (watched == m_ui.stack && event->type() == QEvent::Paint)
|
||||||
|
{
|
||||||
|
if (!m_background_pixmap.isNull())
|
||||||
|
{
|
||||||
|
QPainter painter(m_ui.stack);
|
||||||
|
const auto* paint_event = static_cast<QPaintEvent*>(event);
|
||||||
|
painter.save();
|
||||||
|
painter.setClipRect(paint_event->rect());
|
||||||
|
painter.drawTiledPixmap(m_ui.stack->rect(), m_background_pixmap);
|
||||||
|
painter.restore();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return QWidget::eventFilter(watched, event);
|
||||||
|
}
|
||||||
|
|
||||||
void GameListWidget::resizeTableViewColumnsToFit()
|
void GameListWidget::resizeTableViewColumnsToFit()
|
||||||
{
|
{
|
||||||
QtUtils::ResizeColumnsForTableView(m_table_view, {
|
QtUtils::ResizeColumnsForTableView(m_table_view, {
|
||||||
|
|||||||
@ -10,7 +10,9 @@
|
|||||||
#include "pcsx2/GameList.h"
|
#include "pcsx2/GameList.h"
|
||||||
|
|
||||||
#include <QtGui/QMovie>
|
#include <QtGui/QMovie>
|
||||||
|
#include <QtGui/QPixmap>
|
||||||
#include <QtWidgets/QListView>
|
#include <QtWidgets/QListView>
|
||||||
|
#include <QtWidgets/QStackedWidget>
|
||||||
#include <QtWidgets/QTableView>
|
#include <QtWidgets/QTableView>
|
||||||
|
|
||||||
Q_DECLARE_METATYPE(const GameList::Entry*);
|
Q_DECLARE_METATYPE(const GameList::Entry*);
|
||||||
@ -101,6 +103,7 @@ protected:
|
|||||||
void hideEvent(QHideEvent* event) override;
|
void hideEvent(QHideEvent* event) override;
|
||||||
void resizeEvent(QResizeEvent* event) override;
|
void resizeEvent(QResizeEvent* event) override;
|
||||||
bool event(QEvent* event) override;
|
bool event(QEvent* event) override;
|
||||||
|
bool eventFilter(QObject* watched, QEvent* event) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void loadTableHeaderState();
|
void loadTableHeaderState();
|
||||||
@ -123,6 +126,7 @@ private:
|
|||||||
GameListRefreshThread* m_refresh_thread = nullptr;
|
GameListRefreshThread* m_refresh_thread = nullptr;
|
||||||
|
|
||||||
QMovie* m_background_movie = nullptr;
|
QMovie* m_background_movie = nullptr;
|
||||||
|
QPixmap m_background_pixmap;
|
||||||
QtUtils::ScalingMode m_background_scaling = QtUtils::ScalingMode::Fit;
|
QtUtils::ScalingMode m_background_scaling = QtUtils::ScalingMode::Fit;
|
||||||
float m_background_opacity = 100.0f;
|
float m_background_opacity = 100.0f;
|
||||||
};
|
};
|
||||||
|
|||||||
@ -37,6 +37,7 @@
|
|||||||
#include "pcsx2/PerformanceMetrics.h"
|
#include "pcsx2/PerformanceMetrics.h"
|
||||||
#include "pcsx2/Recording/InputRecording.h"
|
#include "pcsx2/Recording/InputRecording.h"
|
||||||
#include "pcsx2/Recording/InputRecordingControls.h"
|
#include "pcsx2/Recording/InputRecordingControls.h"
|
||||||
|
#include "pcsx2/SaveState.h"
|
||||||
#include "pcsx2/SIO/Sio.h"
|
#include "pcsx2/SIO/Sio.h"
|
||||||
#include "pcsx2/GS/GSExtra.h"
|
#include "pcsx2/GS/GSExtra.h"
|
||||||
|
|
||||||
@ -1192,6 +1193,12 @@ void MainWindow::cancelGameListRefresh()
|
|||||||
m_game_list_widget->cancelRefresh();
|
m_game_list_widget->cancelRefresh();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MainWindow::updateGameListBackground()
|
||||||
|
{
|
||||||
|
if (m_game_list_widget)
|
||||||
|
m_game_list_widget->setCustomBackground();
|
||||||
|
}
|
||||||
|
|
||||||
void MainWindow::reportInfo(const QString& title, const QString& message)
|
void MainWindow::reportInfo(const QString& title, const QString& message)
|
||||||
{
|
{
|
||||||
QMessageBox::information(this, title, message);
|
QMessageBox::information(this, title, message);
|
||||||
@ -1213,6 +1220,103 @@ void MainWindow::onStatusMessage(const QString& message)
|
|||||||
m_ui.statusBar->showMessage(message);
|
m_ui.statusBar->showMessage(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MainWindow::reportStateLoadError(const QString& message, std::optional<s32> slot, bool backup)
|
||||||
|
{
|
||||||
|
const bool prompt_on_error = Host::GetBaseBoolSettingValue("UI", "PromptOnStateLoadSaveFailure", true);
|
||||||
|
if (!prompt_on_error)
|
||||||
|
{
|
||||||
|
SaveState_ReportLoadErrorOSD(message.toStdString(), slot, backup);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString title;
|
||||||
|
if (slot.has_value())
|
||||||
|
{
|
||||||
|
if (backup)
|
||||||
|
title = tr("Failed to Load State From Backup Slot %1").arg(*slot);
|
||||||
|
else
|
||||||
|
title = tr("Failed to Load State From Slot %1").arg(*slot);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
title = tr("Failed to Load State");
|
||||||
|
}
|
||||||
|
|
||||||
|
VMLock lock(pauseAndLockVM());
|
||||||
|
|
||||||
|
QCheckBox* do_not_show_again = new QCheckBox(tr("Do not show again"));
|
||||||
|
|
||||||
|
QPointer<QMessageBox> message_box = new QMessageBox(this);
|
||||||
|
message_box->setWindowTitle(title);
|
||||||
|
message_box->setText(message);
|
||||||
|
message_box->setIcon(QMessageBox::Critical);
|
||||||
|
message_box->addButton(QMessageBox::Ok);
|
||||||
|
message_box->setDefaultButton(QMessageBox::Ok);
|
||||||
|
message_box->setCheckBox(do_not_show_again);
|
||||||
|
|
||||||
|
message_box->exec();
|
||||||
|
if (message_box.isNull())
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (do_not_show_again->isChecked())
|
||||||
|
{
|
||||||
|
Host::SetBaseBoolSettingValue("UI", "PromptOnStateLoadSaveFailure", false);
|
||||||
|
Host::CommitBaseSettingChanges();
|
||||||
|
if (m_settings_window)
|
||||||
|
{
|
||||||
|
InterfaceSettingsWidget* interface_settings = m_settings_window->getInterfaceSettingsWidget();
|
||||||
|
interface_settings->updatePromptOnStateLoadSaveFailureCheckbox(Qt::Unchecked);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
delete message_box;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainWindow::reportStateSaveError(const QString& message, std::optional<s32> slot)
|
||||||
|
{
|
||||||
|
const bool prompt_on_error = Host::GetBaseBoolSettingValue("UI", "PromptOnStateLoadSaveFailure", true);
|
||||||
|
if (!prompt_on_error)
|
||||||
|
{
|
||||||
|
SaveState_ReportSaveErrorOSD(message.toStdString(), slot);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString title;
|
||||||
|
if (slot.has_value())
|
||||||
|
title = tr("Failed to Save State To Slot %1").arg(*slot);
|
||||||
|
else
|
||||||
|
title = tr("Failed to Save State");
|
||||||
|
|
||||||
|
VMLock lock(pauseAndLockVM());
|
||||||
|
|
||||||
|
QCheckBox* do_not_show_again = new QCheckBox(tr("Do not show again"));
|
||||||
|
|
||||||
|
QPointer<QMessageBox> message_box = new QMessageBox(this);
|
||||||
|
message_box->setWindowTitle(title);
|
||||||
|
message_box->setText(message);
|
||||||
|
message_box->setIcon(QMessageBox::Critical);
|
||||||
|
message_box->addButton(QMessageBox::Ok);
|
||||||
|
message_box->setDefaultButton(QMessageBox::Ok);
|
||||||
|
message_box->setCheckBox(do_not_show_again);
|
||||||
|
|
||||||
|
message_box->exec();
|
||||||
|
if (message_box.isNull())
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (do_not_show_again->isChecked())
|
||||||
|
{
|
||||||
|
Host::SetBaseBoolSettingValue("UI", "PromptOnStateLoadSaveFailure", false);
|
||||||
|
Host::CommitBaseSettingChanges();
|
||||||
|
if (m_settings_window)
|
||||||
|
{
|
||||||
|
InterfaceSettingsWidget* interface_settings = m_settings_window->getInterfaceSettingsWidget();
|
||||||
|
interface_settings->updatePromptOnStateLoadSaveFailureCheckbox(Qt::Unchecked);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
delete message_box;
|
||||||
|
}
|
||||||
|
|
||||||
void MainWindow::runOnUIThread(const std::function<void()>& func)
|
void MainWindow::runOnUIThread(const std::function<void()>& func)
|
||||||
{
|
{
|
||||||
func();
|
func();
|
||||||
@ -1734,8 +1838,8 @@ void MainWindow::onCreateGameShortcutTriggered()
|
|||||||
|
|
||||||
const QString title = QString::fromStdString(entry->GetTitle());
|
const QString title = QString::fromStdString(entry->GetTitle());
|
||||||
const QString path = QString::fromStdString(entry->path);
|
const QString path = QString::fromStdString(entry->path);
|
||||||
VMLock lock(pauseAndLockVM());
|
|
||||||
ShortcutCreationDialog dlg(lock.getDialogParent(), title, path);
|
ShortcutCreationDialog dlg(this, title, path);
|
||||||
dlg.exec();
|
dlg.exec();
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@ -116,10 +116,13 @@ public Q_SLOTS:
|
|||||||
void checkForUpdates(bool display_message, bool force_check);
|
void checkForUpdates(bool display_message, bool force_check);
|
||||||
void refreshGameList(bool invalidate_cache, bool popup_on_error);
|
void refreshGameList(bool invalidate_cache, bool popup_on_error);
|
||||||
void cancelGameListRefresh();
|
void cancelGameListRefresh();
|
||||||
|
void updateGameListBackground();
|
||||||
void reportInfo(const QString& title, const QString& message);
|
void reportInfo(const QString& title, const QString& message);
|
||||||
void reportError(const QString& title, const QString& message);
|
void reportError(const QString& title, const QString& message);
|
||||||
bool confirmMessage(const QString& title, const QString& message);
|
bool confirmMessage(const QString& title, const QString& message);
|
||||||
void onStatusMessage(const QString& message);
|
void onStatusMessage(const QString& message);
|
||||||
|
void reportStateLoadError(const QString& message, std::optional<s32> slot, bool backup);
|
||||||
|
void reportStateSaveError(const QString& message, std::optional<s32> slot);
|
||||||
|
|
||||||
void runOnUIThread(const std::function<void()>& func);
|
void runOnUIThread(const std::function<void()>& func);
|
||||||
void requestReset();
|
void requestReset();
|
||||||
|
|||||||
@ -194,6 +194,9 @@ void EmuThread::stopFullscreenUI()
|
|||||||
{
|
{
|
||||||
m_run_fullscreen_ui.store(false, std::memory_order_release);
|
m_run_fullscreen_ui.store(false, std::memory_order_release);
|
||||||
emit onFullscreenUIStateChange(false);
|
emit onFullscreenUIStateChange(false);
|
||||||
|
|
||||||
|
// Resume and refresh background when FullscreenUI exits
|
||||||
|
QMetaObject::invokeMethod(g_main_window, "updateGameListBackground", Qt::QueuedConnection);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -299,7 +302,11 @@ void EmuThread::loadState(const QString& filename)
|
|||||||
|
|
||||||
Error error;
|
Error error;
|
||||||
if (!VMManager::LoadState(filename.toUtf8().constData(), &error))
|
if (!VMManager::LoadState(filename.toUtf8().constData(), &error))
|
||||||
Host::ReportErrorAsync(TRANSLATE_SV("QtHost", "Failed to Load State"), error.GetDescription());
|
{
|
||||||
|
QtHost::RunOnUIThread([message = QString::fromStdString(error.GetDescription())]() {
|
||||||
|
g_main_window->reportStateLoadError(message, std::nullopt, false);
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void EmuThread::loadStateFromSlot(qint32 slot, bool load_backup)
|
void EmuThread::loadStateFromSlot(qint32 slot, bool load_backup)
|
||||||
@ -315,7 +322,11 @@ void EmuThread::loadStateFromSlot(qint32 slot, bool load_backup)
|
|||||||
|
|
||||||
Error error;
|
Error error;
|
||||||
if (!VMManager::LoadStateFromSlot(slot, load_backup, &error))
|
if (!VMManager::LoadStateFromSlot(slot, load_backup, &error))
|
||||||
Host::ReportErrorAsync(TRANSLATE_SV("QtHost", "Failed to Load State"), error.GetDescription());
|
{
|
||||||
|
QtHost::RunOnUIThread([message = QString::fromStdString(error.GetDescription()), slot, load_backup]() {
|
||||||
|
g_main_window->reportStateLoadError(message, slot, load_backup);
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void EmuThread::saveState(const QString& filename)
|
void EmuThread::saveState(const QString& filename)
|
||||||
@ -329,11 +340,11 @@ void EmuThread::saveState(const QString& filename)
|
|||||||
if (!VMManager::HasValidVM())
|
if (!VMManager::HasValidVM())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (!VMManager::SaveState(filename.toUtf8().constData()))
|
VMManager::SaveState(filename.toUtf8().constData(), true, false, [](const std::string& error) {
|
||||||
{
|
QtHost::RunOnUIThread([message = QString::fromStdString(error)]() {
|
||||||
// this one is usually the result of a user-chosen path, so we can display a message box safely here
|
g_main_window->reportStateSaveError(message, std::nullopt);
|
||||||
Console.Error("Failed to save state");
|
});
|
||||||
}
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void EmuThread::saveStateToSlot(qint32 slot)
|
void EmuThread::saveStateToSlot(qint32 slot)
|
||||||
@ -347,7 +358,11 @@ void EmuThread::saveStateToSlot(qint32 slot)
|
|||||||
if (!VMManager::HasValidVM())
|
if (!VMManager::HasValidVM())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
VMManager::SaveStateToSlot(slot);
|
VMManager::SaveStateToSlot(slot, true, [slot](const std::string& error) {
|
||||||
|
QtHost::RunOnUIThread([message = QString::fromStdString(error), slot]() {
|
||||||
|
g_main_window->reportStateSaveError(message, slot);
|
||||||
|
});
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void EmuThread::run()
|
void EmuThread::run()
|
||||||
|
|||||||
@ -141,9 +141,16 @@ namespace QtUtils
|
|||||||
|
|
||||||
void resizeAndScalePixmap(QPixmap* pm, const int expected_width, const int expected_height, const qreal dpr, const ScalingMode scaling_mode, const float opacity)
|
void resizeAndScalePixmap(QPixmap* pm, const int expected_width, const int expected_height, const qreal dpr, const ScalingMode scaling_mode, const float opacity)
|
||||||
{
|
{
|
||||||
if (!pm || pm->isNull() || pm->width() <= 0 || pm->height() <= 0)
|
if (!pm || pm->width() <= 0 || pm->height() <= 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
if (dpr <= 0.0)
|
||||||
|
{
|
||||||
|
Console.ErrorFmt("resizeAndScalePixmap: Invalid device pixel ratio ({}) - pixmap will be null", dpr);
|
||||||
|
*pm = QPixmap();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const int dpr_expected_width = qRound(expected_width * dpr);
|
const int dpr_expected_width = qRound(expected_width * dpr);
|
||||||
const int dpr_expected_height = qRound(expected_height * dpr);
|
const int dpr_expected_height = qRound(expected_height * dpr);
|
||||||
|
|
||||||
@ -196,8 +203,7 @@ namespace QtUtils
|
|||||||
qRound(scaledSize.width()),
|
qRound(scaledSize.width()),
|
||||||
qRound(scaledSize.height()),
|
qRound(scaledSize.height()),
|
||||||
Qt::IgnoreAspectRatio,
|
Qt::IgnoreAspectRatio,
|
||||||
Qt::SmoothTransformation
|
Qt::SmoothTransformation);
|
||||||
);
|
|
||||||
|
|
||||||
const QRectF scaledSrcRect(0, 0, pm->width(), pm->height());
|
const QRectF scaledSrcRect(0, 0, pm->width(), pm->height());
|
||||||
|
|
||||||
@ -211,16 +217,7 @@ namespace QtUtils
|
|||||||
}
|
}
|
||||||
case ScalingMode::Stretch:
|
case ScalingMode::Stretch:
|
||||||
{
|
{
|
||||||
*pm = pm->scaled(
|
painter.drawPixmap(painterRect, *pm, srcRect);
|
||||||
dpr_expected_width,
|
|
||||||
dpr_expected_height,
|
|
||||||
Qt::IgnoreAspectRatio,
|
|
||||||
Qt::SmoothTransformation
|
|
||||||
);
|
|
||||||
|
|
||||||
const QRectF scaledSrcRect(0, 0, pm->width(), pm->height());
|
|
||||||
|
|
||||||
painter.drawPixmap(painterRect, *pm, scaledSrcRect);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case ScalingMode::Center:
|
case ScalingMode::Center:
|
||||||
@ -229,7 +226,6 @@ namespace QtUtils
|
|||||||
const qreal pmHeight = pm->height() / dpr;
|
const qreal pmHeight = pm->height() / dpr;
|
||||||
|
|
||||||
QRectF destRect(0, 0, pmWidth, pmHeight);
|
QRectF destRect(0, 0, pmWidth, pmHeight);
|
||||||
|
|
||||||
destRect.moveCenter(painterRect.center());
|
destRect.moveCenter(painterRect.center());
|
||||||
|
|
||||||
painter.drawPixmap(destRect, *pm, srcRect);
|
painter.drawPixmap(destRect, *pm, srcRect);
|
||||||
@ -243,13 +239,19 @@ namespace QtUtils
|
|||||||
if (tileWidth <= 0 || tileHeight <= 0)
|
if (tileWidth <= 0 || tileHeight <= 0)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
QPixmap tileSource = pm->scaled(tileWidth, tileHeight, Qt::KeepAspectRatio, Qt::SmoothTransformation);
|
if (pm->devicePixelRatio() == dpr)
|
||||||
tileSource.setDevicePixelRatio(dpr);
|
{
|
||||||
|
QBrush tileBrush(*pm);
|
||||||
QBrush tileBrush(tileSource);
|
painter.fillRect(painterRect, tileBrush);
|
||||||
tileBrush.setTextureImage(tileSource.toImage());
|
}
|
||||||
|
else
|
||||||
painter.fillRect(painterRect, tileBrush);
|
{
|
||||||
|
QPixmap tileSource = pm->scaled(tileWidth, tileHeight, Qt::KeepAspectRatio, Qt::SmoothTransformation);
|
||||||
|
tileSource.setDevicePixelRatio(dpr);
|
||||||
|
QBrush tileBrush(tileSource);
|
||||||
|
tileBrush.setTextureImage(tileSource.toImage());
|
||||||
|
painter.fillRect(painterRect, tileBrush);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
|
|||||||
@ -39,6 +39,8 @@ BIOSSettingsWidget::BIOSSettingsWidget(SettingsWindow* settings_dialog, QWidget*
|
|||||||
connect(m_ui.refresh, &QPushButton::clicked, this, &BIOSSettingsWidget::refreshList);
|
connect(m_ui.refresh, &QPushButton::clicked, this, &BIOSSettingsWidget::refreshList);
|
||||||
connect(m_ui.fileList, &QTreeWidget::currentItemChanged, this, &BIOSSettingsWidget::listItemChanged);
|
connect(m_ui.fileList, &QTreeWidget::currentItemChanged, this, &BIOSSettingsWidget::listItemChanged);
|
||||||
connect(m_ui.fastBoot, &QCheckBox::checkStateChanged, this, &BIOSSettingsWidget::fastBootChanged);
|
connect(m_ui.fastBoot, &QCheckBox::checkStateChanged, this, &BIOSSettingsWidget::fastBootChanged);
|
||||||
|
|
||||||
|
fastBootChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
BIOSSettingsWidget::~BIOSSettingsWidget() = default;
|
BIOSSettingsWidget::~BIOSSettingsWidget() = default;
|
||||||
|
|||||||
@ -40,6 +40,9 @@ GameCheatSettingsWidget::GameCheatSettingsWidget(SettingsWindow* settings_dialog
|
|||||||
|
|
||||||
m_ui.cheatList->expandAll();
|
m_ui.cheatList->expandAll();
|
||||||
|
|
||||||
|
m_ui.cheatList->viewport()->installEventFilter(this);
|
||||||
|
m_ui.cheatList->viewport()->setMouseTracking(true);
|
||||||
|
|
||||||
SettingsInterface* sif = dialog()->getSettingsInterface();
|
SettingsInterface* sif = dialog()->getSettingsInterface();
|
||||||
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.enableCheats, "EmuCore", "EnableCheats", false);
|
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.enableCheats, "EmuCore", "EnableCheats", false);
|
||||||
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.allCRCsCheckbox, "EmuCore", "ShowCheatsForAllCRCs", false);
|
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.allCRCsCheckbox, "EmuCore", "ShowCheatsForAllCRCs", false);
|
||||||
@ -83,7 +86,7 @@ void GameCheatSettingsWidget::onCheatListItemDoubleClicked(const QModelIndex& in
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
QVariant data = item->data(Qt::UserRole);
|
QVariant data = item->data(NAME_ROLE);
|
||||||
if (!data.isValid())
|
if (!data.isValid())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@ -95,7 +98,7 @@ void GameCheatSettingsWidget::onCheatListItemDoubleClicked(const QModelIndex& in
|
|||||||
|
|
||||||
void GameCheatSettingsWidget::onCheatListItemChanged(QStandardItem* item)
|
void GameCheatSettingsWidget::onCheatListItemChanged(QStandardItem* item)
|
||||||
{
|
{
|
||||||
QVariant data = item->data(Qt::UserRole);
|
QVariant data = item->data(NAME_ROLE);
|
||||||
if (!data.isValid())
|
if (!data.isValid())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@ -109,6 +112,31 @@ void GameCheatSettingsWidget::onCheatListItemChanged(QStandardItem* item)
|
|||||||
setCheatEnabled(std::move(cheat_name), current_checked, true);
|
setCheatEnabled(std::move(cheat_name), current_checked, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void GameCheatSettingsWidget::onCheatListItemHovered(const QModelIndex& index)
|
||||||
|
{
|
||||||
|
const QModelIndex source_index = m_model_proxy->mapToSource(index);
|
||||||
|
const QModelIndex sibling_index = source_index.siblingAtColumn(0);
|
||||||
|
QStandardItem* item = m_model->itemFromIndex(sibling_index);
|
||||||
|
if (!item)
|
||||||
|
{
|
||||||
|
// No item is selected.
|
||||||
|
m_ui.appliedLabel->clear();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<Patch::patch_place_type> place;
|
||||||
|
|
||||||
|
bool ok;
|
||||||
|
int place_value = item->data(PLACE_ROLE).toInt(&ok);
|
||||||
|
if (ok)
|
||||||
|
{
|
||||||
|
// The patch commands in the group are all applied at the same time.
|
||||||
|
place = static_cast<Patch::patch_place_type>(place_value);
|
||||||
|
}
|
||||||
|
|
||||||
|
m_ui.appliedLabel->setText(tr("<strong>Applied:</strong> %1").arg(Patch::PlaceToString(place)));
|
||||||
|
}
|
||||||
|
|
||||||
void GameCheatSettingsWidget::onReloadClicked()
|
void GameCheatSettingsWidget::onReloadClicked()
|
||||||
{
|
{
|
||||||
reloadList();
|
reloadList();
|
||||||
@ -136,6 +164,32 @@ void GameCheatSettingsWidget::disableAllCheats()
|
|||||||
si->Save();
|
si->Save();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool GameCheatSettingsWidget::eventFilter(QObject* watched, QEvent* event)
|
||||||
|
{
|
||||||
|
if (watched == m_ui.cheatList->viewport())
|
||||||
|
{
|
||||||
|
switch (event->type())
|
||||||
|
{
|
||||||
|
case QEvent::MouseMove:
|
||||||
|
{
|
||||||
|
QMouseEvent* mouse_event = static_cast<QMouseEvent*>(event);
|
||||||
|
onCheatListItemHovered(m_ui.cheatList->indexAt(mouse_event->position().toPoint()));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
case QEvent::Leave:
|
||||||
|
{
|
||||||
|
onCheatListItemHovered(QModelIndex());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return SettingsWidget::eventFilter(watched, event);
|
||||||
|
}
|
||||||
|
|
||||||
void GameCheatSettingsWidget::resizeEvent(QResizeEvent* event)
|
void GameCheatSettingsWidget::resizeEvent(QResizeEvent* event)
|
||||||
{
|
{
|
||||||
QWidget::resizeEvent(event);
|
QWidget::resizeEvent(event);
|
||||||
@ -185,7 +239,7 @@ void GameCheatSettingsWidget::setStateRecursively(QStandardItem* parent, bool en
|
|||||||
for (int i = 0; i < count; i++)
|
for (int i = 0; i < count; i++)
|
||||||
{
|
{
|
||||||
QStandardItem* item = parent ? parent->child(i, 0) : m_model->item(i, 0);
|
QStandardItem* item = parent ? parent->child(i, 0) : m_model->item(i, 0);
|
||||||
QVariant data = item->data(Qt::UserRole);
|
QVariant data = item->data(NAME_ROLE);
|
||||||
if (data.isValid())
|
if (data.isValid())
|
||||||
{
|
{
|
||||||
if ((item->checkState() == Qt::Checked) != enabled)
|
if ((item->checkState() == Qt::Checked) != enabled)
|
||||||
@ -277,7 +331,9 @@ QList<QStandardItem*> GameCheatSettingsWidget::populateTreeViewRow(const Patch::
|
|||||||
const std::string_view name_part = pi.GetNamePart();
|
const std::string_view name_part = pi.GetNamePart();
|
||||||
nameItem->setFlags(Qt::ItemIsUserCheckable | Qt::ItemNeverHasChildren | Qt::ItemIsEnabled);
|
nameItem->setFlags(Qt::ItemIsUserCheckable | Qt::ItemNeverHasChildren | Qt::ItemIsEnabled);
|
||||||
nameItem->setCheckState(enabled ? Qt::Checked : Qt::Unchecked);
|
nameItem->setCheckState(enabled ? Qt::Checked : Qt::Unchecked);
|
||||||
nameItem->setData(QString::fromStdString(pi.name), Qt::UserRole);
|
nameItem->setData(QString::fromStdString(pi.name), NAME_ROLE);
|
||||||
|
if (pi.place.has_value())
|
||||||
|
nameItem->setData(static_cast<int>(*pi.place), PLACE_ROLE);
|
||||||
if (!name_part.empty())
|
if (!name_part.empty())
|
||||||
nameItem->setText(QString::fromUtf8(name_part.data(), name_part.length()));
|
nameItem->setText(QString::fromUtf8(name_part.data(), name_part.length()));
|
||||||
|
|
||||||
|
|||||||
@ -32,6 +32,7 @@ public:
|
|||||||
~GameCheatSettingsWidget();
|
~GameCheatSettingsWidget();
|
||||||
|
|
||||||
void disableAllCheats();
|
void disableAllCheats();
|
||||||
|
bool eventFilter(QObject* watched, QEvent* event) override;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void resizeEvent(QResizeEvent* event) override;
|
void resizeEvent(QResizeEvent* event) override;
|
||||||
@ -39,6 +40,7 @@ protected:
|
|||||||
private Q_SLOTS:
|
private Q_SLOTS:
|
||||||
void onCheatListItemDoubleClicked(const QModelIndex& index);
|
void onCheatListItemDoubleClicked(const QModelIndex& index);
|
||||||
void onCheatListItemChanged(QStandardItem* item);
|
void onCheatListItemChanged(QStandardItem* item);
|
||||||
|
void onCheatListItemHovered(const QModelIndex& index);
|
||||||
void onReloadClicked();
|
void onReloadClicked();
|
||||||
void updateListEnabled();
|
void updateListEnabled();
|
||||||
void reloadList();
|
void reloadList();
|
||||||
@ -50,6 +52,12 @@ private:
|
|||||||
void setStateForAll(bool enabled);
|
void setStateForAll(bool enabled);
|
||||||
void setStateRecursively(QStandardItem* parent, bool enabled);
|
void setStateRecursively(QStandardItem* parent, bool enabled);
|
||||||
|
|
||||||
|
enum Roles
|
||||||
|
{
|
||||||
|
NAME_ROLE = Qt::UserRole,
|
||||||
|
PLACE_ROLE = Qt::UserRole + 1
|
||||||
|
};
|
||||||
|
|
||||||
Ui::GameCheatSettingsWidget m_ui;
|
Ui::GameCheatSettingsWidget m_ui;
|
||||||
QStandardItemModel* m_model = nullptr;
|
QStandardItemModel* m_model = nullptr;
|
||||||
QSortFilterProxyModel* m_model_proxy = nullptr;
|
QSortFilterProxyModel* m_model_proxy = nullptr;
|
||||||
|
|||||||
@ -90,6 +90,13 @@
|
|||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="appliedLabel">
|
||||||
|
<property name="text">
|
||||||
|
<string notr="true"/>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<spacer name="horizontalSpacer">
|
<spacer name="horizontalSpacer">
|
||||||
<property name="orientation">
|
<property name="orientation">
|
||||||
|
|||||||
@ -15,19 +15,23 @@
|
|||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
|
||||||
GamePatchDetailsWidget::GamePatchDetailsWidget(std::string name, const std::string& author,
|
GamePatchDetailsWidget::GamePatchDetailsWidget(const Patch::PatchInfo& info, bool tristate, Qt::CheckState checkState, SettingsWindow* dialog, QWidget* parent)
|
||||||
const std::string& description, bool tristate, Qt::CheckState checkState, SettingsWindow* dialog, QWidget* parent)
|
|
||||||
: QWidget(parent)
|
: QWidget(parent)
|
||||||
, m_dialog(dialog)
|
, m_dialog(dialog)
|
||||||
, m_name(name)
|
, m_name(info.name)
|
||||||
{
|
{
|
||||||
m_ui.setupUi(this);
|
m_ui.setupUi(this);
|
||||||
|
|
||||||
m_ui.name->setText(QString::fromStdString(name));
|
const QString name = QString::fromStdString(info.name);
|
||||||
|
const QString author = !info.author.empty() ? QString::fromStdString(info.author) : tr("Unknown");
|
||||||
|
const QString place = QString::fromUtf8(PlaceToString(info.place));
|
||||||
|
const QString description = !info.description.empty() ? QString::fromStdString(info.description) : tr("No description provided.");
|
||||||
|
m_ui.name->setText(name);
|
||||||
m_ui.description->setText(
|
m_ui.description->setText(
|
||||||
tr("<strong>Author: </strong>%1<br>%2")
|
tr("<strong>Author:</strong> %1<br><strong>Applied:</strong> %2<br>%3")
|
||||||
.arg(author.empty() ? tr("Unknown") : QString::fromStdString(author))
|
.arg(author)
|
||||||
.arg(description.empty() ? tr("No description provided.") : QString::fromStdString(description)));
|
.arg(place)
|
||||||
|
.arg(description));
|
||||||
|
|
||||||
pxAssert(dialog->getSettingsInterface());
|
pxAssert(dialog->getSettingsInterface());
|
||||||
m_ui.enabled->setTristate(tristate);
|
m_ui.enabled->setTristate(tristate);
|
||||||
@ -178,7 +182,7 @@ void GamePatchSettingsWidget::reloadList()
|
|||||||
}
|
}
|
||||||
|
|
||||||
GamePatchDetailsWidget* it =
|
GamePatchDetailsWidget* it =
|
||||||
new GamePatchDetailsWidget(std::move(pi.name), pi.author, pi.description, globally_toggleable_option, check_state, dialog(), container);
|
new GamePatchDetailsWidget(pi, globally_toggleable_option, check_state, dialog(), container);
|
||||||
layout->addWidget(it);
|
layout->addWidget(it);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -20,7 +20,7 @@ class GamePatchDetailsWidget : public QWidget
|
|||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
GamePatchDetailsWidget(std::string name, const std::string& author, const std::string& description, bool tristate, Qt::CheckState checkState,
|
GamePatchDetailsWidget(const Patch::PatchInfo& info, bool tristate, Qt::CheckState checkState,
|
||||||
SettingsWindow* dialog, QWidget* parent);
|
SettingsWindow* dialog, QWidget* parent);
|
||||||
~GamePatchDetailsWidget();
|
~GamePatchDetailsWidget();
|
||||||
|
|
||||||
|
|||||||
@ -97,15 +97,16 @@ InterfaceSettingsWidget::InterfaceSettingsWidget(SettingsWindow* settings_dialog
|
|||||||
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.confirmShutdown, "UI", "ConfirmShutdown", true);
|
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.confirmShutdown, "UI", "ConfirmShutdown", true);
|
||||||
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.pauseOnFocusLoss, "UI", "PauseOnFocusLoss", false);
|
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.pauseOnFocusLoss, "UI", "PauseOnFocusLoss", false);
|
||||||
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.pauseOnControllerDisconnection, "UI", "PauseOnControllerDisconnection", false);
|
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.pauseOnControllerDisconnection, "UI", "PauseOnControllerDisconnection", false);
|
||||||
|
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.promptOnStateLoadSaveFailure, "UI", "PromptOnStateLoadSaveFailure", true);
|
||||||
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.discordPresence, "EmuCore", "EnableDiscordPresence", false);
|
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.discordPresence, "EmuCore", "EnableDiscordPresence", false);
|
||||||
|
|
||||||
#ifdef __linux__ // Mouse locking is only supported on X11
|
#ifdef __linux__ // Mouse locking is only supported on X11
|
||||||
const bool mouse_lock_supported = QGuiApplication::platformName().toLower() == "xcb";
|
const bool mouse_lock_supported = QGuiApplication::platformName().toLower() == "xcb";
|
||||||
#else
|
#else
|
||||||
const bool mouse_lock_supported = true;
|
const bool mouse_lock_supported = true;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if(mouse_lock_supported)
|
if (mouse_lock_supported)
|
||||||
{
|
{
|
||||||
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.mouseLock, "EmuCore", "EnableMouseLock", false);
|
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.mouseLock, "EmuCore", "EnableMouseLock", false);
|
||||||
connect(m_ui.mouseLock, &QCheckBox::checkStateChanged, [](Qt::CheckState state) {
|
connect(m_ui.mouseLock, &QCheckBox::checkStateChanged, [](Qt::CheckState state) {
|
||||||
@ -196,6 +197,8 @@ InterfaceSettingsWidget::InterfaceSettingsWidget(SettingsWindow* settings_dialog
|
|||||||
"and unpauses when you switch back."));
|
"and unpauses when you switch back."));
|
||||||
dialog()->registerWidgetHelp(m_ui.pauseOnControllerDisconnection, tr("Pause On Controller Disconnection"),
|
dialog()->registerWidgetHelp(m_ui.pauseOnControllerDisconnection, tr("Pause On Controller Disconnection"),
|
||||||
tr("Unchecked"), tr("Pauses the emulator when a controller with bindings is disconnected."));
|
tr("Unchecked"), tr("Pauses the emulator when a controller with bindings is disconnected."));
|
||||||
|
dialog()->registerWidgetHelp(m_ui.promptOnStateLoadSaveFailure, tr("Pause On State Load/Save Failure"),
|
||||||
|
tr("Checked"), tr("Display a modal dialog when a save state load/save operation fails."));
|
||||||
dialog()->registerWidgetHelp(m_ui.startFullscreen, tr("Start Fullscreen"), tr("Unchecked"),
|
dialog()->registerWidgetHelp(m_ui.startFullscreen, tr("Start Fullscreen"), tr("Unchecked"),
|
||||||
tr("Automatically switches to fullscreen mode when a game is started."));
|
tr("Automatically switches to fullscreen mode when a game is started."));
|
||||||
dialog()->registerWidgetHelp(m_ui.hideMouseCursor, tr("Hide Cursor In Fullscreen"), tr("Unchecked"),
|
dialog()->registerWidgetHelp(m_ui.hideMouseCursor, tr("Hide Cursor In Fullscreen"), tr("Unchecked"),
|
||||||
@ -224,7 +227,7 @@ InterfaceSettingsWidget::InterfaceSettingsWidget(SettingsWindow* settings_dialog
|
|||||||
dialog()->registerWidgetHelp(
|
dialog()->registerWidgetHelp(
|
||||||
m_ui.backgroundBrowse, tr("Game List Background"), tr("None"),
|
m_ui.backgroundBrowse, tr("Game List Background"), tr("None"),
|
||||||
tr("Enable an animated/static background on the game list (where you launch your games).<br>"
|
tr("Enable an animated/static background on the game list (where you launch your games).<br>"
|
||||||
"This background is only visible in the library and will be hidden once a game is launched. It will also be paused when it's not in focus."));
|
"This background is only visible in the library and will be hidden once a game is launched. It will also be paused when it's not in focus."));
|
||||||
dialog()->registerWidgetHelp(
|
dialog()->registerWidgetHelp(
|
||||||
m_ui.backgroundReset, tr("Disable/Reset Game List Background"), tr("None"),
|
m_ui.backgroundReset, tr("Disable/Reset Game List Background"), tr("None"),
|
||||||
tr("Disable and reset the currently applied game list background."));
|
tr("Disable and reset the currently applied game list background."));
|
||||||
@ -241,6 +244,12 @@ InterfaceSettingsWidget::InterfaceSettingsWidget(SettingsWindow* settings_dialog
|
|||||||
|
|
||||||
InterfaceSettingsWidget::~InterfaceSettingsWidget() = default;
|
InterfaceSettingsWidget::~InterfaceSettingsWidget() = default;
|
||||||
|
|
||||||
|
void InterfaceSettingsWidget::updatePromptOnStateLoadSaveFailureCheckbox(Qt::CheckState state)
|
||||||
|
{
|
||||||
|
QSignalBlocker blocker(m_ui.promptOnStateLoadSaveFailure);
|
||||||
|
m_ui.promptOnStateLoadSaveFailure->setCheckState(state);
|
||||||
|
}
|
||||||
|
|
||||||
void InterfaceSettingsWidget::onRenderToSeparateWindowChanged()
|
void InterfaceSettingsWidget::onRenderToSeparateWindowChanged()
|
||||||
{
|
{
|
||||||
m_ui.hideMainWindow->setEnabled(m_ui.renderToSeparateWindow->isChecked());
|
m_ui.hideMainWindow->setEnabled(m_ui.renderToSeparateWindow->isChecked());
|
||||||
@ -266,7 +275,7 @@ void InterfaceSettingsWidget::onSetGameListBackgroundTriggered()
|
|||||||
if (path.isEmpty())
|
if (path.isEmpty())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
std::string relative_path = Path::MakeRelative(QDir::toNativeSeparators(path).toStdString(), EmuFolders::DataRoot);
|
std::string relative_path = Path::MakeRelative(path.toStdString(), EmuFolders::DataRoot);
|
||||||
Host::SetBaseStringSettingValue("UI", "GameListBackgroundPath", relative_path.c_str());
|
Host::SetBaseStringSettingValue("UI", "GameListBackgroundPath", relative_path.c_str());
|
||||||
|
|
||||||
Host::CommitBaseSettingChanges();
|
Host::CommitBaseSettingChanges();
|
||||||
|
|||||||
@ -15,6 +15,8 @@ public:
|
|||||||
InterfaceSettingsWidget(SettingsWindow* settings_dialog, QWidget* parent);
|
InterfaceSettingsWidget(SettingsWindow* settings_dialog, QWidget* parent);
|
||||||
~InterfaceSettingsWidget();
|
~InterfaceSettingsWidget();
|
||||||
|
|
||||||
|
void updatePromptOnStateLoadSaveFailureCheckbox(Qt::CheckState state);
|
||||||
|
|
||||||
Q_SIGNALS:
|
Q_SIGNALS:
|
||||||
void themeChanged();
|
void themeChanged();
|
||||||
void languageChanged();
|
void languageChanged();
|
||||||
|
|||||||
@ -7,7 +7,7 @@
|
|||||||
<x>0</x>
|
<x>0</x>
|
||||||
<y>0</y>
|
<y>0</y>
|
||||||
<width>725</width>
|
<width>725</width>
|
||||||
<height>625</height>
|
<height>637</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
<layout class="QVBoxLayout" name="verticalLayout">
|
<layout class="QVBoxLayout" name="verticalLayout">
|
||||||
@ -78,6 +78,13 @@
|
|||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
<item row="3" column="1">
|
||||||
|
<widget class="QCheckBox" name="promptOnStateLoadSaveFailure">
|
||||||
|
<property name="text">
|
||||||
|
<string>Prompt On State Load/Save Failure</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
@ -380,6 +387,7 @@
|
|||||||
<tabstop>discordPresence</tabstop>
|
<tabstop>discordPresence</tabstop>
|
||||||
<tabstop>pauseOnControllerDisconnection</tabstop>
|
<tabstop>pauseOnControllerDisconnection</tabstop>
|
||||||
<tabstop>mouseLock</tabstop>
|
<tabstop>mouseLock</tabstop>
|
||||||
|
<tabstop>promptOnStateLoadSaveFailure</tabstop>
|
||||||
<tabstop>startFullscreen</tabstop>
|
<tabstop>startFullscreen</tabstop>
|
||||||
<tabstop>doubleClickTogglesFullscreen</tabstop>
|
<tabstop>doubleClickTogglesFullscreen</tabstop>
|
||||||
<tabstop>renderToSeparateWindow</tabstop>
|
<tabstop>renderToSeparateWindow</tabstop>
|
||||||
|
|||||||
@ -13,7 +13,7 @@
|
|||||||
#include "VMManager.h"
|
#include "VMManager.h"
|
||||||
|
|
||||||
#if defined(_WIN32)
|
#if defined(_WIN32)
|
||||||
#include <Windows.h>
|
#include "common/RedtapeWindows.h"
|
||||||
#include <shlobj.h>
|
#include <shlobj.h>
|
||||||
#include <winnls.h>
|
#include <winnls.h>
|
||||||
#include <shobjidl.h>
|
#include <shobjidl.h>
|
||||||
@ -61,16 +61,6 @@ ShortcutCreationDialog::ShortcutCreationDialog(QWidget* parent, const QString& t
|
|||||||
connect(m_ui.bootOptionToggle, &QCheckBox::toggled, m_ui.bootOptionDropdown, &QPushButton::setEnabled);
|
connect(m_ui.bootOptionToggle, &QCheckBox::toggled, m_ui.bootOptionDropdown, &QPushButton::setEnabled);
|
||||||
connect(m_ui.fullscreenMode, &QCheckBox::toggled, m_ui.fullscreenModeDropdown, &QPushButton::setEnabled);
|
connect(m_ui.fullscreenMode, &QCheckBox::toggled, m_ui.fullscreenModeDropdown, &QPushButton::setEnabled);
|
||||||
|
|
||||||
m_ui.shortcutDesktop->setChecked(true);
|
|
||||||
m_ui.overrideBootELFPath->setEnabled(false);
|
|
||||||
m_ui.overrideBootELFButton->setEnabled(false);
|
|
||||||
m_ui.gameArgs->setEnabled(false);
|
|
||||||
m_ui.bootOptionDropdown->setEnabled(false);
|
|
||||||
m_ui.fullscreenModeDropdown->setEnabled(false);
|
|
||||||
m_ui.loadStateIndex->setEnabled(false);
|
|
||||||
m_ui.loadStateFileBrowse->setEnabled(false);
|
|
||||||
m_ui.loadStateFilePath->setEnabled(false);
|
|
||||||
|
|
||||||
m_ui.loadStateIndex->setMaximum(VMManager::NUM_SAVE_STATE_SLOTS);
|
m_ui.loadStateIndex->setMaximum(VMManager::NUM_SAVE_STATE_SLOTS);
|
||||||
|
|
||||||
if (std::getenv("container"))
|
if (std::getenv("container"))
|
||||||
@ -80,6 +70,7 @@ ShortcutCreationDialog::ShortcutCreationDialog(QWidget* parent, const QString& t
|
|||||||
m_ui.shortcutStartMenu->setEnabled(false);
|
m_ui.shortcutStartMenu->setEnabled(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
connect(m_ui.dialogButtons, &QDialogButtonBox::rejected, this, &QDialog::reject);
|
||||||
connect(m_ui.dialogButtons, &QDialogButtonBox::accepted, this, [&]() {
|
connect(m_ui.dialogButtons, &QDialogButtonBox::accepted, this, [&]() {
|
||||||
std::vector<std::string> args;
|
std::vector<std::string> args;
|
||||||
|
|
||||||
@ -111,21 +102,24 @@ ShortcutCreationDialog::ShortcutCreationDialog(QWidget* parent, const QString& t
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (m_ui.loadStateFileToggle->isChecked() && !m_ui.loadStateFilePath->text().isEmpty())
|
||||||
|
{
|
||||||
|
args.push_back("-statefile");
|
||||||
|
args.push_back(m_ui.loadStateFilePath->text().toStdString());
|
||||||
|
}
|
||||||
|
|
||||||
if (m_ui.fullscreenMode->isChecked())
|
if (m_ui.fullscreenMode->isChecked())
|
||||||
args.push_back(m_ui.fullscreenModeDropdown->currentIndex() ? "-nofullscreen" : "-fullscreen");
|
args.push_back(m_ui.fullscreenModeDropdown->currentIndex() ? "-nofullscreen" : "-fullscreen");
|
||||||
|
|
||||||
if (m_ui.bigPictureModeToggle->isChecked())
|
if (m_ui.bigPictureModeToggle->isChecked())
|
||||||
args.push_back("-bigpicture");
|
args.push_back("-bigpicture");
|
||||||
|
|
||||||
m_desktop = m_ui.shortcutDesktop->isChecked();
|
|
||||||
|
|
||||||
std::string custom_args = m_ui.customArgsInput->text().toStdString();
|
std::string custom_args = m_ui.customArgsInput->text().toStdString();
|
||||||
|
|
||||||
ShortcutCreationDialog::CreateShortcut(title.toStdString(), path.toStdString(), args, custom_args, m_desktop);
|
ShortcutCreationDialog::CreateShortcut(title.toStdString(), path.toStdString(), args, custom_args, m_ui.shortcutDesktop->isChecked());
|
||||||
|
|
||||||
accept();
|
accept();
|
||||||
});
|
});
|
||||||
connect(m_ui.dialogButtons, &QDialogButtonBox::rejected, this, &QDialog::reject);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ShortcutCreationDialog::CreateShortcut(const std::string name, const std::string game_path, std::vector<std::string> passed_cli_args, std::string custom_args, bool is_desktop)
|
void ShortcutCreationDialog::CreateShortcut(const std::string name, const std::string game_path, std::vector<std::string> passed_cli_args, std::string custom_args, bool is_desktop)
|
||||||
@ -183,7 +177,10 @@ void ShortcutCreationDialog::CreateShortcut(const std::string name, const std::s
|
|||||||
lossless &= ShortcutCreationDialog::EscapeShortcutCommandLine(&arg);
|
lossless &= ShortcutCreationDialog::EscapeShortcutCommandLine(&arg);
|
||||||
|
|
||||||
if (!lossless)
|
if (!lossless)
|
||||||
QMessageBox::warning(this, tr("Failed to create shortcut"), tr("File path contains invalid character(s). The resulting shortcut may not work."), QMessageBox::StandardButton::Ok, QMessageBox::StandardButton::Ok);
|
{
|
||||||
|
QMessageBox::warning(this, tr("Failed to create shortcut"), tr("File path contains invalid character(s)."), QMessageBox::StandardButton::Ok, QMessageBox::StandardButton::Ok);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
ShortcutCreationDialog::EscapeShortcutCommandLine(&clean_path);
|
ShortcutCreationDialog::EscapeShortcutCommandLine(&clean_path);
|
||||||
std::string combined_args = StringUtil::JoinString(passed_cli_args.begin(), passed_cli_args.end(), " ");
|
std::string combined_args = StringUtil::JoinString(passed_cli_args.begin(), passed_cli_args.end(), " ");
|
||||||
@ -313,9 +310,6 @@ void ShortcutCreationDialog::CreateShortcut(const std::string name, const std::s
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (is_flatpak) // Flatpak
|
|
||||||
executable_path = "flatpak run net.pcsx2.PCSX2";
|
|
||||||
|
|
||||||
// Find home directory
|
// Find home directory
|
||||||
std::string link_path;
|
std::string link_path;
|
||||||
const char* home = std::getenv("HOME");
|
const char* home = std::getenv("HOME");
|
||||||
@ -340,15 +334,30 @@ void ShortcutCreationDialog::CreateShortcut(const std::string name, const std::s
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
QMessageBox::critical(this, tr("Failed to create shortcut"), tr("Home path is empty."), QMessageBox::StandardButton::Ok, QMessageBox::StandardButton::Ok);
|
QMessageBox::critical(this, tr("Failed to create shortcut"), tr("Path to the Home directory is empty."), QMessageBox::StandardButton::Ok, QMessageBox::StandardButton::Ok);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Checks if a shortcut already exist
|
// Copy PCSX2 icon
|
||||||
if (FileSystem::FileExists(link_path.c_str()))
|
std::string icon_dest;
|
||||||
|
if (xdg_data_home)
|
||||||
|
icon_dest = fmt::format("{}/icons/hicolor/512x512/apps/", xdg_data_home);
|
||||||
|
else
|
||||||
|
icon_dest = fmt::format("{}/.local/share/icons/hicolor/512x512/apps/", home);
|
||||||
|
|
||||||
|
std::string icon_name;
|
||||||
|
if (is_flatpak) // Flatpak
|
||||||
{
|
{
|
||||||
QMessageBox::critical(this, tr("Failed to create shortcut"), tr("A shortcut with the same name already exists."), QMessageBox::StandardButton::Ok, QMessageBox::StandardButton::Ok);
|
executable_path = "flatpak run net.pcsx2.PCSX2";
|
||||||
return;
|
icon_name = "net.pcsx2.PCSX2";
|
||||||
|
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
icon_name = "PCSX2";
|
||||||
|
std::string icon_path = fmt::format("{}/{}.png", icon_dest, icon_name).c_str();
|
||||||
|
if (FileSystem::EnsureDirectoryExists(icon_dest.c_str(), true))
|
||||||
|
FileSystem::CopyFilePath(Path::Combine(EmuFolders::Resources, "icons/AppIconLarge.png").c_str(), icon_path.c_str(), false);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Shortcut CmdLine Args
|
// Shortcut CmdLine Args
|
||||||
@ -357,25 +366,13 @@ void ShortcutCreationDialog::CreateShortcut(const std::string name, const std::s
|
|||||||
lossless &= ShortcutCreationDialog::EscapeShortcutCommandLine(&arg);
|
lossless &= ShortcutCreationDialog::EscapeShortcutCommandLine(&arg);
|
||||||
|
|
||||||
if (!lossless)
|
if (!lossless)
|
||||||
QMessageBox::warning(this, tr("Failed to create shortcut"), tr("File path contains invalid character(s). The resulting shortcut may not work."), QMessageBox::StandardButton::Ok, QMessageBox::StandardButton::Ok);
|
{
|
||||||
|
QMessageBox::warning(this, tr("Failed to create shortcut"), tr("File path contains invalid character(s)."), QMessageBox::StandardButton::Ok, QMessageBox::StandardButton::Ok);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
std::string cmdline = StringUtil::JoinString(passed_cli_args.begin(), passed_cli_args.end(), " ");
|
std::string cmdline = StringUtil::JoinString(passed_cli_args.begin(), passed_cli_args.end(), " ");
|
||||||
|
|
||||||
if (!is_flatpak)
|
|
||||||
{
|
|
||||||
// Copy PCSX2 icon
|
|
||||||
std::string icon_dest;
|
|
||||||
if (xdg_data_home)
|
|
||||||
icon_dest = fmt::format("{}/icons/hicolor/512x512/apps/", xdg_data_home);
|
|
||||||
else
|
|
||||||
icon_dest = fmt::format("{}/.local/share/icons/hicolor/512x512/apps/", home);
|
|
||||||
|
|
||||||
std::string icon_name = "PCSX2.png";
|
|
||||||
std::string icon_path = fmt::format("{}/{}", icon_dest, icon_name).c_str();
|
|
||||||
if (FileSystem::EnsureDirectoryExists(icon_dest.c_str(), true))
|
|
||||||
FileSystem::CopyFilePath(Path::Combine(EmuFolders::Resources, "icons/AppIconLarge.png").c_str(), icon_path.c_str(), false);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Further string sanitization
|
// Further string sanitization
|
||||||
if (!is_flatpak)
|
if (!is_flatpak)
|
||||||
ShortcutCreationDialog::EscapeShortcutCommandLine(&executable_path);
|
ShortcutCreationDialog::EscapeShortcutCommandLine(&executable_path);
|
||||||
@ -393,7 +390,7 @@ void ShortcutCreationDialog::CreateShortcut(const std::string name, const std::s
|
|||||||
"StartupWMClass=PCSX2\n"
|
"StartupWMClass=PCSX2\n"
|
||||||
"Exec=" + final_args + "\n"
|
"Exec=" + final_args + "\n"
|
||||||
"Name=" + clean_name + "\n"
|
"Name=" + clean_name + "\n"
|
||||||
"Icon=net.pcsx2.PCSX2\n"
|
"Icon=" + icon_name + "\n"
|
||||||
"Categories=Game;Emulator;\n";
|
"Categories=Game;Emulator;\n";
|
||||||
std::string_view sv(file_content);
|
std::string_view sv(file_content);
|
||||||
|
|
||||||
|
|||||||
@ -22,8 +22,9 @@ public:
|
|||||||
bool EscapeShortcutCommandLine(std::string* cmdline);
|
bool EscapeShortcutCommandLine(std::string* cmdline);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
QString m_title;
|
const QString m_title;
|
||||||
QString m_path;
|
const QString m_path;
|
||||||
bool m_desktop;
|
|
||||||
|
private:
|
||||||
Ui::ShortcutCreationDialog m_ui;
|
Ui::ShortcutCreationDialog m_ui;
|
||||||
};
|
};
|
||||||
|
|||||||
@ -10,7 +10,7 @@
|
|||||||
<x>0</x>
|
<x>0</x>
|
||||||
<y>0</y>
|
<y>0</y>
|
||||||
<width>500</width>
|
<width>500</width>
|
||||||
<height>685</height>
|
<height>700</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
<layout class="QGridLayout" name="shortcutLayout">
|
<layout class="QGridLayout" name="shortcutLayout">
|
||||||
@ -59,6 +59,9 @@
|
|||||||
</item>
|
</item>
|
||||||
<item row="0" column="1">
|
<item row="0" column="1">
|
||||||
<widget class="QComboBox" name="fullscreenModeDropdown">
|
<widget class="QComboBox" name="fullscreenModeDropdown">
|
||||||
|
<property name="enabled">
|
||||||
|
<bool>false</bool>
|
||||||
|
</property>
|
||||||
<item>
|
<item>
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Force Enable</string>
|
<string>Force Enable</string>
|
||||||
@ -82,10 +85,17 @@
|
|||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="3" column="2" colspan="2">
|
<item row="3" column="2" colspan="2">
|
||||||
<widget class="QLineEdit" name="gameArgs"/>
|
<widget class="QLineEdit" name="gameArgs">
|
||||||
|
<property name="enabled">
|
||||||
|
<bool>false</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="4" column="2" colspan="2">
|
<item row="4" column="2" colspan="2">
|
||||||
<widget class="QComboBox" name="bootOptionDropdown">
|
<widget class="QComboBox" name="bootOptionDropdown">
|
||||||
|
<property name="enabled">
|
||||||
|
<bool>false</bool>
|
||||||
|
</property>
|
||||||
<item>
|
<item>
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Fast Boot</string>
|
<string>Fast Boot</string>
|
||||||
@ -107,13 +117,20 @@
|
|||||||
</item>
|
</item>
|
||||||
<item row="2" column="3">
|
<item row="2" column="3">
|
||||||
<widget class="QPushButton" name="overrideBootELFButton">
|
<widget class="QPushButton" name="overrideBootELFButton">
|
||||||
|
<property name="enabled">
|
||||||
|
<bool>false</bool>
|
||||||
|
</property>
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Browse...</string>
|
<string>Browse...</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="2" column="2">
|
<item row="2" column="2">
|
||||||
<widget class="QLineEdit" name="overrideBootELFPath"/>
|
<widget class="QLineEdit" name="overrideBootELFPath">
|
||||||
|
<property name="enabled">
|
||||||
|
<bool>false</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="10" column="0" colspan="4">
|
<item row="10" column="0" colspan="4">
|
||||||
<widget class="QGroupBox" name="saveStateGroup">
|
<widget class="QGroupBox" name="saveStateGroup">
|
||||||
@ -123,6 +140,9 @@
|
|||||||
<layout class="QGridLayout" name="savestateGridLayout">
|
<layout class="QGridLayout" name="savestateGridLayout">
|
||||||
<item row="1" column="1">
|
<item row="1" column="1">
|
||||||
<widget class="QSpinBox" name="loadStateIndex">
|
<widget class="QSpinBox" name="loadStateIndex">
|
||||||
|
<property name="enabled">
|
||||||
|
<bool>false</bool>
|
||||||
|
</property>
|
||||||
<property name="showGroupSeparator" stdset="0">
|
<property name="showGroupSeparator" stdset="0">
|
||||||
<bool>false</bool>
|
<bool>false</bool>
|
||||||
</property>
|
</property>
|
||||||
@ -132,7 +152,11 @@
|
|||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="2" column="1">
|
<item row="2" column="1">
|
||||||
<widget class="QLineEdit" name="loadStateFilePath"/>
|
<widget class="QLineEdit" name="loadStateFilePath">
|
||||||
|
<property name="enabled">
|
||||||
|
<bool>false</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="0" column="0">
|
<item row="0" column="0">
|
||||||
<widget class="QRadioButton" name="loadStateNone">
|
<widget class="QRadioButton" name="loadStateNone">
|
||||||
@ -160,6 +184,9 @@
|
|||||||
</item>
|
</item>
|
||||||
<item row="2" column="2">
|
<item row="2" column="2">
|
||||||
<widget class="QPushButton" name="loadStateFileBrowse">
|
<widget class="QPushButton" name="loadStateFileBrowse">
|
||||||
|
<property name="enabled">
|
||||||
|
<bool>false</bool>
|
||||||
|
</property>
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Browse...</string>
|
<string>Browse...</string>
|
||||||
</property>
|
</property>
|
||||||
@ -194,7 +221,7 @@
|
|||||||
<item row="0" column="0">
|
<item row="0" column="0">
|
||||||
<widget class="QLabel" name="customArgsInstruction">
|
<widget class="QLabel" name="customArgsInstruction">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>You may add additional (space-separated) <a href="https://pcsx2.net/docs/post/cli/">custom arguments</a> that are not listed above here:</string>
|
<string>You may add additional (space-separated) <a href="https://pcsx2.net/docs/advanced/cli/">custom arguments</a> that are not listed above here:</string>
|
||||||
</property>
|
</property>
|
||||||
<property name="textFormat">
|
<property name="textFormat">
|
||||||
<enum>Qt::TextFormat::RichText</enum>
|
<enum>Qt::TextFormat::RichText</enum>
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@ -106,14 +106,15 @@ static void HotkeyLoadStateSlot(s32 slot)
|
|||||||
|
|
||||||
Error error;
|
Error error;
|
||||||
if (!VMManager::LoadStateFromSlot(slot, false, &error))
|
if (!VMManager::LoadStateFromSlot(slot, false, &error))
|
||||||
Host::AddIconOSDMessage("LoadStateFromSlot", ICON_FA_TRIANGLE_EXCLAMATION,
|
FullscreenUI::ReportStateLoadError(error.GetDescription(), slot, false);
|
||||||
error.GetDescription(), Host::OSD_INFO_DURATION);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
static void HotkeySaveStateSlot(s32 slot)
|
static void HotkeySaveStateSlot(s32 slot)
|
||||||
{
|
{
|
||||||
VMManager::SaveStateToSlot(slot);
|
VMManager::SaveStateToSlot(slot, true, [slot](const std::string& error) {
|
||||||
|
FullscreenUI::ReportStateSaveError(error, slot);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool CanPause()
|
static bool CanPause()
|
||||||
|
|||||||
@ -630,9 +630,9 @@ namespace FullscreenUI
|
|||||||
static std::unique_ptr<GameList::Entry> s_game_settings_entry;
|
static std::unique_ptr<GameList::Entry> s_game_settings_entry;
|
||||||
static std::vector<std::pair<std::string, bool>> s_game_list_directories_cache;
|
static std::vector<std::pair<std::string, bool>> s_game_list_directories_cache;
|
||||||
static std::vector<GSAdapterInfo> s_graphics_adapter_list_cache;
|
static std::vector<GSAdapterInfo> s_graphics_adapter_list_cache;
|
||||||
static Patch::PatchInfoList s_game_patch_list;
|
static std::vector<Patch::PatchInfo> s_game_patch_list;
|
||||||
static std::vector<std::string> s_enabled_game_patch_cache;
|
static std::vector<std::string> s_enabled_game_patch_cache;
|
||||||
static Patch::PatchInfoList s_game_cheats_list;
|
static std::vector<Patch::PatchInfo> s_game_cheats_list;
|
||||||
static std::vector<std::string> s_enabled_game_cheat_cache;
|
static std::vector<std::string> s_enabled_game_cheat_cache;
|
||||||
static u32 s_game_cheat_unlabelled_count = 0;
|
static u32 s_game_cheat_unlabelled_count = 0;
|
||||||
static std::vector<const HotkeyInfo*> s_hotkey_list_cache;
|
static std::vector<const HotkeyInfo*> s_hotkey_list_cache;
|
||||||
@ -670,7 +670,8 @@ namespace FullscreenUI
|
|||||||
static void DrawSaveStateSelector(bool is_loading);
|
static void DrawSaveStateSelector(bool is_loading);
|
||||||
static bool OpenLoadStateSelectorForGameResume(const GameList::Entry* entry);
|
static bool OpenLoadStateSelectorForGameResume(const GameList::Entry* entry);
|
||||||
static void DrawResumeStateSelector();
|
static void DrawResumeStateSelector();
|
||||||
static void DoLoadState(std::string path);
|
static void DoLoadState(std::string path, std::optional<s32> slot, bool backup);
|
||||||
|
static void DoSaveState(s32 slot);
|
||||||
|
|
||||||
static std::vector<SaveStateListEntry> s_save_state_selector_slots;
|
static std::vector<SaveStateListEntry> s_save_state_selector_slots;
|
||||||
static std::string s_save_state_selector_game_path;
|
static std::string s_save_state_selector_game_path;
|
||||||
@ -1220,7 +1221,7 @@ void FullscreenUI::Render()
|
|||||||
|
|
||||||
// see if background setting changed
|
// see if background setting changed
|
||||||
static std::string s_last_background_path;
|
static std::string s_last_background_path;
|
||||||
std::string current_path = Host::GetBaseStringSettingValue("UI", "GameListBackgroundPath");
|
std::string current_path = Host::GetBaseStringSettingValue("UI", "FSUIBackgroundPath");
|
||||||
if (s_last_background_path != current_path)
|
if (s_last_background_path != current_path)
|
||||||
{
|
{
|
||||||
s_last_background_path = current_path;
|
s_last_background_path = current_path;
|
||||||
@ -1239,7 +1240,8 @@ void FullscreenUI::Render()
|
|||||||
s_current_main_window == MainWindowType::Exit ||
|
s_current_main_window == MainWindowType::Exit ||
|
||||||
s_current_main_window == MainWindowType::GameList ||
|
s_current_main_window == MainWindowType::GameList ||
|
||||||
s_current_main_window == MainWindowType::GameListSettings ||
|
s_current_main_window == MainWindowType::GameListSettings ||
|
||||||
s_current_main_window == MainWindowType::Settings) && s_custom_background_enabled && s_custom_background_texture;
|
s_current_main_window == MainWindowType::Settings) &&
|
||||||
|
!VMManager::HasValidVM() && s_custom_background_enabled && s_custom_background_texture;
|
||||||
|
|
||||||
ImVec4 original_background_color;
|
ImVec4 original_background_color;
|
||||||
if (should_draw_background)
|
if (should_draw_background)
|
||||||
@ -1690,7 +1692,7 @@ bool FullscreenUI::ShouldDefaultToGameList()
|
|||||||
|
|
||||||
void FullscreenUI::LoadCustomBackground()
|
void FullscreenUI::LoadCustomBackground()
|
||||||
{
|
{
|
||||||
std::string path = Host::GetBaseStringSettingValue("UI", "GameListBackgroundPath");
|
std::string path = Host::GetBaseStringSettingValue("UI", "FSUIBackgroundPath");
|
||||||
|
|
||||||
if (path.empty())
|
if (path.empty())
|
||||||
{
|
{
|
||||||
@ -1758,19 +1760,25 @@ void FullscreenUI::DrawCustomBackground()
|
|||||||
const ImGuiIO& io = ImGui::GetIO();
|
const ImGuiIO& io = ImGui::GetIO();
|
||||||
const ImVec2 display_size = io.DisplaySize;
|
const ImVec2 display_size = io.DisplaySize;
|
||||||
|
|
||||||
const float opacity = Host::GetBaseFloatSettingValue("UI", "GameListBackgroundOpacity", 100.0f) / 100.0f;
|
const u8 alpha = static_cast<u8>(Host::GetBaseFloatSettingValue("UI", "FSUIBackgroundOpacity", 100.0f) * 2.55f);
|
||||||
const std::string mode = Host::GetBaseStringSettingValue("UI", "GameListBackgroundMode", "fit");
|
const std::string mode = Host::GetBaseStringSettingValue("UI", "FSUIBackgroundMode", "fit");
|
||||||
|
|
||||||
const float tex_width = static_cast<float>(s_custom_background_texture->GetWidth());
|
const float tex_width = static_cast<float>(s_custom_background_texture->GetWidth());
|
||||||
const float tex_height = static_cast<float>(s_custom_background_texture->GetHeight());
|
const float tex_height = static_cast<float>(s_custom_background_texture->GetHeight());
|
||||||
|
|
||||||
ImVec2 img_min, img_max;
|
// Override the UIBackgroundColor that windows use
|
||||||
|
// We need to make windows transparent so our background image shows through
|
||||||
|
const ImVec4 transparent_bg = ImVec4(UIBackgroundColor.x, UIBackgroundColor.y, UIBackgroundColor.z, 0.0f);
|
||||||
|
ImGuiFullscreen::UIBackgroundColor = transparent_bg;
|
||||||
|
|
||||||
|
ImDrawList* bg_draw_list = ImGui::GetBackgroundDrawList();
|
||||||
|
const ImU32 col = IM_COL32(255, 255, 255, alpha);
|
||||||
|
const ImTextureID tex_id = reinterpret_cast<ImTextureID>(s_custom_background_texture->GetNativeHandle());
|
||||||
|
|
||||||
if (mode == "stretch")
|
if (mode == "stretch")
|
||||||
{
|
{
|
||||||
// stretch to fill entire display (ignores aspect ratio)
|
// stretch to fill entire display (ignores aspect ratio)
|
||||||
img_min = ImVec2(0.0f, 0.0f);
|
bg_draw_list->AddImage(tex_id, ImVec2(0.0f, 0.0f), display_size, ImVec2(0.0f, 0.0f), ImVec2(1.0f, 1.0f), col);
|
||||||
img_max = display_size;
|
|
||||||
}
|
}
|
||||||
else if (mode == "fill")
|
else if (mode == "fill")
|
||||||
{
|
{
|
||||||
@ -1795,8 +1803,64 @@ void FullscreenUI::DrawCustomBackground()
|
|||||||
const float offset_x = (display_size.x - scaled_width) * 0.5f;
|
const float offset_x = (display_size.x - scaled_width) * 0.5f;
|
||||||
const float offset_y = (display_size.y - scaled_height) * 0.5f;
|
const float offset_y = (display_size.y - scaled_height) * 0.5f;
|
||||||
|
|
||||||
img_min = ImVec2(offset_x, offset_y);
|
bg_draw_list->AddImage(tex_id,
|
||||||
img_max = ImVec2(offset_x + scaled_width, offset_y + scaled_height);
|
ImVec2(offset_x, offset_y),
|
||||||
|
ImVec2(offset_x + scaled_width, offset_y + scaled_height),
|
||||||
|
ImVec2(0.0f, 0.0f), ImVec2(1.0f, 1.0f), col);
|
||||||
|
}
|
||||||
|
else if (mode == "center")
|
||||||
|
{
|
||||||
|
// Center image at original size
|
||||||
|
const float offset_x = (display_size.x - tex_width) * 0.5f;
|
||||||
|
const float offset_y = (display_size.y - tex_height) * 0.5f;
|
||||||
|
|
||||||
|
bg_draw_list->AddImage(tex_id,
|
||||||
|
ImVec2(offset_x, offset_y),
|
||||||
|
ImVec2(offset_x + tex_width, offset_y + tex_height),
|
||||||
|
ImVec2(0.0f, 0.0f), ImVec2(1.0f, 1.0f), col);
|
||||||
|
}
|
||||||
|
else if (mode == "tile")
|
||||||
|
{
|
||||||
|
// Tile image across entire display
|
||||||
|
// If the image is extremely small, this approach can generate millions of quads
|
||||||
|
// and overflow the backend stream buffer (e.g. Vulkan assertion in VKStreamBuffer).
|
||||||
|
// Since we cannot switch ImGui's sampler to wrap (yet), clamp the maximum number of quads
|
||||||
|
constexpr int MAX_TILE_QUADS = 16384;
|
||||||
|
|
||||||
|
float tile_width = tex_width;
|
||||||
|
float tile_height = tex_height;
|
||||||
|
int tiles_x = static_cast<int>(std::ceil(display_size.x / tile_width));
|
||||||
|
int tiles_y = static_cast<int>(std::ceil(display_size.y / tile_height));
|
||||||
|
|
||||||
|
const int total_tiles = tiles_x * tiles_y;
|
||||||
|
if (total_tiles > MAX_TILE_QUADS)
|
||||||
|
{
|
||||||
|
const float scale = std::sqrt(static_cast<float>(total_tiles) / static_cast<float>(MAX_TILE_QUADS));
|
||||||
|
tile_width *= scale;
|
||||||
|
tile_height *= scale;
|
||||||
|
tiles_x = static_cast<int>(std::ceil(display_size.x / tile_width));
|
||||||
|
tiles_y = static_cast<int>(std::ceil(display_size.y / tile_height));
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int y = 0; y < tiles_y; y++)
|
||||||
|
{
|
||||||
|
for (int x = 0; x < tiles_x; x++)
|
||||||
|
{
|
||||||
|
const float tile_x = static_cast<float>(x) * tile_width;
|
||||||
|
const float tile_y = static_cast<float>(y) * tile_height;
|
||||||
|
const float tile_max_x = std::min(tile_x + tile_width, display_size.x);
|
||||||
|
const float tile_max_y = std::min(tile_y + tile_height, display_size.y);
|
||||||
|
|
||||||
|
// get uvs for partial tiles at edges
|
||||||
|
const float uv_max_x = (tile_max_x - tile_x) / tile_width;
|
||||||
|
const float uv_max_y = (tile_max_y - tile_y) / tile_height;
|
||||||
|
|
||||||
|
bg_draw_list->AddImage(tex_id,
|
||||||
|
ImVec2(tile_x, tile_y),
|
||||||
|
ImVec2(tile_max_x, tile_max_y),
|
||||||
|
ImVec2(0.0f, 0.0f), ImVec2(uv_max_x, uv_max_y), col);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else // "fit" or default
|
else // "fit" or default
|
||||||
{
|
{
|
||||||
@ -1821,19 +1885,11 @@ void FullscreenUI::DrawCustomBackground()
|
|||||||
const float offset_x = (display_size.x - scaled_width) * 0.5f;
|
const float offset_x = (display_size.x - scaled_width) * 0.5f;
|
||||||
const float offset_y = (display_size.y - scaled_height) * 0.5f;
|
const float offset_y = (display_size.y - scaled_height) * 0.5f;
|
||||||
|
|
||||||
img_min = ImVec2(offset_x, offset_y);
|
bg_draw_list->AddImage(tex_id,
|
||||||
img_max = ImVec2(offset_x + scaled_width, offset_y + scaled_height);
|
ImVec2(offset_x, offset_y),
|
||||||
|
ImVec2(offset_x + scaled_width, offset_y + scaled_height),
|
||||||
|
ImVec2(0.0f, 0.0f), ImVec2(1.0f, 1.0f), col);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Override the UIBackgroundColor that windows use
|
|
||||||
// We need to make windows transparent so our background image shows through
|
|
||||||
const ImVec4 transparent_bg = ImVec4(UIBackgroundColor.x, UIBackgroundColor.y, UIBackgroundColor.z, 0.0f);
|
|
||||||
ImGuiFullscreen::UIBackgroundColor = transparent_bg;
|
|
||||||
|
|
||||||
ImDrawList* bg_draw_list = ImGui::GetBackgroundDrawList();
|
|
||||||
const ImU32 col = IM_COL32(255, 255, 255, static_cast<u8>(opacity * 255.0f));
|
|
||||||
bg_draw_list->AddImage(reinterpret_cast<ImTextureID>(s_custom_background_texture->GetNativeHandle()),
|
|
||||||
img_min, img_max, ImVec2(0.0f, 0.0f), ImVec2(1.0f, 1.0f), col);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////
|
||||||
@ -3581,7 +3637,7 @@ void FullscreenUI::PopulateGameListDirectoryCache(SettingsInterface* si)
|
|||||||
|
|
||||||
void FullscreenUI::PopulatePatchesAndCheatsList(const std::string_view serial, u32 crc)
|
void FullscreenUI::PopulatePatchesAndCheatsList(const std::string_view serial, u32 crc)
|
||||||
{
|
{
|
||||||
constexpr auto sort_patches = [](Patch::PatchInfoList& list) {
|
constexpr auto sort_patches = [](std::vector<Patch::PatchInfo>& list) {
|
||||||
std::sort(list.begin(), list.end(), [](const Patch::PatchInfo& lhs, const Patch::PatchInfo& rhs) { return lhs.name < rhs.name; });
|
std::sort(list.begin(), list.end(), [](const Patch::PatchInfo& lhs, const Patch::PatchInfo& rhs) { return lhs.name < rhs.name; });
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -3629,7 +3685,7 @@ void FullscreenUI::DrawSettingsWindow()
|
|||||||
ImVec2(io.DisplaySize.x, LayoutScale(LAYOUT_MENU_BUTTON_HEIGHT_NO_SUMMARY) +
|
ImVec2(io.DisplaySize.x, LayoutScale(LAYOUT_MENU_BUTTON_HEIGHT_NO_SUMMARY) +
|
||||||
(LayoutScale(LAYOUT_MENU_BUTTON_Y_PADDING) * 2.0f) + LayoutScale(2.0f));
|
(LayoutScale(LAYOUT_MENU_BUTTON_Y_PADDING) * 2.0f) + LayoutScale(2.0f));
|
||||||
|
|
||||||
const bool using_custom_bg = s_custom_background_enabled && s_custom_background_texture;
|
const bool using_custom_bg = !VMManager::HasValidVM() && s_custom_background_enabled && s_custom_background_texture;
|
||||||
const float header_bg_alpha = VMManager::HasValidVM() ? 0.90f : 1.0f;
|
const float header_bg_alpha = VMManager::HasValidVM() ? 0.90f : 1.0f;
|
||||||
const float content_bg_alpha = using_custom_bg ? 0.0f : (VMManager::HasValidVM() ? 0.90f : 1.0f);
|
const float content_bg_alpha = using_custom_bg ? 0.0f : (VMManager::HasValidVM() ? 0.90f : 1.0f);
|
||||||
SettingsInterface* bsi = GetEditingSettingsInterface();
|
SettingsInterface* bsi = GetEditingSettingsInterface();
|
||||||
@ -4052,21 +4108,19 @@ void FullscreenUI::DrawInterfaceSettingsPage()
|
|||||||
|
|
||||||
MenuHeading(FSUI_CSTR("Background"));
|
MenuHeading(FSUI_CSTR("Background"));
|
||||||
|
|
||||||
std::string background_path = bsi->GetStringValue("UI", "GameListBackgroundPath", "");
|
std::string background_path = bsi->GetStringValue("UI", "FSUIBackgroundPath", "");
|
||||||
const bool background_enabled = bsi->GetBoolValue("UI", "GameListBackgroundEnabled", false);
|
|
||||||
|
|
||||||
std::string background_display = FSUI_STR("None");
|
std::string background_display = FSUI_STR("None");
|
||||||
if (!background_path.empty() && background_enabled)
|
if (!background_path.empty())
|
||||||
{
|
{
|
||||||
background_display = Path::GetFileName(background_path);
|
background_display = Path::GetFileName(background_path);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (MenuButtonWithValue(FSUI_ICONSTR(ICON_FA_IMAGE, "Background Image"),
|
if (MenuButtonWithValue(FSUI_ICONSTR(ICON_FA_IMAGE, "Background Image"),
|
||||||
FSUI_CSTR("Select a custom background image to use in Big Picture Mode menus."),
|
FSUI_CSTR("Select a custom background image to use in Big Picture Mode menus.\n\nSupported formats: PNG, JPG, JPEG, BMP."),
|
||||||
background_display.c_str()))
|
background_display.c_str()))
|
||||||
{
|
{
|
||||||
OpenFileSelector(FSUI_ICONSTR(ICON_FA_IMAGE, "Select Background Image"), false,
|
OpenFileSelector(FSUI_ICONSTR(ICON_FA_IMAGE, "Select Background Image"), false, [](const std::string& path) {
|
||||||
[](const std::string& path) {
|
|
||||||
if (!path.empty())
|
if (!path.empty())
|
||||||
{
|
{
|
||||||
{
|
{
|
||||||
@ -4074,23 +4128,20 @@ void FullscreenUI::DrawInterfaceSettingsPage()
|
|||||||
SettingsInterface* bsi = GetEditingSettingsInterface(false);
|
SettingsInterface* bsi = GetEditingSettingsInterface(false);
|
||||||
|
|
||||||
std::string relative_path = Path::MakeRelative(path, EmuFolders::DataRoot);
|
std::string relative_path = Path::MakeRelative(path, EmuFolders::DataRoot);
|
||||||
bsi->SetStringValue("UI", "GameListBackgroundPath", relative_path.c_str());
|
bsi->SetStringValue("UI", "FSUIBackgroundPath", relative_path.c_str());
|
||||||
bsi->SetBoolValue("UI", "GameListBackgroundEnabled", true);
|
bsi->SetBoolValue("UI", "FSUIBackgroundEnabled", true);
|
||||||
SetSettingsChanged(bsi);
|
SetSettingsChanged(bsi);
|
||||||
}
|
}
|
||||||
|
|
||||||
LoadCustomBackground();
|
LoadCustomBackground();
|
||||||
}
|
}
|
||||||
CloseFileSelector();
|
CloseFileSelector(); }, GetImageFileFilters());
|
||||||
},
|
|
||||||
GetImageFileFilters());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (MenuButton(FSUI_ICONSTR(ICON_FA_XMARK, "Clear Background Image"),
|
if (MenuButton(FSUI_ICONSTR(ICON_FA_XMARK, "Clear Background Image"),
|
||||||
FSUI_CSTR("Removes the custom background image.")))
|
FSUI_CSTR("Removes the custom background image.")))
|
||||||
{
|
{
|
||||||
bsi->DeleteValue("UI", "GameListBackgroundPath");
|
bsi->DeleteValue("UI", "FSUIBackgroundPath");
|
||||||
bsi->SetBoolValue("UI", "GameListBackgroundEnabled", false);
|
|
||||||
SetSettingsChanged(bsi);
|
SetSettingsChanged(bsi);
|
||||||
|
|
||||||
s_custom_background_texture.reset();
|
s_custom_background_texture.reset();
|
||||||
@ -4100,21 +4151,25 @@ void FullscreenUI::DrawInterfaceSettingsPage()
|
|||||||
|
|
||||||
DrawIntRangeSetting(bsi, FSUI_ICONSTR(ICON_FA_DROPLET, "Background Opacity"),
|
DrawIntRangeSetting(bsi, FSUI_ICONSTR(ICON_FA_DROPLET, "Background Opacity"),
|
||||||
FSUI_CSTR("Sets the transparency of the custom background image."),
|
FSUI_CSTR("Sets the transparency of the custom background image."),
|
||||||
"UI", "GameListBackgroundOpacity", 100, 0, 100, "%d%%");
|
"UI", "FSUIBackgroundOpacity", 100, 0, 100, "%d%%");
|
||||||
|
|
||||||
static constexpr const char* s_background_mode_names[] = {
|
static constexpr const char* s_background_mode_names[] = {
|
||||||
FSUI_NSTR("Fit"),
|
FSUI_NSTR("Fit"),
|
||||||
FSUI_NSTR("Fill"),
|
FSUI_NSTR("Fill"),
|
||||||
FSUI_NSTR("Stretch"),
|
FSUI_NSTR("Stretch"),
|
||||||
|
FSUI_NSTR("Center"),
|
||||||
|
FSUI_NSTR("Tile"),
|
||||||
};
|
};
|
||||||
static constexpr const char* s_background_mode_values[] = {
|
static constexpr const char* s_background_mode_values[] = {
|
||||||
"fit",
|
"fit",
|
||||||
"fill",
|
"fill",
|
||||||
"stretch",
|
"stretch",
|
||||||
|
"center",
|
||||||
|
"tile",
|
||||||
};
|
};
|
||||||
DrawStringListSetting(bsi, FSUI_ICONSTR(ICON_FA_EXPAND, "Background Mode"),
|
DrawStringListSetting(bsi, FSUI_ICONSTR(ICON_FA_EXPAND, "Background Mode"),
|
||||||
FSUI_CSTR("Select how to display the background image."),
|
FSUI_CSTR("Select how to display the background image."),
|
||||||
"UI", "GameListBackgroundMode", "fit", s_background_mode_names, s_background_mode_values, std::size(s_background_mode_names), true);
|
"UI", "FSUIBackgroundMode", "fit", s_background_mode_names, s_background_mode_values, std::size(s_background_mode_names), true);
|
||||||
|
|
||||||
MenuHeading(FSUI_CSTR("Behaviour"));
|
MenuHeading(FSUI_CSTR("Behaviour"));
|
||||||
DrawToggleSetting(bsi, FSUI_ICONSTR(ICON_PF_SNOOZE, "Inhibit Screensaver"),
|
DrawToggleSetting(bsi, FSUI_ICONSTR(ICON_PF_SNOOZE, "Inhibit Screensaver"),
|
||||||
@ -4129,6 +4184,8 @@ void FullscreenUI::DrawInterfaceSettingsPage()
|
|||||||
FSUI_CSTR("Pauses the emulator when a controller with bindings is disconnected."), "UI", "PauseOnControllerDisconnection", false);
|
FSUI_CSTR("Pauses the emulator when a controller with bindings is disconnected."), "UI", "PauseOnControllerDisconnection", false);
|
||||||
DrawToggleSetting(bsi, FSUI_ICONSTR(ICON_FA_RECTANGLE_LIST, "Pause On Menu"),
|
DrawToggleSetting(bsi, FSUI_ICONSTR(ICON_FA_RECTANGLE_LIST, "Pause On Menu"),
|
||||||
FSUI_CSTR("Pauses the emulator when you open the quick menu, and unpauses when you close it."), "UI", "PauseOnMenu", true);
|
FSUI_CSTR("Pauses the emulator when you open the quick menu, and unpauses when you close it."), "UI", "PauseOnMenu", true);
|
||||||
|
DrawToggleSetting(bsi, FSUI_ICONSTR(ICON_FA_FLOPPY_DISK, "Prompt On State Load/Save Failure"),
|
||||||
|
FSUI_CSTR("Display a modal dialog when a save state load/save operation fails."), "UI", "PromptOnStateLoadSaveFailure", true);
|
||||||
DrawToggleSetting(bsi, FSUI_ICONSTR(ICON_FA_POWER_OFF, "Confirm Shutdown"),
|
DrawToggleSetting(bsi, FSUI_ICONSTR(ICON_FA_POWER_OFF, "Confirm Shutdown"),
|
||||||
FSUI_CSTR("Determines whether a prompt will be displayed to confirm shutting down the emulator/game when the hotkey is pressed."),
|
FSUI_CSTR("Determines whether a prompt will be displayed to confirm shutting down the emulator/game when the hotkey is pressed."),
|
||||||
"UI", "ConfirmShutdown", true);
|
"UI", "ConfirmShutdown", true);
|
||||||
@ -4230,6 +4287,8 @@ void FullscreenUI::DrawInterfaceSettingsPage()
|
|||||||
true);
|
true);
|
||||||
DrawToggleSetting(bsi, FSUI_ICONSTR(ICON_FA_ARROW_POINTER, "Hide Cursor In Fullscreen"),
|
DrawToggleSetting(bsi, FSUI_ICONSTR(ICON_FA_ARROW_POINTER, "Hide Cursor In Fullscreen"),
|
||||||
FSUI_CSTR("Hides the mouse pointer/cursor when the emulator is in fullscreen mode."), "UI", "HideMouseCursor", false);
|
FSUI_CSTR("Hides the mouse pointer/cursor when the emulator is in fullscreen mode."), "UI", "HideMouseCursor", false);
|
||||||
|
DrawToggleSetting(bsi, FSUI_ICONSTR(ICON_FA_TABLET_SCREEN_BUTTON, "Start Big Picture UI"),
|
||||||
|
FSUI_CSTR("Automatically starts Big Picture Mode instead of the regular Qt interface when PCSX2 launches."), "UI", "StartBigPictureMode", false);
|
||||||
|
|
||||||
MenuHeading(FSUI_CSTR("On-Screen Display"));
|
MenuHeading(FSUI_CSTR("On-Screen Display"));
|
||||||
DrawIntSpinBoxSetting(bsi, FSUI_ICONSTR(ICON_FA_MAGNIFYING_GLASS, "OSD Scale"),
|
DrawIntSpinBoxSetting(bsi, FSUI_ICONSTR(ICON_FA_MAGNIFYING_GLASS, "OSD Scale"),
|
||||||
@ -6631,7 +6690,7 @@ void FullscreenUI::DrawPatchesOrCheatsSettingsPage(bool cheats)
|
|||||||
{
|
{
|
||||||
SettingsInterface* bsi = GetEditingSettingsInterface();
|
SettingsInterface* bsi = GetEditingSettingsInterface();
|
||||||
|
|
||||||
const Patch::PatchInfoList& patch_list = cheats ? s_game_cheats_list : s_game_patch_list;
|
const std::vector<Patch::PatchInfo>& patch_list = cheats ? s_game_cheats_list : s_game_patch_list;
|
||||||
std::vector<std::string>& enable_list = cheats ? s_enabled_game_cheat_cache : s_enabled_game_patch_cache;
|
std::vector<std::string>& enable_list = cheats ? s_enabled_game_cheat_cache : s_enabled_game_patch_cache;
|
||||||
const char* section = cheats ? Patch::CHEATS_CONFIG_SECTION : Patch::PATCHES_CONFIG_SECTION;
|
const char* section = cheats ? Patch::CHEATS_CONFIG_SECTION : Patch::PATCHES_CONFIG_SECTION;
|
||||||
const bool master_enable = cheats ? GetEffectiveBoolSetting(bsi, "EmuCore", "EnableCheats", false) : true;
|
const bool master_enable = cheats ? GetEffectiveBoolSetting(bsi, "EmuCore", "EnableCheats", false) : true;
|
||||||
@ -7343,9 +7402,9 @@ void FullscreenUI::DrawSaveStateSelector(bool is_loading)
|
|||||||
false, is_loading ? !Achievements::IsHardcoreModeActive() : true, LAYOUT_MENU_BUTTON_HEIGHT_NO_SUMMARY))
|
false, is_loading ? !Achievements::IsHardcoreModeActive() : true, LAYOUT_MENU_BUTTON_HEIGHT_NO_SUMMARY))
|
||||||
{
|
{
|
||||||
if (is_loading)
|
if (is_loading)
|
||||||
DoLoadState(std::move(entry.path));
|
DoLoadState(std::move(entry.path), entry.slot, false);
|
||||||
else
|
else
|
||||||
Host::RunOnCPUThread([slot = entry.slot]() { VMManager::SaveStateToSlot(slot); });
|
DoSaveState(entry.slot);
|
||||||
|
|
||||||
CloseSaveStateSelector();
|
CloseSaveStateSelector();
|
||||||
ReturnToMainWindow();
|
ReturnToMainWindow();
|
||||||
@ -7481,9 +7540,9 @@ void FullscreenUI::DrawSaveStateSelector(bool is_loading)
|
|||||||
if (pressed)
|
if (pressed)
|
||||||
{
|
{
|
||||||
if (is_loading)
|
if (is_loading)
|
||||||
DoLoadState(entry.path);
|
DoLoadState(entry.path, entry.slot, false);
|
||||||
else
|
else
|
||||||
Host::RunOnCPUThread([slot = entry.slot]() { VMManager::SaveStateToSlot(slot); });
|
DoSaveState(entry.slot);
|
||||||
|
|
||||||
CloseSaveStateSelector();
|
CloseSaveStateSelector();
|
||||||
ReturnToMainWindow();
|
ReturnToMainWindow();
|
||||||
@ -7637,19 +7696,16 @@ void FullscreenUI::DrawResumeStateSelector()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void FullscreenUI::DoLoadState(std::string path)
|
void FullscreenUI::DoLoadState(std::string path, std::optional<s32> slot, bool backup)
|
||||||
{
|
{
|
||||||
Host::RunOnCPUThread([boot_path = s_save_state_selector_game_path, path = std::move(path)]() {
|
std::string boot_path = s_save_state_selector_game_path;
|
||||||
|
Host::RunOnCPUThread([boot_path = std::move(boot_path), path = std::move(path), slot, backup]() {
|
||||||
if (VMManager::HasValidVM())
|
if (VMManager::HasValidVM())
|
||||||
{
|
{
|
||||||
Error error;
|
Error error;
|
||||||
if (!VMManager::LoadState(path.c_str(), &error))
|
if (!VMManager::LoadState(path.c_str(), &error))
|
||||||
{
|
{
|
||||||
MTGS::RunOnGSThread([error = std::move(error)]() {
|
ReportStateLoadError(error.GetDescription(), slot, backup);
|
||||||
ImGuiFullscreen::OpenInfoMessageDialog(
|
|
||||||
FSUI_ICONSTR(ICON_FA_TRIANGLE_EXCLAMATION, "Failed to Load State"),
|
|
||||||
error.GetDescription());
|
|
||||||
});
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -7666,6 +7722,15 @@ void FullscreenUI::DoLoadState(std::string path)
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void FullscreenUI::DoSaveState(s32 slot)
|
||||||
|
{
|
||||||
|
Host::RunOnCPUThread([slot]() {
|
||||||
|
VMManager::SaveStateToSlot(slot, true, [slot](const std::string& error) {
|
||||||
|
ReportStateSaveError(error, slot);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
void FullscreenUI::PopulateGameListEntryList()
|
void FullscreenUI::PopulateGameListEntryList()
|
||||||
{
|
{
|
||||||
const int sort = Host::GetBaseIntSettingValue("UI", "FullscreenUIGameSort", 0);
|
const int sort = Host::GetBaseIntSettingValue("UI", "FullscreenUIGameSort", 0);
|
||||||
@ -9167,6 +9232,75 @@ void FullscreenUI::DrawAchievementsSettingsPage(std::unique_lock<std::mutex>& se
|
|||||||
EndMenuButtons();
|
EndMenuButtons();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void FullscreenUI::ReportStateLoadError(const std::string& message, std::optional<s32> slot, bool backup)
|
||||||
|
{
|
||||||
|
MTGS::RunOnGSThread([message, slot, backup]() {
|
||||||
|
const bool prompt_on_error = Host::GetBaseBoolSettingValue("UI", "PromptOnStateLoadSaveFailure", true);
|
||||||
|
if (!prompt_on_error || !ImGuiManager::InitializeFullscreenUI())
|
||||||
|
{
|
||||||
|
SaveState_ReportLoadErrorOSD(message, slot, backup);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string title;
|
||||||
|
if (slot.has_value())
|
||||||
|
{
|
||||||
|
if (backup)
|
||||||
|
title = fmt::format(FSUI_FSTR("Failed to Load State From Backup Slot {}"), *slot);
|
||||||
|
else
|
||||||
|
title = fmt::format(FSUI_FSTR("Failed to Load State From Slot {}"), *slot);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
title = FSUI_STR("Failed to Load State");
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGuiFullscreen::InfoMessageDialogCallback callback;
|
||||||
|
if (VMManager::GetState() == VMState::Running)
|
||||||
|
{
|
||||||
|
Host::RunOnCPUThread([]() { VMManager::SetPaused(true); });
|
||||||
|
callback = []() {
|
||||||
|
Host::RunOnCPUThread([]() { VMManager::SetPaused(false); });
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGuiFullscreen::OpenInfoMessageDialog(
|
||||||
|
fmt::format("{} {}", ICON_FA_TRIANGLE_EXCLAMATION, title),
|
||||||
|
std::move(message), std::move(callback));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void FullscreenUI::ReportStateSaveError(const std::string& message, std::optional<s32> slot)
|
||||||
|
{
|
||||||
|
MTGS::RunOnGSThread([message, slot]() {
|
||||||
|
const bool prompt_on_error = Host::GetBaseBoolSettingValue("UI", "PromptOnStateLoadSaveFailure", true);
|
||||||
|
if (!prompt_on_error || !ImGuiManager::InitializeFullscreenUI())
|
||||||
|
{
|
||||||
|
SaveState_ReportSaveErrorOSD(message, slot);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string title;
|
||||||
|
if (slot.has_value())
|
||||||
|
title = fmt::format(FSUI_FSTR("Failed to Save State To Slot {}"), *slot);
|
||||||
|
else
|
||||||
|
title = FSUI_STR("Failed to Save State");
|
||||||
|
|
||||||
|
ImGuiFullscreen::InfoMessageDialogCallback callback;
|
||||||
|
if (VMManager::GetState() == VMState::Running)
|
||||||
|
{
|
||||||
|
Host::RunOnCPUThread([]() { VMManager::SetPaused(true); });
|
||||||
|
callback = []() {
|
||||||
|
Host::RunOnCPUThread([]() { VMManager::SetPaused(false); });
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGuiFullscreen::OpenInfoMessageDialog(
|
||||||
|
fmt::format("{} {}", ICON_FA_TRIANGLE_EXCLAMATION, title),
|
||||||
|
std::move(message), std::move(callback));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
// Translation String Area
|
// Translation String Area
|
||||||
// To avoid having to type T_RANSLATE("FullscreenUI", ...) everywhere, we use the shorter macros at the top
|
// To avoid having to type T_RANSLATE("FullscreenUI", ...) everywhere, we use the shorter macros at the top
|
||||||
@ -9226,6 +9360,8 @@ TRANSLATE_NOOP("FullscreenUI", "Reset System");
|
|||||||
TRANSLATE_NOOP("FullscreenUI", "Hardcore mode will not be enabled until the system is reset. Do you want to reset the system now?");
|
TRANSLATE_NOOP("FullscreenUI", "Hardcore mode will not be enabled until the system is reset. Do you want to reset the system now?");
|
||||||
TRANSLATE_NOOP("FullscreenUI", "This game has no achievements.");
|
TRANSLATE_NOOP("FullscreenUI", "This game has no achievements.");
|
||||||
TRANSLATE_NOOP("FullscreenUI", "This game has no leaderboards.");
|
TRANSLATE_NOOP("FullscreenUI", "This game has no leaderboards.");
|
||||||
|
TRANSLATE_NOOP("FullscreenUI", "Failed to Load State");
|
||||||
|
TRANSLATE_NOOP("FullscreenUI", "Failed to Save State");
|
||||||
TRANSLATE_NOOP("FullscreenUI", "Game List");
|
TRANSLATE_NOOP("FullscreenUI", "Game List");
|
||||||
TRANSLATE_NOOP("FullscreenUI", "Launch a game from images scanned from your game directories.");
|
TRANSLATE_NOOP("FullscreenUI", "Launch a game from images scanned from your game directories.");
|
||||||
TRANSLATE_NOOP("FullscreenUI", "Start Game");
|
TRANSLATE_NOOP("FullscreenUI", "Start Game");
|
||||||
@ -9271,7 +9407,7 @@ TRANSLATE_NOOP("FullscreenUI", "Selects the color style to be used for Big Pictu
|
|||||||
TRANSLATE_NOOP("FullscreenUI", "When Big Picture mode is started, the game list will be displayed instead of the main menu.");
|
TRANSLATE_NOOP("FullscreenUI", "When Big Picture mode is started, the game list will be displayed instead of the main menu.");
|
||||||
TRANSLATE_NOOP("FullscreenUI", "Show a save state selector UI when switching slots instead of showing a notification bubble.");
|
TRANSLATE_NOOP("FullscreenUI", "Show a save state selector UI when switching slots instead of showing a notification bubble.");
|
||||||
TRANSLATE_NOOP("FullscreenUI", "Background");
|
TRANSLATE_NOOP("FullscreenUI", "Background");
|
||||||
TRANSLATE_NOOP("FullscreenUI", "Select a custom background image to use in Big Picture Mode menus.");
|
TRANSLATE_NOOP("FullscreenUI", "Select a custom background image to use in Big Picture Mode menus.\n\nSupported formats: PNG, JPG, JPEG, BMP.");
|
||||||
TRANSLATE_NOOP("FullscreenUI", "Removes the custom background image.");
|
TRANSLATE_NOOP("FullscreenUI", "Removes the custom background image.");
|
||||||
TRANSLATE_NOOP("FullscreenUI", "Sets the transparency of the custom background image.");
|
TRANSLATE_NOOP("FullscreenUI", "Sets the transparency of the custom background image.");
|
||||||
TRANSLATE_NOOP("FullscreenUI", "Select how to display the background image.");
|
TRANSLATE_NOOP("FullscreenUI", "Select how to display the background image.");
|
||||||
@ -9281,6 +9417,7 @@ TRANSLATE_NOOP("FullscreenUI", "Pauses the emulator when a game is started.");
|
|||||||
TRANSLATE_NOOP("FullscreenUI", "Pauses the emulator when you minimize the window or switch to another application, and unpauses when you switch back.");
|
TRANSLATE_NOOP("FullscreenUI", "Pauses the emulator when you minimize the window or switch to another application, and unpauses when you switch back.");
|
||||||
TRANSLATE_NOOP("FullscreenUI", "Pauses the emulator when a controller with bindings is disconnected.");
|
TRANSLATE_NOOP("FullscreenUI", "Pauses the emulator when a controller with bindings is disconnected.");
|
||||||
TRANSLATE_NOOP("FullscreenUI", "Pauses the emulator when you open the quick menu, and unpauses when you close it.");
|
TRANSLATE_NOOP("FullscreenUI", "Pauses the emulator when you open the quick menu, and unpauses when you close it.");
|
||||||
|
TRANSLATE_NOOP("FullscreenUI", "Display a modal dialog when a save state load/save operation fails.");
|
||||||
TRANSLATE_NOOP("FullscreenUI", "Determines whether a prompt will be displayed to confirm shutting down the emulator/game when the hotkey is pressed.");
|
TRANSLATE_NOOP("FullscreenUI", "Determines whether a prompt will be displayed to confirm shutting down the emulator/game when the hotkey is pressed.");
|
||||||
TRANSLATE_NOOP("FullscreenUI", "Automatically saves the emulator state when powering down or exiting. You can then resume directly from where you left off next time.");
|
TRANSLATE_NOOP("FullscreenUI", "Automatically saves the emulator state when powering down or exiting. You can then resume directly from where you left off next time.");
|
||||||
TRANSLATE_NOOP("FullscreenUI", "Creates a backup copy of a save state if it already exists when the save is created. The backup copy has a .backup suffix");
|
TRANSLATE_NOOP("FullscreenUI", "Creates a backup copy of a save state if it already exists when the save is created. The backup copy has a .backup suffix");
|
||||||
@ -9290,6 +9427,7 @@ TRANSLATE_NOOP("FullscreenUI", "Game Display");
|
|||||||
TRANSLATE_NOOP("FullscreenUI", "Automatically switches to fullscreen mode when a game is started.");
|
TRANSLATE_NOOP("FullscreenUI", "Automatically switches to fullscreen mode when a game is started.");
|
||||||
TRANSLATE_NOOP("FullscreenUI", "Switches between full screen and windowed when the window is double-clicked.");
|
TRANSLATE_NOOP("FullscreenUI", "Switches between full screen and windowed when the window is double-clicked.");
|
||||||
TRANSLATE_NOOP("FullscreenUI", "Hides the mouse pointer/cursor when the emulator is in fullscreen mode.");
|
TRANSLATE_NOOP("FullscreenUI", "Hides the mouse pointer/cursor when the emulator is in fullscreen mode.");
|
||||||
|
TRANSLATE_NOOP("FullscreenUI", "Automatically starts Big Picture Mode instead of the regular Qt interface when PCSX2 launches.");
|
||||||
TRANSLATE_NOOP("FullscreenUI", "On-Screen Display");
|
TRANSLATE_NOOP("FullscreenUI", "On-Screen Display");
|
||||||
TRANSLATE_NOOP("FullscreenUI", "Determines how large the on-screen messages and monitors are.");
|
TRANSLATE_NOOP("FullscreenUI", "Determines how large the on-screen messages and monitors are.");
|
||||||
TRANSLATE_NOOP("FullscreenUI", "%d%%");
|
TRANSLATE_NOOP("FullscreenUI", "%d%%");
|
||||||
@ -9715,6 +9853,9 @@ TRANSLATE_NOOP("FullscreenUI", "Last Played: {}");
|
|||||||
TRANSLATE_NOOP("FullscreenUI", "Size: {:.2f} MB");
|
TRANSLATE_NOOP("FullscreenUI", "Size: {:.2f} MB");
|
||||||
TRANSLATE_NOOP("FullscreenUI", "Are you sure you want to reset the play time for '{}' ({})?\n\nYour current play time is {}.\n\nThis action cannot be undone.");
|
TRANSLATE_NOOP("FullscreenUI", "Are you sure you want to reset the play time for '{}' ({})?\n\nYour current play time is {}.\n\nThis action cannot be undone.");
|
||||||
TRANSLATE_NOOP("FullscreenUI", "Login failed.\nError: {}\n\nPlease check your username and password, and try again.");
|
TRANSLATE_NOOP("FullscreenUI", "Login failed.\nError: {}\n\nPlease check your username and password, and try again.");
|
||||||
|
TRANSLATE_NOOP("FullscreenUI", "Failed to Load State From Backup Slot {}");
|
||||||
|
TRANSLATE_NOOP("FullscreenUI", "Failed to Load State From Slot {}");
|
||||||
|
TRANSLATE_NOOP("FullscreenUI", "Failed to Save State To Slot {}");
|
||||||
TRANSLATE_NOOP("FullscreenUI", "Left: ");
|
TRANSLATE_NOOP("FullscreenUI", "Left: ");
|
||||||
TRANSLATE_NOOP("FullscreenUI", "Top: ");
|
TRANSLATE_NOOP("FullscreenUI", "Top: ");
|
||||||
TRANSLATE_NOOP("FullscreenUI", "Right: ");
|
TRANSLATE_NOOP("FullscreenUI", "Right: ");
|
||||||
@ -9748,13 +9889,14 @@ TRANSLATE_NOOP("FullscreenUI", "AMOLED");
|
|||||||
TRANSLATE_NOOP("FullscreenUI", "Fit");
|
TRANSLATE_NOOP("FullscreenUI", "Fit");
|
||||||
TRANSLATE_NOOP("FullscreenUI", "Fill");
|
TRANSLATE_NOOP("FullscreenUI", "Fill");
|
||||||
TRANSLATE_NOOP("FullscreenUI", "Stretch");
|
TRANSLATE_NOOP("FullscreenUI", "Stretch");
|
||||||
|
TRANSLATE_NOOP("FullscreenUI", "Center");
|
||||||
|
TRANSLATE_NOOP("FullscreenUI", "Tile");
|
||||||
TRANSLATE_NOOP("FullscreenUI", "Enabled");
|
TRANSLATE_NOOP("FullscreenUI", "Enabled");
|
||||||
TRANSLATE_NOOP("FullscreenUI", "Disabled");
|
TRANSLATE_NOOP("FullscreenUI", "Disabled");
|
||||||
TRANSLATE_NOOP("FullscreenUI", "Top Left");
|
TRANSLATE_NOOP("FullscreenUI", "Top Left");
|
||||||
TRANSLATE_NOOP("FullscreenUI", "Top Center");
|
TRANSLATE_NOOP("FullscreenUI", "Top Center");
|
||||||
TRANSLATE_NOOP("FullscreenUI", "Top Right");
|
TRANSLATE_NOOP("FullscreenUI", "Top Right");
|
||||||
TRANSLATE_NOOP("FullscreenUI", "Center Left");
|
TRANSLATE_NOOP("FullscreenUI", "Center Left");
|
||||||
TRANSLATE_NOOP("FullscreenUI", "Center");
|
|
||||||
TRANSLATE_NOOP("FullscreenUI", "Center Right");
|
TRANSLATE_NOOP("FullscreenUI", "Center Right");
|
||||||
TRANSLATE_NOOP("FullscreenUI", "Bottom Left");
|
TRANSLATE_NOOP("FullscreenUI", "Bottom Left");
|
||||||
TRANSLATE_NOOP("FullscreenUI", "Bottom Center");
|
TRANSLATE_NOOP("FullscreenUI", "Bottom Center");
|
||||||
@ -9974,6 +10116,7 @@ TRANSLATE_NOOP("FullscreenUI", "Pause On Start");
|
|||||||
TRANSLATE_NOOP("FullscreenUI", "Pause On Focus Loss");
|
TRANSLATE_NOOP("FullscreenUI", "Pause On Focus Loss");
|
||||||
TRANSLATE_NOOP("FullscreenUI", "Pause On Controller Disconnection");
|
TRANSLATE_NOOP("FullscreenUI", "Pause On Controller Disconnection");
|
||||||
TRANSLATE_NOOP("FullscreenUI", "Pause On Menu");
|
TRANSLATE_NOOP("FullscreenUI", "Pause On Menu");
|
||||||
|
TRANSLATE_NOOP("FullscreenUI", "Prompt On State Load/Save Failure");
|
||||||
TRANSLATE_NOOP("FullscreenUI", "Confirm Shutdown");
|
TRANSLATE_NOOP("FullscreenUI", "Confirm Shutdown");
|
||||||
TRANSLATE_NOOP("FullscreenUI", "Save State On Shutdown");
|
TRANSLATE_NOOP("FullscreenUI", "Save State On Shutdown");
|
||||||
TRANSLATE_NOOP("FullscreenUI", "Create Save State Backups");
|
TRANSLATE_NOOP("FullscreenUI", "Create Save State Backups");
|
||||||
@ -9983,6 +10126,7 @@ TRANSLATE_NOOP("FullscreenUI", "Enable Discord Presence");
|
|||||||
TRANSLATE_NOOP("FullscreenUI", "Start Fullscreen");
|
TRANSLATE_NOOP("FullscreenUI", "Start Fullscreen");
|
||||||
TRANSLATE_NOOP("FullscreenUI", "Double-Click Toggles Fullscreen");
|
TRANSLATE_NOOP("FullscreenUI", "Double-Click Toggles Fullscreen");
|
||||||
TRANSLATE_NOOP("FullscreenUI", "Hide Cursor In Fullscreen");
|
TRANSLATE_NOOP("FullscreenUI", "Hide Cursor In Fullscreen");
|
||||||
|
TRANSLATE_NOOP("FullscreenUI", "Start Big Picture UI");
|
||||||
TRANSLATE_NOOP("FullscreenUI", "OSD Scale");
|
TRANSLATE_NOOP("FullscreenUI", "OSD Scale");
|
||||||
TRANSLATE_NOOP("FullscreenUI", "OSD Messages Position");
|
TRANSLATE_NOOP("FullscreenUI", "OSD Messages Position");
|
||||||
TRANSLATE_NOOP("FullscreenUI", "OSD Performance Position");
|
TRANSLATE_NOOP("FullscreenUI", "OSD Performance Position");
|
||||||
@ -10163,7 +10307,6 @@ TRANSLATE_NOOP("FullscreenUI", "Delete Save");
|
|||||||
TRANSLATE_NOOP("FullscreenUI", "Close Menu");
|
TRANSLATE_NOOP("FullscreenUI", "Close Menu");
|
||||||
TRANSLATE_NOOP("FullscreenUI", "Default Boot");
|
TRANSLATE_NOOP("FullscreenUI", "Default Boot");
|
||||||
TRANSLATE_NOOP("FullscreenUI", "Delete State");
|
TRANSLATE_NOOP("FullscreenUI", "Delete State");
|
||||||
TRANSLATE_NOOP("FullscreenUI", "Failed to Load State");
|
|
||||||
TRANSLATE_NOOP("FullscreenUI", "Full Boot");
|
TRANSLATE_NOOP("FullscreenUI", "Full Boot");
|
||||||
TRANSLATE_NOOP("FullscreenUI", "Reset Play Time");
|
TRANSLATE_NOOP("FullscreenUI", "Reset Play Time");
|
||||||
TRANSLATE_NOOP("FullscreenUI", "Confirm Reset");
|
TRANSLATE_NOOP("FullscreenUI", "Confirm Reset");
|
||||||
|
|||||||
@ -10,6 +10,7 @@
|
|||||||
#include <ctime>
|
#include <ctime>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
#include <optional>
|
||||||
|
|
||||||
struct Pcsx2Config;
|
struct Pcsx2Config;
|
||||||
|
|
||||||
@ -26,6 +27,8 @@ namespace FullscreenUI
|
|||||||
void OpenPauseMenu();
|
void OpenPauseMenu();
|
||||||
bool OpenAchievementsWindow();
|
bool OpenAchievementsWindow();
|
||||||
bool OpenLeaderboardsWindow();
|
bool OpenLeaderboardsWindow();
|
||||||
|
void ReportStateLoadError(const std::string& message, std::optional<s32> slot, bool backup);
|
||||||
|
void ReportStateSaveError(const std::string& message, std::optional<s32> slot);
|
||||||
|
|
||||||
// NOTE: Only call from GS thread.
|
// NOTE: Only call from GS thread.
|
||||||
bool IsAchievementsWindowOpen();
|
bool IsAchievementsWindowOpen();
|
||||||
|
|||||||
@ -2396,6 +2396,7 @@ void ImGuiFullscreen::DrawChoiceDialog()
|
|||||||
ImGui::PushStyleColor(ImGuiCol_Text, UIPrimaryTextColor);
|
ImGui::PushStyleColor(ImGuiCol_Text, UIPrimaryTextColor);
|
||||||
ImGui::PushStyleColor(ImGuiCol_TitleBg, UIPrimaryDarkColor);
|
ImGui::PushStyleColor(ImGuiCol_TitleBg, UIPrimaryDarkColor);
|
||||||
ImGui::PushStyleColor(ImGuiCol_TitleBgActive, UIPrimaryColor);
|
ImGui::PushStyleColor(ImGuiCol_TitleBgActive, UIPrimaryColor);
|
||||||
|
ImGui::PushStyleColor(ImGuiCol_PopupBg, UIPopupBackgroundColor);
|
||||||
|
|
||||||
const float width = LayoutScale(600.0f);
|
const float width = LayoutScale(600.0f);
|
||||||
const float title_height = g_large_font.second + ImGui::GetStyle().FramePadding.y * 2.0f + ImGui::GetStyle().WindowPadding.y * 2.0f;
|
const float title_height = g_large_font.second + ImGui::GetStyle().FramePadding.y * 2.0f + ImGui::GetStyle().WindowPadding.y * 2.0f;
|
||||||
@ -2464,7 +2465,7 @@ void ImGuiFullscreen::DrawChoiceDialog()
|
|||||||
is_open = false;
|
is_open = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
ImGui::PopStyleColor(3);
|
ImGui::PopStyleColor(4);
|
||||||
ImGui::PopStyleVar(3);
|
ImGui::PopStyleVar(3);
|
||||||
ImGui::PopFont();
|
ImGui::PopFont();
|
||||||
|
|
||||||
@ -2523,7 +2524,7 @@ void ImGuiFullscreen::DrawInputDialog()
|
|||||||
ImGui::PushStyleColor(ImGuiCol_Text, UIPrimaryTextColor);
|
ImGui::PushStyleColor(ImGuiCol_Text, UIPrimaryTextColor);
|
||||||
ImGui::PushStyleColor(ImGuiCol_TitleBg, UIPrimaryDarkColor);
|
ImGui::PushStyleColor(ImGuiCol_TitleBg, UIPrimaryDarkColor);
|
||||||
ImGui::PushStyleColor(ImGuiCol_TitleBgActive, UIPrimaryColor);
|
ImGui::PushStyleColor(ImGuiCol_TitleBgActive, UIPrimaryColor);
|
||||||
ImGui::PushStyleColor(ImGuiCol_PopupBg, UIBackgroundColor);
|
ImGui::PushStyleColor(ImGuiCol_PopupBg, UIPopupBackgroundColor);
|
||||||
|
|
||||||
bool is_open = true;
|
bool is_open = true;
|
||||||
if (ImGui::BeginPopupModal(s_input_dialog_title.c_str(), &is_open,
|
if (ImGui::BeginPopupModal(s_input_dialog_title.c_str(), &is_open,
|
||||||
@ -2715,6 +2716,7 @@ void ImGuiFullscreen::DrawMessageDialog()
|
|||||||
ImGui::PushStyleColor(ImGuiCol_Text, UIPrimaryTextColor);
|
ImGui::PushStyleColor(ImGuiCol_Text, UIPrimaryTextColor);
|
||||||
ImGui::PushStyleColor(ImGuiCol_TitleBg, UIPrimaryDarkColor);
|
ImGui::PushStyleColor(ImGuiCol_TitleBg, UIPrimaryDarkColor);
|
||||||
ImGui::PushStyleColor(ImGuiCol_TitleBgActive, UIPrimaryColor);
|
ImGui::PushStyleColor(ImGuiCol_TitleBgActive, UIPrimaryColor);
|
||||||
|
ImGui::PushStyleColor(ImGuiCol_PopupBg, UIPopupBackgroundColor);
|
||||||
|
|
||||||
bool is_open = true;
|
bool is_open = true;
|
||||||
const u32 flags = ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove |
|
const u32 flags = ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove |
|
||||||
@ -2745,7 +2747,7 @@ void ImGuiFullscreen::DrawMessageDialog()
|
|||||||
ImGui::EndPopup();
|
ImGui::EndPopup();
|
||||||
}
|
}
|
||||||
|
|
||||||
ImGui::PopStyleColor(3);
|
ImGui::PopStyleColor(4);
|
||||||
ImGui::PopStyleVar(4);
|
ImGui::PopStyleVar(4);
|
||||||
ImGui::PopFont();
|
ImGui::PopFont();
|
||||||
|
|
||||||
|
|||||||
@ -1382,8 +1382,7 @@ void SaveStateSelectorUI::LoadCurrentSlot()
|
|||||||
Host::RunOnCPUThread([slot = GetCurrentSlot()]() {
|
Host::RunOnCPUThread([slot = GetCurrentSlot()]() {
|
||||||
Error error;
|
Error error;
|
||||||
if (!VMManager::LoadStateFromSlot(slot, false, &error))
|
if (!VMManager::LoadStateFromSlot(slot, false, &error))
|
||||||
Host::AddIconOSDMessage("LoadStateFromSlot", ICON_FA_TRIANGLE_EXCLAMATION,
|
FullscreenUI::ReportStateLoadError(error.GetDescription(), slot, false);
|
||||||
error.GetDescription(), Host::OSD_INFO_DURATION);
|
|
||||||
});
|
});
|
||||||
Close();
|
Close();
|
||||||
}
|
}
|
||||||
@ -1393,8 +1392,7 @@ void SaveStateSelectorUI::LoadCurrentBackupSlot()
|
|||||||
Host::RunOnCPUThread([slot = GetCurrentSlot()]() {
|
Host::RunOnCPUThread([slot = GetCurrentSlot()]() {
|
||||||
Error error;
|
Error error;
|
||||||
if (!VMManager::LoadStateFromSlot(slot, true, &error))
|
if (!VMManager::LoadStateFromSlot(slot, true, &error))
|
||||||
Host::AddIconOSDMessage("LoadStateFromSlot", ICON_FA_TRIANGLE_EXCLAMATION,
|
FullscreenUI::ReportStateLoadError(error.GetDescription(), slot, true);
|
||||||
error.GetDescription(), Host::OSD_INFO_DURATION);
|
|
||||||
});
|
});
|
||||||
Close();
|
Close();
|
||||||
}
|
}
|
||||||
@ -1402,7 +1400,9 @@ void SaveStateSelectorUI::LoadCurrentBackupSlot()
|
|||||||
void SaveStateSelectorUI::SaveCurrentSlot()
|
void SaveStateSelectorUI::SaveCurrentSlot()
|
||||||
{
|
{
|
||||||
Host::RunOnCPUThread([slot = GetCurrentSlot()]() {
|
Host::RunOnCPUThread([slot = GetCurrentSlot()]() {
|
||||||
VMManager::SaveStateToSlot(slot);
|
VMManager::SaveStateToSlot(slot, true, [slot](const std::string& error) {
|
||||||
|
FullscreenUI::ReportStateSaveError(error, slot);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
Close();
|
Close();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -6,6 +6,7 @@
|
|||||||
#include "Host.h"
|
#include "Host.h"
|
||||||
#include "Memory.h"
|
#include "Memory.h"
|
||||||
#include "Elfheader.h"
|
#include "Elfheader.h"
|
||||||
|
#include "SaveState.h"
|
||||||
#include "PINE.h"
|
#include "PINE.h"
|
||||||
#include "VMManager.h"
|
#include "VMManager.h"
|
||||||
#include "common/Error.h"
|
#include "common/Error.h"
|
||||||
@ -19,7 +20,6 @@
|
|||||||
#include <thread>
|
#include <thread>
|
||||||
|
|
||||||
#include "fmt/format.h"
|
#include "fmt/format.h"
|
||||||
#include "IconsFontAwesome6.h"
|
|
||||||
|
|
||||||
#if defined(_WIN32)
|
#if defined(_WIN32)
|
||||||
#define read_portable(a, b, c) (recv(a, (char*)b, c, 0))
|
#define read_portable(a, b, c) (recv(a, (char*)b, c, 0))
|
||||||
@ -646,7 +646,11 @@ PINEServer::IPCBuffer PINEServer::ParseCommand(std::span<u8> buf, std::vector<u8
|
|||||||
goto error;
|
goto error;
|
||||||
if (!SafetyChecks(buf_cnt, 1, ret_cnt, 0, buf_size)) [[unlikely]]
|
if (!SafetyChecks(buf_cnt, 1, ret_cnt, 0, buf_size)) [[unlikely]]
|
||||||
goto error;
|
goto error;
|
||||||
Host::RunOnCPUThread([slot = FromSpan<u8>(buf, buf_cnt)] { VMManager::SaveStateToSlot(slot); });
|
Host::RunOnCPUThread([slot = FromSpan<u8>(buf, buf_cnt)] {
|
||||||
|
VMManager::SaveStateToSlot(slot, true, [slot](const std::string& error) {
|
||||||
|
SaveState_ReportSaveErrorOSD(error, slot);
|
||||||
|
});
|
||||||
|
});
|
||||||
buf_cnt += 1;
|
buf_cnt += 1;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -659,8 +663,7 @@ PINEServer::IPCBuffer PINEServer::ParseCommand(std::span<u8> buf, std::vector<u8
|
|||||||
Host::RunOnCPUThread([slot = FromSpan<u8>(buf, buf_cnt)] {
|
Host::RunOnCPUThread([slot = FromSpan<u8>(buf, buf_cnt)] {
|
||||||
Error state_error;
|
Error state_error;
|
||||||
if (!VMManager::LoadStateFromSlot(slot, false, &state_error))
|
if (!VMManager::LoadStateFromSlot(slot, false, &state_error))
|
||||||
Host::AddIconOSDMessage("LoadStateFromSlot", ICON_FA_TRIANGLE_EXCLAMATION,
|
SaveState_ReportLoadErrorOSD(state_error.GetDescription(), slot, false);
|
||||||
state_error.GetDescription(), Host::OSD_INFO_DURATION);
|
|
||||||
});
|
});
|
||||||
buf_cnt += 1;
|
buf_cnt += 1;
|
||||||
break;
|
break;
|
||||||
|
|||||||
187
pcsx2/Patch.cpp
187
pcsx2/Patch.cpp
@ -51,7 +51,7 @@ namespace Patch
|
|||||||
BYTES_T
|
BYTES_T
|
||||||
};
|
};
|
||||||
|
|
||||||
static constexpr std::array<const char*, 3> s_place_to_string = {{"0", "1", "2"}};
|
static constexpr std::array<const char*, 4> s_place_to_string = {{"0", "1", "2", "3"}};
|
||||||
static constexpr std::array<const char*, 2> s_cpu_to_string = {{"EE", "IOP"}};
|
static constexpr std::array<const char*, 2> s_cpu_to_string = {{"EE", "IOP"}};
|
||||||
static constexpr std::array<const char*, 9> s_type_to_string = {
|
static constexpr std::array<const char*, 9> s_type_to_string = {
|
||||||
{"byte", "short", "word", "double", "extended", "beshort", "beword", "bedouble", "bytes"}};
|
{"byte", "short", "word", "double", "extended", "beshort", "beword", "bedouble", "bytes"}};
|
||||||
@ -125,10 +125,6 @@ namespace Patch
|
|||||||
void (*func)(PatchGroup* group, const std::string_view cmd, const std::string_view param);
|
void (*func)(PatchGroup* group, const std::string_view cmd, const std::string_view param);
|
||||||
};
|
};
|
||||||
|
|
||||||
using PatchList = std::vector<PatchGroup>;
|
|
||||||
using ActivePatchList = std::vector<const PatchCommand*>;
|
|
||||||
using EnablePatchList = std::vector<std::string>;
|
|
||||||
|
|
||||||
namespace PatchFunc
|
namespace PatchFunc
|
||||||
{
|
{
|
||||||
static void patch(PatchGroup* group, const std::string_view cmd, const std::string_view param);
|
static void patch(PatchGroup* group, const std::string_view cmd, const std::string_view param);
|
||||||
@ -141,23 +137,23 @@ namespace Patch
|
|||||||
static int PatchTableExecute(PatchGroup* group, const std::string_view lhs, const std::string_view rhs,
|
static int PatchTableExecute(PatchGroup* group, const std::string_view lhs, const std::string_view rhs,
|
||||||
const std::span<const PatchTextTable>& Table);
|
const std::span<const PatchTextTable>& Table);
|
||||||
static void LoadPatchLine(PatchGroup* group, const std::string_view line);
|
static void LoadPatchLine(PatchGroup* group, const std::string_view line);
|
||||||
static u32 LoadPatchesFromString(PatchList* patch_list, const std::string& patch_file);
|
static u32 LoadPatchesFromString(std::vector<PatchGroup>* patch_list, const std::string& patch_file);
|
||||||
static bool OpenPatchesZip();
|
static bool OpenPatchesZip();
|
||||||
static std::string GetPnachTemplate(
|
static std::string GetPnachTemplate(
|
||||||
const std::string_view serial, u32 crc, bool include_serial, bool add_wildcard, bool all_crcs);
|
const std::string_view serial, u32 crc, bool include_serial, bool add_wildcard, bool all_crcs);
|
||||||
static std::vector<std::string> FindPatchFilesOnDisk(
|
static std::vector<std::string> FindPatchFilesOnDisk(
|
||||||
const std::string_view serial, u32 crc, bool cheats, bool all_crcs);
|
const std::string_view serial, u32 crc, bool cheats, bool all_crcs);
|
||||||
|
|
||||||
static bool ContainsPatchName(const PatchInfoList& patches, const std::string_view patchName);
|
static bool ContainsPatchName(const std::vector<PatchInfo>& patches, const std::string_view patchName);
|
||||||
static bool ContainsPatchName(const PatchList& patches, const std::string_view patchName);
|
static bool ContainsPatchName(const std::vector<PatchGroup>& patches, const std::string_view patchName);
|
||||||
|
|
||||||
template <typename F>
|
template <typename F>
|
||||||
static void EnumeratePnachFiles(const std::string_view serial, u32 crc, bool cheats, bool for_ui, const F& f);
|
static void EnumeratePnachFiles(const std::string_view serial, u32 crc, bool cheats, bool for_ui, const F& f);
|
||||||
|
|
||||||
static bool PatchStringHasUnlabelledPatch(const std::string& pnach_data);
|
static bool PatchStringHasUnlabelledPatch(const std::string& pnach_data);
|
||||||
static void ExtractPatchInfo(PatchInfoList* dst, const std::string& pnach_data, u32* num_unlabelled_patches);
|
static void ExtractPatchInfo(std::vector<PatchInfo>* dst, const std::string& pnach_data, u32* num_unlabelled_patches);
|
||||||
static void ReloadEnabledLists();
|
static void ReloadEnabledLists();
|
||||||
static u32 EnablePatches(const PatchList& patches, const EnablePatchList& enable_list, const EnablePatchList& enable_immediately_list);
|
static u32 EnablePatches(const std::vector<PatchGroup>* patches, const std::vector<std::string>& enable_list, const std::vector<std::string>* enable_immediately_list);
|
||||||
|
|
||||||
static void ApplyPatch(const PatchCommand* p);
|
static void ApplyPatch(const PatchCommand* p);
|
||||||
static void ApplyDynaPatch(const DynamicPatch& patch, u32 address);
|
static void ApplyDynaPatch(const DynamicPatch& patch, u32 address);
|
||||||
@ -175,21 +171,21 @@ namespace Patch
|
|||||||
const char* PATCH_DISABLE_CONFIG_KEY = "Disable";
|
const char* PATCH_DISABLE_CONFIG_KEY = "Disable";
|
||||||
|
|
||||||
static zip_t* s_patches_zip;
|
static zip_t* s_patches_zip;
|
||||||
static PatchList s_gamedb_patches;
|
static std::vector<PatchGroup> s_gamedb_patches;
|
||||||
static PatchList s_game_patches;
|
static std::vector<PatchGroup> s_game_patches;
|
||||||
static PatchList s_cheat_patches;
|
static std::vector<PatchGroup> s_cheat_patches;
|
||||||
|
|
||||||
static u32 s_gamedb_counts = 0;
|
static u32 s_gamedb_counts = 0;
|
||||||
static u32 s_patches_counts = 0;
|
static u32 s_patches_counts = 0;
|
||||||
static u32 s_cheats_counts = 0;
|
static u32 s_cheats_counts = 0;
|
||||||
|
|
||||||
static ActivePatchList s_active_patches;
|
static std::vector<const PatchCommand*> s_active_patches;
|
||||||
static std::vector<DynamicPatch> s_active_gamedb_dynamic_patches;
|
static std::vector<DynamicPatch> s_active_gamedb_dynamic_patches;
|
||||||
static std::vector<DynamicPatch> s_active_pnach_dynamic_patches;
|
static std::vector<DynamicPatch> s_active_pnach_dynamic_patches;
|
||||||
static EnablePatchList s_enabled_cheats;
|
static std::vector<std::string> s_enabled_cheats;
|
||||||
static EnablePatchList s_enabled_patches;
|
static std::vector<std::string> s_enabled_patches;
|
||||||
static EnablePatchList s_just_enabled_cheats;
|
static std::vector<std::string> s_just_enabled_cheats;
|
||||||
static EnablePatchList s_just_enabled_patches;
|
static std::vector<std::string> s_just_enabled_patches;
|
||||||
static u32 s_patches_crc;
|
static u32 s_patches_crc;
|
||||||
static std::optional<float> s_override_aspect_ratio;
|
static std::optional<float> s_override_aspect_ratio;
|
||||||
static std::optional<GSInterlaceMode> s_override_interlace_mode;
|
static std::optional<GSInterlaceMode> s_override_interlace_mode;
|
||||||
@ -218,7 +214,7 @@ void Patch::TrimPatchLine(std::string& buffer)
|
|||||||
buffer.erase(pos);
|
buffer.erase(pos);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Patch::ContainsPatchName(const PatchList& patch_list, const std::string_view patch_name)
|
bool Patch::ContainsPatchName(const std::vector<PatchGroup>& patch_list, const std::string_view patch_name)
|
||||||
{
|
{
|
||||||
return std::find_if(patch_list.begin(), patch_list.end(), [&patch_name](const PatchGroup& patch) {
|
return std::find_if(patch_list.begin(), patch_list.end(), [&patch_name](const PatchGroup& patch) {
|
||||||
return patch.name == patch_name;
|
return patch.name == patch_name;
|
||||||
@ -253,7 +249,7 @@ void Patch::LoadPatchLine(PatchGroup* group, const std::string_view line)
|
|||||||
PatchTableExecute(group, key, value, s_patch_commands);
|
PatchTableExecute(group, key, value, s_patch_commands);
|
||||||
}
|
}
|
||||||
|
|
||||||
u32 Patch::LoadPatchesFromString(PatchList* patch_list, const std::string& patch_file)
|
u32 Patch::LoadPatchesFromString(std::vector<PatchGroup>* patch_list, const std::string& patch_file)
|
||||||
{
|
{
|
||||||
const size_t before = patch_list->size();
|
const size_t before = patch_list->size();
|
||||||
|
|
||||||
@ -264,7 +260,7 @@ u32 Patch::LoadPatchesFromString(PatchList* patch_list, const std::string& patch
|
|||||||
// Ungrouped/legacy patches should merge with other ungrouped patches.
|
// Ungrouped/legacy patches should merge with other ungrouped patches.
|
||||||
if (current_patch_group.name.empty())
|
if (current_patch_group.name.empty())
|
||||||
{
|
{
|
||||||
const PatchList::iterator ungrouped_patch = std::find_if(patch_list->begin(), patch_list->end(),
|
const std::vector<PatchGroup>::iterator ungrouped_patch = std::find_if(patch_list->begin(), patch_list->end(),
|
||||||
[](const PatchGroup& pg) { return pg.name.empty(); });
|
[](const PatchGroup& pg) { return pg.name.empty(); });
|
||||||
if (ungrouped_patch != patch_list->end())
|
if (ungrouped_patch != patch_list->end())
|
||||||
{
|
{
|
||||||
@ -407,7 +403,7 @@ std::vector<std::string> Patch::FindPatchFilesOnDisk(const std::string_view seri
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Patch::ContainsPatchName(const PatchInfoList& patches, const std::string_view patchName)
|
bool Patch::ContainsPatchName(const std::vector<PatchInfo>& patches, const std::string_view patchName)
|
||||||
{
|
{
|
||||||
return std::find_if(patches.begin(), patches.end(), [&patchName](const PatchInfo& patch) {
|
return std::find_if(patches.begin(), patches.end(), [&patchName](const PatchInfo& patch) {
|
||||||
return patch.name == patchName;
|
return patch.name == patchName;
|
||||||
@ -491,11 +487,15 @@ bool Patch::PatchStringHasUnlabelledPatch(const std::string& pnach_data)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Patch::ExtractPatchInfo(PatchInfoList* dst, const std::string& pnach_data, u32* num_unlabelled_patches)
|
void Patch::ExtractPatchInfo(std::vector<PatchInfo>* dst, const std::string& pnach_data, u32* num_unlabelled_patches)
|
||||||
{
|
{
|
||||||
std::istringstream ss(pnach_data);
|
std::istringstream ss(pnach_data);
|
||||||
std::string line;
|
std::string line;
|
||||||
PatchInfo current_patch;
|
PatchInfo current_patch;
|
||||||
|
|
||||||
|
std::optional<patch_place_type> last_place;
|
||||||
|
bool unknown_place = false;
|
||||||
|
|
||||||
while (std::getline(ss, line))
|
while (std::getline(ss, line))
|
||||||
{
|
{
|
||||||
TrimPatchLine(line);
|
TrimPatchLine(line);
|
||||||
@ -522,6 +522,8 @@ void Patch::ExtractPatchInfo(PatchInfoList* dst, const std::string& pnach_data,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
current_patch = {};
|
current_patch = {};
|
||||||
|
last_place = std::nullopt;
|
||||||
|
unknown_place = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
current_patch.name = line.substr(1, line.length() - 2);
|
current_patch.name = line.substr(1, line.length() - 2);
|
||||||
@ -534,13 +536,52 @@ void Patch::ExtractPatchInfo(PatchInfoList* dst, const std::string& pnach_data,
|
|||||||
// Just ignore other directives, who knows what rubbish people have in here.
|
// Just ignore other directives, who knows what rubbish people have in here.
|
||||||
// Use comment for description if it hasn't been otherwise specified.
|
// Use comment for description if it hasn't been otherwise specified.
|
||||||
if (key == "author")
|
if (key == "author")
|
||||||
|
{
|
||||||
current_patch.author = value;
|
current_patch.author = value;
|
||||||
|
}
|
||||||
else if (key == "description")
|
else if (key == "description")
|
||||||
|
{
|
||||||
current_patch.description = value;
|
current_patch.description = value;
|
||||||
|
}
|
||||||
else if (key == "comment" && current_patch.description.empty())
|
else if (key == "comment" && current_patch.description.empty())
|
||||||
|
{
|
||||||
current_patch.description = value;
|
current_patch.description = value;
|
||||||
else if (key == "patch" && !has_patch && num_unlabelled_patches)
|
}
|
||||||
(*num_unlabelled_patches)++;
|
else if (key == "patch")
|
||||||
|
{
|
||||||
|
if (!has_patch && num_unlabelled_patches)
|
||||||
|
(*num_unlabelled_patches)++;
|
||||||
|
|
||||||
|
// Try to extract the place value of the patch lines so we can
|
||||||
|
// display it in the GUI if they all match. TODO: Don't duplicate
|
||||||
|
// all this parsing logic twice.
|
||||||
|
if (unknown_place)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
std::string::size_type comma_pos = value.find(",");
|
||||||
|
if (comma_pos == std::string::npos)
|
||||||
|
comma_pos = 0;
|
||||||
|
const std::string_view padded_place = value.substr(0, comma_pos);
|
||||||
|
const std::string_view place_string = StringUtil::StripWhitespace(padded_place);
|
||||||
|
const std::optional<patch_place_type> place = LookupEnumName<patch_place_type>(
|
||||||
|
place_string, s_place_to_string);
|
||||||
|
if (!place.has_value() || (last_place.has_value() && place != last_place))
|
||||||
|
{
|
||||||
|
// This group contains patch lines with different or invalid
|
||||||
|
// place values.
|
||||||
|
current_patch.place = std::nullopt;
|
||||||
|
unknown_place = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
current_patch.place = place;
|
||||||
|
last_place = place;
|
||||||
|
}
|
||||||
|
else if (key == "dpatch")
|
||||||
|
{
|
||||||
|
current_patch.place = std::nullopt;
|
||||||
|
unknown_place = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Last one.
|
// Last one.
|
||||||
@ -570,9 +611,9 @@ std::string_view Patch::PatchInfo::GetNameParentPart() const
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
Patch::PatchInfoList Patch::GetPatchInfo(const std::string_view serial, u32 crc, bool cheats, bool showAllCRCS, u32* num_unlabelled_patches)
|
std::vector<Patch::PatchInfo> Patch::GetPatchInfo(const std::string_view serial, u32 crc, bool cheats, bool showAllCRCS, u32* num_unlabelled_patches)
|
||||||
{
|
{
|
||||||
PatchInfoList ret;
|
std::vector<PatchInfo> ret;
|
||||||
|
|
||||||
if (num_unlabelled_patches)
|
if (num_unlabelled_patches)
|
||||||
*num_unlabelled_patches = 0;
|
*num_unlabelled_patches = 0;
|
||||||
@ -592,14 +633,14 @@ std::string Patch::GetPnachFilename(const std::string_view serial, u32 crc, bool
|
|||||||
|
|
||||||
void Patch::ReloadEnabledLists()
|
void Patch::ReloadEnabledLists()
|
||||||
{
|
{
|
||||||
const EnablePatchList prev_enabled_cheats = std::move(s_enabled_cheats);
|
const std::vector<std::string> prev_enabled_cheats = std::move(s_enabled_cheats);
|
||||||
if (EmuConfig.EnableCheats && !Achievements::IsHardcoreModeActive())
|
if (EmuConfig.EnableCheats && !Achievements::IsHardcoreModeActive())
|
||||||
s_enabled_cheats = Host::GetStringListSetting(CHEATS_CONFIG_SECTION, PATCH_ENABLE_CONFIG_KEY);
|
s_enabled_cheats = Host::GetStringListSetting(CHEATS_CONFIG_SECTION, PATCH_ENABLE_CONFIG_KEY);
|
||||||
else
|
else
|
||||||
s_enabled_cheats = {};
|
s_enabled_cheats = {};
|
||||||
|
|
||||||
const EnablePatchList prev_enabled_patches = std::exchange(s_enabled_patches, Host::GetStringListSetting(PATCHES_CONFIG_SECTION, PATCH_ENABLE_CONFIG_KEY));
|
const std::vector<std::string> prev_enabled_patches = std::exchange(s_enabled_patches, Host::GetStringListSetting(PATCHES_CONFIG_SECTION, PATCH_ENABLE_CONFIG_KEY));
|
||||||
const EnablePatchList disabled_patches = Host::GetStringListSetting(PATCHES_CONFIG_SECTION, PATCH_DISABLE_CONFIG_KEY);
|
const std::vector<std::string> disabled_patches = Host::GetStringListSetting(PATCHES_CONFIG_SECTION, PATCH_DISABLE_CONFIG_KEY);
|
||||||
|
|
||||||
// Name based matching for widescreen/NI settings.
|
// Name based matching for widescreen/NI settings.
|
||||||
if (EmuConfig.EnableWideScreenPatches)
|
if (EmuConfig.EnableWideScreenPatches)
|
||||||
@ -649,12 +690,10 @@ void Patch::ReloadEnabledLists()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
u32 Patch::EnablePatches(const PatchList& patches, const EnablePatchList& enable_list, const EnablePatchList& enable_immediately_list)
|
u32 Patch::EnablePatches(const std::vector<PatchGroup>* patches, const std::vector<std::string>& enable_list, const std::vector<std::string>* enable_immediately_list)
|
||||||
{
|
{
|
||||||
ActivePatchList patches_to_apply_immediately;
|
|
||||||
|
|
||||||
u32 count = 0;
|
u32 count = 0;
|
||||||
for (const PatchGroup& p : patches)
|
for (const PatchGroup& p : *patches)
|
||||||
{
|
{
|
||||||
// For compatibility, we auto enable anything that's not labelled.
|
// For compatibility, we auto enable anything that's not labelled.
|
||||||
// Also for gamedb patches.
|
// Also for gamedb patches.
|
||||||
@ -664,7 +703,6 @@ u32 Patch::EnablePatches(const PatchList& patches, const EnablePatchList& enable
|
|||||||
Console.WriteLn(Color_Green, fmt::format("Enabled patch: {}",
|
Console.WriteLn(Color_Green, fmt::format("Enabled patch: {}",
|
||||||
p.name.empty() ? std::string_view("<unknown>") : std::string_view(p.name)));
|
p.name.empty() ? std::string_view("<unknown>") : std::string_view(p.name)));
|
||||||
|
|
||||||
const bool apply_immediately = std::find(enable_immediately_list.begin(), enable_immediately_list.end(), p.name) != enable_immediately_list.end();
|
|
||||||
for (const PatchCommand& ip : p.patches)
|
for (const PatchCommand& ip : p.patches)
|
||||||
{
|
{
|
||||||
// print the actual patch lines only in verbose mode (even in devel)
|
// print the actual patch lines only in verbose mode (even in devel)
|
||||||
@ -672,8 +710,6 @@ u32 Patch::EnablePatches(const PatchList& patches, const EnablePatchList& enable
|
|||||||
DevCon.WriteLnFmt(" {}", ip.ToString());
|
DevCon.WriteLnFmt(" {}", ip.ToString());
|
||||||
|
|
||||||
s_active_patches.push_back(&ip);
|
s_active_patches.push_back(&ip);
|
||||||
if (apply_immediately && ip.placetopatch == PPT_ONCE_ON_LOAD)
|
|
||||||
patches_to_apply_immediately.push_back(&ip);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const DynamicPatch& dp : p.dpatches)
|
for (const DynamicPatch& dp : p.dpatches)
|
||||||
@ -690,12 +726,28 @@ u32 Patch::EnablePatches(const PatchList& patches, const EnablePatchList& enable
|
|||||||
count += p.name.empty() ? (static_cast<u32>(p.patches.size()) + static_cast<u32>(p.dpatches.size())) : 1;
|
count += p.name.empty() ? (static_cast<u32>(p.patches.size()) + static_cast<u32>(p.dpatches.size())) : 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!patches_to_apply_immediately.empty())
|
// Apply PPT_ON_LOAD_OR_WHEN_ENABLED patches immediately.
|
||||||
|
if (enable_immediately_list && !enable_immediately_list->empty())
|
||||||
{
|
{
|
||||||
Host::RunOnCPUThread([patches = std::move(patches_to_apply_immediately)]() {
|
// Don't pass pointers to patch objects themselves here just in case the
|
||||||
for (const PatchCommand* i : patches)
|
// patches are reloaded twice in a row before this event makes it.
|
||||||
|
Host::RunOnCPUThread([patches, enable_immediately_list]() {
|
||||||
|
for (const PatchGroup& group : *patches)
|
||||||
{
|
{
|
||||||
ApplyPatch(i);
|
const bool apply_immediately = std::find(
|
||||||
|
enable_immediately_list->begin(),
|
||||||
|
enable_immediately_list->end(),
|
||||||
|
group.name) != enable_immediately_list->end();
|
||||||
|
if (!apply_immediately)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
for (const PatchCommand& command : group.patches)
|
||||||
|
{
|
||||||
|
if (command.placetopatch != PPT_ON_LOAD_OR_WHEN_ENABLED)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
ApplyPatch(&command);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -762,19 +814,23 @@ void Patch::UpdateActivePatches(bool reload_enabled_list, bool verbose, bool ver
|
|||||||
u32 gp_count = 0;
|
u32 gp_count = 0;
|
||||||
if (EmuConfig.EnablePatches)
|
if (EmuConfig.EnablePatches)
|
||||||
{
|
{
|
||||||
gp_count = EnablePatches(s_gamedb_patches, EnablePatchList(), EnablePatchList());
|
gp_count = EnablePatches(&s_gamedb_patches, std::vector<std::string>(), nullptr);
|
||||||
s_gamedb_counts = gp_count;
|
s_gamedb_counts = gp_count;
|
||||||
if (gp_count > 0)
|
if (gp_count > 0)
|
||||||
message.append(TRANSLATE_PLURAL_STR("Patch", "%n GameDB patches are active.", "OSD Message", gp_count));
|
message.append(TRANSLATE_PLURAL_STR("Patch", "%n GameDB patches are active.", "OSD Message", gp_count));
|
||||||
}
|
}
|
||||||
|
|
||||||
const u32 p_count = EnablePatches(s_game_patches, s_enabled_patches, apply_new_patches ? s_just_enabled_patches : EnablePatchList());
|
const u32 p_count = EnablePatches(
|
||||||
|
&s_game_patches, s_enabled_patches, apply_new_patches ? &s_just_enabled_patches : nullptr);
|
||||||
s_patches_counts = p_count;
|
s_patches_counts = p_count;
|
||||||
if (p_count > 0)
|
if (p_count > 0)
|
||||||
message.append_format("{}{}", message.empty() ? "" : "\n",
|
message.append_format("{}{}", message.empty() ? "" : "\n",
|
||||||
TRANSLATE_PLURAL_STR("Patch", "%n game patches are active.", "OSD Message", p_count));
|
TRANSLATE_PLURAL_STR("Patch", "%n game patches are active.", "OSD Message", p_count));
|
||||||
|
|
||||||
const u32 c_count = EmuConfig.EnableCheats ? EnablePatches(s_cheat_patches, s_enabled_cheats, apply_new_patches ? s_just_enabled_cheats : EnablePatchList()) : 0;
|
u32 c_count = 0;
|
||||||
|
if (EmuConfig.EnableCheats)
|
||||||
|
c_count = EnablePatches(
|
||||||
|
&s_cheat_patches, s_enabled_cheats, apply_new_patches ? &s_just_enabled_cheats : nullptr);
|
||||||
s_cheats_counts = c_count;
|
s_cheats_counts = c_count;
|
||||||
if (c_count > 0)
|
if (c_count > 0)
|
||||||
message.append_format("{}{}", message.empty() ? "" : "\n",
|
message.append_format("{}{}", message.empty() ? "" : "\n",
|
||||||
@ -892,7 +948,7 @@ void Patch::PatchFunc::patch(PatchGroup* group, const std::string_view cmd, cons
|
|||||||
|
|
||||||
if (!placetopatch.has_value())
|
if (!placetopatch.has_value())
|
||||||
{
|
{
|
||||||
PATCH_ERROR("Invalid 'place' value '{}' (0 - once on startup, 1: continuously)", pieces[0]);
|
PATCH_ERROR("Invalid 'place' value '{}' (0: on boot only, 1: continuously, 2: on boot and continuously, 3: on boot and when enabled in the GUI)", pieces[0]);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!addr.has_value() || !addr_end.empty())
|
if (!addr.has_value() || !addr_end.empty())
|
||||||
@ -1083,6 +1139,19 @@ void Patch::PatchFunc::dpatch(PatchGroup* group, const std::string_view cmd, con
|
|||||||
group->dpatches.push_back(dpatch);
|
group->dpatches.push_back(dpatch);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Patch::ApplyBootPatches()
|
||||||
|
{
|
||||||
|
ApplyLoadedPatches(PPT_ONCE_ON_LOAD);
|
||||||
|
ApplyLoadedPatches(PPT_COMBINED_0_1);
|
||||||
|
ApplyLoadedPatches(PPT_ON_LOAD_OR_WHEN_ENABLED);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Patch::ApplyVsyncPatches()
|
||||||
|
{
|
||||||
|
ApplyLoadedPatches(PPT_CONTINUOUSLY);
|
||||||
|
ApplyLoadedPatches(PPT_COMBINED_0_1);
|
||||||
|
}
|
||||||
|
|
||||||
// This is for applying patches directly to memory
|
// This is for applying patches directly to memory
|
||||||
void Patch::ApplyLoadedPatches(patch_place_type place)
|
void Patch::ApplyLoadedPatches(patch_place_type place)
|
||||||
{
|
{
|
||||||
@ -1731,3 +1800,31 @@ void Patch::ApplyDynaPatch(const DynamicPatch& patch, u32 address)
|
|||||||
memWrite32(address + replacement.offset, replacement.value);
|
memWrite32(address + replacement.offset, replacement.value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const char* Patch::PlaceToString(std::optional<patch_place_type> place)
|
||||||
|
{
|
||||||
|
if (!place.has_value())
|
||||||
|
//: Time when a patch is applied.
|
||||||
|
return TRANSLATE("Patch", "Unknown");
|
||||||
|
|
||||||
|
switch (*place)
|
||||||
|
{
|
||||||
|
case Patch::PPT_ONCE_ON_LOAD:
|
||||||
|
//: Time when a patch is applied.
|
||||||
|
return TRANSLATE("Patch", "Only On Startup");
|
||||||
|
case Patch::PPT_CONTINUOUSLY:
|
||||||
|
//: Time when a patch is applied.
|
||||||
|
return TRANSLATE("Patch", "Every Frame");
|
||||||
|
case Patch::PPT_COMBINED_0_1:
|
||||||
|
//: Time when a patch is applied.
|
||||||
|
return TRANSLATE("Patch", "On Startup & Every Frame");
|
||||||
|
case Patch::PPT_ON_LOAD_OR_WHEN_ENABLED:
|
||||||
|
//: Time when a patch is applied.
|
||||||
|
return TRANSLATE("Patch", "On Startup & When Enabled");
|
||||||
|
default:
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|||||||
@ -29,23 +29,22 @@ namespace Patch
|
|||||||
// In PCSX2 it indicates how/when/where the patch line should be applied. If
|
// In PCSX2 it indicates how/when/where the patch line should be applied. If
|
||||||
// place is not one of the supported values then the patch line is never applied.
|
// place is not one of the supported values then the patch line is never applied.
|
||||||
// PCSX2 currently supports the following values:
|
// PCSX2 currently supports the following values:
|
||||||
// 0 - apply the patch line once on game boot/startup
|
// 0 - apply the patch line once on game boot only
|
||||||
// 1 - apply the patch line continuously (technically - on every vsync)
|
// 1 - apply the patch line continuously (technically - on every vsync)
|
||||||
// 2 - effect of 0 and 1 combined, see below
|
// 2 - effect of 0 and 1 combined, see below
|
||||||
|
// 3 - apply the patch line once on game boot or when enabled in the GUI
|
||||||
// Note:
|
// Note:
|
||||||
// - while it may seem that a value of 1 does the same as 0, but also later
|
// - while it may seem that a value of 1 does the same as 0, but also later
|
||||||
// continues to apply the patch on every vsync - it's not.
|
// continues to apply the patch on every vsync - it's not.
|
||||||
// The current (and past) behavior is that these patches are applied at different
|
// The current (and past) behavior is that these patches are applied at different
|
||||||
// places at the code, and it's possible, depending on circumstances, that 0 patches
|
// places at the code, and it's possible, depending on circumstances, that 0 patches
|
||||||
// will get applied before the first vsync and therefore earlier than 1 patches.
|
// will get applied before the first vsync and therefore earlier than 1 patches.
|
||||||
// - There's no "place" value which indicates to apply both once on startup
|
|
||||||
// and then also continuously, however such behavior can be achieved by
|
|
||||||
// duplicating the line where one has a 0 place and the other has a 1 place.
|
|
||||||
enum patch_place_type : u8
|
enum patch_place_type : u8
|
||||||
{
|
{
|
||||||
PPT_ONCE_ON_LOAD = 0,
|
PPT_ONCE_ON_LOAD = 0,
|
||||||
PPT_CONTINUOUSLY = 1,
|
PPT_CONTINUOUSLY = 1,
|
||||||
PPT_COMBINED_0_1 = 2,
|
PPT_COMBINED_0_1 = 2,
|
||||||
|
PPT_ON_LOAD_OR_WHEN_ENABLED = 3,
|
||||||
|
|
||||||
PPT_END_MARKER
|
PPT_END_MARKER
|
||||||
};
|
};
|
||||||
@ -56,12 +55,14 @@ namespace Patch
|
|||||||
std::string description;
|
std::string description;
|
||||||
std::string author;
|
std::string author;
|
||||||
|
|
||||||
|
// This is only populated if all the patch lines in a given group have
|
||||||
|
// the same place value.
|
||||||
|
std::optional<patch_place_type> place;
|
||||||
|
|
||||||
std::string_view GetNamePart() const;
|
std::string_view GetNamePart() const;
|
||||||
std::string_view GetNameParentPart() const;
|
std::string_view GetNameParentPart() const;
|
||||||
};
|
};
|
||||||
|
|
||||||
using PatchInfoList = std::vector<PatchInfo>;
|
|
||||||
|
|
||||||
struct DynamicPatchEntry
|
struct DynamicPatchEntry
|
||||||
{
|
{
|
||||||
u32 offset;
|
u32 offset;
|
||||||
@ -80,7 +81,7 @@ namespace Patch
|
|||||||
extern const char* PATCH_ENABLE_CONFIG_KEY;
|
extern const char* PATCH_ENABLE_CONFIG_KEY;
|
||||||
extern const char* PATCH_DISABLE_CONFIG_KEY;
|
extern const char* PATCH_DISABLE_CONFIG_KEY;
|
||||||
|
|
||||||
extern PatchInfoList GetPatchInfo(const std::string_view serial, u32 crc, bool cheats, bool showAllCRCS, u32* num_unlabelled_patches);
|
extern std::vector<PatchInfo> GetPatchInfo(const std::string_view serial, u32 crc, bool cheats, bool showAllCRCS, u32* num_unlabelled_patches);
|
||||||
|
|
||||||
/// Returns the path to a new cheat/patch pnach for the specified serial and CRC.
|
/// Returns the path to a new cheat/patch pnach for the specified serial and CRC.
|
||||||
extern std::string GetPnachFilename(const std::string_view serial, u32 crc, bool cheats);
|
extern std::string GetPnachFilename(const std::string_view serial, u32 crc, bool cheats);
|
||||||
@ -97,6 +98,13 @@ namespace Patch
|
|||||||
extern void LoadDynamicPatches(const std::vector<DynamicPatch>& patches);
|
extern void LoadDynamicPatches(const std::vector<DynamicPatch>& patches);
|
||||||
extern void ApplyDynamicPatches(u32 pc);
|
extern void ApplyDynamicPatches(u32 pc);
|
||||||
|
|
||||||
|
// Apply all loaded patches that should be applied when the entry point is
|
||||||
|
// being recompiled.
|
||||||
|
extern void ApplyBootPatches();
|
||||||
|
|
||||||
|
// Apply all loaded patches that should be applied during vsync.
|
||||||
|
extern void ApplyVsyncPatches();
|
||||||
|
|
||||||
// Patches the emulation memory by applying all the loaded patches with a specific place value.
|
// Patches the emulation memory by applying all the loaded patches with a specific place value.
|
||||||
// Note: unless you know better, there's no need to check whether or not different patch sources
|
// Note: unless you know better, there's no need to check whether or not different patch sources
|
||||||
// are enabled (e.g. ws patches, auto game fixes, etc) before calling ApplyLoadedPatches,
|
// are enabled (e.g. ws patches, auto game fixes, etc) before calling ApplyLoadedPatches,
|
||||||
@ -112,4 +120,6 @@ namespace Patch
|
|||||||
extern u32 GetAllActivePatchesCount();
|
extern u32 GetAllActivePatchesCount();
|
||||||
|
|
||||||
extern bool IsGloballyToggleablePatch(const PatchInfo& patch_info);
|
extern bool IsGloballyToggleablePatch(const PatchInfo& patch_info);
|
||||||
|
|
||||||
|
extern const char* PlaceToString(std::optional<patch_place_type> place);
|
||||||
} // namespace Patch
|
} // namespace Patch
|
||||||
|
|||||||
@ -56,8 +56,9 @@ bool InputRecording::create(const std::string& fileName, const bool fromSaveStat
|
|||||||
m_initial_load_complete = true;
|
m_initial_load_complete = true;
|
||||||
m_watching_for_rerecords = true;
|
m_watching_for_rerecords = true;
|
||||||
setStartingFrame(g_FrameCount);
|
setStartingFrame(g_FrameCount);
|
||||||
// TODO - error handling
|
VMManager::SaveState(savestatePath.c_str(), true, false, [](const std::string& error) {
|
||||||
VMManager::SaveState(savestatePath.c_str());
|
SaveState_ReportSaveErrorOSD(error, std::nullopt);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -395,8 +396,7 @@ void InputRecording::InformGSThread()
|
|||||||
TinyString frame_data_message = TinyString::from_format(TRANSLATE_FS("InputRecording", "Frame: {}/{} ({})"), g_InputRecording.getFrameCounter(), g_InputRecording.getData().getTotalFrames(), g_InputRecording.getFrameCounterStateless());
|
TinyString frame_data_message = TinyString::from_format(TRANSLATE_FS("InputRecording", "Frame: {}/{} ({})"), g_InputRecording.getFrameCounter(), g_InputRecording.getData().getTotalFrames(), g_InputRecording.getFrameCounterStateless());
|
||||||
TinyString undo_count_message = TinyString::from_format(TRANSLATE_FS("InputRecording", "Undo Count: {}"), g_InputRecording.getData().getUndoCount());
|
TinyString undo_count_message = TinyString::from_format(TRANSLATE_FS("InputRecording", "Undo Count: {}"), g_InputRecording.getData().getUndoCount());
|
||||||
|
|
||||||
MTGS::RunOnGSThread([recording_active_message, frame_data_message, undo_count_message](bool is_recording = g_InputRecording.getControls().isRecording())
|
MTGS::RunOnGSThread([recording_active_message, frame_data_message, undo_count_message](bool is_recording = g_InputRecording.getControls().isRecording()) {
|
||||||
{
|
|
||||||
g_InputRecordingData.is_recording = is_recording;
|
g_InputRecordingData.is_recording = is_recording;
|
||||||
g_InputRecordingData.recording_active_message = recording_active_message;
|
g_InputRecordingData.recording_active_message = recording_active_message;
|
||||||
g_InputRecordingData.frame_data_message = frame_data_message;
|
g_InputRecordingData.frame_data_message = frame_data_message;
|
||||||
|
|||||||
@ -38,6 +38,7 @@
|
|||||||
#include "common/StringUtil.h"
|
#include "common/StringUtil.h"
|
||||||
#include "common/ZipHelpers.h"
|
#include "common/ZipHelpers.h"
|
||||||
|
|
||||||
|
#include "IconsFontAwesome6.h"
|
||||||
#include "fmt/format.h"
|
#include "fmt/format.h"
|
||||||
|
|
||||||
#include <csetjmp>
|
#include <csetjmp>
|
||||||
@ -1038,14 +1039,18 @@ static bool SaveState_AddToZip(zip_t* zf, ArchiveEntryList* srclist, SaveStateSc
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SaveState_ZipToDisk(std::unique_ptr<ArchiveEntryList> srclist, std::unique_ptr<SaveStateScreenshotData> screenshot, const char* filename)
|
bool SaveState_ZipToDisk(
|
||||||
|
std::unique_ptr<ArchiveEntryList> srclist, std::unique_ptr<SaveStateScreenshotData> screenshot,
|
||||||
|
const char* filename, Error* error)
|
||||||
{
|
{
|
||||||
zip_error_t ze = {};
|
zip_error_t ze = {};
|
||||||
zip_source_t* zs = zip_source_file_create(filename, 0, 0, &ze);
|
zip_source_t* zs = zip_source_file_create(filename, 0, 0, &ze);
|
||||||
zip_t* zf = nullptr;
|
zip_t* zf = nullptr;
|
||||||
if (zs && !(zf = zip_open_from_source(zs, ZIP_CREATE | ZIP_TRUNCATE, &ze)))
|
if (zs && !(zf = zip_open_from_source(zs, ZIP_CREATE | ZIP_TRUNCATE, &ze)))
|
||||||
{
|
{
|
||||||
Console.Error("Failed to open zip file '%s' for save state: %s", filename, zip_error_strerror(&ze));
|
Error::SetStringFmt(error,
|
||||||
|
TRANSLATE_FS("SaveState", "Failed to open zip file '{}' for save state: {}."),
|
||||||
|
filename, zip_error_strerror(&ze));
|
||||||
|
|
||||||
// have to clean up source
|
// have to clean up source
|
||||||
zip_source_free(zs);
|
zip_source_free(zs);
|
||||||
@ -1055,13 +1060,21 @@ bool SaveState_ZipToDisk(std::unique_ptr<ArchiveEntryList> srclist, std::unique_
|
|||||||
// discard zip file if we fail saving something
|
// discard zip file if we fail saving something
|
||||||
if (!SaveState_AddToZip(zf, srclist.get(), screenshot.get()))
|
if (!SaveState_AddToZip(zf, srclist.get(), screenshot.get()))
|
||||||
{
|
{
|
||||||
Console.Error("Failed to save state to zip file '%s'", filename);
|
Error::SetStringFmt(error,
|
||||||
|
TRANSLATE_FS("SaveState", "Failed to save state to zip file '{}'."), filename);
|
||||||
zip_discard(zf);
|
zip_discard(zf);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// force the zip to close, this is the expensive part with libzip.
|
// force the zip to close, this is the expensive part with libzip.
|
||||||
zip_close(zf);
|
if (zip_close(zf) != 0)
|
||||||
|
{
|
||||||
|
Error::SetStringFmt(error,
|
||||||
|
TRANSLATE_FS("SaveState", "Failed to save state to zip file '{}': {}."), filename, zip_strerror(zf));
|
||||||
|
zip_discard(zf);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1231,3 +1244,37 @@ bool SaveState_UnzipFromDisk(const std::string& filename, Error* error)
|
|||||||
PostLoadPrep();
|
PostLoadPrep();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SaveState_ReportLoadErrorOSD(const std::string& message, std::optional<s32> slot, bool backup)
|
||||||
|
{
|
||||||
|
std::string full_message;
|
||||||
|
if (slot.has_value())
|
||||||
|
{
|
||||||
|
if (backup)
|
||||||
|
full_message = fmt::format(
|
||||||
|
TRANSLATE_FS("SaveState", "Failed to load state from backup slot {}: {}"), *slot, message);
|
||||||
|
else
|
||||||
|
full_message = fmt::format(
|
||||||
|
TRANSLATE_FS("SaveState", "Failed to load state from slot {}: {}"), *slot, message);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
full_message = fmt::format(TRANSLATE_FS("SaveState", "Failed to load state: {}"), message);
|
||||||
|
}
|
||||||
|
|
||||||
|
Host::AddIconOSDMessage("LoadState", ICON_FA_TRIANGLE_EXCLAMATION,
|
||||||
|
full_message, Host::OSD_WARNING_DURATION);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SaveState_ReportSaveErrorOSD(const std::string& message, std::optional<s32> slot)
|
||||||
|
{
|
||||||
|
std::string full_message;
|
||||||
|
if (slot.has_value())
|
||||||
|
full_message = fmt::format(
|
||||||
|
TRANSLATE_FS("SaveState", "Failed to save state to slot {}: {}"), *slot, message);
|
||||||
|
else
|
||||||
|
full_message = fmt::format(TRANSLATE_FS("SaveState", "Failed to save state: {}"), message);
|
||||||
|
|
||||||
|
Host::AddIconOSDMessage("SaveState", ICON_FA_TRIANGLE_EXCLAMATION,
|
||||||
|
full_message, Host::OSD_WARNING_DURATION);
|
||||||
|
}
|
||||||
|
|||||||
@ -5,6 +5,7 @@
|
|||||||
|
|
||||||
#include <deque>
|
#include <deque>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
#include <optional>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
@ -53,7 +54,9 @@ class ArchiveEntryList;
|
|||||||
// These functions assume that the caller has paused the core thread.
|
// These functions assume that the caller has paused the core thread.
|
||||||
extern std::unique_ptr<ArchiveEntryList> SaveState_DownloadState(Error* error);
|
extern std::unique_ptr<ArchiveEntryList> SaveState_DownloadState(Error* error);
|
||||||
extern std::unique_ptr<SaveStateScreenshotData> SaveState_SaveScreenshot();
|
extern std::unique_ptr<SaveStateScreenshotData> SaveState_SaveScreenshot();
|
||||||
extern bool SaveState_ZipToDisk(std::unique_ptr<ArchiveEntryList> srclist, std::unique_ptr<SaveStateScreenshotData> screenshot, const char* filename);
|
extern bool SaveState_ZipToDisk(
|
||||||
|
std::unique_ptr<ArchiveEntryList> srclist, std::unique_ptr<SaveStateScreenshotData> screenshot,
|
||||||
|
const char* filename, Error* error);
|
||||||
extern bool SaveState_ReadScreenshot(const std::string& filename, u32* out_width, u32* out_height, std::vector<u32>* out_pixels);
|
extern bool SaveState_ReadScreenshot(const std::string& filename, u32* out_width, u32* out_height, std::vector<u32>* out_pixels);
|
||||||
extern bool SaveState_UnzipFromDisk(const std::string& filename, Error* error);
|
extern bool SaveState_UnzipFromDisk(const std::string& filename, Error* error);
|
||||||
|
|
||||||
@ -353,3 +356,6 @@ public:
|
|||||||
void FreezeMem(void* data, int size) override;
|
void FreezeMem(void* data, int size) override;
|
||||||
bool IsSaving() const override { return false; }
|
bool IsSaving() const override { return false; }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
void SaveState_ReportLoadErrorOSD(const std::string& message, std::optional<s32> slot, bool backup);
|
||||||
|
void SaveState_ReportSaveErrorOSD(const std::string& message, std::optional<s32> slot);
|
||||||
|
|||||||
@ -115,13 +115,13 @@ namespace VMManager
|
|||||||
|
|
||||||
static std::string GetCurrentSaveStateFileName(s32 slot, bool backup = false);
|
static std::string GetCurrentSaveStateFileName(s32 slot, bool backup = false);
|
||||||
static bool DoLoadState(const char* filename, Error* error = nullptr);
|
static bool DoLoadState(const char* filename, Error* error = nullptr);
|
||||||
static bool DoSaveState(const char* filename, s32 slot_for_message, bool zip_on_thread, bool backup_old_state);
|
static void DoSaveState(const char* filename, s32 slot_for_message, bool zip_on_thread, bool backup_old_state, std::function<void(const std::string&)> error_callback);
|
||||||
static void ZipSaveState(std::unique_ptr<ArchiveEntryList> elist,
|
static void ZipSaveState(std::unique_ptr<ArchiveEntryList> elist,
|
||||||
std::unique_ptr<SaveStateScreenshotData> screenshot, std::string osd_key, const char* filename,
|
std::unique_ptr<SaveStateScreenshotData> screenshot, const char* filename,
|
||||||
s32 slot_for_message);
|
s32 slot_for_message, std::function<void(const std::string&)> error_callback);
|
||||||
static void ZipSaveStateOnThread(std::unique_ptr<ArchiveEntryList> elist,
|
static void ZipSaveStateOnThread(std::unique_ptr<ArchiveEntryList> elist,
|
||||||
std::unique_ptr<SaveStateScreenshotData> screenshot, std::string osd_key, std::string filename,
|
std::unique_ptr<SaveStateScreenshotData> screenshot, std::string filename,
|
||||||
s32 slot_for_message);
|
s32 slot_for_message, std::function<void(const std::string&)> error_callback);
|
||||||
|
|
||||||
static void LoadSettings();
|
static void LoadSettings();
|
||||||
static void LoadCoreSettings(SettingsInterface& si);
|
static void LoadCoreSettings(SettingsInterface& si);
|
||||||
@ -1617,8 +1617,13 @@ void VMManager::Shutdown(bool save_resume_state)
|
|||||||
if (!GSDumpReplayer::IsReplayingDump() && save_resume_state)
|
if (!GSDumpReplayer::IsReplayingDump() && save_resume_state)
|
||||||
{
|
{
|
||||||
std::string resume_file_name(GetCurrentSaveStateFileName(-1));
|
std::string resume_file_name(GetCurrentSaveStateFileName(-1));
|
||||||
if (!resume_file_name.empty() && !DoSaveState(resume_file_name.c_str(), -1, true, false))
|
if (!resume_file_name.empty())
|
||||||
Console.Error("Failed to save resume state");
|
{
|
||||||
|
DoSaveState(resume_file_name.c_str(), -1, true, false, [](const std::string& error) {
|
||||||
|
Host::AddIconOSDMessage("SaveResumeState", ICON_FA_TRIANGLE_EXCLAMATION,
|
||||||
|
fmt::format(TRANSLATE_FS("VMManager", "Failed to save resume state: {}"), error), Host::OSD_QUICK_DURATION);
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// end input recording before clearing state
|
// end input recording before clearing state
|
||||||
@ -1829,7 +1834,7 @@ bool VMManager::DoLoadState(const char* filename, Error* error)
|
|||||||
{
|
{
|
||||||
if (GSDumpReplayer::IsReplayingDump())
|
if (GSDumpReplayer::IsReplayingDump())
|
||||||
{
|
{
|
||||||
Error::SetString(error, TRANSLATE_STR("VMManager", "Cannot load save state while replaying GS dump."));
|
Error::SetString(error, TRANSLATE_STR("VMManager", "Cannot load state while replaying a GS dump."));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1849,21 +1854,20 @@ bool VMManager::DoLoadState(const char* filename, Error* error)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool VMManager::DoSaveState(const char* filename, s32 slot_for_message, bool zip_on_thread, bool backup_old_state)
|
void VMManager::DoSaveState(const char* filename, s32 slot_for_message, bool zip_on_thread, bool backup_old_state, std::function<void(const std::string&)> error_callback)
|
||||||
{
|
{
|
||||||
if (GSDumpReplayer::IsReplayingDump())
|
if (GSDumpReplayer::IsReplayingDump())
|
||||||
return false;
|
{
|
||||||
|
error_callback(TRANSLATE_STR("VMManager", "Cannot save state while replaying a GS dump."));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
std::string osd_key(fmt::format("SaveStateSlot{}", slot_for_message));
|
|
||||||
Error error;
|
Error error;
|
||||||
|
|
||||||
std::unique_ptr<ArchiveEntryList> elist = SaveState_DownloadState(&error);
|
std::unique_ptr<ArchiveEntryList> elist = SaveState_DownloadState(&error);
|
||||||
if (!elist)
|
if (!elist)
|
||||||
{
|
{
|
||||||
Host::AddIconOSDMessage(std::move(osd_key), ICON_FA_TRIANGLE_EXCLAMATION,
|
error_callback(error.GetDescription());
|
||||||
fmt::format(TRANSLATE_FS("VMManager", "Failed to save state: {}."), error.GetDescription()),
|
return;
|
||||||
Host::OSD_ERROR_DURATION);
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<SaveStateScreenshotData> screenshot = SaveState_SaveScreenshot();
|
std::unique_ptr<SaveStateScreenshotData> screenshot = SaveState_SaveScreenshot();
|
||||||
@ -1874,10 +1878,10 @@ bool VMManager::DoSaveState(const char* filename, s32 slot_for_message, bool zip
|
|||||||
Console.WriteLn(fmt::format("Creating save state backup {}...", backup_filename));
|
Console.WriteLn(fmt::format("Creating save state backup {}...", backup_filename));
|
||||||
if (!FileSystem::RenamePath(filename, backup_filename.c_str()))
|
if (!FileSystem::RenamePath(filename, backup_filename.c_str()))
|
||||||
{
|
{
|
||||||
Host::AddIconOSDMessage(osd_key, ICON_FA_TRIANGLE_EXCLAMATION,
|
error_callback(fmt::format(
|
||||||
fmt::format(
|
TRANSLATE_FS("VMManager", "Cannot back up old save state '{}'."),
|
||||||
TRANSLATE_FS("VMManager", "Failed to back up old save state {}."), Path::GetFileName(filename)),
|
Path::GetFileName(filename)));
|
||||||
Host::OSD_ERROR_DURATION);
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1886,48 +1890,48 @@ bool VMManager::DoSaveState(const char* filename, s32 slot_for_message, bool zip
|
|||||||
// lock order here is important; the thread could exit before we resume here.
|
// lock order here is important; the thread could exit before we resume here.
|
||||||
std::unique_lock lock(s_save_state_threads_mutex);
|
std::unique_lock lock(s_save_state_threads_mutex);
|
||||||
s_save_state_threads.emplace_back(&VMManager::ZipSaveStateOnThread, std::move(elist), std::move(screenshot),
|
s_save_state_threads.emplace_back(&VMManager::ZipSaveStateOnThread, std::move(elist), std::move(screenshot),
|
||||||
std::move(osd_key), std::string(filename), slot_for_message);
|
std::string(filename), slot_for_message, std::move(error_callback));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
ZipSaveState(std::move(elist), std::move(screenshot), std::move(osd_key), filename, slot_for_message);
|
ZipSaveState(
|
||||||
|
std::move(elist), std::move(screenshot), filename, slot_for_message, std::move(error_callback));
|
||||||
}
|
}
|
||||||
|
|
||||||
Host::OnSaveStateSaved(filename);
|
Host::OnSaveStateSaved(filename);
|
||||||
MemcardBusy::CheckSaveStateDependency();
|
MemcardBusy::CheckSaveStateDependency();
|
||||||
return true;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
void VMManager::ZipSaveState(std::unique_ptr<ArchiveEntryList> elist,
|
void VMManager::ZipSaveState(std::unique_ptr<ArchiveEntryList> elist,
|
||||||
std::unique_ptr<SaveStateScreenshotData> screenshot, std::string osd_key, const char* filename,
|
std::unique_ptr<SaveStateScreenshotData> screenshot, const char* filename,
|
||||||
s32 slot_for_message)
|
s32 slot_for_message, std::function<void(const std::string&)> error_callback)
|
||||||
{
|
{
|
||||||
Common::Timer timer;
|
Common::Timer timer;
|
||||||
|
|
||||||
if (SaveState_ZipToDisk(std::move(elist), std::move(screenshot), filename))
|
Error error;
|
||||||
|
if (!SaveState_ZipToDisk(std::move(elist), std::move(screenshot), filename, &error))
|
||||||
{
|
{
|
||||||
if (slot_for_message >= 0 && VMManager::HasValidVM())
|
error_callback(error.GetDescription());
|
||||||
{
|
return;
|
||||||
Host::AddIconOSDMessage(std::move(osd_key), ICON_FA_FLOPPY_DISK,
|
|
||||||
fmt::format(TRANSLATE_FS("VMManager", "State saved to slot {}."), slot_for_message),
|
|
||||||
Host::OSD_QUICK_DURATION);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else
|
|
||||||
|
if (slot_for_message >= 0 && VMManager::HasValidVM())
|
||||||
{
|
{
|
||||||
Host::AddIconOSDMessage(std::move(osd_key), ICON_FA_TRIANGLE_EXCLAMATION,
|
Host::AddIconOSDMessage(fmt::format("SaveStateSlot{}", slot_for_message), ICON_FA_FLOPPY_DISK,
|
||||||
fmt::format(TRANSLATE_FS("VMManager", "Failed to save state to slot {}."), slot_for_message,
|
fmt::format(TRANSLATE_FS("VMManager", "Saved state to slot {}."), slot_for_message),
|
||||||
Host::OSD_ERROR_DURATION));
|
Host::OSD_QUICK_DURATION);
|
||||||
}
|
}
|
||||||
|
|
||||||
DevCon.WriteLn("Zipping save state to '%s' took %.2f ms", filename, timer.GetTimeMilliseconds());
|
DevCon.WriteLn("Zipping save state to '%s' took %.2f ms", filename, timer.GetTimeMilliseconds());
|
||||||
}
|
}
|
||||||
|
|
||||||
void VMManager::ZipSaveStateOnThread(std::unique_ptr<ArchiveEntryList> elist,
|
void VMManager::ZipSaveStateOnThread(std::unique_ptr<ArchiveEntryList> elist,
|
||||||
std::unique_ptr<SaveStateScreenshotData> screenshot, std::string osd_key, std::string filename,
|
std::unique_ptr<SaveStateScreenshotData> screenshot, std::string filename,
|
||||||
s32 slot_for_message)
|
s32 slot_for_message, std::function<void(const std::string&)> error_callback)
|
||||||
{
|
{
|
||||||
ZipSaveState(std::move(elist), std::move(screenshot), std::move(osd_key), filename.c_str(), slot_for_message);
|
ZipSaveState(
|
||||||
|
std::move(elist), std::move(screenshot), filename.c_str(), slot_for_message, std::move(error_callback));
|
||||||
|
|
||||||
// remove ourselves from the thread list. if we're joining, we might not be in there.
|
// remove ourselves from the thread list. if we're joining, we might not be in there.
|
||||||
const auto this_id = std::this_thread::get_id();
|
const auto this_id = std::this_thread::get_id();
|
||||||
@ -1985,23 +1989,25 @@ bool VMManager::LoadState(const char* filename, Error* error)
|
|||||||
if (Achievements::IsHardcoreModeActive())
|
if (Achievements::IsHardcoreModeActive())
|
||||||
{
|
{
|
||||||
Error::SetString(error,
|
Error::SetString(error,
|
||||||
TRANSLATE_STR("VMManager", "Cannot load save state while RetroAchievements Hardcore Mode is active."));
|
TRANSLATE_STR("VMManager", "Cannot load state while RetroAchievements Hardcore Mode is active."));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (MemcardBusy::IsBusy())
|
if (MemcardBusy::IsBusy())
|
||||||
{
|
{
|
||||||
Error::SetString(error,
|
Error::SetString(error,
|
||||||
TRANSLATE_STR("VMManager", "Memory card is busy."));
|
TRANSLATE_STR("VMManager", "The memory card is busy, so the state load operation has been cancelled to prevent data loss."));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Save the current state so we don't need to reset.
|
// TODO: Save the current state so we don't need to reset.
|
||||||
if (DoLoadState(filename, error))
|
if (!DoLoadState(filename, error))
|
||||||
return true;
|
{
|
||||||
|
Reset();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
Reset();
|
return true;
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool VMManager::LoadStateFromSlot(s32 slot, bool backup, Error* error)
|
bool VMManager::LoadStateFromSlot(s32 slot, bool backup, Error* error)
|
||||||
@ -2009,74 +2015,84 @@ bool VMManager::LoadStateFromSlot(s32 slot, bool backup, Error* error)
|
|||||||
const std::string filename = GetCurrentSaveStateFileName(slot, backup);
|
const std::string filename = GetCurrentSaveStateFileName(slot, backup);
|
||||||
if (filename.empty() || !FileSystem::FileExists(filename.c_str()))
|
if (filename.empty() || !FileSystem::FileExists(filename.c_str()))
|
||||||
{
|
{
|
||||||
if (backup)
|
Error::SetString(error, TRANSLATE_STR("VMManager", "The save slot is empty."));
|
||||||
Error::SetStringFmt(error,
|
|
||||||
TRANSLATE_FS("VMManager", "There is no save state in backup slot {}."), slot);
|
|
||||||
else
|
|
||||||
Error::SetStringFmt(error,
|
|
||||||
TRANSLATE_FS("VMManager", "There is no save state in slot {}."), slot);
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Achievements::IsHardcoreModeActive())
|
if (Achievements::IsHardcoreModeActive())
|
||||||
{
|
{
|
||||||
if (backup)
|
Error::SetString(error,
|
||||||
Error::SetStringFmt(error,
|
TRANSLATE_STR("VMManager", "Cannot load state while RetroAchievements Hardcore Mode is active."));
|
||||||
TRANSLATE_FS("VMManager", "Cannot load save state from backup slot {} while RetroAchievements Hardcore Mode is active."), slot);
|
|
||||||
else
|
|
||||||
Error::SetStringFmt(error,
|
|
||||||
TRANSLATE_FS("VMManager", "Cannot load save state from slot {} while RetroAchievements Hardcore Mode is active."), slot);
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (MemcardBusy::IsBusy())
|
if (MemcardBusy::IsBusy())
|
||||||
{
|
{
|
||||||
if (backup)
|
Error::SetString(error,
|
||||||
Error::SetStringFmt(error,
|
TRANSLATE_STR("VMManager",
|
||||||
TRANSLATE_FS("VMManager", "Failed to load save state from backup slot {} (memory card is busy)."), slot);
|
"The memory card is busy, so the state load operation has been cancelled to prevent data loss."));
|
||||||
else
|
|
||||||
Error::SetStringFmt(error,
|
|
||||||
TRANSLATE_FS("VMManager", "Failed to load save state from slot {} (memory card is busy)."), slot);
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
Host::AddIconOSDMessage("LoadStateFromSlot", ICON_FA_FOLDER_OPEN,
|
if (!DoLoadState(filename.c_str(), error))
|
||||||
fmt::format(TRANSLATE_FS("VMManager", "Loading {} from slot {}..."), backup ? TRANSLATE("VMManager", "backup state") : TRANSLATE("VMManager", "state"), slot), Host::OSD_QUICK_DURATION);
|
return false;
|
||||||
|
|
||||||
return DoLoadState(filename.c_str(), error);
|
if (backup)
|
||||||
|
{
|
||||||
|
Host::AddIconOSDMessage("LoadStateFromSlot", ICON_FA_FOLDER_OPEN,
|
||||||
|
fmt::format(TRANSLATE_FS("VMManager", "Loaded state from backup slot {}."), slot),
|
||||||
|
Host::OSD_QUICK_DURATION);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Host::AddIconOSDMessage("LoadStateFromSlot", ICON_FA_FOLDER_OPEN,
|
||||||
|
fmt::format(TRANSLATE_FS("VMManager", "Loaded state from slot {}."), slot),
|
||||||
|
Host::OSD_QUICK_DURATION);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool VMManager::SaveState(const char* filename, bool zip_on_thread, bool backup_old_state)
|
void VMManager::SaveState(
|
||||||
|
const char* filename, bool zip_on_thread, bool backup_old_state, std::function<void(const std::string&)> error_callback)
|
||||||
{
|
{
|
||||||
if (MemcardBusy::IsBusy())
|
if (MemcardBusy::IsBusy())
|
||||||
{
|
{
|
||||||
Host::AddIconOSDMessage("LoadStateFromSlot", ICON_FA_TRIANGLE_EXCLAMATION,
|
error_callback(TRANSLATE_STR("VMManager",
|
||||||
fmt::format(TRANSLATE_FS("VMManager", "Failed to save state (Memory card is busy)")),
|
"The memory card is busy, so the state save operation has been cancelled to prevent data loss."));
|
||||||
Host::OSD_QUICK_DURATION);
|
return;
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return DoSaveState(filename, -1, zip_on_thread, backup_old_state);
|
DoSaveState(filename, -1, zip_on_thread, backup_old_state, std::move(error_callback));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool VMManager::SaveStateToSlot(s32 slot, bool zip_on_thread)
|
void VMManager::SaveStateToSlot(s32 slot, bool zip_on_thread, std::function<void(const std::string&)> error_callback)
|
||||||
{
|
{
|
||||||
const std::string filename(GetCurrentSaveStateFileName(slot));
|
const std::string filename(GetCurrentSaveStateFileName(slot));
|
||||||
if (filename.empty())
|
if (filename.empty())
|
||||||
return false;
|
{
|
||||||
|
error_callback(TRANSLATE_STR("VMManager", "Cannot generate filename for save state."));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (MemcardBusy::IsBusy())
|
if (MemcardBusy::IsBusy())
|
||||||
{
|
{
|
||||||
Host::AddIconOSDMessage("LoadStateFromSlot", ICON_FA_TRIANGLE_EXCLAMATION,
|
error_callback(TRANSLATE_STR("VMManager",
|
||||||
fmt::format(TRANSLATE_FS("VMManager", "Failed to save state to slot {} (Memory card is busy)"), slot),
|
"The memory card is busy, so the state save operation has been cancelled to prevent data loss."));
|
||||||
Host::OSD_QUICK_DURATION);
|
return;
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// if it takes more than a minute.. well.. wtf.
|
// if it takes more than a minute.. well.. wtf.
|
||||||
Host::AddIconOSDMessage(fmt::format("SaveStateSlot{}", slot), ICON_FA_FLOPPY_DISK,
|
Host::AddIconOSDMessage(fmt::format("SaveStateSlot{}", slot), ICON_FA_FLOPPY_DISK,
|
||||||
fmt::format(TRANSLATE_FS("VMManager", "Saving state to slot {}..."), slot), 60.0f);
|
fmt::format(TRANSLATE_FS("VMManager", "Saving state to slot {}..."), slot), 60.0f);
|
||||||
return DoSaveState(filename.c_str(), slot, zip_on_thread, EmuConfig.BackupSavestate);
|
|
||||||
|
auto callback = [error_callback = std::move(error_callback), slot](const std::string& error) {
|
||||||
|
Host::RemoveKeyedOSDMessage(fmt::format("SaveStateSlot{}", slot));
|
||||||
|
error_callback(error);
|
||||||
|
};
|
||||||
|
|
||||||
|
return DoSaveState(
|
||||||
|
filename.c_str(), slot, zip_on_thread, EmuConfig.BackupSavestate, std::move(callback));
|
||||||
}
|
}
|
||||||
|
|
||||||
LimiterModeType VMManager::GetLimiterMode()
|
LimiterModeType VMManager::GetLimiterMode()
|
||||||
@ -2816,8 +2832,8 @@ void VMManager::Internal::EntryPointCompilingOnCPUThread()
|
|||||||
|
|
||||||
HandleELFChange(true);
|
HandleELFChange(true);
|
||||||
|
|
||||||
Patch::ApplyLoadedPatches(Patch::PPT_ONCE_ON_LOAD);
|
Patch::ApplyBootPatches();
|
||||||
Patch::ApplyLoadedPatches(Patch::PPT_COMBINED_0_1);
|
|
||||||
// If the config changes at this point, it's a reset, so the game doesn't currently know about the memcard
|
// If the config changes at this point, it's a reset, so the game doesn't currently know about the memcard
|
||||||
// so there's no need to leave the eject running.
|
// so there's no need to leave the eject running.
|
||||||
FileMcd_CancelEject();
|
FileMcd_CancelEject();
|
||||||
@ -2833,8 +2849,7 @@ void VMManager::Internal::VSyncOnCPUThread()
|
|||||||
{
|
{
|
||||||
Pad::UpdateMacroButtons();
|
Pad::UpdateMacroButtons();
|
||||||
|
|
||||||
Patch::ApplyLoadedPatches(Patch::PPT_CONTINUOUSLY);
|
Patch::ApplyVsyncPatches();
|
||||||
Patch::ApplyLoadedPatches(Patch::PPT_COMBINED_0_1);
|
|
||||||
|
|
||||||
// Frame advance must be done *before* pumping messages, because otherwise
|
// Frame advance must be done *before* pumping messages, because otherwise
|
||||||
// we'll immediately reduce the counter we just set.
|
// we'll immediately reduce the counter we just set.
|
||||||
|
|||||||
@ -165,10 +165,11 @@ namespace VMManager
|
|||||||
bool LoadStateFromSlot(s32 slot, bool backup = false, Error* error = nullptr);
|
bool LoadStateFromSlot(s32 slot, bool backup = false, Error* error = nullptr);
|
||||||
|
|
||||||
/// Saves state to the specified filename.
|
/// Saves state to the specified filename.
|
||||||
bool SaveState(const char* filename, bool zip_on_thread = true, bool backup_old_state = false);
|
void SaveState(const char* filename, bool zip_on_thread, bool backup_old_state,
|
||||||
|
std::function<void(const std::string&)> error_callback);
|
||||||
|
|
||||||
/// Saves state to the specified slot.
|
/// Saves state to the specified slot.
|
||||||
bool SaveStateToSlot(s32 slot, bool zip_on_thread = true);
|
void SaveStateToSlot(s32 slot, bool zip_on_thread, std::function<void(const std::string&)> error_callback);
|
||||||
|
|
||||||
/// Waits until all compressing save states have finished saving to disk.
|
/// Waits until all compressing save states have finished saving to disk.
|
||||||
void WaitForSaveStateFlush();
|
void WaitForSaveStateFlush();
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user