Compare commits

...

50 Commits

Author SHA1 Message Date
IvonWei
8eb53596c2 Merge bd4bfbdad0 into 7664f8cde9 2025-03-14 02:28:49 +00:00
Evan Husted
bd4bfbdad0 Merge branch 'master' into master 2025-03-13 21:28:46 -05:00
Keaton
7664f8cde9 update FFmpeg to 6.1.2 & Windows on ARM support (#702)
As stated in the title, win-arm64 (Windows 11 ARM) has been added to the
workflows, so these builds should automatically compile for this PR and
all other releases going forward.

Also updated the FFmpeg runtimes from 5.0.3 to 6.1.2. macOS (x64/arm64)
is _currently_ excluded from the update until a proper cross-compiling
environment can be set up for these architectures.

Windows 11 ARM users, please test the win-arm64 build for any issues.

---------

Co-authored-by: Evan Husted <greem@greemdev.net>
2025-03-13 18:36:57 -05:00
Evan Husted
7f6c1c5bdf Merge branch 'master' into master 2025-03-13 18:29:36 -05:00
Hack茶ん
ddc00cf2d8 Update Korean translation (#764) 2025-03-13 13:56:47 -05:00
Evan Husted
05b4bd8c61 UI: Use a new repo for amiibo stuff specifically 2025-03-12 14:17:14 -05:00
Milihraim
20111a651c Update Russian (#745) 2025-03-12 00:12:37 -05:00
WilliamWsyHK
659e580f4d Update TChinese translation (#738) 2025-03-11 23:34:57 -05:00
Evan Husted
e6339ac950 fix logo in readme 2025-03-11 21:35:25 -05:00
Daniel Nylander
6b402847d2 Updated Swedish in locales.json (#748) 2025-03-11 05:16:33 -05:00
shinyoyo
3b06f4cb78 Updated Zh-CN Simplified Chinese. (#737) 2025-03-11 01:35:19 -05:00
Evan Husted
cdf60eecc0 small readme update (test) 2025-03-11 06:06:10 +00:00
Evan Husted
1219f329c1 misc: chore: [ci skip] fully fix changelog urls for stable 2025-03-10 01:24:06 -05:00
Evan Husted
d5527f87cb UI: [ci skip] fix changelog urls on new release channel 2025-03-08 21:07:03 -06:00
Evan Husted
fc75dbc8b8 Update release.yml 2025-03-08 20:49:21 -06:00
Evan Husted
04361b864a stable release channel repo 2025-03-08 20:45:47 -06:00
Evan Husted
9d59c7265c Merge branch 'master' into master 2025-03-07 01:37:48 -06:00
madwind
b33fe6facc fix: add missing implementation 2025-03-06 17:02:47 +08:00
IvonWei
ceaaf4137c Merge branch 'Ryubing:master' into master 2025-03-06 16:54:21 +08:00
GabCoolGuy
3f2e189407 Merge branch 'master' into master 2025-03-04 12:39:46 +01:00
Evan Husted
30e790ced4 Merge branch 'master' into master 2025-02-14 00:45:06 -06:00
Evan Husted
5e8af26516 Merge branch 'master' into master 2025-02-04 20:28:40 -06:00
madwind
9356b68f26 Add a '*' to label the virtual controller. 2025-01-13 08:41:32 +08:00
IvonWei
14aafebaa6 Merge branch 'Ryubing:master' into master 2025-01-13 08:33:30 +08:00
Evan Husted
4518666a04 Merge branch 'master' into master 2025-01-10 21:39:41 -06:00
madwind
4399edaa9f Fix typo: SQL_JOYBATTERYUPDATED => SDL_JOYBATTERYUPDATED 2025-01-02 11:50:20 +08:00
Evan Husted
4e77bcb55a Merge branch 'master' into master 2025-01-01 21:19:02 -06:00
IvonWei
3cbd7dc1a1 Merge branch 'Ryubing:master' into master 2024-12-31 19:21:10 +08:00
IvonWei
536f792558 Merge branch 'Ryubing:master' into master 2024-12-30 22:04:08 +08:00
madwind
7a451ab160 fix GetMappedStateSnapshot 2024-12-30 22:01:21 +08:00
IvonWei
99c7c3fb14 Merge branch 'Ryubing:master' into master 2024-12-29 18:37:03 +08:00
Evan Husted
09e7b660f4 Merge branch 'master' into master 2024-12-29 03:42:20 -06:00
madwind
69dfd8c60e fix right Stick 2024-12-29 02:11:31 +08:00
madwind
8e50dd9fa6 fix right JoyCon stick 2024-12-29 01:49:25 +08:00
madwind
68c03051ad For the JoyCon controller, wrap SDL2Gamepad as SDL2JoyCon to use a suitable layout and correct the motion sensing and joystick orientation. 2024-12-29 00:56:03 +08:00
Evan Husted
a837294b11 Merge branch 'master' into master 2024-12-28 06:01:06 -06:00
IvonWei
f1c0cc8076 Merge branch 'Ryubing:master' into master 2024-12-28 09:09:10 +08:00
madwind
6dec7ff8ba fix motionData 2024-12-28 09:07:22 +08:00
madwind
20fdbff964 clean log 2024-12-26 14:47:40 +08:00
IvonWei
e426680cb0 Merge branch 'GreemDev:master' into master 2024-12-26 11:58:50 +08:00
madwind
7863e97cb0 invoke OnGamepadConnected and OnGamepadDisconnected 2024-12-26 11:58:00 +08:00
madwind
c4dea0ee28 add SQL_JOYBATTERYUPDATED , OnJoyBatteryUpdated 2024-12-26 11:54:52 +08:00
IvonWei
e0b6a01e9d Merge branch 'GreemDev:master' into master 2024-12-25 17:00:53 +08:00
madwind
e509ffa716 delay 2000ms before ShowPowerLevel 2024-12-25 16:57:36 +08:00
IvonWei
714c68b548 Merge branch 'GreemDev:master' into master 2024-12-25 10:41:20 +08:00
madwind
fec197d9ec log powerLevel 2024-12-25 10:39:07 +08:00
IvonWei
a4b2feef79 Merge branch 'GreemDev:master' into master 2024-12-23 22:13:47 +08:00
IvonWei
ad7d9d1ce0 Update NpadController.cs
add ?
2024-12-23 18:55:49 +08:00
IvonWei
86f9544910 Update NpadController.cs
back to Debug
2024-12-23 18:54:11 +08:00
madwind
e9ecbd44fc Add a virtual controller to merge Joy-Cons. 2024-12-23 17:57:55 +08:00
17 changed files with 807 additions and 158 deletions

View File

@@ -19,6 +19,7 @@ jobs:
configuration: [Debug, Release]
platform:
- { name: win-x64, os: windows-latest, zip_os_name: win_x64 }
- { name: win-arm64, os: windows-latest, zip_os_name: win_arm64 }
- { name: linux-x64, os: ubuntu-latest, zip_os_name: linux_x64 }
- { name: linux-arm64, os: ubuntu-latest, zip_os_name: linux_arm64 }
- { name: osx-x64, os: macos-13, zip_os_name: osx_x64 }

View File

@@ -62,6 +62,7 @@ jobs:
| Platform | Artifact |
|--|--|
| Windows 64-bit | [Canary Windows Artifact](https://github.com/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }}/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }}/releases/download/${{ steps.version_info.outputs.build_version }}/ryujinx-canary-${{ steps.version_info.outputs.build_version }}-win_x64.zip) |
| Windows ARM 64-bit | [Canary Windows Artifact](https://github.com/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }}/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }}/releases/download/${{ steps.version_info.outputs.build_version }}/ryujinx-canary-${{ steps.version_info.outputs.build_version }}-win_arm64.zip) |
| Linux 64-bit | [Canary Linux Artifact](https://github.com/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }}/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }}/releases/download/${{ steps.version_info.outputs.build_version }}/ryujinx-canary-${{ steps.version_info.outputs.build_version }}-linux_x64.tar.gz) |
| Linux ARM 64-bit | [Canary Linux ARM Artifact](https://github.com/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }}/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }}/releases/download/${{ steps.version_info.outputs.build_version }}/ryujinx-canary-${{ steps.version_info.outputs.build_version }}-linux_arm64.tar.gz) |
| macOS | [Canary macOS Artifact](https://github.com/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }}/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }}/releases/download/${{ steps.version_info.outputs.build_version }}/ryujinx-canary-${{ steps.version_info.outputs.build_version }}-macos_universal.app.tar.gz) |
@@ -79,6 +80,7 @@ jobs:
matrix:
platform:
- { name: win-x64, os: windows-latest, zip_os_name: win_x64 }
- { name: win-arm64, os: windows-latest, zip_os_name: win_arm64 }
- { name: linux-x64, os: ubuntu-latest, zip_os_name: linux_x64 }
- { name: linux-arm64, os: ubuntu-latest, zip_os_name: linux_arm64 }
steps:

View File

@@ -12,7 +12,8 @@ env:
RYUJINX_BASE_VERSION: "1.2"
RYUJINX_TARGET_RELEASE_CHANNEL_NAME: "release"
RYUJINX_TARGET_RELEASE_CHANNEL_OWNER: "Ryubing"
RYUJINX_TARGET_RELEASE_CHANNEL_REPO: "Ryujinx"
RYUJINX_TARGET_RELEASE_CHANNEL_SOURCE_REPO: "Ryujinx"
RYUJINX_TARGET_RELEASE_CHANNEL_REPO: "Stable-Releases"
RELEASE: 1
jobs:
@@ -33,7 +34,7 @@ jobs:
script: |
github.rest.git.createRef({
owner: "${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }}",
repo: "${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }}",
repo: "${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_SOURCE_REPO }}",
ref: 'refs/tags/${{ steps.version_info.outputs.build_version }}',
sha: context.sha
})
@@ -52,7 +53,7 @@ jobs:
| Linux ARM 64-bit | [Stable Linux ARM Artifact](https://github.com/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }}/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }}/releases/download/${{ steps.version_info.outputs.build_version }}/ryujinx-${{ steps.version_info.outputs.build_version }}-linux_arm64.tar.gz) |
| macOS | [Stable macOS Artifact](https://github.com/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }}/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }}/releases/download/${{ steps.version_info.outputs.build_version }}/ryujinx-${{ steps.version_info.outputs.build_version }}-macos_universal.app.tar.gz) |
**Full Changelog**: https://github.com/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }}/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }}/compare/${{ steps.version_info.outputs.prev_build_version }}...${{ steps.version_info.outputs.build_version }}
**Full Changelog**: https://github.com/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }}/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_SOURCE_REPO }}/compare/${{ steps.version_info.outputs.prev_build_version }}...${{ steps.version_info.outputs.build_version }}
omitBodyDuringUpdate: true
owner: ${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }}
repo: ${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }}
@@ -65,6 +66,7 @@ jobs:
matrix:
platform:
- { name: win-x64, os: windows-latest, zip_os_name: win_x64 }
- { name: win-arm64, os: windows-latest, zip_os_name: win_arm64 }
- { name: linux-x64, os: ubuntu-latest, zip_os_name: linux_x64 }
- { name: linux-arm64, os: ubuntu-latest, zip_os_name: linux_arm64 }
steps:
@@ -92,7 +94,7 @@ jobs:
sed -r --in-place 's/\%\%RYUJINX_TARGET_RELEASE_CHANNEL_NAME\%\%/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_NAME }}/g;' src/Ryujinx.Common/ReleaseInformation.cs
sed -r --in-place 's/\%\%RYUJINX_TARGET_RELEASE_CHANNEL_OWNER\%\%/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }}/g;' src/Ryujinx.Common/ReleaseInformation.cs
sed -r --in-place 's/\%\%RYUJINX_TARGET_RELEASE_CHANNEL_REPO\%\%/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }}/g;' src/Ryujinx.Common/ReleaseInformation.cs
sed -r --in-place 's/\%\%RYUJINX_TARGET_RELEASE_CHANNEL_SOURCE_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_CONFIG_FILE_NAME\%\%/Config\.json/g;' src/Ryujinx.Common/ReleaseInformation.cs
shell: bash
@@ -116,6 +118,7 @@ jobs:
if: matrix.platform.os == 'ubuntu-latest'
run: |
pushd publish
rm libarmeilleure-jitsupport.dylib
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
@@ -173,7 +176,7 @@ jobs:
| Linux ARM 64-bit | [Stable Linux ARM Artifact](https://github.com/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }}/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }}/releases/download/${{ steps.version_info.outputs.build_version }}/ryujinx-${{ steps.version_info.outputs.build_version }}-linux_arm64.tar.gz) |
| macOS | [Stable macOS Artifact](https://github.com/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }}/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }}/releases/download/${{ steps.version_info.outputs.build_version }}/ryujinx-${{ steps.version_info.outputs.build_version }}-macos_universal.app.tar.gz) |
**Full Changelog**: https://github.com/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }}/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }}/compare/${{ steps.version_info.outputs.prev_build_version }}...${{ steps.version_info.outputs.build_version }}
**Full Changelog**: https://github.com/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }}/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_SOURCE_REPO }}/compare/${{ steps.version_info.outputs.prev_build_version }}...${{ steps.version_info.outputs.build_version }}
omitBodyDuringUpdate: true
allowUpdates: true
replacesArtifacts: true
@@ -222,7 +225,7 @@ jobs:
sed -r --in-place 's/\%\%RYUJINX_TARGET_RELEASE_CHANNEL_NAME\%\%/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_NAME }}/g;' src/Ryujinx.Common/ReleaseInformation.cs
sed -r --in-place 's/\%\%RYUJINX_TARGET_RELEASE_CHANNEL_OWNER\%\%/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }}/g;' src/Ryujinx.Common/ReleaseInformation.cs
sed -r --in-place 's/\%\%RYUJINX_TARGET_RELEASE_CHANNEL_REPO\%\%/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }}/g;' src/Ryujinx.Common/ReleaseInformation.cs
sed -r --in-place 's/\%\%RYUJINX_TARGET_RELEASE_CHANNEL_SOURCE_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_CONFIG_FILE_NAME\%\%/Config\.json/g;' src/Ryujinx.Common/ReleaseInformation.cs
shell: bash

View File

@@ -39,7 +39,7 @@
<PackageVersion Include="OpenTK.Windowing.GraphicsLibraryFramework" Version="4.8.2" />
<PackageVersion Include="Open.NAT.Core" Version="2.1.0.5" />
<PackageVersion Include="Ryujinx.Audio.OpenAL.Dependencies" Version="1.21.0.1" />
<PackageVersion Include="Ryujinx.Graphics.Nvdec.Dependencies" Version="5.0.3-build14" />
<PackageVersion Include="Ryujinx.Graphics.Nvdec.Dependencies.AllArch" Version="6.1.2-build3" />
<PackageVersion Include="Ryujinx.Graphics.Vulkan.Dependencies.MoltenVK" Version="1.2.0" />
<PackageVersion Include="Ryujinx.SDL2-CS" Version="2.30.0-build32" />
<PackageVersion Include="Gommon" Version="2.7.1.1" />
@@ -53,8 +53,8 @@
<PackageVersion Include="SkiaSharp" Version="2.88.9" />
<PackageVersion Include="SkiaSharp.NativeAssets.Linux" Version="2.88.9" />
<PackageVersion Include="SPB" Version="0.0.4-build32" />
<PackageVersion Include="System.IO.Hashing" Version="9.0.0" />
<PackageVersion Include="System.Management" Version="9.0.0" />
<PackageVersion Include="System.IO.Hashing" Version="9.0.2" />
<PackageVersion Include="System.Management" Version="9.0.2" />
<PackageVersion Include="UnicornEngine.Unicorn" Version="2.0.2-rc1-fb78016" />
</ItemGroup>
</Project>

View File

@@ -1,14 +1,14 @@
<table align="center">
<tr>
<td align="center" width="25%">
<img src="https://raw.githubusercontent.com/GreemDev/ryuassets/refs/heads/main/RyujinxApp_1024.png" alt="Ryujinx" >
<img src="https://raw.githubusercontent.com/Ryubing/Assets/refs/heads/main/RyujinxApp_1024.png" alt="Ryujinx" >
</td>
<td align="center" width="75%">
# Ryujinx
[![Release workflow](https://github.com/Ryubing/Ryujinx/actions/workflows/release.yml/badge.svg)](https://github.com/Ryubing/Ryujinx/actions/workflows/release.yml)
[![Latest release](https://img.shields.io/github/v/release/GreemDev/Ryujinx)](https://github.com/Ryubing/Ryujinx/releases/latest)
[![Latest release](https://img.shields.io/github/v/release/Ryubing/Ryujinx)](https://github.com/Ryubing/Ryujinx/releases/latest)
<br>
[![Canary workflow](https://github.com/Ryubing/Ryujinx/actions/workflows/canary.yml/badge.svg)](https://github.com/Ryubing/Ryujinx/actions/workflows/canary.yml)
[![Latest canary release](https://img.shields.io/github/v/release/Ryubing/Canary-Releases?label=canary)](https://github.com/Ryubing/Canary-Releases/releases/latest)

View File

@@ -2,7 +2,7 @@
<PropertyGroup>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<RuntimeIdentifiers>win-x64;osx-x64;linux-x64</RuntimeIdentifiers>
<RuntimeIdentifiers>win-x64;osx-x64;linux-x64;win-arm64;osx-arm64;linux-arm64</RuntimeIdentifiers>
<DefaultItemExcludes>$(DefaultItemExcludes);._*</DefaultItemExcludes>
</PropertyGroup>
@@ -11,15 +11,15 @@
</ItemGroup>
<ItemGroup>
<ContentWithTargetPath Include="Native\libsoundio\libs\libsoundio.dll" Condition="'$(RuntimeIdentifier)' != 'linux-x64' AND '$(RuntimeIdentifier)' != 'linux-arm64' AND '$(RuntimeIdentifier)' != 'osx-x64'">
<ContentWithTargetPath Include="Native\libsoundio\libs\libsoundio.dll" Condition="'$(RuntimeIdentifier)' != 'linux-x64' AND '$(RuntimeIdentifier)' != 'linux-arm64' AND '$(RuntimeIdentifier)' != 'osx-x64' AND '$(RuntimeIdentifier)' != 'osx-arm64'">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<TargetPath>libsoundio.dll</TargetPath>
</ContentWithTargetPath>
<ContentWithTargetPath Include="Native\libsoundio\libs\libsoundio.dylib" Condition="'$(RuntimeIdentifier)' != 'linux-x64' AND '$(RuntimeIdentifier)' != 'linux-arm64' AND '$(RuntimeIdentifier)' != 'win-x64'">
<ContentWithTargetPath Include="Native\libsoundio\libs\libsoundio.dylib" Condition="'$(RuntimeIdentifier)' != 'linux-x64' AND '$(RuntimeIdentifier)' != 'linux-arm64' AND '$(RuntimeIdentifier)' != 'win-x64' AND '$(RuntimeIdentifier)' != 'win-arm64'">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<TargetPath>libsoundio.dylib</TargetPath>
</ContentWithTargetPath>
<ContentWithTargetPath Include="Native\libsoundio\libs\libsoundio.so" Condition="'$(RuntimeIdentifier)' != 'win-x64' AND '$(RuntimeIdentifier)' != 'osx-x64' AND '$(RuntimeIdentifier)' != 'linux-arm64'">
<ContentWithTargetPath Include="Native\libsoundio\libs\libsoundio.so" Condition="'$(RuntimeIdentifier)' != 'win-x64' AND '$(RuntimeIdentifier)' != 'win-arm64' AND '$(RuntimeIdentifier)' != 'osx-x64' AND '$(RuntimeIdentifier)' != 'osx-arm64' AND '$(RuntimeIdentifier)' != 'linux-arm64'">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<TargetPath>libsoundio.so</TargetPath>
</ContentWithTargetPath>

View File

@@ -37,9 +37,9 @@ namespace Ryujinx.Common
public static string GetChangelogUrl(Version currentVersion, Version newVersion) =>
IsCanaryBuild
? $"https://github.com/{ReleaseChannelOwner}/{ReleaseChannelSourceRepo}/compare/Canary-{currentVersion}...Canary-{newVersion}"
: $"https://github.com/{ReleaseChannelOwner}/{ReleaseChannelSourceRepo}/releases/tag/{newVersion}";
: GetChangelogForVersion(newVersion);
public static string GetChangelogForVersion(Version version) =>
$"https://github.com/{ReleaseChannelOwner}/{ReleaseChannelRepo}/releases/tag/{version}";
$"https://github.com/{ReleaseChannelOwner}/{ReleaseChannelRepo}/releases/{version}";
}
}

View File

@@ -12,8 +12,8 @@ namespace Ryujinx.Graphics.Nvdec.FFmpeg.Native
private static readonly Dictionary<string, (int, int)> _librariesWhitelist = new()
{
{ AvCodecLibraryName, (58, 59) },
{ AvUtilLibraryName, (56, 57) },
{ AvCodecLibraryName, (58, 61) },
{ AvUtilLibraryName, (56, 59) },
};
private static string FormatLibraryNameForCurrentOs(string libraryName, int version)

View File

@@ -1,3 +1,4 @@
using Ryujinx.Common.Logging;
using Ryujinx.SDL2.Common;
using System;
using System.Collections.Generic;
@@ -36,6 +37,7 @@ namespace Ryujinx.Input.SDL2
SDL2Driver.Instance.Initialize();
SDL2Driver.Instance.OnJoyStickConnected += HandleJoyStickConnected;
SDL2Driver.Instance.OnJoystickDisconnected += HandleJoyStickDisconnected;
SDL2Driver.Instance.OnJoyBatteryUpdated += HandleJoyBatteryUpdated;
// Add already connected gamepads
int numJoysticks = SDL_NumJoysticks();
@@ -83,19 +85,30 @@ namespace Ryujinx.Input.SDL2
private void HandleJoyStickDisconnected(int joystickInstanceId)
{
bool joyConPairDisconnected = false;
if (!_gamepadsInstanceIdsMapping.Remove(joystickInstanceId, out string id))
return;
lock (_lock)
{
_gamepadsIds.Remove(id);
if (!SDL2JoyConPair.IsCombinable(_gamepadsIds))
{
_gamepadsIds.Remove(SDL2JoyConPair.Id);
joyConPairDisconnected = true;
}
}
OnGamepadDisconnected?.Invoke(id);
if (joyConPairDisconnected)
{
OnGamepadDisconnected?.Invoke(SDL2JoyConPair.Id);
}
}
private void HandleJoyStickConnected(int joystickDeviceId, int joystickInstanceId)
{
bool joyConPairConnected = false;
if (SDL_IsGameController(joystickDeviceId) == SDL_bool.SDL_TRUE)
{
if (_gamepadsInstanceIdsMapping.ContainsKey(joystickInstanceId))
@@ -120,13 +133,29 @@ namespace Ryujinx.Input.SDL2
_gamepadsIds.Insert(joystickDeviceId, id);
else
_gamepadsIds.Add(id);
if (SDL2JoyConPair.IsCombinable(_gamepadsIds))
{
_gamepadsIds.Remove(SDL2JoyConPair.Id);
_gamepadsIds.Add(SDL2JoyConPair.Id);
joyConPairConnected = true;
}
}
OnGamepadConnected?.Invoke(id);
if (joyConPairConnected)
{
OnGamepadConnected?.Invoke(SDL2JoyConPair.Id);
}
}
}
}
private void HandleJoyBatteryUpdated(int joystickDeviceId, SDL_JoystickPowerLevel powerLevel)
{
Logger.Info?.Print(LogClass.Hid,
$"{SDL_GameControllerNameForIndex(joystickDeviceId)} power level: {powerLevel}");
}
protected virtual void Dispose(bool disposing)
{
if (disposing)
@@ -157,6 +186,14 @@ namespace Ryujinx.Input.SDL2
public IGamepad GetGamepad(string id)
{
if (id == SDL2JoyConPair.Id)
{
lock (_lock)
{
return SDL2JoyConPair.GetGamepad(_gamepadsIds);
}
}
int joystickIndex = GetJoystickIndexByGamepadId(id);
if (joystickIndex == -1)
@@ -165,12 +202,16 @@ namespace Ryujinx.Input.SDL2
}
nint gamepadHandle = SDL_GameControllerOpen(joystickIndex);
if (gamepadHandle == nint.Zero)
{
return null;
}
if (SDL_GameControllerName(gamepadHandle).StartsWith(SDL2JoyCon.Prefix))
{
return new SDL2JoyCon(gamepadHandle, id);
}
return new SDL2Gamepad(gamepadHandle, id);
}

View File

@@ -0,0 +1,413 @@
using Ryujinx.Common.Configuration.Hid;
using Ryujinx.Common.Configuration.Hid.Controller;
using Ryujinx.Common.Logging;
using System;
using System.Collections.Generic;
using System.Numerics;
using System.Threading;
using static SDL2.SDL;
namespace Ryujinx.Input.SDL2
{
internal class SDL2JoyCon : IGamepad
{
private bool HasConfiguration => _configuration != null;
private readonly record struct ButtonMappingEntry(GamepadButtonInputId To, GamepadButtonInputId From)
{
public bool IsValid => To is not GamepadButtonInputId.Unbound && From is not GamepadButtonInputId.Unbound;
}
private StandardControllerInputConfig _configuration;
private readonly Dictionary<GamepadButtonInputId,SDL_GameControllerButton> _leftButtonsDriverMapping = new()
{
{ GamepadButtonInputId.LeftStick , SDL_GameControllerButton.SDL_CONTROLLER_BUTTON_LEFTSTICK },
{GamepadButtonInputId.DpadUp ,SDL_GameControllerButton.SDL_CONTROLLER_BUTTON_Y},
{GamepadButtonInputId.DpadDown ,SDL_GameControllerButton.SDL_CONTROLLER_BUTTON_A},
{GamepadButtonInputId.DpadLeft ,SDL_GameControllerButton.SDL_CONTROLLER_BUTTON_B},
{GamepadButtonInputId.DpadRight ,SDL_GameControllerButton.SDL_CONTROLLER_BUTTON_X},
{GamepadButtonInputId.Minus ,SDL_GameControllerButton.SDL_CONTROLLER_BUTTON_START},
{GamepadButtonInputId.LeftShoulder,SDL_GameControllerButton.SDL_CONTROLLER_BUTTON_PADDLE2},
{GamepadButtonInputId.LeftTrigger,SDL_GameControllerButton.SDL_CONTROLLER_BUTTON_PADDLE4},
{GamepadButtonInputId.SingleRightTrigger0,SDL_GameControllerButton.SDL_CONTROLLER_BUTTON_RIGHTSHOULDER},
{GamepadButtonInputId.SingleLeftTrigger0,SDL_GameControllerButton.SDL_CONTROLLER_BUTTON_LEFTSHOULDER},
};
private readonly Dictionary<GamepadButtonInputId,SDL_GameControllerButton> _rightButtonsDriverMapping = new()
{
{GamepadButtonInputId.RightStick,SDL_GameControllerButton.SDL_CONTROLLER_BUTTON_LEFTSTICK},
{GamepadButtonInputId.A,SDL_GameControllerButton.SDL_CONTROLLER_BUTTON_B},
{GamepadButtonInputId.B,SDL_GameControllerButton.SDL_CONTROLLER_BUTTON_Y},
{GamepadButtonInputId.X,SDL_GameControllerButton.SDL_CONTROLLER_BUTTON_A},
{GamepadButtonInputId.Y,SDL_GameControllerButton.SDL_CONTROLLER_BUTTON_X},
{GamepadButtonInputId.Plus,SDL_GameControllerButton.SDL_CONTROLLER_BUTTON_START},
{GamepadButtonInputId.RightShoulder,SDL_GameControllerButton.SDL_CONTROLLER_BUTTON_PADDLE1},
{GamepadButtonInputId.RightTrigger,SDL_GameControllerButton.SDL_CONTROLLER_BUTTON_PADDLE3},
{GamepadButtonInputId.SingleRightTrigger1,SDL_GameControllerButton.SDL_CONTROLLER_BUTTON_RIGHTSHOULDER},
{GamepadButtonInputId.SingleLeftTrigger1,SDL_GameControllerButton.SDL_CONTROLLER_BUTTON_LEFTSHOULDER}
};
private readonly Dictionary<GamepadButtonInputId, SDL_GameControllerButton> _buttonsDriverMapping;
private readonly Lock _userMappingLock = new();
private readonly List<ButtonMappingEntry> _buttonsUserMapping;
private readonly StickInputId[] _stickUserMapping = new StickInputId[(int)StickInputId.Count]
{
StickInputId.Unbound, StickInputId.Left, StickInputId.Right,
};
public GamepadFeaturesFlag Features { get; }
private nint _gamepadHandle;
private enum JoyConType
{
Left, Right
}
public const string Prefix = "Nintendo Switch Joy-Con";
public const string LeftName = "Nintendo Switch Joy-Con (L)";
public const string RightName = "Nintendo Switch Joy-Con (R)";
private readonly JoyConType _joyConType;
public SDL2JoyCon(nint gamepadHandle, string driverId)
{
_gamepadHandle = gamepadHandle;
_buttonsUserMapping = new List<ButtonMappingEntry>(10);
Name = SDL_GameControllerName(_gamepadHandle);
Id = driverId;
Features = GetFeaturesFlag();
// Enable motion tracking
if (Features.HasFlag(GamepadFeaturesFlag.Motion))
{
if (SDL_GameControllerSetSensorEnabled(_gamepadHandle, SDL_SensorType.SDL_SENSOR_ACCEL,
SDL_bool.SDL_TRUE) != 0)
{
Logger.Error?.Print(LogClass.Hid,
$"Could not enable data reporting for SensorType {SDL_SensorType.SDL_SENSOR_ACCEL}.");
}
if (SDL_GameControllerSetSensorEnabled(_gamepadHandle, SDL_SensorType.SDL_SENSOR_GYRO,
SDL_bool.SDL_TRUE) != 0)
{
Logger.Error?.Print(LogClass.Hid,
$"Could not enable data reporting for SensorType {SDL_SensorType.SDL_SENSOR_GYRO}.");
}
}
switch (Name)
{
case LeftName:
{
_buttonsDriverMapping = _leftButtonsDriverMapping;
_joyConType = JoyConType.Left;
break;
}
case RightName:
{
_buttonsDriverMapping = _rightButtonsDriverMapping;
_joyConType = JoyConType.Right;
break;
}
}
}
private GamepadFeaturesFlag GetFeaturesFlag()
{
GamepadFeaturesFlag result = GamepadFeaturesFlag.None;
if (SDL_GameControllerHasSensor(_gamepadHandle, SDL_SensorType.SDL_SENSOR_ACCEL) == SDL_bool.SDL_TRUE &&
SDL_GameControllerHasSensor(_gamepadHandle, SDL_SensorType.SDL_SENSOR_GYRO) == SDL_bool.SDL_TRUE)
{
result |= GamepadFeaturesFlag.Motion;
}
int error = SDL_GameControllerRumble(_gamepadHandle, 0, 0, 100);
if (error == 0)
{
result |= GamepadFeaturesFlag.Rumble;
}
return result;
}
public string Id { get; }
public string Name { get; }
public bool IsConnected => SDL_GameControllerGetAttached(_gamepadHandle) == SDL_bool.SDL_TRUE;
protected virtual void Dispose(bool disposing)
{
if (disposing && _gamepadHandle != nint.Zero)
{
SDL_GameControllerClose(_gamepadHandle);
_gamepadHandle = nint.Zero;
}
}
public void Dispose()
{
Dispose(true);
}
public void SetTriggerThreshold(float triggerThreshold)
{
}
public void Rumble(float lowFrequency, float highFrequency, uint durationMs)
{
if (!Features.HasFlag(GamepadFeaturesFlag.Rumble))
return;
ushort lowFrequencyRaw = (ushort)(lowFrequency * ushort.MaxValue);
ushort highFrequencyRaw = (ushort)(highFrequency * ushort.MaxValue);
if (durationMs == uint.MaxValue)
{
if (SDL_GameControllerRumble(_gamepadHandle, lowFrequencyRaw, highFrequencyRaw, SDL_HAPTIC_INFINITY) !=
0)
Logger.Error?.Print(LogClass.Hid, "Rumble is not supported on this game controller.");
}
else if (durationMs > SDL_HAPTIC_INFINITY)
{
Logger.Error?.Print(LogClass.Hid, $"Unsupported rumble duration {durationMs}");
}
else
{
if (SDL_GameControllerRumble(_gamepadHandle, lowFrequencyRaw, highFrequencyRaw, durationMs) != 0)
Logger.Error?.Print(LogClass.Hid, "Rumble is not supported on this game controller.");
}
}
public Vector3 GetMotionData(MotionInputId inputId)
{
SDL_SensorType sensorType = inputId switch
{
MotionInputId.Accelerometer => SDL_SensorType.SDL_SENSOR_ACCEL,
MotionInputId.Gyroscope => SDL_SensorType.SDL_SENSOR_GYRO,
_ => SDL_SensorType.SDL_SENSOR_INVALID
};
if (!Features.HasFlag(GamepadFeaturesFlag.Motion) || sensorType is SDL_SensorType.SDL_SENSOR_INVALID)
return Vector3.Zero;
const int ElementCount = 3;
unsafe
{
float* values = stackalloc float[ElementCount];
int result = SDL_GameControllerGetSensorData(_gamepadHandle, sensorType, (nint)values, ElementCount);
if (result != 0)
return Vector3.Zero;
Vector3 value = _joyConType switch
{
JoyConType.Left => new Vector3(-values[2], values[1], values[0]),
JoyConType.Right => new Vector3(values[2], values[1], -values[0])
};
return inputId switch
{
MotionInputId.Gyroscope => RadToDegree(value),
MotionInputId.Accelerometer => GsToMs2(value),
_ => value
};
}
}
private static Vector3 RadToDegree(Vector3 rad) => rad * (180 / MathF.PI);
private static Vector3 GsToMs2(Vector3 gs) => gs / SDL_STANDARD_GRAVITY;
public void SetConfiguration(InputConfig configuration)
{
lock (_userMappingLock)
{
_configuration = (StandardControllerInputConfig)configuration;
_buttonsUserMapping.Clear();
// First update sticks
_stickUserMapping[(int)StickInputId.Left] = (StickInputId)_configuration.LeftJoyconStick.Joystick;
_stickUserMapping[(int)StickInputId.Right] = (StickInputId)_configuration.RightJoyconStick.Joystick;
switch (_joyConType)
{
case JoyConType.Left:
_buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.LeftStick, (GamepadButtonInputId)_configuration.LeftJoyconStick.StickButton));
_buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.DpadUp, (GamepadButtonInputId)_configuration.LeftJoycon.DpadUp));
_buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.DpadDown, (GamepadButtonInputId)_configuration.LeftJoycon.DpadDown));
_buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.DpadLeft, (GamepadButtonInputId)_configuration.LeftJoycon.DpadLeft));
_buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.DpadRight, (GamepadButtonInputId)_configuration.LeftJoycon.DpadRight));
_buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.Minus, (GamepadButtonInputId)_configuration.LeftJoycon.ButtonMinus));
_buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.LeftShoulder, (GamepadButtonInputId)_configuration.LeftJoycon.ButtonL));
_buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.LeftTrigger, (GamepadButtonInputId)_configuration.LeftJoycon.ButtonZl));
_buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.SingleRightTrigger0, (GamepadButtonInputId)_configuration.LeftJoycon.ButtonSr));
_buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.SingleLeftTrigger0, (GamepadButtonInputId)_configuration.LeftJoycon.ButtonSl));
break;
case JoyConType.Right:
_buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.RightStick, (GamepadButtonInputId)_configuration.RightJoyconStick.StickButton));
_buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.A, (GamepadButtonInputId)_configuration.RightJoycon.ButtonA));
_buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.B, (GamepadButtonInputId)_configuration.RightJoycon.ButtonB));
_buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.X, (GamepadButtonInputId)_configuration.RightJoycon.ButtonX));
_buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.Y, (GamepadButtonInputId)_configuration.RightJoycon.ButtonY));
_buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.Plus, (GamepadButtonInputId)_configuration.RightJoycon.ButtonPlus));
_buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.RightShoulder, (GamepadButtonInputId)_configuration.RightJoycon.ButtonR));
_buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.RightTrigger, (GamepadButtonInputId)_configuration.RightJoycon.ButtonZr));
_buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.SingleRightTrigger1, (GamepadButtonInputId)_configuration.RightJoycon.ButtonSr));
_buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.SingleLeftTrigger1, (GamepadButtonInputId)_configuration.RightJoycon.ButtonSl));
break;
default:
throw new ArgumentOutOfRangeException();
}
SetTriggerThreshold(_configuration.TriggerThreshold);
}
}
public void SetLed(uint packedRgb)
{
}
public GamepadStateSnapshot GetStateSnapshot()
{
return IGamepad.GetStateSnapshot(this);
}
public GamepadStateSnapshot GetMappedStateSnapshot()
{
GamepadStateSnapshot rawState = GetStateSnapshot();
GamepadStateSnapshot result = default;
lock (_userMappingLock)
{
if (_buttonsUserMapping.Count == 0)
return rawState;
// ReSharper disable once ForeachCanBePartlyConvertedToQueryUsingAnotherGetEnumerator
foreach (ButtonMappingEntry entry in _buttonsUserMapping)
{
if (!entry.IsValid)
continue;
// Do not touch state of button already pressed
if (!result.IsPressed(entry.To))
{
result.SetPressed(entry.To, rawState.IsPressed(entry.From));
}
}
(float leftStickX, float leftStickY) = rawState.GetStick(_stickUserMapping[(int)StickInputId.Left]);
(float rightStickX, float rightStickY) = rawState.GetStick(_stickUserMapping[(int)StickInputId.Right]);
result.SetStick(StickInputId.Left, leftStickX, leftStickY);
result.SetStick(StickInputId.Right, rightStickX, rightStickY);
}
return result;
}
private static float ConvertRawStickValue(short value)
{
const float ConvertRate = 1.0f / (short.MaxValue + 0.5f);
return value * ConvertRate;
}
private JoyconConfigControllerStick<GamepadInputId, Common.Configuration.Hid.Controller.StickInputId>
GetLogicalJoyStickConfig(StickInputId inputId)
{
switch (inputId)
{
case StickInputId.Left:
if (_configuration.RightJoyconStick.Joystick ==
Common.Configuration.Hid.Controller.StickInputId.Left)
return _configuration.RightJoyconStick;
else
return _configuration.LeftJoyconStick;
case StickInputId.Right:
if (_configuration.LeftJoyconStick.Joystick ==
Common.Configuration.Hid.Controller.StickInputId.Right)
return _configuration.LeftJoyconStick;
else
return _configuration.RightJoyconStick;
}
return null;
}
public (float, float) GetStick(StickInputId inputId)
{
if (inputId == StickInputId.Unbound)
return (0.0f, 0.0f);
if (inputId == StickInputId.Left && _joyConType == JoyConType.Right || inputId == StickInputId.Right && _joyConType == JoyConType.Left)
{
return (0.0f, 0.0f);
}
(short stickX, short stickY) = GetStickXY();
float resultX = ConvertRawStickValue(stickX);
float resultY = -ConvertRawStickValue(stickY);
if (HasConfiguration)
{
var joyconStickConfig = GetLogicalJoyStickConfig(inputId);
if (joyconStickConfig != null)
{
if (joyconStickConfig.InvertStickX)
resultX = -resultX;
if (joyconStickConfig.InvertStickY)
resultY = -resultY;
if (joyconStickConfig.Rotate90CW)
{
float temp = resultX;
resultX = resultY;
resultY = -temp;
}
}
}
return inputId switch
{
StickInputId.Left when _joyConType == JoyConType.Left => (resultY, -resultX),
StickInputId.Right when _joyConType == JoyConType.Right => (-resultY, resultX),
_ => (0.0f, 0.0f)
};
}
private (short, short) GetStickXY()
{
return (
SDL_GameControllerGetAxis(_gamepadHandle, SDL_GameControllerAxis.SDL_CONTROLLER_AXIS_LEFTX),
SDL_GameControllerGetAxis(_gamepadHandle, SDL_GameControllerAxis.SDL_CONTROLLER_AXIS_LEFTY));
}
public bool IsPressed(GamepadButtonInputId inputId)
{
if (!_buttonsDriverMapping.TryGetValue(inputId, out var button))
{
return false;
}
return SDL_GameControllerGetButton(_gamepadHandle, button) == 1;
}
}
}

