Compare commits

..

14 Commits

Author SHA1 Message Date
unknown
8a89cef721 Revert "test again"
This reverts commit 26ccf978de1942ea9a276cb558cd9916d4bfaeb2.
2025-11-01 18:04:41 +01:00
unknown
7b37ac2992 test again 2025-11-01 18:04:41 +01:00
unknown
aa8b4ccdbe do not move in return ups 2025-11-01 18:04:41 +01:00
unknown
365f4dfee8 mh, try to check for IsLinked to prevent crash on Linux 2025-11-01 18:04:41 +01:00
unknown
a065adbf94 fix header 2025-11-01 18:04:41 +01:00
unknown
6413de66ac fix closing games 2025-11-01 18:04:41 +01:00
unknown
336b9a806f don't hold handle all the time to the file 2025-11-01 18:04:41 +01:00
unknown
78b9b7808e try to fix access violation 2025-11-01 18:04:41 +01:00
unknown
b311c9c3f4 degrade patch logging 2025-11-01 18:04:41 +01:00
unknown
695ae6a71f fix version display + loading 2025-11-01 18:04:41 +01:00
unknown
809a1b4d06 add all versions to config 2025-11-01 18:04:41 +01:00
unknown
cd92452202 file watcher 2025-11-01 18:04:41 +01:00
unknown
6974dc2247 fix license header 2025-11-01 18:04:41 +01:00
unknown
5ba9d3fb9f [fs/core] initial external content without NAND install 2025-11-01 18:04:41 +01:00
169 changed files with 11148 additions and 7473 deletions

View File

@@ -13,8 +13,6 @@ elseif (${CMAKE_SYSTEM_NAME} STREQUAL "OpenBSD")
set(PLATFORM_OPENBSD ON)
elseif (${CMAKE_SYSTEM_NAME} STREQUAL "NetBSD")
set(PLATFORM_NETBSD ON)
elseif (${CMAKE_SYSTEM_NAME} STREQUAL "DragonFly")
set(PLATFORM_DRAGONFLYBSD ON)
elseif (${CMAKE_SYSTEM_NAME} STREQUAL "Haiku")
set(PLATFORM_HAIKU ON)
elseif (${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
@@ -39,23 +37,17 @@ endif()
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/CMakeModules")
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/externals/cmake-modules")
# https://gitlab.kitware.com/cmake/cmake/-/merge_requests/11112
# This works totally fine on MinGW64, but not CLANG{,ARM}64
if(MINGW AND CXX_CLANG)
set(CMAKE_SYSTEM_VERSION 10.0.0)
endif()
# NB: this does not account for SPARC
# If you get Eden working on SPARC, please shoot crueter@crueter.xyz multiple emails
# and you will be hailed for eternity
if (PLATFORM_SUN)
# Terrific Solaris pkg shenanigans
list(APPEND CMAKE_PREFIX_PATH "${CMAKE_SYSROOT}/usr/lib/qt/6.6/lib/amd64/cmake")
list(APPEND CMAKE_MODULE_PATH "${CMAKE_SYSROOT}/usr/lib/qt/6.6/lib/amd64/cmake")
list(APPEND CMAKE_PREFIX_PATH "/usr/lib/qt/6.6/lib/amd64/cmake")
list(APPEND CMAKE_MODULE_PATH "/usr/lib/qt/6.6/lib/amd64/cmake")
# Amazing - absolutely incredible
list(APPEND CMAKE_PREFIX_PATH "${CMAKE_SYSROOT}/usr/lib/amd64/cmake")
list(APPEND CMAKE_MODULE_PATH "${CMAKE_SYSROOT}/usr/lib/amd64/cmake")
list(APPEND CMAKE_PREFIX_PATH "/usr/lib/amd64/cmake")
list(APPEND CMAKE_MODULE_PATH "/usr/lib/amd64/cmake")
# For some mighty reason, doing a normal release build sometimes may not trigger
# the proper -O3 switch to materialize
@@ -71,18 +63,18 @@ endif()
# Needed for FFmpeg w/ VAAPI and DRM
if (PLATFORM_OPENBSD)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -I${CMAKE_SYSROOT}/usr/X11R6/include")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -I${CMAKE_SYSROOT}/usr/X11R6/include")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -L${CMAKE_SYSROOT}/usr/X11R6/lib")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -I/usr/X11R6/include")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -I/usr/X11R6/include")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -L/usr/X11R6/lib")
elseif (PLATFORM_NETBSD)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -I${CMAKE_SYSROOT}/usr/X11R7/include")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -I${CMAKE_SYSROOT}/usr/X11R7/include")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -L${CMAKE_SYSROOT}/usr/X11R7/lib")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -I/usr/X11R7/include")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -I/usr/X11R7/include")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -L/usr/X11R7/lib")
endif()
# NetBSD: Fun for the whole family!
if (PLATFORM_NETBSD)
set(ENV{PKG_CONFIG_PATH} "${PKG_CONFIG_PATH}:${CMAKE_SYSROOT}/usr/pkg/lib/ffmpeg7/pkgconfig")
set(ENV{PKG_CONFIG_PATH} "${PKG_CONFIG_PATH}:/usr/pkg/lib/ffmpeg7/pkgconfig")
endif()
# Detect current compilation architecture and create standard definitions
@@ -100,14 +92,12 @@ function(detect_architecture symbol arch)
if (ARCHITECTURE_${arch})
set(ARCHITECTURE "${arch}" PARENT_SCOPE)
set(ARCHITECTURE_${arch} 1 PARENT_SCOPE)
add_definitions("-DARCHITECTURE_${arch}=1")
add_definitions(-DARCHITECTURE_${arch}=1)
endif()
endif()
endfunction()
if (NOT ENABLE_GENERIC)
# https://sourceforge.net/p/predef/wiki/Architectures/
# TODO: THIS IS FUCKING FLAWED ONLY THE FIRST SYMBOL THAT APPEARS WILL BE CONSIDERED :(
if (MSVC)
detect_architecture("_M_AMD64" x86_64)
detect_architecture("_M_IX86" x86)
@@ -119,48 +109,6 @@ if (NOT ENABLE_GENERIC)
detect_architecture("__arm__" arm)
detect_architecture("__aarch64__" arm64)
endif()
detect_architecture("__ARM64__" arm64)
detect_architecture("__aarch64__" arm64)
detect_architecture("_M_ARM64" arm64)
detect_architecture("__arm__" arm)
detect_architecture("__TARGET_ARCH_ARM" arm)
detect_architecture("_M_ARM" arm)
detect_architecture("__x86_64" x86_64)
detect_architecture("__x86_64__" x86_64)
detect_architecture("__amd64" x86_64)
detect_architecture("_M_X64" x86_64)
detect_architecture("__i386" x86)
detect_architecture("__i386__" x86)
detect_architecture("_M_IX86" x86)
detect_architecture("__ia64" ia64)
detect_architecture("__ia64__" ia64)
detect_architecture("_M_IA64" ia64)
detect_architecture("__mips" mips)
detect_architecture("__mips__" mips)
detect_architecture("_M_MRX000" mips)
detect_architecture("__powerpc64__" ppc64)
detect_architecture("__ppc64__" ppc64)
detect_architecture("__PPC64__" ppc64)
detect_architecture("_ARCH_PPC64" ppc64)
detect_architecture("__ppc__" ppc)
detect_architecture("__ppc" ppc)
detect_architecture("__powerpc__" ppc)
detect_architecture("_ARCH_COM" ppc)
detect_architecture("_ARCH_PWR" ppc)
detect_architecture("_ARCH_PPC" ppc)
detect_architecture("_M_MPPC" ppc)
detect_architecture("_M_PPC" ppc)
detect_architecture("__riscv" riscv)
detect_architecture("__EMSCRIPTEN__" wasm)
endif()
if (NOT DEFINED ARCHITECTURE)
@@ -191,9 +139,9 @@ if (CXX_CLANG_CL)
$<$<COMPILE_LANGUAGE:C,CXX>:/EHsc> # thanks microsoft
)
# REQUIRED CPU features IN Windows-amd64
if (ARCHITECTURE_x86_64)
add_compile_options(
# Required CPU features for amd64
$<$<COMPILE_LANGUAGE:C,CXX>:-msse4.1>
$<$<COMPILE_LANGUAGE:C,CXX>:-mcx16>
)
@@ -211,8 +159,9 @@ if (MSVC AND NOT CXX_CLANG)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /W3 /WX-")
endif()
if (PLATFORM_FREEBSD OR PLATFORM_DRAGONFLYBSD)
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -L${CMAKE_SYSROOT}/usr/local/lib")
if (PLATFORM_FREEBSD)
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -L/usr/local/lib")
endif()
# Set bundled sdl2/qt as dependent options.
@@ -272,7 +221,6 @@ if(YUZU_ENABLE_LTO)
set(CMAKE_POLICY_DEFAULT_CMP0069 NEW)
set(CMAKE_INTERPROCEDURAL_OPTIMIZATION ${COMPILER_SUPPORTS_LTO})
endif()
option(USE_CCACHE "Use ccache for compilation" OFF)
set(CCACHE_PATH "ccache" CACHE STRING "Path to ccache binary")
if(USE_CCACHE)
@@ -317,11 +265,9 @@ if (ANDROID OR WIN32 OR APPLE OR PLATFORM_SUN)
# your own copy of it.
set(DEFAULT_ENABLE_OPENSSL OFF)
endif()
if (ENABLE_WEB_SERVICE)
set(DEFAULT_ENABLE_OPENSSL ON)
endif()
option(ENABLE_OPENSSL "Enable OpenSSL backend for ISslConnection" ${DEFAULT_ENABLE_OPENSSL})
if (ENABLE_OPENSSL)
set(DEFAULT_YUZU_USE_BUNDLED_OPENSSL OFF)
@@ -586,7 +532,7 @@ else()
find_package(zstd 1.5 REQUIRED MODULE)
# wow
find_package(Boost 1.57.0 CONFIG REQUIRED OPTIONAL_COMPONENTS headers context system fiber filesystem)
find_package(Boost 1.57.0 CONFIG REQUIRED OPTIONAL_COMPONENTS headers context system fiber)
if (CMAKE_SYSTEM_NAME STREQUAL "Linux" OR ANDROID)
find_package(gamemode 1.7 MODULE)
@@ -948,14 +894,6 @@ if(MSVC)
)
endif()
if (MINGW)
# This saves a truly ridiculous amount of time during linking
# In my tests, without this it takes 2 mins, with it takes 3-5 seconds
# or on GitHub Actions, 10 minutes -> 3 seconds
set(MINGW_FLAGS "-Wl,--strip-all -Wl,--gc-sections")
set(CMAKE_EXE_LINKER_FLAGS_RELEASE "${CMAKE_EXE_LINKER_FLAGS_RELEASE} ${MINGW_FLAGS}")
endif()
add_subdirectory(src)
# Set yuzu project or yuzu-cmd project as default StartUp Project in Visual Studio depending on whether QT is enabled or not

View File

@@ -594,14 +594,6 @@ function(AddCIPackage)
add_ci_package(windows-arm64)
endif()
if ((MINGW AND ARCHITECTURE_x86_64) AND NOT "mingw-amd64" IN_LIST DISABLED_PLATFORMS)
add_ci_package(mingw-amd64)
endif()
if ((MINGW AND ARCHITECTURE_arm64) AND NOT "mingw-arm64" IN_LIST DISABLED_PLATFORMS)
add_ci_package(mingw-arm64)
endif()
if (ANDROID AND NOT "android" IN_LIST DISABLED_PLATFORMS)
add_ci_package(android)
endif()

View File

@@ -0,0 +1,10 @@
# SPDX-FileCopyrightText: 2020 yuzu Emulator Project
# SPDX-License-Identifier: GPL-2.0-or-later
function(copy_yuzu_FFmpeg_deps target_dir)
include(WindowsCopyFiles)
set(DLL_DEST "$<TARGET_FILE_DIR:${target_dir}>/")
file(READ "${FFmpeg_PATH}/requirements.txt" FFmpeg_REQUIRED_DLLS)
string(STRIP "${FFmpeg_REQUIRED_DLLS}" FFmpeg_REQUIRED_DLLS)
windows_copy_files(${target_dir} ${FFmpeg_LIBRARY_DIR} ${DLL_DEST} ${FFmpeg_REQUIRED_DLLS})
endfunction(copy_yuzu_FFmpeg_deps)

View File

@@ -0,0 +1,8 @@
# SPDX-FileCopyrightText: 2016 Citra Emulator Project
# SPDX-License-Identifier: GPL-2.0-or-later
function(copy_yuzu_SDL_deps target_dir)
include(WindowsCopyFiles)
set(DLL_DEST "$<TARGET_FILE_DIR:${target_dir}>/")
windows_copy_files(${target_dir} ${SDL2_DLL_DIR} ${DLL_DEST} SDL2.dll)
endfunction(copy_yuzu_SDL_deps)

View File

@@ -1,6 +1,3 @@
# SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
# SPDX-License-Identifier: GPL-3.0-or-later
# SPDX-FileCopyrightText: 2019 Citra Emulator Project
# SPDX-License-Identifier: GPL-2.0-or-later
@@ -78,16 +75,16 @@ function(find_ffmpeg LIBNAME)
)
else()
list(APPEND INCLUDE_PATHS
${CMAKE_SYSROOT}/usr/local/include/ffmpeg
${CMAKE_SYSROOT}/usr/local/include/lib${LIBNAME}
${CMAKE_SYSROOT}/usr/include/ffmpeg
${CMAKE_SYSROOT}/usr/include/lib${LIBNAME}
${CMAKE_SYSROOT}/usr/include/ffmpeg/lib${LIBNAME}
/usr/local/include/ffmpeg
/usr/local/include/lib${LIBNAME}
/usr/include/ffmpeg
/usr/include/lib${LIBNAME}
/usr/include/ffmpeg/lib${LIBNAME}
)
list(APPEND LIB_PATHS
${CMAKE_SYSROOT}/usr/local/lib
${CMAKE_SYSROOT}/usr/lib
/usr/local/lib
/usr/lib
)
endif()

View File

