Compare commits

...

22 Commits

Author SHA1 Message Date
lizzie 2f896f6b44 2026-09-24 08:50:24
Signed-off-by: lizzie <lizzie@eden-emu.dev>
2026-09-24 08:50:24 +00:00
lizzie 6edeaa9b1e 2026-09-24 05:58:35
Signed-off-by: lizzie <lizzie@eden-emu.dev>
2026-09-24 05:58:35 +00:00
lizzie 2f0c78d2dc 2026-09-24 04:59:33
Signed-off-by: lizzie <lizzie@eden-emu.dev>
2026-09-24 04:59:33 +00:00
lizzie ec56438ace 2026-09-24 01:05:18
Signed-off-by: lizzie <lizzie@eden-emu.dev>
2026-09-24 01:05:18 +00:00
lizzie 727150d446 remove evil hacks: TODO: patch in later pr 2026-09-24 02:49:59 +02:00
lizzie 503573e896 no x11 find 2026-09-24 02:49:59 +02:00
lizzie 1f826f2218 extra portability adjustments 2026-09-24 02:49:59 +02:00
lizzie b100b47a2d let openbsd be fucked then i guess (FOR NOW) 2026-09-24 02:49:59 +02:00
lizzie 27dfa6037a fx 2026-09-24 02:49:59 +02:00
lizzie f527acaf9f qt diffs 2026-09-24 02:49:59 +02:00
lizzie 0cbc36be58 oops stray file 2026-09-24 02:49:59 +02:00
lizzie 11ae6496f3 oh, we were the ones doing it wrong, ok 2026-09-24 02:49:59 +02:00
lizzie c8db621706 correct qt6 instructions 2026-09-24 02:49:59 +02:00
lizzie 0830ca147f ok x11 fine 2026-09-24 02:49:59 +02:00
lizzie 7722ad07c7 nvm not x11::x11 2026-09-24 02:49:59 +02:00
lizzie 5b7cdef141 better x11 2026-09-24 02:49:59 +02:00
lizzie 39ebab267b [dynarmic] fix NetBSD builds on gcc14
Signed-off-by: lizzie <lizzie@eden-emu.dev>
2026-09-24 02:49:59 +02:00
PavelBARABANOV cb73a4dcc7 [renderer_vulkan] Skip post-processing on applet layers (#4474)
- [x] I have read and followed the [Contribution Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/CONTRIBUTING.md#code-contributions).
- [x] I have read and followed the [AI Policy](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/AI.md)
- [x] I have read and followed the [Coding Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/Coding.md) to the best of my ability.

-------------------
Detect applet layers via the absence of the Recording bit in layer_stack_mask (only AppletId::Application sets it) and skip PostProcessChain creation and application for them.

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4474
Reviewed-by: CamilleLaVey <camillelavey99@gmail.com>
Reviewed-by: lizzie <lizzie@eden-emu.dev>
2026-09-24 02:48:46 +02:00
lizzie 278f411dad [frontend] Unify cmdline parsing (#4424)
Addresses #4383.

Makes it so both the Qt and SDL frontends now share the same options (bar `-hlaunch` and `-qlaunch`).

Signed-off-by: lizzie <lizzie@eden-emu.dev>

- [x] I have read and followed the [Contribution Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/CONTRIBUTING.md#code-contributions).
- [x] I have read and followed the [AI Policy](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/AI.md)
- [x] I have read and followed the [Coding Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/Coding.md) to the best of my ability.

-------------------

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4424
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
Reviewed-by: Maufeat <sahyno1996@gmail.com>
2026-09-23 14:51:51 +02:00
lizzie 3d37a816c6 [glsl] fix clang 23 build error for unused function SetDefinition (#4389)
Reported by @gixel

> unused function template SetDefinition  in: src/shader_recompiler/backend/glsl/emit_glsl.cpp:32:6: error: unused function template 'SetDefinition' [-Werror,-Wunused-template]

- [x] I have read and followed the [Contribution Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/CONTRIBUTING.md#code-contributions).
- [x] I have read and followed the [AI Policy](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/AI.md)
- [x] I have read and followed the [Coding Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/Coding.md) to the best of my ability.

-------------------

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4389
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
Reviewed-by: Maufeat <sahyno1996@gmail.com>
2026-09-23 14:51:18 +02:00
lizzie a25e32676f [hle] Use correct audren:d service framework (#4472)
Trivial change.

Signed-off-by: lizzie <lizzie@eden-emu.dev>

- [x] I have read and followed the [Contribution Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/CONTRIBUTING.md#code-contributions).
- [x] I have read and followed the [AI Policy](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/AI.md)
- [x] I have read and followed the [Coding Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/Coding.md) to the best of my ability.

-------------------

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4472
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
Reviewed-by: Maufeat <sahyno1996@gmail.com>
2026-09-23 14:39:12 +02:00
Exverge 38df54edfe [common/sparse_large_vector] decommit unused pages + fix first page in zeroed region (#4471)
- [x] I have read and followed the [Contribution Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/CONTRIBUTING.md#code-contributions).
- [x] I have read and followed the [AI Policy](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/AI.md)
- [x] I have read and followed the [Coding Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/Coding.md) to the best of my ability.

-------------------
Fixes a bug where the first page of a zeroed out region would not be properly zeroed, and now frees unused memory.

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4471
Reviewed-by: lizzie <lizzie@eden-emu.dev>
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
2026-09-23 13:52:29 +02:00
21 changed files with 410 additions and 349 deletions
+8 -18
View File
@@ -24,20 +24,8 @@ include(CMakeDependentOption)
include(CTest) include(CTest)
include(CPMUtil) include(CPMUtil)
if (NOT DEFINED ARCHITECTURE) if (CXX_GCC AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS "14.0")
message(FATAL_ERROR "Architecture didn't make it out of scope, did you delete DetectArchitecture.cmake?") message(FATAL_ERROR "GCC versions older than 14.0 are not supported. Update your system, or use clang/clang++ instead.")
endif()
# Needed for FFmpeg w/ VAAPI and DRM
if (OPENBSD)
# OpenBSD 7.8 broke libcxx when upgrading, so we must define the PSTL backend manually
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -I${CMAKE_SYSROOT}/usr/X11R6/include -D_LIBCPP_PSTL_BACKEND_SERIAL=1")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -I${CMAKE_SYSROOT}/usr/X11R6/include -D_LIBCPP_PSTL_BACKEND_SERIAL=1")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -L${CMAKE_SYSROOT}/usr/X11R6/lib")
elseif (NETBSD)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -I${CMAKE_SYSROOT}/usr/X11R7/include -I${CMAKE_SYSROOT}/usr/pkg/include/c++/v1")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -I${CMAKE_SYSROOT}/usr/X11R7/include -I${CMAKE_SYSROOT}/usr/pkg/include/c++/v1")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -L${CMAKE_SYSROOT}/usr/X11R7/lib")
endif() endif()
# NetBSD: Fun for the whole family! # NetBSD: Fun for the whole family!
@@ -85,7 +73,7 @@ option(ENABLE_WERROR "Enable -Werror diagnostics" ON)
# Lossless Scaling frame generation. Only Android. # Lossless Scaling frame generation. Only Android.
cmake_dependent_option(ENABLE_LSFG "Enable Lossless Scaling frame generation" ON "ANDROID" OFF) cmake_dependent_option(ENABLE_LSFG "Enable Lossless Scaling frame generation" ON "ANDROID" OFF)
option(ENABLE_RESHADE "Enable ReShade FX post-processing effects" ON) cmake_dependent_option(ENABLE_RESHADE "Enable ReShade FX post-processing effects" ON "WIN32 OR ANDROID OR APPLE OR LINUX OR FREEBSD" OFF)
# non-linux bundled qt are static # non-linux bundled qt are static
if (YUZU_USE_BUNDLED_QT AND (APPLE OR NOT UNIX)) if (YUZU_USE_BUNDLED_QT AND (APPLE OR NOT UNIX))
@@ -276,7 +264,7 @@ cmake_dependent_option(YUZU_USE_BUNDLED_MOLTENVK "Download bundled MoltenVK lib"
option(YUZU_DISABLE_LLVM "Disable LLVM (useful for CI)" OFF) option(YUZU_DISABLE_LLVM "Disable LLVM (useful for CI)" OFF)
set(DEFAULT_YUZU_USE_BUNDLED_OPENSSL OFF) set(DEFAULT_YUZU_USE_BUNDLED_OPENSSL OFF)
if (EXT_DEFAULT OR SOLARIS OR OPENBSD) if (EXT_DEFAULT)
set(DEFAULT_YUZU_USE_BUNDLED_OPENSSL ON) set(DEFAULT_YUZU_USE_BUNDLED_OPENSSL ON)
endif() endif()
@@ -309,10 +297,12 @@ if (YUZU_ROOM)
endif() endif()
if (UNIX AND NOT (LINUX OR WIN32)) if (UNIX AND NOT (LINUX OR WIN32))
if(CXX_APPLE OR CXX_CLANG) if(CXX_APPLE OR (CXX_CLANG AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS "20.0"))
# libc++ has stop_token and jthread as experimental # libc++ has stop_token and jthread as experimental
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fexperimental-library") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fexperimental-library")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fexperimental-library") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fexperimental-library")
elseif(CXX_CLANG)
# New clang >=20.0, so do nothing
else() else()
# Uses glibc, mostly? # Uses glibc, mostly?
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -D_LIBCPP_ENABLE_EXPERIMENTAL=1") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -D_LIBCPP_ENABLE_EXPERIMENTAL=1")
@@ -618,7 +608,7 @@ if (ENABLE_QT)
# Best practice is to ask for all components at once, so they are from the same version # Best practice is to ask for all components at once, so they are from the same version
set(YUZU_QT_COMPONENTS Core Widgets Charts Concurrent Gui) set(YUZU_QT_COMPONENTS Core Widgets Charts Concurrent Gui)
if (LINUX OR FREEBSD) if (NOT WIN32 AND NOT APPLE)
list(APPEND YUZU_QT_COMPONENTS DBus) list(APPEND YUZU_QT_COMPONENTS DBus)
# yes Qt, we get it # yes Qt, we get it
set(QT_NO_PRIVATE_MODULE_WARNING ON) set(QT_NO_PRIVATE_MODULE_WARNING ON)
+6 -6
View File
@@ -108,6 +108,8 @@ If you have `quazip1_qt6_devel`, uninstall it. It may call `Core5Compat` on CMak
## OpenBSD ## OpenBSD
OpenSSL 4.0 is available via ports, needs to be pointed to directly via `-DOPENSSL_ROOT_DIR=/usr/local/include/eopenssl40`.
System boost doesn't have `context` (as of 7.8); so you may need to specify `-DYUZU_USE_CPM=ON -DBoost_FORCE_BUNDLED=ON`. System boost doesn't have `context` (as of 7.8); so you may need to specify `-DYUZU_USE_CPM=ON -DBoost_FORCE_BUNDLED=ON`.
After configuration, you may need to modify `externals/ffmpeg/CMakeFiles/ffmpeg-build/build.make` to use `-j$(nproc)` instead of just `-j`. After configuration, you may need to modify `externals/ffmpeg/CMakeFiles/ffmpeg-build/build.make` to use `-j$(nproc)` instead of just `-j`.
@@ -147,15 +149,13 @@ System provides a default `g++-10` which doesn't support the current C++ codebas
Make may error out when generating C++ headers of SPIRV shaders, hence it's recommended to use `gmake` over the default system one. Make may error out when generating C++ headers of SPIRV shaders, hence it's recommended to use `gmake` over the default system one.
[parallel/spirv-tools](https://iso.us.netbsd.org/pub/pkgsrc/current/pkgsrc/parallel/spirv-tools/index.html) isn't available in binary form and must be built from source. If [parallel/spirv-tools](https://iso.us.netbsd.org/pub/pkgsrc/current/pkgsrc/parallel/spirv-tools/index.html) isn't available in binary, build from source:
glslang is not available on NetBSD, to circumvent this simply build glslang by yourself:
```sh ```sh
pkgin python313 pkgin install python314
git clone --depth=1 https://github.com/KhronosGroup/glslang.git git clone --depth=1 https://github.com/KhronosGroup/glslang.git
cd glslang cd glslang
python3.13 ./update_glslang_sources.py python3.14 ./update_glslang_sources.py
cmake -B build -DCMAKE_BUILD_TYPE=Release cmake -B build -DCMAKE_BUILD_TYPE=Release
cmake --build build -- -j`nproc` cmake --build build -- -j`nproc`
cmake --install build cmake --install build
@@ -163,7 +163,7 @@ cmake --install build
However, pkgsrc is highly recommended, see [getting pkgsrc](https://iso.us.netbsd.org/pub/pkgsrc/current/pkgsrc/doc/pkgsrc.html#getting). You must get `current` not the `2025Q2` version. However, pkgsrc is highly recommended, see [getting pkgsrc](https://iso.us.netbsd.org/pub/pkgsrc/current/pkgsrc/doc/pkgsrc.html#getting). You must get `current` not the `2025Q2` version.
`QtCore` on NetBSD is included, but due to misconfigurations(!) we MUST include one of the standard headers that include `bits/c++config.h`, since source_location (required by `QtCore`) isn't properly configured to intake `bits/c++config.h` (none of the experimental library is). This is a bug with NetBSD packaging and not our fault, but alas. `QtCore` on NetBSD is included, but due to misconfigurations you must include `bits/c++config.h`, since source_location (required by `QtCore`) isn't properly configured to intake `bits/c++config.h` (none of the experimental library is).
## DragonFlyBSD ## DragonFlyBSD
+2 -2
View File
@@ -213,7 +213,7 @@ First, enable the community repository; [see here](https://wiki.alpinelinux.org/
# Enable the community repository # Enable the community repository
setup-apkrepos -c setup-apkrepos -c
# Install # Install
apk add g++ git cmake make mesa-dev qt6-qtbase-dev qt6-qtbase-private-dev libquazip1-qt6 ffmpeg-dev qt6-charts-dev libusb-dev libtool boost-dev sdl3-dev zstd-dev vulkan-utility-libraries spirv-tools-dev openssl-dev nlohmann-json lz4-dev opus-dev jq patch apk add g++ git cmake make mesa-dev qt6-qtbase-dev qt6-qtbase-private-dev libquazip1-qt6 ffmpeg-dev qt6-qtcharts-dev libusb-dev boost-dev sdl3-dev zstd-dev vulkan-utility-libraries spirv-tools-dev openssl-dev nlohmann-json lz4-dev opus-dev enet-dev glslang libtool jq patch
``` ```
</details> </details>
@@ -286,7 +286,7 @@ pkgin install git cmake boost fmtlib SDL3 catch2 libjwt spirv-headers spirv-tool
```sh ```sh
pkg_add -u pkg_add -u
pkg_add cmake nasm git boost unzip--iconv autoconf-2.72p0 bash ffmpeg glslang gmake qt6 jq fmt nlohmann-json enet boost vulkan-utility-libraries vulkan-headers spirv-headers spirv-tools catch2 sdl3 libusb1-1.0.29 quazip-qt6 pkg_add cmake nasm git boost unzip--iconv autoconf-2.72p0 bash ffmpeg glslang gmake qt6 jq fmt nlohmann-json enet boost vulkan-utility-libraries vulkan-headers spirv-headers spirv-tools catch2 sdl3 libusb1-1.0.29 quazip-qt6 openssl-4.0.2v0
``` ```
[Caveats](./Caveats.md#openbsd). [Caveats](./Caveats.md#openbsd).
+1 -1
View File
@@ -15,7 +15,7 @@
## Cloning Eden with Git ## Cloning Eden with Git
```sh ```sh
git clone --recursive https://git.eden-emu.dev/eden-emu/eden.git git clone --depth=1 https://git.eden-emu.dev/eden-emu/eden.git
``` ```
Eden by default will be cloned into: Eden by default will be cloned into:
+33 -21
View File
@@ -1,32 +1,44 @@
# User Handbook - Command Line # User Handbook - Command Line
There are two main applications, an SDL-based app (`eden-cli`) and a Qt based app (`eden`); both accept command line arguments. There are two main applications, an SDL-based app (`eden-cli`) and a Qt based app (`eden`); both accept the same command line arguments.
## eden
- `./eden <path>`: Running with a single argument and nothing else, will make the emulator look for the given file and load it, this behavior is similar to `eden-cli`; allows dragging and dropping games into the application. - `./eden <path>`: Running with a single argument and nothing else, will make the emulator look for the given file and load it, this behavior is similar to `eden-cli`; allows dragging and dropping games into the application.
- `-g <path>`: Alternate way to specify what to load, overrides. However let it be noted that arguments that use `-` will be treated as options/ignored, if your game, for some reason, starts with `-`, in order to safely handle it you may need to specify it as an argument. - `--debug/-d`: Enter debug mode, allow gdb stub at port `1234`
- `-f`: Use fullscreen. - `--config/-c`: Specify alternate configuration file.
- `-u <number>`: Select the index of the user to load as. - `--fullscreen/-f`: Use fullscreen.
- `-input-profile <name>`: Specifies input profile name to use (for player #0 only). - `--help/-h`: Display help.
- `--game/-g <path>`: Alternate way to specify what to load, overrides. However let it be noted that arguments that use `-` will be treated as options/ignored, if your game, for some reason, starts with `-`, in order to safely handle it you may need to specify it as an argument.
- `--multiplayer/-m`: Specify multiplayer options.
- `--program/-p`: Specify the program arguments to pass (optional).
- `--user/-u <number>`: Specify the user index.
- `--version/-v`: Display version and quit.
- `--input-profile/-i <name>`: Specifies input profile name to use (for player #0 only).
- `--null-render/-n`: Forces the usage of the "Null" render backend irrespective of settings.
- `--filter/-x`: Sets the debug log filter irrespective of settings.
- `--singlecore/-s`: Forces single-core regardless of settings.
Only the Qt frontend supports the following arguments:
- `-qlaunch`: Launch QLaunch. - `-qlaunch`: Launch QLaunch.
- `-hlaunch`: Launch homebrew launcher `nx-hbloader`. - `-hlaunch`: Launch homebrew launcher `nx-hbloader`.
- Requires a copy of Atmosphere to be extracted onto `sdmc`. - Requires a copy of Atmosphere to be extracted onto `sdmc`.
- This is a shorthand for `<eden folder>/sdmc/atmosphere/hbl.nsp`. - This is a shorthand for `<eden folder>/sdmc/atmosphere/hbl.nsp`.
- `-setup`: Launch setup applet. - `-setup`: Launch setup applet.
## eden-cli `eden` (provided with `--room`), and `eden-room` supports the following options as well:
- `--debug/-d`: Enter debug mode, allow gdb stub at port `1234` - `-n/--room-name`: The name of the room.
- `--config/-c`: Specify alternate configuration file. - `-d/--room-description`: The room description.
- `--fullscreen/-f`: Set fullscreen. - `-s/--bind-address`: The bind address for the room.
- `--help/-h`: Display help. - `-p/--port`: The port used for the room.
- `--game/-g`: Specify the game to run. - `-m/--max-members`: The maximum number of players for this room.
- `--multiplayer/-m`: Specify multiplayer options. - `-w/--password`: The password for the room.
- `--program/-p`: Specify the program arguments to pass (optional). - `-g/--preferred-game`: The preferred game for this room.
- `--user/-u`: Specify the user index. - `-i/--preferred-game-id`: The preferred game-id for this room.
- `--version/-v`: Display version and quit. - `-u/--username`: The username used for announce.
- `--input-profile/-i`: Specifies input profile name to use (for player #0 only). - `-t/--token`: The token used for announce.
- `--null-render/-n`: Forces the usage of the "Null" render backend irrespective of settings. - `-a/--web-api-url`: yuzu Web API url.
- `--filter/-x`: Sets the debug log filter irrespective of settings. - `-b/--ban-list-file`: The file for storing the room ban list.
- `--singlecore/-s`: Forces single-core regardless of settings. - `-l/--log-file`: The file for storing the room log.
- `-h/--help`: Display this help and exit.
- `-v/--version`: Output version information and exit.
+15
View File
@@ -96,6 +96,21 @@ bool CommitVectorPage(uintptr_t addr, bool write) noexcept {
#ifndef MAP_NOCORE #ifndef MAP_NOCORE
#define MAP_NOCORE 0 #define MAP_NOCORE 0
#endif #endif
#ifndef MADV_FREE
#define MADV_FREE MADV_DONTNEED
#endif
void DecommitVectorPage(uintptr_t base) noexcept {
#if defined(_WIN32)
VirtualFree(reinterpret_cast<LPVOID>(base), HostPageSize, MEM_DECOMMIT);
#elif defined(__linux__)
// Linux's MADV_DONTNEED zeros out pages for us
madvise(reinterpret_cast<void*>(base), HostPageSize, MADV_DONTNEED);
#else
madvise(reinterpret_cast<void*>(base), HostPageSize, MADV_FREE);
std::memset(reinterpret_cast<void*>(base), 0, HostPageSize);
#endif
}
void* AllocateMemoryPages(std::size_t size) noexcept { void* AllocateMemoryPages(std::size_t size) noexcept {
if (auto page = HostPageSize; size % page != 0) { if (auto page = HostPageSize; size % page != 0) {
+17 -3
View File
@@ -35,6 +35,7 @@ const u64 HostPageMask = ~(HostPageSize - 1);
void* AllocateMemoryPages(std::size_t size) noexcept; void* AllocateMemoryPages(std::size_t size) noexcept;
void FreeMemoryPages(void* base, std::size_t size) noexcept; void FreeMemoryPages(void* base, std::size_t size) noexcept;
void DecommitVectorPage(uintptr_t base) noexcept;
/// A large page-aligned buffer that has optimized memory usage for zero-writes. /// A large page-aligned buffer that has optimized memory usage for zero-writes.
template <typename T> template <typename T>
@@ -114,7 +115,7 @@ public:
const u64 end_page = AlignUp(base, HostPageSize); const u64 end_page = AlignUp(base, HostPageSize);
const u64 first_size = (std::min)(end_page, end) - base; const u64 first_size = (std::min)(end_page, end) - base;
if (IsCommittedPage(start / sizeof(T))) { if (IsCommittedPage(start)) {
std::memset(reinterpret_cast<void*>(base), 0, first_size); std::memset(reinterpret_cast<void*>(base), 0, first_size);
} }
@@ -124,11 +125,16 @@ public:
base = end_page; base = end_page;
for (u64 page = base; page < end; page += HostPageSize) { for (u64 page = base; page < end; page += HostPageSize) {
if (!IsCommittedPage((page - reinterpret_cast<u64>(base_ptr)) / sizeof(T))) { auto index = (page - reinterpret_cast<u64>(base_ptr)) / sizeof(T);
if (!IsCommittedPage(index)) {
continue; continue;
} }
std::memset(reinterpret_cast<void*>(page), 0, (std::min)( HostPageSize, end - page)); if (end - page >= HostPageSize) {
DecommitPage(index);
} else {
std::memset(reinterpret_cast<void*>(page), 0, end - page);
}
} }
} }
@@ -183,6 +189,14 @@ private:
committed_pages[page_index >> 6].fetch_or(1ULL << (page_index & 63), std::memory_order_release); committed_pages[page_index >> 6].fetch_or(1ULL << (page_index & 63), std::memory_order_release);
} }
constexpr void DecommitPage(std::size_t index) noexcept {
auto page_index = (index * sizeof(T)) >> HostPageBits;
auto page = reinterpret_cast<uintptr_t>(base_ptr + index) & HostPageMask;
committed_pages[page_index >> 6].fetch_and(~(1ULL << (page_index & 63)), std::memory_order_release);
DecommitVectorPage(page);
}
std::size_t alloc_size{}; std::size_t alloc_size{};
T* base_ptr{}; T* base_ptr{};
+5
View File
@@ -23,6 +23,8 @@ add_library(core STATIC
core_timing.h core_timing.h
cpu_manager.cpp cpu_manager.cpp
cpu_manager.h cpu_manager.h
launch_params.cpp
launch_params.h
crypto/aes_util.cpp crypto/aes_util.cpp
crypto/aes_util.h crypto/aes_util.h
crypto/ctr_encryption_layer.cpp crypto/ctr_encryption_layer.cpp
@@ -1203,6 +1205,9 @@ endif()
target_include_directories(core PRIVATE ${OPUS_INCLUDE_DIRS}) target_include_directories(core PRIVATE ${OPUS_INCLUDE_DIRS})
target_link_libraries(core PUBLIC common PRIVATE audio_core hid_core network video_core nx_tzdb tz) target_link_libraries(core PUBLIC common PRIVATE audio_core hid_core network video_core nx_tzdb tz)
if (MSVC)
target_link_libraries(core PRIVATE getopt)
endif()
if (BOOST_NO_HEADERS) if (BOOST_NO_HEADERS)
target_link_libraries(core PUBLIC Boost::container Boost::heap Boost::asio Boost::process Boost::crc) target_link_libraries(core PUBLIC Boost::container Boost::heap Boost::asio Boost::process Boost::crc)
+1 -1
View File
@@ -191,7 +191,7 @@ void LoopProcess(Core::System& system) {
server_manager->RegisterNamedService("audout:d", std::make_shared<IAudioOutManagerForDebugger>(system), 30); server_manager->RegisterNamedService("audout:d", std::make_shared<IAudioOutManagerForDebugger>(system), 30);
server_manager->RegisterNamedService("audin:d", std::make_shared<IAudioInManagerForDebugger>(system), 30); server_manager->RegisterNamedService("audin:d", std::make_shared<IAudioInManagerForDebugger>(system), 30);
server_manager->RegisterNamedService("audrec:d", std::make_shared<IFinalOutputRecorderManagerForDebugger>(system), 30); server_manager->RegisterNamedService("audrec:d", std::make_shared<IFinalOutputRecorderManagerForDebugger>(system), 30);
server_manager->RegisterNamedService("audren:d", std::make_shared<IAudioInManager>(system), 30); server_manager->RegisterNamedService("audren:d", std::make_shared<IAudioRendererManagerForDebugger>(system), 30);
server_manager->RegisterNamedService("audin:u", std::make_shared<IAudioInManager>(system), 30); server_manager->RegisterNamedService("audin:u", std::make_shared<IAudioInManager>(system), 30);
server_manager->RegisterNamedService("audin:a", std::make_shared<IAudioInManagerForApplet>(system), 30); server_manager->RegisterNamedService("audin:a", std::make_shared<IAudioInManagerForApplet>(system), 30);
+205
View File
@@ -0,0 +1,205 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#include <regex>
#include <cctype>
#include "common/assert.h"
#include "common/logging.h"
#include "common/settings.h"
#include "common/string_util.h"
#include "common/scm_rev.h"
#include "core/core.h"
#include "core/hle/service/acc/profile_manager.h"
#include "core/launch_params.h"
#undef _UNICODE
#include <getopt.h>
#ifndef _MSC_VER
#include <unistd.h>
#endif
namespace Core {
LaunchParams ParseLaunchParams(Core::System& system, int argc, char *argv[], wchar_t *argv_w[]) noexcept {
LaunchParams p{};
int option_index = 0;
static struct option long_options[] = {
// clang-format off
{"debug", no_argument, 0, 'd'},
{"config", required_argument, 0, 'c'},
{"fullscreen", no_argument, 0, 'f'},
{"help", no_argument, 0, 'h'},
{"game", required_argument, 0, 'g'},
{"multiplayer", required_argument, 0, 'm'},
{"program", optional_argument, 0, 'p'},
{"user", required_argument, 0, 'u'},
{"version", no_argument, 0, 'v'},
{"input-profile", no_argument, 0, 'i'},
{"null-render", no_argument, 0, 'n'},
{"singlecore", no_argument, 0, 's'},
{"filter", no_argument, 0, 'x'},
{0, 0, 0, 0},
// clang-format on
};
while (optind < argc) {
if (int arg = getopt_long(argc, argv, "dc:fhg:m:p:u:vinsx", long_options, &option_index); arg != -1) {
switch (char(arg)) {
case 'd':
p.override_gdb_port = uint16_t(atoi(optarg));
break;
case 'c':
p.config_path = optarg;
break;
case 'f':
p.fullscreen = true;
LOG_INFO(Frontend, "Starting in fullscreen mode...");
break;
case 'h':
p.print_help = true;
break;
case 'g':
p.filepath = std::string(optarg);
break;
case 'i': {
p.input_profile = std::string(optarg);
break;
}
case 'm': {
p.use_multiplayer = true;
const std::string str_arg(optarg);
// regex to check if the format is nickname:password@ip:port
// with optional :password
const std::regex re("^([^:]+)(?::(.+))?@([^:]+)(?::([0-9]+))?$");
if (std::regex_match(str_arg, re)) {
std::smatch match;
std::regex_search(str_arg, match, re);
ASSERT(match.size() == 5);
p.nickname = match[1];
p.password = match[2];
p.address = match[3];
if (!match[4].str().empty()) {
p.port = u16(std::strtoul(match[4].str().c_str(), nullptr, 0));
}
std::regex nickname_re("^[a-zA-Z0-9._\\- ]+$");
ASSERT(std::regex_match(p.nickname, nickname_re) && "Nickname is not valid. Must be 4 to 20 alphanumeric characters");
ASSERT(!p.address.empty() && "Address is empty");
}
break;
}
case 'p':
p.program_args = argv[optind];
++optind;
break;
case 'u': {
// Launch game with a specific user
bool argument_ok = isdigit(optarg[0]);
p.selected_user = atoi(optarg);
if (!argument_ok) {
// try to look it up by username, only finds the first username that matches.
auto const user_idx = system.GetProfileManager().GetUserIndex(optarg);
if (user_idx != std::nullopt) {
p.selected_user = user_idx.value();
} else {
LOG_ERROR(Frontend, "Invalid user argument '{}'", optarg);
break;
}
}
if (system.GetProfileManager().UserExistsIndex(*p.selected_user)) {
Settings::values.current_user = s32(*p.selected_user);
} else {
LOG_ERROR(Frontend, "Selected user {} doesn't exist", *p.selected_user);
}
break;
}
case 'v':
p.print_version = true;
break;
case 'n':
p.force_null_render = true;
break;
case 's':
p.force_single_core = true;
break;
case 'x':
p.log_filter = argv[optind];
++optind;
break;
}
} else {
// only kept due to shortcuts made by Qt frontend which use "-qlaunch"
if (strcmp(argv[optind], "-hlaunch") == 0) {
p.launch_hlaunch = true;
} else if (strcmp(argv[optind], "-qlaunch") == 0) {
p.launch_qlaunch = true;
} else if (strcmp(argv[optind], "-setup") == 0) {
p.launch_setup = true;
} else {
// qt feeds utf8 data, sdl frontend feeds raw utf16 data
#ifdef _WIN32
p.filepath = argv_w != nullptr
? Common::UTF16ToUTF8(argv_w[optind])
: argv[optind];
#else
p.filepath = argv[optind];
#endif
}
optind++;
}
}
p.argv0 = argv[0];
return p;
}
void ApplyLaunchParams(LaunchParams const& lp) noexcept {
// apply the log_filter setting
// the logger was initialized before and doesn't pick up the filter on its own
Common::Log::Filter filter{};
filter.ParseFilterString(lp.log_filter.value_or(Settings::values.log_filter.GetValue()));
Common::Log::SetGlobalFilter(filter);
if (!lp.program_args.empty()) {
Settings::values.program_args = lp.program_args;
}
if (!lp.input_profile.empty()) {
auto& players = Settings::values.players.GetValue();
players[0].profile_name = lp.input_profile;
}
if (lp.selected_user.has_value()) {
Settings::values.current_user = std::clamp(*lp.selected_user, 0, 7);
}
if (lp.override_gdb_port.has_value()) {
Settings::values.use_gdbstub = true;
Settings::values.gdbstub_port = *lp.override_gdb_port;
}
if (lp.force_single_core) {
Settings::values.use_multi_core = false;
}
if (lp.force_null_render) {
Settings::values.renderer_backend = Settings::RendererBackend::Null;
}
if (lp.print_version) {
LOG_INFO(Frontend, "Eden {} {}", Common::g_scm_branch, Common::g_scm_desc);
}
if (lp.print_help) {
LOG_INFO(Frontend,
"Usage: {}"
" [options] <filename>\n"
"-c, --config Load the specified configuration file\n"
"-f, --fullscreen Start in fullscreen mode\n"
"-g, --game File path of the game to load\n"
"-h, --help Display this help and exit\n"
"-m, --multiplayer=nick:password@address:port"
" Nickname, password, address and port for multiplayer\n"
"-p, --program Pass following string as arguments to executable\n"
"-u, --user Select a specific user profile from 0 to 7\n"
"-d, --debug Run the GDB stub on a port from 1 to 65535\n"
"-v, --version Output version information and exit\n",
lp.argv0
);
}
}
}
+43
View File
@@ -0,0 +1,43 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
#include <string>
#include <optional>
#include "common/common_types.h"
#include "network/room.h"
namespace Core {
class System;
struct LaunchParams {
std::string argv0{};
std::string filepath{};
std::string nickname{};
std::string password{};
std::string address{};
std::string input_profile{};
std::string program_args{};
std::optional<std::string> config_path{};
std::optional<int> selected_user{};
std::optional<u16> override_gdb_port{};
std::optional<std::string> log_filter{};
u16 port = Network::DefaultRoomPort;
bool use_multiplayer = false;
bool fullscreen = false;
bool force_null_render = false;
bool force_single_core = false;
// print
bool print_version = false;
bool print_help = false;
// qt
bool launch_hlaunch = false;
bool launch_qlaunch = false;
bool launch_setup = false;
};
LaunchParams ParseLaunchParams(Core::System& system, int argc, char *argv[], wchar_t *argv_w[]) noexcept;
void ApplyLaunchParams(LaunchParams const& lp) noexcept;
}
+1 -3
View File
@@ -67,9 +67,7 @@
#else #else
# define CTX_SP (_UC_MACHINE_SP(ucontext)) # define CTX_SP (_UC_MACHINE_SP(ucontext))
#endif #endif
#endif #elif defined(ARCHITECTURE_arm64)
#if defined(ARCHITECTURE_arm64)
# if defined(__APPLE__) # if defined(__APPLE__)
# define CTX_PC (mctx->__ss.__pc) # define CTX_PC (mctx->__ss.__pc)
# define CTX_SP (mctx->__ss.__sp) # define CTX_SP (mctx->__ss.__sp)
+1 -1
View File
@@ -92,7 +92,7 @@ if (ENABLE_OPENGL)
target_compile_definitions(qt_common PUBLIC HAS_OPENGL) target_compile_definitions(qt_common PUBLIC HAS_OPENGL)
endif() endif()
if (UNIX AND NOT APPLE) if (LINUX AND FREEBSD)
if (DEFINED Qt6Gui_PRIVATE_INCLUDE_DIRS) if (DEFINED Qt6Gui_PRIVATE_INCLUDE_DIRS)
target_include_directories(qt_common PRIVATE ${Qt6Gui_PRIVATE_INCLUDE_DIRS}) target_include_directories(qt_common PRIVATE ${Qt6Gui_PRIVATE_INCLUDE_DIRS})
else() else()
@@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
@@ -28,11 +31,6 @@ struct FuncTraits<ReturnType_ (*)(Args...)> {
using ArgType = std::tuple_element_t<I, std::tuple<Args...>>; using ArgType = std::tuple_element_t<I, std::tuple<Args...>>;
}; };
template <auto func, typename... Args>
void SetDefinition(EmitContext& ctx, IR::Inst* inst, Args... args) {
inst->SetDefinition<Id>(func(ctx, std::forward<Args>(args)...));
}
template <typename ArgType> template <typename ArgType>
auto Arg(EmitContext& ctx, const IR::Value& arg) { auto Arg(EmitContext& ctx, const IR::Value& arg) {
if constexpr (std::is_same_v<ArgType, std::string_view>) { if constexpr (std::is_same_v<ArgType, std::string_view>) {
@@ -53,21 +51,10 @@ auto Arg(EmitContext& ctx, const IR::Value& arg) {
template <auto func, bool is_first_arg_inst, size_t... I> template <auto func, bool is_first_arg_inst, size_t... I>
void Invoke(EmitContext& ctx, IR::Inst* inst, std::index_sequence<I...>) { void Invoke(EmitContext& ctx, IR::Inst* inst, std::index_sequence<I...>) {
using Traits = FuncTraits<decltype(func)>; using Traits = FuncTraits<decltype(func)>;
if constexpr (std::is_same_v<typename Traits::ReturnType, Id>) { if constexpr (is_first_arg_inst) {
if constexpr (is_first_arg_inst) { func(ctx, *inst, Arg<typename Traits::template ArgType<I + 2>>(ctx, inst->Arg(I))...);
SetDefinition<func>(
ctx, inst, *inst,
Arg<typename Traits::template ArgType<I + 2>>(ctx, inst->Arg(I))...);
} else {
SetDefinition<func>(
ctx, inst, Arg<typename Traits::template ArgType<I + 1>>(ctx, inst->Arg(I))...);
}
} else { } else {
if constexpr (is_first_arg_inst) { func(ctx, Arg<typename Traits::template ArgType<I + 1>>(ctx, inst->Arg(I))...);
func(ctx, *inst, Arg<typename Traits::template ArgType<I + 2>>(ctx, inst->Arg(I))...);
} else {
func(ctx, Arg<typename Traits::template ArgType<I + 1>>(ctx, inst->Arg(I))...);
}
} }
} }
@@ -98,7 +98,10 @@ void Layer::ConfigureDraw(const Device& device, PresentPushConstants* out_push_c
RefreshResources(device, framebuffer); RefreshResources(device, framebuffer);
SetAntiAliasPass(device); SetAntiAliasPass(device);
#ifdef HAS_RESHADE #ifdef HAS_RESHADE
SetPostProcessPass(device); const bool is_applet =
(framebuffer.layer_stack_mask & Service::Nvnflinger::LayerStackBit(
Service::Nvnflinger::LayerStackId::Recording)) == 0;
SetPostProcessPass(device, is_applet);
#endif #endif
// Finish any pending renderpass // Finish any pending renderpass
@@ -228,7 +231,11 @@ void Layer::SetAntiAliasPass(const Device& device) {
} }
#ifdef HAS_RESHADE #ifdef HAS_RESHADE
void Layer::SetPostProcessPass(const Device& device) { void Layer::SetPostProcessPass(const Device& device, bool is_applet) {
if (is_applet) {
post_process.reset();
return;
}
const VkExtent2D render_area{ const VkExtent2D render_area{
.width = Settings::values.resolution_info.ScaleUp(raw_width), .width = Settings::values.resolution_info.ScaleUp(raw_width),
.height = Settings::values.resolution_info.ScaleUp(raw_height), .height = Settings::values.resolution_info.ScaleUp(raw_height),
@@ -70,7 +70,7 @@ private:
void RefreshResources(const Device& device, const Tegra::FramebufferConfig& framebuffer); void RefreshResources(const Device& device, const Tegra::FramebufferConfig& framebuffer);
void SetAntiAliasPass(const Device& device); void SetAntiAliasPass(const Device& device);
#ifdef HAS_RESHADE #ifdef HAS_RESHADE
void SetPostProcessPass(const Device& device); void SetPostProcessPass(const Device& device, bool is_applet);
#endif #endif
void ReleaseRawImages(); void ReleaseRawImages();
@@ -43,26 +43,32 @@ namespace {
switch (window_type) { switch (window_type) {
case Core::Frontend::WindowSystemType::Headless: case Core::Frontend::WindowSystemType::Headless:
break; break;
#ifdef _WIN32 #if defined(VK_USE_PLATFORM_WIN32_KHR)
case Core::Frontend::WindowSystemType::Windows: case Core::Frontend::WindowSystemType::Windows:
extensions.push_back(VK_KHR_WIN32_SURFACE_EXTENSION_NAME); extensions.push_back(VK_KHR_WIN32_SURFACE_EXTENSION_NAME);
break; break;
#elif defined(__APPLE__) #endif
#if defined(VK_USE_PLATFORM_METAL_EXT)
case Core::Frontend::WindowSystemType::Cocoa: case Core::Frontend::WindowSystemType::Cocoa:
extensions.push_back(VK_EXT_METAL_SURFACE_EXTENSION_NAME); extensions.push_back(VK_EXT_METAL_SURFACE_EXTENSION_NAME);
break; break;
#elif defined(__ANDROID__) #endif
#if defined(VK_USE_PLATFORM_ANDROID_KHR)
case Core::Frontend::WindowSystemType::Android: case Core::Frontend::WindowSystemType::Android:
extensions.push_back(VK_KHR_ANDROID_SURFACE_EXTENSION_NAME); extensions.push_back(VK_KHR_ANDROID_SURFACE_EXTENSION_NAME);
break; break;
#elif defined(__HAIKU__) #endif
#if defined(VK_USE_PLATFORM_XCB_KHR)
case Core::Frontend::WindowSystemType::Xcb: case Core::Frontend::WindowSystemType::Xcb:
extensions.push_back(VK_KHR_XCB_SURFACE_EXTENSION_NAME); extensions.push_back(VK_KHR_XCB_SURFACE_EXTENSION_NAME);
break; break;
#else #endif
#if defined(VK_USE_PLATFORM_XLIB_KHR)
case Core::Frontend::WindowSystemType::X11: case Core::Frontend::WindowSystemType::X11:
extensions.push_back(VK_KHR_XLIB_SURFACE_EXTENSION_NAME); extensions.push_back(VK_KHR_XLIB_SURFACE_EXTENSION_NAME);
break; break;
#endif
#if defined(VK_USE_PLATFORM_WAYLAND_KHR)
case Core::Frontend::WindowSystemType::Wayland: case Core::Frontend::WindowSystemType::Wayland:
extensions.push_back(VK_KHR_WAYLAND_SURFACE_EXTENSION_NAME); extensions.push_back(VK_KHR_WAYLAND_SURFACE_EXTENSION_NAME);
break; break;
@@ -8,6 +8,7 @@
#include "core/frontend/emu_window.h" #include "core/frontend/emu_window.h"
#include "video_core/vulkan_common/vulkan_surface.h" #include "video_core/vulkan_common/vulkan_surface.h"
#include "video_core/vulkan_common/vulkan_wrapper.h" #include "video_core/vulkan_common/vulkan_wrapper.h"
#include "video_core/vulkan_common/vulkan.h"
namespace Vulkan { namespace Vulkan {
@@ -17,7 +18,7 @@ vk::SurfaceKHR CreateSurface(
[[maybe_unused]] const vk::InstanceDispatch& dld = instance.Dispatch(); [[maybe_unused]] const vk::InstanceDispatch& dld = instance.Dispatch();
VkSurfaceKHR unsafe_surface = VkSurfaceKHR{}; VkSurfaceKHR unsafe_surface = VkSurfaceKHR{};
#ifdef _WIN32 #if defined(VK_USE_PLATFORM_WIN32_KHR)
if (window_info.type == Core::Frontend::WindowSystemType::Windows) { if (window_info.type == Core::Frontend::WindowSystemType::Windows) {
const HWND hWnd = static_cast<HWND>(window_info.render_surface); const HWND hWnd = static_cast<HWND>(window_info.render_surface);
const VkWin32SurfaceCreateInfoKHR win32_ci{VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR, const VkWin32SurfaceCreateInfoKHR win32_ci{VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR,
@@ -30,7 +31,8 @@ vk::SurfaceKHR CreateSurface(
throw vk::Exception(VK_ERROR_INITIALIZATION_FAILED); throw vk::Exception(VK_ERROR_INITIALIZATION_FAILED);
} }
} }
#elif defined(__APPLE__) #endif
#if defined(VK_USE_PLATFORM_METAL_EXT)
if (window_info.type == Core::Frontend::WindowSystemType::Cocoa) { if (window_info.type == Core::Frontend::WindowSystemType::Cocoa) {
const VkMetalSurfaceCreateInfoEXT metal_ci = { const VkMetalSurfaceCreateInfoEXT metal_ci = {
.sType = VK_STRUCTURE_TYPE_METAL_SURFACE_CREATE_INFO_EXT, .sType = VK_STRUCTURE_TYPE_METAL_SURFACE_CREATE_INFO_EXT,
@@ -45,7 +47,8 @@ vk::SurfaceKHR CreateSurface(
throw vk::Exception(VK_ERROR_INITIALIZATION_FAILED); throw vk::Exception(VK_ERROR_INITIALIZATION_FAILED);
} }
} }
#elif defined(__ANDROID__) #endif
#if defined(VK_USE_PLATFORM_ANDROID_KHR)
if (window_info.type == Core::Frontend::WindowSystemType::Android) { if (window_info.type == Core::Frontend::WindowSystemType::Android) {
const VkAndroidSurfaceCreateInfoKHR android_ci{ const VkAndroidSurfaceCreateInfoKHR android_ci{
VK_STRUCTURE_TYPE_ANDROID_SURFACE_CREATE_INFO_KHR, nullptr, 0, VK_STRUCTURE_TYPE_ANDROID_SURFACE_CREATE_INFO_KHR, nullptr, 0,
@@ -59,7 +62,8 @@ vk::SurfaceKHR CreateSurface(
throw vk::Exception(VK_ERROR_INITIALIZATION_FAILED); throw vk::Exception(VK_ERROR_INITIALIZATION_FAILED);
} }
} }
#elif defined(__HAIKU__) #endif
#if defined(VK_USE_PLATFORM_XCB_KHR)
if (window_info.type == Core::Frontend::WindowSystemType::Xcb) { if (window_info.type == Core::Frontend::WindowSystemType::Xcb) {
const VkXcbSurfaceCreateInfoKHR xcb_ci{ const VkXcbSurfaceCreateInfoKHR xcb_ci{
.sType = VK_STRUCTURE_TYPE_XCB_SURFACE_CREATE_INFO_KHR, .sType = VK_STRUCTURE_TYPE_XCB_SURFACE_CREATE_INFO_KHR,
@@ -76,7 +80,8 @@ vk::SurfaceKHR CreateSurface(
throw vk::Exception(VK_ERROR_INITIALIZATION_FAILED); throw vk::Exception(VK_ERROR_INITIALIZATION_FAILED);
} }
} }
#else #endif
#if defined(VK_USE_PLATFORM_XLIB_KHR)
if (window_info.type == Core::Frontend::WindowSystemType::X11) { if (window_info.type == Core::Frontend::WindowSystemType::X11) {
const VkXlibSurfaceCreateInfoKHR xlib_ci{ const VkXlibSurfaceCreateInfoKHR xlib_ci{
VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR, nullptr, 0, VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR, nullptr, 0,
@@ -90,6 +95,8 @@ vk::SurfaceKHR CreateSurface(
throw vk::Exception(VK_ERROR_INITIALIZATION_FAILED); throw vk::Exception(VK_ERROR_INITIALIZATION_FAILED);
} }
} }
#endif
#if defined(VK_USE_PLATFORM_WAYLAND_KHR)
if (window_info.type == Core::Frontend::WindowSystemType::Wayland) { if (window_info.type == Core::Frontend::WindowSystemType::Wayland) {
const VkWaylandSurfaceCreateInfoKHR wayland_ci{ const VkWaylandSurfaceCreateInfoKHR wayland_ci{
VK_STRUCTURE_TYPE_WAYLAND_SURFACE_CREATE_INFO_KHR, nullptr, 0, VK_STRUCTURE_TYPE_WAYLAND_SURFACE_CREATE_INFO_KHR, nullptr, 0,
@@ -105,7 +112,6 @@ vk::SurfaceKHR CreateSurface(
} }
} }
#endif #endif
if (!unsafe_surface) { if (!unsafe_surface) {
LOG_ERROR(Render_Vulkan, "Presentation not supported on this platform"); LOG_ERROR(Render_Vulkan, "Presentation not supported on this platform");
throw vk::Exception(VK_ERROR_INITIALIZATION_FAILED); throw vk::Exception(VK_ERROR_INITIALIZATION_FAILED);
+18 -68
View File
@@ -3,6 +3,7 @@
// Static Qt on macOS doesn't use Vulkan // Static Qt on macOS doesn't use Vulkan
// Other platforms do, and thus have conflicting VulkanMemoryAllocator symbols // Other platforms do, and thus have conflicting VulkanMemoryAllocator symbols
#include "core/launch_params.h"
#if defined(QT_STATICPLUGIN) && !defined(__APPLE__) #if defined(QT_STATICPLUGIN) && !defined(__APPLE__)
#undef VMA_IMPLEMENTATION #undef VMA_IMPLEMENTATION
#endif #endif
@@ -515,79 +516,28 @@ MainWindow::MainWindow(bool has_broken_vulkan)
// to prevent the UI from blowing up. // to prevent the UI from blowing up.
UpdateUITheme(); UpdateUITheme();
QStringList args = QApplication::arguments(); if (QStringList args = QApplication::arguments(); args.size() >= 2) {
QList<QByteArray> qba{};
for (auto const& e : args)
qba.push_back(e.toUtf8());
std::vector<char*> args_cstr{};
for (auto& e : qba)
args_cstr.push_back(e.data());
if (args.size() < 2) { auto const lp = Core::ParseLaunchParams(*QtCommon::system, args_cstr.size(), args_cstr.data(), nullptr);
return;
}
QString game_path; // Override fullscreen setting if gamepath or argument is provided
bool should_launch_qlaunch = false; if (!lp.filepath.empty() || lp.fullscreen) {
bool should_launch_hlaunch = false; ui->action_Fullscreen->setChecked(lp.fullscreen);
bool should_launch_setup = false;
bool has_gamepath = false;
bool is_fullscreen = false;
// Preserves drag/drop functionality
for (int i = 1; i < args.size(); ++i) {
if (args[i] == QStringLiteral("-f")) {
// Launch game in fullscreen mode
is_fullscreen = true;
} else if (args[i] == QStringLiteral("-u") && i < args.size() - 1) {
// Launch game with a specific user
int user_arg_idx = ++i;
bool argument_ok;
std::size_t selected_user = args[user_arg_idx].toUInt(&argument_ok);
if (!argument_ok) {
// try to look it up by username, only finds the first username that matches.
std::string const user_arg_str = args[user_arg_idx].toStdString();
auto const user_idx =
QtCommon::system->GetProfileManager().GetUserIndex(user_arg_str);
if (user_idx != std::nullopt) {
selected_user = user_idx.value();
} else {
LOG_ERROR(Frontend, "Invalid user argument '{}'", user_arg_str);
continue;
}
}
if (QtCommon::system->GetProfileManager().UserExistsIndex(selected_user)) {
Settings::values.current_user = s32(selected_user);
user_flag_cmd_line = true;
} else {
LOG_ERROR(Frontend, "Selected user {} doesn't exist", selected_user);
}
} else if (args[i] == QStringLiteral("-g") && i < args.size() - 1) {
// Launch game at path
game_path = args[++i];
has_gamepath = true;
} else if (args[i] == QStringLiteral("-input-profile") && i < args.size() - 1) {
auto& players = Settings::values.players.GetValue();
players[0].profile_name = args[++i].toStdString();
} else if (args[i] == QStringLiteral("-qlaunch")) {
should_launch_qlaunch = true;
} else if (args[i] == QStringLiteral("-hlaunch")) {
should_launch_hlaunch = true;
} else if (args[i] == QStringLiteral("-setup")) {
should_launch_setup = true;
} else {
game_path = args[i];
has_gamepath = true;
} }
}
// Override fullscreen setting if gamepath or argument is provided if (!lp.filepath.empty()) {
if (has_gamepath || is_fullscreen) { BootGame(QString::fromStdString(lp.filepath), ApplicationAppletParameters());
ui->action_Fullscreen->setChecked(is_fullscreen); } else if (lp.launch_setup) {
} LaunchFirmwareApplet(u64(Service::AM::AppletProgramId::Starter), std::nullopt);
} else if (lp.launch_qlaunch) {
if (should_launch_setup) {
LaunchFirmwareApplet(u64(Service::AM::AppletProgramId::Starter), std::nullopt);
} else {
if (!game_path.isEmpty()) {
BootGame(game_path, ApplicationAppletParameters());
} else if (should_launch_qlaunch) {
LaunchFirmwareApplet(u64(Service::AM::AppletProgramId::QLaunch), std::nullopt); LaunchFirmwareApplet(u64(Service::AM::AppletProgramId::QLaunch), std::nullopt);
} else if (should_launch_hlaunch) { } else if (lp.launch_hlaunch) {
std::filesystem::path const sd_dir = std::filesystem::path const sd_dir =
Common::FS::GetEdenPathString(Common::FS::EdenPath::SDMCDir); Common::FS::GetEdenPathString(Common::FS::EdenPath::SDMCDir);
auto const hbl_path = (sd_dir / "atmosphere" / "hbl.nsp").string(); auto const hbl_path = (sd_dir / "atmosphere" / "hbl.nsp").string();
-3
View File
@@ -42,9 +42,6 @@ target_link_libraries(yuzu-cmd PRIVATE common core input_common frontend_common
if (ENABLE_OPENGL) if (ENABLE_OPENGL)
target_link_libraries(yuzu-cmd PRIVATE glad) target_link_libraries(yuzu-cmd PRIVATE glad)
endif() endif()
if (MSVC)
target_link_libraries(yuzu-cmd PRIVATE getopt)
endif()
target_link_libraries(yuzu-cmd PRIVATE ${PLATFORM_LIBRARIES} Threads::Threads) target_link_libraries(yuzu-cmd PRIVATE ${PLATFORM_LIBRARIES} Threads::Threads)
create_resource("../../dist/eden.bmp" "yuzu_cmd/yuzu_icon.h" "yuzu_icon") create_resource("../../dist/eden.bmp" "yuzu_cmd/yuzu_icon.h" "yuzu_icon")
+15 -189
View File
@@ -10,6 +10,7 @@
#include <regex> #include <regex>
#include <string> #include <string>
#include "common/settings_enums.h" #include "common/settings_enums.h"
#include "core/launch_params.h"
#ifdef __EMSCRIPTEN__ #ifdef __EMSCRIPTEN__
#include <emscripten.h> #include <emscripten.h>
#endif #endif
@@ -52,12 +53,6 @@
#include "common/windows/timer_resolution.h" #include "common/windows/timer_resolution.h"
#endif #endif
#undef _UNICODE
#include <getopt.h>
#ifndef _MSC_VER
#include <unistd.h>
#endif
#ifdef _WIN32 #ifdef _WIN32
extern "C" { extern "C" {
// tells Nvidia and AMD drivers to use the dedicated GPU by default on laptops with switchable // tells Nvidia and AMD drivers to use the dedicated GPU by default on laptops with switchable
@@ -67,25 +62,6 @@ __declspec(dllexport) int AmdPowerXpressRequestHighPerformance = 1;
} }
#endif #endif
static void PrintHelp(const char* argv0) {
std::cout << "Usage: " << argv0
<< " [options] <filename>\n"
"-c, --config Load the specified configuration file\n"
"-f, --fullscreen Start in fullscreen mode\n"
"-g, --game File path of the game to load\n"
"-h, --help Display this help and exit\n"
"-m, --multiplayer=nick:password@address:port"
" Nickname, password, address and port for multiplayer\n"
"-p, --program Pass following string as arguments to executable\n"
"-u, --user Select a specific user profile from 0 to 7\n"
"-d, --debug Run the GDB stub on a port from 1 to 65535\n"
"-v, --version Output version information and exit\n";
}
static void PrintVersion() {
std::cout << "Eden " << Common::g_scm_branch << " " << Common::g_scm_desc << std::endl;
}
static void OnStateChanged(const Network::RoomMember::State& state) { static void OnStateChanged(const Network::RoomMember::State& state) {
switch (state) { switch (state) {
case Network::RoomMember::State::Idle: case Network::RoomMember::State::Idle:
@@ -208,168 +184,18 @@ extern "C" SDL_AppResult SDL_AppInit(void **appstate, int argc, char **argv) {
LOG_CRITICAL(Frontend, "Failed to get command line arguments"); LOG_CRITICAL(Frontend, "Failed to get command line arguments");
return SDL_APP_FAILURE; return SDL_APP_FAILURE;
} }
#endif
std::string filepath;
std::optional<std::string> config_path{};
std::string program_args;
std::optional<int> selected_user{};
std::optional<u16> override_gdb_port{};
bool use_multiplayer = false;
bool fullscreen = false;
bool force_null_render = false;
bool force_single_core = false;
std::string nickname{};
std::string password{};
std::string address{};
std::string input_profile{};
std::optional<std::string> log_filter{};
u16 port = Network::DefaultRoomPort;
static struct option long_options[] = {
// clang-format off
{"debug", no_argument, 0, 'd'},
{"config", required_argument, 0, 'c'},
{"fullscreen", no_argument, 0, 'f'},
{"help", no_argument, 0, 'h'},
{"game", required_argument, 0, 'g'},
{"multiplayer", required_argument, 0, 'm'},
{"program", optional_argument, 0, 'p'},
{"user", required_argument, 0, 'u'},
{"version", no_argument, 0, 'v'},
{"input-profile", no_argument, 0, 'i'},
{"null-render", no_argument, 0, 'n'},
{"singlecore", no_argument, 0, 's'},
{"filter", no_argument, 0, 'x'},
{0, 0, 0, 0},
// clang-format on
};
while (optind < argc) {
int arg = getopt_long(argc, argv, "g:fhvcip::c:u:d:", long_options, &option_index);
if (arg != -1) {
switch (char(arg)) {
case 'd':
override_gdb_port = uint16_t(atoi(optarg));
break;
case 'c':
config_path = optarg;
break;
case 'f':
fullscreen = true;
LOG_INFO(Frontend, "Starting in fullscreen mode...");
break;
case 'h':
PrintHelp(argv[0]);
return SDL_APP_FAILURE;
case 'g':
filepath = std::string(optarg);
break;
case 'i': {
input_profile = std::string(optarg);
break;
}
case 'm': {
use_multiplayer = true;
const std::string str_arg(optarg);
// regex to check if the format is nickname:password@ip:port
// with optional :password
const std::regex re("^([^:]+)(?::(.+))?@([^:]+)(?::([0-9]+))?$");
if (!std::regex_match(str_arg, re)) {
std::cout << "Wrong format for option --multiplayer\n";
PrintHelp(argv[0]);
return SDL_APP_FAILURE;
}
std::smatch match;
std::regex_search(str_arg, match, re);
ASSERT(match.size() == 5);
nickname = match[1];
password = match[2];
address = match[3];
if (!match[4].str().empty()) {
port = u16(std::strtoul(match[4].str().c_str(), nullptr, 0));
}
std::regex nickname_re("^[a-zA-Z0-9._\\- ]+$");
if (!std::regex_match(nickname, nickname_re)) {
LOG_ERROR(Frontend, "Nickname is not valid. Must be 4 to 20 alphanumeric characters");
return SDL_APP_FAILURE;
}
if (address.empty()) {
LOG_ERROR(Frontend, "Address to room must not be empty");
return SDL_APP_FAILURE;
}
break;
}
case 'p':
program_args = argv[optind];
++optind;
break;
case 'u':
selected_user = atoi(optarg);
break;
case 'v':
PrintVersion();
return SDL_APP_FAILURE;
case 'n':
force_null_render = true;
break;
case 's':
force_single_core = true;
break;
case 'x':
log_filter = argv[optind];
++optind;
break;
}
} else {
#ifdef _WIN32
filepath = Common::UTF16ToUTF8(argv_w[optind]);
#else #else
filepath = argv[optind]; wchar_t **argv_w = nullptr;
#endif #endif
optind++; Core::LaunchParams lp = Core::ParseLaunchParams(state->system, argc, argv, argv_w);
}
}
SdlConfig config{config_path};
// apply the log_filter setting
// the logger was initialized before and doesn't pick up the filter on its own
Common::Log::Filter filter;
filter.ParseFilterString(log_filter.value_or(Settings::values.log_filter.GetValue()));
Common::Log::SetGlobalFilter(filter);
if (!program_args.empty()) {
Settings::values.program_args = program_args;
}
if (!input_profile.empty()) {
auto& players = Settings::values.players.GetValue();
players[0].profile_name = input_profile;
}
if (selected_user.has_value()) {
Settings::values.current_user = std::clamp(*selected_user, 0, 7);
}
if (override_gdb_port.has_value()) {
Settings::values.use_gdbstub = true;
Settings::values.gdbstub_port = *override_gdb_port;
}
if (force_single_core) {
Settings::values.use_multi_core = false;
}
if (force_null_render) {
Settings::values.renderer_backend = Settings::RendererBackend::Null;
}
#ifdef _WIN32 #ifdef _WIN32
LocalFree(argv_w); LocalFree(argv_w);
#endif #endif
SdlConfig config{lp.config_path};
if (filepath.empty()) { Core::ApplyLaunchParams(lp);
if (lp.filepath.empty()) {
LOG_CRITICAL(Frontend, "Failed to load ROM: No ROM specified"); LOG_CRITICAL(Frontend, "Failed to load ROM: No ROM specified");
return SDL_APP_FAILURE; return SDL_APP_FAILURE;
} }
@@ -386,14 +212,14 @@ extern "C" SDL_AppResult SDL_AppInit(void **appstate, int argc, char **argv) {
case Settings::RendererBackend::OpenGL_GLSL: case Settings::RendererBackend::OpenGL_GLSL:
case Settings::RendererBackend::OpenGL_GLASM: case Settings::RendererBackend::OpenGL_GLASM:
case Settings::RendererBackend::OpenGL_SPIRV: case Settings::RendererBackend::OpenGL_SPIRV:
state->emu_window = std::make_unique<EmuWindow_SDL3_GL>(&input_subsystem, state->system, fullscreen); state->emu_window = std::make_unique<EmuWindow_SDL3_GL>(&input_subsystem, state->system, lp.fullscreen);
break; break;
#endif #endif
case Settings::RendererBackend::Vulkan: case Settings::RendererBackend::Vulkan:
state->emu_window = std::make_unique<EmuWindow_SDL3_VK>(&input_subsystem, state->system, fullscreen); state->emu_window = std::make_unique<EmuWindow_SDL3_VK>(&input_subsystem, state->system, lp.fullscreen);
break; break;
case Settings::RendererBackend::Null: case Settings::RendererBackend::Null:
state->emu_window = std::make_unique<EmuWindow_SDL3_Null>(&input_subsystem, state->system, fullscreen); state->emu_window = std::make_unique<EmuWindow_SDL3_Null>(&input_subsystem, state->system, lp.fullscreen);
break; break;
default: default:
LOG_CRITICAL(Frontend, "Invalid renderer backend"); LOG_CRITICAL(Frontend, "Invalid renderer backend");
@@ -413,12 +239,12 @@ extern "C" SDL_AppResult SDL_AppInit(void **appstate, int argc, char **argv) {
Service::AM::FrontendAppletParameters load_parameters{ Service::AM::FrontendAppletParameters load_parameters{
.applet_id = Service::AM::AppletId::Application, .applet_id = Service::AM::AppletId::Application,
}; };
const Core::SystemResultStatus load_result = state->system.Load(*state->emu_window, filepath, load_parameters); const Core::SystemResultStatus load_result = state->system.Load(*state->emu_window, lp.filepath, load_parameters);
switch (load_result) { switch (load_result) {
case Core::SystemResultStatus::Success: case Core::SystemResultStatus::Success:
break; // Expected case break; // Expected case
case Core::SystemResultStatus::ErrorGetLoader: case Core::SystemResultStatus::ErrorGetLoader:
LOG_CRITICAL(Frontend, "Failed to obtain loader for {}!", filepath); LOG_CRITICAL(Frontend, "Failed to obtain loader for {}!", lp.filepath);
return SDL_APP_FAILURE; return SDL_APP_FAILURE;
case Core::SystemResultStatus::ErrorLoader: case Core::SystemResultStatus::ErrorLoader:
LOG_CRITICAL(Frontend, "Failed to load ROM!"); LOG_CRITICAL(Frontend, "Failed to load ROM!");
@@ -440,14 +266,14 @@ extern "C" SDL_AppResult SDL_AppInit(void **appstate, int argc, char **argv) {
return SDL_APP_FAILURE; return SDL_APP_FAILURE;
} }
if (use_multiplayer) { if (lp.use_multiplayer) {
if (auto member = Network::GetRoomMember().lock()) { if (auto member = Network::GetRoomMember().lock()) {
member->BindOnChatMessageReceived(OnMessageReceived); member->BindOnChatMessageReceived(OnMessageReceived);
member->BindOnStatusMessageReceived(OnStatusMessageReceived); member->BindOnStatusMessageReceived(OnStatusMessageReceived);
member->BindOnStateChanged(OnStateChanged); member->BindOnStateChanged(OnStateChanged);
member->BindOnError(OnNetworkError); member->BindOnError(OnNetworkError);
LOG_DEBUG(Network, "Start connection to {}:{} with nickname {}", address, port, nickname); LOG_DEBUG(Network, "Start connection to {}:{} with nickname {}", lp.address, lp.port, lp.nickname);
member->Join(nickname, address.c_str(), port, 0, Network::NoPreferredIP, password); member->Join(lp.nickname, lp.address.c_str(), lp.port, 0, Network::NoPreferredIP, lp.password);
} else { } else {
LOG_ERROR(Network, "Could not access RoomMember"); LOG_ERROR(Network, "Could not access RoomMember");
return SDL_APP_FAILURE; return SDL_APP_FAILURE;