View File

@@ -0,0 +1,146 @@
using Ryujinx.Common.Configuration.Hid;
using Ryujinx.Common.Configuration.Hid.Controller;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Numerics;
using static SDL2.SDL;
namespace Ryujinx.Input.SDL2
{
internal class SDL2JoyConPair(IGamepad left, IGamepad right) : IGamepad
{
private StandardControllerInputConfig _configuration;
private readonly StickInputId[] _stickUserMapping =
[
StickInputId.Unbound,
StickInputId.Left,
StickInputId.Right
];
public GamepadFeaturesFlag Features => (left?.Features ?? GamepadFeaturesFlag.None) |
(right?.Features ?? GamepadFeaturesFlag.None);
public const string Id = "JoyConPair";
string IGamepad.Id => Id;
public string Name => "* Nintendo Switch Joy-Con (L/R)";
public bool IsConnected => left is { IsConnected: true } && right is { IsConnected: true };
public void Dispose()
{
left?.Dispose();
right?.Dispose();
}
public GamepadStateSnapshot GetMappedStateSnapshot()
{
return GetStateSnapshot();
}
public Vector3 GetMotionData(MotionInputId inputId)
{
return inputId switch
{
MotionInputId.Accelerometer or
MotionInputId.Gyroscope => left.GetMotionData(inputId),
MotionInputId.SecondAccelerometer => right.GetMotionData(MotionInputId.Accelerometer),
MotionInputId.SecondGyroscope => right.GetMotionData(MotionInputId.Gyroscope),
_ => Vector3.Zero
};
}
public GamepadStateSnapshot GetStateSnapshot()
{
return IGamepad.GetStateSnapshot(this);
}
public (float, float) GetStick(StickInputId inputId)
{
return inputId switch
{
StickInputId.Left => left.GetStick(StickInputId.Left),
StickInputId.Right => right.GetStick(StickInputId.Right),
_ => (0, 0)
};
}
public bool IsPressed(GamepadButtonInputId inputId)
{
return left.IsPressed(inputId) || right.IsPressed(inputId);
}
public void Rumble(float lowFrequency, float highFrequency, uint durationMs)
{
if (lowFrequency != 0)
{
right.Rumble(lowFrequency, lowFrequency, durationMs);
}
if (highFrequency != 0)
{
left.Rumble(highFrequency, highFrequency, durationMs);
}
if (lowFrequency == 0 && highFrequency == 0)
{
left.Rumble(0, 0, durationMs);
right.Rumble(0, 0, durationMs);
}
}
public void SetConfiguration(InputConfig configuration)
{
left.SetConfiguration(configuration);
right.SetConfiguration(configuration);
}
public void SetLed(uint packedRgb)
{
}
public void SetTriggerThreshold(float triggerThreshold)
{
left.SetTriggerThreshold(triggerThreshold);
right.SetTriggerThreshold(triggerThreshold);
}
public static bool IsCombinable(List<string> gamepadsIds)
{
(int leftIndex, int rightIndex) = DetectJoyConPair(gamepadsIds);
return leftIndex >= 0 && rightIndex >= 0;
}
private static (int leftIndex, int rightIndex) DetectJoyConPair(List<string> gamepadsIds)
{
var gamepadNames = gamepadsIds.Where(gamepadId => gamepadId != Id)
.Select((_, index) => SDL_GameControllerNameForIndex(index)).ToList();
int leftIndex = gamepadNames.IndexOf(SDL2JoyCon.LeftName);
int rightIndex = gamepadNames.IndexOf(SDL2JoyCon.RightName);
return (leftIndex, rightIndex);
}
public static IGamepad GetGamepad(List<string> gamepadsIds)
{
(int leftIndex, int rightIndex) = DetectJoyConPair(gamepadsIds);
if (leftIndex == -1 || rightIndex == -1)
{
return null;
}
nint leftGamepadHandle = SDL_GameControllerOpen(leftIndex);
nint rightGamepadHandle = SDL_GameControllerOpen(rightIndex);
if (leftGamepadHandle == nint.Zero || rightGamepadHandle == nint.Zero)
{
return null;
}
return new SDL2JoyConPair(new SDL2JoyCon(leftGamepadHandle, gamepadsIds[leftIndex]),
new SDL2JoyCon(rightGamepadHandle, gamepadsIds[rightIndex]));
}
}
}

