Merge branch 'master' into fix-remember-window-position-on-linux
This commit is contained in:
commit
456e34f20b
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 }}
|
4
.github/workflows/release.yml
vendored
4
.github/workflows/release.yml
vendored
@ -4,7 +4,7 @@ on:
|
||||
workflow_dispatch:
|
||||
inputs: {}
|
||||
push:
|
||||
branches: [ master ]
|
||||
branches: [ release ]
|
||||
paths-ignore:
|
||||
- '.github/**'
|
||||
- 'docs/**'
|
||||
@ -20,7 +20,7 @@ env:
|
||||
POWERSHELL_TELEMETRY_OPTOUT: 1
|
||||
DOTNET_CLI_TELEMETRY_OPTOUT: 1
|
||||
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_REPO: "Ryujinx"
|
||||
RELEASE: 1
|
||||
|
@ -61,9 +61,9 @@ 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;
|
||||
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**.
|
||||
|
||||
You can find the latest release [here](https://github.com/GreemDev/Ryujinx/releases/latest).
|
||||
@ -74,6 +74,7 @@ If you are planning to contribute or just want to learn more about this project
|
||||
|
||||
## Building
|
||||
|
||||
Building the project is for advanced users.
|
||||
If you wish to build the emulator yourself, follow these steps:
|
||||
|
||||
### Step 1
|
||||
|
17
Ryujinx.sln
17
Ryujinx.sln
@ -29,14 +29,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Graphics.Nvdec", "s
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Audio", "src\Ryujinx.Audio\Ryujinx.Audio.csproj", "{806ACF6D-90B0-45D0-A1AC-5F220F3B3985}"
|
||||
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
|
||||
Release Script = .github/workflows/release.yml
|
||||
Build Script = .github/workflows/build.yml
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Memory", "src\Ryujinx.Memory\Ryujinx.Memory.csproj", "{A5E6C691-9E22-4263-8F40-42F002CE66BE}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Tests.Memory", "src\Ryujinx.Tests.Memory\Ryujinx.Tests.Memory.csproj", "{D1CC5322-7325-4F6B-9625-194B30BE1296}"
|
||||
@ -89,6 +81,15 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Horizon.Kernel.Gene
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.HLE.Generators", "src\Ryujinx.HLE.Generators\Ryujinx.HLE.Generators.csproj", "{B575BCDE-2FD8-4A5D-8756-31CDD7FE81F0}"
|
||||
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
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
|
Binary file not shown.
@ -46,5 +46,5 @@ then
|
||||
rcodesign sign --entitlements-xml-path "$ENTITLEMENTS_FILE_PATH" "$APP_BUNDLE_DIRECTORY"
|
||||
else
|
||||
echo "Usign codesign for ad-hoc signing"
|
||||
codesign --entitlements "$ENTITLEMENTS_FILE_PATH" -f --deep -s - "$APP_BUNDLE_DIRECTORY"
|
||||
fi
|
||||
codesign --entitlements "$ENTITLEMENTS_FILE_PATH" -f -s - "$APP_BUNDLE_DIRECTORY"
|
||||
fi
|
||||
|
@ -99,7 +99,7 @@ then
|
||||
rcodesign sign --entitlements-xml-path "$ENTITLEMENTS_FILE_PATH" "$UNIVERSAL_APP_BUNDLE"
|
||||
else
|
||||
echo "Using codesign for ad-hoc signing"
|
||||
codesign --entitlements "$ENTITLEMENTS_FILE_PATH" -f --deep -s - "$UNIVERSAL_APP_BUNDLE"
|
||||
codesign --entitlements "$ENTITLEMENTS_FILE_PATH" -f -s - "$UNIVERSAL_APP_BUNDLE"
|
||||
fi
|
||||
|
||||
echo "Creating archive"
|
||||
@ -111,4 +111,4 @@ rm "$RELEASE_TAR_FILE_NAME"
|
||||
|
||||
popd
|
||||
|
||||
echo "Done"
|
||||
echo "Done"
|
||||
|
@ -95,7 +95,7 @@ else
|
||||
echo "Using codesign for ad-hoc signing"
|
||||
for FILE in "$UNIVERSAL_OUTPUT"/*; do
|
||||
if [[ $(file "$FILE") == *"Mach-O"* ]]; then
|
||||
codesign --entitlements "$ENTITLEMENTS_FILE_PATH" -f --deep -s - "$FILE"
|
||||
codesign --entitlements "$ENTITLEMENTS_FILE_PATH" -f -s - "$FILE"
|
||||
fi
|
||||
done
|
||||
fi
|
||||
@ -108,4 +108,4 @@ gzip -9 < "$RELEASE_TAR_FILE_NAME" > "$RELEASE_TAR_FILE_NAME.gz"
|
||||
rm "$RELEASE_TAR_FILE_NAME"
|
||||
popd
|
||||
|
||||
echo "Done"
|
||||
echo "Done"
|
||||
|
@ -81,7 +81,7 @@ namespace Ryujinx.Audio.Renderer.Dsp
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
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}");
|
||||
|
||||
|
@ -72,5 +72,6 @@ namespace Ryujinx.Common.Logging
|
||||
TamperMachine,
|
||||
UI,
|
||||
Vic,
|
||||
XCIFileTrimmer
|
||||
}
|
||||
}
|
||||
|
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
|
||||
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%%";
|
||||
public const string BuildGitHash = "%%RYUJINX_BUILD_GIT_HASH%%";
|
||||
@ -24,7 +26,11 @@ namespace Ryujinx.Common
|
||||
!ReleaseChannelRepo.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;
|
||||
}
|
||||
|
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;
|
||||
}
|
||||
}
|
||||
}
|
@ -13,6 +13,7 @@ namespace Ryujinx.HLE.Generators
|
||||
var syntaxReceiver = (ServiceSyntaxReceiver)context.SyntaxReceiver;
|
||||
CodeGenerator generator = new CodeGenerator();
|
||||
|
||||
generator.AppendLine("#nullable enable");
|
||||
generator.AppendLine("using System;");
|
||||
generator.EnterScope($"namespace Ryujinx.HLE.HOS.Services.Sm");
|
||||
generator.EnterScope($"partial class IUserInterface");
|
||||
@ -58,6 +59,7 @@ namespace Ryujinx.HLE.Generators
|
||||
|
||||
generator.LeaveScope();
|
||||
generator.LeaveScope();
|
||||
generator.AppendLine("#nullable disable");
|
||||
context.AddSource($"IUserInterface.g.cs", generator.ToString());
|
||||
}
|
||||
|
||||
|
@ -5,6 +5,7 @@ using Ryujinx.HLE.Exceptions;
|
||||
using Ryujinx.HLE.HOS.Kernel.Memory;
|
||||
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.DebugMouse;
|
||||
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.Mouse;
|
||||
@ -28,6 +29,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid
|
||||
public DebugPadDevice DebugPad;
|
||||
public TouchDevice Touchscreen;
|
||||
public MouseDevice Mouse;
|
||||
public DebugMouseDevice DebugMouse;
|
||||
public KeyboardDevice Keyboard;
|
||||
public NpadDevices Npads;
|
||||
|
||||
@ -44,6 +46,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid
|
||||
CheckTypeSizeOrThrow<RingLifo<DebugPadState>>(0x2c8);
|
||||
CheckTypeSizeOrThrow<RingLifo<TouchScreenState>>(0x2C38);
|
||||
CheckTypeSizeOrThrow<RingLifo<MouseState>>(0x350);
|
||||
CheckTypeSizeOrThrow<RingLifo<DebugMouseState>>(0x350);
|
||||
CheckTypeSizeOrThrow<RingLifo<KeyboardState>>(0x3D8);
|
||||
CheckTypeSizeOrThrow<Array10<NpadState>>(0x32000);
|
||||
CheckTypeSizeOrThrow<SharedMemory>(Horizon.HidSize);
|
||||
@ -64,6 +67,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid
|
||||
DebugPad = new DebugPadDevice(_device, true);
|
||||
Touchscreen = new TouchDevice(_device, true);
|
||||
Mouse = new MouseDevice(_device, false);
|
||||
DebugMouse = new DebugMouseDevice(_device, false);
|
||||
Keyboard = new KeyboardDevice(_device, false);
|
||||
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;
|
||||
}
|
||||
|
||||
[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)]
|
||||
// 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.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.Keyboard;
|
||||
using Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Mouse;
|
||||
@ -44,6 +45,12 @@ namespace Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory
|
||||
/// </summary>
|
||||
[FieldOffset(0x9A00)]
|
||||
public Array10<NpadState> Npads;
|
||||
|
||||
/// <summary>
|
||||
/// Debug mouse.
|
||||
/// </summary>
|
||||
[FieldOffset(0x3DC00)]
|
||||
public RingLifo<DebugMouseState> DebugMouse;
|
||||
|
||||
public static SharedMemory Create()
|
||||
{
|
||||
|
@ -1,4 +1,4 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
@ -17,7 +17,7 @@
|
||||
</ItemGroup>
|
||||
|
||||
<Target Name="PostBuild" AfterTargets="PostBuildEvent" Condition="$([MSBuild]::IsOSPlatform('OSX'))">
|
||||
<Exec Command="codesign --entitlements '$(ProjectDir)..\..\distribution\macos\entitlements.xml' -f --deep -s $(SigningCertificate) '$(TargetDir)$(TargetName)'" />
|
||||
<Exec Command="codesign --entitlements '$(ProjectDir)..\..\distribution\macos\entitlements.xml' -f -s $(SigningCertificate) '$(TargetDir)$(TargetName)'" />
|
||||
</Target>
|
||||
|
||||
<ItemGroup>
|
||||
|
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();
|
||||
}
|
||||
}
|
||||
}
|
@ -23,8 +23,10 @@ namespace Ryujinx.Ava
|
||||
{
|
||||
internal static string FormatTitle(LocaleKeys? windowTitleKey = null)
|
||||
=> windowTitleKey is null
|
||||
? $"Ryujinx {Program.Version}"
|
||||
: $"Ryujinx {Program.Version} - {LocaleManager.Instance[windowTitleKey.Value]}";
|
||||
? $"{FullAppName} {Program.Version}"
|
||||
: $"{FullAppName} {Program.Version} - {LocaleManager.Instance[windowTitleKey.Value]}";
|
||||
|
||||
public static readonly string FullAppName = ReleaseInformation.IsCanaryBuild ? "Ryujinx Canary" : "Ryujinx";
|
||||
|
||||
public static MainWindow MainWindow => Current!
|
||||
.ApplicationLifetime.Cast<IClassicDesktopStyleApplicationLifetime>()
|
||||
|
@ -10,7 +10,10 @@
|
||||
"SettingsTabSystemUseHypervisor": "استخدم مراقب الأجهزة الافتراضية",
|
||||
"MenuBarFile": "_ملف",
|
||||
"MenuBarFileOpenFromFile": "_تحميل تطبيق من ملف",
|
||||
"MenuBarFileOpenFromFileError": "No applications found in selected file.",
|
||||
"MenuBarFileOpenUnpacked": "تحميل لُعْبَة غير محزومة",
|
||||
"MenuBarFileLoadDlcFromFolder": "Load DLC From Folder",
|
||||
"MenuBarFileLoadTitleUpdatesFromFolder": "Load Title Updates From Folder",
|
||||
"MenuBarFileOpenEmuFolder": "فتح مجلد Ryujinx",
|
||||
"MenuBarFileOpenLogsFolder": "فتح مجلد السجلات",
|
||||
"MenuBarFileExit": "_خروج",
|
||||
@ -103,6 +106,8 @@
|
||||
"SettingsTabGeneralHideCursorOnIdle": "عند الخمول",
|
||||
"SettingsTabGeneralHideCursorAlways": "دائما",
|
||||
"SettingsTabGeneralGameDirectories": "مجلدات الألعاب",
|
||||
"SettingsTabGeneralAutoloadDirectories": "Autoload DLC/Updates Directories",
|
||||
"SettingsTabGeneralAutoloadNote": "DLC and Updates which refer to missing files will be unloaded automatically",
|
||||
"SettingsTabGeneralAdd": "إضافة",
|
||||
"SettingsTabGeneralRemove": "إزالة",
|
||||
"SettingsTabSystem": "النظام",
|
||||
@ -411,6 +416,7 @@
|
||||
"GameListContextMenuToggleFavorite": "تعيين كمفضل",
|
||||
"GameListContextMenuToggleFavoriteToolTip": "تبديل الحالة المفضلة للعبة",
|
||||
"SettingsTabGeneralTheme": "السمة:",
|
||||
"SettingsTabGeneralThemeAuto": "Auto",
|
||||
"SettingsTabGeneralThemeDark": "داكن",
|
||||
"SettingsTabGeneralThemeLight": "فاتح",
|
||||
"ControllerSettingsConfigureGeneral": "ضبط",
|
||||
@ -561,6 +567,9 @@
|
||||
"AddGameDirBoxTooltip": "أدخل مجلد اللعبة لإضافته إلى القائمة",
|
||||
"AddGameDirTooltip": "إضافة مجلد اللعبة إلى القائمة",
|
||||
"RemoveGameDirTooltip": "إزالة مجلد اللعبة المحدد",
|
||||
"AddAutoloadDirBoxTooltip": "Enter an autoload directory to add to the list",
|
||||
"AddAutoloadDirTooltip": "Add an autoload directory to the list",
|
||||
"RemoveAutoloadDirTooltip": "Remove selected autoload directory",
|
||||
"CustomThemeCheckTooltip": "استخدم سمة أفالونيا المخصصة لواجهة المستخدم الرسومية لتغيير مظهر قوائم المحاكي",
|
||||
"CustomThemePathTooltip": "مسار سمة واجهة المستخدم المخصصة",
|
||||
"CustomThemeBrowseTooltip": "تصفح للحصول على سمة واجهة المستخدم المخصصة",
|
||||
@ -606,6 +615,8 @@
|
||||
"DebugLogTooltip": "طباعة رسائل سجل التصحيح في وحدة التحكم.\n\nاستخدم هذا فقط إذا طلب منك أحد الموظفين تحديدًا ذلك، لأنه سيجعل من الصعب قراءة السجلات وسيؤدي إلى تدهور أداء المحاكي.",
|
||||
"LoadApplicationFileTooltip": "افتح مستكشف الملفات لاختيار ملف متوافق مع سويتش لتحميله",
|
||||
"LoadApplicationFolderTooltip": "افتح مستكشف الملفات لاختيار تطبيق متوافق مع سويتش للتحميل",
|
||||
"LoadDlcFromFolderTooltip": "Open a file explorer to choose one or more folders to bulk load DLC from",
|
||||
"LoadTitleUpdatesFromFolderTooltip": "Open a file explorer to choose one or more folders to bulk load title updates from",
|
||||
"OpenRyujinxFolderTooltip": "فتح مجلد نظام ملفات ريوجينكس",
|
||||
"OpenRyujinxLogsTooltip": "يفتح المجلد الذي تتم كتابة السجلات إليه",
|
||||
"ExitTooltip": "الخروج من ريوجينكس",
|
||||
@ -657,6 +668,8 @@
|
||||
"OpenSetupGuideMessage": "فتح دليل الإعداد",
|
||||
"NoUpdate": "لا يوجد تحديث",
|
||||
"TitleUpdateVersionLabel": "الإصدار: {0}",
|
||||
"TitleBundledUpdateVersionLabel": "Bundled: Version {0}",
|
||||
"TitleBundledDlcLabel": "Bundled:",
|
||||
"RyujinxInfo": "ريوجينكس - معلومات",
|
||||
"RyujinxConfirm": "ريوجينكس - تأكيد",
|
||||
"FileDialogAllTypes": "كل الأنواع",
|
||||
@ -714,9 +727,17 @@
|
||||
"DlcWindowTitle": "إدارة المحتوى القابل للتنزيل لـ {0} ({1})",
|
||||
"ModWindowTitle": "إدارة التعديلات لـ {0} ({1})",
|
||||
"UpdateWindowTitle": "مدير تحديث العنوان",
|
||||
"UpdateWindowUpdateAddedMessage": "{0} new update(s) added",
|
||||
"UpdateWindowBundledContentNotice": "Bundled updates cannot be removed, only disabled.",
|
||||
"CheatWindowHeading": "الغش متوفر لـ {0} [{1}]",
|
||||
"BuildId": "معرف البناء:",
|
||||
"DlcWindowBundledContentNotice": "Bundled DLC cannot be removed, only disabled.",
|
||||
"DlcWindowHeading": "المحتويات القابلة للتنزيل {0}",
|
||||
"DlcWindowDlcAddedMessage": "{0} new downloadable content(s) added",
|
||||
"AutoloadDlcAddedMessage": "{0} new downloadable content(s) added",
|
||||
"AutoloadDlcRemovedMessage": "{0} missing downloadable content(s) removed",
|
||||
"AutoloadUpdateAddedMessage": "{0} new update(s) added",
|
||||
"AutoloadUpdateRemovedMessage": "{0} missing update(s) removed",
|
||||
"ModWindowHeading": "{0} تعديل",
|
||||
"UserProfilesEditProfile": "تعديل المحدد",
|
||||
"Cancel": "إلغاء",
|
||||
@ -767,6 +788,7 @@
|
||||
"GraphicsScalingFilterBilinear": "Bilinear",
|
||||
"GraphicsScalingFilterNearest": "Nearest",
|
||||
"GraphicsScalingFilterFsr": "FSR",
|
||||
"GraphicsScalingFilterArea": "Area",
|
||||
"GraphicsScalingFilterLevelLabel": "المستوى",
|
||||
"GraphicsScalingFilterLevelTooltip": "اضبط مستوى وضوح FSR 1.0. الأعلى هو أكثر وضوحا.",
|
||||
"SmaaLow": "SMAA منخفض",
|
||||
|
@ -10,7 +10,10 @@
|
||||
"SettingsTabSystemUseHypervisor": "Hypervisor verwenden",
|
||||
"MenuBarFile": "_Datei",
|
||||
"MenuBarFileOpenFromFile": "Datei _öffnen",
|
||||
"MenuBarFileOpenFromFileError": "No applications found in selected file.",
|
||||
"MenuBarFileOpenUnpacked": "_Entpacktes Spiel öffnen",
|
||||
"MenuBarFileLoadDlcFromFolder": "Load DLC From Folder",
|
||||
"MenuBarFileLoadTitleUpdatesFromFolder": "Load Title Updates From Folder",
|
||||
"MenuBarFileOpenEmuFolder": "Ryujinx-Ordner öffnen",
|
||||
"MenuBarFileOpenLogsFolder": "Logs-Ordner öffnen",
|
||||
"MenuBarFileExit": "_Beenden",
|
||||
@ -103,6 +106,8 @@
|
||||
"SettingsTabGeneralHideCursorOnIdle": "Mauszeiger bei Inaktivität ausblenden",
|
||||
"SettingsTabGeneralHideCursorAlways": "Immer",
|
||||
"SettingsTabGeneralGameDirectories": "Spielverzeichnisse",
|
||||
"SettingsTabGeneralAutoloadDirectories": "Autoload DLC/Updates Directories",
|
||||
"SettingsTabGeneralAutoloadNote": "DLC and Updates which refer to missing files will be unloaded automatically",
|
||||
"SettingsTabGeneralAdd": "Hinzufügen",
|
||||
"SettingsTabGeneralRemove": "Entfernen",
|
||||
"SettingsTabSystem": "System",
|
||||
@ -411,6 +416,7 @@
|
||||
"GameListContextMenuToggleFavorite": "Als Favoriten hinzufügen/entfernen",
|
||||
"GameListContextMenuToggleFavoriteToolTip": "Aktiviert den Favoriten-Status des Spiels",
|
||||
"SettingsTabGeneralTheme": "Design:",
|
||||
"SettingsTabGeneralThemeAuto": "Auto",
|
||||
"SettingsTabGeneralThemeDark": "Dunkel",
|
||||
"SettingsTabGeneralThemeLight": "Hell",
|
||||
"ControllerSettingsConfigureGeneral": "Konfigurieren",
|
||||
@ -561,6 +567,9 @@
|
||||
"AddGameDirBoxTooltip": "Gibt das Spielverzeichnis an, das der Liste hinzuzufügt wird",
|
||||
"AddGameDirTooltip": "Fügt ein neues Spielverzeichnis hinzu",
|
||||
"RemoveGameDirTooltip": "Entfernt das ausgewähltes Spielverzeichnis",
|
||||
"AddAutoloadDirBoxTooltip": "Enter an autoload directory to add to the list",
|
||||
"AddAutoloadDirTooltip": "Add an autoload directory to the list",
|
||||
"RemoveAutoloadDirTooltip": "Remove selected autoload directory",
|
||||
"CustomThemeCheckTooltip": "Verwende ein eigenes Design für die Emulator-Benutzeroberfläche",
|
||||
"CustomThemePathTooltip": "Gibt den Pfad zum Design für die Emulator-Benutzeroberfläche an",
|
||||
"CustomThemeBrowseTooltip": "Ermöglicht die Suche nach einem benutzerdefinierten Design für die Emulator-Benutzeroberfläche",
|
||||
@ -606,6 +615,8 @@
|
||||
"DebugLogTooltip": "Ausgabe von Debug-Logs in der Konsole.\n\nVerwende diese Option nur auf ausdrückliche Anweisung von Ryujinx Entwicklern, da sie das Lesen der Protokolle erschwert und die Leistung des Emulators verschlechtert.",
|
||||
"LoadApplicationFileTooltip": "Öffnet die Dateiauswahl um Datei zu laden, welche mit der Switch kompatibel ist",
|
||||
"LoadApplicationFolderTooltip": "Öffnet die Dateiauswahl um ein Spiel zu laden, welches mit der Switch kompatibel ist",
|
||||
"LoadDlcFromFolderTooltip": "Open a file explorer to choose one or more folders to bulk load DLC from",
|
||||
"LoadTitleUpdatesFromFolderTooltip": "Open a file explorer to choose one or more folders to bulk load title updates from",
|
||||
"OpenRyujinxFolderTooltip": "Öffnet den Ordner, der das Ryujinx Dateisystem enthält",
|
||||
"OpenRyujinxLogsTooltip": "Öffnet den Ordner, in welchem die Logs gespeichert werden",
|
||||
"ExitTooltip": "Beendet Ryujinx",
|
||||
@ -657,6 +668,8 @@
|
||||
"OpenSetupGuideMessage": "Öffne den 'Setup Guide'",
|
||||
"NoUpdate": "Kein Update",
|
||||
"TitleUpdateVersionLabel": "Version {0} - {1}",
|
||||
"TitleBundledUpdateVersionLabel": "Bundled: Version {0}",
|
||||
"TitleBundledDlcLabel": "Bundled:",
|
||||
"RyujinxInfo": "Ryujinx - Info",
|
||||
"RyujinxConfirm": "Ryujinx - Bestätigung",
|
||||
"FileDialogAllTypes": "Alle Typen",
|
||||
@ -714,9 +727,17 @@
|
||||
"DlcWindowTitle": "Spiel-DLC verwalten",
|
||||
"ModWindowTitle": "Manage Mods for {0} ({1})",
|
||||
"UpdateWindowTitle": "Spiel-Updates verwalten",
|
||||
"UpdateWindowUpdateAddedMessage": "{0} new update(s) added",
|
||||
"UpdateWindowBundledContentNotice": "Bundled updates cannot be removed, only disabled.",
|
||||
"CheatWindowHeading": "Cheats verfügbar für {0} [{1}]",
|
||||
"DlcWindowBundledContentNotice": "Bundled DLC cannot be removed, only disabled.",
|
||||
"BuildId": "BuildId:",
|
||||
"DlcWindowHeading": "DLC verfügbar für {0} [{1}]",
|
||||
"DlcWindowDlcAddedMessage": "{0} new downloadable content(s) added",
|
||||
"AutoloadDlcAddedMessage": "{0} new downloadable content(s) added",
|
||||
"AutoloadDlcRemovedMessage": "{0} missing downloadable content(s) removed",
|
||||
"AutoloadUpdateAddedMessage": "{0} new update(s) added",
|
||||
"AutoloadUpdateRemovedMessage": "{0} missing update(s) removed",
|
||||
"ModWindowHeading": "{0} Mod(s)",
|
||||
"UserProfilesEditProfile": "Profil bearbeiten",
|
||||
"Cancel": "Abbrechen",
|
||||
@ -767,6 +788,7 @@
|
||||
"GraphicsScalingFilterBilinear": "Bilinear",
|
||||
"GraphicsScalingFilterNearest": "Nächstes",
|
||||
"GraphicsScalingFilterFsr": "FSR",
|
||||
"GraphicsScalingFilterArea": "Area",
|
||||
"GraphicsScalingFilterLevelLabel": "Stufe",
|
||||
"GraphicsScalingFilterLevelTooltip": "FSR 1.0 Schärfelevel festlegen. Höher ist schärfer.",
|
||||
"SmaaLow": "SMAA Niedrig",
|
||||
|
@ -10,7 +10,10 @@
|
||||
"SettingsTabSystemUseHypervisor": "Χρήση Hypervisor",
|
||||
"MenuBarFile": "_Αρχείο",
|
||||
"MenuBarFileOpenFromFile": "_Φόρτωση Αρχείου Εφαρμογής",
|
||||
"MenuBarFileOpenFromFileError": "No applications found in selected file.",
|
||||
"MenuBarFileOpenUnpacked": "Φόρτωση Απακετάριστου _Παιχνιδιού",
|
||||
"MenuBarFileLoadDlcFromFolder": "Load DLC From Folder",
|
||||
"MenuBarFileLoadTitleUpdatesFromFolder": "Load Title Updates From Folder",
|
||||
"MenuBarFileOpenEmuFolder": "Άνοιγμα Φακέλου Ryujinx",
|
||||
"MenuBarFileOpenLogsFolder": "Άνοιγμα Φακέλου Καταγραφής",
|
||||
"MenuBarFileExit": "_Έξοδος",
|
||||
@ -103,6 +106,8 @@
|
||||
"SettingsTabGeneralHideCursorOnIdle": "Απόκρυψη Δρομέα στην Αδράνεια",
|
||||
"SettingsTabGeneralHideCursorAlways": "Πάντα",
|
||||
"SettingsTabGeneralGameDirectories": "Τοποθεσίες παιχνιδιών",
|
||||
"SettingsTabGeneralAutoloadDirectories": "Autoload DLC/Updates Directories",
|
||||
"SettingsTabGeneralAutoloadNote": "DLC and Updates which refer to missing files will be unloaded automatically",
|
||||
"SettingsTabGeneralAdd": "Προσθήκη",
|
||||
"SettingsTabGeneralRemove": "Αφαίρεση",
|
||||
"SettingsTabSystem": "Σύστημα",
|
||||
@ -411,6 +416,7 @@
|
||||
"GameListContextMenuToggleFavorite": "Εναλλαγή Αγαπημένου",
|
||||
"GameListContextMenuToggleFavoriteToolTip": "Εναλλαγή της Κατάστασης Αγαπημένο του Παιχνιδιού",
|
||||
"SettingsTabGeneralTheme": "Theme:",
|
||||
"SettingsTabGeneralThemeAuto": "Auto",
|
||||
"SettingsTabGeneralThemeDark": "Dark",
|
||||
"SettingsTabGeneralThemeLight": "Light",
|
||||
"ControllerSettingsConfigureGeneral": "Παραμέτρων",
|
||||
@ -561,6 +567,9 @@
|
||||
"AddGameDirBoxTooltip": "Εισαγάγετε μία τοποθεσία παιχνιδιών για προσθήκη στη λίστα",
|
||||
"AddGameDirTooltip": "Προσθέστε μία τοποθεσία παιχνιδιών στη λίστα",
|
||||
"RemoveGameDirTooltip": "Αφαιρέστε την επιλεγμένη τοποθεσία παιχνιδιών",
|
||||
"AddAutoloadDirBoxTooltip": "Enter an autoload directory to add to the list",
|
||||
"AddAutoloadDirTooltip": "Add an autoload directory to the list",
|
||||
"RemoveAutoloadDirTooltip": "Remove selected autoload directory",
|
||||
"CustomThemeCheckTooltip": "Ενεργοποίηση ή απενεργοποίηση προσαρμοσμένων θεμάτων στο GUI",
|
||||
"CustomThemePathTooltip": "Διαδρομή προς το προσαρμοσμένο θέμα GUI",
|
||||
"CustomThemeBrowseTooltip": "Αναζητήστε ένα προσαρμοσμένο θέμα GUI",
|
||||
@ -606,6 +615,8 @@
|
||||
"DebugLogTooltip": "Ενεργοποιεί την εκτύπωση μηνυμάτων αρχείου καταγραφής εντοπισμού σφαλμάτων",
|
||||
"LoadApplicationFileTooltip": "Ανοίξτε έναν επιλογέα αρχείων για να επιλέξετε ένα αρχείο συμβατό με το Switch για φόρτωση",
|
||||
"LoadApplicationFolderTooltip": "Ανοίξτε έναν επιλογέα αρχείων για να επιλέξετε μία μη συσκευασμένη εφαρμογή, συμβατή με το Switch για φόρτωση",
|
||||
"LoadDlcFromFolderTooltip": "Open a file explorer to choose one or more folders to bulk load DLC from",
|
||||
"LoadTitleUpdatesFromFolderTooltip": "Open a file explorer to choose one or more folders to bulk load title updates from",
|
||||
"OpenRyujinxFolderTooltip": "Ανοίξτε το φάκελο συστήματος αρχείων Ryujinx",
|
||||
"OpenRyujinxLogsTooltip": "Ανοίξτε το φάκελο στον οποίο διατηρούνται τα αρχεία καταγραφής",
|
||||
"ExitTooltip": "Έξοδος από το Ryujinx",
|
||||
@ -657,6 +668,8 @@
|
||||
"OpenSetupGuideMessage": "Ανοίξτε τον Οδηγό Εγκατάστασης.",
|
||||
"NoUpdate": "Καμία Eνημέρωση",
|
||||
"TitleUpdateVersionLabel": "Version {0} - {1}",
|
||||
"TitleBundledUpdateVersionLabel": "Bundled: Version {0}",
|
||||
"TitleBundledDlcLabel": "Bundled:",
|
||||
"RyujinxInfo": "Ryujinx - Πληροφορίες",
|
||||
"RyujinxConfirm": "Ryujinx - Επιβεβαίωση",
|
||||
"FileDialogAllTypes": "Όλοι οι τύποι",
|
||||
@ -714,9 +727,17 @@
|
||||
"DlcWindowTitle": "Downloadable Content Manager",
|
||||
"ModWindowTitle": "Manage Mods for {0} ({1})",
|
||||
"UpdateWindowTitle": "Διαχειριστής Ενημερώσεων Τίτλου",
|
||||
"UpdateWindowUpdateAddedMessage": "{0} new update(s) added",
|
||||
"UpdateWindowBundledContentNotice": "Bundled updates cannot be removed, only disabled.",
|
||||
"CheatWindowHeading": "Διαθέσιμα Cheats για {0} [{1}]",
|
||||
"BuildId": "BuildId:",
|
||||
"DlcWindowBundledContentNotice": "Bundled DLC cannot be removed, only disabled.",
|
||||
"DlcWindowHeading": "{0} Downloadable Content(s) available for {1} ({2})",
|
||||
"DlcWindowDlcAddedMessage": "{0} new downloadable content(s) added",
|
||||
"AutoloadDlcAddedMessage": "{0} new downloadable content(s) added",
|
||||
"AutoloadDlcRemovedMessage": "{0} missing downloadable content(s) removed",
|
||||
"AutoloadUpdateAddedMessage": "{0} new update(s) added",
|
||||
"AutoloadUpdateRemovedMessage": "{0} missing update(s) removed",
|
||||
"ModWindowHeading": "{0} Mod(s)",
|
||||
"UserProfilesEditProfile": "Επεξεργασία Επιλεγμένων",
|
||||
"Cancel": "Ακύρωση",
|
||||
@ -767,6 +788,7 @@
|
||||
"GraphicsScalingFilterBilinear": "Bilinear",
|
||||
"GraphicsScalingFilterNearest": "Nearest",
|
||||
"GraphicsScalingFilterFsr": "FSR",
|
||||
"GraphicsScalingFilterArea": "Area",
|
||||
"GraphicsScalingFilterLevelLabel": "Επίπεδο",
|
||||
"GraphicsScalingFilterLevelTooltip": "Set FSR 1.0 sharpening level. Higher is sharper.",
|
||||
"SmaaLow": "Χαμηλό SMAA",
|
||||
|
@ -33,6 +33,7 @@
|
||||
"MenuBarToolsManageFileTypes": "Manage file types",
|
||||
"MenuBarToolsInstallFileTypes": "Install file types",
|
||||
"MenuBarToolsUninstallFileTypes": "Uninstall file types",
|
||||
"MenuBarToolsXCITrimmer": "Trim XCI Files",
|
||||
"MenuBarView": "_View",
|
||||
"MenuBarViewWindow": "Window Size",
|
||||
"MenuBarViewWindow720": "720p",
|
||||
@ -84,8 +85,11 @@
|
||||
"GameListContextMenuOpenModsDirectoryToolTip": "Opens the directory which contains Application's Mods",
|
||||
"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.",
|
||||
"GameListContextMenuTrimXCI": "Check and Trim XCI File",
|
||||
"GameListContextMenuTrimXCIToolTip": "Check and Trim XCI File to Save Disk Space",
|
||||
"StatusBarGamesLoaded": "{0}/{1} Games Loaded",
|
||||
"StatusBarSystemVersion": "System Version: {0}",
|
||||
"StatusBarXCIFileTrimming": "Trimming XCI File '{0}'",
|
||||
"LinuxVmMaxMapCountDialogTitle": "Low limit for memory mappings detected",
|
||||
"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.",
|
||||
@ -400,6 +404,8 @@
|
||||
"InputDialogTitle": "Input Dialog",
|
||||
"InputDialogOk": "OK",
|
||||
"InputDialogCancel": "Cancel",
|
||||
"InputDialogCancelling": "Cancelling",
|
||||
"InputDialogClose": "Close",
|
||||
"InputDialogAddNewProfileTitle": "Choose the Profile Name",
|
||||
"InputDialogAddNewProfileHeader": "Please Enter a Profile Name",
|
||||
"InputDialogAddNewProfileSubtext": "(Max Length: {0})",
|
||||
@ -439,13 +445,13 @@
|
||||
"DialogNcaExtractionMessage": "Extracting {0} section from {1}...",
|
||||
"DialogNcaExtractionTitle": "Ryujinx - NCA Section Extractor",
|
||||
"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.",
|
||||
"DialogUpdaterConvertFailedMessage": "Failed to convert the current Ryujinx version.",
|
||||
"DialogUpdaterCancelUpdateMessage": "Cancelling Update!",
|
||||
"DialogUpdaterAlreadyOnLatestVersionMessage": "You are already using the most updated 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.",
|
||||
"DialogUpdaterConvertFailedGithubMessage": "Failed to convert the received Ryujinx version from Github Release.",
|
||||
"DialogUpdaterConvertFailedMessage": "Unable to convert the current Ryujinx version.",
|
||||
"DialogUpdaterCancelUpdateMessage": "Update canceled!",
|
||||
"DialogUpdaterAlreadyOnLatestVersionMessage": "You are already using the latest version of Ryujinx!",
|
||||
"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 Ryujinx version received from GitHub.",
|
||||
"DialogUpdaterDownloadingMessage": "Downloading Update...",
|
||||
"DialogUpdaterExtractionMessage": "Extracting Update...",
|
||||
"DialogUpdaterRenamingMessage": "Renaming Update...",
|
||||
@ -455,7 +461,7 @@
|
||||
"DialogUpdaterNoInternetMessage": "You are not connected to the Internet!",
|
||||
"DialogUpdaterNoInternetSubMessage": "Please verify that you have a working Internet connection!",
|
||||
"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",
|
||||
"DialogThemeRestartMessage": "Theme has been saved. A restart is needed to apply the theme.",
|
||||
"DialogThemeRestartSubMessage": "Do you want to restart",
|
||||
@ -468,6 +474,7 @@
|
||||
"DialogUninstallFileTypesSuccessMessage": "Successfully uninstalled file types!",
|
||||
"DialogUninstallFileTypesErrorMessage": "Failed to uninstall file types.",
|
||||
"DialogOpenSettingsWindowLabel": "Open Settings Window",
|
||||
"DialogOpenXCITrimmerWindowLabel": "XCI Trimmer Window",
|
||||
"DialogControllerAppletTitle": "Controller Applet",
|
||||
"DialogMessageDialogErrorExceptionMessage": "Error displaying Message Dialog: {0}",
|
||||
"DialogSoftwareKeyboardErrorExceptionMessage": "Error displaying Software Keyboard: {0}",
|
||||
@ -670,6 +677,12 @@
|
||||
"TitleUpdateVersionLabel": "Version {0}",
|
||||
"TitleBundledUpdateVersionLabel": "Bundled: Version {0}",
|
||||
"TitleBundledDlcLabel": "Bundled:",
|
||||
"TitleXCIStatusPartialLabel": "Partial",
|
||||
"TitleXCIStatusTrimmableLabel": "Untrimmed",
|
||||
"TitleXCIStatusUntrimmableLabel": "Trimmed",
|
||||
"TitleXCIStatusFailedLabel": "(Failed)",
|
||||
"TitleXCICanSaveLabel": "Save {0:n0} Mb",
|
||||
"TitleXCISavingLabel": "Saved {0:n0} Mb",
|
||||
"RyujinxInfo": "Ryujinx - Info",
|
||||
"RyujinxConfirm": "Ryujinx - Confirmation",
|
||||
"FileDialogAllTypes": "All types",
|
||||
@ -722,17 +735,43 @@
|
||||
"SelectDlcDialogTitle": "Select DLC files",
|
||||
"SelectUpdateDialogTitle": "Select update files",
|
||||
"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",
|
||||
"CheatWindowTitle": "Cheats Manager",
|
||||
"DlcWindowTitle": "Manage Downloadable Content for {0} ({1})",
|
||||
"ModWindowTitle": "Manage Mods for {0} ({1})",
|
||||
"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",
|
||||
"UpdateWindowBundledContentNotice": "Bundled updates cannot be removed, only disabled.",
|
||||
"CheatWindowHeading": "Cheats Available for {0} [{1}]",
|
||||
"BuildId": "BuildId:",
|
||||
"DlcWindowBundledContentNotice": "Bundled DLC cannot be removed, only disabled.",
|
||||
"DlcWindowHeading": "{0} Downloadable Content(s)",
|
||||
"DlcWindowHeading": "{0} Downloadable Content(s) available for {1} ({2})",
|
||||
"DlcWindowDlcAddedMessage": "{0} new downloadable content(s) added",
|
||||
"AutoloadDlcAddedMessage": "{0} new downloadable content(s) added",
|
||||
"AutoloadDlcRemovedMessage": "{0} missing downloadable content(s) removed",
|
||||
@ -740,6 +779,7 @@
|
||||
"AutoloadUpdateRemovedMessage": "{0} missing update(s) removed",
|
||||
"ModWindowHeading": "{0} Mod(s)",
|
||||
"UserProfilesEditProfile": "Edit Selected",
|
||||
"Continue": "Continue",
|
||||
"Cancel": "Cancel",
|
||||
"Save": "Save",
|
||||
"Discard": "Discard",
|
||||
|
@ -10,7 +10,10 @@
|
||||
"SettingsTabSystemUseHypervisor": "Usar hipervisor",
|
||||
"MenuBarFile": "_Archivo",
|
||||
"MenuBarFileOpenFromFile": "_Cargar aplicación desde un archivo",
|
||||
"MenuBarFileOpenFromFileError": "No applications found in selected file.",
|
||||
"MenuBarFileOpenUnpacked": "Cargar juego _desempaquetado",
|
||||
"MenuBarFileLoadDlcFromFolder": "Load DLC From Folder",
|
||||
"MenuBarFileLoadTitleUpdatesFromFolder": "Load Title Updates From Folder",
|
||||
"MenuBarFileOpenEmuFolder": "Abrir carpeta de Ryujinx",
|
||||
"MenuBarFileOpenLogsFolder": "Abrir carpeta de registros",
|
||||
"MenuBarFileExit": "_Salir",
|
||||
@ -103,6 +106,8 @@
|
||||
"SettingsTabGeneralHideCursorOnIdle": "Ocultar cursor cuando esté inactivo",
|
||||
"SettingsTabGeneralHideCursorAlways": "Siempre",
|
||||
"SettingsTabGeneralGameDirectories": "Carpetas de juegos",
|
||||
"SettingsTabGeneralAutoloadDirectories": "Autoload DLC/Updates Directories",
|
||||
"SettingsTabGeneralAutoloadNote": "DLC and Updates which refer to missing files will be unloaded automatically",
|
||||
"SettingsTabGeneralAdd": "Agregar",
|
||||
"SettingsTabGeneralRemove": "Quitar",
|
||||
"SettingsTabSystem": "Sistema",
|
||||
@ -411,6 +416,7 @@
|
||||
"GameListContextMenuToggleFavorite": "Marcar favorito",
|
||||
"GameListContextMenuToggleFavoriteToolTip": "Marca o desmarca el juego como favorito",
|
||||
"SettingsTabGeneralTheme": "Tema:",
|
||||
"SettingsTabGeneralThemeAuto": "Auto",
|
||||
"SettingsTabGeneralThemeDark": "Oscuro",
|
||||
"SettingsTabGeneralThemeLight": "Claro",
|
||||
"ControllerSettingsConfigureGeneral": "Configurar",
|
||||
@ -561,6 +567,9 @@
|
||||
"AddGameDirBoxTooltip": "Elige un directorio de juegos para mostrar en la ventana principal",
|
||||
"AddGameDirTooltip": "Agrega un directorio de juegos a la lista",
|
||||
"RemoveGameDirTooltip": "Quita el directorio seleccionado de la lista",
|
||||
"AddAutoloadDirBoxTooltip": "Enter an autoload directory to add to the list",
|
||||
"AddAutoloadDirTooltip": "Add an autoload directory to the list",
|
||||
"RemoveAutoloadDirTooltip": "Remove selected autoload directory",
|
||||
"CustomThemeCheckTooltip": "Activa o desactiva los temas personalizados para la interfaz",
|
||||
"CustomThemePathTooltip": "Carpeta que contiene los temas personalizados para la interfaz",
|
||||
"CustomThemeBrowseTooltip": "Busca un tema personalizado para la interfaz",
|
||||
@ -606,6 +615,8 @@
|
||||
"DebugLogTooltip": "Escribe mensajes de debug en la consola\n\nActiva esto solo si un miembro del equipo te lo pide expresamente, pues hará que el registro sea difícil de leer y empeorará el rendimiento del emulador.",
|
||||
"LoadApplicationFileTooltip": "Abre el explorador de archivos para elegir un archivo compatible con Switch para cargar",
|
||||
"LoadApplicationFolderTooltip": "Abre el explorador de archivos para elegir un archivo desempaquetado y compatible con Switch para cargar",
|
||||
"LoadDlcFromFolderTooltip": "Open a file explorer to choose one or more folders to bulk load DLC from",
|
||||
"LoadTitleUpdatesFromFolderTooltip": "Open a file explorer to choose one or more folders to bulk load title updates from",
|
||||
"OpenRyujinxFolderTooltip": "Abre la carpeta de sistema de Ryujinx",
|
||||
"OpenRyujinxLogsTooltip": "Abre la carpeta en la que se guardan los registros",
|
||||
"ExitTooltip": "Cierra Ryujinx",
|
||||
@ -657,6 +668,8 @@
|
||||
"OpenSetupGuideMessage": "Abrir la guía de instalación",
|
||||
"NoUpdate": "No actualizado",
|
||||
"TitleUpdateVersionLabel": "Versión {0} - {1}",
|
||||
"TitleBundledUpdateVersionLabel": "Bundled: Version {0}",
|
||||
"TitleBundledDlcLabel": "Bundled:",
|
||||
"RyujinxInfo": "Ryujinx - Info",
|
||||
"RyujinxConfirm": "Ryujinx - Confirmación",
|
||||
"FileDialogAllTypes": "Todos los tipos",
|
||||
@ -714,9 +727,16 @@
|
||||
"DlcWindowTitle": "Administrar contenido descargable",
|
||||
"ModWindowTitle": "Manage Mods for {0} ({1})",
|
||||
"UpdateWindowTitle": "Administrar actualizaciones",
|
||||
"UpdateWindowUpdateAddedMessage": "{0} new update(s) added",
|
||||
"UpdateWindowBundledContentNotice": "Bundled updates cannot be removed, only disabled.",
|
||||
"CheatWindowHeading": "Cheats disponibles para {0} [{1}]",
|
||||
"BuildId": "Id de compilación:",
|
||||
"DlcWindowHeading": "Contenido descargable disponible para {0} [{1}]",
|
||||
"DlcWindowDlcAddedMessage": "{0} new downloadable content(s) added",
|
||||
"AutoloadDlcAddedMessage": "{0} new downloadable content(s) added",
|
||||
"AutoloadDlcRemovedMessage": "{0} missing downloadable content(s) removed",
|
||||
"AutoloadUpdateAddedMessage": "{0} new update(s) added",
|
||||
"AutoloadUpdateRemovedMessage": "{0} missing update(s) removed",
|
||||
"ModWindowHeading": "{0} Mod(s)",
|
||||
"UserProfilesEditProfile": "Editar selección",
|
||||
"Cancel": "Cancelar",
|
||||
@ -767,6 +787,7 @@
|
||||
"GraphicsScalingFilterBilinear": "Bilinear\n",
|
||||
"GraphicsScalingFilterNearest": "Cercano",
|
||||
"GraphicsScalingFilterFsr": "FSR",
|
||||
"GraphicsScalingFilterArea": "Area",
|
||||
"GraphicsScalingFilterLevelLabel": "Nivel",
|
||||
"GraphicsScalingFilterLevelTooltip": "Ajuste el nivel de nitidez FSR 1.0. Mayor es más nítido.",
|
||||
"SmaaLow": "SMAA Bajo",
|
||||
|
@ -100,7 +100,7 @@
|
||||
"SettingsTabGeneralCheckUpdatesOnLaunch": "Vérifier les mises à jour au démarrage",
|
||||
"SettingsTabGeneralShowConfirmExitDialog": "Afficher le message de \"Confirmation de sortie\"",
|
||||
"SettingsTabGeneralRememberWindowState": "Mémoriser la taille/position de la fenêtre",
|
||||
"SettingsTabGeneralShowTitleBar": "Show Title Bar (Requires restart)",
|
||||
"SettingsTabGeneralShowTitleBar": "Afficher Barre de Titre (Nécessite redémarrage)",
|
||||
"SettingsTabGeneralHideCursor": "Masquer le Curseur :",
|
||||
"SettingsTabGeneralHideCursorNever": "Jamais",
|
||||
"SettingsTabGeneralHideCursorOnIdle": "Masquer le curseur si inactif",
|
||||
@ -151,7 +151,7 @@
|
||||
"SettingsTabSystemAudioBackendSDL2": "SDL2",
|
||||
"SettingsTabSystemHacks": "Hacks",
|
||||
"SettingsTabSystemHacksNote": "Cela peut causer des instabilités",
|
||||
"SettingsTabSystemDramSize": "Taille de la DRAM:",
|
||||
"SettingsTabSystemDramSize": "Taille de la DRAM :",
|
||||
"SettingsTabSystemDramSize4GiB": "4GiO",
|
||||
"SettingsTabSystemDramSize6GiB": "6GiO",
|
||||
"SettingsTabSystemDramSize8GiB": "8GiO",
|
||||
@ -181,7 +181,7 @@
|
||||
"SettingsTabGraphicsAspectRatio32x9": "32:9",
|
||||
"SettingsTabGraphicsAspectRatioStretch": "Étirer pour remplir la fenêtre",
|
||||
"SettingsTabGraphicsDeveloperOptions": "Options développeur",
|
||||
"SettingsTabGraphicsShaderDumpPath": "Chemin du dossier de copie des shaders:",
|
||||
"SettingsTabGraphicsShaderDumpPath": "Chemin du dossier de copie des shaders :",
|
||||
"SettingsTabLogging": "Journaux",
|
||||
"SettingsTabLoggingLogging": "Journaux",
|
||||
"SettingsTabLoggingEnableLoggingToFile": "Activer la sauvegarde des journaux vers un fichier",
|
||||
@ -387,7 +387,7 @@
|
||||
"UserProfilesSelectedUserProfile": "Profil utilisateur sélectionné :",
|
||||
"UserProfilesSaveProfileName": "Enregistrer le nom du profil",
|
||||
"UserProfilesChangeProfileImage": "Changer l'image du profil",
|
||||
"UserProfilesAvailableUserProfiles": "Profils utilisateurs disponibles:",
|
||||
"UserProfilesAvailableUserProfiles": "Profils utilisateurs disponibles :",
|
||||
"UserProfilesAddNewProfile": "Créer un profil",
|
||||
"UserProfilesDelete": "Supprimer",
|
||||
"UserProfilesClose": "Fermer",
|
||||
@ -669,7 +669,7 @@
|
||||
"NoUpdate": "Aucune mise à jour",
|
||||
"TitleUpdateVersionLabel": "Version {0}",
|
||||
"TitleBundledUpdateVersionLabel": "Inclus avec le jeu: Version {0}",
|
||||
"TitleBundledDlcLabel": "Inclus avec le jeu:",
|
||||
"TitleBundledDlcLabel": "Inclus avec le jeu :",
|
||||
"RyujinxInfo": "Ryujinx - Info",
|
||||
"RyujinxConfirm": "Ryujinx - Confirmation",
|
||||
"FileDialogAllTypes": "Tous les types",
|
||||
|
@ -10,7 +10,10 @@
|
||||
"SettingsTabSystemUseHypervisor": "השתמש ב Hypervisor",
|
||||
"MenuBarFile": "_קובץ",
|
||||
"MenuBarFileOpenFromFile": "_טען יישום מקובץ",
|
||||
"MenuBarFileOpenFromFileError": "No applications found in selected file.",
|
||||
"MenuBarFileOpenUnpacked": "טען משחק _שאינו ארוז",
|
||||
"MenuBarFileLoadDlcFromFolder": "Load DLC From Folder",
|
||||
"MenuBarFileLoadTitleUpdatesFromFolder": "Load Title Updates From Folder",
|
||||
"MenuBarFileOpenEmuFolder": "פתח את תיקיית ריוג'ינקס",
|
||||
"MenuBarFileOpenLogsFolder": "פתח את תיקיית קבצי הלוג",
|
||||
"MenuBarFileExit": "_יציאה",
|
||||
@ -103,6 +106,8 @@
|
||||
"SettingsTabGeneralHideCursorOnIdle": "במצב סרק",
|
||||
"SettingsTabGeneralHideCursorAlways": "תמיד",
|
||||
"SettingsTabGeneralGameDirectories": "תקיות משחקים",
|
||||
"SettingsTabGeneralAutoloadDirectories": "Autoload DLC/Updates Directories",
|
||||
"SettingsTabGeneralAutoloadNote": "DLC and Updates which refer to missing files will be unloaded automatically",
|
||||
"SettingsTabGeneralAdd": "הוסף",
|
||||
"SettingsTabGeneralRemove": "הסר",
|
||||
"SettingsTabSystem": "מערכת",
|
||||
@ -411,6 +416,7 @@
|
||||
"GameListContextMenuToggleFavorite": "למתג העדפה",
|
||||
"GameListContextMenuToggleFavoriteToolTip": "למתג סטטוס העדפה של משחק",
|
||||
"SettingsTabGeneralTheme": "ערכת נושא:",
|
||||
"SettingsTabGeneralThemeAuto": "Auto",
|
||||
"SettingsTabGeneralThemeDark": "כהה",
|
||||
"SettingsTabGeneralThemeLight": "בהיר",
|
||||
"ControllerSettingsConfigureGeneral": "הגדר",
|
||||
@ -561,6 +567,9 @@
|
||||
"AddGameDirBoxTooltip": "הזן תקיית משחקים כדי להוסיף לרשימה",
|
||||
"AddGameDirTooltip": "הוסף תקיית משחקים לרשימה",
|
||||
"RemoveGameDirTooltip": "הסר את תקיית המשחקים שנבחרה",
|
||||
"AddAutoloadDirBoxTooltip": "Enter an autoload directory to add to the list",
|
||||
"AddAutoloadDirTooltip": "Add an autoload directory to the list",
|
||||
"RemoveAutoloadDirTooltip": "Remove selected autoload directory",
|
||||
"CustomThemeCheckTooltip": "השתמש בעיצוב מותאם אישית של אבלוניה עבור ה-ממשק הגראפי כדי לשנות את המראה של תפריטי האמולטור",
|
||||
"CustomThemePathTooltip": "נתיב לערכת נושא לממשק גראפי מותאם אישית",
|
||||
"CustomThemeBrowseTooltip": "חפש עיצוב ממשק גראפי מותאם אישית",
|
||||
@ -606,6 +615,8 @@
|
||||
"DebugLogTooltip": "מדפיס הודעות יומן ניפוי באגים בשורת הפקודות.",
|
||||
"LoadApplicationFileTooltip": "פתח סייר קבצים כדי לבחור קובץ תואם סוויץ' לטעינה",
|
||||
"LoadApplicationFolderTooltip": "פתח סייר קבצים כדי לבחור יישום תואם סוויץ', לא ארוז לטעינה.",
|
||||
"LoadDlcFromFolderTooltip": "Open a file explorer to choose one or more folders to bulk load DLC from",
|
||||
"LoadTitleUpdatesFromFolderTooltip": "Open a file explorer to choose one or more folders to bulk load title updates from",
|
||||
"OpenRyujinxFolderTooltip": "פתח את תיקיית מערכת הקבצים ריוג'ינקס",
|
||||
"OpenRyujinxLogsTooltip": "פותח את התיקיה שאליה נכתבים רישומים",
|
||||
"ExitTooltip": "צא מריוג'ינקס",
|
||||
@ -657,6 +668,8 @@
|
||||
"OpenSetupGuideMessage": "פתח מדריך התקנה",
|
||||
"NoUpdate": "אין עדכון",
|
||||
"TitleUpdateVersionLabel": "גרסה {0}",
|
||||
"TitleBundledUpdateVersionLabel": "Bundled: Version {0}",
|
||||
"TitleBundledDlcLabel": "Bundled:",
|
||||
"RyujinxInfo": "ריוג'ינקס - מידע",
|
||||
"RyujinxConfirm": "ריוג'ינקס - אישור",
|
||||
"FileDialogAllTypes": "כל הסוגים",
|
||||
@ -714,9 +727,17 @@
|
||||
"DlcWindowTitle": "נהל הרחבות משחק עבור {0} ({1})",
|
||||
"ModWindowTitle": "Manage Mods for {0} ({1})",
|
||||
"UpdateWindowTitle": "נהל עדכוני משחקים",
|
||||
"UpdateWindowUpdateAddedMessage": "{0} new update(s) added",
|
||||
"UpdateWindowBundledContentNotice": "Bundled updates cannot be removed, only disabled.",
|
||||
"CheatWindowHeading": "צ'יטים זמינים עבור {0} [{1}]",
|
||||
"BuildId": "מזהה בניה:",
|
||||
"DlcWindowBundledContentNotice": "Bundled DLC cannot be removed, only disabled.",
|
||||
"DlcWindowHeading": "{0} הרחבות משחק",
|
||||
"DlcWindowDlcAddedMessage": "{0} new downloadable content(s) added",
|
||||
"AutoloadDlcAddedMessage": "{0} new downloadable content(s) added",
|
||||
"AutoloadDlcRemovedMessage": "{0} missing downloadable content(s) removed",
|
||||
"AutoloadUpdateAddedMessage": "{0} new update(s) added",
|
||||
"AutoloadUpdateRemovedMessage": "{0} missing update(s) removed",
|
||||
"ModWindowHeading": "{0} מוד(ים)",
|
||||
"UserProfilesEditProfile": "ערוך נבחר/ים",
|
||||
"Cancel": "בטל",
|
||||
@ -767,6 +788,7 @@
|
||||
"GraphicsScalingFilterBilinear": "Bilinear",
|
||||
"GraphicsScalingFilterNearest": "Nearest",
|
||||
"GraphicsScalingFilterFsr": "FSR",
|
||||
"GraphicsScalingFilterArea": "Area",
|
||||
"GraphicsScalingFilterLevelLabel": "רמה",
|
||||
"GraphicsScalingFilterLevelTooltip": "Set FSR 1.0 sharpening level. Higher is sharper.",
|
||||
"SmaaLow": "SMAA נמוך",
|
||||
|
@ -788,12 +788,12 @@
|
||||
"GraphicsScalingFilterBilinear": "Bilineare",
|
||||
"GraphicsScalingFilterNearest": "Nearest",
|
||||
"GraphicsScalingFilterFsr": "FSR",
|
||||
"GraphicsScalingFilterArea": "Area",
|
||||
"GraphicsScalingFilterLevelLabel": "Livello",
|
||||
"GraphicsScalingFilterLevelTooltip": "Imposta il livello di nitidezza di FSR 1.0. Valori più alti comportano una maggiore nitidezza.",
|
||||
"SmaaLow": "SMAA Basso",
|
||||
"SmaaMedium": "SMAA Medio",
|
||||
"SmaaHigh": "SMAA Alto",
|
||||
"GraphicsScalingFilterArea": "Area",
|
||||
"SmaaUltra": "SMAA Ultra",
|
||||
"UserEditorTitle": "Modificare L'Utente",
|
||||
"UserEditorTitleCreate": "Crea Un Utente",
|
||||
|
@ -10,7 +10,10 @@
|
||||
"SettingsTabSystemUseHypervisor": "ハイパーバイザーを使用",
|
||||
"MenuBarFile": "ファイル(_F)",
|
||||
"MenuBarFileOpenFromFile": "ファイルからアプリケーションをロード(_L)",
|
||||
"MenuBarFileOpenFromFileError": "No applications found in selected file.",
|
||||
"MenuBarFileOpenUnpacked": "展開されたゲームをロード",
|
||||
"MenuBarFileLoadDlcFromFolder": "Load DLC From Folder",
|
||||
"MenuBarFileLoadTitleUpdatesFromFolder": "Load Title Updates From Folder",
|
||||
"MenuBarFileOpenEmuFolder": "Ryujinx フォルダを開く",
|
||||
"MenuBarFileOpenLogsFolder": "ログフォルダを開く",
|
||||
"MenuBarFileExit": "終了(_E)",
|
||||
@ -103,6 +106,8 @@
|
||||
"SettingsTabGeneralHideCursorOnIdle": "アイドル時",
|
||||
"SettingsTabGeneralHideCursorAlways": "常時",
|
||||
"SettingsTabGeneralGameDirectories": "ゲームディレクトリ",
|
||||
"SettingsTabGeneralAutoloadDirectories": "Autoload DLC/Updates Directories",
|
||||
"SettingsTabGeneralAutoloadNote": "DLC and Updates which refer to missing files will be unloaded automatically",
|
||||
"SettingsTabGeneralAdd": "追加",
|
||||
"SettingsTabGeneralRemove": "削除",
|
||||
"SettingsTabSystem": "システム",
|
||||
@ -411,6 +416,7 @@
|
||||
"GameListContextMenuToggleFavorite": "お気に入りを切り替え",
|
||||
"GameListContextMenuToggleFavoriteToolTip": "ゲームをお気に入りに含めるかどうかを切り替えます",
|
||||
"SettingsTabGeneralTheme": "テーマ:",
|
||||
"SettingsTabGeneralThemeAuto": "Auto",
|
||||
"SettingsTabGeneralThemeDark": "ダーク",
|
||||
"SettingsTabGeneralThemeLight": "ライト",
|
||||
"ControllerSettingsConfigureGeneral": "設定",
|
||||
@ -561,6 +567,9 @@
|
||||
"AddGameDirBoxTooltip": "リストに追加するゲームディレクトリを入力します",
|
||||
"AddGameDirTooltip": "リストにゲームディレクトリを追加します",
|
||||
"RemoveGameDirTooltip": "選択したゲームディレクトリを削除します",
|
||||
"AddAutoloadDirBoxTooltip": "Enter an autoload directory to add to the list",
|
||||
"AddAutoloadDirTooltip": "Add an autoload directory to the list",
|
||||
"RemoveAutoloadDirTooltip": "Remove selected autoload directory",
|
||||
"CustomThemeCheckTooltip": "エミュレータのメニュー外観を変更するためカスタム Avalonia テーマを使用します",
|
||||
"CustomThemePathTooltip": "カスタム GUI テーマのパスです",
|
||||
"CustomThemeBrowseTooltip": "カスタム GUI テーマを参照します",
|
||||
@ -606,6 +615,8 @@
|
||||
"DebugLogTooltip": "デバッグログメッセージをコンソールに出力します.\n\nログが読みづらくなり,エミュレータのパフォーマンスが低下するため,開発者から特別な指示がある場合のみ使用してください.",
|
||||
"LoadApplicationFileTooltip": "ロードする Switch 互換のファイルを選択するためファイルエクスプローラを開きます",
|
||||
"LoadApplicationFolderTooltip": "ロードする Switch 互換の展開済みアプリケーションを選択するためファイルエクスプローラを開きます",
|
||||
"LoadDlcFromFolderTooltip": "Open a file explorer to choose one or more folders to bulk load DLC from",
|
||||
"LoadTitleUpdatesFromFolderTooltip": "Open a file explorer to choose one or more folders to bulk load title updates from",
|
||||
"OpenRyujinxFolderTooltip": "Ryujinx ファイルシステムフォルダを開きます",
|
||||
"OpenRyujinxLogsTooltip": "ログが格納されるフォルダを開きます",
|
||||
"ExitTooltip": "Ryujinx を終了します",
|
||||
@ -657,6 +668,8 @@
|
||||
"OpenSetupGuideMessage": "セットアップガイドを開く",
|
||||
"NoUpdate": "アップデートなし",
|
||||
"TitleUpdateVersionLabel": "バージョン {0} - {1}",
|
||||
"TitleBundledUpdateVersionLabel": "Bundled: Version {0}",
|
||||
"TitleBundledDlcLabel": "Bundled:",
|
||||
"RyujinxInfo": "Ryujinx - 情報",
|
||||
"RyujinxConfirm": "Ryujinx - 確認",
|
||||
"FileDialogAllTypes": "すべての種別",
|
||||
@ -714,9 +727,16 @@
|
||||
"DlcWindowTitle": "DLC 管理",
|
||||
"ModWindowTitle": "Manage Mods for {0} ({1})",
|
||||
"UpdateWindowTitle": "アップデート管理",
|
||||
"UpdateWindowUpdateAddedMessage": "{0} new update(s) added",
|
||||
"UpdateWindowBundledContentNotice": "Bundled updates cannot be removed, only disabled.",
|
||||
"CheatWindowHeading": "利用可能なチート {0} [{1}]",
|
||||
"BuildId": "ビルドID:",
|
||||
"DlcWindowHeading": "利用可能な DLC {0} [{1}]",
|
||||
"DlcWindowDlcAddedMessage": "{0} new downloadable content(s) added",
|
||||
"AutoloadDlcAddedMessage": "{0} new downloadable content(s) added",
|
||||
"AutoloadDlcRemovedMessage": "{0} missing downloadable content(s) removed",
|
||||
"AutoloadUpdateAddedMessage": "{0} new update(s) added",
|
||||
"AutoloadUpdateRemovedMessage": "{0} missing update(s) removed",
|
||||
"ModWindowHeading": "{0} Mod(s)",
|
||||
"UserProfilesEditProfile": "編集",
|
||||
"Cancel": "キャンセル",
|
||||
@ -767,6 +787,7 @@
|
||||
"GraphicsScalingFilterBilinear": "Bilinear",
|
||||
"GraphicsScalingFilterNearest": "Nearest",
|
||||
"GraphicsScalingFilterFsr": "FSR",
|
||||
"GraphicsScalingFilterArea": "Area",
|
||||
"GraphicsScalingFilterLevelLabel": "レベル",
|
||||
"GraphicsScalingFilterLevelTooltip": "FSR 1.0のシャープ化レベルを設定します. 高い値ほどシャープになります.",
|
||||
"SmaaLow": "SMAA Low",
|
||||
|
@ -10,7 +10,10 @@
|
||||
"SettingsTabSystemUseHypervisor": "하이퍼바이저 사용하기",
|
||||
"MenuBarFile": "_파일",
|
||||
"MenuBarFileOpenFromFile": "_파일에서 응용 프로그램 불러오기",
|
||||
"MenuBarFileOpenFromFileError": "No applications found in selected file.",
|
||||
"MenuBarFileOpenUnpacked": "_압축을 푼 게임 불러오기",
|
||||
"MenuBarFileLoadDlcFromFolder": "Load DLC From Folder",
|
||||
"MenuBarFileLoadTitleUpdatesFromFolder": "Load Title Updates From Folder",
|
||||
"MenuBarFileOpenEmuFolder": "Ryujinx 폴더 열기",
|
||||
"MenuBarFileOpenLogsFolder": "로그 폴더 열기",
|
||||
"MenuBarFileExit": "_종료",
|
||||
@ -103,6 +106,8 @@
|
||||
"SettingsTabGeneralHideCursorOnIdle": "유휴 상태",
|
||||
"SettingsTabGeneralHideCursorAlways": "언제나",
|
||||
"SettingsTabGeneralGameDirectories": "게임 디렉터리",
|
||||
"SettingsTabGeneralAutoloadDirectories": "Autoload DLC/Updates Directories",
|
||||
"SettingsTabGeneralAutoloadNote": "DLC and Updates which refer to missing files will be unloaded automatically",
|
||||
"SettingsTabGeneralAdd": "추가",
|
||||
"SettingsTabGeneralRemove": "제거",
|
||||
"SettingsTabSystem": "시스템",
|
||||
@ -411,6 +416,7 @@
|
||||
"GameListContextMenuToggleFavorite": "즐겨찾기 전환",
|
||||
"GameListContextMenuToggleFavoriteToolTip": "게임 즐겨찾기 상태 전환",
|
||||
"SettingsTabGeneralTheme": "테마:",
|
||||
"SettingsTabGeneralThemeAuto": "Auto",
|
||||
"SettingsTabGeneralThemeDark": "어두운 테마",
|
||||
"SettingsTabGeneralThemeLight": "밝은 테마",
|
||||
"ControllerSettingsConfigureGeneral": "구성",
|
||||
@ -561,6 +567,9 @@
|
||||
"AddGameDirBoxTooltip": "목록에 추가할 게임 디렉터리 입력",
|
||||
"AddGameDirTooltip": "목록에 게임 디렉터리 추가",
|
||||
"RemoveGameDirTooltip": "선택한 게임 디렉터리 제거",
|
||||
"AddAutoloadDirBoxTooltip": "Enter an autoload directory to add to the list",
|
||||
"AddAutoloadDirTooltip": "Add an autoload directory to the list",
|
||||
"RemoveAutoloadDirTooltip": "Remove selected autoload directory",
|
||||
"CustomThemeCheckTooltip": "GUI에 사용자 지정 Avalonia 테마를 사용하여 에뮬레이터 메뉴의 모양 변경",
|
||||
"CustomThemePathTooltip": "사용자 정의 GUI 테마 경로",
|
||||
"CustomThemeBrowseTooltip": "사용자 정의 GUI 테마 찾아보기",
|
||||
@ -606,6 +615,8 @@
|
||||
"DebugLogTooltip": "콘솔에 디버그 로그 메시지를 인쇄합니다.\n\n로그를 읽기 어렵게 만들고 에뮬레이터 성능을 악화시키므로 직원이 구체적으로 지시한 경우에만 사용하세요.",
|
||||
"LoadApplicationFileTooltip": "파일 탐색기를 열어 불러올 스위치 호환 파일 선택",
|
||||
"LoadApplicationFolderTooltip": "파일 탐색기를 열어 불러올 스위치 호환 압축 해제 응용 프로그램 선택",
|
||||
"LoadDlcFromFolderTooltip": "Open a file explorer to choose one or more folders to bulk load DLC from",
|
||||
"LoadTitleUpdatesFromFolderTooltip": "Open a file explorer to choose one or more folders to bulk load title updates from",
|
||||
"OpenRyujinxFolderTooltip": "Ryujinx 파일 시스템 폴더 열기",
|
||||
"OpenRyujinxLogsTooltip": "로그가 기록된 폴더 열기",
|
||||
"ExitTooltip": "Ryujinx 종료",
|
||||
@ -657,6 +668,8 @@
|
||||
"OpenSetupGuideMessage": "설정 가이드 열기",
|
||||
"NoUpdate": "업데이트 없음",
|
||||
"TitleUpdateVersionLabel": "버전 {0}",
|
||||
"TitleBundledUpdateVersionLabel": "Bundled: Version {0}",
|
||||
"TitleBundledDlcLabel": "Bundled:",
|
||||
"RyujinxInfo": "Ryujinx - 정보",
|
||||
"RyujinxConfirm": "Ryujinx - 확인",
|
||||
"FileDialogAllTypes": "모든 유형",
|
||||
@ -714,9 +727,17 @@
|
||||
"DlcWindowTitle": "{0} ({1})의 다운로드 가능한 콘텐츠 관리",
|
||||
"ModWindowTitle": "{0} ({1})의 Mod 관리",
|
||||
"UpdateWindowTitle": "타이틀 업데이트 관리자",
|
||||
"UpdateWindowUpdateAddedMessage": "{0} new update(s) added",
|
||||
"UpdateWindowBundledContentNotice": "Bundled updates cannot be removed, only disabled.",
|
||||
"CheatWindowHeading": "{0} [{1}]에 사용할 수 있는 치트",
|
||||
"BuildId": "빌드ID :",
|
||||
"DlcWindowBundledContentNotice": "Bundled DLC cannot be removed, only disabled.",
|
||||
"DlcWindowHeading": "{0} 내려받기 가능한 콘텐츠",
|
||||
"DlcWindowDlcAddedMessage": "{0} new downloadable content(s) added",
|
||||
"AutoloadDlcAddedMessage": "{0} new downloadable content(s) added",
|
||||
"AutoloadDlcRemovedMessage": "{0} missing downloadable content(s) removed",
|
||||
"AutoloadUpdateAddedMessage": "{0} new update(s) added",
|
||||
"AutoloadUpdateRemovedMessage": "{0} missing update(s) removed",
|
||||
"ModWindowHeading": "{0} Mod(s)",
|
||||
"UserProfilesEditProfile": "선택된 항목 편집",
|
||||
"Cancel": "취소",
|
||||
@ -767,6 +788,7 @@
|
||||
"GraphicsScalingFilterBilinear": "Bilinear",
|
||||
"GraphicsScalingFilterNearest": "Nearest",
|
||||
"GraphicsScalingFilterFsr": "FSR",
|
||||
"GraphicsScalingFilterArea": "Area",
|
||||
"GraphicsScalingFilterLevelLabel": "수준",
|
||||
"GraphicsScalingFilterLevelTooltip": "FSR 1.0의 샤프닝 레벨을 설정하세요. 높을수록 더 또렷해집니다.",
|
||||
"SmaaLow": "SMAA 낮음",
|
||||
|
@ -10,7 +10,10 @@
|
||||
"SettingsTabSystemUseHypervisor": "Użyj Hipernadzorcy",
|
||||
"MenuBarFile": "_Plik",
|
||||
"MenuBarFileOpenFromFile": "_Załaduj aplikację z pliku",
|
||||
"MenuBarFileOpenFromFileError": "No applications found in selected file.",
|
||||
"MenuBarFileOpenUnpacked": "Załaduj _rozpakowaną grę",
|
||||
"MenuBarFileLoadDlcFromFolder": "Load DLC From Folder",
|
||||
"MenuBarFileLoadTitleUpdatesFromFolder": "Load Title Updates From Folder",
|
||||
"MenuBarFileOpenEmuFolder": "Otwórz folder Ryujinx",
|
||||
"MenuBarFileOpenLogsFolder": "Otwórz folder plików dziennika zdarzeń",
|
||||
"MenuBarFileExit": "_Wyjdź",
|
||||
@ -103,6 +106,8 @@
|
||||
"SettingsTabGeneralHideCursorOnIdle": "Gdy bezczynny",
|
||||
"SettingsTabGeneralHideCursorAlways": "Zawsze",
|
||||
"SettingsTabGeneralGameDirectories": "Katalogi gier",
|
||||
"SettingsTabGeneralAutoloadDirectories": "Autoload DLC/Updates Directories",
|
||||
"SettingsTabGeneralAutoloadNote": "DLC and Updates which refer to missing files will be unloaded automatically",
|
||||
"SettingsTabGeneralAdd": "Dodaj",
|
||||
"SettingsTabGeneralRemove": "Usuń",
|
||||
"SettingsTabSystem": "System",
|
||||
@ -411,6 +416,7 @@
|
||||
"GameListContextMenuToggleFavorite": "Przełącz na ulubione",
|
||||
"GameListContextMenuToggleFavoriteToolTip": "Przełącz status Ulubionej Gry",
|
||||
"SettingsTabGeneralTheme": "Motyw:",
|
||||
"SettingsTabGeneralThemeAuto": "Auto",
|
||||
"SettingsTabGeneralThemeDark": "Ciemny",
|
||||
"SettingsTabGeneralThemeLight": "Jasny",
|
||||
"ControllerSettingsConfigureGeneral": "Konfiguruj",
|
||||
@ -561,6 +567,9 @@
|
||||
"AddGameDirBoxTooltip": "Wprowadź katalog gier aby dodać go do listy",
|
||||
"AddGameDirTooltip": "Dodaj katalog gier do listy",
|
||||
"RemoveGameDirTooltip": "Usuń wybrany katalog gier",
|
||||
"AddAutoloadDirBoxTooltip": "Enter an autoload directory to add to the list",
|
||||
"AddAutoloadDirTooltip": "Add an autoload directory to the list",
|
||||
"RemoveAutoloadDirTooltip": "Remove selected autoload directory",
|
||||
"CustomThemeCheckTooltip": "Użyj niestandardowego motywu Avalonia dla GUI, aby zmienić wygląd menu emulatora",
|
||||
"CustomThemePathTooltip": "Ścieżka do niestandardowego motywu GUI",
|
||||
"CustomThemeBrowseTooltip": "Wyszukaj niestandardowy motyw GUI",
|
||||
@ -606,6 +615,8 @@
|
||||
"DebugLogTooltip": "Wyświetla komunikaty dziennika debugowania w konsoli.\n\nUżywaj tego tylko na wyraźne polecenie członka załogi, ponieważ utrudni to odczytanie dzienników i pogorszy wydajność emulatora.",
|
||||
"LoadApplicationFileTooltip": "Otwórz eksplorator plików, aby wybrać plik kompatybilny z Switch do wczytania",
|
||||
"LoadApplicationFolderTooltip": "Otwórz eksplorator plików, aby wybrać zgodną z Switch, rozpakowaną aplikację do załadowania",
|
||||
"LoadDlcFromFolderTooltip": "Open a file explorer to choose one or more folders to bulk load DLC from",
|
||||
"LoadTitleUpdatesFromFolderTooltip": "Open a file explorer to choose one or more folders to bulk load title updates from",
|
||||
"OpenRyujinxFolderTooltip": "Otwórz folder systemu plików Ryujinx",
|
||||
"OpenRyujinxLogsTooltip": "Otwiera folder, w którym zapisywane są logi",
|
||||
"ExitTooltip": "Wyjdź z Ryujinx",
|
||||
@ -657,6 +668,8 @@
|
||||
"OpenSetupGuideMessage": "Otwórz Podręcznik Konfiguracji",
|
||||
"NoUpdate": "Brak Aktualizacji",
|
||||
"TitleUpdateVersionLabel": "Wersja {0} - {1}",
|
||||
"TitleBundledUpdateVersionLabel": "Bundled: Version {0}",
|
||||
"TitleBundledDlcLabel": "Bundled:",
|
||||
"RyujinxInfo": "Ryujinx - Info",
|
||||
"RyujinxConfirm": "Ryujinx - Potwierdzenie",
|
||||
"FileDialogAllTypes": "Wszystkie typy",
|
||||
@ -714,9 +727,17 @@
|
||||
"DlcWindowTitle": "Menedżer Zawartości do Pobrania",
|
||||
"ModWindowTitle": "Zarządzaj modami dla {0} ({1})",
|
||||
"UpdateWindowTitle": "Menedżer Aktualizacji Tytułu",
|
||||
"UpdateWindowUpdateAddedMessage": "{0} new update(s) added",
|
||||
"UpdateWindowBundledContentNotice": "Bundled updates cannot be removed, only disabled.",
|
||||
"CheatWindowHeading": "Kody Dostępne dla {0} [{1}]",
|
||||
"BuildId": "Identyfikator wersji:",
|
||||
"DlcWindowBundledContentNotice": "Bundled DLC cannot be removed, only disabled.",
|
||||
"DlcWindowHeading": "{0} Zawartości do Pobrania dostępna dla {1} ({2})",
|
||||
"DlcWindowDlcAddedMessage": "{0} new downloadable content(s) added",
|
||||
"AutoloadDlcAddedMessage": "{0} new downloadable content(s) added",
|
||||
"AutoloadDlcRemovedMessage": "{0} missing downloadable content(s) removed",
|
||||
"AutoloadUpdateAddedMessage": "{0} new update(s) added",
|
||||
"AutoloadUpdateRemovedMessage": "{0} missing update(s) removed",
|
||||
"ModWindowHeading": "{0} Mod(y/ów)",
|
||||
"UserProfilesEditProfile": "Edytuj Zaznaczone",
|
||||
"Cancel": "Anuluj",
|
||||
@ -767,6 +788,7 @@
|
||||
"GraphicsScalingFilterBilinear": "Dwuliniowe",
|
||||
"GraphicsScalingFilterNearest": "Najbliższe",
|
||||
"GraphicsScalingFilterFsr": "FSR",
|
||||
"GraphicsScalingFilterArea": "Area",
|
||||
"GraphicsScalingFilterLevelLabel": "Poziom",
|
||||
"GraphicsScalingFilterLevelTooltip": "Ustaw poziom ostrzeżenia FSR 1.0. Wyższy jest ostrzejszy.",
|
||||
"SmaaLow": "SMAA Niskie",
|
||||
|
@ -10,7 +10,10 @@
|
||||
"SettingsTabSystemUseHypervisor": "Использовать Hypervisor",
|
||||
"MenuBarFile": "_Файл",
|
||||
"MenuBarFileOpenFromFile": "_Добавить приложение из файла",
|
||||
"MenuBarFileOpenFromFileError": "No applications found in selected file.",
|
||||
"MenuBarFileOpenUnpacked": "Добавить _распакованную игру",
|
||||
"MenuBarFileLoadDlcFromFolder": "Load DLC From Folder",
|
||||
"MenuBarFileLoadTitleUpdatesFromFolder": "Load Title Updates From Folder",
|
||||
"MenuBarFileOpenEmuFolder": "Открыть папку Ryujinx",
|
||||
"MenuBarFileOpenLogsFolder": "Открыть папку с логами",
|
||||
"MenuBarFileExit": "_Выход",
|
||||
@ -103,6 +106,8 @@
|
||||
"SettingsTabGeneralHideCursorOnIdle": "В простое",
|
||||
"SettingsTabGeneralHideCursorAlways": "Всегда",
|
||||
"SettingsTabGeneralGameDirectories": "Папки с играми",
|
||||
"SettingsTabGeneralAutoloadDirectories": "Autoload DLC/Updates Directories",
|
||||
"SettingsTabGeneralAutoloadNote": "DLC and Updates which refer to missing files will be unloaded automatically",
|
||||
"SettingsTabGeneralAdd": "Добавить",
|
||||
"SettingsTabGeneralRemove": "Удалить",
|
||||
"SettingsTabSystem": "Система",
|
||||
@ -411,6 +416,7 @@
|
||||
"GameListContextMenuToggleFavorite": "Добавить в избранное",
|
||||
"GameListContextMenuToggleFavoriteToolTip": "Добавляет игру в избранное и помечает звездочкой",
|
||||
"SettingsTabGeneralTheme": "Тема:",
|
||||
"SettingsTabGeneralThemeAuto": "Auto",
|
||||
"SettingsTabGeneralThemeDark": "Темная",
|
||||
"SettingsTabGeneralThemeLight": "Светлая",
|
||||
"ControllerSettingsConfigureGeneral": "Настройка",
|
||||
@ -561,6 +567,9 @@
|
||||
"AddGameDirBoxTooltip": "Введите путь к папке с играми для добавления ее в список выше",
|
||||
"AddGameDirTooltip": "Добавить папку с играми в список",
|
||||
"RemoveGameDirTooltip": "Удалить выбранную папку игры",
|
||||
"AddAutoloadDirBoxTooltip": "Enter an autoload directory to add to the list",
|
||||
"AddAutoloadDirTooltip": "Add an autoload directory to the list",
|
||||
"RemoveAutoloadDirTooltip": "Remove selected autoload directory",
|
||||
"CustomThemeCheckTooltip": "Включить или отключить пользовательские темы",
|
||||
"CustomThemePathTooltip": "Путь к пользовательской теме для интерфейса",
|
||||
"CustomThemeBrowseTooltip": "Просмотр пользовательской темы интерфейса",
|
||||
@ -606,6 +615,8 @@
|
||||
"DebugLogTooltip": "Выводит журнал сообщений отладки в консоли.\n\nИспользуйте только в случае просьбы разработчика, так как включение этой функции затруднит чтение журналов и ухудшит работу эмулятора.",
|
||||
"LoadApplicationFileTooltip": "Открывает файловый менеджер для выбора файла, совместимого с Nintendo Switch.",
|
||||
"LoadApplicationFolderTooltip": "Открывает файловый менеджер для выбора распакованного приложения, совместимого с Nintendo Switch.",
|
||||
"LoadDlcFromFolderTooltip": "Open a file explorer to choose one or more folders to bulk load DLC from",
|
||||
"LoadTitleUpdatesFromFolderTooltip": "Open a file explorer to choose one or more folders to bulk load title updates from",
|
||||
"OpenRyujinxFolderTooltip": "Открывает папку с файлами Ryujinx. ",
|
||||
"OpenRyujinxLogsTooltip": "Открывает папку в которую записываются логи",
|
||||
"ExitTooltip": "Выйти из Ryujinx",
|
||||
@ -657,6 +668,8 @@
|
||||
"OpenSetupGuideMessage": "Открыть руководство по установке",
|
||||
"NoUpdate": "Без обновлений",
|
||||
"TitleUpdateVersionLabel": "Version {0} - {1}",
|
||||
"TitleBundledUpdateVersionLabel": "Bundled: Version {0}",
|
||||
"TitleBundledDlcLabel": "Bundled:",
|
||||
"RyujinxInfo": "Ryujinx - Информация",
|
||||
"RyujinxConfirm": "Ryujinx - Подтверждение",
|
||||
"FileDialogAllTypes": "Все типы",
|
||||
@ -714,9 +727,17 @@
|
||||
"DlcWindowTitle": "Управление DLC для {0} ({1})",
|
||||
"ModWindowTitle": "Управление модами для {0} ({1})",
|
||||
"UpdateWindowTitle": "Менеджер обновлений игр",
|
||||
"UpdateWindowUpdateAddedMessage": "{0} new update(s) added",
|
||||
"UpdateWindowBundledContentNotice": "Bundled updates cannot be removed, only disabled.",
|
||||
"CheatWindowHeading": "Доступные читы для {0} [{1}]",
|
||||
"BuildId": "ID версии:",
|
||||
"DlcWindowBundledContentNotice": "Bundled DLC cannot be removed, only disabled.",
|
||||
"DlcWindowHeading": "{0} DLC",
|
||||
"DlcWindowDlcAddedMessage": "{0} new downloadable content(s) added",
|
||||
"AutoloadDlcAddedMessage": "{0} new downloadable content(s) added",
|
||||
"AutoloadDlcRemovedMessage": "{0} missing downloadable content(s) removed",
|
||||
"AutoloadUpdateAddedMessage": "{0} new update(s) added",
|
||||
"AutoloadUpdateRemovedMessage": "{0} missing update(s) removed",
|
||||
"ModWindowHeading": "Моды для {0} ",
|
||||
"UserProfilesEditProfile": "Изменить выбранные",
|
||||
"Cancel": "Отмена",
|
||||
@ -767,6 +788,7 @@
|
||||
"GraphicsScalingFilterBilinear": "Билинейная",
|
||||
"GraphicsScalingFilterNearest": "Ступенчатая",
|
||||
"GraphicsScalingFilterFsr": "FSR",
|
||||
"GraphicsScalingFilterArea": "Area",
|
||||
"GraphicsScalingFilterLevelLabel": "Уровень",
|
||||
"GraphicsScalingFilterLevelTooltip": "Выбор режима работы FSR 1.0. Выше - четче.",
|
||||
"SmaaLow": "SMAA Низкое",
|
||||
|
@ -107,6 +107,7 @@
|
||||
"SettingsTabGeneralHideCursorAlways": "ตลอดเวลา",
|
||||
"SettingsTabGeneralGameDirectories": "ไดเรกทอรี่ของเกม",
|
||||
"SettingsTabGeneralAutoloadDirectories": "โหลดไดเรกทอรี DLC/ไฟล์อัปเดต อัตโนมัติ",
|
||||
"SettingsTabGeneralAutoloadNote": "DLC and Updates which refer to missing files will be unloaded automatically",
|
||||
"SettingsTabGeneralAdd": "เพิ่ม",
|
||||
"SettingsTabGeneralRemove": "เอาออก",
|
||||
"SettingsTabSystem": "ระบบ",
|
||||
@ -734,8 +735,9 @@
|
||||
"DlcWindowHeading": "{0} DLC ที่สามารถดาวน์โหลดได้",
|
||||
"DlcWindowDlcAddedMessage": "{0} DLC ใหม่ที่เพิ่มเข้ามา",
|
||||
"AutoloadDlcAddedMessage": "{0} ใหม่ที่เพิ่มเข้ามา",
|
||||
"AutoloadDlcRemovedMessage": "{0} missing downloadable content(s) removed",
|
||||
"AutoloadUpdateAddedMessage": "{0} อัพเดตใหม่ที่เพิ่มเข้ามา",
|
||||
"AutoloadDlcAndUpdateAddedMessage": "{0} DLC ใหม่ที่เพิ่มเข้ามาและ {1} อัพเดตใหม่ที่เพิ่มเข้ามา",
|
||||
"AutoloadUpdateRemovedMessage": "{0} missing update(s) removed",
|
||||
"ModWindowHeading": "{0} ม็อด",
|
||||
"UserProfilesEditProfile": "แก้ไขที่เลือกแล้ว",
|
||||
"Cancel": "ยกเลิก",
|
||||
|
@ -10,7 +10,10 @@
|
||||
"SettingsTabSystemUseHypervisor": "Hypervisor Kullan",
|
||||
"MenuBarFile": "_Dosya",
|
||||
"MenuBarFileOpenFromFile": "_Dosyadan Uygulama Aç",
|
||||
"MenuBarFileOpenFromFileError": "No applications found in selected file.",
|
||||
"MenuBarFileOpenUnpacked": "_Sıkıştırılmamış Oyun Aç",
|
||||
"MenuBarFileLoadDlcFromFolder": "Load DLC From Folder",
|
||||
"MenuBarFileLoadTitleUpdatesFromFolder": "Load Title Updates From Folder",
|
||||
"MenuBarFileOpenEmuFolder": "Ryujinx Klasörünü aç",
|
||||
"MenuBarFileOpenLogsFolder": "Logs Klasörünü aç",
|
||||
"MenuBarFileExit": "_Çıkış",
|
||||
@ -103,6 +106,8 @@
|
||||
"SettingsTabGeneralHideCursorOnIdle": "Hareketsiz Durumda",
|
||||
"SettingsTabGeneralHideCursorAlways": "Her Zaman",
|
||||
"SettingsTabGeneralGameDirectories": "Oyun Dizinleri",
|
||||
"SettingsTabGeneralAutoloadDirectories": "Autoload DLC/Updates Directories",
|
||||
"SettingsTabGeneralAutoloadNote": "DLC and Updates which refer to missing files will be unloaded automatically",
|
||||
"SettingsTabGeneralAdd": "Ekle",
|
||||
"SettingsTabGeneralRemove": "Kaldır",
|
||||
"SettingsTabSystem": "Sistem",
|
||||
@ -411,6 +416,7 @@
|
||||
"GameListContextMenuToggleFavorite": "Favori Ayarla",
|
||||
"GameListContextMenuToggleFavoriteToolTip": "Oyunu Favorilere Ekle/Çıkar",
|
||||
"SettingsTabGeneralTheme": "Tema:",
|
||||
"SettingsTabGeneralThemeAuto": "Auto",
|
||||
"SettingsTabGeneralThemeDark": "Karanlık",
|
||||
"SettingsTabGeneralThemeLight": "Aydınlık",
|
||||
"ControllerSettingsConfigureGeneral": "Ayarla",
|
||||
@ -561,6 +567,9 @@
|
||||
"AddGameDirBoxTooltip": "Listeye eklemek için oyun dizini seçin",
|
||||
"AddGameDirTooltip": "Listeye oyun dizini ekle",
|
||||
"RemoveGameDirTooltip": "Seçili oyun dizinini kaldır",
|
||||
"AddAutoloadDirBoxTooltip": "Enter an autoload directory to add to the list",
|
||||
"AddAutoloadDirTooltip": "Add an autoload directory to the list",
|
||||
"RemoveAutoloadDirTooltip": "Remove selected autoload directory",
|
||||
"CustomThemeCheckTooltip": "Emülatör pencerelerinin görünümünü değiştirmek için özel bir Avalonia teması kullan",
|
||||
"CustomThemePathTooltip": "Özel arayüz temasının yolu",
|
||||
"CustomThemeBrowseTooltip": "Özel arayüz teması için göz at",
|
||||
@ -606,6 +615,8 @@
|
||||
"DebugLogTooltip": "Debug log mesajlarını konsola yazdırır.\n\nBu seçeneği yalnızca geliştirici üyemiz belirtirse aktifleştirin, çünkü bu seçenek log dosyasını okumayı zorlaştırır ve emülatörün performansını düşürür.",
|
||||
"LoadApplicationFileTooltip": "Switch ile uyumlu bir dosya yüklemek için dosya tarayıcısını açar",
|
||||
"LoadApplicationFolderTooltip": "Switch ile uyumlu ayrıştırılmamış bir uygulama yüklemek için dosya tarayıcısını açar",
|
||||
"LoadDlcFromFolderTooltip": "Open a file explorer to choose one or more folders to bulk load DLC from",
|
||||
"LoadTitleUpdatesFromFolderTooltip": "Open a file explorer to choose one or more folders to bulk load title updates from",
|
||||
"OpenRyujinxFolderTooltip": "Ryujinx dosya sistem klasörünü açar",
|
||||
"OpenRyujinxLogsTooltip": "Log dosyalarının bulunduğu klasörü açar",
|
||||
"ExitTooltip": "Ryujinx'ten çıkış yapmayı sağlar",
|
||||
@ -657,6 +668,8 @@
|
||||
"OpenSetupGuideMessage": "Kurulum Kılavuzunu Aç",
|
||||
"NoUpdate": "Güncelleme Yok",
|
||||
"TitleUpdateVersionLabel": "Sürüm {0} - {1}",
|
||||
"TitleBundledUpdateVersionLabel": "Bundled: Version {0}",
|
||||
"TitleBundledDlcLabel": "Bundled:",
|
||||
"RyujinxInfo": "Ryujinx - Bilgi",
|
||||
"RyujinxConfirm": "Ryujinx - Doğrulama",
|
||||
"FileDialogAllTypes": "Tüm türler",
|
||||
@ -714,9 +727,17 @@
|
||||
"DlcWindowTitle": "Oyun DLC'lerini Yönet",
|
||||
"ModWindowTitle": "Manage Mods for {0} ({1})",
|
||||
"UpdateWindowTitle": "Oyun Güncellemelerini Yönet",
|
||||
"UpdateWindowUpdateAddedMessage": "{0} new update(s) added",
|
||||
"UpdateWindowBundledContentNotice": "Bundled updates cannot be removed, only disabled.",
|
||||
"CheatWindowHeading": "{0} için Hile mevcut [{1}]",
|
||||
"BuildId": "BuildId:",
|
||||
"DlcWindowHeading": "{0} için DLC mevcut [{1}]",
|
||||
"DlcWindowBundledContentNotice": "Bundled DLC cannot be removed, only disabled.",
|
||||
"DlcWindowHeading": "{0} Downloadable Content(s) available for {1} ({2})",
|
||||
"DlcWindowDlcAddedMessage": "{0} new downloadable content(s) added",
|
||||
"AutoloadDlcAddedMessage": "{0} new downloadable content(s) added",
|
||||
"AutoloadDlcRemovedMessage": "{0} missing downloadable content(s) removed",
|
||||
"AutoloadUpdateAddedMessage": "{0} new update(s) added",
|
||||
"AutoloadUpdateRemovedMessage": "{0} missing update(s) removed",
|
||||
"ModWindowHeading": "{0} Mod(lar)",
|
||||
"UserProfilesEditProfile": "Seçiliyi Düzenle",
|
||||
"Cancel": "İptal",
|
||||
@ -767,6 +788,7 @@
|
||||
"GraphicsScalingFilterBilinear": "Bilinear",
|
||||
"GraphicsScalingFilterNearest": "Nearest",
|
||||
"GraphicsScalingFilterFsr": "FSR",
|
||||
"GraphicsScalingFilterArea": "Area",
|
||||
"GraphicsScalingFilterLevelLabel": "Seviye",
|
||||
"GraphicsScalingFilterLevelTooltip": "Set FSR 1.0 sharpening level. Higher is sharper.",
|
||||
"SmaaLow": "Düşük SMAA",
|
||||
|
@ -10,7 +10,10 @@
|
||||
"SettingsTabSystemUseHypervisor": "Використовувати гіпервізор",
|
||||
"MenuBarFile": "_Файл",
|
||||
"MenuBarFileOpenFromFile": "_Завантажити програму з файлу",
|
||||
"MenuBarFileOpenFromFileError": "No applications found in selected file.",
|
||||
"MenuBarFileOpenUnpacked": "Завантажити _розпаковану гру",
|
||||
"MenuBarFileLoadDlcFromFolder": "Load DLC From Folder",
|
||||
"MenuBarFileLoadTitleUpdatesFromFolder": "Load Title Updates From Folder",
|
||||
"MenuBarFileOpenEmuFolder": "Відкрити теку Ryujinx",
|
||||
"MenuBarFileOpenLogsFolder": "Відкрити теку журналів змін",
|
||||
"MenuBarFileExit": "_Вихід",
|
||||
@ -103,6 +106,8 @@
|
||||
"SettingsTabGeneralHideCursorOnIdle": "Сховати у режимі очікування",
|
||||
"SettingsTabGeneralHideCursorAlways": "Завжди",
|
||||
"SettingsTabGeneralGameDirectories": "Тека ігор",
|
||||
"SettingsTabGeneralAutoloadDirectories": "Autoload DLC/Updates Directories",
|
||||
"SettingsTabGeneralAutoloadNote": "DLC and Updates which refer to missing files will be unloaded automatically",
|
||||
"SettingsTabGeneralAdd": "Додати",
|
||||
"SettingsTabGeneralRemove": "Видалити",
|
||||
"SettingsTabSystem": "Система",
|
||||
@ -411,6 +416,7 @@
|
||||
"GameListContextMenuToggleFavorite": "Перемкнути вибране",
|
||||
"GameListContextMenuToggleFavoriteToolTip": "Перемкнути улюблений статус гри",
|
||||
"SettingsTabGeneralTheme": "Тема:",
|
||||
"SettingsTabGeneralThemeAuto": "Auto",
|
||||
"SettingsTabGeneralThemeDark": "Темна",
|
||||
"SettingsTabGeneralThemeLight": "Світла",
|
||||
"ControllerSettingsConfigureGeneral": "Налаштування",
|
||||
@ -561,6 +567,9 @@
|
||||
"AddGameDirBoxTooltip": "Введіть каталог ігор, щоб додати до списку",
|
||||
"AddGameDirTooltip": "Додати каталог гри до списку",
|
||||
"RemoveGameDirTooltip": "Видалити вибраний каталог гри",
|
||||
"AddAutoloadDirBoxTooltip": "Enter an autoload directory to add to the list",
|
||||
"AddAutoloadDirTooltip": "Add an autoload directory to the list",
|
||||
"RemoveAutoloadDirTooltip": "Remove selected autoload directory",
|
||||
"CustomThemeCheckTooltip": "Використовуйте користувацьку тему Avalonia для графічного інтерфейсу, щоб змінити вигляд меню емулятора",
|
||||
"CustomThemePathTooltip": "Шлях до користувацької теми графічного інтерфейсу",
|
||||
"CustomThemeBrowseTooltip": "Огляд користувацької теми графічного інтерфейсу",
|
||||
@ -606,6 +615,8 @@
|
||||
"DebugLogTooltip": "Друкує повідомлення журналу налагодження на консолі.\n\nВикористовуйте це лише за спеціальною вказівкою співробітника, оскільки це ускладнить читання журналів і погіршить роботу емулятора.",
|
||||
"LoadApplicationFileTooltip": "Відкриває файловий провідник, щоб вибрати для завантаження сумісний файл Switch",
|
||||
"LoadApplicationFolderTooltip": "Відкриває файловий провідник, щоб вибрати сумісну з комутатором розпаковану програму для завантаження",
|
||||
"LoadDlcFromFolderTooltip": "Open a file explorer to choose one or more folders to bulk load DLC from",
|
||||
"LoadTitleUpdatesFromFolderTooltip": "Open a file explorer to choose one or more folders to bulk load title updates from",
|
||||
"OpenRyujinxFolderTooltip": "Відкриває папку файлової системи Ryujinx",
|
||||
"OpenRyujinxLogsTooltip": "Відкриває папку, куди записуються журнали",
|
||||
"ExitTooltip": "Виходить з Ryujinx",
|
||||
@ -657,6 +668,8 @@
|
||||
"OpenSetupGuideMessage": "Відкрити посібник із налаштування",
|
||||
"NoUpdate": "Немає оновлень",
|
||||
"TitleUpdateVersionLabel": "Версія {0} - {1}",
|
||||
"TitleBundledUpdateVersionLabel": "Bundled: Version {0}",
|
||||
"TitleBundledDlcLabel": "Bundled:",
|
||||
"RyujinxInfo": "Ryujin x - Інформація",
|
||||
"RyujinxConfirm": "Ryujinx - Підтвердження",
|
||||
"FileDialogAllTypes": "Всі типи",
|
||||
@ -714,9 +727,17 @@
|
||||
"DlcWindowTitle": "Менеджер вмісту для завантаження",
|
||||
"ModWindowTitle": "Керувати модами для {0} ({1})",
|
||||
"UpdateWindowTitle": "Менеджер оновлення назв",
|
||||
"UpdateWindowUpdateAddedMessage": "{0} new update(s) added",
|
||||
"UpdateWindowBundledContentNotice": "Bundled updates cannot be removed, only disabled.",
|
||||
"CheatWindowHeading": "Коди доступні для {0} [{1}]",
|
||||
"BuildId": "ID збірки:",
|
||||
"DlcWindowBundledContentNotice": "Bundled DLC cannot be removed, only disabled.",
|
||||
"DlcWindowHeading": "Вміст для завантаження, доступний для {1} ({2}): {0}",
|
||||
"DlcWindowDlcAddedMessage": "{0} new downloadable content(s) added",
|
||||
"AutoloadDlcAddedMessage": "{0} new downloadable content(s) added",
|
||||
"AutoloadDlcRemovedMessage": "{0} missing downloadable content(s) removed",
|
||||
"AutoloadUpdateAddedMessage": "{0} new update(s) added",
|
||||
"AutoloadUpdateRemovedMessage": "{0} missing update(s) removed",
|
||||
"ModWindowHeading": "{0} мод(ів)",
|
||||
"UserProfilesEditProfile": "Редагувати вибране",
|
||||
"Cancel": "Скасувати",
|
||||
@ -767,6 +788,7 @@
|
||||
"GraphicsScalingFilterBilinear": "Білінійний",
|
||||
"GraphicsScalingFilterNearest": "Найближчий",
|
||||
"GraphicsScalingFilterFsr": "FSR",
|
||||
"GraphicsScalingFilterArea": "Area",
|
||||
"GraphicsScalingFilterLevelLabel": "Рівень",
|
||||
"GraphicsScalingFilterLevelTooltip": "Встановити рівень різкості в FSR 1.0. Чим вище - тим різкіше.",
|
||||
"SmaaLow": "SMAA Низький",
|
||||
|
@ -107,6 +107,7 @@
|
||||
"SettingsTabGeneralHideCursorAlways": "始终隐藏",
|
||||
"SettingsTabGeneralGameDirectories": "游戏目录",
|
||||
"SettingsTabGeneralAutoloadDirectories": "自动加载DLC/游戏更新目录",
|
||||
"SettingsTabGeneralAutoloadNote": "DLC and Updates which refer to missing files will be unloaded automatically",
|
||||
"SettingsTabGeneralAdd": "添加",
|
||||
"SettingsTabGeneralRemove": "删除",
|
||||
"SettingsTabSystem": "系统",
|
||||
@ -734,8 +735,9 @@
|
||||
"DlcWindowHeading": "{0} 个 DLC",
|
||||
"DlcWindowDlcAddedMessage": "{0} 个DLC被添加",
|
||||
"AutoloadDlcAddedMessage": "{0} 个DLC被添加",
|
||||
"AutoloadDlcRemovedMessage": "{0} missing downloadable content(s) removed",
|
||||
"AutoloadUpdateAddedMessage": "{0} 个游戏更新被添加",
|
||||
"AutoloadDlcAndUpdateAddedMessage": "{0} 个DLC和{1} 个游戏更新被添加",
|
||||
"AutoloadUpdateRemovedMessage": "{0} missing update(s) removed",
|
||||
"ModWindowHeading": "{0} Mod(s)",
|
||||
"UserProfilesEditProfile": "编辑所选",
|
||||
"Cancel": "取消",
|
||||
|
@ -10,7 +10,10 @@
|
||||
"SettingsTabSystemUseHypervisor": "使用 Hypervisor",
|
||||
"MenuBarFile": "檔案(_F)",
|
||||
"MenuBarFileOpenFromFile": "從檔案載入應用程式(_L)",
|
||||
"MenuBarFileOpenFromFileError": "No applications found in selected file.",
|
||||
"MenuBarFileOpenUnpacked": "載入未封裝的遊戲(_U)",
|
||||
"MenuBarFileLoadDlcFromFolder": "Load DLC From Folder",
|
||||
"MenuBarFileLoadTitleUpdatesFromFolder": "Load Title Updates From Folder",
|
||||
"MenuBarFileOpenEmuFolder": "開啟 Ryujinx 資料夾",
|
||||
"MenuBarFileOpenLogsFolder": "開啟日誌資料夾",
|
||||
"MenuBarFileExit": "結束(_E)",
|
||||
@ -103,6 +106,8 @@
|
||||
"SettingsTabGeneralHideCursorOnIdle": "閒置時",
|
||||
"SettingsTabGeneralHideCursorAlways": "總是",
|
||||
"SettingsTabGeneralGameDirectories": "遊戲資料夾",
|
||||
"SettingsTabGeneralAutoloadDirectories": "Autoload DLC/Updates Directories",
|
||||
"SettingsTabGeneralAutoloadNote": "DLC and Updates which refer to missing files will be unloaded automatically",
|
||||
"SettingsTabGeneralAdd": "新增",
|
||||
"SettingsTabGeneralRemove": "刪除",
|
||||
"SettingsTabSystem": "系統",
|
||||
@ -411,6 +416,7 @@
|
||||
"GameListContextMenuToggleFavorite": "加入/移除為我的最愛",
|
||||
"GameListContextMenuToggleFavoriteToolTip": "切換遊戲的我的最愛狀態",
|
||||
"SettingsTabGeneralTheme": "佈景主題:",
|
||||
"SettingsTabGeneralThemeAuto": "Auto",
|
||||
"SettingsTabGeneralThemeDark": "深色",
|
||||
"SettingsTabGeneralThemeLight": "淺色",
|
||||
"ControllerSettingsConfigureGeneral": "配置",
|
||||
@ -561,6 +567,9 @@
|
||||
"AddGameDirBoxTooltip": "輸入要新增到清單中的遊戲資料夾",
|
||||
"AddGameDirTooltip": "新增遊戲資料夾到清單中",
|
||||
"RemoveGameDirTooltip": "移除選取的遊戲資料夾",
|
||||
"AddAutoloadDirBoxTooltip": "Enter an autoload directory to add to the list",
|
||||
"AddAutoloadDirTooltip": "Add an autoload directory to the list",
|
||||
"RemoveAutoloadDirTooltip": "Remove selected autoload directory",
|
||||
"CustomThemeCheckTooltip": "為圖形使用者介面使用自訂 Avalonia 佈景主題,變更模擬器功能表的外觀",
|
||||
"CustomThemePathTooltip": "自訂 GUI 佈景主題的路徑",
|
||||
"CustomThemeBrowseTooltip": "瀏覽自訂 GUI 佈景主題",
|
||||
@ -606,6 +615,8 @@
|
||||
"DebugLogTooltip": "在控制台中輸出偵錯日誌訊息。\n\n只有在人員特別指示的情況下才能使用,因為這會導致日誌難以閱讀,並降低模擬器效能。",
|
||||
"LoadApplicationFileTooltip": "開啟檔案總管,選擇與 Switch 相容的檔案來載入",
|
||||
"LoadApplicationFolderTooltip": "開啟檔案總管,選擇與 Switch 相容且未封裝的應用程式來載入",
|
||||
"LoadDlcFromFolderTooltip": "Open a file explorer to choose one or more folders to bulk load DLC from",
|
||||
"LoadTitleUpdatesFromFolderTooltip": "Open a file explorer to choose one or more folders to bulk load title updates from",
|
||||
"OpenRyujinxFolderTooltip": "開啟 Ryujinx 檔案系統資料夾",
|
||||
"OpenRyujinxLogsTooltip": "開啟日誌被寫入的資料夾",
|
||||
"ExitTooltip": "結束 Ryujinx",
|
||||
@ -657,6 +668,8 @@
|
||||
"OpenSetupGuideMessage": "開啟設定指南",
|
||||
"NoUpdate": "沒有更新",
|
||||
"TitleUpdateVersionLabel": "版本 {0}",
|
||||
"TitleBundledUpdateVersionLabel": "Bundled: Version {0}",
|
||||
"TitleBundledDlcLabel": "Bundled:",
|
||||
"RyujinxInfo": "Ryujinx - 資訊",
|
||||
"RyujinxConfirm": "Ryujinx - 確認",
|
||||
"FileDialogAllTypes": "全部類型",
|
||||
@ -714,9 +727,17 @@
|
||||
"DlcWindowTitle": "管理 {0} 的可下載內容 ({1})",
|
||||
"ModWindowTitle": "管理 {0} 的模組 ({1})",
|
||||
"UpdateWindowTitle": "遊戲更新管理員",
|
||||
"UpdateWindowUpdateAddedMessage": "{0} new update(s) added",
|
||||
"UpdateWindowBundledContentNotice": "Bundled updates cannot be removed, only disabled.",
|
||||
"CheatWindowHeading": "可用於 {0} [{1}] 的密技",
|
||||
"BuildId": "組建識別碼:",
|
||||
"DlcWindowBundledContentNotice": "Bundled DLC cannot be removed, only disabled.",
|
||||
"DlcWindowHeading": "{0} 個可下載內容",
|
||||
"DlcWindowDlcAddedMessage": "{0} new downloadable content(s) added",
|
||||
"AutoloadDlcAddedMessage": "{0} new downloadable content(s) added",
|
||||
"AutoloadDlcRemovedMessage": "{0} missing downloadable content(s) removed",
|
||||
"AutoloadUpdateAddedMessage": "{0} new update(s) added",
|
||||
"AutoloadUpdateRemovedMessage": "{0} missing update(s) removed",
|
||||
"ModWindowHeading": "{0} 模組",
|
||||
"UserProfilesEditProfile": "編輯所選",
|
||||
"Cancel": "取消",
|
||||
@ -767,6 +788,7 @@
|
||||
"GraphicsScalingFilterBilinear": "雙線性 (Bilinear)",
|
||||
"GraphicsScalingFilterNearest": "近鄰性 (Nearest)",
|
||||
"GraphicsScalingFilterFsr": "FSR",
|
||||
"GraphicsScalingFilterArea": "Area",
|
||||
"GraphicsScalingFilterLevelLabel": "日誌等級",
|
||||
"GraphicsScalingFilterLevelTooltip": "設定 FSR 1.0 銳化等級。越高越清晰。",
|
||||
"SmaaLow": "低階 SMAA",
|
||||
|
@ -43,6 +43,10 @@
|
||||
</StackPanel>
|
||||
</Border>
|
||||
</Design.PreviewWith>
|
||||
<Style Selector="DropDownButton">
|
||||
<Setter Property="FontSize"
|
||||
Value="12" />
|
||||
</Style>
|
||||
<Style Selector="Border.small">
|
||||
<Setter Property="Width"
|
||||
Value="100" />
|
||||
@ -231,6 +235,14 @@
|
||||
<Setter Property="MinWidth"
|
||||
Value="80" />
|
||||
</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">
|
||||
<Setter Property="IsVisible"
|
||||
Value="False" />
|
||||
@ -389,7 +401,7 @@
|
||||
<x:Double x:Key="ControlContentThemeFontSize">13</x:Double>
|
||||
<x:Double x:Key="MenuItemHeight">26</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>
|
||||
</Styles.Resources>
|
||||
</Styles>
|
||||
|
@ -2,19 +2,38 @@
|
||||
using Avalonia.Markup.Xaml;
|
||||
using Avalonia.Markup.Xaml.MarkupExtensions;
|
||||
using Avalonia.Markup.Xaml.MarkupExtensions.CompiledBindings;
|
||||
using Gommon;
|
||||
using System;
|
||||
// ReSharper disable VirtualMemberNeverOverridden.Global
|
||||
// ReSharper disable MemberCanBeProtected.Global
|
||||
// ReSharper disable MemberCanBePrivate.Global
|
||||
|
||||
#nullable enable
|
||||
|
||||
namespace Ryujinx.Ava.Common.Markup
|
||||
{
|
||||
internal abstract class BasicMarkupExtension : MarkupExtension
|
||||
internal abstract class BasicMarkupExtension<T> : MarkupExtension
|
||||
{
|
||||
protected abstract ClrPropertyInfo PropertyInfo { get; }
|
||||
|
||||
public override object ProvideValue(IServiceProvider serviceProvider) =>
|
||||
new CompiledBindingExtension(
|
||||
new CompiledBindingPathBuilder()
|
||||
.Property(PropertyInfo, PropertyInfoAccessorFactory.CreateInpcPropertyAccessor)
|
||||
.Build()
|
||||
).ProvideValue(serviceProvider);
|
||||
public virtual string Name => "Item";
|
||||
public virtual Action<object, T?>? Setter => null;
|
||||
|
||||
protected abstract T? GetValue();
|
||||
|
||||
protected virtual void ConfigureBindingExtension(CompiledBindingExtension _) { }
|
||||
|
||||
private ClrPropertyInfo PropertyInfo =>
|
||||
new(Name,
|
||||
_ => GetValue(),
|
||||
Setter as Action<object, object?>,
|
||||
typeof(T));
|
||||
|
||||
public override object ProvideValue(IServiceProvider serviceProvider)
|
||||
=> new CompiledBindingExtension(
|
||||
new CompiledBindingPathBuilder()
|
||||
.Property(PropertyInfo, PropertyInfoAccessorFactory.CreateInpcPropertyAccessor)
|
||||
.Build()
|
||||
)
|
||||
.Apply(ConfigureBindingExtension)
|
||||
.ProvideValue(serviceProvider);
|
||||
}
|
||||
}
|
||||
|
@ -1,51 +1,24 @@
|
||||
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
|
||||
internal class IconExtension(string iconString) : BasicMarkupExtension<Icon>
|
||||
{
|
||||
protected override ClrPropertyInfo PropertyInfo
|
||||
=> new(
|
||||
"Item",
|
||||
_ => new Icon { Value = iconString },
|
||||
null,
|
||||
typeof(Icon)
|
||||
);
|
||||
protected override Icon GetValue() => new() { Value = iconString };
|
||||
}
|
||||
|
||||
internal class SpinningIconExtension(string iconString) : BasicMarkupExtension
|
||||
internal class SpinningIconExtension(string iconString) : BasicMarkupExtension<Icon>
|
||||
{
|
||||
protected override ClrPropertyInfo PropertyInfo
|
||||
=> new(
|
||||
"Item",
|
||||
_ => new Icon { Value = iconString, Animation = IconAnimation.Spin },
|
||||
null,
|
||||
typeof(Icon)
|
||||
);
|
||||
protected override Icon GetValue() => new() { Value = iconString, Animation = IconAnimation.Spin };
|
||||
}
|
||||
|
||||
internal class LocaleExtension(LocaleKeys key) : MarkupExtension
|
||||
internal class LocaleExtension(LocaleKeys key) : BasicMarkupExtension<string>
|
||||
{
|
||||
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);
|
||||
protected override string GetValue() => LocaleManager.Instance[key];
|
||||
|
||||
protected override void ConfigureBindingExtension(CompiledBindingExtension bindingExtension)
|
||||
=> bindingExtension.Source = LocaleManager.Instance;
|
||||
}
|
||||
}
|
||||
|
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));
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
@ -23,21 +23,15 @@ namespace Ryujinx.Ava.Input
|
||||
public bool IsConnected => true;
|
||||
public GamepadFeaturesFlag Features => GamepadFeaturesFlag.None;
|
||||
|
||||
private class ButtonMappingEntry
|
||||
private class ButtonMappingEntry(GamepadButtonInputId to, Key from)
|
||||
{
|
||||
public readonly Key From;
|
||||
public readonly GamepadButtonInputId To;
|
||||
|
||||
public ButtonMappingEntry(GamepadButtonInputId to, Key from)
|
||||
{
|
||||
To = to;
|
||||
From = from;
|
||||
}
|
||||
public readonly GamepadButtonInputId To = to;
|
||||
public readonly Key From = from;
|
||||
}
|
||||
|
||||
public AvaloniaKeyboard(AvaloniaKeyboardDriver driver, string id, string name)
|
||||
{
|
||||
_buttonsUserMapping = new List<ButtonMappingEntry>();
|
||||
_buttonsUserMapping = [];
|
||||
|
||||
_driver = driver;
|
||||
Id = id;
|
||||
|
@ -14,7 +14,7 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<Target Name="PostBuild" AfterTargets="PostBuildEvent" Condition="$([MSBuild]::IsOSPlatform('OSX'))">
|
||||
<Exec Command="codesign --entitlements '$(ProjectDir)..\..\distribution\macos\entitlements.xml' -f --deep -s $(SigningCertificate) '$(TargetDir)$(TargetName)'" />
|
||||
<Exec Command="codesign --entitlements '$(ProjectDir)..\..\distribution\macos\entitlements.xml' -f -s $(SigningCertificate) '$(TargetDir)$(TargetName)'" />
|
||||
</Target>
|
||||
|
||||
<PropertyGroup Condition="'$(RuntimeIdentifier)' != ''">
|
||||
|
@ -127,11 +127,11 @@ namespace Ryujinx.Ava.UI.Applet
|
||||
try
|
||||
{
|
||||
_parent.ViewModel.AppHost.NpadManager.BlockInputUpdates();
|
||||
var response = await SwkbdAppletDialog.ShowInputDialog(LocaleManager.Instance[LocaleKeys.SoftwareKeyboard], args);
|
||||
(UserResult result, string userInput) = await SwkbdAppletDialog.ShowInputDialog(LocaleManager.Instance[LocaleKeys.SoftwareKeyboard], args);
|
||||
|
||||
if (response.Result == UserResult.Ok)
|
||||
if (result == UserResult.Ok)
|
||||
{
|
||||
inputText = response.Input;
|
||||
inputText = userInput;
|
||||
okPressed = true;
|
||||
}
|
||||
}
|
||||
|
@ -69,6 +69,12 @@
|
||||
Header="{ext:Locale GameListContextMenuOpenSdModsDirectory}"
|
||||
Icon="{ext:Icon mdi-folder-file}"
|
||||
ToolTip.Tip="{ext:Locale GameListContextMenuOpenSdModsDirectoryToolTip}" />
|
||||
<Separator />
|
||||
<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
|
||||
|
@ -2,6 +2,7 @@ using Avalonia.Controls;
|
||||
using Avalonia.Interactivity;
|
||||
using Avalonia.Markup.Xaml;
|
||||
using Avalonia.Platform.Storage;
|
||||
using Avalonia.Threading;
|
||||
using LibHac.Fs;
|
||||
using LibHac.Tools.FsSystem.NcaUtils;
|
||||
using Ryujinx.Ava.Common;
|
||||
@ -17,6 +18,8 @@ using SkiaSharp;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Path = System.IO.Path;
|
||||
|
||||
namespace Ryujinx.Ava.UI.Controls
|
||||
@ -323,5 +326,15 @@ namespace Ryujinx.Ava.UI.Controls
|
||||
if (sender is MenuItem { DataContext: MainWindowViewModel { SelectedApplication: not null } viewModel })
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -22,6 +22,7 @@ using Ryujinx.Ava.UI.Windows;
|
||||
using Ryujinx.Common;
|
||||
using Ryujinx.Common.Configuration;
|
||||
using Ryujinx.Common.Logging;
|
||||
using Ryujinx.Common.Utilities;
|
||||
using Ryujinx.Cpu;
|
||||
using Ryujinx.HLE;
|
||||
using Ryujinx.HLE.FileSystem;
|
||||
@ -84,6 +85,8 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
private bool _isAppletMenuActive;
|
||||
private int _statusBarProgressMaximum;
|
||||
private int _statusBarProgressValue;
|
||||
private string _statusBarProgressStatusText;
|
||||
private bool _statusBarProgressStatusVisible;
|
||||
private bool _isPaused;
|
||||
private bool _showContent = true;
|
||||
private bool _isLoadingIndeterminate = true;
|
||||
@ -391,6 +394,8 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
|
||||
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 CreateShortcutEnabled => !ReleaseInformation.IsFlatHubBuild;
|
||||
@ -505,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
|
||||
{
|
||||
get => _fifoStatusText;
|
||||
@ -1776,6 +1803,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
if (WindowState is not WindowState.Normal)
|
||||
{
|
||||
WindowState = WindowState.Normal;
|
||||
Window.TitleBar.ExtendsContentIntoTitleBar = !ConfigurationState.Instance.ShowTitleBar;
|
||||
|
||||
if (IsGameRunning)
|
||||
{
|
||||
@ -1785,6 +1813,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
else
|
||||
{
|
||||
WindowState = WindowState.FullScreen;
|
||||
Window.TitleBar.ExtendsContentIntoTitleBar = true;
|
||||
|
||||
if (IsGameRunning)
|
||||
{
|
||||
@ -1832,6 +1861,98 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
|
541
src/Ryujinx/UI/ViewModels/XCITrimmerViewModel.cs
Normal file
541
src/Ryujinx/UI/ViewModels/XCITrimmerViewModel.cs
Normal file
@ -0,0 +1,541 @@
|
||||
using Avalonia.Collections;
|
||||
using DynamicData;
|
||||
using Gommon;
|
||||
using Avalonia.Threading;
|
||||
using Ryujinx.Ava.Common;
|
||||
using Ryujinx.Ava.Common.Locale;
|
||||
using Ryujinx.Ava.UI.Helpers;
|
||||
using Ryujinx.Common.Utilities;
|
||||
using Ryujinx.UI.App.Common;
|
||||
using Ryujinx.UI.Common.Models;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using static Ryujinx.Common.Utilities.XCIFileTrimmer;
|
||||
|
||||
namespace Ryujinx.Ava.UI.ViewModels
|
||||
{
|
||||
public class XCITrimmerViewModel : BaseModel
|
||||
{
|
||||
private const long _bytesPerMB = 1024 * 1024;
|
||||
private enum ProcessingMode
|
||||
{
|
||||
Trimming,
|
||||
Untrimming
|
||||
}
|
||||
|
||||
public enum SortField
|
||||
{
|
||||
Name,
|
||||
Saved
|
||||
}
|
||||
|
||||
private const string _FileExtXCI = "XCI";
|
||||
|
||||
private readonly Ryujinx.Common.Logging.XCIFileTrimmerLog _logger;
|
||||
private readonly ApplicationLibrary _applicationLibrary;
|
||||
private Optional<XCITrimmerFileModel> _processingApplication = null;
|
||||
private AvaloniaList<XCITrimmerFileModel> _allXCIFiles = new();
|
||||
private AvaloniaList<XCITrimmerFileModel> _selectedXCIFiles = new();
|
||||
private AvaloniaList<XCITrimmerFileModel> _displayedXCIFiles = new();
|
||||
private MainWindowViewModel _mainWindowViewModel;
|
||||
private CancellationTokenSource _cancellationTokenSource;
|
||||
private string _search;
|
||||
private ProcessingMode _processingMode;
|
||||
private SortField _sortField = SortField.Name;
|
||||
private bool _sortAscending = true;
|
||||
|
||||
public XCITrimmerViewModel(MainWindowViewModel mainWindowViewModel)
|
||||
{
|
||||
_logger = new XCIFileTrimmerWindowLog(this);
|
||||
_mainWindowViewModel = mainWindowViewModel;
|
||||
_applicationLibrary = _mainWindowViewModel.ApplicationLibrary;
|
||||
LoadXCIApplications();
|
||||
}
|
||||
|
||||
private void LoadXCIApplications()
|
||||
{
|
||||
var apps = _applicationLibrary.Applications.Items
|
||||
.Where(app => app.FileExtension == _FileExtXCI);
|
||||
|
||||
foreach (var xciApp in apps)
|
||||
AddOrUpdateXCITrimmerFile(CreateXCITrimmerFile(xciApp.Path));
|
||||
|
||||
ApplicationsChanged();
|
||||
}
|
||||
|
||||
private XCITrimmerFileModel CreateXCITrimmerFile(
|
||||
string path,
|
||||
OperationOutcome operationOutcome = OperationOutcome.Undetermined)
|
||||
{
|
||||
var xciApp = _applicationLibrary.Applications.Items.First(app => app.FileExtension == _FileExtXCI && app.Path == path);
|
||||
return XCITrimmerFileModel.FromApplicationData(xciApp, _logger) with { ProcessingOutcome = operationOutcome };
|
||||
}
|
||||
|
||||
private bool AddOrUpdateXCITrimmerFile(XCITrimmerFileModel xci, bool suppressChanged = true, bool autoSelect = true)
|
||||
{
|
||||
bool replaced = _allXCIFiles.ReplaceWith(xci);
|
||||
_displayedXCIFiles.ReplaceWith(xci, Filter(xci));
|
||||
_selectedXCIFiles.ReplaceWith(xci, xci.Trimmable && autoSelect);
|
||||
|
||||
if (!suppressChanged)
|
||||
ApplicationsChanged();
|
||||
|
||||
return replaced;
|
||||
}
|
||||
|
||||
private void FilteringChanged()
|
||||
{
|
||||
OnPropertyChanged(nameof(Search));
|
||||
SortAndFilter();
|
||||
}
|
||||
|
||||
private void SortingChanged()
|
||||
{
|
||||
OnPropertyChanged(nameof(IsSortedByName));
|
||||
OnPropertyChanged(nameof(IsSortedBySaved));
|
||||
OnPropertyChanged(nameof(SortingAscending));
|
||||
OnPropertyChanged(nameof(SortingField));
|
||||
OnPropertyChanged(nameof(SortingFieldName));
|
||||
SortAndFilter();
|
||||
}
|
||||
|
||||
private void DisplayedChanged()
|
||||
{
|
||||
OnPropertyChanged(nameof(Status));
|
||||
OnPropertyChanged(nameof(DisplayedXCIFiles));
|
||||
OnPropertyChanged(nameof(SelectedDisplayedXCIFiles));
|
||||
}
|
||||
|
||||
private void ApplicationsChanged()
|
||||
{
|
||||
OnPropertyChanged(nameof(AllXCIFiles));
|
||||
OnPropertyChanged(nameof(Status));
|
||||
OnPropertyChanged(nameof(PotentialSavings));
|
||||
OnPropertyChanged(nameof(ActualSavings));
|
||||
OnPropertyChanged(nameof(CanTrim));
|
||||
OnPropertyChanged(nameof(CanUntrim));
|
||||
DisplayedChanged();
|
||||
SortAndFilter();
|
||||
}
|
||||
|
||||
private void SelectionChanged(bool displayedChanged = true)
|
||||
{
|
||||
OnPropertyChanged(nameof(Status));
|
||||
OnPropertyChanged(nameof(CanTrim));
|
||||
OnPropertyChanged(nameof(CanUntrim));
|
||||
OnPropertyChanged(nameof(SelectedXCIFiles));
|
||||
|
||||
if (displayedChanged)
|
||||
OnPropertyChanged(nameof(SelectedDisplayedXCIFiles));
|
||||
}
|
||||
|
||||
private void ProcessingChanged()
|
||||
{
|
||||
OnPropertyChanged(nameof(Processing));
|
||||
OnPropertyChanged(nameof(Cancel));
|
||||
OnPropertyChanged(nameof(Status));
|
||||
OnPropertyChanged(nameof(CanTrim));
|
||||
OnPropertyChanged(nameof(CanUntrim));
|
||||
}
|
||||
|
||||
private IEnumerable<XCITrimmerFileModel> GetSelectedDisplayedXCIFiles()
|
||||
{
|
||||
return _displayedXCIFiles.Where(xci => _selectedXCIFiles.Contains(xci));
|
||||
}
|
||||
|
||||
private void PerformOperation(ProcessingMode processingMode)
|
||||
{
|
||||
if (Processing)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_processingMode = processingMode;
|
||||
Processing = true;
|
||||
var cancellationToken = _cancellationTokenSource.Token;
|
||||
|
||||
Thread XCIFileTrimThread = new(() =>
|
||||
{
|
||||
var toProcess = Sort(SelectedXCIFiles
|
||||
.Where(xci =>
|
||||
(processingMode == ProcessingMode.Untrimming && xci.Untrimmable) ||
|
||||
(processingMode == ProcessingMode.Trimming && xci.Trimmable)
|
||||
)).ToList();
|
||||
|
||||
var viewsSaved = DisplayedXCIFiles.ToList();
|
||||
|
||||
Dispatcher.UIThread.Post(() =>
|
||||
{
|
||||
_selectedXCIFiles.Clear();
|
||||
_displayedXCIFiles.Clear();
|
||||
_displayedXCIFiles.AddRange(toProcess);
|
||||
});
|
||||
|
||||
try
|
||||
{
|
||||
foreach (var xciApp in toProcess)
|
||||
{
|
||||
if (cancellationToken.IsCancellationRequested)
|
||||
break;
|
||||
|
||||
var trimmer = new XCIFileTrimmer(xciApp.Path, _logger);
|
||||
|
||||
Dispatcher.UIThread.Post(() =>
|
||||
{
|
||||
ProcessingApplication = xciApp;
|
||||
});
|
||||
|
||||
var outcome = OperationOutcome.Undetermined;
|
||||
|
||||
try
|
||||
{
|
||||
if (cancellationToken.IsCancellationRequested)
|
||||
break;
|
||||
|
||||
switch (processingMode)
|
||||
{
|
||||
case ProcessingMode.Trimming:
|
||||
outcome = trimmer.Trim(cancellationToken);
|
||||
break;
|
||||
case ProcessingMode.Untrimming:
|
||||
outcome = trimmer.Untrim(cancellationToken);
|
||||
break;
|
||||
}
|
||||
|
||||
if (outcome == OperationOutcome.Cancelled)
|
||||
outcome = OperationOutcome.Undetermined;
|
||||
}
|
||||
finally
|
||||
{
|
||||
Dispatcher.UIThread.Post(() =>
|
||||
{
|
||||
ProcessingApplication = CreateXCITrimmerFile(xciApp.Path);
|
||||
AddOrUpdateXCITrimmerFile(ProcessingApplication, false, false);
|
||||
ProcessingApplication = null;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
Dispatcher.UIThread.Post(() =>
|
||||
{
|
||||
_displayedXCIFiles.AddOrReplaceMatching(_allXCIFiles, viewsSaved);
|
||||
_selectedXCIFiles.AddOrReplaceMatching(_allXCIFiles, toProcess);
|
||||
Processing = false;
|
||||
ApplicationsChanged();
|
||||
});
|
||||
}
|
||||
})
|
||||
{
|
||||
Name = "GUI.XCIFilesTrimmerThread",
|
||||
IsBackground = true,
|
||||
};
|
||||
|
||||
XCIFileTrimThread.Start();
|
||||
}
|
||||
|
||||
private bool Filter<T>(T arg)
|
||||
{
|
||||
if (arg is XCITrimmerFileModel content)
|
||||
{
|
||||
return string.IsNullOrWhiteSpace(_search)
|
||||
|| content.Name.ToLower().Contains(_search.ToLower())
|
||||
|| content.Path.ToLower().Contains(_search.ToLower());
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private class CompareXCITrimmerFiles : IComparer<XCITrimmerFileModel>
|
||||
{
|
||||
private XCITrimmerViewModel _viewModel;
|
||||
|
||||
public CompareXCITrimmerFiles(XCITrimmerViewModel ViewModel)
|
||||
{
|
||||
_viewModel = ViewModel;
|
||||
}
|
||||
|
||||
public int Compare(XCITrimmerFileModel x, XCITrimmerFileModel y)
|
||||
{
|
||||
int result = 0;
|
||||
|
||||
switch (_viewModel.SortingField)
|
||||
{
|
||||
case SortField.Name:
|
||||
result = x.Name.CompareTo(y.Name);
|
||||
break;
|
||||
case SortField.Saved:
|
||||
result = x.PotentialSavingsB.CompareTo(y.PotentialSavingsB);
|
||||
break;
|
||||
}
|
||||
|
||||
if (!_viewModel.SortingAscending)
|
||||
result = -result;
|
||||
|
||||
if (result == 0)
|
||||
result = x.Path.CompareTo(y.Path);
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
private IOrderedEnumerable<XCITrimmerFileModel> Sort(IEnumerable<XCITrimmerFileModel> list)
|
||||
{
|
||||
return list
|
||||
.OrderBy(xci => xci, new CompareXCITrimmerFiles(this))
|
||||
.ThenBy(it => it.Path);
|
||||
}
|
||||
|
||||
public void TrimSelected()
|
||||
{
|
||||
PerformOperation(ProcessingMode.Trimming);
|
||||
}
|
||||
|
||||
public void UntrimSelected()
|
||||
{
|
||||
PerformOperation(ProcessingMode.Untrimming);
|
||||
}
|
||||
|
||||
public void SetProgress(int current, int maximum)
|
||||
{
|
||||
if (_processingApplication != null)
|
||||
{
|
||||
int percentageProgress = 100 * current / maximum;
|
||||
if (!ProcessingApplication.HasValue || (ProcessingApplication.Value.PercentageProgress != percentageProgress))
|
||||
ProcessingApplication = ProcessingApplication.Value with { PercentageProgress = percentageProgress };
|
||||
}
|
||||
}
|
||||
|
||||
public void SelectDisplayed()
|
||||
{
|
||||
SelectedXCIFiles.AddRange(DisplayedXCIFiles);
|
||||
SelectionChanged();
|
||||
}
|
||||
|
||||
public void DeselectDisplayed()
|
||||
{
|
||||
SelectedXCIFiles.RemoveMany(DisplayedXCIFiles);
|
||||
SelectionChanged();
|
||||
}
|
||||
|
||||
public void Select(XCITrimmerFileModel model)
|
||||
{
|
||||
bool selectionChanged = !SelectedXCIFiles.Contains(model);
|
||||
bool displayedSelectionChanged = !SelectedDisplayedXCIFiles.Contains(model);
|
||||
SelectedXCIFiles.ReplaceOrAdd(model, model);
|
||||
if (selectionChanged)
|
||||
SelectionChanged(displayedSelectionChanged);
|
||||
}
|
||||
|
||||
public void Deselect(XCITrimmerFileModel model)
|
||||
{
|
||||
bool displayedSelectionChanged = !SelectedDisplayedXCIFiles.Contains(model);
|
||||
if (SelectedXCIFiles.Remove(model))
|
||||
SelectionChanged(displayedSelectionChanged);
|
||||
}
|
||||
|
||||
public void SortAndFilter()
|
||||
{
|
||||
if (Processing)
|
||||
return;
|
||||
|
||||
Sort(AllXCIFiles)
|
||||
.AsObservableChangeSet()
|
||||
.Filter(Filter)
|
||||
.Bind(out var view).AsObservableList();
|
||||
|
||||
_displayedXCIFiles.Clear();
|
||||
_displayedXCIFiles.AddRange(view);
|
||||
|
||||
DisplayedChanged();
|
||||
}
|
||||
|
||||
public Optional<XCITrimmerFileModel> ProcessingApplication
|
||||
{
|
||||
get => _processingApplication;
|
||||
set
|
||||
{
|
||||
if (!value.HasValue && _processingApplication.HasValue)
|
||||
value = _processingApplication.Value with { PercentageProgress = null };
|
||||
|
||||
if (value.HasValue)
|
||||
_displayedXCIFiles.ReplaceWith(value.Value);
|
||||
|
||||
_processingApplication = value;
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
|
||||
public bool Processing
|
||||
{
|
||||
get => _cancellationTokenSource != null;
|
||||
private set
|
||||
{
|
||||
if (value && !Processing)
|
||||
{
|
||||
_cancellationTokenSource = new CancellationTokenSource();
|
||||
}
|
||||
else if (!value && Processing)
|
||||
{
|
||||
_cancellationTokenSource.Dispose();
|
||||
_cancellationTokenSource = null;
|
||||
}
|
||||
|
||||
ProcessingChanged();
|
||||
}
|
||||
}
|
||||
|
||||
public bool Cancel
|
||||
{
|
||||
get => _cancellationTokenSource != null && _cancellationTokenSource.IsCancellationRequested;
|
||||
set
|
||||
{
|
||||
if (value)
|
||||
{
|
||||
if (!Processing)
|
||||
return;
|
||||
|
||||
_cancellationTokenSource.Cancel();
|
||||
}
|
||||
|
||||
ProcessingChanged();
|
||||
}
|
||||
}
|
||||
|
||||
public string Status
|
||||
{
|
||||
get
|
||||
{
|
||||
if (Processing)
|
||||
{
|
||||
return _processingMode switch
|
||||
{
|
||||
ProcessingMode.Trimming => string.Format(LocaleManager.Instance[LocaleKeys.XCITrimmerTitleStatusTrimming], DisplayedXCIFiles.Count),
|
||||
ProcessingMode.Untrimming => string.Format(LocaleManager.Instance[LocaleKeys.XCITrimmerTitleStatusUntrimming], DisplayedXCIFiles.Count),
|
||||
_ => string.Empty
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
return string.IsNullOrEmpty(Search) ?
|
||||
string.Format(LocaleManager.Instance[LocaleKeys.XCITrimmerTitleStatusCount], SelectedXCIFiles.Count, AllXCIFiles.Count) :
|
||||
string.Format(LocaleManager.Instance[LocaleKeys.XCITrimmerTitleStatusCountWithFilter], SelectedXCIFiles.Count, AllXCIFiles.Count, DisplayedXCIFiles.Count);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public string Search
|
||||
{
|
||||
get => _search;
|
||||
set
|
||||
{
|
||||
_search = value;
|
||||
FilteringChanged();
|
||||
}
|
||||
}
|
||||
|
||||
public SortField SortingField
|
||||
{
|
||||
get => _sortField;
|
||||
set
|
||||
{
|
||||
_sortField = value;
|
||||
SortingChanged();
|
||||
}
|
||||
}
|
||||
|
||||
public string SortingFieldName
|
||||
{
|
||||
get
|
||||
{
|
||||
return SortingField switch
|
||||
{
|
||||
SortField.Name => LocaleManager.Instance[LocaleKeys.XCITrimmerSortName],
|
||||
SortField.Saved => LocaleManager.Instance[LocaleKeys.XCITrimmerSortSaved],
|
||||
_ => string.Empty,
|
||||
};
|
||||
}
|
||||
}
|
||||
public bool SortingAscending
|
||||
{
|
||||
get => _sortAscending;
|
||||
set
|
||||
{
|
||||
_sortAscending = value;
|
||||
SortingChanged();
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsSortedByName
|
||||
{
|
||||
get => _sortField == SortField.Name;
|
||||
}
|
||||
|
||||
public bool IsSortedBySaved
|
||||
{
|
||||
get => _sortField == SortField.Saved;
|
||||
}
|
||||
|
||||
public AvaloniaList<XCITrimmerFileModel> SelectedXCIFiles
|
||||
{
|
||||
get => _selectedXCIFiles;
|
||||
set
|
||||
{
|
||||
_selectedXCIFiles = value;
|
||||
SelectionChanged();
|
||||
}
|
||||
}
|
||||
|
||||
public AvaloniaList<XCITrimmerFileModel> AllXCIFiles
|
||||
{
|
||||
get => _allXCIFiles;
|
||||
}
|
||||
|
||||
public AvaloniaList<XCITrimmerFileModel> DisplayedXCIFiles
|
||||
{
|
||||
get => _displayedXCIFiles;
|
||||
}
|
||||
|
||||
public string PotentialSavings
|
||||
{
|
||||
get
|
||||
{
|
||||
return string.Format(LocaleManager.Instance[LocaleKeys.XCITrimmerSavingsMb], AllXCIFiles.Sum(xci => xci.PotentialSavingsB / _bytesPerMB));
|
||||
}
|
||||
}
|
||||
|
||||
public string ActualSavings
|
||||
{
|
||||
get
|
||||
{
|
||||
return string.Format(LocaleManager.Instance[LocaleKeys.XCITrimmerSavingsMb], AllXCIFiles.Sum(xci => xci.CurrentSavingsB / _bytesPerMB));
|
||||
}
|
||||
}
|
||||
|
||||
public IEnumerable<XCITrimmerFileModel> SelectedDisplayedXCIFiles
|
||||
{
|
||||
get
|
||||
{
|
||||
return GetSelectedDisplayedXCIFiles().ToList();
|
||||
}
|
||||
}
|
||||
|
||||
public bool CanTrim
|
||||
{
|
||||
get
|
||||
{
|
||||
return !Processing && _selectedXCIFiles.Any(xci => xci.Trimmable);
|
||||
}
|
||||
}
|
||||
|
||||
public bool CanUntrim
|
||||
{
|
||||
get
|
||||
{
|
||||
return !Processing && _selectedXCIFiles.Any(xci => xci.Untrimmable);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -33,7 +33,7 @@ namespace Ryujinx.Ava.UI.Views.Input
|
||||
{
|
||||
base.OnPointerReleased(e);
|
||||
|
||||
if (_currentAssigner != null && _currentAssigner.ToggledButton != null && !_currentAssigner.ToggledButton.IsPointerOver)
|
||||
if (_currentAssigner is { ToggledButton.IsPointerOver: false })
|
||||
{
|
||||
_currentAssigner.Cancel();
|
||||
}
|
||||
@ -41,143 +41,146 @@ namespace Ryujinx.Ava.UI.Views.Input
|
||||
|
||||
private void Button_IsCheckedChanged(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (sender is ToggleButton button)
|
||||
if (sender is not ToggleButton button)
|
||||
return;
|
||||
|
||||
if (button.IsChecked is true)
|
||||
{
|
||||
if ((bool)button.IsChecked)
|
||||
if (_currentAssigner != null && button == _currentAssigner.ToggledButton)
|
||||
{
|
||||
if (_currentAssigner != null && button == _currentAssigner.ToggledButton)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (_currentAssigner == null)
|
||||
{
|
||||
_currentAssigner = new ButtonKeyAssigner(button);
|
||||
|
||||
Focus(NavigationMethod.Pointer);
|
||||
|
||||
PointerPressed += MouseClick;
|
||||
|
||||
if (DataContext is not KeyboardInputViewModel viewModel)
|
||||
return;
|
||||
}
|
||||
|
||||
if (_currentAssigner == null)
|
||||
IKeyboard keyboard =
|
||||
(IKeyboard)viewModel.ParentModel.AvaloniaKeyboardDriver.GetGamepad("0"); // Open Avalonia keyboard for cancel operations.
|
||||
IButtonAssigner assigner =
|
||||
new KeyboardKeyAssigner((IKeyboard)viewModel.ParentModel.SelectedGamepad);
|
||||
|
||||
_currentAssigner.ButtonAssigned += (_, e) =>
|
||||
{
|
||||
_currentAssigner = new ButtonKeyAssigner(button);
|
||||
|
||||
Focus(NavigationMethod.Pointer);
|
||||
|
||||
PointerPressed += MouseClick;
|
||||
|
||||
var viewModel = (DataContext as KeyboardInputViewModel);
|
||||
|
||||
IKeyboard keyboard = (IKeyboard)viewModel.ParentModel.AvaloniaKeyboardDriver.GetGamepad("0"); // Open Avalonia keyboard for cancel operations.
|
||||
IButtonAssigner assigner = CreateButtonAssigner();
|
||||
|
||||
_currentAssigner.ButtonAssigned += (sender, e) =>
|
||||
if (e.ButtonValue.HasValue)
|
||||
{
|
||||
if (e.ButtonValue.HasValue)
|
||||
var buttonValue = e.ButtonValue.Value;
|
||||
viewModel.ParentModel.IsModified = true;
|
||||
|
||||
switch (button.Name)
|
||||
{
|
||||
var buttonValue = e.ButtonValue.Value;
|
||||
viewModel.ParentModel.IsModified = true;
|
||||
|
||||
switch (button.Name)
|
||||
{
|
||||
case "ButtonZl":
|
||||
viewModel.Config.ButtonZl = buttonValue.AsHidType<Key>();
|
||||
break;
|
||||
case "ButtonL":
|
||||
viewModel.Config.ButtonL = buttonValue.AsHidType<Key>();
|
||||
break;
|
||||
case "ButtonMinus":
|
||||
viewModel.Config.ButtonMinus = buttonValue.AsHidType<Key>();
|
||||
break;
|
||||
case "LeftStickButton":
|
||||
viewModel.Config.LeftStickButton = buttonValue.AsHidType<Key>();
|
||||
break;
|
||||
case "LeftStickUp":
|
||||
viewModel.Config.LeftStickUp = buttonValue.AsHidType<Key>();
|
||||
break;
|
||||
case "LeftStickDown":
|
||||
viewModel.Config.LeftStickDown = buttonValue.AsHidType<Key>();
|
||||
break;
|
||||
case "LeftStickRight":
|
||||
viewModel.Config.LeftStickRight = buttonValue.AsHidType<Key>();
|
||||
break;
|
||||
case "LeftStickLeft":
|
||||
viewModel.Config.LeftStickLeft = buttonValue.AsHidType<Key>();
|
||||
break;
|
||||
case "DpadUp":
|
||||
viewModel.Config.DpadUp = buttonValue.AsHidType<Key>();
|
||||
break;
|
||||
case "DpadDown":
|
||||
viewModel.Config.DpadDown = buttonValue.AsHidType<Key>();
|
||||
break;
|
||||
case "DpadLeft":
|
||||
viewModel.Config.DpadLeft = buttonValue.AsHidType<Key>();
|
||||
break;
|
||||
case "DpadRight":
|
||||
viewModel.Config.DpadRight = buttonValue.AsHidType<Key>();
|
||||
break;
|
||||
case "LeftButtonSr":
|
||||
viewModel.Config.LeftButtonSr = buttonValue.AsHidType<Key>();
|
||||
break;
|
||||
case "LeftButtonSl":
|
||||
viewModel.Config.LeftButtonSl = buttonValue.AsHidType<Key>();
|
||||
break;
|
||||
case "RightButtonSr":
|
||||
viewModel.Config.RightButtonSr = buttonValue.AsHidType<Key>();
|
||||
break;
|
||||
case "RightButtonSl":
|
||||
viewModel.Config.RightButtonSl = buttonValue.AsHidType<Key>();
|
||||
break;
|
||||
case "ButtonZr":
|
||||
viewModel.Config.ButtonZr = buttonValue.AsHidType<Key>();
|
||||
break;
|
||||
case "ButtonR":
|
||||
viewModel.Config.ButtonR = buttonValue.AsHidType<Key>();
|
||||
break;
|
||||
case "ButtonPlus":
|
||||
viewModel.Config.ButtonPlus = buttonValue.AsHidType<Key>();
|
||||
break;
|
||||
case "ButtonA":
|
||||
viewModel.Config.ButtonA = buttonValue.AsHidType<Key>();
|
||||
break;
|
||||
case "ButtonB":
|
||||
viewModel.Config.ButtonB = buttonValue.AsHidType<Key>();
|
||||
break;
|
||||
case "ButtonX":
|
||||
viewModel.Config.ButtonX = buttonValue.AsHidType<Key>();
|
||||
break;
|
||||
case "ButtonY":
|
||||
viewModel.Config.ButtonY = buttonValue.AsHidType<Key>();
|
||||
break;
|
||||
case "RightStickButton":
|
||||
viewModel.Config.RightStickButton = buttonValue.AsHidType<Key>();
|
||||
break;
|
||||
case "RightStickUp":
|
||||
viewModel.Config.RightStickUp = buttonValue.AsHidType<Key>();
|
||||
break;
|
||||
case "RightStickDown":
|
||||
viewModel.Config.RightStickDown = buttonValue.AsHidType<Key>();
|
||||
break;
|
||||
case "RightStickRight":
|
||||
viewModel.Config.RightStickRight = buttonValue.AsHidType<Key>();
|
||||
break;
|
||||
case "RightStickLeft":
|
||||
viewModel.Config.RightStickLeft = buttonValue.AsHidType<Key>();
|
||||
break;
|
||||
}
|
||||
case "ButtonZl":
|
||||
viewModel.Config.ButtonZl = buttonValue.AsHidType<Key>();
|
||||
break;
|
||||
case "ButtonL":
|
||||
viewModel.Config.ButtonL = buttonValue.AsHidType<Key>();
|
||||
break;
|
||||
case "ButtonMinus":
|
||||
viewModel.Config.ButtonMinus = buttonValue.AsHidType<Key>();
|
||||
break;
|
||||
case "LeftStickButton":
|
||||
viewModel.Config.LeftStickButton = buttonValue.AsHidType<Key>();
|
||||
break;
|
||||
case "LeftStickUp":
|
||||
viewModel.Config.LeftStickUp = buttonValue.AsHidType<Key>();
|
||||
break;
|
||||
case "LeftStickDown":
|
||||
viewModel.Config.LeftStickDown = buttonValue.AsHidType<Key>();
|
||||
break;
|
||||
case "LeftStickRight":
|
||||
viewModel.Config.LeftStickRight = buttonValue.AsHidType<Key>();
|
||||
break;
|
||||
case "LeftStickLeft":
|
||||
viewModel.Config.LeftStickLeft = buttonValue.AsHidType<Key>();
|
||||
break;
|
||||
case "DpadUp":
|
||||
viewModel.Config.DpadUp = buttonValue.AsHidType<Key>();
|
||||
break;
|
||||
case "DpadDown":
|
||||
viewModel.Config.DpadDown = buttonValue.AsHidType<Key>();
|
||||
break;
|
||||
case "DpadLeft":
|
||||
viewModel.Config.DpadLeft = buttonValue.AsHidType<Key>();
|
||||
break;
|
||||
case "DpadRight":
|
||||
viewModel.Config.DpadRight = buttonValue.AsHidType<Key>();
|
||||
break;
|
||||
case "LeftButtonSr":
|
||||
viewModel.Config.LeftButtonSr = buttonValue.AsHidType<Key>();
|
||||
break;
|
||||
case "LeftButtonSl":
|
||||
viewModel.Config.LeftButtonSl = buttonValue.AsHidType<Key>();
|
||||
break;
|
||||
case "RightButtonSr":
|
||||
viewModel.Config.RightButtonSr = buttonValue.AsHidType<Key>();
|
||||
break;
|
||||
case "RightButtonSl":
|
||||
viewModel.Config.RightButtonSl = buttonValue.AsHidType<Key>();
|
||||
break;
|
||||
case "ButtonZr":
|
||||
viewModel.Config.ButtonZr = buttonValue.AsHidType<Key>();
|
||||
break;
|
||||
case "ButtonR":
|
||||
viewModel.Config.ButtonR = buttonValue.AsHidType<Key>();
|
||||
break;
|
||||
case "ButtonPlus":
|
||||
viewModel.Config.ButtonPlus = buttonValue.AsHidType<Key>();
|
||||
break;
|
||||
case "ButtonA":
|
||||
viewModel.Config.ButtonA = buttonValue.AsHidType<Key>();
|
||||
break;
|
||||
case "ButtonB":
|
||||
viewModel.Config.ButtonB = buttonValue.AsHidType<Key>();
|
||||
break;
|
||||
case "ButtonX":
|
||||
viewModel.Config.ButtonX = buttonValue.AsHidType<Key>();
|
||||
break;
|
||||
case "ButtonY":
|
||||
viewModel.Config.ButtonY = buttonValue.AsHidType<Key>();
|
||||
break;
|
||||
case "RightStickButton":
|
||||
viewModel.Config.RightStickButton = buttonValue.AsHidType<Key>();
|
||||
break;
|
||||
case "RightStickUp":
|
||||
viewModel.Config.RightStickUp = buttonValue.AsHidType<Key>();
|
||||
break;
|
||||
case "RightStickDown":
|
||||
viewModel.Config.RightStickDown = buttonValue.AsHidType<Key>();
|
||||
break;
|
||||
case "RightStickRight":
|
||||
viewModel.Config.RightStickRight = buttonValue.AsHidType<Key>();
|
||||
break;
|
||||
case "RightStickLeft":
|
||||
viewModel.Config.RightStickLeft = buttonValue.AsHidType<Key>();
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
_currentAssigner.GetInputAndAssign(assigner, keyboard);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (_currentAssigner != null)
|
||||
{
|
||||
_currentAssigner.Cancel();
|
||||
_currentAssigner = null;
|
||||
button.IsChecked = false;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
_currentAssigner.GetInputAndAssign(assigner, keyboard);
|
||||
}
|
||||
else
|
||||
{
|
||||
_currentAssigner?.Cancel();
|
||||
_currentAssigner = null;
|
||||
if (_currentAssigner != null)
|
||||
{
|
||||
_currentAssigner.Cancel();
|
||||
_currentAssigner = null;
|
||||
button.IsChecked = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_currentAssigner?.Cancel();
|
||||
_currentAssigner = null;
|
||||
}
|
||||
}
|
||||
|
||||
private void MouseClick(object sender, PointerPressedEventArgs e)
|
||||
@ -189,15 +192,6 @@ namespace Ryujinx.Ava.UI.Views.Input
|
||||
PointerPressed -= MouseClick;
|
||||
}
|
||||
|
||||
private IButtonAssigner CreateButtonAssigner()
|
||||
{
|
||||
IButtonAssigner assigner;
|
||||
|
||||
assigner = new KeyboardKeyAssigner((IKeyboard)(DataContext as KeyboardInputViewModel).ParentModel.SelectedGamepad);
|
||||
|
||||
return assigner;
|
||||
}
|
||||
|
||||
protected override void OnDetachedFromVisualTree(VisualTreeAttachmentEventArgs e)
|
||||
{
|
||||
base.OnDetachedFromVisualTree(e);
|
||||
|
@ -12,13 +12,13 @@
|
||||
<viewModels:MainWindowViewModel />
|
||||
</Design.DataContext>
|
||||
<DockPanel HorizontalAlignment="Stretch">
|
||||
<Border Name="RyuLogo" Padding="7, 0, 0, 0" VerticalAlignment="Center" HorizontalAlignment="Center">
|
||||
<Image
|
||||
ToolTip.Tip="{Binding Title}"
|
||||
Height="25"
|
||||
Width="25"
|
||||
Source="resm:Ryujinx.UI.Common.Resources.Logo_Ryujinx.png?assembly=Ryujinx.UI.Common" />
|
||||
</Border>
|
||||
<Image
|
||||
Name="RyuLogo"
|
||||
Margin="7,0"
|
||||
Height="25"
|
||||
Width="25"
|
||||
ToolTip.Tip="{Binding Title}"
|
||||
Source="resm:Ryujinx.UI.Common.Resources.Logo_Ryujinx.png?assembly=Ryujinx.UI.Common" />
|
||||
<Menu
|
||||
Name="Menu"
|
||||
Height="35"
|
||||
@ -268,11 +268,13 @@
|
||||
<MenuItem Header="{ext:Locale MenuBarToolsInstallFileTypes}" Click="InstallFileTypes_Click"/>
|
||||
<MenuItem Header="{ext:Locale MenuBarToolsUninstallFileTypes}" Click="UninstallFileTypes_Click"/>
|
||||
</MenuItem>
|
||||
<Separator />
|
||||
<MenuItem Header="{ext:Locale MenuBarToolsXCITrimmer}" Click="OpenXCITrimmerWindow" Icon="{ext:Icon fa-solid fa-scissors}" />
|
||||
</MenuItem>
|
||||
<MenuItem VerticalAlignment="Center" Header="{ext:Locale MenuBarView}">
|
||||
<MenuItem VerticalAlignment="Center" Header="{ext:Locale MenuBarViewWindow}">
|
||||
<MenuItem Header="{ext:Locale MenuBarViewWindow720}" Tag="720 1280" Click="ChangeWindowSize_Click" />
|
||||
<MenuItem Header="{ext:Locale MenuBarViewWindow1080}" Tag="1080 1920" Click="ChangeWindowSize_Click" />
|
||||
<MenuItem Header="{ext:Locale MenuBarViewWindow720}" Tag="1280 720" Click="ChangeWindowSize_Click" />
|
||||
<MenuItem Header="{ext:Locale MenuBarViewWindow1080}" Tag="1920 1080" Click="ChangeWindowSize_Click" />
|
||||
</MenuItem>
|
||||
</MenuItem>
|
||||
<MenuItem VerticalAlignment="Center" Header="{ext:Locale MenuBarHelp}">
|
||||
|
@ -184,8 +184,10 @@ namespace Ryujinx.Ava.UI.Views.Main
|
||||
if (sender is not MenuItem { Tag: string resolution })
|
||||
return;
|
||||
|
||||
(int height, int width) = resolution.Split(' ')
|
||||
.Into(parts => (int.Parse(parts[0]), int.Parse(parts[1])));
|
||||
(int width, int height) = resolution.Split(' ', 2)
|
||||
.Into(parts =>
|
||||
(int.Parse(parts[0]), int.Parse(parts[1]))
|
||||
);
|
||||
|
||||
await Dispatcher.UIThread.InvokeAsync(() =>
|
||||
{
|
||||
@ -200,9 +202,11 @@ namespace Ryujinx.Ava.UI.Views.Main
|
||||
public async void CheckForUpdates(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (Updater.CanUpdate(true))
|
||||
await Updater.BeginParse(Window, true);
|
||||
await Window.BeginUpdateAsync(true);
|
||||
}
|
||||
|
||||
public async void OpenXCITrimmerWindow(object sender, RoutedEventArgs e) => await XCITrimmerWindow.Show(ViewModel);
|
||||
|
||||
public async void OpenAboutWindow(object sender, RoutedEventArgs e) => await AboutWindow.Show();
|
||||
|
||||
public void CloseWindow(object sender, RoutedEventArgs e) => Window.Close();
|
||||
|
@ -29,7 +29,7 @@
|
||||
Margin="5"
|
||||
VerticalAlignment="Center"
|
||||
IsVisible="{Binding EnableNonGameRunningControls}">
|
||||
<Grid Margin="0" ColumnDefinitions="Auto,Auto,*">
|
||||
<Grid Margin="0" ColumnDefinitions="Auto,Auto,Auto,*">
|
||||
<Button
|
||||
Width="25"
|
||||
Height="25"
|
||||
@ -50,9 +50,18 @@
|
||||
VerticalAlignment="Center"
|
||||
IsVisible="{Binding EnableNonGameRunningControls}"
|
||||
Text="{ext:Locale StatusBarGamesLoaded}" />
|
||||
<TextBlock
|
||||
Name="StatusBarProgressStatus"
|
||||
Grid.Column="2"
|
||||
MinWidth="200"
|
||||
Margin="10,0,5,0"
|
||||
VerticalAlignment="Center"
|
||||
IsVisible="{Binding StatusBarProgressStatusVisible}"
|
||||
Text="{Binding StatusBarProgressStatusText}" />
|
||||
<ProgressBar
|
||||
Name="LoadProgressBar"
|
||||
Grid.Column="2"
|
||||
Grid.Column="3"
|
||||
MinWidth="200"
|
||||
Height="6"
|
||||
VerticalAlignment="Center"
|
||||
Foreground="{DynamicResource SystemAccentColorLight2}"
|
||||
|
@ -63,8 +63,7 @@ namespace Ryujinx.Ava.UI.Views.User
|
||||
|
||||
private async void Import_OnClick(object sender, RoutedEventArgs e)
|
||||
{
|
||||
var window = this.GetVisualRoot() as Window;
|
||||
var result = await window.StorageProvider.OpenFilePickerAsync(new FilePickerOpenOptions
|
||||
var result = await ((Window)this.GetVisualRoot()!).StorageProvider.OpenFilePickerAsync(new FilePickerOpenOptions
|
||||
{
|
||||
AllowMultiple = false,
|
||||
FileTypeFilter = new List<FilePickerFileType>
|
||||
|
@ -28,6 +28,7 @@ using Ryujinx.UI.Common.Configuration;
|
||||
using Ryujinx.UI.Common.Helper;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reactive.Linq;
|
||||
using System.Runtime.Versioning;
|
||||
using System.Threading;
|
||||
@ -349,12 +350,12 @@ namespace Ryujinx.Ava.UI.Windows
|
||||
await Dispatcher.UIThread.InvokeAsync(async () => await UserErrorDialog.ShowUserErrorDialog(UserError.NoKeys));
|
||||
}
|
||||
|
||||
if (ConfigurationState.Instance.CheckUpdatesOnStart && Updater.CanUpdate(false))
|
||||
if (ConfigurationState.Instance.CheckUpdatesOnStart && Updater.CanUpdate())
|
||||
{
|
||||
await Updater.BeginParse(this, false).ContinueWith(task =>
|
||||
{
|
||||
Logger.Error?.Print(LogClass.Application, $"Updater Error: {task.Exception}");
|
||||
}, TaskContinuationOptions.OnlyOnFaulted);
|
||||
await this.BeginUpdateAsync()
|
||||
.ContinueWith(
|
||||
task => Logger.Error?.Print(LogClass.Application, $"Updater Error: {task.Exception}"),
|
||||
TaskContinuationOptions.OnlyOnFaulted);
|
||||
}
|
||||
}
|
||||
|
||||
@ -394,30 +395,17 @@ namespace Ryujinx.Ava.UI.Windows
|
||||
|
||||
Arrange(new Rect(savedPoint.X, savedPoint.Y, ViewModel.WindowWidth, ViewModel.WindowHeight));
|
||||
|
||||
if (CheckScreenBounds(savedPoint))
|
||||
if (Screens.All.Any(screen => screen.Bounds.Contains(savedPoint)))
|
||||
{
|
||||
Position = savedPoint;
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.Warning?.Print(LogClass.Application, "Failed to find valid start-up coordinates. Defaulting to primary monitor center.");
|
||||
WindowStartupLocation = WindowStartupLocation.CenterScreen;
|
||||
}
|
||||
}
|
||||
|
||||
private bool CheckScreenBounds(PixelPoint configPoint)
|
||||
{
|
||||
for (int i = 0; i < Screens.ScreenCount; i++)
|
||||
{
|
||||
if (Screens.All[i].Bounds.Contains(configPoint))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
Logger.Warning?.Print(LogClass.Application, "Failed to find valid start-up coordinates. Defaulting to primary monitor center.");
|
||||
return false;
|
||||
}
|
||||
|
||||
private void SaveWindowSizePosition()
|
||||
{
|
||||
ConfigurationState.Instance.UI.WindowStartup.WindowMaximized.Value = WindowState == WindowState.Maximized;
|
||||
@ -509,8 +497,7 @@ namespace Ryujinx.Ava.UI.Windows
|
||||
|
||||
private void VolumeStatus_CheckedChanged(object sender, RoutedEventArgs e)
|
||||
{
|
||||
var volumeSplitButton = sender as ToggleSplitButton;
|
||||
if (ViewModel.IsGameRunning)
|
||||
if (ViewModel.IsGameRunning && sender is ToggleSplitButton volumeSplitButton)
|
||||
{
|
||||
if (!volumeSplitButton.IsChecked)
|
||||
{
|
||||
|
354
src/Ryujinx/UI/Windows/XCITrimmerWindow.axaml
Normal file
354
src/Ryujinx/UI/Windows/XCITrimmerWindow.axaml
Normal file
@ -0,0 +1,354 @@
|
||||
<UserControl
|
||||
x:Class="Ryujinx.Ava.UI.Windows.XCITrimmerWindow"
|
||||
xmlns="https://github.com/avaloniaui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:ext="clr-namespace:Ryujinx.Ava.Common.Markup"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:viewModels="clr-namespace:Ryujinx.Ava.UI.ViewModels"
|
||||
xmlns:models="clr-namespace:Ryujinx.UI.Common.Models;assembly=Ryujinx.UI.Common"
|
||||
xmlns:ui="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia"
|
||||
xmlns:helpers="clr-namespace:Ryujinx.Ava.UI.Helpers"
|
||||
Width="700"
|
||||
Height="600"
|
||||
x:DataType="viewModels:XCITrimmerViewModel"
|
||||
Focusable="True"
|
||||
mc:Ignorable="d">
|
||||
<UserControl.Resources>
|
||||
<helpers:XCITrimmerFileStatusConverter x:Key="StatusLabel" />
|
||||
<helpers:XCITrimmerFileStatusDetailConverter x:Key="StatusDetailLabel" />
|
||||
<helpers:XCITrimmerFileSpaceSavingsConverter x:Key="SpaceSavingsLabel" />
|
||||
</UserControl.Resources>
|
||||
<Grid Margin="20 0 20 0">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="*" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
<Panel
|
||||
Margin="10 10 10 10"
|
||||
Grid.Row="0">
|
||||
<TextBlock Text="{Binding Status}" />
|
||||
</Panel>
|
||||
<Panel
|
||||
Margin="0 0 10 10"
|
||||
IsVisible="{Binding !Processing}"
|
||||
Grid.Row="1">
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<StackPanel
|
||||
Grid.Column="0"
|
||||
Orientation="Horizontal">
|
||||
<TextBlock
|
||||
Margin="10,0"
|
||||
HorizontalAlignment="Left"
|
||||
VerticalAlignment="Center"
|
||||
Text="{ext:Locale CommonSort}" />
|
||||
<DropDownButton
|
||||
Width="150"
|
||||
HorizontalAlignment="Left"
|
||||
VerticalAlignment="Center"
|
||||
Content="{Binding SortingFieldName}">
|
||||
<DropDownButton.Flyout>
|
||||
<Flyout Placement="Bottom">
|
||||
<StackPanel
|
||||
Margin="0"
|
||||
HorizontalAlignment="Stretch"
|
||||
Orientation="Vertical">
|
||||
<StackPanel>
|
||||
<RadioButton
|
||||
Checked="Sort_Checked"
|
||||
Content="{ext:Locale XCITrimmerSortName}"
|
||||
GroupName="Sort"
|
||||
IsChecked="{Binding IsSortedByName, Mode=OneTime}"
|
||||
Tag="Name" />
|
||||
<RadioButton
|
||||
Checked="Sort_Checked"
|
||||
Content="{ext:Locale XCITrimmerSortSaved}"
|
||||
GroupName="Sort"
|
||||
IsChecked="{Binding IsSortedBySaved, Mode=OneTime}"
|
||||
Tag="Saved" />
|
||||
</StackPanel>
|
||||
<Border
|
||||
Width="60"
|
||||
Height="2"
|
||||
Margin="5"
|
||||
HorizontalAlignment="Stretch"
|
||||
BorderBrush="White"
|
||||
BorderThickness="0,1,0,0">
|
||||
<Separator Height="0" HorizontalAlignment="Stretch" />
|
||||
</Border>
|
||||
<RadioButton
|
||||
Checked="Order_Checked"
|
||||
Content="{ext:Locale OrderAscending}"
|
||||
GroupName="Order"
|
||||
IsChecked="{Binding SortingAscending, Mode=OneTime}"
|
||||
Tag="Ascending" />
|
||||
<RadioButton
|
||||
Checked="Order_Checked"
|
||||
Content="{ext:Locale OrderDescending}"
|
||||
GroupName="Order"
|
||||
IsChecked="{Binding !SortingAscending, Mode=OneTime}"
|
||||
Tag="Descending" />
|
||||
</StackPanel>
|
||||
</Flyout>
|
||||
</DropDownButton.Flyout>
|
||||
</DropDownButton>
|
||||
</StackPanel>
|
||||
<TextBox
|
||||
Grid.Column="1"
|
||||
MinHeight="29"
|
||||
MaxHeight="29"
|
||||
Margin="5 0 5 0"
|
||||
HorizontalAlignment="Stretch"
|
||||
Watermark="{ext:Locale Search}"
|
||||
Text="{Binding Search}" />
|
||||
<StackPanel
|
||||
Grid.Column="2"
|
||||
Orientation="Horizontal">
|
||||
<Button
|
||||
Name="SelectDisplayedButton"
|
||||
MinWidth="90"
|
||||
Margin="5"
|
||||
Command="{Binding SelectDisplayed}">
|
||||
<TextBlock Text="{ext:Locale XCITrimmerSelectDisplayed}" />
|
||||
</Button>
|
||||
<Button
|
||||
Name="DeselectDisplayedButton"
|
||||
MinWidth="90"
|
||||
Margin="5"
|
||||
Command="{Binding DeselectDisplayed}">
|
||||
<TextBlock Text="{ext:Locale XCITrimmerDeselectDisplayed}" />
|
||||
</Button>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</Panel>
|
||||
<Border
|
||||
Grid.Row="2"
|
||||
Margin="0 0 0 10"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Stretch"
|
||||
BorderBrush="{DynamicResource AppListHoverBackgroundColor}"
|
||||
BorderThickness="1"
|
||||
CornerRadius="5"
|
||||
Padding="2.5">
|
||||
<ListBox
|
||||
AutoScrollToSelectedItem="{Binding Processing}"
|
||||
SelectedItem="{Binding ProcessingApplication.Value}"
|
||||
SelectionMode="Multiple, Toggle"
|
||||
Background="Transparent"
|
||||
SelectionChanged="OnSelectionChanged"
|
||||
SelectedItems="{Binding SelectedDisplayedXCIFiles, Mode=OneWay}"
|
||||
ItemsSource="{Binding DisplayedXCIFiles}"
|
||||
IsEnabled="{Binding !Processing}">
|
||||
<ListBox.DataTemplates>
|
||||
<DataTemplate
|
||||
DataType="models:XCITrimmerFileModel">
|
||||
<Panel Margin="10">
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="65*" />
|
||||
<ColumnDefinition Width="35*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock
|
||||
Grid.Column="0"
|
||||
Margin="10 0 10 0"
|
||||
HorizontalAlignment="Left"
|
||||
VerticalAlignment="Center"
|
||||
MaxLines="2"
|
||||
TextWrapping="Wrap"
|
||||
TextTrimming="CharacterEllipsis"
|
||||
Text="{Binding Name}">
|
||||
</TextBlock>
|
||||
<Grid Grid.Column="1">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="45*" />
|
||||
<ColumnDefinition Width="55*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<ProgressBar
|
||||
Height="10"
|
||||
Margin="10 0 10 0"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Center"
|
||||
CornerRadius="5"
|
||||
IsVisible="{Binding $parent[UserControl].((viewModels:XCITrimmerViewModel)DataContext).Processing}"
|
||||
Maximum="100"
|
||||
Minimum="0"
|
||||
Value="{Binding PercentageProgress}" />
|
||||
<TextBlock
|
||||
Grid.Column="0"
|
||||
Margin="10 0 10 0"
|
||||
HorizontalAlignment="Left"
|
||||
VerticalAlignment="Center"
|
||||
MaxLines="1"
|
||||
Text="{Binding ., Converter={StaticResource StatusLabel}}">
|
||||
<ToolTip.Tip>
|
||||
<StackPanel
|
||||
IsVisible="{Binding IsFailed}">
|
||||
<TextBlock
|
||||
Classes="h1"
|
||||
Text="{ext:Locale XCITrimmerTitleStatusFailed}" />
|
||||
<TextBlock
|
||||
Text="{Binding ., Converter={StaticResource StatusDetailLabel}}"
|
||||
MaxLines="5"
|
||||
MaxWidth="200"
|
||||
MaxHeight="100"
|
||||
TextTrimming="None"
|
||||
TextWrapping="Wrap"/>
|
||||
</StackPanel>
|
||||
</ToolTip.Tip>
|
||||
</TextBlock>
|
||||
<TextBlock
|
||||
Grid.Column="1"
|
||||
Margin="10 0 10 0"
|
||||
HorizontalAlignment="Left"
|
||||
VerticalAlignment="Center"
|
||||
MaxLines="1"
|
||||
Text="{Binding ., Converter={StaticResource SpaceSavingsLabel}}">>
|
||||
</TextBlock>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Panel>
|
||||
</DataTemplate>
|
||||
</ListBox.DataTemplates>
|
||||
<ListBox.Styles>
|
||||
<Style Selector="ListBoxItem">
|
||||
<Setter Property="Background" Value="Transparent" />
|
||||
</Style>
|
||||
</ListBox.Styles>
|
||||
</ListBox>
|
||||
</Border>
|
||||
<Border
|
||||
Grid.Row="3"
|
||||
Margin="0 0 0 10"
|
||||
HorizontalAlignment="Stretch"
|
||||
BorderBrush="{DynamicResource AppListHoverBackgroundColor}"
|
||||
BorderThickness="1"
|
||||
CornerRadius="5"
|
||||
Padding="2.5">
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
<TextBlock
|
||||
Grid.Column="0"
|
||||
Grid.Row="0"
|
||||
Classes="h1"
|
||||
Margin="5"
|
||||
HorizontalAlignment="Right"
|
||||
VerticalAlignment="Center"
|
||||
MaxLines="1"
|
||||
Text="{ext:Locale XCITrimmerPotentialSavings}" />
|
||||
<TextBlock
|
||||
Grid.Column="0"
|
||||
Grid.Row="1"
|
||||
Classes="h1"
|
||||
Margin="5"
|
||||
HorizontalAlignment="Right"
|
||||
VerticalAlignment="Center"
|
||||
MaxLines="1"
|
||||
Text="{ext:Locale XCITrimmerActualSavings}" />
|
||||
<TextBlock
|
||||
Grid.Column="1"
|
||||
Grid.Row="0"
|
||||
Margin="5"
|
||||
HorizontalAlignment="Left"
|
||||
VerticalAlignment="Center"
|
||||
MaxLines="1"
|
||||
Text="{Binding PotentialSavings}" />
|
||||
<TextBlock
|
||||
Grid.Column="1"
|
||||
Grid.Row="1"
|
||||
Margin="5"
|
||||
HorizontalAlignment="Left"
|
||||
VerticalAlignment="Center"
|
||||
MaxLines="1"
|
||||
Text="{Binding ActualSavings}" />
|
||||
</Grid>
|
||||
</Border>
|
||||
<Panel
|
||||
Grid.Row="4"
|
||||
HorizontalAlignment="Stretch">
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<StackPanel
|
||||
Grid.Column="0"
|
||||
Orientation="Horizontal"
|
||||
Spacing="10"
|
||||
HorizontalAlignment="Left">
|
||||
<Button
|
||||
Name="TrimButton"
|
||||
MinWidth="90"
|
||||
Margin="5"
|
||||
Click="Trim"
|
||||
IsEnabled="{Binding CanTrim}">
|
||||
<TextBlock Text="Trim" />
|
||||
</Button>
|
||||
<Button
|
||||
Name="UntrimButton"
|
||||
MinWidth="90"
|
||||
Margin="5"
|
||||
Click="Untrim"
|
||||
IsEnabled="{Binding CanUntrim}">
|
||||
<TextBlock Text="Untrim" />
|
||||
</Button>
|
||||
</StackPanel>
|
||||
<StackPanel
|
||||
Grid.Column="1"
|
||||
Orientation="Horizontal"
|
||||
Spacing="10"
|
||||
HorizontalAlignment="Right">
|
||||
<Button
|
||||
Name="CancellingButton"
|
||||
MinWidth="90"
|
||||
Margin="5"
|
||||
Click="Cancel"
|
||||
IsEnabled="False">
|
||||
<Button.IsVisible>
|
||||
<MultiBinding Converter="{x:Static BoolConverters.And}">
|
||||
<Binding Path="Processing" />
|
||||
<Binding Path="Cancel" />
|
||||
</MultiBinding>
|
||||
</Button.IsVisible>
|
||||
<TextBlock Text="{ext:Locale InputDialogCancelling}" />
|
||||
</Button>
|
||||
<Button
|
||||
Name="CancelButton"
|
||||
MinWidth="90"
|
||||
Margin="5"
|
||||
Click="Cancel">
|
||||
<Button.IsVisible>
|
||||
<MultiBinding Converter="{x:Static BoolConverters.And}">
|
||||
<Binding Path="Processing" />
|
||||
<Binding Path="!Cancel" />
|
||||
</MultiBinding>
|
||||
</Button.IsVisible>
|
||||
<TextBlock Text="{ext:Locale InputDialogCancel}" />
|
||||
</Button>
|
||||
<Button
|
||||
Name="CloseButton"
|
||||
MinWidth="90"
|
||||
Margin="5"
|
||||
Click="Close"
|
||||
IsVisible="{Binding !Processing}">
|
||||
<TextBlock Text="{ext:Locale InputDialogClose}" />
|
||||
</Button>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</Panel>
|
||||
</Grid>
|
||||
</UserControl>
|
101
src/Ryujinx/UI/Windows/XCITrimmerWindow.axaml.cs
Normal file
101
src/Ryujinx/UI/Windows/XCITrimmerWindow.axaml.cs
Normal file
@ -0,0 +1,101 @@
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Interactivity;
|
||||
using Avalonia.Styling;
|
||||
using FluentAvalonia.UI.Controls;
|
||||
using Ryujinx.Ava.Common.Locale;
|
||||
using Ryujinx.Ava.UI.ViewModels;
|
||||
using Ryujinx.UI.Common.Models;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Ryujinx.Ava.UI.Windows
|
||||
{
|
||||
public partial class XCITrimmerWindow : UserControl
|
||||
{
|
||||
public XCITrimmerViewModel ViewModel;
|
||||
|
||||
public XCITrimmerWindow()
|
||||
{
|
||||
DataContext = this;
|
||||
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
public XCITrimmerWindow(MainWindowViewModel mainWindowViewModel)
|
||||
{
|
||||
DataContext = ViewModel = new XCITrimmerViewModel(mainWindowViewModel);
|
||||
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
public static async Task Show(MainWindowViewModel mainWindowViewModel)
|
||||
{
|
||||
ContentDialog contentDialog = new()
|
||||
{
|
||||
PrimaryButtonText = "",
|
||||
SecondaryButtonText = "",
|
||||
CloseButtonText = "",
|
||||
Content = new XCITrimmerWindow(mainWindowViewModel),
|
||||
Title = string.Format(LocaleManager.Instance[LocaleKeys.XCITrimmerWindowTitle]),
|
||||
};
|
||||
|
||||
Style bottomBorder = new(x => x.OfType<Grid>().Name("DialogSpace").Child().OfType<Border>());
|
||||
bottomBorder.Setters.Add(new Setter(IsVisibleProperty, false));
|
||||
|
||||
contentDialog.Styles.Add(bottomBorder);
|
||||
|
||||
await contentDialog.ShowAsync();
|
||||
}
|
||||
|
||||
private void Trim(object sender, RoutedEventArgs e)
|
||||
{
|
||||
ViewModel.TrimSelected();
|
||||
}
|
||||
|
||||
private void Untrim(object sender, RoutedEventArgs e)
|
||||
{
|
||||
ViewModel.UntrimSelected();
|
||||
}
|
||||
|
||||
private void Close(object sender, RoutedEventArgs e)
|
||||
{
|
||||
((ContentDialog)Parent).Hide();
|
||||
}
|
||||
|
||||
private void Cancel(Object sender, RoutedEventArgs e)
|
||||
{
|
||||
ViewModel.Cancel = true;
|
||||
}
|
||||
|
||||
public void Sort_Checked(object sender, RoutedEventArgs args)
|
||||
{
|
||||
if (sender is RadioButton { Tag: string sortField })
|
||||
ViewModel.SortingField = Enum.Parse<XCITrimmerViewModel.SortField>(sortField);
|
||||
}
|
||||
|
||||
public void Order_Checked(object sender, RoutedEventArgs args)
|
||||
{
|
||||
if (sender is RadioButton { Tag: string sortOrder })
|
||||
ViewModel.SortingAscending = sortOrder is "Ascending";
|
||||
}
|
||||
|
||||
private void OnSelectionChanged(object sender, SelectionChangedEventArgs e)
|
||||
{
|
||||
foreach (var content in e.AddedItems)
|
||||
{
|
||||
if (content is XCITrimmerFileModel applicationData)
|
||||
{
|
||||
ViewModel.Select(applicationData);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var content in e.RemovedItems)
|
||||
{
|
||||
if (content is XCITrimmerFileModel applicationData)
|
||||
{
|
||||
ViewModel.Deselect(applicationData);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -32,6 +32,8 @@ namespace Ryujinx.Ava
|
||||
internal static class Updater
|
||||
{
|
||||
private const string GitHubApiUrl = "https://api.github.com";
|
||||
private const string LatestReleaseUrl = $"{GitHubApiUrl}/repos/{ReleaseInformation.ReleaseChannelOwner}/{ReleaseInformation.ReleaseChannelRepo}/releases/latest";
|
||||
|
||||
private static readonly GithubReleasesJsonSerializerContext _serializerContext = new(JsonHelper.GetDefaultSerializerOptions());
|
||||
|
||||
private static readonly string _homeDir = AppDomain.CurrentDomain.BaseDirectory;
|
||||
@ -46,9 +48,9 @@ namespace Ryujinx.Ava
|
||||
private static bool _updateSuccessful;
|
||||
private static bool _running;
|
||||
|
||||
private static readonly string[] _windowsDependencyDirs = Array.Empty<string>();
|
||||
private static readonly string[] _windowsDependencyDirs = [];
|
||||
|
||||
public static async Task BeginParse(Window mainWindow, bool showVersionUpToDate)
|
||||
public static async Task BeginUpdateAsync(this Window mainWindow, bool showVersionUpToDate = false)
|
||||
{
|
||||
if (_running)
|
||||
{
|
||||
@ -96,9 +98,8 @@ namespace Ryujinx.Ava
|
||||
try
|
||||
{
|
||||
using HttpClient jsonClient = ConstructHttpClient();
|
||||
|
||||
string buildInfoUrl = $"{GitHubApiUrl}/repos/{ReleaseInformation.ReleaseChannelOwner}/{ReleaseInformation.ReleaseChannelRepo}/releases/latest";
|
||||
string fetchedJson = await jsonClient.GetStringAsync(buildInfoUrl);
|
||||
|
||||
string fetchedJson = await jsonClient.GetStringAsync(LatestReleaseUrl);
|
||||
var fetched = JsonHelper.Deserialize(fetchedJson, _serializerContext.GithubReleasesJsonResponse);
|
||||
_buildVer = fetched.Name;
|
||||
|
||||
@ -159,7 +160,7 @@ namespace Ryujinx.Ava
|
||||
}
|
||||
catch
|
||||
{
|
||||
Logger.Error?.Print(LogClass.Application, "Failed to convert the received Ryujinx version from Github!");
|
||||
Logger.Error?.Print(LogClass.Application, $"Failed to convert the received {App.FullAppName} version from GitHub!");
|
||||
|
||||
await ContentDialogHelper.CreateWarningDialog(
|
||||
LocaleManager.Instance[LocaleKeys.DialogUpdaterConvertFailedGithubMessage],
|
||||
@ -636,7 +637,7 @@ namespace Ryujinx.Ava
|
||||
taskDialog.Hide();
|
||||
}
|
||||
|
||||
public static bool CanUpdate(bool showWarnings)
|
||||
public static bool CanUpdate(bool showWarnings = false)
|
||||
{
|
||||
#if !DISABLE_UPDATER
|
||||
if (!NetworkInterface.GetIsNetworkAvailable())
|
||||
|
Loading…
x
Reference in New Issue
Block a user