Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7d9ad6daab | ||
|
|
822b166753 | ||
|
|
8fad768d39 | ||
|
|
d46b8390f1 | ||
|
|
4262715590 | ||
|
|
9818446c7d | ||
|
|
22682e4150 | ||
|
|
1d63779b7d | ||
|
|
c82f800794 | ||
|
|
d96dea49bf | ||
|
|
c574c3e08c | ||
|
|
e7e6f765b5 | ||
|
|
240586875c | ||
|
|
d6c5903569 | ||
|
|
55da5cc397 | ||
|
|
943359e328 | ||
|
|
92c54cc1f8 | ||
|
|
6ec0b44487 | ||
|
|
85d2ca626f | ||
|
|
68865a2bca | ||
|
|
8362e6f435 | ||
|
|
3804a58729 | ||
|
|
fbb67a6ee9 | ||
|
|
19d8b59ca7 | ||
|
|
7ca726492f | ||
|
|
96a43ca254 | ||
|
|
bf34eb32ed | ||
|
|
433612cde0 | ||
|
|
807db91140 | ||
|
|
7791809224 | ||
|
|
9b3d31cd47 | ||
|
|
65a3a97ab2 | ||
|
|
e3593574b0 | ||
|
|
f81d391546 | ||
|
|
bd3b00793a | ||
|
|
757a72fb57 | ||
|
|
fbbec480af | ||
|
|
ba3b43c66c | ||
|
|
e040b83b2c | ||
|
|
76f4a5b2fe | ||
|
|
4f4b9956b3 | ||
|
|
ba37a0f3a9 | ||
|
|
d4acb95d7c | ||
|
|
2887baefb8 | ||
|
|
9678bf1e2f | ||
|
|
e2fc68ff2d | ||
|
|
4a7b641bc3 | ||
|
|
719b83bf42 | ||
|
|
f201a2e5dd | ||
|
|
fd6f47f5ca | ||
|
|
8524fbbaf9 | ||
|
|
a371c5b7d0 | ||
|
|
ef7ce4dfc8 | ||
|
|
189f6fa2a3 | ||
|
|
472f4922bd | ||
|
|
8ba2f342df | ||
|
|
5d6b9d25bd | ||
|
|
1231499aaa | ||
|
|
9c4788432d | ||
|
|
4ded2caa0b | ||
|
|
494afb9d43 | ||
|
|
c994e4828d | ||
|
|
e50f12a925 | ||
|
|
df75d70df4 | ||
|
|
c5aa04d4eb | ||
|
|
81ae1ce362 | ||
|
|
e1921e660d | ||
|
|
32aa24f6fd | ||
|
|
7fbc63b8e8 | ||
|
|
443adcdd5b | ||
|
|
05b064d513 | ||
|
|
b327033333 | ||
|
|
36c9b68b6c | ||
|
|
7d8d16091e | ||
|
|
c1c6bde429 | ||
|
|
8f0901e7a5 | ||
|
|
6edba3820b | ||
|
|
db1e1bcc1f | ||
|
|
51aeaeb508 | ||
|
|
dcd0a1f002 | ||
|
|
819b61937f | ||
|
|
9da7628083 | ||
|
|
c5f47a8db3 | ||
|
|
c2786b91ce | ||
|
|
4290c16997 | ||
|
|
475e90b654 | ||
|
|
ebb291e06d | ||
|
|
a2c9069dda | ||
|
|
48cfe9ca73 | ||
|
|
b53e9856b8 | ||
|
|
20420da326 | ||
|
|
9162f176a2 | ||
|
|
3c140c2ef4 | ||
|
|
511d37e7c3 | ||
|
|
10a5ea2a29 | ||
|
|
c049f6814b | ||
|
|
0716abdbf8 | ||
|
|
989f2bbbb0 | ||
|
|
45c564749c | ||
|
|
a62737b244 | ||
|
|
1fa3111e67 | ||
|
|
0e78f3f3bc | ||
|
|
6bf07086a0 | ||
|
|
b3f8f4e8ec | ||
|
|
65ee7e82aa | ||
|
|
c78f3b4e24 | ||
|
|
51aa7c8ecf | ||
|
|
64ab92ced6 | ||
|
|
8f9f351940 | ||
|
|
a9a1af7307 | ||
|
|
5057dfedba | ||
|
|
ecabadbf95 | ||
|
|
97630039d8 | ||
|
|
e7f1178469 | ||
|
|
87245ef978 | ||
|
|
455aa28724 | ||
|
|
92f70228c9 | ||
|
|
5fa862b346 |
@@ -0,0 +1,28 @@
|
||||
name: 🌎 Update Base Translation
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: "0 0 * * *" # Every day at 12am UTC.
|
||||
workflow_dispatch: # As well as manually.
|
||||
|
||||
jobs:
|
||||
update:
|
||||
if: github.repository == 'PCSX2/pcsx2'
|
||||
name: "Update Base Translation"
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Update Base Translation
|
||||
run: ./.github/workflows/scripts/common/update_base_translation.sh
|
||||
|
||||
- name: Create Pull Request
|
||||
uses: peter-evans/create-pull-request@v5
|
||||
with:
|
||||
title: "Qt: Update Base Translation"
|
||||
commit-message: "Qt: Update Base Translation"
|
||||
committer: "PCSX2 Bot <PCSX2Bot@users.noreply.github.com>"
|
||||
author: "PCSX2 Bot <PCSX2Bot@users.noreply.github.com>"
|
||||
body: "Daily update of base translation sources."
|
||||
branch: update-base-translation
|
||||
delete-branch: true
|
||||
@@ -28,14 +28,14 @@ on:
|
||||
required: false
|
||||
type: boolean
|
||||
default: false
|
||||
detail:
|
||||
required: false
|
||||
type: string
|
||||
default: ""
|
||||
patches_url:
|
||||
patchesUrl:
|
||||
required: false
|
||||
type: string
|
||||
default: https://github.com/PCSX2/pcsx2_patches/releases/latest/download
|
||||
fetchTags:
|
||||
required: false
|
||||
type: boolean
|
||||
default: false
|
||||
|
||||
jobs:
|
||||
build_linux:
|
||||
@@ -51,9 +51,9 @@ jobs:
|
||||
|
||||
# Hackity hack. When running the workflow on a schedule, we don't have the tag,
|
||||
# it doesn't fetch tags, therefore we don't get a version. So grab them manually.
|
||||
# actions/checkout elides tags, fetch them primarily for releases
|
||||
- name: Fetch tags
|
||||
if: inputs.publish == true
|
||||
id: fetch-tags
|
||||
if: ${{ inputs.fetchTags }}
|
||||
run: git fetch --tags --no-recurse-submodules
|
||||
|
||||
- name: Prepare Artifact Metadata
|
||||
@@ -61,7 +61,6 @@ jobs:
|
||||
shell: bash
|
||||
env:
|
||||
OS: linux
|
||||
GUI_FRAMEWORK: QT
|
||||
BUILD_SYSTEM: flatpak
|
||||
ARCH: ${{ inputs.platform }}
|
||||
EVENT_NAME: ${{ github.event_name }}
|
||||
@@ -78,7 +77,7 @@ jobs:
|
||||
- name: Download patches
|
||||
run: |
|
||||
cd bin/resources
|
||||
aria2c -Z "${{ inputs.patches_url }}/patches.zip"
|
||||
aria2c -Z "${{ inputs.patchesUrl }}/patches.zip"
|
||||
|
||||
- name: Generate AppStream XML
|
||||
run: |
|
||||
@@ -98,7 +97,7 @@ jobs:
|
||||
branch: ${{ inputs.branch }}
|
||||
cache: true
|
||||
restore-cache: true
|
||||
cache-key: ${{ inputs.os }} ${{ inputs.platform }} ${{ inputs.compiler }} ${{ inputs.detail }} flatpak ${{ hashFiles('.github/workflows/scripts/linux/flatpak/**/*.json') }}
|
||||
cache-key: ${{ inputs.os }} ${{ inputs.platform }} ${{ inputs.compiler }} flatpak ${{ hashFiles('.github/workflows/scripts/linux/flatpak/**/*.json') }}
|
||||
|
||||
- name: Commit screenshots to OSTree
|
||||
run: |
|
||||
|
||||
@@ -14,7 +14,7 @@ jobs:
|
||||
if: github.repository != 'PCSX2/pcsx2' || github.event_name == 'pull_request'
|
||||
uses: ./.github/workflows/linux_build_qt.yml
|
||||
with:
|
||||
jobName: "Qt"
|
||||
jobName: "AppImage Build"
|
||||
compiler: clang
|
||||
cmakeflags: ""
|
||||
buildAppImage: true
|
||||
@@ -24,7 +24,7 @@ jobs:
|
||||
if: github.repository != 'PCSX2/pcsx2' || github.event_name == 'pull_request'
|
||||
uses: ./.github/workflows/linux_build_flatpak.yml
|
||||
with:
|
||||
jobName: "Qt"
|
||||
jobName: "Flatpak Build"
|
||||
compiler: clang
|
||||
cmakeflags: ""
|
||||
publish: false
|
||||
|
||||
@@ -9,7 +9,7 @@ on:
|
||||
os:
|
||||
required: false
|
||||
type: string
|
||||
default: ubuntu-20.04
|
||||
default: ubuntu-22.04
|
||||
platform:
|
||||
required: false
|
||||
type: string
|
||||
@@ -28,10 +28,14 @@ on:
|
||||
required: false
|
||||
type: string
|
||||
default: ""
|
||||
patches_url:
|
||||
patchesUrl:
|
||||
required: false
|
||||
type: string
|
||||
default: https://github.com/PCSX2/pcsx2_patches/releases/latest/download
|
||||
fetchTags:
|
||||
required: false
|
||||
type: boolean
|
||||
default: false
|
||||
|
||||
jobs:
|
||||
build_linux:
|
||||
@@ -52,12 +56,16 @@ jobs:
|
||||
with:
|
||||
submodules: recursive
|
||||
|
||||
# actions/checkout elides tags, fetch them primarily for releases
|
||||
- name: Fetch Tags
|
||||
if: ${{ inputs.fetchTags }}
|
||||
run: git fetch --tags --no-recurse-submodules
|
||||
|
||||
- name: Prepare Artifact Metadata
|
||||
id: artifact-metadata
|
||||
shell: bash
|
||||
env:
|
||||
OS: linux
|
||||
GUI_FRAMEWORK: QT
|
||||
ARCH: ${{ inputs.platform }}
|
||||
EVENT_NAME: ${{ github.event_name }}
|
||||
PR_TITLE: ${{ github.event.pull_request.title }}
|
||||
@@ -87,7 +95,7 @@ jobs:
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: ~/deps
|
||||
key: ${{ inputs.os }} ${{ inputs.platform }} ${{ inputs.gui }} deps ${{ hashFiles('.github/workflows/scripts/linux/build-dependencies-qt.sh') }}
|
||||
key: ${{ inputs.os }} ${{ inputs.platform }} deps ${{ hashFiles('.github/workflows/scripts/linux/build-dependencies-qt.sh') }}
|
||||
|
||||
- name: Build Dependencies
|
||||
if: steps.cache-deps.outputs.cache-hit != 'true'
|
||||
@@ -96,7 +104,7 @@ jobs:
|
||||
- name: Download patches
|
||||
run: |
|
||||
cd bin/resources
|
||||
aria2c -Z "${{ inputs.patches_url }}/patches.zip"
|
||||
aria2c -Z "${{ inputs.patchesUrl }}/patches.zip"
|
||||
|
||||
- name: Generate CMake
|
||||
env:
|
||||
@@ -120,9 +128,9 @@ jobs:
|
||||
env:
|
||||
NAME: ${{ steps.artifact-metadata.outputs.artifact-name }}
|
||||
run: |
|
||||
.github/workflows/scripts/linux/appimage-qt.sh "$(realpath .)" "$(realpath ./build)" "$HOME/deps" "$NAME"
|
||||
mkdir -p "$GITHUB_WORKSPACE"/ci-artifacts/
|
||||
mv "${NAME}.AppImage" "$GITHUB_WORKSPACE"/ci-artifacts/
|
||||
.github/workflows/scripts/linux/appimage-qt.sh "$(realpath .)" "$(realpath ./build)" "$HOME/deps" "$NAME"
|
||||
mkdir -p "$GITHUB_WORKSPACE"/ci-artifacts/
|
||||
mv "${NAME}.AppImage" "$GITHUB_WORKSPACE"/ci-artifacts/
|
||||
|
||||
- name: Upload artifact
|
||||
if: inputs.buildAppImage == true
|
||||
|
||||
@@ -10,17 +10,14 @@ on:
|
||||
required: false
|
||||
type: string
|
||||
default: macos-13
|
||||
platform:
|
||||
required: false
|
||||
type: string
|
||||
default: x64
|
||||
gui:
|
||||
required: true
|
||||
type: string
|
||||
patches_url:
|
||||
patchesUrl:
|
||||
required: false
|
||||
type: string
|
||||
default: https://github.com/PCSX2/pcsx2_patches/releases/latest/download
|
||||
fetchTags:
|
||||
required: false
|
||||
type: boolean
|
||||
default: false
|
||||
|
||||
jobs:
|
||||
build_macos:
|
||||
@@ -42,6 +39,11 @@ jobs:
|
||||
with:
|
||||
submodules: recursive
|
||||
|
||||
# actions/checkout elides tags, fetch them primarily for releases
|
||||
- name: Fetch Tags
|
||||
if: ${{ inputs.fetchTags }}
|
||||
run: git fetch --tags --no-recurse-submodules
|
||||
|
||||
- name: Use Xcode 14.3.1
|
||||
run: sudo xcode-select -s /Applications/Xcode_14.3.1.app
|
||||
|
||||
@@ -50,8 +52,6 @@ jobs:
|
||||
shell: bash
|
||||
env:
|
||||
OS: macos
|
||||
GUI_FRAMEWORK: ${{ inputs.gui }}
|
||||
ARCH: ${{ inputs.platform }}
|
||||
EVENT_NAME: ${{ github.event_name }}
|
||||
PR_TITLE: ${{ github.event.pull_request.title }}
|
||||
PR_NUM: ${{ github.event.pull_request.number }}
|
||||
@@ -60,7 +60,7 @@ jobs:
|
||||
|
||||
- name: Install Packages
|
||||
env:
|
||||
PLATFORM: ${{ inputs.platform }}
|
||||
PLATFORM: "x64"
|
||||
HOMEBREW_NO_INSTALL_CLEANUP: 1
|
||||
HOMEBREW_NO_ANALYTICS: 1
|
||||
run: |
|
||||
@@ -77,18 +77,16 @@ jobs:
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: ~/deps
|
||||
key: ${{ inputs.os }} ${{ inputs.platform }} ${{ inputs.gui }} deps ${{ hashFiles('.github/workflows/scripts/macos/build-dependencies.sh') }}
|
||||
key: ${{ inputs.os }} deps ${{ hashFiles('.github/workflows/scripts/macos/build-dependencies.sh') }}
|
||||
|
||||
- name: Build Dependencies
|
||||
if: steps.cache-deps.outputs.cache-hit != 'true'
|
||||
env:
|
||||
GUI: ${{ inputs.gui }}
|
||||
run: .github/workflows/scripts/macos/build-dependencies.sh
|
||||
|
||||
- name: Download patches
|
||||
run: |
|
||||
cd bin/resources
|
||||
aria2c -Z "${{ inputs.patches_url }}/patches.zip"
|
||||
aria2c -Z "${{ inputs.patchesUrl }}/patches.zip"
|
||||
|
||||
# -- SETUP CCACHE - https://cristianadam.eu/20200113/speeding-up-c-plus-plus-github-actions-using-ccache/
|
||||
- name: Prepare ccache timestamp
|
||||
@@ -99,8 +97,8 @@ jobs:
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: .ccache
|
||||
key: ${{ inputs.os }} ${{ inputs.platform }} ${{ inputs.gui }} ccache ${{ steps.ccache_cache_timestamp.outputs.timestamp }}
|
||||
restore-keys: ${{ inputs.os }} ${{ inputs.platform }} ${{ inputs.gui }} ccache
|
||||
key: ${{ inputs.os }} ccache ${{ steps.ccache_cache_timestamp.outputs.timestamp }}
|
||||
restore-keys: ${{ inputs.os }} ccache
|
||||
|
||||
- name: Generate CMake Files
|
||||
run: |
|
||||
@@ -133,7 +131,6 @@ jobs:
|
||||
done
|
||||
|
||||
- name: Run Tests
|
||||
if: inputs.gui == 'Qt'
|
||||
working-directory: build
|
||||
run: make -j$(getconf _NPROCESSORS_ONLN) unittests
|
||||
|
||||
@@ -149,7 +146,7 @@ jobs:
|
||||
mv build/pcsx2*/PCSX2.app "$APPNAME.app"
|
||||
tar --options xz:compression-level=9 -cvJf "${{ steps.artifact-metadata.outputs.artifact-name }}.tar.xz" "$APPNAME.app"
|
||||
mkdir ci-artifacts
|
||||
cp "${{ steps.artifact-metadata.outputs.artifact-name }}.tar.xz" ci-artifacts/macOS-${{ inputs.gui }}.tar.xz
|
||||
cp "${{ steps.artifact-metadata.outputs.artifact-name }}.tar.xz" ci-artifacts/macOS.tar.xz
|
||||
|
||||
- name: Upload Artifact
|
||||
uses: actions/upload-artifact@v3
|
||||
|
||||
@@ -14,6 +14,5 @@ jobs:
|
||||
if: github.repository != 'PCSX2/pcsx2' || github.event_name == 'pull_request'
|
||||
uses: ./.github/workflows/macos_build.yml
|
||||
with:
|
||||
jobName: "Qt"
|
||||
gui: "Qt"
|
||||
jobName: "MacOS Build"
|
||||
secrets: inherit
|
||||
|
||||
@@ -0,0 +1,186 @@
|
||||
# Whenever a commit is pushed to master (ideally via a pull-request!)
|
||||
# this action will create the next release, which means:
|
||||
# 1. tag master with the proper version
|
||||
# 2. create a new draft release (pre-released if a nightly build)
|
||||
# 3. add release notes
|
||||
|
||||
name: 🏭 Create Release
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
# TODO - future work
|
||||
# workflow_dispatch:
|
||||
# inputs:
|
||||
# isStable:
|
||||
# description: 'Should it be a stable release?'
|
||||
# required: true
|
||||
# default: 'false'
|
||||
# versionTag:
|
||||
# description: 'The version to tag with'
|
||||
# required: true
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
jobs:
|
||||
cut-release:
|
||||
if: github.repository == 'PCSX2/pcsx2'
|
||||
runs-on: ubuntu-latest
|
||||
name: "Create Tag and Release"
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
# Docs - https://github.com/mathieudutour/github-tag-action
|
||||
- name: Bump Version and Push Tag
|
||||
id: tag_version
|
||||
uses: mathieudutour/github-tag-action@v6.1
|
||||
with:
|
||||
github_token: ${{ github.token }}
|
||||
tag_prefix: v
|
||||
default_bump: patch
|
||||
|
||||
# TODO - we could do this and remove the node.js script, but auto-generated notes only work
|
||||
# with PRs -- not commits (determine how much we care).
|
||||
# - name: Create Draft Release
|
||||
# run: |
|
||||
# echo "Creating release with tag - ${{ steps.tag_version.outputs.new_tag }}"
|
||||
# gh release create ${{ steps.tag_version.outputs.new_tag }} --draft --generate-notes -title ${{ steps.tag_version.outputs.new_tag }}
|
||||
|
||||
- name: Generate Release Notes
|
||||
env:
|
||||
OWNER: PCSX2
|
||||
REPO: pcsx2
|
||||
GITHUB_TOKEN: ${{ github.token }}
|
||||
COMMIT_SHA: ${{ github.SHA }}
|
||||
run: |
|
||||
cd ./.github/workflows/scripts/releases/generate-release-notes
|
||||
npm ci
|
||||
node index.js
|
||||
mv ./release-notes.md ${GITHUB_WORKSPACE}/release-notes.md
|
||||
|
||||
- name: Create a GitHub Release
|
||||
uses: softprops/action-gh-release@v1
|
||||
if: steps.tag_version.outputs.new_tag
|
||||
with:
|
||||
body_path: ./release-notes.md
|
||||
draft: true
|
||||
prerelease: true
|
||||
tag_name: ${{ steps.tag_version.outputs.new_tag }}
|
||||
|
||||
# Build Everything
|
||||
# Linux
|
||||
build_linux_qt:
|
||||
if: github.repository == 'PCSX2/pcsx2'
|
||||
needs:
|
||||
- cut-release
|
||||
name: "Linux"
|
||||
uses: ./.github/workflows/linux_build_qt.yml
|
||||
with:
|
||||
jobName: "AppImage Build"
|
||||
compiler: clang
|
||||
cmakeflags: ""
|
||||
buildAppImage: true
|
||||
fetchTags: true
|
||||
secrets: inherit
|
||||
|
||||
build_linux_flatpak:
|
||||
if: github.repository == 'PCSX2/pcsx2'
|
||||
needs:
|
||||
- cut-release
|
||||
name: "Linux"
|
||||
uses: ./.github/workflows/linux_build_flatpak.yml
|
||||
with:
|
||||
jobName: "Flatpak Build"
|
||||
compiler: clang
|
||||
cmakeflags: ""
|
||||
branch: "stable"
|
||||
publish: false
|
||||
fetchTags: true
|
||||
secrets: inherit
|
||||
|
||||
# Windows
|
||||
build_windows_qt:
|
||||
if: github.repository == 'PCSX2/pcsx2'
|
||||
needs:
|
||||
- cut-release
|
||||
name: "Windows"
|
||||
uses: ./.github/workflows/windows_build_qt.yml
|
||||
with:
|
||||
jobName: "Windows Build"
|
||||
configuration: CMake
|
||||
buildSystem: cmake
|
||||
cmakeFlags: -DCMAKE_C_COMPILER=clang-cl -DCMAKE_CXX_COMPILER=clang-cl
|
||||
fetchTags: true
|
||||
secrets: inherit
|
||||
|
||||
# MacOS
|
||||
build_macos_qt:
|
||||
if: github.repository == 'PCSX2/pcsx2'
|
||||
needs:
|
||||
- cut-release
|
||||
name: "MacOS"
|
||||
uses: ./.github/workflows/macos_build.yml
|
||||
with:
|
||||
jobName: "MacOS Build"
|
||||
fetchTags: true
|
||||
secrets: inherit
|
||||
|
||||
# Upload the Artifacts
|
||||
upload_artifacts:
|
||||
if: github.repository == 'PCSX2/pcsx2'
|
||||
needs:
|
||||
- build_linux_flatpak
|
||||
- build_linux_qt
|
||||
- build_windows_qt
|
||||
- build_macos_qt
|
||||
name: "Upload Artifacts"
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
# actions/checkout elides tags, fetch them primarily for releases
|
||||
- name: Fetch Tags
|
||||
run: git fetch --tags --no-recurse-submodules
|
||||
|
||||
- name: Prepare Artifact Folder
|
||||
run: mkdir ./ci-artifacts/
|
||||
|
||||
- uses: actions/download-artifact@v3
|
||||
name: Download all Artifacts
|
||||
with:
|
||||
path: ./ci-artifacts/
|
||||
|
||||
- name: Display structure of downloaded files
|
||||
run: ls ./ci-artifacts/
|
||||
|
||||
# Prepare artifacts, they are all zips from github!
|
||||
- name: Prepare Artifacts
|
||||
working-directory: ./ci-artifacts/
|
||||
run: for d in *windows*/; do 7z a "${d}asset.7z" ./$d/*; done
|
||||
|
||||
# Artifact Naming:
|
||||
# MacOS: PCSX2-<tag>-macOS-[additional hyphen seperated tags]
|
||||
# Windows|Linux: PCSX2-<tag>-<windows|linux>-<32bit|64bit>--[additional hyphen seperated tags]
|
||||
- name: Name and Upload the Release Assets
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ github.token }}
|
||||
SCAN_DIR: ${{ github.WORKSPACE }}/ci-artifacts
|
||||
OUT_DIR: ${{ github.WORKSPACE }}/ci-artifacts/out
|
||||
run: |
|
||||
TAG_VAL=$(git tag --points-at HEAD)
|
||||
echo "TAG_VAL=${TAG_VAL}"
|
||||
gh release list --repo PCSX2/pcsx2
|
||||
mkdir -p ${{ github.WORKSPACE }}/ci-artifacts/out
|
||||
TAG_VAL=${TAG_VAL} python ./.github/workflows/scripts/releases/rename-release-assets.py
|
||||
ls ${{ github.WORKSPACE }}/ci-artifacts/out
|
||||
gh release upload "${TAG_VAL}" ${{ github.WORKSPACE }}/ci-artifacts/out/* --repo PCSX2/pcsx2 --clobber
|
||||
|
||||
- name: Publish Release
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ github.token }}
|
||||
run: |
|
||||
TAG_VAL=$(git tag --points-at HEAD)
|
||||
echo "TAG_VAL=${TAG_VAL}"
|
||||
gh release edit ${TAG_VAL} --draft=false --repo PCSX2/pcsx2
|
||||
@@ -1,71 +0,0 @@
|
||||
# Whenever a commit is pushed to master (ideally via a pull-request!)
|
||||
# this action will create the next release, which means:
|
||||
# 1. tag master with the proper version
|
||||
# 2. create a new draft release (pre-released if a nightly build)
|
||||
# 3. add release notes
|
||||
|
||||
name: 🏭 Create Release
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
# TODO - future work
|
||||
# workflow_dispatch:
|
||||
# inputs:
|
||||
# isStable:
|
||||
# description: 'Should it be a stable release?'
|
||||
# required: true
|
||||
# default: 'false'
|
||||
# versionTag:
|
||||
# description: 'The version to tag with'
|
||||
# required: true
|
||||
|
||||
jobs:
|
||||
cut-release:
|
||||
if: github.repository == 'PCSX2/pcsx2'
|
||||
runs-on: ubuntu-latest
|
||||
name: "Create Tag and Release"
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
# Docs - https://github.com/mathieudutour/github-tag-action
|
||||
- name: Bump Version and Push Tag
|
||||
id: tag_version
|
||||
uses: mathieudutour/github-tag-action@v6.1
|
||||
with:
|
||||
# Workflows cannot trigger other workflows implicitly
|
||||
# - https://github.community/t/github-actions-workflow-not-triggering-with-tag-push/17053/7
|
||||
github_token: ${{ secrets.BOT_PAT }}
|
||||
tag_prefix: v
|
||||
default_bump: patch
|
||||
|
||||
# TODO - we could do this and remove the node.js script, but auto-generated notes only work
|
||||
# with PRs -- not commits (determine how much we care).
|
||||
# - name: Create Draft Release
|
||||
# env:
|
||||
# GITHUB_TOKEN: ${{ secrets.BOT_PAT }}
|
||||
# run: |
|
||||
# echo "Creating release with tag - ${{ steps.tag_version.outputs.new_tag }}"
|
||||
# gh release create ${{ steps.tag_version.outputs.new_tag }} --draft --generate-notes -title ${{ steps.tag_version.outputs.new_tag }}
|
||||
|
||||
- name: Generate Release Notes
|
||||
env:
|
||||
OWNER: PCSX2
|
||||
REPO: pcsx2
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
COMMIT_SHA: ${{ github.SHA }}
|
||||
run: |
|
||||
cd ./.github/workflows/scripts/releases/generate-release-notes
|
||||
npm ci
|
||||
node index.js
|
||||
mv ./release-notes.md ${GITHUB_WORKSPACE}/release-notes.md
|
||||
|
||||
- name: Create a GitHub Release
|
||||
uses: softprops/action-gh-release@v1
|
||||
if: steps.tag_version.outputs.new_tag
|
||||
with:
|
||||
body_path: ./release-notes.md
|
||||
draft: true
|
||||
prerelease: true
|
||||
tag_name: ${{ steps.tag_version.outputs.new_tag }}
|
||||
@@ -1,107 +0,0 @@
|
||||
name: 🏭 Release Pipeline
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- v*
|
||||
|
||||
jobs:
|
||||
# Build Everything
|
||||
# Linux
|
||||
build_linux_qt:
|
||||
if: github.repository == 'PCSX2/pcsx2'
|
||||
name: "Linux"
|
||||
uses: ./.github/workflows/linux_build_qt.yml
|
||||
with:
|
||||
jobName: "Qt"
|
||||
compiler: clang
|
||||
cmakeflags: ""
|
||||
buildAppImage: true
|
||||
secrets: inherit
|
||||
build_linux_flatpak:
|
||||
if: github.repository == 'PCSX2/pcsx2'
|
||||
name: "Linux"
|
||||
uses: ./.github/workflows/linux_build_flatpak.yml
|
||||
with:
|
||||
jobName: "Flatpak"
|
||||
compiler: clang
|
||||
cmakeflags: ""
|
||||
branch: "stable"
|
||||
publish: false
|
||||
secrets: inherit
|
||||
|
||||
# Windows
|
||||
build_windows_qt:
|
||||
if: github.repository == 'PCSX2/pcsx2'
|
||||
name: "Windows"
|
||||
uses: ./.github/workflows/windows_build_qt.yml
|
||||
with:
|
||||
jobName: Qt
|
||||
configuration: CMake
|
||||
buildSystem: cmake
|
||||
cmakeFlags: -DCMAKE_C_COMPILER=clang-cl -DCMAKE_CXX_COMPILER=clang-cl
|
||||
secrets: inherit
|
||||
|
||||
# MacOS
|
||||
build_macos_qt:
|
||||
if: github.repository == 'PCSX2/pcsx2'
|
||||
name: "MacOS"
|
||||
uses: ./.github/workflows/macos_build.yml
|
||||
with:
|
||||
jobName: "Qt"
|
||||
gui: "Qt"
|
||||
secrets: inherit
|
||||
|
||||
# Upload the Artifacts
|
||||
upload_artifacts:
|
||||
if: github.repository == 'PCSX2/pcsx2'
|
||||
needs:
|
||||
- build_linux_flatpak
|
||||
- build_linux_qt
|
||||
- build_windows_qt
|
||||
- build_macos_qt
|
||||
name: "Upload Artifacts"
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Prepare Artifact Folder
|
||||
run: mkdir ./ci-artifacts/
|
||||
|
||||
- uses: actions/download-artifact@v3
|
||||
name: Download all Artifacts
|
||||
with:
|
||||
path: ./ci-artifacts/
|
||||
|
||||
- name: Display structure of downloaded files
|
||||
run: ls ./ci-artifacts/
|
||||
|
||||
# Prepare artifacts, they are all zips from github!
|
||||
- name: Prepare Artifacts
|
||||
working-directory: ./ci-artifacts/
|
||||
run: for d in *windows*/; do 7z a "${d}asset.7z" ./$d/*; done
|
||||
|
||||
# Artifact Naming:
|
||||
# MacOS: PCSX2-<tag>-macOS-[additional hyphen seperated tags]
|
||||
# Windows|Linux: PCSX2-<tag>-<windows|linux>-<32bit|64bit>--[additional hyphen seperated tags]
|
||||
- name: Name and Upload the Release Assets
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
TAG: ${{ github.REF }}
|
||||
SCAN_DIR: ${{ github.WORKSPACE }}/ci-artifacts
|
||||
OUT_DIR: ${{ github.WORKSPACE }}/ci-artifacts/out
|
||||
run: |
|
||||
gh release list --repo PCSX2/pcsx2
|
||||
mkdir -p ${{ github.WORKSPACE }}/ci-artifacts/out
|
||||
python ./.github/workflows/scripts/releases/rename-release-assets.py
|
||||
ls ${{ github.WORKSPACE }}/ci-artifacts/out
|
||||
TAG_VAL=$(echo ${{ github.REF }} | awk -F'refs/tags/' '{print $2}')
|
||||
gh release upload "${TAG_VAL}" ${{ github.WORKSPACE }}/ci-artifacts/out/* --repo PCSX2/pcsx2 --clobber
|
||||
|
||||
- name: Publish Release
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.BOT_PAT }}
|
||||
run: |
|
||||
TAG_VAL=$(echo ${{ github.REF }} | awk -F'refs/tags/' '{print $2}')
|
||||
gh release edit ${TAG_VAL} --draft=false --repo PCSX2/pcsx2
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Artifact Naming Scheme:
|
||||
# PCSX2-<OS>-<GUI>-[ARCH]-[SIMD]-[pr\[PR_NUM\]]-[title|sha\[SHA|PR_TITLE\]
|
||||
# PCSX2-<OS>-Qt-[ARCH]-[SIMD]-[pr\[PR_NUM\]]-[title|sha\[SHA|PR_TITLE\]
|
||||
# -- limited to 200 chars
|
||||
# Outputs:
|
||||
# - artifact-name
|
||||
@@ -9,7 +9,6 @@
|
||||
# Inputs as env-vars
|
||||
# OS
|
||||
# BUILD_SYSTEM
|
||||
# GUI_FRAMEWORK
|
||||
# ARCH
|
||||
# SIMD
|
||||
# EVENT_NAME
|
||||
@@ -20,11 +19,12 @@
|
||||
NAME=""
|
||||
|
||||
if [ "${OS}" == "macos" ]; then
|
||||
NAME="PCSX2-${OS}-${GUI_FRAMEWORK}"
|
||||
# MacOS has combined binaries for x64 and ARM64.
|
||||
NAME="PCSX2-${OS}-Qt"
|
||||
elif [[ ("${OS}" == "windows" && "$BUILD_SYSTEM" != "cmake") ]]; then
|
||||
NAME="PCSX2-${OS}-${GUI_FRAMEWORK}-${ARCH}-${SIMD}"
|
||||
NAME="PCSX2-${OS}-Qt-${ARCH}-${SIMD}"
|
||||
else
|
||||
NAME="PCSX2-${OS}-${GUI_FRAMEWORK}-${ARCH}"
|
||||
NAME="PCSX2-${OS}-Qt-${ARCH}"
|
||||
fi
|
||||
|
||||
# Add cmake if used to differentate it from msbuild builds
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
#!/bin/bash
|
||||
|
||||
SCRIPTDIR=$(dirname "${BASH_SOURCE[0]}")
|
||||
source "$SCRIPTDIR/../linux/functions.sh"
|
||||
|
||||
set -e
|
||||
|
||||
# While we use custom Qt builds for our releases, the Qt6 package will be good enough
|
||||
# for just updating translations. Saves building it for this action alone.
|
||||
retry_command sudo apt-get -y install qt6-l10n-tools
|
||||
|
||||
PATH=/usr/lib/qt6/bin:$PATH "$SCRIPTDIR/../../../../pcsx2-qt/Translations/update_en_translation.sh"
|
||||
@@ -1,4 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
APPDIR=$(dirname "$0")
|
||||
exec "$APPDIR/usr/bin/pcsx2-qt" "$@"
|
||||
@@ -40,255 +40,94 @@ NAME=$4
|
||||
|
||||
BINARY=pcsx2-qt
|
||||
APPDIRNAME=PCSX2.AppDir
|
||||
STRIP=llvm-strip-12
|
||||
STRIP=strip
|
||||
|
||||
declare -a SYSLIBS=(
|
||||
"libaio.so.1"
|
||||
"libz.so.1"
|
||||
"libuuid.so.1"
|
||||
"libapparmor.so.1"
|
||||
"libblkid.so.1"
|
||||
"libbsd.so.0"
|
||||
"libdbus-1.so.3"
|
||||
"libgcrypt.so.20"
|
||||
"liblzma.so.5"
|
||||
"libmount.so.1"
|
||||
"libnsl.so.1"
|
||||
"libpcre.so.3"
|
||||
"libselinux.so.1"
|
||||
"libsystemd.so.0"
|
||||
"libudev.so.1"
|
||||
"libwrap.so.0"
|
||||
"libharfbuzz.so.0"
|
||||
"libFLAC.so.8"
|
||||
"libXau.so.6"
|
||||
"libXcomposite.so.1"
|
||||
"libXcursor.so.1"
|
||||
"libXdamage.so.1"
|
||||
"libXdmcp.so.6"
|
||||
"libXext.so.6"
|
||||
"libXfixes.so.3"
|
||||
"libXi.so.6"
|
||||
"libXinerama.so.1"
|
||||
"libXrandr.so.2"
|
||||
"libXrender.so.1"
|
||||
"libXxf86vm.so.1"
|
||||
"libasyncns.so.0"
|
||||
"libcrypto.so.1.1"
|
||||
"libjpeg.so.8"
|
||||
"liblz4.so.1"
|
||||
"libogg.so.0"
|
||||
"libpcap.so.0.8"
|
||||
"libpng16.so.16"
|
||||
"libpulse.so.0"
|
||||
"libsamplerate.so.0"
|
||||
"libsndfile.so.1"
|
||||
"libvorbis.so.0"
|
||||
"libvorbisenc.so.2"
|
||||
"libxcb.so.1"
|
||||
"libxcb-cursor.so.0"
|
||||
"libxcb-render.so.0"
|
||||
"libxcb-shm.so.0"
|
||||
"libxkbcommon.so.0"
|
||||
"libxkbcommon-x11.so.0"
|
||||
"pulseaudio/libpulsecommon-13.99.so"
|
||||
"libasound.so.2"
|
||||
"libfreetype.so.6"
|
||||
"libpcre2-16.so.0"
|
||||
"libexpat.so.1"
|
||||
"libffi.so.7"
|
||||
"libgraphite2.so.3"
|
||||
"libresolv.so.2"
|
||||
"libgpg-error.so.0"
|
||||
"libpcre2-16.so.0"
|
||||
"libpng16.so.16"
|
||||
"libxcb-icccm.so.4"
|
||||
"libxcb-image.so.0"
|
||||
"libxcb-keysyms.so.1"
|
||||
"libxcb-randr.so.0"
|
||||
"libxcb-render.so.0"
|
||||
"libxcb-render-util.so.0"
|
||||
"libxcb-shape.so.0"
|
||||
"libxcb-sync.so.1"
|
||||
"libxcb-util.so.1"
|
||||
"libxcb-xfixes.so.0"
|
||||
"libxcb-xkb.so.1"
|
||||
"libevdev.so.2"
|
||||
"libgudev-1.0.so.0"
|
||||
"libinput.so.10"
|
||||
"libjpeg.so.8"
|
||||
"libmtdev.so.1"
|
||||
"libpng16.so.16"
|
||||
"libudev.so.1"
|
||||
"libuuid.so.1"
|
||||
"libcurl.so.4"
|
||||
"libnghttp2.so.14"
|
||||
"libidn2.so.0"
|
||||
"librtmp.so.1"
|
||||
"libssh.so.4"
|
||||
"libpsl.so.5"
|
||||
"libssl.so.1.1"
|
||||
"libnettle.so.7"
|
||||
"libgnutls.so.30"
|
||||
"libgssapi_krb5.so.2"
|
||||
"libldap_r-2.4.so.2"
|
||||
"liblber-2.4.so.2"
|
||||
"libbrotlidec.so.1"
|
||||
"libunistring.so.2"
|
||||
"libhogweed.so.5"
|
||||
"libgmp.so.10"
|
||||
"libp11-kit.so.0"
|
||||
"libtasn1.so.6"
|
||||
"libkrb5.so.3"
|
||||
"libk5crypto.so.3"
|
||||
"libcom_err.so.2"
|
||||
"libkrb5support.so.0"
|
||||
"libsasl2.so.2"
|
||||
"libgssapi.so.3"
|
||||
"libbrotlicommon.so.1"
|
||||
"libkeyutils.so.1"
|
||||
"libheimntlm.so.0"
|
||||
"libkrb5.so.26"
|
||||
"libasn1.so.8"
|
||||
"libhcrypto.so.4"
|
||||
"libroken.so.18"
|
||||
"libwind.so.0"
|
||||
"libheimbase.so.1"
|
||||
"libhx509.so.5"
|
||||
"libsqlite3.so.0"
|
||||
"libcrypt.so.1"
|
||||
"libdbus-1.so.3"
|
||||
)
|
||||
|
||||
declare -a DEPLIBS=(
|
||||
"libSDL2-2.0.so.0"
|
||||
)
|
||||
|
||||
declare -a QTLIBS=(
|
||||
"libQt6Core.so.6"
|
||||
"libQt6Gui.so.6"
|
||||
"libQt6Network.so.6"
|
||||
"libQt6OpenGL.so.6"
|
||||
"libQt6Svg.so.6"
|
||||
"libQt6WaylandClient.so.6"
|
||||
"libQt6WaylandCompositor.so.6"
|
||||
declare -a MANUAL_QT_LIBS=(
|
||||
"libQt6WaylandEglClientHwIntegration.so.6"
|
||||
"libQt6WaylandEglCompositorHwIntegration.so.6"
|
||||
"libQt6Widgets.so.6"
|
||||
"libQt6XcbQpa.so.6"
|
||||
)
|
||||
|
||||
declare -a QTPLUGINS=(
|
||||
"plugins/iconengines"
|
||||
"plugins/imageformats"
|
||||
"plugins/platforms"
|
||||
#"plugins/platformthemes" # Enable this if we want to ship GTK+ themes at any point.
|
||||
"plugins/tls"
|
||||
"plugins/wayland-decoration-client"
|
||||
"plugins/wayland-graphics-integration-client"
|
||||
"plugins/wayland-graphics-integration-server"
|
||||
"plugins/wayland-shell-integration"
|
||||
"plugins/xcbglintegrations"
|
||||
declare -a MANUAL_QT_PLUGINS=(
|
||||
"wayland-decoration-client"
|
||||
"wayland-graphics-integration-client"
|
||||
"wayland-shell-integration"
|
||||
)
|
||||
|
||||
set -e
|
||||
|
||||
if [ ! -f appimagetool-x86_64.AppImage ]; then
|
||||
retry_command wget -O appimagetool-x86_64.AppImage https://github.com/AppImage/AppImageKit/releases/download/continuous/appimagetool-x86_64.AppImage
|
||||
chmod +x appimagetool-x86_64.AppImage
|
||||
LINUXDEPLOY=./linuxdeploy-x86_64.AppImage
|
||||
LINUXDEPLOY_PLUGIN_QT=./linuxdeploy-plugin-qt-x86_64.AppImage
|
||||
APPIMAGETOOL=./appimagetool-x86_64.AppImage
|
||||
PATCHELF=patchelf
|
||||
|
||||
if [ ! -f "$LINUXDEPLOY" ]; then
|
||||
retry_command wget -O "$LINUXDEPLOY" https://github.com/linuxdeploy/linuxdeploy/releases/download/continuous/linuxdeploy-x86_64.AppImage
|
||||
chmod +x "$LINUXDEPLOY"
|
||||
fi
|
||||
|
||||
if [ ! -f "$LINUXDEPLOY_PLUGIN_QT" ]; then
|
||||
retry_command wget -O "$LINUXDEPLOY_PLUGIN_QT" https://github.com/linuxdeploy/linuxdeploy-plugin-qt/releases/download/continuous/linuxdeploy-plugin-qt-x86_64.AppImage
|
||||
chmod +x "$LINUXDEPLOY_PLUGIN_QT"
|
||||
fi
|
||||
|
||||
if [ ! -f "$APPIMAGETOOL" ]; then
|
||||
retry_command wget -O "$APPIMAGETOOL" https://github.com/AppImage/AppImageKit/releases/download/continuous/appimagetool-x86_64.AppImage
|
||||
chmod +x "$APPIMAGETOOL"
|
||||
fi
|
||||
|
||||
OUTDIR=$(realpath "./$APPDIRNAME")
|
||||
SCRIPTDIR=$(dirname "${BASH_SOURCE[0]}")
|
||||
rm -fr "$OUTDIR"
|
||||
mkdir "$OUTDIR"
|
||||
mkdir "$OUTDIR/usr"
|
||||
|
||||
echo "Copying binary and resources..."
|
||||
cp -a "$BUILDDIR/bin" "$OUTDIR/usr"
|
||||
echo "Copying desktop file..."
|
||||
cp "$PCSX2DIR/.github/workflows/scripts/linux/pcsx2-qt.desktop" .
|
||||
cp "$PCSX2DIR/bin/resources/icons/AppIconLarge.png" "PCSX2.png"
|
||||
|
||||
# Get rid of unit tests, we don't want them bloating the squashfs.
|
||||
rm -fv "$OUTDIR"/usr/bin/*_test
|
||||
echo "Running linuxdeploy to create AppDir..."
|
||||
EXTRA_QT_PLUGINS="core;gui;network;svg;waylandclient;widgets;xcbqpa" \
|
||||
EXTRA_PLATFORM_PLUGINS="libqwayland-egl.so;libqwayland-generic.so" \
|
||||
QMAKE="$DEPSDIR/bin/qmake" \
|
||||
$LINUXDEPLOY --plugin qt --appdir="$OUTDIR" --executable="$BUILDDIR/bin/pcsx2-qt" \
|
||||
--desktop-file="pcsx2-qt.desktop" --icon-file="PCSX2.png"
|
||||
|
||||
# Patch RPATH so the binary goes hunting for shared libraries in the AppDir instead of system.
|
||||
echo "Patching RPATH in ${BINARY}..."
|
||||
patchelf --set-rpath '$ORIGIN/../lib' "$OUTDIR/usr/bin/$BINARY"
|
||||
echo "Copying resources into AppDir..."
|
||||
cp -a "$BUILDDIR/bin/resources" "$OUTDIR/usr/bin"
|
||||
|
||||
# Currently we leave the main binary unstripped, uncomment if this is not desired.
|
||||
#$STRIP "$OUTDIR/usr/bin/$BINARY"
|
||||
|
||||
# Libraries we pull in from the system.
|
||||
echo "Copying system libraries..."
|
||||
mkdir -p "$OUTDIR/usr/lib" "$OUTDIR/usr/lib/pulseaudio"
|
||||
for lib in "${SYSLIBS[@]}"; do
|
||||
blib=$(basename "$lib")
|
||||
if [ -f "/lib/x86_64-linux-gnu/$lib" ]; then
|
||||
cp "/lib/x86_64-linux-gnu/$lib" "$OUTDIR/usr/lib/$blib"
|
||||
elif [ -f "$CHROOT/usr/lib/x86_64-linux-gnu/$lib" ]; then
|
||||
cp "$CHROOT/usr/lib/x86_64-linux-gnu/$lib" "$OUTDIR/usr/lib/$blib"
|
||||
elif [ -f "$CHROOT/lib/$lib" ]; then
|
||||
cp "$CHROOT/lib/$lib" "$OUTDIR/usr/lib/$blib"
|
||||
elif [ -f "$CHROOT/usr/lib/$lib" ]; then
|
||||
cp "$CHROOT/usr/lib/$lib" "$OUTDIR/usr/lib/$blib"
|
||||
else
|
||||
echo "*** Failed to find '$blib'"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
$STRIP "$OUTDIR/usr/lib/$blib"
|
||||
# LinuxDeploy's Qt plugin doesn't include Wayland support. So manually copy in the additional Wayland libraries.
|
||||
echo "Copying Qt Wayland libraries..."
|
||||
for lib in "${MANUAL_QT_LIBS[@]}"; do
|
||||
srcpath="$DEPSDIR/lib/$lib"
|
||||
dstpath="$OUTDIR/usr/lib/$lib"
|
||||
echo " $srcpath -> $dstpath"
|
||||
cp "$srcpath" "$dstpath"
|
||||
$PATCHELF --set-rpath '$ORIGIN' "$dstpath"
|
||||
$STRIP "$dstpath"
|
||||
done
|
||||
|
||||
# Dependencies we built, at this point it's just SDL.
|
||||
echo "Copying dependency libraries..."
|
||||
for lib in "${DEPLIBS[@]}"; do
|
||||
blib=$(basename "$lib")
|
||||
if [ -f "$DEPSDIR/lib/$lib" ]; then
|
||||
cp "$DEPSDIR/lib/$lib" "$OUTDIR/usr/lib/$blib"
|
||||
else
|
||||
echo "*** Failed to find '$blib'"
|
||||
exit 1
|
||||
fi
|
||||
# .. and plugins.
|
||||
echo "Copying Qt Wayland plugins..."
|
||||
for GROUP in "${MANUAL_QT_PLUGINS[@]}"; do
|
||||
srcpath="$DEPSDIR/plugins/$GROUP"
|
||||
dstpath="$OUTDIR/usr/plugins/$GROUP"
|
||||
echo " $srcpath -> $dstpath"
|
||||
mkdir -p "$dstpath"
|
||||
|
||||
$STRIP "$OUTDIR/usr/lib/$blib"
|
||||
for srcsopath in $(find "$DEPSDIR/plugins/$GROUP" -iname '*.so'); do
|
||||
# This is ../../ because it's usually plugins/group/name.so
|
||||
soname=$(basename "$srcsopath")
|
||||
dstsopath="$dstpath/$soname"
|
||||
echo " $srcsopath -> $dstsopath"
|
||||
cp "$srcsopath" "$dstsopath"
|
||||
$PATCHELF --set-rpath '$ORIGIN/../../lib:$ORIGIN' "$dstsopath"
|
||||
$STRIP "$dstsopath"
|
||||
done
|
||||
done
|
||||
|
||||
echo "Copying Qt libraries..."
|
||||
for lib in "${QTLIBS[@]}"; do
|
||||
cp -aL "$DEPSDIR/lib/$lib" "$OUTDIR/usr/lib"
|
||||
$STRIP "$OUTDIR/usr/lib/$lib"
|
||||
done
|
||||
# Fix up translations.
|
||||
rm -fr "$OUTDIR/usr/bin/translations"
|
||||
mv "$OUTDIR/usr/translations" "$OUTDIR/usr/bin"
|
||||
cp -a "$BUILDDIR/bin/translations" "$OUTDIR/usr/bin"
|
||||
|
||||
echo "Copying Qt plugins..."
|
||||
mkdir -p "$OUTDIR/usr/lib/plugins"
|
||||
for plugin in "${QTPLUGINS[@]}"; do
|
||||
mkdir -p "$OUTDIR/usr/lib/$plugin"
|
||||
cp -aL "$DEPSDIR/$plugin"/*.so "$OUTDIR/usr/lib/$plugin/"
|
||||
done
|
||||
echo "Generating AppImage..."
|
||||
rm -f "$NAME.AppImage"
|
||||
$APPIMAGETOOL -v "$OUTDIR" "$NAME.AppImage"
|
||||
|
||||
for so in $(find "$OUTDIR/usr/lib/plugins" -iname '*.so'); do
|
||||
# This is ../../ because it's usually plugins/group/name.so
|
||||
echo "Patching RPATH in ${so}..."
|
||||
patchelf --set-rpath '$ORIGIN/../..' "$so"
|
||||
$STRIP "$so"
|
||||
done
|
||||
|
||||
for so in $(find "$OUTDIR/usr/lib" -maxdepth 1); do
|
||||
if [ -f "$so" ]; then
|
||||
echo "Patching RPATH in ${so}"
|
||||
patchelf --set-rpath '$ORIGIN' "$so"
|
||||
fi
|
||||
done
|
||||
|
||||
echo "Creating qt.conf..."
|
||||
cat > "$OUTDIR/usr/bin/qt.conf" << EOF
|
||||
[Paths]
|
||||
Plugins = ../lib/plugins
|
||||
EOF
|
||||
|
||||
echo "Copy desktop/icon..."
|
||||
cp "$PCSX2DIR/bin/resources/icons/AppIconLarge.png" "$OUTDIR/PCSX2.png"
|
||||
cp "$SCRIPTDIR/pcsx2-qt.desktop" "$OUTDIR/PCSX2.desktop"
|
||||
cp "$SCRIPTDIR/AppRun-qt" "$OUTDIR/AppRun"
|
||||
|
||||
echo "Generate AppImage"
|
||||
./appimagetool-x86_64.AppImage -v "$OUTDIR" "$NAME.AppImage"
|
||||
|
||||
@@ -4,7 +4,7 @@ set -e
|
||||
|
||||
INSTALLDIR="$HOME/deps"
|
||||
NPROCS="$(getconf _NPROCESSORS_ONLN)"
|
||||
SDL=SDL2-2.26.5
|
||||
SDL=SDL2-2.28.1
|
||||
QT=6.5.0
|
||||
LIBBACKTRACE=ad106d5fdd5d960bd33fae1c48a351af567fd075
|
||||
|
||||
@@ -12,7 +12,7 @@ mkdir -p deps-build
|
||||
cd deps-build
|
||||
|
||||
cat > SHASUMS <<EOF
|
||||
ad8fea3da1be64c83c45b1d363a6b4ba8fd60f5bde3b23ec73855709ec5eabf7 $SDL.tar.gz
|
||||
4977ceba5c0054dbe6c2f114641aced43ce3bf2b41ea64b6a372d6ba129cb15d $SDL.tar.gz
|
||||
fd6f417fe9e3a071cf1424a5152d926a34c4a3c5070745470be6cf12a404ed79 $LIBBACKTRACE.zip
|
||||
fde1aa7b4fbe64ec1b4fc576a57f4688ad1453d2fab59cbadd948a10a6eaf5ef qtbase-everywhere-src-$QT.tar.xz
|
||||
64ca7e61f44d51e28bcbb4e0509299b53a9a7e38879e00a7fe91643196067a4f qtsvg-everywhere-src-$QT.tar.xz
|
||||
|
||||
@@ -28,8 +28,8 @@
|
||||
"sources": [
|
||||
{
|
||||
"type": "archive",
|
||||
"url": "https://libsdl.org/release/SDL2-2.26.5.tar.gz",
|
||||
"sha256": "ad8fea3da1be64c83c45b1d363a6b4ba8fd60f5bde3b23ec73855709ec5eabf7"
|
||||
"url": "https://libsdl.org/release/SDL2-2.28.1.tar.gz",
|
||||
"sha256": "4977ceba5c0054dbe6c2f114641aced43ce3bf2b41ea64b6a372d6ba129cb15d"
|
||||
}
|
||||
],
|
||||
"cleanup": [
|
||||
|
||||
@@ -22,7 +22,8 @@
|
||||
"--socket=fallback-x11",
|
||||
"--socket=wayland",
|
||||
"--socket=pulseaudio",
|
||||
"--filesystem=host:ro"
|
||||
"--filesystem=host:ro",
|
||||
"--talk-name=org.freedesktop.ScreenSaver"
|
||||
],
|
||||
"modules": [
|
||||
"modules/10-libpcap.json",
|
||||
|
||||
@@ -8,14 +8,13 @@ set -e
|
||||
# Packages - Build and Qt
|
||||
declare -a BUILD_PACKAGES=(
|
||||
"build-essential"
|
||||
"g++"
|
||||
"git"
|
||||
"cmake"
|
||||
"ccache"
|
||||
"ninja-build"
|
||||
"libclang-dev" # Qt goes hunting for libclang-11 specifically.
|
||||
"libclang-11-dev"
|
||||
"libclang-12-dev"
|
||||
"patchelf"
|
||||
"libfuse2"
|
||||
"libglib2.0-dev"
|
||||
"libfontconfig1-dev"
|
||||
"libharfbuzz-dev"
|
||||
@@ -37,6 +36,7 @@ declare -a BUILD_PACKAGES=(
|
||||
|
||||
# Packages - PCSX2
|
||||
declare -a PCSX2_PACKAGES=(
|
||||
"extra-cmake-modules"
|
||||
"libaio-dev"
|
||||
"libasound2-dev"
|
||||
"libbz2-dev"
|
||||
@@ -63,16 +63,12 @@ declare -a PCSX2_PACKAGES=(
|
||||
"zlib1g-dev"
|
||||
)
|
||||
|
||||
if [ "${COMPILER}" = "gcc" ]; then
|
||||
BUILD_PACKAGES+=("g++-10")
|
||||
else
|
||||
if [ "${COMPILER}" = "clang" ]; then
|
||||
BUILD_PACKAGES+=("llvm-16" "lld-16" "clang-16")
|
||||
|
||||
# Ubuntu 20.04 doesn't ship with LLVM 16, so we need to pull it from the llvm.org repos.
|
||||
# Ubuntu 22.04 doesn't ship with LLVM 16, so we need to pull it from the llvm.org repos.
|
||||
retry_command wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | sudo apt-key add -
|
||||
sudo apt-add-repository -n 'deb http://apt.llvm.org/focal/ llvm-toolchain-focal-16 main'
|
||||
retry_command sudo apt-get update
|
||||
retry_command sudo apt-get install clang-16 lld-16
|
||||
sudo apt-add-repository -n 'deb http://apt.llvm.org/jammy/ llvm-toolchain-jammy-16 main'
|
||||
fi
|
||||
|
||||
retry_command sudo apt-get -qq update && break
|
||||
|
||||
@@ -6,7 +6,7 @@ export MACOSX_DEPLOYMENT_TARGET=10.14
|
||||
|
||||
INSTALLDIR="$HOME/deps"
|
||||
NPROCS="$(getconf _NPROCESSORS_ONLN)"
|
||||
SDL=SDL2-2.26.5
|
||||
SDL=SDL2-2.28.1
|
||||
PNG=1.6.37
|
||||
JPG=9e
|
||||
SOUNDTOUCH=soundtouch-2.3.1
|
||||
@@ -21,7 +21,7 @@ export CFLAGS="-I$INSTALLDIR/include -Os $CFLAGS"
|
||||
export CXXFLAGS="-I$INSTALLDIR/include -Os $CXXFLAGS"
|
||||
|
||||
cat > SHASUMS <<EOF
|
||||
ad8fea3da1be64c83c45b1d363a6b4ba8fd60f5bde3b23ec73855709ec5eabf7 $SDL.tar.gz
|
||||
4977ceba5c0054dbe6c2f114641aced43ce3bf2b41ea64b6a372d6ba129cb15d $SDL.tar.gz
|
||||
505e70834d35383537b6491e7ae8641f1a4bed1876dbfe361201fc80868d88ca libpng-$PNG.tar.xz
|
||||
4077d6a6a75aeb01884f708919d25934c93305e49f7e3f36db9129320e6f4f3d jpegsrc.v$JPG.tar.gz
|
||||
6900996607258496ce126924a19fe9d598af9d892cf3f33d1e4daaa9b42ae0b1 $SOUNDTOUCH.tar.gz
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import os
|
||||
import shutil
|
||||
|
||||
tag = os.environ['TAG'].split("refs/tags/")[1]
|
||||
tag = os.environ['TAG_VAL']
|
||||
scan_dir = os.environ['SCAN_DIR']
|
||||
output_dir = os.environ['OUT_DIR']
|
||||
accepted_exts = ["AppImage", "flatpak", "tar.xz", "7z"]
|
||||
@@ -13,11 +13,11 @@ for dir_name in os.listdir(scan_dir):
|
||||
asset_name += "-macos"
|
||||
elif "linux" in dir_name.lower():
|
||||
if "flatpak" in dir_name.lower():
|
||||
asset_name += "-linux-Flatpak-64bit"
|
||||
asset_name += "-linux-flatpak-x64"
|
||||
else:
|
||||
asset_name += "-linux-AppImage-64bit"
|
||||
asset_name += "-linux-appimage-x64"
|
||||
elif "windows" in dir_name.lower():
|
||||
asset_name += "-windows-64bit"
|
||||
asset_name += "-windows-x64"
|
||||
else:
|
||||
continue;
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@ jobs:
|
||||
if: github.repository != 'PCSX2/pcsx2' || github.event_name == 'pull_request'
|
||||
uses: ./.github/workflows/windows_build_qt.yml
|
||||
with:
|
||||
jobName: Qt
|
||||
jobName: "MSVC SSE4"
|
||||
configuration: Release
|
||||
simd: "SSE4"
|
||||
secrets: inherit
|
||||
@@ -37,7 +37,7 @@ jobs:
|
||||
if: github.repository != 'PCSX2/pcsx2' || github.event_name == 'pull_request'
|
||||
uses: ./.github/workflows/windows_build_qt.yml
|
||||
with:
|
||||
jobName: Qt
|
||||
jobName: "MSVC AVX2"
|
||||
configuration: Release AVX2
|
||||
secrets: inherit
|
||||
|
||||
@@ -46,7 +46,7 @@ jobs:
|
||||
if: github.repository != 'PCSX2/pcsx2' || github.event_name == 'pull_request'
|
||||
uses: ./.github/workflows/windows_build_qt.yml
|
||||
with:
|
||||
jobName: Qt
|
||||
jobName: "CMake MSVC"
|
||||
configuration: CMake
|
||||
buildSystem: cmake
|
||||
secrets: inherit
|
||||
@@ -57,7 +57,7 @@ jobs:
|
||||
if: github.repository != 'PCSX2/pcsx2' || github.event_name == 'pull_request'
|
||||
uses: ./.github/workflows/windows_build_qt.yml
|
||||
with:
|
||||
jobName: Qt Clang
|
||||
jobName: "Clang SSE4"
|
||||
configuration: Release Clang
|
||||
simd: "SSE4"
|
||||
secrets: inherit
|
||||
@@ -68,7 +68,7 @@ jobs:
|
||||
if: github.repository != 'PCSX2/pcsx2' || github.event_name == 'pull_request'
|
||||
uses: ./.github/workflows/windows_build_qt.yml
|
||||
with:
|
||||
jobName: Qt Clang
|
||||
jobName: "Clang AVX2"
|
||||
configuration: Release Clang AVX2
|
||||
secrets: inherit
|
||||
|
||||
@@ -77,7 +77,7 @@ jobs:
|
||||
if: github.repository != 'PCSX2/pcsx2' || github.event_name == 'pull_request'
|
||||
uses: ./.github/workflows/windows_build_qt.yml
|
||||
with:
|
||||
jobName: Qt Clang
|
||||
jobName: "CMake Clang"
|
||||
configuration: CMake
|
||||
buildSystem: cmake
|
||||
cmakeFlags: -DCMAKE_C_COMPILER=clang-cl -DCMAKE_CXX_COMPILER=clang-cl -DPCSX2_EXE_NAME=pcsx2-qt-clang
|
||||
|
||||
@@ -37,10 +37,14 @@ on:
|
||||
required: false
|
||||
type: string
|
||||
default: 3rdparty\qt\6.5.0\msvc2022_64
|
||||
patches_url:
|
||||
patchesUrl:
|
||||
required: false
|
||||
type: string
|
||||
default: https://github.com/PCSX2/pcsx2_patches/releases/latest/download
|
||||
fetchTags:
|
||||
required: false
|
||||
type: boolean
|
||||
default: false
|
||||
|
||||
jobs:
|
||||
build_windows_qt:
|
||||
@@ -57,13 +61,17 @@ jobs:
|
||||
with:
|
||||
submodules: recursive
|
||||
|
||||
# actions/checkout elides tags, fetch them primarily for releases
|
||||
- name: Fetch Tags
|
||||
if: ${{ inputs.fetchTags }}
|
||||
run: git fetch --tags --no-recurse-submodules
|
||||
|
||||
- name: Prepare Artifact Metadata
|
||||
id: artifact-metadata
|
||||
shell: bash
|
||||
env:
|
||||
OS: windows
|
||||
BUILD_SYSTEM: ${{ inputs.buildSystem }}
|
||||
GUI_FRAMEWORK: Qt
|
||||
ARCH: ${{ inputs.platform }}
|
||||
SIMD: ${{ inputs.simd }}
|
||||
EVENT_NAME: ${{ github.event_name }}
|
||||
@@ -88,7 +96,7 @@ jobs:
|
||||
shell: cmd
|
||||
run: |
|
||||
cd bin/resources
|
||||
aria2c -Z "${{ inputs.patches_url }}/patches.zip"
|
||||
aria2c -Z "${{ inputs.patchesUrl }}/patches.zip"
|
||||
|
||||
- name: Generate CMake
|
||||
if: inputs.configuration == 'CMake'
|
||||
|
||||
@@ -90,8 +90,6 @@ oprofile_data/
|
||||
/ipch
|
||||
|
||||
!/3rdparty/libjpeg/change.log
|
||||
/pcsx2/gui/Resources/*.h
|
||||
!/pcsx2/gui/Resources/EmbeddedImage.h
|
||||
/tools/bin
|
||||
.vs
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Setting it to a range tells it that it supports the features on the newer
|
||||
# versions as well, avoiding setting policies.
|
||||
cmake_minimum_required(VERSION 3.12...3.24)
|
||||
cmake_minimum_required(VERSION 3.16...3.24)
|
||||
|
||||
#Enabling this cmake policy gets rid of warnings regarding LTO.
|
||||
cmake_policy(SET CMP0069 NEW)
|
||||
|
||||
@@ -22,7 +22,7 @@ Installers and binaries for both stable and development builds are available fro
|
||||
|
||||
| Operating System | CPU | GPU | RAM |
|
||||
| ------------------------------------------------------------------------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---- |
|
||||
| - Windows 10 21H2 (1809 or later) (64-bit) <br/> - Ubuntu 20.04/Debian or newer, Arch Linux, or other distro (64-bit) <br/> - macOS 10.14 | - Supports SSE4.1 <br/> - [PassMark Thread Performance](https://www.cpubenchmark.net/CPU_mega_page.html) rating near or greater than 1800<br/> - Two physical cores, with hyperthreading | - Direct3D10 support <br/> - OpenGL 3.x support <br/> - Vulkan 1.1 support <br/> - Metal support <br/> - [PassMark G3D Mark](https://www.videocardbenchmark.net/high_end_gpus.html) rating around 3000 (Geforce GTX 750, Radeon RX 560, Intel Arc A380) <br/> - 2 GB Video Memory | 4 GB |
|
||||
| - Windows 10 Version 1809 or later (64-bit) <br/> - Ubuntu 22.04/Debian or newer, Arch Linux, or other distro (64-bit) <br/> - macOS 10.14 | - Supports SSE4.1 <br/> - [PassMark Thread Performance](https://www.cpubenchmark.net/CPU_mega_page.html) rating near or greater than 1800<br/> - Two physical cores, with hyperthreading | - Direct3D10 support <br/> - OpenGL 3.x support <br/> - Vulkan 1.1 support <br/> - Metal support <br/> - [PassMark G3D Mark](https://www.videocardbenchmark.net/high_end_gpus.html) rating around 3000 (Geforce GTX 750, Radeon RX 560, Intel Arc A380) <br/> - 2 GB Video Memory | 4 GB |
|
||||
|
||||
_Note: Recommended Single Thread Performance is based on moderately complex games. Games that pushed the PS2 hardware to its limits will struggle on CPUs at this level. Some release titles and 2D games which underutilized the PS2 hardware may run on CPUs rated as low as 1200. A quick reference for CPU **intensive games**: [Wiki](https://wiki.pcsx2.net/Category:CPU_intensive_games), [Forum](https://forums.pcsx2.net/Thread-LIST-The-Most-CPU-Intensive-Games) and CPU **light** games: [Forum](https://forums.pcsx2.net/Thread-LIST-Games-that-don-t-need-a-strong-CPU-to-emulate)_
|
||||
|
||||
@@ -30,7 +30,7 @@ _Note: Recommended Single Thread Performance is based on moderately complex game
|
||||
|
||||
| Operating System | CPU | GPU | RAM |
|
||||
| ----------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---- |
|
||||
| - Windows 10 21H2 (1809 or later) (64-bit) <br/> - Ubuntu 22.04/Debian or newer, Arch Linux, or other distro (64-bit) <br/> - macOS 10.14 | - Supports AVX2 <br/> - [PassMark Single Thread Performance](https://www.cpubenchmark.net/CPU_mega_page.html) rating near or greater than 2600<br/> - Four physical cores, with or without hyperthreading | - Direct3D12 support <br/> - OpenGL 4.6 support <br/> - Vulkan 1.3 support <br/> - Metal support <br/> - [PassMark G3D Mark](https://www.videocardbenchmark.net/high_end_gpus.html) rating around 6000 (GeForce GTX 1650, Radeon RX 570) <br/> - 4 GB Video Memory | 8 GB |
|
||||
| - Windows 10 22H2 (64-bit) <br/> - Ubuntu 23.04/Debian or newer, Arch Linux, or other distro (64-bit) <br/> - macOS 10.14 | - Supports AVX2 <br/> - [PassMark Single Thread Performance](https://www.cpubenchmark.net/CPU_mega_page.html) rating near or greater than 2600<br/> - Four physical cores, with or without hyperthreading | - Direct3D12 support <br/> - OpenGL 4.6 support <br/> - Vulkan 1.3 support <br/> - Metal support <br/> - [PassMark G3D Mark](https://www.videocardbenchmark.net/high_end_gpus.html) rating around 6000 (GeForce GTX 1650, Radeon RX 570) <br/> - 4 GB Video Memory | 8 GB |
|
||||
|
||||
_Note: Recommended GPU is based on 3x Internal, ~1080p resolution requirements. Higher resolutions will require stronger cards; 6x Internal, ~4K resolution will require a [PassMark G3D Mark](https://www.videocardbenchmark.net/high_end_gpus.html) rating around 12000 (GeForce RTX 2060 Radeon RX 6600 Intel Arc A750). Just like CPU requirements, this is also highly game dependent. A quick reference for GPU **intensive games**: [Wiki](https://wiki.pcsx2.net/Category:GPU_intensive_games)_
|
||||
|
||||
|
||||
@@ -1067,10 +1067,10 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
|
||||
05000000c82d00000851000000010000,8BitDo P30,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:a8,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,
|
||||
03000000c82d00000660000011010000,8BitDo Pro 2,a:b1,b:b0,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:b4,y:b3,platform:Linux,
|
||||
05000000c82d00000660000000010000,8BitDo Pro 2,a:b1,b:b0,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:b4,y:b3,platform:Linux,
|
||||
03000000c82d00000631000014010000,8BitDo Pro 2 Wired Controller,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,
|
||||
03000000c82d00001030000011010000,8BitDo Pro 2 Wired Controller,a:b1,b:b0,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:b4,y:b3,platform:Linux,
|
||||
03000000c82d00000020000000000000,8BitDo Pro 2 Wired Controller for Xbox,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,
|
||||
06000000c82d00000020000006010000,8BitDo Pro 2 Wired Controller for Xbox,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,
|
||||
03000000c82d00000631000014010000,8BitDo Pro 2,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,
|
||||
03000000c82d00001030000011010000,8BitDo Pro 2,a:b1,b:b0,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:b4,y:b3,platform:Linux,
|
||||
03000000c82d00000020000000000000,8BitDo Pro 2 for Xbox,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,
|
||||
06000000c82d00000020000006010000,8BitDo Pro 2 for Xbox,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,
|
||||
03000000c82d00000131000011010000,8BitDo Receiver,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux,
|
||||
03000000c82d00000231000011010000,8BitDo Receiver,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux,
|
||||
03000000c82d00000331000011010000,8BitDo Receiver,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux,
|
||||
@@ -1094,7 +1094,7 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
|
||||
05000000c82d00000261000000010000,8BitDo SN30 Pro Plus,a:b1,b:b0,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:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux,
|
||||
05000000202800000900000000010000,8BitDo SNES30,a:b1,b:b0,back:b10,dpdown:b122,dpleft:b119,dpright:b120,dpup:b117,leftshoulder:b6,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Linux,
|
||||
05000000c82d00001230000000010000,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:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,
|
||||
03000000c82d00001730000011010000,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:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,
|
||||
03000000c82d00001730000011010000,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:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,
|
||||
03000000c82d00001530000011010000,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:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,
|
||||
03000000c82d00001630000011010000,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:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,
|
||||
03000000c82d00001130000011010000,8BitDo Ultimate Wired,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,misc1:b26,paddle1:b24,paddle2:b25,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,
|
||||
@@ -1252,6 +1252,7 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
|
||||
03000000242f00008a00000011010000,JYS Adapter,a:b1,b:b4,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b0,y:b3,platform:Linux,
|
||||
030000006f0e00000103000000020000,Logic3 Controller,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,
|
||||
030000006d040000d1ca000000000000,Logitech Chillstream,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,
|
||||
030000006d040000d1ca000011010000,Logitech Chillstream,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,
|
||||
030000006d04000019c2000010010000,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:Linux,
|
||||
030000006d04000016c2000010010000,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:Linux,
|
||||
030000006d04000016c2000011010000,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:Linux,
|
||||
|
||||
@@ -56,7 +56,7 @@ void main(uint3 LocalThreadId : SV_GroupThreadID, uint3 WorkGroupId : SV_GroupID
|
||||
#endif
|
||||
|
||||
// Filter.
|
||||
AF3 c;
|
||||
AF3 c = (float4)0.0f;
|
||||
|
||||
CasFilter(c.r, c.g, c.b, gxy, const0, const1, sharpenOnly);
|
||||
OutputTexture[ASU2(gxy)] = AF4(c, 1);
|
||||
|
||||
@@ -395,4 +395,46 @@ PS_OUTPUT ps_filter_lottes(PS_INPUT input)
|
||||
return output;
|
||||
}
|
||||
|
||||
PS_OUTPUT ps_4x_rgss(PS_INPUT input)
|
||||
{
|
||||
PS_OUTPUT output;
|
||||
|
||||
float2 dxy = float2(ddx(input.t.x), ddy(input.t.y));
|
||||
float3 color = 0;
|
||||
|
||||
float s = 1.0/8.0;
|
||||
float l = 3.0/8.0;
|
||||
|
||||
color += sample_c(input.t + float2( s, l) * dxy).rgb;
|
||||
color += sample_c(input.t + float2( l,-s) * dxy).rgb;
|
||||
color += sample_c(input.t + float2(-s,-l) * dxy).rgb;
|
||||
color += sample_c(input.t + float2(-l, s) * dxy).rgb;
|
||||
|
||||
output.c = float4(color * 0.25,1);
|
||||
return output;
|
||||
}
|
||||
|
||||
PS_OUTPUT ps_automagical_supersampling(PS_INPUT input)
|
||||
{
|
||||
PS_OUTPUT output;
|
||||
|
||||
float2 ratio = (u_source_size / u_target_size) * 0.5;
|
||||
float2 steps = floor(ratio);
|
||||
float3 col = sample_c(input.t).rgb;
|
||||
float div = 1;
|
||||
|
||||
for (float y = 0; y < steps.y; y++)
|
||||
{
|
||||
for (float x = 0; x < steps.x; x++)
|
||||
{
|
||||
float2 offset = float2(x,y) - ratio * 0.5;
|
||||
col += sample_c(input.t + offset * u_rcp_source_resolution * 2.0).rgb;
|
||||
div++;
|
||||
}
|
||||
}
|
||||
|
||||
output.c = float4(col / div, 1);
|
||||
return output;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -46,7 +46,7 @@ void main()
|
||||
AU2 gxy = ARmp8x8(gl_LocalInvocationID.x)+AU2(gl_WorkGroupID.x<<4u,gl_WorkGroupID.y<<4u);
|
||||
|
||||
// Filter.
|
||||
AF4 c;
|
||||
AF4 c = vec4(0.0f);
|
||||
CasFilter(c.r, c.g, c.b, gxy, const0, const1, CAS_SHARPEN_ONLY);
|
||||
imageStore(imgDst, ASU2(gxy), c);
|
||||
gxy.x += 8u;
|
||||
|
||||
@@ -367,7 +367,7 @@ uniform ivec2 EMOD;
|
||||
void ps_yuv()
|
||||
{
|
||||
vec4 i = sample_c();
|
||||
vec4 o;
|
||||
vec4 o = vec4(0.0f);
|
||||
|
||||
mat3 rgb2yuv; // Value from GS manual
|
||||
rgb2yuv[0] = vec3(0.587, -0.311, -0.419);
|
||||
@@ -380,7 +380,8 @@ void ps_yuv()
|
||||
float Cr = float(0xE0)/255.0f * yuv.y + float(0x80)/255.0f;
|
||||
float Cb = float(0xE0)/255.0f * yuv.z + float(0x80)/255.0f;
|
||||
|
||||
switch(EMOD.x) {
|
||||
switch(EMOD.x)
|
||||
{
|
||||
case 0:
|
||||
o.a = i.a;
|
||||
break;
|
||||
@@ -395,7 +396,8 @@ void ps_yuv()
|
||||
break;
|
||||
}
|
||||
|
||||
switch(EMOD.y) {
|
||||
switch(EMOD.y)
|
||||
{
|
||||
case 0:
|
||||
o.rgb = i.rgb;
|
||||
break;
|
||||
|
||||
@@ -54,6 +54,11 @@ vec4 sample_c()
|
||||
return texture(TextureSampler, PSin_t);
|
||||
}
|
||||
|
||||
vec4 sample_c(vec2 uv)
|
||||
{
|
||||
return texture(TextureSampler, uv);
|
||||
}
|
||||
|
||||
vec4 ps_crt(uint i)
|
||||
{
|
||||
vec4 mask[4] = vec4[4](
|
||||
@@ -384,4 +389,44 @@ void ps_filter_lottes()
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef ps_4x_rgss
|
||||
void ps_4x_rgss()
|
||||
{
|
||||
vec2 dxy = vec2(dFdx(PSin_t.x), dFdy(PSin_t.y));
|
||||
vec3 color = vec3(0);
|
||||
|
||||
float s = 1.0/8.0;
|
||||
float l = 3.0/8.0;
|
||||
|
||||
color += sample_c(PSin_t + vec2( s, l) * dxy).rgb;
|
||||
color += sample_c(PSin_t + vec2( l,-s) * dxy).rgb;
|
||||
color += sample_c(PSin_t + vec2(-s,-l) * dxy).rgb;
|
||||
color += sample_c(PSin_t + vec2(-l, s) * dxy).rgb;
|
||||
|
||||
SV_Target0 = vec4(color * 0.25,1);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef ps_automagical_supersampling
|
||||
void ps_automagical_supersampling()
|
||||
{
|
||||
vec2 ratio = (u_source_size / u_target_size) * 0.5;
|
||||
vec2 steps = floor(ratio);
|
||||
vec3 col = sample_c(PSin_t).rgb;
|
||||
float div = 1;
|
||||
|
||||
for (float y = 0; y < steps.y; y++)
|
||||
{
|
||||
for (float x = 0; x < steps.x; x++)
|
||||
{
|
||||
vec2 offset = vec2(x,y) - ratio * 0.5;
|
||||
col += sample_c(PSin_t + offset * u_rcp_source_resolution * 2.0).rgb;
|
||||
div++;
|
||||
}
|
||||
}
|
||||
|
||||
SV_Target0 = vec4(col / div, 1);
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
@@ -53,7 +53,7 @@ void main()
|
||||
AU2 gxy = ARmp8x8(gl_LocalInvocationID.x)+AU2(gl_WorkGroupID.x<<4u,gl_WorkGroupID.y<<4u);
|
||||
|
||||
// Filter.
|
||||
AF4 c;
|
||||
AF4 c = vec4(0.0f);
|
||||
CasFilter(c.r, c.g, c.b, gxy, const0, const1, sharpenOnly != 0);
|
||||
imageStore(imgDst, ASU2(gxy), c);
|
||||
gxy.x += 8u;
|
||||
|
||||
@@ -348,7 +348,7 @@ layout(push_constant) uniform cb10
|
||||
void ps_yuv()
|
||||
{
|
||||
vec4 i = sample_c(v_tex);
|
||||
vec4 o;
|
||||
vec4 o = vec4(0.0f);
|
||||
|
||||
mat3 rgb2yuv;
|
||||
rgb2yuv[0] = vec3(0.587, -0.311, -0.419);
|
||||
@@ -361,7 +361,8 @@ void ps_yuv()
|
||||
float Cr = float(0xE0)/255.0f * yuv.y + float(0x80)/255.0f;
|
||||
float Cb = float(0xE0)/255.0f * yuv.z + float(0x80)/255.0f;
|
||||
|
||||
switch(EMODA) {
|
||||
switch(EMODA)
|
||||
{
|
||||
case 0:
|
||||
o.a = i.a;
|
||||
break;
|
||||
@@ -376,7 +377,8 @@ void ps_yuv()
|
||||
break;
|
||||
}
|
||||
|
||||
switch(EMODC) {
|
||||
switch(EMODC)
|
||||
{
|
||||
case 0:
|
||||
o.rgb = i.rgb;
|
||||
break;
|
||||
|
||||
@@ -359,4 +359,44 @@ void ps_filter_lottes()
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef ps_4x_rgss
|
||||
void ps_4x_rgss()
|
||||
{
|
||||
vec2 dxy = vec2(dFdx(v_tex.x), dFdy(v_tex.y));
|
||||
vec3 color = vec3(0);
|
||||
|
||||
float s = 1.0/8.0;
|
||||
float l = 3.0/8.0;
|
||||
|
||||
color += sample_c(v_tex + vec2( s, l) * dxy).rgb;
|
||||
color += sample_c(v_tex + vec2( l,-s) * dxy).rgb;
|
||||
color += sample_c(v_tex + vec2(-s,-l) * dxy).rgb;
|
||||
color += sample_c(v_tex + vec2(-l, s) * dxy).rgb;
|
||||
|
||||
o_col0 = vec4(color * 0.25,1);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef ps_automagical_supersampling
|
||||
void ps_automagical_supersampling()
|
||||
{
|
||||
vec2 ratio = (u_source_size / u_target_size) * 0.5;
|
||||
vec2 steps = floor(ratio);
|
||||
vec3 col = sample_c(v_tex).rgb;
|
||||
float div = 1;
|
||||
|
||||
for (float y = 0; y < steps.y; y++)
|
||||
{
|
||||
for (float x = 0; x < steps.x; x++)
|
||||
{
|
||||
vec2 offset = vec2(x,y) - ratio * 0.5;
|
||||
col += sample_c(v_tex + offset * u_rcp_source_resolution * 2.0).rgb;
|
||||
div++;
|
||||
}
|
||||
}
|
||||
|
||||
o_col0 = vec4(col / div, 1);
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
@@ -336,12 +336,12 @@ layout(set = 1, binding = 1) uniform texture2D Palette;
|
||||
#endif
|
||||
|
||||
#if PS_FEEDBACK_LOOP_IS_NEEDED
|
||||
#ifndef DISABLE_TEXTURE_BARRIER
|
||||
layout(input_attachment_index = 0, set = 2, binding = 0) uniform subpassInput RtSampler;
|
||||
vec4 sample_from_rt() { return subpassLoad(RtSampler); }
|
||||
#else
|
||||
#if defined(DISABLE_TEXTURE_BARRIER) || defined(HAS_FEEDBACK_LOOP_LAYOUT)
|
||||
layout(set = 2, binding = 0) uniform texture2D RtSampler;
|
||||
vec4 sample_from_rt() { return texelFetch(RtSampler, ivec2(gl_FragCoord.xy), 0); }
|
||||
#else
|
||||
layout(input_attachment_index = 0, set = 2, binding = 0) uniform subpassInput RtSampler;
|
||||
vec4 sample_from_rt() { return subpassLoad(RtSampler); }
|
||||
#endif
|
||||
#endif
|
||||
|
||||
@@ -1218,7 +1218,7 @@ void main()
|
||||
#endif
|
||||
|
||||
#if (SW_AD_TO_HW)
|
||||
vec4 RT = trunc(subpassLoad(RtSampler) * 255.0f + 0.1f);
|
||||
vec4 RT = trunc(sample_from_rt() * 255.0f + 0.1f);
|
||||
vec4 alpha_blend = vec4(RT.a / 128.0f);
|
||||
#else
|
||||
vec4 alpha_blend = vec4(C.a / 128.0f);
|
||||
|
||||
@@ -1,68 +0,0 @@
|
||||
# Try to find Wayland on a Unix system
|
||||
#
|
||||
# This will define:
|
||||
#
|
||||
# Wayland_FOUND - True if Wayland is found
|
||||
#
|
||||
# The following imported targets:
|
||||
# Wayland::Client - Imported Client
|
||||
# Wayland::Server - Imported Server
|
||||
# Wayland::Egl - Imported Egl
|
||||
# Wayland::Cursor - Imported Cursor
|
||||
#
|
||||
# Copyright (c) 2013 Martin Gräßlin <mgraesslin@kde.org>
|
||||
#
|
||||
# Redistribution and use is allowed according to the terms of the BSD license.
|
||||
# For details see the accompanying COPYING-CMAKE-SCRIPTS file.
|
||||
|
||||
IF (NOT WIN32)
|
||||
IF (WAYLAND_INCLUDE_DIR AND WAYLAND_LIBRARIES)
|
||||
# In the cache already
|
||||
SET(WAYLAND_FIND_QUIETLY TRUE)
|
||||
ENDIF ()
|
||||
|
||||
FIND_PATH(WAYLAND_CLIENT_INCLUDE_DIR NAMES wayland-client.h)
|
||||
FIND_PATH(WAYLAND_SERVER_INCLUDE_DIR NAMES wayland-server.h)
|
||||
FIND_PATH(WAYLAND_EGL_INCLUDE_DIR NAMES wayland-egl.h)
|
||||
FIND_PATH(WAYLAND_CURSOR_INCLUDE_DIR NAMES wayland-cursor.h)
|
||||
|
||||
FIND_LIBRARY(WAYLAND_CLIENT_LIBRARIES NAMES wayland-client)
|
||||
FIND_LIBRARY(WAYLAND_SERVER_LIBRARIES NAMES wayland-server)
|
||||
FIND_LIBRARY(WAYLAND_EGL_LIBRARIES NAMES wayland-egl)
|
||||
FIND_LIBRARY(WAYLAND_CURSOR_LIBRARIES NAMES wayland-cursor)
|
||||
|
||||
include(FindPackageHandleStandardArgs)
|
||||
|
||||
# FIND_PACKAGE_HANDLE_STANDARD_ARGS is just meant to find the main package and set package found. Not set variables or find individual libs
|
||||
FIND_PACKAGE_HANDLE_STANDARD_ARGS(Wayland REQUIRED_VARS
|
||||
WAYLAND_CLIENT_LIBRARIES WAYLAND_CLIENT_INCLUDE_DIR
|
||||
WAYLAND_SERVER_LIBRARIES WAYLAND_SERVER_INCLUDE_DIR
|
||||
WAYLAND_EGL_LIBRARIES WAYLAND_EGL_INCLUDE_DIR
|
||||
WAYLAND_CURSOR_LIBRARIES WAYLAND_CURSOR_INCLUDE_DIR
|
||||
)
|
||||
|
||||
if (WAYLAND_CLIENT_INCLUDE_DIR AND WAYLAND_CLIENT_LIBRARIES AND NOT TARGET Wayland::Client)
|
||||
add_library(Wayland::Client UNKNOWN IMPORTED)
|
||||
set_target_properties(Wayland::Client PROPERTIES INTERFACE_INCLUDE_DIRECTORIES "${WAYLAND_CLIENT_INCLUDE_DIR}" IMPORTED_LOCATION "${WAYLAND_CLIENT_LIBRARIES}")
|
||||
endif()
|
||||
if (WAYLAND_SERVER_INCLUDE_DIR AND WAYLAND_SERVER_LIBRARIES AND NOT TARGET Wayland::Server)
|
||||
add_library(Wayland::Server UNKNOWN IMPORTED)
|
||||
set_target_properties(Wayland::Server PROPERTIES INTERFACE_INCLUDE_DIRECTORIES "${WAYLAND_SERVER_INCLUDE_DIR}" IMPORTED_LOCATION "${WAYLAND_SERVER_LIBRARIES}")
|
||||
endif()
|
||||
if (WAYLAND_EGL_INCLUDE_DIR AND WAYLAND_EGL_LIBRARIES AND NOT TARGET Wayland::Egl)
|
||||
add_library(Wayland::Egl UNKNOWN IMPORTED)
|
||||
set_target_properties(Wayland::Egl PROPERTIES INTERFACE_INCLUDE_DIRECTORIES "${WAYLAND_EGL_INCLUDE_DIR}" IMPORTED_LOCATION "${WAYLAND_EGL_LIBRARIES}")
|
||||
endif()
|
||||
if (WAYLAND_CURSOR_INCLUDE_DIR AND WAYLAND_CURSOR_LIBRARIES AND NOT TARGET Wayland::Cursor)
|
||||
add_library(Wayland::Cursor UNKNOWN IMPORTED)
|
||||
set_target_properties(Wayland::Cursor PROPERTIES INTERFACE_INCLUDE_DIRECTORIES "${WAYLAND_CURSOR_INCLUDE_DIR}" IMPORTED_LOCATION "${WAYLAND_CURSOR_LIBRARIES}")
|
||||
endif()
|
||||
|
||||
MARK_AS_ADVANCED(
|
||||
WAYLAND_CLIENT_INCLUDE_DIR WAYLAND_CLIENT_LIBRARIES
|
||||
WAYLAND_SERVER_INCLUDE_DIR WAYLAND_SERVER_LIBRARIES
|
||||
WAYLAND_EGL_INCLUDE_DIR WAYLAND_EGL_LIBRARIES
|
||||
WAYLAND_CURSOR_INCLUDE_DIR WAYLAND_CURSOR_LIBRARIES
|
||||
)
|
||||
|
||||
ENDIF ()
|
||||
@@ -82,7 +82,9 @@ else()
|
||||
make_imported_target_if_missing(X11::X11 X11)
|
||||
|
||||
if(WAYLAND_API)
|
||||
find_package(Wayland REQUIRED)
|
||||
find_package(ECM REQUIRED NO_MODULE)
|
||||
list(APPEND CMAKE_MODULE_PATH "${ECM_MODULE_PATH}")
|
||||
find_package(Wayland REQUIRED Egl)
|
||||
endif()
|
||||
|
||||
find_package(Libbacktrace)
|
||||
|
||||
@@ -30,6 +30,7 @@ target_sources(common PRIVATE
|
||||
Semaphore.cpp
|
||||
SettingsWrapper.cpp
|
||||
StringUtil.cpp
|
||||
TextureDecompress.cpp
|
||||
Timer.cpp
|
||||
ThreadPool.cpp
|
||||
WAVWriter.cpp
|
||||
@@ -94,6 +95,7 @@ target_sources(common PRIVATE
|
||||
SettingsWrapper.h
|
||||
StringUtil.h
|
||||
Timer.h
|
||||
TextureDecompress.h
|
||||
Threading.h
|
||||
ThreadPool.h
|
||||
TraceLog.h
|
||||
|
||||
@@ -38,6 +38,7 @@
|
||||
|
||||
#if defined(_WIN32)
|
||||
#include "RedtapeWindows.h"
|
||||
#include <io.h>
|
||||
#include <winioctl.h>
|
||||
#include <share.h>
|
||||
#include <shlobj.h>
|
||||
@@ -190,6 +191,27 @@ void Path::SanitizeFileName(std::string* str, bool strip_slashes /* = true */)
|
||||
#endif
|
||||
}
|
||||
|
||||
bool Path::IsValidFileName(const std::string_view& str, bool allow_slashes)
|
||||
{
|
||||
const size_t len = str.length();
|
||||
size_t pos = 0;
|
||||
while (pos < len)
|
||||
{
|
||||
char32_t ch;
|
||||
pos += StringUtil::DecodeUTF8(str.data() + pos, pos - len, &ch);
|
||||
if (!FileSystemCharacterIsSane(ch, !allow_slashes))
|
||||
return false;
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
// Windows: Can't end filename with a period.
|
||||
if (len > 0 && str.back() == '.')
|
||||
return false;
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Path::IsAbsolute(const std::string_view& path)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
@@ -651,7 +673,7 @@ int FileSystem::OpenFDFile(const char* filename, int flags, int mode, Error* err
|
||||
|
||||
FileSystem::ManagedCFilePtr FileSystem::OpenManagedCFile(const char* filename, const char* mode, Error* error)
|
||||
{
|
||||
return ManagedCFilePtr(OpenCFile(filename, mode, error), [](std::FILE* fp) { std::fclose(fp); });
|
||||
return ManagedCFilePtr(OpenCFile(filename, mode, error));
|
||||
}
|
||||
|
||||
std::FILE* FileSystem::OpenSharedCFile(const char* filename, const char* mode, FileShareMode share_mode, Error* error)
|
||||
@@ -696,7 +718,7 @@ std::FILE* FileSystem::OpenSharedCFile(const char* filename, const char* mode, F
|
||||
|
||||
FileSystem::ManagedCFilePtr FileSystem::OpenManagedSharedCFile(const char* filename, const char* mode, FileShareMode share_mode, Error* error)
|
||||
{
|
||||
return ManagedCFilePtr(OpenSharedCFile(filename, mode, share_mode, error), [](std::FILE* fp) { std::fclose(fp); });
|
||||
return ManagedCFilePtr(OpenSharedCFile(filename, mode, share_mode, error));
|
||||
}
|
||||
|
||||
int FileSystem::FSeek64(std::FILE* fp, s64 offset, int whence)
|
||||
@@ -726,6 +748,13 @@ s64 FileSystem::FTell64(std::FILE* fp)
|
||||
|
||||
s64 FileSystem::FSize64(std::FILE* fp)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
const int fd = _fileno(fp);
|
||||
if (fd >= 0)
|
||||
{
|
||||
return _filelengthi64(fd);
|
||||
}
|
||||
#else
|
||||
const s64 pos = FTell64(fp);
|
||||
if (pos >= 0)
|
||||
{
|
||||
@@ -736,6 +765,7 @@ s64 FileSystem::FSize64(std::FILE* fp)
|
||||
return size;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
return -1;
|
||||
}
|
||||
@@ -760,12 +790,11 @@ std::optional<std::vector<u8>> FileSystem::ReadBinaryFile(const char* filename)
|
||||
|
||||
std::optional<std::vector<u8>> FileSystem::ReadBinaryFile(std::FILE* fp)
|
||||
{
|
||||
std::fseek(fp, 0, SEEK_END);
|
||||
const long size = std::ftell(fp);
|
||||
std::fseek(fp, 0, SEEK_SET);
|
||||
const s64 size = FSize64(fp);
|
||||
if (size < 0)
|
||||
return std::nullopt;
|
||||
|
||||
std::fseek(fp, 0, SEEK_SET);
|
||||
std::vector<u8> res(static_cast<size_t>(size));
|
||||
if (size > 0 && std::fread(res.data(), 1u, static_cast<size_t>(size), fp) != static_cast<size_t>(size))
|
||||
return std::nullopt;
|
||||
@@ -784,12 +813,11 @@ std::optional<std::string> FileSystem::ReadFileToString(const char* filename)
|
||||
|
||||
std::optional<std::string> FileSystem::ReadFileToString(std::FILE* fp)
|
||||
{
|
||||
std::fseek(fp, 0, SEEK_END);
|
||||
const long size = std::ftell(fp);
|
||||
std::fseek(fp, 0, SEEK_SET);
|
||||
const s64 size = FSize64(fp);
|
||||
if (size < 0)
|
||||
return std::nullopt;
|
||||
|
||||
std::fseek(fp, 0, SEEK_SET);
|
||||
std::string res;
|
||||
res.resize(static_cast<size_t>(size));
|
||||
// NOTE - assumes mode 'rb', for example, this will fail over missing Windows carriage return bytes
|
||||
|
||||
@@ -102,8 +102,17 @@ namespace FileSystem
|
||||
/// Rename file
|
||||
bool RenamePath(const char* OldPath, const char* NewPath);
|
||||
|
||||
/// Deleter functor for managed file pointers
|
||||
struct FileDeleter
|
||||
{
|
||||
void operator()(std::FILE* fp)
|
||||
{
|
||||
std::fclose(fp);
|
||||
}
|
||||
};
|
||||
|
||||
/// open files
|
||||
using ManagedCFilePtr = std::unique_ptr<std::FILE, void (*)(std::FILE*)>;
|
||||
using ManagedCFilePtr = std::unique_ptr<std::FILE, FileDeleter>;
|
||||
ManagedCFilePtr OpenManagedCFile(const char* filename, const char* mode, Error* error = nullptr);
|
||||
std::FILE* OpenCFile(const char* filename, const char* mode, Error* error = nullptr);
|
||||
int FSeek64(std::FILE* fp, s64 offset, int whence);
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
|
||||
#include "common/Pcsx2Types.h"
|
||||
#include "common/General.h"
|
||||
#include "common/ScopedGuard.h"
|
||||
#include "common/StringUtil.h"
|
||||
#include "common/Threading.h"
|
||||
#include "common/WindowInfo.h"
|
||||
@@ -70,66 +71,72 @@ std::string GetOSVersionString()
|
||||
|
||||
#ifdef DBUS_API
|
||||
|
||||
bool ChangeScreenSaverStateDBus(const bool inhibit_requested, const char* program_name, const char* reason)
|
||||
static bool SetScreensaverInhibitDBus(const bool inhibit_requested, const char* program_name, const char* reason)
|
||||
{
|
||||
static dbus_uint32_t s_cookie;
|
||||
// "error_dbus" doesn't need to be cleared in the end with "dbus_message_unref" at least if there is
|
||||
// no error set, since calling "dbus_error_free" reinitializes it like "dbus_error_init" after freeing.
|
||||
const char* bus_method = (inhibit_requested) ? "Inhibit" : "UnInhibit";
|
||||
DBusError error_dbus;
|
||||
dbus_error_init(&error_dbus);
|
||||
DBusConnection* connection = nullptr;
|
||||
static DBusConnection* s_comparison_connection;
|
||||
DBusMessage* message = nullptr;
|
||||
DBusMessage* response = nullptr;
|
||||
// Initialized here because initializations should be before "goto" statements.
|
||||
const char* bus_method = (inhibit_requested) ? "Inhibit" : "UnInhibit";
|
||||
// "dbus_bus_get" gets a pointer to the same connection in libdbus, if exists, without creating a new connection.
|
||||
// this doesn't need to be deleted, except if there's an error then calling "dbus_connection_unref", to free it,
|
||||
// might be better so a new connection is established on the next try.
|
||||
if (!(connection = dbus_bus_get(DBUS_BUS_SESSION, &error_dbus)) || (dbus_error_is_set(&error_dbus)))
|
||||
goto cleanup;
|
||||
if (!(message = dbus_message_new_method_call("org.freedesktop.ScreenSaver", "/org/freedesktop/ScreenSaver", "org.freedesktop.ScreenSaver", bus_method)))
|
||||
goto cleanup;
|
||||
// Initialize an append iterator for the message, gets freed with the message.
|
||||
DBusMessageIter message_itr;
|
||||
|
||||
ScopedGuard cleanup = [&]() {
|
||||
if (dbus_error_is_set(&error_dbus))
|
||||
dbus_error_free(&error_dbus);
|
||||
if (message)
|
||||
dbus_message_unref(message);
|
||||
if (response)
|
||||
dbus_message_unref(response);
|
||||
};
|
||||
|
||||
dbus_error_init(&error_dbus);
|
||||
// Calling dbus_bus_get() after the first time returns a pointer to the existing connection.
|
||||
connection = dbus_bus_get(DBUS_BUS_SESSION, &error_dbus);
|
||||
if (!connection || (dbus_error_is_set(&error_dbus)))
|
||||
return false;
|
||||
if (s_comparison_connection != connection)
|
||||
{
|
||||
dbus_connection_set_exit_on_disconnect(connection, false);
|
||||
s_cookie = 0;
|
||||
s_comparison_connection = connection;
|
||||
}
|
||||
message = dbus_message_new_method_call("org.freedesktop.ScreenSaver", "/org/freedesktop/ScreenSaver", "org.freedesktop.ScreenSaver", bus_method);
|
||||
if (!message)
|
||||
return false;
|
||||
// Initialize an append iterator for the message, gets freed with the message.
|
||||
dbus_message_iter_init_append(message, &message_itr);
|
||||
if (inhibit_requested)
|
||||
{
|
||||
// Guard against repeat inhibitions which would add extra inhibitors each generating a different cookie.
|
||||
if (s_cookie)
|
||||
return false;
|
||||
// Append process/window name.
|
||||
if (!dbus_message_iter_append_basic(&message_itr, DBUS_TYPE_STRING, &program_name))
|
||||
goto cleanup;
|
||||
return false;
|
||||
// Append reason for inhibiting the screensaver.
|
||||
if (!dbus_message_iter_append_basic(&message_itr, DBUS_TYPE_STRING, &reason))
|
||||
goto cleanup;
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Only Append the cookie.
|
||||
if (!dbus_message_iter_append_basic(&message_itr, DBUS_TYPE_UINT32, &s_cookie))
|
||||
goto cleanup;
|
||||
return false;
|
||||
}
|
||||
// Send message and get response.
|
||||
if (!(response = dbus_connection_send_with_reply_and_block(connection, message, DBUS_TIMEOUT_USE_DEFAULT, &error_dbus))
|
||||
|| dbus_error_is_set(&error_dbus))
|
||||
goto cleanup;
|
||||
response = dbus_connection_send_with_reply_and_block(connection, message, DBUS_TIMEOUT_USE_DEFAULT, &error_dbus);
|
||||
if (!response || dbus_error_is_set(&error_dbus))
|
||||
return false;
|
||||
s_cookie = 0;
|
||||
if (inhibit_requested)
|
||||
{
|
||||
// Get the cookie from the response message.
|
||||
if (!dbus_message_get_args(response, &error_dbus, DBUS_TYPE_UINT32, &s_cookie, DBUS_TYPE_INVALID))
|
||||
goto cleanup;
|
||||
if (!dbus_message_get_args(response, &error_dbus, DBUS_TYPE_UINT32, &s_cookie, DBUS_TYPE_INVALID) || dbus_error_is_set(&error_dbus))
|
||||
return false;
|
||||
}
|
||||
dbus_message_unref(message);
|
||||
dbus_message_unref(response);
|
||||
return true;
|
||||
cleanup:
|
||||
if (dbus_error_is_set(&error_dbus))
|
||||
dbus_error_free(&error_dbus);
|
||||
if (connection)
|
||||
dbus_connection_unref(connection);
|
||||
if (message)
|
||||
dbus_message_unref(message);
|
||||
if (response)
|
||||
dbus_message_unref(response);
|
||||
return false;
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -176,11 +183,10 @@ bool WindowInfo::InhibitScreensaver(const WindowInfo& wi, bool inhibit)
|
||||
|
||||
#ifdef DBUS_API
|
||||
|
||||
return ChangeScreenSaverStateDBus(inhibit, "PCSX2", "PCSX2 VM is running.");
|
||||
return SetScreensaverInhibitDBus(inhibit, "PCSX2", "PCSX2 VM is running.");
|
||||
|
||||
#else
|
||||
|
||||
//ChangeScreenSaverStateDBus
|
||||
if (s_inhibit_window_info.has_value())
|
||||
{
|
||||
// Bit of extra logic here, because wx spams it and we don't want to
|
||||
|
||||
@@ -41,6 +41,9 @@ namespace Path
|
||||
std::string SanitizeFileName(const std::string_view& str, bool strip_slashes = true);
|
||||
void SanitizeFileName(std::string* str, bool strip_slashes = true);
|
||||
|
||||
/// Returns true if the specified filename is valid on this operating system.
|
||||
bool IsValidFileName(const std::string_view& str, bool allow_slashes = false);
|
||||
|
||||
/// Returns true if the specified path is an absolute path (C:\Path on Windows or /path on Unix).
|
||||
bool IsAbsolute(const std::string_view& path);
|
||||
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
|
||||
#pragma once
|
||||
#include "Pcsx2Types.h"
|
||||
#include <algorithm>
|
||||
#include <charconv>
|
||||
#include <cstdarg>
|
||||
#include <cstddef>
|
||||
@@ -340,4 +341,15 @@ namespace StringUtil
|
||||
/// Converts unsigned 128-bit data to string.
|
||||
std::string U128ToString(const u128& u);
|
||||
std::string& AppendU128ToString(const u128& u, std::string& s);
|
||||
|
||||
template <typename ContainerType>
|
||||
static inline bool ContainsSubString(const ContainerType& haystack, const std::string_view& needle)
|
||||
{
|
||||
using ValueType = typename ContainerType::value_type;
|
||||
if (needle.empty())
|
||||
return std::empty(haystack);
|
||||
|
||||
return std::search(std::begin(haystack), std::end(haystack), reinterpret_cast<const ValueType*>(needle.data()),
|
||||
reinterpret_cast<const ValueType*>(needle.data() + needle.length())) != std::end(haystack);
|
||||
}
|
||||
} // namespace StringUtil
|
||||
|
||||
@@ -0,0 +1,199 @@
|
||||
// See TextureDecompress.cpp for license info.
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable:4201) // nonstandard extension used: nameless struct/union
|
||||
#endif
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <algorithm>
|
||||
#include <math.h>
|
||||
#include <assert.h>
|
||||
|
||||
enum BC4Mode
|
||||
{
|
||||
BC4_UNORM = 0,
|
||||
BC4_SNORM = 1
|
||||
};
|
||||
|
||||
enum BC5Mode
|
||||
{
|
||||
BC5_UNORM = 0,
|
||||
BC5_SNORM = 1
|
||||
};
|
||||
|
||||
void DecompressBlockBC1(uint32_t x, uint32_t y, uint32_t stride,
|
||||
const uint8_t* blockStorage, unsigned char* image);
|
||||
void DecompressBlockBC2(uint32_t x, uint32_t y, uint32_t stride,
|
||||
const uint8_t* blockStorage, unsigned char* image);
|
||||
void DecompressBlockBC3(uint32_t x, uint32_t y, uint32_t stride,
|
||||
const uint8_t* blockStorage, unsigned char* image);
|
||||
void DecompressBlockBC4(uint32_t x, uint32_t y, uint32_t stride,
|
||||
enum BC4Mode mode, const uint8_t* blockStorage, unsigned char* image);
|
||||
void DecompressBlockBC5(uint32_t x, uint32_t y, uint32_t stride,
|
||||
enum BC5Mode mode, const uint8_t* blockStorage, unsigned char* image);
|
||||
|
||||
namespace bc7decomp
|
||||
{
|
||||
|
||||
enum eNoClamp { cNoClamp };
|
||||
|
||||
template <typename S> inline S clamp(S value, S low, S high) { return (value < low) ? low : ((value > high) ? high : value); }
|
||||
|
||||
class color_rgba
|
||||
{
|
||||
public:
|
||||
union
|
||||
{
|
||||
uint8_t m_comps[4];
|
||||
|
||||
struct
|
||||
{
|
||||
uint8_t r;
|
||||
uint8_t g;
|
||||
uint8_t b;
|
||||
uint8_t a;
|
||||
};
|
||||
};
|
||||
|
||||
inline color_rgba()
|
||||
{
|
||||
static_assert(sizeof(*this) == 4, "sizeof(*this) != 4");
|
||||
}
|
||||
|
||||
inline color_rgba(int y)
|
||||
{
|
||||
set(y);
|
||||
}
|
||||
|
||||
inline color_rgba(int y, int na)
|
||||
{
|
||||
set(y, na);
|
||||
}
|
||||
|
||||
inline color_rgba(int sr, int sg, int sb, int sa)
|
||||
{
|
||||
set(sr, sg, sb, sa);
|
||||
}
|
||||
|
||||
inline color_rgba(eNoClamp, int sr, int sg, int sb, int sa)
|
||||
{
|
||||
set_noclamp_rgba((uint8_t)sr, (uint8_t)sg, (uint8_t)sb, (uint8_t)sa);
|
||||
}
|
||||
|
||||
inline color_rgba& set_noclamp_y(int y)
|
||||
{
|
||||
m_comps[0] = (uint8_t)y;
|
||||
m_comps[1] = (uint8_t)y;
|
||||
m_comps[2] = (uint8_t)y;
|
||||
m_comps[3] = (uint8_t)255;
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline color_rgba &set_noclamp_rgba(int sr, int sg, int sb, int sa)
|
||||
{
|
||||
m_comps[0] = (uint8_t)sr;
|
||||
m_comps[1] = (uint8_t)sg;
|
||||
m_comps[2] = (uint8_t)sb;
|
||||
m_comps[3] = (uint8_t)sa;
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline color_rgba &set(int y)
|
||||
{
|
||||
m_comps[0] = static_cast<uint8_t>(clamp<int>(y, 0, 255));
|
||||
m_comps[1] = m_comps[0];
|
||||
m_comps[2] = m_comps[0];
|
||||
m_comps[3] = 255;
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline color_rgba &set(int y, int na)
|
||||
{
|
||||
m_comps[0] = static_cast<uint8_t>(clamp<int>(y, 0, 255));
|
||||
m_comps[1] = m_comps[0];
|
||||
m_comps[2] = m_comps[0];
|
||||
m_comps[3] = static_cast<uint8_t>(clamp<int>(na, 0, 255));
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline color_rgba &set(int sr, int sg, int sb, int sa)
|
||||
{
|
||||
m_comps[0] = static_cast<uint8_t>(clamp<int>(sr, 0, 255));
|
||||
m_comps[1] = static_cast<uint8_t>(clamp<int>(sg, 0, 255));
|
||||
m_comps[2] = static_cast<uint8_t>(clamp<int>(sb, 0, 255));
|
||||
m_comps[3] = static_cast<uint8_t>(clamp<int>(sa, 0, 255));
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline color_rgba &set_rgb(int sr, int sg, int sb)
|
||||
{
|
||||
m_comps[0] = static_cast<uint8_t>(clamp<int>(sr, 0, 255));
|
||||
m_comps[1] = static_cast<uint8_t>(clamp<int>(sg, 0, 255));
|
||||
m_comps[2] = static_cast<uint8_t>(clamp<int>(sb, 0, 255));
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline color_rgba &set_rgb(const color_rgba &other)
|
||||
{
|
||||
r = other.r;
|
||||
g = other.g;
|
||||
b = other.b;
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline const uint8_t &operator[] (uint32_t index) const { assert(index < 4); return m_comps[index]; }
|
||||
inline uint8_t &operator[] (uint32_t index) { assert(index < 4); return m_comps[index]; }
|
||||
|
||||
inline void clear()
|
||||
{
|
||||
m_comps[0] = 0;
|
||||
m_comps[1] = 0;
|
||||
m_comps[2] = 0;
|
||||
m_comps[3] = 0;
|
||||
}
|
||||
|
||||
inline bool operator== (const color_rgba &rhs) const
|
||||
{
|
||||
if (m_comps[0] != rhs.m_comps[0]) return false;
|
||||
if (m_comps[1] != rhs.m_comps[1]) return false;
|
||||
if (m_comps[2] != rhs.m_comps[2]) return false;
|
||||
if (m_comps[3] != rhs.m_comps[3]) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
inline bool operator!= (const color_rgba &rhs) const
|
||||
{
|
||||
return !(*this == rhs);
|
||||
}
|
||||
|
||||
inline bool operator<(const color_rgba &rhs) const
|
||||
{
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
if (m_comps[i] < rhs.m_comps[i])
|
||||
return true;
|
||||
else if (m_comps[i] != rhs.m_comps[i])
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
inline int get_601_luma() const { return (19595U * m_comps[0] + 38470U * m_comps[1] + 7471U * m_comps[2] + 32768U) >> 16U; }
|
||||
inline int get_709_luma() const { return (13938U * m_comps[0] + 46869U * m_comps[1] + 4729U * m_comps[2] + 32768U) >> 16U; }
|
||||
inline int get_luma(bool luma_601) const { return luma_601 ? get_601_luma() : get_709_luma(); }
|
||||
|
||||
static color_rgba comp_min(const color_rgba& a, const color_rgba& b) { return color_rgba(std::min(a[0], b[0]), std::min(a[1], b[1]), std::min(a[2], b[2]), std::min(a[3], b[3])); }
|
||||
static color_rgba comp_max(const color_rgba& a, const color_rgba& b) { return color_rgba(std::max(a[0], b[0]), std::max(a[1], b[1]), std::max(a[2], b[2]), std::max(a[3], b[3])); }
|
||||
};
|
||||
|
||||
bool unpack_bc7(const void *pBlock, color_rgba *pPixels);
|
||||
|
||||
} // namespace bc7decomp
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(pop)
|
||||
#endif
|
||||
@@ -69,6 +69,7 @@
|
||||
<ClCompile Include="StackWalker.cpp" />
|
||||
<ClCompile Include="StringUtil.cpp" />
|
||||
<ClCompile Include="SettingsWrapper.cpp" />
|
||||
<ClCompile Include="TextureDecompress.cpp" />
|
||||
<ClCompile Include="Timer.cpp" />
|
||||
<ClCompile Include="WAVWriter.cpp" />
|
||||
<ClCompile Include="WindowInfo.cpp" />
|
||||
@@ -141,6 +142,7 @@
|
||||
<ClInclude Include="RedtapeWindows.h" />
|
||||
<ClInclude Include="SafeArray.h" />
|
||||
<ClInclude Include="ThreadPool.h" />
|
||||
<ClInclude Include="TextureDecompress.h" />
|
||||
<ClInclude Include="Timer.h" />
|
||||
<ClInclude Include="WAVWriter.h" />
|
||||
<ClInclude Include="WindowInfo.h" />
|
||||
|
||||
@@ -142,6 +142,9 @@
|
||||
<ClCompile Include="Assertions.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="TextureDecompress.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="AlignedMalloc.h">
|
||||
@@ -354,6 +357,9 @@
|
||||
<ClInclude Include="Error.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="TextureDecompress.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Filter Include="Source Files">
|
||||
|
||||
@@ -334,6 +334,14 @@ void Host::SetFullscreen(bool enabled)
|
||||
{
|
||||
}
|
||||
|
||||
void Host::OnCaptureStarted(const std::string& filename)
|
||||
{
|
||||
}
|
||||
|
||||
void Host::OnCaptureStopped()
|
||||
{
|
||||
}
|
||||
|
||||
void Host::RequestExit(bool allow_confirm)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -224,7 +224,6 @@ void AutoUpdaterDialog::getLatestReleaseComplete(QNetworkReply* reply)
|
||||
bool is_symbols = false;
|
||||
bool is_avx2 = false;
|
||||
bool is_sse4 = false;
|
||||
bool is_qt_asset = false;
|
||||
bool is_perfect_match = false;
|
||||
for (const QJsonValue& additional_tag : additional_tags_array)
|
||||
{
|
||||
@@ -235,12 +234,6 @@ void AutoUpdaterDialog::getLatestReleaseComplete(QNetworkReply* reply)
|
||||
is_symbols = true;
|
||||
break;
|
||||
}
|
||||
else if (additional_tag_str.startsWith(QStringLiteral("Qt")))
|
||||
{
|
||||
// found a qt build
|
||||
// Note: The website improperly parses macOS file names, and gives them the tag "Qt.tar" instead of "Qt"
|
||||
is_qt_asset = true;
|
||||
}
|
||||
else if (additional_tag_str == QStringLiteral("SSE4"))
|
||||
{
|
||||
is_sse4 = true;
|
||||
@@ -258,7 +251,7 @@ void AutoUpdaterDialog::getLatestReleaseComplete(QNetworkReply* reply)
|
||||
#endif
|
||||
}
|
||||
|
||||
if (!is_qt_asset || is_symbols || (!x86caps.hasAVX2 && is_avx2))
|
||||
if (is_symbols || (!x86caps.hasAVX2 && is_avx2))
|
||||
{
|
||||
// skip this asset
|
||||
continue;
|
||||
|
||||
@@ -68,9 +68,6 @@ target_sources(pcsx2-qt PRIVATE
|
||||
Settings/ControllerSettingsDialog.h
|
||||
Settings/ControllerSettingsDialog.ui
|
||||
Settings/ControllerSettingWidgetBinder.h
|
||||
Settings/CreateMemoryCardDialog.cpp
|
||||
Settings/CreateMemoryCardDialog.h
|
||||
Settings/CreateMemoryCardDialog.ui
|
||||
Settings/DebugSettingsWidget.cpp
|
||||
Settings/DebugSettingsWidget.h
|
||||
Settings/DebugSettingsWidget.ui
|
||||
@@ -114,6 +111,9 @@ target_sources(pcsx2-qt PRIVATE
|
||||
Settings/MemoryCardConvertDialog.ui
|
||||
Settings/MemoryCardConvertWorker.cpp
|
||||
Settings/MemoryCardConvertWorker.h
|
||||
Settings/MemoryCardCreateDialog.cpp
|
||||
Settings/MemoryCardCreateDialog.h
|
||||
Settings/MemoryCardCreateDialog.ui
|
||||
Settings/MemoryCardSettingsWidget.cpp
|
||||
Settings/MemoryCardSettingsWidget.h
|
||||
Settings/MemoryCardSettingsWidget.ui
|
||||
@@ -204,8 +204,23 @@ if(WIN32)
|
||||
set_source_files_properties(${TS_FILES} PROPERTIES OUTPUT_LOCATION "${CMAKE_SOURCE_DIR}/bin/translations")
|
||||
qt_add_lrelease(pcsx2-qt TS_FILES ${TS_FILES})
|
||||
elseif(APPLE)
|
||||
# Hopefully macdeployqt will pick these up.
|
||||
qt_add_lrelease(pcsx2-qt TS_FILES ${TS_FILES})
|
||||
qt_add_lrelease(pcsx2-qt TS_FILES ${TS_FILES} QM_FILES_OUTPUT_VARIABLE QM_FILES)
|
||||
set(PCSX2_MACOS_LOCALIZATIONS)
|
||||
foreach (TS_FILE IN LISTS TS_FILES)
|
||||
get_filename_component(TS_FILE_NAME ${TS_FILE} NAME)
|
||||
set(regex "^pcsx2-qt_([a-zA-Z0-9\\-]+)\\.ts$")
|
||||
if (TS_FILE_NAME MATCHES ${regex})
|
||||
string(REGEX REPLACE ${regex} "\\1" language ${TS_FILE_NAME})
|
||||
string(REPLACE "-" "_" language ${language})
|
||||
set(PCSX2_MACOS_LOCALIZATIONS "${PCSX2_MACOS_LOCALIZATIONS}\n\t\t<string>${language}</string>")
|
||||
else()
|
||||
message(WARNING "Unrecognized ts file ${TS_FILE_NAME}")
|
||||
endif()
|
||||
endforeach()
|
||||
foreach (QM_FILE IN LISTS QM_FILES)
|
||||
target_sources(pcsx2-qt PRIVATE ${QM_FILE})
|
||||
set_source_files_properties(${QM_FILE} PROPERTIES MACOSX_PACKAGE_LOCATION Resources/translations/)
|
||||
endforeach()
|
||||
else()
|
||||
# TODO: Copy base translations.
|
||||
qt_add_lrelease(pcsx2-qt TS_FILES ${TS_FILES} QM_FILES_OUTPUT_VARIABLE QM_FILES)
|
||||
|
||||
@@ -582,7 +582,7 @@ static std::vector<u32> searchWorker(DebugInterface* cpu, u32 start, u32 end, T
|
||||
return hitAddresses;
|
||||
}
|
||||
|
||||
static std::vector<u32> searchWorkerString(DebugInterface* cpu, u32 start, u32 end, std::string value)
|
||||
static std::vector<u32> searchWorkerByteArray(DebugInterface* cpu, u32 start, u32 end, QByteArray value)
|
||||
{
|
||||
std::vector<u32> hitAddresses;
|
||||
for (u32 addr = start; addr < end; addr += 1)
|
||||
@@ -590,7 +590,7 @@ static std::vector<u32> searchWorkerString(DebugInterface* cpu, u32 start, u32 e
|
||||
bool hit = true;
|
||||
for (size_t i = 0; i < value.length(); i++)
|
||||
{
|
||||
if (static_cast<char>(cpu->read8(addr + i)) != value[i])
|
||||
if (cpu->read8(addr + i) != value[i])
|
||||
{
|
||||
hit = false;
|
||||
break;
|
||||
@@ -624,7 +624,9 @@ std::vector<u32> startWorker(DebugInterface* cpu, int type, u32 start, u32 end,
|
||||
case 5:
|
||||
return searchWorker<double>(cpu, start, end, value.toDouble());
|
||||
case 6:
|
||||
return searchWorkerString(cpu, start, end, value.toStdString());
|
||||
return searchWorkerByteArray(cpu, start, end, value.toUtf8());
|
||||
case 7:
|
||||
return searchWorkerByteArray(cpu, start, end, QByteArray::fromHex(value.toUtf8()));
|
||||
default:
|
||||
Console.Error("Debugger: Unknown type when doing memory search!");
|
||||
break;
|
||||
@@ -667,13 +669,24 @@ void CpuWidget::onSearchButtonClicked()
|
||||
|
||||
unsigned long long value;
|
||||
|
||||
if (searchType < 4)
|
||||
switch (searchType)
|
||||
{
|
||||
value = searchValue.toULongLong(&ok, searchHex ? 16 : 10);
|
||||
}
|
||||
else if (searchType != 6)
|
||||
{
|
||||
searchValue.toDouble(&ok);
|
||||
case 0:
|
||||
case 1:
|
||||
case 2:
|
||||
case 3:
|
||||
value = searchValue.toULongLong(&ok, searchHex ? 16 : 10);
|
||||
break;
|
||||
case 4:
|
||||
case 5:
|
||||
searchValue.toDouble(&ok);
|
||||
break;
|
||||
case 6:
|
||||
ok = !searchValue.isEmpty();
|
||||
break;
|
||||
case 7:
|
||||
ok = !searchValue.trimmed().isEmpty();
|
||||
break;
|
||||
}
|
||||
|
||||
if (!ok)
|
||||
@@ -684,6 +697,7 @@ void CpuWidget::onSearchButtonClicked()
|
||||
|
||||
switch (searchType)
|
||||
{
|
||||
case 7:
|
||||
case 6:
|
||||
case 5:
|
||||
case 4:
|
||||
|
||||
@@ -231,6 +231,11 @@
|
||||
<string>String</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Array of byte</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="2">
|
||||
|
||||
@@ -421,6 +421,8 @@ void MainWindow::connectVMThreadSignals(EmuThread* thread)
|
||||
connect(thread, &EmuThread::onVMResumed, this, &MainWindow::onVMResumed);
|
||||
connect(thread, &EmuThread::onVMStopped, this, &MainWindow::onVMStopped);
|
||||
connect(thread, &EmuThread::onGameChanged, this, &MainWindow::onGameChanged);
|
||||
connect(thread, &EmuThread::onCaptureStarted, this, &MainWindow::onCaptureStarted);
|
||||
connect(thread, &EmuThread::onCaptureStopped, this, &MainWindow::onCaptureStopped);
|
||||
|
||||
connect(m_ui.actionReset, &QAction::triggered, thread, &EmuThread::resetVM);
|
||||
connect(m_ui.actionPause, &QAction::toggled, thread, &EmuThread::setVMPaused);
|
||||
@@ -586,6 +588,10 @@ void MainWindow::onToolsVideoCaptureToggled(bool checked)
|
||||
if (!s_vm_valid)
|
||||
return;
|
||||
|
||||
// Reset the checked state, we'll get updated by the GS thread.
|
||||
QSignalBlocker sb(m_ui.actionToolsVideoCapture);
|
||||
m_ui.actionToolsVideoCapture->setChecked(!checked);
|
||||
|
||||
if (!checked)
|
||||
{
|
||||
g_emu_thread->endCapture();
|
||||
@@ -599,15 +605,29 @@ void MainWindow::onToolsVideoCaptureToggled(bool checked)
|
||||
QString path(QStringLiteral("%1.%2").arg(QString::fromStdString(GSGetBaseVideoFilename())).arg(container));
|
||||
path = QFileDialog::getSaveFileName(this, tr("Video Capture"), path, filter);
|
||||
if (path.isEmpty())
|
||||
{
|
||||
QSignalBlocker sb(m_ui.actionToolsVideoCapture);
|
||||
m_ui.actionToolsVideoCapture->setChecked(false);
|
||||
return;
|
||||
}
|
||||
|
||||
g_emu_thread->beginCapture(path);
|
||||
}
|
||||
|
||||
void MainWindow::onCaptureStarted(const QString& filename)
|
||||
{
|
||||
if (!s_vm_valid)
|
||||
return;
|
||||
|
||||
QSignalBlocker sb(m_ui.actionToolsVideoCapture);
|
||||
m_ui.actionToolsVideoCapture->setChecked(true);
|
||||
}
|
||||
|
||||
void MainWindow::onCaptureStopped()
|
||||
{
|
||||
if (!s_vm_valid)
|
||||
return;
|
||||
|
||||
QSignalBlocker sb(m_ui.actionToolsVideoCapture);
|
||||
m_ui.actionToolsVideoCapture->setChecked(false);
|
||||
}
|
||||
|
||||
void MainWindow::onSettingsTriggeredFromToolbar()
|
||||
{
|
||||
if (s_vm_valid)
|
||||
@@ -2114,7 +2134,11 @@ SettingsDialog* MainWindow::getSettingsDialog()
|
||||
connect(m_settings_dialog->getInterfaceSettingsWidget(), &InterfaceSettingsWidget::languageChanged, this, [this]() {
|
||||
// reopen settings dialog after it applies
|
||||
updateLanguage();
|
||||
g_main_window->doSettings("Interface");
|
||||
// If you doSettings now, on macOS, the window will somehow end up underneath the main window that was created above
|
||||
// Delay it slightly...
|
||||
QtHost::RunOnUIThread([]{
|
||||
g_main_window->doSettings("Interface");
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@@ -2124,7 +2148,11 @@ SettingsDialog* MainWindow::getSettingsDialog()
|
||||
void MainWindow::doSettings(const char* category /* = nullptr */)
|
||||
{
|
||||
SettingsDialog* dlg = getSettingsDialog();
|
||||
if (!dlg->isVisible())
|
||||
if (dlg->isVisible())
|
||||
{
|
||||
dlg->raise();
|
||||
}
|
||||
else
|
||||
{
|
||||
dlg->setModal(false);
|
||||
dlg->show();
|
||||
|
||||
@@ -183,6 +183,9 @@ private Q_SLOTS:
|
||||
void onGameChanged(const QString& title, const QString& elf_override, const QString& disc_path,
|
||||
const QString& serial, quint32 disc_crc, quint32 crc);
|
||||
|
||||
void onCaptureStarted(const QString& filename);
|
||||
void onCaptureStopped();
|
||||
|
||||
protected:
|
||||
void showEvent(QShowEvent* event) override;
|
||||
void closeEvent(QCloseEvent* event) override;
|
||||
|
||||
@@ -274,7 +274,7 @@
|
||||
<normaloff>.</normaloff>.</iconset>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string comment="In Toolbar">Start &File...</string>
|
||||
<string comment="In Toolbar">Start File</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionStartDisc">
|
||||
@@ -292,7 +292,7 @@
|
||||
<normaloff>.</normaloff>.</iconset>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string comment="In Toolbar">Start &Disc...</string>
|
||||
<string comment="In Toolbar">Start Disc</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionStartBios">
|
||||
@@ -310,7 +310,7 @@
|
||||
<normaloff>.</normaloff>.</iconset>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string comment="In Toolbar">Start &BIOS</string>
|
||||
<string comment="In Toolbar">Start BIOS</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionScanForNewGames">
|
||||
@@ -346,7 +346,7 @@
|
||||
<normaloff>.</normaloff>.</iconset>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string comment="In Toolbar">Shut &Down</string>
|
||||
<string comment="In Toolbar">Shut Down</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionPowerOffWithoutSaving">
|
||||
@@ -373,7 +373,7 @@
|
||||
<normaloff>.</normaloff>.</iconset>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string comment="In Toolbar">&Reset</string>
|
||||
<string comment="In Toolbar">Reset</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionPause">
|
||||
@@ -397,7 +397,7 @@
|
||||
<normaloff>.</normaloff>.</iconset>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string comment="In Toolbar">&Pause</string>
|
||||
<string comment="In Toolbar">Pause</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionToolbarLoadState">
|
||||
@@ -406,7 +406,7 @@
|
||||
<normaloff>.</normaloff>.</iconset>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string comment="In Toolbar">&Load State</string>
|
||||
<string comment="In Toolbar">Load State</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionToolbarSaveState">
|
||||
@@ -415,7 +415,7 @@
|
||||
<normaloff>.</normaloff>.</iconset>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string comment="In Toolbar">&Save State</string>
|
||||
<string comment="In Toolbar">Save State</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionExit">
|
||||
@@ -460,7 +460,7 @@
|
||||
<normaloff>.</normaloff>.</iconset>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string comment="In Toolbar">&Controllers</string>
|
||||
<string comment="In Toolbar">Controllers</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionHotkeySettings">
|
||||
@@ -635,7 +635,7 @@
|
||||
<normaloff>.</normaloff>.</iconset>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string comment="In Toolbar">&Settings</string>
|
||||
<string comment="In Toolbar">Settings</string>
|
||||
</property>
|
||||
<property name="menuRole">
|
||||
<enum>QAction::PreferencesRole</enum>
|
||||
@@ -681,7 +681,7 @@
|
||||
<normaloff>.</normaloff>.</iconset>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string comment="In Toolbar">&Screenshot</string>
|
||||
<string comment="In Toolbar">Screenshot</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionMemoryCardSettings">
|
||||
|
||||
@@ -1186,6 +1186,16 @@ void Host::SetFullscreen(bool enabled)
|
||||
g_emu_thread->setFullscreen(enabled, true);
|
||||
}
|
||||
|
||||
void Host::OnCaptureStarted(const std::string& filename)
|
||||
{
|
||||
emit g_emu_thread->onCaptureStarted(QString::fromStdString(filename));
|
||||
}
|
||||
|
||||
void Host::OnCaptureStopped()
|
||||
{
|
||||
emit g_emu_thread->onCaptureStopped();
|
||||
}
|
||||
|
||||
bool QtHost::InitializeConfig()
|
||||
{
|
||||
if (!EmuFolders::InitializeCriticalFolders())
|
||||
|
||||
@@ -158,6 +158,10 @@ Q_SIGNALS:
|
||||
/// Called when achievements are reloaded/refreshed (e.g. game change, login, option change).
|
||||
void onAchievementsRefreshed(quint32 id, const QString& game_info_string, quint32 total, quint32 points);
|
||||
|
||||
/// Called when video capture starts/stops.
|
||||
void onCaptureStarted(const QString& filename);
|
||||
void onCaptureStopped();
|
||||
|
||||
protected:
|
||||
void run();
|
||||
|
||||
|
||||
@@ -101,7 +101,10 @@ void ControllerSettingsDialog::onCurrentProfileChanged(int index)
|
||||
|
||||
void ControllerSettingsDialog::onNewProfileClicked()
|
||||
{
|
||||
const QString profile_name(QInputDialog::getText(this, tr("Create Input Profile"), tr("Enter the name for the new input profile:")));
|
||||
const QString profile_name(QInputDialog::getText(this, tr("Create Input Profile"),
|
||||
tr("Custom input profiles are used to override the Shared input profile for specific games.\n"
|
||||
"To apply a custom input profile to a game, go to its Game Properties, then change the 'Input Profile' on the Summary tab.\n\n"
|
||||
"Enter the name for the new input profile:")));
|
||||
if (profile_name.isEmpty())
|
||||
return;
|
||||
|
||||
|
||||
@@ -1479,6 +1479,16 @@
|
||||
<string extracomment="Lottes = Timothy Lottes, the creator of the shader filter. Leave as-is. CRT= Cathode Ray Tube, an old type of television technology.">Lottes CRT</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>4xRGSS downsampling (4x Rotated Grid SuperSampling)</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>NxAGSS downsampling (Nx Automatic Grid SuperSampling)</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
|
||||
@@ -74,9 +74,14 @@ void InputBindingWidget::initialize(
|
||||
|
||||
void InputBindingWidget::updateText()
|
||||
{
|
||||
const QString binding_tip(tr("\n\nLeft click to assign a new button\nShift + left click for additional bindings"));
|
||||
const QString binding_clear_tip(tr("\nRight click to clear binding"));
|
||||
|
||||
if (m_bindings.empty())
|
||||
{
|
||||
setText(QString());
|
||||
|
||||
setToolTip(tr("No bindings registered") + binding_tip);
|
||||
}
|
||||
else if (m_bindings.size() > 1)
|
||||
{
|
||||
@@ -93,12 +98,12 @@ void InputBindingWidget::updateText()
|
||||
ss << "\n";
|
||||
ss << binding;
|
||||
}
|
||||
setToolTip(QString::fromStdString(ss.str()));
|
||||
setToolTip(QString::fromStdString(ss.str()) + binding_tip + binding_clear_tip);
|
||||
}
|
||||
else
|
||||
{
|
||||
QString binding_text(QString::fromStdString(m_bindings[0]));
|
||||
setToolTip(binding_text);
|
||||
setToolTip(binding_text + binding_tip + binding_clear_tip);
|
||||
|
||||
// fix up accelerators, and if it's too long, ellipsise it
|
||||
if (binding_text.contains('&'))
|
||||
|
||||
@@ -22,12 +22,12 @@
|
||||
#include <QtWidgets/QMessageBox>
|
||||
#include <QtWidgets/QPushButton>
|
||||
|
||||
#include "Settings/CreateMemoryCardDialog.h"
|
||||
#include "Settings/MemoryCardCreateDialog.h"
|
||||
|
||||
#include "pcsx2/MemoryCardFile.h"
|
||||
#include "pcsx2/System.h"
|
||||
|
||||
CreateMemoryCardDialog::CreateMemoryCardDialog(QWidget* parent /* = nullptr */)
|
||||
MemoryCardCreateDialog::MemoryCardCreateDialog(QWidget* parent /* = nullptr */)
|
||||
: QDialog(parent)
|
||||
{
|
||||
m_ui.setupUi(this);
|
||||
@@ -35,7 +35,7 @@ CreateMemoryCardDialog::CreateMemoryCardDialog(QWidget* parent /* = nullptr */)
|
||||
|
||||
setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
|
||||
|
||||
connect(m_ui.name, &QLineEdit::textChanged, this, &CreateMemoryCardDialog::nameTextChanged);
|
||||
connect(m_ui.name, &QLineEdit::textChanged, this, &MemoryCardCreateDialog::nameTextChanged);
|
||||
|
||||
connect(m_ui.size8MB, &QRadioButton::clicked, this, [this]() { setType(MemoryCardType::File, MemoryCardFileType::PS2_8MB); });
|
||||
connect(m_ui.size16MB, &QRadioButton::clicked, this, [this]() { setType(MemoryCardType::File, MemoryCardFileType::PS2_16MB); });
|
||||
@@ -46,9 +46,9 @@ CreateMemoryCardDialog::CreateMemoryCardDialog(QWidget* parent /* = nullptr */)
|
||||
|
||||
disconnect(m_ui.buttonBox, &QDialogButtonBox::accepted, this, nullptr);
|
||||
|
||||
connect(m_ui.buttonBox->button(QDialogButtonBox::Ok), &QPushButton::clicked, this, &CreateMemoryCardDialog::createCard);
|
||||
connect(m_ui.buttonBox->button(QDialogButtonBox::Cancel), &QPushButton::clicked, this, &CreateMemoryCardDialog::close);
|
||||
connect(m_ui.buttonBox->button(QDialogButtonBox::RestoreDefaults), &QPushButton::clicked, this, &CreateMemoryCardDialog::restoreDefaults);
|
||||
connect(m_ui.buttonBox->button(QDialogButtonBox::Ok), &QPushButton::clicked, this, &MemoryCardCreateDialog::createCard);
|
||||
connect(m_ui.buttonBox->button(QDialogButtonBox::Cancel), &QPushButton::clicked, this, &MemoryCardCreateDialog::close);
|
||||
connect(m_ui.buttonBox->button(QDialogButtonBox::RestoreDefaults), &QPushButton::clicked, this, &MemoryCardCreateDialog::restoreDefaults);
|
||||
|
||||
#ifndef _WIN32
|
||||
m_ui.ntfsCompression->setEnabled(false);
|
||||
@@ -57,9 +57,9 @@ CreateMemoryCardDialog::CreateMemoryCardDialog(QWidget* parent /* = nullptr */)
|
||||
updateState();
|
||||
}
|
||||
|
||||
CreateMemoryCardDialog::~CreateMemoryCardDialog() = default;
|
||||
MemoryCardCreateDialog::~MemoryCardCreateDialog() = default;
|
||||
|
||||
void CreateMemoryCardDialog::nameTextChanged()
|
||||
void MemoryCardCreateDialog::nameTextChanged()
|
||||
{
|
||||
QString controlName(m_ui.name->text());
|
||||
const int cursorPos = m_ui.name->cursorPosition();
|
||||
@@ -76,14 +76,14 @@ void CreateMemoryCardDialog::nameTextChanged()
|
||||
updateState();
|
||||
}
|
||||
|
||||
void CreateMemoryCardDialog::setType(MemoryCardType type, MemoryCardFileType fileType)
|
||||
void MemoryCardCreateDialog::setType(MemoryCardType type, MemoryCardFileType fileType)
|
||||
{
|
||||
m_type = type;
|
||||
m_fileType = fileType;
|
||||
updateState();
|
||||
}
|
||||
|
||||
void CreateMemoryCardDialog::restoreDefaults()
|
||||
void MemoryCardCreateDialog::restoreDefaults()
|
||||
{
|
||||
setType(MemoryCardType::File, MemoryCardFileType::PS2_8MB);
|
||||
m_ui.size8MB->setChecked(true);
|
||||
@@ -94,7 +94,7 @@ void CreateMemoryCardDialog::restoreDefaults()
|
||||
m_ui.sizeFolder->setChecked(false);
|
||||
}
|
||||
|
||||
void CreateMemoryCardDialog::updateState()
|
||||
void MemoryCardCreateDialog::updateState()
|
||||
{
|
||||
const bool okay = (m_ui.name->text().length() > 0);
|
||||
|
||||
@@ -104,30 +104,27 @@ void CreateMemoryCardDialog::updateState()
|
||||
#endif
|
||||
}
|
||||
|
||||
void CreateMemoryCardDialog::createCard()
|
||||
void MemoryCardCreateDialog::createCard()
|
||||
{
|
||||
QString name(m_ui.name->text());
|
||||
std::string nameStr;
|
||||
|
||||
if (m_fileType == MemoryCardFileType::PS1)
|
||||
const QString name = m_ui.name->text();
|
||||
const std::string name_str = QStringLiteral("%1.%2").arg(name)
|
||||
.arg((m_fileType == MemoryCardFileType::PS1) ? QStringLiteral("mcr") : QStringLiteral("ps2"))
|
||||
.toStdString();
|
||||
if (!Path::IsValidFileName(name_str, false))
|
||||
{
|
||||
name += QStringLiteral(".mcr");
|
||||
}
|
||||
else
|
||||
{
|
||||
name += QStringLiteral(".ps2");
|
||||
QMessageBox::critical(this, tr("Create Memory Card"),
|
||||
tr("Failed to create the Memory Card, because the name '%1' contains one or more invalid characters.").arg(name));
|
||||
return;
|
||||
}
|
||||
|
||||
nameStr = name.toStdString();
|
||||
|
||||
if (FileMcd_GetCardInfo(nameStr).has_value())
|
||||
if (FileMcd_GetCardInfo(name_str).has_value())
|
||||
{
|
||||
QMessageBox::critical(this, tr("Create Memory Card"),
|
||||
tr("Failed to create the Memory Card, because another card with the name '%1' already exists.").arg(name));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!FileMcd_CreateNewCard(nameStr, m_type, m_fileType))
|
||||
if (!FileMcd_CreateNewCard(name_str, m_type, m_fileType))
|
||||
{
|
||||
QMessageBox::critical(this, tr("Create Memory Card"),
|
||||
tr("Failed to create the Memory Card, the log may contain more information."));
|
||||
@@ -137,7 +134,7 @@ void CreateMemoryCardDialog::createCard()
|
||||
#ifdef _WIN32
|
||||
if (m_ui.ntfsCompression->isChecked() && m_type == MemoryCardType::File)
|
||||
{
|
||||
const std::string fullPath(Path::Combine(EmuFolders::MemoryCards, nameStr));
|
||||
const std::string fullPath(Path::Combine(EmuFolders::MemoryCards, name_str));
|
||||
FileSystem::SetPathCompression(fullPath.c_str(), true);
|
||||
}
|
||||
#endif
|
||||
@@ -17,17 +17,17 @@
|
||||
|
||||
#include <QtWidgets/QDialog>
|
||||
|
||||
#include "ui_CreateMemoryCardDialog.h"
|
||||
#include "ui_MemoryCardCreateDialog.h"
|
||||
|
||||
#include "pcsx2/Config.h"
|
||||
|
||||
class CreateMemoryCardDialog final : public QDialog
|
||||
class MemoryCardCreateDialog final : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit CreateMemoryCardDialog(QWidget* parent = nullptr);
|
||||
~CreateMemoryCardDialog();
|
||||
explicit MemoryCardCreateDialog(QWidget* parent = nullptr);
|
||||
~MemoryCardCreateDialog();
|
||||
|
||||
private Q_SLOTS:
|
||||
void nameTextChanged();
|
||||
@@ -38,7 +38,7 @@ private:
|
||||
void restoreDefaults();
|
||||
void updateState();
|
||||
|
||||
Ui::CreateMemoryCardDialog m_ui;
|
||||
Ui::MemoryCardCreateDialog m_ui;
|
||||
|
||||
MemoryCardType m_type = MemoryCardType::File;
|
||||
MemoryCardFileType m_fileType = MemoryCardFileType::PS2_8MB;
|
||||
@@ -1,7 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>CreateMemoryCardDialog</class>
|
||||
<widget class="QDialog" name="CreateMemoryCardDialog">
|
||||
<class>MemoryCardCreateDialog</class>
|
||||
<widget class="QDialog" name="MemoryCardCreateDialog">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
@@ -285,7 +285,7 @@
|
||||
<connection>
|
||||
<sender>buttonBox</sender>
|
||||
<signal>accepted()</signal>
|
||||
<receiver>CreateMemoryCardDialog</receiver>
|
||||
<receiver>MemoryCardCreateDialog</receiver>
|
||||
<slot>accept()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
@@ -301,7 +301,7 @@
|
||||
<connection>
|
||||
<sender>buttonBox</sender>
|
||||
<signal>rejected()</signal>
|
||||
<receiver>CreateMemoryCardDialog</receiver>
|
||||
<receiver>MemoryCardCreateDialog</receiver>
|
||||
<slot>reject()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
@@ -24,8 +24,8 @@
|
||||
|
||||
#include "common/StringUtil.h"
|
||||
|
||||
#include "CreateMemoryCardDialog.h"
|
||||
#include "MemoryCardConvertDialog.h"
|
||||
#include "MemoryCardCreateDialog.h"
|
||||
#include "MemoryCardSettingsWidget.h"
|
||||
#include "QtHost.h"
|
||||
#include "QtUtils.h"
|
||||
@@ -186,7 +186,7 @@ void MemoryCardSettingsWidget::ejectSlot(u32 slot)
|
||||
|
||||
void MemoryCardSettingsWidget::createCard()
|
||||
{
|
||||
CreateMemoryCardDialog dialog(QtUtils::GetRootWidget(this));
|
||||
MemoryCardCreateDialog dialog(QtUtils::GetRootWidget(this));
|
||||
if (dialog.exec() == QDialog::Accepted)
|
||||
refresh();
|
||||
}
|
||||
|
||||
@@ -111,11 +111,11 @@ void SettingsDialog::setupUi(const GameList::Entry* game)
|
||||
if (isPerGameSettings() && game && game->crc != 0)
|
||||
{
|
||||
addWidget(m_game_patch_settings_widget = new GamePatchSettingsWidget(game, this, m_ui.settingsContainer),
|
||||
tr("Patches"), QStringLiteral("tools-line"),
|
||||
tr("Patches"), QStringLiteral("band-aid-line"),
|
||||
tr("<strong>Patches</strong><hr>This section allows you to select optional patches to apply to the game, "
|
||||
"which may provide performance, visual, or gameplay improvements."));
|
||||
addWidget(m_game_cheat_settings_widget = new GameCheatSettingsWidget(game, this, m_ui.settingsContainer),
|
||||
tr("Cheats"), QStringLiteral("flask-line"),
|
||||
tr("Cheats"), QStringLiteral("cheats-line"),
|
||||
tr("<strong>Cheats</strong><hr>This section allows you to select which cheats you wish to enable. You "
|
||||
"cannot enable/disable cheats without labels for old-format pnach files, those will automatically "
|
||||
"activate if the main cheat enable option is checked."));
|
||||
@@ -125,7 +125,7 @@ void SettingsDialog::setupUi(const GameList::Entry* game)
|
||||
if (show_advanced_settings && isPerGameSettings())
|
||||
{
|
||||
addWidget(m_game_fix_settings_widget = new GameFixSettingsWidget(this, m_ui.settingsContainer), tr("Game Fixes"),
|
||||
QStringLiteral("hammer-line"),
|
||||
QStringLiteral("tools-line"),
|
||||
tr("<strong>Game Fixes Settings</strong><hr>Game Fixes can work around incorrect emulation in some titles.<br>However, they can "
|
||||
"also cause problems in games if used incorrectly.<br>It is best to leave them all disabled unless advised otherwise."));
|
||||
}
|
||||
|
||||
@@ -39,6 +39,17 @@
|
||||
#include <ShlObj.h>
|
||||
#endif
|
||||
|
||||
#if 0
|
||||
// Qt internal strings we'd like to have translated
|
||||
QT_TRANSLATE_NOOP("MAC_APPLICATION_MENU", "Services")
|
||||
QT_TRANSLATE_NOOP("MAC_APPLICATION_MENU", "Hide %1")
|
||||
QT_TRANSLATE_NOOP("MAC_APPLICATION_MENU", "Hide Others")
|
||||
QT_TRANSLATE_NOOP("MAC_APPLICATION_MENU", "Show All")
|
||||
QT_TRANSLATE_NOOP("MAC_APPLICATION_MENU", "Preferences...")
|
||||
QT_TRANSLATE_NOOP("MAC_APPLICATION_MENU", "Quit %1")
|
||||
QT_TRANSLATE_NOOP("MAC_APPLICATION_MENU", "About %1")
|
||||
#endif
|
||||
|
||||
namespace QtHost
|
||||
{
|
||||
struct GlyphInfo
|
||||
@@ -59,6 +70,34 @@ namespace QtHost
|
||||
|
||||
static std::vector<QTranslator*> s_translators;
|
||||
|
||||
static QString getSystemLanguage() {
|
||||
std::vector<std::pair<QString, QString>> available = QtHost::GetAvailableLanguageList();
|
||||
QString locale = QLocale::system().name();
|
||||
locale.replace('_', '-');
|
||||
// Can we find an exact match?
|
||||
for (const std::pair<QString, QString>& entry : available)
|
||||
{
|
||||
if (entry.second == locale)
|
||||
return locale;
|
||||
}
|
||||
// How about a partial match?
|
||||
QStringView lang = QStringView(locale);
|
||||
lang = lang.left(lang.indexOf('-'));
|
||||
for (const std::pair<QString, QString>& entry : available)
|
||||
{
|
||||
QStringView avail = QStringView(entry.second);
|
||||
avail = avail.left(avail.indexOf('-'));
|
||||
if (avail == lang) {
|
||||
Console.Warning("Couldn't find translation for system language %s, using %s instead",
|
||||
locale.toStdString().c_str(), entry.second.toStdString().c_str());
|
||||
return entry.second;
|
||||
}
|
||||
}
|
||||
// No matches :(
|
||||
Console.Warning("Couldn't find translation for system language %s, using en instead", locale.toStdString().c_str());
|
||||
return QStringLiteral("en");
|
||||
}
|
||||
|
||||
void QtHost::InstallTranslator()
|
||||
{
|
||||
for (QTranslator* translator : s_translators)
|
||||
@@ -68,11 +107,17 @@ void QtHost::InstallTranslator()
|
||||
}
|
||||
s_translators.clear();
|
||||
|
||||
const QString language =
|
||||
QString language =
|
||||
QString::fromStdString(Host::GetBaseStringSettingValue("UI", "Language", GetDefaultLanguage()));
|
||||
if (language == QStringLiteral("system"))
|
||||
language = getSystemLanguage();
|
||||
|
||||
// Install the base qt translation first.
|
||||
#ifdef __APPLE__
|
||||
const QString base_dir = QStringLiteral("%1/../Resources/translations").arg(qApp->applicationDirPath());
|
||||
#else
|
||||
const QString base_dir = QStringLiteral("%1/translations").arg(qApp->applicationDirPath());
|
||||
#endif
|
||||
QString base_path = QStringLiteral("%1/qt_%2.qm").arg(base_dir).arg(language);
|
||||
bool has_base_ts = QFile::exists(base_path);
|
||||
if (!has_base_ts)
|
||||
@@ -157,9 +202,12 @@ static std::string QtHost::GetFontPath(const GlyphInfo* gi)
|
||||
}
|
||||
else
|
||||
{
|
||||
font_path = fmt::format("C:\\Windows\\Fonts\\%s", gi->windows_font_name);
|
||||
font_path = fmt::format("C:\\Windows\\Fonts\\{}", gi->windows_font_name);
|
||||
}
|
||||
}
|
||||
#elif defined(__APPLE__)
|
||||
if (gi->mac_font_name)
|
||||
font_path = fmt::format("/System/Library/Fonts/{}", gi->mac_font_name);
|
||||
#endif
|
||||
|
||||
return font_path;
|
||||
@@ -167,7 +215,7 @@ static std::string QtHost::GetFontPath(const GlyphInfo* gi)
|
||||
|
||||
const char* QtHost::GetDefaultLanguage()
|
||||
{
|
||||
return "en";
|
||||
return "system";
|
||||
}
|
||||
|
||||
s32 Host::Internal::GetTranslatedStringImpl(
|
||||
@@ -190,6 +238,7 @@ s32 Host::Internal::GetTranslatedStringImpl(
|
||||
std::vector<std::pair<QString, QString>> QtHost::GetAvailableLanguageList()
|
||||
{
|
||||
return {
|
||||
{QCoreApplication::translate("InterfaceSettingsWidget", "System Language [Default]"), QStringLiteral("system")},
|
||||
{QStringLiteral("Afrikaans (af-ZA)"), QStringLiteral("af-ZA")},
|
||||
{QStringLiteral("عربي (ar-SA)"), QStringLiteral("ar-SA")},
|
||||
{QStringLiteral("Català (ca-ES)"), QStringLiteral("ca-ES")},
|
||||
@@ -197,7 +246,7 @@ std::vector<std::pair<QString, QString>> QtHost::GetAvailableLanguageList()
|
||||
{QStringLiteral("Dansk (da-DK)"), QStringLiteral("da-DK")},
|
||||
{QStringLiteral("Deutsch (de-DE)"), QStringLiteral("de-DE")},
|
||||
{QStringLiteral("Ελληνικά (el-GR)"), QStringLiteral("el-GR")},
|
||||
{QStringLiteral("English (en) [Default]"), QStringLiteral("en")},
|
||||
{QStringLiteral("English (en)"), QStringLiteral("en")},
|
||||
{QStringLiteral("Español (Hispanoamérica) (es-419)"), QStringLiteral("es-419")},
|
||||
{QStringLiteral("Español (España) (es-ES)"), QStringLiteral("es-ES")},
|
||||
{QStringLiteral("فارسی (fa-IR)"), QStringLiteral("fa-IR")},
|
||||
@@ -286,22 +335,22 @@ static constexpr const QtHost::GlyphInfo s_glyph_info[] = {
|
||||
{ "uk-UA", nullptr, nullptr, nullptr, s_cyrillic_ranges },
|
||||
|
||||
{
|
||||
"ja-JP", "msgothic.ttc", nullptr, nullptr,
|
||||
"ja-JP", "msgothic.ttc", nullptr, "ヒラギノ角ゴシック W3.ttc",
|
||||
// auto update by update_glyph_ranges.py with pcsx2-qt_ja-JP.ts
|
||||
u"□□△△◯◯✕✕、。〜〜ああいいううええおきくぐここささしすせせそそただっつてにのはびびへべほほまみめもややよれわわをんァイウコサソタチッツテニネパビロワワンンーー一一上下不不中中丸丸了了互互今今他他代代仮仮作作使使保保信信修修倍倍値値停停備備像像入入全全公公内内再再出出切切別別利利制制削削副副割割力力加加効効動動勧勧化化十十南南参参反収取取古古可台右右合合同名向向回回固固国国在在報報場場境境壊壊変変外外大大失失奨奨始始字存完完定定実実左左帰帰常常幅幅度度式式張張形形待待後後御御性性情情想想意意感感態態成成投投択択拡拡推推提提換換敗敗数数整整新新方方既既日日明明時時更更書書替最有有期期本本果果検検標標橙橙機機止正毎毎比比況況注注消消港港湾湾準準無無照照状状率率現現璧璧環環生生用用画画異異的的直直知知確確示示種種稿稿空空索索終終緑緑編編績績能能自自般般行行表表製製複複視視覧覧解解言言設設認認語語読読赤赤起起跡跡転転軸軸込込近近追追送送通通速速進進遅遅遊遊適適選選部部長長閉閉開開関関防防限限除除隅隅集集青青非非面面韓韓音音類類香香高高黄黄++??"
|
||||
},
|
||||
{
|
||||
"ko-KR", "malgun.ttf", nullptr, nullptr,
|
||||
"ko-KR", "malgun.ttf", nullptr, "AppleSDGothicNeo.ttc",
|
||||
// auto update by update_glyph_ranges.py with pcsx2-qt_ko-KR.ts
|
||||
u""
|
||||
},
|
||||
{
|
||||
"zh-CN", "msyh.ttc", nullptr, nullptr,
|
||||
"zh-CN", "msyh.ttc", nullptr, "Hiragino Sans GB.ttc",
|
||||
// auto update by update_glyph_ranges.py with pcsx2-qt_zh-CN.ts
|
||||
u"‘’□□△△○○、。一丁三下不与且且世世丢丢两两个个中中丰丰串串为主么义之之乎乎乐乐乘乘也也了了事二于于互互亚些交交产产享享亮亮亲亲人人什什仅仅介介仍从他他代以们们件价任任份份伍伍休休优优会会传传估估伸伸似似但但位住佑佑体体何何余余作作佣佣佳佳使使例例供供侧侧便便俄俄保保信信修修倍倍值值倾倾假假偏偏做做停停储储像像儿儿允允元元充充先光克克免免入入全全六六兰共关关兵典兼兼内内册再写写冲决况况净净准准减减几几出击函函刀刀刃刃分切列列则则创创删删利利别别到到制刷刹刹前前剪剪副副力力功务动助势势勿勿包包化化匹区十十升升半半协协单单南南占卡卫卫印印即即压压原原去去参参及及双反发发取变叠叠口古另另只只可台右右号号各各合吉同后向向吗吗否否含含启启呈呈告告员员味味命命和和哈哈响响哪哪唤唤商商善善器器四四回回因因团团围围国图圈圈在在地地场场址址均均坏坐块块坛坛垂垂型型域域基基堆堆堪堪塔塔填填增增士士声声处处备备复复外外多多够够大大天太失失头头夹夹奇奇奏奏套套奥奥奶奶好好如如始始威威娱娱婴婴媒媒子子字存它它安安完完宏宏官官定定宝实害害家家容容宽宽宿宿寄寄密密富富寸对导导封封射射将将小小少少尔尔尚尚尝尝就就尺尺尼尾局局层层屏屏展展属属崩崩工左巨巨差差己已巴巴希希带帧帮帮常常幅幅幕幕平平并并幸幸序序库底度度延延建建开开异弃弊弊式式引引张张弹强归当录录形形彩彩影影径待很很得得循循微微德德心心必忆志志忙忙快快忽忽态态性性怪怪恢恢息息您您悬悬情情惑惑惯惯意意感感慢慢戏我或或战战截截戳戳户户所扁手手打打托托执执扩扩扫扬扳扳找找技技抑抑抖抗护护拆拆拉拉拍拍拒拒拟拟拦拦择择括括拳拳持持指指按按挑挑挡挡挪挪振振捕捕损损换换据据掌掌排排接接控掩描提插插握握搜搜摇摇撕撕撤撤播播操擎支支收收改改放放故故效效敏敏散散数数整整文文断断斯新方方旋旋无无日日旧旧时时明明易易星映是是显显晕晕普普晰晰暂暂暗暗曲曲更更替最有有服服期期未未本本机机权权杆杆束束条条来来板板极极果果柄柄某某染染查查栅栅标栈栏栏校校样根格格框框案案档档桥桥检检棕棕榜榜模模橙橙次次欧欧止步死死殊殊段段每每比比毫毫水水求求汇汇没没油油法法波波注注泻泻洲洲活活流流浅浅测测浏浏浪浪浮浮海海消消深深混混添添清清港港渲渲游游湖湖湾湾溃溃源源溢溢滑滑满满滤滤演演澳澳激激灰灰灵灵点点烈烈热热焦焦然然煞煞照照片版牌牌牙牙特特状状狂狂独独狼狼猎猎猩猩率率王王玩玩环现班班理理瑞瑞甚甚生生用用由甲电电画画畅畅界界留留略略疤疤登登白百的的皇皇盖盘目目直直相相省省看看真眠着着知知短短石石码码破破础础硬硬确确碌碌磁磁示示神神禁禁离离种种秒秒积称移移程程稍稍稳稳空空突突窗窗立立站站端端符符第第等等筛筛签签简简算算管管类类粉粉粘粘精精糊糊系系素素索索紫紫纠纠红红级级纯纯纳纳纹纹线线组组细终经经绑绑结结绕绕绘给络绝统统继继绪绪续续维维绿缀缓缓编编缩缩网网罗罗置置美美翻翻考考者者而而耗耗耳耳联联肩肩胖胖能能腊腊自自至致舍舍良良色色节节芬芬英英范范荐荐荷荷莱莱获获菜菜萄萄著著葡葡蓝蓝藏藏虑虑虚虚融融行行衡衡补补表表被被裁裂装装西西要要覆覆观观规规视视览觉角角解解触触言言警警计计认认让让议议记记许许论论设访证证诊诊译译试试询询该详语语误误说说请诸读读调调谍谍豹豹负负败账质质贴贴费费赛赛起起超超越越足足跟跟跨跨路路跳跳踏踏踪踪身身车车轨轨转转轮软轴轴轻轻载载较较辅辅辑辑输输辨辨边边达达过过运近还这进进连迟述述追追退适选选逐逐递递通通速造遇遇道道避避那那部部都都配配醒醒采采释释里重量量针针钮钮铁铁铺铺链链销锁锐锐错错键锯镜镜长长门门闭问闲闲间间队队防防阴阴附附陆陆降降限限除除险险随隐隔隔隙隙障障雄雄集集零零雾雾需需震震静静非非靠靠面面韩韩音音顶顶项须顿顿预预频频题题颜额风风饱饱馈馈首首香香马马驱驱验验高高鬼鬼魂魂魔魔麦麦黄黄黑黑默默鼓鼓鼠鼠齐齐齿齿,,??"
|
||||
},
|
||||
{
|
||||
"zh-TW", "msyh.ttc", nullptr, nullptr,
|
||||
"zh-TW", "msyh.ttc", nullptr, "Hiragino Sans GB.ttc",
|
||||
// auto update by update_glyph_ranges.py with pcsx2-qt_zh-TW.ts
|
||||
u""
|
||||
},
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
#!/bin/bash
|
||||
|
||||
SCRIPTDIR=$(dirname "${BASH_SOURCE[0]}")
|
||||
|
||||
OPTS="-tr-function-alias QT_TRANSLATE_NOOP+=TRANSLATE,QT_TRANSLATE_NOOP+=TRANSLATE_SV,QT_TRANSLATE_NOOP+=TRANSLATE_STR,QT_TRANSLATE_N_NOOP3+=TRANSLATE_FMT,QT_TRANSLATE_NOOP+=TRANSLATE_NOOP"
|
||||
SRCDIRS=$(realpath "$SCRIPTDIR/..")/\ $(realpath "$SCRIPTDIR/../../pcsx2")/
|
||||
OUTDIR=$(realpath "$SCRIPTDIR")
|
||||
|
||||
lupdate $SRCDIRS $OPTS -no-obsolete -source-language en -ts "$OUTDIR/pcsx2-qt_en.ts"
|
||||
@@ -105,7 +105,7 @@
|
||||
<ClCompile Include="Settings\BIOSSettingsWidget.cpp" />
|
||||
<ClCompile Include="Settings\ControllerBindingWidgets.cpp" />
|
||||
<ClCompile Include="Settings\ControllerGlobalSettingsWidget.cpp" />
|
||||
<ClCompile Include="Settings\CreateMemoryCardDialog.cpp" />
|
||||
<ClCompile Include="Settings\MemoryCardCreateDialog.cpp" />
|
||||
<ClCompile Include="Settings\EmulationSettingsWidget.cpp" />
|
||||
<ClCompile Include="Settings\GameListSettingsWidget.cpp" />
|
||||
<ClCompile Include="Settings\GraphicsSettingsWidget.cpp" />
|
||||
@@ -172,7 +172,7 @@
|
||||
<QtMoc Include="Settings\GamePatchSettingsWidget.h" />
|
||||
<ClInclude Include="Settings\HddCreateQt.h" />
|
||||
<QtMoc Include="Settings\GameSummaryWidget.h" />
|
||||
<QtMoc Include="Settings\CreateMemoryCardDialog.h" />
|
||||
<QtMoc Include="Settings\MemoryCardCreateDialog.h" />
|
||||
<QtMoc Include="Settings\AchievementLoginDialog.h" />
|
||||
<QtMoc Include="Settings\AchievementSettingsWidget.h" />
|
||||
<QtMoc Include="GameList\GameListModel.h" />
|
||||
@@ -212,7 +212,7 @@
|
||||
<ClCompile Include="$(IntDir)Settings\moc_ControllerBindingWidgets.cpp" />
|
||||
<ClCompile Include="$(IntDir)Settings\moc_ControllerGlobalSettingsWidget.cpp" />
|
||||
<ClCompile Include="$(IntDir)Settings\moc_ControllerSettingsDialog.cpp" />
|
||||
<ClCompile Include="$(IntDir)Settings\moc_CreateMemoryCardDialog.cpp" />
|
||||
<ClCompile Include="$(IntDir)Settings\moc_MemoryCardCreateDialog.cpp" />
|
||||
<ClCompile Include="$(IntDir)Settings\moc_EmulationSettingsWidget.cpp" />
|
||||
<ClCompile Include="$(IntDir)Settings\moc_GameListSettingsWidget.cpp" />
|
||||
<ClCompile Include="$(IntDir)Settings\moc_GraphicsSettingsWidget.cpp" />
|
||||
@@ -312,7 +312,7 @@
|
||||
<QtUi Include="Settings\ControllerGlobalSettingsWidget.ui">
|
||||
<FileType>Document</FileType>
|
||||
</QtUi>
|
||||
<QtUi Include="Settings\CreateMemoryCardDialog.ui">
|
||||
<QtUi Include="Settings\MemoryCardCreateDialog.ui">
|
||||
<FileType>Document</FileType>
|
||||
</QtUi>
|
||||
<QtUi Include="Settings\AudioSettingsWidget.ui">
|
||||
|
||||
@@ -190,10 +190,10 @@
|
||||
<ClCompile Include="Settings\AudioSettingsWidget.cpp">
|
||||
<Filter>Settings</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="$(IntDir)Settings\moc_CreateMemoryCardDialog.cpp">
|
||||
<ClCompile Include="$(IntDir)Settings\moc_MemoryCardCreateDialog.cpp">
|
||||
<Filter>moc</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Settings\CreateMemoryCardDialog.cpp">
|
||||
<ClCompile Include="Settings\MemoryCardCreateDialog.cpp">
|
||||
<Filter>Settings</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Settings\DEV9DnsHostDialog.cpp">
|
||||
@@ -423,7 +423,7 @@
|
||||
<QtMoc Include="Settings\AudioSettingsWidget.h">
|
||||
<Filter>Settings</Filter>
|
||||
</QtMoc>
|
||||
<QtMoc Include="Settings\CreateMemoryCardDialog.h">
|
||||
<QtMoc Include="Settings\MemoryCardCreateDialog.h">
|
||||
<Filter>Settings</Filter>
|
||||
</QtMoc>
|
||||
<QtMoc Include="Settings\DEV9DnsHostDialog.h">
|
||||
@@ -549,7 +549,7 @@
|
||||
<QtUi Include="Settings\AudioSettingsWidget.ui">
|
||||
<Filter>Settings</Filter>
|
||||
</QtUi>
|
||||
<QtUi Include="Settings\CreateMemoryCardDialog.ui">
|
||||
<QtUi Include="Settings\MemoryCardCreateDialog.ui">
|
||||
<Filter>Settings</Filter>
|
||||
</QtUi>
|
||||
<QtUi Include="Settings\DEV9DnsHostDialog.ui">
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Generator: Adobe Illustrator 26.5.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
|
||||
<svg
|
||||
version="1.1"
|
||||
id="bandaid"
|
||||
x="0px"
|
||||
y="0px"
|
||||
viewBox="0 0 21.6 21.6"
|
||||
style="enable-background:new 0 0 21.6 21.6"
|
||||
xml:space="preserve"
|
||||
sodipodi:docname="band-aid-line-dark.svg"
|
||||
inkscape:version="1.2.2 (b0a8486541, 2022-12-01)"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"><defs
|
||||
id="defs8" /><sodipodi:namedview
|
||||
id="namedview6"
|
||||
pagecolor="#505050"
|
||||
bordercolor="#ffffff"
|
||||
borderopacity="1"
|
||||
inkscape:showpageshadow="0"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pagecheckerboard="1"
|
||||
inkscape:deskcolor="#505050"
|
||||
showgrid="false"
|
||||
inkscape:zoom="35.231481"
|
||||
inkscape:cx="10.785808"
|
||||
inkscape:cy="10.785808"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="956"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="42"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="bandaid" />
|
||||
<style
|
||||
type="text/css"
|
||||
id="style2">
|
||||
.st0{fill:#FFFFFF;}
|
||||
</style>
|
||||
<path
|
||||
id="By_Maxihplay"
|
||||
class="st0"
|
||||
d="M16.4,10.8L19,8.3c1-1,1-2.7,0-3.8l-1.9-1.9c-0.5-0.5-1.2-0.8-1.9-0.8s-1.4,0.3-1.9,0.8 l-2.5,2.5L8.3,2.6c-1-1-2.7-1-3.8,0L2.6,4.5c-1,1-1,2.7,0,3.8l2.5,2.5l-2.5,2.5c-1,1-1,2.7,0,3.8L4.5,19c0.5,0.5,1.2,0.8,1.9,0.8 s1.4-0.3,1.9-0.8l2.5-2.5l2.5,2.5c1,1,2.7,1,3.8,0l1.9-1.9c1-1,1-2.7,0-3.8L16.4,10.8z M8.4,9.8c0.5,0,0.9,0.4,0.9,0.9 s-0.4,0.9-0.9,0.9s-0.9-0.4-0.9-0.9C7.6,10.2,8,9.8,8.4,9.8z M7.1,17.8c-0.2,0.2-0.5,0.3-0.7,0.3S5.8,18,5.6,17.8L3.8,16 c-0.4-0.4-0.4-1.1,0-1.5L6.3,12l3.3,3.3C9.7,15.3,7.1,17.8,7.1,17.8z M10.8,13.8c-0.5,0-0.9-0.4-0.9-0.9s0.4-0.9,0.9-0.9 s0.9,0.4,0.9,0.9C11.7,13.5,11.3,13.8,10.8,13.8z M10.8,9.1c-0.5,0-0.9-0.4-0.9-0.9s0.4-0.9,0.9-0.9s0.9,0.4,0.9,0.9 S11.3,9.1,10.8,9.1z M13.2,11.5c-0.5,0-0.9-0.4-0.9-0.9s0.4-0.9,0.9-0.9s0.9,0.4,0.9,0.9C14.1,11.1,13.7,11.5,13.2,11.5z M11.9,6.3 l2.5-2.5c0.2-0.2,0.5-0.3,0.7-0.3c0.3,0,0.5,0.1,0.7,0.3l1.9,1.9c0.4,0.4,0.4,1.1,0,1.5l-2.5,2.5L11.9,6.3z"
|
||||
style="fill:#000000" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.2 KiB |
@@ -0,0 +1 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?><svg id="cheats" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><defs><style>.cls-1{fill:none;}.cls-2{fill-rule:evenodd;}</style></defs><g id="tela"><g><path class="cls-1" d="M0,0H24V24H0V0Z"/><path d="M2,4c0-.5,.5-1,1-1H21c.5,0,1,.4,1,1v14c0,.5-.5,1-1,1H3c-.5,0-1-.5-1-1h0V4Zm2,1v12H20V5H4Zm1,15h14v2H5v-2Z"/></g></g><g id="by_maxihplay"><path class="cls-2" d="M13.4,6.7c-1.1-1.2-2.6-.9-4,0-1.4-.9-2.9-1.1-4,0-.3,.4-.4,.9-.5,1.4-.2,1.1,.3,2.3,1.1,3.1l3.4,3.5,3.4-3.5c.8-.8,1.3-1.9,1.1-3.1,0-.5-.1-1-.5-1.4Z"/><path d="M17.1,12.2c.6,0,1,.2,1.5,.6,.4,.4,.6,.9,.6,1.5s-.2,1.1-.6,1.5c-.4,.4-.9,.6-1.5,.6s-1.3-.4-1.8-1c-.6,.7-1.2,1-1.8,1s-1.1-.2-1.4-.6c-.4-.4-.6-.9-.6-1.5s.2-1.1,.6-1.5c.4-.4,.9-.6,1.4-.6,.6,0,1.3,.4,1.8,1.1,.6-.8,1.2-1.1,1.8-1.1Zm-3.7,3c.4,0,.8-.3,1.2-.9-.4-.6-.8-.9-1.2-.9-.2,0-.3,0-.5,.1s-.3,.2-.3,.3-.1,.3-.1,.4c0,.2,.1,.5,.3,.6,.1,.3,.3,.4,.6,.4h0Zm3.9,0h.3c.1,0,.2-.1,.3-.1s.1-.1,.1-.2,.1-.2,.1-.3v-.3c0-.2-.1-.5-.3-.6-.2-.2-.4-.3-.7-.3-.4,0-.8,.3-1.2,.9,.6,.6,1,.9,1.4,.9h0Z"/></g></svg>
|
||||
|
After Width: | Height: | Size: 1.0 KiB |
@@ -1,6 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
||||
<g>
|
||||
<path fill="none" d="M0 0H24V24H0z"/>
|
||||
<path d="M16 2v2h-1v3.243c0 1.158.251 2.301.736 3.352l4.282 9.276c.347.753.018 1.644-.734 1.99-.197.092-.411.139-.628.139H5.344c-.828 0-1.5-.672-1.5-1.5 0-.217.047-.432.138-.629l4.282-9.276C8.749 9.545 9 8.401 9 7.243V4H8V2h8zm-2.612 8.001h-2.776c-.104.363-.23.721-.374 1.071l-.158.361L6.125 20h11.749l-3.954-8.567c-.214-.464-.392-.943-.532-1.432zM11 7.243c0 .253-.01.506-.029.758h2.058c-.01-.121-.016-.242-.021-.364L13 7.243V4h-2v3.243z"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 580 B |
@@ -1 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M20 2C20.5523 2 21 2.44772 21 3V8C21 8.55228 20.5523 9 20 9H15V22C15 22.5523 14.5523 23 14 23H10C9.44772 23 9 22.5523 9 22V9H3.5C2.94772 9 2.5 8.55228 2.5 8V5.61803C2.5 5.23926 2.714 4.893 3.05279 4.72361L8.5 2H20ZM15 4H8.97214L4.5 6.23607V7H11V21H13V7H15V4ZM19 4H17V7H19V4Z" fill="#000000"></path></svg>
|
||||
|
Before Width: | Height: | Size: 373 B |
@@ -1,61 +1 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
width="512"
|
||||
height="512"
|
||||
viewBox="0 0 512 512"
|
||||
version="1.1"
|
||||
id="svg5"
|
||||
inkscape:version="1.2.2 (b0a8486541, 2022-12-01)"
|
||||
sodipodi:docname="heart-monitor-line-black.svg"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg">
|
||||
<sodipodi:namedview
|
||||
id="namedview7"
|
||||
pagecolor="#505050"
|
||||
bordercolor="#ffffff"
|
||||
borderopacity="1"
|
||||
inkscape:showpageshadow="0"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pagecheckerboard="1"
|
||||
inkscape:deskcolor="#505050"
|
||||
inkscape:document-units="px"
|
||||
showgrid="false"
|
||||
inkscape:zoom="1.3037281"
|
||||
inkscape:cx="178.33473"
|
||||
inkscape:cy="273.8301"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="956"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="42"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="layer1" />
|
||||
<defs
|
||||
id="defs2" />
|
||||
<g
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1">
|
||||
<rect
|
||||
style="fill:none;stroke:#000000;stroke-width:41.8148;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none"
|
||||
id="rect234"
|
||||
width="405.06821"
|
||||
height="238.85057"
|
||||
x="53.465897"
|
||||
y="95.476929" />
|
||||
<rect
|
||||
style="fill:#000000;fill-opacity:1;stroke:#000000;stroke-width:14.5739;stroke-linecap:round;stroke-linejoin:round"
|
||||
id="rect404"
|
||||
width="305.17819"
|
||||
height="33.373306"
|
||||
x="103.41091"
|
||||
y="400.26862" />
|
||||
<path
|
||||
style="fill:none;stroke:#000000;stroke-width:18.3381;stroke-linecap:square;stroke-dasharray:none"
|
||||
d="M 54.043377,215.32624 H 200.04731 l 38.29399,-66.32716 35.31739,131.80626 44.38011,-76.86859 h 126.75493"
|
||||
id="path359" />
|
||||
</g>
|
||||
</svg>
|
||||
<?xml version="1.0" encoding="UTF-8"?><svg id="heart" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 467.6 467.6"><defs><style>.cls-1{stroke:#000;stroke-linecap:square;stroke-width:23px;}.cls-1,.cls-2{fill:none;}</style></defs><path id="By_Kam" class="cls-2" d="M0,0H467.6V467.6H0V0Z"/><path id="tela" d="M39,68.2c0-10.8,8.9-19.5,19.3-19.5H409.3c10.7,0,19.3,8.7,19.3,19.5V341c0,10.8-8.9,19.5-19.3,19.5H58.3c-10.7,0-19.3-8.7-19.3-19.4V68.2Zm38.9,19.5v233.8H389.6V87.7H77.9Zm19.5,292.2H370.2v39H97.4v-39Z"/><path id="raio" class="cls-1" d="M55.8,204.1h118l38.3-66.3,35.3,131.8,44.4-76.9h115.8"/></svg>
|
||||
|
Before Width: | Height: | Size: 2.0 KiB After Width: | Height: | Size: 600 B |
@@ -1,64 +1 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
viewBox="0 0 24 24"
|
||||
version="1.1"
|
||||
id="svg8"
|
||||
sodipodi:docname="interface-line-black.svg"
|
||||
inkscape:version="1.2.2 (b0a8486541, 2022-12-01)"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg">
|
||||
<defs
|
||||
id="defs12" />
|
||||
<sodipodi:namedview
|
||||
id="namedview10"
|
||||
pagecolor="#505050"
|
||||
bordercolor="#ffffff"
|
||||
borderopacity="1"
|
||||
inkscape:showpageshadow="0"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pagecheckerboard="1"
|
||||
inkscape:deskcolor="#505050"
|
||||
showgrid="false"
|
||||
inkscape:zoom="22.627417"
|
||||
inkscape:cx="13.678097"
|
||||
inkscape:cy="16.30765"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="956"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="42"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="svg8" />
|
||||
<path
|
||||
style="fill:none;stroke:#010101;stroke-width:1.30518;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="m 6.727468,7.7120958 c 6.477744,0 6.477744,0.045113 6.477744,0.045113"
|
||||
id="path858" />
|
||||
<circle
|
||||
style="fill:none;stroke:#010101;stroke-width:1.26308;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="path1155"
|
||||
cx="15.312269"
|
||||
cy="7.6872182"
|
||||
r="1.9730911" />
|
||||
<circle
|
||||
style="fill:none;stroke:#010101;stroke-width:1.26308;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="path1155-3"
|
||||
cx="9.0217237"
|
||||
cy="12.922025"
|
||||
r="1.9730911" />
|
||||
<path
|
||||
style="fill:none;stroke:#010101;stroke-width:1.26308;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="m 17.36834,12.922025 c -6.299228,0 -6.299228,0 -6.299228,0"
|
||||
id="path1211" />
|
||||
<rect
|
||||
style="fill:none;stroke:#010101;stroke-width:1.60321;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none"
|
||||
id="rect899"
|
||||
width="18.606068"
|
||||
height="13.961316"
|
||||
x="2.6969662"
|
||||
y="3.311192" />
|
||||
<path
|
||||
style="fill:none;stroke:#010101;stroke-width:2.13371;stroke-linecap:round;stroke-linejoin:miter;stroke-dasharray:none"
|
||||
d="M 4.1156628,20.476585 H 19.884335"
|
||||
id="path1269" />
|
||||
</svg>
|
||||
<?xml version="1.0" encoding="UTF-8"?><svg id="interface" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><defs><style>.cls-1{stroke-width:1.31px;}.cls-1,.cls-2,.cls-3{fill:none;}.cls-1,.cls-3{stroke:#010101;stroke-linecap:round;stroke-linejoin:round;}.cls-3{stroke-width:1.26px;}</style></defs><g id="tela"><g><path class="cls-2" d="M0,0H24V24H0V0Z"/><path d="M2,4c0-.6,.5-1,1-1H21c.5,0,1,.4,1,1v14c0,.6-.5,1-1,1H3c-.5,0-1-.4-1-1h0V4Zm2,1v12H20V5M5,20h14v2H5v-2Z"/></g></g><g id="guias"><path id="path858" class="cls-1" d="M6.7,8.5h6.5"/><circle id="path1155" class="cls-3" cx="15.3" cy="8.4" r="2"/><circle id="path1155-3" class="cls-3" cx="9" cy="13.7" r="2"/><path id="path1211" class="cls-3" d="M17.3,13.7h-6.3"/></g></svg>
|
||||
|
Before Width: | Height: | Size: 2.4 KiB After Width: | Height: | Size: 735 B |
@@ -22,9 +22,9 @@
|
||||
inkscape:pagecheckerboard="1"
|
||||
inkscape:deskcolor="#505050"
|
||||
showgrid="false"
|
||||
inkscape:zoom="0.84555556"
|
||||
inkscape:cx="91.655716"
|
||||
inkscape:cy="449.40867"
|
||||
inkscape:zoom="0.59789807"
|
||||
inkscape:cx="71.918613"
|
||||
inkscape:cy="403.07874"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="956"
|
||||
inkscape:window-x="0"
|
||||
|
||||
|
Before Width: | Height: | Size: 2.6 KiB After Width: | Height: | Size: 2.6 KiB |
@@ -0,0 +1,16 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 26.5.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="bandaid" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 21.6 21.6" style="enable-background:new 0 0 21.6 21.6;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{fill:#FFFFFF;}
|
||||
</style>
|
||||
<path id="By_Maxihplay" class="st0" d="M16.4,10.8L19,8.3c1-1,1-2.7,0-3.8l-1.9-1.9c-0.5-0.5-1.2-0.8-1.9-0.8s-1.4,0.3-1.9,0.8
|
||||
l-2.5,2.5L8.3,2.6c-1-1-2.7-1-3.8,0L2.6,4.5c-1,1-1,2.7,0,3.8l2.5,2.5l-2.5,2.5c-1,1-1,2.7,0,3.8L4.5,19c0.5,0.5,1.2,0.8,1.9,0.8
|
||||
s1.4-0.3,1.9-0.8l2.5-2.5l2.5,2.5c1,1,2.7,1,3.8,0l1.9-1.9c1-1,1-2.7,0-3.8L16.4,10.8z M8.4,9.8c0.5,0,0.9,0.4,0.9,0.9
|
||||
s-0.4,0.9-0.9,0.9s-0.9-0.4-0.9-0.9C7.6,10.2,8,9.8,8.4,9.8z M7.1,17.8c-0.2,0.2-0.5,0.3-0.7,0.3S5.8,18,5.6,17.8L3.8,16
|
||||
c-0.4-0.4-0.4-1.1,0-1.5L6.3,12l3.3,3.3C9.7,15.3,7.1,17.8,7.1,17.8z M10.8,13.8c-0.5,0-0.9-0.4-0.9-0.9s0.4-0.9,0.9-0.9
|
||||
s0.9,0.4,0.9,0.9C11.7,13.5,11.3,13.8,10.8,13.8z M10.8,9.1c-0.5,0-0.9-0.4-0.9-0.9s0.4-0.9,0.9-0.9s0.9,0.4,0.9,0.9
|
||||
S11.3,9.1,10.8,9.1z M13.2,11.5c-0.5,0-0.9-0.4-0.9-0.9s0.4-0.9,0.9-0.9s0.9,0.4,0.9,0.9C14.1,11.1,13.7,11.5,13.2,11.5z M11.9,6.3
|
||||
l2.5-2.5c0.2-0.2,0.5-0.3,0.7-0.3c0.3,0,0.5,0.1,0.7,0.3l1.9,1.9c0.4,0.4,0.4,1.1,0,1.5l-2.5,2.5L11.9,6.3z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.3 KiB |
@@ -0,0 +1 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?><svg id="cheats" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><defs><style>.cls-1,.cls-2{fill:#fff;}.cls-2{fill-rule:evenodd;}.cls-3{fill:none;}</style></defs><g id="tela"><g><path class="cls-3" d="M0,0H24V24H0V0Z"/><path class="cls-1" d="M2,4c0-.5,.5-1,1-1H21c.5,0,1,.4,1,1v14c0,.5-.5,1-1,1H3c-.5,0-1-.5-1-1h0V4Zm2,1v12H20V5H4Zm1,15h14v2H5v-2Z"/></g></g><g id="by_maxihplay"><path class="cls-2" d="M13.4,6.7c-1.1-1.2-2.6-.71-4,.19-1.4-.9-2.9-1.29-4-.19-.3,.4-.4,.9-.5,1.4-.2,1.1,.3,2.3,1.1,3.1l3.4,3.5,3.4-3.5c.8-.8,1.3-1.9,1.1-3.1,0-.5-.1-1-.5-1.4Z"/><path class="cls-1" d="M17.1,12.2c.6,0,1,.2,1.5,.6,.4,.4,.6,.9,.6,1.5s-.2,1.1-.6,1.5c-.4,.4-.9,.6-1.5,.6s-1.3-.4-1.8-1c-.6,.7-1.2,1-1.8,1s-1.1-.2-1.4-.6c-.4-.4-.6-.9-.6-1.5s.2-1.1,.6-1.5c.4-.4,.9-.6,1.4-.6,.6,0,1.3,.4,1.8,1.1,.6-.8,1.2-1.1,1.8-1.1Zm-3.7,3c.4,0,.8-.3,1.2-.9-.4-.6-.8-.9-1.2-.9-.2,0-.3,0-.5,.1s-.3,.2-.3,.3-.1,.3-.1,.4c0,.2,.1,.5,.3,.6,.1,.3,.3,.4,.6,.4h0Zm3.9,0h.3c.1,0,.2-.1,.3-.1s.1-.1,.1-.2,.1-.2,.1-.3v-.3c0-.2-.1-.5-.3-.6-.2-.2-.4-.3-.7-.3-.4,0-.8,.3-1.2,.9,.6,.6,1,.9,1.4,.9h0Z"/></g></svg>
|
||||
|
After Width: | Height: | Size: 1.1 KiB |
@@ -1,6 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
||||
<g>
|
||||
<path fill="none" d="M0 0H24V24H0z"/>
|
||||
<path fill="#ffffff" d="M16 2v2h-1v3.243c0 1.158.251 2.301.736 3.352l4.282 9.276c.347.753.018 1.644-.734 1.99-.197.092-.411.139-.628.139H5.344c-.828 0-1.5-.672-1.5-1.5 0-.217.047-.432.138-.629l4.282-9.276C8.749 9.545 9 8.401 9 7.243V4H8V2h8zm-2.612 8.001h-2.776c-.104.363-.23.721-.374 1.071l-.158.361L6.125 20h11.749l-3.954-8.567c-.214-.464-.392-.943-.532-1.432zM11 7.243c0 .253-.01.506-.029.758h2.058c-.01-.121-.016-.242-.021-.364L13 7.243V4h-2v3.243z"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 595 B |
@@ -1 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M20 2C20.5523 2 21 2.44772 21 3V8C21 8.55228 20.5523 9 20 9H15V22C15 22.5523 14.5523 23 14 23H10C9.44772 23 9 22.5523 9 22V9H3.5C2.94772 9 2.5 8.55228 2.5 8V5.61803C2.5 5.23926 2.714 4.893 3.05279 4.72361L8.5 2H20ZM15 4H8.97214L4.5 6.23607V7H11V21H13V7H15V4ZM19 4H17V7H19V4Z" fill="#ffffff"></path></svg>
|
||||
|
Before Width: | Height: | Size: 373 B |
@@ -1,61 +1 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
width="512"
|
||||
height="512"
|
||||
viewBox="0 0 512 512"
|
||||
version="1.1"
|
||||
id="svg5"
|
||||
inkscape:version="1.2.2 (b0a8486541, 2022-12-01)"
|
||||
sodipodi:docname="heart-monitor-line.svg"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg">
|
||||
<sodipodi:namedview
|
||||
id="namedview7"
|
||||
pagecolor="#505050"
|
||||
bordercolor="#ffffff"
|
||||
borderopacity="1"
|
||||
inkscape:showpageshadow="0"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pagecheckerboard="1"
|
||||
inkscape:deskcolor="#505050"
|
||||
inkscape:document-units="px"
|
||||
showgrid="false"
|
||||
inkscape:zoom="1.3037281"
|
||||
inkscape:cx="177.5677"
|
||||
inkscape:cy="273.8301"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="956"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="42"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="layer1" />
|
||||
<defs
|
||||
id="defs2" />
|
||||
<g
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1">
|
||||
<rect
|
||||
style="fill:none;stroke:#ffffff;stroke-width:41.8148;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none"
|
||||
id="rect234"
|
||||
width="405.06821"
|
||||
height="238.85057"
|
||||
x="53.465897"
|
||||
y="95.476929" />
|
||||
<rect
|
||||
style="fill:#ffffff;fill-opacity:1;stroke:#ffffff;stroke-width:14.5739;stroke-linecap:round;stroke-linejoin:round"
|
||||
id="rect404"
|
||||
width="305.17819"
|
||||
height="33.373306"
|
||||
x="103.41091"
|
||||
y="400.26862" />
|
||||
<path
|
||||
style="fill:none;stroke:#ffffff;stroke-width:18.3381;stroke-linecap:square;stroke-dasharray:none"
|
||||
d="M 54.043377,215.32624 H 200.04731 l 38.29399,-66.32716 35.31739,131.80626 44.38011,-76.86859 h 126.75493"
|
||||
id="path359" />
|
||||
</g>
|
||||
</svg>
|
||||
<?xml version="1.0" encoding="UTF-8"?><svg id="heart" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 467.6 467.6"><defs><style>.cls-1{fill:#fff;}.cls-2,.cls-3{fill:none;}.cls-3{stroke:#fff;stroke-linecap:square;stroke-width:23px;}</style></defs><path id="By_Kam" class="cls-2" d="M0,0H467.6V467.6H0V0Z"/><path id="tela" class="cls-1" d="M39,68.2c0-10.8,8.9-19.5,19.3-19.5H409.3c10.7,0,19.3,8.7,19.3,19.5V341c0,10.8-8.9,19.5-19.3,19.5H58.3c-10.7,0-19.3-8.7-19.3-19.4V68.2Zm38.9,19.5v233.8H389.6V87.7H77.9Zm19.5,292.2H370.2v39H97.4v-39Z"/><path id="raio" class="cls-3" d="M55.8,204.1h118l38.3-66.3,35.3,131.8,44.4-76.9h115.8"/></svg>
|
||||
|
Before Width: | Height: | Size: 1.9 KiB After Width: | Height: | Size: 632 B |
@@ -1,64 +1 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
viewBox="0 0 24 24"
|
||||
version="1.1"
|
||||
id="svg8"
|
||||
sodipodi:docname="interface-line.svg"
|
||||
inkscape:version="1.2.2 (b0a8486541, 2022-12-01)"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg">
|
||||
<defs
|
||||
id="defs12" />
|
||||
<sodipodi:namedview
|
||||
id="namedview10"
|
||||
pagecolor="#505050"
|
||||
bordercolor="#ffffff"
|
||||
borderopacity="1"
|
||||
inkscape:showpageshadow="0"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pagecheckerboard="1"
|
||||
inkscape:deskcolor="#505050"
|
||||
showgrid="false"
|
||||
inkscape:zoom="22.627417"
|
||||
inkscape:cx="13.633903"
|
||||
inkscape:cy="16.30765"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="956"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="42"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="svg8" />
|
||||
<path
|
||||
style="fill:none;stroke:#fefefe;stroke-width:1.30518;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="m 6.727468,7.7120958 c 6.477744,0 6.477744,0.045113 6.477744,0.045113"
|
||||
id="path858" />
|
||||
<circle
|
||||
style="fill:none;stroke:#fefefe;stroke-width:1.26308;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="path1155"
|
||||
cx="15.312269"
|
||||
cy="7.6872182"
|
||||
r="1.9730911" />
|
||||
<circle
|
||||
style="fill:none;stroke:#fefefe;stroke-width:1.26308;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="path1155-3"
|
||||
cx="9.0217237"
|
||||
cy="12.922025"
|
||||
r="1.9730911" />
|
||||
<path
|
||||
style="fill:none;stroke:#fefefe;stroke-width:1.26308;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="m 17.36834,12.922025 c -6.299228,0 -6.299228,0 -6.299228,0"
|
||||
id="path1211" />
|
||||
<rect
|
||||
style="fill:none;stroke:#fefefe;stroke-width:1.60321;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none"
|
||||
id="rect899"
|
||||
width="18.606068"
|
||||
height="13.961316"
|
||||
x="2.6969662"
|
||||
y="3.311192" />
|
||||
<path
|
||||
style="fill:none;stroke:#fefefe;stroke-width:2.13371;stroke-linecap:round;stroke-linejoin:miter;stroke-dasharray:none"
|
||||
d="M 4.1156628,20.476585 H 19.884335"
|
||||
id="path1269" />
|
||||
</svg>
|
||||
<?xml version="1.0" encoding="UTF-8"?><svg id="interface" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><defs><style>.cls-1{fill:#fff;}.cls-2{stroke-width:1.31px;}.cls-2,.cls-3,.cls-4{fill:none;}.cls-2,.cls-4{stroke:#fff;stroke-linecap:round;stroke-linejoin:round;}.cls-4{stroke-width:1.26px;}</style></defs><g id="tela"><g><path class="cls-3" d="M0,0H24V24H0V0Z"/><path class="cls-1" d="M2,4c0-.6,.5-1,1-1H21c.5,0,1,.4,1,1v14c0,.6-.5,1-1,1H3c-.5,0-1-.4-1-1h0V4Zm2,1v12H20V5M5,20h14v2H5v-2Z"/></g></g><g id="guias"><path id="path858" class="cls-2" d="M6.7,8.5h6.5"/><circle id="path1155" class="cls-4" cx="15.3" cy="8.4" r="2"/><circle id="path1155-3" class="cls-4" cx="9" cy="13.7" r="2"/><path id="path1211" class="cls-4" d="M17.3,13.7h-6.3"/></g></svg>
|
||||
|
Before Width: | Height: | Size: 2.3 KiB After Width: | Height: | Size: 764 B |
@@ -6,7 +6,9 @@
|
||||
<file>icons/black/svg/arrow-left-right-line.svg</file>
|
||||
<file>icons/black/svg/artboard-2-line.svg</file>
|
||||
<file>icons/black/svg/at.svg</file>
|
||||
<file>icons/black/svg/band-aid-line.svg</file>
|
||||
<file>icons/black/svg/brush-line.svg</file>
|
||||
<file>icons/black/svg/cheats-line.svg</file>
|
||||
<file>icons/black/svg/checkbox-multiple-blank-line.svg</file>
|
||||
<file>icons/black/svg/chip-line.svg</file>
|
||||
<file>icons/black/svg/close-line.svg</file>
|
||||
@@ -28,7 +30,6 @@
|
||||
<file>icons/black/svg/file-settings-line.svg</file>
|
||||
<file>icons/black/svg/filter-line.svg</file>
|
||||
<file>icons/black/svg/flashlight-line.svg</file>
|
||||
<file>icons/black/svg/flask-line.svg</file>
|
||||
<file>icons/black/svg/floppy-in-line.svg</file>
|
||||
<file>icons/black/svg/floppy-out-line.svg</file>
|
||||
<file>icons/black/svg/folder-add-line.svg</file>
|
||||
@@ -39,7 +40,6 @@
|
||||
<file>icons/black/svg/function-line.svg</file>
|
||||
<file>icons/black/svg/github.svg</file>
|
||||
<file>icons/black/svg/global-line.svg</file>
|
||||
<file>icons/black/svg/hammer-line.svg</file>
|
||||
<file>icons/black/svg/heart-circle-line.svg</file>
|
||||
<file>icons/black/svg/heart-monitor-line.svg</file>
|
||||
<file>icons/black/svg/image-fill.svg</file>
|
||||
@@ -78,7 +78,9 @@
|
||||
<file>icons/white/svg/arrow-left-right-line.svg</file>
|
||||
<file>icons/white/svg/artboard-2-line.svg</file>
|
||||
<file>icons/white/svg/at.svg</file>
|
||||
<file>icons/white/svg/band-aid-line.svg</file>
|
||||
<file>icons/white/svg/brush-line.svg</file>
|
||||
<file>icons/white/svg/cheats-line.svg</file>
|
||||
<file>icons/white/svg/checkbox-multiple-blank-line.svg</file>
|
||||
<file>icons/white/svg/chip-line.svg</file>
|
||||
<file>icons/white/svg/close-line.svg</file>
|
||||
@@ -100,7 +102,6 @@
|
||||
<file>icons/white/svg/file-settings-line.svg</file>
|
||||
<file>icons/white/svg/filter-line.svg</file>
|
||||
<file>icons/white/svg/flashlight-line.svg</file>
|
||||
<file>icons/white/svg/flask-line.svg</file>
|
||||
<file>icons/white/svg/floppy-in-line.svg</file>
|
||||
<file>icons/white/svg/floppy-out-line.svg</file>
|
||||
<file>icons/white/svg/folder-add-line.svg</file>
|
||||
@@ -111,7 +112,6 @@
|
||||
<file>icons/white/svg/function-line.svg</file>
|
||||
<file>icons/white/svg/github.svg</file>
|
||||
<file>icons/white/svg/global-line.svg</file>
|
||||
<file>icons/white/svg/hammer-line.svg</file>
|
||||
<file>icons/white/svg/heart-circle-line.svg</file>
|
||||
<file>icons/white/svg/heart-monitor-line.svg</file>
|
||||
<file>icons/white/svg/image-fill.svg</file>
|
||||
|
||||
@@ -18,8 +18,7 @@
|
||||
#ifdef ENABLE_ACHIEVEMENTS
|
||||
|
||||
#include "Achievements.h"
|
||||
#include "CDVD/IsoFS/IsoFS.h"
|
||||
#include "CDVD/IsoFS/IsoFSCDVD.h"
|
||||
#include "CDVD/CDVD.h"
|
||||
#include "Elfheader.h"
|
||||
#include "Host.h"
|
||||
#include "ImGui/FullscreenUI.h"
|
||||
@@ -36,6 +35,7 @@
|
||||
#include "common/Assertions.h"
|
||||
#include "common/Console.h"
|
||||
#include "common/FileSystem.h"
|
||||
#include "common/Error.h"
|
||||
#include "common/General.h"
|
||||
#include "common/HTTPDownloader.h"
|
||||
#include "common/MD5Digest.h"
|
||||
@@ -121,7 +121,6 @@ namespace Achievements
|
||||
static void GetLbInfoCallback(s32 status_code, const std::string& content_type, Common::HTTPDownloader::Request::Data data);
|
||||
static void GetPatches(u32 game_id);
|
||||
static std::string_view GetELFNameForHash(const std::string& elf_path);
|
||||
static std::optional<std::vector<u8>> ReadELFFromCurrentDisc(const std::string& elf_path);
|
||||
static std::string GetGameHash();
|
||||
static void SetChallengeMode(bool enabled);
|
||||
static void SendGetGameId();
|
||||
@@ -1356,47 +1355,6 @@ std::string_view Achievements::GetELFNameForHash(const std::string& elf_path)
|
||||
return std::string_view(elf_path).substr(start, end - start);
|
||||
}
|
||||
|
||||
std::optional<std::vector<u8>> Achievements::ReadELFFromCurrentDisc(const std::string& elf_path)
|
||||
{
|
||||
std::optional<std::vector<u8>> ret;
|
||||
|
||||
// ELF suffix hack. Taken from CDVD::loadElf
|
||||
// MLB2k6 has an elf whose suffix is actually ;2
|
||||
std::string filepath(elf_path);
|
||||
const std::string::size_type semi_pos = filepath.rfind(';');
|
||||
if (semi_pos != std::string::npos && std::string_view(filepath).substr(semi_pos) != ";1")
|
||||
{
|
||||
Console.Warning(fmt::format("(Achievements) Non-conforming version suffix ({}) detected and replaced.", elf_path));
|
||||
filepath.erase(semi_pos);
|
||||
filepath += ";1";
|
||||
}
|
||||
|
||||
IsoFSCDVD isofs;
|
||||
IsoFile file(isofs);
|
||||
Error error;
|
||||
if (!file.open(filepath, &error))
|
||||
{
|
||||
Console.Error(fmt::format("(Achievements) Failed to open ELF '{}' on disc: {}", elf_path, error.GetDescription()));
|
||||
return ret;
|
||||
}
|
||||
|
||||
const u32 size = file.getLength();
|
||||
ret = std::vector<u8>();
|
||||
ret->resize(size);
|
||||
|
||||
if (size > 0)
|
||||
{
|
||||
const s32 bytes_read = file.read(ret->data(), static_cast<s32>(size));
|
||||
if (bytes_read != static_cast<s32>(size))
|
||||
{
|
||||
Console.Error(fmt::format("(Achievements) Only read {} of {} bytes of ELF '{}'", bytes_read, size, elf_path));
|
||||
ret.reset();
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::string Achievements::GetGameHash()
|
||||
{
|
||||
const std::string elf_path = VMManager::GetDiscELF();
|
||||
@@ -1404,34 +1362,37 @@ std::string Achievements::GetGameHash()
|
||||
return {};
|
||||
|
||||
// this.. really shouldn't be invalid
|
||||
const std::string_view name_for_hash(GetELFNameForHash(elf_path));
|
||||
const std::string_view name_for_hash = GetELFNameForHash(elf_path);
|
||||
if (name_for_hash.empty())
|
||||
return {};
|
||||
|
||||
std::optional<std::vector<u8>> elf_data(ReadELFFromCurrentDisc(elf_path));
|
||||
if (!elf_data.has_value())
|
||||
ElfObject elfo;
|
||||
Error error;
|
||||
if (!cdvdLoadElf(&elfo, elf_path, false, &error))
|
||||
{
|
||||
Console.Error(fmt::format("(Achievements) Failed to read ELF '{}' on disc: {}", elf_path, error.GetDescription()));
|
||||
return {};
|
||||
}
|
||||
|
||||
// See rcheevos hash.c - rc_hash_ps2().
|
||||
const u32 MAX_HASH_SIZE = 64 * 1024 * 1024;
|
||||
const u32 hash_size = std::min<u32>(static_cast<u32>(elf_data->size()), MAX_HASH_SIZE);
|
||||
pxAssert(hash_size <= elf_data->size());
|
||||
const u32 hash_size = std::min<u32>(elfo.GetSize(), MAX_HASH_SIZE);
|
||||
pxAssert(hash_size <= elfo.GetSize());
|
||||
|
||||
MD5Digest digest;
|
||||
if (!name_for_hash.empty())
|
||||
digest.Update(name_for_hash.data(), static_cast<u32>(name_for_hash.size()));
|
||||
if (hash_size > 0)
|
||||
digest.Update(elf_data->data(), hash_size);
|
||||
digest.Update(elfo.GetData().data(), hash_size);
|
||||
|
||||
u8 hash[16];
|
||||
digest.Final(hash);
|
||||
|
||||
std::string hash_str(
|
||||
const std::string hash_str =
|
||||
StringUtil::StdStringFromFormat("%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x", hash[0], hash[1], hash[2],
|
||||
hash[3], hash[4], hash[5], hash[6], hash[7], hash[8], hash[9], hash[10], hash[11], hash[12], hash[13], hash[14], hash[15]));
|
||||
hash[3], hash[4], hash[5], hash[6], hash[7], hash[8], hash[9], hash[10], hash[11], hash[12], hash[13], hash[14], hash[15]);
|
||||
|
||||
Console.WriteLn("Hash for '%.*s' (%zu bytes, %u bytes hashed): %s", static_cast<int>(name_for_hash.size()), name_for_hash.data(),
|
||||
elf_data->size(), hash_size, hash_str.c_str());
|
||||
Console.WriteLn(fmt::format("Hash for '{}' ({} bytes, {} bytes hashed): {}", name_for_hash, elfo.GetSize(), hash_size, hash_str));
|
||||
return hash_str;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/* PCSX2 - PS2 Emulator for PCs
|
||||
* Copyright (C) 2002-2021 PCSX2 Dev Team
|
||||
* Copyright (C) 2002-2023 PCSX2 Dev Team
|
||||
*
|
||||
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
|
||||
* of the GNU Lesser General Public License as published by the Free Software Found-
|
||||
@@ -14,6 +14,17 @@
|
||||
*/
|
||||
|
||||
#include "PrecompiledHeader.h"
|
||||
|
||||
#include "CDVD/CDVD.h"
|
||||
#include "CDVD/Ps1CD.h"
|
||||
#include "CDVD/CDVD_internal.h"
|
||||
#include "CDVD/IsoReader.h"
|
||||
#include "CDVD/IsoFileFormats.h"
|
||||
#include "GS.h"
|
||||
#include "Elfheader.h"
|
||||
#include "ps2/BiosTools.h"
|
||||
#include "Recording/InputRecording.h"
|
||||
#include "Host.h"
|
||||
#include "R3000A.h"
|
||||
#include "Common.h"
|
||||
#include "IopHw.h"
|
||||
@@ -21,25 +32,15 @@
|
||||
#include "VMManager.h"
|
||||
#include "Sio.h"
|
||||
|
||||
#include <cctype>
|
||||
#include <ctime>
|
||||
#include <memory>
|
||||
|
||||
#include "common/Error.h"
|
||||
#include "common/FileSystem.h"
|
||||
#include "common/Path.h"
|
||||
#include "common/StringUtil.h"
|
||||
#include "common/Threading.h"
|
||||
|
||||
#include "Ps1CD.h"
|
||||
#include "CDVD.h"
|
||||
#include "CDVD_internal.h"
|
||||
#include "IsoFileFormats.h"
|
||||
|
||||
#include "GS.h" // for gsVideoMode
|
||||
#include "Elfheader.h"
|
||||
#include "ps2/BiosTools.h"
|
||||
#include "Recording/InputRecording.h"
|
||||
#include "Host.h"
|
||||
#include <cctype>
|
||||
#include <ctime>
|
||||
#include <memory>
|
||||
|
||||
cdvdStruct cdvd;
|
||||
|
||||
@@ -383,49 +384,72 @@ s32 cdvdWriteConfig(const u8* config)
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool cdvdLoadElf(ElfObject* elfo, std::string elfpath, bool isPSXElf, Error* error)
|
||||
static bool cdvdUncheckedLoadDiscElf(ElfObject* elfo, IsoReader& isor, const std::string_view& elfpath, bool isPSXElf, Error* error)
|
||||
{
|
||||
// Strip out cdrom: prefix, and any leading slashes.
|
||||
size_t start_pos = (elfpath[5] == '0') ? 7 : 6;
|
||||
while (start_pos < elfpath.size() && (elfpath[start_pos] == '\\' || elfpath[start_pos] == '/'))
|
||||
start_pos++;
|
||||
|
||||
// Strip out any version information. Some games use ;2 (MLB2k6), others put multiple versions in
|
||||
// (Syphon Filter Omega Strain). The PS2 BIOS appears to ignore the suffix entirely, so we'll do
|
||||
// the same, and hope that no games actually have multiple ELFs with different versions.
|
||||
// Previous notes:
|
||||
// Mimic PS2 behavior!
|
||||
// Much trial-and-error with changing the ISOFS and BOOT2 contents of an image have shown that
|
||||
// the PS2 BIOS performs the peculiar task of *ignoring* the version info from the parsed BOOT2
|
||||
// filename *and* the ISOFS, when loading the game's ELF image. What this means is:
|
||||
//
|
||||
// 1. a valid PS2 ELF can have any version (ISOFS), and the version need not match the one in SYSTEM.CNF.
|
||||
// 2. the version info on the file in the BOOT2 parameter of SYSTEM.CNF can be missing, 10 chars long,
|
||||
// or anything else. Its all ignored.
|
||||
// 3. Games loading their own files do *not* exhibit this behavior; likely due to using newer IOP modules
|
||||
// or lower level filesystem APIs (fortunately that doesn't affect us).
|
||||
//
|
||||
size_t length = elfpath.length() - start_pos;
|
||||
const size_t semi_pos = elfpath.find(';', start_pos);
|
||||
if (semi_pos != std::string::npos)
|
||||
length = semi_pos - start_pos;
|
||||
|
||||
std::string iso_filename(elfpath.substr(start_pos, length));
|
||||
DevCon.WriteLn(fmt::format("cvdLoadElf(): '{}' -> '{}' in ISO.", elfpath, iso_filename));
|
||||
if (iso_filename.empty())
|
||||
{
|
||||
Error::SetString(error, "ISO filename is empty.");
|
||||
return false;
|
||||
}
|
||||
|
||||
return elfo->OpenIsoFile(std::move(iso_filename), isor, isPSXElf, error);
|
||||
}
|
||||
|
||||
bool cdvdLoadElf(ElfObject* elfo, const std::string_view& elfpath, bool isPSXElf, Error* error)
|
||||
{
|
||||
if (StringUtil::StartsWith(elfpath, "host:"))
|
||||
{
|
||||
std::string host_filename(elfpath.substr(5));
|
||||
if (!elfo->OpenFile(host_filename, isPSXElf, error))
|
||||
return elfo->OpenFile(host_filename, isPSXElf, error);
|
||||
}
|
||||
else if (StringUtil::StartsWith(elfpath, "cdrom:") || StringUtil::StartsWith(elfpath, "cdrom0:"))
|
||||
{
|
||||
IsoReader isor;
|
||||
if (!isor.Open(error))
|
||||
return false;
|
||||
|
||||
return cdvdLoadDiscElf(elfo, isor, elfpath, isPSXElf, error);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Mimic PS2 behavior!
|
||||
// Much trial-and-error with changing the ISOFS and BOOT2 contents of an image have shown that
|
||||
// the PS2 BIOS performs the peculiar task of *ignoring* the version info from the parsed BOOT2
|
||||
// filename *and* the ISOFS, when loading the game's ELF image. What this means is:
|
||||
//
|
||||
// 1. a valid PS2 ELF can have any version (ISOFS), and the version need not match the one in SYSTEM.CNF.
|
||||
// 2. the version info on the file in the BOOT2 parameter of SYSTEM.CNF can be missing, 10 chars long,
|
||||
// or anything else. Its all ignored.
|
||||
// 3. Games loading their own files do *not* exhibit this behavior; likely due to using newer IOP modules
|
||||
// or lower level filesystem APIs (fortunately that doesn't affect us).
|
||||
//
|
||||
// FIXME: Properly mimicing this behavior is troublesome since we need to add support for "ignoring"
|
||||
// version information when doing file searches. I'll add this later. For now, assuming a ;1 should
|
||||
// be sufficient (no known games have their ELF binary as anything but version ;1)
|
||||
const std::string::size_type semi_pos = elfpath.rfind(';');
|
||||
if (semi_pos != std::string::npos && std::string_view(elfpath).substr(semi_pos) != ";1")
|
||||
{
|
||||
Console.WriteLn(Color_Blue, "(LoadELF) Non-conforming version suffix (%s) detected and replaced.", elfpath.c_str());
|
||||
elfpath.erase(semi_pos);
|
||||
elfpath += ";1";
|
||||
}
|
||||
|
||||
// Fix cdrom:path, the iso reader doesn't like it.
|
||||
if (StringUtil::StartsWith(elfpath, "cdrom:") && elfpath[6] != '\\' && elfpath[6] != '/')
|
||||
elfpath.insert(6, 1, '\\');
|
||||
|
||||
IsoFSCDVD isofs;
|
||||
IsoFile file(isofs);
|
||||
if (!file.open(elfpath, error) || !elfo->OpenIsoFile(elfpath, file, isPSXElf, error))
|
||||
return false;
|
||||
Console.Error(fmt::format("cdvdLoadElf(): Unknown device in ELF path '{}'", elfpath));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
bool cdvdLoadDiscElf(ElfObject* elfo, IsoReader& isor, const std::string_view& elfpath, bool isPSXElf, Error* error)
|
||||
{
|
||||
if (!StringUtil::StartsWith(elfpath, "cdrom:") && !StringUtil::StartsWith(elfpath, "cdrom0:"))
|
||||
return false;
|
||||
|
||||
return cdvdUncheckedLoadDiscElf(elfo, isor, elfpath, isPSXElf, error);
|
||||
}
|
||||
|
||||
u32 cdvdGetElfCRC(const std::string& path)
|
||||
@@ -437,65 +461,57 @@ u32 cdvdGetElfCRC(const std::string& path)
|
||||
return elfo.GetCRC();
|
||||
}
|
||||
|
||||
static CDVDDiscType GetPS2ElfName(std::string* name, std::string* version)
|
||||
static CDVDDiscType GetPS2ElfName(IsoReader& isor, std::string* name, std::string* version, Error* error)
|
||||
{
|
||||
CDVDDiscType retype = CDVDDiscType::Other;
|
||||
name->clear();
|
||||
version->clear();
|
||||
|
||||
Error error;
|
||||
IsoFSCDVD isofs;
|
||||
IsoFile file(isofs);
|
||||
if (!file.open("SYSTEM.CNF;1", &error))
|
||||
{
|
||||
Console.Error(fmt::format("(GetElfName) Failed to open SYSTEM.CNF: {}", error.GetDescription()));
|
||||
return CDVDDiscType::Other;
|
||||
}
|
||||
|
||||
const int size = file.getLength();
|
||||
if (size == 0)
|
||||
std::vector<u8> data;
|
||||
if (!isor.ReadFile("SYSTEM.CNF", &data, error))
|
||||
return CDVDDiscType::Other;
|
||||
|
||||
while (!file.eof())
|
||||
const std::vector<std::string_view> lines =
|
||||
StringUtil::SplitString(std::string_view(reinterpret_cast<const char*>(data.data()), data.size()), '\n');
|
||||
for (size_t lineno = 0; lineno < lines.size(); lineno++)
|
||||
{
|
||||
const std::string line(file.readLine());
|
||||
const std::string_view line = StringUtil::StripWhitespace(lines[lineno]);
|
||||
std::string_view key, value;
|
||||
if (!StringUtil::ParseAssignmentString(line, &key, &value))
|
||||
continue;
|
||||
|
||||
if (value.empty() && file.getLength() != file.getSeekPos())
|
||||
{ // Some games have a character on the last line of the file, don't print the error in those cases.
|
||||
// Some games have a character on the last line of the file, don't print the error in those cases.
|
||||
if (value.empty() && (lineno == (lines.size() - 1)))
|
||||
{
|
||||
Console.Warning("(SYSTEM.CNF) Unusual or malformed entry in SYSTEM.CNF ignored:");
|
||||
Console.Indent().WriteLn(line);
|
||||
Console.Indent().WriteLn(std::string(line));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (key == "BOOT2")
|
||||
{
|
||||
Console.WriteLn(Color_StrongBlue, fmt::format("(SYSTEM.CNF) Detected PS2 Disc = {}", value));
|
||||
DevCon.WriteLn(Color_StrongBlue, fmt::format("(SYSTEM.CNF) Detected PS2 Disc = {}", value));
|
||||
*name = value;
|
||||
retype = CDVDDiscType::PS2Disc;
|
||||
}
|
||||
else if (key == "BOOT")
|
||||
{
|
||||
Console.WriteLn(Color_StrongBlue, fmt::format("(SYSTEM.CNF) Detected PSX/PSone Disc = {}", value));
|
||||
DevCon.WriteLn(Color_StrongBlue, fmt::format("(SYSTEM.CNF) Detected PSX/PSone Disc = {}", value));
|
||||
*name = value;
|
||||
retype = CDVDDiscType::PS1Disc;
|
||||
}
|
||||
else if (key == "VMODE")
|
||||
{
|
||||
Console.WriteLn(Color_Blue, fmt::format("(SYSTEM.CNF) Disc region type = {}", value));
|
||||
DevCon.WriteLn(Color_Blue, fmt::format("(SYSTEM.CNF) Disc region type = {}", value));
|
||||
}
|
||||
else if (key == "VER")
|
||||
{
|
||||
Console.WriteLn(Color_Blue, fmt::format("(SYSTEM.CNF) Software version = {}", value));
|
||||
DevCon.WriteLn(Color_Blue, fmt::format("(SYSTEM.CNF) Software version = {}", value));
|
||||
*version = value;
|
||||
}
|
||||
}
|
||||
|
||||
if (retype == CDVDDiscType::Other)
|
||||
Console.Error("(GetElfName) Disc image is *not* a PlayStation or PS2 game!");
|
||||
|
||||
Error::SetString(error, "Disc image is *not* a PlayStation or PS2 game");
|
||||
return retype;
|
||||
}
|
||||
|
||||
@@ -554,8 +570,13 @@ static std::string ExecutablePathToSerial(const std::string& path)
|
||||
void cdvdGetDiscInfo(std::string* out_serial, std::string* out_elf_path, std::string* out_version, u32* out_crc,
|
||||
CDVDDiscType* out_disc_type)
|
||||
{
|
||||
Error error;
|
||||
IsoReader isor;
|
||||
|
||||
std::string elfpath, version;
|
||||
const CDVDDiscType disc_type = GetPS2ElfName(&elfpath, &version);
|
||||
CDVDDiscType disc_type = CDVDDiscType::Other;
|
||||
if (!isor.Open(&error) || (disc_type = GetPS2ElfName(isor, &elfpath, &version, &error)) == CDVDDiscType::Other)
|
||||
Console.Error(fmt::format("Failed to get ELF name: {}", error.GetDescription()));
|
||||
|
||||
// Don't bother parsing it if we don't need the CRC.
|
||||
if (out_crc)
|
||||
@@ -564,10 +585,9 @@ void cdvdGetDiscInfo(std::string* out_serial, std::string* out_elf_path, std::st
|
||||
|
||||
if (disc_type == CDVDDiscType::PS2Disc || disc_type == CDVDDiscType::PS1Disc)
|
||||
{
|
||||
Error error;
|
||||
ElfObject elfo;
|
||||
const bool isPSXElf = (disc_type == CDVDDiscType::PS1Disc);
|
||||
if (!cdvdLoadElf(&elfo, elfpath, isPSXElf, &error))
|
||||
if (!cdvdLoadDiscElf(&elfo, isor, elfpath, isPSXElf, &error))
|
||||
Console.Error(fmt::format("Failed to load ELF info for {}: {}", elfpath, error.GetDescription()));
|
||||
else
|
||||
crc = elfo.GetCRC();
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
|
||||
class Error;
|
||||
class ElfObject;
|
||||
class IsoReader;
|
||||
|
||||
#define btoi(b) ((b) / 16 * 10 + (b) % 16) /* BCD to u_char */
|
||||
#define itob(i) ((i) / 10 * 16 + (i) % 10) /* u_char to BCD */
|
||||
@@ -190,7 +191,8 @@ extern void cdvdWrite(u8 key, u8 rt);
|
||||
extern void cdvdGetDiscInfo(std::string* out_serial, std::string* out_elf_path, std::string* out_version, u32* out_crc,
|
||||
CDVDDiscType* out_disc_type);
|
||||
extern u32 cdvdGetElfCRC(const std::string& path);
|
||||
extern bool cdvdLoadElf(ElfObject* elfo, std::string filename, bool isPSXElf, Error* error);
|
||||
extern bool cdvdLoadElf(ElfObject* elfo, const std::string_view& elfpath, bool isPSXElf, Error* error);
|
||||
extern bool cdvdLoadDiscElf(ElfObject* elfo, IsoReader& isor, const std::string_view& elfpath, bool isPSXElf, Error* error);
|
||||
|
||||
extern s32 cdvdCtrlTrayOpen();
|
||||
extern s32 cdvdCtrlTrayClose();
|
||||
|
||||
@@ -15,6 +15,8 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Common.h"
|
||||
|
||||
/*
|
||||
Interrupts - values are flag bits.
|
||||
|
||||
|
||||
@@ -16,29 +16,29 @@
|
||||
|
||||
#include "PrecompiledHeader.h"
|
||||
|
||||
#define ENABLE_TIMESTAMPS
|
||||
|
||||
#include <ctype.h>
|
||||
#include <time.h>
|
||||
#include <exception>
|
||||
#include <memory>
|
||||
|
||||
#include "fmt/core.h"
|
||||
|
||||
#include "IsoFS/IsoFS.h"
|
||||
#include "IsoFS/IsoFSCDVD.h"
|
||||
#include "IsoFileFormats.h"
|
||||
|
||||
#include "common/Assertions.h"
|
||||
#include "common/FileSystem.h"
|
||||
#include "common/Path.h"
|
||||
#include "common/StringUtil.h"
|
||||
#include "CDVD/CDVDcommon.h"
|
||||
#include "CDVD/IsoReader.h"
|
||||
#include "CDVD/IsoFileFormats.h"
|
||||
#include "DebugTools/SymbolMap.h"
|
||||
#include "Config.h"
|
||||
#include "Host.h"
|
||||
#include "IconsFontAwesome5.h"
|
||||
|
||||
CDVD_API* CDVD = NULL;
|
||||
#include "common/Assertions.h"
|
||||
#include "common/FileSystem.h"
|
||||
#include "common/Path.h"
|
||||
#include "common/StringUtil.h"
|
||||
|
||||
#include <ctype.h>
|
||||
#include <exception>
|
||||
#include <memory>
|
||||
#include <time.h>
|
||||
|
||||
#include "fmt/core.h"
|
||||
|
||||
#define ENABLE_TIMESTAMPS
|
||||
|
||||
CDVD_API* CDVD = nullptr;
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// diskTypeCached
|
||||
@@ -71,37 +71,35 @@ static void CheckNullCDVD()
|
||||
//
|
||||
static int CheckDiskTypeFS(int baseType)
|
||||
{
|
||||
IsoFSCDVD isofs;
|
||||
IsoDirectory rootdir(isofs);
|
||||
if (rootdir.OpenRootDirectory())
|
||||
IsoReader isor;
|
||||
if (isor.Open())
|
||||
{
|
||||
if (IsoFile file(isofs); file.open(rootdir, "SYSTEM.CNF;1"))
|
||||
std::vector<u8> data;
|
||||
if (isor.ReadFile("SYSTEM.CNF", &data))
|
||||
{
|
||||
const int size = file.getLength();
|
||||
const std::unique_ptr<char[]> buffer = std::make_unique<char[]>(size + 1);
|
||||
file.read(buffer.get(), size);
|
||||
buffer[size] = '\0';
|
||||
|
||||
char* pos = strstr(buffer.get(), "BOOT2");
|
||||
if (pos == NULL)
|
||||
if (StringUtil::ContainsSubString(data, "BOOT2"))
|
||||
{
|
||||
pos = strstr(buffer.get(), "BOOT");
|
||||
if (pos == NULL)
|
||||
return CDVD_TYPE_ILLEGAL;
|
||||
// PS2 DVD/CD.
|
||||
return (baseType == CDVD_TYPE_DETCTCD) ? CDVD_TYPE_PS2CD : CDVD_TYPE_PS2DVD;
|
||||
}
|
||||
|
||||
if (StringUtil::ContainsSubString(data, "BOOT"))
|
||||
{
|
||||
// PSX CD.
|
||||
return CDVD_TYPE_PSCD;
|
||||
}
|
||||
|
||||
return (baseType == CDVD_TYPE_DETCTCD) ? CDVD_TYPE_PS2CD : CDVD_TYPE_PS2DVD;
|
||||
return CDVD_TYPE_ILLEGAL;
|
||||
}
|
||||
|
||||
// PS2 Linux disc 2, doesn't have a System.CNF or a normal ELF
|
||||
if (rootdir.Exists("P2L_0100.02;1"))
|
||||
if (isor.FileExists("P2L_0100.02"))
|
||||
return CDVD_TYPE_PS2DVD;
|
||||
|
||||
if (rootdir.Exists("PSX.EXE;1"))
|
||||
if (isor.FileExists("PSX.EXE"))
|
||||
return CDVD_TYPE_PSCD;
|
||||
|
||||
if (rootdir.Exists("VIDEO_TS/VIDEO_TS.IFO;1"))
|
||||
if (isor.FileExists("VIDEO_TS/VIDEO_TS.IFO"))
|
||||
return CDVD_TYPE_DVDV;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,60 +0,0 @@
|
||||
/* PCSX2 - PS2 Emulator for PCs
|
||||
* Copyright (C) 2002-2010 PCSX2 Dev Team
|
||||
*
|
||||
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
|
||||
* of the GNU Lesser General Public License as published by the Free Software Found-
|
||||
* ation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
||||
* PURPOSE. See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with PCSX2.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/Pcsx2Defs.h"
|
||||
#include <optional>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
|
||||
class Error;
|
||||
|
||||
enum IsoFS_Type
|
||||
{
|
||||
FStype_ISO9660 = 1,
|
||||
FStype_Joliet = 2,
|
||||
};
|
||||
|
||||
class IsoDirectory final
|
||||
{
|
||||
public:
|
||||
SectorSource& internalReader;
|
||||
std::vector<IsoFileDescriptor> files;
|
||||
IsoFS_Type m_fstype = FStype_ISO9660;
|
||||
|
||||
public:
|
||||
IsoDirectory(SectorSource& r);
|
||||
~IsoDirectory();
|
||||
|
||||
bool OpenRootDirectory(Error* error = nullptr);
|
||||
bool Open(const IsoFileDescriptor& directoryEntry, Error* error = nullptr);
|
||||
|
||||
std::string FStype_ToString() const;
|
||||
SectorSource& GetReader() const { return internalReader; }
|
||||
|
||||
bool Exists(const std::string_view& filePath) const;
|
||||
bool IsFile(const std::string_view& filePath) const;
|
||||
bool IsDir(const std::string_view& filePath) const;
|
||||
|
||||
u32 GetFileSize(const std::string_view& filePath) const;
|
||||
|
||||
std::optional<IsoFileDescriptor> FindFile(const std::string_view& filePath) const;
|
||||
|
||||
protected:
|
||||
const IsoFileDescriptor& GetEntry(size_t index) const;
|
||||
|
||||
int GetIndexOf(const std::string_view& fileName) const;
|
||||
};
|
||||
@@ -1,322 +0,0 @@
|
||||
/* PCSX2 - PS2 Emulator for PCs
|
||||
* Copyright (C) 2002-2010 PCSX2 Dev Team
|
||||
*
|
||||
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
|
||||
* of the GNU Lesser General Public License as published by the Free Software Found-
|
||||
* ation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
||||
* PURPOSE. See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with PCSX2.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
#include "PrecompiledHeader.h"
|
||||
|
||||
#include "IsoFS.h"
|
||||
#include "IsoFile.h"
|
||||
|
||||
#include "common/Assertions.h"
|
||||
#include "common/Error.h"
|
||||
#include "common/FileSystem.h"
|
||||
#include "common/Path.h"
|
||||
#include "common/StringUtil.h"
|
||||
|
||||
#include <memory>
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// IsoDirectory
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
//u8 filesystemType; // 0x01 = ISO9660, 0x02 = Joliet, 0xFF = NULL
|
||||
//u8 volID[5]; // "CD001"
|
||||
|
||||
|
||||
std::string IsoDirectory::FStype_ToString() const
|
||||
{
|
||||
switch (m_fstype)
|
||||
{
|
||||
case FStype_ISO9660:
|
||||
return "ISO9660";
|
||||
case FStype_Joliet:
|
||||
return "Joliet";
|
||||
}
|
||||
|
||||
return StringUtil::StdStringFromFormat("Unrecognized Code (0x%x)", m_fstype);
|
||||
}
|
||||
|
||||
// Used to load the Root directory from an image
|
||||
IsoDirectory::IsoDirectory(SectorSource& r)
|
||||
: internalReader(r)
|
||||
{
|
||||
}
|
||||
|
||||
IsoDirectory::~IsoDirectory() = default;
|
||||
|
||||
bool IsoDirectory::OpenRootDirectory(Error* error /* = nullptr */)
|
||||
{
|
||||
IsoFileDescriptor rootDirEntry;
|
||||
bool isValid = false;
|
||||
bool done = false;
|
||||
uint i = 16;
|
||||
|
||||
m_fstype = FStype_ISO9660;
|
||||
|
||||
while (!done)
|
||||
{
|
||||
u8 sector[2048];
|
||||
// If this fails, we're not reading an iso, or it's bad.
|
||||
if (!internalReader.readSector(sector, i))
|
||||
break;
|
||||
|
||||
if (memcmp(§or[1], "CD001", 5) == 0)
|
||||
{
|
||||
switch (sector[0])
|
||||
{
|
||||
case 0:
|
||||
DevCon.WriteLn(Color_Green, "(IsoFS) Block 0x%x: Boot partition info.", i);
|
||||
break;
|
||||
|
||||
case 1:
|
||||
DevCon.WriteLn("(IsoFS) Block 0x%x: Primary partition info.", i);
|
||||
rootDirEntry.Load(sector + 156, 38);
|
||||
isValid = true;
|
||||
break;
|
||||
|
||||
case 2:
|
||||
// Probably means Joliet (long filenames support), which PCSX2 doesn't care about.
|
||||
DevCon.WriteLn(Color_Green, "(IsoFS) Block 0x%x: Extended partition info.", i);
|
||||
m_fstype = FStype_Joliet;
|
||||
break;
|
||||
|
||||
case 0xff:
|
||||
// Null terminator. End of partition information.
|
||||
done = true;
|
||||
break;
|
||||
|
||||
default:
|
||||
Console.Error("(IsoFS) Unknown partition type ID=%d, encountered at block 0x%x", sector[0], i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
sector[9] = 0;
|
||||
Console.Error("(IsoFS) Invalid partition descriptor encountered at block 0x%x: '%s'", i, §or[1]);
|
||||
break; // if no valid root partition was found, an exception will be thrown below.
|
||||
}
|
||||
|
||||
++i;
|
||||
}
|
||||
|
||||
if (!isValid)
|
||||
{
|
||||
Error::SetString(error, "IsoFS could not find the root directory on the ISO image.");
|
||||
return false;
|
||||
}
|
||||
|
||||
DevCon.WriteLn("(IsoFS) Filesystem is %s", FStype_ToString().c_str());
|
||||
return Open(rootDirEntry);
|
||||
}
|
||||
|
||||
// Used to load a specific directory from a file descriptor
|
||||
bool IsoDirectory::Open(const IsoFileDescriptor& directoryEntry, Error* error /* = nullptr */)
|
||||
{
|
||||
// parse directory sector
|
||||
IsoFile dataStream(internalReader, directoryEntry);
|
||||
|
||||
files.clear();
|
||||
|
||||
uint remainingSize = directoryEntry.size;
|
||||
|
||||
u8 b[257];
|
||||
|
||||
while (remainingSize >= 4) // hm hack :P
|
||||
{
|
||||
b[0] = dataStream.read<u8>();
|
||||
|
||||
if (b[0] == 0)
|
||||
{
|
||||
break; // or continue?
|
||||
}
|
||||
|
||||
remainingSize -= b[0];
|
||||
|
||||
dataStream.read(b + 1, b[0] - 1);
|
||||
|
||||
auto isoFile = IsoFileDescriptor(b, b[0]);
|
||||
|
||||
files.push_back(isoFile);
|
||||
|
||||
const std::string::size_type semi_pos = isoFile.name.rfind(';');
|
||||
if (semi_pos != std::string::npos && std::string_view(isoFile.name).substr(semi_pos) != ";1")
|
||||
{
|
||||
const std::string origName = isoFile.name;
|
||||
isoFile.name.erase(semi_pos);
|
||||
isoFile.name += ";1";
|
||||
Console.WriteLn("(IsoFS) Non-conforming version suffix (%s) detected. Creating 'hard-linked' entry (%s)",origName.c_str(), isoFile.name.c_str());
|
||||
|
||||
files.push_back(isoFile);
|
||||
}
|
||||
}
|
||||
|
||||
b[0] = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
const IsoFileDescriptor& IsoDirectory::GetEntry(size_t index) const
|
||||
{
|
||||
return files[index];
|
||||
}
|
||||
|
||||
int IsoDirectory::GetIndexOf(const std::string_view& fileName) const
|
||||
{
|
||||
for (unsigned int i = 0; i < files.size(); i++)
|
||||
{
|
||||
if (files[i].name == fileName)
|
||||
return i;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
std::optional<IsoFileDescriptor> IsoDirectory::FindFile(const std::string_view& filePath) const
|
||||
{
|
||||
if (filePath.empty())
|
||||
return std::nullopt;
|
||||
|
||||
// DOS-style parser should work fine for ISO 9660 path names. Only practical difference
|
||||
// is case sensitivity, and that won't matter for path splitting.
|
||||
std::vector<std::string_view> parts(Path::SplitWindowsPath(filePath));
|
||||
const IsoDirectory* dir = this;
|
||||
IsoDirectory subdir(internalReader);
|
||||
|
||||
// walk through path ("." and ".." entries are in the directories themselves, so even if the
|
||||
// path included . and/or .., it still works)
|
||||
|
||||
// ignore the device (cdrom0:\)
|
||||
const bool has_device = (parts.front().back() == ':');
|
||||
|
||||
for (size_t index = has_device ? 1 : 0; index < (parts.size() - 1); index++)
|
||||
{
|
||||
const int subdir_index = GetIndexOf(parts[index]);
|
||||
if (subdir_index < 0)
|
||||
return std::nullopt;
|
||||
|
||||
const IsoFileDescriptor& subdir_entry = GetEntry(static_cast<size_t>(index));
|
||||
if (subdir_entry.IsFile() || !subdir.Open(subdir_entry, nullptr))
|
||||
return std::nullopt;
|
||||
|
||||
dir = &subdir;
|
||||
}
|
||||
|
||||
const int file_index = dir->GetIndexOf(parts.back());
|
||||
if (file_index < 0)
|
||||
return std::nullopt;
|
||||
|
||||
return GetEntry(static_cast<size_t>(file_index));
|
||||
}
|
||||
|
||||
bool IsoDirectory::Exists(const std::string_view& filePath) const
|
||||
{
|
||||
if (filePath.empty())
|
||||
return false;
|
||||
|
||||
const std::optional<IsoFileDescriptor> fd(FindFile(filePath));
|
||||
return fd.has_value();
|
||||
}
|
||||
|
||||
bool IsoDirectory::IsFile(const std::string_view& filePath) const
|
||||
{
|
||||
if (filePath.empty())
|
||||
return false;
|
||||
|
||||
const std::optional<IsoFileDescriptor> fd(FindFile(filePath));
|
||||
if (fd.has_value())
|
||||
return false;
|
||||
|
||||
return ((fd->flags & 2) != 2);
|
||||
}
|
||||
|
||||
bool IsoDirectory::IsDir(const std::string_view& filePath) const
|
||||
{
|
||||
if (filePath.empty())
|
||||
return false;
|
||||
|
||||
const std::optional<IsoFileDescriptor> fd(FindFile(filePath));
|
||||
if (fd.has_value())
|
||||
return false;
|
||||
|
||||
return ((fd->flags & 2) == 2);
|
||||
}
|
||||
|
||||
u32 IsoDirectory::GetFileSize(const std::string_view& filePath) const
|
||||
{
|
||||
if (filePath.empty())
|
||||
return 0;
|
||||
|
||||
const std::optional<IsoFileDescriptor> fd(FindFile(filePath));
|
||||
if (fd.has_value())
|
||||
return 0;
|
||||
|
||||
return fd->size;
|
||||
}
|
||||
|
||||
IsoFileDescriptor::IsoFileDescriptor()
|
||||
: lba(0)
|
||||
, size(0)
|
||||
, flags(0)
|
||||
{
|
||||
memset(&date, 0, sizeof(date));
|
||||
}
|
||||
|
||||
IsoFileDescriptor::IsoFileDescriptor(const u8* data, int length)
|
||||
{
|
||||
Load(data, length);
|
||||
}
|
||||
|
||||
void IsoFileDescriptor::Load(const u8* data, int length)
|
||||
{
|
||||
lba = (u32&)data[2];
|
||||
size = (u32&)data[10];
|
||||
|
||||
date.year = data[18] + 1900;
|
||||
date.month = data[19];
|
||||
date.day = data[20];
|
||||
date.hour = data[21];
|
||||
date.minute = data[22];
|
||||
date.second = data[23];
|
||||
date.gmtOffset = data[24];
|
||||
|
||||
flags = data[25];
|
||||
|
||||
int fileNameLength = data[32];
|
||||
|
||||
if (fileNameLength == 1)
|
||||
{
|
||||
u8 c = data[33];
|
||||
|
||||
switch (c)
|
||||
{
|
||||
case 0:
|
||||
name = ".";
|
||||
break;
|
||||
case 1:
|
||||
name = "..";
|
||||
break;
|
||||
default:
|
||||
name = static_cast<char>(c);
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// copy string and up-convert from ascii to wxChar
|
||||
|
||||
const u8* fnsrc = data + 33;
|
||||
name.assign(reinterpret_cast<const char*>(fnsrc), fileNameLength);
|
||||
}
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
/* PCSX2 - PS2 Emulator for PCs
|
||||
* Copyright (C) 2002-2010 PCSX2 Dev Team
|
||||
*
|
||||
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
|
||||
* of the GNU Lesser General Public License as published by the Free Software Found-
|
||||
* ation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
||||
* PURPOSE. See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with PCSX2.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
class IsoFile;
|
||||
class IsoDirectory;
|
||||
struct IsoFileDescriptor;
|
||||
|
||||
#include "SectorSource.h"
|
||||
#include "IsoFileDescriptor.h"
|
||||
#include "IsoDirectory.h"
|
||||
#include "IsoFile.h"
|
||||
@@ -1,37 +0,0 @@
|
||||
/* PCSX2 - PS2 Emulator for PCs
|
||||
* Copyright (C) 2002-2010 PCSX2 Dev Team
|
||||
*
|
||||
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
|
||||
* of the GNU Lesser General Public License as published by the Free Software Found-
|
||||
* ation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
||||
* PURPOSE. See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with PCSX2.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
#include "PrecompiledHeader.h"
|
||||
|
||||
#include "IsoFSCDVD.h"
|
||||
#include "CDVD/CDVDcommon.h"
|
||||
|
||||
IsoFSCDVD::IsoFSCDVD()
|
||||
{
|
||||
}
|
||||
|
||||
bool IsoFSCDVD::readSector(unsigned char* buffer, int lba)
|
||||
{
|
||||
return DoCDVDreadSector(buffer, lba, CDVD_MODE_2048) >= 0;
|
||||
}
|
||||
|
||||
int IsoFSCDVD::getNumSectors()
|
||||
{
|
||||
cdvdTD td;
|
||||
CDVD->getTD(0, &td);
|
||||
|
||||
return td.lsn;
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
/* PCSX2 - PS2 Emulator for PCs
|
||||
* Copyright (C) 2002-2010 PCSX2 Dev Team
|
||||
*
|
||||
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
|
||||
* of the GNU Lesser General Public License as published by the Free Software Found-
|
||||
* ation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
||||
* PURPOSE. See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with PCSX2.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include "SectorSource.h"
|
||||
|
||||
class IsoFSCDVD final : public SectorSource
|
||||
{
|
||||
public:
|
||||
IsoFSCDVD();
|
||||
virtual ~IsoFSCDVD() = default;
|
||||
|
||||
virtual bool readSector(unsigned char* buffer, int lba);
|
||||
|
||||
virtual int getNumSectors();
|
||||
};
|
||||
@@ -1,262 +0,0 @@
|
||||
/* PCSX2 - PS2 Emulator for PCs
|
||||
* Copyright (C) 2002-2010 PCSX2 Dev Team
|
||||
*
|
||||
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
|
||||
* of the GNU Lesser General Public License as published by the Free Software Found-
|
||||
* ation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
||||
* PURPOSE. See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with PCSX2.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
#include "PrecompiledHeader.h"
|
||||
#include "common/Assertions.h"
|
||||
#include "common/Error.h"
|
||||
|
||||
#include "IsoFS.h"
|
||||
#include "IsoFile.h"
|
||||
|
||||
#include <cstdio>
|
||||
|
||||
IsoFile::IsoFile(SectorSource& reader)
|
||||
: internalReader(reader)
|
||||
{
|
||||
}
|
||||
|
||||
IsoFile::~IsoFile() = default;
|
||||
|
||||
IsoFile::IsoFile(SectorSource& reader, const IsoFileDescriptor& fileEntry)
|
||||
: internalReader(reader)
|
||||
, fileEntry(fileEntry)
|
||||
{
|
||||
Init();
|
||||
}
|
||||
|
||||
void IsoFile::Init()
|
||||
{
|
||||
//pxAssertDev( fileEntry.IsFile(), "IsoFile Error: Filename points to a directory." );
|
||||
|
||||
currentSectorNumber = fileEntry.lba;
|
||||
currentOffset = 0;
|
||||
sectorOffset = 0;
|
||||
maxOffset = fileEntry.size;
|
||||
|
||||
if (maxOffset > 0)
|
||||
internalReader.readSector(currentSector, currentSectorNumber);
|
||||
}
|
||||
|
||||
bool IsoFile::open(const IsoDirectory& dir, const std::string_view& filename, Error* error /*= nullptr*/)
|
||||
{
|
||||
const std::optional<IsoFileDescriptor> fd(dir.FindFile(filename));
|
||||
if (!fd.has_value())
|
||||
{
|
||||
Error::SetString(error, fmt::format("Failed to find file '{}'", filename));
|
||||
return false;
|
||||
}
|
||||
|
||||
fileEntry = std::move(fd.value());
|
||||
Init();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool IsoFile::open(const std::string_view& filename, Error* error /*= nullptr*/)
|
||||
{
|
||||
IsoDirectory dir(internalReader);
|
||||
if (!dir.OpenRootDirectory(error))
|
||||
return false;
|
||||
|
||||
return open(dir, filename, error);
|
||||
}
|
||||
|
||||
u32 IsoFile::seek(u32 absoffset)
|
||||
{
|
||||
u32 endOffset = absoffset;
|
||||
|
||||
int oldSectorNumber = currentSectorNumber;
|
||||
int newSectorNumber = fileEntry.lba + (int)(endOffset / sectorLength);
|
||||
|
||||
if (oldSectorNumber != newSectorNumber)
|
||||
{
|
||||
internalReader.readSector(currentSector, newSectorNumber);
|
||||
}
|
||||
|
||||
currentOffset = endOffset;
|
||||
currentSectorNumber = newSectorNumber;
|
||||
sectorOffset = (int)(currentOffset % sectorLength);
|
||||
|
||||
return currentOffset;
|
||||
}
|
||||
|
||||
// Returns the new offset in the file. Out-of-bounds seeks are automatically truncated at 0
|
||||
// and fileLength.
|
||||
u32 IsoFile::seek(s64 offset, int mode)
|
||||
{
|
||||
switch (mode)
|
||||
{
|
||||
case SEEK_SET:
|
||||
pxAssertDev(offset >= 0 && offset <= (s64)ULONG_MAX, "Invalid seek position from start.");
|
||||
return seek(offset);
|
||||
|
||||
case SEEK_CUR:
|
||||
// truncate negative values to zero, and positive values to 4gb
|
||||
return seek(std::min(std::max<s64>(0, (s64)currentOffset + offset), (s64)ULONG_MAX));
|
||||
|
||||
case SEEK_END:
|
||||
// truncate negative values to zero, and positive values to 4gb
|
||||
return seek(std::min(std::max<s64>(0, (s64)fileEntry.size + offset), (s64)ULONG_MAX));
|
||||
|
||||
jNO_DEFAULT;
|
||||
}
|
||||
|
||||
return 0; // unreachable
|
||||
}
|
||||
|
||||
void IsoFile::reset()
|
||||
{
|
||||
seek(0);
|
||||
}
|
||||
|
||||
// Returns the number of bytes actually skipped.
|
||||
s32 IsoFile::skip(s32 n)
|
||||
{
|
||||
s32 oldOffset = currentOffset;
|
||||
|
||||
if (n < 0)
|
||||
return 0;
|
||||
|
||||
seek(currentOffset + n);
|
||||
|
||||
return currentOffset - oldOffset;
|
||||
}
|
||||
|
||||
u32 IsoFile::getSeekPos() const
|
||||
{
|
||||
return currentOffset;
|
||||
}
|
||||
|
||||
bool IsoFile::eof() const
|
||||
{
|
||||
return (currentOffset == maxOffset);
|
||||
}
|
||||
|
||||
// loads the current sector index into the CurrentSector buffer.
|
||||
void IsoFile::makeDataAvailable()
|
||||
{
|
||||
if (sectorOffset >= sectorLength)
|
||||
{
|
||||
currentSectorNumber++;
|
||||
internalReader.readSector(currentSector, currentSectorNumber);
|
||||
sectorOffset -= sectorLength;
|
||||
}
|
||||
}
|
||||
|
||||
u8 IsoFile::readByte()
|
||||
{
|
||||
if (currentOffset >= maxOffset)
|
||||
return 0;
|
||||
|
||||
makeDataAvailable();
|
||||
|
||||
currentOffset++;
|
||||
|
||||
return currentSector[sectorOffset++];
|
||||
}
|
||||
|
||||
// Reads data from a single sector at a time. Reads cannot cross sector boundaries.
|
||||
int IsoFile::internalRead(void* dest, int off, int len)
|
||||
{
|
||||
if (len > 0)
|
||||
{
|
||||
size_t slen = len;
|
||||
if (slen > (maxOffset - currentOffset))
|
||||
{
|
||||
slen = (int)(maxOffset - currentOffset);
|
||||
}
|
||||
|
||||
memcpy((u8*)dest + off, currentSector + sectorOffset, slen);
|
||||
|
||||
sectorOffset += slen;
|
||||
currentOffset += slen;
|
||||
return slen;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// returns the number of bytes actually read.
|
||||
s32 IsoFile::read(void* dest, s32 len)
|
||||
{
|
||||
pxAssert(dest != NULL);
|
||||
pxAssert(len >= 0); // should we silent-fail on negative length reads? prolly not...
|
||||
|
||||
if (len <= 0)
|
||||
return 0;
|
||||
|
||||
int off = 0;
|
||||
|
||||
int totalLength = 0;
|
||||
|
||||
int firstSector = internalRead(dest, off, std::min(len, sectorLength - sectorOffset));
|
||||
off += firstSector;
|
||||
len -= firstSector;
|
||||
totalLength += firstSector;
|
||||
|
||||
// Read whole sectors
|
||||
while ((len >= sectorLength) && (currentOffset < maxOffset))
|
||||
{
|
||||
makeDataAvailable();
|
||||
int n = internalRead(dest, off, sectorLength);
|
||||
off += n;
|
||||
len -= n;
|
||||
totalLength += n;
|
||||
}
|
||||
|
||||
// Read remaining, if any
|
||||
if (len > 0)
|
||||
{
|
||||
makeDataAvailable();
|
||||
int lastSector = internalRead(dest, off, len);
|
||||
totalLength += lastSector;
|
||||
}
|
||||
|
||||
return totalLength;
|
||||
}
|
||||
|
||||
// Reads data until it reaches a newline character (either \n, \r, or ASCII-Z). The caller is
|
||||
// responsible for handling files with DOS-style newlines (CR/LF pairs), if needed. The resulting
|
||||
// string has no newlines.
|
||||
//
|
||||
// Read data is unformatted 8 bit / Ascii. If the source file is known to be UTF8, use the fromUTF8()
|
||||
// conversion helper provided by PCSX2 utility classes.
|
||||
//
|
||||
std::string IsoFile::readLine()
|
||||
{
|
||||
std::string s;
|
||||
s.reserve(512);
|
||||
|
||||
while (!eof())
|
||||
{
|
||||
u8 c = read<u8>();
|
||||
|
||||
if ((c == '\n') || (c == '\r') || (c == 0))
|
||||
break;
|
||||
|
||||
s += c;
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
u32 IsoFile::getLength()
|
||||
{
|
||||
return maxOffset;
|
||||
}
|
||||
|
||||
const IsoFileDescriptor& IsoFile::getEntry()
|
||||
{
|
||||
return fileEntry;
|
||||
}
|
||||
@@ -1,83 +0,0 @@
|
||||
/* PCSX2 - PS2 Emulator for PCs
|
||||
* Copyright (C) 2002-2010 PCSX2 Dev Team
|
||||
*
|
||||
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
|
||||
* of the GNU Lesser General Public License as published by the Free Software Found-
|
||||
* ation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
||||
* PURPOSE. See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with PCSX2.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "IsoFileDescriptor.h"
|
||||
#include "SectorSource.h"
|
||||
|
||||
#include "common/Pcsx2Defs.h"
|
||||
#include <string_view>
|
||||
|
||||
class Error;
|
||||
|
||||
class IsoFile final
|
||||
{
|
||||
public:
|
||||
static const int sectorLength = 2048;
|
||||
|
||||
protected:
|
||||
SectorSource& internalReader;
|
||||
IsoFileDescriptor fileEntry = {};
|
||||
|
||||
u32 currentOffset = 0;
|
||||
u32 maxOffset = 0;
|
||||
|
||||
int currentSectorNumber = 0;
|
||||
int sectorOffset = 0;
|
||||
u8 currentSector[sectorLength];
|
||||
|
||||
public:
|
||||
IsoFile(SectorSource& reader);
|
||||
IsoFile(SectorSource& reader, const IsoFileDescriptor& fileEntry);
|
||||
~IsoFile();
|
||||
|
||||
bool open(const IsoDirectory& dir, const std::string_view& filename, Error* error = nullptr);
|
||||
bool open(const std::string_view& filename, Error* error = nullptr);
|
||||
|
||||
u32 seek(u32 absoffset);
|
||||
u32 seek(s64 offset, int mode);
|
||||
void reset();
|
||||
|
||||
s32 skip(s32 n);
|
||||
u32 getSeekPos() const;
|
||||
u32 getLength();
|
||||
bool eof() const;
|
||||
|
||||
const IsoFileDescriptor& getEntry();
|
||||
|
||||
u8 readByte();
|
||||
s32 read(void* dest, s32 len);
|
||||
std::string readLine();
|
||||
|
||||
// Tool to read a specific value type, including structs.
|
||||
template <class T>
|
||||
T read()
|
||||
{
|
||||
if (sizeof(T) == 1)
|
||||
return (T)readByte();
|
||||
else
|
||||
{
|
||||
T t;
|
||||
read((u8*)&t, sizeof(t));
|
||||
return t;
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
void makeDataAvailable();
|
||||
int internalRead(void* dest, int off, int len);
|
||||
void Init();
|
||||
};
|
||||
@@ -1,47 +0,0 @@
|
||||
/* PCSX2 - PS2 Emulator for PCs
|
||||
* Copyright (C) 2002-2010 PCSX2 Dev Team
|
||||
*
|
||||
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
|
||||
* of the GNU Lesser General Public License as published by the Free Software Found-
|
||||
* ation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
||||
* PURPOSE. See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with PCSX2.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/Pcsx2Defs.h"
|
||||
#include <string>
|
||||
|
||||
struct IsoFileDescriptor
|
||||
{
|
||||
struct FileDate // not 1:1 with iso9660 date struct
|
||||
{
|
||||
s32 year;
|
||||
u8 month;
|
||||
u8 day;
|
||||
u8 hour;
|
||||
u8 minute;
|
||||
u8 second;
|
||||
u8 gmtOffset; // Offset from Greenwich Mean Time in number of 15 min intervals from -48 (West) to + 52 (East)
|
||||
|
||||
} date;
|
||||
|
||||
u32 lba;
|
||||
u32 size;
|
||||
int flags;
|
||||
std::string name;
|
||||
|
||||
IsoFileDescriptor();
|
||||
IsoFileDescriptor(const u8* data, int length);
|
||||
|
||||
void Load(const u8* data, int length);
|
||||
|
||||
bool IsFile() const { return !(flags & 2); }
|
||||
bool IsDir() const { return !IsFile(); }
|
||||
};
|
||||
@@ -1,24 +0,0 @@
|
||||
/* PCSX2 - PS2 Emulator for PCs
|
||||
* Copyright (C) 2002-2010 PCSX2 Dev Team
|
||||
*
|
||||
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
|
||||
* of the GNU Lesser General Public License as published by the Free Software Found-
|
||||
* ation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
||||
* PURPOSE. See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with PCSX2.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
class SectorSource
|
||||
{
|
||||
public:
|
||||
virtual int getNumSectors() = 0;
|
||||
virtual bool readSector(unsigned char* buffer, int lba) = 0;
|
||||
virtual ~SectorSource() = default;
|
||||
};
|
||||
@@ -0,0 +1,315 @@
|
||||
/* PCSX2 - PS2 Emulator for PCs
|
||||
* Copyright (C) 2002-2023 PCSX2 Dev Team
|
||||
*
|
||||
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
|
||||
* of the GNU Lesser General Public License as published by the Free Software Found-
|
||||
* ation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
||||
* PURPOSE. See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with PCSX2.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "PrecompiledHeader.h"
|
||||
|
||||
#include "CDVD/CDVDcommon.h"
|
||||
#include "CDVD/IsoReader.h"
|
||||
|
||||
#include "common/Assertions.h"
|
||||
#include "common/Error.h"
|
||||
#include "common/StringUtil.h"
|
||||
|
||||
#include "fmt/format.h"
|
||||
|
||||
#include <cctype>
|
||||
|
||||
IsoReader::IsoReader() = default;
|
||||
|
||||
IsoReader::~IsoReader() = default;
|
||||
|
||||
bool IsoReader::Open(Error* error)
|
||||
{
|
||||
if (!ReadPVD(error))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool IsoReader::ReadSector(u8* buf, u32 lsn, Error* error)
|
||||
{
|
||||
if (DoCDVDreadSector(buf, lsn, CDVD_MODE_2048) != 0)
|
||||
{
|
||||
Error::SetString(error, fmt::format("Failed to read sector LSN #{}", lsn));
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool IsoReader::ReadPVD(Error* error)
|
||||
{
|
||||
// volume descriptor start at sector 16
|
||||
static constexpr u32 START_SECTOR = 16;
|
||||
|
||||
// try only a maximum of 256 volume descriptors
|
||||
for (u32 i = 0; i < 256; i++)
|
||||
{
|
||||
u8 buffer[SECTOR_SIZE];
|
||||
if (!ReadSector(buffer, START_SECTOR + i, error))
|
||||
return false;
|
||||
|
||||
const ISOVolumeDescriptorHeader* header = reinterpret_cast<ISOVolumeDescriptorHeader*>(buffer);
|
||||
if (std::memcmp(header->standard_identifier, "CD001", 5) != 0)
|
||||
continue;
|
||||
else if (header->type_code != 1)
|
||||
continue;
|
||||
else if (header->type_code == 255)
|
||||
break;
|
||||
|
||||
std::memcpy(&m_pvd, buffer, sizeof(ISOPrimaryVolumeDescriptor));
|
||||
DevCon.WriteLn(fmt::format("ISOReader: PVD found at index {}", i));
|
||||
return true;
|
||||
}
|
||||
|
||||
Error::SetString(error, "Failed to find the Primary Volume Descriptor.");
|
||||
return false;
|
||||
}
|
||||
|
||||
std::optional<IsoReader::ISODirectoryEntry> IsoReader::LocateFile(const std::string_view& path, Error* error)
|
||||
{
|
||||
const ISODirectoryEntry* root_de = reinterpret_cast<const ISODirectoryEntry*>(m_pvd.root_directory_entry);
|
||||
if (path.empty() || path == "/" || path == "\\")
|
||||
{
|
||||
// locating the root directory
|
||||
return *root_de;
|
||||
}
|
||||
|
||||
// start at the root directory
|
||||
u8 sector_buffer[SECTOR_SIZE];
|
||||
return LocateFile(path, sector_buffer, root_de->location_le, root_de->length_le, error);
|
||||
}
|
||||
|
||||
std::string_view IsoReader::GetDirectoryEntryFileName(const u8* sector, u32 de_sector_offset)
|
||||
{
|
||||
const ISODirectoryEntry* de = reinterpret_cast<const ISODirectoryEntry*>(sector + de_sector_offset);
|
||||
if ((sizeof(ISODirectoryEntry) + de->filename_length) > de->entry_length ||
|
||||
(sizeof(ISODirectoryEntry) + de->filename_length + de_sector_offset) > SECTOR_SIZE)
|
||||
{
|
||||
return std::string_view();
|
||||
}
|
||||
|
||||
const char* str = reinterpret_cast<const char*>(sector + de_sector_offset + sizeof(ISODirectoryEntry));
|
||||
if (de->filename_length == 1)
|
||||
{
|
||||
if (str[0] == '\0')
|
||||
return ".";
|
||||
else if (str[0] == '\1')
|
||||
return "..";
|
||||
}
|
||||
|
||||
// Strip any version information like the PS2 BIOS does.
|
||||
u32 length_without_version = 0;
|
||||
for (; length_without_version < de->filename_length; length_without_version++)
|
||||
{
|
||||
if (str[length_without_version] == ';' || str[length_without_version] == '\0')
|
||||
break;
|
||||
}
|
||||
|
||||
return std::string_view(str, length_without_version);
|
||||
}
|
||||
|
||||
std::optional<IsoReader::ISODirectoryEntry> IsoReader::LocateFile(
|
||||
const std::string_view& path, u8* sector_buffer, u32 directory_record_lba, u32 directory_record_size, Error* error)
|
||||
{
|
||||
if (directory_record_size == 0)
|
||||
{
|
||||
Error::SetString(error, fmt::format("Directory entry record size 0 while looking for '{}'", path));
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
// strip any leading slashes
|
||||
size_t path_component_start = 0;
|
||||
while (path_component_start < path.length() &&
|
||||
(path[path_component_start] == '/' || path[path_component_start] == '\\'))
|
||||
{
|
||||
path_component_start++;
|
||||
}
|
||||
|
||||
size_t path_component_length = 0;
|
||||
while ((path_component_start + path_component_length) < path.length() &&
|
||||
path[path_component_start + path_component_length] != '/' &&
|
||||
path[path_component_start + path_component_length] != '\\')
|
||||
{
|
||||
path_component_length++;
|
||||
}
|
||||
|
||||
const std::string_view path_component = path.substr(path_component_start, path_component_length);
|
||||
if (path_component.empty())
|
||||
{
|
||||
Error::SetString(error, fmt::format("Empty path component in {}", path));
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
// start reading directory entries
|
||||
const u32 num_sectors = (directory_record_size + (SECTOR_SIZE - 1)) / SECTOR_SIZE;
|
||||
for (u32 i = 0; i < num_sectors; i++)
|
||||
{
|
||||
if (!ReadSector(sector_buffer, directory_record_lba + i, error))
|
||||
return std::nullopt;
|
||||
|
||||
u32 sector_offset = 0;
|
||||
while ((sector_offset + sizeof(ISODirectoryEntry)) < SECTOR_SIZE)
|
||||
{
|
||||
const ISODirectoryEntry* de = reinterpret_cast<const ISODirectoryEntry*>(§or_buffer[sector_offset]);
|
||||
if (de->entry_length < sizeof(ISODirectoryEntry))
|
||||
break;
|
||||
|
||||
const std::string_view de_filename = GetDirectoryEntryFileName(sector_buffer, sector_offset);
|
||||
sector_offset += de->entry_length;
|
||||
|
||||
// Empty file would be pretty strange..
|
||||
if (de_filename.empty() || de_filename == "." || de_filename == "..")
|
||||
continue;
|
||||
|
||||
if (!StringUtil::compareNoCase(de_filename, path_component))
|
||||
continue;
|
||||
|
||||
// found it. is this the file we're looking for?
|
||||
if ((path_component_start + path_component_length) == path.length())
|
||||
return *de;
|
||||
|
||||
// if it is a directory, recurse into it
|
||||
if (de->flags & ISODirectoryEntryFlag_Directory)
|
||||
{
|
||||
return LocateFile(path.substr(path_component_start + path_component_length), sector_buffer,
|
||||
de->location_le, de->length_le, error);
|
||||
}
|
||||
|
||||
// we're looking for a directory but got a file
|
||||
Error::SetString(error, fmt::format("Looking for directory '{}' but got file", path_component));
|
||||
return std::nullopt;
|
||||
}
|
||||
}
|
||||
|
||||
Error::SetString(error, fmt::format("Path component '{}' not found", path_component));
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::vector<std::string> IsoReader::GetFilesInDirectory(const std::string_view& path, Error* error)
|
||||
{
|
||||
std::string base_path(path);
|
||||
u32 directory_record_lsn;
|
||||
u32 directory_record_length;
|
||||
if (base_path.empty())
|
||||
{
|
||||
// root directory
|
||||
const ISODirectoryEntry* root_de = reinterpret_cast<const ISODirectoryEntry*>(m_pvd.root_directory_entry);
|
||||
directory_record_lsn = root_de->location_le;
|
||||
directory_record_length = root_de->length_le;
|
||||
}
|
||||
else
|
||||
{
|
||||
auto directory_de = LocateFile(base_path, error);
|
||||
if (!directory_de.has_value())
|
||||
return {};
|
||||
|
||||
if ((directory_de->flags & ISODirectoryEntryFlag_Directory) == 0)
|
||||
{
|
||||
Error::SetString(error, fmt::format("Path '{}' is not a directory, can't list", path));
|
||||
return {};
|
||||
}
|
||||
|
||||
directory_record_lsn = directory_de->location_le;
|
||||
directory_record_length = directory_de->length_le;
|
||||
|
||||
if (base_path[base_path.size() - 1] != '/')
|
||||
base_path += '/';
|
||||
}
|
||||
|
||||
// start reading directory entries
|
||||
const u32 num_sectors = (directory_record_length + (SECTOR_SIZE - 1)) / SECTOR_SIZE;
|
||||
std::vector<std::string> files;
|
||||
u8 sector_buffer[SECTOR_SIZE];
|
||||
for (u32 i = 0; i < num_sectors; i++)
|
||||
{
|
||||
if (!ReadSector(sector_buffer, directory_record_lsn + i, error))
|
||||
break;
|
||||
|
||||
u32 sector_offset = 0;
|
||||
while ((sector_offset + sizeof(ISODirectoryEntry)) < SECTOR_SIZE)
|
||||
{
|
||||
const ISODirectoryEntry* de = reinterpret_cast<const ISODirectoryEntry*>(§or_buffer[sector_offset]);
|
||||
if (de->entry_length < sizeof(ISODirectoryEntry))
|
||||
break;
|
||||
|
||||
const std::string_view de_filename = GetDirectoryEntryFileName(sector_buffer, sector_offset);
|
||||
sector_offset += de->entry_length;
|
||||
|
||||
// Empty file would be pretty strange..
|
||||
if (de_filename.empty() || de_filename == "." || de_filename == "..")
|
||||
continue;
|
||||
|
||||
files.push_back(fmt::format("{}/{}", base_path, de_filename));
|
||||
}
|
||||
}
|
||||
|
||||
return files;
|
||||
}
|
||||
|
||||
bool IsoReader::FileExists(const std::string_view& path, Error* error)
|
||||
{
|
||||
auto de = LocateFile(path, error);
|
||||
if (!de)
|
||||
return false;
|
||||
|
||||
return (de->flags & ISODirectoryEntryFlag_Directory) == 0;
|
||||
}
|
||||
|
||||
bool IsoReader::DirectoryExists(const std::string_view& path, Error* error)
|
||||
{
|
||||
auto de = LocateFile(path, error);
|
||||
if (!de)
|
||||
return false;
|
||||
|
||||
return (de->flags & ISODirectoryEntryFlag_Directory) == ISODirectoryEntryFlag_Directory;
|
||||
}
|
||||
|
||||
bool IsoReader::ReadFile(const std::string_view& path, std::vector<u8>* data, Error* error)
|
||||
{
|
||||
auto de = LocateFile(path, error);
|
||||
if (!de)
|
||||
return false;
|
||||
|
||||
return ReadFile(de.value(), data, error);
|
||||
}
|
||||
|
||||
bool IsoReader::ReadFile(const ISODirectoryEntry& de, std::vector<u8>* data, Error* error /*= nullptr*/)
|
||||
{
|
||||
if (de.flags & ISODirectoryEntryFlag_Directory)
|
||||
{
|
||||
Error::SetString(error, "File is a directory");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (de.length_le == 0)
|
||||
{
|
||||
data->clear();
|
||||
return true;
|
||||
}
|
||||
|
||||
static_assert(sizeof(size_t) == sizeof(u64));
|
||||
const u32 num_sectors = (de.length_le + (SECTOR_SIZE - 1)) / SECTOR_SIZE;
|
||||
data->resize(num_sectors * static_cast<u64>(SECTOR_SIZE));
|
||||
for (u32 i = 0, lsn = de.location_le; i < num_sectors; i++, lsn++)
|
||||
{
|
||||
if (!ReadSector(data->data() + (i * SECTOR_SIZE), lsn, error))
|
||||
return false;
|
||||
}
|
||||
|
||||
// Might not be sector aligned, so reduce it back.
|
||||
data->resize(de.length_le);
|
||||
return true;
|
||||
}
|
||||