View File

@@ -269,6 +269,7 @@ namespace Ryujinx.Input.HLE
if (motionConfig.MotionBackend != MotionInputBackendType.CemuHook)
{
_leftMotionInput = new MotionInput();
_rightMotionInput = new MotionInput();
}
else
{
@@ -301,7 +302,20 @@ namespace Ryujinx.Input.HLE
if (controllerConfig.ControllerType == ConfigControllerType.JoyconPair)
{
_rightMotionInput = _leftMotionInput;
if (gamepad.Id== "JoyConPair")
{
Vector3 rightAccelerometer = gamepad.GetMotionData(MotionInputId.SecondAccelerometer);
Vector3 rightGyroscope = gamepad.GetMotionData(MotionInputId.SecondGyroscope);
rightAccelerometer = new Vector3(rightAccelerometer.X, -rightAccelerometer.Z, rightAccelerometer.Y);
rightGyroscope = new Vector3(rightGyroscope.X, -rightGyroscope.Z, rightGyroscope.Y);
_rightMotionInput.Update(rightAccelerometer, rightGyroscope, (ulong)PerformanceCounter.ElapsedNanoseconds / 1000, controllerConfig.Motion.Sensitivity, (float)controllerConfig.Motion.GyroDeadzone);
}
else
{
_rightMotionInput = _leftMotionInput;
}
}
}
}
@@ -336,6 +350,7 @@ namespace Ryujinx.Input.HLE
// Reset states
State = default;
_leftMotionInput = null;
_rightMotionInput = null;
}
}

