Compare commits

...

18 Commits

Author SHA1 Message Date
lizzie d724265658 fix getopt 2026-08-14 05:25:55 +00:00
lizzie 40e4a3f0a3 windowed death 2026-08-14 03:35:58 +00:00
lizzie 8b58a4f438 eatshi 2026-08-14 00:32:50 +00:00
lizzie 10b8a1b3c7 no way 2026-08-14 00:11:57 +00:00
lizzie a01fcd51cb ra 2026-08-13 23:35:40 +00:00
lizzie 5d05c88d9b g 2026-08-13 22:51:38 +00:00
lizzie 850394bd8c fixine 2026-08-13 19:43:33 +00:00
lizzie e84ce480f1 saucier arg parsing! 2026-08-13 19:02:23 +00:00
lizzie 13c9a055f9 [eden-cli] merge eden-room unto eden-cli
Signed-off-by: lizzie <lizzie@eden-emu.dev>
2026-08-13 18:32:29 +00:00
lizzie e7a931d06b [eden-cli] rework loop to use new SDL3 event dispatch system (#4108)
modernized into the new SDL3 API for event dispatching.

also stopped using the periodic comparison and just added a SDL timer like it wants us to do
is it better than before? well it's certainly more portable now. docs say this improves battery usage on busywaits for event polling.

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

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4108
Reviewed-by: Maufeat <sahyno1996@gmail.com>
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
2026-08-12 18:10:24 +02:00
crueter 8648c27cbb [cmake] Simplify DetectPlatform and normalize platform names (#4183)
Since 3.25 introduced `LINUX` and `BSD`, use them, and standardize other platforms to that style

Signed-off-by: crueter <crueter@eden-emu.dev>
Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4183
Reviewed-by: Samuel <lizzie@eden-emu.dev>
Reviewed-by: CamilleLaVey <camillelavey99@gmail.com>
2026-08-10 04:57:38 +02:00
codeman4033 def76946f0 [forgejo] Replace mentions of yuzu in issue templates with eden (#3842)
Also fix the broken link for log files in the bug report

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

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/3842
Reviewed-by: crueter <crueter@eden-emu.dev>
Reviewed-by: Samuel <lizzie@eden-emu.dev>
2026-08-10 03:55:03 +02:00
BoiledElectricity 13d39f39aa [qt] fix relative ROM paths passed on the CLI on macOS (#4105)
On macOS, convert relative paths to absolute paths before changing the
working directory. Otherwise, valid Switch formats may incorrectly fail
with a "The ROM format is not supported" error.

Fixes: https://github.com/eden-emulator/Issue-Reports/issues/404
Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4105
Reviewed-by: crueter <crueter@eden-emu.dev>
Reviewed-by: Samuel <lizzie@eden-emu.dev>
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
2026-08-09 17:18:58 +02:00
lizzie 7731b5bc86 [core/hle/kernel/svc] demote ResetSignal log Debug->Trace (#4161)
I don't see why it's LOG_DEBUG if WaitSynchronization is LOG_TRACE, leaves incomplete data
may as well bundle it to trace with the others? :)

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

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4161
Reviewed-by: CamilleLaVey <camillelavey99@gmail.com>
Reviewed-by: Maufeat <sahyno1996@gmail.com>
2026-08-09 14:13:27 +02:00
lizzie c4ab2f6b9e [cmake] Add ENABLE_WERROR option (#4080)
Should warnings be errors? that's a philosophical debate which I can only answer with "no, but if you think otherwise here is an option for you"

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

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4080
Reviewed-by: crueter <crueter@eden-emu.dev>
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
2026-08-09 02:56:28 +02:00
xbzk dd12266c26 [input] added option to disable wgi/xinput to prevent SDL GUIDE hack (#4237)
- [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.

-------------------
So, many users hate the fact when they press START+SELECT, an additional HOME event is sent.

That is annoying coz for most users' default hotkey settings, it closes eden when not in game, and when in game it calls a NOT controller responsive prompt about leaving Eden.
There are several games with reasons to press start+select, also there are homebrews which relies on that combo to open internal menus.
Again. That is annoying.

Digging deep i've found that SDL implements something called guide hack: When Start+Select is pressed it sends a synthetic GUIDE(which code equals to HOME).
This was intended for controllers missing the HOME button, but SDL fails to track them all, and misses API to disabling it.

I've figured out a patch to SDL to disable it, but since it would be better to invest on a PR straight into SDL repo, on our side i thought it better to simply add an option to disable WGI (Windows.Gaming.Input) and Xinput, both paths which could lead into the hack enabling.

Setting is available in a remote place no one will bother.

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4237
Reviewed-by: Samuel <lizzie@eden-emu.dev>
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
2026-08-09 02:53:37 +02:00
crueter a3d32a18b8 [meta] Discourage profanity usage (#4239)
I'm not into millennial humor, personally.

Proactively removed as much profanity as I could grep.

Signed-off-by: crueter <crueter@eden-emu.dev>
Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4239
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
Reviewed-by: Samuel <lizzie@eden-emu.dev>
2026-08-09 02:53:16 +02:00
crueter c0ffc900cd [ci] Only update translations once a week (#4256)
They've slowed down enough at this point.

Signed-off-by: crueter <crueter@eden-emu.dev>
Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4256
2026-08-08 05:46:52 +02:00
66 changed files with 1044 additions and 1142 deletions
+2 -2
View File
@@ -1,9 +1,9 @@
name: tx-pull
on:
# tuesday, saturday at 2pm
# monday at 4pm
schedule:
- cron: '0 14 * * 2,6'
- cron: '0 16 * * 1'
workflow_dispatch:
jobs:
+2 -2
View File
@@ -3,7 +3,7 @@ description: File a bug report
body:
- type: markdown
attributes:
value: Tech support does not belong here. You should only file an issue here if you think you have experienced an actual bug with yuzu.
value: Tech support does not belong here. You should only file an issue here if you think you have experienced an actual bug with Eden.
- type: checkboxes
attributes:
label: Is there an existing issue for this?
@@ -43,7 +43,7 @@ body:
id: log
attributes:
label: Log File
description: A log file will help our developers to better diagnose and fix the issue. Instructions can be found [here](https://yuzu-emu.org/help/reference/log-files).
description: A log file will help our developers to better diagnose and fix the issue. Instructions can be found [here](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/user/HowToAccessLogs.md).
validations:
required: true
- type: textarea
+2 -2
View File
@@ -4,7 +4,7 @@ labels: "request"
body:
- type: markdown
attributes:
value: Tech support does not belong here. You should only file an issue here if you are requesting a feature you believe would make yuzu better.
value: Tech support does not belong here. You should only file an issue here if you are requesting a feature you believe would make Eden better.
- type: checkboxes
attributes:
label: Is there an existing issue for this?
@@ -23,6 +23,6 @@ body:
id: why-feature
attributes:
label: Why would this feature be useful?
description: A brief description of why this feature would make yuzu better.
description: A brief description of why this feature would make Eden better.
validations:
required: true
+29 -25
View File
@@ -28,27 +28,24 @@ if (NOT DEFINED ARCHITECTURE)
endif()
# Needed for FFmpeg w/ VAAPI and DRM
if (PLATFORM_OPENBSD)
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 (PLATFORM_NETBSD)
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()
# NetBSD: Fun for the whole family!
if (PLATFORM_NETBSD)
if (NETBSD)
set(ENV{PKG_CONFIG_PATH} "${PKG_CONFIG_PATH}:${CMAKE_SYSROOT}/usr/pkg/lib/ffmpeg7/pkgconfig")
endif()
cmake_dependent_option(YUZU_STATIC_ROOM "Build a static room executable only (CI only)" OFF "PLATFORM_LINUX" OFF)
cmake_dependent_option(YUZU_STATIC_ROOM "Build a static room executable only (CI only)" OFF "LINUX" OFF)
if (YUZU_STATIC_ROOM)
set(YUZU_ROOM ON)
set(YUZU_ROOM_STANDALONE ON)
# disable e v e r y t h i n g
set(ENABLE_QT OFF)
set(YUZU_CMD OFF)
@@ -79,6 +76,7 @@ set(YUZU_QT_MIRROR "" CACHE STRING "What mirror to use for downloading the bundl
cmake_dependent_option(YUZU_USE_BUNDLED_QT "Download bundled Qt binaries" "${MSVC}" "ENABLE_QT" OFF)
option(ENABLE_DEBUG_TOOLS "Enable debugging tools (maxwell disassembler, SPIRV translator, etc)" OFF)
option(ENABLE_WERROR "Enable -Werror diagnostics" ON)
# non-linux bundled qt are static
if (YUZU_USE_BUNDLED_QT AND (APPLE OR NOT UNIX))
@@ -157,6 +155,19 @@ if (CXX_CLANG_CL)
$<$<COMPILE_LANGUAGE:C,CXX>:-Wno-reserved-identifier>
$<$<COMPILE_LANGUAGE:C,CXX>:-Wno-deprecated-declarations>
$<$<COMPILE_LANGUAGE:C,CXX>:-Wno-cast-function-type-mismatch>
$<$<COMPILE_LANGUAGE:C,CXX>:-Wno-c99-extensions>
$<$<COMPILE_LANGUAGE:C,CXX>:-Wno-c++17-compat>
$<$<COMPILE_LANGUAGE:C,CXX>:-Wno-c++11-compat-reserved-user-defined-literal>
$<$<COMPILE_LANGUAGE:C,CXX>:-Wno-c++11-compat-deprecated-writable-strings>
$<$<COMPILE_LANGUAGE:C,CXX>:-Wno-c++11-compat-pedantic>
$<$<COMPILE_LANGUAGE:C,CXX>:-Wno-c++11-compat>
$<$<COMPILE_LANGUAGE:C,CXX>:-Wno-c++0x-compat>
$<$<COMPILE_LANGUAGE:C,CXX>:-Wno-c++98-c++11-compat-binary-literal>
$<$<COMPILE_LANGUAGE:C,CXX>:-Wno-c++98-compat-pedantic>
$<$<COMPILE_LANGUAGE:C,CXX>:-Wno-c++98-compat>
$<$<COMPILE_LANGUAGE:C,CXX>:-Wno-c99-compat>
$<$<COMPILE_LANGUAGE:C,CXX>:-Wno-c98-compat>
$<$<COMPILE_LANGUAGE:C,CXX>:-Wno-c99-extensions>
$<$<COMPILE_LANGUAGE:C,CXX>:/EHsc>)
# REQUIRED CPU features IN Windows-amd64
if (ARCHITECTURE_x86_64)
@@ -210,7 +221,7 @@ endif()
# ffmpeg
option(YUZU_USE_BUNDLED_FFMPEG "Download bundled FFmpeg" ${EXT_DEFAULT})
cmake_dependent_option(YUZU_USE_EXTERNAL_FFMPEG "Build FFmpeg from external source" "${PLATFORM_SUN}" "NOT WIN32 AND NOT ANDROID" OFF)
cmake_dependent_option(YUZU_USE_EXTERNAL_FFMPEG "Build FFmpeg from external source" "${SOLARIS}" "NOT WIN32 AND NOT ANDROID" OFF)
# sirit
set(BUNDLED_SIRIT_DEFAULT OFF)
@@ -221,7 +232,7 @@ endif()
option(YUZU_USE_BUNDLED_SIRIT "Download bundled sirit" ${BUNDLED_SIRIT_DEFAULT})
# FreeBSD 15+ has libusb, versions below should disable it
cmake_dependent_option(ENABLE_LIBUSB "Enable the use of LibUSB" ON "WIN32 OR PLATFORM_LINUX OR PLATFORM_FREEBSD OR APPLE" OFF)
cmake_dependent_option(ENABLE_LIBUSB "Enable the use of LibUSB" ON "WIN32 OR LINUX OR FREEBSD OR APPLE" OFF)
cmake_dependent_option(ENABLE_OPENGL "Enable OpenGL" ON "NOT (WIN32 AND ARCHITECTURE_arm64) AND NOT APPLE" OFF)
mark_as_advanced(FORCE ENABLE_OPENGL)
@@ -235,7 +246,7 @@ option(YUZU_TESTS "Compile tests" "${BUILD_TESTING}")
# Install udev rules on Linux (mainly for gyros)
# Only acts on joysticks and nothing else.
cmake_dependent_option(YUZU_INSTALL_UDEV_RULES "Install udev rules for gyro access" OFF "PLATFORM_LINUX" OFF)
cmake_dependent_option(YUZU_INSTALL_UDEV_RULES "Install udev rules for gyro access" OFF "LINUX" OFF)
option(YUZU_DOWNLOAD_ANDROID_VVL "Download validation layer binary for android" ON)
@@ -243,12 +254,9 @@ option(YUZU_LEGACY "Apply patches that improve compatibility with older GPUs (e.
option(NIGHTLY_BUILD "Use Nightly qualifiers in the update checker and build metadata" OFF)
cmake_dependent_option(YUZU_ROOM "Enable dedicated room functionality" ON "NOT ANDROID" OFF)
cmake_dependent_option(YUZU_ROOM_STANDALONE "Enable standalone room executable" ON "YUZU_ROOM" OFF)
cmake_dependent_option(YUZU_CMD "Compile the eden-cli executable" ON "NOT ANDROID" OFF)
cmake_dependent_option(YUZU_CRASH_DUMPS "Compile crash dump (Minidump) support" OFF "WIN32 OR PLATFORM_LINUX" OFF)
cmake_dependent_option(YUZU_CRASH_DUMPS "Compile crash dump (Minidump) support" OFF "WIN32 OR LINUX" OFF)
option(YUZU_DOWNLOAD_TIME_ZONE_DATA "Always download time zone binaries" ON)
set(YUZU_TZDB_PATH "" CACHE STRING "Path to a pre-downloaded timezone database")
@@ -258,7 +266,7 @@ cmake_dependent_option(YUZU_USE_BUNDLED_MOLTENVK "Download bundled MoltenVK lib"
option(YUZU_DISABLE_LLVM "Disable LLVM (useful for CI)" OFF)
set(DEFAULT_YUZU_USE_BUNDLED_OPENSSL OFF)
if (EXT_DEFAULT OR PLATFORM_SUN OR PLATFORM_OPENBSD)
if (EXT_DEFAULT OR SOLARIS OR OPENBSD)
set(DEFAULT_YUZU_USE_BUNDLED_OPENSSL ON)
endif()
@@ -281,16 +289,12 @@ if(EXISTS ${PROJECT_SOURCE_DIR}/hooks/pre-commit AND NOT EXISTS ${PROJECT_SOURCE
endif()
endif()
if (ARCHITECTURE_arm64 AND (ANDROID OR PLATFORM_LINUX))
if (ARCHITECTURE_arm64 AND (ANDROID OR LINUX))
set(HAS_NCE 1)
add_compile_definitions(HAS_NCE=1)
endif()
if (YUZU_ROOM)
add_compile_definitions(YUZU_ROOM)
endif()
if (UNIX AND NOT (PLATFORM_LINUX OR WIN32))
if (UNIX AND NOT (LINUX OR WIN32))
if(CXX_APPLE OR CXX_CLANG)
# libc++ has stop_token and jthread as experimental
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fexperimental-library")
@@ -404,7 +408,7 @@ if (Boost_ADDED)
if (NOT MSVC OR CXX_CLANG)
# boost sucks
if (PLATFORM_SUN)
if (SOLARIS)
add_compile_options($<$<COMPILE_LANGUAGE:C,CXX>:-pthreads>)
endif()
@@ -508,9 +512,9 @@ elseif (WIN32)
# PSAPI is the Process Status API
set(PLATFORM_LIBRARIES ${PLATFORM_LIBRARIES} psapi imm32 version crypt32 rpcrt4 gdi32 wldap32 mswsock)
endif()
elseif (PLATFORM_MANAGARM)
elseif (MANAGARM)
set(PLATFORM_LIBRARIES iconv intl)
elseif (PLATFORM_HAIKU)
elseif (HAIKUOS)
# Haiku is so special :)
set(PLATFORM_LIBRARIES bsd /boot/system/lib/libnetwork.so)
elseif (CMAKE_SYSTEM_NAME MATCHES "^(Linux|kFreeBSD|GNU|SunOS)$")
@@ -604,7 +608,7 @@ if (ENABLE_QT)
# 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)
if (PLATFORM_LINUX OR PLATFORM_FREEBSD)
if (LINUX OR FREEBSD)
list(APPEND YUZU_QT_COMPONENTS DBus)
# yes Qt, we get it
set(QT_NO_PRIVATE_MODULE_WARNING ON)
+1 -1
View File
@@ -13,7 +13,7 @@ find_package_handle_standard_args(Opus
VERSION_VAR OPUS_VERSION
)
if (PLATFORM_MSYS)
if (MSYS2)
FixMsysPath(PkgConfig::OPUS)
endif()
+1 -1
View File
@@ -14,7 +14,7 @@ find_package_handle_standard_args(enet
VERSION_VAR ENET_VERSION
)
if (PLATFORM_MSYS)
if (MSYS2)
FixMsysPath(PkgConfig::ENET)
endif()
+1 -1
View File
@@ -14,7 +14,7 @@ find_package_handle_standard_args(libusb
VERSION_VAR LIBUSB_VERSION
)
if (PLATFORM_MSYS)
if (MSYS2)
FixMsysPath(PkgConfig::LIBUSB)
endif()
+1 -1
View File
@@ -13,7 +13,7 @@ else()
find_package(PkgConfig QUIET)
pkg_search_module(LZ4 QUIET IMPORTED_TARGET liblz4)
if (PLATFORM_MSYS)
if (MSYS2)
FixMsysPath(PkgConfig::LZ4)
endif()
+5 -1
View File
@@ -22,9 +22,13 @@ Eden is free, open-source, copyleft software, licensed under the terms of the [G
- No LLM or AI usage, *period*, for patches, pull requests, issues, comments, debugging, brainstorming, etc.
- For details on why, see the [detailed AI policy](docs/policies/AI.md).
- Usage of any form of profanity or otherwise unsavory language is generally discouraged.
- This is primarily because it rarely helps to actually understand what's going on.
- Remember that your comments should be focused and actually address what's happening. With very few exceptions, expletives are actively detrimental at best.
- New code must follow the same general style as the surrounding codebase. Exceptions may be granted in certain cases.
- Maintainers reserve the right to change your patches and pull requests at will. We will try to avoid this.
- You should respect all decisions made by the [code owners](docs/CODEOWNERS) in your particular subsystem. If you feel they are overstepping or are incorrect, don't be afraid to stand your ground!
- You should generally respect all decisions made by the [code owners](docs/CODEOWNERS) in your particular subsystem.
- However, if you feel they are overstepping or are incorrect, don't be afraid to stand your ground! Maintainers are not always correct.
- While we do *not* adhere to the terms of a formal code of conduct, you will generally be expected to respect other developers, contributors, and community members.
- You **must** have basic knowledge of [Git](https://git-scm.com/learn). Knowing how to manage your branches and follow proper fork policies is a necessity.
+4 -2
View File
@@ -46,6 +46,8 @@ These options control dependencies.
- `YUZU_INSTALL_UDEV_RULES` (OFF) Install udev rules to enable hidraw access
- Needed for gyroscopes
- Only available on Linux
- `ENABLE_DEBUG_TOOLS` (OFF) Enables debugging and development tools, see [tools](../tools/README.md).
- `ENABLE_WERROR` (ON) Enables warnings as errors (-Werror).
### Flavors
@@ -63,8 +65,6 @@ These options control executables and build flavors.
**Desktop only**:
- `YUZU_CMD` (ON) Compile the SDL-based frontend (eden-cli)
- `YUZU_ROOM` (OFF) Compile dedicated room functionality into the main executable
- `YUZU_ROOM_STANDALONE` (OFF) Compile a separate executable for room functionality
- `YUZU_STATIC_ROOM` (OFF) Compile the room executable *only* as a static, portable executable
- This is only usable on Alpine Linux.
@@ -99,5 +99,7 @@ The following options were a part of Eden at one point, but have since been reti
- `YUZU_USE_CPM` - This option once had a purpose, but that purpose has long since passed us by. *All* builds use CPMUtil to manage dependencies now.
- If you want to *force* the usage of system dependencies, use `-DCPMUTIL_FORCE_SYSTEM=ON`.
- `YUZU_USE_EXTERNAL_SDL` - This is now handled automatically. It was included even after CPM for purposes that have not applied for a very long time.
- `YUZU_ROOM` - Room functionality is now built-in into eden-cli unconditionally.
- `YUZU_ROOM_STANDALONE` - Likewise.
See `src/dynarmic/CMakeLists.txt` for additional options--usually, these don't need changed
+1 -1
View File
@@ -34,7 +34,7 @@ Everyone has their own way of viewing good/bad C++ practices, my general outline
- The reason is because the project has `-fno-rtti` disabled by default, due to the costs of dynamic polymorphism.
- Always copy-on-value for objects with `sizeof(void *) >= sizeof(T) * 2`, i.e objects sized as 2 pointers or less, for bigger objects you can use ref/pointer as usual.
- Try using move semantics instead of references, whenever possible.
- Remember function parameters are extremelly cheap as fuck, don't be afraid to place upto 8 parameters on a given function.
- Function parameters are cheap. Don't be afraid to use as many as needed (API usability permitting).
- Don't save a reference in structures of a parent object, i.e:
```c++
+37 -22
View File
@@ -1,32 +1,47 @@
# 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 command line arguments and share almost the same accepted 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 behaviour 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.
- `-f`: Use fullscreen.
- `-u <number>`: Select the index of the user to load as.
- `-input-profile <name>`: Specifies input profile name to use (for player #0 only).
- `-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`.
- This is a shorthand for `<eden folder>/sdmc/atmosphere/hbl.nsp`.
- `-setup`: Launch setup applet.
- `--setup`: Launch setup applet.
- `-q/--qlaunch`: Launch QLaunch.
- `-d/--debug`: Enter debug mode, allow gdb stub at port `1234`
- `-c/--config`: Specify alternate configuration file.
- `-f/--fullscreen`: Set fullscreen.
- `-h/--help`: Display help.
- `-g/--game`: 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.
- `-m/--multiplayer`: Specify multiplayer options.
- `-p/--program`: Specify the program arguments to pass (optional).
- `-u/--user`: Select the index of the user to load as.
- `-v/--version`: Display version and quit.
- `-i/--input-profile`: Specifies input profile name to use (for player #0 only).
- `-n/--null-render`: Forces the usage of the "Null" render backend irrespective of settings.
- `-x/--filter`: Sets the debug log filter irrespective of settings.
- `-s/--singlecore`: Forces single-core regardless of settings.
- `-l/--log-file`: The file for storing the room log.
- `-H/--headless`: Force headless mode (no GUI). Currently only used for rooms.
## eden-cli
Room settings:
- `-N/--name`: The name of the room.
- `-D/--description`: The room description.
- `-S/--bind-address`: The bind address for the room.
- `-P/--port`: The port used for the room.
- `-M/--max-members`: The maximum number of players for this room.
- `-W/--password`: The password for the room.
- `-G/--preferred-game`: The preferred game for this room.
- `-I/--preferred-game-id`: The preferred game-id for this room.
- `-U/--username`: The username used for announce.
- `-T/--token`: The token used for announce.
- `-A/--web-api-url`: yuzu Web API url.
- `-B/--ban-list-file`: The file for storing the room ban list.
- `-h/--help`: Display this help and exit.
- `-v/--version`: Output version information and exit.,
- `--debug/-d`: Enter debug mode, allow gdb stub at port `1234`
- `--config/-c`: Specify alternate configuration file.
- `--fullscreen/-f`: Set fullscreen.
- `--help/-h`: Display help.
- `--game/-g`: Specify the game to run.
- `--multiplayer/-m`: Specify multiplayer options.
- `--program/-p`: Specify the program arguments to pass (optional).
- `--user/-u`: Specify the user index.
- `--version/-v`: Display version and quit.
- `--input-profile/-i`: 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.
If the name and description of the room is specified, then it will default to headless mode if not already specified.
Old settings `-hlaunch`, `-qlaunch` and `-setup` are recognized independently. They're kept for backwards compatibility with shortcuts made before the change. Using one of these makes the parser immediately halt and ignore every other option.
+1 -1
View File
@@ -164,7 +164,7 @@ if (NOT ANDROID)
AddJsonPackage(sdl3)
else()
message(STATUS "Using bundled SDL3")
if (PLATFORM_FREEBSD)
if (FREEBSD)
set(BUILD_SHARED_LIBS ON)
endif()
AddJsonPackage(sdl3-ci)
+23 -80
View File
@@ -1,45 +1,46 @@
# SPDX-FileCopyrightText: Copyright 2025 crueter
# SPDX-FileCopyrightText: Copyright 2026 crueter
# SPDX-License-Identifier: LGPL-3.0-or-later
## DetectPlatform ##
# This is a small helper that sets PLATFORM_<platform> variables for various
# This is a small helper that sets platform variables for various
# operating systems and distributions. Note that Apple, Windows, Android, etc.
# are not covered, as CMake already does that for us.
# It also sets CXX_<compiler> for the C++ compiler.
# Furthermore, some platforms have really silly requirements/quirks, so this
# also does a few of those.
# This module contains contributions from the Eden Emulator Project,
# notably from crueter and Lizzie.
if (${CMAKE_SYSTEM_NAME} STREQUAL "SunOS")
set(PLATFORM_SUN ON)
set(SOLARIS ON)
elseif (${CMAKE_SYSTEM_NAME} STREQUAL "OpenOrbis")
set(PLATFORM_PS4 ON)
set(OPENORBIS ON)
elseif (${CMAKE_SYSTEM_NAME} STREQUAL "managarm")
set(PLATFORM_MANAGARM ON)
elseif (${CMAKE_SYSTEM_NAME} STREQUAL "FreeBSD")
set(PLATFORM_FREEBSD ON)
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)
set(MANAGARM ON)
elseif (${CMAKE_SYSTEM_NAME} STREQUAL "Haiku")
set(PLATFORM_HAIKU ON)
elseif (${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
set(PLATFORM_LINUX ON)
set(HAIKUOS ON)
endif()
# BSD
if (DEFINED BSD)
if ("${BSD}" STREQUAL "DragonFlyBSD")
set(DRAGONFLYBSD ON)
elseif ("${BSD}" STREQUAL "FreeBSD")
set(FREEBSD ON)
elseif ("${BSD}" STREQUAL "OpenBSD")
set(OPENBSD ON)
elseif ("${BSD}" STREQUAL "NetBSD")
set(NETBSD ON)
endif()
endif()
# dumb heuristic to detect msys2
if (CMAKE_COMMAND MATCHES "msys64")
set(PLATFORM_MSYS ON)
set(MSYS2 ON)
endif()
# compiler checks
if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
set(CXX_CLANG ON)
if (MSVC)
@@ -47,8 +48,6 @@ if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
endif()
elseif (CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
set(CXX_GCC ON)
elseif (CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")
set(CXX_CL ON)
elseif (CMAKE_CXX_COMPILER_ID STREQUAL "IntelLLVM")
set(CXX_ICC ON)
elseif (CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang")
@@ -61,37 +60,13 @@ if(MINGW AND CXX_CLANG)
set(CMAKE_SYSTEM_VERSION 10.0.0)
endif()
# NB: this does not account for SPARC
if (PLATFORM_SUN)
# Terrific OpenIndiana 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")
# 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")
# For some mighty reason, doing a normal release build sometimes
# may not trigger the proper -O3 switch to materialize
if (CMAKE_BUILD_TYPE MATCHES "Release")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -O3")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O3")
endif()
if (CMAKE_BUILD_TYPE MATCHES "RelWithDebInfo")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -O2")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O2")
endif()
endif()
# MSYS2 utilities
# Sometimes, PkgConfig modules will incorrectly reference / when CMake
# wants you to reference it as C:/msys64/. This function corrects that.
# Example in a Find module:
#[[
if (PLATFORM_MSYS)
if (MSYS2)
FixMsysPath(PkgConfig::OPUS)
endif()
]]
@@ -116,41 +91,9 @@ function(FixMsysPath target)
INTERFACE_INCLUDE_DIRECTORIES ${include_dir})
endfunction()
# MSYSTEM handling + program_path
if (PLATFORM_MSYS)
# really, really dumb heuristic to detect what environment we are in
macro(system var)
if (CMAKE_COMMAND MATCHES ${var})
set(MSYSTEM ${var})
endif()
endmacro()
system(mingw64)
system(clang64)
system(clangarm64)
system(ucrt64)
if (NOT DEFINED MSYSTEM)
set(MSYSTEM msys2)
endif()
# We generally want to prioritize environment-specific binaries if possible
# some, like autoconf, are not present on environments besides msys2 though
set(CMAKE_PROGRAM_PATH C:/msys64/${MSYSTEM}/bin C:/msys64/usr/bin)
set(ENV{PKG_CONFIG_PATH} C:/msys64/${MSYSTEM}/lib/pkgconfig)
endif()
# This saves a truly ridiculous amount of time during linking
# In my tests, without this, Eden takes 2 mins, with this, it takes 3-5 seconds
# or on GitHub Actions, 10 minutes -> 3 seconds
# Saves linking time
if (MINGW)
set(MINGW_FLAGS "-Wl,--strip-all -Wl,--gc-sections")
set(CMAKE_EXE_LINKER_FLAGS_RELEASE
"${CMAKE_EXE_LINKER_FLAGS_RELEASE} ${MINGW_FLAGS}")
endif()
# awesome
if (PLATFORM_FREEBSD OR PLATFORM_DRAGONFLYBSD)
set(CMAKE_EXE_LINKER_FLAGS
"${CMAKE_EXE_LINKER_FLAGS} -L${CMAKE_SYSROOT}/usr/local/lib")
endif()
+5 -5
View File
@@ -56,7 +56,7 @@ if (NOT YUZU_USE_BUNDLED_FFMPEG)
endif()
endif()
if (PLATFORM_PS4 OR PLATFORM_MANAGARM)
if (OPENORBIS OR MANAGARM)
# Doesn't support VA-API, don't go thru the embarrassment of trying to enable it
list(APPEND FFmpeg_HWACCEL_FLAGS --disable-vaapi)
elseif (ANDROID)
@@ -75,7 +75,7 @@ elseif (UNIX AND NOT DEFINED FFmpeg_IS_CROSS_COMPILING AND NOT ANDROID)
if(X11_FOUND)
if (NOT APPLE)
# In Solaris needs explicit linking for ffmpeg which links to /lib/amd64/libX11.so
if(PLATFORM_SUN)
if(SOLARIS)
list(APPEND FFmpeg_HWACCEL_LIBRARIES
X11
"${CMAKE_SYSROOT}/usr/lib/xorg/amd64/libdrm.so")
@@ -158,7 +158,7 @@ elseif (UNIX AND NOT DEFINED FFmpeg_IS_CROSS_COMPILING AND NOT ANDROID)
endif()
endif()
if (PLATFORM_PS4)
if (OPENORBIS)
list(APPEND FFmpeg_CROSS_COMPILE_LIBS
-lkernel
-lSceUserService
@@ -172,7 +172,7 @@ if (PLATFORM_PS4)
--extra-cxxflags=${CMAKE_SYSROOT}/usr/include
--extra-libs="${FFmpeg_CROSS_COMPILE_LIBS}"
)
elseif (PLATFORM_MANAGARM)
elseif (MANAGARM)
# Required for proper stuff
list(APPEND FFmpeg_CROSS_COMPILE_FLAGS
--disable-pthreads
@@ -299,7 +299,7 @@ else()
set(FFmpeg_BUILD_LIBRARIES ${FFmpeg_LIBRARIES})
# BSD make or Solaris make don't support ffmpeg make-j8
if (PLATFORM_LINUX OR ANDROID OR APPLE OR WIN32 OR PLATFORM_FREEBSD)
if (LINUX OR ANDROID OR APPLE OR WIN32 OR FREEBSD)
set(FFmpeg_MAKE_ARGS -j${SYSTEM_THREADS})
else()
set(FFmpeg_MAKE_ARGS "")
+1 -1
View File
@@ -11,7 +11,7 @@ if (NOT libusb_ADDED)
endif()
# TODO: *BSD fails to compile--may need different configs/symbols
if (MINGW OR PLATFORM_LINUX OR APPLE)
if (MINGW OR LINUX OR APPLE)
set(LIBUSB_FOUND ON CACHE BOOL "libusb is present" FORCE)
set(LIBUSB_VERSION "1.0.24" CACHE STRING "libusb version string" FORCE)
+2 -2
View File
@@ -1177,7 +1177,7 @@ static void *stbi__load_main(stbi__context *s, int *x, int *y, int *comp, int re
#endif
#ifndef STBI_NO_TGA
// test tga last because it's a crappy test!
// test tga last because it's a bad test!
if (stbi__tga_test(s))
return stbi__tga_load(s,x,y,comp,req_comp, ri);
#endif
@@ -7662,7 +7662,7 @@ static int stbi__info_main(stbi__context *s, int *x, int *y, int *comp)
if (stbi__hdr_info(s, x, y, comp)) return 1;
#endif
// test tga last because it's a crappy test!
// test tga last because it's a bad test!
#ifndef STBI_NO_TGA
if (stbi__tga_info(s, x, y, comp))
return 1;
+21 -18
View File
@@ -155,12 +155,24 @@ else()
$<$<COMPILE_LANGUAGE:CXX>:-fno-rtti>)
endif()
if (ENABLE_WERROR)
add_compile_options(
$<$<COMPILE_LANGUAGE:C,CXX>:-Werror=all>
$<$<COMPILE_LANGUAGE:C,CXX>:-Werror=extra>
$<$<COMPILE_LANGUAGE:C,CXX>:-Werror=missing-declarations>
$<$<COMPILE_LANGUAGE:C,CXX>:-Werror=shadow>
$<$<COMPILE_LANGUAGE:C,CXX>:-Werror=unused>)
else()
add_compile_options(
$<$<COMPILE_LANGUAGE:C,CXX>:-Wall>
$<$<COMPILE_LANGUAGE:C,CXX>:-Wextra>
$<$<COMPILE_LANGUAGE:C,CXX>:-Wmissing-declarations>
$<$<COMPILE_LANGUAGE:C,CXX>:-Wshadow>
$<$<COMPILE_LANGUAGE:C,CXX>:-Wunused>
# Some compilers could particularly misbehave :)
$<$<COMPILE_LANGUAGE:C,CXX>:-Wno-error-all>)
endif()
add_compile_options(
$<$<COMPILE_LANGUAGE:C,CXX>:-Werror=all>
$<$<COMPILE_LANGUAGE:C,CXX>:-Werror=extra>
$<$<COMPILE_LANGUAGE:C,CXX>:-Werror=missing-declarations>
$<$<COMPILE_LANGUAGE:C,CXX>:-Werror=shadow>
$<$<COMPILE_LANGUAGE:C,CXX>:-Werror=unused>
$<$<COMPILE_LANGUAGE:C,CXX>:-Wno-attributes>
$<$<COMPILE_LANGUAGE:C,CXX>:-Wno-invalid-offsetof>
$<$<COMPILE_LANGUAGE:C,CXX>:-Wno-unused-parameter>
@@ -169,9 +181,9 @@ else()
if (CXX_CLANG OR CXX_ICC OR CXX_APPLE) # Clang, AppleClang, or Intel C++
if (NOT MSVC)
add_compile_options(
$<$<COMPILE_LANGUAGE:C,CXX>:-Werror=shadow-uncaptured-local>
$<$<COMPILE_LANGUAGE:C,CXX>:-Werror=implicit-fallthrough>
$<$<COMPILE_LANGUAGE:C,CXX>:-Werror=type-limits>)
$<$<COMPILE_LANGUAGE:C,CXX>:-Wshadow-uncaptured-local>
$<$<COMPILE_LANGUAGE:C,CXX>:-Wimplicit-fallthrough>
$<$<COMPILE_LANGUAGE:C,CXX>:-Wtype-limits>)
endif()
add_compile_options(
$<$<COMPILE_LANGUAGE:C,CXX>:-Wno-braced-scalar-init>
@@ -181,7 +193,7 @@ else()
if (ARCHITECTURE_x86_64)
add_compile_options($<$<COMPILE_LANGUAGE:C,CXX>:-mcx16>)
if (PLATFORM_LINUX OR PLATFORM_FREEBSD)
if (LINUX OR FREEBSD)
add_compile_options($<$<COMPILE_LANGUAGE:C,CXX>:-mtls-dialect=gnu2>)
endif()
endif()
@@ -239,15 +251,6 @@ endif()
add_subdirectory(common)
add_subdirectory(network)
if (YUZU_ROOM)
add_subdirectory(dedicated_room)
endif()
if (YUZU_ROOM_STANDALONE)
add_subdirectory(yuzu_room_standalone)
set_target_properties(yuzu-room PROPERTIES OUTPUT_NAME "eden-room")
endif()
if (YUZU_STATIC_ROOM)
return()
endif()
+1 -1
View File
@@ -222,7 +222,7 @@ if (MSVC)
)
else()
target_compile_options(audio_core PRIVATE
$<$<COMPILE_LANGUAGE:C,CXX>:-Werror=conversion>
$<$<COMPILE_LANGUAGE:C,CXX>:-Wconversion>
$<$<COMPILE_LANGUAGE:C,CXX>:-Wno-sign-conversion>)
endif()
+6 -2
View File
@@ -133,6 +133,8 @@ add_library(
typed_address.h
uint128.h
unique_function.h
program_args.cpp
program_args.h
random.cpp
random.h
uuid.cpp
@@ -154,7 +156,9 @@ if(WIN32)
windows/timer_resolution.h)
target_link_libraries(common PRIVATE ntdll)
endif()
if (MSVC)
target_link_libraries(common PRIVATE getopt)
endif()
if(NOT WIN32)
target_sources(common PRIVATE signal_chain.cpp signal_chain.h)
endif()
@@ -220,7 +224,7 @@ endif()
if(CXX_CLANG)
target_compile_options(common PRIVATE
$<$<COMPILE_LANGUAGE:C,CXX>:-fsized-deallocation>
$<$<COMPILE_LANGUAGE:C,CXX>:-Werror=unreachable-code-aggressive>)
$<$<COMPILE_LANGUAGE:C,CXX>:-Wunreachable-code-aggressive>)
target_compile_definitions(
common
PRIVATE
+280
View File
@@ -0,0 +1,280 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#include <regex>
#include "common/assert.h"
#include "common/program_args.h"
#include "common/logging.h"
#include "common/scm_rev.h"
#include "common/string_util.h"
#include "network/room.h"
#ifdef _WIN32
// windows.h needs to be included before shellapi.h
#include <windows.h>
#include <shellapi.h>
#include "common/windows/timer_resolution.h"
#endif
#undef _UNICODE
#include <getopt.h>
#ifndef _MSC_VER
#include <unistd.h>
#endif
namespace Common {
static void PrintHelp(const char* argv0) {
LOG_INFO(Frontend, "Usage: {} [options] <filename>\n"
"Core options:\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"
"-i, --input-profile Specifies input profile name to use (for player #0 only)\n"
"-n, --null-render Forces the usage of the \"Null\" render backend irrespective of settings\n"
"-x, --filter Sets the debug log filter irrespective of settings\n"
"-s, --singlecore Forces single-core regardless of settings\n"
"Shared options:\n"
"-l, --log-file The file for storing the room log\n"
"-H, --headless Force headless mode (no GUI). Currently only used for rooms\n"
"Room options:\n"
"-N, --name The name of the room\n"
"-D, --description The room description\n"
"-S, --bind-address The bind address for the room\n"
"-P, --port The port used for the room\n"
"-M, --max-members The maximum number of players for this room\n"
"-W, --password The password for the room\n"
"-G, --preferred-game The preferred game for this room\n"
"-I, --preferred-game-id The preferred game-id for this room\n"
"-U, --username The username used for announce\n"
"-T, --token The token used for announce\n"
"-A, --web-api-url yuzu Web API url\n"
"-B, --ban-list-file The file for storing the room ban list\n"
"Misc. options:\n"
"-h, --help Display this help and exit\n"
"-v, --version Output version information and exit\n",
argv0);
}
static void PrintVersion() {
LOG_INFO(Frontend, "Eden {} {}, Libnetwork: {}", Common::g_scm_branch, Common::g_scm_desc, Network::network_version);
}
int ParseArguments(ProgramArguments& args, int argc, char *argv[]) {
int option_index = 0;
#ifdef _WIN32
int argc_w;
auto argv_w = CommandLineToArgvW(GetCommandLineW(), &argc_w);
if (argv_w == nullptr) {
LOG_CRITICAL(Frontend, "Failed to get command line arguments");
return -1;
}
#endif
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'},
{"log-file", required_argument, 0, 'l'},
{"headless", required_argument, 0, 'H'},
{"room-name", required_argument, 0, 'N'},
{"room-description", required_argument, 0, 'D'},
{"bind-address", required_argument, 0, 'S'},
{"port", required_argument, 0, 'P'},
{"max-members", required_argument, 0, 'M'},
{"password", required_argument, 0, 'W'},
{"preferred-game", required_argument, 0, 'G'},
{"preferred-game-id", required_argument, 0, 'I'},
{"username", optional_argument, 0, 'U'},
{"token", required_argument, 0, 'T'},
{"web-api-url", required_argument, 0, 'A'},
{"ban-list-file", required_argument, 0, 'B'},
// Entry option
{"room", no_argument, 0, 0},
{"hlaunch", no_argument, 0, 500},
{"qlaunch", no_argument, 0, 'q'},
{"setup", no_argument, 0, 502},
{0, 0, 0, 0},
// clang-format on
};
// Kept for compatibility!
for (int i = 1; i < argc; i++) {
if (strcmp(argv[i], "-hlaunch") == 0) {
args.should_launch_hlaunch = true;
return 0;
} else if (strcmp(argv[i], "-qlaunch") == 0) {
args.should_launch_qlaunch = true;
return 0;
} else if (strcmp(argv[i], "-setup") == 0) {
args.should_launch_setup = true;
return 0;
}
}
// Preserves drag and drop functionality (i.e ./eden <game path>)
[[maybe_unused]] char *endarg = nullptr;
while (optind < argc) {
int arg = getopt_long(argc, argv, "g:fhvcip::c:u:d:", long_options, &option_index);
if (arg != -1) {
switch (arg) {
case 'd':
args.override_gdb_port = uint16_t(atoi(optarg));
break;
case 'c':
args.config_path = optarg;
break;
case 'f':
args.fullscreen = true;
LOG_INFO(Frontend, "Starting in fullscreen mode...");
break;
case 'h':
PrintHelp(argv[0]);
return 0;
case 'g':
args.filepath = std::string(optarg);
break;
case 'i': {
args.input_profile = std::string(optarg);
break;
}
case 'm': {
args.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)) {
LOG_ERROR(Frontend, "Wrong format for option --multiplayer");
return -1;
} else {
std::smatch match;
std::regex_search(str_arg, match, re);
ASSERT(match.size() == 5);
args.nickname = match[1];
args.password = match[2];
args.address = match[3];
if (!match[4].str().empty()) {
args.port = u16(std::strtoul(match[4].str().c_str(), nullptr, 0));
}
std::regex nickname_re("^[a-zA-Z0-9._\\- ]+$");
if (!std::regex_match(args.nickname, nickname_re)) {
LOG_ERROR(Frontend, "Nickname is not valid. Must be 4 to 20 alphanumeric characters");
return -1;
} else {
if (args.address.empty()) {
LOG_ERROR(Frontend, "Address to room must not be empty");
return -1;
}
}
}
break;
}
case 'p':
args.program_args.assign(optarg);
break;
case 'u':
args.selected_user = atoi(optarg);
break;
case 'v':
PrintVersion();
break;
case 'n':
args.force_null_render = true;
break;
case 's':
args.force_single_core = true;
break;
case 'x':
args.log_filter.emplace(optarg);
break;
// shared
case 'l':
args.log_file.assign(optarg);
break;
case 'H':
args.headless.emplace(true);
break;
// room
case 'N':
args.room_name.assign(optarg);
break;
case 'D':
args.room_description.assign(optarg);
break;
case 'S':
args.bind_address.assign(optarg);
break;
case 'P': {
auto const value = strtoul(optarg, &endarg, 0);
if (value <= USHRT_MAX) {
args.port = value;
} else {
LOG_ERROR(Frontend, "port must be between 0-{}", USHRT_MAX);
}
break;
}
case 'M': {
auto const value = strtoul(optarg, &endarg, 0);
if (value >= 2 && value <= Network::MaxConcurrentConnections) {
args.max_members = value;
} else {
LOG_ERROR(Frontend, "max members must be between 2-{}", value, Network::MaxConcurrentConnections);
}
break;
}
case 'W':
args.password.assign(optarg);
break;
case 'G':
args.preferred_game.assign(optarg);
break;
case 'I':
args.preferred_game_id = strtoull(optarg, &endarg, 16);
break;
case 'U':
args.nickname.assign(optarg);
break;
case 'T':
args.token.assign(optarg);
break;
case 'A':
args.web_api_url.assign(optarg);
break;
case 'B':
args.ban_list_file.assign(optarg);
break;
}
} else {
#ifdef _WIN32
args.filepath = Common::UTF16ToUTF8(argv_w[optind]);
#else
args.filepath = argv[optind];
#endif
optind++;
}
}
#ifdef _WIN32
LocalFree(argv_w);
#endif
return 0;
}
}
+49
View File
@@ -0,0 +1,49 @@
// 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"
namespace Common {
inline constexpr u16 DEFAULT_ROOM_PORT = 24872;
struct ProgramArguments {
std::optional<std::string> config_path{};
std::optional<std::string> log_filter{};
std::string nickname{};
std::string password{};
std::string address{};
std::string input_profile{};
std::string filepath{};
std::string program_args{};
std::string room_name{};
std::string room_description{};
std::string preferred_game{};
std::string username{};
std::string token{};
std::string web_api_url{};
std::string ban_list_file{};
std::string log_file = "eden-room.log";
std::string bind_address{};
std::optional<int> selected_user{};
std::optional<u16> override_gdb_port{};
std::optional<bool> headless{};
u64 preferred_game_id = 0;
u32 max_members = 16;
u16 port = DEFAULT_ROOM_PORT;
bool use_multiplayer = false;
bool fullscreen = false;
bool force_null_render = false;
bool force_single_core = false;
bool should_launch_qlaunch = false;
bool should_launch_hlaunch = false;
bool should_launch_setup = false;
};
int ParseArguments(ProgramArguments& args, int argc, char *argv[]);
}
+9 -1
View File
@@ -702,7 +702,15 @@ struct Values {
// Controls
InputSetting<std::array<PlayerInput, 10>> players;
Setting<bool> disable_wgi_xinput{
linkage, false, "disable_wgi_xinput", Category::Controls, Specialization::Default,
// Only read/write disable_wgi_xinput on Windows platforms
#ifdef _WIN32
true
#else
false
#endif
};
Setting<bool> enable_raw_input{
linkage, false, "enable_raw_input", Category::Controls, Specialization::Default,
// Only read/write enable_raw_input on Windows platforms
+2 -2
View File
@@ -1173,7 +1173,7 @@ add_library(core STATIC
if (ENABLE_WIFI_SCAN)
target_sources(core PRIVATE internal_network/wifi_scanner.cpp)
if (PLATFORM_LINUX)
if (LINUX)
target_link_libraries(core PRIVATE iw)
endif()
else()
@@ -1199,7 +1199,7 @@ if (MSVC)
)
else()
target_compile_options(core PRIVATE
$<$<COMPILE_LANGUAGE:C,CXX>:-Werror=conversion>
$<$<COMPILE_LANGUAGE:C,CXX>:-Wconversion>
$<$<COMPILE_LANGUAGE:C,CXX>:-Wno-sign-conversion>
$<$<COMPILE_LANGUAGE:C,CXX>:-Wno-cast-function-type>
$<$<CXX_COMPILER_ID:Clang>:-fsized-deallocation>)
@@ -28,7 +28,7 @@ Result CloseHandle(Core::System& system, Handle handle) {
/// Clears the signaled state of an event or process.
Result ResetSignal(Core::System& system, Handle handle) {
LOG_DEBUG(Kernel_SVC, "called handle {:#08x}", handle);
LOG_TRACE(Kernel_SVC, "called handle {:#08x}", handle);
// Get the current handle table.
const auto& handle_table = GetCurrentProcess(system.Kernel()).GetHandleTable();
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator
@@ -84,7 +84,7 @@ private:
RestrictionSettings restriction_settings{};
std::array<char, 8> pin_code{};
Capability capability{};
// TODO: this is RAW as fuck
// TODO: this is raw
PlayTimerSettings raw_play_timer_settings{};
KernelHelpers::ServiceContext service_context;
@@ -1020,7 +1020,7 @@ Result ISystemSettingsServer::GetBatteryLot(Out<BatteryLot> out_battery_lot) {
c.lot_number[1] = 'H';
c.lot_number[2] = 'A';
c.lot_number[3] = 'C';
// TODO: I have no fucking idea what the letters mean
// TODO: what do the letters mean?
c.lot_number[4] = 'H';
c.lot_number[5] = 'Z';
c.lot_number[6] = 'Z';
+2 -1
View File
@@ -531,7 +531,8 @@ int TranslateTypeToNative(Type type) {
NETWORK_PROTOCOL_TRANSLATE_ELEM(MPLS) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(PFSYNC)
#elif defined(__linux__)
// Other platforms get fucked
// Other platforms may not support some niche protocols.
// This is usually not an issue
#define NETWORK_PROTOCOL_TRANSLATE_LIST \
NETWORK_PROTOCOL_TRANSLATE_ELEM(IP) \
/*NETWORK_PROTOCOL_TRANSLATE_ELEM(HOPOPTS)*/ \
-27
View File
@@ -1,27 +0,0 @@
# SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
# SPDX-License-Identifier: GPL-3.0-or-later
# SPDX-FileCopyrightText: 2017 Citra Emulator Project
# SPDX-License-Identifier: GPL-2.0-or-later
add_library(yuzu-room STATIC EXCLUDE_FROM_ALL
yuzu_room.cpp
yuzu_room.h
yuzu_room.rc)
target_link_libraries(yuzu-room PRIVATE common network)
if (ENABLE_WEB_SERVICE)
target_compile_definitions(yuzu-room PRIVATE ENABLE_WEB_SERVICE)
target_link_libraries(yuzu-room PRIVATE web_service)
endif()
target_link_libraries(yuzu-room PRIVATE
OpenSSL::SSL
OpenSSL::Crypto)
if (MSVC)
target_link_libraries(yuzu-room PRIVATE getopt)
endif()
target_link_libraries(yuzu-room PRIVATE ${PLATFORM_LIBRARIES} Threads::Threads)
create_target_directory_groups(yuzu-room)
-394
View File
@@ -1,394 +0,0 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2017 Citra Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
// SPDX-FileCopyrightText: Copyright yuzu/Citra Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#include <chrono>
#include <fstream>
#include <iostream>
#include <memory>
#include <regex>
#include <string>
#include <thread>
#ifdef _WIN32
// windows.h needs to be included before shellapi.h
#include <windows.h>
#include <shellapi.h>
#endif
#include <openssl/evp.h>
#include "common/common_types.h"
#include "common/fs/file.h"
#include "common/fs/fs.h"
#include "common/fs/path_util.h"
#include "common/logging.h"
#include "common/scm_rev.h"
#include "common/settings.h"
#include "common/string_util.h"
#include "core/core.h"
#include "network/announce_multiplayer_session.h"
#include "network/network.h"
#include "network/room.h"
#include "network/verify_user.h"
#ifdef ENABLE_WEB_SERVICE
#include "web_service/verify_user_jwt.h"
#endif
#undef _UNICODE
#include <getopt.h>
#ifndef _MSC_VER
#include <unistd.h>
#endif
#include "yuzu_room.h"
static void PrintHelp(const char* argv0) {
LOG_INFO(Network,
"Usage: {}"
" [options] <filename>\n"
"-n, --room-name The name of the room\n"
"-d, --room-description The room description\n"
"-s, --bind-address The bind address for the room\n"
"-p, --port The port used for the room\n"
"-m, --max-members The maximum number of players for this room\n"
"-w, --password The password for the room\n"
"-g, --preferred-game The preferred game for this room\n"
"-i, --preferred-game-id The preferred game-id for this room\n"
"-u, --username The username used for announce\n"
"-t, --token The token used for announce\n"
"-a, --web-api-url yuzu Web API url\n"
"-b, --ban-list-file The file for storing the room ban list\n"
"-l, --log-file The file for storing the room log\n"
"-h, --help Display this help and exit\n"
"-v, --version Output version information and exit\n",
argv0);
}
static void PrintVersion() {
LOG_INFO(Network, "Eden dedicated room {} {} Libnetwork: {}", Common::g_scm_branch,
Common::g_scm_desc, Network::network_version);
}
/// The magic text at the beginning of a yuzu-room ban list file.
static constexpr char BanListMagic[] = "YuzuRoom-BanList-1";
static constexpr char token_delimiter{':'};
static void PadToken(std::string& token) {
std::array<unsigned char, 512> output{};
std::array<unsigned char, 2048> roundtrip{};
for (size_t i = 0; i < 3; i++) {
EVP_DecodeBlock(output.data(), reinterpret_cast<const unsigned char*>(token.c_str()), token.size());
EVP_EncodeBlock(output.data(), roundtrip.data(), roundtrip.size());
if (memcmp(roundtrip.data(), token.data(), token.size()) == 0) {
break;
}
token.push_back('=');
}
}
static std::string UsernameFromDisplayToken(const std::string& display_token) {
std::size_t outlen = 4 * ((display_token.length() + 2) / 3);
std::array<unsigned char, 512> output{};
EVP_DecodeBlock(output.data(), reinterpret_cast<const unsigned char*>(display_token.c_str()), display_token.length());
std::string decoded_display_token(reinterpret_cast<char*>(&output), outlen);
return decoded_display_token.substr(0, decoded_display_token.find(token_delimiter));
}
static std::string TokenFromDisplayToken(const std::string& display_token) {
std::size_t outlen = 4 * ((display_token.length() + 2) / 3);
std::array<unsigned char, 512> output{};
EVP_DecodeBlock(output.data(), reinterpret_cast<const unsigned char*>(display_token.c_str()), display_token.length());
std::string decoded_display_token(reinterpret_cast<char*>(&output), outlen);
return decoded_display_token.substr(decoded_display_token.find(token_delimiter) + 1);
}
static Network::Room::BanList LoadBanList(const std::string& path) {
std::ifstream file;
Common::FS::OpenFileStream(file, path, std::ios_base::in);
if (!file || file.eof()) {
LOG_ERROR(Network, "Could not open ban list!");
return {};
}
std::string magic;
std::getline(file, magic);
if (magic != BanListMagic) {
LOG_ERROR(Network, "Ban list is not valid!");
return {};
}
// false = username ban list, true = ip ban list
bool ban_list_type = false;
Network::Room::UsernameBanList username_ban_list;
Network::Room::IPBanList ip_ban_list;
while (!file.eof()) {
std::string line;
std::getline(file, line);
line.erase(std::remove(line.begin(), line.end(), '\0'), line.end());
line = Common::StripSpaces(line);
if (line.empty()) {
// An empty line marks start of the IP ban list
ban_list_type = true;
continue;
}
if (ban_list_type) {
ip_ban_list.emplace_back(line);
} else {
username_ban_list.emplace_back(line);
}
}
return {username_ban_list, ip_ban_list};
}
static void SaveBanList(const Network::Room::BanList& ban_list, const std::string& path) {
std::ofstream file;
Common::FS::OpenFileStream(file, path, std::ios_base::out);
if (!file) {
LOG_ERROR(Network, "Could not save ban list!");
return;
}
file << BanListMagic << "\n";
// Username ban list
for (const auto& username : ban_list.first) {
file << username << "\n";
}
file << "\n";
// IP ban list
for (const auto& ip : ban_list.second) {
file << ip << "\n";
}
}
/// Application entry point
void LaunchRoom(int argc, char** argv, bool called_by_option) {
int option_index = 0;
char* endarg;
char* new_argv0 = argv[0];
if (called_by_option) {
strncat(new_argv0, " --room", 8);
}
std::string room_name;
std::string room_description;
std::string password;
std::string preferred_game;
std::string username;
std::string token;
std::string web_api_url;
std::string ban_list_file;
std::string log_file = "eden-room.log";
std::string bind_address;
u64 preferred_game_id = 0;
u32 port = Network::DefaultRoomPort;
u32 max_members = 16;
static struct option long_options[] = {
{"room-name", required_argument, 0, 'n'},
{"room-description", required_argument, 0, 'd'},
{"bind-address", required_argument, 0, 's'},
{"port", required_argument, 0, 'p'},
{"max-members", required_argument, 0, 'm'},
{"password", required_argument, 0, 'w'},
{"preferred-game", required_argument, 0, 'g'},
{"preferred-game-id", required_argument, 0, 'i'},
{"username", optional_argument, 0, 'u'},
{"token", required_argument, 0, 't'},
{"web-api-url", required_argument, 0, 'a'},
{"ban-list-file", required_argument, 0, 'b'},
{"log-file", required_argument, 0, 'l'},
{"help", no_argument, 0, 'h'},
{"version", no_argument, 0, 'v'},
// Entry option
{"room", 0, 0, 0},
{0, 0, 0, 0},
};
Common::Log::Initialize();
Common::Log::SetColorConsoleBackendEnabled(true);
Common::Log::Start();
while (optind < argc) {
int arg = getopt_long(argc, argv, "n:d:s:p:m:w:g:u:t:a:i:l:hv", long_options, &option_index);
if (arg != -1) {
char carg = static_cast<char>(arg);
switch (carg) {
case 'n':
room_name.assign(optarg);
break;
case 'd':
room_description.assign(optarg);
break;
case 's':
bind_address.assign(optarg);
break;
case 'p':
port = strtoul(optarg, &endarg, 0);
break;
case 'm':
max_members = strtoul(optarg, &endarg, 0);
break;
case 'w':
password.assign(optarg);
break;
case 'g':
preferred_game.assign(optarg);
break;
case 'i':
preferred_game_id = strtoull(optarg, &endarg, 16);
break;
case 'u':
username.assign(optarg);
break;
case 't':
token.assign(optarg);
break;
case 'a':
web_api_url.assign(optarg);
break;
case 'b':
ban_list_file.assign(optarg);
break;
case 'l':
log_file.assign(optarg);
break;
case 'h':
PrintHelp(argv[0]);
std::exit(0);
case 'v':
PrintVersion();
std::exit(0);
default:
break;
}
}
}
if (room_name.empty()) {
LOG_ERROR(Network, "Room name is empty!");
PrintHelp(argv[0]);
std::exit(-1);
}
if (preferred_game.empty()) {
LOG_ERROR(Network, "Preferred game is empty!");
PrintHelp(argv[0]);
std::exit(-1);
}
if (preferred_game_id == 0) {
LOG_ERROR(Network,
"preferred-game-id not set!\nThis should get set to allow users to find your "
"room.\nSet with --preferred-game-id id");
}
if (max_members > Network::MaxConcurrentConnections || max_members < 2) {
LOG_ERROR(Network,
"max_members needs to be in the range 2 - {}!",
Network::MaxConcurrentConnections);
PrintHelp(argv[0]);
std::exit(-1);
}
if (bind_address.empty()) {
LOG_INFO(Network, "Bind address is empty: defaulting to 0.0.0.0");
}
if (port > UINT16_MAX) {
LOG_ERROR(Network, "Port needs to be in the range 0 - 65535!");
PrintHelp(argv[0]);
std::exit(-1);
}
if (ban_list_file.empty()) {
LOG_ERROR(Network,
"Ban list file not set!\nThis should get set to load and save room ban "
"list.\nSet with --ban-list-file <file>");
}
bool announce = true;
if (token.empty() && announce) {
announce = false;
LOG_INFO(Network, "Token is empty: Hosting a private room");
}
if (web_api_url.empty() && announce) {
announce = false;
LOG_INFO(Network, "Endpoint url is empty: Hosting a private room");
}
if (announce) {
if (username.empty()) {
LOG_INFO(Network, "Hosting a public room");
Settings::values.web_api_url = web_api_url;
PadToken(token);
Settings::values.eden_username = UsernameFromDisplayToken(token);
username = Settings::values.eden_username.GetValue();
Settings::values.eden_token = TokenFromDisplayToken(token);
} else {
LOG_INFO(Network, "Hosting a public room");
Settings::values.web_api_url = web_api_url;
Settings::values.eden_username = username;
Settings::values.eden_token = token;
}
}
// Load the ban list
Network::Room::BanList ban_list;
if (!ban_list_file.empty()) {
ban_list = LoadBanList(ban_list_file);
}
std::unique_ptr<Network::VerifyUser::Backend> verify_backend;
if (announce) {
#ifdef ENABLE_WEB_SERVICE
verify_backend =
std::make_unique<WebService::VerifyUserJWT>(Settings::values.web_api_url.GetValue());
#else
LOG_INFO(Network,
"Eden Web Services is not available with this build: validation is disabled.");
verify_backend = std::make_unique<Network::VerifyUser::NullBackend>();
#endif
} else {
verify_backend = std::make_unique<Network::VerifyUser::NullBackend>();
}
Network::Init();
if (auto room = Network::GetRoom().lock()) {
AnnounceMultiplayerRoom::GameInfo preferred_game_info{.name = preferred_game,
.id = preferred_game_id};
if (!room->Create(room_name, room_description, bind_address, static_cast<u16>(port),
password, max_members, username, preferred_game_info,
std::move(verify_backend), ban_list)) {
LOG_INFO(Network, "Failed to create room: ");
std::exit(-1);
}
LOG_INFO(Network, "Room is open. Close with Q+Enter...");
auto announce_session = std::make_unique<Core::AnnounceMultiplayerSession>();
if (announce) {
announce_session->Start();
}
while (room->GetState() == Network::Room::State::Open) {
std::string in;
std::cin >> in;
if (in.size() > 0) {
break;
}
std::this_thread::sleep_for(std::chrono::milliseconds(100));
}
if (announce) {
announce_session->Stop();
}
announce_session.reset();
// Save the ban list
if (!ban_list_file.empty()) {
SaveBanList(room->GetBanList(), ban_list_file);
}
room->Destroy();
}
Network::Shutdown();
std::exit(0);
}
-7
View File
@@ -1,7 +0,0 @@
// Copyright eden Emulator Project
// Licensed under GPLv3 or any later version
// Refer to the license.txt file included.
#pragma once
void LaunchRoom(int argc, char** argv, bool called_by_option);
-20
View File
@@ -1,20 +0,0 @@
// SPDX-FileCopyrightText: 2017 Citra Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "winresrc.h"
/////////////////////////////////////////////////////////////////////////////
//
// Icon
//
// Icon with lowest ID value placed first to ensure application icon
// remains consistent on all systems.
YUZU_ICON ICON "../../dist/eden.ico"
/////////////////////////////////////////////////////////////////////////////
//
// RT_MANIFEST
//
0 RT_MANIFEST "../../dist/yuzu.manifest"
+1 -1
View File
@@ -25,7 +25,7 @@ endif()
# Dynarmic project options
option(DYNARMIC_ENABLE_CPU_FEATURE_DETECTION "Turning this off causes dynarmic to assume the host CPU doesn't support anything later than SSE3" ON)
if (PLATFORM_OPENBSD OR PLATFORM_DRAGONFLY OR PLATFORM_NETBSD)
if (OPENBSD OR DRAGONFLY OR NETBSD)
set(REQUIRE_WX ON)
else()
set(REQUIRE_WX OFF)
+1 -1
View File
@@ -363,7 +363,7 @@ elseif (APPLE)
backend/exception_handler_macos_mig.c
)
endif()
elseif (UNIX AND NOT PLATFORM_HAIKU)
elseif (UNIX AND NOT HAIKUOS)
# Haiku lacks <ucontext.h>
if (CMAKE_SYSTEM_NAME STREQUAL "Linux")
target_link_libraries(dynarmic PRIVATE rt)
@@ -234,7 +234,7 @@ private:
BlockOfCode block_of_code;
A32EmitX64 emitter;
Optimization::PolyfillOptions polyfill_options;
// Keep it here, you don't wanna mess with the fuckery that's initializer lists
// Keep it here in order to not mess with initializer lists
const A32::UserConfig conf;
Jit* jit_interface;
@@ -2160,8 +2160,6 @@ void EmitFPVectorToFixed(BlockOfCode& code, EmitContext& ctx, IR::Inst* inst) {
ROUNDING_MODE_CASE(CASE, 0x3e) \
ROUNDING_MODE_CASE(CASE, 0x3f)
// FUCK YOU MSVC, FUCKING DEPTH CANT EVEN HANDLE 8+16+32+64 DEPTH OF A ELSE STATMENT YOU FUCKING STUPID
// BURN MSVC BURN IT STUPID COMPILER CAN'T EVEN COMPILE THE MOST BASIC C++
ROUNDING_MODE_SWITCH(ToNearest_TieEven)
ROUNDING_MODE_SWITCH(TowardsPlusInfinity)
ROUNDING_MODE_SWITCH(TowardsMinusInfinity)
+1 -1
View File
@@ -787,7 +787,7 @@ static void FoldCountLeadingZeros(IR::Inst& inst, bool is_32_bit) {
/// Folds division operations based on the following:
///
/// 1. x / 0 -> 0 (NOTE: This is an ARM-specific behavior defined in the architecture reference manual)
/// 2a. 0x8000_0000 / 0xFFFF_FFFF -> 0x8000_0000 (NOTE: More ARM bullshit)
/// 2a. 0x8000_0000 / 0xFFFF_FFFF -> 0x8000_0000 (NOTE: More ARM errata)
/// 2b. 0x8000_0000_0000_0000 / 0xFFFF_FFFF_FFFF_FFFF -> 0x8000_0000_0000_0000
/// 3. imm_x / imm_y -> result
/// 4. x / 1 -> x
+1 -1
View File
@@ -151,7 +151,7 @@ if (MSVC)
)
else()
target_compile_options(hid_core PRIVATE
$<$<COMPILE_LANGUAGE:C,CXX>:-Werror=conversion>
$<$<COMPILE_LANGUAGE:C,CXX>:-Wconversion>
$<$<COMPILE_LANGUAGE:C,CXX>:-Wno-sign-conversion>
$<$<COMPILE_LANGUAGE:C,CXX>:-Wno-cast-function-type>
$<$<CXX_COMPILER_ID:Clang>:-fsized-deallocation>)
+1 -1
View File
@@ -44,7 +44,7 @@ if (MSVC)
/we4800 # Implicit conversion from 'type' to bool. Possible information loss
)
else()
target_compile_options(input_common PRIVATE $<$<COMPILE_LANGUAGE:C,CXX>:-Werror=conversion>)
target_compile_options(input_common PRIVATE $<$<COMPILE_LANGUAGE:C,CXX>:-Wconversion>)
endif()
if (ANDROID)
+5 -2
View File
@@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
@@ -157,8 +160,8 @@ Common::ParamPackage Android::BuildButtonParamPackageForButton(PadIdentifier ide
bool Android::MatchVID(Common::UUID device, const std::vector<std::string>& vids) const {
for (size_t i = 0; i < vids.size(); ++i) {
auto fucker = device.RawString();
if (fucker.find(vids[i]) != std::string::npos) {
auto dev_str = device.RawString();
if (dev_str.find(vids[i]) != std::string::npos) {
return true;
}
}
+7
View File
@@ -648,6 +648,13 @@ SDLDriver::SDLDriver(std::string input_engine_) : InputEngine(std::move(input_en
// Disable raw input. When enabled this setting causes SDL to die when a web applet opens
SDL_SetHint(SDL_HINT_JOYSTICK_RAWINPUT, Settings::values.enable_raw_input ? "1" : "0");
#ifdef _WIN32
if (Settings::values.disable_wgi_xinput) {
SDL_SetHintWithPriority(SDL_HINT_JOYSTICK_RAWINPUT_CORRELATE_XINPUT, "0", SDL_HINT_OVERRIDE);
SDL_SetHintWithPriority(SDL_HINT_JOYSTICK_WGI, "0", SDL_HINT_OVERRIDE);
}
#endif
// SDL3 defaults Steam Controller Bluetooth HIDAPI support to off, which can disable gyro.
SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_STEAM, "1");
SDL_SetHint(SDL_HINT_GAMECONTROLLER_SENSOR_FUSION, "1");
+2 -2
View File
@@ -1,4 +1,4 @@
# SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
# SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
# SPDX-License-Identifier: GPL-3.0-or-later
# SPDX-FileCopyrightText: 2022 yuzu Emulator Project
@@ -28,6 +28,6 @@ if (ENABLE_WEB_SERVICE)
endif()
# Solaris uses /lib/amd64/libsocket.so and /lib/amd64/libnsl.so
if (PLATFORM_SUN)
if (SOLARIS)
target_link_libraries(network PRIVATE socket nsl)
endif()
+208
View File
@@ -6,18 +6,31 @@
#include <algorithm>
#include <atomic>
#include <iomanip>
#include <iostream>
#include <mutex>
#include <random>
#include <regex>
#include <shared_mutex>
#include <sstream>
#include <thread>
#include <fstream>
#include <openssl/evp.h>
#include "common/fs/file.h"
#include "common/polyfill_thread.h"
#include "common/logging.h"
#include "common/settings.h"
#include "common/string_util.h"
#include "enet/enet.h"
#include "network/announce_multiplayer_session.h"
#include "network/packet.h"
#include "network/room.h"
#include "network/network.h"
#include "network/verify_user.h"
#ifdef ENABLE_WEB_SERVICE
#include "web_service/verify_user_jwt.h"
#endif
namespace Network {
@@ -1142,4 +1155,199 @@ void Room::Destroy() {
room_impl->room_information.name.clear();
}
/// The magic text at the beginning of a yuzu-room ban list file.
static constexpr char BAN_LIST_MAGIC[] = "YuzuRoom-BanList-1";
static constexpr char TOKEN_DELIMITER{':'};
static void PadToken(std::string& token) {
std::array<unsigned char, 512> output{};
std::array<unsigned char, 2048> roundtrip{};
for (size_t i = 0; i < 3; i++) {
EVP_DecodeBlock(output.data(), reinterpret_cast<const unsigned char*>(token.c_str()), token.size());
EVP_EncodeBlock(output.data(), roundtrip.data(), roundtrip.size());
if (memcmp(roundtrip.data(), token.data(), token.size()) == 0) {
break;
}
token.push_back('=');
}
}
static std::string UsernameFromDisplayToken(const std::string& display_token) {
std::size_t outlen = 4 * ((display_token.length() + 2) / 3);
std::array<unsigned char, 512> output{};
EVP_DecodeBlock(output.data(), reinterpret_cast<const unsigned char*>(display_token.c_str()), display_token.length());
std::string decoded_display_token(reinterpret_cast<char*>(&output), outlen);
return decoded_display_token.substr(0, decoded_display_token.find(TOKEN_DELIMITER));
}
static std::string TokenFromDisplayToken(const std::string& display_token) {
std::size_t outlen = 4 * ((display_token.length() + 2) / 3);
std::array<unsigned char, 512> output{};
EVP_DecodeBlock(output.data(), reinterpret_cast<const unsigned char*>(display_token.c_str()), display_token.length());
std::string decoded_display_token(reinterpret_cast<char*>(&output), outlen);
return decoded_display_token.substr(decoded_display_token.find(TOKEN_DELIMITER) + 1);
}
static Network::Room::BanList LoadBanList(const std::string& path) {
std::ifstream file;
Common::FS::OpenFileStream(file, path, std::ios_base::in);
if (!file || file.eof()) {
LOG_ERROR(Network, "Could not open ban list!");
return {};
}
std::string magic;
std::getline(file, magic);
if (magic != BAN_LIST_MAGIC) {
LOG_ERROR(Network, "Ban list is not valid!");
return {};
}
// false = username ban list, true = ip ban list
bool ban_list_type = false;
Network::Room::UsernameBanList username_ban_list;
Network::Room::IPBanList ip_ban_list;
while (!file.eof()) {
std::string line;
std::getline(file, line);
line.erase(std::remove(line.begin(), line.end(), '\0'), line.end());
line = Common::StripSpaces(line);
if (line.empty()) {
// An empty line marks start of the IP ban list
ban_list_type = true;
continue;
}
if (ban_list_type) {
ip_ban_list.emplace_back(line);
} else {
username_ban_list.emplace_back(line);
}
}
return {username_ban_list, ip_ban_list};
}
static void SaveBanList(const Network::Room::BanList& ban_list, const std::string& path) {
std::ofstream file;
Common::FS::OpenFileStream(file, path, std::ios_base::out);
if (!file) {
LOG_ERROR(Network, "Could not save ban list!");
return;
}
file << BAN_LIST_MAGIC << "\n";
// Username ban list
for (const auto& username : ban_list.first)
file << username << "\n";
file << "\n";
// IP ban list
for (const auto& ip : ban_list.second)
file << ip << "\n";
}
int LaunchRoomLoopWithArguments(Common::ProgramArguments& args) {
if (args.room_name.empty()) {
LOG_ERROR(Network, "Room name is empty!");
return -1;
}
if (args.preferred_game.empty()) {
LOG_ERROR(Network, "Preferred game is empty!");
return -1;
}
if (args.preferred_game_id == 0) {
LOG_WARNING(Network,
"preferred-game-id not set!\n"
"This should get set to allow users to find your room.\n"
"Set with --preferred-game-id id");
}
if (args.bind_address.empty()) {
LOG_INFO(Network, "Bind address is empty: defaulting to 0.0.0.0");
}
if (args.ban_list_file.empty()) {
LOG_WARNING(Network,
"Ban list file not set!\n"
"This should get set to load and save room ban list.\n"
"Set with --ban-list-file <file>");
}
bool announce = true;
if (args.token.empty() && announce) {
announce = false;
LOG_INFO(Network, "Token is empty: Hosting a private room");
}
if (args.web_api_url.empty() && announce) {
announce = false;
LOG_INFO(Network, "Endpoint url is empty: Hosting a private room");
}
if (announce) {
if (args.username.empty()) {
LOG_INFO(Network, "Hosting a public room");
Settings::values.web_api_url = args.web_api_url;
PadToken(args.token);
Settings::values.eden_username = UsernameFromDisplayToken(args.token);
args.username = Settings::values.eden_username.GetValue();
Settings::values.eden_token = TokenFromDisplayToken(args.token);
} else {
LOG_INFO(Network, "Hosting a public room");
Settings::values.web_api_url = args.web_api_url;
Settings::values.eden_username = args.username;
Settings::values.eden_token = args.token;
}
}
// Load the ban list
Network::Room::BanList ban_list;
if (!args.ban_list_file.empty()) {
ban_list = LoadBanList(args.ban_list_file);
}
std::unique_ptr<Network::VerifyUser::Backend> verify_backend;
if (announce) {
#ifdef ENABLE_WEB_SERVICE
verify_backend =
std::make_unique<WebService::VerifyUserJWT>(Settings::values.web_api_url.GetValue());
#else
LOG_INFO(Network,
"Eden Web Services is not available with this build: validation is disabled.");
verify_backend = std::make_unique<Network::VerifyUser::NullBackend>();
#endif
} else {
verify_backend = std::make_unique<Network::VerifyUser::NullBackend>();
}
Network::Init();
if (auto room = Network::GetRoom().lock()) {
AnnounceMultiplayerRoom::GameInfo preferred_game_info{
.name = args.preferred_game,
.id = args.preferred_game_id
};
if (!room->Create(args.room_name, args.room_description, args.bind_address, u16(args.port),
args.password, args.max_members, args.username, preferred_game_info,
std::move(verify_backend), ban_list)) {
LOG_INFO(Network, "Failed to create room: ");
return -1;
}
LOG_INFO(Network, "Room is open. Close with Q+Enter...");
auto announce_session = std::make_unique<Core::AnnounceMultiplayerSession>();
if (announce) {
announce_session->Start();
}
while (room->GetState() == Network::Room::State::Open) {
std::string in;
std::cin >> in;
if (in.size() > 0) {
break;
}
std::this_thread::sleep_for(std::chrono::milliseconds(100));
}
if (announce) {
announce_session->Stop();
}
announce_session.reset();
// Save the ban list
if (!args.ban_list_file.empty()) {
SaveBanList(room->GetBanList(), args.ban_list_file);
}
room->Destroy();
}
Network::Shutdown();
return 0;
}
} // namespace Network
+4 -1
View File
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2017 Citra Emulator Project
@@ -10,6 +10,7 @@
#include <memory>
#include <string>
#include <vector>
#include "common/program_args.h"
#include "common/announce_multiplayer_room.h"
#include "common/common_types.h"
#include "common/socket_types.h"
@@ -148,4 +149,6 @@ private:
std::unique_ptr<RoomImpl> room_impl;
};
int LaunchRoomLoopWithArguments(Common::ProgramArguments& args);
} // namespace Network
+1 -1
View File
@@ -253,7 +253,7 @@ if (MSVC)
)
else()
target_compile_options(shader_recompiler PRIVATE
$<$<COMPILE_LANGUAGE:C,CXX>:-Werror=conversion>
$<$<COMPILE_LANGUAGE:C,CXX>:-Wconversion>
# Bracket depth determines maximum size of a fold expression in Clang since 9c9974c3ccb6.
# And this in turns limits the size of a std::array.
$<$<CXX_COMPILER_ID:Clang>:-fbracket-depth=1024>
+1 -1
View File
@@ -373,7 +373,7 @@ else()
$<$<COMPILE_LANGUAGE:C,CXX>:-Wno-shadow>
$<$<COMPILE_LANGUAGE:C,CXX>:-Wno-unused-local-typedef>)
else()
target_compile_options(video_core PRIVATE $<$<COMPILE_LANGUAGE:C,CXX>:-Werror=conversion>)
target_compile_options(video_core PRIVATE $<$<COMPILE_LANGUAGE:C,CXX>:-Wconversion>)
endif()
target_compile_options(video_core PRIVATE $<$<COMPILE_LANGUAGE:C,CXX>:-Wno-sign-conversion>)
+1 -1
View File
@@ -2257,7 +2257,7 @@ public:
/// Returns whether the vertex array specified by index is supposed to be
/// accessed per instance or not.
bool IsInstancingEnabled(std::size_t index) const {
return bool(is_instanced[index]); //FUCK YOU MSVC
return bool(is_instanced[index]);
}
};
-1
View File
@@ -40,7 +40,6 @@ public:
}
struct Regs {
// No fucking idea
INSERT_PADDING_BYTES_NOINIT(0x48);
} regs{};
private:
+1 -1
View File
@@ -963,7 +963,7 @@ void Vic::WriteABGR(const OutputSurfaceConfig& output_surface_config, VideoPixel
auto pixel1213 = _mm_load_si128((__m128i*)&inp[src + x + 12]);
auto pixel1415 = _mm_load_si128((__m128i*)&inp[src + x + 14]);
// Right-shift the channels by 16 to un-do the left shit on read and bring the range
// Right-shift the channels by 16 to un-do the left shift on read and bring the range
// back to 8-bit.
pixel01 = _mm_srli_epi16(pixel01, 2);
pixel23 = _mm_srli_epi16(pixel23, 2);
+1 -1
View File
@@ -88,7 +88,7 @@ set(SHADER_FILES
${CMAKE_CURRENT_SOURCE_DIR}/sgsr1_shader_mobile_edge_direction.frag
)
if (PLATFORM_HAIKU)
if (HAIKUOS)
# glslangValidator WILL crash, glslang will not
set(GLSLANGVALIDATOR "glslang")
else()
+1 -1
View File
@@ -441,7 +441,7 @@ void MacroInterpreterImpl::Reset() {
pc = 0;
delayed_pc = {};
method_address.raw = 0;
// Vector must hold its last indices otherwise wonky shit will happen
// Vector must hold its last indices otherwise chaos will ensue
// The next parameter index starts at 1, because $r1 already has the value of the first
// parameter.
next_parameter_index = 1;
+1 -1
View File
@@ -59,7 +59,7 @@ std::vector<std::string> GetExtensions() {
std::vector<std::string> extensions;
for (GLint index = 0; index < num_extensions; ++index) {
auto const* p = reinterpret_cast<const char*>(glGetStringi(GL_EXTENSIONS, GLuint(index)));
if (p != nullptr) // Fuck you? - sincerely, buggy mesa drivers
if (p != nullptr)
extensions.push_back(std::string{p});
}
return extensions;
+4 -5
View File
@@ -8,7 +8,7 @@ set(CMAKE_INCLUDE_CURRENT_DIR ON)
# Set the RPATH for Qt Libraries
# This must be done before the `yuzu` target is created
if (YUZU_USE_BUNDLED_QT AND PLATFORM_LINUX)
if (YUZU_USE_BUNDLED_QT AND LINUX)
set(CMAKE_BUILD_RPATH "${CMAKE_BINARY_DIR}/bin/lib/")
endif()
@@ -398,6 +398,9 @@ endif()
target_link_libraries(yuzu PRIVATE nlohmann_json::nlohmann_json)
target_link_libraries(yuzu PRIVATE common core input_common frontend_common network video_core qt_common)
target_link_libraries(yuzu PRIVATE Boost::headers Qt6::Widgets Qt6::Charts Qt6::Concurrent)
target_link_libraries(yuzu PRIVATE
OpenSSL::SSL
OpenSSL::Crypto)
target_link_libraries(yuzu PRIVATE ${PLATFORM_LIBRARIES} Threads::Threads)
if (ENABLE_OPENGL)
target_link_libraries(yuzu PRIVATE glad)
@@ -434,10 +437,6 @@ if (ARCHITECTURE_x86_64 OR ARCHITECTURE_arm64)
target_link_libraries(yuzu PRIVATE dynarmic::dynarmic)
endif()
if (YUZU_ROOM)
target_link_libraries(yuzu PRIVATE yuzu-room)
endif()
if (NOT MSVC AND (APPLE OR NOT YUZU_STATIC_BUILD))
# needed for vma
target_compile_options(yuzu PRIVATE
@@ -99,6 +99,7 @@ ConfigureInputAdvanced::ConfigureInputAdvanced(Core::HID::HIDCore& hid_core_, QW
#ifndef _WIN32
ui->enable_raw_input->setVisible(false);
ui->disable_wgi_xinput->setVisible(false);
#endif
LoadConfiguration();
@@ -139,6 +140,7 @@ void ConfigureInputAdvanced::ApplyConfiguration() {
Settings::values.emulate_analog_keyboard = ui->emulate_analog_keyboard->isChecked();
Settings::values.touchscreen.enabled = ui->touchscreen_enabled->isChecked();
Settings::values.enable_raw_input = ui->enable_raw_input->isChecked();
Settings::values.disable_wgi_xinput = ui->disable_wgi_xinput->isChecked();
Settings::values.enable_udp_controller = ui->enable_udp_controller->isChecked();
Settings::values.controller_navigation = ui->controller_navigation->isChecked();
Settings::values.enable_ring_controller = ui->enable_ring_controller->isChecked();
@@ -174,6 +176,7 @@ void ConfigureInputAdvanced::LoadConfiguration() {
ui->emulate_analog_keyboard->setChecked(Settings::values.emulate_analog_keyboard.GetValue());
ui->touchscreen_enabled->setChecked(Settings::values.touchscreen.enabled);
ui->enable_raw_input->setChecked(Settings::values.enable_raw_input.GetValue());
ui->disable_wgi_xinput->setChecked(Settings::values.disable_wgi_xinput.GetValue());
ui->enable_udp_controller->setChecked(Settings::values.enable_udp_controller.GetValue());
ui->controller_navigation->setChecked(Settings::values.controller_navigation.GetValue());
ui->enable_ring_controller->setChecked(Settings::values.enable_ring_controller.GetValue());
@@ -2757,6 +2757,22 @@
</property>
</widget>
</item>
<item row="9" column="0">
<widget class="QCheckBox" name="disable_wgi_xinput">
<property name="toolTip">
<string>Aimed to disable SDL GUIDE button hack: synthetic GUIDE(HOME) event when SELECT(MINUS) + START(PLUS) pressed. May impact Win related trigger/rumble/etc stuff</string>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>23</height>
</size>
</property>
<property name="text">
<string>Disable SDL WGI/XInput (Requires restart)</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
+27 -19
View File
@@ -2,12 +2,10 @@
// SPDX-License-Identifier: GPL-3.0-or-later
#include <QApplication>
#include "common/program_args.h"
#include "network/room.h"
#include "startup_checks.h"
#if YUZU_ROOM
#include <cstring>
#include "dedicated_room/yuzu_room.h"
#endif
#ifdef __unix__
#include "qt_common/gui_settings.h"
#endif
@@ -16,6 +14,12 @@
#include <sys/resource.h>
#endif
#if defined(__APPLE__)
#include <climits>
#include <cstdlib>
#include <cstring>
#endif
#include "main_window.h"
#ifdef _WIN32
@@ -72,20 +76,6 @@ static Qt::HighDpiScaleFactorRoundingPolicy GetHighDpiRoundingPolicy() {
}
int main(int argc, char* argv[]) {
#if YUZU_ROOM
bool launch_room = false;
for (int i = 1; i < argc; i++) {
if (strcmp(argv[i], "--room") == 0) {
launch_room = true;
}
}
if (launch_room) {
LaunchRoom(argc, argv, true);
return 0;
}
#endif
bool has_broken_vulkan = false;
bool is_child = false;
if (CheckEnvVars(&is_child)) {
@@ -126,6 +116,16 @@ int main(int argc, char* argv[]) {
#endif // _WIN32
#if defined(__APPLE__)
// Convert the relative path to an absolute path before the chdir
char resolved[PATH_MAX];
for (int i = 1; i < argc; ++i) {
if (strcmp(argv[i], "-u") == 0 || strcmp(argv[i], "-input-profile") == 0) {
++i;
} else if (argv[i][0] != '-' && argv[i][0] != '/' && realpath(argv[i], resolved)) {
argv[i] = strdup(resolved);
}
}
// If you start a bundle (binary) on OSX without the Terminal, the working directory is "/".
// But since we require the working directory to be the executable path for the location of
// the user folder in the Qt Frontend, we need to cd into that working directory
@@ -184,7 +184,15 @@ int main(int argc, char* argv[]) {
// generating shaders
setlocale(LC_ALL, "C");
MainWindow main_window{has_broken_vulkan};
Common::ProgramArguments args{};
Common::ParseArguments(args, argc, argv);
if (!args.room_name.empty() || !args.room_description.empty()) {
LOG_INFO(Frontend, "Assuming (headless) room mode");
Network::LaunchRoomLoopWithArguments(args);
return -1;
}
MainWindow main_window{std::move(args), has_broken_vulkan};
// After settings have been loaded by GMainWindow, apply the filter
main_window.show();
+21 -60
View File
@@ -1,7 +1,8 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// Qt on macOS doesn't define VMA shit
// Static Qt on macOS doesn't use Vulkan
// Other platforms do, and thus have conflicting VulkanMemoryAllocator symbols
#if defined(QT_STATICPLUGIN) && !defined(__APPLE__)
#undef VMA_IMPLEMENTATION
#endif
@@ -349,7 +350,7 @@ inline static bool isDarkMode() {
}
#endif // _WIN32
MainWindow::MainWindow(bool has_broken_vulkan)
MainWindow::MainWindow(Common::ProgramArguments&& opts, bool has_broken_vulkan)
: ui{std::make_unique<Ui::MainWindow>()},
input_subsystem{std::make_shared<InputCommon::InputSubsystem>()}, user_data_migrator{this} {
QtCommon::Init(this);
@@ -511,73 +512,33 @@ MainWindow::MainWindow(bool has_broken_vulkan)
return;
}
QString game_path;
bool should_launch_qlaunch = false;
bool should_launch_hlaunch = false;
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;
if (opts.selected_user) {
auto user_index = *opts.selected_user;
if (QtCommon::system->GetProfileManager().UserExistsIndex(user_index)) {
Settings::values.current_user = s32(user_index);
user_flag_cmd_line = true;
} else {
game_path = args[i];
has_gamepath = true;
LOG_ERROR(Frontend, "Selected user {} doesn't exist", user_index);
}
}
// Override fullscreen setting if gamepath or argument is provided
if (has_gamepath || is_fullscreen) {
ui->action_Fullscreen->setChecked(is_fullscreen);
if (!opts.input_profile.empty()) {
Settings::values.players.GetValue()[0].profile_name = opts.input_profile;
}
if (should_launch_setup) {
// Override fullscreen setting if gamepath or argument is provided
if (!opts.filepath.empty() && opts.fullscreen) {
ui->action_Fullscreen->setChecked(opts.fullscreen);
}
if (opts.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) {
if (!opts.filepath.empty()) {
BootGame(QString::fromStdString(opts.filepath), ApplicationAppletParameters());
} else if (opts.should_launch_qlaunch) {
LaunchFirmwareApplet(u64(Service::AM::AppletProgramId::QLaunch), std::nullopt);
} else if (should_launch_hlaunch) {
} else if (opts.should_launch_hlaunch) {
std::filesystem::path const sd_dir =
Common::FS::GetEdenPathString(Common::FS::EdenPath::SDMCDir);
auto const hbl_path = (sd_dir / "atmosphere" / "hbl.nsp").string();
+2 -1
View File
@@ -17,6 +17,7 @@
#include <QTranslator>
#include <qaction.h>
#include "common/program_args.h"
#include "common/common_types.h"
#include "common/settings_enums.h"
#include "frontend_common/content_manager.h"
@@ -165,7 +166,7 @@ class MainWindow : public QMainWindow {
public:
void filterBarSetChecked(bool state);
void UpdateUITheme();
explicit MainWindow(bool has_broken_vulkan);
explicit MainWindow(Common::ProgramArguments&& args, bool has_broken_vulkan);
~MainWindow() override;
bool DropAction(QDropEvent* event);
+1 -1
View File
@@ -38,7 +38,7 @@ void MigrationWorker::process() {
try {
fs::remove_all(eden_dir);
} catch (fs::filesystem_error& _) {
// ignore because linux does stupid crap sometimes
// directory may not exist at this point, that's fine
}
switch (strategy) {
+10 -1
View File
@@ -38,7 +38,16 @@ add_executable(yuzu-cmd
${OPENGL_SOURCES}
)
target_link_libraries(yuzu-cmd PRIVATE common core input_common frontend_common video_core)
target_link_libraries(yuzu-cmd PRIVATE common network core input_common frontend_common video_core)
if (ENABLE_WEB_SERVICE)
target_compile_definitions(yuzu-cmd PRIVATE ENABLE_WEB_SERVICE)
target_link_libraries(yuzu-cmd PRIVATE web_service)
endif()
target_link_libraries(yuzu-cmd PRIVATE
OpenSSL::SSL
OpenSSL::Crypto)
if (ENABLE_OPENGL)
target_link_libraries(yuzu-cmd PRIVATE glad)
endif()
+36 -56
View File
@@ -5,6 +5,10 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#include <SDL3/SDL.h>
#include <SDL3/SDL_timer.h>
#ifdef __EMSCRIPTEN__
#include <emscripten.h>
#endif
#include "common/logging.h"
#include "common/scm_rev.h"
@@ -26,9 +30,17 @@ EmuWindow_SDL3::EmuWindow_SDL3(InputCommon::InputSubsystem* input_subsystem_, Co
LOG_CRITICAL(Frontend, "Failed to initialize SDL3: {}, Exiting...", SDL_GetError());
exit(1);
}
titlebar_timer = SDL_AddTimer(2000, [](void *userdata, SDL_TimerID, Uint32) -> Uint32 {
auto* this_ = (EmuWindow_SDL3*)userdata;
auto const results = this_->system.GetAndResetPerfStats();
auto const title = fmt::format("{} | {}-{} | FPS: {:.0f} ({:.0f}%)", Common::g_build_fullname, Common::g_scm_branch, Common::g_scm_desc, results.average_game_fps, results.emulation_speed * 100.0f);
SDL_SetWindowTitle(this_->render_window, title.c_str());
return 2000;
}, this);
}
EmuWindow_SDL3::~EmuWindow_SDL3() {
SDL_RemoveTimer(titlebar_timer);
system.HIDCore().UnloadInputDevices();
input_subsystem->Shutdown();
SDL_Quit();
@@ -132,8 +144,7 @@ void EmuWindow_SDL3::Fullscreen() {
switch (Settings::values.fullscreen_mode.GetValue()) {
case Settings::FullscreenMode::Exclusive:
// Set window size to render size before entering fullscreen in exclusive mode.
if (const SDL_DisplayMode* display_mode_ptr =
SDL_GetDesktopDisplayMode(SDL_GetDisplayForWindow(render_window))) {
if (const SDL_DisplayMode* display_mode_ptr = SDL_GetDesktopDisplayMode(SDL_GetDisplayForWindow(render_window))) {
display_mode = *display_mode_ptr;
SDL_SetWindowSize(render_window, display_mode.w, display_mode.h);
SDL_SetWindowFullscreenMode(render_window, &display_mode);
@@ -165,92 +176,60 @@ void EmuWindow_SDL3::Fullscreen() {
}
}
void EmuWindow_SDL3::WaitEvent() {
// Called on main thread
SDL_Event event;
if (!SDL_WaitEvent(&event)) {
const char* error = SDL_GetError();
if (!error || strcmp(error, "") == 0) {
// https://github.com/libsdl-org/SDL/issues/5780
// Sometimes SDL will return without actually having hit an error condition;
// just ignore it in this case.
return;
}
LOG_CRITICAL(Frontend, "SDL_WaitEvent failed: {}", error);
exit(1);
}
void EmuWindow_SDL3::OnEvent(SDL_Event& event) {
// Notice how we skip the "update title" aspect on most events
// this is because some WMs do NOT like changing titles while resizing
// so let's just... not do that, thanks :)
// Afterall we don't really expect the user to pay attention to the titlebar
// while they're moving a lot of shit around...
switch (event.type) {
case SDL_EVENT_WINDOW_RESIZED:
case SDL_EVENT_WINDOW_PIXEL_SIZE_CHANGED:
case SDL_EVENT_WINDOW_MAXIMIZED:
case SDL_EVENT_WINDOW_RESTORED:
OnResize();
break;
return OnResize();
case SDL_EVENT_WINDOW_MINIMIZED:
is_shown = false;
OnResize();
break;
return OnResize();
case SDL_EVENT_WINDOW_EXPOSED:
is_shown = true;
OnResize();
break;
return OnResize();
case SDL_EVENT_WINDOW_CLOSE_REQUESTED:
is_open = false;
break;
return;
case SDL_EVENT_KEY_DOWN:
case SDL_EVENT_KEY_UP:
OnKeyEvent(static_cast<int>(event.key.scancode), event.key.down ? 1 : 0);
break;
return OnKeyEvent(int(event.key.scancode), event.key.down ? 1 : 0);
case SDL_EVENT_MOUSE_MOTION:
// ignore if it came from touch
if (event.button.which != SDL_TOUCH_MOUSEID)
OnMouseMotion(event.motion.x, event.motion.y);
break;
return;
case SDL_EVENT_MOUSE_BUTTON_DOWN:
case SDL_EVENT_MOUSE_BUTTON_UP:
// ignore if it came from touch
if (event.button.which != SDL_TOUCH_MOUSEID) {
OnMouseButton(event.button.button, event.button.down ? 1 : 0,
static_cast<s32>(event.button.x), static_cast<s32>(event.button.y));
}
break;
if (event.button.which != SDL_TOUCH_MOUSEID)
OnMouseButton(event.button.button, event.button.down ? 1 : 0, s32(event.button.x), s32(event.button.y));
return;
case SDL_EVENT_FINGER_DOWN:
OnFingerDown(event.tfinger.x, event.tfinger.y,
static_cast<std::size_t>(event.tfinger.touchID));
break;
return OnFingerDown(event.tfinger.x, event.tfinger.y, std::size_t(event.tfinger.touchID));
case SDL_EVENT_FINGER_MOTION:
OnFingerMotion(event.tfinger.x, event.tfinger.y,
static_cast<std::size_t>(event.tfinger.touchID));
break;
return OnFingerMotion(event.tfinger.x, event.tfinger.y, std::size_t(event.tfinger.touchID));
case SDL_EVENT_FINGER_UP:
OnFingerUp();
break;
return OnFingerUp();
case SDL_EVENT_QUIT:
is_open = false;
break;
return;
default:
break;
}
const u32 current_time = SDL_GetTicks();
if (current_time > last_time + 2000) {
const auto results = system.GetAndResetPerfStats();
const auto title = fmt::format("{} | {}-{} | FPS: {:.0f} ({:.0f}%)",
Common::g_build_fullname,
Common::g_scm_branch,
Common::g_scm_desc,
results.average_game_fps,
results.emulation_speed * 100.0);
SDL_SetWindowTitle(render_window, title.c_str());
last_time = current_time;
}
}
// Credits to Samantas5855 and others for this function.
void EmuWindow_SDL3::SetWindowIcon() {
#if defined(__EMSCRIPTEN__) || defined(__wasi__)
// Icons do not work yet
#else
SDL_IOStream* const yuzu_icon_stream = SDL_IOFromConstMem((void*)yuzu_icon, yuzu_icon_size);
if (yuzu_icon_stream == nullptr) {
LOG_WARNING(Frontend, "Failed to create Eden icon stream.");
@@ -264,6 +243,7 @@ void EmuWindow_SDL3::SetWindowIcon() {
// The icon is attached to the window pointer
SDL_SetWindowIcon(render_window, window_icon);
SDL_DestroySurface(window_icon);
#endif
}
void EmuWindow_SDL3::OnMinimalClientAreaChangeRequest(std::pair<u32, u32> minimal_size) {
+7 -6
View File
@@ -9,11 +9,12 @@
#include <tuple>
#include <utility>
#include <SDL3/SDL.h>
#include <SDL3/SDL_timer.h>
#include "core/frontend/emu_window.h"
#include "core/frontend/graphics_context.h"
struct SDL_Window;
namespace Core {
class System;
}
@@ -35,7 +36,7 @@ public:
bool IsShown() const override;
/// Wait for the next event on the main thread.
void WaitEvent();
void OnEvent(SDL_Event& event);
// Sets the window icon from yuzu.bmp
void SetWindowIcon();
@@ -80,6 +81,9 @@ protected:
/// Called when a configuration change affects the minimal size of the window
void OnMinimalClientAreaChangeRequest(std::pair<u32, u32> minimal_size) override;
/// Periodic changer of titlebar (independent of event loop)
SDL_TimerID titlebar_timer;
/// Is the window still open?
bool is_open = true;
@@ -89,9 +93,6 @@ protected:
/// Internal SDL3 render window
SDL_Window* render_window{};
/// Keeps track of how often to update the title bar during gameplay
u32 last_time = 0;
/// Input subsystem to use with this window.
InputCommon::InputSubsystem* input_subsystem;
+186 -309
View File
@@ -8,6 +8,16 @@
#include <memory>
#include <regex>
#include <string>
#include <SDL3/SDL_init.h>
#include <openssl/evp.h>
#include "common/fs/file.h"
#include "common/program_args.h"
#include "common/settings_enums.h"
#ifdef __EMSCRIPTEN__
#include <emscripten.h>
#endif
#define SDL_MAIN_USE_CALLBACKS 1
#include <SDL3/SDL_main.h>
#include <fmt/ostream.h>
@@ -26,9 +36,14 @@
#include "core/loader/loader.h"
#include "frontend_common/config.h"
#include "input_common/main.h"
#include "network/announce_multiplayer_session.h"
#include "network/network.h"
#include "sdl_config.h"
#include "network/room.h"
#include "network/verify_user.h"
#include "yuzu_cmd/sdl_config.h"
#include "video_core/renderer_base.h"
#include "yuzu_cmd/emu_window/emu_window_sdl3.h"
#ifdef HAS_OPENGL
#include "yuzu_cmd/emu_window/emu_window_sdl3_gl.h"
@@ -39,9 +54,7 @@
#ifdef _WIN32
// windows.h needs to be included before shellapi.h
#include <windows.h>
#include <shellapi.h>
#include "common/windows/timer_resolution.h"
#endif
@@ -60,25 +73,6 @@ __declspec(dllexport) int AmdPowerXpressRequestHighPerformance = 1;
}
#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) {
switch (state) {
case Network::RoomMember::State::Idle:
@@ -174,8 +168,154 @@ static void OnStatusMessageReceived(const Network::StatusMessageEntry& msg) {
std::cout << std::endl << "* " << message << std::endl << std::endl;
}
/// Application entry point
int main(int argc, char** argv) {
struct SdlState {
Core::System system{};
std::unique_ptr<EmuWindow_SDL3> emu_window;
// settings
Common::ProgramArguments opts = {};
};
static SDL_AppResult ExecuteWithGUI(SdlState& state) {
SdlConfig config{state.opts.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(state.opts.log_filter.value_or(Settings::values.log_filter.GetValue()));
Common::Log::SetGlobalFilter(filter);
if (!state.opts.program_args.empty()) {
Settings::values.program_args = state.opts.program_args;
}
if (!state.opts.input_profile.empty()) {
auto& players = Settings::values.players.GetValue();
players[0].profile_name = state.opts.input_profile;
}
if (state.opts.selected_user.has_value()) {
Settings::values.current_user = std::clamp(*state.opts.selected_user, 0, 7);
}
if (state.opts.override_gdb_port.has_value()) {
Settings::values.use_gdbstub = true;
Settings::values.gdbstub_port = *state.opts.override_gdb_port;
}
if (state.opts.force_single_core) {
Settings::values.use_multi_core = false;
}
if (state.opts.force_null_render) {
Settings::values.renderer_backend = Settings::RendererBackend::Null;
}
if (state.opts.filepath.empty()) {
LOG_CRITICAL(Frontend, "Failed to load ROM: No ROM specified");
return SDL_APP_FAILURE;
}
state.system.Initialize();
InputCommon::InputSubsystem input_subsystem{};
// Apply the command line arguments
state.system.ApplySettings();
switch (Settings::values.renderer_backend.GetValue()) {
#ifdef HAS_OPENGL
case Settings::RendererBackend::OpenGL_GLSL:
case Settings::RendererBackend::OpenGL_GLASM:
case Settings::RendererBackend::OpenGL_SPIRV:
state.emu_window = std::make_unique<EmuWindow_SDL3_GL>(&input_subsystem, state.system, state.opts.fullscreen);
break;
#endif
case Settings::RendererBackend::Vulkan:
state.emu_window = std::make_unique<EmuWindow_SDL3_VK>(&input_subsystem, state.system, state.opts.fullscreen);
break;
case Settings::RendererBackend::Null:
state.emu_window = std::make_unique<EmuWindow_SDL3_Null>(&input_subsystem, state.system, state.opts.fullscreen);
break;
default:
LOG_CRITICAL(Frontend, "Invalid renderer backend");
return SDL_APP_FAILURE;
}
#ifdef _WIN32
Common::Windows::SetCurrentTimerResolutionToMaximum();
state.system.CoreTiming().SetTimerResolutionNs(Common::Windows::GetCurrentTimerResolution());
#endif
state.system.SetContentProvider(std::make_unique<FileSys::ContentProviderUnion>());
state.system.SetFilesystem(std::make_shared<FileSys::RealVfsFilesystem>());
state.system.GetFileSystemController().CreateFactories(*state.system.GetFilesystem());
state.system.GetUserChannel().clear();
Service::AM::FrontendAppletParameters load_parameters{
.applet_id = Service::AM::AppletId::Application,
};
const Core::SystemResultStatus load_result = state.system.Load(*state.emu_window, state.opts.filepath, load_parameters);
switch (load_result) {
case Core::SystemResultStatus::Success:
break; // Expected case
case Core::SystemResultStatus::ErrorGetLoader:
LOG_CRITICAL(Frontend, "Failed to obtain loader for {}!", state.opts.filepath);
return SDL_APP_FAILURE;
case Core::SystemResultStatus::ErrorLoader:
LOG_CRITICAL(Frontend, "Failed to load ROM!");
return SDL_APP_FAILURE;
case Core::SystemResultStatus::ErrorNotInitialized:
LOG_CRITICAL(Frontend, "CPUCore not initialized");
return SDL_APP_FAILURE;
case Core::SystemResultStatus::ErrorVideoCore:
LOG_CRITICAL(Frontend, "Failed to initialize VideoCore!");
return SDL_APP_FAILURE;
default:
const u16 loader_id = u16(Core::SystemResultStatus::ErrorLoader);
const u16 error_id = u16(load_result) - loader_id;
LOG_CRITICAL(Frontend,
"While attempting to load the ROM requested, an error occurred. Please "
"refer to the Eden wiki for more information or the Eden discord for "
"additional help.\n\nError Code: {:04X}-{:04X}\nError Description: {}",
loader_id, error_id, Loader::ResultStatus(error_id));
return SDL_APP_FAILURE;
}
if (state.opts.use_multiplayer) {
if (auto member = Network::GetRoomMember().lock()) {
member->BindOnChatMessageReceived(OnMessageReceived);
member->BindOnStatusMessageReceived(OnStatusMessageReceived);
member->BindOnStateChanged(OnStateChanged);
member->BindOnError(OnNetworkError);
LOG_DEBUG(Network, "Start connection to {}:{} with nickname {}", state.opts.address, state.opts.port, state.opts.nickname);
member->Join(state.opts.nickname, state.opts.address.c_str(), state.opts.port, 0, Network::NoPreferredIP, state.opts.password);
} else {
LOG_ERROR(Network, "Could not access RoomMember");
return SDL_APP_FAILURE;
}
}
// Core is loaded, start the GPU (makes the GPU contexts current to this thread)
state.system.GPU().Start();
state.system.GetCpuManager().OnGpuReady();
if (Settings::values.use_disk_shader_cache.GetValue()) {
state.system.Renderer().ReadRasterizer()->LoadDiskResources(
state.system.GetApplicationProcessProgramID(), std::stop_token{},
[](VideoCore::LoadCallbackStage, size_t value, size_t total) {});
}
// don't do anything, SDL3 already exists for us :D
state.system.RegisterExitCallback([] {});
void(state.system.Run());
if (state.system.DebuggerEnabled())
state.system.InitializeDebugger();
return SDL_APP_SUCCESS;
}
extern "C" SDL_AppResult SDL_AppInit(void **appstate, int argc, char **argv) {
SdlState* state = new SdlState();
#ifdef _WIN32
if (AttachConsole(ATTACH_PARENT_PROCESS)) {
freopen("CONOUT$", "wb", stdout);
@@ -187,292 +327,29 @@ int main(int argc, char** argv) {
Common::Log::SetColorConsoleBackendEnabled(true);
Common::Log::Start();
int option_index = 0;
#ifdef _WIN32
int argc_w;
auto argv_w = CommandLineToArgvW(GetCommandLineW(), &argc_w);
if (argv_w == nullptr) {
LOG_CRITICAL(Frontend, "Failed to get command line arguments");
return -1;
Common::ParseArguments(state->opts, argc, argv);
if (!state->opts.room_name.empty() || !state->opts.room_description.empty()) {
LOG_INFO(Frontend, "Assuming (headless) room mode");
Network::LaunchRoomLoopWithArguments(state->opts);
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 0;
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 -1;
}
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 -1;
}
if (address.empty()) {
LOG_ERROR(Frontend, "Address to room must not be empty");
return -1;
}
break;
}
case 'p':
program_args = argv[optind];
++optind;
break;
case 'u':
selected_user = atoi(optarg);
break;
case 'v':
PrintVersion();
return -1;
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
filepath = argv[optind];
#endif
optind++;
}
}
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
LocalFree(argv_w);
#endif
if (filepath.empty()) {
LOG_CRITICAL(Frontend, "Failed to load ROM: No ROM specified");
return -1;
}
Core::System system{};
system.Initialize();
InputCommon::InputSubsystem input_subsystem{};
// Apply the command line arguments
system.ApplySettings();
std::unique_ptr<EmuWindow_SDL3> emu_window;
switch (Settings::values.renderer_backend.GetValue()) {
#ifdef HAS_OPENGL
case Settings::RendererBackend::OpenGL_GLSL:
case Settings::RendererBackend::OpenGL_GLASM:
case Settings::RendererBackend::OpenGL_SPIRV:
emu_window = std::make_unique<EmuWindow_SDL3_GL>(&input_subsystem, system, fullscreen);
break;
#endif
case Settings::RendererBackend::Vulkan:
emu_window = std::make_unique<EmuWindow_SDL3_VK>(&input_subsystem, system, fullscreen);
break;
case Settings::RendererBackend::Null:
emu_window = std::make_unique<EmuWindow_SDL3_Null>(&input_subsystem, system, fullscreen);
break;
default:
LOG_CRITICAL(Frontend, "Invalid renderer backend");
return -1;
}
#ifdef _WIN32
Common::Windows::SetCurrentTimerResolutionToMaximum();
system.CoreTiming().SetTimerResolutionNs(Common::Windows::GetCurrentTimerResolution());
#endif
system.SetContentProvider(std::make_unique<FileSys::ContentProviderUnion>());
system.SetFilesystem(std::make_shared<FileSys::RealVfsFilesystem>());
system.GetFileSystemController().CreateFactories(*system.GetFilesystem());
system.GetUserChannel().clear();
Service::AM::FrontendAppletParameters load_parameters{
.applet_id = Service::AM::AppletId::Application,
};
const Core::SystemResultStatus load_result{system.Load(*emu_window, filepath, load_parameters)};
switch (load_result) {
case Core::SystemResultStatus::ErrorGetLoader:
LOG_CRITICAL(Frontend, "Failed to obtain loader for {}!", filepath);
return -1;
case Core::SystemResultStatus::ErrorLoader:
LOG_CRITICAL(Frontend, "Failed to load ROM!");
return -1;
case Core::SystemResultStatus::ErrorNotInitialized:
LOG_CRITICAL(Frontend, "CPUCore not initialized");
return -1;
case Core::SystemResultStatus::ErrorVideoCore:
LOG_CRITICAL(Frontend, "Failed to initialize VideoCore!");
return -1;
case Core::SystemResultStatus::Success:
break; // Expected case
default:
if (static_cast<u32>(load_result) >
static_cast<u32>(Core::SystemResultStatus::ErrorLoader)) {
const u16 loader_id = static_cast<u16>(Core::SystemResultStatus::ErrorLoader);
const u16 error_id = static_cast<u16>(load_result) - loader_id;
LOG_CRITICAL(Frontend,
"While attempting to load the ROM requested, an error occurred. Please "
"refer to the Eden wiki for more information or the Eden discord for "
"additional help.\n\nError Code: {:04X}-{:04X}\nError Description: {}",
loader_id, error_id, static_cast<Loader::ResultStatus>(error_id));
}
break;
}
if (use_multiplayer) {
if (auto member = Network::GetRoomMember().lock()) {
member->BindOnChatMessageReceived(OnMessageReceived);
member->BindOnStatusMessageReceived(OnStatusMessageReceived);
member->BindOnStateChanged(OnStateChanged);
member->BindOnError(OnNetworkError);
LOG_DEBUG(Network, "Start connection to {}:{} with nickname {}", address, port,
nickname);
member->Join(nickname, address.c_str(), port, 0, Network::NoPreferredIP, password);
} else {
LOG_ERROR(Network, "Could not access RoomMember");
return 0;
}
}
// Core is loaded, start the GPU (makes the GPU contexts current to this thread)
system.GPU().Start();
system.GetCpuManager().OnGpuReady();
if (Settings::values.use_disk_shader_cache.GetValue()) {
system.Renderer().ReadRasterizer()->LoadDiskResources(
system.GetApplicationProcessProgramID(), std::stop_token{},
[](VideoCore::LoadCallbackStage, size_t value, size_t total) {});
}
system.RegisterExitCallback([&] {
// Just exit right away.
exit(0);
});
void(system.Run());
if (system.DebuggerEnabled()) {
system.InitializeDebugger();
}
while (emu_window->IsOpen()) {
emu_window->WaitEvent();
}
system.DetachDebugger();
void(system.Pause());
system.ShutdownMainProcess();
return 0;
return ExecuteWithGUI(*state);
}
extern "C" SDL_AppResult SDL_AppIterate(void *appstate) {
SdlState *state = (SdlState *)appstate;
return state->emu_window->IsOpen() ? SDL_APP_CONTINUE : SDL_APP_SUCCESS;
}
extern "C" SDL_AppResult SDL_AppEvent(void *appstate, SDL_Event *event) {
SdlState *state = (SdlState *)appstate;
state->emu_window->OnEvent(*event);
return SDL_APP_SUCCESS;
}
extern "C" void SDL_AppQuit(void *appstate, SDL_AppResult result) {
SdlState *state = (SdlState *)appstate;
state->system.DetachDebugger();
void(state->system.Pause());
state->system.ShutdownMainProcess();
delete state;
}
#define VMA_IMPLEMENTATION
-18
View File
@@ -1,18 +0,0 @@
# SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
# SPDX-License-Identifier: GPL-3.0-or-later
add_executable(yuzu_room_standalone yuzu_room_standalone.cpp)
set_target_properties(yuzu_room_standalone PROPERTIES OUTPUT_NAME "eden-room")
target_link_libraries(yuzu_room_standalone PRIVATE yuzu-room)
if(UNIX AND NOT APPLE)
install(TARGETS yuzu_room_standalone RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}/bin")
endif()
if (YUZU_STATIC_ROOM)
target_link_options(yuzu_room_standalone PRIVATE "-static")
endif()
create_target_directory_groups(yuzu_room_standalone)
-9
View File
@@ -1,9 +0,0 @@
#include <iostream>
using namespace std;
int main()
{
cout << "Hello World!" << endl;
return 0;
}
@@ -1,9 +0,0 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#include "dedicated_room/yuzu_room.h"
int main(int argc, char* argv[])
{
LaunchRoom(argc, argv, false);
}