Compare commits

...

50 Commits

Author SHA1 Message Date
GreemDev
0a6a95754c Attempt 1 2026-05-12 20:01:40 -05:00
Renovate Bot
49891ba7af Update avalonia monorepo to 11.3.15 (#85)
This PR contains the following updates:

| Package | Change | [Age](https://docs.renovatebot.com/merge-confidence/) | [Confidence](https://docs.renovatebot.com/merge-confidence/) |
|---|---|---|---|
| [Avalonia](https://avaloniaui.net/?utm_source=nuget&utm_medium=referral&utm_content=project_homepage_link) ([source](https://github.com/AvaloniaUI/Avalonia)) | `11.3.14` → `11.3.15` | ![age](https://developer.mend.io/api/mc/badges/age/nuget/Avalonia/11.3.15?slim=true) | ![confidence](https://developer.mend.io/api/mc/badges/confidence/nuget/Avalonia/11.3.14/11.3.15?slim=true) |
| [Avalonia.Desktop](https://avaloniaui.net/?utm_source=nuget&utm_medium=referral&utm_content=project_homepage_link) ([source](https://github.com/AvaloniaUI/Avalonia)) | `11.3.14` → `11.3.15` | ![age](https://developer.mend.io/api/mc/badges/age/nuget/Avalonia.Desktop/11.3.15?slim=true) | ![confidence](https://developer.mend.io/api/mc/badges/confidence/nuget/Avalonia.Desktop/11.3.14/11.3.15?slim=true) |
| [Avalonia.Diagnostics](https://avaloniaui.net/?utm_source=nuget&utm_medium=referral&utm_content=project_homepage_link) ([source](https://github.com/AvaloniaUI/Avalonia)) | `11.3.14` → `11.3.15` | ![age](https://developer.mend.io/api/mc/badges/age/nuget/Avalonia.Diagnostics/11.3.15?slim=true) | ![confidence](https://developer.mend.io/api/mc/badges/confidence/nuget/Avalonia.Diagnostics/11.3.14/11.3.15?slim=true) |
| [Avalonia.Markup.Xaml.Loader](https://avaloniaui.net/?utm_source=nuget&utm_medium=referral&utm_content=project_homepage_link) ([source](https://github.com/AvaloniaUI/Avalonia)) | `11.3.14` → `11.3.15` | ![age](https://developer.mend.io/api/mc/badges/age/nuget/Avalonia.Markup.Xaml.Loader/11.3.15?slim=true) | ![confidence](https://developer.mend.io/api/mc/badges/confidence/nuget/Avalonia.Markup.Xaml.Loader/11.3.14/11.3.15?slim=true) |

---

### Configuration

📅 **Schedule**: (UTC)

- Branch creation
  - At any time (no schedule defined)
- Automerge
  - At any time (no schedule defined)

🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about these updates again.

---

 - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box

---

This PR has been generated by [Mend Renovate](https://github.com/renovatebot/renovate).
<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0My4xNjUuMSIsInVwZGF0ZWRJblZlciI6IjQzLjE2NS4xIiwidGFyZ2V0QnJhbmNoIjoibWFzdGVyIiwibGFiZWxzIjpbXX0=-->

Reviewed-on: https://git.ryujinx.app/projects/Ryubing/pulls/85
2026-05-12 02:41:10 +00:00
Max
89ea41ef84 Check if the Device is rendering before waiting on it (#86)
Fixes an issue where there were missed references and an ``OperationCancelled`` exception when exiting an application.

Reviewed-on: https://git.ryujinx.app/projects/Ryubing/pulls/86
2026-05-12 02:38:09 +00:00
yell0wsuit
f8167eb625 [HLE] Match hardware screenshot buffer size behavior for captures (#44)
## Description

~~Fixes a fatal CLR crash when `caps` screenshot saving receives an input buffer larger than `0x384000`.~~

~~Resolves a crash in Tomodachi Life: Living the Dream where saving the pictures to the system's album crashes with 0x80131506.~~

Follow up to #18. This PR adjusts the validation and copy behavior to better match real hardware, and adds logging to make invalid screenshot buffer cases easier to diagnose.

Real hardware accepts screenshot buffers with a size greater than or equal to `0x384000`, but only `0x384000` bytes are needed for the 1280x720 RGBA image.

This changes screenshot saving to:

- reject buffers smaller than `0x384000`
- accept buffers equal to or larger than `0x384000`
- copy only the first `0x384000` bytes into the 1280x720 bitmap

## Testing

Tested with a real Switch NRO using `capssuSaveScreenShotEx0`, `capssuSaveScreenShotEx1`, and `capssuSaveScreenShotEx2`.

Observed hardware behavior:

```text
0x384000     => OK
0x384000 - 1 => NullInputBuffer
0x384000 + 1 => OK
0x3C0000     => OK // Tomo life picture size
```

Co-authored-by: yell0wsuit <5692900+yell0wsuit@users.noreply.github.com>
Reviewed-on: https://git.ryujinx.app/projects/Ryubing/pulls/44
2026-05-10 23:57:05 +00:00
Babib3l
bab160d650 Fix Windows fullscreen gap when toggling from maximized (#80)
This PR aims to Fix the Windows fullscreen gap when toggling from maximized state by using canonical Win32 fullscreen approach. When entering fullscreen, the window style is saved and replaced with WS_POPUP | WS_VISIBLE to remove all window chrome (title bar, borders), then SetWindowPos sizes the window to cover the full screen with SWP_FRAMECHANGED to force Win32 to recalculate the frame. On exit, the original window style is restored. This eliminates the few-pixel gap at the top that occurred because Avalonia's WindowState = FullScreen transition from a maximized state retained the title bar non-client area, leaving visible space at the top of the screen (Fullscreen resolution would be 1920 x 1072p on a 1080p monitor, it now correctly renders at 1920 x 1080p)

Reviewed-on: https://git.ryujinx.app/projects/Ryubing/pulls/80
2026-05-10 23:47:39 +00:00
yell0wsuit
e8cc252d9a [HLE] Fix StoreData layout and implement IDatabaseService.Append (#43)
- Fixes `StoreData` layout/update handling so `UpdateLatest` returns the stored Mii data correctly.
- Implements `IDatabaseService.Append` (https://switchbrew.org/wiki/Shared_Database_services#IDatabaseService)

Also adds regression tests for `UpdateLatest` and `Append`.

(Might) fix Mario Kart 8 Deluxe crashing on first boot due to failed Mii verification check (due to custom Mii from emulator), and potentially Tomodachi Life: Living the Dream for the "Import Mii from system" option.

Co-authored-by: yell0wsuit <5692900+yell0wsuit@users.noreply.github.com>
Reviewed-on: https://git.ryujinx.app/projects/Ryubing/pulls/43
2026-05-10 23:39:19 +00:00
Max
8065dec744 [HLE] Renamed INotificationServicesForSystem and implemented a few commands and stubs (#6)
Should allow Ring Fit and other games that rely heavily on the notification system to get in-game (?), needs testing.

if you're wondering what happened to the first branch -- no you're not. (duplicated history somehow??)

Reviewed-on: https://git.ryujinx.app/projects/Ryubing/pulls/6
2026-05-10 23:31:45 +00:00
Shyanne
e9c31bea3b [DEBUG] Implemented NetLog logging type (#5)
Implemented a new debug log type called NetLog and added more verbose logging to the LDN service.
I'd like some feedback on what all should be logged under this category -- I'm likely going to be adding logs for sockets as well, but I want to know specifically if the logging should be more or less verbose and what would be the most helpful things to log.

![image](/attachments/70d5d467-2b57-436b-944f-7bf7a1f609af)

Reviewed-on: https://git.ryujinx.app/projects/Ryubing/pulls/5
2026-05-10 23:29:15 +00:00
Frosch
5511ff5686 fix: gamepads have the same name (#27)
When connecting multiple controllers of the same model, the first device's name ends with (0), the second with (1), the third with (1), the fourth with (1), and so on. To ensure these names are truly unique, GetUniqueGamepadName is now called recursively.

Before:
![image](/attachments/c27ab407-0945-48d8-92a8-6f1fe7fb2727)

After:
![image](/attachments/da7b1427-958c-45d5-8351-6f977d971e1e)

Reviewed-on: https://git.ryujinx.app/projects/Ryubing/pulls/27
2026-05-10 23:19:59 +00:00
Max
bf7f978f9d [HLE] Implemented ILibraryAppletSelfAccessor:1 (#79)
Needed for Tomodachi Life: Living the Dream (?)

based on [this](https://www.reddit.com/r/Ryubing/comments/1t4lfc9/comment/ok4e7tu/)

Reviewed-on: https://git.ryujinx.app/projects/Ryubing/pulls/79
2026-05-10 23:18:53 +00:00
Max
1f9bfab923 Updated OpenGL calls to no longer be deprecated (#83)
- updated SharpCompress 0.47.4 -> 0.48.0 (security)
- set ProcessResult to be nullable (since it is)

Reviewed-on: https://git.ryujinx.app/projects/Ryubing/pulls/83
2026-05-10 23:18:23 +00:00
greem
5d8cb3e378 This stupid bullshit doesn't work, I'm done 2026-05-10 21:09:26 +00:00
GreemDev
708186d8d2 Revert "Update .forgejo/workflows/build.yml"
This reverts commit 2d2661298c.
2026-05-10 15:56:48 -05:00
GreemDev
ad34237fc6 Revert "use new workflow type in conditions"
This reverts commit bf083a716c.
2026-05-10 15:56:44 -05:00
greem
bf083a716c use new workflow type in conditions 2026-05-05 10:12:06 +00:00
greem
2d2661298c Update .forgejo/workflows/build.yml 2026-05-05 09:29:41 +00:00
greem
c4788154fd even more annoying skill issue! 2026-05-05 09:15:54 +00:00
greem
49dd56953c annoying error 2026-05-05 08:52:08 +00:00
greem
722eb93554 Use a dedicated access token instead of the runner-generated one. 2026-05-05 08:07:38 +00:00
ryuadmin
b0179e6433 [ci skip] Improve logging for PR build comment step. 2026-05-05 07:50:58 +00:00
ryuadmin
1d3d4197b7 fix: *hopefully* fix build comments 2026-05-05 06:22:51 +00:00
GreemDev
d2b2d65061 chore: Remove unused variable. 2026-05-04 23:33:28 -05:00
GreemDev
e1dcaef709 fix: don't try to access .length if artifacts is undefined 2026-05-04 23:32:53 -05:00
GreemDev
b222f671f3 fix: use run_number instead of run_id 2026-05-04 23:32:35 -05:00
Babib3l
4d0cd61b6a Fix Windows console hide path targeting the foreground window (#32)
This PR addresses [Ryubing/Issues#345](https://github.com/Ryubing/Issues/issues/345) by fixing the Windows console hide/show path so it only acts on Ryujinx’s own console window instead of whatever window happens to be focused during startup. Previously, when Show Console was disabled, the helper could race with focus changes and end up affecting another app or shell window while leaving the console visible; this change removes that foreground-window dependency and keeps the startup behavior scoped to the Ryujinx console.

Reviewed-on: https://git.ryujinx.app/projects/Ryubing/pulls/32
2026-05-05 03:48:23 +00:00
greem
4e86159bce [ci skip] fix: Invalid workflow templates in github-script source 2026-05-05 03:15:33 +00:00
GreemDev
0d66cfa281 chore: Update actions/github-script to v9 (not sure how this got lost)
also add explicit semicolon for getOctokit
2026-05-04 21:18:33 -05:00
GreemDev
e656de5fff fix: use ubuntu-latest in release.yml post-ci steps. 2026-05-04 20:46:40 -05:00
GreemDev
518dd65484 fix: Collapse PR comment into PR build workflow
Not sure why this was ever separate, and Forgejo doesn't seem to run 'workflow_run` post-execution workflows.
2026-05-04 20:45:45 -05:00
GreemDev
88421959a6 Rework nightly_pr_comment for Forgejo Actions 2026-05-04 20:12:38 -05:00
Max
87ce5162be skia-natives (again) (#78)
Reviewed-on: https://git.ryujinx.app/projects/Ryubing/pulls/78
2026-05-04 12:42:52 +00:00
ryuadmin
a3e10a1e5a [ci skip] Use gradient CSS classes in README.
Recent change in our fork: 50e0549dac
2026-05-04 06:18:29 +00:00
ryuadmin
1e06c86d47 [ci skip] revert 3a3e5e5c5a
broke macOS CI for some reason
2026-05-04 05:38:44 +00:00
Max
3a3e5e5c5a added skia native assets for windows, macOS and switched to no-depend for linux (#77)
im not really sure why these were missing, but theyre here now :woopernod:

Reviewed-on: https://git.ryujinx.app/projects/Ryubing/pulls/77
2026-05-03 19:09:47 +00:00
Max
c1c47d308d revert 96f8d519e6 (#76)
Reviewed-on: https://git.ryujinx.app/projects/Ryubing/pulls/76
2026-05-03 16:27:51 +00:00
Renovate Bot
2b929c5537 Update dependency Microsoft.IdentityModel.JsonWebTokens to 8.18.0 (#75)
This PR contains the following updates:

| Package | Change | [Age](https://docs.renovatebot.com/merge-confidence/) | [Confidence](https://docs.renovatebot.com/merge-confidence/) |
|---|---|---|---|
| [Microsoft.IdentityModel.JsonWebTokens](https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet) | `8.17.0` → `8.18.0` | ![age](https://developer.mend.io/api/mc/badges/age/nuget/Microsoft.IdentityModel.JsonWebTokens/8.18.0?slim=true) | ![confidence](https://developer.mend.io/api/mc/badges/confidence/nuget/Microsoft.IdentityModel.JsonWebTokens/8.17.0/8.18.0?slim=true) |

---

### Configuration

📅 **Schedule**: (UTC)

- Branch creation
  - At any time (no schedule defined)
- Automerge
  - At any time (no schedule defined)

🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update again.

---

 - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box

---

This PR has been generated by [Mend Renovate](https://github.com/renovatebot/renovate).
<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0My4xNjAuMSIsInVwZGF0ZWRJblZlciI6IjQzLjE2MC4xIiwidGFyZ2V0QnJhbmNoIjoibWFzdGVyIiwibGFiZWxzIjpbXX0=-->

Reviewed-on: https://git.ryujinx.app/projects/Ryubing/pulls/75
2026-05-02 19:05:24 +00:00
Renovate Bot
ddfb56c424 Update dependency Newtonsoft.Json to 13.0.4 (#74)
This PR contains the following updates:

| Package | Change | [Age](https://docs.renovatebot.com/merge-confidence/) | [Confidence](https://docs.renovatebot.com/merge-confidence/) |
|---|---|---|---|
| [Newtonsoft.Json](https://www.newtonsoft.com/json) ([source](https://github.com/JamesNK/Newtonsoft.Json)) | `13.0.3` → `13.0.4` | ![age](https://developer.mend.io/api/mc/badges/age/nuget/Newtonsoft.Json/13.0.4?slim=true) | ![confidence](https://developer.mend.io/api/mc/badges/confidence/nuget/Newtonsoft.Json/13.0.3/13.0.4?slim=true) |

---

### Configuration

📅 **Schedule**: (UTC)

- Branch creation
  - At any time (no schedule defined)
- Automerge
  - At any time (no schedule defined)

🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update again.

---

 - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box

---

This PR has been generated by [Mend Renovate](https://github.com/renovatebot/renovate).
<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0My4xNjAuMSIsInVwZGF0ZWRJblZlciI6IjQzLjE2MC4xIiwidGFyZ2V0QnJhbmNoIjoibWFzdGVyIiwibGFiZWxzIjpbXX0=-->

Reviewed-on: https://git.ryujinx.app/projects/Ryubing/pulls/74
2026-05-02 19:05:11 +00:00
Max
c9c4ed67b9 updated a whole bunch of dependencies :) (#48)
hi greem ily :cheart:

Reviewed-on: https://git.ryujinx.app/projects/Ryubing/pulls/48
2026-05-02 16:04:58 +00:00
Renovate Bot
44d77f8e59 Update dependency Microsoft.Build.Utilities.Core to 17.12.50 [SECURITY] (#50)
This PR contains the following updates:

| Package | Change | [Age](https://docs.renovatebot.com/merge-confidence/) | [Confidence](https://docs.renovatebot.com/merge-confidence/) |
|---|---|---|---|
| [Microsoft.Build.Utilities.Core](http://go.microsoft.com/fwlink/?LinkId=624683) ([source](https://github.com/dotnet/msbuild)) | `17.12.6` → `17.12.50` | ![age](https://developer.mend.io/api/mc/badges/age/nuget/Microsoft.Build.Utilities.Core/17.12.50?slim=true) | ![confidence](https://developer.mend.io/api/mc/badges/confidence/nuget/Microsoft.Build.Utilities.Core/17.12.6/17.12.50?slim=true) |

---

### Microsoft Security Advisory CVE-2025-55247 | .NET Denial of Service Vulnerability
BIT-dotnet-2025-55247 / BIT-dotnet-sdk-2025-55247 / [CVE-2025-55247](https://nvd.nist.gov/vuln/detail/CVE-2025-55247) / [GHSA-w3q9-fxm7-j8fq](https://github.com/advisories/GHSA-w3q9-fxm7-j8fq)

<details>
<summary>More information</summary>

#### Details
##### Microsoft Security Advisory CVE-2025-55247 | .NET Denial of Service Vulnerability

##### <a name="executive-summary"></a>Executive summary

Microsoft is releasing this security advisory to provide information about a vulnerability in .NET 8.0.xxx, .NET 9.0.xxx and .NET 10.0.xxx. This advisory also provides guidance on what developers can do to update their environments to remove this vulnerability.

A vulnerability exists in .NET where predictable paths for MSBuild's temporary directories on Linux let another user create the directories ahead of MSBuild, leading to DoS of builds. This only affects .NET on Linux operating systems.

##### Announcement

Announcement for this issue can be found at  https://github.com/dotnet/announcements/issues/370

##### <a name="mitigation-factors"></a>Mitigation factors

Projects which do not utilize the [DownloadFile](https://learn.microsoft.com/visualstudio/msbuild/downloadfile-task)  build task are not susceptible to this vulnerability.

##### <a name="affected-software"></a>Affected software

* Any installation of .NET 10.0.100-rc.1.25451.107 SDK or earlier.
* Any installation of .NET 9.0.110 SDK, .NET 9.0.305 SDK or earlier.
* Any installation of .NET 8.0.120 SDK, .NET 8.0.317 SDK, .NET 8.0.414 SDK or earlier.

##### <a name="affected-packages"></a>Affected Packages

The vulnerability affects any Microsoft .NET Core project if it uses any of affected packages versions listed below

Package name |Affected version | Patched version
------------ |---------------- | -------------------------
[Microsoft.Build.Tasks.Core](https://www.nuget.org/packages/Microsoft.Build.Tasks.Core) | 17.15.0-preview-25277-114 <br />>=17.14.0, <= 17.14.8 <br />>= 17.12.0, <= 17.12.36 <br/> >= 17.11.0, <= 17.11.31<br />  >= 17.10.0, <= 17.10.29 <br />  >= 17.8.0, <= 17.8.29 <br /> | 18.0.0-preview-25476-107 <br />17.14.28 <br />17.12.50 <br/>17.11.48 <br />17.10.46 <br />17.8.43 <br />

Package name|Affected version | Patched version
------------ |---------------|----------------
[Microsoft.Build](https://www.nuget.org/packages/Microsoft.Build) | 17.15.0-preview-25277-114 <br />>=17.14.0, <= 17.14.8 <br />>= 17.12.0, <= 17.12.36 <br/> >= 17.11.0, <= 17.11.31<br />  >= 17.10.0, <= 17.10.29 <br />  >= 17.8.0, <= 17.8.29 <br /> | 18.0.0-preview-25476-107 <br />17.14.28 <br />17.12.50 <br/>17.11.48 <br />17.10.46 <br />17.8.43 <br />

Package name  |Affected version | Patched version
------------ |---------------|----------------
[Microsoft.Build.Utilities.core](https://www.nuget.org/packages/Microsoft.Build.Utilities.Core/17.15.0-preview-25277-114)| 17.15.0-preview-25277-114 <br />>=17.14.0, <= 17.14.8 <br />>= 17.12.0, <= 17.12.36 <br/> >= 17.11.0, <= 17.11.31<br />  >= 17.10.0, <= 17.10.29 <br />  >= 17.8.0, <= 17.8.29 <br /> | 18.0.0-preview-25476-107 <br />17.14.28 <br />17.12.50 <br/>17.11.48 <br />17.10.46 <br />17.8.43 <br />

##### Advisory FAQ

##### <a name="how-affected"></a>How do I know if I am affected?

If you have a .NET SDK with a version listed, or an affected package listed in [affected software](#affected-packages) or [affected packages](#affected-software), you're exposed to the vulnerability.

##### <a name="how-fix"></a>How do I fix the issue?

1. To fix the issue please install the latest version of .NET 10.0 SDK, .NET 9.0 SDK or .NET 8.0 SDK. If you have installed one or more .NET SDKs through Visual Studio, Visual Studio will prompt you to update Visual Studio, which will also update your .NET  SDKs.
2. If your application references the vulnerable package, update the package reference to the patched version.

* You can list the versions you have installed by running the `dotnet --info` command. You will see output like the following;

```
.NET SDK:
 Version:           9.0.100
 Commit:            59db016f11
 Workload version:  9.0.100-manifests.3068a692
 MSBuild version:   17.12.7+5b8665660

Runtime Environment:
 OS Name:     Mac OS X
 OS Version:  15.2
 OS Platform: Darwin
 RID:         osx-arm64
 Base Path:   /usr/local/share/dotnet/sdk/9.0.100/

.NET workloads installed:
There are no installed workloads to display.
Configured to use loose manifests when installing new manifests.

Host:
  Version:      9.0.0
  Architecture: arm64
  Commit:       9d5a6a9aa4

.NET SDKs installed:
  9.0.100 [/usr/local/share/dotnet/sdk]

.NET runtimes installed:
  Microsoft.AspNetCore.App 9.0.0 [/usr/local/share/dotnet/shared/Microsoft.AspNetCore.App]
  Microsoft.NETCore.App 9.0.0 [/usr/local/share/dotnet/shared/Microsoft.NETCore.App]

Other architectures found:
  x64   [/usr/local/share/dotnet]
    registered at [/etc/dotnet/install_location_x64]

Environment variables:
  Not set

global.json file:
  Not found

Learn more:
  https://aka.ms/dotnet/info

Download .NET:
  https://aka.ms/dotnet/download
```

* If you're using .NET 10.0, you should download and install the appropriate SDK: `.NET 10.0.100-rc.2.25502.107` for Visual Studio 2026 v18 Preview 3. Download from https://dotnet.microsoft.com/download/dotnet-core/10.0.

* If you're using .NET 9.0, you should download and install the appropriate SDK: `.NET 9.0.306` for Visual Studio 2022 v17.14 or `.NET 9.0.111` for v17.12. Download from https://dotnet.microsoft.com/download/dotnet-core/9.0.

* If you're using .NET 8.0, you should download and install the appropriate SDK: `.NET 8.0.415` for Visual Studio 2022 v17.11, `.NET 8.0.318` for v17.10, or `.NET 8.0.121` for v17.8. Download from https://dotnet.microsoft.com/download/dotnet-core/8.0.

Once you have installed the updated SDK, restart your apps for the update to take effect.

##### Other Information

##### Reporting Security Issues

If you have found a potential security issue in .NET 8.0, .NET 9.0 or .NET 10.0, please email details to secure@microsoft.com. Reports may qualify for the Microsoft .NET Core & .NET 5 Bounty. Details of the Microsoft .NET Bounty Program including terms and conditions are at <https://aka.ms/corebounty>.

##### Support

You can ask questions about this issue on GitHub in the .NET GitHub organization. The main repos are located at https://github.com/dotnet/runtime. The Announcements repo (https://github.com/dotnet/Announcements) will contain this bulletin as an issue and will include a link to a discussion issue. You can ask questions in the linked discussion issue.

##### Disclaimer

The information provided in this advisory is provided "as is" without warranty of any kind. Microsoft disclaims all warranties, either express or implied, including the warranties of merchantability and fitness for a particular purpose. In no event shall Microsoft Corporation or its suppliers be liable for any damages whatsoever including direct, indirect, incidental, consequential, loss of business profits or special damages, even if Microsoft Corporation or its suppliers have been advised of the possibility of such damages. Some states do not allow the exclusion or limitation of liability for consequential or incidental damages so the foregoing limitation may not apply.

##### External Links

[CVE-2025-55247]( https://www.cve.org/CVERecord?id=CVE-2025-55247)

##### Revisions

V1.0 (October 14, 2025): Advisory published.

#### Severity
- CVSS Score: 7.3 / 10 (High)
- Vector String: `CVSS:3.1/AV:L/AC:L/PR:L/UI:R/S:U/C:H/I:H/A:H`

#### References
- [https://github.com/dotnet/msbuild/security/advisories/GHSA-w3q9-fxm7-j8fq](https://github.com/dotnet/msbuild/security/advisories/GHSA-w3q9-fxm7-j8fq)
- [https://nvd.nist.gov/vuln/detail/CVE-2025-55247](https://nvd.nist.gov/vuln/detail/CVE-2025-55247)
- [https://github.com/dotnet/msbuild](https://github.com/dotnet/msbuild)
- [https://msrc.microsoft.com/update-guide/vulnerability/CVE-2025-55247](https://msrc.microsoft.com/update-guide/vulnerability/CVE-2025-55247)

This data is provided by [OSV](https://osv.dev/vulnerability/GHSA-w3q9-fxm7-j8fq) and the [GitHub Advisory Database](https://github.com/github/advisory-database) ([CC-BY 4.0](https://github.com/github/advisory-database/blob/main/LICENSE.md)).
</details>

---

### Configuration

📅 **Schedule**: (UTC)

- Branch creation
  - ""
- Automerge
  - At any time (no schedule defined)

🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update again.

---

 - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box

---

This PR has been generated by [Mend Renovate](https://github.com/renovatebot/renovate).
<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0My4xNjAuMSIsInVwZGF0ZWRJblZlciI6IjQzLjE2MC4xIiwidGFyZ2V0QnJhbmNoIjoibWFzdGVyIiwibGFiZWxzIjpbXX0=-->

Reviewed-on: https://git.ryujinx.app/projects/Ryubing/pulls/50
2026-05-02 15:45:20 +00:00
Renovate Bot
bd11cbde08 [ci skip] Update actions/upload-artifact action to v5 (#55)
This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
| [actions/upload-artifact](https://git.ryujinx.app/actions/upload-artifact) | action | major | `v4` → `v5` |

---

### Release Notes

<details>
<summary>actions/upload-artifact (actions/upload-artifact)</summary>

### [`v5`](https://git.ryujinx.app/actions/upload-artifact/compare/v4.3.1...v5)

[Compare Source](https://git.ryujinx.app/actions/upload-artifact/compare/v4.3.1...v5)

### [`v4.3.1`](https://git.ryujinx.app/actions/upload-artifact/compare/v4.3.0...v4.3.1)

[Compare Source](https://git.ryujinx.app/actions/upload-artifact/compare/v4.3.0...v4.3.1)

### [`v4.3.0`](https://git.ryujinx.app/actions/upload-artifact/compare/v4.2.0...v4.3.0)

[Compare Source](https://git.ryujinx.app/actions/upload-artifact/compare/v4.2.0...v4.3.0)

### [`v4.2.0`](https://git.ryujinx.app/actions/upload-artifact/compare/v4.1.0...v4.2.0)

[Compare Source](https://git.ryujinx.app/actions/upload-artifact/compare/v4.1.0...v4.2.0)

### [`v4.1.0`](https://git.ryujinx.app/actions/upload-artifact/compare/v4...v4.1.0)

[Compare Source](https://git.ryujinx.app/actions/upload-artifact/compare/v4...v4.1.0)

</details>

Reviewed-on: https://git.ryujinx.app/projects/Ryubing/pulls/55
2026-05-02 06:37:21 +00:00
Renovate Bot
f749cf90b6 [ci skip] Update actions/setup-dotnet action to v5 (#54)
This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
| [actions/setup-dotnet](https://git.ryujinx.app/actions/setup-dotnet) | action | major | `v4` → `v5` |

---

### Release Notes

<details>
<summary>actions/setup-dotnet (actions/setup-dotnet)</summary>

### [`v5.2.0`](https://git.ryujinx.app/actions/setup-dotnet/compare/v5.1.0...v5.2.0)

[Compare Source](https://git.ryujinx.app/actions/setup-dotnet/compare/v5.1.0...v5.2.0)

### [`v5.1.0`](https://git.ryujinx.app/actions/setup-dotnet/compare/v5.0.1...v5.1.0)

[Compare Source](https://git.ryujinx.app/actions/setup-dotnet/compare/v5.0.1...v5.1.0)

### [`v5.0.1`](https://git.ryujinx.app/actions/setup-dotnet/compare/v5...v5.0.1)

[Compare Source](https://git.ryujinx.app/actions/setup-dotnet/compare/v5...v5.0.1)

### [`v5.0.0`](https://git.ryujinx.app/actions/setup-dotnet/compare/v5...v5)

[Compare Source](https://git.ryujinx.app/actions/setup-dotnet/compare/v5...v5)

### [`v5`](https://git.ryujinx.app/actions/setup-dotnet/compare/v4.3.1...v5)

[Compare Source](https://git.ryujinx.app/actions/setup-dotnet/compare/v4.3.1...v5)

### [`v4.3.1`](https://git.ryujinx.app/actions/setup-dotnet/compare/v4.3.0...v4.3.1)

[Compare Source](https://git.ryujinx.app/actions/setup-dotnet/compare/v4.3.0...v4.3.1)

### [`v4.3.0`](https://git.ryujinx.app/actions/setup-dotnet/compare/v4.2.0...v4.3.0)

[Compare Source](https://git.ryujinx.app/actions/setup-dotnet/compare/v4.2.0...v4.3.0)

### [`v4.2.0`](https://git.ryujinx.app/actions/setup-dotnet/compare/v4.1.0...v4.2.0)

[Compare Source](https://git.ryujinx.app/actions/setup-dotnet/compare/v4.1.0...v4.2.0)

### [`v4.1.0`](https://git.ryujinx.app/actions/setup-dotnet/compare/v4.0.1...v4.1.0)

[Compare Source](https://git.ryujinx.app/actions/setup-dotnet/compare/v4.0.1...v4.1.0)

### [`v4.0.1`](https://git.ryujinx.app/actions/setup-dotnet/compare/v4...v4.0.1)

[Compare Source](https://git.ryujinx.app/actions/setup-dotnet/compare/v4...v4.0.1)

</details>

Reviewed-on: https://git.ryujinx.app/projects/Ryubing/pulls/54
2026-05-02 06:36:28 +00:00
Renovate Bot
1b1ceeaa11 [ci skip] Update actions/github-script action to v9 (#53)
This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
| [actions/github-script](https://git.ryujinx.app/actions/github-script) | action | major | `v6` → `v9` |

---

### Release Notes

<details>
<summary>actions/github-script (actions/github-script)</summary>

### [`v9.0.0`](https://git.ryujinx.app/actions/github-script/compare/v9...v9)

[Compare Source](https://git.ryujinx.app/actions/github-script/compare/v9...v9)

### [`v9`](https://git.ryujinx.app/actions/github-script/compare/v8...v9)

[Compare Source](https://git.ryujinx.app/actions/github-script/compare/v8...v9)

### [`v8.0.0`](https://git.ryujinx.app/actions/github-script/compare/v8...v8)

[Compare Source](https://git.ryujinx.app/actions/github-script/compare/v8...v8)

### [`v8`](https://git.ryujinx.app/actions/github-script/compare/v7.1.0...v8)

[Compare Source](https://git.ryujinx.app/actions/github-script/compare/v7.1.0...v8)

### [`v7.1.0`](https://git.ryujinx.app/actions/github-script/compare/v7.0.1...v7.1.0)

[Compare Source](https://git.ryujinx.app/actions/github-script/compare/v7.0.1...v7.1.0)

### [`v7.0.1`](https://git.ryujinx.app/actions/github-script/compare/v7...v7.0.1)

[Compare Source](https://git.ryujinx.app/actions/github-script/compare/v7...v7.0.1)

### [`v7.0.0`](https://git.ryujinx.app/actions/github-script/compare/v7...v7)

[Compare Source](https://git.ryujinx.app/actions/github-script/compare/v7...v7)

### [`v7`](https://git.ryujinx.app/actions/github-script/compare/v6.4.1...v7)

[Compare Source](https://git.ryujinx.app/actions/github-script/compare/v6.4.1...v7)

### [`v6.4.1`](https://git.ryujinx.app/actions/github-script/compare/v6.4.0...v6.4.1)

[Compare Source](https://git.ryujinx.app/actions/github-script/compare/v6.4.0...v6.4.1)

### [`v6.4.0`](https://git.ryujinx.app/actions/github-script/compare/v6.3.3...v6.4.0)

[Compare Source](https://git.ryujinx.app/actions/github-script/compare/v6.3.3...v6.4.0)

### [`v6.3.3`](https://git.ryujinx.app/actions/github-script/compare/v6.3.2...v6.3.3)

[Compare Source](https://git.ryujinx.app/actions/github-script/compare/v6.3.2...v6.3.3)

### [`v6.3.2`](https://git.ryujinx.app/actions/github-script/compare/v6.3.1...v6.3.2)

[Compare Source](https://git.ryujinx.app/actions/github-script/compare/v6.3.1...v6.3.2)

### [`v6.3.1`](https://git.ryujinx.app/actions/github-script/compare/v6.3.0...v6.3.1)

[Compare Source](https://git.ryujinx.app/actions/github-script/compare/v6.3.0...v6.3.1)

### [`v6.3.0`](https://git.ryujinx.app/actions/github-script/compare/v6.2.0...v6.3.0)

[Compare Source](https://git.ryujinx.app/actions/github-script/compare/v6.2.0...v6.3.0)

### [`v6.2.0`](https://git.ryujinx.app/actions/github-script/compare/v6.1.1...v6.2.0)

[Compare Source](https://git.ryujinx.app/actions/github-script/compare/v6.1.1...v6.2.0)

### [`v6.1.1`](https://git.ryujinx.app/actions/github-script/compare/v6.1.0...v6.1.1)

[Compare Source](https://git.ryujinx.app/actions/github-script/compare/v6.1.0...v6.1.1)

### [`v6.1.0`](https://git.ryujinx.app/actions/github-script/compare/v6...v6.1.0)

[Compare Source](https://git.ryujinx.app/actions/github-script/compare/v6...v6.1.0)

</details>

Reviewed-on: https://git.ryujinx.app/projects/Ryubing/pulls/53
2026-05-02 06:07:19 +00:00
Renovate Bot
3f9d37da83 [ci skip] Update actions/checkout action to v6 (#52)
This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
| [actions/checkout](https://git.ryujinx.app/actions/checkout) | action | major | `v4` → `v6` |

---

### Release Notes

<details>
<summary>actions/checkout (actions/checkout)</summary>

### [`v6.0.2`](https://git.ryujinx.app/actions/checkout/blob/HEAD/CHANGELOG.md#v602)

[Compare Source](https://git.ryujinx.app/actions/checkout/compare/v6.0.1...v6.0.2)

- Fix tag handling: preserve annotations and explicit fetch-tags by [@&#8203;ericsciple](https://github.com/ericsciple) in [#&#8203;2356](https://github.com/actions/checkout/pull/2356)

### [`v6.0.1`](https://git.ryujinx.app/actions/checkout/blob/HEAD/CHANGELOG.md#v601)

[Compare Source](https://git.ryujinx.app/actions/checkout/compare/v6...v6.0.1)

- Add worktree support for persist-credentials includeIf by [@&#8203;ericsciple](https://github.com/ericsciple) in [#&#8203;2327](https://github.com/actions/checkout/pull/2327)

### [`v6.0.0`](https://git.ryujinx.app/actions/checkout/blob/HEAD/CHANGELOG.md#v600)

[Compare Source](https://git.ryujinx.app/actions/checkout/compare/v6...v6)

- Persist creds to a separate file by [@&#8203;ericsciple](https://github.com/ericsciple) in [#&#8203;2286](https://github.com/actions/checkout/pull/2286)
- Update README to include Node.js 24 support details and requirements by [@&#8203;salmanmkc](https://github.com/salmanmkc) in [#&#8203;2248](https://github.com/actions/checkout/pull/2248)

### [`v6`](https://git.ryujinx.app/actions/checkout/blob/HEAD/CHANGELOG.md#v602)

[Compare Source](https://git.ryujinx.app/actions/checkout/compare/v5.0.1...v6)

- Fix tag handling: preserve annotations and explicit fetch-tags by [@&#8203;ericsciple](https://github.com/ericsciple) in [#&#8203;2356](https://github.com/actions/checkout/pull/2356)

### [`v5.0.1`](https://git.ryujinx.app/actions/checkout/blob/HEAD/CHANGELOG.md#v501)

[Compare Source](https://git.ryujinx.app/actions/checkout/compare/v5...v5.0.1)

- Port v6 cleanup to v5 by [@&#8203;ericsciple](https://github.com/ericsciple) in [#&#8203;2301](https://github.com/actions/checkout/pull/2301)

### [`v5.0.0`](https://git.ryujinx.app/actions/checkout/blob/HEAD/CHANGELOG.md#v500)

[Compare Source](https://git.ryujinx.app/actions/checkout/compare/v5...v5)

- Update actions checkout to use node 24 by [@&#8203;salmanmkc](https://github.com/salmanmkc) in [#&#8203;2226](https://github.com/actions/checkout/pull/2226)

### [`v5`](https://git.ryujinx.app/actions/checkout/blob/HEAD/CHANGELOG.md#v501)

[Compare Source](https://git.ryujinx.app/actions/checkout/compare/v4.3.1...v5)

- Port v6 cleanup to v5 by [@&#8203;ericsciple](https://github.com/ericsciple) in [#&#8203;2301](https://github.com/actions/checkout/pull/2301)

### [`v4.3.1`](https://git.ryujinx.app/actions/checkout/blob/HEAD/CHANGELOG.md#v431)

[Compare Source](https://git.ryujinx.app/actions/checkout/compare/v4.3.0...v4.3.1)

- Port v6 cleanup to v4 by [@&#8203;ericsciple](https://github.com/ericsciple) in [#&#8203;2305](https://github.com/actions/checkout/pull/2305)

### [`v4.3.0`](https://git.ryujinx.app/actions/checkout/blob/HEAD/CHANGELOG.md#v430)

[Compare Source](https://git.ryujinx.app/actions/checkout/compare/v4.2.2...v4.3.0)

- docs: update README.md by [@&#8203;motss](https://github.com/motss) in [#&#8203;1971](https://github.com/actions/checkout/pull/1971)
- Add internal repos for checking out multiple repositories by [@&#8203;mouismail](https://github.com/mouismail) in [#&#8203;1977](https://github.com/actions/checkout/pull/1977)
- Documentation update - add recommended permissions to Readme by [@&#8203;benwells](https://github.com/benwells) in [#&#8203;2043](https://github.com/actions/checkout/pull/2043)
- Adjust positioning of user email note and permissions heading by [@&#8203;joshmgross](https://github.com/joshmgross) in [#&#8203;2044](https://github.com/actions/checkout/pull/2044)
- Update README.md by [@&#8203;nebuk89](https://github.com/nebuk89) in [#&#8203;2194](https://github.com/actions/checkout/pull/2194)
- Update CODEOWNERS for actions by [@&#8203;TingluoHuang](https://github.com/TingluoHuang) in [#&#8203;2224](https://github.com/actions/checkout/pull/2224)
- Update package dependencies by [@&#8203;salmanmkc](https://github.com/salmanmkc) in [#&#8203;2236](https://github.com/actions/checkout/pull/2236)

### [`v4.2.2`](https://git.ryujinx.app/actions/checkout/blob/HEAD/CHANGELOG.md#v422)

[Compare Source](https://git.ryujinx.app/actions/checkout/compare/v4.2.1...v4.2.2)

- `url-helper.ts` now leverages well-known environment variables by [@&#8203;jww3](https://github.com/jww3) in [#&#8203;1941](https://github.com/actions/checkout/pull/1941)
- Expand unit test coverage for `isGhes` by [@&#8203;jww3](https://github.com/jww3) in [#&#8203;1946](https://github.com/actions/checkout/pull/1946)

### [`v4.2.1`](https://git.ryujinx.app/actions/checkout/blob/HEAD/CHANGELOG.md#v421)

[Compare Source](https://git.ryujinx.app/actions/checkout/compare/v4.2.0...v4.2.1)

- Check out other refs/\* by commit if provided, fall back to ref by [@&#8203;orhantoy](https://github.com/orhantoy) in [#&#8203;1924](https://github.com/actions/checkout/pull/1924)

### [`v4.2.0`](https://git.ryujinx.app/actions/checkout/blob/HEAD/CHANGELOG.md#v420)

[Compare Source](https://git.ryujinx.app/actions/checkout/compare/v4.1.7...v4.2.0)

- Add Ref and Commit outputs by [@&#8203;lucacome](https://github.com/lucacome) in [#&#8203;1180](https://github.com/actions/checkout/pull/1180)
- Dependency updates by [@&#8203;dependabot-](https://github.com/dependabot-) [#&#8203;1777](https://github.com/actions/checkout/pull/1777), [#&#8203;1872](https://github.com/actions/checkout/pull/1872)

### [`v4.1.7`](https://git.ryujinx.app/actions/checkout/blob/HEAD/CHANGELOG.md#v417)

[Compare Source](https://git.ryujinx.app/actions/checkout/compare/v4.1.6...v4.1.7)

- Bump the minor-npm-dependencies group across 1 directory with 4 updates by [@&#8203;dependabot](https://github.com/dependabot) in [#&#8203;1739](https://github.com/actions/checkout/pull/1739)
- Bump actions/checkout from 3 to 4 by [@&#8203;dependabot](https://github.com/dependabot) in [#&#8203;1697](https://github.com/actions/checkout/pull/1697)
- Check out other refs/\* by commit by [@&#8203;orhantoy](https://github.com/orhantoy) in [#&#8203;1774](https://github.com/actions/checkout/pull/1774)
- Pin actions/checkout's own workflows to a known, good, stable version. by [@&#8203;jww3](https://github.com/jww3) in [#&#8203;1776](https://github.com/actions/checkout/pull/1776)

### [`v4.1.6`](https://git.ryujinx.app/actions/checkout/blob/HEAD/CHANGELOG.md#v416)

[Compare Source](https://git.ryujinx.app/actions/checkout/compare/v4.1.5...v4.1.6)

- Check platform to set archive extension appropriately by [@&#8203;cory-miller](https://github.com/cory-miller) in [#&#8203;1732](https://github.com/actions/checkout/pull/1732)

### [`v4.1.5`](https://git.ryujinx.app/actions/checkout/blob/HEAD/CHANGELOG.md#v415)

[Compare Source](https://git.ryujinx.app/actions/checkout/compare/v4.1.4...v4.1.5)

- Update NPM dependencies by [@&#8203;cory-miller](https://github.com/cory-miller) in [#&#8203;1703](https://github.com/actions/checkout/pull/1703)
- Bump github/codeql-action from 2 to 3 by [@&#8203;dependabot](https://github.com/dependabot) in [#&#8203;1694](https://github.com/actions/checkout/pull/1694)
- Bump actions/setup-node from 1 to 4 by [@&#8203;dependabot](https://github.com/dependabot) in [#&#8203;1696](https://github.com/actions/checkout/pull/1696)
- Bump actions/upload-artifact from 2 to 4 by [@&#8203;dependabot](https://github.com/dependabot) in [#&#8203;1695](https://github.com/actions/checkout/pull/1695)
- README: Suggest `user.email` to be `41898282+github-actions[bot]@&#8203;users.noreply.github.com` by [@&#8203;cory-miller](https://github.com/cory-miller) in [#&#8203;1707](https://github.com/actions/checkout/pull/1707)

### [`v4.1.4`](https://git.ryujinx.app/actions/checkout/blob/HEAD/CHANGELOG.md#v414)

[Compare Source](https://git.ryujinx.app/actions/checkout/compare/v4.1.3...v4.1.4)

- Disable `extensions.worktreeConfig` when disabling `sparse-checkout` by [@&#8203;jww3](https://github.com/jww3) in [#&#8203;1692](https://github.com/actions/checkout/pull/1692)
- Add dependabot config by [@&#8203;cory-miller](https://github.com/cory-miller) in [#&#8203;1688](https://github.com/actions/checkout/pull/1688)
- Bump the minor-actions-dependencies group with 2 updates by [@&#8203;dependabot](https://github.com/dependabot) in [#&#8203;1693](https://github.com/actions/checkout/pull/1693)
- Bump word-wrap from 1.2.3 to 1.2.5 by [@&#8203;dependabot](https://github.com/dependabot) in [#&#8203;1643](https://github.com/actions/checkout/pull/1643)

### [`v4.1.3`](https://git.ryujinx.app/actions/checkout/blob/HEAD/CHANGELOG.md#v413)

[Compare Source](https://git.ryujinx.app/actions/checkout/compare/v4.1.2...v4.1.3)

- Check git version before attempting to disable `sparse-checkout` by [@&#8203;jww3](https://github.com/jww3) in [#&#8203;1656](https://github.com/actions/checkout/pull/1656)
- Add SSH user parameter by [@&#8203;cory-miller](https://github.com/cory-miller) in [#&#8203;1685](https://github.com/actions/checkout/pull/1685)
- Update `actions/checkout` version in `update-main-version.yml` by [@&#8203;jww3](https://github.com/jww3) in [#&#8203;1650](https://github.com/actions/checkout/pull/1650)

### [`v4.1.2`](https://git.ryujinx.app/actions/checkout/blob/HEAD/CHANGELOG.md#v412)

[Compare Source](https://git.ryujinx.app/actions/checkout/compare/v4.1.1...v4.1.2)

- Fix: Disable sparse checkout whenever `sparse-checkout` option is not present [@&#8203;dscho](https://github.com/dscho) in [#&#8203;1598](https://github.com/actions/checkout/pull/1598)

### [`v4.1.1`](https://git.ryujinx.app/actions/checkout/blob/HEAD/CHANGELOG.md#v411)

[Compare Source](https://git.ryujinx.app/actions/checkout/compare/v4.1.0...v4.1.1)

- Correct link to GitHub Docs by [@&#8203;peterbe](https://github.com/peterbe) in [#&#8203;1511](https://github.com/actions/checkout/pull/1511)
- Link to release page from what's new section by [@&#8203;cory-miller](https://github.com/cory-miller) in [#&#8203;1514](https://github.com/actions/checkout/pull/1514)

### [`v4.1.0`](https://git.ryujinx.app/actions/checkout/blob/HEAD/CHANGELOG.md#v410)

[Compare Source](https://git.ryujinx.app/actions/checkout/compare/v4...v4.1.0)

- [Add support for partial checkout filters](https://github.com/actions/checkout/pull/1396)

</details>

Reviewed-on: https://git.ryujinx.app/projects/Ryubing/pulls/52
2026-05-02 06:06:40 +00:00
Renovate Bot
0c0b6404b1 Configure Renovate (#49)
This PR has been generated by [Mend Renovate](https://github.com/renovatebot/renovate).

Co-authored-by: GreemDev <greemdev@ryujinx.app>
Reviewed-on: https://git.ryujinx.app/projects/Ryubing/pulls/49
2026-05-02 03:34:38 +00:00
Max
47c0180ba4 [CPU] Increased base JIT cache size (#22)
@MaxLastBreath found that The Legend of Zelda: Tears of the Kingdom experiences inconsistent crashing with a limited JIT cache size. Increasing the cache size seems to help with this, and increasing it shouldn't degrade performance for other titles.

@LotP plans to look this over later, but for now, this should suffice as a fix for affected users.

Reviewed-on: https://git.ryujinx.app/projects/Ryubing/pulls/22
2026-05-02 02:49:59 +00:00
Max
8705fabdb0 UI: LoadGuestApplication asynchronous cancellation (#1)
Fixed LoadGuestApplication hanging when cancelled.
Since startup procedure has technically changed, we should consider testing this with a variety of game formats to ensure regressions do not occur.
Closes [#20](https://github.com/Ryubing/Issues/issues/20)

Reviewed-on: https://git.ryujinx.app/projects/Ryubing/pulls/1
2026-05-02 02:30:57 +00:00
sh0inx
2a4eb8c529 revert b7f42de5a6 (#46)
@Schuay asked for a revert: see [here](https://github.com/Ryubing/Issues/issues/394#issuecomment-4359848762)

revert Disable SDL3 HIDAPI Switch driver when hid_nintendo is loaded

On Linux, the hid_nintendo kernel module creates separate evdev
devices for gamepad input and IMU (motion sensors), which SDL3's
evdev backend properly combines. When HIDAPI is active instead, it
conflicts with the kernel driver, breaking motion/gyro input and
controller hotplugging (especially via wireless dongles).

Automatically detect hid_nintendo via /sys/module and fall back to
the evdev backend. Can still be overridden with the env var
SDL_JOYSTICK_HIDAPI_SWITCH=1.

Reviewed-on: https://git.ryujinx.app/projects/Ryubing/pulls/46
2026-05-02 01:03:53 +00:00
Max
51d0d888fb Fixed crash on Windows when a controller is connected (#47)
Updates ``Ryujinx.SDL3-CS`` to ``2026.501.0``.

@KeatonTheBot found that [this commit](d5fca9628a) was crashing Ryujinx with ``Fatal Error. 0xC0000005 at SDL.SDL3.SDL_Init(SDL.SDL_InitFlags)``. Once SDL [patched the issue](4a52a5ee04), he updated the [nuget package](https://www.nuget.org/packages/Ryujinx.SDL3-CS).

Reviewed-on: https://git.ryujinx.app/projects/Ryubing/pulls/47
2026-05-02 01:01:59 +00:00
ryuadmin
a69eab4619 infra: Allow issues to be made, but still direct people to GitHub. (#45)
This is done so we can use Renovate Bot. The Dependency Dashboard feature uses Forgejo issues.
2026-05-01 08:22:16 +00:00
Djibril
93f23cde4f Added and fixed some german translations. (!41)
I fixed and added some german translations, as some were missing.

Reviewed-on: https://git.ryujinx.app/projects/Ryubing/pulls/41
2026-04-29 19:22:07 +00:00
60 changed files with 1337 additions and 564 deletions

View File

@@ -0,0 +1,5 @@
blank_issues_enabled: true
contact_links:
- name: Ryubing Issue Tracker
url: https://github.com/Ryubing/Issues/issues/
about: "Please use this GitHub repository instead of creating issues on this Forgejo repository. Blank issues should only be used by maintainers and authorized bots. Issues made on this repository can and will be deleted."

35
.forgejo/renovate.json Normal file
View File

@@ -0,0 +1,35 @@
{
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
"extends": [
"renovate/config"
],
"enabledManagers": ["nuget", "github-actions"],
"packageRules": [
{
// require approval for *all* NuGet package updates, not just major versions.
"matchDepTypes": "nuget",
"dependencyDashboardApproval": true
},
{
// Ignore Gommon for automatic updates. I make breaking changes on minor updates not infrequently.
"matchDepNames": "Gommon",
"matchDepTypes": "nuget",
"enabled": false
},
{
"description": "group Silk.NET packages",
"extends": ["renovate/config//groups/silkdotnet.json"],
"groupName": "Silk.NET"
},
{
"description": "group OpenTK packages",
"extends": ["renovate/config//groups/opentk.json"],
"groupName": "OpenTK"
},
{
"description": "group Svg.Controls packages",
"extends": ["renovate/config//groups/svgcontrols.json"],
"groupName": "Svg.Controls"
}
]
}

View File

@@ -21,7 +21,7 @@ jobs:
name: ${{ matrix.platform.name }} (${{ matrix.configuration }}) name: ${{ matrix.platform.name }} (${{ matrix.configuration }})
runs-on: docker runs-on: docker
container: container:
image: ghcr.io/catthehacker/ubuntu:act-latest image: ghcr.io/gruke-build/ubuntu:dotnet-latest
timeout-minutes: 45 timeout-minutes: 45
strategy: strategy:
matrix: matrix:
@@ -35,20 +35,7 @@ jobs:
fail-fast: false fail-fast: false
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v6
- uses: actions/setup-dotnet@v4
with:
global-json-file: global.json
- name: Install GLI
uses: actions/setup-gli@v1
with:
token: ${{ secrets.SETUP_GLI_TOKEN }}
- name: Install 7zip
run: |
sudo apt update && sudo apt install -y 7zip
- name: Overwrite csc problem matcher - name: Overwrite csc problem matcher
run: echo "::add-matcher::.forgejo/csc.json" run: echo "::add-matcher::.forgejo/csc.json"
@@ -94,7 +81,7 @@ jobs:
shell: bash shell: bash
- name: Upload Ryujinx Windows artifact - name: Upload Ryujinx Windows artifact
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v5
with: with:
name: ryujinx-${{ matrix.configuration }}-${{ steps.version_info.outputs.result }}+${{ steps.version_info.outputs.git_short_hash }}-${{ matrix.platform.zip_os_name }} name: ryujinx-${{ matrix.configuration }}-${{ steps.version_info.outputs.result }}+${{ steps.version_info.outputs.git_short_hash }}-${{ matrix.platform.zip_os_name }}
path: artifact path: artifact
@@ -107,14 +94,6 @@ jobs:
PLATFORM_NAME="${{ matrix.platform.name }}" PLATFORM_NAME="${{ matrix.platform.name }}"
sudo apt update && sudo apt install -y zsync desktop-file-utils appstream libfuse2t64
mkdir -p tools
export PATH="$PATH:$(readlink -f tools)"
# Setup appimagetool
wget -q -O tools/appimagetool "https://github.com/AppImage/appimagetool/releases/download/continuous/appimagetool-x86_64.AppImage"
chmod +x tools/appimagetool
chmod +x distribution/linux/appimage/build-appimage.sh 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)
@@ -133,7 +112,7 @@ jobs:
shell: bash shell: bash
- name: Upload Ryujinx AppImage artifact - name: Upload Ryujinx AppImage artifact
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v5
if: forgejo.event_name == 'pull_request' && contains(matrix.platform.name, 'linux') if: forgejo.event_name == 'pull_request' && contains(matrix.platform.name, 'linux')
with: with:
name: ryujinx-${{ matrix.configuration }}-${{ steps.version_info.outputs.result }}+${{ steps.version_info.outputs.git_short_hash }}-${{ matrix.platform.zip_os_name }}-AppImage name: ryujinx-${{ matrix.configuration }}-${{ steps.version_info.outputs.result }}+${{ steps.version_info.outputs.git_short_hash }}-${{ matrix.platform.zip_os_name }}-AppImage
@@ -141,18 +120,16 @@ jobs:
build_macos: build_macos:
name: macOS Universal (${{ matrix.configuration }}) name: macOS Universal (${{ matrix.configuration }})
runs-on: ubuntu-latest runs-on: docker
container:
image: ghcr.io/gruke-build/ubuntu:dotnet-latest
timeout-minutes: 45 timeout-minutes: 45
strategy: strategy:
matrix: matrix:
configuration: [ Release ] configuration: [ Release ]
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v6
- uses: actions/setup-dotnet@v4
with:
global-json-file: global.json
- name: Setup LLVM 17 - name: Setup LLVM 17
run: | run: |
@@ -160,11 +137,6 @@ jobs:
chmod +x llvm.sh chmod +x llvm.sh
sudo ./llvm.sh 17 sudo ./llvm.sh 17
- name: Install GLI
uses: actions/setup-gli@v1
with:
token: ${{ secrets.SETUP_GLI_TOKEN }}
- name: Install rcodesign - name: Install rcodesign
run: | run: |
gli ghr -R indygreg/apple-platform-rs -p apple-codesign-*-x86_64-unknown-linux-musl.tar.gz -O apple-codesign.tar.gz gli ghr -R indygreg/apple-platform-rs -p apple-codesign-*-x86_64-unknown-linux-musl.tar.gz -O apple-codesign.tar.gz
@@ -197,7 +169,7 @@ jobs:
shell: bash shell: bash
- name: Upload Ryujinx artifact - name: Upload Ryujinx artifact
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v5
with: with:
name: ryujinx-${{ matrix.configuration }}-${{ steps.version_info.outputs.result }}+${{ steps.version_info.outputs.git_short_hash }}-macos_universal name: ryujinx-${{ matrix.configuration }}-${{ steps.version_info.outputs.result }}+${{ steps.version_info.outputs.git_short_hash }}-macos_universal
path: "publish/*.tar.gz" path: "publish/*.tar.gz"

View File

@@ -31,29 +31,16 @@ jobs:
strategy: strategy:
matrix: matrix:
platform: platform:
- { name: win-x64, os: ghcr.io/catthehacker/ubuntu:act-latest, zip_os_name: win_x64 } - { name: win-x64, os: ghcr.io/gruke-build/ubuntu:dotnet-latest, zip_os_name: win_x64 }
#- { name: win-arm64, os: ghcr.io/catthehacker/ubuntu:act-latest, zip_os_name: win_arm64 } #- { name: win-arm64, os: ghcr.io/gruke-build/ubuntu:dotnet-latest, zip_os_name: win_arm64 }
- { name: linux-x64, os: ghcr.io/catthehacker/ubuntu:act-latest, zip_os_name: linux_x64 } - { name: linux-x64, os: ghcr.io/gruke-build/ubuntu:dotnet-latest, zip_os_name: linux_x64 }
- { name: linux-arm64, os: ghcr.io/catthehacker/ubuntu:act-latest, zip_os_name: linux_arm64 } - { name: linux-arm64, os: ghcr.io/gruke-build/ubuntu:dotnet-latest, zip_os_name: linux_arm64 }
steps: steps:
- uses: actions/checkout@v6 - uses: actions/checkout@v6
- uses: actions/setup-dotnet@v5
with:
global-json-file: global.json
- name: Overwrite csc problem matcher - name: Overwrite csc problem matcher
run: echo "::add-matcher::.forgejo/csc.json" run: echo "::add-matcher::.forgejo/csc.json"
- name: Install GLI
uses: actions/setup-gli@v1
with:
token: ${{ secrets.SETUP_GLI_TOKEN }}
- name: Install 7zip
run: |
sudo apt update && sudo apt install -y 7zip
- name: Get version info - name: Get version info
id: version_info id: version_info
run: | run: |
@@ -105,14 +92,6 @@ jobs:
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 update && sudo apt install -y zsync desktop-file-utils appstream libfuse2t64
mkdir -p tools
export PATH="$PATH:$(readlink -f tools)"
# Setup appimagetool
wget -q -O tools/appimagetool "https://github.com/AppImage/appimagetool/releases/download/continuous/appimagetool-x86_64.AppImage"
chmod +x tools/appimagetool
chmod +x distribution/linux/appimage/build-appimage.sh 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)
@@ -149,24 +128,15 @@ jobs:
name: Release MacOS universal name: Release MacOS universal
runs-on: docker runs-on: docker
container: container:
image: ghcr.io/catthehacker/ubuntu:act-latest image: ghcr.io/gruke-build/ubuntu:dotnet-latest
steps: steps:
- uses: actions/checkout@v6 - uses: actions/checkout@v6
- uses: actions/setup-dotnet@v5
with:
global-json-file: global.json
- name: Setup LLVM 17 - 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 17 sudo ./llvm.sh 17
- name: Install GLI
uses: actions/setup-gli@v1
with:
token: ${{ secrets.SETUP_GLI_TOKEN }}
- name: Install rcodesign - name: Install rcodesign
run: | run: |
@@ -212,18 +182,11 @@ jobs:
name: Post CI Steps name: Post CI Steps
runs-on: docker runs-on: docker
container: container:
image: ghcr.io/catthehacker/ubuntu:act-latest image: ghcr.io/gruke-build/ubuntu:act-latest
needs: needs:
- macos_release - macos_release
- release - release
steps: steps:
- uses: actions/checkout@v4
- name: Install GLI
uses: actions/setup-gli@v1
with:
token: ${{ secrets.SETUP_GLI_TOKEN }}
- name: Get version info - name: Get version info
id: version_info id: version_info
run: | run: |

View File

@@ -10,7 +10,7 @@ jobs:
steps: steps:
# Grab sources to get latest labeler.yml # Grab sources to get latest labeler.yml
- name: Fetch sources - name: Fetch sources
uses: actions/checkout@v4 uses: actions/checkout@v6
with: with:
# Ensure we pin the source origin as pull_request_target run under forks. # Ensure we pin the source origin as pull_request_target run under forks.
fetch-depth: 0 fetch-depth: 0

View File

@@ -25,28 +25,15 @@ jobs:
strategy: strategy:
matrix: matrix:
platform: platform:
- { name: win-x64, os: ghcr.io/catthehacker/ubuntu:act-latest, zip_os_name: win_x64 } - { name: win-x64, os: ghcr.io/gruke-build/ubuntu:act-latest, zip_os_name: win_x64 }
#- { name: win-arm64, os: ghcr.io/catthehacker/ubuntu:act-latest, zip_os_name: win_arm64 } #- { name: win-arm64, os: ghcr.io/gruke-build/ubuntu:act-latest, zip_os_name: win_arm64 }
- { name: linux-x64, os: ghcr.io/catthehacker/ubuntu:act-latest, zip_os_name: linux_x64 } - { name: linux-x64, os: ghcr.io/gruke-build/ubuntu:act-latest, zip_os_name: linux_x64 }
- { name: linux-arm64, os: ghcr.io/catthehacker/ubuntu:act-latest, zip_os_name: linux_arm64 } - { name: linux-arm64, os: ghcr.io/gruke-build/ubuntu:act-latest, zip_os_name: linux_arm64 }
steps: steps:
- uses: actions/checkout@v6 - uses: actions/checkout@v6
- uses: actions/setup-dotnet@v5
with:
global-json-file: global.json
- name: Overwrite csc problem matcher - name: Overwrite csc problem matcher
run: echo "::add-matcher::.github/csc.json" run: echo "::add-matcher::.github/csc.json"
- name: Install 7zip
run: |
sudo apt install -y 7zip
- name: Install GLI
uses: actions/setup-gli@v1
with:
token: ${{ secrets.SETUP_GLI_TOKEN }}
- name: Get version info - name: Get version info
id: version_info id: version_info
@@ -102,14 +89,6 @@ jobs:
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
mkdir -p tools
export PATH="$PATH:$(readlink -f tools)"
# Setup appimagetool
wget -q -O tools/appimagetool "https://github.com/AppImage/appimagetool/releases/download/continuous/appimagetool-x86_64.AppImage"
chmod +x tools/appimagetool
chmod +x distribution/linux/appimage/build-appimage.sh 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)
@@ -145,7 +124,7 @@ jobs:
name: Release MacOS universal name: Release MacOS universal
runs-on: docker runs-on: docker
container: container:
image: ghcr.io/catthehacker/ubuntu:act-latest image: ghcr.io/gruke-build/ubuntu:dotnet-latest
steps: steps:
- uses: actions/checkout@v6 - uses: actions/checkout@v6
@@ -158,11 +137,6 @@ jobs:
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 17 sudo ./llvm.sh 17
- name: Install GLI
uses: actions/setup-gli@v1
with:
token: ${{ secrets.SETUP_GLI_TOKEN }}
- name: Install rcodesign - name: Install rcodesign
run: | run: |
@@ -207,18 +181,13 @@ jobs:
post_ci: post_ci:
name: Post-CI Steps name: Post-CI Steps
runs-on: ubuntu-24.04 runs-on: docker
container:
image: ghcr.io/gruke-build/ubuntu:act-latest
needs: needs:
- macos_release - macos_release
- release - release
steps: steps:
- uses: actions/checkout@v4
- name: Install GLI
uses: actions/setup-gli@v1
with:
token: ${{ secrets.SETUP_GLI_TOKEN }}
- name: Get version info - name: Get version info
id: version_info id: version_info
run: | run: |

View File

@@ -1,61 +0,0 @@
name: Comment PR artifacts links
on:
workflow_run:
workflows: ['Build PR']
types: [completed]
jobs:
pr_comment:
if: github.event.workflow_run.event == 'pull_request' && github.event.workflow_run.conclusion == 'success'
runs-on: ubuntu-latest
steps:
- uses: actions/github-script@v6
with:
script: |
const {owner, repo} = context.repo;
const run_id = ${{github.event.workflow_run.id}};
const pull_head_sha = '${{github.event.workflow_run.head_sha}}';
const issue_number = await (async () => {
const pulls = await github.rest.pulls.list({owner, repo});
for await (const {data} of github.paginate.iterator(pulls)) {
for (const pull of data) {
if (pull.head.sha === pull_head_sha) {
return pull.number;
}
}
}
})();
if (issue_number) {
core.info(`Using pull request ${issue_number}`);
} else {
return core.error(`No matching pull request found`);
}
const {data: {artifacts}} = await github.rest.actions.listWorkflowRunArtifacts({owner, repo, run_id});
if (!artifacts.length) {
return core.error(`No artifacts found`);
}
let body = `Download the artifacts for this pull request:\n`;
let hidden_debug_artifacts = `\n\n <details><summary>Only for Developers</summary>\n`;
for (const art of artifacts) {
const url = `https://nightly.link/${owner}/${repo}/actions/artifacts/${art.id}.zip`;
if (art.name.includes('Debug')) {
hidden_debug_artifacts += `\n* [${art.name}](${url})`;
} else {
body += `\n* [${art.name}](${url})`;
}
}
hidden_debug_artifacts += `\n</details>`;
body += hidden_debug_artifacts;
const {data: comments} = await github.rest.issues.listComments({repo, owner, issue_number});
const existing_comment = comments.find((c) => c.user.login === 'github-actions[bot]');
if (existing_comment) {
core.info(`Updating comment ${existing_comment.id}`);
await github.rest.issues.updateComment({repo, owner, comment_id: existing_comment.id, body});
} else {
core.info(`Creating a comment`);
await github.rest.issues.createComment({repo, owner, issue_number, body});
}

View File

@@ -3,59 +3,65 @@
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally> <ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageVersion Include="Avalonia" Version="11.3.14" /> <PackageVersion Include="Avalonia" Version="11.3.15" />
<PackageVersion Include="Avalonia.Controls.DataGrid" Version="11.3.13" /> <PackageVersion Include="Avalonia.Controls.DataGrid" Version="11.3.13" />
<PackageVersion Include="Avalonia.Desktop" Version="11.3.14" /> <PackageVersion Include="Avalonia.Desktop" Version="11.3.15" />
<PackageVersion Include="Avalonia.Diagnostics" Version="11.3.14" /> <PackageVersion Include="Avalonia.Diagnostics" Version="11.3.15" />
<PackageVersion Include="Avalonia.Markup.Xaml.Loader" Version="11.3.14" /> <PackageVersion Include="Avalonia.Markup.Xaml.Loader" Version="11.3.15" />
<PackageVersion Include="SharpCompress" Version="0.47.4" /> <PackageVersion Include="SharpCompress" Version="0.48.0" />
<PackageVersion Include="Svg.Controls.Avalonia" Version="11.3.9.4" /> <PackageVersion Include="Svg.Controls.Avalonia" Version="11.3.9.5" />
<PackageVersion Include="Svg.Controls.Skia.Avalonia" Version="11.3.9.4" /> <PackageVersion Include="Svg.Controls.Skia.Avalonia" Version="11.3.9.5" />
<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.50" />
<PackageVersion Include="Newtonsoft.Json" Version="13.0.3" /> <PackageVersion Include="Newtonsoft.Json" Version="13.0.4" />
<PackageVersion Include="Projektanker.Icons.Avalonia" Version="9.6.2" /> <PackageVersion Include="Projektanker.Icons.Avalonia" Version="9.6.2" />
<PackageVersion Include="Projektanker.Icons.Avalonia.FontAwesome" Version="9.6.2" /> <PackageVersion Include="Projektanker.Icons.Avalonia.FontAwesome" Version="9.6.2" />
<PackageVersion Include="Projektanker.Icons.Avalonia.MaterialDesign" Version="9.6.2" /> <PackageVersion Include="Projektanker.Icons.Avalonia.MaterialDesign" Version="9.6.2" />
<PackageVersion Include="Ryujinx.SDL3-CS" Version="2026.426.0" /> <PackageVersion Include="Ryujinx.SDL3-CS" Version="2026.501.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.2" />
<PackageVersion Include="Concentus" Version="2.2.2" /> <PackageVersion Include="Concentus" Version="2.2.2" />
<PackageVersion Include="DiscordRichPresence" Version="1.6.1.70" /> <PackageVersion Include="DiscordRichPresence" Version="1.6.1.70" />
<PackageVersion Include="DynamicData" Version="9.4.1" /> <PackageVersion Include="DynamicData" Version="9.4.31" />
<PackageVersion Include="FluentAvaloniaUI" Version="2.5.0" /> <PackageVersion Include="FluentAvaloniaUI" Version="2.5.1" />
<PackageVersion Include="Humanizer" Version="2.14.1" /> <PackageVersion Include="Humanizer" Version="2.14.1" />
<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.3.0" /> <PackageVersion Include="Microsoft.IdentityModel.JsonWebTokens" Version="8.18.0" />
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.9.0" /> <PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.14.1" />
<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" />
<PackageVersion Include="NetCoreServer" Version="8.0.7" /> <PackageVersion Include="NetCoreServer" Version="8.0.7" />
<PackageVersion Include="NUnit" Version="3.13.3" /> <PackageVersion Include="NUnit" Version="3.14.0" />
<PackageVersion Include="NUnit3TestAdapter" Version="4.1.0" /> <PackageVersion Include="NUnit3TestAdapter" Version="4.6.0" />
<PackageVersion Include="OpenTK.Core" Version="4.8.2" /> <PackageVersion Include="OpenTK.Core" Version="4.9.4" />
<PackageVersion Include="OpenTK.Graphics" Version="4.8.2" /> <PackageVersion Include="OpenTK.Graphics" Version="4.9.4" />
<PackageVersion Include="OpenTK.Audio.OpenAL" Version="4.8.2" /> <!-- OpenTk.Audio.OpenAL has moved to OpenTk.Audio -->
<PackageVersion Include="OpenTK.Windowing.GraphicsLibraryFramework" Version="4.8.2" /> <!--<PackageVersion Include="OpenTK.Audio" Version="5.0.0-pre.15" />-->
<PackageVersion Include="OpenTK.Audio.OpenAL" Version="4.9.4" />
<PackageVersion Include="OpenTK.Windowing.GraphicsLibraryFramework" Version="4.9.4" />
<PackageVersion Include="Open.NAT.Core" Version="2.1.0.5" /> <PackageVersion Include="Open.NAT.Core" Version="2.1.0.5" />
<PackageVersion Include="Ryujinx.Audio.OpenAL.Dependencies" Version="1.21.0.1" /> <!-- Ryujinx.Audio.OpenAL.Dependencies is from the original project, last updated 12/30/20 -->
<PackageVersion Include="Ryujinx.Graphics.Nvdec.Dependencies.AllArch" Version="6.1.2-build3" /> <!--<PackageVersion Include="Ryujinx.Audio.OpenAL.Dependencies" Version="1.21.0.1" />-->
<PackageVersion Include="Ryujinx.Audio.OpenAL" Version="1.25.1" />
<PackageVersion Include="Ryujinx.Graphics.Nvdec.Dependencies.AllArch" Version="6.1.4-build6" />
<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.LibHac" Version="0.21.0-alpha.129" /> <PackageVersion Include="Ryujinx.LibHac" Version="0.21.0-alpha.133" />
<PackageVersion Include="Ryujinx.UpdateClient" Version="2.0.6" /> <PackageVersion Include="Ryujinx.UpdateClient" Version="2.0.6" />
<PackageVersion Include="Ryujinx.Systems.Update.Common" Version="2.0.6" /> <PackageVersion Include="Ryujinx.Systems.Update.Common" Version="2.0.6" />
<PackageVersion Include="Gommon" Version="2.8.0.1" /> <PackageVersion Include="Gommon" Version="2.8.1.2" />
<PackageVersion Include="securifybv.ShellLink" Version="0.1.0" /> <PackageVersion Include="securifybv.ShellLink" Version="0.1.0" />
<PackageVersion Include="Sep" Version="0.11.1" /> <PackageVersion Include="Sep" Version="0.13.0" />
<PackageVersion Include="shaderc.net" Version="0.1.0" /> <PackageVersion Include="shaderc.net" Version="0.1.0" />
<PackageVersion Include="Silk.NET.Vulkan" Version="2.22.0" /> <PackageVersion Include="Silk.NET.Vulkan" Version="2.22.0" />
<PackageVersion Include="Silk.NET.Vulkan.Extensions.EXT" Version="2.22.0" /> <PackageVersion Include="Silk.NET.Vulkan.Extensions.EXT" Version="2.22.0" />
<PackageVersion Include="Silk.NET.Vulkan.Extensions.KHR" Version="2.22.0" /> <PackageVersion Include="Silk.NET.Vulkan.Extensions.KHR" Version="2.22.0" />
<PackageVersion Include="SkiaSharp" Version="2.88.9" /> <PackageVersion Include="SkiaSharp" Version="2.88.9" />
<PackageVersion Include="SkiaSharp.NativeAssets.Linux" Version="2.88.9" /> <PackageVersion Include="SkiaSharp.NativeAssets.Win32" Version="2.88.9" />
<PackageVersion Include="SkiaSharp.NativeAssets.macOS" Version="2.88.9" />
<PackageVersion Include="SkiaSharp.NativeAssets.Linux.NoDependencies" 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.2" /> <PackageVersion Include="System.IO.Hashing" Version="9.0.15" />
<PackageVersion Include="UnicornEngine.Unicorn" Version="2.0.2-rc1-fb78016" /> <PackageVersion Include="UnicornEngine.Unicorn" Version="2.1.3" />
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@@ -5,7 +5,7 @@
</td> </td>
<td align="center" width="75%"> <td align="center" width="75%">
# Ryujinx <h1 class="ryu-gradient-text">Ryujinx</h1>
[![Latest release](https://git.ryujinx.app/projects/Ryubing/badges/release.svg?label=stable&color=32cd32)](https://update.ryujinx.app/latest/stable) [![Latest release](https://git.ryujinx.app/projects/Ryubing/badges/release.svg?label=stable&color=32cd32)](https://update.ryujinx.app/latest/stable)
[![Latest canary release](https://git.ryujinx.app/Ryubing/Canary/badges/release.svg?label=canary&color=FF4500)](https://update.ryujinx.app/latest/canary) [![Latest canary release](https://git.ryujinx.app/Ryubing/Canary/badges/release.svg?label=canary&color=FF4500)](https://update.ryujinx.app/latest/canary)
@@ -21,7 +21,7 @@
Ryujinx is an open-source Nintendo Switch emulator, originally created by gdkchan, written in C#. Ryujinx is an open-source Nintendo Switch emulator, originally created by gdkchan, written in C#.
This emulator aims at providing excellent accuracy and performance, a user-friendly interface and consistent builds. This emulator aims at providing excellent accuracy and performance, a user-friendly interface and consistent builds.
It was written from scratch and development on the project began in September 2017. It was written from scratch and development on the project began in September 2017.
Ryujinx is available on a self-managed <a href="https://github.com/Ryubing/forgejo" target="_blank">modified</a> <a href="https://forgejo.org/" target="_blank">Forgejo</a> instance under the <a href="https://git.ryujinx.app/projects/Ryubing/src/branch/master/LICENSE.txt" target="_blank">MIT license</a>. Ryujinx is available on a self-managed <a class="forgejo-gradient-text" href="https://github.com/Ryubing/forgejo" target="_blank">modified Forgejo</a> instance under the <a href="https://git.ryujinx.app/projects/Ryubing/src/branch/master/LICENSE.txt" target="_blank">MIT license</a>.
<br /> <br />
</p> </p>
<p align="center"> <p align="center">

View File

@@ -4,7 +4,7 @@
"ID": "MenuBarActions_StartCapture", "ID": "MenuBarActions_StartCapture",
"Translations": { "Translations": {
"ar_SA": "", "ar_SA": "",
"de_DE": "", "de_DE": "RenderDoc Frame-Aufnahme starten",
"el_GR": "", "el_GR": "",
"en_US": "Start RenderDoc Frame Capture", "en_US": "Start RenderDoc Frame Capture",
"es_ES": "Iniciar una captura de fotograma de RenderDoc", "es_ES": "Iniciar una captura de fotograma de RenderDoc",
@@ -29,7 +29,7 @@
"ID": "MenuBarActions_EndCapture", "ID": "MenuBarActions_EndCapture",
"Translations": { "Translations": {
"ar_SA": "", "ar_SA": "",
"de_DE": "", "de_DE": "RenderDoc Frame-Aufnahme beenden",
"el_GR": "", "el_GR": "",
"en_US": "End RenderDoc Frame Capture", "en_US": "End RenderDoc Frame Capture",
"es_ES": "Detener la captura de fotograma de RenderDoc", "es_ES": "Detener la captura de fotograma de RenderDoc",
@@ -54,7 +54,7 @@
"ID": "MenuBarActions_DiscardCapture", "ID": "MenuBarActions_DiscardCapture",
"Translations": { "Translations": {
"ar_SA": "", "ar_SA": "",
"de_DE": "", "de_DE": "RenderDoc Frame-Aufnahme verwerfen",
"el_GR": "", "el_GR": "",
"en_US": "Discard RenderDoc Frame Capture", "en_US": "Discard RenderDoc Frame Capture",
"es_ES": "Descartar la captura de fotograma de RenderDoc", "es_ES": "Descartar la captura de fotograma de RenderDoc",
@@ -79,7 +79,7 @@
"ID": "MenuBarActions_DiscardCapture_ToolTip", "ID": "MenuBarActions_DiscardCapture_ToolTip",
"Translations": { "Translations": {
"ar_SA": "", "ar_SA": "",
"de_DE": "", "de_DE": "Beendet die jetzige RenderDoc Frame-Aufnahme, verwirft sofort das Ergebnis.",
"el_GR": "", "el_GR": "",
"en_US": "Ends the currently active RenderDoc Frame Capture, immediately discarding its result.", "en_US": "Ends the currently active RenderDoc Frame Capture, immediately discarding its result.",
"es_ES": "Finaliza la captura de fotograma de RenderDoc actualmente activa y descarta inmediatamente su resultado.", "es_ES": "Finaliza la captura de fotograma de RenderDoc actualmente activa y descarta inmediatamente su resultado.",

View File

@@ -4,7 +4,7 @@
"ID": "MenuBarActionsOpenMiiEditor", "ID": "MenuBarActionsOpenMiiEditor",
"Translations": { "Translations": {
"ar_SA": "", "ar_SA": "",
"de_DE": "", "de_DE": "Mii-Editor",
"el_GR": "", "el_GR": "",
"en_US": "Mii Editor", "en_US": "Mii Editor",
"es_ES": "Editor de Mii", "es_ES": "Editor de Mii",
@@ -254,7 +254,7 @@
"ID": "MenuBarFileOpenFromFileError", "ID": "MenuBarFileOpenFromFileError",
"Translations": { "Translations": {
"ar_SA": "", "ar_SA": "",
"de_DE": "Keine Anwendungen im ausgewählten Datei gefunden.", "de_DE": "Keine Anwendungen in ausgewählter Datei gefunden.",
"el_GR": "", "el_GR": "",
"en_US": "No applications found in selected file.", "en_US": "No applications found in selected file.",
"es_ES": "No se encontraron aplicaciones en el archivo seleccionado.", "es_ES": "No se encontraron aplicaciones en el archivo seleccionado.",
@@ -379,7 +379,7 @@
"ID": "MenuBarFileOpenScreenshotsFolder", "ID": "MenuBarFileOpenScreenshotsFolder",
"Translations": { "Translations": {
"ar_SA": "", "ar_SA": "",
"de_DE": "", "de_DE": "Screenshots-Ordner öffnen",
"el_GR": "", "el_GR": "",
"en_US": "Open Screenshots Folder", "en_US": "Open Screenshots Folder",
"es_ES": "Abrir Carpeta de Capturas de Pantalla", "es_ES": "Abrir Carpeta de Capturas de Pantalla",
@@ -579,7 +579,7 @@
"ID": "MenuBarOptionsRestartEmulation", "ID": "MenuBarOptionsRestartEmulation",
"Translations": { "Translations": {
"ar_SA": "", "ar_SA": "",
"de_DE": "", "de_DE": "Emulation neustarten",
"el_GR": "", "el_GR": "",
"en_US": "Restart Emulation", "en_US": "Restart Emulation",
"es_ES": "Reiniciar Emulación", "es_ES": "Reiniciar Emulación",
@@ -1054,7 +1054,7 @@
"ID": "MenuBarActionsTools", "ID": "MenuBarActionsTools",
"Translations": { "Translations": {
"ar_SA": "", "ar_SA": "",
"de_DE": "", "de_DE": "Werkzeuge",
"el_GR": "", "el_GR": "",
"en_US": "Tools", "en_US": "Tools",
"es_ES": "Herramientas", "es_ES": "Herramientas",
@@ -1354,7 +1354,7 @@
"ID": "MenuBarHelpMultiplayer", "ID": "MenuBarHelpMultiplayer",
"Translations": { "Translations": {
"ar_SA": "متعدد اللاعبين (LDN/LAN)", "ar_SA": "متعدد اللاعبين (LDN/LAN)",
"de_DE": "", "de_DE": "Mehrspieler (LDN/LAN)",
"el_GR": "Πολλαπλοί Παίκτες (LDN/LAN)", "el_GR": "Πολλαπλοί Παίκτες (LDN/LAN)",
"en_US": "Multiplayer (LDN/LAN)", "en_US": "Multiplayer (LDN/LAN)",
"es_ES": "Multijugador (LDN/LAN)", "es_ES": "Multijugador (LDN/LAN)",
@@ -1804,7 +1804,7 @@
"ID": "GameListSortStatusNameAscending", "ID": "GameListSortStatusNameAscending",
"Translations": { "Translations": {
"ar_SA": "", "ar_SA": "",
"de_DE": "", "de_DE": "Titel: A-Z",
"el_GR": "Όνομα: A-Z", "el_GR": "Όνομα: A-Z",
"en_US": "Title: A-Z", "en_US": "Title: A-Z",
"es_ES": "Título: A-Z", "es_ES": "Título: A-Z",
@@ -1829,7 +1829,7 @@
"ID": "GameListSortStatusNameDescending", "ID": "GameListSortStatusNameDescending",
"Translations": { "Translations": {
"ar_SA": "", "ar_SA": "",
"de_DE": "", "de_DE": "Titel: Z-A",
"el_GR": "Τίτλος: Z-A", "el_GR": "Τίτλος: Z-A",
"en_US": "Title: Z-A", "en_US": "Title: Z-A",
"es_ES": "Título: Z-A", "es_ES": "Título: Z-A",
@@ -1954,7 +1954,7 @@
"ID": "GameListHeaderTitleId", "ID": "GameListHeaderTitleId",
"Translations": { "Translations": {
"ar_SA": "", "ar_SA": "",
"de_DE": "", "de_DE": "Titel ID:",
"el_GR": "", "el_GR": "",
"en_US": "Title ID:", "en_US": "Title ID:",
"es_ES": "ID del Titulo:", "es_ES": "ID del Titulo:",
@@ -2254,7 +2254,7 @@
"ID": "GameListContextMenuCacheManagementNukePptc", "ID": "GameListContextMenuCacheManagementNukePptc",
"Translations": { "Translations": {
"ar_SA": "", "ar_SA": "",
"de_DE": "", "de_DE": "PPTC Cache löschen",
"el_GR": "", "el_GR": "",
"en_US": "Purge PPTC Cache", "en_US": "Purge PPTC Cache",
"es_ES": "Purgar Caché PPTC", "es_ES": "Purgar Caché PPTC",
@@ -2279,7 +2279,7 @@
"ID": "GameListContextMenuCacheManagementPurgeShaderCache", "ID": "GameListContextMenuCacheManagementPurgeShaderCache",
"Translations": { "Translations": {
"ar_SA": "تنظيف ذاكرة مرشحات الفيديو المؤقتة", "ar_SA": "تنظيف ذاكرة مرشحات الفيديو المؤقتة",
"de_DE": "Shader Cache löschen", "de_DE": "Shader-Cache löschen",
"el_GR": "Εκκαθάριση Προσωρινής Μνήμης Shader", "el_GR": "Εκκαθάριση Προσωρινής Μνήμης Shader",
"en_US": "Purge Shader Cache", "en_US": "Purge Shader Cache",
"es_ES": "Limpiar Caché de Sombreadores", "es_ES": "Limpiar Caché de Sombreadores",
@@ -2604,7 +2604,7 @@
"ID": "GameListContextMenuCreateShortcutToolTip", "ID": "GameListContextMenuCreateShortcutToolTip",
"Translations": { "Translations": {
"ar_SA": "أنشئ اختصار سطح مكتب لتشغيل التطبيق المحدد.", "ar_SA": "أنشئ اختصار سطح مكتب لتشغيل التطبيق المحدد.",
"de_DE": "Erstelle eine Desktop-Verknüpfung die die gewählte Anwendung startet.", "de_DE": "Erstelle eine Desktop-Verknüpfung, die die gewählte Anwendung startet.",
"el_GR": "Δημιουργία συντόμευσης επιφάνειας εργασίας που ανοίγει την επιλεγμένη εφαρμογή.", "el_GR": "Δημιουργία συντόμευσης επιφάνειας εργασίας που ανοίγει την επιλεγμένη εφαρμογή.",
"en_US": "Create a Desktop Shortcut that launches the selected Application.", "en_US": "Create a Desktop Shortcut that launches the selected Application.",
"es_ES": "Crear un acceso directo en el escritorio que lance la aplicación seleccionada.", "es_ES": "Crear un acceso directo en el escritorio que lance la aplicación seleccionada.",
@@ -2629,7 +2629,7 @@
"ID": "GameListContextMenuCreateCustomConfiguration", "ID": "GameListContextMenuCreateCustomConfiguration",
"Translations": { "Translations": {
"ar_SA": "", "ar_SA": "",
"de_DE": "", "de_DE": "Eigene Konfiguration erstellen",
"el_GR": "", "el_GR": "",
"en_US": "Create Custom Configuration", "en_US": "Create Custom Configuration",
"es_ES": "Crear Configuración Personalizada", "es_ES": "Crear Configuración Personalizada",
@@ -2654,7 +2654,7 @@
"ID": "GameListContextMenuEditCustomConfiguration", "ID": "GameListContextMenuEditCustomConfiguration",
"Translations": { "Translations": {
"ar_SA": "", "ar_SA": "",
"de_DE": "", "de_DE": "Eigene Konfiguration bearbeiten",
"el_GR": "", "el_GR": "",
"en_US": "Edit Custom Configuration", "en_US": "Edit Custom Configuration",
"es_ES": "Editar Configuración Personalizada", "es_ES": "Editar Configuración Personalizada",
@@ -2679,7 +2679,7 @@
"ID": "GameListContextMenuCreateShortcutToolTipMacOS", "ID": "GameListContextMenuCreateShortcutToolTipMacOS",
"Translations": { "Translations": {
"ar_SA": "أنشئ اختصار يُشغل التطبيق المحدد في مجلد تطبيقات macOS.", "ar_SA": "أنشئ اختصار يُشغل التطبيق المحدد في مجلد تطبيقات macOS.",
"de_DE": "Erstellen Sie eine Verknüpfung im MacOS-Programme-Ordner, die die ausgewählte Anwendung startet.", "de_DE": "Erstellt eine Verknüpfung im MacOS-Programme-Ordner, die die ausgewählte Anwendung startet.",
"el_GR": "", "el_GR": "",
"en_US": "Create a shortcut in macOS's Applications folder that launches the selected Application.", "en_US": "Create a shortcut in macOS's Applications folder that launches the selected Application.",
"es_ES": "Crea un acceso directo en la carpeta de Aplicaciones de macOS que inicie la Aplicación seleccionada.", "es_ES": "Crea un acceso directo en la carpeta de Aplicaciones de macOS que inicie la Aplicación seleccionada.",
@@ -2704,7 +2704,7 @@
"ID": "GameListContextMenuShowCompatEntry", "ID": "GameListContextMenuShowCompatEntry",
"Translations": { "Translations": {
"ar_SA": "", "ar_SA": "",
"de_DE": "", "de_DE": "Kompatibilitätseintrag",
"el_GR": "", "el_GR": "",
"en_US": "Compatibility Entry", "en_US": "Compatibility Entry",
"es_ES": "Entrada de Compatibilidad", "es_ES": "Entrada de Compatibilidad",
@@ -2729,7 +2729,7 @@
"ID": "GameListContextMenuShowCompatEntryToolTip", "ID": "GameListContextMenuShowCompatEntryToolTip",
"Translations": { "Translations": {
"ar_SA": "", "ar_SA": "",
"de_DE": "", "de_DE": "Zeigt das ausgewählte Spiel in der Kompatibilitätsliste an, die normalerweise über das Hilfe-Menü aufgerufen werden kann.",
"el_GR": "", "el_GR": "",
"en_US": "Shows the selected game in the Compatibility List, normally accessible via the Help menu.", "en_US": "Shows the selected game in the Compatibility List, normally accessible via the Help menu.",
"es_ES": "Mostra el juego seleccionado en la lista de compatibilidad, a la que normalmente se accede desde el menú Ayuda.", "es_ES": "Mostra el juego seleccionado en la lista de compatibilidad, a la que normalmente se accede desde el menú Ayuda.",
@@ -2754,7 +2754,7 @@
"ID": "CreateCustomConfigurationToolTip", "ID": "CreateCustomConfigurationToolTip",
"Translations": { "Translations": {
"ar_SA": "ينشئ تكوينًا مستقلًا للعبة الحالية", "ar_SA": "ينشئ تكوينًا مستقلًا للعبة الحالية",
"de_DE": "Erstellt eine unabhängige Konfiguration für das aktuelle Spiel", "de_DE": "Erstellt eine benutzerdefinierte Konfiguration für das ausgewählte Spiel",
"el_GR": "Δημιουργεί μια ανεξάρτητη διαμόρφωση για το τρέχον παιχνίδι", "el_GR": "Δημιουργεί μια ανεξάρτητη διαμόρφωση για το τρέχον παιχνίδι",
"en_US": "Creates an independent configuration for the selected game", "en_US": "Creates an independent configuration for the selected game",
"es_ES": "Crea una configuración independiente para el juego actual", "es_ES": "Crea una configuración independiente para el juego actual",
@@ -2779,7 +2779,7 @@
"ID": "EditCustomConfigurationToolTip", "ID": "EditCustomConfigurationToolTip",
"Translations": { "Translations": {
"ar_SA": "", "ar_SA": "",
"de_DE": "", "de_DE": "Bearbeitet deine benutzerdefinierte Konfiguration für das ausgewählte Spiel.",
"el_GR": "", "el_GR": "",
"en_US": "Edit your existing independent configuration for the selected game", "en_US": "Edit your existing independent configuration for the selected game",
"es_ES": "Editar su configuración independiente existente para el juego seleccionado", "es_ES": "Editar su configuración independiente existente para el juego seleccionado",
@@ -2804,7 +2804,7 @@
"ID": "GameListContextMenuShowGameData", "ID": "GameListContextMenuShowGameData",
"Translations": { "Translations": {
"ar_SA": "", "ar_SA": "",
"de_DE": "", "de_DE": "Spiel-Informationen",
"el_GR": "", "el_GR": "",
"en_US": "Game Info", "en_US": "Game Info",
"es_ES": "Información del Juego", "es_ES": "Información del Juego",
@@ -2904,7 +2904,7 @@
"ID": "GameListContextMenuTrimXCI", "ID": "GameListContextMenuTrimXCI",
"Translations": { "Translations": {
"ar_SA": "", "ar_SA": "",
"de_DE": "", "de_DE": "XCI-Datei prüfen & trimmen",
"el_GR": "", "el_GR": "",
"en_US": "Check & Trim XCI File", "en_US": "Check & Trim XCI File",
"es_ES": "Verificar & Recortar Archivo XCI", "es_ES": "Verificar & Recortar Archivo XCI",
@@ -2929,7 +2929,7 @@
"ID": "GameListContextMenuTrimXCIToolTip", "ID": "GameListContextMenuTrimXCIToolTip",
"Translations": { "Translations": {
"ar_SA": "", "ar_SA": "",
"de_DE": "", "de_DE": "Trimmt und prüft die XCI-Datei um Speicherplatz zu sparen.",
"el_GR": "", "el_GR": "",
"en_US": "Check and Trim XCI File to Save Disk Space", "en_US": "Check and Trim XCI File to Save Disk Space",
"es_ES": "Verificar y Recortar Archivo XCI para Ahorrar Espacio en Disco", "es_ES": "Verificar y Recortar Archivo XCI para Ahorrar Espacio en Disco",
@@ -3004,7 +3004,7 @@
"ID": "StatusBarXCIFileTrimming", "ID": "StatusBarXCIFileTrimming",
"Translations": { "Translations": {
"ar_SA": "", "ar_SA": "",
"de_DE": "", "de_DE": "XCI-Datei trimmen: '{0}'",
"el_GR": "", "el_GR": "",
"en_US": "Trimming XCI File '{0}'", "en_US": "Trimming XCI File '{0}'",
"es_ES": "Recortando el Siguiente Archivo XCI: '{0}'", "es_ES": "Recortando el Siguiente Archivo XCI: '{0}'",
@@ -3604,7 +3604,7 @@
"ID": "SettingsTabGeneralDisableInputWhenOutOfFocus", "ID": "SettingsTabGeneralDisableInputWhenOutOfFocus",
"Translations": { "Translations": {
"ar_SA": "", "ar_SA": "",
"de_DE": "", "de_DE": "Eingabe deaktivieren wenn nicht fokussiert",
"el_GR": "", "el_GR": "",
"en_US": "Disable Input when Out of Focus", "en_US": "Disable Input when Out of Focus",
"es_ES": "Desactivar la Entrada al Perder el Foco", "es_ES": "Desactivar la Entrada al Perder el Foco",
@@ -4629,7 +4629,7 @@
"ID": "SettingsTabSystemSystemTimeMatch", "ID": "SettingsTabSystemSystemTimeMatch",
"Translations": { "Translations": {
"ar_SA": "", "ar_SA": "",
"de_DE": "", "de_DE": "Mit Systemzeit synchronisieren",
"el_GR": "", "el_GR": "",
"en_US": "Match System Time", "en_US": "Match System Time",
"es_ES": "Usar Hora del Sistema", "es_ES": "Usar Hora del Sistema",
@@ -4704,7 +4704,7 @@
"ID": "SettingsTabSystemTurboMultiplier", "ID": "SettingsTabSystemTurboMultiplier",
"Translations": { "Translations": {
"ar_SA": "", "ar_SA": "",
"de_DE": "", "de_DE": "Turbo-Modus Multiplikator:",
"el_GR": "", "el_GR": "",
"en_US": "Turbo Mode Multiplier:", "en_US": "Turbo Mode Multiplier:",
"es_ES": "Multiplicator del Modo Turbo:", "es_ES": "Multiplicator del Modo Turbo:",
@@ -5154,7 +5154,7 @@
"ID": "SettingsTabSystemIgnoreControllerApplet", "ID": "SettingsTabSystemIgnoreControllerApplet",
"Translations": { "Translations": {
"ar_SA": "", "ar_SA": "",
"de_DE": "", "de_DE": "Controller-Applet ignorieren",
"el_GR": "", "el_GR": "",
"en_US": "Ignore Controller Applet", "en_US": "Ignore Controller Applet",
"es_ES": "Ignorar el Applet del Controlador", "es_ES": "Ignorar el Applet del Controlador",
@@ -6100,6 +6100,31 @@
"zh_TW": "檔案系統全域存取日誌模式:" "zh_TW": "檔案系統全域存取日誌模式:"
} }
}, },
{
"ID": "SettingsTabLoggingEnableNetLogs",
"Translations": {
"ar_SA": "",
"de_DE": "",
"el_GR": "",
"en_US": "Enable Net Logs",
"es_ES": "Habilitar registros de red.",
"fr_FR": "",
"he_IL": "",
"it_IT": "",
"ja_JP": "",
"ko_KR": "",
"no_NO": "",
"pl_PL": "",
"pt_BR": "",
"ru_RU": "",
"sv_SE": "",
"th_TH": "",
"tr_TR": "",
"uk_UA": "",
"zh_CN": "",
"zh_TW": ""
}
},
{ {
"ID": "SettingsTabLoggingDeveloperOptions", "ID": "SettingsTabLoggingDeveloperOptions",
"Translations": { "Translations": {
@@ -6479,7 +6504,7 @@
"ID": "SettingsButtonResetConfirm", "ID": "SettingsButtonResetConfirm",
"Translations": { "Translations": {
"ar_SA": "", "ar_SA": "",
"de_DE": "", "de_DE": "Ich möchte meine Einstellungen zurücksetzen.",
"el_GR": "", "el_GR": "",
"en_US": "I want to reset my settings.", "en_US": "I want to reset my settings.",
"es_ES": "Quiero restablecer mi Configuración.", "es_ES": "Quiero restablecer mi Configuración.",
@@ -12804,7 +12829,7 @@
"ID": "DialogRebooterMessage", "ID": "DialogRebooterMessage",
"Translations": { "Translations": {
"ar_SA": "من فضلك انتظر، المحاكي في طور إعادة التشغيل", "ar_SA": "من فضلك انتظر، المحاكي في طور إعادة التشغيل",
"de_DE": "Bitte warten Sie, der Emulator wird neu gestartet", "de_DE": "Bitte warte, der Emulator wird neu gestartet",
"el_GR": "Παρακαλώ περιμένετε, ο εξομοιωτής επανεκκινείται", "el_GR": "Παρακαλώ περιμένετε, ο εξομοιωτής επανεκκινείται",
"en_US": "Please wait, the emulator is restarting", "en_US": "Please wait, the emulator is restarting",
"es_ES": "Por favor, espere, el emulador se está reiniciando", "es_ES": "Por favor, espere, el emulador se está reiniciando",
@@ -14379,7 +14404,7 @@
"ID": "DialogUserProfileUnsavedChangesMessage", "ID": "DialogUserProfileUnsavedChangesMessage",
"Translations": { "Translations": {
"ar_SA": "لقد قمت بإجراء تغييرات على الملف الشخصي لهذا المستخدم هذا ولم يتم حفظها.", "ar_SA": "لقد قمت بإجراء تغييرات على الملف الشخصي لهذا المستخدم هذا ولم يتم حفظها.",
"de_DE": "Sie haben Änderungen an diesem Nutzerprofil vorgenommen, die nicht gespeichert wurden.", "de_DE": "Du hast Änderungen an diesem Nutzerprofil vorgenommen, die nicht gespeichert wurden.",
"el_GR": "Έχετε κάνει αλλαγές σε αυτό το προφίλ χρήστη που δεν έχουν αποθηκευτεί.", "el_GR": "Έχετε κάνει αλλαγές σε αυτό το προφίλ χρήστη που δεν έχουν αποθηκευτεί.",
"en_US": "You have made changes to this user profile that have not been saved.", "en_US": "You have made changes to this user profile that have not been saved.",
"es_ES": "Ha realizado cambios en este perfil de usuario que no han sido guardados.", "es_ES": "Ha realizado cambios en este perfil de usuario que no han sido guardados.",
@@ -14404,7 +14429,7 @@
"ID": "DialogUserProfileUnsavedChangesSubMessage", "ID": "DialogUserProfileUnsavedChangesSubMessage",
"Translations": { "Translations": {
"ar_SA": "هل تريد تجاهل التغييرات؟", "ar_SA": "هل تريد تجاهل التغييرات؟",
"de_DE": "Möchten Sie Ihre Änderungen wirklich verwerfen?", "de_DE": "Möchtest du deine Änderungen wirklich verwerfen?",
"el_GR": "Θέλετε να απορρίψετε τις αλλαγές σας;", "el_GR": "Θέλετε να απορρίψετε τις αλλαγές σας;",
"en_US": "Do you want to discard your changes?", "en_US": "Do you want to discard your changes?",
"es_ES": "¿Quieres descartar los cambios realizados?", "es_ES": "¿Quieres descartar los cambios realizados?",
@@ -16104,7 +16129,7 @@
"ID": "AddGameDirBoxTooltip", "ID": "AddGameDirBoxTooltip",
"Translations": { "Translations": {
"ar_SA": "أدخل مسار الدليل أو استخدم زر \"إضافة\"...", "ar_SA": "أدخل مسار الدليل أو استخدم زر \"إضافة\"...",
"de_DE": "Geben Sie einen Pfad ein oder nutzen Sie \"Hinzufügen\"...", "de_DE": "Gebe einen Pfad ein oder nutze \"Hinzufügen\"...",
"el_GR": "Εισαγάγετε διαδρομή ή χρησιμοποιήστε το \"Προσθήκη\"...", "el_GR": "Εισαγάγετε διαδρομή ή χρησιμοποιήστε το \"Προσθήκη\"...",
"en_US": "Enter a directory path or use the \"Add\" button...", "en_US": "Enter a directory path or use the \"Add\" button...",
"es_ES": "Escriba una ruta o use el botón \"Agregar\"...", "es_ES": "Escriba una ruta o use el botón \"Agregar\"...",
@@ -16654,7 +16679,7 @@
"ID": "SkipUserProfilesTooltip", "ID": "SkipUserProfilesTooltip",
"Translations": { "Translations": {
"ar_SA": "", "ar_SA": "",
"de_DE": "Diese Option überspringt den Dialog 'Benutzerprofile verwalten' während des Spiels und verwendet ein voreingestelltes Profil.\n\nDie Profilumschaltung finden Sie unter 'Einstellungen' - 'Benutzerprofile verwalten'. Wählen Sie das gewünschte Profil aus, bevor Sie das Spiel laden.", "de_DE": "Diese Option überspringt den Dialog 'Benutzerprofile verwalten' während des Spiels und verwendet ein voreingestelltes Profil.\n\nDie Profilumschaltung findest du unter 'Einstellungen' - 'Benutzerprofile verwalten'. Wähle das gewünschte Profil aus, bevor du das Spiel lädst.",
"el_GR": "Αυτή η επιλογή παρακάμπτει το παράθυρο διαλόγου 'Διαχειριστής Προφίλ Χρήστη' κατά τη διάρκεια του παιχνιδιού, χρησιμοποιώντας ένα προεπιλεγμένο προφίλ.\n\nΗ εναλλαγή προφίλ βρίσκεται στις 'Ρυθμίσεις' - 'Διαχειριστής Προφίλ Χρήστη'. Επιλέξτε το επιθυμητό προφίλ πριν φορτώσετε το παιχνίδι.", "el_GR": "Αυτή η επιλογή παρακάμπτει το παράθυρο διαλόγου 'Διαχειριστής Προφίλ Χρήστη' κατά τη διάρκεια του παιχνιδιού, χρησιμοποιώντας ένα προεπιλεγμένο προφίλ.\n\nΗ εναλλαγή προφίλ βρίσκεται στις 'Ρυθμίσεις' - 'Διαχειριστής Προφίλ Χρήστη'. Επιλέξτε το επιθυμητό προφίλ πριν φορτώσετε το παιχνίδι.",
"en_US": "This option skips the 'Manage User Profiles' dialog during gameplay, using a pre-selected profile.\n\nProfile switching is found in 'Options' - 'User Profiles'. Select the desired profile before loading the game.", "en_US": "This option skips the 'Manage User Profiles' dialog during gameplay, using a pre-selected profile.\n\nProfile switching is found in 'Options' - 'User Profiles'. Select the desired profile before loading the game.",
"es_ES": "Esta opción omite el diálogo de 'Gestionar perfiles de usuario' durante el juego, utilizando un perfil preseleccionado.\n\nEl cambio de perfil se encuentra en 'Opciones' - 'Perfiles de Usuario'. Seleccione el perfil deseado antes de cargar el juego.", "es_ES": "Esta opción omite el diálogo de 'Gestionar perfiles de usuario' durante el juego, utilizando un perfil preseleccionado.\n\nEl cambio de perfil se encuentra en 'Opciones' - 'Perfiles de Usuario'. Seleccione el perfil deseado antes de cargar el juego.",
@@ -16754,7 +16779,7 @@
"ID": "ResolutionScaleTooltip", "ID": "ResolutionScaleTooltip",
"Translations": { "Translations": {
"ar_SA": "يضاعف دقة عرض اللعبة.\n\nقد لا تعمل بعض الألعاب مع هذا وتبدو منقطة حتى عند زيادة الدقة؛ بالنسبة لهذه الألعاب، قد تحتاج إلى العثور على تعديلات تزيل تنعيم الحواف أو تزيد من دقة العرض الداخلي. لاستخدام الأخير، من المحتمل أن ترغب في تحديد أصلي.\n\nيمكن تغيير هذا الخيار أثناء تشغيل اللعبة بالنقر فوق \"تطبيق\" أدناه؛ يمكنك ببساطة تحريك نافذة الإعدادات جانبًا والتجربة حتى تجد المظهر المفضل للعبة.\n\nضع في اعتبارك أن 4x مبالغة في أي إعداد تقريبًا.", "ar_SA": "يضاعف دقة عرض اللعبة.\n\nقد لا تعمل بعض الألعاب مع هذا وتبدو منقطة حتى عند زيادة الدقة؛ بالنسبة لهذه الألعاب، قد تحتاج إلى العثور على تعديلات تزيل تنعيم الحواف أو تزيد من دقة العرض الداخلي. لاستخدام الأخير، من المحتمل أن ترغب في تحديد أصلي.\n\nيمكن تغيير هذا الخيار أثناء تشغيل اللعبة بالنقر فوق \"تطبيق\" أدناه؛ يمكنك ببساطة تحريك نافذة الإعدادات جانبًا والتجربة حتى تجد المظهر المفضل للعبة.\n\nضع في اعتبارك أن 4x مبالغة في أي إعداد تقريبًا.",
"de_DE": "Multipliziert die Rendering-Auflösung des Spiels.\n\nEinige wenige Spiele funktionieren damit nicht und sehen auch bei höherer Auflösung pixelig aus; für diese Spiele müssen Sie möglicherweise Mods finden, die Anti-Aliasing entfernen oder die interne Rendering-Auflösung erhöhen. Für die Verwendung von Letzterem sollten Sie Native wählen.\n\nSie können diese Option ändern, während ein Spiel läuft, indem Sie unten auf \"Übernehmen\" klicken; Sie können das Einstellungsfenster einfach zur Seite schieben und experimentieren, bis Sie Ihr bevorzugtes Aussehen für ein Spiel gefunden haben.\n\nDenken Sie daran, dass 4x für praktisch jedes Setup Overkill ist.", "de_DE": "Multipliziert die Rendering-Auflösung des Spiels.\n\nEinige wenige Spiele funktionieren damit nicht und sehen auch bei höherer Auflösung pixelig aus; für diese Spiele musst du möglicherweise Mods finden, die Anti-Aliasing entfernen oder die interne Rendering-Auflösung erhöhen. Für die Verwendung von Letzterem solltest du Native wählen.\n\nDu kannst diese Option ändern, während ein Spiel läuft, indem du unten auf \"Übernehmen\" klickst; Du kannst das Einstellungsfenster einfach zur Seite schieben und experimentieren, bis das bevorzugte Aussehen für ein Spiel gefunden wurde.\n\nDenk daran, dass 4x für praktisch jedes Setup Overkill ist.",
"el_GR": "", "el_GR": "",
"en_US": "Multiplies the game's rendering resolution.\n\nA few games may not work with this and look pixelated even when the resolution is increased; for those games, you may need to find mods that remove anti-aliasing or that increase their internal rendering resolution. For using the latter, you'll likely want to select Native.\n\nThis option can be changed while a game is running by clicking \"Apply\" below; you can simply move the settings window aside and experiment until you find your preferred look for a game.\n\nKeep in mind 4x is overkill for virtually any setup.", "en_US": "Multiplies the game's rendering resolution.\n\nA few games may not work with this and look pixelated even when the resolution is increased; for those games, you may need to find mods that remove anti-aliasing or that increase their internal rendering resolution. For using the latter, you'll likely want to select Native.\n\nThis option can be changed while a game is running by clicking \"Apply\" below; you can simply move the settings window aside and experiment until you find your preferred look for a game.\n\nKeep in mind 4x is overkill for virtually any setup.",
"es_ES": "Multiplica la resolución de rendereo del juego.\n\nAlgunos juegos podrían no funcionar con esto y verse pixelado al aumentar la resolución; en esos casos, quizás sería necesario buscar mods que de anti-aliasing o que aumenten la resolución interna. Para usar este último, probablemente necesitarás seleccionar Nativa.\n\nEsta opción puede ser modificada mientras que un juego este corriendo haciendo click en \"Aplicar\" más abajo; simplemente puedes mover la ventana de configuración a un lado y experimentar hasta que encuentres tu estilo preferido para un juego.\n\nTener en cuenta que 4x es excesivo para prácticamente cualquier configuración.", "es_ES": "Multiplica la resolución de rendereo del juego.\n\nAlgunos juegos podrían no funcionar con esto y verse pixelado al aumentar la resolución; en esos casos, quizás sería necesario buscar mods que de anti-aliasing o que aumenten la resolución interna. Para usar este último, probablemente necesitarás seleccionar Nativa.\n\nEsta opción puede ser modificada mientras que un juego este corriendo haciendo click en \"Aplicar\" más abajo; simplemente puedes mover la ventana de configuración a un lado y experimentar hasta que encuentres tu estilo preferido para un juego.\n\nTener en cuenta que 4x es excesivo para prácticamente cualquier configuración.",
@@ -16829,7 +16854,7 @@
"ID": "AspectRatioTooltip", "ID": "AspectRatioTooltip",
"Translations": { "Translations": {
"ar_SA": "يتم تطبيق نسبة العرض إلى الارتفاع على نافذة العارض.\n\nقم بتغيير هذا فقط إذا كنت تستخدم تعديل نسبة العرض إلى الارتفاع للعبتك، وإلا سيتم تمديد الرسومات.\n\nاتركه16:9 إذا لم تكن متأكدا.", "ar_SA": "يتم تطبيق نسبة العرض إلى الارتفاع على نافذة العارض.\n\nقم بتغيير هذا فقط إذا كنت تستخدم تعديل نسبة العرض إلى الارتفاع للعبتك، وإلا سيتم تمديد الرسومات.\n\nاتركه16:9 إذا لم تكن متأكدا.",
"de_DE": "Seitenverhältnis, das auf das Renderer-Fenster angewendet wird.\n\nÄndern Sie dies nur, wenn Sie einen Seitenverhältnis-Mod für Ihr Spiel verwenden, da sonst die Grafik gestreckt wird.\n\nLassen Sie es auf 16:9, wenn Sie unsicher sind.", "de_DE": "Seitenverhältnis, das auf das Renderer-Fenster angewendet wird.\n\nÄnder dies nur, wenn ein Seitenverhältnis-Mod für das Spiel verwendet wird, da sonst die Grafik gestreckt wird.\n\nLass es auf 16:9, wenn du unsicher bist.",
"el_GR": "", "el_GR": "",
"en_US": "Aspect Ratio applied to the renderer window.\n\nOnly change this if you're using an aspect ratio mod for your game, otherwise the graphics will be stretched.\n\nLeave on 16:9 if unsure.", "en_US": "Aspect Ratio applied to the renderer window.\n\nOnly change this if you're using an aspect ratio mod for your game, otherwise the graphics will be stretched.\n\nLeave on 16:9 if unsure.",
"es_ES": "Relación de aspecto aplicada a la ventana del renderizador.\n\nSolamente modificar esto si estás utilizando un mod de relación de aspecto para su juego, en cualquier otro caso los gráficos se estirarán.\n\nDejar en 16:9 si no sabe que hacer.", "es_ES": "Relación de aspecto aplicada a la ventana del renderizador.\n\nSolamente modificar esto si estás utilizando un mod de relación de aspecto para su juego, en cualquier otro caso los gráficos se estirarán.\n\nDejar en 16:9 si no sabe que hacer.",
@@ -17075,6 +17100,31 @@
"zh_TW": "啟用檔案系統存取日誌輸出到控制台中。可能的模式為 0 到 3。" "zh_TW": "啟用檔案系統存取日誌輸出到控制台中。可能的模式為 0 到 3。"
} }
}, },
{
"ID": "NetLogTooltip",
"Translations": {
"ar_SA": "",
"de_DE": "",
"el_GR": "",
"en_US": "Prints network log messages in the console.",
"es_ES": "Imprimir registros de red en la consola.",
"fr_FR": "",
"he_IL": "",
"it_IT": "",
"ja_JP": "",
"ko_KR": "",
"no_NO": "",
"pl_PL": "",
"pt_BR": "",
"ru_RU": "",
"sv_SE": "",
"th_TH": "",
"tr_TR": "",
"uk_UA": "",
"zh_CN": "",
"zh_TW": ""
}
},
{ {
"ID": "DeveloperOptionTooltip", "ID": "DeveloperOptionTooltip",
"Translations": { "Translations": {
@@ -18729,7 +18779,7 @@
"ID": "ControllerAppletDescription", "ID": "ControllerAppletDescription",
"Translations": { "Translations": {
"ar_SA": "الإعدادات الحالية غير صالحة. افتح الإعدادات وأعد تكوين المدخلات الخاصة بك.", "ar_SA": "الإعدادات الحالية غير صالحة. افتح الإعدادات وأعد تكوين المدخلات الخاصة بك.",
"de_DE": "Ihre aktuelle Konfiguration ist ungültig. Öffnen Sie die Einstellungen und konfigurieren Sie Ihre Eingaben neu.", "de_DE": "Ihre aktuelle Konfiguration ist ungültig. Öffne die Einstellungen und konfiguriere die Eingaben neu.",
"el_GR": "", "el_GR": "",
"en_US": "Your current configuration is invalid. Open settings and reconfigure your inputs.", "en_US": "Your current configuration is invalid. Open settings and reconfigure your inputs.",
"es_ES": "Tu configuración actual no es válida. Abre la configuración y vuelve a configurar tus entradas", "es_ES": "Tu configuración actual no es válida. Abre la configuración y vuelve a configurar tus entradas",
@@ -21279,7 +21329,7 @@
"ID": "SettingsTabGraphicsBackendTooltip", "ID": "SettingsTabGraphicsBackendTooltip",
"Translations": { "Translations": {
"ar_SA": "حدد الواجهة الخلفية للرسومات التي سيتم استخدامها في المحاكي.\n\nيعد برنامج فولكان أفضل بشكل عام لجميع بطاقات الرسومات الحديثة، طالما أن برامج التشغيل الخاصة بها محدثة. يتميز فولكان أيضا بتجميع مظللات أسرع (أقل تقطيعا) على جميع بائعي وحدات معالجة الرسومات.\n\nقد يحقق أوبن جي أل نتائج أفضل على وحدات معالجة الرسومات إنفيديا القديمة، أو على وحدات معالجة الرسومات إي إم دي القديمة على لينكس، أو على وحدات معالجة الرسومات ذات ذاكرة الوصول العشوائي للفيديوالأقل، على الرغم من أن تعثرات تجميع المظللات ستكون أكبر.\n\nاضبط على فولكان إذا لم تكن متأكدا. اضبط على أوبن جي أل إذا كانت وحدة معالجة الرسومات الخاصة بك لا تدعم فولكان حتى مع أحدث برامج تشغيل الرسومات.", "ar_SA": "حدد الواجهة الخلفية للرسومات التي سيتم استخدامها في المحاكي.\n\nيعد برنامج فولكان أفضل بشكل عام لجميع بطاقات الرسومات الحديثة، طالما أن برامج التشغيل الخاصة بها محدثة. يتميز فولكان أيضا بتجميع مظللات أسرع (أقل تقطيعا) على جميع بائعي وحدات معالجة الرسومات.\n\nقد يحقق أوبن جي أل نتائج أفضل على وحدات معالجة الرسومات إنفيديا القديمة، أو على وحدات معالجة الرسومات إي إم دي القديمة على لينكس، أو على وحدات معالجة الرسومات ذات ذاكرة الوصول العشوائي للفيديوالأقل، على الرغم من أن تعثرات تجميع المظللات ستكون أكبر.\n\nاضبط على فولكان إذا لم تكن متأكدا. اضبط على أوبن جي أل إذا كانت وحدة معالجة الرسومات الخاصة بك لا تدعم فولكان حتى مع أحدث برامج تشغيل الرسومات.",
"de_DE": "Wählen Sie das Grafik-Backend, das im Emulator verwendet werden soll.\n\nVulkan ist insgesamt besser für alle modernen Grafikkarten geeignet, sofern deren Treiber auf dem neuesten Stand sind. Vulkan bietet auch eine schnellere Shader-Kompilierung (weniger Stottern) auf allen GPU-Anbietern.\n\nOpenGL kann auf alten Nvidia-GPUs, alten AMD-GPUs unter Linux oder auf GPUs mit geringerem VRAM bessere Ergebnisse erzielen, obwohl die Shader-Kompilierung stärker stottert.\n\nSetzen Sie auf Vulkan, wenn Sie unsicher sind. Stellen Sie OpenGL ein, wenn Ihr Grafikprozessor selbst mit den neuesten Grafiktreibern Vulkan nicht unterstützt.", "de_DE": "Wähle das Grafik-Backend, das im Emulator verwendet werden soll.\n\nVulkan ist insgesamt besser für alle modernen Grafikkarten geeignet, sofern deren Treiber auf dem neuesten Stand sind. Vulkan bietet auch eine schnellere Shader-Kompilierung (weniger Stottern) auf allen GPU-Anbietern.\n\nOpenGL kann auf alten Nvidia-GPUs, alten AMD-GPUs unter Linux oder auf GPUs mit geringerem VRAM bessere Ergebnisse erzielen, obwohl die Shader-Kompilierung stärker stottert.\n\nSetze auf Vulkan, wenn du unsicher bist. Stelle auf OpenGL ein, wenn der Grafikprozessor selbst mit den neuesten Grafiktreibern Vulkan nicht unterstützt.",
"el_GR": "", "el_GR": "",
"en_US": "Select the graphics backend that will be used in the emulator.\n\nVulkan is overall better for all modern graphics cards, as long as their drivers are up to date. Vulkan also features faster shader compilation (less stuttering) on all GPU vendors.\n\nOpenGL may achieve better results on old Nvidia GPUs, on old AMD GPUs on Linux, or on GPUs with lower VRAM, though shader compilation stutters will be greater.\n\nSet to Vulkan if unsure. Set to OpenGL if your GPU does not support Vulkan even with the latest graphics drivers.", "en_US": "Select the graphics backend that will be used in the emulator.\n\nVulkan is overall better for all modern graphics cards, as long as their drivers are up to date. Vulkan also features faster shader compilation (less stuttering) on all GPU vendors.\n\nOpenGL may achieve better results on old Nvidia GPUs, on old AMD GPUs on Linux, or on GPUs with lower VRAM, though shader compilation stutters will be greater.\n\nSet to Vulkan if unsure. Set to OpenGL if your GPU does not support Vulkan even with the latest graphics drivers.",
"es_ES": "Seleccione el backend gráfico que utilizará el emulador.\n\nVulkan, en general, es mejor para todas las tarjetas gráficas modernas, mientras que sus controladores estén actualizados. Vulkan también cuenta con complicación más rápida de sombreadores (menos tirones) en todos los proveredores de GPU.\n\nOpenGL puede lograr mejores resultados en GPU Nvidia antiguas, GPU AMD antiguas en Linux o en GPUs con menor VRAM, aunque tirones de compilación de sombreadores serán mayores.\n\nSetear en Vulkan si no sabe que hacer. Setear en OpenGL si su GPU no tiene soporte para Vulkan aún con los últimos controladores gráficos.", "es_ES": "Seleccione el backend gráfico que utilizará el emulador.\n\nVulkan, en general, es mejor para todas las tarjetas gráficas modernas, mientras que sus controladores estén actualizados. Vulkan también cuenta con complicación más rápida de sombreadores (menos tirones) en todos los proveredores de GPU.\n\nOpenGL puede lograr mejores resultados en GPU Nvidia antiguas, GPU AMD antiguas en Linux o en GPUs con menor VRAM, aunque tirones de compilación de sombreadores serán mayores.\n\nSetear en Vulkan si no sabe que hacer. Setear en OpenGL si su GPU no tiene soporte para Vulkan aún con los últimos controladores gráficos.",
@@ -21329,7 +21379,7 @@
"ID": "SettingsEnableTextureRecompressionTooltip", "ID": "SettingsEnableTextureRecompressionTooltip",
"Translations": { "Translations": {
"ar_SA": "يضغط تكستر ASTC من أجل تقليل استخدام ذاكرة الوصول العشوائي للفيديو.\n\nتتضمن الألعاب التي تستخدم تنسيق النسيج هذا Astral Chain وBayonetta 3 وFire Emblem Engage وMetroid Prime Remastered وSuper Mario Bros. Wonder وThe Legend of Zelda: Tears of the Kingdom.\n\nمن المحتمل أن تتعطل بطاقات الرسومات التي تحتوي على 4 جيجا بايت من ذاكرة الوصول العشوائي للفيديو أو أقل في مرحلة ما أثناء تشغيل هذه الألعاب.\n\nقم بالتمكين فقط في حالة نفاد ذاكرة الوصول العشوائي للفيديو في الألعاب المذكورة أعلاه. اتركه معطلا إذا لم تكن متأكدا.", "ar_SA": "يضغط تكستر ASTC من أجل تقليل استخدام ذاكرة الوصول العشوائي للفيديو.\n\nتتضمن الألعاب التي تستخدم تنسيق النسيج هذا Astral Chain وBayonetta 3 وFire Emblem Engage وMetroid Prime Remastered وSuper Mario Bros. Wonder وThe Legend of Zelda: Tears of the Kingdom.\n\nمن المحتمل أن تتعطل بطاقات الرسومات التي تحتوي على 4 جيجا بايت من ذاكرة الوصول العشوائي للفيديو أو أقل في مرحلة ما أثناء تشغيل هذه الألعاب.\n\nقم بالتمكين فقط في حالة نفاد ذاكرة الوصول العشوائي للفيديو في الألعاب المذكورة أعلاه. اتركه معطلا إذا لم تكن متأكدا.",
"de_DE": "Komprimiert ASTC-Texturen, um die VRAM-Nutzung zu reduzieren.\n\nZu den Spielen, die dieses Texturformat verwenden, gehören Astral Chain, Bayonetta 3, Fire Emblem Engage, Metroid Prime Remastered, Super Mario Bros. Wonder und The Legend of Zelda: Tears of the Kingdom.\n\nGrafikkarten mit 4GiB VRAM oder weniger werden beim Ausführen dieser Spiele wahrscheinlich irgendwann abstürzen.\n\nAktivieren Sie diese Option nur, wenn Ihnen bei den oben genannten Spielen der VRAM ausgeht. Lassen Sie es aus, wenn Sie unsicher sind.", "de_DE": "Komprimiert ASTC-Texturen, um die VRAM-Nutzung zu reduzieren.\n\nZu den Spielen, die dieses Texturformat verwenden, gehören Astral Chain, Bayonetta 3, Fire Emblem Engage, Metroid Prime Remastered, Super Mario Bros. Wonder und The Legend of Zelda: Tears of the Kingdom.\n\nGrafikkarten mit 4GiB VRAM oder weniger werden beim Ausführen dieser Spiele wahrscheinlich irgendwann abstürzen.\n\nAktiviere diese Option nur, wenn Ihnen bei den oben genannten Spielen der VRAM ausgeht. Lass es aus, wenn du unsicher bist.",
"el_GR": "", "el_GR": "",
"en_US": "Compresses ASTC textures in order to reduce VRAM usage.\n\nGames using this texture format include Astral Chain, Bayonetta 3, Fire Emblem Engage, Metroid Prime Remastered, Super Mario Bros. Wonder and The Legend of Zelda: Tears of the Kingdom.\n\nGraphics cards with 4GiB VRAM or less will likely crash at some point while running these games.\n\nEnable only if you're running out of VRAM on the aforementioned games. Leave OFF if unsure.", "en_US": "Compresses ASTC textures in order to reduce VRAM usage.\n\nGames using this texture format include Astral Chain, Bayonetta 3, Fire Emblem Engage, Metroid Prime Remastered, Super Mario Bros. Wonder and The Legend of Zelda: Tears of the Kingdom.\n\nGraphics cards with 4GiB VRAM or less will likely crash at some point while running these games.\n\nEnable only if you're running out of VRAM on the aforementioned games. Leave OFF if unsure.",
"es_ES": "Comprimir texturas ASTC para reducir uso de VRAM.\n\nJuegos que utilizan este formato de textura incluyen Astral Chain, Bayonetta 3, Fire Emblem Engage, Metroid Prime Remastered, Super Mario Bros. Wonder y The Legend of Zelda: Tears of the Kingdom.\n\nTarjetas gráficas con 4GiB de VRAM o menos probalemente se caeran en algún momento mientras que estén corriendo estos juegos.\n\nActivar solo si está quedan sin VRAM en los juegos antes mencionados. Desactívalo si no sabes qué hacer.", "es_ES": "Comprimir texturas ASTC para reducir uso de VRAM.\n\nJuegos que utilizan este formato de textura incluyen Astral Chain, Bayonetta 3, Fire Emblem Engage, Metroid Prime Remastered, Super Mario Bros. Wonder y The Legend of Zelda: Tears of the Kingdom.\n\nTarjetas gráficas con 4GiB de VRAM o menos probalemente se caeran en algún momento mientras que estén corriendo estos juegos.\n\nActivar solo si está quedan sin VRAM en los juegos antes mencionados. Desactívalo si no sabes qué hacer.",
@@ -21425,6 +21475,31 @@
"zh_TW": "需要重新啟動 Ryujinx" "zh_TW": "需要重新啟動 Ryujinx"
} }
}, },
{
"ID": "SettingsShowConsoleRestartMessage",
"Translations": {
"ar_SA": "",
"de_DE": "",
"el_GR": "",
"en_US": "The console will be available the next time Ryujinx starts.",
"es_ES": "La consola estará disponible la próxima vez que se inicie Ryujinx.",
"fr_FR": "La console sera disponible au prochain démarrage de Ryujinx.",
"he_IL": "",
"it_IT": "",
"ja_JP": "",
"ko_KR": "",
"no_NO": "",
"pl_PL": "",
"pt_BR": "",
"ru_RU": "",
"sv_SE": "",
"th_TH": "",
"tr_TR": "",
"uk_UA": "",
"zh_CN": "",
"zh_TW": ""
}
},
{ {
"ID": "SettingsGpuBackendRestartMessage", "ID": "SettingsGpuBackendRestartMessage",
"Translations": { "Translations": {
@@ -21979,7 +22054,7 @@
"ID": "GraphicsAATooltip", "ID": "GraphicsAATooltip",
"Translations": { "Translations": {
"ar_SA": "يتم تطبيق تنعيم الحواف على عرض اللعبة.\n\nسوف يقوم FXAA بتعتيم معظم الصورة، بينما سيحاول SMAA العثور على حواف خشنة وتنعيمها.\n\nلا ينصح باستخدامه مع فلتر FSR لتكبير.\n\nيمكن تغيير هذا الخيار أثناء تشغيل اللعبة بالنقر فوق \"تطبيق\" أدناه؛ يمكنك ببساطة تحريك نافذة الإعدادات جانبا والتجربة حتى تجد المظهر المفضل للعبة.\n\nاتركه على لا شيء إذا لم تكن متأكدا.", "ar_SA": "يتم تطبيق تنعيم الحواف على عرض اللعبة.\n\nسوف يقوم FXAA بتعتيم معظم الصورة، بينما سيحاول SMAA العثور على حواف خشنة وتنعيمها.\n\nلا ينصح باستخدامه مع فلتر FSR لتكبير.\n\nيمكن تغيير هذا الخيار أثناء تشغيل اللعبة بالنقر فوق \"تطبيق\" أدناه؛ يمكنك ببساطة تحريك نافذة الإعدادات جانبا والتجربة حتى تجد المظهر المفضل للعبة.\n\nاتركه على لا شيء إذا لم تكن متأكدا.",
"de_DE": "Wendet Anti-Aliasing auf das Rendering des Spiels an.\n\nFXAA verwischt den größten Teil des Bildes, während SMAA versucht, gezackte Kanten zu finden und sie zu glätten.\n\nEs wird nicht empfohlen, diese Option in Verbindung mit dem FSR-Skalierungsfilter zu verwenden.\n\nDiese Option kann geändert werden, während ein Spiel läuft, indem Sie unten auf \"Anwenden\" klicken; Sie können das Einstellungsfenster einfach zur Seite schieben und experimentieren, bis Sie Ihr bevorzugtes Aussehen für ein Spiel gefunden haben.\n\nLassen Sie die Option auf NONE, wenn Sie unsicher sind.", "de_DE": "Wendet Anti-Aliasing auf das Rendering des Spiels an.\n\nFXAA verwischt den größten Teil des Bildes, während SMAA versucht, gezackte Kanten zu finden und sie zu glätten.\n\nEs wird nicht empfohlen, diese Option in Verbindung mit dem FSR-Skalierungsfilter zu verwenden.\n\nDiese Option kann geändert werden, während ein Spiel läuft, indem du unten auf \"Anwenden\" klickst; Du können das Einstellungsfenster einfach zur Seite schieben und experimentieren, bis das bevorzugte Aussehen für ein Spiel gefunden wurde.\n\nLass die Option auf NONE, wenn du unsicher bist.",
"el_GR": "", "el_GR": "",
"en_US": "Applies anti-aliasing to the game render.\n\nFXAA will blur most of the image, while SMAA will attempt to find jagged edges and smooth them out.\n\nNot recommended to use in conjunction with the FSR scaling filter.\n\nThis option can be changed while a game is running by clicking \"Apply\" below; you can simply move the settings window aside and experiment until you find your preferred look for a game.\n\nLeave on NONE if unsure.", "en_US": "Applies anti-aliasing to the game render.\n\nFXAA will blur most of the image, while SMAA will attempt to find jagged edges and smooth them out.\n\nNot recommended to use in conjunction with the FSR scaling filter.\n\nThis option can be changed while a game is running by clicking \"Apply\" below; you can simply move the settings window aside and experiment until you find your preferred look for a game.\n\nLeave on NONE if unsure.",
"es_ES": "Aplica antia-aliasing al rendereo del juego.\n\nFXAA desenfocará la mayor parte del la iamgen, mientras que SMAA intentará encontrar bordes irregulares y suavizarlos.\n\nNo se recomienda usar en conjunto con filtro de escala FSR.\n\nEsta opción puede ser modificada mientras que esté corriendo el juego haciendo click en \"Aplicar\" más abajo; simplemente puedes mover la ventana de configuración a un lado y experimentar hasta que encuentres tu estilo preferido para un juego.\n\nDejar en NADA si no está seguro.", "es_ES": "Aplica antia-aliasing al rendereo del juego.\n\nFXAA desenfocará la mayor parte del la iamgen, mientras que SMAA intentará encontrar bordes irregulares y suavizarlos.\n\nNo se recomienda usar en conjunto con filtro de escala FSR.\n\nEsta opción puede ser modificada mientras que esté corriendo el juego haciendo click en \"Aplicar\" más abajo; simplemente puedes mover la ventana de configuración a un lado y experimentar hasta que encuentres tu estilo preferido para un juego.\n\nDejar en NADA si no está seguro.",
@@ -22054,7 +22129,7 @@
"ID": "GraphicsScalingFilterTooltip", "ID": "GraphicsScalingFilterTooltip",
"Translations": { "Translations": {
"ar_SA": "اختر فلتر التكبير الذي سيتم تطبيقه عند استخدام مقياس الدقة.\n\nيعمل Bilinear بشكل جيد مع الألعاب ثلاثية الأبعاد وهو خيار افتراضي آمن.\n\nيوصى باستخدام Nearest لألعاب البكسل الفنية.\n\nFSR 1.0 هو مجرد مرشح توضيحي، ولا ينصح باستخدامه مع FXAA أو SMAA.\n\nيمكن تغيير هذا الخيار أثناء تشغيل اللعبة بالنقر فوق \"تطبيق\" أدناه؛ يمكنك ببساطة تحريك نافذة الإعدادات جانبا والتجربة حتى تجد المظهر المفضل للعبة.\n\nاتركه على Bilinear إذا لم تكن متأكدا.", "ar_SA": "اختر فلتر التكبير الذي سيتم تطبيقه عند استخدام مقياس الدقة.\n\nيعمل Bilinear بشكل جيد مع الألعاب ثلاثية الأبعاد وهو خيار افتراضي آمن.\n\nيوصى باستخدام Nearest لألعاب البكسل الفنية.\n\nFSR 1.0 هو مجرد مرشح توضيحي، ولا ينصح باستخدامه مع FXAA أو SMAA.\n\nيمكن تغيير هذا الخيار أثناء تشغيل اللعبة بالنقر فوق \"تطبيق\" أدناه؛ يمكنك ببساطة تحريك نافذة الإعدادات جانبا والتجربة حتى تجد المظهر المفضل للعبة.\n\nاتركه على Bilinear إذا لم تكن متأكدا.",
"de_DE": "Wählen Sie den Skalierungsfilter, der bei der Auflösungsskalierung angewendet werden soll.\n\nBilinear eignet sich gut für 3D-Spiele und ist eine sichere Standardoption.\n\nNearest wird für Pixel-Art-Spiele empfohlen.\n\nFSR 1.0 ist lediglich ein Schärfungsfilter und wird nicht für die Verwendung mit FXAA oder SMAA empfohlen.\n\nDiese Option kann geändert werden, während ein Spiel läuft, indem Sie unten auf \"Anwenden\" klicken; Sie können das Einstellungsfenster einfach zur Seite schieben und experimentieren, bis Sie Ihr bevorzugtes Aussehen für ein Spiel gefunden haben.\n\nBleiben Sie auf BILINEAR, wenn Sie unsicher sind.", "de_DE": "Wähle den Skalierungsfilter, der bei der Auflösungsskalierung angewendet werden soll.\n\nBilinear eignet sich gut für 3D-Spiele und ist eine sichere Standardoption.\n\nNearest wird für Pixel-Art-Spiele empfohlen.\n\nFSR 1.0 ist lediglich ein Schärfungsfilter und wird nicht für die Verwendung mit FXAA oder SMAA empfohlen.\n\nDiese Option kann geändert werden, während ein Spiel läuft, indem du unten auf \"Anwenden\" klicken; Du kannst das Einstellungsfenster einfach zur Seite schieben und experimentieren, bis das bevorzugtes Aussehen für ein Spiel gefunden wurde.\n\nBleibe auf BILINEAR, wenn du unsicher bist.",
"el_GR": "", "el_GR": "",
"en_US": "Choose the scaling filter that will be applied when using resolution scale.\n\nBilinear works well for 3D games and is a safe default option.\n\nNearest is recommended for pixel art games.\n\nFSR 1.0 is merely a sharpening filter, not recommended for use with FXAA or SMAA.\n\nArea scaling is recommended when downscaling resolutions that are larger than the output window. It can be used to achieve a supersampled anti-aliasing effect when downscaling by more than 2x.\n\nThis option can be changed while a game is running by clicking \"Apply\" below; you can simply move the settings window aside and experiment until you find your preferred look for a game.\n\nLeave on BILINEAR if unsure.", "en_US": "Choose the scaling filter that will be applied when using resolution scale.\n\nBilinear works well for 3D games and is a safe default option.\n\nNearest is recommended for pixel art games.\n\nFSR 1.0 is merely a sharpening filter, not recommended for use with FXAA or SMAA.\n\nArea scaling is recommended when downscaling resolutions that are larger than the output window. It can be used to achieve a supersampled anti-aliasing effect when downscaling by more than 2x.\n\nThis option can be changed while a game is running by clicking \"Apply\" below; you can simply move the settings window aside and experiment until you find your preferred look for a game.\n\nLeave on BILINEAR if unsure.",
"es_ES": "Elija el filtro de escala que se aplicará al utilizar la escala de resolución.\n\nBilinear funciona bien para juegos 3D y es una opción predeterminada segura.\n\nSe recomienda el bilinear para juegos de pixel art.\n\nFSR 1.0 es simplemente un filtro de afilado, no se recomienda su uso con FXAA o SMAA.\n\nEsta opción se puede cambiar mientras se ejecuta un juego haciendo clic en \"Aplicar\" a continuación; simplemente puedes mover la ventana de configuración a un lado y experimentar hasta que encuentres tu estilo preferido para un juego.\n\nDéjelo en BILINEAR si no está seguro.", "es_ES": "Elija el filtro de escala que se aplicará al utilizar la escala de resolución.\n\nBilinear funciona bien para juegos 3D y es una opción predeterminada segura.\n\nSe recomienda el bilinear para juegos de pixel art.\n\nFSR 1.0 es simplemente un filtro de afilado, no se recomienda su uso con FXAA o SMAA.\n\nEsta opción se puede cambiar mientras se ejecuta un juego haciendo clic en \"Aplicar\" a continuación; simplemente puedes mover la ventana de configuración a un lado y experimentar hasta que encuentres tu estilo preferido para un juego.\n\nDéjelo en BILINEAR si no está seguro.",
@@ -22679,7 +22754,7 @@
"ID": "MultiplayerDisableP2P", "ID": "MultiplayerDisableP2P",
"Translations": { "Translations": {
"ar_SA": "", "ar_SA": "",
"de_DE": "", "de_DE": "P2P-Netzwerk-Hosting deaktivieren (kann Latenz erhöhen)",
"el_GR": "", "el_GR": "",
"en_US": "Disable P2P Network Hosting (may increase latency)", "en_US": "Disable P2P Network Hosting (may increase latency)",
"es_ES": "Desactivar el Hosteo de Red P2P (puede aumentar latencia)", "es_ES": "Desactivar el Hosteo de Red P2P (puede aumentar latencia)",
@@ -22704,7 +22779,7 @@
"ID": "MultiplayerDisableP2PTooltip", "ID": "MultiplayerDisableP2PTooltip",
"Translations": { "Translations": {
"ar_SA": "", "ar_SA": "",
"de_DE": "", "de_DE": "Deaktiviert das P2P-Netzwerk-Hosting. Andere Teilnehmer werden über den Master-Server geleitet, anstatt eine direkte Verbindung zu dir herzustellen.",
"el_GR": "", "el_GR": "",
"en_US": "Disable P2P network hosting, peers will proxy through the master server instead of connecting to you directly.", "en_US": "Disable P2P network hosting, peers will proxy through the master server instead of connecting to you directly.",
"es_ES": "Desactivar el hosteo de red P2P, pares se conectarán a través del servidor maestro en lugar de conectarse directamente contigo.", "es_ES": "Desactivar el hosteo de red P2P, pares se conectarán a través del servidor maestro en lugar de conectarse directamente contigo.",
@@ -22729,7 +22804,7 @@
"ID": "LdnPassphrase", "ID": "LdnPassphrase",
"Translations": { "Translations": {
"ar_SA": "", "ar_SA": "",
"de_DE": "", "de_DE": "Netzwerk-Passphrase",
"el_GR": "", "el_GR": "",
"en_US": "Network Passphrase:", "en_US": "Network Passphrase:",
"es_ES": "Frase de Contraseña de la Red:", "es_ES": "Frase de Contraseña de la Red:",
@@ -22754,7 +22829,7 @@
"ID": "LdnPassphraseTooltip", "ID": "LdnPassphraseTooltip",
"Translations": { "Translations": {
"ar_SA": "", "ar_SA": "",
"de_DE": "", "de_DE": "Du wirst nur gehostete Spiele sehen können, die dieselbe Passphrase wie du verwenden.",
"el_GR": "", "el_GR": "",
"en_US": "You will only be able to see hosted games with the same passphrase as you.", "en_US": "You will only be able to see hosted games with the same passphrase as you.",
"es_ES": "Solo podrás ver los juegos hosteados con la misma frase de contraseña que tú.", "es_ES": "Solo podrás ver los juegos hosteados con la misma frase de contraseña que tú.",
@@ -22779,7 +22854,7 @@
"ID": "LdnPassphraseInputTooltip", "ID": "LdnPassphraseInputTooltip",
"Translations": { "Translations": {
"ar_SA": "", "ar_SA": "",
"de_DE": "", "de_DE": "Gib eine Passphrase im Format Ryujinx-<8 Hex-Zeichen> ein. Du wirst nur gehostete Spiele sehen können, die dieselbe Passphrase wie du verwenden.",
"el_GR": "", "el_GR": "",
"en_US": "Enter a passphrase in the format Ryujinx-<8 hex chars>. You will only be able to see hosted games with the same passphrase as you.", "en_US": "Enter a passphrase in the format Ryujinx-<8 hex chars>. You will only be able to see hosted games with the same passphrase as you.",
"es_ES": "Ingresar una frase de contraseña en formato Ryujinx-<8 caracteres hexadecimales>. Solamente podrás ver juegos hosteados con la misma frase de contraseña que tú.", "es_ES": "Ingresar una frase de contraseña en formato Ryujinx-<8 caracteres hexadecimales>. Solamente podrás ver juegos hosteados con la misma frase de contraseña que tú.",
@@ -22804,7 +22879,7 @@
"ID": "LdnPassphraseInputPublic", "ID": "LdnPassphraseInputPublic",
"Translations": { "Translations": {
"ar_SA": "", "ar_SA": "",
"de_DE": "", "de_DE": "(öffentlich)",
"el_GR": "", "el_GR": "",
"en_US": "(public)", "en_US": "(public)",
"es_ES": "(público)", "es_ES": "(público)",
@@ -22829,7 +22904,7 @@
"ID": "GenLdnPass", "ID": "GenLdnPass",
"Translations": { "Translations": {
"ar_SA": "", "ar_SA": "",
"de_DE": "", "de_DE": "Zufällig generieren",
"el_GR": "", "el_GR": "",
"en_US": "Generate Random", "en_US": "Generate Random",
"es_ES": "Generar Aleatorio", "es_ES": "Generar Aleatorio",
@@ -22854,7 +22929,7 @@
"ID": "GenLdnPassTooltip", "ID": "GenLdnPassTooltip",
"Translations": { "Translations": {
"ar_SA": "", "ar_SA": "",
"de_DE": "", "de_DE": "Generiert eine neue Passphrase, welche mit anderen Spielern geteilt werden kann.",
"el_GR": "", "el_GR": "",
"en_US": "Generates a new passphrase, which can be shared with other players.", "en_US": "Generates a new passphrase, which can be shared with other players.",
"es_ES": "Genera una nueva frase de contraseña, que puede ser compartida con otros jugadores.", "es_ES": "Genera una nueva frase de contraseña, que puede ser compartida con otros jugadores.",
@@ -22879,7 +22954,7 @@
"ID": "ClearLdnPass", "ID": "ClearLdnPass",
"Translations": { "Translations": {
"ar_SA": "", "ar_SA": "",
"de_DE": "", "de_DE": "Löschen",
"el_GR": "", "el_GR": "",
"en_US": "Clear", "en_US": "Clear",
"es_ES": "Borrar", "es_ES": "Borrar",
@@ -22904,7 +22979,7 @@
"ID": "ClearLdnPassTooltip", "ID": "ClearLdnPassTooltip",
"Translations": { "Translations": {
"ar_SA": "", "ar_SA": "",
"de_DE": "", "de_DE": "Löscht die jetzige Passphrase.",
"el_GR": "", "el_GR": "",
"en_US": "Clears the current passphrase, returning to the public network.", "en_US": "Clears the current passphrase, returning to the public network.",
"es_ES": "Borra la frase de contraseña actual, regresando a la red pública.", "es_ES": "Borra la frase de contraseña actual, regresando a la red pública.",
@@ -22929,7 +23004,7 @@
"ID": "InvalidLdnPassphrase", "ID": "InvalidLdnPassphrase",
"Translations": { "Translations": {
"ar_SA": "", "ar_SA": "",
"de_DE": "", "de_DE": "Ungültige Passphrase! Muss im Format \"Ryujinx-<8 Hex Zeichen>\" sein",
"el_GR": "", "el_GR": "",
"en_US": "Invalid Passphrase! Must be in the format \"Ryujinx-<8 hex chars>\"", "en_US": "Invalid Passphrase! Must be in the format \"Ryujinx-<8 hex chars>\"",
"es_ES": "Frase de Contraseña Inválida! Debe ser en formato \"Ryujinx-<8 caracteres hexadecimales>\"", "es_ES": "Frase de Contraseña Inválida! Debe ser en formato \"Ryujinx-<8 caracteres hexadecimales>\"",
@@ -22979,7 +23054,7 @@
"ID": "SettingsTabSystemEnableCustomVSyncInterval", "ID": "SettingsTabSystemEnableCustomVSyncInterval",
"Translations": { "Translations": {
"ar_SA": "", "ar_SA": "",
"de_DE": "", "de_DE": "Benutzerdefinierte Bildwiederholrate aktivieren (Experimentell)",
"el_GR": "", "el_GR": "",
"en_US": "Enable Custom Refresh Rate (Experimental)", "en_US": "Enable Custom Refresh Rate (Experimental)",
"es_ES": "Activar Frecuencia de Actualización Personalizada (Experimental)", "es_ES": "Activar Frecuencia de Actualización Personalizada (Experimental)",
@@ -23029,7 +23104,7 @@
"ID": "SettingsTabSystemVSyncModeUnbounded", "ID": "SettingsTabSystemVSyncModeUnbounded",
"Translations": { "Translations": {
"ar_SA": "", "ar_SA": "",
"de_DE": "", "de_DE": "Unbegrenzt",
"el_GR": "", "el_GR": "",
"en_US": "Unbounded", "en_US": "Unbounded",
"es_ES": "Sin Límite", "es_ES": "Sin Límite",
@@ -23129,7 +23204,7 @@
"ID": "SettingsTabSystemEnableCustomVSyncIntervalTooltip", "ID": "SettingsTabSystemEnableCustomVSyncIntervalTooltip",
"Translations": { "Translations": {
"ar_SA": "", "ar_SA": "",
"de_DE": "Ermöglicht es dem Benutzer, eine emulierte Bildwiederholfrequenz festzulegen. In einigen Titeln kann dies die Geschwindigkeit der Spiel-Logik erhöhen oder verringern. In anderen Titeln kann dies dazu führen, dass die FPS auf ein Vielfaches der Bildwiederholfrequenz begrenzt werden oder zu unvorhersehbarem Verhalten führen. Dies ist eine experimentelle Funktion, ohne Garantien dafür, wie sich das Gameplay auswirkt. \n\nLassen Sie diese Option deaktiviert, wenn Sie sich nicht sicher sind.", "de_DE": "Ermöglicht es dem Benutzer, eine emulierte Bildwiederholfrequenz festzulegen. In einigen Titeln kann dies die Geschwindigkeit der Spiel-Logik erhöhen oder verringern. In anderen Titeln kann dies dazu führen, dass die FPS auf ein Vielfaches der Bildwiederholfrequenz begrenzt werden oder zu unvorhersehbarem Verhalten führen. Dies ist eine experimentelle Funktion, ohne Garantien dafür, wie sich das Gameplay auswirkt. \n\nLass diese Option deaktiviert, wenn du dir nicht sicher bist.",
"el_GR": "", "el_GR": "",
"en_US": "Allows the user to specify an emulated refresh rate. In some titles, this may speed up or slow down the rate of gameplay logic. In other titles, it may allow for capping FPS at some multiple of the refresh rate, or lead to unpredictable behavior. This is an experimental feature, with no guarantees for how gameplay will be affected. \n\nLeave OFF if unsure.", "en_US": "Allows the user to specify an emulated refresh rate. In some titles, this may speed up or slow down the rate of gameplay logic. In other titles, it may allow for capping FPS at some multiple of the refresh rate, or lead to unpredictable behavior. This is an experimental feature, with no guarantees for how gameplay will be affected. \n\nLeave OFF if unsure.",
"es_ES": "Permite al usuario especificar una frecuencia de actualización emulada. En algunos títulos, esto puede acelerar o ralentizar la lógica del juego. En otros títulos, puede permitir limitar los FPS a algún múltiplo de la frecuencia de actualización, o provocar un comportamiento impredecible. Esta es una función experimental, sin garantías sobre cómo se verá afectada la jugabilidad.\n\nDéjalo DESACTIVADO si no estás seguro.", "es_ES": "Permite al usuario especificar una frecuencia de actualización emulada. En algunos títulos, esto puede acelerar o ralentizar la lógica del juego. En otros títulos, puede permitir limitar los FPS a algún múltiplo de la frecuencia de actualización, o provocar un comportamiento impredecible. Esta es una función experimental, sin garantías sobre cómo se verá afectada la jugabilidad.\n\nDéjalo DESACTIVADO si no estás seguro.",
@@ -24154,7 +24229,7 @@
"ID": "SettingsTabDebugEnableGDBStub", "ID": "SettingsTabDebugEnableGDBStub",
"Translations": { "Translations": {
"ar_SA": "", "ar_SA": "",
"de_DE": "", "de_DE": "GDB Stub aktivieren",
"el_GR": "", "el_GR": "",
"en_US": "Enable GDB Stub", "en_US": "Enable GDB Stub",
"es_ES": "Activar Stub GDB", "es_ES": "Activar Stub GDB",
@@ -24179,7 +24254,7 @@
"ID": "SettingsTabDebugGDBStubToggleTooltip", "ID": "SettingsTabDebugGDBStubToggleTooltip",
"Translations": { "Translations": {
"ar_SA": "", "ar_SA": "",
"de_DE": "", "de_DE": "Aktiviert den GDB Stub, welcher debugging an der Anwendung ermöglicht. Nur für Entwicklerzwecke!",
"el_GR": "", "el_GR": "",
"en_US": "Enables the GDB stub which makes it possible to debug the running application. For development use only!", "en_US": "Enables the GDB stub which makes it possible to debug the running application. For development use only!",
"es_ES": "Activa el stub GDB, lo que permite depurar la aplicación en ejecución. ¡Solo para uso de desarrollo!", "es_ES": "Activa el stub GDB, lo que permite depurar la aplicación en ejecución. ¡Solo para uso de desarrollo!",
@@ -24229,7 +24304,7 @@
"ID": "SettingsTabDebugSuspendOnStart", "ID": "SettingsTabDebugSuspendOnStart",
"Translations": { "Translations": {
"ar_SA": "", "ar_SA": "",
"de_DE": "", "de_DE": "Anwendung beim Start anhalten.",
"el_GR": "", "el_GR": "",
"en_US": "Suspend Application on Start", "en_US": "Suspend Application on Start",
"es_ES": "Suspender la Aplicación al Iniciar", "es_ES": "Suspender la Aplicación al Iniciar",
@@ -24254,7 +24329,7 @@
"ID": "SettingsTabDebugSuspendOnStartTooltip", "ID": "SettingsTabDebugSuspendOnStartTooltip",
"Translations": { "Translations": {
"ar_SA": "", "ar_SA": "",
"de_DE": "", "de_DE": "Hält die Anwendung vor der Ausführung des ersten Befehls an, um Debugging vom frühestmöglichen Zeitpunkt an zu ermöglichen.",
"el_GR": "", "el_GR": "",
"en_US": "Suspends the application before executing the first instruction, allowing for debugging from the earliest point.", "en_US": "Suspends the application before executing the first instruction, allowing for debugging from the earliest point.",
"es_ES": "Suspende la aplicación antes de ejecutar la primera instrucción, permitiendo depurar desde el punto más temprano.", "es_ES": "Suspende la aplicación antes de ejecutar la primera instrucción, permitiendo depurar desde el punto más temprano.",
@@ -24279,7 +24354,7 @@
"ID": "LdnGameListOpen", "ID": "LdnGameListOpen",
"Translations": { "Translations": {
"ar_SA": "", "ar_SA": "",
"de_DE": "", "de_DE": "LDN Spiele-Liste",
"el_GR": "", "el_GR": "",
"en_US": "LDN Game List", "en_US": "LDN Game List",
"es_ES": "Lista de Juegos LDN", "es_ES": "Lista de Juegos LDN",
@@ -24304,7 +24379,7 @@
"ID": "LdnGameListTitle", "ID": "LdnGameListTitle",
"Translations": { "Translations": {
"ar_SA": "", "ar_SA": "",
"de_DE": "", "de_DE": "LDN-Spiele-Browser - {0} Spiele",
"el_GR": "", "el_GR": "",
"en_US": "LDN Game Browser - {0} games", "en_US": "LDN Game Browser - {0} games",
"es_ES": "Navegador de Juegos LDN - {0} juegos", "es_ES": "Navegador de Juegos LDN - {0} juegos",
@@ -24329,7 +24404,7 @@
"ID": "LdnGameListSearchBoxWatermark", "ID": "LdnGameListSearchBoxWatermark",
"Translations": { "Translations": {
"ar_SA": "", "ar_SA": "",
"de_DE": "", "de_DE": "Suche {0} LDN Spiele...",
"el_GR": "", "el_GR": "",
"en_US": "Search {0} LDN games...", "en_US": "Search {0} LDN games...",
"es_ES": "Buscar {0} Juegos LDN...", "es_ES": "Buscar {0} Juegos LDN...",
@@ -24354,7 +24429,7 @@
"ID": "LdnGameListInfoButtonToolTip", "ID": "LdnGameListInfoButtonToolTip",
"Translations": { "Translations": {
"ar_SA": "", "ar_SA": "",
"de_DE": "", "de_DE": "Was ist LDN?",
"el_GR": "", "el_GR": "",
"en_US": "What is LDN?", "en_US": "What is LDN?",
"es_ES": "¿Qué es LDN?", "es_ES": "¿Qué es LDN?",
@@ -24379,7 +24454,7 @@
"ID": "LdnGameListRefreshToolTip", "ID": "LdnGameListRefreshToolTip",
"Translations": { "Translations": {
"ar_SA": "", "ar_SA": "",
"de_DE": "", "de_DE": "Verfügbare Spiele vom Server unter {0} aktualisieren.",
"el_GR": "", "el_GR": "",
"en_US": "Refresh available games from the server at {0}.", "en_US": "Refresh available games from the server at {0}.",
"es_ES": "Actualizar los juegos disponibles del servidor a las {0}.", "es_ES": "Actualizar los juegos disponibles del servidor a las {0}.",
@@ -24404,7 +24479,7 @@
"ID": "LdnGameListPlayerSortDisable", "ID": "LdnGameListPlayerSortDisable",
"Translations": { "Translations": {
"ar_SA": "", "ar_SA": "",
"de_DE": "", "de_DE": "Spieleranzahl - Deaktivieren",
"el_GR": "", "el_GR": "",
"en_US": "Player Count - Disable", "en_US": "Player Count - Disable",
"es_ES": "Contador de jugadores - Desactivar", "es_ES": "Contador de jugadores - Desactivar",
@@ -24429,7 +24504,7 @@
"ID": "LdnGameListPlayerSortAscending", "ID": "LdnGameListPlayerSortAscending",
"Translations": { "Translations": {
"ar_SA": "", "ar_SA": "",
"de_DE": "", "de_DE": "Spieleranzahl - Aufsteigend",
"el_GR": "", "el_GR": "",
"en_US": "Player Count - Ascending", "en_US": "Player Count - Ascending",
"es_ES": "Contador de jugadores - Ascendente", "es_ES": "Contador de jugadores - Ascendente",
@@ -24454,7 +24529,7 @@
"ID": "LdnGameListPlayerSortDescending", "ID": "LdnGameListPlayerSortDescending",
"Translations": { "Translations": {
"ar_SA": "", "ar_SA": "",
"de_DE": "", "de_DE": "Spieleranzahl - Absteigend",
"el_GR": "", "el_GR": "",
"en_US": "Player Count - Descending", "en_US": "Player Count - Descending",
"es_ES": "Contador de jugadores - Descendente", "es_ES": "Contador de jugadores - Descendente",
@@ -24479,7 +24554,7 @@
"ID": "LdnGameListFiltersHeading", "ID": "LdnGameListFiltersHeading",
"Translations": { "Translations": {
"ar_SA": "", "ar_SA": "",
"de_DE": "", "de_DE": "Filter",
"el_GR": "", "el_GR": "",
"en_US": "Filters", "en_US": "Filters",
"es_ES": "Filtros", "es_ES": "Filtros",
@@ -24504,7 +24579,7 @@
"ID": "LdnGameListFiltersOnlyShowPublicGames", "ID": "LdnGameListFiltersOnlyShowPublicGames",
"Translations": { "Translations": {
"ar_SA": "", "ar_SA": "",
"de_DE": "", "de_DE": "Nur öffentliche Spiele anzeigen",
"el_GR": "", "el_GR": "",
"en_US": "Only show public games", "en_US": "Only show public games",
"es_ES": "Solo mostrar juegos públicos", "es_ES": "Solo mostrar juegos públicos",
@@ -24529,7 +24604,7 @@
"ID": "LdnGameListFiltersOnlyShowJoinableGames", "ID": "LdnGameListFiltersOnlyShowJoinableGames",
"Translations": { "Translations": {
"ar_SA": "", "ar_SA": "",
"de_DE": "", "de_DE": "Nur beitretbare Spiele anzeigen",
"el_GR": "", "el_GR": "",
"en_US": "Only show joinable games", "en_US": "Only show joinable games",
"es_ES": "Solo mostrar juegos a los que se puede unir", "es_ES": "Solo mostrar juegos a los que se puede unir",
@@ -24604,7 +24679,7 @@
"ID": "LdnGameListConnectionTypeMasterServerProxyToolTip", "ID": "LdnGameListConnectionTypeMasterServerProxyToolTip",
"Translations": { "Translations": {
"ar_SA": "", "ar_SA": "",
"de_DE": "", "de_DE": "Verbindet durch RyuLDN-Server (langsamer)",
"el_GR": "", "el_GR": "",
"en_US": "Connects through the RyuLDN server (slower).", "en_US": "Connects through the RyuLDN server (slower).",
"es_ES": "Conexión a través del servidor RyuLDN (más lento).", "es_ES": "Conexión a través del servidor RyuLDN (más lento).",
@@ -24629,7 +24704,7 @@
"ID": "LdnGameListConnectionTypeP2PToolTip", "ID": "LdnGameListConnectionTypeP2PToolTip",
"Translations": { "Translations": {
"ar_SA": "", "ar_SA": "",
"de_DE": "", "de_DE": "Verbindet über Peer-to-Peer durch UPnP (schneller)",
"el_GR": "", "el_GR": "",
"en_US": "Connects via Peer-to-Peer via UPnP (faster).", "en_US": "Connects via Peer-to-Peer via UPnP (faster).",
"es_ES": "Conexión Peer-to-Peer a través de UPnP (más rápido).", "es_ES": "Conexión Peer-to-Peer a través de UPnP (más rápido).",
@@ -24654,7 +24729,7 @@
"ID": "LdnGameListCreatedAt", "ID": "LdnGameListCreatedAt",
"Translations": { "Translations": {
"ar_SA": "", "ar_SA": "",
"de_DE": "", "de_DE": "Erstellt: {0}",
"el_GR": "", "el_GR": "",
"en_US": "Created: {0}", "en_US": "Created: {0}",
"es_ES": "Creado: {0}", "es_ES": "Creado: {0}",
@@ -24679,7 +24754,7 @@
"ID": "LdnGameListPlayersAndPlayerCount", "ID": "LdnGameListPlayersAndPlayerCount",
"Translations": { "Translations": {
"ar_SA": "", "ar_SA": "",
"de_DE": "", "de_DE": "Spieler ({0} von {1}:",
"el_GR": "", "el_GR": "",
"en_US": "Players ({0} out of {1}):", "en_US": "Players ({0} out of {1}):",
"es_ES": "Jugadores ({0} de {1}):", "es_ES": "Jugadores ({0} de {1}):",
@@ -24704,7 +24779,7 @@
"ID": "LdnGameListJoinable", "ID": "LdnGameListJoinable",
"Translations": { "Translations": {
"ar_SA": "", "ar_SA": "",
"de_DE": "", "de_DE": "Beitretbar",
"el_GR": "", "el_GR": "",
"en_US": "Joinable", "en_US": "Joinable",
"es_ES": "Unible", "es_ES": "Unible",
@@ -24729,7 +24804,7 @@
"ID": "LdnGameListJoinableToolTip", "ID": "LdnGameListJoinableToolTip",
"Translations": { "Translations": {
"ar_SA": "", "ar_SA": "",
"de_DE": "", "de_DE": "Spiel ist beitrittbar, wenn es öffentlich ist, oder du die Passphrase kennst.",
"el_GR": "", "el_GR": "",
"en_US": "Game is joinable if it is public or if you know the passphrase.", "en_US": "Game is joinable if it is public or if you know the passphrase.",
"es_ES": "El juego es unible si es público o si conoces la frase de contraseña.", "es_ES": "El juego es unible si es público o si conoces la frase de contraseña.",
@@ -24754,7 +24829,7 @@
"ID": "LdnGameListNotJoinable", "ID": "LdnGameListNotJoinable",
"Translations": { "Translations": {
"ar_SA": "", "ar_SA": "",
"de_DE": "", "de_DE": "Nicht beitretbar",
"el_GR": "", "el_GR": "",
"en_US": "Not Joinable", "en_US": "Not Joinable",
"es_ES": "No se puede unir", "es_ES": "No se puede unir",
@@ -24779,7 +24854,7 @@
"ID": "LdnGameListNotJoinableToolTip", "ID": "LdnGameListNotJoinableToolTip",
"Translations": { "Translations": {
"ar_SA": "", "ar_SA": "",
"de_DE": "", "de_DE": "Spiel läuft bereits.",
"el_GR": "", "el_GR": "",
"en_US": "Game is currently in progress.", "en_US": "Game is currently in progress.",
"es_ES": "El juego está en curso.", "es_ES": "El juego está en curso.",
@@ -24804,7 +24879,7 @@
"ID": "LdnGameListPublic", "ID": "LdnGameListPublic",
"Translations": { "Translations": {
"ar_SA": "", "ar_SA": "",
"de_DE": "", "de_DE": "Öffentlich",
"el_GR": "", "el_GR": "",
"en_US": "Public", "en_US": "Public",
"es_ES": "Público", "es_ES": "Público",
@@ -24829,7 +24904,7 @@
"ID": "LdnGameListPublicToolTip", "ID": "LdnGameListPublicToolTip",
"Translations": { "Translations": {
"ar_SA": "", "ar_SA": "",
"de_DE": "", "de_DE": "Jeder kann das Spiel beitreten.",
"el_GR": "", "el_GR": "",
"en_US": "Anyone can join this game.", "en_US": "Anyone can join this game.",
"es_ES": "Cualquiera puede unirse a este juego.", "es_ES": "Cualquiera puede unirse a este juego.",
@@ -24854,7 +24929,7 @@
"ID": "LdnGameListPrivate", "ID": "LdnGameListPrivate",
"Translations": { "Translations": {
"ar_SA": "", "ar_SA": "",
"de_DE": "", "de_DE": "Privat",
"el_GR": "", "el_GR": "",
"en_US": "Private", "en_US": "Private",
"es_ES": "Privado", "es_ES": "Privado",
@@ -24879,7 +24954,7 @@
"ID": "LdnGameListPrivateToolTip", "ID": "LdnGameListPrivateToolTip",
"Translations": { "Translations": {
"ar_SA": "", "ar_SA": "",
"de_DE": "", "de_DE": "Du kannst dieses Spiel nur beitreten, wenn du die selbe LDN-Passphrase eingestellt hast.",
"el_GR": "", "el_GR": "",
"en_US": "You can only join this game if you also have the same LDN Passphrase in your settings.", "en_US": "You can only join this game if you also have the same LDN Passphrase in your settings.",
"es_ES": "Solo puedes unirte a este juego si también tienes la misma frase de contraseña LDN en tus ajustes.", "es_ES": "Solo puedes unirte a este juego si también tienes la misma frase de contraseña LDN en tus ajustes.",

View File

@@ -12,11 +12,12 @@ namespace Ryujinx.Common.Helper
private static partial nint GetConsoleWindow(); private static partial nint GetConsoleWindow();
[SupportedOSPlatform("windows")] [SupportedOSPlatform("windows")]
[LibraryImport("user32")] [LibraryImport("kernel32", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)] [return: MarshalAs(UnmanagedType.Bool)]
private static partial bool ShowWindow(nint hWnd, int nCmdShow); private static partial bool FreeConsole();
public static bool SetConsoleWindowStateSupported => OperatingSystem.IsWindows(); public static bool SetConsoleWindowStateSupported => OperatingSystem.IsWindows();
public static bool HasConsoleWindow => OperatingSystem.IsWindows() && GetConsoleWindow() != nint.Zero;
public static void SetConsoleWindowState(bool show) public static void SetConsoleWindowState(bool show)
{ {
@@ -33,18 +34,31 @@ namespace Ryujinx.Common.Helper
[SupportedOSPlatform("windows")] [SupportedOSPlatform("windows")]
private static void SetConsoleWindowStateWindows(bool show) private static void SetConsoleWindowStateWindows(bool show)
{ {
const int SW_HIDE = 0; if (show)
const int SW_SHOW = 5;
nint hWnd = GetConsoleWindow();
if (hWnd == nint.Zero)
{ {
Logger.Warning?.Print(LogClass.Application, "Attempted to show/hide console window but console window does not exist"); if (GetConsoleWindow() != nint.Zero)
{
Logger.SetConsoleTargetEnabled(true);
}
return; return;
} }
ShowWindow(hWnd, show ? SW_SHOW : SW_HIDE); Logger.SetConsoleTargetEnabled(false);
DetachConsole();
}
[SupportedOSPlatform("windows")]
private static void DetachConsole()
{
if (GetConsoleWindow() == nint.Zero)
{
return;
}
if (!FreeConsole())
{
Logger.Warning?.Print(LogClass.Application, "Attempted to detach console window but the operation failed");
}
} }
} }
} }

View File

@@ -51,6 +51,7 @@ namespace Ryujinx.Common.Logging
ServiceNgct, ServiceNgct,
ServiceNifm, ServiceNifm,
ServiceNim, ServiceNim,
ServiceNotification,
ServiceNs, ServiceNs,
ServiceNsd, ServiceNsd,
ServiceNtc, ServiceNtc,

View File

@@ -12,6 +12,7 @@ namespace Ryujinx.Common.Logging
Error, Error,
Guest, Guest,
AccessLog, AccessLog,
NetLog,
Notice, Notice,
Trace, Trace,
} }

View File

@@ -119,6 +119,7 @@ namespace Ryujinx.Common.Logging
public static Log? Error { get; private set; } public static Log? Error { get; private set; }
public static Log? Guest { get; private set; } public static Log? Guest { get; private set; }
public static Log? AccessLog { get; private set; } public static Log? AccessLog { get; private set; }
public static Log? NetLog { get; private set; }
public static Log? Stub { get; private set; } public static Log? Stub { get; private set; }
public static Log? Trace { get; private set; } public static Log? Trace { get; private set; }
public static Log Notice { get; } // Always enabled public static Log Notice { get; } // Always enabled
@@ -136,11 +137,7 @@ namespace Ryujinx.Common.Logging
_time = Stopwatch.StartNew(); _time = Stopwatch.StartNew();
// Logger should log to console by default SetConsoleTargetEnabled(true);
AddTarget(new AsyncLogTargetWrapper(
new ConsoleLogTarget("console"),
1000,
AsyncLogTargetOverflowAction.Discard));
Notice = new Log(LogLevel.Notice); Notice = new Log(LogLevel.Notice);
@@ -173,6 +170,21 @@ namespace Ryujinx.Common.Logging
Updated += target.Log; Updated += target.Log;
} }
public static void SetConsoleTargetEnabled(bool enabled)
{
if (enabled)
{
AddTarget(new AsyncLogTargetWrapper(
new ConsoleLogTarget("console"),
1000,
AsyncLogTargetOverflowAction.Discard));
}
else
{
RemoveTarget("console");
}
}
public static void RemoveTarget(string target) public static void RemoveTarget(string target)
{ {
ILogTarget logTarget = GetTarget(target); ILogTarget logTarget = GetTarget(target);
@@ -236,6 +248,7 @@ namespace Ryujinx.Common.Logging
case LogLevel.Error : Error = enabled ? new Log(LogLevel.Error) : null; break; case LogLevel.Error : Error = enabled ? new Log(LogLevel.Error) : null; break;
case LogLevel.Guest : Guest = enabled ? new Log(LogLevel.Guest) : null; break; case LogLevel.Guest : Guest = enabled ? new Log(LogLevel.Guest) : null; break;
case LogLevel.AccessLog : AccessLog = enabled ? new Log(LogLevel.AccessLog) : null; break; case LogLevel.AccessLog : AccessLog = enabled ? new Log(LogLevel.AccessLog) : null; break;
case LogLevel.NetLog : NetLog = enabled ? new Log(LogLevel.NetLog) : null; break;
case LogLevel.Stub : Stub = enabled ? new Log(LogLevel.Stub) : null; break; case LogLevel.Stub : Stub = enabled ? new Log(LogLevel.Stub) : null; break;
case LogLevel.Trace : Trace = enabled ? new Log(LogLevel.Trace) : null; break; case LogLevel.Trace : Trace = enabled ? new Log(LogLevel.Trace) : null; break;
case LogLevel.Notice : break; case LogLevel.Notice : break;

View File

@@ -17,7 +17,8 @@ namespace Ryujinx.Cpu.LightningJit.Cache
private static readonly int _pageMask = _pageSize - 1; private static readonly int _pageMask = _pageSize - 1;
private const int CodeAlignment = 4; // Bytes. private const int CodeAlignment = 4; // Bytes.
private const int CacheSize = 256 * 1024 * 1024; // TODO: JIT Cache size should be application dependent, not global.
private const int CacheSize = 1024 * (1024 * 1024); // Megabytes * Size of Megabytes (since its in bytes).
private static JitCacheInvalidation _jitCacheInvalidator; private static JitCacheInvalidation _jitCacheInvalidator;
@@ -33,6 +34,14 @@ namespace Ryujinx.Cpu.LightningJit.Cache
[SupportedOSPlatform("windows")] [SupportedOSPlatform("windows")]
[LibraryImport("kernel32.dll", SetLastError = true)] [LibraryImport("kernel32.dll", SetLastError = true)]
public static partial nint FlushInstructionCache(nint hProcess, nint lpAddress, nuint dwSize); public static partial nint FlushInstructionCache(nint hProcess, nint lpAddress, nuint dwSize);
[SupportedOSPlatform("macos")]
[LibraryImport("libSystem.dylib", EntryPoint = "sys_icache_invalidate")]
internal static partial void SysICacheInvalidate(nint start, nuint len);
[SupportedOSPlatform("linux")]
[LibraryImport("libgcc_s.so.1", EntryPoint = "__clear_cache")]
internal static partial void ClearCache(nint begin, nint end);
public static void Initialize(IJitMemoryAllocator allocator) public static void Initialize(IJitMemoryAllocator allocator)
{ {

View File

@@ -551,7 +551,7 @@ namespace Ryujinx.Graphics.OpenGL.Image
level, level,
x, x,
width, width,
format.PixelFormat, (InternalFormat) format.PixelFormat,
mipSize, mipSize,
data); data);
} }
@@ -579,7 +579,7 @@ namespace Ryujinx.Graphics.OpenGL.Image
layer, layer,
width, width,
1, 1,
format.PixelFormat, (InternalFormat) format.PixelFormat,
mipSize, mipSize,
data); data);
} }
@@ -609,7 +609,7 @@ namespace Ryujinx.Graphics.OpenGL.Image
y, y,
width, width,
height, height,
format.PixelFormat, (InternalFormat) format.PixelFormat,
mipSize, mipSize,
data); data);
} }
@@ -643,7 +643,7 @@ namespace Ryujinx.Graphics.OpenGL.Image
width, width,
height, height,
1, 1,
format.PixelFormat, (InternalFormat) format.PixelFormat,
mipSize, mipSize,
data); data);
} }
@@ -675,7 +675,7 @@ namespace Ryujinx.Graphics.OpenGL.Image
y, y,
width, width,
height, height,
format.PixelFormat, (InternalFormat) format.PixelFormat,
mipSize, mipSize,
data); data);
} }
@@ -744,7 +744,7 @@ namespace Ryujinx.Graphics.OpenGL.Image
level, level,
0, 0,
width, width,
format.PixelFormat, (InternalFormat) format.PixelFormat,
mipSize, mipSize,
data); data);
} }
@@ -773,7 +773,7 @@ namespace Ryujinx.Graphics.OpenGL.Image
0, 0,
width, width,
height, height,
format.PixelFormat, (InternalFormat) format.PixelFormat,
mipSize, mipSize,
data); data);
} }
@@ -807,7 +807,7 @@ namespace Ryujinx.Graphics.OpenGL.Image
width, width,
height, height,
depth, depth,
format.PixelFormat, (InternalFormat) format.PixelFormat,
mipSize, mipSize,
data); data);
} }
@@ -843,7 +843,7 @@ namespace Ryujinx.Graphics.OpenGL.Image
0, 0,
width, width,
height, height,
format.PixelFormat, (InternalFormat) format.PixelFormat,
mipSize / 6, mipSize / 6,
data + faceOffset); data + faceOffset);
} }

View File

@@ -27,7 +27,7 @@ namespace Ryujinx.Graphics.OpenGL
public void Map(BufferHandle handle, int size) public void Map(BufferHandle handle, int size)
{ {
GL.BindBuffer(BufferTarget.CopyWriteBuffer, handle); GL.BindBuffer(BufferTarget.CopyWriteBuffer, handle);
nint ptr = GL.MapBufferRange(BufferTarget.CopyWriteBuffer, nint.Zero, size, BufferAccessMask.MapReadBit | BufferAccessMask.MapPersistentBit); nint ptr = GL.MapBufferRange(BufferTarget.CopyWriteBuffer, nint.Zero, size, MapBufferAccessMask.MapReadBit | MapBufferAccessMask.MapPersistentBit);
_maps[handle] = ptr; _maps[handle] = ptr;
} }
@@ -75,7 +75,7 @@ namespace Ryujinx.Graphics.OpenGL
GL.BindBuffer(BufferTarget.CopyWriteBuffer, _copyBufferHandle); GL.BindBuffer(BufferTarget.CopyWriteBuffer, _copyBufferHandle);
GL.BufferStorage(BufferTarget.CopyWriteBuffer, requiredSize, nint.Zero, BufferStorageFlags.MapReadBit | BufferStorageFlags.MapPersistentBit); GL.BufferStorage(BufferTarget.CopyWriteBuffer, requiredSize, nint.Zero, BufferStorageFlags.MapReadBit | BufferStorageFlags.MapPersistentBit);
_bufferMap = GL.MapBufferRange(BufferTarget.CopyWriteBuffer, nint.Zero, requiredSize, BufferAccessMask.MapReadBit | BufferAccessMask.MapPersistentBit); _bufferMap = GL.MapBufferRange(BufferTarget.CopyWriteBuffer, nint.Zero, requiredSize, MapBufferAccessMask.MapReadBit | MapBufferAccessMask.MapPersistentBit);
} }
} }

View File

@@ -924,8 +924,8 @@ namespace Ryujinx.Graphics.OpenGL
GL.Disable(EnableCap.CullFace); GL.Disable(EnableCap.CullFace);
return; return;
} }
GL.CullFace(face.Convert()); GL.CullFace((TriangleFace) face.Convert());
GL.Enable(EnableCap.CullFace); GL.Enable(EnableCap.CullFace);
} }
@@ -1085,12 +1085,12 @@ namespace Ryujinx.Graphics.OpenGL
{ {
if (frontMode == backMode) if (frontMode == backMode)
{ {
GL.PolygonMode(MaterialFace.FrontAndBack, frontMode.Convert()); GL.PolygonMode((TriangleFace) MaterialFace.FrontAndBack, frontMode.Convert());
} }
else else
{ {
GL.PolygonMode(MaterialFace.Front, frontMode.Convert()); GL.PolygonMode((TriangleFace) MaterialFace.Front, frontMode.Convert());
GL.PolygonMode(MaterialFace.Back, backMode.Convert()); GL.PolygonMode((TriangleFace) MaterialFace.Back, backMode.Convert());
} }
} }

View File

@@ -59,7 +59,7 @@ namespace Ryujinx.Graphics.OpenGL
GL.CompileShader(shaderHandle); GL.CompileShader(shaderHandle);
break; break;
case TargetLanguage.Spirv: case TargetLanguage.Spirv:
GL.ShaderBinary(1, ref shaderHandle, (BinaryFormat)All.ShaderBinaryFormatSpirVArb, shader.BinaryCode, shader.BinaryCode.Length); GL.ShaderBinary(1, ref shaderHandle, ShaderBinaryFormat.ShaderBinaryFormatSpirV, shader.BinaryCode, shader.BinaryCode.Length);
GL.SpecializeShader(shaderHandle, "main", 0, (int[])null, (int[])null); GL.SpecializeShader(shaderHandle, "main", 0, (int[])null, (int[])null);
break; break;
} }

View File

@@ -32,7 +32,7 @@ namespace Ryujinx.Graphics.OpenGL.Queries
GL.BufferStorage(BufferTarget.QueryBuffer, sizeof(long), (nint)(&defaultValue), BufferStorageFlags.MapReadBit | BufferStorageFlags.MapWriteBit | BufferStorageFlags.MapPersistentBit); GL.BufferStorage(BufferTarget.QueryBuffer, sizeof(long), (nint)(&defaultValue), BufferStorageFlags.MapReadBit | BufferStorageFlags.MapWriteBit | BufferStorageFlags.MapPersistentBit);
} }
_bufferMap = GL.MapBufferRange(BufferTarget.QueryBuffer, nint.Zero, sizeof(long), BufferAccessMask.MapReadBit | BufferAccessMask.MapWriteBit | BufferAccessMask.MapPersistentBit); _bufferMap = GL.MapBufferRange(BufferTarget.QueryBuffer, nint.Zero, sizeof(long), MapBufferAccessMask.MapReadBit | MapBufferAccessMask.MapWriteBit | MapBufferAccessMask.MapPersistentBit);
} }
public void Reset() public void Reset()

View File

@@ -1,3 +1,4 @@
using Ryujinx.Common.Logging;
using System; using System;
using System.Diagnostics; using System.Diagnostics;
using System.Threading; using System.Threading;
@@ -114,7 +115,7 @@ namespace Ryujinx.Graphics.Vulkan
cbs.AddDependant(this); cbs.AddDependant(this);
// We need to add a dependency on the command buffer to all objects this object // We need to add a dependency on the command buffer to all objects this object
// references aswell. // references as well.
if (_referencedObjs != null) if (_referencedObjs != null)
{ {
for (int i = 0; i < _referencedObjs.Length; i++) for (int i = 0; i < _referencedObjs.Length; i++)
@@ -175,7 +176,7 @@ namespace Ryujinx.Graphics.Vulkan
} }
} }
} }
Debug.Assert(_referenceCount >= 0); Debug.Assert(_referenceCount >= 0);
} }

View File

@@ -44,6 +44,22 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Lib
return ResultCode.Success; return ResultCode.Success;
} }
[CommandCmif(1)]
// PushOutData(object<nn::am::service::IStorage>)
public ResultCode PushOutData(ServiceCtx context)
{
IStorage appletData = GetObject<IStorage>(context, 0);
if (appletData == null || appletData.Data.Length == 0) // is this necessary?
{
return ResultCode.NullObject;
}
_appletStandalone.InputData.Enqueue(appletData.Data);
return ResultCode.Success;
}
[CommandCmif(11)] [CommandCmif(11)]
// GetLibraryAppletInfo() -> nn::am::service::LibraryAppletInfo // GetLibraryAppletInfo() -> nn::am::service::LibraryAppletInfo

View File

@@ -1,7 +1,9 @@
using Ryujinx.Common.Logging;
using Ryujinx.Common.Memory; using Ryujinx.Common.Memory;
using Ryujinx.HLE.HOS.Services.Caps.Types; using Ryujinx.HLE.HOS.Services.Caps.Types;
using SkiaSharp; using SkiaSharp;
using System; using System;
using System.Globalization;
using System.IO; using System.IO;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
@@ -9,16 +11,20 @@ using System.Security.Cryptography;
namespace Ryujinx.HLE.HOS.Services.Caps namespace Ryujinx.HLE.HOS.Services.Caps
{ {
class CaptureManager internal class CaptureManager
{ {
private readonly string _sdCardPath; public CaptureManager(Switch device)
{
_ = device;
}
private readonly string _sdCardPath = FileSystem.VirtualFileSystem.GetSdCardPath();
private uint _shimLibraryVersion; private uint _shimLibraryVersion;
public CaptureManager(Switch device) private const int ScreenshotWidth = 1280;
{ private const int ScreenshotHeight = 720;
_sdCardPath = FileSystem.VirtualFileSystem.GetSdCardPath(); private const int ScreenshotBytesPerPixel = 4;
} private const int ScreenshotDataSize = ScreenshotWidth * ScreenshotHeight * ScreenshotBytesPerPixel; // 0x384000
public ResultCode SetShimLibraryVersion(ServiceCtx context) public ResultCode SetShimLibraryVersion(ServiceCtx context)
{ {
@@ -53,84 +59,94 @@ namespace Ryujinx.HLE.HOS.Services.Caps
return resultCode; return resultCode;
} }
public ResultCode SaveScreenShot(byte[] screenshotData, ulong appletResourceUserId, ulong titleId, out ApplicationAlbumEntry applicationAlbumEntry) public ResultCode SaveScreenShot(
byte[] screenshotData,
ulong appletResourceUserId,
ulong titleId,
out ApplicationAlbumEntry applicationAlbumEntry)
{ {
Logger.Stub?.PrintStub(LogClass.ServiceCaps, new
{
appletResourceUserId,
titleId,
screenshotDataLength = screenshotData?.Length ?? 0,
});
applicationAlbumEntry = default; applicationAlbumEntry = default;
if (screenshotData.Length == 0) if (screenshotData == null || screenshotData.Length == 0)
{ {
return ResultCode.NullInputBuffer; return ResultCode.NullInputBuffer;
} }
/* if (screenshotData.Length < ScreenshotDataSize)
// NOTE: On our current implementation, appletResourceUserId starts at 0, disable it for now. {
if (appletResourceUserId == 0) Logger.Warning?.PrintMsg(
LogClass.ServiceCaps,
$"Invalid screenshot buffer size 0x{screenshotData.Length:X}; expected at least 0x{ScreenshotDataSize:X}.");
return ResultCode.NullInputBuffer;
}
DateTime currentDateTime = DateTime.Now;
applicationAlbumEntry = new ApplicationAlbumEntry()
{
Size = (ulong)Unsafe.SizeOf<ApplicationAlbumEntry>(),
TitleId = titleId,
AlbumFileDateTime = new AlbumFileDateTime()
{
Year = (ushort)currentDateTime.Year,
Month = (byte)currentDateTime.Month,
Day = (byte)currentDateTime.Day,
Hour = (byte)currentDateTime.Hour,
Minute = (byte)currentDateTime.Minute,
Second = (byte)currentDateTime.Second,
UniqueId = 0,
},
AlbumStorage = AlbumStorage.Sd,
ContentType = ContentType.Screenshot,
Padding = new Array5<byte>(),
Unknown0x1f = 1,
};
// NOTE: The hex hash is a HMAC-SHA256 (first 32 bytes) using a hardcoded secret key over the titleId, we can simulate it by hashing the titleId instead.
string hash = Convert.ToHexString(SHA256.HashData(BitConverter.GetBytes(titleId)))[..0x20];
string folderPath = Path.Combine(
_sdCardPath,
"Nintendo",
"Album",
currentDateTime.Year.ToString("0000", CultureInfo.InvariantCulture),
currentDateTime.Month.ToString("00", CultureInfo.InvariantCulture),
currentDateTime.Day.ToString("00", CultureInfo.InvariantCulture));
string filePath = GenerateFilePath(folderPath, applicationAlbumEntry, currentDateTime, hash);
_ = Directory.CreateDirectory(folderPath);
while (File.Exists(filePath))
{
applicationAlbumEntry.AlbumFileDateTime.UniqueId++;
filePath = GenerateFilePath(folderPath, applicationAlbumEntry, currentDateTime, hash);
}
using SKBitmap bitmap = new(new SKImageInfo(ScreenshotWidth, ScreenshotHeight, SKColorType.Rgba8888));
IntPtr pixels = bitmap.GetPixels();
if (pixels == IntPtr.Zero)
{ {
return ResultCode.InvalidArgument; return ResultCode.InvalidArgument;
} }
*/
/* Marshal.Copy(screenshotData, 0, pixels, ScreenshotDataSize);
// Doesn't occur in our case.
if (applicationAlbumEntry == null)
{
return ResultCode.NullOutputBuffer;
}
*/
if (screenshotData.Length >= 0x384000) using SKData data = bitmap.Encode(SKEncodedImageFormat.Jpeg, 80);
{ using FileStream file = File.OpenWrite(filePath);
DateTime currentDateTime = DateTime.Now; data.SaveTo(file);
applicationAlbumEntry = new ApplicationAlbumEntry() return ResultCode.Success;
{
Size = (ulong)Unsafe.SizeOf<ApplicationAlbumEntry>(),
TitleId = titleId,
AlbumFileDateTime = new AlbumFileDateTime()
{
Year = (ushort)currentDateTime.Year,
Month = (byte)currentDateTime.Month,
Day = (byte)currentDateTime.Day,
Hour = (byte)currentDateTime.Hour,
Minute = (byte)currentDateTime.Minute,
Second = (byte)currentDateTime.Second,
UniqueId = 0,
},
AlbumStorage = AlbumStorage.Sd,
ContentType = ContentType.Screenshot,
Padding = new Array5<byte>(),
Unknown0x1f = 1,
};
// NOTE: The hex hash is a HMAC-SHA256 (first 32 bytes) using a hardcoded secret key over the titleId, we can simulate it by hashing the titleId instead.
string hash = Convert.ToHexString(SHA256.HashData(BitConverter.GetBytes(titleId)))[..0x20];
string folderPath = Path.Combine(_sdCardPath, "Nintendo", "Album", currentDateTime.Year.ToString("00"), currentDateTime.Month.ToString("00"), currentDateTime.Day.ToString("00"));
string filePath = GenerateFilePath(folderPath, applicationAlbumEntry, currentDateTime, hash);
// TODO: Handle that using the FS service implementation and return the right error code instead of throwing exceptions.
Directory.CreateDirectory(folderPath);
while (File.Exists(filePath))
{
applicationAlbumEntry.AlbumFileDateTime.UniqueId++;
filePath = GenerateFilePath(folderPath, applicationAlbumEntry, currentDateTime, hash);
}
// NOTE: The saved JPEG file doesn't have the limitation in the extra EXIF data.
using SKBitmap bitmap = new(new SKImageInfo(1280, 720, SKColorType.Rgba8888, SKAlphaType.Premul));
int dataLen = screenshotData.Length > bitmap.ByteCount ? bitmap.ByteCount : screenshotData.Length;
Marshal.Copy(screenshotData, 0, bitmap.GetPixels(), dataLen);
using SKData data = bitmap.Encode(SKEncodedImageFormat.Jpeg, 80);
using FileStream file = File.OpenWrite(filePath);
data.SaveTo(file);
return ResultCode.Success;
}
return ResultCode.NullInputBuffer;
} }
private string GenerateFilePath(string folderPath, ApplicationAlbumEntry applicationAlbumEntry, DateTime currentDateTime, string hash) private string GenerateFilePath(string folderPath, ApplicationAlbumEntry applicationAlbumEntry, DateTime currentDateTime, string hash)

View File

@@ -1,13 +1,19 @@
using Ryujinx.Common; using Ryujinx.Common;
using Ryujinx.Common.Logging;
using Ryujinx.HLE.HOS.Services.Caps.Types; using Ryujinx.HLE.HOS.Services.Caps.Types;
namespace Ryujinx.HLE.HOS.Services.Caps namespace Ryujinx.HLE.HOS.Services.Caps
{ {
[Service("caps:su")] // 6.0.0+ [Service("caps:su")] // 6.0.0+
class IScreenShotApplicationService : IpcService internal class IScreenShotApplicationService : IpcService
{ {
public IScreenShotApplicationService(ServiceCtx context) { } private const ulong ScreenshotDataSize = 0x384000;
private const ulong ApplicationDataSize = 0x404;
public IScreenShotApplicationService(ServiceCtx context)
{
_ = context;
}
[CommandCmif(32)] // 7.0.0+ [CommandCmif(32)] // 7.0.0+
// SetShimLibraryVersion(pid, u64, nn::applet::AppletResourceUserId) // SetShimLibraryVersion(pid, u64, nn::applet::AppletResourceUserId)
public ResultCode SetShimLibraryVersion(ServiceCtx context) public ResultCode SetShimLibraryVersion(ServiceCtx context)
@@ -33,6 +39,15 @@ namespace Ryujinx.HLE.HOS.Services.Caps
ulong screenshotDataPosition = context.Request.SendBuff[0].Position; ulong screenshotDataPosition = context.Request.SendBuff[0].Position;
ulong screenshotDataSize = context.Request.SendBuff[0].Size; ulong screenshotDataSize = context.Request.SendBuff[0].Size;
if (screenshotDataSize < ScreenshotDataSize)
{
Logger.Warning?.PrintMsg(
LogClass.ServiceCaps,
$"Invalid screenshot buffer size 0x{screenshotDataSize:X}; expected at least 0x{ScreenshotDataSize:X}.");
return ResultCode.NullInputBuffer;
}
byte[] screenshotData = context.Memory.GetSpan(screenshotDataPosition, (int)screenshotDataSize, true).ToArray(); byte[] screenshotData = context.Memory.GetSpan(screenshotDataPosition, (int)screenshotDataSize, true).ToArray();
ResultCode resultCode = context.Device.System.CaptureManager.SaveScreenShot(screenshotData, appletResourceUserId, context.Device.Processes.ActiveApplication.ProgramId, out ApplicationAlbumEntry applicationAlbumEntry); ResultCode resultCode = context.Device.System.CaptureManager.SaveScreenShot(screenshotData, appletResourceUserId, context.Device.Processes.ActiveApplication.ProgramId, out ApplicationAlbumEntry applicationAlbumEntry);
@@ -60,6 +75,24 @@ namespace Ryujinx.HLE.HOS.Services.Caps
ulong screenshotDataPosition = context.Request.SendBuff[1].Position; ulong screenshotDataPosition = context.Request.SendBuff[1].Position;
ulong screenshotDataSize = context.Request.SendBuff[1].Size; ulong screenshotDataSize = context.Request.SendBuff[1].Size;
if (applicationDataSize != ApplicationDataSize)
{
Logger.Warning?.PrintMsg(
LogClass.ServiceCaps,
$"Invalid ApplicationData size 0x{applicationDataSize:X}; expected 0x{ApplicationDataSize:X}.");
return ResultCode.InvalidArgument;
}
if (screenshotDataSize < ScreenshotDataSize)
{
Logger.Warning?.PrintMsg(
LogClass.ServiceCaps,
$"Invalid screenshot buffer size 0x{screenshotDataSize:X}; expected at least 0x{ScreenshotDataSize:X}.");
return ResultCode.NullInputBuffer;
}
// TODO: Parse the application data: At 0x00 it's UserData (Size of 0x400), at 0x404 it's a uint UserDataSize (Always empty for now). // TODO: Parse the application data: At 0x00 it's UserData (Size of 0x400), at 0x404 it's a uint UserDataSize (Always empty for now).
_ = context.Memory.GetSpan(applicationDataPosition, (int)applicationDataSize).ToArray(); _ = context.Memory.GetSpan(applicationDataPosition, (int)applicationDataSize).ToArray();
@@ -88,6 +121,23 @@ namespace Ryujinx.HLE.HOS.Services.Caps
ulong screenshotDataPosition = context.Request.SendBuff[1].Position; ulong screenshotDataPosition = context.Request.SendBuff[1].Position;
ulong screenshotDataSize = context.Request.SendBuff[1].Size; ulong screenshotDataSize = context.Request.SendBuff[1].Size;
if (userIdListSize != 0x88)
{
Logger.Warning?.PrintMsg(
LogClass.ServiceCaps,
$"Invalid UserIdList size 0x{userIdListSize:X}; expected 0x88.");
return ResultCode.InvalidArgument;
}
if (screenshotDataSize < ScreenshotDataSize)
{
Logger.Warning?.PrintMsg(
LogClass.ServiceCaps,
$"Invalid screenshot buffer size 0x{screenshotDataSize:X}; expected at least 0x{ScreenshotDataSize:X}.");
return ResultCode.NullInputBuffer;
}
// TODO: Parse the UserIdList. // TODO: Parse the UserIdList.
_ = context.Memory.GetSpan(userIdListPosition, (int)userIdListSize).ToArray(); _ = context.Memory.GetSpan(userIdListPosition, (int)userIdListSize).ToArray();

View File

@@ -5,7 +5,6 @@ using Ryujinx.Common.Logging;
using Ryujinx.Common.Memory; using Ryujinx.Common.Memory;
using Ryujinx.Common.Utilities; using Ryujinx.Common.Utilities;
using Ryujinx.Cpu; using Ryujinx.Cpu;
using Ryujinx.HLE.Exceptions;
using Ryujinx.HLE.HOS.Ipc; using Ryujinx.HLE.HOS.Ipc;
using Ryujinx.HLE.HOS.Kernel.Threading; using Ryujinx.HLE.HOS.Kernel.Threading;
using Ryujinx.HLE.HOS.Services.Ldn.Types; using Ryujinx.HLE.HOS.Services.Ldn.Types;
@@ -15,7 +14,6 @@ using Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator.Types;
using Ryujinx.Horizon.Common; using Ryujinx.Horizon.Common;
using Ryujinx.Memory; using Ryujinx.Memory;
using System; using System;
using System.ComponentModel;
using System.IO; using System.IO;
using System.Net; using System.Net;
using System.Net.NetworkInformation; using System.Net.NetworkInformation;
@@ -68,10 +66,11 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator
{ {
if (localCommunicationId == localCommunicationIdChecked) if (localCommunicationId == localCommunicationIdChecked)
{ {
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn,"CheckLocalCommumicationIdPermission: Checked!");
return true; return true;
} }
} }
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn,"CheckLocalCommumicationIdPermission: Check failed!");
return false; return false;
} }
@@ -82,7 +81,7 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator
if (_nifmResultCode != ResultCode.Success) if (_nifmResultCode != ResultCode.Success)
{ {
context.ResponseData.Write((int)NetworkState.Error); context.ResponseData.Write((int)NetworkState.Error);
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn,$"GetState: _nifmResultCode = {_nifmResultCode.ToString()}");
return ResultCode.Success; return ResultCode.Success;
} }
@@ -114,12 +113,14 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator
if (_nifmResultCode != ResultCode.Success) if (_nifmResultCode != ResultCode.Success)
{ {
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn,$"GetNetworkInfo: _nifmResultCode = {_nifmResultCode.ToString()}");
return _nifmResultCode; return _nifmResultCode;
} }
ResultCode resultCode = GetNetworkInfoImpl(out NetworkInfo networkInfo); ResultCode resultCode = GetNetworkInfoImpl(out NetworkInfo networkInfo);
if (resultCode != ResultCode.Success) if (resultCode != ResultCode.Success)
{ {
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn,$"GetState: resultCode = {resultCode.ToString()}");
return resultCode; return resultCode;
} }
@@ -135,18 +136,22 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator
if (_state == NetworkState.StationConnected) if (_state == NetworkState.StationConnected)
{ {
networkInfo = _station.NetworkInfo; networkInfo = _station.NetworkInfo;
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn,"GetNetworkInfoImpl: _station");
} }
else if (_state == NetworkState.AccessPointCreated) else if (_state == NetworkState.AccessPointCreated)
{ {
networkInfo = _accessPoint.NetworkInfo; networkInfo = _accessPoint.NetworkInfo;
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn,"GetNetworkInfoImpl: _accessPoint");
} }
else else
{ {
networkInfo = new NetworkInfo(); networkInfo = new NetworkInfo();
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn,"GetNetworkInfoImpl: Invalid state!");
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn,$"GetNetworkInfoImpl: networkInfo = {networkInfo}");
return ResultCode.InvalidState; return ResultCode.InvalidState;
} }
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn,$"GetNetworkInfoImpl: networkInfo = {networkInfo}");
return ResultCode.Success; return ResultCode.Success;
} }
@@ -198,7 +203,7 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator
} }
else else
{ {
Logger.Info?.Print(LogClass.ServiceLdn, $"Console's LDN IP is \"{unicastAddress.Address}\"."); Logger.NetLog?.Print(LogClass.ServiceLdn, $"Console's LDN IP is \"{unicastAddress.Address}\".");
context.ResponseData.Write(NetworkHelpers.ConvertIpv4Address(unicastAddress.Address)); context.ResponseData.Write(NetworkHelpers.ConvertIpv4Address(unicastAddress.Address));
context.ResponseData.Write(NetworkHelpers.ConvertIpv4Address(unicastAddress.IPv4Mask)); context.ResponseData.Write(NetworkHelpers.ConvertIpv4Address(unicastAddress.IPv4Mask));
@@ -206,7 +211,7 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator
} }
else else
{ {
Logger.Info?.Print(LogClass.ServiceLdn, $"LDN obtained proxy IP."); Logger.NetLog?.Print(LogClass.ServiceLdn, $"LDN obtained proxy IP.");
context.ResponseData.Write(config.ProxyIp); context.ResponseData.Write(config.ProxyIp);
context.ResponseData.Write(config.ProxySubnetMask); context.ResponseData.Write(config.ProxySubnetMask);
@@ -227,7 +232,7 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator
// NOTE: Returns ResultCode.InvalidArgument if _disconnectReason is null, doesn't occur in our case. // NOTE: Returns ResultCode.InvalidArgument if _disconnectReason is null, doesn't occur in our case.
context.ResponseData.Write((short)_disconnectReason); context.ResponseData.Write((short)_disconnectReason);
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn, $"GetDisconnectReason: {_disconnectReason}");
return ResultCode.Success; return ResultCode.Success;
} }
@@ -247,12 +252,14 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator
{ {
if (_nifmResultCode != ResultCode.Success) if (_nifmResultCode != ResultCode.Success)
{ {
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn, $"GetSecurityParameter: _nifmResultCode = {_nifmResultCode}");
return _nifmResultCode; return _nifmResultCode;
} }
ResultCode resultCode = GetNetworkInfoImpl(out NetworkInfo networkInfo); ResultCode resultCode = GetNetworkInfoImpl(out NetworkInfo networkInfo);
if (resultCode != ResultCode.Success) if (resultCode != ResultCode.Success)
{ {
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn, $"GetSecurityParameter: resultCode = {resultCode}");
return resultCode; return resultCode;
} }
@@ -263,7 +270,8 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator
}; };
context.ResponseData.WriteStruct(securityParameter); context.ResponseData.WriteStruct(securityParameter);
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn, $"GetSecurityParameter: securityParameter = {securityParameter}");
return ResultCode.Success; return ResultCode.Success;
} }
@@ -273,12 +281,14 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator
{ {
if (_nifmResultCode != ResultCode.Success) if (_nifmResultCode != ResultCode.Success)
{ {
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn, $"GetNetworkConfig: _nifmResultCode = {_nifmResultCode}");
return _nifmResultCode; return _nifmResultCode;
} }
ResultCode resultCode = GetNetworkInfoImpl(out NetworkInfo networkInfo); ResultCode resultCode = GetNetworkInfoImpl(out NetworkInfo networkInfo);
if (resultCode != ResultCode.Success) if (resultCode != ResultCode.Success)
{ {
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn, $"GetNetworkConfig: resultCode = {resultCode}");
return resultCode; return resultCode;
} }
@@ -292,6 +302,8 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator
}; };
context.ResponseData.WriteStruct(networkConfig); context.ResponseData.WriteStruct(networkConfig);
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn, $"GetNetworkConfig: networkConfig = {networkConfig}");
return ResultCode.Success; return ResultCode.Success;
} }
@@ -322,12 +334,14 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator
if (_nifmResultCode != ResultCode.Success) if (_nifmResultCode != ResultCode.Success)
{ {
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn, $"GetNetworkInfoLatestUpdate: _nifmResultCode = {_nifmResultCode}");
return _nifmResultCode; return _nifmResultCode;
} }
ResultCode resultCode = GetNetworkInfoImpl(out NetworkInfo networkInfo); ResultCode resultCode = GetNetworkInfoImpl(out NetworkInfo networkInfo);
if (resultCode != ResultCode.Success) if (resultCode != ResultCode.Success)
{ {
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn, $"GetNetworkInfoLatestUpdate: resultCode = {resultCode}");
return resultCode; return resultCode;
} }
@@ -378,6 +392,7 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator
if (_nifmResultCode != ResultCode.Success) if (_nifmResultCode != ResultCode.Success)
{ {
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn, $"ScanImpl: _nifmResultCode = {_nifmResultCode}");
return _nifmResultCode; return _nifmResultCode;
} }
@@ -400,6 +415,7 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator
{ {
if (scanFilter.Ssid.Length <= 31) if (scanFilter.Ssid.Length <= 31)
{ {
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn, $"ScanImpl: resultCode = {resultCode}");
return resultCode; return resultCode;
} }
} }
@@ -408,11 +424,13 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator
{ {
if (scanFilterFlag > ScanFilterFlag.All) if (scanFilterFlag > ScanFilterFlag.All)
{ {
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn, $"ScanImpl: resultCode = {resultCode}");
return resultCode; return resultCode;
} }
if (_state - 3 >= NetworkState.AccessPoint) if (_state - 3 >= NetworkState.AccessPoint)
{ {
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn, "ScanImpl: Invalid state!");
resultCode = ResultCode.InvalidState; resultCode = ResultCode.InvalidState;
} }
else else
@@ -437,7 +455,8 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator
} }
} }
} }
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn, $"ScanImpl: resultCode = {resultCode}");
return resultCode; return resultCode;
} }
@@ -462,6 +481,7 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator
} }
} }
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn, $"ScanInternal: availableGames = {availableGames}");
return ResultCode.Success; return ResultCode.Success;
} }
@@ -502,7 +522,8 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator
throw new ArgumentException($"{GetType().FullName}: Protocol value is not 1 or 3!! Protocol value: {protocolValue}"); throw new ArgumentException($"{GetType().FullName}: Protocol value is not 1 or 3!! Protocol value: {protocolValue}");
} }
Logger.Stub?.PrintStub(LogClass.ServiceLdn, $"Protocol value: {protocolValue}"); Logger.NetLog?.PrintMsg(LogClass.ServiceLdn, $"SetProtocol: protocolValue = {protocolValue}");
Logger.Stub?.PrintStub(LogClass.ServiceLdn, new { protocolValue});
return ResultCode.Success; return ResultCode.Success;
} }
@@ -512,11 +533,13 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator
{ {
if (_nifmResultCode != ResultCode.Success) if (_nifmResultCode != ResultCode.Success)
{ {
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn, $"OpenAccessPoint: _nifmResultCode = {_nifmResultCode}");
return _nifmResultCode; return _nifmResultCode;
} }
if (_state != NetworkState.Initialized) if (_state != NetworkState.Initialized)
{ {
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn, "OpenAccessPoint: Invalid state!");
return ResultCode.InvalidState; return ResultCode.InvalidState;
} }
@@ -538,6 +561,7 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator
{ {
if (_nifmResultCode != ResultCode.Success) if (_nifmResultCode != ResultCode.Success)
{ {
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn, $"CloseAccessPoint: _nifmResultCode = {_nifmResultCode}");
return _nifmResultCode; return _nifmResultCode;
} }
@@ -547,6 +571,7 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator
} }
else else
{ {
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn, "CloseAccessPoint: Invalid state!");
return ResultCode.InvalidState; return ResultCode.InvalidState;
} }
@@ -596,11 +621,13 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator
bool isLocalCommunicationIdValid = CheckLocalCommunicationIdPermission(context, (ulong)networkConfig.IntentId.LocalCommunicationId); bool isLocalCommunicationIdValid = CheckLocalCommunicationIdPermission(context, (ulong)networkConfig.IntentId.LocalCommunicationId);
if (!isLocalCommunicationIdValid && NetworkClient.NeedsRealId) if (!isLocalCommunicationIdValid && NetworkClient.NeedsRealId)
{ {
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn, "CreateNetworkImpl: Invalid object!");
return ResultCode.InvalidObject; return ResultCode.InvalidObject;
} }
if (_nifmResultCode != ResultCode.Success) if (_nifmResultCode != ResultCode.Success)
{ {
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn, $"CreateNetworkImpl: _nifmResultCode = {_nifmResultCode}");
return _nifmResultCode; return _nifmResultCode;
} }
@@ -629,16 +656,22 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator
AddressList addressList = MemoryMarshal.Cast<byte, AddressList>(addressListBytes)[0]; AddressList addressList = MemoryMarshal.Cast<byte, AddressList>(addressListBytes)[0];
_accessPoint.CreateNetworkPrivate(securityConfig, securityParameter, userConfig, networkConfig, addressList); _accessPoint.CreateNetworkPrivate(securityConfig, securityParameter, userConfig, networkConfig, addressList);
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn, $"CreateNetworkImpl: Created private network! " +
$"| securityConfig = {securityConfig} | securityParameter = {securityParameter} " +
$"| userConfig = {userConfig} | networkConfig = {networkConfig} | addressList = {addressList}");
} }
else else
{ {
_accessPoint.CreateNetwork(securityConfig, userConfig, networkConfig); _accessPoint.CreateNetwork(securityConfig, userConfig, networkConfig);
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn, $"CreateNetworkImpl: Created network! " +
$"| securityConfig = {securityConfig} | userConfig = {userConfig} | networkConfig = {networkConfig}");
} }
return ResultCode.Success; return ResultCode.Success;
} }
else else
{ {
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn, "CreateNetworkImpl: Invalid state!");
return ResultCode.InvalidState; return ResultCode.InvalidState;
} }
} }
@@ -660,6 +693,7 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator
{ {
if (_nifmResultCode != ResultCode.Success) if (_nifmResultCode != ResultCode.Success)
{ {
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn, $"DestroyNetworkImpl: _nifmResultCode = {_nifmResultCode}");
return _nifmResultCode; return _nifmResultCode;
} }
@@ -676,9 +710,11 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator
CloseAccessPoint(); CloseAccessPoint();
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn, "DestroyNetworkImpl: Invalid state!");
return ResultCode.InvalidState; return ResultCode.InvalidState;
} }
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn, "DestroyNetworkImpl: Invalid argument!");
return ResultCode.InvalidArgument; return ResultCode.InvalidArgument;
} }
@@ -695,14 +731,17 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator
{ {
if (_nifmResultCode != ResultCode.Success) if (_nifmResultCode != ResultCode.Success)
{ {
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn, $"RejectImpl: _nifmResultCode = {_nifmResultCode}");
return _nifmResultCode; return _nifmResultCode;
} }
if (_state != NetworkState.AccessPointCreated) if (_state != NetworkState.AccessPointCreated)
{ {
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn, "RejectImpl: Invalid state!");
return ResultCode.InvalidState; // Must be network host to reject nodes. return ResultCode.InvalidState; // Must be network host to reject nodes.
} }
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn, $"RejectImpl: disconnectReason = {disconnectReason} | nodeId = {nodeId}");
return NetworkClient.Reject(disconnectReason, nodeId); return NetworkClient.Reject(disconnectReason, nodeId);
} }
@@ -714,11 +753,13 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator
if (_nifmResultCode != ResultCode.Success) if (_nifmResultCode != ResultCode.Success)
{ {
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn, $"SetAdvertiseData: _nifmResultCode = {_nifmResultCode}");
return _nifmResultCode; return _nifmResultCode;
} }
if (bufferSize is 0 or > LdnConst.AdvertiseDataSizeMax) if (bufferSize is 0 or > LdnConst.AdvertiseDataSizeMax)
{ {
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn, "SetAdvertiseData: Invalid argument!");
return ResultCode.InvalidArgument; return ResultCode.InvalidArgument;
} }
@@ -727,11 +768,12 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator
byte[] advertiseData = new byte[bufferSize]; byte[] advertiseData = new byte[bufferSize];
context.Memory.Read(bufferPosition, advertiseData); context.Memory.Read(bufferPosition, advertiseData);
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn, $"SetAdvertiseData: advertiseData = {advertiseData}");
return _accessPoint.SetAdvertiseData(advertiseData); return _accessPoint.SetAdvertiseData(advertiseData);
} }
else else
{ {
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn, "SetAdvertiseData: Invalid state!");
return ResultCode.InvalidState; return ResultCode.InvalidState;
} }
} }
@@ -744,20 +786,24 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator
if (_nifmResultCode != ResultCode.Success) if (_nifmResultCode != ResultCode.Success)
{ {
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn, $"SetStationAcceptPolicy: _nifmResultCode = {_nifmResultCode}");
return _nifmResultCode; return _nifmResultCode;
} }
if (acceptPolicy > AcceptPolicy.WhiteList) if (acceptPolicy > AcceptPolicy.WhiteList)
{ {
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn, "SetStationAcceptPolicy: Invalid argument!");
return ResultCode.InvalidArgument; return ResultCode.InvalidArgument;
} }
if (_state is NetworkState.AccessPoint or NetworkState.AccessPointCreated) if (_state is NetworkState.AccessPoint or NetworkState.AccessPointCreated)
{ {
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn, $"SetStationAcceptPolicy: acceptPolicy = {acceptPolicy}");
return _accessPoint.SetStationAcceptPolicy(acceptPolicy); return _accessPoint.SetStationAcceptPolicy(acceptPolicy);
} }
else else
{ {
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn, "SetStationAcceptPolicy: Invalid state!");
return ResultCode.InvalidState; return ResultCode.InvalidState;
} }
} }
@@ -768,6 +814,7 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator
{ {
if (_nifmResultCode != ResultCode.Success) if (_nifmResultCode != ResultCode.Success)
{ {
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn, $"AddAcceptFilterEntry: _nifmResultCode = {_nifmResultCode}");
return _nifmResultCode; return _nifmResultCode;
} }
@@ -782,6 +829,7 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator
{ {
if (_nifmResultCode != ResultCode.Success) if (_nifmResultCode != ResultCode.Success)
{ {
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn, $"ClearAcceptFilter: _nifmResultCode = {_nifmResultCode}");
return _nifmResultCode; return _nifmResultCode;
} }
@@ -796,11 +844,13 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator
{ {
if (_nifmResultCode != ResultCode.Success) if (_nifmResultCode != ResultCode.Success)
{ {
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn, $"OpenStation: _nifmResultCode = {_nifmResultCode}");
return _nifmResultCode; return _nifmResultCode;
} }
if (_state != NetworkState.Initialized) if (_state != NetworkState.Initialized)
{ {
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn, "OpenStation: Invalid state!");
return ResultCode.InvalidState; return ResultCode.InvalidState;
} }
@@ -813,6 +863,8 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator
// NOTE: Calls nifm service and returns related result codes. // NOTE: Calls nifm service and returns related result codes.
// Since we use our own implementation we can return ResultCode.Success. // Since we use our own implementation we can return ResultCode.Success.
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn, $"OpenStation: _station = {_station}");
return ResultCode.Success; return ResultCode.Success;
} }
@@ -823,6 +875,7 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator
{ {
if (_nifmResultCode != ResultCode.Success) if (_nifmResultCode != ResultCode.Success)
{ {
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn, $"CloseStation: _nifmResultCode = {_nifmResultCode}");
return _nifmResultCode; return _nifmResultCode;
} }
@@ -832,11 +885,13 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator
} }
else else
{ {
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn, "CloseStation: Invalid state!");
return ResultCode.InvalidState; return ResultCode.InvalidState;
} }
SetState(NetworkState.Initialized); SetState(NetworkState.Initialized);
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn, "CloseStation: Closed.");
return ResultCode.Success; return ResultCode.Success;
} }
@@ -901,11 +956,13 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator
bool isLocalCommunicationIdValid = CheckLocalCommunicationIdPermission(context, (ulong)networkInfo.NetworkId.IntentId.LocalCommunicationId); bool isLocalCommunicationIdValid = CheckLocalCommunicationIdPermission(context, (ulong)networkInfo.NetworkId.IntentId.LocalCommunicationId);
if (!isLocalCommunicationIdValid && NetworkClient.NeedsRealId) if (!isLocalCommunicationIdValid && NetworkClient.NeedsRealId)
{ {
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn, "ConnectImpl: Invalid object!");
return ResultCode.InvalidObject; return ResultCode.InvalidObject;
} }
if (_nifmResultCode != ResultCode.Success) if (_nifmResultCode != ResultCode.Success)
{ {
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn, $"ConnectImpl: _nifmResultCode = {_nifmResultCode}");
return _nifmResultCode; return _nifmResultCode;
} }
@@ -925,6 +982,7 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator
{ {
if (_state != NetworkState.Station) if (_state != NetworkState.Station)
{ {
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn, "ConnectImpl: Invalid state!");
resultCode = ResultCode.InvalidState; resultCode = ResultCode.InvalidState;
} }
else else
@@ -932,10 +990,16 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator
if (isPrivate) if (isPrivate)
{ {
resultCode = _station.ConnectPrivate(securityConfig, securityParameter, userConfig, localCommunicationVersion, optionUnknown, networkConfig); resultCode = _station.ConnectPrivate(securityConfig, securityParameter, userConfig, localCommunicationVersion, optionUnknown, networkConfig);
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn, $"ConnectImpl: Private connection established! " +
$"| securityConfig = {securityConfig} | securityParameter = {securityParameter} | userConfig = {userConfig} " +
$"| localCommunicationVersion = {localCommunicationVersion} | optionUnknown = {optionUnknown} | networkConfig = {networkConfig}");
} }
else else
{ {
resultCode = _station.Connect(securityConfig, userConfig, localCommunicationVersion, optionUnknown, networkInfo); resultCode = _station.Connect(securityConfig, userConfig, localCommunicationVersion, optionUnknown, networkInfo);
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn, $"ConnectImpl: Connection established! " +
$"| securityConfig = {securityConfig} | userConfig = {userConfig} " +
$"| localCommunicationVersion = {localCommunicationVersion} | optionUnknown = {optionUnknown} | networkConfig = {networkConfig}");
} }
} }
} }
@@ -943,6 +1007,8 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator
} }
} }
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn, $"ConnectImpl: resultCode = {resultCode}");
return resultCode; return resultCode;
} }
@@ -957,6 +1023,7 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator
{ {
if (_nifmResultCode != ResultCode.Success) if (_nifmResultCode != ResultCode.Success)
{ {
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn, $"DisconnectImpl: _nifmResultCode = {_nifmResultCode}");
return _nifmResultCode; return _nifmResultCode;
} }
@@ -970,14 +1037,17 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator
_disconnectReason = disconnectReason; _disconnectReason = disconnectReason;
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn, $"DisconnectImpl: _disconnectReason = {_disconnectReason}");
return ResultCode.Success; return ResultCode.Success;
} }
CloseStation(); CloseStation();
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn, "DisconnectImpl: Invalid state!");
return ResultCode.InvalidState; return ResultCode.InvalidState;
} }
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn, "DisconnectImpl: Invalid argument!");
return ResultCode.InvalidArgument; return ResultCode.InvalidArgument;
} }
@@ -994,6 +1064,7 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator
{ {
if (_nifmResultCode != ResultCode.Success) if (_nifmResultCode != ResultCode.Success)
{ {
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn, $"Finalize: _disconnectReason = {_disconnectReason}");
return _nifmResultCode; return _nifmResultCode;
} }
@@ -1010,11 +1081,13 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator
_stateChangeEventHandle = 0; _stateChangeEventHandle = 0;
} }
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn, $"Finalize: resultCode = {resultCode}");
return resultCode; return resultCode;
} }
private ResultCode FinalizeImpl(bool isCausedBySystem) private ResultCode FinalizeImpl(bool isCausedBySystem)
{ {
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn, "FinalizeImpl");
DisconnectReason disconnectReason; DisconnectReason disconnectReason;
switch (_state) switch (_state)
@@ -1138,7 +1211,6 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator
NetworkClient.SetGameVersion(context.Device.Processes.ActiveApplication.ApplicationControlProperties.DisplayVersion); NetworkClient.SetGameVersion(context.Device.Processes.ActiveApplication.ApplicationControlProperties.DisplayVersion);
resultCode = ResultCode.Success; resultCode = ResultCode.Success;
_nifmResultCode = resultCode; _nifmResultCode = resultCode;
SetState(NetworkState.Initialized); SetState(NetworkState.Initialized);
@@ -1152,6 +1224,7 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator
} }
} }
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn, $"InitializeImpl: resultCode = {resultCode}");
return resultCode; return resultCode;
} }

View File

@@ -132,7 +132,7 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator.LdnRyu
protected override void OnConnected() protected override void OnConnected()
{ {
Logger.Info?.PrintMsg(LogClass.ServiceLdn, $"LDN TCP client connected a new session with Id {Id}"); Logger.NetLog?.PrintMsg(LogClass.ServiceLdn, $"LDN TCP client connected a new session with Id {Id}");
UpdatePassphraseIfNeeded(); UpdatePassphraseIfNeeded();
@@ -141,7 +141,7 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator.LdnRyu
protected override void OnDisconnected() protected override void OnDisconnected()
{ {
Logger.Info?.PrintMsg(LogClass.ServiceLdn, $"LDN TCP client disconnected a session with Id {Id}"); Logger.NetLog?.PrintMsg(LogClass.ServiceLdn, $"LDN TCP client disconnected a session with Id {Id}");
_passphrase = null; _passphrase = null;
@@ -174,7 +174,7 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator.LdnRyu
protected override void OnError(SocketError error) protected override void OnError(SocketError error)
{ {
Logger.Info?.PrintMsg(LogClass.ServiceLdn, $"LDN TCP client caught an error with code {error}"); Logger.NetLog?.PrintMsg(LogClass.ServiceLdn, $"LDN TCP client caught an error with code {error}");
_error.Set(); _error.Set();
} }
@@ -428,7 +428,7 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator.LdnRyu
} }
else else
{ {
Logger.Info?.Print(LogClass.ServiceLdn, $"Created a wireless P2P network on port {request.ExternalProxyPort}."); Logger.NetLog?.Print(LogClass.ServiceLdn, $"Created a wireless P2P network on port {request.ExternalProxyPort}.");
_hostedProxy.Start(); _hostedProxy.Start();
(_, UnicastIPAddressInformation unicastAddress) = NetworkHelpers.GetLocalInterface(); (_, UnicastIPAddressInformation unicastAddress) = NetworkHelpers.GetLocalInterface();

View File

@@ -1,3 +1,4 @@
using Ryujinx.Common.Logging;
using Ryujinx.Common.Memory; using Ryujinx.Common.Memory;
using Ryujinx.HLE.HOS.Services.Ldn.Types; using Ryujinx.HLE.HOS.Services.Ldn.Types;
using Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator.Types; using Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator.Types;
@@ -36,10 +37,12 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator
if (Connected) if (Connected)
{ {
_parent.SetState(NetworkState.StationConnected); _parent.SetState(NetworkState.StationConnected);
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn,$"NetworkChanged: {NetworkInfo}");
} }
else else
{ {
_parent.SetDisconnectReason(e.DisconnectReasonOrDefault(DisconnectReason.DestroyedByUser)); _parent.SetDisconnectReason(e.DisconnectReasonOrDefault(DisconnectReason.DestroyedByUser));
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn,"NetworkChanged: Disconnected (DestroyedByUser)");
} }
} }
else else

View File

@@ -81,8 +81,10 @@ namespace Ryujinx.HLE.HOS.Services.Mii
return ResultCode.Success; return ResultCode.Success;
} }
public ResultCode UpdateLatest<T>(DatabaseSessionMetadata metadata, IStoredData<T> oldMiiData, SourceFlag flag, IStoredData<T> newMiiData) where T : unmanaged public ResultCode UpdateLatest<T>(DatabaseSessionMetadata metadata, T oldMiiData, SourceFlag flag, out T newMiiData) where T : unmanaged, IStoredData<T>
{ {
newMiiData = default;
if (!flag.HasFlag(SourceFlag.Database)) if (!flag.HasFlag(SourceFlag.Database))
{ {
return ResultCode.NotFound; return ResultCode.NotFound;
@@ -106,7 +108,7 @@ namespace Ryujinx.HLE.HOS.Services.Mii
newMiiData.SetFromStoreData(storeData); newMiiData.SetFromStoreData(storeData);
if (oldMiiData == newMiiData) if (oldMiiData.Equals(newMiiData))
{ {
return ResultCode.NotUpdated; return ResultCode.NotUpdated;
} }
@@ -286,6 +288,18 @@ namespace Ryujinx.HLE.HOS.Services.Mii
return result; return result;
} }
public ResultCode Append(DatabaseSessionMetadata metadata, CharInfo charInfo)
{
ResultCode result = _miiDatabase.Append(metadata, _utilityImpl, charInfo);
if (result == ResultCode.Success)
{
result = _miiDatabase.SaveDatabase();
}
return result;
}
public ResultCode ConvertCharInfoToCoreData(CharInfo charInfo, out CoreData coreData) public ResultCode ConvertCharInfoToCoreData(CharInfo charInfo, out CoreData coreData)
{ {
coreData = new CoreData(); coreData = new CoreData();

View File

@@ -449,6 +449,32 @@ namespace Ryujinx.HLE.HOS.Services.Mii
return ResultCode.Success; return ResultCode.Success;
} }
public ResultCode Append(DatabaseSessionMetadata metadata, UtilityImpl utilityImpl, CharInfo charInfo)
{
if (!charInfo.IsValid())
{
return ResultCode.InvalidCharInfo;
}
if (charInfo.Type == 1)
{
return ResultCode.InvalidOperationOnSpecialMii;
}
CoreData coreData = new();
coreData.SetFromCharInfo(charInfo);
StoreData storeData;
do
{
storeData = StoreData.BuildFromCoreData(utilityImpl, coreData);
}
while (_database.GetIndexByCreatorId(out _, storeData.CreateId));
return AddOrReplace(metadata, storeData);
}
public ResultCode Delete(DatabaseSessionMetadata metadata, CreateId createId) public ResultCode Delete(DatabaseSessionMetadata metadata, CreateId createId)
{ {
if (!_database.GetIndexByCreatorId(out int index, createId)) if (!_database.GetIndexByCreatorId(out int index, createId))

View File

@@ -54,9 +54,7 @@ namespace Ryujinx.HLE.HOS.Services.Mii.StaticService
protected override ResultCode UpdateLatest(CharInfo oldCharInfo, SourceFlag flag, out CharInfo newCharInfo) protected override ResultCode UpdateLatest(CharInfo oldCharInfo, SourceFlag flag, out CharInfo newCharInfo)
{ {
newCharInfo = default; return _database.UpdateLatest(_metadata, oldCharInfo, flag, out newCharInfo);
return _database.UpdateLatest(_metadata, oldCharInfo, flag, newCharInfo);
} }
protected override ResultCode BuildRandom(Age age, Gender gender, Race race, out CharInfo charInfo) protected override ResultCode BuildRandom(Age age, Gender gender, Race race, out CharInfo charInfo)
@@ -113,14 +111,14 @@ namespace Ryujinx.HLE.HOS.Services.Mii.StaticService
protected override ResultCode UpdateLatest1(StoreData oldStoreData, SourceFlag flag, out StoreData newStoreData) protected override ResultCode UpdateLatest1(StoreData oldStoreData, SourceFlag flag, out StoreData newStoreData)
{ {
newStoreData = default;
if (!_isSystem) if (!_isSystem)
{ {
newStoreData = default;
return ResultCode.PermissionDenied; return ResultCode.PermissionDenied;
} }
return _database.UpdateLatest(_metadata, oldStoreData, flag, newStoreData); return _database.UpdateLatest(_metadata, oldStoreData, flag, out newStoreData);
} }
protected override ResultCode FindIndex(CreateId createId, bool isSpecial, out int index) protected override ResultCode FindIndex(CreateId createId, bool isSpecial, out int index)
@@ -262,5 +260,10 @@ namespace Ryujinx.HLE.HOS.Services.Mii.StaticService
{ {
return _database.ConvertCharInfoToCoreData(charInfo, out coreData); return _database.ConvertCharInfoToCoreData(charInfo, out coreData);
} }
protected override ResultCode Append(CharInfo charInfo)
{
return _database.Append(_metadata, charInfo);
}
} }
} }

View File

@@ -340,6 +340,15 @@ namespace Ryujinx.HLE.HOS.Services.Mii.StaticService
return result; return result;
} }
[CommandCmif(26)] // 10.2.0+
// Append(nn::mii::CharInfo char_info)
public ResultCode Append(ServiceCtx context)
{
CharInfo charInfo = context.RequestData.ReadStruct<CharInfo>();
return Append(charInfo);
}
private Span<byte> CreateByteSpanFromBuffer(ServiceCtx context, IpcBuffDesc ipcBuff, bool isOutput) private Span<byte> CreateByteSpanFromBuffer(ServiceCtx context, IpcBuffDesc ipcBuff, bool isOutput)
{ {
byte[] rawData; byte[] rawData;
@@ -421,5 +430,7 @@ namespace Ryujinx.HLE.HOS.Services.Mii.StaticService
protected abstract ResultCode ConvertCoreDataToCharInfo(CoreData coreData, out CharInfo charInfo); protected abstract ResultCode ConvertCoreDataToCharInfo(CoreData coreData, out CharInfo charInfo);
protected abstract ResultCode ConvertCharInfoToCoreData(CharInfo charInfo, out CoreData coreData); protected abstract ResultCode ConvertCharInfoToCoreData(CharInfo charInfo, out CoreData coreData);
protected abstract ResultCode Append(CharInfo charInfo);
} }
} }

View File

@@ -0,0 +1,24 @@
namespace Ryujinx.HLE.HOS.Services.Notification
{
[Service("notif:s")] // 9.0.0+
class INotificationServices : IpcService
{
public INotificationServices(ServiceCtx context) { }
[CommandCmif(1000)] // 9.0.0+
// GetNotificationCount() -> nn::notification::server::INotificationSystemEventAccessor
public ResultCode GetNotificationCount(ServiceCtx context)
{
MakeObject(context, new INotificationSystemEventAccessor(context));
return ResultCode.Success;
}
[CommandCmif(1040)] // 9.0.0+
// GetNotificationSendingNotifier() -> nn::notification::server::INotificationSystemEventAccessor
public ResultCode GetNotificationSendingNotifier(ServiceCtx context)
{
MakeObject(context, new INotificationSystemEventAccessor(context));
return ResultCode.Success;
}
}
}

View File

@@ -1,8 +1,33 @@
using Ryujinx.Common.Logging;
namespace Ryujinx.HLE.HOS.Services.Notification namespace Ryujinx.HLE.HOS.Services.Notification
{ {
[Service("notif:a")] // 9.0.0+ [Service("notif:a")] // 9.0.0+
class INotificationServicesForApplication : IpcService class INotificationServicesForApplication : IpcService
{ {
public INotificationServicesForApplication(ServiceCtx context) { } public INotificationServicesForApplication(ServiceCtx context) { }
// Leaving this here since I can never find it: https://switchbrew.org/wiki/Glue_services
[CommandCmif(520)] // 9.0.0+
// ListAlarmSettings(nn::arp::ApplicationCertificate) -> s32 AlarmSettingsCount
public ResultCode ListAlarmSettings(ServiceCtx context)
{
// TO-DO: Currently just returns 0. Should read in an ApplicationCertificate.
int alarmSettingsCount = 0;
context.ResponseData.Write(alarmSettingsCount);
return ResultCode.Success;
}
[CommandCmif(1000)] // 9.0.0+
// Initialize(PID-descriptor, u64 pid_reserved)
public ResultCode Intialize(ServiceCtx context)
{
ulong pid = context.Request.HandleDesc.PId;
context.RequestData.ReadUInt64(); // pid placeholder, zero
Logger.Stub?.PrintStub(LogClass.ServiceNotification, new { pid });
return ResultCode.Success;
}
} }
} }

View File

@@ -1,8 +0,0 @@
namespace Ryujinx.HLE.HOS.Services.Notification
{
[Service("notif:s")] // 9.0.0+
class INotificationServicesForSystem : IpcService
{
public INotificationServicesForSystem(ServiceCtx context) { }
}
}

View File

@@ -0,0 +1,32 @@
using Ryujinx.Common.Logging;
using Ryujinx.HLE.HOS.Ipc;
using Ryujinx.HLE.HOS.Kernel.Threading;
using Ryujinx.Horizon.Common;
using System;
namespace Ryujinx.HLE.HOS.Services.Notification
{
class INotificationSystemEventAccessor : IpcService
{
private readonly KEvent _getNotificationSendingNotifierEvent;
private int _getNotificationSendingNotifierEventHandle;
public INotificationSystemEventAccessor(ServiceCtx context) { }
[CommandCmif(0)] // 9.0.0+
// GetNotificationSendingNotifier() -> nn::notification::server::INotificationSystemEventAccessor
public ResultCode GetSystemEvent(ServiceCtx context)
{
if (_getNotificationSendingNotifierEventHandle == 0)
{
if (context.Process.HandleTable.GenerateHandle(_getNotificationSendingNotifierEvent.ReadableEvent, out _getNotificationSendingNotifierEventHandle) != Result.Success)
{
throw new InvalidOperationException("Out of handles!");
}
}
context.Response.HandleDesc = IpcHandleDesc.MakeCopy(_getNotificationSendingNotifierEventHandle);
return ResultCode.Success;
}
}
}

View File

@@ -14,6 +14,7 @@ using Ryujinx.HLE.Loaders.Executables;
using Ryujinx.HLE.Loaders.Processes.Extensions; using Ryujinx.HLE.Loaders.Processes.Extensions;
using System; using System;
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.Collections.Generic;
using System.IO; using System.IO;
using Path = System.IO.Path; using Path = System.IO.Path;
@@ -27,10 +28,16 @@ namespace Ryujinx.HLE.Loaders.Processes
private ulong _latestPid; private ulong _latestPid;
public ProcessResult ActiveApplication #nullable enable
public ProcessResult? ActiveApplication
{ {
get get
{ {
return _processesByPid.GetValueOrDefault(_latestPid);
// Using this if statement locks up the UI and prevents a new game from loading.
// Haven't quite deduced why yet.
if (!_processesByPid.TryGetValue(_latestPid, out ProcessResult value)) if (!_processesByPid.TryGetValue(_latestPid, out ProcessResult value))
throw new RyujinxException( throw new RyujinxException(
$"The HLE Process map did not have a process with ID {_latestPid}. Are you missing firmware?"); $"The HLE Process map did not have a process with ID {_latestPid}. Are you missing firmware?");
@@ -38,6 +45,7 @@ namespace Ryujinx.HLE.Loaders.Processes
return value; return value;
} }
} }
#nullable disable
public ProcessLoader(Switch device) public ProcessLoader(Switch device)
{ {
@@ -144,7 +152,7 @@ namespace Ryujinx.HLE.Loaders.Processes
public bool LoadUnpackedNca(string exeFsDirPath, string romFsPath = null) public bool LoadUnpackedNca(string exeFsDirPath, string romFsPath = null)
{ {
ProcessResult processResult = new LocalFileSystem(exeFsDirPath).Load(_device, romFsPath); ProcessResult processResult = new LocalFileSystem(exeFsDirPath).Load(_device, romFsPath);
if (processResult.ProcessId != 0 && _processesByPid.TryAdd(processResult.ProcessId, processResult)) if (processResult.ProcessId != 0 && _processesByPid.TryAdd(processResult.ProcessId, processResult))
{ {
if (processResult.Start(_device)) if (processResult.Start(_device))

View File

@@ -4,7 +4,6 @@ using LibHac.Ns;
using Ryujinx.Common.Logging; using Ryujinx.Common.Logging;
using Ryujinx.Cpu; using Ryujinx.Cpu;
using Ryujinx.HLE.HOS.SystemState; using Ryujinx.HLE.HOS.SystemState;
using Ryujinx.HLE.Loaders.Processes.Extensions;
using Ryujinx.Horizon.Common; using Ryujinx.Horizon.Common;
namespace Ryujinx.HLE.Loaders.Processes namespace Ryujinx.HLE.Loaders.Processes
@@ -52,6 +51,7 @@ namespace Ryujinx.HLE.Loaders.Processes
if (metaLoader is not null) if (metaLoader is not null)
{ {
Logger.Info?.Print(LogClass.Application,$"metaLoader: {metaLoader}");
ulong programId = metaLoader.ProgramId; ulong programId = metaLoader.ProgramId;
Name = ApplicationControlProperties.Title[(int)titleLanguage].NameString.ToString(); Name = ApplicationControlProperties.Title[(int)titleLanguage].NameString.ToString();
@@ -71,8 +71,15 @@ namespace Ryujinx.HLE.Loaders.Processes
ProgramId = programId; ProgramId = programId;
ProgramIdText = $"{programId:x16}"; ProgramIdText = $"{programId:x16}";
Is64Bit = metaLoader.IsProgram64Bit; Is64Bit = metaLoader.IsProgram64Bit;
}
else
{
Logger.Error?.Print(LogClass.Application,$"metaLoader is null !!!");
ProcessId = 0;
return;
} }
DiskCacheEnabled = diskCacheEnabled; DiskCacheEnabled = diskCacheEnabled;
AllowCodeMemoryForJit = allowCodeMemoryForJit; AllowCodeMemoryForJit = allowCodeMemoryForJit;
} }

View File

@@ -27,7 +27,9 @@
<PackageReference Include="Microsoft.IdentityModel.JsonWebTokens" /> <PackageReference Include="Microsoft.IdentityModel.JsonWebTokens" />
<PackageReference Include="MsgPack.Cli" /> <PackageReference Include="MsgPack.Cli" />
<PackageReference Include="SkiaSharp" /> <PackageReference Include="SkiaSharp" />
<PackageReference Include="SkiaSharp.NativeAssets.Linux" /> <PackageReference Include="SkiaSharp.NativeAssets.Win32" />
<PackageReference Include="SkiaSharp.NativeAssets.macOS" />
<PackageReference Include="SkiaSharp.NativeAssets.Linux.NoDependencies" />
<PackageReference Include="NetCoreServer" /> <PackageReference Include="NetCoreServer" />
<PackageReference Include="Open.NAT.Core" /> <PackageReference Include="Open.NAT.Core" />
</ItemGroup> </ItemGroup>

View File

@@ -61,14 +61,6 @@ namespace Ryujinx.SDL3.Common
SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_JOY_CONS, "1"); SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_JOY_CONS, "1");
SDL_SetHint(SDL_HINT_VIDEO_ALLOW_SCREENSAVER, "1"); SDL_SetHint(SDL_HINT_VIDEO_ALLOW_SCREENSAVER, "1");
// When hid_nintendo is loaded, it creates separate evdev devices for the gamepad
// and IMU which SDL3's evdev backend combines via UNIQ matching. Using HIDAPI
// instead conflicts with the kernel driver and breaks motion and hotplug.
if (OperatingSystem.IsLinux() && Directory.Exists("/sys/module/hid_nintendo"))
{
SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_SWITCH, "0");
}
// NOTE: As of SDL3 2.24.0, joycons are combined by default but the motion source only come from one of them. // NOTE: As of SDL3 2.24.0, joycons are combined by default but the motion source only come from one of them.
// We disable this behavior for now. // We disable this behavior for now.
SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_COMBINE_JOY_CONS, "0"); SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_COMBINE_JOY_CONS, "0");

View File

@@ -0,0 +1,187 @@
using NUnit.Framework;
using Ryujinx.HLE.HOS.Services.Caps;
using Ryujinx.HLE.HOS.Services.Caps.Types;
using SkiaSharp;
using System;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
namespace Ryujinx.Tests.HLE
{
public class CaptureManagerTests
{
private const int ScreenshotWidth = 1280;
private const int ScreenshotHeight = 720;
private const int BytesPerPixel = 4;
private const int ScreenshotDataSize = ScreenshotWidth * ScreenshotHeight * BytesPerPixel; // 0x384000
private const int PaddedScreenshotDataSize = ScreenshotWidth * 768 * BytesPerPixel; // 0x3C0000
[Test]
public void SaveScreenShotRejectsBufferSmallerThan720p()
{
using TempSdCard tempSdCard = new();
CaptureManager captureManager = CreateCaptureManager(tempSdCard.Path);
byte[] screenshotData = new byte[ScreenshotDataSize - 1];
ResultCode result = captureManager.SaveScreenShot(
screenshotData,
appletResourceUserId: 0,
titleId: 0x0100000000001000,
out _);
Assert.That(result, Is.EqualTo(ResultCode.NullInputBuffer));
Assert.That(Directory.Exists(Path.Combine(tempSdCard.Path, "Nintendo", "Album")), Is.False);
}
[Test]
public void SaveScreenShotAcceptsExact720pBuffer()
{
using TempSdCard tempSdCard = new();
CaptureManager captureManager = CreateCaptureManager(tempSdCard.Path);
byte[] screenshotData = CreateTestPattern(ScreenshotDataSize);
ResultCode result = captureManager.SaveScreenShot(
screenshotData,
appletResourceUserId: 0,
titleId: 0x0100000000001000,
out ApplicationAlbumEntry applicationAlbumEntry);
string filePath = GetSingleAlbumFile(tempSdCard.Path);
using SKBitmap bitmap = SKBitmap.Decode(filePath);
Assert.Multiple(() =>
{
Assert.That(result, Is.EqualTo(ResultCode.Success));
Assert.That(bitmap.Width, Is.EqualTo(ScreenshotWidth));
Assert.That(bitmap.Height, Is.EqualTo(ScreenshotHeight));
Assert.That(applicationAlbumEntry.TitleId, Is.EqualTo(0x0100000000001000));
Assert.That(applicationAlbumEntry.AlbumStorage, Is.EqualTo(AlbumStorage.Sd));
Assert.That(applicationAlbumEntry.ContentType, Is.EqualTo(ContentType.Screenshot));
Assert.That(applicationAlbumEntry.Unknown0x1f, Is.EqualTo(1));
});
}
[Test]
public void SaveScreenShotAcceptsBufferLargerThan720p()
{
using TempSdCard tempSdCard = new();
CaptureManager captureManager = CreateCaptureManager(tempSdCard.Path);
byte[] screenshotData = CreateTestPattern(PaddedScreenshotDataSize);
ResultCode result = captureManager.SaveScreenShot(
screenshotData,
appletResourceUserId: 0,
titleId: 0x0100000000001000,
out ApplicationAlbumEntry applicationAlbumEntry);
string filePath = GetSingleAlbumFile(tempSdCard.Path);
using SKBitmap bitmap = SKBitmap.Decode(filePath);
Assert.Multiple(() =>
{
Assert.That(result, Is.EqualTo(ResultCode.Success));
Assert.That(bitmap.Width, Is.EqualTo(ScreenshotWidth));
Assert.That(bitmap.Height, Is.EqualTo(ScreenshotHeight));
Assert.That(applicationAlbumEntry.TitleId, Is.EqualTo(0x0100000000001000));
});
}
[Test]
public void SaveScreenShotCreatesUniqueFileNamesForRepeatedSaves()
{
using TempSdCard tempSdCard = new();
CaptureManager captureManager = CreateCaptureManager(tempSdCard.Path);
byte[] screenshotData = CreateTestPattern(ScreenshotDataSize);
ResultCode firstResult = captureManager.SaveScreenShot(
screenshotData,
appletResourceUserId: 0,
titleId: 0x0100000000001000,
out _);
ResultCode secondResult = captureManager.SaveScreenShot(
screenshotData,
appletResourceUserId: 0,
titleId: 0x0100000000001000,
out _);
string[] files = Directory.GetFiles(
Path.Combine(tempSdCard.Path, "Nintendo", "Album"),
"*.jpg",
SearchOption.AllDirectories);
Assert.Multiple(() =>
{
Assert.That(firstResult, Is.EqualTo(ResultCode.Success));
Assert.That(secondResult, Is.EqualTo(ResultCode.Success));
Assert.That(files, Has.Length.EqualTo(2));
});
}
private static CaptureManager CreateCaptureManager(string sdCardPath)
{
CaptureManager captureManager = (CaptureManager)RuntimeHelpers.GetUninitializedObject(typeof(CaptureManager));
typeof(CaptureManager)
.GetField("_sdCardPath", BindingFlags.Instance | BindingFlags.NonPublic)
.SetValue(captureManager, sdCardPath);
return captureManager;
}
private static string GetSingleAlbumFile(string sdCardPath)
{
string albumPath = Path.Combine(sdCardPath, "Nintendo", "Album");
string[] files = Directory.GetFiles(albumPath, "*.jpg", SearchOption.AllDirectories);
Assert.That(files, Has.Length.EqualTo(1));
return files.Single();
}
private static byte[] CreateTestPattern(int size)
{
byte[] data = new byte[size];
int pixelCount = size / BytesPerPixel;
for (int i = 0; i < pixelCount; i++)
{
int x = i % ScreenshotWidth;
int y = i / ScreenshotWidth;
data[(i * 4) + 0] = (byte)(x & 0xff);
data[(i * 4) + 1] = (byte)(y & 0xff);
data[(i * 4) + 2] = 0x80;
data[(i * 4) + 3] = 0xff;
}
return data;
}
private sealed class TempSdCard : IDisposable
{
public string Path { get; } = System.IO.Path.Combine(
TestContext.CurrentContext.WorkDirectory,
"sdcard-" + Guid.NewGuid());
public void Dispose()
{
if (Directory.Exists(Path))
{
Directory.Delete(Path, recursive: true);
}
}
}
}
}

View File

@@ -0,0 +1,122 @@
using System.Reflection;
using NUnit.Framework;
using Ryujinx.Cpu;
using Ryujinx.HLE.HOS.Services.Mii;
using Ryujinx.HLE.HOS.Services.Mii.StaticService;
using Ryujinx.HLE.HOS.Services.Mii.Types;
namespace Ryujinx.Tests.HLE
{
public class MiiDatabaseTests
{
[Test]
public void UpdateLatestReturnsStoredCharInfo()
{
DatabaseImpl database = new();
StoreData storedData = StoreData.BuildDefault(new UtilityImpl(new TickSource(19200000)), 0);
MiiDatabaseManager databaseManager = GetDatabaseManager(database);
NintendoFigurineDatabase figurineDatabase = new();
figurineDatabase.Format();
figurineDatabase.Add(storedData);
SetFigurineDatabase(databaseManager, figurineDatabase);
TestDatabaseService service = new(database);
CharInfo oldCharInfo = new();
oldCharInfo.SetFromStoreData(storedData);
oldCharInfo.Height--;
ResultCode result = service.UpdateLatestForTest(oldCharInfo, SourceFlag.Database, out CharInfo newCharInfo);
Assert.Multiple(() =>
{
Assert.That(result, Is.EqualTo(ResultCode.Success));
Assert.That(newCharInfo.CreateId, Is.EqualTo(oldCharInfo.CreateId));
Assert.That(newCharInfo.Height, Is.EqualTo(storedData.CoreData.Height));
Assert.That(newCharInfo.IsValid(), Is.True);
});
}
[Test]
public void AppendAddsRegularCharInfoToDatabase()
{
DatabaseImpl database = new();
UtilityImpl utilityImpl = new(new TickSource(19200000));
SetUtilityImpl(database, utilityImpl);
MiiDatabaseManager databaseManager = GetDatabaseManager(database);
SetFigurineDatabase(databaseManager, CreateFormattedDatabase());
StoreData defaultStoreData = StoreData.BuildDefault(utilityImpl, 0);
Assert.Multiple(() =>
{
Assert.That(defaultStoreData.CoreData.IsValid(), Is.True);
Assert.That(defaultStoreData.IsValidDataCrc(), Is.True);
Assert.That(defaultStoreData.IsValidDeviceCrc(), Is.True);
Assert.That(defaultStoreData.IsValid(), Is.True);
});
CharInfo charInfo = new();
charInfo.SetFromStoreData(defaultStoreData);
DatabaseSessionMetadata metadata = database.CreateSessionMetadata(new SpecialMiiKeyCode());
ResultCode result = databaseManager.Append(metadata, utilityImpl, charInfo);
int count = databaseManager.GetCount(metadata);
databaseManager.Get(metadata, 0, out StoreData storedData);
CoreData expectedCoreData = new();
expectedCoreData.SetFromCharInfo(charInfo);
Assert.Multiple(() =>
{
Assert.That(result, Is.EqualTo(ResultCode.Success));
Assert.That(count, Is.EqualTo(1));
Assert.That(storedData.IsValid(), Is.True);
Assert.That(storedData.CreateId, Is.Not.EqualTo(charInfo.CreateId));
Assert.That(storedData.CoreData, Is.EqualTo(expectedCoreData));
});
}
private sealed class TestDatabaseService(DatabaseImpl database) : DatabaseServiceImpl(database, true, new SpecialMiiKeyCode())
{
public ResultCode UpdateLatestForTest(CharInfo oldCharInfo, SourceFlag flag, out CharInfo newCharInfo)
{
return UpdateLatest(oldCharInfo, flag, out newCharInfo);
}
}
private static MiiDatabaseManager GetDatabaseManager(DatabaseImpl database)
{
return (MiiDatabaseManager)typeof(DatabaseImpl)
.GetField("_miiDatabase", BindingFlags.Instance | BindingFlags.NonPublic)
.GetValue(database);
}
private static void SetFigurineDatabase(MiiDatabaseManager databaseManager, NintendoFigurineDatabase figurineDatabase)
{
typeof(MiiDatabaseManager)
.GetField("_database", BindingFlags.Instance | BindingFlags.NonPublic)
.SetValue(databaseManager, figurineDatabase);
}
private static NintendoFigurineDatabase CreateFormattedDatabase()
{
NintendoFigurineDatabase figurineDatabase = new();
figurineDatabase.Format();
return figurineDatabase;
}
private static void SetUtilityImpl(DatabaseImpl database, UtilityImpl utilityImpl)
{
typeof(DatabaseImpl)
.GetField("_utilityImpl", BindingFlags.Instance | BindingFlags.NonPublic)
.SetValue(database, utilityImpl);
}
}
}

View File

@@ -254,6 +254,7 @@ namespace Ryujinx.Headless
Logger.SetEnable(LogLevel.Trace, option.LoggingEnableTrace); Logger.SetEnable(LogLevel.Trace, option.LoggingEnableTrace);
Logger.SetEnable(LogLevel.Guest, !option.LoggingDisableGuest); Logger.SetEnable(LogLevel.Guest, !option.LoggingDisableGuest);
Logger.SetEnable(LogLevel.AccessLog, option.LoggingEnableFsAccessLog); Logger.SetEnable(LogLevel.AccessLog, option.LoggingEnableFsAccessLog);
Logger.SetEnable(LogLevel.NetLog, option.LoggingEnableFsAccessLog);
if (!option.DisableFileLog) if (!option.DisableFileLog)
{ {

View File

@@ -108,6 +108,9 @@ namespace Ryujinx.Headless
if (NeedsOverride(nameof(LoggingEnableFsAccessLog))) if (NeedsOverride(nameof(LoggingEnableFsAccessLog)))
LoggingEnableFsAccessLog = configurationState.Logger.EnableFsAccessLog; LoggingEnableFsAccessLog = configurationState.Logger.EnableFsAccessLog;
if (NeedsOverride(nameof(LoggingEnableNetLog)))
LoggingEnableNetLog = configurationState.Logger.EnableNetLog;
if (NeedsOverride(nameof(LoggingGraphicsDebugLevel))) if (NeedsOverride(nameof(LoggingGraphicsDebugLevel)))
LoggingGraphicsDebugLevel = configurationState.Logger.GraphicsDebugLevel; LoggingGraphicsDebugLevel = configurationState.Logger.GraphicsDebugLevel;
@@ -370,6 +373,9 @@ namespace Ryujinx.Headless
[Option("enable-fs-access-logs", Required = false, Default = false, HelpText = "Enables printing FS access log messages.")] [Option("enable-fs-access-logs", Required = false, Default = false, HelpText = "Enables printing FS access log messages.")]
public bool LoggingEnableFsAccessLog { get; set; } public bool LoggingEnableFsAccessLog { get; set; }
[Option("enable-net-logs", Required = false, Default = false, HelpText = "Enables printing net log messages.")]
public bool LoggingEnableNetLog { get; set; }
[Option("graphics-debug-level", Required = false, Default = GraphicsDebugLevel.None, HelpText = "Change Graphics API debug log level.")] [Option("graphics-debug-level", Required = false, Default = GraphicsDebugLevel.None, HelpText = "Change Graphics API debug log level.")]
public GraphicsDebugLevel LoggingGraphicsDebugLevel { get; set; } public GraphicsDebugLevel LoggingGraphicsDebugLevel { get; set; }

View File

@@ -24,11 +24,9 @@ using Ryujinx.Headless;
using Ryujinx.SDL3.Common; using Ryujinx.SDL3.Common;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics;
using System.IO; using System.IO;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Security.Principal; using System.Security.Principal;
using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace Ryujinx.Ava namespace Ryujinx.Ava
@@ -54,22 +52,6 @@ namespace Ryujinx.Ava
if (OperatingSystem.IsWindows()) if (OperatingSystem.IsWindows())
{ {
#if !DEBUG
// this fixes the "hide console" option by forcing the emulator to launch in an old-school cmd
if (!Console.Title.Contains("conhost.exe"))
{
StringBuilder sb = new();
foreach (string arg in args)
{
sb.Append(arg.Contains(' ') ? $" \"{arg}\"" : $" {arg}");
}
Process.Start("conhost.exe", $"{Environment.ProcessPath} {sb}");
return 0;
}
#endif
if (!OperatingSystem.IsWindowsVersionAtLeast(10, 0, 19041)) if (!OperatingSystem.IsWindowsVersionAtLeast(10, 0, 19041))
{ {
_ = Win32NativeInterop.MessageBoxA(nint.Zero, "You are running an outdated version of Windows.\n\nRyujinx supports Windows 10 version 20H1 and newer.\n", $"Ryujinx {Version}", MbIconwarning); _ = Win32NativeInterop.MessageBoxA(nint.Zero, "You are running an outdated version of Windows.\n\nRyujinx supports Windows 10 version 20H1 and newer.\n", $"Ryujinx {Version}", MbIconwarning);
@@ -103,7 +85,7 @@ namespace Ryujinx.Ava
CoreDumpArg = coreDumpArg; CoreDumpArg = coreDumpArg;
// TODO: Ryujinx causes core dumps on Linux when it exits "uncleanly", eg. through an unhandled exception. // TODO: Ryujinx causes core dumps on Linux when it exits "uncleanly", eg. through an unhandled exception.
// This is undesirable and causes very odd behavior during development (the process stops responding, // This is undesirable and causes very odd behavior during development (the process stops responding,
// the .NET debugger freezes or suddenly detaches, /tmp/ gets filled etc.), unless explicitly requested by the user. // the .NET debugger freezes or suddenly detaches, /tmp/ gets filled etc.), unless explicitly requested by the user.
// This needs to be investigated, but calling prctl() is better than modifying system-wide settings or leaving this be. // This needs to be investigated, but calling prctl() is better than modifying system-wide settings or leaving this be.
if (!coreDumpArg) if (!coreDumpArg)
@@ -260,7 +242,7 @@ namespace Ryujinx.Ava
ConfigurationPath = appDataConfigurationPath; ConfigurationPath = appDataConfigurationPath;
} }
} }
if (ConfigurationPath == null) if (ConfigurationPath == null)
{ {
// No configuration, we load the default values and save it to disk // No configuration, we load the default values and save it to disk
@@ -331,28 +313,28 @@ namespace Ryujinx.Ava
_ => ConfigurationState.Instance.HideCursor, _ => ConfigurationState.Instance.HideCursor,
}; };
// Check if memoryManagerMode was overridden. // Check if memoryManagerMode was overridden.
if (CommandLineState.OverrideMemoryManagerMode is not null) if (CommandLineState.OverrideMemoryManagerMode is not null)
if (Enum.TryParse(CommandLineState.OverrideMemoryManagerMode, true, out MemoryManagerMode result)) if (Enum.TryParse(CommandLineState.OverrideMemoryManagerMode, true, out MemoryManagerMode result))
{ {
ConfigurationState.Instance.System.MemoryManagerMode.Value = result; ConfigurationState.Instance.System.MemoryManagerMode.Value = result;
} }
// Check if PPTC was overridden. // Check if PPTC was overridden.
if (CommandLineState.OverridePPTC is not null) if (CommandLineState.OverridePPTC is not null)
if (Enum.TryParse(CommandLineState.OverridePPTC, true, out bool result)) if (Enum.TryParse(CommandLineState.OverridePPTC, true, out bool result))
{ {
ConfigurationState.Instance.System.EnablePtc.Value = result; ConfigurationState.Instance.System.EnablePtc.Value = result;
} }
// Check if region was overridden. // Check if region was overridden.
if (CommandLineState.OverrideSystemRegion is not null) if (CommandLineState.OverrideSystemRegion is not null)
if (Enum.TryParse(CommandLineState.OverrideSystemRegion, true, out Region result)) if (Enum.TryParse(CommandLineState.OverrideSystemRegion, true, out Region result))
{ {
ConfigurationState.Instance.System.Region.Value = result; ConfigurationState.Instance.System.Region.Value = result;
} }
//Check if language was overridden. //Check if language was overridden.
if (CommandLineState.OverrideSystemLanguage is not null) if (CommandLineState.OverrideSystemLanguage is not null)
if (Enum.TryParse(CommandLineState.OverrideSystemLanguage, true, out Language result)) if (Enum.TryParse(CommandLineState.OverrideSystemLanguage, true, out Language result))
{ {

View File

@@ -49,6 +49,9 @@
<PackageReference Include="SharpCompress" /> <PackageReference Include="SharpCompress" />
<PackageReference Include="Svg.Controls.Avalonia" /> <PackageReference Include="Svg.Controls.Avalonia" />
<PackageReference Include="Svg.Controls.Skia.Avalonia" /> <PackageReference Include="Svg.Controls.Skia.Avalonia" />
<PackageReference Include="SkiaSharp.NativeAssets.Win32" Condition="'$(RuntimeIdentifier)' != 'linux-x64' AND '$(RuntimeIdentifier)' != 'linux-arm64' AND '$(RuntimeIdentifier)' != 'osx-x64' AND '$(RuntimeIdentifier)' != 'osx-arm64'" />
<PackageReference Include="SkiaSharp.NativeAssets.macOS" Condition="'$(RuntimeIdentifier)' != 'linux-x64' AND '$(RuntimeIdentifier)' != 'linux-arm64' AND '$(RuntimeIdentifier)' != 'win-x64' AND '$(RuntimeIdentifier)' != 'win-arm64'" />
<PackageReference Include="SkiaSharp.NativeAssets.Linux.NoDependencies" Condition="'$(RuntimeIdentifier)' != 'win-x64' AND '$(RuntimeIdentifier)' != 'win-arm64' AND '$(RuntimeIdentifier)' != 'osx-x64' AND '$(RuntimeIdentifier)' != 'osx-arm64'" />
<PackageReference Include="DynamicData" /> <PackageReference Include="DynamicData" />
<PackageReference Include="FluentAvaloniaUI" /> <PackageReference Include="FluentAvaloniaUI" />
<PackageReference Include="CommandLineParser" /> <PackageReference Include="CommandLineParser" />
@@ -58,7 +61,7 @@
<PackageReference Include="Projektanker.Icons.Avalonia.FontAwesome" /> <PackageReference Include="Projektanker.Icons.Avalonia.FontAwesome" />
<PackageReference Include="Projektanker.Icons.Avalonia.MaterialDesign" /> <PackageReference Include="Projektanker.Icons.Avalonia.MaterialDesign" />
<PackageReference Include="OpenTK.Core" /> <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.Audio.OpenAL" Condition="'$(RuntimeIdentifier)' != 'linux-x64' AND '$(RuntimeIdentifier)' != 'linux-arm64' AND '$(RuntimeIdentifier)' != 'osx-x64' AND '$(RuntimeIdentifier)' != 'osx-arm64'" />
<PackageReference Include="Ryujinx.Graphics.Nvdec.Dependencies.AllArch" /> <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="Ryujinx.Graphics.Vulkan.Dependencies.MoltenVK" Condition="'$(RuntimeIdentifier)' != 'linux-x64' AND '$(RuntimeIdentifier)' != 'linux-arm64' AND '$(RuntimeIdentifier)' != 'win-x64' AND '$(RuntimeIdentifier)' != 'win-arm64'" />
<PackageReference Include="Ryujinx.UpdateClient" /> <PackageReference Include="Ryujinx.UpdateClient" />

View File

@@ -62,7 +62,7 @@ using VSyncMode = Ryujinx.Common.Configuration.VSyncMode;
namespace Ryujinx.Ava.Systems namespace Ryujinx.Ava.Systems
{ {
internal class AppHost internal class AppHost : IDisposable
{ {
private const int CursorHideIdleTime = 5; // Hide Cursor seconds. private const int CursorHideIdleTime = 5; // Hide Cursor seconds.
private const float MaxResolutionScale = 4.0f; // Max resolution hotkeys can scale to before wrapping. private const float MaxResolutionScale = 4.0f; // Max resolution hotkeys can scale to before wrapping.
@@ -438,7 +438,7 @@ namespace Ryujinx.Ava.Systems
SaveBitmapAsPng(bitmapToSave, path); SaveBitmapAsPng(bitmapToSave, path);
Logger.Notice.Print(LogClass.Application, $"Screenshot saved to {path}", "Screenshot"); Logger.Notice.Print(LogClass.Application, $"Screenshot saved to '{path}'.", "Screenshot");
} }
}); });
} }
@@ -611,27 +611,40 @@ namespace Ryujinx.Ava.Systems
_isActive = false; _isActive = false;
// NOTE: The render loop is allowed to stay alive until the renderer itself is disposed, as it may handle resource dispose.
// We only need to wait for all commands submitted during the main gpu loop to be processed.
_gpuDoneEvent.WaitOne();
_gpuDoneEvent.Dispose();
DisplaySleep.Restore(); DisplaySleep.Restore();
NpadManager.Dispose(); NpadManager.Dispose();
TouchScreenManager.Dispose(); TouchScreenManager.Dispose();
Device.Dispose(); Device.Dispose();
// NOTE: The render loop is allowed to stay alive until the renderer itself is disposed, as it may handle resource dispose.
// We only need to wait for all commands submitted during the main gpu loop to be processed.
// If the GPU has no work and is cancelled, we need to handle that as well.
WaitHandle.WaitAny(new[] { _gpuDoneEvent, _gpuCancellationTokenSource.Token.WaitHandle });
if (_renderingStarted)
{
// Waiting for work to be finished before we dispose.
Device.Gpu.WaitUntilGpuReady();
}
_gpuDoneEvent.Dispose();
_gpuCancellationTokenSource.Dispose();
DisposeGpu(); DisposeGpu();
AppExit?.Invoke(this, EventArgs.Empty); AppExit?.Invoke(this, EventArgs.Empty);
} }
private void Dispose() // MUST be public to inherit from IDisposable
public void Dispose()
{ {
if (Device.Processes != null) if (Device.Processes != null)
MainWindowViewModel.UpdateGameMetadata(Device.Processes.ActiveApplication.ProgramIdText, _playTimer.Elapsed); {
MainWindowViewModel.UpdateGameMetadata(Device.Processes.ActiveApplication?.ProgramIdText,
_playTimer.Elapsed);
}
ConfigurationState.Instance.System.IgnoreMissingServices.Event -= UpdateIgnoreMissingServicesState; ConfigurationState.Instance.System.IgnoreMissingServices.Event -= UpdateIgnoreMissingServicesState;
ConfigurationState.Instance.Graphics.AspectRatio.Event -= UpdateAspectRatioState; ConfigurationState.Instance.Graphics.AspectRatio.Event -= UpdateAspectRatioState;
ConfigurationState.Instance.System.EnableDockedMode.Event -= UpdateDockedModeState; ConfigurationState.Instance.System.EnableDockedMode.Event -= UpdateDockedModeState;
@@ -646,7 +659,6 @@ namespace Ryujinx.Ava.Systems
_topLevel.PointerExited -= TopLevel_PointerExited; _topLevel.PointerExited -= TopLevel_PointerExited;
_gpuCancellationTokenSource.Cancel(); _gpuCancellationTokenSource.Cancel();
_gpuCancellationTokenSource.Dispose();
_chrono.Stop(); _chrono.Stop();
_playTimer.Stop(); _playTimer.Stop();
@@ -672,6 +684,12 @@ namespace Ryujinx.Ava.Systems
} }
else else
{ {
// No use waiting on something that never started work
if (_renderingStarted)
{
Device.Gpu.WaitUntilGpuReady();
}
Device.DisposeGpu(); Device.DisposeGpu();
} }
} }
@@ -686,7 +704,7 @@ namespace Ryujinx.Ava.Systems
_cursorState = CursorStates.ForceChangeCursor; _cursorState = CursorStates.ForceChangeCursor;
} }
public async Task<bool> LoadGuestApplication(BlitStruct<ApplicationControlProperty>? customNacpData = null) public async Task LoadGuestApplication(CancellationTokenSource cts, BlitStruct<ApplicationControlProperty>? customNacpData = null)
{ {
DiscordIntegrationModule.GuestAppStartedAt = Timestamps.Now; DiscordIntegrationModule.GuestAppStartedAt = Timestamps.Now;
@@ -715,7 +733,8 @@ namespace Ryujinx.Ava.Systems
await UserErrorDialog.ShowUserErrorDialog(userError); await UserErrorDialog.ShowUserErrorDialog(userError);
Device.Dispose(); Device.Dispose();
return false; cts.Cancel();
throw new OperationCanceledException(cts.Token);
} }
} }
@@ -724,10 +743,11 @@ namespace Ryujinx.Ava.Systems
await UserErrorDialog.ShowUserErrorDialog(userError); await UserErrorDialog.ShowUserErrorDialog(userError);
Device.Dispose(); Device.Dispose();
return false; cts.Cancel();
throw new OperationCanceledException(cts.Token);
} }
// Tell the user that we installed a firmware for them. // Tell the user that we installed firmware for them.
if (userError is UserError.NoFirmware) if (userError is UserError.NoFirmware)
{ {
firmwareVersion = ContentManager.GetCurrentFirmwareVersion(); firmwareVersion = ContentManager.GetCurrentFirmwareVersion();
@@ -747,7 +767,8 @@ namespace Ryujinx.Ava.Systems
await UserErrorDialog.ShowUserErrorDialog(userError); await UserErrorDialog.ShowUserErrorDialog(userError);
Device.Dispose(); Device.Dispose();
return false; cts.Cancel();
throw new OperationCanceledException(cts.Token);
} }
} }
} }
@@ -762,7 +783,8 @@ namespace Ryujinx.Ava.Systems
{ {
Device.Dispose(); Device.Dispose();
return false; cts.Cancel();
throw new OperationCanceledException(cts.Token);
} }
} }
else if (Directory.Exists(ApplicationPath)) else if (Directory.Exists(ApplicationPath))
@@ -782,20 +804,24 @@ namespace Ryujinx.Ava.Systems
if (!Device.LoadCart(ApplicationPath, romFsFiles[0])) if (!Device.LoadCart(ApplicationPath, romFsFiles[0]))
{ {
await ContentDialogHelper.CreateErrorDialog(
"Please specify an unpacked game directory with a valid exefs or NSO/NRO.");
Device.Dispose(); Device.Dispose();
return false; cts.Cancel();
throw new OperationCanceledException(cts.Token);
} }
} }
else else
{ {
Logger.Info?.Print(LogClass.Application, "Loading as cart WITHOUT RomFS."); Logger.Info?.Print(LogClass.Application, "Loading as cart WITHOUT RomFS.");
if (!Device.LoadCart(ApplicationPath)) if (!Device.LoadCart(ApplicationPath))
{ {
await ContentDialogHelper.CreateErrorDialog(
"Please specify an unpacked game directory with a valid exefs or NSO/NRO.");
Device.Dispose(); Device.Dispose();
cts.Cancel();
return false; throw new OperationCanceledException(cts.Token);
} }
} }
} }
@@ -813,7 +839,8 @@ namespace Ryujinx.Ava.Systems
{ {
Device.Dispose(); Device.Dispose();
return false; cts.Cancel();
throw new OperationCanceledException(cts.Token);
} }
break; break;
@@ -826,7 +853,8 @@ namespace Ryujinx.Ava.Systems
{ {
Device.Dispose(); Device.Dispose();
return false; cts.Cancel();
throw new OperationCanceledException(cts.Token);
} }
break; break;
@@ -840,7 +868,8 @@ namespace Ryujinx.Ava.Systems
{ {
Device.Dispose(); Device.Dispose();
return false; cts.Cancel();
throw new OperationCanceledException(cts.Token);
} }
break; break;
@@ -855,7 +884,8 @@ namespace Ryujinx.Ava.Systems
{ {
Device.Dispose(); Device.Dispose();
return false; cts.Cancel();
throw new OperationCanceledException(cts.Token);
} }
} }
catch (ArgumentOutOfRangeException) catch (ArgumentOutOfRangeException)
@@ -864,7 +894,8 @@ namespace Ryujinx.Ava.Systems
Device.Dispose(); Device.Dispose();
return false; cts.Cancel();
throw new OperationCanceledException(cts.Token);
} }
break; break;
@@ -873,19 +904,18 @@ namespace Ryujinx.Ava.Systems
} }
else else
{ {
Logger.Warning?.Print(LogClass.Application, "Please specify a valid XCI/NCA/NSP/PFS0/NRO file."); Logger.Warning?.Print(LogClass.Application, "Please specify a valid XCI/NCA/NSP/PFS0/NSO/NRO file.");
Device.Dispose(); Device.Dispose();
return false; cts.Cancel();
throw new OperationCanceledException(cts.Token);
} }
ApplicationLibrary.LoadAndSaveMetaData(Device.Processes.ActiveApplication.ProgramIdText, ApplicationLibrary.LoadAndSaveMetaData(Device.Processes.ActiveApplication.ProgramIdText,
appMetadata => appMetadata.UpdatePreGame() appMetadata => appMetadata.UpdatePreGame()
); );
_playTimer.Start(); _playTimer.Start();
return true;
} }
internal void Resume() internal void Resume()
@@ -895,7 +925,7 @@ namespace Ryujinx.Ava.Systems
_viewModel.IsPaused = false; _viewModel.IsPaused = false;
_playTimer.Start(); _playTimer.Start();
_viewModel.Title = TitleHelper.ActiveApplicationTitle(Device?.Processes.ActiveApplication, Program.Version, !ConfigurationState.Instance.ShowOldUI); _viewModel.Title = TitleHelper.ActiveApplicationTitle(Device?.Processes.ActiveApplication, Program.Version, !ConfigurationState.Instance.ShowOldUI);
Logger.Info?.Print(LogClass.Emulation, "Emulation was resumed"); Logger.Info?.Print(LogClass.Emulation, "Emulation was resumed.");
} }
internal void Pause() internal void Pause()
@@ -905,7 +935,7 @@ namespace Ryujinx.Ava.Systems
_viewModel.IsPaused = true; _viewModel.IsPaused = true;
_playTimer.Stop(); _playTimer.Stop();
_viewModel.Title = TitleHelper.ActiveApplicationTitle(Device?.Processes.ActiveApplication, Program.Version, !ConfigurationState.Instance.ShowOldUI, LocaleManager.Instance[LocaleKeys.Paused]); _viewModel.Title = TitleHelper.ActiveApplicationTitle(Device?.Processes.ActiveApplication, Program.Version, !ConfigurationState.Instance.ShowOldUI, LocaleManager.Instance[LocaleKeys.Paused]);
Logger.Info?.Print(LogClass.Emulation, "Emulation was paused"); Logger.Info?.Print(LogClass.Emulation, "Emulation was paused.");
} }
private void InitEmulatedSwitch() private void InitEmulatedSwitch()
@@ -1065,49 +1095,56 @@ namespace Ryujinx.Ava.Systems
Device.Gpu.Renderer.RunLoop(() => Device.Gpu.Renderer.RunLoop(() =>
{ {
Device.Gpu.SetGpuThread(); try
Device.Gpu.InitializeShaderCache(_gpuCancellationTokenSource.Token);
_renderer.Window.ChangeVSyncMode(Device.VSyncMode);
while (_isActive)
{ {
_ticks += _chrono.ElapsedTicks; Device.Gpu.SetGpuThread();
Device.Gpu.InitializeShaderCache(_gpuCancellationTokenSource.Token);
_chrono.Restart(); _renderer.Window.ChangeVSyncMode(Device.VSyncMode);
if (Device.WaitFifo()) while (_isActive)
{ {
Device.Statistics.RecordFifoStart(); _ticks += _chrono.ElapsedTicks;
Device.ProcessFrame();
Device.Statistics.RecordFifoEnd();
}
while (Device.ConsumeFrameAvailable()) _chrono.Restart();
{
if (!_renderingStarted) if (Device.WaitFifo())
{ {
_renderingStarted = true; Device.Statistics.RecordFifoStart();
_viewModel.SwitchToRenderer(false); Device.ProcessFrame();
InitStatus(); Device.Statistics.RecordFifoEnd();
} }
Device.PresentFrame(() => (RendererHost.EmbeddedWindow as EmbeddedWindowOpenGL)?.SwapBuffers()); while (Device.ConsumeFrameAvailable())
} {
if (!_renderingStarted)
{
_renderingStarted = true;
_viewModel.SwitchToRenderer(false);
InitStatus();
}
if (_ticks >= _ticksPerFrame) Device.PresentFrame(() =>
{ (RendererHost.EmbeddedWindow as EmbeddedWindowOpenGL)?.SwapBuffers());
UpdateStatus(); }
if (_ticks >= _ticksPerFrame)
{
UpdateStatus();
}
} }
} }
finally
// Make sure all commands in the run loop are fully executed before leaving the loop.
if (Device.Gpu.Renderer is ThreadedRenderer threaded)
{ {
threaded.FlushThreadedCommands(); // Make sure all commands in the run loop are fully executed before leaving the loop.
if (Device.Gpu.Renderer is ThreadedRenderer threaded)
{
Logger.Info?.PrintMsg(LogClass.Gpu, "Flushing threaded commands...");
threaded.FlushThreadedCommands();
Logger.Info?.PrintMsg(LogClass.Gpu, "Flushed!");
}
_gpuDoneEvent.Set();
} }
_gpuDoneEvent.Set();
}); });
(RendererHost.EmbeddedWindow as EmbeddedWindowOpenGL)?.MakeCurrent(true); (RendererHost.EmbeddedWindow as EmbeddedWindowOpenGL)?.MakeCurrent(true);

View File

@@ -849,7 +849,8 @@ namespace Ryujinx.Ava.Systems.AppLibrary
foreach (ApplicationData installedApplication in Applications.Items) foreach (ApplicationData installedApplication in Applications.Items)
{ {
temporary += LoadAndSaveMetaData(installedApplication.IdString).TimePlayed; // this should always exist... should...
temporary += LoadAndSaveMetaData(installedApplication.IdString).Value.TimePlayed;
} }
TotalTimePlayed = temporary; TotalTimePlayed = temporary;
@@ -1159,15 +1160,22 @@ namespace Ryujinx.Ava.Systems.AppLibrary
ApplicationCountUpdated?.Invoke(null, e); ApplicationCountUpdated?.Invoke(null, e);
} }
public static ApplicationMetadata LoadAndSaveMetaData(string titleId, Action<ApplicationMetadata> modifyFunction = null) public static Gommon.Optional<ApplicationMetadata> LoadAndSaveMetaData(string titleId, Action<ApplicationMetadata> modifyFunction = null)
{ {
if (titleId is null)
{
Logger.Warning?.PrintMsg(LogClass.Application, "Cannot save metadata because title ID is invalid.");
return null;
}
string metadataFolder = Path.Combine(AppDataManager.GamesDirPath, titleId, "gui"); string metadataFolder = Path.Combine(AppDataManager.GamesDirPath, titleId, "gui");
string metadataFile = Path.Combine(metadataFolder, "metadata.json"); string metadataFile = Path.Combine(metadataFolder, "metadata.json");
ApplicationMetadata appMetadata; ApplicationMetadata appMetadata;
if (!File.Exists(metadataFile)) if (!File.Exists(metadataFile))
{ {
Logger.Info?.Print(LogClass.Application, $"Metadata file does not exist. Creating metadata for {titleId}...");
Directory.CreateDirectory(metadataFolder); Directory.CreateDirectory(metadataFolder);
appMetadata = new ApplicationMetadata(); appMetadata = new ApplicationMetadata();
@@ -1177,12 +1185,12 @@ namespace Ryujinx.Ava.Systems.AppLibrary
try try
{ {
Logger.Debug?.Print(LogClass.Application, $"Deserializing metadata for {titleId}...");
appMetadata = JsonHelper.DeserializeFromFile(metadataFile, _serializerContext.ApplicationMetadata); appMetadata = JsonHelper.DeserializeFromFile(metadataFile, _serializerContext.ApplicationMetadata);
} }
catch (JsonException) catch (JsonException)
{ {
Logger.Warning?.Print(LogClass.Application, $"Failed to parse metadata json for {titleId}. Loading defaults."); Logger.Warning?.Print(LogClass.Application, $"Failed to parse metadata json for {titleId}. Loading defaults.");
appMetadata = new ApplicationMetadata(); appMetadata = new ApplicationMetadata();
} }

View File

@@ -17,7 +17,7 @@ namespace Ryujinx.Ava.Systems.Configuration
/// <summary> /// <summary>
/// The current version of the file format /// The current version of the file format
/// </summary> /// </summary>
public const int CurrentVersion = 71; public const int CurrentVersion = 72;
/// <summary> /// <summary>
/// Version of the configuration file format /// Version of the configuration file format
@@ -113,6 +113,11 @@ namespace Ryujinx.Ava.Systems.Configuration
/// Enables printing FS access log messages /// Enables printing FS access log messages
/// </summary> /// </summary>
public bool LoggingEnableFsAccessLog { get; set; } public bool LoggingEnableFsAccessLog { get; set; }
/// <summary>
/// Enables printing network log messages
/// </summary>
public bool LoggingEnableNetLog { get; set; }
/// <summary> /// <summary>
/// Enables log messages from Avalonia /// Enables log messages from Avalonia

View File

@@ -1,4 +1,4 @@
using Avalonia.Media; using Avalonia.Media;
using Gommon; using Gommon;
using Ryujinx.Ava.Systems.Configuration.System; using Ryujinx.Ava.Systems.Configuration.System;
using Ryujinx.Ava.Systems.Configuration.UI; using Ryujinx.Ava.Systems.Configuration.UI;
@@ -68,6 +68,7 @@ namespace Ryujinx.Ava.Systems.Configuration
Logger.EnableTrace.Value = cff.LoggingEnableTrace; Logger.EnableTrace.Value = cff.LoggingEnableTrace;
Logger.EnableGuest.Value = cff.LoggingEnableGuest; Logger.EnableGuest.Value = cff.LoggingEnableGuest;
Logger.EnableFsAccessLog.Value = cff.LoggingEnableFsAccessLog; Logger.EnableFsAccessLog.Value = cff.LoggingEnableFsAccessLog;
Logger.EnableNetLog.Value = cff.LoggingEnableNetLog;
Logger.FilteredClasses.Value = cff.LoggingFilteredClasses; Logger.FilteredClasses.Value = cff.LoggingFilteredClasses;
Logger.GraphicsDebugLevel.Value = cff.LoggingGraphicsDebugLevel; Logger.GraphicsDebugLevel.Value = cff.LoggingGraphicsDebugLevel;

View File

@@ -257,6 +257,11 @@ namespace Ryujinx.Ava.Systems.Configuration
/// Enables printing FS access log messages /// Enables printing FS access log messages
/// </summary> /// </summary>
public ReactiveObject<bool> EnableFsAccessLog { get; private set; } public ReactiveObject<bool> EnableFsAccessLog { get; private set; }
/// <summary>
/// Enables printing network log messages
/// </summary>
public ReactiveObject<bool> EnableNetLog { get; private set; }
/// <summary> /// <summary>
/// Enables log messages from Avalonia /// Enables log messages from Avalonia
@@ -289,6 +294,7 @@ namespace Ryujinx.Ava.Systems.Configuration
EnableTrace = new ReactiveObject<bool>(); EnableTrace = new ReactiveObject<bool>();
EnableGuest = new ReactiveObject<bool>(); EnableGuest = new ReactiveObject<bool>();
EnableFsAccessLog = new ReactiveObject<bool>(); EnableFsAccessLog = new ReactiveObject<bool>();
EnableNetLog = new ReactiveObject<bool>();
EnableAvaloniaLog = new ReactiveObject<bool>(); EnableAvaloniaLog = new ReactiveObject<bool>();
FilteredClasses = new ReactiveObject<LogClass[]>(); FilteredClasses = new ReactiveObject<LogClass[]>();
EnableFileLog = new ReactiveObject<bool>(); EnableFileLog = new ReactiveObject<bool>();

View File

@@ -47,6 +47,7 @@ namespace Ryujinx.Ava.Systems.Configuration
LoggingEnableTrace = Logger.EnableTrace, LoggingEnableTrace = Logger.EnableTrace,
LoggingEnableGuest = Logger.EnableGuest, LoggingEnableGuest = Logger.EnableGuest,
LoggingEnableFsAccessLog = Logger.EnableFsAccessLog, LoggingEnableFsAccessLog = Logger.EnableFsAccessLog,
LoggingEnableNetLog = Logger.EnableNetLog,
LoggingEnableAvalonia = Logger.EnableAvaloniaLog, LoggingEnableAvalonia = Logger.EnableAvaloniaLog,
LoggingFilteredClasses = Logger.FilteredClasses, LoggingFilteredClasses = Logger.FilteredClasses,
LoggingGraphicsDebugLevel = Logger.GraphicsDebugLevel, LoggingGraphicsDebugLevel = Logger.GraphicsDebugLevel,
@@ -176,6 +177,7 @@ namespace Ryujinx.Ava.Systems.Configuration
Logger.EnableTrace.Value = false; Logger.EnableTrace.Value = false;
Logger.EnableGuest.Value = true; Logger.EnableGuest.Value = true;
Logger.EnableFsAccessLog.Value = false; Logger.EnableFsAccessLog.Value = false;
Logger.EnableNetLog.Value = false;
Logger.EnableAvaloniaLog.Value = false; Logger.EnableAvaloniaLog.Value = false;
Logger.FilteredClasses.Value = []; Logger.FilteredClasses.Value = [];
Logger.GraphicsDebugLevel.Value = GraphicsDebugLevel.None; Logger.GraphicsDebugLevel.Value = GraphicsDebugLevel.None;

View File

@@ -26,6 +26,8 @@ namespace Ryujinx.Ava.Systems.Configuration
(_, e) => Logger.SetEnable(LogLevel.Guest, e.NewValue); (_, e) => Logger.SetEnable(LogLevel.Guest, e.NewValue);
ConfigurationState.Instance.Logger.EnableFsAccessLog.Event += ConfigurationState.Instance.Logger.EnableFsAccessLog.Event +=
(_, e) => Logger.SetEnable(LogLevel.AccessLog, e.NewValue); (_, e) => Logger.SetEnable(LogLevel.AccessLog, e.NewValue);
ConfigurationState.Instance.Logger.EnableNetLog.Event +=
(_, e) => Logger.SetEnable(LogLevel.NetLog, e.NewValue);
ConfigurationState.Instance.Logger.FilteredClasses.Event += (_, e) => ConfigurationState.Instance.Logger.FilteredClasses.Event += (_, e) =>
{ {

View File

@@ -82,7 +82,7 @@ namespace Ryujinx.Ava.Systems
public static void Use(Optional<string> titleId) public static void Use(Optional<string> titleId)
{ {
if (titleId.TryGet(out string tid)) if (titleId.TryGet(out string tid) && Switch.Shared.Processes.ActiveApplication is not null)
SwitchToPlayingState( SwitchToPlayingState(
ApplicationLibrary.LoadAndSaveMetaData(tid), ApplicationLibrary.LoadAndSaveMetaData(tid),
Switch.Shared.Processes.ActiveApplication Switch.Shared.Processes.ActiveApplication

View File

@@ -8,6 +8,12 @@ namespace Ryujinx.Ava.UI.Helpers
internal partial class Win32NativeInterop internal partial class Win32NativeInterop
{ {
internal const int GWLP_WNDPROC = -4; internal const int GWLP_WNDPROC = -4;
internal const int GWL_STYLE = -16;
internal const int GWL_EXSTYLE = -20;
internal const uint WS_OVERLAPPEDWINDOW = 0x00CF0000;
internal const uint WS_POPUP = 0x80000000;
internal const uint WS_VISIBLE = 0x10000000;
[Flags] [Flags]
public enum ClassStyles : uint public enum ClassStyles : uint
@@ -107,9 +113,29 @@ namespace Ryujinx.Ava.UI.Helpers
nint hInstance, nint hInstance,
nint lpParam); nint lpParam);
[LibraryImport("user32.dll", SetLastError = true, EntryPoint = "GetWindowLongPtrW")]
public static partial nint GetWindowLongPtrW(nint hWnd, int nIndex);
[LibraryImport("user32.dll", SetLastError = true)] [LibraryImport("user32.dll", SetLastError = true)]
public static partial nint SetWindowLongPtrW(nint hWnd, int nIndex, nint value); public static partial nint SetWindowLongPtrW(nint hWnd, int nIndex, nint value);
[LibraryImport("user32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static partial bool SetWindowPos(
nint hWnd,
nint hWndInsertAfter,
int x,
int y,
int cx,
int cy,
uint uFlags);
internal const uint SWP_NOZORDER = 0x0004;
internal const uint SWP_NOACTIVATE = 0x0010;
internal const uint SWP_FRAMECHANGED = 0x0020;
internal const uint SWP_NOMOVE = 0x0002;
internal const uint SWP_NOSIZE = 0x0001;
[LibraryImport("user32.dll", SetLastError = true)] [LibraryImport("user32.dll", SetLastError = true)]
public static partial ushort GetAsyncKeyState(int nVirtKey); public static partial ushort GetAsyncKeyState(int nVirtKey);

View File

@@ -57,8 +57,15 @@ namespace Ryujinx.Ava.UI.Models
} }
else else
{ {
ApplicationMetadata appMetadata = ApplicationLibrary.LoadAndSaveMetaData(TitleIdString); Gommon.Optional<ApplicationMetadata> appMetadata = ApplicationLibrary.LoadAndSaveMetaData(TitleIdString);
Title = appMetadata.Title ?? TitleIdString; if (appMetadata != null)
{
Title = appMetadata.Value.Title ?? TitleIdString;
}
else
{
Title = "<INVALID>";
}
} }
Task.Run(() => Task.Run(() =>

View File

@@ -574,7 +574,7 @@ namespace Ryujinx.Ava.UI.ViewModels.Input
if (Devices.Any(controller => controller.Name == name)) if (Devices.Any(controller => controller.Name == name))
{ {
controllerNumber++; controllerNumber++;
name = GetGamepadName(gamepad, controllerNumber); name = GetUniqueGamepadName(gamepad, ref controllerNumber);
} }
return name; return name;

View File

@@ -5,6 +5,7 @@ using Avalonia.Input;
using Avalonia.Media; using Avalonia.Media;
using Avalonia.Platform.Storage; using Avalonia.Platform.Storage;
using Avalonia.Threading; using Avalonia.Threading;
using System.Runtime.Versioning;
using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input; using CommunityToolkit.Mvvm.Input;
using DynamicData; using DynamicData;
@@ -656,10 +657,19 @@ namespace Ryujinx.Ava.UI.ViewModels
get => ConfigurationState.Instance.UI.ShowConsole; get => ConfigurationState.Instance.UI.ShowConsole;
set set
{ {
bool restartRequired = value && !ConsoleHelper.HasConsoleWindow;
ConfigurationState.Instance.UI.ShowConsole.Value = value; ConfigurationState.Instance.UI.ShowConsole.Value = value;
ConfigurationState.Instance.ToFileFormat().SaveConfig(Program.ConfigurationPath); ConfigurationState.Instance.ToFileFormat().SaveConfig(Program.ConfigurationPath);
if (restartRequired)
{
NotificationHelper.ShowInformation(
LocaleManager.Instance[LocaleKeys.SettingsAppRequiredRestartMessage],
LocaleManager.Instance[LocaleKeys.SettingsShowConsoleRestartMessage]);
}
OnPropertyChanged(); OnPropertyChanged();
} }
} }
@@ -1760,11 +1770,6 @@ namespace Ryujinx.Ava.UI.ViewModels
Logger.RestartTime(); Logger.RestartTime();
SelectedIcon ??= ApplicationLibrary.GetApplicationIcon(application.Path,
ConfigurationState.Instance.System.Language, application.Id);
PrepareLoadScreen();
RendererHostControl = new RendererHost(); RendererHostControl = new RendererHost();
AppHost = new AppHost( AppHost = new AppHost(
@@ -1778,18 +1783,34 @@ namespace Ryujinx.Ava.UI.ViewModels
UserChannelPersistence, UserChannelPersistence,
this, this,
TopLevel); TopLevel);
CancellationTokenSource cts = new CancellationTokenSource();
if (!await AppHost.LoadGuestApplication(customNacpData)) try
{ {
await AppHost.LoadGuestApplication(cts, customNacpData);
}
catch (OperationCanceledException exception)
{
Logger.Info?.Print(LogClass.Application,
"LoadGuestApplication was interrupted !!! " + exception.Message);
AppHost.DisposeContext(); AppHost.DisposeContext();
AppHost = null; AppHost = null;
return; return;
} }
finally
{
cts.Dispose();
}
CanUpdate = false; CanUpdate = false;
application.Name ??= AppHost.Device.Processes.ActiveApplication.Name; application.Name ??= AppHost.Device.Processes.ActiveApplication.Name;
SelectedIcon ??= ApplicationLibrary.GetApplicationIcon(application.Path,
ConfigurationState.Instance.System.Language, application.Id);
PrepareLoadScreen();
LoadHeading = LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.LoadingHeading, application.Name); LoadHeading = LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.LoadingHeading, application.Name);
@@ -1811,9 +1832,9 @@ namespace Ryujinx.Ava.UI.ViewModels
RendererHostControl.Focus(); RendererHostControl.Focus();
}); });
public static void UpdateGameMetadata(string titleId, TimeSpan playTime) public static void UpdateGameMetadata(string titleId, TimeSpan playTime)
=> ApplicationLibrary.LoadAndSaveMetaData(titleId, appMetadata => appMetadata.UpdatePostGame(playTime)); => ApplicationLibrary.LoadAndSaveMetaData(titleId, appMetadata => appMetadata.UpdatePostGame(playTime));
public void RefreshFirmwareStatus() public void RefreshFirmwareStatus()
{ {
SystemVersion version = null; SystemVersion version = null;
@@ -1994,7 +2015,7 @@ namespace Ryujinx.Ava.UI.ViewModels
LastFullscreenToggle = Environment.TickCount64; LastFullscreenToggle = Environment.TickCount64;
if (WindowState is not WindowState.Normal) if (WindowState is WindowState.FullScreen)
{ {
WindowState = WindowState.Normal; WindowState = WindowState.Normal;
Window.TitleBar.ExtendsContentIntoTitleBar = !ConfigurationState.Instance.ShowOldUI; Window.TitleBar.ExtendsContentIntoTitleBar = !ConfigurationState.Instance.ShowOldUI;
@@ -2003,21 +2024,74 @@ namespace Ryujinx.Ava.UI.ViewModels
{ {
ShowMenuAndStatusBar = true; ShowMenuAndStatusBar = true;
} }
if (OperatingSystem.IsWindows())
{
RestoreWindowFromFullscreen();
}
} }
else else
{ {
WindowState = WindowState.FullScreen;
Window.TitleBar.ExtendsContentIntoTitleBar = true; Window.TitleBar.ExtendsContentIntoTitleBar = true;
if (IsGameRunning) if (IsGameRunning)
{ {
ShowMenuAndStatusBar = false; ShowMenuAndStatusBar = false;
} }
if (OperatingSystem.IsWindows())
{
MakeWindowFullscreen();
}
else
{
WindowState = WindowState.FullScreen;
}
} }
IsFullScreen = WindowState is WindowState.FullScreen; IsFullScreen = WindowState is WindowState.FullScreen;
} }
private nint _savedWindowStyle;
[SupportedOSPlatform("windows")]
private void MakeWindowFullscreen()
{
nint hwnd = Window.TryGetPlatformHandle()?.Handle ?? nint.Zero;
if (hwnd == nint.Zero) return;
// Save current style and placement
_savedWindowStyle = Win32NativeInterop.GetWindowLongPtrW(hwnd, Win32NativeInterop.GWL_STYLE);
// Remove window chrome: WS_OVERLAPPEDWINDOW -> WS_POPUP | WS_VISIBLE
Win32NativeInterop.SetWindowLongPtrW(hwnd, Win32NativeInterop.GWL_STYLE,
unchecked((nint)(Win32NativeInterop.WS_POPUP | Win32NativeInterop.WS_VISIBLE)));
Avalonia.Platform.Screen? screen = Window.Screens.ScreenFromVisual(Window);
int w = screen?.Bounds.Width ?? 0;
int h = screen?.Bounds.Height ?? 0;
Win32NativeInterop.SetWindowPos(hwnd, nint.Zero, 0, 0, w, h,
Win32NativeInterop.SWP_NOZORDER | Win32NativeInterop.SWP_NOACTIVATE | Win32NativeInterop.SWP_FRAMECHANGED);
WindowState = WindowState.FullScreen;
}
[SupportedOSPlatform("windows")]
private void RestoreWindowFromFullscreen()
{
nint hwnd = Window.TryGetPlatformHandle()?.Handle ?? nint.Zero;
if (hwnd == nint.Zero) return;
// Restore original window style
Win32NativeInterop.SetWindowLongPtrW(hwnd, Win32NativeInterop.GWL_STYLE, _savedWindowStyle);
Win32NativeInterop.SetWindowPos(hwnd, nint.Zero, 0, 0, 0, 0,
Win32NativeInterop.SWP_NOZORDER | Win32NativeInterop.SWP_NOACTIVATE |
Win32NativeInterop.SWP_FRAMECHANGED | Win32NativeInterop.SWP_NOMOVE | Win32NativeInterop.SWP_NOSIZE);
}
public static void SaveConfig() public static void SaveConfig()
{ {
ConfigurationState.Instance.ToFileFormat().SaveConfig(Program.ConfigurationPath); ConfigurationState.Instance.ToFileFormat().SaveConfig(Program.ConfigurationPath);

View File

@@ -273,6 +273,7 @@ namespace Ryujinx.Ava.UI.ViewModels
public bool EnableTrace { get; set; } public bool EnableTrace { get; set; }
public bool EnableGuest { get; set; } public bool EnableGuest { get; set; }
public bool EnableFsAccessLog { get; set; } public bool EnableFsAccessLog { get; set; }
public bool EnableNetLog { get; set; }
public bool EnableAvaloniaLog { get; set; } public bool EnableAvaloniaLog { get; set; }
public bool EnableDebug { get; set; } public bool EnableDebug { get; set; }
public bool IsOpenAlEnabled { get; set; } public bool IsOpenAlEnabled { get; set; }
@@ -725,6 +726,7 @@ namespace Ryujinx.Ava.UI.ViewModels
EnableGuest = config.Logger.EnableGuest; EnableGuest = config.Logger.EnableGuest;
EnableDebug = config.Logger.EnableDebug; EnableDebug = config.Logger.EnableDebug;
EnableFsAccessLog = config.Logger.EnableFsAccessLog; EnableFsAccessLog = config.Logger.EnableFsAccessLog;
EnableNetLog = config.Logger.EnableNetLog;
EnableAvaloniaLog = config.Logger.EnableAvaloniaLog; EnableAvaloniaLog = config.Logger.EnableAvaloniaLog;
FsGlobalAccessLogMode = config.System.FsGlobalAccessLogMode; FsGlobalAccessLogMode = config.System.FsGlobalAccessLogMode;
OpenglDebugLevel = (int)config.Logger.GraphicsDebugLevel.Value; OpenglDebugLevel = (int)config.Logger.GraphicsDebugLevel.Value;
@@ -848,6 +850,7 @@ namespace Ryujinx.Ava.UI.ViewModels
config.Logger.EnableGuest.Value = EnableGuest; config.Logger.EnableGuest.Value = EnableGuest;
config.Logger.EnableDebug.Value = EnableDebug; config.Logger.EnableDebug.Value = EnableDebug;
config.Logger.EnableFsAccessLog.Value = EnableFsAccessLog; config.Logger.EnableFsAccessLog.Value = EnableFsAccessLog;
config.Logger.EnableNetLog.Value = EnableNetLog;
config.Logger.EnableAvaloniaLog.Value = EnableAvaloniaLog; config.Logger.EnableAvaloniaLog.Value = EnableAvaloniaLog;
config.System.FsGlobalAccessLogMode.Value = FsGlobalAccessLogMode; config.System.FsGlobalAccessLogMode.Value = FsGlobalAccessLogMode;
config.Logger.GraphicsDebugLevel.Value = (GraphicsDebugLevel)OpenglDebugLevel; config.Logger.GraphicsDebugLevel.Value = (GraphicsDebugLevel)OpenglDebugLevel;

View File

@@ -70,6 +70,10 @@
ToolTip.Tip="{ext:Locale FileAccessLogTooltip}"> ToolTip.Tip="{ext:Locale FileAccessLogTooltip}">
<TextBlock Text="{ext:Locale SettingsTabLoggingEnableFsAccessLogs}" /> <TextBlock Text="{ext:Locale SettingsTabLoggingEnableFsAccessLogs}" />
</CheckBox> </CheckBox>
<CheckBox IsChecked="{Binding EnableNetLog}"
ToolTip.Tip="{ext:Locale NetLogTooltip}">
<TextBlock Text="{ext:Locale SettingsTabLoggingEnableNetLogs}" />
</CheckBox>
<CheckBox IsChecked="{Binding EnableDebug}" <CheckBox IsChecked="{Binding EnableDebug}"
ToolTip.Tip="{ext:Locale DebugLogTooltip}"> ToolTip.Tip="{ext:Locale DebugLogTooltip}">
<TextBlock Text="{ext:Locale SettingsTabLoggingEnableDebugLogs}" /> <TextBlock Text="{ext:Locale SettingsTabLoggingEnableDebugLogs}" />