View File

@@ -21,5 +21,17 @@ namespace Ryujinx.Input
/// </summary>
/// <remarks>Values are in degrees</remarks>
Gyroscope,
/// <summary>
/// Second accelerometer.
/// </summary>
/// <remarks>Values are in m/s^2</remarks>
SecondAccelerometer,
/// <summary>
/// Second gyroscope.
/// </summary>
/// <remarks>Values are in degrees</remarks>
SecondGyroscope
}
}

View File

@@ -25,14 +25,17 @@ namespace Ryujinx.SDL2.Common
public static Action<Action> MainThreadDispatcher { get; set; }
private const uint SdlInitFlags = SDL_INIT_EVENTS | SDL_INIT_GAMECONTROLLER | SDL_INIT_JOYSTICK | SDL_INIT_AUDIO | SDL_INIT_VIDEO;
private const uint SdlInitFlags = SDL_INIT_EVENTS | SDL_INIT_GAMECONTROLLER | SDL_INIT_JOYSTICK |
SDL_INIT_AUDIO | SDL_INIT_VIDEO;
private bool _isRunning;
private uint _refereceCount;
private Thread _worker;
private const uint SDL_JOYBATTERYUPDATED = 1543;
public event Action<int, int> OnJoyStickConnected;
public event Action<int> OnJoystickDisconnected;
public event Action<int, SDL_JoystickPowerLevel> OnJoyBatteryUpdated;
private ConcurrentDictionary<uint, Action<SDL_Event>> _registeredWindowHandlers;
@@ -78,12 +81,14 @@ namespace Ryujinx.SDL2.Common
// First ensure that we only enable joystick events (for connected/disconnected).
if (SDL_GameControllerEventState(SDL_IGNORE) != SDL_IGNORE)
{
Logger.Error?.PrintMsg(LogClass.Application, "Couldn't change the state of game controller events.");
Logger.Error?.PrintMsg(LogClass.Application,
"Couldn't change the state of game controller events.");
}
if (SDL_JoystickEventState(SDL_ENABLE) < 0)
{
Logger.Error?.PrintMsg(LogClass.Application, $"Failed to enable joystick event polling: {SDL_GetError()}");
Logger.Error?.PrintMsg(LogClass.Application,
$"Failed to enable joystick event polling: {SDL_GetError()}");
}
// Disable all joysticks information, we don't need them no need to flood the event queue for that.
@@ -143,7 +148,12 @@ namespace Ryujinx.SDL2.Common
OnJoystickDisconnected?.Invoke(evnt.cbutton.which);
}
else if (evnt.type is SDL_EventType.SDL_WINDOWEVENT or SDL_EventType.SDL_MOUSEBUTTONDOWN or SDL_EventType.SDL_MOUSEBUTTONUP)
else if ((uint)evnt.type == SDL_JOYBATTERYUPDATED)
{
OnJoyBatteryUpdated?.Invoke(evnt.cbutton.which, (SDL_JoystickPowerLevel)evnt.user.code);
}
else if (evnt.type is SDL_EventType.SDL_WINDOWEVENT or SDL_EventType.SDL_MOUSEBUTTONDOWN
or SDL_EventType.SDL_MOUSEBUTTONUP)
{
if (_registeredWindowHandlers.TryGetValue(evnt.window.windowID, out Action<SDL_Event> handler))
{

View File

@@ -84,7 +84,7 @@
"he_IL": "",
"it_IT": "Applet Editor Mii",
"ja_JP": "",
"ko_KR": "Mii 편집 애플릿",
"ko_KR": "애플릿 Mii 편집",
"no_NO": "Mii-redigeringsapplet",
"pl_PL": "",
"pt_BR": "Editor de Mii",
@@ -459,7 +459,7 @@
"he_IL": "",
"it_IT": "",
"ja_JP": "",
"ko_KR": "",
"ko_KR": "스크린샷 폴더 열기",
"no_NO": "",
"pl_PL": "",
"pt_BR": "Abrir Pasta de Capturas de Tela",
@@ -469,7 +469,7 @@
"tr_TR": "",
"uk_UA": "Відкрити теку скріншотів",
"zh_CN": "打开截图文件夹",
"zh_TW": ""
"zh_TW": "開啟螢幕擷取畫面資料夾"
}
},
{
@@ -619,7 +619,7 @@
"tr_TR": "",
"uk_UA": "Запускати ігри з прихованим інтерфейсом",
"zh_CN": "启动游戏时隐藏 UI",
"zh_TW": ""
"zh_TW": "開啟遊戲時隱藏 UI"
}
},
{
@@ -1559,7 +1559,7 @@
"he_IL": "",
"it_IT": "",
"ja_JP": "",
"ko_KR": "",
"ko_KR": "{0}에서 개발",
"no_NO": "Utviklet av {0}",
"pl_PL": "",
"pt_BR": "Desenvolvido por {0}",
@@ -1569,7 +1569,7 @@
"tr_TR": "",
"uk_UA": "Розроблено: {0}",
"zh_CN": "由 {0} 开发",
"zh_TW": ""
"zh_TW": "由 {0} 開發"
}
},
{
@@ -1664,7 +1664,7 @@
"pl_PL": "Rozszerzenie pliku: {0}",
"pt_BR": "Extensão: {0}",
"ru_RU": "Расширение файла: {0}",
"sv_SE": "Filänd: {0}",
"sv_SE": "Filändelse: {0}",
"th_TH": "นามสกุลไฟล์: {0}",
"tr_TR": "Dosya Uzantısı: {0}",
"uk_UA": "Розширення файлу: {0}",
@@ -1789,7 +1789,7 @@
"pl_PL": "Rozszerzenie pliku",
"pt_BR": "Extensão",
"ru_RU": "Расширение файла",
"sv_SE": "Filänd",
"sv_SE": "Filändelse",
"th_TH": "นามสกุลไฟล์",
"tr_TR": "Dosya Uzantısı",
"uk_UA": "Розширення файлу",
@@ -1859,7 +1859,7 @@
"he_IL": "",
"it_IT": "",
"ja_JP": "",
"ko_KR": "",
"ko_KR": "호환성 :",
"no_NO": "Kompatibilitet",
"pl_PL": "",
"pt_BR": "Compatibilidade:",
@@ -1869,7 +1869,7 @@
"tr_TR": "",
"uk_UA": "Сумісність:",
"zh_CN": "兼容性:",
"zh_TW": ""
"zh_TW": "相容性:"
}
},
{
@@ -1884,7 +1884,7 @@
"he_IL": "",
"it_IT": "",
"ja_JP": "",
"ko_KR": "",
"ko_KR": "타이틀 ID :",
"no_NO": "Tittel ID:",
"pl_PL": "",
"pt_BR": "ID do Título:",
@@ -1894,7 +1894,7 @@
"tr_TR": "",
"uk_UA": "ID гри:",
"zh_CN": "标题 ID:",
"zh_TW": ""
"zh_TW": "標題 ID:"
}
},
{
@@ -1909,7 +1909,7 @@
"he_IL": "",
"it_IT": "",
"ja_JP": "",
"ko_KR": "",
"ko_KR": "호스트 게임 : {0}",
"no_NO": "Spill som Arrangeres: {0}",
"pl_PL": "",
"pt_BR": "Jogos Hospedados: {0}",
@@ -1919,7 +1919,7 @@
"tr_TR": "",
"uk_UA": "Розміщені ігри: {0}",
"zh_CN": "服务的游戏: {0}",
"zh_TW": ""
"zh_TW": "LDN 上主持的遊戲數量: {0}"
}
},
{
@@ -1934,7 +1934,7 @@
"he_IL": "",
"it_IT": "",
"ja_JP": "",
"ko_KR": "",
"ko_KR": "온라인 플레이어 : {0}",
"no_NO": "Online-spillere: {0}",
"pl_PL": "",
"pt_BR": "Jogadores Online: {0}",
@@ -1944,7 +1944,7 @@
"tr_TR": "",
"uk_UA": "Гравців онлайн: {0}",
"zh_CN": "在线玩家: {0}",
"zh_TW": ""
"zh_TW": "LDN 上在線的玩家數量: {0}"
}
},
{
@@ -2294,7 +2294,7 @@
"tr_TR": "",
"uk_UA": "Очистити кеш PPTC",
"zh_CN": "清理 PPTC 缓存",
"zh_TW": ""
"zh_TW": "清除 PPTC"
}
},
{
@@ -2319,7 +2319,7 @@
"tr_TR": "",
"uk_UA": "Видаляє всі файли кешу PPTC для застосунку",
"zh_CN": "删除应用程序的所有 PPTC 缓存",
"zh_TW": ""
"zh_TW": "清除應用程式的 PPTC"
}
},
{
@@ -2369,7 +2369,7 @@
"tr_TR": "Uygulamanın shader önbelleğini temizler",
"uk_UA": "Видаляє кеш шейдерів застосунку (гри)",
"zh_CN": "删除游戏的着色器缓存文件,下次启动游戏时重新生成着色器缓存文件",
"zh_TW": "除應用程式的著色器快取"
"zh_TW": "除應用程式的著色器快取檔案"
}
},
{
@@ -2644,7 +2644,7 @@
"tr_TR": "",
"uk_UA": "Витягти RomFS з обраного файлу DLC",
"zh_CN": "从选定的 DLC 文件中解压 RomFS",
"zh_TW": ""
"zh_TW": "從已選擇的 DLC 檔案中提取 RomFS"
}
},
{
@@ -2759,17 +2759,17 @@
"he_IL": "",
"it_IT": "",
"ja_JP": "",
"ko_KR": "",
"ko_KR": "사용자 정의 구성 만들기",
"no_NO": "",
"pl_PL": "",
"pt_BR": "",
"ru_RU": "Создать пользовательскую конфигурацию",
"ru_RU": "Задать индивидуальные параметры",
"sv_SE": "",
"th_TH": "",
"tr_TR": "",
"uk_UA": "Створити користувацьку конфігурацію",
"zh_CN": "",
"zh_TW": ""
"zh_CN": "创建自定义设置",
"zh_TW": "建立遊戲獨立自訂 (per-game) 設定檔"
}
},
{
@@ -2784,17 +2784,17 @@
"he_IL": "",
"it_IT": "",
"ja_JP": "",
"ko_KR": "",
"ko_KR": "사용자 정의 구성 편집",
"no_NO": "",
"pl_PL": "",
"pt_BR": "",
"ru_RU": "Изменить пользовательскую конфигурацию",
"ru_RU": "Изменить индивидуальные параметры",
"sv_SE": "",
"th_TH": "",
"tr_TR": "",
"uk_UA": "Редагувати користувацьку конфігурацію",
"zh_CN": "",
"zh_TW": ""
"zh_CN": "编辑自定义设置",
"zh_TW": "編輯遊戲獨立自訂 (per-game) 設定檔"
}
},
{
@@ -2844,7 +2844,7 @@
"tr_TR": "Mevcut oyun için bağımsız bir yapılandırma oluşturur",
"uk_UA": "Створюйте незалежну конфігурацію для поточної гри",
"zh_CN": "为当前游戏创建独立的配置",
"zh_TW": "為當前遊戲創建獨立的配置"
"zh_TW": "為已選擇的遊戲建立遊戲獨立自訂 (game-specific) 的設定檔"
}
},
{
@@ -2859,17 +2859,17 @@
"he_IL": "",
"it_IT": "",
"ja_JP": "",
"ko_KR": "",
"ko_KR": "선택한 게임에 대한 기존 독립 구성 편집",
"no_NO": "",
"pl_PL": "",
"pt_BR": "",
"ru_RU": "Отредактировать существующую независимую конфигурацию для выбранной игры.",
"ru_RU": "Отредактировать существующие независимые параметры для выбранной игры.",
"sv_SE": "",
"th_TH": "",
"tr_TR": "",
"uk_UA": "Відредагувати наявну індивідуальну конфігурацію для цієї гри.",
"zh_CN": "",
"zh_TW": ""
"zh_CN": "编辑选定游戏的现存独立配置",
"zh_TW": "為已選擇的遊戲編輯遊戲獨立自訂 (game-specific) 的設定檔"
}
},
{
@@ -2894,7 +2894,7 @@
"tr_TR": "",
"uk_UA": "Iнформація про сумісність",
"zh_CN": "显示兼容性项目",
"zh_TW": ""
"zh_TW": "顯示相容性資訊"
}
},
{
@@ -2919,7 +2919,7 @@
"tr_TR": "",
"uk_UA": "Показати цю гру в Списку Сумісності. Список сумісності також можна зайти в меню Довідки.",
"zh_CN": "在兼容性列表中显示选定的游戏,您通常可以通过帮助菜单访问。",
"zh_TW": ""
"zh_TW": "在相容性列表中顯示已選擇的遊戲。你也可以透過「說明」選單開啟。"
}
},
{
@@ -2944,7 +2944,7 @@
"tr_TR": "",
"uk_UA": "Інформація про гру",
"zh_CN": "显示游戏信息",
"zh_TW": ""
"zh_TW": "顯示遊戲資訊"
}
},
{
@@ -2969,7 +2969,7 @@
"tr_TR": "",
"uk_UA": "Показати статистику та деталі обраної гри.",
"zh_CN": "显示当前选定游戏的状态与详细信息。",
"zh_TW": ""
"zh_TW": "顯示目前已選擇遊戲的狀態及詳細資訊。"
}
},
{
@@ -3509,7 +3509,7 @@
"he_IL": "",
"it_IT": "",
"ja_JP": "",
"ko_KR": "",
"ko_KR": "업데이트 확인 :",
"no_NO": "Se etter Oppdateringer:",
"pl_PL": "",
"pt_BR": "Verificar Atualizações:",
@@ -3519,7 +3519,7 @@
"tr_TR": "",
"uk_UA": "Перевірка оновлень:",
"zh_CN": "检查更新",
"zh_TW": ""
"zh_TW": "檢查更新:"
}
},
{
@@ -3534,7 +3534,7 @@
"he_IL": "",
"it_IT": "",
"ja_JP": "",
"ko_KR": "",
"ko_KR": "",
"no_NO": "Av",
"pl_PL": "",
"pt_BR": "Desligado",
@@ -3544,7 +3544,7 @@
"tr_TR": "",
"uk_UA": "Вимкнути",
"zh_CN": "关闭",
"zh_TW": ""
"zh_TW": "關閉"
}
},
{
@@ -3559,7 +3559,7 @@
"he_IL": "",
"it_IT": "",
"ja_JP": "",
"ko_KR": "",
"ko_KR": "프롬프트",
"no_NO": "Spør",
"pl_PL": "",
"pt_BR": "Ao Abrir",
@@ -3569,7 +3569,7 @@
"tr_TR": "",
"uk_UA": "Запитувати щоразу",
"zh_CN": "提示",
"zh_TW": ""
"zh_TW": "提示"
}
},
{
@@ -3584,7 +3584,7 @@
"he_IL": "",
"it_IT": "",
"ja_JP": "",
"ko_KR": "",
"ko_KR": "백그라운드",
"no_NO": "Bakgrunn",
"pl_PL": "",
"pt_BR": "2° Plano",
@@ -3594,7 +3594,7 @@
"tr_TR": "",
"uk_UA": "Оновлювати в фоні",
"zh_CN": "背景",
"zh_TW": ""
"zh_TW": "背景"
}
},
{
@@ -3609,7 +3609,7 @@
"he_IL": "",
"it_IT": "",
"ja_JP": "",
"ko_KR": "",
"ko_KR": "에뮬레이터 초점 손실 :",
"no_NO": "På Emulator Fokus Tapt:",
"pl_PL": "",
"pt_BR": "Ao Perder o Foco:",
@@ -3619,7 +3619,7 @@
"tr_TR": "",
"uk_UA": "При втраті фокуса емулятором:",
"zh_CN": "当模拟器在后台时:",
"zh_TW": ""
"zh_TW": "當模擬器「失去焦點」(如切換工作)時:"
}
},
{
@@ -3634,7 +3634,7 @@
"he_IL": "",
"it_IT": "",
"ja_JP": "",
"ko_KR": "",
"ko_KR": "아무것도 하지 않음",
"no_NO": "Gjør Ingenting",
"pl_PL": "",
"pt_BR": "Não Fazer Nada",
@@ -3644,7 +3644,7 @@
"tr_TR": "",
"uk_UA": "Нічого не робити",
"zh_CN": "什么事情也不做",
"zh_TW": ""
"zh_TW": "沒有動作"
}
},
{
@@ -3659,7 +3659,7 @@
"he_IL": "",
"it_IT": "",
"ja_JP": "",
"ko_KR": "",
"ko_KR": "입력 차단",
"no_NO": "Blokkinngang",
"pl_PL": "",
"pt_BR": "Bloquear Controles",
@@ -3669,7 +3669,7 @@
"tr_TR": "",
"uk_UA": "Блокувати введення",
"zh_CN": "禁用输入",
"zh_TW": ""
"zh_TW": "停用輸入"
}
},
{
@@ -3684,7 +3684,7 @@
"he_IL": "",
"it_IT": "",
"ja_JP": "",
"ko_KR": "",
"ko_KR": "음소거",
"no_NO": "Demp Lyd",
"pl_PL": "",
"pt_BR": "Ficar Mudo",
@@ -3694,7 +3694,7 @@
"tr_TR": "",
"uk_UA": "Вимкнути звук",
"zh_CN": "静音",
"zh_TW": ""
"zh_TW": "靜音"
}
},
{
@@ -3709,7 +3709,7 @@
"he_IL": "",
"it_IT": "",
"ja_JP": "",
"ko_KR": "",
"ko_KR": "입력 차단 및 음소거",
"no_NO": "Blokker Inputs og demp Volumet",
"pl_PL": "",
"pt_BR": "Bloquear Controles & Ficar Mudo",
@@ -3719,7 +3719,7 @@
"tr_TR": "",
"uk_UA": "Блокувати введення та Вимкнути звук",
"zh_CN": "阻止输入且静音",
"zh_TW": ""
"zh_TW": "停用輸入且靜音"
}
},
{
@@ -3734,7 +3734,7 @@
"he_IL": "",
"it_IT": "",
"ja_JP": "",
"ko_KR": "",
"ko_KR": "에뮬레이션 일시 중지",
"no_NO": "Pause Emulatoren",
"pl_PL": "",
"pt_BR": "Pausar a Emulação",
@@ -3744,7 +3744,7 @@
"tr_TR": "",
"uk_UA": "Поставити на паузу",
"zh_CN": "暂停模拟",
"zh_TW": ""
"zh_TW": "暫停模擬"
}
},
{
@@ -3809,7 +3809,7 @@
"he_IL": "",
"it_IT": "",
"ja_JP": "",
"ko_KR": "",
"ko_KR": "초점이 맞지 않으면 입력 비활성화",
"no_NO": "Deaktiver inndata når vinduet er ute av fokus",
"pl_PL": "",
"pt_BR": "Desativar Controles Quando Estiver Fora de Foco",
@@ -3819,7 +3819,7 @@
"tr_TR": "",
"uk_UA": "Вимкнути введення, якщо вікно неактивне",
"zh_CN": "在后台时禁用输入",
"zh_TW": ""
"zh_TW": "在「失去焦點」時停用輸入"
}
},
{
@@ -3834,16 +3834,16 @@
"he_IL": "",
"it_IT": "",
"ja_JP": "",
"ko_KR": "",
"ko_KR": "원래 UI 스타일 표시(다시 시작 필요)",
"no_NO": "Vis original UI-stil (krever omstart)",
"pl_PL": "",
"pt_BR": "",
"ru_RU": "",
"ru_RU": "Включить оригинальный интерфейса (требуется перезагрузка)",
"sv_SE": "",
"th_TH": "",
"tr_TR": "",
"uk_UA": "Показати оригінальний UI (Потрібен перезапуск)",
"zh_CN": "",
"zh_CN": "显示原始 UI 样式 (需要重启)",
"zh_TW": ""
}
},
@@ -3859,16 +3859,16 @@
"he_IL": "",
"it_IT": "",
"ja_JP": "",
"ko_KR": "",
"ko_KR": "Ryujinx 1.1.1403을 연상시키는 이전 Avalonia Ryujinx UI를 표시합니다. 이 기능은 Windows가 아닌 플랫폼에서는 기본적으로 활성화됩니다.\n 클래식 스타일의 타이틀 바가 돌아왔고 주요 창 레이아웃 재작업이 역전되었습니다. 이 툴팁 위의 설정 탐색 배치와 같은 작업입니다.",
"no_NO": "Vis det eldre Avalonia Ryujinx-grensesnittet som minner om Ryujinx 1.1.1403. Dette er aktivert som standard på plattformer som ikke er Windows.\nTittellinjen i klassisk stil er tilbake, og store omarbeidinger av vindusoppsettet er reversert, for eksempel plasseringen av innstillingsnavigasjonen over dette verktøytipset.",
"pl_PL": "",
"pt_BR": "",
"ru_RU": "",
"ru_RU": "Показать старый пользовательский интерфейс Avalonia Ryujinx, напоминающий Ryujinx 1.1.1403. Включено по умолчанию на платформах, отличных от Windows.\nСтрока заголовка в классическом стиле вернётся на место, а основные изменения в оформлении окна будут отменены; например, расположение навигации по настройкам над этой всплывающей подсказкой.",
"sv_SE": "",
"th_TH": "",
"tr_TR": "",
"uk_UA": "Показати старий інтерфейс Avalonia Ryujinx, який був у Ryujinx 1.1.1403. Ця опція активна за замовчуванням на всіх інших, окрім Windows платформах.\nПовернеться класична панель заголовка, а всі суттєві зміни інтерфейсу будуть скасовані, зокрема горизонтальне розміщення навігації в налаштуваннях.",
"zh_CN": "",
"zh_CN": "显示旧的类似 Ryujinx 1.1.1403 的 Avalonia Ryujinx UI。在非 Windows 平台上默认启用此选项。\n经典样式的标题栏已回归并且恢复了对窗口布局的重大重构;例如在工具提示上方放置设置导航。",
"zh_TW": ""
}
},
@@ -4014,7 +4014,7 @@
"pl_PL": "",
"pt_BR": "Carregar Automaticamente Pasta de DLC e Atualizações",
"ru_RU": "Автозагрузка папки с DLC/Обновлениями",
"sv_SE": "Läs automatisk in DLC/speluppdateringar",
"sv_SE": "Läs automatiskt in DLC/speluppdateringar",
"th_TH": "โหลดไดเรกทอรี DLC/ไฟล์อัปเดต อัตโนมัติ",
"tr_TR": "",
"uk_UA": "Автозавантаження теки DLC/Оновлень",
@@ -4869,7 +4869,7 @@
"tr_TR": "",
"uk_UA": "Синхронізувати з системним годинником",
"zh_CN": "与系统时间同步",
"zh_TW": ""
"zh_TW": "與系統時間同步"
}
},
{
@@ -5038,7 +5038,7 @@
"no_NO": "Lyd Inn/Ut",
"pl_PL": "",
"pt_BR": "",
"ru_RU": "Выход/Вход звука",
"ru_RU": "",
"sv_SE": "",
"th_TH": "",
"tr_TR": "",
@@ -5284,7 +5284,7 @@
"he_IL": "",
"it_IT": "",
"ja_JP": "",
"ko_KR": "",
"ko_KR": "컨트롤러 애플릿 무시",
"no_NO": "",
"pl_PL": "",
"pt_BR": "Ignorar Applet do Controlador",
@@ -5294,7 +5294,7 @@
"tr_TR": "",
"uk_UA": "Ігнорувати Аплет Контролера",
"zh_CN": "忽略控制器小程序",
"zh_TW": ""
"zh_TW": "忽略控制器小程式"
}
},
{
@@ -6134,7 +6134,7 @@
"he_IL": "",
"it_IT": "",
"ja_JP": "",
"ko_KR": "",
"ko_KR": "UI 로그 활성화",
"no_NO": "Aktivere UI-logger",
"pl_PL": "",
"pt_BR": "Habilitar Logs da IU",
@@ -6144,7 +6144,7 @@
"tr_TR": "",
"uk_UA": "Увімкнути журнали інтерфейсу",
"zh_CN": "启用 UI 日志",
"zh_TW": ""
"zh_TW": "啟用 UI 日誌"
}
},
{
@@ -6534,7 +6534,7 @@
"he_IL": "",
"it_IT": "",
"ja_JP": "",
"ko_KR": "",
"ko_KR": "설정 초기화",
"no_NO": "Tilbakestill innstillinger",
"pl_PL": "",
"pt_BR": "Redefinir Configurações",
@@ -6544,7 +6544,7 @@
"tr_TR": "",
"uk_UA": "Скинути налаштування",
"zh_CN": "重置设置",
"zh_TW": ""
"zh_TW": "重設設定"
}
},
{
@@ -6559,7 +6559,7 @@
"he_IL": "",
"it_IT": "",
"ja_JP": "",
"ko_KR": "",
"ko_KR": "설정을 초기화하고 싶습니다.",
"no_NO": "Jeg vil tilbakestille innstillingene mine.",
"pl_PL": "",
"pt_BR": "Quero redefinir minhas configurações.",
@@ -6569,7 +6569,7 @@
"tr_TR": "",
"uk_UA": "Я хочу скинути налаштування.",
"zh_CN": "我要重置我的设置。",
"zh_TW": ""
"zh_TW": "我想重設我的設定。"
}
},
{
@@ -6588,7 +6588,7 @@
"no_NO": "",
"pl_PL": "",
"pt_BR": "",
"ru_RU": "Ок",
"ru_RU": "",
"sv_SE": "Ok",
"th_TH": "ตกลง",
"tr_TR": "Tamam",
@@ -7038,7 +7038,7 @@
"no_NO": "",
"pl_PL": "Pro Kontroler",
"pt_BR": "",
"ru_RU": "Pro контроллер",
"ru_RU": "",
"sv_SE": "",
"th_TH": "โปรคอนโทรลเลอร์",
"tr_TR": "Profesyonel Kumanda",
@@ -7264,7 +7264,7 @@
"pl_PL": "Przyciski",
"pt_BR": "Botões",
"ru_RU": "Кнопки",
"sv_SE": "Knappar",
"sv_SE": "Handlingsknappar",
"th_TH": "ปุ่มกด",
"tr_TR": "Tuşlar",
"uk_UA": "Кнопки",
@@ -8494,7 +8494,7 @@
"tr_TR": "",
"uk_UA": "Вимкнути",
"zh_CN": "关闭",
"zh_TW": ""
"zh_TW": "關閉"
}
},
{
@@ -8519,7 +8519,7 @@
"tr_TR": "",
"uk_UA": "Веселка",
"zh_CN": "彩虹",
"zh_TW": ""
"zh_TW": "彩虹"
}
},
{
@@ -8544,7 +8544,7 @@
"tr_TR": "",
"uk_UA": "Швидкість зміни кольорів",
"zh_CN": "彩虹滚动速度",
"zh_TW": ""
"zh_TW": "彩虹滾動速度"
}
},
{
@@ -8569,7 +8569,7 @@
"tr_TR": "",
"uk_UA": "",
"zh_CN": "颜色",
"zh_TW": ""
"zh_TW": "顏色"
}
},
{
@@ -13484,7 +13484,7 @@
"he_IL": "שגיאה בהצגת דיאלוג ErrorApplet: {0}",
"it_IT": "Errore nella visualizzazione della finestra dell'ErrorApplet: {0}",
"ja_JP": "エラーアプレットダイアログ表示エラー: {0}",
"ko_KR": "애플릿 오류 대화 상자 표시 오류 : {0}",
"ko_KR": "애플릿 오류 대화 상자 표시 오류 : {0}",
"no_NO": "Feil ved visning av Feilmeldingsdialog: {0}",
"pl_PL": "Błąd wyświetlania okna Dialogowego ErrorApplet: {0}",
"pt_BR": "Erro ao exibir applet ErrorApplet: {0}",
@@ -13844,7 +13844,7 @@
"tr_TR": "",
"uk_UA": "Ви збираєтесь видалити всі дані PPTC з:\n\n{0}\n\nБажаєте продовжити цю операцію?",
"zh_CN": "您正要清理 PPTC 数据:\n\n{0}\n\n您确实要继续吗?",
"zh_TW": ""
"zh_TW": "您將要刪除以下遊戲的 PPTC:\n\n{0}\n\n您確定要繼續嗎?"
}
},
{
@@ -14914,7 +14914,7 @@
"pl_PL": "Wielowątkowość Backendu Graficznego:",
"pt_BR": "Multi Enfileiramento do Renderizador Gráfico:",
"ru_RU": "Многопоточность графического бэкенда:",
"sv_SE": "Multithreading för grafikbakände:",
"sv_SE": "Multitrådning för grafikbakände:",
"th_TH": "มัลติเธรด กราฟิกเบื้องหลัง:",
"tr_TR": "Grafik Backend Multithreading:",
"uk_UA": "Багатопотоковість графічного сервера:",
@@ -16684,7 +16684,7 @@
"he_IL": "",
"it_IT": "",
"ja_JP": "",
"ko_KR": "",
"ko_KR": "앱이 실행 중일 때, 게임패드의 연결이 끊어지면 컨트롤러 애플릿 대화 상자가 나타나지 않습니다.\n\n모르시면 끔으로 두십시오.",
"no_NO": "",
"pl_PL": "",
"pt_BR": "A caixa de diálogo do Applet do controlador não aparecerá se o controle for desconectado enquanto um aplicativo estiver em execução.\n\nDeixe a opção DESLIGADO se não tiver certeza.",
@@ -16694,7 +16694,7 @@
"tr_TR": "",
"uk_UA": "Діалогове вікно Аплету Контролера не з'явиться, якщо геймпад було відключено під час роботи програми.\n\nЗалиште вимкненим якщо не впевнені.",
"zh_CN": "在应用程序运行时如果游戏手柄断开连接则不会显示控制器小程序对话框。\n\n如果不确定请保持关闭状态。",
"zh_TW": ""
"zh_TW": "在模擬應用程式時如果遊戲手柄中斷連線則不會顯示控制器小程式。\n\n如果不確定請保持關閉狀態。"
}
},
{
@@ -17159,7 +17159,7 @@
"he_IL": "",
"it_IT": "",
"ja_JP": "",
"ko_KR": "",
"ko_KR": "콘솔에 Avalonia(UI) 로그 메시지를 출력합니다.",
"no_NO": "Skriver ut Avalonia (UI)-loggmeldinger i konsollen.",
"pl_PL": "",
"pt_BR": "Imprime mensagens de log do Avalonia (UI) no console.",
@@ -17169,7 +17169,7 @@
"tr_TR": "",
"uk_UA": "Виводити повідомлення журналу Avalonia (UI) в консоль",
"zh_CN": "在控制台显示 Avalonia (UI) 的日志信息",
"zh_TW": ""
"zh_TW": "在控制台中輸出 Avalonia (UI) 日誌訊息。"
}
},
{
@@ -17359,7 +17359,7 @@
"he_IL": "",
"it_IT": "",
"ja_JP": "",
"ko_KR": "",
"ko_KR": "Ryujinx 스크린샷 폴더 열기",
"no_NO": "",
"pl_PL": "",
"pt_BR": "Abre a pasta de capturas de tela do Ryujinx",
@@ -17369,7 +17369,7 @@
"tr_TR": "",
"uk_UA": "Відкрити теку куди зберігаються скріншоти Ryujinx",
"zh_CN": "打开 Ryujinx 截图文件夹",
"zh_TW": ""
"zh_TW": "開啟 Ryujinx 螢幕擷取畫面資料夾"
}
},
{
@@ -18109,7 +18109,7 @@
"he_IL": "",
"it_IT": "",
"ja_JP": "",
"ko_KR": "",
"ko_KR": "업데이트 가능!",
"no_NO": "Oppdatering tilgjengelig!",
"pl_PL": "",
"pt_BR": "Atualização Disponível!",
@@ -18119,7 +18119,7 @@
"tr_TR": "",
"uk_UA": "Доступне оновлення!",
"zh_CN": "有可用的更新!",
"zh_TW": ""
"zh_TW": "有可用的更新!"
}
},
{
@@ -18714,7 +18714,7 @@
"pl_PL": "",
"pt_BR": "Não Reduzido",
"ru_RU": "Не обрезан",
"sv_SE": "Inte optimerad",
"sv_SE": "Orörd",
"th_TH": "",
"tr_TR": "",
"uk_UA": "Необрізані",
@@ -19944,7 +19944,7 @@
"tr_TR": "",
"uk_UA": "Налаштування LED",
"zh_CN": "LED 设置",
"zh_TW": ""
"zh_TW": "LED 設定"
}
},
{
@@ -21794,7 +21794,7 @@
"tr_TR": "Yeniden Doku Sıkıştırılmasını Aktif Et",
"uk_UA": "Увімкнути рекомпресію текстури",
"zh_CN": "启用纹理压缩",
"zh_TW": "啟材質重新壓縮"
"zh_TW": "啟材質重新壓縮"
}
},
{
@@ -23859,16 +23859,16 @@
"he_IL": "",
"it_IT": "",
"ja_JP": "",
"ko_KR": "",
"ko_KR": "호환성 목록 - {0}개 항목",
"no_NO": "",
"pl_PL": "",
"pt_BR": "",
"ru_RU": "",
"ru_RU": "Список совместимости — записей: {0}",
"sv_SE": "",
"th_TH": "",
"tr_TR": "",
"uk_UA": "Список сумісності — {0} ігор",
"zh_CN": "",
"zh_CN": "兼容性列表 - {0} 条",
"zh_TW": ""
}
},
@@ -23934,16 +23934,16 @@
"he_IL": "",
"it_IT": "",
"ja_JP": "",
"ko_KR": "",
"ko_KR": "어카이브 {0} 호환성 항목...",
"no_NO": "Søk i {0} kompatibilitetsoppføringer...",
"pl_PL": "",
"pt_BR": "",
"ru_RU": "",
"ru_RU": "Поиск среди {0} записей о совместимости...",
"sv_SE": "",
"th_TH": "",
"tr_TR": "",
"uk_UA": "Шукати серед {0} перевірених ігор...",
"zh_CN": "",
"zh_CN": "搜索 {0} 兼容性条目...",
"zh_TW": ""
}
},
@@ -24134,7 +24134,7 @@
"he_IL": "",
"it_IT": "",
"ja_JP": "",
"ko_KR": "",
"ko_KR": "어떠한 충돌이나 GPU 버그 없이 부팅 및 플레이가 가능하며, 일반 PC에서 충분히 즐길 수 있을 만큼 빠른 속도입니다.",
"no_NO": "Starter opp og spiller uten krasj eller GPU-feil av noe slag, og med en hastighet som er rask nok til å ha rimelig glede av på en gjennomsnittlig PC.",
"pl_PL": "",
"pt_BR": "Inicializa e roda sem travamentos ou bugs de GPU de qualquer tipo, e em uma velocidade rápida o suficiente para ser aproveitado em um PC comum.",
@@ -24144,7 +24144,7 @@
"tr_TR": "",
"uk_UA": "Запускається та оптимально працює (без збоїв або графічних багів) на середньостатистичному комп'ютері.",
"zh_CN": "启动和游戏时不会出现任何崩溃或任何类型的 GPU bug 且速度足够快可以在一般 PC 上尽情游玩。",
"zh_TW": ""
"zh_TW": "啟動和遊玩時不會出現任何崩潰或任何類型的 GPU bug 且速度足夠快可以在一般 PC 上盡情遊玩。"
}
},
{
@@ -24159,7 +24159,7 @@
"he_IL": "",
"it_IT": "",
"ja_JP": "",
"ko_KR": "",
"ko_KR": "부팅하고 게임에 들어가지만 충돌, 교착 상태, GPU 버그, 방해가 될 정도로 나쁜 오디오 또는 너무 느린 문제 중 하나 이상으로 인해 문제가 발생합니다. 게임은 여전히 ​​가능할 수 있습니다.",
"no_NO": "Starter og går i gang i spillet, men lider av ett eller flere av følgende: krasjer, fastlåser, GPU-feil, distraherende dårlig lyd eller er rett og slett for tregt. Spillet kan fortsatt spilles helt til ende, men ikke slik det er ment å spilles.",
"pl_PL": "",
"pt_BR": "Inicializa e entra no jogo, mas sofre de um ou mais dos seguintes: travamentos, deadlocks, bugs de GPU, áudio ruim que distrai ou é simplesmente muito lento. O jogo ainda pode ser jogado até o fim, mas não da forma como foi criado para ser jogado.",
@@ -24169,7 +24169,7 @@
"tr_TR": "",
"uk_UA": "Запускається, але в грі на вас чекатимуть одна або декілька наступних проблем: збої, зависання, графічні баги, спотворений звук або ж гра загалом працюватиме надто повільно. Можливо, її все ще можна пройти, але досвід буде не найкращим.",
"zh_CN": "可以成功启动并进入游戏但可能会遇到以下一种或多种问题: 崩溃、卡死、GPU bug、令人无法接受的音频,或者只是太慢。仍然可以继续进行游戏,但是可能无法达到预期。",
"zh_TW": ""
"zh_TW": "能啟動並進入遊戲但可能會遇到下列狀況崩潰、卡死、GPU bug、令人無法接受的聲音、或遊戲過慢。遊戲或可繼續進行但是可能無法達到預期效果。"
}
},
{
@@ -24184,7 +24184,7 @@
"he_IL": "",
"it_IT": "",
"ja_JP": "",
"ko_KR": "",
"ko_KR": "부팅하고 타이틀 화면이 나오지만 메인 게임 플레이로 진입할 수 없습니다.",
"no_NO": "Starter opp og går forbi tittelskjermen, men kommer ikke inn i hovedspillet.",
"pl_PL": "",
"pt_BR": "Inicializa e passa da tela de título, mas não entra no jogo principal.",
@@ -24194,7 +24194,7 @@
"tr_TR": "",
"uk_UA": "Запускається та проходить початковий екран, але пограти не вийде.",
"zh_CN": "可以启动并通过标题画面但是无法进入到主要的游戏流程。",
"zh_TW": ""
"zh_TW": "能啟動並通過標題畫面,但是無法進入主要的遊戲畫面。"
}
},
{
@@ -24209,7 +24209,7 @@
"he_IL": "",
"it_IT": "",
"ja_JP": "",
"ko_KR": "",
"ko_KR": "부팅되지만 타이틀 화면을 통과하지 못합니다.",
"no_NO": "Starter, men kommer ikke lenger enn til tittelskjermen.",
"pl_PL": "",
"pt_BR": "Inizializa, mas não passa da tela de título.",
@@ -24219,7 +24219,7 @@
"tr_TR": "",
"uk_UA": "Запускається, але не відображає навіть початкового екрану.",
"zh_CN": "可以启动但是无法通过标题画面。",
"zh_TW": ""
"zh_TW": "能啟動,但是無法通過標題畫面。"
}
},
{
@@ -24234,7 +24234,7 @@
"he_IL": "",
"it_IT": "",
"ja_JP": "",
"ko_KR": "",
"ko_KR": "부팅되지 않거나 활동 흔적이 보이지 않습니다.",
"no_NO": "Starter ikke opp eller viser ingen tegn til aktivitet.",
"pl_PL": "",
"pt_BR": "Não inicializa ou não mostra sinais de atividade.",
@@ -24244,7 +24244,7 @@
"tr_TR": "",
"uk_UA": "Взагалі не запускається.",
"zh_CN": "无法启动或显示无任何动静。",
"zh_TW": ""
"zh_TW": "無法啟動"
}
},
{
@@ -24259,17 +24259,17 @@
"he_IL": "",
"it_IT": "",
"ja_JP": "",
"ko_KR": "",
"ko_KR": "사용자 정의 설정",
"no_NO": "",
"pl_PL": "",
"pt_BR": "",
"ru_RU": "",
"ru_RU": "Индивидуальные параметры",
"sv_SE": "",
"th_TH": "",
"tr_TR": "",
"uk_UA": "Власна конфігурація",
"zh_CN": "",
"zh_TW": ""
"zh_CN": "自定义配置",
"zh_TW": "遊戲獨立自訂 (game-specific) 設定"
}
},
{
@@ -24284,17 +24284,17 @@
"he_IL": "",
"it_IT": "",
"ja_JP": "",
"ko_KR": "",
"ko_KR": "(글로벌)",
"no_NO": "",
"pl_PL": "",
"pt_BR": "",
"ru_RU": "",
"ru_RU": "(Глобальный)",
"sv_SE": "",
"th_TH": "",
"tr_TR": "",
"uk_UA": "(Глобальні)",
"zh_CN": "",
"zh_TW": ""
"zh_CN": "(全局)",
"zh_TW": "(全域)"
}
},
{
@@ -24319,7 +24319,7 @@
"tr_TR": "",
"uk_UA": "Оберіть DLC які бажаєте вилучити",
"zh_CN": "选择一个要解压的 DLC",
"zh_TW": ""
"zh_TW": "選擇要提取的 DLC"
}
},
{
@@ -24334,7 +24334,7 @@
"he_IL": "",
"it_IT": "",
"ja_JP": "",
"ko_KR": "",
"ko_KR": "리치 프레즌스 이미지",
"no_NO": "Rikt nærværsbilde",
"pl_PL": "",
"pt_BR": "Imagem da Presença do Discord",
@@ -24344,7 +24344,7 @@
"tr_TR": "",
"uk_UA": "Зображення картки активності Discord",
"zh_CN": "Rich Presence 图像",
"zh_TW": ""
"zh_TW": "Rich Presence 圖像"
}
},
{
@@ -24359,7 +24359,7 @@
"he_IL": "",
"it_IT": "",
"ja_JP": "",
"ko_KR": "",
"ko_KR": "다이내믹 리치 프레즌스",
"no_NO": "Dynamisk og rik tilstedeværelse",
"pl_PL": "",
"pt_BR": "Presença Dinâmica do Discord",
@@ -24369,7 +24369,7 @@
"tr_TR": "",
"uk_UA": "Динамічна картка активності Discord",
"zh_CN": "动态 Rich Presence",
"zh_TW": ""
"zh_TW": "動態 Rich Presence"
}
}
]

