Use concrete types when possible for improved performance #492
4
.github/workflows/build.yml
vendored
4
.github/workflows/build.yml
vendored
@ -129,11 +129,11 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
global-json-file: global.json
|
global-json-file: global.json
|
||||||
|
|
||||||
- name: Setup LLVM 14
|
- name: Setup LLVM 17
|
||||||
run: |
|
run: |
|
||||||
wget https://apt.llvm.org/llvm.sh
|
wget https://apt.llvm.org/llvm.sh
|
||||||
chmod +x llvm.sh
|
chmod +x llvm.sh
|
||||||
sudo ./llvm.sh 14
|
sudo ./llvm.sh 17
|
||||||
|
|
||||||
- name: Install rcodesign
|
- name: Install rcodesign
|
||||||
run: |
|
run: |
|
||||||
|
81
.github/workflows/canary.yml
vendored
81
.github/workflows/canary.yml
vendored
@ -108,6 +108,7 @@ jobs:
|
|||||||
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_TARGET_RELEASE_CHANNEL_REPO\%\%/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }}/g;' src/Ryujinx.Common/ReleaseInformation.cs
|
||||||
sed -r --in-place 's/\%\%RYUJINX_TARGET_RELEASE_CHANNEL_SOURCE_REPO\%\%/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_SOURCE_REPO }}/g;' src/Ryujinx.Common/ReleaseInformation.cs
|
sed -r --in-place 's/\%\%RYUJINX_TARGET_RELEASE_CHANNEL_SOURCE_REPO\%\%/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_SOURCE_REPO }}/g;' src/Ryujinx.Common/ReleaseInformation.cs
|
||||||
sed -r --in-place 's/\%\%RYUJINX_CONFIG_FILE_NAME\%\%/Config\.json/g;' src/Ryujinx.Common/ReleaseInformation.cs
|
sed -r --in-place 's/\%\%RYUJINX_CONFIG_FILE_NAME\%\%/Config\.json/g;' src/Ryujinx.Common/ReleaseInformation.cs
|
||||||
|
sed -r --in-place '/^Name=Ryujinx$/s/Name=Ryujinx/Name=Ryujinx-Canary/' distribution/linux/Ryujinx.desktop
|
||||||
shell: bash
|
shell: bash
|
||||||
|
|
||||||
- name: Create output dir
|
- name: Create output dir
|
||||||
@ -115,71 +116,69 @@ jobs:
|
|||||||
|
|
||||||
- name: Publish
|
- name: Publish
|
||||||
run: |
|
run: |
|
||||||
dotnet publish -c Release -r "${{ matrix.platform.name }}" -o ./publish_ava/publish -p:Version="${{ steps.version_info.outputs.build_version }}" -p:SourceRevisionId="${{ steps.version_info.outputs.git_short_hash }}" -p:DebugType=embedded src/Ryujinx --self-contained
|
dotnet publish -c Release -r "${{ matrix.platform.name }}" -o ./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
|
||||||
|
|
||||||
- name: Packing Windows builds
|
- name: Packing Windows builds
|
||||||
if: matrix.platform.os == 'windows-latest'
|
if: matrix.platform.os == 'windows-latest'
|
||||||
run: |
|
run: |
|
||||||
pushd publish_ava
|
pushd publish
|
||||||
rm publish/libarmeilleure-jitsupport.dylib
|
rm libarmeilleure-jitsupport.dylib
|
||||||
7z a ../release_output/ryujinx-canary-${{ steps.version_info.outputs.build_version }}-${{ matrix.platform.zip_os_name }}.zip publish
|
7z a ../release_output/ryujinx-canary-${{ steps.version_info.outputs.build_version }}-${{ matrix.platform.zip_os_name }}.zip ../publish
|
||||||
popd
|
popd
|
||||||
shell: bash
|
shell: bash
|
||||||
|
|
||||||
- name: Packing Linux builds
|
- name: Packing Linux builds
|
||||||
if: matrix.platform.os == 'ubuntu-latest'
|
if: matrix.platform.os == 'ubuntu-latest'
|
||||||
run: |
|
run: |
|
||||||
pushd publish_ava
|
pushd publish
|
||||||
rm publish/libarmeilleure-jitsupport.dylib
|
rm libarmeilleure-jitsupport.dylib
|
||||||
chmod +x publish/Ryujinx.sh publish/Ryujinx
|
chmod +x Ryujinx.sh Ryujinx
|
||||||
tar -czvf ../release_output/ryujinx-canary-${{ steps.version_info.outputs.build_version }}-${{ matrix.platform.zip_os_name }}.tar.gz publish
|
tar -czvf ../release_output/ryujinx-canary-${{ steps.version_info.outputs.build_version }}-${{ matrix.platform.zip_os_name }}.tar.gz ../publish
|
||||||
popd
|
popd
|
||||||
shell: bash
|
shell: bash
|
||||||
|
|
||||||
#- name: Build AppImage (Linux)
|
- name: Build AppImage (Linux)
|
||||||
# if: matrix.platform.os == 'ubuntu-latest'
|
if: matrix.platform.os == 'ubuntu-latest'
|
||||||
# run: |
|
run: |
|
||||||
# BUILD_VERSION="${{ steps.version_info.outputs.build_version }}"
|
BUILD_VERSION="${{ steps.version_info.outputs.build_version }}"
|
||||||
# PLATFORM_NAME="${{ matrix.platform.name }}"
|
PLATFORM_NAME="${{ matrix.platform.name }}"
|
||||||
|
|
||||||
# sudo apt install -y zsync desktop-file-utils appstream
|
sudo apt install -y zsync desktop-file-utils appstream
|
||||||
|
|
||||||
# mkdir -p tools
|
mkdir -p tools
|
||||||
# export PATH="$PATH:$(readlink -f tools)"
|
export PATH="$PATH:$(readlink -f tools)"
|
||||||
|
|
||||||
# Setup appimagetool
|
# Setup appimagetool
|
||||||
# wget -q -O tools/appimagetool "https://github.com/AppImage/appimagetool/releases/download/continuous/appimagetool-x86_64.AppImage"
|
wget -q -O tools/appimagetool "https://github.com/AppImage/appimagetool/releases/download/continuous/appimagetool-x86_64.AppImage"
|
||||||
# chmod +x tools/appimagetool
|
chmod +x tools/appimagetool
|
||||||
# chmod +x distribution/linux/appimage/build-appimage.sh
|
chmod +x distribution/linux/appimage/build-appimage.sh
|
||||||
|
|
||||||
# Explicitly set $ARCH for appimagetool ($ARCH_NAME is for the file name)
|
# Explicitly set $ARCH for appimagetool ($ARCH_NAME is for the file name)
|
||||||
# if [ "$PLATFORM_NAME" = "linux-x64" ]; then
|
if [ "$PLATFORM_NAME" = "linux-x64" ]; then
|
||||||
# ARCH_NAME=x64
|
ARCH_NAME=x64
|
||||||
# export ARCH=x86_64
|
export ARCH=x86_64
|
||||||
# elif [ "$PLATFORM_NAME" = "linux-arm64" ]; then
|
elif [ "$PLATFORM_NAME" = "linux-arm64" ]; then
|
||||||
# ARCH_NAME=arm64
|
ARCH_NAME=arm64
|
||||||
# export ARCH=aarch64
|
export ARCH=aarch64
|
||||||
# else
|
else
|
||||||
# echo "Unexpected PLATFORM_NAME "$PLATFORM_NAME""
|
echo "Unexpected PLATFORM_NAME "$PLATFORM_NAME""
|
||||||
# exit 1
|
exit 1
|
||||||
# fi
|
fi
|
||||||
|
|
||||||
# export UFLAG="gh-releases-zsync|${{ github.repository_owner }}|${{ github.event.repository.name }}|latest|*-$ARCH_NAME.AppImage.zsync"
|
export UFLAG="gh-releases-zsync|${{ github.repository_owner }}|Canary-Releases|latest|*-$ARCH_NAME.AppImage.zsync"
|
||||||
# BUILDDIR=publish_ava OUTDIR=publish_ava_appimage distribution/linux/appimage/build-appimage.sh
|
BUILDDIR=publish OUTDIR=publish_appimage distribution/linux/appimage/build-appimage.sh
|
||||||
|
|
||||||
# Add to release output
|
pushd publish_appimage
|
||||||
# pushd publish_ava_appimage
|
mv Ryujinx.AppImage ../release_output/ryujinx-canary-$BUILD_VERSION-$ARCH_NAME.AppImage
|
||||||
# mv Ryujinx.AppImage ../release_output/ryujinx-$BUILD_VERSION-$ARCH_NAME.AppImage
|
mv Ryujinx.AppImage.zsync ../release_output/ryujinx-canary-$BUILD_VERSION-$ARCH_NAME.AppImage.zsync
|
||||||
# mv Ryujinx.AppImage.zsync ../release_output/ryujinx-$BUILD_VERSION-$ARCH_NAME.AppImage.zsync
|
popd
|
||||||
# popd
|
shell: bash
|
||||||
# shell: bash
|
|
||||||
|
|
||||||
- name: Pushing new release
|
- name: Pushing new release
|
||||||
uses: ncipollo/release-action@v1
|
uses: ncipollo/release-action@v1
|
||||||
with:
|
with:
|
||||||
name: ${{ steps.version_info.outputs.build_version }}
|
name: ${{ steps.version_info.outputs.build_version }}
|
||||||
artifacts: "release_output/*.tar.gz,release_output/*.zip"
|
artifacts: "release_output/*.tar.gz,release_output/*.zip,release_output/*AppImage*"
|
||||||
#artifacts: "release_output/*.tar.gz,release_output/*.zip,release_output/*AppImage*"
|
|
||||||
tag: ${{ steps.version_info.outputs.build_version }}
|
tag: ${{ steps.version_info.outputs.build_version }}
|
||||||
body: |
|
body: |
|
||||||
# Canary builds:
|
# Canary builds:
|
||||||
@ -211,11 +210,11 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
global-json-file: global.json
|
global-json-file: global.json
|
||||||
|
|
||||||
- name: Setup LLVM 15
|
- name: Setup LLVM 17
|
||||||
run: |
|
run: |
|
||||||
wget https://apt.llvm.org/llvm.sh
|
wget https://apt.llvm.org/llvm.sh
|
||||||
chmod +x llvm.sh
|
chmod +x llvm.sh
|
||||||
sudo ./llvm.sh 15
|
sudo ./llvm.sh 17
|
||||||
|
|
||||||
- name: Install rcodesign
|
- name: Install rcodesign
|
||||||
run: |
|
run: |
|
||||||
|
4
.github/workflows/nightly_pr_comment.yml
vendored
4
.github/workflows/nightly_pr_comment.yml
vendored
@ -37,10 +37,10 @@ jobs:
|
|||||||
if (!artifacts.length) {
|
if (!artifacts.length) {
|
||||||
return core.error(`No artifacts found`);
|
return core.error(`No artifacts found`);
|
||||||
}
|
}
|
||||||
let body = `*You need to be logged into GitHub to download these files.*\n\nDownload the artifacts for this pull request:\n`;
|
let body = `Download the artifacts for this pull request:\n`;
|
||||||
let hidden_debug_artifacts = `\n\n <details><summary>Only for Developers</summary>\n`;
|
let hidden_debug_artifacts = `\n\n <details><summary>Only for Developers</summary>\n`;
|
||||||
for (const art of artifacts) {
|
for (const art of artifacts) {
|
||||||
const url = `https://github.com/Ryubing/Ryujinx/actions/runs/${run_id}/artifacts/${art.id}`;
|
const url = `https://nightly.link/${owner}/${repo}/actions/artifacts/${art.id}.zip`;
|
||||||
if (art.name.includes('Debug')) {
|
if (art.name.includes('Debug')) {
|
||||||
hidden_debug_artifacts += `\n* [${art.name}](${url})`;
|
hidden_debug_artifacts += `\n* [${art.name}](${url})`;
|
||||||
} else {
|
} else {
|
||||||
|
32
.github/workflows/release.yml
vendored
32
.github/workflows/release.yml
vendored
@ -3,16 +3,6 @@ name: Release job
|
|||||||
on:
|
on:
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
inputs: {}
|
inputs: {}
|
||||||
push:
|
|
||||||
branches: [ release ]
|
|
||||||
paths-ignore:
|
|
||||||
- '.github/**'
|
|
||||||
- 'docs/**'
|
|
||||||
- 'assets/**'
|
|
||||||
- '*.yml'
|
|
||||||
- '*.json'
|
|
||||||
- '*.config'
|
|
||||||
- '*.md'
|
|
||||||
|
|
||||||
concurrency: release
|
concurrency: release
|
||||||
|
|
||||||
@ -122,6 +112,15 @@ jobs:
|
|||||||
popd
|
popd
|
||||||
shell: bash
|
shell: bash
|
||||||
|
|
||||||
|
- name: Packing Linux builds
|
||||||
|
if: matrix.platform.os == 'ubuntu-latest'
|
||||||
|
run: |
|
||||||
|
pushd publish
|
||||||
|
chmod +x Ryujinx.sh Ryujinx
|
||||||
|
tar -czvf ../release_output/ryujinx-${{ steps.version_info.outputs.build_version }}-${{ matrix.platform.zip_os_name }}.tar.gz ../publish
|
||||||
|
popd
|
||||||
|
shell: bash
|
||||||
|
|
||||||
- name: Build AppImage (Linux)
|
- name: Build AppImage (Linux)
|
||||||
if: matrix.platform.os == 'ubuntu-latest'
|
if: matrix.platform.os == 'ubuntu-latest'
|
||||||
run: |
|
run: |
|
||||||
@ -159,15 +158,6 @@ jobs:
|
|||||||
popd
|
popd
|
||||||
shell: bash
|
shell: bash
|
||||||
|
|
||||||
- name: Packing Linux builds
|
|
||||||
if: matrix.platform.os == 'ubuntu-latest'
|
|
||||||
run: |
|
|
||||||
pushd publish
|
|
||||||
chmod +x Ryujinx.sh Ryujinx
|
|
||||||
tar -czvf ../release_output/ryujinx-${{ steps.version_info.outputs.build_version }}-${{ matrix.platform.zip_os_name }}.tar.gz ../publish
|
|
||||||
popd
|
|
||||||
shell: bash
|
|
||||||
|
|
||||||
- name: Pushing new release
|
- name: Pushing new release
|
||||||
uses: ncipollo/release-action@v1
|
uses: ncipollo/release-action@v1
|
||||||
with:
|
with:
|
||||||
@ -201,11 +191,11 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
global-json-file: global.json
|
global-json-file: global.json
|
||||||
|
|
||||||
- name: Setup LLVM 15
|
- name: Setup LLVM 17
|
||||||
run: |
|
run: |
|
||||||
wget https://apt.llvm.org/llvm.sh
|
wget https://apt.llvm.org/llvm.sh
|
||||||
chmod +x llvm.sh
|
chmod +x llvm.sh
|
||||||
sudo ./llvm.sh 15
|
sudo ./llvm.sh 17
|
||||||
|
|
||||||
- name: Install rcodesign
|
- name: Install rcodesign
|
||||||
run: |
|
run: |
|
||||||
|
@ -3,13 +3,13 @@
|
|||||||
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
|
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageVersion Include="Avalonia" Version="11.0.10" />
|
<PackageVersion Include="Avalonia" Version="11.0.13" />
|
||||||
<PackageVersion Include="Avalonia.Controls.DataGrid" Version="11.0.10" />
|
<PackageVersion Include="Avalonia.Controls.DataGrid" Version="11.0.13" />
|
||||||
<PackageVersion Include="Avalonia.Desktop" Version="11.0.10" />
|
<PackageVersion Include="Avalonia.Desktop" Version="11.0.13" />
|
||||||
<PackageVersion Include="Avalonia.Diagnostics" Version="11.0.10" />
|
<PackageVersion Include="Avalonia.Diagnostics" Version="11.0.13" />
|
||||||
<PackageVersion Include="Avalonia.Markup.Xaml.Loader" Version="11.0.10" />
|
<PackageVersion Include="Avalonia.Markup.Xaml.Loader" Version="11.0.13" />
|
||||||
<PackageVersion Include="Avalonia.Svg" Version="11.0.0.18" />
|
<PackageVersion Include="Avalonia.Svg" Version="11.0.0.19" />
|
||||||
<PackageVersion Include="Avalonia.Svg.Skia" Version="11.0.0.18" />
|
<PackageVersion Include="Avalonia.Svg.Skia" Version="11.0.0.19" />
|
||||||
<PackageVersion Include="Microsoft.Build.Framework" Version="17.11.4" />
|
<PackageVersion Include="Microsoft.Build.Framework" Version="17.11.4" />
|
||||||
<PackageVersion Include="Microsoft.Build.Utilities.Core" Version="17.12.6" />
|
<PackageVersion Include="Microsoft.Build.Utilities.Core" Version="17.12.6" />
|
||||||
<PackageVersion Include="Newtonsoft.Json" Version="13.0.3" />
|
<PackageVersion Include="Newtonsoft.Json" Version="13.0.3" />
|
||||||
@ -18,7 +18,7 @@
|
|||||||
<PackageVersion Include="Projektanker.Icons.Avalonia.MaterialDesign" Version="9.4.0"/>
|
<PackageVersion Include="Projektanker.Icons.Avalonia.MaterialDesign" Version="9.4.0"/>
|
||||||
<PackageVersion Include="CommandLineParser" Version="2.9.1" />
|
<PackageVersion Include="CommandLineParser" Version="2.9.1" />
|
||||||
<PackageVersion Include="CommunityToolkit.Mvvm" Version="8.4.0"/>
|
<PackageVersion Include="CommunityToolkit.Mvvm" Version="8.4.0"/>
|
||||||
<PackageVersion Include="Concentus" Version="2.2.0" />
|
<PackageVersion Include="Concentus" Version="2.2.2" />
|
||||||
<PackageVersion Include="DiscordRichPresence" Version="1.2.1.24" />
|
<PackageVersion Include="DiscordRichPresence" Version="1.2.1.24" />
|
||||||
<PackageVersion Include="DynamicData" Version="9.0.4" />
|
<PackageVersion Include="DynamicData" Version="9.0.4" />
|
||||||
<PackageVersion Include="FluentAvaloniaUI" Version="2.0.5" />
|
<PackageVersion Include="FluentAvaloniaUI" Version="2.0.5" />
|
||||||
@ -26,7 +26,7 @@
|
|||||||
<PackageVersion Include="LibHac" Version="0.19.0" />
|
<PackageVersion Include="LibHac" Version="0.19.0" />
|
||||||
<PackageVersion Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.4" />
|
<PackageVersion Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.4" />
|
||||||
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp" Version="4.9.2" />
|
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp" Version="4.9.2" />
|
||||||
<PackageVersion Include="Microsoft.IdentityModel.JsonWebTokens" Version="8.1.2" />
|
<PackageVersion Include="Microsoft.IdentityModel.JsonWebTokens" Version="8.3.0" />
|
||||||
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.9.0" />
|
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.9.0" />
|
||||||
<PackageVersion Include="Microsoft.IO.RecyclableMemoryStream" Version="3.0.1" />
|
<PackageVersion Include="Microsoft.IO.RecyclableMemoryStream" Version="3.0.1" />
|
||||||
<PackageVersion Include="MsgPack.Cli" Version="1.0.1" />
|
<PackageVersion Include="MsgPack.Cli" Version="1.0.1" />
|
||||||
@ -42,16 +42,17 @@
|
|||||||
<PackageVersion Include="Ryujinx.Graphics.Nvdec.Dependencies" Version="5.0.3-build14" />
|
<PackageVersion Include="Ryujinx.Graphics.Nvdec.Dependencies" Version="5.0.3-build14" />
|
||||||
<PackageVersion Include="Ryujinx.Graphics.Vulkan.Dependencies.MoltenVK" Version="1.2.0" />
|
<PackageVersion Include="Ryujinx.Graphics.Vulkan.Dependencies.MoltenVK" Version="1.2.0" />
|
||||||
<PackageVersion Include="Ryujinx.SDL2-CS" Version="2.30.0-build32" />
|
<PackageVersion Include="Ryujinx.SDL2-CS" Version="2.30.0-build32" />
|
||||||
<PackageVersion Include="Gommon" Version="2.7.0.1" />
|
<PackageVersion Include="Gommon" Version="2.7.0.2" />
|
||||||
<PackageVersion Include="securifybv.ShellLink" Version="0.1.0" />
|
<PackageVersion Include="securifybv.ShellLink" Version="0.1.0" />
|
||||||
|
<PackageVersion Include="Sep" Version="0.6.0" />
|
||||||
<PackageVersion Include="shaderc.net" Version="0.1.0" />
|
<PackageVersion Include="shaderc.net" Version="0.1.0" />
|
||||||
<PackageVersion Include="SharpMetal" Version="1.0.0-preview21" />
|
<PackageVersion Include="SharpMetal" Version="1.0.0-preview21" />
|
||||||
<PackageVersion Include="SharpZipLib" Version="1.4.2" />
|
<PackageVersion Include="SharpZipLib" Version="1.4.2" />
|
||||||
<PackageVersion Include="Silk.NET.Vulkan" Version="2.21.0" />
|
<PackageVersion Include="Silk.NET.Vulkan" Version="2.22.0" />
|
||||||
<PackageVersion Include="Silk.NET.Vulkan.Extensions.EXT" Version="2.21.0" />
|
<PackageVersion Include="Silk.NET.Vulkan.Extensions.EXT" Version="2.22.0" />
|
||||||
<PackageVersion Include="Silk.NET.Vulkan.Extensions.KHR" Version="2.21.0" />
|
<PackageVersion Include="Silk.NET.Vulkan.Extensions.KHR" Version="2.22.0" />
|
||||||
<PackageVersion Include="SkiaSharp" Version="2.88.7" />
|
<PackageVersion Include="SkiaSharp" Version="2.88.9" />
|
||||||
<PackageVersion Include="SkiaSharp.NativeAssets.Linux" Version="2.88.7" />
|
<PackageVersion Include="SkiaSharp.NativeAssets.Linux" Version="2.88.9" />
|
||||||
<PackageVersion Include="SPB" Version="0.0.4-build32" />
|
<PackageVersion Include="SPB" Version="0.0.4-build32" />
|
||||||
<PackageVersion Include="System.IO.Hashing" Version="9.0.0" />
|
<PackageVersion Include="System.IO.Hashing" Version="9.0.0" />
|
||||||
<PackageVersion Include="System.Management" Version="9.0.0" />
|
<PackageVersion Include="System.Management" Version="9.0.0" />
|
||||||
|
@ -6,7 +6,7 @@ cd "$ROOTDIR"
|
|||||||
|
|
||||||
BUILDDIR=${BUILDDIR:-publish}
|
BUILDDIR=${BUILDDIR:-publish}
|
||||||
OUTDIR=${OUTDIR:-publish_appimage}
|
OUTDIR=${OUTDIR:-publish_appimage}
|
||||||
UFLAG=${UFLAG:-"gh-releases-zsync|GreemDev|ryujinx|latest|*-x64.AppImage.zsync"}
|
UFLAG=${UFLAG:-"gh-releases-zsync|Ryubing|ryujinx|latest|*-x64.AppImage.zsync"}
|
||||||
|
|
||||||
rm -rf AppDir
|
rm -rf AppDir
|
||||||
mkdir -p AppDir/usr/bin
|
mkdir -p AppDir/usr/bin
|
||||||
|
@ -19,7 +19,7 @@ if platform.system() == "Darwin":
|
|||||||
else:
|
else:
|
||||||
OTOOL = shutil.which("llvm-otool")
|
OTOOL = shutil.which("llvm-otool")
|
||||||
if OTOOL is None:
|
if OTOOL is None:
|
||||||
for llvm_ver in [15, 14, 13]:
|
for llvm_ver in [17, 16, 15, 14, 13]:
|
||||||
otool_path = shutil.which(f"llvm-otool-{llvm_ver}")
|
otool_path = shutil.which(f"llvm-otool-{llvm_ver}")
|
||||||
if otool_path is not None:
|
if otool_path is not None:
|
||||||
OTOOL = otool_path
|
OTOOL = otool_path
|
||||||
|
@ -26,7 +26,7 @@ else:
|
|||||||
LIPO = shutil.which("llvm-lipo")
|
LIPO = shutil.which("llvm-lipo")
|
||||||
|
|
||||||
if LIPO is None:
|
if LIPO is None:
|
||||||
for llvm_ver in [15, 14, 13]:
|
for llvm_ver in [17, 16, 15, 14, 13]:
|
||||||
lipo_path = shutil.which(f"llvm-lipo-{llvm_ver}")
|
lipo_path = shutil.which(f"llvm-lipo-{llvm_ver}")
|
||||||
if lipo_path is not None:
|
if lipo_path is not None:
|
||||||
LIPO = lipo_path
|
LIPO = lipo_path
|
||||||
|
@ -67,11 +67,11 @@ python3 "$BASE_DIR/distribution/macos/construct_universal_dylib.py" "$ARM64_APP_
|
|||||||
|
|
||||||
if ! [ -x "$(command -v lipo)" ];
|
if ! [ -x "$(command -v lipo)" ];
|
||||||
then
|
then
|
||||||
if ! [ -x "$(command -v llvm-lipo-14)" ];
|
if ! [ -x "$(command -v llvm-lipo-17)" ];
|
||||||
then
|
then
|
||||||
LIPO=llvm-lipo
|
LIPO=llvm-lipo
|
||||||
else
|
else
|
||||||
LIPO=llvm-lipo-14
|
LIPO=llvm-lipo-17
|
||||||
fi
|
fi
|
||||||
else
|
else
|
||||||
LIPO=lipo
|
LIPO=lipo
|
||||||
|
@ -62,11 +62,11 @@ python3 "$BASE_DIR/distribution/macos/construct_universal_dylib.py" "$ARM64_OUTP
|
|||||||
|
|
||||||
if ! [ -x "$(command -v lipo)" ];
|
if ! [ -x "$(command -v lipo)" ];
|
||||||
then
|
then
|
||||||
if ! [ -x "$(command -v llvm-lipo-14)" ];
|
if ! [ -x "$(command -v llvm-lipo-17)" ];
|
||||||
then
|
then
|
||||||
LIPO=llvm-lipo
|
LIPO=llvm-lipo
|
||||||
else
|
else
|
||||||
LIPO=llvm-lipo-14
|
LIPO=llvm-lipo-17
|
||||||
fi
|
fi
|
||||||
else
|
else
|
||||||
LIPO=lipo
|
LIPO=lipo
|
||||||
|
3424
docs/compatibility.csv
Normal file
3424
docs/compatibility.csv
Normal file
File diff suppressed because it is too large
Load Diff
10
src/Ryujinx.Common/RyujinxException.cs
Normal file
10
src/Ryujinx.Common/RyujinxException.cs
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Ryujinx.Common
|
||||||
|
{
|
||||||
|
public class RyujinxException : Exception
|
||||||
|
{
|
||||||
|
public RyujinxException(string message) : base(message)
|
||||||
|
{ }
|
||||||
|
}
|
||||||
|
}
|
@ -168,14 +168,16 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
return BinarySearch(list, offset, size) >= 0;
|
return BinarySearch(list, offset, size) >= 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public readonly IEnumerable<Range> FindOverlaps(int offset, int size)
|
public readonly List<Range> FindOverlaps(int offset, int size)
|
||||||
{
|
{
|
||||||
var list = _ranges;
|
var list = _ranges;
|
||||||
if (list == null)
|
if (list == null)
|
||||||
{
|
{
|
||||||
yield break;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
List<Range> result = null;
|
||||||
|
|
||||||
int index = BinarySearch(list, offset, size);
|
int index = BinarySearch(list, offset, size);
|
||||||
|
|
||||||
if (index >= 0)
|
if (index >= 0)
|
||||||
@ -187,10 +189,12 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
|
|
||||||
do
|
do
|
||||||
{
|
{
|
||||||
yield return list[index++];
|
(result ??= []).Add(list[index++]);
|
||||||
}
|
}
|
||||||
while (index < list.Count && list[index].OverlapsWith(offset, size));
|
while (index < list.Count && list[index].OverlapsWith(offset, size));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static int BinarySearch(List<Range> list, int offset, int size)
|
private static int BinarySearch(List<Range> list, int offset, int size)
|
||||||
|
@ -92,7 +92,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
DriverId.MesaDozen => "Dozen",
|
DriverId.MesaDozen => "Dozen",
|
||||||
DriverId.MesaNvk => "NVK",
|
DriverId.MesaNvk => "NVK",
|
||||||
DriverId.ImaginationOpenSourceMesa => "Imagination (Open)",
|
DriverId.ImaginationOpenSourceMesa => "Imagination (Open)",
|
||||||
DriverId.MesaAgxv => "Honeykrisp",
|
DriverId.MesaHoneykrisp => "Honeykrisp",
|
||||||
_ => id.ToString(),
|
_ => id.ToString(),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -284,7 +284,7 @@ namespace Ryujinx.HLE.HOS
|
|||||||
ProcessCreationInfo creationInfo = new("Service", 1, 0, 0x8000000, 1, Flags, 0, 0);
|
ProcessCreationInfo creationInfo = new("Service", 1, 0, 0x8000000, 1, Flags, 0, 0);
|
||||||
|
|
||||||
uint[] defaultCapabilities = {
|
uint[] defaultCapabilities = {
|
||||||
0x030363F7,
|
(((uint)KScheduler.CpuCoresCount - 1) << 24) + (((uint)KScheduler.CpuCoresCount - 1) << 16) + 0x63F7u,
|
||||||
0x1FFFFFCF,
|
0x1FFFFFCF,
|
||||||
0x207FFFEF,
|
0x207FFFEF,
|
||||||
0x47E0060F,
|
0x47E0060F,
|
||||||
|
@ -63,6 +63,7 @@ namespace Ryujinx.HLE.HOS.Kernel
|
|||||||
TickSource = tickSource;
|
TickSource = tickSource;
|
||||||
Device = device;
|
Device = device;
|
||||||
Memory = memory;
|
Memory = memory;
|
||||||
|
KScheduler.CpuCoresCount = device.CpuCoresCount;
|
||||||
|
|
||||||
Running = true;
|
Running = true;
|
||||||
|
|
||||||
|
@ -37,7 +37,7 @@ namespace Ryujinx.HLE.HOS.Kernel
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
process.DefaultCpuCore = 3;
|
process.DefaultCpuCore = KScheduler.CpuCoresCount - 1;
|
||||||
|
|
||||||
context.Processes.TryAdd(process.Pid, process);
|
context.Processes.TryAdd(process.Pid, process);
|
||||||
|
|
||||||
|
@ -277,7 +277,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
result = Capabilities.InitializeForUser(capabilities, MemoryManager);
|
result = Capabilities.InitializeForUser(capabilities, MemoryManager, IsApplication);
|
||||||
|
|
||||||
if (result != Result.Success)
|
if (result != Result.Success)
|
||||||
{
|
{
|
||||||
|
@ -35,15 +35,15 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
|
|||||||
DebuggingFlags &= ~3u;
|
DebuggingFlags &= ~3u;
|
||||||
KernelReleaseVersion = KProcess.KernelVersionPacked;
|
KernelReleaseVersion = KProcess.KernelVersionPacked;
|
||||||
|
|
||||||
return Parse(capabilities, memoryManager);
|
return Parse(capabilities, memoryManager, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Result InitializeForUser(ReadOnlySpan<uint> capabilities, KPageTableBase memoryManager)
|
public Result InitializeForUser(ReadOnlySpan<uint> capabilities, KPageTableBase memoryManager, bool isApplication)
|
||||||
{
|
{
|
||||||
return Parse(capabilities, memoryManager);
|
return Parse(capabilities, memoryManager, isApplication);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Result Parse(ReadOnlySpan<uint> capabilities, KPageTableBase memoryManager)
|
private Result Parse(ReadOnlySpan<uint> capabilities, KPageTableBase memoryManager, bool isApplication)
|
||||||
{
|
{
|
||||||
int mask0 = 0;
|
int mask0 = 0;
|
||||||
int mask1 = 0;
|
int mask1 = 0;
|
||||||
@ -54,7 +54,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
|
|||||||
|
|
||||||
if (cap.GetCapabilityType() != CapabilityType.MapRange)
|
if (cap.GetCapabilityType() != CapabilityType.MapRange)
|
||||||
{
|
{
|
||||||
Result result = ParseCapability(cap, ref mask0, ref mask1, memoryManager);
|
Result result = ParseCapability(cap, ref mask0, ref mask1, memoryManager, isApplication);
|
||||||
|
|
||||||
if (result != Result.Success)
|
if (result != Result.Success)
|
||||||
{
|
{
|
||||||
@ -120,7 +120,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
|
|||||||
return Result.Success;
|
return Result.Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Result ParseCapability(uint cap, ref int mask0, ref int mask1, KPageTableBase memoryManager)
|
private Result ParseCapability(uint cap, ref int mask0, ref int mask1, KPageTableBase memoryManager, bool isApplication)
|
||||||
{
|
{
|
||||||
CapabilityType code = cap.GetCapabilityType();
|
CapabilityType code = cap.GetCapabilityType();
|
||||||
|
|
||||||
@ -176,6 +176,9 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
|
|||||||
AllowedCpuCoresMask = GetMaskFromMinMax(lowestCpuCore, highestCpuCore);
|
AllowedCpuCoresMask = GetMaskFromMinMax(lowestCpuCore, highestCpuCore);
|
||||||
AllowedThreadPriosMask = GetMaskFromMinMax(lowestThreadPrio, highestThreadPrio);
|
AllowedThreadPriosMask = GetMaskFromMinMax(lowestThreadPrio, highestThreadPrio);
|
||||||
|
|
||||||
|
if (isApplication)
|
||||||
|
Ryujinx.Common.Logging.Logger.Info?.Print(Ryujinx.Common.Logging.LogClass.Application, $"Application requested cores with index range {lowestCpuCore} to {highestCpuCore}");
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2683,7 +2683,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
|||||||
return KernelResult.InvalidCombination;
|
return KernelResult.InvalidCombination;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((uint)preferredCore > 3)
|
if ((uint)preferredCore > KScheduler.CpuCoresCount - 1)
|
||||||
{
|
{
|
||||||
if ((preferredCore | 2) != -1)
|
if ((preferredCore | 2) != -1)
|
||||||
{
|
{
|
||||||
|
@ -9,13 +9,11 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
|
|||||||
partial class KScheduler : IDisposable
|
partial class KScheduler : IDisposable
|
||||||
{
|
{
|
||||||
public const int PrioritiesCount = 64;
|
public const int PrioritiesCount = 64;
|
||||||
public const int CpuCoresCount = 4;
|
public static int CpuCoresCount;
|
||||||
|
|
||||||
private const int RoundRobinTimeQuantumMs = 10;
|
private const int RoundRobinTimeQuantumMs = 10;
|
||||||
|
|
||||||
private static readonly int[] _preemptionPriorities = { 59, 59, 59, 63 };
|
private static int[] _srcCoresHighestPrioThreads;
|
||||||
|
|
||||||
private static readonly int[] _srcCoresHighestPrioThreads = new int[CpuCoresCount];
|
|
||||||
|
|
||||||
private readonly KernelContext _context;
|
private readonly KernelContext _context;
|
||||||
private readonly int _coreId;
|
private readonly int _coreId;
|
||||||
@ -47,6 +45,16 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
|
|||||||
_coreId = coreId;
|
_coreId = coreId;
|
||||||
|
|
||||||
_currentThread = null;
|
_currentThread = null;
|
||||||
|
|
||||||
|
if (_srcCoresHighestPrioThreads == null)
|
||||||
|
{
|
||||||
|
_srcCoresHighestPrioThreads = new int[CpuCoresCount];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int PreemptionPriorities(int index)
|
||||||
|
{
|
||||||
|
return index == CpuCoresCount - 1 ? 63 : 59;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ulong SelectThreads(KernelContext context)
|
public static ulong SelectThreads(KernelContext context)
|
||||||
@ -437,7 +445,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
|
|||||||
|
|
||||||
for (int core = 0; core < CpuCoresCount; core++)
|
for (int core = 0; core < CpuCoresCount; core++)
|
||||||
{
|
{
|
||||||
RotateScheduledQueue(context, core, _preemptionPriorities[core]);
|
RotateScheduledQueue(context, core, PreemptionPriorities(core));
|
||||||
}
|
}
|
||||||
|
|
||||||
context.CriticalSection.Leave();
|
context.CriticalSection.Leave();
|
||||||
|
@ -703,6 +703,18 @@ namespace Ryujinx.HLE.HOS.Services.Hid
|
|||||||
return ResultCode.Success;
|
return ResultCode.Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[CommandCmif(92)]
|
||||||
|
// SetGestureOutputRanges(pid, ushort Unknown0)
|
||||||
|
public ResultCode SetGestureOutputRanges(ServiceCtx context)
|
||||||
|
{
|
||||||
|
ulong pid = context.Request.HandleDesc.PId;
|
||||||
|
ushort unknown0 = context.RequestData.ReadUInt16();
|
||||||
|
|
||||||
|
Logger.Stub?.PrintStub(LogClass.ServiceHid, new { pid, unknown0 });
|
||||||
|
|
||||||
|
return ResultCode.Success;
|
||||||
|
}
|
||||||
|
|
||||||
[CommandCmif(100)]
|
[CommandCmif(100)]
|
||||||
// SetSupportedNpadStyleSet(pid, nn::applet::AppletResourceUserId, nn::hid::NpadStyleTag)
|
// SetSupportedNpadStyleSet(pid, nn::applet::AppletResourceUserId, nn::hid::NpadStyleTag)
|
||||||
public ResultCode SetSupportedNpadStyleSet(ServiceCtx context)
|
public ResultCode SetSupportedNpadStyleSet(ServiceCtx context)
|
||||||
|
@ -25,14 +25,14 @@ namespace Ryujinx.HLE.HOS.Services
|
|||||||
// not large enough.
|
// not large enough.
|
||||||
private const int PointerBufferSize = 0x8000;
|
private const int PointerBufferSize = 0x8000;
|
||||||
|
|
||||||
private readonly static uint[] _defaultCapabilities = {
|
private static uint[] _defaultCapabilities => [
|
||||||
0x030363F7,
|
(((uint)KScheduler.CpuCoresCount - 1) << 24) + (((uint)KScheduler.CpuCoresCount - 1) << 16) + 0x63F7u,
|
||||||
0x1FFFFFCF,
|
0x1FFFFFCF,
|
||||||
0x207FFFEF,
|
0x207FFFEF,
|
||||||
0x47E0060F,
|
0x47E0060F,
|
||||||
0x0048BFFF,
|
0x0048BFFF,
|
||||||
0x01007FFF,
|
0x01007FFF,
|
||||||
};
|
];
|
||||||
|
|
||||||
// The amount of time Dispose() will wait to Join() the thread executing the ServerLoop()
|
// The amount of time Dispose() will wait to Join() the thread executing the ServerLoop()
|
||||||
private static readonly TimeSpan _threadJoinTimeout = TimeSpan.FromSeconds(3);
|
private static readonly TimeSpan _threadJoinTimeout = TimeSpan.FromSeconds(3);
|
||||||
|
@ -26,7 +26,17 @@ namespace Ryujinx.HLE.Loaders.Processes
|
|||||||
|
|
||||||
private ulong _latestPid;
|
private ulong _latestPid;
|
||||||
|
|
||||||
public ProcessResult ActiveApplication => _processesByPid[_latestPid];
|
public ProcessResult ActiveApplication
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (!_processesByPid.TryGetValue(_latestPid, out ProcessResult value))
|
||||||
|
throw new RyujinxException(
|
||||||
|
$"The HLE Process map did not have a process with ID {_latestPid}. Are you missing firmware?");
|
||||||
|
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public ProcessLoader(Switch device)
|
public ProcessLoader(Switch device)
|
||||||
{
|
{
|
||||||
|
@ -32,6 +32,8 @@ namespace Ryujinx.HLE
|
|||||||
public TamperMachine TamperMachine { get; }
|
public TamperMachine TamperMachine { get; }
|
||||||
public IHostUIHandler UIHandler { get; }
|
public IHostUIHandler UIHandler { get; }
|
||||||
|
|
||||||
|
public int CpuCoresCount = 4; //Switch 1 has 4 cores
|
||||||
|
|
||||||
public VSyncMode VSyncMode { get; set; } = VSyncMode.Switch;
|
public VSyncMode VSyncMode { get; set; } = VSyncMode.Switch;
|
||||||
public bool CustomVSyncIntervalEnabled { get; set; } = false;
|
public bool CustomVSyncIntervalEnabled { get; set; } = false;
|
||||||
public int CustomVSyncInterval { get; set; }
|
public int CustomVSyncInterval { get; set; }
|
||||||
|
@ -1,36 +0,0 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
|
||||||
|
|
||||||
<PropertyGroup>
|
|
||||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
|
||||||
<DefaultItemExcludes>$(DefaultItemExcludes);._*</DefaultItemExcludes>
|
|
||||||
</PropertyGroup>
|
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<None Remove="Resources\Controller_JoyConLeft.svg" />
|
|
||||||
<None Remove="Resources\Controller_JoyConPair.svg" />
|
|
||||||
<None Remove="Resources\Controller_JoyConRight.svg" />
|
|
||||||
<None Remove="Resources\Controller_ProCon.svg" />
|
|
||||||
<None Remove="Resources\Icon_NCA.png" />
|
|
||||||
<None Remove="Resources\Icon_NRO.png" />
|
|
||||||
<None Remove="Resources\Icon_NSO.png" />
|
|
||||||
<None Remove="Resources\Icon_NSP.png" />
|
|
||||||
<None Remove="Resources\Icon_XCI.png" />
|
|
||||||
<None Remove="Resources\Logo_Amiibo.png" />
|
|
||||||
<None Remove="Resources\Logo_Discord.png" />
|
|
||||||
<None Remove="Resources\Logo_GitHub.png" />
|
|
||||||
<None Remove="Resources\Logo_Ryujinx.png" />
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<PackageReference Include="DiscordRichPresence" />
|
|
||||||
<PackageReference Include="DynamicData" />
|
|
||||||
<PackageReference Include="securifybv.ShellLink" />
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<ProjectReference Include="..\Ryujinx.Common\Ryujinx.Common.csproj" />
|
|
||||||
<ProjectReference Include="..\Ryujinx.HLE\Ryujinx.HLE.csproj" />
|
|
||||||
<ProjectReference Include="..\Ryujinx.Graphics.Vulkan\Ryujinx.Graphics.Vulkan.csproj" />
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
</Project>
|
|
@ -19,7 +19,7 @@ namespace Ryujinx.UI.LocaleGenerator
|
|||||||
|
|
||||||
StringBuilder enumSourceBuilder = new();
|
StringBuilder enumSourceBuilder = new();
|
||||||
enumSourceBuilder.AppendLine("namespace Ryujinx.Ava.Common.Locale;");
|
enumSourceBuilder.AppendLine("namespace Ryujinx.Ava.Common.Locale;");
|
||||||
enumSourceBuilder.AppendLine("internal enum LocaleKeys");
|
enumSourceBuilder.AppendLine("public enum LocaleKeys");
|
||||||
enumSourceBuilder.AppendLine("{");
|
enumSourceBuilder.AppendLine("{");
|
||||||
foreach (var line in lines)
|
foreach (var line in lines)
|
||||||
{
|
{
|
||||||
|
10
src/Ryujinx/Assets/Fonts/Mono/AUTHORS.txt
Normal file
10
src/Ryujinx/Assets/Fonts/Mono/AUTHORS.txt
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
# This is the official list of project authors for copyright purposes.
|
||||||
|
# This file is distinct from the CONTRIBUTORS.txt file.
|
||||||
|
# See the latter for an explanation.
|
||||||
|
#
|
||||||
|
# Names should be added to this file as:
|
||||||
|
# Name or Organization <email address>
|
||||||
|
|
||||||
|
JetBrains <>
|
||||||
|
Philipp Nurullin <philipp.nurullin@jetbrains.com>
|
||||||
|
Konstantin Bulenkov <kb@jetbrains.com>
|
BIN
src/Ryujinx/Assets/Fonts/Mono/JetBrainsMonoNL-Bold.ttf
Normal file
BIN
src/Ryujinx/Assets/Fonts/Mono/JetBrainsMonoNL-Bold.ttf
Normal file
Binary file not shown.
BIN
src/Ryujinx/Assets/Fonts/Mono/JetBrainsMonoNL-BoldItalic.ttf
Normal file
BIN
src/Ryujinx/Assets/Fonts/Mono/JetBrainsMonoNL-BoldItalic.ttf
Normal file
Binary file not shown.
BIN
src/Ryujinx/Assets/Fonts/Mono/JetBrainsMonoNL-Italic.ttf
Normal file
BIN
src/Ryujinx/Assets/Fonts/Mono/JetBrainsMonoNL-Italic.ttf
Normal file
Binary file not shown.
BIN
src/Ryujinx/Assets/Fonts/Mono/JetBrainsMonoNL-Regular.ttf
Normal file
BIN
src/Ryujinx/Assets/Fonts/Mono/JetBrainsMonoNL-Regular.ttf
Normal file
Binary file not shown.
93
src/Ryujinx/Assets/Fonts/Mono/OFL.txt
Normal file
93
src/Ryujinx/Assets/Fonts/Mono/OFL.txt
Normal file
@ -0,0 +1,93 @@
|
|||||||
|
Copyright 2020 The JetBrains Mono Project Authors (https://github.com/JetBrains/JetBrainsMono)
|
||||||
|
|
||||||
|
This Font Software is licensed under the SIL Open Font License, Version 1.1.
|
||||||
|
This license is copied below, and is also available with a FAQ at:
|
||||||
|
https://scripts.sil.org/OFL
|
||||||
|
|
||||||
|
|
||||||
|
-----------------------------------------------------------
|
||||||
|
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
|
||||||
|
-----------------------------------------------------------
|
||||||
|
|
||||||
|
PREAMBLE
|
||||||
|
The goals of the Open Font License (OFL) are to stimulate worldwide
|
||||||
|
development of collaborative font projects, to support the font creation
|
||||||
|
efforts of academic and linguistic communities, and to provide a free and
|
||||||
|
open framework in which fonts may be shared and improved in partnership
|
||||||
|
with others.
|
||||||
|
|
||||||
|
The OFL allows the licensed fonts to be used, studied, modified and
|
||||||
|
redistributed freely as long as they are not sold by themselves. The
|
||||||
|
fonts, including any derivative works, can be bundled, embedded,
|
||||||
|
redistributed and/or sold with any software provided that any reserved
|
||||||
|
names are not used by derivative works. The fonts and derivatives,
|
||||||
|
however, cannot be released under any other type of license. The
|
||||||
|
requirement for fonts to remain under this license does not apply
|
||||||
|
to any document created using the fonts or their derivatives.
|
||||||
|
|
||||||
|
DEFINITIONS
|
||||||
|
"Font Software" refers to the set of files released by the Copyright
|
||||||
|
Holder(s) under this license and clearly marked as such. This may
|
||||||
|
include source files, build scripts and documentation.
|
||||||
|
|
||||||
|
"Reserved Font Name" refers to any names specified as such after the
|
||||||
|
copyright statement(s).
|
||||||
|
|
||||||
|
"Original Version" refers to the collection of Font Software components as
|
||||||
|
distributed by the Copyright Holder(s).
|
||||||
|
|
||||||
|
"Modified Version" refers to any derivative made by adding to, deleting,
|
||||||
|
or substituting -- in part or in whole -- any of the components of the
|
||||||
|
Original Version, by changing formats or by porting the Font Software to a
|
||||||
|
new environment.
|
||||||
|
|
||||||
|
"Author" refers to any designer, engineer, programmer, technical
|
||||||
|
writer or other person who contributed to the Font Software.
|
||||||
|
|
||||||
|
PERMISSION & CONDITIONS
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
a copy of the Font Software, to use, study, copy, merge, embed, modify,
|
||||||
|
redistribute, and sell modified and unmodified copies of the Font
|
||||||
|
Software, subject to the following conditions:
|
||||||
|
|
||||||
|
1) Neither the Font Software nor any of its individual components,
|
||||||
|
in Original or Modified Versions, may be sold by itself.
|
||||||
|
|
||||||
|
2) Original or Modified Versions of the Font Software may be bundled,
|
||||||
|
redistributed and/or sold with any software, provided that each copy
|
||||||
|
contains the above copyright notice and this license. These can be
|
||||||
|
included either as stand-alone text files, human-readable headers or
|
||||||
|
in the appropriate machine-readable metadata fields within text or
|
||||||
|
binary files as long as those fields can be easily viewed by the user.
|
||||||
|
|
||||||
|
3) No Modified Version of the Font Software may use the Reserved Font
|
||||||
|
Name(s) unless explicit written permission is granted by the corresponding
|
||||||
|
Copyright Holder. This restriction only applies to the primary font name as
|
||||||
|
presented to the users.
|
||||||
|
|
||||||
|
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
|
||||||
|
Software shall not be used to promote, endorse or advertise any
|
||||||
|
Modified Version, except to acknowledge the contribution(s) of the
|
||||||
|
Copyright Holder(s) and the Author(s) or with their explicit written
|
||||||
|
permission.
|
||||||
|
|
||||||
|
5) The Font Software, modified or unmodified, in part or in whole,
|
||||||
|
must be distributed entirely under this license, and must not be
|
||||||
|
distributed under any other license. The requirement for fonts to
|
||||||
|
remain under this license does not apply to any document created
|
||||||
|
using the Font Software.
|
||||||
|
|
||||||
|
TERMINATION
|
||||||
|
This license becomes null and void if any of the above conditions are
|
||||||
|
not met.
|
||||||
|
|
||||||
|
DISCLAIMER
|
||||||
|
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
|
||||||
|
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
|
||||||
|
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
|
||||||
|
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||||
|
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
|
||||||
|
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||||
|
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
|
||||||
|
OTHER DEALINGS IN THE FONT SOFTWARE.
|
@ -402,7 +402,7 @@
|
|||||||
<x:Double x:Key="ControlContentThemeFontSize">13</x:Double>
|
<x:Double x:Key="ControlContentThemeFontSize">13</x:Double>
|
||||||
<x:Double x:Key="MenuItemHeight">26</x:Double>
|
<x:Double x:Key="MenuItemHeight">26</x:Double>
|
||||||
<x:Double x:Key="TabItemMinHeight">28</x:Double>
|
<x:Double x:Key="TabItemMinHeight">28</x:Double>
|
||||||
<x:Double x:Key="ContentDialogMaxWidth">700</x:Double>
|
<x:Double x:Key="ContentDialogMaxWidth">900</x:Double>
|
||||||
<x:Double x:Key="ContentDialogMaxHeight">756</x:Double>
|
<x:Double x:Key="ContentDialogMaxHeight">756</x:Double>
|
||||||
</Styles.Resources>
|
</Styles.Resources>
|
||||||
</Styles>
|
</Styles>
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -1,4 +1,5 @@
|
|||||||
using DiscordRPC;
|
using DiscordRPC;
|
||||||
|
using Gommon;
|
||||||
using Humanizer;
|
using Humanizer;
|
||||||
using Humanizer.Localisation;
|
using Humanizer.Localisation;
|
||||||
using Ryujinx.Ava.Utilities.AppLibrary;
|
using Ryujinx.Ava.Utilities.AppLibrary;
|
||||||
@ -45,16 +46,7 @@ namespace Ryujinx.Ava
|
|||||||
};
|
};
|
||||||
|
|
||||||
ConfigurationState.Instance.EnableDiscordIntegration.Event += Update;
|
ConfigurationState.Instance.EnableDiscordIntegration.Event += Update;
|
||||||
TitleIDs.CurrentApplication.Event += (_, e) =>
|
TitleIDs.CurrentApplication.Event += (_, e) => Use(e.NewValue);
|
||||||
{
|
|
||||||
if (e.NewValue)
|
|
||||||
SwitchToPlayingState(
|
|
||||||
ApplicationLibrary.LoadAndSaveMetaData(e.NewValue),
|
|
||||||
Switch.Shared.Processes.ActiveApplication
|
|
||||||
);
|
|
||||||
else
|
|
||||||
SwitchToMainState();
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void Update(object sender, ReactiveEventArgs<bool> evnt)
|
private static void Update(object sender, ReactiveEventArgs<bool> evnt)
|
||||||
@ -75,11 +67,23 @@ namespace Ryujinx.Ava
|
|||||||
_discordClient = new DiscordRpcClient(ApplicationId);
|
_discordClient = new DiscordRpcClient(ApplicationId);
|
||||||
|
|
||||||
_discordClient.Initialize();
|
_discordClient.Initialize();
|
||||||
_discordClient.SetPresence(_discordPresenceMain);
|
|
||||||
|
Use(TitleIDs.CurrentApplication);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void Use(Optional<string> titleId)
|
||||||
|
{
|
||||||
|
if (titleId.TryGet(out string tid))
|
||||||
|
SwitchToPlayingState(
|
||||||
|
ApplicationLibrary.LoadAndSaveMetaData(tid),
|
||||||
|
Switch.Shared.Processes.ActiveApplication
|
||||||
|
);
|
||||||
|
else
|
||||||
|
SwitchToMainState();
|
||||||
|
}
|
||||||
|
|
||||||
private static void SwitchToPlayingState(ApplicationMetadata appMeta, ProcessResult procRes)
|
private static void SwitchToPlayingState(ApplicationMetadata appMeta, ProcessResult procRes)
|
||||||
{
|
{
|
||||||
_discordClient?.SetPresence(new RichPresence
|
_discordClient?.SetPresence(new RichPresence
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
using DiscordRPC;
|
using DiscordRPC;
|
||||||
using LibHac.Tools.FsSystem;
|
using LibHac.Tools.FsSystem;
|
||||||
using Ryujinx.Audio.Backends.SDL2;
|
using Ryujinx.Audio.Backends.SDL2;
|
||||||
using Ryujinx.Ava;
|
using Ryujinx.Ava;
|
||||||
|
@ -21,6 +21,7 @@ using Ryujinx.Graphics.Vulkan.MoltenVK;
|
|||||||
using Ryujinx.Headless;
|
using Ryujinx.Headless;
|
||||||
using Ryujinx.SDL2.Common;
|
using Ryujinx.SDL2.Common;
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
@ -243,16 +244,33 @@ namespace Ryujinx.Ava
|
|||||||
: $"Launch Mode: {AppDataManager.Mode}");
|
: $"Launch Mode: {AppDataManager.Mode}");
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static void ProcessUnhandledException(object sender, Exception ex, bool isTerminating)
|
internal static void ProcessUnhandledException(object sender, Exception initialException, bool isTerminating)
|
||||||
{
|
{
|
||||||
Logger.Log log = Logger.Error ?? Logger.Notice;
|
Logger.Log log = Logger.Error ?? Logger.Notice;
|
||||||
string message = $"Unhandled exception caught: {ex}";
|
|
||||||
|
|
||||||
|
List<Exception> exceptions = [];
|
||||||
|
|
||||||
|
if (initialException is AggregateException ae)
|
||||||
|
{
|
||||||
|
exceptions.AddRange(ae.InnerExceptions);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
exceptions.Add(initialException);
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var e in exceptions)
|
||||||
|
{
|
||||||
|
string message = $"Unhandled exception caught: {e}";
|
||||||
// ReSharper disable once ConstantConditionalAccessQualifier
|
// ReSharper disable once ConstantConditionalAccessQualifier
|
||||||
if (sender?.GetType()?.AsPrettyString() is { } senderName)
|
if (sender?.GetType()?.AsPrettyString() is { } senderName)
|
||||||
log.Print(LogClass.Application, message, senderName);
|
log.Print(LogClass.Application, message, senderName);
|
||||||
else
|
else
|
||||||
log.PrintMsg(LogClass.Application, message);
|
log.PrintMsg(LogClass.Application, message);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if (isTerminating)
|
if (isTerminating)
|
||||||
Exit();
|
Exit();
|
||||||
|
@ -61,6 +61,7 @@
|
|||||||
<PackageReference Include="Ryujinx.Graphics.Nvdec.Dependencies" />
|
<PackageReference Include="Ryujinx.Graphics.Nvdec.Dependencies" />
|
||||||
<PackageReference Include="Ryujinx.Graphics.Vulkan.Dependencies.MoltenVK" Condition="'$(RuntimeIdentifier)' != 'linux-x64' AND '$(RuntimeIdentifier)' != 'linux-arm64' AND '$(RuntimeIdentifier)' != 'win-x64'" />
|
<PackageReference Include="Ryujinx.Graphics.Vulkan.Dependencies.MoltenVK" Condition="'$(RuntimeIdentifier)' != 'linux-x64' AND '$(RuntimeIdentifier)' != 'linux-arm64' AND '$(RuntimeIdentifier)' != 'win-x64'" />
|
||||||
<PackageReference Include="securifybv.ShellLink" />
|
<PackageReference Include="securifybv.ShellLink" />
|
||||||
|
<PackageReference Include="Sep" />
|
||||||
<PackageReference Include="Silk.NET.Vulkan" />
|
<PackageReference Include="Silk.NET.Vulkan" />
|
||||||
<PackageReference Include="Silk.NET.Vulkan.Extensions.EXT" />
|
<PackageReference Include="Silk.NET.Vulkan.Extensions.EXT" />
|
||||||
<PackageReference Include="Silk.NET.Vulkan.Extensions.KHR" />
|
<PackageReference Include="Silk.NET.Vulkan.Extensions.KHR" />
|
||||||
@ -113,6 +114,10 @@
|
|||||||
<AvaloniaResource Include="UI\**\*.xaml">
|
<AvaloniaResource Include="UI\**\*.xaml">
|
||||||
<SubType>Designer</SubType>
|
<SubType>Designer</SubType>
|
||||||
</AvaloniaResource>
|
</AvaloniaResource>
|
||||||
|
<AvaloniaResource Include="Assets\Fonts\Mono\JetBrainsMonoNL-Bold.ttf" />
|
||||||
|
<AvaloniaResource Include="Assets\Fonts\Mono\JetBrainsMonoNL-BoldItalic.ttf" />
|
||||||
|
<AvaloniaResource Include="Assets\Fonts\Mono\JetBrainsMonoNL-Italic.ttf" />
|
||||||
|
<AvaloniaResource Include="Assets\Fonts\Mono\JetBrainsMonoNL-Regular.ttf" />
|
||||||
<AvaloniaResource Include="Assets\Fonts\SegoeFluentIcons.ttf" />
|
<AvaloniaResource Include="Assets\Fonts\SegoeFluentIcons.ttf" />
|
||||||
<AvaloniaResource Include="Assets\Styles\Themes.xaml">
|
<AvaloniaResource Include="Assets\Styles\Themes.xaml">
|
||||||
<Generator>MSBuild:Compile</Generator>
|
<Generator>MSBuild:Compile</Generator>
|
||||||
@ -140,6 +145,9 @@
|
|||||||
<EmbeddedResource Include="..\..\distribution\macos\shortcut-template.plist">
|
<EmbeddedResource Include="..\..\distribution\macos\shortcut-template.plist">
|
||||||
<Link>Assets\ShortcutFiles\shortcut-template.plist</Link>
|
<Link>Assets\ShortcutFiles\shortcut-template.plist</Link>
|
||||||
</EmbeddedResource>
|
</EmbeddedResource>
|
||||||
|
<EmbeddedResource Include="..\..\docs\compatibility.csv" LogicalName="RyujinxGameCompatibilityList">
|
||||||
|
<Link>Assets\RyujinxGameCompatibility.csv</Link>
|
||||||
|
</EmbeddedResource>
|
||||||
<EmbeddedResource Include="Assets\locales.json" />
|
<EmbeddedResource Include="Assets\locales.json" />
|
||||||
<EmbeddedResource Include="Assets\Styles\Styles.xaml" />
|
<EmbeddedResource Include="Assets\Styles\Styles.xaml" />
|
||||||
<EmbeddedResource Include="Assets\Icons\Controller_JoyConLeft.svg" />
|
<EmbeddedResource Include="Assets\Icons\Controller_JoyConLeft.svg" />
|
||||||
@ -163,4 +171,7 @@
|
|||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<AdditionalFiles Include="Assets\locales.json" />
|
<AdditionalFiles Include="Assets\locales.json" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Folder Include="Assets\Fonts\Mono\" />
|
||||||
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
||||||
|
@ -6,6 +6,9 @@
|
|||||||
<Application.Resources>
|
<Application.Resources>
|
||||||
<ResourceDictionary>
|
<ResourceDictionary>
|
||||||
<ResourceDictionary.MergedDictionaries>
|
<ResourceDictionary.MergedDictionaries>
|
||||||
|
<ResourceDictionary>
|
||||||
|
<FontFamily x:Key="JetBrainsMono">avares://Ryujinx/Assets/Fonts/Mono/#JetBrains Mono</FontFamily>
|
||||||
|
</ResourceDictionary>
|
||||||
<MergeResourceInclude Source="/Assets/Styles/Themes.xaml"/>
|
<MergeResourceInclude Source="/Assets/Styles/Themes.xaml"/>
|
||||||
</ResourceDictionary.MergedDictionaries>
|
</ResourceDictionary.MergedDictionaries>
|
||||||
</ResourceDictionary>
|
</ResourceDictionary>
|
||||||
|
@ -133,12 +133,13 @@
|
|||||||
Spacing="5">
|
Spacing="5">
|
||||||
<TextBlock
|
<TextBlock
|
||||||
HorizontalAlignment="Stretch"
|
HorizontalAlignment="Stretch"
|
||||||
Text="{Binding TimePlayedString}"
|
Text="{Binding LastPlayedString}"
|
||||||
TextAlignment="End"
|
TextAlignment="End"
|
||||||
TextWrapping="Wrap" />
|
TextWrapping="Wrap" />
|
||||||
<TextBlock
|
<TextBlock
|
||||||
HorizontalAlignment="Stretch"
|
HorizontalAlignment="Stretch"
|
||||||
Text="{Binding LastPlayedString}"
|
Text="{Binding TimePlayedString}"
|
||||||
|
IsVisible="{Binding HasPlayedPreviously}"
|
||||||
TextAlignment="End"
|
TextAlignment="End"
|
||||||
TextWrapping="Wrap" />
|
TextWrapping="Wrap" />
|
||||||
<TextBlock
|
<TextBlock
|
||||||
|
@ -7,6 +7,7 @@
|
|||||||
Title="Ryujinx - Waiting"
|
Title="Ryujinx - Waiting"
|
||||||
SizeToContent="WidthAndHeight"
|
SizeToContent="WidthAndHeight"
|
||||||
WindowStartupLocation="CenterOwner"
|
WindowStartupLocation="CenterOwner"
|
||||||
|
CanResize="False"
|
||||||
mc:Ignorable="d"
|
mc:Ignorable="d"
|
||||||
Focusable="True">
|
Focusable="True">
|
||||||
<Grid
|
<Grid
|
||||||
|
@ -0,0 +1,28 @@
|
|||||||
|
using Avalonia.Data.Converters;
|
||||||
|
using Avalonia.Media;
|
||||||
|
using Gommon;
|
||||||
|
using Ryujinx.Ava.Common.Locale;
|
||||||
|
using System;
|
||||||
|
using System.Globalization;
|
||||||
|
|
||||||
|
namespace Ryujinx.Ava.UI.Helpers
|
||||||
|
{
|
||||||
|
public class PlayabilityStatusConverter : IValueConverter
|
||||||
|
{
|
||||||
|
private static readonly Lazy<PlayabilityStatusConverter> _shared = new(() => new());
|
||||||
|
public static PlayabilityStatusConverter Shared => _shared.Value;
|
||||||
|
|
||||||
|
public object Convert(object value, Type _, object __, CultureInfo ___)
|
||||||
|
=> value.Cast<LocaleKeys>() switch
|
||||||
|
{
|
||||||
|
LocaleKeys.CompatibilityListNothing or
|
||||||
|
LocaleKeys.CompatibilityListBoots or
|
||||||
|
LocaleKeys.CompatibilityListMenus => Brushes.Red,
|
||||||
|
LocaleKeys.CompatibilityListIngame => Brushes.Yellow,
|
||||||
|
_ => Brushes.ForestGreen
|
||||||
|
};
|
||||||
|
|
||||||
|
public object ConvertBack(object value, Type _, object __, CultureInfo ___)
|
||||||
|
=> throw new NotSupportedException();
|
||||||
|
}
|
||||||
|
}
|
@ -63,6 +63,7 @@ namespace Ryujinx.Ava.UI.Helpers
|
|||||||
public static MiniCommand Create(Action callback) => new MiniCommand<object>(_ => callback());
|
public static MiniCommand Create(Action callback) => new MiniCommand<object>(_ => callback());
|
||||||
public static MiniCommand Create<TArg>(Action<TArg> callback) => new MiniCommand<TArg>(callback);
|
public static MiniCommand Create<TArg>(Action<TArg> callback) => new MiniCommand<TArg>(callback);
|
||||||
public static MiniCommand CreateFromTask(Func<Task> callback) => new MiniCommand<object>(_ => callback());
|
public static MiniCommand CreateFromTask(Func<Task> callback) => new MiniCommand<object>(_ => callback());
|
||||||
|
public static MiniCommand CreateFromTask<TArg>(Func<TArg, Task> callback) => new MiniCommand<TArg>(callback);
|
||||||
|
|
||||||
public abstract bool CanExecute(object parameter);
|
public abstract bool CanExecute(object parameter);
|
||||||
public abstract void Execute(object parameter);
|
public abstract void Execute(object parameter);
|
||||||
|
@ -1,135 +1,38 @@
|
|||||||
|
using CommunityToolkit.Mvvm.ComponentModel;
|
||||||
using Ryujinx.Ava.UI.ViewModels;
|
using Ryujinx.Ava.UI.ViewModels;
|
||||||
using Ryujinx.Common.Configuration.Hid;
|
using Ryujinx.Common.Configuration.Hid;
|
||||||
|
|
||||||
namespace Ryujinx.Ava.UI.Models.Input
|
namespace Ryujinx.Ava.UI.Models.Input
|
||||||
{
|
{
|
||||||
public class HotkeyConfig : BaseModel
|
public partial class HotkeyConfig : BaseModel
|
||||||
{
|
{
|
||||||
private Key _toggleVSyncMode;
|
[ObservableProperty] private Key _toggleVSyncMode;
|
||||||
public Key ToggleVSyncMode
|
|
||||||
{
|
|
||||||
get => _toggleVSyncMode;
|
|
||||||
set
|
|
||||||
{
|
|
||||||
_toggleVSyncMode = value;
|
|
||||||
OnPropertyChanged();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private Key _screenshot;
|
[ObservableProperty] private Key _screenshot;
|
||||||
public Key Screenshot
|
|
||||||
{
|
|
||||||
get => _screenshot;
|
|
||||||
set
|
|
||||||
{
|
|
||||||
_screenshot = value;
|
|
||||||
OnPropertyChanged();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private Key _showUI;
|
[ObservableProperty] private Key _showUI;
|
||||||
public Key ShowUI
|
|
||||||
{
|
|
||||||
get => _showUI;
|
|
||||||
set
|
|
||||||
{
|
|
||||||
_showUI = value;
|
|
||||||
OnPropertyChanged();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private Key _pause;
|
[ObservableProperty] private Key _pause;
|
||||||
public Key Pause
|
|
||||||
{
|
|
||||||
get => _pause;
|
|
||||||
set
|
|
||||||
{
|
|
||||||
_pause = value;
|
|
||||||
OnPropertyChanged();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private Key _toggleMute;
|
[ObservableProperty] private Key _toggleMute;
|
||||||
public Key ToggleMute
|
|
||||||
{
|
|
||||||
get => _toggleMute;
|
|
||||||
set
|
|
||||||
{
|
|
||||||
_toggleMute = value;
|
|
||||||
OnPropertyChanged();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private Key _resScaleUp;
|
[ObservableProperty] private Key _resScaleUp;
|
||||||
public Key ResScaleUp
|
|
||||||
{
|
|
||||||
get => _resScaleUp;
|
|
||||||
set
|
|
||||||
{
|
|
||||||
_resScaleUp = value;
|
|
||||||
OnPropertyChanged();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private Key _resScaleDown;
|
[ObservableProperty] private Key _resScaleDown;
|
||||||
public Key ResScaleDown
|
|
||||||
{
|
|
||||||
get => _resScaleDown;
|
|
||||||
set
|
|
||||||
{
|
|
||||||
_resScaleDown = value;
|
|
||||||
OnPropertyChanged();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private Key _volumeUp;
|
[ObservableProperty] private Key _volumeUp;
|
||||||
public Key VolumeUp
|
|
||||||
{
|
|
||||||
get => _volumeUp;
|
|
||||||
set
|
|
||||||
{
|
|
||||||
_volumeUp = value;
|
|
||||||
OnPropertyChanged();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private Key _volumeDown;
|
[ObservableProperty] private Key _volumeDown;
|
||||||
public Key VolumeDown
|
|
||||||
{
|
|
||||||
get => _volumeDown;
|
|
||||||
set
|
|
||||||
{
|
|
||||||
_volumeDown = value;
|
|
||||||
OnPropertyChanged();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private Key _customVSyncIntervalIncrement;
|
[ObservableProperty] private Key _customVSyncIntervalIncrement;
|
||||||
public Key CustomVSyncIntervalIncrement
|
|
||||||
{
|
|
||||||
get => _customVSyncIntervalIncrement;
|
|
||||||
set
|
|
||||||
{
|
|
||||||
_customVSyncIntervalIncrement = value;
|
|
||||||
OnPropertyChanged();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private Key _customVSyncIntervalDecrement;
|
[ObservableProperty] private Key _customVSyncIntervalDecrement;
|
||||||
public Key CustomVSyncIntervalDecrement
|
|
||||||
{
|
|
||||||
get => _customVSyncIntervalDecrement;
|
|
||||||
set
|
|
||||||
{
|
|
||||||
_customVSyncIntervalDecrement = value;
|
|
||||||
OnPropertyChanged();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public HotkeyConfig(KeyboardHotkeys config)
|
public HotkeyConfig(KeyboardHotkeys config)
|
||||||
{
|
{
|
||||||
if (config != null)
|
if (config == null)
|
||||||
{
|
return;
|
||||||
|
|
||||||
ToggleVSyncMode = config.ToggleVSyncMode;
|
ToggleVSyncMode = config.ToggleVSyncMode;
|
||||||
Screenshot = config.Screenshot;
|
Screenshot = config.Screenshot;
|
||||||
ShowUI = config.ShowUI;
|
ShowUI = config.ShowUI;
|
||||||
@ -142,11 +45,9 @@ namespace Ryujinx.Ava.UI.Models.Input
|
|||||||
CustomVSyncIntervalIncrement = config.CustomVSyncIntervalIncrement;
|
CustomVSyncIntervalIncrement = config.CustomVSyncIntervalIncrement;
|
||||||
CustomVSyncIntervalDecrement = config.CustomVSyncIntervalDecrement;
|
CustomVSyncIntervalDecrement = config.CustomVSyncIntervalDecrement;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
public KeyboardHotkeys GetConfig()
|
public KeyboardHotkeys GetConfig() =>
|
||||||
{
|
new()
|
||||||
var config = new KeyboardHotkeys
|
|
||||||
{
|
{
|
||||||
ToggleVSyncMode = ToggleVSyncMode,
|
ToggleVSyncMode = ToggleVSyncMode,
|
||||||
Screenshot = Screenshot,
|
Screenshot = Screenshot,
|
||||||
@ -160,8 +61,5 @@ namespace Ryujinx.Ava.UI.Models.Input
|
|||||||
CustomVSyncIntervalIncrement = CustomVSyncIntervalIncrement,
|
CustomVSyncIntervalIncrement = CustomVSyncIntervalIncrement,
|
||||||
CustomVSyncIntervalDecrement = CustomVSyncIntervalDecrement,
|
CustomVSyncIntervalDecrement = CustomVSyncIntervalDecrement,
|
||||||
};
|
};
|
||||||
|
|
||||||
return config;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,21 +1,13 @@
|
|||||||
using Avalonia.Svg.Skia;
|
using Avalonia.Svg.Skia;
|
||||||
|
using CommunityToolkit.Mvvm.ComponentModel;
|
||||||
using Ryujinx.Ava.UI.Models.Input;
|
using Ryujinx.Ava.UI.Models.Input;
|
||||||
using Ryujinx.Ava.UI.Views.Input;
|
using Ryujinx.Ava.UI.Views.Input;
|
||||||
|
|
||||||
namespace Ryujinx.Ava.UI.ViewModels.Input
|
namespace Ryujinx.Ava.UI.ViewModels.Input
|
||||||
{
|
{
|
||||||
public class ControllerInputViewModel : BaseModel
|
public partial class ControllerInputViewModel : BaseModel
|
||||||
{
|
{
|
||||||
private GamepadInputConfig _config;
|
[ObservableProperty] private GamepadInputConfig _config;
|
||||||
public GamepadInputConfig Config
|
|
||||||
{
|
|
||||||
get => _config;
|
|
||||||
set
|
|
||||||
{
|
|
||||||
_config = value;
|
|
||||||
OnPropertyChanged();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private bool _isLeft;
|
private bool _isLeft;
|
||||||
public bool IsLeft
|
public bool IsLeft
|
||||||
@ -43,16 +35,7 @@ namespace Ryujinx.Ava.UI.ViewModels.Input
|
|||||||
|
|
||||||
public bool HasSides => IsLeft ^ IsRight;
|
public bool HasSides => IsLeft ^ IsRight;
|
||||||
|
|
||||||
private SvgImage _image;
|
[ObservableProperty] private SvgImage _image;
|
||||||
public SvgImage Image
|
|
||||||
{
|
|
||||||
get => _image;
|
|
||||||
set
|
|
||||||
{
|
|
||||||
_image = value;
|
|
||||||
OnPropertyChanged();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public readonly InputViewModel ParentModel;
|
public readonly InputViewModel ParentModel;
|
||||||
|
|
||||||
|
@ -1,9 +1,8 @@
|
|||||||
using Avalonia;
|
|
||||||
using Avalonia.Collections;
|
using Avalonia.Collections;
|
||||||
using Avalonia.Controls;
|
using Avalonia.Controls;
|
||||||
using Avalonia.Controls.ApplicationLifetimes;
|
|
||||||
using Avalonia.Svg.Skia;
|
using Avalonia.Svg.Skia;
|
||||||
using Avalonia.Threading;
|
using Avalonia.Threading;
|
||||||
|
using CommunityToolkit.Mvvm.ComponentModel;
|
||||||
using Ryujinx.Ava.Common.Locale;
|
using Ryujinx.Ava.Common.Locale;
|
||||||
using Ryujinx.Ava.Input;
|
using Ryujinx.Ava.Input;
|
||||||
using Ryujinx.Ava.UI.Helpers;
|
using Ryujinx.Ava.UI.Helpers;
|
||||||
@ -32,7 +31,7 @@ using Key = Ryujinx.Common.Configuration.Hid.Key;
|
|||||||
|
|
||||||
namespace Ryujinx.Ava.UI.ViewModels.Input
|
namespace Ryujinx.Ava.UI.ViewModels.Input
|
||||||
{
|
{
|
||||||
public class InputViewModel : BaseModel, IDisposable
|
public partial class InputViewModel : BaseModel, IDisposable
|
||||||
{
|
{
|
||||||
private const string Disabled = "disabled";
|
private const string Disabled = "disabled";
|
||||||
private const string ProControllerResource = "Ryujinx/Assets/Icons/Controller_ProCon.svg";
|
private const string ProControllerResource = "Ryujinx/Assets/Icons/Controller_ProCon.svg";
|
||||||
@ -48,8 +47,8 @@ namespace Ryujinx.Ava.UI.ViewModels.Input
|
|||||||
private int _controller;
|
private int _controller;
|
||||||
private string _controllerImage;
|
private string _controllerImage;
|
||||||
private int _device;
|
private int _device;
|
||||||
private object _configViewModel;
|
[ObservableProperty] private object _configViewModel;
|
||||||
private string _profileName;
|
[ObservableProperty] private string _profileName;
|
||||||
private bool _isLoaded;
|
private bool _isLoaded;
|
||||||
|
|
||||||
private static readonly InputConfigJsonSerializerContext _serializerContext = new(JsonHelper.GetDefaultSerializerOptions());
|
private static readonly InputConfigJsonSerializerContext _serializerContext = new(JsonHelper.GetDefaultSerializerOptions());
|
||||||
@ -73,17 +72,6 @@ namespace Ryujinx.Ava.UI.ViewModels.Input
|
|||||||
public bool IsModified { get; set; }
|
public bool IsModified { get; set; }
|
||||||
public event Action NotifyChangesEvent;
|
public event Action NotifyChangesEvent;
|
||||||
|
|
||||||
public object ConfigViewModel
|
|
||||||
{
|
|
||||||
get => _configViewModel;
|
|
||||||
set
|
|
||||||
{
|
|
||||||
_configViewModel = value;
|
|
||||||
|
|
||||||
OnPropertyChanged();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public PlayerIndex PlayerIdChoose
|
public PlayerIndex PlayerIdChoose
|
||||||
{
|
{
|
||||||
get => _playerIdChoose;
|
get => _playerIdChoose;
|
||||||
@ -200,16 +188,6 @@ namespace Ryujinx.Ava.UI.ViewModels.Input
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public string ProfileName
|
|
||||||
{
|
|
||||||
get => _profileName; set
|
|
||||||
{
|
|
||||||
_profileName = value;
|
|
||||||
|
|
||||||
OnPropertyChanged();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public int Device
|
public int Device
|
||||||
{
|
{
|
||||||
get => _device;
|
get => _device;
|
||||||
|
@ -1,20 +1,12 @@
|
|||||||
using Avalonia.Svg.Skia;
|
using Avalonia.Svg.Skia;
|
||||||
|
using CommunityToolkit.Mvvm.ComponentModel;
|
||||||
using Ryujinx.Ava.UI.Models.Input;
|
using Ryujinx.Ava.UI.Models.Input;
|
||||||
|
|
||||||
namespace Ryujinx.Ava.UI.ViewModels.Input
|
namespace Ryujinx.Ava.UI.ViewModels.Input
|
||||||
{
|
{
|
||||||
public class KeyboardInputViewModel : BaseModel
|
public partial class KeyboardInputViewModel : BaseModel
|
||||||
{
|
{
|
||||||
private KeyboardInputConfig _config;
|
[ObservableProperty] private KeyboardInputConfig _config;
|
||||||
public KeyboardInputConfig Config
|
|
||||||
{
|
|
||||||
get => _config;
|
|
||||||
set
|
|
||||||
{
|
|
||||||
_config = value;
|
|
||||||
OnPropertyChanged();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private bool _isLeft;
|
private bool _isLeft;
|
||||||
public bool IsLeft
|
public bool IsLeft
|
||||||
@ -42,16 +34,7 @@ namespace Ryujinx.Ava.UI.ViewModels.Input
|
|||||||
|
|
||||||
public bool HasSides => IsLeft ^ IsRight;
|
public bool HasSides => IsLeft ^ IsRight;
|
||||||
|
|
||||||
private SvgImage _image;
|
[ObservableProperty] private SvgImage _image;
|
||||||
public SvgImage Image
|
|
||||||
{
|
|
||||||
get => _image;
|
|
||||||
set
|
|
||||||
{
|
|
||||||
_image = value;
|
|
||||||
OnPropertyChanged();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public readonly InputViewModel ParentModel;
|
public readonly InputViewModel ParentModel;
|
||||||
|
|
||||||
|
@ -1,93 +1,23 @@
|
|||||||
|
using CommunityToolkit.Mvvm.ComponentModel;
|
||||||
|
|
||||||
namespace Ryujinx.Ava.UI.ViewModels.Input
|
namespace Ryujinx.Ava.UI.ViewModels.Input
|
||||||
{
|
{
|
||||||
public class MotionInputViewModel : BaseModel
|
public partial class MotionInputViewModel : BaseModel
|
||||||
{
|
{
|
||||||
private int _slot;
|
[ObservableProperty] private int _slot;
|
||||||
public int Slot
|
|
||||||
{
|
|
||||||
get => _slot;
|
|
||||||
set
|
|
||||||
{
|
|
||||||
_slot = value;
|
|
||||||
OnPropertyChanged();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private int _altSlot;
|
[ObservableProperty] private int _altSlot;
|
||||||
public int AltSlot
|
|
||||||
{
|
|
||||||
get => _altSlot;
|
|
||||||
set
|
|
||||||
{
|
|
||||||
_altSlot = value;
|
|
||||||
OnPropertyChanged();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private string _dsuServerHost;
|
[ObservableProperty] private string _dsuServerHost;
|
||||||
public string DsuServerHost
|
|
||||||
{
|
|
||||||
get => _dsuServerHost;
|
|
||||||
set
|
|
||||||
{
|
|
||||||
_dsuServerHost = value;
|
|
||||||
OnPropertyChanged();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private int _dsuServerPort;
|
[ObservableProperty] private int _dsuServerPort;
|
||||||
public int DsuServerPort
|
|
||||||
{
|
|
||||||
get => _dsuServerPort;
|
|
||||||
set
|
|
||||||
{
|
|
||||||
_dsuServerPort = value;
|
|
||||||
OnPropertyChanged();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private bool _mirrorInput;
|
[ObservableProperty] private bool _mirrorInput;
|
||||||
public bool MirrorInput
|
|
||||||
{
|
|
||||||
get => _mirrorInput;
|
|
||||||
set
|
|
||||||
{
|
|
||||||
_mirrorInput = value;
|
|
||||||
OnPropertyChanged();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private int _sensitivity;
|
[ObservableProperty] private int _sensitivity;
|
||||||
public int Sensitivity
|
|
||||||
{
|
|
||||||
get => _sensitivity;
|
|
||||||
set
|
|
||||||
{
|
|
||||||
_sensitivity = value;
|
|
||||||
OnPropertyChanged();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private double _gryoDeadzone;
|
[ObservableProperty] private double _gyroDeadzone;
|
||||||
public double GyroDeadzone
|
|
||||||
{
|
|
||||||
get => _gryoDeadzone;
|
|
||||||
set
|
|
||||||
{
|
|
||||||
_gryoDeadzone = value;
|
|
||||||
OnPropertyChanged();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private bool _enableCemuHookMotion;
|
[ObservableProperty] private bool _enableCemuHookMotion;
|
||||||
public bool EnableCemuHookMotion
|
|
||||||
{
|
|
||||||
get => _enableCemuHookMotion;
|
|
||||||
set
|
|
||||||
{
|
|
||||||
_enableCemuHookMotion = value;
|
|
||||||
OnPropertyChanged();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,27 +1,11 @@
|
|||||||
|
using CommunityToolkit.Mvvm.ComponentModel;
|
||||||
|
|
||||||
namespace Ryujinx.Ava.UI.ViewModels.Input
|
namespace Ryujinx.Ava.UI.ViewModels.Input
|
||||||
{
|
{
|
||||||
public class RumbleInputViewModel : BaseModel
|
public partial class RumbleInputViewModel : BaseModel
|
||||||
{
|
{
|
||||||
private float _strongRumble;
|
[ObservableProperty] private float _strongRumble;
|
||||||
public float StrongRumble
|
|
||||||
{
|
|
||||||
get => _strongRumble;
|
|
||||||
set
|
|
||||||
{
|
|
||||||
_strongRumble = value;
|
|
||||||
OnPropertyChanged();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private float _weakRumble;
|
[ObservableProperty] private float _weakRumble;
|
||||||
public float WeakRumble
|
|
||||||
{
|
|
||||||
get => _weakRumble;
|
|
||||||
set
|
|
||||||
{
|
|
||||||
_weakRumble = value;
|
|
||||||
OnPropertyChanged();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -741,7 +741,10 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
Applications.ToObservableChangeSet()
|
Applications.ToObservableChangeSet()
|
||||||
.Filter(Filter)
|
.Filter(Filter)
|
||||||
.Sort(GetComparer())
|
.Sort(GetComparer())
|
||||||
.Bind(out _appsObservableList).AsObservableList();
|
#pragma warning disable MVVMTK0034
|
||||||
|
.Bind(out _appsObservableList)
|
||||||
|
#pragma warning restore MVVMTK0034
|
||||||
|
.AsObservableList();
|
||||||
|
|
||||||
OnPropertyChanged(nameof(AppsObservableList));
|
OnPropertyChanged(nameof(AppsObservableList));
|
||||||
}
|
}
|
||||||
|
@ -56,7 +56,7 @@
|
|||||||
ToolTip.Tip="{ext:Locale LoadTitleUpdatesFromFolderTooltip}" />
|
ToolTip.Tip="{ext:Locale LoadTitleUpdatesFromFolderTooltip}" />
|
||||||
<MenuItem Header="{ext:Locale MenuBarFileOpenApplet}" IsEnabled="{Binding IsAppletMenuActive}" Icon="{ext:Icon mdi-launch}">
|
<MenuItem Header="{ext:Locale MenuBarFileOpenApplet}" IsEnabled="{Binding IsAppletMenuActive}" Icon="{ext:Icon mdi-launch}">
|
||||||
<MenuItem
|
<MenuItem
|
||||||
Click="OpenMiiApplet"
|
Name="MiiAppletMenuItem"
|
||||||
Header="{ext:Locale MenuBarFileOpenAppletOpenMiiApplet}"
|
Header="{ext:Locale MenuBarFileOpenAppletOpenMiiApplet}"
|
||||||
Icon="{ext:Icon fa-solid fa-person}"
|
Icon="{ext:Icon fa-solid fa-person}"
|
||||||
ToolTip.Tip="{ext:Locale MenuBarFileOpenAppletOpenMiiAppletToolTip}" />
|
ToolTip.Tip="{ext:Locale MenuBarFileOpenAppletOpenMiiAppletToolTip}" />
|
||||||
@ -72,7 +72,7 @@
|
|||||||
ToolTip.Tip="{ext:Locale OpenRyujinxLogsTooltip}" />
|
ToolTip.Tip="{ext:Locale OpenRyujinxLogsTooltip}" />
|
||||||
<Separator />
|
<Separator />
|
||||||
<MenuItem
|
<MenuItem
|
||||||
Click="CloseWindow"
|
Name="CloseRyujinxMenuItem"
|
||||||
Header="{ext:Locale MenuBarFileExit}"
|
Header="{ext:Locale MenuBarFileExit}"
|
||||||
Icon="{ext:Icon fa-solid fa-xmark}"
|
Icon="{ext:Icon fa-solid fa-xmark}"
|
||||||
ToolTip.Tip="{ext:Locale ExitTooltip}" />
|
ToolTip.Tip="{ext:Locale ExitTooltip}" />
|
||||||
@ -167,7 +167,7 @@
|
|||||||
Header="{ext:Locale MenuBarShowFileTypes}" />
|
Header="{ext:Locale MenuBarShowFileTypes}" />
|
||||||
<Separator />
|
<Separator />
|
||||||
<MenuItem
|
<MenuItem
|
||||||
Click="OpenSettings"
|
Name="OpenSettingsMenuItem"
|
||||||
Padding="0"
|
Padding="0"
|
||||||
Header="{ext:Locale MenuBarOptionsSettings}"
|
Header="{ext:Locale MenuBarOptionsSettings}"
|
||||||
Icon="{ext:Icon fa-solid fa-gear}"
|
Icon="{ext:Icon fa-solid fa-gear}"
|
||||||
@ -210,21 +210,21 @@
|
|||||||
Header="{ext:Locale MenuBarActions}"
|
Header="{ext:Locale MenuBarActions}"
|
||||||
IsEnabled="{Binding IsGameRunning}">
|
IsEnabled="{Binding IsGameRunning}">
|
||||||
<MenuItem
|
<MenuItem
|
||||||
Click="PauseEmulation_Click"
|
Name="PauseEmulationMenuItem"
|
||||||
Header="{ext:Locale MenuBarOptionsPauseEmulation}"
|
Header="{ext:Locale MenuBarOptionsPauseEmulation}"
|
||||||
Icon="{ext:Icon fa-solid fa-pause}"
|
Icon="{ext:Icon fa-solid fa-pause}"
|
||||||
InputGesture="{Binding PauseKey}"
|
InputGesture="{Binding PauseKey}"
|
||||||
IsEnabled="{Binding !IsPaused}"
|
IsEnabled="{Binding !IsPaused}"
|
||||||
IsVisible="{Binding !IsPaused}" />
|
IsVisible="{Binding !IsPaused}" />
|
||||||
<MenuItem
|
<MenuItem
|
||||||
Click="ResumeEmulation_Click"
|
Name="ResumeEmulationMenuItem"
|
||||||
Header="{ext:Locale MenuBarOptionsResumeEmulation}"
|
Header="{ext:Locale MenuBarOptionsResumeEmulation}"
|
||||||
Icon="{ext:Icon fa-solid fa-play}"
|
Icon="{ext:Icon fa-solid fa-play}"
|
||||||
InputGesture="{Binding PauseKey}"
|
InputGesture="{Binding PauseKey}"
|
||||||
IsEnabled="{Binding IsPaused}"
|
IsEnabled="{Binding IsPaused}"
|
||||||
IsVisible="{Binding IsPaused}" />
|
IsVisible="{Binding IsPaused}" />
|
||||||
<MenuItem
|
<MenuItem
|
||||||
Click="StopEmulation_Click"
|
Name="StopEmulationMenuItem"
|
||||||
Header="{ext:Locale MenuBarOptionsStopEmulation}"
|
Header="{ext:Locale MenuBarOptionsStopEmulation}"
|
||||||
Icon="{ext:Icon fa-solid fa-stop}"
|
Icon="{ext:Icon fa-solid fa-stop}"
|
||||||
InputGesture="Escape"
|
InputGesture="Escape"
|
||||||
@ -233,17 +233,15 @@
|
|||||||
<MenuItem Command="{Binding SimulateWakeUpMessage}" Header="{ext:Locale MenuBarOptionsSimulateWakeUpMessage}" />
|
<MenuItem Command="{Binding SimulateWakeUpMessage}" Header="{ext:Locale MenuBarOptionsSimulateWakeUpMessage}" />
|
||||||
<Separator />
|
<Separator />
|
||||||
<MenuItem
|
<MenuItem
|
||||||
Name="ScanAmiiboMenuItem"
|
Command="{Binding OpenAmiiboWindow}"
|
||||||
AttachedToVisualTree="ScanAmiiboMenuItem_AttachedToVisualTree"
|
AttachedToVisualTree="ScanAmiiboMenuItem_AttachedToVisualTree"
|
||||||
Click="OpenAmiiboWindow"
|
|
||||||
Header="{ext:Locale MenuBarActionsScanAmiibo}"
|
Header="{ext:Locale MenuBarActionsScanAmiibo}"
|
||||||
Icon="{ext:Icon mdi-cube-scan}"
|
Icon="{ext:Icon mdi-cube-scan}"
|
||||||
InputGesture="Ctrl + A"
|
InputGesture="Ctrl + A"
|
||||||
IsEnabled="{Binding IsAmiiboRequested}" />
|
IsEnabled="{Binding IsAmiiboRequested}" />
|
||||||
<MenuItem
|
<MenuItem
|
||||||
Name="ScanAmiiboMenuItemFromBin"
|
Command="{Binding OpenBinFile}"
|
||||||
AttachedToVisualTree="ScanBinAmiiboMenuItem_AttachedToVisualTree"
|
AttachedToVisualTree="ScanBinAmiiboMenuItem_AttachedToVisualTree"
|
||||||
Click="OpenBinFile"
|
|
||||||
Header="{ext:Locale MenuBarActionsScanAmiiboBin}"
|
Header="{ext:Locale MenuBarActionsScanAmiiboBin}"
|
||||||
Icon="{ext:Icon mdi-cube-scan}"
|
Icon="{ext:Icon mdi-cube-scan}"
|
||||||
IsVisible="{Binding CanScanAmiiboBinaries}"
|
IsVisible="{Binding CanScanAmiiboBinaries}"
|
||||||
@ -262,7 +260,7 @@
|
|||||||
InputGesture="{Binding ShowUiKey}"
|
InputGesture="{Binding ShowUiKey}"
|
||||||
IsEnabled="{Binding IsGameRunning}" />
|
IsEnabled="{Binding IsGameRunning}" />
|
||||||
<MenuItem
|
<MenuItem
|
||||||
Click="OpenCheatManagerForCurrentApp"
|
Name="CheatManagerMenuItem"
|
||||||
Header="{ext:Locale GameListContextMenuManageCheat}"
|
Header="{ext:Locale GameListContextMenuManageCheat}"
|
||||||
Icon="{ext:Icon fa-solid fa-code}"
|
Icon="{ext:Icon fa-solid fa-code}"
|
||||||
IsEnabled="{Binding IsGameRunning}" />
|
IsEnabled="{Binding IsGameRunning}" />
|
||||||
@ -277,52 +275,55 @@
|
|||||||
<MenuItem Command="{Binding InstallFirmwareFromFolder}" Header="{ext:Locale MenuBarFileToolsInstallFirmwareFromDirectory}" Icon="{ext:Icon mdi-folder-cog}" />
|
<MenuItem Command="{Binding InstallFirmwareFromFolder}" Header="{ext:Locale MenuBarFileToolsInstallFirmwareFromDirectory}" Icon="{ext:Icon mdi-folder-cog}" />
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
<MenuItem Header="{ext:Locale MenuBarToolsManageFileTypes}" IsVisible="{Binding ManageFileTypesVisible}">
|
<MenuItem Header="{ext:Locale MenuBarToolsManageFileTypes}" IsVisible="{Binding ManageFileTypesVisible}">
|
||||||
<MenuItem Header="{ext:Locale MenuBarToolsInstallFileTypes}" Click="InstallFileTypes_Click" IsEnabled="{Binding AreMimeTypesRegistered, Converter={x:Static BoolConverters.Not}}" />
|
<MenuItem Name="InstallFileTypesMenuItem" Header="{ext:Locale MenuBarToolsInstallFileTypes}" IsEnabled="{Binding AreMimeTypesRegistered, Converter={x:Static BoolConverters.Not}}" />
|
||||||
<MenuItem Header="{ext:Locale MenuBarToolsUninstallFileTypes}" Click="UninstallFileTypes_Click" IsEnabled="{Binding AreMimeTypesRegistered}" />
|
<MenuItem Name="UninstallFileTypesMenuItem" Header="{ext:Locale MenuBarToolsUninstallFileTypes}" IsEnabled="{Binding AreMimeTypesRegistered}" />
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
<Separator />
|
<Separator />
|
||||||
<MenuItem Header="{ext:Locale MenuBarToolsXCITrimmer}" IsEnabled="{Binding EnableNonGameRunningControls}" Click="OpenXCITrimmerWindow" Icon="{ext:Icon fa-solid fa-scissors}" />
|
<MenuItem Name="XciTrimmerMenuItem" Header="{ext:Locale MenuBarToolsXCITrimmer}" IsEnabled="{Binding EnableNonGameRunningControls}" Icon="{ext:Icon fa-solid fa-scissors}" />
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
<MenuItem VerticalAlignment="Center" Header="{ext:Locale MenuBarView}">
|
<MenuItem VerticalAlignment="Center" Header="{ext:Locale MenuBarView}">
|
||||||
<MenuItem VerticalAlignment="Center" Header="{ext:Locale MenuBarViewWindow}">
|
<MenuItem VerticalAlignment="Center" Header="{ext:Locale MenuBarViewWindow}">
|
||||||
<MenuItem Header="{ext:Locale MenuBarViewWindow720}" Tag="1280 720" Click="ChangeWindowSize_Click" />
|
<MenuItem Name="WindowSize720PMenuItem" Header="{ext:Locale MenuBarViewWindow720}" CommandParameter="1280 720" />
|
||||||
<MenuItem Header="{ext:Locale MenuBarViewWindow1080}" Tag="1920 1080" Click="ChangeWindowSize_Click" />
|
<MenuItem Name="WindowSize1080PMenuItem" Header="{ext:Locale MenuBarViewWindow1080}" CommandParameter="1920 1080" />
|
||||||
<MenuItem Header="{ext:Locale MenuBarViewWindow1440}" Tag="2560 1440" Click="ChangeWindowSize_Click" />
|
<MenuItem Name="WindowSize1440PMenuItem" Header="{ext:Locale MenuBarViewWindow1440}" CommandParameter="2560 1440" />
|
||||||
<MenuItem Header="{ext:Locale MenuBarViewWindow2160}" Tag="3840 2160" Click="ChangeWindowSize_Click" />
|
<MenuItem Name="WindowSize2160PMenuItem" Header="{ext:Locale MenuBarViewWindow2160}" CommandParameter="3840 2160" />
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
<MenuItem VerticalAlignment="Center" Header="{ext:Locale MenuBarHelp}">
|
<MenuItem VerticalAlignment="Center" Header="{ext:Locale MenuBarHelp}">
|
||||||
<MenuItem
|
<MenuItem
|
||||||
Click="OpenAboutWindow"
|
Name="AboutWindowMenuItem"
|
||||||
Header="{ext:Locale MenuBarHelpAbout}"
|
Header="{ext:Locale MenuBarHelpAbout}"
|
||||||
Icon="{ext:Icon fa-solid fa-circle-info}"
|
Icon="{ext:Icon fa-solid fa-circle-info}"
|
||||||
ToolTip.Tip="{ext:Locale OpenAboutTooltip}" />
|
ToolTip.Tip="{ext:Locale OpenAboutTooltip}" />
|
||||||
<MenuItem
|
<MenuItem
|
||||||
Name="UpdateMenuItem"
|
Name="UpdateMenuItem"
|
||||||
IsEnabled="{Binding CanUpdate}"
|
IsEnabled="{Binding CanUpdate}"
|
||||||
Click="CheckForUpdates"
|
|
||||||
Header="{ext:Locale MenuBarHelpCheckForUpdates}"
|
Header="{ext:Locale MenuBarHelpCheckForUpdates}"
|
||||||
Icon="{ext:Icon mdi-update}"
|
Icon="{ext:Icon mdi-update}"
|
||||||
ToolTip.Tip="{ext:Locale CheckUpdatesTooltip}" />
|
ToolTip.Tip="{ext:Locale CheckUpdatesTooltip}" />
|
||||||
|
<MenuItem
|
||||||
|
Name="CompatibilityListMenuItem"
|
||||||
|
Header="{ext:Locale CompatibilityListOpen}"
|
||||||
|
Icon="{ext:Icon mdi-gamepad}"/>
|
||||||
<Separator />
|
<Separator />
|
||||||
<MenuItem VerticalAlignment="Center" Header="{ext:Locale MenuBarHelpFaqAndGuides}" Icon="{ext:Icon fa-solid fa-question}" >
|
<MenuItem VerticalAlignment="Center" Header="{ext:Locale MenuBarHelpFaqAndGuides}" Icon="{ext:Icon fa-solid fa-question}" >
|
||||||
<MenuItem
|
<MenuItem
|
||||||
Click="MenuItem_OnClick"
|
Name="FaqMenuItem"
|
||||||
Header="{ext:Locale MenuBarHelpFaq}"
|
Header="{ext:Locale MenuBarHelpFaq}"
|
||||||
Icon="{ext:Icon fa-github}"
|
Icon="{ext:Icon fa-github}"
|
||||||
Tag="https://github.com/GreemDev/Ryujinx/wiki/FAQ-and-Troubleshooting"
|
CommandParameter="https://github.com/GreemDev/Ryujinx/wiki/FAQ-and-Troubleshooting"
|
||||||
ToolTip.Tip="{ext:Locale MenuBarHelpFaqTooltip}" />
|
ToolTip.Tip="{ext:Locale MenuBarHelpFaqTooltip}" />
|
||||||
<MenuItem
|
<MenuItem
|
||||||
Click="MenuItem_OnClick"
|
Name="SetupGuideMenuItem"
|
||||||
Header="{ext:Locale MenuBarHelpSetup}"
|
Header="{ext:Locale MenuBarHelpSetup}"
|
||||||
Icon="{ext:Icon fa-github}"
|
Icon="{ext:Icon fa-github}"
|
||||||
Tag="https://github.com/GreemDev/Ryujinx/wiki/Ryujinx-Setup-&-Configuration-Guide"
|
CommandParameter="https://github.com/GreemDev/Ryujinx/wiki/Ryujinx-Setup-&-Configuration-Guide"
|
||||||
ToolTip.Tip="{ext:Locale MenuBarHelpSetupTooltip}" />
|
ToolTip.Tip="{ext:Locale MenuBarHelpSetupTooltip}" />
|
||||||
<MenuItem
|
<MenuItem
|
||||||
Click="MenuItem_OnClick"
|
Name="LdnGuideMenuItem"
|
||||||
Header="{ext:Locale MenuBarHelpMultiplayer}"
|
Header="{ext:Locale MenuBarHelpMultiplayer}"
|
||||||
Icon="{ext:Icon fa-github}"
|
Icon="{ext:Icon fa-github}"
|
||||||
Tag="https://github.com/GreemDev/Ryujinx/wiki/Multiplayer%E2%80%90(LDN%E2%80%90Local%E2%80%90Wireless)%E2%80%90Guide"
|
CommandParameter="https://github.com/GreemDev/Ryujinx/wiki/Multiplayer%E2%80%90(LDN%E2%80%90Local%E2%80%90Wireless)%E2%80%90Guide"
|
||||||
ToolTip.Tip="{ext:Locale MenuBarHelpMultiplayerTooltip}" />
|
ToolTip.Tip="{ext:Locale MenuBarHelpMultiplayerTooltip}" />
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
|
@ -1,13 +1,15 @@
|
|||||||
using Avalonia;
|
using Avalonia;
|
||||||
using Avalonia.Controls;
|
using Avalonia.Controls;
|
||||||
using Avalonia.Interactivity;
|
using Avalonia.Layout;
|
||||||
using Avalonia.Threading;
|
using Avalonia.Threading;
|
||||||
|
using CommunityToolkit.Mvvm.Input;
|
||||||
using Gommon;
|
using Gommon;
|
||||||
using Ryujinx.Ava.Common.Locale;
|
using Ryujinx.Ava.Common.Locale;
|
||||||
using Ryujinx.Ava.UI.Helpers;
|
using Ryujinx.Ava.UI.Helpers;
|
||||||
using Ryujinx.Ava.UI.ViewModels;
|
using Ryujinx.Ava.UI.ViewModels;
|
||||||
using Ryujinx.Ava.UI.Windows;
|
using Ryujinx.Ava.UI.Windows;
|
||||||
using Ryujinx.Ava.Utilities;
|
using Ryujinx.Ava.Utilities;
|
||||||
|
using Ryujinx.Ava.Utilities.Compat;
|
||||||
using Ryujinx.Ava.Utilities.Configuration;
|
using Ryujinx.Ava.Utilities.Configuration;
|
||||||
using Ryujinx.Common;
|
using Ryujinx.Common;
|
||||||
using Ryujinx.Common.Helper;
|
using Ryujinx.Common.Helper;
|
||||||
@ -16,6 +18,7 @@ using Ryujinx.HLE.HOS.Services.Nfc.AmiiboDecryption;
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace Ryujinx.Ava.UI.Views.Main
|
namespace Ryujinx.Ava.UI.Views.Main
|
||||||
{
|
{
|
||||||
@ -33,9 +36,37 @@ namespace Ryujinx.Ava.UI.Views.Main
|
|||||||
|
|
||||||
ToggleFileTypesMenuItem.ItemsSource = GenerateToggleFileTypeItems();
|
ToggleFileTypesMenuItem.ItemsSource = GenerateToggleFileTypeItems();
|
||||||
ChangeLanguageMenuItem.ItemsSource = GenerateLanguageMenuItems();
|
ChangeLanguageMenuItem.ItemsSource = GenerateLanguageMenuItems();
|
||||||
|
|
||||||
|
MiiAppletMenuItem.Command = new AsyncRelayCommand(OpenMiiApplet);
|
||||||
|
CloseRyujinxMenuItem.Command = new RelayCommand(CloseWindow);
|
||||||
|
OpenSettingsMenuItem.Command = new AsyncRelayCommand(OpenSettings);
|
||||||
|
PauseEmulationMenuItem.Command = new RelayCommand(() => ViewModel.AppHost?.Pause());
|
||||||
|
ResumeEmulationMenuItem.Command = new RelayCommand(() => ViewModel.AppHost?.Resume());
|
||||||
|
StopEmulationMenuItem.Command = new AsyncRelayCommand(() => ViewModel.AppHost?.ShowExitPrompt().OrCompleted());
|
||||||
|
CheatManagerMenuItem.Command = new AsyncRelayCommand(OpenCheatManagerForCurrentApp);
|
||||||
|
InstallFileTypesMenuItem.Command = new AsyncRelayCommand(InstallFileTypes);
|
||||||
|
UninstallFileTypesMenuItem.Command = new AsyncRelayCommand(UninstallFileTypes);
|
||||||
|
XciTrimmerMenuItem.Command = new AsyncRelayCommand(() => XCITrimmerWindow.Show(ViewModel));
|
||||||
|
AboutWindowMenuItem.Command = new AsyncRelayCommand(AboutWindow.Show);
|
||||||
|
CompatibilityListMenuItem.Command = new AsyncRelayCommand(CompatibilityList.Show);
|
||||||
|
|
||||||
|
UpdateMenuItem.Command = new AsyncRelayCommand(async () =>
|
||||||
|
{
|
||||||
|
if (Updater.CanUpdate(true))
|
||||||
|
await Updater.BeginUpdateAsync(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
FaqMenuItem.Command =
|
||||||
|
SetupGuideMenuItem.Command =
|
||||||
|
LdnGuideMenuItem.Command = new RelayCommand<string>(OpenHelper.OpenUrl);
|
||||||
|
|
||||||
|
WindowSize720PMenuItem.Command =
|
||||||
|
WindowSize1080PMenuItem.Command =
|
||||||
|
WindowSize1440PMenuItem.Command =
|
||||||
|
WindowSize2160PMenuItem.Command = new RelayCommand<string>(ChangeWindowSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
private CheckBox[] GenerateToggleFileTypeItems() =>
|
private IEnumerable<CheckBox> GenerateToggleFileTypeItems() =>
|
||||||
Enum.GetValues<FileTypes>()
|
Enum.GetValues<FileTypes>()
|
||||||
.Select(it => (FileName: Enum.GetName(it)!, FileType: it))
|
.Select(it => (FileName: Enum.GetName(it)!, FileType: it))
|
||||||
.Select(it =>
|
.Select(it =>
|
||||||
@ -45,15 +76,13 @@ namespace Ryujinx.Ava.UI.Views.Main
|
|||||||
IsChecked = it.FileType.GetConfigValue(ConfigurationState.Instance.UI.ShownFileTypes),
|
IsChecked = it.FileType.GetConfigValue(ConfigurationState.Instance.UI.ShownFileTypes),
|
||||||
Command = MiniCommand.Create(() => Window.ToggleFileType(it.FileName))
|
Command = MiniCommand.Create(() => Window.ToggleFileType(it.FileName))
|
||||||
}
|
}
|
||||||
).ToArray();
|
);
|
||||||
|
|
||||||
private static MenuItem[] GenerateLanguageMenuItems()
|
private static IEnumerable<MenuItem> GenerateLanguageMenuItems()
|
||||||
{
|
{
|
||||||
List<MenuItem> menuItems = new();
|
const string LocalePath = "Ryujinx/Assets/locales.json";
|
||||||
|
|
||||||
string localePath = "Ryujinx/Assets/locales.json";
|
string languageJson = EmbeddedResources.ReadAllText(LocalePath);
|
||||||
|
|
||||||
string languageJson = EmbeddedResources.ReadAllText(localePath);
|
|
||||||
|
|
||||||
LocalesJson locales = JsonHelper.Deserialize(languageJson, LocalesJsonContext.Default.LocalesJson);
|
LocalesJson locales = JsonHelper.Deserialize(languageJson, LocalesJsonContext.Default.LocalesJson);
|
||||||
|
|
||||||
@ -68,20 +97,23 @@ namespace Ryujinx.Ava.UI.Views.Main
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
languageName = locales.Locales[index].Translations[language] == "" ? language : locales.Locales[index].Translations[language];
|
string tr = locales.Locales[index].Translations[language];
|
||||||
|
languageName = string.IsNullOrEmpty(tr)
|
||||||
|
? language
|
||||||
|
: tr;
|
||||||
}
|
}
|
||||||
|
|
||||||
MenuItem menuItem = new()
|
MenuItem menuItem = new()
|
||||||
{
|
{
|
||||||
Padding = new Thickness(10, 0, 0, 0),
|
Padding = new Thickness(15, 0, 0, 0),
|
||||||
Header = " " + languageName,
|
Margin = new Thickness(3, 0, 3, 0),
|
||||||
|
HorizontalAlignment = HorizontalAlignment.Stretch,
|
||||||
|
Header = languageName,
|
||||||
Command = MiniCommand.Create(() => MainWindowViewModel.ChangeLanguage(language))
|
Command = MiniCommand.Create(() => MainWindowViewModel.ChangeLanguage(language))
|
||||||
};
|
};
|
||||||
|
|
||||||
menuItems.Add(menuItem);
|
yield return menuItem;
|
||||||
}
|
}
|
||||||
|
|
||||||
return menuItems.ToArray();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e)
|
protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e)
|
||||||
@ -95,22 +127,7 @@ namespace Ryujinx.Ava.UI.Views.Main
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async void StopEmulation_Click(object sender, RoutedEventArgs e)
|
public async Task OpenSettings()
|
||||||
{
|
|
||||||
await ViewModel.AppHost?.ShowExitPrompt().OrCompleted()!;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void PauseEmulation_Click(object sender, RoutedEventArgs e)
|
|
||||||
{
|
|
||||||
ViewModel.AppHost?.Pause();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void ResumeEmulation_Click(object sender, RoutedEventArgs e)
|
|
||||||
{
|
|
||||||
ViewModel.AppHost?.Resume();
|
|
||||||
}
|
|
||||||
|
|
||||||
public async void OpenSettings(object sender, RoutedEventArgs e)
|
|
||||||
{
|
{
|
||||||
Window.SettingsWindow = new(Window.VirtualFileSystem, Window.ContentManager);
|
Window.SettingsWindow = new(Window.VirtualFileSystem, Window.ContentManager);
|
||||||
|
|
||||||
@ -123,7 +140,7 @@ namespace Ryujinx.Ava.UI.Views.Main
|
|||||||
|
|
||||||
public static readonly AppletMetadata MiiApplet = new("miiEdit", 0x0100000000001009);
|
public static readonly AppletMetadata MiiApplet = new("miiEdit", 0x0100000000001009);
|
||||||
|
|
||||||
public async void OpenMiiApplet(object sender, RoutedEventArgs e)
|
public async Task OpenMiiApplet()
|
||||||
{
|
{
|
||||||
if (MiiApplet.CanStart(ViewModel.ContentManager, out var appData, out var nacpData))
|
if (MiiApplet.CanStart(ViewModel.ContentManager, out var appData, out var nacpData))
|
||||||
{
|
{
|
||||||
@ -131,13 +148,7 @@ namespace Ryujinx.Ava.UI.Views.Main
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async void OpenAmiiboWindow(object sender, RoutedEventArgs e)
|
public async Task OpenCheatManagerForCurrentApp()
|
||||||
=> await ViewModel.OpenAmiiboWindow();
|
|
||||||
|
|
||||||
public async void OpenBinFile(object sender, RoutedEventArgs e)
|
|
||||||
=> await ViewModel.OpenBinFile();
|
|
||||||
|
|
||||||
public async void OpenCheatManagerForCurrentApp(object sender, RoutedEventArgs e)
|
|
||||||
{
|
{
|
||||||
if (!ViewModel.IsGameRunning)
|
if (!ViewModel.IsGameRunning)
|
||||||
return;
|
return;
|
||||||
@ -165,7 +176,7 @@ namespace Ryujinx.Ava.UI.Views.Main
|
|||||||
ViewModel.IsAmiiboBinRequested = ViewModel.IsAmiiboRequested && AmiiboBinReader.HasAmiiboKeyFile;
|
ViewModel.IsAmiiboBinRequested = ViewModel.IsAmiiboRequested && AmiiboBinReader.HasAmiiboKeyFile;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async void InstallFileTypes_Click(object sender, RoutedEventArgs e)
|
private async Task InstallFileTypes()
|
||||||
{
|
{
|
||||||
ViewModel.AreMimeTypesRegistered = FileAssociationHelper.Install();
|
ViewModel.AreMimeTypesRegistered = FileAssociationHelper.Install();
|
||||||
if (ViewModel.AreMimeTypesRegistered)
|
if (ViewModel.AreMimeTypesRegistered)
|
||||||
@ -174,7 +185,7 @@ namespace Ryujinx.Ava.UI.Views.Main
|
|||||||
await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance[LocaleKeys.DialogInstallFileTypesErrorMessage]);
|
await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance[LocaleKeys.DialogInstallFileTypesErrorMessage]);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async void UninstallFileTypes_Click(object sender, RoutedEventArgs e)
|
private async Task UninstallFileTypes()
|
||||||
{
|
{
|
||||||
ViewModel.AreMimeTypesRegistered = !FileAssociationHelper.Uninstall();
|
ViewModel.AreMimeTypesRegistered = !FileAssociationHelper.Uninstall();
|
||||||
if (!ViewModel.AreMimeTypesRegistered)
|
if (!ViewModel.AreMimeTypesRegistered)
|
||||||
@ -183,11 +194,8 @@ namespace Ryujinx.Ava.UI.Views.Main
|
|||||||
await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance[LocaleKeys.DialogUninstallFileTypesErrorMessage]);
|
await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance[LocaleKeys.DialogUninstallFileTypesErrorMessage]);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async void ChangeWindowSize_Click(object sender, RoutedEventArgs e)
|
private void ChangeWindowSize(string resolution)
|
||||||
{
|
{
|
||||||
if (sender is not MenuItem { Tag: string resolution })
|
|
||||||
return;
|
|
||||||
|
|
||||||
(int resolutionWidth, int resolutionHeight) = resolution.Split(' ', 2)
|
(int resolutionWidth, int resolutionHeight) = resolution.Split(' ', 2)
|
||||||
.Into(parts =>
|
.Into(parts =>
|
||||||
(int.Parse(parts[0]), int.Parse(parts[1]))
|
(int.Parse(parts[0]), int.Parse(parts[1]))
|
||||||
@ -200,7 +208,7 @@ namespace Ryujinx.Ava.UI.Views.Main
|
|||||||
double windowWidthScaled = (resolutionWidth * Program.WindowScaleFactor);
|
double windowWidthScaled = (resolutionWidth * Program.WindowScaleFactor);
|
||||||
double windowHeightScaled = ((resolutionHeight + barsHeight) * Program.WindowScaleFactor);
|
double windowHeightScaled = ((resolutionHeight + barsHeight) * Program.WindowScaleFactor);
|
||||||
|
|
||||||
await Dispatcher.UIThread.InvokeAsync(() =>
|
Dispatcher.UIThread.Post(() =>
|
||||||
{
|
{
|
||||||
ViewModel.WindowState = WindowState.Normal;
|
ViewModel.WindowState = WindowState.Normal;
|
||||||
|
|
||||||
@ -208,22 +216,7 @@ namespace Ryujinx.Ava.UI.Views.Main
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public async void CheckForUpdates(object sender, RoutedEventArgs e)
|
public void CloseWindow() => Window.Close();
|
||||||
{
|
|
||||||
if (Updater.CanUpdate(true))
|
|
||||||
await Updater.BeginUpdateAsync(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void MenuItem_OnClick(object sender, RoutedEventArgs e)
|
|
||||||
{
|
|
||||||
if (sender is MenuItem { Tag: string url })
|
|
||||||
OpenHelper.OpenUrl(url);
|
|
||||||
}
|
|
||||||
|
|
||||||
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();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -164,7 +164,7 @@ namespace Ryujinx.Ava.UI.Windows
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ApplicationLibrary_LdnGameDataReceived(object sender, LdnGameDataReceivedEventArgs e)
|
private void ApplicationLibrary_LdnGameDataReceived(LdnGameDataReceivedEventArgs e)
|
||||||
{
|
{
|
||||||
Dispatcher.UIThread.Post(() =>
|
Dispatcher.UIThread.Post(() =>
|
||||||
{
|
{
|
||||||
@ -408,13 +408,10 @@ namespace Ryujinx.Ava.UI.Windows
|
|||||||
{
|
{
|
||||||
StatusBarView.VolumeStatus.Click += VolumeStatus_CheckedChanged;
|
StatusBarView.VolumeStatus.Click += VolumeStatus_CheckedChanged;
|
||||||
|
|
||||||
|
ApplicationGrid.DataContext = ApplicationList.DataContext = ViewModel;
|
||||||
|
|
||||||
ApplicationGrid.ApplicationOpened += Application_Opened;
|
ApplicationGrid.ApplicationOpened += Application_Opened;
|
||||||
|
|
||||||
ApplicationGrid.DataContext = ViewModel;
|
|
||||||
|
|
||||||
ApplicationList.ApplicationOpened += Application_Opened;
|
ApplicationList.ApplicationOpened += Application_Opened;
|
||||||
|
|
||||||
ApplicationList.DataContext = ViewModel;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void SetWindowSizePosition()
|
private void SetWindowSizePosition()
|
||||||
|
@ -1,4 +1,6 @@
|
|||||||
|
using Avalonia;
|
||||||
using Avalonia.Controls;
|
using Avalonia.Controls;
|
||||||
|
using Avalonia.Input;
|
||||||
using FluentAvalonia.Core;
|
using FluentAvalonia.Core;
|
||||||
using FluentAvalonia.UI.Controls;
|
using FluentAvalonia.UI.Controls;
|
||||||
using Ryujinx.Ava.Common.Locale;
|
using Ryujinx.Ava.Common.Locale;
|
||||||
@ -23,6 +25,11 @@ namespace Ryujinx.Ava.UI.Windows
|
|||||||
|
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
Load();
|
Load();
|
||||||
|
|
||||||
|
#if DEBUG
|
||||||
|
this.AttachDevTools(new KeyGesture(Key.F12, KeyModifiers.Alt));
|
||||||
|
#endif
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public SettingsWindow()
|
public SettingsWindow()
|
||||||
|
@ -37,6 +37,8 @@ namespace Ryujinx.Ava.Utilities.AppLibrary
|
|||||||
|
|
||||||
public string TimePlayedString => ValueFormatUtils.FormatTimeSpan(TimePlayed);
|
public string TimePlayedString => ValueFormatUtils.FormatTimeSpan(TimePlayed);
|
||||||
|
|
||||||
|
public bool HasPlayedPreviously => TimePlayedString != string.Empty;
|
||||||
|
|
||||||
public string LastPlayedString => ValueFormatUtils.FormatDateTime(LastPlayed)?.Replace(" ", "\n");
|
public string LastPlayedString => ValueFormatUtils.FormatDateTime(LastPlayed)?.Replace(" ", "\n");
|
||||||
|
|
||||||
public string FileSizeString => ValueFormatUtils.FormatFileSize(FileSize);
|
public string FileSizeString => ValueFormatUtils.FormatFileSize(FileSize);
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
using DynamicData;
|
using DynamicData;
|
||||||
|
using DynamicData.Kernel;
|
||||||
using Gommon;
|
using Gommon;
|
||||||
using LibHac;
|
using LibHac;
|
||||||
using LibHac.Common;
|
using LibHac.Common;
|
||||||
@ -44,7 +45,7 @@ namespace Ryujinx.Ava.Utilities.AppLibrary
|
|||||||
public const string DefaultLanPlayWebHost = "ryuldnweb.vudjun.com";
|
public const string DefaultLanPlayWebHost = "ryuldnweb.vudjun.com";
|
||||||
public Language DesiredLanguage { get; set; }
|
public Language DesiredLanguage { get; set; }
|
||||||
public event EventHandler<ApplicationCountUpdatedEventArgs> ApplicationCountUpdated;
|
public event EventHandler<ApplicationCountUpdatedEventArgs> ApplicationCountUpdated;
|
||||||
public event EventHandler<LdnGameDataReceivedEventArgs> LdnGameDataReceived;
|
public event Action<LdnGameDataReceivedEventArgs> LdnGameDataReceived;
|
||||||
|
|
||||||
public readonly IObservableCache<ApplicationData, ulong> Applications;
|
public readonly IObservableCache<ApplicationData, ulong> Applications;
|
||||||
public readonly IObservableCache<(TitleUpdateModel TitleUpdate, bool IsSelected), TitleUpdateModel> TitleUpdates;
|
public readonly IObservableCache<(TitleUpdateModel TitleUpdate, bool IsSelected), TitleUpdateModel> TitleUpdates;
|
||||||
@ -778,7 +779,7 @@ namespace Ryujinx.Ava.Utilities.AppLibrary
|
|||||||
using HttpClient httpClient = new HttpClient();
|
using HttpClient httpClient = new HttpClient();
|
||||||
string ldnGameDataArrayString = await httpClient.GetStringAsync($"https://{ldnWebHost}/api/public_games");
|
string ldnGameDataArrayString = await httpClient.GetStringAsync($"https://{ldnWebHost}/api/public_games");
|
||||||
ldnGameDataArray = JsonHelper.Deserialize(ldnGameDataArrayString, _ldnDataSerializerContext.IEnumerableLdnGameData);
|
ldnGameDataArray = JsonHelper.Deserialize(ldnGameDataArrayString, _ldnDataSerializerContext.IEnumerableLdnGameData);
|
||||||
LdnGameDataReceived?.Invoke(null, new LdnGameDataReceivedEventArgs
|
LdnGameDataReceived?.Invoke(new LdnGameDataReceivedEventArgs
|
||||||
{
|
{
|
||||||
LdnData = ldnGameDataArray
|
LdnData = ldnGameDataArray
|
||||||
});
|
});
|
||||||
@ -786,7 +787,7 @@ namespace Ryujinx.Ava.Utilities.AppLibrary
|
|||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
Logger.Warning?.Print(LogClass.Application, $"Failed to fetch the public games JSON from the API. Player and game count in the game list will be unavailable.\n{ex.Message}");
|
Logger.Warning?.Print(LogClass.Application, $"Failed to fetch the public games JSON from the API. Player and game count in the game list will be unavailable.\n{ex.Message}");
|
||||||
LdnGameDataReceived?.Invoke(null, new LdnGameDataReceivedEventArgs
|
LdnGameDataReceived?.Invoke(new LdnGameDataReceivedEventArgs
|
||||||
{
|
{
|
||||||
LdnData = Array.Empty<LdnGameData>()
|
LdnData = Array.Empty<LdnGameData>()
|
||||||
});
|
});
|
||||||
@ -794,7 +795,7 @@ namespace Ryujinx.Ava.Utilities.AppLibrary
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
LdnGameDataReceived?.Invoke(null, new LdnGameDataReceivedEventArgs
|
LdnGameDataReceived?.Invoke(new LdnGameDataReceivedEventArgs
|
||||||
{
|
{
|
||||||
LdnData = Array.Empty<LdnGameData>()
|
LdnData = Array.Empty<LdnGameData>()
|
||||||
});
|
});
|
||||||
@ -1069,10 +1070,11 @@ namespace Ryujinx.Ava.Utilities.AppLibrary
|
|||||||
{
|
{
|
||||||
if (update == null) return false;
|
if (update == null) return false;
|
||||||
|
|
||||||
var currentlySelected = TitleUpdates.Items.FindFirst(it =>
|
var currentlySelected = TitleUpdates.Items.FirstOrOptional(it =>
|
||||||
it.TitleUpdate.TitleIdBase == update.TitleIdBase && it.IsSelected);
|
it.TitleUpdate.TitleIdBase == update.TitleIdBase && it.IsSelected);
|
||||||
|
|
||||||
var shouldSelect = currentlySelected.Check(curr => curr.TitleUpdate?.Version < update.Version);
|
var shouldSelect = !currentlySelected.HasValue ||
|
||||||
|
currentlySelected.Value.TitleUpdate.Version < update.Version;
|
||||||
|
|
||||||
_titleUpdates.AddOrUpdate((update, shouldSelect));
|
_titleUpdates.AddOrUpdate((update, shouldSelect));
|
||||||
|
|
||||||
@ -1463,7 +1465,7 @@ namespace Ryujinx.Ava.Utilities.AppLibrary
|
|||||||
TitleUpdatesHelper.LoadTitleUpdatesJson(_virtualFileSystem, application.IdBase);
|
TitleUpdatesHelper.LoadTitleUpdatesJson(_virtualFileSystem, application.IdBase);
|
||||||
it.AddOrUpdate(savedUpdates);
|
it.AddOrUpdate(savedUpdates);
|
||||||
|
|
||||||
var selectedUpdate = savedUpdates.FindFirst(update => update.IsSelected);
|
var selectedUpdate = savedUpdates.FirstOrOptional(update => update.IsSelected);
|
||||||
|
|
||||||
if (TryGetTitleUpdatesFromFile(application.Path, out var bundledUpdates))
|
if (TryGetTitleUpdatesFromFile(application.Path, out var bundledUpdates))
|
||||||
{
|
{
|
||||||
@ -1475,9 +1477,10 @@ namespace Ryujinx.Ava.Utilities.AppLibrary
|
|||||||
if (!savedUpdateLookup.Contains(update))
|
if (!savedUpdateLookup.Contains(update))
|
||||||
{
|
{
|
||||||
bool shouldSelect = false;
|
bool shouldSelect = false;
|
||||||
if (selectedUpdate.Check(su => su.Update?.Version < update.Version))
|
if (!selectedUpdate.HasValue || selectedUpdate.Value.Item1.Version < update.Version)
|
||||||
{
|
{
|
||||||
shouldSelect = true;
|
shouldSelect = true;
|
||||||
|
if (selectedUpdate.HasValue)
|
||||||
_titleUpdates.AddOrUpdate((selectedUpdate.Value.Update, false));
|
_titleUpdates.AddOrUpdate((selectedUpdate.Value.Update, false));
|
||||||
selectedUpdate = (update, true);
|
selectedUpdate = (update, true);
|
||||||
}
|
}
|
||||||
|
@ -32,29 +32,27 @@ namespace Ryujinx.Ava.Utilities
|
|||||||
|
|
||||||
public string GetContentPath(ContentManager contentManager)
|
public string GetContentPath(ContentManager contentManager)
|
||||||
=> (contentManager ?? _contentManager)
|
=> (contentManager ?? _contentManager)
|
||||||
.GetInstalledContentPath(ProgramId, StorageId.BuiltInSystem, NcaContentType.Program);
|
?.GetInstalledContentPath(ProgramId, StorageId.BuiltInSystem, NcaContentType.Program);
|
||||||
|
|
||||||
public bool CanStart(ContentManager contentManager, out ApplicationData appData,
|
public bool CanStart(ContentManager contentManager, out ApplicationData appData,
|
||||||
out BlitStruct<ApplicationControlProperty> appControl)
|
out BlitStruct<ApplicationControlProperty> appControl)
|
||||||
{
|
{
|
||||||
contentManager ??= _contentManager;
|
contentManager ??= _contentManager;
|
||||||
if (contentManager == null)
|
if (contentManager == null)
|
||||||
{
|
goto BadData;
|
||||||
|
|
||||||
|
string contentPath = GetContentPath(contentManager);
|
||||||
|
if (string.IsNullOrEmpty(contentPath))
|
||||||
|
goto BadData;
|
||||||
|
|
||||||
|
appData = new() { Name = Name, Id = ProgramId, Path = GetContentPath(contentManager) };
|
||||||
|
appControl = StructHelpers.CreateCustomNacpData(Name, Version);
|
||||||
|
return true;
|
||||||
|
|
||||||
|
BadData:
|
||||||
appData = null;
|
appData = null;
|
||||||
appControl = new BlitStruct<ApplicationControlProperty>(0);
|
appControl = new BlitStruct<ApplicationControlProperty>(0);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
appData = new() { Name = Name, Id = ProgramId, Path = GetContentPath(contentManager) };
|
|
||||||
|
|
||||||
if (string.IsNullOrEmpty(appData.Path))
|
|
||||||
{
|
|
||||||
appControl = new BlitStruct<ApplicationControlProperty>(0);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
appControl = StructHelpers.CreateCustomNacpData(Name, Version);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
169
src/Ryujinx/Utilities/Compat/CompatibilityCsv.cs
Normal file
169
src/Ryujinx/Utilities/Compat/CompatibilityCsv.cs
Normal file
@ -0,0 +1,169 @@
|
|||||||
|
using Gommon;
|
||||||
|
using Humanizer;
|
||||||
|
using nietras.SeparatedValues;
|
||||||
|
using Ryujinx.Ava.Common.Locale;
|
||||||
|
using Ryujinx.Common.Logging;
|
||||||
|
using System;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace Ryujinx.Ava.Utilities.Compat
|
||||||
|
{
|
||||||
|
public struct ColumnIndices(Func<ReadOnlySpan<char>, int> getIndex)
|
||||||
|
{
|
||||||
|
public const string TitleIdCol = "\"title_id\"";
|
||||||
|
public const string GameNameCol = "\"game_name\"";
|
||||||
|
public const string LabelsCol = "\"labels\"";
|
||||||
|
public const string StatusCol = "\"status\"";
|
||||||
|
public const string LastUpdatedCol = "\"last_updated\"";
|
||||||
|
|
||||||
|
public readonly int TitleId = getIndex(TitleIdCol);
|
||||||
|
public readonly int GameName = getIndex(GameNameCol);
|
||||||
|
public readonly int Labels = getIndex(LabelsCol);
|
||||||
|
public readonly int Status = getIndex(StatusCol);
|
||||||
|
public readonly int LastUpdated = getIndex(LastUpdatedCol);
|
||||||
|
}
|
||||||
|
|
||||||
|
public class CompatibilityCsv
|
||||||
|
{
|
||||||
|
static CompatibilityCsv()
|
||||||
|
{
|
||||||
|
using Stream csvStream = Assembly.GetExecutingAssembly()
|
||||||
|
.GetManifestResourceStream("RyujinxGameCompatibilityList")!;
|
||||||
|
csvStream.Position = 0;
|
||||||
|
|
||||||
|
using SepReader reader = Sep.Reader().From(csvStream);
|
||||||
|
ColumnIndices columnIndices = new(reader.Header.IndexOf);
|
||||||
|
|
||||||
|
Entries = reader
|
||||||
|
.Enumerate(row => new CompatibilityEntry(ref columnIndices, row))
|
||||||
|
.OrderBy(it => it.GameName)
|
||||||
|
.ToArray();
|
||||||
|
|
||||||
|
Logger.Debug?.Print(LogClass.UI, "Compatibility CSV loaded.", "LoadCompatCsv");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static CompatibilityEntry[] Entries { get; private set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class CompatibilityEntry
|
||||||
|
{
|
||||||
|
public CompatibilityEntry(ref ColumnIndices indices, SepReader.Row row)
|
||||||
|
{
|
||||||
|
string titleIdRow = ColStr(row[indices.TitleId]);
|
||||||
|
TitleId = !string.IsNullOrEmpty(titleIdRow)
|
||||||
|
? titleIdRow
|
||||||
|
: default(Optional<string>);
|
||||||
|
|
||||||
|
GameName = ColStr(row[indices.GameName]);
|
||||||
|
|
||||||
|
Labels = ColStr(row[indices.Labels]).Split(';');
|
||||||
|
Status = ColStr(row[indices.Status]).ToLower() switch
|
||||||
|
{
|
||||||
|
"playable" => LocaleKeys.CompatibilityListPlayable,
|
||||||
|
"ingame" => LocaleKeys.CompatibilityListIngame,
|
||||||
|
"menus" => LocaleKeys.CompatibilityListMenus,
|
||||||
|
"boots" => LocaleKeys.CompatibilityListBoots,
|
||||||
|
"nothing" => LocaleKeys.CompatibilityListNothing,
|
||||||
|
_ => null
|
||||||
|
};
|
||||||
|
|
||||||
|
if (DateTime.TryParse(ColStr(row[indices.LastUpdated]), out var dt))
|
||||||
|
LastUpdated = dt;
|
||||||
|
|
||||||
|
return;
|
||||||
|
|
||||||
|
string ColStr(SepReader.Col col) => col.ToString().Trim('"');
|
||||||
|
}
|
||||||
|
|
||||||
|
public string GameName { get; }
|
||||||
|
public Optional<string> TitleId { get; }
|
||||||
|
public string[] Labels { get; }
|
||||||
|
public LocaleKeys? Status { get; }
|
||||||
|
public DateTime LastUpdated { get; }
|
||||||
|
|
||||||
|
public string LocalizedLastUpdated =>
|
||||||
|
LocaleManager.FormatDynamicValue(LocaleKeys.CompatibilityListLastUpdated, LastUpdated.Humanize());
|
||||||
|
|
||||||
|
public string LocalizedStatus => LocaleManager.Instance[Status!.Value];
|
||||||
|
public string FormattedTitleId => TitleId
|
||||||
|
.OrElse(new string(' ', 16));
|
||||||
|
|
||||||
|
public string FormattedIssueLabels => Labels
|
||||||
|
.Select(FormatLabelName)
|
||||||
|
.JoinToString(", ");
|
||||||
|
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
StringBuilder sb = new("CompatibilityEntry: {");
|
||||||
|
sb.Append($"{nameof(GameName)}=\"{GameName}\", ");
|
||||||
|
sb.Append($"{nameof(TitleId)}={TitleId}, ");
|
||||||
|
sb.Append($"{nameof(Labels)}={
|
||||||
|
Labels.FormatCollection(it => $"\"{it}\"", separator: ", ", prefix: "[", suffix: "]")
|
||||||
|
}, ");
|
||||||
|
sb.Append($"{nameof(Status)}=\"{Status}\", ");
|
||||||
|
sb.Append($"{nameof(LastUpdated)}=\"{LastUpdated}\"");
|
||||||
|
sb.Append('}');
|
||||||
|
|
||||||
|
return sb.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string FormatLabelName(string labelName) => labelName.ToLower() switch
|
||||||
|
{
|
||||||
|
"audio" => "Audio",
|
||||||
|
"bug" => "Bug",
|
||||||
|
"cpu" => "CPU",
|
||||||
|
"gpu" => "GPU",
|
||||||
|
"gui" => "GUI",
|
||||||
|
"help wanted" => "Help Wanted",
|
||||||
|
"horizon" => "Horizon",
|
||||||
|
"infra" => "Project Infra",
|
||||||
|
"invalid" => "Invalid",
|
||||||
|
"kernel" => "Kernel",
|
||||||
|
"ldn" => "LDN",
|
||||||
|
"linux" => "Linux",
|
||||||
|
"macos" => "macOS",
|
||||||
|
"question" => "Question",
|
||||||
|
"windows" => "Windows",
|
||||||
|
"graphics-backend:opengl" => "Graphics: OpenGL",
|
||||||
|
"graphics-backend:vulkan" => "Graphics: Vulkan",
|
||||||
|
"ldn-works" => "LDN Works",
|
||||||
|
"ldn-untested" => "LDN Untested",
|
||||||
|
"ldn-broken" => "LDN Broken",
|
||||||
|
"ldn-partial" => "Partial LDN",
|
||||||
|
"nvdec" => "NVDEC",
|
||||||
|
"services" => "NX Services",
|
||||||
|
"services-horizon" => "Horizon OS Services",
|
||||||
|
"slow" => "Runs Slow",
|
||||||
|
"crash" => "Crashes",
|
||||||
|
"deadlock" => "Deadlock",
|
||||||
|
"regression" => "Regression",
|
||||||
|
"opengl" => "OpenGL",
|
||||||
|
"opengl-backend-bug" => "OpenGL Backend Bug",
|
||||||
|
"vulkan-backend-bug" => "Vulkan Backend Bug",
|
||||||
|
"mac-bug" => "Mac-specific Bug(s)",
|
||||||
|
"amd-vendor-bug" => "AMD GPU Bug",
|
||||||
|
"intel-vendor-bug" => "Intel GPU Bug",
|
||||||
|
"loader-allocator" => "Loader Allocator",
|
||||||
|
"audout" => "AudOut",
|
||||||
|
"32-bit" => "32-bit Game",
|
||||||
|
"UE4" => "Unreal Engine 4",
|
||||||
|
"homebrew" => "Homebrew Content",
|
||||||
|
"online-broken" => "Online Broken",
|
||||||
|
_ => Capitalize(labelName)
|
||||||
|
};
|
||||||
|
|
||||||
|
public static string Capitalize(string value)
|
||||||
|
{
|
||||||
|
if (value == string.Empty)
|
||||||
|
return string.Empty;
|
||||||
|
|
||||||
|
char firstChar = value[0];
|
||||||
|
string rest = value[1..];
|
||||||
|
|
||||||
|
return $"{char.ToUpper(firstChar)}{rest}";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
79
src/Ryujinx/Utilities/Compat/CompatibilityList.axaml
Normal file
79
src/Ryujinx/Utilities/Compat/CompatibilityList.axaml
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
<UserControl xmlns="https://github.com/avaloniaui"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
|
xmlns:local="using:Ryujinx.Ava.Utilities.Compat"
|
||||||
|
xmlns:helpers="using:Ryujinx.Ava.UI.Helpers"
|
||||||
|
xmlns:ext="using:Ryujinx.Ava.Common.Markup"
|
||||||
|
xmlns:ui="using:FluentAvalonia.UI.Controls"
|
||||||
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
|
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
||||||
|
x:Class="Ryujinx.Ava.Utilities.Compat.CompatibilityList"
|
||||||
|
x:DataType="local:CompatibilityViewModel">
|
||||||
|
<UserControl.DataContext>
|
||||||
|
<local:CompatibilityViewModel />
|
||||||
|
</UserControl.DataContext>
|
||||||
|
<Grid RowDefinitions="*,Auto,*">
|
||||||
|
<Grid
|
||||||
|
Grid.Row="0"
|
||||||
|
HorizontalAlignment="Center"
|
||||||
|
ColumnDefinitions="Auto,*"
|
||||||
|
Margin="0 0 0 10">
|
||||||
|
<ui:FontIcon
|
||||||
|
Grid.Column="0"
|
||||||
|
Margin="0"
|
||||||
|
HorizontalAlignment="Stretch"
|
||||||
|
FontFamily="avares://FluentAvalonia/Fonts#Symbols"
|
||||||
|
Glyph="{helpers:GlyphValueConverter Important}" />
|
||||||
|
<!-- NOTE: aligning to bottom for better visual alignment with glyph -->
|
||||||
|
<TextBlock
|
||||||
|
Grid.Column="1"
|
||||||
|
Margin="5, 0, 0, 0"
|
||||||
|
FontStyle="Italic"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
TextWrapping="Wrap"
|
||||||
|
Text="{ext:Locale CompatibilityListWarning}" />
|
||||||
|
</Grid>
|
||||||
|
<Grid Grid.Row="1" ColumnDefinitions="*,Auto,Auto">
|
||||||
|
<TextBox Grid.Column="0" HorizontalAlignment="Stretch" Watermark="{ext:Locale CompatibilityListSearchBoxWatermark}" TextChanged="TextBox_OnTextChanged" />
|
||||||
|
<CheckBox Grid.Column="1" Margin="7, 0, 0, 0" IsChecked="{Binding OnlyShowOwnedGames}" />
|
||||||
|
<TextBlock Grid.Column="2" Margin="-10, 0, 0, 0" Text="{ext:Locale CompatibilityListOnlyShowOwnedGames}" />
|
||||||
|
</Grid>
|
||||||
|
<ScrollViewer Grid.Row="2">
|
||||||
|
<ListBox Margin="0,5, 0, 0"
|
||||||
|
Background="Transparent"
|
||||||
|
ItemsSource="{Binding CurrentEntries}">
|
||||||
|
<ListBox.ItemTemplate>
|
||||||
|
<DataTemplate DataType="{x:Type local:CompatibilityEntry}">
|
||||||
|
<Grid Width="750"
|
||||||
|
Margin="5"
|
||||||
|
ColumnDefinitions="Auto,Auto,Auto,*"
|
||||||
|
Background="Transparent"
|
||||||
|
ToolTip.Tip="{Binding LocalizedLastUpdated}">
|
||||||
|
<TextBlock Grid.Column="0"
|
||||||
|
Text="{Binding GameName}"
|
||||||
|
Width="320"
|
||||||
|
TextWrapping="Wrap" />
|
||||||
|
<TextBlock Grid.Column="1"
|
||||||
|
Width="135"
|
||||||
|
Padding="7, 0, 0, 0"
|
||||||
|
FontFamily="{StaticResource JetBrainsMono}"
|
||||||
|
Text="{Binding FormattedTitleId}"
|
||||||
|
TextWrapping="Wrap" />
|
||||||
|
<TextBlock Grid.Column="2"
|
||||||
|
Padding="7, 0"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
Text="{Binding LocalizedStatus}"
|
||||||
|
Width="85"
|
||||||
|
Foreground="{Binding Status, Converter={x:Static helpers:PlayabilityStatusConverter.Shared}}"
|
||||||
|
TextWrapping="NoWrap" />
|
||||||
|
<TextBlock Grid.Column="3"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
Text="{Binding FormattedIssueLabels}"
|
||||||
|
TextWrapping="WrapWithOverflow" />
|
||||||
|
</Grid>
|
||||||
|
</DataTemplate>
|
||||||
|
</ListBox.ItemTemplate>
|
||||||
|
</ListBox>
|
||||||
|
</ScrollViewer>
|
||||||
|
</Grid>
|
||||||
|
</UserControl>
|
56
src/Ryujinx/Utilities/Compat/CompatibilityList.axaml.cs
Normal file
56
src/Ryujinx/Utilities/Compat/CompatibilityList.axaml.cs
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
using Avalonia.Controls;
|
||||||
|
using Avalonia.Styling;
|
||||||
|
using FluentAvalonia.UI.Controls;
|
||||||
|
using nietras.SeparatedValues;
|
||||||
|
using Ryujinx.Ava.Common.Locale;
|
||||||
|
using Ryujinx.Ava.UI.Helpers;
|
||||||
|
using System.IO;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Ryujinx.Ava.Utilities.Compat
|
||||||
|
{
|
||||||
|
public partial class CompatibilityList : UserControl
|
||||||
|
{
|
||||||
|
public static async Task Show()
|
||||||
|
{
|
||||||
|
ContentDialog contentDialog = new()
|
||||||
|
{
|
||||||
|
PrimaryButtonText = string.Empty,
|
||||||
|
SecondaryButtonText = string.Empty,
|
||||||
|
CloseButtonText = LocaleManager.Instance[LocaleKeys.SettingsButtonClose],
|
||||||
|
Content = new CompatibilityList
|
||||||
|
{
|
||||||
|
DataContext = new CompatibilityViewModel(RyujinxApp.MainWindow.ViewModel.ApplicationLibrary)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Style closeButton = new(x => x.Name("CloseButton"));
|
||||||
|
closeButton.Setters.Add(new Setter(WidthProperty, 80d));
|
||||||
|
|
||||||
|
Style closeButtonParent = new(x => x.Name("CommandSpace"));
|
||||||
|
closeButtonParent.Setters.Add(new Setter(HorizontalAlignmentProperty, Avalonia.Layout.HorizontalAlignment.Right));
|
||||||
|
|
||||||
|
contentDialog.Styles.Add(closeButton);
|
||||||
|
contentDialog.Styles.Add(closeButtonParent);
|
||||||
|
|
||||||
|
await ContentDialogHelper.ShowAsync(contentDialog);
|
||||||
|
}
|
||||||
|
|
||||||
|
public CompatibilityList()
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void TextBox_OnTextChanged(object sender, TextChangedEventArgs e)
|
||||||
|
{
|
||||||
|
if (DataContext is not CompatibilityViewModel cvm)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (sender is not TextBox searchBox)
|
||||||
|
return;
|
||||||
|
|
||||||
|
cvm.Search(searchBox.Text);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
58
src/Ryujinx/Utilities/Compat/CompatibilityViewModel.cs
Normal file
58
src/Ryujinx/Utilities/Compat/CompatibilityViewModel.cs
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
using CommunityToolkit.Mvvm.ComponentModel;
|
||||||
|
using ExCSS;
|
||||||
|
using Gommon;
|
||||||
|
using Ryujinx.Ava.Utilities.AppLibrary;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
|
namespace Ryujinx.Ava.Utilities.Compat
|
||||||
|
{
|
||||||
|
public partial class CompatibilityViewModel : ObservableObject
|
||||||
|
{
|
||||||
|
[ObservableProperty] private bool _onlyShowOwnedGames = true;
|
||||||
|
|
||||||
|
private IEnumerable<CompatibilityEntry> _currentEntries = CompatibilityCsv.Entries;
|
||||||
|
private readonly string[] _ownedGameTitleIds = [];
|
||||||
|
private readonly ApplicationLibrary _appLibrary;
|
||||||
|
|
||||||
|
public IEnumerable<CompatibilityEntry> CurrentEntries => OnlyShowOwnedGames
|
||||||
|
? _currentEntries.Where(x =>
|
||||||
|
x.TitleId.Check(tid => _ownedGameTitleIds.ContainsIgnoreCase(tid)))
|
||||||
|
: _currentEntries;
|
||||||
|
|
||||||
|
public CompatibilityViewModel() {}
|
||||||
|
|
||||||
|
public CompatibilityViewModel(ApplicationLibrary appLibrary)
|
||||||
|
{
|
||||||
|
_appLibrary = appLibrary;
|
||||||
|
_ownedGameTitleIds = appLibrary.Applications.Keys.Select(x => x.ToString("X16")).ToArray();
|
||||||
|
|
||||||
|
PropertyChanged += (_, args) =>
|
||||||
|
{
|
||||||
|
if (args.PropertyName is nameof(OnlyShowOwnedGames))
|
||||||
|
OnPropertyChanged(nameof(CurrentEntries));
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Search(string searchTerm)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(searchTerm))
|
||||||
|
{
|
||||||
|
SetEntries(CompatibilityCsv.Entries);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
SetEntries(CompatibilityCsv.Entries.Where(x =>
|
||||||
|
x.GameName.ContainsIgnoreCase(searchTerm)
|
||||||
|
|| x.TitleId.Check(tid => tid.ContainsIgnoreCase(searchTerm))));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SetEntries(IEnumerable<CompatibilityEntry> entries)
|
||||||
|
{
|
||||||
|
#pragma warning disable MVVMTK0034
|
||||||
|
_currentEntries = entries.ToList();
|
||||||
|
#pragma warning restore MVVMTK0034
|
||||||
|
OnPropertyChanged(nameof(CurrentEntries));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,3 +1,5 @@
|
|||||||
|
using Humanizer;
|
||||||
|
using Humanizer.Localisation;
|
||||||
using Ryujinx.Ava.Common.Locale;
|
using Ryujinx.Ava.Common.Locale;
|
||||||
using System;
|
using System;
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
@ -31,7 +33,7 @@ namespace Ryujinx.Ava.Utilities
|
|||||||
Gigabytes = 9,
|
Gigabytes = 9,
|
||||||
Terabytes = 10,
|
Terabytes = 10,
|
||||||
Petabytes = 11,
|
Petabytes = 11,
|
||||||
Exabytes = 12,
|
Exabytes = 12
|
||||||
}
|
}
|
||||||
|
|
||||||
private const double SizeBase10 = 1000;
|
private const double SizeBase10 = 1000;
|
||||||
@ -48,22 +50,24 @@ namespace Ryujinx.Ava.Utilities
|
|||||||
public static string FormatTimeSpan(TimeSpan? timeSpan)
|
public static string FormatTimeSpan(TimeSpan? timeSpan)
|
||||||
{
|
{
|
||||||
if (!timeSpan.HasValue || timeSpan.Value.TotalSeconds < 1)
|
if (!timeSpan.HasValue || timeSpan.Value.TotalSeconds < 1)
|
||||||
{
|
return string.Empty;
|
||||||
// Game was never played
|
|
||||||
return TimeSpan.Zero.ToString("c", CultureInfo.InvariantCulture);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (timeSpan.Value.TotalDays < 1)
|
if (timeSpan.Value.TotalSeconds < 60)
|
||||||
{
|
return timeSpan.Value.Humanize(1,
|
||||||
// Game was played for less than a day
|
countEmptyUnits: false,
|
||||||
return timeSpan.Value.ToString("c", CultureInfo.InvariantCulture);
|
maxUnit: TimeUnit.Second,
|
||||||
}
|
minUnit: TimeUnit.Second);
|
||||||
|
|
||||||
// Game was played for more than a day
|
if (timeSpan.Value.TotalMinutes < 60)
|
||||||
TimeSpan onlyTime = timeSpan.Value.Subtract(TimeSpan.FromDays(timeSpan.Value.Days));
|
return timeSpan.Value.Humanize(1,
|
||||||
string onlyTimeString = onlyTime.ToString("c", CultureInfo.InvariantCulture);
|
countEmptyUnits: false,
|
||||||
|
maxUnit: TimeUnit.Minute,
|
||||||
|
minUnit: TimeUnit.Minute);
|
||||||
|
|
||||||
return $"{timeSpan.Value.Days}d, {onlyTimeString}";
|
return timeSpan.Value.Humanize(1,
|
||||||
|
countEmptyUnits: false,
|
||||||
|
maxUnit: TimeUnit.Hour,
|
||||||
|
minUnit: TimeUnit.Hour);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user