Compare commits

...

23 Commits

Author SHA1 Message Date
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
100 changed files with 2316 additions and 11035 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

@@ -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,8 +237,7 @@ 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; });
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; });
}

View File

@@ -240,7 +240,7 @@ private:
/// Ring buffer of the samples waiting to be played or consumed
Common::RingBuffer<s16, 0x10000> samples_buffer;
/// Audio buffers queued and waiting to play
Common::SPSCQueue<SinkBuffer, 0x10000> queue;
Common::SPSCQueue<SinkBuffer, 0x40000> queue;
/// The currently-playing audio buffer
SinkBuffer playing_buffer{};
/// The last played (or received) frame of audio, used when the callback underruns

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
@@ -270,8 +268,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

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

@@ -410,15 +410,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

@@ -135,8 +135,4 @@ target_include_directories(dynarmic_tests PRIVATE . ../src)
target_compile_options(dynarmic_tests PRIVATE ${DYNARMIC_CXX_FLAGS})
target_compile_definitions(dynarmic_tests PRIVATE FMT_USE_USER_DEFINED_LITERALS=1)
if ("x86_64" IN_LIST ARCHITECTURE)
target_compile_options(dynarmic_tests PRIVATE -mavx2)
endif()
add_test(dynarmic_tests dynarmic_tests --durations yes)

View File

@@ -10,10 +10,6 @@
#include "common/scm_rev.h"
#include <fmt/format.h>
#ifndef CPPHTTPLIB_OPENSSL_SUPPORT
#define CPPHTTPLIB_OPENSSL_SUPPORT
#endif
#include <httplib.h>
#ifdef YUZU_BUNDLED_OPENSSL

View File

@@ -134,7 +134,6 @@ add_library(hid_core STATIC
hid_result.h
hid_types.h
hid_util.h
precompiled_headers.h
resource_manager.cpp
resource_manager.h
)
@@ -160,7 +159,3 @@ endif()
create_target_directory_groups(hid_core)
target_link_libraries(hid_core PUBLIC core)
if (YUZU_USE_PRECOMPILED_HEADERS)
target_precompile_headers(hid_core PRIVATE precompiled_headers.h)
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

