Compare commits
23 Commits
liz-crash-
...
v0.0.4.tes
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
dd9cae4ebc | ||
|
|
8fa36a7737 | ||
|
|
07b09b3849 | ||
|
|
903faacaab | ||
|
|
c713d44c88 | ||
|
|
2a3e815dcd | ||
|
|
dc907616a9 | ||
|
|
0be29d2947 | ||
|
|
683c2834aa | ||
|
|
c788dbb3ef | ||
|
|
7846f4de31 | ||
|
|
a0769ad835 | ||
|
|
470214412b | ||
|
|
41e15e95b1 | ||
|
|
73ebf59af7 | ||
|
|
53bfd56b70 | ||
|
|
6ba25b6cc0 | ||
|
|
8d565d7793 | ||
|
|
311c71146d | ||
|
|
7751f86c1b | ||
|
|
4834fec159 | ||
|
|
b5d54b8df7 | ||
|
|
cd4bcb91cc |
@@ -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"
|
||||
152
.github/workflows/build.yml
vendored
152
.github/workflows/build.yml
vendored
@@ -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/*
|
||||
203
.github/workflows/trigger_release.yml
vendored
203
.github/workflows/trigger_release.yml
vendored
@@ -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/
|
||||
52
.patch/httplib/0001-mingw.patch
Normal file
52
.patch/httplib/0001-mingw.patch
Normal 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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
# =======================================================================
|
||||
|
||||
@@ -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)
|
||||
354
dist/compatibility_list/compatibility_list.json
vendored
Normal file
354
dist/compatibility_list/compatibility_list.json
vendored
Normal 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"
|
||||
}
|
||||
]
|
||||
@@ -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>
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
77
docs/Coding.md
Normal 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);
|
||||
```
|
||||
68
docs/Deps.md
68
docs/Deps.md
@@ -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).
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)**
|
||||
|
||||
3
externals/CMakeLists.txt
vendored
3
externals/CMakeLists.txt
vendored
@@ -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()
|
||||
|
||||
14
externals/cpmfile.json
vendored
14
externals/cpmfile.json
vendored
@@ -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"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
9285
externals/sse2neon/sse2neon.h
vendored
9285
externals/sse2neon/sse2neon.h
vendored
File diff suppressed because it is too large
Load Diff
19
shell.nix
Normal file
19
shell.nix
Normal 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
|
||||
];
|
||||
}
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
*/
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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()!!)
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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"
|
||||
@@ -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; });
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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"
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
@@ -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)
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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 += "&";
|
||||
break;
|
||||
case '"':
|
||||
escaped += """;
|
||||
break;
|
||||
case '<':
|
||||
escaped += "<";
|
||||
break;
|
||||
case '>':
|
||||
escaped += ">";
|
||||
break;
|
||||
case '&': escaped += "&"; break;
|
||||
case '"': escaped += """; break;
|
||||
case '<': escaped += "<"; break;
|
||||
case '>': escaped += ">"; 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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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+
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -105,6 +105,7 @@ public:
|
||||
|
||||
void ResetUserSaveFile();
|
||||
|
||||
std::vector<std::string> FindGoodProfiles();
|
||||
std::vector<std::string> FindOrphanedProfiles();
|
||||
|
||||
private:
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
27
src/core/hle/service/ldn/client_process_monitor.cpp
Normal file
27
src/core/hle/service/ldn/client_process_monitor.cpp
Normal 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
|
||||
25
src/core/hle/service/ldn/client_process_monitor.h
Normal file
25
src/core/hle/service/ldn/client_process_monitor.h
Normal 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
|
||||
@@ -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> {
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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> {
|
||||
|
||||
@@ -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"
|
||||
@@ -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)
|
||||
|
||||
@@ -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"
|
||||
@@ -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&
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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"
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"
|
||||
@@ -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()
|
||||
|
||||
@@ -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"
|
||||
@@ -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()
|
||||
|
||||
|
||||
@@ -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};
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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"
|
||||
@@ -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()
|
||||
|
||||
@@ -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"
|
||||
@@ -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()
|
||||
|
||||
@@ -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);
|
||||
|
||||
46
src/video_core/polygon_mode_utils.h
Normal file
46
src/video_core/polygon_mode_utils.h
Normal 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
|
||||
|
||||
@@ -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"
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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() ==
|
||||
|
||||
68
src/video_core/renderer_vulkan/line_loop_utils.h
Normal file
68
src/video_core/renderer_vulkan/line_loop_utils.h
Normal 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
|
||||
@@ -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,
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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_,
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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"
|
||||
@@ -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);
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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"
|
||||
@@ -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)
|
||||
|
||||
@@ -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"
|
||||
Reference in New Issue
Block a user