@@ -7,9 +7,7 @@
"version": "3.6.0",
"min_version": "1.1.1",
"disabled_platforms": [
"macos-universal",
"mingw-amd64",
"mingw-arm64"
"macos-universal"
]
},
"boost": {
@@ -20,7 +18,7 @@
"hash": "4fb7f6fde92762305aad8754d7643cd918dd1f3f67e104e9ab385b18c73178d72a17321354eb203b790b6702f2cf6d725a5d6e2dfbc63b1e35f9eb59fb42ece9",
"git_version": "1.89.0",
"version": "1.57",
"find_args": "CONFIG OPTIONAL_COMPONENTS headers context system fiber filesystem",
"find_args": "CONFIG",
"patches": [
"0001-clang-cl.patch",
"0002-use-marmasm.patch",

0
dist/dev.eden_emu.eden.desktop vendored Executable file → Normal file
View File

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 9.2 KiB

After

Width:  |  Height:  |  Size: 13 KiB

BIN
dist/eden.icns vendored

Binary file not shown.

BIN
dist/eden.ico vendored

Binary file not shown.

Before

Width:  |  Height:  |  Size: 315 KiB

After

Width:  |  Height:  |  Size: 403 KiB

83
dist/eden_named.svg vendored

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 37 KiB

After

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 29 KiB

After

Width:  |  Height:  |  Size: 38 KiB

View File

@@ -821,6 +821,31 @@ QTabBar QToolButton::left-arrow:disabled {
image: url(:/qss_icons/rc/left_arrow_disabled.png);
}
QDockWidget {
background: #31363b;
border: 1px solid #403F3F;
titlebar-close-icon: url(:/qss_icons/rc/close.png);
titlebar-normal-icon: url(:/qss_icons/rc/undock.png);
}
QDockWidget::close-button,
QDockWidget::float-button {
border: 1px solid transparent;
border-radius: 2px;
background: transparent;
}
QDockWidget::close-button:hover,
QDockWidget::float-button:hover {
background: rgba(255, 255, 255, 10);
}
QDockWidget::close-button:pressed,
QDockWidget::float-button:pressed {
padding: 1px -1px -1px 1px;
background: rgba(255, 255, 255, 10);
}
QTreeView,
QListView {
border: 1px solid #54575B;

View File

@@ -1685,6 +1685,54 @@ QTabBar QToolButton::right-arrow:disabled {
image: url(":/qss_icons/rc/arrow_right_disabled.png");
}
/* QDockWiget -------------------------------------------------------------
--------------------------------------------------------------------------- */
QDockWidget {
outline: 1px solid #32414B;
background-color: #19232D;
border: 1px solid #32414B;
border-radius: 4px;
titlebar-close-icon: url(":/qss_icons/rc/window_close.png");
titlebar-normal-icon: url(":/qss_icons/rc/window_undock.png");
}
QDockWidget::title {
/* Better size for title bar */
padding: 6px;
spacing: 4px;
border: none;
background-color: #32414B;
}
QDockWidget::close-button {
background-color: #32414B;
border-radius: 4px;
border: none;
}
QDockWidget::close-button:hover {
image: url(":/qss_icons/rc/window_close_focus.png");
}
QDockWidget::close-button:pressed {
image: url(":/qss_icons/rc/window_close_pressed.png");
}
QDockWidget::float-button {
background-color: #32414B;
border-radius: 4px;
border: none;
}
QDockWidget::float-button:hover {
image: url(":/qss_icons/rc/window_undock_focus.png");
}
QDockWidget::float-button:pressed {
image: url(":/qss_icons/rc/window_undock_pressed.png");
}
/* QTreeView QListView QTableView -----------------------------------------
https://doc.qt.io/qt-5/stylesheet-examples.html#customizing-qtreeview

BIN
dist/yuzu.bmp vendored

Binary file not shown.

Before

Width:  |  Height:  |  Size: 256 KiB

After

Width:  |  Height:  |  Size: 256 KiB

BIN
dist/yuzu.icns vendored

Binary file not shown.

View File

@@ -9,8 +9,6 @@
- `DISABLED_PLATFORMS`: List of platforms that lack artifacts for this package. Options:
* `windows-amd64`
* `windows-arm64`
* `mingw-amd64`
* `mingw-arm64`
* `android`
* `solaris-amd64`
* `freebsd-amd64`

View File

@@ -1,28 +1,24 @@
# Caveats
<!-- TOC -->
- [Caveats](#caveats)
- [Arch Linux](#arch-linux)
- [Gentoo Linux](#gentoo-linux)
- [macOS](#macos)
- [Solaris](#solaris)
- [HaikuOS](#haikuos)
- [OpenBSD](#openbsd)
- [FreeBSD](#freebsd)
- [NetBSD](#netbsd)
- [MSYS2](#msys2)
- [Windows 8.1 and below](#windows-81-and-below)
- [Arch Linux](#arch-linux)
- [Gentoo Linux](#gentoo-linux)
- [macOS](#macos)
- [Solaris](#solaris)
- [HaikuOS](#haikuos)
- [OpenBSD](#openbsd)
- [FreeBSD](#freebsd)
- [NetBSD](#netbsd)
<!-- /TOC -->
## Arch Linux
Eden is also available as an [AUR package](https://aur.archlinux.org/packages/eden-git). If you are unable to build, either use that or compare your process to the PKGBUILD.
- httplib AUR package is broken. Set `httplib_FORCE_BUNDLED=ON` if you have it installed.
- Eden is also available as an [AUR package](https://aur.archlinux.org/packages/eden-git). If you are unable to build, either use that or compare your process to the PKGBUILD.
## Gentoo Linux
[`games-emulation/eden`](https://gitweb.gentoo.org/repo/proj/guru.git/tree/games-emulation/eden) is available in the GURU. This repository also contains some additional dependencies, such as mcl, sirit, oaknut, etc.
If you're having issues with building, always consult that ebuild.
Do not use the system sirit or xbyak packages.
## macOS
@@ -30,13 +26,6 @@ macOS is largely untested. Expect crashes, significant Vulkan issues, and other
## Solaris
Always consult [the OpenIndiana package list](https://pkg.openindiana.org/hipster/en/index.shtml) to cross-verify availability.
Run the usual update + install of essential toolings: `sudo pkg update && sudo pkg install git cmake`.
- **gcc**: `sudo pkg install developer/gcc-14`.
- **clang**: Version 20 is broken, use `sudo pkg install developer/clang-19`.
Qt Widgets appears to be broken. For now, add `-DENABLE_QT=OFF` to your configure command. In the meantime, a Qt Quick frontend is in the works--check back later!
This is needed for some dependencies that call cc directly (tz):
@@ -81,7 +70,7 @@ Still will not run flawlessly until `mesa-24` is available. Modify CMakeCache.tx
After configuration, you may need to modify `externals/ffmpeg/CMakeFiles/ffmpeg-build/build.make` to use `-j$(nproc)` instead of just `-j`.
`-lc++-experimental` doesn't exist in OpenBSD but the LLVM driver still tries to link against it, to solve just symlink `ln -s /usr/lib/libc++.a /usr/lib/libc++experimental.a`. Builds are currently not working due to lack of `std::jthread` and such, either compile libc++ manually or wait for ports to catch up.
`-lc++-experimental` doesn't exist in OpenBSD but the LLVM driver still tries to link against it, to solve just symlink `ln -s /usr/lib/libc++.a /usr/lib/libc++experimental.a`.
If clang has errors, try using `g++-11`.
@@ -113,87 +102,3 @@ cmake -B build -DCMAKE_BUILD_TYPE=Release
cmake --build build -- -j`nproc`
cmake --install build
```
# DragonFlyBSD
If `libstdc++.so.6` is not found (`GLIBCXX_3.4.30`) then attempt:
```sh
rm /usr/local/lib/gcc11/libstdc++.so.6
ln -s /usr/local/lib/gcc14/libstdc++.so /usr/local/lib/gcc11/libstdc++.so.6
```
This may have unforeseen consequences of which we don't need to worry about for now.
Default `g++` (and the libstdc++) is too outdated - so install `gcc14` and redirect CMake to the new compiler toolchain `-DCMAKE_CXX_COMPILER=gcc14 -DCMAKE_C_COMPILER=g++14`.
There is also `llvm18` and use `-DCMAKE_CXX_COMPILER=clang++18 -DCMAKE_C_COMPILER=clang18` (note the `18` suffix at the end). NOTE: It doesn't have an updated libcxx so `<span>` will be missing, either build it manually or use gcc.
If build hangs, use `hammer2 bulkfree`.
## MSYS2
`qt6-static` isn't supported yet.
Only the `MINGW64` environment is tested; however, all of the others should work (in theory) sans `MINGW32`.
Currently, only FFmpeg can be used as a system dependency; the others will result in linker errors.
When packaging an MSYS2 build, you will need to copy all dependent DLLs recursively alongside the `windeployqt6`; for example:
```sh
# MSYS_TOOLCHAIN is typically just mingw64
# since Windows is case-insensitive, you can set this to $MSYSTEM
# or, if cross-compiling from Linux, set it to usr/x86_64-w64-mingw32
export PATH="/${MSYS_TOOLCHAIN}/bin:$PATH"
# grab deps of a dll or exe and place them in the current dir
deps() {
# string parsing is fun
objdump -p "$1" | awk '/DLL Name:/ {print $3}' | while read -r dll; do
[ -z "$dll" ] && continue
# bin directory is used for DLLs, so we can do a quick "hack"
# and use command to find the path of the DLL
dllpath=$(command -v "$dll" 2>/dev/null || true)
[ -z "$dllpath" ] && continue
# explicitly exclude system32/syswow64 deps
# these aren't needed to be bundled, as all systems already have them
case "$dllpath" in
*System32* | *SysWOW64*) continue ;;
esac
# avoid copying deps multiple times
if [ ! -f "$dll" ]; then
echo "$dllpath"
cp "$dllpath" "$dll"
# also grab the dependencies of the dependent DLL; e.g.
# double-conversion is a dep of Qt6Core.dll but NOT eden.exe
deps "$dllpath"
fi
done
}
# NB: must be done in a directory containing eden.exe
deps eden.exe
# deploy Qt plugins and such
windeployqt6 --no-compiler-runtime --no-opengl-sw --no-system-dxc-compiler \
--no-system-d3d-compiler eden.exe
# grab deps for Qt plugins
find ./*/ -name "*.dll" | while read -r dll; do deps "$dll"; done
```
## Windows 8.1 and below
DirectX 12 is not available - simply copy and paste a random DLL and name it `d3d12.dll`.
Install [Qt6 compatibility libraries](github.com/ANightly/qt6windows7) specifically Qt 6.9.5.
## RedoxOS
The package install may randomly hang at times, in which case it has to be restarted. ALWAYS do a `sudo pkg update` or the chances of it hanging will be close to 90%. If "multiple" installs fail at once, try installing 1 by 1 the packages.
When CMake invokes certain file syscalls - it may sometimes cause crashes or corruptions on the (kernel?) address space - so reboot the system if there is a "hang" in CMake.

View File

@@ -8,8 +8,6 @@ But for new developers you may find that following these guidelines will make ev
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`.
Except for Qt MOC where `functionName` is preferred.
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`.

View File

@@ -87,53 +87,6 @@ Notes for writers: Include build tools as well, assume user has NOTHING installe
Click on the arrows to expand.
<details>
<summary>Gentoo Linux</summary>
GURU must be enabled:
```
sudo emerge -a app-eselect/eselect-repository
sudo eselect repository enable guru
sudo emaint sync -r guru
```
Now, install all deps:
```sh
sudo emerge -a \
app-arch/lz4 app-arch/zstd app-arch/unzip \
dev-libs/libfmt dev-libs/libusb dev-libs/mcl dev-libs/sirit \
dev-libs/unordered_dense dev-libs/boost dev-libs/openssl dev-libs/discord-rpc \
dev-util/spirv-tools dev-util/spirv-headers dev-util/vulkan-headers \
dev-util/vulkan-utility-libraries dev-util/glslang \
media-gfx/renderdoc media-libs/libva media-libs/opus media-video/ffmpeg \
media-libs/VulkanMemoryAllocator media-libs/libsdl2 media-libs/cubeb \
net-libs/enet net-libs/mbedtls \
sys-libs/zlib \
dev-cpp/nlohmann_json dev-cpp/simpleini dev-cpp/cpp-httplib dev-cpp/cpp-jwt \
games-util/gamemode \
net-wireless/wireless-tools \
dev-qt/qtbase:6 dev-libs/quazip \
virtual/pkgconfig
```
- On `amd64`, also add `dev-libs/xbyak`
- On `riscv64`, also add `dev-libs/biscuit` (currently unavailable)
- On `aarch64`, also add `dev-libs/oaknut`
- If tests are enabled, also add `dev-libs/oaknut` and `dev-cpp/catch`
Required USE flags:
- `dev-qt/qtbase network concurrent dbus gui widgets`
- `dev-libs/quazip qt6`
- `net-libs/mbedtls cmac`
- `media-libs/libsdl2 haptic joystick sound video`
- `dev-cpp/cpp-httplib ssl`
[Caveats](./Caveats.md#gentoo-linux)
</details>
<details>
<summary>Arch Linux</summary>
@@ -196,7 +149,6 @@ apk add g++ git cmake make mbedtls-dev mbedtls-static mesa-dev qt6-qtbase-dev qt
`mbedtls-static` has to be specified otherwise `libeverest.a` and `libp256m.a` will fail to be found.
</details>
<details>
<summary>Void Linux</summary>
```sh
@@ -206,7 +158,6 @@ xbps-install -Su git make cmake clang pkg-config patch mbedtls-devel SPIRV-Tools
Yes, `nlohmann-json` is just named `json-c++`. Why?
</details>
<details>
<summary>NixOS</summary>
@@ -235,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 mbedtls3 vulkan-headers quazip-qt6`
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.
@@ -260,23 +211,18 @@ pkg_add cmake nasm git boost unzip--iconv autoconf-2.72p0 bash ffmpeg glslang gm
[Caveats](./Caveats.md#openbsd).
</details>
<details>
<summary>DragonFlyBSD</summary>
```sh
pkg install gcc14 git cmake unzip nasm autoconf bash pkgconf ffmpeg glslang gmake jq nlohmann-json enet spirv-tools sdl2 vulkan-utility-libraries vulkan-headers catch2 libfmt openssl liblz4 boost-libs cpp-httplib qt6-base quazip-qt6 unordered-dense libva-vdpau-driver libva-utils libva-intel-driver
```
[Caveats](./Caveats.md#dragonflybsd).
</details>
<details>
<summary>Solaris / OpenIndiana</summary>
```sh
sudo pkg install qt6 boost glslang libzip library/lz4 libusb-1 nlohmann-json openssl opus sdl2 zlib compress/zstd unzip pkg-config nasm autoconf mesa library/libdrm header-drm developer/fmt
```
Always consult [the OpenIndiana package list](https://pkg.openindiana.org/hipster/en/index.shtml) to cross-verify availability.
Run the usual update + install of essential toolings: `sudo pkg update && sudo pkg install git cmake`.
- **gcc**: `sudo pkg install developer/gcc-14`.
- **clang**: Version 20 is broken, use `sudo pkg install developer/clang-19`.
Then install the libraries: `sudo pkg install qt6 boost glslang libzip library/lz4 libusb-1 nlohmann-json openssl opus sdl2 zlib compress/zstd unzip pkg-config nasm autoconf mesa library/libdrm header-drm developer/fmt`.
[Caveats](./Caveats.md#solaris).
@@ -288,27 +234,24 @@ sudo pkg install qt6 boost glslang libzip library/lz4 libusb-1 nlohmann-json ope
* Download and install all dependencies:
```
BASE="git make autoconf libtool automake-wrapper jq patch"
MINGW="qt6-base qt6-tools qt6-translations qt6-svg cmake toolchain clang python-pip openssl vulkan-memory-allocator vulkan-devel glslang boost fmt lz4 nlohmann-json zlib zstd enet opus mbedtls libusb unordered_dense"
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 -Syuu --needed --noconfirm $packages
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.
- GCC is proven to work better with the MinGW environment. If you choose to use Clang, you *may* be better off using the clang64 environment.
- 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`
[Caveats](./Caveats.md#msys2).
</details>
<details>
<summary>HaikuOS</summary>
@@ -320,16 +263,14 @@ pkgman install git cmake patch libfmt_devel nlohmann_json lz4_devel opus_devel b
[Caveats](./Caveats.md#haikuos).
</details>
<details>
<summary>RedoxOS</summary>
TODO: Fix syscall crashes (heavy IO stalls and hangup due to net mutexes?)
```sh
sudo pkg update
sudo pkg install git cmake ffmpeg6 sdl2 zlib llvm18
sudo pkg update && sudo pkg install git cmake
sudo pkg install ffmpeg6 sdl2 zlib llvm18
```
[Caveats](./Caveats.md#redoxos).
</details>
## All Done

View File

@@ -23,11 +23,7 @@
"package": "sirit",
"name": "sirit",
"repo": "eden-emulator/sirit",
"version": "1.0.2",
"disabled_platforms": [
"mingw-amd64",
"mingw-arm64"
]
"version": "1.0.2"
},
"httplib": {
"repo": "yhirose/cpp-httplib",

View File

@@ -107,10 +107,9 @@ endif()
if (YUZU_USE_BUNDLED_FFMPEG)
# MSVC conflicts with ksuser otherwise
# MinGW has the funny quirk of requiring avutil after avcodec
# Android needs some deps to be compiled with PIC (TODO)
# TODO(crueter) fix
if (ANDROID)
if (MSVC OR ANDROID)
set(BUILD_SHARED_LIBS ON)
else()
set(BUILD_SHARED_LIBS OFF)

View File

@@ -15,7 +15,6 @@
"disabled_platforms": [
"freebsd-amd64",
"solaris-amd64",
"openbsd-amd64",
"macos-universal"
]
}

View File

@@ -9,15 +9,11 @@ pkgs.mkShellNoCC {
# libraries
openssl boost fmt nlohmann_json lz4 zlib zstd
enet libopus vulkan-headers vulkan-utility-libraries
spirv-tools spirv-headers vulkan-loader unzip mbedtls
glslang python3 httplib cpp-jwt ffmpeg-headless
libusb1 cubeb
# eden
qt6.qtbase qt6.qtmultimedia qt6.qtwayland qt6.qttools
qt6.qtwebengine qt6.qt5compat
# eden-cli
SDL2
# optional components
discord-rpc gamemode
spirv-tools spirv-headers simpleini vulkan-memory-allocator
vulkan-loader unzip mbedtls glslang python3 httplib
cpp-jwt ffmpeg-headless libusb1 cubeb
qt6.full # eden
SDL2 # eden-cli
discord-rpc gamemode # optional components
];
}

View File

@@ -178,10 +178,8 @@ else()
if (MINGW)
add_compile_definitions(MINGW_HAS_SECURE_API)
# Only windows has this requirement, thanks windows
if (WIN32)
add_compile_options("-msse4.1")
endif()
add_compile_options("-msse4.1")
if (MINGW_STATIC_BUILD)
add_compile_definitions(QT_STATICPLUGIN)
add_compile_options("-static")

View File

@@ -1511,9 +1511,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
emulationState.newSurface(holder.surface)
} else {
// Surface changed due to rotation/config change
// Only update surface reference, don't trigger state changes
emulationState.updateSurfaceReference(holder.surface)
emulationState.newSurface(holder.surface)
}
}
@@ -1844,14 +1842,6 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
}
}
@Synchronized
fun updateSurfaceReference(surface: Surface?) {
this.surface = surface
if (this.surface != null && state == State.RUNNING) {
NativeLibrary.surfaceChanged(this.surface)
}
}
@Synchronized
fun clearSurface() {
if (surface == null) {

Binary file not shown.

Before

Width:  |  Height:  |  Size: 65 KiB

After

Width:  |  Height:  |  Size: 74 KiB

View File

@@ -84,7 +84,6 @@ static void UpdateReverbEffectParameter(const ReverbInfo::ParameterVersion2& par
const auto pow_10 = [](f32 val) -> f32 {
return (val >= 0.0f) ? 1.0f : (val <= -5.3f) ? 0.0f : std::pow(10.0f, val);
};
const auto cos = [](f32 degrees) -> f32 {
return std::cos(degrees * std::numbers::pi_v<f32> / 180.0f);
};
@@ -92,39 +91,26 @@ static void UpdateReverbEffectParameter(const ReverbInfo::ParameterVersion2& par
static bool unk_initialized{false};
static Common::FixedPoint<50, 14> unk_value{};
auto sample_rate = Common::FixedPoint<50, 14>::from_base(params.sample_rate);
if (sample_rate.to_float() < 1.0f) sample_rate = Common::FixedPoint<50, 14>(1.0f);
auto pre_delay_time = Common::FixedPoint<50, 14>::from_base(params.pre_delay);
if (pre_delay_time.to_float() < 0.0f) pre_delay_time = Common::FixedPoint<50, 14>(0.0f);
const auto sample_rate{Common::FixedPoint<50, 14>::from_base(params.sample_rate)};
const auto pre_delay_time{Common::FixedPoint<50, 14>::from_base(params.pre_delay)};
for (u32 i = 0; i < ReverbInfo::MaxDelayTaps; i++) {
int target_delay = ((pre_delay_time + EarlyDelayTimes[params.early_mode][i]) * sample_rate).to_int();
if (target_delay < 0) target_delay = 0;
if (target_delay > state.pre_delay_line.sample_count_max) target_delay = state.pre_delay_line.sample_count_max;
int old_delay = state.early_delay_times[i] - 1;
int smooth_delay = old_delay + (target_delay - old_delay) / 4;
state.early_delay_times[i] = smooth_delay + 1;
auto target_gain = Common::FixedPoint<50, 14>::from_base(params.early_gain) * EarlyDelayGains[params.early_mode][i];
if (target_gain.to_float() < 0.0f) target_gain = Common::FixedPoint<50, 14>(0.0f);
if (target_gain.to_float() > 1.0f) target_gain = Common::FixedPoint<50, 14>(1.0f);
state.early_gains[i] = state.early_gains[i] + (target_gain - state.early_gains[i]) / 4;
auto early_delay{
((pre_delay_time + EarlyDelayTimes[params.early_mode][i]) * sample_rate).to_int()};
early_delay = (std::min)(early_delay, state.pre_delay_line.sample_count_max);
state.early_delay_times[i] = early_delay + 1;
state.early_gains[i] = Common::FixedPoint<50, 14>::from_base(params.early_gain) *
EarlyDelayGains[params.early_mode][i];
}
if (params.channel_count == 2) {
state.early_gains[4] *= Common::FixedPoint<50, 14>(0.5f);
state.early_gains[5] *= Common::FixedPoint<50, 14>(0.5f);
state.early_gains[4] * 0.5f;
state.early_gains[5] * 0.5f;
}
int target_pre_time = ((pre_delay_time + EarlyDelayTimes[params.early_mode][10]) * sample_rate).to_int();
if (target_pre_time < 0) target_pre_time = 0;
if (target_pre_time > state.pre_delay_line.sample_count_max) target_pre_time = state.pre_delay_line.sample_count_max;
int old_pre_time = state.pre_delay_time;
state.pre_delay_time = old_pre_time + (target_pre_time - old_pre_time) / 4;
auto pre_time{
((pre_delay_time + EarlyDelayTimes[params.early_mode][10]) * sample_rate).to_int()};
state.pre_delay_time = (std::min)(pre_time, state.pre_delay_line.sample_count_max);
if (!unk_initialized) {
unk_value = cos((1280.0f / sample_rate).to_float());
@@ -132,64 +118,45 @@ static void UpdateReverbEffectParameter(const ReverbInfo::ParameterVersion2& par
}
for (u32 i = 0; i < ReverbInfo::MaxDelayLines; i++) {
int target_fdn_delay = (FdnDelayTimes[params.late_mode][i] * sample_rate).to_int();
if (target_fdn_delay < 0) target_fdn_delay = 0;
if (target_fdn_delay > state.fdn_delay_lines[i].sample_count_max) target_fdn_delay = state.fdn_delay_lines[i].sample_count_max;
const auto fdn_delay{(FdnDelayTimes[params.late_mode][i] * sample_rate).to_int()};
state.fdn_delay_lines[i].sample_count =
(std::min)(fdn_delay, state.fdn_delay_lines[i].sample_count_max);
state.fdn_delay_lines[i].buffer_end =
&state.fdn_delay_lines[i].buffer[state.fdn_delay_lines[i].sample_count - 1];
int old_fdn = state.fdn_delay_lines[i].sample_count;
state.fdn_delay_lines[i].sample_count = old_fdn + (target_fdn_delay - old_fdn) / 4;
state.fdn_delay_lines[i].buffer_end = &state.fdn_delay_lines[i].buffer[state.fdn_delay_lines[i].sample_count - 1];
const auto decay_delay{(DecayDelayTimes[params.late_mode][i] * sample_rate).to_int()};
state.decay_delay_lines[i].sample_count =
(std::min)(decay_delay, state.decay_delay_lines[i].sample_count_max);
state.decay_delay_lines[i].buffer_end =
&state.decay_delay_lines[i].buffer[state.decay_delay_lines[i].sample_count - 1];
int target_decay_delay = (DecayDelayTimes[params.late_mode][i] * sample_rate).to_int();
if (target_decay_delay < 0) target_decay_delay = 0;
if (target_decay_delay > state.decay_delay_lines[i].sample_count_max) target_decay_delay = state.decay_delay_lines[i].sample_count_max;
state.decay_delay_lines[i].decay =
0.5999755859375f * (1.0f - Common::FixedPoint<50, 14>::from_base(params.colouration));
int old_decay = state.decay_delay_lines[i].sample_count;
state.decay_delay_lines[i].sample_count = old_decay + (target_decay_delay - old_decay) / 4;
state.decay_delay_lines[i].buffer_end = &state.decay_delay_lines[i].buffer[state.decay_delay_lines[i].sample_count - 1];
auto a{(Common::FixedPoint<50, 14>(state.fdn_delay_lines[i].sample_count_max) +
state.decay_delay_lines[i].sample_count_max) *
-3};
auto b{a / (Common::FixedPoint<50, 14>::from_base(params.decay_time) * sample_rate)};
Common::FixedPoint<50, 14> c{0.0f};
Common::FixedPoint<50, 14> d{0.0f};
auto hf_decay_ratio{Common::FixedPoint<50, 14>::from_base(params.high_freq_decay_ratio)};
auto colouration = Common::FixedPoint<50, 14>::from_base(params.colouration);
if (colouration.to_float() < 0.0f) colouration = Common::FixedPoint<50, 14>(0.0f);
if (colouration.to_float() > 1.0f) colouration = Common::FixedPoint<50, 14>(1.0f);
state.decay_delay_lines[i].decay = state.decay_delay_lines[i].decay + (0.5999755859375f * (1.0f - colouration) - state.decay_delay_lines[i].decay) / 4;
auto decay_time_fp = Common::FixedPoint<50, 14>::from_base(params.decay_time);
if (decay_time_fp.to_float() <= 0.0f) decay_time_fp = Common::FixedPoint<50, 14>(0.0001f);
auto a = (Common::FixedPoint<50, 14>(state.fdn_delay_lines[i].sample_count_max) + state.decay_delay_lines[i].sample_count_max) * -3;
auto b = a / (decay_time_fp * sample_rate);
auto hf_decay_ratio = Common::FixedPoint<50, 14>::from_base(params.high_freq_decay_ratio);
if (hf_decay_ratio.to_float() < 0.001f) hf_decay_ratio = Common::FixedPoint<50, 14>(0.001f);
if (hf_decay_ratio.to_float() > 0.999f) hf_decay_ratio = Common::FixedPoint<50, 14>(0.999f);
Common::FixedPoint<50, 14> c{0.0f}, d{0.0f};
if (hf_decay_ratio.to_float() > 0.9949f) {
if (hf_decay_ratio > 0.99493408203125f) {
c = 0.0f;
d = 1.0f;
} else {
auto e = pow_10(((((1.0f / hf_decay_ratio.to_float()) - 1.0f) * 2) / 100 * (b / 10)).to_float());
if (e < 0.0001f) e = 0.0001f;
if (e > 1.0f) e = 1.0f;
auto f = 1.0f - e;
if (f < 0.0001f) f = 0.0001f;
auto g = 2.0f - (unk_value.to_float() * e * 2);
auto h_sq = g * g - f * f * 4;
if (h_sq < 0.0f) h_sq = 0.0f;
auto h = std::sqrt(h_sq);
const auto e{
pow_10(((((1.0f / hf_decay_ratio) - 1.0f) * 2) / 100 * (b / 10)).to_float())};
const auto f{1.0f - e};
const auto g{2.0f - (unk_value * e * 2)};
const auto h{std::sqrt(std::pow(g.to_float(), 2.0f) - (std::pow(f, 2.0f) * 4))};
c = (g - h) / (f * 2.0f);
d = 1.0f - c;
}
state.hf_decay_prev_gain[i] = state.hf_decay_prev_gain[i] + (c - state.hf_decay_prev_gain[i]) / 4;
state.hf_decay_gain[i] = state.hf_decay_gain[i] + (pow_10((b / 1000).to_float()) * d * 0.70709228515625f - state.hf_decay_gain[i]) / 4;
state.hf_decay_prev_gain[i] = c;
state.hf_decay_gain[i] = pow_10((b / 1000).to_float()) * d * 0.70709228515625f;
state.prev_feedback_output[i] = 0;
}
}
@@ -224,8 +191,6 @@ static void InitializeReverbEffect(const ReverbInfo::ParameterVersion2& params,
const auto center_delay_time{(5 * delay).to_uint_floor()};
state.center_delay_line.Initialize(center_delay_time, 1.0f);
UpdateReverbEffectParameter(params, state);
for (u32 i = 0; i < ReverbInfo::MaxDelayLines; i++) {
std::ranges::fill(state.fdn_delay_lines[i].buffer, 0);
std::ranges::fill(state.decay_delay_lines[i].buffer, 0);

View File

@@ -220,8 +220,8 @@ u64 SinkStream::GetExpectedPlayedSampleCount() {
auto time_delta{cur_time - last_sample_count_update_time};
auto exp_played_sample_count{min_played_sample_count + (TargetSampleRate * time_delta) / std::chrono::seconds{1}};
// Add 25ms of latency in sample reporting to allow for some leeway in scheduler timings
return std::min<u64>(exp_played_sample_count, max_played_sample_count) + TargetSampleCount * 5;
// Add 15ms of latency in sample reporting to allow for some leeway in scheduler timings
return std::min<u64>(exp_played_sample_count, max_played_sample_count) + TargetSampleCount * 3;
}
void SinkStream::WaitFreeSpace(std::stop_token stop_token) {
@@ -231,9 +231,9 @@ void SinkStream::WaitFreeSpace(std::stop_token stop_token) {
return paused || queued_buffers < max_queue_size;
};
release_cv.wait_for(lk, std::chrono::milliseconds(7), can_continue);
release_cv.wait_for(lk, std::chrono::milliseconds(10), can_continue);
if (queued_buffers > max_queue_size + 3) {
if (queued_buffers > max_queue_size + 10) {
release_cv.wait(lk, stop_token, can_continue);
}
}

View File

@@ -252,13 +252,11 @@ if(CXX_CLANG)
endif()
if (BOOST_NO_HEADERS)
target_link_libraries(common PUBLIC Boost::algorithm Boost::icl Boost::pool Boost::filesystem)
target_link_libraries(common PUBLIC Boost::algorithm Boost::icl Boost::pool)
else()
target_link_libraries(common PUBLIC Boost::headers)
endif()
target_link_libraries(common PUBLIC Boost::filesystem)
if (lz4_ADDED)
target_include_directories(common PRIVATE ${lz4_SOURCE_DIR}/lib)
endif()

View File

@@ -1,35 +1,21 @@
// 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
#include "common/assert.h"
#include "common/common_funcs.h"
#include "common/logging/backend.h"
#include "common/settings.h"
#ifdef _MSC_VER
extern "C" {
__declspec(dllimport) void __stdcall DebugBreak(void);
}
#endif
void AssertFailSoftImpl() {
void assert_fail_impl() {
if (Settings::values.use_debug_asserts) {
Common::Log::Stop();
#ifndef _MSC_VER
# if defined(ARCHITECTURE_x86_64)
__asm__ __volatile__("int $3");
# elif defined(ARCHITECTURE_arm64)
__asm__ __volatile__("brk #0");
# else
exit(1);
# endif
#else // POSIX ^^^ _MSC_VER vvv
DebugBreak();
#endif
Crash();
}
}
void AssertFatalImpl() {
[[noreturn]] void unreachable_impl() {
Common::Log::Stop();
std::abort();
Crash();
throw std::runtime_error("Unreachable code");
}

View File

@@ -1,6 +1,3 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: 2013 Dolphin Emulator Project
// SPDX-FileCopyrightText: 2014 Citra Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@@ -13,8 +10,8 @@
// However touching this file yields a global recompilation as this header is included almost
// everywhere. So let's just move the handling of the failed assert to a single cpp file.
void AssertFailSoftImpl();
[[noreturn]] void AssertFatalImpl();
void assert_fail_impl();
[[noreturn]] void unreachable_impl();
#ifdef _MSC_VER
#define YUZU_NO_INLINE __declspec(noinline)
@@ -25,29 +22,29 @@ void AssertFailSoftImpl();
#define ASSERT(_a_) \
([&]() YUZU_NO_INLINE { \
if (!(_a_)) [[unlikely]] { \
LOG_CRITICAL(Debug, "Assert"); \
AssertFailSoftImpl(); \
LOG_CRITICAL(Debug, "Assertion Failed!"); \
assert_fail_impl(); \
} \
}())
#define ASSERT_MSG(_a_, ...) \
([&]() YUZU_NO_INLINE { \
if (!(_a_)) [[unlikely]] { \
LOG_CRITICAL(Debug, "Assert\n" __VA_ARGS__); \
AssertFailSoftImpl(); \
LOG_CRITICAL(Debug, "Assertion Failed!\n" __VA_ARGS__); \
assert_fail_impl(); \
} \
}())
#define UNREACHABLE() \
do { \
LOG_CRITICAL(Debug, "Unreachable"); \
AssertFatalImpl(); \
LOG_CRITICAL(Debug, "Unreachable code!"); \
unreachable_impl(); \
} while (0)
#define UNREACHABLE_MSG(...) \
do { \
LOG_CRITICAL(Debug, "Unreachable\n" __VA_ARGS__); \
AssertFatalImpl(); \
LOG_CRITICAL(Debug, "Unreachable code!\n" __VA_ARGS__); \
unreachable_impl(); \
} while (0)
#ifdef _DEBUG

View File

@@ -32,9 +32,27 @@
#define INSERT_PADDING_WORDS_NOINIT(num_words) \
[[maybe_unused]] std::array<u32, num_words> CONCAT2(pad, __LINE__)
#ifdef _MSC_VER
# define locale_t _locale_t // Locale Cross-Compatibility
#endif // _MSC_VER
#ifndef _MSC_VER
#if defined(ARCHITECTURE_x86_64)
#define Crash() __asm__ __volatile__("int $3")
#elif defined(ARCHITECTURE_arm64)
#define Crash() __asm__ __volatile__("brk #0")
#else
#define Crash() exit(1)
#endif
#else // _MSC_VER
// Locale Cross-Compatibility
#define locale_t _locale_t
extern "C" {
__declspec(dllimport) void __stdcall DebugBreak(void);
}
#define Crash() DebugBreak()
#endif // _MSC_VER ndef
#define DECLARE_ENUM_FLAG_OPERATORS(type) \
[[nodiscard]] constexpr type operator|(type a, type b) noexcept { \

View File

@@ -1,6 +1,3 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@@ -15,4 +12,24 @@ namespace Common {
template <typename T>
concept IsContiguousContainer = std::contiguous_iterator<typename T::iterator>;
// TODO: Replace with std::derived_from when the <concepts> header
// is available on all supported platforms.
template <typename Derived, typename Base>
concept DerivedFrom = requires {
std::is_base_of_v<Base, Derived>;
std::is_convertible_v<const volatile Derived*, const volatile Base*>;
};
// TODO: Replace with std::convertible_to when libc++ implements it.
template <typename From, typename To>
concept ConvertibleTo = std::is_convertible_v<From, To>;
// No equivalents in the stdlib
template <typename T>
concept IsArithmetic = std::is_arithmetic_v<T>;
template <typename T>
concept IsIntegral = std::is_integral_v<T>;
} // namespace Common

View File

@@ -1,6 +1,3 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: 2015 Evan Teran
// SPDX-License-Identifier: MIT
@@ -19,10 +16,6 @@
namespace Common {
// No equivalent for "std::arithmetic" in the stdlib
template <typename T>
concept IsArithmetic = std::is_arithmetic_v<T>;
template <size_t I, size_t F>
class FixedPoint;
@@ -399,13 +392,13 @@ public: // binary math operators, effects underlying bit pattern since these
return *this;
}
template <std::integral Integer>
template <IsIntegral Integer>
constexpr FixedPoint& operator>>=(Integer n) {
data_ >>= n;
return *this;
}
template <std::integral Integer>
template <IsIntegral Integer>
constexpr FixedPoint& operator<<=(Integer n) {
data_ <<= n;
return *this;
@@ -594,12 +587,12 @@ constexpr FixedPoint<I, F> operator/(Number lhs, FixedPoint<I, F> rhs) {
}
// shift operators
template <size_t I, size_t F, std::integral Integer>
template <size_t I, size_t F, IsIntegral Integer>
constexpr FixedPoint<I, F> operator<<(FixedPoint<I, F> lhs, Integer rhs) {
lhs <<= rhs;
return lhs;
}
template <size_t I, size_t F, std::integral Integer>
template <size_t I, size_t F, IsIntegral Integer>
constexpr FixedPoint<I, F> operator>>(FixedPoint<I, F> lhs, Integer rhs) {
lhs >>= rhs;
return lhs;

View File

@@ -14,29 +14,16 @@ namespace fs = std::filesystem;
fs::path GetKvdbPath()
{
return GetKvdbPath(GetLegacyPath(EmuPath::RyujinxDir));
}
fs::path GetKvdbPath(const fs::path& path) {
return path / "bis" / "system" / "save" / "8000000000000000" / "0"
return GetLegacyPath(EmuPath::RyujinxDir) / "bis" / "system" / "save" / "8000000000000000" / "0"
/ "imkvdb.arc";
}
fs::path GetRyuPathFromSavePath(const fs::path& path) {
// This is a horrible hack, but I cba to find something better
return path.parent_path().parent_path().parent_path().parent_path().parent_path();
}
fs::path GetRyuSavePath(const u64 &save_id)
{
return GetRyuSavePath(GetLegacyPath(EmuPath::RyujinxDir), save_id);
}
std::filesystem::path GetRyuSavePath(const std::filesystem::path& path, const u64& save_id) {
std::string hex = fmt::format("{:016x}", save_id);
// TODO: what's the difference between 0 and 1?
return path / "bis" / "user" / "save" / hex / "0";
// 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)

View File

@@ -7,17 +7,16 @@
#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;
std::filesystem::path GetKvdbPath();
std::filesystem::path GetKvdbPath(const std::filesystem::path &path);
std::filesystem::path GetRyuPathFromSavePath(const std::filesystem::path &path);
std::filesystem::path GetRyuSavePath(const u64 &save_id);
std::filesystem::path GetRyuSavePath(const std::filesystem::path &path, const u64 &save_id);
fs::path GetKvdbPath();
fs::path GetRyuSavePath(const u64 &program_id);
enum class IMENReadResult {
Nonexistent, // ryujinx not found
@@ -36,6 +35,6 @@ struct IMEN
static_assert(sizeof(IMEN) == 0x10, "IMEN has incorrect size.");
IMENReadResult ReadKvdb(const std::filesystem::path &path, std::vector<IMEN> &imens);
IMENReadResult ReadKvdb(const fs::path &path, std::vector<IMEN> &imens);
} // namespace Common::FS

View File

@@ -4,12 +4,10 @@
#include "symlink.h"
#ifdef _WIN32
#include <fmt/format.h>
#include <windows.h>
#include <fmt/format.h>
#endif
#include <boost/filesystem.hpp>
namespace fs = std::filesystem;
// The sole purpose of this file is to treat symlinks like symlinks on POSIX,
@@ -17,40 +15,29 @@ namespace fs = std::filesystem;
// 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.
// And no, they do NOT provide a standard API for this (at least to my knowledge).
// CreateSymbolicLink, even when EXPLICITLY TOLD to create a junction, still fails
// because of their security policy.
// I don't know what kind of drugs the Windows developers have been on since NT started.
// Microsoft still has not implemented any of this in their std::filesystem implemenation,
// which ALSO means that it DOES NOT FOLLOW ANY DIRECTORY JUNCTIONS... AT ALL.
// Nor does any of their command line utilities or APIs. So you're quite literally
// on your own.
namespace Common::FS {
bool CreateSymlink(fs::path from, fs::path to)
bool CreateSymlink(const fs::path &from, const fs::path &to)
{
from.make_preferred();
to.make_preferred();
#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);
#ifdef _WIN32
if (ec) {
const std::string command = fmt::format("mklink /J \"{}\" \"{}\"",
to.string(),
from.string());
return system(command.c_str()) == 0;
}
#endif
return !ec;
#endif
}
bool IsSymlink(const fs::path &path)
{
return boost::filesystem::is_symlink(boost::filesystem::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

View File

@@ -6,7 +6,7 @@
#include <filesystem>
namespace Common::FS {
bool CreateSymlink(std::filesystem::path from, std::filesystem::path to);
bool CreateSymlink(const std::filesystem::path &from, const std::filesystem::path &to);
bool IsSymlink(const std::filesystem::path &path);
} // namespace Common::FS

View File

@@ -521,29 +521,14 @@ public:
#else
fd = memfd_create("HostMemory", 0);
#endif
bool use_anon = false;
if (fd <= 0) {
LOG_WARNING(Common_Memory, "memfd_create: {}", strerror(errno));
use_anon = true;
}
if (!use_anon) {
// Defined to extend the file with zeros
int ret = ftruncate(fd, backing_size);
if (ret != 0) {
LOG_WARNING(Common_Memory, "ftruncate: {} (likely out-of-emory)", strerror(errno));
use_anon = true;
}
}
if (use_anon) {
LOG_WARNING(Common_Memory, "Using private mappings instead of shared ones");
backing_base = static_cast<u8*>(mmap(nullptr, backing_size, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0));
if (fd > 0) {
fd = -1;
close(fd);
}
} else {
backing_base = static_cast<u8*>(mmap(nullptr, backing_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0));
}
ASSERT_MSG(fd >= 0, "memfd_create failed: {}", strerror(errno));
// Defined to extend the file with zeros
int ret = ftruncate(fd, backing_size);
ASSERT_MSG(ret == 0, "ftruncate failed with {}, are you out-of-memory?", strerror(errno));
backing_base = static_cast<u8*>(
mmap(nullptr, backing_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0));
ASSERT_MSG(backing_base != MAP_FAILED, "mmap failed: {}", strerror(errno));
// Virtual memory initialization
@@ -567,18 +552,22 @@ public:
free_manager.AllocateBlock(virtual_base + virtual_offset, length);
// Deduce mapping protection flags.
int prot_flags = PROT_NONE;
if (True(perms & MemoryPermission::Read))
prot_flags |= PROT_READ;
if (True(perms & MemoryPermission::Write))
prot_flags |= PROT_WRITE;
int flags = PROT_NONE;
if (True(perms & MemoryPermission::Read)) {
flags |= PROT_READ;
}
if (True(perms & MemoryPermission::Write)) {
flags |= PROT_WRITE;
}
#ifdef ARCHITECTURE_arm64
if (True(perms & MemoryPermission::Execute))
prot_flags |= PROT_EXEC;
if (True(perms & MemoryPermission::Execute)) {
flags |= PROT_EXEC;
}
#endif
int flags = (fd > 0 ? MAP_SHARED : MAP_PRIVATE) | MAP_FIXED;
void* ret = mmap(virtual_base + virtual_offset, length, prot_flags, flags, fd, host_offset);
ASSERT_MSG(ret != MAP_FAILED, "mmap: {}", strerror(errno));
void* ret = mmap(virtual_base + virtual_offset, length, flags, MAP_SHARED | MAP_FIXED, fd,
host_offset);
ASSERT_MSG(ret != MAP_FAILED, "mmap failed: {}", strerror(errno));
}
void Unmap(size_t virtual_offset, size_t length) {

View File

@@ -169,7 +169,7 @@ bool IsFastmemEnabled() {
if (values.cpu_accuracy.GetValue() == CpuAccuracy::Unsafe) {
return bool(values.cpuopt_unsafe_host_mmu);
}
#if !defined(__APPLE__) && !defined(__linux__) && !defined(__ANDROID__) && !defined(_WIN32)
#if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__sun__) || defined(__HAIKU__)
return false;
#else
return true;

View File

@@ -240,7 +240,7 @@ struct Values {
Category::Cpu};
SwitchableSetting<CpuAccuracy, true> cpu_accuracy{linkage, CpuAccuracy::Auto,
"cpu_accuracy", Category::Cpu};
SwitchableSetting<bool> vtable_bouncing{linkage, true, "vtable_bouncing", Category::Cpu};
SwitchableSetting<bool> use_fast_cpu_time{linkage,
false,
"use_fast_cpu_time",
@@ -296,7 +296,7 @@ struct Values {
Category::CpuDebug};
SwitchableSetting<bool> cpuopt_unsafe_host_mmu{linkage,
#if !defined(__APPLE__) && !defined(__linux__) && !defined(__ANDROID__) && !defined(_WIN32)
#if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__sun__) || defined(__HAIKU__)
false,
#else
true,
@@ -759,6 +759,7 @@ struct Values {
// Add-Ons
std::map<u64, std::vector<std::string>> disabled_addons;
std::vector<std::string> external_dirs;
};
extern Values values;

View File

@@ -81,10 +81,7 @@ void SetCurrentThreadPriority(ThreadPriority new_priority) {
// Sets the debugger-visible name of the current thread.
void SetCurrentThreadName(const char* name) {
if (auto pf = (decltype(&SetThreadDescription))(void*)GetProcAddress(GetModuleHandle(TEXT("KernelBase.dll")), "SetThreadDescription"); pf)
pf(GetCurrentThread(), UTF8ToUTF16W(name).data()); // Windows 10+
else
; // No-op
SetThreadDescription(GetCurrentThread(), UTF8ToUTF16W(name).data());
}
#else // !MSVC_VER, so must be POSIX threads
@@ -112,7 +109,7 @@ void SetCurrentThreadName(const char* name) {
buf[len] = '\0';
pthread_setname_np(pthread_self(), buf);
}
#elif defined(_WIN32)
#elif !defined(_WIN32) || defined(_MSC_VER)
// mingw stub
(void)name;
#else

View File

@@ -54,6 +54,8 @@ add_library(core STATIC
file_sys/control_metadata.cpp
file_sys/control_metadata.h
file_sys/errors.h
file_sys/external_content_index.cpp
file_sys/external_content_index.h
file_sys/fs_directory.h
file_sys/fs_file.h
file_sys/fs_filesystem.h

View File

@@ -14,33 +14,11 @@ namespace Core {
void ArmInterface::LogBacktrace(Kernel::KProcess* process) const {
Kernel::Svc::ThreadContext ctx;
this->GetContext(ctx);
std::array<u64, 32> xreg{
ctx.r[0], ctx.r[1], ctx.r[2], ctx.r[3],
ctx.r[4], ctx.r[5], ctx.r[6], ctx.r[7],
ctx.r[8], ctx.r[9], ctx.r[10], ctx.r[11],
ctx.r[12], ctx.r[13], ctx.r[14], ctx.r[15],
ctx.r[16], ctx.r[17], ctx.r[18], ctx.r[19],
ctx.r[20], ctx.r[21], ctx.r[22], ctx.r[23],
ctx.r[24], ctx.r[25], ctx.r[26], ctx.r[27],
ctx.r[28], ctx.fp, ctx.lr, ctx.sp,
};
std::string msg = fmt::format("Backtrace @ PC={:016X}\n", ctx.pc);
for (size_t i = 0; i < 32; i += 4)
msg += fmt::format("R{:02}={:016X} R{:02}={:016X} R{:02}={:016X} R{:02}={:016X}\n",
i + 0, xreg[i + 0], i + 1, xreg[i + 1],
i + 2, xreg[i + 2], i + 3, xreg[i + 3]);
for (size_t i = 0; i < 32; i += 2)
msg += fmt::format("V{:02}={:016X}_{:016X} V{:02}={:016X}_{:016X}\n",
i + 0, ctx.v[i + 0][0], ctx.v[i + 0][1],
i + 1, ctx.v[i + 1][0], ctx.v[i + 1][1]);
msg += fmt::format("PSTATE={:08X} FPCR={:08X} FPSR={:08X} TPIDR={:016X}\n", ctx.pstate, ctx.fpcr, ctx.fpsr, ctx.tpidr);
msg += fmt::format("{:20}{:20}{:20}{:20}{}\n", "Module", "Address", "Original Address", "Offset", "Symbol");
LOG_ERROR(Core_ARM, "Backtrace, sp={:016X}, pc={:016X}", ctx.sp, ctx.pc);
LOG_ERROR(Core_ARM, "{:20}{:20}{:20}{:20}{}", "Module Name", "Address", "Original Address", "Offset", "Symbol");
auto const backtrace = GetBacktraceFromContext(process, ctx);
for (auto const& entry : backtrace)
msg += fmt::format("{:20}{:016X} {:016X} {:016X} {}\n", entry.module, entry.address, entry.original_address, entry.offset, entry.name);
LOG_ERROR(Core_ARM, "{}", msg);
LOG_ERROR(Core_ARM, "{:20}{:016X} {:016X} {:016X} {}", entry.module, entry.address, entry.original_address, entry.offset, entry.name);
}
const Kernel::DebugWatchpoint* ArmInterface::MatchingWatchpoint(

View File

@@ -292,7 +292,7 @@ std::shared_ptr<Dynarmic::A32::Jit> ArmDynarmic32::MakeJit(Common::PageTable* pa
// Curated optimizations
case Settings::CpuAccuracy::Auto:
config.unsafe_optimizations = true;
#if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__sun__) || defined(__HAIKU__) || defined(__DragonFly__)
#if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__sun__) || defined(__HAIKU__)
config.fastmem_pointer = std::nullopt;
config.fastmem_exclusive_access = false;
#endif

View File

@@ -351,7 +351,7 @@ std::shared_ptr<Dynarmic::A64::Jit> ArmDynarmic64::MakeJit(Common::PageTable* pa
// Safe optimisations
case Settings::CpuAccuracy::Auto:
config.unsafe_optimizations = true;
#if !defined(__APPLE__) && !defined(__linux__) && !defined(__ANDROID__) && !defined(_WIN32)
#if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__sun__) || defined(__HAIKU__)
config.fastmem_pointer = std::nullopt;
config.fastmem_exclusive_access = false;
#endif

View File

@@ -388,6 +388,14 @@ void ArmNce::SignalInterrupt(Kernel::KThread* thread) {
const std::size_t CACHE_PAGE_SIZE = 4096;
void ArmNce::ClearInstructionCache() {
#if defined(__GNUC__) || defined(__clang__)
void* start = (void*)((uintptr_t)__builtin_return_address(0) & ~(CACHE_PAGE_SIZE - 1));
void* end =
(void*)((uintptr_t)start + CACHE_PAGE_SIZE * 2); // Clear two pages for better coverage
// Prefetch next likely pages
__builtin_prefetch((void*)((uintptr_t)end), 1, 3);
__builtin___clear_cache(static_cast<char*>(start), static_cast<char*>(end));
#endif
#ifdef __aarch64__
// Ensure all previous memory operations complete
asm volatile("dmb ish" ::: "memory");
@@ -397,6 +405,20 @@ void ArmNce::ClearInstructionCache() {
}
void ArmNce::InvalidateCacheRange(u64 addr, std::size_t size) {
#if defined(__GNUC__) || defined(__clang__)
// Align the start address to cache line boundary for better performance
const size_t CACHE_LINE_SIZE = 64;
addr &= ~(CACHE_LINE_SIZE - 1);
// Round up size to nearest cache line
size = (size + CACHE_LINE_SIZE - 1) & ~(CACHE_LINE_SIZE - 1);
// Prefetch the range to be invalidated
for (size_t offset = 0; offset < size; offset += CACHE_LINE_SIZE) {
__builtin_prefetch((void*)(addr + offset), 1, 3);
}
#endif
this->ClearInstructionCache();
}

View File

@@ -181,6 +181,14 @@ bool InterpreterVisitor::Ordered(size_t size, bool L, bool o0, Reg Rn, Reg Rt) {
const size_t dbytes = datasize / 8;
u64 address = (Rn == Reg::SP) ? this->GetSp() : this->GetReg(Rn);
// Conservative prefetch for atomic ops
if (memop == MemOp::Load) {
__builtin_prefetch(reinterpret_cast<const void*>(address), 0, 1);
} else {
__builtin_prefetch(reinterpret_cast<const void*>(address), 1, 1);
}
switch (memop) {
case MemOp::Store: {
std::atomic_thread_fence(std::memory_order_seq_cst);
@@ -427,6 +435,21 @@ bool InterpreterVisitor::RegisterImmediate(bool wback, bool postindex, size_t sc
if (!postindex)
address += offset;
// Optimized prefetch for loads
if (memop == MemOp::Load) {
const size_t access_size = datasize / 8;
const bool is_aligned = (address % access_size) == 0;
if (is_aligned) {
__builtin_prefetch(reinterpret_cast<const void*>(address), 0, 3);
if (access_size >= 8 && access_size <= 32) {
__builtin_prefetch(reinterpret_cast<const void*>(address + PREFETCH_STRIDE), 0, 3);
}
} else {
__builtin_prefetch(reinterpret_cast<const void*>(address), 0, 1);
}
}
switch (memop) {
case MemOp::Store: {
u64 data = this->GetReg(Rt);
@@ -493,6 +516,15 @@ bool InterpreterVisitor::SIMDImmediate(bool wback, bool postindex, size_t scale,
if (!postindex)
address += offset;
// Aggressive prefetch for SIMD
if (memop == MemOp::Load) {
__builtin_prefetch(reinterpret_cast<const void*>(address), 0, 3);
__builtin_prefetch(reinterpret_cast<const void*>(address + CACHE_LINE_SIZE), 0, 3);
if (datasize >= SIMD_PREFETCH_THRESHOLD) {
__builtin_prefetch(reinterpret_cast<const void*>(address + PREFETCH_STRIDE), 0, 3);
}
}
switch (memop) {
case MemOp::Store: {
u128 data = VectorGetElement(this->GetVec(Vt), datasize);

View File

@@ -0,0 +1,313 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#include "core/file_sys/external_content_index.h"
#include <algorithm>
#include <cctype>
#include <filesystem>
#include <string>
#include "common/fs/fs_util.h"
#include "common/hex_util.h"
#include "common/logging/log.h"
#include "core/file_sys/common_funcs.h"
#include "core/file_sys/content_archive.h"
#include "core/file_sys/nca_metadata.h"
#include "core/file_sys/registered_cache.h"
#include "core/file_sys/romfs.h"
#include "core/file_sys/submission_package.h"
#include "core/file_sys/vfs/vfs.h"
#include "core/loader/loader.h"
namespace fs = std::filesystem;
namespace FileSys {
ExternalContentIndexer::ExternalContentIndexer(VirtualFilesystem vfs,
ManualContentProvider& provider,
ExternalContentPaths paths)
: m_vfs(std::move(vfs)), m_provider(provider), m_paths(std::move(paths)) {}
void ExternalContentIndexer::Rebuild() {
m_provider.ClearAllEntries();
m_updates_by_title.clear();
m_all_dlc.clear();
for (const auto& dir : m_paths.update_dirs) {
IndexUpdatesDir(dir);
}
for (const auto& dir : m_paths.dlc_dirs) {
IndexDlcDir(dir);
}
Commit();
}
static std::string ToLowerCopy(const std::string& s) {
std::string out;
out.resize(s.size());
std::transform(s.begin(), s.end(), out.begin(),
[](unsigned char ch) { return static_cast<char>(std::tolower(ch)); });
return out;
}
void ExternalContentIndexer::IndexUpdatesDir(const std::string& dir) {
try {
const fs::path p = Common::FS::ToU8String(dir);
std::error_code ec;
if (!fs::exists(p, ec) || ec)
return;
if (fs::is_directory(p, ec) && !ec) {
for (const auto& entry : fs::recursive_directory_iterator(
p, fs::directory_options::skip_permission_denied, ec)) {
if (entry.is_directory(ec))
continue;
TryIndexFileAsContainer(Common::FS::ToUTF8String(entry.path().u8string()), true);
}
TryIndexLooseDir(Common::FS::ToUTF8String(p.u8string()), true);
} else {
TryIndexFileAsContainer(Common::FS::ToUTF8String(p.u8string()), true);
}
} catch (const std::exception& e) {
LOG_ERROR(Loader, "Error accessing update directory '{}': {}", dir, e.what());
}
}
void ExternalContentIndexer::IndexDlcDir(const std::string& dir) {
try {
const fs::path p = Common::FS::ToU8String(dir);
std::error_code ec;
if (!fs::exists(p, ec) || ec)
return;
if (fs::is_directory(p, ec) && !ec) {
for (const auto& entry : fs::recursive_directory_iterator(
p, fs::directory_options::skip_permission_denied, ec)) {
if (entry.is_directory(ec))
continue;
TryIndexFileAsContainer(Common::FS::ToUTF8String(entry.path().u8string()), false);
}
TryIndexLooseDir(Common::FS::ToUTF8String(p.u8string()), false);
} else {
TryIndexFileAsContainer(Common::FS::ToUTF8String(p.u8string()), false);
}
} catch (const std::exception& e) {
LOG_ERROR(Loader, "Error accessing DLC directory '{}': {}", dir, e.what());
}
}
void ExternalContentIndexer::TryIndexFileAsContainer(const std::string& path, bool is_update) {
const auto lower = ToLowerCopy(path);
if (lower.size() >= 4 && lower.rfind(".nsp") == lower.size() - 4) {
if (auto vf = m_vfs->OpenFile(path, OpenMode::Read)) {
ParseContainerNSP(vf, is_update);
}
}
}
void ExternalContentIndexer::TryIndexLooseDir(const std::string& dir, bool is_update) {
fs::path p = Common::FS::ToU8String(dir);
std::error_code ec;
if (!fs::is_directory(p, ec) || ec)
return;
for (const auto& entry :
fs::recursive_directory_iterator(p, fs::directory_options::skip_permission_denied, ec)) {
if (ec)
break;
if (!entry.is_regular_file(ec))
continue;
const auto path = Common::FS::ToUTF8String(entry.path().u8string());
const auto lower = ToLowerCopy(path);
if (lower.size() >= 9 && lower.rfind(".cnmt.nca") == lower.size() - 9) {
if (auto vf = m_vfs->OpenFile(path, OpenMode::Read)) {
ParseLooseCnmtNca(
vf, Common::FS::ToUTF8String(entry.path().parent_path().u8string()), is_update);
}
}
}
}
void ExternalContentIndexer::ParseContainerNSP(VirtualFile file, bool is_update) {
if (file == nullptr)
return;
NSP nsp(file);
if (nsp.GetStatus() != Loader::ResultStatus::Success) {
LOG_WARNING(Loader, "ExternalContent: NSP parse failed");
return;
}
const auto title_map = nsp.GetNCAs();
if (title_map.empty())
return;
for (const auto& [title_id, nca_map] : title_map) {
std::shared_ptr<NCA> meta_nca;
for (const auto& [key, nca_ptr] : nca_map) {
if (nca_ptr && nca_ptr->GetType() == NCAContentType::Meta) {
meta_nca = nca_ptr;
break;
}
}
if (!meta_nca)
continue;
auto cnmt_opt = ExtractCnmtFromMetaNca(*meta_nca);
if (!cnmt_opt)
continue;
const auto& cnmt = *cnmt_opt;
const auto base_id = BaseTitleId(title_id);
if (is_update && cnmt.GetType() == TitleType::Update) {
ParsedUpdate candidate{};
// Register updates under their Update TID so PatchManager can find/apply them
candidate.title_id = FileSys::GetUpdateTitleID(base_id);
candidate.version = cnmt.GetTitleVersion();
for (const auto& rec : cnmt.GetContentRecords()) {
const auto it = nca_map.find({cnmt.GetType(), rec.type});
if (it != nca_map.end() && it->second) {
candidate.ncas[rec.type] = it->second->GetBaseFile();
}
}
auto& vec = m_updates_by_title[base_id];
vec.emplace_back(std::move(candidate));
} else if (cnmt.GetType() == TitleType::AOC) {
const auto dlc_title_id = cnmt.GetTitleID();
for (const auto& rec : cnmt.GetContentRecords()) {
const auto it = nca_map.find({cnmt.GetType(), rec.type});
if (it != nca_map.end() && it->second) {
m_all_dlc.push_back(
ParsedDlcRecord{dlc_title_id, {}, it->second->GetBaseFile()});
}
}
}
}
}
void ExternalContentIndexer::ParseLooseCnmtNca(VirtualFile meta_nca_file, const std::string& folder,
bool is_update) {
if (meta_nca_file == nullptr)
return;
NCA meta(meta_nca_file);
if (!IsMeta(meta))
return;
auto cnmt_opt = ExtractCnmtFromMetaNca(meta);
if (!cnmt_opt)
return;
const auto& cnmt = *cnmt_opt;
const auto base_id = BaseTitleId(cnmt.GetTitleID());
if (is_update && cnmt.GetType() == TitleType::Update) {
ParsedUpdate candidate{};
// Register updates under their Update TID so PatchManager can find/apply them
candidate.title_id = FileSys::GetUpdateTitleID(base_id);
candidate.version = cnmt.GetTitleVersion();
for (const auto& rec : cnmt.GetContentRecords()) {
const auto file_name = Common::HexToString(rec.nca_id) + ".nca";
const auto full = Common::FS::ToUTF8String(
(fs::path(Common::FS::ToU8String(folder)) / fs::path(file_name)).u8string());
if (auto vf = m_vfs->OpenFile(full, OpenMode::Read)) {
candidate.ncas[rec.type] = vf;
}
}
auto& vec = m_updates_by_title[base_id];
vec.emplace_back(std::move(candidate));
} else if (cnmt.GetType() == TitleType::AOC) {
const auto dlc_title_id = cnmt.GetTitleID();
for (const auto& rec : cnmt.GetContentRecords()) {
const auto file_name = Common::HexToString(rec.nca_id) + ".nca";
const auto full = Common::FS::ToUTF8String(
(fs::path(Common::FS::ToU8String(folder)) / fs::path(file_name)).u8string());
if (auto vf = m_vfs->OpenFile(full, OpenMode::Read)) {
ParsedDlcRecord dl{dlc_title_id, {}, vf};
m_all_dlc.push_back(std::move(dl));
}
}
}
}
std::optional<CNMT> ExternalContentIndexer::ExtractCnmtFromMetaNca(const NCA& meta_nca) {
if (meta_nca.GetStatus() != Loader::ResultStatus::Success)
return std::nullopt;
const auto subs = meta_nca.GetSubdirectories();
if (subs.empty() || !subs[0])
return std::nullopt;
const auto files = subs[0]->GetFiles();
if (files.empty() || !files[0])
return std::nullopt;
CNMT cnmt(files[0]);
return cnmt;
}
ExternalContentIndexer::TitleID ExternalContentIndexer::BaseTitleId(TitleID id) {
return FileSys::GetBaseTitleID(id);
}
bool ExternalContentIndexer::IsMeta(const NCA& nca) {
return nca.GetType() == NCAContentType::Meta;
}
void ExternalContentIndexer::Commit() {
// Updates: register all discovered versions per base title under unique variant TIDs,
// and additionally register the highest version under the canonical update TID for default
// usage.
size_t update_variants_count = 0;
for (auto& [base_title, vec] : m_updates_by_title) {
if (vec.empty())
continue;
// sort ascending by version, dedupe identical versions (for NAND overlap, for example)
std::stable_sort(vec.begin(), vec.end(), [](const ParsedUpdate& a, const ParsedUpdate& b) {
return a.version < b.version;
});
vec.erase(std::unique(vec.begin(), vec.end(),
[](const ParsedUpdate& a, const ParsedUpdate& b) {
return a.version == b.version;
}),
vec.end());
// highest version for canonical TID
const auto& latest = vec.back();
for (const auto& [rtype, file] : latest.ncas) {
if (!file)
continue;
const auto canonical_tid = FileSys::GetUpdateTitleID(base_title);
m_provider.AddEntry(TitleType::Update, rtype, canonical_tid, file);
}
// variants under update_tid + i (i starts at1 to avoid colliding with canonical)
for (size_t i = 0; i < vec.size(); ++i) {
const auto& upd = vec[i];
const u64 variant_tid = FileSys::GetUpdateTitleID(base_title) + static_cast<u64>(i + 1);
for (const auto& [rtype, file] : upd.ncas) {
if (!file)
continue;
m_provider.AddEntry(TitleType::Update, rtype, variant_tid, file);
}
}
update_variants_count += vec.size();
}
// DLC: additive
for (const auto& dlc : m_all_dlc) {
if (!dlc.file)
continue;
m_provider.AddEntry(TitleType::AOC, ContentRecordType::Data, dlc.title_id, dlc.file);
}
LOG_INFO(Loader,
"ExternalContent: registered updates for {} titles ({} variants), {} DLC records",
m_updates_by_title.size(), update_variants_count, m_all_dlc.size());
}
} // namespace FileSys

View File

@@ -0,0 +1,74 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
#include <optional>
#include <string>
#include <unordered_map>
#include <vector>
#include <memory>
#include "common/common_types.h"
#include "core/file_sys/registered_cache.h"
#include "core/file_sys/vfs/vfs.h"
#include "core/file_sys/nca_metadata.h"
namespace FileSys {
class ManualContentProvider;
class NCA;
struct ExternalContentPaths {
std::vector<std::string> update_dirs;
std::vector<std::string> dlc_dirs;
};
class ExternalContentIndexer {
public:
ExternalContentIndexer(VirtualFilesystem vfs,
ManualContentProvider& provider,
ExternalContentPaths paths);
void Rebuild();
private:
using TitleID = u64;
struct ParsedUpdate {
TitleID title_id{};
u32 version{};
std::unordered_map<ContentRecordType, VirtualFile> ncas;
};
struct ParsedDlcRecord {
TitleID title_id{};
NcaID nca_id{};
VirtualFile file{};
};
void IndexUpdatesDir(const std::string& dir);
void IndexDlcDir(const std::string& dir);
void TryIndexFileAsContainer(const std::string& path, bool is_update);
void TryIndexLooseDir(const std::string& dir, bool is_update);
void ParseContainerNSP(VirtualFile file, bool is_update);
void ParseLooseCnmtNca(VirtualFile meta_nca_file, const std::string& folder, bool is_update);
static std::optional<CNMT> ExtractCnmtFromMetaNca(const NCA& meta_nca);
static TitleID BaseTitleId(TitleID id);
static bool IsMeta(const NCA& nca);
void Commit();
private:
VirtualFilesystem m_vfs;
ManualContentProvider& m_provider;
ExternalContentPaths m_paths;
std::unordered_map<TitleID, std::vector<ParsedUpdate>> m_updates_by_title;
std::vector<ParsedDlcRecord> m_all_dlc;
};
} // namespace FileSys

View File

@@ -63,10 +63,12 @@ constexpr bool IsInvalidCharacter(char c) {
return impl::IsInvalidCharacterImpl<InvalidCharacters, std::size(InvalidCharacters)>(c);
}
constexpr bool IsInvalidCharacterForHostName(char c) {
return impl::IsInvalidCharacterImpl<InvalidCharactersForHostName, std::size(InvalidCharactersForHostName)>(c);
return impl::IsInvalidCharacterImpl<InvalidCharactersForHostName,
std::size(InvalidCharactersForHostName)>(c);
}
constexpr bool IsInvalidCharacterForMountName(char c) {
return impl::IsInvalidCharacterImpl<InvalidCharactersForMountName, std::size(InvalidCharactersForMountName)>(c);
return impl::IsInvalidCharacterImpl<InvalidCharactersForMountName,
std::size(InvalidCharactersForMountName)>(c);
}
} // namespace StringTraits

View File

@@ -8,6 +8,9 @@
#include <array>
#include <cstddef>
#include <cstring>
#include <set>
#include <string>
#include <vector>
#include "common/hex_util.h"
#include "common/logging/log.h"
@@ -64,6 +67,30 @@ std::string FormatTitleVersion(u32 version,
return fmt::format("v{}.{}.{}", bytes[3], bytes[2], bytes[1]);
}
static std::array<int, 4> ParseVersionComponents(std::string_view label) {
std::array<int, 4> out{0, 0, 0, 0};
if (!label.empty() && (label.front() == 'v' || label.front() == 'V')) {
label.remove_prefix(1);
}
size_t part = 0;
size_t start = 0;
std::string s(label);
while (part < out.size() && start < s.size()) {
size_t dot = s.find('.', start);
auto token = s.substr(start, dot == std::string::npos ? std::string::npos : dot - start);
try {
out[part] = std::stoi(token);
} catch (...) {
out[part] = 0;
}
++part;
if (dot == std::string::npos)
break;
start = dot + 1;
}
return out;
}
// Returns a directory with name matching name case-insensitive. Returns nullptr if directory
// doesn't have a directory with name.
VirtualDir FindSubdirectoryCaseless(const VirtualDir dir, std::string_view name) {
@@ -117,6 +144,83 @@ void AppendCommaIfNotEmpty(std::string& to, std::string_view with) {
bool IsDirValidAndNonEmpty(const VirtualDir& dir) {
return dir != nullptr && (!dir->GetFiles().empty() || !dir->GetSubdirectories().empty());
}
static std::vector<u64> EnumerateUpdateVariants(const ContentProvider& provider, u64 base_title_id,
ContentRecordType type) {
std::vector<u64> tids;
const auto entries = provider.ListEntriesFilter(TitleType::Update, type);
for (const auto& e : entries) {
if (GetBaseTitleID(e.title_id) == base_title_id) {
tids.push_back(e.title_id);
}
}
std::sort(tids.begin(), tids.end());
tids.erase(std::unique(tids.begin(), tids.end()), tids.end());
return tids;
}
static std::string GetUpdateVersionLabel(u64 update_tid,
const Service::FileSystem::FileSystemController& fs,
const ContentProvider& provider) {
PatchManager pm{update_tid, fs, provider};
const auto meta = pm.GetControlMetadata();
if (meta.first != nullptr) {
auto str = meta.first->GetVersionString();
if (!str.empty()) {
if (str.front() != 'v' && str.front() != 'V') {
str.insert(str.begin(), 'v');
}
return str;
}
}
const auto ver = provider.GetEntryVersion(update_tid).value_or(0);
return FormatTitleVersion(ver);
}
static std::optional<u64> ChooseUpdateVariant(const ContentProvider& provider, u64 base_title_id,
ContentRecordType type,
const Service::FileSystem::FileSystemController& fs) {
const auto& disabled = Settings::values.disabled_addons[base_title_id];
if (std::find(disabled.cbegin(), disabled.cend(), "Update") != disabled.cend()) {
return std::nullopt;
}
const auto candidates = EnumerateUpdateVariants(provider, base_title_id, type);
if (candidates.empty()) {
return std::nullopt;
}
// Sort candidates by numeric version descending, using meta version; fallback to0
std::vector<std::pair<u32, u64>> ordered; // (version,uTid)
ordered.reserve(candidates.size());
for (const auto tid : candidates) {
const u32 ver = provider.GetEntryVersion(tid).value_or(0);
ordered.emplace_back(ver, tid);
}
std::sort(ordered.begin(), ordered.end(), [](auto const& a, auto const& b) {
return a.first > b.first; // highest version first
});
// Pick the first candidate that is not specifically disabled via "Update vX.Y.Z"
for (const auto& [ver, tid] : ordered) {
const auto label = GetUpdateVersionLabel(tid, fs, provider);
const auto toggle_name = fmt::format("Update {}", label);
if (std::find(disabled.cbegin(), disabled.cend(), toggle_name) == disabled.cend()) {
return tid;
}
}
// All variants disabled, do not apply any update
return std::nullopt;
}
static bool HasVariantPreference(const std::vector<std::string>& disabled) {
return std::any_of(disabled.begin(), disabled.end(), [](const std::string& s) {
return s.rfind("Update v", 0) == 0;
});
}
} // Anonymous namespace
PatchManager::PatchManager(u64 title_id_,
@@ -141,13 +245,22 @@ VirtualDir PatchManager::PatchExeFS(VirtualDir exefs) const {
std::find(disabled.cbegin(), disabled.cend(), "Update") != disabled.cend();
// Game Updates
const auto update_tid = GetUpdateTitleID(title_id);
const auto update = content_provider.GetEntry(update_tid, ContentRecordType::Program);
std::optional<u64> selected_update_tid;
if (!update_disabled) {
selected_update_tid = ChooseUpdateVariant(content_provider, title_id,
ContentRecordType::Program, fs_controller);
if (!selected_update_tid.has_value()) {
selected_update_tid = GetUpdateTitleID(title_id);
}
}
if (!update_disabled && update != nullptr && update->GetExeFS() != nullptr) {
LOG_INFO(Loader, " ExeFS: Update ({}) applied successfully",
FormatTitleVersion(content_provider.GetEntryVersion(update_tid).value_or(0)));
exefs = update->GetExeFS();
if (selected_update_tid.has_value()) {
const auto update =
content_provider.GetEntry(*selected_update_tid, ContentRecordType::Program);
if (update != nullptr && update->GetExeFS() != nullptr) {
LOG_INFO(Loader, " ExeFS: Update applied successfully");
exefs = update->GetExeFS();
}
}
// LayeredExeFS
@@ -316,7 +429,8 @@ bool PatchManager::HasNSOPatch(const BuildID& build_id_, std::string_view name)
return !CollectPatches(patch_dirs, build_id).empty();
}
std::vector<Core::Memory::CheatEntry> PatchManager::CreateCheatList(const BuildID& build_id_) const {
std::vector<Core::Memory::CheatEntry> PatchManager::CreateCheatList(
const BuildID& build_id_) const {
const auto load_dir = fs_controller.GetModificationLoadRoot(title_id);
if (load_dir == nullptr) {
LOG_ERROR(Loader, "Cannot load mods for invalid title_id={:016X}", title_id);
@@ -325,16 +439,19 @@ std::vector<Core::Memory::CheatEntry> PatchManager::CreateCheatList(const BuildI
const auto& disabled = Settings::values.disabled_addons[title_id];
auto patch_dirs = load_dir->GetSubdirectories();
std::sort(patch_dirs.begin(), patch_dirs.end(), [](auto const& l, auto const& r) { return l->GetName() < r->GetName(); });
std::sort(patch_dirs.begin(), patch_dirs.end(),
[](auto const& l, auto const& r) { return l->GetName() < r->GetName(); });
// <mod dir> / <folder> / cheats / <build id>.txt
std::vector<Core::Memory::CheatEntry> out;
for (const auto& subdir : patch_dirs) {
if (std::find(disabled.cbegin(), disabled.cend(), subdir->GetName()) == disabled.cend()) {
if (auto cheats_dir = FindSubdirectoryCaseless(subdir, "cheats"); cheats_dir != nullptr) {
if (auto cheats_dir = FindSubdirectoryCaseless(subdir, "cheats");
cheats_dir != nullptr) {
if (auto const res = ReadCheatFileFromFolder(title_id, build_id_, cheats_dir, true))
std::copy(res->begin(), res->end(), std::back_inserter(out));
if (auto const res = ReadCheatFileFromFolder(title_id, build_id_, cheats_dir, false))
if (auto const res =
ReadCheatFileFromFolder(title_id, build_id_, cheats_dir, false))
std::copy(res->begin(), res->end(), std::back_inserter(out));
}
}
@@ -344,14 +461,17 @@ std::vector<Core::Memory::CheatEntry> PatchManager::CreateCheatList(const BuildI
auto const patch_files = load_dir->GetFiles();
for (auto const& f : patch_files) {
auto const name = f->GetName();
if (name.starts_with("cheat_") && std::find(disabled.cbegin(), disabled.cend(), name) == disabled.cend()) {
if (name.starts_with("cheat_") &&
std::find(disabled.cbegin(), disabled.cend(), name) == disabled.cend()) {
std::vector<u8> data(f->GetSize());
if (f->Read(data.data(), data.size()) == data.size()) {
const Core::Memory::TextCheatParser parser;
auto const res = parser.Parse(std::string_view(reinterpret_cast<const char*>(data.data()), data.size()));
auto const res = parser.Parse(
std::string_view(reinterpret_cast<const char*>(data.data()), data.size()));
std::copy(res.begin(), res.end(), std::back_inserter(out));
} else {
LOG_INFO(Common_Filesystem, "Failed to read cheats file for title_id={:016X}", title_id);
LOG_INFO(Common_Filesystem, "Failed to read cheats file for title_id={:016X}",
title_id);
}
}
}
@@ -447,8 +567,12 @@ VirtualFile PatchManager::PatchRomFS(const NCA* base_nca, VirtualFile base_romfs
auto romfs = base_romfs;
// Game Updates
const auto update_tid = GetUpdateTitleID(title_id);
const auto update_raw = content_provider.GetEntryRaw(update_tid, type);
std::optional<u64> selected_update_tid =
ChooseUpdateVariant(content_provider, title_id, type, fs_controller);
if (!selected_update_tid.has_value()) {
selected_update_tid = GetUpdateTitleID(title_id);
}
const auto update_raw = content_provider.GetEntryRaw(*selected_update_tid, type);
const auto& disabled = Settings::values.disabled_addons[title_id];
const auto update_disabled =
@@ -458,18 +582,45 @@ VirtualFile PatchManager::PatchRomFS(const NCA* base_nca, VirtualFile base_romfs
const auto new_nca = std::make_shared<NCA>(update_raw, base_nca);
if (new_nca->GetStatus() == Loader::ResultStatus::Success &&
new_nca->GetRomFS() != nullptr) {
LOG_INFO(Loader, " RomFS: Update ({}) applied successfully",
FormatTitleVersion(content_provider.GetEntryVersion(update_tid).value_or(0)));
const auto ver_num = content_provider.GetEntryVersion(*selected_update_tid).value_or(0);
LOG_DEBUG(Loader, " RomFS: Update ({}) applied successfully",
FormatTitleVersion(ver_num));
romfs = new_nca->GetRomFS();
const auto version =
FormatTitleVersion(content_provider.GetEntryVersion(update_tid).value_or(0));
} else {
LOG_WARNING(Loader, " RomFS: Update NCA is not valid");
}
} else if (!update_disabled && base_nca != nullptr) {
ContentRecordType alt_type = type;
if (type == ContentRecordType::Program) {
alt_type = ContentRecordType::Data;
} else if (type == ContentRecordType::Data) {
alt_type = ContentRecordType::Program;
}
if (alt_type != type) {
const auto alt_update_raw =
content_provider.GetEntryRaw(*selected_update_tid, alt_type);
if (alt_update_raw != nullptr) {
const auto new_nca = std::make_shared<NCA>(alt_update_raw, base_nca);
if (new_nca->GetStatus() == Loader::ResultStatus::Success &&
new_nca->GetRomFS() != nullptr) {
LOG_DEBUG(Loader, " RomFS: Update (fallback {}) applied successfully",
alt_type == ContentRecordType::Data ? "DATA" : "PROGRAM");
romfs = new_nca->GetRomFS();
} else {
LOG_WARNING(Loader, " RomFS: Update (fallback) NCA is not valid");
}
}
}
} else if (!update_disabled && packed_update_raw != nullptr && base_nca != nullptr) {
const auto new_nca = std::make_shared<NCA>(packed_update_raw, base_nca);
if (new_nca->GetStatus() == Loader::ResultStatus::Success &&
new_nca->GetRomFS() != nullptr) {
LOG_INFO(Loader, " RomFS: Update (PACKED) applied successfully");
LOG_DEBUG(Loader, " RomFS: Update (PACKED) applied successfully");
romfs = new_nca->GetRomFS();
} else {
LOG_WARNING(Loader, " RomFS: Update (PACKED) NCA is not valid");
}
}
@@ -489,36 +640,86 @@ std::vector<Patch> PatchManager::GetPatches(VirtualFile update_raw) const {
std::vector<Patch> out;
const auto& disabled = Settings::values.disabled_addons[title_id];
// Game Updates
const auto update_tid = GetUpdateTitleID(title_id);
PatchManager update{update_tid, fs_controller, content_provider};
const auto metadata = update.GetControlMetadata();
const auto& nacp = metadata.first;
auto variant_tids =
EnumerateUpdateVariants(content_provider, title_id, ContentRecordType::Program);
{
auto data_tids =
EnumerateUpdateVariants(content_provider, title_id, ContentRecordType::Data);
variant_tids.insert(variant_tids.end(), data_tids.begin(), data_tids.end());
auto control_tids =
EnumerateUpdateVariants(content_provider, title_id, ContentRecordType::Control);
variant_tids.insert(variant_tids.end(), control_tids.begin(), control_tids.end());
std::sort(variant_tids.begin(), variant_tids.end());
variant_tids.erase(std::unique(variant_tids.begin(), variant_tids.end()),
variant_tids.end());
}
const auto update_disabled =
std::find(disabled.cbegin(), disabled.cend(), "Update") != disabled.cend();
Patch update_patch = {.enabled = !update_disabled,
.name = "Update",
.version = "",
.type = PatchType::Update,
.program_id = title_id,
.title_id = title_id};
if (!variant_tids.empty()) {
const auto update_disabled =
std::find(disabled.cbegin(), disabled.cend(), "Update") != disabled.cend();
if (nacp != nullptr) {
update_patch.version = nacp->GetVersionString();
out.push_back(update_patch);
} else {
if (content_provider.HasEntry(update_tid, ContentRecordType::Program)) {
const auto meta_ver = content_provider.GetEntryVersion(update_tid);
if (meta_ver.value_or(0) == 0) {
out.push_back(update_patch);
out.push_back({.enabled = !update_disabled,
.name = "Update",
.version = "",
.type = PatchType::Update,
.program_id = title_id,
.title_id = title_id});
std::optional<u64> selected_variant_tid;
if (!update_disabled) {
const bool has_pref = HasVariantPreference(Settings::values.disabled_addons[title_id]);
if (has_pref) {
selected_variant_tid = ChooseUpdateVariant(
content_provider, title_id, ContentRecordType::Program, fs_controller);
} else {
update_patch.version = FormatTitleVersion(*meta_ver);
out.push_back(update_patch);
selected_variant_tid = GetUpdateTitleID(title_id);
}
} else if (update_raw != nullptr) {
update_patch.version = "PACKED";
out.push_back(update_patch);
}
std::vector<std::pair<std::string, u64>> variant_labels;
variant_labels.reserve(variant_tids.size());
for (const auto tid : variant_tids) {
variant_labels.emplace_back(GetUpdateVersionLabel(tid, fs_controller, content_provider),
tid);
}
std::sort(variant_labels.begin(), variant_labels.end(),
[this](auto const& a, auto const& b) {
const auto va = content_provider.GetEntryVersion(a.second).value_or(0);
const auto vb = content_provider.GetEntryVersion(b.second).value_or(0);
if (va != vb)
return va > vb;
const auto ca = ParseVersionComponents(a.first);
const auto cb = ParseVersionComponents(b.first);
if (ca != cb)
return ca > cb;
return a.first > b.first;
});
std::set<std::string> seen_versions;
for (const auto& [label, tid] : variant_labels) {
std::string version = label;
if (!version.empty() && (version.front() == 'v' || version.front() == 'V')) {
version.erase(version.begin());
}
if (seen_versions.find(version) != seen_versions.end()) {
continue;
}
const bool is_selected =
selected_variant_tid.has_value() && tid == *selected_variant_tid;
const bool variant_disabled = update_disabled || !is_selected;
out.push_back({.enabled = !variant_disabled,
.name = "Update",
.version = version,
.type = PatchType::Update,
.program_id = title_id,
.title_id = tid});
seen_versions.insert(version);
}
}

View File

@@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@@ -208,6 +211,7 @@ enum class ContentProviderUnionSlot {
UserNAND, ///< User NAND
SDMC, ///< SD Card
FrontendManual, ///< Frontend-defined game list or similar
External ///< External Updates/DLCs (not installed to NAND)
};
// Combines multiple ContentProvider(s) (i.e. SysNAND, UserNAND, SDMC) into one interface.

View File

@@ -203,7 +203,7 @@ std::unique_lock<std::mutex> RealVfsFilesystem::RefreshReference(const std::stri
FileReference& reference) {
std::unique_lock lk{list_lock};
// Temporarily remove from list.
// Temporarily remove from list (regardless of the current list).
this->RemoveReferenceFromListLocked(reference);
// Restore file if needed.
@@ -211,7 +211,8 @@ std::unique_lock<std::mutex> RealVfsFilesystem::RefreshReference(const std::stri
this->EvictSingleReferenceLocked();
reference.file =
FS::FileOpen(path, ModeFlagsToFileAccessMode(perms), FS::FileType::BinaryFile);
FS::FileOpen(path, ModeFlagsToFileAccessMode(perms), FS::FileType::BinaryFile,
FS::FileShareFlag::ShareReadWrite);
if (reference.file) {
num_open_files++;
}
@@ -226,7 +227,7 @@ std::unique_lock<std::mutex> RealVfsFilesystem::RefreshReference(const std::stri
void RealVfsFilesystem::DropReference(std::unique_ptr<FileReference>&& reference) {
std::scoped_lock lk{list_lock};
// Remove from list.
// Remove from list if present.
this->RemoveReferenceFromListLocked(*reference);
// Close the file.
@@ -236,6 +237,19 @@ void RealVfsFilesystem::DropReference(std::unique_ptr<FileReference>&& reference
}
}
void RealVfsFilesystem::CloseReference(FileReference& reference) {
std::scoped_lock lk{list_lock};
if (!reference.file) {
return;
}
this->RemoveReferenceFromListLocked(reference);
reference.file.reset();
if (num_open_files > 0) {
num_open_files--;
}
this->InsertReferenceIntoListLocked(reference);
}
void RealVfsFilesystem::EvictSingleReferenceLocked() {
if (num_open_files < MaxOpenFiles || open_references.empty()) {
return;
@@ -256,6 +270,18 @@ void RealVfsFilesystem::EvictSingleReferenceLocked() {
}
void RealVfsFilesystem::InsertReferenceIntoListLocked(FileReference& reference) {
// Ensure the node is not already linked to any list before inserting.
if (reference.IsLinked()) {
// Unlink from the list it currently belongs to.
if (reference.file) {
open_references.erase(open_references.iterator_to(reference));
}
if (reference.IsLinked()) {
closed_references.erase(closed_references.iterator_to(reference));
}
}
if (reference.file) {
open_references.push_front(reference);
} else {
@@ -264,9 +290,17 @@ void RealVfsFilesystem::InsertReferenceIntoListLocked(FileReference& reference)
}
void RealVfsFilesystem::RemoveReferenceFromListLocked(FileReference& reference) {
// Unlink from whichever list the node currently belongs to, if any.
if (!reference.IsLinked()) {
return;
}
// Erase from the correct list to avoid cross-list corruption.
if (reference.file) {
open_references.erase(open_references.iterator_to(reference));
} else {
}
if(reference.IsLinked()) {
closed_references.erase(closed_references.iterator_to(reference));
}
}
@@ -296,13 +330,19 @@ std::size_t RealVfsFile::GetSize() const {
return *size;
}
auto lk = base.RefreshReference(path, perms, *reference);
return reference->file ? reference->file->GetSize() : 0;
const auto result = reference->file ? reference->file->GetSize() : 0;
lk.unlock();
base.CloseReference(*reference);
return result;
}
bool RealVfsFile::Resize(std::size_t new_size) {
size.reset();
auto lk = base.RefreshReference(path, perms, *reference);
return reference->file ? reference->file->SetSize(new_size) : false;
const bool ok = reference->file ? reference->file->SetSize(new_size) : false;
lk.unlock();
base.CloseReference(*reference);
return ok;
}
VirtualDir RealVfsFile::GetContainingDirectory() const {
@@ -318,20 +358,37 @@ bool RealVfsFile::IsReadable() const {
}
std::size_t RealVfsFile::Read(u8* data, std::size_t length, std::size_t offset) const {
auto lk = base.RefreshReference(path, perms, *reference);
if (!reference->file || !reference->file->Seek(static_cast<s64>(offset))) {
if (length != 0 && data == nullptr) {
LOG_ERROR(Common_Filesystem,
"RealVfsFile::Read called with null buffer (len={}, off={}, path={})",
length, offset, path);
return 0;
}
return reference->file->ReadSpan(std::span{data, length});
auto lk = base.RefreshReference(path, perms, *reference);
if (!reference->file || !reference->file->Seek(static_cast<s64>(offset))) {
lk.unlock();
base.CloseReference(*reference);
return 0;
}
const auto read = reference->file->ReadSpan(std::span{data, length});
lk.unlock();
base.CloseReference(*reference);
return read;
}
std::size_t RealVfsFile::Write(const u8* data, std::size_t length, std::size_t offset) {
size.reset();
auto lk = base.RefreshReference(path, perms, *reference);
if (!reference->file || !reference->file->Seek(static_cast<s64>(offset))) {
lk.unlock();
base.CloseReference(*reference);
return 0;
}
return reference->file->WriteSpan(std::span{data, length});
const auto written = reference->file->WriteSpan(std::span{data, length});
lk.unlock();
base.CloseReference(*reference);
return written;
}
bool RealVfsFile::Rename(std::string_view name) {

View File

@@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@@ -58,6 +61,7 @@ private:
std::unique_lock<std::mutex> RefreshReference(const std::string& path, OpenMode perms,
FileReference& reference);
void DropReference(std::unique_ptr<FileReference>&& reference);
void CloseReference(FileReference& reference);
private:
friend class RealVfsDirectory;

View File

@@ -15,6 +15,7 @@
#include "common/alignment.h"
#include "common/assert.h"
#include "common/common_types.h"
#include "common/concepts.h"
namespace Kernel {
@@ -23,7 +24,7 @@ class KThread;
template <typename T>
concept KPriorityQueueAffinityMask = !
std::is_reference_v<T>&& requires(T& t) {
{ t.GetAffinityMask() } -> std::convertible_to<u64>;
{ t.GetAffinityMask() } -> Common::ConvertibleTo<u64>;
{ t.SetAffinityMask(0) };
{ t.GetAffinity(0) } -> std::same_as<bool>;
@@ -49,9 +50,9 @@ std::is_reference_v<T>&& requires(T& t) {
std::remove_cvref_t<decltype(t.GetAffinityMask())>()
} -> KPriorityQueueAffinityMask;
{ t.GetActiveCore() } -> std::convertible_to<s32>;
{ t.GetPriority() } -> std::convertible_to<s32>;
{ t.IsDummyThread() } -> std::convertible_to<bool>;
{ t.GetActiveCore() } -> Common::ConvertibleTo<s32>;
{ t.GetPriority() } -> Common::ConvertibleTo<s32>;
{ t.IsDummyThread() } -> Common::ConvertibleTo<bool>;
};
template <typename Member, size_t NumCores_, int LowestPriority, int HighestPriority>

View File

@@ -28,6 +28,9 @@ namespace Kernel {
namespace {
// TODO: Remove this workaround when proper ASLR is implemented for all address spaces.
constexpr u64 CodeStartOffset = 0x500000UL;
Result TerminateChildren(KernelCore& kernel, KProcess* process,
const KThread* thread_to_not_terminate) {
// Request that all children threads terminate.
@@ -1154,7 +1157,7 @@ KProcess::KProcess(KernelCore& kernel)
KProcess::~KProcess() = default;
Result KProcess::LoadFromMetadata(const FileSys::ProgramMetadata& metadata, std::size_t code_size,
KProcessAddress aslr_space_start, size_t aslr_space_offset, bool is_hbl) {
KProcessAddress aslr_space_start, bool is_hbl) {
// Create a resource limit for the process.
const auto pool = static_cast<KMemoryManager::Pool>(metadata.GetPoolPartition());
const auto physical_memory_size = m_kernel.MemoryManager().GetSize(pool);
@@ -1184,24 +1187,25 @@ Result KProcess::LoadFromMetadata(const FileSys::ProgramMetadata& metadata, std:
// Set the address space type and code address.
switch (metadata.GetAddressSpaceType()) {
case FileSys::ProgramAddressSpaceType::Is39Bit:
flag |= Svc::CreateProcessFlag::AddressSpace64Bit;
// For 39-bit processes, the ASLR region starts at 0x800'0000 and is ~512GiB large.
// However, some (buggy) programs/libraries like skyline incorrectly depend on the
// existence of ASLR pages before the entry point, so we will adjust the load address
// to point to about 2GiB into the ASLR region.
flag |= Svc::CreateProcessFlag::AddressSpace64Bit;
code_address = 0x8000'0000 + aslr_space_offset;
code_address = 0x8000'0000;
break;
case FileSys::ProgramAddressSpaceType::Is36Bit:
flag |= Svc::CreateProcessFlag::AddressSpace64BitDeprecated;
code_address = 0x800'0000 + aslr_space_offset;
code_address = 0x800'0000;
break;
case FileSys::ProgramAddressSpaceType::Is32Bit:
flag |= Svc::CreateProcessFlag::AddressSpace32Bit;
code_address = 0x20'0000 + aslr_space_offset;
code_address = 0x20'0000 + CodeStartOffset;
break;
case FileSys::ProgramAddressSpaceType::Is32BitNoMap:
flag |= Svc::CreateProcessFlag::AddressSpace32BitWithoutAlias;
code_address = 0x20'0000 + aslr_space_offset;
code_address = 0x20'0000 + CodeStartOffset;
break;
}

View File

@@ -511,7 +511,7 @@ public:
public:
Result LoadFromMetadata(const FileSys::ProgramMetadata& metadata, std::size_t code_size,
KProcessAddress aslr_space_start, size_t aslr_space_offset, bool is_hbl);
KProcessAddress aslr_space_start, bool is_hbl);
void LoadModule(CodeSet code_set, KProcessAddress base_addr);

View File

@@ -103,26 +103,12 @@ void PhysicalCore::RunThread(Kernel::KThread* thread) {
const bool data_abort = True(hr & Core::HaltReason::DataAbort);
const bool interrupt = True(hr & Core::HaltReason::BreakLoop);
bool may_abort = true; // Ignore aborting virtual functions (for debugging)
if (prefetch_abort && ::Settings::values.vtable_bouncing) {
auto& lock = m_kernel.GlobalSchedulerContext().SchedulerLock();
lock.Lock();
Kernel::Svc::ThreadContext ctx;
interface->GetContext(ctx);
LOG_WARNING(Core_ARM, "vtable bouncing {:016X}", ctx.lr);
ctx.pc = ctx.lr;
ctx.r[0] = 0;
interface->SetContext(ctx);
lock.Unlock();
may_abort = false;
}
// Since scheduling may occur here, we cannot use any cached
// state after returning from calls we make.
// Notify the debugger and go to sleep if a breakpoint was hit,
// or if the thread is unable to continue for any reason.
if (breakpoint || (prefetch_abort && may_abort)) {
if (breakpoint || prefetch_abort) {
if (breakpoint) {
interface->RewindBreakpointInstruction();
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,10 +1,7 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-late
// SPDX-License-Identifier: GPL-2.0-or-later
// This file is automatically generated using svc_generator.py.
// DO NOT MODIFY IT MANUALLY
#pragma once
@@ -19,6 +16,8 @@ class System;
#include "core/hle/result.h"
namespace Kernel::Svc {
// clang-format off
Result SetHeapSize(Core::System& system, uint64_t* out_address, uint64_t size);
Result SetMemoryPermission(Core::System& system, uint64_t address, uint64_t size, MemoryPermission perm);
Result SetMemoryAttribute(Core::System& system, uint64_t address, uint64_t size, uint32_t mask, uint32_t attr);
@@ -507,6 +506,8 @@ enum class SvcId : u32 {
MapInsecureMemory = 0x90,
UnmapInsecureMemory = 0x91,
};
// clang-format on
// Custom ABI.
Result ReplyAndReceiveLight(Core::System& system, Handle handle, uint32_t* args);
Result ReplyAndReceiveLight64From32(Core::System& system, Handle handle, uint32_t* args);

View File

@@ -1,6 +1,3 @@
#!/usr/bin/python3
# SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
# SPDX-License-Identifier: GPL-3.0-or-later
# SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
# SPDX-License-Identifier: GPL-2.0-or-later
@@ -381,7 +378,7 @@ def emit_gather(sources, name, type_name, reg_size):
if len(sources) == 1:
s, = sources
line = f"{type_name} {name} = Convert<{type_name}>({get_fn}(args, {s}));"
line = f"{name} = Convert<{type_name}>({get_fn}(args, {s}));"
return [line]
var_type = f"std::array<uint{reg_size*8}_t, {len(sources)}>"
@@ -389,9 +386,10 @@ def emit_gather(sources, name, type_name, reg_size):
f"{var_type} {name}_gather{{}};"
]
for i in range(0, len(sources)):
lines.append(f"{name}_gather[{i}] = {get_fn}(args, {sources[i]});")
lines.append(
f"{name}_gather[{i}] = {get_fn}(args, {sources[i]});")
lines.append(f"{type_name} {name} = Convert<{type_name}>({name}_gather);")
lines.append(f"{name} = Convert<{type_name}>({name}_gather);")
return lines
@@ -407,9 +405,14 @@ def emit_scatter(destinations, name, reg_size):
return [line]
var_type = f"std::array<{reg_type}, {len(destinations)}>"
lines = [f"auto {name}_scatter = Convert<{var_type}>({name});"]
lines = [
f"auto {name}_scatter = Convert<{var_type}>({name});"
]
for i in range(0, len(destinations)):
lines.append(f"{set_fn}(args, {destinations[i]}, {name}_scatter[{i}]);")
lines.append(
f"{set_fn}(args, {destinations[i]}, {name}_scatter[{i}]);")
return lines
@@ -433,8 +436,25 @@ def emit_wrapper(wrapped_fn, suffix, register_info, arguments, byte_size):
f"static void SvcWrap_{wrapped_fn}{suffix}(Core::System& system, std::span<uint64_t, 8> args) {{"
]
# Get everything ready.
for return_type, _ in return_write:
lines.append(f"{return_type} ret{{}};")
if return_write:
lines.append("")
for output_type, var_name, _, is_address in output_writes:
output_type = "uint64_t" if is_address else output_type
lines.append(f"{output_type} {var_name}{{}};")
for input_type, var_name, _ in input_reads:
lines.append(f"{input_type} {var_name}{{}};")
if output_writes or input_reads:
lines.append("")
for input_type, var_name, sources in input_reads:
lines += emit_gather(sources, var_name, input_type, byte_size)
if input_reads:
lines.append("")
# Build the call.
call_arguments = ["system"]
@@ -444,18 +464,16 @@ def emit_wrapper(wrapped_fn, suffix, register_info, arguments, byte_size):
else:
call_arguments.append(arg.var_name)
# Get everything ready.
for output_type, var_name, _, is_address in output_writes:
output_type = "uint64_t" if is_address else output_type
lines.append(f"{output_type} {var_name}{{}};")
line = ""
for return_type, _ in return_write:
line += f"{return_type} ret = "
if return_write:
line += "ret = "
line += f"{wrapped_fn}{suffix}({', '.join(call_arguments)});"
lines.append(line)
if return_write or output_writes:
lines.append("")
# Write back the return value and outputs.
for _, destinations in return_write:
lines += emit_scatter(destinations, "ret", byte_size)
@@ -467,13 +485,10 @@ def emit_wrapper(wrapped_fn, suffix, register_info, arguments, byte_size):
COPYRIGHT = """\
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-late
// SPDX-License-Identifier: GPL-2.0-or-later
// This file is automatically generated using svc_generator.py.
// DO NOT MODIFY IT MANUALLY
"""
PROLOGUE_H = """
@@ -490,9 +505,13 @@ class System;
#include "core/hle/result.h"
namespace Kernel::Svc {
// clang-format off
"""
EPILOGUE_H = """
// clang-format on
// Custom ABI.
Result ReplyAndReceiveLight(Core::System& system, Handle handle, uint32_t* args);
Result ReplyAndReceiveLight64From32(Core::System& system, Handle handle, uint32_t* args);
@@ -534,7 +553,7 @@ PROLOGUE_CPP = """
namespace Kernel::Svc {
static uint32_t GetArg32(std::span<uint64_t, 8> args, int n) {
return uint32_t(args[n]);
return static_cast<uint32_t>(args[n]);
}
static void SetArg32(std::span<uint64_t, 8> args, int n, uint32_t result) {
@@ -555,27 +574,37 @@ template <typename To, typename From>
requires(std::is_trivial_v<To> && std::is_trivially_copyable_v<From>)
static To Convert(const From& from) {
To to{};
if constexpr (sizeof(To) >= sizeof(From))
if constexpr (sizeof(To) >= sizeof(From)) {
std::memcpy(std::addressof(to), std::addressof(from), sizeof(From));
else
} else {
std::memcpy(std::addressof(to), std::addressof(from), sizeof(To));
}
return to;
}
// clang-format off
"""
EPILOGUE_CPP = """
// clang-format on
void Call(Core::System& system, u32 imm) {
auto& kernel = system.Kernel();
auto& process = GetCurrentProcess(kernel);
std::array<uint64_t, 8> args;
kernel.CurrentPhysicalCore().SaveSvcArguments(process, args);
//kernel.EnterSVCProfile();
if (process.Is64Bit())
kernel.EnterSVCProfile();
if (process.Is64Bit()) {
Call64(system, imm, args);
else
} else {
Call32(system, imm, args);
//kernel.ExitSVCProfile();
}
kernel.ExitSVCProfile();
kernel.CurrentPhysicalCore().LoadSvcArguments(process, args);
}
@@ -588,11 +617,12 @@ def emit_call(bitness, names, suffix):
indent = " "
lines = [
f"static void Call{bit_size}(Core::System& system, u32 imm, std::span<uint64_t, 8> args) {{",
f"{indent}switch (SvcId(imm)) {{"
f"{indent}switch (static_cast<SvcId>(imm)) {{"
]
for _, name in names:
lines.append(f"{indent}case SvcId::{name}: return SvcWrap_{name}{suffix}(system, args);")
lines.append(f"{indent}case SvcId::{name}:")
lines.append(f"{indent*2}return SvcWrap_{name}{suffix}(system, args);")
lines.append(f"{indent}default:")
lines.append(
@@ -662,7 +692,7 @@ def main():
call_64 = emit_call(BIT_64, names, SUFFIX_NAMES[BIT_64])
enum_decls = build_enum_declarations()
with open("src/core/hle/kernel/svc.h", "w") as f:
with open("svc.h", "w") as f:
f.write(COPYRIGHT)
f.write(PROLOGUE_H)
f.write("\n".join(svc_fw_declarations))
@@ -674,7 +704,7 @@ def main():
f.write(enum_decls)
f.write(EPILOGUE_H)
with open("src/core/hle/kernel/svc.cpp", "w") as f:
with open("svc.cpp", "w") as f:
f.write(COPYRIGHT)
f.write(PROLOGUE_CPP)
f.write(emit_size_check())

View File

@@ -7,6 +7,8 @@
#include <algorithm>
#include <cstring>
#include <filesystem>
#include <iostream>
#include <random>
#include <boost/algorithm/string/case_conv.hpp>
#include <boost/algorithm/string/find.hpp>
@@ -16,11 +18,11 @@
#include "common/fs/fs.h"
#include "common/fs/fs_types.h"
#include "common/fs/path_util.h"
#include "common/fs/symlink.h"
#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 {
@@ -490,32 +492,6 @@ void ProfileManager::ResetUserSaveFile()
ParseUserSaveFile();
}
std::vector<UUID> ProfileManager::FindExistingProfileUUIDs()
{
std::vector<UUID> uuids;
for (const ProfileInfo& p : profiles) {
auto uuid = p.user_uuid;
if (!uuid.IsInvalid()) {
uuids.emplace_back(uuid);
}
}
return uuids;
}
std::vector<std::string> ProfileManager::FindExistingProfileStrings()
{
std::vector<UUID> uuids = FindExistingProfileUUIDs();
std::vector<std::string> uuid_strings;
for (const UUID &uuid : uuids) {
auto user_id = uuid.AsU128();
uuid_strings.emplace_back(fmt::format("{:016X}{:016X}", user_id[1], user_id[0]));
}
return uuid_strings;
}
std::vector<std::string> ProfileManager::FindGoodProfiles()
{
namespace fs = std::filesystem;
@@ -525,17 +501,31 @@ std::vector<std::string> ProfileManager::FindGoodProfiles()
const auto path = Common::FS::GetEdenPath(Common::FS::EdenPath::NANDDir)
/ "user/save/0000000000000000";
// some exceptions, e.g. the "system" profile
static constexpr const std::array<const char* const, 1> EXCEPTION_UUIDS
= {"00000000000000000000000000000000"};
// 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);
}
auto existing = FindExistingProfileStrings();
good_uuids.insert(good_uuids.end(), existing.begin(), existing.end());
for (const ProfileInfo& p : profiles) {
std::string uuid_string = [p]() -> std::string {
auto uuid = p.user_uuid;
// "ignore" invalid uuids
if (uuid.IsInvalid()) {
return "0";
}
auto user_id = uuid.AsU128();
return fmt::format("{:016X}{:016X}", user_id[1], user_id[0]);
}();
if (uuid_string != "0") good_uuids.emplace_back(uuid_string);
}
return good_uuids;
}
@@ -572,8 +562,7 @@ std::vector<std::string> ProfileManager::FindOrphanedProfiles()
override = true;
// if there are any regular files (NOT directories) there, do NOT delete it :p
// Also: check for symlinks
if (file.is_regular_file() || Common::FS::IsSymlink(file.path()))
if (file.is_regular_file())
return false;
}
} catch (const fs::filesystem_error& e) {

View File

@@ -105,8 +105,6 @@ public:
void ResetUserSaveFile();
std::vector<Common::UUID> FindExistingProfileUUIDs();
std::vector<std::string> FindExistingProfileStrings();
std::vector<std::string> FindGoodProfiles();
std::vector<std::string> FindOrphanedProfiles();

View File

@@ -1,6 +1,3 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@@ -33,8 +30,8 @@ ICommonStateGetter::ICommonStateGetter(Core::System& system_, std::shared_ptr<Ap
{8, D<&ICommonStateGetter::GetBootMode>, "GetBootMode"},
{9, D<&ICommonStateGetter::GetCurrentFocusState>, "GetCurrentFocusState"},
{10, D<&ICommonStateGetter::RequestToAcquireSleepLock>, "RequestToAcquireSleepLock"},
{11, D<&ICommonStateGetter::ReleaseSleepLock>, "ReleaseSleepLock"},
{12, D<&ICommonStateGetter::ReleaseSleepLockTransiently>, "ReleaseSleepLockTransiently"},
{11, nullptr, "ReleaseSleepLock"},
{12, nullptr, "ReleaseSleepLockTransiently"},
{13, D<&ICommonStateGetter::GetAcquiredSleepLockEvent>, "GetAcquiredSleepLockEvent"},
{14, nullptr, "GetWakeupCount"},
{20, nullptr, "PushToGeneralChannel"},
@@ -115,20 +112,6 @@ Result ICommonStateGetter::RequestToAcquireSleepLock() {
R_SUCCEED();
}
Result ICommonStateGetter::ReleaseSleepLock() {
LOG_WARNING(Service_AM, "(STUBBED) called");
m_applet->sleep_lock_event.Clear();
R_SUCCEED();
}
Result ICommonStateGetter::ReleaseSleepLockTransiently() {
LOG_WARNING(Service_AM, "(STUBBED) called");
m_applet->sleep_lock_event.Clear();
R_SUCCEED();
}
Result ICommonStateGetter::GetAcquiredSleepLockEvent(
OutCopyHandle<Kernel::KReadableEvent> out_event) {
LOG_WARNING(Service_AM, "called");

View File

@@ -1,6 +1,3 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@@ -32,8 +29,6 @@ private:
Result ReceiveMessage(Out<AppletMessage> out_applet_message);
Result GetCurrentFocusState(Out<FocusState> out_focus_state);
Result RequestToAcquireSleepLock();
Result ReleaseSleepLock();
Result ReleaseSleepLockTransiently();
Result GetAcquiredSleepLockEvent(OutCopyHandle<Kernel::KReadableEvent> out_event);
Result GetReaderLockAccessorEx(Out<SharedPointer<ILockAccessor>> out_lock_accessor,
u32 button_type);

View File

@@ -1,7 +1,11 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <utility>
#include <filesystem>
#include "common/assert.h"
#include "common/fs/fs.h"
@@ -12,6 +16,7 @@
#include "core/file_sys/card_image.h"
#include "core/file_sys/control_metadata.h"
#include "core/file_sys/errors.h"
#include "core/file_sys/external_content_index.h"
#include "core/file_sys/patch_manager.h"
#include "core/file_sys/registered_cache.h"
#include "core/file_sys/romfs_factory.h"
@@ -713,6 +718,36 @@ void FileSystemController::CreateFactories(FileSys::VfsFilesystem& vfs, bool ove
system.RegisterContentProvider(FileSys::ContentProviderUnionSlot::SDMC,
sdmc_factory->GetSDMCContents());
}
if (external_provider == nullptr) {
external_provider = std::make_unique<FileSys::ManualContentProvider>();
system.RegisterContentProvider(FileSys::ContentProviderUnionSlot::External,
external_provider.get());
}
RebuildExternalContentIndex();
}
void FileSystemController::RebuildExternalContentIndex() {
if (external_provider == nullptr) {
LOG_WARNING(Service_FS, "External provider not initialized, skipping re-index.");
return;
}
if (!Settings::values.external_dirs.empty()) {
FileSys::ExternalContentPaths paths{};
for (const auto& dir : Settings::values.external_dirs) {
if (dir.empty())
continue;
paths.update_dirs.push_back(dir);
paths.dlc_dirs.push_back(dir);
}
FileSys::ExternalContentIndexer indexer{system.GetFilesystem(), *this->external_provider,
std::move(paths)};
indexer.Rebuild();
} else {
external_provider->ClearAllEntries();
}
}
void FileSystemController::Reset() {

View File

@@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@@ -5,6 +8,7 @@
#include <memory>
#include <mutex>
#include <core/file_sys/registered_cache.h>
#include "common/common_types.h"
#include "core/file_sys/fs_directory.h"
#include "core/file_sys/fs_filesystem.h"
@@ -121,6 +125,8 @@ public:
// above is called.
void CreateFactories(FileSys::VfsFilesystem& vfs, bool overwrite = true);
void RebuildExternalContentIndex();
void Reset();
private:
@@ -141,6 +147,7 @@ private:
std::unique_ptr<FileSys::XCI> gamecard;
std::unique_ptr<FileSys::RegisteredCache> gamecard_registered;
std::unique_ptr<FileSys::PlaceholderCache> gamecard_placeholder;
std::unique_ptr<FileSys::ManualContentProvider> external_provider;
Core::System& system;
};

View File

@@ -1,6 +1,3 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@@ -11,8 +8,8 @@
#include <mutex>
#include <string>
#include <unordered_map>
#include <concepts>
#include "common/concepts.h"
#include "core/hle/kernel/k_port.h"
#include "core/hle/kernel/svc.h"
#include "core/hle/result.h"
@@ -66,7 +63,7 @@ public:
Result UnregisterService(const std::string& name);
Result GetServicePort(Kernel::KClientPort** out_client_port, const std::string& name);
template <std::derived_from<SessionRequestHandler> T>
template <Common::DerivedFrom<SessionRequestHandler> T>
std::shared_ptr<T> GetService(const std::string& service_name, bool block = false) const {
auto service = registered_services.find(service_name);
if (service == registered_services.end() && !block) {

View File

@@ -19,10 +19,6 @@
#include "core/internal_network/network.h"
#include "core/internal_network/sockets.h"
#ifdef YUZU_BUNDLED_OPENSSL
#include <openssl/cert.h>
#endif
using namespace Common::FS;
namespace Service::SSL {
@@ -45,85 +41,11 @@ void OneTimeInit();
void OneTimeInitLogFile();
bool OneTimeInitBIO();
#ifdef YUZU_BUNDLED_OPENSSL
// This is ported from httplib
struct scope_exit {
explicit scope_exit(std::function<void(void)> &&f)
: exit_function(std::move(f)), execute_on_destruction{true} {}
scope_exit(scope_exit &&rhs) noexcept
: exit_function(std::move(rhs.exit_function)),
execute_on_destruction{rhs.execute_on_destruction} {
rhs.release();
}
~scope_exit() {
if (execute_on_destruction) { this->exit_function(); }
}
void release() { this->execute_on_destruction = false; }
private:
scope_exit(const scope_exit &) = delete;
void operator=(const scope_exit &) = delete;
scope_exit &operator=(scope_exit &&) = delete;
std::function<void(void)> exit_function;
bool execute_on_destruction;
};
inline X509_STORE *CreateCaCertStore(const char *ca_cert,
std::size_t size) {
auto mem = BIO_new_mem_buf(ca_cert, static_cast<int>(size));
auto se = scope_exit([&] { BIO_free_all(mem); });
if (!mem) { return nullptr; }
auto inf = PEM_X509_INFO_read_bio(mem, nullptr, nullptr, nullptr);
if (!inf) { return nullptr; }
auto cts = X509_STORE_new();
if (cts) {
for (auto i = 0; i < static_cast<int>(sk_X509_INFO_num(inf)); i++) {
auto itmp = sk_X509_INFO_value(inf, i);
if (!itmp) { continue; }
if (itmp->x509) { X509_STORE_add_cert(cts, itmp->x509); }
if (itmp->crl) { X509_STORE_add_crl(cts, itmp->crl); }
}
}
sk_X509_INFO_pop_free(inf, X509_INFO_free);
return cts;
}
inline void SetCaCertStore(SSL_CTX *ctx, X509_STORE *ca_cert_store) {
if (ca_cert_store) {
if (ctx) {
if (SSL_CTX_get_cert_store(ctx) != ca_cert_store) {
// Free memory allocated for old cert and use new store `ca_cert_store`
SSL_CTX_set_cert_store(ctx, ca_cert_store);
}
} else {
X509_STORE_free(ca_cert_store);
}
}
}
inline void LoadCaCertStore(SSL_CTX* ctx, const char* ca_cert, std::size_t size)
{
SetCaCertStore(ctx, CreateCaCertStore(ca_cert, size));
}
#endif
} // namespace
class SSLConnectionBackendOpenSSL final : public SSLConnectionBackend {
public:
Result Init() {
// on bundled OpenSSL, load ca cert store
#ifdef YUZU_BUNDLED_OPENSSL
LoadCaCertStore(ssl_ctx, kCert, sizeof(kCert));
#endif
std::call_once(one_time_init_flag, OneTimeInit);
if (!one_time_init_success) {

View File

@@ -1,6 +1,3 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@@ -226,13 +223,8 @@ AppLoader_DeconstructedRomDirectory::LoadResult AppLoader_DeconstructedRomDirect
// Add patch size to the total module size
code_size += patch_ctx.GetTotalPatchSize();
// TODO: this is bad form of ASLR, it sucks
size_t aslr_offset = ((::Settings::values.rng_seed_enabled.GetValue()
? ::Settings::values.rng_seed.GetValue()
: std::rand()) * 0x734287f27) & 0xfff000;
// Setup the process code layout
if (process.LoadFromMetadata(metadata, code_size, fastmem_base, aslr_offset, is_hbl).IsError()) {
if (process.LoadFromMetadata(metadata, code_size, fastmem_base, is_hbl).IsError()) {
return {ResultStatus::ErrorUnableToParseKernelMetadata, {}};
}

View File

@@ -5,7 +5,6 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#include <cstring>
#include "common/settings.h"
#include "core/file_sys/kernel_executable.h"
#include "core/file_sys/program_metadata.h"
#include "core/hle/kernel/code_set.h"
@@ -77,10 +76,11 @@ AppLoader::LoadResult AppLoader_KIP::Load(Kernel::KProcess& process,
Kernel::CodeSet codeset;
Kernel::PhysicalMemory program_image;
const auto load_segment = [&program_image](Kernel::CodeSet::Segment& segment, const std::vector<u8>& data, u32 offset) {
const auto load_segment = [&program_image](Kernel::CodeSet::Segment& segment,
const std::vector<u8>& data, u32 offset) {
segment.addr = offset;
segment.offset = offset;
segment.size = PageAlignSize(u32(data.size()));
segment.size = PageAlignSize(static_cast<u32>(data.size()));
program_image.resize(offset + data.size());
std::memcpy(program_image.data() + offset, data.data(), data.size());
};
@@ -92,14 +92,10 @@ AppLoader::LoadResult AppLoader_KIP::Load(Kernel::KProcess& process,
program_image.resize(PageAlignSize(kip->GetBSSOffset()) + kip->GetBSSSize());
codeset.DataSegment().size += kip->GetBSSSize();
// TODO: this is bad form of ASLR, it sucks
size_t aslr_offset = ((::Settings::values.rng_seed_enabled.GetValue()
? ::Settings::values.rng_seed.GetValue()
: std::rand()) * 0x734287f27) & 0xfff000;
// Setup the process code layout
if (process
.LoadFromMetadata(FileSys::ProgramMetadata::GetDefault(), program_image.size(), 0, aslr_offset, false)
.LoadFromMetadata(FileSys::ProgramMetadata::GetDefault(), program_image.size(), 0,
false)
.IsError()) {
return {ResultStatus::ErrorNotInitialized, {}};
}

View File

@@ -8,7 +8,6 @@
#include <optional>
#include <ostream>
#include <string>
#include <concepts>
#include "common/concepts.h"
#include "common/fs/path_util.h"
#include "common/logging/log.h"
@@ -28,7 +27,7 @@ namespace Loader {
namespace {
template <std::derived_from<AppLoader> T>
template <Common::DerivedFrom<AppLoader> T>
std::optional<FileType> IdentifyFileLoader(FileSys::VirtualFile file) {
const auto file_type = T::IdentifyType(file);
if (file_type != FileType::Error) {

View File

@@ -1,6 +1,3 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@@ -240,14 +237,10 @@ static bool LoadNroImpl(Core::System& system, Kernel::KProcess& process,
return 0;
}();
// TODO: this is bad form of ASLR, it sucks
size_t aslr_offset = ((::Settings::values.rng_seed_enabled.GetValue()
? ::Settings::values.rng_seed.GetValue()
: std::rand()) * 0x734287f27) & 0xfff000;
// Setup the process code layout
if (process
.LoadFromMetadata(FileSys::ProgramMetadata::GetDefault(), image_size, fastmem_base, aslr_offset, false)
.LoadFromMetadata(FileSys::ProgramMetadata::GetDefault(), image_size, fastmem_base,
false)
.IsError()) {
return false;
}

View File

@@ -50,6 +50,7 @@ endif()
list(APPEND CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/CMakeModules")
# Arch detection
include(DetectArchitecture)
if (NOT DEFINED ARCHITECTURE)
message(FATAL_ERROR "Unsupported architecture encountered. Ending CMake generation.")
endif()

View File

@@ -0,0 +1,62 @@
include(CheckSymbolExists)
if (CMAKE_OSX_ARCHITECTURES)
set(DYNARMIC_MULTIARCH_BUILD 1)
set(ARCHITECTURE "${CMAKE_OSX_ARCHITECTURES}")
return()
endif()
function(detect_architecture symbol arch)
if (NOT DEFINED ARCHITECTURE)
set(CMAKE_REQUIRED_QUIET YES)
check_symbol_exists("${symbol}" "" DETECT_ARCHITECTURE_${arch})
unset(CMAKE_REQUIRED_QUIET)
if (DETECT_ARCHITECTURE_${arch})
set(ARCHITECTURE "${arch}" PARENT_SCOPE)
endif()
unset(DETECT_ARCHITECTURE_${arch} CACHE)
endif()
endfunction()
detect_architecture("__ARM64__" arm64)
detect_architecture("__aarch64__" arm64)
detect_architecture("_M_ARM64" arm64)
detect_architecture("__arm__" arm)
detect_architecture("__TARGET_ARCH_ARM" arm)
detect_architecture("_M_ARM" arm)
detect_architecture("__x86_64" x86_64)
detect_architecture("__x86_64__" x86_64)
detect_architecture("__amd64" x86_64)
detect_architecture("_M_X64" x86_64)
detect_architecture("__i386" x86)
detect_architecture("__i386__" x86)
detect_architecture("_M_IX86" x86)
detect_architecture("__ia64" ia64)
detect_architecture("__ia64__" ia64)
detect_architecture("_M_IA64" ia64)
detect_architecture("__mips" mips)
detect_architecture("__mips__" mips)
detect_architecture("_M_MRX000" mips)
detect_architecture("__ppc64__" ppc64)
detect_architecture("__powerpc64__" ppc64)
detect_architecture("__ppc__" ppc)
detect_architecture("__ppc" ppc)
detect_architecture("__powerpc__" ppc)
detect_architecture("_ARCH_COM" ppc)
detect_architecture("_ARCH_PWR" ppc)
detect_architecture("_ARCH_PPC" ppc)
detect_architecture("_M_MPPC" ppc)
detect_architecture("_M_PPC" ppc)
detect_architecture("__riscv" riscv)
detect_architecture("__EMSCRIPTEN__" wasm)

View File

@@ -125,10 +125,6 @@ struct Jit::Impl final {
current_state.exclusive_state = false;
}
std::string Disassemble() const {
return {};
}
private:
void PerformRequestedCacheInvalidation(HaltReason hr) {
if (Has(hr, HaltReason::CacheInvalidation)) {
@@ -235,8 +231,4 @@ void Jit::ClearExclusiveState() {
impl->ClearExclusiveState();
}
std::string Jit::Disassemble() const {
return impl->Disassemble();
}
} // namespace Dynarmic::A32

View File

@@ -152,7 +152,7 @@ struct Jit::Impl final {
}
std::string Disassemble() const {
return {};
UNREACHABLE();
}
private:

View File

@@ -468,9 +468,8 @@ void EmitIR<IR::Opcode::FPRoundInt32>(oaknut::CodeGenerator& code, EmitContext&
case FP::RoundingMode::ToNearest_TieAwayFromZero:
code.FRINTA(Sresult, Soperand);
break;
default:
UNREACHABLE();
}
UNREACHABLE();
}
}

View File

@@ -1668,9 +1668,8 @@ void EmitIR<IR::Opcode::VectorTableLookup64>(oaknut::CodeGenerator& code, EmitCo
code.TBX(Dresult->B8(), oaknut::List{V0.B16(), V1.B16()}, Dindices->B8());
}
break;
default:
UNREACHABLE();
}
UNREACHABLE();
}
template<>
@@ -1732,9 +1731,8 @@ void EmitIR<IR::Opcode::VectorTableLookup128>(oaknut::CodeGenerator& code, EmitC
code.TBX(Qresult->B16(), oaknut::List{V0.B16(), V1.B16(), V2.B16(), V3.B16()}, Qindices->B16());
}
break;
default:
UNREACHABLE();
}
UNREACHABLE();
}
template<>

View File

@@ -645,9 +645,8 @@ void EmitIR<IR::Opcode::FPVectorRoundInt32>(oaknut::CodeGenerator& code, EmitCon
case FP::RoundingMode::ToNearest_TieAwayFromZero:
code.FRINTA(Qresult->S4(), Qoperand->S4());
break;
default:
UNREACHABLE();
}
UNREACHABLE();
}
});
}
@@ -685,9 +684,8 @@ void EmitIR<IR::Opcode::FPVectorRoundInt64>(oaknut::CodeGenerator& code, EmitCon
case FP::RoundingMode::ToNearest_TieAwayFromZero:
code.FRINTA(Qresult->D2(), Qoperand->D2());
break;
default:
UNREACHABLE();
}
UNREACHABLE();
}
});
}

View File

@@ -371,9 +371,8 @@ void RAReg<T>::Realize() {
case RWType::ReadWrite:
reg = T{reg_alloc.RealizeReadWriteImpl<kind>(read_value, write_value)};
break;
default:
UNREACHABLE();
}
UNREACHABLE();
}
} // namespace Dynarmic::Backend::Arm64

View File

@@ -188,15 +188,15 @@ private:
ExceptionHandler::ExceptionHandler() = default;
ExceptionHandler::~ExceptionHandler() = default;
#if defined(ARCHITECTURE_x86_64)
#if defined(MCL_ARCHITECTURE_X86_64)
void ExceptionHandler::Register(X64::BlockOfCode& code) {
impl = std::make_unique<Impl>(std::bit_cast<u64>(code.getCode()), code.GetTotalCodeSize());
}
#elif defined(ARCHITECTURE_arm64)
#elif defined(MCL_ARCHITECTURE_ARM64)
void ExceptionHandler::Register(oaknut::CodeBlock& mem, std::size_t size) {
impl = std::make_unique<Impl>(std::bit_cast<u64>(mem.ptr()), size);
}
#elif defined(ARCHITECTURE_riscv64)
#elif defined(MCL_ARCHITECTURE_RISCV)
void ExceptionHandler::Register(RV64::CodeBlock& mem, std::size_t size) {
impl = std::make_unique<Impl>(std::bit_cast<u64>(mem.ptr<u64>()), size);
}

View File

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

View File

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

View File

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

View File

@@ -72,9 +72,6 @@
# elif defined(__sun__)
# define CTX_RIP (mctx.gregs[REG_RIP])
# define CTX_RSP (mctx.gregs[REG_RSP])
# elif defined(__DragonFly__)
# define CTX_RIP (mctx.mc_rip)
# define CTX_RSP (mctx.mc_rsp)
# else
# error "Unknown platform"
# endif

View File

@@ -44,13 +44,13 @@ Block::iterator Block::PrependNewInst(iterator insertion_point, Opcode opcode, s
// hugely benefit from the coherency of faster allocations...
IR::Inst* inst;
if (inlined_inst.size() < inlined_inst.max_size()) {
inst = &inlined_inst[inlined_inst.size()];
inlined_inst.emplace_back(opcode);
inst = &inlined_inst[inlined_inst.size() - 1];
} else {
if (pooled_inst.empty() || pooled_inst.back().size() == pooled_inst.back().max_size())
pooled_inst.emplace_back();
inst = &pooled_inst.back()[pooled_inst.back().size()];
pooled_inst.back().emplace_back(opcode);
inst = &pooled_inst.back()[pooled_inst.back().size() - 1];
}
DEBUG_ASSERT(args.size() == inst->NumArgs());
std::for_each(args.begin(), args.end(), [&inst, index = size_t(0)](const auto& arg) mutable {

View File

@@ -654,11 +654,3 @@ constexpr bool MayGetNZCVFromOp(const Opcode op) noexcept {
}
} // namespace Dynarmic::IR
template<>
struct fmt::formatter<Dynarmic::IR::Opcode> : fmt::formatter<std::string_view> {
template<typename FormatContext>
auto format(Dynarmic::IR::Opcode op, FormatContext& ctx) const {
return formatter<std::string_view>::format(GetNameOf(op), ctx);
}
};

View File

@@ -29,6 +29,7 @@ target_link_libraries(dynarmic_tests PRIVATE merry::oaknut)
if (DYNARMIC_TESTS_USE_UNICORN)
target_link_libraries(dynarmic_tests PRIVATE Unicorn::Unicorn)
target_sources(dynarmic_tests PRIVATE
fuzz_util.cpp
fuzz_util.h

View File

@@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@@ -283,6 +286,19 @@ void Config::ReadDataStorageValues() {
ReadCategory(Settings::Category::DataStorage);
Settings::values.external_dirs.clear();
const int num_dirs = BeginArray(std::string("external_dirs"));
Settings::values.external_dirs.reserve(num_dirs);
for (int i = 0; i < num_dirs; ++i) {
SetArrayIndex(i);
std::string dir = ReadStringSetting(std::string("path"), std::string(""));
if (!dir.empty()) {
Settings::values.external_dirs.emplace_back(std::move(dir));
}
}
EndArray();
EndGroup();
}
@@ -591,6 +607,14 @@ void Config::SaveDataStorageValues() {
WriteCategory(Settings::Category::DataStorage);
BeginArray(std::string("external_dirs"));
for (std::size_t i = 0; i < Settings::values.external_dirs.size(); ++i) {
SetArrayIndex(static_cast<int>(i));
WriteStringSetting(std::string("path"), Settings::values.external_dirs[i],
std::make_optional(std::string("")));
}
EndArray();
EndGroup();
}

View File

@@ -28,7 +28,7 @@ const QString GetOpenFileName(const QString &title,
Options options)
{
#ifdef YUZU_QT_WIDGETS
return QFileDialog::getOpenFileName(rootObject, title, dir, filter, selectedFilter, options);
return QFileDialog::getOpenFileName((QWidget *) rootObject, title, dir, filter, selectedFilter, options);
#endif
}
@@ -39,14 +39,7 @@ const QString GetSaveFileName(const QString &title,
Options options)
{
#ifdef YUZU_QT_WIDGETS
return QFileDialog::getSaveFileName(rootObject, title, dir, filter, selectedFilter, options);
#endif
}
const QString GetExistingDirectory(const QString& caption, const QString& dir,
Options options) {
#ifdef YUZU_QT_WIDGETS
return QFileDialog::getExistingDirectory(rootObject, caption, dir, options);
return QFileDialog::getSaveFileName((QWidget *) rootObject, title, dir, filter, selectedFilter, options);
#endif
}

View File

@@ -135,9 +135,5 @@ const QString GetSaveFileName(const QString &title,
QString *selectedFilter = nullptr,
Options options = Options());
const QString GetExistingDirectory(const QString &caption = QString(),
const QString &dir = QString(),
Options options = Option::ShowDirsOnly);
} // namespace QtCommon::Frontend
#endif // FRONTEND_H

View File

@@ -294,17 +294,6 @@ void QtConfig::ReadUIGamelistValues() {
}
EndArray();
const int linked_size = BeginArray("ryujinx_linked");
for (int i = 0; i < linked_size; ++i) {
SetArrayIndex(i);
QDir ryu_dir = QString::fromStdString(ReadStringSetting("ryujinx_path"));
u64 program_id = ReadUnsignedIntegerSetting("program_id");
UISettings::values.ryujinx_link_paths.insert(program_id, ryu_dir);
}
EndArray();
EndGroup();
}
@@ -510,21 +499,6 @@ void QtConfig::SaveUIGamelistValues() {
}
EndArray(); // favorites
BeginArray(std::string("ryujinx_linked"));
int i = 0;
QMapIterator iter(UISettings::values.ryujinx_link_paths);
while (iter.hasNext()) {
iter.next();
SetArrayIndex(i);
WriteIntegerSetting("program_id", iter.key());
WriteStringSetting("ryujinx_path", iter.value().absolutePath().toStdString());
++i;
}
EndArray(); // ryujinx
EndGroup();
}

View File

@@ -109,10 +109,6 @@ std::unique_ptr<TranslationMap> InitializeTranslations(QObject* parent)
"cause deadlocks. A range of 77-21000 is recommended."));
INSERT(Settings, cpu_backend, tr("Backend:"), QString());
INSERT(Settings, vtable_bouncing,
tr("Virtual Table Bouncing"),
tr("Bounces (by emulating a 0-valued return) any functions that triggers a prefetch abort"));
// Cpu Debug
// Cpu Unsafe

View File

@@ -28,54 +28,54 @@ std::unique_ptr<TranslationMap> InitializeTranslations(QObject *parent);
std::unique_ptr<ComboboxTranslationMap> ComboboxEnumeration(QObject* parent);
static const std::map<Settings::AntiAliasing, QString> anti_aliasing_texts_map = {
{Settings::AntiAliasing::None, QStringLiteral(QT_TRANSLATE_NOOP("MainWindow", "None"))},
{Settings::AntiAliasing::Fxaa, QStringLiteral(QT_TRANSLATE_NOOP("MainWindow", "FXAA"))},
{Settings::AntiAliasing::Smaa, QStringLiteral(QT_TRANSLATE_NOOP("MainWindow", "SMAA"))},
{Settings::AntiAliasing::None, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "None"))},
{Settings::AntiAliasing::Fxaa, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "FXAA"))},
{Settings::AntiAliasing::Smaa, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "SMAA"))},
};
static const std::map<Settings::ScalingFilter, QString> scaling_filter_texts_map = {
{Settings::ScalingFilter::NearestNeighbor,
QStringLiteral(QT_TRANSLATE_NOOP("MainWindow", "Nearest"))},
QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Nearest"))},
{Settings::ScalingFilter::Bilinear,
QStringLiteral(QT_TRANSLATE_NOOP("MainWindow", "Bilinear"))},
{Settings::ScalingFilter::Bicubic, QStringLiteral(QT_TRANSLATE_NOOP("MainWindow", "Bicubic"))},
{Settings::ScalingFilter::ZeroTangent, QStringLiteral(QT_TRANSLATE_NOOP("MainWindow", "Zero-Tangent"))},
{Settings::ScalingFilter::BSpline, QStringLiteral(QT_TRANSLATE_NOOP("MainWindow", "B-Spline"))},
{Settings::ScalingFilter::Mitchell, QStringLiteral(QT_TRANSLATE_NOOP("MainWindow", "Mitchell"))},
QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Bilinear"))},
{Settings::ScalingFilter::Bicubic, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Bicubic"))},
{Settings::ScalingFilter::ZeroTangent, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Zero-Tangent"))},
{Settings::ScalingFilter::BSpline, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "B-Spline"))},
{Settings::ScalingFilter::Mitchell, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Mitchell"))},
{Settings::ScalingFilter::Spline1,
QStringLiteral(QT_TRANSLATE_NOOP("MainWindow", "Spline-1"))},
QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Spline-1"))},
{Settings::ScalingFilter::Gaussian,
QStringLiteral(QT_TRANSLATE_NOOP("MainWindow", "Gaussian"))},
QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Gaussian"))},
{Settings::ScalingFilter::Lanczos,
QStringLiteral(QT_TRANSLATE_NOOP("MainWindow", "Lanczos"))},
QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Lanczos"))},
{Settings::ScalingFilter::ScaleForce,
QStringLiteral(QT_TRANSLATE_NOOP("MainWindow", "ScaleForce"))},
{Settings::ScalingFilter::Fsr, QStringLiteral(QT_TRANSLATE_NOOP("MainWindow", "FSR"))},
{Settings::ScalingFilter::Area, QStringLiteral(QT_TRANSLATE_NOOP("MainWindow", "Area"))},
{Settings::ScalingFilter::Mmpx, QStringLiteral(QT_TRANSLATE_NOOP("MainWindow", "MMPX"))},
QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "ScaleForce"))},
{Settings::ScalingFilter::Fsr, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "FSR"))},
{Settings::ScalingFilter::Area, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Area"))},
{Settings::ScalingFilter::Mmpx, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "MMPX"))},
};
static const std::map<Settings::ConsoleMode, QString> use_docked_mode_texts_map = {
{Settings::ConsoleMode::Docked, QStringLiteral(QT_TRANSLATE_NOOP("MainWindow", "Docked"))},
{Settings::ConsoleMode::Handheld, QStringLiteral(QT_TRANSLATE_NOOP("MainWindow", "Handheld"))},
{Settings::ConsoleMode::Docked, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Docked"))},
{Settings::ConsoleMode::Handheld, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Handheld"))},
};
static const std::map<Settings::GpuAccuracy, QString> gpu_accuracy_texts_map = {
{Settings::GpuAccuracy::Normal, QStringLiteral(QT_TRANSLATE_NOOP("MainWindow", "Normal"))},
{Settings::GpuAccuracy::High, QStringLiteral(QT_TRANSLATE_NOOP("MainWindow", "High"))},
{Settings::GpuAccuracy::Extreme, QStringLiteral(QT_TRANSLATE_NOOP("MainWindow", "Extreme"))},
{Settings::GpuAccuracy::Normal, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Normal"))},
{Settings::GpuAccuracy::High, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "High"))},
{Settings::GpuAccuracy::Extreme, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Extreme"))},
};
static const std::map<Settings::RendererBackend, QString> renderer_backend_texts_map = {
{Settings::RendererBackend::Vulkan, QStringLiteral(QT_TRANSLATE_NOOP("MainWindow", "Vulkan"))},
{Settings::RendererBackend::OpenGL, QStringLiteral(QT_TRANSLATE_NOOP("MainWindow", "OpenGL"))},
{Settings::RendererBackend::Null, QStringLiteral(QT_TRANSLATE_NOOP("MainWindow", "Null"))},
{Settings::RendererBackend::Vulkan, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Vulkan"))},
{Settings::RendererBackend::OpenGL, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "OpenGL"))},
{Settings::RendererBackend::Null, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Null"))},
};
static const std::map<Settings::ShaderBackend, QString> shader_backend_texts_map = {
{Settings::ShaderBackend::Glsl, QStringLiteral(QT_TRANSLATE_NOOP("MainWindow", "GLSL"))},
{Settings::ShaderBackend::Glasm, QStringLiteral(QT_TRANSLATE_NOOP("MainWindow", "GLASM"))},
{Settings::ShaderBackend::SpirV, QStringLiteral(QT_TRANSLATE_NOOP("MainWindow", "SPIRV"))},
{Settings::ShaderBackend::Glsl, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "GLSL"))},
{Settings::ShaderBackend::Glasm, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "GLASM"))},
{Settings::ShaderBackend::SpirV, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "SPIRV"))},
};
} // namespace ConfigurationShared

View File

@@ -14,7 +14,6 @@
#include <QString>
#include <QStringList>
#include <QVector>
#include <qdir.h>
#include "common/common_types.h"
#include "common/settings.h"
#include "common/settings_enums.h"
@@ -202,7 +201,6 @@ struct Values {
Setting<bool> cache_game_list{linkage, true, "cache_game_list", Category::UiGameList};
Setting<bool> favorites_expanded{linkage, true, "favorites_expanded", Category::UiGameList};
QVector<u64> favorited_ids;
QMap<u64, QDir> ryujinx_link_paths;
// Compatibility List
Setting<bool> show_compat{linkage, true, "show_compat", Category::UiGameList};

View File

@@ -8,12 +8,6 @@
#include "frozen/map.h"
#include "frozen/string.h"
/// Small helper to look up enums.
/// res = the result code
/// base = the base matching value in the StringKey table
#define LOOKUP_ENUM(res, base) StringLookup::Lookup( \
static_cast<StringLookup::StringKey>((int) res + (int) StringLookup::base))
namespace QtCommon::StringLookup {
Q_NAMESPACE

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