Compare commits

...

20 Commits

Author SHA1 Message Date
lizzie e9e6ddf2a9 [ports, freebsd] use shm_create_largepage(3) with a DEFAULT allocation to exploit defragmented RAM situations
Signed-off-by: lizzie <lizzie@eden-emu.dev>
2026-06-16 01:11:17 +00:00
lizzie c8c61a12c3 [dynarmic, MacroHLE] Fix crashes on W^X systems (#4090)
should fix crashes on:
- NetBSD
- SELinux
- OpenBSD
- other W^X systems
- some person trying to be funny with W^X on their system even if they dont have to

perf impact should be minimal

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

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4090
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
Reviewed-by: Maufeat <sahyno1996@gmail.com>
2026-06-15 22:06:14 +02:00
lizzie b05463ee19 [fs] use XDG_CACHE_HOME for shader caches (#4093)
makes it so the shader cache goes properly in `XDG_CACHE_HOME`.

SOME people have it set to point to `/tmp` AND as a ramdisk, if they can do that they probably would want their shader caches to be there as well and wouldnt mind the extra hit from invalidations on reboot.

afterall, its a cache, not some holy data that needs to be kept at all costs.

Signed-off-by: lizzie <lizzie@eden-emu.dev>
Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4093
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
Reviewed-by: Maufeat <sahyno1996@gmail.com>
2026-06-15 22:05:53 +02:00
lizzie 561adac0af [common] fallback to VirtualBuffer if we can't use file mappings (#4005)
fixes:
- windows 8.1
- windows 7
- managarm
- openorbis

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

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4005
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
Reviewed-by: Maufeat <sahyno1996@gmail.com>
2026-06-15 22:05:42 +02:00
lizzie 62ef8b15fd [video_core] correct not-quite-correct PTE_kind enumeration (#4077)
should now be more align with actual values/enums :)
trivial change

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

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4077
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
Reviewed-by: Maufeat <sahyno1996@gmail.com>
2026-06-15 22:05:18 +02:00
lizzie 8e9513cb5f [service/ro] Fix too many plugin.nro not being loaded for SSBU (#3982)
Good fix or absolute trash?

For me it seems like an OK balance, better would be fully dynamic; but I believe 256 is a reasonable limit?

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

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/3982
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
Reviewed-by: Maufeat <sahyno1996@gmail.com>
2026-06-15 21:59:53 +02:00
lizzie 950e0f82fb [hle/sm] implement sm:AtmosphereHasService (#3771)
should make a few more homebrew boot :)

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

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/3771
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
Reviewed-by: Maufeat <sahyno1996@gmail.com>
2026-06-15 21:56:22 +02:00
lizzie 74a6607f8e [fs] do not early abort on IterateDirEntries{Recursively} (#4100)
should fix some issues with some NAS setups
where people have their games on a NAS and they
try to access it but for some reason some file
just doesnt load, instead of aborting early we continue
for the rest of the files in the directory

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

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4100
Reviewed-by: Shinmegumi <shinmegumi@eden-emu.dev>
Reviewed-by: Maufeat <sahyno1996@gmail.com>
2026-06-15 21:31:20 +02:00
BoiledElectricity 5ebb5b8772 [opengl] query GL_MAX_CLIP_DISTANCES instead of hardcoding 8 (#4095)
The GL shader profile hardcoded max_user_clip_distances to 8. Query the
device limit like the Vulkan path already does (it reads
maxClipDistances), so we use what the host actually reports.

Clamp to Maxwell's NumClipDistances (8) since the guest never produces
more than that and the SPIR-V output array is sized for at most 8. So a
host reporting fewer than 8 is respected, and one reporting more can't
overrun anything.

Fixes #3910
https://git.eden-emu.dev/eden-emu/eden/issues/3910

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4095
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
Reviewed-by: Lizzie <lizzie@eden-emu.dev>
2026-06-15 04:30:40 +02:00
xbzk 73918d23d5 [video_core, maxwell] fixes for homebrew games that use MESA compiler (#4012)
two main issues:

shader_environment.cpp

Support Mesa nv50_ir-compiled homebrew fragment shaders
Mesa nv50_ir (used by NZP-Switch via libdrm_nouveau) emits Maxwell SASS that differs from NVN in two ways our shader pipeline didn't account for, causing "infinite" loops during shader scanning.

How?  TryFindSize() previously detected end-of-shader only via NVN's "BRA $-1" self-branch trailer (SELF_BRANCH_A/B). Mesa-compiled shaders end with an unconditional @PT EXIT and emit no trailer, so scanning ran past the shader and looped to MAXIMUM_SIZE.

Fix: Added a secondary match against @PT EXIT T (opcode 0xE30, predicate PT, flow T) as a fallback terminator. The mask is estrictive enough to reject predicated EXITs, conditional-flow EXITs, and sched control words, so well-formed NVN shaders see no ehavior change (their single @PT EXIT immediately precedes BRA $-1, and both detections return the same size).

load_store_attribute.cpp + attribute.h

IPA's is_perspective check only applied the ×position_w correction for IR::IsGeneric() attributes, and used a per-component SPH lookup. Two failure modes followed:

1. Mesa fragment shaders read varyings via legacy attribute slots
(ColorFrontDiffuse, FixedFncTexture, FogCoordinate) which are
remapped to generic varyings later by ConvertLegacyToGeneric. At
IPA-translation time they're still legacy, so IsGeneric() was
false and the inject was skipped — but the resulting GLSL/SPIR-V
varying was Smooth (perspective-correct), and the SASS still
issued its manual perspective dance via MUFU.RCP(ATTR_W)+FMUL,
compounding an extra ×clip_w factor. NZP fog (eye_z distance
varying) ended up as eye_z⁴×density² instead of eye_z²×density²,
saturating fog to white everywhere.

2. Even for legacy attrs that should inject, Mesa stores them in two formats selected by the IPA's interpolation_mode field:
Pass/Multiply/Constant → attr/w (perspective); inject ×position_w
Sc (ScreenLinear)      → raw attr; do NOT inject
Unconditionally injecting for Sc dims vertex colors by 1/clip_w because there's no SASS multiplier to round-trip it back.

Fix: extend the IPA correction path to also fire on IsLegacyAttribute(), gated on interpolation_mode != Sc. While here, the generic-path check is widened from per-component to vector-level (first non-Unused PixelImap, with all-Unused fallthrough treated as Perspective) so it matches the GLSL interpolation qualifier picked by CollectInterpolationInfo — this fixes the same all-zeroed-imap case Mesa exposes for explicit generics. IsLegacyAttribute moved from translate_program.cpp's anonymous namespace into attribute.h next to IsGeneric so IPA can reach it.

Tested on NaziZombies:Portable (Switch homebrew): fog now matches real
hardware, vertex-colored geometry (hand, gun, decals) renders correctly.

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4012
Reviewed-by: Lizzie <lizzie@eden-emu.dev>
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
2026-06-15 03:05:11 +02:00
BoiledElectricity ef4113aeaa [video_core] fix TOCTOU in Vulkan instance extension enumeration (#4072)
So CreateInstance was enumerating instance extensions twice, and that could race if the driver returned a different list the second time. On some AMD iGPU drivers, we could enable an extension from the first list, then fail the second check with VK_ERROR_EXTENSION_NOT_PRESENT.

Fix this by enumerating once and passing that same snapshot into RequiredExtensions.

Fixes: https://github.com/eden-emulator/Issue-Reports/issues/414
Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4072
Reviewed-by: Lizzie <lizzie@eden-emu.dev>
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
2026-06-15 03:02:53 +02:00
xbzk 78a1cd0533 [hle] support passing arguments to homebrew applications (#4013)
Homebrew (.NRO) program args support

Adds a 'program_args' setting and delivers it to NRO homebrews via libnx's homebrew ABI. NROs previously had no way to receive CLI flags (e.g. NZ:P's -noglsl).

Setting: program_args, Category::System, startup-only.
Surfaced in Qt + Android Debug sections.

NRO loader: builds a 4-entry 'ConfigEntry' table + argv at the data segment tail; prepends 'homebrew ' so user args land at argv[1]; scans for 'svc #7' to use as LR. Drops the stale NSO-style argdata append.

KProcess: stores loader-provided guest addresses; 'Run' switches to the homebrew entry (x0=config_ptr, x1=-1, lr=svc7) and patches the real MainThreadHandle into the ConfigEntry slot. Legacy NSO path unchanged.

Tested on NZP — args reach 'argv' correctly.

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4013
Reviewed-by: Lizzie <lizzie@eden-emu.dev>
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
2026-06-15 03:02:33 +02:00
xbzk 026974211e [vk, homebrew] ETC2 format list added (#4061)
Liz' command. ETC2 format list added. Used mainly in homebrew, NZ:P for example.

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4061
Reviewed-by: Lizzie <lizzie@eden-emu.dev>
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
2026-06-15 03:00:58 +02:00
BoiledElectricity d698c3b601 [hle/fs] fix handle Temporary/ProperSystem/SafeMode save spaces instead of ASSERT(false) (#4069)
OpenSaveDataFileSystem was missing a few SaveDataSpaceId mappings, so it would hit ASSERT(false). This broke games that open cache storage, with TOTK being the obvious one, because save enumeration could assert before the game even finished loading.

Fixed this by mapping Temporary to user nand, and ProperSystem/SafeMode to system nand. These only get used for the free-space check here, so they just need to point somewhere sane.

Fixes: https://github.com/eden-emulator/Issue-Reports/issues/368
Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4069
Reviewed-by: Lizzie <lizzie@eden-emu.dev>
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
2026-06-15 03:00:21 +02:00
lizzie 60e1032771 [qt] fix various crashes due to invalid/corrupted/outdated settings (#4070)
lots of "out of index" errors :)

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

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4070
Reviewed-by: crueter <crueter@eden-emu.dev>
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
2026-06-14 20:48:24 +02:00
Eden CI 1071353291 [dist, android] Update translations from Transifex for Jun 13 (#4091)
Automatic translation update for Jun 13

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4091
Reviewed-by: Lizzie <lizzie@eden-emu.dev>
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
2026-06-13 18:35:50 +02:00
MaranBr eb8086a011 [buffer_cache] Fix Xenoblade Chronicles series (#4066)
This fixes a bug introduced in the Xenoblade Chronicles series where some small elements would occasionally flicker on the screen.

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4066
Reviewed-by: crueter <crueter@eden-emu.dev>
Reviewed-by: Lizzie <lizzie@eden-emu.dev>
2026-06-13 01:06:57 +02:00
lizzie 4edb1ac6c5 [common/fs] create XDG_DATA_{HOME,CACHE,CONFIG}_HOME if we are the first application to ever be ran or they dont exist for a reason (#4087)
samu will be happy

basically this handles the edge case where either:
a) eden is the first application ever ran (i.e no config dirs made yet)
b) user didnt have them
c) user is trying to run on a sandbox appimage or whatever bs

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

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4087
Reviewed-by: crueter <crueter@eden-emu.dev>
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
2026-06-12 23:55:09 +02:00
lizzie 3875093c70 [hle] Fixes for nxident, nxmp and nxplay, remove hbl.nsp workarounds (#3996)
adds `gpio` and `i2c` services
implements `IApplicationFunctions::SetMediaPlaybackStateForApplicatio`
stubs `IBtmSystemCore::GetDiscoveredAudioDevice`
implements `nvhost_ctrl_gpu::PmuGetGpuLoad` IOCTL (0x20)
implements `PSM::GetBatteryAgePercentage`, `PSM::GetBatteryVoltageState` and `PSM::GetBatteryChargeInfoFields`
implements `ISystemSettingsServer::GetConsoleInformationUploadFlag`, `ISystemSettingsServer::SetConsoleInformationUploadFlag`, `ISystemSettingsServer::GetAutomaticApplicationDownloadFlag`, `ISystemSettingsServer::SetAutomaticApplicationDownloadFlag`, `&ISystemSettingsServer::GetUsb30EnableFlag`, `ISystemSettingsServer::SetUsb30EnableFlag`
removes `hbl.nsp` specific workarounds

Signed-off-by: lizzie <lizzie@eden-emu.dev>
Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/3996
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
Reviewed-by: crueter <crueter@eden-emu.dev>
2026-06-12 23:48:24 +02:00
RedBlackAka 86895a5f6a [windows] Add NSIS installer (#4008)
Adds an NSIS Windows installer file. Includes:

- Installation to `user\AppData\Local\Eden` (user install; does not require administrator and makes the integrated updater work and same as e.g. yuzu, Citra, Azahar)
- Shortcuts for Start Menu and optionally to Desktop
- File association with `.nsp`, `.xci`, `.nro` and `.kip`
- Throughout uninstallation and registry cleanup

And finally, needs to be implemented into the GitHub CI workflow, will submit a PR there later uwu

Co-authored-by: RedBlackAka <140876408+RedBlackAka@users.noreply.github.com>
Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4008
Reviewed-by: crueter <crueter@eden-emu.dev>
Reviewed-by: Lizzie <lizzie@eden-emu.dev>
2026-06-12 23:44:13 +02:00
70 changed files with 1171 additions and 371 deletions
+184
View File
@@ -0,0 +1,184 @@
; SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
; SPDX-License-Identifier: GPL-3.0-or-later
; Usage:
; get the latest nsis: https://nsis.sourceforge.io/Download
; Require these for makensis.
!ifndef PRODUCT_VERSION
!error "PRODUCT_VERSION must be defined"
!endif
!ifndef ARCH
!error "ARCH must be defined"
!endif
!ifndef VARIANT
!error "VARIANT must be defined"
!endif
Unicode true
ManifestDPIAware true
!define PRODUCT_NAME "Eden"
!define PRODUCT_PUBLISHER "Utopia LLC"
!define PRODUCT_WEB_SITE "https://git.eden-emu.dev"
!define PRODUCT_DIR_REGKEY "Software\Microsoft\Windows\CurrentVersion\App Paths\${PRODUCT_NAME}.exe"
!define PRODUCT_UNINST_KEY "Software\Microsoft\Windows\CurrentVersion\Uninstall\${PRODUCT_NAME}"
!define BINARY_SOURCE_DIR "..\bin"
Name "${PRODUCT_NAME}"
OutFile "${PRODUCT_NAME}-Windows-${PRODUCT_VERSION}-${ARCH}-${VARIANT}-installer.exe"
SetCompressor /SOLID lzma
InstallDir "$LOCALAPPDATA\$(^Name)"
ShowInstDetails show
ShowUnInstDetails show
!include "MUI2.nsh"
; Custom page plugin
!include "nsDialogs.nsh"
; MUI Settings
!define MUI_ICON "eden.ico"
!define MUI_UNICON "${NSISDIR}\Contrib\Graphics\Icons\modern-uninstall.ico"
; License page
!insertmacro MUI_PAGE_LICENSE "..\LICENSE.txt"
; Desktop Shortcut page
Page custom desktopShortcutPageCreate desktopShortcutPageLeave
; Directory page
!insertmacro MUI_PAGE_DIRECTORY
; Instfiles page
!insertmacro MUI_PAGE_INSTFILES
; Finish page
!define MUI_FINISHPAGE_RUN "$INSTDIR\eden.exe"
!insertmacro MUI_PAGE_FINISH
; Uninstaller pages
!insertmacro MUI_UNPAGE_INSTFILES
; Variables
Var DesktopShortcutPageDialog
Var DesktopShortcutCheckbox
Var DesktopShortcut
; Language files
!insertmacro MUI_LANGUAGE "English"
!insertmacro MUI_LANGUAGE "SimpChinese"
!insertmacro MUI_LANGUAGE "TradChinese"
!insertmacro MUI_LANGUAGE "Danish"
!insertmacro MUI_LANGUAGE "Dutch"
!insertmacro MUI_LANGUAGE "French"
!insertmacro MUI_LANGUAGE "German"
!insertmacro MUI_LANGUAGE "Hungarian"
!insertmacro MUI_LANGUAGE "Italian"
!insertmacro MUI_LANGUAGE "Japanese"
!insertmacro MUI_LANGUAGE "Korean"
!insertmacro MUI_LANGUAGE "Lithuanian"
!insertmacro MUI_LANGUAGE "Norwegian"
!insertmacro MUI_LANGUAGE "Polish"
!insertmacro MUI_LANGUAGE "PortugueseBR"
!insertmacro MUI_LANGUAGE "Romanian"
!insertmacro MUI_LANGUAGE "Russian"
!insertmacro MUI_LANGUAGE "Spanish"
!insertmacro MUI_LANGUAGE "Swedish"
!insertmacro MUI_LANGUAGE "Turkish"
!insertmacro MUI_LANGUAGE "Vietnamese"
; MUI end ------
Function .onInit
StrCpy $DesktopShortcut 1
!insertmacro MUI_LANGDLL_DISPLAY
FunctionEnd
Function desktopShortcutPageCreate
!insertmacro MUI_HEADER_TEXT "Create Desktop Shortcut" "Would you like to create a desktop shortcut?"
nsDialogs::Create 1018
Pop $DesktopShortcutPageDialog
${If} $DesktopShortcutPageDialog == error
Abort
${EndIf}
${NSD_CreateCheckbox} 0u 0u 100% 12u "Create a desktop shortcut"
Pop $DesktopShortcutCheckbox
${NSD_SetState} $DesktopShortcutCheckbox $DesktopShortcut
nsDialogs::Show
FunctionEnd
Function desktopShortcutPageLeave
${NSD_GetState} $DesktopShortcutCheckbox $DesktopShortcut
FunctionEnd
Section "Base"
ExecWait '"$INSTDIR\uninst.exe" /S _?=$INSTDIR'
SectionIn RO
SetOutPath "$INSTDIR"
; The binplaced build output will be included verbatim.
File /r "${BINARY_SOURCE_DIR}\*"
; Create start menu and desktop shortcuts
CreateShortCut "$SMPROGRAMS\$(^Name).lnk" "$INSTDIR\eden.exe"
${If} $DesktopShortcut == 1
CreateShortCut "$DESKTOP\$(^Name).lnk" "$INSTDIR\eden.exe"
${EndIf}
SectionEnd
!include "FileFunc.nsh"
Section -Post
WriteUninstaller "$INSTDIR\uninst.exe"
WriteRegStr HKCU "${PRODUCT_DIR_REGKEY}" "" "$INSTDIR\eden.exe"
; Write metadata for add/remove programs applet
WriteRegStr HKCU "${PRODUCT_UNINST_KEY}" "DisplayName" "$(^Name)"
WriteRegStr HKCU "${PRODUCT_UNINST_KEY}" "UninstallString" "$INSTDIR\uninst.exe"
WriteRegStr HKCU "${PRODUCT_UNINST_KEY}" "DisplayIcon" "$INSTDIR\eden.exe"
WriteRegStr HKCU "${PRODUCT_UNINST_KEY}" "URLInfoAbout" "${PRODUCT_WEB_SITE}"
WriteRegStr HKCU "${PRODUCT_UNINST_KEY}" "Publisher" "${PRODUCT_PUBLISHER}"
WriteRegStr HKCU "${PRODUCT_UNINST_KEY}" "InstallLocation" "$INSTDIR"
${GetSize} "$INSTDIR" "/S=0K" $0 $1 $2
IntFmt $0 "0x%08X" $0
WriteRegDWORD HKCU "${PRODUCT_UNINST_KEY}" "EstimatedSize" "$0"
WriteRegStr HKCU "Software\Classes\.nsp" "" "$(^Name)"
WriteRegStr HKCU "Software\Classes\.xci" "" "$(^Name)"
WriteRegStr HKCU "Software\Classes\.nro" "" "$(^Name)"
WriteRegStr HKCU "Software\Classes\.kip" "" "$(^Name)"
WriteRegStr HKCU "Software\Classes\$(^Name)\DefaultIcon" "" "$INSTDIR\eden.exe,0"
WriteRegStr HKCU "Software\Classes\$(^Name)\Shell\open\command" "" '"$INSTDIR\eden.exe" %1'
SectionEnd
Section Uninstall
Delete "$DESKTOP\$(^Name).lnk"
Delete "$SMPROGRAMS\$(^Name).lnk"
; Be a bit careful to not delete files a user may have put into the install directory.
Delete "$INSTDIR\eden.exe"
Delete "$INSTDIR\eden-cli.exe"
Delete "$INSTDIR\uninst.exe"
Delete "$INSTDIR\LICENSE.txt"
Delete "$INSTDIR\README.md"
RMDir /r "$INSTDIR\LICENSES"
RMDir "$INSTDIR"
DeleteRegKey HKCU "Software\Classes\.nsp"
DeleteRegKey HKCU "Software\Classes\.xci"
DeleteRegKey HKCU "Software\Classes\.nro"
DeleteRegKey HKCU "Software\Classes\.kip"
DeleteRegKey HKCU "Software\Classes\$(^Name)"
DeleteRegKey HKCU "Software\Classes\discord-1397286652128264252"
DeleteRegKey HKCU "${PRODUCT_UNINST_KEY}"
DeleteRegKey HKCU "${PRODUCT_DIR_REGKEY}"
SetAutoClose true
SectionEnd
@@ -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: 2023 yuzu Emulator Project
@@ -11,6 +11,7 @@ import org.yuzu.yuzu_emu.utils.NativeConfig
enum class StringSetting(override val key: String) : AbstractStringSetting {
DRIVER_PATH("driver_path"),
DEVICE_NAME("device_name"),
PROGRAM_ARGS("program_args"),
WEB_TOKEN("eden_token"),
WEB_USERNAME("eden_username")
@@ -125,6 +125,13 @@ abstract class SettingsItem(
// List of all general
val settingsItems = HashMap<String, SettingsItem>().apply {
put(StringInputSetting(StringSetting.DEVICE_NAME, titleId = R.string.device_name))
put(
StringInputSetting(
StringSetting.PROGRAM_ARGS,
titleId = R.string.program_args,
descriptionId = R.string.program_args_description
)
)
put(
SwitchSetting(
BooleanSetting.RENDERER_USE_SPEED_LIMIT,
@@ -1286,6 +1286,7 @@ class SettingsFragmentPresenter(
add(HeaderSetting(R.string.general))
add(ShortSetting.DEBUG_KNOBS.key)
add(StringSetting.PROGRAM_ARGS.key)
add(HeaderSetting(R.string.gpu_logging_header))
add(BooleanSetting.GPU_LOGGING_ENABLED.key)
@@ -29,8 +29,8 @@
<string name="overlay_auto_hide">触控叠加层自动隐藏</string>
<string name="overlay_auto_hide_description">在指定时间内未进行任何操作后,自动隐藏触控叠加层。</string>
<string name="enable_input_overlay_auto_hide">启用触控叠加层自动隐藏</string>
<string name="hide_overlay_on_controller_input">使用控制器时隐藏触控叠加层</string>
<string name="hide_overlay_on_controller_input_description">在使用实体控制器时自动隐藏触控叠加层,而当控制器断开触控叠加层则会重新显现。</string>
<string name="hide_overlay_on_controller_input">使用控制器控制输入时隐藏触控叠加层</string>
<string name="hide_overlay_on_controller_input_description">在使用实体控制器时自动隐藏触控叠加层,而当控制器断开连接时,触控叠加层则会重新显现。</string>
<string name="invert_confirm_back_controller_buttons">切换“确认/返回”控制器按钮功能</string>
<string name="invert_confirm_back_controller_buttons_description">在与本应用的界面交互时,交换 Android 的“确认”与“返回”按钮的处理方式,以匹配 Switch 和 Xbox 的风格。</string>
@@ -432,7 +432,7 @@
<!-- CPU -->
<string name="fast_cpu_time">CPU 超频</string>
<string name="fast_cpu_time_description">强制模拟的 CPU 以更高的时钟频率运行,从而除某些 FPS 限制。使用 Boost (1700MHz) 可让游戏以 Switch 的最高原生时钟运行,或使用 Fast (2000MHz) 以 2 倍时钟运行。</string>
<string name="fast_cpu_time_description">强制模拟的 CPU 以更高的时钟频率运行,从而除某些帧率限制。使用“升频”(1700MHz)以在 Switch 的最高原生时钟频率运行,或使用“快速”(2000MHz以 2 倍时钟频率运行。</string>
<string name="custom_cpu_ticks">自定义CPU时钟</string>
<string name="custom_cpu_ticks_description">设置自定义的CPU时钟值。更高的值可能提高性能,但也可能导致游戏卡顿。建议范围为77-21000。</string>
<string name="cpu_ticks">时钟</string>
@@ -460,15 +460,15 @@
<string name="advanced">高级</string>
<string name="renderer_accuracy">GPU 模式</string>
<string name="renderer_accuracy_description">控制 GPU 模拟的精确度。大部分游戏在性能或平衡模式下可以正常渲染,但部分游戏需要设置为精确。粒子效果通常只有在精确模式下才能正确显示</string>
<string name="renderer_accuracy_description">控制 GPU 模拟模式。大多数游戏在“快速”或“平衡模式下都能正常渲染,但有些游戏仍需使用“精确”模式。粒子效果通常只有在精确模式下才能正确渲染</string>
<string name="dma_accuracy">DMA 精度</string>
<string name="dma_accuracy_description">控制 DMA 度。安全精度可以修复某些游戏中的问题,但在某些情况下也可能影响性能。如不确定,请保留为“默认”。</string>
<string name="dma_accuracy_description">控制 DMA 的精准度。安全精度可以修复存在于某些游戏中的问题,但在某些情况下也会对性能造成影响。如不确定,请保“默认”。</string>
<string name="anisotropic_filtering">各向异性过滤</string>
<string name="anisotropic_filtering_description">提高斜角的纹理质量</string>
<string name="vram_usage_mode">显存使用模式</string>
<string name="vram_usage_mode_description">控制显存分配策略</string>
<string name="accelerate_astc">ASTC解码方式</string>
<string name="accelerate_astc_description">选择ASTC压缩纹理解码方式:CPU慢速、安全)GPU(快速推荐)CPU异步(无卡顿,可能导致问题)</string>
<string name="accelerate_astc_description">选择渲染时使用的 ASTC 压缩纹理解码方式:CPU缓慢,安全)GPU(快速推荐),或 CPU 异步(无卡顿,可能导致问题)</string>
<string name="sync_memory_operations">同步内存操作</string>
<string name="sync_memory_operations_description">确保计算和内存操作之间的数据一致性。 此选项应能修复某些游戏中的问题,但在某些情况下可能会降低性能。 使用Unreal Engine 4的游戏似乎受影响最大。</string>
@@ -477,11 +477,11 @@
<string name="renderer_force_max_clock">强制最大时钟 (仅限 Adreno)</string>
<string name="renderer_force_max_clock_description">强制 GPU 以最大时钟运行 (温控依然生效)。</string>
<string name="renderer_asynchronous_gpu_emulation">GPU 异步模拟</string>
<string name="renderer_asynchronous_gpu_emulation_description">此技巧可通过异步运行 GPU 模拟来提升性能,但在执行与时序相关的操作时,可能引入图形问题和增加崩溃概率。</string>
<string name="renderer_asynchronous_gpu_emulation_description">此技巧可通过异步运行 GPU 模拟来提升性能,但在执行与时序相关的操作时,可能带来图形显示问题以及增加崩溃概率。</string>
<string name="renderer_async_presentation">异步呈现</string>
<string name="renderer_async_presentation_description">此技巧通过将图形呈现移至独立的 CPU 线程来提升性能,但可能会引入图形显示问题。</string>
<string name="renderer_async_presentation_description">此技巧通过将图形呈现移至独立的 CPU 线程来提升性能,但可能会带来图形显示问题。</string>
<string name="renderer_reactive_flushing">启用反应性刷新</string>
<string name="renderer_reactive_flushing_description">通过牺牲性能来提某些游戏的渲染精度。</string>
<string name="renderer_reactive_flushing_description">通过牺牲性能来提某些游戏的渲染精度。</string>
<string name="enable_buffer_history">启用缓冲区历史</string>
<string name="enable_buffer_history_description">启用对先前缓冲区状态的访问。此选项可在某些游戏中提升渲染质量并保持性能的一致性。</string>
<string name="use_optimized_vertex_buffers">优化顶点缓冲区</string>
@@ -489,10 +489,10 @@
<string name="hacks">Hacks</string>
<string name="fast_gpu_time">GPU 超频频率</string>
<string name="fast_gpu_time">快速 GPU 时间</string>
<string name="fast_gpu_time_description">强制大多数游戏以其最高原生分辨率运行。设置为 256 可获得最佳性能,设置为 512 可获得最佳画面保真度。</string>
<string name="skip_cpu_inner_invalidation">跳过CPU内部无效化</string>
<string name="skip_cpu_inner_invalidation_description">内存更新期间跳过某些CPU端缓存无效化,减少CPU使用率并提高其性能。可能会导致某些游戏出现故障或崩溃。</string>
<string name="skip_cpu_inner_invalidation_description">更新内存时跳过某些 CPU 端的缓存失效操作,从而降低 CPU用率并提性能。可能会某些游戏中引发故障或崩溃。</string>
<string name="antiflicker">防闪烁</string>
<string name="antiflicker_description">强制 GPU 围栏回调等待已提交的 GPU 任务。配合“快速 GPU 模式”一起使用,以避免画面闪烁现象,仅会牺牲少量性能。</string>
<string name="fix_bloom_effects">修复 Bloom 效果</string>
@@ -502,16 +502,16 @@
<string name="rescale_hack">启用旧版缩放处理</string>
<string name="rescale_hack_description">启用通过使用快速缩放路径,来为游戏提供缩放配置处理的传统处理方式</string>
<string name="renderer_asynchronous_shaders">使用异步着色器</string>
<string name="renderer_asynchronous_shaders_description">异步编译着色器。这可能会减少卡顿,但也可能会导致图形错误</string>
<string name="gpu_unswizzle_settings">GPU 还原设置</string>
<string name="gpu_unswizzle_settings_description">配置基于 GPU 的纹理还原参数,或将其完全禁用调整这些设置以平衡性能与纹理加载质量。</string>
<string name="gpu_unswizzle_enable">启用 GPU 还原</string>
<string name="renderer_asynchronous_shaders_description">异步方式编译着色器。采用此方式或可减少卡顿,但也可能引入故障点</string>
<string name="gpu_unswizzle_settings">GPU Unswizzle 设置</string>
<string name="gpu_unswizzle_settings_description">配置基于 GPU 的纹理 unswizzling 参数,或完全禁用该功能。通过调整这些设置,以尝试在性能与纹理加载质量之间取得平衡</string>
<string name="gpu_unswizzle_enable">启用 GPU Unswizzle</string>
<string name="gpu_unswizzle_disabled">禁用</string>
<string name="gpu_unswizzle_texture_size">GPU 还原最大纹理尺寸</string>
<string name="gpu_unswizzle_texture_size_description">设置基于 GPU 的纹理还原的最大尺寸(单位:MiB)。\n虽然 GPU 处理中和大型纹理速度更快,但对于非常小的纹理,CPU 的效率可能更高。\n调整此设置,以便在 GPU 加速 CPU 开销之间找到最佳平衡</string>
<string name="gpu_unswizzle_stream_size">GPU 还原流大小</string>
<string name="gpu_unswizzle_stream_size_description">设置用于 unswizzling 大型纹理时的每帧数据限制。较高的数值可以加纹理的加载速度,但会增加帧延迟。而较低的数值可以降低 GPU 的开销,但可能会导致可见的纹理 闪现。</string>
<string name="gpu_unswizzle_chunk_size">GPU 还原块大小</string>
<string name="gpu_unswizzle_texture_size">GPU Unswizzle 最大纹理尺寸</string>
<string name="gpu_unswizzle_texture_size_description">设置基于 GPU 的纹理 unswizzling 的最大尺寸(MB)。虽然 GPU 处理中和大型纹理速度更快,但对于非常小的纹理,CPU 可能更为高效。通过调节此项设置,以尝试在 GPU 加速 CPU 开销之间找到平衡。</string>
<string name="gpu_unswizzle_stream_size">GPU Unswizzle 流大小</string>
<string name="gpu_unswizzle_stream_size_description">设置用于 unswizzling 大型纹理时的每帧数据限制。较高的数值可以加纹理的加载过程,但会带来更高的帧延迟。而较低的数值可以降低 GPU 的开销,但可能会导致可见的纹理闪现。</string>
<string name="gpu_unswizzle_chunk_size">GPU Unswizzle 块大小</string>
<string name="gpu_unswizzle_chunk_size_description">定义了 3D 纹理每批次处理的深度切片数量。增加此数值可在高性能 GPU 上提升吞吐效率,但在性能较弱的硬件上可能会导致卡顿或驱动超时。</string>
<string name="gpu_unswizzle_default_button">默认</string>
@@ -524,7 +524,7 @@
<string name="vertex_input_dynamic_state">顶点输入动态状态</string>
<string name="vertex_input_dynamic_state_description">启用此功能可实现更灵活的顶点输入处理,可能减少顶点/缓冲区的管线编译时间。</string>
<string name="sample_shading_fraction">采样着色</string>
<string name="sample_shading_fraction_description">允许片段着色器在多采样片段中每个样本执行一次,而不是每个片段执行一次。以提高性能为代价改善图形质量</string>
<string name="sample_shading_fraction_description">允许片段着色器在多采样片段中每个采样点执行一次操作,而非对每个片段执行一次。在提升图形显示质量的同时,会牺牲一部分性能</string>
<string name="display">显示</string>
@@ -548,7 +548,7 @@
<string name="renderer_debug_description">将图形 API 设置为较慢的调试模式。</string>
<string name="patch_old_qcom_drivers">BCn 纹理补丁</string>
<string name="patch_old_qcom_drivers_description">在 Adreno GPU 上覆盖自动 BCn 纹理格式检测。通常根据 Android 版本自动检测(在 API 28 及以上启用)。</string>
<string name="fastmem">Fastmem 内存访问</string>
<string name="fastmem">Fastmem</string>
<string name="log">日志记录</string>
<string name="flush_by_line">按行刷新调试日志</string>
@@ -615,7 +615,7 @@
<string name="unused">未使用</string>
<string name="input_mapping_filter">输入映射过滤器</string>
<string name="input_mapping_filter_description">选择一个设备过滤输入映射</string>
<string name="auto_map">控制器自动映射</string>
<string name="auto_map">自动映射控制器键位</string>
<string name="auto_map_description">选择一个设备以尝试自动映射</string>
<string name="attempted_auto_map">尝试为 %1$s 自动映射</string>
<string name="controller_type">控制器类型</string>
@@ -731,10 +731,10 @@
<string name="preferences_audio">声音</string>
<string name="preferences_audio_description">输出引擎及音量</string>
<string name="preferences_controls">控制</string>
<string name="preferences_controls_description">使用控制器来映射输入</string>
<string name="preferences_controls_description">映射控制器键位输入</string>
<string name="preferences_player">玩家 %d</string>
<string name="preferences_debug">调试</string>
<string name="preferences_debug_description">CPU/GPU 调试、图形 API 及 fastmem 内存访问</string>
<string name="preferences_debug_description">CPU/GPU 调试、图形 API 及 fastmem</string>
<string name="preferences_custom_paths">自定义路径</string>
<string name="preferences_custom_paths_description">存档目录</string>
@@ -878,15 +878,15 @@
<string name="emulation_rel_stick_center">相对摇杆中心</string>
<string name="emulation_dpad_slide">十字方向键滑动</string>
<string name="emulation_haptics">触觉反馈</string>
<string name="emulation_show_overlay">显示控制器</string>
<string name="emulation_hide_overlay">隐藏控制器</string>
<string name="emulation_show_overlay">显示触控叠加层</string>
<string name="emulation_hide_overlay">隐藏触控叠加层</string>
<string name="emulation_toggle_all">全部切换</string>
<string name="emulation_control_adjust">调整触控叠加层</string>
<string name="emulation_control_scale">缩放</string>
<string name="emulation_control_opacity">不透明度</string>
<string name="emulation_touch_overlay_reset">重置触控叠加层</string>
<string name="emulation_touch_overlay_edit">编辑触控叠加层</string>
<string name="emulation_snap_to_grid">截图到网格</string>
<string name="emulation_snap_to_grid">对齐到网格</string>
<string name="emulation_pause">暂停模拟</string>
<string name="emulation_unpause">继续模拟</string>
<string name="emulation_input_overlay">触控叠加层选项</string>
@@ -982,7 +982,7 @@
<string name="renderer_none"></string>
<!-- Renderer Accuracy -->
<string name="renderer_accuracy_low">性能</string>
<string name="renderer_accuracy_low">快速</string>
<string name="renderer_accuracy_medium">平衡</string>
<string name="renderer_accuracy_high">精确</string>
@@ -434,6 +434,9 @@
<string name="cpu_accuracy">CPU accuracy</string>
<string name="value_with_units">%1$s%2$s</string>
<string name="program_args">Homebrew Args</string>
<string name="program_args_description">Command-line arguments passed to homebrew at launch (e.g. -noglsl).</string>
<!-- System settings strings -->
<string name="device_name">Device name</string>
<string name="use_docked_mode">Docked Mode</string>
+20 -69
View File
@@ -409,107 +409,62 @@ bool RenameDir(const fs::path& old_path, const fs::path& new_path) {
return true;
}
void IterateDirEntries(const std::filesystem::path& path, const DirEntryCallable& callback,
DirEntryFilter filter) {
void IterateDirEntries(const std::filesystem::path& path, const DirEntryCallable& callback, DirEntryFilter filter) {
if (!ValidatePath(path)) {
LOG_ERROR(Common_Filesystem, "Input path is not valid, path={}", PathToUTF8String(path));
return;
}
if (!Exists(path)) {
LOG_ERROR(Common_Filesystem, "Filesystem object at path={} does not exist",
PathToUTF8String(path));
LOG_ERROR(Common_Filesystem, "Filesystem object at path={} does not exist", PathToUTF8String(path));
return;
}
if (!IsDir(path)) {
LOG_ERROR(Common_Filesystem, "Filesystem object at path={} is not a directory",
PathToUTF8String(path));
LOG_ERROR(Common_Filesystem, "Filesystem object at path={} is not a directory", PathToUTF8String(path));
return;
}
bool callback_error = false;
std::error_code ec;
for (const auto& entry : fs::directory_iterator(path, ec)) {
if (ec) {
break;
}
if (True(filter & DirEntryFilter::File) &&
entry.status().type() == fs::file_type::regular) {
bool callback_error = false;
for (auto const& entry : fs::directory_iterator(path, ec)) {
if ((True(filter & DirEntryFilter::File) && entry.status().type() == fs::file_type::regular)
|| (True(filter & DirEntryFilter::Directory) && entry.status().type() == fs::file_type::directory)) {
if (!callback(entry)) {
callback_error = true;
break;
}
}
if (True(filter & DirEntryFilter::Directory) &&
entry.status().type() == fs::file_type::directory) {
if (!callback(entry)) {
callback_error = true;
break;
}
}
}
if (callback_error || ec) {
LOG_ERROR(Common_Filesystem,
"Failed to visit all the directory entries of path={}, ec_message={}",
PathToUTF8String(path), ec.message());
return;
LOG_ERROR(Common_Filesystem, "Failed to visit all the directory entries of path={}, ec_message={}, callback_error={}", PathToUTF8String(path), ec.message(), callback_error);
} else {
LOG_DEBUG(Common_Filesystem, "Visited all the directory entries of path={}", PathToUTF8String(path));
}
LOG_DEBUG(Common_Filesystem, "Successfully visited all the directory entries of path={}",
PathToUTF8String(path));
}
void IterateDirEntriesRecursively(const std::filesystem::path& path,
const DirEntryCallable& callback, DirEntryFilter filter) {
void IterateDirEntriesRecursively(const std::filesystem::path& path, const DirEntryCallable& callback, DirEntryFilter filter) {
if (!ValidatePath(path)) {
LOG_ERROR(Common_Filesystem, "Input path is not valid, path={}", PathToUTF8String(path));
return;
}
if (!Exists(path)) {
LOG_ERROR(Common_Filesystem, "Filesystem object at path={} does not exist",
PathToUTF8String(path));
LOG_ERROR(Common_Filesystem, "Filesystem object at path={} does not exist", PathToUTF8String(path));
return;
}
if (!IsDir(path)) {
LOG_ERROR(Common_Filesystem, "Filesystem object at path={} is not a directory",
PathToUTF8String(path));
LOG_ERROR(Common_Filesystem, "Filesystem object at path={} is not a directory", PathToUTF8String(path));
return;
}
bool callback_error = false;
std::error_code ec;
// TODO (Morph): Replace this with recursive_directory_iterator once it's fixed in MSVC.
std::error_code ec;
bool callback_error = false;
for (const auto& entry : fs::directory_iterator(path, ec)) {
if (ec) {
break;
}
if (True(filter & DirEntryFilter::File) &&
entry.status().type() == fs::file_type::regular) {
if ((True(filter & DirEntryFilter::File) && entry.status().type() == fs::file_type::regular)
|| (True(filter & DirEntryFilter::Directory) && entry.status().type() == fs::file_type::directory)) {
if (!callback(entry)) {
callback_error = true;
break;
}
}
if (True(filter & DirEntryFilter::Directory) &&
entry.status().type() == fs::file_type::directory) {
if (!callback(entry)) {
callback_error = true;
break;
}
}
// TODO (Morph): Remove this when MSVC fixes recursive_directory_iterator.
// recursive_directory_iterator throws an exception despite passing in a std::error_code.
if (entry.status().type() == fs::file_type::directory) {
@@ -518,14 +473,10 @@ void IterateDirEntriesRecursively(const std::filesystem::path& path,
}
if (callback_error || ec) {
LOG_ERROR(Common_Filesystem,
"Failed to visit all the directory entries of path={}, ec_message={}",
PathToUTF8String(path), ec.message());
return;
LOG_ERROR(Common_Filesystem, "Failed to visit all the directory entries of path={}, ec_message={}, callback_error={}", PathToUTF8String(path), ec.message(), callback_error);
} else {
LOG_DEBUG(Common_Filesystem, "Visited all the directory entries of path={}", PathToUTF8String(path));
}
LOG_DEBUG(Common_Filesystem, "Successfully visited all the directory entries of path={}",
PathToUTF8String(path));
}
// Generic Filesystem Operations
+7 -5
View File
@@ -90,7 +90,7 @@ public:
void CreateEdenPaths() {
std::for_each(eden_paths.begin(), eden_paths.end(), [](auto &path) {
void(FS::CreateDir(path.second));
void(FS::CreateDirs(path.second));
});
}
@@ -149,10 +149,9 @@ public:
LEGACY_PATH(Suyu, SUYU)
#undef LEGACY_PATH
#endif
// data
GenerateEdenPath(EdenPath::EdenDir, eden_path);
GenerateEdenPath(EdenPath::AmiiboDir, eden_path / AMIIBO_DIR);
GenerateEdenPath(EdenPath::CacheDir, eden_path_cache);
GenerateEdenPath(EdenPath::ConfigDir, eden_path_config);
GenerateEdenPath(EdenPath::CrashDumpsDir, eden_path / CRASH_DUMPS_DIR);
GenerateEdenPath(EdenPath::DumpDir, eden_path / DUMP_DIR);
GenerateEdenPath(EdenPath::KeysDir, eden_path / KEYS_DIR);
@@ -163,10 +162,13 @@ public:
GenerateEdenPath(EdenPath::SaveDir, eden_path / NAND_DIR);
GenerateEdenPath(EdenPath::ScreenshotsDir, eden_path / SCREENSHOTS_DIR);
GenerateEdenPath(EdenPath::SDMCDir, eden_path / SDMC_DIR);
GenerateEdenPath(EdenPath::ShaderDir, eden_path / SHADER_DIR);
GenerateEdenPath(EdenPath::TASDir, eden_path / TAS_DIR);
GenerateEdenPath(EdenPath::IconsDir, eden_path / ICONS_DIR);
// config
GenerateEdenPath(EdenPath::ConfigDir, eden_path_config);
// cache
GenerateEdenPath(EdenPath::CacheDir, eden_path_cache);
GenerateEdenPath(EdenPath::ShaderDir, eden_path_cache / SHADER_DIR);
#ifdef _WIN32
GenerateLegacyPath(EmuPath::RyujinxDir, GetAppDataRoamingDirectory() / RYUJINX_DIR);
#else
+110 -40
View File
@@ -22,6 +22,7 @@
#include <sys/mman.h>
#include <unistd.h>
#include "common/scope_exit.h"
#include "common/literals.h"
#if defined(__linux__)
#include <sys/random.h>
@@ -32,6 +33,8 @@
#include <mach/mach.h>
#elif defined(__FreeBSD__)
#include <sys/shm.h>
#elif defined(__OPENORBIS__)
#include <orbis/libkernel.h>
#endif
// FreeBSD
@@ -122,54 +125,55 @@ static void GetFuncAddress(Common::DynamicLibrary& dll, const char* name, T& pfn
class HostMemory::Impl {
public:
explicit Impl(size_t backing_size_, size_t virtual_size_)
: backing_size{backing_size_}, virtual_size{virtual_size_}, process{GetCurrentProcess()},
kernelbase_dll("Kernelbase") {
: backing_size{backing_size_}
, virtual_size{virtual_size_}
, process{GetCurrentProcess()}
, kernelbase_dll("Kernelbase")
{}
bool Init() {
if (!kernelbase_dll.IsOpen()) {
LOG_CRITICAL(HW_Memory, "Failed to load Kernelbase.dll");
throw std::bad_alloc{};
return false;
}
GetFuncAddress(kernelbase_dll, "CreateFileMapping2", pfn_CreateFileMapping2);
GetFuncAddress(kernelbase_dll, "VirtualAlloc2", pfn_VirtualAlloc2);
GetFuncAddress(kernelbase_dll, "MapViewOfFile3", pfn_MapViewOfFile3);
GetFuncAddress(kernelbase_dll, "UnmapViewOfFile2", pfn_UnmapViewOfFile2);
if (!pfn_CreateFileMapping2 || !pfn_VirtualAlloc2 || !pfn_MapViewOfFile3 || !pfn_UnmapViewOfFile2) {
LOG_CRITICAL(HW_Memory, "Failed to find functions for virtual allocs");
return false;
}
// Allocate backing file map
backing_handle =
pfn_CreateFileMapping2(INVALID_HANDLE_VALUE, nullptr, FILE_MAP_WRITE | FILE_MAP_READ,
PAGE_READWRITE, SEC_COMMIT, backing_size, nullptr, nullptr, 0);
backing_handle = pfn_CreateFileMapping2(INVALID_HANDLE_VALUE, nullptr, FILE_MAP_WRITE | FILE_MAP_READ, PAGE_READWRITE, SEC_COMMIT, backing_size, nullptr, nullptr, 0);
if (!backing_handle) {
LOG_CRITICAL(HW_Memory, "Failed to allocate {} MiB of backing memory",
backing_size >> 20);
throw std::bad_alloc{};
LOG_CRITICAL(HW_Memory, "Failed to allocate {} MiB of backing memory", backing_size >> 20);
return false;
}
// Allocate a virtual memory for the backing file map as placeholder
backing_base = static_cast<u8*>(pfn_VirtualAlloc2(process, nullptr, backing_size,
MEM_RESERVE | MEM_RESERVE_PLACEHOLDER,
PAGE_NOACCESS, nullptr, 0));
backing_base = static_cast<u8*>(pfn_VirtualAlloc2(process, nullptr, backing_size, MEM_RESERVE | MEM_RESERVE_PLACEHOLDER, PAGE_NOACCESS, nullptr, 0));
if (!backing_base) {
Release();
LOG_CRITICAL(HW_Memory, "Failed to reserve {} MiB of virtual memory",
backing_size >> 20);
throw std::bad_alloc{};
LOG_CRITICAL(HW_Memory, "Failed to reserve {} MiB of virtual memory", backing_size >> 20);
return false;
}
// Map backing placeholder
void* const ret = pfn_MapViewOfFile3(backing_handle, process, backing_base, 0, backing_size,
MEM_REPLACE_PLACEHOLDER, PAGE_READWRITE, nullptr, 0);
void* const ret = pfn_MapViewOfFile3(backing_handle, process, backing_base, 0, backing_size, MEM_REPLACE_PLACEHOLDER, PAGE_READWRITE, nullptr, 0);
if (ret != backing_base) {
Release();
LOG_CRITICAL(HW_Memory, "Failed to map {} MiB of virtual memory", backing_size >> 20);
throw std::bad_alloc{};
return false;
}
// Allocate virtual address placeholder
virtual_base = static_cast<u8*>(pfn_VirtualAlloc2(process, nullptr, virtual_size,
MEM_RESERVE | MEM_RESERVE_PLACEHOLDER,
PAGE_NOACCESS, nullptr, 0));
virtual_base = static_cast<u8*>(pfn_VirtualAlloc2(process, nullptr, virtual_size, MEM_RESERVE | MEM_RESERVE_PLACEHOLDER, PAGE_NOACCESS, nullptr, 0));
if (!virtual_base) {
Release();
LOG_CRITICAL(HW_Memory, "Failed to reserve {} GiB of virtual memory",
virtual_size >> 30);
throw std::bad_alloc{};
LOG_CRITICAL(HW_Memory, "Failed to reserve {} GiB of virtual memory", virtual_size >> 30);
return false;
}
return true;
}
~Impl() {
@@ -391,6 +395,9 @@ private:
ankerl::unordered_dense::map<size_t, size_t> placeholder_host_pointers; ///< Placeholder backing offset
};
#elif defined(__OPENORBIS__) || defined(__managarm__)
// None of the luxuries of POSIX, all of the suffering
// For managarm: see https://github.com/managarm/managarm/issues/1370
#else // ^^^ Windows ^^^ vvv POSIX vvv
#ifdef ARCHITECTURE_arm64
@@ -496,18 +503,44 @@ static int shm_open_anon(int flags, mode_t mode) {
class HostMemory::Impl {
public:
explicit Impl(size_t backing_size_, size_t virtual_size_)
: backing_size{backing_size_}, virtual_size{virtual_size_} {
: backing_size{backing_size_}
, virtual_size{virtual_size_}
{}
bool Init() {
long page_size = sysconf(_SC_PAGESIZE);
ASSERT_MSG(page_size == 0x1000, "page size {:#x} is incompatible with 4K paging",
page_size);
ASSERT_MSG(page_size == 0x1000, "page size {:#x} is incompatible with 4K paging", page_size);
// Backing memory initialization
#if defined(__sun__) || defined(__HAIKU__) || defined(__NetBSD__) || defined(__DragonFly__)
fd = shm_open_anon(O_RDWR | O_CREAT | O_EXCL | O_NOFOLLOW, 0600);
#elif defined(__OpenBSD__)
fd = shm_open_anon(O_RDWR | O_CREAT | O_EXCL | O_NOFOLLOW, 0600);
#elif defined(__FreeBSD__)
fd = shm_open(SHM_ANON, O_RDWR, 0600);
#elif defined(__APPLE__)
int n_page_sizes = getpagesizes(nullptr, 0);
if (n_page_sizes > 0) {
std::vector<size_t> page_sizes(n_page_sizes);
if (getpagesizes(page_sizes.data(), n_page_sizes) > 0) {
size_t max_size = page_sizes[0];
size_t max_index = 0;
for (size_t i = 0; i < page_sizes.size(); ++i) {
using namespace Common::Literals;
if (page_sizes[i] <= 4_GiB) {
max_size = (std::max)(max_size, page_sizes[i]);
max_index = i;
}
}
LOG_WARNING(Common_Memory, "using largepage of size {} #{}", max_size, max_index);
// Do not use SHM_LARGEPAGE_ALLOC_HARD, yknow what will happen when you do?
// the entire Eden process will hang for eternity! that's what will happen
// Want to fuck around and find out? Go ahead, I tempt you change the "default" to "hard"
fd = shm_create_largepage(SHM_ANON, O_RDWR, max_index, SHM_LARGEPAGE_ALLOC_DEFAULT, 0600);
}
}
if (fd < 0) {
LOG_WARNING(Common_Memory, "unable to force largepage: {}", strerror(errno));
fd = shm_open(SHM_ANON, O_RDWR, 0600);
}
#elif defined(__APPLE__) || defined(__managarm__)
// macOS doesn't have memfd_create, use anonymous temporary file
char template_path[] = "/tmp/eden_mem_XXXXXX";
fd = mkstemp(template_path);
@@ -538,17 +571,29 @@ public:
close(fd);
}
} else {
#ifdef __FreeBSD__
// Prevent dirty tracking of memfd
backing_base = static_cast<u8*>(mmap(nullptr, backing_size, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_NOSYNC, fd, 0));
#else
backing_base = static_cast<u8*>(mmap(nullptr, backing_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0));
#endif
}
if (backing_base == MAP_FAILED) {
LOG_CRITICAL(HW_Memory, "mmap failed: {}", strerror(errno));
return false;
}
ASSERT_MSG(backing_base != MAP_FAILED, "mmap failed: {}", strerror(errno));
// Virtual memory initialization
virtual_base = virtual_map_base = static_cast<u8*>(ChooseVirtualBase(virtual_size));
ASSERT_MSG(virtual_base != MAP_FAILED, "mmap failed: {}", strerror(errno));
if (virtual_base == MAP_FAILED) {
LOG_CRITICAL(HW_Memory, "mmap failed: {}", strerror(errno));
return false;
}
#if defined(__linux__)
madvise(virtual_base, virtual_size, MADV_HUGEPAGE);
#endif
free_manager.SetAddressSpace(virtual_base, virtual_size);
return true;
}
~Impl() {
@@ -669,17 +714,35 @@ private:
#endif // ^^^ POSIX ^^^
HostMemory::HostMemory(size_t backing_size_, size_t virtual_size_) : backing_size(backing_size_), virtual_size(virtual_size_) {
HostMemory::HostMemory(size_t backing_size_, size_t virtual_size_)
: backing_size(backing_size_)
, virtual_size(virtual_size_)
{
#if defined(__OPENORBIS__) || defined(__managarm__)
LOG_WARNING(HW_Memory, "Platform doesn't support fastmem");
fallback_buffer.emplace(backing_size);
backing_base = fallback_buffer->data();
virtual_base = nullptr;
#else
// Try to allocate a fastmem arena.
// The implementation will fail with std::bad_alloc on errors.
impl = std::make_unique<HostMemory::Impl>(AlignUp(backing_size, PageAlignment), AlignUp(virtual_size, PageAlignment) + HugePageSize);
backing_base = impl->backing_base;
virtual_base = impl->virtual_base;
if (virtual_base) {
// Ensure the virtual base is aligned to the L2 block size.
virtual_base = reinterpret_cast<u8*>(Common::AlignUp(uintptr_t(virtual_base), HugePageSize));
virtual_base_offset = virtual_base - impl->virtual_base;
if (impl->Init()) {
backing_base = impl->backing_base;
virtual_base = impl->virtual_base;
if (virtual_base) {
// Ensure the virtual base is aligned to the L2 block size.
virtual_base = reinterpret_cast<u8*>(Common::AlignUp(uintptr_t(virtual_base), HugePageSize));
virtual_base_offset = virtual_base - impl->virtual_base;
}
} else {
impl.reset();
LOG_WARNING(HW_Memory, "Platform can support fastmem, but can't create it");
fallback_buffer.emplace(backing_size);
backing_base = fallback_buffer->data();
virtual_base = nullptr;
}
#endif
}
HostMemory::~HostMemory() = default;
@@ -688,8 +751,8 @@ HostMemory::HostMemory(HostMemory&&) noexcept = default;
HostMemory& HostMemory::operator=(HostMemory&&) noexcept = default;
void HostMemory::Map(size_t virtual_offset, size_t host_offset, size_t length,
MemoryPermission perms, bool separate_heap) {
void HostMemory::Map(size_t virtual_offset, size_t host_offset, size_t length, MemoryPermission perms, bool separate_heap) {
#if !(defined(__OPENORBIS__) || defined(__managarm__))
ASSERT(virtual_offset % PageAlignment == 0);
ASSERT(host_offset % PageAlignment == 0);
ASSERT(length % PageAlignment == 0);
@@ -699,9 +762,11 @@ void HostMemory::Map(size_t virtual_offset, size_t host_offset, size_t length,
return;
}
impl->Map(virtual_offset + virtual_base_offset, host_offset, length, perms);
#endif
}
void HostMemory::Unmap(size_t virtual_offset, size_t length, bool separate_heap) {
#if !(defined(__OPENORBIS__) || defined(__managarm__))
ASSERT(virtual_offset % PageAlignment == 0);
ASSERT(length % PageAlignment == 0);
ASSERT(virtual_offset + length <= virtual_size);
@@ -709,9 +774,11 @@ void HostMemory::Unmap(size_t virtual_offset, size_t length, bool separate_heap)
return;
}
impl->Unmap(virtual_offset + virtual_base_offset, length);
#endif
}
void HostMemory::Protect(size_t virtual_offset, size_t length, MemoryPermission perm) {
#if !(defined(__OPENORBIS__) || defined(__managarm__))
ASSERT(virtual_offset % PageAlignment == 0);
ASSERT(length % PageAlignment == 0);
ASSERT(virtual_offset + length <= virtual_size);
@@ -722,6 +789,7 @@ void HostMemory::Protect(size_t virtual_offset, size_t length, MemoryPermission
const bool write = True(perm & MemoryPermission::Write);
const bool execute = True(perm & MemoryPermission::Execute);
impl->Protect(virtual_offset + virtual_base_offset, length, read, write, execute);
#endif
}
void HostMemory::ClearBackingRegion(size_t physical_offset, size_t length, u32 fill_value) {
@@ -729,10 +797,12 @@ void HostMemory::ClearBackingRegion(size_t physical_offset, size_t length, u32 f
}
void HostMemory::EnableDirectMappedAddress() {
#if !(defined(__OPENORBIS__) || defined(__managarm__))
if (impl) {
impl->EnableDirectMappedAddress();
virtual_size += reinterpret_cast<uintptr_t>(virtual_base);
}
#endif
}
} // namespace Common
+6 -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 2019 yuzu Emulator Project
@@ -7,6 +7,7 @@
#pragma once
#include <memory>
#include <optional>
#include "common/common_funcs.h"
#include "common/common_types.h"
#include "common/virtual_buffer.h"
@@ -76,12 +77,16 @@ private:
size_t backing_size{};
size_t virtual_size{};
#if !(defined(__OPENORBIS__) || defined(__managarm__))
// Low level handler for the platform dependent memory routines
class Impl;
std::unique_ptr<Impl> impl;
#endif
u8* backing_base{};
u8* virtual_base{};
size_t virtual_base_offset{};
// Windows requires it for kernels whom lack proper support for some functions!
std::optional<Common::VirtualBuffer<u8>> fallback_buffer;
};
} // namespace Common
+11 -5
View File
@@ -336,7 +336,7 @@ struct Values {
RendererBackend::Vulkan,
#endif
"backend", Category::Renderer};
SwitchableSetting<int> vulkan_device{linkage, 0, "vulkan_device", Category::Renderer, Specialization::RuntimeList};
SwitchableSetting<u32> vulkan_device{linkage, 0, "vulkan_device", Category::Renderer, Specialization::RuntimeList};
// Graphics Settings
ResolutionScalingInfo resolution_info{};
@@ -661,8 +661,8 @@ struct Values {
false, true, &custom_rtc_enabled};
SwitchableSetting<s64, true> custom_rtc_offset{linkage,
0,
(std::numeric_limits<int>::min)(),
(std::numeric_limits<int>::max)(),
(std::numeric_limits<s64>::min)(),
(std::numeric_limits<s64>::max)(),
"custom_rtc_offset",
Category::System,
Specialization::Countable,
@@ -751,7 +751,7 @@ struct Values {
Setting<std::string> touch_device{linkage, "min_x:100,min_y:50,max_x:1800,max_y:850",
"touch_device", Category::Controls};
Setting<int> touch_from_button_map_index{linkage, 0, "touch_from_button_map",
Setting<u32> touch_from_button_map_index{linkage, 0, "touch_from_button_map",
Category::Controls};
std::vector<TouchFromButtonMap> touch_from_button_maps;
@@ -779,7 +779,13 @@ struct Values {
bool record_frame_times;
Setting<bool> use_gdbstub{linkage, false, "use_gdbstub", Category::Debugging};
Setting<u16> gdbstub_port{linkage, 6543, "gdbstub_port", Category::Debugging};
Setting<std::string> program_args{linkage, std::string(), "program_args", Category::Debugging};
SwitchableSetting<std::string> program_args{linkage,
std::string(),
"program_args",
Category::System,
Specialization::Default,
true, // save_ — persist in config file
false}; // runtime_modifiable_ — startup-only
Setting<bool> dump_exefs{linkage, false, "dump_exefs", Category::Debugging};
Setting<bool> dump_nso{linkage, false, "dump_nso", Category::Debugging};
Setting<bool> dump_shaders{
+2 -2
View File
@@ -145,8 +145,8 @@ ENUM(ConfirmStop, Ask_Always, Ask_Based_On_Game, Ask_Never);
ENUM(FullscreenMode, Borderless, Exclusive);
ENUM(NvdecEmulation, Off, Cpu, Gpu);
ENUM(ResolutionSetup, Res1_4X, Res1_2X, Res3_4X, Res1X, Res5_4X, Res3_2X, Res2X, Res3X, Res4X, Res5X, Res6X, Res7X, Res8X);
ENUM(ScalingFilter, NearestNeighbor, Bilinear, Bicubic, Gaussian, Lanczos, ScaleForce, Fsr, Area, ZeroTangent, BSpline, Mitchell, Spline1, Mmpx, Sgsr, SgsrEdge, MaxEnum);
ENUM(AntiAliasing, None, Fxaa, Smaa, MaxEnum);
ENUM(ScalingFilter, NearestNeighbor, Bilinear, Bicubic, Gaussian, Lanczos, ScaleForce, Fsr, Area, ZeroTangent, BSpline, Mitchell, Spline1, Mmpx, Sgsr, SgsrEdge);
ENUM(AntiAliasing, None, Fxaa, Smaa);
ENUM(AspectRatio, R16_9, R4_3, R21_9, R16_10, Stretch);
ENUM(ConsoleMode, Handheld, Docked);
ENUM(AppletMode, HLE, LLE);
+20 -3
View File
@@ -11,6 +11,7 @@
#include <optional>
#include <stdexcept>
#include <string>
#include <type_traits>
#include <typeindex>
#include <typeinfo>
#include <fmt/core.h>
@@ -101,7 +102,15 @@ public:
* @param val The desired value
*/
virtual void SetValue(const Type& val) {
Type temp{ranged ? std::clamp(val, minimum, maximum) : val};
// Enums have a maximal range which they're allowed
Type temp{};
if constexpr (std::is_enum_v<Type>) {
auto const r_min = std::underlying_type_t<Type>(0);
auto const r_max = std::underlying_type_t<Type>(EnumMetadata<Type>::GetLast());
temp = Type(std::clamp(std::underlying_type_t<Type>(val), r_min, r_max));
} else {
temp = ranged ? std::clamp(val, this->minimum, this->maximum) : val;
}
std::swap(value, temp);
}
@@ -129,7 +138,7 @@ protected:
} else if constexpr (std::is_floating_point_v<Type>) {
return fmt::format("{:f}", value_);
} else if constexpr (std::is_enum_v<Type>) {
return std::to_string(u32(value_));
return std::to_string(std::underlying_type_t<Type>(value_));
} else {
return std::to_string(value_);
}
@@ -371,7 +380,15 @@ public:
* @param val The new value
*/
void SetValue(const Type& val) override final {
Type temp{ranged ? std::clamp(val, this->minimum, this->maximum) : val};
// Enums have a maximal range which they're allowed
Type temp{};
if constexpr (std::is_enum_v<Type>) {
auto const r_min = std::underlying_type_t<Type>(0);
auto const r_max = std::underlying_type_t<Type>(EnumMetadata<Type>::GetLast());
temp = Type(std::clamp(std::underlying_type_t<Type>(val), r_min, r_max));
} else {
temp = ranged ? std::clamp(val, this->minimum, this->maximum) : val;
}
if (use_global) {
std::swap(this->value, temp);
} else {
+4
View File
@@ -1121,6 +1121,10 @@ add_library(core STATIC
hle/service/vi/vi_types.h
hle/service/vi/vsync_manager.cpp
hle/service/vi/vsync_manager.h
hle/service/gpio/gpio.cpp
hle/service/gpio/gpio.h
hle/service/i2c/i2c.cpp
hle/service/i2c/i2c.h
internal_network/emu_net_state.cpp
internal_network/emu_net_state.h
internal_network/network.cpp
+25 -6
View File
@@ -211,6 +211,9 @@ Result KProcess::Initialize(const Svc::CreateProcessParameter& params, KResource
m_version = params.version;
m_program_id = params.program_id;
m_code_address = params.code_address;
m_arg_pointer = 0;
m_arg_return_address = 0;
m_main_thread_handle_addr = 0;
m_code_size = params.code_num_pages * PageSize;
m_is_application = True(params.flags & Svc::CreateProcessFlag::IsApplication);
@@ -995,9 +998,27 @@ Result KProcess::Run(s32 priority, size_t stack_size) {
Handle thread_handle;
R_TRY(m_handle_table.Add(std::addressof(thread_handle), main_thread));
// Set the thread arguments.
main_thread->GetContext().r[0] = 0;
main_thread->GetContext().r[1] = thread_handle;
// Set the thread arguments. Two distinct entry conventions:
// * Kernel/NSO entry (no homebrew ABI): x0 = 0, x1 = thread_handle
// * Homebrew/NRO ABI (loader set arg ptr): x0 = ConfigEntry ptr, x1 = -1ULL
// libnx's switch_crt0.s tests `x0==0 || x1==0xFFFFFFFFFFFFFFFF` to take
// its normal init path; any other combination is interpreted as a user
// exception handler entry.
if (GetInteger(m_arg_pointer) != 0) {
main_thread->GetContext().r[0] = GetInteger(m_arg_pointer);
main_thread->GetContext().r[1] = UINT64_MAX;
main_thread->GetContext().lr = GetInteger(m_arg_return_address);
// Patch the MainThreadHandle entry in the ConfigEntry table now that
// the actual handle exists. libnx stores this verbatim and uses it
// for thread-control SVCs later; a pseudo-handle wouldn't survive
// svcCloseHandle on exit.
if (GetInteger(m_main_thread_handle_addr) != 0) {
this->GetMemory().Write32(m_main_thread_handle_addr, thread_handle);
}
} else {
main_thread->GetContext().r[0] = 0;
main_thread->GetContext().r[1] = thread_handle;
}
// Pass the thread handle to the thread local region.
this->GetMemory().Write32(GetInteger(main_thread->GetTlsAddress()) + 0x110, thread_handle);
@@ -1174,8 +1195,7 @@ KProcess::KProcess(KernelCore& kernel)
KProcess::~KProcess() = default;
Result KProcess::LoadFromMetadata(const FileSys::ProgramMetadata& metadata, std::size_t code_size,
KProcessAddress aslr_space_start, size_t aslr_space_offset, bool is_hbl) {
Result KProcess::LoadFromMetadata(const FileSys::ProgramMetadata& metadata, std::size_t code_size, KProcessAddress aslr_space_start, size_t aslr_space_offset) {
// Create a resource limit for the process.
const auto pool = static_cast<KMemoryManager::Pool>(metadata.GetPoolPartition());
const auto physical_memory_size = m_kernel.MemoryManager().GetSize(pool);
@@ -1247,7 +1267,6 @@ Result KProcess::LoadFromMetadata(const FileSys::ProgramMetadata& metadata, std:
aslr_space_start));
// Assign remaining properties.
m_is_hbl = is_hbl;
m_ideal_core_id = metadata.GetMainThreadCore();
// Set up emulation context.
+14 -7
View File
@@ -84,6 +84,9 @@ private:
Core::Memory::Memory m_memory;
KCapabilities m_capabilities{};
KProcessAddress m_code_address{};
KProcessAddress m_arg_pointer{};
KProcessAddress m_arg_return_address{};
KProcessAddress m_main_thread_handle_addr{};
KHandleTable m_handle_table;
KProcessAddress m_plr_address{};
ThreadList m_thread_list{};
@@ -133,7 +136,6 @@ private:
bool m_is_initialized : 1 = false;
bool m_is_application : 1 = false;
bool m_is_default_application_system_resource : 1 = false;
bool m_is_hbl : 1 = false;
bool m_is_suspended : 1 = false;
bool m_is_immortal : 1 = false;
bool m_is_handle_table_initialized : 1 = false;
@@ -220,6 +222,16 @@ public:
return m_code_address;
}
void SetArgPointer(KProcessAddress addr) {
m_arg_pointer = addr;
}
void SetArgReturnAddress(KProcessAddress addr) {
m_arg_return_address = addr;
}
void SetMainThreadHandleAddr(KProcessAddress addr) {
m_main_thread_handle_addr = addr;
}
size_t GetMainStackSize() const {
return m_main_thread_stack_size;
}
@@ -277,10 +289,6 @@ public:
return m_capabilities.CanForceDebug();
}
bool IsHbl() const {
return m_is_hbl;
}
u32 GetAllocateOption() const {
return m_page_table.GetAllocateOption();
}
@@ -514,8 +522,7 @@ public:
}
public:
Result LoadFromMetadata(const FileSys::ProgramMetadata& metadata, std::size_t code_size,
KProcessAddress aslr_space_start, size_t aslr_space_offset, bool is_hbl);
Result LoadFromMetadata(const FileSys::ProgramMetadata& metadata, std::size_t code_size, KProcessAddress aslr_space_start, size_t aslr_space_offset);
void LoadModule(CodeSet code_set, KProcessAddress base_addr);
+4 -3
View File
@@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@@ -106,9 +109,7 @@ void Break(Core::System& system, BreakReason reason, u64 info1, u64 info2) {
system.CurrentPhysicalCore().LogBacktrace();
}
const bool is_hbl = GetCurrentProcess(system.Kernel()).IsHbl();
const bool should_break = is_hbl || !notification_only;
const bool should_break = !notification_only;
if (system.DebuggerEnabled() && should_break) {
auto* thread = system.Kernel().GetCurrentEmuThread();
system.GetDebugger().NotifyThreadStopped(thread);
+2 -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 2024 yuzu Emulator Project
@@ -83,6 +83,7 @@ struct Applet {
// Application functions
bool game_play_recording_supported{};
bool media_playback_state{};
GamePlayRecordingState game_play_recording_state{GamePlayRecordingState::Disabled};
bool jit_service_launched{};
bool application_crash_report_enabled{};
@@ -11,6 +11,7 @@
#include "core/file_sys/registered_cache.h"
#include "core/file_sys/savedata_factory.h"
#include "core/hle/kernel/k_transfer_memory.h"
#include "core/hle/result.h"
#include "core/hle/service/am/am_results.h"
#include "core/hle/service/am/applet.h"
#include "core/hle/service/am/service/application_functions.h"
@@ -56,7 +57,7 @@ IApplicationFunctions::IApplicationFunctions(Core::System& system_, std::shared_
{37, nullptr, "GetLimitedApplicationLicenseUpgradableEvent"},
{40, D<&IApplicationFunctions::NotifyRunning>, "NotifyRunning"},
{50, D<&IApplicationFunctions::GetPseudoDeviceId>, "GetPseudoDeviceId"},
{60, nullptr, "SetMediaPlaybackStateForApplication"},
{60, D<&IApplicationFunctions::SetMediaPlaybackStateForApplication>, "SetMediaPlaybackStateForApplication"},
{65, D<&IApplicationFunctions::IsGamePlayRecordingSupported>, "IsGamePlayRecordingSupported"},
{66, D<&IApplicationFunctions::InitializeGamePlayRecording>, "InitializeGamePlayRecording"},
{67, D<&IApplicationFunctions::SetGamePlayRecordingState>, "SetGamePlayRecordingState"},
@@ -364,6 +365,13 @@ Result IApplicationFunctions::InitializeGamePlayRecording(
R_SUCCEED();
}
Result IApplicationFunctions::SetMediaPlaybackStateForApplication(bool enabled) {
LOG_WARNING(Service_AM, "(stubbed) {}", enabled);
std::scoped_lock lk{m_applet->lock};
m_applet->media_playback_state = enabled;
R_SUCCEED();
}
Result IApplicationFunctions::SetGamePlayRecordingState(
GamePlayRecordingState game_play_recording_state) {
LOG_WARNING(Service_AM, "(STUBBED) called");
@@ -53,6 +53,7 @@ private:
Result IsGamePlayRecordingSupported(Out<bool> out_is_game_play_recording_supported);
Result InitializeGamePlayRecording(
u64 transfer_memory_size, InCopyHandle<Kernel::KTransferMemory> transfer_memory_handle);
Result SetMediaPlaybackStateForApplication(bool enabled);
Result SetGamePlayRecordingState(GamePlayRecordingState game_play_recording_state);
Result EnableApplicationCrashReport(bool enabled);
Result InitializeApplicationCopyrightFrameBuffer(
+6 -1
View File
@@ -29,7 +29,7 @@ IBtmSystemCore::IBtmSystemCore(Core::System& system_)
{10, nullptr, "StartAudioDeviceDiscovery"},
{11, nullptr, "StopAudioDeviceDiscovery"},
{12, nullptr, "IsDiscoveryingAudioDevice"},
{13, nullptr, "GetDiscoveredAudioDevice"},
{13, C<&IBtmSystemCore::GetDiscoveredAudioDevice>, "GetDiscoveredAudioDevice"},
{14, C<&IBtmSystemCore::AcquireAudioDeviceConnectionEvent>, "AcquireAudioDeviceConnectionEvent"},
{15, nullptr, "ConnectAudioDevice"},
{16, nullptr, "IsConnectingAudioDevice"},
@@ -93,6 +93,11 @@ Result IBtmSystemCore::AcquireRadioEvent(Out<bool> out_is_valid,
R_SUCCEED();
}
Result IBtmSystemCore::GetDiscoveredAudioDevice(OutArray<std::array<u8, 0xFF>, BufferAttr_HipcPointer> out_audio_devices, s32 count, Out<s32> out_total) {
LOG_WARNING(Service_BTM, "(STUBBED) called");
R_SUCCEED();
}
Result IBtmSystemCore::AcquireAudioDeviceConnectionEvent(
OutCopyHandle<Kernel::KReadableEvent> out_event) {
LOG_WARNING(Service_BTM, "(STUBBED) called");
+5 -3
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
@@ -34,9 +37,8 @@ private:
Result DisableRadio();
Result IsRadioEnabled(Out<bool> out_is_enabled);
Result AcquireRadioEvent(Out<bool> out_is_valid,
OutCopyHandle<Kernel::KReadableEvent> out_event);
Result AcquireRadioEvent(Out<bool> out_is_valid, OutCopyHandle<Kernel::KReadableEvent> out_event);
Result GetDiscoveredAudioDevice(OutArray<std::array<u8, 0xFF>, BufferAttr_HipcPointer> out_audio_devices, s32 count, Out<s32> out_total);
Result AcquireAudioDeviceConnectionEvent(OutCopyHandle<Kernel::KReadableEvent> out_event);
Result GetConnectedAudioDevices(
@@ -151,7 +151,7 @@ Result IFileSystem::GetTotalSpaceSize(
Result IFileSystem::GetFileTimeStampRaw(
Out<FileSys::FileTimeStampRaw> out_timestamp,
const InLargeData<FileSys::Sf::Path, BufferAttr_HipcPointer> path) {
LOG_WARNING(Service_FS, "(Partial Implementation) called. file={}", path->str);
LOG_DEBUG(Service_FS, "(Partial Implementation) called. file={}", path->str);
FileSys::FileTimeStampRaw vfs_timestamp{};
R_TRY(backend->GetFileTimeStampRaw(&vfs_timestamp, FileSys::Path(path->str)));
@@ -284,9 +284,17 @@ Result FSP_SRV::OpenSaveDataFileSystem(OutInterface<IFileSystem> out_interface,
id = FileSys::StorageId::NandSystem;
break;
case FileSys::SaveDataSpaceId::Temporary:
// ok this is definitely wrong. ASSERT(false) here just kills the whole game the first
// time it opens cache storage, and plenty of games do that (TOTK for one). there is
// user-space scratch storage so it belongs on user nand. map it, do not crash.
id = FileSys::StorageId::NandUser;
break;
case FileSys::SaveDataSpaceId::ProperSystem:
case FileSys::SaveDataSpaceId::SafeMode:
ASSERT(false);
// same deal for these two. they are system-level spaces so they go on system nand.
// way better than nuking the title over a save-space id we just did not list out.
id = FileSys::StorageId::NandSystem;
break;
}
*out_interface =
@@ -324,9 +332,15 @@ Result FSP_SRV::OpenSaveDataFileSystemBySystemSaveDataId(OutInterface<IFileSyste
id = FileSys::StorageId::NandSystem;
break;
case FileSys::SaveDataSpaceId::Temporary:
// same broken switch as OpenSaveDataFileSystem above. do not ASSERT(false) and kill the
// game over a save-space id, just map Temporary to user nand like it should be.
id = FileSys::StorageId::NandUser;
break;
case FileSys::SaveDataSpaceId::ProperSystem:
case FileSys::SaveDataSpaceId::SafeMode:
ASSERT(false);
// system spaces -> system nand. handled, not crashed.
id = FileSys::StorageId::NandSystem;
break;
}
*out_interface =
+33
View File
@@ -0,0 +1,33 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "core/core.h"
#include "core/hle/service/gpio/gpio.h"
#include "core/hle/service/ipc_helpers.h"
#include "core/hle/service/service.h"
namespace Service::GPIO {
class GPIO final : public ServiceFramework<GPIO> {
public:
explicit GPIO(Core::System& system_)
: ServiceFramework{system_, "gpio"}
{
static const FunctionInfo functions[] = {
{0, nullptr, "Cmd0"},
};
RegisterHandlers(functions);
}
~GPIO() override = default;
};
void LoopProcess(Core::System& system) {
auto server_manager = std::make_unique<ServerManager>(system);
server_manager->RegisterNamedService("gpio", std::make_shared<GPIO>(system));
ServerManager::RunServer(std::move(server_manager));
}
} // namespace Service::GPIO
+15
View File
@@ -0,0 +1,15 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
namespace Core {
class System;
}
namespace Service::GPIO {
void LoopProcess(Core::System& system);
} // namespace Service::GPIO
+76
View File
@@ -0,0 +1,76 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "core/core.h"
#include "core/hle/result.h"
#include "core/hle/service/i2c/i2c.h"
#include "core/hle/service/ipc_helpers.h"
#include "core/hle/service/cmif_serialization.h"
#include "core/hle/service/cmif_types.h"
#include "core/hle/service/service.h"
#include "core/hle/service/server_manager.h"
namespace Service::I2C {
class I2CSession final : public ServiceFramework<I2CSession> {
public:
explicit I2CSession(Core::System& system_)
: ServiceFramework{system_, "I2CSession"}
{
static const FunctionInfo functions[] = {
{0, nullptr, "SendOld"},
{1, nullptr, "ReceiveOld"},
{2, nullptr, "ExecuteCommandListOld"},
{10, C<&I2CSession::Send>, "Send"},
{11, nullptr, "Receive"},
{12, nullptr, "ExecuteCommandList"},
{13, nullptr, "SetRetryPolicy"},
};
RegisterHandlers(functions);
}
~I2CSession() override = default;
Result Send(InBuffer<BufferAttr_HipcMapAlias> in_data, u32 transaction_option) {
LOG_WARNING(Service, "(stubbed) topt={}", transaction_option);
R_THROW(ResultUnknown);
}
};
enum class I2CDevice : u32 {
ClassicController,
Ftm3bd56,
};
class I2C final : public ServiceFramework<I2C> {
public:
explicit I2C(Core::System& system_)
: ServiceFramework{system_, "i2c"}
{
static const FunctionInfo functions[] = {
{0, nullptr, "OpenSessionForDev"},
{1, C<&I2C::OpenSession>, "OpenSession"},
{2, nullptr, "HasDevice"},
{3, nullptr, "HasDeviceForDev"},
{4, nullptr, "OpenSession2"},
};
RegisterHandlers(functions);
}
~I2C() override = default;
Result OpenSession(I2CDevice device, OutInterface<I2CSession> out_session) {
LOG_DEBUG(Service, "(stubbed)");
*out_session = std::make_shared<I2CSession>(system);
R_SUCCEED();
}
};
void LoopProcess(Core::System& system) {
auto server_manager = std::make_unique<ServerManager>(system);
server_manager->RegisterNamedService("i2c", std::make_shared<I2C>(system));
ServerManager::RunServer(std::move(server_manager));
}
} // namespace Service::I2C
+15
View File
@@ -0,0 +1,15 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
namespace Core {
class System;
}
namespace Service::I2C {
void LoopProcess(Core::System& system);
} // namespace Service::I2C
@@ -47,6 +47,8 @@ NvResult nvhost_ctrl_gpu::Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8>
return WrapFixed(this, &nvhost_ctrl_gpu::FlushL2, input, output);
case 0x14:
return WrapFixed(this, &nvhost_ctrl_gpu::GetActiveSlotMask, input, output);
case 0x15:
return WrapFixed(this, &nvhost_ctrl_gpu::PmuGetGpuLoad, input, output);
case 0x1c:
return WrapFixed(this, &nvhost_ctrl_gpu::GetGpuTime, input, output);
default:
@@ -234,6 +236,12 @@ NvResult nvhost_ctrl_gpu::GetActiveSlotMask(IoctlActiveSlotMask& params) {
return NvResult::Success;
}
NvResult nvhost_ctrl_gpu::PmuGetGpuLoad(IoctlPmuGetLoad& params) {
LOG_WARNING(Service_NVDRV, "(stubbed) called");
params.pmu_gpu_load = 100;
return NvResult::Success;
}
NvResult nvhost_ctrl_gpu::ZCullGetCtxSize(IoctlZcullGetCtxSize& params) {
LOG_DEBUG(Service_NVDRV, "called");
params.size = 0x1;
@@ -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 2018 yuzu Emulator Project
@@ -184,6 +184,11 @@ private:
};
static_assert(sizeof(IoctlGetCpuTimeCorrelationInfo) == 264);
struct IoctlPmuGetLoad {
u32 pmu_gpu_load;
};
static_assert(sizeof(IoctlPmuGetLoad) == 4);
NvResult GetCharacteristics1(IoctlCharacteristics& params);
NvResult GetCharacteristics3(IoctlCharacteristics& params,
std::span<IoctlGpuCharacteristics> gpu_characteristics);
@@ -192,6 +197,7 @@ private:
NvResult GetTPCMasks3(IoctlGpuGetTpcMasksArgs& params, std::span<u32> tpc_mask);
NvResult GetActiveSlotMask(IoctlActiveSlotMask& params);
NvResult PmuGetGpuLoad(IoctlPmuGetLoad& params);
NvResult ZCullGetCtxSize(IoctlZcullGetCtxSize& params);
NvResult ZCullGetInfo(IoctlNvgpuGpuZcullGetInfoArgs& params);
NvResult ZBCSetTable(IoctlZbcSetTable& params);
@@ -25,18 +25,20 @@ NvResult nvhost_nvdec::Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> in
switch (command.group) {
case 0x0:
switch (command.cmd) {
case 0x1:
case 0x01:
return WrapFixedVariable(this, &nvhost_nvdec::Submit, input, output, fd);
case 0x2:
case 0x02:
return WrapFixed(this, &nvhost_nvdec::GetSyncpoint, input, output);
case 0x3:
case 0x03:
return WrapFixed(this, &nvhost_nvdec::GetWaitbase, input, output);
case 0x7:
case 0x07:
return WrapFixed(this, &nvhost_nvdec::SetSubmitTimeout, input, output);
case 0x9:
case 0x09:
return WrapFixedVariable(this, &nvhost_nvdec::MapBuffer, input, output, fd);
case 0xa:
case 0x0a:
return WrapFixedVariable(this, &nvhost_nvdec::UnmapBuffer, input, output);
case 0x23:
return WrapFixed(this, &nvhost_nvdec::GetClkRate, input, output);
default:
break;
}
@@ -168,6 +168,13 @@ NvResult nvhost_nvdec_common::SetSubmitTimeout(u32 timeout) {
return NvResult::Success;
}
NvResult nvhost_nvdec_common::GetClkRate(IoctlGetClkRate& params) {
LOG_WARNING(Service_NVDRV, "(STUBBED) called");
params.clk_rate = 614400000;
params.module_id = 0;
return NvResult::Success;
}
Kernel::KEvent* nvhost_nvdec_common::QueryEvent(u32 event_id) {
LOG_CRITICAL(Service_NVDRV, "Unknown HOSTX1 Event {}", event_id);
return nullptr;
@@ -111,6 +111,12 @@ protected:
};
static_assert(sizeof(IoctlMapBuffer) == 0x0C, "IoctlMapBuffer is incorrect size");
struct IoctlGetClkRate {
u32_le clk_rate{};
u32_le module_id{};
};
static_assert(sizeof(IoctlGetClkRate) == 8);
/// Ioctl command implementations
NvResult SetNVMAPfd(IoctlSetNvmapFD&);
NvResult Submit(IoctlSubmit& params, std::span<u8> input, DeviceFD fd);
@@ -119,6 +125,7 @@ protected:
NvResult MapBuffer(IoctlMapBuffer& params, std::span<MapBufferEntry> entries, DeviceFD fd);
NvResult UnmapBuffer(IoctlMapBuffer& params, std::span<MapBufferEntry> entries);
NvResult SetSubmitTimeout(u32 timeout);
NvResult GetClkRate(IoctlGetClkRate& params);
Kernel::KEvent* QueryEvent(u32 event_id) override;
+65 -3
View File
@@ -6,10 +6,12 @@
#include <memory>
#include "common/common_funcs.h"
#include "common/device_power_state.h"
#include "common/logging.h"
#include "core/core.h"
#include "core/hle/kernel/k_event.h"
#include "core/hle/result.h"
#include "core/hle/service/ipc_helpers.h"
#include "core/hle/service/kernel_helpers.h"
#include "core/hle/service/ptm/psm.h"
@@ -134,12 +136,12 @@ PSM::PSM(Core::System& system_) : ServiceFramework{system_, "psm"} {
{9, nullptr, "DisableEnoughPowerChargeEmulation"},
{10, nullptr, "EnableFastBatteryCharging"},
{11, nullptr, "DisableFastBatteryCharging"},
{12, nullptr, "GetBatteryVoltageState"},
{12, &PSM::GetBatteryVoltageState, "GetBatteryVoltageState"},
{13, nullptr, "GetRawBatteryChargePercentage"},
{14, nullptr, "IsEnoughPowerSupplied"},
{15, nullptr, "GetBatteryAgePercentage"},
{15, &PSM::GetBatteryAgePercentage, "GetBatteryAgePercentage"},
{16, nullptr, "GetBatteryChargeInfoEvent"},
{17, nullptr, "GetBatteryChargeInfoFields"},
{17, &PSM::GetBatteryChargeInfoFields, "GetBatteryChargeInfoFields"},
{18, nullptr, "GetBatteryChargeCalibratedEvent"},
};
// clang-format on
@@ -187,4 +189,64 @@ void PSM::OpenSession(HLERequestContext& ctx) {
rb.PushIpcInterface<IPsmSession>(system);
}
void PSM::GetBatteryVoltageState(HLERequestContext& ctx) {
LOG_DEBUG(Service_PTM, "(stubbed)");
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
rb.PushRaw<u32>(0); //
}
void PSM::GetBatteryAgePercentage(HLERequestContext& ctx) {
LOG_DEBUG(Service_PTM, "(stubbed)");
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
rb.PushRaw<f64>(1.f);
}
struct BatteryChargeInfoFields {
u32 input_current_limit; //mA
u32 boost_mode_current_limit;
u32 fast_charge_current_limit;
u32 charge_voltage_limit;
u32 charger_type;
u8 hi2_mode;
u8 battery_charging;
INSERT_PADDING_BYTES_NOINIT(2);
u32 vdd50_state;
u32 temperature_celcius;
f32 battery_charge_percentage;
u32 battery_charge_milli_voltage;
f32 battery_age_percentage;
u32 usb_power_role;
u32 usb_charger_type;
u32 charger_input_voltage_limit;
u32 charger_input_current_limit;
u8 fast_battery_charging;
u8 controller_power_supply;
u8 otg_request;
INSERT_PADDING_BYTES_NOINIT(1);
INSERT_PADDING_BYTES_NOINIT(0x14); //[+17.0.0]
};
static_assert(sizeof(struct BatteryChargeInfoFields) == 0x54);
void PSM::GetBatteryChargeInfoFields(HLERequestContext& ctx) {
LOG_DEBUG(Service_PTM, "called");
Common::PowerStatus power_status = Common::GetPowerStatus();
BatteryChargeInfoFields r{};
r.battery_charge_percentage = f32(power_status.percentage); //100%
r.battery_age_percentage = f32(power_status.percentage); //100%
r.battery_charging = power_status.charging ? 1 : 0;
r.charger_type = u32(power_status.has_battery && power_status.charging
? ChargerType::RegularCharger : ChargerType::Unplugged);
r.charger_input_voltage_limit = 100;
r.charger_input_voltage_limit = 100;
r.input_current_limit = 100;
r.boost_mode_current_limit = 100;
r.fast_charge_current_limit = 100;
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
rb.PushRaw<BatteryChargeInfoFields>(r);
}
} // namespace Service::PTM
+4 -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: Copyright 2018 yuzu Emulator Project
@@ -22,10 +22,12 @@ private:
LowPowerCharger = 2,
Unknown = 3,
};
void GetBatteryChargePercentage(HLERequestContext& ctx);
void GetChargerType(HLERequestContext& ctx);
void OpenSession(HLERequestContext& ctx);
void GetBatteryVoltageState(HLERequestContext& ctx);
void GetBatteryAgePercentage(HLERequestContext& ctx);
void GetBatteryChargeInfoFields(HLERequestContext& ctx);
};
} // namespace Service::PTM
+7 -4
View File
@@ -21,10 +21,13 @@ namespace Service::RO {
namespace {
// Convenience definitions.
constexpr size_t MaxSessions = 0x3;
constexpr size_t MaxNrrInfos = 0x40;
constexpr size_t MaxNroInfos = 0x40;
// Atmosphere defines as follows:
// Sessions = 0x03, NrrInfos = 0x40, NroInfos = 0x40
// This may not be enough for some mods (plugin.nro dependant games) like SSBU
// Suppose someone loads like 64 plugins of these, now what?
constexpr size_t MaxSessions = 0x03; // No change
constexpr size_t MaxNrrInfos = 0x100; // Up to 256 NRRs
constexpr size_t MaxNroInfos = 0x100; // Up to 256 NROs
constexpr u64 InvalidProcessId = 0xffffffffffffffffULL;
constexpr u64 InvalidContextId = 0xffffffffffffffffULL;
+6 -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: Copyright 2024 yuzu Emulator Project
@@ -25,8 +25,10 @@
#include "core/hle/service/friend/friend.h"
#include "core/hle/service/glue/glue.h"
#include "core/hle/service/grc/grc.h"
#include "core/hle/service/gpio/gpio.h"
#include "core/hle/service/hid/hid.h"
#include "core/hle/service/ipc_helpers.h"
#include "core/hle/service/i2c/i2c.h"
#include "core/hle/service/jit/jit.h"
#include "core/hle/service/lbl/lbl.h"
#include "core/hle/service/ldn/ldn.h"
@@ -144,7 +146,9 @@ Services::Services(std::shared_ptr<SM::ServiceManager>& sm, Core::System& system
{"ro", &RO::LoopProcess},
{"spl", &SPL::LoopProcess},
{"ssl", &SSL::LoopProcess},
{"usb", &USB::LoopProcess}
{"usb", &USB::LoopProcess},
{"i2c", &I2C::LoopProcess},
{"gpio", &GPIO::LoopProcess},
})
kernel.RunOnGuestCoreProcess(std::string(e.first), [&system, f = e.second] { f(system); });
}
@@ -142,10 +142,10 @@ ISystemSettingsServer::ISystemSettingsServer(Core::System& system_)
{22, C<&ISystemSettingsServer::SetEulaVersions>, "SetEulaVersions"},
{23, C<&ISystemSettingsServer::GetColorSetId>, "GetColorSetId"},
{24, C<&ISystemSettingsServer::SetColorSetId>, "SetColorSetId"},
{25, nullptr, "GetConsoleInformationUploadFlag"},
{26, nullptr, "SetConsoleInformationUploadFlag"},
{27, nullptr, "GetAutomaticApplicationDownloadFlag"},
{28, nullptr, "SetAutomaticApplicationDownloadFlag"},
{25, C<&ISystemSettingsServer::GetConsoleInformationUploadFlag>, "GetConsoleInformationUploadFlag"},
{26, C<&ISystemSettingsServer::SetConsoleInformationUploadFlag>, "SetConsoleInformationUploadFlag"},
{27, C<&ISystemSettingsServer::GetAutomaticApplicationDownloadFlag>, "GetAutomaticApplicationDownloadFlag"},
{28, C<&ISystemSettingsServer::SetAutomaticApplicationDownloadFlag>, "SetAutomaticApplicationDownloadFlag"},
{29, C<&ISystemSettingsServer::GetNotificationSettings>, "GetNotificationSettings"},
{30, C<&ISystemSettingsServer::SetNotificationSettings>, "SetNotificationSettings"},
{31, C<&ISystemSettingsServer::GetAccountNotificationSettings>, "GetAccountNotificationSettings"},
@@ -160,8 +160,8 @@ ISystemSettingsServer::ISystemSettingsServer(Core::System& system_)
{42, nullptr, "SetEdid"},
{43, C<&ISystemSettingsServer::GetAudioOutputMode>, "GetAudioOutputMode"},
{44, C<&ISystemSettingsServer::SetAudioOutputMode>, "SetAudioOutputMode"},
{45, C<&ISystemSettingsServer::GetSpeakerAutoMuteFlag> , "GetSpeakerAutoMuteFlag"},
{46, C<&ISystemSettingsServer::SetSpeakerAutoMuteFlag> , "SetSpeakerAutoMuteFlag"},
{45, C<&ISystemSettingsServer::GetSpeakerAutoMuteFlag>, "GetSpeakerAutoMuteFlag"},
{46, C<&ISystemSettingsServer::SetSpeakerAutoMuteFlag>, "SetSpeakerAutoMuteFlag"},
{47, C<&ISystemSettingsServer::GetQuestFlag>, "GetQuestFlag"},
{48, C<&ISystemSettingsServer::SetQuestFlag>, "SetQuestFlag"},
{49, nullptr, "GetDataDeletionSettings"},
@@ -180,8 +180,8 @@ ISystemSettingsServer::ISystemSettingsServer(Core::System& system_)
{62, C<&ISystemSettingsServer::GetDebugModeFlag>, "GetDebugModeFlag"},
{63, C<&ISystemSettingsServer::GetPrimaryAlbumStorage>, "GetPrimaryAlbumStorage"},
{64, C<&ISystemSettingsServer::SetPrimaryAlbumStorage>, "SetPrimaryAlbumStorage"},
{65, nullptr, "GetUsb30EnableFlag"},
{66, nullptr, "SetUsb30EnableFlag"},
{65, C<&ISystemSettingsServer::GetUsb30EnableFlag>, "GetUsb30EnableFlag"},
{66, C<&ISystemSettingsServer::SetUsb30EnableFlag>, "SetUsb30EnableFlag"},
{67, C<&ISystemSettingsServer::GetBatteryLot>, "GetBatteryLot"},
{68, C<&ISystemSettingsServer::GetSerialNumber>, "GetSerialNumber"},
{69, C<&ISystemSettingsServer::GetNfcEnableFlag>, "GetNfcEnableFlag"},
@@ -1074,6 +1074,45 @@ Result ISystemSettingsServer::SetNfcEnableFlag(bool nfc_enable_flag) {
R_SUCCEED();
}
Result ISystemSettingsServer::GetConsoleInformationUploadFlag(Out<bool> out_flag) {
LOG_INFO(Service_SET, "called {}", m_system_settings.console_information_upload_flag);
*out_flag = m_system_settings.console_information_upload_flag;
R_SUCCEED();
}
Result ISystemSettingsServer::SetConsoleInformationUploadFlag(bool flag) {
LOG_INFO(Service_SET, "called {}", flag);
m_system_settings.usb_30_enable_flag = flag;
SetSaveNeeded();
R_SUCCEED();
}
Result ISystemSettingsServer::GetAutomaticApplicationDownloadFlag(Out<bool> out_flag) {
LOG_INFO(Service_SET, "called {}", m_system_settings.usb_30_enable_flag);
*out_flag = m_system_settings.automatic_application_download_flag;
R_SUCCEED();
}
Result ISystemSettingsServer::SetAutomaticApplicationDownloadFlag(bool flag) {
LOG_INFO(Service_SET, "called {}", flag);
m_system_settings.automatic_application_download_flag = flag;
SetSaveNeeded();
R_SUCCEED();
}
Result ISystemSettingsServer::GetUsb30EnableFlag(Out<bool> out_usb30_enable_flag) {
LOG_INFO(Service_SET, "called, usb30_enable_flag={}", m_system_settings.usb_30_enable_flag);
*out_usb30_enable_flag = m_system_settings.usb_30_enable_flag;
R_SUCCEED();
}
Result ISystemSettingsServer::SetUsb30EnableFlag(bool usb30_enable_flag) {
LOG_INFO(Service_SET, "called, usb30_enable_flag={}", usb30_enable_flag);
m_system_settings.usb_30_enable_flag = usb30_enable_flag;
SetSaveNeeded();
R_SUCCEED();
}
Result ISystemSettingsServer::GetSleepSettings(Out<SleepSettings> out_sleep_settings) {
LOG_INFO(Service_SET, "called, flags={}, handheld_sleep_plan={}, console_sleep_plan={}",
m_system_settings.sleep_settings.flags.raw,
@@ -109,6 +109,12 @@ public:
Result SetPrimaryAlbumStorage(PrimaryAlbumStorage primary_album_storage);
Result GetBatteryLot(Out<BatteryLot> out_battery_lot);
Result GetSerialNumber(Out<SerialNumber> out_console_serial);
Result GetConsoleInformationUploadFlag(Out<bool> out_flag);
Result SetConsoleInformationUploadFlag(bool flag);
Result GetAutomaticApplicationDownloadFlag(Out<bool> out_flag);
Result SetAutomaticApplicationDownloadFlag(bool flag);
Result GetUsb30EnableFlag(Out<bool> out_usb30_enable_flag);
Result SetUsb30EnableFlag(bool usb30_enable_flag);
Result GetNfcEnableFlag(Out<bool> out_nfc_enable_flag);
Result SetNfcEnableFlag(bool nfc_enable_flag);
Result GetSleepSettings(Out<SleepSettings> out_sleep_settings);
+37 -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 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@@ -11,6 +14,7 @@
#include "core/hle/kernel/k_scoped_resource_reservation.h"
#include "core/hle/kernel/k_server_port.h"
#include "core/hle/result.h"
#include "core/hle/service/cmif_types.h"
#include "core/hle/service/ipc_helpers.h"
#include "core/hle/service/server_manager.h"
#include "core/hle/service/sm/sm.h"
@@ -250,15 +254,37 @@ void SM::UnregisterService(HLERequestContext& ctx) {
rb.Push(service_manager.UnregisterService(name));
}
void SM::AtmosphereHasService(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
std::string name(PopServiceName(rp));
LOG_WARNING(Service_SM, "(stubbed) called with name={}", name);
IPC::ResponseBuilder rb{ctx, 3};
Kernel::KClientPort* out_client_port = nullptr;
rb.Push(ResultSuccess);
rb.Push<bool>(service_manager.GetServicePort(&out_client_port, name) == ResultSuccess);
}
SM::SM(ServiceManager& service_manager_, Core::System& system_)
: ServiceFramework{system_, "sm:", 4},
service_manager{service_manager_}, kernel{system_.Kernel()} {
: ServiceFramework{system_, "sm:", 4}
, service_manager{service_manager_}
, kernel{system_.Kernel()}
{
RegisterHandlers({
{0, &SM::Initialize, "Initialize"},
{1, &SM::GetServiceCmif, "GetService"},
{2, &SM::RegisterServiceCmif, "RegisterService"},
{3, &SM::UnregisterService, "UnregisterService"},
{4, nullptr, "DetachClient"},
// TODO: are these non-TIPC as well?
{65000, nullptr, "AtmosphereInstallMitm"},
{65001, nullptr, "AtmosphereUninstallMitm"},
{65002, nullptr, "Deprecated_AtmosphereAssociatePidTidForMitm"},
{65003, nullptr, "AtmosphereAcknowledgeMitmSession"},
{65004, nullptr, "AtmosphereHasMitm"},
{65005, nullptr, "AtmosphereWaitMitm"},
{65006, nullptr, "AtmosphereDeclareFutureMitm"},
{65100, &SM::AtmosphereHasService, "AtmosphereHasService"},
{65101, nullptr, "AtmosphereWaitService"},
});
RegisterHandlersTipc({
{0, &SM::Initialize, "Initialize"},
@@ -266,6 +292,15 @@ SM::SM(ServiceManager& service_manager_, Core::System& system_)
{2, &SM::RegisterServiceTipc, "RegisterService"},
{3, &SM::UnregisterService, "UnregisterService"},
{4, nullptr, "DetachClient"},
{65000, nullptr, "AtmosphereInstallMitm"},
{65001, nullptr, "AtmosphereUninstallMitm"},
{65002, nullptr, "Deprecated_AtmosphereAssociatePidTidForMitm"},
{65003, nullptr, "AtmosphereAcknowledgeMitmSession"},
{65004, nullptr, "AtmosphereHasMitm"},
{65005, nullptr, "AtmosphereWaitMitm"},
{65006, nullptr, "AtmosphereDeclareFutureMitm"},
{65100, &SM::AtmosphereHasService, "AtmosphereHasService"},
{65101, nullptr, "AtmosphereWaitService"},
});
}
+1
View File
@@ -47,6 +47,7 @@ private:
void RegisterServiceCmif(HLERequestContext& ctx);
void RegisterServiceTipc(HLERequestContext& ctx);
void UnregisterService(HLERequestContext& ctx);
void AtmosphereHasService(HLERequestContext& ctx);
Result GetServiceImpl(Kernel::KClientSession** out_client_session, HLERequestContext& ctx);
void RegisterServiceImpl(HLERequestContext& ctx, std::string name, u32 max_session_count,
@@ -71,9 +71,10 @@ struct PatchCollection {
std::array<s32, 13> module_patcher_indices{};
};
AppLoader_DeconstructedRomDirectory::AppLoader_DeconstructedRomDirectory(FileSys::VirtualFile file_,
bool override_update_)
: AppLoader(std::move(file_)), override_update(override_update_), is_hbl(false) {
AppLoader_DeconstructedRomDirectory::AppLoader_DeconstructedRomDirectory(FileSys::VirtualFile file_, bool override_update_)
: AppLoader(std::move(file_))
, override_update(override_update_)
{
const auto file_dir = file->GetContainingDirectory();
// Title ID
@@ -124,9 +125,11 @@ AppLoader_DeconstructedRomDirectory::AppLoader_DeconstructedRomDirectory(FileSys
}
AppLoader_DeconstructedRomDirectory::AppLoader_DeconstructedRomDirectory(
FileSys::VirtualDir directory, bool override_update_, bool is_hbl_)
: AppLoader(directory->GetFile("main")), dir(std::move(directory)),
override_update(override_update_), is_hbl(is_hbl_) {}
FileSys::VirtualDir directory, bool override_update_)
: AppLoader(directory->GetFile("main"))
, dir(std::move(directory))
, override_update(override_update_)
{}
FileType AppLoader_DeconstructedRomDirectory::IdentifyType(const FileSys::VirtualFile& dir_file) {
if (FileSys::IsDirectoryExeFS(dir_file->GetContainingDirectory())) {
@@ -232,7 +235,7 @@ AppLoader_DeconstructedRomDirectory::LoadResult AppLoader_DeconstructedRomDirect
? ::Settings::values.rng_seed.GetValue() : Common::Random::Random64(0)) << 12) & 0xfff000;
// Setup the process code layout
if (process.LoadFromMetadata(metadata, code_size, fastmem_base, aslr_offset, is_hbl).IsError()) {
if (process.LoadFromMetadata(metadata, code_size, fastmem_base, aslr_offset).IsError()) {
return {ResultStatus::ErrorUnableToParseKernelMetadata, {}};
}
@@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@@ -22,13 +25,9 @@ namespace Loader {
*/
class AppLoader_DeconstructedRomDirectory final : public AppLoader {
public:
explicit AppLoader_DeconstructedRomDirectory(FileSys::VirtualFile main_file,
bool override_update_ = false);
explicit AppLoader_DeconstructedRomDirectory(FileSys::VirtualFile main_file, bool override_update_ = false);
// Overload to accept exefs directory. Must contain 'main' and 'main.npdm'
explicit AppLoader_DeconstructedRomDirectory(FileSys::VirtualDir directory,
bool override_update_ = false,
bool is_hbl_ = false);
explicit AppLoader_DeconstructedRomDirectory(FileSys::VirtualDir directory, bool override_update_ = false);
/**
* Identifies whether or not the given file is a deconstructed ROM directory.
@@ -63,7 +62,6 @@ private:
std::string name;
u64 title_id{};
bool override_update;
bool is_hbl;
Modules modules;
};
+1 -1
View File
@@ -93,7 +93,7 @@ AppLoader::LoadResult AppLoader_KIP::Load(Kernel::KProcess& process,
? ::Settings::values.rng_seed.GetValue() : Common::Random::Random64(0)) << 12) & 0xfff000;
// Setup the process code layout
if (process.LoadFromMetadata(FileSys::ProgramMetadata::GetDefault(), codeset.memory.size(), 0, aslr_offset, false).IsError()) {
if (process.LoadFromMetadata(FileSys::ProgramMetadata::GetDefault(), codeset.memory.size(), 0, aslr_offset).IsError()) {
return {ResultStatus::ErrorNotInitialized, {}};
}
const VAddr base_address = GetInteger(process.GetEntryPoint());
+79 -15
View File
@@ -4,9 +4,15 @@
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <array>
#include <cstddef>
#include <cstring>
#include <optional>
#include <string>
#include <utility>
#include <vector>
#include "common/alignment.h"
#include "common/common_funcs.h"
#include "common/common_types.h"
#include "common/logging.h"
@@ -23,7 +29,6 @@
#include "core/hle/kernel/k_thread.h"
#include "core/hle/service/filesystem/filesystem.h"
#include "core/loader/nro.h"
#include "core/loader/nso.h"
#include "core/memory.h"
#ifdef HAS_NCE
@@ -174,19 +179,6 @@ static bool LoadNroImpl(Core::System& system, Kernel::KProcess& process,
codeset.segments[i].size = PageAlignSize(nro_header.segments[i].size);
}
if (!Settings::values.program_args.GetValue().empty()) {
const auto arg_data = Settings::values.program_args.GetValue();
codeset.DataSegment().size += NSO_ARGUMENT_DATA_ALLOCATION_SIZE;
NSOArgumentHeader args_header{
NSO_ARGUMENT_DATA_ALLOCATION_SIZE, static_cast<u32_le>(arg_data.size()), {}};
const auto end_offset = program_image.size();
program_image.resize(static_cast<u32>(program_image.size()) +
NSO_ARGUMENT_DATA_ALLOCATION_SIZE);
std::memcpy(program_image.data() + end_offset, &args_header, sizeof(NSOArgumentHeader));
std::memcpy(program_image.data() + end_offset + sizeof(NSOArgumentHeader), arg_data.data(),
arg_data.size());
}
// Default .bss to NRO header bss size if MOD0 section doesn't exist
u32 bss_size{PageAlignSize(nro_header.bss_size)};
@@ -203,6 +195,47 @@ static bool LoadNroImpl(Core::System& system, Kernel::KProcess& process,
codeset.DataSegment().size += bss_size;
program_image.resize(static_cast<u32>(program_image.size()) + bss_size);
struct ConfigEntry {
u32_le key;
u32_le flags;
u64_le value[2];
};
static_assert(sizeof(ConfigEntry) == 0x18);
// AArch64 encoding for svc #0x7 (ExitProcess).
constexpr u32 kSvcExitProcessInstruction = 0xD40000E1;
constexpr size_t kNumEntries = 4; // MainThreadHandle, AppletType, Argv, EndOfList
constexpr size_t kConfigTableSize = kNumEntries * sizeof(ConfigEntry);
std::string argv_string;
size_t args_offset_in_image = 0;
std::optional<size_t> exit_process_offset_in_image;
const auto& program_args = Settings::values.program_args.GetValue();
if (!program_args.empty()) {
argv_string = "homebrew ";
argv_string += program_args;
argv_string.push_back('\0');
const auto& code = codeset.CodeSegment();
const size_t code_end = (std::min)(program_image.size(), code.offset + code.size);
for (size_t offset = code.offset; offset + sizeof(u32) <= code_end; offset += sizeof(u32)) {
u32 instruction{};
std::memcpy(&instruction, program_image.data() + offset, sizeof(instruction));
if (instruction == kSvcExitProcessInstruction) {
exit_process_offset_in_image = offset;
break;
}
}
if (!exit_process_offset_in_image) {
LOG_WARNING(Loader,
"Unable to find svcExitProcess in NRO; returning from main may fault");
}
const size_t entries_and_argv =
Common::AlignUp(kConfigTableSize + argv_string.size(), Core::Memory::YUZU_PAGESIZE);
args_offset_in_image = program_image.size();
codeset.DataSegment().size += static_cast<u32>(entries_and_argv);
program_image.resize(args_offset_in_image + entries_and_argv);
}
size_t image_size = program_image.size();
#ifdef HAS_NCE
@@ -247,7 +280,7 @@ static bool LoadNroImpl(Core::System& system, Kernel::KProcess& process,
// Setup the process code layout
if (process
.LoadFromMetadata(FileSys::ProgramMetadata::GetDefault(), image_size, fastmem_base, aslr_offset, false)
.LoadFromMetadata(FileSys::ProgramMetadata::GetDefault(), image_size, fastmem_base, aslr_offset)
.IsError()) {
return false;
}
@@ -264,6 +297,37 @@ static bool LoadNroImpl(Core::System& system, Kernel::KProcess& process,
// Load codeset for current process
codeset.memory = std::move(program_image);
process.LoadModule(std::move(codeset), process.GetEntryPoint());
if (!argv_string.empty()) {
constexpr u32 kEntryEndOfList = 0;
constexpr u32 kEntryMainThreadHandle = 1;
constexpr u32 kEntryArgv = 5;
constexpr u32 kEntryAppletType = 7;
constexpr u32 kAppletTypeApplication = 0;
const u64 base = GetInteger(process.GetEntryPoint());
const u64 config_addr = base + args_offset_in_image;
const u64 argv_addr = config_addr + kConfigTableSize;
const ConfigEntry entries[kNumEntries] = {
{kEntryMainThreadHandle, 0, {0, 0}}, // Value[0] patched in Run()
{kEntryAppletType, 0, {kAppletTypeApplication, 0}},
{kEntryArgv, 0, {0, argv_addr}},
{kEntryEndOfList, 0, {0, 0}},
};
process.GetMemory().WriteBlock(Common::ProcessAddress{config_addr}, entries,
sizeof(entries));
process.GetMemory().WriteBlock(Common::ProcessAddress{argv_addr},
argv_string.data(), argv_string.size());
constexpr size_t kMainThreadHandleValueOffset = offsetof(ConfigEntry, value);
process.SetArgPointer(Kernel::KProcessAddress{config_addr});
if (exit_process_offset_in_image) {
process.SetArgReturnAddress(
Kernel::KProcessAddress{base + *exit_process_offset_in_image});
}
process.SetMainThreadHandleAddr(
Kernel::KProcessAddress{config_addr + kMainThreadHandleValueOffset});
}
return true;
}
+1 -2
View File
@@ -34,8 +34,7 @@ AppLoader_NSP::AppLoader_NSP(FileSys::VirtualFile file_,
}
if (nsp->IsExtractedType()) {
secondary_loader = std::make_unique<AppLoader_DeconstructedRomDirectory>(
nsp->GetExeFS(), false, file->GetName() == "hbl.nsp");
secondary_loader = std::make_unique<AppLoader_DeconstructedRomDirectory>(nsp->GetExeFS(), false);
} else {
const auto control_nca =
nsp->GetNCA(nsp->GetProgramTitleID(), FileSys::ContentRecordType::Control);
@@ -90,6 +90,8 @@ std::once_flag flag;
std::optional<SpinLockImpl> impl;
void SpinLockImpl::Initialize() noexcept {
// Needed for W^X systems (i.e SElinux, OpenBSD)
code.setProtectMode(Xbyak::CodeArray::ProtectMode::PROTECT_RW);
Xbyak::Reg64 const ABI_PARAM1 = Backend::X64::HostLocToReg64(Backend::X64::ABI_PARAM1);
code.align();
lock = code.getCurr<void (*)(volatile int*)>();
@@ -99,6 +101,7 @@ void SpinLockImpl::Initialize() noexcept {
unlock = code.getCurr<void (*)(volatile int*)>();
EmitSpinLockUnlock(code, ABI_PARAM1, code.eax);
code.ret();
code.setProtectMode(Xbyak::CodeArray::ProtectMode::PROTECT_RE);
}
void SpinLockImpl::GlobalInitialize() noexcept {
+17 -33
View File
@@ -238,33 +238,27 @@ void Config::ReadControlValues() {
void Config::ReadMotionTouchValues() {
Settings::values.touch_from_button_maps.clear();
int num_touch_from_button_maps = BeginArray(std::string("touch_from_button_maps"));
if (num_touch_from_button_maps > 0) {
for (int i = 0; i < num_touch_from_button_maps; ++i) {
SetArrayIndex(i);
Settings::TouchFromButtonMap map;
map.name = ReadStringSetting(std::string("name"), std::string("default"));
const int num_touch_maps = BeginArray(std::string("entries"));
map.buttons.reserve(num_touch_maps);
int const num_touch_maps = BeginArray(std::string("entries"));
map.buttons.resize(num_touch_maps);
for (int j = 0; j < num_touch_maps; j++) {
SetArrayIndex(j);
std::string touch_mapping = ReadStringSetting(std::string("bind"));
map.buttons.emplace_back(std::move(touch_mapping));
map.buttons[j] = ReadStringSetting(std::string("bind"));
}
EndArray(); // entries
Settings::values.touch_from_button_maps.emplace_back(std::move(map));
}
} else {
Settings::values.touch_from_button_maps.emplace_back(
Settings::TouchFromButtonMap{"default", {}});
Settings::values.touch_from_button_maps.emplace_back(Settings::TouchFromButtonMap{"default", {}});
num_touch_from_button_maps = 1;
}
EndArray(); // touch_from_button_maps
Settings::values.touch_from_button_map_index = std::clamp(
Settings::values.touch_from_button_map_index.GetValue(), 0, num_touch_from_button_maps - 1);
Settings::values.touch_from_button_map_index = (std::min)(Settings::values.touch_from_button_map_index.GetValue(), u32(num_touch_from_button_maps - 1));
}
void Config::ReadCoreValues() {
@@ -501,15 +495,12 @@ void Config::SaveMotionTouchValues() {
BeginArray(std::string("touch_from_button_maps"));
for (std::size_t p = 0; p < Settings::values.touch_from_button_maps.size(); ++p) {
SetArrayIndex(int(p));
WriteStringSetting(std::string("name"), Settings::values.touch_from_button_maps[p].name,
std::make_optional(std::string("default")));
WriteStringSetting(std::string("name"), Settings::values.touch_from_button_maps[p].name, std::make_optional(std::string("default")));
BeginArray(std::string("entries"));
for (std::size_t q = 0; q < Settings::values.touch_from_button_maps[p].buttons.size();
++q) {
SetArrayIndex(int(q));
WriteStringSetting(std::string("bind"),
Settings::values.touch_from_button_maps[p].buttons[q]);
WriteStringSetting(std::string("bind"), Settings::values.touch_from_button_maps[p].buttons[q]);
}
EndArray(); // entries
}
@@ -638,8 +629,7 @@ void Config::SaveDisabledAddOnValues() {
BeginArray(std::string("disabled"));
for (std::size_t j = 0; j < elem.second.size(); ++j) {
SetArrayIndex(int(j));
WriteStringSetting(std::string("d"), elem.second[j],
std::make_optional(std::string("")));
WriteStringSetting(std::string("d"), elem.second[j], std::make_optional(std::string("")));
}
EndArray(); // disabled
++i;
@@ -733,21 +723,18 @@ s64 Config::ReadIntegerSetting(const std::string& key, const std::optional<s64>
std::string full_key = GetFullKey(key, false);
if (!default_value.has_value()) {
try {
return std::stoll(
std::string(config->GetValue(GetSection().c_str(), full_key.c_str(), "0")));
return std::stoll(std::string(config->GetValue(GetSection().c_str(), full_key.c_str(), "0")));
} catch (...) {
return 0;
}
}
s64 result = 0;
if (config->GetBoolValue(GetSection().c_str(),
std::string(full_key).append("\\default").c_str(), true)) {
if (config->GetBoolValue(GetSection().c_str(), std::string(full_key).append("\\default").c_str(), true)) {
result = default_value.value();
} else {
try {
result = std::stoll(std::string(config->GetValue(
GetSection().c_str(), full_key.c_str(), ToString(default_value.value()).c_str())));
result = std::stoll(std::string(config->GetValue(GetSection().c_str(), full_key.c_str(), ToString(default_value.value()).c_str())));
} catch (...) {
result = default_value.value();
}
@@ -919,14 +906,12 @@ void Config::ReadSettingGeneric(Settings::BasicSetting* const setting) {
bool use_global = true;
if (setting->Switchable() && !global) {
use_global =
ReadBooleanSetting(std::string(key).append("\\use_global"), std::make_optional(true));
use_global = ReadBooleanSetting(std::string(key).append("\\use_global"), std::make_optional(true));
setting->SetGlobal(use_global);
}
if (global || !use_global) {
const bool is_default =
ReadBooleanSetting(std::string(key).append("\\default"), std::make_optional(true));
const bool is_default = ReadBooleanSetting(std::string(key).append("\\default"), std::make_optional(true));
if (!is_default) {
setting->LoadString(ReadStringSetting(key, default_value));
} else {
@@ -1050,10 +1035,9 @@ std::string Config::GetFullKey(const std::string& key, bool skipArrayIndex) {
int Config::BeginArray(const std::string& array) {
array_stack.push_back(ConfigArray{AdjustKey(array), 0, 0});
const int size = config->GetLongValue(GetSection().c_str(),
GetFullKey(std::string("size"), true).c_str(), 0);
array_stack.back().size = size;
return size;
const int size = config->GetLongValue(GetSection().c_str(), GetFullKey(std::string("size"), true).c_str(), 0);
array_stack.back().size = (std::max)(0, size);
return array_stack.back().size;
}
void Config::EndArray() {
@@ -1071,7 +1055,7 @@ void Config::EndArray() {
// Edge-case where the first array created doesn't have a name
config->SetValue(GetSection().c_str(), std::string("size").c_str(), ToString(size).c_str());
} else {
const auto key = GetFullKey(std::string("size"), true);
auto const key = GetFullKey(std::string("size"), true);
config->SetValue(GetSection().c_str(), key.c_str(), ToString(size).c_str());
}
+11 -4
View File
@@ -31,8 +31,10 @@ public:
using clock = std::chrono::system_clock;
explicit Socket(const std::string& host, u16 port, SocketCallback callback_)
: callback(std::move(callback_)), timer(io_context),
socket(io_context, udp::endpoint(udp::v4(), 0)), client_id(Common::Random::Random32(0)) {
: callback(std::move(callback_)), timer(io_context)
, socket(io_context, udp::endpoint(udp::v4(), 0))
, client_id(Common::Random::Random32(0))
{
boost::system::error_code ec{};
auto ipv4 = boost::asio::ip::make_address_v4(host, ec);
if (ec.value() != boost::system::errc::success) {
@@ -353,8 +355,13 @@ PadIdentifier UDPClient::GetPadIdentifier(std::size_t pad_index) const {
}
Common::UUID UDPClient::GetHostUUID(const std::string& host) const {
const auto ip = boost::asio::ip::make_address_v4(host);
const auto hex_host = fmt::format("00000000-0000-0000-0000-0000{:06x}", ip.to_uint());
boost::system::error_code ec{};
auto ip = boost::asio::ip::make_address_v4(host, ec);
if (ec.value() != boost::system::errc::success) {
LOG_ERROR(Input, "Invalid IPv4 address \"{}\" provided", host);
ip = boost::asio::ip::address_v4{};
}
auto const hex_host = fmt::format("00000000-0000-0000-0000-0000{:06x}", ip.to_uint());
return Common::UUID{hex_host};
}
@@ -301,6 +301,8 @@ std::unique_ptr<TranslationMap> InitializeTranslations(QObject* parent) {
tr("Controls the seed of the random number generator.\nMainly used for speedrunning."));
INSERT(Settings, rng_seed_enabled, QString(), QString());
INSERT(Settings, device_name, tr("Device Name"), tr("The name of the console."));
INSERT(Settings, program_args, tr("Homebrew Args"),
tr("Command-line arguments passed to homebrew at launch (e.g. -noglsl)."));
INSERT(Settings, custom_rtc, tr("Custom RTC Date:"),
tr("This option allows to change the clock of the console.\n"
"Can be used to manipulate time in games."));
+3 -4
View File
@@ -207,12 +207,11 @@ struct Values {
// Game List
Setting<bool> show_add_ons{linkage, true, "show_add_ons", Category::UiGameList};
Setting<u32> game_icon_size{linkage, 64, "game_icon_size", Category::UiGameList};
Setting<u32> folder_icon_size{linkage, 48, "folder_icon_size", Category::UiGameList};
Setting<u32, true> game_icon_size{linkage, 64, 8, 512, "game_icon_size", Category::UiGameList};
Setting<u32, true> folder_icon_size{linkage, 48, 8, 512, "folder_icon_size", Category::UiGameList};
Setting<u8> row_1_text_id{linkage, 3, "row_1_text_id", Category::UiGameList};
Setting<u8> row_2_text_id{linkage, 2, "row_2_text_id", Category::UiGameList};
Setting<Settings::GameListMode> game_list_mode{linkage, Settings::GameListMode::TreeView,
"game_list_mode", Category::UiGameList};
Setting<Settings::GameListMode> game_list_mode{linkage, Settings::GameListMode::TreeView, "game_list_mode", Category::UiGameList};
Setting<bool> show_game_name{linkage, true, "show_game_name", Category::UiGameList};
std::atomic_bool is_game_list_reload_pending{false};
@@ -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 2021 yuzu Emulator Project
@@ -240,6 +240,14 @@ constexpr size_t NUM_FIXEDFNCTEXTURE = 10;
return attribute >= Attribute::Generic0X && attribute <= Attribute::Generic31X;
}
[[nodiscard]] inline bool IsLegacyAttribute(Attribute attribute) noexcept {
return (attribute >= Attribute::ColorFrontDiffuseR &&
attribute <= Attribute::ColorBackSpecularA) ||
attribute == Attribute::FogCoordinate ||
(attribute >= Attribute::FixedFncTexture0S &&
attribute <= Attribute::FixedFncTexture9Q);
}
[[nodiscard]] inline u32 GenericAttributeIndex(Attribute attribute) {
if (!IsGeneric(attribute))
throw InvalidArgument("Attribute is not generic {}", attribute);
@@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@@ -163,12 +166,24 @@ void TranslatorVisitor::IPA(u64 insn) {
const IR::Attribute attribute{ipa.attribute};
IR::F32 value{is_indexed ? ir.GetAttributeIndexed(X(ipa.index_reg))
: ir.GetAttribute(attribute)};
if (IR::IsGeneric(attribute)) {
const ProgramHeader& sph{env.SPH()};
const u32 attr_index{IR::GenericAttributeIndex(attribute)};
const u32 element{static_cast<u32>(attribute) % 4};
const std::array input_map{sph.ps.GenericInputMap(attr_index)};
const bool is_perspective{input_map[element] == Shader::PixelImap::Perspective};
const bool is_legacy{IR::IsLegacyAttribute(attribute)};
if (IR::IsGeneric(attribute) || is_legacy) {
bool is_perspective{is_legacy &&
ipa.interpolation_mode != InterpolationMode::Sc};
if (!is_legacy) {
const ProgramHeader& sph{env.SPH()};
const u32 attr_index{IR::GenericAttributeIndex(attribute)};
const std::array input_map{sph.ps.GenericInputMap(attr_index)};
Shader::PixelImap effective_imap{Shader::PixelImap::Unused};
for (const Shader::PixelImap component : input_map) {
if (component != Shader::PixelImap::Unused) {
effective_imap = component;
break;
}
}
is_perspective = effective_imap == Shader::PixelImap::Perspective ||
effective_imap == Shader::PixelImap::Unused;
}
if (is_perspective) {
const IR::F32 position_w{ir.GetAttribute(IR::Attribute::PositionW)};
value = ir.FPMul(value, position_w);
@@ -132,13 +132,7 @@ void AddNVNStorageBuffers(IR::Program& program) {
}
}
bool IsLegacyAttribute(IR::Attribute attribute) {
return (attribute >= IR::Attribute::ColorFrontDiffuseR &&
attribute <= IR::Attribute::ColorBackSpecularA) ||
attribute == IR::Attribute::FogCoordinate ||
(attribute >= IR::Attribute::FixedFncTexture0S &&
attribute <= IR::Attribute::FixedFncTexture9Q);
}
using IR::IsLegacyAttribute; //rescoped to attribute.h to make it visible in load_store_attribute.cpp IPA
std::map<IR::Attribute, IR::Attribute> GenerateLegacyToGenericMappings(
const VaryingState& state, std::queue<IR::Attribute> unused_generics,
@@ -1644,8 +1644,6 @@ bool BufferCache<P>::SynchronizeBuffer(Buffer& buffer, DAddr device_addr, u32 si
start = (std::max)(start, gend);
});
push(start, end);
ClearDownload(addr, range_size);
gpu_modified_ranges.Subtract(addr, range_size);
});
if (upload_copies.empty()) {
return true;
+3
View File
@@ -1117,6 +1117,8 @@ void MacroJITx64Impl::Optimizer_ScanFlags() {
}
void MacroJITx64Impl::Compile() {
// Matching PROTECT_RE needed for W^X systems
setProtectMode(Xbyak::CodeArray::ProtectMode::PROTECT_RW);
labels.fill(Xbyak::Label());
Common::X64::ABI_PushRegistersAndAdjustStack(*this, Common::X64::ABI_ALL_CALLEE_SAVED, 8);
@@ -1164,6 +1166,7 @@ void MacroJITx64Impl::Compile() {
Common::X64::ABI_PopRegistersAndAdjustStack(*this, Common::X64::ABI_ALL_CALLEE_SAVED, 8);
ret();
ready();
setProtectMode(Xbyak::CodeArray::ProtectMode::PROTECT_RE);
program = getCode<ProgramType>();
}
+30 -40
View File
@@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@@ -9,28 +12,22 @@ namespace Tegra {
// https://github.com/NVIDIA/open-gpu-doc/blob/master/manuals/volta/gv100/dev_mmu.ref.txt
enum class PTEKind : u8 {
INVALID = 0xff,
PITCH = 0x00,
Z16 = 0x01,
Z16_2C = 0x02,
Z16_MS2_2C = 0x03,
Z16_MS4_2C = 0x04,
Z16_MS8_2C = 0x05,
Z16_MS16_2C = 0x06,
Z16_2Z = 0x07,
Z16_MS2_2Z = 0x08,
Z16_MS4_2Z = 0x09,
Z16_MS8_2Z = 0x0a,
Z16_MS16_2Z = 0x0b,
Z16_2CZ = 0x36,
Z16_MS2_2CZ = 0x37,
Z16_MS4_2CZ = 0x38,
Z16_MS8_2CZ = 0x39,
Z16_MS16_2CZ = 0x5f,
Z16_4CZ = 0x0c,
Z16_MS2_4CZ = 0x0d,
Z16_MS4_4CZ = 0x0e,
Z16_MS8_4CZ = 0x0f,
PITCH = 0x0,
Z16 = 0x1,
Z16_2C = 0x2,
Z16_MS2_2C = 0x3,
Z16_MS4_2C = 0x4,
Z16_MS8_2C = 0x5,
Z16_MS16_2C = 0x6,
Z16_2Z = 0x7,
Z16_MS2_2Z = 0x8,
Z16_MS4_2Z = 0x9,
Z16_MS8_2Z = 0xa,
Z16_MS16_2Z = 0xb,
Z16_4CZ = 0xc,
Z16_MS2_4CZ = 0xd,
Z16_MS4_4CZ = 0xe,
Z16_MS8_4CZ = 0xf,
Z16_MS16_4CZ = 0x10,
S8Z24 = 0x11,
S8Z24_1Z = 0x12,
@@ -43,7 +40,7 @@ enum class PTEKind : u8 {
S8Z24_MS4_2CZ = 0x19,
S8Z24_MS8_2CZ = 0x1a,
S8Z24_MS16_2CZ = 0x1b,
S8Z24_2CS = 0x1c,
S8Z24_2CS = 0x1C,
S8Z24_MS2_2CS = 0x1d,
S8Z24_MS4_2CS = 0x1e,
S8Z24_MS8_2CS = 0x1f,
@@ -57,6 +54,8 @@ enum class PTEKind : u8 {
V8Z24_MS4_VC4 = 0x27,
V8Z24_MS8_VC8 = 0x28,
V8Z24_MS8_VC24 = 0x29,
S8 = 0x2a,
S8_2S = 0x2b,
V8Z24_MS4_VC12_1ZV = 0x2e,
V8Z24_MS4_VC4_1ZV = 0x2f,
V8Z24_MS8_VC8_1ZV = 0x30,
@@ -99,15 +98,9 @@ enum class PTEKind : u8 {
Z24S8_MS8_4CSZV = 0x59,
Z24S8_MS16_4CSZV = 0x5a,
Z24V8_MS4_VC12 = 0x5b,
Z24V8_MS4_VC4 = 0x5c,
Z24V8_MS4_VC4 = 0x5C,
Z24V8_MS8_VC8 = 0x5d,
Z24V8_MS8_VC24 = 0x5e,
YUV_B8C1_2Y = 0x60,
YUV_B8C2_2Y = 0x61,
YUV_B10C1_2Y = 0x62,
YUV_B10C2_2Y = 0x6b,
YUV_B12C1_2Y = 0x6c,
YUV_B12C2_2Y = 0x6d,
Z24V8_MS4_VC12_1ZV = 0x63,
Z24V8_MS4_VC4_1ZV = 0x64,
Z24V8_MS8_VC8_1ZV = 0x65,
@@ -129,7 +122,7 @@ enum class PTEKind : u8 {
Z24V8_MS8_VC8_4CSZV = 0x79,
Z24V8_MS8_VC24_4CSZV = 0x7a,
ZF32 = 0x7b,
ZF32_1Z = 0x7c,
ZF32_1Z = 0x7C,
ZF32_MS2_1Z = 0x7d,
ZF32_MS4_1Z = 0x7e,
ZF32_MS8_1Z = 0x7f,
@@ -198,6 +191,9 @@ enum class PTEKind : u8 {
ZF32_X24S8_MS4_1CS = 0xc6,
ZF32_X24S8_MS8_1CS = 0xc7,
ZF32_X24S8_MS16_1CS = 0xc8,
SMASKED_MESSAGE = 0xca,
SMHOST_MESSAGE = 0xcb,
C64_MS2_2CRA = 0xcd,
ZF32_X24S8_2CSZV = 0xce,
ZF32_X24S8_MS2_2CSZV = 0xcf,
ZF32_X24S8_MS4_2CSZV = 0xd0,
@@ -208,9 +204,6 @@ enum class PTEKind : u8 {
ZF32_X24S8_MS4_2CS = 0xd5,
ZF32_X24S8_MS8_2CS = 0xd6,
ZF32_X24S8_MS16_2CS = 0xd7,
S8 = 0x2a,
S8_2S = 0x2b,
GENERIC_16BX2 = 0xfe,
C32_2C = 0xd8,
C32_2CBR = 0xd9,
C32_2CBA = 0xda,
@@ -218,13 +211,12 @@ enum class PTEKind : u8 {
C32_2BRA = 0xdc,
C32_MS2_2C = 0xdd,
C32_MS2_2CBR = 0xde,
C32_MS2_4CBRA = 0xcc,
C32_MS2_2CRA = 0xcc,
C32_MS4_2C = 0xdf,
C32_MS4_2CBR = 0xe0,
C32_MS4_2CBA = 0xe1,
C32_MS4_2CRA = 0xe2,
C32_MS4_2BRA = 0xe3,
C32_MS4_4CBRA = 0x2c,
C32_MS8_MS16_2C = 0xe4,
C32_MS8_MS16_2CRA = 0xe5,
C64_2C = 0xe6,
@@ -234,13 +226,11 @@ enum class PTEKind : u8 {
C64_2BRA = 0xea,
C64_MS2_2C = 0xeb,
C64_MS2_2CBR = 0xec,
C64_MS2_4CBRA = 0xcd,
C64_MS4_2C = 0xed,
C64_MS4_2CBR = 0xee,
C64_MS4_2CBA = 0xef,
C64_MS4_2CRA = 0xf0,
C64_MS4_2BRA = 0xf1,
C64_MS4_4CBRA = 0x2d,
C64_MS8_MS16_2C = 0xf2,
C64_MS8_MS16_2CRA = 0xf3,
C128_2C = 0xf4,
@@ -253,8 +243,8 @@ enum class PTEKind : u8 {
C128_MS8_MS16_2CR = 0xfb,
X8C24 = 0xfc,
PITCH_NO_SWIZZLE = 0xfd,
SMSKED_MESSAGE = 0xca,
SMHOST_MESSAGE = 0xcb,
GENERIC_16BX2 = 0xfe,
INVALID = 0xff,
};
constexpr bool IsPitchKind(PTEKind kind) {
@@ -203,6 +203,7 @@ Device::Device(Core::Frontend::EmuWindow& emu_window) {
max_varyings = GetInteger<u32>(GL_MAX_VARYING_VECTORS);
max_compute_shared_memory_size = GetInteger<u32>(GL_MAX_COMPUTE_SHARED_MEMORY_SIZE);
max_glasm_storage_buffer_blocks = GetInteger<u32>(GL_MAX_VERTEX_SHADER_STORAGE_BLOCKS);
max_user_clip_distances = GetInteger<u32>(GL_MAX_CLIP_DISTANCES);
has_warp_intrinsics = GLAD_GL_NV_gpu_shader5 && GLAD_GL_NV_shader_thread_group &&
GLAD_GL_NV_shader_thread_shuffle;
has_shader_ballot = GLAD_GL_ARB_shader_ballot;
+6 -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 2019 yuzu Emulator Project
@@ -47,6 +47,10 @@ public:
return max_compute_shared_memory_size;
}
u32 GetMaxUserClipDistances() const {
return max_user_clip_distances;
}
u32 GetMaxGLASMStorageBufferBlocks() const {
return max_glasm_storage_buffer_blocks;
}
@@ -202,6 +206,7 @@ private:
u32 max_varyings{};
u32 max_compute_shared_memory_size{};
u32 max_glasm_storage_buffer_blocks{};
u32 max_user_clip_distances{};
bool has_warp_intrinsics{};
bool has_shader_ballot{};
@@ -238,7 +238,11 @@ ShaderCache::ShaderCache(Tegra::MaxwellDeviceMemoryManager& device_memory_,
.ignore_nan_fp_comparisons = true,
.gl_max_compute_smem_size = device.GetMaxComputeSharedMemorySize(),
.min_ssbo_alignment = device.GetShaderStorageBufferAlignment(),
.max_user_clip_distances = 8,
// Use the host limit, but never more than the guest can produce. Maxwell exposes 8 clip
// distances and the SPIR-V output array is sized for at most 8, so clamping here keeps a
// host that reports a different count from under- or over-running that array.
.max_user_clip_distances =
std::min<u32>(device.GetMaxUserClipDistances(), Maxwell::Regs::NumClipDistances),
},
host_info{
.support_float64 = true,
@@ -89,11 +89,10 @@ std::string BuildCommaSeparatedExtensions(
} // Anonymous namespace
Device CreateDevice(const vk::Instance& instance, const vk::InstanceDispatch& dld,
VkSurfaceKHR surface) {
Device CreateDevice(const vk::Instance& instance, const vk::InstanceDispatch& dld, VkSurfaceKHR surface) {
const std::vector<VkPhysicalDevice> devices = instance.EnumeratePhysicalDevices();
const s32 device_index = Settings::values.vulkan_device.GetValue();
if (device_index < 0 || device_index >= static_cast<s32>(devices.size())) {
const u32 device_index = Settings::values.vulkan_device.GetValue();
if (device_index >= u32(devices.size())) {
LOG_ERROR(Render_Vulkan, "Invalid device index {}!", device_index);
throw vk::Exception(VK_ERROR_INITIALIZATION_FAILED);
}
+6
View File
@@ -254,6 +254,9 @@ std::optional<u64> GenericEnvironment::TryFindSize() {
static constexpr u64 SELF_BRANCH_A = 0xE2400FFFFF87000FULL;
static constexpr u64 SELF_BRANCH_B = 0xE2400FFFFF07000FULL;
static constexpr u64 MESA_EXIT_MASK = 0xFFF00000000F001FULL;
static constexpr u64 MESA_EXIT_VALUE = (0xE30ULL << 52) | (0x7ULL << 16) | 0xFULL;
code.resize(MAXIMUM_SIZE / INST_SIZE);
GPUVAddr guest_addr{program_base + start_address};
@@ -267,6 +270,9 @@ std::optional<u64> GenericEnvironment::TryFindSize() {
if (inst == SELF_BRANCH_A || inst == SELF_BRANCH_B) {
return offset + index;
}
if ((inst & MESA_EXIT_MASK) == MESA_EXIT_VALUE) {
return offset + index + INST_SIZE;
}
}
guest_addr += BLOCK_SIZE;
size += BLOCK_SIZE;
@@ -286,6 +286,12 @@ ankerl::unordered_dense::map<VkFormat, VkFormatProperties> GetFormatProperties(v
VK_FORMAT_R8_UNORM,
VK_FORMAT_R8_USCALED,
VK_FORMAT_S8_UINT,
VK_FORMAT_ETC2_R8G8B8_UNORM_BLOCK,
VK_FORMAT_ETC2_R8G8B8A8_UNORM_BLOCK,
VK_FORMAT_ETC2_R8G8B8A1_UNORM_BLOCK,
VK_FORMAT_ETC2_R8G8B8_SRGB_BLOCK,
VK_FORMAT_ETC2_R8G8B8A8_SRGB_BLOCK,
VK_FORMAT_ETC2_R8G8B8A1_SRGB_BLOCK,
};
ankerl::unordered_dense::map<VkFormat, VkFormatProperties> format_properties;
for (const auto format : formats) {
@@ -36,8 +36,8 @@ namespace {
}
[[nodiscard]] std::vector<const char*> RequiredExtensions(
const vk::InstanceDispatch& dld, Core::Frontend::WindowSystemType window_type,
bool enable_validation) {
const vk::InstanceDispatch& dld, std::vector<VkExtensionProperties> const& properties,
Core::Frontend::WindowSystemType window_type, bool enable_validation) {
std::vector<const char*> extensions;
extensions.reserve(6);
switch (window_type) {
@@ -74,14 +74,14 @@ namespace {
if (window_type != Core::Frontend::WindowSystemType::Headless) {
extensions.push_back(VK_KHR_SURFACE_EXTENSION_NAME);
}
if (auto const properties = vk::EnumerateInstanceExtensionProperties(dld); properties) {
// Probe optional extensions against the same snapshot the caller verifies against, so the
// check here and the verification in CreateInstance can never disagree (see TOCTOU note below).
#ifdef __APPLE__
if (AreExtensionsSupported(dld, *properties, std::array{VK_KHR_PORTABILITY_ENUMERATION_EXTENSION_NAME}))
extensions.push_back(VK_KHR_PORTABILITY_ENUMERATION_EXTENSION_NAME);
if (AreExtensionsSupported(dld, properties, std::array{VK_KHR_PORTABILITY_ENUMERATION_EXTENSION_NAME}))
extensions.push_back(VK_KHR_PORTABILITY_ENUMERATION_EXTENSION_NAME);
#endif
if (enable_validation && AreExtensionsSupported(dld, *properties, std::array{VK_EXT_DEBUG_UTILS_EXTENSION_NAME}))
extensions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME);
}
if (enable_validation && AreExtensionsSupported(dld, properties, std::array{VK_EXT_DEBUG_UTILS_EXTENSION_NAME}))
extensions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME);
return extensions;
}
@@ -128,9 +128,19 @@ vk::Instance CreateInstance(const Common::DynamicLibrary& library, vk::InstanceD
LOG_ERROR(Render_Vulkan, "Failed to load Vulkan function pointers");
throw vk::Exception(VK_ERROR_INITIALIZATION_FAILED);
}
std::vector<const char*> const extensions = RequiredExtensions(dld, window_type, enable_validation);
// Enumerate instance extensions exactly once. RequiredExtensions() used to enumerate a second
// time internally; if the driver returned a different set between the two calls (a real TOCTOU
// seen on some AMD iGPU drivers), an extension added to the list could be missing from the
// verification snapshot, throwing EXTENSION_NOT_PRESENT on an otherwise valid launch. Sharing
// one snapshot for both the optional-extension probe and the final check removes that window.
auto const properties = vk::EnumerateInstanceExtensionProperties(dld);
if (!properties || !AreExtensionsSupported(dld, *properties, extensions))
if (!properties) {
LOG_ERROR(Render_Vulkan, "Failed to query instance extension properties");
throw vk::Exception(VK_ERROR_EXTENSION_NOT_PRESENT);
}
std::vector<const char*> const extensions =
RequiredExtensions(dld, *properties, window_type, enable_validation);
if (!AreExtensionsSupported(dld, *properties, extensions))
throw vk::Exception(VK_ERROR_EXTENSION_NOT_PRESENT);
std::vector<const char*> layers = Layers(enable_validation);
RemoveUnavailableLayers(dld, layers);
@@ -94,11 +94,10 @@ void ConfigureMotionTouch::SetConfiguration() {
const Common::ParamPackage touch_param(Settings::values.touch_device.GetValue());
touch_from_button_maps = Settings::values.touch_from_button_maps;
for (const auto& touch_map : touch_from_button_maps) {
for (const auto& touch_map : touch_from_button_maps)
ui->touch_from_button_map->addItem(QString::fromStdString(touch_map.name));
}
ui->touch_from_button_map->setCurrentIndex(
Settings::values.touch_from_button_map_index.GetValue());
if (auto const index = Settings::values.touch_from_button_map_index.GetValue(); int(index) < ui->touch_from_button_map->count())
ui->touch_from_button_map->setCurrentIndex(index);
min_x = touch_param.Get("min_x", 100);
min_y = touch_param.Get("min_y", 50);
+3 -5
View File
@@ -183,7 +183,7 @@ void GameList::ResetViewMode() {
tree_view->setVisible(false);
break;
default:
break;
UNREACHABLE();
}
auto view = m_currentView->viewport();
@@ -196,10 +196,8 @@ void GameList::ResetViewMode() {
auto scroller = QScroller::scroller(view);
QScrollerProperties props;
props.setScrollMetric(QScrollerProperties::HorizontalOvershootPolicy,
QScrollerProperties::OvershootAlwaysOff);
props.setScrollMetric(QScrollerProperties::VerticalOvershootPolicy,
QScrollerProperties::OvershootAlwaysOff);
props.setScrollMetric(QScrollerProperties::HorizontalOvershootPolicy, QScrollerProperties::OvershootAlwaysOff);
props.setScrollMetric(QScrollerProperties::VerticalOvershootPolicy, QScrollerProperties::OvershootAlwaysOff);
scroller->setScrollerProperties(props);
if (m_isTreeMode != newTreeMode) {
+7 -9
View File
@@ -581,7 +581,7 @@ MainWindow::MainWindow(bool has_broken_vulkan)
} else if (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();
BootGame(QString::fromStdString(hbl_path), ApplicationAppletParameters());
BootGame(QString::fromStdString(hbl_path), LibraryAppletParameters(0x010000000000100Dull, Service::AM::AppletId::QLaunch));
}
}
}
@@ -1088,10 +1088,9 @@ void MainWindow::InitializeWidgets() {
aa_status_button->setFocusPolicy(Qt::NoFocus);
connect(aa_status_button, &QPushButton::clicked, [&] {
auto aa_mode = Settings::values.anti_aliasing.GetValue();
aa_mode = static_cast<Settings::AntiAliasing>(static_cast<u32>(aa_mode) + 1);
if (aa_mode == Settings::AntiAliasing::MaxEnum) {
aa_mode = Settings::AntiAliasing::None;
}
aa_mode = Settings::AntiAliasing(u32(aa_mode) + 1);
if (u32(aa_mode) > u32(Settings::EnumMetadata<Settings::AntiAliasing>::GetLast()))
aa_mode = Settings::EnumMetadata<Settings::AntiAliasing>::GetFirst();
Settings::values.anti_aliasing.SetValue(aa_mode);
aa_status_button->setChecked(true);
UpdateAAText();
@@ -3623,10 +3622,9 @@ void MainWindow::OnIncreaseVolume() {
void MainWindow::OnToggleAdaptingFilter() {
auto filter = Settings::values.scaling_filter.GetValue();
filter = static_cast<Settings::ScalingFilter>(static_cast<u32>(filter) + 1);
if (filter == Settings::ScalingFilter::MaxEnum) {
filter = Settings::ScalingFilter::NearestNeighbor;
}
filter = Settings::ScalingFilter(u32(filter) + 1);
if (u32(filter) > u32(Settings::EnumMetadata<Settings::ScalingFilter>::GetLast()))
filter = Settings::EnumMetadata<Settings::ScalingFilter>::GetFirst();
Settings::values.scaling_filter.SetValue(filter);
filter_status_button->setChecked(true);
UpdateFilterText();
+2
View File
@@ -28,6 +28,8 @@ Tools for Eden and other subprojects. When adding new scripts please use `#!/bin
- `clang-format.sh`: Runs `clang-format` on the entire codebase.
* Requires: clang
- `find-unused-strings.sh`: Find any unused strings in the Android app (XML -> Kotlin).
- `cpp-lint.sh`: Homemade dumb C++ linter.
- `fuzzsettings.cpp`: Fuzz settings files.
## Android
It's recommended to run these scritps after almost any Android change, as they are relatively fast and important both for APK bloat and CI.
+63
View File
@@ -0,0 +1,63 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#include <cstdio>
#include <cstdlib>
#include <cctype>
#include <cstring>
#include <ctime>
#include <string>
#include <string_view>
int main(int argc, char *argv[]) {
std::srand(unsigned(std::time(nullptr)));
FILE *fp = std::fopen(argv[1], "rt");
if (fp) {
char line[BUFSIZ];
while (std::fgets(line, sizeof(line), fp)) {
if (line[0] == '[') {
std::printf("%s", line);
} else if (std::isspace(line[0])) {
std::printf("%s", line);
} else {
char *p = std::strchr(line, '=');
if (std::strstr(line, "\\default") == nullptr) {
// not default
*p = '\0';
std::string new_line{line};
std::string value{p + 1};
if (value == "true" || value == "false") {
new_line += std::string{} + "=TreufLAlse857874FJJakshjryiu475" + '\n';
} else if (std::isdigit(value[0])) {
if (new_line == "size"
|| std::strstr(new_line.c_str(), "entries\\size") != nullptr
|| std::strstr(new_line.c_str(), "\\size")) {
new_line += "=-1\n";
} else {
new_line += '=' + std::to_string(int(std::rand())) + '\n';
}
} else {
std::string_view const cset{"03832///1/1/.1/1./1./1./1.1/.1194573290uwmgjouidyhiomHMNIODASJK,POF MSHDVLJPOIuksdtpsunmghns"};
std::string rst{"000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"};
for (size_t i = 0; i < rst.size(); ++i)
rst[i] = cset[std::rand() % cset.size()];
//new_line += "=\"" + rst + "\"";
new_line += "=" + value;
}
std::printf("%s", new_line.c_str());
} else {
// yes default
*p = '\0';
std::string new_line{line};
std::string value{p + 1};
new_line += "=false\n";
std::printf("%s", new_line.c_str());
}
}
}
std::fclose(fp);
}
return 0;
}
+12
View File
@@ -0,0 +1,12 @@
#!/bin/sh -ex
# SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
# SPDX-License-Identifier: GPL-3.0-or-later
ROOTDIR=$(CDPATH='' cd -- "$(dirname -- "$0")/" && pwd)
touch "$2"
c++ "$ROOTDIR/fuzzsettings.cpp" -o fuzzsettings
./fuzzsettings "$1" >"$2"
rm fuzzsettings