@@ -39,7 +39,7 @@ Result NpadVibration::SetSettingsService(
Result NpadVibration::SetVibrationMasterVolume(f32 master_volume) {
std::scoped_lock lock{mutex};
if (master_volume < 0.0f && master_volume > 1.0f) {
if (master_volume < 0.0f || master_volume > 1.0f) {
return ResultVibrationStrengthOutOfRange;
}

View File

@@ -35,7 +35,6 @@ add_library(input_common STATIC
input_poller.h
main.cpp
main.h
precompiled_headers.h
)
if (MSVC)
@@ -92,10 +91,6 @@ endif()
create_target_directory_groups(input_common)
target_link_libraries(input_common PUBLIC hid_core PRIVATE common Boost::headers)
if (YUZU_USE_PRECOMPILED_HEADERS)
target_precompile_headers(input_common PRIVATE precompiled_headers.h)
endif()
if (ANDROID)
target_sources(input_common PRIVATE
drivers/android.cpp

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

@@ -11,7 +11,6 @@ add_library(network STATIC
network.h
packet.cpp
packet.h
precompiled_headers.h
room.cpp
room.h
room_member.cpp
@@ -32,7 +31,3 @@ endif()
if (PLATFORM_SUN)
target_link_libraries(network PRIVATE socket nsl)
endif()
if (YUZU_USE_PRECOMPILED_HEADERS)
target_precompile_headers(network PRIVATE precompiled_headers.h)
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

@@ -36,7 +36,11 @@ if (USE_DISCORD_PRESENCE)
discord/discord_impl.cpp
discord/discord_impl.h
)
target_link_libraries(qt_common PUBLIC DiscordRPC::discord-rpc Qt6::Network)
target_link_libraries(qt_common PUBLIC DiscordRPC::discord-rpc httplib::httplib)
if (YUZU_USE_BUNDLED_OPENSSL)
target_link_libraries(qt_common PUBLIC OpenSSL::SSL)
target_compile_definitions(qt_common PRIVATE CPPHTTPLIB_OPENSSL_SUPPORT)
endif()
target_compile_definitions(qt_common PUBLIC USE_DISCORD_PRESENCE)
endif()

View File

@@ -203,7 +203,7 @@ struct Values {
QVector<u64> favorited_ids;
// Compatibility List
Setting<bool> show_compat{linkage, false, "show_compat", Category::UiGameList};
Setting<bool> show_compat{linkage, true, "show_compat", Category::UiGameList};
// Size & File Types Column
Setting<bool> show_size{linkage, true, "show_size", Category::UiGameList};

View File

@@ -8,17 +8,23 @@
#include <string>
#include <QEventLoop>
#include <QNetworkAccessManager>
#include <QNetworkReply>
#include <boost/algorithm/string/replace.hpp>
#include <httplib.h>
#include <discord_rpc.h>
#include <fmt/format.h>
#include <qdebug.h>
#include "common/common_types.h"
#include "common/string_util.h"
#include "core/core.h"
#include "core/loader/loader.h"
#include "qt_common/discord/discord_impl.h"
#include "discord_impl.h"
#ifdef YUZU_BUNDLED_OPENSSL
#include <openssl/cert.h>
#endif
namespace DiscordRPC {
@@ -44,6 +50,7 @@ std::string DiscordImpl::GetGameString(const std::string& title) {
// Replace spaces with dashes
std::replace(icon_name.begin(), icon_name.end(), ' ', '-');
boost::replace_all(icon_name, "é", "e");
// Remove non-alphanumeric characters but keep dashes
std::erase_if(icon_name, [](char c) { return !std::isalnum(c) && c != '-'; });
@@ -81,6 +88,8 @@ void DiscordImpl::UpdateGameStatus(bool use_default) {
presence.details = "Currently in game";
presence.startTimestamp = start_time;
Discord_UpdatePresence(&presence);
qDebug() << "game status updated";
}
void DiscordImpl::Update() {
@@ -97,15 +106,22 @@ void DiscordImpl::Update() {
"https://raw.githubusercontent.com/eden-emulator/boxart/refs/heads/master/img/{}.png",
icon_name);
QNetworkAccessManager manager;
QNetworkRequest request;
request.setUrl(QUrl(QString::fromStdString(game_url)));
request.setTransferTimeout(3000);
QNetworkReply* reply = manager.head(request);
QEventLoop request_event_loop;
reply->connect(reply, &QNetworkReply::finished, &request_event_loop, &QEventLoop::quit);
request_event_loop.exec();
UpdateGameStatus(reply->error());
httplib::SSLClient client(game_url);
client.set_connection_timeout(3);
client.set_read_timeout(3);
client.set_follow_location(true);
#ifdef YUZU_BUNDLED_OPENSSL
client.load_ca_cert_store(kCert, sizeof(kCert));
#endif
httplib::Request request{
.method = "HEAD",
.path = game_url,
};
auto res = client.send(request);
UpdateGameStatus(res && res->status == 200);
return;
}

View File

@@ -330,6 +330,7 @@ void FixProfiles()
// TODO: better solution
system->GetProfileManager().ResetUserSaveFile();
std::vector<std::string> orphaned = system->GetProfileManager().FindOrphanedProfiles();
std::vector<std::string> good = system->GetProfileManager().FindGoodProfiles();
// no orphaned dirs--all good :)
if (orphaned.empty())
@@ -346,15 +347,27 @@ void FixProfiles()
qorphaned = qorphaned % QStringLiteral("\n") % QString::fromStdString(s);
}
QString qgood;
// max. of 8 good profiles is fair, I think
// 33 = 32 (UUID) + 1 (\n)
qgood.reserve(8 * 33);
for (const std::string& s : good) {
qgood = qgood % QStringLiteral("\n") % QString::fromStdString(s);
}
QtCommon::Frontend::Critical(
tr("Orphaned Profiles Detected!"),
tr("UNEXPECTED BAD THINGS MAY HAPPEN IF YOU DON'T READ THIS!\n"
"Eden has detected the following save directories with no attached profile:\n"
"%1\n\n"
"The following profiles are valid:\n"
"%2\n\n"
"Click \"OK\" to open your save folder and fix up your profiles.\n"
"Hint: copy the contents of the largest or last-modified folder elsewhere, "
"Hint: copy the contents of the largest or last-modified folder elsewhere, "
"delete all orphaned profiles, and move your copied contents to the good profile.")
.arg(qorphaned));
.arg(qorphaned, qgood));
QtCommon::Game::OpenSaveFolder();
}

View File

@@ -237,7 +237,6 @@ add_library(shader_recompiler STATIC
ir_opt/vendor_workaround_pass.cpp
ir_opt/verification_pass.cpp
object_pool.h
precompiled_headers.h
profile.h
program_header.h
runtime_info.h
@@ -266,7 +265,3 @@ else()
endif()
create_target_directory_groups(shader_recompiler)
if (YUZU_USE_PRECOMPILED_HEADERS)
target_precompile_headers(shader_recompiler PRIVATE precompiled_headers.h)
endif()

View File

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

View File

@@ -17,7 +17,6 @@ add_executable(tests
common/unique_function.cpp
core/core_timing.cpp
core/internal_network/network.cpp
precompiled_headers.h
video_core/memory_tracker.cpp
input_common/calibration_configuration_job.cpp
)
@@ -28,7 +27,3 @@ target_link_libraries(tests PRIVATE common core input_common video_core)
target_link_libraries(tests PRIVATE ${PLATFORM_LIBRARIES} Catch2::Catch2WithMain Threads::Threads)
add_test(NAME tests COMMAND tests)
if (YUZU_USE_PRECOMPILED_HEADERS)
target_precompile_headers(tests PRIVATE precompiled_headers.h)
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

@@ -102,7 +102,6 @@ add_library(video_core STATIC
invalidation_accumulator.h
memory_manager.cpp
memory_manager.h
precompiled_headers.h
present.h
pte_kind.h
query_cache/bank_base.h
@@ -395,10 +394,6 @@ if (ARCHITECTURE_x86_64 OR ARCHITECTURE_arm64)
target_link_libraries(video_core PRIVATE dynarmic::dynarmic)
endif()
if (YUZU_USE_PRECOMPILED_HEADERS)
target_precompile_headers(video_core PRIVATE precompiled_headers.h)
endif()
if (ANDROID AND ARCHITECTURE_arm64)
target_link_libraries(video_core PRIVATE adrenotools)
endif()

View File

@@ -7,6 +7,7 @@
#pragma once
#include <algorithm>
#include <cstring>
#include <memory>
#include <numeric>
@@ -15,6 +16,8 @@
#include "video_core/guest_memory.h"
#include "video_core/host1x/gpu_device_memory_manager.h"
#include "video_core/texture_cache/util.h"
#include "video_core/polygon_mode_utils.h"
#include "video_core/renderer_vulkan/line_loop_utils.h"
namespace VideoCommon {
@@ -353,14 +356,37 @@ void BufferCache<P>::UpdateComputeBuffers() {
template <class P>
void BufferCache<P>::BindHostGeometryBuffers(bool is_indexed) {
const auto& draw_state = maxwell3d->draw_manager->GetDrawState();
if (is_indexed) {
BindHostIndexBuffer();
} else if constexpr (!HAS_FULL_INDEX_AND_PRIMITIVE_SUPPORT) {
const auto& draw_state = maxwell3d->draw_manager->GetDrawState();
if (draw_state.topology == Maxwell::PrimitiveTopology::Quads ||
draw_state.topology == Maxwell::PrimitiveTopology::QuadStrip) {
runtime.BindQuadIndexBuffer(draw_state.topology, draw_state.vertex_buffer.first,
draw_state.vertex_buffer.count);
} else {
if constexpr (!P::IS_OPENGL) {
const auto polygon_mode = VideoCore::EffectivePolygonMode(maxwell3d->regs);
if (draw_state.topology == Maxwell::PrimitiveTopology::Polygon &&
polygon_mode == Maxwell::PolygonMode::Line && draw_state.vertex_buffer.count > 1) {
const u32 vertex_count = draw_state.vertex_buffer.count;
const u32 generated_count = vertex_count + 1;
const bool use_u16 = vertex_count <= 0x10000;
const u32 element_size = use_u16 ? sizeof(u16) : sizeof(u32);
auto staging = runtime.UploadStagingBuffer(
static_cast<size_t>(generated_count) * element_size);
std::span<u8> dst_span{staging.mapped_span.data(),
generated_count * static_cast<size_t>(element_size)};
Vulkan::LineLoop::GenerateSequentialWithClosureRaw(dst_span, element_size);
const auto synthetic_format = use_u16 ? Maxwell::IndexFormat::UnsignedShort
: Maxwell::IndexFormat::UnsignedInt;
runtime.BindIndexBuffer(draw_state.topology, synthetic_format,
draw_state.vertex_buffer.first, generated_count,
staging.buffer, static_cast<u32>(staging.offset),
generated_count * element_size);
}
}
if constexpr (!HAS_FULL_INDEX_AND_PRIMITIVE_SUPPORT) {
if (draw_state.topology == Maxwell::PrimitiveTopology::Quads ||
draw_state.topology == Maxwell::PrimitiveTopology::QuadStrip) {
runtime.BindQuadIndexBuffer(draw_state.topology, draw_state.vertex_buffer.first,
draw_state.vertex_buffer.count);
}
}
}
BindHostVertexBuffers();
@@ -689,6 +715,44 @@ void BufferCache<P>::BindHostIndexBuffer() {
const u32 offset = buffer.Offset(channel_state->index_buffer.device_addr);
const u32 size = channel_state->index_buffer.size;
const auto& draw_state = maxwell3d->draw_manager->GetDrawState();
if constexpr (!P::IS_OPENGL) {
const auto polygon_mode = VideoCore::EffectivePolygonMode(maxwell3d->regs);
const bool polygon_line =
draw_state.topology == Maxwell::PrimitiveTopology::Polygon &&
polygon_mode == Maxwell::PolygonMode::Line;
if (polygon_line && draw_state.index_buffer.count > 1) {
const u32 element_size = draw_state.index_buffer.FormatSizeInBytes();
const size_t src_bytes = static_cast<size_t>(draw_state.index_buffer.count) * element_size;
const size_t total_bytes = src_bytes + element_size;
auto staging = runtime.UploadStagingBuffer(total_bytes);
std::span<u8> dst_span{staging.mapped_span.data(), total_bytes};
std::span<const u8> src_span;
if (!draw_state.inline_index_draw_indexes.empty()) {
const u8* const src =
draw_state.inline_index_draw_indexes.data() +
static_cast<size_t>(draw_state.index_buffer.first) * element_size;
src_span = {src, src_bytes};
} else if (const u8* const cpu_base =
device_memory.GetPointer<u8>(channel_state->index_buffer.device_addr)) {
const u8* const src = cpu_base +
static_cast<size_t>(draw_state.index_buffer.first) * element_size;
src_span = {src, src_bytes};
} else {
const DAddr src_addr =
channel_state->index_buffer.device_addr +
static_cast<DAddr>(draw_state.index_buffer.first) * element_size;
device_memory.ReadBlockUnsafe(src_addr, dst_span.data(), src_bytes);
src_span = {dst_span.data(), src_bytes};
}
Vulkan::LineLoop::CopyWithClosureRaw(dst_span, src_span, element_size);
buffer.MarkUsage(offset, size);
runtime.BindIndexBuffer(draw_state.topology, draw_state.index_buffer.format,
draw_state.index_buffer.first, draw_state.index_buffer.count + 1,
staging.buffer, static_cast<u32>(staging.offset),
static_cast<u32>(total_bytes));
return;
}
}
if (!draw_state.inline_index_draw_indexes.empty()) [[unlikely]] {
if constexpr (USE_MEMORY_MAPS_FOR_UPLOADS) {
auto upload_staging = runtime.UploadStagingBuffer(size);

View File

@@ -0,0 +1,46 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
#include "video_core/engines/maxwell_3d.h"
namespace VideoCore {
inline Tegra::Engines::Maxwell3D::Regs::PolygonMode EffectivePolygonMode(
const Tegra::Engines::Maxwell3D::Regs& regs) {
using Maxwell = Tegra::Engines::Maxwell3D::Regs;
const bool cull_enabled = regs.gl_cull_test_enabled != 0;
const auto cull_face = regs.gl_cull_face;
const bool cull_front = cull_enabled && (cull_face == Maxwell::CullFace::Front ||
cull_face == Maxwell::CullFace::FrontAndBack);
const bool cull_back = cull_enabled && (cull_face == Maxwell::CullFace::Back ||
cull_face == Maxwell::CullFace::FrontAndBack);
const bool render_front = !cull_front;
const bool render_back = !cull_back;
const auto front_mode = regs.polygon_mode_front;
const auto back_mode = regs.polygon_mode_back;
if (render_front && render_back && front_mode != back_mode) {
if (front_mode == Maxwell::PolygonMode::Line || back_mode == Maxwell::PolygonMode::Line) {
return Maxwell::PolygonMode::Line;
}
if (front_mode == Maxwell::PolygonMode::Point || back_mode == Maxwell::PolygonMode::Point) {
return Maxwell::PolygonMode::Point;
}
}
if (render_front) {
return front_mode;
}
if (render_back) {
return back_mode;
}
return front_mode;
}
} // namespace VideoCore

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

@@ -1081,7 +1081,7 @@ void RasterizerOpenGL::SyncColorMask() {
flags[Dirty::ColorMask0] = false;
auto& mask = regs.color_mask[0];
glColorMask(mask.R != 0, mask.B != 0, mask.G != 0, mask.A != 0);
glColorMask(mask.R != 0, mask.G != 0, mask.B != 0, mask.A != 0);
return;
}

View File

@@ -14,6 +14,7 @@
#include "video_core/engines/draw_manager.h"
#include "video_core/renderer_vulkan/fixed_pipeline_state.h"
#include "video_core/renderer_vulkan/vk_state_tracker.h"
#include "video_core/polygon_mode_utils.h"
namespace Vulkan {
namespace {
@@ -64,7 +65,7 @@ void FixedPipelineState::Refresh(Tegra::Engines::Maxwell3D& maxwell3d, DynamicFe
dynamic_vertex_input.Assign(features.has_dynamic_vertex_input ? 1 : 0);
xfb_enabled.Assign(regs.transform_feedback_enabled != 0);
ndc_minus_one_to_one.Assign(regs.depth_mode == Maxwell::DepthMode::MinusOneToOne ? 1 : 0);
polygon_mode.Assign(PackPolygonMode(regs.polygon_mode_front));
polygon_mode.Assign(PackPolygonMode(VideoCore::EffectivePolygonMode(regs)));
tessellation_primitive.Assign(static_cast<u32>(regs.tessellation.params.domain_type.Value()));
tessellation_spacing.Assign(static_cast<u32>(regs.tessellation.params.spacing.Value()));
tessellation_clockwise.Assign(regs.tessellation.params.output_primitives.Value() ==

View File

@@ -0,0 +1,68 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
#include <algorithm>
#include <cstring>
#include <span>
#include "common/assert.h"
#include "common/common_types.h"
namespace Vulkan::LineLoop {
inline void CopyWithClosureRaw(std::span<u8> dst, std::span<const u8> src, size_t element_size) {
ASSERT_MSG(dst.size() == src.size() + element_size, "Invalid line loop copy sizes");
if (src.empty()) {
if (!dst.empty()) {
std::fill(dst.begin(), dst.end(), u8{0});
}
return;
}
std::memcpy(dst.data(), src.data(), src.size());
std::memcpy(dst.data() + src.size(), src.data(), element_size);
}
inline void GenerateSequentialWithClosureRaw(std::span<u8> dst, size_t element_size,
u64 start_value = 0) {
if (dst.empty()) {
return;
}
const size_t last = dst.size() - element_size;
size_t offset = 0;
u64 value = start_value;
while (offset < last) {
std::memcpy(dst.data() + offset, &value, element_size);
offset += element_size;
++value;
}
std::memcpy(dst.data() + offset, &start_value, element_size);
}
template <typename T>
inline void CopyWithClosure(std::span<T> dst, std::span<const T> src) {
ASSERT_MSG(dst.size() == src.size() + 1, "Invalid destination size for line loop copy");
if (src.empty()) {
if (!dst.empty()) {
dst.front() = {};
}
return;
}
std::copy(src.begin(), src.end(), dst.begin());
dst.back() = src.front();
}
template <typename T>
inline void GenerateSequentialWithClosure(std::span<T> dst, T start_value = {}) {
if (dst.empty()) {
return;
}
const size_t last = dst.size() - 1;
for (size_t i = 0; i < last; ++i) {
dst[i] = static_cast<T>(start_value + static_cast<T>(i));
}
dst.back() = start_value;
}
} // namespace Vulkan::LineLoop

View File

@@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@@ -326,44 +329,9 @@ VkShaderStageFlagBits ShaderStage(Shader::Stage stage) {
}
VkPrimitiveTopology PrimitiveTopology([[maybe_unused]] const Device& device,
Maxwell::PrimitiveTopology topology) {
switch (topology) {
case Maxwell::PrimitiveTopology::Points:
return VK_PRIMITIVE_TOPOLOGY_POINT_LIST;
case Maxwell::PrimitiveTopology::Lines:
return VK_PRIMITIVE_TOPOLOGY_LINE_LIST;
case Maxwell::PrimitiveTopology::LineLoop:
return VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
case Maxwell::PrimitiveTopology::LineStrip:
return VK_PRIMITIVE_TOPOLOGY_LINE_STRIP;
case Maxwell::PrimitiveTopology::Triangles:
return VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
case Maxwell::PrimitiveTopology::TriangleStrip:
return VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP;
case Maxwell::PrimitiveTopology::TriangleFan:
return VK_PRIMITIVE_TOPOLOGY_TRIANGLE_FAN;
case Maxwell::PrimitiveTopology::LinesAdjacency:
return VK_PRIMITIVE_TOPOLOGY_LINE_LIST_WITH_ADJACENCY;
case Maxwell::PrimitiveTopology::LineStripAdjacency:
return VK_PRIMITIVE_TOPOLOGY_LINE_STRIP_WITH_ADJACENCY;
case Maxwell::PrimitiveTopology::TrianglesAdjacency:
return VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST_WITH_ADJACENCY;
case Maxwell::PrimitiveTopology::TriangleStripAdjacency:
return VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP_WITH_ADJACENCY;
case Maxwell::PrimitiveTopology::Quads:
case Maxwell::PrimitiveTopology::QuadStrip:
// TODO: Use VK_PRIMITIVE_TOPOLOGY_QUAD_LIST_EXT/VK_PRIMITIVE_TOPOLOGY_QUAD_STRIP_EXT
// whenever it releases
return VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
case Maxwell::PrimitiveTopology::Patches:
return VK_PRIMITIVE_TOPOLOGY_PATCH_LIST;
case Maxwell::PrimitiveTopology::Polygon:
LOG_WARNING(Render_Vulkan, "Draw mode is Polygon with a polygon mode of lines should be a "
"single body and not a bunch of triangles.");
return VK_PRIMITIVE_TOPOLOGY_TRIANGLE_FAN;
}
UNIMPLEMENTED_MSG("Unimplemented topology={}", topology);
return {};
Maxwell::PrimitiveTopology topology,
Maxwell::PolygonMode polygon_mode) {
return detail::PrimitiveTopologyNoDevice(topology, polygon_mode);
}
VkFormat VertexFormat(const Device& device, Maxwell::VertexAttribute::Type type,

View File

@@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@@ -15,6 +18,52 @@ namespace Vulkan::MaxwellToVK {
using Maxwell = Tegra::Engines::Maxwell3D::Regs;
using PixelFormat = VideoCore::Surface::PixelFormat;
namespace detail {
constexpr VkPrimitiveTopology PrimitiveTopologyNoDevice(Maxwell::PrimitiveTopology topology,
Maxwell::PolygonMode polygon_mode) {
switch (topology) {
case Maxwell::PrimitiveTopology::Points:
return VK_PRIMITIVE_TOPOLOGY_POINT_LIST;
case Maxwell::PrimitiveTopology::Lines:
return VK_PRIMITIVE_TOPOLOGY_LINE_LIST;
case Maxwell::PrimitiveTopology::LineLoop:
return VK_PRIMITIVE_TOPOLOGY_LINE_STRIP;
case Maxwell::PrimitiveTopology::LineStrip:
return VK_PRIMITIVE_TOPOLOGY_LINE_STRIP;
case Maxwell::PrimitiveTopology::Triangles:
return VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
case Maxwell::PrimitiveTopology::TriangleStrip:
return VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP;
case Maxwell::PrimitiveTopology::TriangleFan:
return VK_PRIMITIVE_TOPOLOGY_TRIANGLE_FAN;
case Maxwell::PrimitiveTopology::LinesAdjacency:
return VK_PRIMITIVE_TOPOLOGY_LINE_LIST_WITH_ADJACENCY;
case Maxwell::PrimitiveTopology::LineStripAdjacency:
return VK_PRIMITIVE_TOPOLOGY_LINE_STRIP_WITH_ADJACENCY;
case Maxwell::PrimitiveTopology::TrianglesAdjacency:
return VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST_WITH_ADJACENCY;
case Maxwell::PrimitiveTopology::TriangleStripAdjacency:
return VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP_WITH_ADJACENCY;
case Maxwell::PrimitiveTopology::Quads:
case Maxwell::PrimitiveTopology::QuadStrip:
return VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
case Maxwell::PrimitiveTopology::Patches:
return VK_PRIMITIVE_TOPOLOGY_PATCH_LIST;
case Maxwell::PrimitiveTopology::Polygon:
switch (polygon_mode) {
case Maxwell::PolygonMode::Fill:
return VK_PRIMITIVE_TOPOLOGY_TRIANGLE_FAN;
case Maxwell::PolygonMode::Line:
return VK_PRIMITIVE_TOPOLOGY_LINE_STRIP;
case Maxwell::PolygonMode::Point:
return VK_PRIMITIVE_TOPOLOGY_POINT_LIST;
}
break;
}
return VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
}
} // namespace detail
namespace Sampler {
VkFilter Filter(Tegra::Texture::TextureFilter filter);
@@ -46,7 +95,8 @@ struct FormatInfo {
VkShaderStageFlagBits ShaderStage(Shader::Stage stage);
VkPrimitiveTopology PrimitiveTopology(const Device& device, Maxwell::PrimitiveTopology topology);
VkPrimitiveTopology PrimitiveTopology(const Device& device, Maxwell::PrimitiveTopology topology,
Maxwell::PolygonMode polygon_mode);
VkFormat VertexFormat(const Device& device, Maxwell::VertexAttribute::Type type,
Maxwell::VertexAttribute::Size size);

View File

@@ -7,6 +7,7 @@
#include <algorithm>
#include <iostream>
#include <span>
#include <string_view>
#include <boost/container/small_vector.hpp>
#include <boost/container/static_vector.hpp>
@@ -22,6 +23,7 @@
#include "video_core/renderer_vulkan/vk_scheduler.h"
#include "video_core/renderer_vulkan/vk_texture_cache.h"
#include "video_core/renderer_vulkan/vk_update_descriptor.h"
#include "video_core/polygon_mode_utils.h"
#include "video_core/shader_notify.h"
#include "video_core/texture_cache/texture_cache.h"
#include "video_core/vulkan_common/vulkan_device.h"
@@ -614,7 +616,10 @@ void GraphicsPipeline::MakePipeline(VkRenderPass render_pass) {
vertex_input_ci.pNext = &input_divisor_ci;
}
const bool has_tess_stages = spv_modules[1] || spv_modules[2];
auto input_assembly_topology = MaxwellToVK::PrimitiveTopology(device, key.state.topology);
const auto polygon_mode =
FixedPipelineState::UnpackPolygonMode(key.state.polygon_mode.Value());
auto input_assembly_topology =
MaxwellToVK::PrimitiveTopology(device, key.state.topology, polygon_mode);
if (input_assembly_topology == VK_PRIMITIVE_TOPOLOGY_PATCH_LIST) {
if (!has_tess_stages) {
LOG_WARNING(Render_Vulkan, "Patch topology used without tessellation, using points");
@@ -629,6 +634,33 @@ void GraphicsPipeline::MakePipeline(VkRenderPass render_pass) {
input_assembly_topology = VK_PRIMITIVE_TOPOLOGY_PATCH_LIST;
}
}
if (key.state.topology == Maxwell::PrimitiveTopology::Polygon) {
const auto polygon_mode_name = [polygon_mode]() -> std::string_view {
switch (polygon_mode) {
case Maxwell::PolygonMode::Fill:
return "Fill";
case Maxwell::PolygonMode::Line:
return "Line";
case Maxwell::PolygonMode::Point:
return "Point";
}
return "Unknown";
}();
const auto vk_topology_name = [input_assembly_topology]() -> std::string_view {
switch (input_assembly_topology) {
case VK_PRIMITIVE_TOPOLOGY_TRIANGLE_FAN:
return "TriangleFan";
case VK_PRIMITIVE_TOPOLOGY_LINE_STRIP:
return "LineStrip";
case VK_PRIMITIVE_TOPOLOGY_POINT_LIST:
return "PointList";
default:
return "Unexpected";
}
}();
LOG_DEBUG(Render_Vulkan, "Polygon primitive in {} mode mapped to {}", polygon_mode_name,
vk_topology_name);
}
const VkPipelineInputAssemblyStateCreateInfo input_assembly_ci{
.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO,
.pNext = nullptr,

View File

@@ -37,6 +37,7 @@
#include "video_core/renderer_vulkan/vk_update_descriptor.h"
#include "video_core/shader_cache.h"
#include "video_core/texture_cache/texture_cache_base.h"
#include "video_core/polygon_mode_utils.h"
#include "video_core/vulkan_common/vulkan_device.h"
#include "video_core/vulkan_common/vulkan_wrapper.h"
@@ -148,7 +149,8 @@ VkRect2D GetScissorState(const Maxwell& regs, size_t index, u32 up_scale = 1, u3
return scissor;
}
DrawParams MakeDrawParams(const MaxwellDrawState& draw_state, u32 num_instances, bool is_indexed) {
DrawParams MakeDrawParams(const MaxwellDrawState& draw_state, u32 num_instances, bool is_indexed,
Maxwell::PolygonMode polygon_mode) {
DrawParams params{
.base_instance = draw_state.base_instance,
.num_instances = num_instances,
@@ -168,6 +170,21 @@ DrawParams MakeDrawParams(const MaxwellDrawState& draw_state, u32 num_instances,
params.base_vertex = 0;
params.is_indexed = true;
}
const bool polygon_line =
draw_state.topology == Maxwell::PrimitiveTopology::Polygon &&
polygon_mode == Maxwell::PolygonMode::Line;
if (polygon_line) {
if (params.is_indexed) {
if (draw_state.index_buffer.count > 1) {
params.num_vertices = draw_state.index_buffer.count + 1;
}
} else if (draw_state.vertex_buffer.count > 1) {
params.num_vertices = draw_state.vertex_buffer.count + 1;
params.is_indexed = true;
params.first_index = 0;
params.base_vertex = draw_state.vertex_buffer.first;
}
}
return params;
}
} // Anonymous namespace
@@ -236,7 +253,8 @@ void RasterizerVulkan::Draw(bool is_indexed, u32 instance_count) {
PrepareDraw(is_indexed, [this, is_indexed, instance_count] {
const auto& draw_state = maxwell3d->draw_manager->GetDrawState();
const u32 num_instances{instance_count};
const DrawParams draw_params{MakeDrawParams(draw_state, num_instances, is_indexed)};
const auto polygon_mode = VideoCore::EffectivePolygonMode(maxwell3d->regs);
const DrawParams draw_params{MakeDrawParams(draw_state, num_instances, is_indexed, polygon_mode)};
scheduler.Record([draw_params](vk::CommandBuffer cmdbuf) {
if (draw_params.is_indexed) {
cmdbuf.DrawIndexed(draw_params.num_vertices, draw_params.num_instances,

View File

@@ -59,7 +59,7 @@ static Shader::TextureType ConvertTextureType(const Tegra::Texture::TICEntry& en
case Tegra::Texture::TextureType::TextureCubeArray:
return Shader::TextureType::ColorArrayCube;
default:
UNIMPLEMENTED();
LOG_ERROR(Shader, "Invalid texture_type={}, falling back to texture_type={}", static_cast<int>(entry.texture_type.Value()), Shader::TextureType::Color2D);
return Shader::TextureType::Color2D;
}
}

View File

@@ -51,70 +51,84 @@ ImageInfo::ImageInfo(const TICEntry& config) noexcept {
config.texture_type != TextureType::Texture2DNoMipmap) {
ASSERT(!config.IsPitchLinear());
}
//Normalize so that the 1D that actually uses layers is treated as 1DArray
TextureType tex_type = config.texture_type;
if (tex_type == TextureType::Texture1D &&
(config.Depth() > 1 || config.BaseLayer() != 0)) {
if (tex_type == TextureType::Texture1D && (config.Depth() > 1 || config.BaseLayer() != 0)) {
tex_type = TextureType::Texture1DArray;
} else if (tex_type == TextureType::Texture2D && (config.Depth() > 1 || config.BaseLayer() != 0)) {
tex_type = TextureType::Texture2DArray;
}
switch (tex_type) {
case TextureType::Texture1D:
ASSERT(config.BaseLayer() == 0);
type = ImageType::e1D;
size.width = config.Width();
resources.layers = 1;
break;
case TextureType::Texture1DArray:
type = ImageType::e1D;
size.width = config.Width();
resources.layers = config.BaseLayer() + config.Depth();
break;
case TextureType::Texture2D:
case TextureType::Texture2DNoMipmap:
ASSERT(config.Depth() == 1);
type = config.IsPitchLinear() ? ImageType::Linear : ImageType::e2D;
rescaleable = !config.IsPitchLinear();
size.width = config.Width();
size.height = config.Height();
resources.layers = config.BaseLayer() + 1;
break;
case TextureType::Texture2DArray:
type = ImageType::e2D;
rescaleable = true;
size.width = config.Width();
size.height = config.Height();
resources.layers = config.BaseLayer() + config.Depth();
break;
case TextureType::TextureCubemap:
ASSERT(config.Depth() == 1);
type = ImageType::e2D;
size.width = config.Width();
size.height = config.Height();
resources.layers = config.BaseLayer() + 6;
break;
case TextureType::TextureCubeArray:
UNIMPLEMENTED_IF(config.load_store_hint != 0);
type = ImageType::e2D;
size.width = config.Width();
size.height = config.Height();
resources.layers = config.BaseLayer() + config.Depth() * 6;
break;
case TextureType::Texture3D:
ASSERT(config.BaseLayer() == 0);
type = ImageType::e3D;
size.width = config.Width();
size.height = config.Height();
size.depth = config.Depth();
resources.layers = 1;
break;
case TextureType::Texture1DBuffer:
type = ImageType::Buffer;
size.width = config.Width();
resources.layers = 1;
break;
default:
ASSERT_MSG(false, "Invalid texture_type={}", static_cast<int>(config.texture_type.Value()));
break;
case TextureType::Texture1D:
ASSERT(config.BaseLayer() == 0);
ASSERT(config.Depth() == 1);
type = ImageType::e1D;
size.width = config.Width();
size.depth = 1;
resources.layers = 1;
break;
case TextureType::Texture1DArray:
ASSERT(config.Depth() > 0);
ASSERT(config.BaseLayer() < config.Depth());
type = ImageType::e1D;
size.width = config.Width();
size.depth = 1;
resources.layers = config.Depth() - config.BaseLayer();
break;
case TextureType::Texture2D:
case TextureType::Texture2DNoMipmap:
ASSERT(config.BaseLayer() == 0);
ASSERT(config.Depth() == 1);
type = config.IsPitchLinear() ? ImageType::Linear : ImageType::e2D;
rescaleable = !config.IsPitchLinear();
size.width = config.Width();
size.height = config.Height();
size.depth = 1;
resources.layers = 1;
break;
case TextureType::Texture2DArray:
ASSERT(config.Depth() > 0);
ASSERT(config.BaseLayer() < config.Depth());
type = ImageType::e2D;
rescaleable = true;
size.width = config.Width();
size.height = config.Height();
size.depth = 1;
resources.layers = config.Depth() - config.BaseLayer();
break;
case TextureType::TextureCubemap:
ASSERT(config.Depth() == 1);
type = ImageType::e2D;
size.width = config.Width();
size.height = config.Height();
size.depth = 1;
resources.layers = 6;
break;
case TextureType::TextureCubeArray:
UNIMPLEMENTED_IF(config.load_store_hint != 0);
ASSERT(config.Depth() > 0);
type = ImageType::e2D;
size.width = config.Width();
size.height = config.Height();
size.depth = 1;
resources.layers = (config.Depth() - config.BaseLayer()) * 6;
break;
case TextureType::Texture3D:
ASSERT(config.BaseLayer() == 0);
type = ImageType::e3D;
size.width = config.Width();
size.height = config.Height();
size.depth = config.Depth();
resources.layers = 1;
break;
case TextureType::Texture1DBuffer:
type = ImageType::Buffer;
size.width = config.Width();
size.depth = 1;
resources.layers = 1;
break;
default:
ASSERT_MSG(false, "Invalid texture_type={}", static_cast<int>(tex_type));
break;
}
if (num_samples > 1) {
size.width *= NumSamplesX(config.msaa_mode);

View File

@@ -30,64 +30,73 @@ constexpr u8 RENDER_TARGET_SWIZZLE = (std::numeric_limits<u8>::max)();
} // Anonymous namespace
ImageViewInfo::ImageViewInfo(const TICEntry& config, s32 base_layer) noexcept
: format{PixelFormatFromTIC(config)},
x_source{CastSwizzle(config.x_source)},
y_source{CastSwizzle(config.y_source)},
z_source{CastSwizzle(config.z_source)},
w_source{CastSwizzle(config.w_source)} {
range.base = SubresourceBase{
.level = static_cast<s32>(config.res_min_mip_level),
.layer = base_layer,
};
range.extent.levels = config.res_max_mip_level - config.res_min_mip_level + 1;
TextureType tex_type = config.texture_type;
//normalize 1D texture with many layers
if (tex_type == TextureType::Texture1D &&
(config.Depth() > 1 || base_layer != 0)) {
tex_type = TextureType::Texture1DArray;
}
switch (tex_type) {
case TextureType::Texture1D:
ASSERT(config.Height() == 1);
ASSERT(config.Depth() == 1);
type = ImageViewType::e1D;
break;
case TextureType::Texture1DArray:
ASSERT(config.Height() == 1);
type = ImageViewType::e1DArray;
range.extent.layers = config.Depth();
break;
case TextureType::Texture2D:
case TextureType::Texture2DNoMipmap:
ASSERT(config.Depth() == 1);
type = config.normalized_coords ? ImageViewType::e2D : ImageViewType::Rect;
break;
case TextureType::Texture2DArray:
type = ImageViewType::e2DArray;
range.extent.layers = config.Depth();
break;
case TextureType::Texture3D:
type = ImageViewType::e3D;
break;
case TextureType::TextureCubemap:
ASSERT(config.Depth() == 1);
type = ImageViewType::Cube;
range.extent.layers = 6;
break;
case TextureType::TextureCubeArray:
type = ImageViewType::CubeArray;
range.extent.layers = config.Depth() * 6;
break;
case TextureType::Texture1DBuffer:
type = ImageViewType::Buffer;
break;
default:
ASSERT_MSG(false, "Invalid texture_type={}", static_cast<int>(config.texture_type.Value()));
break;
}
: format{PixelFormatFromTIC(config)},
x_source{CastSwizzle(config.x_source)},
y_source{CastSwizzle(config.y_source)},
z_source{CastSwizzle(config.z_source)},
w_source{CastSwizzle(config.w_source)} {
range.base = SubresourceBase{
.level = static_cast<s32>(config.res_min_mip_level),
.layer = base_layer,
};
range.extent.levels = config.res_max_mip_level - config.res_min_mip_level + 1;
TextureType tex_type = config.texture_type;
if (tex_type == TextureType::Texture1D && (config.Depth() > 1 || base_layer != 0)) {
tex_type = TextureType::Texture1DArray;
} else if (tex_type == TextureType::Texture2D && (config.Depth() > 1 || base_layer != 0)) {
tex_type = TextureType::Texture2DArray;
}
switch (tex_type) {
case TextureType::Texture1D:
ASSERT(config.Height() == 1);
ASSERT(config.Depth() == 1);
ASSERT(base_layer == 0);
type = ImageViewType::e1D;
range.extent.layers = 1;
break;
case TextureType::Texture1DArray:
ASSERT(config.Depth() > 0);
ASSERT(static_cast<u32>(base_layer) < config.Depth());
type = ImageViewType::e1DArray;
range.extent.layers = config.Depth() - base_layer;
break;
case TextureType::Texture2D:
case TextureType::Texture2DNoMipmap:
ASSERT(config.Depth() == 1);
ASSERT(base_layer == 0);
type = config.normalized_coords ? ImageViewType::e2D : ImageViewType::Rect;
range.extent.layers = 1;
break;
case TextureType::Texture2DArray:
ASSERT(config.Depth() > 0);
ASSERT(static_cast<u32>(base_layer) < config.Depth());
type = ImageViewType::e2DArray;
range.extent.layers = config.Depth() - base_layer;
break;
case TextureType::TextureCubemap:
ASSERT(config.Depth() == 1);
type = ImageViewType::Cube;
range.extent.layers = 6;
break;
case TextureType::TextureCubeArray:
ASSERT(config.Depth() > 0);
ASSERT(static_cast<u32>(base_layer) < config.Depth());
type = ImageViewType::CubeArray;
range.extent.layers = (config.Depth() - base_layer) * 6;
break;
case TextureType::Texture3D:
ASSERT(base_layer == 0);
type = ImageViewType::e3D;
range.extent.layers = 1;
break;
case TextureType::Texture1DBuffer:
type = ImageViewType::Buffer;
range.extent.layers = 1;
break;
default:
ASSERT_MSG(false, "Invalid texture_type={}", static_cast<int>(tex_type));
break;
}
}
ImageViewInfo::ImageViewInfo(ImageViewType type_, PixelFormat format_,

View File

@@ -734,8 +734,12 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
dynamic_state3_enables = true;
}
if (is_mvk && Settings::values.dyna_state.GetValue() != 0) {
LOG_WARNING(Render_Vulkan, "MoltenVK detected: Forcing dynamic state to 0 to prevent black screen issues");
// Mesa Intel drivers on UHD 620 have broken EDS causing extreme flickering - unknown if it affects other iGPUs
// ALSO affects ALL versions of UHD drivers on Windows 10+, seems to cause even worse issues like straight up crashing
// So... Yeah, UHD drivers fucking suck -- maybe one day we can work past this, maybe; some driver hacking?
// And then we can rest in peace by doing `< VK_MAKE_API_VERSION(26, 0, 0)` for our beloved mesa drivers... one day
if ((is_mvk || (is_integrated && is_intel_anv) || (is_integrated && is_intel_windows)) && Settings::values.dyna_state.GetValue() != 0) {
LOG_WARNING(Render_Vulkan, "Driver has broken dynamic state, forcing to 0 to prevent graphical issues");
Settings::values.dyna_state.SetValue(0);
}

View File

@@ -4,7 +4,6 @@
add_library(web_service STATIC
announce_room_json.cpp
announce_room_json.h
precompiled_headers.h
verify_login.cpp
verify_login.h
verify_user_jwt.cpp
@@ -20,7 +19,4 @@ target_link_libraries(web_service PRIVATE common network nlohmann_json::nlohmann
find_package(OpenSSL REQUIRED)
target_link_libraries(web_service PRIVATE OpenSSL::SSL OpenSSL::Crypto)
if (YUZU_USE_PRECOMPILED_HEADERS)
target_precompile_headers(web_service PRIVATE precompiled_headers.h)
endif()
target_compile_definitions(web_service PRIVATE CPPHTTPLIB_OPENSSL_SUPPORT)

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

@@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: 2017 Citra Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@@ -18,6 +21,10 @@
#pragma GCC diagnostic pop
#endif
#ifdef YUZU_BUNDLED_OPENSSL
#include <openssl/cert.h>
#endif
#include "common/logging/log.h"
#include "web_service/web_backend.h"
#include "web_service/web_result.h"
@@ -80,6 +87,9 @@ struct Client::Impl {
cli->set_connection_timeout(TIMEOUT_SECONDS);
cli->set_read_timeout(TIMEOUT_SECONDS);
cli->set_write_timeout(TIMEOUT_SECONDS);
#ifdef YUZU_BUNDLED_OPENSSL
cli->load_ca_cert_store(kCert, sizeof(kCert));
#endif
}
if (!cli->is_valid()) {
LOG_ERROR(WebService, "Invalid URL {}", host + path);

View File

@@ -198,7 +198,6 @@ add_executable(yuzu
multiplayer/state.cpp
multiplayer/state.h
multiplayer/validation.h
precompiled_headers.h
startup_checks.cpp
startup_checks.h
set_play_time_dialog.cpp
@@ -386,6 +385,7 @@ elseif(WIN32)
set_target_properties(yuzu PROPERTIES LINK_FLAGS_RELEASE "/SUBSYSTEM:WINDOWS")
elseif(MINGW)
set_target_properties(yuzu PROPERTIES LINK_FLAGS_RELEASE "-Wl,--subsystem,windows")
target_link_libraries(yuzu PRIVATE dwmapi)
endif()
endif()
@@ -447,10 +447,6 @@ if (ARCHITECTURE_x86_64 OR ARCHITECTURE_arm64)
target_link_libraries(yuzu PRIVATE dynarmic::dynarmic)
endif()
if (YUZU_USE_PRECOMPILED_HEADERS)
target_precompile_headers(yuzu PRIVATE precompiled_headers.h)
endif()
if (YUZU_ROOM)
target_link_libraries(yuzu PRIVATE yuzu-room)
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

@@ -24,7 +24,6 @@ add_executable(yuzu-cmd
emu_window/emu_window_sdl2_null.h
emu_window/emu_window_sdl2_vk.cpp
emu_window/emu_window_sdl2_vk.h
precompiled_headers.h
sdl_config.cpp
sdl_config.h
yuzu.cpp
@@ -61,8 +60,4 @@ if (MSVC)
copy_yuzu_SDL_deps(yuzu-cmd)
endif()
if (YUZU_USE_PRECOMPILED_HEADERS)
target_precompile_headers(yuzu-cmd PRIVATE precompiled_headers.h)
endif()
create_target_directory_groups(yuzu-cmd)

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"