mirror of
https://git.eden-emu.dev/eden-emu/eden.git
synced 2026-10-29 02:41:51 +00:00
Compare commits
30 Commits
10bcd2d849
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
| 26a03d2473 | |||
| b7c1ded159 | |||
| cbd4cf2d8c | |||
| a3debcf731 | |||
| 5239cbe2a7 | |||
| 0e11fe8b5e | |||
| 0689f73147 | |||
| a95efaf18f | |||
| 58968bd127 | |||
| 05f36d990c | |||
| eb88a40a8e | |||
| 305f40100d | |||
| 989768aa46 | |||
| de7e6e01d4 | |||
| e0d47f2790 | |||
| d8c6c59b67 | |||
| aaa0bcb9bf | |||
| e255c20db4 | |||
| 14da4482a4 | |||
| fb51a12534 | |||
| a594c8d60e | |||
| 6b3cb5772b | |||
| 218c6705d9 | |||
| 4dc69c06f7 | |||
| 9580e54f2f | |||
| 6352612425 | |||
| fecc1b4ee3 | |||
| c746982201 | |||
| 67bada77f8 | |||
| 3cdd2ce141 |
@@ -1,5 +1,5 @@
|
|||||||
- [ ] I have read and followed the [Contribution Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/CONTRIBUTING.md#code-contributions).
|
- [ ] I have read and followed the [Contribution Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/CONTRIBUTING.md#code-contributions).
|
||||||
- [ ] I have read and followed the [AI Policy](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/AI.md)
|
- [ ] I have read and followed the [AI Policy](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/AI.md).
|
||||||
- [ ] I have read and followed the [Coding Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/Coding.md) to the best of my ability.
|
- [ ] I have read and followed the [Coding Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/Coding.md) to the best of my ability.
|
||||||
|
|
||||||
-------------------
|
-------------------
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
# Build directory
|
# Build directory
|
||||||
/[Bb]uild*/
|
/[Bb]uild*/
|
||||||
doc-build/
|
doc-build/
|
||||||
|
out/
|
||||||
AppDir/
|
AppDir/
|
||||||
uruntime
|
uruntime
|
||||||
|
|
||||||
|
|||||||
@@ -1 +1,7 @@
|
|||||||
AI and LLM use is *strictly* prohibited within our codebase and surrounding community, including issues and comments. This includes using AI or LLMs to write docs/commit messages, debug issues, brainstorm ideas, research concepts, or search the codebase.
|
# AGENTS
|
||||||
|
|
||||||
|
Read and follow the AI policy found in `docs/policies/AI.md`, and the coding policies found in `docs/policies/Coding.md`.
|
||||||
|
|
||||||
|
Do not, under any circumstances, ever submit PRs or push commits on behalf of the user.
|
||||||
|
|
||||||
|
Keep comments to a reasonable level. Do not overexplain or add/remove comments from unrelated parts of the codebase.
|
||||||
|
|||||||
@@ -1 +0,0 @@
|
|||||||
AI and LLM use is *strictly* prohibited within our codebase and surrounding community, including issues and comments. This includes using AI or LLMs to write docs/commit messages, debug issues, brainstorm ideas, research concepts, or search the codebase.
|
|
||||||
@@ -24,7 +24,7 @@ else()
|
|||||||
endif()
|
endif()
|
||||||
|
|
||||||
if (lz4_FOUND AND NOT TARGET lz4::lz4)
|
if (lz4_FOUND AND NOT TARGET lz4::lz4)
|
||||||
if (TARGET LZ4::lz4_shared)
|
if (TARGET LZ4::lz4_shared AND NOT YUZU_STATIC_BUILD)
|
||||||
add_library(lz4::lz4 ALIAS LZ4::lz4_shared)
|
add_library(lz4::lz4 ALIAS LZ4::lz4_shared)
|
||||||
elseif (TARGET LZ4::lz4_static)
|
elseif (TARGET LZ4::lz4_static)
|
||||||
add_library(lz4::lz4 ALIAS LZ4::lz4_static)
|
add_library(lz4::lz4 ALIAS LZ4::lz4_static)
|
||||||
|
|||||||
+1
-6
@@ -1,9 +1,5 @@
|
|||||||
# Contributing
|
# Contributing
|
||||||
|
|
||||||
<!--
|
|
||||||
This document is not to be perused by AI models.
|
|
||||||
-->
|
|
||||||
|
|
||||||
Eden has lots of different ways that you can contribute to its efforts, even without knowing how to write code:
|
Eden has lots of different ways that you can contribute to its efforts, even without knowing how to write code:
|
||||||
|
|
||||||
- Donate! This will help us live our lives, pay for infrastructure/testing hardware, and more
|
- Donate! This will help us live our lives, pay for infrastructure/testing hardware, and more
|
||||||
@@ -20,8 +16,7 @@ Eden is free, open-source, copyleft software, licensed under the terms of the [G
|
|||||||
|
|
||||||
### Policies
|
### Policies
|
||||||
|
|
||||||
- No LLM or AI usage, *period*, for patches, pull requests, issues, comments, debugging, brainstorming, etc.
|
- We are trialing a slightly relaxed AI policy to allow high-quality contributors to use AI *responsibly*. Please read our [detailed AI policy](docs/policies/AI.md) for more info.
|
||||||
- For details on why, see the [detailed AI policy](docs/policies/AI.md).
|
|
||||||
- Usage of any form of profanity or otherwise unsavory language is generally discouraged.
|
- Usage of any form of profanity or otherwise unsavory language is generally discouraged.
|
||||||
- This is primarily because it rarely helps to actually understand what's going on.
|
- This is primarily because it rarely helps to actually understand what's going on.
|
||||||
- Remember that your comments should be focused and actually address what's happening. With very few exceptions, expletives are actively detrimental at best.
|
- Remember that your comments should be focused and actually address what's happening. With very few exceptions, expletives are actively detrimental at best.
|
||||||
|
|||||||
+2
-2
@@ -351,13 +351,13 @@
|
|||||||
},
|
},
|
||||||
"zstd": {
|
"zstd": {
|
||||||
"find_args": "MODULE",
|
"find_args": "MODULE",
|
||||||
"hash": "cc5ad4b119a9c2ea57f0b71eeff01113bb506e0d17000159c5409cb8236d22e38c52d5e9e97e7947a4bf1b2dfc44b6c503ab2d9aedbd59458435c6a2849cb029",
|
"hash": "2b006c3c110c38bd2f5896b42d36a38931c8eae261f30552f9a9fd04458d218d8f1a1425f117fd12af545ed77042e4b2d1a1a9b6286c4d0b9d74ef79f20a8f08",
|
||||||
"min_version": "1.5",
|
"min_version": "1.5",
|
||||||
"options": [
|
"options": [
|
||||||
"ZSTD_BUILD_SHARED OFF"
|
"ZSTD_BUILD_SHARED OFF"
|
||||||
],
|
],
|
||||||
"repo": "facebook/zstd",
|
"repo": "facebook/zstd",
|
||||||
"source_subdir": "build/cmake",
|
"source_subdir": "build/cmake",
|
||||||
"version": "b8d6101fba"
|
"version": "33618c8"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Vendored
+310
-302
File diff suppressed because it is too large
Load Diff
Vendored
+298
-289
File diff suppressed because it is too large
Load Diff
Vendored
+298
-289
File diff suppressed because it is too large
Load Diff
Vendored
+298
-289
File diff suppressed because it is too large
Load Diff
Vendored
+298
-290
File diff suppressed because it is too large
Load Diff
Vendored
+298
-289
File diff suppressed because it is too large
Load Diff
Vendored
+298
-290
File diff suppressed because it is too large
Load Diff
Vendored
+304
-295
File diff suppressed because it is too large
Load Diff
Vendored
+298
-289
File diff suppressed because it is too large
Load Diff
Vendored
+298
-289
File diff suppressed because it is too large
Load Diff
Vendored
+298
-289
File diff suppressed because it is too large
Load Diff
Vendored
+298
-289
File diff suppressed because it is too large
Load Diff
Vendored
+298
-289
File diff suppressed because it is too large
Load Diff
Vendored
+298
-290
File diff suppressed because it is too large
Load Diff
Vendored
+298
-289
File diff suppressed because it is too large
Load Diff
Vendored
+298
-289
File diff suppressed because it is too large
Load Diff
Vendored
+298
-289
File diff suppressed because it is too large
Load Diff
Vendored
+298
-290
File diff suppressed because it is too large
Load Diff
Vendored
+298
-289
File diff suppressed because it is too large
Load Diff
Vendored
+298
-290
File diff suppressed because it is too large
Load Diff
Vendored
+355
-347
File diff suppressed because it is too large
Load Diff
Vendored
+298
-289
File diff suppressed because it is too large
Load Diff
Vendored
+298
-290
File diff suppressed because it is too large
Load Diff
Vendored
+298
-289
File diff suppressed because it is too large
Load Diff
Vendored
+298
-289
File diff suppressed because it is too large
Load Diff
Vendored
+298
-290
File diff suppressed because it is too large
Load Diff
Vendored
+298
-289
File diff suppressed because it is too large
Load Diff
+143
-56
@@ -1,65 +1,159 @@
|
|||||||
# AI Policy
|
# AI Policy
|
||||||
|
|
||||||
AI and LLM use is *strictly* prohibited within our codebase and surrounding community, including issues and comments. This includes using AI or LLMs to write docs/commit messages, debug issues, brainstorm ideas, research concepts, or search the codebase.
|
> [!WARNING]
|
||||||
|
> We are trialing a slightly relaxed AI policy to allow high-quality contributors to use AI *responsibly*.
|
||||||
|
> This policy is subject to change at any point in time, so check back frequently.
|
||||||
|
>
|
||||||
|
> This banner will be removed once we have finalized our revised policy.
|
||||||
|
|
||||||
- [AI Policy](#ai-policy)
|
- [AI Policy](#ai-policy)
|
||||||
- [Low-quality code](#low-quality-code)
|
- [TL;DR](#tldr)
|
||||||
- [Licensing concerns](#licensing-concerns)
|
- [Definitions](#definitions)
|
||||||
- [Vibe-coding](#vibe-coding)
|
- [Scope](#scope)
|
||||||
- [Commit messages](#commit-messages)
|
- [Introduction](#introduction)
|
||||||
- [Miscellaneous concerns](#miscellaneous-concerns)
|
- [Requirements](#requirements)
|
||||||
- [Unacceptable Use Examples](#unacceptable-use-examples)
|
- [Sign-off, Authorship, and Copyright](#sign-off-authorship-and-copyright)
|
||||||
- [Addendum: Commit Messages](#addendum-commit-messages)
|
- [Permitted Usage](#permitted-usage)
|
||||||
|
- [Recommended Prior Discussion](#recommended-prior-discussion)
|
||||||
|
- [Forbidden Usage](#forbidden-usage)
|
||||||
|
- [Human Effort Denial of Service](#human-effort-denial-of-service)
|
||||||
|
- [Disclosure](#disclosure)
|
||||||
|
- [Codebase Ingestion](#codebase-ingestion)
|
||||||
|
- [Enforcement](#enforcement)
|
||||||
|
- [Addendum: AI Commit Messages](#addendum-ai-commit-messages)
|
||||||
|
- [References](#references)
|
||||||
|
|
||||||
## Low-quality code
|
## TL;DR
|
||||||
|
|
||||||
AI is notorious for producing low-quality code; be it:
|
AI-assisted contributions are welcome, provided you take responsibility for them.
|
||||||
|
|
||||||
- nonfunctional,
|
- You **must** disclose any AI or LLM usage in your pull request description *and* commit message.
|
||||||
- verbose/inefficient,
|
- You **must** be able to explain, defend, and revise every line of code you submit.
|
||||||
- breaking other parts of the codebase,
|
- Commit messages, PR descriptions, issues, and comments **must** be written by you. Verbatim AI output is not acceptable.
|
||||||
- or writing/architecting in a completely different style
|
- Undisclosed AI usage will result in your contribution being rejected.
|
||||||
|
- Low-quality contributions, disclosed or not, will be reworked or rejected.
|
||||||
|
- Spamming automated PRs, comments, or issues is treated as a Denial of Service (DoS) attack on our reviewers; see [Human Effort Denial of Service](#human-effort-denial-of-service).
|
||||||
|
|
||||||
All code, AI or not, is held under a **strict standard of excellence**. AI/LLM-generated code will fail this test 10 times out of 10.
|
## Definitions
|
||||||
|
|
||||||
## Licensing concerns
|
An *AI or LLM-based tool* is any piece of software that generates code, text, or other content through machine learning models. This includes, but is not limited to: chat assistants, agentic coding tools/harnesses, inline code completion suggestions you have accepted into the code, and AI-driven explanation/refactoring features.
|
||||||
|
|
||||||
This is an area of ongoing litigation, and as such, is still very iffy. For the time being, know that allowing AI to ingest the codebase may end up with its copyleft code regurgitated into incompatibly-licensed proprietary or permissive software. For you, this means to **not** feed code into LLMs.
|
For the purposes of this policy, basic deterministic/static development tools (such as clang-tidy, git, compilers, etc.) are not considered AI tools.
|
||||||
|
|
||||||
AI models may have also ingested AGPLv3 code, which is license-incompatible with our codebase.
|
Terms such as "substantially", "large-scale", "majority", and "low-quality" are intentionally left vague, and will be interpreted at the maintainers' discretion. When in doubt, ask first.
|
||||||
|
|
||||||
## Vibe-coding
|
## Scope
|
||||||
|
|
||||||
Just don't. If you're not going to put the effort in to understand every line of code you wrote, neither will we, and your patch or pull request will be ignored.
|
This policy applies to all code contributions, alongside issues and pull requests surrounding this codebase. This includes:
|
||||||
|
|
||||||
## Commit messages
|
- Commit messages
|
||||||
|
- PR descriptions
|
||||||
|
- Code review comments
|
||||||
|
- Issue/PR comments
|
||||||
|
- Patches submitted via e-mail
|
||||||
|
|
||||||
AI-generated commit messages are absolutely terrible, and your pull request or patch will immediately be rejected if you choose to do this. They are, quite simply, actively detrimental to our understanding of your changes, and if you're not willing to summarize the intent behind your changes, then we're not going to bother reading the code you wrote.
|
Areas that are not directly related to code--such as our surrounding communities--are not covered by the terms of this policy. Additionally, spelling/grammar checking via AI or LLM-based tools is not covered under this document.
|
||||||
|
|
||||||
Write concise, simple, and descriptive commit messages that actually convey the proper intent behind what your change is trying to do.
|
## Introduction
|
||||||
|
|
||||||
See the [Addendum](#addendum-commit-messages) for an instance of how bad AI models are at commit messages.
|
In the right hands, AI and LLM-based tools are incredibly useful for getting features and bugfixes out the door as fast as possible. At the same time, reviewer bandwidth is a scarce resource. We are therefore open to AI-assisted contributions, provided they are produced and disclosed properly.
|
||||||
|
|
||||||
## Miscellaneous concerns
|
The goal of this document is *not* to set arbitrary restrictions, but rather to keep our development healthy and stable, while still allowing contributors to use the tools they find helpful. A contribution must be worth more to the project than the time it takes to review it.
|
||||||
|
|
||||||
- While many environmental concerns about AI are typically blown out of proportion, it *is* a legitimate issue, and should be taken into account.
|
Throughout this document, **must** and **must not** are inflexible, binding requirements. Refusal to follow these is treated as a violation of the terms outlined in this document.
|
||||||
- Many people have significant concerns over the ethics of AI usage due to inhumane and predatory behavior by large AI companies, particularly Anthropic and OpenAI. This can technically be avoided through the usage of local LLMs.
|
|
||||||
- LLMs have a tendency to add unicode characters (such as the arrow → and the em-dash — symbols) to their output, which can make viewing code or documents harder on command-line editors and viewers.
|
|
||||||
- Dedicated coding models--namely Claude--also like to add a lot of comments to overexplain every individual line of code it produces. This actually makes it *harder* to understand the code!
|
|
||||||
|
|
||||||
## Unacceptable Use Examples
|
## Requirements
|
||||||
|
|
||||||
Here are a few examples of unacceptable use:
|
These apply to all contributions, whether AI-assisted or not. AI-assisted contributions also carry additional disclosure requirements as outlined in [Disclosure](#disclosure).
|
||||||
|
|
||||||
- Solving problems
|
1. *Disclosure*. Any and all usage of AI or LLM-based tools within any contribution **must** be explicitly marked and disclosed as appropriate.
|
||||||
- Slapping a few files and a "please fix bug XYZ" into an LLM is a recipe for disaster that will pretty much never work.
|
2. *Responsibility*. The human submitter of any contribution is held wholly responsible for the accuracy, completeness, and intent of what is submitted. You **must** be able to explain, defend, and revise your code during review.
|
||||||
- Fully AI-generated code, aka "vibecoding"
|
3. *Review*. All code **must** be thoroughly reviewed by the author before opening a PR or marking it ready for review.
|
||||||
- Writing code based on pseudo-instructions
|
4. *Testing*. All code changes **must** be tested directly by the human author.
|
||||||
- If you don't know how to write code, don't. If you've figured out the root cause (preferably without feeding random files into an LLM) and actively know what's going on, provide information to other developers or friends of yours who have knowledge of the language and/or the codebase.
|
5. *Quality*. All submitted code is held to a strict standard of excellence. Low-quality AI-generated content does not uphold this standard.
|
||||||
|
6. *Licensing*. All code **must** be compatible with the GPLv3 or later. You **must** warrant that you have the right to submit it under those terms, and that it was not copied from work published under an incompatible license.
|
||||||
|
|
||||||
## Addendum: Commit Messages
|
## Sign-off, Authorship, and Copyright
|
||||||
|
|
||||||
The patchset for pull request [#3422](https://git.eden-emu.dev/eden-emu/eden/pulls/3422) was fed into several LLMs to generate a commit message. One LLM produced the following:
|
AI agents **must not** add `Signed-off-by:` tags. The human submitter **must** sign off on all changes.
|
||||||
|
|
||||||
|
Similarly, AI agents cannot hold authorship or copyright over code, and **must not** add `Co-authored-by:` or `Co-developed-by:` tags. The human submitter **must** author all changes.
|
||||||
|
|
||||||
|
## Permitted Usage
|
||||||
|
|
||||||
|
This is a non-exhaustive list of permitted uses of AI and LLM-based tools, provided they are disclosed properly.
|
||||||
|
|
||||||
|
- Drafting issues and pull requests, provided the final text and submitted code are substantially your own.
|
||||||
|
- Debugging issues, bugs, glitches, and crashes, whether pre-existing or introduced by your own changes.
|
||||||
|
- Reviewing your own changes before submitting a PR.
|
||||||
|
- Research, brainstorming, and codebase searching/understanding.
|
||||||
|
- Applying tedious but not particularly large changes, such as mass-migrating specific classes to polyfills or abstractions.
|
||||||
|
|
||||||
|
## Recommended Prior Discussion
|
||||||
|
|
||||||
|
Some usage cases are not necessarily forbidden nor permitted. These should be discussed with maintainers in an issue or e-mail *before* submission. Maintainers and reviewers may also comment on this during the review/submission phase.
|
||||||
|
|
||||||
|
- Generating boilerplate/scaffolding for new code.
|
||||||
|
- Writing unit tests.
|
||||||
|
- Large-scale refactors primarily driven by an AI agent.
|
||||||
|
- Any contribution where an AI agent or model was responsible for the majority of the implementation.
|
||||||
|
|
||||||
|
## Forbidden Usage
|
||||||
|
|
||||||
|
This is a non-exhaustive list of strictly forbidden uses of AI and LLM-based tools.
|
||||||
|
|
||||||
|
- Vibecoding, or changes entirely authored by AI agents without human intervention or understanding.
|
||||||
|
- If you don't choose to put in the necessary effort to understand every line of code you submitted, neither will we.
|
||||||
|
- Fully AI-generated commit messages, PR descriptions, issues, or comments. See [Addendum: AI Commit Messages](#addendum-ai-commit-messages).
|
||||||
|
- Submission of AI-assisted work that is not disclosed per the [Disclosure](#disclosure) requirements.
|
||||||
|
- Low-quality submissions that fail to satisfy our standard of code excellence.
|
||||||
|
- Submitting secrets, credentials, private user data, or unpublished security reports to third-party AI services.
|
||||||
|
- This includes console keys, encryption material, and private vulnerability disclosures.
|
||||||
|
- Verbatim AI code reviews (e.g. Codex/Copilot reviews).
|
||||||
|
|
||||||
|
## Human Effort Denial of Service
|
||||||
|
|
||||||
|
Using automated tools and AI to submit PRs, issues, and comments that we have to carefully review and handle is considered a [Denial-of-service attack](https://en.wikipedia.org/wiki/Denial-of-service_attack) on our human effort.
|
||||||
|
|
||||||
|
It takes very little effort on your side (an LLM prompt) to generate a large amount of effort on ours (careful and thorough code review). Please respect our time and don't do that.
|
||||||
|
|
||||||
|
## Disclosure
|
||||||
|
|
||||||
|
How you disclose AI usage is up to you. However, it **must** be explicit, easy to identify, and placed both within the commit message *and* the surrounding PR description. For patches submitted by e-mail, the cover letter counts as the PR description. We recommend an `Assisted-by:` footer:
|
||||||
|
|
||||||
|
```txt
|
||||||
|
Assisted-by: <model> (<what it was used for>)
|
||||||
|
```
|
||||||
|
|
||||||
|
For instance:
|
||||||
|
|
||||||
|
```txt
|
||||||
|
Assisted-by: Claude Opus 5.5 (initial draft and code review)
|
||||||
|
```
|
||||||
|
|
||||||
|
Contributions without a commit message (e.g. issues/comments) should instead use a plain sentence in the text body such as `I used <model> to help with <X>`. PRs and commit messages may opt to use this format as well.
|
||||||
|
|
||||||
|
Multi-commit series/squashes **must** maintain the trailer. Maintainers will handle this for merges to master, but you are responsible for ensuring the disclosure survives into the final commit/PR body of squashes within your branches.
|
||||||
|
|
||||||
|
Regardless, what matters is that reviewers can tell *at a glance* where and how AI was involved in your contribution.
|
||||||
|
|
||||||
|
## Codebase Ingestion
|
||||||
|
|
||||||
|
Ingestion of this codebase by LLMs for any purpose--such as training or prompt context--is permitted *for now*. This may change at any point as litigation and court rulings around AI and code copyright proceed.
|
||||||
|
|
||||||
|
AI models may have ingested AGPLv3 or otherwise copyleft-incompatible code and may regurgitate it. You remain responsible for ensuring that all code you submit is compatible with the GPLv3 or later.
|
||||||
|
|
||||||
|
## Enforcement
|
||||||
|
|
||||||
|
- If we suspect undisclosed AI usage in a contribution, we will ask for disclosure.
|
||||||
|
- Refusal to provide disclosure will result in rejection of the contribution, and your PR or issue will be closed. Maintainers may choose to rework it themselves if the change is worth having.
|
||||||
|
- Accounts that spam us with repeated automated PRs or comments will be blocked or permanently suspended from our Git instance.
|
||||||
|
- Maintainers reserve the right to close your issues and pull requests if you don't follow the guidelines and procedures outlined in this document, and refuse or fail to make changes as necessary to comply.
|
||||||
|
- Repeated failures to comply may result in loss of contribution privileges.
|
||||||
|
|
||||||
|
## Addendum: AI Commit Messages
|
||||||
|
|
||||||
|
The patchset for pull request [#3422](https://git.eden-emu.dev/eden-emu/eden/pulls/3422) was fed into a dedicated code AI model, and it produced the following:
|
||||||
|
|
||||||
```txt
|
```txt
|
||||||
profile_manager: Refactor user deletion to use index instead of UUID
|
profile_manager: Refactor user deletion to use index instead of UUID
|
||||||
@@ -89,28 +183,21 @@ These changes eliminate redundant code, reduce the risk of UUID → index mismat
|
|||||||
Fixes: potential inconsistency when user order changes between UI and backend
|
Fixes: potential inconsistency when user order changes between UI and backend
|
||||||
```
|
```
|
||||||
|
|
||||||
This is:
|
This:
|
||||||
|
|
||||||
- Needlessly verbose (nobody cares about most of these details)
|
- Is needlessly verbose (nobody cares about most of these details)
|
||||||
- Doesn't address the actual purpose of the PR (fixing a double-deletion bug in the profile manager)
|
- Doesn't address the actual purpose of the PR (fixing a double-deletion bug in the profile manager)
|
||||||
- Has unicode arrows (this is bad for command-line editors)
|
- Has unicode arrows (this is bad for command-line editors)
|
||||||
- Uses corporate and word-salad language
|
- Uses corporate and word-salad language that pads out the message to make it look like a lot changed
|
||||||
|
|
||||||
Another (code-oriented) LLM output the following:
|
A good commit message should be concise, descriptive, and convey the *intent* behind the change, such as the bug being fixed or the feature being added. It should *not* be a detailed changelog of every individual byte that's changed.
|
||||||
|
|
||||||
```txt
|
## References
|
||||||
profile(manager/ui): switch to index-based deletion and unify removal logic
|
|
||||||
|
|
||||||
- Make ConfigureProfileManagerDeleteDialog emit a deleteUser(int) signal instead of taking a callback. Store the target index in the dialog (m_index) and emit it on acceptance.
|
This policy document has been adapted from the AI policies of numerous other free and open-source projects, whether restrictive or permissive.
|
||||||
- Change ConfigureProfileManager to pass an index to SetInfo and to accept an index in DeleteUser. Wire the dialog signal to the new DeleteUser slot.
|
|
||||||
- Update headers: add the signal and m_index, add TODO to move dialog to a .ui file, and update slot/signature declarations.
|
|
||||||
- Add ProfileManager::RemoveProfileAtIndex(std::size_t) and refactor RemoveUser(UUID) to call RemoveProfileAtIndex to avoid duplicated removal logic. Ensure the removal path marks saves as needed and writes the user save file.
|
|
||||||
- Ensure the profile list updates immediately after deletes by calling profile_manager.ResetUserSaveFile() when populating the user list (qlaunch fix).
|
|
||||||
- Misc: update SPDX copyright year and fix build breakages caused by the API changes.
|
|
||||||
|
|
||||||
This consolidates profile removal behavior, fixes potential race conditions in the profile dialog, and removes duplicated removal code.
|
- [Gitea](https://github.com/go-gitea/gitea/blob/main/CONTRIBUTING.md#ai-contribution-policy)
|
||||||
```
|
- The [Linux kernel](https://kernel.org/doc/html/next/process/coding-assistants.html)
|
||||||
|
- [Gentoo Linux](https://wiki.gentoo.org/wiki/Project:Council/AI_policy)
|
||||||
This has all of the same problems as the other one. Needlessly verbose, doesn't address *what* it actually fixes ("consolidates profile removal behavior"... okay, why? What does it fix?), etc. It even has the bonus of totally hallucinating the addition of a method!
|
- [cURL](https://curl.se/dev/contribute.html#on-ai-use-in-curl)
|
||||||
|
- [FastAPI](https://tiangolo.com/open-source/contributing/#automated-code-and-ai)
|
||||||
On a more "philosophical" note, LLMs tend to be geared towards *corporate language*, as that's what they're trained on. This is why AI-generated commit messages feel like "word salad", and typically pad out the commit message to make it *look* like a lot of things were changed (trust me, it's like that in the corporate world). They typically also drift towards unneeded buzzwords and useless implementation details.
|
|
||||||
|
|||||||
@@ -259,7 +259,7 @@ android {
|
|||||||
|
|
||||||
externalNativeBuild {
|
externalNativeBuild {
|
||||||
cmake {
|
cmake {
|
||||||
version = "3.31.6"
|
version = "3.31.5"
|
||||||
path = file("${edenDir}/CMakeLists.txt")
|
path = file("${edenDir}/CMakeLists.txt")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
-3
@@ -16,8 +16,6 @@ enum class BooleanSetting(override val key: String) : AbstractBooleanSetting {
|
|||||||
RENDERER_USE_SPEED_LIMIT("use_speed_limit"),
|
RENDERER_USE_SPEED_LIMIT("use_speed_limit"),
|
||||||
USE_CUSTOM_CPU_TICKS("use_custom_cpu_ticks"),
|
USE_CUSTOM_CPU_TICKS("use_custom_cpu_ticks"),
|
||||||
SKIP_CPU_INNER_INVALIDATION("skip_cpu_inner_invalidation"),
|
SKIP_CPU_INNER_INVALIDATION("skip_cpu_inner_invalidation"),
|
||||||
NCE_INVALIDATION_GPU_READBACK("nce_invalidation_gpu_readback"),
|
|
||||||
NCE_RUNTIME_NRO_PATCH("nce_runtime_nro_patch"),
|
|
||||||
FIX_BLOOM_EFFECTS("fix_bloom_effects"),
|
FIX_BLOOM_EFFECTS("fix_bloom_effects"),
|
||||||
EMULATE_BGR565("emulate_bgr565"),
|
EMULATE_BGR565("emulate_bgr565"),
|
||||||
RESCALE_HACK("rescale_hack"),
|
RESCALE_HACK("rescale_hack"),
|
||||||
@@ -27,7 +25,6 @@ enum class BooleanSetting(override val key: String) : AbstractBooleanSetting {
|
|||||||
RENDERER_USE_DISK_SHADER_CACHE("use_disk_shader_cache"),
|
RENDERER_USE_DISK_SHADER_CACHE("use_disk_shader_cache"),
|
||||||
RENDERER_FORCE_MAX_CLOCK("force_max_clock"),
|
RENDERER_FORCE_MAX_CLOCK("force_max_clock"),
|
||||||
RENDERER_ASYNCHRONOUS_GPU_EMULATION("use_asynchronous_gpu_emulation"),
|
RENDERER_ASYNCHRONOUS_GPU_EMULATION("use_asynchronous_gpu_emulation"),
|
||||||
RENDERER_ASYNC_PRESENTATION("async_presentation"),
|
|
||||||
RENDERER_ASYNCHRONOUS_SHADERS("use_asynchronous_shaders"),
|
RENDERER_ASYNCHRONOUS_SHADERS("use_asynchronous_shaders"),
|
||||||
RENDERER_REACTIVE_FLUSHING("use_reactive_flushing"),
|
RENDERER_REACTIVE_FLUSHING("use_reactive_flushing"),
|
||||||
ENABLE_BUFFER_HISTORY("enable_buffer_history"),
|
ENABLE_BUFFER_HISTORY("enable_buffer_history"),
|
||||||
|
|||||||
-21
@@ -753,13 +753,6 @@ abstract class SettingsItem(
|
|||||||
descriptionId = R.string.renderer_asynchronous_gpu_emulation_description
|
descriptionId = R.string.renderer_asynchronous_gpu_emulation_description
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
put(
|
|
||||||
SwitchSetting(
|
|
||||||
BooleanSetting.RENDERER_ASYNC_PRESENTATION,
|
|
||||||
titleId = R.string.renderer_async_presentation,
|
|
||||||
descriptionId = R.string.renderer_async_presentation_description
|
|
||||||
)
|
|
||||||
)
|
|
||||||
put(
|
put(
|
||||||
SingleChoiceSetting(
|
SingleChoiceSetting(
|
||||||
IntSetting.DMA_ACCURACY,
|
IntSetting.DMA_ACCURACY,
|
||||||
@@ -866,20 +859,6 @@ abstract class SettingsItem(
|
|||||||
descriptionId = R.string.skip_cpu_inner_invalidation_description
|
descriptionId = R.string.skip_cpu_inner_invalidation_description
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
put(
|
|
||||||
SwitchSetting(
|
|
||||||
BooleanSetting.NCE_INVALIDATION_GPU_READBACK,
|
|
||||||
titleId = R.string.nce_invalidation_gpu_readback,
|
|
||||||
descriptionId = R.string.nce_invalidation_gpu_readback_description
|
|
||||||
)
|
|
||||||
)
|
|
||||||
put(
|
|
||||||
SwitchSetting(
|
|
||||||
BooleanSetting.NCE_RUNTIME_NRO_PATCH,
|
|
||||||
titleId = R.string.nce_runtime_nro_patch,
|
|
||||||
descriptionId = R.string.nce_runtime_nro_patch_description
|
|
||||||
)
|
|
||||||
)
|
|
||||||
put(
|
put(
|
||||||
SwitchSetting(
|
SwitchSetting(
|
||||||
BooleanSetting.FIX_BLOOM_EFFECTS,
|
BooleanSetting.FIX_BLOOM_EFFECTS,
|
||||||
|
|||||||
-3
@@ -554,14 +554,11 @@ class SettingsFragmentPresenter(
|
|||||||
add(HeaderSetting(R.string.hacks))
|
add(HeaderSetting(R.string.hacks))
|
||||||
|
|
||||||
add(BooleanSetting.SKIP_CPU_INNER_INVALIDATION.key)
|
add(BooleanSetting.SKIP_CPU_INNER_INVALIDATION.key)
|
||||||
add(BooleanSetting.NCE_INVALIDATION_GPU_READBACK.key)
|
|
||||||
add(BooleanSetting.NCE_RUNTIME_NRO_PATCH.key)
|
|
||||||
add(BooleanSetting.FIX_BLOOM_EFFECTS.key)
|
add(BooleanSetting.FIX_BLOOM_EFFECTS.key)
|
||||||
add(BooleanSetting.EMULATE_BGR565.key)
|
add(BooleanSetting.EMULATE_BGR565.key)
|
||||||
add(BooleanSetting.RENDERER_ASYNCHRONOUS_SHADERS.key)
|
add(BooleanSetting.RENDERER_ASYNCHRONOUS_SHADERS.key)
|
||||||
add(IntSetting.ANDROID_PIPELINE_WORKERS.key)
|
add(IntSetting.ANDROID_PIPELINE_WORKERS.key)
|
||||||
add(BooleanSetting.RENDERER_ASYNCHRONOUS_GPU_EMULATION.key)
|
add(BooleanSetting.RENDERER_ASYNCHRONOUS_GPU_EMULATION.key)
|
||||||
add(BooleanSetting.RENDERER_ASYNC_PRESENTATION.key)
|
|
||||||
add(SettingsItem.GPU_UNSWIZZLE_COMBINED)
|
add(SettingsItem.GPU_UNSWIZZLE_COMBINED)
|
||||||
|
|
||||||
add(HeaderSetting(R.string.extensions))
|
add(HeaderSetting(R.string.extensions))
|
||||||
|
|||||||
@@ -100,7 +100,7 @@ class Game(
|
|||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
val extensions: Set<String> = HashSet(
|
val extensions: Set<String> = HashSet(
|
||||||
listOf("xci", "nsp", "nca", "nro")
|
listOf("xci", "nsp", "nca", "nro", "nsz", "xcz")
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -109,7 +109,7 @@ object GameHelper {
|
|||||||
|
|
||||||
// File extensions considered as external content, buuut should
|
// File extensions considered as external content, buuut should
|
||||||
// be done better imo.
|
// be done better imo.
|
||||||
private val externalContentExtensions = setOf("nsp", "xci")
|
private val externalContentExtensions = setOf("nsp", "xci", "nsz", "xcz")
|
||||||
|
|
||||||
private fun scanContentContainersRecursive(
|
private fun scanContentContainersRecursive(
|
||||||
files: Array<MinimalDocumentFile>,
|
files: Array<MinimalDocumentFile>,
|
||||||
|
|||||||
@@ -76,7 +76,9 @@ jboolean Java_org_yuzu_yuzu_1emu_utils_GameMetadata_getIsValid(JNIEnv* env, jobj
|
|||||||
auto const file_type = loader->GetFileType();
|
auto const file_type = loader->GetFileType();
|
||||||
if (file_type == Loader::FileType::Unknown || file_type == Loader::FileType::Error)
|
if (file_type == Loader::FileType::Unknown || file_type == Loader::FileType::Error)
|
||||||
return false;
|
return false;
|
||||||
if ((file_type == Loader::FileType::NSP || file_type == Loader::FileType::XCI) && !Loader::IsBootableGameContainer(file, file_type))
|
if ((file_type == Loader::FileType::NSP || file_type == Loader::FileType::XCI ||
|
||||||
|
file_type == Loader::FileType::NSZ || file_type == Loader::FileType::XCZ) &&
|
||||||
|
!Loader::IsBootableGameContainer(file, file_type))
|
||||||
return false;
|
return false;
|
||||||
u64 program_id = 0;
|
u64 program_id = 0;
|
||||||
return loader->ReadProgramId(program_id) == Loader::ResultStatus::Success;
|
return loader->ReadProgramId(program_id) == Loader::ResultStatus::Success;
|
||||||
|
|||||||
@@ -254,7 +254,7 @@
|
|||||||
<string name="emulator_data">إعداد بيانات المحاكي</string>
|
<string name="emulator_data">إعداد بيانات المحاكي</string>
|
||||||
<string name="emulator_data_description">يلزم وجود مفاتيح حتى يعمل المحاكي، ويوصى باستخدام الفيرموير ويكون ضروريًا لاستخدام تطبيق القائمة الرئيسية.</string>
|
<string name="emulator_data_description">يلزم وجود مفاتيح حتى يعمل المحاكي، ويوصى باستخدام الفيرموير ويكون ضروريًا لاستخدام تطبيق القائمة الرئيسية.</string>
|
||||||
<string name="permissions">منح الأذونات</string>
|
<string name="permissions">منح الأذونات</string>
|
||||||
<string name="permissions_description">منح أذونات اختيارية لاستخدام ميزات معينة للمحاكي</string>
|
<string name="permissions_description">منح أذونات اختيارية لاستخدام ميزات محددة في المحاكي</string>
|
||||||
<string name="install_firmware_warning_description">العديد من الألعاب تتطلب الوصول إلى الفيرموير لتشغيل بشكل صحيح.</string>
|
<string name="install_firmware_warning_description">العديد من الألعاب تتطلب الوصول إلى الفيرموير لتشغيل بشكل صحيح.</string>
|
||||||
<string name="install_firmware_warning_help">https://yuzu-mirror.github.io/help/quickstart/#guide-introduction</string>
|
<string name="install_firmware_warning_help">https://yuzu-mirror.github.io/help/quickstart/#guide-introduction</string>
|
||||||
<string name="notifications">الإشعارات</string>
|
<string name="notifications">الإشعارات</string>
|
||||||
@@ -574,8 +574,6 @@
|
|||||||
<string name="renderer_force_max_clock_description">يجبر وحدة معالجة الرسومات على العمل بأقصى سرعة ممكنة (سيظل يتم تطبيق القيود الحرارية).</string>
|
<string name="renderer_force_max_clock_description">يجبر وحدة معالجة الرسومات على العمل بأقصى سرعة ممكنة (سيظل يتم تطبيق القيود الحرارية).</string>
|
||||||
<string name="renderer_asynchronous_gpu_emulation">محاكاة غير متزامنة لوحدة معالجة الرسومات</string>
|
<string name="renderer_asynchronous_gpu_emulation">محاكاة غير متزامنة لوحدة معالجة الرسومات</string>
|
||||||
<string name="renderer_asynchronous_gpu_emulation_description">يمكن لهذه الحيلة أن تزيد الأداء عن طريق تشغيل محاكاة وحدة معالجة الرسومات بشكل غير متزامن على حساب مشاكل الرسومات وزيادة معدلات الأعطال بسبب العمليات المتعلقة بالتوقيت.</string>
|
<string name="renderer_asynchronous_gpu_emulation_description">يمكن لهذه الحيلة أن تزيد الأداء عن طريق تشغيل محاكاة وحدة معالجة الرسومات بشكل غير متزامن على حساب مشاكل الرسومات وزيادة معدلات الأعطال بسبب العمليات المتعلقة بالتوقيت.</string>
|
||||||
<string name="renderer_async_presentation">عرض غير متزامن</string>
|
|
||||||
<string name="renderer_async_presentation_description">يمكن لهذه الحيلة أن تزيد من الأداء عن طريق نقل عملية العرض إلى خيط معالجة منفصل على حساب مشاكل الرسوميات.</string>
|
|
||||||
<string name="renderer_reactive_flushing">استخدم التنظيف التفاعلي</string>
|
<string name="renderer_reactive_flushing">استخدم التنظيف التفاعلي</string>
|
||||||
<string name="renderer_reactive_flushing_description">يحسن دقة العرض في بعض الألعاب على حساب الأداء.</string>
|
<string name="renderer_reactive_flushing_description">يحسن دقة العرض في بعض الألعاب على حساب الأداء.</string>
|
||||||
<string name="enable_buffer_history">تفعيل سجل التخزين المؤقت</string>
|
<string name="enable_buffer_history">تفعيل سجل التخزين المؤقت</string>
|
||||||
|
|||||||
@@ -518,8 +518,6 @@
|
|||||||
<string name="renderer_force_max_clock_description">Fuerza a la GPU a ejecutarse a la velocidad máxima de reloj posible (se seguirán aplicando restricciones térmicas).</string>
|
<string name="renderer_force_max_clock_description">Fuerza a la GPU a ejecutarse a la velocidad máxima de reloj posible (se seguirán aplicando restricciones térmicas).</string>
|
||||||
<string name="renderer_asynchronous_gpu_emulation">Emulación de GPU asíncrona</string>
|
<string name="renderer_asynchronous_gpu_emulation">Emulación de GPU asíncrona</string>
|
||||||
<string name="renderer_asynchronous_gpu_emulation_description">Este hack puede aumentar el rendimiento ejecutando la emulación de la GPU de forma asíncrona, a costa de problemas gráficos y un aumento en la tasa de fallos debido a operaciones relacionadas con la sincronización.</string>
|
<string name="renderer_asynchronous_gpu_emulation_description">Este hack puede aumentar el rendimiento ejecutando la emulación de la GPU de forma asíncrona, a costa de problemas gráficos y un aumento en la tasa de fallos debido a operaciones relacionadas con la sincronización.</string>
|
||||||
<string name="renderer_async_presentation">Presentación asíncrona</string>
|
|
||||||
<string name="renderer_async_presentation_description">Este hack puede aumentar el rendimiento al mover la presentación a un hilo independiente de la CPU a costa de problemas gráficos.</string>
|
|
||||||
<string name="renderer_reactive_flushing">Usar limpieza reactiva</string>
|
<string name="renderer_reactive_flushing">Usar limpieza reactiva</string>
|
||||||
<string name="renderer_reactive_flushing_description">Mejora la precisión de renderizado en algunos juegos, pero reduce el rendimiento.</string>
|
<string name="renderer_reactive_flushing_description">Mejora la precisión de renderizado en algunos juegos, pero reduce el rendimiento.</string>
|
||||||
<string name="enable_buffer_history">Activar el historial del búfer</string>
|
<string name="enable_buffer_history">Activar el historial del búfer</string>
|
||||||
|
|||||||
@@ -563,8 +563,6 @@
|
|||||||
<string name="renderer_force_max_clock_description">Заставляет ГПУ работать на максимально возможных тактовых частотах (тепловые ограничения все равно будут применяться).</string>
|
<string name="renderer_force_max_clock_description">Заставляет ГПУ работать на максимально возможных тактовых частотах (тепловые ограничения все равно будут применяться).</string>
|
||||||
<string name="renderer_asynchronous_gpu_emulation">Асинхронная эмуляция ГПУ</string>
|
<string name="renderer_asynchronous_gpu_emulation">Асинхронная эмуляция ГПУ</string>
|
||||||
<string name="renderer_asynchronous_gpu_emulation_description">Может повысить производительность за счёт асинхронного запуска эмуляции ГПУ, но ценой появления графических ошибок и увеличения частоты вылетов из-за операций, зависящих от синхронизации.</string>
|
<string name="renderer_asynchronous_gpu_emulation_description">Может повысить производительность за счёт асинхронного запуска эмуляции ГПУ, но ценой появления графических ошибок и увеличения частоты вылетов из-за операций, зависящих от синхронизации.</string>
|
||||||
<string name="renderer_async_presentation">Асинхронная презентация</string>
|
|
||||||
<string name="renderer_async_presentation_description">Может повысить производительность за счёт перемещения вывода кадров в отдельный поток ЦП, но ценой возникновения графических проблем.</string>
|
|
||||||
<string name="renderer_reactive_flushing">Реактивная очистка</string>
|
<string name="renderer_reactive_flushing">Реактивная очистка</string>
|
||||||
<string name="renderer_reactive_flushing_description">Повышение точности рендеринга в некоторых играх за счет снижения производительности.</string>
|
<string name="renderer_reactive_flushing_description">Повышение точности рендеринга в некоторых играх за счет снижения производительности.</string>
|
||||||
<string name="enable_buffer_history">Включить историю буфера</string>
|
<string name="enable_buffer_history">Включить историю буфера</string>
|
||||||
|
|||||||
@@ -481,8 +481,6 @@
|
|||||||
<string name="renderer_force_max_clock_description">Змушує GPU працювати на максимальній тактовій частоті.</string>
|
<string name="renderer_force_max_clock_description">Змушує GPU працювати на максимальній тактовій частоті.</string>
|
||||||
<string name="renderer_asynchronous_gpu_emulation">Асинхронна емуляція ГП</string>
|
<string name="renderer_asynchronous_gpu_emulation">Асинхронна емуляція ГП</string>
|
||||||
<string name="renderer_asynchronous_gpu_emulation_description">Це обхідне рішення може покращити продуктивність завдяки асинхронному виконанню емуляції ГП, але спричинить проблеми з графікою та збільшить частоту збоїв чутливих до таймінгів операцій.</string>
|
<string name="renderer_asynchronous_gpu_emulation_description">Це обхідне рішення може покращити продуктивність завдяки асинхронному виконанню емуляції ГП, але спричинить проблеми з графікою та збільшить частоту збоїв чутливих до таймінгів операцій.</string>
|
||||||
<string name="renderer_async_presentation">Асинхронне подання</string>
|
|
||||||
<string name="renderer_async_presentation_description">Це обхідне рішення може покращити продуктивність завдяки переміщенню подання на окремий потік ЦП, але спричинить проблеми з графікою.</string>
|
|
||||||
<string name="renderer_reactive_flushing">Реактивне очищення</string>
|
<string name="renderer_reactive_flushing">Реактивне очищення</string>
|
||||||
<string name="renderer_reactive_flushing_description">Покращує точність рендерингу в деяких іграх.</string>
|
<string name="renderer_reactive_flushing_description">Покращує точність рендерингу в деяких іграх.</string>
|
||||||
<string name="enable_buffer_history">Увімкнути історію буфера</string>
|
<string name="enable_buffer_history">Увімкнути історію буфера</string>
|
||||||
|
|||||||
@@ -564,8 +564,6 @@
|
|||||||
<string name="renderer_force_max_clock_description">强制 GPU 以最大时钟运行 (温控依然生效)。</string>
|
<string name="renderer_force_max_clock_description">强制 GPU 以最大时钟运行 (温控依然生效)。</string>
|
||||||
<string name="renderer_asynchronous_gpu_emulation">GPU 异步模拟</string>
|
<string name="renderer_asynchronous_gpu_emulation">GPU 异步模拟</string>
|
||||||
<string name="renderer_asynchronous_gpu_emulation_description">此技巧可通过异步运行 GPU 模拟来提升性能,但在执行与时序相关的操作时,可能带来图形显示问题以及增加崩溃概率。</string>
|
<string name="renderer_asynchronous_gpu_emulation_description">此技巧可通过异步运行 GPU 模拟来提升性能,但在执行与时序相关的操作时,可能带来图形显示问题以及增加崩溃概率。</string>
|
||||||
<string name="renderer_async_presentation">异步呈现</string>
|
|
||||||
<string name="renderer_async_presentation_description">此技巧通过将图形呈现移至独立的 CPU 线程来提升性能,但可能会带来图形显示问题。</string>
|
|
||||||
<string name="renderer_reactive_flushing">启用反应性刷新</string>
|
<string name="renderer_reactive_flushing">启用反应性刷新</string>
|
||||||
<string name="renderer_reactive_flushing_description">通过牺牲性能来提升某些游戏的渲染精度。</string>
|
<string name="renderer_reactive_flushing_description">通过牺牲性能来提升某些游戏的渲染精度。</string>
|
||||||
<string name="enable_buffer_history">启用缓冲区历史</string>
|
<string name="enable_buffer_history">启用缓冲区历史</string>
|
||||||
|
|||||||
@@ -54,7 +54,7 @@
|
|||||||
<string name="enable_stats_overlay_">啟用效能統計疊加層</string>
|
<string name="enable_stats_overlay_">啟用效能統計疊加層</string>
|
||||||
<string name="stats_overlay_options_description">設定疊加層中顯示的資訊</string>
|
<string name="stats_overlay_options_description">設定疊加層中顯示的資訊</string>
|
||||||
<string name="show_fps">顯示影格率</string>
|
<string name="show_fps">顯示影格率</string>
|
||||||
<string name="show_fps_description">顯示當前影格率</string>
|
<string name="show_fps_description">顯示目前的影格率</string>
|
||||||
<string name="show_frametime">顯示影格時間</string>
|
<string name="show_frametime">顯示影格時間</string>
|
||||||
<string name="show_app_ram_usage">顯示應用程式的記憶體用量</string>
|
<string name="show_app_ram_usage">顯示應用程式的記憶體用量</string>
|
||||||
<string name="show_app_ram_usage_description">顯示模擬器的記憶體用量</string>
|
<string name="show_app_ram_usage_description">顯示模擬器的記憶體用量</string>
|
||||||
@@ -143,7 +143,7 @@
|
|||||||
<string name="multiplayer_host_kicked">您已被房主踢出</string>
|
<string name="multiplayer_host_kicked">您已被房主踢出</string>
|
||||||
<string name="multiplayer_unknown_error">未知錯誤</string>
|
<string name="multiplayer_unknown_error">未知錯誤</string>
|
||||||
<string name="multiplayer_room_uninitialized">房間未初始化</string>
|
<string name="multiplayer_room_uninitialized">房間未初始化</string>
|
||||||
<string name="multiplayer_room_idle">房間閒置</string>
|
<string name="multiplayer_room_idle">房間閒置中</string>
|
||||||
<string name="multiplayer_room_joining">正在加入房間</string>
|
<string name="multiplayer_room_joining">正在加入房間</string>
|
||||||
<string name="multiplayer_room_joined">已加入房間</string>
|
<string name="multiplayer_room_joined">已加入房間</string>
|
||||||
<string name="multiplayer_room_moderator">房間管理員</string>
|
<string name="multiplayer_room_moderator">房間管理員</string>
|
||||||
@@ -185,8 +185,8 @@
|
|||||||
<string name="multiplayer_preferred_game_name">遊戲</string>
|
<string name="multiplayer_preferred_game_name">遊戲</string>
|
||||||
<string name="multiplayer_lobby_type">大廳類型</string>
|
<string name="multiplayer_lobby_type">大廳類型</string>
|
||||||
<string name="multiplayer_room_name_error">長度需為 3-20 個字元</string>
|
<string name="multiplayer_room_name_error">長度需為 3-20 個字元</string>
|
||||||
<string name="multiplayer_required">必填</string>
|
<string name="multiplayer_required">請填寫這個欄位</string>
|
||||||
<string name="multiplayer_token_required">需要Web令牌,請前往進階設定 -> 系統 -> 網路</string>
|
<string name="multiplayer_token_required">需要 Web 令牌,請前往進階設定 -> 系統 -> 網路</string>
|
||||||
<string name="multiplayer_ip_error">IP 格式無效</string>
|
<string name="multiplayer_ip_error">IP 格式無效</string>
|
||||||
<string name="multiplayer_username_error">必須介於 4-20 個字元之間,並且僅能使用英文字母、數字、句點、破折號、底線和空格(標點符號須為英文格式)</string>
|
<string name="multiplayer_username_error">必須介於 4-20 個字元之間,並且僅能使用英文字母、數字、句點、破折號、底線和空格(標點符號須為英文格式)</string>
|
||||||
<string name="multiplayer_nickname_invalid">使用者名稱無效,請在系統 -> 網路中檢查設定</string>
|
<string name="multiplayer_nickname_invalid">使用者名稱無效,請在系統 -> 網路中檢查設定</string>
|
||||||
@@ -383,12 +383,12 @@
|
|||||||
<string name="notification_no_directory_link">無法開啟 Eden 目錄</string>
|
<string name="notification_no_directory_link">無法開啟 Eden 目錄</string>
|
||||||
<string name="notification_no_directory_link_description">請使用檔案管理員的側邊面板手動定位到使用者資料夾</string>
|
<string name="notification_no_directory_link_description">請使用檔案管理員的側邊面板手動定位到使用者資料夾</string>
|
||||||
<string name="manage_save_data">管理儲存資料</string>
|
<string name="manage_save_data">管理儲存資料</string>
|
||||||
<string name="manage_save_data_description">導入/導出遊戲的儲存資料\n壓縮檔必須為.zip格式\n壓縮檔中首個子資料夾名稱必須為遊戲ID</string>
|
<string name="manage_save_data_description">匯入/匯出遊戲的儲存資料\n壓縮檔必須為.zip格式\n壓縮檔中首個子資料夾名稱必須為遊戲ID</string>
|
||||||
<string name="import_save_warning">導入儲存資料</string>
|
<string name="import_save_warning">匯入儲存資料</string>
|
||||||
<string name="import_save_warning_description">這將會以提供的檔案覆寫現有的該遊戲儲存資料,您確定要繼續嗎?</string>
|
<string name="import_save_warning_description">這將會以提供的檔案覆蓋現有的該遊戲儲存資料,您確定要繼續嗎?</string>
|
||||||
<string name="save_files_importing">正在導入儲存資料…</string>
|
<string name="save_files_importing">正在匯入儲存資料…</string>
|
||||||
<string name="save_files_exporting">正在導出儲存資料…</string>
|
<string name="save_files_exporting">正在匯出儲存資料…</string>
|
||||||
<string name="save_file_imported_success">已成功導入</string>
|
<string name="save_file_imported_success">已成功匯入</string>
|
||||||
<string name="save_file_invalid_zip_structure">無效的儲存目錄結構</string>
|
<string name="save_file_invalid_zip_structure">無效的儲存目錄結構</string>
|
||||||
<string name="save_file_invalid_zip_structure_description">首個子資料夾名稱必須為遊戲 ID</string>
|
<string name="save_file_invalid_zip_structure_description">首個子資料夾名稱必須為遊戲 ID</string>
|
||||||
<string name="install_firmware">安裝韌體</string>
|
<string name="install_firmware">安裝韌體</string>
|
||||||
@@ -478,13 +478,13 @@
|
|||||||
<string name="licenses_description">這些專案使 Eden Android 版成為可能</string>
|
<string name="licenses_description">這些專案使 Eden Android 版成為可能</string>
|
||||||
<string name="build">組建版本</string>
|
<string name="build">組建版本</string>
|
||||||
<string name="user_data">使用者資料</string>
|
<string name="user_data">使用者資料</string>
|
||||||
<string name="user_data_description">導入/導出所有應用程式資料\n\n導入使用者資料時,現有的使用者資料將被取代!\n\n直接從 Citron 導入使用者資料可能會出現問題,建議手動導入所有所需資料</string>
|
<string name="user_data_description">匯入/匯出所有應用程式資料\n\n匯入使用者資料時,現有的使用者資料將被取代!\n\n直接從 Citron 匯入使用者資料可能會出現問題,建議手動匯入所有所需資料</string>
|
||||||
<string name="exporting_user_data">正在導出使用者資料…</string>
|
<string name="exporting_user_data">正在匯出使用者資料…</string>
|
||||||
<string name="importing_user_data">正在導入使用者資料…</string>
|
<string name="importing_user_data">正在匯入使用者資料…</string>
|
||||||
<string name="invalid_yuzu_backup">無效的 Eden 備份</string>
|
<string name="invalid_yuzu_backup">無效的 Eden 備份</string>
|
||||||
<string name="user_data_export_success">使用者資料導出成功</string>
|
<string name="user_data_export_success">使用者資料匯出成功</string>
|
||||||
<string name="user_data_import_success">使用者資料導入成功</string>
|
<string name="user_data_import_success">使用者資料匯入成功</string>
|
||||||
<string name="user_data_export_cancelled">導出已取消</string>
|
<string name="user_data_export_cancelled">匯出已取消</string>
|
||||||
<string name="user_data_import_failed_description">請確保使用者資料夾位於 zip 壓縮檔的根目錄,並在 config/config.ini 路徑中包含組態檔案後重試</string>
|
<string name="user_data_import_failed_description">請確保使用者資料夾位於 zip 壓縮檔的根目錄,並在 config/config.ini 路徑中包含組態檔案後重試</string>
|
||||||
<!-- General settings strings -->
|
<!-- General settings strings -->
|
||||||
<string name="frame_limit_enable">限制模擬速度</string>
|
<string name="frame_limit_enable">限制模擬速度</string>
|
||||||
@@ -521,7 +521,7 @@
|
|||||||
<string name="custom_cpu_ticks_description">自訂 CPU 時脈,更高的值可能提高效能,但也可能導致遊戲卡死。建議範圍為 77-21000</string>
|
<string name="custom_cpu_ticks_description">自訂 CPU 時脈,更高的值可能提高效能,但也可能導致遊戲卡死。建議範圍為 77-21000</string>
|
||||||
<string name="cpu_ticks">時脈</string>
|
<string name="cpu_ticks">時脈</string>
|
||||||
<string name="memory_layout">記憶體布局</string>
|
<string name="memory_layout">記憶體布局</string>
|
||||||
<string name="memory_layout_description">(實驗性)更改模擬記憶體布局,此設定不會提高效能,但可能有助於使用模組來提高解析度的遊戲的載入。不要在RAM為8GB或更少的手機上使用。若遊戲崩潰請將進階設定裡偵錯選項中的CPU後端改為Dynarmic (JIT)</string>
|
<string name="memory_layout_description">(實驗性)更改模擬記憶體布局,此設定不會提高效能,但可能有助於使用模組來提高解析度的遊戲的載入。不要在 RAM 為 8GB 或更少的手機上使用。若遊戲崩潰請將進階設定裡偵錯選項中的 CPU 後端改為 Dynarmic(JIT)</string>
|
||||||
|
|
||||||
<string name="generate">生成</string>
|
<string name="generate">生成</string>
|
||||||
|
|
||||||
@@ -564,14 +564,12 @@
|
|||||||
<string name="renderer_force_max_clock_description">強制GPU以可能的最大時脈運作(熱溫限制仍會被套用)</string>
|
<string name="renderer_force_max_clock_description">強制GPU以可能的最大時脈運作(熱溫限制仍會被套用)</string>
|
||||||
<string name="renderer_asynchronous_gpu_emulation">GPU 非同步模擬</string>
|
<string name="renderer_asynchronous_gpu_emulation">GPU 非同步模擬</string>
|
||||||
<string name="renderer_asynchronous_gpu_emulation_description">此修改可透過使用 GPU 非同步模擬來提升性能,不過可能會導致圖形問題且會提高在進行時序相關操作時當機的機率</string>
|
<string name="renderer_asynchronous_gpu_emulation_description">此修改可透過使用 GPU 非同步模擬來提升性能,不過可能會導致圖形問題且會提高在進行時序相關操作時當機的機率</string>
|
||||||
<string name="renderer_async_presentation">非同步呈現</string>
|
|
||||||
<string name="renderer_async_presentation_description">此修改可以透過將渲染移至單獨的CPU執行緒來提升性能,但可能會導致圖形問題</string>
|
|
||||||
<string name="renderer_reactive_flushing">使用重新啟用排清</string>
|
<string name="renderer_reactive_flushing">使用重新啟用排清</string>
|
||||||
<string name="renderer_reactive_flushing_description">犧牲效能以改善部分遊戲的轉譯準確度</string>
|
<string name="renderer_reactive_flushing_description">犧牲效能以改善部分遊戲的轉譯準確度</string>
|
||||||
<string name="enable_buffer_history">啟用緩衝區歷史</string>
|
<string name="enable_buffer_history">啟用緩衝區歷史</string>
|
||||||
<string name="enable_buffer_history_description">允許存取先前的緩衝區狀態,此選項可能會改善部分遊戲的渲染品質與效能穩定性</string>
|
<string name="enable_buffer_history_description">允許存取先前的緩衝區狀態,此選項可能會改善部分遊戲的渲染品質與效能穩定性</string>
|
||||||
<string name="use_optimized_vertex_buffers">最佳化頂點緩衝區</string>
|
<string name="use_optimized_vertex_buffers">最佳化頂點緩衝區</string>
|
||||||
<string name="use_optimized_vertex_buffers_description">啟用最佳化的頂點緩衝區綁定,需要安裝 Mesa 26.0+ Turnip drivers/Qualcomm drivers。使用舊版 Turnip drivers 會導致當機(25.3版和更低的版本)</string>
|
<string name="use_optimized_vertex_buffers_description">啟用最佳化的頂點緩衝區綁定,需要安裝 Mesa 26.0+ Turnip drivers/Qualcomm drivers。使用舊版 Turnip drivers 會導致當機(25.3 版或更低的版本)</string>
|
||||||
|
|
||||||
<string name="hacks">修改</string>
|
<string name="hacks">修改</string>
|
||||||
|
|
||||||
@@ -637,7 +635,7 @@
|
|||||||
|
|
||||||
<string name="log">日誌</string>
|
<string name="log">日誌</string>
|
||||||
<string name="flush_by_line">按行寫入偵錯日誌</string>
|
<string name="flush_by_line">按行寫入偵錯日誌</string>
|
||||||
<string name="flush_by_line_description">在寫入一行日誌就立刻更新偵錯記錄,讓程式在當機或閃退時更容易偵錯</string>
|
<string name="flush_by_line_description">寫入一行日誌就立刻更新偵錯記錄,讓程式在當機或畫面凍結時更容易偵錯</string>
|
||||||
<string name="extended_logging">啟用更詳細的日誌記錄</string>
|
<string name="extended_logging">啟用更詳細的日誌記錄</string>
|
||||||
<string name="extended_logging_description">將日誌檔的最大檔案限制從 100MB 提高到 1GB</string>
|
<string name="extended_logging_description">將日誌檔的最大檔案限制從 100MB 提高到 1GB</string>
|
||||||
<string name="log_filter">日誌過濾器</string>
|
<string name="log_filter">日誌過濾器</string>
|
||||||
@@ -740,7 +738,7 @@
|
|||||||
<string name="button_map_description">按下一個按鈕或肩部按鍵/移動搖桿</string>
|
<string name="button_map_description">按下一個按鈕或肩部按鍵/移動搖桿</string>
|
||||||
<string name="map_dpad_direction">將方向鍵配對到 %1$s</string>
|
<string name="map_dpad_direction">將方向鍵配對到 %1$s</string>
|
||||||
<string name="map_control">配對到 %1$s</string>
|
<string name="map_control">配對到 %1$s</string>
|
||||||
<string name="failed_to_load_profile">導入設定檔失敗</string>
|
<string name="failed_to_load_profile">匯入設定檔失敗</string>
|
||||||
<string name="failed_to_save_profile">儲存設定檔失敗</string>
|
<string name="failed_to_save_profile">儲存設定檔失敗</string>
|
||||||
<string name="reset_mapping">重設控制器配對</string>
|
<string name="reset_mapping">重設控制器配對</string>
|
||||||
<string name="reset_mapping_description">您確定要將此控制器的所有配對重設為預設值?此動作無法復原</string>
|
<string name="reset_mapping_description">您確定要將此控制器的所有配對重設為預設值?此動作無法復原</string>
|
||||||
@@ -761,21 +759,21 @@
|
|||||||
<string name="auto">自動</string>
|
<string name="auto">自動</string>
|
||||||
<string name="submit">提交</string>
|
<string name="submit">提交</string>
|
||||||
<string name="string_null">無</string>
|
<string name="string_null">無</string>
|
||||||
<string name="string_import">導入</string>
|
<string name="string_import">匯入</string>
|
||||||
<string name="export">導出</string>
|
<string name="export">匯出</string>
|
||||||
<string name="export_failed">導出失敗</string>
|
<string name="export_failed">匯出失敗</string>
|
||||||
<string name="import_failed">導入失敗</string>
|
<string name="import_failed">匯入失敗</string>
|
||||||
<string name="cancelling">正在取消</string>
|
<string name="cancelling">正在取消</string>
|
||||||
<string name="install">安裝</string>
|
<string name="install">安裝</string>
|
||||||
<string name="fetch">下載</string>
|
<string name="fetch">下載</string>
|
||||||
<string name="delete">刪除</string>
|
<string name="delete">刪除</string>
|
||||||
<string name="edit">編輯</string>
|
<string name="edit">編輯</string>
|
||||||
<string name="import_success">已成功導入</string>
|
<string name="import_success">已成功匯入</string>
|
||||||
<string name="export_success">已成功導出</string>
|
<string name="export_success">已成功匯出</string>
|
||||||
<string name="start">開始</string>
|
<string name="start">開始</string>
|
||||||
<string name="global">全域設定</string>
|
<string name="global">全域設定</string>
|
||||||
<string name="custom">個別遊戲設定</string>
|
<string name="custom">個別遊戲設定</string>
|
||||||
<string name="import_complete">導入完成</string>
|
<string name="import_complete">匯入完成</string>
|
||||||
<string name="use_global_setting">使用全域設定</string>
|
<string name="use_global_setting">使用全域設定</string>
|
||||||
<string name="operation_completed_successfully">操作已成功完成</string>
|
<string name="operation_completed_successfully">操作已成功完成</string>
|
||||||
<string name="confirm">確認</string>
|
<string name="confirm">確認</string>
|
||||||
@@ -838,7 +836,7 @@
|
|||||||
<string name="save_migration_complete">成功遷移儲存資料</string>
|
<string name="save_migration_complete">成功遷移儲存資料</string>
|
||||||
<string name="save_migration_failed">儲存資料遷移失敗</string>
|
<string name="save_migration_failed">儲存資料遷移失敗</string>
|
||||||
<string name="save_directory_set">存檔目錄設定完成</string>
|
<string name="save_directory_set">存檔目錄設定完成</string>
|
||||||
<string name="destination_has_saves">所選取的位置已有儲存資料。是否要取代該檔案?</string>
|
<string name="destination_has_saves">所選取的位置已有儲存資料,是否要取代該檔案?</string>
|
||||||
<string name="all_files_permission_required">若要自訂路徑必須授予所有檔案的存取權</string>
|
<string name="all_files_permission_required">若要自訂路徑必須授予所有檔案的存取權</string>
|
||||||
<string name="grant_permission">授予權限</string>
|
<string name="grant_permission">授予權限</string>
|
||||||
<string name="custom_nand_directory">NAND 路徑</string>
|
<string name="custom_nand_directory">NAND 路徑</string>
|
||||||
@@ -875,13 +873,13 @@
|
|||||||
<string name="hours_must_be_between_0_and_9999">必須介於 0 到 9999 小時之間</string>
|
<string name="hours_must_be_between_0_and_9999">必須介於 0 到 9999 小時之間</string>
|
||||||
<string name="minutes_must_be_between_0_and_59">必須介於 0 到 59 分鐘之間</string>
|
<string name="minutes_must_be_between_0_and_59">必須介於 0 到 59 分鐘之間</string>
|
||||||
<string name="seconds_must_be_between_0_and_59">必須介於 0 到 59 秒之間</string>
|
<string name="seconds_must_be_between_0_and_59">必須介於 0 到 59 秒之間</string>
|
||||||
<string name="playtime_updated_successfully">成功更新遊玩時間</string>
|
<string name="playtime_updated_successfully">成功修改遊玩時間</string>
|
||||||
<string name="clear_shader_cache">清除著色器快取</string>
|
<string name="clear_shader_cache">清除著色器快取</string>
|
||||||
<string name="clear_shader_cache_description">清除此遊戲所有已編譯的著色器快取</string>
|
<string name="clear_shader_cache_description">清除此遊戲所有已編譯的著色器快取</string>
|
||||||
<string name="clear_shader_cache_warning_description">重新編譯著色器快取的過程中可能會導致遊戲有些微卡頓</string>
|
<string name="clear_shader_cache_warning_description">重新編譯著色器快取的過程中可能會導致遊戲有些微卡頓</string>
|
||||||
<string name="cleared_shaders_successfully">著色器快取已成功清除</string>
|
<string name="cleared_shaders_successfully">著色器快取已成功清除</string>
|
||||||
<string name="driver_shader_wipe_dialog_title">已刪除著色器快取</string>
|
<string name="driver_shader_wipe_dialog_title">已刪除著色器快取</string>
|
||||||
<string name="driver_shader_wipe_dialog_message">為了維持Vulkan管線驗證,Eden已自動清除所有著色器快取。為了預防閃退和圖形錯誤,這是必須的動作。因為著色器需要重新編譯,您在遊戲過程中可能會遇到些微卡頓的情況</string>
|
<string name="driver_shader_wipe_dialog_message">為了維持Vulkan管線驗證,Eden 已自動清除所有著色器快取,若要預防閃退和圖形錯誤,這是必須的動作。因為著色器需要重新編譯,您在遊戲過程中可能會遇到些微卡頓的情況</string>
|
||||||
<string name="addons_game">附加元件:%1$s</string>
|
<string name="addons_game">附加元件:%1$s</string>
|
||||||
<string name="save_data">儲存資料</string>
|
<string name="save_data">儲存資料</string>
|
||||||
<string name="save_data_description">管理此遊戲的儲存資料</string>
|
<string name="save_data_description">管理此遊戲的儲存資料</string>
|
||||||
@@ -913,8 +911,8 @@
|
|||||||
<string name="verify_no_result_description">未完成文件完整性檢查</string>
|
<string name="verify_no_result_description">未完成文件完整性檢查</string>
|
||||||
<string name="verification_failed_for">以下文件的完整性驗證失敗:\n %1$s</string>
|
<string name="verification_failed_for">以下文件的完整性驗證失敗:\n %1$s</string>
|
||||||
<string name="share_game_settings">分享設定</string>
|
<string name="share_game_settings">分享設定</string>
|
||||||
<string name="import_config">導入設定</string>
|
<string name="import_config">匯入設定</string>
|
||||||
<string name="export_config">導出設定</string>
|
<string name="export_config">匯出設定</string>
|
||||||
<string name="share_config_failed">分享設定失敗</string>
|
<string name="share_config_failed">分享設定失敗</string>
|
||||||
|
|
||||||
<!-- ROM loading errors -->
|
<!-- ROM loading errors -->
|
||||||
@@ -935,7 +933,7 @@
|
|||||||
<string name="custom_settings_failed_message">無法套用自訂設定 %1$s: %2$s</string>
|
<string name="custom_settings_failed_message">無法套用自訂設定 %1$s: %2$s</string>
|
||||||
<string name="launch_with_default_settings">使用預設設定啟動</string>
|
<string name="launch_with_default_settings">使用預設設定啟動</string>
|
||||||
<string name="launch_cancelled">啟動已取消</string>
|
<string name="launch_cancelled">啟動已取消</string>
|
||||||
<string name="custom_settings_failure_reasons">無法套用請求的設定。這可能是由於缺少GPU驅動程式或設定問題。</string>
|
<string name="custom_settings_failure_reasons">無法套用請求的設定,這可能是由於缺少 GPU 驅動程式或設定問題</string>
|
||||||
<string name="custom_settings_applied">已套用自訂設定</string>
|
<string name="custom_settings_applied">已套用自訂設定</string>
|
||||||
<string name="launching_game">正在啟動 %1$s...</string>
|
<string name="launching_game">正在啟動 %1$s...</string>
|
||||||
<string name="failed_to_initialize_game">無法初始化遊戲</string>
|
<string name="failed_to_initialize_game">無法初始化遊戲</string>
|
||||||
@@ -956,10 +954,10 @@
|
|||||||
<!-- Driver strings -->
|
<!-- Driver strings -->
|
||||||
<string name="driver_not_found">未安裝所需的驅動程式: %s</string>
|
<string name="driver_not_found">未安裝所需的驅動程式: %s</string>
|
||||||
<string name="invalid_driver_file">無效的驅動程式檔案: %s</string>
|
<string name="invalid_driver_file">無效的驅動程式檔案: %s</string>
|
||||||
<string name="network_unavailable">無可用網路連線。請檢查您的網路連線並重試</string>
|
<string name="network_unavailable">無可用網路連線,請檢查您的網路連線並重試</string>
|
||||||
<string name="driver_missing_title">需要 GPU 驅動程式</string>
|
<string name="driver_missing_title">需要 GPU 驅動程式</string>
|
||||||
<string name="driver_missing_message">這個遊戲的設定需要 \"%s\"驅動程式,而它並沒有安裝在您的裝置上\n\n要下載並安裝此驅動程式嗎?</string>
|
<string name="driver_missing_message">這個遊戲的設定需要 \"%s\"驅動程式,而它並沒有安裝在您的裝置上\n\n要下載並安裝此驅動程式嗎?</string>
|
||||||
<string name="driver_download_cancelled">驅動程式下載已取消。沒有所需的驅動程式無法啟動遊戲。</string>
|
<string name="driver_download_cancelled">驅動程式下載已取消,沒有所需的驅動程式無法啟動遊戲</string>
|
||||||
<string name="download">下載</string>
|
<string name="download">下載</string>
|
||||||
|
|
||||||
<!-- Emulation Menu -->
|
<!-- Emulation Menu -->
|
||||||
@@ -994,7 +992,7 @@
|
|||||||
<string name="amiibo_not_valid">您所選擇的並不是正確的 amiibo 金鑰檔</string>
|
<string name="amiibo_not_valid">您所選擇的並不是正確的 amiibo 金鑰檔</string>
|
||||||
<string name="amiibo_in_use">選擇的檔案已在使用中</string>
|
<string name="amiibo_in_use">選擇的檔案已在使用中</string>
|
||||||
<string name="amiibo_unknown_error">發生未知的錯誤</string>
|
<string name="amiibo_unknown_error">發生未知的錯誤</string>
|
||||||
<string name="amiibo_load_success">已導入Amiibo</string>
|
<string name="amiibo_load_success">Amiibo 已匯入</string>
|
||||||
|
|
||||||
|
|
||||||
<!-- Software keyboard -->
|
<!-- Software keyboard -->
|
||||||
@@ -1023,9 +1021,9 @@
|
|||||||
<string name="region_taiwan">臺灣</string>
|
<string name="region_taiwan">臺灣</string>
|
||||||
|
|
||||||
<!-- Memory Layouts -->
|
<!-- Memory Layouts -->
|
||||||
<string name="memory_4gb">4GB (推薦)</string>
|
<string name="memory_4gb">4GB(推薦)</string>
|
||||||
<string name="memory_6gb">6GB (不穩定)</string>
|
<string name="memory_6gb">6GB(不穩定)</string>
|
||||||
<string name="memory_8gb">8GB (不穩定)</string>
|
<string name="memory_8gb">8GB(不穩定)</string>
|
||||||
|
|
||||||
<!-- CPU clock levels -->
|
<!-- CPU clock levels -->
|
||||||
<string name="clock_normal">正常</string>
|
<string name="clock_normal">正常</string>
|
||||||
@@ -1075,20 +1073,26 @@
|
|||||||
|
|
||||||
<!-- Renderer Accuracy -->
|
<!-- Renderer Accuracy -->
|
||||||
<string name="renderer_accuracy_low">效能</string>
|
<string name="renderer_accuracy_low">效能</string>
|
||||||
<string name="renderer_accuracy_high">準確</string>
|
<string name="renderer_accuracy_high">品質</string>
|
||||||
|
|
||||||
<!-- DMA Accuracy -->
|
<!-- DMA Accuracy -->
|
||||||
<string name="dma_accuracy_default">預設</string>
|
<string name="dma_accuracy_default">預設</string>
|
||||||
<string name="dma_accuracy_unsafe">不穩定</string>
|
<string name="dma_accuracy_unsafe">不穩定</string>
|
||||||
<string name="dma_accuracy_safe">穩定</string>
|
<string name="dma_accuracy_safe">穩定</string>
|
||||||
|
|
||||||
|
<!-- GPU Fence Behavior -->
|
||||||
|
<string name="gpu_fence_behavior_default">預設</string>
|
||||||
|
<string name="gpu_fence_behavior_immediate">即時</string>
|
||||||
|
<string name="gpu_fence_behavior_balanced">平衡</string>
|
||||||
|
<string name="gpu_fence_behavior_accurate">準確</string>
|
||||||
|
|
||||||
<string name="vram_usage_conservative">保守</string>
|
<string name="vram_usage_conservative">保守</string>
|
||||||
<string name="vram_usage_aggressive">積極</string>
|
<string name="vram_usage_aggressive">積極</string>
|
||||||
|
|
||||||
<!-- Renderer VSync -->
|
<!-- Renderer VSync -->
|
||||||
<string name="renderer_vsync_immediate">即時 (關閉)</string>
|
<string name="renderer_vsync_immediate">即時(關閉)</string>
|
||||||
<string name="renderer_vsync_mailbox">Mailbox</string>
|
<string name="renderer_vsync_mailbox">Mailbox</string>
|
||||||
<string name="renderer_vsync_fifo">FIFO (開啟)</string>
|
<string name="renderer_vsync_fifo">FIFO(開啟)</string>
|
||||||
<string name="renderer_vsync_fifo_relaxed">FIFO 寬鬆</string>
|
<string name="renderer_vsync_fifo_relaxed">FIFO 寬鬆</string>
|
||||||
|
|
||||||
<!-- Anti-Aliasing -->
|
<!-- Anti-Aliasing -->
|
||||||
@@ -1110,6 +1114,18 @@
|
|||||||
<string name="cpu_accuracy_paranoid">不合理</string>
|
<string name="cpu_accuracy_paranoid">不合理</string>
|
||||||
<string name="cpu_accuracy_debugging">偵錯</string>
|
<string name="cpu_accuracy_debugging">偵錯</string>
|
||||||
|
|
||||||
|
<!-- Freedreno Settings -->
|
||||||
|
<string name="freedreno_settings_title">Freedreno 設定</string>
|
||||||
|
<string name="gpu_driver_settings">GPU 驅動程式設定</string>
|
||||||
|
<string name="freedreno_presets">預先提供的設定檔</string>
|
||||||
|
<string name="freedreno_current_settings">目前使用的設定</string>
|
||||||
|
<string name="freedreno_debug">進階設定</string>
|
||||||
|
<string name="freedreno_var_name">變數名稱 (例如: TU_DEBUG)</string>
|
||||||
|
<string name="freedreno_var_value">變數值</string>
|
||||||
|
<string name="freedreno_add_variable">新增變數</string>
|
||||||
|
<string name="freedreno_clear_all">全部清除</string>
|
||||||
|
<string name="freedreno_saved">Freedreno 設定已儲存</string>
|
||||||
|
<string name="freedreno_cleared_all">已清除所有 Freedreno 變數</string>
|
||||||
<!-- Gamepad Buttons -->
|
<!-- Gamepad Buttons -->
|
||||||
<string name="gamepad_d_pad">方向鍵</string>
|
<string name="gamepad_d_pad">方向鍵</string>
|
||||||
<string name="gamepad_left_stick">左搖桿</string>
|
<string name="gamepad_left_stick">左搖桿</string>
|
||||||
|
|||||||
@@ -580,8 +580,6 @@
|
|||||||
<string name="renderer_force_max_clock_description">Forces the GPU to run at the maximum possible clocks (thermal constraints will still be applied).</string>
|
<string name="renderer_force_max_clock_description">Forces the GPU to run at the maximum possible clocks (thermal constraints will still be applied).</string>
|
||||||
<string name="renderer_asynchronous_gpu_emulation">GPU async emulation</string>
|
<string name="renderer_asynchronous_gpu_emulation">GPU async emulation</string>
|
||||||
<string name="renderer_asynchronous_gpu_emulation_description">This hack can increase performance by running GPU emulation asynchronously at the cost of graphical issues and increased crash rates by timing-related operations.</string>
|
<string name="renderer_asynchronous_gpu_emulation_description">This hack can increase performance by running GPU emulation asynchronously at the cost of graphical issues and increased crash rates by timing-related operations.</string>
|
||||||
<string name="renderer_async_presentation">Asynchronous presentation</string>
|
|
||||||
<string name="renderer_async_presentation_description">This hack can increase performance by moving presentation to a separate CPU thread at the cost of graphical issues.</string>
|
|
||||||
<string name="renderer_reactive_flushing">Use reactive flushing</string>
|
<string name="renderer_reactive_flushing">Use reactive flushing</string>
|
||||||
<string name="renderer_reactive_flushing_description">Improves rendering accuracy in some games at the cost of performance.</string>
|
<string name="renderer_reactive_flushing_description">Improves rendering accuracy in some games at the cost of performance.</string>
|
||||||
<string name="enable_buffer_history">Enable buffer history</string>
|
<string name="enable_buffer_history">Enable buffer history</string>
|
||||||
@@ -595,10 +593,6 @@
|
|||||||
<string name="fast_gpu_time_description">Makes the game believe GPU work finishes faster than it does, so it stops lowering resolution and render distance to fit the Switch\'s clocks.</string>
|
<string name="fast_gpu_time_description">Makes the game believe GPU work finishes faster than it does, so it stops lowering resolution and render distance to fit the Switch\'s clocks.</string>
|
||||||
<string name="skip_cpu_inner_invalidation">Skip CPU Inner Invalidation</string>
|
<string name="skip_cpu_inner_invalidation">Skip CPU Inner Invalidation</string>
|
||||||
<string name="skip_cpu_inner_invalidation_description">Skips certain CPU-side cache invalidations during memory updates, reducing CPU usage and improving it\'s performance. This may cause glitches or crashes on some games.</string>
|
<string name="skip_cpu_inner_invalidation_description">Skips certain CPU-side cache invalidations during memory updates, reducing CPU usage and improving it\'s performance. This may cause glitches or crashes on some games.</string>
|
||||||
<string name="nce_invalidation_gpu_readback">GPU Readback Before NCE Invalidation</string>
|
|
||||||
<string name="nce_invalidation_gpu_readback_description">Temporary. Fix Ender Magnolia (and other UE titles) crashes. May reduce performance. Improvement in progress.</string>
|
|
||||||
<string name="nce_runtime_nro_patch">Patch Runtime NROs for NCE</string>
|
|
||||||
<string name="nce_runtime_nro_patch_description">Temporary. Fix Biomutant boot. May impact performance. Improvement in progress.</string>
|
|
||||||
<string name="fix_bloom_effects">Fix Bloom Effects</string>
|
<string name="fix_bloom_effects">Fix Bloom Effects</string>
|
||||||
<string name="fix_bloom_effects_description">Reduces bloom blur in LA/EOW (Adreno A6XX - A7XX/ Turnip), removes bloom in Burnout. Warning: may cause graphical artifacts in other games.</string>
|
<string name="fix_bloom_effects_description">Reduces bloom blur in LA/EOW (Adreno A6XX - A7XX/ Turnip), removes bloom in Burnout. Warning: may cause graphical artifacts in other games.</string>
|
||||||
<string name="emulate_bgr565">Emulate BGR565</string>
|
<string name="emulate_bgr565">Emulate BGR565</string>
|
||||||
|
|||||||
@@ -14,21 +14,6 @@
|
|||||||
#include "core/core_timing.h"
|
#include "core/core_timing.h"
|
||||||
#include "core/hle/kernel/k_event.h"
|
#include "core/hle/kernel/k_event.h"
|
||||||
|
|
||||||
// See texture_cache/util.h
|
|
||||||
template<typename T, size_t N>
|
|
||||||
#if BOOST_VERSION >= 108100 || __GNUC__ > 12
|
|
||||||
[[nodiscard]] boost::container::static_vector<T, N> FixStaticVectorADL(const boost::container::static_vector<T, N>& v) {
|
|
||||||
return v;
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
[[nodiscard]] std::vector<T> FixStaticVectorADL(const boost::container::static_vector<T, N>& v) {
|
|
||||||
std::vector<T> u;
|
|
||||||
for (auto const& e : v)
|
|
||||||
u.push_back(e);
|
|
||||||
return u;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
namespace AudioCore::AudioIn {
|
namespace AudioCore::AudioIn {
|
||||||
|
|
||||||
System::System(Core::System& system_, Kernel::KEvent* event_, const size_t session_id_)
|
System::System(Core::System& system_, Kernel::KEvent* event_, const size_t session_id_)
|
||||||
@@ -110,7 +95,7 @@ Result System::Start() {
|
|||||||
|
|
||||||
boost::container::static_vector<AudioBuffer, BufferCount> buffers_to_flush{};
|
boost::container::static_vector<AudioBuffer, BufferCount> buffers_to_flush{};
|
||||||
buffers.RegisterBuffers(buffers_to_flush);
|
buffers.RegisterBuffers(buffers_to_flush);
|
||||||
session->AppendBuffers(FixStaticVectorADL(buffers_to_flush));
|
session->AppendBuffers(buffers_to_flush);
|
||||||
session->SetRingSize(static_cast<u32>(buffers_to_flush.size()));
|
session->SetRingSize(static_cast<u32>(buffers_to_flush.size()));
|
||||||
|
|
||||||
return ResultSuccess;
|
return ResultSuccess;
|
||||||
@@ -155,7 +140,7 @@ void System::RegisterBuffers() {
|
|||||||
if (state == State::Started) {
|
if (state == State::Started) {
|
||||||
boost::container::static_vector<AudioBuffer, BufferCount> registered_buffers{};
|
boost::container::static_vector<AudioBuffer, BufferCount> registered_buffers{};
|
||||||
buffers.RegisterBuffers(registered_buffers);
|
buffers.RegisterBuffers(registered_buffers);
|
||||||
session->AppendBuffers(FixStaticVectorADL(registered_buffers));
|
session->AppendBuffers(registered_buffers);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+12
-19
@@ -171,8 +171,13 @@ struct Values {
|
|||||||
SwitchableSetting<bool> enable_overlay{linkage, false, "enable_overlay", Category::LibraryApplet};
|
SwitchableSetting<bool> enable_overlay{linkage, false, "enable_overlay", Category::LibraryApplet};
|
||||||
|
|
||||||
// Audio
|
// Audio
|
||||||
SwitchableSetting<AudioEngine> sink_id{linkage, AudioEngine::Auto, "output_engine",
|
SwitchableSetting<AudioEngine> sink_id{linkage,
|
||||||
Category::Audio, Specialization::RuntimeList};
|
#ifdef __ANDROID__
|
||||||
|
AudioEngine::Sdl3,
|
||||||
|
#else
|
||||||
|
AudioEngine::Auto,
|
||||||
|
#endif
|
||||||
|
"output_engine", Category::Audio, Specialization::RuntimeList};
|
||||||
SwitchableSetting<std::string> audio_output_device_id{
|
SwitchableSetting<std::string> audio_output_device_id{
|
||||||
linkage, "auto", "output_device", Category::Audio, Specialization::RuntimeList};
|
linkage, "auto", "output_device", Category::Audio, Specialization::RuntimeList};
|
||||||
SwitchableSetting<std::string> audio_input_device_id{
|
SwitchableSetting<std::string> audio_input_device_id{
|
||||||
@@ -472,13 +477,8 @@ struct Values {
|
|||||||
SwitchableSetting<bool> frame_gen_dump_flow{linkage, false, "frame_gen_dump_flow",
|
SwitchableSetting<bool> frame_gen_dump_flow{linkage, false, "frame_gen_dump_flow",
|
||||||
Category::Renderer};
|
Category::Renderer};
|
||||||
|
|
||||||
SwitchableSetting<bool> use_asynchronous_gpu_emulation{linkage,
|
SwitchableSetting<bool> use_asynchronous_gpu_emulation{linkage, true, "use_asynchronous_gpu_emulation",
|
||||||
#ifdef __ANDROID__
|
Category::Renderer};
|
||||||
false,
|
|
||||||
#else
|
|
||||||
true,
|
|
||||||
#endif
|
|
||||||
"use_asynchronous_gpu_emulation", Category::Renderer};
|
|
||||||
// *nix platforms may have issues with the borderless windowed fullscreen mode.
|
// *nix platforms may have issues with the borderless windowed fullscreen mode.
|
||||||
// Default to exclusive fullscreen on these platforms for now.
|
// Default to exclusive fullscreen on these platforms for now.
|
||||||
SwitchableSetting<FullscreenMode, true> fullscreen_mode{linkage,
|
SwitchableSetting<FullscreenMode, true> fullscreen_mode{linkage,
|
||||||
@@ -537,7 +537,7 @@ struct Values {
|
|||||||
"vram_usage_mode",
|
"vram_usage_mode",
|
||||||
Category::RendererAdvanced};
|
Category::RendererAdvanced};
|
||||||
|
|
||||||
SwitchableSetting<NvdecEmulation> nvdec_emulation{linkage, NvdecEmulation::Gpu,
|
SwitchableSetting<NvdecEmulation> nvdec_emulation{linkage, NvdecEmulation::Cpu,
|
||||||
"nvdec_emulation", Category::RendererAdvanced};
|
"nvdec_emulation", Category::RendererAdvanced};
|
||||||
|
|
||||||
SwitchableSetting<AnisotropyMode, true> max_anisotropy{linkage,
|
SwitchableSetting<AnisotropyMode, true> max_anisotropy{linkage,
|
||||||
@@ -639,13 +639,9 @@ struct Values {
|
|||||||
Specialization::Default,
|
Specialization::Default,
|
||||||
true,
|
true,
|
||||||
true};
|
true};
|
||||||
SwitchableSetting<bool> nce_invalidation_gpu_readback{
|
|
||||||
linkage, false, "nce_invalidation_gpu_readback", Category::RendererHacks};
|
|
||||||
SwitchableSetting<bool> nce_runtime_nro_patch{
|
|
||||||
linkage, false, "nce_runtime_nro_patch", Category::RendererHacks};
|
|
||||||
SwitchableSetting<bool> async_presentation{linkage,
|
SwitchableSetting<bool> async_presentation{linkage,
|
||||||
#ifdef __ANDROID__
|
#ifdef __ANDROID__
|
||||||
false,
|
true,
|
||||||
#else
|
#else
|
||||||
false,
|
false,
|
||||||
#endif
|
#endif
|
||||||
@@ -657,9 +653,6 @@ struct Values {
|
|||||||
SwitchableSetting<bool> emulate_bgr565{linkage, false, "emulate_bgr565",
|
SwitchableSetting<bool> emulate_bgr565{linkage, false, "emulate_bgr565",
|
||||||
Category::RendererHacks};
|
Category::RendererHacks};
|
||||||
|
|
||||||
SwitchableSetting<bool> rescale_hack{linkage, false, "rescale_hack",
|
|
||||||
Category::RendererHacks};
|
|
||||||
|
|
||||||
SwitchableSetting<bool> use_asynchronous_shaders{linkage, false, "use_asynchronous_shaders",
|
SwitchableSetting<bool> use_asynchronous_shaders{linkage, false, "use_asynchronous_shaders",
|
||||||
Category::RendererHacks};
|
Category::RendererHacks};
|
||||||
|
|
||||||
@@ -936,7 +929,7 @@ struct Values {
|
|||||||
// Network
|
// Network
|
||||||
Setting<std::string> network_interface{linkage, std::string(), "network_interface",
|
Setting<std::string> network_interface{linkage, std::string(), "network_interface",
|
||||||
Category::Network};
|
Category::Network};
|
||||||
SwitchableSetting<bool> airplane_mode{linkage, false, "airplane_mode", Category::Network};
|
SwitchableSetting<bool> airplane_mode{linkage, true, "airplane_mode", Category::Network};
|
||||||
|
|
||||||
// WebService
|
// WebService
|
||||||
Setting<std::string> web_api_url{linkage, "api.ynet-fun.xyz", "web_api_url",
|
Setting<std::string> web_api_url{linkage, "api.ynet-fun.xyz", "web_api_url",
|
||||||
|
|||||||
@@ -1,3 +1,6 @@
|
|||||||
|
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
@@ -46,4 +49,10 @@ std::vector<u8> DecompressDataZSTD(std::span<const u8> compressed) {
|
|||||||
return decompressed;
|
return decompressed;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::size_t DecompressDataZSTD(std::span<const u8> compressed, std::span<u8> output) {
|
||||||
|
const std::size_t result =
|
||||||
|
ZSTD_decompress(output.data(), output.size(), compressed.data(), compressed.size());
|
||||||
|
return ZSTD_isError(result) ? 0 : result;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace Common::Compression
|
} // namespace Common::Compression
|
||||||
|
|||||||
@@ -1,3 +1,6 @@
|
|||||||
|
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
@@ -42,4 +45,15 @@ namespace Common::Compression {
|
|||||||
*/
|
*/
|
||||||
[[nodiscard]] std::vector<u8> DecompressDataZSTD(std::span<const u8> compressed);
|
[[nodiscard]] std::vector<u8> DecompressDataZSTD(std::span<const u8> compressed);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decompresses a single Zstandard frame into a caller-provided buffer of known size.
|
||||||
|
*
|
||||||
|
* @param compressed the compressed source memory region.
|
||||||
|
* @param output the destination buffer. Its size is the maximum number of bytes to write.
|
||||||
|
*
|
||||||
|
* @return the number of bytes written, or 0 if the frame is invalid or doesn't fit. An empty
|
||||||
|
* frame also returns 0.
|
||||||
|
*/
|
||||||
|
[[nodiscard]] std::size_t DecompressDataZSTD(std::span<const u8> compressed, std::span<u8> output);
|
||||||
|
|
||||||
} // namespace Common::Compression
|
} // namespace Common::Compression
|
||||||
|
|||||||
@@ -33,6 +33,8 @@ add_library(core STATIC
|
|||||||
crypto/encryption_layer.h
|
crypto/encryption_layer.h
|
||||||
crypto/key_manager.cpp
|
crypto/key_manager.cpp
|
||||||
crypto/key_manager.h
|
crypto/key_manager.h
|
||||||
|
crypto/ncz_decompressor.cpp
|
||||||
|
crypto/ncz_decompressor.h
|
||||||
crypto/partition_data_manager.cpp
|
crypto/partition_data_manager.cpp
|
||||||
crypto/partition_data_manager.h
|
crypto/partition_data_manager.h
|
||||||
crypto/xts_encryption_layer.cpp
|
crypto/xts_encryption_layer.cpp
|
||||||
|
|||||||
@@ -369,8 +369,8 @@ void ArmNce::SetContext(const Kernel::Svc::ThreadContext& ctx) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void ArmNce::SignalInterrupt(Kernel::KThread* thread) {
|
void ArmNce::SignalInterrupt(Kernel::KThread* thread) {
|
||||||
// Add break loop condition.
|
constexpr u64 break_loop = static_cast<u64>(HaltReason::BreakLoop);
|
||||||
m_guest_ctx.esr_el1.fetch_or(static_cast<u64>(HaltReason::BreakLoop));
|
if ((m_guest_ctx.esr_el1.fetch_or(break_loop) & break_loop) != 0) return;
|
||||||
|
|
||||||
auto* params = &thread->GetNativeExecutionParameters();
|
auto* params = &thread->GetNativeExecutionParameters();
|
||||||
LockThreadParameters(params);
|
LockThreadParameters(params);
|
||||||
|
|||||||
@@ -1,3 +1,6 @@
|
|||||||
|
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
/* SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project */
|
/* SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project */
|
||||||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||||
|
|
||||||
@@ -5,9 +8,22 @@
|
|||||||
|
|
||||||
#define __ASSEMBLY__
|
#define __ASSEMBLY__
|
||||||
|
|
||||||
|
// Newer Linux UAPI headers check __ASSEMBLER__ instead of __ASSEMBLY__ to hide C declarations.
|
||||||
|
// Without it, asm-generic/signal.h redeclares sigset_t and clashes with glibc's. Only define it
|
||||||
|
// around these includes, since glibc headers also look at __ASSEMBLER__.
|
||||||
|
#ifndef __ASSEMBLER__
|
||||||
|
#define __ASSEMBLER__
|
||||||
|
#define EDEN_NCE_UNDEF_ASSEMBLER
|
||||||
|
#endif
|
||||||
|
|
||||||
#include <asm-generic/signal.h>
|
#include <asm-generic/signal.h>
|
||||||
#include <asm-generic/unistd.h>
|
#include <asm-generic/unistd.h>
|
||||||
|
|
||||||
|
#ifdef EDEN_NCE_UNDEF_ASSEMBLER
|
||||||
|
#undef __ASSEMBLER__
|
||||||
|
#undef EDEN_NCE_UNDEF_ASSEMBLER
|
||||||
|
#endif
|
||||||
|
|
||||||
#define ReturnToRunCodeByExceptionLevelChangeSignal SIGUSR2
|
#define ReturnToRunCodeByExceptionLevelChangeSignal SIGUSR2
|
||||||
#define BreakFromRunCodeSignal SIGURG
|
#define BreakFromRunCodeSignal SIGURG
|
||||||
#define GuestAccessFaultSignal SIGSEGV
|
#define GuestAccessFaultSignal SIGSEGV
|
||||||
|
|||||||
@@ -45,6 +45,23 @@ Patcher::Patcher() : c(m_patch_instructions), c_pre(m_patch_instructions_pre) {
|
|||||||
WriteLoadContext(c_pre);
|
WriteLoadContext(c_pre);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Patcher::Patcher(std::span<const u8> text) : c(m_patch_instructions), c_pre(m_patch_instructions_pre) {
|
||||||
|
modules.emplace_back();
|
||||||
|
curr_patch = &modules.back();
|
||||||
|
const auto words = std::span<const u32>{reinterpret_cast<const u32*>(text.data()), text.size() / sizeof(u32)};
|
||||||
|
for (u32 i = ModuleCodeIndex; i < static_cast<u32>(words.size()); ++i) {
|
||||||
|
const uintptr_t offset = i * sizeof(u32);
|
||||||
|
if (auto mrs = MRS{words[i]}; mrs.Verify() && mrs.GetRt() != 31 && (mrs.GetSystemReg() == TpidrEl0 || mrs.GetSystemReg() == TpidrroEl0)) {
|
||||||
|
BranchToPatch(offset);
|
||||||
|
const auto src_reg = mrs.GetSystemReg() == TpidrroEl0 ? oaknut::SystemReg::TPIDRRO_EL0 : oaknut::SystemReg::TPIDR_EL0;
|
||||||
|
WriteMrsHandler(offset + sizeof(u32), oaknut::XReg{static_cast<int>(mrs.GetRt())}, src_reg);
|
||||||
|
} else if (auto msr = MSR{words[i]}; msr.Verify() && msr.GetSystemReg() == TpidrEl0) {
|
||||||
|
BranchToPatch(offset);
|
||||||
|
WriteMsrHandler(offset + sizeof(u32), oaknut::XReg{static_cast<int>(msr.GetRt())});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Patcher::~Patcher() = default;
|
Patcher::~Patcher() = default;
|
||||||
|
|
||||||
bool Patcher::PatchText(std::span<const u8> program_image, const Kernel::CodeSet::Segment& code) {
|
bool Patcher::PatchText(std::span<const u8> program_image, const Kernel::CodeSet::Segment& code) {
|
||||||
@@ -182,6 +199,31 @@ bool Patcher::PatchText(std::span<const u8> program_image, const Kernel::CodeSet
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::span<const u32> Patcher::RelocateRuntimeTls(
|
||||||
|
s64 patch_offset, std::vector<InstructionPatch>& out_patches) {
|
||||||
|
const auto& patch = modules.front();
|
||||||
|
const auto can_branch = [](s64 offset) {
|
||||||
|
return offset % 4 == 0 && offset >= -static_cast<s64>(MaxRelativeBranch) && offset < static_cast<s64>(MaxRelativeBranch);
|
||||||
|
};
|
||||||
|
out_patches.clear();
|
||||||
|
out_patches.reserve(patch.m_branch_to_patch_relocations.size());
|
||||||
|
for (const auto& rel : patch.m_branch_to_patch_relocations) {
|
||||||
|
const s64 offset = patch_offset + rel.patch_offset - static_cast<s64>(rel.module_offset);
|
||||||
|
if (!can_branch(offset)) {return {};}
|
||||||
|
u32 replacement;
|
||||||
|
oaknut::CodeGenerator rc{&replacement};
|
||||||
|
rc.B(offset);
|
||||||
|
out_patches.push_back({rel.module_offset, replacement});
|
||||||
|
}
|
||||||
|
for (const auto& rel : patch.m_branch_to_module_relocations) {
|
||||||
|
const s64 offset = static_cast<s64>(rel.module_offset) - patch_offset - rel.patch_offset;
|
||||||
|
if (!can_branch(offset)) {return {};}
|
||||||
|
oaknut::CodeGenerator rc{m_patch_instructions.data() + rel.patch_offset / sizeof(u32)};
|
||||||
|
rc.B(offset);
|
||||||
|
}
|
||||||
|
return m_patch_instructions;
|
||||||
|
}
|
||||||
|
|
||||||
bool Patcher::RelocateAndCopy(Common::ProcessAddress load_base, const Kernel::CodeSet::Segment& code, std::vector<u8>& program_image, EntryTrampolines* out_trampolines) {
|
bool Patcher::RelocateAndCopy(Common::ProcessAddress load_base, const Kernel::CodeSet::Segment& code, std::vector<u8>& program_image, EntryTrampolines* out_trampolines) {
|
||||||
const size_t patch_size = GetSectionSize();
|
const size_t patch_size = GetSectionSize();
|
||||||
const size_t pre_patch_size = GetPreSectionSize();
|
const size_t pre_patch_size = GetPreSectionSize();
|
||||||
|
|||||||
@@ -48,15 +48,22 @@ using ModuleTextAddress = u64;
|
|||||||
using PatchTextAddress = u64;
|
using PatchTextAddress = u64;
|
||||||
using EntryTrampolines = ::Common::unordered_map<ModuleTextAddress, PatchTextAddress>;
|
using EntryTrampolines = ::Common::unordered_map<ModuleTextAddress, PatchTextAddress>;
|
||||||
|
|
||||||
|
struct InstructionPatch {
|
||||||
|
u64 offset;
|
||||||
|
u32 replacement;
|
||||||
|
};
|
||||||
|
|
||||||
class Patcher {
|
class Patcher {
|
||||||
public:
|
public:
|
||||||
void SetModuleID(const ModuleID& id) {
|
void SetModuleID(const ModuleID& id) {
|
||||||
module_id = id;
|
module_id = id;
|
||||||
}
|
}
|
||||||
explicit Patcher();
|
explicit Patcher();
|
||||||
|
explicit Patcher(std::span<const u8> text);
|
||||||
~Patcher();
|
~Patcher();
|
||||||
bool PatchText(std::span<const u8> program_image, const Kernel::CodeSet::Segment& code);
|
bool PatchText(std::span<const u8> program_image, const Kernel::CodeSet::Segment& code);
|
||||||
bool RelocateAndCopy(Common::ProcessAddress load_base, const Kernel::CodeSet::Segment& code, std::vector<u8>& program_image, EntryTrampolines* out_trampolines);
|
bool RelocateAndCopy(Common::ProcessAddress load_base, const Kernel::CodeSet::Segment& code, std::vector<u8>& program_image, EntryTrampolines* out_trampolines);
|
||||||
|
std::span<const u32> RelocateRuntimeTls(s64 patch_offset, std::vector<InstructionPatch>& out_patches);
|
||||||
size_t GetSectionSize() const noexcept;
|
size_t GetSectionSize() const noexcept;
|
||||||
size_t GetPreSectionSize() const noexcept;
|
size_t GetPreSectionSize() const noexcept;
|
||||||
|
|
||||||
|
|||||||
+6
-34
@@ -4,7 +4,6 @@
|
|||||||
#include <array>
|
#include <array>
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <unordered_map>
|
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
#include "game_settings.h"
|
#include "game_settings.h"
|
||||||
@@ -249,30 +248,12 @@ struct System::Impl {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void NotifyNVDECChannelOpen(u64 process_id) {
|
void SetNVDECActive(bool is_nvdec_active) {
|
||||||
std::scoped_lock lock{nvdec_active_mutex};
|
nvdec_active = is_nvdec_active;
|
||||||
++nvdec_active_channels[process_id];
|
|
||||||
}
|
|
||||||
|
|
||||||
void NotifyNVDECChannelClose(u64 process_id) {
|
|
||||||
std::scoped_lock lock{nvdec_active_mutex};
|
|
||||||
const auto it = nvdec_active_channels.find(process_id);
|
|
||||||
if (it == nvdec_active_channels.end()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (--it->second == 0) {
|
|
||||||
nvdec_active_channels.erase(it);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GetNVDECActive() {
|
bool GetNVDECActive() {
|
||||||
std::scoped_lock lock{nvdec_active_mutex};
|
return nvdec_active;
|
||||||
return !nvdec_active_channels.empty();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool IsNVDECActiveForProcess(u64 process_id) {
|
|
||||||
std::scoped_lock lock{nvdec_active_mutex};
|
|
||||||
return nvdec_active_channels.contains(process_id);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void InitializeDebugger(System& system, u16 port) {
|
void InitializeDebugger(System& system, u16 port) {
|
||||||
@@ -528,8 +509,6 @@ struct System::Impl {
|
|||||||
|
|
||||||
mutable std::mutex suspend_guard;
|
mutable std::mutex suspend_guard;
|
||||||
std::mutex general_channel_mutex;
|
std::mutex general_channel_mutex;
|
||||||
std::mutex nvdec_active_mutex;
|
|
||||||
std::unordered_map<u64, u32> nvdec_active_channels;
|
|
||||||
std::atomic_bool is_paused{};
|
std::atomic_bool is_paused{};
|
||||||
std::atomic_bool is_shutting_down{};
|
std::atomic_bool is_shutting_down{};
|
||||||
std::atomic_bool is_powered_on{};
|
std::atomic_bool is_powered_on{};
|
||||||
@@ -537,6 +516,7 @@ struct System::Impl {
|
|||||||
bool extended_memory_layout : 1 = false;
|
bool extended_memory_layout : 1 = false;
|
||||||
bool exit_locked : 1 = false;
|
bool exit_locked : 1 = false;
|
||||||
bool exit_requested : 1 = false;
|
bool exit_requested : 1 = false;
|
||||||
|
bool nvdec_active : 1 = false;
|
||||||
|
|
||||||
void EnsureGeneralChannelInitialized(System& system) {
|
void EnsureGeneralChannelInitialized(System& system) {
|
||||||
if (!general_channel_event) {
|
if (!general_channel_event) {
|
||||||
@@ -600,22 +580,14 @@ void System::UnstallApplication() {
|
|||||||
impl->UnstallApplication();
|
impl->UnstallApplication();
|
||||||
}
|
}
|
||||||
|
|
||||||
void System::NotifyNVDECChannelOpen(u64 process_id) {
|
void System::SetNVDECActive(bool is_nvdec_active) {
|
||||||
impl->NotifyNVDECChannelOpen(process_id);
|
impl->SetNVDECActive(is_nvdec_active);
|
||||||
}
|
|
||||||
|
|
||||||
void System::NotifyNVDECChannelClose(u64 process_id) {
|
|
||||||
impl->NotifyNVDECChannelClose(process_id);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool System::GetNVDECActive() {
|
bool System::GetNVDECActive() {
|
||||||
return impl->GetNVDECActive();
|
return impl->GetNVDECActive();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool System::IsNVDECActiveForProcess(u64 process_id) {
|
|
||||||
return impl->IsNVDECActiveForProcess(process_id);
|
|
||||||
}
|
|
||||||
|
|
||||||
void System::InitializeDebugger() {
|
void System::InitializeDebugger() {
|
||||||
impl->InitializeDebugger(*this, Settings::values.gdbstub_port.GetValue());
|
impl->InitializeDebugger(*this, Settings::values.gdbstub_port.GetValue());
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-3
@@ -191,10 +191,8 @@ public:
|
|||||||
std::unique_lock<std::mutex> StallApplication();
|
std::unique_lock<std::mutex> StallApplication();
|
||||||
void UnstallApplication();
|
void UnstallApplication();
|
||||||
|
|
||||||
void NotifyNVDECChannelOpen(u64 process_id);
|
void SetNVDECActive(bool is_nvdec_active);
|
||||||
void NotifyNVDECChannelClose(u64 process_id);
|
|
||||||
[[nodiscard]] bool GetNVDECActive();
|
[[nodiscard]] bool GetNVDECActive();
|
||||||
[[nodiscard]] bool IsNVDECActiveForProcess(u64 process_id);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initialize the debugger.
|
* Initialize the debugger.
|
||||||
|
|||||||
@@ -0,0 +1,430 @@
|
|||||||
|
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <array>
|
||||||
|
#include <cstring>
|
||||||
|
#include <memory>
|
||||||
|
#include <mutex>
|
||||||
|
#include <optional>
|
||||||
|
#include <span>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include <zstd.h>
|
||||||
|
|
||||||
|
#include "common/common_types.h"
|
||||||
|
#include "common/logging.h"
|
||||||
|
#include "common/zstd_compression.h"
|
||||||
|
#include "core/crypto/aes_util.h"
|
||||||
|
#include "core/crypto/key_manager.h"
|
||||||
|
#include "core/crypto/ncz_decompressor.h"
|
||||||
|
#include "core/file_sys/vfs/vfs.h"
|
||||||
|
|
||||||
|
namespace Core::Crypto {
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
constexpr u64 HeaderSize = 0x4000;
|
||||||
|
constexpr std::size_t SectionWire = 0x40;
|
||||||
|
constexpr std::size_t BlockFixed = 0x18;
|
||||||
|
constexpr std::size_t Chunk = 0x100000;
|
||||||
|
// Upper bound on NCZ section entries (64 bytes each, so the table is at most 4 MiB). A base NCA
|
||||||
|
// has a handful of sections, but nsz stores an update's patch (BKTR) data one entry per
|
||||||
|
// subsection, which is thousands of them.
|
||||||
|
constexpr u64 MaxSectionCount = 0x10000;
|
||||||
|
constexpr std::array<u8, 8> SectionsMagic{'N', 'C', 'Z', 'S', 'E', 'C', 'T', 'N'};
|
||||||
|
constexpr std::array<u8, 8> BlockMagic{'N', 'C', 'Z', 'B', 'L', 'O', 'C', 'K'};
|
||||||
|
|
||||||
|
struct Section {
|
||||||
|
s64 offset, size;
|
||||||
|
u64 crypto_type;
|
||||||
|
Key128 key, counter;
|
||||||
|
bool Ctr() const { return crypto_type == 3 || crypto_type == 4; }
|
||||||
|
};
|
||||||
|
|
||||||
|
struct BlockHeader {
|
||||||
|
u32 exponent;
|
||||||
|
u64 decompressed_size;
|
||||||
|
std::vector<u32> sizes;
|
||||||
|
};
|
||||||
|
|
||||||
|
u64 Le(const u8* p, std::size_t n) {
|
||||||
|
u64 v = 0;
|
||||||
|
for (std::size_t i = n; i-- > 0;) v = (v << 8) | p[i];
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mirrors nsz AESCTR.seek(): upper 8 bytes = stored counter, lower 8 = absolute NCA offset / 16.
|
||||||
|
void SetIv(Key128& iv, const Key128& base, u64 off) {
|
||||||
|
iv = base;
|
||||||
|
u64 blk = off >> 4;
|
||||||
|
for (std::size_t i = 0; i < 8; ++i) { iv[15 - i] = static_cast<u8>(blk & 0xFF); blk >>= 8; }
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse header + section table + optional block header. Returns payload_start.
|
||||||
|
std::optional<std::pair<std::vector<Section>, std::optional<BlockHeader>>> ParseHeader(
|
||||||
|
const FileSys::VirtualFile& f, u64& payload_start) {
|
||||||
|
const u64 fsize = f->GetSize();
|
||||||
|
if (fsize < HeaderSize + 16) return std::nullopt;
|
||||||
|
|
||||||
|
const auto head = f->ReadBytes(16, HeaderSize);
|
||||||
|
if (head.size() != 16 || !std::equal(head.begin(), head.begin() + 8, SectionsMagic.begin()))
|
||||||
|
return std::nullopt;
|
||||||
|
const u64 count = Le(head.data() + 8, 8);
|
||||||
|
if (count == 0 || count > MaxSectionCount || count * SectionWire > fsize - HeaderSize - 16)
|
||||||
|
return std::nullopt;
|
||||||
|
|
||||||
|
const auto table = f->ReadBytes(count * SectionWire, HeaderSize + 16);
|
||||||
|
if (table.size() != count * SectionWire) return std::nullopt;
|
||||||
|
|
||||||
|
std::vector<Section> sections;
|
||||||
|
sections.reserve(count + 1);
|
||||||
|
for (u64 i = 0; i < count; ++i) {
|
||||||
|
const u8* e = table.data() + i * SectionWire;
|
||||||
|
Section s{};
|
||||||
|
s.offset = static_cast<s64>(Le(e, 8));
|
||||||
|
s.size = static_cast<s64>(Le(e + 8, 8));
|
||||||
|
s.crypto_type = Le(e + 16, 8);
|
||||||
|
std::memcpy(s.key.data(), e + 32, 16);
|
||||||
|
std::memcpy(s.counter.data(), e + 48, 16);
|
||||||
|
sections.push_back(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
const u64 block_start = HeaderSize + 16 + count * SectionWire;
|
||||||
|
const auto fixed = f->ReadBytes(BlockFixed, block_start);
|
||||||
|
std::optional<BlockHeader> bh;
|
||||||
|
if (fixed.size() == BlockFixed &&
|
||||||
|
std::equal(fixed.begin(), fixed.begin() + 8, BlockMagic.begin())) {
|
||||||
|
BlockHeader b{};
|
||||||
|
b.exponent = fixed[11];
|
||||||
|
const s32 n = static_cast<s32>(Le(fixed.data() + 12, 4));
|
||||||
|
if (n <= 0) return std::nullopt;
|
||||||
|
b.decompressed_size = Le(fixed.data() + 16, 8);
|
||||||
|
const auto sizes = f->ReadBytes(static_cast<u64>(n) * 4, block_start + BlockFixed);
|
||||||
|
if (sizes.size() != static_cast<u64>(n) * 4) return std::nullopt;
|
||||||
|
b.sizes.resize(n);
|
||||||
|
for (s32 i = 0; i < n; ++i) b.sizes[i] = static_cast<u32>(Le(sizes.data() + i * 4, 4));
|
||||||
|
payload_start = block_start + BlockFixed + static_cast<u64>(n) * 4;
|
||||||
|
bh = std::move(b);
|
||||||
|
} else {
|
||||||
|
payload_start = block_start;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!sections.empty() && sections[0].offset > static_cast<s64>(HeaderSize)) {
|
||||||
|
Section fake{};
|
||||||
|
fake.offset = static_cast<s64>(HeaderSize);
|
||||||
|
fake.size = sections[0].offset - static_cast<s64>(HeaderSize);
|
||||||
|
sections.insert(sections.begin(), fake);
|
||||||
|
}
|
||||||
|
return std::make_pair(std::move(sections), std::move(bh));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sequential reader over independently-compressed blocks, one block cached (like nsz_rs).
|
||||||
|
class BlockReader {
|
||||||
|
public:
|
||||||
|
BlockReader(FileSys::VirtualFile f, const BlockHeader& bh, u64 payload_start)
|
||||||
|
: source{std::move(f)}, block_size{1ULL << bh.exponent}, sizes{bh.sizes},
|
||||||
|
decompressed_size{bh.decompressed_size} {
|
||||||
|
if (bh.exponent < 14 || bh.exponent > 32 || sizes.empty())
|
||||||
|
throw std::runtime_error("bad NCZBLOCK header");
|
||||||
|
u64 off = payload_start;
|
||||||
|
offsets.reserve(sizes.size());
|
||||||
|
for (u32 s : sizes) { offsets.push_back(off); off += s; }
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fills `out` from `position`; returns bytes written.
|
||||||
|
std::size_t ReadAt(u64 position, std::span<u8> out) {
|
||||||
|
std::size_t done = 0;
|
||||||
|
while (done < out.size() && position < decompressed_size) {
|
||||||
|
const u64 id = position / block_size;
|
||||||
|
if (id >= sizes.size()) break;
|
||||||
|
if (cache_id != id && !Load(id)) break;
|
||||||
|
const u64 in = position - id * block_size;
|
||||||
|
const std::size_t n = static_cast<std::size_t>(
|
||||||
|
std::min<u64>(out.size() - done, cache.size() - in));
|
||||||
|
std::memcpy(out.data() + done, cache.data() + in, n);
|
||||||
|
done += n;
|
||||||
|
position += n;
|
||||||
|
}
|
||||||
|
return done;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool Load(u64 id) {
|
||||||
|
u64 want = block_size;
|
||||||
|
if (id + 1 == sizes.size()) {
|
||||||
|
const u64 rem = decompressed_size % block_size;
|
||||||
|
if (rem > 0) want = rem; // avoid the 0-size bug from the original nsz
|
||||||
|
}
|
||||||
|
try { cache.resize(static_cast<std::size_t>(want)); }
|
||||||
|
catch (const std::bad_alloc&) { return false; }
|
||||||
|
const u32 csize = sizes[id];
|
||||||
|
if (csize < want) {
|
||||||
|
const auto comp = source->ReadBytes(csize, offsets[id]);
|
||||||
|
if (comp.size() != csize ||
|
||||||
|
Common::Compression::DecompressDataZSTD(comp, cache) != want) return false;
|
||||||
|
} else if (source->Read(cache.data(), cache.size(), offsets[id]) != cache.size()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
cache_id = id;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
FileSys::VirtualFile source;
|
||||||
|
u64 block_size;
|
||||||
|
std::vector<u64> offsets;
|
||||||
|
std::vector<u32> sizes;
|
||||||
|
u64 decompressed_size;
|
||||||
|
u64 cache_id = ~0ULL;
|
||||||
|
std::vector<u8> cache;
|
||||||
|
};
|
||||||
|
|
||||||
|
class BlockNczFile final : public FileSys::VfsFile {
|
||||||
|
public:
|
||||||
|
BlockNczFile(std::string name_, std::vector<u8> hdr_, std::vector<Section> sections_,
|
||||||
|
std::unique_ptr<BlockReader> body_)
|
||||||
|
: name{std::move(name_)}, hdr{std::move(hdr_)}, sections{std::move(sections_)},
|
||||||
|
body{std::move(body_)} {
|
||||||
|
nca_size = HeaderSize;
|
||||||
|
for (const auto& s : sections)
|
||||||
|
nca_size = std::max<u64>(nca_size, static_cast<u64>(s.offset + s.size));
|
||||||
|
ciphers.reserve(sections.size());
|
||||||
|
for (const auto& s : sections)
|
||||||
|
ciphers.push_back(s.Ctr() ? std::make_unique<AESCipher<Key128>>(s.key, Mode::CTR)
|
||||||
|
: nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string GetName() const override { return name; }
|
||||||
|
std::size_t GetSize() const override { return static_cast<std::size_t>(nca_size); }
|
||||||
|
bool Resize(std::size_t) override { return false; }
|
||||||
|
FileSys::VirtualDir GetContainingDirectory() const override { return nullptr; }
|
||||||
|
bool IsWritable() const override { return false; }
|
||||||
|
bool IsReadable() const override { return true; }
|
||||||
|
|
||||||
|
std::size_t Read(u8* data, std::size_t len, std::size_t off) const override {
|
||||||
|
if (off >= nca_size) return 0;
|
||||||
|
len = static_cast<std::size_t>(std::min<u64>(len, nca_size - off));
|
||||||
|
const u64 end = off + len;
|
||||||
|
u64 pos = off;
|
||||||
|
if (pos < HeaderSize) {
|
||||||
|
const u64 n = std::min<u64>(end, HeaderSize) - pos;
|
||||||
|
std::memcpy(data, hdr.data() + pos, static_cast<std::size_t>(n));
|
||||||
|
pos += n;
|
||||||
|
}
|
||||||
|
std::scoped_lock lock{mutex};
|
||||||
|
for (std::size_t i = 0; i < sections.size() && pos < end; ++i) {
|
||||||
|
const auto& s = sections[i];
|
||||||
|
const u64 begin = std::max<u64>(static_cast<u64>(s.offset), HeaderSize);
|
||||||
|
const u64 stop = std::max<u64>(static_cast<u64>(s.offset + s.size), begin);
|
||||||
|
if (stop <= pos) continue;
|
||||||
|
if (pos < begin) {
|
||||||
|
const u64 g = std::min(end, begin);
|
||||||
|
std::memset(data + (pos - off), 0, static_cast<std::size_t>(g - pos));
|
||||||
|
pos = g;
|
||||||
|
if (pos >= end) break;
|
||||||
|
}
|
||||||
|
const u64 pe = std::min(end, stop);
|
||||||
|
const std::span<u8> piece(data + (pos - off), static_cast<std::size_t>(pe - pos));
|
||||||
|
if (body->ReadAt(pos - HeaderSize, piece) != piece.size())
|
||||||
|
return static_cast<std::size_t>(pos - off);
|
||||||
|
if (ciphers[i]) {
|
||||||
|
Key128 iv{};
|
||||||
|
SetIv(iv, s.counter, pos);
|
||||||
|
ciphers[i]->SetIV(iv);
|
||||||
|
ciphers[i]->Transcode(piece.data(), piece.size(), piece.data(), Op::Encrypt);
|
||||||
|
}
|
||||||
|
pos = pe;
|
||||||
|
}
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::size_t Write(const u8*, std::size_t, std::size_t) override { return 0; }
|
||||||
|
bool Rename(std::string_view) override { return false; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
const std::string name;
|
||||||
|
const std::vector<u8> hdr;
|
||||||
|
const std::vector<Section> sections;
|
||||||
|
const std::unique_ptr<BlockReader> body;
|
||||||
|
std::vector<std::unique_ptr<AESCipher<Key128>>> ciphers;
|
||||||
|
mutable std::mutex mutex;
|
||||||
|
u64 nca_size{};
|
||||||
|
};
|
||||||
|
|
||||||
|
// Solid NCZ: one zstd stream over the whole body. Decoded incrementally into memory, up to the
|
||||||
|
// requested offset, so sequential install reads are O(n) instead of O(n^2).
|
||||||
|
class SolidNczFile final : public FileSys::VfsFile {
|
||||||
|
public:
|
||||||
|
SolidNczFile(std::string name_, FileSys::VirtualFile source_,
|
||||||
|
std::vector<Section> sections_, u64 payload_start_)
|
||||||
|
: name{std::move(name_)}, source{std::move(source_)}, sections{std::move(sections_)},
|
||||||
|
payload_start{payload_start_} {
|
||||||
|
const auto raw_header = source->ReadBytes(HeaderSize, 0);
|
||||||
|
data.assign(raw_header.begin(), raw_header.end());
|
||||||
|
frontier = HeaderSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
~SolidNczFile() override {
|
||||||
|
if (stream != nullptr) ZSTD_freeDStream(stream);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string GetName() const override { return name; }
|
||||||
|
std::size_t GetSize() const override {
|
||||||
|
u64 size = HeaderSize;
|
||||||
|
for (const auto& s : sections)
|
||||||
|
size = std::max<u64>(size, static_cast<u64>(s.offset + s.size));
|
||||||
|
return static_cast<std::size_t>(size);
|
||||||
|
}
|
||||||
|
bool Resize(std::size_t) override { return false; }
|
||||||
|
FileSys::VirtualDir GetContainingDirectory() const override { return nullptr; }
|
||||||
|
bool IsWritable() const override { return false; }
|
||||||
|
bool IsReadable() const override { return true; }
|
||||||
|
|
||||||
|
std::size_t Read(u8* out, std::size_t len, std::size_t off) const override {
|
||||||
|
std::scoped_lock lock{mutex};
|
||||||
|
const u64 nca_size = GetSize();
|
||||||
|
if (off >= nca_size) return 0;
|
||||||
|
len = static_cast<std::size_t>(std::min<u64>(len, nca_size - off));
|
||||||
|
const u64 needed = off + len;
|
||||||
|
if (needed > frontier && !DecodeTo(needed)) return 0;
|
||||||
|
std::memcpy(out, data.data() + off, len);
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::size_t Write(const u8*, std::size_t, std::size_t) override { return 0; }
|
||||||
|
bool Rename(std::string_view) override { return false; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool DecodeTo(u64 needed) const {
|
||||||
|
if (stream == nullptr) {
|
||||||
|
stream = ZSTD_createDStream();
|
||||||
|
if (stream == nullptr) return false;
|
||||||
|
ZSTD_DCtx_setParameter(stream, ZSTD_d_windowLogMax, 31);
|
||||||
|
read_pos = payload_start;
|
||||||
|
}
|
||||||
|
|
||||||
|
const u64 file_size = source->GetSize();
|
||||||
|
std::vector<u8> out_buf(Chunk);
|
||||||
|
|
||||||
|
std::size_t index = 0;
|
||||||
|
for (; index < sections.size(); ++index) {
|
||||||
|
const auto& s = sections[index];
|
||||||
|
const u64 begin = std::max<u64>(static_cast<u64>(s.offset), HeaderSize);
|
||||||
|
const u64 end = std::max<u64>(static_cast<u64>(s.offset) + static_cast<u64>(s.size),
|
||||||
|
begin);
|
||||||
|
if (end > frontier) break;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (; index < sections.size() && frontier < needed; ++index) {
|
||||||
|
const auto& s = sections[index];
|
||||||
|
const u64 begin = std::max<u64>(static_cast<u64>(s.offset), HeaderSize);
|
||||||
|
const u64 end = std::max<u64>(static_cast<u64>(s.offset) + static_cast<u64>(s.size),
|
||||||
|
begin);
|
||||||
|
if (end <= frontier) continue;
|
||||||
|
u64 pos = std::max<u64>(begin, frontier);
|
||||||
|
|
||||||
|
std::optional<AESCipher<Key128>> cipher;
|
||||||
|
if (s.Ctr()) cipher.emplace(s.key, Mode::CTR);
|
||||||
|
|
||||||
|
while (pos < end && frontier < needed) {
|
||||||
|
const u64 want = std::min<u64>(end - pos, out_buf.size());
|
||||||
|
std::size_t produced = 0;
|
||||||
|
while (produced < want) {
|
||||||
|
if (in.pos >= in.size) {
|
||||||
|
const u64 remaining = file_size - read_pos;
|
||||||
|
const std::size_t chunk = static_cast<std::size_t>(
|
||||||
|
std::min<u64>(Chunk, remaining));
|
||||||
|
if (chunk == 0) { LOG_ERROR(Crypto, "Solid NCZ: truncated"); return false; }
|
||||||
|
const auto bytes = source->ReadBytes(chunk, read_pos);
|
||||||
|
if (bytes.empty()) { LOG_ERROR(Crypto, "Solid NCZ: truncated"); return false; }
|
||||||
|
read_pos += bytes.size();
|
||||||
|
in_buf.assign(bytes.begin(), bytes.end());
|
||||||
|
in.src = in_buf.data();
|
||||||
|
in.size = in_buf.size();
|
||||||
|
in.pos = 0;
|
||||||
|
}
|
||||||
|
ZSTD_outBuffer outb{out_buf.data() + produced, want - produced, 0};
|
||||||
|
const std::size_t res = ZSTD_decompressStream(stream, &outb, &in);
|
||||||
|
if (ZSTD_isError(res)) {
|
||||||
|
LOG_ERROR(Crypto, "Solid NCZ: zstd error");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
produced += outb.pos;
|
||||||
|
}
|
||||||
|
if (cipher) {
|
||||||
|
Key128 iv{};
|
||||||
|
SetIv(iv, s.counter, pos);
|
||||||
|
cipher->SetIV(iv);
|
||||||
|
cipher->Transcode(out_buf.data(), produced, out_buf.data(), Op::Encrypt);
|
||||||
|
}
|
||||||
|
data.insert(data.end(), out_buf.begin(), out_buf.begin() + produced);
|
||||||
|
pos += produced;
|
||||||
|
frontier = pos;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return frontier >= needed;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string name;
|
||||||
|
const FileSys::VirtualFile source;
|
||||||
|
const std::vector<Section> sections;
|
||||||
|
const u64 payload_start;
|
||||||
|
mutable std::mutex mutex;
|
||||||
|
mutable std::vector<u8> data;
|
||||||
|
mutable ZSTD_DStream* stream = nullptr;
|
||||||
|
mutable u64 read_pos = 0;
|
||||||
|
mutable u64 frontier = 0;
|
||||||
|
mutable std::vector<u8> in_buf;
|
||||||
|
mutable ZSTD_inBuffer in{nullptr, 0, 0};
|
||||||
|
};
|
||||||
|
|
||||||
|
// Shared opener: block path always; solid only when allow_solid is true.
|
||||||
|
FileSys::VirtualFile OpenNcz(const FileSys::VirtualFile& f, std::string_view name,
|
||||||
|
bool allow_solid) {
|
||||||
|
if (!f) return nullptr;
|
||||||
|
u64 ps = 0;
|
||||||
|
auto parsed = ParseHeader(f, ps);
|
||||||
|
if (!parsed) return nullptr;
|
||||||
|
auto& [sections, block] = *parsed;
|
||||||
|
|
||||||
|
if (block) {
|
||||||
|
auto hdr = f->ReadBytes(HeaderSize, 0);
|
||||||
|
if (hdr.size() != HeaderSize) return nullptr;
|
||||||
|
std::unique_ptr<BlockReader> body;
|
||||||
|
try { body = std::make_unique<BlockReader>(f, *block, ps); }
|
||||||
|
catch (const std::exception& e) {
|
||||||
|
LOG_ERROR(Crypto, "Failed to open NCZBLOCK: {}", e.what());
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
return std::make_shared<BlockNczFile>(std::string{name}, std::move(hdr),
|
||||||
|
std::move(sections), std::move(body));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (allow_solid) {
|
||||||
|
return std::make_shared<SolidNczFile>(std::string{name}, f, std::move(sections), ps);
|
||||||
|
}
|
||||||
|
|
||||||
|
LOG_DEBUG(Crypto, "Solid NCZ {} skipped (only NCZBLOCK is supported for play)", name);
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // Anonymous namespace
|
||||||
|
|
||||||
|
bool IsBlockCompressedNCZ(const FileSys::VirtualFile& f) {
|
||||||
|
if (!f) return false;
|
||||||
|
u64 ps = 0;
|
||||||
|
const auto p = ParseHeader(f, ps);
|
||||||
|
return p && p->second.has_value();
|
||||||
|
}
|
||||||
|
|
||||||
|
FileSys::VirtualFile DecompressNCZ(const FileSys::VirtualFile& f, std::string_view name) {
|
||||||
|
return OpenNcz(f, name, /*allow_solid=*/false);
|
||||||
|
}
|
||||||
|
|
||||||
|
FileSys::VirtualFile DecompressNCZForInstall(const FileSys::VirtualFile& f,
|
||||||
|
std::string_view name) {
|
||||||
|
return OpenNcz(f, name, /*allow_solid=*/true);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Core::Crypto
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <string_view>
|
||||||
|
|
||||||
|
#include "core/file_sys/vfs/vfs_types.h"
|
||||||
|
|
||||||
|
namespace Core::Crypto {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Opens an NCZBLOCK-compressed NCA (nsz -B / .xcz block format) as a VirtualFile backed by
|
||||||
|
* on-demand block decoding. Nothing is written to disk; each read decodes only the blocks it
|
||||||
|
* covers and re-encrypts CTR sections. Solid NCZ is not supported.
|
||||||
|
*
|
||||||
|
* @param ncz_file The raw NCZ payload (e.g. "<id>.ncz" entry from an NSZ/XCZ PFS0).
|
||||||
|
* @param output_name Name to give the returned VirtualFile (e.g. "<id>.nca").
|
||||||
|
*
|
||||||
|
* @return a VirtualFile for the NCA, or nullptr if the NCZ is malformed or not NCZBLOCK.
|
||||||
|
*/
|
||||||
|
[[nodiscard]] FileSys::VirtualFile DecompressNCZ(const FileSys::VirtualFile& ncz_file,
|
||||||
|
std::string_view output_name);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if the given file is an NCZ payload in block-compressed (NCZBLOCK) form,
|
||||||
|
* i.e. one that DecompressNCZ() can open. Solid NCZ returns false.
|
||||||
|
*/
|
||||||
|
[[nodiscard]] bool IsBlockCompressedNCZ(const FileSys::VirtualFile& ncz_file);
|
||||||
|
|
||||||
|
// Install to NAND: same as DecompressNCZ, but solid NCZ is decoded on demand too.
|
||||||
|
[[nodiscard]] FileSys::VirtualFile DecompressNCZForInstall(
|
||||||
|
const FileSys::VirtualFile& ncz_file, std::string_view output_name);
|
||||||
|
|
||||||
|
} // namespace Core::Crypto
|
||||||
@@ -45,6 +45,7 @@ NCA::NCA(VirtualFile file_, const NCA* base_nca, bool allow_missing_base)
|
|||||||
// Ensure we have the proper key area keys to continue.
|
// Ensure we have the proper key area keys to continue.
|
||||||
const u8 master_key_id = MasterKeyIdForKeyGeneration(reader->GetKeyGeneration());
|
const u8 master_key_id = MasterKeyIdForKeyGeneration(reader->GetKeyGeneration());
|
||||||
if (!keys.HasKey(Core::Crypto::S128KeyType::KeyArea, master_key_id, reader->GetKeyIndex())) {
|
if (!keys.HasKey(Core::Crypto::S128KeyType::KeyArea, master_key_id, reader->GetKeyIndex())) {
|
||||||
|
LOG_ERROR(Loader, "Cannot decrypt {}: keys for gen 0x{:02X}+ required.", file->GetName(), reader->GetKeyGeneration());
|
||||||
status = Loader::ResultStatus::ErrorMissingKeyAreaKey;
|
status = Loader::ResultStatus::ErrorMissingKeyAreaKey;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,6 +16,7 @@
|
|||||||
#include "common/random.h"
|
#include "common/random.h"
|
||||||
#include "common/string_util.h"
|
#include "common/string_util.h"
|
||||||
#include "core/crypto/key_manager.h"
|
#include "core/crypto/key_manager.h"
|
||||||
|
#include "core/crypto/ncz_decompressor.h"
|
||||||
#include "core/file_sys/card_image.h"
|
#include "core/file_sys/card_image.h"
|
||||||
#include "core/file_sys/common_funcs.h"
|
#include "core/file_sys/common_funcs.h"
|
||||||
#include "core/file_sys/content_archive.h"
|
#include "core/file_sys/content_archive.h"
|
||||||
@@ -158,6 +159,25 @@ static std::shared_ptr<NSP> OpenContainerAsNsp(const VirtualFile& file, Loader::
|
|||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Checks that every .ncz entry in the container is NCZBLOCK. Solid NCZ has no random access,
|
||||||
|
// and the whole point of this build is to reject it at scan time so such files never appear
|
||||||
|
// in the game list. Containers without any .ncz (plain NSP/XCI) pass through.
|
||||||
|
static bool ContainerIsFullyBlockCompressed(const std::shared_ptr<NSP>& nsp) {
|
||||||
|
if (!nsp) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
for (const auto& entry : nsp->GetFiles()) {
|
||||||
|
const auto name = entry->GetName();
|
||||||
|
if (name.size() < 4 || name.substr(name.size() - 4) != ".ncz") {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (!Core::Crypto::IsBlockCompressedNCZ(entry)) {
|
||||||
|
return false; // solid NCZ -> reject the whole container
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true; // no .ncz at all, or all block-compressed
|
||||||
|
}
|
||||||
|
|
||||||
template <typename Callback>
|
template <typename Callback>
|
||||||
bool ForEachContainerEntry(const std::shared_ptr<NSP>& nsp, bool only_content,
|
bool ForEachContainerEntry(const std::shared_ptr<NSP>& nsp, bool only_content,
|
||||||
std::optional<u64> base_program_id, Callback&& on_entry) {
|
std::optional<u64> base_program_id, Callback&& on_entry) {
|
||||||
@@ -811,7 +831,14 @@ std::vector<ContentProviderEntry> RegisteredCache::ListEntriesFilter(
|
|||||||
}
|
}
|
||||||
|
|
||||||
static std::shared_ptr<NCA> GetNCAFromNSPForID(const NSP& nsp, const NcaID& id) {
|
static std::shared_ptr<NCA> GetNCAFromNSPForID(const NSP& nsp, const NcaID& id) {
|
||||||
auto file = nsp.GetFile(fmt::format("{}.nca", Common::HexToString(id, false)));
|
const auto id_string = Common::HexToString(id, false);
|
||||||
|
auto file = nsp.GetFile(fmt::format("{}.nca", id_string));
|
||||||
|
if (file == nullptr) {
|
||||||
|
if (auto ncz_file = nsp.GetFile(fmt::format("{}.ncz", id_string))) {
|
||||||
|
file = Core::Crypto::DecompressNCZForInstall(
|
||||||
|
ncz_file, fmt::format("{}.nca", id_string));
|
||||||
|
}
|
||||||
|
}
|
||||||
if (file == nullptr) {
|
if (file == nullptr) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
@@ -1273,6 +1300,12 @@ bool ManualContentProvider::AddEntriesFromContainer(VirtualFile file, bool only_
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Reject solid NCZ (.nsz without -B): only block-compressed NCZ is supported.
|
||||||
|
if (!ContainerIsFullyBlockCompressed(nsp)) {
|
||||||
|
LOG_DEBUG(Service_FS, "Skipping solid-compressed container: {}", file->GetName());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
return ForEachContainerEntry(
|
return ForEachContainerEntry(
|
||||||
nsp, only_content, base_program_id,
|
nsp, only_content, base_program_id,
|
||||||
[this](TitleType title_type, ContentRecordType content_type, u64 title_id,
|
[this](TitleType title_type, ContentRecordType content_type, u64 title_id,
|
||||||
@@ -1415,8 +1448,9 @@ void ExternalContentProvider::ScanDirectory(const VirtualDir& dir) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const auto extension = Common::ToLower(filename.substr(dot_pos + 1));
|
const auto extension = Common::ToLower(filename.substr(dot_pos + 1));
|
||||||
|
// .xcz is an XCI container, but its updates only work through the NSP/PFS0 path
|
||||||
if (extension == "nsp") {
|
// (DecompressNCZ), not ProcessXCI. So route .xcz here on purpose.
|
||||||
|
if (extension == "nsp" || extension == "nsz" || extension == "xcz") {
|
||||||
ProcessNSP(file);
|
ProcessNSP(file);
|
||||||
} else if (extension == "xci") {
|
} else if (extension == "xci") {
|
||||||
ProcessXCI(file);
|
ProcessXCI(file);
|
||||||
@@ -1434,6 +1468,12 @@ void ExternalContentProvider::ProcessNSP(const VirtualFile& file) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Reject solid NCZ (.nsz without -B): Eden only supports block-compressed NCZ.
|
||||||
|
if (!ContainerIsFullyBlockCompressed(nsp)) {
|
||||||
|
LOG_DEBUG(Service_FS, "Skipping solid-compressed container: {}", file->GetName());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
LOG_DEBUG(Service_FS, "Processing NSP file: {}", file->GetName());
|
LOG_DEBUG(Service_FS, "Processing NSP file: {}", file->GetName());
|
||||||
AddExternalEntriesFromContainer(nsp, entries, versions, multi_version_entries);
|
AddExternalEntriesFromContainer(nsp, entries, versions, multi_version_entries);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,6 +12,7 @@
|
|||||||
#include "common/hex_util.h"
|
#include "common/hex_util.h"
|
||||||
#include "common/logging.h"
|
#include "common/logging.h"
|
||||||
#include "core/crypto/key_manager.h"
|
#include "core/crypto/key_manager.h"
|
||||||
|
#include "core/crypto/ncz_decompressor.h"
|
||||||
#include "core/file_sys/common_funcs.h"
|
#include "core/file_sys/common_funcs.h"
|
||||||
#include "core/file_sys/content_archive.h"
|
#include "core/file_sys/content_archive.h"
|
||||||
#include "core/file_sys/nca_metadata.h"
|
#include "core/file_sys/nca_metadata.h"
|
||||||
@@ -246,6 +247,17 @@ void NSP::ReadNCAs(const std::vector<VirtualFile>& files) {
|
|||||||
const auto id_string = Common::HexToString(rec.nca_id, false);
|
const auto id_string = Common::HexToString(rec.nca_id, false);
|
||||||
auto next_file = pfs->GetFile(fmt::format("{}.nca", id_string));
|
auto next_file = pfs->GetFile(fmt::format("{}.nca", id_string));
|
||||||
|
|
||||||
|
if (next_file == nullptr) {
|
||||||
|
auto ncz_file = pfs->GetFile(fmt::format("{}.ncz", id_string));
|
||||||
|
if (ncz_file != nullptr) {
|
||||||
|
next_file = Core::Crypto::DecompressNCZ(ncz_file, fmt::format("{}.nca", id_string));
|
||||||
|
if (next_file == nullptr) {
|
||||||
|
LOG_DEBUG(Service_FS, "Skipping solid or unsupported NCZ content {}.ncz", id_string);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (next_file == nullptr) {
|
if (next_file == nullptr) {
|
||||||
if (rec.type != ContentRecordType::DeltaFragment) {
|
if (rec.type != ContentRecordType::DeltaFragment) {
|
||||||
LOG_WARNING(Service_FS,
|
LOG_WARNING(Service_FS,
|
||||||
|
|||||||
@@ -1,3 +1,6 @@
|
|||||||
|
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,6 @@
|
|||||||
|
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,6 @@
|
|||||||
|
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,6 @@
|
|||||||
|
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||||
|
|||||||
@@ -1,3 +1,6 @@
|
|||||||
|
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
|
||||||
|
|||||||
@@ -1,3 +1,6 @@
|
|||||||
|
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,6 @@
|
|||||||
|
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,6 @@
|
|||||||
|
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,6 @@
|
|||||||
|
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|||||||
@@ -1,3 +1,6 @@
|
|||||||
|
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,6 @@
|
|||||||
|
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,6 @@
|
|||||||
|
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||||
|
|||||||
@@ -1,3 +1,6 @@
|
|||||||
|
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,6 @@
|
|||||||
|
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,6 @@
|
|||||||
|
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||||
|
|||||||
@@ -1,3 +1,6 @@
|
|||||||
|
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,6 @@
|
|||||||
|
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,6 @@
|
|||||||
|
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,6 @@
|
|||||||
|
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||||
|
|||||||
@@ -1,3 +1,6 @@
|
|||||||
|
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,6 @@
|
|||||||
|
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,6 @@
|
|||||||
|
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,6 @@
|
|||||||
|
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,6 @@
|
|||||||
|
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||||
|
|||||||
@@ -1,3 +1,6 @@
|
|||||||
|
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,6 @@
|
|||||||
|
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user