mirror of
https://github.com/PCSX2/pcsx2.git
synced 2025-12-16 04:08:48 +00:00
Compare commits
40 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
748f232976 | ||
|
|
a33612cf7d | ||
|
|
5c123f3183 | ||
|
|
d30a7fb991 | ||
|
|
d4f661c27c | ||
|
|
6d05d0220d | ||
|
|
7fab935c2d | ||
|
|
cd120c3cfd | ||
|
|
0180ec060b | ||
|
|
cf4412ecbe | ||
|
|
743b0b11d8 | ||
|
|
e8c2cfa843 | ||
|
|
764875ddbf | ||
|
|
92c7eaa383 | ||
|
|
4d2c1a82c9 | ||
|
|
eb50aaea35 | ||
|
|
d69c71e058 | ||
|
|
7cc8c7eee6 | ||
|
|
dd96f2c296 | ||
|
|
3cb2f3d2d9 | ||
|
|
1f9a9940e9 | ||
|
|
8164f2b2db | ||
|
|
d0c54de330 | ||
|
|
af5cd8d48e | ||
|
|
a43f051852 | ||
|
|
0f8c5066e3 | ||
|
|
1bf1f458d0 | ||
|
|
5ef1993496 | ||
|
|
80baa73578 | ||
|
|
b46c85ee7f | ||
|
|
7f233ca620 | ||
|
|
47fe2344a5 | ||
|
|
4e763d6ff7 | ||
|
|
e1cc994cca | ||
|
|
5b7f85e571 | ||
|
|
7475cfb325 | ||
|
|
cee01a22e1 | ||
|
|
c6437bccad | ||
|
|
7419311296 | ||
|
|
2807a06d76 |
2
.github/ISSUE_TEMPLATE/app_bug_report.yaml
vendored
2
.github/ISSUE_TEMPLATE/app_bug_report.yaml
vendored
@ -59,7 +59,7 @@ body:
|
||||
attributes:
|
||||
label: PCSX2 Revision
|
||||
description: "Please ensure you are on the latest version before making an issue"
|
||||
placeholder: "Example: v1.7.1337"
|
||||
placeholder: "Example: v2.5.374"
|
||||
validations:
|
||||
required: true
|
||||
- type: dropdown
|
||||
|
||||
2
.github/ISSUE_TEMPLATE/emu_bug_report.yaml
vendored
2
.github/ISSUE_TEMPLATE/emu_bug_report.yaml
vendored
@ -76,7 +76,7 @@ body:
|
||||
attributes:
|
||||
label: PCSX2 Revision
|
||||
description: "We only accept bug reports for the latest dev version. Please try upgrading before making an issue."
|
||||
placeholder: "Example: v1.7.1337"
|
||||
placeholder: "Example: v2.5.374"
|
||||
validations:
|
||||
required: true
|
||||
- type: dropdown
|
||||
|
||||
@ -17,7 +17,7 @@ jobs:
|
||||
run: ./.github/workflows/scripts/common/update_base_translation.sh
|
||||
|
||||
- name: Create Pull Request
|
||||
uses: peter-evans/create-pull-request@22a9089034f40e5a961c8808d113e2c98fb63676
|
||||
uses: peter-evans/create-pull-request@98357b18bf14b5342f975ff684046ec3b2a07725
|
||||
with:
|
||||
title: "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
|
||||
|
||||
- name: Create Pull Request
|
||||
uses: peter-evans/create-pull-request@22a9089034f40e5a961c8808d113e2c98fb63676
|
||||
uses: peter-evans/create-pull-request@98357b18bf14b5342f975ff684046ec3b2a07725
|
||||
with:
|
||||
title: "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/
|
||||
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-artifact@v5
|
||||
uses: actions/upload-artifact@v6
|
||||
with:
|
||||
name: ${{ steps.artifact-metadata.outputs.artifact-name }}
|
||||
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
|
||||
|
||||
- name: ccache cache files
|
||||
uses: actions/cache@v4
|
||||
uses: actions/cache@v5
|
||||
with:
|
||||
path: .ccache
|
||||
key: ${{ inputs.os }} ${{ inputs.platform }} ${{ inputs.compiler }} ${{ inputs.detail }} ccache ${{ steps.ccache_cache_timestamp.outputs.timestamp }}
|
||||
@ -114,7 +114,7 @@ jobs:
|
||||
|
||||
- name: Cache Dependencies
|
||||
id: cache-deps
|
||||
uses: actions/cache@v4
|
||||
uses: actions/cache@v5
|
||||
with:
|
||||
path: ~/deps
|
||||
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
|
||||
if: inputs.buildAppImage == true
|
||||
uses: actions/upload-artifact@v5
|
||||
uses: actions/upload-artifact@v6
|
||||
with:
|
||||
name: ${{ steps.artifact-metadata.outputs.artifact-name }}
|
||||
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
|
||||
id: cache-deps
|
||||
uses: actions/cache@v4
|
||||
uses: actions/cache@v5
|
||||
with:
|
||||
path: ~/deps
|
||||
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
|
||||
|
||||
- name: Cache ccache cache
|
||||
uses: actions/cache@v4
|
||||
uses: actions/cache@v5
|
||||
with:
|
||||
path: .ccache
|
||||
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
|
||||
|
||||
- name: Upload Artifact
|
||||
uses: actions/upload-artifact@v5
|
||||
uses: actions/upload-artifact@v6
|
||||
with:
|
||||
name: ${{ steps.artifact-metadata.outputs.artifact-name }}
|
||||
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
|
||||
run: mkdir ./ci-artifacts/
|
||||
|
||||
- uses: actions/download-artifact@v6
|
||||
- uses: actions/download-artifact@v7
|
||||
name: Download all Artifacts
|
||||
with:
|
||||
path: ./ci-artifacts/
|
||||
|
||||
@ -23,7 +23,7 @@ FREETYPE=2.14.1
|
||||
HARFBUZZ=12.2.0
|
||||
LIBBACKTRACE=ad106d5fdd5d960bd33fae1c48a351af567fd075
|
||||
LIBJPEGTURBO=3.1.2
|
||||
LIBPNG=1.6.51
|
||||
LIBPNG=1.6.53
|
||||
LIBWEBP=1.6.0
|
||||
NVENC=11.1.5.3
|
||||
SDL=SDL3-3.2.26
|
||||
@ -52,10 +52,10 @@ b2751fccb6cc4c77708113cd78b561059b6fa904b24162fa0be2d60273d27b8e ffmpeg-$FFMPEG
|
||||
f63fc519f150465bd0bdafcdf3d0e9c23474f4c474171cd515ea1b3a72c081fb harfbuzz-$HARFBUZZ.tar.gz
|
||||
fd6f417fe9e3a071cf1424a5152d926a34c4a3c5070745470be6cf12a404ed79 $LIBBACKTRACE.zip
|
||||
8f0012234b464ce50890c490f18194f913a7b1f4e6a03d6644179fa0f867d0cf libjpeg-turbo-$LIBJPEGTURBO.tar.gz
|
||||
a050a892d3b4a7bb010c3a95c7301e49656d72a64f1fc709a90b8aded192bed2 libpng-$LIBPNG.tar.xz
|
||||
1d3fb8ccc2932d04aa3663e22ef5ef490244370f4e568d7850165068778d98d4 libpng-$LIBPNG.tar.xz
|
||||
e4ab7009bf0629fd11982d4c2aa83964cf244cffba7347ecd39019a9e38c4564 libwebp-$LIBWEBP.tar.gz
|
||||
dad488474a51a0b01d547cd2834893d6299328d2e30f479a3564088b5476bae2 $SDL.tar.gz
|
||||
9c16ec5654be709f062a705d0c6f529193f1c2123fe7f102fda6733913689023 libpng-$LIBPNG-apng.patch.gz
|
||||
452a1a290bd0cf18737fad0057dc17b7fdf10a73eda2d6d4f31ba04fda25ef2c libpng-$LIBPNG-apng.patch.gz
|
||||
537512904744b35e232912055ccf8ec66d768639ff3abe5788d90d792ec5f48b lz4-$LZ4.tar.gz
|
||||
2974b91062197e0527dffa3aadd8fe3bfa6681ae45f5ff9181bc0ca6479abd59 nv-codec-headers-$NVENC.tar.gz
|
||||
c465aa56757e7746ac707f582b6e2d51546569a4a2488c1172fb543aa5fdfc2c vulkan-sdk-$VULKAN.tar.gz
|
||||
|
||||
@ -14,21 +14,21 @@
|
||||
"sources": [
|
||||
{
|
||||
"type": "archive",
|
||||
"url": "https://downloads.sourceforge.net/project/libpng/libpng16/1.6.51/libpng-1.6.51.tar.xz",
|
||||
"sha256": "a050a892d3b4a7bb010c3a95c7301e49656d72a64f1fc709a90b8aded192bed2"
|
||||
"url": "https://downloads.sourceforge.net/project/libpng/libpng16/1.6.53/libpng-1.6.53.tar.xz",
|
||||
"sha256": "1d3fb8ccc2932d04aa3663e22ef5ef490244370f4e568d7850165068778d98d4"
|
||||
},
|
||||
{
|
||||
"type": "file",
|
||||
"url": "https://download.sourceforge.net/libpng-apng/libpng-1.6.51-apng.patch.gz",
|
||||
"dest-filename": "libpng-1.6.51-apng.patch.gz",
|
||||
"sha256": "9c16ec5654be709f062a705d0c6f529193f1c2123fe7f102fda6733913689023"
|
||||
"url": "https://download.sourceforge.net/libpng-apng/libpng-1.6.53-apng.patch.gz",
|
||||
"dest-filename": "libpng-1.6.53-apng.patch.gz",
|
||||
"sha256": "452a1a290bd0cf18737fad0057dc17b7fdf10a73eda2d6d4f31ba04fda25ef2c"
|
||||
},
|
||||
{
|
||||
"type": "shell",
|
||||
"commands":
|
||||
[
|
||||
"gunzip -f libpng-1.6.51-apng.patch.gz",
|
||||
"patch -p1 < \"libpng-1.6.51-apng.patch\""
|
||||
"gunzip -f libpng-1.6.53-apng.patch.gz",
|
||||
"patch -p1 < \"libpng-1.6.53-apng.patch\""
|
||||
]
|
||||
}
|
||||
],
|
||||
|
||||
@ -43,7 +43,7 @@ HARFBUZZ=12.2.0
|
||||
SDL=SDL3-3.2.26
|
||||
ZSTD=1.5.7
|
||||
LZ4=1.10.0
|
||||
LIBPNG=1.6.51
|
||||
LIBPNG=1.6.53
|
||||
LIBJPEGTURBO=3.1.2
|
||||
LIBWEBP=1.6.0
|
||||
FFMPEG=8.0
|
||||
@ -83,9 +83,9 @@ f63fc519f150465bd0bdafcdf3d0e9c23474f4c474171cd515ea1b3a72c081fb harfbuzz-$HARF
|
||||
dad488474a51a0b01d547cd2834893d6299328d2e30f479a3564088b5476bae2 $SDL.tar.gz
|
||||
eb33e51f49a15e023950cd7825ca74a4a2b43db8354825ac24fc1b7ee09e6fa3 zstd-$ZSTD.tar.gz
|
||||
537512904744b35e232912055ccf8ec66d768639ff3abe5788d90d792ec5f48b lz4-$LZ4.tar.gz
|
||||
a050a892d3b4a7bb010c3a95c7301e49656d72a64f1fc709a90b8aded192bed2 libpng-$LIBPNG.tar.xz
|
||||
1d3fb8ccc2932d04aa3663e22ef5ef490244370f4e568d7850165068778d98d4 libpng-$LIBPNG.tar.xz
|
||||
e4ab7009bf0629fd11982d4c2aa83964cf244cffba7347ecd39019a9e38c4564 libwebp-$LIBWEBP.tar.gz
|
||||
9c16ec5654be709f062a705d0c6f529193f1c2123fe7f102fda6733913689023 libpng-$LIBPNG-apng.patch.gz
|
||||
452a1a290bd0cf18737fad0057dc17b7fdf10a73eda2d6d4f31ba04fda25ef2c libpng-$LIBPNG-apng.patch.gz
|
||||
8f0012234b464ce50890c490f18194f913a7b1f4e6a03d6644179fa0f867d0cf libjpeg-turbo-$LIBJPEGTURBO.tar.gz
|
||||
b2751fccb6cc4c77708113cd78b561059b6fa904b24162fa0be2d60273d27b8e ffmpeg-$FFMPEG.tar.xz
|
||||
f415a09385030c6510a936155ce211f617c31506db5fbc563e804345f1ecf56e v$MOLTENVK.tar.gz
|
||||
|
||||
@ -25,7 +25,7 @@ HARFBUZZ=12.2.0
|
||||
SDL=SDL3-3.2.26
|
||||
ZSTD=1.5.7
|
||||
LZ4=1.10.0
|
||||
LIBPNG=1.6.51
|
||||
LIBPNG=1.6.53
|
||||
LIBJPEGTURBO=3.1.2
|
||||
LIBWEBP=1.6.0
|
||||
FFMPEG=8.0
|
||||
@ -64,9 +64,9 @@ f63fc519f150465bd0bdafcdf3d0e9c23474f4c474171cd515ea1b3a72c081fb harfbuzz-$HARF
|
||||
dad488474a51a0b01d547cd2834893d6299328d2e30f479a3564088b5476bae2 $SDL.tar.gz
|
||||
eb33e51f49a15e023950cd7825ca74a4a2b43db8354825ac24fc1b7ee09e6fa3 zstd-$ZSTD.tar.gz
|
||||
537512904744b35e232912055ccf8ec66d768639ff3abe5788d90d792ec5f48b lz4-$LZ4.tar.gz
|
||||
a050a892d3b4a7bb010c3a95c7301e49656d72a64f1fc709a90b8aded192bed2 libpng-$LIBPNG.tar.xz
|
||||
1d3fb8ccc2932d04aa3663e22ef5ef490244370f4e568d7850165068778d98d4 libpng-$LIBPNG.tar.xz
|
||||
e4ab7009bf0629fd11982d4c2aa83964cf244cffba7347ecd39019a9e38c4564 libwebp-$LIBWEBP.tar.gz
|
||||
9c16ec5654be709f062a705d0c6f529193f1c2123fe7f102fda6733913689023 libpng-$LIBPNG-apng.patch.gz
|
||||
452a1a290bd0cf18737fad0057dc17b7fdf10a73eda2d6d4f31ba04fda25ef2c libpng-$LIBPNG-apng.patch.gz
|
||||
8f0012234b464ce50890c490f18194f913a7b1f4e6a03d6644179fa0f867d0cf libjpeg-turbo-$LIBJPEGTURBO.tar.gz
|
||||
b2751fccb6cc4c77708113cd78b561059b6fa904b24162fa0be2d60273d27b8e ffmpeg-$FFMPEG.tar.xz
|
||||
f415a09385030c6510a936155ce211f617c31506db5fbc563e804345f1ecf56e v$MOLTENVK.tar.gz
|
||||
|
||||
@ -45,8 +45,8 @@ cd "%BUILDDIR%"
|
||||
set FREETYPE=2.14.1
|
||||
set HARFBUZZ=12.2.0
|
||||
set LIBJPEGTURBO=3.1.2
|
||||
set LIBPNG=1651
|
||||
set LIBPNGLONG=1.6.51
|
||||
set LIBPNG=1653
|
||||
set LIBPNGLONG=1.6.53
|
||||
set SDL=SDL3-3.2.26
|
||||
set QT=6.10.1
|
||||
set QTMINOR=6.10
|
||||
@ -67,8 +67,8 @@ set SHADERC_SPIRVTOOLS=971a7b6e8d7740035bbff089bbbf9f42951ecfd5
|
||||
|
||||
call :downloadfile "freetype-%FREETYPE%.tar.gz" https://sourceforge.net/projects/freetype/files/freetype2/%FREETYPE%/freetype-%FREETYPE%.tar.gz/download 174d9e53402e1bf9ec7277e22ec199ba3e55a6be2c0740cb18c0ee9850fc8c34 || goto error
|
||||
call :downloadfile "harfbuzz-%HARFBUZZ%.zip" https://github.com/harfbuzz/harfbuzz/archive/refs/tags/%HARFBUZZ%.zip 31490c781bacd2ce56862555b11c51c964977c39f14f51b817dfaecf0be089fe || goto error
|
||||
call :downloadfile "lpng%LIBPNG%.zip" https://download.sourceforge.net/libpng/lpng1651.zip 31c2c6505fc1bb613574fd12357684b4e0292650607416ef1e68e6e4e0c470c8 || goto error
|
||||
call :downloadfile "lpng%LIBPNG%-apng.patch.gz" https://download.sourceforge.net/libpng-apng/libpng-%LIBPNGLONG%-apng.patch.gz 9c16ec5654be709f062a705d0c6f529193f1c2123fe7f102fda6733913689023 || goto error
|
||||
call :downloadfile "lpng%LIBPNG%.zip" https://download.sourceforge.net/libpng/lpng1653.zip 140566abc64bb2320cb35f1d154d1cb3eb7174a12234d33bfdffb446bdc0a1d2 || goto error
|
||||
call :downloadfile "lpng%LIBPNG%-apng.patch.gz" https://download.sourceforge.net/libpng-apng/libpng-%LIBPNGLONG%-apng.patch.gz 452a1a290bd0cf18737fad0057dc17b7fdf10a73eda2d6d4f31ba04fda25ef2c || goto error
|
||||
call :downloadfile "libjpeg-turbo-%LIBJPEGTURBO%.tar.gz" "https://github.com/libjpeg-turbo/libjpeg-turbo/releases/download/%LIBJPEGTURBO%/libjpeg-turbo-%LIBJPEGTURBO%.tar.gz" 8f0012234b464ce50890c490f18194f913a7b1f4e6a03d6644179fa0f867d0cf || goto error
|
||||
call :downloadfile "libwebp-%WEBP%.tar.gz" "https://storage.googleapis.com/downloads.webmproject.org/releases/webp/libwebp-%WEBP%.tar.gz" e4ab7009bf0629fd11982d4c2aa83964cf244cffba7347ecd39019a9e38c4564 || goto error
|
||||
call :downloadfile "%SDL%.zip" "https://libsdl.org/release/%SDL%.zip" 739356eef1192fff9d641c320a8f5ef4a10506b8927def4b9ceb764c7e947369 || goto error
|
||||
|
||||
@ -43,8 +43,8 @@ cd "%BUILDDIR%"
|
||||
set FREETYPE=2.14.1
|
||||
set HARFBUZZ=12.2.0
|
||||
set LIBJPEGTURBO=3.1.2
|
||||
set LIBPNG=1651
|
||||
set LIBPNGLONG=1.6.51
|
||||
set LIBPNG=1653
|
||||
set LIBPNGLONG=1.6.53
|
||||
set SDL=SDL3-3.2.26
|
||||
set QT=6.10.1
|
||||
set QTMINOR=6.10
|
||||
@ -65,8 +65,8 @@ set SHADERC_SPIRVTOOLS=971a7b6e8d7740035bbff089bbbf9f42951ecfd5
|
||||
|
||||
call :downloadfile "freetype-%FREETYPE%.tar.gz" https://sourceforge.net/projects/freetype/files/freetype2/%FREETYPE%/freetype-%FREETYPE%.tar.gz/download 174d9e53402e1bf9ec7277e22ec199ba3e55a6be2c0740cb18c0ee9850fc8c34 || goto error
|
||||
call :downloadfile "harfbuzz-%HARFBUZZ%.zip" https://github.com/harfbuzz/harfbuzz/archive/refs/tags/%HARFBUZZ%.zip 31490c781bacd2ce56862555b11c51c964977c39f14f51b817dfaecf0be089fe || goto error
|
||||
call :downloadfile "lpng%LIBPNG%.zip" https://download.sourceforge.net/libpng/lpng1651.zip 31c2c6505fc1bb613574fd12357684b4e0292650607416ef1e68e6e4e0c470c8 || goto error
|
||||
call :downloadfile "lpng%LIBPNG%-apng.patch.gz" https://download.sourceforge.net/libpng-apng/libpng-%LIBPNGLONG%-apng.patch.gz 9c16ec5654be709f062a705d0c6f529193f1c2123fe7f102fda6733913689023 || goto error
|
||||
call :downloadfile "lpng%LIBPNG%.zip" https://download.sourceforge.net/libpng/lpng1653.zip 140566abc64bb2320cb35f1d154d1cb3eb7174a12234d33bfdffb446bdc0a1d2 || goto error
|
||||
call :downloadfile "lpng%LIBPNG%-apng.patch.gz" https://download.sourceforge.net/libpng-apng/libpng-%LIBPNGLONG%-apng.patch.gz 452a1a290bd0cf18737fad0057dc17b7fdf10a73eda2d6d4f31ba04fda25ef2c || goto error
|
||||
call :downloadfile "libjpeg-turbo-%LIBJPEGTURBO%.tar.gz" "https://github.com/libjpeg-turbo/libjpeg-turbo/releases/download/%LIBJPEGTURBO%/libjpeg-turbo-%LIBJPEGTURBO%.tar.gz" 8f0012234b464ce50890c490f18194f913a7b1f4e6a03d6644179fa0f867d0cf || goto error
|
||||
call :downloadfile "libwebp-%WEBP%.tar.gz" "https://storage.googleapis.com/downloads.webmproject.org/releases/webp/libwebp-%WEBP%.tar.gz" e4ab7009bf0629fd11982d4c2aa83964cf244cffba7347ecd39019a9e38c4564 || goto error
|
||||
call :downloadfile "%SDL%.zip" "https://libsdl.org/release/%SDL%.zip" 739356eef1192fff9d641c320a8f5ef4a10506b8927def4b9ceb764c7e947369 || goto error
|
||||
|
||||
6
.github/workflows/windows_build_qt.yml
vendored
6
.github/workflows/windows_build_qt.yml
vendored
@ -115,7 +115,7 @@ jobs:
|
||||
|
||||
- name: Cache Dependencies
|
||||
id: cache-deps
|
||||
uses: actions/cache@v4
|
||||
uses: actions/cache@v5
|
||||
with:
|
||||
path: deps
|
||||
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
|
||||
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-artifact@v5
|
||||
uses: actions/upload-artifact@v6
|
||||
with:
|
||||
name: ${{ steps.artifact-metadata.outputs.artifact-name }}
|
||||
path: |
|
||||
@ -186,7 +186,7 @@ jobs:
|
||||
}
|
||||
|
||||
- name: Upload artifact - with symbols
|
||||
uses: actions/upload-artifact@v5
|
||||
uses: actions/upload-artifact@v6
|
||||
with:
|
||||
name: ${{ steps.artifact-metadata.outputs.artifact-name }}-symbols
|
||||
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,
|
||||
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,
|
||||
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,
|
||||
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,
|
||||
@ -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,
|
||||
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,
|
||||
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,
|
||||
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,
|
||||
@ -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,
|
||||
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,
|
||||
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,
|
||||
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,
|
||||
@ -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,
|
||||
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,
|
||||
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,
|
||||
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,
|
||||
@ -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,
|
||||
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,
|
||||
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,
|
||||
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,
|
||||
@ -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,
|
||||
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,
|
||||
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,
|
||||
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,
|
||||
@ -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,
|
||||
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,
|
||||
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
|
||||
|
||||
#if defined(_WIN32)
|
||||
#include "RedtapeWindows.h"
|
||||
#include "common/RedtapeWindows.h"
|
||||
#include <io.h>
|
||||
#include <malloc.h>
|
||||
#include <pathcch.h>
|
||||
|
||||
@ -1148,7 +1148,7 @@ bool BMPBufferLoader(RGBA8Image* image, const void* buffer, size_t buffer_size)
|
||||
return false;
|
||||
}
|
||||
|
||||
if (width > 65536 || height > 65536)
|
||||
if (width >= 65536 || height >= 65536)
|
||||
{
|
||||
Console.Error("BMP dimensions too large: %ux%u", width, height);
|
||||
return false;
|
||||
|
||||
@ -272,7 +272,7 @@ if (NOT APPLE)
|
||||
)
|
||||
endif()
|
||||
|
||||
file(GLOB TS_FILES ${CMAKE_CURRENT_SOURCE_DIR}/Translations/*.ts)
|
||||
file(GLOB TS_FILES ${CMAKE_CURRENT_SOURCE_DIR}/Translations/pcsx2-qt_*-*.ts)
|
||||
|
||||
target_precompile_headers(pcsx2-qt PRIVATE PrecompiledHeader.h)
|
||||
set_source_files_properties(PrecompiledHeader.cpp PROPERTIES HEADER_FILE_ONLY TRUE)
|
||||
|
||||
@ -220,6 +220,8 @@ void GameListWidget::initialize()
|
||||
m_sort_model->setSourceModel(m_model);
|
||||
|
||||
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++)
|
||||
{
|
||||
@ -353,6 +355,7 @@ void GameListWidget::setCustomBackground()
|
||||
m_background_movie = nullptr;
|
||||
}
|
||||
|
||||
// Get the path to the custom background
|
||||
std::string path = Host::GetBaseStringSettingValue("UI", "GameListBackgroundPath");
|
||||
if (!Path::IsAbsolute(path))
|
||||
path = Path::Combine(EmuFolders::DataRoot, path);
|
||||
@ -360,27 +363,26 @@ void GameListWidget::setCustomBackground()
|
||||
// Only try to create background if path are valid
|
||||
if (!path.empty() && FileSystem::FileExists(path.c_str()))
|
||||
{
|
||||
QMovie* new_movie;
|
||||
QString img_path = QString::fromStdString(path);
|
||||
if (img_path.endsWith(".png", Qt::CaseInsensitive))
|
||||
// Use apng plugin
|
||||
new_movie = new QMovie(img_path, "apng", this);
|
||||
else
|
||||
new_movie = new QMovie(img_path, QByteArray(), this);
|
||||
|
||||
if (new_movie->isValid())
|
||||
m_background_movie = new_movie;
|
||||
else
|
||||
const QByteArray format = (img_path.endsWith(".png", Qt::CaseInsensitive)) ? QByteArray("apng") : QByteArray();
|
||||
m_background_movie = new QMovie(img_path, format, this);
|
||||
if (!m_background_movie->isValid())
|
||||
{
|
||||
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 (!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);
|
||||
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)]);
|
||||
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])
|
||||
{
|
||||
@ -405,8 +407,13 @@ void GameListWidget::setCustomBackground()
|
||||
|
||||
// Selected Custom background is valid, connect the signals and start animation in gamelist
|
||||
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);
|
||||
m_table_view->setAlternatingRowColors(false);
|
||||
processBackgroundFrames();
|
||||
}
|
||||
|
||||
void GameListWidget::updateCustomBackgroundState(const bool force_start)
|
||||
@ -422,7 +429,7 @@ void GameListWidget::updateCustomBackgroundState(const bool force_start)
|
||||
|
||||
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_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);
|
||||
|
||||
QPalette bg_palette(m_ui.stack->palette());
|
||||
bg_palette.setBrush(QPalette::Base, pm);
|
||||
m_ui.stack->setPalette(bg_palette);
|
||||
m_background_pixmap = std::move(pm);
|
||||
m_ui.stack->update();
|
||||
}
|
||||
}
|
||||
|
||||
@ -725,6 +731,7 @@ bool GameListWidget::event(QEvent* event)
|
||||
if (event->type() == QEvent::DevicePixelRatioChange)
|
||||
{
|
||||
m_model->setDevicePixelRatio(devicePixelRatioF());
|
||||
processBackgroundFrames();
|
||||
QWidget::event(event);
|
||||
return true;
|
||||
}
|
||||
@ -732,6 +739,25 @@ bool GameListWidget::event(QEvent* 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()
|
||||
{
|
||||
QtUtils::ResizeColumnsForTableView(m_table_view, {
|
||||
|
||||
@ -10,7 +10,9 @@
|
||||
#include "pcsx2/GameList.h"
|
||||
|
||||
#include <QtGui/QMovie>
|
||||
#include <QtGui/QPixmap>
|
||||
#include <QtWidgets/QListView>
|
||||
#include <QtWidgets/QStackedWidget>
|
||||
#include <QtWidgets/QTableView>
|
||||
|
||||
Q_DECLARE_METATYPE(const GameList::Entry*);
|
||||
@ -101,6 +103,7 @@ protected:
|
||||
void hideEvent(QHideEvent* event) override;
|
||||
void resizeEvent(QResizeEvent* event) override;
|
||||
bool event(QEvent* event) override;
|
||||
bool eventFilter(QObject* watched, QEvent* event) override;
|
||||
|
||||
private:
|
||||
void loadTableHeaderState();
|
||||
@ -123,6 +126,7 @@ private:
|
||||
GameListRefreshThread* m_refresh_thread = nullptr;
|
||||
|
||||
QMovie* m_background_movie = nullptr;
|
||||
QPixmap m_background_pixmap;
|
||||
QtUtils::ScalingMode m_background_scaling = QtUtils::ScalingMode::Fit;
|
||||
float m_background_opacity = 100.0f;
|
||||
};
|
||||
|
||||
@ -37,6 +37,7 @@
|
||||
#include "pcsx2/PerformanceMetrics.h"
|
||||
#include "pcsx2/Recording/InputRecording.h"
|
||||
#include "pcsx2/Recording/InputRecordingControls.h"
|
||||
#include "pcsx2/SaveState.h"
|
||||
#include "pcsx2/SIO/Sio.h"
|
||||
#include "pcsx2/GS/GSExtra.h"
|
||||
|
||||
@ -1192,6 +1193,12 @@ void MainWindow::cancelGameListRefresh()
|
||||
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)
|
||||
{
|
||||
QMessageBox::information(this, title, message);
|
||||
@ -1213,6 +1220,103 @@ void MainWindow::onStatusMessage(const QString& 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)
|
||||
{
|
||||
func();
|
||||
@ -1734,8 +1838,8 @@ void MainWindow::onCreateGameShortcutTriggered()
|
||||
|
||||
const QString title = QString::fromStdString(entry->GetTitle());
|
||||
const QString path = QString::fromStdString(entry->path);
|
||||
VMLock lock(pauseAndLockVM());
|
||||
ShortcutCreationDialog dlg(lock.getDialogParent(), title, path);
|
||||
|
||||
ShortcutCreationDialog dlg(this, title, path);
|
||||
dlg.exec();
|
||||
}
|
||||
#endif
|
||||
@ -1876,7 +1980,7 @@ void MainWindow::onInputRecPlayActionTriggered()
|
||||
|
||||
QFileDialog dialog(this);
|
||||
dialog.setFileMode(QFileDialog::ExistingFile);
|
||||
dialog.setWindowTitle("Select a File");
|
||||
dialog.setWindowTitle(tr("Select a File"));
|
||||
dialog.setNameFilter(tr("Input Recording Files (*.p2m2)"));
|
||||
QStringList fileNames;
|
||||
if (dialog.exec())
|
||||
@ -3058,7 +3162,7 @@ void MainWindow::populateLoadStateMenu(QMenu* menu, const QString& filename, con
|
||||
}
|
||||
|
||||
const u32 deleted = VMManager::DeleteSaveStates(serial.toUtf8().constData(), crc, true);
|
||||
QMessageBox::information(this, tr("Delete Save States"), tr("%1 save states deleted.").arg(deleted));
|
||||
QMessageBox::information(this, tr("Delete Save States"), tr("%n save states deleted.", "", deleted));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@ -116,10 +116,13 @@ public Q_SLOTS:
|
||||
void checkForUpdates(bool display_message, bool force_check);
|
||||
void refreshGameList(bool invalidate_cache, bool popup_on_error);
|
||||
void cancelGameListRefresh();
|
||||
void updateGameListBackground();
|
||||
void reportInfo(const QString& title, const QString& message);
|
||||
void reportError(const QString& title, const QString& message);
|
||||
bool confirmMessage(const QString& title, 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 requestReset();
|
||||
|
||||
@ -1047,7 +1047,7 @@
|
||||
<iconset theme="camera-video"/>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>&Video Capture</string>
|
||||
<string comment="In Toolbar">&Video Capture</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionEditCheats">
|
||||
|
||||
@ -194,6 +194,9 @@ void EmuThread::stopFullscreenUI()
|
||||
{
|
||||
m_run_fullscreen_ui.store(false, std::memory_order_release);
|
||||
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;
|
||||
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)
|
||||
@ -315,7 +322,11 @@ void EmuThread::loadStateFromSlot(qint32 slot, bool load_backup)
|
||||
|
||||
Error 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)
|
||||
@ -329,11 +340,11 @@ void EmuThread::saveState(const QString& filename)
|
||||
if (!VMManager::HasValidVM())
|
||||
return;
|
||||
|
||||
if (!VMManager::SaveState(filename.toUtf8().constData()))
|
||||
{
|
||||
// this one is usually the result of a user-chosen path, so we can display a message box safely here
|
||||
Console.Error("Failed to save state");
|
||||
}
|
||||
VMManager::SaveState(filename.toUtf8().constData(), true, false, [](const std::string& error) {
|
||||
QtHost::RunOnUIThread([message = QString::fromStdString(error)]() {
|
||||
g_main_window->reportStateSaveError(message, std::nullopt);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
void EmuThread::saveStateToSlot(qint32 slot)
|
||||
@ -347,7 +358,11 @@ void EmuThread::saveStateToSlot(qint32 slot)
|
||||
if (!VMManager::HasValidVM())
|
||||
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()
|
||||
@ -1586,7 +1601,7 @@ bool QtHost::DownloadFile(QWidget* parent, const QString& title, std::string url
|
||||
!FileSystem::WriteBinaryFile(path.c_str(), data.data(), data.size()))
|
||||
{
|
||||
QMessageBox::critical(parent, qApp->translate("EmuThread", "Error"),
|
||||
qApp->translate("EmuThread", "Failed to write '%1'.").arg(QString::fromStdString(path)));
|
||||
qApp->translate("EmuThread", "Failed to write downloaded data to file '%1'.").arg(QString::fromStdString(path)));
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@ -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)
|
||||
{
|
||||
if (!pm || pm->isNull() || pm->width() <= 0 || pm->height() <= 0)
|
||||
if (!pm || pm->width() <= 0 || pm->height() <= 0)
|
||||
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_height = qRound(expected_height * dpr);
|
||||
|
||||
@ -196,8 +203,7 @@ namespace QtUtils
|
||||
qRound(scaledSize.width()),
|
||||
qRound(scaledSize.height()),
|
||||
Qt::IgnoreAspectRatio,
|
||||
Qt::SmoothTransformation
|
||||
);
|
||||
Qt::SmoothTransformation);
|
||||
|
||||
const QRectF scaledSrcRect(0, 0, pm->width(), pm->height());
|
||||
|
||||
@ -211,16 +217,7 @@ namespace QtUtils
|
||||
}
|
||||
case ScalingMode::Stretch:
|
||||
{
|
||||
*pm = pm->scaled(
|
||||
dpr_expected_width,
|
||||
dpr_expected_height,
|
||||
Qt::IgnoreAspectRatio,
|
||||
Qt::SmoothTransformation
|
||||
);
|
||||
|
||||
const QRectF scaledSrcRect(0, 0, pm->width(), pm->height());
|
||||
|
||||
painter.drawPixmap(painterRect, *pm, scaledSrcRect);
|
||||
painter.drawPixmap(painterRect, *pm, srcRect);
|
||||
break;
|
||||
}
|
||||
case ScalingMode::Center:
|
||||
@ -229,7 +226,6 @@ namespace QtUtils
|
||||
const qreal pmHeight = pm->height() / dpr;
|
||||
|
||||
QRectF destRect(0, 0, pmWidth, pmHeight);
|
||||
|
||||
destRect.moveCenter(painterRect.center());
|
||||
|
||||
painter.drawPixmap(destRect, *pm, srcRect);
|
||||
@ -243,13 +239,19 @@ namespace QtUtils
|
||||
if (tileWidth <= 0 || tileHeight <= 0)
|
||||
break;
|
||||
|
||||
if (pm->devicePixelRatio() == dpr)
|
||||
{
|
||||
QBrush tileBrush(*pm);
|
||||
painter.fillRect(painterRect, tileBrush);
|
||||
}
|
||||
else
|
||||
{
|
||||
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;
|
||||
}
|
||||
default:
|
||||
@ -285,12 +287,12 @@ namespace QtUtils
|
||||
{
|
||||
#if defined(_WIN32)
|
||||
//: Windows action to show a file in Windows Explorer
|
||||
return QCoreApplication::translate("FileOperations", "Show in Folder");
|
||||
return QCoreApplication::translate("FileOperations", "Show in Explorer");
|
||||
#elif defined(__APPLE__)
|
||||
//: macOS action to show a file in Finder
|
||||
return QCoreApplication::translate("FileOperations", "Show in Finder");
|
||||
#else
|
||||
//: Opens the system file manager to the directory containing a selected file
|
||||
//: Linux/*NIX: Opens the system file manager to the directory containing a selected file
|
||||
return QCoreApplication::translate("FileOperations", "Open Containing Directory");
|
||||
#endif
|
||||
}
|
||||
|
||||
@ -1263,7 +1263,7 @@ namespace SettingWidgetBinder
|
||||
|
||||
static inline void BindWidgetToFileSetting(SettingsInterface* sif, QLineEdit* widget, QAbstractButton* browse_button,
|
||||
QAbstractButton* open_button, QAbstractButton* reset_button, std::string section, std::string key, std::string default_value,
|
||||
const char* filter, bool allow_pergame = false, bool use_relative = true)
|
||||
const QString& filter, bool allow_pergame = false, bool use_relative = true)
|
||||
{
|
||||
using Accessor = SettingAccessor<QLineEdit>;
|
||||
|
||||
|
||||
@ -17,7 +17,7 @@
|
||||
#include <QtCore/QDateTime>
|
||||
#include <QtWidgets/QMessageBox>
|
||||
|
||||
const char* AUDIO_FILE_FILTER = QT_TRANSLATE_NOOP("MainWindow", "Audio Files (*.wav)");
|
||||
const char* AchievementSettingsWidget::AUDIO_FILE_FILTER = QT_TRANSLATE_NOOP("AchievementSettingsWidget", "WAV Audio Files (*.wav)");
|
||||
|
||||
AchievementSettingsWidget::AchievementSettingsWidget(SettingsWindow* settings_dialog, QWidget* parent)
|
||||
: SettingsWidget(settings_dialog, parent)
|
||||
@ -44,9 +44,9 @@ AchievementSettingsWidget::AchievementSettingsWidget(SettingsWindow* settings_di
|
||||
SettingWidgetBinder::BindWidgetToIntSetting(sif, m_ui.achievementNotificationsDuration, "Achievements", "NotificationsDuration", Pcsx2Config::AchievementsOptions::DEFAULT_NOTIFICATION_DURATION);
|
||||
SettingWidgetBinder::BindWidgetToIntSetting(sif, m_ui.leaderboardNotificationsDuration, "Achievements", "LeaderboardsDuration", Pcsx2Config::AchievementsOptions::DEFAULT_LEADERBOARD_DURATION);
|
||||
|
||||
SettingWidgetBinder::BindWidgetToFileSetting(sif, m_ui.notificationSoundPath, m_ui.notificationSoundBrowse, m_ui.notificationSoundOpen, m_ui.notificationSoundReset, "Achievements", "InfoSoundName", Path::Combine(EmuFolders::Resources, EmuConfig.Achievements.DEFAULT_INFO_SOUND_NAME), AUDIO_FILE_FILTER, true, false);
|
||||
SettingWidgetBinder::BindWidgetToFileSetting(sif, m_ui.unlockSoundPath, m_ui.unlockSoundBrowse, m_ui.unlockSoundOpen, m_ui.unlockSoundReset, "Achievements", "UnlockSoundName", Path::Combine(EmuFolders::Resources, EmuConfig.Achievements.DEFAULT_UNLOCK_SOUND_NAME), AUDIO_FILE_FILTER, true, false);
|
||||
SettingWidgetBinder::BindWidgetToFileSetting(sif, m_ui.lbSoundPath, m_ui.lbSoundBrowse, m_ui.lbSoundOpen, m_ui.lbSoundReset, "Achievements", "LBSubmitSoundName", Path::Combine(EmuFolders::Resources, EmuConfig.Achievements.DEFAULT_LBSUBMIT_SOUND_NAME), AUDIO_FILE_FILTER, true, false);
|
||||
SettingWidgetBinder::BindWidgetToFileSetting(sif, m_ui.notificationSoundPath, m_ui.notificationSoundBrowse, m_ui.notificationSoundOpen, m_ui.notificationSoundReset, "Achievements", "InfoSoundName", Path::Combine(EmuFolders::Resources, EmuConfig.Achievements.DEFAULT_INFO_SOUND_NAME), qApp->translate("AchievementSettingsWidget", AUDIO_FILE_FILTER), true, false);
|
||||
SettingWidgetBinder::BindWidgetToFileSetting(sif, m_ui.unlockSoundPath, m_ui.unlockSoundBrowse, m_ui.unlockSoundOpen, m_ui.unlockSoundReset, "Achievements", "UnlockSoundName", Path::Combine(EmuFolders::Resources, EmuConfig.Achievements.DEFAULT_UNLOCK_SOUND_NAME), qApp->translate("AchievementSettingsWidget", AUDIO_FILE_FILTER), true, false);
|
||||
SettingWidgetBinder::BindWidgetToFileSetting(sif, m_ui.lbSoundPath, m_ui.lbSoundBrowse, m_ui.lbSoundOpen, m_ui.lbSoundReset, "Achievements", "LBSubmitSoundName", Path::Combine(EmuFolders::Resources, EmuConfig.Achievements.DEFAULT_LBSUBMIT_SOUND_NAME), qApp->translate("AchievementSettingsWidget", AUDIO_FILE_FILTER), true, false);
|
||||
|
||||
dialog()->registerWidgetHelp(m_ui.enable, tr("Enable Achievements"), tr("Unchecked"), tr("When enabled and logged in, PCSX2 will scan for achievements on startup."));
|
||||
dialog()->registerWidgetHelp(m_ui.hardcoreMode, tr("Enable Hardcore Mode"), tr("Unchecked"), tr("\"Challenge\" mode for achievements, including leaderboard tracking. Disables save state, cheats, and slowdown functions."));
|
||||
|
||||
@ -26,6 +26,7 @@ private Q_SLOTS:
|
||||
|
||||
private:
|
||||
void updateLoginState();
|
||||
static const char* AUDIO_FILE_FILTER;
|
||||
|
||||
Ui::AchievementSettingsWidget m_ui;
|
||||
};
|
||||
|
||||
@ -115,7 +115,7 @@ AudioSettingsWidget::AudioSettingsWidget(SettingsWindow* settings_dialog, QWidge
|
||||
dialog()->registerWidgetHelp(m_ui.expansionSettings, tr("Expansion Settings"), tr("N/A"),
|
||||
tr("These settings fine-tune the behavior of the FreeSurround-based channel expander."));
|
||||
dialog()->registerWidgetHelp(m_ui.syncMode, tr("Synchronization"), tr("TimeStretch (Recommended)"),
|
||||
tr("When running outside of 100% speed, adjusts the tempo on audio instead of dropping frames. Produces much nicer fast-forward/slowdown audio."));
|
||||
tr("When the emulation isn't running at 100% speed, adjusts the tempo of the audio which produces much nicer sound during fast-forward/slowdown."));
|
||||
dialog()->registerWidgetHelp(m_ui.stretchSettings, tr("Stretch Settings"), tr("N/A"),
|
||||
tr("These settings fine-tune the behavior of the SoundTouch audio time stretcher when running outside of 100% speed."));
|
||||
dialog()->registerWidgetHelp(m_ui.resetStandardVolume, tr("Reset Standard Volume"), tr("N/A"),
|
||||
@ -180,7 +180,7 @@ void AudioSettingsWidget::updateDriverNames()
|
||||
const AudioBackend backend = getEffectiveBackend();
|
||||
const std::vector<std::pair<std::string, std::string>> names = AudioStream::GetDriverNames(backend);
|
||||
|
||||
m_ui.driver->disconnect();
|
||||
QObject::disconnect(m_ui.driver, &QComboBox::currentIndexChanged, nullptr, nullptr);
|
||||
m_ui.driver->clear();
|
||||
if (names.empty())
|
||||
{
|
||||
@ -208,7 +208,7 @@ void AudioSettingsWidget::updateDeviceNames()
|
||||
const std::string current_device = dialog()->getEffectiveStringValue("SPU2/Output", "DeviceName", "");
|
||||
const std::vector<AudioStream::DeviceInfo> devices = AudioStream::GetOutputDevices(backend, driver_name.c_str());
|
||||
|
||||
m_ui.outputDevice->disconnect();
|
||||
QObject::disconnect(m_ui.outputDevice, &QComboBox::currentIndexChanged, nullptr, nullptr);
|
||||
m_ui.outputDevice->clear();
|
||||
m_output_device_latency = 0;
|
||||
|
||||
|
||||
@ -39,6 +39,8 @@ BIOSSettingsWidget::BIOSSettingsWidget(SettingsWindow* settings_dialog, QWidget*
|
||||
connect(m_ui.refresh, &QPushButton::clicked, this, &BIOSSettingsWidget::refreshList);
|
||||
connect(m_ui.fileList, &QTreeWidget::currentItemChanged, this, &BIOSSettingsWidget::listItemChanged);
|
||||
connect(m_ui.fastBoot, &QCheckBox::checkStateChanged, this, &BIOSSettingsWidget::fastBootChanged);
|
||||
|
||||
fastBootChanged();
|
||||
}
|
||||
|
||||
BIOSSettingsWidget::~BIOSSettingsWidget() = default;
|
||||
|
||||
@ -30,7 +30,7 @@ DEV9DnsHostDialog::DEV9DnsHostDialog(std::vector<HostEntryUi> hosts, QWidget* pa
|
||||
QStringList headers;
|
||||
headers.push_back(tr("Selected"));
|
||||
headers.push_back(tr("Name"));
|
||||
headers.push_back(tr("Url"));
|
||||
headers.push_back(tr("Hostname"));
|
||||
headers.push_back(tr("Address"));
|
||||
headers.push_back(tr("Enabled"));
|
||||
m_ethHost_model->setHorizontalHeaderLabels(headers);
|
||||
|
||||
@ -139,7 +139,7 @@ DEV9SettingsWidget::DEV9SettingsWidget(SettingsWindow* settings_dialog, QWidget*
|
||||
|
||||
QStringList headers;
|
||||
headers.push_back(tr("Name"));
|
||||
headers.push_back(tr("Url"));
|
||||
headers.push_back(tr("Hostname"));
|
||||
headers.push_back(tr("Address"));
|
||||
headers.push_back(tr("Enabled"));
|
||||
m_ethHost_model->setHorizontalHeaderLabels(headers);
|
||||
|
||||
@ -83,7 +83,7 @@
|
||||
<item>
|
||||
<widget class="QLabel" name="symbolSourceErrorMessage">
|
||||
<property name="text">
|
||||
<string><html><head/><body><p><br/></p></body></html></string>
|
||||
<string notr="true"><html><head/><body><p><br/></p></body></html></string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignmentFlag::AlignCenter</set>
|
||||
|
||||
@ -40,6 +40,9 @@ GameCheatSettingsWidget::GameCheatSettingsWidget(SettingsWindow* settings_dialog
|
||||
|
||||
m_ui.cheatList->expandAll();
|
||||
|
||||
m_ui.cheatList->viewport()->installEventFilter(this);
|
||||
m_ui.cheatList->viewport()->setMouseTracking(true);
|
||||
|
||||
SettingsInterface* sif = dialog()->getSettingsInterface();
|
||||
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.enableCheats, "EmuCore", "EnableCheats", false);
|
||||
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.allCRCsCheckbox, "EmuCore", "ShowCheatsForAllCRCs", false);
|
||||
@ -83,7 +86,7 @@ void GameCheatSettingsWidget::onCheatListItemDoubleClicked(const QModelIndex& in
|
||||
return;
|
||||
}
|
||||
|
||||
QVariant data = item->data(Qt::UserRole);
|
||||
QVariant data = item->data(NAME_ROLE);
|
||||
if (!data.isValid())
|
||||
return;
|
||||
|
||||
@ -95,7 +98,7 @@ void GameCheatSettingsWidget::onCheatListItemDoubleClicked(const QModelIndex& in
|
||||
|
||||
void GameCheatSettingsWidget::onCheatListItemChanged(QStandardItem* item)
|
||||
{
|
||||
QVariant data = item->data(Qt::UserRole);
|
||||
QVariant data = item->data(NAME_ROLE);
|
||||
if (!data.isValid())
|
||||
return;
|
||||
|
||||
@ -109,6 +112,31 @@ void GameCheatSettingsWidget::onCheatListItemChanged(QStandardItem* item)
|
||||
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()
|
||||
{
|
||||
reloadList();
|
||||
@ -136,6 +164,32 @@ void GameCheatSettingsWidget::disableAllCheats()
|
||||
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)
|
||||
{
|
||||
QWidget::resizeEvent(event);
|
||||
@ -185,7 +239,7 @@ void GameCheatSettingsWidget::setStateRecursively(QStandardItem* parent, bool en
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
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 ((item->checkState() == Qt::Checked) != enabled)
|
||||
@ -277,7 +331,9 @@ QList<QStandardItem*> GameCheatSettingsWidget::populateTreeViewRow(const Patch::
|
||||
const std::string_view name_part = pi.GetNamePart();
|
||||
nameItem->setFlags(Qt::ItemIsUserCheckable | Qt::ItemNeverHasChildren | Qt::ItemIsEnabled);
|
||||
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())
|
||||
nameItem->setText(QString::fromUtf8(name_part.data(), name_part.length()));
|
||||
|
||||
|
||||
@ -32,6 +32,7 @@ public:
|
||||
~GameCheatSettingsWidget();
|
||||
|
||||
void disableAllCheats();
|
||||
bool eventFilter(QObject* watched, QEvent* event) override;
|
||||
|
||||
protected:
|
||||
void resizeEvent(QResizeEvent* event) override;
|
||||
@ -39,6 +40,7 @@ protected:
|
||||
private Q_SLOTS:
|
||||
void onCheatListItemDoubleClicked(const QModelIndex& index);
|
||||
void onCheatListItemChanged(QStandardItem* item);
|
||||
void onCheatListItemHovered(const QModelIndex& index);
|
||||
void onReloadClicked();
|
||||
void updateListEnabled();
|
||||
void reloadList();
|
||||
@ -50,6 +52,12 @@ private:
|
||||
void setStateForAll(bool enabled);
|
||||
void setStateRecursively(QStandardItem* parent, bool enabled);
|
||||
|
||||
enum Roles
|
||||
{
|
||||
NAME_ROLE = Qt::UserRole,
|
||||
PLACE_ROLE = Qt::UserRole + 1
|
||||
};
|
||||
|
||||
Ui::GameCheatSettingsWidget m_ui;
|
||||
QStandardItemModel* m_model = nullptr;
|
||||
QSortFilterProxyModel* m_model_proxy = nullptr;
|
||||
|
||||
@ -90,6 +90,13 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="appliedLabel">
|
||||
<property name="text">
|
||||
<string notr="true"/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer">
|
||||
<property name="orientation">
|
||||
|
||||
@ -44,7 +44,7 @@ GameFixSettingsWidget::GameFixSettingsWidget(SettingsWindow* settings_dialog, QW
|
||||
dialog()->registerWidgetHelp(m_ui.EETimingHack, tr("EE Timing Hack"), tr("Unchecked"), tr("General-purpose timing hack. Known to affect following games: Digital Devil Saga, SSX."));
|
||||
dialog()->registerWidgetHelp(m_ui.InstantDMAHack, tr("Instant DMA Hack"), tr("Unchecked"), tr("Good for cache emulation problems. Known to affect following games: Fire Pro Wrestling Z."));
|
||||
dialog()->registerWidgetHelp(m_ui.DMABusyHack, tr("DMA Busy Hack"), tr("Unchecked"), tr("Known to affect following games: Mana Khemia 1, Metal Saga, Pilot Down Behind Enemy Lines."));
|
||||
dialog()->registerWidgetHelp(m_ui.GIFFIFOHack, tr("Emulate GIF FIFO"), tr("Unchecked"), tr("Correct but slower. Known to affect the following games: Fifa Street 2."));
|
||||
dialog()->registerWidgetHelp(m_ui.GIFFIFOHack, tr("Emulate GIF FIFO"), tr("Unchecked"), tr("Correct but slower. Known to affect the following games: FIFA Street 2."));
|
||||
dialog()->registerWidgetHelp(m_ui.VIFFIFOHack, tr("Emulate VIF FIFO"), tr("Unchecked"), tr("Simulate VIF1 FIFO read ahead. Known to affect following games: Test Drive Unlimited, Transformers."));
|
||||
dialog()->registerWidgetHelp(m_ui.VIF1StallHack, tr("Delay VIF1 Stalls"), tr("Unchecked"), tr("For SOCOM 2 HUD and Spy Hunter loading hang."));
|
||||
dialog()->registerWidgetHelp(m_ui.VuAddSubHack, tr("VU Add Hack"), tr("Unchecked"), tr("For Tri-Ace Games: Star Ocean 3, Radiata Stories, Valkyrie Profile 2."));
|
||||
|
||||
@ -15,19 +15,23 @@
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
GamePatchDetailsWidget::GamePatchDetailsWidget(std::string name, const std::string& author,
|
||||
const std::string& description, bool tristate, Qt::CheckState checkState, SettingsWindow* dialog, QWidget* parent)
|
||||
GamePatchDetailsWidget::GamePatchDetailsWidget(const Patch::PatchInfo& info, bool tristate, Qt::CheckState checkState, SettingsWindow* dialog, QWidget* parent)
|
||||
: QWidget(parent)
|
||||
, m_dialog(dialog)
|
||||
, m_name(name)
|
||||
, m_name(info.name)
|
||||
{
|
||||
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(
|
||||
tr("<strong>Author: </strong>%1<br>%2")
|
||||
.arg(author.empty() ? tr("Unknown") : QString::fromStdString(author))
|
||||
.arg(description.empty() ? tr("No description provided.") : QString::fromStdString(description)));
|
||||
tr("<strong>Author:</strong> %1<br><strong>Applied:</strong> %2<br>%3")
|
||||
.arg(author)
|
||||
.arg(place)
|
||||
.arg(description));
|
||||
|
||||
pxAssert(dialog->getSettingsInterface());
|
||||
m_ui.enabled->setTristate(tristate);
|
||||
@ -178,7 +182,7 @@ void GamePatchSettingsWidget::reloadList()
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
@ -20,7 +20,7 @@ class GamePatchDetailsWidget : public QWidget
|
||||
Q_OBJECT
|
||||
|
||||
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);
|
||||
~GamePatchDetailsWidget();
|
||||
|
||||
|
||||
@ -15,7 +15,7 @@
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<widget class="QGroupBox" name="groupBox">
|
||||
<widget class="QGroupBox" name="screenshotGroupBox">
|
||||
<property name="title">
|
||||
<string>Screenshot Capture Setup</string>
|
||||
</property>
|
||||
@ -42,7 +42,7 @@
|
||||
</property>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Window Resolution (Aspect Corrected)</string>
|
||||
<string>Display Resolution (Aspect Corrected)</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
@ -186,6 +186,9 @@
|
||||
<property name="singleStep">
|
||||
<number>1</number>
|
||||
</property>
|
||||
<property name="value">
|
||||
<number>67</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0" colspan="2">
|
||||
@ -273,6 +276,9 @@
|
||||
<property name="singleStep">
|
||||
<number>100</number>
|
||||
</property>
|
||||
<property name="value">
|
||||
<number>420</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0">
|
||||
@ -325,7 +331,7 @@
|
||||
<number>16</number>
|
||||
</property>
|
||||
<property name="value">
|
||||
<number>240</number>
|
||||
<number>480</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
|
||||
@ -472,7 +472,7 @@ GraphicsSettingsWidget::GraphicsSettingsWidget(SettingsWindow* settings_dialog,
|
||||
|
||||
dialog()->registerWidgetHelp(m_display.interlacing, tr("Deinterlacing"), tr("Automatic (Default)"), tr("Determines the deinterlacing method to be used on the interlaced screen of the emulated console. Automatic should be able to correctly deinterlace most games, but if you see visibly shaky graphics, try one of the other options."));
|
||||
|
||||
dialog()->registerWidgetHelp(m_capture.screenshotSize, tr("Screenshot Resolution"), tr("Screen Resolution"),
|
||||
dialog()->registerWidgetHelp(m_capture.screenshotSize, tr("Screenshot Resolution"), tr("Display Resolution"),
|
||||
tr("Determines the resolution at which screenshots will be saved. Internal resolutions preserve more detail at the cost of "
|
||||
"file size."));
|
||||
|
||||
@ -587,10 +587,10 @@ GraphicsSettingsWidget::GraphicsSettingsWidget(SettingsWindow* settings_dialog,
|
||||
dialog()->registerWidgetHelp(m_fixes.gpuTargetCLUTMode, tr("GPU Target CLUT"), tr("Disabled"),
|
||||
tr("Tries to detect when a game is drawing its own color palette and then renders it on the GPU with special handling."));
|
||||
|
||||
dialog()->registerWidgetHelp(m_fixes.skipDrawStart, tr("Skipdraw Range Start"), tr("0"),
|
||||
dialog()->registerWidgetHelp(m_fixes.skipDrawStart, tr("Skip Draw Range Start"), tr("0"),
|
||||
tr("Completely skips drawing surfaces from the surface in the left box up to the surface specified in the box on the right."));
|
||||
|
||||
dialog()->registerWidgetHelp(m_fixes.skipDrawEnd, tr("Skipdraw Range End"), tr("0"),
|
||||
dialog()->registerWidgetHelp(m_fixes.skipDrawEnd, tr("Skip Draw Range End"), tr("0"),
|
||||
tr("Completely skips drawing surfaces from the surface in the left box up to the surface specified in the box on the right."));
|
||||
|
||||
dialog()->registerWidgetHelp(m_fixes.hwAutoFlush, tr("Auto Flush"), tr("Unchecked"),
|
||||
@ -660,7 +660,7 @@ GraphicsSettingsWidget::GraphicsSettingsWidget(SettingsWindow* settings_dialog,
|
||||
//: Wild Arms: name of a game series. Leave as-is or use an official translation.
|
||||
tr("Lowers the GS precision to avoid gaps between pixels when upscaling. Fixes the text on Wild Arms games."));
|
||||
|
||||
dialog()->registerWidgetHelp(m_upscaling.bilinearHack, tr("Bilinear Upscale"), tr("Unchecked"),
|
||||
dialog()->registerWidgetHelp(m_upscaling.bilinearHack, tr("Bilinear Dirty Upscale"), tr("Unchecked"),
|
||||
tr("Can smooth out textures due to be bilinear filtered when upscaling. E.g. Brave sun glare."));
|
||||
|
||||
dialog()->registerWidgetHelp(m_upscaling.mergeSprite, tr("Merge Sprite"), tr("Unchecked"),
|
||||
@ -729,7 +729,7 @@ GraphicsSettingsWidget::GraphicsSettingsWidget(SettingsWindow* settings_dialog,
|
||||
tr("Shows the number of internal video frames displayed per second by the system."));
|
||||
|
||||
dialog()->registerWidgetHelp(m_osd.showVPS, tr("Show VPS"), tr("Unchecked"),
|
||||
tr("Shows the number of V-syncs performed per second by the system."));
|
||||
tr("Shows the number of Vsyncs performed per second by the system."));
|
||||
|
||||
dialog()->registerWidgetHelp(m_osd.showResolution, tr("Show Resolution"), tr("Unchecked"),
|
||||
tr("Shows the internal resolution of the game."));
|
||||
@ -765,10 +765,10 @@ GraphicsSettingsWidget::GraphicsSettingsWidget(SettingsWindow* settings_dialog,
|
||||
tr("Shows the current controller state of the system in the bottom-left corner of the display."));
|
||||
|
||||
dialog()->registerWidgetHelp(m_osd.showVideoCapture, tr("Show Video Capture Status"), tr("Checked"),
|
||||
tr("Shows the status of the currently active video capture in the top-right corner of the display.."));
|
||||
tr("Shows the status of the currently active video capture in the top-right corner of the display."));
|
||||
|
||||
dialog()->registerWidgetHelp(m_osd.showInputRec, tr("Show Input Recording Status"), tr("Checked"),
|
||||
tr("Shows the status of the currently active input recording in the top-right corner of the display.."));
|
||||
tr("Shows the status of the currently active input recording in the top-right corner of the display."));
|
||||
|
||||
dialog()->registerWidgetHelp(m_osd.showTextureReplacements, tr("Show Texture Replacement Status"), tr("Unchecked"),
|
||||
tr("Shows the status of the number of dumped and loaded texture replacements in the top-right corner of the display."));
|
||||
@ -981,7 +981,7 @@ void GraphicsSettingsWidget::onCaptureContainerChanged()
|
||||
const std::string container(
|
||||
dialog()->getEffectiveStringValue("EmuCore/GS", "CaptureContainer", Pcsx2Config::GSOptions::DEFAULT_CAPTURE_CONTAINER));
|
||||
|
||||
m_capture.videoCaptureCodec->disconnect();
|
||||
QObject::disconnect(m_capture.videoCaptureCodec, &QComboBox::currentIndexChanged, nullptr, nullptr);
|
||||
m_capture.videoCaptureCodec->clear();
|
||||
//: This string refers to a default codec, whether it's an audio codec or a video codec.
|
||||
m_capture.videoCaptureCodec->addItem(tr("Default"), QString());
|
||||
@ -996,7 +996,7 @@ void GraphicsSettingsWidget::onCaptureContainerChanged()
|
||||
dialog()->getSettingsInterface(), m_capture.videoCaptureCodec, "EmuCore/GS", "VideoCaptureCodec");
|
||||
connect(m_capture.videoCaptureCodec, &QComboBox::currentIndexChanged, this, &GraphicsSettingsWidget::onCaptureCodecChanged);
|
||||
|
||||
m_capture.audioCaptureCodec->disconnect();
|
||||
QObject::disconnect(m_capture.audioCaptureCodec, &QComboBox::currentIndexChanged, nullptr, nullptr);
|
||||
m_capture.audioCaptureCodec->clear();
|
||||
m_capture.audioCaptureCodec->addItem(tr("Default"), QString());
|
||||
for (const auto& [format, name] : GSCapture::GetAudioCodecList(container.c_str()))
|
||||
@ -1012,7 +1012,7 @@ void GraphicsSettingsWidget::onCaptureContainerChanged()
|
||||
|
||||
void GraphicsSettingsWidget::GraphicsSettingsWidget::onCaptureCodecChanged()
|
||||
{
|
||||
m_capture.videoCaptureFormat->disconnect();
|
||||
QObject::disconnect(m_capture.videoCaptureFormat, &QComboBox::currentIndexChanged, nullptr, nullptr);
|
||||
m_capture.videoCaptureFormat->clear();
|
||||
//: This string refers to a default pixel format
|
||||
m_capture.videoCaptureFormat->addItem(tr("Default"), "");
|
||||
|
||||
@ -93,7 +93,7 @@
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="nativeScalingLabel">
|
||||
<property name="text">
|
||||
<string>Native Scaling</string>
|
||||
<string>Native Scaling:</string>
|
||||
</property>
|
||||
<property name="buddy">
|
||||
<cstring>nativeScaling</cstring>
|
||||
|
||||
@ -22,9 +22,6 @@
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<widget class="QLabel" name="title">
|
||||
<property name="text">
|
||||
<string>Bindings for Controller0/ButtonCircle</string>
|
||||
</property>
|
||||
<property name="buddy">
|
||||
<cstring>bindingList</cstring>
|
||||
</property>
|
||||
|
||||
@ -97,6 +97,7 @@ InterfaceSettingsWidget::InterfaceSettingsWidget(SettingsWindow* settings_dialog
|
||||
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.confirmShutdown, "UI", "ConfirmShutdown", true);
|
||||
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.pauseOnFocusLoss, "UI", "PauseOnFocusLoss", 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);
|
||||
|
||||
#ifdef __linux__ // Mouse locking is only supported on X11
|
||||
@ -196,6 +197,8 @@ InterfaceSettingsWidget::InterfaceSettingsWidget(SettingsWindow* settings_dialog
|
||||
"and unpauses when you switch back."));
|
||||
dialog()->registerWidgetHelp(m_ui.pauseOnControllerDisconnection, tr("Pause On Controller Disconnection"),
|
||||
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"),
|
||||
tr("Automatically switches to fullscreen mode when a game is started."));
|
||||
dialog()->registerWidgetHelp(m_ui.hideMouseCursor, tr("Hide Cursor In Fullscreen"), tr("Unchecked"),
|
||||
@ -241,6 +244,12 @@ InterfaceSettingsWidget::InterfaceSettingsWidget(SettingsWindow* settings_dialog
|
||||
|
||||
InterfaceSettingsWidget::~InterfaceSettingsWidget() = default;
|
||||
|
||||
void InterfaceSettingsWidget::updatePromptOnStateLoadSaveFailureCheckbox(Qt::CheckState state)
|
||||
{
|
||||
QSignalBlocker blocker(m_ui.promptOnStateLoadSaveFailure);
|
||||
m_ui.promptOnStateLoadSaveFailure->setCheckState(state);
|
||||
}
|
||||
|
||||
void InterfaceSettingsWidget::onRenderToSeparateWindowChanged()
|
||||
{
|
||||
m_ui.hideMainWindow->setEnabled(m_ui.renderToSeparateWindow->isChecked());
|
||||
@ -261,12 +270,12 @@ void InterfaceSettingsWidget::populateLanguages()
|
||||
void InterfaceSettingsWidget::onSetGameListBackgroundTriggered()
|
||||
{
|
||||
const QString path = QDir::toNativeSeparators(
|
||||
QFileDialog::getOpenFileName(this, tr("Select Background Image"), QString(), IMAGE_FILE_FILTER));
|
||||
QFileDialog::getOpenFileName(this, tr("Select Background Image"), QString(), qApp->translate("InterfaceSettingsWidget", IMAGE_FILE_FILTER)));
|
||||
|
||||
if (path.isEmpty())
|
||||
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::CommitBaseSettingChanges();
|
||||
|
||||
@ -15,6 +15,8 @@ public:
|
||||
InterfaceSettingsWidget(SettingsWindow* settings_dialog, QWidget* parent);
|
||||
~InterfaceSettingsWidget();
|
||||
|
||||
void updatePromptOnStateLoadSaveFailureCheckbox(Qt::CheckState state);
|
||||
|
||||
Q_SIGNALS:
|
||||
void themeChanged();
|
||||
void languageChanged();
|
||||
|
||||
@ -7,7 +7,7 @@
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>725</width>
|
||||
<height>625</height>
|
||||
<height>637</height>
|
||||
</rect>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
@ -78,6 +78,13 @@
|
||||
</property>
|
||||
</widget>
|
||||
</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>
|
||||
</widget>
|
||||
</item>
|
||||
@ -380,6 +387,7 @@
|
||||
<tabstop>discordPresence</tabstop>
|
||||
<tabstop>pauseOnControllerDisconnection</tabstop>
|
||||
<tabstop>mouseLock</tabstop>
|
||||
<tabstop>promptOnStateLoadSaveFailure</tabstop>
|
||||
<tabstop>startFullscreen</tabstop>
|
||||
<tabstop>doubleClickTogglesFullscreen</tabstop>
|
||||
<tabstop>renderToSeparateWindow</tabstop>
|
||||
|
||||
@ -261,8 +261,10 @@ void SetupWizardDialog::onDirectoryListContextMenuRequested(const QPoint& point)
|
||||
const int row = selection[0].row();
|
||||
|
||||
QMenu menu;
|
||||
//: Part of the right-click menu for game directory entries
|
||||
menu.addAction(tr("Remove"), [this]() { onRemoveSearchDirectoryButtonClicked(); });
|
||||
menu.addSeparator();
|
||||
//: Part of the right-click menu for game directory entries
|
||||
menu.addAction(tr("Open Directory..."),
|
||||
[this, row]() { QtUtils::OpenURL(this, QUrl::fromLocalFile(m_ui.searchDirectoryList->item(row, 0)->text())); });
|
||||
menu.exec(m_ui.searchDirectoryList->mapToGlobal(point));
|
||||
|
||||
@ -13,7 +13,7 @@
|
||||
#include "VMManager.h"
|
||||
|
||||
#if defined(_WIN32)
|
||||
#include <Windows.h>
|
||||
#include "common/RedtapeWindows.h"
|
||||
#include <shlobj.h>
|
||||
#include <winnls.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.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);
|
||||
|
||||
if (std::getenv("container"))
|
||||
@ -80,6 +70,7 @@ ShortcutCreationDialog::ShortcutCreationDialog(QWidget* parent, const QString& t
|
||||
m_ui.shortcutStartMenu->setEnabled(false);
|
||||
}
|
||||
|
||||
connect(m_ui.dialogButtons, &QDialogButtonBox::rejected, this, &QDialog::reject);
|
||||
connect(m_ui.dialogButtons, &QDialogButtonBox::accepted, this, [&]() {
|
||||
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())
|
||||
args.push_back(m_ui.fullscreenModeDropdown->currentIndex() ? "-nofullscreen" : "-fullscreen");
|
||||
|
||||
if (m_ui.bigPictureModeToggle->isChecked())
|
||||
args.push_back("-bigpicture");
|
||||
|
||||
m_desktop = m_ui.shortcutDesktop->isChecked();
|
||||
|
||||
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();
|
||||
});
|
||||
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)
|
||||
@ -183,7 +177,10 @@ void ShortcutCreationDialog::CreateShortcut(const std::string name, const std::s
|
||||
lossless &= ShortcutCreationDialog::EscapeShortcutCommandLine(&arg);
|
||||
|
||||
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);
|
||||
std::string combined_args = StringUtil::JoinString(passed_cli_args.begin(), passed_cli_args.end(), " ");
|
||||
@ -203,7 +200,7 @@ void ShortcutCreationDialog::CreateShortcut(const std::string name, const std::s
|
||||
if (FAILED(res))
|
||||
{
|
||||
Console.ErrorFmt("Failed to create shortcut: CoInitialize failed ({})", str_error(res));
|
||||
QMessageBox::critical(this, tr("Failed to create shortcut"), tr("CoInitialize failed (%1").arg(str_error(res)), QMessageBox::StandardButton::Ok, QMessageBox::StandardButton::Ok);
|
||||
QMessageBox::critical(this, tr("Failed to create shortcut"), tr("CoInitialize failed (%1)").arg(str_error(res)), QMessageBox::StandardButton::Ok, QMessageBox::StandardButton::Ok);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -313,9 +310,6 @@ void ShortcutCreationDialog::CreateShortcut(const std::string name, const std::s
|
||||
return;
|
||||
}
|
||||
|
||||
if (is_flatpak) // Flatpak
|
||||
executable_path = "flatpak run net.pcsx2.PCSX2";
|
||||
|
||||
// Find home directory
|
||||
std::string link_path;
|
||||
const char* home = std::getenv("HOME");
|
||||
@ -340,15 +334,30 @@ void ShortcutCreationDialog::CreateShortcut(const std::string name, const std::s
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
// Checks if a shortcut already exist
|
||||
if (FileSystem::FileExists(link_path.c_str()))
|
||||
// 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;
|
||||
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);
|
||||
return;
|
||||
executable_path = "flatpak run net.pcsx2.PCSX2";
|
||||
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
|
||||
@ -357,25 +366,13 @@ void ShortcutCreationDialog::CreateShortcut(const std::string name, const std::s
|
||||
lossless &= ShortcutCreationDialog::EscapeShortcutCommandLine(&arg);
|
||||
|
||||
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(), " ");
|
||||
|
||||
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
|
||||
if (!is_flatpak)
|
||||
ShortcutCreationDialog::EscapeShortcutCommandLine(&executable_path);
|
||||
@ -393,7 +390,7 @@ void ShortcutCreationDialog::CreateShortcut(const std::string name, const std::s
|
||||
"StartupWMClass=PCSX2\n"
|
||||
"Exec=" + final_args + "\n"
|
||||
"Name=" + clean_name + "\n"
|
||||
"Icon=net.pcsx2.PCSX2\n"
|
||||
"Icon=" + icon_name + "\n"
|
||||
"Categories=Game;Emulator;\n";
|
||||
std::string_view sv(file_content);
|
||||
|
||||
|
||||
@ -22,8 +22,9 @@ public:
|
||||
bool EscapeShortcutCommandLine(std::string* cmdline);
|
||||
|
||||
protected:
|
||||
QString m_title;
|
||||
QString m_path;
|
||||
bool m_desktop;
|
||||
const QString m_title;
|
||||
const QString m_path;
|
||||
|
||||
private:
|
||||
Ui::ShortcutCreationDialog m_ui;
|
||||
};
|
||||
|
||||
@ -10,7 +10,7 @@
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>500</width>
|
||||
<height>685</height>
|
||||
<height>700</height>
|
||||
</rect>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="shortcutLayout">
|
||||
@ -59,6 +59,9 @@
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QComboBox" name="fullscreenModeDropdown">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Force Enable</string>
|
||||
@ -82,10 +85,17 @@
|
||||
</widget>
|
||||
</item>
|
||||
<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 row="4" column="2" colspan="2">
|
||||
<widget class="QComboBox" name="bootOptionDropdown">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Fast Boot</string>
|
||||
@ -107,13 +117,20 @@
|
||||
</item>
|
||||
<item row="2" column="3">
|
||||
<widget class="QPushButton" name="overrideBootELFButton">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Browse...</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<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 row="10" column="0" colspan="4">
|
||||
<widget class="QGroupBox" name="saveStateGroup">
|
||||
@ -123,6 +140,9 @@
|
||||
<layout class="QGridLayout" name="savestateGridLayout">
|
||||
<item row="1" column="1">
|
||||
<widget class="QSpinBox" name="loadStateIndex">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="showGroupSeparator" stdset="0">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
@ -132,7 +152,11 @@
|
||||
</widget>
|
||||
</item>
|
||||
<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 row="0" column="0">
|
||||
<widget class="QRadioButton" name="loadStateNone">
|
||||
@ -160,6 +184,9 @@
|
||||
</item>
|
||||
<item row="2" column="2">
|
||||
<widget class="QPushButton" name="loadStateFileBrowse">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Browse...</string>
|
||||
</property>
|
||||
@ -194,7 +221,7 @@
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="customArgsInstruction">
|
||||
<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 name="textFormat">
|
||||
<enum>Qt::TextFormat::RichText</enum>
|
||||
|
||||
@ -82,7 +82,7 @@ void InputRecordingViewer::openFile()
|
||||
{
|
||||
QFileDialog dialog(this);
|
||||
dialog.setFileMode(QFileDialog::ExistingFile);
|
||||
dialog.setWindowTitle("Select a File");
|
||||
dialog.setWindowTitle(tr("Select a File"));
|
||||
dialog.setNameFilter(tr("Input Recording Files (*.p2m2)"));
|
||||
QStringList fileNames;
|
||||
if (dialog.exec())
|
||||
|
||||
@ -4,8 +4,8 @@
|
||||
<context>
|
||||
<name>AchievementSettingsWidget</name>
|
||||
<message numerus="yes">
|
||||
<location filename="../Settings/AchievementSettingsWidget.cpp" line="134"/>
|
||||
<location filename="../Settings/AchievementSettingsWidget.cpp" line="141"/>
|
||||
<location filename="../Settings/AchievementSettingsWidget.cpp" line="196"/>
|
||||
<location filename="../Settings/AchievementSettingsWidget.cpp" line="203"/>
|
||||
<source>%n seconds</source>
|
||||
<translation>
|
||||
<numerusform>%n second</numerusform>
|
||||
@ -16,7 +16,7 @@
|
||||
<context>
|
||||
<name>Achievements</name>
|
||||
<message numerus="yes">
|
||||
<location filename="../../pcsx2/Achievements.cpp" line="1023"/>
|
||||
<location filename="../../pcsx2/Achievements.cpp" line="1104"/>
|
||||
<source>You have unlocked {} of %n achievements</source>
|
||||
<comment>Achievement popup</comment>
|
||||
<translation>
|
||||
@ -25,7 +25,7 @@
|
||||
</translation>
|
||||
</message>
|
||||
<message numerus="yes">
|
||||
<location filename="../../pcsx2/Achievements.cpp" line="1026"/>
|
||||
<location filename="../../pcsx2/Achievements.cpp" line="1107"/>
|
||||
<source>and earned {} of %n points</source>
|
||||
<comment>Achievement popup</comment>
|
||||
<translation>
|
||||
@ -34,7 +34,7 @@
|
||||
</translation>
|
||||
</message>
|
||||
<message numerus="yes">
|
||||
<location filename="../../pcsx2/Achievements.cpp" line="1111"/>
|
||||
<location filename="../../pcsx2/Achievements.cpp" line="1191"/>
|
||||
<source>%n achievements</source>
|
||||
<comment>Mastery popup</comment>
|
||||
<translation>
|
||||
@ -43,7 +43,7 @@
|
||||
</translation>
|
||||
</message>
|
||||
<message numerus="yes">
|
||||
<location filename="../../pcsx2/Achievements.cpp" line="1113"/>
|
||||
<location filename="../../pcsx2/Achievements.cpp" line="1193"/>
|
||||
<source>%n points</source>
|
||||
<comment>Mastery popup</comment>
|
||||
<translation>
|
||||
@ -55,8 +55,8 @@
|
||||
<context>
|
||||
<name>GameList</name>
|
||||
<message numerus="yes">
|
||||
<location filename="../GameList/GameListModel.cpp" line="268"/>
|
||||
<location filename="../../pcsx2/GameList.cpp" line="1142"/>
|
||||
<location filename="../GameList/GameListModel.cpp" line="223"/>
|
||||
<location filename="../../pcsx2/GameList.cpp" line="1248"/>
|
||||
<source>%n hours</source>
|
||||
<translation>
|
||||
<numerusform>%n hour</numerusform>
|
||||
@ -64,19 +64,27 @@
|
||||
</translation>
|
||||
</message>
|
||||
<message numerus="yes">
|
||||
<location filename="../GameList/GameListModel.cpp" line="270"/>
|
||||
<location filename="../../pcsx2/GameList.cpp" line="1144"/>
|
||||
<location filename="../GameList/GameListModel.cpp" line="227"/>
|
||||
<location filename="../../pcsx2/GameList.cpp" line="1250"/>
|
||||
<source>%n minutes</source>
|
||||
<translation>
|
||||
<numerusform>%n minute</numerusform>
|
||||
<numerusform>%n minutes</numerusform>
|
||||
</translation>
|
||||
</message>
|
||||
<message numerus="yes">
|
||||
<location filename="../../pcsx2/GameList.cpp" line="1252"/>
|
||||
<source>%n seconds</source>
|
||||
<translation>
|
||||
<numerusform>%n second</numerusform>
|
||||
<numerusform>%n seconds</numerusform>
|
||||
</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>InputBindingWidget</name>
|
||||
<message numerus="yes">
|
||||
<location filename="../Settings/InputBindingWidget.cpp" line="73"/>
|
||||
<location filename="../Settings/InputBindingWidget.cpp" line="77"/>
|
||||
<source>%n bindings</source>
|
||||
<translation>
|
||||
<numerusform>%n binding</numerusform>
|
||||
@ -84,10 +92,21 @@
|
||||
</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>MainWindow</name>
|
||||
<message numerus="yes">
|
||||
<location filename="../MainWindow.cpp" line="3061"/>
|
||||
<source>%n save states deleted.</source>
|
||||
<translation>
|
||||
<numerusform>%n save state deleted.</numerusform>
|
||||
<numerusform>%n save states deleted.</numerusform>
|
||||
</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>Patch</name>
|
||||
<message numerus="yes">
|
||||
<location filename="../../pcsx2/Patch.cpp" line="698"/>
|
||||
<location filename="../../pcsx2/Patch.cpp" line="768"/>
|
||||
<source>%n GameDB patches are active.</source>
|
||||
<comment>OSD Message</comment>
|
||||
<translation>
|
||||
@ -96,7 +115,7 @@
|
||||
</translation>
|
||||
</message>
|
||||
<message numerus="yes">
|
||||
<location filename="../../pcsx2/Patch.cpp" line="705"/>
|
||||
<location filename="../../pcsx2/Patch.cpp" line="775"/>
|
||||
<source>%n game patches are active.</source>
|
||||
<comment>OSD Message</comment>
|
||||
<translation>
|
||||
@ -105,7 +124,7 @@
|
||||
</translation>
|
||||
</message>
|
||||
<message numerus="yes">
|
||||
<location filename="../../pcsx2/Patch.cpp" line="712"/>
|
||||
<location filename="../../pcsx2/Patch.cpp" line="781"/>
|
||||
<source>%n cheat patches are active.</source>
|
||||
<comment>OSD Message</comment>
|
||||
<translation>
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -2942,8 +2942,19 @@ static void cdvdWrite16(u8 rt) // SCOMMAND
|
||||
|
||||
bit_ofs = mg_BIToffset(&cdvd.mg_buffer[0]);
|
||||
|
||||
memcpy(&cdvd.mg_kbit[0], &cdvd.mg_buffer[bit_ofs - 0x20], 0x10);
|
||||
memcpy(&cdvd.mg_kcon[0], &cdvd.mg_buffer[bit_ofs - 0x10], 0x10);
|
||||
const size_t buf_size = sizeof(cdvd.mg_buffer);
|
||||
|
||||
if (bit_ofs < 0x20 || (size_t)bit_ofs > buf_size)
|
||||
{
|
||||
fail_pol_cal();
|
||||
break;
|
||||
}
|
||||
|
||||
const size_t kbit_ofs = bit_ofs - 0x20;
|
||||
const size_t kcon_ofs = bit_ofs - 0x10;
|
||||
|
||||
std::memcpy(&cdvd.mg_kbit[0], &cdvd.mg_buffer[kbit_ofs], 0x10);
|
||||
std::memcpy(&cdvd.mg_kcon[0], &cdvd.mg_buffer[kcon_ofs], 0x10);
|
||||
|
||||
if ((cdvd.mg_buffer[bit_ofs + 5] || cdvd.mg_buffer[bit_ofs + 6] || cdvd.mg_buffer[bit_ofs + 7]) ||
|
||||
(GetBufferU16(&cdvd.mg_buffer[0],bit_ofs + 4) * 16 + bit_ofs + 8 + 16 != GetBufferU16(&cdvd.mg_buffer[0], 0x14)))
|
||||
@ -2969,7 +2980,31 @@ static void cdvdWrite16(u8 rt) // SCOMMAND
|
||||
{
|
||||
SetSCMDResultSize(3); //in:0
|
||||
const int bit_ofs = mg_BIToffset(&cdvd.mg_buffer[0]);
|
||||
memcpy(&cdvd.mg_buffer[0], &cdvd.mg_buffer[bit_ofs], static_cast<size_t>(8 + 16 * static_cast<int>(cdvd.mg_buffer[bit_ofs + 4])));
|
||||
|
||||
if (bit_ofs < 0)
|
||||
{
|
||||
fail_pol_cal();
|
||||
break;
|
||||
}
|
||||
|
||||
const size_t bufsize = sizeof(cdvd.mg_buffer);
|
||||
const size_t ofs = static_cast<size_t>(bit_ofs);
|
||||
|
||||
if (ofs > bufsize - 5) // Make sure we can read the block count
|
||||
{
|
||||
fail_pol_cal();
|
||||
break;
|
||||
}
|
||||
const unsigned int blocks = static_cast<unsigned int>(cdvd.mg_buffer[ofs + 4]);
|
||||
const size_t copy_len = 8 + 16 * static_cast<size_t>(blocks);
|
||||
|
||||
if (copy_len > bufsize - ofs) // Make sure we can read the blocks
|
||||
{
|
||||
fail_pol_cal();
|
||||
break;
|
||||
}
|
||||
|
||||
std::memmove(&cdvd.mg_buffer[0], &cdvd.mg_buffer[ofs], copy_len);
|
||||
|
||||
cdvd.mg_maxsize = 0; // don't allow any write
|
||||
cdvd.mg_size = 8 + 16 * cdvd.mg_buffer[4]; //new offset, i just moved the data
|
||||
|
||||
@ -3427,6 +3427,9 @@ void GSDevice12::BeginRenderPass(D3D12_RENDER_PASS_BEGINNING_ACCESS_TYPE color_b
|
||||
m_dirty_flags &= ~DIRTY_FLAG_RENDER_TARGET;
|
||||
m_in_render_pass = true;
|
||||
|
||||
if (stencil_end == D3D12_RENDER_PASS_ENDING_ACCESS_TYPE_DISCARD)
|
||||
GL_INS("D3D12: BeginRenderPass() end stencil is DISCARDED.");
|
||||
|
||||
D3D12_RENDER_PASS_RENDER_TARGET_DESC rt = {};
|
||||
if (m_current_render_target)
|
||||
{
|
||||
@ -4008,7 +4011,8 @@ void GSDevice12::RenderHW(GSHWDrawConfig& config)
|
||||
draw_ds ? D3D12_RENDER_PASS_ENDING_ACCESS_TYPE_PRESERVE : D3D12_RENDER_PASS_ENDING_ACCESS_TYPE_NO_ACCESS,
|
||||
stencil_DATE ? D3D12_RENDER_PASS_BEGINNING_ACCESS_TYPE_PRESERVE :
|
||||
D3D12_RENDER_PASS_BEGINNING_ACCESS_TYPE_NO_ACCESS,
|
||||
stencil_DATE ? D3D12_RENDER_PASS_ENDING_ACCESS_TYPE_DISCARD :
|
||||
stencil_DATE ? (draw_rt_clone ? D3D12_RENDER_PASS_ENDING_ACCESS_TYPE_PRESERVE :
|
||||
D3D12_RENDER_PASS_ENDING_ACCESS_TYPE_DISCARD) :
|
||||
D3D12_RENDER_PASS_ENDING_ACCESS_TYPE_NO_ACCESS,
|
||||
clear_color, draw_ds ? draw_ds->GetClearDepth() : 0.0f, 1);
|
||||
}
|
||||
|
||||
@ -106,14 +106,15 @@ static void HotkeyLoadStateSlot(s32 slot)
|
||||
|
||||
Error error;
|
||||
if (!VMManager::LoadStateFromSlot(slot, false, &error))
|
||||
Host::AddIconOSDMessage("LoadStateFromSlot", ICON_FA_TRIANGLE_EXCLAMATION,
|
||||
error.GetDescription(), Host::OSD_INFO_DURATION);
|
||||
FullscreenUI::ReportStateLoadError(error.GetDescription(), slot, false);
|
||||
});
|
||||
}
|
||||
|
||||
static void HotkeySaveStateSlot(s32 slot)
|
||||
{
|
||||
VMManager::SaveStateToSlot(slot);
|
||||
VMManager::SaveStateToSlot(slot, true, [slot](const std::string& error) {
|
||||
FullscreenUI::ReportStateSaveError(error, slot);
|
||||
});
|
||||
}
|
||||
|
||||
static bool CanPause()
|
||||
|
||||
@ -630,9 +630,9 @@ namespace FullscreenUI
|
||||
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<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 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 u32 s_game_cheat_unlabelled_count = 0;
|
||||
static std::vector<const HotkeyInfo*> s_hotkey_list_cache;
|
||||
@ -670,7 +670,8 @@ namespace FullscreenUI
|
||||
static void DrawSaveStateSelector(bool is_loading);
|
||||
static bool OpenLoadStateSelectorForGameResume(const GameList::Entry* entry);
|
||||
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::string s_save_state_selector_game_path;
|
||||
@ -1220,7 +1221,7 @@ void FullscreenUI::Render()
|
||||
|
||||
// see if background setting changed
|
||||
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)
|
||||
{
|
||||
s_last_background_path = current_path;
|
||||
@ -1239,7 +1240,8 @@ void FullscreenUI::Render()
|
||||
s_current_main_window == MainWindowType::Exit ||
|
||||
s_current_main_window == MainWindowType::GameList ||
|
||||
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;
|
||||
if (should_draw_background)
|
||||
@ -1690,7 +1692,7 @@ bool FullscreenUI::ShouldDefaultToGameList()
|
||||
|
||||
void FullscreenUI::LoadCustomBackground()
|
||||
{
|
||||
std::string path = Host::GetBaseStringSettingValue("UI", "GameListBackgroundPath");
|
||||
std::string path = Host::GetBaseStringSettingValue("UI", "FSUIBackgroundPath");
|
||||
|
||||
if (path.empty())
|
||||
{
|
||||
@ -1758,19 +1760,25 @@ void FullscreenUI::DrawCustomBackground()
|
||||
const ImGuiIO& io = ImGui::GetIO();
|
||||
const ImVec2 display_size = io.DisplaySize;
|
||||
|
||||
const float opacity = Host::GetBaseFloatSettingValue("UI", "GameListBackgroundOpacity", 100.0f) / 100.0f;
|
||||
const std::string mode = Host::GetBaseStringSettingValue("UI", "GameListBackgroundMode", "fit");
|
||||
const u8 alpha = static_cast<u8>(Host::GetBaseFloatSettingValue("UI", "FSUIBackgroundOpacity", 100.0f) * 2.55f);
|
||||
const std::string mode = Host::GetBaseStringSettingValue("UI", "FSUIBackgroundMode", "fit");
|
||||
|
||||
const float tex_width = static_cast<float>(s_custom_background_texture->GetWidth());
|
||||
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")
|
||||
{
|
||||
// stretch to fill entire display (ignores aspect ratio)
|
||||
img_min = ImVec2(0.0f, 0.0f);
|
||||
img_max = display_size;
|
||||
bg_draw_list->AddImage(tex_id, ImVec2(0.0f, 0.0f), display_size, ImVec2(0.0f, 0.0f), ImVec2(1.0f, 1.0f), col);
|
||||
}
|
||||
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_y = (display_size.y - scaled_height) * 0.5f;
|
||||
|
||||
img_min = ImVec2(offset_x, offset_y);
|
||||
img_max = ImVec2(offset_x + scaled_width, offset_y + scaled_height);
|
||||
bg_draw_list->AddImage(tex_id,
|
||||
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
|
||||
{
|
||||
@ -1821,19 +1885,11 @@ void FullscreenUI::DrawCustomBackground()
|
||||
const float offset_x = (display_size.x - scaled_width) * 0.5f;
|
||||
const float offset_y = (display_size.y - scaled_height) * 0.5f;
|
||||
|
||||
img_min = ImVec2(offset_x, offset_y);
|
||||
img_max = ImVec2(offset_x + scaled_width, offset_y + scaled_height);
|
||||
bg_draw_list->AddImage(tex_id,
|
||||
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)
|
||||
{
|
||||
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; });
|
||||
};
|
||||
|
||||
@ -3629,7 +3685,7 @@ void FullscreenUI::DrawSettingsWindow()
|
||||
ImVec2(io.DisplaySize.x, LayoutScale(LAYOUT_MENU_BUTTON_HEIGHT_NO_SUMMARY) +
|
||||
(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 content_bg_alpha = using_custom_bg ? 0.0f : (VMManager::HasValidVM() ? 0.90f : 1.0f);
|
||||
SettingsInterface* bsi = GetEditingSettingsInterface();
|
||||
@ -4052,21 +4108,19 @@ void FullscreenUI::DrawInterfaceSettingsPage()
|
||||
|
||||
MenuHeading(FSUI_CSTR("Background"));
|
||||
|
||||
std::string background_path = bsi->GetStringValue("UI", "GameListBackgroundPath", "");
|
||||
const bool background_enabled = bsi->GetBoolValue("UI", "GameListBackgroundEnabled", false);
|
||||
std::string background_path = bsi->GetStringValue("UI", "FSUIBackgroundPath", "");
|
||||
|
||||
std::string background_display = FSUI_STR("None");
|
||||
if (!background_path.empty() && background_enabled)
|
||||
if (!background_path.empty())
|
||||
{
|
||||
background_display = Path::GetFileName(background_path);
|
||||
}
|
||||
|
||||
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()))
|
||||
{
|
||||
OpenFileSelector(FSUI_ICONSTR(ICON_FA_IMAGE, "Select Background Image"), false,
|
||||
[](const std::string& path) {
|
||||
OpenFileSelector(FSUI_ICONSTR(ICON_FA_IMAGE, "Select Background Image"), false, [](const std::string& path) {
|
||||
if (!path.empty())
|
||||
{
|
||||
{
|
||||
@ -4074,23 +4128,20 @@ void FullscreenUI::DrawInterfaceSettingsPage()
|
||||
SettingsInterface* bsi = GetEditingSettingsInterface(false);
|
||||
|
||||
std::string relative_path = Path::MakeRelative(path, EmuFolders::DataRoot);
|
||||
bsi->SetStringValue("UI", "GameListBackgroundPath", relative_path.c_str());
|
||||
bsi->SetBoolValue("UI", "GameListBackgroundEnabled", true);
|
||||
bsi->SetStringValue("UI", "FSUIBackgroundPath", relative_path.c_str());
|
||||
bsi->SetBoolValue("UI", "FSUIBackgroundEnabled", true);
|
||||
SetSettingsChanged(bsi);
|
||||
}
|
||||
|
||||
LoadCustomBackground();
|
||||
}
|
||||
CloseFileSelector();
|
||||
},
|
||||
GetImageFileFilters());
|
||||
CloseFileSelector(); }, GetImageFileFilters());
|
||||
}
|
||||
|
||||
if (MenuButton(FSUI_ICONSTR(ICON_FA_XMARK, "Clear Background Image"),
|
||||
FSUI_CSTR("Removes the custom background image.")))
|
||||
{
|
||||
bsi->DeleteValue("UI", "GameListBackgroundPath");
|
||||
bsi->SetBoolValue("UI", "GameListBackgroundEnabled", false);
|
||||
bsi->DeleteValue("UI", "FSUIBackgroundPath");
|
||||
SetSettingsChanged(bsi);
|
||||
|
||||
s_custom_background_texture.reset();
|
||||
@ -4100,21 +4151,25 @@ void FullscreenUI::DrawInterfaceSettingsPage()
|
||||
|
||||
DrawIntRangeSetting(bsi, FSUI_ICONSTR(ICON_FA_DROPLET, "Background Opacity"),
|
||||
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[] = {
|
||||
FSUI_NSTR("Fit"),
|
||||
FSUI_NSTR("Fill"),
|
||||
FSUI_NSTR("Stretch"),
|
||||
FSUI_NSTR("Center"),
|
||||
FSUI_NSTR("Tile"),
|
||||
};
|
||||
static constexpr const char* s_background_mode_values[] = {
|
||||
"fit",
|
||||
"fill",
|
||||
"stretch",
|
||||
"center",
|
||||
"tile",
|
||||
};
|
||||
DrawStringListSetting(bsi, FSUI_ICONSTR(ICON_FA_EXPAND, "Background Mode"),
|
||||
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"));
|
||||
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);
|
||||
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);
|
||||
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"),
|
||||
FSUI_CSTR("Determines whether a prompt will be displayed to confirm shutting down the emulator/game when the hotkey is pressed."),
|
||||
"UI", "ConfirmShutdown", true);
|
||||
@ -4162,7 +4219,7 @@ void FullscreenUI::DrawInterfaceSettingsPage()
|
||||
}
|
||||
|
||||
SmallStackString<256> swap_summery;
|
||||
swap_summery.format(FSUI_FSTR("Uses {} as confirm when using a controller"), ICON_PF_BUTTON_CIRCLE);
|
||||
swap_summery.format(FSUI_FSTR("Uses {} as confirm when using a controller."), ICON_PF_BUTTON_CIRCLE);
|
||||
if (MenuButtonWithValue(FSUI_ICONSTR(ICON_FA_GAMEPAD, "Swap OK/Cancel in Big Picture Mode"), swap_summery.c_str(),
|
||||
(swap_index < std::size(swap_values)) ? Host::TranslateToCString(TR_CONTEXT, swap_names[swap_index]) : FSUI_CSTR("Unknown")))
|
||||
{
|
||||
@ -4230,6 +4287,8 @@ void FullscreenUI::DrawInterfaceSettingsPage()
|
||||
true);
|
||||
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);
|
||||
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"));
|
||||
DrawIntSpinBoxSetting(bsi, FSUI_ICONSTR(ICON_FA_MAGNIFYING_GLASS, "OSD Scale"),
|
||||
@ -4268,7 +4327,7 @@ void FullscreenUI::DrawInterfaceSettingsPage()
|
||||
FSUI_CSTR("Shows the number of internal video frames displayed per second by the system."),
|
||||
"EmuCore/GS", "OsdShowFPS", false);
|
||||
DrawToggleSetting(bsi, FSUI_ICONSTR(ICON_FA_CLAPPERBOARD, "Show VPS"),
|
||||
FSUI_CSTR("Shows the number of V-syncs performed per second by the system."), "EmuCore/GS", "OsdShowVPS", false);
|
||||
FSUI_CSTR("Shows the number of Vsyncs performed per second by the system."), "EmuCore/GS", "OsdShowVPS", false);
|
||||
DrawToggleSetting(bsi, FSUI_ICONSTR(ICON_PF_MONITOR_CODE, "Show Resolution"),
|
||||
FSUI_CSTR("Shows the internal resolution of the game."), "EmuCore/GS", "OsdShowResolution", false);
|
||||
DrawToggleSetting(bsi, FSUI_ICONSTR(ICON_FA_COMPUTER, "Show Hardware Info"),
|
||||
@ -4723,9 +4782,9 @@ void FullscreenUI::DrawGraphicsSettingsPage(SettingsInterface* bsi, bool show_ad
|
||||
FSUI_NSTR("Disabled (Ignore Transfers)"),
|
||||
};
|
||||
static constexpr const char* s_screenshot_sizes[] = {
|
||||
FSUI_NSTR("Screen Resolution"),
|
||||
FSUI_NSTR("Internal Resolution"),
|
||||
FSUI_NSTR("Internal Resolution (Aspect Uncorrected)"),
|
||||
FSUI_NSTR("Display Resolution (Aspect Corrected)"),
|
||||
FSUI_NSTR("Internal Resolution (Aspect Corrected)"),
|
||||
FSUI_NSTR("Internal Resolution (No Aspect Correction)"),
|
||||
};
|
||||
static constexpr const char* s_screenshot_formats[] = {
|
||||
FSUI_NSTR("PNG"),
|
||||
@ -4965,7 +5024,7 @@ void FullscreenUI::DrawGraphicsSettingsPage(SettingsInterface* bsi, bool show_ad
|
||||
"UserHacks_native_scaling", 0, s_native_scaling_options, std::size(s_native_scaling_options), true);
|
||||
DrawIntListSetting(bsi, FSUI_CSTR("Round Sprite"), FSUI_CSTR("Adjusts sprite coordinates."), "EmuCore/GS",
|
||||
"UserHacks_round_sprite_offset", 0, s_round_sprite_options, std::size(s_round_sprite_options), true);
|
||||
DrawIntListSetting(bsi, FSUI_CSTR("Bilinear Upscale"),
|
||||
DrawIntListSetting(bsi, FSUI_CSTR("Bilinear Dirty Upscale"),
|
||||
FSUI_CSTR("Can smooth out textures due to be bilinear filtered when upscaling. E.g. Brave sun glare."), "EmuCore/GS",
|
||||
"UserHacks_BilinearHack", static_cast<int>(GSBilinearDirtyMode::Automatic), s_bilinear_dirty_options,
|
||||
std::size(s_bilinear_dirty_options), true);
|
||||
@ -5141,7 +5200,7 @@ void FullscreenUI::DrawAudioSettingsPage()
|
||||
|
||||
DrawEnumSetting(
|
||||
bsi, FSUI_ICONSTR(ICON_FA_VOLUME_OFF, "Audio Backend"),
|
||||
FSUI_CSTR("The audio backend determines how frames produced by the emulator are submitted to the host."), "SPU2/Output",
|
||||
FSUI_CSTR("Determines how audio frames produced by the emulator are submitted to the host."), "SPU2/Output",
|
||||
"Backend", Pcsx2Config::SPU2Options::DEFAULT_BACKEND, &AudioStream::ParseBackendName, &AudioStream::GetBackendName,
|
||||
&AudioStream::GetBackendDisplayName, AudioBackend::Count);
|
||||
DrawEnumSetting(bsi, FSUI_ICONSTR(ICON_PF_SPEAKER_ALT, "Expansion"),
|
||||
@ -5196,7 +5255,7 @@ void FullscreenUI::DrawMemoryCardSettingsPage()
|
||||
std::string file_key(fmt::format("Slot{}_Filename", port + 1));
|
||||
|
||||
DrawToggleSetting(bsi,
|
||||
SmallString::from_format(fmt::runtime(FSUI_ICONSTR_S(ICON_PF_MEMORY_CARD, "Card Enabled", "##card_enabled_{}")), port),
|
||||
SmallString::from_format(fmt::runtime(FSUI_ICONSTR_S(ICON_PF_MEMORY_CARD, "Memory Card Enabled", "##card_enabled_{}")), port),
|
||||
FSUI_CSTR("If not set, this card will be considered unplugged."), "MemoryCards", enable_key.c_str(), true);
|
||||
|
||||
const bool enabled = GetEffectiveBoolSetting(bsi, "MemoryCards", enable_key.c_str(), true);
|
||||
@ -6613,7 +6672,7 @@ void FullscreenUI::DrawAdvancedSettingsPage()
|
||||
FSUI_CSTR("Performs just-in-time binary translation of 32-bit MIPS-I machine code to native code."), "EmuCore/CPU/Recompiler",
|
||||
"EnableIOP", true);
|
||||
|
||||
MenuHeading(FSUI_CSTR("Savestate"));
|
||||
MenuHeading(FSUI_CSTR("Save State Management"));
|
||||
DrawIntListSetting(bsi, FSUI_ICONSTR(ICON_FA_BOX_OPEN, "Compression Method"), FSUI_CSTR("Sets the compression algorithm for savestate."), "EmuCore",
|
||||
"SavestateCompressionType", static_cast<int>(SavestateCompressionMethod::Zstandard), s_savestate_compression_type, std::size(s_savestate_compression_type), true);
|
||||
DrawIntListSetting(bsi, FSUI_ICONSTR(ICON_FA_COMPRESS, "Compression Level"), FSUI_CSTR("Sets the compression level for savestate."), "EmuCore",
|
||||
@ -6631,7 +6690,7 @@ void FullscreenUI::DrawPatchesOrCheatsSettingsPage(bool cheats)
|
||||
{
|
||||
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;
|
||||
const char* section = cheats ? Patch::CHEATS_CONFIG_SECTION : Patch::PATCHES_CONFIG_SECTION;
|
||||
const bool master_enable = cheats ? GetEffectiveBoolSetting(bsi, "EmuCore", "EnableCheats", false) : true;
|
||||
@ -6739,7 +6798,7 @@ void FullscreenUI::DrawGameFixesSettingsPage()
|
||||
FSUI_CSTR("Known to affect following games: Bleach Blade Battlers, Growlanser II and III, Wizardry."), "EmuCore/Gamefixes",
|
||||
"OPHFlagHack", false);
|
||||
DrawToggleSetting(
|
||||
bsi, FSUI_CSTR("Emulate GIF FIFO"), FSUI_CSTR("Correct but slower. Known to affect the following games: Fifa Street 2."), "EmuCore/Gamefixes", "GIFFIFOHack", false);
|
||||
bsi, FSUI_CSTR("Emulate GIF FIFO"), FSUI_CSTR("Correct but slower. Known to affect the following games: FIFA Street 2."), "EmuCore/Gamefixes", "GIFFIFOHack", false);
|
||||
DrawToggleSetting(bsi, FSUI_CSTR("DMA Busy Hack"),
|
||||
FSUI_CSTR("Known to affect following games: Mana Khemia 1, Metal Saga, Pilot Down Behind Enemy Lines."), "EmuCore/Gamefixes",
|
||||
"DMABusyHack", false);
|
||||
@ -7343,9 +7402,9 @@ void FullscreenUI::DrawSaveStateSelector(bool is_loading)
|
||||
false, is_loading ? !Achievements::IsHardcoreModeActive() : true, LAYOUT_MENU_BUTTON_HEIGHT_NO_SUMMARY))
|
||||
{
|
||||
if (is_loading)
|
||||
DoLoadState(std::move(entry.path));
|
||||
DoLoadState(std::move(entry.path), entry.slot, false);
|
||||
else
|
||||
Host::RunOnCPUThread([slot = entry.slot]() { VMManager::SaveStateToSlot(slot); });
|
||||
DoSaveState(entry.slot);
|
||||
|
||||
CloseSaveStateSelector();
|
||||
ReturnToMainWindow();
|
||||
@ -7481,9 +7540,9 @@ void FullscreenUI::DrawSaveStateSelector(bool is_loading)
|
||||
if (pressed)
|
||||
{
|
||||
if (is_loading)
|
||||
DoLoadState(entry.path);
|
||||
DoLoadState(entry.path, entry.slot, false);
|
||||
else
|
||||
Host::RunOnCPUThread([slot = entry.slot]() { VMManager::SaveStateToSlot(slot); });
|
||||
DoSaveState(entry.slot);
|
||||
|
||||
CloseSaveStateSelector();
|
||||
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())
|
||||
{
|
||||
Error error;
|
||||
if (!VMManager::LoadState(path.c_str(), &error))
|
||||
{
|
||||
MTGS::RunOnGSThread([error = std::move(error)]() {
|
||||
ImGuiFullscreen::OpenInfoMessageDialog(
|
||||
FSUI_ICONSTR(ICON_FA_TRIANGLE_EXCLAMATION, "Failed to Load State"),
|
||||
error.GetDescription());
|
||||
});
|
||||
ReportStateLoadError(error.GetDescription(), slot, backup);
|
||||
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()
|
||||
{
|
||||
const int sort = Host::GetBaseIntSettingValue("UI", "FullscreenUIGameSort", 0);
|
||||
@ -9167,6 +9232,75 @@ void FullscreenUI::DrawAchievementsSettingsPage(std::unique_lock<std::mutex>& se
|
||||
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
|
||||
// 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", "This game has no achievements.");
|
||||
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", "Launch a game from images scanned from your game directories.");
|
||||
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", "Show a save state selector UI when switching slots instead of showing a notification bubble.");
|
||||
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", "Sets the transparency of the custom 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 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", "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", "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");
|
||||
@ -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", "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", "Automatically starts Big Picture Mode instead of the regular Qt interface when PCSX2 launches.");
|
||||
TRANSLATE_NOOP("FullscreenUI", "On-Screen Display");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Determines how large the on-screen messages and monitors are.");
|
||||
TRANSLATE_NOOP("FullscreenUI", "%d%%");
|
||||
@ -9298,7 +9436,7 @@ TRANSLATE_NOOP("FullscreenUI", "Determines where performance statistics are posi
|
||||
TRANSLATE_NOOP("FullscreenUI", "Shows the current PCSX2 version.");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Shows the current emulation speed of the system as a percentage.");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Shows the number of internal video frames displayed per second by the system.");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Shows the number of V-syncs performed per second by the system.");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Shows the number of Vsyncs performed per second by the system.");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Shows the internal resolution of the game.");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Shows the current system CPU and GPU information.");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Shows statistics about the emulated GS such as primitives and draw calls.");
|
||||
@ -9410,7 +9548,7 @@ TRANSLATE_NOOP("FullscreenUI", "Native Scaling");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Attempt to do rescaling at native resolution.");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Round Sprite");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Adjusts sprite coordinates.");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Bilinear Upscale");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Bilinear Dirty Upscale");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Can smooth out textures due to be bilinear filtered when upscaling. E.g. Brave sun glare.");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Texture Offset X");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Adjusts target texture offsets.");
|
||||
@ -9476,7 +9614,7 @@ TRANSLATE_NOOP("FullscreenUI", "Controls the volume of the audio played on the h
|
||||
TRANSLATE_NOOP("FullscreenUI", "Controls the volume of the audio played on the host when fast forwarding.");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Prevents the emulator from producing any audible sound.");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Backend Settings");
|
||||
TRANSLATE_NOOP("FullscreenUI", "The audio backend determines how frames produced by the emulator are submitted to the host.");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Determines how audio frames produced by the emulator are submitted to the host.");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Determines how audio is expanded from stereo to surround for supported games.");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Changes when SPU samples are generated relative to system emulation.");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Determines the amount of audio buffered before being pulled by the host API.");
|
||||
@ -9570,7 +9708,7 @@ TRANSLATE_NOOP("FullscreenUI", "Runs VU1 instantly. Provides a modest speed impr
|
||||
TRANSLATE_NOOP("FullscreenUI", "I/O Processor");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Enable IOP Recompiler");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Performs just-in-time binary translation of 32-bit MIPS-I machine code to native code.");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Savestate");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Save State Management");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Sets the compression algorithm for savestate.");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Sets the compression level for savestate.");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Graphics");
|
||||
@ -9600,7 +9738,7 @@ TRANSLATE_NOOP("FullscreenUI", "Good for cache emulation problems. Known to affe
|
||||
TRANSLATE_NOOP("FullscreenUI", "OPH Flag Hack");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Known to affect following games: Bleach Blade Battlers, Growlanser II and III, Wizardry.");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Emulate GIF FIFO");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Correct but slower. Known to affect the following games: Fifa Street 2.");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Correct but slower. Known to affect the following games: FIFA Street 2.");
|
||||
TRANSLATE_NOOP("FullscreenUI", "DMA Busy Hack");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Known to affect following games: Mana Khemia 1, Metal Saga, Pilot Down Behind Enemy Lines.");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Delay VIF1 Stalls");
|
||||
@ -9679,7 +9817,7 @@ TRANSLATE_NOOP("FullscreenUI", "Automatic mapping completed for {}.");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Automatic mapping failed for {}.");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Game settings initialized with global settings for '{}'.");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Game settings have been cleared for '{}'.");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Uses {} as confirm when using a controller");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Uses {} as confirm when using a controller.");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Swaps both {}/{} (When Swap OK/Cancel is set to automatic) and {}/{} buttons");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Slot {}");
|
||||
TRANSLATE_NOOP("FullscreenUI", "{} (Current)");
|
||||
@ -9715,6 +9853,9 @@ TRANSLATE_NOOP("FullscreenUI", "Last Played: {}");
|
||||
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", "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", "Top: ");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Right: ");
|
||||
@ -9748,13 +9889,14 @@ TRANSLATE_NOOP("FullscreenUI", "AMOLED");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Fit");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Fill");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Stretch");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Center");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Tile");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Enabled");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Disabled");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Top Left");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Top Center");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Top Right");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Center Left");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Center");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Center Right");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Bottom Left");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Bottom Center");
|
||||
@ -9854,9 +9996,9 @@ TRANSLATE_NOOP("FullscreenUI", "Accurate (Recommended)");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Disable Readbacks (Synchronize GS Thread)");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Unsynchronized (Non-Deterministic)");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Disabled (Ignore Transfers)");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Screen Resolution");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Internal Resolution");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Internal Resolution (Aspect Uncorrected)");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Display Resolution (Aspect Corrected)");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Internal Resolution (Aspect Corrected)");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Internal Resolution (No Aspect Correction)");
|
||||
TRANSLATE_NOOP("FullscreenUI", "PNG");
|
||||
TRANSLATE_NOOP("FullscreenUI", "JPEG");
|
||||
TRANSLATE_NOOP("FullscreenUI", "WebP");
|
||||
@ -9974,6 +10116,7 @@ TRANSLATE_NOOP("FullscreenUI", "Pause On Start");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Pause On Focus Loss");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Pause On Controller Disconnection");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Pause On Menu");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Prompt On State Load/Save Failure");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Confirm Shutdown");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Save State On Shutdown");
|
||||
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", "Double-Click Toggles Fullscreen");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Hide Cursor In Fullscreen");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Start Big Picture UI");
|
||||
TRANSLATE_NOOP("FullscreenUI", "OSD Scale");
|
||||
TRANSLATE_NOOP("FullscreenUI", "OSD Messages Position");
|
||||
TRANSLATE_NOOP("FullscreenUI", "OSD Performance Position");
|
||||
@ -10037,6 +10181,7 @@ TRANSLATE_NOOP("FullscreenUI", "Integer Upscaling");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Screen Offsets");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Show Overscan");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Anti-Blur");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Internal Resolution");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Bilinear Filtering");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Trilinear Filtering");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Anisotropic Filtering");
|
||||
@ -10162,7 +10307,6 @@ TRANSLATE_NOOP("FullscreenUI", "Delete Save");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Close Menu");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Default Boot");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Delete State");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Failed to Load State");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Full Boot");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Reset Play Time");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Confirm Reset");
|
||||
@ -10207,7 +10351,7 @@ TRANSLATE_NOOP("FullscreenUI", "Not Logged In");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Game: {0} ({1})");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Rich presence inactive or unsupported.");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Game not loaded or no RetroAchievements available.");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Card Enabled");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Memory Card Enabled");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Card Name");
|
||||
TRANSLATE_NOOP("FullscreenUI", "Eject Card");
|
||||
// TRANSLATION-STRING-AREA-END
|
||||
|
||||
@ -10,6 +10,7 @@
|
||||
#include <ctime>
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
|
||||
struct Pcsx2Config;
|
||||
|
||||
@ -26,6 +27,8 @@ namespace FullscreenUI
|
||||
void OpenPauseMenu();
|
||||
bool OpenAchievementsWindow();
|
||||
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.
|
||||
bool IsAchievementsWindowOpen();
|
||||
|
||||
@ -2396,6 +2396,7 @@ void ImGuiFullscreen::DrawChoiceDialog()
|
||||
ImGui::PushStyleColor(ImGuiCol_Text, UIPrimaryTextColor);
|
||||
ImGui::PushStyleColor(ImGuiCol_TitleBg, UIPrimaryDarkColor);
|
||||
ImGui::PushStyleColor(ImGuiCol_TitleBgActive, UIPrimaryColor);
|
||||
ImGui::PushStyleColor(ImGuiCol_PopupBg, UIPopupBackgroundColor);
|
||||
|
||||
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;
|
||||
@ -2464,7 +2465,7 @@ void ImGuiFullscreen::DrawChoiceDialog()
|
||||
is_open = false;
|
||||
}
|
||||
|
||||
ImGui::PopStyleColor(3);
|
||||
ImGui::PopStyleColor(4);
|
||||
ImGui::PopStyleVar(3);
|
||||
ImGui::PopFont();
|
||||
|
||||
@ -2523,7 +2524,7 @@ void ImGuiFullscreen::DrawInputDialog()
|
||||
ImGui::PushStyleColor(ImGuiCol_Text, UIPrimaryTextColor);
|
||||
ImGui::PushStyleColor(ImGuiCol_TitleBg, UIPrimaryDarkColor);
|
||||
ImGui::PushStyleColor(ImGuiCol_TitleBgActive, UIPrimaryColor);
|
||||
ImGui::PushStyleColor(ImGuiCol_PopupBg, UIBackgroundColor);
|
||||
ImGui::PushStyleColor(ImGuiCol_PopupBg, UIPopupBackgroundColor);
|
||||
|
||||
bool is_open = true;
|
||||
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_TitleBg, UIPrimaryDarkColor);
|
||||
ImGui::PushStyleColor(ImGuiCol_TitleBgActive, UIPrimaryColor);
|
||||
ImGui::PushStyleColor(ImGuiCol_PopupBg, UIPopupBackgroundColor);
|
||||
|
||||
bool is_open = true;
|
||||
const u32 flags = ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove |
|
||||
@ -2745,7 +2747,7 @@ void ImGuiFullscreen::DrawMessageDialog()
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
|
||||
ImGui::PopStyleColor(3);
|
||||
ImGui::PopStyleColor(4);
|
||||
ImGui::PopStyleVar(4);
|
||||
ImGui::PopFont();
|
||||
|
||||
|
||||
@ -1382,8 +1382,7 @@ void SaveStateSelectorUI::LoadCurrentSlot()
|
||||
Host::RunOnCPUThread([slot = GetCurrentSlot()]() {
|
||||
Error error;
|
||||
if (!VMManager::LoadStateFromSlot(slot, false, &error))
|
||||
Host::AddIconOSDMessage("LoadStateFromSlot", ICON_FA_TRIANGLE_EXCLAMATION,
|
||||
error.GetDescription(), Host::OSD_INFO_DURATION);
|
||||
FullscreenUI::ReportStateLoadError(error.GetDescription(), slot, false);
|
||||
});
|
||||
Close();
|
||||
}
|
||||
@ -1393,8 +1392,7 @@ void SaveStateSelectorUI::LoadCurrentBackupSlot()
|
||||
Host::RunOnCPUThread([slot = GetCurrentSlot()]() {
|
||||
Error error;
|
||||
if (!VMManager::LoadStateFromSlot(slot, true, &error))
|
||||
Host::AddIconOSDMessage("LoadStateFromSlot", ICON_FA_TRIANGLE_EXCLAMATION,
|
||||
error.GetDescription(), Host::OSD_INFO_DURATION);
|
||||
FullscreenUI::ReportStateLoadError(error.GetDescription(), slot, true);
|
||||
});
|
||||
Close();
|
||||
}
|
||||
@ -1402,7 +1400,9 @@ void SaveStateSelectorUI::LoadCurrentBackupSlot()
|
||||
void SaveStateSelectorUI::SaveCurrentSlot()
|
||||
{
|
||||
Host::RunOnCPUThread([slot = GetCurrentSlot()]() {
|
||||
VMManager::SaveStateToSlot(slot);
|
||||
VMManager::SaveStateToSlot(slot, true, [slot](const std::string& error) {
|
||||
FullscreenUI::ReportStateSaveError(error, slot);
|
||||
});
|
||||
});
|
||||
Close();
|
||||
}
|
||||
|
||||
@ -765,9 +765,9 @@ std::optional<InputBindingKey> SDLInputSource::ParseKeyString(const std::string_
|
||||
{
|
||||
shown_prompt = true;
|
||||
Host::ReportInfoAsync(TRANSLATE("SDLInputSource", "SDL3 Migration"),
|
||||
TRANSLATE("SDLInputSource", "As part of our upgrade to SDL3, we've had to migrate your binds\n"
|
||||
"Your controller did not match the Xbox layout and may need rebinding\n"
|
||||
"Please verify your controller settings and amend if required"));
|
||||
TRANSLATE("SDLInputSource", "As part of our upgrade to SDL3, we've had to migrate your binds.\n"
|
||||
"Your controller did not match the Xbox layout and may need rebinding.\n"
|
||||
"Please verify your controller settings and amend if required."));
|
||||
|
||||
// Also apply BPM setting for legacy binds
|
||||
// We assume this is a Nintendo controller, BPM will check if it is
|
||||
|
||||
@ -6,6 +6,7 @@
|
||||
#include "Host.h"
|
||||
#include "Memory.h"
|
||||
#include "Elfheader.h"
|
||||
#include "SaveState.h"
|
||||
#include "PINE.h"
|
||||
#include "VMManager.h"
|
||||
#include "common/Error.h"
|
||||
@ -19,7 +20,6 @@
|
||||
#include <thread>
|
||||
|
||||
#include "fmt/format.h"
|
||||
#include "IconsFontAwesome6.h"
|
||||
|
||||
#if defined(_WIN32)
|
||||
#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;
|
||||
if (!SafetyChecks(buf_cnt, 1, ret_cnt, 0, buf_size)) [[unlikely]]
|
||||
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;
|
||||
break;
|
||||
}
|
||||
@ -659,8 +663,7 @@ PINEServer::IPCBuffer PINEServer::ParseCommand(std::span<u8> buf, std::vector<u8
|
||||
Host::RunOnCPUThread([slot = FromSpan<u8>(buf, buf_cnt)] {
|
||||
Error state_error;
|
||||
if (!VMManager::LoadStateFromSlot(slot, false, &state_error))
|
||||
Host::AddIconOSDMessage("LoadStateFromSlot", ICON_FA_TRIANGLE_EXCLAMATION,
|
||||
state_error.GetDescription(), Host::OSD_INFO_DURATION);
|
||||
SaveState_ReportLoadErrorOSD(state_error.GetDescription(), slot, false);
|
||||
});
|
||||
buf_cnt += 1;
|
||||
break;
|
||||
|
||||
185
pcsx2/Patch.cpp
185
pcsx2/Patch.cpp
@ -51,7 +51,7 @@ namespace Patch
|
||||
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*, 9> s_type_to_string = {
|
||||
{"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);
|
||||
};
|
||||
|
||||
using PatchList = std::vector<PatchGroup>;
|
||||
using ActivePatchList = std::vector<const PatchCommand*>;
|
||||
using EnablePatchList = std::vector<std::string>;
|
||||
|
||||
namespace PatchFunc
|
||||
{
|
||||
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,
|
||||
const std::span<const PatchTextTable>& Table);
|
||||
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 std::string GetPnachTemplate(
|
||||
const std::string_view serial, u32 crc, bool include_serial, bool add_wildcard, bool all_crcs);
|
||||
static std::vector<std::string> FindPatchFilesOnDisk(
|
||||
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 PatchList& patches, const std::string_view patchName);
|
||||
static bool ContainsPatchName(const std::vector<PatchInfo>& patches, const std::string_view patchName);
|
||||
static bool ContainsPatchName(const std::vector<PatchGroup>& patches, const std::string_view patchName);
|
||||
|
||||
template <typename 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 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 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 ApplyDynaPatch(const DynamicPatch& patch, u32 address);
|
||||
@ -175,21 +171,21 @@ namespace Patch
|
||||
const char* PATCH_DISABLE_CONFIG_KEY = "Disable";
|
||||
|
||||
static zip_t* s_patches_zip;
|
||||
static PatchList s_gamedb_patches;
|
||||
static PatchList s_game_patches;
|
||||
static PatchList s_cheat_patches;
|
||||
static std::vector<PatchGroup> s_gamedb_patches;
|
||||
static std::vector<PatchGroup> s_game_patches;
|
||||
static std::vector<PatchGroup> s_cheat_patches;
|
||||
|
||||
static u32 s_gamedb_counts = 0;
|
||||
static u32 s_patches_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_pnach_dynamic_patches;
|
||||
static EnablePatchList s_enabled_cheats;
|
||||
static EnablePatchList s_enabled_patches;
|
||||
static EnablePatchList s_just_enabled_cheats;
|
||||
static EnablePatchList s_just_enabled_patches;
|
||||
static std::vector<std::string> s_enabled_cheats;
|
||||
static std::vector<std::string> s_enabled_patches;
|
||||
static std::vector<std::string> s_just_enabled_cheats;
|
||||
static std::vector<std::string> s_just_enabled_patches;
|
||||
static u32 s_patches_crc;
|
||||
static std::optional<float> s_override_aspect_ratio;
|
||||
static std::optional<GSInterlaceMode> s_override_interlace_mode;
|
||||
@ -218,7 +214,7 @@ void Patch::TrimPatchLine(std::string& buffer)
|
||||
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 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);
|
||||
}
|
||||
|
||||
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();
|
||||
|
||||
@ -264,7 +260,7 @@ u32 Patch::LoadPatchesFromString(PatchList* patch_list, const std::string& patch
|
||||
// Ungrouped/legacy patches should merge with other ungrouped patches.
|
||||
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(); });
|
||||
if (ungrouped_patch != patch_list->end())
|
||||
{
|
||||
@ -407,7 +403,7 @@ std::vector<std::string> Patch::FindPatchFilesOnDisk(const std::string_view seri
|
||||
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 patch.name == patchName;
|
||||
@ -491,11 +487,15 @@ bool Patch::PatchStringHasUnlabelledPatch(const std::string& pnach_data)
|
||||
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::string line;
|
||||
PatchInfo current_patch;
|
||||
|
||||
std::optional<patch_place_type> last_place;
|
||||
bool unknown_place = false;
|
||||
|
||||
while (std::getline(ss, line))
|
||||
{
|
||||
TrimPatchLine(line);
|
||||
@ -522,6 +522,8 @@ void Patch::ExtractPatchInfo(PatchInfoList* dst, const std::string& pnach_data,
|
||||
}
|
||||
}
|
||||
current_patch = {};
|
||||
last_place = std::nullopt;
|
||||
unknown_place = false;
|
||||
}
|
||||
|
||||
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.
|
||||
// Use comment for description if it hasn't been otherwise specified.
|
||||
if (key == "author")
|
||||
{
|
||||
current_patch.author = value;
|
||||
}
|
||||
else if (key == "description")
|
||||
{
|
||||
current_patch.description = value;
|
||||
}
|
||||
else if (key == "comment" && current_patch.description.empty())
|
||||
{
|
||||
current_patch.description = value;
|
||||
else if (key == "patch" && !has_patch && 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.
|
||||
@ -570,9 +611,9 @@ std::string_view Patch::PatchInfo::GetNameParentPart() const
|
||||
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)
|
||||
*num_unlabelled_patches = 0;
|
||||
@ -592,14 +633,14 @@ std::string Patch::GetPnachFilename(const std::string_view serial, u32 crc, bool
|
||||
|
||||
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())
|
||||
s_enabled_cheats = Host::GetStringListSetting(CHEATS_CONFIG_SECTION, PATCH_ENABLE_CONFIG_KEY);
|
||||
else
|
||||
s_enabled_cheats = {};
|
||||
|
||||
const EnablePatchList 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> prev_enabled_patches = std::exchange(s_enabled_patches, Host::GetStringListSetting(PATCHES_CONFIG_SECTION, PATCH_ENABLE_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.
|
||||
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;
|
||||
for (const PatchGroup& p : patches)
|
||||
for (const PatchGroup& p : *patches)
|
||||
{
|
||||
// For compatibility, we auto enable anything that's not labelled.
|
||||
// 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: {}",
|
||||
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)
|
||||
{
|
||||
// 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());
|
||||
|
||||
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)
|
||||
@ -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;
|
||||
}
|
||||
|
||||
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)]() {
|
||||
for (const PatchCommand* i : patches)
|
||||
// Don't pass pointers to patch objects themselves here just in case the
|
||||
// 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;
|
||||
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;
|
||||
if (gp_count > 0)
|
||||
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;
|
||||
if (p_count > 0)
|
||||
message.append_format("{}{}", message.empty() ? "" : "\n",
|
||||
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;
|
||||
if (c_count > 0)
|
||||
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())
|
||||
{
|
||||
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;
|
||||
}
|
||||
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);
|
||||
}
|
||||
|
||||
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
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
// place is not one of the supported values then the patch line is never applied.
|
||||
// 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)
|
||||
// 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:
|
||||
// - 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.
|
||||
// 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
|
||||
// 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
|
||||
{
|
||||
PPT_ONCE_ON_LOAD = 0,
|
||||
PPT_CONTINUOUSLY = 1,
|
||||
PPT_COMBINED_0_1 = 2,
|
||||
PPT_ON_LOAD_OR_WHEN_ENABLED = 3,
|
||||
|
||||
PPT_END_MARKER
|
||||
};
|
||||
@ -56,12 +55,14 @@ namespace Patch
|
||||
std::string description;
|
||||
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 GetNameParentPart() const;
|
||||
};
|
||||
|
||||
using PatchInfoList = std::vector<PatchInfo>;
|
||||
|
||||
struct DynamicPatchEntry
|
||||
{
|
||||
u32 offset;
|
||||
@ -80,7 +81,7 @@ namespace Patch
|
||||
extern const char* PATCH_ENABLE_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.
|
||||
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 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.
|
||||
// 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,
|
||||
@ -112,4 +120,6 @@ namespace Patch
|
||||
extern u32 GetAllActivePatchesCount();
|
||||
|
||||
extern bool IsGloballyToggleablePatch(const PatchInfo& patch_info);
|
||||
|
||||
extern const char* PlaceToString(std::optional<patch_place_type> place);
|
||||
} // namespace Patch
|
||||
|
||||
@ -56,8 +56,9 @@ bool InputRecording::create(const std::string& fileName, const bool fromSaveStat
|
||||
m_initial_load_complete = true;
|
||||
m_watching_for_rerecords = true;
|
||||
setStartingFrame(g_FrameCount);
|
||||
// TODO - error handling
|
||||
VMManager::SaveState(savestatePath.c_str());
|
||||
VMManager::SaveState(savestatePath.c_str(), true, false, [](const std::string& error) {
|
||||
SaveState_ReportSaveErrorOSD(error, std::nullopt);
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -93,7 +94,7 @@ bool InputRecording::play(const std::string& filename)
|
||||
if (!FileSystem::FileExists(savestatePath.c_str()))
|
||||
{
|
||||
InputRec::consoleLog(fmt::format("Could not locate savestate file at location - {}", savestatePath));
|
||||
InputRec::log(TRANSLATE_STR("InputRecording", "Savestate load failed for input recording"), Host::OSD_ERROR_DURATION);
|
||||
InputRec::log(TRANSLATE_STR("InputRecording", "Failed to load state for input recording"), Host::OSD_ERROR_DURATION);
|
||||
m_file.close();
|
||||
return false;
|
||||
}
|
||||
@ -103,7 +104,7 @@ bool InputRecording::play(const std::string& filename)
|
||||
const auto loaded = VMManager::LoadState(savestatePath.c_str());
|
||||
if (!loaded)
|
||||
{
|
||||
InputRec::log(TRANSLATE_STR("InputRecording", "Savestate load failed for input recording, unsupported version?"), Host::OSD_ERROR_DURATION);
|
||||
InputRec::log(TRANSLATE_STR("InputRecording", "Failed to load state for input recording, unsupported version?"), Host::OSD_ERROR_DURATION);
|
||||
m_file.close();
|
||||
m_is_active = false;
|
||||
return false;
|
||||
@ -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 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.recording_active_message = recording_active_message;
|
||||
g_InputRecordingData.frame_data_message = frame_data_message;
|
||||
|
||||
@ -581,9 +581,10 @@ bool Pad::Freeze(StateWrapper& sw)
|
||||
|
||||
const auto& [port, slot] = sioConvertPadToPortAndSlot(unifiedSlot);
|
||||
Host::AddIconOSDMessage(fmt::format("UnfreezePad{}Changed", unifiedSlot), ICON_FA_GAMEPAD,
|
||||
//: {0} and {1} are the port and multitap slot, {2} and {3} are controller types (e.g. "DualShock 2", "Jogcon")
|
||||
fmt::format(TRANSLATE_FS("Pad",
|
||||
"Controller port {0}, slot {1} has a {2} connected, but the save state has a "
|
||||
"{3}.\nEjecting {3} and replacing it with {2}."),
|
||||
"{3}.\nEjecting {2} and replacing it with {3}."),
|
||||
port, slot,
|
||||
GetControllerTypeName(currentPad ? currentPad->GetType() : Pad::ControllerType::NotConnected),
|
||||
GetControllerTypeName(statePadType)));
|
||||
|
||||
@ -38,6 +38,7 @@
|
||||
#include "common/StringUtil.h"
|
||||
#include "common/ZipHelpers.h"
|
||||
|
||||
#include "IconsFontAwesome6.h"
|
||||
#include "fmt/format.h"
|
||||
|
||||
#include <csetjmp>
|
||||
@ -1038,14 +1039,18 @@ static bool SaveState_AddToZip(zip_t* zf, ArchiveEntryList* srclist, SaveStateSc
|
||||
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_source_t* zs = zip_source_file_create(filename, 0, 0, &ze);
|
||||
zip_t* zf = nullptr;
|
||||
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
|
||||
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
|
||||
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);
|
||||
return false;
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
@ -1231,3 +1244,37 @@ bool SaveState_UnzipFromDisk(const std::string& filename, Error* error)
|
||||
PostLoadPrep();
|
||||
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 <memory>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
@ -53,7 +54,9 @@ class ArchiveEntryList;
|
||||
// These functions assume that the caller has paused the core thread.
|
||||
extern std::unique_ptr<ArchiveEntryList> SaveState_DownloadState(Error* error);
|
||||
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_UnzipFromDisk(const std::string& filename, Error* error);
|
||||
|
||||
@ -353,3 +356,6 @@ public:
|
||||
void FreezeMem(void* data, int size) override;
|
||||
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);
|
||||
|
||||
@ -1243,7 +1243,7 @@ namespace usb_msd
|
||||
{
|
||||
static constexpr const SettingInfo settings[] = {
|
||||
{SettingInfo::Type::Path, "ImagePathMsd", TRANSLATE_NOOP("USB", "Image Path"),
|
||||
TRANSLATE_NOOP("USB", "Sets the path to image which will back the virtual mass storage device.")},
|
||||
TRANSLATE_NOOP("USB", "Sets the path to the disk image which will back the virtual mass storage device.")},
|
||||
};
|
||||
return settings;
|
||||
}
|
||||
@ -1251,7 +1251,7 @@ namespace usb_msd
|
||||
{
|
||||
static constexpr const SettingInfo settings[] = {
|
||||
{SettingInfo::Type::Path, "ImagePathMsac", TRANSLATE_NOOP("USB", "Image Path"),
|
||||
TRANSLATE_NOOP("USB", "Sets the path to image which will back the virtual mass storage device.")},
|
||||
TRANSLATE_NOOP("USB", "Sets the path to the disk image which will back the virtual mass storage device.")},
|
||||
};
|
||||
return settings;
|
||||
}
|
||||
|
||||
@ -115,13 +115,13 @@ namespace VMManager
|
||||
|
||||
static std::string GetCurrentSaveStateFileName(s32 slot, bool backup = false);
|
||||
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,
|
||||
std::unique_ptr<SaveStateScreenshotData> screenshot, std::string osd_key, const char* filename,
|
||||
s32 slot_for_message);
|
||||
std::unique_ptr<SaveStateScreenshotData> screenshot, const char* filename,
|
||||
s32 slot_for_message, std::function<void(const std::string&)> error_callback);
|
||||
static void ZipSaveStateOnThread(std::unique_ptr<ArchiveEntryList> elist,
|
||||
std::unique_ptr<SaveStateScreenshotData> screenshot, std::string osd_key, std::string filename,
|
||||
s32 slot_for_message);
|
||||
std::unique_ptr<SaveStateScreenshotData> screenshot, std::string filename,
|
||||
s32 slot_for_message, std::function<void(const std::string&)> error_callback);
|
||||
|
||||
static void LoadSettings();
|
||||
static void LoadCoreSettings(SettingsInterface& si);
|
||||
@ -1617,8 +1617,13 @@ void VMManager::Shutdown(bool save_resume_state)
|
||||
if (!GSDumpReplayer::IsReplayingDump() && save_resume_state)
|
||||
{
|
||||
std::string resume_file_name(GetCurrentSaveStateFileName(-1));
|
||||
if (!resume_file_name.empty() && !DoSaveState(resume_file_name.c_str(), -1, true, false))
|
||||
Console.Error("Failed to save resume state");
|
||||
if (!resume_file_name.empty())
|
||||
{
|
||||
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
|
||||
@ -1829,7 +1834,7 @@ bool VMManager::DoLoadState(const char* filename, Error* error)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
@ -1849,21 +1854,20 @@ bool VMManager::DoLoadState(const char* filename, Error* error)
|
||||
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())
|
||||
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;
|
||||
|
||||
std::unique_ptr<ArchiveEntryList> elist = SaveState_DownloadState(&error);
|
||||
if (!elist)
|
||||
{
|
||||
Host::AddIconOSDMessage(std::move(osd_key), ICON_FA_TRIANGLE_EXCLAMATION,
|
||||
fmt::format(TRANSLATE_FS("VMManager", "Failed to save state: {}."), error.GetDescription()),
|
||||
Host::OSD_ERROR_DURATION);
|
||||
return false;
|
||||
error_callback(error.GetDescription());
|
||||
return;
|
||||
}
|
||||
|
||||
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));
|
||||
if (!FileSystem::RenamePath(filename, backup_filename.c_str()))
|
||||
{
|
||||
Host::AddIconOSDMessage(osd_key, ICON_FA_TRIANGLE_EXCLAMATION,
|
||||
fmt::format(
|
||||
TRANSLATE_FS("VMManager", "Failed to back up old save state {}."), Path::GetFileName(filename)),
|
||||
Host::OSD_ERROR_DURATION);
|
||||
error_callback(fmt::format(
|
||||
TRANSLATE_FS("VMManager", "Cannot back up old save state '{}'."),
|
||||
Path::GetFileName(filename)));
|
||||
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.
|
||||
std::unique_lock lock(s_save_state_threads_mutex);
|
||||
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
|
||||
{
|
||||
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);
|
||||
MemcardBusy::CheckSaveStateDependency();
|
||||
return true;
|
||||
return;
|
||||
}
|
||||
|
||||
void VMManager::ZipSaveState(std::unique_ptr<ArchiveEntryList> elist,
|
||||
std::unique_ptr<SaveStateScreenshotData> screenshot, std::string osd_key, const char* filename,
|
||||
s32 slot_for_message)
|
||||
std::unique_ptr<SaveStateScreenshotData> screenshot, const char* filename,
|
||||
s32 slot_for_message, std::function<void(const std::string&)> error_callback)
|
||||
{
|
||||
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))
|
||||
{
|
||||
error_callback(error.GetDescription());
|
||||
return;
|
||||
}
|
||||
|
||||
if (slot_for_message >= 0 && VMManager::HasValidVM())
|
||||
{
|
||||
Host::AddIconOSDMessage(std::move(osd_key), ICON_FA_FLOPPY_DISK,
|
||||
fmt::format(TRANSLATE_FS("VMManager", "State saved to slot {}."), slot_for_message),
|
||||
Host::AddIconOSDMessage(fmt::format("SaveStateSlot{}", slot_for_message), ICON_FA_FLOPPY_DISK,
|
||||
fmt::format(TRANSLATE_FS("VMManager", "Saved state to slot {}."), slot_for_message),
|
||||
Host::OSD_QUICK_DURATION);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Host::AddIconOSDMessage(std::move(osd_key), ICON_FA_TRIANGLE_EXCLAMATION,
|
||||
fmt::format(TRANSLATE_FS("VMManager", "Failed to save state to slot {}."), slot_for_message,
|
||||
Host::OSD_ERROR_DURATION));
|
||||
}
|
||||
|
||||
DevCon.WriteLn("Zipping save state to '%s' took %.2f ms", filename, timer.GetTimeMilliseconds());
|
||||
}
|
||||
|
||||
void VMManager::ZipSaveStateOnThread(std::unique_ptr<ArchiveEntryList> elist,
|
||||
std::unique_ptr<SaveStateScreenshotData> screenshot, std::string osd_key, std::string filename,
|
||||
s32 slot_for_message)
|
||||
std::unique_ptr<SaveStateScreenshotData> screenshot, std::string filename,
|
||||
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.
|
||||
const auto this_id = std::this_thread::get_id();
|
||||
@ -1985,98 +1989,110 @@ bool VMManager::LoadState(const char* filename, Error* error)
|
||||
if (Achievements::IsHardcoreModeActive())
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
if (MemcardBusy::IsBusy())
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
// TODO: Save the current state so we don't need to reset.
|
||||
if (DoLoadState(filename, error))
|
||||
return true;
|
||||
|
||||
if (!DoLoadState(filename, error))
|
||||
{
|
||||
Reset();
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool VMManager::LoadStateFromSlot(s32 slot, bool backup, Error* error)
|
||||
{
|
||||
const std::string filename = GetCurrentSaveStateFileName(slot, backup);
|
||||
if (filename.empty() || !FileSystem::FileExists(filename.c_str()))
|
||||
{
|
||||
if (backup)
|
||||
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);
|
||||
Error::SetString(error, TRANSLATE_STR("VMManager", "The save slot is empty."));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (Achievements::IsHardcoreModeActive())
|
||||
{
|
||||
if (backup)
|
||||
Error::SetStringFmt(error,
|
||||
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);
|
||||
Error::SetString(error,
|
||||
TRANSLATE_STR("VMManager", "Cannot load state while RetroAchievements Hardcore Mode is active."));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (MemcardBusy::IsBusy())
|
||||
{
|
||||
if (backup)
|
||||
Error::SetStringFmt(error,
|
||||
TRANSLATE_FS("VMManager", "Failed to load save state from backup slot {} (memory card is busy)."), slot);
|
||||
else
|
||||
Error::SetStringFmt(error,
|
||||
TRANSLATE_FS("VMManager", "Failed to load save state from slot {} (memory card is busy)."), slot);
|
||||
Error::SetString(error,
|
||||
TRANSLATE_STR("VMManager",
|
||||
"The memory card is busy, so the state load operation has been cancelled to prevent data loss."));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!DoLoadState(filename.c_str(), error))
|
||||
return false;
|
||||
|
||||
if (backup)
|
||||
{
|
||||
Host::AddIconOSDMessage("LoadStateFromSlot", ICON_FA_FOLDER_OPEN,
|
||||
fmt::format(TRANSLATE_FS("VMManager", "Loading {} from slot {}..."), backup ? TRANSLATE("VMManager", "backup state") : TRANSLATE("VMManager", "state"), slot), Host::OSD_QUICK_DURATION);
|
||||
|
||||
return DoLoadState(filename.c_str(), error);
|
||||
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);
|
||||
}
|
||||
|
||||
bool VMManager::SaveState(const char* filename, bool zip_on_thread, bool backup_old_state)
|
||||
return true;
|
||||
}
|
||||
|
||||
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())
|
||||
{
|
||||
Host::AddIconOSDMessage("LoadStateFromSlot", ICON_FA_TRIANGLE_EXCLAMATION,
|
||||
fmt::format(TRANSLATE_FS("VMManager", "Failed to save state (Memory card is busy)")),
|
||||
Host::OSD_QUICK_DURATION);
|
||||
return false;
|
||||
error_callback(TRANSLATE_STR("VMManager",
|
||||
"The memory card is busy, so the state save operation has been cancelled to prevent data loss."));
|
||||
return;
|
||||
}
|
||||
|
||||
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));
|
||||
if (filename.empty())
|
||||
return false;
|
||||
{
|
||||
error_callback(TRANSLATE_STR("VMManager", "Cannot generate filename for save state."));
|
||||
return;
|
||||
}
|
||||
|
||||
if (MemcardBusy::IsBusy())
|
||||
{
|
||||
Host::AddIconOSDMessage("LoadStateFromSlot", ICON_FA_TRIANGLE_EXCLAMATION,
|
||||
fmt::format(TRANSLATE_FS("VMManager", "Failed to save state to slot {} (Memory card is busy)"), slot),
|
||||
Host::OSD_QUICK_DURATION);
|
||||
return false;
|
||||
error_callback(TRANSLATE_STR("VMManager",
|
||||
"The memory card is busy, so the state save operation has been cancelled to prevent data loss."));
|
||||
return;
|
||||
}
|
||||
|
||||
// if it takes more than a minute.. well.. wtf.
|
||||
Host::AddIconOSDMessage(fmt::format("SaveStateSlot{}", slot), ICON_FA_FLOPPY_DISK,
|
||||
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()
|
||||
@ -2816,8 +2832,8 @@ void VMManager::Internal::EntryPointCompilingOnCPUThread()
|
||||
|
||||
HandleELFChange(true);
|
||||
|
||||
Patch::ApplyLoadedPatches(Patch::PPT_ONCE_ON_LOAD);
|
||||
Patch::ApplyLoadedPatches(Patch::PPT_COMBINED_0_1);
|
||||
Patch::ApplyBootPatches();
|
||||
|
||||
// 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.
|
||||
FileMcd_CancelEject();
|
||||
@ -2833,8 +2849,7 @@ void VMManager::Internal::VSyncOnCPUThread()
|
||||
{
|
||||
Pad::UpdateMacroButtons();
|
||||
|
||||
Patch::ApplyLoadedPatches(Patch::PPT_CONTINUOUSLY);
|
||||
Patch::ApplyLoadedPatches(Patch::PPT_COMBINED_0_1);
|
||||
Patch::ApplyVsyncPatches();
|
||||
|
||||
// Frame advance must be done *before* pumping messages, because otherwise
|
||||
// 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);
|
||||
|
||||
/// 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.
|
||||
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.
|
||||
void WaitForSaveStateFlush();
|
||||
|
||||
Loading…
Reference in New Issue
Block a user