Compare commits

...

30 Commits

Author SHA1 Message Date
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
172 changed files with 3396 additions and 11601 deletions

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

@@ -0,0 +1,129 @@
From d765ebed3598ddfd7167fc546474626ac5ef9498 Mon Sep 17 00:00:00 2001
From: Anthony Roberts <anthony.roberts@linaro.org>
Date: Fri, 2 Aug 2024 16:55:57 +0100
Subject: [PATCH] Add support for clang-cl on Windows (#633)
This commit adds support for clang-cl (clang, pretending to be MSVC) to
SSE2NEON on Windows ARM64 platforms. This change is part of some Blender
work, as using clang-cl provides a ~20-40% speedup compared to MSVC.
Compiled with the following command line (via a VS2022 Native ARM64 Tools
CMD window):
msbuild sse2neon.vcxproj /p:Configuration=Release /p:CLToolExe=clang-cl.exe
/p:CLToolPath="C:\Program Files\LLVM\bin\"
Known failures in test suite:
Test mm_cvttpd_epi32
Test rdtsc
Co-authored-by: Anthony Roberts <anthony.roberts@linaro.org>
---
sse2neon.h | 22 +++++++++++-----------
1 file changed, 11 insertions(+), 11 deletions(-)
diff --git a/sse2neon.h b/sse2neon.h
index 56254b5..76cf8e3 100644
--- a/sse2neon.h
+++ b/sse2neon.h
@@ -180,7 +180,7 @@
}
/* Compiler barrier */
-#if defined(_MSC_VER)
+#if defined(_MSC_VER) && !defined(__clang__)
#define SSE2NEON_BARRIER() _ReadWriteBarrier()
#else
#define SSE2NEON_BARRIER() \
@@ -856,7 +856,7 @@ FORCE_INLINE uint64x2_t _sse2neon_vmull_p64(uint64x1_t _a, uint64x1_t _b)
{
poly64_t a = vget_lane_p64(vreinterpret_p64_u64(_a), 0);
poly64_t b = vget_lane_p64(vreinterpret_p64_u64(_b), 0);
-#if defined(_MSC_VER)
+#if defined(_MSC_VER) && !defined(__clang__)
__n64 a1 = {a}, b1 = {b};
return vreinterpretq_u64_p128(vmull_p64(a1, b1));
#else
@@ -1767,7 +1767,7 @@ FORCE_INLINE void _mm_free(void *addr)
FORCE_INLINE uint64_t _sse2neon_get_fpcr(void)
{
uint64_t value;
-#if defined(_MSC_VER)
+#if defined(_MSC_VER) && !defined(__clang__)
value = _ReadStatusReg(ARM64_FPCR);
#else
__asm__ __volatile__("mrs %0, FPCR" : "=r"(value)); /* read */
@@ -1777,7 +1777,7 @@ FORCE_INLINE uint64_t _sse2neon_get_fpcr(void)
FORCE_INLINE void _sse2neon_set_fpcr(uint64_t value)
{
-#if defined(_MSC_VER)
+#if defined(_MSC_VER) && !defined(__clang__)
_WriteStatusReg(ARM64_FPCR, value);
#else
__asm__ __volatile__("msr FPCR, %0" ::"r"(value)); /* write */
@@ -2246,7 +2246,7 @@ FORCE_INLINE __m128 _mm_or_ps(__m128 a, __m128 b)
FORCE_INLINE void _mm_prefetch(char const *p, int i)
{
(void) i;
-#if defined(_MSC_VER)
+#if defined(_MSC_VER) && !defined(__clang__)
switch (i) {
case _MM_HINT_NTA:
__prefetch2(p, 1);
@@ -4817,7 +4817,7 @@ FORCE_INLINE __m128i _mm_packus_epi16(const __m128i a, const __m128i b)
// https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_pause
FORCE_INLINE void _mm_pause(void)
{
-#if defined(_MSC_VER)
+#if defined(_MSC_VER) && !defined(__clang__)
__isb(_ARM64_BARRIER_SY);
#else
__asm__ __volatile__("isb\n");
@@ -5713,7 +5713,7 @@ FORCE_INLINE __m128d _mm_undefined_pd(void)
#pragma GCC diagnostic ignored "-Wuninitialized"
#endif
__m128d a;
-#if defined(_MSC_VER)
+#if defined(_MSC_VER) && !defined(__clang__)
a = _mm_setzero_pd();
#endif
return a;
@@ -8127,7 +8127,7 @@ FORCE_INLINE int _sse2neon_sido_negative(int res, int lb, int imm8, int bound)
FORCE_INLINE int _sse2neon_clz(unsigned int x)
{
-#ifdef _MSC_VER
+#if defined(_MSC_VER) && !defined(__clang__)
unsigned long cnt = 0;
if (_BitScanReverse(&cnt, x))
return 31 - cnt;
@@ -8139,7 +8139,7 @@ FORCE_INLINE int _sse2neon_clz(unsigned int x)
FORCE_INLINE int _sse2neon_ctz(unsigned int x)
{
-#ifdef _MSC_VER
+#if defined(_MSC_VER) && !defined(__clang__)
unsigned long cnt = 0;
if (_BitScanForward(&cnt, x))
return cnt;
@@ -9055,7 +9055,7 @@ FORCE_INLINE __m128i _mm_aeskeygenassist_si128(__m128i a, const int rcon)
// AESE does ShiftRows and SubBytes on A
uint8x16_t u8 = vaeseq_u8(vreinterpretq_u8_m128i(a), vdupq_n_u8(0));
-#ifndef _MSC_VER
+#if !defined(_MSC_VER) || defined(__clang__)
uint8x16_t dest = {
// Undo ShiftRows step from AESE and extract X1 and X3
u8[0x4], u8[0x1], u8[0xE], u8[0xB], // SubBytes(X1)
@@ -9242,7 +9242,7 @@ FORCE_INLINE uint64_t _rdtsc(void)
* bits wide and it is attributed with the flag 'cap_user_time_short'
* is true.
*/
-#if defined(_MSC_VER)
+#if defined(_MSC_VER) && !defined(__clang__)
val = _ReadStatusReg(ARM64_SYSREG(3, 3, 14, 0, 2));
#else
__asm__ __volatile__("mrs %0, cntvct_el0" : "=r"(val));
--
2.48.1

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,9 +230,6 @@ 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}")
endif()
@@ -579,6 +571,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
@@ -590,6 +611,9 @@ find_package(VulkanUtilityLibraries)
find_package(SimpleIni)
find_package(SPIRV-Tools)
find_package(sirit)
if (ARCHITECTURE_arm64)
find_package(sse2neon)
endif()
if (ARCHITECTURE_x86 OR ARCHITECTURE_x86_64)
find_package(xbyak)
@@ -687,12 +711,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 +737,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 +753,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,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>

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

@@ -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
@@ -84,6 +86,8 @@ 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>
@@ -134,6 +138,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>
@@ -171,7 +204,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 +215,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 +230,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 zydis 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 zydis_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,6 +5,7 @@ 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)**

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

View File

@@ -402,6 +402,7 @@ endif()
# sse2neon
if (ARCHITECTURE_arm64 AND NOT TARGET sse2neon)
AddJsonPackage(sse2neon)
add_library(sse2neon INTERFACE)
target_include_directories(sse2neon INTERFACE sse2neon)
target_include_directories(sse2neon INTERFACE ${sse2neon_SOURCE_DIR})
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",
@@ -210,5 +213,14 @@
"key": "steamdeck",
"bundled": true,
"skip_updates": "true"
},
"sse2neon": {
"repo": "DLTcollab/sse2neon",
"sha": "66267b52fd",
"hash": "3aed8676e1b8c428acb076464663e3968a721457b08710a7c5f8df2fbdaa5601053c1606169a55e987e7a58dd17e3cc3b7fbf953aa891c5ac5f8ce2941862e4b",
"download_only": "true",
"patches": [
"0001-Add-support-for-clang-cl-on-Windows-633.patch"
]
}
}

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 zydis 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) {

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

@@ -673,7 +673,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 +803,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

@@ -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

@@ -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

@@ -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

@@ -488,7 +488,7 @@ void ProfileManager::ResetUserSaveFile()
ParseUserSaveFile();
}
std::vector<std::string> ProfileManager::FindOrphanedProfiles()
std::vector<std::string> ProfileManager::FindGoodProfiles()
{
std::vector<std::string> good_uuids;
@@ -512,6 +512,13 @@ std::vector<std::string> ProfileManager::FindOrphanedProfiles()
// 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();
// TODO: fetch save_id programmatically
const auto path = Common::FS::GetEdenPath(Common::FS::EdenPath::NANDDir)
/ "user/save/0000000000000000";

View File

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

View File

@@ -11,6 +11,7 @@
#include "core/hle/service/cmif_types.h"
#include "core/hle/service/ipc_helpers.h"
#include "core/hle/service/service.h"
#include <typeinfo>
namespace Service {
@@ -439,7 +440,16 @@ template <bool Domain, typename T, typename... A>
void CmifReplyWrapImpl(HLERequestContext& ctx, T& t, Result (T::*f)(A...)) {
// Verify domain state.
if constexpr (!Domain) {
ASSERT_MSG(!ctx.GetManager()->IsDomain(), "Non-domain reply used on domain session");
const auto _mgr = ctx.GetManager();
const bool _is_domain = _mgr ? _mgr->IsDomain() : false;
ASSERT_MSG(!_is_domain,
"Non-domain reply used on domain session\n"
"Service={} (type={})\nTIPC={} CmdType={} Cmd=0x{:08X}\n"
"HasDomainHeader={} DomainHandlers={}\nDesc={}",
t.GetServiceName(), typeid(T).name(), ctx.IsTipc(),
static_cast<u32>(ctx.GetCommandType()), static_cast<u32>(ctx.GetCommand()),
ctx.HasDomainMessageHeader(), _mgr ? static_cast<u32>(_mgr->DomainHandlerCount()) : 0u,
ctx.Description());
}
const bool is_domain = Domain ? ctx.GetManager()->IsDomain() : false;

View File

@@ -0,0 +1,27 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#include "core/hle/service/cmif_serialization.h"
#include "core/hle/service/ldn/ldn_results.h"
#include "core/hle/service/ldn/client_process_monitor.h"
#include "core/hle/service/ipc_helpers.h"
namespace Service::LDN {
IClientProcessMonitor::IClientProcessMonitor(Core::System& system_)
: ServiceFramework{system_, "IClientProcessMonitor"} {
static const FunctionInfo functions[] = {
{0, D<&IClientProcessMonitor::RegisterClient>, "RegisterClient"},
};
RegisterHandlers(functions);
}
IClientProcessMonitor::~IClientProcessMonitor() = default;
Result IClientProcessMonitor::RegisterClient() {
LOG_WARNING(Service_LDN, "(STUBBED) called");
R_SUCCEED();
}
} // namespace Service::LDN

View File

@@ -0,0 +1,25 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
#include "core/hle/service/cmif_types.h"
#include "core/hle/service/service.h"
namespace Core {
class System;
}
namespace Service::LDN {
class IClientProcessMonitor final
: public ServiceFramework<IClientProcessMonitor> {
public:
explicit IClientProcessMonitor(Core::System& system_);
~IClientProcessMonitor() override;
private:
Result RegisterClient();
};
} // namespace Service::LDN

View File

@@ -1,9 +1,13 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "core/core.h"
#include "core/hle/service/cmif_serialization.h"
#include "core/hle/service/ldn/ldn.h"
#include "core/hle/service/ldn/client_process_monitor.h"
#include "core/hle/service/ldn/monitor_service.h"
#include "core/hle/service/ldn/sf_monitor_service.h"
#include "core/hle/service/ldn/sf_service.h"
@@ -40,7 +44,7 @@ public:
// clang-format off
static const FunctionInfo functions[] = {
{0, C<&ISystemServiceCreator::CreateSystemLocalCommunicationService>, "CreateSystemLocalCommunicationService"},
{1, nullptr, "CreateClientProcessMonitor"} // 18.0.0+
{1, C<&ISystemServiceCreator::CreateClientProcessMonitor>, "CreateClientProcessMonitor"} // 18.0.0+
};
// clang-format on
@@ -55,6 +59,14 @@ private:
*out_interface = std::make_shared<ISystemLocalCommunicationService>(system);
R_SUCCEED();
}
Result CreateClientProcessMonitor(
OutInterface<IClientProcessMonitor> out_interface) {
LOG_DEBUG(Service_LDN, "called");
*out_interface = std::make_shared<IClientProcessMonitor>(system);
R_SUCCEED();
}
};
class IUserServiceCreator final : public ServiceFramework<IUserServiceCreator> {
@@ -62,8 +74,8 @@ public:
explicit IUserServiceCreator(Core::System& system_) : ServiceFramework{system_, "ldn:u"} {
// clang-format off
static const FunctionInfo functions[] = {
{0, C<&IUserServiceCreator::CreateUserLocalCommunicationService>, "CreateUserLocalCommunicationService"},
{1, nullptr, "CreateClientProcessMonitor"} // 18.0.0+
{0, D<&IUserServiceCreator::CreateUserLocalCommunicationService>, "CreateUserLocalCommunicationService"},
{1, D<&IUserServiceCreator::CreateClientProcessMonitor>, "CreateClientProcessMonitor"} // 18.0.0+
};
// clang-format on
@@ -78,6 +90,14 @@ private:
*out_interface = std::make_shared<IUserLocalCommunicationService>(system);
R_SUCCEED();
}
Result CreateClientProcessMonitor(
OutInterface<IClientProcessMonitor> out_interface) {
LOG_DEBUG(Service_LDN, "called");
*out_interface = std::make_shared<IClientProcessMonitor>(system);
R_SUCCEED();
}
};
class ISfServiceCreator final : public ServiceFramework<ISfServiceCreator> {

View File

@@ -27,35 +27,35 @@ IUserLocalCommunicationService::IUserLocalCommunicationService(Core::System& sys
lan_discovery{} {
// clang-format off
static const FunctionInfo functions[] = {
{0, C<&IUserLocalCommunicationService::GetState>, "GetState"},
{1, C<&IUserLocalCommunicationService::GetNetworkInfo>, "GetNetworkInfo"},
{2, C<&IUserLocalCommunicationService::GetIpv4Address>, "GetIpv4Address"},
{3, C<&IUserLocalCommunicationService::GetDisconnectReason>, "GetDisconnectReason"},
{4, C<&IUserLocalCommunicationService::GetSecurityParameter>, "GetSecurityParameter"},
{5, C<&IUserLocalCommunicationService::GetNetworkConfig>, "GetNetworkConfig"},
{100, C<&IUserLocalCommunicationService::AttachStateChangeEvent>, "AttachStateChangeEvent"},
{101, C<&IUserLocalCommunicationService::GetNetworkInfoLatestUpdate>, "GetNetworkInfoLatestUpdate"},
{102, C<&IUserLocalCommunicationService::Scan>, "Scan"},
{103, C<&IUserLocalCommunicationService::ScanPrivate>, "ScanPrivate"},
{104, C<&IUserLocalCommunicationService::SetWirelessControllerRestriction>, "SetWirelessControllerRestriction"},
{200, C<&IUserLocalCommunicationService::OpenAccessPoint>, "OpenAccessPoint"},
{201, C<&IUserLocalCommunicationService::CloseAccessPoint>, "CloseAccessPoint"},
{202, C<&IUserLocalCommunicationService::CreateNetwork>, "CreateNetwork"},
{203, C<&IUserLocalCommunicationService::CreateNetworkPrivate>, "CreateNetworkPrivate"},
{204, C<&IUserLocalCommunicationService::DestroyNetwork>, "DestroyNetwork"},
{0, D<&IUserLocalCommunicationService::GetState>, "GetState"},
{1, D<&IUserLocalCommunicationService::GetNetworkInfo>, "GetNetworkInfo"},
{2, D<&IUserLocalCommunicationService::GetIpv4Address>, "GetIpv4Address"},
{3, D<&IUserLocalCommunicationService::GetDisconnectReason>, "GetDisconnectReason"},
{4, D<&IUserLocalCommunicationService::GetSecurityParameter>, "GetSecurityParameter"},
{5, D<&IUserLocalCommunicationService::GetNetworkConfig>, "GetNetworkConfig"},
{100, D<&IUserLocalCommunicationService::AttachStateChangeEvent>, "AttachStateChangeEvent"},
{101, D<&IUserLocalCommunicationService::GetNetworkInfoLatestUpdate>, "GetNetworkInfoLatestUpdate"},
{102, D<&IUserLocalCommunicationService::Scan>, "Scan"},
{103, D<&IUserLocalCommunicationService::ScanPrivate>, "ScanPrivate"},
{104, D<&IUserLocalCommunicationService::SetWirelessControllerRestriction>, "SetWirelessControllerRestriction"},
{200, D<&IUserLocalCommunicationService::OpenAccessPoint>, "OpenAccessPoint"},
{201, D<&IUserLocalCommunicationService::CloseAccessPoint>, "CloseAccessPoint"},
{202, D<&IUserLocalCommunicationService::CreateNetwork>, "CreateNetwork"},
{203, D<&IUserLocalCommunicationService::CreateNetworkPrivate>, "CreateNetworkPrivate"},
{204, D<&IUserLocalCommunicationService::DestroyNetwork>, "DestroyNetwork"},
{205, nullptr, "Reject"},
{206, C<&IUserLocalCommunicationService::SetAdvertiseData>, "SetAdvertiseData"},
{207, C<&IUserLocalCommunicationService::SetStationAcceptPolicy>, "SetStationAcceptPolicy"},
{208, C<&IUserLocalCommunicationService::AddAcceptFilterEntry>, "AddAcceptFilterEntry"},
{206, D<&IUserLocalCommunicationService::SetAdvertiseData>, "SetAdvertiseData"},
{207, D<&IUserLocalCommunicationService::SetStationAcceptPolicy>, "SetStationAcceptPolicy"},
{208, D<&IUserLocalCommunicationService::AddAcceptFilterEntry>, "AddAcceptFilterEntry"},
{209, nullptr, "ClearAcceptFilter"},
{300, C<&IUserLocalCommunicationService::OpenStation>, "OpenStation"},
{301, C<&IUserLocalCommunicationService::CloseStation>, "CloseStation"},
{302, C<&IUserLocalCommunicationService::Connect>, "Connect"},
{300, D<&IUserLocalCommunicationService::OpenStation>, "OpenStation"},
{301, D<&IUserLocalCommunicationService::CloseStation>, "CloseStation"},
{302, D<&IUserLocalCommunicationService::Connect>, "Connect"},
{303, nullptr, "ConnectPrivate"},
{304, C<&IUserLocalCommunicationService::Disconnect>, "Disconnect"},
{400, C<&IUserLocalCommunicationService::Initialize>, "Initialize"},
{401, C<&IUserLocalCommunicationService::Finalize>, "Finalize"},
{402, C<&IUserLocalCommunicationService::Initialize2>, "Initialize2"},
{304, D<&IUserLocalCommunicationService::Disconnect>, "Disconnect"},
{400, D<&IUserLocalCommunicationService::Initialize>, "Initialize"},
{401, D<&IUserLocalCommunicationService::Finalize>, "Finalize"},
{402, D<&IUserLocalCommunicationService::Initialize2>, "Initialize2"},
};
// clang-format on

View File

@@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@@ -112,7 +115,7 @@ void NintendoFigurineDatabase::CleanDatabase() {
void NintendoFigurineDatabase::CorruptCrc() {
crc = GenerateDatabaseCrc();
crc = ~crc;
crc = static_cast<u16>(~crc);
}
Result NintendoFigurineDatabase::CheckIntegrity() {

View File

@@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@@ -7,43 +10,43 @@
#include "core/hle/service/server_manager.h"
#include "core/hle/service/sm/sm.h"
#include <vector>
namespace Service::MM {
enum class Module : u32 {
CPU = 0,
GPU = 1,
EMC = 2,
SYS_BUS = 3,
M_SELECT = 4,
NVDEC = 5,
NVENC = 6,
NVJPG = 7,
TEST = 8
};
class Session {
public:
Session(Module module_, u32 request_id_, bool is_auto_clear_event_) {
this->module = module_;
this->request_id = request_id_;
this->is_auto_clear_event = is_auto_clear_event_;
this->min = 0;
this->max = -1;
};
public:
Module module;
u32 request_id, min;
s32 max;
bool is_auto_clear_event;
#include <vector>
namespace Service::MM {
enum class Module : u32 {
CPU = 0,
GPU = 1,
EMC = 2,
SYS_BUS = 3,
M_SELECT = 4,
NVDEC = 5,
NVENC = 6,
NVJPG = 7,
TEST = 8
};
class Session {
public:
Session(Module module_, u32 request_id_, bool is_auto_clear_event_) {
this->module = module_;
this->request_id = request_id_;
this->is_auto_clear_event = is_auto_clear_event_;
this->min = 0;
this->max = -1;
};
public:
Module module;
u32 request_id, min;
s32 max;
bool is_auto_clear_event;
void SetAndWait(u32 min_, s32 max_) {
this->min = min_;
this->max = max_;
}
};
void SetAndWait(u32 min_, s32 max_) {
this->min = min_;
this->max = max_;
}
};
class MM_U final : public ServiceFramework<MM_U> {
public:
explicit MM_U(Core::System& system_) : ServiceFramework{system_, "mm:u"} {
@@ -65,53 +68,53 @@ public:
private:
void InitializeOld(HLERequestContext& ctx) {
LOG_DEBUG(Service_MM, "(STUBBED) called");
IPC::RequestParser rp{ctx};
const auto module = rp.PopEnum<Module>();
rp.Pop<u32>();
const auto event_clear_mode = rp.Pop<u32>();
LOG_DEBUG(Service_MM, "(STUBBED) called");
IPC::RequestParser rp{ctx};
const auto module = rp.PopEnum<Module>();
rp.Pop<u32>();
const auto event_clear_mode = rp.Pop<u32>();
const bool is_auto_clear_event = event_clear_mode == 1;
sessions.push_back({module, request_id++, is_auto_clear_event});
const bool is_auto_clear_event = event_clear_mode == 1;
sessions.push_back({module, request_id++, is_auto_clear_event});
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
void FinalizeOld(HLERequestContext& ctx) {
LOG_DEBUG(Service_MM, "(STUBBED) called");
IPC::RequestParser rp{ctx};
const auto module = rp.PopEnum<Module>();
for (auto it = sessions.begin(); it != sessions.end(); ++it) {
if (it->module == module) {
sessions.erase(it);
break;
}
}
LOG_DEBUG(Service_MM, "(STUBBED) called");
IPC::RequestParser rp{ctx};
const auto module = rp.PopEnum<Module>();
for (auto it = sessions.begin(); it != sessions.end(); ++it) {
if (it->module == module) {
sessions.erase(it);
break;
}
}
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
void SetAndWaitOld(HLERequestContext& ctx) {
LOG_DEBUG(Service_MM, "(STUBBED) called");
IPC::RequestParser rp{ctx};
const auto module = rp.PopEnum<Module>();
const auto min = rp.Pop<u32>();
const auto max = rp.Pop<s32>();
LOG_DEBUG(Service_MM, "(STUBBED) called");
IPC::RequestParser rp{ctx};
const auto module = rp.PopEnum<Module>();
const auto min = rp.Pop<u32>();
const auto max = rp.Pop<s32>();
for (auto& session : sessions) {
if (session.module == module) {
session.SetAndWait(min, max);
break;
}
}
for (auto& session : sessions) {
if (session.module == module) {
session.SetAndWait(min, max);
break;
}
}
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
@@ -119,72 +122,72 @@ private:
void GetOld(HLERequestContext& ctx) {
LOG_DEBUG(Service_MM, "(STUBBED) called");
IPC::RequestParser rp{ctx};
const auto module = rp.PopEnum<Module>();
for (const auto& session : sessions) {
if (session.module == module) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
rb.Push(session.min);
return;
}
}
IPC::RequestParser rp{ctx};
const auto module = rp.PopEnum<Module>();
for (const auto& session : sessions) {
if (session.module == module) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
rb.Push(session.min);
return;
}
}
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
rb.Push<u32>(0);
rb.Push<u32>(0);
}
void Initialize(HLERequestContext& ctx) {
LOG_DEBUG(Service_MM, "(STUBBED) called");
IPC::RequestParser rp{ctx};
const auto module = rp.PopEnum<Module>();
rp.Pop<u32>();
const auto event_clear_mode = rp.Pop<u32>();
const bool is_auto_clear_event = event_clear_mode == 1;
sessions.push_back({module, request_id++, is_auto_clear_event});
LOG_DEBUG(Service_MM, "(STUBBED) called");
IPC::RequestParser rp{ctx};
const auto module = rp.PopEnum<Module>();
rp.Pop<u32>();
const auto event_clear_mode = rp.Pop<u32>();
const bool is_auto_clear_event = event_clear_mode == 1;
sessions.push_back({module, request_id++, is_auto_clear_event});
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
rb.Push(request_id - 1);
rb.Push(request_id - 1);
}
void Finalize(HLERequestContext& ctx) {
LOG_DEBUG(Service_MM, "(STUBBED) called");
LOG_DEBUG(Service_MM, "(STUBBED) called");
IPC::RequestParser rp{ctx};
const auto id = rp.Pop<u32>();
for (auto it = sessions.begin(); it != sessions.end(); ++it) {
if (it->request_id == id) {
sessions.erase(it);
break;
}
}
IPC::RequestParser rp{ctx};
const auto id = rp.Pop<u32>();
for (auto it = sessions.begin(); it != sessions.end(); ++it) {
if (it->request_id == id) {
sessions.erase(it);
break;
}
}
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
void SetAndWait(HLERequestContext& ctx) {
LOG_DEBUG(Service_MM, "(STUBBED) called");
IPC::RequestParser rp{ctx};
const auto id = rp.Pop<u32>();
const auto min = rp.Pop<u32>();
const auto max = rp.Pop<s32>();
LOG_DEBUG(Service_MM, "(STUBBED) called");
IPC::RequestParser rp{ctx};
const auto id = rp.Pop<u32>();
const auto min = rp.Pop<u32>();
const auto max = rp.Pop<s32>();
for (auto& session : sessions) {
if (session.request_id == id) {
session.SetAndWait(min, max);
break;
}
}
for (auto& session : sessions) {
if (session.request_id == id) {
session.SetAndWait(min, max);
break;
}
}
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
@@ -192,25 +195,25 @@ private:
void Get(HLERequestContext& ctx) {
LOG_DEBUG(Service_MM, "(STUBBED) called");
IPC::RequestParser rp{ctx};
const auto id = rp.Pop<u32>();
for (const auto& session : sessions) {
if (session.request_id == id) {
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
rb.Push(session.min);
return;
}
}
IPC::RequestParser rp{ctx};
const auto id = rp.Pop<u32>();
for (const auto& session : sessions) {
if (session.request_id == id) {
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
rb.Push(session.min);
return;
}
}
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
rb.Push<u32>(0);
rb.Push<u32>(0);
}
std::vector<Session> sessions;
u32 request_id{1};
std::vector<Session> sessions;
u32 request_id{1};
};
void LoopProcess(Core::System& system) {

View File

@@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@@ -232,7 +235,7 @@ public:
{2, nullptr, "ClearDebugResponse"},
{3, nullptr, "RegisterDebugResponse"},
{4, &NIM_ECA::IsLargeResourceAvailable, "IsLargeResourceAvailable"},
{5, &NIM_ECA::CreateServerInterface2, "CreateServerInterface2"} // 17.0.0+
{5, &NIM_ECA::CreateServerInterface2, "CreateServerInterface2"} // 17.0.0+
};
// clang-format on
@@ -241,7 +244,7 @@ public:
private:
void CreateServerInterface(HLERequestContext& ctx) {
LOG_DEBUG(Service_NIM, "(STUBBED) called");
LOG_DEBUG(Service_NIM, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess);
rb.PushIpcInterface<IShopServiceAccessServer>(system);
@@ -258,14 +261,14 @@ private:
rb.Push(ResultSuccess);
rb.Push(false);
}
void CreateServerInterface2(HLERequestContext& ctx) {
LOG_DEBUG(Service_NIM, "(STUBBED) called.");
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess);
rb.PushIpcInterface<IShopServiceAccessServer>(system);
}
void CreateServerInterface2(HLERequestContext& ctx) {
LOG_DEBUG(Service_NIM, "(STUBBED) called.");
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess);
rb.PushIpcInterface<IShopServiceAccessServer>(system);
}
};
class NIM_SHP final : public ServiceFramework<NIM_SHP> {

View File

@@ -1,11 +0,0 @@
// SPDX-FileCopyrightText: 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <boost/container/flat_map.hpp> // used by service.h which is heavily included
#include <boost/intrusive/rbtree.hpp> // used by k_auto_object.h which is heavily included
#include "common/common_precompiled_headers.h"
#include "core/hle/kernel/k_process.h"

View File

@@ -8,7 +8,6 @@
# SPDX-License-Identifier: GPL-3.0-or-later
add_library(yuzu-room STATIC EXCLUDE_FROM_ALL
precompiled_headers.h
yuzu_room.cpp
yuzu_room.h
yuzu_room.rc
@@ -26,8 +25,4 @@ if (MSVC)
endif()
target_link_libraries(yuzu-room PRIVATE ${PLATFORM_LIBRARIES} Threads::Threads)
if (YUZU_USE_PRECOMPILED_HEADERS)
target_precompile_headers(yuzu-room PRIVATE precompiled_headers.h)
endif()
create_target_directory_groups(yuzu-room)

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

@@ -20,16 +20,12 @@ option(DYNARMIC_ENABLE_CPU_FEATURE_DETECTION "Turning this off causes dynarmic t
option(DYNARMIC_ENABLE_NO_EXECUTE_SUPPORT "Enables support for systems that require W^X" ${PLATFORM_OPENBSD})
option(DYNARMIC_FATAL_ERRORS "Errors are fatal" OFF)
option(DYNARMIC_IGNORE_ASSERTS "Ignore asserts" OFF)
option(DYNARMIC_TESTS_USE_UNICORN "Enable fuzzing tests against unicorn" OFF)
CMAKE_DEPENDENT_OPTION(DYNARMIC_USE_LLVM "Support disassembly of jitted x86_64 code using LLVM" OFF "NOT YUZU_DISABLE_LLVM" OFF)
option(DYNARMIC_USE_PRECOMPILED_HEADERS "Use precompiled headers" OFF)
option(DYNARMIC_INSTALL "Install dynarmic headers and CMake files" OFF)
option(DYNARMIC_USE_BUNDLED_EXTERNALS "Use all bundled externals (useful when e.g. cross-compiling)" OFF)
option(DYNARMIC_WARNINGS_AS_ERRORS "Warnings as errors" ${MASTER_PROJECT})
option(DYNARMIC_ENABLE_LTO "Enable LTO" OFF)
# Default to a Release build
@@ -91,11 +87,6 @@ if (MSVC)
string(REPLACE "/Zi" "/Z7" CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG}")
endif()
if (DYNARMIC_WARNINGS_AS_ERRORS)
list(APPEND DYNARMIC_CXX_FLAGS
/WX)
endif()
if (CXX_CLANG)
list(APPEND DYNARMIC_CXX_FLAGS
-Qunused-arguments
@@ -109,21 +100,6 @@ else()
-pedantic
-Wno-missing-braces)
if (ARCHITECTURE STREQUAL "x86_64")
list(APPEND DYNARMIC_CXX_FLAGS -mtune=core2)
endif()
if (DYNARMIC_WARNINGS_AS_ERRORS)
list(APPEND DYNARMIC_CXX_FLAGS
-pedantic-errors
-Werror)
endif()
if (DYNARMIC_FATAL_ERRORS)
list(APPEND DYNARMIC_CXX_FLAGS
-Wfatal-errors)
endif()
if (CXX_GCC)
# GCC produces bogus -Warray-bounds warnings from xbyak headers for code paths that are not
# actually reachable. Specifically, it happens in cases where some code casts an Operand&

View File

@@ -6,10 +6,9 @@ add_library(dynarmic STATIC
backend/block_range_information.cpp
backend/block_range_information.h
backend/exception_handler.h
common/always_false.h
common/assert.cpp
common/assert.h
common/cast_util.h
common/type_util.h
common/common_types.h
common/crypto/aes.cpp
common/crypto/aes.h
@@ -410,15 +409,3 @@ target_compile_definitions(dynarmic PRIVATE FMT_USE_USER_DEFINED_LITERALS=1)
if (DYNARMIC_ENABLE_LTO)
set_property(TARGET dynarmic PROPERTY INTERPROCEDURAL_OPTIMIZATION TRUE)
endif()
if (DYNARMIC_USE_PRECOMPILED_HEADERS)
set(PRECOMPILED_HEADERS "$<$<COMPILE_LANGUAGE:CXX>:${CMAKE_CURRENT_SOURCE_DIR}/ir/ir_emitter.h>")
if ("x86_64" IN_LIST ARCHITECTURE)
list(PREPEND PRECOMPILED_HEADERS "$<$<COMPILE_LANGUAGE:CXX>:<xbyak/xbyak.h$<ANGLE-R>>")
endif()
if ("arm64" IN_LIST ARCHITECTURE)
list(PREPEND PRECOMPILED_HEADERS "$<$<COMPILE_LANGUAGE:CXX>:<oaknut/oaknut.hpp$<ANGLE-R>>")
endif()
target_precompile_headers(dynarmic PRIVATE ${PRECOMPILED_HEADERS})
set(CMAKE_PCH_INSTANTIATE_TEMPLATES ON)
endif()

View File

@@ -13,7 +13,7 @@
#include "dynarmic/backend/arm64/devirtualize.h"
#include "dynarmic/backend/arm64/emit_arm64.h"
#include "dynarmic/backend/arm64/stack_layout.h"
#include "dynarmic/common/cast_util.h"
#include "dynarmic/common/type_util.h"
#include "dynarmic/common/fp/fpcr.h"
#include "dynarmic/frontend/A32/a32_location_descriptor.h"
#include "dynarmic/frontend/A32/translate/a32_translate.h"
@@ -93,9 +93,9 @@ static void* EmitExclusiveReadCallTrampoline(oaknut::CodeGenerator& code, const
code.align(8);
code.l(l_this);
code.dx(mcl::bit_cast<u64>(&conf));
code.dx(std::bit_cast<u64>(&conf));
code.l(l_addr);
code.dx(mcl::bit_cast<u64>(Common::FptrCast(fn)));
code.dx(std::bit_cast<u64>(Common::FptrCast(fn)));
return target;
}
@@ -151,9 +151,9 @@ static void* EmitExclusiveWriteCallTrampoline(oaknut::CodeGenerator& code, const
code.align(8);
code.l(l_this);
code.dx(mcl::bit_cast<u64>(&conf));
code.dx(std::bit_cast<u64>(&conf));
code.l(l_addr);
code.dx(mcl::bit_cast<u64>(Common::FptrCast(fn)));
code.dx(std::bit_cast<u64>(Common::FptrCast(fn)));
return target;
}
@@ -219,7 +219,7 @@ void A32AddressSpace::EmitPrelude() {
code.MOV(Xstate, X1);
code.MOV(Xhalt, X2);
if (conf.page_table) {
code.MOV(Xpagetable, mcl::bit_cast<u64>(conf.page_table));
code.MOV(Xpagetable, std::bit_cast<u64>(conf.page_table));
}
if (conf.fastmem_pointer) {
code.MOV(Xfastmem, *conf.fastmem_pointer);
@@ -258,7 +258,7 @@ void A32AddressSpace::EmitPrelude() {
code.MOV(Xstate, X1);
code.MOV(Xhalt, X2);
if (conf.page_table) {
code.MOV(Xpagetable, mcl::bit_cast<u64>(conf.page_table));
code.MOV(Xpagetable, std::bit_cast<u64>(conf.page_table));
}
if (conf.fastmem_pointer) {
code.MOV(Xfastmem, *conf.fastmem_pointer);
@@ -317,9 +317,9 @@ void A32AddressSpace::EmitPrelude() {
code.align(8);
code.l(l_this);
code.dx(mcl::bit_cast<u64>(this));
code.dx(std::bit_cast<u64>(this));
code.l(l_addr);
code.dx(mcl::bit_cast<u64>(Common::FptrCast(fn)));
code.dx(std::bit_cast<u64>(Common::FptrCast(fn)));
}
prelude_info.return_from_run_code = code.xptr<void*>();
@@ -347,7 +347,7 @@ void A32AddressSpace::EmitPrelude() {
code.align(8);
code.l(l_return_to_dispatcher);
code.dx(mcl::bit_cast<u64>(prelude_info.return_to_dispatcher));
code.dx(std::bit_cast<u64>(prelude_info.return_to_dispatcher));
prelude_info.end_of_prelude = code.offset();
@@ -369,7 +369,7 @@ EmitConfig A32AddressSpace::GetEmitConfig() {
.check_halt_on_memory_access = conf.check_halt_on_memory_access,
.page_table_pointer = mcl::bit_cast<u64>(conf.page_table),
.page_table_pointer = std::bit_cast<u64>(conf.page_table),
.page_table_address_space_bits = 32,
.page_table_pointer_mask_bits = conf.page_table_pointer_mask_bits,
.silently_mirror_page_table = true,

View File

@@ -13,7 +13,7 @@
#include "dynarmic/backend/arm64/devirtualize.h"
#include "dynarmic/backend/arm64/emit_arm64.h"
#include "dynarmic/backend/arm64/stack_layout.h"
#include "dynarmic/common/cast_util.h"
#include "dynarmic/common/type_util.h"
#include "dynarmic/frontend/A64/a64_location_descriptor.h"
#include "dynarmic/frontend/A64/translate/a64_translate.h"
#include "dynarmic/interface/A64/config.h"
@@ -92,9 +92,9 @@ static void* EmitExclusiveReadCallTrampoline(oaknut::CodeGenerator& code, const
code.align(8);
code.l(l_this);
code.dx(mcl::bit_cast<u64>(&conf));
code.dx(std::bit_cast<u64>(&conf));
code.l(l_addr);
code.dx(mcl::bit_cast<u64>(Common::FptrCast(fn)));
code.dx(std::bit_cast<u64>(Common::FptrCast(fn)));
return target;
}
@@ -150,9 +150,9 @@ static void* EmitExclusiveWriteCallTrampoline(oaknut::CodeGenerator& code, const
code.align(8);
code.l(l_this);
code.dx(mcl::bit_cast<u64>(&conf));
code.dx(std::bit_cast<u64>(&conf));
code.l(l_addr);
code.dx(mcl::bit_cast<u64>(Common::FptrCast(fn)));
code.dx(std::bit_cast<u64>(Common::FptrCast(fn)));
return target;
}
@@ -235,9 +235,9 @@ static void* EmitExclusiveRead128CallTrampoline(oaknut::CodeGenerator& code, con
code.align(8);
code.l(l_this);
code.dx(mcl::bit_cast<u64>(&conf));
code.dx(std::bit_cast<u64>(&conf));
code.l(l_addr);
code.dx(mcl::bit_cast<u64>(Common::FptrCast(fn)));
code.dx(std::bit_cast<u64>(Common::FptrCast(fn)));
return target;
}
@@ -317,9 +317,9 @@ static void* EmitExclusiveWrite128CallTrampoline(oaknut::CodeGenerator& code, co
code.align(8);
code.l(l_this);
code.dx(mcl::bit_cast<u64>(&conf));
code.dx(std::bit_cast<u64>(&conf));
code.l(l_addr);
code.dx(mcl::bit_cast<u64>(Common::FptrCast(fn)));
code.dx(std::bit_cast<u64>(Common::FptrCast(fn)));
return target;
}
@@ -396,7 +396,7 @@ void A64AddressSpace::EmitPrelude() {
code.MOV(Xstate, X1);
code.MOV(Xhalt, X2);
if (conf.page_table) {
code.MOV(Xpagetable, mcl::bit_cast<u64>(conf.page_table));
code.MOV(Xpagetable, std::bit_cast<u64>(conf.page_table));
}
if (conf.fastmem_pointer) {
code.MOV(Xfastmem, *conf.fastmem_pointer);
@@ -434,7 +434,7 @@ void A64AddressSpace::EmitPrelude() {
code.MOV(Xstate, X1);
code.MOV(Xhalt, X2);
if (conf.page_table) {
code.MOV(Xpagetable, mcl::bit_cast<u64>(conf.page_table));
code.MOV(Xpagetable, std::bit_cast<u64>(conf.page_table));
}
if (conf.fastmem_pointer) {
code.MOV(Xfastmem, *conf.fastmem_pointer);
@@ -492,9 +492,9 @@ void A64AddressSpace::EmitPrelude() {
code.align(8);
code.l(l_this);
code.dx(mcl::bit_cast<u64>(this));
code.dx(std::bit_cast<u64>(this));
code.l(l_addr);
code.dx(mcl::bit_cast<u64>(Common::FptrCast(fn)));
code.dx(std::bit_cast<u64>(Common::FptrCast(fn)));
}
prelude_info.return_from_run_code = code.xptr<void*>();
@@ -522,7 +522,7 @@ void A64AddressSpace::EmitPrelude() {
code.align(8);
code.l(l_return_to_dispatcher);
code.dx(mcl::bit_cast<u64>(prelude_info.return_to_dispatcher));
code.dx(std::bit_cast<u64>(prelude_info.return_to_dispatcher));
prelude_info.end_of_prelude = code.offset();
@@ -544,7 +544,7 @@ EmitConfig A64AddressSpace::GetEmitConfig() {
.check_halt_on_memory_access = conf.check_halt_on_memory_access,
.page_table_pointer = mcl::bit_cast<u64>(conf.page_table),
.page_table_pointer = std::bit_cast<u64>(conf.page_table),
.page_table_address_space_bits = conf.page_table_address_space_bits,
.page_table_pointer_mask_bits = conf.page_table_pointer_mask_bits,
.silently_mirror_page_table = conf.silently_mirror_page_table,

View File

@@ -17,7 +17,6 @@
#include "dynarmic/common/assert.h"
#include <oaknut/oaknut.hpp>
#include "dynarmic/common/always_false.h"
namespace Dynarmic::Backend::Arm64 {
@@ -37,7 +36,8 @@ constexpr auto Rscratch0() {
} else if constexpr (bitsize == 64) {
return Xscratch0;
} else {
static_assert(Common::always_false_v<mcl::mp::lift_value<bitsize>>);
// TODO: This codepath is regarded as "takeable" on gcc12
return Xscratch0; //static_assert(false);
}
}
@@ -48,7 +48,8 @@ constexpr auto Rscratch1() {
} else if constexpr (bitsize == 64) {
return Xscratch1;
} else {
static_assert(Common::always_false_v<mcl::mp::lift_value<bitsize>>);
// TODO: This codepath is regarded as "takeable" on gcc12
return Xscratch1; //static_assert(false);
}
}

View File

@@ -1,11 +1,14 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
/* This file is part of the dynarmic project.
* Copyright (c) 2022 MerryMage
* SPDX-License-Identifier: 0BSD
*/
#include <cstdio>
#include <mcl/bit_cast.hpp>
#include <bit>
#include <numeric>
#include "dynarmic/backend/arm64/a64_address_space.h"
#include "dynarmic/backend/arm64/a64_jitstate.h"
@@ -13,7 +16,7 @@
#include "dynarmic/backend/arm64/devirtualize.h"
#include "dynarmic/backend/arm64/emit_arm64.h"
#include "dynarmic/backend/arm64/stack_layout.h"
#include "dynarmic/common/cast_util.h"
#include "dynarmic/common/type_util.h"
#include "dynarmic/common/fp/fpcr.h"
#include "dynarmic/common/llvm_disassemble.h"
#include "dynarmic/interface/exclusive_monitor.h"
@@ -99,7 +102,7 @@ void AddressSpace::ClearCache() {
void AddressSpace::DumpDisassembly() const {
for (u32* ptr = mem.ptr(); ptr < code.xptr<u32*>(); ptr++) {
std::printf("%s", Common::DisassembleAArch64(*ptr, mcl::bit_cast<u64>(ptr)).c_str());
std::printf("%s", Common::DisassembleAArch64(*ptr, std::bit_cast<u64>(ptr)).c_str());
}
}
@@ -316,7 +319,7 @@ void AddressSpace::RelinkForDescriptor(IR::LocationDescriptor target_descriptor,
FakeCall AddressSpace::FastmemCallback(u64 host_pc) {
{
const auto host_ptr = mcl::bit_cast<CodePtr>(host_pc);
const auto host_ptr = std::bit_cast<CodePtr>(host_pc);
const auto entry_point = ReverseGetEntryPoint(host_ptr);
if (!entry_point) {

View File

@@ -8,7 +8,8 @@
#pragma once
#include <mcl/bit_cast.hpp>
#include <bit>
#include <numeric>
#include "dynarmic/common/common_types.h"
#include <mcl/type_traits/function_info.hpp>
@@ -23,7 +24,7 @@ struct DevirtualizedCall {
template<auto mfp>
DevirtualizedCall DevirtualizeWindows(mcl::class_type<decltype(mfp)>* this_) {
static_assert(sizeof(mfp) == 8);
return DevirtualizedCall{mcl::bit_cast<u64>(mfp), reinterpret_cast<u64>(this_)};
return DevirtualizedCall{std::bit_cast<u64>(mfp), reinterpret_cast<u64>(this_)};
}
// https://github.com/ARM-software/abi-aa/blob/main/cppabi64/cppabi64.rst#representation-of-pointer-to-member-function
@@ -34,16 +35,16 @@ DevirtualizedCall DevirtualizeDefault(mcl::class_type<decltype(mfp)>* this_) {
u64 ptr;
// LSB is discriminator for if function is virtual. Other bits are this adjustment.
u64 adj;
} mfp_struct = mcl::bit_cast<MemberFunctionPointer>(mfp);
} mfp_struct = std::bit_cast<MemberFunctionPointer>(mfp);
static_assert(sizeof(MemberFunctionPointer) == 16);
static_assert(sizeof(MemberFunctionPointer) == sizeof(mfp));
u64 fn_ptr = mfp_struct.ptr;
u64 this_ptr = mcl::bit_cast<u64>(this_) + (mfp_struct.adj >> 1);
u64 this_ptr = std::bit_cast<u64>(this_) + (mfp_struct.adj >> 1);
if (mfp_struct.adj & 1) {
u64 vtable = mcl::bit_cast_pointee<u64>(this_ptr);
fn_ptr = mcl::bit_cast_pointee<u64>(vtable + fn_ptr);
u64 vtable = std::bit_cast<u64>(this_ptr);
fn_ptr = std::bit_cast<u64>(vtable + fn_ptr);
}
return DevirtualizedCall{fn_ptr, this_ptr};
}

View File

@@ -1,9 +1,13 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
/* This file is part of the dynarmic project.
* Copyright (c) 2022 MerryMage
* SPDX-License-Identifier: 0BSD
*/
#include <mcl/bit_cast.hpp>
#include <bit>
#include <numeric>
#include <oaknut/oaknut.hpp>
#include "dynarmic/backend/arm64/a64_jitstate.h"
@@ -495,7 +499,7 @@ template<>
void EmitIR<IR::Opcode::A64GetTPIDR>(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Inst* inst) {
auto Xvalue = ctx.reg_alloc.WriteX(inst);
RegAlloc::Realize(Xvalue);
code.MOV(Xscratch0, mcl::bit_cast<u64>(ctx.conf.tpidr_el0));
code.MOV(Xscratch0, std::bit_cast<u64>(ctx.conf.tpidr_el0));
code.LDR(Xvalue, Xscratch0);
}
@@ -503,7 +507,7 @@ template<>
void EmitIR<IR::Opcode::A64GetTPIDRRO>(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Inst* inst) {
auto Xvalue = ctx.reg_alloc.WriteX(inst);
RegAlloc::Realize(Xvalue);
code.MOV(Xscratch0, mcl::bit_cast<u64>(ctx.conf.tpidrro_el0));
code.MOV(Xscratch0, std::bit_cast<u64>(ctx.conf.tpidrro_el0));
code.LDR(Xvalue, Xscratch0);
}
@@ -512,7 +516,7 @@ void EmitIR<IR::Opcode::A64SetTPIDR>(oaknut::CodeGenerator& code, EmitContext& c
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
auto Xvalue = ctx.reg_alloc.ReadX(args[0]);
RegAlloc::Realize(Xvalue);
code.MOV(Xscratch0, mcl::bit_cast<u64>(ctx.conf.tpidr_el0));
code.MOV(Xscratch0, std::bit_cast<u64>(ctx.conf.tpidr_el0));
code.STR(Xvalue, Xscratch0);
}

View File

@@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
/* This file is part of the dynarmic project.
* Copyright (c) 2022 MerryMage
* SPDX-License-Identifier: 0BSD
@@ -7,8 +10,8 @@
#include <optional>
#include <utility>
#include <mcl/bit_cast.hpp>
#include <bit>
#include <numeric>
#include <oaknut/oaknut.hpp>
#include "dynarmic/backend/arm64/abi.h"
@@ -548,7 +551,7 @@ void FastmemEmitReadMemory(oaknut::CodeGenerator& code, EmitContext& ctx, IR::In
FastmemPatchInfo{
.marker = marker,
.fc = FakeCall{
.call_pc = mcl::bit_cast<u64>(code.xptr<void*>()),
.call_pc = std::bit_cast<u64>(code.xptr<void*>()),
},
.recompile = ctx.conf.recompile_on_fastmem_failure,
});
@@ -598,7 +601,7 @@ void FastmemEmitWriteMemory(oaknut::CodeGenerator& code, EmitContext& ctx, IR::I
FastmemPatchInfo{
.marker = marker,
.fc = FakeCall{
.call_pc = mcl::bit_cast<u64>(code.xptr<void*>()),
.call_pc = std::bit_cast<u64>(code.xptr<void*>()),
},
.recompile = ctx.conf.recompile_on_fastmem_failure,
});

View File

@@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
/* This file is part of the dynarmic project.
* Copyright (c) 2022 MerryMage
* SPDX-License-Identifier: 0BSD
@@ -12,7 +15,6 @@
#include "dynarmic/backend/arm64/emit_context.h"
#include "dynarmic/backend/arm64/fpsr_manager.h"
#include "dynarmic/backend/arm64/reg_alloc.h"
#include "dynarmic/common/always_false.h"
#include "dynarmic/ir/basic_block.h"
#include "dynarmic/ir/microinstruction.h"
#include "dynarmic/ir/opcodes.h"
@@ -43,7 +45,7 @@ static void EmitTwoOpArranged(oaknut::CodeGenerator& code, EmitContext& ctx, IR:
} else if constexpr (size == 64) {
emit(Qresult->D2(), Qoperand->D2());
} else {
static_assert(Common::always_false_v<mcl::mp::lift_value<size>>);
//static_assert(false);
}
});
}
@@ -66,7 +68,7 @@ static void EmitTwoOpArrangedWiden(oaknut::CodeGenerator& code, EmitContext& ctx
} else if constexpr (size == 32) {
emit(Qresult->D2(), Qoperand->toD().S2());
} else {
static_assert(Common::always_false_v<mcl::mp::lift_value<size>>);
//static_assert(false);
}
});
}
@@ -81,7 +83,7 @@ static void EmitTwoOpArrangedNarrow(oaknut::CodeGenerator& code, EmitContext& ct
} else if constexpr (size == 64) {
emit(Qresult->toD().S2(), Qoperand->D2());
} else {
static_assert(Common::always_false_v<mcl::mp::lift_value<size>>);
//static_assert(false);
}
});
}
@@ -104,7 +106,7 @@ static void EmitTwoOpArrangedPairWiden(oaknut::CodeGenerator& code, EmitContext&
} else if constexpr (size == 32) {
emit(Qresult->D2(), Qoperand->S4());
} else {
static_assert(Common::always_false_v<mcl::mp::lift_value<size>>);
//static_assert(false);
}
});
}
@@ -119,7 +121,7 @@ static void EmitTwoOpArrangedLower(oaknut::CodeGenerator& code, EmitContext& ctx
} else if constexpr (size == 32) {
emit(Qresult->toD().S2(), Qoperand->toD().S2());
} else {
static_assert(Common::always_false_v<mcl::mp::lift_value<size>>);
//static_assert(false);
}
});
}
@@ -147,7 +149,7 @@ static void EmitThreeOpArranged(oaknut::CodeGenerator& code, EmitContext& ctx, I
} else if constexpr (size == 64) {
emit(Qresult->D2(), Qa->D2(), Qb->D2());
} else {
static_assert(Common::always_false_v<mcl::mp::lift_value<size>>);
//static_assert(false);
}
});
}
@@ -172,7 +174,7 @@ static void EmitThreeOpArrangedWiden(oaknut::CodeGenerator& code, EmitContext& c
} else if constexpr (size == 64) {
emit(Qresult->Q1(), Qa->toD().D1(), Qb->toD().D1());
} else {
static_assert(Common::always_false_v<mcl::mp::lift_value<size>>);
//static_assert(false);
}
});
}
@@ -195,7 +197,7 @@ static void EmitThreeOpArrangedLower(oaknut::CodeGenerator& code, EmitContext& c
} else if constexpr (size == 32) {
emit(Qresult->toD().S2(), Qa->toD().S2(), Qb->toD().S2());
} else {
static_assert(Common::always_false_v<mcl::mp::lift_value<size>>);
//static_assert(false);
}
});
}
@@ -217,7 +219,7 @@ static void EmitSaturatedAccumulate(oaknut::CodeGenerator&, EmitContext& ctx, IR
} else if constexpr (size == 64) {
emit(Qaccumulator->D2(), Qoperand->D2());
} else {
static_assert(Common::always_false_v<mcl::mp::lift_value<size>>);
//static_assert(false);
}
}
@@ -238,7 +240,7 @@ static void EmitImmShift(oaknut::CodeGenerator&, EmitContext& ctx, IR::Inst* ins
} else if constexpr (size == 64) {
emit(Qresult->D2(), Qoperand->D2(), shift_amount);
} else {
static_assert(Common::always_false_v<mcl::mp::lift_value<size>>);
//static_assert(false);
}
}
@@ -266,7 +268,7 @@ static void EmitReduce(oaknut::CodeGenerator&, EmitContext& ctx, IR::Inst* inst,
} else if constexpr (size == 64) {
emit(Vresult, Qoperand->D2());
} else {
static_assert(Common::always_false_v<mcl::mp::lift_value<size>>);
//static_assert(false);
}
}

View File

@@ -6,7 +6,8 @@
* SPDX-License-Identifier: 0BSD
*/
#include <mcl/bit_cast.hpp>
#include <bit>
#include <numeric>
#include <mcl/mp/metavalue/lift_value.hpp>
#include <mcl/mp/typelist/cartesian_product.hpp>
#include <mcl/mp/typelist/get.hpp>
@@ -14,7 +15,6 @@
#include <mcl/mp/typelist/list.hpp>
#include <mcl/mp/typelist/lower_to_tuple.hpp>
#include <mcl/type_traits/function_info.hpp>
#include <mcl/type_traits/integer_of_size.hpp>
#include <oaknut/oaknut.hpp>
#include "dynarmic/backend/arm64/a32_jitstate.h"
@@ -24,8 +24,7 @@
#include "dynarmic/backend/arm64/emit_context.h"
#include "dynarmic/backend/arm64/fpsr_manager.h"
#include "dynarmic/backend/arm64/reg_alloc.h"
#include "dynarmic/common/always_false.h"
#include "dynarmic/common/cast_util.h"
#include "dynarmic/common/type_util.h"
#include "dynarmic/common/fp/fpcr.h"
#include "dynarmic/common/fp/fpsr.h"
#include "dynarmic/common/fp/info.h"
@@ -84,7 +83,7 @@ static void EmitTwoOpArranged(oaknut::CodeGenerator& code, EmitContext& ctx, IR:
} else if constexpr (size == 64) {
emit(Qresult->D2(), Qa->D2());
} else {
static_assert(Common::always_false_v<mcl::mp::lift_value<size>>);
//static_assert(false);
}
});
}
@@ -112,7 +111,7 @@ static void EmitThreeOpArranged(oaknut::CodeGenerator& code, EmitContext& ctx, I
} else if constexpr (size == 64) {
emit(Qresult->D2(), Qa->D2(), Qb->D2());
} else {
static_assert(Common::always_false_v<mcl::mp::lift_value<size>>);
//static_assert(false);
}
});
}
@@ -135,7 +134,7 @@ static void EmitFMA(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Inst* ins
} else if constexpr (size == 64) {
emit(Qresult->D2(), Qm->D2(), Qn->D2());
} else {
static_assert(Common::always_false_v<mcl::mp::lift_value<size>>);
//static_assert(false);
}
});
}
@@ -157,7 +156,7 @@ static void EmitFromFixed(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Ins
} else if constexpr (size == 64) {
emit(Qto->D2(), Qfrom->D2(), fbits);
} else {
static_assert(Common::always_false_v<mcl::mp::lift_value<size>>);
//static_assert(false);
}
});
}
@@ -179,7 +178,7 @@ void EmitToFixed(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Inst* inst)
} else if constexpr (fsize == 64) {
return Qto->D2();
} else {
static_assert(Common::always_false_v<mcl::mp::lift_value<fsize>>);
return Qto->D2(); //static_assert(false);
}
}();
auto Vfrom = [&] {
@@ -188,7 +187,7 @@ void EmitToFixed(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Inst* inst)
} else if constexpr (fsize == 64) {
return Qfrom->D2();
} else {
static_assert(Common::always_false_v<mcl::mp::lift_value<fsize>>);
return Qfrom->D2(); //static_assert(false);
}
}();
@@ -271,7 +270,7 @@ static void EmitTwoOpFallbackWithoutRegAlloc(oaknut::CodeGenerator& code, EmitCo
ABI_PushRegisters(code, ABI_CALLER_SAVE & ~(1ull << Qresult.index()), stack_size);
code.MOV(Xscratch0, mcl::bit_cast<u64>(fn));
code.MOV(Xscratch0, std::bit_cast<u64>(fn));
code.ADD(X0, SP, 0 * 16);
code.ADD(X1, SP, 1 * 16);
code.MOV(X2, fpcr);

View File

@@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
/* This file is part of the dynarmic project.
* Copyright (c) 2022 MerryMage
* SPDX-License-Identifier: 0BSD
@@ -12,7 +15,6 @@
#include "dynarmic/backend/arm64/emit_context.h"
#include "dynarmic/backend/arm64/fpsr_manager.h"
#include "dynarmic/backend/arm64/reg_alloc.h"
#include "dynarmic/common/always_false.h"
#include "dynarmic/ir/basic_block.h"
#include "dynarmic/ir/microinstruction.h"
#include "dynarmic/ir/opcodes.h"
@@ -39,7 +41,7 @@ static void Emit(oaknut::CodeGenerator&, EmitContext& ctx, IR::Inst* inst, EmitF
} else if constexpr (size == 64) {
emit(Qresult->D2(), Qa->D2(), Qb->D2());
} else {
static_assert(Common::always_false_v<mcl::mp::lift_value<size>>);
//static_assert(false);
}
}

View File

@@ -11,10 +11,10 @@
#include <algorithm>
#include <array>
#include <iterator>
#include <bit>
#include <numeric>
#include "dynarmic/common/assert.h"
#include <mcl/bit/bit_field.hpp>
#include <mcl/bit_cast.hpp>
#include <mcl/mp/metavalue/lift_value.hpp>
#include "dynarmic/common/common_types.h"
@@ -22,7 +22,6 @@
#include "dynarmic/backend/arm64/emit_context.h"
#include "dynarmic/backend/arm64/fpsr_manager.h"
#include "dynarmic/backend/arm64/verbose_debugging_output.h"
#include "dynarmic/common/always_false.h"
namespace Dynarmic::Backend::Arm64 {
@@ -246,7 +245,7 @@ void RegAlloc::AssertNoMoreUses() const {
}
void RegAlloc::EmitVerboseDebuggingOutput() {
code.MOV(X19, mcl::bit_cast<u64>(&PrintVerboseDebuggingOutputLine)); // Non-volatile register
code.MOV(X19, std::bit_cast<u64>(&PrintVerboseDebuggingOutputLine)); // Non-volatile register
const auto do_location = [&](HostLocInfo& info, HostLocType type, size_t index) {
using namespace oaknut::util;
@@ -301,7 +300,7 @@ int RegAlloc::GenerateImmediate(const IR::Value& value) {
return 0;
} else {
static_assert(Common::always_false_v<mcl::mp::lift_value<kind>>);
return 0;//static_assert(false);
}
}
@@ -370,7 +369,7 @@ int RegAlloc::RealizeReadImpl(const IR::Value& value) {
} else if constexpr (required_kind == HostLoc::Kind::Flags) {
ASSERT_FALSE("A simple read from flags is likely a logic error.");
} else {
static_assert(Common::always_false_v<mcl::mp::lift_value<required_kind>>);
return 0;//static_assert(false);
}
}
@@ -395,7 +394,7 @@ int RegAlloc::RealizeWriteImpl(const IR::Inst* value) {
flags.SetupLocation(value);
return 0;
} else {
static_assert(Common::always_false_v<mcl::mp::lift_value<kind>>);
return 0; //static_assert(false);
}
}
@@ -416,7 +415,7 @@ int RegAlloc::RealizeReadWriteImpl(const IR::Value& read_value, const IR::Inst*
} else if constexpr (kind == HostLoc::Kind::Flags) {
ASSERT_FALSE("Incorrect function for ReadWrite of flags");
} else {
static_assert(Common::always_false_v<mcl::mp::lift_value<kind>>);
return write_loc; //static_assert(false);
}
}

View File

@@ -11,19 +11,17 @@
#include <functional>
#include <memory>
#include <optional>
#include <mcl/macro/architecture.hpp>
#include "dynarmic/common/common_types.h"
#if defined(MCL_ARCHITECTURE_X86_64)
#if defined(ARCHITECTURE_x86_64)
namespace Dynarmic::Backend::X64 {
class BlockOfCode;
} // namespace Dynarmic::Backend::X64
#elif defined(MCL_ARCHITECTURE_ARM64)
#elif defined(ARCHITECTURE_arm64)
namespace oaknut {
class CodeBlock;
} // namespace oaknut
#elif defined(MCL_ARCHITECTURE_RISCV)
#elif defined(ARCHITECTURE_riscv64)
namespace Dynarmic::Backend::RV64 {
class CodeBlock;
} // namespace Dynarmic::Backend::RV64
@@ -33,16 +31,16 @@ class CodeBlock;
namespace Dynarmic::Backend {
#if defined(MCL_ARCHITECTURE_X86_64)
#if defined(ARCHITECTURE_x86_64)
struct FakeCall {
u64 call_rip;
u64 ret_rip;
};
#elif defined(MCL_ARCHITECTURE_ARM64)
#elif defined(ARCHITECTURE_arm64)
struct FakeCall {
u64 call_pc;
};
#elif defined(MCL_ARCHITECTURE_RISCV)
#elif defined(ARCHITECTURE_riscv64)
struct FakeCall {
};
#else
@@ -54,11 +52,11 @@ public:
ExceptionHandler();
~ExceptionHandler();
#if defined(MCL_ARCHITECTURE_X86_64)
#if defined(ARCHITECTURE_x86_64)
void Register(X64::BlockOfCode& code);
#elif defined(MCL_ARCHITECTURE_ARM64)
#elif defined(ARCHITECTURE_arm64)
void Register(oaknut::CodeBlock& mem, std::size_t mem_size);
#elif defined(MCL_ARCHITECTURE_RISCV)
#elif defined(ARCHITECTURE_riscv64)
void Register(RV64::CodeBlock& mem, std::size_t mem_size);
#else
# error "Invalid architecture"

View File

@@ -19,8 +19,7 @@
#include <fmt/format.h>
#include "dynarmic/common/assert.h"
#include <mcl/bit_cast.hpp>
#include <mcl/macro/architecture.hpp>
#include <numeric>
#include "dynarmic/common/common_types.h"
#include "dynarmic/backend/exception_handler.h"
@@ -146,7 +145,7 @@ kern_return_t MachHandler::HandleRequest(x86_thread_state64_t* ts) {
FakeCall fc = iter->cb(ts->__rip);
ts->__rsp -= sizeof(u64);
*mcl::bit_cast<u64*>(ts->__rsp) = fc.ret_rip;
*std::bit_cast<u64*>(ts->__rsp) = fc.ret_rip;
ts->__rip = fc.call_rip;
return KERN_SUCCESS;
@@ -271,13 +270,13 @@ ExceptionHandler::~ExceptionHandler() = default;
#if defined(ARCHITECTURE_x86_64)
void ExceptionHandler::Register(X64::BlockOfCode& code) {
const u64 code_begin = mcl::bit_cast<u64>(code.getCode());
const u64 code_begin = std::bit_cast<u64>(code.getCode());
const u64 code_end = code_begin + code.GetTotalCodeSize();
impl = std::make_unique<Impl>(code_begin, code_end);
}
#elif defined(ARCHITECTURE_arm64)
void ExceptionHandler::Register(oaknut::CodeBlock& mem, std::size_t size) {
const u64 code_begin = mcl::bit_cast<u64>(mem.ptr());
const u64 code_begin = std::bit_cast<u64>(mem.ptr());
const u64 code_end = code_begin + size;
impl = std::make_unique<Impl>(code_begin, code_end);
}

View File

@@ -3,8 +3,6 @@
* SPDX-License-Identifier: 0BSD
*/
#include <mcl/macro/architecture.hpp>
#if defined(ARCHITECTURE_x86_64)
# include "dynarmic/backend/x64/mig/mach_exc_server.c"
#elif defined(ARCHITECTURE_arm64)

View File

@@ -27,7 +27,7 @@
#else
# error "Invalid architecture"
#endif
#include <mcl/bit_cast.hpp>
#include <numeric>
namespace Dynarmic::Backend {
@@ -122,7 +122,7 @@ void SigHandler::SigAction(int sig, siginfo_t* info, void* raw_context) {
if (auto const iter = sig_handler->FindCodeBlockInfo(CTX_RIP); iter != sig_handler->code_block_infos.end()) {
FakeCall fc = iter->second.cb(CTX_RIP);
CTX_RSP -= sizeof(u64);
*mcl::bit_cast<u64*>(CTX_RSP) = fc.ret_rip;
*std::bit_cast<u64*>(CTX_RSP) = fc.ret_rip;
CTX_RIP = fc.call_rip;
return;
}
@@ -187,17 +187,17 @@ private:
ExceptionHandler::ExceptionHandler() = default;
ExceptionHandler::~ExceptionHandler() = default;
#if defined(MCL_ARCHITECTURE_X86_64)
#if defined(ARCHITECTURE_x86_64)
void ExceptionHandler::Register(X64::BlockOfCode& code) {
impl = std::make_unique<Impl>(mcl::bit_cast<u64>(code.getCode()), code.GetTotalCodeSize());
impl = std::make_unique<Impl>(std::bit_cast<u64>(code.getCode()), code.GetTotalCodeSize());
}
#elif defined(MCL_ARCHITECTURE_ARM64)
#elif defined(ARCHITECTURE_arm64)
void ExceptionHandler::Register(oaknut::CodeBlock& mem, std::size_t size) {
impl = std::make_unique<Impl>(mcl::bit_cast<u64>(mem.ptr()), size);
impl = std::make_unique<Impl>(std::bit_cast<u64>(mem.ptr()), size);
}
#elif defined(MCL_ARCHITECTURE_RISCV)
#elif defined(ARCHITECTURE_riscv64)
void ExceptionHandler::Register(RV64::CodeBlock& mem, std::size_t size) {
impl = std::make_unique<Impl>(mcl::bit_cast<u64>(mem.ptr<u64>()), size);
impl = std::make_unique<Impl>(std::bit_cast<u64>(mem.ptr<u64>()), size);
}
#else
# error "Invalid architecture"

View File

@@ -6,8 +6,6 @@
* SPDX-License-Identifier: 0BSD
*/
#include <mcl/macro/architecture.hpp>
#if defined(ARCHITECTURE_x86_64)
# include "dynarmic/backend/x64/exception_handler_windows.cpp"
#elif defined(ARCHITECTURE_arm64)

View File

@@ -15,7 +15,6 @@
#include <mcl/mp/metavalue/lift_value.hpp>
#include "dynarmic/common/common_types.h"
#include "dynarmic/common/always_false.h"
namespace Dynarmic::Backend::RV64 {
@@ -164,7 +163,7 @@ u32 RegAlloc::GenerateImmediate(const IR::Value& value) {
} else if constexpr (kind == HostLoc::Kind::Fpr) {
UNIMPLEMENTED();
} else {
static_assert(Common::always_false_v<mcl::mp::lift_value<kind>>);
//static_assert(false);
}
return 0;
@@ -227,7 +226,7 @@ u32 RegAlloc::RealizeReadImpl(const IR::Value& value) {
fprs[new_location_index].realized = true;
return new_location_index;
} else {
static_assert(Common::always_false_v<mcl::mp::lift_value<required_kind>>);
return 0; //static_assert(false);
}
}
@@ -254,7 +253,7 @@ u32 RegAlloc::RealizeWriteImpl(const IR::Inst* value) {
setup_location(fprs[new_location_index]);
return new_location_index;
} else {
static_assert(Common::always_false_v<mcl::mp::lift_value<required_kind>>);
return 0;//static_assert(false);
}
}

View File

@@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
/* This file is part of the dynarmic project.
* Copyright (c) 2022 MerryMage
* SPDX-License-Identifier: 0BSD
@@ -10,7 +13,6 @@
#include <fmt/format.h>
#include <fmt/ostream.h>
#include <mcl/type_traits/integer_of_size.hpp>
#include <xbyak/xbyak.h>
#include "dynarmic/backend/x64/a32_emit_x64.h"

View File

@@ -9,11 +9,12 @@
#include <functional>
#include <memory>
#include <mutex>
#include <bit>
#include <boost/icl/interval_set.hpp>
#include <fmt/format.h>
#include "dynarmic/common/assert.h"
#include <mcl/bit_cast.hpp>
#include <numeric>
#include <mcl/scope_exit.hpp>
#include "dynarmic/common/common_types.h"
@@ -47,7 +48,7 @@ static RunCodeCallbacks GenRunCodeCallbacks(A32::UserCallbacks* cb, CodePtr (*Lo
static std::function<void(BlockOfCode&)> GenRCP(const A32::UserConfig& conf) {
return [conf](BlockOfCode& code) {
if (conf.page_table) {
code.mov(code.r14, mcl::bit_cast<u64>(conf.page_table));
code.mov(code.r14, std::bit_cast<u64>(conf.page_table));
}
if (conf.fastmem_pointer) {
code.mov(code.r13, *conf.fastmem_pointer);

View File

@@ -13,7 +13,6 @@
#include "dynarmic/common/assert.h"
#include <mcl/scope_exit.hpp>
#include "dynarmic/common/common_types.h"
#include <mcl/type_traits/integer_of_size.hpp>
#include <boost/container/static_vector.hpp>
#include "dynarmic/backend/x64/a64_jitstate.h"

View File

@@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
/* This file is part of the dynarmic project.
* Copyright (c) 2022 MerryMage
* SPDX-License-Identifier: 0BSD
@@ -10,7 +13,6 @@
#include <fmt/format.h>
#include <fmt/ostream.h>
#include <mcl/type_traits/integer_of_size.hpp>
#include <xbyak/xbyak.h>
#include "dynarmic/backend/x64/a64_emit_x64.h"

View File

@@ -9,10 +9,11 @@
#include <cstring>
#include <memory>
#include <mutex>
#include <bit>
#include <boost/icl/interval_set.hpp>
#include "dynarmic/common/assert.h"
#include <mcl/bit_cast.hpp>
#include <numeric>
#include <mcl/scope_exit.hpp>
#include "dynarmic/backend/x64/a64_emit_x64.h"
@@ -43,7 +44,7 @@ static RunCodeCallbacks GenRunCodeCallbacks(A64::UserCallbacks* cb, CodePtr (*Lo
static std::function<void(BlockOfCode&)> GenRCP(const A64::UserConfig& conf) {
return [conf](BlockOfCode& code) {
if (conf.page_table) {
code.mov(code.r14, mcl::bit_cast<u64>(conf.page_table));
code.mov(code.r14, std::bit_cast<u64>(conf.page_table));
}
if (conf.fastmem_pointer) {
code.mov(code.r13, *conf.fastmem_pointer);

View File

@@ -23,7 +23,7 @@
#include "dynarmic/backend/x64/constant_pool.h"
#include "dynarmic/backend/x64/host_feature.h"
#include "dynarmic/backend/x64/jitstate_info.h"
#include "dynarmic/common/cast_util.h"
#include "dynarmic/common/type_util.h"
#include "dynarmic/interface/halt_reason.h"
#include "dynarmic/ir/cond.h"

View File

@@ -10,8 +10,8 @@
#include <cstring>
#include <utility>
#include <mcl/bit_cast.hpp>
#include <bit>
#include <numeric>
#include "dynarmic/common/common_types.h"
#include <mcl/type_traits/function_info.hpp>
@@ -42,7 +42,7 @@ ArgCallback DevirtualizeGeneric(mcl::class_type<decltype(mfp)>* this_) {
template<auto mfp>
ArgCallback DevirtualizeWindows(mcl::class_type<decltype(mfp)>* this_) {
static_assert(sizeof(mfp) == 8);
return ArgCallback{mcl::bit_cast<u64>(mfp), reinterpret_cast<u64>(this_)};
return ArgCallback{std::bit_cast<u64>(mfp), reinterpret_cast<u64>(this_)};
}
template<auto mfp>
@@ -53,7 +53,7 @@ ArgCallback DevirtualizeItanium(mcl::class_type<decltype(mfp)>* this_) {
u64 ptr;
/// The required adjustment to `this`, prior to the call.
u64 adj;
} mfp_struct = mcl::bit_cast<MemberFunctionPointer>(mfp);
} mfp_struct = std::bit_cast<MemberFunctionPointer>(mfp);
static_assert(sizeof(MemberFunctionPointer) == 16);
static_assert(sizeof(MemberFunctionPointer) == sizeof(mfp));
@@ -61,8 +61,8 @@ ArgCallback DevirtualizeItanium(mcl::class_type<decltype(mfp)>* this_) {
u64 fn_ptr = mfp_struct.ptr;
u64 this_ptr = reinterpret_cast<u64>(this_) + mfp_struct.adj;
if (mfp_struct.ptr & 1) {
u64 vtable = mcl::bit_cast_pointee<u64>(this_ptr);
fn_ptr = mcl::bit_cast_pointee<u64>(vtable + fn_ptr - 1);
u64 vtable = std::bit_cast<u64>(this_ptr);
fn_ptr = std::bit_cast<u64>(vtable + fn_ptr - 1);
}
return ArgCallback{fn_ptr, this_ptr};
}

View File

@@ -18,14 +18,13 @@
#include <mcl/mp/typelist/list.hpp>
#include <mcl/mp/typelist/lower_to_tuple.hpp>
#include "dynarmic/common/common_types.h"
#include <mcl/type_traits/integer_of_size.hpp>
#include <xbyak/xbyak.h>
#include "dynarmic/backend/x64/abi.h"
#include "dynarmic/backend/x64/block_of_code.h"
#include "dynarmic/backend/x64/constants.h"
#include "dynarmic/backend/x64/emit_x64.h"
#include "dynarmic/common/cast_util.h"
#include "dynarmic/common/type_util.h"
#include "dynarmic/common/fp/fpcr.h"
#include "dynarmic/common/fp/fpsr.h"
#include "dynarmic/common/fp/info.h"
@@ -36,22 +35,8 @@
#include "dynarmic/ir/basic_block.h"
#include "dynarmic/ir/microinstruction.h"
#define FCODE(NAME) \
[&code](auto... args) { \
if constexpr (fsize == 32) { \
code.NAME##s(args...); \
} else { \
code.NAME##d(args...); \
} \
}
#define ICODE(NAME) \
[&code](auto... args) { \
if constexpr (fsize == 32) { \
code.NAME##d(args...); \
} else { \
code.NAME##q(args...); \
} \
}
#define FCODE(NAME) [&](auto... args) { if (fsize == 32) code.NAME##s(args...); else code.NAME##d(args...); }
#define ICODE(NAME) [&](auto... args) { if (fsize == 32) code.NAME##d(args...); else code.NAME##q(args...); }
namespace Dynarmic::Backend::X64 {
@@ -105,7 +90,7 @@ void ForceDenormalsToZero(BlockOfCode& code, std::initializer_list<Xbyak::Xmm> t
for (const Xbyak::Xmm& xmm : to_daz) {
code.movaps(xmm0, code.Const(xword, fsize == 32 ? f32_non_sign_mask : f64_non_sign_mask));
code.andps(xmm0, xmm);
if constexpr (fsize == 32) {
if (fsize == 32) {
code.pcmpgtd(xmm0, code.Const(xword, f32_smallest_normal - 1));
} else if (code.HasHostFeature(HostFeature::SSE42)) {
code.pcmpgtq(xmm0, code.Const(xword, f64_smallest_normal - 1));
@@ -120,13 +105,11 @@ void ForceDenormalsToZero(BlockOfCode& code, std::initializer_list<Xbyak::Xmm> t
template<size_t fsize>
void DenormalsAreZero(BlockOfCode& code, EmitContext& ctx, std::initializer_list<Xbyak::Xmm> to_daz) {
if (ctx.FPCR().FZ()) {
if (ctx.FPCR().FZ())
ForceDenormalsToZero<fsize>(code, to_daz);
}
}
template<size_t fsize>
void ZeroIfNaN(BlockOfCode& code, Xbyak::Xmm xmm_value, Xbyak::Xmm xmm_scratch) {
void ZeroIfNaN(BlockOfCode& code, Xbyak::Xmm xmm_value, Xbyak::Xmm xmm_scratch, size_t fsize) {
if (code.HasHostFeature(HostFeature::AVX512_OrthoFloat)) {
constexpr u32 nan_to_zero = FixupLUT(FpFixup::PosZero,
FpFixup::PosZero);
@@ -141,8 +124,7 @@ void ZeroIfNaN(BlockOfCode& code, Xbyak::Xmm xmm_value, Xbyak::Xmm xmm_scratch)
}
}
template<size_t fsize>
void ForceToDefaultNaN(BlockOfCode& code, Xbyak::Xmm result) {
void ForceToDefaultNaN(BlockOfCode& code, Xbyak::Xmm result, size_t fsize) {
if (code.HasHostFeature(HostFeature::AVX512_OrthoFloat)) {
const Xbyak::Opmask nan_mask = k1;
FCODE(vfpclasss)(nan_mask, result, u8(FpClass::QNaN | FpClass::SNaN));
@@ -208,7 +190,7 @@ void PostProcessNaN(BlockOfCode& code, Xbyak::Xmm result, Xbyak::Xmm tmp) {
// We allow for the case where op1 and result are the same register. We do not read from op1 once result is written to.
template<size_t fsize>
void EmitPostProcessNaNs(BlockOfCode& code, Xbyak::Xmm result, Xbyak::Xmm op1, Xbyak::Xmm op2, Xbyak::Reg64 tmp, Xbyak::Label end) {
using FPT = mcl::unsigned_integer_of_size<fsize>;
using FPT = Common::UnsignedIntegerN<fsize>;
constexpr FPT exponent_mask = FP::FPInfo<FPT>::exponent_mask;
constexpr FPT mantissa_msb = FP::FPInfo<FPT>::mantissa_msb;
constexpr u8 mantissa_msb_bit = static_cast<u8>(FP::FPInfo<FPT>::explicit_mantissa_width - 1);
@@ -236,7 +218,7 @@ void EmitPostProcessNaNs(BlockOfCode& code, Xbyak::Xmm result, Xbyak::Xmm op1, X
}
constexpr size_t shift = fsize == 32 ? 0 : 48;
if constexpr (fsize == 32) {
if (fsize == 32) {
code.movd(tmp.cvt32(), xmm0);
} else {
// We do this to avoid requiring 64-bit immediates
@@ -252,7 +234,7 @@ void EmitPostProcessNaNs(BlockOfCode& code, Xbyak::Xmm result, Xbyak::Xmm op1, X
// op1 == QNaN && op2 == SNaN <<< The problematic case
// op1 == QNaN && op2 == Inf
if constexpr (fsize == 32) {
if (fsize == 32) {
code.movd(tmp.cvt32(), op2);
code.shl(tmp.cvt32(), 32 - mantissa_msb_bit);
} else {
@@ -291,7 +273,7 @@ void FPTwoOp(BlockOfCode& code, EmitContext& ctx, IR::Inst* inst, Function fn) {
if (ctx.HasOptimization(OptimizationFlag::Unsafe_InaccurateNaN)) {
// Do nothing
} else if (ctx.FPCR().DN()) {
ForceToDefaultNaN<fsize>(code, result);
ForceToDefaultNaN(code, result, fsize);
} else {
PostProcessNaN<fsize>(code, result, xmm0);
}
@@ -302,7 +284,7 @@ void FPTwoOp(BlockOfCode& code, EmitContext& ctx, IR::Inst* inst, Function fn) {
template<size_t fsize, typename Function>
void FPThreeOp(BlockOfCode& code, EmitContext& ctx, IR::Inst* inst, Function fn) {
using FPT = mcl::unsigned_integer_of_size<fsize>;
using FPT = Common::UnsignedIntegerN<fsize>;
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
@@ -317,7 +299,7 @@ void FPThreeOp(BlockOfCode& code, EmitContext& ctx, IR::Inst* inst, Function fn)
}
if (!ctx.HasOptimization(OptimizationFlag::Unsafe_InaccurateNaN)) {
ForceToDefaultNaN<fsize>(code, result);
ForceToDefaultNaN(code, result, fsize);
}
ctx.reg_alloc.DefineValue(inst, result);
@@ -361,7 +343,7 @@ void FPThreeOp(BlockOfCode& code, EmitContext& ctx, IR::Inst* inst, Function fn)
template<size_t fsize>
void FPAbs(BlockOfCode& code, EmitContext& ctx, IR::Inst* inst) {
using FPT = mcl::unsigned_integer_of_size<fsize>;
using FPT = Common::UnsignedIntegerN<fsize>;
constexpr FPT non_sign_mask = FP::FPInfo<FPT>::sign_mask - FPT(1u);
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
@@ -387,7 +369,7 @@ void EmitX64::EmitFPAbs64(EmitContext& ctx, IR::Inst* inst) {
template<size_t fsize>
void FPNeg(BlockOfCode& code, EmitContext& ctx, IR::Inst* inst) {
using FPT = mcl::unsigned_integer_of_size<fsize>;
using FPT = Common::UnsignedIntegerN<fsize>;
constexpr FPT sign_mask = FP::FPInfo<FPT>::sign_mask;
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
@@ -442,7 +424,7 @@ static void EmitFPMinMax(BlockOfCode& code, EmitContext& ctx, IR::Inst* inst) {
FCODE(ucomis)(result, operand);
code.jz(*equal, code.T_NEAR);
if constexpr (is_max) {
if (is_max) {
FCODE(maxs)(result, operand);
} else {
FCODE(mins)(result, operand);
@@ -454,7 +436,7 @@ static void EmitFPMinMax(BlockOfCode& code, EmitContext& ctx, IR::Inst* inst) {
code.L(*equal);
code.jp(nan);
if constexpr (is_max) {
if (is_max) {
code.andps(result, operand);
} else {
code.orps(result, operand);
@@ -477,7 +459,7 @@ static void EmitFPMinMax(BlockOfCode& code, EmitContext& ctx, IR::Inst* inst) {
template<size_t fsize, bool is_max>
static inline void EmitFPMinMaxNumeric(BlockOfCode& code, EmitContext& ctx, IR::Inst* inst) noexcept {
using FPT = mcl::unsigned_integer_of_size<fsize>;
using FPT = Common::UnsignedIntegerN<fsize>;
constexpr FPT default_nan = FP::FPInfo<FPT>::DefaultNaN();
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
@@ -502,7 +484,7 @@ static inline void EmitFPMinMaxNumeric(BlockOfCode& code, EmitContext& ctx, IR::
tmp.setBit(fsize);
const auto move_to_tmp = [=, &code](const Xbyak::Xmm& xmm) {
if constexpr (fsize == 32) {
if (fsize == 32) {
code.movd(tmp.cvt32(), xmm);
} else {
code.movq(tmp.cvt64(), xmm);
@@ -513,7 +495,7 @@ static inline void EmitFPMinMaxNumeric(BlockOfCode& code, EmitContext& ctx, IR::
FCODE(ucomis)(op1, op2);
code.jz(*z, code.T_NEAR);
if constexpr (is_max) {
if (is_max) {
FCODE(maxs)(op2, op1);
} else {
FCODE(mins)(op2, op1);
@@ -527,7 +509,7 @@ static inline void EmitFPMinMaxNumeric(BlockOfCode& code, EmitContext& ctx, IR::
code.L(*z);
code.jp(nan);
if constexpr (is_max) {
if (is_max) {
code.andps(op2, op1);
} else {
code.orps(op2, op1);
@@ -629,12 +611,12 @@ void EmitX64::EmitFPMul64(EmitContext& ctx, IR::Inst* inst) {
template<size_t fsize, bool negate_product>
static void EmitFPMulAdd(BlockOfCode& code, EmitContext& ctx, IR::Inst* inst) {
using FPT = mcl::unsigned_integer_of_size<fsize>;
using FPT = Common::UnsignedIntegerN<fsize>;
const auto fallback_fn = negate_product ? &FP::FPMulSub<FPT> : &FP::FPMulAdd<FPT>;
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
if constexpr (fsize != 16) {
if (fsize != 16) {
const bool needs_rounding_correction = ctx.FPCR().FZ();
const bool needs_nan_correction = !ctx.FPCR().DN();
@@ -643,13 +625,13 @@ static void EmitFPMulAdd(BlockOfCode& code, EmitContext& ctx, IR::Inst* inst) {
const Xbyak::Xmm operand2 = ctx.reg_alloc.UseXmm(args[1]);
const Xbyak::Xmm operand3 = ctx.reg_alloc.UseXmm(args[2]);
if constexpr (negate_product) {
if (negate_product) {
FCODE(vfnmadd231s)(result, operand2, operand3);
} else {
FCODE(vfmadd231s)(result, operand2, operand3);
}
if (ctx.FPCR().DN()) {
ForceToDefaultNaN<fsize>(code, result);
ForceToDefaultNaN(code, result, fsize);
}
ctx.reg_alloc.DefineValue(inst, result);
@@ -665,7 +647,7 @@ static void EmitFPMulAdd(BlockOfCode& code, EmitContext& ctx, IR::Inst* inst) {
const Xbyak::Xmm result = ctx.reg_alloc.ScratchXmm();
code.movaps(result, operand1);
if constexpr (negate_product) {
if (negate_product) {
FCODE(vfnmadd231s)(result, operand2, operand3);
} else {
FCODE(vfmadd231s)(result, operand2, operand3);
@@ -686,9 +668,8 @@ static void EmitFPMulAdd(BlockOfCode& code, EmitContext& ctx, IR::Inst* inst) {
} else {
UNREACHABLE();
}
if (ctx.FPCR().DN()) {
ForceToDefaultNaN<fsize>(code, result);
}
if (ctx.FPCR().DN())
ForceToDefaultNaN(code, result, fsize);
code.L(*end);
ctx.deferred_emits.emplace_back([=, &code, &ctx] {
@@ -769,7 +750,7 @@ static void EmitFPMulAdd(BlockOfCode& code, EmitContext& ctx, IR::Inst* inst) {
code.ptest(operand2, xmm0);
code.jnz(op2_done);
code.vorps(result, operand2, xmm0);
if constexpr (negate_product) {
if (negate_product) {
code.xorps(result, code.Const(xword, FP::FPInfo<FPT>::sign_mask));
}
code.jmp(*end);
@@ -785,7 +766,7 @@ static void EmitFPMulAdd(BlockOfCode& code, EmitContext& ctx, IR::Inst* inst) {
// at this point, all SNaNs have been handled
// if op1 was not a QNaN and op2 is, negate the result
if constexpr (negate_product) {
if (negate_product) {
FCODE(ucomis)(operand1, operand1);
code.jp(*end);
FCODE(ucomis)(operand2, operand2);
@@ -806,7 +787,7 @@ static void EmitFPMulAdd(BlockOfCode& code, EmitContext& ctx, IR::Inst* inst) {
const Xbyak::Xmm operand2 = ctx.reg_alloc.UseScratchXmm(args[1]);
const Xbyak::Xmm operand3 = ctx.reg_alloc.UseXmm(args[2]);
if constexpr (negate_product) {
if (negate_product) {
code.xorps(operand2, code.Const(xword, FP::FPInfo<FPT>::sign_mask));
}
FCODE(muls)(operand2, operand3);
@@ -857,7 +838,7 @@ void EmitX64::EmitFPMulSub64(EmitContext& ctx, IR::Inst* inst) {
template<size_t fsize>
static void EmitFPMulX(BlockOfCode& code, EmitContext& ctx, IR::Inst* inst) {
using FPT = mcl::unsigned_integer_of_size<fsize>;
using FPT = Common::UnsignedIntegerN<fsize>;
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
@@ -917,9 +898,9 @@ void EmitX64::EmitFPMulX64(EmitContext& ctx, IR::Inst* inst) {
template<size_t fsize>
static void EmitFPRecipEstimate(BlockOfCode& code, EmitContext& ctx, IR::Inst* inst) {
using FPT = mcl::unsigned_integer_of_size<fsize>;
using FPT = Common::UnsignedIntegerN<fsize>;
if constexpr (fsize != 16) {
if (fsize != 16) {
if (ctx.HasOptimization(OptimizationFlag::Unsafe_ReducedErrorFP)) {
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
const Xbyak::Xmm operand = ctx.reg_alloc.UseXmm(args[0]);
@@ -928,7 +909,7 @@ static void EmitFPRecipEstimate(BlockOfCode& code, EmitContext& ctx, IR::Inst* i
if (code.HasHostFeature(HostFeature::AVX512_OrthoFloat)) {
FCODE(vrcp14s)(result, operand, operand);
} else {
if constexpr (fsize == 32) {
if (fsize == 32) {
code.rcpss(result, operand);
} else {
code.cvtsd2ss(result, operand);
@@ -963,7 +944,7 @@ void EmitX64::EmitFPRecipEstimate64(EmitContext& ctx, IR::Inst* inst) {
template<size_t fsize>
static void EmitFPRecipExponent(BlockOfCode& code, EmitContext& ctx, IR::Inst* inst) {
using FPT = mcl::unsigned_integer_of_size<fsize>;
using FPT = Common::UnsignedIntegerN<fsize>;
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
ctx.reg_alloc.HostCall(inst, args[0]);
@@ -986,11 +967,11 @@ void EmitX64::EmitFPRecipExponent64(EmitContext& ctx, IR::Inst* inst) {
template<size_t fsize>
static void EmitFPRecipStepFused(BlockOfCode& code, EmitContext& ctx, IR::Inst* inst) {
using FPT = mcl::unsigned_integer_of_size<fsize>;
using FPT = Common::UnsignedIntegerN<fsize>;
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
if constexpr (fsize != 16) {
if (fsize != 16) {
if (code.HasHostFeature(HostFeature::FMA) && ctx.HasOptimization(OptimizationFlag::Unsafe_InaccurateNaN)) {
Xbyak::Label end, fallback;
@@ -1123,9 +1104,9 @@ void EmitX64::EmitFPRoundInt64(EmitContext& ctx, IR::Inst* inst) {
template<size_t fsize>
static void EmitFPRSqrtEstimate(BlockOfCode& code, EmitContext& ctx, IR::Inst* inst) {
using FPT = mcl::unsigned_integer_of_size<fsize>;
using FPT = Common::UnsignedIntegerN<fsize>;
if constexpr (fsize != 16) {
if (fsize != 16) {
if (ctx.HasOptimization(OptimizationFlag::Unsafe_ReducedErrorFP)) {
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
const Xbyak::Xmm operand = ctx.reg_alloc.UseXmm(args[0]);
@@ -1134,7 +1115,7 @@ static void EmitFPRSqrtEstimate(BlockOfCode& code, EmitContext& ctx, IR::Inst* i
if (code.HasHostFeature(HostFeature::AVX512_OrthoFloat)) {
FCODE(vrsqrt14s)(result, operand, operand);
} else {
if constexpr (fsize == 32) {
if (fsize == 32) {
code.rsqrtss(result, operand);
} else {
code.cvtsd2ss(result, operand);
@@ -1180,7 +1161,7 @@ static void EmitFPRSqrtEstimate(BlockOfCode& code, EmitContext& ctx, IR::Inst* i
bool needs_fallback = false;
code.L(*bad_values);
if constexpr (fsize == 32) {
if (fsize == 32) {
code.movd(tmp, operand);
if (!ctx.FPCR().FZ()) {
@@ -1302,11 +1283,11 @@ void EmitX64::EmitFPRSqrtEstimate64(EmitContext& ctx, IR::Inst* inst) {
template<size_t fsize>
static void EmitFPRSqrtStepFused(BlockOfCode& code, EmitContext& ctx, IR::Inst* inst) {
using FPT = mcl::unsigned_integer_of_size<fsize>;
using FPT = Common::UnsignedIntegerN<fsize>;
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
if constexpr (fsize != 16) {
if (fsize != 16) {
if (code.HasHostFeature(HostFeature::FMA | HostFeature::AVX) && ctx.HasOptimization(OptimizationFlag::Unsafe_InaccurateNaN)) {
const Xbyak::Xmm operand1 = ctx.reg_alloc.UseXmm(args[0]);
const Xbyak::Xmm operand2 = ctx.reg_alloc.UseXmm(args[1]);
@@ -1485,9 +1466,8 @@ void EmitX64::EmitFPHalfToDouble(EmitContext& ctx, IR::Inst* inst) {
// Double-conversion here is acceptable as this is expanding precision.
code.vcvtph2ps(result, value);
code.vcvtps2pd(result, result);
if (ctx.FPCR().DN()) {
ForceToDefaultNaN<64>(code, result);
}
if (ctx.FPCR().DN())
ForceToDefaultNaN(code, result, 64);
ctx.reg_alloc.DefineValue(inst, result);
return;
@@ -1509,9 +1489,8 @@ void EmitX64::EmitFPHalfToSingle(EmitContext& ctx, IR::Inst* inst) {
const Xbyak::Xmm value = ctx.reg_alloc.UseXmm(args[0]);
code.vcvtph2ps(result, value);
if (ctx.FPCR().DN()) {
ForceToDefaultNaN<32>(code, result);
}
if (ctx.FPCR().DN())
ForceToDefaultNaN(code, result, 32);
ctx.reg_alloc.DefineValue(inst, result);
return;
@@ -1519,23 +1498,22 @@ void EmitX64::EmitFPHalfToSingle(EmitContext& ctx, IR::Inst* inst) {
ctx.reg_alloc.HostCall(inst, args[0]);
code.mov(code.ABI_PARAM2.cvt32(), ctx.FPCR().Value());
code.mov(code.ABI_PARAM3.cvt32(), static_cast<u32>(rounding_mode));
code.mov(code.ABI_PARAM3.cvt32(), u32(rounding_mode));
code.lea(code.ABI_PARAM4, code.ptr[code.ABI_JIT_PTR + code.GetJitStateInfo().offsetof_fpsr_exc]);
code.CallFunction(&FP::FPConvert<u32, u16>);
}
void EmitX64::EmitFPSingleToDouble(EmitContext& ctx, IR::Inst* inst) {
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
const auto rounding_mode = static_cast<FP::RoundingMode>(args[1].GetImmediateU8());
const auto rounding_mode = FP::RoundingMode(args[1].GetImmediateU8());
// We special-case the non-IEEE-defined ToOdd rounding mode.
if (rounding_mode == ctx.FPCR().RMode() && rounding_mode != FP::RoundingMode::ToOdd) {
const Xbyak::Xmm result = ctx.reg_alloc.UseScratchXmm(args[0]);
code.cvtss2sd(result, result);
if (ctx.FPCR().DN()) {
ForceToDefaultNaN<64>(code, result);
}
if (ctx.FPCR().DN())
ForceToDefaultNaN(code, result, 64);
ctx.reg_alloc.DefineValue(inst, result);
} else {
ctx.reg_alloc.HostCall(inst, args[0]);
@@ -1553,12 +1531,9 @@ void EmitX64::EmitFPSingleToHalf(EmitContext& ctx, IR::Inst* inst) {
if (code.HasHostFeature(HostFeature::F16C) && !ctx.FPCR().AHP() && !ctx.FPCR().FZ16()) {
const Xbyak::Xmm result = ctx.reg_alloc.UseScratchXmm(args[0]);
if (ctx.FPCR().DN()) {
ForceToDefaultNaN<32>(code, result);
}
code.vcvtps2ph(result, result, static_cast<u8>(*round_imm));
if (ctx.FPCR().DN())
ForceToDefaultNaN(code, result, 32);
code.vcvtps2ph(result, result, u8(*round_imm));
ctx.reg_alloc.DefineValue(inst, result);
return;
}
@@ -1586,21 +1561,18 @@ void EmitX64::EmitFPDoubleToHalf(EmitContext& ctx, IR::Inst* inst) {
void EmitX64::EmitFPDoubleToSingle(EmitContext& ctx, IR::Inst* inst) {
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
const auto rounding_mode = static_cast<FP::RoundingMode>(args[1].GetImmediateU8());
const auto rounding_mode = FP::RoundingMode(args[1].GetImmediateU8());
// We special-case the non-IEEE-defined ToOdd rounding mode.
if (rounding_mode == ctx.FPCR().RMode() && rounding_mode != FP::RoundingMode::ToOdd) {
const Xbyak::Xmm result = ctx.reg_alloc.UseScratchXmm(args[0]);
code.cvtsd2ss(result, result);
if (ctx.FPCR().DN()) {
ForceToDefaultNaN<32>(code, result);
}
if (ctx.FPCR().DN())
ForceToDefaultNaN(code, result, 32);
ctx.reg_alloc.DefineValue(inst, result);
} else {
ctx.reg_alloc.HostCall(inst, args[0]);
code.mov(code.ABI_PARAM2.cvt32(), ctx.FPCR().Value());
code.mov(code.ABI_PARAM3.cvt32(), static_cast<u32>(rounding_mode));
code.mov(code.ABI_PARAM3.cvt32(), u32(rounding_mode));
code.lea(code.ABI_PARAM4, code.ptr[code.ABI_JIT_PTR + code.GetJitStateInfo().offsetof_fpsr_exc]);
code.CallFunction(&FP::FPConvert<u32, u64>);
}
@@ -1615,7 +1587,7 @@ void EmitX64::EmitFPDoubleToSingle(EmitContext& ctx, IR::Inst* inst) {
/// Better than spamming thousands of templates aye?
template<size_t fsize>
static u64 EmitFPToFixedThunk(u64 input, FP::FPSR& fpsr, FP::FPCR fpcr, u32 extra_args) {
using FPT = mcl::unsigned_integer_of_size<fsize>;
using FPT = Common::UnsignedIntegerN<fsize>;
auto const unsigned_ = ((extra_args >> 24) & 0xff) != 0;
auto const isize = ((extra_args >> 16) & 0xff);
auto const rounding = FP::RoundingMode((extra_args >> 8) & 0xff);
@@ -1630,7 +1602,7 @@ static void EmitFPToFixed(BlockOfCode& code, EmitContext& ctx, IR::Inst* inst) {
const size_t fbits = args[1].GetImmediateU8();
const auto rounding_mode = FP::RoundingMode(args[2].GetImmediateU8());
if constexpr (fsize != 16) {
if (fsize != 16) {
const auto round_imm = ConvertRoundingModeToX64Immediate(rounding_mode);
// cvttsd2si truncates during operation so rounding (and thus SSE4.1) not required
@@ -1640,7 +1612,7 @@ static void EmitFPToFixed(BlockOfCode& code, EmitContext& ctx, IR::Inst* inst) {
const Xbyak::Xmm src = ctx.reg_alloc.UseScratchXmm(args[0]);
const Xbyak::Reg64 result = ctx.reg_alloc.ScratchGpr().cvt64();
if constexpr (fsize == 64) {
if (fsize == 64) {
if (fbits != 0) {
const u64 scale_factor = static_cast<u64>((fbits + 1023) << 52);
code.mulsd(src, code.Const(xword, scale_factor));
@@ -1662,13 +1634,13 @@ static void EmitFPToFixed(BlockOfCode& code, EmitContext& ctx, IR::Inst* inst) {
code.cvtss2sd(src, src);
}
if constexpr (isize == 64) {
if (isize == 64) {
const Xbyak::Xmm scratch = ctx.reg_alloc.ScratchXmm();
if (!unsigned_) {
SharedLabel saturate_max = GenSharedLabel(), end = GenSharedLabel();
ZeroIfNaN<64>(code, src, scratch);
ZeroIfNaN(code, src, scratch, 64);
code.movsd(scratch, code.Const(xword, f64_max_s64_lim));
code.comisd(scratch, src);
@@ -1706,11 +1678,11 @@ static void EmitFPToFixed(BlockOfCode& code, EmitContext& ctx, IR::Inst* inst) {
code.sar(result2, 63);
code.or_(result, result2);
}
} else if constexpr (isize == 32) {
} else if (isize == 32) {
if (!unsigned_) {
const Xbyak::Xmm scratch = ctx.reg_alloc.ScratchXmm();
ZeroIfNaN<64>(code, src, scratch);
ZeroIfNaN(code, src, scratch, 64);
code.minsd(src, code.Const(xword, f64_max_s32));
// maxsd not required as cvttsd2si results in 0x8000'0000 when out of range
code.cvttsd2si(result.cvt32(), src); // 32 bit gpr
@@ -1723,7 +1695,7 @@ static void EmitFPToFixed(BlockOfCode& code, EmitContext& ctx, IR::Inst* inst) {
} else {
const Xbyak::Xmm scratch = ctx.reg_alloc.ScratchXmm();
ZeroIfNaN<64>(code, src, scratch);
ZeroIfNaN(code, src, scratch, 64);
code.maxsd(src, code.Const(xword, unsigned_ ? f64_min_u16 : f64_min_s16));
code.minsd(src, code.Const(xword, unsigned_ ? f64_max_u16 : f64_max_s16));
code.cvttsd2si(result, src); // 64 bit gpr

View File

@@ -4,6 +4,7 @@
*/
#include <mcl/macro/concatenate_tokens.hpp>
#include "dynarmic/common/type_util.h"
#define AxxEmitX64 CONCATENATE_TOKENS(Axx, EmitX64)
#define AxxEmitContext CONCATENATE_TOKENS(Axx, EmitContext)
@@ -15,14 +16,11 @@ using Vector = std::array<u64, 2>;
}
std::optional<AxxEmitX64::DoNotFastmemMarker> AxxEmitX64::ShouldFastmem(AxxEmitContext& ctx, IR::Inst* inst) const {
if (!conf.fastmem_pointer || !exception_handler.SupportsFastmem()) {
if (!conf.fastmem_pointer || !exception_handler.SupportsFastmem())
return std::nullopt;
}
const auto marker = std::make_tuple(ctx.Location(), inst->GetName());
if (do_not_fastmem.count(marker) > 0) {
if (do_not_fastmem.count(marker) > 0)
return std::nullopt;
}
return marker;
}
@@ -58,16 +56,12 @@ void AxxEmitX64::EmitMemoryRead(AxxEmitContext& ctx, IR::Inst* inst) {
// Neither fastmem nor page table: Use callbacks
if constexpr (bitsize == 128) {
ctx.reg_alloc.HostCall(nullptr, {}, args[1]);
if (ordered) {
code.mfence();
}
if (ordered) code.mfence();
code.CallFunction(memory_read_128);
ctx.reg_alloc.DefineValue(inst, xmm1);
} else {
ctx.reg_alloc.HostCall(inst, {}, args[1]);
if (ordered) {
code.mfence();
}
if (ordered) code.mfence();
Devirtualize<callback>(conf.callbacks).EmitCall(code);
code.ZeroExtendFrom(bitsize, code.ABI_RETURN);
}
@@ -102,10 +96,10 @@ void AxxEmitX64::EmitMemoryRead(AxxEmitContext& ctx, IR::Inst* inst) {
code.call(wrapped_fn);
fastmem_patch_info.emplace(
mcl::bit_cast<u64>(location),
std::bit_cast<u64>(location),
FastmemPatchInfo{
mcl::bit_cast<u64>(code.getCurr()),
mcl::bit_cast<u64>(wrapped_fn),
std::bit_cast<u64>(code.getCurr()),
std::bit_cast<u64>(wrapped_fn),
*fastmem_marker,
conf.recompile_on_fastmem_failure,
});
@@ -153,9 +147,7 @@ void AxxEmitX64::EmitMemoryWrite(AxxEmitContext& ctx, IR::Inst* inst) {
ctx.reg_alloc.HostCall(nullptr, {}, args[1], args[2]);
Devirtualize<callback>(conf.callbacks).EmitCall(code);
}
if (ordered) {
code.mfence();
}
if (ordered) code.mfence();
EmitCheckMemoryAbort(ctx, inst);
return;
}
@@ -189,10 +181,10 @@ void AxxEmitX64::EmitMemoryWrite(AxxEmitContext& ctx, IR::Inst* inst) {
code.call(wrapped_fn);
fastmem_patch_info.emplace(
mcl::bit_cast<u64>(location),
std::bit_cast<u64>(location),
FastmemPatchInfo{
mcl::bit_cast<u64>(code.getCurr()),
mcl::bit_cast<u64>(wrapped_fn),
std::bit_cast<u64>(code.getCurr()),
std::bit_cast<u64>(wrapped_fn),
*fastmem_marker,
conf.recompile_on_fastmem_failure,
});
@@ -223,7 +215,7 @@ void AxxEmitX64::EmitExclusiveReadMemory(AxxEmitContext& ctx, IR::Inst* inst) {
const bool ordered = IsOrdered(args[2].GetImmediateAccType());
if constexpr (bitsize != 128) {
using T = mcl::unsigned_integer_of_size<bitsize>;
using T = Common::UnsignedIntegerN<bitsize>;
ctx.reg_alloc.HostCall(inst, {}, args[1]);
@@ -290,16 +282,14 @@ void AxxEmitX64::EmitExclusiveWriteMemory(AxxEmitContext& ctx, IR::Inst* inst) {
code.mov(code.byte[code.ABI_JIT_PTR + offsetof(AxxJitState, exclusive_state)], u8(0));
code.mov(code.ABI_PARAM1, reinterpret_cast<u64>(&conf));
if constexpr (bitsize != 128) {
using T = mcl::unsigned_integer_of_size<bitsize>;
using T = Common::UnsignedIntegerN<bitsize>;
code.CallLambda(
[](AxxUserConfig& conf, Axx::VAddr vaddr, T value) -> u32 {
return conf.global_monitor->DoExclusiveOperation<T>(conf.processor_id, vaddr,
[&](T expected) -> bool {
return (conf.callbacks->*callback)(vaddr, value, expected);
})
? 0
: 1;
[&](T expected) -> bool {
return (conf.callbacks->*callback)(vaddr, value, expected);
}) ? 0 : 1;
});
if (ordered) {
code.mfence();
@@ -311,11 +301,9 @@ void AxxEmitX64::EmitExclusiveWriteMemory(AxxEmitContext& ctx, IR::Inst* inst) {
code.CallLambda(
[](AxxUserConfig& conf, Axx::VAddr vaddr, Vector& value) -> u32 {
return conf.global_monitor->DoExclusiveOperation<Vector>(conf.processor_id, vaddr,
[&](Vector expected) -> bool {
return (conf.callbacks->*callback)(vaddr, value, expected);
})
? 0
: 1;
[&](Vector expected) -> bool {
return (conf.callbacks->*callback)(vaddr, value, expected);
}) ? 0 : 1;
});
if (ordered) {
code.mfence();
@@ -356,7 +344,7 @@ void AxxEmitX64::EmitExclusiveReadMemoryInline(AxxEmitContext& ctx, IR::Inst* in
EmitExclusiveLock(code, conf, tmp, tmp2.cvt32());
code.mov(code.byte[code.ABI_JIT_PTR + offsetof(AxxJitState, exclusive_state)], u8(1));
code.mov(tmp, mcl::bit_cast<u64>(GetExclusiveMonitorAddressPointer(conf.global_monitor, conf.processor_id)));
code.mov(tmp, std::bit_cast<u64>(GetExclusiveMonitorAddressPointer(conf.global_monitor, conf.processor_id)));
code.mov(qword[tmp], vaddr);
const auto fastmem_marker = ShouldFastmem(ctx, inst);
@@ -369,10 +357,10 @@ void AxxEmitX64::EmitExclusiveReadMemoryInline(AxxEmitContext& ctx, IR::Inst* in
const auto location = EmitReadMemoryMov<bitsize>(code, value_idx, src_ptr, ordered);
fastmem_patch_info.emplace(
mcl::bit_cast<u64>(location),
std::bit_cast<u64>(location),
FastmemPatchInfo{
mcl::bit_cast<u64>(code.getCurr()),
mcl::bit_cast<u64>(wrapped_fn),
std::bit_cast<u64>(code.getCurr()),
std::bit_cast<u64>(wrapped_fn),
*fastmem_marker,
conf.recompile_on_exclusive_fastmem_failure,
});
@@ -390,7 +378,7 @@ void AxxEmitX64::EmitExclusiveReadMemoryInline(AxxEmitContext& ctx, IR::Inst* in
code.call(wrapped_fn);
}
code.mov(tmp, mcl::bit_cast<u64>(GetExclusiveMonitorValuePointer(conf.global_monitor, conf.processor_id)));
code.mov(tmp, std::bit_cast<u64>(GetExclusiveMonitorValuePointer(conf.global_monitor, conf.processor_id)));
EmitWriteMemoryMov<bitsize>(code, tmp, value_idx, false);
EmitExclusiveUnlock(code, conf, tmp, tmp2.cvt32());
@@ -437,7 +425,7 @@ void AxxEmitX64::EmitExclusiveWriteMemoryInline(AxxEmitContext& ctx, IR::Inst* i
SharedLabel end = GenSharedLabel();
code.mov(tmp, mcl::bit_cast<u64>(GetExclusiveMonitorAddressPointer(conf.global_monitor, conf.processor_id)));
code.mov(tmp, std::bit_cast<u64>(GetExclusiveMonitorAddressPointer(conf.global_monitor, conf.processor_id)));
code.mov(status, u32(1));
code.cmp(code.byte[code.ABI_JIT_PTR + offsetof(AxxJitState, exclusive_state)], u8(0));
code.je(*end, code.T_NEAR);
@@ -447,7 +435,7 @@ void AxxEmitX64::EmitExclusiveWriteMemoryInline(AxxEmitContext& ctx, IR::Inst* i
EmitExclusiveTestAndClear(code, conf, vaddr, tmp, rax);
code.mov(code.byte[code.ABI_JIT_PTR + offsetof(AxxJitState, exclusive_state)], u8(0));
code.mov(tmp, mcl::bit_cast<u64>(GetExclusiveMonitorValuePointer(conf.global_monitor, conf.processor_id)));
code.mov(tmp, std::bit_cast<u64>(GetExclusiveMonitorValuePointer(conf.global_monitor, conf.processor_id)));
if constexpr (bitsize == 128) {
code.mov(rax, qword[tmp + 0]);
@@ -475,25 +463,20 @@ void AxxEmitX64::EmitExclusiveWriteMemoryInline(AxxEmitContext& ctx, IR::Inst* i
const auto location = code.getCurr();
if constexpr (bitsize == 128) {
code.lock();
code.cmpxchg16b(ptr[dest_ptr]);
code.lock(); code.cmpxchg16b(ptr[dest_ptr]);
} else {
switch (bitsize) {
case 8:
code.lock();
code.cmpxchg(code.byte[dest_ptr], value.cvt8());
code.lock(); code.cmpxchg(code.byte[dest_ptr], value.cvt8());
break;
case 16:
code.lock();
code.cmpxchg(word[dest_ptr], value.cvt16());
code.lock(); code.cmpxchg(word[dest_ptr], value.cvt16());
break;
case 32:
code.lock();
code.cmpxchg(dword[dest_ptr], value.cvt32());
code.lock(); code.cmpxchg(dword[dest_ptr], value.cvt32());
break;
case 64:
code.lock();
code.cmpxchg(qword[dest_ptr], value.cvt64());
code.lock(); code.cmpxchg(qword[dest_ptr], value.cvt64());
break;
default:
UNREACHABLE();
@@ -506,10 +489,10 @@ void AxxEmitX64::EmitExclusiveWriteMemoryInline(AxxEmitContext& ctx, IR::Inst* i
code.call(wrapped_fn);
fastmem_patch_info.emplace(
mcl::bit_cast<u64>(location),
std::bit_cast<u64>(location),
FastmemPatchInfo{
mcl::bit_cast<u64>(code.getCurr()),
mcl::bit_cast<u64>(wrapped_fn),
std::bit_cast<u64>(code.getCurr()),
std::bit_cast<u64>(wrapped_fn),
*fastmem_marker,
conf.recompile_on_exclusive_fastmem_failure,
});

View File

@@ -1,9 +1,13 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
/* This file is part of the dynarmic project.
* Copyright (c) 2022 MerryMage
* SPDX-License-Identifier: 0BSD
*/
#include <mcl/bit_cast.hpp>
#include <numeric>
#include <bit>
#include <xbyak/xbyak.h>
#include "dynarmic/backend/x64/a32_emit_x64.h"
@@ -342,7 +346,7 @@ void EmitExclusiveLock(BlockOfCode& code, const UserConfig& conf, Xbyak::Reg64 p
return;
}
code.mov(pointer, mcl::bit_cast<u64>(GetExclusiveMonitorLockPointer(conf.global_monitor)));
code.mov(pointer, std::bit_cast<u64>(GetExclusiveMonitorLockPointer(conf.global_monitor)));
EmitSpinLockLock(code, pointer, tmp);
}
@@ -352,7 +356,7 @@ void EmitExclusiveUnlock(BlockOfCode& code, const UserConfig& conf, Xbyak::Reg64
return;
}
code.mov(pointer, mcl::bit_cast<u64>(GetExclusiveMonitorLockPointer(conf.global_monitor)));
code.mov(pointer, std::bit_cast<u64>(GetExclusiveMonitorLockPointer(conf.global_monitor)));
EmitSpinLockUnlock(code, pointer, tmp);
}
@@ -369,7 +373,7 @@ void EmitExclusiveTestAndClear(BlockOfCode& code, const UserConfig& conf, Xbyak:
continue;
}
Xbyak::Label ok;
code.mov(pointer, mcl::bit_cast<u64>(GetExclusiveMonitorAddressPointer(conf.global_monitor, processor_index)));
code.mov(pointer, std::bit_cast<u64>(GetExclusiveMonitorAddressPointer(conf.global_monitor, processor_index)));
code.cmp(qword[pointer], vaddr);
code.jne(ok, code.T_NEAR);
code.mov(qword[pointer], tmp);

View File

@@ -11,13 +11,14 @@
#include "dynarmic/common/assert.h"
#include <mcl/bit/bit_field.hpp>
#include "dynarmic/common/common_types.h"
#include <mcl/type_traits/integer_of_size.hpp>
#include "dynarmic/backend/x64/block_of_code.h"
#include "dynarmic/backend/x64/emit_x64.h"
#include "dynarmic/ir/basic_block.h"
#include "dynarmic/ir/microinstruction.h"
#include "dynarmic/ir/opcodes.h"
#include "dynarmic/common/fp/util.h"
#include "dynarmic/common/type_util.h"
namespace Dynarmic::Backend::X64 {
@@ -38,7 +39,7 @@ void EmitSignedSaturatedOp(BlockOfCode& code, EmitContext& ctx, IR::Inst* inst)
Xbyak::Reg addend = ctx.reg_alloc.UseGpr(args[1]).changeBit(size);
Xbyak::Reg overflow = ctx.reg_alloc.ScratchGpr().changeBit(size);
constexpr u64 int_max = static_cast<u64>((std::numeric_limits<mcl::signed_integer_of_size<size>>::max)());
constexpr u64 int_max = static_cast<u64>((std::numeric_limits<Common::SignedIntegerN<size>>::max)());
if constexpr (size < 64) {
code.xor_(overflow.cvt32(), overflow.cvt32());
code.bt(result.cvt32(), size - 1);
@@ -82,7 +83,7 @@ void EmitUnsignedSaturatedOp(BlockOfCode& code, EmitContext& ctx, IR::Inst* inst
Xbyak::Reg op_result = ctx.reg_alloc.UseScratchGpr(args[0]).changeBit(size);
Xbyak::Reg addend = ctx.reg_alloc.UseScratchGpr(args[1]).changeBit(size);
constexpr u64 boundary = op == Op::Add ? (std::numeric_limits<mcl::unsigned_integer_of_size<size>>::max)() : 0;
constexpr u64 boundary = op == Op::Add ? (std::numeric_limits<Common::UnsignedIntegerN<size>>::max)() : 0;
if constexpr (op == Op::Add) {
code.add(op_result, addend);

View File

@@ -20,7 +20,6 @@
#include <mcl/mp/typelist/list.hpp>
#include <mcl/mp/typelist/lower_to_tuple.hpp>
#include <mcl/type_traits/function_info.hpp>
#include <mcl/type_traits/integer_of_size.hpp>
#include <xbyak/xbyak.h>
#include "dynarmic/backend/x64/abi.h"
@@ -31,27 +30,14 @@
#include "dynarmic/common/fp/info.h"
#include "dynarmic/common/fp/op.h"
#include "dynarmic/common/fp/util.h"
#include "dynarmic/common/type_util.h"
#include "dynarmic/common/lut_from_list.h"
#include "dynarmic/interface/optimization_flags.h"
#include "dynarmic/ir/basic_block.h"
#include "dynarmic/ir/microinstruction.h"
#define FCODE(NAME) \
[&code](auto... args) { \
if constexpr (fsize == 32) { \
code.NAME##s(args...); \
} else { \
code.NAME##d(args...); \
} \
}
#define ICODE(NAME) \
[&code](auto... args) { \
if constexpr (fsize == 32) { \
code.NAME##d(args...); \
} else { \
code.NAME##q(args...); \
} \
}
#define FCODE(NAME) [&](auto... args) { if (fsize == 32) code.NAME##s(args...); else code.NAME##d(args...); }
#define ICODE(NAME) [&](auto... args) { if (fsize == 32) code.NAME##d(args...); else code.NAME##q(args...); }
namespace Dynarmic::Backend::X64 {
@@ -76,7 +62,7 @@ void MaybeStandardFPSCRValue(BlockOfCode& code, EmitContext& ctx, bool fpcr_cont
template<size_t fsize, template<typename> class Indexer, size_t narg>
struct NaNHandler {
public:
using FPT = mcl::unsigned_integer_of_size<fsize>;
using FPT = Common::UnsignedIntegerN<fsize>;
using function_type = void (*)(std::array<VectorArray<FPT>, narg>&, FP::FPCR);
@@ -158,33 +144,33 @@ Xbyak::Address GetVectorOf(BlockOfCode& code) {
template<size_t fsize>
Xbyak::Address GetNaNVector(BlockOfCode& code) {
using FPT = mcl::unsigned_integer_of_size<fsize>;
using FPT = Common::UnsignedIntegerN<fsize>;
return GetVectorOf<fsize, FP::FPInfo<FPT>::DefaultNaN()>(code);
}
template<size_t fsize>
Xbyak::Address GetNegativeZeroVector(BlockOfCode& code) {
using FPT = mcl::unsigned_integer_of_size<fsize>;
using FPT = Common::UnsignedIntegerN<fsize>;
return GetVectorOf<fsize, FP::FPInfo<FPT>::Zero(true)>(code);
}
template<size_t fsize>
Xbyak::Address GetNonSignMaskVector(BlockOfCode& code) {
using FPT = mcl::unsigned_integer_of_size<fsize>;
using FPT = Common::UnsignedIntegerN<fsize>;
constexpr FPT non_sign_mask = FP::FPInfo<FPT>::exponent_mask | FP::FPInfo<FPT>::mantissa_mask;
return GetVectorOf<fsize, non_sign_mask>(code);
}
template<size_t fsize>
Xbyak::Address GetSmallestNormalVector(BlockOfCode& code) {
using FPT = mcl::unsigned_integer_of_size<fsize>;
using FPT = Common::UnsignedIntegerN<fsize>;
constexpr FPT smallest_normal_number = FP::FPValue<FPT, false, FP::FPInfo<FPT>::exponent_min, 1>();
return GetVectorOf<fsize, smallest_normal_number>(code);
}
template<size_t fsize, bool sign, int exponent, mcl::unsigned_integer_of_size<fsize> value>
template<size_t fsize, bool sign, int exponent, Common::UnsignedIntegerN<fsize> value>
Xbyak::Address GetVectorOf(BlockOfCode& code) {
using FPT = mcl::unsigned_integer_of_size<fsize>;
using FPT = Common::UnsignedIntegerN<fsize>;
return GetVectorOf<fsize, FP::FPValue<FPT, sign, exponent, value>()>(code);
}
@@ -1085,7 +1071,7 @@ static void EmitFPVectorMinMaxNumeric(BlockOfCode& code, EmitContext& ctx, IR::I
if (code.HasHostFeature(HostFeature::AVX)) {
MaybeStandardFPSCRValue(code, ctx, fpcr_controlled, [&] {
using FPT = mcl::unsigned_integer_of_size<fsize>;
using FPT = Common::UnsignedIntegerN<fsize>;
// result = xmm_a == SNaN || xmm_b == QNaN
{
@@ -1158,7 +1144,7 @@ static void EmitFPVectorMinMaxNumeric(BlockOfCode& code, EmitContext& ctx, IR::I
}
MaybeStandardFPSCRValue(code, ctx, fpcr_controlled, [&] {
using FPT = mcl::unsigned_integer_of_size<fsize>;
using FPT = Common::UnsignedIntegerN<fsize>;
// result = xmm_a == SNaN || xmm_b == QNaN
{
@@ -1314,7 +1300,7 @@ static void EmitFPVectorMulAddFallback(VectorArray<FPT>& result, const VectorArr
template<size_t fsize>
void EmitFPVectorMulAdd(BlockOfCode& code, EmitContext& ctx, IR::Inst* inst) {
using FPT = mcl::unsigned_integer_of_size<fsize>;
using FPT = Common::UnsignedIntegerN<fsize>;
const auto fallback_fn = [](VectorArray<FPT>& result, const VectorArray<FPT>& addend, const VectorArray<FPT>& op1, const VectorArray<FPT>& op2, FP::FPCR fpcr, FP::FPSR& fpsr) {
for (size_t i = 0; i < result.size(); i++) {
@@ -1425,7 +1411,7 @@ void EmitX64::EmitFPVectorMulAdd64(EmitContext& ctx, IR::Inst* inst) {
template<size_t fsize>
static void EmitFPVectorMulX(BlockOfCode& code, EmitContext& ctx, IR::Inst* inst) {
using FPT = mcl::unsigned_integer_of_size<fsize>;
using FPT = Common::UnsignedIntegerN<fsize>;
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
const bool fpcr_controlled = args[2].GetImmediateU1();
@@ -1491,7 +1477,7 @@ void EmitX64::EmitFPVectorMulX64(EmitContext& ctx, IR::Inst* inst) {
template<size_t fsize>
void FPVectorNeg(BlockOfCode& code, EmitContext& ctx, IR::Inst* inst) {
using FPT = mcl::unsigned_integer_of_size<fsize>;
using FPT = Common::UnsignedIntegerN<fsize>;
constexpr FPT sign_mask = FP::FPInfo<FPT>::sign_mask;
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
@@ -1544,7 +1530,7 @@ void EmitX64::EmitFPVectorPairedAddLower64(EmitContext& ctx, IR::Inst* inst) {
template<size_t fsize>
static void EmitRecipEstimate(BlockOfCode& code, EmitContext& ctx, IR::Inst* inst) {
using FPT = mcl::unsigned_integer_of_size<fsize>;
using FPT = Common::UnsignedIntegerN<fsize>;
if constexpr (fsize != 16) {
if (ctx.HasOptimization(OptimizationFlag::Unsafe_ReducedErrorFP)) {
@@ -1590,7 +1576,7 @@ void EmitX64::EmitFPVectorRecipEstimate64(EmitContext& ctx, IR::Inst* inst) {
template<size_t fsize>
static void EmitRecipStepFused(BlockOfCode& code, EmitContext& ctx, IR::Inst* inst) {
using FPT = mcl::unsigned_integer_of_size<fsize>;
using FPT = Common::UnsignedIntegerN<fsize>;
const auto fallback_fn = [](VectorArray<FPT>& result, const VectorArray<FPT>& op1, const VectorArray<FPT>& op2, FP::FPCR fpcr, FP::FPSR& fpsr) {
for (size_t i = 0; i < result.size(); i++) {
@@ -1714,7 +1700,7 @@ void EmitFPVectorRoundInt(BlockOfCode& code, EmitContext& ctx, IR::Inst* inst) {
}
// Do not make a LUT out of this, let the compiler do it's thing
using FPT = mcl::unsigned_integer_of_size<fsize>;
using FPT = Common::UnsignedIntegerN<fsize>;
switch (rounding) {
case FP::RoundingMode::ToNearest_TieEven:
exact
@@ -1760,7 +1746,7 @@ void EmitX64::EmitFPVectorRoundInt64(EmitContext& ctx, IR::Inst* inst) {
template<size_t fsize>
static void EmitRSqrtEstimate(BlockOfCode& code, EmitContext& ctx, IR::Inst* inst) {
using FPT = mcl::unsigned_integer_of_size<fsize>;
using FPT = Common::UnsignedIntegerN<fsize>;
const auto fallback_fn = [](VectorArray<FPT>& result, const VectorArray<FPT>& operand, FP::FPCR fpcr, FP::FPSR& fpsr) {
for (size_t i = 0; i < result.size(); i++) {
@@ -1852,7 +1838,7 @@ void EmitX64::EmitFPVectorRSqrtEstimate64(EmitContext& ctx, IR::Inst* inst) {
template<size_t fsize>
static void EmitRSqrtStepFused(BlockOfCode& code, EmitContext& ctx, IR::Inst* inst) {
using FPT = mcl::unsigned_integer_of_size<fsize>;
using FPT = Common::UnsignedIntegerN<fsize>;
const auto fallback_fn = [](VectorArray<FPT>& result, const VectorArray<FPT>& op1, const VectorArray<FPT>& op2, FP::FPCR fpcr, FP::FPSR& fpsr) {
for (size_t i = 0; i < result.size(); i++) {
@@ -2126,7 +2112,7 @@ void EmitFPVectorToFixed(BlockOfCode& code, EmitContext& ctx, IR::Inst* inst) {
FCODE(orp)(src, exceed_unsigned);
}
} else {
using FPT = mcl::unsigned_integer_of_size<fsize>; // WORKAROUND: For issue 678 on MSVC
using FPT = Common::UnsignedIntegerN<fsize>; // WORKAROUND: For issue 678 on MSVC
constexpr u64 integer_max = FPT((std::numeric_limits<std::conditional_t<unsigned_, FPT, std::make_signed_t<FPT>>>::max)());
code.movaps(xmm0, GetVectorOf<fsize, float_upper_limit_signed>(code));
@@ -2150,7 +2136,7 @@ void EmitFPVectorToFixed(BlockOfCode& code, EmitContext& ctx, IR::Inst* inst) {
mp::lift_value<FP::RoundingMode::ToNearest_TieAwayFromZero>>;
static const auto lut = Common::GenerateLookupTableFromList([]<typename I>(I) {
using FPT = mcl::unsigned_integer_of_size<fsize>; // WORKAROUND: For issue 678 on MSVC
using FPT = Common::UnsignedIntegerN<fsize>; // WORKAROUND: For issue 678 on MSVC
return std::pair{
mp::lower_to_tuple_v<I>,
Common::FptrCast([](VectorArray<FPT>& output, const VectorArray<FPT>& input, FP::FPCR fpcr, FP::FPSR& fpsr) {

View File

@@ -14,23 +14,8 @@
#include "dynarmic/ir/microinstruction.h"
#include "dynarmic/ir/opcodes.h"
#define FCODE(NAME) \
[&code](auto... args) { \
if constexpr (esize == 32) { \
code.NAME##s(args...); \
} else { \
code.NAME##d(args...); \
} \
}
#define ICODE(NAME) \
[&code](auto... args) { \
if constexpr (esize == 32) { \
code.NAME##d(args...); \
} else { \
code.NAME##q(args...); \
} \
}
#define FCODE(NAME) [&](auto... args) { if (esize == 32) code.NAME##s(args...); else code.NAME##d(args...); }
#define ICODE(NAME) [&](auto... args) { if (esize == 32) code.NAME##d(args...); else code.NAME##q(args...); }
namespace Dynarmic::Backend::X64 {

View File

@@ -13,7 +13,7 @@
#include <vector>
#include "dynarmic/common/assert.h"
#include <mcl/bit_cast.hpp>
#include <numeric>
#include "dynarmic/common/common_types.h"
#include "dynarmic/backend/exception_handler.h"
@@ -184,20 +184,20 @@ struct ExceptionHandler::Impl final {
// Our 3rd argument is a PCONTEXT.
// If not within our codeblock, ignore this exception.
code.mov(code.rax, Safe::Negate(mcl::bit_cast<u64>(code.getCode())));
code.mov(code.rax, Safe::Negate(std::bit_cast<u64>(code.getCode())));
code.add(code.rax, code.qword[code.ABI_PARAM3 + Xbyak::RegExp(offsetof(CONTEXT, Rip))]);
code.cmp(code.rax, static_cast<u32>(code.GetTotalCodeSize()));
code.ja(exception_handler_without_cb);
code.lea(code.rsp, code.ptr[code.rsp - 8]);
code.mov(code.ABI_PARAM1, mcl::bit_cast<u64>(&cb));
code.mov(code.ABI_PARAM1, std::bit_cast<u64>(&cb));
code.mov(code.ABI_PARAM2, code.ABI_PARAM3);
code.CallLambda(
[](const std::function<FakeCall(u64)>& cb_, PCONTEXT ctx) {
FakeCall fc = cb_(ctx->Rip);
ctx->Rsp -= sizeof(u64);
*mcl::bit_cast<u64*>(ctx->Rsp) = fc.ret_rip;
*std::bit_cast<u64*>(ctx->Rsp) = fc.ret_rip;
ctx->Rip = fc.call_rip;
});
code.add(code.rsp, 8);

View File

@@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
/* This file is part of the dynarmic project.
* Copyright (c) 2018 MerryMage
* SPDX-License-Identifier: 0BSD
@@ -6,8 +9,8 @@
#pragma once
#include <string_view>
#include <mcl/bit_cast.hpp>
#include <bit>
#include <numeric>
namespace Dynarmic::Backend::X64 {
@@ -17,7 +20,7 @@ void PerfMapRegister(const void* start, const void* end, std::string_view friend
template<typename T>
void PerfMapRegister(T start, const void* end, std::string_view friendly_name) {
detail::PerfMapRegister(mcl::bit_cast<const void*>(start), end, friendly_name);
detail::PerfMapRegister(std::bit_cast<const void*>(start), end, friendly_name);
}
void PerfMapClear();

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