Compare commits
60 Commits
fix/discor
...
lock-term-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d34400226b | ||
|
|
d498db1106 | ||
|
|
e93159b047 | ||
|
|
d989166044 | ||
|
|
c96defb80d | ||
|
|
06275885f1 | ||
|
|
206e7af162 | ||
|
|
cdf4ce07bc | ||
|
|
2f591d33d3 | ||
|
|
e4b0c03a22 | ||
|
|
9582dfffee | ||
|
|
cf9f78636a | ||
|
|
ae46778cc3 | ||
|
|
c31761ab75 | ||
|
|
b50f8c620b | ||
|
|
fa718acfbe | ||
|
|
ce0a299bdb | ||
|
|
d3dbabcfc7 | ||
|
|
effe8109a7 | ||
|
|
2924297790 | ||
|
|
5c6aaa7eb1 | ||
|
|
83c162d80d | ||
|
|
1a2d1821b5 | ||
|
|
a9b87077f8 | ||
|
|
2e092d5f5d | ||
|
|
a5996c7511 | ||
|
|
1c57172524 | ||
|
|
d34b44ce39 | ||
|
|
ecf7af0cde | ||
|
|
6b01c13975 | ||
|
|
15becaa5a3 | ||
|
|
3d4bb08cfc | ||
|
|
b2ac18173f | ||
|
|
39f226a853 | ||
|
|
61ab1be0e7 | ||
|
|
3ba9769d7a | ||
|
|
dd9cae4ebc | ||
|
|
8fa36a7737 | ||
|
|
07b09b3849 | ||
|
|
903faacaab | ||
|
|
c713d44c88 | ||
|
|
2a3e815dcd | ||
|
|
dc907616a9 | ||
|
|
0be29d2947 | ||
|
|
683c2834aa | ||
|
|
c788dbb3ef | ||
|
|
7846f4de31 | ||
|
|
a0769ad835 | ||
|
|
470214412b | ||
|
|
41e15e95b1 | ||
|
|
73ebf59af7 | ||
|
|
53bfd56b70 | ||
|
|
6ba25b6cc0 | ||
|
|
8d565d7793 | ||
|
|
311c71146d | ||
|
|
7751f86c1b | ||
|
|
4834fec159 | ||
|
|
b5d54b8df7 | ||
|
|
cd4bcb91cc | ||
|
|
199bc6a170 |
@@ -4,7 +4,7 @@
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
# specify full path if dupes may exist
|
||||
EXCLUDE_FILES="CPM.cmake CPMUtil.cmake GetSCMRev.cmake sse2neon.h renderdoc_app.h tools/cpm tools/shellcheck.sh tools/update-cpm.sh externals/stb externals/glad externals/getopt externals/gamemode externals/FidelityFX-FSR externals/demangle externals/bc_decoder"
|
||||
EXCLUDE_FILES="CPM.cmake CPMUtil.cmake GetSCMRev.cmake renderdoc_app.h tools/cpm tools/shellcheck.sh tools/update-cpm.sh externals/stb externals/glad externals/getopt externals/gamemode externals/FidelityFX-FSR externals/demangle externals/bc_decoder"
|
||||
|
||||
# license header constants, please change when needed :))))
|
||||
YEAR=2025
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
|
||||
|
||||
@@ -212,11 +212,6 @@ cmake_dependent_option(USE_DISCORD_PRESENCE "Enables Discord Rich Presence" OFF
|
||||
|
||||
option(YUZU_TESTS "Compile tests" "${BUILD_TESTING}")
|
||||
|
||||
option(YUZU_USE_PRECOMPILED_HEADERS "Use precompiled headers" OFF)
|
||||
if (YUZU_USE_PRECOMPILED_HEADERS)
|
||||
message(STATUS "Using Precompiled Headers.")
|
||||
set(CMAKE_PCH_INSTANTIATE_TEMPLATES ON)
|
||||
endif()
|
||||
option(YUZU_ENABLE_LTO "Enable link-time optimization" OFF)
|
||||
if(YUZU_ENABLE_LTO)
|
||||
include(CheckIPOSupported)
|
||||
@@ -235,11 +230,8 @@ if(USE_CCACHE)
|
||||
message(STATUS "Found ccache at: ${CCACHE_BINARY}")
|
||||
set(CMAKE_C_COMPILER_LAUNCHER ${CCACHE_BINARY})
|
||||
set(CMAKE_CXX_COMPILER_LAUNCHER ${CCACHE_BINARY})
|
||||
if (YUZU_USE_PRECOMPILED_HEADERS)
|
||||
message(FATAL_ERROR "Precompiled headers are incompatible with ccache. Re-run CMake with -DYUZU_USE_PRECOMPILED_HEADERS=OFF.")
|
||||
endif()
|
||||
else()
|
||||
message(WARNING "USE_CCACHE enabled, but no executable found at: ${CCACHE_PATH}")
|
||||
message(FATAL_ERROR "USE_CCACHE enabled, but no executable found at: ${CCACHE_PATH}")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
@@ -352,10 +344,6 @@ if (NOT EXISTS ${PROJECT_BINARY_DIR}/dist/compatibility_list/compatibility_list.
|
||||
file(WRITE ${PROJECT_BINARY_DIR}/dist/compatibility_list/compatibility_list.json "")
|
||||
endif()
|
||||
|
||||
if (UNIX)
|
||||
add_compile_definitions(YUZU_UNIX=1)
|
||||
endif()
|
||||
|
||||
if (YUZU_LEGACY)
|
||||
message(WARNING "Making legacy build. Performance may suffer.")
|
||||
add_compile_definitions(YUZU_LEGACY)
|
||||
@@ -385,6 +373,8 @@ endif()
|
||||
# Build/optimization presets
|
||||
if (PLATFORM_LINUX OR CXX_CLANG)
|
||||
if (ARCHITECTURE_x86_64)
|
||||
# See https://gcc.gnu.org/onlinedocs/gcc/x86-Options.html
|
||||
# Generic supports Pentium Pro instruction set and above
|
||||
set(YUZU_BUILD_PRESET "custom" CACHE STRING "Build preset to use. One of: custom, generic, v3, zen2, zen4, native")
|
||||
if (${YUZU_BUILD_PRESET} STREQUAL "generic")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -march=x86-64 -mtune=generic")
|
||||
@@ -403,13 +393,17 @@ if (PLATFORM_LINUX OR CXX_CLANG)
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -march=native -mtune=native")
|
||||
endif()
|
||||
elseif(ARCHITECTURE_arm64)
|
||||
set(YUZU_BUILD_PRESET "custom" CACHE STRING "Build preset to use. One of: custom, generic, armv9")
|
||||
# See https://gcc.gnu.org/onlinedocs/gcc/AArch64-Options.html
|
||||
set(YUZU_BUILD_PRESET "custom" CACHE STRING "Build preset to use. One of: custom, generic, armv9, native")
|
||||
if (${YUZU_BUILD_PRESET} STREQUAL "generic")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -march=armv8-a -mtune=generic")
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -march=armv8-a -mtune=generic")
|
||||
elseif (${YUZU_BUILD_PRESET} STREQUAL "armv9")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -march=armv9-a -mtune=generic")
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -march=armv9-a -mtune=generic")
|
||||
elseif (${YUZU_BUILD_PRESET} STREQUAL "native")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -march=native -mtune=native")
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -march=native -mtune=native")
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
@@ -579,6 +573,35 @@ function(create_target_directory_groups target_name)
|
||||
endforeach()
|
||||
endfunction()
|
||||
|
||||
# Platform-specific library requirements
|
||||
# Put these BEFORE EXTERNALS or Boost WILL die
|
||||
# =============================================
|
||||
|
||||
if (APPLE)
|
||||
# Umbrella framework for everything GUI-related
|
||||
find_library(COCOA_LIBRARY Cocoa)
|
||||
set(PLATFORM_LIBRARIES ${COCOA_LIBRARY} ${IOKIT_LIBRARY} ${COREVIDEO_LIBRARY})
|
||||
find_library(ICONV_LIBRARY iconv REQUIRED)
|
||||
list(APPEND PLATFORM_LIBRARIES ${ICONV_LIBRARY})
|
||||
elseif (WIN32)
|
||||
# Target Windows 10
|
||||
add_compile_definitions(_WIN32_WINNT=0x0A00 WINVER=0x0A00)
|
||||
set(PLATFORM_LIBRARIES winmm ws2_32 iphlpapi)
|
||||
if (MINGW)
|
||||
# PSAPI is the Process Status API
|
||||
set(PLATFORM_LIBRARIES ${PLATFORM_LIBRARIES} psapi imm32 version)
|
||||
endif()
|
||||
elseif (PLATFORM_HAIKU)
|
||||
# Haiku is so special :)
|
||||
# Some fucking genius decided to name an entire module "network" in 2019
|
||||
# this caused great disaster amongst the Haiku community who had came first with
|
||||
# their "libnetwork.so"; since CMake doesn't do magic, we have to use an ABSOLUTE PATH
|
||||
# to the library itself, otherwise it will think we are linking to... our network thing
|
||||
set(PLATFORM_LIBRARIES bsd /boot/system/lib/libnetwork.so)
|
||||
elseif (CMAKE_SYSTEM_NAME MATCHES "^(Linux|kFreeBSD|GNU|SunOS)$")
|
||||
set(PLATFORM_LIBRARIES rt)
|
||||
endif()
|
||||
|
||||
add_subdirectory(externals)
|
||||
|
||||
# pass targets from externals
|
||||
@@ -729,34 +752,6 @@ endif()
|
||||
set(THREADS_PREFER_PTHREAD_FLAG ON)
|
||||
find_package(Threads REQUIRED)
|
||||
|
||||
# Platform-specific library requirements
|
||||
# ======================================
|
||||
|
||||
if (APPLE)
|
||||
# Umbrella framework for everything GUI-related
|
||||
find_library(COCOA_LIBRARY Cocoa)
|
||||
set(PLATFORM_LIBRARIES ${COCOA_LIBRARY} ${IOKIT_LIBRARY} ${COREVIDEO_LIBRARY})
|
||||
find_library(ICONV_LIBRARY iconv REQUIRED)
|
||||
list(APPEND PLATFORM_LIBRARIES ${ICONV_LIBRARY})
|
||||
elseif (WIN32)
|
||||
# Target Windows 10
|
||||
add_compile_definitions(_WIN32_WINNT=0x0A00 WINVER=0x0A00)
|
||||
set(PLATFORM_LIBRARIES winmm ws2_32 iphlpapi)
|
||||
if (MINGW)
|
||||
# PSAPI is the Process Status API
|
||||
set(PLATFORM_LIBRARIES ${PLATFORM_LIBRARIES} psapi imm32 version)
|
||||
endif()
|
||||
elseif (PLATFORM_HAIKU)
|
||||
# Haiku is so special :)
|
||||
# Some fucking genius decided to name an entire module "network" in 2019
|
||||
# this caused great disaster amongst the Haiku community who had came first with
|
||||
# their "libnetwork.so"; since CMake doesn't do magic, we have to use an ABSOLUTE PATH
|
||||
# to the library itself, otherwise it will think we are linking to... our network thing
|
||||
set(PLATFORM_LIBRARIES bsd /boot/system/lib/libnetwork.so)
|
||||
elseif (CMAKE_SYSTEM_NAME MATCHES "^(Linux|kFreeBSD|GNU|SunOS)$")
|
||||
set(PLATFORM_LIBRARIES rt)
|
||||
endif()
|
||||
|
||||
# Setup a custom clang-format target (if clang-format can be found) that will run
|
||||
# against all the src files. This should be used before making a pull request.
|
||||
# =======================================================================
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
# SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
# SPDX-FileCopyrightText: 2022 yuzu Emulator Project
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
|
||||
@@ -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
@@ -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>
|
||||
|
||||
107
dist/dev.eden_emu.eden.svg
vendored
|
Before Width: | Height: | Size: 9.2 KiB After Width: | Height: | Size: 13 KiB |
BIN
dist/eden.ico
vendored
|
Before Width: | Height: | Size: 316 KiB After Width: | Height: | Size: 403 KiB |
BIN
dist/eden_named.ico
vendored
|
Before Width: | Height: | Size: 54 KiB |
117
dist/eden_named.svg
vendored
|
Before Width: | Height: | Size: 4.9 MiB After Width: | Height: | Size: 39 KiB |
1
dist/icon_variations/base.svg
vendored
Normal file
|
After Width: | Height: | Size: 9.2 KiB |
81
dist/icon_variations/base_named.svg
vendored
Normal file
|
After Width: | Height: | Size: 37 KiB |
106
dist/icon_variations/halloween2025.svg
vendored
Normal file
@@ -0,0 +1,106 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
width="512"
|
||||
height="512"
|
||||
fill="none"
|
||||
viewBox="0 0 512 512"
|
||||
version="1.1"
|
||||
id="svg7"
|
||||
sodipodi:docname="dev.eden_emu.eden.svg"
|
||||
inkscape:version="1.4.2 (ebf0e940d0, 2025-05-08)"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg">
|
||||
<defs
|
||||
id="defs7" />
|
||||
<sodipodi:namedview
|
||||
id="namedview7"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#000000"
|
||||
borderopacity="0.25"
|
||||
inkscape:showpageshadow="2"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:deskcolor="#d1d1d1"
|
||||
inkscape:zoom="1.25"
|
||||
inkscape:cx="186.8"
|
||||
inkscape:cy="210.4"
|
||||
inkscape:window-width="1600"
|
||||
inkscape:window-height="849"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="27"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="svg7" />
|
||||
<path
|
||||
fill="#bf42f6"
|
||||
d="M346.71 255.306c.788.006 1.577.013 3.124.059 4.006.027 7.253.015 10.501.003 33.391.022 66.783.028 100.175.078 8.174.013 16.349.279 24.522.209 3.084-.026 4.596.75 4.356 4.156-.293 4.157-.31 8.343-.259 12.513.049 3.964-1.248 5.539-5.679 5.524-48.85-.158-97.701-.071-146.552-.056-22.107.007-44.214 0-66.321 0h-5.43v7.117h223.011c-1.553 8.254-2.894 15.76-4.475 23.215-.17.802-1.666 1.617-2.685 1.87-1.312.327-2.763.099-4.155.099H265.099v6.701h216.666c-1.609 5.425-2.887 10.074-4.367 14.658-2.758 8.54-2.807 8.524-11.564 8.524-64.929 0-129.859.014-194.788-.04-3.401-.003-6.19.251-4.758 4.652.349 1.075 1.875 2.026 3.071 2.491 1.075.418 2.439.092 3.676.092l191.542.001h7.17c-3.533 7.29-6.399 13.72-9.834 19.83-.651 1.157-3.562 1.448-5.432 1.454-27.981.088-55.963.061-83.944.061-31.692 0-63.384.005-95.075-.008-2.61-.002-5.219-.124-8.53-.021-3.11.173-5.518.177-7.926.181-.849-7.032-2.289-14.051-2.44-21.098-.547-25.616-.773-51.242-.844-76.864-.014-4.88 1.228-9.763 2.52-14.927 2.88-.296 5.132-.31 7.81-.259 22.492.021 44.557-.024 67.034-.066 4.149-.049 7.886-.099 11.624-.149"
|
||||
id="path1"
|
||||
style="fill:#fea265;fill-opacity:1" />
|
||||
<path
|
||||
fill="#ff44c4"
|
||||
d="M168.124 257.114v-9.301c-2.407 0-4.67-.001-6.933.001-44.522.025-89.044.01-133.566.146-3.858.012-5.358-1.03-4.744-5 .683-4.418 1.254-8.885 1.393-13.346.106-3.386 1.603-4.005 4.608-4.012 45.9-.11 91.801-.344 137.702-.452 3.865-.009 6.842-.344 6.032-5.831H25.546c1.39-6.599 2.354-12.681 4.138-18.513.413-1.35 3.628-2.615 5.555-2.618 47.458-.084 94.917-.01 142.375.109 3.275.008 5.855-.38 6.138-5.045H31.147c3.43-9.311 6.348-17.71 9.74-25.913.483-1.166 3.265-1.942 4.987-1.95 24.582-.124 49.165-.21 73.745.014 5.283.048 8.073-2.583 11.128-7.248h-86.45c4.35-7.835 8.002-14.753 12.077-21.412.67-1.096 3.197-1.473 4.868-1.478 30.611-.083 61.226-.291 91.832.113 7.185.094 12.719-2.619 18.321-6.774H61.385c2.592-3.805 4.355-6.546 6.268-9.178 8.88-12.216 8.896-12.209 24.069-12.21q60.991-.002 121.982-.001h5.885c-3.968-4.128-7.007-6.966-12.321-6.944-38.959.165-77.92.09-116.88.09H85.03l-.741-1.31c3.832-3.852 7.564-7.81 11.514-11.537 11.456-10.807 11.495-10.996 27.246-10.717 23.209.411 45.103 6.8 66.234 15.885 5.657 2.432 10.56 3.715 16.604.944 4.726-2.166 10.217-2.664 16.034-3.521-4.411 8.139.887 12.041 5.52 16.414 5.73 5.408 11.223 11.07 16.751 16.688 1.165 1.185 2.035 2.659 3.685 4.857-2.256.128-3.665.274-5.075.275-9.739.008-19.489.286-29.214-.085-28.045-1.07-51.592 9.244-71.602 28.209-7.152 6.778-13.525 14.379-20.212 21.643-.683.742-1.079 1.748-2.209 3.632 33.94-11.926 65.876-27.377 101.589-28.637l.859 1.395c-4.228 4.943-8.188 10.148-12.73 14.783-18.091 18.461-28.905 40.673-33.892 65.773-1.402 7.057-1.306 14.412-2.504 21.941-1.992.248-3.377.184-4.762.121m19.995-91.726c3.865.004 7.763-.315 11.585.09 5.562.589 8.92-2.178 12.525-7.52-9.134 2.152-17.144 4.039-25.139 5.988-.008.002.209.928 1.029 1.442"
|
||||
id="path2"
|
||||
style="fill:#ff7f2a;fill-opacity:1" />
|
||||
<path
|
||||
fill="#bf43f5"
|
||||
d="M167.719 257.136c1.79.041 3.175.105 4.979.185.569 3.091.719 6.166.87 9.24l1.623.577c2.3-3.392 4.599-6.785 7.467-10.458 2.456-.382 4.343-.483 6.573-.598 1.405-.133 2.466-.342 3.53-.356 12.08-.162 24.16-.295 36.693-.368 3.463.111 6.473.155 9.484.2-1.12 6.677-2.306 13.345-3.347 20.035-4.715 30.285-5.404 60.697-3.186 91.651-2.469.746-4.517 1.048-6.565 1.35-56.117-.025-112.236-.135-168.353.061-5.334.018-8.298-1.559-10.185-6.415-1.874-4.824-4.295-9.436-6.907-15.069h17.147c53.955 0 107.909.013 161.863-.044 2.026-.002 4.864 1.218 5.327-2.599.469-3.862-1.271-4.625-4.781-4.616-38.803.103-77.607.061-116.411.068-20.405.003-40.811-.065-61.215.125-3.545.033-5.36-1.044-6.324-4.419-1.642-5.75-3.577-11.416-5.879-18.659 65.363-.259 129.677-.027 194.424-.144l.372-6.533c-1.901-.099-3.398-.244-4.896-.245-58.283-.032-116.565-.056-174.848-.06-4.48 0-8.964.279-13.439.163-1.138-.029-3.078-.897-3.247-1.681-1.676-7.766-3.056-15.595-4.624-23.961 8.433 0 15.808-.031 23.182.02 1.998.014 3.995.4 5.992.401 55.964.023 111.928.02 167.892.017 6.075 0 6.307-.256 6.266-7.198H23.431c-.284-6.643-.72-12.739-.592-18.823.016-.786 2.94-2.144 4.53-2.165 15.149-.197 30.3-.177 45.45-.191 17.623-.016 35.248-.072 52.871.018 13.876.07 27.75.335 42.029.491"
|
||||
id="path3"
|
||||
style="fill:#fea265;fill-opacity:1" />
|
||||
<path
|
||||
fill="#985ded"
|
||||
d="M225.868 369.021c2.02-.729 4.068-1.031 6.551-1.351 2.672 17.851 4.169 35.853 7.334 53.556 4.047 22.64 9.785 44.94 18.63 66.298.29.699.498 1.433.742 2.142-25.39 10.452-124.622-22.609-146.929-49.453h122.669c.302-4.81-.85-6.915-5.85-6.891-39.883.193-79.768.055-119.652.182-4.441.014-8.036-.845-11.208-4.17-6.87-7.203-13.99-14.166-21.008-21.228l.69-1.319H229.63l-1.24-7.289h-5.726c-42.821 0-85.642.011-128.464-.013-6.18-.003-12.371-.428-18.537-.173-3.704.154-6.14-1.066-8.104-4.073-4.111-6.292-8.367-12.489-13.256-19.755h171.593c0-2.433 0-4.234-.027-6.463m35.103.062c2.442-.418 4.85-.422 7.641-.431.482 1.987.581 3.979.706 6.499H457.62c-3.126 4.951-5.544 8.946-8.123 12.835-7.64 11.521-7.665 11.508-21.226 11.509l-147.5.003h-5.407c-.602 6.688-.15 7.214 5.824 7.214q73.75.004 147.5.004h6.891c-4.048 4.533-7.454 7.812-10.233 11.556-9.047 12.19-20.575 16.124-35.967 15.542-33.039-1.25-66.159-.384-99.246-.384h-5.45c-.275 5.77.583 6.774 5.594 6.776 34.478.012 68.957.006 103.435.007h5.302l.443 1.274c-3.495 2.62-6.91 5.358-10.5 7.842-23.505 16.265-49.192 27.744-76.893 34.696-3.263.819-6.763 1.374-10.069 1.085-1.874-.164-4.144-1.915-5.266-3.597-9.503-14.258-15.474-30.133-21.055-46.227-7.418-21.394-11.59-43.465-14.702-66.203"
|
||||
id="path4"
|
||||
style="fill:#fe8463;fill-opacity:1" />
|
||||
<path
|
||||
fill="#ff43c4"
|
||||
d="M360.319 254.985c-3.232.395-6.479.407-10.189.359 2.612-14.328 2.295-28.523-1.162-42.703-4.74-19.439-15.808-34.254-32.502-45.039-.639-.413-1.27-.839-1.852-2.137h52.547l.213-1.506c-7.026-2.665-13.978-5.752-21.733-5.819-15.455-.136-30.914.047-46.369-.12-3.092-.034-6.173-1.044-9.202-2.411 18.374-7.385 37.172-6.583 56.061-3.6 19.128 3.021 35.771 12.957 54.614 20.622l-3.662-6.95c1.846-.148 3.252-.359 4.658-.36 21.485-.021 42.97.048 64.454-.082 3.262-.019 5.062.848 6.121 4.101 2.319 7.128 5.012 14.134 7.891 22.129h-127.22c.144 4.537 2.105 5.796 5.897 5.783q43.588-.154 87.175-.028c10.803.031 21.607.309 32.402.688 1.327.047 3.486 1.183 3.763 2.215 1.549 5.774 2.638 11.671 4.021 18.169H361.127c-.548 6.29-.254 6.61 5.207 6.619 38.949.061 77.898.158 116.847.146 2.863-.001 4.211.672 4.397 3.772.304 5.076.956 10.136 1.609 15.183.414 3.199-.637 4.443-4.005 4.328-6.635-.228-13.283-.105-19.926-.106q-49.152-.005-98.304.001c-6.266.001-6.266.007-6.633 6.746"
|
||||
id="path5"
|
||||
style="fill:#ff7f2a;fill-opacity:1" />
|
||||
<path
|
||||
fill="#ff42c3"
|
||||
d="M214.837 27.347c41.832-7.02 81.069-2.12 103 5.453-3.084 7.222-6.153 14.42-9.231 21.612-1.64 3.833-3.549 7.574-4.869 11.513-1.091 3.256-2.992 4.06-6.217 4.042-22.092-.119-44.185-.062-67.454-.062 3.94 4.046 6.873 7.296 12.308 7.245 16.689-.157 33.381-.056 50.072-.055h6.745c-3.661 6.1-6.056 11.84-9.975 16.217-6.139 6.856-13.44 12.659-19.972 19.184-2.516 2.514-3.687 2.416-6.101-.229-9.873-10.813-19.809-21.593-30.196-31.908-11.454-11.376-25.983-16.088-41.742-17.908-14.011-1.618-27.312.965-40.451 5.507a34.7 34.7 0 0 1-9.886 1.86c-7.248.275-14.513.085-21.995-.508 28.501-21.58 60.452-35.31 95.964-41.963m53.746 77.311 3.605-4.283h-11.056c1.525 2.155 2.339 3.846 3.653 4.931.643.531 2.216-.065 3.798-.648"
|
||||
id="path6"
|
||||
style="fill:#63915a;fill-opacity:1" />
|
||||
<path
|
||||
fill="#ff44c4"
|
||||
d="M377.152 144.943c-12.092-9.869-26.287-13.386-40.919-15.513-16.956-2.465-33.583-1.51-50.074 5.653.952-1.742 1.722-3.617 2.884-5.206 15.646-21.395 34.923-38.76 58.357-51.192 3.545-1.881 8.365-1.78 12.607-1.849 13.445-.22 26.898-.165 40.345-.002 2.017.024 4.514.652 5.947 1.938 7.01 6.292 13.741 12.894 20.545 19.414.386.37.542.98 1.172 2.175h-5.468c-30.918 0-61.837.116-92.753-.114-5.213-.039-8.095 2.606-11.98 6.969h6.295c35.092-.001 70.184.065 105.275-.091 4.005-.018 6.738 1.186 8.994 4.365 3.728 5.252 7.642 10.372 12.234 16.566h-90.207l-.153 1.134c6.582 2.444 12.387 6.339 20.175 6.183 23.179-.463 46.374-.087 69.561-.245 3.754-.026 6.054 1.177 7.725 4.48 2.979 5.887 6.211 11.646 9.711 18.154-2.304.15-3.753.325-5.203.326-22.415.017-44.831-.042-67.246.074-3.19.016-5.54-.802-7.705-3.211-3.093-3.441-6.561-6.545-10.119-10.008M267.626 255.455a972 972 0 0 1-7.18.028c2.056-26.604 4.319-53.217 12.215-78.952 1.17-3.813 2.458-5.484 6.241-2.773 3.757 2.69 8.105 4.741 11.346 7.934 22.076 21.743 42.878 44.52 56.347 73.308-3.623.356-7.36.406-11.542.092-1.222-4.579-3.365-6.359-8.021-6.284-17.697.286-35.402.122-53.104.126h-6.302zm2.176-30.951h44.834l.519-1.678c-1.826-.906-3.637-2.562-5.479-2.597-12.466-.234-24.937-.104-37.407-.144-2.831-.009-4.108.992-2.467 4.419m10.378-31.312c-3.285-.223-6.209-.007-5.651 4.577h16.788c-2.211-5.078-6.298-4.716-11.137-4.577m-41.043 62.082c-3.21.24-6.22.195-9.566-.301-.214-2.054-.093-3.657.08-5.95-11.47 0-22.329-.006-33.188.003-5.917.004-6.716.761-7.574 7.07-1.888.101-3.775.202-6.063.302 4.551-9.736 8.81-19.906 14.604-29.11 10.304-16.372 21.983-31.76 37.616-43.656 7.217-5.492 15.096-9.592 23.847-11.977 1.13-.308 2.333-.345 4.833-.691-10.43 27.968-18.906 55.583-24.589 84.31m-25.72-36.778c-4.442-.439-5.459 2.888-7.32 6.624 9.882 0 19.096-.135 28.302.061 4.188.089 5.338-1.767 4.777-6.598-8.393 0-16.684 0-25.759-.087m33.855-22.914c-1.112-.66-2.178-1.777-3.343-1.89-2.903-.28-5.86.032-8.779-.147-3.761-.229-5.839 1.749-7.932 5.507 6.203 0 11.685.126 17.15-.12.95-.043 1.829-1.662 2.904-3.35m96.491-154.518c17.922 7.326 34.473 16.35 49.706 28.527-30.671-2.027-59.888 3.402-88.579 15.8.855-4.673 2.395-7.566 6.408-8.806 5.86-1.811 11.674-3.77 17.472-6.488h-16.478c5.867-11.505 11.161-22.041 16.71-32.442.395-.74 2.807-.966 4.029-.597 3.534 1.07 6.939 2.564 10.732 4.006"
|
||||
id="path7"
|
||||
style="fill:#ff7f2a;fill-opacity:1" />
|
||||
<path
|
||||
style="fill:#000000;fill-opacity:1"
|
||||
d="m 130,278.4 c 69.2,16 30.4,65.6 30.4,65.6 0,0 -14.8,-92.4 28.8,-46.4 43.6,46 -59.2,-19.2 -59.2,-19.2 z"
|
||||
id="path8" />
|
||||
<path
|
||||
style="fill:#000000;fill-opacity:1"
|
||||
d="m 76.08469,213.54625 c 17.819091,7.91959 0.282842,24.32447 22.061731,7.91959 21.778889,-16.40487 53.174429,-48.3661 21.778889,-16.40487 -31.395541,31.96122 -43.84062,8.48528 -43.84062,8.48528 z"
|
||||
id="path9" />
|
||||
<path
|
||||
style="fill:#000000;fill-opacity:1"
|
||||
d="m 310.4,292.8 c 36,20.8 16,50.4 40,18.8 24,-31.6 46.8,-57.2 14.8,-33.6 -32,23.6 -54.8,14.8 -54.8,14.8 z"
|
||||
id="path10" />
|
||||
<path
|
||||
style="fill:#000000;fill-opacity:1"
|
||||
d="m 380.8,197.2 c 41.6,30.4 18.4,62.8 42,30.4 23.6,-32.4 10,-68.8 23.6,-32.4 13.6,36.4 58,64.4 13.6,36.4 -44.4,-28 -79.2,-34.4 -79.2,-34.4 z"
|
||||
id="path11" />
|
||||
<path
|
||||
style="fill:#000000;fill-opacity:1"
|
||||
d="M 99.6,145.2 C 146,120.4 170.4,80.8 146,120.4 121.6,160 121.2,187.6 120.4,159.6 119.6,131.6 99.6,145.2 99.6,145.2 Z"
|
||||
id="path12" />
|
||||
<path
|
||||
style="fill:#000000;fill-opacity:1"
|
||||
d="m 166.4,395.2 c 36.4,-34.4 26.8,-68.4 35.2,-32.8 8.4,35.6 -36,4.8 -36,4.8 z"
|
||||
id="path13" />
|
||||
<path
|
||||
style="fill:#000000;fill-opacity:1"
|
||||
d="M 325.6,419.2 C 390,378.4 402,310 388.8,379.6 c -13.2,69.6 7.6,39.6 -15.6,67.6 -23.2,28 81.6,-113.2 50,-90.8 -31.6,22.4 -97.6,62.8 -97.6,62.8 z"
|
||||
id="path14" />
|
||||
<path
|
||||
style="fill:#000000;fill-opacity:1"
|
||||
d="m 298.11622,232.77955 c 9.33381,30.82986 27.43574,16.68772 9.05097,29.41564 -18.38478,12.72793 -23.19311,44.12347 -17.53625,11.59656 5.65685,-32.52692 8.48528,-41.0122 8.48528,-41.0122 z"
|
||||
id="path15" />
|
||||
<path
|
||||
style="fill:#000000;fill-opacity:1"
|
||||
d="M 54.305801,291.32799 C 110.5915,257.10403 141.42136,225.1428 108.89444,258.51824 76.367532,291.89368 96.166522,307.73287 78.913117,291.04515 61.659711,274.35743 54.022958,289.06525 54.022958,289.06525 l 53.174432,-31.1127 z"
|
||||
id="path16" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 13 KiB |
110
dist/icon_variations/halloween2025_named.svg
vendored
Normal file
|
After Width: | Height: | Size: 39 KiB |
BIN
dist/qt_themes/default/icons/256x256/eden.png
vendored
|
Before Width: | Height: | Size: 4.9 KiB After Width: | Height: | Size: 27 KiB |
BIN
dist/qt_themes/default/icons/256x256/eden_named.png
vendored
|
Before Width: | Height: | Size: 2.7 MiB After Width: | Height: | Size: 38 KiB |
BIN
dist/yuzu.bmp
vendored
|
Before Width: | Height: | Size: 256 KiB After Width: | Height: | Size: 256 KiB |
@@ -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
@@ -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);
|
||||
```
|
||||
@@ -1,5 +1,23 @@
|
||||
# Debug Guidelines
|
||||
|
||||
## Issue reports
|
||||
|
||||
When reporting issues or finding bugs, we often need backtraces, debug logs, or both in order to track down the issue.
|
||||
|
||||
### Graphics Debugging
|
||||
|
||||
If your bug is related to a graphical issue--e.g. mismatched colors, vertex explosions, flickering, etc.--then you are required to include graphical debugging logs in your issue reports.
|
||||
|
||||
Graphics Debugging is found in General -> Debug on desktop, and Advanced Settings -> Debug on Android. Android users are all set; however, desktop users may need to install the Vulkan Validation Layers:
|
||||
- Windows: Install the [Vulkan SDK](https://vulkan.lunarg.com/sdk/home)
|
||||
- Linux, BSD, etc: Install `vulkan-validation-layers`, `vulkan-layers`, or similar from your package manager. It should be located in e.g. `/usr/lib64/libVkLayer_khronos_validation.so`
|
||||
|
||||
Once Graphics Debugging is enabled, run the problematic game again and continue. Note that the game may run extremely slow on weak hardware.
|
||||
|
||||
### Debug Logs
|
||||
|
||||
Debug logs can be found in General -> Debug -> Open Log Location on desktop, and Share Debug Logs on Android. This MUST be included in all bug reports, except for certain UI bugs--but we still highly recommend them even for UI bugs.
|
||||
|
||||
## Debugging (host code)
|
||||
|
||||
Ignoring SIGSEGV when debugging in host:
|
||||
@@ -47,10 +65,10 @@ Renderdoc is a free, cross platform, multi-graphics API debugger. It is an inval
|
||||
|
||||
Before using renderdoc to diagnose issues, it is always good to make sure there are no validation errors. Any errors means the behavior of the application is undefined. That said, renderdoc can help debug validation errors if you do have them.
|
||||
|
||||
When debugging a black screen, there are many ways the application could have setup Vulkan wrong.
|
||||
When debugging a black screen, there are many ways the application could have setup Vulkan wrong.
|
||||
Here is a short checklist of items to look at to make sure are appropriate:
|
||||
* Draw call counts are correct (aka not zero, or if rendering many triangles, not 3)
|
||||
* Vertex buffers are bound
|
||||
* Vertex buffers are bound
|
||||
* vertex attributes are correct - Make sure the size & offset of each attribute matches what should it should be
|
||||
* Any bound push constants and descriptors have the right data - including:
|
||||
* Matrices have correct values - double check the model, view, & projection matrices are uploaded correctly
|
||||
@@ -63,6 +81,6 @@ Here is a short checklist of items to look at to make sure are appropriate:
|
||||
* Blend state is correct
|
||||
* Depth state is correct - typically enabled with Function set to Less than or Equal
|
||||
* Swapchain images are bound when rendering to the swapchain
|
||||
* Image being rendered to is the same as the one being presented when rendering to the swapchain
|
||||
* Image being rendered to is the same as the one being presented when rendering to the swapchain
|
||||
|
||||
Alternatively, a [RenderDoc Extension](https://github.com/baldurk/renderdoc-contrib/tree/main/baldurk/whereismydraw) ([Archive](https://web.archive.org/web/20250000000000*/https://github.com/baldurk/renderdoc-contrib/tree/main/baldurk/whereismydraw)) exists which automates doing a lot of these manual steps.
|
||||
|
||||
77
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
|
||||
@@ -70,9 +72,6 @@ Certain other dependencies will be fetched by CPM regardless. System packages *c
|
||||
|
||||
On amd64:
|
||||
* [xbyak](https://github.com/herumi/xbyak) - 7.22 or earlier is recommended
|
||||
* [zycore](https://github.com/zyantific/zycore-c)
|
||||
* [zydis](https://github.com/zyantific/zydis) 4+
|
||||
* Note: zydis and zycore-c MUST match. Using one as a system dependency and the other as a bundled dependency WILL break things
|
||||
|
||||
On aarch64 OR if `DYNARMIC_TESTS` is on:
|
||||
* [oaknut](https://github.com/merryhime/oaknut) 2.0.1+
|
||||
@@ -84,13 +83,15 @@ On riscv64:
|
||||
|
||||
These are commands to install all necessary dependencies on various Linux and BSD distributions, as well as macOS. Always review what you're running before you hit Enter!
|
||||
|
||||
Notes for writers: Include build tools as well, assume user has NOTHING installed (i.e a fresh install) but that they have updated beforehand so no `upgrade && update` or equivalent should be mentioned - except for rolling release systems like Arch.
|
||||
|
||||
Click on the arrows to expand.
|
||||
|
||||
<details>
|
||||
<summary>Arch Linux</summary>
|
||||
|
||||
```sh
|
||||
sudo pacman -Syu --needed base-devel boost catch2 cmake enet ffmpeg fmt git glslang libzip lz4 mbedtls ninja nlohmann-json openssl opus qt6-base qt6-multimedia sdl2 zlib zstd zip unzip zydis zycore vulkan-headers vulkan-utility-libraries libusb spirv-tools spirv-headers
|
||||
sudo pacman -Syu --needed base-devel boost catch2 cmake enet ffmpeg fmt git glslang libzip lz4 mbedtls ninja nlohmann-json openssl opus qt6-base qt6-multimedia sdl2 zlib zstd zip unzip vulkan-headers vulkan-utility-libraries libusb spirv-tools spirv-headers
|
||||
```
|
||||
|
||||
* Building with QT Web Engine requires `qt6-webengine` as well.
|
||||
@@ -102,7 +103,7 @@ sudo pacman -Syu --needed base-devel boost catch2 cmake enet ffmpeg fmt git glsl
|
||||
<summary>Ubuntu, Debian, Mint Linux</summary>
|
||||
|
||||
```sh
|
||||
sudo apt-get install autoconf cmake g++ gcc git glslang-tools libglu1-mesa-dev libhidapi-dev libpulse-dev libtool libudev-dev libxcb-icccm4 libxcb-image0 libxcb-keysyms1 libxcb-render-util0 libxcb-xinerama0 libxcb-xkb1 libxext-dev libxkbcommon-x11-0 mesa-common-dev nasm ninja-build qt6-base-private-dev libmbedtls-dev catch2 libfmt-dev liblz4-dev nlohmann-json3-dev libzstd-dev libssl-dev libavfilter-dev libavcodec-dev libswscale-dev pkg-config zlib1g-dev libva-dev libvdpau-dev qt6-tools-dev libzydis-dev zydis-tools libzycore-dev libvulkan-dev spirv-tools spirv-headers libusb-1.0-0-dev libxbyak-dev libboost-dev libboost-fiber-dev libboost-context-dev libsdl2-dev libopus-dev libasound2t64 vulkan-utility-libraries-dev
|
||||
sudo apt-get install autoconf cmake g++ gcc git glslang-tools libglu1-mesa-dev libhidapi-dev libpulse-dev libtool libudev-dev libxcb-icccm4 libxcb-image0 libxcb-keysyms1 libxcb-render-util0 libxcb-xinerama0 libxcb-xkb1 libxext-dev libxkbcommon-x11-0 mesa-common-dev nasm ninja-build qt6-base-private-dev libmbedtls-dev catch2 libfmt-dev liblz4-dev nlohmann-json3-dev libzstd-dev libssl-dev libavfilter-dev libavcodec-dev libswscale-dev pkg-config zlib1g-dev libva-dev libvdpau-dev qt6-tools-dev libvulkan-dev spirv-tools spirv-headers libusb-1.0-0-dev libxbyak-dev libboost-dev libboost-fiber-dev libboost-context-dev libsdl2-dev libopus-dev libasound2t64 vulkan-utility-libraries-dev
|
||||
```
|
||||
|
||||
* Ubuntu 22.04, Linux Mint 20, or Debian 12 or later is required.
|
||||
@@ -134,6 +135,35 @@ sudo dnf install qt6-qtbase-private-devel
|
||||
* Fedora 36+ users with GCC 12 need Clang and should configure CMake with: `cmake -DCMAKE_CXX_COMPILER=clang++ -DCMAKE_C_COMPILER=clang -B build`
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>Alpine Linux</summary>
|
||||
|
||||
First, enable the community repository; [see here](https://wiki.alpinelinux.org/wiki/Repositories#Enabling_the_community_repository).
|
||||
```sh
|
||||
# Enable the community repository
|
||||
setup-apkrepos -c
|
||||
# Install
|
||||
apk add g++ git cmake make mbedtls-dev mbedtls-static mesa-dev qt6-qtbase-dev qt6-qtbase-private-dev libquazip1-qt6 ffmpeg-dev libusb-dev libtool boost-dev sdl2-dev zstd-dev vulkan-utility-libraries spirv-tools-dev openssl-dev nlohmann-json lz4-dev opus-dev jq patch
|
||||
```
|
||||
|
||||
`mbedtls-static` has to be specified otherwise `libeverest.a` and `libp256m.a` will fail to be found.
|
||||
|
||||
</details>
|
||||
<summary>Void Linux</summary>
|
||||
|
||||
```sh
|
||||
xbps-install -Su git make cmake clang pkg-config patch mbedtls-devel SPIRV-Tools-devel SPIRV-Headers lz4 liblz4-devel boost-devel ffmpeg6-devel catch2 Vulkan-Utility-Libraries Vulkan-Headers glslang openssl-devel SDL2-devel quazip-qt6-devel qt6-base-devel qt6-qt5compat-devel fmt-devel json-c++ libenet-devel libusb-devel
|
||||
```
|
||||
|
||||
Yes, `nlohmann-json` is just named `json-c++`. Why?
|
||||
|
||||
</details>
|
||||
<details>
|
||||
<summary>NixOS</summary>
|
||||
|
||||
A convenience script is provided on the root of this project [shell.nix](../shell.nix). Run the usual `nix-shell`.
|
||||
|
||||
</details>
|
||||
<details>
|
||||
<summary>macOS</summary>
|
||||
|
||||
@@ -156,7 +186,7 @@ brew install molten-vk vulkan-loader
|
||||
<details>
|
||||
<summary>FreeBSD</summary>
|
||||
|
||||
As root run: `pkg install devel/cmake devel/sdl20 devel/boost-libs devel/catch2 devel/libfmt devel/nlohmann-json devel/ninja devel/nasm devel/autoconf devel/pkgconf devel/qt6-base devel/simpleini net/enet multimedia/ffnvcodec-headers multimedia/ffmpeg audio/opus archivers/liblz4 lang/gcc12 graphics/glslang graphics/vulkan-utility-libraries graphics/spirv-tools www/cpp-httplib devel/jwt-cpp devel/unordered-dense devel/zydis`
|
||||
As root run: `pkg install devel/cmake devel/sdl20 devel/boost-libs devel/catch2 devel/libfmt devel/nlohmann-json devel/ninja devel/nasm devel/autoconf devel/pkgconf devel/qt6-base devel/simpleini net/enet multimedia/ffnvcodec-headers multimedia/ffmpeg audio/opus archivers/liblz4 lang/gcc12 graphics/glslang graphics/vulkan-utility-libraries graphics/spirv-tools www/cpp-httplib devel/jwt-cpp devel/unordered-dense`
|
||||
|
||||
If using FreeBSD 12 or prior, use `devel/pkg-config` instead.
|
||||
|
||||
@@ -171,7 +201,6 @@ For NetBSD +10.1: `pkgin install git cmake boost fmtlib SDL2 catch2 libjwt spirv
|
||||
[Caveats](./Caveats.md#netbsd).
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>OpenBSD</summary>
|
||||
|
||||
@@ -183,7 +212,6 @@ pkg_add cmake nasm git boost unzip--iconv autoconf-2.72p0 bash ffmpeg glslang gm
|
||||
[Caveats](./Caveats.md#openbsd).
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>Solaris / OpenIndiana</summary>
|
||||
|
||||
@@ -199,24 +227,37 @@ Then install the libraries: `sudo pkg install qt6 boost glslang libzip library/l
|
||||
[Caveats](./Caveats.md#solaris).
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>MSYS2</summary>
|
||||
|
||||
* Open the `MSYS2 MinGW 64-bit` shell (`mingw64.exe`)
|
||||
* Download and install all dependencies using:
|
||||
* `pacman -Syu git make mingw-w64-x86_64-SDL2 mingw-w64-x86_64-cmake mingw-w64-x86_64-python-pip mingw-w64-x86_64-qt6 mingw-w64-x86_64-toolchain autoconf libtool automake-wrapper`
|
||||
* Add MinGW binaries to the PATH:
|
||||
* `echo 'PATH=/mingw64/bin:$PATH' >> ~/.bashrc`
|
||||
* Add VulkanSDK to the PATH:
|
||||
* `echo 'PATH=$(readlink -e /c/VulkanSDK/*/Bin/):$PATH' >> ~/.bashrc`
|
||||
</details>
|
||||
* Download and install all dependencies:
|
||||
```
|
||||
BASE="git make autoconf libtool automake-wrapper jq patch"
|
||||
MINGW="SDL2 cmake python-pip qt6-base toolchain ffmpeg boost catch fmt lz4 nlohmann-json openssl zlib zstd enet opus mbedtls vulkan-devel libusb vulkan-memory-allocator unordered_dense clang ccache"
|
||||
|
||||
packages="$BASE"
|
||||
for pkg in $MINGW; do
|
||||
packages="$packages mingw-w64-x86_64-$pkg"
|
||||
done
|
||||
|
||||
pacman -Syu --needed --noconfirm $packages
|
||||
```
|
||||
* Notes:
|
||||
- Using `qt6-static` is possible but currently untested.
|
||||
- Other environments are entirely untested, but should theoretically work provided you install all the necessary packages.
|
||||
- Clang is installed as it generally works better here. You can compile with GCC just fine, however.
|
||||
- Add `qt-creator` to the `MINGW` variable to install Qt Creator. You can then create a Start Menu shortcut to the MinGW Qt Creator by running `powershell "\$s=(New-Object -COM WScript.Shell).CreateShortcut('C:\\ProgramData\\Microsoft\\Windows\\Start Menu\\Programs\\Qt Creator.lnk');\$s.TargetPath='C:\\msys64\\mingw64\\bin\\qtcreator.exe';\$s.Save()"` in Git Bash or MSYS2.
|
||||
* Add MinGW binaries to the PATH if they aren't already:
|
||||
* `echo 'PATH=/mingw64/bin:$PATH' >> ~/.bashrc`
|
||||
* or `echo 'PATH=/mingw64/bin:$PATH' >> ~/.zshrc`
|
||||
|
||||
</details>
|
||||
<details>
|
||||
<summary>HaikuOS</summary>
|
||||
|
||||
```sh
|
||||
pkgman install git cmake libfmt_devel nlohmann_json lz4_devel opus_devel boost1.89_devel vulkan_devel qt6_base_devel libsdl2_devel ffmpeg7_devel libx11_devel enet_devel catch2_devel quazip1_qt6_devel qt6_5compat_devel zydis_devel glslang
|
||||
pkgman install git cmake patch libfmt_devel nlohmann_json lz4_devel opus_devel boost1.89_devel vulkan_devel qt6_base_devel libsdl2_devel ffmpeg7_devel libx11_devel enet_devel catch2_devel quazip1_qt6_devel qt6_5compat_devel libusb1_devel libz_devel glslang mbedtls3
|
||||
```
|
||||
|
||||
[Caveats](./Caveats.md#haikuos).
|
||||
|
||||
@@ -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,8 +5,9 @@ This contains documentation created by developers. This contains build instructi
|
||||
- **[General Build Instructions](Build.md)**
|
||||
- **[Cross Compiling](CrossCompile.md)**
|
||||
- **[Development Guidelines](Development.md)**
|
||||
- **[Coding guidelines](Coding.md)**
|
||||
- **[Dependencies](Deps.md)**
|
||||
- **[Debug Guidelines](./Debug.md)**
|
||||
- **[CPM - CMake Package Manager](CPMUtil.md)**
|
||||
- **[Platform-Specific Caveats](Caveats.md)**
|
||||
- **[User Handbook](User.md)**
|
||||
- **[User Handbook](./user)**
|
||||
|
||||
12
docs/User.md
@@ -1,12 +0,0 @@
|
||||
# User Handbook
|
||||
|
||||
The "FAQ".
|
||||
|
||||
This handbook is primarily aimed at the end-user - baking useful knowledge for enhancing their emulation experience.
|
||||
|
||||
- **[The Basics](user/Basics.md)**
|
||||
- **[Audio](user/Audio.md)**
|
||||
- **[Graphics](user/Graphics.md)**
|
||||
- **[Platforms and Architectures](user/Architectures.md)**
|
||||
- **[Testing](user/Testing.md)**
|
||||
- **[Data, savefiles and storage](user/Storage.md)**
|
||||
@@ -47,6 +47,26 @@ Various graphical filters exist - each of them aimed at a specific target/image
|
||||
|
||||
While stock shaders offer a basic subset of options for most users, programs such as [ReShade](https://github.com/crosire/reshade) offer a more flexible experience. In addition to that users can also seek out modifications (mods) for enhancing visual experience (60 FPS mods, HDR, etc).
|
||||
|
||||
**Installing ReShade (Windows)**
|
||||
1. [Download ReShade](https://reshade.me/#download) with add-on support.
|
||||
- [ReShade Setup 6.6.2 (Windows 64-bit) with add-on support](https://reshade.me/downloads/ReShade_Setup_6.6.2_Addon.exe)
|
||||
- SHA512 checksum: `1f09a73afa160480c13ffdd73cc04b1dc82943dddea58ad3bb9375f26b48c2787d0a85197e46b6fce32a4fd3472465520a3355ed3436241e17fba7ebaff7ffec`.
|
||||
2. Open ReShade and hit browse, then the folder where `eden.exe` is at, hit open, then hit next.
|
||||
3. Select Vulkan as the rendering API, hit next.
|
||||
4. In "Select effects to install" screen: hit next don't change anything.
|
||||
5. In "Select add on" screen: click the box for `Shader Toggler by Otis` ([GitHub](https://github.com/FransBouma/ShaderToggler)) and hit next.
|
||||
|
||||
**Using the Shader Toggler**
|
||||
1. Launch a game, you must see a ReShade pop up afterwards.
|
||||
2. Progress to a point with a flickering shader.
|
||||
3. Hit the Home key on keyboard (or change binds if you don't have one).
|
||||
4. Navigate to the add on tab at the top of the ReShade menu.
|
||||
5. At the bottom where Shader Toggler is at open the drop down and max out the slider that says "# of fames collected" then select change shaders while staring at the flickering shader.
|
||||
6. When the Shader Toggler finishes collecting frames in the top left hit Numpad 2 till it turns off the flickering lines.
|
||||
7. Hit Numpad 3 to add it the group of shaders to turn off and hit done and save all toggle groups.
|
||||
8. Hit the edit button and select "active at startup" for the shader to be turned off on every game launch.
|
||||
9. Caps lock to manually turn on and off the shader (default key you can change it with the previous edit button)
|
||||
|
||||
## Driver specifics
|
||||
|
||||
### Mesa environment variable hacks
|
||||
|
||||
31
docs/user/Orphaned.md
Normal file
@@ -0,0 +1,31 @@
|
||||
# Orphaned Profiles
|
||||
|
||||
A bug present in earlier versions of Eden and Yuzu caused some profiles to be read from the incorrect location if your NAND directory was set to anything other than the default. This bug was fixed in Eden v0.0.4-rc1, but it can be destructive if you're not careful.
|
||||
|
||||
## What are they?
|
||||
|
||||
Orphaned profiles refer to emulated user profiles that may or may not contain valid save data, but are not referenced by the internal profile map. This means the save data is effectively inaccessible, and should be fixed in order to access your save data.
|
||||
|
||||
## How do I fix it?
|
||||
|
||||
There are lots of different cases of varying complexity.
|
||||
|
||||
Remember to ALWAYS back up your saves!
|
||||
|
||||
### Simple Copy
|
||||
|
||||
Sometimes, a simple copying is all you need. For example, if the orphaned profile folder contains game saves, BUT the good profile is completely empty, then you can simply remove the empty folder and rename the orphaned profile to the same name as the good one.
|
||||
|
||||
### Combination
|
||||
|
||||
In more extreme cases, game saves can be strewn all throughout different profiles. In this case, you must look at each profile individually.
|
||||
|
||||
Typically, one folder will clearly have more recent/numerous save data, in which case you can remove all the other profile folders and follow the same procedure as the simple copy.
|
||||
|
||||
If multiple profile folders contain valid data, the recommended approach is to copy the contents of one folder into the other. There are likely to be file conflicts, the resolution of which is up to you.
|
||||
|
||||
An alternate method for dealing with multiple valid profiles is to go into System -> Profiles, and create a new profile. From there, you can copy the contents of each previously-orphaned profile into a new profile.
|
||||
|
||||
### Edge Cases
|
||||
|
||||
There are way too many edge cases to cover here, but in general, make backups! You can never go wrong if you always have a backup of your saves.
|
||||
13
docs/user/README.md
Normal file
@@ -0,0 +1,13 @@
|
||||
# User Handbook
|
||||
|
||||
The "FAQ".
|
||||
|
||||
This handbook is primarily aimed at the end-user - baking useful knowledge for enhancing their emulation experience.
|
||||
|
||||
- **[The Basics](Basics.md)**
|
||||
- **[Audio](Audio.md)**
|
||||
- **[Graphics](Graphics.md)**
|
||||
- **[Platforms and Architectures](Architectures.md)**
|
||||
- **[Testing](Testing.md)**
|
||||
- **[Data, savefiles and storage](Storage.md)**
|
||||
- **[Orphaned Profiles](Orphaned.md)**
|
||||
@@ -27,6 +27,8 @@ When you report your results:
|
||||
2. "Tested on based master — issue already present. Good result for PR, not a regression."
|
||||
```
|
||||
|
||||
This approach helps maintain clarity and accountability in the testing process and ensures regressions are caught and addressed efficiently. If the behavior seems normal for a certain game/feature then it may not be always required to check against the based master.
|
||||
This approach helps maintain clarity and accountability in the testing process and ensures regressions are caught and addressed efficiently. If the behavior seems normal for a certain game/feature then it may not be always required to check against the based master.
|
||||
|
||||
If a master build for the PR' based master does not exist. It will be helpful to just test past and future builds nearby. That would help with gathering more information about the problem.
|
||||
|
||||
**Always include [debugging info](../Debug.md) as needed**.
|
||||
8
externals/CMakeLists.txt
vendored
@@ -219,7 +219,7 @@ if (ENABLE_WEB_SERVICE OR ENABLE_UPDATE_CHECKER)
|
||||
endif()
|
||||
|
||||
# cpp-jwt
|
||||
if (ENABLE_WEB_SERVICE)
|
||||
if (ENABLE_WEB_SERVICE OR ENABLE_UPDATE_CHECKER)
|
||||
AddJsonPackage(cpp-jwt)
|
||||
endif()
|
||||
|
||||
@@ -399,9 +399,3 @@ if (ANDROID)
|
||||
|
||||
add_library(oboe::oboe ALIAS oboe)
|
||||
endif()
|
||||
|
||||
# sse2neon
|
||||
if (ARCHITECTURE_arm64 AND NOT TARGET sse2neon)
|
||||
add_library(sse2neon INTERFACE)
|
||||
target_include_directories(sse2neon INTERFACE sse2neon)
|
||||
endif()
|
||||
|
||||
5
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",
|
||||
|
||||
9285
externals/sse2neon/sse2neon.h
vendored
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 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) {
|
||||
|
||||
|
Before Width: | Height: | Size: 48 KiB After Width: | Height: | Size: 74 KiB |
@@ -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"
|
||||
|
||||
@@ -374,9 +374,6 @@
|
||||
<string name="firmware_uninstalling">إلغاء تثبيت الفريموير</string>
|
||||
<string name="firmware_uninstalled_success">تم إزالة الفريموير بنجاح</string>
|
||||
|
||||
<string name="error_firmware_missing">الفريموير مطلوب لتشغيل بعض الألعاب واستخدام التطبيقات. يعمل عدن فقط مع الفريموير 19.0.1 أو الأقدم.</string>
|
||||
<string name="error_firmware_corrupted">الفريموير موجود ولكن لا يمكن قراءته. تحقق من مفاتيح التشفير وأعد نسخ الفريموير إذا لزم الأمر.</string>
|
||||
<string name="error_firmware_too_new">الفريموير جديد جداً أو لا يمكن قراءته. يعمل عدن فقط مع الفريموير 19.0.1 أو الأقدم.</string>
|
||||
|
||||
<string name="keys_failed">فشل تثبيت المفاتيح</string>
|
||||
<string name="keys_install_success">تم تثبيت المفاتيح بنجاح</string>
|
||||
|
||||
@@ -322,9 +322,6 @@
|
||||
<string name="firmware_uninstalling">جاري إزالة البرنامج الثابت</string>
|
||||
<string name="firmware_uninstalled_success">تم إزالة البرنامج الثابت بنجاح</string>
|
||||
|
||||
<string name="error_firmware_missing">فریموێر پێویستە بۆ کارپێکردنی هەندێک یاری و بەکارهێنانی بەرنامەکان. ئێدین تەنها لەگەڵ فریموێری 19.0.1 یان کۆنتر کار دەکات.</string>
|
||||
<string name="error_firmware_corrupted">فریموێر هەیە بەڵام ناتوانرێت بخوێنرێتەوە. پشکنین بکە بۆ کلیلی شیکردنەوە و دووبارە فریموێرەکە دامپ بکە ئەگەر پێویست بوو.</string>
|
||||
<string name="error_firmware_too_new">فریموێر زۆر نوێ یان ناتوانرێت بخوێنرێتەوە. ئێدین تەنها لەگەڵ فریموێری 19.0.1 یان کۆنتر کار دەکات.</string>
|
||||
|
||||
<string name="keys_failed">سەرکەوتوو نەبوو لە ناساندنی کلیلی</string>
|
||||
<string name="keys_install_success">کلیلەکان بە سەرکەوتوویی ناسێندران</string>
|
||||
|
||||
@@ -301,9 +301,6 @@
|
||||
<string name="firmware_uninstalling">Odstraňování firmwaru</string>
|
||||
<string name="firmware_uninstalled_success">Firmware byl úspěšně odinstalován</string>
|
||||
|
||||
<string name="error_firmware_missing">Firmware je vyžadován pro spuštění některých her a aplikací. Eden funguje pouze s firmwarem 19.0.1 nebo starším.</string>
|
||||
<string name="error_firmware_corrupted">Firmware byl nahlášen jako přítomný, ale nelze jej přečíst. Zkontrolujte šifrovací klíče a v případě potřeby firmware převezměte znovu.</string>
|
||||
<string name="error_firmware_too_new">Firmware je příliš nový nebo nelze přečíst. Eden funguje pouze s firmwarem 19.0.1 nebo starším.</string>
|
||||
|
||||
<string name="keys_failed">Selhání instalace klíčů</string>
|
||||
<string name="keys_install_success">Klíče úspěšně nainstalovány</string>
|
||||
|
||||
@@ -353,9 +353,6 @@ Wirklich fortfahren?</string>
|
||||
<string name="firmware_uninstalling">Firmware wird deinstalliert...</string>
|
||||
<string name="firmware_uninstalled_success">Firmware erfolgreich deinstalliert</string>
|
||||
|
||||
<string name="error_firmware_missing">Firmware wird benötigt, um bestimmte Spiele und Applikationen zu nutzen. Eden funktioniert nur mit Firmware 19.0.1 oder älter.</string>
|
||||
<string name="error_firmware_corrupted">Firmware wurde als vorhanden gemeldet, konnte aber nicht gelesen werden. Überprüfen Sie die Entschlüsselungsschlüssel und dumpen Sie die Firmware bei Bedarf neu.</string>
|
||||
<string name="error_firmware_too_new">Firmware ist zu neu oder konnte nicht gelesen werden. Eden funktioniert nur mit Firmware 19.0.1 oder älter.</string>
|
||||
|
||||
<string name="keys_failed">Schlüsselinstallation fehlgeschlagen</string>
|
||||
<string name="keys_install_success">Schlüssel erfolgreich installiert</string>
|
||||
|
||||
@@ -368,9 +368,6 @@
|
||||
<string name="firmware_uninstalling">Desinstalando firmware...</string>
|
||||
<string name="firmware_uninstalled_success">Firmware desinstalado correctamente</string>
|
||||
|
||||
<string name="error_firmware_missing">Se requiere firmware para ejecutar ciertos juegos y aplicaciones. Eden solo funciona con firmware 19.0.1 o anterior.</string>
|
||||
<string name="error_firmware_corrupted">El firmware está presente pero no se pudo leer. Verifique las claves de descifrado y vuelva a volcar el firmware si es necesario.</string>
|
||||
<string name="error_firmware_too_new">El firmware es demasiado nuevo o no se pudo leer. Eden solo funciona con firmware 19.0.1 o anterior.</string>
|
||||
|
||||
<string name="keys_failed">Error al instalar claves</string>
|
||||
<string name="keys_install_success">Claves instaladas correctamente</string>
|
||||
|
||||
@@ -387,9 +387,6 @@
|
||||
<string name="verify_installed_content_description">تمام محتوای نصب شده را از نظر خرابی بررسی میکند</string>
|
||||
<string name="keys_missing">کلیدهای رمزگذاری وجود ندارند</string>
|
||||
<string name="keys_missing_description">ثابتافزار و بازیهای فروشگاهی قابل رمزگشایی نیستند</string>
|
||||
<string name="error_firmware_missing">برای اجرای برخی بازیها و برنامهها نیاز به فیرمور است. ادن فقط با فیرمور 19.0.1 یا قدیمیتر کار میکند.</string>
|
||||
<string name="error_firmware_corrupted">فیرمور موجود است اما قابل خواندن نیست. کلیدهای رمزگشایی را بررسی کنید و در صورت نیاز فیرمور را دوباره دامپ کنید.</string>
|
||||
<string name="error_firmware_too_new">فیرمور خیلی جدید است یا قابل خواندن نیست. ادن فقط با فیرمور 19.0.1 یا قدیمیتر کار میکند.</string>
|
||||
<string name="keys_failed">خطا در نصب کلیدها</string>
|
||||
<string name="keys_install_success">کلیدها با موفقیت نصب شدند</string>
|
||||
<string name="error_keys_copy_failed">یک یا چند کلید با خطا در کپی مواجه شد.</string>
|
||||
|
||||
@@ -368,9 +368,6 @@
|
||||
<string name="firmware_uninstalling">Désinstallation du firmware...</string>
|
||||
<string name="firmware_uninstalled_success">Firmware désinstallé avec succès</string>
|
||||
|
||||
<string name="error_firmware_missing">Le firmware est requis pour exécuter certains jeux et applications. Eden ne fonctionne qu\'avec le firmware 19.0.1 ou antérieur.</string>
|
||||
<string name="error_firmware_corrupted">Firmware présent mais illisible. Vérifiez les clés de décryptage et redumpz le firmware si nécessaire.</string>
|
||||
<string name="error_firmware_too_new">Firmware trop récent ou illisible. Eden ne supporte que le firmware 19.0.1 ou antérieur.</string>
|
||||
|
||||
<string name="keys_failed">Échec de l\'installation des clés</string>
|
||||
<string name="keys_install_success">Clés installées avec succès</string>
|
||||
|
||||
@@ -334,9 +334,6 @@
|
||||
<string name="firmware_uninstalling">מסיר קושחה...</string>
|
||||
<string name="firmware_uninstalled_success">הקושחה הוסרה בהצלחה</string>
|
||||
|
||||
<string name="error_firmware_missing">נדרש קושחה להפעלת משחקים ויישומים מסוימים. עדן עובד רק עם קושחה 19.0.1 או ישנה יותר.</string>
|
||||
<string name="error_firmware_corrupted">קושחה קיימת אך לא ניתנת לקריאה. בדוק מפתחות פענוח ושמור קושחה מחדש אם needed.</string>
|
||||
<string name="error_firmware_too_new">קושחה חדשה מדי או לא ניתנת לקריאה. עדן תומך רק בקושחה 19.0.1 או ישנה יותר.</string>
|
||||
|
||||
<string name="keys_failed">התקנת מפתחות נכשלה</string>
|
||||
<string name="keys_install_success">מפתחות הותקנו בהצלחה</string>
|
||||
|
||||
@@ -331,9 +331,6 @@
|
||||
<string name="firmware_uninstalling">Firmware eltávolítása...</string>
|
||||
<string name="firmware_uninstalled_success">A firmware sikeresen eltávolítva</string>
|
||||
|
||||
<string name="error_firmware_missing">A firmware szükséges egyes játékok és alkalmazások futtatásához. Az Eden csak 19.0.1 vagy régebbi firmware-ekkel működik.</string>
|
||||
<string name="error_firmware_corrupted">A firmware jelen van, de nem olvasható. Ellenőrizze a visszafejtési kulcsokat és szükség esetén dumpolja újra a firmware-t.</string>
|
||||
<string name="error_firmware_too_new">A firmware túl új vagy nem olvasható. Az Eden csak a 19.0.1 vagy régebbi firmware-ekkel működik.</string>
|
||||
|
||||
<string name="keys_failed">A kulcsok telepítése sikertelen</string>
|
||||
<string name="keys_install_success">A kulcsok sikeresen telepítve</string>
|
||||
|
||||
@@ -364,9 +364,6 @@
|
||||
<string name="firmware_uninstalling">Mencopot firmware...</string>
|
||||
<string name="firmware_uninstalled_success">Firmware berhasil dicopot</string>
|
||||
|
||||
<string name="error_firmware_missing">Firmware diperlukan untuk menjalankan game dan aplikasi tertentu. Eden hanya bekerja dengan firmware 19.0.1 atau lebih lama.</string>
|
||||
<string name="error_firmware_corrupted">Firmware dilaporkan ada tetapi tidak dapat dibaca. Periksa kunci dekripsi dan dump ulang firmware jika diperlukan.</string>
|
||||
<string name="error_firmware_too_new">Firmware terlalu baru atau tidak dapat dibaca. Eden hanya bekerja dengan firmware 19.0.1 atau lebih lama.</string>
|
||||
|
||||
<string name="keys_failed">Gagal menginstal kunci</string>
|
||||
<string name="keys_install_success">Kunci berhasil diinstal</string>
|
||||
|
||||
@@ -360,9 +360,6 @@
|
||||
<string name="firmware_uninstalling">Disinstallazione firmware...</string>
|
||||
<string name="firmware_uninstalled_success">Firmware disinstallato con successo</string>
|
||||
|
||||
<string name="error_firmware_missing">Il firmware è richiesto per eseguire alcuni giochi e applicazioni. Eden funziona solo con firmware 19.0.1 o precedente.</string>
|
||||
<string name="error_firmware_corrupted">Il firmware è presente ma non può essere letto. Controlla le chiavi di decrittazione e ridumpa il firmware se necessario.</string>
|
||||
<string name="error_firmware_too_new">Il firmware è troppo recente o illeggibile. Eden supporta solo firmware 19.0.1 o precedenti.</string>
|
||||
|
||||
<string name="keys_failed">Installazione chiavi fallita</string>
|
||||
<string name="keys_install_success">Chiavi installate con successo</string>
|
||||
|
||||
@@ -333,9 +333,6 @@
|
||||
<string name="firmware_uninstalling">ファームウェアをアンインストール中...</string>
|
||||
<string name="firmware_uninstalled_success">ファームウェアのアンインストールが成功しました</string>
|
||||
|
||||
<string name="error_firmware_missing">一部のゲームとアプリケーションを実行するにはファームウェアが必要です。Edenは19.0.1以前のファームウェアでのみ動作します。</string>
|
||||
<string name="error_firmware_corrupted">ファームウェアは存在しますが読み取れません。復号キーを確認し、必要に応じてファームウェアを再ダンプしてください。</string>
|
||||
<string name="error_firmware_too_new">ファームウェアが新しすぎるか読み取れません。Edenは19.0.1以前のファームウェアのみに対応しています。</string>
|
||||
|
||||
<string name="keys_failed">キーのインストールに失敗</string>
|
||||
<string name="keys_install_success">キーが正常にインストールされました</string>
|
||||
|
||||
@@ -330,9 +330,6 @@
|
||||
<string name="firmware_uninstalling">펌웨어 제거 중...</string>
|
||||
<string name="firmware_uninstalled_success">펌웨어가 성공적으로 제거되었습니다</string>
|
||||
|
||||
<string name="error_firmware_missing">일부 게임 및 애플리케이션 실행을 위해 펌웨어가 필요합니다. Eden은 19.0.1 이하 버전의 펌웨어에서만 작동합니다.</string>
|
||||
<string name="error_firmware_corrupted">펌웨어가 존재하지만 읽을 수 없습니다. 복호화 키를 확인하고 필요한 경우 펌웨어를 다시 덤프하세요.</string>
|
||||
<string name="error_firmware_too_new">펌웨어가 너무 최신이거나 읽을 수 없습니다. Eden은 19.0.1 이전 버전의 펌웨어에서만 작동합니다.</string>
|
||||
|
||||
<string name="keys_failed">키 설치 실패</string>
|
||||
<string name="keys_install_success">키가 성공적으로 설치됨</string>
|
||||
|
||||
@@ -322,9 +322,6 @@
|
||||
<string name="firmware_uninstalling">Avinstallerer firmware...</string>
|
||||
<string name="firmware_uninstalled_success">Firmware avinstallert</string>
|
||||
|
||||
<string name="error_firmware_missing">Fastvare kreves for å kjøre enkelte spill og applikasjoner. Eden fungerer bare med fastvare 19.0.1 eller eldre.</string>
|
||||
<string name="error_firmware_corrupted">Fastvare er til stede, men kan ikke leses. Sjekk dekrypteringsnøklene og dump fastvaren på nytt om nødvendig.</string>
|
||||
<string name="error_firmware_too_new">Fastvaren er for ny eller kan ikke leses. Eden fungerer bare med fastvare 19.0.1 eller eldre.</string>
|
||||
|
||||
<string name="keys_failed">Kunne ikke installere nøkler</string>
|
||||
<string name="keys_install_success">Nøklene ble installert</string>
|
||||
|
||||
@@ -370,9 +370,6 @@
|
||||
<string name="firmware_uninstalling">Odinstalowywanie firmware...</string>
|
||||
<string name="firmware_uninstalled_success">Firmware odinstalowany pomyślnie</string>
|
||||
|
||||
<string name="error_firmware_missing">Firmware jest wymagany do uruchamiania niektórych gier i aplikacji. Eden działa tylko z firmwarem 19.0.1 lub starszym.</string>
|
||||
<string name="error_firmware_corrupted">Oprogramowanie sprzętowe jest obecne, ale nie można go odczytać. Sprawdź klucze deszyfrujące i w razie potrzeby zrzuć oprogramowanie ponownie.</string>
|
||||
<string name="error_firmware_too_new">Oprogramowanie sprzętowe jest zbyt nowe lub nie można go odczytać. Eden działa tylko z oprogramowaniem sprzętowym w wersji 19.0.1 lub starszej.</string>
|
||||
|
||||
<string name="keys_failed">Nie udało się zainstalować kluczy</string>
|
||||
<string name="keys_install_success">Pomyślnie zainstalowano klucze</string>
|
||||
|
||||
@@ -368,9 +368,6 @@
|
||||
<string name="firmware_uninstalling">Desinstalando firmware</string>
|
||||
<string name="firmware_uninstalled_success">Firmware desinstalado com sucesso</string>
|
||||
|
||||
<string name="error_firmware_missing">Firmware é necessário para executar certos jogos e aplicativos. Eden só funciona com firmware 19.0.1 ou anterior.</string>
|
||||
<string name="error_firmware_corrupted">Firmware está presente mas não pode ser lido. Verifique as chaves de descriptografia e refaça o dump do firmware se necessário.</string>
|
||||
<string name="error_firmware_too_new">Firmware é muito novo ou não pode ser lido. Eden só funciona com firmware 19.0.1 ou anterior.</string>
|
||||
|
||||
<string name="keys_failed">Falha ao instalar chaves (keys)</string>
|
||||
<string name="keys_install_success">Chaves (keys) instaladas com sucesso</string>
|
||||
|
||||
@@ -334,9 +334,6 @@
|
||||
<string name="firmware_uninstalling">A desinstalar firmware...</string>
|
||||
<string name="firmware_uninstalled_success">Firmware desinstalado com sucesso</string>
|
||||
|
||||
<string name="error_firmware_missing">É necessário firmware para executar alguns jogos e aplicações. O Eden só funciona com firmware 19.0.1 ou anterior.</string>
|
||||
<string name="error_firmware_corrupted">Firmware está presente mas não pode ser lido. Verifique chaves de descriptografia e volte a fazer dump do firmware se necessário.</string>
|
||||
<string name="error_firmware_too_new">Firmware é demasiado recente ou ilegível. O Eden só funciona com firmware 19.0.1 ou anterior.</string>
|
||||
|
||||
<string name="keys_failed">Falha ao instalar chaves</string>
|
||||
<string name="keys_install_success">Chaves instaladas com sucesso</string>
|
||||
|
||||
@@ -370,9 +370,6 @@
|
||||
<string name="firmware_uninstalling">Удаление прошивки...</string>
|
||||
<string name="firmware_uninstalled_success">Прошивка успешно удалена</string>
|
||||
|
||||
<string name="error_firmware_missing">Для запуска некоторых игр и системных апплетов требуется прошивка. Eden работает только с прошивкой версии 19.0.1 и ниже.</string>
|
||||
<string name="error_firmware_corrupted">Прошивка обнаружена, но не может быть прочитана. Проверьте наличие ключей дешифрования и при необходимости пересохраните прошивку.</string>
|
||||
<string name="error_firmware_too_new">Прошивка слишком новая или не может быть прочитана. Eden работает только с прошивкой версии 19.0.1 и ниже.</string>
|
||||
|
||||
<string name="keys_failed">Ошибка установки ключей</string>
|
||||
<string name="keys_install_success">Ключи успешно установлены</string>
|
||||
|
||||
@@ -334,9 +334,6 @@
|
||||
<string name="firmware_uninstalling">Деинсталирање фирмвера</string>
|
||||
<string name="firmware_uninstalled_success">Фирмваре је успешно деинсталирано</string>
|
||||
|
||||
<string name="error_firmware_missing">Firmware је потребан за покретање одређених игара и апликација. Eden ради само са firmware-ом 19.0.1 или старијим.</string>
|
||||
<string name="error_firmware_corrupted">Firmware је присутан али се не може прочитати. Проверите кључеве за дешифровање и поново направите firmware дамп ако је потребно.</string>
|
||||
<string name="error_firmware_too_new">Firmware је превише нов или се не може прочитати. Eden ради само са firmware-ом 19.0.1 или старијим.</string>
|
||||
|
||||
<string name="keys_failed">Неуспела инсталација кључева</string>
|
||||
<string name="keys_install_success">Кључеви успешно инсталирани</string>
|
||||
|
||||
@@ -370,9 +370,6 @@
|
||||
<string name="firmware_uninstalling">Видалення прошивки...</string>
|
||||
<string name="firmware_uninstalled_success">Прошивку успішно видалено</string>
|
||||
|
||||
<string name="error_firmware_missing">Для запуску деяких ігор та системних аплетів потрібна прошивка. Eden працює лише з прошивкою 19.0.1 або старішою.</string>
|
||||
<string name="error_firmware_corrupted">Прошивка є, але не може бути прочитана. Перевірте ключі дешифрування та повторно зробіть дамп прошивки за необхідності.</string>
|
||||
<string name="error_firmware_too_new">Прошивка занадто нова або нечитабельна. Eden працює лише з прошивкою 19.0.1 або старішою.</string>
|
||||
|
||||
<string name="keys_failed">Не вдалося встановити ключі</string>
|
||||
<string name="keys_install_success">Ключі успішно встановлено</string>
|
||||
|
||||
@@ -320,9 +320,6 @@
|
||||
<string name="firmware_uninstalling">Đang gỡ cài đặt firmware...</string>
|
||||
<string name="firmware_uninstalled_success">Gỡ cài đặt firmware thành công</string>
|
||||
|
||||
<string name="error_firmware_missing">Firmware là cần thiết để chạy một số trò chơi và ứng dụng. Eden chỉ hoạt động với firmware 19.0.1 trở về trước.</string>
|
||||
<string name="error_firmware_corrupted">Firmware được báo cáo là có nhưng không thể đọc được. Kiểm tra khóa giải mã và dump lại firmware nếu cần.</string>
|
||||
<string name="error_firmware_too_new">Firmware quá mới hoặc không thể đọc. Eden chỉ hoạt động với firmware 19.0.1 trở về trước.</string>
|
||||
|
||||
<string name="keys_failed">Không thể cài đặt khóa</string>
|
||||
<string name="keys_install_success">Đã cài đặt khóa thành công</string>
|
||||
|
||||
@@ -335,9 +335,6 @@
|
||||
<string name="firmware_uninstalling">正在卸载固件...</string>
|
||||
<string name="firmware_uninstalled_success">固件卸载成功</string>
|
||||
|
||||
<string name="error_firmware_missing">运行某些游戏和系统应用需要固件。Eden仅支持19.0.1及更早版本的固件。</string>
|
||||
<string name="error_firmware_corrupted">检测到固件存在但无法读取。请检查解密密钥并在必要时重新转储固件。</string>
|
||||
<string name="error_firmware_too_new">固件过新或无法读取。Eden仅支持19.0.1及更早版本的固件。</string>
|
||||
|
||||
<string name="keys_failed">密钥安装失败</string>
|
||||
<string name="keys_install_success">密钥安装成功</string>
|
||||
|
||||
@@ -364,9 +364,6 @@
|
||||
<string name="firmware_uninstalling">正在解除安裝韌體...</string>
|
||||
<string name="firmware_uninstalled_success">韌體解除安裝成功</string>
|
||||
|
||||
<string name="error_firmware_missing">執行某些遊戲和系統應用程式需要韌體。Eden僅支援19.0.1及更早版本的韌體。</string>
|
||||
<string name="error_firmware_corrupted">檢測到韌體存在但無法讀取。請檢查解密金鑰並在必要時重新轉儲韌體。</string>
|
||||
<string name="error_firmware_too_new">韌體過新或無法讀取。Eden僅支援19.0.1及更早版本的韌體。</string>
|
||||
|
||||
<string name="keys_failed">金鑰安裝失敗</string>
|
||||
<string name="keys_install_success">金鑰安裝成功</string>
|
||||
|
||||
@@ -376,9 +376,6 @@
|
||||
<string name="firmware_uninstalling">Uninstalling firmware</string>
|
||||
<string name="firmware_uninstalled_success">Firmware uninstalled successfully</string>
|
||||
|
||||
<string name="error_firmware_missing">Firmware is required to run certain games and use system applications. Eden only works with firmware 19.0.1 and earlier.</string>
|
||||
<string name="error_firmware_corrupted">Firmware reported as present, but was unable to be read. Check for decryption keys and redump firmware if necessary.</string>
|
||||
<string name="error_firmware_too_new">Firmware is too new or could not be read. Eden only works with firmware 19.0.1 and earlier.</string>
|
||||
|
||||
<string name="keys_failed">Failed to Install Keys</string>
|
||||
<string name="keys_install_success">Keys successfully installed</string>
|
||||
@@ -673,7 +670,6 @@
|
||||
<string name="preferences_controls">Controls</string>
|
||||
<string name="preferences_controls_description">Map controller input</string>
|
||||
<string name="preferences_player">Player %d</string>
|
||||
<string name="preferences_app_settings">App vSettings</string>
|
||||
<string name="preferences_debug">Debug</string>
|
||||
<string name="preferences_debug_description">CPU/GPU debugging, graphics API, fastmem</string>
|
||||
|
||||
@@ -804,11 +800,21 @@
|
||||
<string name="emulation_pause">Pause emulation</string>
|
||||
<string name="emulation_unpause">Unpause emulation</string>
|
||||
<string name="emulation_input_overlay">Overlay options</string>
|
||||
<string name="load_amiibo">Load Amiibo</string>
|
||||
<string name="touchscreen">Touchscreen</string>
|
||||
<string name="lock_drawer">Lock drawer</string>
|
||||
<string name="unlock_drawer">Unlock drawer</string>
|
||||
<string name="reset">Reset</string>
|
||||
|
||||
<!-- Amiibo -->
|
||||
<string name="amiibo_title">Amiibo</string>
|
||||
<string name="amiibo_removed_message">The current amiibo has been removed</string>
|
||||
<string name="amiibo_wrong_state">The current game is not looking for amiibo</string>
|
||||
<string name="amiibo_not_valid">The selected file is not a valid amiibo</string>
|
||||
<string name="amiibo_in_use">The selected file is already in use</string>
|
||||
<string name="amiibo_unknown_error">An unknown error occurred</string>
|
||||
<string name="amiibo_load_success">Amiibo loaded</string>
|
||||
|
||||
|
||||
<!-- Software keyboard -->
|
||||
<string name="software_keyboard">Software keyboard</string>
|
||||
|
||||
@@ -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,10 +237,15 @@ u64 SinkStream::GetExpectedPlayedSampleCount() {
|
||||
|
||||
void SinkStream::WaitFreeSpace(std::stop_token stop_token) {
|
||||
std::unique_lock lk{release_mutex};
|
||||
release_cv.wait_for(lk, std::chrono::milliseconds(5),
|
||||
[this]() { return paused || queued_buffers < max_queue_size; });
|
||||
if (queued_buffers > max_queue_size + 3) {
|
||||
release_cv.wait(lk, stop_token, [this] { return paused || queued_buffers < max_queue_size; });
|
||||
|
||||
const auto has_space = [this]() {
|
||||
const u32 current_size = queued_buffers.load(std::memory_order_relaxed);
|
||||
return paused || max_queue_size == 0 || current_size < max_queue_size;
|
||||
};
|
||||
|
||||
if (!has_space()) {
|
||||
// Wait until the queue falls below the configured limit or the stream is paused/stopped.
|
||||
release_cv.wait(lk, stop_token, has_space);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -36,7 +36,6 @@ add_library(
|
||||
cityhash.cpp
|
||||
cityhash.h
|
||||
common_funcs.h
|
||||
common_precompiled_headers.h
|
||||
common_types.h
|
||||
concepts.h
|
||||
container_hash.h
|
||||
@@ -102,7 +101,6 @@ add_library(
|
||||
param_package.h
|
||||
parent_of_member.h
|
||||
point.h
|
||||
precompiled_headers.h
|
||||
quaternion.h
|
||||
range_map.h
|
||||
range_mutex.h
|
||||
@@ -155,6 +153,8 @@ add_library(
|
||||
wall_clock.h
|
||||
zstd_compression.cpp
|
||||
zstd_compression.h
|
||||
fs/ryujinx_compat.h fs/ryujinx_compat.cpp
|
||||
fs/symlink.h fs/symlink.cpp
|
||||
)
|
||||
|
||||
if(WIN32)
|
||||
@@ -270,8 +270,4 @@ if(ANDROID)
|
||||
target_link_libraries(common PRIVATE android)
|
||||
endif()
|
||||
|
||||
if(YUZU_USE_PRECOMPILED_HEADERS)
|
||||
target_precompile_headers(common PRIVATE precompiled_headers.h)
|
||||
endif()
|
||||
|
||||
create_target_directory_groups(common)
|
||||
|
||||
@@ -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"
|
||||
@@ -33,6 +33,7 @@
|
||||
#define SUDACHI_DIR "sudachi"
|
||||
#define YUZU_DIR "yuzu"
|
||||
#define SUYU_DIR "suyu"
|
||||
#define RYUJINX_DIR "Ryujinx"
|
||||
|
||||
// yuzu-specific files
|
||||
#define LOG_FILE "eden_log.txt"
|
||||
|
||||
@@ -84,7 +84,7 @@ public:
|
||||
return eden_paths.at(eden_path);
|
||||
}
|
||||
|
||||
[[nodiscard]] const fs::path& GetLegacyPathImpl(LegacyPath legacy_path) {
|
||||
[[nodiscard]] const fs::path& GetLegacyPathImpl(EmuPath legacy_path) {
|
||||
return legacy_paths.at(legacy_path);
|
||||
}
|
||||
|
||||
@@ -98,7 +98,7 @@ public:
|
||||
eden_paths.insert_or_assign(eden_path, new_path);
|
||||
}
|
||||
|
||||
void SetLegacyPathImpl(LegacyPath legacy_path, const fs::path& new_path) {
|
||||
void SetLegacyPathImpl(EmuPath legacy_path, const fs::path& new_path) {
|
||||
legacy_paths.insert_or_assign(legacy_path, new_path);
|
||||
}
|
||||
|
||||
@@ -118,9 +118,9 @@ public:
|
||||
}
|
||||
eden_path_cache = eden_path / CACHE_DIR;
|
||||
eden_path_config = eden_path / CONFIG_DIR;
|
||||
#define LEGACY_PATH(titleName, upperName) GenerateLegacyPath(LegacyPath::titleName##Dir, GetAppDataRoamingDirectory() / upperName##_DIR); \
|
||||
GenerateLegacyPath(LegacyPath::titleName##ConfigDir, GetAppDataRoamingDirectory() / upperName##_DIR / CONFIG_DIR); \
|
||||
GenerateLegacyPath(LegacyPath::titleName##CacheDir, GetAppDataRoamingDirectory() / upperName##_DIR / CACHE_DIR);
|
||||
#define LEGACY_PATH(titleName, upperName) GenerateLegacyPath(EmuPath::titleName##Dir, GetAppDataRoamingDirectory() / upperName##_DIR); \
|
||||
GenerateLegacyPath(EmuPath::titleName##ConfigDir, GetAppDataRoamingDirectory() / upperName##_DIR / CONFIG_DIR); \
|
||||
GenerateLegacyPath(EmuPath::titleName##CacheDir, GetAppDataRoamingDirectory() / upperName##_DIR / CACHE_DIR);
|
||||
LEGACY_PATH(Citron, CITRON)
|
||||
LEGACY_PATH(Sudachi, SUDACHI)
|
||||
LEGACY_PATH(Yuzu, YUZU)
|
||||
@@ -140,9 +140,9 @@ public:
|
||||
eden_path_cache = eden_path / CACHE_DIR;
|
||||
eden_path_config = eden_path / CONFIG_DIR;
|
||||
}
|
||||
#define LEGACY_PATH(titleName, upperName) GenerateLegacyPath(LegacyPath::titleName##Dir, GetDataDirectory("XDG_DATA_HOME") / upperName##_DIR); \
|
||||
GenerateLegacyPath(LegacyPath::titleName##ConfigDir, GetDataDirectory("XDG_CONFIG_HOME") / upperName##_DIR); \
|
||||
GenerateLegacyPath(LegacyPath::titleName##CacheDir, GetDataDirectory("XDG_CACHE_HOME") / upperName##_DIR);
|
||||
#define LEGACY_PATH(titleName, upperName) GenerateLegacyPath(EmuPath::titleName##Dir, GetDataDirectory("XDG_DATA_HOME") / upperName##_DIR); \
|
||||
GenerateLegacyPath(EmuPath::titleName##ConfigDir, GetDataDirectory("XDG_CONFIG_HOME") / upperName##_DIR); \
|
||||
GenerateLegacyPath(EmuPath::titleName##CacheDir, GetDataDirectory("XDG_CACHE_HOME") / upperName##_DIR);
|
||||
LEGACY_PATH(Citron, CITRON)
|
||||
LEGACY_PATH(Sudachi, SUDACHI)
|
||||
LEGACY_PATH(Yuzu, YUZU)
|
||||
@@ -165,6 +165,15 @@ public:
|
||||
GenerateEdenPath(EdenPath::ShaderDir, eden_path / SHADER_DIR);
|
||||
GenerateEdenPath(EdenPath::TASDir, eden_path / TAS_DIR);
|
||||
GenerateEdenPath(EdenPath::IconsDir, eden_path / ICONS_DIR);
|
||||
|
||||
#ifdef _WIN32
|
||||
GenerateLegacyPath(EmuPath::RyujinxDir, GetAppDataRoamingDirectory() / RYUJINX_DIR);
|
||||
#else
|
||||
// In Ryujinx's infinite wisdom, it places EVERYTHING in the config directory on UNIX
|
||||
// This is incredibly stupid and violates a million XDG standards, but whatever
|
||||
GenerateLegacyPath(EmuPath::RyujinxDir, GetDataDirectory("XDG_CONFIG_HOME") / RYUJINX_DIR);
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
private:
|
||||
@@ -179,12 +188,12 @@ private:
|
||||
SetEdenPathImpl(eden_path, new_path);
|
||||
}
|
||||
|
||||
void GenerateLegacyPath(LegacyPath legacy_path, const fs::path& new_path) {
|
||||
void GenerateLegacyPath(EmuPath legacy_path, const fs::path& new_path) {
|
||||
SetLegacyPathImpl(legacy_path, new_path);
|
||||
}
|
||||
|
||||
std::unordered_map<EdenPath, fs::path> eden_paths;
|
||||
std::unordered_map<LegacyPath, fs::path> legacy_paths;
|
||||
std::unordered_map<EmuPath, fs::path> legacy_paths;
|
||||
};
|
||||
|
||||
bool ValidatePath(const fs::path& path) {
|
||||
@@ -272,7 +281,7 @@ const fs::path& GetEdenPath(EdenPath eden_path) {
|
||||
return PathManagerImpl::GetInstance().GetEdenPathImpl(eden_path);
|
||||
}
|
||||
|
||||
const std::filesystem::path& GetLegacyPath(LegacyPath legacy_path) {
|
||||
const std::filesystem::path& GetLegacyPath(EmuPath legacy_path) {
|
||||
return PathManagerImpl::GetInstance().GetLegacyPathImpl(legacy_path);
|
||||
}
|
||||
|
||||
@@ -280,7 +289,7 @@ std::string GetEdenPathString(EdenPath eden_path) {
|
||||
return PathToUTF8String(GetEdenPath(eden_path));
|
||||
}
|
||||
|
||||
std::string GetLegacyPathString(LegacyPath legacy_path) {
|
||||
std::string GetLegacyPathString(EmuPath legacy_path) {
|
||||
return PathToUTF8String(GetLegacyPath(legacy_path));
|
||||
}
|
||||
|
||||
|
||||
@@ -32,22 +32,26 @@ enum class EdenPath {
|
||||
IconsDir, // Where Icons for Windows shortcuts are stored.
|
||||
};
|
||||
|
||||
enum LegacyPath {
|
||||
CitronDir, // Citron Directories for migration
|
||||
// migration/compat dirs
|
||||
enum EmuPath {
|
||||
CitronDir,
|
||||
CitronConfigDir,
|
||||
CitronCacheDir,
|
||||
|
||||
SudachiDir, // Sudachi Directories for migration
|
||||
SudachiDir,
|
||||
SudachiConfigDir,
|
||||
SudachiCacheDir,
|
||||
|
||||
YuzuDir, // Yuzu Directories for migration
|
||||
YuzuDir,
|
||||
YuzuConfigDir,
|
||||
YuzuCacheDir,
|
||||
|
||||
SuyuDir, // Suyu Directories for migration
|
||||
SuyuDir,
|
||||
SuyuConfigDir,
|
||||
SuyuCacheDir,
|
||||
|
||||
// used exclusively for save data linking
|
||||
RyujinxDir,
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -229,7 +233,7 @@ void SetAppDirectory(const std::string& app_directory);
|
||||
*
|
||||
* @returns The filesystem path associated with the LegacyPath enum.
|
||||
*/
|
||||
[[nodiscard]] const std::filesystem::path& GetLegacyPath(LegacyPath legacy_path);
|
||||
[[nodiscard]] const std::filesystem::path& GetLegacyPath(EmuPath legacy_path);
|
||||
|
||||
/**
|
||||
* Gets the filesystem path associated with the EdenPath enum as a UTF-8 encoded std::string.
|
||||
@@ -247,7 +251,7 @@ void SetAppDirectory(const std::string& app_directory);
|
||||
*
|
||||
* @returns The filesystem path associated with the LegacyPath enum as a UTF-8 encoded std::string.
|
||||
*/
|
||||
[[nodiscard]] std::string GetLegacyPathString(LegacyPath legacy_path);
|
||||
[[nodiscard]] std::string GetLegacyPathString(EmuPath legacy_path);
|
||||
|
||||
/**
|
||||
* Sets a new filesystem path associated with the EdenPath enum.
|
||||
|
||||
93
src/common/fs/ryujinx_compat.cpp
Normal file
@@ -0,0 +1,93 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#include "ryujinx_compat.h"
|
||||
#include "common/fs/path_util.h"
|
||||
#include <cstddef>
|
||||
#include <cstring>
|
||||
#include <fmt/ranges.h>
|
||||
#include <fstream>
|
||||
|
||||
namespace Common::FS {
|
||||
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
fs::path GetKvdbPath()
|
||||
{
|
||||
return GetLegacyPath(EmuPath::RyujinxDir) / "bis" / "system" / "save" / "8000000000000000" / "0"
|
||||
/ "imkvdb.arc";
|
||||
}
|
||||
|
||||
fs::path GetRyuSavePath(const u64 &save_id)
|
||||
{
|
||||
std::string hex = fmt::format("{:016x}", save_id);
|
||||
|
||||
// TODO: what's the difference between 0 and 1?
|
||||
return GetLegacyPath(EmuPath::RyujinxDir) / "bis" / "user" / "save" / hex / "0";
|
||||
}
|
||||
|
||||
IMENReadResult ReadKvdb(const fs::path &path, std::vector<IMEN> &imens)
|
||||
{
|
||||
std::ifstream kvdb{path, std::ios::binary | std::ios::ate};
|
||||
|
||||
if (!kvdb) {
|
||||
return IMENReadResult::Nonexistent;
|
||||
}
|
||||
|
||||
size_t file_size = kvdb.tellg();
|
||||
|
||||
// IMKV header + 8 bytes
|
||||
if (file_size < 0xB) {
|
||||
return IMENReadResult::NoHeader;
|
||||
}
|
||||
|
||||
// magic (not the wizard kind)
|
||||
kvdb.seekg(0, std::ios::beg);
|
||||
char header[12];
|
||||
kvdb.read(header, 12);
|
||||
|
||||
if (std::memcmp(header, IMKV_MAGIC, 4) != 0) {
|
||||
return IMENReadResult::InvalidMagic;
|
||||
}
|
||||
|
||||
// calculate num. of imens left
|
||||
std::size_t remaining = (file_size - 12);
|
||||
std::size_t num_imens = remaining / IMEN_SIZE;
|
||||
|
||||
// File is misaligned and probably corrupt (rip)
|
||||
if (remaining % IMEN_SIZE != 0) {
|
||||
return IMENReadResult::Misaligned;
|
||||
}
|
||||
|
||||
// if there aren't any IMENs, it's empty and we can safely no-op out of here
|
||||
if (num_imens == 0) {
|
||||
return IMENReadResult::NoImens;
|
||||
}
|
||||
|
||||
imens.reserve(num_imens);
|
||||
|
||||
// initially I wanted to do a struct, but imkvdb is 140 bytes
|
||||
// while the compiler will murder you if you try to align u64 to 4 bytes
|
||||
for (std::size_t i = 0; i < num_imens; ++i) {
|
||||
char magic[4];
|
||||
u64 title_id = 0;
|
||||
u64 save_id = 0;
|
||||
|
||||
kvdb.read(magic, 4);
|
||||
if (std::memcmp(magic, IMEN_MAGIC, 4) != 0) {
|
||||
return IMENReadResult::InvalidMagic;
|
||||
}
|
||||
|
||||
kvdb.ignore(0x8);
|
||||
kvdb.read(reinterpret_cast<char *>(&title_id), 8);
|
||||
kvdb.ignore(0x38);
|
||||
kvdb.read(reinterpret_cast<char *>(&save_id), 8);
|
||||
kvdb.ignore(0x38);
|
||||
|
||||
imens.emplace_back(IMEN{title_id, save_id});
|
||||
}
|
||||
|
||||
return IMENReadResult::Success;
|
||||
}
|
||||
|
||||
} // namespace Common::FS
|
||||
40
src/common/fs/ryujinx_compat.h
Normal file
@@ -0,0 +1,40 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include <filesystem>
|
||||
#include <vector>
|
||||
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
namespace Common::FS {
|
||||
|
||||
constexpr const char IMEN_MAGIC[4] = {0x49, 0x4d, 0x45, 0x4e};
|
||||
constexpr const char IMKV_MAGIC[4] = {0x49, 0x4d, 0x4b, 0x56};
|
||||
constexpr const u8 IMEN_SIZE = 0x8c;
|
||||
|
||||
fs::path GetKvdbPath();
|
||||
fs::path GetRyuSavePath(const u64 &program_id);
|
||||
|
||||
enum class IMENReadResult {
|
||||
Nonexistent, // ryujinx not found
|
||||
NoHeader, // file isn't big enough for header
|
||||
InvalidMagic, // no IMKV or IMEN header
|
||||
Misaligned, // file isn't aligned to expected IMEN boundaries
|
||||
NoImens, // no-op, there are no IMENs
|
||||
Success, // :)
|
||||
};
|
||||
|
||||
struct IMEN
|
||||
{
|
||||
u64 title_id;
|
||||
u64 save_id;
|
||||
};
|
||||
|
||||
static_assert(sizeof(IMEN) == 0x10, "IMEN has incorrect size.");
|
||||
|
||||
IMENReadResult ReadKvdb(const fs::path &path, std::vector<IMEN> &imens);
|
||||
|
||||
} // namespace Common::FS
|
||||
43
src/common/fs/symlink.cpp
Normal file
@@ -0,0 +1,43 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#include "symlink.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
#include <fmt/format.h>
|
||||
#endif
|
||||
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
// The sole purpose of this file is to treat symlinks like symlinks on POSIX,
|
||||
// or treat them as directory junctions on Windows.
|
||||
// This is because, for some inexplicable reason, Microsoft has locked symbolic
|
||||
// links behind a "security policy", whereas directory junctions--functionally identical
|
||||
// for directories, by the way--are not. Why? I don't know.
|
||||
|
||||
namespace Common::FS {
|
||||
|
||||
bool CreateSymlink(const fs::path &from, const fs::path &to)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
const std::string command = fmt::format("mklink /J {} {}", to.string(), from.string());
|
||||
return system(command.c_str()) == 0;
|
||||
#else
|
||||
std::error_code ec;
|
||||
fs::create_directory_symlink(from, to, ec);
|
||||
return !ec;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool IsSymlink(const fs::path &path)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
auto attributes = GetFileAttributesW(path.wstring().c_str());
|
||||
return attributes & FILE_ATTRIBUTE_REPARSE_POINT;
|
||||
#else
|
||||
return fs::is_symlink(path);
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace Common::FS
|
||||
12
src/common/fs/symlink.h
Normal file
@@ -0,0 +1,12 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <filesystem>
|
||||
namespace Common::FS {
|
||||
|
||||
bool CreateSymlink(const std::filesystem::path &from, const std::filesystem::path &to);
|
||||
bool IsSymlink(const std::filesystem::path &path);
|
||||
|
||||
} // namespace Common::FS
|
||||
@@ -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"
|
||||
@@ -622,6 +622,10 @@ struct Values {
|
||||
|
||||
// Linux
|
||||
SwitchableSetting<bool> enable_gamemode{linkage, true, "enable_gamemode", Category::Linux};
|
||||
#ifdef __unix__
|
||||
SwitchableSetting<bool> gui_force_x11{linkage, false, "gui_force_x11", Category::Linux};
|
||||
Setting<bool> gui_hide_backend_warning{linkage, false, "gui_hide_backend_warning", Category::Linux};
|
||||
#endif
|
||||
|
||||
// Controls
|
||||
InputSetting<std::array<PlayerInput, 10>> players;
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -210,12 +210,9 @@ std::shared_ptr<Dynarmic::A32::Jit> ArmDynarmic32::MakeJit(Common::PageTable* pa
|
||||
config.wall_clock_cntpct = m_uses_wall_clock;
|
||||
config.enable_cycle_counting = !m_uses_wall_clock;
|
||||
|
||||
// Code cache size
|
||||
#ifdef ARCHITECTURE_arm64
|
||||
// Code cache size - max in ARM is 128MiB, max in x86_64 is 2GiB
|
||||
// Solaris doesn't support kPageSize >= 512MiB
|
||||
config.code_cache_size = std::uint32_t(128_MiB);
|
||||
#else
|
||||
config.code_cache_size = std::uint32_t(512_MiB);
|
||||
#endif
|
||||
|
||||
// Allow memory fault handling to work
|
||||
if (m_system.DebuggerEnabled()) {
|
||||
|
||||
@@ -269,12 +269,9 @@ std::shared_ptr<Dynarmic::A64::Jit> ArmDynarmic64::MakeJit(Common::PageTable* pa
|
||||
config.wall_clock_cntpct = m_uses_wall_clock;
|
||||
config.enable_cycle_counting = !m_uses_wall_clock;
|
||||
|
||||
// Code cache size
|
||||
#ifdef ARCHITECTURE_arm64
|
||||
// Code cache size - max in ARM is 128MiB, max in x86_64 is 2GiB
|
||||
// Solaris doesn't support kPageSize >= 512MiB
|
||||
config.code_cache_size = std::uint32_t(128_MiB);
|
||||
#else
|
||||
config.code_cache_size = std::uint32_t(512_MiB);
|
||||
#endif
|
||||
|
||||
// Allow memory fault handling to work
|
||||
if (m_system.DebuggerEnabled()) {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -429,19 +429,23 @@ void KThread::StartTermination() {
|
||||
}
|
||||
|
||||
void KThread::FinishTermination() {
|
||||
// Acquire the scheduler lock.
|
||||
KScopedSchedulerLock lk{m_kernel};
|
||||
|
||||
// Ensure that the thread is not executing on any core.
|
||||
if (m_parent != nullptr) {
|
||||
for (std::size_t i = 0; i < static_cast<std::size_t>(Core::Hardware::NUM_CPU_CORES); ++i) {
|
||||
KThread* core_thread{};
|
||||
do {
|
||||
core_thread = m_kernel.Scheduler(i).GetSchedulerCurrentThread();
|
||||
} while (core_thread == this);
|
||||
bool wait_thread = true;
|
||||
lk.unlock();
|
||||
while (wait_thread) {
|
||||
// now "pin" the scheduler so it wont change stuff mid-way
|
||||
lk.lock();
|
||||
for (std::size_t i = 0; i < std::size_t(Core::Hardware::NUM_CPU_CORES); ++i)
|
||||
is_on_schedule |= m_kernel.Scheduler(i).GetSchedulerCurrentThread() == this;
|
||||
// let scheduler try again
|
||||
if (wait_thread)
|
||||
lk.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
// Acquire the scheduler lock.
|
||||
KScopedSchedulerLock sl{m_kernel};
|
||||
|
||||
// Signal.
|
||||
m_signaled = true;
|
||||
KSynchronizationObject::NotifyAvailable();
|
||||
|
||||
@@ -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+
|
||||
|
||||
@@ -10,16 +10,19 @@
|
||||
#include <iostream>
|
||||
#include <random>
|
||||
|
||||
#include <boost/algorithm/string/case_conv.hpp>
|
||||
#include <boost/algorithm/string/find.hpp>
|
||||
#include <fmt/ranges.h>
|
||||
|
||||
#include "common/fs/file.h"
|
||||
#include "common/fs/fs.h"
|
||||
#include "common/fs/fs_types.h"
|
||||
#include "common/fs/path_util.h"
|
||||
#include <ranges>
|
||||
#include "common/settings.h"
|
||||
#include "common/string_util.h"
|
||||
#include "core/file_sys/savedata_factory.h"
|
||||
#include "core/hle/service/acc/profile_manager.h"
|
||||
#include <ranges>
|
||||
|
||||
namespace Service::Account {
|
||||
|
||||
@@ -39,6 +42,7 @@ struct ProfileDataRaw {
|
||||
INSERT_PADDING_BYTES(0x10);
|
||||
std::array<UserRaw, MAX_USERS> users{};
|
||||
};
|
||||
|
||||
static_assert(sizeof(ProfileDataRaw) == 0x650, "ProfileDataRaw has incorrect size.");
|
||||
|
||||
// TODO(ogniK): Get actual error codes
|
||||
@@ -488,10 +492,24 @@ void ProfileManager::ResetUserSaveFile()
|
||||
ParseUserSaveFile();
|
||||
}
|
||||
|
||||
std::vector<std::string> ProfileManager::FindOrphanedProfiles()
|
||||
std::vector<std::string> ProfileManager::FindGoodProfiles()
|
||||
{
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
std::vector<std::string> good_uuids;
|
||||
|
||||
const auto path = Common::FS::GetEdenPath(Common::FS::EdenPath::NANDDir)
|
||||
/ "user/save/0000000000000000";
|
||||
|
||||
// some exceptions because certain games just LOVE TO CAUSE ISSUES
|
||||
static constexpr const std::array<const char* const, 2> EXCEPTION_UUIDS
|
||||
= {"5755CC2A545A87128500000000000000", "00000000000000000000000000000000"};
|
||||
|
||||
for (const char *const uuid : EXCEPTION_UUIDS) {
|
||||
if (fs::exists(path / uuid))
|
||||
good_uuids.emplace_back(uuid);
|
||||
}
|
||||
|
||||
for (const ProfileInfo& p : profiles) {
|
||||
std::string uuid_string = [p]() -> std::string {
|
||||
auto uuid = p.user_uuid;
|
||||
@@ -506,11 +524,17 @@ std::vector<std::string> ProfileManager::FindOrphanedProfiles()
|
||||
return fmt::format("{:016X}{:016X}", user_id[1], user_id[0]);
|
||||
}();
|
||||
|
||||
good_uuids.emplace_back(uuid_string);
|
||||
if (uuid_string != "0") good_uuids.emplace_back(uuid_string);
|
||||
}
|
||||
|
||||
// used for acnh, etc
|
||||
good_uuids.emplace_back("00000000000000000000000000000000");
|
||||
return good_uuids;
|
||||
}
|
||||
|
||||
std::vector<std::string> ProfileManager::FindOrphanedProfiles()
|
||||
{
|
||||
std::vector<std::string> good_uuids = FindGoodProfiles();
|
||||
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
// TODO: fetch save_id programmatically
|
||||
const auto path = Common::FS::GetEdenPath(Common::FS::EdenPath::NANDDir)
|
||||
@@ -523,32 +547,47 @@ std::vector<std::string> ProfileManager::FindOrphanedProfiles()
|
||||
[&good_uuids, &orphaned_profiles](const std::filesystem::directory_entry& entry) -> bool {
|
||||
const std::string uuid = entry.path().stem().string();
|
||||
|
||||
bool override = false;
|
||||
|
||||
// first off, we should always clear empty profiles
|
||||
// 99% of the time these are useless. If not, they are recreated anyways...
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
const auto is_empty = [&entry]() -> bool {
|
||||
const auto is_empty = [&entry, &override]() -> bool {
|
||||
try {
|
||||
for (const auto& file : fs::recursive_directory_iterator(entry.path())) {
|
||||
if (file.is_regular_file()) {
|
||||
return true;
|
||||
}
|
||||
// TODO: .yuzu_save_size is a weird file that gets created by certain games
|
||||
// I have no idea what its purpose is, but TEMPORARY SOLUTION: just mark the profile as valid if
|
||||
// this file exists (???) e.g. for SSBU
|
||||
// In short: if .yuzu_save_size is the ONLY file in a profile it's probably fine to keep
|
||||
if (file.path().filename().string() == FileSys::GetSaveDataSizeFileName())
|
||||
override = true;
|
||||
|
||||
// if there are any regular files (NOT directories) there, do NOT delete it :p
|
||||
if (file.is_regular_file())
|
||||
return false;
|
||||
}
|
||||
} catch (const fs::filesystem_error& e) {
|
||||
// if we get an error--no worries, just pretend it's not empty
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}();
|
||||
|
||||
if (!is_empty) {
|
||||
if (is_empty) {
|
||||
fs::remove_all(entry);
|
||||
return true;
|
||||
}
|
||||
|
||||
// edge-case: some filesystems forcefully change filenames to lowercase
|
||||
// so we can just ignore any differences
|
||||
// looking at you microsoft... ;)
|
||||
std::string upper_uuid = uuid;
|
||||
boost::to_upper(upper_uuid);
|
||||
|
||||
// if profiles.dat contains the UUID--all good
|
||||
// if not--it's an orphaned profile and should be resolved by the user
|
||||
if (std::find(good_uuids.begin(), good_uuids.end(), uuid) == good_uuids.end()) {
|
||||
if (!override
|
||||
&& std::find(good_uuids.begin(), good_uuids.end(), upper_uuid) == good_uuids.end()) {
|
||||
orphaned_profiles.emplace_back(uuid);
|
||||
}
|
||||
return true;
|
||||
|
||||
@@ -105,6 +105,7 @@ public:
|
||||
|
||||
void ResetUserSaveFile();
|
||||
|
||||
std::vector<std::string> FindGoodProfiles();
|
||||
std::vector<std::string> FindOrphanedProfiles();
|
||||
|
||||
private:
|
||||
|
||||