Compare commits

...

60 Commits

Author SHA1 Message Date
lizzie
d34400226b [core/hle/kernel] fix potential datarace where thread may be told to be non-running but inbetween the acquisition of the lock there may be another holder whom puts it on
Signed-off-by: lizzie <lizzie@eden-emu.dev>
2025-10-30 20:02:21 +00:00
lizzie
d498db1106 [dynarmic] nuke zydis/zycore out of orbit forever (#2893)
LLVM does what Zydisn't
Signed-off-by: lizzie <lizzie@eden-emu.dev>

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/2893
Reviewed-by: crueter <crueter@eden-emu.dev>
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
Co-authored-by: lizzie <lizzie@eden-emu.dev>
Co-committed-by: lizzie <lizzie@eden-emu.dev>
2025-10-30 15:00:14 +01:00
crueter
e93159b047 [qt] clean up some orphaned_profiles bugs; add help (#2894)
Some weird edge cases of "phantom" profiles that are actually needed
for... reasons I guess

Also, fixed some of the logic w.r.t empty checking, plus added a help
page

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

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/2894
Reviewed-by: Lizzie <lizzie@eden-emu.dev>
Reviewed-by: Maufeat <sahyno1996@gmail.com>
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
2025-10-30 11:03:08 +01:00
lizzie
d989166044 Revert "[docs] Add @Ribbit to video_core/ and shader_recompiler/ CODEOWNERS (#2892)" (#2896)
This reverts commit 206e7af162.

?ploo

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/2896
Co-authored-by: lizzie <lizzie@eden-emu.dev>
Co-committed-by: lizzie <lizzie@eden-emu.dev>
2025-10-30 04:47:32 +01:00
lizzie
c96defb80d Reapply PRs #2778 and #2812 (#2867) (#2868)
Reverts the "problematic" part of the pr (swap of s64/F16x2FTZRN) + leaves a very visible comment for newcomers (since yes, disas says it should be like that but oh well)
 Signed-off-by: lizzie lizzie@eden-emu.dev

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/2868
Reviewed-by: crueter <crueter@eden-emu.dev>
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
Co-authored-by: lizzie <lizzie@eden-emu.dev>
Co-committed-by: lizzie <lizzie@eden-emu.dev>
2025-10-30 03:47:56 +01:00
MaranBr
06275885f1 [video_core] Remove redundant check on image_view and image_view_info (#2891)
This removes a redundant check on image_view and image_view_info. This check is not needed here.

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/2891
Co-authored-by: MaranBr <maranbr@outlook.com>
Co-committed-by: MaranBr <maranbr@outlook.com>
2025-10-30 03:45:43 +01:00
lizzie
206e7af162 [docs] Add @Ribbit to video_core/ and shader_recompiler/ CODEOWNERS (#2892)
Signed-off-by: lizzie <lizzie@eden-emu.dev>
Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/2892
Reviewed-by: Shinmegumi <shinmegumi@eden-emu.dev>
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
Co-authored-by: lizzie <lizzie@eden-emu.dev>
Co-committed-by: lizzie <lizzie@eden-emu.dev>
2025-10-29 19:38:24 +01:00
lizzie
cdf4ce07bc [dist] Halloween icons (#2886)
Graphic design is my passion?
eden_named.ico wasn't used by anything
Signed-off-by: lizzie <lizzie@eden-emu.dev>

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/2886
Co-authored-by: lizzie <lizzie@eden-emu.dev>
Co-committed-by: lizzie <lizzie@eden-emu.dev>
2025-10-29 13:17:50 +01:00
Caio Oliveira
2f591d33d3 [meta] Add option to FORCE X11 as Graphics Backend (#2820)
* save the option on a external file because settings
  are loaded AFTER Qt window is created and then
  the graphics backend is already applied

Signed-off-by: Caio Oliveira <caiooliveirafarias0@gmail.com>

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/2820
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
Reviewed-by: Shinmegumi <shinmegumi@eden-emu.dev>
Co-authored-by: Caio Oliveira <caiooliveirafarias0@gmail.com>
Co-committed-by: Caio Oliveira <caiooliveirafarias0@gmail.com>
2025-10-29 13:16:57 +01:00
lizzie
e4b0c03a22 [dynarmic] use (v)pshufb (SSSE3+) for VREV32/64 emits (#2851)
Signed-off-by: lizzie <lizzie@eden-emu.dev>
Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/2851
Reviewed-by: crueter <crueter@eden-emu.dev>
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
Reviewed-by: Caio Oliveira <caiooliveirafarias0@gmail.com>
Co-authored-by: lizzie <lizzie@eden-emu.dev>
Co-committed-by: lizzie <lizzie@eden-emu.dev>
2025-10-29 13:12:39 +01:00
lizzie
9582dfffee [core/hle/service] use single lambda dispatcher as opposed to spamming 48+6 tiny cold cloned capturing lambdas (#2880)
Cold clones suck

This is a very insignificant change that shouldn't even affect anything except the removal of some cold clones (also it will make your backtraces a bit nicer)

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

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/2880
Reviewed-by: Caio Oliveira <caiooliveirafarias0@gmail.com>
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
Co-authored-by: lizzie <lizzie@eden-emu.dev>
Co-committed-by: lizzie <lizzie@eden-emu.dev>
2025-10-29 13:12:07 +01:00
DraVee
cf9f78636a [cmake] if CCACHE is set but not found, make it fatal (#2885)
Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/2885
Reviewed-by: Lizzie <lizzie@eden-emu.dev>
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
Co-authored-by: DraVee <caiooliveirafarias0@gmail.com>
Co-committed-by: DraVee <caiooliveirafarias0@gmail.com>
2025-10-29 13:09:40 +01:00
lizzie
ae46778cc3 [qt, discord-rpc] Don't use named variant for icon (#2888)
Signed-off-by: lizzie <lizzie@eden-emu.dev>
Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/2888
Reviewed-by: Maufeat <sahyno1996@gmail.com>
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
Co-authored-by: lizzie <lizzie@eden-emu.dev>
Co-committed-by: lizzie <lizzie@eden-emu.dev>
2025-10-29 13:09:25 +01:00
lizzie
c31761ab75 [tools] fix optimize-assets not being multithreaded due to -print0 (#2887)
Signed-off-by: lizzie <lizzie@eden-emu.dev>
Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/2887
Reviewed-by: crueter <crueter@eden-emu.dev>
Reviewed-by: Maufeat <sahyno1996@gmail.com>
Co-authored-by: lizzie <lizzie@eden-emu.dev>
Co-committed-by: lizzie <lizzie@eden-emu.dev>
2025-10-29 13:09:05 +01:00
lizzie
b50f8c620b [dynarmic] replace mcl::bit_cast with std one (#2866)
Don't merge till it works on steamdeck :)
Signed-off-by: lizzie <lizzie@eden-emu.dev>

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/2866
Reviewed-by: Maufeat <sahyno1996@gmail.com>
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
Reviewed-by: crueter <crueter@eden-emu.dev>
Reviewed-by: Caio Oliveira <caiooliveirafarias0@gmail.com>
Co-authored-by: lizzie <lizzie@eden-emu.dev>
Co-committed-by: lizzie <lizzie@eden-emu.dev>
2025-10-29 04:10:02 +01:00
lizzie
fa718acfbe [tools, android/res] Remove unused strings, update unused-strings.sh to work on POSIX shells (#2879)
I had this pending but oh well, the double sed is fine for sh I believe
Signed-off-by: lizzie <lizzie@eden-emu.dev>

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/2879
Reviewed-by: crueter <crueter@eden-emu.dev>
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
Co-authored-by: lizzie <lizzie@eden-emu.dev>
Co-committed-by: lizzie <lizzie@eden-emu.dev>
2025-10-29 03:26:09 +01:00
lizzie
ce0a299bdb [qt] do not fatally error out immediately if OpenGL is missing some extensions (#2876)
It may be possible to run without the extensions, with decreased stability of course (or partial implementation thereof)
Signed-off-by: lizzie <lizzie@eden-emu.dev>

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/2876
Reviewed-by: crueter <crueter@eden-emu.dev>
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
Co-authored-by: lizzie <lizzie@eden-emu.dev>
Co-committed-by: lizzie <lizzie@eden-emu.dev>
2025-10-29 03:25:22 +01:00
crueter
d3dbabcfc7 [docs] debug log/gfx debug info (#2871)
Signed-off-by: crueter <crueter@eden-emu.dev>
Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/2871
Reviewed-by: Caio Oliveira <caiooliveirafarias0@gmail.com>
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
2025-10-29 03:24:20 +01:00
lizzie
effe8109a7 [frontend] fix Simpleini warnings SI_NO_CONVERSION macro being redefined when provided by cmdline (freebsd, linux, etc) (#2877)
Signed-off-by: lizzie <lizzie@eden-emu.dev>
Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/2877
Reviewed-by: crueter <crueter@eden-emu.dev>
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
Co-authored-by: lizzie <lizzie@eden-emu.dev>
Co-committed-by: lizzie <lizzie@eden-emu.dev>
2025-10-29 03:23:46 +01:00
lizzie
2924297790 [qt] move Discord/revolt/X links to the about section (#2883)
highly subjective change
Signed-off-by: lizzie <lizzie@eden-emu.dev>

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/2883
Reviewed-by: crueter <crueter@eden-emu.dev>
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
Co-authored-by: lizzie <lizzie@eden-emu.dev>
Co-committed-by: lizzie <lizzie@eden-emu.dev>
2025-10-29 03:23:25 +01:00
lizzie
5c6aaa7eb1 [video_core/vic] remove handrolled vector implementation that also uses software prefetching(!!!!); don't try to outsmart compiler - also remove template spam (#2856)
Also removes sse2neon :)
Software prefetching SUCKS and it's evil don't do it
Signed-off-by: lizzie <lizzie@eden-emu.dev>

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/2856
Reviewed-by: crueter <crueter@eden-emu.dev>
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
Reviewed-by: Caio Oliveira <caiooliveirafarias0@gmail.com>
Co-authored-by: lizzie <lizzie@eden-emu.dev>
Co-committed-by: lizzie <lizzie@eden-emu.dev>
2025-10-29 03:13:24 +01:00
MaranBr
83c162d80d [video_core] Fix regression on image_view and image_view_info (#2873)
Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/2873
Reviewed-by: Lizzie <lizzie@eden-emu.dev>
Reviewed-by: crueter <crueter@eden-emu.dev>
Co-authored-by: MaranBr <maranbr@outlook.com>
Co-committed-by: MaranBr <maranbr@outlook.com>
2025-10-29 00:07:13 +01:00
lizzie
1a2d1821b5 [meta] add missing license headers for some .cmake/.sh (#2878)
Mainly so doing `UPDATE=1 .ci/license-header.sh` doesnt just spam them everytime (I have like a copy of them on almost every stash ever since haha)
Signed-off-by: lizzie <lizzie@eden-emu.dev>

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/2878
Reviewed-by: crueter <crueter@eden-emu.dev>
Co-authored-by: lizzie <lizzie@eden-emu.dev>
Co-committed-by: lizzie <lizzie@eden-emu.dev>
2025-10-28 23:54:45 +01:00
lizzie
a9b87077f8 [cmake] add native build preset in aarch64 (#2874)
Signed-off-by: lizzie <lizzie@eden-emu.dev>
Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/2874
Reviewed-by: Caio Oliveira <caiooliveirafarias0@gmail.com>
Reviewed-by: crueter <crueter@eden-emu.dev>
Co-authored-by: lizzie <lizzie@eden-emu.dev>
Co-committed-by: lizzie <lizzie@eden-emu.dev>
2025-10-28 23:52:49 +01:00
lizzie
2e092d5f5d [cmake] remove unused YUZU_UNIX define (#2875)
Signed-off-by: lizzie <lizzie@eden-emu.dev>
Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/2875
Reviewed-by: Caio Oliveira <caiooliveirafarias0@gmail.com>
Reviewed-by: crueter <crueter@eden-emu.dev>
Co-authored-by: lizzie <lizzie@eden-emu.dev>
Co-committed-by: lizzie <lizzie@eden-emu.dev>
2025-10-28 23:52:38 +01:00
lizzie
a5996c7511 [dynarmic] Refactoring to reduce latency hit from recompilation (#358)
Signed-off-by: lizzie <lizzie@eden-emu.dev>
Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/358
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
Reviewed-by: Caio Oliveira <caiooliveirafarias0@gmail.com>
Reviewed-by: crueter <crueter@eden-emu.dev>
Co-authored-by: lizzie <lizzie@eden-emu.dev>
Co-committed-by: lizzie <lizzie@eden-emu.dev>
2025-10-28 23:42:31 +01:00
lizzie
1c57172524 [cmake] fix cpp-jwt build fail even if ENABLE_UPDATE_CHECKER is defined but NOT ENABLE_WEB_SERVICE (#2881)
Signed-off-by: lizzie <lizzie@eden-emu.dev>
Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/2881
Co-authored-by: lizzie <lizzie@eden-emu.dev>
Co-committed-by: lizzie <lizzie@eden-emu.dev>
2025-10-28 23:33:30 +01:00
crueter
d34b44ce39 [desktop] move "Link to Ryujinx" (#2869)
Signed-off-by: crueter <crueter@eden-emu.dev>
Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/2869
2025-10-28 22:05:23 +01:00
MaranBr
ecf7af0cde Revert PRs #2778 and #2812 (#2867)
Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/2867
Co-authored-by: MaranBr <maranbr@outlook.com>
Co-committed-by: MaranBr <maranbr@outlook.com>
2025-10-28 18:56:03 +01:00
unknown
6b01c13975 Revert "[dynarmic] replace mcl::bit_cast with std::bit_cast; fix IR_emit codepath warnings for UNREACHABLE(); remove type trait mcl::integer_of_size dependency (#2775)" (#2863)
This reverts commit 61ab1be0e7.

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/2863
Co-authored-by: unknown <sahyno1996@gmail.com>
Co-committed-by: unknown <sahyno1996@gmail.com>
2025-10-28 14:14:42 +01:00
Ribbit
15becaa5a3 [Audio] Tighten sink queue back pressure (#2840)
Co-authored-by: Ribbit <ribbit@placeholder.com>
Co-authored-by: MaranBr <maranbr@outlook.com>
Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/2840
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
Co-authored-by: Ribbit <ribbit@eden-emu.dev>
Co-committed-by: Ribbit <ribbit@eden-emu.dev>
2025-10-28 04:26:58 +01:00
Ribbit
3d4bb08cfc Revert "[Shader Recompiler] Caching optimization for Texture_Pass (#481)" (#2859)
This commit has some bugs that causes FPS drops, and crashes after playing for more than 1hr+.

Needs to be reworked and further tested.

Co-authored-by: Ribbit <ribbit@placeholder.com>
Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/2859
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
Co-authored-by: Ribbit <ribbit@eden-emu.dev>
Co-committed-by: Ribbit <ribbit@eden-emu.dev>
2025-10-28 04:26:45 +01:00
MaranBr
b2ac18173f [video_core] Fix more edge cases in image_view and image_view_info (#2853)
This fixes more edge cases where incorrect textures were used in image_view and image_view_info.

Complement to PR 2830.

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/2853
Reviewed-by: crueter <crueter@eden-emu.dev>
Co-authored-by: MaranBr <maranbr@outlook.com>
Co-committed-by: MaranBr <maranbr@outlook.com>
2025-10-28 04:25:23 +01:00
crueter
39f226a853 [qt] Ryujinx save data link (#2815)
This adds an action to the Game List context menu that lets users link
save data from Eden to Ryujinx, or vice versa.

Unfortunately, this isn't so simple to deal with due to the way Ryujinx's saves work. Ryujinx stores its saves in the... config directory... in `bis/user/save`. Unlike Yuzu, however, it doesn't store things by TitleID, instead it's just a bunch of directories from 000...01 to 000...0f and so on. The way it *maps* TitleID to SaveID is via `imkvdb.arc` in `bis/system/save/8000000000000000/0/` and also an identical copy in the `1` directory for... some reason. `imkvdb.arc` is handled by `FlatMapKeyValueStore` in LibHac, which, as the name implies, is a key-value storage system that `imkvdb.arc`, and seemingly `imkvdb.arc` alone, uses. The way this class is written is really weird, almost as if it's designed to accommodate more types of kvdbs... but for now we can safely assume that there aren't gonna be any other `kvdb` implementations added to HorizonNX.

Regardless, the file format is ridiculously simple so I didn't actually need to do a deep dive into C# code... of which I can basically only read Avalonia. A simple `xxd` on the `imkvdb.arc` is all that's needed, and here's everything that matters:
- The `IMKV` magic header (4 bytes)
- 8 bytes that don't really have anything useful to us, except for a size byte (presumably a `u32`) strewn at offset `0x08` from the start of the file, which is useless to us
- Then we start the `IMEN` list. I don't know what the `IM` stands for, but `IMEN` is just, well, an ENtry. Offsets shown are relative to the start of the `IMEN` header.
  * 4-byte `IMEN` magic header at 0x0
  * 8 bytes of filler data. It contains two `0x40` bytes, but I'm not really sure what they do
  * TitleID (u64) at `0xC`, for example `00a0 df10  501f 0001` for Legends: Arceus (the byte order is swapped)
  * 0x38 bytes of filler starting at offset 0x14
  * SaveID (u64) at `0x4C`, for example `0a00 0000 0000 0000` for my Legends: Arceus save
  * 0x38 bytes of filler starting at offset 0x54

Full example for Legends: Arceus:
```
000001b0: 494d 454e 4000 0000 4000 0000 00a0 df10  IMEN@...@.......
000001c0: 501f 0001 0100 0000 0000 0000 0000 0000  P...............
000001d0: 0000 0000 0000 0000 0000 0000 0100 0000  ................
000001e0: 0000 0000 0000 0000 0000 0000 0000 0000  ................
000001f0: 0000 0000 0000 0000 0000 0000 0a00 0000  ................
00000200: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00000210: 0000 0000 0100 0000 0000 0000 0000 0000  ................
00000220: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00000230: 0000 0000 0000 0000 0000 0000 494d 454e  ............IMEN
```
Ultimately, the size of the `IMEN` sits at 0x8C or 140 bytes. With this knowledge reading all the TitleID -> SaveID pairs is basically free, and outside of validation and stuff is like 15 lines of relevant code. Some interesting caveats, though:
- There are two entries for some TitleIDs for... some reason? Ignoring the second one seems to work though.
- Within each save directory, there are directories `0` and `1`... and only `0` ever seems used??? It's where Ryujinx points you to for save, so I just chose to use that.

Once everything is parsed, the rest of the implementation is extremely trivial:
- When the user requests a Ryujinx link, match the current program_id to the corresponding SaveID in `imkvdb`
- If it doesn't exist, just error out (save data is probably nonexistent)
- If it does though, give the user the option to use Eden's current save data OR Ryujinx's current save data.

Old save data is deleted depending on which one you chose.

Signed-off-by: crueter <crueter@eden-emu.dev>
Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/2815
Reviewed-by: Lizzie <lizzie@eden-emu.dev>
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
2025-10-28 03:46:47 +01:00
lizzie
61ab1be0e7 [dynarmic] replace mcl::bit_cast with std::bit_cast; fix IR_emit codepath warnings for UNREACHABLE(); remove type trait mcl::integer_of_size dependency (#2775)
- std::bit_cast already on libc++ (see also #2774)
- type trait is a 5-liner for u8/u16/u32/u64
- UNREACHABLE(); can just be converted into __builtin_unreachable() (TODO: eventually phase out to std::unreachable() once all platforms support it)\
The templatery parametrization is creating many copies of the same function, polluting i-cache and just bloating code size for minimal savings (and in fact could be negative net savings because of the bloated code size) - this is mostly in preparation to a partial removal of template<size_t XXX> in functions to remove instancing (think of the function being copied x4 times... because the compiler is forced to inline it...)
Signed-off-by: lizzie lizzie@eden-emu.dev

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/2775
Reviewed-by: crueter <crueter@eden-emu.dev>
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
Co-authored-by: lizzie <lizzie@eden-emu.dev>
Co-committed-by: lizzie <lizzie@eden-emu.dev>
2025-10-28 03:46:24 +01:00
lizzie
3ba9769d7a [docs/user] add reshade instructions to fix flickers and such (#2848)
mini-tutorial courtesy of bob
Signed-off-by: lizzie <lizzie@eden-emu.dev>

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/2848
Reviewed-by: crueter <crueter@eden-emu.dev>
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
Co-authored-by: lizzie <lizzie@eden-emu.dev>
Co-committed-by: lizzie <lizzie@eden-emu.dev>
2025-10-28 03:44:38 +01:00
lizzie
dd9cae4ebc [logging, debugger] remove unescesary logic and only query USER env variable once (#2800)
- censoring an username would lead to the variable being queried everytime something is written, just store it on a static
- dont use a map<> for something that can be done in a switch statment (and that the compiler will optimise for free!!!)
Signed-off-by: lizzie <lizzie@eden-emu.dev>

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/2800
Reviewed-by: crueter <crueter@eden-emu.dev>
Co-authored-by: lizzie <lizzie@eden-emu.dev>
Co-committed-by: lizzie <lizzie@eden-emu.dev>
2025-10-27 20:55:01 +01:00
Caio Oliveira
8fa36a7737 nuke: Goodbye PCH, you will not be missed (#2821)
Signed-off-by: Caio Oliveira <caiooliveirafarias0@gmail.com>
Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/2821
Reviewed-by: crueter <crueter@eden-emu.dev>
Co-authored-by: Caio Oliveira <caiooliveirafarias0@gmail.com>
Co-committed-by: Caio Oliveira <caiooliveirafarias0@gmail.com>
2025-10-27 20:50:16 +01:00
crueter
07b09b3849 [codeowners] nyxynx -> kleidis (#2855)
Signed-off-by: crueter <crueter@eden-emu.dev>
Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/2855
2025-10-27 17:31:06 +01:00
crueter
903faacaab [qt] clarify orphaned profiles by showing GOOD uuids (#2850)
Shows what profile UUIDs are actually good so the user knows which one
to copy their saves to.

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

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/2850
Reviewed-by: Lizzie <lizzie@eden-emu.dev>
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
2025-10-27 11:25:42 +01:00
lizzie
c713d44c88 [dynarmic, docs] Add support for DragonFly, shared mapping fallback to private, and update docs (#2829)
Add deps instructions for void linux, alpine, nixOS, DragonFlyBSD, update haiku new stuff; add basic coding guidelines

Also fixes ucontext on dragonfly :)

Allows fallback if ftruncate() fails (DragonFly doesnt like shm of big files with hammer2...) This will also indirectly help OpenIndiana and Solaris since they dont like big shared files either.

The downside is that no inspection of the memory can be easily done. But that's just an edge case anyways.

Signed-off-by: lizzie <lizzie@eden-emu.dev>
Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/2829
Reviewed-by: Shinmegumi <shinmegumi@eden-emu.dev>
Reviewed-by: crueter <crueter@eden-emu.dev>
Reviewed-by: CamilleLaVey <camillelavey99@gmail.com>
Co-authored-by: lizzie <lizzie@eden-emu.dev>
Co-committed-by: lizzie <lizzie@eden-emu.dev>
2025-10-27 05:13:44 +01:00
lizzie
2a3e815dcd [cmake, dynarmic/tests] do not enable avx2, remove unused cmake opts (#2844)
Signed-off-by: lizzie lizzie@eden-emu.dev
Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/2844
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
Reviewed-by: crueter <crueter@eden-emu.dev>
Co-authored-by: lizzie <lizzie@eden-emu.dev>
Co-committed-by: lizzie <lizzie@eden-emu.dev>
2025-10-27 04:52:27 +01:00
Ribbit
dc907616a9 [Android] Fix Amiibo bug (#2847)
Co-authored-by: Ribbit <ribbit@placeholder.com>
Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/2847
Reviewed-by: crueter <crueter@eden-emu.dev>
Co-authored-by: Ribbit <ribbit@eden-emu.dev>
Co-committed-by: Ribbit <ribbit@eden-emu.dev>
2025-10-27 04:52:08 +01:00
Caio Oliveira
0be29d2947 [meta] fix building with clang 21.1.4 (#2839)
Signed-off-by: Caio Oliveira <caiooliveirafarias0@gmail.com>
Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/2839
Co-authored-by: Caio Oliveira <caiooliveirafarias0@gmail.com>
Co-committed-by: Caio Oliveira <caiooliveirafarias0@gmail.com>
2025-10-27 00:37:24 +01:00
Ribbit
683c2834aa [Android] Finally add Amiibo load support to Android (#2845)
Co-authored-by: Ribbit <ribbit@placeholder.com>
Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/2845
Reviewed-by: crueter <crueter@eden-emu.dev>
Reviewed-by: Maufeat <sahyno1996@gmail.com>
Co-authored-by: Ribbit <ribbit@eden-emu.dev>
Co-committed-by: Ribbit <ribbit@eden-emu.dev>
2025-10-26 22:33:18 +01:00
xbzk
c788dbb3ef single commit to fix glCcolorMagic logic (#2842)
just pushing this fix for some silly bug before i forget it for the second time.

just too obvious that don't even need to test.

may fix some blued stuff where it should be green, and vice versa.

Co-authored-by: Allison Cunha <allisonbzk@gmail.com>
Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/2842
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
Co-authored-by: xbzk <xbzk@eden-emu.dev>
Co-committed-by: xbzk <xbzk@eden-emu.dev>
2025-10-26 20:51:21 +01:00
crueter
7846f4de31 [ci] delete old workflows (#2843)
unneeded, and highly outdated

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

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/2843
2025-10-26 20:45:06 +01:00
crueter
a0769ad835 [cmake] MinGW builds; fix line endings; fix weird multiplayer lobby behavior (#2835)
MinGW builds perform pretty dramatically better than MSVC in my (brief) testing, getting 40% better FPS on my KVM than MSVC. How this will translate to the real world, who knows, but this is a really good target to have.

TODO: Add this to CI, potentially replace clang-cl

Signed-off-by: crueter <crueter@eden-emu.dev>
Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/2835
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
2025-10-26 17:28:14 +01:00
DraVee
470214412b [cmake] sse2neon: vendor it! (#2817)
sse2neon: Revert to "DLTcollab/sse2neon@66267b52fd74951d8c224a4ed8baad4102c3cfd7"

2024-01-30 21:15:29 +0800 Optimize CRC intrinisics for targets lacking of CRC extension
github.com/DLTcollab/sse2neon/commit/66267b52fd74951d8c224a4ed8baad4102c3cfd7

* superseed #107

Signed-off-by: Caio Oliveira <caiooliveirafarias0@gmail.com>
Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/2817
Reviewed-by: Maufeat <sahyno1996@gmail.com>
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
Co-authored-by: DraVee <caiooliveirafarias0@gmail.com>
Co-committed-by: DraVee <caiooliveirafarias0@gmail.com>
2025-10-26 02:59:51 +01:00
godpow
41e15e95b1 [crypto] Rework AES CTR/XTS streaming and squash heap churn (#2782)
AES Updates:
Replaced heap churn with stack scratch buffers tail handling now stays in-place, no more recursive transcode detours.
CTR/XTS modes read in larger, aligned chunks and still handle odd offsets cleanly.
XTS prefetches a few sectors ahead to reduce extra reads.
AesCtrStorage writer now uses the pooled buffer properly one stack slab, chunk forward, bump counter, repeat.
Result: less malloc noise, fewer watchdog spikes at startup (though mbedtls still sets the pace).
This should make the loading speed slightly better than before. Make sure to test.

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/2782
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
Reviewed-by: crueter <crueter@eden-emu.dev>
Co-authored-by: godpow <thesaviorsrule@yahoo.com>
Co-committed-by: godpow <thesaviorsrule@yahoo.com>
2025-10-26 02:12:27 +01:00
MaranBr
73ebf59af7 [video_core] Improve texture rendering (#2830)
This fixes some edge cases and improves texture rendering, bringing it closer to specifications. This fixes many assertions that occur in some games, such as EOW.

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/2830
Reviewed-by: Maufeat <sahyno1996@gmail.com>
Co-authored-by: MaranBr <maranbr@outlook.com>
Co-committed-by: MaranBr <maranbr@outlook.com>
2025-10-26 02:14:45 +02:00
Bix
53bfd56b70 [Frontend] Added data for in emulator compatibility list (#2784)
This update restores the in emu compatibility list to Eden. I’ve moved it in-tree since it’s only a few kilobytes in size. If you’d prefer it to be hosted in a separate repository, let me know or feel free to move it yourself.

The compatibility list currently includes only a limited selection of games, so contributions are welcome and encouraged.
Authored-by: Bix <bix@bixed.xyz>
Signed-off-by: Bix <bix@bixed.xyz>

Co-authored-by: the great timothy IV <bix@X1Carbon.home>
Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/2784
Reviewed-by: Lizzie <lizzie@eden-emu.dev>
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
Co-authored-by: Bix <bix@bixed.xyz>
Co-committed-by: Bix <bix@bixed.xyz>
2025-10-26 02:14:34 +02:00
Ribbit
6ba25b6cc0 [vk] Correct polygon draw topology mapping for line and point modes (#2834)
Co-authored-by: Ribbit <ribbit@placeholder.com>
Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/2834
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
Reviewed-by: crueter <crueter@eden-emu.dev>
Co-authored-by: Ribbit <ribbit@eden-emu.dev>
Co-committed-by: Ribbit <ribbit@eden-emu.dev>
2025-10-26 01:38:08 +02:00
crueter
8d565d7793 [qt] fix Discord RPC by using httplib (#2825)
Commit `a079a93645a7219ada1bef84f476eb8269614f5b` inexplicably replaced
the httplib implementation of discord_impl.cpp with an inferior
Qt::Network version. This causes a lot of issues especially w.r.t CA
certs which are handled differently with bundled OpenSSL. Thus, this
just adds back the httplib implementation and makes discord RPC work
again.

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

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/2825
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
2025-10-25 07:28:08 +02:00
Caio Oliveira
311c71146d android: Duplicate string (#2833)
Signed-off-by: Caio Oliveira <caiooliveirafarias0@gmail.com>
Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/2833
Co-authored-by: Caio Oliveira <caiooliveirafarias0@gmail.com>
Co-committed-by: Caio Oliveira <caiooliveirafarias0@gmail.com>
2025-10-25 03:26:33 +02:00
lizzie
7751f86c1b [vk] Disable EDS on Mesa Intel integrated UHD drivers (#2788)
Affects specifically mesa 22.3.6-1 with an iGPU of UHD 620.

Signed-off-by: lizzie <lizzie@eden-emu.dev>
Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/2788
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
Reviewed-by: crueter <crueter@eden-emu.dev>
Co-authored-by: lizzie <lizzie@eden-emu.dev>
Co-committed-by: lizzie <lizzie@eden-emu.dev>
2025-10-24 22:21:01 +02:00
lizzie
4834fec159 [audio] Fix BOTW by increasing ring-size (#2822)
Signed-off-by: lizzie <lizzie@eden-emu.dev>
Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/2822
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
Reviewed-by: Maufeat <sahyno1996@gmail.com>
Co-authored-by: lizzie <lizzie@eden-emu.dev>
Co-committed-by: lizzie <lizzie@eden-emu.dev>
2025-10-24 12:38:42 +02:00
JPikachu
b5d54b8df7 [acc, ldn] Implement CreateClientProcessMonitor and stub LoadIdTokenCache changes (#2810)
- IManagerForApplication/SystemService: from 19.0.0+ LoadIdTokenCache (3) is now LoadIdTokenCacheDeprecated (3)
- IManagerForApplication/SystemService: LoadIdTokenCache (4) stubbed
- Implement CreateClientProcessMonitor to ldn:s and ldn:u
- Create new client_process_monitor.cpp/.h files
- Change non-domain (C) functions into domain (D) functions to fix crashes

Thanks to SwitchBrew documentation, fixes 'Pokemon: Legends Z-A' LDN issues.

Co-authored-by: JPikachu <jpikachu.eden@gmail.com>
Co-authored-by: unknown <sahyno1996@gmail.com>
Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/2810
Reviewed-by: Maufeat <sahyno1996@gmail.com>
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
Co-authored-by: JPikachu <jpikachu@eden-emu.dev>
Co-committed-by: JPikachu <jpikachu@eden-emu.dev>
2025-10-24 12:34:49 +02:00
MaranBr
cd4bcb91cc [video_core] Better error messages (#2827)
Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/2827
Reviewed-by: Maufeat <sahyno1996@gmail.com>
Reviewed-by: Lizzie <lizzie@eden-emu.dev>
Co-authored-by: MaranBr <maranbr@outlook.com>
Co-committed-by: MaranBr <maranbr@outlook.com>
2025-10-24 11:35:33 +02:00
unknown
199bc6a170 [texture_cache] Fix WIN32 #ifdef for texture_cache (#2823)
The #ifdef introduced in #2720 was wrong

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/2823
Co-authored-by: unknown <sahyno1996@gmail.com>
Co-committed-by: unknown <sahyno1996@gmail.com>
2025-10-23 08:12:24 +02:00
264 changed files with 4553 additions and 13181 deletions

View File

@@ -4,7 +4,7 @@
# SPDX-License-Identifier: GPL-3.0-or-later
# specify full path if dupes may exist
EXCLUDE_FILES="CPM.cmake CPMUtil.cmake GetSCMRev.cmake sse2neon.h renderdoc_app.h tools/cpm tools/shellcheck.sh tools/update-cpm.sh externals/stb externals/glad externals/getopt externals/gamemode externals/FidelityFX-FSR externals/demangle externals/bc_decoder"
EXCLUDE_FILES="CPM.cmake CPMUtil.cmake GetSCMRev.cmake renderdoc_app.h tools/cpm tools/shellcheck.sh tools/update-cpm.sh externals/stb externals/glad externals/getopt externals/gamemode externals/FidelityFX-FSR externals/demangle externals/bc_decoder"
# license header constants, please change when needed :))))
YEAR=2025

View File

@@ -1,60 +0,0 @@
# SPDX-FileCopyrightText: 2025 Eden Emulator Project
# SPDX-License-Identifier: GPL-3.0-or-later
$ErrorActionPreference = "Stop"
# Check if running as administrator
if (-not ([bool](net session 2>$null))) {
Write-Host "This script must be run with administrator privileges!"
Exit 1
}
$VSVer = "17"
$ExeFile = "vs_community.exe"
$Uri = "https://aka.ms/vs/$VSVer/release/$ExeFile"
$Destination = "./$ExeFile"
Write-Host "Downloading Visual Studio Build Tools from $Uri"
$WebClient = New-Object System.Net.WebClient
$WebClient.DownloadFile($Uri, $Destination)
Write-Host "Finished downloading $ExeFile"
$Arguments = @(
"--quiet", # Suppress installer UI
"--wait", # Wait for installation to complete
"--norestart", # Prevent automatic restart
"--force", # Force installation even if components are already installed
"--add Microsoft.VisualStudio.Workload.NativeDesktop", # Desktop development with C++
"--add Microsoft.VisualStudio.Component.VC.Tools.x86.x64", # Core C++ compiler/tools for x86/x64
"--add Microsoft.VisualStudio.Component.Windows11SDK.26100",# Windows 11 SDK (26100)
"--add Microsoft.VisualStudio.Component.Windows10SDK.19041",# Windows 10 SDK (19041)
"--add Microsoft.VisualStudio.Component.VC.Llvm.Clang", # LLVM Clang compiler
"--add Microsoft.VisualStudio.Component.VC.Llvm.ClangToolset", # LLVM Clang integration toolset
"--add Microsoft.VisualStudio.Component.Windows11SDK.22621",# Windows 11 SDK (22621)
"--add Microsoft.VisualStudio.Component.VC.CMake.Project", # CMake project support
"--add Microsoft.VisualStudio.ComponentGroup.VC.Tools.142.x86.x64", # VC++ 14.2 toolset
"--add Microsoft.VisualStudio.ComponentGroup.NativeDesktop.Llvm.Clang" # LLVM Clang for native desktop
)
Write-Host "Installing Visual Studio Build Tools"
$InstallProcess = Start-Process -FilePath $Destination -NoNewWindow -PassThru -ArgumentList $Arguments
# Spinner while installing
$Spinner = "|/-\"
$i = 0
while (-not $InstallProcess.HasExited) {
Write-Host -NoNewline ("`rInstalling... " + $Spinner[$i % $Spinner.Length])
Start-Sleep -Milliseconds 250
$i++
}
# Clear spinner line
Write-Host "`rSetup completed! "
$ExitCode = $InstallProcess.ExitCode
if ($ExitCode -ne 0) {
Write-Host "Error installing Visual Studio Build Tools (Error: $ExitCode)"
Exit $ExitCode
}
Write-Host "Finished installing Visual Studio Build Tools"

View File

@@ -1,152 +0,0 @@
# TODO: This document needs to be formatted,
# some stuff needs cleaned up etc
name: eden-build
#on:
# push:
# branches: [ "master" ]
# TODO: combine build.yml into trigger_release.yml
jobs:
source:
if: ${{ !github.head_ref }}
runs-on: source
steps:
- uses: actions/checkout@v4
with:
submodules: recursive
- name: Pack
run: ./.ci/source.sh
- name: Upload
uses: forgejo/upload-artifact@v4
with:
retention-days: 2
name: source.zip
path: artifacts/
windows:
runs-on: windows
strategy:
matrix:
target: ["msvc"] # TODO: Add msys2
defaults:
run:
shell: ${{ (matrix.target == 'msys2' && 'msys2') || 'bash' }} {0}
env:
CCACHE_DIR: ${{ runner.workspace }}/.cache/.ccache
CCACHE_COMPILERCHECK: content
CCACHE_SLOPPINESS: time_macros
OS: windows
TARGET: ${{ matrix.target }}
steps:
- uses: actions/checkout@v4
with:
submodules: recursive
fetch-depth: 0
fetch-tags: true
- name: Set up vcpkg cache
uses: actions/cache@v4
with:
path: |
${{ github.workspace }}/build/vcpkg_installed
${{ github.workspace }}/build/externals
${{ github.workspace }}/.vcpkg
key: ${{ runner.os }}-${{ matrix.target }}-vcpkg-${{ hashFiles('**/CMakeLists.txt', '**/vcpkg.json') }}-${{ github.run_id }}
restore-keys: |
${{ runner.os }}-${{ matrix.target }}-vcpkg-
- name: Set up MSVC
uses: https://github.com/ilammy/msvc-dev-cmd@v1
if: ${{ matrix.target == 'msvc' }}
- name: Cygwin with autoconf # NEEDED FOR LIBUSB
shell: cmd
run: ./.ci/windows/cygwin.bat
- name: Configure & Build
shell: bash
run: |
./.ci/windows/qt-envvars.sh
DEVEL=true WINDEPLOYQT="/c/Qt/6.9.0/msvc2022_64/bin/windeployqt6.exe" .ci/windows/build.sh -DCMAKE_PREFIX_PATH=C:/Qt/6.9.0/msvc2022_64/lib/cmake/Qt6
- name: Package artifacts
shell: bash
run: |
./.ci/windows/qt-envvars.sh
./.ci/windows/package.sh
- name: Upload Windows artifacts
uses: forgejo/upload-artifact@v4
with:
retention-days: 2
name: ${{ matrix.target }}.zip
path: artifacts/*
linux:
runs-on: linux
env:
CCACHE_DIR: /home/runner/.cache/ccache
CCACHE_COMPILERCHECK: content
CCACHE_SLOPPINESS: time_macros
OS: linux
TARGET: fresh
steps:
- uses: actions/checkout@v4
with:
submodules: recursive
fetch-depth: 0
fetch-tags: true
- name: Build
run: TARGET=appimage DEVEL=true ./.ci/linux/build.sh
- name: Package AppImage
run: DEVEL=true ./.ci/linux/package.sh &> /dev/null
- name: Upload Linux artifacts
uses: forgejo/upload-artifact@v4
with:
retention-days: 3
name: linux.zip
path: ./*.AppImage
android:
runs-on: android
env:
OS: android
TARGET: universal
steps:
- uses: actions/checkout@v4
with:
submodules: recursive
fetch-depth: 0
fetch-tags: true
- name: Set tag name
run: |
if [[ "$GITHUB_REF_TYPE" == "tag" ]]; then
echo "GIT_TAG_NAME=$GITHUB_REF_NAME" >> $GITHUB_ENV
fi
echo $GIT_TAG_NAME
- name: Build
run: ANDROID_HOME=/home/runner/sdk ./.ci/android/build.sh
env:
ANDROID_KEYSTORE_B64: ${{ secrets.ANDROID_KEYSTORE_B64 }}
ANDROID_KEY_ALIAS: ${{ secrets.ANDROID_KEY_ALIAS }}
ANDROID_KEYSTORE_PASS: ${{ secrets.ANDROID_KEYSTORE_PASS }}
- name: Package Android artifacts
run: ./.ci/android/package.sh
- name: Upload Android artifacts
uses: forgejo/upload-artifact@v4
with:
retention-days: 2
name: android.zip
path: artifacts/*

View File

@@ -1,203 +0,0 @@
name: Build Application and Make Release
#on:
# push:
# tags: [ "*" ]
permissions:
contents: write
jobs:
source:
runs-on: source
steps:
- uses: actions/checkout@v4
with:
submodules: recursive
- name: Pack
run: ./.ci/source.sh
- name: Upload
uses: forgejo/upload-artifact@v4
with:
retention-days: 2
name: source.zip
path: artifacts/
windows:
runs-on: windows
strategy:
matrix:
target: ["msvc"] # TODO: Add msys2
defaults:
run:
shell: ${{ (matrix.target == 'msys2' && 'msys2') || 'bash' }} {0}
env:
CCACHE_DIR: ${{ runner.workspace }}/.cache/.ccache
CCACHE_COMPILERCHECK: content
CCACHE_SLOPPINESS: time_macros
OS: windows
TARGET: ${{ matrix.target }}
steps:
- uses: actions/checkout@v4
with:
submodules: recursive
fetch-depth: 0
fetch-tags: true
- name: Set up vcpkg cache
uses: actions/cache@v4
with:
path: |
${{ github.workspace }}/build/vcpkg_installed
${{ github.workspace }}/build/externals
${{ github.workspace }}/.vcpkg
key: ${{ runner.os }}-${{ matrix.target }}-vcpkg-${{ hashFiles('**/CMakeLists.txt', '**/vcpkg.json') }}-${{ github.run_id }}
restore-keys: |
${{ runner.os }}-${{ matrix.target }}-vcpkg-
- name: Set up MSVC
uses: https://github.com/ilammy/msvc-dev-cmd@v1
if: ${{ matrix.target == 'msvc' }}
- name: Cygwin with autoconf # NEEDED FOR LIBUSB
shell: cmd
run: ./.ci/windows/cygwin.bat
- name: Configure & Build
shell: bash
run: DEVEL=false ./.ci/windows/build.sh
- name: Package artifacts
shell: bash
run: |
export PATH="${PATH}:/c/Qt/6.9.0/msvc2022_64/bin"
./.ci/windows/package.sh
- name: Upload Windows artifacts
uses: forgejo/upload-artifact@v4
with:
retention-days: 2
name: ${{ matrix.target }}.zip
path: artifacts/*
linux:
runs-on: linux
env:
CCACHE_DIR: /home/runner/.cache/ccache
CCACHE_COMPILERCHECK: content
CCACHE_SLOPPINESS: time_macros
OS: linux
TARGET: fresh
steps:
- uses: actions/checkout@v4
with:
submodules: recursive
fetch-depth: 0
fetch-tags: true
- name: Build
run: TARGET=appimage RELEASE=1 DEVEL=false ./.ci/linux/build.sh v3 8
- name: Package AppImage
run: ./.ci/linux/package.sh v3 &> /dev/null
- name: Upload Linux artifacts
uses: forgejo/upload-artifact@v4
with:
retention-days: 2
name: linux.zip
path: ./*.AppImage*
android:
runs-on: android
env:
CCACHE_DIR: /home/runner/.cache/ccache
CCACHE_COMPILERCHECK: content
CCACHE_SLOPPINESS: time_macros
OS: android
TARGET: universal
steps:
- uses: actions/checkout@v4
with:
submodules: recursive
fetch-depth: 0
fetch-tags: true
- name: Set tag name
run: |
if [[ "$GITHUB_REF_TYPE" == "tag" ]]; then
echo "GIT_TAG_NAME=$GITHUB_REF_NAME" >> $GITHUB_ENV
fi
echo $GIT_TAG_NAME
- name: Build
run: DEVEL=false ANDROID_HOME=/home/runner/sdk ./.ci/android/build.sh
env:
ANDROID_KEYSTORE_B64: ${{ secrets.ANDROID_KEYSTORE_B64 }}
ANDROID_KEY_ALIAS: ${{ secrets.ANDROID_KEY_ALIAS }}
ANDROID_KEYSTORE_PASS: ${{ secrets.ANDROID_KEYSTORE_PASS }}
- name: Package Android artifacts
run: ./.ci/android/package.sh
- name: Upload Android artifacts
uses: forgejo/upload-artifact@v4
with:
retention-days: 2
name: android.zip
path: artifacts/*
create_release:
needs: [linux, windows, android]
runs-on: linux
outputs:
upload_url: ${{ steps.create_release.outputs.upload_url }}
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
submodules: 'recursive'
path: 'eden-source'
- name: Download artifacts
uses: forgejo/download-artifact@v4
with:
path: artifacts
- name: Grab and store version
run: |
cd eden-source
tag_name=$(git describe --tags --abbrev=0)
echo "VERSION=$tag_name" >> $GITHUB_ENV
echo $tag_name
- name: Package artifacts properly
shell: bash
run: |
set -ex
mv ${{ github.workspace }}/eden-source eden-${{ env.VERSION }}
cd artifacts
ls *.zip
mkdir -p dist
cp linux.zip/Eden-*.AppImage dist/Eden-Linux-${{ env.VERSION }}-amd64.AppImage
cp linux.zip/Eden-*.AppImage.zsync dist/Eden-Linux-${{ env.VERSION }}-amd64.AppImage.zsync
cp msvc.zip/eden-windows-msvc*.zip dist/Eden-Windows-MSVC-${{ env.VERSION }}-amd64.zip
cp android.zip/eden-android*.apk dist/Eden-Android-${{ env.VERSION }}.apk
cp android.zip/eden-android*.aab dist/Eden-Android-${{ env.VERSION }}.aab
cp source.zip/eden-unified-source*.tar.xz dist/Eden-Source-${{ env.VERSION }}.tar.xz
cp source.zip/eden-unified-source*.tar.xz.sha256sum dist/Eden-Source-${{ env.VERSION }}.tar.xz.sha256sum
- name: Create release
id: create_release
uses: actions/forgejo-release@v2.6.0
with:
direction: upload
tag: ${{ env.VERSION }}
title: Eden ${{ env.VERSION }}
release-dir: artifacts/dist/

View File

@@ -0,0 +1,52 @@
From e1a946ffb79022d38351a0623f819a5419965c3e Mon Sep 17 00:00:00 2001
From: crueter <crueter@eden-emu.dev>
Date: Fri, 24 Oct 2025 23:41:09 -0700
Subject: [PATCH] [build] Fix MinGW missing GetAddrInfoExCancel definition
MinGW does not define GetAddrInfoExCancel in its wstcpi whatever header,
so to get around this we can just load it with GetProcAddress et al.
Signed-off-by: crueter <crueter@eden-emu.dev>
---
httplib.h | 14 ++++++++++++--
1 file changed, 12 insertions(+), 2 deletions(-)
diff --git a/httplib.h b/httplib.h
index e15ba44..90a76dc 100644
--- a/httplib.h
+++ b/httplib.h
@@ -203,11 +203,13 @@
#error Sorry, Visual Studio versions prior to 2015 are not supported
#endif
-#pragma comment(lib, "ws2_32.lib")
-
using ssize_t = __int64;
#endif // _MSC_VER
+#if defined(_MSC_VER) || defined(__MINGW32__)
+#pragma comment(lib, "ws2_32.lib")
+#endif
+
#ifndef S_ISREG
#define S_ISREG(m) (((m) & S_IFREG) == S_IFREG)
#endif // S_ISREG
@@ -3557,7 +3559,15 @@ inline int getaddrinfo_with_timeout(const char *node, const char *service,
auto wait_result =
::WaitForSingleObject(event, static_cast<DWORD>(timeout_sec * 1000));
if (wait_result == WAIT_TIMEOUT) {
+#ifdef __MINGW32__
+ typedef INT (WSAAPI *PFN_GETADDRINFOEXCANCEL)(HANDLE *CancelHandle);
+ auto wsdll = LoadLibraryW((wchar_t*) "ws2_32.lib");
+ PFN_GETADDRINFOEXCANCEL GetAddrInfoExCancel = (PFN_GETADDRINFOEXCANCEL) GetProcAddress(wsdll, "GetAddrInfoExCancel");
+
+ if (cancel_handle) { GetAddrInfoExCancel(&cancel_handle); }
+#else
if (cancel_handle) { ::GetAddrInfoExCancel(&cancel_handle); }
+#endif
::CloseHandle(event);
return EAI_AGAIN;
}
--
2.51.0

View File

@@ -212,11 +212,6 @@ cmake_dependent_option(USE_DISCORD_PRESENCE "Enables Discord Rich Presence" OFF
option(YUZU_TESTS "Compile tests" "${BUILD_TESTING}")
option(YUZU_USE_PRECOMPILED_HEADERS "Use precompiled headers" OFF)
if (YUZU_USE_PRECOMPILED_HEADERS)
message(STATUS "Using Precompiled Headers.")
set(CMAKE_PCH_INSTANTIATE_TEMPLATES ON)
endif()
option(YUZU_ENABLE_LTO "Enable link-time optimization" OFF)
if(YUZU_ENABLE_LTO)
include(CheckIPOSupported)
@@ -235,11 +230,8 @@ if(USE_CCACHE)
message(STATUS "Found ccache at: ${CCACHE_BINARY}")
set(CMAKE_C_COMPILER_LAUNCHER ${CCACHE_BINARY})
set(CMAKE_CXX_COMPILER_LAUNCHER ${CCACHE_BINARY})
if (YUZU_USE_PRECOMPILED_HEADERS)
message(FATAL_ERROR "Precompiled headers are incompatible with ccache. Re-run CMake with -DYUZU_USE_PRECOMPILED_HEADERS=OFF.")
endif()
else()
message(WARNING "USE_CCACHE enabled, but no executable found at: ${CCACHE_PATH}")
message(FATAL_ERROR "USE_CCACHE enabled, but no executable found at: ${CCACHE_PATH}")
endif()
endif()
@@ -352,10 +344,6 @@ if (NOT EXISTS ${PROJECT_BINARY_DIR}/dist/compatibility_list/compatibility_list.
file(WRITE ${PROJECT_BINARY_DIR}/dist/compatibility_list/compatibility_list.json "")
endif()
if (UNIX)
add_compile_definitions(YUZU_UNIX=1)
endif()
if (YUZU_LEGACY)
message(WARNING "Making legacy build. Performance may suffer.")
add_compile_definitions(YUZU_LEGACY)
@@ -385,6 +373,8 @@ endif()
# Build/optimization presets
if (PLATFORM_LINUX OR CXX_CLANG)
if (ARCHITECTURE_x86_64)
# See https://gcc.gnu.org/onlinedocs/gcc/x86-Options.html
# Generic supports Pentium Pro instruction set and above
set(YUZU_BUILD_PRESET "custom" CACHE STRING "Build preset to use. One of: custom, generic, v3, zen2, zen4, native")
if (${YUZU_BUILD_PRESET} STREQUAL "generic")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -march=x86-64 -mtune=generic")
@@ -403,13 +393,17 @@ if (PLATFORM_LINUX OR CXX_CLANG)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -march=native -mtune=native")
endif()
elseif(ARCHITECTURE_arm64)
set(YUZU_BUILD_PRESET "custom" CACHE STRING "Build preset to use. One of: custom, generic, armv9")
# See https://gcc.gnu.org/onlinedocs/gcc/AArch64-Options.html
set(YUZU_BUILD_PRESET "custom" CACHE STRING "Build preset to use. One of: custom, generic, armv9, native")
if (${YUZU_BUILD_PRESET} STREQUAL "generic")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -march=armv8-a -mtune=generic")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -march=armv8-a -mtune=generic")
elseif (${YUZU_BUILD_PRESET} STREQUAL "armv9")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -march=armv9-a -mtune=generic")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -march=armv9-a -mtune=generic")
elseif (${YUZU_BUILD_PRESET} STREQUAL "native")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -march=native -mtune=native")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -march=native -mtune=native")
endif()
endif()
endif()
@@ -579,6 +573,35 @@ function(create_target_directory_groups target_name)
endforeach()
endfunction()
# Platform-specific library requirements
# Put these BEFORE EXTERNALS or Boost WILL die
# =============================================
if (APPLE)
# Umbrella framework for everything GUI-related
find_library(COCOA_LIBRARY Cocoa)
set(PLATFORM_LIBRARIES ${COCOA_LIBRARY} ${IOKIT_LIBRARY} ${COREVIDEO_LIBRARY})
find_library(ICONV_LIBRARY iconv REQUIRED)
list(APPEND PLATFORM_LIBRARIES ${ICONV_LIBRARY})
elseif (WIN32)
# Target Windows 10
add_compile_definitions(_WIN32_WINNT=0x0A00 WINVER=0x0A00)
set(PLATFORM_LIBRARIES winmm ws2_32 iphlpapi)
if (MINGW)
# PSAPI is the Process Status API
set(PLATFORM_LIBRARIES ${PLATFORM_LIBRARIES} psapi imm32 version)
endif()
elseif (PLATFORM_HAIKU)
# Haiku is so special :)
# Some fucking genius decided to name an entire module "network" in 2019
# this caused great disaster amongst the Haiku community who had came first with
# their "libnetwork.so"; since CMake doesn't do magic, we have to use an ABSOLUTE PATH
# to the library itself, otherwise it will think we are linking to... our network thing
set(PLATFORM_LIBRARIES bsd /boot/system/lib/libnetwork.so)
elseif (CMAKE_SYSTEM_NAME MATCHES "^(Linux|kFreeBSD|GNU|SunOS)$")
set(PLATFORM_LIBRARIES rt)
endif()
add_subdirectory(externals)
# pass targets from externals
@@ -687,12 +710,17 @@ function(set_yuzu_qt_components)
if (ENABLE_QT_TRANSLATION)
list(APPEND YUZU_QT_COMPONENTS2 LinguistTools)
endif()
if (USE_DISCORD_PRESENCE)
list(APPEND YUZU_QT_COMPONENTS2 Network)
endif()
set(YUZU_QT_COMPONENTS ${YUZU_QT_COMPONENTS2} PARENT_SCOPE)
endfunction(set_yuzu_qt_components)
if(ENABLE_QT)
set_yuzu_qt_components()
find_package(Qt6 REQUIRED COMPONENTS ${YUZU_QT_COMPONENTS})
set(QT_MAJOR_VERSION 6)
# Qt6 sets cxx_std_17 and we need to undo that
set_target_properties(Qt6::Platform PROPERTIES INTERFACE_COMPILE_FEATURES "")
endif()
if (UNIX AND NOT APPLE AND NOT ANDROID)
find_package(PkgConfig REQUIRED)
pkg_check_modules(LIBVA libva)
@@ -708,14 +736,6 @@ if (NOT (YUZU_USE_BUNDLED_FFMPEG OR YUZU_USE_EXTERNAL_FFMPEG))
set_property(GLOBAL APPEND PROPERTY CPM_PACKAGE_URLS "https://github.com/FFmpeg/FFmpeg")
endif()
if(ENABLE_QT)
set_yuzu_qt_components()
find_package(Qt6 REQUIRED COMPONENTS ${YUZU_QT_COMPONENTS})
set(QT_MAJOR_VERSION 6)
# Qt6 sets cxx_std_17 and we need to undo that
set_target_properties(Qt6::Platform PROPERTIES INTERFACE_COMPILE_FEATURES "")
endif()
if (WIN32 AND YUZU_CRASH_DUMPS)
set(BREAKPAD_VER "breakpad-c89f9dd")
download_bundled_external("breakpad/" ${BREAKPAD_VER} "breakpad-win" BREAKPAD_PREFIX "c89f9dd")
@@ -732,34 +752,6 @@ endif()
set(THREADS_PREFER_PTHREAD_FLAG ON)
find_package(Threads REQUIRED)
# Platform-specific library requirements
# ======================================
if (APPLE)
# Umbrella framework for everything GUI-related
find_library(COCOA_LIBRARY Cocoa)
set(PLATFORM_LIBRARIES ${COCOA_LIBRARY} ${IOKIT_LIBRARY} ${COREVIDEO_LIBRARY})
find_library(ICONV_LIBRARY iconv REQUIRED)
list(APPEND PLATFORM_LIBRARIES ${ICONV_LIBRARY})
elseif (WIN32)
# Target Windows 10
add_compile_definitions(_WIN32_WINNT=0x0A00 WINVER=0x0A00)
set(PLATFORM_LIBRARIES winmm ws2_32 iphlpapi)
if (MINGW)
# PSAPI is the Process Status API
set(PLATFORM_LIBRARIES ${PLATFORM_LIBRARIES} psapi imm32 version)
endif()
elseif (PLATFORM_HAIKU)
# Haiku is so special :)
# Some fucking genius decided to name an entire module "network" in 2019
# this caused great disaster amongst the Haiku community who had came first with
# their "libnetwork.so"; since CMake doesn't do magic, we have to use an ABSOLUTE PATH
# to the library itself, otherwise it will think we are linking to... our network thing
set(PLATFORM_LIBRARIES bsd /boot/system/lib/libnetwork.so)
elseif (CMAKE_SYSTEM_NAME MATCHES "^(Linux|kFreeBSD|GNU|SunOS)$")
set(PLATFORM_LIBRARIES rt)
endif()
# Setup a custom clang-format target (if clang-format can be found) that will run
# against all the src files. This should be used before making a pull request.
# =======================================================================

View File

@@ -1,3 +1,6 @@
# SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
# SPDX-License-Identifier: GPL-3.0-or-later
# SPDX-FileCopyrightText: 2022 yuzu Emulator Project
# SPDX-License-Identifier: GPL-2.0-or-later

View File

@@ -1,15 +0,0 @@
# SPDX-FileCopyrightText: 2022 yuzu Emulator Project
# SPDX-License-Identifier: GPL-3.0-or-later
# buildcache wrapper
OPTION(USE_CCACHE "Use buildcache for compilation" OFF)
IF(USE_CCACHE)
FIND_PROGRAM(CCACHE buildcache)
IF (CCACHE)
MESSAGE(STATUS "Using buildcache found in PATH")
SET_PROPERTY(GLOBAL PROPERTY RULE_LAUNCH_COMPILE ${CCACHE})
SET_PROPERTY(GLOBAL PROPERTY RULE_LAUNCH_LINK ${CCACHE})
ELSE(CCACHE)
MESSAGE(WARNING "USE_CCACHE enabled, but no buildcache executable found")
ENDIF(CCACHE)
ENDIF(USE_CCACHE)

View File

@@ -0,0 +1,354 @@
[
{
"compatibility": 0,
"directory": "the-legend-of-zelda-breath-of-the-wild",
"releases": [
{"id": "01007EF00011E000"}
],
"title": "The Legend of Zelda: Breath of the Wild"
},
{
"compatibility": 1,
"directory": "super-mario-odyssey",
"releases": [
{"id": "0100000000010000"}
],
"title": "Super Mario Odyssey"
},
{
"compatibility": 0,
"directory": "animal-crossing-new-horizons",
"releases": [
{"id": "01006F8002326000"}
],
"title": "Animal Crossing: New Horizons"
},
{
"compatibility": 1,
"directory": "pokemon-legends-z-a",
"releases": [
{"id": "0100F43008C44000"}
],
"title": "Pokémon Legends: Z-A"
},
{
"compatibility": 1,
"directory": "the-legend-of-zelda-tears-of-the-kingdom",
"releases": [
{"id": "0100F2C0115B6000"}
],
"title": "The Legend of Zelda: Tears of the Kingdom"
},
{
"compatibility": 0,
"directory": "super-mario-galaxy",
"releases": [
{"id": "010099C022B96000"}
],
"title": "Super Mario Galaxy"
},
{
"compatibility": 3,
"directory": "star-wars-republic-commando",
"releases": [
{"id": "0100FA10115F8000"}
],
"title": "Star Wars: Republic Commando"
},
{
"compatibility": 0,
"directory": "doki-doki-literature-club-plus",
"releases": [
{"id": "010086901543E000"}
],
"title": "Doki Doki Literature Club Plus"
},
{
"compatibility": 1,
"directory": "pokemon-scarlet",
"releases": [
{"id": "0100A3D008C5C000"}
],
"title": "Pokémon Scarlet"
},
{
"compatibility": 1,
"directory": "pokemon-violet",
"releases": [
{"id": "01008F6008C5E000"}
],
"title": "Pokémon Violet"
},
{
"compatibility": 2,
"directory": "pokemon-legends-arceus",
"releases": [
{"id": "01001E300D162000"}
],
"title": "Pokémon Legends: Arceus"
},
{
"compatibility": 0,
"directory": "splatoon-2",
"releases": [
{"id": "01003BC0000A0000"}
],
"title": "Splatoon 2"
},
{
"compatibility": 1,
"directory": "super-smash-bros-ultimate",
"releases": [
{"id": "01006A800016E000"}
],
"title": "Super Smash Bros. Ultimate"
},
{
"compatibility": 0,
"directory": "mario-kart-8-deluxe",
"releases": [
{"id": "0100152000022000"}
],
"title": "Mario Kart 8 Deluxe"
},
{
"compatibility": 0,
"directory": "splatoon-3",
"releases": [
{"id": "0100C2500FC20000"}
],
"title": "Splatoon 3"
},
{
"compatibility": 0,
"directory": "new-super-mario-bros-u-deluxe",
"releases": [
{"id": "0100EA80032EA000"}
],
"title": "New Super Mario Bros. U Deluxe"
},
{
"compatibility": 0,
"directory": "hyrule-warriors-age-of-calamity",
"releases": [
{"id": "01002B00111A2000"}
],
"title": "Hyrule Warriors: Age of Calamity"
},
{
"compatibility": 2,
"directory": "luigis-mansion-3",
"releases": [
{"id": "0100DCA0064A6000"}
],
"title": "Luigi's Mansion 3"
},
{
"compatibility": 2,
"directory": "pokemon-brilliant-diamond",
"releases": [
{"id": "0100000011D90000"}
],
"title": "Pokémon Brilliant Diamond"
},
{
"compatibility": 2,
"directory": "pokemon-shining-pearl",
"releases": [
{"id": "010018E011D92000"}
],
"title": "Pokémon Shining Pearl"
},
{
"compatibility": 1,
"directory": "super-mario-3d-world-bowsers-fury",
"releases": [
{"id": "010028600EBDA000"}
],
"title": "Super Mario 3D World + Bowser's Fury"
},
{
"compatibility": 0,
"directory": "the-legend-of-zelda-links-awakening",
"releases": [
{"id": "01006BB00C6F0000"}
],
"title": "The Legend of Zelda: Link's Awakening"
},
{
"compatibility": 1,
"directory": "fire-emblem-three-houses",
"releases": [
{"id": "010055D009F78000"}
],
"title": "Fire Emblem: Three Houses"
},
{
"compatibility": 2,
"directory": "metroid-dread",
"releases": [
{"id": "010093801237C000"}
],
"title": "Metroid Dread"
},
{
"compatibility": 0,
"directory": "paper-mario-the-origami-king",
"releases": [
{"id": "0100A3900C3E2000"}
],
"title": "Paper Mario: The Origami King"
},
{
"compatibility": 1,
"directory": "xenoblade-chronicles-definitive-edition",
"releases": [
{"id": "0100FF500E34A000"}
],
"title": "Xenoblade Chronicles: Definitive Edition"
},
{
"compatibility": 2,
"directory": "xenoblade-chronicles-3",
"releases": [
{"id": "010074F013262000"}
],
"title": "Xenoblade Chronicles 3"
},
{
"compatibility": 1,
"directory": "pikmin-3-deluxe",
"releases": [
{"id": "0100F8600D4B0000"}
],
"title": "Pikmin 3 Deluxe"
},
{
"compatibility": 0,
"directory": "donkey-kong-country-tropical-freeze",
"releases": [
{"id": "0100C1F0054B6000"}
],
"title": "Donkey Kong Country: Tropical Freeze"
},
{
"compatibility": 1,
"directory": "kirby-and-the-forgotten-land",
"releases": [
{"id": "01004D300C5AE000"}
],
"title": "Kirby and the Forgotten Land"
},
{
"compatibility": 2,
"directory": "mario-party-superstars",
"releases": [
{"id": "01006B400D8B2000"}
],
"title": "Mario Party Superstars"
},
{
"compatibility": 0,
"directory": "clubhouse-games-51-worldwide-classics",
"releases": [
{"id": "0100F8600D4B0000"}
],
"title": "Clubhouse Games: 51 Worldwide Classics"
},
{
"compatibility": 1,
"directory": "ring-fit-adventure",
"releases": [
{"id": "01006B300BAF8000"}
],
"title": "Ring Fit Adventure"
},
{
"compatibility": 2,
"directory": "arms",
"releases": [
{"id": "01009B500007C000"}
],
"title": "ARMS"
},
{
"compatibility": 0,
"directory": "super-mario-maker-2",
"releases": [
{"id": "01009B90006DC000"}
],
"title": "Super Mario Maker 2"
},
{
"compatibility": 0,
"directory": "pokemon-lets-go-pikachu",
"releases": [
{"id": "010003F003A34000"}
],
"title": "Pokémon: Let's Go, Pikachu!"
},
{
"compatibility": 1,
"directory": "pokemon-lets-go-eevee",
"releases": [
{"id": "0100187003A36000"}
],
"title": "Pokémon: Let's Go, Eevee!"
},
{
"compatibility": 2,
"directory": "pokemon-sword",
"releases": [
{"id": "0100ABF008968000"}
],
"title": "Pokémon Sword"
},
{
"compatibility": 2,
"directory": "pokemon-shield",
"releases": [
{"id": "01008DB008C2C000"}
],
"title": "Pokémon Shield"
},
{
"compatibility": 1,
"directory": "new-pokemon-snap",
"releases": [
{"id": "0100F4300C182000"}
],
"title": "New Pokémon Snap"
},
{
"compatibility": 0,
"directory": "mario-golf-super-rush",
"releases": [
{"id": "0100C9C00E25C000"}
],
"title": "Mario Golf: Super Rush"
},
{
"compatibility": 1,
"directory": "mario-tennis-aces",
"releases": [
{"id": "0100BDE00862A000"}
],
"title": "Mario Tennis Aces"
},
{
"compatibility": 2,
"directory": "wario-ware-get-it-together",
"releases": [
{"id": "0100563010F22000"}
],
"title": "WarioWare: Get It Together!"
},
{
"compatibility": 0,
"directory": "big-brain-academy-brain-vs-brain",
"releases": [
{"id": "0100190010F24000"}
],
"title": "Big Brain Academy: Brain vs. Brain"
}
]

View File

@@ -1,8 +1,3 @@
<!--
SPDX-FileCopyrightText: 2018 yuzu Emulator Project
SPDX-License-Identifier: GPL-2.0-or-later
-->
<RCC>
<qresource prefix="compatibility_list">
<file>compatibility_list.json</file>

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 9.2 KiB

After

Width:  |  Height:  |  Size: 13 KiB

BIN
dist/eden.ico vendored

Binary file not shown.

Before

Width:  |  Height:  |  Size: 316 KiB

After

Width:  |  Height:  |  Size: 403 KiB

BIN
dist/eden_named.ico vendored

Binary file not shown.

Before

Width:  |  Height:  |  Size: 54 KiB

117
dist/eden_named.svg vendored

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 4.9 MiB

After

Width:  |  Height:  |  Size: 39 KiB

1
dist/icon_variations/base.svg vendored Normal file

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 9.2 KiB

81
dist/icon_variations/base_named.svg vendored Normal file

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 37 KiB

106
dist/icon_variations/halloween2025.svg vendored Normal file
View File

@@ -0,0 +1,106 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
width="512"
height="512"
fill="none"
viewBox="0 0 512 512"
version="1.1"
id="svg7"
sodipodi:docname="dev.eden_emu.eden.svg"
inkscape:version="1.4.2 (ebf0e940d0, 2025-05-08)"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<defs
id="defs7" />
<sodipodi:namedview
id="namedview7"
pagecolor="#ffffff"
bordercolor="#000000"
borderopacity="0.25"
inkscape:showpageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
inkscape:zoom="1.25"
inkscape:cx="186.8"
inkscape:cy="210.4"
inkscape:window-width="1600"
inkscape:window-height="849"
inkscape:window-x="0"
inkscape:window-y="27"
inkscape:window-maximized="1"
inkscape:current-layer="svg7" />
<path
fill="#bf42f6"
d="M346.71 255.306c.788.006 1.577.013 3.124.059 4.006.027 7.253.015 10.501.003 33.391.022 66.783.028 100.175.078 8.174.013 16.349.279 24.522.209 3.084-.026 4.596.75 4.356 4.156-.293 4.157-.31 8.343-.259 12.513.049 3.964-1.248 5.539-5.679 5.524-48.85-.158-97.701-.071-146.552-.056-22.107.007-44.214 0-66.321 0h-5.43v7.117h223.011c-1.553 8.254-2.894 15.76-4.475 23.215-.17.802-1.666 1.617-2.685 1.87-1.312.327-2.763.099-4.155.099H265.099v6.701h216.666c-1.609 5.425-2.887 10.074-4.367 14.658-2.758 8.54-2.807 8.524-11.564 8.524-64.929 0-129.859.014-194.788-.04-3.401-.003-6.19.251-4.758 4.652.349 1.075 1.875 2.026 3.071 2.491 1.075.418 2.439.092 3.676.092l191.542.001h7.17c-3.533 7.29-6.399 13.72-9.834 19.83-.651 1.157-3.562 1.448-5.432 1.454-27.981.088-55.963.061-83.944.061-31.692 0-63.384.005-95.075-.008-2.61-.002-5.219-.124-8.53-.021-3.11.173-5.518.177-7.926.181-.849-7.032-2.289-14.051-2.44-21.098-.547-25.616-.773-51.242-.844-76.864-.014-4.88 1.228-9.763 2.52-14.927 2.88-.296 5.132-.31 7.81-.259 22.492.021 44.557-.024 67.034-.066 4.149-.049 7.886-.099 11.624-.149"
id="path1"
style="fill:#fea265;fill-opacity:1" />
<path
fill="#ff44c4"
d="M168.124 257.114v-9.301c-2.407 0-4.67-.001-6.933.001-44.522.025-89.044.01-133.566.146-3.858.012-5.358-1.03-4.744-5 .683-4.418 1.254-8.885 1.393-13.346.106-3.386 1.603-4.005 4.608-4.012 45.9-.11 91.801-.344 137.702-.452 3.865-.009 6.842-.344 6.032-5.831H25.546c1.39-6.599 2.354-12.681 4.138-18.513.413-1.35 3.628-2.615 5.555-2.618 47.458-.084 94.917-.01 142.375.109 3.275.008 5.855-.38 6.138-5.045H31.147c3.43-9.311 6.348-17.71 9.74-25.913.483-1.166 3.265-1.942 4.987-1.95 24.582-.124 49.165-.21 73.745.014 5.283.048 8.073-2.583 11.128-7.248h-86.45c4.35-7.835 8.002-14.753 12.077-21.412.67-1.096 3.197-1.473 4.868-1.478 30.611-.083 61.226-.291 91.832.113 7.185.094 12.719-2.619 18.321-6.774H61.385c2.592-3.805 4.355-6.546 6.268-9.178 8.88-12.216 8.896-12.209 24.069-12.21q60.991-.002 121.982-.001h5.885c-3.968-4.128-7.007-6.966-12.321-6.944-38.959.165-77.92.09-116.88.09H85.03l-.741-1.31c3.832-3.852 7.564-7.81 11.514-11.537 11.456-10.807 11.495-10.996 27.246-10.717 23.209.411 45.103 6.8 66.234 15.885 5.657 2.432 10.56 3.715 16.604.944 4.726-2.166 10.217-2.664 16.034-3.521-4.411 8.139.887 12.041 5.52 16.414 5.73 5.408 11.223 11.07 16.751 16.688 1.165 1.185 2.035 2.659 3.685 4.857-2.256.128-3.665.274-5.075.275-9.739.008-19.489.286-29.214-.085-28.045-1.07-51.592 9.244-71.602 28.209-7.152 6.778-13.525 14.379-20.212 21.643-.683.742-1.079 1.748-2.209 3.632 33.94-11.926 65.876-27.377 101.589-28.637l.859 1.395c-4.228 4.943-8.188 10.148-12.73 14.783-18.091 18.461-28.905 40.673-33.892 65.773-1.402 7.057-1.306 14.412-2.504 21.941-1.992.248-3.377.184-4.762.121m19.995-91.726c3.865.004 7.763-.315 11.585.09 5.562.589 8.92-2.178 12.525-7.52-9.134 2.152-17.144 4.039-25.139 5.988-.008.002.209.928 1.029 1.442"
id="path2"
style="fill:#ff7f2a;fill-opacity:1" />
<path
fill="#bf43f5"
d="M167.719 257.136c1.79.041 3.175.105 4.979.185.569 3.091.719 6.166.87 9.24l1.623.577c2.3-3.392 4.599-6.785 7.467-10.458 2.456-.382 4.343-.483 6.573-.598 1.405-.133 2.466-.342 3.53-.356 12.08-.162 24.16-.295 36.693-.368 3.463.111 6.473.155 9.484.2-1.12 6.677-2.306 13.345-3.347 20.035-4.715 30.285-5.404 60.697-3.186 91.651-2.469.746-4.517 1.048-6.565 1.35-56.117-.025-112.236-.135-168.353.061-5.334.018-8.298-1.559-10.185-6.415-1.874-4.824-4.295-9.436-6.907-15.069h17.147c53.955 0 107.909.013 161.863-.044 2.026-.002 4.864 1.218 5.327-2.599.469-3.862-1.271-4.625-4.781-4.616-38.803.103-77.607.061-116.411.068-20.405.003-40.811-.065-61.215.125-3.545.033-5.36-1.044-6.324-4.419-1.642-5.75-3.577-11.416-5.879-18.659 65.363-.259 129.677-.027 194.424-.144l.372-6.533c-1.901-.099-3.398-.244-4.896-.245-58.283-.032-116.565-.056-174.848-.06-4.48 0-8.964.279-13.439.163-1.138-.029-3.078-.897-3.247-1.681-1.676-7.766-3.056-15.595-4.624-23.961 8.433 0 15.808-.031 23.182.02 1.998.014 3.995.4 5.992.401 55.964.023 111.928.02 167.892.017 6.075 0 6.307-.256 6.266-7.198H23.431c-.284-6.643-.72-12.739-.592-18.823.016-.786 2.94-2.144 4.53-2.165 15.149-.197 30.3-.177 45.45-.191 17.623-.016 35.248-.072 52.871.018 13.876.07 27.75.335 42.029.491"
id="path3"
style="fill:#fea265;fill-opacity:1" />
<path
fill="#985ded"
d="M225.868 369.021c2.02-.729 4.068-1.031 6.551-1.351 2.672 17.851 4.169 35.853 7.334 53.556 4.047 22.64 9.785 44.94 18.63 66.298.29.699.498 1.433.742 2.142-25.39 10.452-124.622-22.609-146.929-49.453h122.669c.302-4.81-.85-6.915-5.85-6.891-39.883.193-79.768.055-119.652.182-4.441.014-8.036-.845-11.208-4.17-6.87-7.203-13.99-14.166-21.008-21.228l.69-1.319H229.63l-1.24-7.289h-5.726c-42.821 0-85.642.011-128.464-.013-6.18-.003-12.371-.428-18.537-.173-3.704.154-6.14-1.066-8.104-4.073-4.111-6.292-8.367-12.489-13.256-19.755h171.593c0-2.433 0-4.234-.027-6.463m35.103.062c2.442-.418 4.85-.422 7.641-.431.482 1.987.581 3.979.706 6.499H457.62c-3.126 4.951-5.544 8.946-8.123 12.835-7.64 11.521-7.665 11.508-21.226 11.509l-147.5.003h-5.407c-.602 6.688-.15 7.214 5.824 7.214q73.75.004 147.5.004h6.891c-4.048 4.533-7.454 7.812-10.233 11.556-9.047 12.19-20.575 16.124-35.967 15.542-33.039-1.25-66.159-.384-99.246-.384h-5.45c-.275 5.77.583 6.774 5.594 6.776 34.478.012 68.957.006 103.435.007h5.302l.443 1.274c-3.495 2.62-6.91 5.358-10.5 7.842-23.505 16.265-49.192 27.744-76.893 34.696-3.263.819-6.763 1.374-10.069 1.085-1.874-.164-4.144-1.915-5.266-3.597-9.503-14.258-15.474-30.133-21.055-46.227-7.418-21.394-11.59-43.465-14.702-66.203"
id="path4"
style="fill:#fe8463;fill-opacity:1" />
<path
fill="#ff43c4"
d="M360.319 254.985c-3.232.395-6.479.407-10.189.359 2.612-14.328 2.295-28.523-1.162-42.703-4.74-19.439-15.808-34.254-32.502-45.039-.639-.413-1.27-.839-1.852-2.137h52.547l.213-1.506c-7.026-2.665-13.978-5.752-21.733-5.819-15.455-.136-30.914.047-46.369-.12-3.092-.034-6.173-1.044-9.202-2.411 18.374-7.385 37.172-6.583 56.061-3.6 19.128 3.021 35.771 12.957 54.614 20.622l-3.662-6.95c1.846-.148 3.252-.359 4.658-.36 21.485-.021 42.97.048 64.454-.082 3.262-.019 5.062.848 6.121 4.101 2.319 7.128 5.012 14.134 7.891 22.129h-127.22c.144 4.537 2.105 5.796 5.897 5.783q43.588-.154 87.175-.028c10.803.031 21.607.309 32.402.688 1.327.047 3.486 1.183 3.763 2.215 1.549 5.774 2.638 11.671 4.021 18.169H361.127c-.548 6.29-.254 6.61 5.207 6.619 38.949.061 77.898.158 116.847.146 2.863-.001 4.211.672 4.397 3.772.304 5.076.956 10.136 1.609 15.183.414 3.199-.637 4.443-4.005 4.328-6.635-.228-13.283-.105-19.926-.106q-49.152-.005-98.304.001c-6.266.001-6.266.007-6.633 6.746"
id="path5"
style="fill:#ff7f2a;fill-opacity:1" />
<path
fill="#ff42c3"
d="M214.837 27.347c41.832-7.02 81.069-2.12 103 5.453-3.084 7.222-6.153 14.42-9.231 21.612-1.64 3.833-3.549 7.574-4.869 11.513-1.091 3.256-2.992 4.06-6.217 4.042-22.092-.119-44.185-.062-67.454-.062 3.94 4.046 6.873 7.296 12.308 7.245 16.689-.157 33.381-.056 50.072-.055h6.745c-3.661 6.1-6.056 11.84-9.975 16.217-6.139 6.856-13.44 12.659-19.972 19.184-2.516 2.514-3.687 2.416-6.101-.229-9.873-10.813-19.809-21.593-30.196-31.908-11.454-11.376-25.983-16.088-41.742-17.908-14.011-1.618-27.312.965-40.451 5.507a34.7 34.7 0 0 1-9.886 1.86c-7.248.275-14.513.085-21.995-.508 28.501-21.58 60.452-35.31 95.964-41.963m53.746 77.311 3.605-4.283h-11.056c1.525 2.155 2.339 3.846 3.653 4.931.643.531 2.216-.065 3.798-.648"
id="path6"
style="fill:#63915a;fill-opacity:1" />
<path
fill="#ff44c4"
d="M377.152 144.943c-12.092-9.869-26.287-13.386-40.919-15.513-16.956-2.465-33.583-1.51-50.074 5.653.952-1.742 1.722-3.617 2.884-5.206 15.646-21.395 34.923-38.76 58.357-51.192 3.545-1.881 8.365-1.78 12.607-1.849 13.445-.22 26.898-.165 40.345-.002 2.017.024 4.514.652 5.947 1.938 7.01 6.292 13.741 12.894 20.545 19.414.386.37.542.98 1.172 2.175h-5.468c-30.918 0-61.837.116-92.753-.114-5.213-.039-8.095 2.606-11.98 6.969h6.295c35.092-.001 70.184.065 105.275-.091 4.005-.018 6.738 1.186 8.994 4.365 3.728 5.252 7.642 10.372 12.234 16.566h-90.207l-.153 1.134c6.582 2.444 12.387 6.339 20.175 6.183 23.179-.463 46.374-.087 69.561-.245 3.754-.026 6.054 1.177 7.725 4.48 2.979 5.887 6.211 11.646 9.711 18.154-2.304.15-3.753.325-5.203.326-22.415.017-44.831-.042-67.246.074-3.19.016-5.54-.802-7.705-3.211-3.093-3.441-6.561-6.545-10.119-10.008M267.626 255.455a972 972 0 0 1-7.18.028c2.056-26.604 4.319-53.217 12.215-78.952 1.17-3.813 2.458-5.484 6.241-2.773 3.757 2.69 8.105 4.741 11.346 7.934 22.076 21.743 42.878 44.52 56.347 73.308-3.623.356-7.36.406-11.542.092-1.222-4.579-3.365-6.359-8.021-6.284-17.697.286-35.402.122-53.104.126h-6.302zm2.176-30.951h44.834l.519-1.678c-1.826-.906-3.637-2.562-5.479-2.597-12.466-.234-24.937-.104-37.407-.144-2.831-.009-4.108.992-2.467 4.419m10.378-31.312c-3.285-.223-6.209-.007-5.651 4.577h16.788c-2.211-5.078-6.298-4.716-11.137-4.577m-41.043 62.082c-3.21.24-6.22.195-9.566-.301-.214-2.054-.093-3.657.08-5.95-11.47 0-22.329-.006-33.188.003-5.917.004-6.716.761-7.574 7.07-1.888.101-3.775.202-6.063.302 4.551-9.736 8.81-19.906 14.604-29.11 10.304-16.372 21.983-31.76 37.616-43.656 7.217-5.492 15.096-9.592 23.847-11.977 1.13-.308 2.333-.345 4.833-.691-10.43 27.968-18.906 55.583-24.589 84.31m-25.72-36.778c-4.442-.439-5.459 2.888-7.32 6.624 9.882 0 19.096-.135 28.302.061 4.188.089 5.338-1.767 4.777-6.598-8.393 0-16.684 0-25.759-.087m33.855-22.914c-1.112-.66-2.178-1.777-3.343-1.89-2.903-.28-5.86.032-8.779-.147-3.761-.229-5.839 1.749-7.932 5.507 6.203 0 11.685.126 17.15-.12.95-.043 1.829-1.662 2.904-3.35m96.491-154.518c17.922 7.326 34.473 16.35 49.706 28.527-30.671-2.027-59.888 3.402-88.579 15.8.855-4.673 2.395-7.566 6.408-8.806 5.86-1.811 11.674-3.77 17.472-6.488h-16.478c5.867-11.505 11.161-22.041 16.71-32.442.395-.74 2.807-.966 4.029-.597 3.534 1.07 6.939 2.564 10.732 4.006"
id="path7"
style="fill:#ff7f2a;fill-opacity:1" />
<path
style="fill:#000000;fill-opacity:1"
d="m 130,278.4 c 69.2,16 30.4,65.6 30.4,65.6 0,0 -14.8,-92.4 28.8,-46.4 43.6,46 -59.2,-19.2 -59.2,-19.2 z"
id="path8" />
<path
style="fill:#000000;fill-opacity:1"
d="m 76.08469,213.54625 c 17.819091,7.91959 0.282842,24.32447 22.061731,7.91959 21.778889,-16.40487 53.174429,-48.3661 21.778889,-16.40487 -31.395541,31.96122 -43.84062,8.48528 -43.84062,8.48528 z"
id="path9" />
<path
style="fill:#000000;fill-opacity:1"
d="m 310.4,292.8 c 36,20.8 16,50.4 40,18.8 24,-31.6 46.8,-57.2 14.8,-33.6 -32,23.6 -54.8,14.8 -54.8,14.8 z"
id="path10" />
<path
style="fill:#000000;fill-opacity:1"
d="m 380.8,197.2 c 41.6,30.4 18.4,62.8 42,30.4 23.6,-32.4 10,-68.8 23.6,-32.4 13.6,36.4 58,64.4 13.6,36.4 -44.4,-28 -79.2,-34.4 -79.2,-34.4 z"
id="path11" />
<path
style="fill:#000000;fill-opacity:1"
d="M 99.6,145.2 C 146,120.4 170.4,80.8 146,120.4 121.6,160 121.2,187.6 120.4,159.6 119.6,131.6 99.6,145.2 99.6,145.2 Z"
id="path12" />
<path
style="fill:#000000;fill-opacity:1"
d="m 166.4,395.2 c 36.4,-34.4 26.8,-68.4 35.2,-32.8 8.4,35.6 -36,4.8 -36,4.8 z"
id="path13" />
<path
style="fill:#000000;fill-opacity:1"
d="M 325.6,419.2 C 390,378.4 402,310 388.8,379.6 c -13.2,69.6 7.6,39.6 -15.6,67.6 -23.2,28 81.6,-113.2 50,-90.8 -31.6,22.4 -97.6,62.8 -97.6,62.8 z"
id="path14" />
<path
style="fill:#000000;fill-opacity:1"
d="m 298.11622,232.77955 c 9.33381,30.82986 27.43574,16.68772 9.05097,29.41564 -18.38478,12.72793 -23.19311,44.12347 -17.53625,11.59656 5.65685,-32.52692 8.48528,-41.0122 8.48528,-41.0122 z"
id="path15" />
<path
style="fill:#000000;fill-opacity:1"
d="M 54.305801,291.32799 C 110.5915,257.10403 141.42136,225.1428 108.89444,258.51824 76.367532,291.89368 96.166522,307.73287 78.913117,291.04515 61.659711,274.35743 54.022958,289.06525 54.022958,289.06525 l 53.174432,-31.1127 z"
id="path16" />
</svg>

After

Width:  |  Height:  |  Size: 13 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.9 KiB

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 MiB

After

Width:  |  Height:  |  Size: 38 KiB

BIN
dist/yuzu.bmp vendored

Binary file not shown.

Before

Width:  |  Height:  |  Size: 256 KiB

After

Width:  |  Height:  |  Size: 256 KiB

View File

@@ -1,5 +1,5 @@
# ui stuff
/src/android @AleksandrPopovich @nyxynx @Producdevity
/src/android @AleksandrPopovich @kleidis @Producdevity
/src/yuzu @crueter
/src/eden @crueter
/src/frontend_common @crueter

View File

@@ -1,5 +1,16 @@
# Caveats
<!-- TOC -->
- [Arch Linux](#arch-linux)
- [Gentoo Linux](#gentoo-linux)
- [macOS](#macos)
- [Solaris](#solaris)
- [HaikuOS](#haikuos)
- [OpenBSD](#openbsd)
- [FreeBSD](#freebsd)
- [NetBSD](#netbsd)
<!-- /TOC -->
## Arch Linux
- httplib AUR package is broken. Set `httplib_FORCE_BUNDLED=ON` if you have it installed.
@@ -53,6 +64,8 @@ For this reason this patch is NOT applied to default on all platforms (for obvio
`cubeb_devel` will also not work, either disable cubeb or uninstall it.
Still will not run flawlessly until `mesa-24` is available. Modify CMakeCache.txt with the `.so` of libGL and libGLESv2 by doing the incredibly difficult task of copy pasting them (`cp /boot/system/lib/libGL.so .`)
## OpenBSD
After configuration, you may need to modify `externals/ffmpeg/CMakeFiles/ffmpeg-build/build.make` to use `-j$(nproc)` instead of just `-j`.

77
docs/Coding.md Normal file
View File

@@ -0,0 +1,77 @@
# Coding guidelines
These are mostly "suggestions", if you feel like your code is readable, comprehensible to others; and most importantly doesn't result in unreadable spaghetti you're fine to go.
But for new developers you may find that following these guidelines will make everything x10 easier.
## Naming conventions
Simply put, types/classes are named as `PascalCase`, same for methods and functions like `AddElement`. Variables are named `like_this_snake_case` and constants are `IN_SCREAMING_CASE`.
Template typenames prefer short names like `T`, `I`, `U`, if a longer name is required either `Iterator` or `perform_action` are fine as well.
Macros must always be in `SCREAMING_CASE`. Do not use short letter macros as systems like Solaris will conflict with them; a good rule of thumb is >5 characters per macro - i.e `THIS_MACRO_IS_GOOD`, `AND_ALSO_THIS_ONE`.
Try not using hungarian notation, if you're able.
## Formatting
Do not put if/while/etc braces after lines:
```c++
// no dont do this
if (thing)
{
some(); // ...
}
// do this
if (thing) {
some(); // ...
}
// or this
if (thing)
some(); // ...
// this is also ok
if (thing) some();
```
Brace rules are lax, if you can get the point across, do it:
```c++
// this is fine
do {
if (thing) {
return 0;
}
} while (other);
// this is also ok --- albeit a bit more dense
do if (thing) return 0; while (other);
// ok as well
do {
if (thing) return 0;
} while (other);
```
There is no 80-column limit but preferably be mindful of other developer's readability (like don't just put everything onto one line).
```c++
// someone is going to be mad due to this
SDL_AudioSpec obtained;
device_name.empty() ? device = SDL_OpenAudioDevice(nullptr, capture, &spec, &obtained, false) : device = SDL_OpenAudioDevice(device_name.c_str(), capture, &spec, &obtained, false);
// maybe consider this
SDL_AudioSpec obtained;
if (device_name.empty()) {
device = SDL_OpenAudioDevice(nullptr, capture, &spec, &obtained, false);
} else {
device = SDL_OpenAudioDevice(device_name.c_str(), capture, &spec, &obtained, false);
}
// or this is fine as well
SDL_AudioSpec obtained;
device = SDL_OpenAudioDevice(device_name.empty() ? nullptr : device_name.c_str(), capture, &spec, &obtained, false);
```

View File

@@ -1,5 +1,23 @@
# Debug Guidelines
## Issue reports
When reporting issues or finding bugs, we often need backtraces, debug logs, or both in order to track down the issue.
### Graphics Debugging
If your bug is related to a graphical issue--e.g. mismatched colors, vertex explosions, flickering, etc.--then you are required to include graphical debugging logs in your issue reports.
Graphics Debugging is found in General -> Debug on desktop, and Advanced Settings -> Debug on Android. Android users are all set; however, desktop users may need to install the Vulkan Validation Layers:
- Windows: Install the [Vulkan SDK](https://vulkan.lunarg.com/sdk/home)
- Linux, BSD, etc: Install `vulkan-validation-layers`, `vulkan-layers`, or similar from your package manager. It should be located in e.g. `/usr/lib64/libVkLayer_khronos_validation.so`
Once Graphics Debugging is enabled, run the problematic game again and continue. Note that the game may run extremely slow on weak hardware.
### Debug Logs
Debug logs can be found in General -> Debug -> Open Log Location on desktop, and Share Debug Logs on Android. This MUST be included in all bug reports, except for certain UI bugs--but we still highly recommend them even for UI bugs.
## Debugging (host code)
Ignoring SIGSEGV when debugging in host:
@@ -47,10 +65,10 @@ Renderdoc is a free, cross platform, multi-graphics API debugger. It is an inval
Before using renderdoc to diagnose issues, it is always good to make sure there are no validation errors. Any errors means the behavior of the application is undefined. That said, renderdoc can help debug validation errors if you do have them.
When debugging a black screen, there are many ways the application could have setup Vulkan wrong.
When debugging a black screen, there are many ways the application could have setup Vulkan wrong.
Here is a short checklist of items to look at to make sure are appropriate:
* Draw call counts are correct (aka not zero, or if rendering many triangles, not 3)
* Vertex buffers are bound
* Vertex buffers are bound
* vertex attributes are correct - Make sure the size & offset of each attribute matches what should it should be
* Any bound push constants and descriptors have the right data - including:
* Matrices have correct values - double check the model, view, & projection matrices are uploaded correctly
@@ -63,6 +81,6 @@ Here is a short checklist of items to look at to make sure are appropriate:
* Blend state is correct
* Depth state is correct - typically enabled with Function set to Less than or Equal
* Swapchain images are bound when rendering to the swapchain
* Image being rendered to is the same as the one being presented when rendering to the swapchain
* Image being rendered to is the same as the one being presented when rendering to the swapchain
Alternatively, a [RenderDoc Extension](https://github.com/baldurk/renderdoc-contrib/tree/main/baldurk/whereismydraw) ([Archive](https://web.archive.org/web/20250000000000*/https://github.com/baldurk/renderdoc-contrib/tree/main/baldurk/whereismydraw)) exists which automates doing a lot of these manual steps.

View File

@@ -5,7 +5,6 @@ To build Eden, you MUST have a C++ compiler.
- GCC 12 also requires Clang 14+
* On Windows, this is either:
- **[MSVC](https://visualstudio.microsoft.com/downloads/)** (you should select *Community* option),
* *A convenience script to install the Visual Community Studio 2022 with necessary tools is provided in `.ci/windows/install-msvc.ps1`*
- clang-cl - can be downloaded from the MSVC installer,
- or **[MSYS2](https://www.msys2.org)**
* On macOS, this is Apple Clang
@@ -26,6 +25,9 @@ If you are on desktop and plan to use the Qt frontend, you *must* install Qt 6,
* Linux and macOS users may choose to use the installer as well.
* MSYS2 can also install Qt 6 via the package manager
If you are on Windows, a convenience script to install MSVC, MSYS2, Qt, all necessary packages for MSYS2, and set up a zsh environment with useful keybinds and aliases can be found [here](https://git.crueter.xyz/scripts/windev).
- For help setting up Qt Creator, run `./install.sh -h qtcreator`
If you are on Windows and NOT building with MSYS2, you may go [back home](Build.md) and continue.
## Externals
@@ -70,9 +72,6 @@ Certain other dependencies will be fetched by CPM regardless. System packages *c
On amd64:
* [xbyak](https://github.com/herumi/xbyak) - 7.22 or earlier is recommended
* [zycore](https://github.com/zyantific/zycore-c)
* [zydis](https://github.com/zyantific/zydis) 4+
* Note: zydis and zycore-c MUST match. Using one as a system dependency and the other as a bundled dependency WILL break things
On aarch64 OR if `DYNARMIC_TESTS` is on:
* [oaknut](https://github.com/merryhime/oaknut) 2.0.1+
@@ -84,13 +83,15 @@ On riscv64:
These are commands to install all necessary dependencies on various Linux and BSD distributions, as well as macOS. Always review what you're running before you hit Enter!
Notes for writers: Include build tools as well, assume user has NOTHING installed (i.e a fresh install) but that they have updated beforehand so no `upgrade && update` or equivalent should be mentioned - except for rolling release systems like Arch.
Click on the arrows to expand.
<details>
<summary>Arch Linux</summary>
```sh
sudo pacman -Syu --needed base-devel boost catch2 cmake enet ffmpeg fmt git glslang libzip lz4 mbedtls ninja nlohmann-json openssl opus qt6-base qt6-multimedia sdl2 zlib zstd zip unzip zydis zycore vulkan-headers vulkan-utility-libraries libusb spirv-tools spirv-headers
sudo pacman -Syu --needed base-devel boost catch2 cmake enet ffmpeg fmt git glslang libzip lz4 mbedtls ninja nlohmann-json openssl opus qt6-base qt6-multimedia sdl2 zlib zstd zip unzip vulkan-headers vulkan-utility-libraries libusb spirv-tools spirv-headers
```
* Building with QT Web Engine requires `qt6-webengine` as well.
@@ -102,7 +103,7 @@ sudo pacman -Syu --needed base-devel boost catch2 cmake enet ffmpeg fmt git glsl
<summary>Ubuntu, Debian, Mint Linux</summary>
```sh
sudo apt-get install autoconf cmake g++ gcc git glslang-tools libglu1-mesa-dev libhidapi-dev libpulse-dev libtool libudev-dev libxcb-icccm4 libxcb-image0 libxcb-keysyms1 libxcb-render-util0 libxcb-xinerama0 libxcb-xkb1 libxext-dev libxkbcommon-x11-0 mesa-common-dev nasm ninja-build qt6-base-private-dev libmbedtls-dev catch2 libfmt-dev liblz4-dev nlohmann-json3-dev libzstd-dev libssl-dev libavfilter-dev libavcodec-dev libswscale-dev pkg-config zlib1g-dev libva-dev libvdpau-dev qt6-tools-dev libzydis-dev zydis-tools libzycore-dev libvulkan-dev spirv-tools spirv-headers libusb-1.0-0-dev libxbyak-dev libboost-dev libboost-fiber-dev libboost-context-dev libsdl2-dev libopus-dev libasound2t64 vulkan-utility-libraries-dev
sudo apt-get install autoconf cmake g++ gcc git glslang-tools libglu1-mesa-dev libhidapi-dev libpulse-dev libtool libudev-dev libxcb-icccm4 libxcb-image0 libxcb-keysyms1 libxcb-render-util0 libxcb-xinerama0 libxcb-xkb1 libxext-dev libxkbcommon-x11-0 mesa-common-dev nasm ninja-build qt6-base-private-dev libmbedtls-dev catch2 libfmt-dev liblz4-dev nlohmann-json3-dev libzstd-dev libssl-dev libavfilter-dev libavcodec-dev libswscale-dev pkg-config zlib1g-dev libva-dev libvdpau-dev qt6-tools-dev libvulkan-dev spirv-tools spirv-headers libusb-1.0-0-dev libxbyak-dev libboost-dev libboost-fiber-dev libboost-context-dev libsdl2-dev libopus-dev libasound2t64 vulkan-utility-libraries-dev
```
* Ubuntu 22.04, Linux Mint 20, or Debian 12 or later is required.
@@ -134,6 +135,35 @@ sudo dnf install qt6-qtbase-private-devel
* Fedora 36+ users with GCC 12 need Clang and should configure CMake with: `cmake -DCMAKE_CXX_COMPILER=clang++ -DCMAKE_C_COMPILER=clang -B build`
</details>
<details>
<summary>Alpine Linux</summary>
First, enable the community repository; [see here](https://wiki.alpinelinux.org/wiki/Repositories#Enabling_the_community_repository).
```sh
# Enable the community repository
setup-apkrepos -c
# Install
apk add g++ git cmake make mbedtls-dev mbedtls-static mesa-dev qt6-qtbase-dev qt6-qtbase-private-dev libquazip1-qt6 ffmpeg-dev libusb-dev libtool boost-dev sdl2-dev zstd-dev vulkan-utility-libraries spirv-tools-dev openssl-dev nlohmann-json lz4-dev opus-dev jq patch
```
`mbedtls-static` has to be specified otherwise `libeverest.a` and `libp256m.a` will fail to be found.
</details>
<summary>Void Linux</summary>
```sh
xbps-install -Su git make cmake clang pkg-config patch mbedtls-devel SPIRV-Tools-devel SPIRV-Headers lz4 liblz4-devel boost-devel ffmpeg6-devel catch2 Vulkan-Utility-Libraries Vulkan-Headers glslang openssl-devel SDL2-devel quazip-qt6-devel qt6-base-devel qt6-qt5compat-devel fmt-devel json-c++ libenet-devel libusb-devel
```
Yes, `nlohmann-json` is just named `json-c++`. Why?
</details>
<details>
<summary>NixOS</summary>
A convenience script is provided on the root of this project [shell.nix](../shell.nix). Run the usual `nix-shell`.
</details>
<details>
<summary>macOS</summary>
@@ -156,7 +186,7 @@ brew install molten-vk vulkan-loader
<details>
<summary>FreeBSD</summary>
As root run: `pkg install devel/cmake devel/sdl20 devel/boost-libs devel/catch2 devel/libfmt devel/nlohmann-json devel/ninja devel/nasm devel/autoconf devel/pkgconf devel/qt6-base devel/simpleini net/enet multimedia/ffnvcodec-headers multimedia/ffmpeg audio/opus archivers/liblz4 lang/gcc12 graphics/glslang graphics/vulkan-utility-libraries graphics/spirv-tools www/cpp-httplib devel/jwt-cpp devel/unordered-dense devel/zydis`
As root run: `pkg install devel/cmake devel/sdl20 devel/boost-libs devel/catch2 devel/libfmt devel/nlohmann-json devel/ninja devel/nasm devel/autoconf devel/pkgconf devel/qt6-base devel/simpleini net/enet multimedia/ffnvcodec-headers multimedia/ffmpeg audio/opus archivers/liblz4 lang/gcc12 graphics/glslang graphics/vulkan-utility-libraries graphics/spirv-tools www/cpp-httplib devel/jwt-cpp devel/unordered-dense`
If using FreeBSD 12 or prior, use `devel/pkg-config` instead.
@@ -171,7 +201,6 @@ For NetBSD +10.1: `pkgin install git cmake boost fmtlib SDL2 catch2 libjwt spirv
[Caveats](./Caveats.md#netbsd).
</details>
<details>
<summary>OpenBSD</summary>
@@ -183,7 +212,6 @@ pkg_add cmake nasm git boost unzip--iconv autoconf-2.72p0 bash ffmpeg glslang gm
[Caveats](./Caveats.md#openbsd).
</details>
<details>
<summary>Solaris / OpenIndiana</summary>
@@ -199,24 +227,37 @@ Then install the libraries: `sudo pkg install qt6 boost glslang libzip library/l
[Caveats](./Caveats.md#solaris).
</details>
<details>
<summary>MSYS2</summary>
* Open the `MSYS2 MinGW 64-bit` shell (`mingw64.exe`)
* Download and install all dependencies using:
* `pacman -Syu git make mingw-w64-x86_64-SDL2 mingw-w64-x86_64-cmake mingw-w64-x86_64-python-pip mingw-w64-x86_64-qt6 mingw-w64-x86_64-toolchain autoconf libtool automake-wrapper`
* Add MinGW binaries to the PATH:
* `echo 'PATH=/mingw64/bin:$PATH' >> ~/.bashrc`
* Add VulkanSDK to the PATH:
* `echo 'PATH=$(readlink -e /c/VulkanSDK/*/Bin/):$PATH' >> ~/.bashrc`
</details>
* Download and install all dependencies:
```
BASE="git make autoconf libtool automake-wrapper jq patch"
MINGW="SDL2 cmake python-pip qt6-base toolchain ffmpeg boost catch fmt lz4 nlohmann-json openssl zlib zstd enet opus mbedtls vulkan-devel libusb vulkan-memory-allocator unordered_dense clang ccache"
packages="$BASE"
for pkg in $MINGW; do
packages="$packages mingw-w64-x86_64-$pkg"
done
pacman -Syu --needed --noconfirm $packages
```
* Notes:
- Using `qt6-static` is possible but currently untested.
- Other environments are entirely untested, but should theoretically work provided you install all the necessary packages.
- Clang is installed as it generally works better here. You can compile with GCC just fine, however.
- Add `qt-creator` to the `MINGW` variable to install Qt Creator. You can then create a Start Menu shortcut to the MinGW Qt Creator by running `powershell "\$s=(New-Object -COM WScript.Shell).CreateShortcut('C:\\ProgramData\\Microsoft\\Windows\\Start Menu\\Programs\\Qt Creator.lnk');\$s.TargetPath='C:\\msys64\\mingw64\\bin\\qtcreator.exe';\$s.Save()"` in Git Bash or MSYS2.
* Add MinGW binaries to the PATH if they aren't already:
* `echo 'PATH=/mingw64/bin:$PATH' >> ~/.bashrc`
* or `echo 'PATH=/mingw64/bin:$PATH' >> ~/.zshrc`
</details>
<details>
<summary>HaikuOS</summary>
```sh
pkgman install git cmake libfmt_devel nlohmann_json lz4_devel opus_devel boost1.89_devel vulkan_devel qt6_base_devel libsdl2_devel ffmpeg7_devel libx11_devel enet_devel catch2_devel quazip1_qt6_devel qt6_5compat_devel zydis_devel glslang
pkgman install git cmake patch libfmt_devel nlohmann_json lz4_devel opus_devel boost1.89_devel vulkan_devel qt6_base_devel libsdl2_devel ffmpeg7_devel libx11_devel enet_devel catch2_devel quazip1_qt6_devel qt6_5compat_devel libusb1_devel libz_devel glslang mbedtls3
```
[Caveats](./Caveats.md#haikuos).

View File

@@ -22,7 +22,6 @@ Notes:
- `YUZU_USE_BUNDLED_FFMPEG` (ON for non-UNIX) Download (Windows, Android) or build (UNIX) bundled FFmpeg
- `ENABLE_CUBEB` (ON) Enables the cubeb audio backend
- `YUZU_TESTS` (ON) Compile tests - requires Catch2
- `YUZU_USE_PRECOMPILED_HEADERS` (ON for non-UNIX) Use precompiled headers
- `YUZU_DOWNLOAD_ANDROID_VVL` (ON) Download validation layer binary for Android
- `YUZU_ENABLE_LTO` (OFF) Enable link-time optimization
* Not recommended on Windows

View File

@@ -5,8 +5,9 @@ This contains documentation created by developers. This contains build instructi
- **[General Build Instructions](Build.md)**
- **[Cross Compiling](CrossCompile.md)**
- **[Development Guidelines](Development.md)**
- **[Coding guidelines](Coding.md)**
- **[Dependencies](Deps.md)**
- **[Debug Guidelines](./Debug.md)**
- **[CPM - CMake Package Manager](CPMUtil.md)**
- **[Platform-Specific Caveats](Caveats.md)**
- **[User Handbook](User.md)**
- **[User Handbook](./user)**

View File

@@ -1,12 +0,0 @@
# User Handbook
The "FAQ".
This handbook is primarily aimed at the end-user - baking useful knowledge for enhancing their emulation experience.
- **[The Basics](user/Basics.md)**
- **[Audio](user/Audio.md)**
- **[Graphics](user/Graphics.md)**
- **[Platforms and Architectures](user/Architectures.md)**
- **[Testing](user/Testing.md)**
- **[Data, savefiles and storage](user/Storage.md)**

View File

@@ -47,6 +47,26 @@ Various graphical filters exist - each of them aimed at a specific target/image
While stock shaders offer a basic subset of options for most users, programs such as [ReShade](https://github.com/crosire/reshade) offer a more flexible experience. In addition to that users can also seek out modifications (mods) for enhancing visual experience (60 FPS mods, HDR, etc).
**Installing ReShade (Windows)**
1. [Download ReShade](https://reshade.me/#download) with add-on support.
- [ReShade Setup 6.6.2 (Windows 64-bit) with add-on support](https://reshade.me/downloads/ReShade_Setup_6.6.2_Addon.exe)
- SHA512 checksum: `1f09a73afa160480c13ffdd73cc04b1dc82943dddea58ad3bb9375f26b48c2787d0a85197e46b6fce32a4fd3472465520a3355ed3436241e17fba7ebaff7ffec`.
2. Open ReShade and hit browse, then the folder where `eden.exe` is at, hit open, then hit next.
3. Select Vulkan as the rendering API, hit next.
4. In "Select effects to install" screen: hit next don't change anything.
5. In "Select add on" screen: click the box for `Shader Toggler by Otis` ([GitHub](https://github.com/FransBouma/ShaderToggler)) and hit next.
**Using the Shader Toggler**
1. Launch a game, you must see a ReShade pop up afterwards.
2. Progress to a point with a flickering shader.
3. Hit the Home key on keyboard (or change binds if you don't have one).
4. Navigate to the add on tab at the top of the ReShade menu.
5. At the bottom where Shader Toggler is at open the drop down and max out the slider that says "# of fames collected" then select change shaders while staring at the flickering shader.
6. When the Shader Toggler finishes collecting frames in the top left hit Numpad 2 till it turns off the flickering lines.
7. Hit Numpad 3 to add it the group of shaders to turn off and hit done and save all toggle groups.
8. Hit the edit button and select "active at startup" for the shader to be turned off on every game launch.
9. Caps lock to manually turn on and off the shader (default key you can change it with the previous edit button)
## Driver specifics
### Mesa environment variable hacks

31
docs/user/Orphaned.md Normal file
View File

@@ -0,0 +1,31 @@
# Orphaned Profiles
A bug present in earlier versions of Eden and Yuzu caused some profiles to be read from the incorrect location if your NAND directory was set to anything other than the default. This bug was fixed in Eden v0.0.4-rc1, but it can be destructive if you're not careful.
## What are they?
Orphaned profiles refer to emulated user profiles that may or may not contain valid save data, but are not referenced by the internal profile map. This means the save data is effectively inaccessible, and should be fixed in order to access your save data.
## How do I fix it?
There are lots of different cases of varying complexity.
Remember to ALWAYS back up your saves!
### Simple Copy
Sometimes, a simple copying is all you need. For example, if the orphaned profile folder contains game saves, BUT the good profile is completely empty, then you can simply remove the empty folder and rename the orphaned profile to the same name as the good one.
### Combination
In more extreme cases, game saves can be strewn all throughout different profiles. In this case, you must look at each profile individually.
Typically, one folder will clearly have more recent/numerous save data, in which case you can remove all the other profile folders and follow the same procedure as the simple copy.
If multiple profile folders contain valid data, the recommended approach is to copy the contents of one folder into the other. There are likely to be file conflicts, the resolution of which is up to you.
An alternate method for dealing with multiple valid profiles is to go into System -> Profiles, and create a new profile. From there, you can copy the contents of each previously-orphaned profile into a new profile.
### Edge Cases
There are way too many edge cases to cover here, but in general, make backups! You can never go wrong if you always have a backup of your saves.

13
docs/user/README.md Normal file
View File

@@ -0,0 +1,13 @@
# User Handbook
The "FAQ".
This handbook is primarily aimed at the end-user - baking useful knowledge for enhancing their emulation experience.
- **[The Basics](Basics.md)**
- **[Audio](Audio.md)**
- **[Graphics](Graphics.md)**
- **[Platforms and Architectures](Architectures.md)**
- **[Testing](Testing.md)**
- **[Data, savefiles and storage](Storage.md)**
- **[Orphaned Profiles](Orphaned.md)**

View File

@@ -27,6 +27,8 @@ When you report your results:
2. "Tested on based master — issue already present. Good result for PR, not a regression."
```
This approach helps maintain clarity and accountability in the testing process and ensures regressions are caught and addressed efficiently. If the behavior seems normal for a certain game/feature then it may not be always required to check against the based master.
This approach helps maintain clarity and accountability in the testing process and ensures regressions are caught and addressed efficiently. If the behavior seems normal for a certain game/feature then it may not be always required to check against the based master.
If a master build for the PR' based master does not exist. It will be helpful to just test past and future builds nearby. That would help with gathering more information about the problem.
**Always include [debugging info](../Debug.md) as needed**.

View File

@@ -219,7 +219,7 @@ if (ENABLE_WEB_SERVICE OR ENABLE_UPDATE_CHECKER)
endif()
# cpp-jwt
if (ENABLE_WEB_SERVICE)
if (ENABLE_WEB_SERVICE OR ENABLE_UPDATE_CHECKER)
AddJsonPackage(cpp-jwt)
endif()
@@ -399,9 +399,3 @@ if (ANDROID)
add_library(oboe::oboe ALIAS oboe)
endif()
# sse2neon
if (ARCHITECTURE_arm64 AND NOT TARGET sse2neon)
add_library(sse2neon INTERFACE)
target_include_directories(sse2neon INTERFACE sse2neon)
endif()

View File

@@ -30,7 +30,10 @@
"tag": "v%VERSION%",
"hash": "b364500f76e2ecb0fe21b032d831272e3f1dfeea71af74e325f8fc4ce9dcdb3c941b97a5b422bdeafb9facd058597b90f8bfc284fb9afe3c33fefa15dd5a010b",
"git_version": "0.26.0",
"find_args": "MODULE GLOBAL"
"find_args": "MODULE GLOBAL",
"patches": [
"0001-mingw.patch"
]
},
"cpp-jwt": {
"version": "1.4",

File diff suppressed because it is too large Load Diff

19
shell.nix Normal file
View File

@@ -0,0 +1,19 @@
let
nixpkgs = fetchTarball "https://github.com/NixOS/nixpkgs/tarball/nixos-24.05";
pkgs = import nixpkgs { config = {}; overlays = []; };
in
pkgs.mkShellNoCC {
packages = with pkgs; [
# essential programs
git cmake clang gnumake patch jq pkg-config
# libraries
openssl boost fmt nlohmann_json lz4 zlib zstd
enet libopus vulkan-headers vulkan-utility-libraries
spirv-tools spirv-headers simpleini vulkan-memory-allocator
vulkan-loader unzip mbedtls glslang python3 httplib
cpp-jwt ffmpeg-headless libusb1 cubeb
qt6.full # eden
SDL2 # eden-cli
discord-rpc gamemode # optional components
];
}

View File

@@ -62,9 +62,6 @@ if (MSVC AND NOT CXX_CLANG)
/Zc:throwingNew
/GT
# Modules
/experimental:module- # Explicitly disable module support due to conflicts with precompiled headers.
# External headers diagnostics
/external:anglebrackets # Treats all headers included by #include <header>, where the header file is enclosed in angle brackets (< >), as external headers
/external:W0 # Sets the default warning level to 0 for external headers, effectively disabling warnings for them.

View File

@@ -549,6 +549,21 @@ object NativeLibrary {
*/
external fun clearFilesystemProvider()
/**
* Gets the current virtual amiibo state reported by the core.
*
* @return Native enum value for the current amiibo state.
*/
external fun getVirtualAmiiboState(): Int
/**
* Loads amiibo data into the currently running emulation session.
*
* @param data Raw amiibo file contents.
* @return Native enum value representing the load result.
*/
external fun loadAmiibo(data: ByteArray): Int
/**
* Checks if all necessary keys are present for decryption
*/

View File

@@ -24,7 +24,7 @@ object Settings {
SECTION_INPUT_PLAYER_SIX,
SECTION_INPUT_PLAYER_SEVEN,
SECTION_INPUT_PLAYER_EIGHT,
SECTION_APP_SETTINGS(R.string.preferences_app_settings),
SECTION_APP_SETTINGS(R.string.app_settings),
SECTION_DEBUG(R.string.preferences_debug),
SECTION_EDEN_VEIL(R.string.eden_veil),
SECTION_APPLETS(R.string.applets_menu);

View File

@@ -35,6 +35,8 @@ import android.widget.FrameLayout
import android.widget.TextView
import android.widget.Toast
import androidx.activity.OnBackPressedCallback
import androidx.activity.result.contract.ActivityResultContracts
import androidx.annotation.StringRes
import androidx.appcompat.widget.PopupMenu
import androidx.core.content.res.ResourcesCompat
import androidx.core.graphics.Insets
@@ -54,12 +56,19 @@ import androidx.window.layout.WindowLayoutInfo
import com.google.android.material.color.MaterialColors
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.google.android.material.textview.MaterialTextView
import kotlinx.coroutines.CancellationException
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.ensureActive
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import org.yuzu.yuzu_emu.HomeNavigationDirections
import org.yuzu.yuzu_emu.NativeLibrary
import org.yuzu.yuzu_emu.R
import org.yuzu.yuzu_emu.activities.EmulationActivity
import org.yuzu.yuzu_emu.databinding.DialogOverlayAdjustBinding
import org.yuzu.yuzu_emu.databinding.FragmentEmulationBinding
import org.yuzu.yuzu_emu.features.input.NativeInput
import org.yuzu.yuzu_emu.features.settings.model.BooleanSetting
import org.yuzu.yuzu_emu.features.settings.model.IntSetting
import org.yuzu.yuzu_emu.features.settings.model.Settings
@@ -82,12 +91,11 @@ import org.yuzu.yuzu_emu.utils.ViewUtils
import org.yuzu.yuzu_emu.utils.ViewUtils.setVisible
import org.yuzu.yuzu_emu.utils.collect
import org.yuzu.yuzu_emu.utils.CustomSettingsHandler
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import kotlinx.coroutines.Dispatchers
import java.io.ByteArrayOutputStream
import java.io.File
import kotlin.coroutines.coroutineContext
import kotlin.coroutines.resume
import kotlin.coroutines.suspendCoroutine
import java.io.File
class EmulationFragment : Fragment(), SurfaceHolder.Callback {
private lateinit var emulationState: EmulationState
@@ -121,6 +129,60 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
private var perfStatsRunnable: Runnable? = null
private var socRunnable: Runnable? = null
private var isAmiiboPickerOpen = false
private var amiiboLoadJob: Job? = null
private val loadAmiiboLauncher =
registerForActivityResult(ActivityResultContracts.OpenDocument()) { uri ->
isAmiiboPickerOpen = false
val binding = _binding ?: return@registerForActivityResult
binding.inGameMenu.requestFocus()
if (!isAdded || uri == null) {
return@registerForActivityResult
}
if (!NativeLibrary.isRunning()) {
showAmiiboDialog(R.string.amiibo_wrong_state)
return@registerForActivityResult
}
amiiboLoadJob?.cancel()
val owner = viewLifecycleOwner
amiiboLoadJob = owner.lifecycleScope.launch {
val bytes = readAmiiboFile(uri)
if (!isAdded) {
return@launch
}
if (bytes == null || bytes.isEmpty()) {
showAmiiboDialog(
if (bytes == null) R.string.amiibo_unknown_error else R.string.amiibo_not_valid
)
return@launch
}
val result = withContext(Dispatchers.IO) {
if (NativeLibrary.isRunning()) {
NativeLibrary.loadAmiibo(bytes)
} else {
AmiiboLoadResult.WrongDeviceState.value
}
}
if (!isAdded) {
return@launch
}
handleAmiiboLoadResult(result)
}.also { job ->
job.invokeOnCompletion {
if (amiiboLoadJob == job) {
amiiboLoadJob = null
}
}
}
}
override fun onAttach(context: Context) {
super.onAttach(context)
@@ -623,6 +685,8 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
true
}
R.id.menu_load_amiibo -> handleLoadAmiiboSelection()
R.id.menu_controls -> {
val action = HomeNavigationDirections.actionGlobalSettingsActivity(
null,
@@ -893,6 +957,96 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
}
}
private fun handleLoadAmiiboSelection(): Boolean {
val binding = _binding ?: return true
binding.inGameMenu.requestFocus()
if (!NativeLibrary.isRunning()) {
showAmiiboDialog(R.string.amiibo_wrong_state)
return true
}
when (AmiiboState.fromValue(NativeLibrary.getVirtualAmiiboState())) {
AmiiboState.TagNearby -> {
amiiboLoadJob?.cancel()
NativeInput.onRemoveNfcTag()
showAmiiboDialog(R.string.amiibo_removed_message)
}
AmiiboState.WaitingForAmiibo -> {
if (isAmiiboPickerOpen) {
return true
}
isAmiiboPickerOpen = true
binding.drawerLayout.close()
loadAmiiboLauncher.launch(AMIIBO_MIME_TYPES)
}
else -> showAmiiboDialog(R.string.amiibo_wrong_state)
}
return true
}
private fun handleAmiiboLoadResult(result: Int) {
when (AmiiboLoadResult.fromValue(result)) {
AmiiboLoadResult.Success -> {
if (!isAdded) {
return
}
Toast.makeText(
requireContext(),
getString(R.string.amiibo_load_success),
Toast.LENGTH_SHORT
).show()
}
AmiiboLoadResult.UnableToLoad -> showAmiiboDialog(R.string.amiibo_in_use)
AmiiboLoadResult.NotAnAmiibo -> showAmiiboDialog(R.string.amiibo_not_valid)
AmiiboLoadResult.WrongDeviceState -> showAmiiboDialog(R.string.amiibo_wrong_state)
AmiiboLoadResult.Unknown -> showAmiiboDialog(R.string.amiibo_unknown_error)
}
}
private fun showAmiiboDialog(@StringRes messageRes: Int) {
if (!isAdded) {
return
}
MaterialAlertDialogBuilder(requireContext())
.setTitle(R.string.amiibo_title)
.setMessage(messageRes)
.setPositiveButton(R.string.ok, null)
.show()
}
private suspend fun readAmiiboFile(uri: Uri): ByteArray? =
withContext(Dispatchers.IO) {
val resolver = context?.contentResolver ?: return@withContext null
try {
resolver.openInputStream(uri)?.use { stream ->
val buffer = ByteArrayOutputStream()
val chunk = ByteArray(DEFAULT_BUFFER_SIZE)
while (true) {
coroutineContext.ensureActive()
val read = stream.read(chunk)
if (read == -1) {
break
}
buffer.write(chunk, 0, read)
}
buffer.toByteArray()
}
} catch (ce: CancellationException) {
throw ce
} catch (e: Exception) {
Log.error("[EmulationFragment] Failed to read amiibo: ${e.message}")
null
}
}
override fun onPause() {
if (this::emulationState.isInitialized) {
if (emulationState.isRunning && emulationActivity?.isInPictureInPictureMode != true) {
@@ -905,7 +1059,10 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
override fun onDestroyView() {
super.onDestroyView()
amiiboLoadJob?.cancel()
amiiboLoadJob = null
_binding = null
isAmiiboPickerOpen = false
}
override fun onDetach() {
@@ -1739,7 +1896,34 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
}
}
private enum class AmiiboState(val value: Int) {
Disabled(0),
Initialized(1),
WaitingForAmiibo(2),
TagNearby(3);
companion object {
fun fromValue(value: Int): AmiiboState =
values().firstOrNull { it.value == value } ?: Disabled
}
}
private enum class AmiiboLoadResult(val value: Int) {
Success(0),
UnableToLoad(1),
NotAnAmiibo(2),
WrongDeviceState(3),
Unknown(4);
companion object {
fun fromValue(value: Int): AmiiboLoadResult =
values().firstOrNull { it.value == value } ?: Unknown
}
}
companion object {
private val AMIIBO_MIME_TYPES =
arrayOf("application/octet-stream", "application/x-binary", "*/*")
private val perfStatsUpdateHandler = Handler(Looper.myLooper()!!)
private val socUpdateHandler = Handler(Looper.myLooper()!!)
}

View File

@@ -7,8 +7,10 @@
#include <codecvt>
#include <locale>
#include <span>
#include <string>
#include <string_view>
#include <vector>
#include <dlfcn.h>
#ifdef ARCHITECTURE_arm64
@@ -67,6 +69,7 @@
#include "hid_core/frontend/emulated_controller.h"
#include "hid_core/hid_core.h"
#include "hid_core/hid_types.h"
#include "input_common/drivers/virtual_amiibo.h"
#include "jni/native.h"
#include "video_core/renderer_base.h"
#include "video_core/renderer_vulkan/renderer_vulkan.h"
@@ -1005,6 +1008,44 @@ jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_areKeysPresent(JNIEnv* env, jobje
return ContentManager::AreKeysPresent();
}
jint Java_org_yuzu_yuzu_1emu_NativeLibrary_getVirtualAmiiboState(JNIEnv* env, jobject jobj) {
if (!EmulationSession::GetInstance().IsRunning()) {
return static_cast<jint>(InputCommon::VirtualAmiibo::State::Disabled);
}
auto* virtual_amiibo =
EmulationSession::GetInstance().GetInputSubsystem().GetVirtualAmiibo();
if (virtual_amiibo == nullptr) {
return static_cast<jint>(InputCommon::VirtualAmiibo::State::Disabled);
}
return static_cast<jint>(virtual_amiibo->GetCurrentState());
}
jint Java_org_yuzu_yuzu_1emu_NativeLibrary_loadAmiibo(JNIEnv* env, jobject jobj,
jbyteArray jdata) {
if (!EmulationSession::GetInstance().IsRunning() || jdata == nullptr) {
return static_cast<jint>(InputCommon::VirtualAmiibo::Info::WrongDeviceState);
}
auto* virtual_amiibo =
EmulationSession::GetInstance().GetInputSubsystem().GetVirtualAmiibo();
if (virtual_amiibo == nullptr) {
return static_cast<jint>(InputCommon::VirtualAmiibo::Info::Unknown);
}
const jsize length = env->GetArrayLength(jdata);
std::vector<u8> bytes(static_cast<std::size_t>(length));
if (length > 0) {
env->GetByteArrayRegion(jdata, 0, length,
reinterpret_cast<jbyte*>(bytes.data()));
}
const auto info =
virtual_amiibo->LoadAmiibo(std::span<u8>(bytes.data(), bytes.size()));
return static_cast<jint>(info);
}
JNIEXPORT void JNICALL
Java_org_yuzu_yuzu_1emu_NativeLibrary_initMultiplayer(
JNIEnv* env, [[maybe_unused]] jobject obj) {

Binary file not shown.

Before

Width:  |  Height:  |  Size: 48 KiB

After

Width:  |  Height:  |  Size: 74 KiB

View File

@@ -33,6 +33,11 @@
android:icon="@drawable/ic_two_users"
android:title="@string/multiplayer" />
<item
android:id="@+id/menu_load_amiibo"
android:icon="@drawable/ic_nfc"
android:title="@string/load_amiibo" />
<item
android:id="@+id/menu_overlay_controls"

View File

@@ -374,9 +374,6 @@
<string name="firmware_uninstalling">إلغاء تثبيت الفريموير</string>
<string name="firmware_uninstalled_success">تم إزالة الفريموير بنجاح</string>
<string name="error_firmware_missing">الفريموير مطلوب لتشغيل بعض الألعاب واستخدام التطبيقات. يعمل عدن فقط مع الفريموير 19.0.1 أو الأقدم.</string>
<string name="error_firmware_corrupted">الفريموير موجود ولكن لا يمكن قراءته. تحقق من مفاتيح التشفير وأعد نسخ الفريموير إذا لزم الأمر.</string>
<string name="error_firmware_too_new">الفريموير جديد جداً أو لا يمكن قراءته. يعمل عدن فقط مع الفريموير 19.0.1 أو الأقدم.</string>
<string name="keys_failed">فشل تثبيت المفاتيح</string>
<string name="keys_install_success">تم تثبيت المفاتيح بنجاح</string>

View File

@@ -322,9 +322,6 @@
<string name="firmware_uninstalling">جاري إزالة البرنامج الثابت</string>
<string name="firmware_uninstalled_success">تم إزالة البرنامج الثابت بنجاح</string>
<string name="error_firmware_missing">فریموێر پێویستە بۆ کارپێکردنی هەندێک یاری و بەکارهێنانی بەرنامەکان. ئێدین تەنها لەگەڵ فریموێری 19.0.1 یان کۆنتر کار دەکات.</string>
<string name="error_firmware_corrupted">فریموێر هەیە بەڵام ناتوانرێت بخوێنرێتەوە. پشکنین بکە بۆ کلیلی شیکردنەوە و دووبارە فریموێرەکە دامپ بکە ئەگەر پێویست بوو.</string>
<string name="error_firmware_too_new">فریموێر زۆر نوێ یان ناتوانرێت بخوێنرێتەوە. ئێدین تەنها لەگەڵ فریموێری 19.0.1 یان کۆنتر کار دەکات.</string>
<string name="keys_failed">سەرکەوتوو نەبوو لە ناساندنی کلیلی</string>
<string name="keys_install_success">کلیلەکان بە سەرکەوتوویی ناسێندران</string>

View File

@@ -301,9 +301,6 @@
<string name="firmware_uninstalling">Odstraňování firmwaru</string>
<string name="firmware_uninstalled_success">Firmware byl úspěšně odinstalován</string>
<string name="error_firmware_missing">Firmware je vyžadován pro spuštění některých her a aplikací. Eden funguje pouze s firmwarem 19.0.1 nebo starším.</string>
<string name="error_firmware_corrupted">Firmware byl nahlášen jako přítomný, ale nelze jej přečíst. Zkontrolujte šifrovací klíče a v případě potřeby firmware převezměte znovu.</string>
<string name="error_firmware_too_new">Firmware je příliš nový nebo nelze přečíst. Eden funguje pouze s firmwarem 19.0.1 nebo starším.</string>
<string name="keys_failed">Selhání instalace klíčů</string>
<string name="keys_install_success">Klíče úspěšně nainstalovány</string>

View File

@@ -353,9 +353,6 @@ Wirklich fortfahren?</string>
<string name="firmware_uninstalling">Firmware wird deinstalliert...</string>
<string name="firmware_uninstalled_success">Firmware erfolgreich deinstalliert</string>
<string name="error_firmware_missing">Firmware wird benötigt, um bestimmte Spiele und Applikationen zu nutzen. Eden funktioniert nur mit Firmware 19.0.1 oder älter.</string>
<string name="error_firmware_corrupted">Firmware wurde als vorhanden gemeldet, konnte aber nicht gelesen werden. Überprüfen Sie die Entschlüsselungsschlüssel und dumpen Sie die Firmware bei Bedarf neu.</string>
<string name="error_firmware_too_new">Firmware ist zu neu oder konnte nicht gelesen werden. Eden funktioniert nur mit Firmware 19.0.1 oder älter.</string>
<string name="keys_failed">Schlüsselinstallation fehlgeschlagen</string>
<string name="keys_install_success">Schlüssel erfolgreich installiert</string>

View File

@@ -368,9 +368,6 @@
<string name="firmware_uninstalling">Desinstalando firmware...</string>
<string name="firmware_uninstalled_success">Firmware desinstalado correctamente</string>
<string name="error_firmware_missing">Se requiere firmware para ejecutar ciertos juegos y aplicaciones. Eden solo funciona con firmware 19.0.1 o anterior.</string>
<string name="error_firmware_corrupted">El firmware está presente pero no se pudo leer. Verifique las claves de descifrado y vuelva a volcar el firmware si es necesario.</string>
<string name="error_firmware_too_new">El firmware es demasiado nuevo o no se pudo leer. Eden solo funciona con firmware 19.0.1 o anterior.</string>
<string name="keys_failed">Error al instalar claves</string>
<string name="keys_install_success">Claves instaladas correctamente</string>

View File

@@ -387,9 +387,6 @@
<string name="verify_installed_content_description">تمام محتوای نصب شده را از نظر خرابی بررسی می‌کند</string>
<string name="keys_missing">کلیدهای رمزگذاری وجود ندارند</string>
<string name="keys_missing_description">ثابت‌افزار و بازی‌های فروشگاهی قابل رمزگشایی نیستند</string>
<string name="error_firmware_missing">برای اجرای برخی بازی‌ها و برنامه‌ها نیاز به فیرمور است. ادن فقط با فیرمور 19.0.1 یا قدیمی‌تر کار می‌کند.</string>
<string name="error_firmware_corrupted">فیرمور موجود است اما قابل خواندن نیست. کلیدهای رمزگشایی را بررسی کنید و در صورت نیاز فیرمور را دوباره دامپ کنید.</string>
<string name="error_firmware_too_new">فیرمور خیلی جدید است یا قابل خواندن نیست. ادن فقط با فیرمور 19.0.1 یا قدیمی‌تر کار می‌کند.</string>
<string name="keys_failed">خطا در نصب کلیدها</string>
<string name="keys_install_success">کلیدها با موفقیت نصب شدند</string>
<string name="error_keys_copy_failed">یک یا چند کلید با خطا در کپی مواجه شد.</string>

View File

@@ -368,9 +368,6 @@
<string name="firmware_uninstalling">Désinstallation du firmware...</string>
<string name="firmware_uninstalled_success">Firmware désinstallé avec succès</string>
<string name="error_firmware_missing">Le firmware est requis pour exécuter certains jeux et applications. Eden ne fonctionne qu\'avec le firmware 19.0.1 ou antérieur.</string>
<string name="error_firmware_corrupted">Firmware présent mais illisible. Vérifiez les clés de décryptage et redumpz le firmware si nécessaire.</string>
<string name="error_firmware_too_new">Firmware trop récent ou illisible. Eden ne supporte que le firmware 19.0.1 ou antérieur.</string>
<string name="keys_failed">Échec de l\'installation des clés</string>
<string name="keys_install_success">Clés installées avec succès</string>

View File

@@ -334,9 +334,6 @@
<string name="firmware_uninstalling">מסיר קושחה...</string>
<string name="firmware_uninstalled_success">הקושחה הוסרה בהצלחה</string>
<string name="error_firmware_missing">נדרש קושחה להפעלת משחקים ויישומים מסוימים. עדן עובד רק עם קושחה 19.0.1 או ישנה יותר.</string>
<string name="error_firmware_corrupted">קושחה קיימת אך לא ניתנת לקריאה. בדוק מפתחות פענוח ושמור קושחה מחדש אם needed.</string>
<string name="error_firmware_too_new">קושחה חדשה מדי או לא ניתנת לקריאה. עדן תומך רק בקושחה 19.0.1 או ישנה יותר.</string>
<string name="keys_failed">התקנת מפתחות נכשלה</string>
<string name="keys_install_success">מפתחות הותקנו בהצלחה</string>

View File

@@ -331,9 +331,6 @@
<string name="firmware_uninstalling">Firmware eltávolítása...</string>
<string name="firmware_uninstalled_success">A firmware sikeresen eltávolítva</string>
<string name="error_firmware_missing">A firmware szükséges egyes játékok és alkalmazások futtatásához. Az Eden csak 19.0.1 vagy régebbi firmware-ekkel működik.</string>
<string name="error_firmware_corrupted">A firmware jelen van, de nem olvasható. Ellenőrizze a visszafejtési kulcsokat és szükség esetén dumpolja újra a firmware-t.</string>
<string name="error_firmware_too_new">A firmware túl új vagy nem olvasható. Az Eden csak a 19.0.1 vagy régebbi firmware-ekkel működik.</string>
<string name="keys_failed">A kulcsok telepítése sikertelen</string>
<string name="keys_install_success">A kulcsok sikeresen telepítve</string>

View File

@@ -364,9 +364,6 @@
<string name="firmware_uninstalling">Mencopot firmware...</string>
<string name="firmware_uninstalled_success">Firmware berhasil dicopot</string>
<string name="error_firmware_missing">Firmware diperlukan untuk menjalankan game dan aplikasi tertentu. Eden hanya bekerja dengan firmware 19.0.1 atau lebih lama.</string>
<string name="error_firmware_corrupted">Firmware dilaporkan ada tetapi tidak dapat dibaca. Periksa kunci dekripsi dan dump ulang firmware jika diperlukan.</string>
<string name="error_firmware_too_new">Firmware terlalu baru atau tidak dapat dibaca. Eden hanya bekerja dengan firmware 19.0.1 atau lebih lama.</string>
<string name="keys_failed">Gagal menginstal kunci</string>
<string name="keys_install_success">Kunci berhasil diinstal</string>

View File

@@ -360,9 +360,6 @@
<string name="firmware_uninstalling">Disinstallazione firmware...</string>
<string name="firmware_uninstalled_success">Firmware disinstallato con successo</string>
<string name="error_firmware_missing">Il firmware è richiesto per eseguire alcuni giochi e applicazioni. Eden funziona solo con firmware 19.0.1 o precedente.</string>
<string name="error_firmware_corrupted">Il firmware è presente ma non può essere letto. Controlla le chiavi di decrittazione e ridumpa il firmware se necessario.</string>
<string name="error_firmware_too_new">Il firmware è troppo recente o illeggibile. Eden supporta solo firmware 19.0.1 o precedenti.</string>
<string name="keys_failed">Installazione chiavi fallita</string>
<string name="keys_install_success">Chiavi installate con successo</string>

View File

@@ -333,9 +333,6 @@
<string name="firmware_uninstalling">ファームウェアをアンインストール中...</string>
<string name="firmware_uninstalled_success">ファームウェアのアンインストールが成功しました</string>
<string name="error_firmware_missing">一部のゲームとアプリケーションを実行するにはファームウェアが必要です。Edenは19.0.1以前のファームウェアでのみ動作します。</string>
<string name="error_firmware_corrupted">ファームウェアは存在しますが読み取れません。復号キーを確認し、必要に応じてファームウェアを再ダンプしてください。</string>
<string name="error_firmware_too_new">ファームウェアが新しすぎるか読み取れません。Edenは19.0.1以前のファームウェアのみに対応しています。</string>
<string name="keys_failed">キーのインストールに失敗</string>
<string name="keys_install_success">キーが正常にインストールされました</string>

View File

@@ -330,9 +330,6 @@
<string name="firmware_uninstalling">펌웨어 제거 중...</string>
<string name="firmware_uninstalled_success">펌웨어가 성공적으로 제거되었습니다</string>
<string name="error_firmware_missing">일부 게임 및 애플리케이션 실행을 위해 펌웨어가 필요합니다. Eden은 19.0.1 이하 버전의 펌웨어에서만 작동합니다.</string>
<string name="error_firmware_corrupted">펌웨어가 존재하지만 읽을 수 없습니다. 복호화 키를 확인하고 필요한 경우 펌웨어를 다시 덤프하세요.</string>
<string name="error_firmware_too_new">펌웨어가 너무 최신이거나 읽을 수 없습니다. Eden은 19.0.1 이전 버전의 펌웨어에서만 작동합니다.</string>
<string name="keys_failed">키 설치 실패</string>
<string name="keys_install_success">키가 성공적으로 설치됨</string>

View File

@@ -322,9 +322,6 @@
<string name="firmware_uninstalling">Avinstallerer firmware...</string>
<string name="firmware_uninstalled_success">Firmware avinstallert</string>
<string name="error_firmware_missing">Fastvare kreves for å kjøre enkelte spill og applikasjoner. Eden fungerer bare med fastvare 19.0.1 eller eldre.</string>
<string name="error_firmware_corrupted">Fastvare er til stede, men kan ikke leses. Sjekk dekrypteringsnøklene og dump fastvaren på nytt om nødvendig.</string>
<string name="error_firmware_too_new">Fastvaren er for ny eller kan ikke leses. Eden fungerer bare med fastvare 19.0.1 eller eldre.</string>
<string name="keys_failed">Kunne ikke installere nøkler</string>
<string name="keys_install_success">Nøklene ble installert</string>

View File

@@ -370,9 +370,6 @@
<string name="firmware_uninstalling">Odinstalowywanie firmware...</string>
<string name="firmware_uninstalled_success">Firmware odinstalowany pomyślnie</string>
<string name="error_firmware_missing">Firmware jest wymagany do uruchamiania niektórych gier i aplikacji. Eden działa tylko z firmwarem 19.0.1 lub starszym.</string>
<string name="error_firmware_corrupted">Oprogramowanie sprzętowe jest obecne, ale nie można go odczytać. Sprawdź klucze deszyfrujące i w razie potrzeby zrzuć oprogramowanie ponownie.</string>
<string name="error_firmware_too_new">Oprogramowanie sprzętowe jest zbyt nowe lub nie można go odczytać. Eden działa tylko z oprogramowaniem sprzętowym w wersji 19.0.1 lub starszej.</string>
<string name="keys_failed">Nie udało się zainstalować kluczy</string>
<string name="keys_install_success">Pomyślnie zainstalowano klucze</string>

View File

@@ -368,9 +368,6 @@
<string name="firmware_uninstalling">Desinstalando firmware</string>
<string name="firmware_uninstalled_success">Firmware desinstalado com sucesso</string>
<string name="error_firmware_missing">Firmware é necessário para executar certos jogos e aplicativos. Eden só funciona com firmware 19.0.1 ou anterior.</string>
<string name="error_firmware_corrupted">Firmware está presente mas não pode ser lido. Verifique as chaves de descriptografia e refaça o dump do firmware se necessário.</string>
<string name="error_firmware_too_new">Firmware é muito novo ou não pode ser lido. Eden só funciona com firmware 19.0.1 ou anterior.</string>
<string name="keys_failed">Falha ao instalar chaves (keys)</string>
<string name="keys_install_success">Chaves (keys) instaladas com sucesso</string>

View File

@@ -334,9 +334,6 @@
<string name="firmware_uninstalling">A desinstalar firmware...</string>
<string name="firmware_uninstalled_success">Firmware desinstalado com sucesso</string>
<string name="error_firmware_missing">É necessário firmware para executar alguns jogos e aplicações. O Eden só funciona com firmware 19.0.1 ou anterior.</string>
<string name="error_firmware_corrupted">Firmware está presente mas não pode ser lido. Verifique chaves de descriptografia e volte a fazer dump do firmware se necessário.</string>
<string name="error_firmware_too_new">Firmware é demasiado recente ou ilegível. O Eden só funciona com firmware 19.0.1 ou anterior.</string>
<string name="keys_failed">Falha ao instalar chaves</string>
<string name="keys_install_success">Chaves instaladas com sucesso</string>

View File

@@ -370,9 +370,6 @@
<string name="firmware_uninstalling">Удаление прошивки...</string>
<string name="firmware_uninstalled_success">Прошивка успешно удалена</string>
<string name="error_firmware_missing">Для запуска некоторых игр и системных апплетов требуется прошивка. Eden работает только с прошивкой версии 19.0.1 и ниже.</string>
<string name="error_firmware_corrupted">Прошивка обнаружена, но не может быть прочитана. Проверьте наличие ключей дешифрования и при необходимости пересохраните прошивку.</string>
<string name="error_firmware_too_new">Прошивка слишком новая или не может быть прочитана. Eden работает только с прошивкой версии 19.0.1 и ниже.</string>
<string name="keys_failed">Ошибка установки ключей</string>
<string name="keys_install_success">Ключи успешно установлены</string>

View File

@@ -334,9 +334,6 @@
<string name="firmware_uninstalling">Деинсталирање фирмвера</string>
<string name="firmware_uninstalled_success">Фирмваре је успешно деинсталирано</string>
<string name="error_firmware_missing">Firmware је потребан за покретање одређених игара и апликација. Eden ради само са firmware-ом 19.0.1 или старијим.</string>
<string name="error_firmware_corrupted">Firmware је присутан али се не може прочитати. Проверите кључеве за дешифровање и поново направите firmware дамп ако је потребно.</string>
<string name="error_firmware_too_new">Firmware је превише нов или се не може прочитати. Eden ради само са firmware-ом 19.0.1 или старијим.</string>
<string name="keys_failed">Неуспела инсталација кључева</string>
<string name="keys_install_success">Кључеви успешно инсталирани</string>

View File

@@ -370,9 +370,6 @@
<string name="firmware_uninstalling">Видалення прошивки...</string>
<string name="firmware_uninstalled_success">Прошивку успішно видалено</string>
<string name="error_firmware_missing">Для запуску деяких ігор та системних аплетів потрібна прошивка. Eden працює лише з прошивкою 19.0.1 або старішою.</string>
<string name="error_firmware_corrupted">Прошивка є, але не може бути прочитана. Перевірте ключі дешифрування та повторно зробіть дамп прошивки за необхідності.</string>
<string name="error_firmware_too_new">Прошивка занадто нова або нечитабельна. Eden працює лише з прошивкою 19.0.1 або старішою.</string>
<string name="keys_failed">Не вдалося встановити ключі</string>
<string name="keys_install_success">Ключі успішно встановлено</string>

View File

@@ -320,9 +320,6 @@
<string name="firmware_uninstalling">Đang gỡ cài đặt firmware...</string>
<string name="firmware_uninstalled_success">Gỡ cài đặt firmware thành công</string>
<string name="error_firmware_missing">Firmware là cần thiết để chạy một số trò chơi và ứng dụng. Eden chỉ hoạt động với firmware 19.0.1 trở về trước.</string>
<string name="error_firmware_corrupted">Firmware được báo cáo là có nhưng không thể đọc được. Kiểm tra khóa giải mã và dump lại firmware nếu cần.</string>
<string name="error_firmware_too_new">Firmware quá mới hoặc không thể đọc. Eden chỉ hoạt động với firmware 19.0.1 trở về trước.</string>
<string name="keys_failed">Không thể cài đặt khóa</string>
<string name="keys_install_success">Đã cài đặt khóa thành công</string>

View File

@@ -335,9 +335,6 @@
<string name="firmware_uninstalling">正在卸载固件...</string>
<string name="firmware_uninstalled_success">固件卸载成功</string>
<string name="error_firmware_missing">运行某些游戏和系统应用需要固件。Eden仅支持19.0.1及更早版本的固件。</string>
<string name="error_firmware_corrupted">检测到固件存在但无法读取。请检查解密密钥并在必要时重新转储固件。</string>
<string name="error_firmware_too_new">固件过新或无法读取。Eden仅支持19.0.1及更早版本的固件。</string>
<string name="keys_failed">密钥安装失败</string>
<string name="keys_install_success">密钥安装成功</string>

View File

@@ -364,9 +364,6 @@
<string name="firmware_uninstalling">正在解除安裝韌體...</string>
<string name="firmware_uninstalled_success">韌體解除安裝成功</string>
<string name="error_firmware_missing">執行某些遊戲和系統應用程式需要韌體。Eden僅支援19.0.1及更早版本的韌體。</string>
<string name="error_firmware_corrupted">檢測到韌體存在但無法讀取。請檢查解密金鑰並在必要時重新轉儲韌體。</string>
<string name="error_firmware_too_new">韌體過新或無法讀取。Eden僅支援19.0.1及更早版本的韌體。</string>
<string name="keys_failed">金鑰安裝失敗</string>
<string name="keys_install_success">金鑰安裝成功</string>

View File

@@ -376,9 +376,6 @@
<string name="firmware_uninstalling">Uninstalling firmware</string>
<string name="firmware_uninstalled_success">Firmware uninstalled successfully</string>
<string name="error_firmware_missing">Firmware is required to run certain games and use system applications. Eden only works with firmware 19.0.1 and earlier.</string>
<string name="error_firmware_corrupted">Firmware reported as present, but was unable to be read. Check for decryption keys and redump firmware if necessary.</string>
<string name="error_firmware_too_new">Firmware is too new or could not be read. Eden only works with firmware 19.0.1 and earlier.</string>
<string name="keys_failed">Failed to Install Keys</string>
<string name="keys_install_success">Keys successfully installed</string>
@@ -673,7 +670,6 @@
<string name="preferences_controls">Controls</string>
<string name="preferences_controls_description">Map controller input</string>
<string name="preferences_player">Player %d</string>
<string name="preferences_app_settings">App vSettings</string>
<string name="preferences_debug">Debug</string>
<string name="preferences_debug_description">CPU/GPU debugging, graphics API, fastmem</string>
@@ -804,11 +800,21 @@
<string name="emulation_pause">Pause emulation</string>
<string name="emulation_unpause">Unpause emulation</string>
<string name="emulation_input_overlay">Overlay options</string>
<string name="load_amiibo">Load Amiibo</string>
<string name="touchscreen">Touchscreen</string>
<string name="lock_drawer">Lock drawer</string>
<string name="unlock_drawer">Unlock drawer</string>
<string name="reset">Reset</string>
<!-- Amiibo -->
<string name="amiibo_title">Amiibo</string>
<string name="amiibo_removed_message">The current amiibo has been removed</string>
<string name="amiibo_wrong_state">The current game is not looking for amiibo</string>
<string name="amiibo_not_valid">The selected file is not a valid amiibo</string>
<string name="amiibo_in_use">The selected file is already in use</string>
<string name="amiibo_unknown_error">An unknown error occurred</string>
<string name="amiibo_load_success">Amiibo loaded</string>
<!-- Software keyboard -->
<string name="software_keyboard">Software keyboard</string>

View File

@@ -56,7 +56,6 @@ add_library(audio_core STATIC
out/audio_out.h
out/audio_out_system.cpp
out/audio_out_system.h
precompiled_headers.h
renderer/audio_device.cpp
renderer/audio_device.h
renderer/audio_renderer.h
@@ -266,8 +265,4 @@ if(ANDROID)
target_compile_definitions(audio_core PUBLIC HAVE_OBOE)
endif()
if (YUZU_USE_PRECOMPILED_HEADERS)
target_precompile_headers(audio_core PRIVATE precompiled_headers.h)
endif()
create_target_directory_groups(audio_core)

View File

@@ -1,6 +0,0 @@
// SPDX-FileCopyrightText: 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "common/common_precompiled_headers.h"

View File

@@ -22,110 +22,61 @@
namespace AudioCore::Sink {
void SinkStream::AppendBuffer(SinkBuffer& buffer, std::span<s16> samples) {
SCOPE_EXIT {
queue.EmplaceWait(buffer);
++queued_buffers;
};
if (type == StreamType::In) {
if (type == StreamType::In)
return;
}
constexpr s32 min{(std::numeric_limits<s16>::min)()};
constexpr s32 max{(std::numeric_limits<s16>::max)()};
auto yuzu_volume{Settings::Volume()};
if (yuzu_volume > 1.0f) {
yuzu_volume = 0.6f + 20 * std::log10(yuzu_volume);
}
auto volume{system_volume * device_volume * yuzu_volume};
if (system_channels == 6 && device_channels == 2) {
constexpr s32 min = (std::numeric_limits<s16>::min)();
constexpr s32 max = (std::numeric_limits<s16>::max)();
auto yuzu_volume = Settings::Volume();
if (yuzu_volume > 1.0f)
yuzu_volume = 0.6f + 20.0f * std::log10(yuzu_volume);
auto const volume = system_volume * device_volume * yuzu_volume;
if (system_channels > device_channels) {
// "Topological" coefficients, basically makes back sounds be less noisy :)
// Front = 1.0; Center = 0.596; LFE = 0.354; Back = 0.707
static constexpr std::array<f32, 4> tcoeff{1.0f, 0.596f, 0.354f, 0.707f};
// We're given 6 channels, but our device only outputs 2, so downmix.
// Front = 1.0
// Center = 0.596
// LFE = 0.354
// Back = 0.707
static constexpr std::array<f32, 4> down_mix_coeff{1.0f, 0.596f, 0.354f, 0.707f};
for (u32 read_index = 0, write_index = 0; read_index < samples.size();
read_index += system_channels, write_index += device_channels) {
const auto fl =
static_cast<f32>(samples[read_index + static_cast<u32>(Channels::FrontLeft)]);
const auto fr =
static_cast<f32>(samples[read_index + static_cast<u32>(Channels::FrontRight)]);
const auto c =
static_cast<f32>(samples[read_index + static_cast<u32>(Channels::Center)]);
const auto lfe =
static_cast<f32>(samples[read_index + static_cast<u32>(Channels::LFE)]);
const auto bl =
static_cast<f32>(samples[read_index + static_cast<u32>(Channels::BackLeft)]);
const auto br =
static_cast<f32>(samples[read_index + static_cast<u32>(Channels::BackRight)]);
const auto left_sample{
static_cast<s32>((fl * down_mix_coeff[0] + c * down_mix_coeff[1] +
lfe * down_mix_coeff[2] + bl * down_mix_coeff[3]) *
volume)};
const auto right_sample{
static_cast<s32>((fr * down_mix_coeff[0] + c * down_mix_coeff[1] +
lfe * down_mix_coeff[2] + br * down_mix_coeff[3]) *
volume)};
samples[write_index + static_cast<u32>(Channels::FrontLeft)] =
static_cast<s16>(std::clamp(left_sample, min, max));
samples[write_index + static_cast<u32>(Channels::FrontRight)] =
static_cast<s16>(std::clamp(right_sample, min, max));
for (u32 r_offs = 0, w_offs = 0; r_offs < samples.size(); r_offs += system_channels, w_offs += device_channels) {
std::array<f32, 6> ccoeff{0.f};
for (u32 i = 0; i < system_channels; ++i)
ccoeff[i] = f32(samples[r_offs + i]);
std::array<f32, 6> rcoeff{
ccoeff[u32(Channels::FrontLeft)],
ccoeff[u32(Channels::BackLeft)],
ccoeff[u32(Channels::Center)],
ccoeff[u32(Channels::LFE)],
ccoeff[u32(Channels::BackRight)],
ccoeff[u32(Channels::FrontRight)],
};
std::array<f32, 6> scoeff{
rcoeff[0] * tcoeff[0] + rcoeff[2] * tcoeff[1] + rcoeff[3] * tcoeff[2] + rcoeff[1] * tcoeff[3],
rcoeff[5] * tcoeff[0] + rcoeff[2] * tcoeff[1] + rcoeff[3] * tcoeff[2] + rcoeff[4] * tcoeff[3],
rcoeff[4] * tcoeff[0] + rcoeff[3] * tcoeff[1] + rcoeff[2] * tcoeff[2] + rcoeff[2] * tcoeff[3],
rcoeff[3] * tcoeff[0] + rcoeff[3] * tcoeff[1] + rcoeff[2] * tcoeff[2] + rcoeff[3] * tcoeff[3],
rcoeff[2] * tcoeff[0] + rcoeff[4] * tcoeff[1] + rcoeff[1] * tcoeff[2] + rcoeff[0] * tcoeff[3],
rcoeff[1] * tcoeff[0] + rcoeff[4] * tcoeff[1] + rcoeff[1] * tcoeff[2] + rcoeff[5] * tcoeff[3]
};
for (u32 i = 0; i < system_channels; ++i)
samples[w_offs + i] = s16(std::clamp(s32(scoeff[i] * volume), min, max));
}
samples_buffer.Push(samples.subspan(0, samples.size() / system_channels * device_channels));
return;
}
if (system_channels == 2 && device_channels == 6) {
} else if (system_channels < device_channels) {
// We need moar samples! Not all games will provide 6 channel audio.
// TODO: Implement some upmixing here. Currently just passthrough, with other
// channels left as silence.
std::vector<s16> new_samples(samples.size() / system_channels * device_channels);
for (u32 read_index = 0, write_index = 0; read_index < samples.size();
read_index += system_channels, write_index += device_channels) {
const auto left_sample{static_cast<s16>(std::clamp(
static_cast<s32>(
static_cast<f32>(samples[read_index + static_cast<u32>(Channels::FrontLeft)]) *
volume),
min, max))};
new_samples[write_index + static_cast<u32>(Channels::FrontLeft)] = left_sample;
const auto right_sample{static_cast<s16>(std::clamp(
static_cast<s32>(
static_cast<f32>(samples[read_index + static_cast<u32>(Channels::FrontRight)]) *
volume),
min, max))};
new_samples[write_index + static_cast<u32>(Channels::FrontRight)] = right_sample;
}
for (u32 r_offs = 0, w_offs = 0; r_offs < samples.size(); r_offs += system_channels, w_offs += device_channels)
for (u32 channel = 0; channel < system_channels; ++channel)
new_samples[w_offs + channel] = s16(std::clamp(s32(f32(samples[r_offs + channel]) * volume), min, max));
samples_buffer.Push(new_samples);
return;
} else {
for (u32 i = 0; i < samples.size() && volume != 1.0f; ++i)
samples[i] = s16(std::clamp(s32(f32(samples[i]) * volume), min, max));
samples_buffer.Push(samples);
}
if (volume != 1.0f) {
for (u32 i = 0; i < samples.size(); ++i) {
samples[i] = static_cast<s16>(
std::clamp(static_cast<s32>(static_cast<f32>(samples[i]) * volume), min, max));
}
}
samples_buffer.Push(samples);
queue.EmplaceWait(buffer);
++queued_buffers;
}
std::vector<s16> SinkStream::ReleaseBuffer(u64 num_samples) {
constexpr s32 min = (std::numeric_limits<s16>::min)();
constexpr s32 max = (std::numeric_limits<s16>::max)();
auto samples{samples_buffer.Pop(num_samples)};
// TODO: Up-mix to 6 channels if the game expects it.
@@ -133,23 +84,22 @@ std::vector<s16> SinkStream::ReleaseBuffer(u64 num_samples) {
// Incoming mic volume seems to always be very quiet, so multiply by an additional 8 here.
// TODO: Play with this and find something that works better.
constexpr s32 min = (std::numeric_limits<s16>::min)();
constexpr s32 max = (std::numeric_limits<s16>::max)();
auto volume{system_volume * device_volume * 8};
for (u32 i = 0; i < samples.size(); i++) {
samples[i] = static_cast<s16>(
std::clamp(static_cast<s32>(static_cast<f32>(samples[i]) * volume), min, max));
}
for (u32 i = 0; i < samples.size(); i++)
samples[i] = s16(std::clamp(s32(f32(samples[i]) * volume), min, max));
if (samples.size() < num_samples) {
if (samples.size() < num_samples)
samples.resize(num_samples, 0);
}
return samples;
}
void SinkStream::ClearQueue() {
samples_buffer.Pop();
SinkBuffer tmp;
while (queue.TryPop(tmp)) {
}
while (queue.TryPop(tmp))
;
queued_buffers = 0;
playing_buffer = {};
playing_buffer.consumed = true;
@@ -163,9 +113,8 @@ void SinkStream::ProcessAudioIn(std::span<const s16> input_buffer, std::size_t n
// If we're paused or going to shut down, we don't want to consume buffers as coretiming is
// paused and we'll desync, so just return.
if (system.IsPaused() || system.IsShuttingDown()) {
if (system.IsPaused() || system.IsShuttingDown())
return;
}
while (frames_written < num_frames) {
// If the playing buffer has been consumed or has no frames, we need a new one
@@ -195,9 +144,8 @@ void SinkStream::ProcessAudioIn(std::span<const s16> input_buffer, std::size_t n
// If that's all the frames in the current buffer, add its samples and mark it as
// consumed
if (playing_buffer.frames_played >= playing_buffer.frames) {
if (playing_buffer.frames_played >= playing_buffer.frames)
playing_buffer.consumed = true;
}
}
std::memcpy(&last_frame[0], &input_buffer[(frames_written - 1) * frame_size], frame_size_bytes);
@@ -222,9 +170,8 @@ void SinkStream::ProcessAudioOutAndRender(std::span<s16> output_buffer, std::siz
}
static constexpr std::array<s16, 6> silence{};
for (size_t i = frames_written; i < num_frames; i++) {
for (size_t i = frames_written; i < num_frames; i++)
std::memcpy(&output_buffer[i * frame_size], &silence[0], frame_size_bytes);
}
return;
}
@@ -234,17 +181,16 @@ void SinkStream::ProcessAudioOutAndRender(std::span<s16> output_buffer, std::siz
if (!queue.TryPop(playing_buffer)) {
// If no buffer was available we've underrun, fill the remaining buffer with
// the last written frame and continue.
for (size_t i = frames_written; i < num_frames; i++) {
for (size_t i = frames_written; i < num_frames; i++)
std::memcpy(&output_buffer[i * frame_size], &last_frame[0], frame_size_bytes);
}
frames_written = num_frames;
continue;
}
// Successfully dequeued a new buffer.
queued_buffers--;
{ std::unique_lock lk{release_mutex}; }
{
std::unique_lock lk{release_mutex};
queued_buffers--;
}
release_cv.notify_one();
}
@@ -291,10 +237,15 @@ u64 SinkStream::GetExpectedPlayedSampleCount() {
void SinkStream::WaitFreeSpace(std::stop_token stop_token) {
std::unique_lock lk{release_mutex};
release_cv.wait_for(lk, std::chrono::milliseconds(5),
[this]() { return paused || queued_buffers < max_queue_size; });
if (queued_buffers > max_queue_size + 3) {
release_cv.wait(lk, stop_token, [this] { return paused || queued_buffers < max_queue_size; });
const auto has_space = [this]() {
const u32 current_size = queued_buffers.load(std::memory_order_relaxed);
return paused || max_queue_size == 0 || current_size < max_queue_size;
};
if (!has_space()) {
// Wait until the queue falls below the configured limit or the stream is paused/stopped.
release_cv.wait(lk, stop_token, has_space);
}
}

View File

@@ -36,7 +36,6 @@ add_library(
cityhash.cpp
cityhash.h
common_funcs.h
common_precompiled_headers.h
common_types.h
concepts.h
container_hash.h
@@ -102,7 +101,6 @@ add_library(
param_package.h
parent_of_member.h
point.h
precompiled_headers.h
quaternion.h
range_map.h
range_mutex.h
@@ -155,6 +153,8 @@ add_library(
wall_clock.h
zstd_compression.cpp
zstd_compression.h
fs/ryujinx_compat.h fs/ryujinx_compat.cpp
fs/symlink.h fs/symlink.cpp
)
if(WIN32)
@@ -270,8 +270,4 @@ if(ANDROID)
target_link_libraries(common PRIVATE android)
endif()
if(YUZU_USE_PRECOMPILED_HEADERS)
target_precompile_headers(common PRIVATE precompiled_headers.h)
endif()
create_target_directory_groups(common)

View File

@@ -1,14 +0,0 @@
// SPDX-FileCopyrightText: 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <algorithm>
#include <array>
#include <chrono>
#include <memory>
#include <fmt/ranges.h>
#include "common/assert.h"
#include "common/common_types.h"

View File

@@ -33,6 +33,7 @@
#define SUDACHI_DIR "sudachi"
#define YUZU_DIR "yuzu"
#define SUYU_DIR "suyu"
#define RYUJINX_DIR "Ryujinx"
// yuzu-specific files
#define LOG_FILE "eden_log.txt"

View File

@@ -84,7 +84,7 @@ public:
return eden_paths.at(eden_path);
}
[[nodiscard]] const fs::path& GetLegacyPathImpl(LegacyPath legacy_path) {
[[nodiscard]] const fs::path& GetLegacyPathImpl(EmuPath legacy_path) {
return legacy_paths.at(legacy_path);
}
@@ -98,7 +98,7 @@ public:
eden_paths.insert_or_assign(eden_path, new_path);
}
void SetLegacyPathImpl(LegacyPath legacy_path, const fs::path& new_path) {
void SetLegacyPathImpl(EmuPath legacy_path, const fs::path& new_path) {
legacy_paths.insert_or_assign(legacy_path, new_path);
}
@@ -118,9 +118,9 @@ public:
}
eden_path_cache = eden_path / CACHE_DIR;
eden_path_config = eden_path / CONFIG_DIR;
#define LEGACY_PATH(titleName, upperName) GenerateLegacyPath(LegacyPath::titleName##Dir, GetAppDataRoamingDirectory() / upperName##_DIR); \
GenerateLegacyPath(LegacyPath::titleName##ConfigDir, GetAppDataRoamingDirectory() / upperName##_DIR / CONFIG_DIR); \
GenerateLegacyPath(LegacyPath::titleName##CacheDir, GetAppDataRoamingDirectory() / upperName##_DIR / CACHE_DIR);
#define LEGACY_PATH(titleName, upperName) GenerateLegacyPath(EmuPath::titleName##Dir, GetAppDataRoamingDirectory() / upperName##_DIR); \
GenerateLegacyPath(EmuPath::titleName##ConfigDir, GetAppDataRoamingDirectory() / upperName##_DIR / CONFIG_DIR); \
GenerateLegacyPath(EmuPath::titleName##CacheDir, GetAppDataRoamingDirectory() / upperName##_DIR / CACHE_DIR);
LEGACY_PATH(Citron, CITRON)
LEGACY_PATH(Sudachi, SUDACHI)
LEGACY_PATH(Yuzu, YUZU)
@@ -140,9 +140,9 @@ public:
eden_path_cache = eden_path / CACHE_DIR;
eden_path_config = eden_path / CONFIG_DIR;
}
#define LEGACY_PATH(titleName, upperName) GenerateLegacyPath(LegacyPath::titleName##Dir, GetDataDirectory("XDG_DATA_HOME") / upperName##_DIR); \
GenerateLegacyPath(LegacyPath::titleName##ConfigDir, GetDataDirectory("XDG_CONFIG_HOME") / upperName##_DIR); \
GenerateLegacyPath(LegacyPath::titleName##CacheDir, GetDataDirectory("XDG_CACHE_HOME") / upperName##_DIR);
#define LEGACY_PATH(titleName, upperName) GenerateLegacyPath(EmuPath::titleName##Dir, GetDataDirectory("XDG_DATA_HOME") / upperName##_DIR); \
GenerateLegacyPath(EmuPath::titleName##ConfigDir, GetDataDirectory("XDG_CONFIG_HOME") / upperName##_DIR); \
GenerateLegacyPath(EmuPath::titleName##CacheDir, GetDataDirectory("XDG_CACHE_HOME") / upperName##_DIR);
LEGACY_PATH(Citron, CITRON)
LEGACY_PATH(Sudachi, SUDACHI)
LEGACY_PATH(Yuzu, YUZU)
@@ -165,6 +165,15 @@ public:
GenerateEdenPath(EdenPath::ShaderDir, eden_path / SHADER_DIR);
GenerateEdenPath(EdenPath::TASDir, eden_path / TAS_DIR);
GenerateEdenPath(EdenPath::IconsDir, eden_path / ICONS_DIR);
#ifdef _WIN32
GenerateLegacyPath(EmuPath::RyujinxDir, GetAppDataRoamingDirectory() / RYUJINX_DIR);
#else
// In Ryujinx's infinite wisdom, it places EVERYTHING in the config directory on UNIX
// This is incredibly stupid and violates a million XDG standards, but whatever
GenerateLegacyPath(EmuPath::RyujinxDir, GetDataDirectory("XDG_CONFIG_HOME") / RYUJINX_DIR);
#endif
}
private:
@@ -179,12 +188,12 @@ private:
SetEdenPathImpl(eden_path, new_path);
}
void GenerateLegacyPath(LegacyPath legacy_path, const fs::path& new_path) {
void GenerateLegacyPath(EmuPath legacy_path, const fs::path& new_path) {
SetLegacyPathImpl(legacy_path, new_path);
}
std::unordered_map<EdenPath, fs::path> eden_paths;
std::unordered_map<LegacyPath, fs::path> legacy_paths;
std::unordered_map<EmuPath, fs::path> legacy_paths;
};
bool ValidatePath(const fs::path& path) {
@@ -272,7 +281,7 @@ const fs::path& GetEdenPath(EdenPath eden_path) {
return PathManagerImpl::GetInstance().GetEdenPathImpl(eden_path);
}
const std::filesystem::path& GetLegacyPath(LegacyPath legacy_path) {
const std::filesystem::path& GetLegacyPath(EmuPath legacy_path) {
return PathManagerImpl::GetInstance().GetLegacyPathImpl(legacy_path);
}
@@ -280,7 +289,7 @@ std::string GetEdenPathString(EdenPath eden_path) {
return PathToUTF8String(GetEdenPath(eden_path));
}
std::string GetLegacyPathString(LegacyPath legacy_path) {
std::string GetLegacyPathString(EmuPath legacy_path) {
return PathToUTF8String(GetLegacyPath(legacy_path));
}

View File

@@ -32,22 +32,26 @@ enum class EdenPath {
IconsDir, // Where Icons for Windows shortcuts are stored.
};
enum LegacyPath {
CitronDir, // Citron Directories for migration
// migration/compat dirs
enum EmuPath {
CitronDir,
CitronConfigDir,
CitronCacheDir,
SudachiDir, // Sudachi Directories for migration
SudachiDir,
SudachiConfigDir,
SudachiCacheDir,
YuzuDir, // Yuzu Directories for migration
YuzuDir,
YuzuConfigDir,
YuzuCacheDir,
SuyuDir, // Suyu Directories for migration
SuyuDir,
SuyuConfigDir,
SuyuCacheDir,
// used exclusively for save data linking
RyujinxDir,
};
/**
@@ -229,7 +233,7 @@ void SetAppDirectory(const std::string& app_directory);
*
* @returns The filesystem path associated with the LegacyPath enum.
*/
[[nodiscard]] const std::filesystem::path& GetLegacyPath(LegacyPath legacy_path);
[[nodiscard]] const std::filesystem::path& GetLegacyPath(EmuPath legacy_path);
/**
* Gets the filesystem path associated with the EdenPath enum as a UTF-8 encoded std::string.
@@ -247,7 +251,7 @@ void SetAppDirectory(const std::string& app_directory);
*
* @returns The filesystem path associated with the LegacyPath enum as a UTF-8 encoded std::string.
*/
[[nodiscard]] std::string GetLegacyPathString(LegacyPath legacy_path);
[[nodiscard]] std::string GetLegacyPathString(EmuPath legacy_path);
/**
* Sets a new filesystem path associated with the EdenPath enum.

View File

@@ -0,0 +1,93 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#include "ryujinx_compat.h"
#include "common/fs/path_util.h"
#include <cstddef>
#include <cstring>
#include <fmt/ranges.h>
#include <fstream>
namespace Common::FS {
namespace fs = std::filesystem;
fs::path GetKvdbPath()
{
return GetLegacyPath(EmuPath::RyujinxDir) / "bis" / "system" / "save" / "8000000000000000" / "0"
/ "imkvdb.arc";
}
fs::path GetRyuSavePath(const u64 &save_id)
{
std::string hex = fmt::format("{:016x}", save_id);
// TODO: what's the difference between 0 and 1?
return GetLegacyPath(EmuPath::RyujinxDir) / "bis" / "user" / "save" / hex / "0";
}
IMENReadResult ReadKvdb(const fs::path &path, std::vector<IMEN> &imens)
{
std::ifstream kvdb{path, std::ios::binary | std::ios::ate};
if (!kvdb) {
return IMENReadResult::Nonexistent;
}
size_t file_size = kvdb.tellg();
// IMKV header + 8 bytes
if (file_size < 0xB) {
return IMENReadResult::NoHeader;
}
// magic (not the wizard kind)
kvdb.seekg(0, std::ios::beg);
char header[12];
kvdb.read(header, 12);
if (std::memcmp(header, IMKV_MAGIC, 4) != 0) {
return IMENReadResult::InvalidMagic;
}
// calculate num. of imens left
std::size_t remaining = (file_size - 12);
std::size_t num_imens = remaining / IMEN_SIZE;
// File is misaligned and probably corrupt (rip)
if (remaining % IMEN_SIZE != 0) {
return IMENReadResult::Misaligned;
}
// if there aren't any IMENs, it's empty and we can safely no-op out of here
if (num_imens == 0) {
return IMENReadResult::NoImens;
}
imens.reserve(num_imens);
// initially I wanted to do a struct, but imkvdb is 140 bytes
// while the compiler will murder you if you try to align u64 to 4 bytes
for (std::size_t i = 0; i < num_imens; ++i) {
char magic[4];
u64 title_id = 0;
u64 save_id = 0;
kvdb.read(magic, 4);
if (std::memcmp(magic, IMEN_MAGIC, 4) != 0) {
return IMENReadResult::InvalidMagic;
}
kvdb.ignore(0x8);
kvdb.read(reinterpret_cast<char *>(&title_id), 8);
kvdb.ignore(0x38);
kvdb.read(reinterpret_cast<char *>(&save_id), 8);
kvdb.ignore(0x38);
imens.emplace_back(IMEN{title_id, save_id});
}
return IMENReadResult::Success;
}
} // namespace Common::FS

View File

@@ -0,0 +1,40 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
#include "common/common_types.h"
#include <filesystem>
#include <vector>
namespace fs = std::filesystem;
namespace Common::FS {
constexpr const char IMEN_MAGIC[4] = {0x49, 0x4d, 0x45, 0x4e};
constexpr const char IMKV_MAGIC[4] = {0x49, 0x4d, 0x4b, 0x56};
constexpr const u8 IMEN_SIZE = 0x8c;
fs::path GetKvdbPath();
fs::path GetRyuSavePath(const u64 &program_id);
enum class IMENReadResult {
Nonexistent, // ryujinx not found
NoHeader, // file isn't big enough for header
InvalidMagic, // no IMKV or IMEN header
Misaligned, // file isn't aligned to expected IMEN boundaries
NoImens, // no-op, there are no IMENs
Success, // :)
};
struct IMEN
{
u64 title_id;
u64 save_id;
};
static_assert(sizeof(IMEN) == 0x10, "IMEN has incorrect size.");
IMENReadResult ReadKvdb(const fs::path &path, std::vector<IMEN> &imens);
} // namespace Common::FS

43
src/common/fs/symlink.cpp Normal file
View File

@@ -0,0 +1,43 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#include "symlink.h"
#ifdef _WIN32
#include <windows.h>
#include <fmt/format.h>
#endif
namespace fs = std::filesystem;
// The sole purpose of this file is to treat symlinks like symlinks on POSIX,
// or treat them as directory junctions on Windows.
// This is because, for some inexplicable reason, Microsoft has locked symbolic
// links behind a "security policy", whereas directory junctions--functionally identical
// for directories, by the way--are not. Why? I don't know.
namespace Common::FS {
bool CreateSymlink(const fs::path &from, const fs::path &to)
{
#ifdef _WIN32
const std::string command = fmt::format("mklink /J {} {}", to.string(), from.string());
return system(command.c_str()) == 0;
#else
std::error_code ec;
fs::create_directory_symlink(from, to, ec);
return !ec;
#endif
}
bool IsSymlink(const fs::path &path)
{
#ifdef _WIN32
auto attributes = GetFileAttributesW(path.wstring().c_str());
return attributes & FILE_ATTRIBUTE_REPARSE_POINT;
#else
return fs::is_symlink(path);
#endif
}
} // namespace Common::FS

12
src/common/fs/symlink.h Normal file
View File

@@ -0,0 +1,12 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
#include <filesystem>
namespace Common::FS {
bool CreateSymlink(const std::filesystem::path &from, const std::filesystem::path &to);
bool IsSymlink(const std::filesystem::path &path);
} // namespace Common::FS

View File

@@ -53,11 +53,8 @@ constexpr const char* TrimSourcePath(std::string_view source) {
class Backend {
public:
virtual ~Backend() = default;
virtual void Write(const Entry& entry) = 0;
virtual void EnableForStacktrace() = 0;
virtual void Flush() = 0;
};
@@ -65,13 +62,11 @@ public:
class ColorConsoleBackend final : public Backend {
public:
explicit ColorConsoleBackend() = default;
~ColorConsoleBackend() override = default;
void Write(const Entry& entry) override {
if (enabled.load(std::memory_order_relaxed)) {
if (enabled.load(std::memory_order_relaxed))
PrintColoredMessage(entry);
}
}
void Flush() override {
@@ -97,51 +92,50 @@ public:
auto old_filename = filename;
old_filename += ".old.txt";
// Existence checks are done within the functions themselves.
// We don't particularly care if these succeed or not.
// Existence checks are done within the functions themselves.
// We don't particularly care if these succeed or not.
static_cast<void>(FS::RemoveFile(old_filename));
static_cast<void>(FS::RenameFile(filename, old_filename));
file = std::make_unique<FS::IOFile>(filename, FS::FileAccessMode::Write,
FS::FileType::TextFile);
file = std::make_unique<FS::IOFile>(filename, FS::FileAccessMode::Write, FS::FileType::TextFile);
}
~FileBackend() override = default;
void Write(const Entry& entry) override {
if (!enabled) {
if (!enabled)
return;
}
auto message = FormatLogMessage(entry).append(1, '\n');
#ifndef ANDROID
#ifndef __ANDROID__
if (Settings::values.censor_username.GetValue()) {
char* username = getenv("USER");
if (!username) {
username = getenv("USERNAME");
}
boost::replace_all(message, username, "user");
// This must be a static otherwise it would get checked on EVERY
// instance of logging an entry...
static std::string username = []() -> std::string {
auto* s = getenv("USER");
if (s == nullptr)
s = getenv("USERNAME");
return std::string{s};
}();
if (!username.empty())
boost::replace_all(message, username, "user");
}
#endif
bytes_written += file->WriteString(message);
// Option to log each line rather than 4k buffers
if (Settings::values.log_flush_line.GetValue()) {
if (Settings::values.log_flush_line.GetValue())
file->Flush();
}
using namespace Common::Literals;
// Prevent logs from exceeding a set maximum size in the event that log entries are spammed.
const auto write_limit = Settings::values.extended_logging.GetValue() ? 1_GiB : 100_MiB;
const bool write_limit_exceeded = bytes_written > write_limit;
if (entry.log_level >= Level::Error || write_limit_exceeded) {
if (write_limit_exceeded) {
// Stop writing after the write limit is exceeded.
// Don't close the file so we can print a stacktrace if necessary
// Stop writing after the write limit is exceeded.
// Don't close the file so we can print a stacktrace if necessary
if (write_limit_exceeded)
enabled = false;
}
file->Flush();
}
}
@@ -157,8 +151,8 @@ public:
private:
std::unique_ptr<FS::IOFile> file;
bool enabled = true;
std::size_t bytes_written = 0;
bool enabled = true;
};
/**
@@ -209,9 +203,8 @@ bool initialization_in_progress_suppress_logging = true;
class Impl {
public:
static Impl& Instance() {
if (!instance) {
if (!instance)
throw std::runtime_error("Using Logging instance before its initialization");
}
return *instance;
}
@@ -277,25 +270,21 @@ private:
};
while (!stop_token.stop_requested()) {
message_queue.PopWait(entry, stop_token);
if (entry.filename != nullptr) {
if (entry.filename != nullptr)
write_logs();
}
}
// Drain the logging queue. Only writes out up to MAX_LOGS_TO_WRITE to prevent a
// case where a system is repeatedly spamming logs even on close.
int max_logs_to_write = filter.IsDebug() ? INT_MAX : 100;
while (max_logs_to_write-- && message_queue.TryPop(entry)) {
while (max_logs_to_write-- && message_queue.TryPop(entry))
write_logs();
}
});
}
void StopBackendThread() {
backend_thread.request_stop();
if (backend_thread.joinable()) {
if (backend_thread.joinable())
backend_thread.join();
}
ForEachBackend([](Backend& backend) { backend.Flush(); });
}
@@ -304,7 +293,6 @@ private:
using std::chrono::duration_cast;
using std::chrono::microseconds;
using std::chrono::steady_clock;
return {
.timestamp = duration_cast<microseconds>(steady_clock::now() - time_origin),
.log_class = log_class,

View File

@@ -1,14 +1,17 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: 2014 Citra Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <array>
#include <cstdio>
#include <cstdint>
#include <string>
#ifdef _WIN32
#include <windows.h>
#endif
#ifdef ANDROID
#elif defined(__ANDROID__)
#include <android/log.h>
#endif
@@ -21,123 +24,77 @@
namespace Common::Log {
std::string FormatLogMessage(const Entry& entry) {
unsigned int time_seconds = static_cast<unsigned int>(entry.timestamp.count() / 1000000);
unsigned int time_fractional = static_cast<unsigned int>(entry.timestamp.count() % 1000000);
const char* class_name = GetLogClassName(entry.log_class);
const char* level_name = GetLevelName(entry.log_level);
auto const time_seconds = uint32_t(entry.timestamp.count() / 1000000);
auto const time_fractional = uint32_t(entry.timestamp.count() % 1000000);
char const* class_name = GetLogClassName(entry.log_class);
char const* level_name = GetLevelName(entry.log_level);
return fmt::format("[{:4d}.{:06d}] {} <{}> {}:{}:{}: {}", time_seconds, time_fractional,
class_name, level_name, entry.filename, entry.function, entry.line_num,
entry.message);
}
void PrintMessage(const Entry& entry) {
const auto str = FormatLogMessage(entry).append(1, '\n');
fputs(str.c_str(), stderr);
#ifdef _WIN32
auto const str = FormatLogMessage(entry).append(1, '\n');
#else
#define ESC "\x1b"
auto str = std::string{[&entry]() -> const char* {
switch (entry.log_level) {
case Level::Debug: return ESC "[0;36m"; // Cyan
case Level::Info: return ESC "[0;37m"; // Bright gray
case Level::Warning: return ESC "[1;33m"; // Bright yellow
case Level::Error: return ESC "[1;31m"; // Bright red
case Level::Critical: return ESC "[1;35m"; // Bright magenta
default: return ESC "[1;30m"; // Grey
}
}()};
str.append(FormatLogMessage(entry));
str.append(ESC "[0m\n");
#undef ESC
#endif
fwrite(str.c_str(), 1, str.size(), stderr);
}
void PrintColoredMessage(const Entry& entry) {
#ifdef _WIN32
HANDLE console_handle = GetStdHandle(STD_ERROR_HANDLE);
if (console_handle == INVALID_HANDLE_VALUE) {
if (console_handle == INVALID_HANDLE_VALUE)
return;
}
CONSOLE_SCREEN_BUFFER_INFO original_info = {};
GetConsoleScreenBufferInfo(console_handle, &original_info);
WORD color = 0;
switch (entry.log_level) {
case Level::Trace: // Grey
color = FOREGROUND_INTENSITY;
break;
case Level::Debug: // Cyan
color = FOREGROUND_GREEN | FOREGROUND_BLUE;
break;
case Level::Info: // Bright gray
color = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE;
break;
case Level::Warning: // Bright yellow
color = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_INTENSITY;
break;
case Level::Error: // Bright red
color = FOREGROUND_RED | FOREGROUND_INTENSITY;
break;
case Level::Critical: // Bright magenta
color = FOREGROUND_RED | FOREGROUND_BLUE | FOREGROUND_INTENSITY;
break;
case Level::Count:
UNREACHABLE();
}
WORD color = WORD([&entry]() {
switch (entry.log_level) {
case Level::Debug: return FOREGROUND_GREEN | FOREGROUND_BLUE; // Cyan
case Level::Info: return FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE; // Bright gray
case Level::Warning: return FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_INTENSITY;
case Level::Error: return FOREGROUND_RED | FOREGROUND_INTENSITY;
case Level::Critical: return FOREGROUND_RED | FOREGROUND_BLUE | FOREGROUND_INTENSITY;
default: break;
}
return FOREGROUND_INTENSITY; // Grey
}());
SetConsoleTextAttribute(console_handle, color);
#else
#define ESC "\x1b"
const char* color = "";
switch (entry.log_level) {
case Level::Trace: // Grey
color = ESC "[1;30m";
break;
case Level::Debug: // Cyan
color = ESC "[0;36m";
break;
case Level::Info: // Bright gray
color = ESC "[0;37m";
break;
case Level::Warning: // Bright yellow
color = ESC "[1;33m";
break;
case Level::Error: // Bright red
color = ESC "[1;31m";
break;
case Level::Critical: // Bright magenta
color = ESC "[1;35m";
break;
case Level::Count:
UNREACHABLE();
}
fputs(color, stderr);
#endif
PrintMessage(entry);
#ifdef _WIN32
SetConsoleTextAttribute(console_handle, original_info.wAttributes);
#else
fputs(ESC "[0m", stderr);
#undef ESC
#endif
}
void PrintMessageToLogcat(const Entry& entry) {
#ifdef ANDROID
const auto str = FormatLogMessage(entry);
android_LogPriority android_log_priority;
switch (entry.log_level) {
case Level::Trace:
android_log_priority = ANDROID_LOG_VERBOSE;
break;
case Level::Debug:
android_log_priority = ANDROID_LOG_DEBUG;
break;
case Level::Info:
android_log_priority = ANDROID_LOG_INFO;
break;
case Level::Warning:
android_log_priority = ANDROID_LOG_WARN;
break;
case Level::Error:
android_log_priority = ANDROID_LOG_ERROR;
break;
case Level::Critical:
android_log_priority = ANDROID_LOG_FATAL;
break;
case Level::Count:
UNREACHABLE();
}
android_LogPriority android_log_priority = [&]() {
switch (entry.log_level) {
case Level::Debug: return ANDROID_LOG_DEBUG;
case Level::Info: return ANDROID_LOG_INFO;
case Level::Warning: return ANDROID_LOG_WARN;
case Level::Error: return ANDROID_LOG_ERROR;
case Level::Critical: return ANDROID_LOG_FATAL;
case Level::Count:
case Level::Trace: return ANDROID_LOG_VERBOSE;
}
}();
auto const str = FormatLogMessage(entry);
__android_log_print(android_log_priority, "YuzuNative", "%s", str.c_str());
#endif
}

View File

@@ -1,6 +0,0 @@
// SPDX-FileCopyrightText: 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "common/common_precompiled_headers.h"

View File

@@ -622,6 +622,10 @@ struct Values {
// Linux
SwitchableSetting<bool> enable_gamemode{linkage, true, "enable_gamemode", Category::Linux};
#ifdef __unix__
SwitchableSetting<bool> gui_force_x11{linkage, false, "gui_force_x11", Category::Linux};
Setting<bool> gui_hide_backend_warning{linkage, false, "gui_hide_backend_warning", Category::Linux};
#endif
// Controls
InputSetting<std::array<PlayerInput, 10>> players;

View File

@@ -708,6 +708,8 @@ add_library(core STATIC
hle/service/kernel_helpers.h
hle/service/lbl/lbl.cpp
hle/service/lbl/lbl.h
hle/service/ldn/client_process_monitor.cpp
hle/service/ldn/client_process_monitor.h
hle/service/ldn/lan_discovery.cpp
hle/service/ldn/lan_discovery.h
hle/service/ldn/ldn.cpp
@@ -1144,7 +1146,6 @@ add_library(core STATIC
memory/dmnt_cheat_vm.h
perf_stats.cpp
perf_stats.h
precompiled_headers.h
reporter.cpp
reporter.h
tools/freezer.cpp
@@ -1179,13 +1180,14 @@ if (MSVC)
else()
target_compile_options(core PRIVATE
-Werror=conversion
-Wno-sign-conversion
-Wno-cast-function-type
$<$<CXX_COMPILER_ID:Clang>:-fsized-deallocation>
$<$<CXX_COMPILER_ID:Clang>:-Wno-cast-function-type-mismatch>
)
# pre-clang19 will spam with "OH DID YOU MEAN THIS?" otherwise...
if (CXX_CLANG AND CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 19)
target_compile_options(core PRIVATE -Wno-cast-function-type-mismatch)
endif()
endif()
target_include_directories(core PRIVATE ${OPUS_INCLUDE_DIRS})
@@ -1199,7 +1201,7 @@ endif()
target_link_libraries(core PRIVATE fmt::fmt nlohmann_json::nlohmann_json RenderDoc::API MbedTLS::mbedcrypto MbedTLS::mbedtls)
if (MINGW)
target_link_libraries(core PRIVATE ${MSWSOCK_LIBRARY})
target_link_libraries(core PRIVATE ws2_32 mswsock wlanapi)
endif()
if (ENABLE_WEB_SERVICE)
@@ -1269,9 +1271,4 @@ else()
hle/service/ssl/ssl_backend_none.cpp)
endif()
if (YUZU_USE_PRECOMPILED_HEADERS)
target_precompile_headers(core PRIVATE precompiled_headers.h)
endif()
create_target_directory_groups(core)

View File

@@ -210,12 +210,9 @@ std::shared_ptr<Dynarmic::A32::Jit> ArmDynarmic32::MakeJit(Common::PageTable* pa
config.wall_clock_cntpct = m_uses_wall_clock;
config.enable_cycle_counting = !m_uses_wall_clock;
// Code cache size
#ifdef ARCHITECTURE_arm64
// Code cache size - max in ARM is 128MiB, max in x86_64 is 2GiB
// Solaris doesn't support kPageSize >= 512MiB
config.code_cache_size = std::uint32_t(128_MiB);
#else
config.code_cache_size = std::uint32_t(512_MiB);
#endif
// Allow memory fault handling to work
if (m_system.DebuggerEnabled()) {

View File

@@ -269,12 +269,9 @@ std::shared_ptr<Dynarmic::A64::Jit> ArmDynarmic64::MakeJit(Common::PageTable* pa
config.wall_clock_cntpct = m_uses_wall_clock;
config.enable_cycle_counting = !m_uses_wall_clock;
// Code cache size
#ifdef ARCHITECTURE_arm64
// Code cache size - max in ARM is 128MiB, max in x86_64 is 2GiB
// Solaris doesn't support kPageSize >= 512MiB
config.code_cache_size = std::uint32_t(128_MiB);
#else
config.code_cache_size = std::uint32_t(512_MiB);
#endif
// Allow memory fault handling to work
if (m_system.DebuggerEnabled()) {

View File

@@ -4,8 +4,9 @@
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <algorithm>
#include <array>
#include <vector>
#include <cstring>
#include <mbedtls/cipher.h>
#include "common/assert.h"
#include "common/logging/log.h"
@@ -15,6 +16,7 @@
namespace Core::Crypto {
namespace {
using NintendoTweak = std::array<u8, 16>;
constexpr std::size_t AesBlockBytes = 16;
NintendoTweak CalculateNintendoTweak(std::size_t sector_id) {
NintendoTweak out{};
@@ -75,39 +77,51 @@ void AESCipher<Key, KeySize>::Transcode(const u8* src, std::size_t size, u8* des
mbedtls_cipher_reset(context);
// Only ECB strictly requires block sized chunks.
if (size == 0)
return;
const auto mode = mbedtls_cipher_get_cipher_mode(context);
std::size_t written = 0;
if (mbedtls_cipher_get_cipher_mode(context) != MBEDTLS_MODE_ECB) {
mbedtls_cipher_update(context, src, size, dest, &written);
if (written != size)
if (mode != MBEDTLS_MODE_ECB) {
const int ret = mbedtls_cipher_update(context, src, size, dest, &written);
ASSERT(ret == 0);
if (written != size) {
LOG_WARNING(Crypto, "Not all data was processed requested={:016X}, actual={:016X}.", size, written);
}
return;
}
// ECB path: operate in block sized chunks and mirror previous behavior.
const auto block_size = mbedtls_cipher_get_block_size(context);
if (size < block_size) {
std::vector<u8> block(block_size);
std::memcpy(block.data(), src, size);
Transcode(block.data(), block.size(), block.data(), op);
std::memcpy(dest, block.data(), size);
return;
}
ASSERT(block_size <= AesBlockBytes);
for (std::size_t offset = 0; offset < size; offset += block_size) {
const auto length = std::min<std::size_t>(block_size, size - offset);
mbedtls_cipher_update(context, src + offset, length, dest + offset, &written);
if (written != length) {
if (length < block_size) {
std::vector<u8> block(block_size);
std::memcpy(block.data(), src + offset, length);
Transcode(block.data(), block.size(), block.data(), op);
std::memcpy(dest + offset, block.data(), length);
return;
}
LOG_WARNING(Crypto, "Not all data was processed requested={:016X}, actual={:016X}.", length, written);
const std::size_t whole_block_bytes = size - (size % block_size);
if (whole_block_bytes != 0) {
const int ret = mbedtls_cipher_update(context, src, whole_block_bytes, dest, &written);
ASSERT(ret == 0);
if (written != whole_block_bytes) {
LOG_WARNING(Crypto, "Not all data was processed requested={:016X}, actual={:016X}.",
whole_block_bytes, written);
}
}
const std::size_t tail = size - whole_block_bytes;
if (tail == 0)
return;
std::array<u8, AesBlockBytes> tail_buffer{};
std::memcpy(tail_buffer.data(), src + whole_block_bytes, tail);
std::size_t tail_written = 0;
const int ret = mbedtls_cipher_update(context, tail_buffer.data(), block_size, tail_buffer.data(),
&tail_written);
ASSERT(ret == 0);
if (tail_written != block_size) {
LOG_WARNING(Crypto, "Not all data was processed requested={:016X}, actual={:016X}.", block_size,
tail_written);
}
std::memcpy(dest + whole_block_bytes, tail_buffer.data(), tail);
}
template <typename Key, std::size_t KeySize>

View File

@@ -5,6 +5,7 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#include <algorithm>
#include <array>
#include <cstring>
#include "core/crypto/ctr_encryption_layer.h"
@@ -18,35 +19,84 @@ std::size_t CTREncryptionLayer::Read(u8* data, std::size_t length, std::size_t o
if (length == 0)
return 0;
constexpr std::size_t BlockSize = 0x10;
constexpr std::size_t MaxChunkSize = 0x10000;
std::size_t total_read = 0;
// Handle an initial misaligned portion if needed.
if (auto const sector_offset = offset & 0xF; sector_offset != 0) {
const std::size_t aligned_off = offset - sector_offset;
std::array<u8, 0x10> block{};
if (auto const got = base->Read(block.data(), block.size(), aligned_off); got != 0) {
UpdateIV(base_offset + aligned_off);
cipher.Transcode(block.data(), got, block.data(), Op::Decrypt);
auto const to_copy = std::min<std::size_t>(length, got > sector_offset ? got - sector_offset : 0);
if (to_copy > 0) {
std::memcpy(data, block.data() + sector_offset, to_copy);
data += to_copy;
offset += to_copy;
length -= to_copy;
total_read += to_copy;
}
} else {
return 0;
auto* out = data;
std::size_t remaining = length;
std::size_t current_offset = offset;
const auto read_exact = [this](u8* dst, std::size_t bytes, std::size_t src_offset) {
std::size_t filled = 0;
while (filled < bytes) {
const std::size_t got = base->Read(dst + filled, bytes - filled, src_offset + filled);
if (got == 0)
break;
filled += got;
}
return filled;
};
if (const std::size_t intra_block = current_offset & (BlockSize - 1); intra_block != 0) {
std::array<u8, BlockSize> block{};
const std::size_t aligned_offset = current_offset - intra_block;
const std::size_t got = read_exact(block.data(), BlockSize, aligned_offset);
if (got <= intra_block)
return total_read;
UpdateIV(base_offset + aligned_offset);
cipher.Transcode(block.data(), got, block.data(), Op::Decrypt);
const std::size_t available = got - intra_block;
const std::size_t to_copy = std::min<std::size_t>(remaining, available);
std::memcpy(out, block.data() + intra_block, to_copy);
out += to_copy;
current_offset += to_copy;
remaining -= to_copy;
total_read += to_copy;
if (to_copy != available)
return total_read;
}
if (length > 0) {
// Now aligned to 0x10
UpdateIV(base_offset + offset);
const std::size_t got = base->Read(data, length, offset);
if (got > 0) {
cipher.Transcode(data, got, data, Op::Decrypt);
total_read += got;
}
while (remaining >= BlockSize) {
const std::size_t chunk_request = std::min<std::size_t>(remaining, MaxChunkSize);
const std::size_t aligned_request = chunk_request - (chunk_request % BlockSize);
if (aligned_request == 0)
break;
const std::size_t got = read_exact(out, aligned_request, current_offset);
if (got == 0)
break;
UpdateIV(base_offset + current_offset);
cipher.Transcode(out, got, out, Op::Decrypt);
out += got;
current_offset += got;
remaining -= got;
total_read += got;
if (got < aligned_request)
return total_read;
}
if (remaining > 0) {
std::array<u8, BlockSize> block{};
const std::size_t got = read_exact(block.data(), BlockSize, current_offset);
if (got == 0)
return total_read;
UpdateIV(base_offset + current_offset);
cipher.Transcode(block.data(), got, block.data(), Op::Decrypt);
const std::size_t to_copy = std::min<std::size_t>(remaining, got);
std::memcpy(out, block.data(), to_copy);
total_read += to_copy;
}
return total_read;
}

View File

@@ -20,67 +20,49 @@ std::size_t XTSEncryptionLayer::Read(u8* data, std::size_t length, std::size_t o
if (length == 0)
return 0;
constexpr std::size_t PrefetchSectors = 4;
auto* out = data;
std::size_t remaining = length;
std::size_t current_offset = offset;
std::size_t total_read = 0;
// Handle initial unaligned part within a sector.
if (auto const sector_offset = offset % XTS_SECTOR_SIZE; sector_offset != 0) {
const std::size_t aligned_off = offset - sector_offset;
std::array<u8, XTS_SECTOR_SIZE> block{};
if (auto const got = base->Read(block.data(), XTS_SECTOR_SIZE, aligned_off); got > 0) {
if (got < XTS_SECTOR_SIZE)
std::memset(block.data() + got, 0, XTS_SECTOR_SIZE - got);
cipher.XTSTranscode(block.data(), XTS_SECTOR_SIZE, block.data(), aligned_off / XTS_SECTOR_SIZE,
XTS_SECTOR_SIZE, Op::Decrypt);
auto const to_copy = std::min<std::size_t>(length, got > sector_offset ? got - sector_offset : 0);
if (to_copy > 0) {
std::memcpy(data, block.data() + sector_offset, to_copy);
data += to_copy;
offset += to_copy;
length -= to_copy;
total_read += to_copy;
}
} else {
return 0;
}
}
std::array<u8, XTS_SECTOR_SIZE> sector{};
if (length > 0) {
// Process aligned middle inplace, in sector sized multiples.
while (length >= XTS_SECTOR_SIZE) {
const std::size_t req = (length / XTS_SECTOR_SIZE) * XTS_SECTOR_SIZE;
const std::size_t got = base->Read(data, req, offset);
if (got == 0) {
while (remaining > 0) {
const std::size_t sector_index = current_offset / XTS_SECTOR_SIZE;
const std::size_t sector_offset = current_offset % XTS_SECTOR_SIZE;
const std::size_t sectors_to_read = std::min<std::size_t>(PrefetchSectors,
(remaining + sector_offset +
XTS_SECTOR_SIZE - 1) /
XTS_SECTOR_SIZE);
for (std::size_t s = 0; s < sectors_to_read && remaining > 0; ++s) {
const std::size_t index = sector_index + s;
const std::size_t read_offset = index * XTS_SECTOR_SIZE;
const std::size_t got = base->Read(sector.data(), XTS_SECTOR_SIZE, read_offset);
if (got == 0)
return total_read;
}
const std::size_t got_rounded = got - (got % XTS_SECTOR_SIZE);
if (got_rounded > 0) {
cipher.XTSTranscode(data, got_rounded, data, offset / XTS_SECTOR_SIZE, XTS_SECTOR_SIZE, Op::Decrypt);
data += got_rounded;
offset += got_rounded;
length -= got_rounded;
total_read += got_rounded;
}
// If we didn't get a full sector next, break to handle tail.
if (got_rounded != got) {
break;
}
}
// Handle tail within a sector, if any.
if (length > 0) {
std::array<u8, XTS_SECTOR_SIZE> block{};
const std::size_t got = base->Read(block.data(), XTS_SECTOR_SIZE, offset);
if (got > 0) {
if (got < XTS_SECTOR_SIZE) {
std::memset(block.data() + got, 0, XTS_SECTOR_SIZE - got);
}
cipher.XTSTranscode(block.data(), XTS_SECTOR_SIZE, block.data(),
offset / XTS_SECTOR_SIZE, XTS_SECTOR_SIZE, Op::Decrypt);
const std::size_t to_copy = std::min<std::size_t>(length, got);
std::memcpy(data, block.data(), to_copy);
total_read += to_copy;
}
if (got < XTS_SECTOR_SIZE)
std::memset(sector.data() + got, 0, XTS_SECTOR_SIZE - got);
cipher.XTSTranscode(sector.data(), XTS_SECTOR_SIZE, sector.data(), index, XTS_SECTOR_SIZE,
Op::Decrypt);
const std::size_t local_offset = (s == 0) ? sector_offset : 0;
const std::size_t available = XTS_SECTOR_SIZE - local_offset;
const std::size_t to_copy = std::min<std::size_t>(available, remaining);
std::memcpy(out, sector.data() + local_offset, to_copy);
out += to_copy;
current_offset += to_copy;
remaining -= to_copy;
total_read += to_copy;
}
}
return total_read;
}
} // namespace Core::Crypto

View File

@@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2025 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
@@ -28,26 +31,18 @@ class DebuggerBackend {
public:
virtual ~DebuggerBackend() = default;
/**
* Can be invoked from a callback to synchronously wait for more data.
* Will return as soon as least one byte is received. Reads up to 4096 bytes.
*/
/// Can be invoked from a callback to synchronously wait for more data.
/// Will return as soon as least one byte is received. Reads up to 4096 bytes.
virtual std::span<const u8> ReadFromClient() = 0;
/**
* Can be invoked from a callback to write data to the client.
* Returns immediately after the data is sent.
*/
/// Can be invoked from a callback to write data to the client.
/// Returns immediately after the data is sent.
virtual void WriteToClient(std::span<const u8> data) = 0;
/**
* Gets the currently active thread when the debugger is stopped.
*/
/// Gets the currently active thread when the debugger is stopped.
virtual Kernel::KThread* GetActiveThread() = 0;
/**
* Sets the currently active thread when the debugger is stopped.
*/
/// Sets the currently active thread when the debugger is stopped.
virtual void SetActiveThread(Kernel::KThread* thread) = 0;
};
@@ -57,30 +52,20 @@ public:
virtual ~DebuggerFrontend() = default;
/**
* Called after the client has successfully connected to the port.
*/
/// Called after the client has successfully connected to the port.
virtual void Connected() = 0;
/**
* Called when emulation has stopped.
*/
/// Called when emulation has stopped.
virtual void Stopped(Kernel::KThread* thread) = 0;
/**
* Called when emulation is shutting down.
*/
/// Called when emulation is shutting down.
virtual void ShuttingDown() = 0;
/*
* Called when emulation has stopped on a watchpoint.
*/
/// Called when emulation has stopped on a watchpoint.
virtual void Watchpoint(Kernel::KThread* thread, const Kernel::DebugWatchpoint& watch) = 0;
/**
* Called when new data is asynchronously received on the client socket.
* A list of actions to perform is returned.
*/
/// Called when new data is asynchronously received on the client socket.
/// A list of actions to perform is returned.
[[nodiscard]] virtual std::vector<DebuggerAction> ClientData(std::span<const u8> data) = 0;
protected:

View File

@@ -49,28 +49,14 @@ static u8 CalculateChecksum(std::string_view data) {
static std::string EscapeGDB(std::string_view data) {
std::string escaped;
escaped.reserve(data.size());
for (char c : data) {
for (char const c : data)
switch (c) {
case '#':
escaped += "}\x03";
break;
case '$':
escaped += "}\x04";
break;
case '*':
escaped += "}\x0a";
break;
case '}':
escaped += "}\x5d";
break;
default:
escaped += c;
break;
case '#': escaped += "}\x03"; break;
case '$': escaped += "}\x04"; break;
case '*': escaped += "}\x0a"; break;
case '}': escaped += "}\x5d"; break;
default: escaped += c; break;
}
}
return escaped;
}
@@ -82,38 +68,26 @@ static std::string EscapeXML(std::string_view data) {
}
std::string escaped;
escaped.reserve(data.size());
for (char32_t c : converted) {
for (char32_t const c : converted)
switch (c) {
case '&':
escaped += "&amp;";
break;
case '"':
escaped += "&quot;";
break;
case '<':
escaped += "&lt;";
break;
case '>':
escaped += "&gt;";
break;
case '&': escaped += "&amp;"; break;
case '"': escaped += "&quot;"; break;
case '<': escaped += "&lt;"; break;
case '>': escaped += "&gt;"; break;
default:
if (c > 0x7f) {
escaped += fmt::format("&#{};", static_cast<u32>(c));
escaped += fmt::format("&#{};", u32(c));
} else {
escaped += static_cast<char>(c);
escaped += char(c);
}
break;
}
}
return escaped;
}
GDBStub::GDBStub(DebuggerBackend& backend_, Core::System& system_, Kernel::KProcess* debug_process_)
: DebuggerFrontend(backend_), system{system_}, debug_process{debug_process_} {
if (GetProcess()->Is64Bit()) {
if (debug_process->Is64Bit()) {
arch = std::make_unique<GDBStubA64>();
} else {
arch = std::make_unique<GDBStubA32>();
@@ -148,56 +122,42 @@ void GDBStub::Watchpoint(Kernel::KThread* thread, const Kernel::DebugWatchpoint&
}
std::vector<DebuggerAction> GDBStub::ClientData(std::span<const u8> data) {
std::vector<DebuggerAction> actions;
current_command.insert(current_command.end(), data.begin(), data.end());
while (current_command.size() != 0) {
std::vector<DebuggerAction> actions;
while (!current_command.empty())
ProcessData(actions);
}
return actions;
}
void GDBStub::ProcessData(std::vector<DebuggerAction>& actions) {
const char c{current_command[0]};
const char c = current_command[0];
// Acknowledgement
if (c == GDB_STUB_ACK || c == GDB_STUB_NACK) {
current_command.erase(current_command.begin());
return;
}
// Interrupt
if (c == GDB_STUB_INT3) {
} else if (c == GDB_STUB_INT3) {
LOG_INFO(Debug_GDBStub, "Received interrupt");
current_command.erase(current_command.begin());
actions.push_back(DebuggerAction::Interrupt);
SendStatus(GDB_STUB_ACK);
return;
}
// Otherwise, require the data to be the start of a command
if (c != GDB_STUB_START) {
} else if (c != GDB_STUB_START) {
LOG_ERROR(Debug_GDBStub, "Invalid command buffer contents: {}", current_command.data());
current_command.clear();
SendStatus(GDB_STUB_NACK);
return;
}
// Continue reading until command is complete
while (CommandEnd() == current_command.end()) {
const auto new_data{backend.ReadFromClient()};
current_command.insert(current_command.end(), new_data.begin(), new_data.end());
}
// Execute and respond to GDB
const auto command{DetachCommand()};
if (command) {
SendStatus(GDB_STUB_ACK);
ExecuteCommand(*command, actions);
} else {
SendStatus(GDB_STUB_NACK);
// Continue reading until command is complete
while (CommandEnd() == current_command.end()) {
const auto new_data{backend.ReadFromClient()};
current_command.insert(current_command.end(), new_data.begin(), new_data.end());
}
// Execute and respond to GDB
if (auto const cmd = DetachCommand(); cmd) {
SendStatus(GDB_STUB_ACK);
ExecuteCommand(*cmd, actions);
} else {
SendStatus(GDB_STUB_NACK);
}
}
}
@@ -207,9 +167,7 @@ void GDBStub::ExecuteCommand(std::string_view packet, std::vector<DebuggerAction
if (packet.length() == 0) {
SendReply(GDB_STUB_REPLY_ERR);
return;
}
if (packet.starts_with("vCont")) {
} else if (packet.starts_with("vCont")) {
HandleVCont(packet.substr(5), actions);
return;
}
@@ -218,14 +176,8 @@ void GDBStub::ExecuteCommand(std::string_view packet, std::vector<DebuggerAction
switch (packet[0]) {
case 'H': {
Kernel::KThread* thread{nullptr};
s64 thread_id{strtoll(command.data() + 1, nullptr, 16)};
if (thread_id >= 1) {
thread = GetThreadByID(thread_id);
} else {
thread = backend.GetActiveThread();
}
s64 thread_id = strtoll(command.data() + 1, nullptr, 16);
Kernel::KThread* thread = thread_id >= 1 ? GetThreadByID(thread_id) : backend.GetActiveThread();
if (thread) {
SendReply(GDB_STUB_REPLY_OK);
backend.SetActiveThread(thread);
@@ -235,7 +187,7 @@ void GDBStub::ExecuteCommand(std::string_view packet, std::vector<DebuggerAction
break;
}
case 'T': {
s64 thread_id{strtoll(command.data(), nullptr, 16)};
s64 thread_id = strtoll(command.data(), nullptr, 16);
if (GetThreadByID(thread_id)) {
SendReply(GDB_STUB_REPLY_OK);
} else {
@@ -262,27 +214,26 @@ void GDBStub::ExecuteCommand(std::string_view packet, std::vector<DebuggerAction
SendReply(GDB_STUB_REPLY_OK);
break;
case 'p': {
const size_t reg{static_cast<size_t>(strtoll(command.data(), nullptr, 16))};
const size_t reg = size_t(strtoll(command.data(), nullptr, 16));
SendReply(arch->RegRead(backend.GetActiveThread(), reg));
break;
}
case 'P': {
const auto sep{std::find(command.begin(), command.end(), '=') - command.begin() + 1};
const size_t reg{static_cast<size_t>(strtoll(command.data(), nullptr, 16))};
const auto sep = std::find(command.begin(), command.end(), '=') - command.begin() + 1;
const size_t reg = size_t(strtoll(command.data(), nullptr, 16));
arch->RegWrite(backend.GetActiveThread(), reg, std::string_view(command).substr(sep));
SendReply(GDB_STUB_REPLY_OK);
break;
}
case 'm': {
const auto sep{std::find(command.begin(), command.end(), ',') - command.begin() + 1};
const size_t addr{static_cast<size_t>(strtoll(command.data(), nullptr, 16))};
const size_t size{static_cast<size_t>(strtoll(command.data() + sep, nullptr, 16))};
const size_t addr = size_t(strtoll(command.data(), nullptr, 16));
const size_t size = size_t(strtoll(command.data() + sep, nullptr, 16));
std::vector<u8> mem(size);
if (GetMemory().ReadBlock(addr, mem.data(), size)) {
if (debug_process->GetMemory().ReadBlock(addr, mem.data(), size)) {
// Restore any bytes belonging to replaced instructions.
auto it = replaced_instructions.lower_bound(addr);
for (; it != replaced_instructions.end() && it->first < addr + size; it++) {
for (auto it = replaced_instructions.lower_bound(addr); it != replaced_instructions.end() && it->first < addr + size; it++) {
// Get the bytes of the instruction we previously replaced.
const u32 original_bytes = it->second;
@@ -307,14 +258,14 @@ void GDBStub::ExecuteCommand(std::string_view packet, std::vector<DebuggerAction
const auto size_sep{std::find(command.begin(), command.end(), ',') - command.begin() + 1};
const auto mem_sep{std::find(command.begin(), command.end(), ':') - command.begin() + 1};
const size_t addr{static_cast<size_t>(strtoll(command.data(), nullptr, 16))};
const size_t size{static_cast<size_t>(strtoll(command.data() + size_sep, nullptr, 16))};
const size_t addr{size_t(strtoll(command.data(), nullptr, 16))};
const size_t size{size_t(strtoll(command.data() + size_sep, nullptr, 16))};
const auto mem_substr{std::string_view(command).substr(mem_sep)};
const auto mem{Common::HexStringToVector(mem_substr, false)};
if (GetMemory().WriteBlock(addr, mem.data(), size)) {
Core::InvalidateInstructionCacheRange(GetProcess(), addr, size);
if (debug_process->GetMemory().WriteBlock(addr, mem.data(), size)) {
Core::InvalidateInstructionCacheRange(debug_process, addr, size);
SendReply(GDB_STUB_REPLY_OK);
} else {
SendReply(GDB_STUB_REPLY_ERR);
@@ -349,14 +300,13 @@ enum class BreakpointType {
};
void GDBStub::HandleBreakpointInsert(std::string_view command) {
const auto type{static_cast<BreakpointType>(strtoll(command.data(), nullptr, 16))};
const auto addr_sep{std::find(command.begin(), command.end(), ',') - command.begin() + 1};
const auto size_sep{std::find(command.begin() + addr_sep, command.end(), ',') -
command.begin() + 1};
const size_t addr{static_cast<size_t>(strtoll(command.data() + addr_sep, nullptr, 16))};
const size_t size{static_cast<size_t>(strtoll(command.data() + size_sep, nullptr, 16))};
const auto type = BreakpointType(strtoll(command.data(), nullptr, 16));
const auto addr_sep = std::find(command.begin(), command.end(), ',') - command.begin() + 1;
const auto size_sep = std::find(command.begin() + addr_sep, command.end(), ',') - command.begin() + 1;
const size_t addr = size_t(strtoll(command.data() + addr_sep, nullptr, 16));
const size_t size = size_t(strtoll(command.data() + size_sep, nullptr, 16));
if (!GetMemory().IsValidVirtualAddressRange(addr, size)) {
if (!debug_process->GetMemory().IsValidVirtualAddressRange(addr, size)) {
SendReply(GDB_STUB_REPLY_ERR);
return;
}
@@ -365,20 +315,19 @@ void GDBStub::HandleBreakpointInsert(std::string_view command) {
switch (type) {
case BreakpointType::Software:
replaced_instructions[addr] = GetMemory().Read32(addr);
GetMemory().Write32(addr, arch->BreakpointInstruction());
Core::InvalidateInstructionCacheRange(GetProcess(), addr, sizeof(u32));
replaced_instructions[addr] = debug_process->GetMemory().Read32(addr);
debug_process->GetMemory().Write32(addr, arch->BreakpointInstruction());
Core::InvalidateInstructionCacheRange(debug_process, addr, sizeof(u32));
success = true;
break;
case BreakpointType::WriteWatch:
success = GetProcess()->InsertWatchpoint(addr, size, Kernel::DebugWatchpointType::Write);
success = debug_process->InsertWatchpoint(addr, size, Kernel::DebugWatchpointType::Write);
break;
case BreakpointType::ReadWatch:
success = GetProcess()->InsertWatchpoint(addr, size, Kernel::DebugWatchpointType::Read);
success = debug_process->InsertWatchpoint(addr, size, Kernel::DebugWatchpointType::Read);
break;
case BreakpointType::AccessWatch:
success =
GetProcess()->InsertWatchpoint(addr, size, Kernel::DebugWatchpointType::ReadOrWrite);
success = debug_process->InsertWatchpoint(addr, size, Kernel::DebugWatchpointType::ReadOrWrite);
break;
case BreakpointType::Hardware:
default:
@@ -393,41 +342,37 @@ void GDBStub::HandleBreakpointInsert(std::string_view command) {
}
}
void GDBStub::HandleBreakpointRemove(std::string_view command) {
const auto type{static_cast<BreakpointType>(strtoll(command.data(), nullptr, 16))};
const auto addr_sep{std::find(command.begin(), command.end(), ',') - command.begin() + 1};
const auto size_sep{std::find(command.begin() + addr_sep, command.end(), ',') -
command.begin() + 1};
const size_t addr{static_cast<size_t>(strtoll(command.data() + addr_sep, nullptr, 16))};
const size_t size{static_cast<size_t>(strtoll(command.data() + size_sep, nullptr, 16))};
void GDBStub::HandleBreakpointRemove(std::string_view sv) {
const auto type = BreakpointType(strtoll(sv.data(), nullptr, 16));
const auto addr_sep = std::find(sv.begin(), sv.end(), ',') - sv.begin() + 1;
const auto size_sep = std::find(sv.begin() + addr_sep, sv.end(), ',') - sv.begin() + 1;
const size_t addr = size_t(strtoll(sv.data() + addr_sep, nullptr, 16));
const size_t size = size_t(strtoll(sv.data() + size_sep, nullptr, 16));
if (!GetMemory().IsValidVirtualAddressRange(addr, size)) {
if (!debug_process->GetMemory().IsValidVirtualAddressRange(addr, size)) {
SendReply(GDB_STUB_REPLY_ERR);
return;
}
bool success{};
bool success = false;
switch (type) {
case BreakpointType::Software: {
const auto orig_insn{replaced_instructions.find(addr)};
if (orig_insn != replaced_instructions.end()) {
GetMemory().Write32(addr, orig_insn->second);
Core::InvalidateInstructionCacheRange(GetProcess(), addr, sizeof(u32));
if (auto const orig_insn = replaced_instructions.find(addr); orig_insn != replaced_instructions.end()) {
debug_process->GetMemory().Write32(addr, orig_insn->second);
Core::InvalidateInstructionCacheRange(debug_process, addr, sizeof(u32));
replaced_instructions.erase(addr);
success = true;
}
break;
}
case BreakpointType::WriteWatch:
success = GetProcess()->RemoveWatchpoint(addr, size, Kernel::DebugWatchpointType::Write);
success = debug_process->RemoveWatchpoint(addr, size, Kernel::DebugWatchpointType::Write);
break;
case BreakpointType::ReadWatch:
success = GetProcess()->RemoveWatchpoint(addr, size, Kernel::DebugWatchpointType::Read);
success = debug_process->RemoveWatchpoint(addr, size, Kernel::DebugWatchpointType::Read);
break;
case BreakpointType::AccessWatch:
success =
GetProcess()->RemoveWatchpoint(addr, size, Kernel::DebugWatchpointType::ReadOrWrite);
success = debug_process->RemoveWatchpoint(addr, size, Kernel::DebugWatchpointType::ReadOrWrite);
break;
case BreakpointType::Hardware:
default:
@@ -454,22 +399,21 @@ static std::string PaginateBuffer(std::string_view buffer, std::string_view requ
}
}
void GDBStub::HandleQuery(std::string_view command) {
if (command.starts_with("TStatus")) {
void GDBStub::HandleQuery(std::string_view sv) {
if (sv.starts_with("TStatus")) {
// no tracepoint support
SendReply("T0");
} else if (command.starts_with("Supported")) {
} else if (sv.starts_with("Supported")) {
SendReply("PacketSize=4000;qXfer:features:read+;qXfer:threads:read+;qXfer:libraries:read+;"
"vContSupported+;QStartNoAckMode+");
} else if (command.starts_with("Xfer:features:read:target.xml:")) {
} else if (sv.starts_with("Xfer:features:read:target.xml:")) {
const auto target_xml{arch->GetTargetXML()};
SendReply(PaginateBuffer(target_xml, command.substr(30)));
} else if (command.starts_with("Offsets")) {
const auto main_offset = Core::FindMainModuleEntrypoint(GetProcess());
SendReply(PaginateBuffer(target_xml, sv.substr(30)));
} else if (sv.starts_with("Offsets")) {
const auto main_offset = Core::FindMainModuleEntrypoint(debug_process);
SendReply(fmt::format("TextSeg={:x}", GetInteger(main_offset)));
} else if (command.starts_with("Xfer:libraries:read::")) {
auto modules = Core::FindModules(GetProcess());
} else if (sv.starts_with("Xfer:libraries:read::")) {
auto modules = Core::FindModules(debug_process);
std::string buffer;
buffer += R"(<?xml version="1.0"?>)";
buffer += "<library-list>";
@@ -479,24 +423,23 @@ void GDBStub::HandleQuery(std::string_view command) {
}
buffer += "</library-list>";
SendReply(PaginateBuffer(buffer, command.substr(21)));
} else if (command.starts_with("fThreadInfo")) {
SendReply(PaginateBuffer(buffer, sv.substr(21)));
} else if (sv.starts_with("fThreadInfo")) {
// beginning of list
const auto& threads = GetProcess()->GetThreadList();
const auto& threads = debug_process->GetThreadList();
std::vector<std::string> thread_ids;
for (const auto& thread : threads) {
for (const auto& thread : threads)
thread_ids.push_back(fmt::format("{:x}", thread.GetThreadId()));
}
SendReply(fmt::format("m{}", fmt::join(thread_ids, ",")));
} else if (command.starts_with("sThreadInfo")) {
} else if (sv.starts_with("sThreadInfo")) {
// end of list
SendReply("l");
} else if (command.starts_with("Xfer:threads:read::")) {
} else if (sv.starts_with("Xfer:threads:read::")) {
std::string buffer;
buffer += R"(<?xml version="1.0"?>)";
buffer += "<threads>";
const auto& threads = GetProcess()->GetThreadList();
const auto& threads = debug_process->GetThreadList();
for (const auto& thread : threads) {
auto thread_name{Core::GetThreadName(&thread)};
if (!thread_name) {
@@ -510,109 +453,95 @@ void GDBStub::HandleQuery(std::string_view command) {
buffer += "</threads>";
SendReply(PaginateBuffer(buffer, command.substr(19)));
} else if (command.starts_with("Attached")) {
SendReply(PaginateBuffer(buffer, sv.substr(19)));
} else if (sv.starts_with("Attached")) {
SendReply("0");
} else if (command.starts_with("StartNoAckMode")) {
} else if (sv.starts_with("StartNoAckMode")) {
no_ack = true;
SendReply(GDB_STUB_REPLY_OK);
} else if (command.starts_with("Rcmd,")) {
HandleRcmd(Common::HexStringToVector(command.substr(5), false));
} else if (sv.starts_with("Rcmd,")) {
HandleRcmd(Common::HexStringToVector(sv.substr(5), false));
} else {
SendReply(GDB_STUB_REPLY_EMPTY);
}
}
void GDBStub::HandleVCont(std::string_view command, std::vector<DebuggerAction>& actions) {
if (command == "?") {
// Continuing and stepping are supported
// (signal is ignored, but required for GDB to use vCont)
void GDBStub::HandleVCont(std::string_view sv, std::vector<DebuggerAction>& actions) {
// Continuing and stepping are supported (signal is ignored, but required for GDB to use vCont)
if (sv == "?") {
SendReply("vCont;c;C;s;S");
return;
}
Kernel::KThread* stepped_thread{nullptr};
bool lock_execution{true};
std::vector<std::string> entries;
boost::split(entries, command.substr(1), boost::is_any_of(";"));
for (const auto& thread_action : entries) {
std::vector<std::string> parts;
boost::split(parts, thread_action, boost::is_any_of(":"));
if (parts.size() == 1 && (parts[0] == "c" || parts[0].starts_with("C"))) {
lock_execution = false;
}
if (parts.size() == 2 && (parts[0] == "s" || parts[0].starts_with("S"))) {
stepped_thread = GetThreadByID(strtoll(parts[1].data(), nullptr, 16));
}
}
if (stepped_thread) {
backend.SetActiveThread(stepped_thread);
actions.push_back(lock_execution ? DebuggerAction::StepThreadLocked
: DebuggerAction::StepThreadUnlocked);
} else {
actions.push_back(DebuggerAction::Continue);
Kernel::KThread* stepped_thread = nullptr;
bool lock_execution = true;
std::vector<std::string> entries;
boost::split(entries, sv.substr(1), boost::is_any_of(";"));
for (auto const& thread_action : entries) {
std::vector<std::string> parts;
boost::split(parts, thread_action, boost::is_any_of(":"));
if (parts.size() == 1 && (parts[0] == "c" || parts[0].starts_with("C")))
lock_execution = false;
if (parts.size() == 2 && (parts[0] == "s" || parts[0].starts_with("S")))
stepped_thread = GetThreadByID(strtoll(parts[1].data(), nullptr, 16));
}
if (stepped_thread) {
backend.SetActiveThread(stepped_thread);
actions.push_back(lock_execution ? DebuggerAction::StepThreadLocked : DebuggerAction::StepThreadUnlocked);
} else {
actions.push_back(DebuggerAction::Continue);
}
}
}
static constexpr const char* GetMemoryStateName(Kernel::Svc::MemoryState state) {
constexpr std::array<std::pair<const char*, Kernel::Svc::MemoryState>, 22> MemoryStateNames{{
{"----- Free ------", Kernel::Svc::MemoryState::Free},
{"Io ", Kernel::Svc::MemoryState::Io},
{"Static ", Kernel::Svc::MemoryState::Static},
{"Code ", Kernel::Svc::MemoryState::Code},
{"CodeData ", Kernel::Svc::MemoryState::CodeData},
{"Normal ", Kernel::Svc::MemoryState::Normal},
{"Shared ", Kernel::Svc::MemoryState::Shared},
{"AliasCode ", Kernel::Svc::MemoryState::AliasCode},
{"AliasCodeData ", Kernel::Svc::MemoryState::AliasCodeData},
{"Ipc ", Kernel::Svc::MemoryState::Ipc},
{"Stack ", Kernel::Svc::MemoryState::Stack},
{"ThreadLocal ", Kernel::Svc::MemoryState::ThreadLocal},
{"Transferred ", Kernel::Svc::MemoryState::Transferred},
{"SharedTransferred", Kernel::Svc::MemoryState::SharedTransferred},
{"SharedCode ", Kernel::Svc::MemoryState::SharedCode},
{"Inaccessible ", Kernel::Svc::MemoryState::Inaccessible},
{"NonSecureIpc ", Kernel::Svc::MemoryState::NonSecureIpc},
{"NonDeviceIpc ", Kernel::Svc::MemoryState::NonDeviceIpc},
{"Kernel ", Kernel::Svc::MemoryState::Kernel},
{"GeneratedCode ", Kernel::Svc::MemoryState::GeneratedCode},
{"CodeOut ", Kernel::Svc::MemoryState::CodeOut},
{"Coverage ", Kernel::Svc::MemoryState::Coverage},
}};
for (size_t i = 0; i < MemoryStateNames.size(); i++) {
if (std::get<1>(MemoryStateNames[i]) == state) {
return std::get<0>(MemoryStateNames[i]);
}
#define MEMORY_STATE_LIST \
MEMORY_STATE_ELEM(Free) \
MEMORY_STATE_ELEM(Io) \
MEMORY_STATE_ELEM(Static) \
MEMORY_STATE_ELEM(Code) \
MEMORY_STATE_ELEM(CodeData) \
MEMORY_STATE_ELEM(Normal) \
MEMORY_STATE_ELEM(Shared) \
MEMORY_STATE_ELEM(AliasCode) \
MEMORY_STATE_ELEM(AliasCodeData) \
MEMORY_STATE_ELEM(Ipc) \
MEMORY_STATE_ELEM(Stack) \
MEMORY_STATE_ELEM(ThreadLocal) \
MEMORY_STATE_ELEM(Transferred) \
MEMORY_STATE_ELEM(SharedTransferred) \
MEMORY_STATE_ELEM(SharedCode) \
MEMORY_STATE_ELEM(Inaccessible) \
MEMORY_STATE_ELEM(NonSecureIpc) \
MEMORY_STATE_ELEM(NonDeviceIpc) \
MEMORY_STATE_ELEM(Kernel) \
MEMORY_STATE_ELEM(GeneratedCode) \
MEMORY_STATE_ELEM(CodeOut) \
MEMORY_STATE_ELEM(Coverage)
switch (state) {
#define MEMORY_STATE_ELEM(elem) case Kernel::Svc::MemoryState::elem: return #elem;
MEMORY_STATE_LIST
#undef MEMORY_STATE_LIST
default: return "Unknown";
}
return "Unknown ";
}
static constexpr const char* GetMemoryPermissionString(const Kernel::Svc::MemoryInfo& info) {
if (info.state == Kernel::Svc::MemoryState::Free) {
return " ";
}
switch (info.permission) {
case Kernel::Svc::MemoryPermission::ReadExecute:
return "r-x";
case Kernel::Svc::MemoryPermission::Read:
return "r--";
case Kernel::Svc::MemoryPermission::ReadWrite:
return "rw-";
default:
return "---";
} else {
switch (info.permission) {
case Kernel::Svc::MemoryPermission::ReadExecute: return "r-x";
case Kernel::Svc::MemoryPermission::Read: return "r--";
case Kernel::Svc::MemoryPermission::ReadWrite: return "rw-";
default: return "---";
}
}
}
void GDBStub::HandleRcmd(const std::vector<u8>& command) {
std::string_view command_str{reinterpret_cast<const char*>(&command[0]), command.size()};
std::string reply;
auto* process = GetProcess();
auto& page_table = process->GetPageTable();
auto& page_table = debug_process->GetPageTable();
if (command_str == "fastmem" || command_str == "get fastmem") {
if (Settings::IsFastmemEnabled()) {
const auto& impl = page_table.GetImpl();
@@ -627,11 +556,13 @@ void GDBStub::HandleRcmd(const std::vector<u8>& command) {
reply = "Fastmem is not enabled.\n";
}
} else if (command_str == "info" || command_str == "get info") {
auto modules = Core::FindModules(process);
auto modules = Core::FindModules(debug_process);
reply = fmt::format("Process: {:#x} ({})\n"
"Program Id: {:#018x}\n",
process->GetProcessId(), process->GetName(), process->GetProgramId());
debug_process->GetProcessId(),
debug_process->GetName(),
debug_process->GetProgramId());
reply += fmt::format(
"Layout:\n"
" Alias: {:#012x} - {:#012x}\n"
@@ -648,10 +579,8 @@ void GDBStub::HandleRcmd(const std::vector<u8>& command) {
GetInteger(page_table.GetStackRegionStart()),
GetInteger(page_table.GetStackRegionStart()) + page_table.GetStackRegionSize() - 1);
for (const auto& [vaddr, name] : modules) {
reply += fmt::format(" {:#012x} - {:#012x} {}\n", vaddr,
GetInteger(Core::GetModuleEnd(process, vaddr)), name);
}
for (const auto& [vaddr, name] : modules)
reply += fmt::format(" {:#012x} - {:#012x} {}\n", vaddr, GetInteger(Core::GetModuleEnd(debug_process, vaddr)), name);
} else if (command_str == "mappings" || command_str == "get mappings") {
reply = "Mappings:\n";
VAddr cur_addr = 0;
@@ -683,10 +612,8 @@ void GDBStub::HandleRcmd(const std::vector<u8>& command) {
}
const uintptr_t next_address = svc_mem_info.base_address + svc_mem_info.size;
if (next_address <= cur_addr) {
if (next_address <= cur_addr)
break;
}
cur_addr = next_address;
}
} else {
@@ -698,20 +625,16 @@ void GDBStub::HandleRcmd(const std::vector<u8>& command) {
}
Kernel::KThread* GDBStub::GetThreadByID(u64 thread_id) {
auto& threads{GetProcess()->GetThreadList()};
for (auto& thread : threads) {
if (thread.GetThreadId() == thread_id) {
auto& threads = debug_process->GetThreadList();
for (auto& thread : threads)
if (thread.GetThreadId() == thread_id)
return std::addressof(thread);
}
}
return nullptr;
}
std::vector<char>::const_iterator GDBStub::CommandEnd() const {
// Find the end marker
const auto end{std::find(current_command.begin(), current_command.end(), GDB_STUB_END)};
const auto end = std::find(current_command.begin(), current_command.end(), GDB_STUB_END);
// Require the checksum to be present
return (std::min)(end + 2, current_command.end());
}
@@ -737,8 +660,7 @@ std::optional<std::string> GDBStub::DetachCommand() {
// Verify checksum
if (calculated != received) {
LOG_ERROR(Debug_GDBStub, "Checksum mismatch: calculated {:02x}, received {:02x}",
calculated, received);
LOG_ERROR(Debug_GDBStub, "Checksum mismatch: calculated {:02x}, received {:02x}", calculated, received);
return std::nullopt;
}
@@ -746,9 +668,8 @@ std::optional<std::string> GDBStub::DetachCommand() {
}
void GDBStub::SendReply(std::string_view data) {
const auto escaped{EscapeGDB(data)};
const auto output{fmt::format("{}{}{}{:02x}", GDB_STUB_START, escaped, GDB_STUB_END,
CalculateChecksum(escaped))};
const auto escaped = EscapeGDB(data);
const auto output = fmt::format("{}{}{}{:02x}", GDB_STUB_START, escaped, GDB_STUB_END, CalculateChecksum(escaped));
LOG_TRACE(Debug_GDBStub, "Writing reply: {}", output);
// C++ string support is complete rubbish
@@ -758,21 +679,11 @@ void GDBStub::SendReply(std::string_view data) {
}
void GDBStub::SendStatus(char status) {
if (no_ack) {
return;
if (!no_ack) {
std::array<u8, 1> buf = {u8(status)};
LOG_TRACE(Debug_GDBStub, "Writing status: {}", status);
backend.WriteToClient(buf);
}
std::array<u8, 1> buf = {static_cast<u8>(status)};
LOG_TRACE(Debug_GDBStub, "Writing status: {}", status);
backend.WriteToClient(buf);
}
Kernel::KProcess* GDBStub::GetProcess() {
return debug_process;
}
Core::Memory::Memory& GDBStub::GetMemory() {
return GetProcess()->GetMemory();
}
} // namespace Core

View File

@@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2025 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
@@ -24,19 +27,14 @@ namespace Core {
class System;
class GDBStub : public DebuggerFrontend {
public:
explicit GDBStub(DebuggerBackend& backend, Core::System& system,
Kernel::KProcess* debug_process);
struct GDBStub : public DebuggerFrontend {
explicit GDBStub(DebuggerBackend& backend, Core::System& system, Kernel::KProcess* debug_process);
~GDBStub() override;
void Connected() override;
void Stopped(Kernel::KThread* thread) override;
void ShuttingDown() override;
void Watchpoint(Kernel::KThread* thread, const Kernel::DebugWatchpoint& watch) override;
std::vector<DebuggerAction> ClientData(std::span<const u8> data) override;
private:
void ProcessData(std::vector<DebuggerAction>& actions);
void ExecuteCommand(std::string_view packet, std::vector<DebuggerAction>& actions);
void HandleVCont(std::string_view command, std::vector<DebuggerAction>& actions);
@@ -47,14 +45,8 @@ private:
std::vector<char>::const_iterator CommandEnd() const;
std::optional<std::string> DetachCommand();
Kernel::KThread* GetThreadByID(u64 thread_id);
void SendReply(std::string_view data);
void SendStatus(char status);
Kernel::KProcess* GetProcess();
Core::Memory::Memory& GetMemory();
private:
Core::System& system;
Kernel::KProcess* debug_process;
std::unique_ptr<GDBStubArch> arch;

View File

@@ -86,18 +86,21 @@ size_t AesCtrStorage::Write(const u8* buffer, size_t size, size_t offset) {
// Loop until all data is written using a pooled buffer residing on the stack (blocksize = 0x10)
boost::container::static_vector<u8, BlockSize> pooled_buffer;
for (size_t remaining = size; remaining > 0; ) {
// Determine data we're writing and where.
auto const write_size = (std::min)(pooled_buffer.size(), remaining);
u8* write_buf = pooled_buffer.data();
pooled_buffer.resize(BlockSize);
const u8* cur = buffer;
size_t remaining = size;
size_t current_offset = offset;
while (remaining > 0) {
const size_t write_size = std::min<std::size_t>(pooled_buffer.size(), remaining);
// Encrypt the data and then write it.
m_cipher->SetIV(ctr);
m_cipher->Transcode(buffer, write_size, write_buf, Core::Crypto::Op::Encrypt);
m_base_storage->Write(write_buf, write_size, offset);
m_cipher->Transcode(cur, write_size, pooled_buffer.data(), Core::Crypto::Op::Encrypt);
m_base_storage->Write(pooled_buffer.data(), write_size, current_offset);
// Advance next write chunk
offset += write_size;
cur += write_size;
current_offset += write_size;
remaining -= write_size;
if (remaining > 0)
AddCounter(ctr.data(), IvSize, write_size / BlockSize);

View File

@@ -65,10 +65,13 @@ size_t AesXtsStorage::Read(u8* buffer, size_t size, size_t offset) const {
if ((offset % m_block_size) != 0) {
// Decrypt into our pooled stack buffer (max bound = NCA::XtsBlockSize)
boost::container::static_vector<u8, NcaHeader::XtsBlockSize> tmp_buf;
ASSERT(m_block_size <= tmp_buf.max_size());
tmp_buf.resize(m_block_size);
// Determine the size of the pre-data read.
auto const skip_size = size_t(offset - Common::AlignDown(offset, m_block_size));
auto const data_size = (std::min)(size, m_block_size - skip_size);
std::fill_n(tmp_buf.begin(), skip_size, u8{0});
if (skip_size > 0)
std::fill_n(tmp_buf.begin(), skip_size, u8{0});
std::memcpy(tmp_buf.data() + skip_size, buffer, data_size);
m_cipher->SetIV(ctr);
m_cipher->Transcode(tmp_buf.data(), m_block_size, tmp_buf.data(), Core::Crypto::Op::Decrypt);

View File

@@ -429,19 +429,23 @@ void KThread::StartTermination() {
}
void KThread::FinishTermination() {
// Acquire the scheduler lock.
KScopedSchedulerLock lk{m_kernel};
// Ensure that the thread is not executing on any core.
if (m_parent != nullptr) {
for (std::size_t i = 0; i < static_cast<std::size_t>(Core::Hardware::NUM_CPU_CORES); ++i) {
KThread* core_thread{};
do {
core_thread = m_kernel.Scheduler(i).GetSchedulerCurrentThread();
} while (core_thread == this);
bool wait_thread = true;
lk.unlock();
while (wait_thread) {
// now "pin" the scheduler so it wont change stuff mid-way
lk.lock();
for (std::size_t i = 0; i < std::size_t(Core::Hardware::NUM_CPU_CORES); ++i)
is_on_schedule |= m_kernel.Scheduler(i).GetSchedulerCurrentThread() == this;
// let scheduler try again
if (wait_thread)
lk.unlock();
}
}
// Acquire the scheduler lock.
KScopedSchedulerLock sl{m_kernel};
// Signal.
m_signaled = true;
KSynchronizationObject::NotifyAvailable();

View File

@@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@@ -539,7 +542,7 @@ public:
}
void ClearDpc(DpcFlag flag) {
this->GetStackParameters().dpc_flags &= ~static_cast<u8>(flag);
this->GetStackParameters().dpc_flags &= static_cast<u8>(~static_cast<u8>(flag));
}
u8 GetDpc() const {

View File

@@ -84,7 +84,8 @@ public:
{0, D<&IManagerForSystemService::CheckAvailability>, "CheckAvailability"},
{1, D<&IManagerForSystemService::GetAccountId>, "GetAccountId"},
{2, nullptr, "EnsureIdTokenCacheAsync"},
{3, nullptr, "LoadIdTokenCache"},
{3, D<&IManagerForSystemService::LoadIdTokenCacheDeprecated>, "LoadIdTokenCacheDeprecated"}, // 19.0.0+
{4, D<&IManagerForSystemService::LoadIdTokenCache>, "LoadIdTokenCache"}, // 19.0.0+
{100, nullptr, "SetSystemProgramIdentification"},
{101, nullptr, "RefreshNotificationTokenAsync"}, // 7.0.0+
{110, nullptr, "GetServiceEntryRequirementCache"}, // 4.0.0+
@@ -126,6 +127,16 @@ private:
R_SUCCEED();
}
Result LoadIdTokenCacheDeprecated() {
LOG_WARNING(Service_ACC, "(STUBBED) called");
R_SUCCEED();
}
Result LoadIdTokenCache() {
LOG_WARNING(Service_ACC, "(STUBBED) called");
R_SUCCEED();
}
Result GetNetworkServiceLicenseCacheEx() {
LOG_DEBUG(Service_ACC, "(STUBBED) called.");
@@ -647,7 +658,7 @@ public:
{0, &IManagerForApplication::CheckAvailability, "CheckAvailability"},
{1, &IManagerForApplication::GetAccountId, "GetAccountId"},
{2, &IManagerForApplication::EnsureIdTokenCacheAsync, "EnsureIdTokenCacheAsync"},
{3, &IManagerForApplication::LoadIdTokenCacheDeprecated, "LoadIdTokenCache"},
{3, &IManagerForApplication::LoadIdTokenCacheDeprecated, "LoadIdTokenCacheDeprecated"},
{4, &IManagerForApplication::LoadIdTokenCache, "LoadIdTokenCache"},
{130, &IManagerForApplication::GetNintendoAccountUserResourceCacheForApplication, "GetNintendoAccountUserResourceCacheForApplication"},
{136, &IManagerForApplication::GetNintendoAccountUserResourceCacheForApplication, "GetNintendoAccountUserResourceCache"}, // 19.0.0+

View File

@@ -10,16 +10,19 @@
#include <iostream>
#include <random>
#include <boost/algorithm/string/case_conv.hpp>
#include <boost/algorithm/string/find.hpp>
#include <fmt/ranges.h>
#include "common/fs/file.h"
#include "common/fs/fs.h"
#include "common/fs/fs_types.h"
#include "common/fs/path_util.h"
#include <ranges>
#include "common/settings.h"
#include "common/string_util.h"
#include "core/file_sys/savedata_factory.h"
#include "core/hle/service/acc/profile_manager.h"
#include <ranges>
namespace Service::Account {
@@ -39,6 +42,7 @@ struct ProfileDataRaw {
INSERT_PADDING_BYTES(0x10);
std::array<UserRaw, MAX_USERS> users{};
};
static_assert(sizeof(ProfileDataRaw) == 0x650, "ProfileDataRaw has incorrect size.");
// TODO(ogniK): Get actual error codes
@@ -488,10 +492,24 @@ void ProfileManager::ResetUserSaveFile()
ParseUserSaveFile();
}
std::vector<std::string> ProfileManager::FindOrphanedProfiles()
std::vector<std::string> ProfileManager::FindGoodProfiles()
{
namespace fs = std::filesystem;
std::vector<std::string> good_uuids;
const auto path = Common::FS::GetEdenPath(Common::FS::EdenPath::NANDDir)
/ "user/save/0000000000000000";
// some exceptions because certain games just LOVE TO CAUSE ISSUES
static constexpr const std::array<const char* const, 2> EXCEPTION_UUIDS
= {"5755CC2A545A87128500000000000000", "00000000000000000000000000000000"};
for (const char *const uuid : EXCEPTION_UUIDS) {
if (fs::exists(path / uuid))
good_uuids.emplace_back(uuid);
}
for (const ProfileInfo& p : profiles) {
std::string uuid_string = [p]() -> std::string {
auto uuid = p.user_uuid;
@@ -506,11 +524,17 @@ std::vector<std::string> ProfileManager::FindOrphanedProfiles()
return fmt::format("{:016X}{:016X}", user_id[1], user_id[0]);
}();
good_uuids.emplace_back(uuid_string);
if (uuid_string != "0") good_uuids.emplace_back(uuid_string);
}
// used for acnh, etc
good_uuids.emplace_back("00000000000000000000000000000000");
return good_uuids;
}
std::vector<std::string> ProfileManager::FindOrphanedProfiles()
{
std::vector<std::string> good_uuids = FindGoodProfiles();
namespace fs = std::filesystem;
// TODO: fetch save_id programmatically
const auto path = Common::FS::GetEdenPath(Common::FS::EdenPath::NANDDir)
@@ -523,32 +547,47 @@ std::vector<std::string> ProfileManager::FindOrphanedProfiles()
[&good_uuids, &orphaned_profiles](const std::filesystem::directory_entry& entry) -> bool {
const std::string uuid = entry.path().stem().string();
bool override = false;
// first off, we should always clear empty profiles
// 99% of the time these are useless. If not, they are recreated anyways...
namespace fs = std::filesystem;
const auto is_empty = [&entry]() -> bool {
const auto is_empty = [&entry, &override]() -> bool {
try {
for (const auto& file : fs::recursive_directory_iterator(entry.path())) {
if (file.is_regular_file()) {
return true;
}
// TODO: .yuzu_save_size is a weird file that gets created by certain games
// I have no idea what its purpose is, but TEMPORARY SOLUTION: just mark the profile as valid if
// this file exists (???) e.g. for SSBU
// In short: if .yuzu_save_size is the ONLY file in a profile it's probably fine to keep
if (file.path().filename().string() == FileSys::GetSaveDataSizeFileName())
override = true;
// if there are any regular files (NOT directories) there, do NOT delete it :p
if (file.is_regular_file())
return false;
}
} catch (const fs::filesystem_error& e) {
// if we get an error--no worries, just pretend it's not empty
return false;
return true;
}
return false;
return true;
}();
if (!is_empty) {
if (is_empty) {
fs::remove_all(entry);
return true;
}
// edge-case: some filesystems forcefully change filenames to lowercase
// so we can just ignore any differences
// looking at you microsoft... ;)
std::string upper_uuid = uuid;
boost::to_upper(upper_uuid);
// if profiles.dat contains the UUID--all good
// if not--it's an orphaned profile and should be resolved by the user
if (std::find(good_uuids.begin(), good_uuids.end(), uuid) == good_uuids.end()) {
if (!override
&& std::find(good_uuids.begin(), good_uuids.end(), upper_uuid) == good_uuids.end()) {
orphaned_profiles.emplace_back(uuid);
}
return true;

View File

@@ -105,6 +105,7 @@ public:
void ResetUserSaveFile();
std::vector<std::string> FindGoodProfiles();
std::vector<std::string> FindOrphanedProfiles();
private:

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