Compare commits

...

39 Commits

Author SHA1 Message Date
lizzie 1fe88221d1 2026-09-23 11:51:29
Signed-off-by: lizzie <lizzie@eden-emu.dev>
2026-09-23 11:51:29 +00:00
lizzie e9bfd97c7f 2026-09-23 09:25:56
Signed-off-by: lizzie <lizzie@eden-emu.dev>
2026-09-23 09:25:56 +00:00
lizzie 773c29ed85 2026-09-09 16:05:11
Signed-off-by: lizzie <lizzie@eden-emu.dev>
2026-09-23 10:42:17 +02:00
lizzie 09f05f0313 2026-09-07 23:31:06
Signed-off-by: lizzie <lizzie@eden-emu.dev>
2026-09-23 10:42:17 +02:00
lizzie 3b03ade8d8 2026-09-07 23:30:42
Signed-off-by: lizzie <lizzie@eden-emu.dev>
2026-09-23 10:42:17 +02:00
lizzie 78cacac308 fix span shit 2026-09-23 10:42:17 +02:00
lizzie 84f43f2c63 [audio_core] remove dangling Core::System& references
Signed-off-by: lizzie <lizzie@eden-emu.dev>
2026-09-23 10:42:17 +02:00
PavelBARABANOV 3266c20e3f [ips_layer] read magic from IPS file and fix EOF check (#4469)
- [x] I have read and followed the [Contribution Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/CONTRIBUTING.md#code-contributions).
- [x] I have read and followed the [AI Policy](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/AI.md)
- [x] 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.

-------------------
PatchIPS was reading the magic bytes from the target file instead
of the IPS patch, and IsEOF used a strict > comparison that never
matched for 3-byte IPS EOF markers. This caused IPS patches to be
silently skipped, breaking the Russian localization mod for
Persona 5.

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4469
Reviewed-by: lizzie <lizzie@eden-emu.dev>
Reviewed-by: CamilleLaVey <camillelavey99@gmail.com>
2026-09-23 00:36:47 +02:00
Eden CI 9cd24e85c2 [dist, android] Update translations from Transifex for Sep 21 (#4466)
Automatic translation update for Sep 21

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4466
2026-09-21 19:25:54 +02:00
xbzk 74b5e10dc5 [native] doesUpdateMatchProgram update mask fix (#4465)
- [x] I have read and followed the [Contribution Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/CONTRIBUTING.md#code-contributions).
- [x] I have read and followed the [AI Policy](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/AI.md)
- [x] 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.

-------------------

Old silly bug i've been neglecting:
On Android, installing content to NAND, under some content id conditions (ie MK8D DLC), base/update IDs mismatch, and UI prompts "The content that you selected does not match this game. Install anyway?
Useless, yet fixed.

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4465
Reviewed-by: lizzie <lizzie@eden-emu.dev>
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
2026-09-20 22:56:06 +02:00
xbzk a277b62fe4 [fs] revert #4319 savedata_factory changes (#4462)
- [x] I have read and followed the [Contribution Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/CONTRIBUTING.md#code-contributions).
- [x] I have read and followed the [AI Policy](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/AI.md)
- [x] 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.

-------------------

This one reverts part of the PR 4319 which was aimed to allow child titles to inherit bundle settings.
The fix seems good so far, but by applying it to save folders caused a trap:
Now child's save folders move to bundle folder, hence saves will vanish, and the android/qt save tools are still pointing to old folders.
Since the save part was only a compliance fix, instead of fiddle in master i'd rather revert this part now, then only touch this again if any bug show up.
No worries. I'll be right here waiting.

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4462
Reviewed-by: lizzie <lizzie@eden-emu.dev>
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
2026-09-20 04:40:14 +02:00
lizzie 908b1e9a37 [dynarmic] fix bad arm codegen on relocations (#4458)
In an earlier version I had these as constants which were referred to via `db`, I haphazardly left the LDR and didn't notice.
Even still relocations apply to only 1 ins at a time.

Fixes regressions on #4158

Signed-off-by: lizzie <lizzie@eden-emu.dev>

- [x] I have read and followed the [Contribution Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/CONTRIBUTING.md#code-contributions).
- [x] I have read and followed the [AI Policy](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/AI.md)
- [x] 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.

-------------------

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4458
Reviewed-by: CamilleLaVey <camillelavey99@gmail.com>
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
2026-09-20 00:29:28 +02:00
xbzk 847e91c3a8 [applet] add post exit cleanups to frontend applets to avoid accumulation (#4457)
- [x] I have read and followed the [Contribution Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/CONTRIBUTING.md#code-contributions).
- [x] I have read and followed the [AI Policy](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/AI.md)
- [x] 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.

-------------------

Maran reported MK8D if one press Plus button 38 times.
This could be impacting other games around, but 4442 already quenched the spontaneous accumulation cases, by avoiding multiple event signals. MK8D is an atypical induced example.
The reason was a controller applet accumulation, as we had no proper way to keep track and erase child applets on exit.
Now we have. Enjoy your Plus button rushing fetish!

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4457
Reviewed-by: lizzie <lizzie@eden-emu.dev>
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
2026-09-19 17:44:12 +02:00
lizzie 2d1eb0dab9 [common/error] remove preprocessor macros for strerror_r gating (#4450)
We use C++, we can just use SFINAE for this.

Signed-off-by: lizzie <lizzie@eden-emu.dev>

- [x] I have read and followed the [Contribution Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/CONTRIBUTING.md#code-contributions).
- [x] I have read and followed the [AI Policy](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/AI.md)
- [x] 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.

-------------------

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4450
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
Reviewed-by: Shinmegumi <shinmegumi@eden-emu.dev>
2026-09-19 08:21:39 +02:00
lizzie 9a30172e45 [dynarmic] Coalesce non-exclusive Write/Read fallback functions (#4158)
the general idea is to have a common procedure form whom to call
this way theres less "jumping around" for values of different sizes
additionally this **should** allow for better codegen since
most of the u8,u16,u32,u64 can be held within a u64
theoretically this means that you could deifne callbacks in suck
a way that it's essentially as costly as a `mov r64, m64`
but that's not doable due to the fact we have to do translations...

Is this a good change?
Primarily aimed for x86 and ARM to benefit, but I suppose LooooooongArch64 can benefit too.

Signed-off-by: lizzie <lizzie@eden-emu.dev>
Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4158
Reviewed-by: crueter <crueter@eden-emu.dev>
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
Reviewed-by: CamilleLaVey <camillelavey99@gmail.com>
2026-09-19 01:39:46 +02:00
xbzk 07f40d5cac [npad,hid,am] npad WriteEmptyEntry fix, hid function 551 stub, and applet PopOutData rework (#4442)
- [x] I have read and followed the [Contribution Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/CONTRIBUTING.md#code-contributions).
- [x] I have read and followed the [AI Policy](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/AI.md)
- [x] 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.

-------------------
This was intended to fix Dave the Diver issue, but we found a real silly hid bug, then a maybe broader applet issue:

By testing with John it was found out only the last controller connected worked, the others caused the game to freeze peacefully, no crashes, suggesting some loop or deadlock.

After seeking that lead, it was found that the freeze was caused by a malformed HID history entry when the controller applet removed P2.
Pressing P1 opens the controller applet, then Qt applet disconnects P2, which is expected.

Upon seeking callstack and hid sync path, it was found out that NPad::WriteEmptyEntry:
was using: ReadCurrentEntry().sampling_number + 1
instead of: ReadCurrentEntry().state.sampling_number + 1 (checked AtomicStorage in src\hid_core\resources\ring_lifo.h)
Fixing that fixed the freeze, but killed the sound!

Later handheld/docked was accidentally clicked and sound returned. Good lead.
Then i just had to corner which part of the toggle was causing the refresh and do something similar at the controller applet return routine (PopOutData in library_applet_accessor.cpp). This part seems harmless, but i'm not clearly sure about how proper it is, since it was like a transplant and test operation.

UPDATE:
To try Diablo 3 on my machine it was needed to stub HID function 551.

And to fix all abnormalies like multiple applet requests and crash the function PopOutData was reworked to ensurey idempotency among different applet focus conditions:

focus state changed: updates state and signals if needed
no state change, HLE application frontend: requests focus state changed notification
already pending change message: flag remains true and no duplicate pushed to queue entry

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4442
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
Reviewed-by: lizzie <lizzie@eden-emu.dev>
2026-09-18 23:49:17 +02:00
xbzk 3c38afad74 [fs] share parent resources with bundled programs (#4319)
- [x] I have read and followed the [Contribution Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/CONTRIBUTING.md#code-contributions).
- [x] I have read and followed the [AI Policy](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/AI.md)
- [x] 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.

-------------------

Normal games are Applications;
Bundled games are Application + Programs;

We've been treating both as the same thing, calling both programs, and making programs unable to see settings and saves from application.

References:
https://switchbrew.org/wiki/Filesystem_services
https://switchbrew.org/wiki/NCM_services#ApplicationId
https://switchbrew.org/wiki/Super_Mario_3D_All-Stars
Ryujinx.HLE/HOS/ApplicationLoader.cs:GetMultiProgramInfo

UPDATE:

The first version was a big rework to make AppId/ProgId concepts proper, and it was successful in most tests, but it caused a regression with applets, which are programs that breed straight from no application.
In order to simplify things i've reverted all the rework, added a helper to identify whether a program has a parent Application and bounded it to it's proper stores.

Although this is no big deal coz it doesn't have impact on emulation, it permeates to several routines, so it needs to be tested broadly. Applets, Normal games, Bundled games, Blobbed NSPs/XCIs, Mods, Updates, Saves, DLCs,.

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4319
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
Reviewed-by: lizzie <lizzie@eden-emu.dev>
2026-09-18 23:48:35 +02:00
Feng Chen 0ce29be608 [common] Fix RDTSC nanosecond conversion (#4422)
Split the RDTSC ticks-per-nanosecond ratio into integer and Q0.64 fractional components. This avoids overflowing GetFixedPoint64Factor for TSC frequencies above 1 GHz while preserving the multiply-only conversion path.

Use the invariant clock only above 1 GHz because the reverse nanoseconds-per-tick factor cannot represent an exact ratio of one.

- [x] I have read and followed the [Contribution Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/CONTRIBUTING.md#code-contributions).
- [x] I have read and followed the [AI Policy](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/AI.md)
- [x] 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.

-------------------

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4422
Reviewed-by: lizzie <lizzie@eden-emu.dev>
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
2026-09-17 19:50:19 +02:00
lizzie 1203082a8f [msvc] fix build errors (#4451)
MSVC doesn't consider atomic<u64> to be trivially copyable on x86 (why?).
And other chary idiosyncrasies from constructing `std::optional<uint8_t>`

Signed-off-by: lizzie <lizzie@eden-emu.dev>

- [x] I have read and followed the [Contribution Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/CONTRIBUTING.md#code-contributions).
- [x] I have read and followed the [AI Policy](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/AI.md)
- [x] 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.

-------------------

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4451
Reviewed-by: Maufeat <sahyno1996@gmail.com>
Reviewed-by: CamilleLaVey <camillelavey99@gmail.com>
2026-09-17 08:16:18 +02:00
lizzie 14235dc0d0 [ips] fix .pchtxt with >256 bytes on a single line (#4445)
It wasn't correct to assume pchtxt would've been less than 256 bytes per line.

Signed-off-by: lizzie <lizzie@eden-emu.dev>

- [x] I have read and followed the [Contribution Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/CONTRIBUTING.md#code-contributions).
- [x] I have read and followed the [AI Policy](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/AI.md)
- [x] 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.

-------------------

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4445
Reviewed-by: Maufeat <sahyno1996@gmail.com>
Reviewed-by: CamilleLaVey <camillelavey99@gmail.com>
2026-09-17 08:15:58 +02:00
lizzie 6374f7f51f Partial revert: [dynarmic] fix sign extension and optimize vaddr lookups for 64bit on x64 systems (#4446) (#4448)
Breaks Mario Kart 8 Deluxe.

#4446

Signed-off-by: lizzie <lizzie@eden-emu.dev>

- [x] I have read and followed the [Contribution Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/CONTRIBUTING.md#code-contributions).
- [x] I have read and followed the [AI Policy](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/AI.md)
- [x] 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.

-------------------

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4448
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
Reviewed-by: CamilleLaVey <camillelavey99@gmail.com>
2026-09-17 05:52:36 +02:00
lizzie 73e004de6e [common/page_table] unmark non-constexpr functions (#4433)
`std::atomic` member functions don't translate unto `constexpr`.

Trivial change, fixes CI warning.

Signed-off-by: lizzie <lizzie@eden-emu.dev>

- [x] I have read and followed the [Contribution Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/CONTRIBUTING.md#code-contributions).
- [x] I have read and followed the [AI Policy](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/AI.md)
- [x] 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.

-------------------

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4433
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
Reviewed-by: CamilleLaVey <camillelavey99@gmail.com>
2026-09-17 05:51:23 +02:00
lizzie f7b8ade40e [hle] stpl:sys, stpl:u initial implementation (#4435)
These cmds are hardcoded to use 0x20b, also the vftable is the same for `nn::sf::hipc`.

Streamplay is probably not gonna be implemented fully, but some future updates *may* hang when querying for the service, thus registering it at once will avoid such issues.

- [x] I have read and followed the [Contribution Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/CONTRIBUTING.md#code-contributions).
- [x] I have read and followed the [AI Policy](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/AI.md)
- [x] 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.

-------------------

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4435
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
Reviewed-by: CamilleLaVey <camillelavey99@gmail.com>
2026-09-17 05:45:37 +02:00
lizzie 8c1194474e [hle/ssl] Implement Peek(),{Get,Set}RenegotiationMode,Pending,Poll and stub miscellany (#4428)
Mainly because of Pending() as it's used in some games. May as well implement the rest.

Skyline implemented ISslContext::RegisterInternalPki which is just a success stub (for now) for JJBA:ASB

Signed-off-by: lizzie <lizzie@eden-emu.dev>

- [x] I have read and followed the [Contribution Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/CONTRIBUTING.md#code-contributions).
- [x] I have read and followed the [AI Policy](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/AI.md)
- [x] 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.

-------------------

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4428
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
Reviewed-by: Maufeat <sahyno1996@gmail.com>
Reviewed-by: CamilleLaVey <camillelavey99@gmail.com>
2026-09-17 05:43:59 +02:00
lizzie 90aafeedc1 [k_scheduler] fix for libc++ that lazily initializes mutexes (#4447)
`__m_` is nullptr when `unlock();` is called, so `try_lock();` will force materialization of the backing mutex.

Signed-off-by: lizzie <lizzie@eden-emu.dev>

- [x] I have read and followed the [Contribution Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/CONTRIBUTING.md#code-contributions).
- [x] I have read and followed the [AI Policy](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/AI.md)
- [x] 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.

-------------------

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4447
Reviewed-by: CamilleLaVey <camillelavey99@gmail.com>
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
2026-09-17 00:28:37 +02:00
Exverge 4fe5f62c38 [dynarmic] fix sign extension and optimize vaddr lookups for 64bit on x64 systems (#4446)
- [x] I have read and followed the [Contribution Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/CONTRIBUTING.md#code-contributions).
- [x] I have read and followed the [AI Policy](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/AI.md)
- [x] 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.

-------------------
Same as #4415 + #4444 just ported to A64 because I amazingly forgot

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4446
Reviewed-by: lizzie <lizzie@eden-emu.dev>
Reviewed-by: CamilleLaVey <camillelavey99@gmail.com>
2026-09-16 22:47:16 +02:00
Feng Chen c3f1e6562b [common/fs] Preserve Windows roots during path sanitization (#4437)
Commit 5b4c29b123 made SanitizePath rebuild paths from split components after resolving traversal. That reconstruction retained only one leading separator, collapsing UNC paths and breaking network-backed directories.

Parse the path root separately from normal components. Preserve UNC prefixes, drive-absolute and drive-relative forms, and extended UNC/device namespaces while normalizing separators and dot components.

Treat the UNC server/share portion as part of the root so parent traversal cannot escape into another share. Also preserve root-only results such as / and C:\\ instead of stripping their final separator.

- [x] I have read and followed the [Contribution Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/CONTRIBUTING.md#code-contributions).
- [x] I have read and followed the [AI Policy](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/AI.md)
- [x] 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.

-------------------

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4437
Reviewed-by: lizzie <lizzie@eden-emu.dev>
Reviewed-by: CamilleLaVey <camillelavey99@gmail.com>
2026-09-16 22:12:01 +02:00
lizzie ac35358b3f [common] Replace Common::SpinLock with std::mutex (#4436)
`std::mutex` can be better than the spin-lock specially since we don't waste CPU cycles for no reason.

Test less stuttering/heat consumption.

Signed-off-by: lizzie <lizzie@eden-emu.dev>

- [x] I have read and followed the [Contribution Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/CONTRIBUTING.md#code-contributions).
- [x] I have read and followed the [AI Policy](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/AI.md)
- [x] 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.

-------------------

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4436
Reviewed-by: CamilleLaVey <camillelavey99@gmail.com>
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
2026-09-16 15:35:13 +02:00
lizzie 7bf95be2c2 [dynarmic] fix A32 crash due to wrong LEA encoding (#4444)
LEA 1<<n, not just n

Signed-off-by: lizzie <lizzie@eden-emu.dev>

- [x] I have read and followed the [Contribution Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/CONTRIBUTING.md#code-contributions).
- [x] I have read and followed the [AI Policy](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/AI.md)
- [x] 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.

-------------------

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4444
Reviewed-by: CamilleLaVey <camillelavey99@gmail.com>
Reviewed-by: Maufeat <sahyno1996@gmail.com>
2026-09-16 09:29:12 +02:00
lizzie 4ce45b3b37 [hle] Fix crash on MK8D and SM3DW LDN (#4417)
Crash is due to SIGILL due to mismatched N and span<> ctor().
Also some astray nullptr references.

Signed-off-by: lizzie <lizzie@eden-emu.dev>

- [x] I have read and followed the [Contribution Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/CONTRIBUTING.md#code-contributions).
- [x] I have read and followed the [AI Policy](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/AI.md)
- [x] 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.

-------------------

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4417
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
Reviewed-by: Maufeat <sahyno1996@gmail.com>
2026-09-16 08:10:29 +02:00
lizzie fd34024f0e [hle/ns] fix build error due to missing async_result.h (#4440)
We may have merged quite a bit too fast before letting CI finish.

Signed-off-by: lizzie <lizzie@eden-emu.dev>

- [x] I have read and followed the [Contribution Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/CONTRIBUTING.md#code-contributions).
- [x] I have read and followed the [AI Policy](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/AI.md)
- [x] 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.

-------------------

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4440
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
Reviewed-by: CamilleLaVey <camillelavey99@gmail.com>
2026-09-16 04:45:45 +02:00
lizzie d76f8f91c4 [hle] Fix nullptr on ListApplicationIcon (#4438)
Control could be null, handle such case

Signed-off-by: lizzie <lizzie@eden-emu.dev>

- [x] I have read and followed the [Contribution Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/CONTRIBUTING.md#code-contributions).
- [x] I have read and followed the [AI Policy](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/AI.md)
- [x] 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.

-------------------

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4438
Reviewed-by: CamilleLaVey <camillelavey99@gmail.com>
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
2026-09-16 04:00:59 +02:00
PavelBARABANOV a7061eb4c8 [ns] Rename i_async_result -> async_result (#4439)
- [x] I have read and followed the [Contribution Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/CONTRIBUTING.md#code-contributions).
- [x] I have read and followed the [AI Policy](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/AI.md)
- [x] 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.

-------------------

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4439
Reviewed-by: lizzie <lizzie@eden-emu.dev>
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
2026-09-16 03:46:44 +02:00
PavelBARABANOV fa4e7c6992 [vk_texture_cache] drop depth comparison sampler fallback (#4408)
- [x] I have read and followed the [Contribution Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/CONTRIBUTING.md#code-contributions).
- [x] I have read and followed the [AI Policy](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/AI.md)
- [x] 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.

-------------------

This fixes the flashlight in Alan Wake.

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4408
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
Reviewed-by: CamilleLaVey <camillelavey99@gmail.com>
2026-09-16 01:42:37 +02:00
xbzk b77308ced6 [applet] ReconfigureControllers fallback unconditional disconnect fix (#4420)
- [x] I have read and followed the [Contribution Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/CONTRIBUTING.md#code-contributions).
- [x] I have read and followed the [AI Policy](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/AI.md)
- [x] 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.

-------------------

So, about Absolum's no overlay applet P2 issue:

The fallback controller applet ignored keep_controllers_connected, according comment: "This makes it easy to connect the desired controllers". Unfortunately it was not good enough. It disconnected every controller, then reconnected only the minimum player count. That disconnect interacts with some guests (absolum in this case) causing undesired behaviors.

The fix is entwined to original routine but it is a lot simple tho: preserves compatible connected controllers within the requested player range, while the existing routine configures only the remaining slots.

This may fix some games, and should not harm others. Let us give it a go.

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4420
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
2026-09-15 21:53:07 +02:00
PavelBARABANOV defddec47f [ns] add stubs for cmd936 and cmd4042 (#4429)
- [x] I have read and followed the [Contribution Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/CONTRIBUTING.md#code-contributions).
- [x] I have read and followed the [AI Policy](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/AI.md)
- [x] 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.

-------------------
This is necessary to close Absolum and Hades 2 in qlaunch.

Cmd4042 is necessary that there are no freezes when opening virtual cartridges.

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4429
Reviewed-by: lizzie <lizzie@eden-emu.dev>
Reviewed-by: Maufeat <sahyno1996@gmail.com>
2026-09-15 08:01:17 +02:00
CamilleLaVey c5405250d1 [common, ui] Refining UI for FX shaders for PC (#4426)
Self-explanatory refinements on the already existing access/usage of post-processing shaders on Eden for PC UI.

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4426
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
Reviewed-by: lizzie <lizzie@eden-emu.dev>
2026-09-15 07:23:12 +02:00
crueter 10c07d700c [cmake] Use a debloated boost distribution (#4198)
This addresses another issue that caused Windows configure time to take significantly longer than other platforms.

Signed-off-by: crueter <crueter@eden-emu.dev>
Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4198
Reviewed-by: CamilleLaVey <camillelavey99@gmail.com>
Reviewed-by: lizzie <lizzie@eden-emu.dev>
2026-09-15 07:22:23 +02:00
PavelBARABANOV 505b157647 [qt common] use ConfigurationShared translation context (#4430)
- [x] I have read and followed the [Contribution Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/CONTRIBUTING.md#code-contributions).
- [x] I have read and followed the [AI Policy](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/AI.md)
- [x] 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.

-------------------

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4430
Reviewed-by: crueter <crueter@eden-emu.dev>
Reviewed-by: CamilleLaVey <camillelavey99@gmail.com>
2026-09-15 03:42:11 +02:00
134 changed files with 16662 additions and 14980 deletions
-13
View File
@@ -1,13 +0,0 @@
diff --git a/libs/cobalt/include/boost/cobalt/concepts.hpp b/libs/cobalt/include/boost/cobalt/concepts.hpp
index d49f2ec..a9bdb80 100644
--- a/libs/cobalt/include/boost/cobalt/concepts.hpp
+++ b/libs/cobalt/include/boost/cobalt/concepts.hpp
@@ -62,7 +62,7 @@ struct enable_awaitables
template <typename T>
concept with_get_executor = requires (T& t)
{
- {t.get_executor()} -> asio::execution::executor;
+ t.get_executor();
};
+3 -7
View File
@@ -417,12 +417,9 @@ AddJsonPackage(boost)
set(BOOST_NO_HEADERS ${Boost_ADDED})
if (Boost_ADDED)
if (MSVC OR ANDROID)
add_compile_definitions(YUZU_BOOST_v1)
endif()
add_compile_definitions(YUZU_BOOST_v1)
if (NOT MSVC OR CXX_CLANG)
# boost sucks
# solaris sucks
if (SOLARIS)
add_compile_options($<$<COMPILE_LANGUAGE:C,CXX>:-pthreads>)
endif()
@@ -431,8 +428,7 @@ if (Boost_ADDED)
target_compile_options(boost_icl INTERFACE $<$<COMPILE_LANGUAGE:C,CXX>:-Wno-shadow>)
target_compile_options(boost_asio INTERFACE
$<$<COMPILE_LANGUAGE:C,CXX>:-Wno-conversion>
$<$<COMPILE_LANGUAGE:C,CXX>:-Wno-implicit-fallthrough>
)
$<$<COMPILE_LANGUAGE:C,CXX>:-Wno-implicit-fallthrough>)
endif()
endif()
+6 -9
View File
@@ -6,22 +6,19 @@
"version": "v0.19.0"
},
"boost": {
"artifact": "%VERSION%-cmake.tar.xz",
"artifact": "boost-%VERSION%.tar.zst",
"find_args": "CONFIG OPTIONAL_COMPONENTS headers context system fiber filesystem",
"hash": "6ae6e94664fe7f2fb01976b59b276ac5df8085c7503fa829d810fbfe495960cfec44fa2c36e2cb23480bc19c956ed199d4952b02639a00a6c07625d4e7130c2d",
"hash": "681d97be4386767662e230b2e7c9754d45e709cc5db4e747c23c27d1f5b0e87770bdf19b0bc44dcb0e8349324887bbb8fffb432098f4d0ce74a5043021a90afc",
"min_version": "1.57",
"package": "Boost",
"patches": [
"0001-clang-cl.patch"
],
"repo": "boostorg/boost",
"version": "boost-1.90.0"
"repo": "eden-emulator/ext-boost",
"version": "1.92.0"
},
"boost_headers": {
"bundled": true,
"hash": "4ef845775e2277a8104ded6ddf749aa262ce52cf8438042869a048f9a0156dd772fbbcfa74efa1378fecef339b7286f6fe4b4feb5c45d49966b35d08e3e83507",
"hash": "c5fa2cd72f6e6666b7963b97bc359c75284b8fb540c30f3629a028b85270c9bc66c8a051383964f2bd4c1e005a4691593d15696e7ef39ea87cf6cff9e5691fb2",
"repo": "boostorg/headers",
"version": "boost-1.90.0"
"version": "boost-1.91.0"
},
"catch2": {
"hash": "7eea385d79d88a5690cde131fe7ccda97d5c54ea09d6f515000d7bf07c828809d61c1ac99912c1ee507cf933f61c1c47ecdcc45df7850ffa82714034b0fccf35",
+628 -583
View File
File diff suppressed because it is too large Load Diff
+547 -502
View File
File diff suppressed because it is too large Load Diff
+547 -502
View File
File diff suppressed because it is too large Load Diff
+547 -502
View File
File diff suppressed because it is too large Load Diff
+547 -502
View File
File diff suppressed because it is too large Load Diff
+547 -502
View File
File diff suppressed because it is too large Load Diff
+548 -503
View File
File diff suppressed because it is too large Load Diff
+547 -502
View File
File diff suppressed because it is too large Load Diff
+547 -502
View File
File diff suppressed because it is too large Load Diff
+547 -502
View File
File diff suppressed because it is too large Load Diff
+547 -502
View File
File diff suppressed because it is too large Load Diff
+547 -502
View File
File diff suppressed because it is too large Load Diff
+547 -502
View File
File diff suppressed because it is too large Load Diff
+547 -502
View File
File diff suppressed because it is too large Load Diff
+547 -502
View File
File diff suppressed because it is too large Load Diff
+547 -502
View File
File diff suppressed because it is too large Load Diff
+547 -502
View File
File diff suppressed because it is too large Load Diff
+547 -502
View File
File diff suppressed because it is too large Load Diff
+547 -502
View File
File diff suppressed because it is too large Load Diff
+547 -502
View File
File diff suppressed because it is too large Load Diff
+550 -508
View File
File diff suppressed because it is too large Load Diff
+547 -502
View File
File diff suppressed because it is too large Load Diff
+547 -502
View File
File diff suppressed because it is too large Load Diff
+547 -502
View File
File diff suppressed because it is too large Load Diff
+547 -502
View File
File diff suppressed because it is too large Load Diff
+573 -527
View File
File diff suppressed because it is too large Load Diff
+547 -502
View File
File diff suppressed because it is too large Load Diff
+2 -2
View File
@@ -788,7 +788,7 @@ int Java_org_yuzu_yuzu_1emu_NativeLibrary_installFileToNand(JNIEnv* env, jobject
jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_doesUpdateMatchProgram(JNIEnv* env, jobject jobj,
jstring jprogramId,
jstring jupdatePath) {
u64 program_id = EmulationSession::GetProgramId(env, jprogramId);
const u64 program_id = FileSys::GetBaseTitleID(EmulationSession::GetProgramId(env, jprogramId));
std::string updatePath = Common::Android::GetJString(env, jupdatePath);
std::shared_ptr<FileSys::NSP> nsp = std::make_shared<FileSys::NSP>(
EmulationSession::GetInstance().System().GetFilesystem()->OpenFile(
@@ -796,7 +796,7 @@ jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_doesUpdateMatchProgram(JNIEnv* en
for (const auto& item : nsp->GetNCAs()) {
for (const auto& nca_details : item.second) {
if (nca_details.second->GetName().ends_with(".cnmt.nca")) {
auto update_id = nca_details.second->GetTitleId() & ~0xFFFULL;
const auto update_id = FileSys::GetBaseTitleID(nca_details.second->GetTitleId());
if (update_id == program_id) {
return true;
}
@@ -289,6 +289,7 @@
<string name="gpu_driver_fetcher">Obtenedor de controladores de la GPU</string>
<string name="gpu_driver_manager">Gestor de controladores de la GPU</string>
<string name="install_gpu_driver_description">Instale los controladores alternativos para obtener un posible mejor rendimiento o precisión</string>
<string name="post_processing_add">Añadir efecto</string>
<string name="post_processing_open_list">Abrir lista</string>
<string name="post_processing_close_list">Cerrar lista</string>
<string name="post_processing_reset">Restablecer a predeterminado</string>
@@ -318,6 +318,13 @@
<string name="frame_gen_target_rate">Целевая частота кадров</string>
<string name="frame_gen_target_rate_description">Выберите значение под ваш дисплей. Множитель подстроится сам, чтобы его держать, и откатит изменения, если игра начнёт тормозить.</string>
<string name="frame_gen_target_rate_off">Использовать фиксированный множитель</string>
<string name="frame_gen_on">Вкл.</string>
<string name="frame_gen_off">Выкл.</string>
<string name="frame_gen_fixed">Фикс.</string>
<string name="frame_gen_flow_auto">Авто</string>
<string name="frame_gen_quick_description">Включите или выключите генерацию кадров и выберите множитель кадров.</string>
<string name="frame_gen_target_rate_quick_description">Генерация кадров с заданной частотой кадров. Множитель регулируется автоматически.</string>
<string name="frame_gen_flow_scale_quick_description">Разрешение, используемое для оценки движения между кадрами. Меньшие значения экономят время ГПУ.</string>
<string name="frame_gen_queue_target">Целевой размер очереди кадров</string>
<string name="frame_gen_queue_target_description">Сколько готовых кадров может ожидать перед выводом на экран. Более длинные очереди сглаживают скачки ГПУ ценой задержки ввода.</string>
<string name="frame_gen_queue_target_0">Минимальная задержка (Без буферизации)</string>
@@ -328,12 +335,14 @@
<string name="frame_gen_flow_scale">Разрешение оценки движения</string>
<string name="frame_gen_flow_scale_description">Разрешение прохода оптического потока в долях от выходного разрешения. Его понижение — самый дешёвый способ вернуть производительность.</string>
<string name="frame_gen_unsupported">Генерация кадров недоступна</string>
<string name="frame_gen_unsupported_description">В этом ГПУ драйвере отсутствует поддержка модели памяти Vulkan или половинной точности (float16), которая требуется шейдерам Lossless Scaling.</string>
<string name="lossless_scaling_setup_description">Опционально. Укажите свой Lossless.dll для включения генерации кадров позже.</string>
<string name="lossless_scaling_install">Установить Lossless.dll</string>
<string name="lossless_scaling_install_description">Для генерации кадров требуется ваша собственная легальная копия Lossless.dll из Lossless Scaling</string>
<string name="lossless_scaling_replace_description">Выбрать другой файл Lossless.dll</string>
<string name="frame_generation_support">Генерация кадров</string>
<string name="frame_generation_supported">Поддерживается</string>
<string name="frame_generation_unsupported">Не поддерживается (нет модели памяти Vulkan или float16)</string>
<string name="lossless_scaling_description">Предоставьте свою копию Lossless.dll для включения генерации кадров</string>
<string name="lossless_scaling_installed">Установлена</string>
<string name="lossless_scaling_not_installed">Не установлена</string>
@@ -1,6 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation">
<string name="app_disclaimer">本软件可以运行用于 Nintendo Switch 游戏机的游戏。不包含任何游戏或密钥。<br /><br />在您开始之前, 请定位您设备存储上的 <![CDATA[<b> prod.keys </b>]]> 文件。<br /><br /><![CDATA[<a href=\"https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/user/QuickStart.md\">了解更多</a>]]></string>
<string name="notice_notification_channel_name">错误与注意事项</string>
<string name="notice_notification_channel_description">当发生错误时显示通知。</string>
<string name="notification_permission_not_granted">未授予通知权限!</string>
@@ -290,6 +291,25 @@
<string name="gpu_driver_fetcher">GPU驱动获取器</string>
<string name="gpu_driver_manager">GPU 驱动管理器</string>
<string name="install_gpu_driver_description">安装替代的驱动程序以获得更好的性能和精度</string>
<string name="post_processing">后处理效果</string>
<string name="post_processing_description">在渲染完成后应用 ReShade FX 效果</string>
<string name="post_processing_per_game_description">配置此游戏的效果链</string>
<string name="post_processing_add">添加效果</string>
<string name="post_processing_remove">移除</string>
<string name="post_processing_open_list">打开列表</string>
<string name="post_processing_close_list">关闭列表</string>
<string name="post_processing_remove_all">移除效果</string>
<string name="post_processing_preset_locked">预设已激活。在开始游戏前可以在后处理效果中编辑它。</string>
<string name="post_processing_preset_modified">数值已从原始预设被更改。</string>
<string name="post_processing_presets">预设</string>
<string name="post_processing_preset_new">新建预设</string>
<string name="post_processing_preset_new_description">把您加载的效果及当前数值保存为一个预设,以便今后选用。</string>
<string name="post_processing_preset_name_invalid">请给预设起一个不含等号的名称。</string>
<string name="post_processing_preset_none">无预设</string>
<string name="post_processing_preset_delete">删除预设</string>
<string name="post_processing_preset_reset">重置预设值</string>
<string name="post_processing_reset">重置为默认值</string>
<string name="post_processing_empty">找不到效果。请在此文件中放置 .fx 文件:</string>
<string name="frame_gen">帧生成</string>
<string name="frame_gen_per_game_description">配置针对此游戏的帧生成设定</string>
<string name="frame_gen_description">设定要在使用无损缩放渲染的帧之间应用的插帧。启用后强制采用FIFO呈现模式 。</string>
@@ -304,6 +324,13 @@
<string name="frame_gen_target_rate_60">60 FPS</string>
<string name="frame_gen_target_rate_90">90 FPS</string>
<string name="frame_gen_target_rate_120">120 FPS</string>
<string name="frame_gen_on">开</string>
<string name="frame_gen_off">关</string>
<string name="frame_gen_fixed">固定</string>
<string name="frame_gen_flow_auto">自动</string>
<string name="frame_gen_quick_description">打开或关闭帧生成并选择帧生成倍数。</string>
<string name="frame_gen_target_rate_quick_description">生成帧到目标帧率。倍率会自动调整。</string>
<string name="frame_gen_flow_scale_quick_description">用来估算帧间运动的分辨率。较低的值可以节省 GPU 时间。</string>
<string name="frame_gen_queue_target">帧队列目标</string>
<string name="frame_gen_queue_target_description">在显示之前有多少已完成渲染的帧正在等待。较大的队列可以缓解 GPU 突发的压力,但会增加输入延迟。</string>
<string name="frame_gen_queue_target_0">最低延迟 (无缓冲)</string>
@@ -314,12 +341,14 @@
<string name="frame_gen_flow_scale">运动预估分辨率</string>
<string name="frame_gen_flow_scale_description">光流通道的分辨率,以输出的比例表示。降低它是提升性能最经济的做法。</string>
<string name="frame_gen_unsupported">帧生成不可用</string>
<string name="frame_gen_unsupported_description">这个 GPU 驱动缺少 Lossless Scaling 着色器所需的 Vulkan 内存模型或半精度 (float16) 支持。</string>
<string name="lossless_scaling_setup_description">作为可选项。请提供您自己的 Lossless.dll 以便在之后可以启用帧生成。</string>
<string name="lossless_scaling_install">安装 Lossless.dll</string>
<string name="lossless_scaling_install_description">帧生成需要您自己从 Lossless Scaling 获得合法的 Lossless.dll 副本</string>
<string name="lossless_scaling_replace_description">选择其它 Lossless.dll 副本</string>
<string name="frame_generation_support">帧生成</string>
<string name="frame_generation_supported">支持</string>
<string name="frame_generation_unsupported">不支持 (无 Vulkan 内存模型或 float16)</string>
<string name="lossless_scaling">无损缩放</string>
<string name="lossless_scaling_description">提供您自己的 Lossless.dll 文件以启用帧生成</string>
<string name="lossless_scaling_installed">已安装</string>
@@ -611,6 +640,11 @@
<string name="log">日志记录</string>
<string name="flush_by_line">按行刷新调试日志</string>
<string name="flush_by_line_description">在每行写入时刷新调试日志,使在崩溃或冻结时调试更容易。</string>
<string name="extended_logging">开启扩展日志</string>
<string name="extended_logging_description">将最大日志文件大小从 100 MiB 提升至 1GiB。</string>
<string name="log_filter">日志筛选器</string>
<string name="log_filter_description">控制 Eden 的日志类别。例如: *:Info Service.LM:Debug</string>
<!-- GPU Logging strings -->
<string name="gpu_logging_header">GPU 日志</string>
<string name="gpu_log_level">日志等级</string>
@@ -894,6 +928,8 @@
<string name="loader_error_file_not_found">ROM 文件不存在</string>
<string name="loader_requires_firmware">游戏需要固件</string>
<string name="loader_requires_firmware_description"><![CDATA[这个游戏可能需要固件才能正常运行,但您还没有安装任何固件。请在启动前安装固件,或者按 “确定” 继续启动。]]></string>
<!-- Intent Launch strings -->
<string name="searching_for_game">正在搜索游戏...</string>
<string name="game_not_found_for_title_id">未找到标题ID的游戏: %1$s</string>
@@ -229,7 +229,7 @@
<string name="add_directory_success">遊戲目錄新增成功</string>
<string name="enable_update_checks">檢查更新</string>
<string name="enable_update_checks_description">在Eden啟動時檢查有無新版本並選擇是否要下載與安裝更新</string>
<string name="update_available">偵測到新版本</string>
<string name="update_available">發現新版本</string>
<string name="update_available_description">有更新可供安裝:%1$s\n\n您要進行更新嗎?</string>
<string name="downloading_update">下載更新中</string>
<string name="update_download_failed">更新下載失敗</string>
@@ -1020,9 +1020,40 @@
<string name="memory_6gb">6GB (不穩定)</string>
<string name="memory_8gb">8GB (不穩定)</string>
<!-- CPU clock levels -->
<string name="clock_normal">正常</string>
<string name="clock_boost">加速</string>
<string name="clock_fast">超頻</string>
<!-- GPU clock levels -->
<string name="fast_gpu_normal">正常</string>
<string name="fast_gpu_medium">加速</string>
<string name="fast_gpu_high">超頻</string>
<!-- GPU swizzle texture size -->
<string name="gpu_texturesizeswizzle_verysmall">極小 (16MB)</string>
<string name="gpu_texturesizeswizzle_small">小(32MB)</string>
<string name="gpu_texturesizeswizzle_normal">正常(128MB)</string>
<string name="gpu_texturesizeswizzle_large">大(256MB)</string>
<string name="gpu_texturesizeswizzle_verylarge">極大(512MB)</string>
<!-- GPU swizzle streams -->
<string name="gpu_swizzle_verylow">極小(4MB)</string>
<string name="gpu_swizzle_low">小(8MB)</string>
<string name="gpu_swizzle_normal">正常(16MB)</string>
<string name="gpu_swizzle_medium">中(32MB)</string>
<string name="gpu_swizzle_high">大(64MB)</string>
<!-- GPU swizzle chunks -->
<string name="gpu_swizzlechunk_verylow">極小(32)</string>
<string name="gpu_swizzlechunk_low">小(64)</string>
<string name="gpu_swizzlechunk_normal">正常(128)</string>
<string name="gpu_swizzlechunk_medium">中(256)</string>
<string name="gpu_swizzlechunk_high">大(512)</string>
<!-- Temperature Units -->
<string name="temperature_celsius">攝氏度</string>
<string name="temperature_fahrenheit">華氏度</string>
<string name="temperature_celsius">攝氏</string>
<string name="temperature_fahrenheit">華氏</string>
<!-- Memory Sizes -->
<string name="memory_byte_shorthand">B</string>
@@ -1035,6 +1066,8 @@
<string name="renderer_none">無</string>
<!-- Renderer Accuracy -->
<string name="renderer_accuracy_low">效能</string>
<string name="renderer_accuracy_high">準確</string>
<!-- DMA Accuracy -->
+1 -1
View File
@@ -12,7 +12,7 @@
namespace AudioCore {
AudioCore::AudioCore(Core::System& system) {
audio_manager.emplace();
audio_manager.emplace(system);
CreateSinks();
// Must be created after the sinks
adsp.emplace(system, *output_sink);
+12 -15
View File
@@ -15,12 +15,12 @@
namespace AudioCore::AudioIn {
Manager::Manager(Core::System& system_) : system{system_} {
Manager::Manager(Core::System& system) {
std::iota(session_ids.begin(), session_ids.end(), 0);
num_free_sessions = MaxInSessions;
}
Result Manager::AcquireSessionId(size_t& session_id) {
Result Manager::AcquireSessionId(Core::System& system, size_t& session_id) {
if (num_free_sessions == 0) {
LOG_ERROR(Service_Audio, "All 4 AudioIn sessions are in use, cannot create any more");
return Service::Audio::ResultOutOfSessions;
@@ -31,7 +31,7 @@ Result Manager::AcquireSessionId(size_t& session_id) {
return ResultSuccess;
}
void Manager::ReleaseSessionId(const size_t session_id) {
void Manager::ReleaseSessionId(Core::System& system, const size_t session_id) {
std::scoped_lock l{mutex};
LOG_DEBUG(Service_Audio, "Freeing AudioIn session {}", session_id);
session_ids[free_session_id] = session_id;
@@ -41,21 +41,20 @@ void Manager::ReleaseSessionId(const size_t session_id) {
applet_resource_user_ids[session_id] = 0;
}
Result Manager::LinkToManager() {
Result Manager::LinkToManager(Core::System& system) {
std::scoped_lock l{mutex};
if (!linked_to_manager) {
system.AudioCore().GetAudioManager().SetInManager(std::bind(&Manager::BufferReleaseAndRegister, this));
system.AudioCore().GetAudioManager().SetInManager(this, &Manager::BufferReleaseAndRegister);
linked_to_manager = true;
}
return ResultSuccess;
}
void Manager::Start() {
void Manager::Start(Core::System& system) {
if (sessions_started) {
return;
}
std::scoped_lock l{mutex};
for (auto& session : sessions) {
if (session) {
@@ -66,21 +65,19 @@ void Manager::Start() {
sessions_started = true;
}
void Manager::BufferReleaseAndRegister() {
std::scoped_lock l{mutex};
for (auto& session : sessions) {
void Manager::BufferReleaseAndRegister(void *data, Core::System& system) noexcept {
Manager* this_ = (Manager*)data;
std::scoped_lock l{this_->mutex};
for (auto& session : this_->sessions) {
if (session != nullptr) {
session->ReleaseAndRegisterBuffers();
}
}
}
u32 Manager::GetDeviceNames(std::span<Renderer::AudioDevice::AudioDeviceName> names,
[[maybe_unused]] const bool filter) {
u32 Manager::GetDeviceNames(Core::System& system, std::span<Renderer::AudioDevice::AudioDeviceName> names, [[maybe_unused]] const bool filter) {
std::scoped_lock l{mutex};
LinkToManager();
LinkToManager(system);
auto input_devices{Sink::GetDeviceListForSink(Settings::values.sink_id.GetValue(), true)};
if (!input_devices.empty() && !names.empty()) {
names[0] = Renderer::AudioDevice::AudioDeviceName("Uac");
+10 -11
View File
@@ -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-License-Identifier: GPL-2.0-or-later
@@ -30,31 +33,29 @@ public:
* @param session_id - Output session_id.
* @return Result code.
*/
Result AcquireSessionId(size_t& session_id);
Result AcquireSessionId(Core::System& system, size_t& session_id);
/**
* Release a session id on close.
*
* @param session_id - Session id to free.
*/
void ReleaseSessionId(size_t session_id);
void ReleaseSessionId(Core::System& system, const size_t session_id);
/**
* Link the audio in manager to the main audio manager.
*
* @return Result code.
*/
Result LinkToManager();
Result LinkToManager(Core::System& system);
/**
* Start the audio in manager.
*/
void Start();
void Start(Core::System& system);
/**
* Callback function, called by the audio manager when the audio in event is signalled.
*/
void BufferReleaseAndRegister();
/// @brief Callback function, called by the audio manager when the audio in event is signalled.
static void BufferReleaseAndRegister(void *data, Core::System& system) noexcept;
/**
* Get a list of audio in device names.
@@ -64,10 +65,8 @@ public:
*
* @return Number of names written.
*/
u32 GetDeviceNames(std::span<Renderer::AudioDevice::AudioDeviceName> names, bool filter);
u32 GetDeviceNames(Core::System& system, std::span<Renderer::AudioDevice::AudioDeviceName> names, bool filter);
/// Core system
Core::System& system;
/// Array of session ids
std::array<size_t, MaxInSessions> session_ids{};
/// Array of resource user ids
+7 -5
View File
@@ -11,8 +11,8 @@
namespace AudioCore {
AudioManager::AudioManager() {
thread = std::jthread([this](std::stop_token stop_token) {
AudioManager::AudioManager(Core::System& system) {
thread = std::jthread([&](std::stop_token stop_token) {
Common::SetCurrentThreadName("AudioManager");
std::unique_lock l{events.GetAudioEventLock()};
events.ClearEvents();
@@ -25,7 +25,7 @@ AudioManager::AudioManager() {
const auto event_type = Event::Type(i);
if (events.CheckAudioEventSet(event_type) || timed_out) {
if (buffer_events[i]) {
buffer_events[i]();
buffer_events[i](buffer_data[i], system);
}
}
events.SetAudioEvent(event_type, false);
@@ -42,12 +42,13 @@ void AudioManager::Shutdown() {
}
}
Result AudioManager::SetOutManager(BufferEventFunc buffer_func) {
Result AudioManager::SetOutManager(void *data, BufferEventFunc buffer_func) {
if (thread.joinable()) {
std::scoped_lock l{lock};
const auto index{events.GetManagerIndex(Event::Type::AudioOutManager)};
if (buffer_events[index] == nullptr) {
buffer_events[index] = std::move(buffer_func);
buffer_data[index] = data;
needs_update = true;
events.SetAudioEvent(Event::Type::AudioOutManager, true);
}
@@ -56,12 +57,13 @@ Result AudioManager::SetOutManager(BufferEventFunc buffer_func) {
return Service::Audio::ResultOperationFailed;
}
Result AudioManager::SetInManager(BufferEventFunc buffer_func) {
Result AudioManager::SetInManager(void *data, BufferEventFunc buffer_func) {
if (thread.joinable()) {
std::scoped_lock l{lock};
const auto index{events.GetManagerIndex(Event::Type::AudioInManager)};
if (buffer_events[index] == nullptr) {
buffer_events[index] = std::move(buffer_func);
buffer_data[index] = data;
needs_update = true;
events.SetAudioEvent(Event::Type::AudioInManager, true);
}
+16 -18
View File
@@ -16,6 +16,10 @@
#include "audio_core/audio_event.h"
namespace Core {
class System;
}
union Result;
namespace AudioCore {
@@ -34,31 +38,24 @@ namespace AudioCore {
* This is only used by audio in and audio out.
*/
class AudioManager {
using BufferEventFunc = std::function<void()>;
using BufferEventFunc = void (*)(void *data, Core::System& system) noexcept;
public:
explicit AudioManager();
explicit AudioManager(Core::System& system);
/**
* Shutdown the audio manager.
*/
void Shutdown();
/**
* Register the out manager, keeping a function to be called when the out event is signalled.
*
* @param buffer_func - Function to be called on signal.
* @return Result code.
*/
Result SetOutManager(BufferEventFunc buffer_func);
/// Register the out manager, keeping a function to be called when the out event is signalled.
/// @param buffer_func - Function to be called on signal.
/// @return Result code.
Result SetOutManager(void *data, BufferEventFunc buffer_func);
/**
* Register the in manager, keeping a function to be called when the in event is signalled.
*
* @param buffer_func - Function to be called on signal.
* @return Result code.
*/
Result SetInManager(BufferEventFunc buffer_func);
/// Register the in manager, keeping a function to be called when the in event is signalled.
/// @param buffer_func - Function to be called on signal.
/// @return Result code.
Result SetInManager(void *data, BufferEventFunc buffer_func);
/**
* Set an event to signalled, and signal the thread.
@@ -73,8 +70,9 @@ private:
bool needs_update{};
/// Events to be set and signalled
Event events{};
/// Callbacks for each manager
/// Callbacks (and user data) for each manager
std::array<BufferEventFunc, 3> buffer_events{};
std::array<void*, 3> buffer_data{};
/// General lock
std::mutex lock{};
/// Main thread for waiting and callbacks
+17 -25
View File
@@ -14,12 +14,12 @@
namespace AudioCore::AudioOut {
Manager::Manager(Core::System& system_) : system{system_} {
Manager::Manager(Core::System& system) {
std::iota(session_ids.begin(), session_ids.end(), 0);
num_free_sessions = MaxOutSessions;
}
Result Manager::AcquireSessionId(size_t& session_id) {
Result Manager::AcquireSessionId(Core::System& system, size_t& session_id) {
if (num_free_sessions == 0) {
LOG_ERROR(Service_Audio, "All 12 Audio Out sessions are in use, cannot create any more");
return Service::Audio::ResultOutOfSessions;
@@ -30,7 +30,7 @@ Result Manager::AcquireSessionId(size_t& session_id) {
return ResultSuccess;
}
void Manager::ReleaseSessionId(const size_t session_id) {
void Manager::ReleaseSessionId(Core::System& system, const size_t session_id) {
std::scoped_lock l{mutex};
LOG_DEBUG(Service_Audio, "Freeing AudioOut session {}", session_id);
session_ids[free_session_id] = session_id;
@@ -40,44 +40,36 @@ void Manager::ReleaseSessionId(const size_t session_id) {
applet_resource_user_ids[session_id] = 0;
}
Result Manager::LinkToManager() {
Result Manager::LinkToManager(Core::System& system) {
std::scoped_lock l{mutex};
if (!linked_to_manager) {
system.AudioCore().GetAudioManager().SetOutManager(std::bind(&Manager::BufferReleaseAndRegister, this));
system.AudioCore().GetAudioManager().SetOutManager(this, &Manager::BufferReleaseAndRegister);
linked_to_manager = true;
}
return ResultSuccess;
}
void Manager::Start() {
if (sessions_started) {
return;
}
std::scoped_lock l{mutex};
for (auto& session : sessions) {
if (session) {
session->StartSession();
void Manager::Start(Core::System& system) {
if (!sessions_started) {
std::scoped_lock l{mutex};
for (auto& session : sessions) {
if (session) {
session->StartSession();
}
}
sessions_started = true;
}
sessions_started = true;
}
void Manager::BufferReleaseAndRegister() {
std::scoped_lock l{mutex};
for (auto& session : sessions) {
void Manager::BufferReleaseAndRegister(void *data, Core::System& system) noexcept {
Manager* this_ = (Manager*)data;
std::scoped_lock l{this_->mutex};
for (auto& session : this_->sessions) {
if (session != nullptr) {
session->ReleaseAndRegisterBuffers();
}
}
}
u32 Manager::GetAudioOutDeviceNames(
std::vector<Renderer::AudioDevice::AudioDeviceName>& names) const {
names.emplace_back("DeviceOut");
return 1;
}
} // namespace AudioCore::AudioOut
+8 -15
View File
@@ -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-License-Identifier: GPL-2.0-or-later
@@ -29,42 +32,32 @@ public:
* @param session_id - Output session_id.
* @return Result code.
*/
Result AcquireSessionId(size_t& session_id);
Result AcquireSessionId(Core::System& system, size_t& session_id);
/**
* Release a session id on close.
*
* @param session_id - Session id to free.
*/
void ReleaseSessionId(size_t session_id);
void ReleaseSessionId(Core::System& system, const size_t session_id);
/**
* Link this manager to the main audio manager.
*
* @return Result code.
*/
Result LinkToManager();
Result LinkToManager(Core::System& system);
/**
* Start the audio out manager.
*/
void Start();
void Start(Core::System& system);
/**
* Callback function, called by the audio manager when the audio out event is signalled.
*/
void BufferReleaseAndRegister();
static void BufferReleaseAndRegister(void* data, Core::System& system) noexcept;
/**
* Get a list of audio out device names.
*
* @param names - Output container to write names to.
* @return Number of names written.
*/
u32 GetAudioOutDeviceNames(std::vector<Renderer::AudioDevice::AudioDeviceName>& names) const;
/// Core system
Core::System& system;
/// Array of session ids
std::array<size_t, MaxOutSessions> session_ids{};
/// Array of resource user ids
+4 -4
View File
@@ -6,14 +6,14 @@
#include "audio_core/audio_render_manager.h"
#include "audio_core/common/audio_renderer_parameter.h"
#include "audio_core/renderer/system_manager.h"
#include "audio_core/common/feature_support.h"
#include "core/core.h"
namespace AudioCore::Renderer {
Manager::Manager(Core::System& system_)
: system{system_}
, system_manager{std::make_unique<SystemManager>(system)}
: system_manager{std::make_unique<SystemManager>(system_)}
{
std::iota(session_ids.begin(), session_ids.end(), 0);
}
@@ -62,11 +62,11 @@ u32 Manager::GetSessionCount() const {
return session_count;
}
bool Manager::AddSystem(System& system_) {
bool Manager::AddSystem(Renderer::System& system_) {
return system_manager->Add(system_);
}
bool Manager::RemoveSystem(System& system_) {
bool Manager::RemoveSystem(Renderer::System& system_) {
return system_manager->Remove(system_);
}
+5 -4
View File
@@ -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-License-Identifier: GPL-2.0-or-later
@@ -71,7 +74,7 @@ public:
* @param system - The system to add.
* @return True if the system was successfully added, otherwise false.
*/
bool AddSystem(System& system);
bool AddSystem(Renderer::System& system);
/**
* Remove a renderer system from the manager.
@@ -79,7 +82,7 @@ public:
* @param system - The system to remove.
* @return True if the system was successfully removed, otherwise false.
*/
bool RemoveSystem(System& system);
bool RemoveSystem(Renderer::System& system);
/**
* Free a session id when the system wants to shut down.
@@ -89,8 +92,6 @@ public:
void ReleaseSessionId(s32 session_id);
private:
/// Core system
Core::System& system;
/// Session ids, -1 when in use
std::array<s32, MaxRendererSessions> session_ids{};
/// Number of active renderers
+24 -20
View File
@@ -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-License-Identifier: GPL-2.0-or-later
@@ -8,42 +11,43 @@
namespace AudioCore::AudioIn {
In::In(Core::System& system_, Manager& manager_, Kernel::KEvent* event_, size_t session_id_)
: manager{manager_}, parent_mutex{manager.mutex}, event{event_}, system{system_, event,
session_id_} {}
: manager{manager_}, parent_mutex{manager.mutex}, event{event_}
, audio_system{system_, event, session_id_}
{}
void In::Free() {
void In::Free(Core::System& system) {
std::scoped_lock l{parent_mutex};
manager.ReleaseSessionId(system.GetSessionId());
manager.ReleaseSessionId(system, audio_system.GetSessionId());
}
System& In::GetSystem() {
return system;
return audio_system;
}
AudioIn::State In::GetState() {
std::scoped_lock l{parent_mutex};
return system.GetState();
return audio_system.GetState();
}
Result In::StartSystem() {
std::scoped_lock l{parent_mutex};
return system.Start();
return audio_system.Start();
}
void In::StartSession() {
std::scoped_lock l{parent_mutex};
system.StartSession();
audio_system.StartSession();
}
Result In::StopSystem() {
std::scoped_lock l{parent_mutex};
return system.Stop();
return audio_system.Stop();
}
Result In::AppendBuffer(const AudioInBuffer& buffer, u64 tag) {
std::scoped_lock l{parent_mutex};
if (system.AppendBuffer(buffer, tag)) {
if (audio_system.AppendBuffer(buffer, tag)) {
return ResultSuccess;
}
return Service::Audio::ResultBufferCountReached;
@@ -51,20 +55,20 @@ Result In::AppendBuffer(const AudioInBuffer& buffer, u64 tag) {
void In::ReleaseAndRegisterBuffers() {
std::scoped_lock l{parent_mutex};
if (system.GetState() == State::Started) {
system.ReleaseBuffers();
system.RegisterBuffers();
if (audio_system.GetState() == State::Started) {
audio_system.ReleaseBuffers();
audio_system.RegisterBuffers();
}
}
bool In::FlushAudioInBuffers() {
std::scoped_lock l{parent_mutex};
return system.FlushAudioInBuffers();
return audio_system.FlushAudioInBuffers();
}
u32 In::GetReleasedBuffers(std::span<u64> tags) {
std::scoped_lock l{parent_mutex};
return system.GetReleasedBuffers(tags);
return audio_system.GetReleasedBuffers(tags);
}
Kernel::KReadableEvent& In::GetBufferEvent() {
@@ -74,27 +78,27 @@ Kernel::KReadableEvent& In::GetBufferEvent() {
f32 In::GetVolume() const {
std::scoped_lock l{parent_mutex};
return system.GetVolume();
return audio_system.GetVolume();
}
void In::SetVolume(f32 volume) {
std::scoped_lock l{parent_mutex};
system.SetVolume(volume);
audio_system.SetVolume(volume);
}
bool In::ContainsAudioBuffer(u64 tag) const {
std::scoped_lock l{parent_mutex};
return system.ContainsAudioBuffer(tag);
return audio_system.ContainsAudioBuffer(tag);
}
u32 In::GetBufferCount() const {
std::scoped_lock l{parent_mutex};
return system.GetBufferCount();
return audio_system.GetBufferCount();
}
u64 In::GetPlayedSampleCount() const {
std::scoped_lock l{parent_mutex};
return system.GetPlayedSampleCount();
return audio_system.GetPlayedSampleCount();
}
} // namespace AudioCore::AudioIn
+5 -2
View File
@@ -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-License-Identifier: GPL-2.0-or-later
@@ -30,7 +33,7 @@ public:
/**
* Free this audio in from the audio in manager.
*/
void Free();
void Free(Core::System& system);
/**
* Get this audio in's system.
@@ -141,7 +144,7 @@ private:
/// Buffer event, signalled when buffers are ready to be released
Kernel::KEvent* event;
/// Main audio in system
System system;
System audio_system;
};
} // namespace AudioCore::AudioIn
+24 -20
View File
@@ -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-License-Identifier: GPL-2.0-or-later
@@ -8,42 +11,43 @@
namespace AudioCore::AudioOut {
Out::Out(Core::System& system_, Manager& manager_, Kernel::KEvent* event_, size_t session_id_)
: manager{manager_}, parent_mutex{manager.mutex}, event{event_}, system{system_, event,
session_id_} {}
: manager{manager_}, parent_mutex{manager.mutex}, event{event_}
, audio_system{system_, event, session_id_}
{}
void Out::Free() {
void Out::Free(Core::System& system) {
std::scoped_lock l{parent_mutex};
manager.ReleaseSessionId(system.GetSessionId());
manager.ReleaseSessionId(system, audio_system.GetSessionId());
}
System& Out::GetSystem() {
return system;
return audio_system;
}
AudioOut::State Out::GetState() {
std::scoped_lock l{parent_mutex};
return system.GetState();
return audio_system.GetState();
}
Result Out::StartSystem() {
std::scoped_lock l{parent_mutex};
return system.Start();
return audio_system.Start();
}
void Out::StartSession() {
std::scoped_lock l{parent_mutex};
system.StartSession();
audio_system.StartSession();
}
Result Out::StopSystem() {
std::scoped_lock l{parent_mutex};
return system.Stop();
return audio_system.Stop();
}
Result Out::AppendBuffer(const AudioOutBuffer& buffer, const u64 tag) {
std::scoped_lock l{parent_mutex};
if (system.AppendBuffer(buffer, tag)) {
if (audio_system.AppendBuffer(buffer, tag)) {
return ResultSuccess;
}
return Service::Audio::ResultBufferCountReached;
@@ -51,20 +55,20 @@ Result Out::AppendBuffer(const AudioOutBuffer& buffer, const u64 tag) {
void Out::ReleaseAndRegisterBuffers() {
std::scoped_lock l{parent_mutex};
if (system.GetState() == State::Started) {
system.ReleaseBuffers();
system.RegisterBuffers();
if (audio_system.GetState() == State::Started) {
audio_system.ReleaseBuffers();
audio_system.RegisterBuffers();
}
}
bool Out::FlushAudioOutBuffers() {
std::scoped_lock l{parent_mutex};
return system.FlushAudioOutBuffers();
return audio_system.FlushAudioOutBuffers();
}
u32 Out::GetReleasedBuffers(std::span<u64> tags) {
std::scoped_lock l{parent_mutex};
return system.GetReleasedBuffers(tags);
return audio_system.GetReleasedBuffers(tags);
}
Kernel::KReadableEvent& Out::GetBufferEvent() {
@@ -74,27 +78,27 @@ Kernel::KReadableEvent& Out::GetBufferEvent() {
f32 Out::GetVolume() const {
std::scoped_lock l{parent_mutex};
return system.GetVolume();
return audio_system.GetVolume();
}
void Out::SetVolume(const f32 volume) {
std::scoped_lock l{parent_mutex};
system.SetVolume(volume);
audio_system.SetVolume(volume);
}
bool Out::ContainsAudioBuffer(const u64 tag) const {
std::scoped_lock l{parent_mutex};
return system.ContainsAudioBuffer(tag);
return audio_system.ContainsAudioBuffer(tag);
}
u32 Out::GetBufferCount() const {
std::scoped_lock l{parent_mutex};
return system.GetBufferCount();
return audio_system.GetBufferCount();
}
u64 Out::GetPlayedSampleCount() const {
std::scoped_lock l{parent_mutex};
return system.GetPlayedSampleCount();
return audio_system.GetPlayedSampleCount();
}
} // namespace AudioCore::AudioOut
+5 -2
View File
@@ -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-License-Identifier: GPL-2.0-or-later
@@ -30,7 +33,7 @@ public:
/**
* Free this audio out from the audio out manager.
*/
void Free();
void Free(Core::System& system);
/**
* Get this audio out's system.
@@ -141,7 +144,7 @@ private:
/// Buffer event, signalled when buffers are ready to be released
Kernel::KEvent* event;
/// Main audio out system
System system;
System audio_system;
};
} // namespace AudioCore::AudioOut
+13 -18
View File
@@ -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-FileCopyrightText: Copyright 2022 yuzu Emulator Project
@@ -34,42 +34,37 @@ constexpr std::array output_device_names{
AudioDevice::AudioDeviceName{"AudioExternalOutput"},
};
AudioDevice::AudioDevice(Core::System& system, const u64 applet_resource_user_id_,
const u32 revision)
: output_sink{system.AudioCore().GetOutputSink()},
applet_resource_user_id{applet_resource_user_id_}, user_revision{revision} {}
AudioDevice::AudioDevice(Core::System& system, const u64 applet_resource_user_id_, const u32 revision)
: applet_resource_user_id{applet_resource_user_id_}
, user_revision{revision}
{}
u32 AudioDevice::ListAudioDeviceName(std::span<AudioDeviceName> out_buffer) const {
std::span<const AudioDeviceName> names{};
if (CheckFeatureSupported(SupportTags::AudioUsbDeviceOutput, user_revision)) {
names = usb_device_names;
} else {
names = device_names;
}
const u32 out_count{static_cast<u32>((std::min)(out_buffer.size(), names.size()))};
for (u32 i = 0; i < out_count; i++) {
const u32 out_count = u32((std::min)(out_buffer.size(), names.size()));
for (u32 i = 0; i < out_count; i++)
out_buffer[i] = names[i];
}
return out_count;
}
u32 AudioDevice::ListAudioOutputDeviceName(std::span<AudioDeviceName> out_buffer) const {
const u32 out_count{static_cast<u32>((std::min)(out_buffer.size(), output_device_names.size()))};
for (u32 i = 0; i < out_count; i++) {
const u32 out_count = u32((std::min)(out_buffer.size(), output_device_names.size()));
for (u32 i = 0; i < out_count; i++)
out_buffer[i] = output_device_names[i];
}
return out_count;
}
void AudioDevice::SetDeviceVolumes(const f32 volume) {
output_sink.SetDeviceVolume(volume);
void AudioDevice::SetDeviceVolumes(Core::System& system, const f32 volume) {
system.AudioCore().GetOutputSink().SetDeviceVolume(volume);
}
f32 AudioDevice::GetDeviceVolume([[maybe_unused]] std::string_view name) const {
return output_sink.GetDeviceVolume();
f32 AudioDevice::GetDeviceVolume(Core::System& system, [[maybe_unused]] std::string_view name) const {
return system.AudioCore().GetOutputSink().GetDeviceVolume();
}
} // namespace AudioCore::Renderer
+5 -4
View File
@@ -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-License-Identifier: GPL-2.0-or-later
@@ -54,7 +57,7 @@ public:
*
* @param volume - Volume to set.
*/
void SetDeviceVolumes(f32 volume);
void SetDeviceVolumes(Core::System& system, f32 volume);
/**
* Get the volume for a given device name.
@@ -63,11 +66,9 @@ public:
* @param name - Name of the device to check. Unused.
* @return Volume of the device.
*/
f32 GetDeviceVolume(std::string_view name) const;
f32 GetDeviceVolume(Core::System& system, std::string_view name) const;
private:
/// Backend output sink for the device
Sink::Sink& output_sink;
/// Resource id this device is used for
const u64 applet_resource_user_id;
/// User audio renderer revision
+18 -25
View File
@@ -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-License-Identifier: GPL-2.0-or-later
@@ -13,56 +16,46 @@
namespace AudioCore::Renderer {
Renderer::Renderer(Core::System& system_, Manager& manager_, Kernel::KEvent* rendered_event)
: core{system_}, manager{manager_}, system{system_, rendered_event} {}
: manager{manager_}
, audio_system{system_, rendered_event}
{}
Result Renderer::Initialize(const AudioRendererParameterInternal& params,
Kernel::KTransferMemory* transfer_memory,
const u64 transfer_memory_size, Kernel::KProcess* process_handle,
const u64 applet_resource_user_id, const s32 session_id) {
Result Renderer::Initialize(const AudioRendererParameterInternal& params, Kernel::KTransferMemory* transfer_memory, const u64 transfer_memory_size, Kernel::KProcess* process_handle, const u64 applet_resource_user_id, const s32 session_id) {
if (params.execution_mode == ExecutionMode::Auto) {
if (!manager.AddSystem(system)) {
LOG_ERROR(Service_Audio,
"Both Audio Render sessions are in use, cannot create any more");
if (!manager.AddSystem(audio_system)) {
LOG_ERROR(Service_Audio, "Both Audio Render sessions are in use, cannot create any more");
return Service::Audio::ResultOutOfSessions;
}
system_registered = true;
}
initialized = true;
system.Initialize(params, transfer_memory, transfer_memory_size, process_handle,
applet_resource_user_id, session_id);
audio_system.Initialize(params, transfer_memory, transfer_memory_size, process_handle, applet_resource_user_id, session_id);
return ResultSuccess;
}
void Renderer::Finalize() {
auto session_id{system.GetSessionId()};
system.Finalize();
auto const session_id{audio_system.GetSessionId()};
audio_system.Finalize();
if (system_registered) {
manager.RemoveSystem(system);
manager.RemoveSystem(audio_system);
system_registered = false;
}
manager.ReleaseSessionId(session_id);
}
System& Renderer::GetSystem() {
return system;
return audio_system;
}
void Renderer::Start() {
system.Start();
audio_system.Start();
}
void Renderer::Stop() {
system.Stop();
audio_system.Stop();
}
Result Renderer::RequestUpdate(std::span<const u8> input, std::span<u8> performance,
std::span<u8> output) {
return system.Update(input, performance, output);
Result Renderer::RequestUpdate(std::span<const u8> input, std::span<u8> performance, std::span<u8> output) {
return audio_system.Update(input, performance, output);
}
} // namespace AudioCore::Renderer
+4 -5
View File
@@ -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-License-Identifier: GPL-2.0-or-later
@@ -83,16 +86,12 @@ public:
std::span<u8> output);
private:
/// System core
Core::System& core;
/// Manager this renderer is registered with
Manager& manager;
/// Is the audio renderer initialized?
bool initialized{};
/// Is the system registered with the manager?
bool system_registered{};
/// Audio render system, main driver of audio rendering
System system;
System audio_system;
};
} // namespace Renderer
+10 -12
View File
@@ -100,8 +100,9 @@ u64 System::GetWorkBufferSize(const AudioRendererParameterInternal& params) {
}
System::System(Core::System& core_, Kernel::KEvent* adsp_rendered_event_)
: core{core_}, audio_renderer{core.AudioCore().ADSP().AudioRenderer()},
adsp_rendered_event{adsp_rendered_event_} {}
: core{core_}
, adsp_rendered_event{adsp_rendered_event_}
{}
Result System::Initialize(const AudioRendererParameterInternal& params,
Kernel::KTransferMemory* transfer_memory, u64 transfer_memory_size,
@@ -406,7 +407,7 @@ void System::Finalize() {
return;
}
if (active) {
if (IsActive()) {
Stop();
}
@@ -433,14 +434,12 @@ void System::Start() {
std::scoped_lock l{lock};
frames_elapsed = 0;
state = State::Started;
active = true;
}
void System::Stop() {
{
std::scoped_lock l{lock};
state = State::Stopped;
active = false;
}
if (execution_mode == ExecutionMode::Auto) {
@@ -480,8 +479,7 @@ Result System::Update(std::span<const u8> input, std::span<u8> performance, std:
return result;
}
result = info_updater.UpdateEffects(effect_context, active, memory_pool_workbuffer,
memory_pool_count);
result = info_updater.UpdateEffects(effect_context, IsActive(), memory_pool_workbuffer, memory_pool_count);
if (result.IsError()) {
LOG_ERROR(Service_Audio, "Failed to update Effects!");
return result;
@@ -582,16 +580,16 @@ u32 System::GetRenderingDevice() const {
}
bool System::IsActive() const {
return active;
return state == State::Started;
}
void System::SendCommandToDsp() {
std::scoped_lock l{lock};
auto& audio_renderer = core.AudioCore().ADSP().AudioRenderer();
if (initialized) {
if (active) {
if (IsActive()) {
terminate_event.Reset();
const auto remaining_command_count{audio_renderer.GetRemainCommandCount(session_id)};
const auto remaining_command_count = audio_renderer.GetRemainCommandCount(session_id);
u64 command_size{0};
if (remaining_command_count) {
@@ -738,7 +736,7 @@ u64 System::GenerateCommand(std::span<u8> in_command_buffer,
const auto end_time{core.CoreTiming().GetGlobalTimeNs().count()};
total_ticks_elapsed += end_time - start_time;
num_command_lists_generated++;
render_start_tick = audio_renderer.GetRenderingStartTick(session_id);
render_start_tick = core.AudioCore().ADSP().AudioRenderer().GetRenderingStartTick(session_id);
frames_elapsed++;
return command_buffer.size;
+3 -4
View File
@@ -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-License-Identifier: GPL-2.0-or-later
@@ -219,12 +222,8 @@ public:
private:
/// Core system
Core::System& core;
/// Reference to the ADSP's AudioRenderer for communication
::AudioCore::ADSP::AudioRenderer::AudioRenderer& audio_renderer;
/// Is this system initialized?
bool initialized{};
/// Is this system currently active?
std::atomic<bool> active{};
/// State of the system
State state{State::Stopped};
/// Sample rate for the system
+17 -25
View File
@@ -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-FileCopyrightText: Copyright 2022 yuzu Emulator Project
@@ -16,22 +16,22 @@
namespace AudioCore::Renderer {
SystemManager::SystemManager(Core::System& core_)
: core{core_}, audio_renderer{core.AudioCore().ADSP().AudioRenderer()} {}
: core{core_}
{}
SystemManager::~SystemManager() {
Stop();
}
void SystemManager::InitializeUnsafe() {
if (!active) {
active = true;
audio_renderer.Start();
if (!thread.joinable()) {
core.AudioCore().ADSP().AudioRenderer().Start();
thread = std::jthread([this](std::stop_token stop_token) {
Common::SetCurrentThreadName("AudioRenderSystemManager");
Common::SetCurrentThreadPriority(Common::ThreadPriority::High);
while (active && !stop_token.stop_requested()) {
auto& audio_renderer = core.AudioCore().ADSP().AudioRenderer();
while (!stop_token.stop_requested()) {
{
std::scoped_lock l{mutex1};
std::scoped_lock lk{mutex};
for (auto system : systems)
system->SendCommandToDsp();
}
@@ -43,39 +43,31 @@ void SystemManager::InitializeUnsafe() {
}
void SystemManager::Stop() {
if (active) {
active = false;
if (thread.joinable()) {
thread.request_stop();
thread.join();
audio_renderer.Stop();
core.AudioCore().ADSP().AudioRenderer().Stop();
}
}
bool SystemManager::Add(System& system_) {
std::scoped_lock l2{mutex2};
std::scoped_lock lk{mutex};
if (systems.size() + 1 > MaxRendererSessions) {
LOG_ERROR(Service_Audio, "Maximum AudioRenderer Systems active, cannot add more!");
return false;
}
{
std::scoped_lock l{mutex1};
if (systems.empty())
InitializeUnsafe();
}
if (systems.empty())
InitializeUnsafe();
systems.push_back(&system_);
return true;
}
bool SystemManager::Remove(System& system_) {
std::scoped_lock l2{mutex2};
{
std::scoped_lock l{mutex1};
if (systems.remove(&system_) == 0) {
LOG_ERROR(Service_Audio, "Failed to remove a render system, it was not found in the list!");
return false;
}
std::scoped_lock lk{mutex};
if (systems.remove(&system_) == 0) {
LOG_ERROR(Service_Audio, "Failed to remove a render system, it was not found in the list!");
return false;
}
if (systems.empty())
Stop();
return true;
+3 -9
View File
@@ -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-FileCopyrightText: Copyright 2022 yuzu Emulator Project
@@ -75,14 +75,8 @@ private:
std::list<System*> systems{};
/// Main worker thread for generating command lists
std::jthread thread;
/// Mutex for the systems
std::mutex mutex1{};
/// Mutex for adding/removing systems
std::mutex mutex2{};
/// Is the system manager thread active?
std::atomic<bool> active{};
/// Reference to the ADSP's AudioRenderer for communication
::AudioCore::ADSP::AudioRenderer::AudioRenderer& audio_renderer;
/// Mutex for the systems list
std::mutex mutex{};
};
} // namespace AudioCore::Renderer
-1
View File
@@ -110,7 +110,6 @@ add_library(
socket_types.h
sparse_large_vector.cpp
sparse_large_vector.h
spin_lock.h
stb.cpp
stb.h
steady_clock.cpp
+4 -3
View File
@@ -244,7 +244,8 @@ WallClock::WallClock(bool invariant_, u64 rdtsc_frequency_) noexcept
, ns_rdtsc_factor{invariant_ ? GetFixedPoint64Factor(NsRatio::den, rdtsc_frequency_) : 0}
, us_rdtsc_factor{invariant_ ? GetFixedPoint64Factor(UsRatio::den, rdtsc_frequency_) : 0}
, ms_rdtsc_factor{invariant_ ? GetFixedPoint64Factor(MsRatio::den, rdtsc_frequency_) : 0}
, rdtsc_ns_factor{invariant_ ? GetFixedPoint64Factor(rdtsc_frequency_, NsRatio::den) : 1}
, rdtsc_ns_integer{invariant_ ? rdtsc_frequency_ / NsRatio::den : 1}
, rdtsc_ns_factor{invariant_ ? GetFixedPoint64Factor(rdtsc_frequency_ % NsRatio::den, NsRatio::den) : 0}
, cntpct_rdtsc_factor{invariant_ ? GetFixedPoint64Factor(CNTFRQ, rdtsc_frequency_) : 0}
, gputick_rdtsc_factor{invariant_ ? GetFixedPoint64Factor(GPUTickFreq, rdtsc_frequency_) : 0}
, invariant{invariant_}
@@ -291,7 +292,7 @@ bool WallClock::IsNative() const {
}
u64 WallClock::NsToTicks(std::chrono::nanoseconds ns) const {
return invariant ? MultiplyHigh(ns.count(), rdtsc_ns_factor) : ns.count();
return ns.count() * rdtsc_ns_integer + MultiplyHigh(ns.count(), rdtsc_ns_factor);
}
#elif defined(HAS_NCE)
namespace {
@@ -416,7 +417,7 @@ u64 WallClock::NsToTicks(std::chrono::nanoseconds ns) const {
const WallClock g_wall_clock = [] {
#if defined(ARCHITECTURE_x86_64)
auto const& caps = Common::g_cpu_caps;
return WallClock(caps.invariant_tsc && caps.tsc_frequency >= std::nano::den, caps.tsc_frequency);
return WallClock(caps.invariant_tsc && caps.tsc_frequency > std::nano::den, caps.tsc_frequency);
#elif defined(HAS_NCE)
return WallClock(false, 1);
#else
+1
View File
@@ -96,6 +96,7 @@ public:
u64 ns_rdtsc_factor;
u64 us_rdtsc_factor;
u64 ms_rdtsc_factor;
u64 rdtsc_ns_integer;
u64 rdtsc_ns_factor;
u64 cntpct_rdtsc_factor;
u64 gputick_rdtsc_factor;
+18 -20
View File
@@ -17,35 +17,33 @@
namespace Common {
// glibc, mlibc, musl, and newlib all define their own variants of strerror_r
// We don't need to use the preprocessor, we can just select depending on return type
template<typename T> std::string HandleStrerrorR(T r, char *err_str);
template<> std::string HandleStrerrorR(char* r, char *) { return std::string{r}; }
template<> std::string HandleStrerrorR(const char* r, char *) { return std::string{r}; }
template<> std::string HandleStrerrorR(int r, char *err_str) {
return std::string{r != 0
? "(strerror_r failed to format error)"
: err_str};
}
std::string NativeErrorToString(int e) {
#ifdef _WIN32
LPSTR err_str;
DWORD res = FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_IGNORE_INSERTS,
nullptr, e, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
reinterpret_cast<LPSTR>(&err_str), 1, nullptr);
if (!res) {
return "(FormatMessageA failed to format error)";
LPSTR(&err_str), 1, nullptr);
if (res) {
std::string ret(err_str);
LocalFree(err_str);
return ret;
}
std::string ret(err_str);
LocalFree(err_str);
return ret;
return "(FormatMessageA failed to format error)";
#else
char err_str[255];
#if defined(__ANDROID__) || \
(defined(__GLIBC__) && (_GNU_SOURCE || (_POSIX_C_SOURCE < 200112L && _XOPEN_SOURCE < 600)))
// Thread safe (GNU-specific)
const char* str = strerror_r(e, err_str, sizeof(err_str));
return std::string(str);
#else
// Thread safe (XSI-compliant)
int second_err = strerror_r(e, err_str, sizeof(err_str));
if (second_err != 0) {
return "(strerror_r failed to format error)";
}
return std::string(err_str);
#endif // GLIBC etc.
return HandleStrerrorR(strerror_r(e, err_str, sizeof(err_str)), err_str);
#endif // _WIN32
}
+66 -13
View File
@@ -5,6 +5,7 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#include <algorithm>
#include <cctype>
#include <iostream>
#include <sstream>
#include "common/container/unordered_map.h"
@@ -480,28 +481,80 @@ std::string SanitizePath(std::string_view path_, DirectorySeparator directory_se
[type2](char c1, char c2) { return c1 == type2 && c2 == type2; }),
path.end());
const bool absolute = !path.empty() && path[0] == type2;
std::vector<std::string_view> parts;
std::string root;
std::string_view components{path};
bool drive_relative = false;
for (const auto part : SplitPathComponents(path))
{
if (part.empty() || part == ".")
continue;
if (part == ".." && !parts.empty() && parts.back() != "..")
parts.pop_back();
else if (part != "..") parts.push_back(part);
#ifdef _WIN32
const bool network = path.size() > 1 && path[0] == type2 && path[1] == type2;
const bool drive =
path.size() > 1 && std::isalpha(static_cast<unsigned char>(path[0])) && path[1] == ':';
if (network) {
root.assign(2, type2);
components.remove_prefix(2);
} else if (drive) {
root.assign(path.data(), 2);
components.remove_prefix(2);
if (!components.empty() && components.front() == type2) {
root += type2;
components.remove_prefix(1);
} else {
drive_relative = true;
}
}
#endif
if (root.empty() && !components.empty() && components.front() == type2) {
root += type2;
components.remove_prefix(1);
}
std::string resolved = absolute ? std::string(1, type2) : std::string{};
for (std::size_t i = 0; i < parts.size(); ++i)
{
if (i != 0)
const auto path_parts = SplitPathComponents(components);
std::size_t root_component_count = 0;
#ifdef _WIN32
if (network) {
root_component_count = 2;
const auto is_unc = [](std::string_view part) {
return part.size() == 3 && (part[0] == 'U' || part[0] == 'u') &&
(part[1] == 'N' || part[1] == 'n') && (part[2] == 'C' || part[2] == 'c');
};
if (path_parts.size() >= 2 && path_parts[0] == "?" && is_unc(path_parts[1])) {
root_component_count = 4;
}
}
#endif
std::vector<std::string_view> parts;
for (std::size_t i = 0; i < path_parts.size(); ++i) {
const auto part = path_parts[i];
if (i < root_component_count) {
parts.push_back(part);
} else if (part.empty() || part == ".") {
continue;
} else if (part == "..") {
if (parts.size() > root_component_count) {
parts.pop_back();
}
} else {
parts.push_back(part);
}
}
const std::size_t root_length = root.size();
std::string resolved = std::move(root);
for (std::size_t i = 0; i < parts.size(); ++i) {
if (i != 0 || (!resolved.empty() && resolved.back() != type2 && !drive_relative))
resolved += type2;
resolved.append(parts[i].data(), parts[i].size());
}
path = std::move(resolved);
if (!path.empty() && path.size() == root_length) {
return path;
}
return std::string(RemoveTrailingSlash(path));
}
+3 -2
View File
@@ -347,8 +347,9 @@ enum class DirectorySeparator {
// i.e. "C:\Users\Yuzu\Documents\save.bin" becomes {"C:", "Users", "Yuzu", "Documents", "save.bin" }
[[nodiscard]] std::vector<std::string> SplitPathComponentsCopy(std::string_view filename);
// Removes trailing slash, makes all '\\' into '/', and removes duplicate '/'. Makes '/' into '\\'
// depending if directory_separator is BackwardSlash or PlatformDefault and running on windows
// Normalizes directory separators, removes duplicate and non-root trailing separators, and resolves
// '.' and '..' components without traversing above the path root. Windows drive and UNC roots are
// preserved.
[[nodiscard]] std::string SanitizePath(
std::string_view path,
DirectorySeparator directory_separator = DirectorySeparator::ForwardSlash);
+3 -3
View File
@@ -96,15 +96,15 @@ struct PageTable {
}
/// Write page info atomically
constexpr void Store(bool marked, PageType type, u16 block, uintptr_t pointer) noexcept {
inline void Store(bool marked, PageType type, u16 block, uintptr_t pointer) noexcept {
data_raw.store(std::bit_cast<u64>(Data{marked, type, block, pointer}));
}
constexpr void MarkRasterizerCached() noexcept {
inline void MarkRasterizerCached() noexcept {
data_raw.fetch_or(0b111);
}
constexpr void MarkDebug(u64 ptr, u16 block) noexcept {
inline void MarkDebug(u64 ptr, u16 block) noexcept {
Store(true, PageType::DebugMemory, block, ptr);
}
+2 -1
View File
@@ -38,7 +38,8 @@ void FreeMemoryPages(void* base, std::size_t size) noexcept;
/// A large page-aligned buffer that has optimized memory usage for zero-writes.
template <typename T>
requires std::is_trivially_copyable_v<T>
// MSVC doesn't regard structs with atomics as trivially copyable
// requires std::is_trivially_copyable_v<T>
class SparseLargeVector final {
public:
constexpr SparseLargeVector() = default;
-50
View File
@@ -1,50 +0,0 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#ifdef _MSC_VER
#include <intrin.h>
#elif defined(ARCHITECTURE_x86_64)
#include <xmmintrin.h>
#endif
#include <atomic>
namespace Common {
/// @brief A lock similar to mutex that forces a thread to spin wait instead calling the
/// supervisor. Should be used on short sequences of code.
struct SpinLock {
SpinLock() noexcept = default;
SpinLock(const SpinLock&) noexcept = delete;
SpinLock& operator=(const SpinLock&) noexcept = delete;
SpinLock(SpinLock&&) noexcept = delete;
SpinLock& operator=(SpinLock&&) noexcept = delete;
inline void lock() noexcept {
while (lck.test_and_set(std::memory_order_acquire)) {
#if defined(ARCHITECTURE_x86_64)
_mm_pause();
#elif defined(ARCHITECTURE_arm64) && defined(_MSC_VER)
__yield();
#elif defined(ARCHITECTURE_arm64)
asm("yield");
#endif
}
}
inline void unlock() noexcept {
lck.clear(std::memory_order_release);
}
[[nodiscard]] inline bool try_lock() noexcept {
return !lck.test_and_set(std::memory_order_acquire);
}
std::atomic_flag lck = ATOMIC_FLAG_INIT;
};
} // namespace Common
+2 -2
View File
@@ -208,9 +208,9 @@ UUID UUID::MakeRandomRFC4122V4() {
return uuid;
}
UUID UUID::MakeRFC4122V5(std::span<u8, 20> sha1) {
UUID UUID::MakeRFC4122V5(std::span<u8, 16> sha1) {
UUID uuid{};
std::memcpy(&uuid.uuid, sha1.data(), sizeof(UUID));
std::memcpy(&uuid.uuid, sha1.data(), sha1.size());
uuid.uuid[8] = 0x80 | (uuid.uuid[8] & 0x3F);
uuid.uuid[6] = 0x50 | (uuid.uuid[6] & 0xF);
return uuid;
+1 -1
View File
@@ -104,7 +104,7 @@ struct UUID {
/// @returns A random UUID that is RFC 4122 Version 4 compliant.
[[nodiscard]] static UUID MakeRandomRFC4122V4();
[[nodiscard]] static UUID MakeRFC4122V5(std::span<u8, 20> sha1);
[[nodiscard]] static UUID MakeRFC4122V5(std::span<u8, 16> sha1);
friend constexpr bool operator==(const UUID& lhs, const UUID& rhs) = default;
};
+2
View File
@@ -796,6 +796,8 @@ add_library(core STATIC
hle/service/ns/application_manager_interface.h
hle/service/ns/application_version_interface.cpp
hle/service/ns/application_version_interface.h
hle/service/ns/async_result.cpp
hle/service/ns/async_result.h
hle/service/ns/content_management_interface.cpp
hle/service/ns/content_management_interface.h
hle/service/ns/develop_interface.cpp
+20 -36
View File
@@ -22,21 +22,15 @@ DynarmicCallbacks32::DynarmicCallbacks32(ArmDynarmic32& parent, Kernel::KProcess
, m_check_memory_access{m_debugger_enabled || !Settings::values.cpuopt_ignore_memory_aborts.GetValue()}
{}
u8 DynarmicCallbacks32::MemoryRead8(u32 vaddr) {
CheckMemoryAccess(vaddr, 1, Kernel::DebugWatchpointType::Read);
return m_memory.Read8(vaddr);
}
u16 DynarmicCallbacks32::MemoryRead16(u32 vaddr) {
CheckMemoryAccess(vaddr, 2, Kernel::DebugWatchpointType::Read);
return m_memory.Read16(vaddr);
}
u32 DynarmicCallbacks32::MemoryRead32(u32 vaddr) {
CheckMemoryAccess(vaddr, 4, Kernel::DebugWatchpointType::Read);
return m_memory.Read32(vaddr);
}
u64 DynarmicCallbacks32::MemoryRead64(u32 vaddr) {
CheckMemoryAccess(vaddr, 8, Kernel::DebugWatchpointType::Read);
return m_memory.Read64(vaddr);
u64 DynarmicCallbacks32::MemoryRead(u32 vaddr, size_t size) {
CheckMemoryAccess(vaddr, size, Kernel::DebugWatchpointType::Read);
switch (size) {
case sizeof(u64): return m_memory.Read64(vaddr);
case sizeof(u32): return m_memory.Read32(vaddr);
case sizeof(u16): return m_memory.Read16(vaddr);
case sizeof(u8): return m_memory.Read8(vaddr);
default: UNREACHABLE();
}
}
std::optional<u32> DynarmicCallbacks32::MemoryReadCode(u32 vaddr) {
@@ -50,27 +44,17 @@ std::optional<u32> DynarmicCallbacks32::MemoryReadCode(u32 vaddr) {
return cached_code_page.inst[(vaddr & Core::Memory::YUZU_PAGEMASK) / sizeof(u32)];
}
void DynarmicCallbacks32::MemoryWrite8(u32 vaddr, u8 value) {
if (CheckMemoryAccess(vaddr, 1, Kernel::DebugWatchpointType::Write)) {
m_memory.Write8(vaddr, value);
void DynarmicCallbacks32::MemoryWrite(Dynarmic::A32::VAddr vaddr, u64 value, size_t size) {
if (CheckMemoryAccess(vaddr, size, Kernel::DebugWatchpointType::Write)) {
switch (size) {
case sizeof(u64): return m_memory.Write64(vaddr, value);
case sizeof(u32): return m_memory.Write32(vaddr, u32(value));
case sizeof(u16): return m_memory.Write16(vaddr, u16(value));
case sizeof(u8): return m_memory.Write8(vaddr, u8(value));
default: UNREACHABLE();
}
}
}
void DynarmicCallbacks32::MemoryWrite16(u32 vaddr, u16 value) {
if (CheckMemoryAccess(vaddr, 2, Kernel::DebugWatchpointType::Write)) {
m_memory.Write16(vaddr, value);
}
}
void DynarmicCallbacks32::MemoryWrite32(u32 vaddr, u32 value) {
if (CheckMemoryAccess(vaddr, 4, Kernel::DebugWatchpointType::Write)) {
m_memory.Write32(vaddr, value);
}
}
void DynarmicCallbacks32::MemoryWrite64(u32 vaddr, u64 value) {
if (CheckMemoryAccess(vaddr, 8, Kernel::DebugWatchpointType::Write)) {
m_memory.Write64(vaddr, value);
}
}
bool DynarmicCallbacks32::MemoryWriteExclusive8(u32 vaddr, u8 value, u8 expected) {
return CheckMemoryAccess(vaddr, 1, Kernel::DebugWatchpointType::Write) &&
m_memory.WriteExclusive8(vaddr, value, expected);
@@ -177,7 +161,7 @@ void ArmDynarmic32::MakeJit(Common::PageTable* page_table) {
config.page_table = reinterpret_cast<std::array<std::uint8_t*, NumPageTableEntries>*>(
const_cast<Common::PageTable::PageEntryData*>(page_table->entries.data()));
config.page_table_pointer_mask = Common::PageTable::ATTRIBUTE_MASK;
config.page_table_marked_bit = 0;
config.page_table_marked_bit = uint8_t(0);
config.absolute_offset_page_table = true;
config.detect_misaligned_access_via_page_table = 16 | 32 | 64 | 128;
config.only_detect_misalignment_via_page_table_on_page_boundary = true;
@@ -193,7 +177,7 @@ void ArmDynarmic32::MakeJit(Common::PageTable* page_table) {
Kernel::Board::Nintendo::Nx::KSystemControl::Init::GetIntendedMemorySize()) < (1ULL << 39)) {
// Systems like FreeBSD allocate memory really low by default, and since we pack our page table entries,
// we have to manually sign extend when our actual pointer is negative.
config.page_table_sign_extension = Common::PageTable::SIGN_BIT;
config.page_table_sign_extension = std::uint8_t(Common::PageTable::SIGN_BIT);
}
}
+2 -8
View File
@@ -30,18 +30,12 @@ class System;
class DynarmicCallbacks32 : public Dynarmic::A32::UserCallbacks {
public:
explicit DynarmicCallbacks32(ArmDynarmic32& parent, Kernel::KProcess* process);
u8 MemoryRead8(u32 vaddr) override;
u16 MemoryRead16(u32 vaddr) override;
u32 MemoryRead32(u32 vaddr) override;
u64 MemoryRead64(u32 vaddr) override;
u64 MemoryRead(u32 vaddr, size_t size) override;
std::optional<u32> MemoryReadCode(u32 vaddr) override;
void InstructionSynchronizationBarrierRaised() override {
last_code_addr = u64(-1); //reset back, force refetch
}
void MemoryWrite8(u32 vaddr, u8 value) override;
void MemoryWrite16(u32 vaddr, u16 value) override;
void MemoryWrite32(u32 vaddr, u32 value) override;
void MemoryWrite64(u32 vaddr, u64 value) override;
void MemoryWrite(Dynarmic::A32::VAddr vaddr, u64 value, size_t size) override;
bool MemoryWriteExclusive8(u32 vaddr, u8 value, u8 expected) override;
bool MemoryWriteExclusive16(u32 vaddr, u16 value, u16 expected) override;
bool MemoryWriteExclusive32(u32 vaddr, u32 value, u32 expected) override;
+21 -35
View File
@@ -10,6 +10,7 @@
#include "core/arm/dynarmic/dynarmic_exclusive_monitor.h"
#include "core/core_timing.h"
#include "core/hle/kernel/k_process.h"
#include "dynarmic/interface/A64/config.h"
namespace Core {
@@ -21,21 +22,15 @@ DynarmicCallbacks64::DynarmicCallbacks64(ArmDynarmic64& parent, Kernel::KProcess
, m_check_memory_access{m_debugger_enabled || !Settings::values.cpuopt_ignore_memory_aborts.GetValue()}
{}
u8 DynarmicCallbacks64::MemoryRead8(u64 vaddr) {
CheckMemoryAccess(vaddr, 1, Kernel::DebugWatchpointType::Read);
return m_memory.Read8(vaddr);
}
u16 DynarmicCallbacks64::MemoryRead16(u64 vaddr) {
CheckMemoryAccess(vaddr, 2, Kernel::DebugWatchpointType::Read);
return m_memory.Read16(vaddr);
}
u32 DynarmicCallbacks64::MemoryRead32(u64 vaddr) {
CheckMemoryAccess(vaddr, 4, Kernel::DebugWatchpointType::Read);
return m_memory.Read32(vaddr);
}
u64 DynarmicCallbacks64::MemoryRead64(u64 vaddr) {
CheckMemoryAccess(vaddr, 8, Kernel::DebugWatchpointType::Read);
return m_memory.Read64(vaddr);
u64 DynarmicCallbacks64::MemoryRead(u64 vaddr, size_t size) {
CheckMemoryAccess(vaddr, size, Kernel::DebugWatchpointType::Read);
switch (size) {
case sizeof(u64): return m_memory.Read64(vaddr);
case sizeof(u32): return m_memory.Read32(vaddr);
case sizeof(u16): return m_memory.Read16(vaddr);
case sizeof(u8): return m_memory.Read8(vaddr);
default: UNREACHABLE();
}
}
Dynarmic::A64::Vector DynarmicCallbacks64::MemoryRead128(u64 vaddr) {
CheckMemoryAccess(vaddr, 16, Kernel::DebugWatchpointType::Read);
@@ -53,24 +48,15 @@ std::optional<u32> DynarmicCallbacks64::MemoryReadCode(u64 vaddr) {
return cached_code_page.inst[(vaddr & Core::Memory::YUZU_PAGEMASK) / sizeof(u32)];
}
void DynarmicCallbacks64::MemoryWrite8(u64 vaddr, u8 value) {
if (CheckMemoryAccess(vaddr, 1, Kernel::DebugWatchpointType::Write)) {
m_memory.Write8(vaddr, value);
}
}
void DynarmicCallbacks64::MemoryWrite16(u64 vaddr, u16 value) {
if (CheckMemoryAccess(vaddr, 2, Kernel::DebugWatchpointType::Write)) {
m_memory.Write16(vaddr, value);
}
}
void DynarmicCallbacks64::MemoryWrite32(u64 vaddr, u32 value) {
if (CheckMemoryAccess(vaddr, 4, Kernel::DebugWatchpointType::Write)) {
m_memory.Write32(vaddr, value);
}
}
void DynarmicCallbacks64::MemoryWrite64(u64 vaddr, u64 value) {
if (CheckMemoryAccess(vaddr, 8, Kernel::DebugWatchpointType::Write)) {
m_memory.Write64(vaddr, value);
void DynarmicCallbacks64::MemoryWrite(Dynarmic::A64::VAddr vaddr, u64 value, std::size_t size) {
if (CheckMemoryAccess(vaddr, size, Kernel::DebugWatchpointType::Write)) {
switch (size) {
case sizeof(u64): return m_memory.Write64(vaddr, u64(value));
case sizeof(u32): return m_memory.Write32(vaddr, u32(value));
case sizeof(u16): return m_memory.Write16(vaddr, u16(value));
case sizeof(u8): return m_memory.Write8(vaddr, u8(value));
default: UNREACHABLE();
}
}
}
void DynarmicCallbacks64::MemoryWrite128(u64 vaddr, Dynarmic::A64::Vector value) {
@@ -216,7 +202,7 @@ void ArmDynarmic64::MakeJit(Common::PageTable* page_table, std::size_t address_s
const_cast<Common::PageTable::PageEntryData*>(page_table->entries.data()));
config.page_table_address_space_bits = std::uint32_t(address_space_bits);
config.page_table_pointer_mask = Common::PageTable::ATTRIBUTE_MASK;
config.page_table_marked_bit = 0;
config.page_table_marked_bit = uint8_t(0);
config.silently_mirror_page_table = false;
config.absolute_offset_page_table = true;
config.detect_misaligned_access_via_page_table = 16 | 32 | 64 | 128;
@@ -235,7 +221,7 @@ void ArmDynarmic64::MakeJit(Common::PageTable* page_table, std::size_t address_s
Kernel::Board::Nintendo::Nx::KSystemControl::Init::GetIntendedMemorySize()) < (1ULL << 39)) {
// Systems like FreeBSD allocate memory really low by default, and since we pack our page table entries,
// we have to manually sign extend when our actual pointer is negative.
config.page_table_sign_extension = Common::PageTable::SIGN_BIT;
config.page_table_sign_extension = std::uint8_t(Common::PageTable::SIGN_BIT);
}
}
+3 -8
View File
@@ -16,6 +16,7 @@
#include "common/hash.h"
#include "core/arm/arm_interface.h"
#include "core/arm/dynarmic/dynarmic_exclusive_monitor.h"
#include "dynarmic/interface/A64/config.h"
namespace Core::Memory {
class Memory;
@@ -36,19 +37,13 @@ class DynarmicCallbacks64 : public Dynarmic::A64::UserCallbacks {
public:
explicit DynarmicCallbacks64(ArmDynarmic64& parent, Kernel::KProcess* process);
u8 MemoryRead8(u64 vaddr) override;
u16 MemoryRead16(u64 vaddr) override;
u32 MemoryRead32(u64 vaddr) override;
u64 MemoryRead64(u64 vaddr) override;
u64 MemoryRead(u64 vaddr, size_t size) override;
Dynarmic::A64::Vector MemoryRead128(u64 vaddr) override;
std::optional<u32> MemoryReadCode(u64 vaddr) override;
void InstructionSynchronizationBarrierRaised() override {
last_code_addr = u64(-1); //reset back, force refetch
}
void MemoryWrite8(u64 vaddr, u8 value) override;
void MemoryWrite16(u64 vaddr, u16 value) override;
void MemoryWrite32(u64 vaddr, u32 value) override;
void MemoryWrite64(u64 vaddr, u64 value) override;
void MemoryWrite(Dynarmic::A64::VAddr vaddr, u64 value, std::size_t size) override;
void MemoryWrite128(u64 vaddr, Dynarmic::A64::Vector value) override;
bool MemoryWriteExclusive8(u64 vaddr, std::uint8_t value, std::uint8_t expected) override;
bool MemoryWriteExclusive16(u64 vaddr, std::uint16_t value, std::uint16_t expected) override;
+25 -22
View File
@@ -35,19 +35,22 @@ static IPSFileType IdentifyMagic(std::span<const u8> magic) {
}
static bool IsEOF(IPSFileType type, std::span<const u8> magic) {
return (type == IPSFileType::IPS && magic.size() > 3 && std::memcmp(magic.data(), "EOF", 3) == 0)
|| (type == IPSFileType::IPS32 && magic.size() > 4 && std::memcmp(magic.data(), "EEOF", 4) == 0);
return (type == IPSFileType::IPS && magic.size() >= 3 && std::memcmp(magic.data(), "EOF", 3) == 0)
|| (type == IPSFileType::IPS32 && magic.size() >= 4 && std::memcmp(magic.data(), "EEOF", 4) == 0);
}
VirtualFile PatchIPS(const VirtualFile& in, const VirtualFile& ips) {
if (in == nullptr || ips == nullptr)
return nullptr;
auto in_data = in->ReadAllBytes();
auto const type = IdentifyMagic(in_data);
const auto type = IdentifyMagic(ips->ReadBytes(0x5));
if (type == IPSFileType::Error)
return nullptr;
auto in_data = in->ReadAllBytes();
if (in_data.size() == 0)
return nullptr;
std::vector<u8> temp(type == IPSFileType::IPS ? 3 : 4);
u64 offset = 5; // After header
while (ips->Read(temp.data(), temp.size(), offset) == temp.size()) {
@@ -100,8 +103,7 @@ VirtualFile PatchIPS(const VirtualFile& in, const VirtualFile& ips) {
struct IPSwitchRecord {
std::array<uint8_t, 256 - sizeof(size_t)> data;
size_t count;
std::vector<uint8_t> data;
};
struct IPSwitchCompiler::IPSwitchPatch {
::Common::unordered_map<u32, IPSwitchRecord> records;
@@ -122,22 +124,23 @@ static IPSwitchRecord EscapeStringSequences(std::string_view sv) {
IPSwitchRecord r{};
for (auto it = sv.cbegin(); it < sv.cend(); ) {
if (*it == '\\' && it + 1 < sv.cend()) {
switch (it[1]) {
case 'a': r.data[r.count] = '\a'; break;
case 'b': r.data[r.count] = '\b'; break;
case 'e': r.data[r.count] = '\e'; break;
case 'f': r.data[r.count] = '\f'; break;
case 'n': r.data[r.count] = '\n'; break;
case 'r': r.data[r.count] = '\r'; break;
case 't': r.data[r.count] = '\t'; break;
case 'v': r.data[r.count] = '\v'; break;
case '?': r.data[r.count] = '\?'; break;
default: r.data[r.count] = it[1]; break;
}
++r.count;
r.data.push_back([it]() {
switch (it[1]) {
case 'a': return '\a';
case 'b': return '\b';
case 'e': return '\e';
case 'f': return '\f';
case 'n': return '\n';
case 'r': return '\r';
case 't': return '\t';
case 'v': return '\v';
case '?': return '\?';
default: return it[1];
}
}());
it += 2;
} else {
++r.count;
r.data.push_back(*it);
++it;
}
}
@@ -223,8 +226,8 @@ void IPSwitchCompiler::Parse(std::span<u8 const> bytes) {
if (start <= line.cend() && end <= line.cend()) {
// Actually IPS wants ordering from {lsb, ..., msb} -- so LE and BE are inverted, fun!
auto const hs = Common::HexStringToVector({start, end}, is_little_endian);
r.data.resize(hs.size());
std::memcpy(r.data.data(), hs.data(), hs.size());
r.count = hs.size();
LOG_INFO(Loader, "[H] value @ {:#08X}", offset);
patches.back().records.insert_or_assign(u32(offset), std::move(r));
} else {
@@ -293,7 +296,7 @@ VirtualFile IPSwitchCompiler::Apply(const VirtualFile& in) const {
if (patch.enabled) {
for (const auto& record : patch.records) {
if (record.first < in_data.size()) {
auto replace_size = record.second.count;
auto replace_size = record.second.data.size();
if (record.first + replace_size > in_data.size())
replace_size = in_data.size() - record.first;
std::memcpy(in_data.data() + record.first, record.second.data.data(), replace_size);
+50 -18
View File
@@ -9,6 +9,7 @@
#include <cstddef>
#include <cstring>
#include "common/assert.h"
#include "common/hex_util.h"
#include "common/logging.h"
#include "common/settings.h"
@@ -156,12 +157,15 @@ std::string GetUpdateVersionStringFromSlot(const ContentProvider* provider, u64
NACP nacp{nacp_file};
return nacp.GetVersionString();
}
YUZU_NO_INLINE std::optional<u64> GetParentApplicationId(const ContentProvider* provider, u64 title_id) {
return provider == nullptr ? std::nullopt : provider->GetParentApplicationId(title_id);
}
} // Anonymous namespace
PatchManager::PatchManager(u64 title_id_,
const Service::FileSystem::FileSystemController& fs_controller_,
const ContentProvider& content_provider_)
: title_id{title_id_}, fs_controller{fs_controller_}, content_provider{content_provider_} {}
: title_id{title_id_}, parent_title_id{GetParentApplicationId(std::addressof(content_provider_), title_id_)}, fs_controller{fs_controller_}, content_provider{content_provider_} {}
PatchManager::~PatchManager() = default;
@@ -169,13 +173,35 @@ u64 PatchManager::GetTitleID() const {
return title_id;
}
VirtualDir PatchManager::GetModificationLoadRoot(bool sdmc) const {
const auto get_root = [&](u64 id) {
return sdmc ? fs_controller.GetSDMCModificationLoadRoot(id) : fs_controller.GetModificationLoadRoot(id);
};
auto root = get_root(title_id);
if (!parent_title_id) {
return root;
}
std::vector<VirtualDir> roots{std::move(root), get_root(*parent_title_id)};
std::erase(roots, nullptr);
return LayeredVfsDirectory::MakeLayeredDirectory(std::move(roots));
}
std::vector<std::string> PatchManager::GetDisabledAddons() const {
auto disabled = Settings::values.disabled_addons[title_id];
if (parent_title_id) {
const auto& shared = Settings::values.disabled_addons[*parent_title_id];
disabled.insert(disabled.end(), shared.begin(), shared.end());
}
return disabled;
}
VirtualDir PatchManager::PatchExeFS(VirtualDir exefs) const {
LOG_INFO(Loader, "Patching ExeFS for title_id={:016X}", title_id);
if (exefs == nullptr)
return exefs;
const auto& disabled = Settings::values.disabled_addons[title_id];
const auto disabled = GetDisabledAddons();
bool update_disabled = true;
std::optional<u32> enabled_version;
@@ -303,8 +329,8 @@ VirtualDir PatchManager::PatchExeFS(VirtualDir exefs) const {
}
// LayeredExeFS
const auto load_dir = fs_controller.GetModificationLoadRoot(title_id);
const auto sdmc_load_dir = fs_controller.GetSDMCModificationLoadRoot(title_id);
const auto load_dir = GetModificationLoadRoot();
const auto sdmc_load_dir = GetModificationLoadRoot(true);
std::vector<VirtualDir> patch_dirs = {sdmc_load_dir};
if (load_dir != nullptr) {
@@ -346,7 +372,7 @@ VirtualDir PatchManager::PatchExeFS(VirtualDir exefs) const {
}
std::vector<VirtualFile> PatchManager::CollectPatches(const std::vector<VirtualDir>& patch_dirs, const std::string& build_id) const {
const auto& disabled = Settings::values.disabled_addons[title_id];
const auto disabled = GetDisabledAddons();
const auto nso_build_id = fmt::format("{:0<64}", build_id);
std::vector<VirtualFile> out;
@@ -405,7 +431,7 @@ std::vector<u8> PatchManager::PatchNSO(const std::vector<u8>& nso, const std::st
LOG_INFO(Loader, "Patching NSO for name={}, build_id={}", name, build_id);
const auto load_dir = fs_controller.GetModificationLoadRoot(title_id);
const auto load_dir = GetModificationLoadRoot();
if (load_dir == nullptr) {
LOG_ERROR(Loader, "Cannot load mods for invalid title_id={:016X}", title_id);
return nso;
@@ -448,7 +474,7 @@ bool PatchManager::HasNSOPatch(const BuildID& build_id_, std::string_view name)
LOG_INFO(Loader, "Querying NSO patch existence for build_id={}, name={}", build_id, name);
const auto load_dir = fs_controller.GetModificationLoadRoot(title_id);
const auto load_dir = GetModificationLoadRoot();
if (load_dir == nullptr) {
LOG_ERROR(Loader, "Cannot load mods for invalid title_id={:016X}", title_id);
return false;
@@ -462,13 +488,13 @@ bool PatchManager::HasNSOPatch(const BuildID& build_id_, std::string_view name)
}
std::vector<Core::Memory::CheatEntry> PatchManager::CreateCheatList(const BuildID& build_id_) const {
const auto load_dir = fs_controller.GetModificationLoadRoot(title_id);
const auto load_dir = GetModificationLoadRoot();
if (load_dir == nullptr) {
LOG_ERROR(Loader, "Cannot load mods for invalid title_id={:016X}", title_id);
return {};
}
const auto& disabled = Settings::values.disabled_addons[title_id];
const auto disabled = GetDisabledAddons();
auto patch_dirs = load_dir->GetSubdirectories();
std::sort(patch_dirs.begin(), patch_dirs.end(), [](auto const& l, auto const& r) { return l->GetName() < r->GetName(); });
@@ -502,17 +528,16 @@ std::vector<Core::Memory::CheatEntry> PatchManager::CreateCheatList(const BuildI
return out;
}
static void ApplyLayeredFS(VirtualFile& romfs, u64 title_id, ContentRecordType type,
const Service::FileSystem::FileSystemController& fs_controller) {
const auto load_dir = fs_controller.GetModificationLoadRoot(title_id);
const auto sdmc_load_dir = fs_controller.GetSDMCModificationLoadRoot(title_id);
void PatchManager::ApplyLayeredFS(VirtualFile& romfs, ContentRecordType type) const {
const auto load_dir = GetModificationLoadRoot();
const auto sdmc_load_dir = GetModificationLoadRoot(true);
if ((type != ContentRecordType::Program && type != ContentRecordType::Data &&
type != ContentRecordType::HtmlDocument) ||
(load_dir == nullptr && sdmc_load_dir == nullptr)) {
return;
}
const auto& disabled = Settings::values.disabled_addons[title_id];
const auto disabled = GetDisabledAddons();
std::vector<VirtualDir> patch_dirs = load_dir->GetSubdirectories();
if (std::find(disabled.cbegin(), disabled.cend(), "SDMC") == disabled.cend()) {
patch_dirs.push_back(sdmc_load_dir);
@@ -591,7 +616,7 @@ VirtualFile PatchManager::PatchRomFS(const NCA* base_nca, VirtualFile base_romfs
// Game Updates
const auto update_tid = GetUpdateTitleID(title_id);
const auto& disabled = Settings::values.disabled_addons[title_id];
const auto disabled = GetDisabledAddons();
bool update_disabled = true;
std::optional<u32> enabled_version;
@@ -698,7 +723,7 @@ VirtualFile PatchManager::PatchRomFS(const NCA* base_nca, VirtualFile base_romfs
// LayeredFS
if (apply_layeredfs) {
ApplyLayeredFS(romfs, title_id, type, fs_controller);
ApplyLayeredFS(romfs, type);
}
return romfs;
@@ -1072,15 +1097,22 @@ std::vector<Patch> PatchManager::GetPatches(VirtualFile update_raw) const {
std::optional<u32> PatchManager::GetGameVersion() const {
const auto update_tid = GetUpdateTitleID(title_id);
if (content_provider.HasEntry(update_tid, ContentRecordType::Program)) {
return content_provider.GetEntryVersion(update_tid);
const auto version = content_provider.GetEntryVersion(update_tid);
return version || !parent_title_id ? version : content_provider.GetEntryVersion(GetUpdateTitleID(*parent_title_id));
}
return content_provider.GetEntryVersion(title_id);
const auto version = content_provider.GetEntryVersion(title_id);
return version || !parent_title_id ? version : content_provider.GetEntryVersion(*parent_title_id);
}
PatchManager::Metadata PatchManager::GetControlMetadata() const {
const auto base_control_nca = content_provider.GetEntry(title_id, ContentRecordType::Control);
if (base_control_nca == nullptr) {
if (parent_title_id) {
const auto control_id = content_provider.HasEntry(*parent_title_id, ContentRecordType::Control) ? *parent_title_id : GetUpdateTitleID(*parent_title_id);
const PatchManager parent{control_id, fs_controller, content_provider};
return parent.GetControlMetadata();
}
return {};
}
+4
View File
@@ -109,10 +109,14 @@ public:
[[nodiscard]] static PatchManager::Metadata GetMetadataFromBaseOrUpdate(Core::System& system, u64 application_id) noexcept;
private:
[[nodiscard]] VirtualDir GetModificationLoadRoot(bool sdmc = false) const;
[[nodiscard]] std::vector<std::string> GetDisabledAddons() const;
void ApplyLayeredFS(VirtualFile& romfs, ContentRecordType type) const;
[[nodiscard]] std::vector<VirtualFile> CollectPatches(const std::vector<VirtualDir>& patch_dirs,
const std::string& build_id) const;
u64 title_id;
std::optional<u64> parent_title_id;
const Service::FileSystem::FileSystemController& fs_controller;
const ContentProvider& content_provider;
};
+17
View File
@@ -5,6 +5,7 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#include <algorithm>
#include <limits>
#include <random>
#include <regex>
#include <openssl/evp.h>
@@ -348,6 +349,22 @@ std::vector<ContentProviderEntry> ContentProvider::ListEntries() const {
return ListEntriesFilter(std::nullopt, std::nullopt, std::nullopt);
}
std::optional<u64> ContentProvider::GetParentApplicationId(u64 program_id) const {
const auto application_id = GetBaseTitleID(program_id);
const auto program_index = program_id - application_id;
if (program_index == 0 || program_index > std::numeric_limits<u8>::max()) {
return std::nullopt;
}
if (!ListEntriesFilter(TitleType::Application, ContentRecordType::Meta, program_id).empty()) {
return std::nullopt;
}
if ((!ListEntriesFilter(TitleType::Application, ContentRecordType::Meta, application_id).empty() && HasEntry(program_id, ContentRecordType::Program))
|| (!ListEntriesFilter(TitleType::Update, ContentRecordType::Meta, GetUpdateTitleID(application_id)).empty() && HasEntry(GetUpdateTitleID(program_id), ContentRecordType::Program))) {
return application_id;
}
return std::nullopt;
}
PlaceholderCache::PlaceholderCache(VirtualDir dir_) : dir(std::move(dir_)) {}
bool PlaceholderCache::Create(const NcaID& id, u64 size) const {
+2
View File
@@ -100,6 +100,8 @@ public:
std::optional<TitleType> title_type = {}, std::optional<ContentRecordType> record_type = {},
std::optional<u64> title_id = {}) const = 0;
[[nodiscard]] std::optional<u64> GetParentApplicationId(u64 program_id) const;
protected:
// A single instance of KeyManager to be used by GetEntry()
Core::Crypto::KeyManager& keys = Core::Crypto::KeyManager::Instance();
+3
View File
@@ -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-License-Identifier: GPL-2.0-or-later
+25 -8
View File
@@ -12,6 +12,7 @@
#include "hid_core/frontend/emulated_controller.h"
#include "hid_core/hid_core.h"
#include "hid_core/hid_types.h"
#include <array>
namespace Core::Frontend {
@@ -29,23 +30,39 @@ void DefaultControllerApplet::ReconfigureControllers(ReconfigureCallback callbac
const std::size_t min_supported_players =
parameters.enable_single_mode ? 1 : parameters.min_players;
using Core::HID::NpadStyleIndex;
const std::size_t max_supported_players = parameters.enable_single_mode ? 1 : parameters.max_players;
std::size_t num_selected_players = 0;
std::array<bool, HID::HIDCore::available_controllers> keep_connected{};
// Disconnect Handheld first.
// reserve existing AND valid players before filling slots. include Handheld, but not other
for (std::size_t index = 0; index < hid_core.available_controllers - 1; ++index) {
const auto* controller = hid_core.GetEmulatedControllerByIndex(index);
if (!parameters.keep_controllers_connected || !controller->IsConnected() || num_selected_players >= max_supported_players) continue;
const auto style = controller->GetNpadStyleIndex();
keep_connected[index] =
(style == NpadStyleIndex::Fullkey && parameters.allow_pro_controller) ||
(style == NpadStyleIndex::JoyconDual && parameters.allow_dual_joycons) ||
(style == NpadStyleIndex::JoyconLeft && parameters.allow_left_joycon) ||
(style == NpadStyleIndex::JoyconRight && parameters.allow_right_joycon) ||
(style == NpadStyleIndex::Handheld && parameters.enable_single_mode && parameters.allow_handheld && !Settings::IsDockedMode()) ||
(style == NpadStyleIndex::GameCube && parameters.allow_gamecube_controller);
num_selected_players += keep_connected[index];
}
auto* handheld = hid_core.GetEmulatedController(Core::HID::NpadIdType::Handheld);
handheld->Disconnect();
if (!keep_connected[hid_core.available_controllers - 2]) handheld->Disconnect();
// Deduce the best configuration based on the input parameters.
for (std::size_t index = 0; index < hid_core.available_controllers - 2; ++index) {
auto* controller = hid_core.GetEmulatedControllerByIndex(index);
// First, disconnect all controllers regardless of the value of keep_controllers_connected.
// This makes it easy to connect the desired controllers.
if (keep_connected[index]) continue;
controller->Disconnect();
// Only connect the minimum number of required players.
if (index >= min_supported_players) {
continue;
}
// only add players still needed to reach the minimum
if (num_selected_players >= min_supported_players) continue;
++num_selected_players;
// Connect controllers based on the following priority list from highest to lowest priority:
// Pro Controller -> Dual Joycons -> Left Joycon/Right Joycon -> Handheld
+8
View File
@@ -465,6 +465,10 @@ void KScheduler::ScheduleImplFiber(KernelCore& kernel) {
// Check if we need scheduling. If we do, then we can't complete the switch and should
// retry.
if (m_state.needs_scheduling.load(std::memory_order_seq_cst)) {
// Some libc++ lazily init mutex
[[maybe_unused]] auto const can_lock = highest_priority_thread->m_context_guard.try_lock();
DEBUG_ASSERT(!can_lock);
// Our switch failed.
// We should unlock the thread context, and then retry.
highest_priority_thread->m_context_guard.unlock();
@@ -496,6 +500,10 @@ void KScheduler::Unload(KernelCore& kernel, KThread* thread) {
// Check if the thread is terminated by checking the DPC flags.
if ((thread->GetStackParameters().dpc_flags & static_cast<u32>(DpcFlag::Terminated)) == 0) {
// Some libc++ lazily init mutex
[[maybe_unused]] auto const can_lock = thread->m_context_guard.try_lock();
DEBUG_ASSERT(!can_lock);
// The thread isn't terminated, so we want to unlock it.
thread->m_context_guard.unlock();
}
+3 -4
View File
@@ -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-FileCopyrightText: Copyright 2020 yuzu Emulator Project
@@ -12,7 +12,6 @@
#include "common/atomic_ops.h"
#include "common/common_funcs.h"
#include "common/common_types.h"
#include "common/spin_lock.h"
namespace Kernel {
@@ -30,7 +29,7 @@ public:
};
public:
constexpr KSlabHeapImpl() = default;
KSlabHeapImpl() = default;
void Initialize() {
ASSERT(m_head == nullptr);
@@ -68,7 +67,7 @@ public:
private:
std::atomic<Node*> m_head{};
Common::SpinLock m_lock;
std::mutex m_lock;
};
} // namespace impl
+1 -2
View File
@@ -19,7 +19,6 @@
#include "common/intrusive_red_black_tree.h"
#include "common/scratch_buffer.h"
#include "common/spin_lock.h"
#include "core/arm/arm_interface.h"
#include "core/hle/kernel/k_affinity_mask.h"
#include "core/hle/kernel/k_light_lock.h"
@@ -920,7 +919,7 @@ private:
bool m_resource_limit_release_hint{};
bool m_is_kernel_address_key{};
StackParameters m_stack_parameters{};
Common::SpinLock m_context_guard{};
std::mutex m_context_guard{};
// For emulation
std::shared_ptr<Common::Fiber> m_host_context{};
@@ -23,6 +23,12 @@ constexpr std::size_t profile_username_size{32};
using ProfileUsername = std::array<u8, profile_username_size>;
using UserIDArray = std::array<Common::UUID, MAX_USERS>;
// This is nn::account::Uid
struct Uid {
std::array<u8, 0x10> unk0;
};
static_assert(sizeof(Uid) == 0x10);
/// Contains extra data related to a user.
/// TODO: RE this structure
struct UserData {
+11 -4
View File
@@ -29,6 +29,7 @@
#include "core/hle/service/am/frontend/applet_web_browser.h"
#include "core/hle/service/am/frontend/applets.h"
#include "core/hle/service/am/service/storage.h"
#include "core/hle/service/am/window_system.h"
#include "core/hle/service/sm/sm.h"
namespace Service::AM::Frontend {
@@ -72,10 +73,16 @@ void FrontendApplet::PushInteractiveOutData(std::shared_ptr<IStorage> storage) {
void FrontendApplet::Exit() {
auto applet_ = applet.lock();
std::scoped_lock lk{applet_->lock};
applet_->is_completed = true;
applet_->state_changed_event.Signal(system.Kernel());
{
std::scoped_lock lk{applet_->lock};
applet_->is_completed = true;
applet_->state_changed_event.Signal(system.Kernel());
}
if (auto caller_applet = applet_->caller_applet.lock()) {
std::scoped_lock lk{caller_applet->lock};
std::erase(caller_applet->child_applets, applet_);
}
if (auto* window_system = system.GetAppletManager().GetWindowSystem()) window_system->RequestUpdate();
}
FrontendAppletSet::FrontendAppletSet() = default;
@@ -92,6 +92,13 @@ public:
}
}
void RequestFocusStateChangedNotification(Kernel::KernelCore& kernel) {
if (m_focus_state_changed_notification_enabled) {
m_has_focus_state_changed = true;
this->SignalSystemEventIfNeeded(kernel);
}
}
void OnOperationAndPerformanceModeChanged(Kernel::KernelCore& kernel);
public:
@@ -347,21 +347,22 @@ Result IApplicationFunctions::NotifyRunning(Out<bool> out_became_running) {
Result IApplicationFunctions::GetPseudoDeviceId(Out<Common::UUID> out_pseudo_device_id) {
LOG_WARNING(Service_AM, "(stubbed)");
R_UNLESS(out_pseudo_device_id, ResultUnknown);
R_UNLESS(out_pseudo_device_id != nullptr, ResultUnknown);
// This should be hashed with the device specific hash
// for now this will do
const auto res = FileSys::PatchManager::GetMetadataFromBaseOrUpdate(system, m_applet->program_id);
u8 hash[EVP_MAX_MD_SIZE];
R_UNLESS(res.first != nullptr, ResultUnknown);
std::array<u8, EVP_MAX_MD_SIZE> hash;
unsigned int hash_len = 0;
auto const seed = res.first->raw.seed_for_pseudo_device_id;
EVP_MD_CTX *ctx = EVP_MD_CTX_new();
auto const algorithm = EVP_sha1();
EVP_DigestInit_ex(ctx, algorithm, nullptr);
EVP_DigestUpdate(ctx, &seed, sizeof(seed));
EVP_DigestFinal_ex(ctx, hash, &hash_len);
EVP_DigestFinal_ex(ctx, hash.data(), &hash_len);
EVP_MD_CTX_free(ctx);
*out_pseudo_device_id = Common::UUID::MakeRFC4122V5(std::span<u8, 20>{hash, std::size(hash)});
*out_pseudo_device_id = Common::UUID::MakeRFC4122V5(std::span<u8, 16>{hash.begin(), hash.begin() + 16});
R_SUCCEED();
}
@@ -164,14 +164,14 @@ Result ILibraryAppletAccessor::PushInData(SharedPointer<IStorage> storage) {
Result ILibraryAppletAccessor::PopOutData(Out<SharedPointer<IStorage>> out_storage) {
LOG_DEBUG(Service_AM, "called");
if (auto caller_applet = m_applet->caller_applet.lock(); caller_applet) {
caller_applet->lifecycle_manager.GetSystemEvent().Signal(system.Kernel());
caller_applet->lifecycle_manager.RequestResumeNotification();
caller_applet->lifecycle_manager.GetSystemEvent().Clear(system.Kernel());
caller_applet->lifecycle_manager.UpdateRequestedFocusState();
}
R_TRY(m_broker->GetOutData().Pop(system.Kernel(), out_storage.Get()));
if (auto caller_applet = m_applet->caller_applet.lock(); caller_applet) {
std::scoped_lock lk{caller_applet->lock};
const bool focus_state_changed = caller_applet->lifecycle_manager.UpdateRequestedFocusState();
const bool is_front_app = m_applet->frontend && caller_applet->lifecycle_manager.IsApplication();
if (focus_state_changed) caller_applet->lifecycle_manager.SignalSystemEventIfNeeded(system.Kernel());
else if (is_front_app) caller_applet->lifecycle_manager.RequestFocusStateChangedNotification(system.Kernel());
}
if (m_applet->applet_id == AppletId::ProfileSelect && *out_storage) {
auto impl = (*out_storage)->GetImpl();
@@ -122,7 +122,10 @@ std::shared_ptr<ILibraryAppletAccessor> CreateGuestApplet(Core::System& system,
auto broker = std::make_shared<AppletDataBroker>(system);
applet->caller_applet = caller_applet;
applet->caller_applet_broker = broker;
caller_applet->child_applets.push_back(applet);
{
std::scoped_lock lk{caller_applet->lock};
caller_applet->child_applets.push_back(applet);
}
window_system.TrackApplet(applet, false);
return std::make_shared<ILibraryAppletAccessor>(system, broker, applet);
}
@@ -148,10 +151,10 @@ std::shared_ptr<ILibraryAppletAccessor> CreateFrontendApplet(Core::System& syste
applet->caller_applet = caller_applet;
applet->caller_applet_broker = storage;
applet->frontend = system.GetFrontendAppletHolder().GetApplet(applet, applet_id, mode);
caller_applet->child_applets.push_back(applet);
window_system.TrackApplet(applet, false);
{
std::scoped_lock lk{caller_applet->lock};
caller_applet->child_applets.push_back(applet);
}
return std::make_shared<ILibraryAppletAccessor>(system, storage, applet);
}
+2 -2
View File
@@ -97,7 +97,7 @@ Result IAudioDevice::SetAudioDeviceOutputVolumeAuto(
LOG_DEBUG(Service_Audio, "called. name={}, volume={}", device_name, volume);
if (device_name == "AudioTvOutput") {
impl->SetDeviceVolumes(volume);
impl->SetDeviceVolumes(system, volume);
}
R_SUCCEED();
@@ -112,7 +112,7 @@ Result IAudioDevice::GetAudioDeviceOutputVolumeAuto(
*out_volume = 1.0f;
if (device_name == "AudioTvOutput") {
*out_volume = impl->GetDeviceVolume(device_name);
*out_volume = impl->GetDeviceVolume(system, device_name);
}
R_SUCCEED();
+1 -1
View File
@@ -50,7 +50,7 @@ IAudioIn::IAudioIn(Core::System& system_, Manager& manager, size_t session_id,
}
IAudioIn::~IAudioIn() {
impl->Free();
impl->Free(system);
service_context.CloseEvent(event);
process->Close(system.Kernel());
}
@@ -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-License-Identifier: GPL-2.0-or-later
@@ -65,7 +68,7 @@ Result IAudioInManager::OpenAudioInAuto(
Result IAudioInManager::ListAudioInsAutoFiltered(
OutArray<AudioDeviceName, BufferAttr_HipcAutoSelect> out_audio_ins, Out<u32> out_count) {
LOG_DEBUG(Service_Audio, "called");
*out_count = impl->GetDeviceNames(out_audio_ins, true);
*out_count = impl->GetDeviceNames(system, out_audio_ins, true);
R_SUCCEED();
}
@@ -90,8 +93,8 @@ Result IAudioInManager::OpenAudioInProtocolSpecified(
size_t new_session_id{};
R_TRY(impl->LinkToManager());
R_TRY(impl->AcquireSessionId(new_session_id));
R_TRY(impl->LinkToManager(system));
R_TRY(impl->AcquireSessionId(system, new_session_id));
LOG_DEBUG(Service_Audio, "Opening new AudioIn, session_id={}, free sessions={}", new_session_id,
impl->num_free_sessions);
+1 -1
View File
@@ -46,7 +46,7 @@ IAudioOut::IAudioOut(Core::System& system_, Manager& manager, size_t session_id,
}
IAudioOut::~IAudioOut() {
impl->Free();
impl->Free(system);
service_context.CloseEvent(event);
process->Close(system.Kernel());
}
@@ -77,8 +77,8 @@ Result IAudioOutManager::OpenAudioOutAuto(
}
size_t new_session_id{};
R_TRY(impl->LinkToManager());
R_TRY(impl->AcquireSessionId(new_session_id));
R_TRY(impl->LinkToManager(system));
R_TRY(impl->AcquireSessionId(system, new_session_id));
const auto device_name = Common::StringFromBuffer(name[0].name);
LOG_DEBUG(Service_Audio, "Opening new AudioOut, sessionid={}, free sessions={}", new_session_id,
@@ -154,6 +154,9 @@ FSP_SRV::FSP_SRV(Core::System& system_)
{720, nullptr, "AbandonAccessFailure"},
{800, nullptr, "GetAndClearFileSystemProxyErrorInfo"},
{810, nullptr, "RegisterProgramIndexMapInfo"},
{820, nullptr, "GetContentStorageInfoIndex"},
{830, nullptr, "EncryptStreamPlaySaveData"},
{831, nullptr, "DecryptStreamPlaySaveData"},
{1000, nullptr, "SetBisRootForHost"},
{1001, nullptr, "SetSaveDataSize"},
{1002, nullptr, "SetSaveDataRootPath"},
@@ -121,7 +121,7 @@ IHidSystemServer::IHidSystemServer(Core::System& system_, std::shared_ptr<Resour
{547, nullptr, "GetAllowedBluetoothLinksCount"},
{548, &IHidSystemServer::GetRegisteredDevices, "GetRegisteredDevices"},
{549, nullptr, "GetConnectableRegisteredDevices"},
{551, nullptr, "GetRegisteredDevicesForControllerSupport"}, //20.0.0+
{551, &IHidSystemServer::GetRegisteredDevices, "GetRegisteredDevicesForControllerSupport"}, //20.0.0+ //mocked via 548 for Diablo 3 (at least)
{700, nullptr, "ActivateUniquePad"},
{702, &IHidSystemServer::AcquireUniquePadConnectionEventHandle, "AcquireUniquePadConnectionEventHandle"},
{703, &IHidSystemServer::GetUniquePadIds, "GetUniquePadIds"},
@@ -758,7 +758,7 @@ void IHidSystemServer::AcquireDeviceRegisteredEventForControllerSupport(HLEReque
}
void IHidSystemServer::GetRegisteredDevices(HLERequestContext& ctx) {
LOG_WARNING(Service_HID, "(STUBBED) called");
LOG_WARNING(Service_HID, "(STUBBED) called, command={}", ctx.GetCommand()); //548 or 551
struct RegisterData {
std::array<u8, 0x68> data;
+1
View File
@@ -22,6 +22,7 @@
namespace IPC {
constexpr Result ResultNotSupported{ErrorModule::HIPC, 1};
constexpr Result ResultSessionClosed{ErrorModule::HIPC, 301};
struct ResponseBuilder {
+28 -34
View File
@@ -71,17 +71,14 @@ public:
void InstructionSynchronizationBarrierRaised() override {
last_code_addr = u64(-1); //reset back, force refetch
}
u8 MemoryRead8(u64 vaddr) override {
return ReadMemory<u8>(vaddr);
}
u16 MemoryRead16(u64 vaddr) override {
return ReadMemory<u16>(vaddr);
}
u32 MemoryRead32(u64 vaddr) override {
return ReadMemory<u32>(vaddr);
}
u64 MemoryRead64(u64 vaddr) override {
return ReadMemory<u64>(vaddr);
u64 MemoryRead(u64 vaddr, size_t size) override {
switch (size) {
case sizeof(u64): return ReadMemory<u64>(vaddr);
case sizeof(u32): return ReadMemory<u32>(vaddr);
case sizeof(u16): return ReadMemory<u16>(vaddr);
case sizeof(u8): return ReadMemory<u8>(vaddr);
default: UNREACHABLE();
}
}
u128 MemoryRead128(u64 vaddr) override {
return ReadMemory<u128>(vaddr);
@@ -89,22 +86,19 @@ public:
std::string MemoryReadCString(u64 vaddr) {
std::string result{};
u8 next;
while ((next = MemoryRead8(vaddr++)) != 0)
while ((next = u8(MemoryRead(vaddr++, sizeof(u8)))) != 0)
result += char(next);
return result;
}
void MemoryWrite8(u64 vaddr, u8 value) override {
WriteMemory<u8>(vaddr, value);
}
void MemoryWrite16(u64 vaddr, u16 value) override {
WriteMemory<u16>(vaddr, value);
}
void MemoryWrite32(u64 vaddr, u32 value) override {
WriteMemory<u32>(vaddr, value);
}
void MemoryWrite64(u64 vaddr, u64 value) override {
WriteMemory<u64>(vaddr, value);
void MemoryWrite(u64 vaddr, u64 value, size_t size) override {
switch (size) {
case sizeof(u64): WriteMemory<u64>(vaddr, u64(value)); break;
case sizeof(u32): WriteMemory<u32>(vaddr, u32(value)); break;
case sizeof(u16): WriteMemory<u16>(vaddr, u16(value)); break;
case sizeof(u8): WriteMemory<u8>(vaddr, u8(value)); break;
default: UNREACHABLE();
}
}
void MemoryWrite128(u64 vaddr, u128 value) override {
WriteMemory<u128>(vaddr, value);
@@ -193,14 +187,14 @@ public:
// The loaded NRO file has ELF relocations that must be processed before it can run.
// Normally this would be processed by RTLD, but in HLE context, we don't have
// the linker available, so we have to do it ourselves.
const VAddr mod_offset{callbacks->MemoryRead32(4)};
if (callbacks->MemoryRead32(mod_offset) != Common::MakeMagic('M', 'O', 'D', '0'))
const VAddr mod_offset{callbacks->MemoryRead(4, sizeof(u32))};
if (callbacks->MemoryRead(mod_offset, sizeof(u32)) != Common::MakeMagic('M', 'O', 'D', '0'))
return false;
// For more info about dynamic entries, see the ELF ABI specification:
// https://refspecs.linuxbase.org/elf/gabi4+/ch5.dynamic.html
// https://refspecs.linuxbase.org/elf/gabi4+/ch4.reloc.html
VAddr dynamic_offset{mod_offset + callbacks->MemoryRead32(mod_offset + 4)};
VAddr dynamic_offset{mod_offset + callbacks->MemoryRead(mod_offset + 4, sizeof(u32))};
VAddr rela_dyn = 0, relr_dyn = 0;
size_t num_rela = 0, num_relr = 0;
while (true) {
@@ -222,8 +216,8 @@ public:
for (size_t i = 0; i < num_rela; i++) {
const auto rela{callbacks->ReadMemory<Elf64_Rela>(rela_dyn + i * sizeof(Elf64_Rela))};
if (Elf64RelType(rela.r_info) == ElfAArch64Relative) {
const VAddr contents{callbacks->MemoryRead64(rela.r_offset)};
callbacks->MemoryWrite64(rela.r_offset, contents + rela.r_addend);
const VAddr contents{callbacks->MemoryRead(rela.r_offset, sizeof(u64))};
callbacks->MemoryWrite(rela.r_offset, contents + rela.r_addend, sizeof(u64));
}
}
@@ -231,7 +225,7 @@ public:
for (size_t i = 0; i < num_relr; i++) {
const auto relr = callbacks->ReadMemory<Elf64_Relr>(relr_dyn + i * sizeof(Elf64_Relr));
const auto incr = [&](VAddr where) {
callbacks->MemoryWrite64(where, callbacks->MemoryRead64(where) + relocbase);
callbacks->MemoryWrite(where, callbacks->MemoryRead(where, sizeof(u64)) + relocbase, sizeof(u64));
};
if ((relr & 1) == 0) {
// where pointer
@@ -294,7 +288,7 @@ public:
if (argument_stack.size() > 8) {
const VAddr new_sp = Common::AlignDown(top_of_stack - (argument_stack.size() - 8) * sizeof(u64), STACK_ALIGN);
for (size_t i = 8; i < argument_stack.size(); i++)
callbacks->MemoryWrite64(new_sp + (i - 8) * sizeof(u64), argument_stack[i]);
callbacks->MemoryWrite(new_sp + (i - 8) * sizeof(u64), argument_stack[i], sizeof(u64));
jit->SetSP(new_sp);
}
// Reset the call state for the next invocation
@@ -385,17 +379,17 @@ void DynarmicCallbacks64::CallSVC(u32 swi) {
if (dest < src) {
for (size_t i = 0; i < n; i++)
MemoryWrite8(dest + i, MemoryRead8(src + i));
MemoryWrite(dest + i, u8(MemoryRead(src + i, sizeof(u8))), sizeof(u8));
} else {
for (size_t i = n; i > 0; i--)
MemoryWrite8(dest + i - 1, MemoryRead8(src + i - 1));
MemoryWrite(dest + i - 1, u8(MemoryRead(src + i - 1, sizeof(u8))), sizeof(u8));
}
} else if (pc == parent.helpers[size_t(HelperFn::Memset)]) {
const VAddr dest{parent.jit->GetRegister(0)};
const u64 c{parent.jit->GetRegister(1)};
const size_t n{parent.jit->GetRegister(2)};
for (size_t i = 0; i < n; i++)
MemoryWrite8(dest + i, u8(c));
MemoryWrite(dest + i, u8(c), sizeof(u8));
} else if (pc == parent.helpers[size_t(HelperFn::Resolve)]) {
// X0 contains a char* for a symbol to resolve
const auto name{MemoryReadCString(parent.jit->GetRegister(0))};
@@ -422,7 +416,7 @@ void DynarmicCallbacks64::CallSVC(u32 swi) {
}
void DynarmicCallbacks64::ExceptionRaised(u64 pc, Dynarmic::A64::Exception exception) {
auto const inst = MemoryRead32(pc);
auto const inst = MemoryRead(pc, sizeof(u32));
LOG_CRITICAL(Service_JIT, "{} PC @ {:08x}, data = {:08x}", exception, pc, inst);
parent.jit->HaltExecution();
}
+117
View File
@@ -6,10 +6,16 @@
#include "common/string_util.h"
#include "core/core.h"
#include "core/hle/kernel/k_client_session.h"
#include "core/hle/result.h"
#include "core/hle/service/acc/profile_manager.h"
#include "core/hle/service/cmif_types.h"
#include "core/hle/service/cmif_serialization.h"
#include "core/hle/service/ipc_helpers.h"
#include "core/hle/service/ngc/ngc.h"
#include "core/hle/service/server_manager.h"
#include "core/hle/service/service.h"
#include "frontend_common/firmware_manager.h"
namespace Service::NGC {
@@ -166,12 +172,123 @@ public:
}
};
struct SaveDataHandle {
u64 unk0;
};
static_assert(sizeof(SaveDataHandle) == 0x08);
class IUserShimScopedObject final : public ServiceFramework<IUserShimScopedObject> {
public:
explicit IUserShimScopedObject(Core::System& system_) : ServiceFramework(system_, "IUserShimScopedObject") {
// clang-format off
static const FunctionInfo functions[] = {
{450, nullptr, "InitializeForSaveData"},
{451, nullptr, "FinalizeForSaveData"},
{452, D<&IUserShimScopedObject::OpenSaveData>, "OpenSaveData"},
{453, nullptr, "CloseSaveData"},
{454, D<&IUserShimScopedObject::ReadSaveSlot>, "ReadSaveSlot"},
{455, D<&IUserShimScopedObject::WriteSaveSlot>, "WriteSaveSlot"},
{456, nullptr, "FlushSaveSlot"},
{457, nullptr, "CommitSaveData"},
};
// clang-format on
RegisterHandlers(functions);
}
Result OpenSaveData(Account::Uid unk0, Out<SaveDataHandle> unk1) {
LOG_WARNING(Service_NGC, "stubbed");
R_THROW(IPC::ResultNotSupported);
}
Result ReadSaveSlot(s32 offset, SaveDataHandle handle, OutBuffer<BufferAttr_HipcAutoSelect> out_data, Out<u32> out_size) {
LOG_WARNING(Service_NGC, "stubbed");
R_THROW(IPC::ResultNotSupported);
}
Result WriteSaveSlot(s32 offset, SaveDataHandle handle, InBuffer<BufferAttr_HipcAutoSelect> out_data) {
LOG_WARNING(Service_NGC, "stubbed");
// to implement
R_SUCCEED();
}
};
class IUserService final : public ServiceFramework<IUserService> {
public:
explicit IUserService(Core::System& system_) : ServiceFramework(system_, "stpl:u") {
// clang-format off
static const FunctionInfo functions[] = {
{0 , D<&IUserService::Cmd0>, "Cmd0"},
};
// clang-format on
RegisterHandlers(functions);
}
Result Cmd0(u32 unk0, OutInterface<IUserShimScopedObject> out_interface) {
LOG_WARNING(Service_NGC, "stubbed");
*out_interface = std::make_shared<IUserShimScopedObject>(system);
R_SUCCEED();
}
};
class ISystemShimScopedObject final : public ServiceFramework<ISystemShimScopedObject> {
public:
explicit ISystemShimScopedObject(Core::System& system_) : ServiceFramework(system_, "ISystemShimScopedObject") {
// clang-format off
static const FunctionInfo functions[] = {
{106, nullptr, "Cmd106"},
{107, nullptr, "Cmd107"},
{108, D<&ISystemShimScopedObject::Cmd108>, "Cmd108"},
{207, nullptr, "Cmd207"},
{208, D<&ISystemShimScopedObject::Cmd208>, "Cmd208"},
{209, nullptr, "Cmd209"},
{210, nullptr, "Cmd210"},
{211, nullptr, "Cmd211"},
{212, nullptr, "Cmd212"},
};
// clang-format on
RegisterHandlers(functions);
}
Result Cmd108() {
LOG_WARNING(Service_NGC, "stubbed");
R_THROW(IPC::ResultNotSupported);
}
Result Cmd208(Out<std::array<u8, 0x20>> unk0) {
LOG_WARNING(Service_NGC, "stubbed");
R_THROW(IPC::ResultNotSupported);
}
};
class ISystemService final : public ServiceFramework<ISystemService> {
public:
explicit ISystemService(Core::System& system_) : ServiceFramework(system_, "stpl:sys") {
// clang-format off
static const FunctionInfo functions[] = {
{0 , D<&ISystemService::Cmd0>, "Cmd0"},
};
// clang-format on
RegisterHandlers(functions);
}
Result Cmd0(OutInterface<ISystemShimScopedObject> out_interface) {
LOG_WARNING(Service_NGC, "stubbed");
*out_interface = std::make_shared<ISystemShimScopedObject>(system);
R_SUCCEED();
}
};
void LoopProcess(Core::System& system) {
auto server_manager = std::make_unique<ServerManager>(system);
server_manager->RegisterNamedService("ngct:u", std::make_shared<IService>(system), 4);
server_manager->RegisterNamedService("ngct:s", std::make_shared<IServiceWithManagementApi>(system), 4);
server_manager->RegisterNamedService("ngc:u", std::make_shared<NgcServiceImpl>(system), 4);
// +23.0.0
if (FirmwareManager::GetFirmwareVersion(system).first.major >= 23) {
server_manager->RegisterNamedService("stpl:u", std::make_shared<IUserService>(system), 4);
server_manager->RegisterNamedService("stpl:sys", std::make_shared<ISystemService>(system), 4);
}
ServerManager::RunServer(std::move(server_manager));
}
@@ -228,9 +228,9 @@ IApplicationManagerInterface::IApplicationManagerInterface(Core::System& system_
{930, nullptr, "Unknown930"}, //20.0.0+
{931, nullptr, "Unknown931"}, //20.0.0+
{933, nullptr, "Unknown933"}, //20.0.0+
{934, nullptr, "Unknown934"}, //20.0.0+
{935, nullptr, "Unknown935"}, //20.0.0+
{936, nullptr, "Unknown936"}, //20.0.0+
{934, nullptr, "Unknown934"}, //21.0.0+
{935, nullptr, "Unknown935"}, //21.0.0+
{936, D<&IApplicationManagerInterface::Unknown936>, "Unknown936"}, //21.0.0+
{1000, nullptr, "RequestVerifyApplicationDeprecated"},
{1001, nullptr, "CorruptApplicationForDebug"},
{1002, nullptr, "RequestVerifyAddOnContentsRights"},
@@ -422,7 +422,7 @@ IApplicationManagerInterface::IApplicationManagerInterface(Core::System& system_
{4039, nullptr, "Unknown4039"}, //20.0.0+
{4040, nullptr, "Unknown4040"}, //20.0.0+
{4041, nullptr, "Unknown4041"}, //20.0.0+
{4042, nullptr, "Unknown4042"}, //20.0.0+
{4042, D<&IApplicationManagerInterface::Unknown4042>, "Unknown4042"}, //20.0.0+
{4043, nullptr, "Unknown4043"}, //20.0.0+
{4044, nullptr, "Unknown4044"}, //20.0.0+
{4045, nullptr, "Unknown4045"}, //20.0.0+
@@ -639,6 +639,12 @@ Result IApplicationManagerInterface::IsGameCardApplicationRunning(Out<bool> out_
R_SUCCEED();
}
Result IApplicationManagerInterface::Unknown936(Out<u64> out_result) {
LOG_WARNING(Service_NS, "(STUBBED) called.");
*out_result = 0;
R_SUCCEED();
}
Result IApplicationManagerInterface::IsAnyApplicationEntityInstalled(
Out<bool> out_is_any_application_entity_installed) {
LOG_WARNING(Service_NS, "(STUBBED) called");
@@ -862,6 +868,15 @@ Result IApplicationManagerInterface::Unknown4023(Out<u64> out_result) {
R_SUCCEED();
}
Result IApplicationManagerInterface::Unknown4042(OutInterface<IAsyncResult> out_interface,
OutCopyHandle<Kernel::KReadableEvent> out_event,
u64 arg1, u64 arg2) {
LOG_WARNING(Service_NS, "(STUBBED) called, arg1={:016X}, arg2={:016X}", arg1, arg2);
*out_event = unknown_event.GetHandle();
*out_interface = std::make_shared<IAsyncResult>(system, &unknown_event);
R_SUCCEED();
}
Result IApplicationManagerInterface::Unknown4053() {
LOG_WARNING(Service_NS, "(STUBBED) called.");
R_SUCCEED();
@@ -7,6 +7,7 @@
#pragma once
#include "core/hle/service/cmif_types.h"
#include "core/hle/service/ns/async_result.h"
#include "core/hle/service/ns/language.h"
#include "core/hle/service/ns/ns_types.h"
#include "core/hle/service/os/event.h"
@@ -34,6 +35,7 @@ public:
Result GetGameCardMountFailureEvent(OutCopyHandle<Kernel::KReadableEvent> out_event);
Result GetGameCardWakenReadyEvent(OutCopyHandle<Kernel::KReadableEvent> out_event);
Result IsGameCardApplicationRunning(Out<bool> out_is_running);
Result Unknown936(Out<u64> out_result);
Result IsAnyApplicationEntityInstalled(Out<bool> out_is_any_application_entity_installed);
Result GetApplicationViewDeprecated(
OutArray<ApplicationViewV19, BufferAttr_HipcMapAlias> out_application_views,
@@ -71,6 +73,9 @@ public:
InBuffer<BufferAttr_HipcMapAlias> logo_path_buffer);
Result Unknown4022(OutCopyHandle<Kernel::KReadableEvent> out_event);
Result Unknown4023(Out<u64> out_result);
Result Unknown4042(OutInterface<IAsyncResult> out_interface,
OutCopyHandle<Kernel::KReadableEvent> out_event,
u64 arg1, u64 arg2);
Result Unknown4053();
Result Unknown4105();

Some files were not shown because too many files have changed in this diff Show More