Compare commits
26 Commits
1.2.64
...
Canary-1.2
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4831965404 | ||
|
|
47b8145809 | ||
|
|
20cc21add6 | ||
|
|
683baec1af | ||
|
|
f4957d2a09 | ||
|
|
3e1182af22 | ||
|
|
6664ed1b11 | ||
|
|
0c88b9eff7 | ||
|
|
8a064bcd7e | ||
|
|
5ff962be37 | ||
|
|
d9c8b7d937 | ||
|
|
feeeafe8fe | ||
|
|
04f014c777 | ||
|
|
4a677deb50 | ||
|
|
1c07bf3dd0 | ||
|
|
4c83794254 | ||
|
|
6911e288bc | ||
|
|
67ab54e2bb | ||
|
|
139c195eb7 | ||
|
|
9305d171e7 | ||
|
|
fb4ab5ea08 | ||
|
|
d7e17abade | ||
|
|
bdb92224f9 | ||
|
|
b21740c931 | ||
|
|
4f06c343a4 | ||
|
|
6c6f18509b |
4
.github/workflows/build.yml
vendored
4
.github/workflows/build.yml
vendored
@@ -61,11 +61,11 @@ jobs:
|
|||||||
if: matrix.platform.name != 'linux-arm64'
|
if: matrix.platform.name != 'linux-arm64'
|
||||||
|
|
||||||
- name: Publish Ryujinx
|
- name: Publish Ryujinx
|
||||||
run: dotnet publish -c "${{ matrix.configuration }}" -r "${{ matrix.platform.name }}" -o ./publish -p:Version="${{ env.RYUJINX_BASE_VERSION }}" -p:DebugType=embedded -p:SourceRevisionId="${{ steps.git_short_hash.outputs.result }}" -p:ExtraDefineConstants=DISABLE_UPDATER src/Ryujinx --self-contained true
|
run: dotnet publish -c "${{ matrix.configuration }}" -r "${{ matrix.platform.name }}" -o ./publish -p:Version="${{ env.RYUJINX_BASE_VERSION }}" -p:DebugType=embedded -p:SourceRevisionId="${{ steps.git_short_hash.outputs.result }}" -p:ExtraDefineConstants=DISABLE_UPDATER src/Ryujinx --self-contained
|
||||||
if: github.event_name == 'pull_request' && matrix.platform.os != 'macos-13'
|
if: github.event_name == 'pull_request' && matrix.platform.os != 'macos-13'
|
||||||
|
|
||||||
- name: Publish Ryujinx.Headless.SDL2
|
- name: Publish Ryujinx.Headless.SDL2
|
||||||
run: dotnet publish -c "${{ matrix.configuration }}" -r "${{ matrix.platform.name }}" -o ./publish_sdl2_headless -p:Version="${{ env.RYUJINX_BASE_VERSION }}" -p:DebugType=embedded -p:SourceRevisionId="${{ steps.git_short_hash.outputs.result }}" -p:ExtraDefineConstants=DISABLE_UPDATER src/Ryujinx.Headless.SDL2 --self-contained true
|
run: dotnet publish -c "${{ matrix.configuration }}" -r "${{ matrix.platform.name }}" -o ./publish_sdl2_headless -p:Version="${{ env.RYUJINX_BASE_VERSION }}" -p:DebugType=embedded -p:SourceRevisionId="${{ steps.git_short_hash.outputs.result }}" -p:ExtraDefineConstants=DISABLE_UPDATER src/Ryujinx.Headless.SDL2 --self-contained
|
||||||
if: github.event_name == 'pull_request' && matrix.platform.os != 'macos-13'
|
if: github.event_name == 'pull_request' && matrix.platform.os != 'macos-13'
|
||||||
|
|
||||||
- name: Set executable bit
|
- name: Set executable bit
|
||||||
|
|||||||
254
.github/workflows/canary.yml
vendored
Normal file
254
.github/workflows/canary.yml
vendored
Normal file
@@ -0,0 +1,254 @@
|
|||||||
|
name: Canary release job
|
||||||
|
|
||||||
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
|
inputs: {}
|
||||||
|
push:
|
||||||
|
branches: [ master ]
|
||||||
|
paths-ignore:
|
||||||
|
- '.github/**'
|
||||||
|
- 'docs/**'
|
||||||
|
- 'assets/**'
|
||||||
|
- '*.yml'
|
||||||
|
- '*.json'
|
||||||
|
- '*.config'
|
||||||
|
- '*.md'
|
||||||
|
|
||||||
|
concurrency: release
|
||||||
|
|
||||||
|
env:
|
||||||
|
POWERSHELL_TELEMETRY_OPTOUT: 1
|
||||||
|
DOTNET_CLI_TELEMETRY_OPTOUT: 1
|
||||||
|
RYUJINX_BASE_VERSION: "1.2"
|
||||||
|
RYUJINX_TARGET_RELEASE_CHANNEL_NAME: "canary"
|
||||||
|
RYUJINX_TARGET_RELEASE_CHANNEL_OWNER: "GreemDev"
|
||||||
|
RYUJINX_TARGET_RELEASE_CHANNEL_REPO: "Ryujinx-Canary"
|
||||||
|
RELEASE: 1
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
tag:
|
||||||
|
name: Create tag
|
||||||
|
runs-on: ubuntu-20.04
|
||||||
|
steps:
|
||||||
|
- name: Get version info
|
||||||
|
id: version_info
|
||||||
|
run: |
|
||||||
|
echo "build_version=${{ env.RYUJINX_BASE_VERSION }}.${{ github.run_number }}" >> $GITHUB_OUTPUT
|
||||||
|
echo "prev_build_version=${{ env.RYUJINX_BASE_VERSION }}.$((${{ github.run_number }} - 1))" >> $GITHUB_OUTPUT
|
||||||
|
shell: bash
|
||||||
|
|
||||||
|
- name: Create tag
|
||||||
|
uses: actions/github-script@v7
|
||||||
|
with:
|
||||||
|
script: |
|
||||||
|
github.rest.git.createRef({
|
||||||
|
owner: context.repo.owner,
|
||||||
|
repo: context.repo.repo,
|
||||||
|
ref: 'refs/tags/Canary-${{ steps.version_info.outputs.build_version }}',
|
||||||
|
sha: context.sha
|
||||||
|
})
|
||||||
|
|
||||||
|
- name: Create release
|
||||||
|
uses: ncipollo/release-action@v1
|
||||||
|
with:
|
||||||
|
name: "Canary ${{ steps.version_info.outputs.build_version }}"
|
||||||
|
tag: ${{ steps.version_info.outputs.build_version }}
|
||||||
|
body: "**Full Changelog**: https://github.com/${{ github.repository }}/compare/Canary-${{ steps.version_info.outputs.prev_build_version }}...Canary-${{ steps.version_info.outputs.build_version }}"
|
||||||
|
omitBodyDuringUpdate: true
|
||||||
|
owner: ${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }}
|
||||||
|
repo: ${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }}
|
||||||
|
token: ${{ secrets.RELEASE_TOKEN }}
|
||||||
|
|
||||||
|
release:
|
||||||
|
name: Release for ${{ matrix.platform.name }}
|
||||||
|
runs-on: ${{ matrix.platform.os }}
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
platform:
|
||||||
|
- { name: win-x64, os: windows-latest, zip_os_name: win_x64 }
|
||||||
|
- { name: linux-x64, os: ubuntu-latest, zip_os_name: linux_x64 }
|
||||||
|
- { name: linux-arm64, os: ubuntu-latest, zip_os_name: linux_arm64 }
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- uses: actions/setup-dotnet@v4
|
||||||
|
with:
|
||||||
|
global-json-file: global.json
|
||||||
|
|
||||||
|
- name: Overwrite csc problem matcher
|
||||||
|
run: echo "::add-matcher::.github/csc.json"
|
||||||
|
|
||||||
|
- name: Get version info
|
||||||
|
id: version_info
|
||||||
|
run: |
|
||||||
|
echo "build_version=${{ env.RYUJINX_BASE_VERSION }}.${{ github.run_number }}" >> $GITHUB_OUTPUT
|
||||||
|
echo "prev_build_version=${{ env.RYUJINX_BASE_VERSION }}.$((${{ github.run_number }} - 1))" >> $GITHUB_OUTPUT
|
||||||
|
echo "git_short_hash=$(git rev-parse --short "${{ github.sha }}")" >> $GITHUB_OUTPUT
|
||||||
|
shell: bash
|
||||||
|
|
||||||
|
- name: Configure for release
|
||||||
|
run: |
|
||||||
|
sed -r --in-place 's/\%\%RYUJINX_BUILD_VERSION\%\%/${{ steps.version_info.outputs.build_version }}/g;' src/Ryujinx.Common/ReleaseInformation.cs
|
||||||
|
sed -r --in-place 's/\%\%RYUJINX_BUILD_GIT_HASH\%\%/${{ steps.version_info.outputs.git_short_hash }}/g;' src/Ryujinx.Common/ReleaseInformation.cs
|
||||||
|
sed -r --in-place 's/\%\%RYUJINX_TARGET_RELEASE_CHANNEL_NAME\%\%/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_NAME }}/g;' src/Ryujinx.Common/ReleaseInformation.cs
|
||||||
|
sed -r --in-place 's/\%\%RYUJINX_TARGET_RELEASE_CHANNEL_OWNER\%\%/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }}/g;' src/Ryujinx.Common/ReleaseInformation.cs
|
||||||
|
sed -r --in-place 's/\%\%RYUJINX_TARGET_RELEASE_CHANNEL_REPO\%\%/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }}/g;' src/Ryujinx.Common/ReleaseInformation.cs
|
||||||
|
sed -r --in-place 's/\%\%RYUJINX_CONFIG_FILE_NAME\%\%/Config\.json/g;' src/Ryujinx.Common/ReleaseInformation.cs
|
||||||
|
shell: bash
|
||||||
|
|
||||||
|
- name: Create output dir
|
||||||
|
run: "mkdir release_output"
|
||||||
|
|
||||||
|
- name: Publish
|
||||||
|
run: |
|
||||||
|
dotnet publish -c Release -r "${{ matrix.platform.name }}" -o ./publish_ava/publish -p:Version="${{ steps.version_info.outputs.build_version }}" -p:SourceRevisionId="${{ steps.version_info.outputs.git_short_hash }}" -p:DebugType=embedded src/Ryujinx --self-contained -p:IncludeNativeLibrariesForSelfExtract=true
|
||||||
|
dotnet publish -c Release -r "${{ matrix.platform.name }}" -o ./publish_sdl2_headless/publish -p:Version="${{ steps.version_info.outputs.build_version }}" -p:SourceRevisionId="${{ steps.version_info.outputs.git_short_hash }}" -p:DebugType=embedded src/Ryujinx.Headless.SDL2 --self-contained -p:IncludeNativeLibrariesForSelfExtract=true
|
||||||
|
|
||||||
|
- name: Packing Windows builds
|
||||||
|
if: matrix.platform.os == 'windows-latest'
|
||||||
|
run: |
|
||||||
|
pushd publish_ava
|
||||||
|
rm publish/libarmeilleure-jitsupport.dylib
|
||||||
|
7z a ../release_output/ryujinx-${{ steps.version_info.outputs.build_version }}-${{ matrix.platform.zip_os_name }}.zip publish
|
||||||
|
popd
|
||||||
|
|
||||||
|
pushd publish_sdl2_headless
|
||||||
|
rm publish/libarmeilleure-jitsupport.dylib
|
||||||
|
7z a ../release_output/sdl2-ryujinx-headless-${{ steps.version_info.outputs.build_version }}-${{ matrix.platform.zip_os_name }}.zip publish
|
||||||
|
popd
|
||||||
|
shell: bash
|
||||||
|
|
||||||
|
- name: Packing Linux builds
|
||||||
|
if: matrix.platform.os == 'ubuntu-latest'
|
||||||
|
run: |
|
||||||
|
pushd publish_ava
|
||||||
|
rm publish/libarmeilleure-jitsupport.dylib
|
||||||
|
chmod +x publish/Ryujinx.sh publish/Ryujinx
|
||||||
|
tar -czvf ../release_output/ryujinx-${{ steps.version_info.outputs.build_version }}-${{ matrix.platform.zip_os_name }}.tar.gz publish
|
||||||
|
popd
|
||||||
|
|
||||||
|
pushd publish_sdl2_headless
|
||||||
|
rm publish/libarmeilleure-jitsupport.dylib
|
||||||
|
chmod +x publish/Ryujinx.sh publish/Ryujinx.Headless.SDL2
|
||||||
|
tar -czvf ../release_output/sdl2-ryujinx-headless-${{ steps.version_info.outputs.build_version }}-${{ matrix.platform.zip_os_name }}.tar.gz publish
|
||||||
|
popd
|
||||||
|
shell: bash
|
||||||
|
|
||||||
|
#- name: Build AppImage (Linux)
|
||||||
|
# if: matrix.platform.os == 'ubuntu-latest'
|
||||||
|
# run: |
|
||||||
|
# BUILD_VERSION="${{ steps.version_info.outputs.build_version }}"
|
||||||
|
# PLATFORM_NAME="${{ matrix.platform.name }}"
|
||||||
|
|
||||||
|
# sudo apt install -y zsync desktop-file-utils appstream
|
||||||
|
|
||||||
|
# mkdir -p tools
|
||||||
|
# export PATH="$PATH:$(readlink -f tools)"
|
||||||
|
|
||||||
|
# Setup appimagetool
|
||||||
|
# wget -q -O tools/appimagetool "https://github.com/AppImage/appimagetool/releases/download/continuous/appimagetool-x86_64.AppImage"
|
||||||
|
# chmod +x tools/appimagetool
|
||||||
|
# chmod +x distribution/linux/appimage/build-appimage.sh
|
||||||
|
|
||||||
|
# Explicitly set $ARCH for appimagetool ($ARCH_NAME is for the file name)
|
||||||
|
# if [ "$PLATFORM_NAME" = "linux-x64" ]; then
|
||||||
|
# ARCH_NAME=x64
|
||||||
|
# export ARCH=x86_64
|
||||||
|
# elif [ "$PLATFORM_NAME" = "linux-arm64" ]; then
|
||||||
|
# ARCH_NAME=arm64
|
||||||
|
# export ARCH=aarch64
|
||||||
|
# else
|
||||||
|
# echo "Unexpected PLATFORM_NAME "$PLATFORM_NAME""
|
||||||
|
# exit 1
|
||||||
|
# fi
|
||||||
|
|
||||||
|
# export UFLAG="gh-releases-zsync|${{ github.repository_owner }}|${{ github.event.repository.name }}|latest|*-$ARCH_NAME.AppImage.zsync"
|
||||||
|
# BUILDDIR=publish_ava OUTDIR=publish_ava_appimage distribution/linux/appimage/build-appimage.sh
|
||||||
|
|
||||||
|
# Add to release output
|
||||||
|
# pushd publish_ava_appimage
|
||||||
|
# mv Ryujinx.AppImage ../release_output/ryujinx-$BUILD_VERSION-$ARCH_NAME.AppImage
|
||||||
|
# mv Ryujinx.AppImage.zsync ../release_output/ryujinx-$BUILD_VERSION-$ARCH_NAME.AppImage.zsync
|
||||||
|
# popd
|
||||||
|
# shell: bash
|
||||||
|
|
||||||
|
- name: Pushing new release
|
||||||
|
uses: ncipollo/release-action@v1
|
||||||
|
with:
|
||||||
|
name: ${{ steps.version_info.outputs.build_version }}
|
||||||
|
artifacts: "release_output/*.tar.gz,release_output/*.zip"
|
||||||
|
#artifacts: "release_output/*.tar.gz,release_output/*.zip/*AppImage*"
|
||||||
|
tag: ${{ steps.version_info.outputs.build_version }}
|
||||||
|
body: "**Full Changelog**: https://github.com/${{ github.repository }}/compare/Canary-${{ steps.version_info.outputs.prev_build_version }}...Canary-${{ steps.version_info.outputs.build_version }}"
|
||||||
|
omitBodyDuringUpdate: true
|
||||||
|
allowUpdates: true
|
||||||
|
replacesArtifacts: true
|
||||||
|
owner: ${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }}
|
||||||
|
repo: ${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }}
|
||||||
|
token: ${{ secrets.RELEASE_TOKEN }}
|
||||||
|
|
||||||
|
macos_release:
|
||||||
|
name: Release MacOS universal
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- uses: actions/setup-dotnet@v4
|
||||||
|
with:
|
||||||
|
global-json-file: global.json
|
||||||
|
|
||||||
|
- name: Setup LLVM 15
|
||||||
|
run: |
|
||||||
|
wget https://apt.llvm.org/llvm.sh
|
||||||
|
chmod +x llvm.sh
|
||||||
|
sudo ./llvm.sh 15
|
||||||
|
|
||||||
|
- name: Install rcodesign
|
||||||
|
run: |
|
||||||
|
mkdir -p $HOME/.bin
|
||||||
|
gh release download -R indygreg/apple-platform-rs -O apple-codesign.tar.gz -p 'apple-codesign-*-x86_64-unknown-linux-musl.tar.gz'
|
||||||
|
tar -xzvf apple-codesign.tar.gz --wildcards '*/rcodesign' --strip-components=1
|
||||||
|
rm apple-codesign.tar.gz
|
||||||
|
mv rcodesign $HOME/.bin/
|
||||||
|
echo "$HOME/.bin" >> $GITHUB_PATH
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
|
- name: Get version info
|
||||||
|
id: version_info
|
||||||
|
run: |
|
||||||
|
echo "build_version=${{ env.RYUJINX_BASE_VERSION }}.${{ github.run_number }}" >> $GITHUB_OUTPUT
|
||||||
|
echo "prev_build_version=${{ env.RYUJINX_BASE_VERSION }}.$((${{ github.run_number }} - 1))" >> $GITHUB_OUTPUT
|
||||||
|
echo "git_short_hash=$(git rev-parse --short "${{ github.sha }}")" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
|
- name: Configure for release
|
||||||
|
run: |
|
||||||
|
sed -r --in-place 's/\%\%RYUJINX_BUILD_VERSION\%\%/${{ steps.version_info.outputs.build_version }}/g;' src/Ryujinx.Common/ReleaseInformation.cs
|
||||||
|
sed -r --in-place 's/\%\%RYUJINX_BUILD_GIT_HASH\%\%/${{ steps.version_info.outputs.git_short_hash }}/g;' src/Ryujinx.Common/ReleaseInformation.cs
|
||||||
|
sed -r --in-place 's/\%\%RYUJINX_TARGET_RELEASE_CHANNEL_NAME\%\%/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_NAME }}/g;' src/Ryujinx.Common/ReleaseInformation.cs
|
||||||
|
sed -r --in-place 's/\%\%RYUJINX_TARGET_RELEASE_CHANNEL_OWNER\%\%/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }}/g;' src/Ryujinx.Common/ReleaseInformation.cs
|
||||||
|
sed -r --in-place 's/\%\%RYUJINX_TARGET_RELEASE_CHANNEL_REPO\%\%/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }}/g;' src/Ryujinx.Common/ReleaseInformation.cs
|
||||||
|
sed -r --in-place 's/\%\%RYUJINX_CONFIG_FILE_NAME\%\%/Config\.json/g;' src/Ryujinx.Common/ReleaseInformation.cs
|
||||||
|
shell: bash
|
||||||
|
|
||||||
|
- name: Publish macOS Ryujinx
|
||||||
|
run: |
|
||||||
|
./distribution/macos/create_macos_build_ava.sh . publish_tmp_ava publish_ava ./distribution/macos/entitlements.xml "${{ steps.version_info.outputs.build_version }}" "${{ steps.version_info.outputs.git_short_hash }}" Release
|
||||||
|
|
||||||
|
- name: Publish macOS Ryujinx.Headless.SDL2
|
||||||
|
run: |
|
||||||
|
./distribution/macos/create_macos_build_headless.sh . publish_tmp_headless publish_headless ./distribution/macos/entitlements.xml "${{ steps.version_info.outputs.build_version }}" "${{ steps.version_info.outputs.git_short_hash }}" Release
|
||||||
|
|
||||||
|
- name: Pushing new release
|
||||||
|
uses: ncipollo/release-action@v1
|
||||||
|
with:
|
||||||
|
name: "Canary ${{ steps.version_info.outputs.build_version }}"
|
||||||
|
artifacts: "publish_ava/*.tar.gz, publish_headless/*.tar.gz"
|
||||||
|
tag: ${{ steps.version_info.outputs.build_version }}
|
||||||
|
body: "**Full Changelog**: https://github.com/${{ github.repository }}/compare/Canary-${{ steps.version_info.outputs.prev_build_version }}...Canary-${{ steps.version_info.outputs.build_version }}"
|
||||||
|
omitBodyDuringUpdate: true
|
||||||
|
allowUpdates: true
|
||||||
|
replacesArtifacts: true
|
||||||
|
owner: ${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }}
|
||||||
|
repo: ${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }}
|
||||||
|
token: ${{ secrets.RELEASE_TOKEN }}
|
||||||
12
.github/workflows/release.yml
vendored
12
.github/workflows/release.yml
vendored
@@ -4,7 +4,7 @@ on:
|
|||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
inputs: {}
|
inputs: {}
|
||||||
push:
|
push:
|
||||||
branches: [ master ]
|
branches: [ release ]
|
||||||
paths-ignore:
|
paths-ignore:
|
||||||
- '.github/**'
|
- '.github/**'
|
||||||
- 'docs/**'
|
- 'docs/**'
|
||||||
@@ -20,7 +20,7 @@ env:
|
|||||||
POWERSHELL_TELEMETRY_OPTOUT: 1
|
POWERSHELL_TELEMETRY_OPTOUT: 1
|
||||||
DOTNET_CLI_TELEMETRY_OPTOUT: 1
|
DOTNET_CLI_TELEMETRY_OPTOUT: 1
|
||||||
RYUJINX_BASE_VERSION: "1.2"
|
RYUJINX_BASE_VERSION: "1.2"
|
||||||
RYUJINX_TARGET_RELEASE_CHANNEL_NAME: "master"
|
RYUJINX_TARGET_RELEASE_CHANNEL_NAME: "release"
|
||||||
RYUJINX_TARGET_RELEASE_CHANNEL_OWNER: "GreemDev"
|
RYUJINX_TARGET_RELEASE_CHANNEL_OWNER: "GreemDev"
|
||||||
RYUJINX_TARGET_RELEASE_CHANNEL_REPO: "Ryujinx"
|
RYUJINX_TARGET_RELEASE_CHANNEL_REPO: "Ryujinx"
|
||||||
RELEASE: 1
|
RELEASE: 1
|
||||||
@@ -101,17 +101,19 @@ jobs:
|
|||||||
|
|
||||||
- name: Publish
|
- name: Publish
|
||||||
run: |
|
run: |
|
||||||
dotnet publish -c Release -r "${{ matrix.platform.name }}" -o ./publish_ava/publish -p:Version="${{ steps.version_info.outputs.build_version }}" -p:SourceRevisionId="${{ steps.version_info.outputs.git_short_hash }}" -p:DebugType=embedded src/Ryujinx --self-contained true
|
dotnet publish -c Release -r "${{ matrix.platform.name }}" -o ./publish_ava/publish -p:Version="${{ steps.version_info.outputs.build_version }}" -p:SourceRevisionId="${{ steps.version_info.outputs.git_short_hash }}" -p:DebugType=embedded src/Ryujinx --self-contained -p:IncludeNativeLibrariesForSelfExtract=true
|
||||||
dotnet publish -c Release -r "${{ matrix.platform.name }}" -o ./publish_sdl2_headless/publish -p:Version="${{ steps.version_info.outputs.build_version }}" -p:SourceRevisionId="${{ steps.version_info.outputs.git_short_hash }}" -p:DebugType=embedded src/Ryujinx.Headless.SDL2 --self-contained true
|
dotnet publish -c Release -r "${{ matrix.platform.name }}" -o ./publish_sdl2_headless/publish -p:Version="${{ steps.version_info.outputs.build_version }}" -p:SourceRevisionId="${{ steps.version_info.outputs.git_short_hash }}" -p:DebugType=embedded src/Ryujinx.Headless.SDL2 --self-contained -p:IncludeNativeLibrariesForSelfExtract=true
|
||||||
|
|
||||||
- name: Packing Windows builds
|
- name: Packing Windows builds
|
||||||
if: matrix.platform.os == 'windows-latest'
|
if: matrix.platform.os == 'windows-latest'
|
||||||
run: |
|
run: |
|
||||||
pushd publish_ava
|
pushd publish_ava
|
||||||
|
rm publish/libarmeilleure-jitsupport.dylib
|
||||||
7z a ../release_output/ryujinx-${{ steps.version_info.outputs.build_version }}-${{ matrix.platform.zip_os_name }}.zip publish
|
7z a ../release_output/ryujinx-${{ steps.version_info.outputs.build_version }}-${{ matrix.platform.zip_os_name }}.zip publish
|
||||||
popd
|
popd
|
||||||
|
|
||||||
pushd publish_sdl2_headless
|
pushd publish_sdl2_headless
|
||||||
|
rm publish/libarmeilleure-jitsupport.dylib
|
||||||
7z a ../release_output/sdl2-ryujinx-headless-${{ steps.version_info.outputs.build_version }}-${{ matrix.platform.zip_os_name }}.zip publish
|
7z a ../release_output/sdl2-ryujinx-headless-${{ steps.version_info.outputs.build_version }}-${{ matrix.platform.zip_os_name }}.zip publish
|
||||||
popd
|
popd
|
||||||
shell: bash
|
shell: bash
|
||||||
@@ -120,11 +122,13 @@ jobs:
|
|||||||
if: matrix.platform.os == 'ubuntu-latest'
|
if: matrix.platform.os == 'ubuntu-latest'
|
||||||
run: |
|
run: |
|
||||||
pushd publish_ava
|
pushd publish_ava
|
||||||
|
rm publish/libarmeilleure-jitsupport.dylib
|
||||||
chmod +x publish/Ryujinx.sh publish/Ryujinx
|
chmod +x publish/Ryujinx.sh publish/Ryujinx
|
||||||
tar -czvf ../release_output/ryujinx-${{ steps.version_info.outputs.build_version }}-${{ matrix.platform.zip_os_name }}.tar.gz publish
|
tar -czvf ../release_output/ryujinx-${{ steps.version_info.outputs.build_version }}-${{ matrix.platform.zip_os_name }}.tar.gz publish
|
||||||
popd
|
popd
|
||||||
|
|
||||||
pushd publish_sdl2_headless
|
pushd publish_sdl2_headless
|
||||||
|
rm publish/libarmeilleure-jitsupport.dylib
|
||||||
chmod +x publish/Ryujinx.sh publish/Ryujinx.Headless.SDL2
|
chmod +x publish/Ryujinx.sh publish/Ryujinx.Headless.SDL2
|
||||||
tar -czvf ../release_output/sdl2-ryujinx-headless-${{ steps.version_info.outputs.build_version }}-${{ matrix.platform.zip_os_name }}.tar.gz publish
|
tar -czvf ../release_output/sdl2-ryujinx-headless-${{ steps.version_info.outputs.build_version }}-${{ matrix.platform.zip_os_name }}.tar.gz publish
|
||||||
popd
|
popd
|
||||||
|
|||||||
@@ -12,24 +12,15 @@ Please read the entire document before continuing as it can potentially save eve
|
|||||||
|
|
||||||
We always welcome bug reports, feature proposals and overall feedback. Here are a few tips on how you can make reporting your issue as effective as possible.
|
We always welcome bug reports, feature proposals and overall feedback. Here are a few tips on how you can make reporting your issue as effective as possible.
|
||||||
|
|
||||||
### Identify Where to Report
|
|
||||||
|
|
||||||
The Ryujinx codebase is distributed across multiple repositories in the [Ryujinx organization](https://github.com/Ryujinx). Depending on the feedback you might want to file the issue on a different repo. Here are a few common repos:
|
|
||||||
|
|
||||||
* [Ryujinx/Ryujinx](https://github.com/Ryujinx/Ryujinx) Ryujinx core project files.
|
|
||||||
* [Ryujinx/Ryujinx-Games-List](https://github.com/Ryujinx/Ryujinx-Games-List) Ryujinx game compatibility list.
|
|
||||||
* [Ryujinx/Ryujinx-Website](https://github.com/Ryujinx/Ryujinx-Website) Ryujinx website source code.
|
|
||||||
* [Ryujinx/Ryujinx-Ldn-Website](https://github.com/Ryujinx/Ryujinx-Ldn-Website) Ryujinx LDN website source code.
|
|
||||||
|
|
||||||
### Finding Existing Issues
|
### Finding Existing Issues
|
||||||
|
|
||||||
Before filing a new issue, please search our [open issues](https://github.com/Ryujinx/Ryujinx/issues) to check if it already exists.
|
Before filing a new issue, please search our [open issues](https://github.com/GreemDev/Ryujinx/issues) to check if it already exists.
|
||||||
|
|
||||||
If you do find an existing issue, please include your own feedback in the discussion. Do consider upvoting (👍 reaction) the original post, as this helps us prioritize popular issues in our backlog.
|
If you do find an existing issue, please include your own feedback in the discussion. Do consider upvoting (👍 reaction) the original post, as this helps us prioritize popular issues in our backlog.
|
||||||
|
|
||||||
### Writing a Good Feature Request
|
### Writing a Good Feature Request
|
||||||
|
|
||||||
Please review any feature requests already opened to both check it has not already been suggested, and to familiarize yourself with the format. When ready to submit a proposal, please use the [Feature Request issue template](https://github.com/Ryujinx/Ryujinx/issues/new?assignees=&labels=&projects=&template=feature_request.yml&title=%5BFeature+Request%5D).
|
Please review any feature requests already opened to both check it has not already been suggested, and to familiarize yourself with the format. When ready to submit a proposal, please use the [Feature Request issue template](https://github.com/GreemDev/Ryujinx/issues/new?assignees=&labels=&projects=&template=feature_request.yml&title=%5BFeature+Request%5D).
|
||||||
|
|
||||||
### Writing a Good Bug Report
|
### Writing a Good Bug Report
|
||||||
|
|
||||||
@@ -43,13 +34,13 @@ Ideally, a bug report should contain the following information:
|
|||||||
* A Ryujinx log file of the run instance where the issue occurred. Log files can be found in `[Executable Folder]/Logs` and are named chronologically.
|
* A Ryujinx log file of the run instance where the issue occurred. Log files can be found in `[Executable Folder]/Logs` and are named chronologically.
|
||||||
* Additional information, e.g. is it a regression from previous versions? Are there any known workarounds?
|
* Additional information, e.g. is it a regression from previous versions? Are there any known workarounds?
|
||||||
|
|
||||||
When ready to submit a bug report, please use the [Bug Report issue template](https://github.com/Ryujinx/Ryujinx/issues/new?assignees=&labels=bug&projects=&template=bug_report.yml&title=%5BBug%5D).
|
When ready to submit a bug report, please use the [Bug Report issue template](https://github.com/GreemDev/Ryujinx/issues/new?assignees=&labels=bug&projects=&template=bug_report.yml&title=%5BBug%5D).
|
||||||
|
|
||||||
## Contributing Changes
|
## Contributing Changes
|
||||||
|
|
||||||
Project maintainers will merge changes that both improve the project and meet our standards for code quality.
|
Project maintainers will merge changes that both improve the project and meet our standards for code quality.
|
||||||
|
|
||||||
The [Pull Request Guide](docs/workflow/pr-guide.md) and [License](https://github.com/Ryujinx/Ryujinx/blob/master/LICENSE.txt) docs define additional guidance.
|
The [Pull Request Guide](docs/workflow/pr-guide.md) and [License](https://github.com/GreemDev/Ryujinx/blob/master/LICENSE.txt) docs define additional guidance.
|
||||||
|
|
||||||
### DOs and DON'Ts
|
### DOs and DON'Ts
|
||||||
|
|
||||||
@@ -83,15 +74,15 @@ We use and recommend the following workflow:
|
|||||||
3. In your fork, create a branch off of main (`git checkout -b mybranch`).
|
3. In your fork, create a branch off of main (`git checkout -b mybranch`).
|
||||||
- Branches are useful since they isolate your changes from incoming changes from upstream. They also enable you to create multiple PRs from the same fork.
|
- Branches are useful since they isolate your changes from incoming changes from upstream. They also enable you to create multiple PRs from the same fork.
|
||||||
4. Make and commit your changes to your branch.
|
4. Make and commit your changes to your branch.
|
||||||
- [Build Instructions](https://github.com/Ryujinx/Ryujinx#building) explains how to build and test.
|
- [Build Instructions](https://github.com/GreemDev/Ryujinx#building) explains how to build and test.
|
||||||
- Commit messages should be clear statements of action and intent.
|
- Commit messages should be clear statements of action and intent.
|
||||||
6. Build the repository with your changes.
|
6. Build the repository with your changes.
|
||||||
- Make sure that the builds are clean.
|
- Make sure that the builds are clean.
|
||||||
- Make sure that `dotnet format` has been run and any corrections tested and committed.
|
- Make sure that `dotnet format` has been run and any corrections tested and committed.
|
||||||
7. Create a pull request (PR) against the Ryujinx/Ryujinx repository's **main** branch.
|
7. Create a pull request (PR) against the Ryujinx/Ryujinx repository's **main** branch.
|
||||||
- State in the description what issue or improvement your change is addressing.
|
- State in the description what issue or improvement your change is addressing.
|
||||||
- Check if all the Continuous Integration checks are passing. Refer to [Actions](https://github.com/Ryujinx/Ryujinx/actions) to check for outstanding errors.
|
- Check if all the Continuous Integration checks are passing. Refer to [Actions](https://github.com/GreemDev/Ryujinx/actions) to check for outstanding errors.
|
||||||
8. Wait for feedback or approval of your changes from the [core development team](https://github.com/orgs/Ryujinx/teams/developers)
|
8. Wait for feedback or approval of your changes from the core development team
|
||||||
- Details about the pull request [review procedure](docs/workflow/ci/pr-guide.md).
|
- Details about the pull request [review procedure](docs/workflow/ci/pr-guide.md).
|
||||||
9. When the team members have signed off, and all checks are green, your PR will be merged.
|
9. When the team members have signed off, and all checks are green, your PR will be merged.
|
||||||
- The next official build will automatically include your change.
|
- The next official build will automatically include your change.
|
||||||
@@ -99,7 +90,7 @@ We use and recommend the following workflow:
|
|||||||
|
|
||||||
### Good First Issues
|
### Good First Issues
|
||||||
|
|
||||||
The team marks the most straightforward issues as [good first issues](https://github.com/Ryujinx/Ryujinx/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22). This set of issues is the place to start if you are interested in contributing but new to the codebase.
|
The team marks the most straightforward issues as [good first issues](https://github.com/GreemDev/Ryujinx/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22). This set of issues is the place to start if you are interested in contributing but new to the codebase.
|
||||||
|
|
||||||
### Commit Messages
|
### Commit Messages
|
||||||
|
|
||||||
@@ -122,7 +113,7 @@ Also do your best to factor commits appropriately, not too large with unrelated
|
|||||||
|
|
||||||
### PR - CI Process
|
### PR - CI Process
|
||||||
|
|
||||||
The [Ryujinx continuous integration](https://github.com/Ryujinx/Ryujinx/actions) (CI) system will automatically perform the required builds and run tests (including the ones you are expected to run) for PRs. Builds and test runs must be clean or have bugs properly filed against flaky/unexpected failures that are unrelated to your change.
|
The [Ryujinx continuous integration](https://github.com/GreemDev/Ryujinx/actions) (CI) system will automatically perform the required builds and run tests (including the ones you are expected to run) for PRs. Builds and test runs must be clean or have bugs properly filed against flaky/unexpected failures that are unrelated to your change.
|
||||||
|
|
||||||
If the CI build fails for any reason, the PR actions tab should be consulted for further information on the failure. There are a few usual suspects for such a failure:
|
If the CI build fails for any reason, the PR actions tab should be consulted for further information on the failure. There are a few usual suspects for such a failure:
|
||||||
* `dotnet format` has not been run on the PR and has outstanding stylistic issues.
|
* `dotnet format` has not been run on the PR and has outstanding stylistic issues.
|
||||||
@@ -143,5 +134,5 @@ Ryujinx uses some implementations and frameworks from other projects. The follow
|
|||||||
|
|
||||||
- The license of the file is [permissive](https://en.wikipedia.org/wiki/Permissive_free_software_licence).
|
- The license of the file is [permissive](https://en.wikipedia.org/wiki/Permissive_free_software_licence).
|
||||||
- The license of the file is left in-tact.
|
- The license of the file is left in-tact.
|
||||||
- The contribution is correctly attributed in the [3rd party notices](https://github.com/Ryujinx/Ryujinx/blob/master/distribution/legal/THIRDPARTY.md) file in the repository, as needed.
|
- The contribution is correctly attributed in the [3rd party notices](https://github.com/GreemDev/Ryujinx/blob/master/distribution/legal/THIRDPARTY.md) file in the repository, as needed.
|
||||||
|
|
||||||
|
|||||||
38
README.md
38
README.md
@@ -6,6 +6,14 @@
|
|||||||
<br>
|
<br>
|
||||||
<sub><sup><b>(REE-YOU-JINX)</b></sup></sub>
|
<sub><sup><b>(REE-YOU-JINX)</b></sup></sub>
|
||||||
<br>
|
<br>
|
||||||
|
<a href="https://github.com/GreemDev/Ryujinx/actions/workflows/release.yml">
|
||||||
|
<img src="https://github.com/GreemDev/Ryujinx/actions/workflows/release.yml/badge.svg"
|
||||||
|
alt="">
|
||||||
|
</a>
|
||||||
|
<a href="https://github.com/GreemDev/Ryujinx/releases/latest">
|
||||||
|
<img src="https://img.shields.io/github/v/release/GreemDev/Ryujinx"
|
||||||
|
alt="Latest Release">
|
||||||
|
</a>
|
||||||
</h1>
|
</h1>
|
||||||
|
|
||||||
<p align="center">
|
<p align="center">
|
||||||
@@ -17,25 +25,22 @@
|
|||||||
</p>
|
</p>
|
||||||
<p align="center">
|
<p align="center">
|
||||||
On October 1st 2024, Ryujinx was discontinued as the creator was forced to abandon the project.
|
On October 1st 2024, Ryujinx was discontinued as the creator was forced to abandon the project.
|
||||||
This fork is intended to be a direct continuation for existing Ryujinx users.
|
<br>
|
||||||
Guides and documentation will not be provided at this time, though you can find the old ones on the Internet Archive.
|
This fork is intended to be a QoL uplift for existing Ryujinx users.
|
||||||
|
<br>
|
||||||
|
This is not a Ryujinx revival project. This is not a Phoenix project.
|
||||||
|
<br>
|
||||||
|
Guides and documentation can be found on the <a href="https://github.com/GreemDev/Ryujinx/wiki">Wiki tab</a>.
|
||||||
</p>
|
</p>
|
||||||
<p align="center">
|
<p align="center">
|
||||||
If you would like a version more true to original Ryujinx, check out <a href="https://github.com/ryujinx-mirror/ryujinx">ryujinx-mirror</a>.
|
If you would like a version more preservative fork of Ryujinx, check out <a href="https://github.com/ryujinx-mirror/ryujinx">ryujinx-mirror</a>.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<p align="center">
|
<p align="center">
|
||||||
<a href="https://github.com/GreemDev/Ryujinx/actions/workflows/release.yml">
|
Click below to join the Discord:
|
||||||
<img src="https://github.com/GreemDev/Ryujinx/actions/workflows/release.yml/badge.svg"
|
<br>
|
||||||
alt="">
|
|
||||||
</a>
|
|
||||||
<a href="https://crwd.in/ryujinx">
|
|
||||||
<img src="https://badges.crowdin.net/ryujinx/localized.svg"
|
|
||||||
alt="">
|
|
||||||
</a>
|
|
||||||
<a href="https://discord.gg/dHPrkBkkyA">
|
<a href="https://discord.gg/dHPrkBkkyA">
|
||||||
<img src="https://img.shields.io/discord/1294443224030511104?color=5865F2&label=Ryujinx&logo=discord&logoColor=white"
|
<img src="https://img.shields.io/discord/1294443224030511104?color=5865F2&label=Ryubing&logo=discord&logoColor=white" alt="Discord">
|
||||||
alt="Discord">
|
|
||||||
</a>
|
</a>
|
||||||
<br>
|
<br>
|
||||||
<br>
|
<br>
|
||||||
@@ -56,17 +61,20 @@ Use the search function to see if a game has been tested already!
|
|||||||
To run this emulator, your PC must be equipped with at least 8GiB of RAM;
|
To run this emulator, your PC must be equipped with at least 8GiB of RAM;
|
||||||
failing to meet this requirement may result in a poor gameplay experience or unexpected crashes.
|
failing to meet this requirement may result in a poor gameplay experience or unexpected crashes.
|
||||||
|
|
||||||
## Latest build
|
## Latest release
|
||||||
|
|
||||||
These builds are compiled automatically for each commit on the master branch.
|
Releases are compiled automatically for each commit on the master branch.
|
||||||
While we strive to ensure optimal stability and performance prior to pushing an update, our automated builds **may be unstable or completely broken**.
|
While we strive to ensure optimal stability and performance prior to pushing an update, our automated builds **may be unstable or completely broken**.
|
||||||
|
|
||||||
|
You can find the latest release [here](https://github.com/GreemDev/Ryujinx/releases/latest).
|
||||||
|
|
||||||
## Documentation
|
## Documentation
|
||||||
|
|
||||||
If you are planning to contribute or just want to learn more about this project please read through our [documentation](docs/README.md).
|
If you are planning to contribute or just want to learn more about this project please read through our [documentation](docs/README.md).
|
||||||
|
|
||||||
## Building
|
## Building
|
||||||
|
|
||||||
|
Building the project is for advanced users.
|
||||||
If you wish to build the emulator yourself, follow these steps:
|
If you wish to build the emulator yourself, follow these steps:
|
||||||
|
|
||||||
### Step 1
|
### Step 1
|
||||||
|
|||||||
15
Ryujinx.sln
15
Ryujinx.sln
@@ -29,12 +29,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Graphics.Nvdec", "s
|
|||||||
EndProject
|
EndProject
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Audio", "src\Ryujinx.Audio\Ryujinx.Audio.csproj", "{806ACF6D-90B0-45D0-A1AC-5F220F3B3985}"
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Audio", "src\Ryujinx.Audio\Ryujinx.Audio.csproj", "{806ACF6D-90B0-45D0-A1AC-5F220F3B3985}"
|
||||||
EndProject
|
EndProject
|
||||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{36F870C1-3E5F-485F-B426-F0645AF78751}"
|
|
||||||
ProjectSection(SolutionItems) = preProject
|
|
||||||
.editorconfig = .editorconfig
|
|
||||||
Directory.Packages.props = Directory.Packages.props
|
|
||||||
EndProjectSection
|
|
||||||
EndProject
|
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Memory", "src\Ryujinx.Memory\Ryujinx.Memory.csproj", "{A5E6C691-9E22-4263-8F40-42F002CE66BE}"
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Memory", "src\Ryujinx.Memory\Ryujinx.Memory.csproj", "{A5E6C691-9E22-4263-8F40-42F002CE66BE}"
|
||||||
EndProject
|
EndProject
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Tests.Memory", "src\Ryujinx.Tests.Memory\Ryujinx.Tests.Memory.csproj", "{D1CC5322-7325-4F6B-9625-194B30BE1296}"
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Tests.Memory", "src\Ryujinx.Tests.Memory\Ryujinx.Tests.Memory.csproj", "{D1CC5322-7325-4F6B-9625-194B30BE1296}"
|
||||||
@@ -87,6 +81,15 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Horizon.Kernel.Gene
|
|||||||
EndProject
|
EndProject
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.HLE.Generators", "src\Ryujinx.HLE.Generators\Ryujinx.HLE.Generators.csproj", "{B575BCDE-2FD8-4A5D-8756-31CDD7FE81F0}"
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.HLE.Generators", "src\Ryujinx.HLE.Generators\Ryujinx.HLE.Generators.csproj", "{B575BCDE-2FD8-4A5D-8756-31CDD7FE81F0}"
|
||||||
EndProject
|
EndProject
|
||||||
|
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{36F870C1-3E5F-485F-B426-F0645AF78751}"
|
||||||
|
ProjectSection(SolutionItems) = preProject
|
||||||
|
.editorconfig = .editorconfig
|
||||||
|
Directory.Packages.props = Directory.Packages.props
|
||||||
|
.github/workflows/release.yml = .github/workflows/release.yml
|
||||||
|
.github/workflows/canary.yml = .github/workflows/canary.yml
|
||||||
|
.github/workflows/build.yml = .github/workflows/build.yml
|
||||||
|
EndProjectSection
|
||||||
|
EndProject
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
Debug|Any CPU = Debug|Any CPU
|
Debug|Any CPU = Debug|Any CPU
|
||||||
|
|||||||
Binary file not shown.
BIN
docs/shell.png
BIN
docs/shell.png
Binary file not shown.
|
Before Width: | Height: | Size: 905 KiB After Width: | Height: | Size: 1.4 MiB |
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
## Contributing Rules
|
## Contributing Rules
|
||||||
|
|
||||||
All contributions to Ryujinx/Ryujinx repository are made via pull requests (PRs) rather than through direct commits. The pull requests are reviewed and merged by the maintainers after a review and at least two approvals from the core development team.
|
All contributions to GreemDev/Ryujinx repository are made via pull requests (PRs) rather than through direct commits. The pull requests are reviewed and merged by the maintainers after a review and at least two approvals from the core development team.
|
||||||
|
|
||||||
To merge pull requests, you must have write permissions in the repository.
|
To merge pull requests, you must have write permissions in the repository.
|
||||||
|
|
||||||
@@ -24,7 +24,7 @@ If during the code review process a merge conflict occurs, the PR author is resp
|
|||||||
|
|
||||||
## Pull Request Builds
|
## Pull Request Builds
|
||||||
|
|
||||||
When submitting a PR to the `Ryujinx/Ryujinx` repository, various builds will run validating many areas to ensure we keep developer productivity and product quality high. These various workflows can be tracked in the [Actions](https://github.com/Ryujinx/Ryujinx/actions) tab of the repository. If the job continues to completion, the build artifacts will be uploaded and posted as a comment in the PR discussion.
|
When submitting a PR to the `GreemDev/Ryujinx` repository, various builds will run validating many areas to ensure we keep developer productivity and product quality high. These various workflows can be tracked in the [Actions](https://github.com/GreemDev/Ryujinx/actions) tab of the repository. If the job continues to completion, the build artifacts will be uploaded and posted as a comment in the PR discussion.
|
||||||
|
|
||||||
## Review Turnaround Times
|
## Review Turnaround Times
|
||||||
|
|
||||||
@@ -42,7 +42,7 @@ Anyone with write access can merge a pull request manually when the following co
|
|||||||
|
|
||||||
* The PR has been approved by two reviewers and any other objections are addressed.
|
* The PR has been approved by two reviewers and any other objections are addressed.
|
||||||
* You can request follow up reviews from the original reviewers if they requested changes.
|
* You can request follow up reviews from the original reviewers if they requested changes.
|
||||||
* The PR successfully builds and passes all tests in the Continuous Integration (CI) system. In case of failures, refer to the [Actions](https://github.com/Ryujinx/Ryujinx/actions) tab of your PR.
|
* The PR successfully builds and passes all tests in the Continuous Integration (CI) system. In case of failures, refer to the [Actions](https://github.com/GreemDev/Ryujinx/actions) tab of your PR.
|
||||||
|
|
||||||
Typically, PRs are merged as one commit (squash merges). It creates a simpler history than a Merge Commit. "Special circumstances" are rare, and typically mean that there are a series of cleanly separated changes that will be too hard to understand if squashed together, or for some reason we want to preserve the ability to dissect them.
|
Typically, PRs are merged as one commit (squash merges). It creates a simpler history than a Merge Commit. "Special circumstances" are rare, and typically mean that there are a series of cleanly separated changes that will be too hard to understand if squashed together, or for some reason we want to preserve the ability to dissect them.
|
||||||
|
|
||||||
|
|||||||
@@ -855,6 +855,7 @@ namespace ARMeilleure.Translation.PTC
|
|||||||
Thread thread = new(TranslateFuncs)
|
Thread thread = new(TranslateFuncs)
|
||||||
{
|
{
|
||||||
IsBackground = true,
|
IsBackground = true,
|
||||||
|
Name = "Ptc.TranslateThread." + i
|
||||||
};
|
};
|
||||||
|
|
||||||
threads.Add(thread);
|
threads.Add(thread);
|
||||||
@@ -885,6 +886,7 @@ namespace ARMeilleure.Translation.PTC
|
|||||||
Thread preSaveThread = new(PreSave)
|
Thread preSaveThread = new(PreSave)
|
||||||
{
|
{
|
||||||
IsBackground = true,
|
IsBackground = true,
|
||||||
|
Name = "Ptc.DiskWriter"
|
||||||
};
|
};
|
||||||
preSaveThread.Start();
|
preSaveThread.Start();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ namespace Ryujinx.Audio.Backends.OpenAL
|
|||||||
|
|
||||||
public OpenALHardwareDeviceDriver()
|
public OpenALHardwareDeviceDriver()
|
||||||
{
|
{
|
||||||
_device = ALC.OpenDevice("");
|
_device = ALC.OpenDevice(string.Empty);
|
||||||
_context = ALC.CreateContext(_device, new ALContextAttributes());
|
_context = ALC.CreateContext(_device, new ALContextAttributes());
|
||||||
_updateRequiredEvent = new ManualResetEvent(false);
|
_updateRequiredEvent = new ManualResetEvent(false);
|
||||||
_pauseEvent = new ManualResetEvent(true);
|
_pauseEvent = new ManualResetEvent(true);
|
||||||
|
|||||||
@@ -81,7 +81,7 @@ namespace Ryujinx.Audio.Renderer.Dsp
|
|||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
private static short GetCoefficientAtIndex(ReadOnlySpan<short> coefficients, int index)
|
private static short GetCoefficientAtIndex(ReadOnlySpan<short> coefficients, int index)
|
||||||
{
|
{
|
||||||
if ((uint)index > (uint)coefficients.Length)
|
if ((uint)index >= (uint)coefficients.Length)
|
||||||
{
|
{
|
||||||
Logger.Error?.Print(LogClass.AudioRenderer, $"Out of bound read for coefficient at index {index}");
|
Logger.Error?.Print(LogClass.AudioRenderer, $"Out of bound read for coefficient at index {index}");
|
||||||
|
|
||||||
|
|||||||
@@ -119,7 +119,7 @@ namespace Ryujinx.Common.Configuration
|
|||||||
|
|
||||||
private static string SetUpLogsDir()
|
private static string SetUpLogsDir()
|
||||||
{
|
{
|
||||||
string logDir = "";
|
string logDir = string.Empty;
|
||||||
|
|
||||||
if (Mode == LaunchMode.Portable)
|
if (Mode == LaunchMode.Portable)
|
||||||
{
|
{
|
||||||
@@ -148,7 +148,7 @@ namespace Ryujinx.Common.Configuration
|
|||||||
catch
|
catch
|
||||||
{
|
{
|
||||||
Logger.Warning?.Print(LogClass.Application, $"Logging directory could not be created '{logDir}'");
|
Logger.Warning?.Print(LogClass.Application, $"Logging directory could not be created '{logDir}'");
|
||||||
logDir = "";
|
logDir = string.Empty;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (string.IsNullOrEmpty(logDir))
|
if (string.IsNullOrEmpty(logDir))
|
||||||
@@ -179,7 +179,7 @@ namespace Ryujinx.Common.Configuration
|
|||||||
catch
|
catch
|
||||||
{
|
{
|
||||||
Logger.Warning?.Print(LogClass.Application, $"Logging directory could not be created '{logDir}'");
|
Logger.Warning?.Print(LogClass.Application, $"Logging directory could not be created '{logDir}'");
|
||||||
logDir = "";
|
logDir = string.Empty;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (string.IsNullOrEmpty(logDir))
|
if (string.IsNullOrEmpty(logDir))
|
||||||
|
|||||||
@@ -121,8 +121,8 @@ namespace Ryujinx.Common.GraphicsDriver
|
|||||||
};
|
};
|
||||||
application.AppName.Set("Ryujinx.exe");
|
application.AppName.Set("Ryujinx.exe");
|
||||||
application.UserFriendlyName.Set("Ryujinx");
|
application.UserFriendlyName.Set("Ryujinx");
|
||||||
application.Launcher.Set("");
|
application.Launcher.Set(string.Empty);
|
||||||
application.FileInFolder.Set("");
|
application.FileInFolder.Set(string.Empty);
|
||||||
|
|
||||||
Check(NvAPI_DRS_CreateApplication(handle, profileHandle, ref application));
|
Check(NvAPI_DRS_CreateApplication(handle, profileHandle, ref application));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -72,5 +72,6 @@ namespace Ryujinx.Common.Logging
|
|||||||
TamperMachine,
|
TamperMachine,
|
||||||
UI,
|
UI,
|
||||||
Vic,
|
Vic,
|
||||||
|
XCIFileTrimmer
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ namespace Ryujinx.Common.Logging
|
|||||||
{
|
{
|
||||||
if (_enabledClasses[(int)logClass])
|
if (_enabledClasses[(int)logClass])
|
||||||
{
|
{
|
||||||
Updated?.Invoke(null, new LogEventArgs(Level, _time.Elapsed, Thread.CurrentThread.Name, FormatMessage(logClass, "", message)));
|
Updated?.Invoke(null, new LogEventArgs(Level, _time.Elapsed, Thread.CurrentThread.Name, FormatMessage(logClass, string.Empty, message)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
30
src/Ryujinx.Common/Logging/XCIFileTrimmerLog.cs
Normal file
30
src/Ryujinx.Common/Logging/XCIFileTrimmerLog.cs
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
using Ryujinx.Common.Utilities;
|
||||||
|
|
||||||
|
namespace Ryujinx.Common.Logging
|
||||||
|
{
|
||||||
|
public class XCIFileTrimmerLog : XCIFileTrimmer.ILog
|
||||||
|
{
|
||||||
|
public virtual void Progress(long current, long total, string text, bool complete)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Write(XCIFileTrimmer.LogType logType, string text)
|
||||||
|
{
|
||||||
|
switch (logType)
|
||||||
|
{
|
||||||
|
case XCIFileTrimmer.LogType.Info:
|
||||||
|
Logger.Notice.Print(LogClass.XCIFileTrimmer, text);
|
||||||
|
break;
|
||||||
|
case XCIFileTrimmer.LogType.Warn:
|
||||||
|
Logger.Warning?.Print(LogClass.XCIFileTrimmer, text);
|
||||||
|
break;
|
||||||
|
case XCIFileTrimmer.LogType.Error:
|
||||||
|
Logger.Error?.Print(LogClass.XCIFileTrimmer, text);
|
||||||
|
break;
|
||||||
|
case XCIFileTrimmer.LogType.Progress:
|
||||||
|
Logger.Info?.Print(LogClass.XCIFileTrimmer, text);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -5,7 +5,9 @@ namespace Ryujinx.Common
|
|||||||
// DO NOT EDIT, filled by CI
|
// DO NOT EDIT, filled by CI
|
||||||
public static class ReleaseInformation
|
public static class ReleaseInformation
|
||||||
{
|
{
|
||||||
private const string FlatHubChannelOwner = "flathub";
|
private const string FlatHubChannel = "flathub";
|
||||||
|
private const string CanaryChannel = "canary";
|
||||||
|
private const string ReleaseChannel = "release";
|
||||||
|
|
||||||
private const string BuildVersion = "%%RYUJINX_BUILD_VERSION%%";
|
private const string BuildVersion = "%%RYUJINX_BUILD_VERSION%%";
|
||||||
public const string BuildGitHash = "%%RYUJINX_BUILD_GIT_HASH%%";
|
public const string BuildGitHash = "%%RYUJINX_BUILD_GIT_HASH%%";
|
||||||
@@ -24,7 +26,11 @@ namespace Ryujinx.Common
|
|||||||
!ReleaseChannelRepo.StartsWith("%%") &&
|
!ReleaseChannelRepo.StartsWith("%%") &&
|
||||||
!ConfigFileName.StartsWith("%%");
|
!ConfigFileName.StartsWith("%%");
|
||||||
|
|
||||||
public static bool IsFlatHubBuild => IsValid && ReleaseChannelOwner.Equals(FlatHubChannelOwner);
|
public static bool IsFlatHubBuild => IsValid && ReleaseChannelOwner.Equals(FlatHubChannel);
|
||||||
|
|
||||||
|
public static bool IsCanaryBuild => IsValid && ReleaseChannelOwner.Equals(CanaryChannel);
|
||||||
|
|
||||||
|
public static bool IsReleaseBuild => IsValid && ReleaseChannelOwner.Equals(ReleaseChannel);
|
||||||
|
|
||||||
public static string Version => IsValid ? BuildVersion : Assembly.GetEntryAssembly()!.GetCustomAttribute<AssemblyInformationalVersionAttribute>()?.InformationalVersion;
|
public static string Version => IsValid ? BuildVersion : Assembly.GetEntryAssembly()!.GetCustomAttribute<AssemblyInformationalVersionAttribute>()?.InformationalVersion;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,6 +10,7 @@
|
|||||||
<PackageReference Include="Microsoft.IO.RecyclableMemoryStream" />
|
<PackageReference Include="Microsoft.IO.RecyclableMemoryStream" />
|
||||||
<PackageReference Include="MsgPack.Cli" />
|
<PackageReference Include="MsgPack.Cli" />
|
||||||
<PackageReference Include="System.Management" />
|
<PackageReference Include="System.Management" />
|
||||||
|
<PackageReference Include="Humanizer" />
|
||||||
<PackageReference Include="Gommon" />
|
<PackageReference Include="Gommon" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
|||||||
@@ -17,29 +17,19 @@ namespace Ryujinx.Common.Utilities
|
|||||||
/// It is REQUIRED for you to save returned options statically or as a part of static serializer context
|
/// It is REQUIRED for you to save returned options statically or as a part of static serializer context
|
||||||
/// in order to avoid performance issues. You can safely modify returned options for your case before storing.
|
/// in order to avoid performance issues. You can safely modify returned options for your case before storing.
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
public static JsonSerializerOptions GetDefaultSerializerOptions(bool indented = true)
|
public static JsonSerializerOptions GetDefaultSerializerOptions(bool indented = true) =>
|
||||||
{
|
new()
|
||||||
JsonSerializerOptions options = new()
|
|
||||||
{
|
{
|
||||||
DictionaryKeyPolicy = _snakeCasePolicy,
|
DictionaryKeyPolicy = _snakeCasePolicy,
|
||||||
PropertyNamingPolicy = _snakeCasePolicy,
|
PropertyNamingPolicy = _snakeCasePolicy,
|
||||||
WriteIndented = indented,
|
WriteIndented = indented,
|
||||||
AllowTrailingCommas = true,
|
AllowTrailingCommas = true,
|
||||||
ReadCommentHandling = JsonCommentHandling.Skip,
|
ReadCommentHandling = JsonCommentHandling.Skip
|
||||||
};
|
};
|
||||||
|
|
||||||
return options;
|
public static string Serialize<T>(T value, JsonTypeInfo<T> typeInfo) => JsonSerializer.Serialize(value, typeInfo);
|
||||||
}
|
|
||||||
|
|
||||||
public static string Serialize<T>(T value, JsonTypeInfo<T> typeInfo)
|
public static T Deserialize<T>(string value, JsonTypeInfo<T> typeInfo) => JsonSerializer.Deserialize(value, typeInfo);
|
||||||
{
|
|
||||||
return JsonSerializer.Serialize(value, typeInfo);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static T Deserialize<T>(string value, JsonTypeInfo<T> typeInfo)
|
|
||||||
{
|
|
||||||
return JsonSerializer.Deserialize(value, typeInfo);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void SerializeToFile<T>(string filePath, T value, JsonTypeInfo<T> typeInfo)
|
public static void SerializeToFile<T>(string filePath, T value, JsonTypeInfo<T> typeInfo)
|
||||||
{
|
{
|
||||||
@@ -53,10 +43,7 @@ namespace Ryujinx.Common.Utilities
|
|||||||
return JsonSerializer.Deserialize(file, typeInfo);
|
return JsonSerializer.Deserialize(file, typeInfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void SerializeToStream<T>(Stream stream, T value, JsonTypeInfo<T> typeInfo)
|
public static void SerializeToStream<T>(Stream stream, T value, JsonTypeInfo<T> typeInfo) => JsonSerializer.Serialize(stream, value, typeInfo);
|
||||||
{
|
|
||||||
JsonSerializer.Serialize(stream, value, typeInfo);
|
|
||||||
}
|
|
||||||
|
|
||||||
private class SnakeCaseNamingPolicy : JsonNamingPolicy
|
private class SnakeCaseNamingPolicy : JsonNamingPolicy
|
||||||
{
|
{
|
||||||
|
|||||||
524
src/Ryujinx.Common/Utilities/XCIFileTrimmer.cs
Normal file
524
src/Ryujinx.Common/Utilities/XCIFileTrimmer.cs
Normal file
@@ -0,0 +1,524 @@
|
|||||||
|
// Uncomment the line below to ensure XCIFileTrimmer does not modify files
|
||||||
|
//#define XCI_TRIMMER_READ_ONLY_MODE
|
||||||
|
|
||||||
|
using Gommon;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading;
|
||||||
|
|
||||||
|
namespace Ryujinx.Common.Utilities
|
||||||
|
{
|
||||||
|
public sealed class XCIFileTrimmer
|
||||||
|
{
|
||||||
|
private const long BytesInAMegabyte = 1024 * 1024;
|
||||||
|
private const int BufferSize = 8 * (int)BytesInAMegabyte;
|
||||||
|
|
||||||
|
private const long CartSizeMBinFormattedGB = 952;
|
||||||
|
private const int CartKeyAreaSize = 0x1000;
|
||||||
|
private const byte PaddingByte = 0xFF;
|
||||||
|
private const int HeaderFilePos = 0x100;
|
||||||
|
private const int CartSizeFilePos = 0x10D;
|
||||||
|
private const int DataSizeFilePos = 0x118;
|
||||||
|
private const string HeaderMagicValue = "HEAD";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Cartridge Sizes (ByteIdentifier, SizeInGB)
|
||||||
|
/// </summary>
|
||||||
|
private static readonly Dictionary<byte, long> _cartSizesGB = new()
|
||||||
|
{
|
||||||
|
{ 0xFA, 1 },
|
||||||
|
{ 0xF8, 2 },
|
||||||
|
{ 0xF0, 4 },
|
||||||
|
{ 0xE0, 8 },
|
||||||
|
{ 0xE1, 16 },
|
||||||
|
{ 0xE2, 32 }
|
||||||
|
};
|
||||||
|
|
||||||
|
private static long RecordsToByte(long records)
|
||||||
|
{
|
||||||
|
return 512 + (records * 512);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool CanTrim(string filename, ILog log = null)
|
||||||
|
{
|
||||||
|
if (Path.GetExtension(filename).Equals(".XCI", StringComparison.InvariantCultureIgnoreCase))
|
||||||
|
{
|
||||||
|
var trimmer = new XCIFileTrimmer(filename, log);
|
||||||
|
return trimmer.CanBeTrimmed;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool CanUntrim(string filename, ILog log = null)
|
||||||
|
{
|
||||||
|
if (Path.GetExtension(filename).Equals(".XCI", StringComparison.InvariantCultureIgnoreCase))
|
||||||
|
{
|
||||||
|
var trimmer = new XCIFileTrimmer(filename, log);
|
||||||
|
return trimmer.CanBeUntrimmed;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private ILog _log;
|
||||||
|
private string _filename;
|
||||||
|
private FileStream _fileStream;
|
||||||
|
private BinaryReader _binaryReader;
|
||||||
|
private long _offsetB, _dataSizeB, _cartSizeB, _fileSizeB;
|
||||||
|
private bool _fileOK = true;
|
||||||
|
private bool _freeSpaceChecked = false;
|
||||||
|
private bool _freeSpaceValid = false;
|
||||||
|
|
||||||
|
public enum OperationOutcome
|
||||||
|
{
|
||||||
|
Undetermined,
|
||||||
|
InvalidXCIFile,
|
||||||
|
NoTrimNecessary,
|
||||||
|
NoUntrimPossible,
|
||||||
|
FreeSpaceCheckFailed,
|
||||||
|
FileIOWriteError,
|
||||||
|
ReadOnlyFileCannotFix,
|
||||||
|
FileSizeChanged,
|
||||||
|
Successful,
|
||||||
|
Cancelled
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum LogType
|
||||||
|
{
|
||||||
|
Info,
|
||||||
|
Warn,
|
||||||
|
Error,
|
||||||
|
Progress
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface ILog
|
||||||
|
{
|
||||||
|
public void Write(LogType logType, string text);
|
||||||
|
public void Progress(long current, long total, string text, bool complete);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool FileOK => _fileOK;
|
||||||
|
public bool Trimmed => _fileOK && FileSizeB < UntrimmedFileSizeB;
|
||||||
|
public bool ContainsKeyArea => _offsetB != 0;
|
||||||
|
public bool CanBeTrimmed => _fileOK && FileSizeB > TrimmedFileSizeB;
|
||||||
|
public bool CanBeUntrimmed => _fileOK && FileSizeB < UntrimmedFileSizeB;
|
||||||
|
public bool FreeSpaceChecked => _fileOK && _freeSpaceChecked;
|
||||||
|
public bool FreeSpaceValid => _fileOK && _freeSpaceValid;
|
||||||
|
public long DataSizeB => _dataSizeB;
|
||||||
|
public long CartSizeB => _cartSizeB;
|
||||||
|
public long FileSizeB => _fileSizeB;
|
||||||
|
public long DiskSpaceSavedB => CartSizeB - FileSizeB;
|
||||||
|
public long DiskSpaceSavingsB => CartSizeB - DataSizeB;
|
||||||
|
public long TrimmedFileSizeB => _offsetB + _dataSizeB;
|
||||||
|
public long UntrimmedFileSizeB => _offsetB + _cartSizeB;
|
||||||
|
|
||||||
|
public ILog Log
|
||||||
|
{
|
||||||
|
get => _log;
|
||||||
|
set => _log = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String Filename
|
||||||
|
{
|
||||||
|
get => _filename;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
_filename = value;
|
||||||
|
Reset();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public long Pos
|
||||||
|
{
|
||||||
|
get => _fileStream.Position;
|
||||||
|
set => _fileStream.Position = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public XCIFileTrimmer(string path, ILog log = null)
|
||||||
|
{
|
||||||
|
Log = log;
|
||||||
|
Filename = path;
|
||||||
|
ReadHeader();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void CheckFreeSpace(CancellationToken? cancelToken = null)
|
||||||
|
{
|
||||||
|
if (FreeSpaceChecked)
|
||||||
|
return;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (CanBeTrimmed)
|
||||||
|
{
|
||||||
|
_freeSpaceValid = false;
|
||||||
|
|
||||||
|
OpenReaders();
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Pos = TrimmedFileSizeB;
|
||||||
|
bool freeSpaceValid = true;
|
||||||
|
long readSizeB = FileSizeB - TrimmedFileSizeB;
|
||||||
|
|
||||||
|
Stopwatch timedSw = Lambda.Timed(() =>
|
||||||
|
{
|
||||||
|
freeSpaceValid = CheckPadding(readSizeB, cancelToken);
|
||||||
|
});
|
||||||
|
|
||||||
|
if (timedSw.Elapsed.TotalSeconds > 0)
|
||||||
|
{
|
||||||
|
Log?.Write(LogType.Info, $"Checked at {readSizeB / (double)XCIFileTrimmer.BytesInAMegabyte / timedSw.Elapsed.TotalSeconds:N} Mb/sec");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (freeSpaceValid)
|
||||||
|
Log?.Write(LogType.Info, "Free space is valid");
|
||||||
|
|
||||||
|
_freeSpaceValid = freeSpaceValid;
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
CloseReaders();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Log?.Write(LogType.Warn, "There is no free space to check.");
|
||||||
|
_freeSpaceValid = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
_freeSpaceChecked = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool CheckPadding(long readSizeB, CancellationToken? cancelToken = null)
|
||||||
|
{
|
||||||
|
long maxReads = readSizeB / XCIFileTrimmer.BufferSize;
|
||||||
|
long read = 0;
|
||||||
|
var buffer = new byte[BufferSize];
|
||||||
|
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
if (cancelToken.HasValue && cancelToken.Value.IsCancellationRequested)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
int bytes = _fileStream.Read(buffer, 0, XCIFileTrimmer.BufferSize);
|
||||||
|
if (bytes == 0)
|
||||||
|
break;
|
||||||
|
|
||||||
|
Log?.Progress(read, maxReads, "Verifying file can be trimmed", false);
|
||||||
|
if (buffer.Take(bytes).AsParallel().Any(b => b != XCIFileTrimmer.PaddingByte))
|
||||||
|
{
|
||||||
|
Log?.Write(LogType.Warn, "Free space is NOT valid");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
read++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Reset()
|
||||||
|
{
|
||||||
|
_freeSpaceChecked = false;
|
||||||
|
_freeSpaceValid = false;
|
||||||
|
ReadHeader();
|
||||||
|
}
|
||||||
|
|
||||||
|
public OperationOutcome Trim(CancellationToken? cancelToken = null)
|
||||||
|
{
|
||||||
|
if (!FileOK)
|
||||||
|
{
|
||||||
|
return OperationOutcome.InvalidXCIFile;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!CanBeTrimmed)
|
||||||
|
{
|
||||||
|
return OperationOutcome.NoTrimNecessary;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!FreeSpaceChecked)
|
||||||
|
{
|
||||||
|
CheckFreeSpace(cancelToken);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!FreeSpaceValid)
|
||||||
|
{
|
||||||
|
if (cancelToken.HasValue && cancelToken.Value.IsCancellationRequested)
|
||||||
|
{
|
||||||
|
return OperationOutcome.Cancelled;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return OperationOutcome.FreeSpaceCheckFailed;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Log?.Write(LogType.Info, "Trimming...");
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var info = new FileInfo(Filename);
|
||||||
|
if ((info.Attributes & FileAttributes.ReadOnly) == FileAttributes.ReadOnly)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Log?.Write(LogType.Info, "Attempting to remove ReadOnly attribute");
|
||||||
|
File.SetAttributes(Filename, info.Attributes & ~FileAttributes.ReadOnly);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Log?.Write(LogType.Error, e.ToString());
|
||||||
|
return OperationOutcome.ReadOnlyFileCannotFix;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (info.Length != FileSizeB)
|
||||||
|
{
|
||||||
|
Log?.Write(LogType.Error, "File size has changed, cannot safely trim.");
|
||||||
|
return OperationOutcome.FileSizeChanged;
|
||||||
|
}
|
||||||
|
|
||||||
|
var outfileStream = new FileStream(_filename, FileMode.Open, FileAccess.Write, FileShare.Write);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
|
||||||
|
#if !XCI_TRIMMER_READ_ONLY_MODE
|
||||||
|
outfileStream.SetLength(TrimmedFileSizeB);
|
||||||
|
#endif
|
||||||
|
return OperationOutcome.Successful;
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
outfileStream.Close();
|
||||||
|
Reset();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Log?.Write(LogType.Error, e.ToString());
|
||||||
|
return OperationOutcome.FileIOWriteError;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public OperationOutcome Untrim(CancellationToken? cancelToken = null)
|
||||||
|
{
|
||||||
|
if (!FileOK)
|
||||||
|
{
|
||||||
|
return OperationOutcome.InvalidXCIFile;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!CanBeUntrimmed)
|
||||||
|
{
|
||||||
|
return OperationOutcome.NoUntrimPossible;
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Log?.Write(LogType.Info, "Untrimming...");
|
||||||
|
|
||||||
|
var info = new FileInfo(Filename);
|
||||||
|
if ((info.Attributes & FileAttributes.ReadOnly) == FileAttributes.ReadOnly)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Log?.Write(LogType.Info, "Attempting to remove ReadOnly attribute");
|
||||||
|
File.SetAttributes(Filename, info.Attributes & ~FileAttributes.ReadOnly);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Log?.Write(LogType.Error, e.ToString());
|
||||||
|
return OperationOutcome.ReadOnlyFileCannotFix;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (info.Length != FileSizeB)
|
||||||
|
{
|
||||||
|
Log?.Write(LogType.Error, "File size has changed, cannot safely untrim.");
|
||||||
|
return OperationOutcome.FileSizeChanged;
|
||||||
|
}
|
||||||
|
|
||||||
|
var outfileStream = new FileStream(_filename, FileMode.Append, FileAccess.Write, FileShare.Write);
|
||||||
|
long bytesToWriteB = UntrimmedFileSizeB - FileSizeB;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Stopwatch timedSw = Lambda.Timed(() =>
|
||||||
|
{
|
||||||
|
WritePadding(outfileStream, bytesToWriteB, cancelToken);
|
||||||
|
});
|
||||||
|
|
||||||
|
if (timedSw.Elapsed.TotalSeconds > 0)
|
||||||
|
{
|
||||||
|
Log?.Write(LogType.Info, $"Wrote at {bytesToWriteB / (double)XCIFileTrimmer.BytesInAMegabyte / timedSw.Elapsed.TotalSeconds:N} Mb/sec");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cancelToken.HasValue && cancelToken.Value.IsCancellationRequested)
|
||||||
|
{
|
||||||
|
return OperationOutcome.Cancelled;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return OperationOutcome.Successful;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
outfileStream.Close();
|
||||||
|
Reset();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Log?.Write(LogType.Error, e.ToString());
|
||||||
|
return OperationOutcome.FileIOWriteError;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void WritePadding(FileStream outfileStream, long bytesToWriteB, CancellationToken? cancelToken = null)
|
||||||
|
{
|
||||||
|
long bytesLeftToWriteB = bytesToWriteB;
|
||||||
|
long writes = bytesLeftToWriteB / XCIFileTrimmer.BufferSize;
|
||||||
|
int write = 0;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var buffer = new byte[BufferSize];
|
||||||
|
Array.Fill<byte>(buffer, XCIFileTrimmer.PaddingByte);
|
||||||
|
|
||||||
|
while (bytesLeftToWriteB > 0)
|
||||||
|
{
|
||||||
|
if (cancelToken.HasValue && cancelToken.Value.IsCancellationRequested)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
long bytesToWrite = Math.Min(XCIFileTrimmer.BufferSize, bytesLeftToWriteB);
|
||||||
|
|
||||||
|
#if !XCI_TRIMMER_READ_ONLY_MODE
|
||||||
|
outfileStream.Write(buffer, 0, (int)bytesToWrite);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
bytesLeftToWriteB -= bytesToWrite;
|
||||||
|
Log?.Progress(write, writes, "Writing padding data...", false);
|
||||||
|
write++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
Log?.Progress(write, writes, "Writing padding data...", true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OpenReaders()
|
||||||
|
{
|
||||||
|
if (_binaryReader == null)
|
||||||
|
{
|
||||||
|
_fileStream = new FileStream(_filename, FileMode.Open, FileAccess.Read, FileShare.Read);
|
||||||
|
_binaryReader = new BinaryReader(_fileStream);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void CloseReaders()
|
||||||
|
{
|
||||||
|
if (_binaryReader != null && _binaryReader.BaseStream != null)
|
||||||
|
_binaryReader.Close();
|
||||||
|
_binaryReader = null;
|
||||||
|
_fileStream = null;
|
||||||
|
GC.Collect();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ReadHeader()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
OpenReaders();
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// Attempt without key area
|
||||||
|
bool success = CheckAndReadHeader(false);
|
||||||
|
|
||||||
|
if (!success)
|
||||||
|
{
|
||||||
|
// Attempt with key area
|
||||||
|
success = CheckAndReadHeader(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
_fileOK = success;
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
CloseReaders();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Log?.Write(LogType.Error, ex.Message);
|
||||||
|
_fileOK = false;
|
||||||
|
_dataSizeB = 0;
|
||||||
|
_cartSizeB = 0;
|
||||||
|
_fileSizeB = 0;
|
||||||
|
_offsetB = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool CheckAndReadHeader(bool assumeKeyArea)
|
||||||
|
{
|
||||||
|
// Read file size
|
||||||
|
_fileSizeB = _fileStream.Length;
|
||||||
|
if (_fileSizeB < 32 * 1024)
|
||||||
|
{
|
||||||
|
Log?.Write(LogType.Error, "The source file doesn't look like an XCI file as the data size is too small");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Setup offset
|
||||||
|
_offsetB = (long)(assumeKeyArea ? XCIFileTrimmer.CartKeyAreaSize : 0);
|
||||||
|
|
||||||
|
// Check header
|
||||||
|
Pos = _offsetB + XCIFileTrimmer.HeaderFilePos;
|
||||||
|
string head = System.Text.Encoding.ASCII.GetString(_binaryReader.ReadBytes(4));
|
||||||
|
if (head != XCIFileTrimmer.HeaderMagicValue)
|
||||||
|
{
|
||||||
|
if (!assumeKeyArea)
|
||||||
|
{
|
||||||
|
Log?.Write(LogType.Warn, $"Incorrect header found, file mat contain a key area...");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Log?.Write(LogType.Error, "The source file doesn't look like an XCI file as the header is corrupted");
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read Cart Size
|
||||||
|
Pos = _offsetB + XCIFileTrimmer.CartSizeFilePos;
|
||||||
|
byte cartSizeId = _binaryReader.ReadByte();
|
||||||
|
if (!_cartSizesGB.TryGetValue(cartSizeId, out long cartSizeNGB))
|
||||||
|
{
|
||||||
|
Log?.Write(LogType.Error, $"The source file doesn't look like an XCI file as the Cartridge Size is incorrect (0x{cartSizeId:X2})");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
_cartSizeB = cartSizeNGB * XCIFileTrimmer.CartSizeMBinFormattedGB * XCIFileTrimmer.BytesInAMegabyte;
|
||||||
|
|
||||||
|
// Read data size
|
||||||
|
Pos = _offsetB + XCIFileTrimmer.DataSizeFilePos;
|
||||||
|
long records = (long)BitConverter.ToUInt32(_binaryReader.ReadBytes(4), 0);
|
||||||
|
_dataSizeB = RecordsToByte(records);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -152,16 +152,17 @@ namespace Ryujinx.Graphics.Gpu
|
|||||||
/// Creates a new GPU memory manager.
|
/// Creates a new GPU memory manager.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="pid">ID of the process that owns the memory manager</param>
|
/// <param name="pid">ID of the process that owns the memory manager</param>
|
||||||
|
/// <param name="cpuMemorySize">The amount of physical CPU Memory Avaiable on the device.</param>
|
||||||
/// <returns>The memory manager</returns>
|
/// <returns>The memory manager</returns>
|
||||||
/// <exception cref="ArgumentException">Thrown when <paramref name="pid"/> is invalid</exception>
|
/// <exception cref="ArgumentException">Thrown when <paramref name="pid"/> is invalid</exception>
|
||||||
public MemoryManager CreateMemoryManager(ulong pid)
|
public MemoryManager CreateMemoryManager(ulong pid, ulong cpuMemorySize)
|
||||||
{
|
{
|
||||||
if (!PhysicalMemoryRegistry.TryGetValue(pid, out var physicalMemory))
|
if (!PhysicalMemoryRegistry.TryGetValue(pid, out var physicalMemory))
|
||||||
{
|
{
|
||||||
throw new ArgumentException("The PID is invalid or the process was not registered", nameof(pid));
|
throw new ArgumentException("The PID is invalid or the process was not registered", nameof(pid));
|
||||||
}
|
}
|
||||||
|
|
||||||
return new MemoryManager(physicalMemory);
|
return new MemoryManager(physicalMemory, cpuMemorySize);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
using Ryujinx.Common.Logging;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections;
|
using System.Collections;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
@@ -47,11 +48,17 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
{
|
{
|
||||||
private const int MinCountForDeletion = 32;
|
private const int MinCountForDeletion = 32;
|
||||||
private const int MaxCapacity = 2048;
|
private const int MaxCapacity = 2048;
|
||||||
|
private const ulong GiB = 1024 * 1024 * 1024;
|
||||||
|
private ulong MaxTextureSizeCapacity = 4UL * GiB;
|
||||||
private const ulong MinTextureSizeCapacity = 512 * 1024 * 1024;
|
private const ulong MinTextureSizeCapacity = 512 * 1024 * 1024;
|
||||||
private const ulong MaxTextureSizeCapacity = 4UL * 1024 * 1024 * 1024;
|
private const ulong DefaultTextureSizeCapacity = 1 * GiB;
|
||||||
private const ulong DefaultTextureSizeCapacity = 1UL * 1024 * 1024 * 1024;
|
private const ulong TextureSizeCapacity6GiB = 4 * GiB;
|
||||||
|
private const ulong TextureSizeCapacity8GiB = 6 * GiB;
|
||||||
|
private const ulong TextureSizeCapacity12GiB = 12 * GiB;
|
||||||
|
|
||||||
|
|
||||||
private const float MemoryScaleFactor = 0.50f;
|
private const float MemoryScaleFactor = 0.50f;
|
||||||
private ulong _maxCacheMemoryUsage = 0;
|
private ulong _maxCacheMemoryUsage = DefaultTextureSizeCapacity;
|
||||||
|
|
||||||
private readonly LinkedList<Texture> _textures;
|
private readonly LinkedList<Texture> _textures;
|
||||||
private ulong _totalSize;
|
private ulong _totalSize;
|
||||||
@@ -66,18 +73,38 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <remarks>
|
/// <remarks>
|
||||||
/// If the backend GPU has 0 memory capacity, the cache size defaults to `DefaultTextureSizeCapacity`.
|
/// If the backend GPU has 0 memory capacity, the cache size defaults to `DefaultTextureSizeCapacity`.
|
||||||
|
///
|
||||||
|
/// Reads the current Device total CPU Memory, to determine the maximum amount of Vram available. Capped to 50% of Current GPU Memory.
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
/// <param name="context">The GPU context that the cache belongs to</param>
|
/// <param name="context">The GPU context that the cache belongs to</param>
|
||||||
public void Initialize(GpuContext context)
|
/// <param name="cpuMemorySize">The amount of physical CPU Memory Avaiable on the device.</param>
|
||||||
|
public void Initialize(GpuContext context, ulong cpuMemorySize)
|
||||||
{
|
{
|
||||||
|
var cpuMemorySizeGiB = cpuMemorySize / GiB;
|
||||||
|
|
||||||
|
if (cpuMemorySizeGiB < 6 || context.Capabilities.MaximumGpuMemory == 0)
|
||||||
|
{
|
||||||
|
_maxCacheMemoryUsage = DefaultTextureSizeCapacity;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else if (cpuMemorySizeGiB == 6)
|
||||||
|
{
|
||||||
|
MaxTextureSizeCapacity = TextureSizeCapacity6GiB;
|
||||||
|
}
|
||||||
|
else if (cpuMemorySizeGiB == 8)
|
||||||
|
{
|
||||||
|
MaxTextureSizeCapacity = TextureSizeCapacity8GiB;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
MaxTextureSizeCapacity = TextureSizeCapacity12GiB;
|
||||||
|
}
|
||||||
|
|
||||||
var cacheMemory = (ulong)(context.Capabilities.MaximumGpuMemory * MemoryScaleFactor);
|
var cacheMemory = (ulong)(context.Capabilities.MaximumGpuMemory * MemoryScaleFactor);
|
||||||
|
|
||||||
_maxCacheMemoryUsage = Math.Clamp(cacheMemory, MinTextureSizeCapacity, MaxTextureSizeCapacity);
|
_maxCacheMemoryUsage = Math.Clamp(cacheMemory, MinTextureSizeCapacity, MaxTextureSizeCapacity);
|
||||||
|
|
||||||
if (context.Capabilities.MaximumGpuMemory == 0)
|
Logger.Info?.Print(LogClass.Gpu, $"AutoDelete Cache Allocated VRAM : {_maxCacheMemoryUsage / GiB} GiB");
|
||||||
{
|
|
||||||
_maxCacheMemoryUsage = DefaultTextureSizeCapacity;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -71,9 +71,10 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes the cache, setting the maximum texture capacity for the specified GPU context.
|
/// Initializes the cache, setting the maximum texture capacity for the specified GPU context.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public void Initialize()
|
/// <param name="cpuMemorySize">The amount of physical CPU Memory Avaiable on the device.</param>
|
||||||
|
public void Initialize(ulong cpuMemorySize)
|
||||||
{
|
{
|
||||||
_cache.Initialize(_context);
|
_cache.Initialize(_context, cpuMemorySize);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -55,7 +55,8 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
/// Creates a new instance of the GPU memory manager.
|
/// Creates a new instance of the GPU memory manager.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="physicalMemory">Physical memory that this memory manager will map into</param>
|
/// <param name="physicalMemory">Physical memory that this memory manager will map into</param>
|
||||||
internal MemoryManager(PhysicalMemory physicalMemory)
|
/// <param name="cpuMemorySize">The amount of physical CPU Memory Avaiable on the device.</param>
|
||||||
|
internal MemoryManager(PhysicalMemory physicalMemory, ulong cpuMemorySize)
|
||||||
{
|
{
|
||||||
Physical = physicalMemory;
|
Physical = physicalMemory;
|
||||||
VirtualRangeCache = new VirtualRangeCache(this);
|
VirtualRangeCache = new VirtualRangeCache(this);
|
||||||
@@ -65,7 +66,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
MemoryUnmapped += Physical.BufferCache.MemoryUnmappedHandler;
|
MemoryUnmapped += Physical.BufferCache.MemoryUnmappedHandler;
|
||||||
MemoryUnmapped += VirtualRangeCache.MemoryUnmappedHandler;
|
MemoryUnmapped += VirtualRangeCache.MemoryUnmappedHandler;
|
||||||
MemoryUnmapped += CounterCache.MemoryUnmappedHandler;
|
MemoryUnmapped += CounterCache.MemoryUnmappedHandler;
|
||||||
Physical.TextureCache.Initialize();
|
Physical.TextureCache.Initialize(cpuMemorySize);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -432,7 +432,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
|
|||||||
|
|
||||||
bool colorIsVector = isGather || !isShadow;
|
bool colorIsVector = isGather || !isShadow;
|
||||||
|
|
||||||
texCall += ")" + (colorIsVector ? GetMaskMultiDest(texOp.Index) : "");
|
texCall += ")" + (colorIsVector ? GetMaskMultiDest(texOp.Index) : string.Empty);
|
||||||
|
|
||||||
return texCall;
|
return texCall;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -830,12 +830,12 @@ namespace Ryujinx.Graphics.Shader.Translation
|
|||||||
|
|
||||||
if (use.Node != null)
|
if (use.Node != null)
|
||||||
{
|
{
|
||||||
Console.Write($"{indentation} {separator}- ({(use.Inverted ? "INV " : "")}{use.Index})");
|
Console.Write($"{indentation} {separator}- ({(use.Inverted ? "INV " : string.Empty)}{use.Index})");
|
||||||
PrintTreeNode(use.Node, indentation + (last ? " " : " | "));
|
PrintTreeNode(use.Node, indentation + (last ? " " : " | "));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Console.WriteLine($"{indentation} {separator}- ({(use.Inverted ? "INV " : "")}{use.Index}) NULL");
|
Console.WriteLine($"{indentation} {separator}- ({(use.Inverted ? "INV " : string.Empty)}{use.Index}) NULL");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -852,12 +852,12 @@ namespace Ryujinx.Graphics.Shader.Translation
|
|||||||
|
|
||||||
if (use.Node != null)
|
if (use.Node != null)
|
||||||
{
|
{
|
||||||
Console.Write($"{indentation} {separator}- ({(use.Inverted ? "INV " : "")}{use.Index})");
|
Console.Write($"{indentation} {separator}- ({(use.Inverted ? "INV " : string.Empty)}{use.Index})");
|
||||||
PrintTreeNode(use.Node, indentation + (last ? " " : " | "));
|
PrintTreeNode(use.Node, indentation + (last ? " " : " | "));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Console.WriteLine($"{indentation} {separator}- ({(use.Inverted ? "INV " : "")}{use.Index}) NULL");
|
Console.WriteLine($"{indentation} {separator}- ({(use.Inverted ? "INV " : string.Empty)}{use.Index}) NULL");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -104,25 +104,27 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
|
|
||||||
public event EventHandler<ScreenCaptureImageInfo> ScreenCaptured;
|
public event EventHandler<ScreenCaptureImageInfo> ScreenCaptured;
|
||||||
|
|
||||||
public VulkanRenderer(Vk api, Func<Instance, Vk, SurfaceKHR> surfaceFunc, Func<string[]> requiredExtensionsFunc, string preferredGpuId)
|
public VulkanRenderer(Vk api, Func<Instance, Vk, SurfaceKHR> getSurface, Func<string[]> requiredExtensionsFunc, string preferredGpuId)
|
||||||
{
|
{
|
||||||
_getSurface = surfaceFunc;
|
_getSurface = getSurface;
|
||||||
_getRequiredExtensions = requiredExtensionsFunc;
|
_getRequiredExtensions = requiredExtensionsFunc;
|
||||||
_preferredGpuId = preferredGpuId;
|
_preferredGpuId = preferredGpuId;
|
||||||
Api = api;
|
Api = api;
|
||||||
Shaders = new HashSet<ShaderCollection>();
|
Shaders = [];
|
||||||
Textures = new HashSet<ITexture>();
|
Textures = [];
|
||||||
Samplers = new HashSet<SamplerHolder>();
|
Samplers = [];
|
||||||
|
|
||||||
if (OperatingSystem.IsMacOS())
|
// Any device running on MacOS is using MoltenVK, even Intel and AMD vendors.
|
||||||
{
|
if (IsMoltenVk = OperatingSystem.IsMacOS())
|
||||||
MVKInitialization.Initialize();
|
MVKInitialization.Initialize();
|
||||||
|
|
||||||
// Any device running on MacOS is using MoltenVK, even Intel and AMD vendors.
|
|
||||||
IsMoltenVk = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static VulkanRenderer Create(
|
||||||
|
string preferredGpuId,
|
||||||
|
Func<Instance, Vk, SurfaceKHR> getSurface,
|
||||||
|
Func<string[]> getRequiredExtensions
|
||||||
|
) => new(Vk.GetApi(), getSurface, getRequiredExtensions, preferredGpuId);
|
||||||
|
|
||||||
private unsafe void LoadFeatures(uint maxQueueCount, uint queueFamilyIndex)
|
private unsafe void LoadFeatures(uint maxQueueCount, uint queueFamilyIndex)
|
||||||
{
|
{
|
||||||
FormatCapabilities = new FormatCapabilities(Api, _physicalDevice.PhysicalDevice);
|
FormatCapabilities = new FormatCapabilities(Api, _physicalDevice.PhysicalDevice);
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ namespace Ryujinx.HLE.Generators
|
|||||||
var syntaxReceiver = (ServiceSyntaxReceiver)context.SyntaxReceiver;
|
var syntaxReceiver = (ServiceSyntaxReceiver)context.SyntaxReceiver;
|
||||||
CodeGenerator generator = new CodeGenerator();
|
CodeGenerator generator = new CodeGenerator();
|
||||||
|
|
||||||
|
generator.AppendLine("#nullable enable");
|
||||||
generator.AppendLine("using System;");
|
generator.AppendLine("using System;");
|
||||||
generator.EnterScope($"namespace Ryujinx.HLE.HOS.Services.Sm");
|
generator.EnterScope($"namespace Ryujinx.HLE.HOS.Services.Sm");
|
||||||
generator.EnterScope($"partial class IUserInterface");
|
generator.EnterScope($"partial class IUserInterface");
|
||||||
@@ -22,7 +23,7 @@ namespace Ryujinx.HLE.Generators
|
|||||||
{
|
{
|
||||||
if (className.Modifiers.Any(SyntaxKind.AbstractKeyword) || className.Modifiers.Any(SyntaxKind.PrivateKeyword) || !className.AttributeLists.Any(x => x.Attributes.Any(y => y.ToString().StartsWith("Service"))))
|
if (className.Modifiers.Any(SyntaxKind.AbstractKeyword) || className.Modifiers.Any(SyntaxKind.PrivateKeyword) || !className.AttributeLists.Any(x => x.Attributes.Any(y => y.ToString().StartsWith("Service"))))
|
||||||
continue;
|
continue;
|
||||||
var name = GetFullName(className, context).Replace("global::", "");
|
var name = GetFullName(className, context).Replace("global::", string.Empty);
|
||||||
if (!name.StartsWith("Ryujinx.HLE.HOS.Services"))
|
if (!name.StartsWith("Ryujinx.HLE.HOS.Services"))
|
||||||
continue;
|
continue;
|
||||||
var constructors = className.ChildNodes().Where(x => x.IsKind(SyntaxKind.ConstructorDeclaration)).Select(y => y as ConstructorDeclarationSyntax).ToArray();
|
var constructors = className.ChildNodes().Where(x => x.IsKind(SyntaxKind.ConstructorDeclaration)).Select(y => y as ConstructorDeclarationSyntax).ToArray();
|
||||||
@@ -58,6 +59,7 @@ namespace Ryujinx.HLE.Generators
|
|||||||
|
|
||||||
generator.LeaveScope();
|
generator.LeaveScope();
|
||||||
generator.LeaveScope();
|
generator.LeaveScope();
|
||||||
|
generator.AppendLine("#nullable disable");
|
||||||
context.AddSource($"IUserInterface.g.cs", generator.ToString());
|
context.AddSource($"IUserInterface.g.cs", generator.ToString());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -523,7 +523,7 @@ namespace Ryujinx.HLE.FileSystem
|
|||||||
{
|
{
|
||||||
// Clean up the name and get the NcaId
|
// Clean up the name and get the NcaId
|
||||||
|
|
||||||
string[] pathComponents = entry.FullName.Replace(".cnmt", "").Split('/');
|
string[] pathComponents = entry.FullName.Replace(".cnmt", string.Empty).Split('/');
|
||||||
|
|
||||||
string ncaId = pathComponents[^1];
|
string ncaId = pathComponents[^1];
|
||||||
|
|
||||||
|
|||||||
@@ -132,7 +132,7 @@ namespace Ryujinx.HLE.FileSystem
|
|||||||
|
|
||||||
if (systemPath.StartsWith(baseSystemPath))
|
if (systemPath.StartsWith(baseSystemPath))
|
||||||
{
|
{
|
||||||
string rawPath = systemPath.Replace(baseSystemPath, "");
|
string rawPath = systemPath.Replace(baseSystemPath, string.Empty);
|
||||||
int firstSeparatorOffset = rawPath.IndexOf(Path.DirectorySeparatorChar);
|
int firstSeparatorOffset = rawPath.IndexOf(Path.DirectorySeparatorChar);
|
||||||
|
|
||||||
if (firstSeparatorOffset == -1)
|
if (firstSeparatorOffset == -1)
|
||||||
|
|||||||
@@ -107,7 +107,7 @@ namespace Ryujinx.HLE.HOS.Applets.Error
|
|||||||
|
|
||||||
private static string CleanText(string value)
|
private static string CleanText(string value)
|
||||||
{
|
{
|
||||||
return CleanTextRegex().Replace(value, "").Replace("\0", "");
|
return CleanTextRegex().Replace(value, string.Empty).Replace("\0", string.Empty);
|
||||||
}
|
}
|
||||||
|
|
||||||
private string GetMessageText(uint module, uint description, string key)
|
private string GetMessageText(uint module, uint description, string key)
|
||||||
@@ -129,17 +129,15 @@ namespace Ryujinx.HLE.HOS.Applets.Error
|
|||||||
|
|
||||||
return CleanText(reader.ReadToEnd());
|
return CleanText(reader.ReadToEnd());
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
return string.Empty;
|
||||||
return "";
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private string[] GetButtonsText(uint module, uint description, string key)
|
private string[] GetButtonsText(uint module, uint description, string key)
|
||||||
{
|
{
|
||||||
string buttonsText = GetMessageText(module, description, key);
|
string buttonsText = GetMessageText(module, description, key);
|
||||||
|
|
||||||
return (buttonsText == "") ? null : buttonsText.Split(new[] { "\r\n", "\r", "\n" }, StringSplitOptions.None);
|
return (buttonsText == string.Empty) ? null : buttonsText.Split(["\r\n", "\r", "\n"], StringSplitOptions.None);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ParseErrorCommonArg()
|
private void ParseErrorCommonArg()
|
||||||
@@ -156,7 +154,7 @@ namespace Ryujinx.HLE.HOS.Applets.Error
|
|||||||
|
|
||||||
string message = GetMessageText(module, description, "DlgMsg");
|
string message = GetMessageText(module, description, "DlgMsg");
|
||||||
|
|
||||||
if (message == "")
|
if (message == string.Empty)
|
||||||
{
|
{
|
||||||
message = "An error has occured.\n\nPlease try again later.";
|
message = "An error has occured.\n\nPlease try again later.";
|
||||||
}
|
}
|
||||||
@@ -190,7 +188,7 @@ namespace Ryujinx.HLE.HOS.Applets.Error
|
|||||||
|
|
||||||
// TODO: Handle the LanguageCode to return the translated "OK" and "Details".
|
// TODO: Handle the LanguageCode to return the translated "OK" and "Details".
|
||||||
|
|
||||||
if (detailsText.Trim() != "")
|
if (detailsText.Trim() != string.Empty)
|
||||||
{
|
{
|
||||||
buttons.Add("Details");
|
buttons.Add("Details");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ namespace Ryujinx.HLE.HOS.Applets
|
|||||||
|
|
||||||
private byte[] _transferMemory;
|
private byte[] _transferMemory;
|
||||||
|
|
||||||
private string _textValue = "";
|
private string _textValue = string.Empty;
|
||||||
private int _cursorBegin = 0;
|
private int _cursorBegin = 0;
|
||||||
private Encoding _encoding = Encoding.Unicode;
|
private Encoding _encoding = Encoding.Unicode;
|
||||||
private KeyboardResult _lastResult = KeyboardResult.NotSet;
|
private KeyboardResult _lastResult = KeyboardResult.NotSet;
|
||||||
|
|||||||
@@ -305,7 +305,7 @@ namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard
|
|||||||
{
|
{
|
||||||
SKRect bounds = SKRect.Empty;
|
SKRect bounds = SKRect.Empty;
|
||||||
|
|
||||||
if (text == "")
|
if (text == string.Empty)
|
||||||
{
|
{
|
||||||
paint.MeasureText(" ", ref bounds);
|
paint.MeasureText(" ", ref bounds);
|
||||||
}
|
}
|
||||||
@@ -321,7 +321,7 @@ namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard
|
|||||||
{
|
{
|
||||||
SKRect bounds = SKRect.Empty;
|
SKRect bounds = SKRect.Empty;
|
||||||
|
|
||||||
if (text == "")
|
if (text == string.Empty)
|
||||||
{
|
{
|
||||||
paint.MeasureText(" ", ref bounds);
|
paint.MeasureText(" ", ref bounds);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
internal class SoftwareKeyboardUIState
|
internal class SoftwareKeyboardUIState
|
||||||
{
|
{
|
||||||
public string InputText = "";
|
public string InputText = string.Empty;
|
||||||
public int CursorBegin = 0;
|
public int CursorBegin = 0;
|
||||||
public int CursorEnd = 0;
|
public int CursorEnd = 0;
|
||||||
public bool AcceptPressed = false;
|
public bool AcceptPressed = false;
|
||||||
|
|||||||
@@ -238,7 +238,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
info.SubName = "";
|
info.SubName = string.Empty;
|
||||||
}
|
}
|
||||||
|
|
||||||
info.ImageName = GetGuessedNsoNameFromIndex(imageIndex);
|
info.ImageName = GetGuessedNsoNameFromIndex(imageIndex);
|
||||||
|
|||||||
@@ -64,7 +64,7 @@ namespace Ryujinx.HLE.HOS.Services.Account.Acc
|
|||||||
{
|
{
|
||||||
if (userId.IsNull)
|
if (userId.IsNull)
|
||||||
{
|
{
|
||||||
userId = new UserId(Guid.NewGuid().ToString().Replace("-", ""));
|
userId = new UserId(Guid.NewGuid().ToString().Replace("-", string.Empty));
|
||||||
}
|
}
|
||||||
|
|
||||||
UserProfile profile = new(userId, name, image);
|
UserProfile profile = new(userId, name, image);
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ using Ryujinx.HLE.Exceptions;
|
|||||||
using Ryujinx.HLE.HOS.Kernel.Memory;
|
using Ryujinx.HLE.HOS.Kernel.Memory;
|
||||||
using Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory;
|
using Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory;
|
||||||
using Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Common;
|
using Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Common;
|
||||||
|
using Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.DebugMouse;
|
||||||
using Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.DebugPad;
|
using Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.DebugPad;
|
||||||
using Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Keyboard;
|
using Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Keyboard;
|
||||||
using Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Mouse;
|
using Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Mouse;
|
||||||
@@ -28,6 +29,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid
|
|||||||
public DebugPadDevice DebugPad;
|
public DebugPadDevice DebugPad;
|
||||||
public TouchDevice Touchscreen;
|
public TouchDevice Touchscreen;
|
||||||
public MouseDevice Mouse;
|
public MouseDevice Mouse;
|
||||||
|
public DebugMouseDevice DebugMouse;
|
||||||
public KeyboardDevice Keyboard;
|
public KeyboardDevice Keyboard;
|
||||||
public NpadDevices Npads;
|
public NpadDevices Npads;
|
||||||
|
|
||||||
@@ -44,6 +46,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid
|
|||||||
CheckTypeSizeOrThrow<RingLifo<DebugPadState>>(0x2c8);
|
CheckTypeSizeOrThrow<RingLifo<DebugPadState>>(0x2c8);
|
||||||
CheckTypeSizeOrThrow<RingLifo<TouchScreenState>>(0x2C38);
|
CheckTypeSizeOrThrow<RingLifo<TouchScreenState>>(0x2C38);
|
||||||
CheckTypeSizeOrThrow<RingLifo<MouseState>>(0x350);
|
CheckTypeSizeOrThrow<RingLifo<MouseState>>(0x350);
|
||||||
|
CheckTypeSizeOrThrow<RingLifo<DebugMouseState>>(0x350);
|
||||||
CheckTypeSizeOrThrow<RingLifo<KeyboardState>>(0x3D8);
|
CheckTypeSizeOrThrow<RingLifo<KeyboardState>>(0x3D8);
|
||||||
CheckTypeSizeOrThrow<Array10<NpadState>>(0x32000);
|
CheckTypeSizeOrThrow<Array10<NpadState>>(0x32000);
|
||||||
CheckTypeSizeOrThrow<SharedMemory>(Horizon.HidSize);
|
CheckTypeSizeOrThrow<SharedMemory>(Horizon.HidSize);
|
||||||
@@ -64,6 +67,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid
|
|||||||
DebugPad = new DebugPadDevice(_device, true);
|
DebugPad = new DebugPadDevice(_device, true);
|
||||||
Touchscreen = new TouchDevice(_device, true);
|
Touchscreen = new TouchDevice(_device, true);
|
||||||
Mouse = new MouseDevice(_device, false);
|
Mouse = new MouseDevice(_device, false);
|
||||||
|
DebugMouse = new DebugMouseDevice(_device, false);
|
||||||
Keyboard = new KeyboardDevice(_device, false);
|
Keyboard = new KeyboardDevice(_device, false);
|
||||||
Npads = new NpadDevices(_device, true);
|
Npads = new NpadDevices(_device, true);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,29 @@
|
|||||||
|
using Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Common;
|
||||||
|
using Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.DebugMouse;
|
||||||
|
|
||||||
|
namespace Ryujinx.HLE.HOS.Services.Hid
|
||||||
|
{
|
||||||
|
public class DebugMouseDevice : BaseDevice
|
||||||
|
{
|
||||||
|
public DebugMouseDevice(Switch device, bool active) : base(device, active) { }
|
||||||
|
|
||||||
|
public void Update()
|
||||||
|
{
|
||||||
|
ref RingLifo<DebugMouseState> lifo = ref _device.Hid.SharedMemory.DebugMouse;
|
||||||
|
|
||||||
|
ref DebugMouseState previousEntry = ref lifo.GetCurrentEntryRef();
|
||||||
|
|
||||||
|
DebugMouseState newState = new()
|
||||||
|
{
|
||||||
|
SamplingNumber = previousEntry.SamplingNumber + 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (Active)
|
||||||
|
{
|
||||||
|
// TODO: This is a debug device only present in dev environment, do we want to support it?
|
||||||
|
}
|
||||||
|
|
||||||
|
lifo.Write(ref newState);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -130,6 +130,26 @@ namespace Ryujinx.HLE.HOS.Services.Hid
|
|||||||
|
|
||||||
return ResultCode.Success;
|
return ResultCode.Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[CommandCmif(26)]
|
||||||
|
// ActivateDebugMouse(nn::applet::AppletResourceUserId)
|
||||||
|
public ResultCode ActivateDebugMouse(ServiceCtx context)
|
||||||
|
{
|
||||||
|
long appletResourceUserId = context.RequestData.ReadInt64();
|
||||||
|
|
||||||
|
context.Device.Hid.DebugMouse.Active = true;
|
||||||
|
|
||||||
|
// Initialize entries to avoid issues with some games.
|
||||||
|
|
||||||
|
for (int entry = 0; entry < Hid.SharedMemEntryCount; entry++)
|
||||||
|
{
|
||||||
|
context.Device.Hid.DebugMouse.Update();
|
||||||
|
}
|
||||||
|
|
||||||
|
Logger.Stub?.PrintStub(LogClass.ServiceHid, new { appletResourceUserId });
|
||||||
|
|
||||||
|
return ResultCode.Success;
|
||||||
|
}
|
||||||
|
|
||||||
[CommandCmif(31)]
|
[CommandCmif(31)]
|
||||||
// ActivateKeyboard(nn::applet::AppletResourceUserId)
|
// ActivateKeyboard(nn::applet::AppletResourceUserId)
|
||||||
|
|||||||
@@ -0,0 +1,12 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.DebugMouse
|
||||||
|
{
|
||||||
|
[Flags]
|
||||||
|
enum DebugMouseAttribute : uint
|
||||||
|
{
|
||||||
|
None = 0,
|
||||||
|
Transferable = 1 << 0,
|
||||||
|
IsConnected = 1 << 1,
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.DebugMouse
|
||||||
|
{
|
||||||
|
[Flags]
|
||||||
|
enum DebugMouseButton : uint
|
||||||
|
{
|
||||||
|
None = 0,
|
||||||
|
Left = 1 << 0,
|
||||||
|
Right = 1 << 1,
|
||||||
|
Middle = 1 << 2,
|
||||||
|
Forward = 1 << 3,
|
||||||
|
Back = 1 << 4,
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
using Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Common;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
namespace Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.DebugMouse
|
||||||
|
{
|
||||||
|
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
||||||
|
struct DebugMouseState : ISampledDataStruct
|
||||||
|
{
|
||||||
|
public ulong SamplingNumber;
|
||||||
|
public int X;
|
||||||
|
public int Y;
|
||||||
|
public int DeltaX;
|
||||||
|
public int DeltaY;
|
||||||
|
public int WheelDeltaX;
|
||||||
|
public int WheelDeltaY;
|
||||||
|
public DebugMouseButton Buttons;
|
||||||
|
public DebugMouseAttribute Attributes;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
using Ryujinx.Common.Memory;
|
using Ryujinx.Common.Memory;
|
||||||
using Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Common;
|
using Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Common;
|
||||||
|
using Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.DebugMouse;
|
||||||
using Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.DebugPad;
|
using Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.DebugPad;
|
||||||
using Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Keyboard;
|
using Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Keyboard;
|
||||||
using Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Mouse;
|
using Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Mouse;
|
||||||
@@ -44,6 +45,12 @@ namespace Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
[FieldOffset(0x9A00)]
|
[FieldOffset(0x9A00)]
|
||||||
public Array10<NpadState> Npads;
|
public Array10<NpadState> Npads;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Debug mouse.
|
||||||
|
/// </summary>
|
||||||
|
[FieldOffset(0x3DC00)]
|
||||||
|
public RingLifo<DebugMouseState> DebugMouse;
|
||||||
|
|
||||||
public static SharedMemory Create()
|
public static SharedMemory Create()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
using Gommon;
|
||||||
using Ryujinx.Common.Logging;
|
using Ryujinx.Common.Logging;
|
||||||
using Ryujinx.Common.Memory;
|
using Ryujinx.Common.Memory;
|
||||||
using Ryujinx.Common.Utilities;
|
using Ryujinx.Common.Utilities;
|
||||||
@@ -143,7 +144,7 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator.LdnMitm
|
|||||||
if (decompressedLdnData.Length != header.DecompressLength)
|
if (decompressedLdnData.Length != header.DecompressLength)
|
||||||
{
|
{
|
||||||
Logger.Error?.PrintMsg(LogClass.ServiceLdn, $"Decompress error: length does not match. ({decompressedLdnData.Length} != {header.DecompressLength})");
|
Logger.Error?.PrintMsg(LogClass.ServiceLdn, $"Decompress error: length does not match. ({decompressedLdnData.Length} != {header.DecompressLength})");
|
||||||
Logger.Error?.PrintMsg(LogClass.ServiceLdn, $"Decompress error data: '{string.Join("", decompressedLdnData.Select(x => (int)x).ToArray())}'");
|
Logger.Error?.PrintMsg(LogClass.ServiceLdn, $"Decompress error data: '{decompressedLdnData.Select(x => (int)x).JoinToString(string.Empty)}'");
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ namespace Ryujinx.HLE.HOS.Services.Ngct
|
|||||||
ulong bufferSize = context.Request.PtrBuff[0].Size;
|
ulong bufferSize = context.Request.PtrBuff[0].Size;
|
||||||
|
|
||||||
bool isMatch = false;
|
bool isMatch = false;
|
||||||
string text = "";
|
string text = string.Empty;
|
||||||
|
|
||||||
if (bufferSize != 0)
|
if (bufferSize != 0)
|
||||||
{
|
{
|
||||||
@@ -57,8 +57,8 @@ namespace Ryujinx.HLE.HOS.Services.Ngct
|
|||||||
|
|
||||||
ulong bufferFilteredPosition = context.Request.RecvListBuff[0].Position;
|
ulong bufferFilteredPosition = context.Request.RecvListBuff[0].Position;
|
||||||
|
|
||||||
string text = "";
|
string text = string.Empty;
|
||||||
string textFiltered = "";
|
string textFiltered = string.Empty;
|
||||||
|
|
||||||
if (bufferSize != 0)
|
if (bufferSize != 0)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostAsGpu
|
|||||||
|
|
||||||
public NvHostAsGpuDeviceFile(ServiceCtx context, IVirtualMemoryManager memory, ulong owner) : base(context, owner)
|
public NvHostAsGpuDeviceFile(ServiceCtx context, IVirtualMemoryManager memory, ulong owner) : base(context, owner)
|
||||||
{
|
{
|
||||||
_asContext = new AddressSpaceContext(context.Device.Gpu.CreateMemoryManager(owner));
|
_asContext = new AddressSpaceContext(context.Device.Gpu.CreateMemoryManager(owner, context.Device.Memory.Size));
|
||||||
_memoryAllocator = new NvMemoryAllocator();
|
_memoryAllocator = new NvMemoryAllocator();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -36,7 +36,7 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Nsd.Manager
|
|||||||
// TODO: Load Environment from the savedata.
|
// TODO: Load Environment from the savedata.
|
||||||
address = address.Replace("%", IManager.NsdSettings.Environment);
|
address = address.Replace("%", IManager.NsdSettings.Environment);
|
||||||
|
|
||||||
resolvedAddress = "";
|
resolvedAddress = string.Empty;
|
||||||
|
|
||||||
if (IManager.NsdSettings == null)
|
if (IManager.NsdSettings == null)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -63,7 +63,7 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger
|
|||||||
|
|
||||||
if (size < 0)
|
if (size < 0)
|
||||||
{
|
{
|
||||||
return "";
|
return string.Empty;
|
||||||
}
|
}
|
||||||
|
|
||||||
ReadOnlySpan<byte> data = ReadInPlace((size + 1) * 2);
|
ReadOnlySpan<byte> data = ReadInPlace((size + 1) * 2);
|
||||||
|
|||||||
@@ -166,7 +166,7 @@ namespace Ryujinx.HLE.HOS.Services.Vi.RootService
|
|||||||
|
|
||||||
private ResultCode OpenDisplayImpl(ServiceCtx context, string name)
|
private ResultCode OpenDisplayImpl(ServiceCtx context, string name)
|
||||||
{
|
{
|
||||||
if (name == "")
|
if (name == string.Empty)
|
||||||
{
|
{
|
||||||
return ResultCode.InvalidValue;
|
return ResultCode.InvalidValue;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -102,7 +102,7 @@ namespace Ryujinx.HLE.Loaders.Executables
|
|||||||
Match fsSdkMatch = FsSdkRegex().Match(rawTextBuffer);
|
Match fsSdkMatch = FsSdkRegex().Match(rawTextBuffer);
|
||||||
if (fsSdkMatch.Success)
|
if (fsSdkMatch.Success)
|
||||||
{
|
{
|
||||||
stringBuilder.AppendLine($" FS SDK Version: {fsSdkMatch.Value.Replace("sdk_version: ", "")}");
|
stringBuilder.AppendLine($" FS SDK Version: {fsSdkMatch.Value.Replace("sdk_version: ", string.Empty)}");
|
||||||
}
|
}
|
||||||
|
|
||||||
MatchCollection sdkMwMatches = SdkMwRegex().Matches(rawTextBuffer);
|
MatchCollection sdkMwMatches = SdkMwRegex().Matches(rawTextBuffer);
|
||||||
|
|||||||
@@ -89,7 +89,7 @@ namespace Ryujinx.HLE.Loaders.Processes.Extensions
|
|||||||
Logger.Warning?.Print(LogClass.Ptc, "Detected unsupported ExeFs modifications. PTC disabled.");
|
Logger.Warning?.Print(LogClass.Ptc, "Detected unsupported ExeFs modifications. PTC disabled.");
|
||||||
}
|
}
|
||||||
|
|
||||||
string programName = "";
|
string programName = string.Empty;
|
||||||
|
|
||||||
if (!isHomebrew && programId > 0x010000000000FFFF)
|
if (!isHomebrew && programId > 0x010000000000FFFF)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ namespace Ryujinx.HLE.Loaders.Processes
|
|||||||
var nacpData = new BlitStruct<ApplicationControlProperty>(1);
|
var nacpData = new BlitStruct<ApplicationControlProperty>(1);
|
||||||
ulong programId = metaLoader.GetProgramId();
|
ulong programId = metaLoader.GetProgramId();
|
||||||
|
|
||||||
device.Configuration.VirtualFileSystem.ModLoader.CollectMods(new[] { programId });
|
device.Configuration.VirtualFileSystem.ModLoader.CollectMods([programId]);
|
||||||
|
|
||||||
if (programId != 0)
|
if (programId != 0)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -145,7 +145,7 @@ namespace Ryujinx.HLE.Loaders.Processes
|
|||||||
IFileSystem dummyExeFs = null;
|
IFileSystem dummyExeFs = null;
|
||||||
Stream romfsStream = null;
|
Stream romfsStream = null;
|
||||||
|
|
||||||
string programName = "";
|
string programName = string.Empty;
|
||||||
ulong programId = 0000000000000000;
|
ulong programId = 0000000000000000;
|
||||||
|
|
||||||
// Load executable.
|
// Load executable.
|
||||||
|
|||||||
@@ -255,7 +255,7 @@ namespace Ryujinx.HLE.Loaders.Processes
|
|||||||
{
|
{
|
||||||
NsoExecutable nso => Convert.ToHexString(nso.BuildId.ItemsRo.ToArray()),
|
NsoExecutable nso => Convert.ToHexString(nso.BuildId.ItemsRo.ToArray()),
|
||||||
NroExecutable nro => Convert.ToHexString(nro.Header.BuildId),
|
NroExecutable nro => Convert.ToHexString(nro.Header.BuildId),
|
||||||
_ => "",
|
_ => string.Empty
|
||||||
}).ToUpper());
|
}).ToUpper());
|
||||||
|
|
||||||
ulong[] nsoBase = new ulong[executables.Length];
|
ulong[] nsoBase = new ulong[executables.Length];
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
using Humanizer;
|
||||||
using Ryujinx.Common.Configuration;
|
using Ryujinx.Common.Configuration;
|
||||||
using Ryujinx.Common.Configuration.Hid;
|
using Ryujinx.Common.Configuration.Hid;
|
||||||
using Ryujinx.Common.Logging;
|
using Ryujinx.Common.Logging;
|
||||||
@@ -485,10 +486,10 @@ namespace Ryujinx.Headless.SDL2
|
|||||||
{
|
{
|
||||||
string playerCount = args.PlayerCountMin == args.PlayerCountMax ? $"exactly {args.PlayerCountMin}" : $"{args.PlayerCountMin}-{args.PlayerCountMax}";
|
string playerCount = args.PlayerCountMin == args.PlayerCountMax ? $"exactly {args.PlayerCountMin}" : $"{args.PlayerCountMin}-{args.PlayerCountMax}";
|
||||||
|
|
||||||
string message = $"Application requests {playerCount} player(s) with:\n\n"
|
string message = $"Application requests {playerCount} {"player".ToQuantity(args.PlayerCountMin + args.PlayerCountMax, ShowQuantityAs.None)} with:\n\n"
|
||||||
+ $"TYPES: {args.SupportedStyles}\n\n"
|
+ $"TYPES: {args.SupportedStyles}\n\n"
|
||||||
+ $"PLAYERS: {string.Join(", ", args.SupportedPlayers)}\n\n"
|
+ $"PLAYERS: {string.Join(", ", args.SupportedPlayers)}\n\n"
|
||||||
+ (args.IsDocked ? "Docked mode set. Handheld is also invalid.\n\n" : "")
|
+ (args.IsDocked ? "Docked mode set. Handheld is also invalid.\n\n" : string.Empty)
|
||||||
+ "Please reconfigure Input now and then press OK.";
|
+ "Please reconfigure Input now and then press OK.";
|
||||||
|
|
||||||
return DisplayMessageDialog("Controller Applet", message);
|
return DisplayMessageDialog("Controller Applet", message);
|
||||||
|
|||||||
@@ -1235,14 +1235,14 @@ namespace Ryujinx.Horizon.Common
|
|||||||
{ 0x412, "NotFound" },
|
{ 0x412, "NotFound" },
|
||||||
{ 0x612, "NotEnoughBuffer" },
|
{ 0x612, "NotEnoughBuffer" },
|
||||||
{ 0xCA12, "Cancelled" },
|
{ 0xCA12, "Cancelled" },
|
||||||
{ 0x7FE12, "" },
|
{ 0x7FE12, string.Empty },
|
||||||
{ 0xFA212, "" },
|
{ 0xFA212, string.Empty },
|
||||||
{ 0xFA612, "InvalidTaskId" },
|
{ 0xFA612, "InvalidTaskId" },
|
||||||
{ 0xFB612, "InvalidSize" },
|
{ 0xFB612, "InvalidSize" },
|
||||||
{ 0xFCA12, "TaskCancelled" },
|
{ 0xFCA12, "TaskCancelled" },
|
||||||
{ 0xFCC12, "TaskNotCompleted" },
|
{ 0xFCC12, "TaskNotCompleted" },
|
||||||
{ 0xFCE12, "TaskQueueNotAvailable" },
|
{ 0xFCE12, "TaskQueueNotAvailable" },
|
||||||
{ 0x106A12, "" },
|
{ 0x106A12, string.Empty },
|
||||||
{ 0x106C12, "OutOfRpcTask" },
|
{ 0x106C12, "OutOfRpcTask" },
|
||||||
{ 0x109612, "InvalidCategory" },
|
{ 0x109612, "InvalidCategory" },
|
||||||
{ 0x214, "OutOfKeyResource" },
|
{ 0x214, "OutOfKeyResource" },
|
||||||
|
|||||||
@@ -221,7 +221,7 @@ namespace Ryujinx.Horizon.Sdk.Ngc.Detail
|
|||||||
if (includeMultiWord)
|
if (includeMultiWord)
|
||||||
{
|
{
|
||||||
int lastMultiWordIndex = 0;
|
int lastMultiWordIndex = 0;
|
||||||
string multiWord = "";
|
string multiWord = string.Empty;
|
||||||
|
|
||||||
while (_multiWordMap.Has(nodePlainIndex))
|
while (_multiWordMap.Has(nodePlainIndex))
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -162,7 +162,7 @@ namespace Ryujinx.UI.App.Common
|
|||||||
NsoReader reader = new();
|
NsoReader reader = new();
|
||||||
reader.Initialize(nsoFile.Release().AsStorage().AsFile(OpenMode.Read)).ThrowIfFailure();
|
reader.Initialize(nsoFile.Release().AsStorage().AsFile(OpenMode.Read)).ThrowIfFailure();
|
||||||
|
|
||||||
return BitConverter.ToString(reader.Header.ModuleId.ItemsRo.ToArray()).Replace("-", "").ToUpper()[..16];
|
return BitConverter.ToString(reader.Header.ModuleId.ItemsRo.ToArray()).Replace("-", string.Empty).ToUpper()[..16];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -298,16 +298,6 @@ namespace Ryujinx.UI.Common.Configuration
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public string LanguageCode { get; set; }
|
public string LanguageCode { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Enable or disable custom themes in the GUI
|
|
||||||
/// </summary>
|
|
||||||
public bool EnableCustomTheme { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Path to custom GUI theme
|
|
||||||
/// </summary>
|
|
||||||
public string CustomThemePath { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Chooses the base style // Not Used
|
/// Chooses the base style // Not Used
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -144,16 +144,6 @@ namespace Ryujinx.UI.Common.Configuration
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public ReactiveObject<string> LanguageCode { get; private set; }
|
public ReactiveObject<string> LanguageCode { get; private set; }
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Enable or disable custom themes in the GUI
|
|
||||||
/// </summary>
|
|
||||||
public ReactiveObject<bool> EnableCustomTheme { get; private set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Path to custom GUI theme
|
|
||||||
/// </summary>
|
|
||||||
public ReactiveObject<string> CustomThemePath { get; private set; }
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Selects the base style
|
/// Selects the base style
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -202,8 +192,6 @@ namespace Ryujinx.UI.Common.Configuration
|
|||||||
AutoloadDirs = new ReactiveObject<List<string>>();
|
AutoloadDirs = new ReactiveObject<List<string>>();
|
||||||
ShownFileTypes = new ShownFileTypeSettings();
|
ShownFileTypes = new ShownFileTypeSettings();
|
||||||
WindowStartup = new WindowStartupSettings();
|
WindowStartup = new WindowStartupSettings();
|
||||||
EnableCustomTheme = new ReactiveObject<bool>();
|
|
||||||
CustomThemePath = new ReactiveObject<string>();
|
|
||||||
BaseStyle = new ReactiveObject<string>();
|
BaseStyle = new ReactiveObject<string>();
|
||||||
StartFullscreen = new ReactiveObject<bool>();
|
StartFullscreen = new ReactiveObject<bool>();
|
||||||
GameListViewMode = new ReactiveObject<int>();
|
GameListViewMode = new ReactiveObject<int>();
|
||||||
@@ -777,8 +765,6 @@ namespace Ryujinx.UI.Common.Configuration
|
|||||||
WindowMaximized = UI.WindowStartup.WindowMaximized,
|
WindowMaximized = UI.WindowStartup.WindowMaximized,
|
||||||
},
|
},
|
||||||
LanguageCode = UI.LanguageCode,
|
LanguageCode = UI.LanguageCode,
|
||||||
EnableCustomTheme = UI.EnableCustomTheme,
|
|
||||||
CustomThemePath = UI.CustomThemePath,
|
|
||||||
BaseStyle = UI.BaseStyle,
|
BaseStyle = UI.BaseStyle,
|
||||||
GameListViewMode = UI.GameListViewMode,
|
GameListViewMode = UI.GameListViewMode,
|
||||||
ShowNames = UI.ShowNames,
|
ShowNames = UI.ShowNames,
|
||||||
@@ -811,8 +797,8 @@ namespace Ryujinx.UI.Common.Configuration
|
|||||||
Graphics.MaxAnisotropy.Value = -1.0f;
|
Graphics.MaxAnisotropy.Value = -1.0f;
|
||||||
Graphics.AspectRatio.Value = AspectRatio.Fixed16x9;
|
Graphics.AspectRatio.Value = AspectRatio.Fixed16x9;
|
||||||
Graphics.GraphicsBackend.Value = DefaultGraphicsBackend();
|
Graphics.GraphicsBackend.Value = DefaultGraphicsBackend();
|
||||||
Graphics.PreferredGpu.Value = "";
|
Graphics.PreferredGpu.Value = string.Empty;
|
||||||
Graphics.ShadersDumpPath.Value = "";
|
Graphics.ShadersDumpPath.Value = string.Empty;
|
||||||
Logger.EnableDebug.Value = false;
|
Logger.EnableDebug.Value = false;
|
||||||
Logger.EnableStub.Value = true;
|
Logger.EnableStub.Value = true;
|
||||||
Logger.EnableInfo.Value = true;
|
Logger.EnableInfo.Value = true;
|
||||||
@@ -821,7 +807,7 @@ namespace Ryujinx.UI.Common.Configuration
|
|||||||
Logger.EnableTrace.Value = false;
|
Logger.EnableTrace.Value = false;
|
||||||
Logger.EnableGuest.Value = true;
|
Logger.EnableGuest.Value = true;
|
||||||
Logger.EnableFsAccessLog.Value = false;
|
Logger.EnableFsAccessLog.Value = false;
|
||||||
Logger.FilteredClasses.Value = Array.Empty<LogClass>();
|
Logger.FilteredClasses.Value = [];
|
||||||
Logger.GraphicsDebugLevel.Value = GraphicsDebugLevel.None;
|
Logger.GraphicsDebugLevel.Value = GraphicsDebugLevel.None;
|
||||||
System.Language.Value = Language.AmericanEnglish;
|
System.Language.Value = Language.AmericanEnglish;
|
||||||
System.Region.Value = Region.USA;
|
System.Region.Value = Region.USA;
|
||||||
@@ -868,17 +854,15 @@ namespace Ryujinx.UI.Common.Configuration
|
|||||||
UI.GuiColumns.PathColumn.Value = true;
|
UI.GuiColumns.PathColumn.Value = true;
|
||||||
UI.ColumnSort.SortColumnId.Value = 0;
|
UI.ColumnSort.SortColumnId.Value = 0;
|
||||||
UI.ColumnSort.SortAscending.Value = false;
|
UI.ColumnSort.SortAscending.Value = false;
|
||||||
UI.GameDirs.Value = new List<string>();
|
UI.GameDirs.Value = [];
|
||||||
UI.AutoloadDirs.Value = new List<string>();
|
UI.AutoloadDirs.Value = [];
|
||||||
UI.ShownFileTypes.NSP.Value = true;
|
UI.ShownFileTypes.NSP.Value = true;
|
||||||
UI.ShownFileTypes.PFS0.Value = true;
|
UI.ShownFileTypes.PFS0.Value = true;
|
||||||
UI.ShownFileTypes.XCI.Value = true;
|
UI.ShownFileTypes.XCI.Value = true;
|
||||||
UI.ShownFileTypes.NCA.Value = true;
|
UI.ShownFileTypes.NCA.Value = true;
|
||||||
UI.ShownFileTypes.NRO.Value = true;
|
UI.ShownFileTypes.NRO.Value = true;
|
||||||
UI.ShownFileTypes.NSO.Value = true;
|
UI.ShownFileTypes.NSO.Value = true;
|
||||||
UI.EnableCustomTheme.Value = true;
|
|
||||||
UI.LanguageCode.Value = "en_US";
|
UI.LanguageCode.Value = "en_US";
|
||||||
UI.CustomThemePath.Value = "";
|
|
||||||
UI.BaseStyle.Value = "Dark";
|
UI.BaseStyle.Value = "Dark";
|
||||||
UI.GameListViewMode.Value = 0;
|
UI.GameListViewMode.Value = 0;
|
||||||
UI.ShowNames.Value = true;
|
UI.ShowNames.Value = true;
|
||||||
@@ -1628,9 +1612,7 @@ namespace Ryujinx.UI.Common.Configuration
|
|||||||
UI.ShownFileTypes.NCA.Value = configurationFileFormat.ShownFileTypes.NCA;
|
UI.ShownFileTypes.NCA.Value = configurationFileFormat.ShownFileTypes.NCA;
|
||||||
UI.ShownFileTypes.NRO.Value = configurationFileFormat.ShownFileTypes.NRO;
|
UI.ShownFileTypes.NRO.Value = configurationFileFormat.ShownFileTypes.NRO;
|
||||||
UI.ShownFileTypes.NSO.Value = configurationFileFormat.ShownFileTypes.NSO;
|
UI.ShownFileTypes.NSO.Value = configurationFileFormat.ShownFileTypes.NSO;
|
||||||
UI.EnableCustomTheme.Value = configurationFileFormat.EnableCustomTheme;
|
|
||||||
UI.LanguageCode.Value = configurationFileFormat.LanguageCode;
|
UI.LanguageCode.Value = configurationFileFormat.LanguageCode;
|
||||||
UI.CustomThemePath.Value = configurationFileFormat.CustomThemePath;
|
|
||||||
UI.BaseStyle.Value = configurationFileFormat.BaseStyle;
|
UI.BaseStyle.Value = configurationFileFormat.BaseStyle;
|
||||||
UI.GameListViewMode.Value = configurationFileFormat.GameListViewMode;
|
UI.GameListViewMode.Value = configurationFileFormat.GameListViewMode;
|
||||||
UI.ShowNames.Value = configurationFileFormat.ShowNames;
|
UI.ShowNames.Value = configurationFileFormat.ShowNames;
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ namespace Ryujinx.UI.Common.Helper
|
|||||||
if ((uninstall && AreMimeTypesRegisteredLinux()) || (!uninstall && !AreMimeTypesRegisteredLinux()))
|
if ((uninstall && AreMimeTypesRegisteredLinux()) || (!uninstall && !AreMimeTypesRegisteredLinux()))
|
||||||
{
|
{
|
||||||
string mimeTypesFile = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "mime", "Ryujinx.xml");
|
string mimeTypesFile = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "mime", "Ryujinx.xml");
|
||||||
string additionalArgs = !uninstall ? "--novendor" : "";
|
string additionalArgs = !uninstall ? "--novendor" : string.Empty;
|
||||||
|
|
||||||
using Process mimeProcess = new();
|
using Process mimeProcess = new();
|
||||||
|
|
||||||
@@ -83,7 +83,7 @@ namespace Ryujinx.UI.Common.Helper
|
|||||||
|
|
||||||
var openCmd = key.OpenSubKey(@"shell\open\command");
|
var openCmd = key.OpenSubKey(@"shell\open\command");
|
||||||
|
|
||||||
string keyValue = (string)openCmd.GetValue("");
|
string keyValue = (string)openCmd.GetValue(string.Empty);
|
||||||
|
|
||||||
return keyValue is not null && (keyValue.Contains("Ryujinx") || keyValue.Contains(AppDomain.CurrentDomain.FriendlyName));
|
return keyValue is not null && (keyValue.Contains("Ryujinx") || keyValue.Contains(AppDomain.CurrentDomain.FriendlyName));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,18 +12,11 @@ namespace Ryujinx.UI.Common.Helper
|
|||||||
{
|
{
|
||||||
public static bool IsFirmwareValid(ContentManager contentManager, out UserError error)
|
public static bool IsFirmwareValid(ContentManager contentManager, out UserError error)
|
||||||
{
|
{
|
||||||
bool hasFirmware = contentManager.GetCurrentFirmwareVersion() != null;
|
error = contentManager.GetCurrentFirmwareVersion() != null
|
||||||
|
? UserError.Success
|
||||||
|
: UserError.NoFirmware;
|
||||||
|
|
||||||
if (hasFirmware)
|
return error is UserError.Success;
|
||||||
{
|
|
||||||
error = UserError.Success;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
error = UserError.NoFirmware;
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static bool CanFixStartApplication(ContentManager contentManager, string baseApplicationPath, UserError error, out SystemVersion firmwareVersion)
|
public static bool CanFixStartApplication(ContentManager contentManager, string baseApplicationPath, UserError error, out SystemVersion firmwareVersion)
|
||||||
@@ -95,14 +88,18 @@ namespace Ryujinx.UI.Common.Helper
|
|||||||
string baseApplicationExtension = Path.GetExtension(baseApplicationPath).ToLowerInvariant();
|
string baseApplicationExtension = Path.GetExtension(baseApplicationPath).ToLowerInvariant();
|
||||||
|
|
||||||
// NOTE: We don't force homebrew developers to install a system firmware.
|
// NOTE: We don't force homebrew developers to install a system firmware.
|
||||||
if (baseApplicationExtension is not (".nro" or ".nso"))
|
if (baseApplicationExtension is ".nro" or ".nso")
|
||||||
return IsFirmwareValid(contentManager, out error);
|
{
|
||||||
|
error = UserError.Success;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return IsFirmwareValid(contentManager, out error);
|
||||||
}
|
}
|
||||||
|
|
||||||
error = UserError.ApplicationNotFound;
|
error = UserError.ApplicationNotFound;
|
||||||
|
|
||||||
return error is UserError.Success;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -53,7 +53,7 @@ namespace Ryujinx.UI.Common.Helper
|
|||||||
{
|
{
|
||||||
var titleUpdateWindowData = new TitleUpdateMetadata
|
var titleUpdateWindowData = new TitleUpdateMetadata
|
||||||
{
|
{
|
||||||
Selected = "",
|
Selected = string.Empty,
|
||||||
Paths = [],
|
Paths = [],
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
55
src/Ryujinx.UI.Common/Models/XCITrimmerFileModel.cs
Normal file
55
src/Ryujinx.UI.Common/Models/XCITrimmerFileModel.cs
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
using Ryujinx.Common.Logging;
|
||||||
|
using Ryujinx.Common.Utilities;
|
||||||
|
using Ryujinx.UI.App.Common;
|
||||||
|
|
||||||
|
namespace Ryujinx.UI.Common.Models
|
||||||
|
{
|
||||||
|
public record XCITrimmerFileModel(
|
||||||
|
string Name,
|
||||||
|
string Path,
|
||||||
|
bool Trimmable,
|
||||||
|
bool Untrimmable,
|
||||||
|
long PotentialSavingsB,
|
||||||
|
long CurrentSavingsB,
|
||||||
|
int? PercentageProgress,
|
||||||
|
XCIFileTrimmer.OperationOutcome ProcessingOutcome)
|
||||||
|
{
|
||||||
|
public static XCITrimmerFileModel FromApplicationData(ApplicationData applicationData, XCIFileTrimmerLog logger)
|
||||||
|
{
|
||||||
|
var trimmer = new XCIFileTrimmer(applicationData.Path, logger);
|
||||||
|
|
||||||
|
return new XCITrimmerFileModel(
|
||||||
|
applicationData.Name,
|
||||||
|
applicationData.Path,
|
||||||
|
trimmer.CanBeTrimmed,
|
||||||
|
trimmer.CanBeUntrimmed,
|
||||||
|
trimmer.DiskSpaceSavingsB,
|
||||||
|
trimmer.DiskSpaceSavedB,
|
||||||
|
null,
|
||||||
|
XCIFileTrimmer.OperationOutcome.Undetermined
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsFailed
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return ProcessingOutcome != XCIFileTrimmer.OperationOutcome.Undetermined &&
|
||||||
|
ProcessingOutcome != XCIFileTrimmer.OperationOutcome.Successful;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual bool Equals(XCITrimmerFileModel obj)
|
||||||
|
{
|
||||||
|
if (obj == null)
|
||||||
|
return false;
|
||||||
|
else
|
||||||
|
return this.Path == obj.Path;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override int GetHashCode()
|
||||||
|
{
|
||||||
|
return this.Path.GetHashCode();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -51,7 +51,6 @@
|
|||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="DiscordRichPresence" />
|
<PackageReference Include="DiscordRichPresence" />
|
||||||
<PackageReference Include="DynamicData" />
|
<PackageReference Include="DynamicData" />
|
||||||
<PackageReference Include="Humanizer" />
|
|
||||||
<PackageReference Include="securifybv.ShellLink" />
|
<PackageReference Include="securifybv.ShellLink" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ namespace Ryujinx.UI.LocaleGenerator
|
|||||||
|
|
||||||
context.RegisterSourceOutput(contents, (spc, content) =>
|
context.RegisterSourceOutput(contents, (spc, content) =>
|
||||||
{
|
{
|
||||||
var lines = content.Split('\n').Where(x => x.Trim().StartsWith("\"")).Select(x => x.Split(':')[0].Trim().Replace("\"", ""));
|
var lines = content.Split('\n').Where(x => x.Trim().StartsWith("\"")).Select(x => x.Split(':')[0].Trim().Replace("\"", string.Empty));
|
||||||
StringBuilder enumSourceBuilder = new();
|
StringBuilder enumSourceBuilder = new();
|
||||||
enumSourceBuilder.AppendLine("namespace Ryujinx.Ava.Common.Locale;");
|
enumSourceBuilder.AppendLine("namespace Ryujinx.Ava.Common.Locale;");
|
||||||
enumSourceBuilder.AppendLine("internal enum LocaleKeys");
|
enumSourceBuilder.AppendLine("internal enum LocaleKeys");
|
||||||
|
|||||||
@@ -58,11 +58,9 @@ namespace Ryujinx.Ava
|
|||||||
|
|
||||||
if (Program.PreviewerDetached)
|
if (Program.PreviewerDetached)
|
||||||
{
|
{
|
||||||
ApplyConfiguredTheme();
|
ApplyConfiguredTheme(ConfigurationState.Instance.UI.BaseStyle);
|
||||||
|
|
||||||
ConfigurationState.Instance.UI.BaseStyle.Event += ThemeChanged_Event;
|
ConfigurationState.Instance.UI.BaseStyle.Event += ThemeChanged_Event;
|
||||||
ConfigurationState.Instance.UI.CustomThemePath.Event += ThemeChanged_Event;
|
|
||||||
ConfigurationState.Instance.UI.EnableCustomTheme.Event += CustomThemeChanged_Event;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -88,17 +86,13 @@ namespace Ryujinx.Ava
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void CustomThemeChanged_Event(object _, ReactiveEventArgs<bool> __) => ApplyConfiguredTheme();
|
|
||||||
|
|
||||||
private void ThemeChanged_Event(object _, ReactiveEventArgs<string> __) => ApplyConfiguredTheme();
|
private void ThemeChanged_Event(object _, ReactiveEventArgs<string> rArgs) => ApplyConfiguredTheme(rArgs.NewValue);
|
||||||
|
|
||||||
public void ApplyConfiguredTheme()
|
public void ApplyConfiguredTheme(string baseStyle)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
string baseStyle = ConfigurationState.Instance.UI.BaseStyle;
|
|
||||||
|
|
||||||
if (string.IsNullOrWhiteSpace(baseStyle))
|
if (string.IsNullOrWhiteSpace(baseStyle))
|
||||||
{
|
{
|
||||||
ConfigurationState.Instance.UI.BaseStyle.Value = "Auto";
|
ConfigurationState.Instance.UI.BaseStyle.Value = "Auto";
|
||||||
|
|||||||
@@ -604,61 +604,59 @@ namespace Ryujinx.Ava
|
|||||||
|
|
||||||
SystemVersion firmwareVersion = ContentManager.GetCurrentFirmwareVersion();
|
SystemVersion firmwareVersion = ContentManager.GetCurrentFirmwareVersion();
|
||||||
|
|
||||||
if (Application.Current.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
|
if (Application.Current?.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime)
|
||||||
{
|
{
|
||||||
if (!SetupValidator.CanStartApplication(ContentManager, ApplicationPath, out UserError userError))
|
if (!SetupValidator.CanStartApplication(ContentManager, ApplicationPath, out UserError userError))
|
||||||
{
|
{
|
||||||
|
if (SetupValidator.CanFixStartApplication(ContentManager, ApplicationPath, userError, out firmwareVersion))
|
||||||
{
|
{
|
||||||
if (SetupValidator.CanFixStartApplication(ContentManager, ApplicationPath, userError, out firmwareVersion))
|
if (userError is UserError.NoFirmware)
|
||||||
{
|
{
|
||||||
if (userError == UserError.NoFirmware)
|
UserResult result = await ContentDialogHelper.CreateConfirmationDialog(
|
||||||
{
|
LocaleManager.Instance[LocaleKeys.DialogFirmwareNoFirmwareInstalledMessage],
|
||||||
UserResult result = await ContentDialogHelper.CreateConfirmationDialog(
|
LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DialogFirmwareInstallEmbeddedMessage, firmwareVersion.VersionString),
|
||||||
LocaleManager.Instance[LocaleKeys.DialogFirmwareNoFirmwareInstalledMessage],
|
LocaleManager.Instance[LocaleKeys.InputDialogYes],
|
||||||
LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DialogFirmwareInstallEmbeddedMessage, firmwareVersion.VersionString),
|
LocaleManager.Instance[LocaleKeys.InputDialogNo],
|
||||||
LocaleManager.Instance[LocaleKeys.InputDialogYes],
|
string.Empty);
|
||||||
LocaleManager.Instance[LocaleKeys.InputDialogNo],
|
|
||||||
"");
|
|
||||||
|
|
||||||
if (result != UserResult.Yes)
|
if (result != UserResult.Yes)
|
||||||
{
|
|
||||||
await UserErrorDialog.ShowUserErrorDialog(userError);
|
|
||||||
Device.Dispose();
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!SetupValidator.TryFixStartApplication(ContentManager, ApplicationPath, userError, out _))
|
|
||||||
{
|
{
|
||||||
await UserErrorDialog.ShowUserErrorDialog(userError);
|
await UserErrorDialog.ShowUserErrorDialog(userError);
|
||||||
Device.Dispose();
|
Device.Dispose();
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Tell the user that we installed a firmware for them.
|
|
||||||
if (userError == UserError.NoFirmware)
|
|
||||||
{
|
|
||||||
firmwareVersion = ContentManager.GetCurrentFirmwareVersion();
|
|
||||||
|
|
||||||
_viewModel.RefreshFirmwareStatus();
|
|
||||||
|
|
||||||
await ContentDialogHelper.CreateInfoDialog(
|
|
||||||
LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DialogFirmwareInstalledMessage, firmwareVersion.VersionString),
|
|
||||||
LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DialogFirmwareInstallEmbeddedSuccessMessage, firmwareVersion.VersionString),
|
|
||||||
LocaleManager.Instance[LocaleKeys.InputDialogOk],
|
|
||||||
"",
|
|
||||||
LocaleManager.Instance[LocaleKeys.RyujinxInfo]);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else
|
|
||||||
|
if (!SetupValidator.TryFixStartApplication(ContentManager, ApplicationPath, userError, out _))
|
||||||
{
|
{
|
||||||
await UserErrorDialog.ShowUserErrorDialog(userError);
|
await UserErrorDialog.ShowUserErrorDialog(userError);
|
||||||
Device.Dispose();
|
Device.Dispose();
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Tell the user that we installed a firmware for them.
|
||||||
|
if (userError is UserError.NoFirmware)
|
||||||
|
{
|
||||||
|
firmwareVersion = ContentManager.GetCurrentFirmwareVersion();
|
||||||
|
|
||||||
|
_viewModel.RefreshFirmwareStatus();
|
||||||
|
|
||||||
|
await ContentDialogHelper.CreateInfoDialog(
|
||||||
|
LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DialogFirmwareInstalledMessage, firmwareVersion.VersionString),
|
||||||
|
LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DialogFirmwareInstallEmbeddedSuccessMessage, firmwareVersion.VersionString),
|
||||||
|
LocaleManager.Instance[LocaleKeys.InputDialogOk],
|
||||||
|
string.Empty,
|
||||||
|
LocaleManager.Instance[LocaleKeys.RyujinxInfo]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
await UserErrorDialog.ShowUserErrorDialog(userError);
|
||||||
|
Device.Dispose();
|
||||||
|
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -820,20 +818,12 @@ namespace Ryujinx.Ava
|
|||||||
VirtualFileSystem.ReloadKeySet();
|
VirtualFileSystem.ReloadKeySet();
|
||||||
|
|
||||||
// Initialize Renderer.
|
// Initialize Renderer.
|
||||||
IRenderer renderer;
|
IRenderer renderer = ConfigurationState.Instance.Graphics.GraphicsBackend.Value == GraphicsBackend.OpenGl
|
||||||
|
? new OpenGLRenderer()
|
||||||
if (ConfigurationState.Instance.Graphics.GraphicsBackend.Value == GraphicsBackend.Vulkan)
|
: VulkanRenderer.Create(
|
||||||
{
|
ConfigurationState.Instance.Graphics.PreferredGpu,
|
||||||
renderer = new VulkanRenderer(
|
|
||||||
Vk.GetApi(),
|
|
||||||
(RendererHost.EmbeddedWindow as EmbeddedWindowVulkan)!.CreateSurface,
|
(RendererHost.EmbeddedWindow as EmbeddedWindowVulkan)!.CreateSurface,
|
||||||
VulkanHelper.GetRequiredInstanceExtensions,
|
VulkanHelper.GetRequiredInstanceExtensions);
|
||||||
ConfigurationState.Instance.Graphics.PreferredGpu.Value);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
renderer = new OpenGLRenderer();
|
|
||||||
}
|
|
||||||
|
|
||||||
BackendThreading threadingMode = ConfigurationState.Instance.Graphics.BackendThreading;
|
BackendThreading threadingMode = ConfigurationState.Instance.Graphics.BackendThreading;
|
||||||
|
|
||||||
|
|||||||
@@ -33,6 +33,7 @@
|
|||||||
"MenuBarToolsManageFileTypes": "Manage file types",
|
"MenuBarToolsManageFileTypes": "Manage file types",
|
||||||
"MenuBarToolsInstallFileTypes": "Install file types",
|
"MenuBarToolsInstallFileTypes": "Install file types",
|
||||||
"MenuBarToolsUninstallFileTypes": "Uninstall file types",
|
"MenuBarToolsUninstallFileTypes": "Uninstall file types",
|
||||||
|
"MenuBarToolsXCITrimmer": "Trim XCI Files",
|
||||||
"MenuBarView": "_View",
|
"MenuBarView": "_View",
|
||||||
"MenuBarViewWindow": "Window Size",
|
"MenuBarViewWindow": "Window Size",
|
||||||
"MenuBarViewWindow720": "720p",
|
"MenuBarViewWindow720": "720p",
|
||||||
@@ -84,8 +85,11 @@
|
|||||||
"GameListContextMenuOpenModsDirectoryToolTip": "Opens the directory which contains Application's Mods",
|
"GameListContextMenuOpenModsDirectoryToolTip": "Opens the directory which contains Application's Mods",
|
||||||
"GameListContextMenuOpenSdModsDirectory": "Open Atmosphere Mods Directory",
|
"GameListContextMenuOpenSdModsDirectory": "Open Atmosphere Mods Directory",
|
||||||
"GameListContextMenuOpenSdModsDirectoryToolTip": "Opens the alternative SD card Atmosphere directory which contains Application's Mods. Useful for mods that are packaged for real hardware.",
|
"GameListContextMenuOpenSdModsDirectoryToolTip": "Opens the alternative SD card Atmosphere directory which contains Application's Mods. Useful for mods that are packaged for real hardware.",
|
||||||
|
"GameListContextMenuTrimXCI": "Check and Trim XCI File",
|
||||||
|
"GameListContextMenuTrimXCIToolTip": "Check and Trim XCI File to Save Disk Space",
|
||||||
"StatusBarGamesLoaded": "{0}/{1} Games Loaded",
|
"StatusBarGamesLoaded": "{0}/{1} Games Loaded",
|
||||||
"StatusBarSystemVersion": "System Version: {0}",
|
"StatusBarSystemVersion": "System Version: {0}",
|
||||||
|
"StatusBarXCIFileTrimming": "Trimming XCI File '{0}'",
|
||||||
"LinuxVmMaxMapCountDialogTitle": "Low limit for memory mappings detected",
|
"LinuxVmMaxMapCountDialogTitle": "Low limit for memory mappings detected",
|
||||||
"LinuxVmMaxMapCountDialogTextPrimary": "Would you like to increase the value of vm.max_map_count to {0}",
|
"LinuxVmMaxMapCountDialogTextPrimary": "Would you like to increase the value of vm.max_map_count to {0}",
|
||||||
"LinuxVmMaxMapCountDialogTextSecondary": "Some games might try to create more memory mappings than currently allowed. Ryujinx will crash as soon as this limit gets exceeded.",
|
"LinuxVmMaxMapCountDialogTextSecondary": "Some games might try to create more memory mappings than currently allowed. Ryujinx will crash as soon as this limit gets exceeded.",
|
||||||
@@ -400,6 +404,8 @@
|
|||||||
"InputDialogTitle": "Input Dialog",
|
"InputDialogTitle": "Input Dialog",
|
||||||
"InputDialogOk": "OK",
|
"InputDialogOk": "OK",
|
||||||
"InputDialogCancel": "Cancel",
|
"InputDialogCancel": "Cancel",
|
||||||
|
"InputDialogCancelling": "Cancelling",
|
||||||
|
"InputDialogClose": "Close",
|
||||||
"InputDialogAddNewProfileTitle": "Choose the Profile Name",
|
"InputDialogAddNewProfileTitle": "Choose the Profile Name",
|
||||||
"InputDialogAddNewProfileHeader": "Please Enter a Profile Name",
|
"InputDialogAddNewProfileHeader": "Please Enter a Profile Name",
|
||||||
"InputDialogAddNewProfileSubtext": "(Max Length: {0})",
|
"InputDialogAddNewProfileSubtext": "(Max Length: {0})",
|
||||||
@@ -439,13 +445,13 @@
|
|||||||
"DialogNcaExtractionMessage": "Extracting {0} section from {1}...",
|
"DialogNcaExtractionMessage": "Extracting {0} section from {1}...",
|
||||||
"DialogNcaExtractionTitle": "Ryujinx - NCA Section Extractor",
|
"DialogNcaExtractionTitle": "Ryujinx - NCA Section Extractor",
|
||||||
"DialogNcaExtractionMainNcaNotFoundErrorMessage": "Extraction failure. The main NCA was not present in the selected file.",
|
"DialogNcaExtractionMainNcaNotFoundErrorMessage": "Extraction failure. The main NCA was not present in the selected file.",
|
||||||
"DialogNcaExtractionCheckLogErrorMessage": "Extraction failure. Read the log file for further information.",
|
"DialogNcaExtractionCheckLogErrorMessage": "Extraction failed. Please check the log file for more details.",
|
||||||
"DialogNcaExtractionSuccessMessage": "Extraction completed successfully.",
|
"DialogNcaExtractionSuccessMessage": "Extraction completed successfully.",
|
||||||
"DialogUpdaterConvertFailedMessage": "Failed to convert the current Ryujinx version.",
|
"DialogUpdaterConvertFailedMessage": "Unable to convert the current Ryujinx version.",
|
||||||
"DialogUpdaterCancelUpdateMessage": "Cancelling Update!",
|
"DialogUpdaterCancelUpdateMessage": "Update canceled!",
|
||||||
"DialogUpdaterAlreadyOnLatestVersionMessage": "You are already using the most updated version of Ryujinx!",
|
"DialogUpdaterAlreadyOnLatestVersionMessage": "You are already using the latest version of Ryujinx!",
|
||||||
"DialogUpdaterFailedToGetVersionMessage": "An error has occurred when trying to get release information from GitHub Release. This can be caused if a new release is being compiled by GitHub Actions. Try again in a few minutes.",
|
"DialogUpdaterFailedToGetVersionMessage": "An error occurred while trying to retrieve release information from GitHub. This may happen if a new release is currently being compiled by GitHub Actions. Please try again in a few minutes.",
|
||||||
"DialogUpdaterConvertFailedGithubMessage": "Failed to convert the received Ryujinx version from Github Release.",
|
"DialogUpdaterConvertFailedGithubMessage": "Failed to convert the Ryujinx version received from GitHub."
|
||||||
"DialogUpdaterDownloadingMessage": "Downloading Update...",
|
"DialogUpdaterDownloadingMessage": "Downloading Update...",
|
||||||
"DialogUpdaterExtractionMessage": "Extracting Update...",
|
"DialogUpdaterExtractionMessage": "Extracting Update...",
|
||||||
"DialogUpdaterRenamingMessage": "Renaming Update...",
|
"DialogUpdaterRenamingMessage": "Renaming Update...",
|
||||||
@@ -455,7 +461,7 @@
|
|||||||
"DialogUpdaterNoInternetMessage": "You are not connected to the Internet!",
|
"DialogUpdaterNoInternetMessage": "You are not connected to the Internet!",
|
||||||
"DialogUpdaterNoInternetSubMessage": "Please verify that you have a working Internet connection!",
|
"DialogUpdaterNoInternetSubMessage": "Please verify that you have a working Internet connection!",
|
||||||
"DialogUpdaterDirtyBuildMessage": "You Cannot update a Dirty build of Ryujinx!",
|
"DialogUpdaterDirtyBuildMessage": "You Cannot update a Dirty build of Ryujinx!",
|
||||||
"DialogUpdaterDirtyBuildSubMessage": "Please download Ryujinx at https://https://github.com/GreemDev/Ryujinx/releases/ if you are looking for a supported version.",
|
"DialogUpdaterDirtyBuildSubMessage": "Please download Ryujinx at https://github.com/GreemDev/Ryujinx/releases/ if you are looking for a supported version.",
|
||||||
"DialogRestartRequiredMessage": "Restart Required",
|
"DialogRestartRequiredMessage": "Restart Required",
|
||||||
"DialogThemeRestartMessage": "Theme has been saved. A restart is needed to apply the theme.",
|
"DialogThemeRestartMessage": "Theme has been saved. A restart is needed to apply the theme.",
|
||||||
"DialogThemeRestartSubMessage": "Do you want to restart",
|
"DialogThemeRestartSubMessage": "Do you want to restart",
|
||||||
@@ -468,6 +474,7 @@
|
|||||||
"DialogUninstallFileTypesSuccessMessage": "Successfully uninstalled file types!",
|
"DialogUninstallFileTypesSuccessMessage": "Successfully uninstalled file types!",
|
||||||
"DialogUninstallFileTypesErrorMessage": "Failed to uninstall file types.",
|
"DialogUninstallFileTypesErrorMessage": "Failed to uninstall file types.",
|
||||||
"DialogOpenSettingsWindowLabel": "Open Settings Window",
|
"DialogOpenSettingsWindowLabel": "Open Settings Window",
|
||||||
|
"DialogOpenXCITrimmerWindowLabel": "XCI Trimmer Window",
|
||||||
"DialogControllerAppletTitle": "Controller Applet",
|
"DialogControllerAppletTitle": "Controller Applet",
|
||||||
"DialogMessageDialogErrorExceptionMessage": "Error displaying Message Dialog: {0}",
|
"DialogMessageDialogErrorExceptionMessage": "Error displaying Message Dialog: {0}",
|
||||||
"DialogSoftwareKeyboardErrorExceptionMessage": "Error displaying Software Keyboard: {0}",
|
"DialogSoftwareKeyboardErrorExceptionMessage": "Error displaying Software Keyboard: {0}",
|
||||||
@@ -670,6 +677,12 @@
|
|||||||
"TitleUpdateVersionLabel": "Version {0}",
|
"TitleUpdateVersionLabel": "Version {0}",
|
||||||
"TitleBundledUpdateVersionLabel": "Bundled: Version {0}",
|
"TitleBundledUpdateVersionLabel": "Bundled: Version {0}",
|
||||||
"TitleBundledDlcLabel": "Bundled:",
|
"TitleBundledDlcLabel": "Bundled:",
|
||||||
|
"TitleXCIStatusPartialLabel": "Partial",
|
||||||
|
"TitleXCIStatusTrimmableLabel": "Untrimmed",
|
||||||
|
"TitleXCIStatusUntrimmableLabel": "Trimmed",
|
||||||
|
"TitleXCIStatusFailedLabel": "(Failed)",
|
||||||
|
"TitleXCICanSaveLabel": "Save {0:n0} Mb",
|
||||||
|
"TitleXCISavingLabel": "Saved {0:n0} Mb",
|
||||||
"RyujinxInfo": "Ryujinx - Info",
|
"RyujinxInfo": "Ryujinx - Info",
|
||||||
"RyujinxConfirm": "Ryujinx - Confirmation",
|
"RyujinxConfirm": "Ryujinx - Confirmation",
|
||||||
"FileDialogAllTypes": "All types",
|
"FileDialogAllTypes": "All types",
|
||||||
@@ -722,11 +735,37 @@
|
|||||||
"SelectDlcDialogTitle": "Select DLC files",
|
"SelectDlcDialogTitle": "Select DLC files",
|
||||||
"SelectUpdateDialogTitle": "Select update files",
|
"SelectUpdateDialogTitle": "Select update files",
|
||||||
"SelectModDialogTitle": "Select mod directory",
|
"SelectModDialogTitle": "Select mod directory",
|
||||||
|
"TrimXCIFileDialogTitle": "Check and Trim XCI File",
|
||||||
|
"TrimXCIFileDialogPrimaryText": "This function will first check the empty space and then trim the XCI File to save disk space.",
|
||||||
|
"TrimXCIFileDialogSecondaryText": "Current File Size: {0:n} MB\nGame Data Size: {1:n} MB\nDisk Space Savings: {2:n} MB",
|
||||||
|
"TrimXCIFileNoTrimNecessary": "XCI File does not need to be trimmed. Check logs for further details",
|
||||||
|
"TrimXCIFileNoUntrimPossible": "XCI File cannot be untrimmed. Check logs for further details",
|
||||||
|
"TrimXCIFileReadOnlyFileCannotFix": "XCI File is Read Only and could not be made writable. Check logs for further details",
|
||||||
|
"TrimXCIFileFileSizeChanged": "XCI File has changed in size since it was scanned. Please check the file is not being written to and try again.",
|
||||||
|
"TrimXCIFileFreeSpaceCheckFailed": "XCI File has data in the free space area, it is not safe to trim",
|
||||||
|
"TrimXCIFileInvalidXCIFile": "XCI File contains invalid data. Check logs for further details",
|
||||||
|
"TrimXCIFileFileIOWriteError": "XCI File could not be opened for writing. Check logs for further details",
|
||||||
|
"TrimXCIFileFailedPrimaryText": "Trimming of the XCI file failed",
|
||||||
|
"TrimXCIFileCancelled": "The operation was cancelled",
|
||||||
|
"TrimXCIFileFileUndertermined": "No operation was performed",
|
||||||
"UserProfileWindowTitle": "User Profiles Manager",
|
"UserProfileWindowTitle": "User Profiles Manager",
|
||||||
"CheatWindowTitle": "Cheats Manager",
|
"CheatWindowTitle": "Cheats Manager",
|
||||||
"DlcWindowTitle": "Manage Downloadable Content for {0} ({1})",
|
"DlcWindowTitle": "Manage Downloadable Content for {0} ({1})",
|
||||||
"ModWindowTitle": "Manage Mods for {0} ({1})",
|
"ModWindowTitle": "Manage Mods for {0} ({1})",
|
||||||
"UpdateWindowTitle": "Title Update Manager",
|
"UpdateWindowTitle": "Title Update Manager",
|
||||||
|
"XCITrimmerWindowTitle": "XCI File Trimmer",
|
||||||
|
"XCITrimmerTitleStatusCount": "{0} of {1} Title(s) Selected",
|
||||||
|
"XCITrimmerTitleStatusCountWithFilter": "{0} of {1} Title(s) Selected ({2} displayed)",
|
||||||
|
"XCITrimmerTitleStatusTrimming": "Trimming {0} Title(s)...",
|
||||||
|
"XCITrimmerTitleStatusUntrimming": "Untrimming {0} Title(s)...",
|
||||||
|
"XCITrimmerTitleStatusFailed": "Failed",
|
||||||
|
"XCITrimmerPotentialSavings": "Potential Savings",
|
||||||
|
"XCITrimmerActualSavings": "Actual Savings",
|
||||||
|
"XCITrimmerSavingsMb": "{0:n0} Mb",
|
||||||
|
"XCITrimmerSelectDisplayed": "Select Shown",
|
||||||
|
"XCITrimmerDeselectDisplayed": "Deselect Shown",
|
||||||
|
"XCITrimmerSortName": "Title",
|
||||||
|
"XCITrimmerSortSaved": "Space Savings",
|
||||||
"UpdateWindowUpdateAddedMessage": "{0} new update(s) added",
|
"UpdateWindowUpdateAddedMessage": "{0} new update(s) added",
|
||||||
"UpdateWindowBundledContentNotice": "Bundled updates cannot be removed, only disabled.",
|
"UpdateWindowBundledContentNotice": "Bundled updates cannot be removed, only disabled.",
|
||||||
"CheatWindowHeading": "Cheats Available for {0} [{1}]",
|
"CheatWindowHeading": "Cheats Available for {0} [{1}]",
|
||||||
@@ -740,6 +779,7 @@
|
|||||||
"AutoloadUpdateRemovedMessage": "{0} missing update(s) removed",
|
"AutoloadUpdateRemovedMessage": "{0} missing update(s) removed",
|
||||||
"ModWindowHeading": "{0} Mod(s)",
|
"ModWindowHeading": "{0} Mod(s)",
|
||||||
"UserProfilesEditProfile": "Edit Selected",
|
"UserProfilesEditProfile": "Edit Selected",
|
||||||
|
"Continue": "Continue",
|
||||||
"Cancel": "Cancel",
|
"Cancel": "Cancel",
|
||||||
"Save": "Save",
|
"Save": "Save",
|
||||||
"Discard": "Discard",
|
"Discard": "Discard",
|
||||||
|
|||||||
@@ -43,6 +43,10 @@
|
|||||||
</StackPanel>
|
</StackPanel>
|
||||||
</Border>
|
</Border>
|
||||||
</Design.PreviewWith>
|
</Design.PreviewWith>
|
||||||
|
<Style Selector="DropDownButton">
|
||||||
|
<Setter Property="FontSize"
|
||||||
|
Value="12" />
|
||||||
|
</Style>
|
||||||
<Style Selector="Border.small">
|
<Style Selector="Border.small">
|
||||||
<Setter Property="Width"
|
<Setter Property="Width"
|
||||||
Value="100" />
|
Value="100" />
|
||||||
@@ -231,6 +235,14 @@
|
|||||||
<Setter Property="MinWidth"
|
<Setter Property="MinWidth"
|
||||||
Value="80" />
|
Value="80" />
|
||||||
</Style>
|
</Style>
|
||||||
|
<Style Selector="ProgressBar:horizontal">
|
||||||
|
<Setter Property="MinWidth" Value="0"/>
|
||||||
|
<Setter Property="MinHeight" Value="0"/>
|
||||||
|
</Style>
|
||||||
|
<Style Selector="ProgressBar:vertical">
|
||||||
|
<Setter Property="MinWidth" Value="0"/>
|
||||||
|
<Setter Property="MinHeight" Value="0"/>
|
||||||
|
</Style>
|
||||||
<Style Selector="ProgressBar /template/ Border#ProgressBarTrack">
|
<Style Selector="ProgressBar /template/ Border#ProgressBarTrack">
|
||||||
<Setter Property="IsVisible"
|
<Setter Property="IsVisible"
|
||||||
Value="False" />
|
Value="False" />
|
||||||
@@ -389,7 +401,7 @@
|
|||||||
<x:Double x:Key="ControlContentThemeFontSize">13</x:Double>
|
<x:Double x:Key="ControlContentThemeFontSize">13</x:Double>
|
||||||
<x:Double x:Key="MenuItemHeight">26</x:Double>
|
<x:Double x:Key="MenuItemHeight">26</x:Double>
|
||||||
<x:Double x:Key="TabItemMinHeight">28</x:Double>
|
<x:Double x:Key="TabItemMinHeight">28</x:Double>
|
||||||
<x:Double x:Key="ContentDialogMaxWidth">600</x:Double>
|
<x:Double x:Key="ContentDialogMaxWidth">700</x:Double>
|
||||||
<x:Double x:Key="ContentDialogMaxHeight">756</x:Double>
|
<x:Double x:Key="ContentDialogMaxHeight">756</x:Double>
|
||||||
</Styles.Resources>
|
</Styles.Resources>
|
||||||
</Styles>
|
</Styles>
|
||||||
|
|||||||
@@ -1,28 +0,0 @@
|
|||||||
using Avalonia.Data.Core;
|
|
||||||
using Avalonia.Markup.Xaml;
|
|
||||||
using Avalonia.Markup.Xaml.MarkupExtensions;
|
|
||||||
using Avalonia.Markup.Xaml.MarkupExtensions.CompiledBindings;
|
|
||||||
using System;
|
|
||||||
|
|
||||||
namespace Ryujinx.Ava.Common.Locale
|
|
||||||
{
|
|
||||||
internal class LocaleExtension(LocaleKeys key) : MarkupExtension
|
|
||||||
{
|
|
||||||
private ClrPropertyInfo PropertyInfo
|
|
||||||
=> new(
|
|
||||||
"Item",
|
|
||||||
_ => LocaleManager.Instance[key],
|
|
||||||
null,
|
|
||||||
typeof(string)
|
|
||||||
);
|
|
||||||
|
|
||||||
public override object ProvideValue(IServiceProvider serviceProvider) =>
|
|
||||||
new CompiledBindingExtension(
|
|
||||||
new CompiledBindingPathBuilder()
|
|
||||||
.Property(PropertyInfo, PropertyInfoAccessorFactory.CreateInpcPropertyAccessor)
|
|
||||||
.Build()
|
|
||||||
)
|
|
||||||
{ Source = LocaleManager.Instance }
|
|
||||||
.ProvideValue(serviceProvider);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,21 +1,15 @@
|
|||||||
using Avalonia.Data.Core;
|
using Avalonia.Data.Core;
|
||||||
using Avalonia.Markup.Xaml;
|
using Avalonia.Markup.Xaml;
|
||||||
using Avalonia.Markup.Xaml.MarkupExtensions;
|
using Avalonia.Markup.Xaml.MarkupExtensions;
|
||||||
using Avalonia.Markup.Xaml.MarkupExtensions.CompiledBindings;
|
using Avalonia.Markup.Xaml.MarkupExtensions.CompiledBindings;
|
||||||
using System;
|
using System;
|
||||||
|
|
||||||
namespace Ryujinx.Ava.Common.Icon
|
namespace Ryujinx.Ava.Common.Markup
|
||||||
{
|
{
|
||||||
internal class IconExtension(string iconString) : MarkupExtension
|
internal abstract class BasicMarkupExtension : MarkupExtension
|
||||||
{
|
{
|
||||||
private ClrPropertyInfo PropertyInfo
|
protected abstract ClrPropertyInfo PropertyInfo { get; }
|
||||||
=> new(
|
|
||||||
"Item",
|
|
||||||
_ => new Projektanker.Icons.Avalonia.Icon { Value = iconString },
|
|
||||||
null,
|
|
||||||
typeof(Projektanker.Icons.Avalonia.Icon)
|
|
||||||
);
|
|
||||||
|
|
||||||
public override object ProvideValue(IServiceProvider serviceProvider) =>
|
public override object ProvideValue(IServiceProvider serviceProvider) =>
|
||||||
new CompiledBindingExtension(
|
new CompiledBindingExtension(
|
||||||
new CompiledBindingPathBuilder()
|
new CompiledBindingPathBuilder()
|
||||||
51
src/Ryujinx/Common/Markup/MarkupExtensions.cs
Normal file
51
src/Ryujinx/Common/Markup/MarkupExtensions.cs
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
using Avalonia.Data.Core;
|
||||||
|
using Avalonia.Markup.Xaml;
|
||||||
|
using Avalonia.Markup.Xaml.MarkupExtensions;
|
||||||
|
using Avalonia.Markup.Xaml.MarkupExtensions.CompiledBindings;
|
||||||
|
using Projektanker.Icons.Avalonia;
|
||||||
|
using Ryujinx.Ava.Common.Locale;
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Ryujinx.Ava.Common.Markup
|
||||||
|
{
|
||||||
|
internal class IconExtension(string iconString) : BasicMarkupExtension
|
||||||
|
{
|
||||||
|
protected override ClrPropertyInfo PropertyInfo
|
||||||
|
=> new(
|
||||||
|
"Item",
|
||||||
|
_ => new Icon { Value = iconString },
|
||||||
|
null,
|
||||||
|
typeof(Icon)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal class SpinningIconExtension(string iconString) : BasicMarkupExtension
|
||||||
|
{
|
||||||
|
protected override ClrPropertyInfo PropertyInfo
|
||||||
|
=> new(
|
||||||
|
"Item",
|
||||||
|
_ => new Icon { Value = iconString, Animation = IconAnimation.Spin },
|
||||||
|
null,
|
||||||
|
typeof(Icon)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal class LocaleExtension(LocaleKeys key) : MarkupExtension
|
||||||
|
{
|
||||||
|
private ClrPropertyInfo PropertyInfo
|
||||||
|
=> new(
|
||||||
|
"Item",
|
||||||
|
_ => LocaleManager.Instance[key],
|
||||||
|
null,
|
||||||
|
typeof(string)
|
||||||
|
);
|
||||||
|
|
||||||
|
public override object ProvideValue(IServiceProvider serviceProvider) =>
|
||||||
|
new CompiledBindingExtension(
|
||||||
|
new CompiledBindingPathBuilder()
|
||||||
|
.Property(PropertyInfo, PropertyInfoAccessorFactory.CreateInpcPropertyAccessor)
|
||||||
|
.Build()
|
||||||
|
) { Source = LocaleManager.Instance }
|
||||||
|
.ProvideValue(serviceProvider);
|
||||||
|
}
|
||||||
|
}
|
||||||
24
src/Ryujinx/Common/XCIFileTrimmerMainWindowLog.cs
Normal file
24
src/Ryujinx/Common/XCIFileTrimmerMainWindowLog.cs
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
using Avalonia.Threading;
|
||||||
|
using Ryujinx.Ava.UI.ViewModels;
|
||||||
|
|
||||||
|
namespace Ryujinx.Ava.Common
|
||||||
|
{
|
||||||
|
internal class XCIFileTrimmerMainWindowLog : Ryujinx.Common.Logging.XCIFileTrimmerLog
|
||||||
|
{
|
||||||
|
private readonly MainWindowViewModel _viewModel;
|
||||||
|
|
||||||
|
public XCIFileTrimmerMainWindowLog(MainWindowViewModel viewModel)
|
||||||
|
{
|
||||||
|
_viewModel = viewModel;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Progress(long current, long total, string text, bool complete)
|
||||||
|
{
|
||||||
|
Dispatcher.UIThread.Post(() =>
|
||||||
|
{
|
||||||
|
_viewModel.StatusBarProgressMaximum = (int)(total);
|
||||||
|
_viewModel.StatusBarProgressValue = (int)(current);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
23
src/Ryujinx/Common/XCIFileTrimmerWindowLog.cs
Normal file
23
src/Ryujinx/Common/XCIFileTrimmerWindowLog.cs
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
using Avalonia.Threading;
|
||||||
|
using Ryujinx.Ava.UI.ViewModels;
|
||||||
|
|
||||||
|
namespace Ryujinx.Ava.Common
|
||||||
|
{
|
||||||
|
internal class XCIFileTrimmerWindowLog : Ryujinx.Common.Logging.XCIFileTrimmerLog
|
||||||
|
{
|
||||||
|
private readonly XCITrimmerViewModel _viewModel;
|
||||||
|
|
||||||
|
public XCIFileTrimmerWindowLog(XCITrimmerViewModel viewModel)
|
||||||
|
{
|
||||||
|
_viewModel = viewModel;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Progress(long current, long total, string text, bool complete)
|
||||||
|
{
|
||||||
|
Dispatcher.UIThread.Post(() =>
|
||||||
|
{
|
||||||
|
_viewModel.SetProgress((int)(current), (int)(total));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -14,7 +14,6 @@ using Ryujinx.Common.GraphicsDriver;
|
|||||||
using Ryujinx.Common.Logging;
|
using Ryujinx.Common.Logging;
|
||||||
using Ryujinx.Common.SystemInterop;
|
using Ryujinx.Common.SystemInterop;
|
||||||
using Ryujinx.Graphics.Vulkan.MoltenVK;
|
using Ryujinx.Graphics.Vulkan.MoltenVK;
|
||||||
using Ryujinx.Modules;
|
|
||||||
using Ryujinx.SDL2.Common;
|
using Ryujinx.SDL2.Common;
|
||||||
using Ryujinx.UI.App.Common;
|
using Ryujinx.UI.App.Common;
|
||||||
using Ryujinx.UI.Common;
|
using Ryujinx.UI.Common;
|
||||||
@@ -46,8 +45,7 @@ namespace Ryujinx.Ava
|
|||||||
public static int Main(string[] args)
|
public static int Main(string[] args)
|
||||||
{
|
{
|
||||||
Version = ReleaseInformation.Version;
|
Version = ReleaseInformation.Version;
|
||||||
|
|
||||||
|
|
||||||
if (OperatingSystem.IsWindows() && !OperatingSystem.IsWindowsVersionAtLeast(10, 0, 17134))
|
if (OperatingSystem.IsWindows() && !OperatingSystem.IsWindowsVersionAtLeast(10, 0, 17134))
|
||||||
{
|
{
|
||||||
_ = MessageBoxA(nint.Zero, "You are running an outdated version of Windows.\n\nRyujinx supports Windows 10 version 1803 and newer.\n", $"Ryujinx {Version}", MbIconwarning);
|
_ = MessageBoxA(nint.Zero, "You are running an outdated version of Windows.\n\nRyujinx supports Windows 10 version 1803 and newer.\n", $"Ryujinx {Version}", MbIconwarning);
|
||||||
|
|||||||
Binary file not shown.
|
Before Width: | Height: | Size: 193 KiB After Width: | Height: | Size: 177 KiB |
@@ -120,7 +120,7 @@ namespace Ryujinx.Ava.UI.Applet
|
|||||||
|
|
||||||
bool okPressed = false;
|
bool okPressed = false;
|
||||||
bool error = false;
|
bool error = false;
|
||||||
string inputText = args.InitialText ?? "";
|
string inputText = args.InitialText ?? string.Empty;
|
||||||
|
|
||||||
Dispatcher.UIThread.InvokeAsync(async () =>
|
Dispatcher.UIThread.InvokeAsync(async () =>
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
xmlns:locale="clr-namespace:Ryujinx.Ava.Common.Locale"
|
xmlns:ext="clr-namespace:Ryujinx.Ava.Common.Markup"
|
||||||
xmlns:applet="using:Ryujinx.Ava.UI.Applet"
|
xmlns:applet="using:Ryujinx.Ava.UI.Applet"
|
||||||
mc:Ignorable="d"
|
mc:Ignorable="d"
|
||||||
Width="400"
|
Width="400"
|
||||||
@@ -25,11 +25,11 @@
|
|||||||
Spacing="10"
|
Spacing="10"
|
||||||
Margin="10">
|
Margin="10">
|
||||||
<TextBlock
|
<TextBlock
|
||||||
Text="{locale:Locale ControllerAppletDescription}" />
|
Text="{ext:Locale ControllerAppletDescription}" />
|
||||||
<TextBlock
|
<TextBlock
|
||||||
IsVisible="{Binding IsDocked}"
|
IsVisible="{Binding IsDocked}"
|
||||||
FontWeight="Bold"
|
FontWeight="Bold"
|
||||||
Text="{locale:Locale ControllerAppletDocked}" />
|
Text="{ext:Locale ControllerAppletDocked}" />
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</Border>
|
</Border>
|
||||||
<Border
|
<Border
|
||||||
@@ -48,7 +48,7 @@
|
|||||||
VerticalAlignment="Center"
|
VerticalAlignment="Center"
|
||||||
TextAlignment="Center"
|
TextAlignment="Center"
|
||||||
FontWeight="Bold"
|
FontWeight="Bold"
|
||||||
Text="{locale:Locale ControllerAppletControllers}" />
|
Text="{ext:Locale ControllerAppletControllers}" />
|
||||||
<StackPanel
|
<StackPanel
|
||||||
Spacing="10"
|
Spacing="10"
|
||||||
HorizontalAlignment="Center"
|
HorizontalAlignment="Center"
|
||||||
@@ -96,7 +96,7 @@
|
|||||||
VerticalAlignment="Center"
|
VerticalAlignment="Center"
|
||||||
TextAlignment="Center"
|
TextAlignment="Center"
|
||||||
FontWeight="Bold"
|
FontWeight="Bold"
|
||||||
Text="{locale:Locale ControllerAppletPlayers}" />
|
Text="{ext:Locale ControllerAppletPlayers}" />
|
||||||
<Border Height="50">
|
<Border Height="50">
|
||||||
<TextBlock
|
<TextBlock
|
||||||
HorizontalAlignment="Center"
|
HorizontalAlignment="Center"
|
||||||
@@ -121,13 +121,13 @@
|
|||||||
Name="SaveButton"
|
Name="SaveButton"
|
||||||
MinWidth="90"
|
MinWidth="90"
|
||||||
Command="{Binding OpenSettingsWindow}">
|
Command="{Binding OpenSettingsWindow}">
|
||||||
<TextBlock Text="{locale:Locale DialogOpenSettingsWindowLabel}" />
|
<TextBlock Text="{ext:Locale DialogOpenSettingsWindowLabel}" />
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
Name="CancelButton"
|
Name="CancelButton"
|
||||||
MinWidth="90"
|
MinWidth="90"
|
||||||
Command="{Binding Close}">
|
Command="{Binding Close}">
|
||||||
<TextBlock Text="{locale:Locale SettingsButtonClose}" />
|
<TextBlock Text="{ext:Locale SettingsButtonClose}" />
|
||||||
</Button>
|
</Button>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</Panel>
|
</Panel>
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ namespace Ryujinx.Ava.UI.Applet
|
|||||||
public static SvgImage JoyconLeftImage => GetResource(JoyConLeftResource);
|
public static SvgImage JoyconLeftImage => GetResource(JoyConLeftResource);
|
||||||
public static SvgImage JoyconRightImage => GetResource(JoyConRightResource);
|
public static SvgImage JoyconRightImage => GetResource(JoyConRightResource);
|
||||||
|
|
||||||
public string PlayerCount { get; set; } = "";
|
public string PlayerCount { get; set; } = string.Empty;
|
||||||
public bool SupportsProController { get; set; }
|
public bool SupportsProController { get; set; }
|
||||||
public bool SupportsLeftJoycon { get; set; }
|
public bool SupportsLeftJoycon { get; set; }
|
||||||
public bool SupportsRightJoycon { get; set; }
|
public bool SupportsRightJoycon { get; set; }
|
||||||
|
|||||||
@@ -3,9 +3,9 @@
|
|||||||
xmlns="https://github.com/avaloniaui"
|
xmlns="https://github.com/avaloniaui"
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
xmlns:locale="clr-namespace:Ryujinx.Ava.Common.Locale"
|
xmlns:ext="clr-namespace:Ryujinx.Ava.Common.Markup"
|
||||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
Title="{locale:Locale ErrorWindowTitle}"
|
Title="{ext:Locale ErrorWindowTitle}"
|
||||||
xmlns:views="using:Ryujinx.Ava.UI.Applet"
|
xmlns:views="using:Ryujinx.Ava.UI.Applet"
|
||||||
Width="450"
|
Width="450"
|
||||||
Height="340"
|
Height="340"
|
||||||
|
|||||||
@@ -27,13 +27,18 @@ namespace Ryujinx.Ava.UI.Controls
|
|||||||
{
|
{
|
||||||
MainText = mainText;
|
MainText = mainText;
|
||||||
SecondaryText = secondaryText;
|
SecondaryText = secondaryText;
|
||||||
Message = message ?? "";
|
Message = message ?? string.Empty;
|
||||||
DataContext = this;
|
DataContext = this;
|
||||||
_placeholder = placeholder;
|
_placeholder = placeholder;
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
|
|
||||||
Input.Watermark = _placeholder;
|
Input.Watermark = _placeholder;
|
||||||
|
|
||||||
|
if (string.IsNullOrWhiteSpace(Input.Watermark))
|
||||||
|
{
|
||||||
|
Input.UseFloatingWatermark = false;
|
||||||
|
}
|
||||||
|
|
||||||
Input.AddHandler(TextInputEvent, Message_TextInput, RoutingStrategies.Tunnel, true);
|
Input.AddHandler(TextInputEvent, Message_TextInput, RoutingStrategies.Tunnel, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -50,9 +55,9 @@ namespace Ryujinx.Ava.UI.Controls
|
|||||||
Input.Focus();
|
Input.Focus();
|
||||||
}
|
}
|
||||||
|
|
||||||
public string Message { get; set; } = "";
|
public string Message { get; set; } = string.Empty;
|
||||||
public string MainText { get; set; } = "";
|
public string MainText { get; set; } = string.Empty;
|
||||||
public string SecondaryText { get; set; } = "";
|
public string SecondaryText { get; set; } = string.Empty;
|
||||||
|
|
||||||
public static async Task<(UserResult Result, string Input)> ShowInputDialog(string title, SoftwareKeyboardUIArgs args)
|
public static async Task<(UserResult Result, string Input)> ShowInputDialog(string title, SoftwareKeyboardUIArgs args)
|
||||||
{
|
{
|
||||||
@@ -71,7 +76,7 @@ namespace Ryujinx.Ava.UI.Controls
|
|||||||
contentDialog.Title = title;
|
contentDialog.Title = title;
|
||||||
contentDialog.PrimaryButtonText = args.SubmitText;
|
contentDialog.PrimaryButtonText = args.SubmitText;
|
||||||
contentDialog.IsPrimaryButtonEnabled = content._checkLength(content.Message.Length);
|
contentDialog.IsPrimaryButtonEnabled = content._checkLength(content.Message.Length);
|
||||||
contentDialog.SecondaryButtonText = "";
|
contentDialog.SecondaryButtonText = string.Empty;
|
||||||
contentDialog.CloseButtonText = LocaleManager.Instance[LocaleKeys.InputDialogCancel];
|
contentDialog.CloseButtonText = LocaleManager.Instance[LocaleKeys.InputDialogCancel];
|
||||||
contentDialog.Content = content;
|
contentDialog.Content = content;
|
||||||
|
|
||||||
@@ -105,13 +110,13 @@ namespace Ryujinx.Ava.UI.Controls
|
|||||||
Error.IsVisible = false;
|
Error.IsVisible = false;
|
||||||
Error.FontStyle = FontStyle.Italic;
|
Error.FontStyle = FontStyle.Italic;
|
||||||
|
|
||||||
string validationInfoText = "";
|
string validationInfoText = string.Empty;
|
||||||
|
|
||||||
if (_inputMin <= 0 && _inputMax == int.MaxValue) // Disable.
|
if (_inputMin <= 0 && _inputMax == int.MaxValue) // Disable.
|
||||||
{
|
{
|
||||||
Error.IsVisible = false;
|
Error.IsVisible = false;
|
||||||
|
|
||||||
_checkLength = length => true;
|
_checkLength = _ => true;
|
||||||
}
|
}
|
||||||
else if (_inputMin > 0 && _inputMax == int.MaxValue)
|
else if (_inputMin > 0 && _inputMax == int.MaxValue)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -2,109 +2,114 @@
|
|||||||
x:Class="Ryujinx.Ava.UI.Controls.ApplicationContextMenu"
|
x:Class="Ryujinx.Ava.UI.Controls.ApplicationContextMenu"
|
||||||
xmlns="https://github.com/avaloniaui"
|
xmlns="https://github.com/avaloniaui"
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
xmlns:locale="clr-namespace:Ryujinx.Ava.Common.Locale"
|
xmlns:ext="clr-namespace:Ryujinx.Ava.Common.Markup"
|
||||||
xmlns:icon="clr-namespace:Ryujinx.Ava.Common.Icon"
|
|
||||||
xmlns:viewModels="clr-namespace:Ryujinx.Ava.UI.ViewModels"
|
xmlns:viewModels="clr-namespace:Ryujinx.Ava.UI.ViewModels"
|
||||||
x:DataType="viewModels:MainWindowViewModel">
|
x:DataType="viewModels:MainWindowViewModel">
|
||||||
<MenuItem
|
<MenuItem
|
||||||
Click="RunApplication_Click"
|
Click="RunApplication_Click"
|
||||||
Header="{locale:Locale GameListContextMenuRunApplication}"
|
Header="{ext:Locale GameListContextMenuRunApplication}"
|
||||||
Icon="{icon:Icon fa-solid fa-play}"/>
|
Icon="{ext:Icon fa-solid fa-play}"/>
|
||||||
<MenuItem
|
<MenuItem
|
||||||
Click="ToggleFavorite_Click"
|
Click="ToggleFavorite_Click"
|
||||||
Header="{locale:Locale GameListContextMenuToggleFavorite}"
|
Header="{ext:Locale GameListContextMenuToggleFavorite}"
|
||||||
Icon="{icon:Icon fa-solid fa-star}"
|
Icon="{ext:Icon fa-solid fa-star}"
|
||||||
ToolTip.Tip="{locale:Locale GameListContextMenuToggleFavoriteToolTip}" />
|
ToolTip.Tip="{ext:Locale GameListContextMenuToggleFavoriteToolTip}" />
|
||||||
<MenuItem
|
<MenuItem
|
||||||
Click="CreateApplicationShortcut_Click"
|
Click="CreateApplicationShortcut_Click"
|
||||||
Header="{locale:Locale GameListContextMenuCreateShortcut}"
|
Header="{ext:Locale GameListContextMenuCreateShortcut}"
|
||||||
IsEnabled="{Binding CreateShortcutEnabled}"
|
IsEnabled="{Binding CreateShortcutEnabled}"
|
||||||
Icon="{icon:Icon fa-solid fa-bookmark}"
|
Icon="{ext:Icon fa-solid fa-bookmark}"
|
||||||
ToolTip.Tip="{OnPlatform Default={locale:Locale GameListContextMenuCreateShortcutToolTip}, macOS={locale:Locale GameListContextMenuCreateShortcutToolTipMacOS}}" />
|
ToolTip.Tip="{OnPlatform Default={ext:Locale GameListContextMenuCreateShortcutToolTip}, macOS={ext:Locale GameListContextMenuCreateShortcutToolTipMacOS}}" />
|
||||||
<Separator />
|
<Separator />
|
||||||
<MenuItem
|
<MenuItem
|
||||||
Click="OpenUserSaveDirectory_Click"
|
Click="OpenUserSaveDirectory_Click"
|
||||||
Header="{locale:Locale GameListContextMenuOpenUserSaveDirectory}"
|
Header="{ext:Locale GameListContextMenuOpenUserSaveDirectory}"
|
||||||
Icon="{icon:Icon mdi-folder-account}"
|
Icon="{ext:Icon mdi-folder-account}"
|
||||||
IsEnabled="{Binding OpenUserSaveDirectoryEnabled}"
|
IsEnabled="{Binding OpenUserSaveDirectoryEnabled}"
|
||||||
ToolTip.Tip="{locale:Locale GameListContextMenuOpenUserSaveDirectoryToolTip}" />
|
ToolTip.Tip="{ext:Locale GameListContextMenuOpenUserSaveDirectoryToolTip}" />
|
||||||
<MenuItem
|
<MenuItem
|
||||||
Click="OpenDeviceSaveDirectory_Click"
|
Click="OpenDeviceSaveDirectory_Click"
|
||||||
Header="{locale:Locale GameListContextMenuOpenDeviceSaveDirectory}"
|
Header="{ext:Locale GameListContextMenuOpenDeviceSaveDirectory}"
|
||||||
IsEnabled="{Binding OpenDeviceSaveDirectoryEnabled}"
|
IsEnabled="{Binding OpenDeviceSaveDirectoryEnabled}"
|
||||||
ToolTip.Tip="{locale:Locale GameListContextMenuOpenDeviceSaveDirectoryToolTip}" />
|
ToolTip.Tip="{ext:Locale GameListContextMenuOpenDeviceSaveDirectoryToolTip}" />
|
||||||
<MenuItem
|
<MenuItem
|
||||||
Click="OpenBcatSaveDirectory_Click"
|
Click="OpenBcatSaveDirectory_Click"
|
||||||
Header="{locale:Locale GameListContextMenuOpenBcatSaveDirectory}"
|
Header="{ext:Locale GameListContextMenuOpenBcatSaveDirectory}"
|
||||||
IsEnabled="{Binding OpenBcatSaveDirectoryEnabled}"
|
IsEnabled="{Binding OpenBcatSaveDirectoryEnabled}"
|
||||||
ToolTip.Tip="{locale:Locale GameListContextMenuOpenBcatSaveDirectoryToolTip}" />
|
ToolTip.Tip="{ext:Locale GameListContextMenuOpenBcatSaveDirectoryToolTip}" />
|
||||||
<Separator />
|
<Separator />
|
||||||
<MenuItem
|
<MenuItem
|
||||||
Click="OpenTitleUpdateManager_Click"
|
Click="OpenTitleUpdateManager_Click"
|
||||||
Header="{locale:Locale GameListContextMenuManageTitleUpdates}"
|
Header="{ext:Locale GameListContextMenuManageTitleUpdates}"
|
||||||
Icon="{icon:Icon fa-solid fa-code-compare}"
|
Icon="{ext:Icon fa-solid fa-code-compare}"
|
||||||
ToolTip.Tip="{locale:Locale GameListContextMenuManageTitleUpdatesToolTip}" />
|
ToolTip.Tip="{ext:Locale GameListContextMenuManageTitleUpdatesToolTip}" />
|
||||||
<MenuItem
|
<MenuItem
|
||||||
Click="OpenDownloadableContentManager_Click"
|
Click="OpenDownloadableContentManager_Click"
|
||||||
Header="{locale:Locale GameListContextMenuManageDlc}"
|
Header="{ext:Locale GameListContextMenuManageDlc}"
|
||||||
Icon="{icon:Icon fa-solid fa-download}"
|
Icon="{ext:Icon fa-solid fa-download}"
|
||||||
ToolTip.Tip="{locale:Locale GameListContextMenuManageDlcToolTip}" />
|
ToolTip.Tip="{ext:Locale GameListContextMenuManageDlcToolTip}" />
|
||||||
<MenuItem
|
<MenuItem
|
||||||
Click="OpenCheatManager_Click"
|
Click="OpenCheatManager_Click"
|
||||||
Header="{locale:Locale GameListContextMenuManageCheat}"
|
Header="{ext:Locale GameListContextMenuManageCheat}"
|
||||||
Icon="{icon:Icon fa-solid fa-code}"
|
Icon="{ext:Icon fa-solid fa-code}"
|
||||||
ToolTip.Tip="{locale:Locale GameListContextMenuManageCheatToolTip}" />
|
ToolTip.Tip="{ext:Locale GameListContextMenuManageCheatToolTip}" />
|
||||||
<MenuItem
|
<MenuItem
|
||||||
Click="OpenModManager_Click"
|
Click="OpenModManager_Click"
|
||||||
Header="{locale:Locale GameListContextMenuManageMod}"
|
Header="{ext:Locale GameListContextMenuManageMod}"
|
||||||
Icon="{icon:Icon mdi-view-module}"
|
Icon="{ext:Icon mdi-view-module}"
|
||||||
ToolTip.Tip="{locale:Locale GameListContextMenuManageModToolTip}" />
|
ToolTip.Tip="{ext:Locale GameListContextMenuManageModToolTip}" />
|
||||||
<Separator />
|
<Separator />
|
||||||
<MenuItem
|
<MenuItem
|
||||||
Click="OpenModsDirectory_Click"
|
Click="OpenModsDirectory_Click"
|
||||||
Header="{locale:Locale GameListContextMenuOpenModsDirectory}"
|
Header="{ext:Locale GameListContextMenuOpenModsDirectory}"
|
||||||
Icon="{icon:Icon mdi-folder-file}"
|
Icon="{ext:Icon mdi-folder-file}"
|
||||||
ToolTip.Tip="{locale:Locale GameListContextMenuOpenModsDirectoryToolTip}" />
|
ToolTip.Tip="{ext:Locale GameListContextMenuOpenModsDirectoryToolTip}" />
|
||||||
<MenuItem
|
<MenuItem
|
||||||
Click="OpenSdModsDirectory_Click"
|
Click="OpenSdModsDirectory_Click"
|
||||||
Header="{locale:Locale GameListContextMenuOpenSdModsDirectory}"
|
Header="{ext:Locale GameListContextMenuOpenSdModsDirectory}"
|
||||||
Icon="{icon:Icon mdi-folder-file}"
|
Icon="{ext:Icon mdi-folder-file}"
|
||||||
ToolTip.Tip="{locale:Locale GameListContextMenuOpenSdModsDirectoryToolTip}" />
|
ToolTip.Tip="{ext:Locale GameListContextMenuOpenSdModsDirectoryToolTip}" />
|
||||||
<Separator />
|
<Separator />
|
||||||
<MenuItem Header="{locale:Locale GameListContextMenuCacheManagement}" Icon="{icon:Icon mdi-cached}">
|
<MenuItem
|
||||||
|
Click="TrimXCI_Click"
|
||||||
|
Header="{ext:Locale GameListContextMenuTrimXCI}"
|
||||||
|
IsEnabled="{Binding TrimXCIEnabled}"
|
||||||
|
ToolTip.Tip="{ext:Locale GameListContextMenuTrimXCIToolTip}" />
|
||||||
|
<Separator />
|
||||||
|
<MenuItem Header="{ext:Locale GameListContextMenuCacheManagement}" Icon="{ext:Icon mdi-cached}">
|
||||||
<MenuItem
|
<MenuItem
|
||||||
Click="PurgePtcCache_Click"
|
Click="PurgePtcCache_Click"
|
||||||
Header="{locale:Locale GameListContextMenuCacheManagementPurgePptc}"
|
Header="{ext:Locale GameListContextMenuCacheManagementPurgePptc}"
|
||||||
Icon="{icon:Icon mdi-refresh}"
|
Icon="{ext:Icon mdi-refresh}"
|
||||||
ToolTip.Tip="{locale:Locale GameListContextMenuCacheManagementPurgePptcToolTip}" />
|
ToolTip.Tip="{ext:Locale GameListContextMenuCacheManagementPurgePptcToolTip}" />
|
||||||
<MenuItem
|
<MenuItem
|
||||||
Click="PurgeShaderCache_Click"
|
Click="PurgeShaderCache_Click"
|
||||||
Header="{locale:Locale GameListContextMenuCacheManagementPurgeShaderCache}"
|
Header="{ext:Locale GameListContextMenuCacheManagementPurgeShaderCache}"
|
||||||
Icon="{icon:Icon mdi-delete-alert}"
|
Icon="{ext:Icon mdi-delete-alert}"
|
||||||
ToolTip.Tip="{locale:Locale GameListContextMenuCacheManagementPurgeShaderCacheToolTip}" />
|
ToolTip.Tip="{ext:Locale GameListContextMenuCacheManagementPurgeShaderCacheToolTip}" />
|
||||||
<MenuItem
|
<MenuItem
|
||||||
Click="OpenPtcDirectory_Click"
|
Click="OpenPtcDirectory_Click"
|
||||||
Header="{locale:Locale GameListContextMenuCacheManagementOpenPptcDirectory}"
|
Header="{ext:Locale GameListContextMenuCacheManagementOpenPptcDirectory}"
|
||||||
Icon="{icon:Icon mdi-folder-arrow-up-down}"
|
Icon="{ext:Icon mdi-folder-arrow-up-down}"
|
||||||
ToolTip.Tip="{locale:Locale GameListContextMenuCacheManagementOpenPptcDirectoryToolTip}" />
|
ToolTip.Tip="{ext:Locale GameListContextMenuCacheManagementOpenPptcDirectoryToolTip}" />
|
||||||
<MenuItem
|
<MenuItem
|
||||||
Click="OpenShaderCacheDirectory_Click"
|
Click="OpenShaderCacheDirectory_Click"
|
||||||
Header="{locale:Locale GameListContextMenuCacheManagementOpenShaderCacheDirectory}"
|
Header="{ext:Locale GameListContextMenuCacheManagementOpenShaderCacheDirectory}"
|
||||||
Icon="{icon:Icon mdi-folder-arrow-up-down}"
|
Icon="{ext:Icon mdi-folder-arrow-up-down}"
|
||||||
ToolTip.Tip="{locale:Locale GameListContextMenuCacheManagementOpenShaderCacheDirectoryToolTip}" />
|
ToolTip.Tip="{ext:Locale GameListContextMenuCacheManagementOpenShaderCacheDirectoryToolTip}" />
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
<MenuItem Header="{locale:Locale GameListContextMenuExtractData}">
|
<MenuItem Header="{ext:Locale GameListContextMenuExtractData}">
|
||||||
<MenuItem
|
<MenuItem
|
||||||
Click="ExtractApplicationExeFs_Click"
|
Click="ExtractApplicationExeFs_Click"
|
||||||
Header="{locale:Locale GameListContextMenuExtractDataExeFS}"
|
Header="{ext:Locale GameListContextMenuExtractDataExeFS}"
|
||||||
ToolTip.Tip="{locale:Locale GameListContextMenuExtractDataExeFSToolTip}" />
|
ToolTip.Tip="{ext:Locale GameListContextMenuExtractDataExeFSToolTip}" />
|
||||||
<MenuItem
|
<MenuItem
|
||||||
Click="ExtractApplicationRomFs_Click"
|
Click="ExtractApplicationRomFs_Click"
|
||||||
Header="{locale:Locale GameListContextMenuExtractDataRomFS}"
|
Header="{ext:Locale GameListContextMenuExtractDataRomFS}"
|
||||||
ToolTip.Tip="{locale:Locale GameListContextMenuExtractDataRomFSToolTip}" />
|
ToolTip.Tip="{ext:Locale GameListContextMenuExtractDataRomFSToolTip}" />
|
||||||
<MenuItem
|
<MenuItem
|
||||||
Click="ExtractApplicationLogo_Click"
|
Click="ExtractApplicationLogo_Click"
|
||||||
Header="{locale:Locale GameListContextMenuExtractDataLogo}"
|
Header="{ext:Locale GameListContextMenuExtractDataLogo}"
|
||||||
ToolTip.Tip="{locale:Locale GameListContextMenuExtractDataLogoToolTip}" />
|
ToolTip.Tip="{ext:Locale GameListContextMenuExtractDataLogoToolTip}" />
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
</MenuFlyout>
|
</MenuFlyout>
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ using Avalonia.Controls;
|
|||||||
using Avalonia.Interactivity;
|
using Avalonia.Interactivity;
|
||||||
using Avalonia.Markup.Xaml;
|
using Avalonia.Markup.Xaml;
|
||||||
using Avalonia.Platform.Storage;
|
using Avalonia.Platform.Storage;
|
||||||
|
using Avalonia.Threading;
|
||||||
using LibHac.Fs;
|
using LibHac.Fs;
|
||||||
using LibHac.Tools.FsSystem.NcaUtils;
|
using LibHac.Tools.FsSystem.NcaUtils;
|
||||||
using Ryujinx.Ava.Common;
|
using Ryujinx.Ava.Common;
|
||||||
@@ -17,6 +18,8 @@ using SkiaSharp;
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
using Path = System.IO.Path;
|
using Path = System.IO.Path;
|
||||||
|
|
||||||
namespace Ryujinx.Ava.UI.Controls
|
namespace Ryujinx.Ava.UI.Controls
|
||||||
@@ -323,5 +326,15 @@ namespace Ryujinx.Ava.UI.Controls
|
|||||||
if (sender is MenuItem { DataContext: MainWindowViewModel { SelectedApplication: not null } viewModel })
|
if (sender is MenuItem { DataContext: MainWindowViewModel { SelectedApplication: not null } viewModel })
|
||||||
await viewModel.LoadApplication(viewModel.SelectedApplication);
|
await viewModel.LoadApplication(viewModel.SelectedApplication);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async void TrimXCI_Click(object sender, RoutedEventArgs args)
|
||||||
|
{
|
||||||
|
var viewModel = (sender as MenuItem)?.DataContext as MainWindowViewModel;
|
||||||
|
|
||||||
|
if (viewModel?.SelectedApplication != null)
|
||||||
|
{
|
||||||
|
await viewModel.TrimXCIFile(viewModel.SelectedApplication.Path);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -55,14 +55,7 @@
|
|||||||
VerticalAlignment="Stretch"
|
VerticalAlignment="Stretch"
|
||||||
ClipToBounds="True"
|
ClipToBounds="True"
|
||||||
CornerRadius="5">
|
CornerRadius="5">
|
||||||
<Grid>
|
<Grid ColumnDefinitions="Auto,10,*,150,100">
|
||||||
<Grid.ColumnDefinitions>
|
|
||||||
<ColumnDefinition Width="Auto" />
|
|
||||||
<ColumnDefinition Width="10" />
|
|
||||||
<ColumnDefinition Width="*" />
|
|
||||||
<ColumnDefinition Width="150" />
|
|
||||||
<ColumnDefinition Width="100" />
|
|
||||||
</Grid.ColumnDefinitions>
|
|
||||||
<Image
|
<Image
|
||||||
Grid.RowSpan="3"
|
Grid.RowSpan="3"
|
||||||
Grid.Column="0"
|
Grid.Column="0"
|
||||||
|
|||||||
@@ -138,7 +138,7 @@ namespace Ryujinx.Ava.UI.Controls
|
|||||||
|
|
||||||
foreach (var account in lostAccounts)
|
foreach (var account in lostAccounts)
|
||||||
{
|
{
|
||||||
ViewModel.LostProfiles.Add(new UserProfile(new HLE.HOS.Services.Account.Acc.UserProfile(account, "", null), this));
|
ViewModel.LostProfiles.Add(new UserProfile(new HLE.HOS.Services.Account.Acc.UserProfile(account, string.Empty, null), this));
|
||||||
}
|
}
|
||||||
|
|
||||||
ViewModel.Profiles.Add(new BaseModel());
|
ViewModel.Profiles.Add(new BaseModel());
|
||||||
@@ -155,14 +155,11 @@ namespace Ryujinx.Ava.UI.Controls
|
|||||||
|
|
||||||
if (profile == null)
|
if (profile == null)
|
||||||
{
|
{
|
||||||
Dispatcher.UIThread.Post(Action);
|
_ = Dispatcher.UIThread.InvokeAsync(async ()
|
||||||
|
=> await ContentDialogHelper.CreateErrorDialog(
|
||||||
|
LocaleManager.Instance[LocaleKeys.DialogUserProfileDeletionWarningMessage]));
|
||||||
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
static async void Action()
|
|
||||||
{
|
|
||||||
await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance[LocaleKeys.DialogUserProfileDeletionWarningMessage]);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
AccountManager.OpenUser(profile.UserId);
|
AccountManager.OpenUser(profile.UserId);
|
||||||
@@ -170,10 +167,10 @@ namespace Ryujinx.Ava.UI.Controls
|
|||||||
|
|
||||||
var result = await ContentDialogHelper.CreateConfirmationDialog(
|
var result = await ContentDialogHelper.CreateConfirmationDialog(
|
||||||
LocaleManager.Instance[LocaleKeys.DialogUserProfileDeletionConfirmMessage],
|
LocaleManager.Instance[LocaleKeys.DialogUserProfileDeletionConfirmMessage],
|
||||||
"",
|
string.Empty,
|
||||||
LocaleManager.Instance[LocaleKeys.InputDialogYes],
|
LocaleManager.Instance[LocaleKeys.InputDialogYes],
|
||||||
LocaleManager.Instance[LocaleKeys.InputDialogNo],
|
LocaleManager.Instance[LocaleKeys.InputDialogNo],
|
||||||
"");
|
string.Empty);
|
||||||
|
|
||||||
if (result == UserResult.Yes)
|
if (result == UserResult.Yes)
|
||||||
{
|
{
|
||||||
|
|||||||
62
src/Ryujinx/UI/Helpers/AvaloniaListExtensions.cs
Normal file
62
src/Ryujinx/UI/Helpers/AvaloniaListExtensions.cs
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
using Avalonia.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace Ryujinx.Ava.UI.Helpers
|
||||||
|
{
|
||||||
|
public static class AvaloniaListExtensions
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Adds or Replaces an item in an AvaloniaList irrespective of whether the item already exists
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">The type of the element in the AvaoloniaList</typeparam>
|
||||||
|
/// <param name="list">The list containing the item to replace</param>
|
||||||
|
/// <param name="item">The item to replace</param>
|
||||||
|
/// <param name="addIfNotFound">True to add the item if its not found</param>
|
||||||
|
/// <returns>True if the item was found and replaced, false if it was addded</returns>
|
||||||
|
/// <remarks>
|
||||||
|
/// The indexes on the AvaloniaList will only replace if the item does not match,
|
||||||
|
/// this causes the items to not be replaced if the Equality is customised on the
|
||||||
|
/// items. This method will instead find, remove and add the item to ensure it is
|
||||||
|
/// replaced correctly.
|
||||||
|
/// </remarks>
|
||||||
|
public static bool ReplaceWith<T>(this AvaloniaList<T> list, T item, bool addIfNotFound = true)
|
||||||
|
{
|
||||||
|
var index = list.IndexOf(item);
|
||||||
|
|
||||||
|
if (index != -1)
|
||||||
|
{
|
||||||
|
list.RemoveAt(index);
|
||||||
|
list.Insert(index, item);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
list.Add(item);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Adds or Replaces items in an AvaloniaList from another list irrespective of whether the item already exists
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">The type of the element in the AvaoloniaList</typeparam>
|
||||||
|
/// <param name="list">The list containing the item to replace</param>
|
||||||
|
/// <param name="sourceList">The list of items to be actually added to `list`</param>
|
||||||
|
/// <param name="matchingList">The items to use as matching records to search for in the `sourceList', if not found this item will be added instead</params>
|
||||||
|
public static void AddOrReplaceMatching<T>(this AvaloniaList<T> list, IList<T> sourceList, IList<T> matchingList)
|
||||||
|
{
|
||||||
|
foreach (var match in matchingList)
|
||||||
|
{
|
||||||
|
var index = sourceList.IndexOf(match);
|
||||||
|
if (index != -1)
|
||||||
|
{
|
||||||
|
list.ReplaceWith(sourceList[index]);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
list.ReplaceWith(match);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -205,7 +205,7 @@ namespace Ryujinx.Ava.UI.Helpers
|
|||||||
primary,
|
primary,
|
||||||
secondaryText,
|
secondaryText,
|
||||||
acceptButton,
|
acceptButton,
|
||||||
"",
|
string.Empty,
|
||||||
closeButton,
|
closeButton,
|
||||||
(int)Symbol.Important);
|
(int)Symbol.Important);
|
||||||
|
|
||||||
@@ -221,7 +221,7 @@ namespace Ryujinx.Ava.UI.Helpers
|
|||||||
primaryText,
|
primaryText,
|
||||||
secondaryText,
|
secondaryText,
|
||||||
acceptButtonText,
|
acceptButtonText,
|
||||||
"",
|
string.Empty,
|
||||||
cancelButtonText,
|
cancelButtonText,
|
||||||
(int)Symbol.Help,
|
(int)Symbol.Help,
|
||||||
primaryButtonResult);
|
primaryButtonResult);
|
||||||
@@ -239,8 +239,8 @@ namespace Ryujinx.Ava.UI.Helpers
|
|||||||
LocaleManager.Instance[LocaleKeys.DialogUpdaterTitle],
|
LocaleManager.Instance[LocaleKeys.DialogUpdaterTitle],
|
||||||
primary,
|
primary,
|
||||||
secondaryText,
|
secondaryText,
|
||||||
"",
|
string.Empty,
|
||||||
"",
|
string.Empty,
|
||||||
LocaleManager.Instance[LocaleKeys.InputDialogOk],
|
LocaleManager.Instance[LocaleKeys.InputDialogOk],
|
||||||
(int)Symbol.Important);
|
(int)Symbol.Important);
|
||||||
|
|
||||||
@@ -249,8 +249,8 @@ namespace Ryujinx.Ava.UI.Helpers
|
|||||||
LocaleManager.Instance[LocaleKeys.DialogWarningTitle],
|
LocaleManager.Instance[LocaleKeys.DialogWarningTitle],
|
||||||
primary,
|
primary,
|
||||||
secondaryText,
|
secondaryText,
|
||||||
"",
|
string.Empty,
|
||||||
"",
|
string.Empty,
|
||||||
LocaleManager.Instance[LocaleKeys.InputDialogOk],
|
LocaleManager.Instance[LocaleKeys.InputDialogOk],
|
||||||
(int)Symbol.Important);
|
(int)Symbol.Important);
|
||||||
|
|
||||||
@@ -263,7 +263,7 @@ namespace Ryujinx.Ava.UI.Helpers
|
|||||||
LocaleManager.Instance[LocaleKeys.DialogErrorMessage],
|
LocaleManager.Instance[LocaleKeys.DialogErrorMessage],
|
||||||
errorMessage,
|
errorMessage,
|
||||||
secondaryErrorMessage,
|
secondaryErrorMessage,
|
||||||
"",
|
string.Empty,
|
||||||
LocaleManager.Instance[LocaleKeys.InputDialogOk],
|
LocaleManager.Instance[LocaleKeys.InputDialogOk],
|
||||||
(int)Symbol.Dismiss);
|
(int)Symbol.Dismiss);
|
||||||
}
|
}
|
||||||
@@ -282,7 +282,7 @@ namespace Ryujinx.Ava.UI.Helpers
|
|||||||
primary,
|
primary,
|
||||||
secondaryText,
|
secondaryText,
|
||||||
LocaleManager.Instance[LocaleKeys.InputDialogYes],
|
LocaleManager.Instance[LocaleKeys.InputDialogYes],
|
||||||
"",
|
string.Empty,
|
||||||
LocaleManager.Instance[LocaleKeys.InputDialogNo],
|
LocaleManager.Instance[LocaleKeys.InputDialogNo],
|
||||||
(int)Symbol.Help,
|
(int)Symbol.Help,
|
||||||
UserResult.Yes);
|
UserResult.Yes);
|
||||||
|
|||||||
@@ -122,7 +122,7 @@ namespace Ryujinx.Ava.UI.Helpers
|
|||||||
|
|
||||||
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
|
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
|
||||||
{
|
{
|
||||||
string keyString = "";
|
string keyString = string.Empty;
|
||||||
LocaleKeys localeKey;
|
LocaleKeys localeKey;
|
||||||
|
|
||||||
switch (value)
|
switch (value)
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ namespace Ryujinx.Ava.UI.Helpers
|
|||||||
await ContentDialogHelper.CreateInfoDialog(
|
await ContentDialogHelper.CreateInfoDialog(
|
||||||
LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DialogUserErrorDialogMessage, errorCode, GetErrorTitle(error)),
|
LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DialogUserErrorDialogMessage, errorCode, GetErrorTitle(error)),
|
||||||
GetErrorDescription(error),
|
GetErrorDescription(error),
|
||||||
"",
|
string.Empty,
|
||||||
LocaleManager.Instance[LocaleKeys.InputDialogOk],
|
LocaleManager.Instance[LocaleKeys.InputDialogOk],
|
||||||
LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DialogUserErrorDialogTitle, errorCode));
|
LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DialogUserErrorDialogTitle, errorCode));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,48 @@
|
|||||||
|
using Avalonia;
|
||||||
|
using Avalonia.Data;
|
||||||
|
using Avalonia.Data.Converters;
|
||||||
|
using Ryujinx.Ava.Common.Locale;
|
||||||
|
using Ryujinx.UI.Common.Models;
|
||||||
|
using System;
|
||||||
|
using System.Globalization;
|
||||||
|
|
||||||
|
namespace Ryujinx.Ava.UI.Helpers
|
||||||
|
{
|
||||||
|
internal class XCITrimmerFileSpaceSavingsConverter : IValueConverter
|
||||||
|
{
|
||||||
|
private const long _bytesPerMB = 1024 * 1024;
|
||||||
|
public static XCITrimmerFileSpaceSavingsConverter Instance = new();
|
||||||
|
|
||||||
|
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
|
||||||
|
{
|
||||||
|
if (value is UnsetValueType)
|
||||||
|
{
|
||||||
|
return BindingOperations.DoNothing;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!targetType.IsAssignableFrom(typeof(string)))
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value is not XCITrimmerFileModel app)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (app.CurrentSavingsB < app.PotentialSavingsB)
|
||||||
|
{
|
||||||
|
return LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.TitleXCICanSaveLabel, (app.PotentialSavingsB - app.CurrentSavingsB) / _bytesPerMB);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.TitleXCISavingLabel, app.CurrentSavingsB / _bytesPerMB);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
|
||||||
|
{
|
||||||
|
throw new NotSupportedException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
46
src/Ryujinx/UI/Helpers/XCITrimmerFileStatusConverter.cs
Normal file
46
src/Ryujinx/UI/Helpers/XCITrimmerFileStatusConverter.cs
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
using Avalonia;
|
||||||
|
using Avalonia.Data;
|
||||||
|
using Avalonia.Data.Converters;
|
||||||
|
using Ryujinx.Ava.Common.Locale;
|
||||||
|
using Ryujinx.UI.Common.Models;
|
||||||
|
using System;
|
||||||
|
using System.Globalization;
|
||||||
|
using static Ryujinx.Common.Utilities.XCIFileTrimmer;
|
||||||
|
|
||||||
|
namespace Ryujinx.Ava.UI.Helpers
|
||||||
|
{
|
||||||
|
internal class XCITrimmerFileStatusConverter : IValueConverter
|
||||||
|
{
|
||||||
|
public static XCITrimmerFileStatusConverter Instance = new();
|
||||||
|
|
||||||
|
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
|
||||||
|
{
|
||||||
|
if (value is UnsetValueType)
|
||||||
|
{
|
||||||
|
return BindingOperations.DoNothing;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!targetType.IsAssignableFrom(typeof(string)))
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value is not XCITrimmerFileModel app)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return app.PercentageProgress != null ? String.Empty :
|
||||||
|
app.ProcessingOutcome != OperationOutcome.Successful && app.ProcessingOutcome != OperationOutcome.Undetermined ? LocaleManager.Instance[LocaleKeys.TitleXCIStatusFailedLabel] :
|
||||||
|
app.Trimmable & app.Untrimmable ? LocaleManager.Instance[LocaleKeys.TitleXCIStatusPartialLabel] :
|
||||||
|
app.Trimmable ? LocaleManager.Instance[LocaleKeys.TitleXCIStatusTrimmableLabel] :
|
||||||
|
app.Untrimmable ? LocaleManager.Instance[LocaleKeys.TitleXCIStatusUntrimmableLabel] :
|
||||||
|
String.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
|
||||||
|
{
|
||||||
|
throw new NotSupportedException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
using Avalonia;
|
||||||
|
using Avalonia.Data;
|
||||||
|
using Avalonia.Data.Converters;
|
||||||
|
using Ryujinx.UI.Common.Models;
|
||||||
|
using System;
|
||||||
|
using System.Globalization;
|
||||||
|
using static Ryujinx.Common.Utilities.XCIFileTrimmer;
|
||||||
|
|
||||||
|
namespace Ryujinx.Ava.UI.Helpers
|
||||||
|
{
|
||||||
|
internal class XCITrimmerFileStatusDetailConverter : IValueConverter
|
||||||
|
{
|
||||||
|
public static XCITrimmerFileStatusDetailConverter Instance = new();
|
||||||
|
|
||||||
|
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
|
||||||
|
{
|
||||||
|
if (value is UnsetValueType)
|
||||||
|
{
|
||||||
|
return BindingOperations.DoNothing;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!targetType.IsAssignableFrom(typeof(string)))
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value is not XCITrimmerFileModel app)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return app.PercentageProgress != null ? null :
|
||||||
|
app.ProcessingOutcome != OperationOutcome.Successful && app.ProcessingOutcome != OperationOutcome.Undetermined ? app.ProcessingOutcome.ToLocalisedText() :
|
||||||
|
null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
|
||||||
|
{
|
||||||
|
throw new NotSupportedException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
36
src/Ryujinx/UI/Helpers/XCITrimmerOperationOutcomeHelper.cs
Normal file
36
src/Ryujinx/UI/Helpers/XCITrimmerOperationOutcomeHelper.cs
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
using Ryujinx.Ava.Common.Locale;
|
||||||
|
using static Ryujinx.Common.Utilities.XCIFileTrimmer;
|
||||||
|
|
||||||
|
namespace Ryujinx.Ava.UI.Helpers
|
||||||
|
{
|
||||||
|
public static class XCIFileTrimmerOperationOutcomeExtensions
|
||||||
|
{
|
||||||
|
public static string ToLocalisedText(this OperationOutcome operationOutcome)
|
||||||
|
{
|
||||||
|
switch (operationOutcome)
|
||||||
|
{
|
||||||
|
case OperationOutcome.NoTrimNecessary:
|
||||||
|
return LocaleManager.Instance[LocaleKeys.TrimXCIFileNoTrimNecessary];
|
||||||
|
case OperationOutcome.NoUntrimPossible:
|
||||||
|
return LocaleManager.Instance[LocaleKeys.TrimXCIFileNoUntrimPossible];
|
||||||
|
case OperationOutcome.ReadOnlyFileCannotFix:
|
||||||
|
return LocaleManager.Instance[LocaleKeys.TrimXCIFileReadOnlyFileCannotFix];
|
||||||
|
case OperationOutcome.FreeSpaceCheckFailed:
|
||||||
|
return LocaleManager.Instance[LocaleKeys.TrimXCIFileFreeSpaceCheckFailed];
|
||||||
|
case OperationOutcome.InvalidXCIFile:
|
||||||
|
return LocaleManager.Instance[LocaleKeys.TrimXCIFileInvalidXCIFile];
|
||||||
|
case OperationOutcome.FileIOWriteError:
|
||||||
|
return LocaleManager.Instance[LocaleKeys.TrimXCIFileFileIOWriteError];
|
||||||
|
case OperationOutcome.FileSizeChanged:
|
||||||
|
return LocaleManager.Instance[LocaleKeys.TrimXCIFileFileSizeChanged];
|
||||||
|
case OperationOutcome.Cancelled:
|
||||||
|
return LocaleManager.Instance[LocaleKeys.TrimXCIFileCancelled];
|
||||||
|
case OperationOutcome.Undetermined:
|
||||||
|
return LocaleManager.Instance[LocaleKeys.TrimXCIFileFileUndertermined];
|
||||||
|
case OperationOutcome.Successful:
|
||||||
|
default:
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -320,7 +320,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (LastScannedAmiiboId != "")
|
if (LastScannedAmiiboId != string.Empty)
|
||||||
{
|
{
|
||||||
SelectLastScannedAmiibo();
|
SelectLastScannedAmiibo();
|
||||||
}
|
}
|
||||||
@@ -421,7 +421,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
usageStringBuilder.Append($"{LocaleManager.Instance[LocaleKeys.Unknown]}.");
|
usageStringBuilder.Append($"{LocaleManager.Instance[LocaleKeys.Unknown]}.");
|
||||||
}
|
}
|
||||||
|
|
||||||
Usage = $"{LocaleManager.Instance[LocaleKeys.Usage]} {(writable ? $" ({LocaleManager.Instance[LocaleKeys.Writable]})" : "")} : {usageStringBuilder}";
|
Usage = $"{LocaleManager.Instance[LocaleKeys.Usage]} {(writable ? $" ({LocaleManager.Instance[LocaleKeys.Writable]})" : string.Empty)} : {usageStringBuilder}";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -480,7 +480,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
await ContentDialogHelper.CreateInfoDialog(LocaleManager.Instance[LocaleKeys.DialogAmiiboApiTitle],
|
await ContentDialogHelper.CreateInfoDialog(LocaleManager.Instance[LocaleKeys.DialogAmiiboApiTitle],
|
||||||
LocaleManager.Instance[LocaleKeys.DialogAmiiboApiFailFetchMessage],
|
LocaleManager.Instance[LocaleKeys.DialogAmiiboApiFailFetchMessage],
|
||||||
LocaleManager.Instance[LocaleKeys.InputDialogOk],
|
LocaleManager.Instance[LocaleKeys.InputDialogOk],
|
||||||
"",
|
string.Empty,
|
||||||
LocaleManager.Instance[LocaleKeys.RyujinxInfo]);
|
LocaleManager.Instance[LocaleKeys.RyujinxInfo]);
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
@@ -530,7 +530,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
await ContentDialogHelper.CreateInfoDialog(LocaleManager.Instance[LocaleKeys.DialogAmiiboApiTitle],
|
await ContentDialogHelper.CreateInfoDialog(LocaleManager.Instance[LocaleKeys.DialogAmiiboApiTitle],
|
||||||
LocaleManager.Instance[LocaleKeys.DialogAmiiboApiConnectErrorMessage],
|
LocaleManager.Instance[LocaleKeys.DialogAmiiboApiConnectErrorMessage],
|
||||||
LocaleManager.Instance[LocaleKeys.InputDialogOk],
|
LocaleManager.Instance[LocaleKeys.InputDialogOk],
|
||||||
"",
|
string.Empty,
|
||||||
LocaleManager.Instance[LocaleKeys.RyujinxInfo]);
|
LocaleManager.Instance[LocaleKeys.RyujinxInfo]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -287,7 +287,14 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
var msg = string.Format(LocaleManager.Instance[LocaleKeys.DlcWindowDlcAddedMessage], numAdded);
|
var msg = string.Format(LocaleManager.Instance[LocaleKeys.DlcWindowDlcAddedMessage], numAdded);
|
||||||
return Dispatcher.UIThread.InvokeAsync(async () =>
|
return Dispatcher.UIThread.InvokeAsync(async () =>
|
||||||
{
|
{
|
||||||
await ContentDialogHelper.ShowTextDialog(LocaleManager.Instance[LocaleKeys.DialogConfirmationTitle], msg, "", "", "", LocaleManager.Instance[LocaleKeys.InputDialogOk], (int)Symbol.Checkmark);
|
await ContentDialogHelper.ShowTextDialog(
|
||||||
|
LocaleManager.Instance[LocaleKeys.DialogConfirmationTitle],
|
||||||
|
msg,
|
||||||
|
string.Empty,
|
||||||
|
string.Empty,
|
||||||
|
string.Empty,
|
||||||
|
LocaleManager.Instance[LocaleKeys.InputDialogOk],
|
||||||
|
(int)Symbol.Checkmark);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ using Ryujinx.Ava.UI.Windows;
|
|||||||
using Ryujinx.Common;
|
using Ryujinx.Common;
|
||||||
using Ryujinx.Common.Configuration;
|
using Ryujinx.Common.Configuration;
|
||||||
using Ryujinx.Common.Logging;
|
using Ryujinx.Common.Logging;
|
||||||
|
using Ryujinx.Common.Utilities;
|
||||||
using Ryujinx.Cpu;
|
using Ryujinx.Cpu;
|
||||||
using Ryujinx.HLE;
|
using Ryujinx.HLE;
|
||||||
using Ryujinx.HLE.FileSystem;
|
using Ryujinx.HLE.FileSystem;
|
||||||
@@ -29,7 +30,6 @@ using Ryujinx.HLE.HOS;
|
|||||||
using Ryujinx.HLE.HOS.Services.Account.Acc;
|
using Ryujinx.HLE.HOS.Services.Account.Acc;
|
||||||
using Ryujinx.HLE.UI;
|
using Ryujinx.HLE.UI;
|
||||||
using Ryujinx.Input.HLE;
|
using Ryujinx.Input.HLE;
|
||||||
using Ryujinx.Modules;
|
|
||||||
using Ryujinx.UI.App.Common;
|
using Ryujinx.UI.App.Common;
|
||||||
using Ryujinx.UI.Common;
|
using Ryujinx.UI.Common;
|
||||||
using Ryujinx.UI.Common.Configuration;
|
using Ryujinx.UI.Common.Configuration;
|
||||||
@@ -85,6 +85,8 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
private bool _isAppletMenuActive;
|
private bool _isAppletMenuActive;
|
||||||
private int _statusBarProgressMaximum;
|
private int _statusBarProgressMaximum;
|
||||||
private int _statusBarProgressValue;
|
private int _statusBarProgressValue;
|
||||||
|
private string _statusBarProgressStatusText;
|
||||||
|
private bool _statusBarProgressStatusVisible;
|
||||||
private bool _isPaused;
|
private bool _isPaused;
|
||||||
private bool _showContent = true;
|
private bool _showContent = true;
|
||||||
private bool _isLoadingIndeterminate = true;
|
private bool _isLoadingIndeterminate = true;
|
||||||
@@ -392,6 +394,8 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
|
|
||||||
public bool OpenDeviceSaveDirectoryEnabled => !SelectedApplication.ControlHolder.ByteSpan.IsZeros() && SelectedApplication.ControlHolder.Value.DeviceSaveDataSize > 0;
|
public bool OpenDeviceSaveDirectoryEnabled => !SelectedApplication.ControlHolder.ByteSpan.IsZeros() && SelectedApplication.ControlHolder.Value.DeviceSaveDataSize > 0;
|
||||||
|
|
||||||
|
public bool TrimXCIEnabled => Ryujinx.Common.Utilities.XCIFileTrimmer.CanTrim(SelectedApplication.Path, new Common.XCIFileTrimmerMainWindowLog(this));
|
||||||
|
|
||||||
public bool OpenBcatSaveDirectoryEnabled => !SelectedApplication.ControlHolder.ByteSpan.IsZeros() && SelectedApplication.ControlHolder.Value.BcatDeliveryCacheStorageSize > 0;
|
public bool OpenBcatSaveDirectoryEnabled => !SelectedApplication.ControlHolder.ByteSpan.IsZeros() && SelectedApplication.ControlHolder.Value.BcatDeliveryCacheStorageSize > 0;
|
||||||
|
|
||||||
public bool CreateShortcutEnabled => !ReleaseInformation.IsFlatHubBuild;
|
public bool CreateShortcutEnabled => !ReleaseInformation.IsFlatHubBuild;
|
||||||
@@ -506,6 +510,28 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public bool StatusBarProgressStatusVisible
|
||||||
|
{
|
||||||
|
get => _statusBarProgressStatusVisible;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
_statusBarProgressStatusVisible = value;
|
||||||
|
|
||||||
|
OnPropertyChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public string StatusBarProgressStatusText
|
||||||
|
{
|
||||||
|
get => _statusBarProgressStatusText;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
_statusBarProgressStatusText = value;
|
||||||
|
|
||||||
|
OnPropertyChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public string FifoStatusText
|
public string FifoStatusText
|
||||||
{
|
{
|
||||||
get => _fifoStatusText;
|
get => _fifoStatusText;
|
||||||
@@ -1092,7 +1118,12 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
|
|
||||||
string message = LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DialogFirmwareInstallerFirmwareInstallSuccessMessage, firmwareVersion.VersionString);
|
string message = LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DialogFirmwareInstallerFirmwareInstallSuccessMessage, firmwareVersion.VersionString);
|
||||||
|
|
||||||
await ContentDialogHelper.CreateInfoDialog(dialogTitle, message, LocaleManager.Instance[LocaleKeys.InputDialogOk], "", LocaleManager.Instance[LocaleKeys.RyujinxInfo]);
|
await ContentDialogHelper.CreateInfoDialog(
|
||||||
|
dialogTitle,
|
||||||
|
message,
|
||||||
|
LocaleManager.Instance[LocaleKeys.InputDialogOk],
|
||||||
|
string.Empty,
|
||||||
|
LocaleManager.Instance[LocaleKeys.RyujinxInfo]);
|
||||||
|
|
||||||
Logger.Info?.Print(LogClass.Application, message);
|
Logger.Info?.Print(LogClass.Application, message);
|
||||||
|
|
||||||
@@ -1163,7 +1194,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
case LoadState.Loaded:
|
case LoadState.Loaded:
|
||||||
LoadHeading = LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.LoadingHeading, _currentApplicationData.Name);
|
LoadHeading = LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.LoadingHeading, _currentApplicationData.Name);
|
||||||
IsLoadingIndeterminate = true;
|
IsLoadingIndeterminate = true;
|
||||||
CacheLoadStatus = "";
|
CacheLoadStatus = string.Empty;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@@ -1183,7 +1214,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
case ShaderCacheLoadingState.Loaded:
|
case ShaderCacheLoadingState.Loaded:
|
||||||
LoadHeading = LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.LoadingHeading, _currentApplicationData.Name);
|
LoadHeading = LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.LoadingHeading, _currentApplicationData.Name);
|
||||||
IsLoadingIndeterminate = true;
|
IsLoadingIndeterminate = true;
|
||||||
CacheLoadStatus = "";
|
CacheLoadStatus = string.Empty;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@@ -1303,7 +1334,12 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
{
|
{
|
||||||
await ContentDialogHelper.ShowTextDialog(
|
await ContentDialogHelper.ShowTextDialog(
|
||||||
LocaleManager.Instance[numAdded > 0 || numRemoved > 0 ? LocaleKeys.RyujinxConfirm : LocaleKeys.RyujinxInfo],
|
LocaleManager.Instance[numAdded > 0 || numRemoved > 0 ? LocaleKeys.RyujinxConfirm : LocaleKeys.RyujinxInfo],
|
||||||
msg, "", "", "", LocaleManager.Instance[LocaleKeys.InputDialogOk], (int)Symbol.Checkmark);
|
msg,
|
||||||
|
string.Empty,
|
||||||
|
string.Empty,
|
||||||
|
string.Empty,
|
||||||
|
LocaleManager.Instance[LocaleKeys.InputDialogOk],
|
||||||
|
(int)Symbol.Checkmark);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1600,7 +1636,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
LocaleManager.Instance[LocaleKeys.DialogLoadAppGameAlreadyLoadedMessage],
|
LocaleManager.Instance[LocaleKeys.DialogLoadAppGameAlreadyLoadedMessage],
|
||||||
LocaleManager.Instance[LocaleKeys.DialogLoadAppGameAlreadyLoadedSubMessage],
|
LocaleManager.Instance[LocaleKeys.DialogLoadAppGameAlreadyLoadedSubMessage],
|
||||||
LocaleManager.Instance[LocaleKeys.InputDialogOk],
|
LocaleManager.Instance[LocaleKeys.InputDialogOk],
|
||||||
"",
|
string.Empty,
|
||||||
LocaleManager.Instance[LocaleKeys.RyujinxInfo]);
|
LocaleManager.Instance[LocaleKeys.RyujinxInfo]);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
@@ -1767,6 +1803,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
if (WindowState is not WindowState.Normal)
|
if (WindowState is not WindowState.Normal)
|
||||||
{
|
{
|
||||||
WindowState = WindowState.Normal;
|
WindowState = WindowState.Normal;
|
||||||
|
Window.TitleBar.ExtendsContentIntoTitleBar = !ConfigurationState.Instance.ShowTitleBar;
|
||||||
|
|
||||||
if (IsGameRunning)
|
if (IsGameRunning)
|
||||||
{
|
{
|
||||||
@@ -1776,6 +1813,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
WindowState = WindowState.FullScreen;
|
WindowState = WindowState.FullScreen;
|
||||||
|
Window.TitleBar.ExtendsContentIntoTitleBar = true;
|
||||||
|
|
||||||
if (IsGameRunning)
|
if (IsGameRunning)
|
||||||
{
|
{
|
||||||
@@ -1817,12 +1855,104 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
|
|
||||||
if (result == UserResult.Yes)
|
if (result == UserResult.Yes)
|
||||||
{
|
{
|
||||||
ConfigurationState.Instance.Graphics.ShadersDumpPath.Value = "";
|
ConfigurationState.Instance.Graphics.ShadersDumpPath.Value = string.Empty;
|
||||||
|
|
||||||
SaveConfig();
|
SaveConfig();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async void ProcessTrimResult(String filename, Ryujinx.Common.Utilities.XCIFileTrimmer.OperationOutcome operationOutcome)
|
||||||
|
{
|
||||||
|
string notifyUser = operationOutcome.ToLocalisedText();
|
||||||
|
|
||||||
|
if (notifyUser != null)
|
||||||
|
{
|
||||||
|
await ContentDialogHelper.CreateWarningDialog(
|
||||||
|
LocaleManager.Instance[LocaleKeys.TrimXCIFileFailedPrimaryText],
|
||||||
|
notifyUser
|
||||||
|
);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
switch (operationOutcome)
|
||||||
|
{
|
||||||
|
case Ryujinx.Common.Utilities.XCIFileTrimmer.OperationOutcome.Successful:
|
||||||
|
if (Avalonia.Application.Current.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
|
||||||
|
{
|
||||||
|
if (desktop.MainWindow is MainWindow mainWindow)
|
||||||
|
mainWindow.LoadApplications();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task TrimXCIFile(string filename)
|
||||||
|
{
|
||||||
|
if (filename == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var trimmer = new XCIFileTrimmer(filename, new Common.XCIFileTrimmerMainWindowLog(this));
|
||||||
|
|
||||||
|
if (trimmer.CanBeTrimmed)
|
||||||
|
{
|
||||||
|
var savings = (double)trimmer.DiskSpaceSavingsB / 1024.0 / 1024.0;
|
||||||
|
var currentFileSize = (double)trimmer.FileSizeB / 1024.0 / 1024.0;
|
||||||
|
var cartDataSize = (double)trimmer.DataSizeB / 1024.0 / 1024.0;
|
||||||
|
string secondaryText = LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.TrimXCIFileDialogSecondaryText, currentFileSize, cartDataSize, savings);
|
||||||
|
|
||||||
|
var result = await ContentDialogHelper.CreateConfirmationDialog(
|
||||||
|
LocaleManager.Instance[LocaleKeys.TrimXCIFileDialogPrimaryText],
|
||||||
|
secondaryText,
|
||||||
|
LocaleManager.Instance[LocaleKeys.Continue],
|
||||||
|
LocaleManager.Instance[LocaleKeys.Cancel],
|
||||||
|
LocaleManager.Instance[LocaleKeys.TrimXCIFileDialogTitle]
|
||||||
|
);
|
||||||
|
|
||||||
|
if (result == UserResult.Yes)
|
||||||
|
{
|
||||||
|
Thread XCIFileTrimThread = new(() =>
|
||||||
|
{
|
||||||
|
Dispatcher.UIThread.Post(() =>
|
||||||
|
{
|
||||||
|
StatusBarProgressStatusText = LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.StatusBarXCIFileTrimming, Path.GetFileName(filename));
|
||||||
|
StatusBarProgressStatusVisible = true;
|
||||||
|
StatusBarProgressMaximum = 1;
|
||||||
|
StatusBarProgressValue = 0;
|
||||||
|
StatusBarVisible = true;
|
||||||
|
});
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
XCIFileTrimmer.OperationOutcome operationOutcome = trimmer.Trim();
|
||||||
|
|
||||||
|
Dispatcher.UIThread.Post(() =>
|
||||||
|
{
|
||||||
|
ProcessTrimResult(filename, operationOutcome);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
Dispatcher.UIThread.Post(() =>
|
||||||
|
{
|
||||||
|
StatusBarProgressStatusVisible = false;
|
||||||
|
StatusBarProgressStatusText = string.Empty;
|
||||||
|
StatusBarVisible = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
})
|
||||||
|
{
|
||||||
|
Name = "GUI.XCIFileTrimmerThread",
|
||||||
|
IsBackground = true,
|
||||||
|
};
|
||||||
|
XCIFileTrimThread.Start();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user