View File

@@ -1,6 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<RuntimeIdentifiers>win-x64;osx-x64;linux-x64</RuntimeIdentifiers>
<RuntimeIdentifiers>win-x64;osx-x64;linux-x64;win-arm64;osx-arm64;linux-arm64;</RuntimeIdentifiers>
<OutputType>Exe</OutputType>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<Version>1.0.0-dirty</Version>
@@ -29,12 +29,18 @@
<TrimMode>partial</TrimMode>
</PropertyGroup>
<PropertyGroup Condition="'$(RuntimeIdentifier)' == 'win-arm64'">
<PublishSingleFile>true</PublishSingleFile>
<PublishTrimmed>false</PublishTrimmed>
</PropertyGroup>
<!--
FluentAvalonia, used in the Avalonia UI, requires a workaround for the json serializer used internally when using .NET 8+ System.Text.Json.
See:
https://github.com/amwx/FluentAvalonia/issues/481
https://devblogs.microsoft.com/dotnet/system-text-json-in-dotnet-8/
-->
<PropertyGroup>
<JsonSerializerIsReflectionEnabledByDefault>true</JsonSerializerIsReflectionEnabledByDefault>
</PropertyGroup>
@@ -57,8 +63,8 @@
<PackageReference Include="Projektanker.Icons.Avalonia.MaterialDesign" />
<PackageReference Include="OpenTK.Core" />
<PackageReference Include="Ryujinx.Audio.OpenAL.Dependencies" Condition="'$(RuntimeIdentifier)' != 'linux-x64' AND '$(RuntimeIdentifier)' != 'linux-arm64' AND '$(RuntimeIdentifier)' != 'osx-x64' AND '$(RuntimeIdentifier)' != 'osx-arm64'" />
<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.Nvdec.Dependencies.AllArch" />
<PackageReference Include="Ryujinx.Graphics.Vulkan.Dependencies.MoltenVK" Condition="'$(RuntimeIdentifier)' != 'linux-x64' AND '$(RuntimeIdentifier)' != 'linux-arm64' AND '$(RuntimeIdentifier)' != 'win-x64' AND '$(RuntimeIdentifier)' != 'win-arm64'" />
<PackageReference Include="securifybv.ShellLink" />
<PackageReference Include="Sep" />
<PackageReference Include="Silk.NET.Vulkan" />
@@ -67,7 +73,7 @@
<PackageReference Include="SPB" />
<PackageReference Include="SharpZipLib" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Ryujinx.Audio.Backends.SDL2\Ryujinx.Audio.Backends.SDL2.csproj" />
<ProjectReference Include="..\Ryujinx.Graphics.Vulkan\Ryujinx.Graphics.Vulkan.csproj" />
@@ -84,7 +90,7 @@
</ItemGroup>
<ItemGroup>
<Content Include="..\..\distribution\windows\alsoft.ini" Condition="'$(RuntimeIdentifier)' != 'linux-x64' AND '$(RuntimeIdentifier)' != 'linux-arm64' AND '$(RuntimeIdentifier)' != 'osx-x64'">
<Content Include="..\..\distribution\windows\alsoft.ini" Condition="'$(RuntimeIdentifier)' != 'linux-x64' AND '$(RuntimeIdentifier)' != 'linux-arm64' AND '$(RuntimeIdentifier)' != 'osx-x64' AND '$(RuntimeIdentifier)' != 'osx-arm64'">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
<TargetPath>alsoft.ini</TargetPath>
</Content>

View File

@@ -432,7 +432,7 @@ namespace Ryujinx.Ava.UI.ViewModels
{
try
{
HttpResponseMessage response = await _httpClient.SendAsync(new HttpRequestMessage(HttpMethod.Head, "https://raw.githubusercontent.com/Ryubing/Ryujinx/refs/heads/master/assets/amiibo/Amiibo.json"));
HttpResponseMessage response = await _httpClient.SendAsync(new HttpRequestMessage(HttpMethod.Head, "https://raw.githubusercontent.com/Ryubing/Nfc/refs/heads/main/tags.json"));
if (response.IsSuccessStatusCode)
{
@@ -451,7 +451,7 @@ namespace Ryujinx.Ava.UI.ViewModels
{
try
{
HttpResponseMessage response = await _httpClient.GetAsync($"https://raw.githubusercontent.com/Ryubing/Ryujinx/refs/heads/master/assets/amiibo/Amiibo.json");
HttpResponseMessage response = await _httpClient.GetAsync("https://raw.githubusercontent.com/Ryubing/Nfc/refs/heads/main/tags.json");
if (response.IsSuccessStatusCode)
{