Compare commits

...

26 Commits

Author SHA1 Message Date
xbzk a0bc8bfde4 [video_core] Avoid stale macro upload references 2026-08-01 19:44:23 -03:00
xbzk e5b247db35 [frontend] Expose nxlink server mode 2026-08-01 19:44:23 -03:00
xbzk 44c0815dcd [loader] Add nxlink log server mode 2026-08-01 19:44:23 -03:00
xbzk b468602ffa [loader] Preserve nxlink argv markers 2026-08-01 19:44:23 -03:00
xbzk bc7219fd01 [network] Return in-progress for nonblocking connect 2026-08-01 19:44:23 -03:00
xbzk a1caf852ff [fs] Preserve guest file open modes in real VFS 2026-08-01 19:44:23 -03:00
xbzk 801c472017 [input] added option to disable wgi/xinput to prevent SDL GUIDE hack 2026-08-01 19:44:23 -03:00
xbzk 53a98f9de1 [qt_common] Avoid FS factory refresh while powered
Skip FileSystemController factory recreation during game-list repopulation while emulation is powered on.

This avoids poking live FS/VFS state during homebrew self-update and in-place NextLoad flows.
2026-08-01 19:44:22 -03:00
xbzk 6bad422d08 [core] Support libnx homebrew NextLoad handoff
Implement the homebrew NextLoad path used by libnx NROs to request another NRO from svcExitProcess.

Keep the existing process alive, rebuild the homebrew config and argv buffers, reset thread context, refresh process metadata, and add memory/address-space fallbacks needed for repeated in-place handoffs.

Reference: https://switchbrew.github.io/libnx/env_8h.html
2026-08-01 19:44:22 -03:00
xbzk 3469f3789f [nvdrv] Reset process resources for homebrew handoff
Track NVDRV sessions by process and aruid so in-place homebrew handoffs can close process-owned device files and sessions before loading the next NRO.

Also unlock nvmap device-shared pages during session cleanup to avoid stale GPU mappings leaking across repeated handoffs.
2026-08-01 19:44:22 -03:00
xbzk 221ffea4c1 [fsp] Preserve homebrew cwd for SDMC root aliases
Carry the initial homebrew working directory through filesystem process registration and FSP current-process state.

Use that cwd to resolve the homebrew cwd-plus-double-slash alias back to the SDMC root, allowing file browsers to navigate above their launch directory.
2026-08-01 19:44:22 -03:00
xbzk c87f6202d3 [fs] Allow real VFS files to be replaced while open
Add a Windows share-delete file open mode and use it for cached real VFS files. Close cached references before create, move, and delete so guest-side self-update flows can rename or replace files that Eden previously opened.

Also preserve Android real VFS full paths so homebrew path derivation does not lose the original file path.
2026-08-01 16:46:42 -03:00
xbzk 8ab9521cea [video_core] Restrict macro JIT zero-register skips
Only apply the zero-register ALU skip when the operation is safe to elide without changing carry/result semantics.

This avoids invalid-instruction floods seen with Macro JIT enabled while keeping the optimization for operations where a zero source is harmless.
2026-08-01 16:26:57 -03:00
Maufeat 612409c7ba [hid] Add Quaternion to ReloadInput (#4240)
- [x] I have read and followed the [Contribution Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/CONTRIBUTING.md#code-contributions).
- [x] I have read and followed the [AI Policy](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/AI.md)
- [x] I have read and followed the [Coding Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/Coding.md) to the best of my ability.

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

Adds Quaternion to ReloadInput. What does it fix? Displays correct space in VR (only test on SSBU) not tested any further.

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4240
Reviewed-by: Lizzie <lizzie@eden-emu.dev>
Reviewed-by: CamilleLaVey <camillelavey99@gmail.com>
2026-07-31 19:45:38 +02:00
simply0001 54046ac60e [video_core/macro] check HLE hashes before compiling (#4236)
- [x] I have read and followed the [Contribution Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/CONTRIBUTING.md#code-contributions).
- [x] I have read and followed the [AI Policy](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/AI.md)
- [x] I have read and followed the [Coding Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/Coding.md) to the best of my ability.

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

Known HLE macros are identified by a hash, but MacroEngine compiled them first and and afterwards it threw the compiled program away when the hash matched. This fix makes it so it checks the hash first and caches the HLE implementation directly, so it only compiles when the hash is unknown or if HLE is disabled.

Cached macros were also constantly checking the hash again and walking through each `std::get_if` until their variant matched. So I dispatched them through `std::visit` instead, and keep one resolved code span for hashing, compiling, and dumping so mid-method uploads use the right range.

Continues the macro hot path work from [#4067](https://git.eden-emu.dev/eden-emu/eden/pulls/4067)

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4236
Reviewed-by: Shinmegumi <shinmegumi@eden-emu.dev>
Reviewed-by: Lizzie <lizzie@eden-emu.dev>
2026-07-30 06:25:43 +02:00
lizzie 39763e7321 [core/hle/services/am] nuke ButtonPoller and Mouse thread (#4229)
Signed-off-by: lizzie <lizzie@eden-emu.dev>

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

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

doesn't even do anything now, please check if controller i/o is affected by this change
this should also fix latency issues with the mouse
the ButtonPoller thread isn't longer required since we update the state immediately with the .on_change callback registered

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4229
Reviewed-by: CamilleLaVey <camillelavey99@gmail.com>
2026-07-26 05:11:03 +02:00
PavelBARABANOV e69415c07b [android] disable VP8 MediaCodec decoder due to crashes in Diablo II (#4231)
Hotfix, in the future an improved handling on VP8 will be introduced for Android.

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4231
Reviewed-by: CamilleLaVey <camillelavey99@gmail.com>
2026-07-26 04:24:04 +02:00
lizzie dc1486485b [core/hle/service] move 'loader'+'jit' to guest thread (#4148)
loader is barely called whereas audio for example is called super often
thus demote loaderr

jit is only ever used by Super Mario 3D stars, and no other
games require this to be a separate jit thread
additionally even of 3D stars this is called often chary
so no; we dont need a dedicated thread for it either way

this pr should help low power devices/devices with less cores/threads
to schedule the existing emulator threads more efficiently
also moving to guest means the CPU threads get more loaded with
stuffings, which is a good thing as most of the time theyre
sleeping and/or waiting for a mutex

Signed-off-by: lizzie <lizzie@eden-emu.dev>
Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4148
Reviewed-by: Maufeat <sahyno1996@gmail.com>
Reviewed-by: CamilleLaVey <camillelavey99@gmail.com>
2026-07-25 23:46:59 +02:00
lizzie f561a10bd8 [core/core] GPU initialize before CPU (#4228)
Signed-off-by: lizzie <lizzie@eden-emu.dev>

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

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

should fix issue where vulkan dies trying to do locks or whatever, since CPU will infinitely stall or whatever
tl;dr: dynarmic is getting too good and outpacing initialization

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4228
Reviewed-by: Maufeat <sahyno1996@gmail.com>
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
2026-07-25 23:34:22 +02:00
lizzie 6272f8ab24 [hle/service/am] make EventObserver use jthread() (#3971)
I don't remember why this wasn't done earlier, did I miss something or I purpousefully avoided this due to an issue?

Please test NO HANGS when opening/closing/playing

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

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/3971
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
Reviewed-by: Maufeat <sahyno1996@gmail.com>
2026-07-25 22:33:01 +02:00
lizzie eebb4bd91e [eden-cli] add extra CLI options -n/-x/-s (#4173)
from wasm PR:
- `--null-render/-n`: Forces the usage of the "Null" render backend irrespective of settings.
- `--filter/-x`: Sets the debug log filter irrespective of settings.
- `--singlecore/-s`: Forces single-core regardless of settings.

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

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4173
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
Reviewed-by: Maufeat <sahyno1996@gmail.com>
2026-07-25 22:20:31 +02:00
lizzie 58dee53305 [fmt] use {#:x} instead of 0x{:#x} (#4170)
continuation of #309 but applying to even more files than before :)
also makes them lowercase because `0xfafafa` is better as `0XFAFAFA`

Signed-off-by: lizzie <lizzie@eden-emu.dev>
Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4170
Reviewed-by: CamilleLaVey <camillelavey99@gmail.com>
Reviewed-by: Maufeat <sahyno1996@gmail.com>
2026-07-25 21:47:07 +02:00
lizzie 7b13113fbe [dynarmic] flatten terminal variants to not use recursive pointers (#4218)
take If{then, else} for example
100% of the time If{} only has a leaf terminal, supporting If{If{}, If{}} is very dumb and we don't need that
so remove that
this reduces
> The recursive_wrapper class template has an interface similar to a simple value container, but its content is allocated dynamically

aka. uses malloc and spams heap EVERYTIME A CONDITIONAL TERMINAL IS USED
thats bad tbf, i dont like it, removing it is better for general speedup
also we dont REALLY need if{if{if{if{}}}} support, like really

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

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4218
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
Reviewed-by: Maufeat <sahyno1996@gmail.com>
2026-07-25 21:43:58 +02:00
crueter b9a88297cb [fs] fix crash on '#' comments in pchtxt patches (#4124)
Adds `#` as a valid pchtxt comment, and fixes a crash that could occur
when using odd-length values

This patch was sent by Adam Kittelson <adam@apathydrive.com>

Signed-off-by: crueter <crueter@eden-emu.dev>
Co-authored-by: Cole Avenue <cole@melisand.re>
Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4124
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
Reviewed-by: Lizzie <lizzie@eden-emu.dev>
2026-07-25 21:20:04 +02:00
Eden CI 0133caf702 [dist, android] Update translations from Transifex for Jul 25 (#4227)
Automatic translation update for Jul 25

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4227
2026-07-25 16:23:33 +02:00
crueter 9d60030af5 [android] Support Joy-Con D-pad buttons (#4225)
Closes eden-emulator/Issue-Reports#8
Closes eden-emulator/Issue-Reports#428

Currently, [Android's input layer](https://android.googlesource.com/platform/frameworks/base.git/+/refs/heads/android16-release/data/keyboards/Vendor_057e_Product_2009.kl#54) doesn't return `KEYCODE_DPAD_*` for the left Joy-Con, we fall back to the scan code in this case, this will also fix most third party Joy-Con controllers.

- The scan codes are not reliable and may vary from device to another. I searched for (0x220-0x223) values in the [android repo](https://android.googlesource.com/platform/frameworks/base.git/+/refs/heads/android16-release/data/keyboards) and they all have the same DPAD_* key mapping, so not a big issue just might cause future issues if Google change the mapping in another vendor. I could simply limit the fix to the [left Joy-Con](https://github.com/torvalds/linux/blob/master/drivers/hid/hid-ids.h#L1096-L1105) `if (event.keyCode == 0 && event.device.vendorId == 0x057e && event.device.productId == 0x2006)`.
- Auto mapping doesn't work, I'm not very familiar with the JNI Specification, I managed to implement the below workaround, it basically checks if it's the left Joy-Con and add the first four DPAD_* keycodes
  ```cpp
  // ./src/input_common/drivers/android.cpp
  ButtonMapping Android::GetButtonMappingForDevice(const Common::ParamPackage& params) {
      // ...
      const char *yuzu_device_name = env->GetStringUTFChars((jstring) env->CallObjectMethod(j_device, Common::Android::GetYuzuDeviceGetName()), &isCopy);
      const char *switch_left_string_name = "Nintendo Switch Left Joy-Con";
      bool is_switch_left = strncmp(yuzu_device_name, switch_left_string_name, strlen(switch_left_string_name)) == 0;

      std::set<s32> available_keys;
      for (size_t i = 0; i < keycode_ids.size(); ++i) {
          if (j_has_keys[i] || (is_switch_left && i <= 3)) {
              available_keys.insert(keycode_ids[i]);
          }
      }
      // ...
  }
  ```

Signed-off-by: crueter <crueter@eden-emu.dev>
Authored-by: Anas Bouzid <bouzid.anas.1@gmail.com>
Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4225
Reviewed-by: Lizzie <lizzie@eden-emu.dev>
Reviewed-by: Maufeat <sahyno1996@gmail.com>
2026-07-25 15:09:16 +02:00
161 changed files with 2857 additions and 884 deletions
+3 -1
View File
@@ -804,7 +804,9 @@ Particles tend to only render correctly with Accurate mode.</source>
<source>Controls the DMA read mode.
Unsafe is faster, while Safe is more stable and can fix issues in some games.
Default follows the GPU Accuracy setting.</source>
<translation type="unfinished"/>
<translation>Controla el modo de lectura DMA.
Inseguro es más rápido, mientras que seguro es más estable y puede solucionar fallos en algunos juegos.
Predeterminado sigue el ajuste de la precisión de GPU.</translation>
</message>
<message>
<location filename="../../src/qt_common/config/shared_translation.cpp" line="227"/>
+3 -1
View File
@@ -793,7 +793,9 @@ Safe to set at 16x on most GPUs.</source>
<source>Controls the GPU emulation mode.
Most games render fine with Fast, but Accurate is still required for some.
Particles tend to only render correctly with Accurate mode.</source>
<translation type="unfinished"/>
<translation>Керує режимом емуляції ГП.
Більшість ігор добре візуалізуються з режимом «Швидко», але деякі ігри можуть потребувати режиму «Точно».
Частинки зазвичай правильно візуалізуються лише з режимом «Точно».</translation>
</message>
<message>
<location filename="../../src/qt_common/config/shared_translation.cpp" line="225"/>
+3
View File
@@ -27,3 +27,6 @@ There are two main applications, an SDL-based app (`eden-cli`) and a Qt based ap
- `--user/-u`: Specify the user index.
- `--version/-v`: Display version and quit.
- `--input-profile/-i`: Specifies input profile name to use (for player #0 only).
- `--null-render/-n`: Forces the usage of the "Null" render backend irrespective of settings.
- `--filter/-x`: Sets the debug log filter irrespective of settings.
- `--singlecore/-s`: Forces single-core regardless of settings.
@@ -50,6 +50,7 @@ enum class IntSetting(override val key: String) : AbstractIntSetting {
GPU_UNSWIZZLE_TEXTURE_SIZE("gpu_unswizzle_texture_size"),
GPU_UNSWIZZLE_STREAM_SIZE("gpu_unswizzle_stream_size"),
GPU_UNSWIZZLE_CHUNK_SIZE("gpu_unswizzle_chunk_size"),
HOMEBREW_NXLINK_SERVER_MODE("homebrew_nxlink_server_mode"),
BAT_TEMPERATURE_UNIT("bat_temperature_unit"),
CABINET_APPLET("cabinet_applet_mode"),
CONTROLLER_APPLET("controller_applet_mode"),
@@ -132,6 +132,15 @@ abstract class SettingsItem(
descriptionId = R.string.program_args_description
)
)
put(
SingleChoiceSetting(
IntSetting.HOMEBREW_NXLINK_SERVER_MODE,
titleId = R.string.nxlink_server_mode,
descriptionId = R.string.nxlink_server_mode_description,
choicesId = R.array.nxlinkServerModeEntries,
valuesId = R.array.nxlinkServerModeValues
)
)
put(
SwitchSetting(
BooleanSetting.RENDERER_USE_SPEED_LIMIT,
@@ -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: 2024 yuzu Emulator Project
@@ -169,7 +169,7 @@ class InputDialogFragment : DialogFragment() {
NativeInput.onGamePadButtonEvent(
controllerData.getGUID(),
controllerData.getPort(),
event.keyCode,
InputHandler.getButtonIdFromEvent(event),
action
)
onInputReceived(event.device)
@@ -1288,6 +1288,7 @@ class SettingsFragmentPresenter(
add(ShortSetting.DEBUG_KNOBS.key)
add(StringSetting.PROGRAM_ARGS.key)
add(IntSetting.HOMEBREW_NXLINK_SERVER_MODE.key)
if (!NativeConfig.isPerGameConfigLoaded()) {
add(HeaderSetting(R.string.gpu_logging_header))
@@ -49,6 +49,12 @@ object InputHandler {
MotionEvent.AXIS_RTRIGGER
)
// Currently, Android doesn't support Joy-Con D-pad buttons. We fall back to the scan code
private const val LINUX_BUTTON_DPAD_UP = 0x220
private const val LINUX_BUTTON_DPAD_DOWN = 0x221
private const val LINUX_BUTTON_DPAD_LEFT = 0x222
private const val LINUX_BUTTON_DPAD_RIGHT = 0x223
fun isPhysicalGameController(device: InputDevice?): Boolean {
device ?: return false
@@ -87,12 +93,25 @@ object InputHandler {
NativeInput.onGamePadButtonEvent(
controllerData.getGUID(),
controllerData.getPort(),
event.keyCode,
getButtonIdFromEvent(event),
action
)
return true
}
fun getButtonIdFromEvent(event: KeyEvent): Int {
if (event.keyCode == 0) {
return when (event.scanCode) {
LINUX_BUTTON_DPAD_UP -> KeyEvent.KEYCODE_DPAD_UP
LINUX_BUTTON_DPAD_DOWN -> KeyEvent.KEYCODE_DPAD_DOWN
LINUX_BUTTON_DPAD_LEFT -> KeyEvent.KEYCODE_DPAD_LEFT
LINUX_BUTTON_DPAD_RIGHT -> KeyEvent.KEYCODE_DPAD_RIGHT
else -> return 0
}
}
return event.keyCode
}
fun dispatchGenericMotionEvent(event: MotionEvent): Boolean {
val controllerData =
androidControllers[event.device.controllerNumber] ?: return false
@@ -473,7 +473,7 @@
<string name="advanced">متقدم</string>
<string name="renderer_accuracy">وضع وحدة معالجة الرسومات</string>
<string name="renderer_accuracy_description">يتحكم في وضع محاكاة وحدة معالجة الرسومات. تعمل معظم الألعاب بشكل جيد مع وضعي سريع أو متوازن، لكن الوضع الدقيق لا يزال مطلوبًا لبعض الألعاب. تميل الجسيمات إلى العرض بشكل صحيح فقط عند استخدام الوضع الدقيق.</string>
<string name="renderer_accuracy_description">يتحكم هذا الخيار في وضع محاكاة وحدة معالجة الرسومات. تعمل معظم الألعاب بشكل جيد مع الوضع السريع، لكن الوضع الدقيق لا يزال ضروريًا لبعضها. تميل الجسيمات إلى الظهور بشكل صحيح فقط مع الوضع الدقيق.</string>
<string name="dma_accuracy">دقة DMA</string>
<string name="dma_accuracy_description">يتحكم في دقة DMA. يمكن أن تؤدي الدقة الآمنة إلى حل المشكلات في بعض الألعاب، ولكنها قد تؤثر أيضًا على الأداء في بعض الحالات. إذا لم تكن متأكدًا، فاترك هذا الخيار على الإعداد الافتراضي.</string>
<string name="gpu_fence_behavior">سلوك حاجز وحدة معالجة الرسومات</string>
@@ -445,7 +445,6 @@
<string name="advanced">Pokročilé</string>
<string name="renderer_accuracy">Režim GPU</string>
<string name="renderer_accuracy_description">Určuje režim emulovaného GPU. Většina her běží bez problémů v rychlém, nebo vyváženém režimu, ale některé stále vyžadují přesný režim. Částicové efekty se většinou zobrazují korektně pouze v přesném režimu. </string>
<string name="dma_accuracy">Přesnost DMA</string>
<string name="dma_accuracy_description">Ovládá přesnost DMA. Bezpečná přesnost může vyřešit problémy v některých hrách, ale v některých případech může také ovlivnit výkon. Pokud si nejste jisti, použijte výchozí nastavení.</string>
<string name="anisotropic_filtering">Anizotropní filtrování</string>
@@ -457,7 +457,6 @@ Wird der Handheld-Modus verwendet, verringert es die Auflösung und erhöht die
<string name="advanced">Erweitert</string>
<string name="renderer_accuracy">GPU-Modus</string>
<string name="renderer_accuracy_description">Steuert den GPU-Emulationsmodus. Die meisten Spiele werden im Modus \"Fast\" oder \"Balanced\" gut gerendert, für einige ist jedoch weiterhin der Modus \"Accurate\" erforderlich. Partikel werden in der Regel nur im Modus \"Accurate\" korrekt gerendert.</string>
<string name="dma_accuracy">DMA-Genauigkeit</string>
<string name="dma_accuracy_description">Steuert die DMA-Präzisionsgenauigkeit. Sichere Präzision kann Probleme in einigen Spielen beheben, kann aber in einigen Fällen auch die Leistung beeinträchtigen. Im Zweifel lassen Sie dies auf Standard stehen.</string>
<string name="anisotropic_filtering">Anisotrope Filterung</string>
@@ -467,7 +467,6 @@
<string name="advanced">Avanzado</string>
<string name="renderer_accuracy">Modo de la GPU</string>
<string name="renderer_accuracy_description">Controla el modo de la emulación de la GPU. La mayoría de los juegos se renderizan correctamente en los modos Rápido o Equilibrado, pero algunos requieren Preciso. Las partículas tienden a renderizarse correctamente solo con el modo Preciso.</string>
<string name="dma_accuracy">Precisión de DMA</string>
<string name="dma_accuracy_description">Controla la precisión de DMA. La precisión segura puede solucionar problemas en algunos juegos, pero también puede afectar al rendimiento en algunos casos. Si no está seguro, déjelo en Predeterminado.</string>
<string name="gpu_fence_behavior">Comportamiento de vallado de la GPU</string>
@@ -445,7 +445,6 @@
<string name="advanced">Zaawansowane</string>
<string name="renderer_accuracy">Tryb GPU</string>
<string name="renderer_accuracy_description">Steruje trybem emulacji GPU. Większość gier renderuje się poprawnie w trybach Szybki lub Zrównoważony, ale dla niektórych nadal wymagany jest tryb Dokładny. Efekty cząsteczkowe zwykle renderują się poprawnie tylko w trybie Dokładnym.</string>
<string name="dma_accuracy">Dokładność DMA</string>
<string name="dma_accuracy_description">Kontroluje dokładność precyzji DMA. Bezpieczna precyzja może naprawić problemy w niektórych grach, ale w niektórych przypadkach może również wpłynąć na wydajność. Jeśli nie jesteś pewien, pozostaw wartość Domyślną.</string>
<string name="anisotropic_filtering">Filtrowanie anizotropowe</string>
@@ -424,6 +424,9 @@
<string name="cpu_accuracy">Точность ЦП</string>
<string name="value_with_units">%1$s%2$s</string>
<string name="program_args">Аргументы Homebrew</string>
<string name="program_args_description">Аргументы командной строки, переданные Homebrew при запуске (например, -noglsl)</string>
<!-- System settings strings -->
<string name="device_name">Название устройства</string>
<string name="use_docked_mode">Режим док-станции</string>
@@ -466,9 +469,11 @@
<string name="advanced">Расширенные</string>
<string name="renderer_accuracy">Режим ГПУ</string>
<string name="renderer_accuracy_description">Управляет режимом эмуляции графического процессора. Большинство игр нормально отображаются в режимах «Быстрый» или «Сбалансированный», но для некоторых требуется режим «Точный». Частицы обычно корректно отображаются только в режиме «Точный».</string>
<string name="renderer_accuracy_description">Управляет режимом эмуляции ГПУ. Большинство игр нормально отображаются в режиме Быстрый, но для некоторых требуется режим Точный. Частицы обычно корректно отображаются только в режиме Точный.</string>
<string name="dma_accuracy">Точность DMA</string>
<string name="dma_accuracy_description">Управляет точностью DMA. Безопасная точность может исправить проблемы в некоторых играх, но в некоторых случаях также может повлиять на производительность. Если не уверены, оставьте значение По умолчанию.</string>
<string name="gpu_fence_behavior">Поведение барьеров ГПУ</string>
<string name="gpu_fence_behavior_description">Управляет поведением синхронизации через барьеры ГПУ. Мгновенный — самый быстрый вариант, но может вызывать некоторые проблемы. Сбалансированный обеспечивает лучшую совместимость и может исправлять проблемы в некоторых играх. Точный ещё больше повышает совместимость ценой некоторого снижения производительности. Строгий — самый медленный вариант, но может исправлять проблемы, требующие более строгой синхронизации. По умолчанию соответствует настройке точности ГПУ.</string>
<string name="anisotropic_filtering">Анизотропная фильтрация</string>
<string name="anisotropic_filtering_description">Улучшает качество текстур под углом</string>
<string name="vram_usage_mode">Режим VRAM</string>
@@ -490,6 +495,8 @@
<string name="renderer_reactive_flushing_description">Повышение точности рендеринга в некоторых играх за счет снижения производительности.</string>
<string name="enable_buffer_history">Включить историю буфера</string>
<string name="enable_buffer_history_description">Позволяет обращаться к предыдущим состояниям буфера. Эта опция может повысить качество рендеринга и стабильность производительности в некоторых играх.</string>
<string name="enable_gpu_buffer_readback">Включить обратное чтение буфера ГПУ</string>
<string name="enable_gpu_buffer_readback_description">Сохраняет измененные ГПУ данные буфера путем чтения их обратно перед выгрузками. Некоторые игры требуют этого, чтобы рендерить определенные эффекты правильно. Может вызывать проблемы если оборудование не может обработать дополнительную рабочую нагрузку.</string>
<string name="use_optimized_vertex_buffers">Оптимизированные вершинные буферы</string>
<string name="use_optimized_vertex_buffers_description">Включает оптимизированную привязку вершинного буфера для повышения производительности. Требует Mesa Turnip 26.0+ / QCOM. Приводит к вылету на старых версиях драйверов Turnip (25.3 и ниже).</string>
@@ -564,6 +571,12 @@
<string name="gpu_log_level_description">Уровень детализации логов ГПУ (больше значение = больше деталей, выше нагрузка)</string>
<string name="gpu_log_vulkan_calls">Записывать вызовы Vulkan API</string>
<string name="gpu_log_vulkan_calls_description">Отслеживать все вызовы Vulkan API в кольцевом буфере</string>
<string name="gpu_log_shader_dumps">Выгрузить SPIR-V шейдеры</string>
<string name="gpu_log_shader_dumps_description">Сохранять перекомпилированные SPIR-V бинарные файлы (.spv) в папку дампа. Проверять с помощью spirv-dis/spirv-cross/spirv-val.</string>
<string name="dump_guest_shaders">Выгрузить гостевые (Maxwell) шейдеры</string>
<string name="dump_guest_shaders_description">Сохранять файлы байткода гостевых шейдеров Maxwell (*.ash) в папку дампа. Проверять с помощью nvdisasm.</string>
<string name="dump_macros">Выгрузить макросы Maxwell</string>
<string name="dump_macros_description">Сохранять файлы макропрограмм Maxwell (*.macro) в папку дампа. Проверять с помощью envydis.</string>
<string name="gpu_log_memory_tracking">Отслеживать память ГПУ</string>
<string name="gpu_log_memory_tracking_description">Мониторить выделение и освобождение памяти ГПУ</string>
<string name="gpu_log_driver_debug">Отладочная информация драйвера</string>
@@ -990,6 +1003,13 @@
<string name="dma_accuracy_unsafe">Небезопасно</string>
<string name="dma_accuracy_safe">Безопасный</string>
<!-- GPU Fence Behavior -->
<string name="gpu_fence_behavior_default">По умолчанию</string>
<string name="gpu_fence_behavior_immediate">Мгновенный</string>
<string name="gpu_fence_behavior_balanced">Сбалансированный</string>
<string name="gpu_fence_behavior_accurate">Точный</string>
<string name="gpu_fence_behavior_strict">Строгий</string>
<string name="vram_usage_conservative">Консервативный</string>
<string name="vram_usage_aggressive">Агрессивный</string>
@@ -469,7 +469,6 @@
<string name="advanced">Додаткові</string>
<string name="renderer_accuracy">Режим ГП</string>
<string name="renderer_accuracy_description">Керує режимом емуляції ГП. Більшість ігор добре візуалізуються з режимами «Швидко» або «Збалансовано», але деякі ігри можуть потребувати режиму «Точно». Частинки зазвичай правильно візуалізуються лише з режимом «Точно».</string>
<string name="dma_accuracy">Точність DMA</string>
<string name="dma_accuracy_description">Керує точністю DMA. Безпечна точність може виправити проблеми в деяких іграх, але в деяких випадках також може вплинути на продуктивність. Якщо не впевнені, залиште це значення за замовчуванням.</string>
<string name="anisotropic_filtering">Анізотропне фільтрування</string>
@@ -463,7 +463,7 @@
<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="gpu_fence_behavior">GPU 围栏行为</string>
@@ -3,7 +3,7 @@
<string name="app_disclaimer">本軟體可執行Nintendo Switch主機的遊戲,軟體不提供遊戲和金鑰檔案。&lt;br/> &lt;br/>在開始之前,請先安裝您的 &lt;<![CDATA[ &lt;b>prod.keys&lt;/b> ]]> 檔案,&lt;br />&lt;br /><![CDATA[<a href=\"https://yuzu-mirror.github.io/help/quickstart\">了解更多</a>]]></string>
<string name="notice_notification_channel_name">通知和錯誤</string>
<string name="notice_notification_channel_description">發生錯誤時顯示通知</string>
<string name="notice_notification_channel_description">發生錯誤時顯示通知</string>
<string name="notification_permission_not_granted">未授予通知權限!</string>
<string name="app_notification_channel_description">Eden模擬器通知</string>
<string name="app_notification_running">Eden正在執行</string>
@@ -16,7 +16,7 @@
<string name="value_too_high">範圍最大必須為%1$d</string>
<string name="invalid_value">無效的範圍</string>
<string name="using_per_game_config">使用自定義組態</string>
<string name="using_per_game_config">使用個別設定</string>
<!-- Input Overlay -->
<string name="show_input_overlay">顯示虛擬按鍵</string>
@@ -25,7 +25,7 @@
<string name="overlay_snap_to_grid_description">編輯時將虛擬按鍵與網格對齊</string>
<string name="overlay_grid_size">網格大小</string>
<string name="overlay_grid_size_description">調整網格格線間距</string>
<string name="input_overlay_behavior">行為模式</string>
<string name="input_overlay_behavior">隱藏</string>
<string name="overlay_auto_hide">自動隱藏虛擬按鍵</string>
<string name="overlay_auto_hide_description">在未使用虛擬按鍵幾秒後自動隱藏</string>
<string name="enable_input_overlay_auto_hide">啟用自動隱藏虛擬按鍵</string>
@@ -46,7 +46,7 @@
<string name="shaders_suffix">著色器</string>
<string name="charging">(充電中)</string>
<string name="system_info_label">系統:</string>
<string name="system_info_label">系統</string>
<string name="show_stats_overlay">顯示效能統計疊加層</string>
<string name="stats_overlay_customization">自訂</string>
<string name="stats_overlay_items">可見項目</string>
@@ -54,18 +54,20 @@
<string name="enable_stats_overlay_">啟用效能統計疊加層</string>
<string name="stats_overlay_options_description">設定疊加層中顯示的資訊</string>
<string name="show_fps">顯示FPS</string>
<string name="show_fps_description">顯示當前</string>
<string name="show_frametime">顯示時間</string>
<string name="show_fps_description">顯示當前影格</string>
<string name="show_frametime">顯示影格時間</string>
<string name="show_app_ram_usage">顯示應用程式的記憶體用量</string>
<string name="show_app_ram_usage_description">顯示模擬器正在使用的記憶體量</string>
<string name="show_app_ram_usage_description">顯示模擬器的記憶體</string>
<string name="show_system_ram_usage">顯示系統記憶體用量</string>
<string name="show_system_ram_usage_description">顯示系統使用的記憶體量</string>
<string name="show_system_ram_usage_description">顯示系統的記憶體</string>
<string name="show_bat_temperature">顯示電池溫度</string>
<string name="bat_temperature_unit">電池溫度單位</string>
<string name="show_power_info">顯示電池資訊</string>
<string name="show_power_info_description">顯示當前功耗和電池可續航時間</string>
<string name="show_shaders_building">當著色器在編譯時顯示</string>
<string name="show_shaders_building_description">顯示正在編譯的著色器數量</string>
<string name="pipeline_worker_cores">管線工作執行緒</string>
<string name="pipeline_worker_cores_description">設定用於建置 Vulkan 管線的 CPU 核心數量,設定的數值越大效能越好,但溫度也會上升更快</string>
<string name="overlay_position">疊加層位置</string>
<string name="overlay_position_description">選擇疊加層在畫面上的顯示位置</string>
<string name="overlay_position_top_left">左上</string>
@@ -78,10 +80,10 @@
<string name="perf_overlay_background_description">為疊加層添加背景以提高可讀性</string>
<!-- Device Overlay settings -->
<string name="show_soc_overlay">顯示裝置資訊</string>
<string name="enable_soc_overlay">啟用裝置</string>
<string name="soc_overlay_options">裝置</string>
<string name="soc_overlay_options_description">設定裝置層中顯示的資訊</string>
<string name="show_soc_overlay">顯示裝置資訊疊加</string>
<string name="enable_soc_overlay">啟用裝置疊加</string>
<string name="soc_overlay_options">裝置疊加</string>
<string name="soc_overlay_options_description">設定裝置疊加層中顯示的資訊</string>
<string name="show_build_id">顯示Eden的組建版本</string>
<string name="show_driver_version">顯示圖形驅動程式的版本</string>
@@ -92,10 +94,10 @@
<!-- Eden\'s Veil -->
<string name="buffer_reorder_disable">停用緩衝區重新排序</string>
<string name="buffer_reorder_disable_description">勾選時,停用映射記憶體上傳的重新排序功能,允許將上傳與特定繪製關聯。某些情況下可能會降低效能</string>
<string name="buffer_reorder_disable_description">勾選時,停用映射記憶體上傳的重新排序功能,允許將上傳與特定繪製關聯。某些情況下可能會降低效能</string>
<string name="use_sync_core">同步核心速度</string>
<string name="use_sync_core_description">將核心速度與最大速度百分比同步,在不改變遊戲實際速度的情況下提高效能</string>
<string name="use_sync_core_description">將核心速度與最大速度百分比同步,在不改變遊戲實際速度的情況下提高效能</string>
<string name="cpuopt_unsafe_host_mmu">啟用主機 MMU 模擬</string>
<string name="cpuopt_unsafe_host_mmu_description">此最佳化可加速來賓程式的記憶體存取。啟用後,來賓記憶體讀取/寫入將直接在記憶體中執行並利用主機的 MMU。停用此功能將強制所有記憶體存取使用軟體 MMU 模擬。</string>
<string name="debug_knobs">偵錯開關</string>
@@ -171,7 +173,7 @@
<string name="multiplayer_ban">封鎖使用者</string>
<string name="multiplayer_room_browser">公開房間</string>
<string name="multiplayer_no_rooms_found">未找到公開房間</string>
<string name="multiplayer_password_required">需輸入密碼</string>
<string name="multiplayer_password_required">輸入密碼</string>
<string name="multiplayer_player_count">%1$d/%2$d</string>
<string name="multiplayer_game">遊戲</string>
<string name="multiplayer_no_game_info">任意遊戲</string>
@@ -190,7 +192,7 @@
<string name="multiplayer_nickname_invalid">使用者名稱無效,請在系統→網路中檢查設定</string>
<string name="multiplayer_token_error">必須為48個字元,且僅包含小寫字母a-z</string>
<string name="multiplayer_port_error">埠號需為1-65535</string>
<string name="cancel">取消</string>
<string name="cancel">略過</string>
<string name="ok">確定</string>
<string name="refresh">重新整理</string>
<string name="room_list">房間列表</string>
@@ -244,21 +246,21 @@
<string name="home_search_games">搜尋遊戲</string>
<string name="search_settings">搜尋設定</string>
<string name="install_prod_keys">安裝 prod.keys</string>
<string name="install_prod_keys_description">需要解密零售遊戲</string>
<string name="install_prod_keys_description">需要用來解密零售遊戲</string>
<string name="install_prod_keys_warning">跳過安裝金鑰?</string>
<string name="install_prod_keys_warning_description">模擬零售遊戲需要有效的金鑰,若要繼續,將僅有自製遊戲可以運作</string>
<string name="install_prod_keys_warning_description">模擬零售遊戲需要有效的金鑰,如果不安裝將僅有自製遊戲可以運作</string>
<string name="install_prod_keys_warning_help">https://yuzu-mirror.github.io/help/quickstart/#guide-introduction</string>
<string name="install_firmware_warning">跳過安裝韌體?</string>
<string name="emulator_data">設定模擬器資料</string>
<string name="emulator_data_description">模擬器需要金鑰才能正常執行,同時建議安裝韌體以啟動QLaunch小程式</string>
<string name="permissions">授予權限</string>
<string name="permissions_description">授予權限以使用模擬器的特定功能</string>
<string name="install_firmware_warning_description">許多遊戲需要韌體才能正常運作</string>
<string name="install_firmware_warning_description">許多遊戲需要韌體才能正常運作</string>
<string name="install_firmware_warning_help">https://yuzu-mirror.github.io/help/quickstart/#guide-introduction</string>
<string name="notifications">通知</string>
<string name="notifications_description">使用下方的按鈕授予通知權限</string>
<string name="notifications_description">使用下方的按鈕授予通知權限</string>
<string name="permission_denied">權限遭拒</string>
<string name="permission_denied_description">多次拒絕了權限要求,現在您需要在系統設定中手動授予權限。</string>
<string name="permission_denied_description">您多次拒絕了要求的權限,現在您需要在系統設定中手動授予</string>
<string name="about">關於</string>
<string name="about_description">組建版本、製作群、以及更多</string>
<string name="system_information">裝置資訊</string>
@@ -285,7 +287,7 @@
<string name="warning_skip">跳過</string>
<string name="warning_cancel">取消</string>
<string name="install_amiibo_keys">安裝 Amiibo 金鑰</string>
<string name="install_amiibo_keys_description">要在遊戲中使用 Amiibo</string>
<string name="install_amiibo_keys_description">要在遊戲中使用 Amiibo 得先安裝</string>
<string name="gpu_driver_fetcher">GPU驅動程式下載器</string>
<string name="gpu_driver_manager">GPU 驅動程式管理員</string>
<string name="install_gpu_driver_description">安裝替代驅動程式以取得潛在的更佳效能或準確度</string>
@@ -300,14 +302,14 @@
<string name="notification_no_directory_link">無法開啟 Eden 目錄</string>
<string name="notification_no_directory_link_description">請使用檔案管理員的側邊面板手動定位到使用者資料夾。</string>
<string name="manage_save_data">管理儲存資料</string>
<string name="manage_save_data_description">已找到儲存資料,請選取下方的選項。</string>
<string name="manage_save_data_description">導入/導出遊戲的儲存資料</string>
<string name="import_save_warning">導入儲存資料</string>
<string name="import_save_warning_description">這將會以提供的檔案覆寫所有現有的儲存資料,您確定要繼續嗎?</string>
<string name="import_save_warning_description">這將會以提供的檔案覆寫現有的該遊戲儲存資料,您確定要繼續嗎?</string>
<string name="save_files_importing">正在導入儲存資料…</string>
<string name="save_files_exporting">正在導出儲存資料…</string>
<string name="save_file_imported_success">已成功導入</string>
<string name="save_file_invalid_zip_structure">無效的儲存目錄結構</string>
<string name="save_file_invalid_zip_structure_description">首個子資料夾名稱必須為遊戲標題 ID</string>
<string name="save_file_invalid_zip_structure_description">首個子資料夾名稱必須為遊戲 ID</string>
<string name="install_firmware">安裝韌體</string>
<string name="install_firmware_description">韌體必須為 ZIP 壓縮檔,將會用於部分遊戲的啟動</string>
<string name="firmware_installing">正在安裝韌體</string>
@@ -317,12 +319,15 @@
<string name="share_log">分享偵錯記錄</string>
<string name="share_log_description">分享 Eden 的記錄檔以便對相關問題進行偵錯</string>
<string name="share_log_missing">找不到日誌檔案</string>
<string name="share_gpu_log">分享 GPU 日誌</string>
<string name="share_gpu_log_description">分享 Eden 的 GPU 日誌以對圖形問題進行偵錯</string>
<string name="share_gpu_log_missing">找不到 GPU 日誌檔案</string>
<string name="install_game_content">安裝遊戲內容</string>
<string name="install_game_content_description">安裝遊戲更新或 DLC</string>
<string name="installing_game_content">正在安裝內容…</string>
<string name="install_game_content_failure">安裝檔案至 NAND 時發生錯誤</string>
<string name="install_game_content_failure_description">請確保內容有效並且 prod.keys 檔案已安裝</string>
<string name="install_game_content_failure_base">為避免可能的衝突,無法直接安裝遊戲本體</string>
<string name="install_game_content_failure_description">請確保內容有效並且 prod.keys 檔案已安裝</string>
<string name="install_game_content_failure_base">為避免產生衝突,無法直接安裝遊戲本體</string>
<string name="install_game_content_failed_count">%1$d 安裝錯誤</string>
<string name="install_game_content_success">遊戲內容已成功安裝</string>
<string name="install_game_content_success_install">%1$d 安裝成功</string>
@@ -331,12 +336,12 @@
<string name="custom_driver_not_supported">您的裝置不支援自訂驅動程式</string>
<string name="custom_driver_not_supported_description">此裝置不支援注入自訂驅動程式。\n請以後再來查看是否已新增支援!</string>
<string name="manage_yuzu_data">管理 Eden 資料</string>
<string name="manage_yuzu_data_description">安裝韌體、金鑰,導入/導出使用者資料及安裝其項目!</string>
<string name="manage_yuzu_data_description">安裝韌體、金鑰,導入/導出使用者資料及安裝其項目!</string>
<string name="game_folders">遊戲資料夾</string>
<string name="deep_scan">深度掃描</string>
<string name="add_game_folder">新增遊戲資料夾</string>
<string name="folder_already_added">這個資料夾已經新增過了!</string>
<string name="game_folder_properties">遊戲資料夾屬性</string>
<string name="game_folder_properties">遊戲資料夾設定</string>
<plurals name="saves_import_failed">
<item quantity="other">%d 個存檔導入失敗</item>
</plurals>
@@ -347,28 +352,30 @@
<string name="verify_installed_content">驗證已安裝內容的完整性</string>
<string name="verify_installed_content_description">檢查所有已安裝的內容是否有損壞</string>
<string name="keys_missing">缺少解密金鑰</string>
<string name="keys_missing">缺少 prod.keys</string>
<string name="keys_missing_description">無法解密韌體和零售遊戲</string>
<string name="keys_missing_help">https://yuzu-mirror.github.io/help/quickstart/#dumping-decryption-keys</string>
<string name="uninstall_firmware">解除安裝韌體</string>
<string name="uninstall_firmware_description">解除安裝韌體將從裝置中刪除它並可能影響遊戲相容性</string>
<string name="uninstall_firmware_description">解除安裝韌體將從裝置中刪除它並可能影響遊戲相容性</string>
<string name="firmware_uninstalling">正在解除安裝韌體...</string>
<string name="firmware_uninstalled_success">韌體解除安裝成功</string>
<string name="keys_failed">金鑰安裝失敗</string>
<string name="keys_install_success">金鑰安裝成功</string>
<string name="error_keys_copy_failed">一個或多個金鑰複製失敗</string>
<string name="error_keys_copy_failed">一個或多個金鑰安裝失敗</string>
<string name="error_keys_invalid_filename">請確保金鑰檔案具有.keys副檔名後重試。</string>
<string name="error_keys_failed_init">金鑰初始化失敗。請檢查您的轉儲工具是否為最新版本並重新轉儲金鑰。</string>
<!-- Applet launcher strings -->
<string name="qlaunch_applet">Qlaunch</string>
<string name="qlaunch_description">從系統主畫面啟動應用程式</string>
<string name="qlaunch_description">Switch系統主畫面啟動應用程式(目前僅支援英文)</string>
<string name="applets">小程式啟動器</string>
<string name="applets_description">使用已安裝的韌體啟動系統小程式</string>
<string name="applets_error_firmware">未安裝韌體</string>
<string name="applets_error_applet">無法使用小程式</string>
<string name="applets_error_description"><![CDATA[請確定您的<a href=\"https://yuzu-mirror.github.io/help/quickstart/#dumping-prodkeys-and-titlekeys\">prod.keys</a>檔案和<a href=\"https://yuzu-mirror.github.io/help/quickstart/#dumping-system-firmware\">韌體</a>已安裝並重試]]></string>
<string name="album_applet">相簿</string>
<string name="album_applet_description">使用系統相片檢視器查看儲存在使用者螢幕截圖資料夾中的影像</string>
<string name="mii_edit_applet">Mii 編輯</string>
@@ -377,47 +384,58 @@
<string name="cabinet_applet_description">編輯、刪除儲存在 amiibo 上的資料</string>
<string name="cabinet_launcher">Cabinet 啟動器</string>
<string name="cabinet_nickname_and_owner">暱稱和擁有者設定</string>
<string name="cabinet_game_data_eraser">遊戲資料橡皮擦</string>
<string name="cabinet_game_data_eraser">刪除遊戲資料</string>
<string name="cabinet_restorer">還原程式</string>
<string name="cabinet_formatter">格式化程式</string>
<!-- About screen strings -->
<string name="gaia_is_not_real">Gaia不是真的</string>
<string name="gaia_is_not_real">Gaia 不存在</string>
<string name="copied_to_clipboard">已複製到剪貼簿</string>
<string name="about_app_description">一個開放原始碼的 Switch 模擬器</string>
<string name="contributors">參與者</string>
<string name="contributors_description">這些人讓 Eden Android 版成為可能</string>
<string name="licenses_description">這些專案使 Eden Android 版成為可能</string>
<string name="build">組建版本</string>
<string name="user_data">使用者資料</string>
<string name="user_data_description">導入/導出所有應用程式資料。\n\n導入使用者資料時,現有的使用者資料將被取代!\n\n直接從 Citron 導入資料可能會出現問題,建議手動導入所有所需資料。</string>
<string name="user_data_description">導入/導出所有應用程式資料。\n\n導入使用者資料時,現有的使用者資料將被取代!\n\n直接從 Citron 導入使用者資料可能會出現問題,建議手動導入所有所需資料。</string>
<string name="exporting_user_data">正在導出使用者資料…</string>
<string name="importing_user_data">正在導入使用者資料…</string>
<string name="invalid_yuzu_backup">無效的 Eden 備份</string>
<string name="user_data_export_success">使用者資料導出成功</string>
<string name="user_data_import_success">使用者資料導入成功</string>
<string name="user_data_export_cancelled">導出已取消</string>
<string name="user_data_import_failed_description">請確保使用者資料夾位於 zip 壓縮檔的根目錄,並在 config/config.ini 路徑中包含組態檔案,並再試一次</string>
<string name="user_data_import_failed_description">請確保使用者資料夾位於 zip 壓縮檔的根目錄,並在 config/config.ini 路徑中包含組態檔案,並再試一次</string>
<!-- General settings strings -->
<string name="frame_limit_enable">限制速度</string>
<string name="frame_limit_enable_description">將模擬速度限制在標準速度的指定百分比</string>
<string name="frame_limit_enable">限制模擬速度</string>
<string name="frame_limit_enable_description">將模擬速度限制在標準速度的指定百分比</string>
<string name="frame_limit_slider">限制速度百分比</string>
<string name="frame_limit_slider_description">指定限制模擬速度的百分比。100% 為標準速度,更高或更低的值將會增加或減少速度限制</string>
<string name="frame_limit_slider_description">指定模擬速度的百分比。100% 為標準速度,更高或更低的值將會減少或增加速度限制</string>
<string name="turbo_speed_limit">加速模式</string>
<string name="turbo_speed_limit_description">當開啟加速模式時,模擬器將以此速度執行</string>
<string name="slow_speed_limit">慢速模式</string>
<string name="slow_speed_limit_description">當慢速模式開啟時,模擬器將會以此速度執行</string>
<string name="cpu_backend">CPU 後端</string>
<string name="cpu_accuracy">CPU 準確度</string>
<string name="value_with_units">%1$s%2$s</string>
<string name="program_args">Homebrew 參數</string>
<string name="program_args_description">在啟動時傳遞給 Homebrew 的命令列參數(例如:-noglsl)</string>
<!-- System settings strings -->
<string name="device_name">裝置名稱</string>
<string name="use_docked_mode">底座模式</string>
<string name="use_docked_mode_description">提高解析度,降低效能。停用後將會使用手提模式,會降低解析度並提高效能</string>
<string name="use_docked_mode_description">提高解析度,降低效能。停用後將會使用手提模式,會降低解析度並提高效能</string>
<string name="emulated_region">模擬區域</string>
<string name="emulated_language">模擬語言</string>
<string name="select_rtc_date">選擇 RTC 日期</string>
<string name="select_rtc_time">選擇 RTC 時間</string>
<string name="use_custom_rtc">自訂 RTC</string>
<string name="use_custom_rtc_description">允許您設定與您的目前系統時間相互獨立的自訂時間</string>
<string name="use_custom_rtc_description">允許您設定與您的目前系統時間相互獨立的自訂時間</string>
<string name="set_custom_rtc">設定自訂 RTC</string>
<!-- CPU -->
<string name="fast_cpu_time">CPU 超頻</string>
<string name="fast_cpu_time_description">強制模擬 CPU 以更高的時脈運作,減少某些 FPS 限制。使用 加速 (1700MHz) 以 Switch 的最高原生時脈執行,或 高速 (2000MHz) 以雙倍時脈執行</string>
<string name="custom_cpu_ticks">自訂CPU時脈</string>
<string name="custom_cpu_ticks_description">自訂CPU時脈。更高的值可能提高效能,但也可能導致遊戲卡死。建議範圍為77-21000。</string>
<string name="cpu_ticks">時脈</string>
@@ -428,33 +446,41 @@
<!-- Network settings strings -->
<string name="web_token">網路令牌</string>
<string name="web_token_description">用於建立公開大廳的網路令牌。它是由48個小寫字母a-z組成的字串。</string>
<string name="web_token_description">用於建立公開大廳的網路令牌。由48個小寫字母a-z組成</string>
<string name="web_username">網路使用者名稱</string>
<string name="web_username_description">多人遊戲房間中顯示的使用者名稱。必須為4-20個字元,僅能使用英文字母、數字、句點、破折號、底線和空格(標點符號須為英文格式)</string>
<string name="web_username_description">多人遊戲房間中顯示的使用者名稱。必須為4-20個字元,僅能使用英文字母、數字、句點、破折號、底線和空格(標點符號須為英文格式)</string>
<string name="network">網路</string>
<!-- Graphics settings strings -->
<string name="renderer_resolution">解析度 (手提/底座)</string>
<string name="renderer_vsync">垂直同步</string>
<string name="renderer_scaling_filter">視窗適應濾器</string>
<string name="renderer_scaling_filter">視窗適應濾</string>
<string name="fsr_sharpness">FSR/SGSR 銳化度</string>
<string name="fsr_sharpness_description">使用 FSR/SGSR 時圖片的銳化程度</string>
<string name="renderer_anti_aliasing">抗鋸齒</string>
<string name="advanced">進階</string>
<string name="renderer_accuracy">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="gpu_fence_behavior">GPU 同步柵欄模式</string>
<string name="gpu_fence_behavior_description">控制 GPU 同步柵欄模式。即時是速度最快的選項,但可能會導致一些問題。使用平衡可以提供更好的相容性並修復某些遊戲中的錯誤。準確則是在犧牲部分效能的情況下進一步提升相容性。嚴格是最慢的選項,但可以修復那些對同步要求更嚴格的遊戲中的問題。預設則會依照 GPU 準確度設定</string>
<string name="anisotropic_filtering">各向異性過濾</string>
<string name="anisotropic_filtering_description">改善斜角檢視時的紋理品質</string>
<string name="vram_usage_mode">VRAM使用模式</string>
<string name="vram_usage_mode_description">控制GPU記憶體的分配與釋放策略</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>
<string name="sync_memory_operations_description">確保計算和記憶體操作之間的資料一致性。 此選項應能修復某些遊戲中的問題,但在某些情況下可能會降低效能。 使用Unreal Engine 4的遊戲似乎受影響最大</string>
<string name="use_disk_shader_cache">磁碟著色器快取</string>
<string name="use_disk_shader_cache_description">將產生的著色器快取儲存至硬碟以減少中斷</string>
<string name="use_disk_shader_cache_description">將產生的著色器快取儲存至硬碟以減少中斷</string>
<string name="renderer_force_max_clock">強制使用最大時脈 (僅限Adreno)</string>
<string name="renderer_force_max_clock_description">強制 GPU 以可能的最大時脈執行 (熱溫限制仍會被套用)</string>
<string name="renderer_force_max_clock_description">強制 GPU 以可能的最大時脈執行 (熱溫限制仍會被套用)</string>
<string name="renderer_reactive_flushing">使用重新啟用排清</string>
<string name="renderer_reactive_flushing_description">犧牲效能,以改善部分遊戲的轉譯準確度。</string>
<string name="skip_cpu_inner_invalidation">跳過CPU內部失效處理</string>
@@ -603,7 +629,7 @@
<string name="import_complete">導入完成</string>
<string name="use_global_setting">使用全域設定</string>
<string name="operation_completed_successfully">操作已成功完成</string>
<string name="confirm">確定</string>
<string name="confirm">下載</string>
<string name="load">載入</string>
<string name="save">儲存</string>
@@ -821,8 +847,8 @@
<!-- Memory Layouts -->
<string name="memory_4gb">4GB (推薦)</string>
<string name="memory_6gb">6GB (不安全)</string>
<string name="memory_8gb">8GB (不安全)</string>
<string name="memory_6gb">6GB (不穩定)</string>
<string name="memory_8gb">8GB (不穩定)</string>
<!--CPU clock speeds-->
<string name="clock_boost">加速 (1700MHz)</string>
@@ -847,8 +873,8 @@
<!-- DMA Accuracy -->
<string name="dma_accuracy_default">預設</string>
<string name="dma_accuracy_unsafe">安全</string>
<string name="dma_accuracy_safe">安全</string>
<string name="dma_accuracy_unsafe">穩定</string>
<string name="dma_accuracy_safe">穩定</string>
<string name="vram_usage_conservative">保守</string>
<string name="vram_usage_aggressive">積極</string>
@@ -874,7 +900,7 @@
<!-- CPU Accuracy -->
<string name="cpu_accuracy_accurate">高準確度</string>
<string name="cpu_accuracy_unsafe">低準確度(不安全</string>
<string name="cpu_accuracy_unsafe">低準確度(不穩定</string>
<string name="cpu_accuracy_paranoid">不合理</string>
<string name="cpu_accuracy_debugging">偵錯</string>
@@ -630,6 +630,16 @@
<item>3</item>
</integer-array>
<string-array name="nxlinkServerModeEntries">
<item>Disabled</item>
<item>Eden Log</item>
</string-array>
<integer-array name="nxlinkServerModeValues">
<item>0</item>
<item>1</item>
</integer-array>
<string-array name="installKeysResults">
<item>""</item>
<item>""</item>
@@ -436,6 +436,8 @@
<string name="program_args">Homebrew Args</string>
<string name="program_args_description">Command-line arguments passed to homebrew at launch (e.g. -noglsl).</string>
<string name="nxlink_server_mode">nxlink Server</string>
<string name="nxlink_server_mode_description">Starts a local nxlink server for homebrew stdout/stderr streams.</string>
<!-- System settings strings -->
<string name="device_name">Device name</string>
+2 -2
View File
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
@@ -147,7 +147,7 @@ Result OpusDecoder::DecodeInterleavedForMultiStream(u32* out_data_size, u64* out
auto* header_p{reinterpret_cast<const OpusPacketHeader*>(input_data.data())};
OpusPacketHeader header{ReverseHeader(*header_p)};
LOG_TRACE(Service_Audio, "header size {:#X} input data size 0x{:X} in_data size 0x{:X}",
LOG_TRACE(Service_Audio, "header size {:#x} input data size {:#x} in_data size {:#x}",
header.size, input_data.size_bytes(), in_data.size_bytes());
R_UNLESS(in_data.size_bytes() >= header.size &&
@@ -369,7 +369,7 @@ Result InfoUpdater::UpdateMixes(MixContext& mix_context, const u32 mix_buffer_co
if (mix_count < 0 || mix_count > 0x100) {
LOG_ERROR(
Service_Audio,
"Invalid mix count from dirty parameter: count={}, magic=0x{:X}, expected_size={}",
"Invalid mix count from dirty parameter: count={}, magic={:#x}, expected_size={}",
mix_count, in_dirty_params->magic, in_header->mix_size);
return Service::Audio::ResultInvalidUpdateInfo;
}
+28 -21
View File
@@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: 2021 Skyline Team and Contributors
// SPDX-License-Identifier: GPL-3.0-or-later
@@ -43,13 +46,13 @@ MAP_MEMBER(void)::MapLocked(VaType virt, PaType phys, VaType size, ExtraBlockInf
if (virt_end > va_limit) {
ASSERT_MSG(false,
"Trying to map a block past the VA limit: virt_end: 0x{:X}, va_limit: 0x{:X}",
"Trying to map a block past the VA limit: virt_end: {:#x}, va_limit: {:#x}",
virt_end, va_limit);
}
auto block_end_successor{std::lower_bound(blocks.begin(), blocks.end(), virt_end)};
if (block_end_successor == blocks.begin()) {
ASSERT_MSG(false, "Trying to map a block before the VA start: virt_end: 0x{:X}", virt_end);
ASSERT_MSG(false, "Trying to map a block before the VA start: virt_end: {:#x}", virt_end);
}
auto block_end_predecessor{std::prev(block_end_successor)};
@@ -124,7 +127,7 @@ MAP_MEMBER(void)::MapLocked(VaType virt, PaType phys, VaType size, ExtraBlockInf
// Check that the start successor is either the end block or something in between
if (block_start_successor->virt > virt_end) {
ASSERT_MSG(false, "Unsorted block in AS map: virt: 0x{:X}", block_start_successor->virt);
ASSERT_MSG(false, "Unsorted block in AS map: virt: {:#x}", block_start_successor->virt);
} else if (block_start_successor->virt == virt_end) {
// We need to create a new block as there are none spare that we would overwrite
blocks.insert(block_start_successor, Block(virt, phys, extra_info));
@@ -150,13 +153,13 @@ MAP_MEMBER(void)::UnmapLocked(VaType virt, VaType size) {
if (virt_end > va_limit) {
ASSERT_MSG(false,
"Trying to map a block past the VA limit: virt_end: 0x{:X}, va_limit: 0x{:X}",
"Trying to map a block past the VA limit: virt_end: {:#x}, va_limit: {:#x}",
virt_end, va_limit);
}
auto block_end_successor{std::lower_bound(blocks.begin(), blocks.end(), virt_end)};
if (block_end_successor == blocks.begin()) {
ASSERT_MSG(false, "Trying to unmap a block before the VA start: virt_end: 0x{:X}",
ASSERT_MSG(false, "Trying to unmap a block before the VA start: virt_end: {:#x}",
virt_end);
}
@@ -257,7 +260,7 @@ MAP_MEMBER(void)::UnmapLocked(VaType virt, VaType size) {
auto block_start_successor{std::next(block_start_predecessor)};
if (block_start_successor->virt > virt_end) {
ASSERT_MSG(false, "Unsorted block in AS map: virt: 0x{:X}", block_start_successor->virt);
ASSERT_MSG(false, "Unsorted block in AS map: virt: {:#x}", block_start_successor->virt);
} else if (block_start_successor->virt == virt_end) {
// There are no blocks between the start and the end that would let us skip inserting a new
// one for head
@@ -333,22 +336,22 @@ ALLOC_MEMBER(VaType)::Allocate(VaType size) {
current_linear_alloc_end = alloc_start + size;
} else { // If linear allocation overflows the AS then find a gap
if (this->blocks.size() <= 2) {
ASSERT_MSG(false, "Unexpected allocator state!");
}
auto search_predecessor{std::next(this->blocks.begin())};
auto search_successor{std::next(search_predecessor)};
while (search_successor != this->blocks.end() &&
(search_successor->virt - search_predecessor->virt < size ||
search_predecessor->Mapped())) {
search_predecessor = search_successor++;
}
if (search_successor != this->blocks.end()) {
alloc_start = search_predecessor->virt;
alloc_start = virt_start;
} else {
return {}; // AS is full
auto search_predecessor{std::next(this->blocks.begin())};
auto search_successor{std::next(search_predecessor)};
while (search_successor != this->blocks.end() &&
(search_successor->virt - search_predecessor->virt < size ||
search_predecessor->Mapped())) {
search_predecessor = search_successor++;
}
if (search_successor != this->blocks.end()) {
alloc_start = search_predecessor->virt;
} else {
return {}; // AS is full
}
}
}
@@ -361,6 +364,10 @@ ALLOC_MEMBER(void)::AllocateFixed(VaType virt, VaType size) {
}
ALLOC_MEMBER(void)::Free(VaType virt, VaType size) {
const VaType virt_end = virt + size;
this->Unmap(virt, size);
if (virt_end >= virt && virt_end == current_linear_alloc_end) {
current_linear_alloc_end = virt < virt_start ? virt_start : virt;
}
}
} // namespace Common
+62 -1
View File
@@ -4,6 +4,8 @@
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <cerrno>
#include <cstdint>
#include <vector>
#include "common/assert.h"
@@ -15,8 +17,10 @@
#include "common/logging.h"
#ifdef _WIN32
#include <fcntl.h>
#include <io.h>
#include <share.h>
#include <windows.h>
#else
#include <unistd.h>
#endif
@@ -95,10 +99,65 @@ namespace {
case FileShareFlag::ShareWriteOnly:
return _SH_DENYRD;
case FileShareFlag::ShareReadWrite:
case FileShareFlag::ShareReadWriteDelete:
return _SH_DENYNO;
}
}
[[nodiscard]] std::FILE* OpenWithWindowsShareDelete(const fs::path& path, FileAccessMode mode,
FileType type) {
DWORD desired_access{};
DWORD creation_disposition{OPEN_EXISTING};
int open_flags = type == FileType::BinaryFile ? _O_BINARY : _O_TEXT;
switch (mode) {
case FileAccessMode::Read:
desired_access = GENERIC_READ;
open_flags |= _O_RDONLY;
break;
case FileAccessMode::Write:
desired_access = GENERIC_WRITE;
creation_disposition = CREATE_ALWAYS;
open_flags |= _O_WRONLY;
break;
case FileAccessMode::Append:
desired_access = GENERIC_WRITE;
creation_disposition = OPEN_ALWAYS;
open_flags |= _O_WRONLY | _O_APPEND;
break;
case FileAccessMode::ReadWrite:
desired_access = GENERIC_READ | GENERIC_WRITE;
open_flags |= _O_RDWR;
break;
case FileAccessMode::ReadAppend:
desired_access = GENERIC_READ | GENERIC_WRITE;
creation_disposition = OPEN_ALWAYS;
open_flags |= _O_RDWR | _O_APPEND;
break;
}
const auto handle =
CreateFileW(path.c_str(), desired_access,
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, nullptr,
creation_disposition, FILE_ATTRIBUTE_NORMAL, nullptr);
if (handle == INVALID_HANDLE_VALUE) {
errno = EACCES;
return nullptr;
}
const auto fd = _open_osfhandle(reinterpret_cast<intptr_t>(handle), open_flags);
if (fd == -1) {
CloseHandle(handle);
return nullptr;
}
auto* const file = _wfdopen(fd, AccessModeToWStr(mode, type));
if (file == nullptr) {
_close(fd);
}
return file;
}
#else
/**
@@ -254,7 +313,9 @@ void IOFile::Open(const fs::path& path, FileAccessMode mode, FileType type, File
errno = 0;
#ifdef _WIN32
if (flag != FileShareFlag::ShareNone) {
if (flag == FileShareFlag::ShareReadWriteDelete) {
file = OpenWithWindowsShareDelete(path, mode, type);
} else if (flag != FileShareFlag::ShareNone) {
file = _wfsopen(path.c_str(), AccessModeToWStr(mode, type), ToWindowsFileShareFlag(flag));
} else {
_wfopen_s(&file, path.c_str(), AccessModeToWStr(mode, type));
+6 -5
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 2021 yuzu Emulator Project
@@ -49,10 +49,11 @@ enum class FileType {
};
enum class FileShareFlag {
ShareNone, // Provides exclusive access to the file.
ShareReadOnly, // Provides read only shared access to the file.
ShareWriteOnly, // Provides write only shared access to the file.
ShareReadWrite, // Provides read and write shared access to the file.
ShareNone, // Provides exclusive access to the file.
ShareReadOnly, // Provides read only shared access to the file.
ShareWriteOnly, // Provides write only shared access to the file.
ShareReadWrite, // Provides read and write shared access to the file.
ShareReadWriteDelete, // Provides read, write, and delete shared access to the file.
};
enum class DirEntryFilter {
+4 -1
View File
@@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: 2013 Dolphin Emulator Project
// SPDX-FileCopyrightText: 2014 Citra Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@@ -12,7 +15,7 @@ std::vector<u8> HexStringToVector(std::string_view str, bool little_endian) {
for (std::size_t i = str.size() - 2; i <= str.size(); i -= 2)
out[i / 2] = (ToHexNibble(str[i]) << 4) | ToHexNibble(str[i + 1]);
} else {
for (std::size_t i = 0; i < str.size(); i += 2)
for (std::size_t i = 0; i + 1 < str.size(); i += 2)
out[i / 2] = (ToHexNibble(str[i]) << 4) | ToHexNibble(str[i + 1]);
}
return out;
+12 -1
View File
@@ -702,7 +702,15 @@ struct Values {
// Controls
InputSetting<std::array<PlayerInput, 10>> players;
Setting<bool> disable_wgi_xinput{
linkage, false, "disable_wgi_xinput", Category::Controls, Specialization::Default,
// Only read/write disable_wgi_xinput on Windows platforms
#ifdef _WIN32
true
#else
false
#endif
};
Setting<bool> enable_raw_input{
linkage, false, "enable_raw_input", Category::Controls, Specialization::Default,
// Only read/write enable_raw_input on Windows platforms
@@ -832,6 +840,9 @@ struct Values {
Setting<bool> gpu_log_driver_debug{linkage, true, "gpu_log_driver_debug", Category::Debugging};
Setting<s32> gpu_log_ring_buffer_size{linkage, 512, "gpu_log_ring_buffer_size",
Category::Debugging};
Setting<HomebrewNxlinkServerMode> homebrew_nxlink_server_mode{
linkage, HomebrewNxlinkServerMode::Disabled, "homebrew_nxlink_server_mode",
Category::Debugging};
SwitchableSetting<u16, true> debug_knobs{linkage,
0,
+1
View File
@@ -159,6 +159,7 @@ ENUM(GpuUnswizzleChunk, VeryLow, Low, Normal, Medium, High)
ENUM(TemperatureUnits, Celsius, Fahrenheit)
ENUM(ExtendedDynamicState, Disabled, EDS1, EDS2, EDS3);
ENUM(GpuLogLevel, Off, Errors, Standard, Verbose, All)
ENUM(HomebrewNxlinkServerMode, Disabled, EdenLog, HostStdout, File)
ENUM(GameListMode, TreeView, GridView, CarouselView);
ENUM(SpeedMode, Standard, Turbo, Slow);
+2
View File
@@ -1139,6 +1139,8 @@ add_library(core STATIC
launch_timestamp_cache.h
loader/deconstructed_rom_directory.cpp
loader/deconstructed_rom_directory.h
loader/homebrew_nxlink.cpp
loader/homebrew_nxlink.h
loader/kip.cpp
loader/kip.h
loader/loader.cpp
+5 -5
View File
@@ -52,6 +52,7 @@
#include "core/hle/service/set/system_settings_server.h"
#include "core/hle/service/sm/sm.h"
#include "core/internal_network/network.h"
#include "core/loader/homebrew_nxlink.h"
#include "core/loader/loader.h"
#include "core/memory.h"
#include "core/memory/cheat_engine.h"
@@ -264,9 +265,7 @@ struct System::Impl {
// Setting changes may require a full system reinitialization (e.g., disabling multicore).
ReinitializeIfNecessary(system);
kernel.Initialize();
cpu_manager.Initialize();
}
SystemResultStatus SetupForApplicationProcess(System& system, Frontend::EmuWindow& emu_window) {
@@ -293,9 +292,7 @@ struct System::Impl {
return SystemResultStatus::Success;
}
SystemResultStatus Load(System& system, Frontend::EmuWindow& emu_window,
const std::string& filepath,
Service::AM::FrontendAppletParameters& params) {
SystemResultStatus Load(System& system, Frontend::EmuWindow& emu_window, const std::string& filepath, Service::AM::FrontendAppletParameters& params) {
InitializeKernel(system);
const auto file = GetGameFileFromPath(virtual_filesystem, filepath);
@@ -342,6 +339,8 @@ struct System::Impl {
ShutdownMainProcess();
return init_result;
}
// Waiting for GPU before initializing CPU
cpu_manager.Initialize();
// Initialize cheat engine
if (cheat_engine) {
@@ -399,6 +398,7 @@ struct System::Impl {
stop_event.request_stop();
core_timing.SyncPause(false);
Loader::HomebrewNxlink::StopServer();
Network::CancelPendingSocketOperations();
kernel.SuspendEmulation(true);
kernel.CloseServices();
+4 -4
View File
@@ -431,7 +431,7 @@ void DeviceMemoryManager<Traits>::ReadBlock(DAddr address, void* dest_pointer, s
[&](size_t copy_amount, DAddr current_vaddr) {
LOG_ERROR(
HW_Memory,
"Unmapped Device ReadBlock @ 0x{:016X} (start address = 0x{:016X}, size = {})",
"Unmapped Device ReadBlock @ {:#016x} (start address = {:#016x}, size = {})",
current_vaddr, address, size);
std::memset(dest_pointer, 0, copy_amount);
},
@@ -450,7 +450,7 @@ void DeviceMemoryManager<Traits>::WriteBlock(DAddr address, const void* src_poin
[&](size_t copy_amount, DAddr current_vaddr) {
LOG_ERROR(
HW_Memory,
"Unmapped Device WriteBlock @ 0x{:016X} (start address = 0x{:016X}, size = {})",
"Unmapped Device WriteBlock @ {:#016x} (start address = {:#016x}, size = {})",
current_vaddr, address, size);
},
[&](size_t copy_amount, u8* const dst_ptr) {
@@ -489,7 +489,7 @@ void DeviceMemoryManager<Traits>::ReadBlockUnsafe(DAddr address, void* dest_poin
[&](size_t copy_amount, DAddr current_vaddr) {
LOG_ERROR(
HW_Memory,
"Unmapped Device ReadBlock @ 0x{:016X} (start address = 0x{:016X}, size = {})",
"Unmapped Device ReadBlock @ {:#016x} (start address = {:#016x}, size = {})",
current_vaddr, address, size);
std::memset(dest_pointer, 0, copy_amount);
},
@@ -509,7 +509,7 @@ void DeviceMemoryManager<Traits>::WriteBlockUnsafe(DAddr address, const void* sr
[&](size_t copy_amount, DAddr current_vaddr) {
LOG_ERROR(
HW_Memory,
"Unmapped Device WriteBlock @ 0x{:016X} (start address = 0x{:016X}, size = {})",
"Unmapped Device WriteBlock @ {:#016x} (start address = {:#016x}, size = {})",
current_vaddr, address, size);
},
[&](size_t copy_amount, u8* const dst_ptr) {
+1
View File
@@ -12,6 +12,7 @@
namespace FileSys {
enum class OpenMode : u32 {
Default = 0,
Read = (1 << 0),
Write = (1 << 1),
AllowAppend = (1 << 2),
+2 -2
View File
@@ -256,7 +256,7 @@ void IPSwitchCompiler::Parse() {
const auto& patch_line = lines[++i];
// Patch line may contain comments
if (StartsWith(patch_line, "//")) {
if (StartsWith(patch_line, "//") || StartsWith(patch_line, "#")) {
continue;
}
@@ -304,7 +304,7 @@ void IPSwitchCompiler::Parse() {
if (print_values) {
LOG_INFO(Loader,
"[IPSwitchCompiler ('{}')] - Patching value at offset 0x{:08X} "
"[IPSwitchCompiler ('{}')] - Patching value at offset {:#08x} "
"with byte string '{}'",
patch_text->GetName(), offset, Common::HexToString(replace));
}
+9 -9
View File
@@ -181,11 +181,11 @@ const ProgramMetadata::KernelCapabilityDescriptors& ProgramMetadata::GetKernelCa
void ProgramMetadata::Print() const {
LOG_DEBUG(Service_FS, "Magic: {:.4}", npdm_header.magic.data());
LOG_DEBUG(Service_FS, "Main thread priority: 0x{:02X}", npdm_header.main_thread_priority);
LOG_DEBUG(Service_FS, "Main thread priority: {:#02x}", npdm_header.main_thread_priority);
LOG_DEBUG(Service_FS, "Main thread core: {}", npdm_header.main_thread_cpu);
LOG_DEBUG(Service_FS, "Main thread stack size: {:#X} bytes", npdm_header.main_stack_size);
LOG_DEBUG(Service_FS, "Main thread stack size: {:#x} bytes", npdm_header.main_stack_size);
LOG_DEBUG(Service_FS, "Process category: {}", npdm_header.process_category);
LOG_DEBUG(Service_FS, "Flags: 0x{:02X}", npdm_header.flags);
LOG_DEBUG(Service_FS, "Flags: {:#02x}", npdm_header.flags);
LOG_DEBUG(Service_FS, " > 64-bit instructions: {}",
npdm_header.has_64_bit_instructions ? "YES" : "NO");
@@ -209,15 +209,15 @@ void ProgramMetadata::Print() const {
// Begin ACID printing (potential perms, signed)
LOG_DEBUG(Service_FS, "Magic: {:.4}", acid_header.magic.data());
LOG_DEBUG(Service_FS, "Flags: 0x{:02X}", acid_header.flags);
LOG_DEBUG(Service_FS, "Flags: {:#02x}", acid_header.flags);
LOG_DEBUG(Service_FS, " > Is Retail: {}", acid_header.production_flag ? "YES" : "NO");
LOG_DEBUG(Service_FS, "Title ID Min: 0x{:016X}", acid_header.title_id_min);
LOG_DEBUG(Service_FS, "Title ID Max: 0x{:016X}", acid_header.title_id_max);
LOG_DEBUG(Service_FS, "Filesystem Access: 0x{:016X}\n", acid_file_access.permissions);
LOG_DEBUG(Service_FS, "Title ID Min: {:#016x}", acid_header.title_id_min);
LOG_DEBUG(Service_FS, "Title ID Max: {:#016x}", acid_header.title_id_max);
LOG_DEBUG(Service_FS, "Filesystem Access: {:#016x}\n", acid_file_access.permissions);
// Begin ACI0 printing (actual perms, unsigned)
LOG_DEBUG(Service_FS, "Magic: {:.4}", aci_header.magic.data());
LOG_DEBUG(Service_FS, "Title ID: 0x{:016X}", aci_header.title_id);
LOG_DEBUG(Service_FS, "Filesystem Access: 0x{:016X}\n", aci_file_access.permissions);
LOG_DEBUG(Service_FS, "Title ID: {:#016x}", aci_header.title_id);
LOG_DEBUG(Service_FS, "Filesystem Access: {:#016x}\n", aci_file_access.permissions);
}
} // namespace FileSys
@@ -76,7 +76,7 @@ constexpr inline SystemArchiveDescriptor GetSystemArchive(u64 title_id) {
VirtualFile SynthesizeSystemArchive(const u64 title_id) {
auto const desc = GetSystemArchive(title_id);
LOG_INFO(Service_FS, "Synthesizing system archive '{}' (0x{:016X}).", desc.name, title_id);
LOG_INFO(Service_FS, "Synthesizing system archive '{}' ({:#016x}).", desc.name, title_id);
if (desc.supplier != nullptr) {
if (auto const dir = desc.supplier(); dir != nullptr) {
if (auto const romfs = CreateRomFS(dir); romfs != nullptr) {
+7 -4
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
@@ -40,7 +40,7 @@ VfsEntryType VfsFilesystem::GetEntryType(std::string_view path_) const {
VirtualFile VfsFilesystem::OpenFile(std::string_view path_, OpenMode perms) {
const auto path = Common::FS::SanitizePath(path_);
return root->GetFileRelative(path);
return root->GetFileRelative(path, perms);
}
VirtualFile VfsFilesystem::CreateFile(std::string_view path_, OpenMode perms) {
@@ -201,7 +201,7 @@ std::string VfsFile::GetFullPath() const {
return GetContainingDirectory()->GetFullPath() + '/' + GetName();
}
VirtualFile VfsDirectory::GetFileRelative(std::string_view path) const {
VirtualFile VfsDirectory::GetFileRelative(std::string_view path, OpenMode perms) const {
auto vec = Common::FS::SplitPathComponents(path);
if (vec.empty()) {
return nullptr;
@@ -224,7 +224,10 @@ VirtualFile VfsDirectory::GetFileRelative(std::string_view path) const {
return nullptr;
}
return dir->GetFile(vec.back());
if (perms == OpenMode::Default) {
return dir->GetFile(vec.back());
}
return dir->GetFileRelative(vec.back(), perms);
}
VirtualFile VfsDirectory::GetFileAbsolute(std::string_view path) const {
+2 -1
View File
@@ -201,7 +201,8 @@ public:
// Retrieves the file located at path as if the current directory was root. Returns nullptr if
// not found.
virtual VirtualFile GetFileRelative(std::string_view path) const;
virtual VirtualFile GetFileRelative(std::string_view path,
OpenMode perms = OpenMode::Default) const;
// Calls GetFileRelative(path) on the root of the current directory.
virtual VirtualFile GetFileAbsolute(std::string_view path) const;
+2 -2
View File
@@ -27,9 +27,9 @@ VirtualDir LayeredVfsDirectory::MakeLayeredDirectory(std::vector<VirtualDir> dir
return VirtualDir(new LayeredVfsDirectory(std::move(dirs), std::move(name)));
}
VirtualFile LayeredVfsDirectory::GetFileRelative(std::string_view path) const {
VirtualFile LayeredVfsDirectory::GetFileRelative(std::string_view path, OpenMode perms) const {
for (const auto& layer : dirs) {
const auto file = layer->GetFileRelative(path);
const auto file = layer->GetFileRelative(path, perms);
if (file != nullptr)
return file;
}
+5 -1
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
@@ -20,7 +23,8 @@ public:
/// Wrapper function to allow for more efficient handling of dirs.size() == 0, 1 cases.
static VirtualDir MakeLayeredDirectory(std::vector<VirtualDir> dirs, std::string name = "");
VirtualFile GetFileRelative(std::string_view path) const override;
VirtualFile GetFileRelative(std::string_view path,
OpenMode perms = OpenMode::Default) const override;
VirtualDir GetDirectoryRelative(std::string_view path) const override;
VirtualFile GetFile(std::string_view file_name) const override;
VirtualDir GetSubdirectory(std::string_view subdir_name) const override;
+51 -23
View File
@@ -12,7 +12,6 @@
#include "common/fs/file.h"
#include "common/fs/fs.h"
#include "common/fs/path_util.h"
#include "common/logging.h"
#include "core/file_sys/vfs/vfs.h"
#include "core/file_sys/vfs/vfs_real.h"
@@ -46,17 +45,11 @@ bool IsWithinRoot(std::string_view root, std::string_view full_path) {
}
constexpr FS::FileAccessMode ModeFlagsToFileAccessMode(OpenMode mode) {
switch (mode) {
case OpenMode::Read:
return FS::FileAccessMode::Read;
case OpenMode::Write:
case OpenMode::ReadWrite:
case OpenMode::AllowAppend:
case OpenMode::All:
if (True(mode & OpenMode::Write) || True(mode & OpenMode::AllowAppend)) {
return FS::FileAccessMode::ReadWrite;
default:
return {};
}
return FS::FileAccessMode::Read;
}
} // Anonymous namespace
@@ -94,9 +87,11 @@ VirtualFile RealVfsFilesystem::OpenFileFromEntry(std::string_view path_, std::op
std::optional<std::string> parent_path,
OpenMode perms) {
const auto path = FS::SanitizePath(path_, FS::DirectorySeparator::PlatformDefault);
const auto open_perms = perms == OpenMode::Default ? OpenMode::Read : perms;
std::scoped_lock lk{list_lock};
if (auto it = cache.find(path); it != cache.end()) {
const CacheKey cache_key{path, open_perms};
if (auto it = cache.find(cache_key); it != cache.end()) {
if (auto file = it->second.lock(); file) {
return file;
}
@@ -110,8 +105,9 @@ VirtualFile RealVfsFilesystem::OpenFileFromEntry(std::string_view path_, std::op
this->InsertReferenceIntoListLocked(*reference);
auto file = std::shared_ptr<RealVfsFile>(
new RealVfsFile(*this, std::move(reference), path, perms, size, std::move(parent_path)));
cache[path] = file;
new RealVfsFile(*this, std::move(reference), path, open_perms, size,
std::move(parent_path)));
cache[cache_key] = file;
return file;
}
@@ -124,7 +120,7 @@ VirtualFile RealVfsFilesystem::CreateFile(std::string_view path_, OpenMode perms
const auto path = FS::SanitizePath(path_, FS::DirectorySeparator::PlatformDefault);
{
std::scoped_lock lk{list_lock};
cache.erase(path);
CloseCachedFileReferenceLocked(path);
}
// Current usages of CreateFile expect to delete the contents of an existing file.
@@ -157,8 +153,8 @@ VirtualFile RealVfsFilesystem::MoveFile(std::string_view old_path_, std::string_
const auto new_path = FS::SanitizePath(new_path_, FS::DirectorySeparator::PlatformDefault);
{
std::scoped_lock lk{list_lock};
cache.erase(old_path);
cache.erase(new_path);
CloseCachedFileReferenceLocked(old_path);
CloseCachedFileReferenceLocked(new_path);
}
if (!FS::RenameFile(old_path, new_path)) {
return nullptr;
@@ -170,14 +166,15 @@ bool RealVfsFilesystem::DeleteFile(std::string_view path_) {
const auto path = FS::SanitizePath(path_, FS::DirectorySeparator::PlatformDefault);
{
std::scoped_lock lk{list_lock};
cache.erase(path);
CloseCachedFileReferenceLocked(path);
}
return FS::RemoveFile(path);
}
VirtualDir RealVfsFilesystem::OpenDirectory(std::string_view path_, OpenMode perms) {
const auto path = FS::SanitizePath(path_, FS::DirectorySeparator::PlatformDefault);
return std::shared_ptr<RealVfsDirectory>(new RealVfsDirectory(*this, path, perms));
return std::shared_ptr<RealVfsDirectory>(
new RealVfsDirectory(*this, path, perms == OpenMode::Default ? OpenMode::Read : perms));
}
VirtualDir RealVfsFilesystem::CreateDirectory(std::string_view path_, OpenMode perms) {
@@ -222,8 +219,8 @@ std::unique_lock<std::mutex> RealVfsFilesystem::RefreshReference(const std::stri
if (!reference.file) {
this->EvictSingleReferenceLocked();
reference.file =
FS::FileOpen(path, ModeFlagsToFileAccessMode(perms), FS::FileType::BinaryFile);
reference.file = FS::FileOpen(path, ModeFlagsToFileAccessMode(perms),
FS::FileType::BinaryFile, FS::FileShareFlag::ShareReadWriteDelete);
if (reference.file) {
num_open_files++;
}
@@ -297,15 +294,45 @@ RealVfsFile::~RealVfsFile() {
base.DropReference(std::move(reference));
}
void RealVfsFilesystem::CloseCachedFileReferenceLocked(const std::string& path) {
for (auto it = cache.lower_bound(CacheKey{path, OpenMode::Default});
it != cache.end() && it->first.first == path;) {
const auto cached_file = it->second.lock();
if (cached_file) {
auto* const real_file = static_cast<RealVfsFile*>(cached_file.get());
auto& reference = real_file->reference;
if (reference && reference->file) {
RemoveReferenceFromListLocked(*reference);
reference->file.reset();
num_open_files--;
InsertReferenceIntoListLocked(*reference);
}
}
it = cache.erase(it);
}
}
std::string RealVfsFile::GetName() const {
#ifdef __ANDROID__
if (path[0] != '/') {
if (!path.empty() && path[0] != '/') {
return FS::Android::GetFilename(path);
}
#endif
return path_components.empty() ? "" : std::string(path_components.back());
}
std::string RealVfsFile::GetFullPath() const {
#ifdef __ANDROID__
if (!path.empty() && path[0] != '/') {
auto out = path;
std::replace(out.begin(), out.end(), '\\', '/');
return out;
}
#endif
return VfsFile::GetFullPath();
}
std::size_t RealVfsFile::GetSize() const {
if (size) {
return *size;
@@ -411,13 +438,14 @@ RealVfsDirectory::RealVfsDirectory(RealVfsFilesystem& base_, const std::string&
RealVfsDirectory::~RealVfsDirectory() = default;
VirtualFile RealVfsDirectory::GetFileRelative(std::string_view relative_path) const {
VirtualFile RealVfsDirectory::GetFileRelative(std::string_view relative_path,
OpenMode open_perms) const {
const auto full_path = FS::SanitizePath(path + '/' + std::string(relative_path));
if (!FS::Exists(full_path) || FS::IsDir(full_path)
|| !IsWithinRoot(FS::SanitizePath(path), full_path)) {
return nullptr;
}
return base.OpenFile(full_path, perms);
return base.OpenFile(full_path, open_perms == OpenMode::Default ? perms : open_perms);
}
VirtualDir RealVfsDirectory::GetDirectoryRelative(std::string_view relative_path) const {
+7 -2
View File
@@ -10,6 +10,7 @@
#include <mutex>
#include <optional>
#include <string_view>
#include <utility>
#include "common/intrusive_list.h"
#include "core/file_sys/fs_filesystem.h"
#include "core/file_sys/vfs/vfs.h"
@@ -49,8 +50,9 @@ public:
bool DeleteDirectory(std::string_view path) override;
private:
using CacheKey = std::pair<std::string, OpenMode>;
using ReferenceListType = Common::IntrusiveListBaseTraits<FileReference>::ListType;
std::map<std::string, std::weak_ptr<VfsFile>, std::less<>> cache;
std::map<CacheKey, std::weak_ptr<VfsFile>, std::less<>> cache;
ReferenceListType open_references;
ReferenceListType closed_references;
std::mutex list_lock;
@@ -63,6 +65,7 @@ private:
std::unique_lock<std::mutex> RefreshReference(const std::string& path, OpenMode perms,
FileReference& reference);
void DropReference(std::unique_ptr<FileReference>&& reference);
void CloseCachedFileReferenceLocked(const std::string& path);
private:
friend class RealVfsDirectory;
@@ -85,6 +88,7 @@ public:
~RealVfsFile() override;
std::string GetName() const override;
std::string GetFullPath() const override;
std::size_t GetSize() const override;
bool Resize(std::size_t new_size) override;
VirtualDir GetContainingDirectory() const override;
@@ -115,7 +119,8 @@ class RealVfsDirectory : public VfsDirectory {
public:
~RealVfsDirectory() override;
VirtualFile GetFileRelative(std::string_view relative_path) const override;
VirtualFile GetFileRelative(std::string_view relative_path,
OpenMode perms = OpenMode::Default) const override;
VirtualDir GetDirectoryRelative(std::string_view relative_path) const override;
VirtualFile GetFile(std::string_view name) const override;
VirtualDir GetSubdirectory(std::string_view name) const override;
+5
View File
@@ -670,6 +670,11 @@ public:
size_t GetHeapRegionSize() const {
return m_heap_region_end - m_heap_region_start;
}
size_t GetCurrentHeapSize() const {
KScopedLightLock lk(m_general_lock);
return m_current_heap_end - m_heap_region_start;
}
size_t GetAliasRegionSize() const {
return m_alias_region_end - m_alias_region_start;
}
+11
View File
@@ -210,6 +210,11 @@ Result KProcess::Initialize(KernelCore& kernel, const Svc::CreateProcessParamete
m_arg_pointer = 0;
m_arg_return_address = 0;
m_main_thread_handle_addr = 0;
m_process_handle_addr = 0;
m_homebrew_next_load_path_addr = 0;
m_homebrew_next_load_argv_addr = 0;
m_is_homebrew_in_place_next_load = false;
m_has_homebrew_nxlink_argv_marker = false;
m_code_size = params.code_num_pages * PageSize;
m_is_application = True(params.flags & Svc::CreateProcessFlag::IsApplication);
@@ -947,6 +952,7 @@ Result KProcess::Run(KernelCore& kernel, s32 priority, size_t stack_size) {
stack_top = stack_bottom + stack_size;
m_main_thread_stack_size = stack_size;
m_main_thread_stack_top = stack_top;
}
// Ensure our stack is safe to clean up on exit.
@@ -1005,6 +1011,11 @@ Result KProcess::Run(KernelCore& kernel, s32 priority, size_t stack_size) {
if (GetInteger(m_main_thread_handle_addr) != 0) {
this->GetMemory().Write32(m_main_thread_handle_addr, thread_handle);
}
if (GetInteger(m_process_handle_addr) != 0) {
Handle process_handle;
R_TRY(m_handle_table.Add(kernel, std::addressof(process_handle), this));
this->GetMemory().Write32(m_process_handle_addr, process_handle);
}
} else {
main_thread->GetContext().r[0] = 0;
main_thread->GetContext().r[1] = thread_handle;
+52
View File
@@ -6,7 +6,9 @@
#pragma once
#include <array>
#include <map>
#include <string_view>
#include "core/arm/arm_interface.h"
#include "core/file_sys/program_metadata.h"
@@ -87,6 +89,10 @@ private:
KProcessAddress m_arg_pointer{};
KProcessAddress m_arg_return_address{};
KProcessAddress m_main_thread_handle_addr{};
KProcessAddress m_process_handle_addr{};
KProcessAddress m_homebrew_next_load_path_addr{};
KProcessAddress m_homebrew_next_load_argv_addr{};
std::array<char, 16> m_homebrew_nxlink_argv_marker{};
KHandleTable m_handle_table;
KProcessAddress m_plr_address{};
ThreadList m_thread_list{};
@@ -112,6 +118,7 @@ private:
size_t m_code_size{};
size_t m_main_thread_stack_size{};
KProcessAddress m_main_thread_stack_top{};
size_t m_max_process_memory{};
size_t m_memory_release_hint{};
s64 m_schedule_count{};
@@ -139,6 +146,8 @@ private:
bool m_is_suspended : 1 = false;
bool m_is_immortal : 1 = false;
bool m_is_handle_table_initialized : 1 = false;
bool m_is_homebrew_in_place_next_load : 1 = false;
bool m_has_homebrew_nxlink_argv_marker : 1 = false;
private:
Result StartTermination(KernelCore& kernel);
@@ -231,6 +240,49 @@ public:
void SetMainThreadHandleAddr(KProcessAddress addr) {
m_main_thread_handle_addr = addr;
}
void SetProcessHandleAddr(KProcessAddress addr) {
m_process_handle_addr = addr;
}
void SetHomebrewNextLoadBufferAddrs(KProcessAddress path_addr, KProcessAddress argv_addr) {
m_homebrew_next_load_path_addr = path_addr;
m_homebrew_next_load_argv_addr = argv_addr;
}
void SetHomebrewInPlaceNextLoad(bool enabled) {
m_is_homebrew_in_place_next_load = enabled;
}
void SetHomebrewNxlinkArgvMarker(std::string_view marker) {
if (marker.size() != m_homebrew_nxlink_argv_marker.size()) {
m_has_homebrew_nxlink_argv_marker = false;
return;
}
marker.copy(m_homebrew_nxlink_argv_marker.data(), m_homebrew_nxlink_argv_marker.size());
m_has_homebrew_nxlink_argv_marker = true;
}
void ClearHomebrewNxlinkArgvMarker() {
m_has_homebrew_nxlink_argv_marker = false;
}
std::string_view GetHomebrewNxlinkArgvMarker() const {
if (!m_has_homebrew_nxlink_argv_marker) {
return {};
}
return {m_homebrew_nxlink_argv_marker.data(), m_homebrew_nxlink_argv_marker.size()};
}
bool IsHomebrewInPlaceNextLoad() const {
return m_is_homebrew_in_place_next_load;
}
KProcessAddress GetHomebrewNextLoadPathAddr() const {
return m_homebrew_next_load_path_addr;
}
KProcessAddress GetHomebrewNextLoadArgvAddr() const {
return m_homebrew_next_load_argv_addr;
}
size_t GetCodeSize() const {
return m_code_size;
}
KProcessAddress GetMainThreadStackTop() const {
return m_main_thread_stack_top;
}
size_t GetMainStackSize() const {
return m_main_thread_stack_size;
+5 -4
View File
@@ -1,7 +1,8 @@
// 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 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-late
// SPDX-License-Identifier: GPL-2.0-or-later
// This file is automatically generated using svc_generator.py.
// DO NOT MODIFY IT MANUALLY
@@ -128,7 +129,7 @@ static void SvcWrap_QueryMemory64From32(Core::System& system, std::span<uint64_t
}
static void SvcWrap_ExitProcess64From32(Core::System& system, std::span<uint64_t, 8> args) {
ExitProcess64From32(system);
ExitProcess64From32(system, args);
}
static void SvcWrap_CreateThread64From32(Core::System& system, std::span<uint64_t, 8> args) {
@@ -1298,7 +1299,7 @@ static void SvcWrap_QueryMemory64(Core::System& system, std::span<uint64_t, 8> a
}
static void SvcWrap_ExitProcess64(Core::System& system, std::span<uint64_t, 8> args) {
ExitProcess64(system);
ExitProcess64(system, args);
}
static void SvcWrap_CreateThread64(Core::System& system, std::span<uint64_t, 8> args) {
+6 -5
View File
@@ -1,7 +1,8 @@
// 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 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-late
// SPDX-License-Identifier: GPL-2.0-or-later
// This file is automatically generated using svc_generator.py.
// DO NOT MODIFY IT MANUALLY
@@ -25,7 +26,7 @@ Result SetMemoryAttribute(Core::System& system, uint64_t address, uint64_t size,
Result MapMemory(Core::System& system, uint64_t dst_address, uint64_t src_address, uint64_t size);
Result UnmapMemory(Core::System& system, uint64_t dst_address, uint64_t src_address, uint64_t size);
Result QueryMemory(Core::System& system, uint64_t out_memory_info, PageInfo* out_page_info, uint64_t address);
void ExitProcess(Core::System& system);
void ExitProcess(Core::System& system, std::span<uint64_t, 8> args);
Result CreateThread(Core::System& system, Handle* out_handle, uint64_t func, uint64_t arg, uint64_t stack_bottom, int32_t priority, int32_t core_id);
Result StartThread(Core::System& system, Handle thread_handle);
void ExitThread(Core::System& system);
@@ -146,7 +147,7 @@ Result SetMemoryAttribute64From32(Core::System& system, uint32_t address, uint32
Result MapMemory64From32(Core::System& system, uint32_t dst_address, uint32_t src_address, uint32_t size);
Result UnmapMemory64From32(Core::System& system, uint32_t dst_address, uint32_t src_address, uint32_t size);
Result QueryMemory64From32(Core::System& system, uint32_t out_memory_info, PageInfo* out_page_info, uint32_t address);
void ExitProcess64From32(Core::System& system);
void ExitProcess64From32(Core::System& system, std::span<uint64_t, 8> args);
Result CreateThread64From32(Core::System& system, Handle* out_handle, uint32_t func, uint32_t arg, uint32_t stack_bottom, int32_t priority, int32_t core_id);
Result StartThread64From32(Core::System& system, Handle thread_handle);
void ExitThread64From32(Core::System& system);
@@ -267,7 +268,7 @@ Result SetMemoryAttribute64(Core::System& system, uint64_t address, uint64_t siz
Result MapMemory64(Core::System& system, uint64_t dst_address, uint64_t src_address, uint64_t size);
Result UnmapMemory64(Core::System& system, uint64_t dst_address, uint64_t src_address, uint64_t size);
Result QueryMemory64(Core::System& system, uint64_t out_memory_info, PageInfo* out_page_info, uint64_t address);
void ExitProcess64(Core::System& system);
void ExitProcess64(Core::System& system, std::span<uint64_t, 8> args);
Result CreateThread64(Core::System& system, Handle* out_handle, uint64_t func, uint64_t arg, uint64_t stack_bottom, int32_t priority, int32_t core_id);
Result StartThread64(Core::System& system, Handle thread_handle);
void ExitThread64(Core::System& system);
+1 -1
View File
@@ -15,7 +15,7 @@ namespace Kernel::Svc {
/// Sets the thread activity
Result SetThreadActivity(Core::System& system, Handle thread_handle,
ThreadActivity thread_activity) {
LOG_DEBUG(Kernel_SVC, "called, handle=0x{:08X}, activity=0x{:08X}", thread_handle,
LOG_DEBUG(Kernel_SVC, "called, handle={:#08x}, activity={:#08x}", thread_handle,
thread_activity);
// Validate the activity.
@@ -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 2023 yuzu Emulator Project
@@ -43,7 +43,7 @@ constexpr bool IsValidArbitrationType(Svc::ArbitrationType type) {
// Wait for an address (via Address Arbiter)
Result WaitForAddress(Core::System& system, u64 address, ArbitrationType arb_type, s32 value,
s64 timeout_ns) {
LOG_TRACE(Kernel_SVC, "called, address={:#X}, arb_type=0x{:X}, value=0x{:X}, timeout_ns={}",
LOG_TRACE(Kernel_SVC, "called, address={:#x}, arb_type={:#x}, value={:#x}, timeout_ns={}",
address, arb_type, value, timeout_ns);
// Validate input.
@@ -74,7 +74,7 @@ Result WaitForAddress(Core::System& system, u64 address, ArbitrationType arb_typ
// Signals to an address (via Address Arbiter)
Result SignalToAddress(Core::System& system, u64 address, SignalType signal_type, s32 value,
s32 count) {
LOG_TRACE(Kernel_SVC, "called, address={:#X}, signal_type=0x{:X}, value=0x{:X}, count=0x{:X}",
LOG_TRACE(Kernel_SVC, "called, address={:#x}, signal_type={:#x}, value={:#x}, count={:#x}",
address, signal_type, value, count);
// Validate input.
+3 -3
View File
@@ -33,7 +33,7 @@ constexpr bool IsValidUnmapFromOwnerCodeMemoryPermission(MemoryPermission perm)
} // namespace
Result CreateCodeMemory(Core::System& system, Handle* out, u64 address, uint64_t size) {
LOG_TRACE(Kernel_SVC, "called, address={:#X}, size=0x{:X}", address, size);
LOG_TRACE(Kernel_SVC, "called, address={:#x}, size={:#x}", address, size);
// Validate address / size.
R_UNLESS(Common::IsAligned(address, PageSize), ResultInvalidAddress);
@@ -69,8 +69,8 @@ Result ControlCodeMemory(Core::System& system, Handle code_memory_handle,
MemoryPermission perm) {
LOG_TRACE(Kernel_SVC,
"called, code_memory_handle={:#X}, operation=0x{:X}, address=0x{:X}, size=0x{:X}, "
"permission={:#X}",
"called, code_memory_handle={:#x}, operation={:#x}, address={:#x}, size={:#x}, "
"permission={:#x}",
code_memory_handle, operation, address, size, perm);
// Validate the address / size.
@@ -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 2023 yuzu Emulator Project
@@ -17,7 +17,7 @@ namespace Kernel::Svc {
/// Wait process wide key atomic
Result WaitProcessWideKeyAtomic(Core::System& system, u64 address, u64 cv_key, u32 tag,
s64 timeout_ns) {
LOG_TRACE(Kernel_SVC, "called address={:X}, cv_key={:X}, tag=0x{:08X}, timeout_ns={}", address,
LOG_TRACE(Kernel_SVC, "called address={:X}, cv_key={:X}, tag={:#08x}, timeout_ns={}", address,
cv_key, tag, timeout_ns);
// Validate input.
@@ -48,7 +48,7 @@ Result WaitProcessWideKeyAtomic(Core::System& system, u64 address, u64 cv_key, u
/// Signal process wide key
void SignalProcessWideKey(Core::System& system, u64 cv_key, s32 count) {
LOG_TRACE(Kernel_SVC, "called, cv_key={:#X}, count=0x{:08X}", cv_key, count);
LOG_TRACE(Kernel_SVC, "called, cv_key={:#x}, count={:#08x}", cv_key, count);
// Signal the condition variable.
return GetCurrentProcess(system.Kernel())
+3 -3
View File
@@ -14,7 +14,7 @@
namespace Kernel::Svc {
Result SignalEvent(Core::System& system, Handle event_handle) {
LOG_DEBUG(Kernel_SVC, "called, event_handle=0x{:08X}", event_handle);
LOG_DEBUG(Kernel_SVC, "called, event_handle={:#08x}", event_handle);
// Get the current handle table.
const KHandleTable& handle_table = GetCurrentProcess(system.Kernel()).GetHandleTable();
@@ -26,7 +26,7 @@ Result SignalEvent(Core::System& system, Handle event_handle) {
if (event.IsNotNull()) {
event->Signal(system.Kernel());
} else {
LOG_WARNING(Kernel_SVC, "SignalEvent best-effort unknown handle=0x{:08X} (ignored)",
LOG_WARNING(Kernel_SVC, "SignalEvent best-effort unknown handle={:#08x} (ignored)",
event_handle);
}
R_SUCCEED();
@@ -41,7 +41,7 @@ Result SignalEvent(Core::System& system, Handle event_handle) {
}
Result ClearEvent(Core::System& system, Handle event_handle) {
LOG_TRACE(Kernel_SVC, "called, event_handle=0x{:08X}", event_handle);
LOG_TRACE(Kernel_SVC, "called, event_handle={:#08x}", event_handle);
// Get the current handle table.
const auto& handle_table = GetCurrentProcess(system.Kernel()).GetHandleTable();
+11 -10
View File
@@ -4,6 +4,7 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <cctype>
#include "core/core.h"
#include "core/debugger/debugger.h"
#include "core/hle/kernel/k_process.h"
@@ -41,7 +42,7 @@ void Break(Core::System& system, BreakReason reason, u64 info1, u64 info2) {
std::string hexdump;
for (std::size_t i = 0; i < debug_buffer.size(); i++) {
hexdump += fmt::format("{:02X} ", debug_buffer[i]);
if (i != 0 && i % 16 == 0) {
if ((i + 1) % 32 == 0) {
hexdump += '\n';
}
}
@@ -51,35 +52,35 @@ void Break(Core::System& system, BreakReason reason, u64 info1, u64 info2) {
};
switch (break_reason) {
case BreakReason::Panic:
LOG_CRITICAL(Debug_Emulated, "Userspace PANIC! info1=0x{:016X}, info2=0x{:016X}", info1,
LOG_CRITICAL(Debug_Emulated, "Userspace PANIC! info1={:#016x}, info2={:#016x}", info1,
info2);
handle_debug_buffer(info1, info2);
break;
case BreakReason::Assert:
LOG_CRITICAL(Debug_Emulated, "Userspace Assertion failed! info1=0x{:016X}, info2=0x{:016X}",
LOG_CRITICAL(Debug_Emulated, "Userspace Assertion failed! info1={:#016x}, info2={:#016x}",
info1, info2);
handle_debug_buffer(info1, info2);
break;
case BreakReason::User:
LOG_WARNING(Debug_Emulated, "Userspace Break! 0x{:016X} with size 0x{:016X}", info1, info2);
LOG_WARNING(Debug_Emulated, "Userspace Break! {:#016x} with size {:#016x}", info1, info2);
handle_debug_buffer(info1, info2);
break;
case BreakReason::PreLoadDll:
LOG_INFO(Debug_Emulated,
"Userspace Attempting to load an NRO at 0x{:016X} with size 0x{:016X}", info1,
"Userspace Attempting to load an NRO at {:#016x} with size {:#016x}", info1,
info2);
break;
case BreakReason::PostLoadDll:
LOG_INFO(Debug_Emulated, "Userspace Loaded an NRO at 0x{:016X} with size 0x{:016X}", info1,
LOG_INFO(Debug_Emulated, "Userspace Loaded an NRO at {:#016x} with size {:#016x}", info1,
info2);
break;
case BreakReason::PreUnloadDll:
LOG_INFO(Debug_Emulated,
"Userspace Attempting to unload an NRO at 0x{:016X} with size 0x{:016X}", info1,
"Userspace Attempting to unload an NRO at {:#016x} with size {:#016x}", info1,
info2);
break;
case BreakReason::PostUnloadDll:
LOG_INFO(Debug_Emulated, "Userspace Unloaded an NRO at 0x{:016X} with size 0x{:016X}",
LOG_INFO(Debug_Emulated, "Userspace Unloaded an NRO at {:#016x} with size {:#016x}",
info1, info2);
break;
case BreakReason::CppException:
@@ -88,7 +89,7 @@ void Break(Core::System& system, BreakReason reason, u64 info1, u64 info2) {
default:
LOG_WARNING(
Debug_Emulated,
"Signalling debugger, Unknown break reason {:#X}, info1=0x{:016X}, info2=0x{:016X}",
"Signalling debugger, Unknown break reason {:#x}, info1={:#016x}, info2={:#016x}",
reason, info1, info2);
handle_debug_buffer(info1, info2);
break;
@@ -101,7 +102,7 @@ void Break(Core::System& system, BreakReason reason, u64 info1, u64 info2) {
if (!notification_only) {
LOG_CRITICAL(
Debug_Emulated,
"Emulated program broke execution! reason=0x{:016X}, info1=0x{:016X}, info2=0x{:016X}",
"Emulated program broke execution! reason={:#016x}, info1={:#016x}, info2={:#016x}",
reason, info1, info2);
handle_debug_buffer(info1, info2);
+4 -4
View File
@@ -16,7 +16,7 @@ namespace Kernel::Svc {
/// Gets system/memory information for the current process
Result GetInfo(Core::System& system, u64* result, InfoType info_id_type, Handle handle,
u64 info_sub_id) {
LOG_TRACE(Kernel_SVC, "called info_id={:#X}, info_sub_id=0x{:X}, handle=0x{:08X}",
LOG_TRACE(Kernel_SVC, "called info_id={:#x}, info_sub_id={:#x}, handle={:#08x}",
info_id_type, info_sub_id, handle);
u32 info_id = static_cast<u32>(info_id_type);
@@ -153,7 +153,7 @@ Result GetInfo(Core::System& system, u64* result, InfoType info_id_type, Handle
break;
}
LOG_ERROR(Kernel_SVC, "Unimplemented svcGetInfo id=0x{:016X}", info_id);
LOG_ERROR(Kernel_SVC, "Unimplemented svcGetInfo id={:#016x}", info_id);
R_THROW(ResultInvalidEnumValue);
}
@@ -206,7 +206,7 @@ Result GetInfo(Core::System& system, u64* result, InfoType info_id_type, Handle
.GetHandleTable()
.GetObject<KThread>(system.Kernel(), Handle(handle));
if (thread.IsNull()) {
LOG_ERROR(Kernel_SVC, "Thread handle does not exist, handle=0x{:08X}",
LOG_ERROR(Kernel_SVC, "Thread handle does not exist, handle={:#08x}",
static_cast<Handle>(handle));
R_THROW(ResultInvalidHandle);
}
@@ -265,7 +265,7 @@ Result GetInfo(Core::System& system, u64* result, InfoType info_id_type, Handle
R_SUCCEED();
}
default:
LOG_ERROR(Kernel_SVC, "Unimplemented svcGetInfo id=0x{:016X}", info_id);
LOG_ERROR(Kernel_SVC, "Unimplemented svcGetInfo id={:#016x}", info_id);
R_THROW(ResultInvalidEnumValue);
}
}
+3 -3
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 2023 yuzu Emulator Project
@@ -13,7 +13,7 @@ namespace Kernel::Svc {
/// Attempts to locks a mutex
Result ArbitrateLock(Core::System& system, Handle thread_handle, u64 address, u32 tag) {
LOG_TRACE(Kernel_SVC, "called thread_handle=0x{:08X}, address={:#X}, tag=0x{:08X}",
LOG_TRACE(Kernel_SVC, "called thread_handle={:#08x}, address={:#x}, tag={:#08x}",
thread_handle, address, tag);
// Validate the input address.
@@ -25,7 +25,7 @@ Result ArbitrateLock(Core::System& system, Handle thread_handle, u64 address, u3
/// Unlock a mutex
Result ArbitrateUnlock(Core::System& system, u64 address) {
LOG_TRACE(Kernel_SVC, "called address={:#X}", address);
LOG_TRACE(Kernel_SVC, "called address={:#x}", address);
// Validate the input address.
R_UNLESS(!IsKernelAddress(address), ResultInvalidCurrentMemory);
+225 -16
View File
@@ -4,6 +4,9 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <algorithm>
#include <vector>
#include "core/core.h"
#include "core/hle/kernel/k_process.h"
#include "core/hle/kernel/svc.h"
@@ -22,6 +25,179 @@ constexpr bool IsValidSetMemoryPermission(MemoryPermission perm) {
}
}
bool IsHomebrewInPlaceNextLoadCodeRange(const KProcess& process, u64 address, u64 size) {
if (!process.IsHomebrewInPlaceNextLoad()) {
return false;
}
const u64 code_start = GetInteger(process.GetEntryPoint());
const size_t code_size = process.GetCodeSize();
if (code_start == 0 || code_size == 0 || size > code_size || address < code_start) {
return false;
}
return address - code_start <= code_size - size;
}
struct HomebrewInPlaceMemoryBlock {
u64 address;
u64 size;
KMemoryState state;
KMemoryPermission permission;
KMemoryAttribute attribute;
bool use_process_permission;
};
Result SetHomebrewInPlaceMemoryPermissionByBlocks(KProcess& process, u64 address, u64 size,
MemoryPermission perm, Result original_result) {
auto& page_table = process.GetPageTable();
const u64 end = address + size;
const auto requested_permission = ConvertToKMemoryPermission(perm);
std::vector<HomebrewInPlaceMemoryBlock> blocks;
for (u64 cursor = address; cursor < end;) {
KMemoryInfo info;
PageInfo page_info;
const auto query_result = page_table.QueryInfo(std::addressof(info),
std::addressof(page_info), cursor);
if (query_result.IsError()) {
LOG_WARNING(Kernel_SVC,
"NextLoad in-place: split permission query failed "
"address=0x{:016X}, result={:#X}, original={:#X}",
cursor, query_result.raw, original_result.raw);
R_RETURN(original_result);
}
const u64 block_end = (std::min<u64>)(info.GetEndAddress(), end);
if (block_end <= cursor) {
LOG_WARNING(Kernel_SVC,
"NextLoad in-place: split permission walk stalled "
"cursor=0x{:016X}, block=0x{:016X}/0x{:X}, original={:#X}",
cursor, info.GetAddress(), info.GetSize(), original_result.raw);
R_RETURN(original_result);
}
const bool can_reprotect = True(info.GetState() & KMemoryState::FlagCanReprotect);
const bool can_process_reprotect = True(info.GetState() & KMemoryState::FlagCode);
if (!can_reprotect && !can_process_reprotect) {
LOG_WARNING(Kernel_SVC,
"NextLoad in-place: split permission unsupported block "
"address=0x{:016X}, size=0x{:X}, state=0x{:08X}, svc_state={}, "
"perm=0x{:08X}, attr=0x{:08X}, original={:#X}",
cursor, block_end - cursor, static_cast<u32>(info.GetState()),
static_cast<u32>(info.GetSvcState()),
static_cast<u32>(info.GetPermission()),
static_cast<u32>(info.GetAttribute()), original_result.raw);
R_RETURN(original_result);
}
blocks.push_back({
.address = cursor,
.size = block_end - cursor,
.state = info.GetState(),
.permission = info.GetPermission(),
.attribute = info.GetAttribute(),
.use_process_permission = !can_reprotect && can_process_reprotect,
});
cursor = block_end;
}
for (const auto& block : blocks) {
if (block.permission == requested_permission) {
continue;
}
const auto block_result =
block.use_process_permission
? page_table.SetProcessMemoryPermission(block.address, block.size, perm)
: page_table.SetMemoryPermission(block.address, block.size, perm);
if (block_result.IsError()) {
LOG_WARNING(Kernel_SVC,
"NextLoad in-place: split permission block failed "
"address=0x{:016X}, size=0x{:X}, state=0x{:08X}, perm=0x{:08X}, "
"attr=0x{:08X}, result={:#X}, original={:#X}",
block.address, block.size, static_cast<u32>(block.state),
static_cast<u32>(block.permission), static_cast<u32>(block.attribute),
block_result.raw, original_result.raw);
R_RETURN(block_result);
}
}
R_SUCCEED();
}
struct HomebrewInPlaceDeviceSharedBlock {
u64 address;
u64 size;
u16 device_use_count;
};
Result UnlockHomebrewInPlaceDeviceSharedSource(KProcess& process, u64 address, u64 size,
Result original_result) {
auto& page_table = process.GetPageTable();
const u64 end = address + size;
std::vector<HomebrewInPlaceDeviceSharedBlock> blocks;
for (u64 cursor = address; cursor < end;) {
KMemoryInfo info;
PageInfo page_info;
const auto query_result = page_table.QueryInfo(std::addressof(info),
std::addressof(page_info), cursor);
if (query_result.IsError()) {
R_RETURN(original_result);
}
const u64 block_end = (std::min<u64>)(info.GetEndAddress(), end);
if (block_end <= cursor) {
R_RETURN(original_result);
}
const bool can_device_map = True(info.GetState() & KMemoryState::FlagCanDeviceMap);
const bool is_clean_memory =
can_device_map && info.GetPermission() == KMemoryPermission::UserReadWrite &&
info.GetAttribute() == KMemoryAttribute::None && info.m_device_use_count == 0;
const bool is_stale_device_shared =
can_device_map && info.GetPermission() == KMemoryPermission::UserReadWrite &&
info.GetAttribute() == KMemoryAttribute::DeviceShared && info.m_device_use_count > 0;
if (is_clean_memory) {
cursor = block_end;
continue;
}
if (!is_stale_device_shared) {
R_RETURN(original_result);
}
blocks.push_back({
.address = cursor,
.size = block_end - cursor,
.device_use_count = info.m_device_use_count,
});
cursor = block_end;
}
if (blocks.empty()) {
R_RETURN(original_result);
}
for (const auto& block : blocks) {
for (u16 unlock = 0; unlock < block.device_use_count; unlock++) {
const auto unlock_result =
page_table.UnlockForDeviceAddressSpace(block.address, block.size);
if (unlock_result.IsError()) {
LOG_WARNING(Kernel_SVC,
"NextLoad in-place: device-shared source unlock failed "
"address=0x{:016X}, size=0x{:X}, remaining={}, result={:#X}, "
"original={:#X}",
block.address, block.size, block.device_use_count - unlock - 1,
unlock_result.raw, original_result.raw);
R_RETURN(original_result);
}
}
}
R_SUCCEED();
}
// Checks if address + size is greater than the given address
// This can return false if the size causes an overflow of a 64-bit type
// or if the given size is zero.
@@ -34,12 +210,12 @@ constexpr bool IsValidAddressRange(u64 address, u64 size) {
// in the same order.
Result MapUnmapMemorySanityChecks(const KProcessPageTable& manager, u64 dst_addr, u64 src_addr, u64 size) {
if (!Common::IsAligned(dst_addr, Core::Memory::YUZU_PAGESIZE)) {
LOG_ERROR(Kernel_SVC, "Destination address is not aligned to 4KB, 0x{:016X}", dst_addr);
LOG_ERROR(Kernel_SVC, "Destination address is not aligned to 4KB, {:#016X}", dst_addr);
R_THROW(ResultInvalidAddress);
}
if (!Common::IsAligned(src_addr, Core::Memory::YUZU_PAGESIZE)) {
LOG_ERROR(Kernel_SVC, "Source address is not aligned to 4KB, 0x{:016X}", src_addr);
LOG_ERROR(Kernel_SVC, "Source address is not aligned to 4KB, {:#016X}", src_addr);
R_THROW(ResultInvalidSize);
}
@@ -49,26 +225,26 @@ Result MapUnmapMemorySanityChecks(const KProcessPageTable& manager, u64 dst_addr
}
if (!Common::IsAligned(size, Core::Memory::YUZU_PAGESIZE)) {
LOG_ERROR(Kernel_SVC, "Size is not aligned to 4KB, 0x{:016X}", size);
LOG_ERROR(Kernel_SVC, "Size is not aligned to 4KB, {:#016X}", size);
R_THROW(ResultInvalidSize);
}
if (!IsValidAddressRange(dst_addr, size)) {
LOG_ERROR(Kernel_SVC,
"Destination is not a valid address range, addr=0x{:016X}, size=0x{:016X}",
"Destination is not a valid address range, addr={:#016x}, size={:#016x}",
dst_addr, size);
R_THROW(ResultInvalidCurrentMemory);
}
if (!IsValidAddressRange(src_addr, size)) {
LOG_ERROR(Kernel_SVC, "Source is not a valid address range, addr=0x{:016X}, size=0x{:016X}",
LOG_ERROR(Kernel_SVC, "Source is not a valid address range, addr={:#016x}, size={:#016x}",
src_addr, size);
R_THROW(ResultInvalidCurrentMemory);
}
if (!manager.Contains(src_addr, size)) {
LOG_ERROR(Kernel_SVC,
"Source is not within the address space, addr=0x{:016X}, size=0x{:016X}",
"Source is not within the address space, addr={:#016x}, size={:#016x}",
src_addr, size);
R_THROW(ResultInvalidCurrentMemory);
}
@@ -79,7 +255,7 @@ Result MapUnmapMemorySanityChecks(const KProcessPageTable& manager, u64 dst_addr
} // namespace
Result SetMemoryPermission(Core::System& system, u64 address, u64 size, MemoryPermission perm) {
LOG_DEBUG(Kernel_SVC, "called, address=0x{:016X}, size={:#X}, perm=0x{:08X}", address, size,
LOG_DEBUG(Kernel_SVC, "called, address={:#016x}, size={:#x}, perm={:#08x}", address, size,
perm);
// Validate address / size.
@@ -92,16 +268,24 @@ Result SetMemoryPermission(Core::System& system, u64 address, u64 size, MemoryPe
R_UNLESS(IsValidSetMemoryPermission(perm), ResultInvalidNewMemoryPermission);
// Validate that the region is in range for the current process.
auto& page_table = GetCurrentProcess(system.Kernel()).GetPageTable();
auto& process = GetCurrentProcess(system.Kernel());
auto& page_table = process.GetPageTable();
R_UNLESS(page_table.Contains(address, size), ResultInvalidCurrentMemory);
// Set the memory attribute.
R_RETURN(page_table.SetMemoryPermission(address, size, perm));
const auto result = page_table.SetMemoryPermission(address, size, perm);
if (result.raw == ResultInvalidCurrentMemory.raw &&
IsHomebrewInPlaceNextLoadCodeRange(process, address, size)) {
R_RETURN(
SetHomebrewInPlaceMemoryPermissionByBlocks(process, address, size, perm, result));
}
R_RETURN(result);
}
Result SetMemoryAttribute(Core::System& system, u64 address, u64 size, u32 mask, u32 attr) {
LOG_DEBUG(Kernel_SVC,
"called, address=0x{:016X}, size={:#X}, mask=0x{:08X}, attribute=0x{:08X}", address,
"called, address={:#016x}, size={:#x}, mask={:#08x}, attribute={:#08x}", address,
size, mask, attr);
// Validate address / size.
@@ -132,32 +316,57 @@ Result SetMemoryAttribute(Core::System& system, u64 address, u64 size, u32 mask,
/// Maps a memory range into a different range.
Result MapMemory(Core::System& system, u64 dst_addr, u64 src_addr, u64 size) {
LOG_TRACE(Kernel_SVC, "called, dst_addr={:#X}, src_addr=0x{:X}, size=0x{:X}", dst_addr,
LOG_TRACE(Kernel_SVC, "called, dst_addr={:#x}, src_addr={:#x}, size={:#x}", dst_addr,
src_addr, size);
auto& page_table{GetCurrentProcess(system.Kernel()).GetPageTable()};
auto& process = GetCurrentProcess(system.Kernel());
auto& page_table = process.GetPageTable();
if (const Result result{MapUnmapMemorySanityChecks(page_table, dst_addr, src_addr, size)};
result.IsError()) {
return result;
}
R_RETURN(page_table.MapMemory(dst_addr, src_addr, size));
const auto result = page_table.MapMemory(dst_addr, src_addr, size);
if (result.raw == ResultInvalidCurrentMemory.raw && process.IsHomebrewInPlaceNextLoad()) {
if (UnlockHomebrewInPlaceDeviceSharedSource(process, src_addr, size, result).IsSuccess()) {
const auto retry_result = page_table.MapMemory(dst_addr, src_addr, size);
if (retry_result.IsError()) {
LOG_WARNING(Kernel_SVC,
"NextLoad in-place: svcMapMemory retry failed after "
"DeviceShared cleanup dst=0x{:016X}, src=0x{:016X}, size=0x{:X}, "
"result={:#X}",
dst_addr, src_addr, size, retry_result.raw);
}
R_RETURN(retry_result);
}
}
R_RETURN(result);
}
/// Unmaps a region that was previously mapped with svcMapMemory
Result UnmapMemory(Core::System& system, u64 dst_addr, u64 src_addr, u64 size) {
LOG_TRACE(Kernel_SVC, "called, dst_addr={:#X}, src_addr=0x{:X}, size=0x{:X}", dst_addr,
LOG_TRACE(Kernel_SVC, "called, dst_addr={:#x}, src_addr={:#x}, size={:#x}", dst_addr,
src_addr, size);
auto& page_table{GetCurrentProcess(system.Kernel()).GetPageTable()};
auto& process = GetCurrentProcess(system.Kernel());
auto& page_table = process.GetPageTable();
if (const Result result{MapUnmapMemorySanityChecks(page_table, dst_addr, src_addr, size)};
result.IsError()) {
return result;
}
R_RETURN(page_table.UnmapMemory(dst_addr, src_addr, size));
const auto result = page_table.UnmapMemory(dst_addr, src_addr, size);
if (result.raw == ResultInvalidCurrentMemory.raw && process.IsHomebrewInPlaceNextLoad()) {
LOG_WARNING(Kernel_SVC,
"NextLoad in-place: svcUnmapMemory failed dst=0x{:016X}, "
"src=0x{:016X}, size=0x{:X}, result={:#X}",
dst_addr, src_addr, size, result.raw);
}
R_RETURN(result);
}
Result SetMemoryPermission64(Core::System& system, uint64_t address, uint64_t size,
@@ -12,7 +12,7 @@ namespace Kernel::Svc {
/// Set the process heap to a given Size. It can both extend and shrink the heap.
Result SetHeapSize(Core::System& system, u64* out_address, u64 size) {
LOG_TRACE(Kernel_SVC, "called, heap_size={:#X}", size);
LOG_TRACE(Kernel_SVC, "called, heap_size={:#x}", size);
// Validate size.
R_UNLESS(Common::IsAligned(size, HeapSizeAlignment), ResultInvalidSize);
@@ -31,10 +31,10 @@ Result SetHeapSize(Core::System& system, u64* out_address, u64 size) {
/// Maps memory at a desired address
Result MapPhysicalMemory(Core::System& system, u64 addr, u64 size) {
LOG_DEBUG(Kernel_SVC, "called, addr=0x{:016X}, size={:#X}", addr, size);
LOG_DEBUG(Kernel_SVC, "called, addr={:#016x}, size={:#x}", addr, size);
if (!Common::IsAligned(addr, Core::Memory::YUZU_PAGESIZE)) {
LOG_ERROR(Kernel_SVC, "Address is not aligned to 4KB, 0x{:016X}", addr);
LOG_ERROR(Kernel_SVC, "Address is not aligned to 4KB, {:#016X}", addr);
R_THROW(ResultInvalidAddress);
}
@@ -63,14 +63,14 @@ Result MapPhysicalMemory(Core::System& system, u64 addr, u64 size) {
if (!page_table.Contains(addr, size)) {
LOG_ERROR(Kernel_SVC,
"Address is not within the address space, addr=0x{:016X}, size=0x{:016X}", addr,
"Address is not within the address space, addr={:#016x}, size={:#016x}", addr,
size);
R_THROW(ResultInvalidMemoryRegion);
}
if (!page_table.IsInAliasRegion(addr, size)) {
LOG_ERROR(Kernel_SVC,
"Address is not within the alias region, addr=0x{:016X}, size=0x{:016X}", addr,
"Address is not within the alias region, addr={:#016x}, size={:#016x}", addr,
size);
R_THROW(ResultInvalidMemoryRegion);
}
@@ -80,10 +80,10 @@ Result MapPhysicalMemory(Core::System& system, u64 addr, u64 size) {
/// Unmaps memory previously mapped via MapPhysicalMemory
Result UnmapPhysicalMemory(Core::System& system, u64 addr, u64 size) {
LOG_DEBUG(Kernel_SVC, "called, addr=0x{:016X}, size={:#X}", addr, size);
LOG_DEBUG(Kernel_SVC, "called, addr={:#016x}, size={:#x}", addr, size);
if (!Common::IsAligned(addr, Core::Memory::YUZU_PAGESIZE)) {
LOG_ERROR(Kernel_SVC, "Address is not aligned to 4KB, 0x{:016X}", addr);
LOG_ERROR(Kernel_SVC, "Address is not aligned to 4KB, {:#016X}", addr);
R_THROW(ResultInvalidAddress);
}
@@ -112,14 +112,14 @@ Result UnmapPhysicalMemory(Core::System& system, u64 addr, u64 size) {
if (!page_table.Contains(addr, size)) {
LOG_ERROR(Kernel_SVC,
"Address is not within the address space, addr=0x{:016X}, size=0x{:016X}", addr,
"Address is not within the address space, addr={:#016x}, size={:#016x}", addr,
size);
R_THROW(ResultInvalidMemoryRegion);
}
if (!page_table.IsInAliasRegion(addr, size)) {
LOG_ERROR(Kernel_SVC,
"Address is not within the alias region, addr=0x{:016X}, size=0x{:016X}", addr,
"Address is not within the alias region, addr={:#016x}, size={:#016x}", addr,
size);
R_THROW(ResultInvalidMemoryRegion);
}
+209 -10
View File
@@ -4,17 +4,216 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <algorithm>
#include <filesystem>
#include <string>
#include <string_view>
#include <vector>
#include "common/fs/fs.h"
#include "common/fs/path_util.h"
#include "common/input.h"
#include "core/core.h"
#include "core/file_sys/vfs/vfs_types.h"
#include "core/hle/kernel/k_process.h"
#include "core/hle/kernel/k_thread.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/physical_core.h"
#include "core/hle/kernel/svc.h"
#include "core/hle/service/hid/hid_server.h"
#include "core/hle/service/nvdrv/nvdrv_interface.h"
#include "core/hle/service/sm/sm.h"
#include "core/loader/nro.h"
#include "hid_core/frontend/emulated_controller.h"
#include "hid_core/hid_core.h"
#include "hid_core/resource_manager.h"
namespace Kernel::Svc {
namespace {
constexpr size_t HomebrewNextLoadPathSize = 0x200;
constexpr size_t HomebrewNextLoadArgvSize = 0x800;
std::string ReadHomebrewString(Core::Memory::Memory& memory, KProcessAddress address,
size_t max_size) {
if (GetInteger(address) == 0) {
return {};
}
return memory.ReadCString(Common::ProcessAddress{GetInteger(address)}, max_size);
}
} // namespace
/// Exits the current process
void ExitProcess(Core::System& system) {
void ExitProcess(Core::System& system, std::span<uint64_t, 8> args) {
auto* current_process = GetCurrentProcessPointer(system.Kernel());
auto* current_thread = GetCurrentThreadPointer(system.Kernel());
LOG_INFO(Kernel_SVC, "Process {} exiting", current_process->GetProcessId());
const auto next_load_path_addr = current_process->GetHomebrewNextLoadPathAddr();
const auto next_load_argv_addr = current_process->GetHomebrewNextLoadArgvAddr();
if (GetInteger(next_load_path_addr) != 0) {
auto& memory = current_process->GetMemory();
const auto next_load_path =
ReadHomebrewString(memory, next_load_path_addr, HomebrewNextLoadPathSize);
const auto next_load_argv =
ReadHomebrewString(memory, next_load_argv_addr, HomebrewNextLoadArgvSize);
if (!next_load_path.empty()) {
auto guest_path = Common::FS::SanitizePath(next_load_path);
constexpr std::string_view SdmcPrefix{"sdmc:"};
FileSys::VirtualFile file{};
const bool is_sdmc_path = guest_path.rfind(SdmcPrefix, 0) == 0;
const bool is_absolute_guest_path = !guest_path.empty() && guest_path.front() == '/';
if (is_sdmc_path || is_absolute_guest_path) {
auto relative_path =
is_sdmc_path ? guest_path.substr(SdmcPrefix.size()) : guest_path;
while (!relative_path.empty() && relative_path.front() == '/') {
relative_path.erase(relative_path.begin());
}
const auto host_path = Common::FS::GetEdenPath(Common::FS::EdenPath::SDMCDir) /
std::filesystem::path{Common::FS::ToU8String(relative_path)};
const auto host_path_string = Common::FS::PathToUTF8String(host_path);
file = Core::GetGameFileFromPath(system.GetFilesystem(), host_path_string);
if (!file) {
LOG_WARNING(Kernel_SVC,
"NextLoad: failed to open guest_path='{}', host_path='{}'",
next_load_path, host_path_string);
}
} else {
file = Core::GetGameFileFromPath(system.GetFilesystem(), guest_path);
if (!file) {
LOG_WARNING(Kernel_SVC, "NextLoad: failed to open guest_path='{}'",
next_load_path);
}
}
if (file) {
const auto nvdrv =
system.ServiceManager().GetService<Service::Nvidia::NVDRV>("nvdrv:s");
if (!nvdrv) {
LOG_WARNING(Kernel_SVC, "NextLoad: NVDRV service unavailable for reset");
} else {
nvdrv->GetModule()->ResetForProcess(current_process);
}
auto& page_table = current_process->GetPageTable();
const u64 heap_start = GetInteger(page_table.GetHeapRegionStart());
const u64 heap_size = page_table.GetHeapRegionSize();
const u64 heap_end = heap_start + heap_size;
if (heap_start != 0 && heap_size != 0 && heap_end > heap_start) {
struct DeviceSharedBlock {
u64 address;
u64 size;
u16 device_use_count;
};
std::vector<DeviceSharedBlock> blocks;
for (u64 cursor = heap_start; cursor < heap_end;) {
KMemoryInfo info;
PageInfo page_info;
const auto query_result =
page_table.QueryInfo(std::addressof(info), std::addressof(page_info),
cursor);
if (query_result.IsError()) {
LOG_WARNING(Kernel_SVC,
"NextLoad: DeviceShared heap cleanup query failed "
"address=0x{:016X}, result={:#X}",
cursor, query_result.raw);
break;
}
const u64 block_end = (std::min<u64>)(info.GetEndAddress(), heap_end);
if (block_end <= cursor) {
LOG_WARNING(Kernel_SVC,
"NextLoad: DeviceShared heap cleanup walk stalled "
"cursor=0x{:016X}, block=0x{:016X}/0x{:X}",
cursor, info.GetAddress(), info.GetSize());
break;
}
const bool is_device_shared =
True(info.GetState() & KMemoryState::FlagCanDeviceMap) &&
info.GetAttribute() == KMemoryAttribute::DeviceShared &&
info.m_device_use_count > 0;
if (is_device_shared) {
blocks.push_back(DeviceSharedBlock{
.address = cursor,
.size = block_end - cursor,
.device_use_count = info.m_device_use_count,
});
}
cursor = block_end;
}
for (const auto& block : blocks) {
for (u16 unlock = 0; unlock < block.device_use_count; unlock++) {
const auto unlock_result =
page_table.UnlockForDeviceAddressSpace(block.address, block.size);
if (unlock_result.IsError()) {
LOG_WARNING(Kernel_SVC,
"NextLoad: DeviceShared heap cleanup unlock failed "
"address=0x{:016X}, size=0x{:X}, remaining={}, "
"result={:#X}",
block.address, block.size,
block.device_use_count - unlock - 1, unlock_result.raw);
break;
}
}
}
}
if (Loader::LoadNroInPlace(system, *current_process, *current_thread, file,
next_load_path, next_load_argv)) {
const auto aruid = current_process->GetProcessId();
if (const auto hid =
system.ServiceManager().GetService<Service::HID::IHidServer>("hid")) {
const auto resource_manager = hid->GetResourceManager();
resource_manager->UnregisterAppletResourceUserId(aruid);
const auto register_result =
resource_manager->RegisterAppletResourceUserId(aruid, true);
if (register_result.IsError()) {
LOG_WARNING(Kernel_SVC,
"NextLoad: failed to register HID applet resource "
"aruid={}, result={:#X}",
aruid, register_result.raw);
}
} else {
LOG_WARNING(Kernel_SVC, "NextLoad: HID service unavailable for reset");
}
auto& hid_core = system.HIDCore();
hid_core.DisableAllControllerConfiguration();
hid_core.SetSupportedStyleTag({Core::HID::NpadStyleSet::All});
hid_core.ReloadInputDevices();
const auto activate_controller = [&](Core::HID::NpadIdType npad_id) {
auto* controller = hid_core.GetEmulatedController(npad_id);
if (controller == nullptr) {
return;
}
(void)controller->SetPollingMode(Core::HID::EmulatedDeviceIndex::AllDevices,
Common::Input::PollingMode::Active);
};
activate_controller(Core::HID::NpadIdType::Player1);
activate_controller(Core::HID::NpadIdType::Handheld);
system.Kernel().CurrentPhysicalCore().LoadContext(current_thread);
const auto& context = current_thread->GetContext();
for (size_t i = 0; i < args.size(); i++) {
args[i] = context.r[i];
}
return;
}
}
}
}
ASSERT_MSG(current_process->GetState() == KProcess::State::Running,
"Process has already exited");
@@ -23,7 +222,7 @@ void ExitProcess(Core::System& system) {
/// Gets the ID of the specified process or a specified thread's owning process.
Result GetProcessId(Core::System& system, u64* out_process_id, Handle handle) {
LOG_DEBUG(Kernel_SVC, "called handle=0x{:08X}", handle);
LOG_DEBUG(Kernel_SVC, "called handle={:#08x}", handle);
// Get the object from the handle table.
KScopedAutoObject obj = GetCurrentProcess(system.Kernel())
@@ -55,7 +254,7 @@ Result GetProcessId(Core::System& system, u64* out_process_id, Handle handle) {
Result GetProcessList(Core::System& system, s32* out_num_processes, u64 out_process_ids,
int32_t out_process_ids_size) {
LOG_DEBUG(Kernel_SVC, "called. out_process_ids=0x{:016X}, out_process_ids_size={}",
LOG_DEBUG(Kernel_SVC, "called. out_process_ids={:#016x}, out_process_ids_size={}",
out_process_ids, out_process_ids_size);
// If the supplied size is negative or greater than INT32_MAX / sizeof(u64), bail.
@@ -71,7 +270,7 @@ Result GetProcessList(Core::System& system, s32* out_num_processes, u64 out_proc
if (out_process_ids_size > 0 &&
!GetCurrentProcess(kernel).GetPageTable().Contains(out_process_ids, total_copy_size)) {
LOG_ERROR(Kernel_SVC, "Address range outside address space. begin=0x{:016X}, end=0x{:016X}",
LOG_ERROR(Kernel_SVC, "Address range outside address space. begin={:#016x}, end={:#016x}",
out_process_ids, out_process_ids + total_copy_size);
R_THROW(ResultInvalidCurrentMemory);
}
@@ -95,12 +294,12 @@ Result GetProcessList(Core::System& system, s32* out_num_processes, u64 out_proc
Result GetProcessInfo(Core::System& system, s64* out, Handle process_handle,
ProcessInfoType info_type) {
LOG_DEBUG(Kernel_SVC, "called, handle=0x{:08X}, type={:#X}", process_handle, info_type);
LOG_DEBUG(Kernel_SVC, "called, handle={:#08x}, type={:#x}", process_handle, info_type);
const auto& handle_table = GetCurrentProcess(system.Kernel()).GetHandleTable();
KScopedAutoObject process = handle_table.GetObject<KProcess>(system.Kernel(), process_handle);
if (process.IsNull()) {
LOG_ERROR(Kernel_SVC, "Process handle does not exist, process_handle=0x{:08X}",
LOG_ERROR(Kernel_SVC, "Process handle does not exist, process_handle={:#08x}",
process_handle);
R_THROW(ResultInvalidHandle);
}
@@ -132,8 +331,8 @@ Result TerminateProcess(Core::System& system, Handle process_handle) {
R_THROW(ResultNotImplemented);
}
void ExitProcess64(Core::System& system) {
ExitProcess(system);
void ExitProcess64(Core::System& system, std::span<uint64_t, 8> args) {
ExitProcess(system, args);
}
Result GetProcessId64(Core::System& system, uint64_t* out_process_id, Handle process_handle) {
@@ -164,8 +363,8 @@ Result GetProcessInfo64(Core::System& system, int64_t* out_info, Handle process_
R_RETURN(GetProcessInfo(system, out_info, process_handle, info_type));
}
void ExitProcess64From32(Core::System& system) {
ExitProcess(system);
void ExitProcess64From32(Core::System& system, std::span<uint64_t, 8> args) {
ExitProcess(system, args);
}
Result GetProcessId64From32(Core::System& system, uint64_t* out_process_id, Handle process_handle) {
+27 -27
View File
@@ -32,7 +32,7 @@ constexpr bool IsValidProcessMemoryPermission(Svc::MemoryPermission perm) {
Result SetProcessMemoryPermission(Core::System& system, Handle process_handle, u64 address,
u64 size, Svc::MemoryPermission perm) {
LOG_TRACE(Kernel_SVC,
"called, process_handle={:#X}, addr=0x{:X}, size=0x{:X}, permissions=0x{:08X}",
"called, process_handle={:#x}, addr={:#x}, size={:#x}, permissions={:#08x}",
process_handle, address, size, perm);
// Validate the address/size.
@@ -62,7 +62,7 @@ Result SetProcessMemoryPermission(Core::System& system, Handle process_handle, u
Result MapProcessMemory(Core::System& system, u64 dst_address, Handle process_handle,
u64 src_address, u64 size) {
LOG_TRACE(Kernel_SVC,
"called, dst_address={:#X}, process_handle=0x{:X}, src_address=0x{:X}, size=0x{:X}",
"called, dst_address={:#x}, process_handle={:#x}, src_address={:#x}, size={:#x}",
dst_address, process_handle, src_address, size);
// Validate the address/size.
@@ -103,7 +103,7 @@ Result MapProcessMemory(Core::System& system, u64 dst_address, Handle process_ha
Result UnmapProcessMemory(Core::System& system, u64 dst_address, Handle process_handle,
u64 src_address, u64 size) {
LOG_TRACE(Kernel_SVC,
"called, dst_address={:#X}, process_handle=0x{:X}, src_address=0x{:X}, size=0x{:X}",
"called, dst_address={:#x}, process_handle={:#x}, src_address={:#x}, size={:#x}",
dst_address, process_handle, src_address, size);
// Validate the address/size.
@@ -136,39 +136,39 @@ Result UnmapProcessMemory(Core::System& system, u64 dst_address, Handle process_
Result MapProcessCodeMemory(Core::System& system, Handle process_handle, u64 dst_address,
u64 src_address, u64 size) {
LOG_DEBUG(Kernel_SVC,
"called. process_handle=0x{:08X}, dst_address=0x{:016X}, "
"src_address=0x{:016X}, size=0x{:016X}",
"called. process_handle={:#08x}, dst_address={:#016x}, "
"src_address={:#016x}, size={:#016x}",
process_handle, dst_address, src_address, size);
if (!Common::IsAligned(src_address, Core::Memory::YUZU_PAGESIZE)) {
LOG_ERROR(Kernel_SVC, "src_address is not page-aligned (src_address=0x{:016X}).",
LOG_ERROR(Kernel_SVC, "src_address is not page-aligned (src_address={:#016X}).",
src_address);
R_THROW(ResultInvalidAddress);
}
if (!Common::IsAligned(dst_address, Core::Memory::YUZU_PAGESIZE)) {
LOG_ERROR(Kernel_SVC, "dst_address is not page-aligned (dst_address=0x{:016X}).",
LOG_ERROR(Kernel_SVC, "dst_address is not page-aligned (dst_address={:#016X}).",
dst_address);
R_THROW(ResultInvalidAddress);
}
if (size == 0 || !Common::IsAligned(size, Core::Memory::YUZU_PAGESIZE)) {
LOG_ERROR(Kernel_SVC, "Size is zero or not page-aligned (size=0x{:016X})", size);
LOG_ERROR(Kernel_SVC, "Size is zero or not page-aligned (size={:#016X})", size);
R_THROW(ResultInvalidSize);
}
if (!IsValidAddressRange(dst_address, size)) {
LOG_ERROR(Kernel_SVC,
"Destination address range overflows the address space (dst_address=0x{:016X}, "
"size=0x{:016X}).",
"Destination address range overflows the address space (dst_address={:#016x}, "
"size={:#016x}).",
dst_address, size);
R_THROW(ResultInvalidCurrentMemory);
}
if (!IsValidAddressRange(src_address, size)) {
LOG_ERROR(Kernel_SVC,
"Source address range overflows the address space (src_address=0x{:016X}, "
"size=0x{:016X}).",
"Source address range overflows the address space (src_address={:#016x}, "
"size={:#016x}).",
src_address, size);
R_THROW(ResultInvalidCurrentMemory);
}
@@ -176,7 +176,7 @@ Result MapProcessCodeMemory(Core::System& system, Handle process_handle, u64 dst
const auto& handle_table = GetCurrentProcess(system.Kernel()).GetHandleTable();
KScopedAutoObject process = handle_table.GetObject<KProcess>(system.Kernel(), process_handle);
if (process.IsNull()) {
LOG_ERROR(Kernel_SVC, "Invalid process handle specified (handle=0x{:08X}).",
LOG_ERROR(Kernel_SVC, "Invalid process handle specified (handle={:#08x}).",
process_handle);
R_THROW(ResultInvalidHandle);
}
@@ -184,8 +184,8 @@ Result MapProcessCodeMemory(Core::System& system, Handle process_handle, u64 dst
auto& page_table = process->GetPageTable();
if (!page_table.Contains(src_address, size)) {
LOG_ERROR(Kernel_SVC,
"Source address range is not within the address space (src_address=0x{:016X}, "
"size=0x{:016X}).",
"Source address range is not within the address space (src_address={:#016x}, "
"size={:#016x}).",
src_address, size);
R_THROW(ResultInvalidCurrentMemory);
}
@@ -197,39 +197,39 @@ Result MapProcessCodeMemory(Core::System& system, Handle process_handle, u64 dst
Result UnmapProcessCodeMemory(Core::System& system, Handle process_handle, u64 dst_address,
u64 src_address, u64 size) {
LOG_DEBUG(Kernel_SVC,
"called. process_handle=0x{:08X}, dst_address=0x{:016X}, src_address=0x{:016X}, "
"size=0x{:016X}",
"called. process_handle={:#08x}, dst_address={:#016x}, src_address={:#016x}, "
"size={:#016x}",
process_handle, dst_address, src_address, size);
if (!Common::IsAligned(dst_address, Core::Memory::YUZU_PAGESIZE)) {
LOG_ERROR(Kernel_SVC, "dst_address is not page-aligned (dst_address=0x{:016X}).",
LOG_ERROR(Kernel_SVC, "dst_address is not page-aligned (dst_address={:#016X}).",
dst_address);
R_THROW(ResultInvalidAddress);
}
if (!Common::IsAligned(src_address, Core::Memory::YUZU_PAGESIZE)) {
LOG_ERROR(Kernel_SVC, "src_address is not page-aligned (src_address=0x{:016X}).",
LOG_ERROR(Kernel_SVC, "src_address is not page-aligned (src_address={:#016X}).",
src_address);
R_THROW(ResultInvalidAddress);
}
if (size == 0 || !Common::IsAligned(size, Core::Memory::YUZU_PAGESIZE)) {
LOG_ERROR(Kernel_SVC, "Size is zero or not page-aligned (size=0x{:016X}).", size);
LOG_ERROR(Kernel_SVC, "Size is zero or not page-aligned (size={:#016X}).", size);
R_THROW(ResultInvalidSize);
}
if (!IsValidAddressRange(dst_address, size)) {
LOG_ERROR(Kernel_SVC,
"Destination address range overflows the address space (dst_address=0x{:016X}, "
"size=0x{:016X}).",
"Destination address range overflows the address space (dst_address={:#016x}, "
"size={:#016x}).",
dst_address, size);
R_THROW(ResultInvalidCurrentMemory);
}
if (!IsValidAddressRange(src_address, size)) {
LOG_ERROR(Kernel_SVC,
"Source address range overflows the address space (src_address=0x{:016X}, "
"size=0x{:016X}).",
"Source address range overflows the address space (src_address={:#016x}, "
"size={:#016x}).",
src_address, size);
R_THROW(ResultInvalidCurrentMemory);
}
@@ -237,7 +237,7 @@ Result UnmapProcessCodeMemory(Core::System& system, Handle process_handle, u64 d
const auto& handle_table = GetCurrentProcess(system.Kernel()).GetHandleTable();
KScopedAutoObject process = handle_table.GetObject<KProcess>(system.Kernel(), process_handle);
if (process.IsNull()) {
LOG_ERROR(Kernel_SVC, "Invalid process handle specified (handle=0x{:08X}).",
LOG_ERROR(Kernel_SVC, "Invalid process handle specified (handle={:#08x}).",
process_handle);
R_THROW(ResultInvalidHandle);
}
@@ -245,8 +245,8 @@ Result UnmapProcessCodeMemory(Core::System& system, Handle process_handle, u64 d
auto& page_table = process->GetPageTable();
if (!page_table.Contains(src_address, size)) {
LOG_ERROR(Kernel_SVC,
"Source address range is not within the address space (src_address=0x{:016X}, "
"size=0x{:016X}).",
"Source address range is not within the address space (src_address={:#016x}, "
"size={:#016x}).",
src_address, size);
R_THROW(ResultInvalidCurrentMemory);
}
+4 -4
View File
@@ -13,8 +13,8 @@ namespace Kernel::Svc {
Result QueryMemory(Core::System& system, uint64_t out_memory_info, PageInfo* out_page_info,
u64 query_address) {
LOG_TRACE(Kernel_SVC,
"called, out_memory_info=0x{:016X}, "
"query_address=0x{:016X}",
"called, out_memory_info={:#016x}, "
"query_address={:#016x}",
out_memory_info, query_address);
// Query memory is just QueryProcessMemory on the current process.
@@ -24,11 +24,11 @@ Result QueryMemory(Core::System& system, uint64_t out_memory_info, PageInfo* out
Result QueryProcessMemory(Core::System& system, uint64_t out_memory_info, PageInfo* out_page_info,
Handle process_handle, uint64_t address) {
LOG_TRACE(Kernel_SVC, "called process=0x{:08X} address={:X}", process_handle, address);
LOG_TRACE(Kernel_SVC, "called process={:#08x} address={:X}", process_handle, address);
const auto& handle_table = GetCurrentProcess(system.Kernel()).GetHandleTable();
KScopedAutoObject process = handle_table.GetObject<KProcess>(system.Kernel(), process_handle);
if (process.IsNull()) {
LOG_ERROR(Kernel_SVC, "Process handle does not exist, process_handle=0x{:08X}",
LOG_ERROR(Kernel_SVC, "Process handle does not exist, process_handle={:#08x}",
process_handle);
R_THROW(ResultInvalidHandle);
}
@@ -32,7 +32,7 @@ constexpr bool IsValidSharedMemoryPermission(MemoryPermission perm) {
Result MapSharedMemory(Core::System& system, Handle shmem_handle, u64 address, u64 size,
Svc::MemoryPermission map_perm) {
LOG_TRACE(Kernel_SVC,
"called, shared_memory_handle={:#X}, addr=0x{:X}, size=0x{:X}, permissions=0x{:08X}",
"called, shared_memory_handle={:#x}, addr={:#x}, size={:#x}, permissions={:#08x}",
shmem_handle, address, size, map_perm);
// Validate the address/size.
@@ -17,7 +17,7 @@ namespace Kernel::Svc {
/// Close a handle
Result CloseHandle(Core::System& system, Handle handle) {
LOG_TRACE(Kernel_SVC, "Closing handle 0x{:08X}", handle);
LOG_TRACE(Kernel_SVC, "Closing handle {:#08x}", handle);
// Remove the handle.
R_UNLESS(GetCurrentProcess(system.Kernel()).GetHandleTable().Remove(system.Kernel(), handle),
@@ -28,7 +28,7 @@ Result CloseHandle(Core::System& system, Handle handle) {
/// Clears the signaled state of an event or process.
Result ResetSignal(Core::System& system, Handle handle) {
LOG_DEBUG(Kernel_SVC, "called handle 0x{:08X}", handle);
LOG_DEBUG(Kernel_SVC, "called handle {:#08x}", handle);
// Get the current handle table.
const auto& handle_table = GetCurrentProcess(system.Kernel()).GetHandleTable();
@@ -103,7 +103,7 @@ Result WaitSynchronization(Core::System& system, int32_t* out_index, u64 user_ha
/// Resumes a thread waiting on WaitSynchronization
Result CancelSynchronization(Core::System& system, Handle handle) {
LOG_TRACE(Kernel_SVC, "called handle={:#X}", handle);
LOG_TRACE(Kernel_SVC, "called handle={:#x}", handle);
// Get the thread from its handle.
KScopedAutoObject thread = GetCurrentProcess(system.Kernel()).GetHandleTable().GetObject<KThread>(system.Kernel(), handle);
+7 -7
View File
@@ -26,8 +26,8 @@ constexpr bool IsValidVirtualCoreId(int32_t core_id) {
Result CreateThread(Core::System& system, Handle* out_handle, u64 entry_point, u64 arg,
u64 stack_bottom, s32 priority, s32 core_id) {
LOG_DEBUG(Kernel_SVC,
"called entry_point=0x{:08X}, arg=0x{:08X}, stack_bottom=0x{:08X}, "
"priority=0x{:08X}, core_id=0x{:08X}",
"called entry_point={:#08x}, arg={:#08x}, stack_bottom={:#08x}, "
"priority={:#08x}, core_id={:#08x}",
entry_point, arg, stack_bottom, priority, core_id);
// Adjust core id, if it's the default magic.
@@ -85,7 +85,7 @@ Result CreateThread(Core::System& system, Handle* out_handle, u64 entry_point, u
/// Starts the thread for the provided handle
Result StartThread(Core::System& system, Handle thread_handle) {
LOG_DEBUG(Kernel_SVC, "called thread=0x{:08X}", thread_handle);
LOG_DEBUG(Kernel_SVC, "called thread={:#08x}", thread_handle);
// Get the thread from its handle.
KScopedAutoObject thread = GetCurrentProcess(system.Kernel()).GetHandleTable().GetObject<KThread>(system.Kernel(), thread_handle);
@@ -143,7 +143,7 @@ void SleepThread(Core::System& system, s64 ns) {
/// Gets the thread context
Result GetThreadContext3(Core::System& system, u64 out_context, Handle thread_handle) {
LOG_DEBUG(Kernel_SVC, "called, out_context=0x{:08X}, thread_handle={:#X}", out_context, thread_handle);
LOG_DEBUG(Kernel_SVC, "called, out_context={:#08x}, thread_handle={:#x}", out_context, thread_handle);
// Get the thread from its handle.
KScopedAutoObject thread = GetCurrentProcess(system.Kernel()).GetHandleTable().GetObject<KThread>(system.Kernel(), thread_handle);
@@ -202,7 +202,7 @@ Result GetThreadList(Core::System& system, s32* out_num_threads, u64 out_thread_
// TODO: Handle this case when debug events are supported.
UNIMPLEMENTED_IF(debug_handle != InvalidHandle);
LOG_DEBUG(Kernel_SVC, "called. out_thread_ids=0x{:016X}, out_thread_ids_size={}",
LOG_DEBUG(Kernel_SVC, "called. out_thread_ids={:#016x}, out_thread_ids_size={}",
out_thread_ids, out_thread_ids_size);
// If the size is negative or larger than INT32_MAX / sizeof(u64)
@@ -217,7 +217,7 @@ Result GetThreadList(Core::System& system, s32* out_num_threads, u64 out_thread_
if (out_thread_ids_size > 0 &&
!current_process->GetPageTable().Contains(out_thread_ids, total_copy_size)) {
LOG_ERROR(Kernel_SVC, "Address range outside address space. begin=0x{:016X}, end=0x{:016X}",
LOG_ERROR(Kernel_SVC, "Address range outside address space. begin={:#016x}, end={:#016x}",
out_thread_ids, out_thread_ids + total_copy_size);
R_THROW(ResultInvalidCurrentMemory);
}
@@ -239,7 +239,7 @@ Result GetThreadList(Core::System& system, s32* out_num_threads, u64 out_thread_
Result GetThreadCoreMask(Core::System& system, s32* out_core_id, u64* out_affinity_mask,
Handle thread_handle) {
LOG_TRACE(Kernel_SVC, "called, handle=0x{:08X}", thread_handle);
LOG_TRACE(Kernel_SVC, "called, handle={:#08x}", thread_handle);
// Get the thread from its handle.
KScopedAutoObject thread = GetCurrentProcess(system.Kernel()).GetHandleTable().GetObject<KThread>(system.Kernel(), thread_handle);
-19
View File
@@ -41,7 +41,6 @@ ButtonPoller::ButtonPoller(Core::System& system, WindowSystem& window_system) {
Core::HID::ControllerUpdateCallback engine_callback{
.on_change = [this, &window_system](Core::HID::ControllerTriggerType type) {
if (type == Core::HID::ControllerTriggerType::Button) {
std::unique_lock lk{m_mutex};
OnButtonStateChanged(window_system);
}
},
@@ -52,29 +51,11 @@ ButtonPoller::ButtonPoller(Core::System& system, WindowSystem& window_system) {
m_handheld_key = m_handheld->SetCallback(engine_callback);
m_player1 = system.HIDCore().GetEmulatedController(Core::HID::NpadIdType::Player1);
m_player1_key = m_player1->SetCallback(engine_callback);
m_thread = std::jthread([this, &window_system](std::stop_token stop_token) {
Common::SetCurrentThreadName("ButtonPoller");
while (!stop_token.stop_requested()) {
using namespace std::chrono_literals;
std::unique_lock lk{m_mutex};
m_cv.wait_for(lk, 50ms);
if (stop_token.stop_requested())
break;
OnButtonStateChanged(window_system);
std::this_thread::sleep_for(5ms);
}
});
}
ButtonPoller::~ButtonPoller() {
m_handheld->DeleteCallback(m_handheld_key);
m_player1->DeleteCallback(m_player1_key);
m_cv.notify_all();
if (m_thread.joinable()) {
m_thread.request_stop();
m_thread.join();
}
}
void ButtonPoller::OnButtonStateChanged(WindowSystem& window_system) {
-3
View File
@@ -33,9 +33,6 @@ public:
void OnButtonStateChanged(WindowSystem& window_system);
private:
std::mutex m_mutex;
std::condition_variable m_cv;
std::jthread m_thread;
std::optional<std::chrono::steady_clock::time_point> m_home_button_press_start{};
std::optional<std::chrono::steady_clock::time_point> m_capture_button_press_start{};
std::optional<std::chrono::steady_clock::time_point> m_power_button_press_start{};
+12 -12
View File
@@ -26,13 +26,12 @@ EventObserver::EventObserver(Core::System& system, WindowSystem& window_system)
m_window_system.SetEventObserver(this);
m_wakeup_holder.SetUserData(static_cast<uintptr_t>(UserDataTag::WakeupEvent));
m_wakeup_holder.LinkToMultiWait(std::addressof(m_multi_wait));
m_thread = std::thread([this] {
m_thread = std::jthread([this](std::stop_token stop_token) {
Common::SetCurrentThreadName("am:EventObserver");
while (true) {
auto* signaled_holder = this->WaitSignaled();
if (!signaled_holder) {
while (!stop_token.stop_requested()) {
auto* signaled_holder = this->WaitSignaled(stop_token);
if (!signaled_holder)
break;
}
this->Process(signaled_holder);
}
});
@@ -40,9 +39,12 @@ EventObserver::EventObserver(Core::System& system, WindowSystem& window_system)
EventObserver::~EventObserver() {
// Signal thread and wait for processing to finish.
m_stop_source.request_stop();
m_wakeup_event.Signal(m_system.Kernel());
m_thread.join();
if (m_thread.joinable()) {
// Signal thread and wait for processing to finish.
m_thread.request_stop();
m_wakeup_event.Signal(m_system.Kernel());
m_thread.join();
}
// Free remaining owned sessions.
auto it = m_process_holder_list.begin();
@@ -88,12 +90,12 @@ void EventObserver::LinkDeferred() {
m_multi_wait.MoveAll(std::addressof(m_deferred_wait_list));
}
MultiWaitHolder* EventObserver::WaitSignaled() {
MultiWaitHolder* EventObserver::WaitSignaled(std::stop_token stop_token) {
while (true) {
this->LinkDeferred();
// If we're done, return before we start waiting.
if (m_stop_source.stop_requested()) {
if (stop_token.stop_requested()) {
return nullptr;
}
@@ -131,7 +133,6 @@ void EventObserver::OnProcessEvent(ProcessHolder* holder) {
// Check process state.
auto& applet = holder->GetApplet();
auto& process = holder->GetProcess();
{
std::scoped_lock lk{m_lock, applet.lock};
if (process.IsTerminated()) {
@@ -156,7 +157,6 @@ void EventObserver::OnProcessEvent(ProcessHolder* holder) {
void EventObserver::DestroyAppletProcessHolderLocked(ProcessHolder* holder) {
// Remove from owned list.
m_process_holder_list.erase(m_process_holder_list.iterator_to(*holder));
// Destroy and free.
delete holder;
}
+2 -8
View File
@@ -32,19 +32,14 @@ public:
private:
void LinkDeferred();
MultiWaitHolder* WaitSignaled();
MultiWaitHolder* WaitSignaled(std::stop_token stop_token);
void Process(MultiWaitHolder* holder);
bool WaitAndProcessImpl();
void LoopProcess();
private:
void OnWakeupEvent(MultiWaitHolder* holder);
void OnProcessEvent(ProcessHolder* holder);
private:
void DestroyAppletProcessHolderLocked(ProcessHolder* holder);
private:
// System reference and context.
Core::System& m_system;
KernelHelpers::ServiceContext m_context;
@@ -67,8 +62,7 @@ private:
MultiWait m_deferred_wait_list;
// Processing thread.
std::thread m_thread{};
std::stop_source m_stop_source{};
std::jthread m_thread{};
};
} // namespace Service::AM
@@ -88,7 +88,7 @@ Result IAudioRenderer::RequestUpdateAuto(
const auto result = impl->RequestUpdate(input, out_performance_buffer, out_buffer);
if (result.IsFailure()) {
LOG_ERROR(Service_Audio, "RequestUpdate failed error 0x{:02X}!", result.GetDescription());
LOG_ERROR(Service_Audio, "RequestUpdate failed error {:#02x}!", result.GetDescription());
}
R_RETURN(result);
+3 -3
View File
@@ -76,7 +76,7 @@ Result IAlbumAccessorService::GetAlbumFileList(
}
Result IAlbumAccessorService::DeleteAlbumFile(AlbumFileId file_id) {
LOG_INFO(Service_Capture, "called, application_id=0x{:0x}, storage={}, type={}",
LOG_INFO(Service_Capture, "called, application_id={:#0x}, storage={}, type={}",
file_id.application_id, file_id.storage, file_id.type);
const Result result = manager->DeleteAlbumFile(file_id);
@@ -120,7 +120,7 @@ Result IAlbumAccessorService::LoadAlbumScreenShotImageEx1(
OutLargeData<LoadAlbumScreenShotImageOutput, BufferAttr_HipcMapAlias> out_image_output,
OutArray<u8, BufferAttr_HipcMapAlias | BufferAttr_HipcMapTransferAllowsNonSecure> out_image,
OutArray<u8, BufferAttr_HipcMapAlias> out_buffer) {
LOG_INFO(Service_Capture, "called, application_id=0x{:0x}, storage={}, type={}, flags={}",
LOG_INFO(Service_Capture, "called, application_id={:#0x}, storage={}, type={}, flags={}",
file_id.application_id, file_id.storage, file_id.type, decoder_options.flags);
const Result result =
@@ -133,7 +133,7 @@ Result IAlbumAccessorService::LoadAlbumScreenShotThumbnailImageEx1(
OutLargeData<LoadAlbumScreenShotImageOutput, BufferAttr_HipcMapAlias> out_image_output,
OutArray<u8, BufferAttr_HipcMapAlias | BufferAttr_HipcMapTransferAllowsNonSecure> out_image,
OutArray<u8, BufferAttr_HipcMapAlias> out_buffer) {
LOG_INFO(Service_Capture, "called, application_id=0x{:0x}, storage={}, type={}, flags={}",
LOG_INFO(Service_Capture, "called, application_id={:#0x}, storage={}, type={}, flags={}",
file_id.application_id, file_id.storage, file_id.type, decoder_options.flags);
const Result result = manager->LoadAlbumScreenShotThumbnail(*out_image_output, out_image,
+1 -1
View File
@@ -444,7 +444,7 @@ void CmifReplyWrapImpl(HLERequestContext& ctx, T& t, Result (T::*f)(A...)) {
const bool is_domain = mgr ? mgr->IsDomain() : false;
ASSERT_MSG(!is_domain,
"Non-domain reply used on domain session\n"
"Service={} (TIPC={} CmdType={} Cmd=0x{:08X}\n"
"Service={} (TIPC={} CmdType={} Cmd={:#08x}\n"
"HasDomainHeader={} DomainHandlers={}\nDesc={}",
t.GetServiceName(), ctx.IsTipc(),
u32(ctx.GetCommandType()), u32(ctx.GetCommand()),
+5 -5
View File
@@ -71,9 +71,9 @@ static void GenerateErrorReport(Core::System& system, Result error_code, const F
std::string crash_report = fmt::format(
"Eden {}-{} crash report\n"
"Title ID: {:016x}\n"
"Result: {:#X} ({:04}-{:04d})\n"
"Set flags: 0x{:16X}\n"
"Program entry point: 0x{:16X}\n"
"Result: {:#x} ({:04}-{:04d})\n"
"Set flags: {:#16X}\n"
"Program entry point: {:#16X}\n"
"\n",
Common::g_scm_branch, Common::g_scm_desc, title_id, error_code.raw,
2000 + static_cast<u32>(error_code.GetModule()),
@@ -98,7 +98,7 @@ static void GenerateErrorReport(Core::System& system, Result error_code, const F
}
crash_report += fmt::format("Architecture: {}\n", info.ArchAsString());
crash_report += fmt::format("Unknown 10: 0x{:016x}\n", info.unk10);
crash_report += fmt::format("Unknown 10: {:#016x}\n", info.unk10);
}
LOG_ERROR(Service_Fatal, "{}", crash_report);
@@ -111,7 +111,7 @@ static void GenerateErrorReport(Core::System& system, Result error_code, const F
static void ThrowFatalError(Core::System& system, Result error_code, FatalType fatal_type,
const FatalInfo& info) {
LOG_ERROR(Service_Fatal, "Threw fatal error type {} with error code {:#X}", fatal_type,
LOG_ERROR(Service_Fatal, "Threw fatal error type {} with error code {:#x}", fatal_type,
error_code.raw);
switch (fatal_type) {
+15 -8
View File
@@ -4,6 +4,7 @@
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <string_view>
#include <utility>
#include "common/assert.h"
@@ -257,7 +258,7 @@ Result VfsDirectoryServiceWrapper::OpenFile(FileSys::VirtualFile* out_file,
npath.remove_prefix(1);
}
auto file = backing->GetFileRelative(npath);
auto file = backing->GetFileRelative(npath, mode);
if (file == nullptr) {
return FileSys::ResultPathNotFound;
}
@@ -333,14 +334,16 @@ FileSystemController::~FileSystemController() = default;
Result FileSystemController::RegisterProcess(
ProcessId process_id, ProgramId program_id,
std::shared_ptr<FileSys::RomFSFactory>&& romfs_factory) {
std::shared_ptr<FileSys::RomFSFactory>&& romfs_factory, std::string homebrew_initial_cwd) {
std::scoped_lock lk{registration_lock};
registrations.emplace(process_id, Registration{
.program_id = program_id,
.romfs_factory = std::move(romfs_factory),
.save_data_factory = CreateSaveDataFactory(program_id),
});
registrations.insert_or_assign(process_id,
Registration{
.program_id = program_id,
.romfs_factory = std::move(romfs_factory),
.save_data_factory = CreateSaveDataFactory(program_id),
.homebrew_initial_cwd = std::move(homebrew_initial_cwd),
});
LOG_DEBUG(Service_FS, "Registered for process {}", process_id);
return ResultSuccess;
@@ -348,7 +351,8 @@ Result FileSystemController::RegisterProcess(
Result FileSystemController::OpenProcess(
ProgramId* out_program_id, std::shared_ptr<SaveDataController>* out_save_data_controller,
std::shared_ptr<RomFsController>* out_romfs_controller, ProcessId process_id) {
std::shared_ptr<RomFsController>* out_romfs_controller, ProcessId process_id,
std::string* out_homebrew_initial_cwd) {
std::scoped_lock lk{registration_lock};
const auto it = registrations.find(process_id);
@@ -361,6 +365,9 @@ Result FileSystemController::OpenProcess(
std::make_shared<SaveDataController>(system, it->second.save_data_factory);
*out_romfs_controller =
std::make_shared<RomFsController>(it->second.romfs_factory, it->second.program_id);
if (out_homebrew_initial_cwd != nullptr) {
*out_homebrew_initial_cwd = it->second.homebrew_initial_cwd;
}
return ResultSuccess;
}
+7 -4
View File
@@ -8,6 +8,7 @@
#include <memory>
#include <mutex>
#include <string>
#include "common/common_types.h"
#include "core/file_sys/fs_directory.h"
#include "core/file_sys/fs_filesystem.h"
@@ -71,11 +72,12 @@ public:
~FileSystemController();
Result RegisterProcess(ProcessId process_id, ProgramId program_id,
std::shared_ptr<FileSys::RomFSFactory>&& factory);
std::shared_ptr<FileSys::RomFSFactory>&& factory,
std::string homebrew_initial_cwd = {});
Result OpenProcess(ProgramId* out_program_id,
std::shared_ptr<SaveDataController>* out_save_data_controller,
std::shared_ptr<RomFsController>* out_romfs_controller,
ProcessId process_id);
std::shared_ptr<SaveDataController>* out_save_data_controller,
std::shared_ptr<RomFsController>* out_romfs_controller,
ProcessId process_id, std::string* out_homebrew_initial_cwd = nullptr);
void SetPackedUpdate(ProcessId process_id, FileSys::VirtualFile update_raw);
std::shared_ptr<SaveDataController> OpenSaveDataController();
@@ -136,6 +138,7 @@ private:
ProgramId program_id;
std::shared_ptr<FileSys::RomFSFactory> romfs_factory;
std::shared_ptr<FileSys::SaveDataFactory> save_data_factory;
std::string homebrew_initial_cwd;
};
std::mutex registration_lock;
@@ -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 2023 yuzu Emulator Project
@@ -30,7 +30,7 @@ Result IFile::Read(
FileSys::ReadOption option, Out<s64> out_size, s64 offset,
const OutBuffer<BufferAttr_HipcMapAlias | BufferAttr_HipcMapTransferAllowsNonSecure> out_buffer,
s64 size) {
LOG_DEBUG(Service_FS, "called, option={}, offset={:#X}, length={}", option.value, offset,
LOG_DEBUG(Service_FS, "called, option={}, offset={:#x}, length={}", option.value, offset,
size);
// Read the data from the Storage backend
@@ -41,7 +41,7 @@ Result IFile::Read(
Result IFile::Write(
const InBuffer<BufferAttr_HipcMapAlias | BufferAttr_HipcMapTransferAllowsNonSecure> buffer,
FileSys::WriteOption option, s64 offset, s64 size) {
LOG_DEBUG(Service_FS, "called, option={}, offset={:#X}, length={}", option.value, offset,
LOG_DEBUG(Service_FS, "called, option={}, offset={:#x}, length={}", option.value, offset,
size);
R_RETURN(backend->Write(offset, buffer.data(), size, option));
@@ -4,6 +4,9 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <string_view>
#include "common/fs/path_util.h"
#include "common/string_util.h"
#include "core/file_sys/fssrv/fssrv_sf_path.h"
#include "core/hle/service/cmif_serialization.h"
@@ -13,10 +16,29 @@
namespace Service::FileSystem {
IFileSystem::IFileSystem(Core::System& system_, FileSys::VirtualDir dir_, SizeGetter size_getter_)
: ServiceFramework{system_, "IFileSystem"}, backend{std::make_unique<FileSys::Fsa::IFileSystem>(
dir_)},
size_getter{std::move(size_getter_)} {
static std::string ResolveHomebrewCwdRootAlias(std::string path,
std::string_view homebrew_initial_cwd) {
if (homebrew_initial_cwd.empty()) {
return path;
}
const std::string normalized_path = Common::FS::SanitizePath(path);
if (normalized_path.empty() || normalized_path == "/" ||
normalized_path != homebrew_initial_cwd ||
path.size() != normalized_path.size() + 2 ||
path.substr(0, normalized_path.size()) != normalized_path ||
path.substr(normalized_path.size()) != "//") {
return path;
}
return "/";
}
IFileSystem::IFileSystem(Core::System& system_, FileSys::VirtualDir dir_, SizeGetter size_getter_,
std::string homebrew_initial_cwd_)
: ServiceFramework{system_, "IFileSystem"},
backend{std::make_unique<FileSys::Fsa::IFileSystem>(dir_)},
size_getter{std::move(size_getter_)}, homebrew_initial_cwd{std::move(homebrew_initial_cwd_)} {
static const FunctionInfo functions[] = {
{0, D<&IFileSystem::CreateFile>, "CreateFile"},
{1, D<&IFileSystem::DeleteFile>, "DeleteFile"},
@@ -41,9 +63,10 @@ IFileSystem::IFileSystem(Core::System& system_, FileSys::VirtualDir dir_, SizeGe
Result IFileSystem::CreateFile(const InLargeData<FileSys::Sf::Path, BufferAttr_HipcPointer> path,
s32 option, s64 size) {
LOG_DEBUG(Service_FS, "called. file={}, option={:#X}, size=0x{:08X}", path->str, option, size);
LOG_DEBUG(Service_FS, "called. file={}, option={:#x}, size={:#08x}", path->str, option, size);
R_RETURN(backend->CreateFile(FileSys::Path(path->str), size));
const auto fs_path = ResolveHomebrewCwdRootAlias(path->str, homebrew_initial_cwd);
R_RETURN(backend->CreateFile(FileSys::Path(fs_path.c_str()), size));
}
Result IFileSystem::DeleteFile(const InLargeData<FileSys::Sf::Path, BufferAttr_HipcPointer> path) {
@@ -94,7 +117,8 @@ Result IFileSystem::OpenFile(OutInterface<IFile> out_interface,
LOG_DEBUG(Service_FS, "called. file={}, mode={}", path->str, mode);
FileSys::VirtualFile vfs_file{};
R_TRY(backend->OpenFile(&vfs_file, FileSys::Path(path->str),
const auto fs_path = ResolveHomebrewCwdRootAlias(path->str, homebrew_initial_cwd);
R_TRY(backend->OpenFile(&vfs_file, FileSys::Path(fs_path.c_str()),
static_cast<FileSys::OpenMode>(mode)));
*out_interface = std::make_shared<IFile>(system, vfs_file);
@@ -107,7 +131,8 @@ Result IFileSystem::OpenDirectory(OutInterface<IDirectory> out_interface,
LOG_DEBUG(Service_FS, "called. directory={}, mode={}", path->str, mode);
FileSys::VirtualDir vfs_dir{};
R_TRY(backend->OpenDirectory(&vfs_dir, FileSys::Path(path->str),
const auto fs_path = ResolveHomebrewCwdRootAlias(path->str, homebrew_initial_cwd);
R_TRY(backend->OpenDirectory(&vfs_dir, FileSys::Path(fs_path.c_str()),
static_cast<FileSys::OpenDirectoryMode>(mode)));
*out_interface = std::make_shared<IDirectory>(system, vfs_dir,
@@ -120,7 +145,8 @@ Result IFileSystem::GetEntryType(
LOG_DEBUG(Service_FS, "called. file={}", path->str);
FileSys::DirectoryEntryType vfs_entry_type{};
R_TRY(backend->GetEntryType(&vfs_entry_type, FileSys::Path(path->str)));
const auto fs_path = ResolveHomebrewCwdRootAlias(path->str, homebrew_initial_cwd);
R_TRY(backend->GetEntryType(&vfs_entry_type, FileSys::Path(fs_path.c_str())));
*out_type = static_cast<u32>(vfs_entry_type);
R_SUCCEED();
@@ -1,8 +1,13 @@
// 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
#pragma once
#include <string>
#include "common/common_funcs.h"
#include "core/file_sys/fs_filesystem.h"
#include "core/file_sys/fsa/fs_i_filesystem.h"
@@ -23,7 +28,8 @@ class IDirectory;
class IFileSystem final : public ServiceFramework<IFileSystem> {
public:
explicit IFileSystem(Core::System& system_, FileSys::VirtualDir dir_, SizeGetter size_getter_);
explicit IFileSystem(Core::System& system_, FileSys::VirtualDir dir_, SizeGetter size_getter_,
std::string homebrew_initial_cwd_ = {});
Result CreateFile(const InLargeData<FileSys::Sf::Path, BufferAttr_HipcPointer> path, s32 option,
s64 size);
@@ -55,6 +61,7 @@ public:
private:
std::unique_ptr<FileSys::Fsa::IFileSystem> backend;
SizeGetter size_getter;
std::string homebrew_initial_cwd;
};
} // namespace Service::FileSystem
@@ -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 2023 yuzu Emulator Project
@@ -26,7 +26,7 @@ IStorage::IStorage(Core::System& system_, FileSys::VirtualFile backend_)
Result IStorage::Read(
OutBuffer<BufferAttr_HipcMapAlias | BufferAttr_HipcMapTransferAllowsNonSecure> out_bytes,
s64 offset, s64 length) {
LOG_DEBUG(Service_FS, "called, offset={:#X}, length={}", offset, length);
LOG_DEBUG(Service_FS, "called, offset={:#x}, length={}", offset, length);
R_UNLESS(length >= 0, FileSys::ResultInvalidSize);
R_UNLESS(offset >= 0, FileSys::ResultInvalidOffset);
@@ -190,10 +190,11 @@ FSP_SRV::~FSP_SRV() = default;
Result FSP_SRV::SetCurrentProcess(ClientProcessId pid) {
current_process_id = *pid;
LOG_DEBUG(Service_FS, "called. current_process_id=0x{:016X}", current_process_id);
LOG_DEBUG(Service_FS, "called. current_process_id={:#016x}", current_process_id);
R_RETURN(
fsc.OpenProcess(&program_id, &save_data_controller, &romfs_controller, current_process_id));
homebrew_initial_cwd.clear();
R_RETURN(fsc.OpenProcess(&program_id, &save_data_controller, &romfs_controller,
current_process_id, &homebrew_initial_cwd));
}
Result FSP_SRV::OpenFileSystemWithPatch(OutInterface<IFileSystem> out_interface,
@@ -224,7 +225,8 @@ Result FSP_SRV::OpenSdCardFileSystem(OutInterface<IFileSystem> out_interface) {
fsc.OpenSDMC(&sdmc_dir);
*out_interface = std::make_shared<IFileSystem>(
system, sdmc_dir, SizeGetter::FromStorageId(fsc, FileSys::StorageId::SdCard));
system, sdmc_dir, SizeGetter::FromStorageId(fsc, FileSys::StorageId::SdCard),
homebrew_initial_cwd);
R_SUCCEED();
}
@@ -7,6 +7,7 @@
#pragma once
#include <memory>
#include <string>
#include "core/file_sys/fs_save_data_types.h"
#include "core/hle/service/cmif_types.h"
#include "core/hle/service/filesystem/fsp/fsp_types.h"
@@ -123,6 +124,7 @@ private:
u32 access_log_program_index = 0;
AccessLogMode access_log_mode = AccessLogMode::None;
u64 program_id = 0;
std::string homebrew_initial_cwd;
std::shared_ptr<SaveDataController> save_data_controller;
std::shared_ptr<RomFsController> romfs_controller;
};
+1 -1
View File
@@ -30,7 +30,7 @@ Result IAppletResource::GetSharedMemoryHandle(
OutCopyHandle<Kernel::KSharedMemory> out_shared_memory_handle) {
const auto result = resource_manager->GetSharedMemoryHandle(out_shared_memory_handle, aruid);
LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}, result={:#X}", aruid, result.raw);
LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}, result={:#x}", aruid, result.raw);
R_RETURN(result);
}
+2 -2
View File
@@ -251,7 +251,7 @@ Result IHidServer::CreateAppletResource(OutInterface<IAppletResource> out_applet
ClientAppletResourceUserId aruid) {
const auto result = GetResourceManager()->CreateAppletResource(aruid.pid);
LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}, result={:#X}", aruid.pid,
LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}, result={:#x}", aruid.pid,
result.raw);
*out_applet_resource = std::make_shared<IAppletResource>(system, resource_manager, aruid.pid);
@@ -1150,7 +1150,7 @@ Result IHidServer::InitializeSevenSixAxisSensor(ClientAppletResourceUserId aruid
InCopyHandle<Kernel::KTransferMemory> t_mem_1,
InCopyHandle<Kernel::KTransferMemory> t_mem_2) {
LOG_WARNING(Service_HID,
"called, t_mem_1_size=0x{:08X}, t_mem_2_size=0x{:08X}, "
"called, t_mem_1_size={:#08x}, t_mem_2_size={:#08x}, "
"applet_resource_user_id={}",
t_mem_1_size, t_mem_2_size, aruid.pid);
+1 -1
View File
@@ -113,7 +113,7 @@ Result SessionRequestManager::HandleDomainSyncRequest(Kernel::KServerSession* se
}
case IPC::DomainMessageHeader::CommandType::CloseVirtualHandle: {
LOG_DEBUG(IPC, "CloseVirtualHandle, object_id=0x{:08X}", object_id);
LOG_DEBUG(IPC, "CloseVirtualHandle, object_id={:#08x}", object_id);
this->CloseDomainHandler(object_id - 1);
+3 -3
View File
@@ -142,7 +142,7 @@ public:
if (boost::icl::contains(mapped_ranges, vaddr)) {
memory.ReadBlock(vaddr, &ret, sizeof(T));
} else if (vaddr + sizeof(T) > local_memory.size()) {
LOG_CRITICAL(Service_JIT, "plugin: unmapped read @ 0x{:016x}", vaddr);
LOG_CRITICAL(Service_JIT, "plugin: unmapped read @ {:#016x}", vaddr);
} else {
std::memcpy(&ret, local_memory.data() + vaddr, sizeof(T));
}
@@ -154,7 +154,7 @@ public:
if (boost::icl::contains(mapped_ranges, vaddr)) {
memory.WriteBlock(vaddr, &value, sizeof(T));
} else if (vaddr + sizeof(T) > local_memory.size()) {
LOG_CRITICAL(Service_JIT, "plugin: unmapped write @ 0x{:016x}", vaddr);
LOG_CRITICAL(Service_JIT, "plugin: unmapped write @ {:#016x}", vaddr);
} else {
std::memcpy(local_memory.data() + vaddr, &value, sizeof(T));
}
@@ -416,7 +416,7 @@ void DynarmicCallbacks64::CallSVC(u32 swi) {
LOG_CRITICAL(Service_JIT, "plugin panicked!");
parent.jit->HaltExecution();
} else {
LOG_CRITICAL(Service_JIT, "plugin issued syscall at unknown address 0x{:x}", pc);
LOG_CRITICAL(Service_JIT, "plugin issued syscall at unknown address {:#x}", pc);
parent.jit->HaltExecution();
}
}
@@ -77,7 +77,7 @@ Result DatabaseManager::Initialize(DatabaseSessionMetadata& metadata, bool& is_d
const auto result = database.CheckIntegrity();
if (result.IsError()) {
LOG_ERROR(Service_Mii, "Mii database is corrupted 0x{:0x}", result.raw);
LOG_ERROR(Service_Mii, "Mii database is corrupted {:#0x}", result.raw);
database.CleanDatabase();
return ResultSuccess;
}
+91 -1
View File
@@ -1,10 +1,15 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: 2022 yuzu Emulator Project
// SPDX-FileCopyrightText: 2022 Skyline Team and Contributors
// SPDX-License-Identifier: GPL-3.0-or-later
#include <algorithm>
#include <atomic>
#include <deque>
#include <mutex>
#include <vector>
#include "core/hle/kernel/k_process.h"
#include "core/hle/service/nvdrv/core/container.h"
@@ -40,6 +45,16 @@ Container::Container(Tegra::Host1x::Host1x& host1x_) {
Container::~Container() = default;
static bool IsSameProcess(Kernel::KProcess* lhs, Kernel::KProcess* rhs) {
if (lhs == rhs) {
return true;
}
if (lhs == nullptr || rhs == nullptr) {
return false;
}
return lhs->GetProcessId() == rhs->GetProcessId();
}
SessionId Container::OpenSession(Kernel::KProcess* process) {
using namespace Common::Literals;
@@ -48,7 +63,7 @@ SessionId Container::OpenSession(Kernel::KProcess* process) {
if (!session.is_active) {
continue;
}
if (session.process == process) {
if (IsSameProcess(session.process, process)) {
session.ref_count++;
return session.id;
}
@@ -116,7 +131,15 @@ SessionId Container::OpenSession(Kernel::KProcess* process) {
void Container::CloseSession(SessionId session_id) {
std::scoped_lock lk(impl->session_guard);
if (session_id.id >= impl->sessions.size()) {
return;
}
auto& session = impl->sessions[session_id.id];
if (!session.is_active || session.ref_count <= 0) {
return;
}
if (--session.ref_count > 0) {
return;
}
@@ -134,6 +157,73 @@ void Container::CloseSession(SessionId session_id) {
impl->id_pool.emplace_front(session_id.id);
}
size_t Container::CloseSessions(std::span<const SessionId> session_ids) {
std::vector<SessionId> valid_session_ids;
valid_session_ids.reserve(session_ids.size());
{
std::scoped_lock lk(impl->session_guard);
for (const auto session_id : session_ids) {
if (session_id.id >= impl->sessions.size()) {
continue;
}
auto& session = impl->sessions[session_id.id];
if (!session.is_active) {
continue;
}
const auto duplicate = std::ranges::any_of(
valid_session_ids, [session_id](const auto candidate) {
return candidate.id == session_id.id;
});
if (duplicate) {
continue;
}
session.ref_count = 1;
valid_session_ids.push_back(session_id);
}
}
for (const auto session_id : valid_session_ids) {
CloseSession(session_id);
}
return valid_session_ids.size();
}
std::vector<SessionId> Container::GetSessionIdsForProcess(Kernel::KProcess* process) {
std::vector<SessionId> session_ids;
std::scoped_lock lk(impl->session_guard);
for (const auto& session : impl->sessions) {
if (!session.is_active || !IsSameProcess(session.process, process)) {
continue;
}
session_ids.push_back(session.id);
}
return session_ids;
}
std::vector<SessionId> Container::GetActiveSessionIds() const {
std::vector<SessionId> session_ids;
std::scoped_lock lk(impl->session_guard);
for (const auto& session : impl->sessions) {
if (session.is_active) {
session_ids.push_back(session.id);
}
}
return session_ids;
}
bool Container::IsSessionActive(SessionId session_id) const {
std::scoped_lock lk(impl->session_guard);
return session_id.id < impl->sessions.size() && impl->sessions[session_id.id].is_active;
}
Session* Container::GetSession(SessionId session_id) {
std::atomic_thread_fence(std::memory_order_acquire);
return &impl->sessions[session_id.id];
@@ -9,7 +9,10 @@
#include <deque>
#include <memory>
#include <span>
#include <cstddef>
#include <ankerl/unordered_dense.h>
#include <vector>
#include "core/device_memory_manager.h"
#include "core/hle/service/nvdrv/nvdata.h"
@@ -59,6 +62,10 @@ public:
SessionId OpenSession(Kernel::KProcess* process);
void CloseSession(SessionId id);
size_t CloseSessions(std::span<const SessionId> session_ids);
std::vector<SessionId> GetSessionIdsForProcess(Kernel::KProcess* process);
std::vector<SessionId> GetActiveSessionIds() const;
bool IsSessionActive(SessionId id) const;
Session* GetSession(SessionId id);
+56 -8
View File
@@ -6,10 +6,12 @@
// SPDX-License-Identifier: GPL-3.0-or-later
#include <functional>
#include <vector>
#include "common/alignment.h"
#include "common/assert.h"
#include "common/logging.h"
#include "core/hle/kernel/k_process.h"
#include "core/hle/service/nvdrv/core/container.h"
#include "core/hle/service/nvdrv/core/heap_mapper.h"
#include "core/hle/service/nvdrv/core/nvmap.h"
@@ -326,19 +328,65 @@ std::optional<NvMap::FreeInfo> NvMap::FreeHandle(Handle::Id handle, bool interna
}
void NvMap::UnmapAllHandles(NvCore::SessionId session_id) {
auto handles_copy = [&] {
auto* session = core.GetSession(session_id);
auto* process = session != nullptr ? session->process : nullptr;
auto handle_ids = [&] {
std::scoped_lock lk{handles_lock};
return handles;
std::vector<Handle::Id> ids;
ids.reserve(handles.size());
for (const auto& entry : handles) {
ids.push_back(entry.first);
}
return ids;
}();
for (auto& [id, handle] : handles_copy) {
{
std::scoped_lock lk{handle->mutex};
if (handle->session_id.id != session_id.id || handle->dupes <= 0) {
continue;
for (const auto id : handle_ids) {
bool unlocked_pages = false;
while (true) {
bool last_user_reference = false;
VAddr address = 0;
size_t size = 0;
{
const auto handle = GetHandle(id);
if (!handle) {
break;
}
std::scoped_lock lk{handle->mutex};
if (handle->session_id.id != session_id.id || handle->dupes <= 0) {
break;
}
last_user_reference = handle->dupes == 1;
address = handle->address;
size = handle->size;
}
const auto free_info = FreeHandle(id, false);
if (!free_info) {
break;
}
if (!unlocked_pages && process != nullptr && address != 0 && size != 0 &&
(free_info->can_unlock || last_user_reference)) {
const auto unlock_result =
process->GetPageTable().UnlockForDeviceAddressSpace(address, size);
if (unlock_result.IsError()) {
LOG_WARNING(Service_NVDRV,
"NextLoad: nvmap session cleanup unlock failed, "
"handle={}, session={}, address=0x{:016X}, size=0x{:X}, "
"result={:#X}",
id, session_id.id, address, size, unlock_result.raw);
}
unlocked_pages = true;
}
if (last_user_reference) {
break;
}
}
FreeHandle(id, false);
}
}
@@ -94,7 +94,7 @@ void nvhost_as_gpu::OnOpen(NvCore::SessionId session_id, DeviceFD fd) {}
void nvhost_as_gpu::OnClose(DeviceFD fd) {}
NvResult nvhost_as_gpu::AllocAsEx(IoctlAllocAsEx& params) {
LOG_DEBUG(Service_NVDRV, "called, big_page_size={:#X}", params.big_page_size);
LOG_DEBUG(Service_NVDRV, "called, big_page_size={:#x}", params.big_page_size);
std::scoped_lock lock(mutex);
@@ -105,12 +105,12 @@ NvResult nvhost_as_gpu::AllocAsEx(IoctlAllocAsEx& params) {
if (params.big_page_size) {
if (!std::has_single_bit(params.big_page_size)) {
LOG_ERROR(Service_NVDRV, "Non power-of-2 big page size: {:#X}!", params.big_page_size);
LOG_ERROR(Service_NVDRV, "Non power-of-2 big page size: {:#x}!", params.big_page_size);
return NvResult::BadValue;
}
if ((params.big_page_size & VM::SUPPORTED_BIG_PAGE_SIZES) == 0) {
LOG_ERROR(Service_NVDRV, "Unsupported big page size: {:#X}!", params.big_page_size);
LOG_ERROR(Service_NVDRV, "Unsupported big page size: {:#x}!", params.big_page_size);
return NvResult::BadValue;
}
@@ -252,7 +252,7 @@ NvResult nvhost_as_gpu::FreeSpace(IoctlFreeSpace& params) {
}
NvResult nvhost_as_gpu::Remap(std::span<IoctlRemapEntry> entries) {
LOG_DEBUG(Service_NVDRV, "called, num_entries={:#X}", entries.size());
LOG_DEBUG(Service_NVDRV, "called, num_entries={:#x}", entries.size());
if (!vm.initialised) {
return NvResult::BadValue;
@@ -300,7 +300,7 @@ NvResult nvhost_as_gpu::Remap(std::span<IoctlRemapEntry> entries) {
NvResult nvhost_as_gpu::MapBufferEx(IoctlMapBufferEx& params) {
LOG_DEBUG(Service_NVDRV,
"called, flags={:X}, nvmap_handle={:X}, buffer_offset={}, mapping_size={}"
", offset={:#X}",
", offset={:#x}",
params.flags, params.handle, params.buffer_offset, params.mapping_size,
params.offset);
@@ -315,7 +315,7 @@ NvResult nvhost_as_gpu::MapBufferEx(IoctlMapBufferEx& params) {
if (auto const it = mapping_map.find(params.offset); it != mapping_map.end()) {
auto const mapping = it->second;
if (mapping.size < params.mapping_size) {
LOG_WARNING(Service_NVDRV, "Cannot remap a partially mapped GPU address space region: {:#X}", params.offset);
LOG_WARNING(Service_NVDRV, "Cannot remap a partially mapped GPU address space region: {:#x}", params.offset);
return NvResult::BadValue;
}
u64 gpu_address = u64(params.offset + params.buffer_offset);
@@ -323,7 +323,7 @@ NvResult nvhost_as_gpu::MapBufferEx(IoctlMapBufferEx& params) {
gmmu->Map(gpu_address, device_address, params.mapping_size, Tegra::PTEKind(params.kind), mapping.big_page);
return NvResult::Success;
} else {
LOG_WARNING(Service_NVDRV, "Cannot remap an unmapped GPU address space region: {:#X}", params.offset);
LOG_WARNING(Service_NVDRV, "Cannot remap an unmapped GPU address space region: {:#x}", params.offset);
return NvResult::BadValue;
}
}
@@ -383,7 +383,7 @@ NvResult nvhost_as_gpu::MapBufferEx(IoctlMapBufferEx& params) {
NvResult nvhost_as_gpu::UnmapBuffer(IoctlUnmapBuffer& params) {
std::scoped_lock lock(mutex);
if (auto const offset_it = map_buffer_offsets.find(params.offset); offset_it != map_buffer_offsets.end()) {
LOG_DEBUG(Service_NVDRV, "called, offset={:#X}", params.offset);
LOG_DEBUG(Service_NVDRV, "called, offset={:#x}", params.offset);
if (!vm.initialised) {
return NvResult::BadValue;
}
@@ -210,7 +210,7 @@ NvResult nvhost_ctrl_gpu::GetCharacteristics3(
}
NvResult nvhost_ctrl_gpu::GetTPCMasks1(IoctlGpuGetTpcMasksArgs& params) {
LOG_DEBUG(Service_NVDRV, "called, mask_buffer_size={:#X}", params.mask_buffer_size);
LOG_DEBUG(Service_NVDRV, "called, mask_buffer_size={:#x}", params.mask_buffer_size);
if (params.mask_buffer_size != 0) {
params.tcp_mask = 3;
}
@@ -218,7 +218,7 @@ NvResult nvhost_ctrl_gpu::GetTPCMasks1(IoctlGpuGetTpcMasksArgs& params) {
}
NvResult nvhost_ctrl_gpu::GetTPCMasks3(IoctlGpuGetTpcMasksArgs& params, std::span<u32> tpc_mask) {
LOG_DEBUG(Service_NVDRV, "called, mask_buffer_size={:#X}", params.mask_buffer_size);
LOG_DEBUG(Service_NVDRV, "called, mask_buffer_size={:#x}", params.mask_buffer_size);
if (params.mask_buffer_size != 0) {
params.tcp_mask = 3;
}
@@ -265,7 +265,7 @@ NvResult nvhost_ctrl_gpu::ZCullGetInfo(IoctlNvgpuGpuZcullGetInfoArgs& params) {
NvResult nvhost_ctrl_gpu::ZBCSetTable(IoctlZbcSetTable& params) {
if (params.type > supported_types) {
LOG_ERROR(Service_NVDRV, "ZBCSetTable: invalid type {:#X}", params.type);
LOG_ERROR(Service_NVDRV, "ZBCSetTable: invalid type {:#x}", params.type);
return NvResult::BadParameter;
}
@@ -288,11 +288,11 @@ NvResult nvhost_ctrl_gpu::ZBCSetTable(IoctlZbcSetTable& params) {
if (color_it != zbc_colors.end()) {
++color_it->ref_cnt;
LOG_DEBUG(Service_NVDRV, "ZBCSetTable: reused color entry fmt={:#X}, ref_cnt={:#X}",
LOG_DEBUG(Service_NVDRV, "ZBCSetTable: reused color entry fmt={:#x}, ref_cnt={:#x}",
params.format, color_it->ref_cnt);
} else {
zbc_colors.push_back(color_entry);
LOG_DEBUG(Service_NVDRV, "ZBCSetTable: added color entry fmt={:#X}, index={:#X}",
LOG_DEBUG(Service_NVDRV, "ZBCSetTable: added color entry fmt={:#x}, index={:#x}",
params.format, zbc_colors.size() - 1);
}
break;
@@ -308,11 +308,11 @@ NvResult nvhost_ctrl_gpu::ZBCSetTable(IoctlZbcSetTable& params) {
if (depth_it != zbc_depths.end()) {
++depth_it->ref_cnt;
LOG_DEBUG(Service_NVDRV, "ZBCSetTable: reused depth entry fmt={:#X}, ref_cnt={:#X}",
LOG_DEBUG(Service_NVDRV, "ZBCSetTable: reused depth entry fmt={:#x}, ref_cnt={:#x}",
depth_entry.format, depth_it->ref_cnt);
} else {
zbc_depths.push_back(depth_entry);
LOG_DEBUG(Service_NVDRV, "ZBCSetTable: added depth entry fmt={:#X}, index={:#X}",
LOG_DEBUG(Service_NVDRV, "ZBCSetTable: added depth entry fmt={:#x}, index={:#x}",
depth_entry.format, zbc_depths.size() - 1);
}
}
@@ -323,7 +323,7 @@ NvResult nvhost_ctrl_gpu::ZBCSetTable(IoctlZbcSetTable& params) {
NvResult nvhost_ctrl_gpu::ZBCQueryTable(IoctlZbcQueryTable& params) {
if (params.type > supported_types) {
LOG_ERROR(Service_NVDRV, "ZBCQueryTable: invalid type {:#X}", params.type);
LOG_ERROR(Service_NVDRV, "ZBCQueryTable: invalid type {:#x}", params.type);
return NvResult::BadParameter;
}
@@ -332,7 +332,7 @@ NvResult nvhost_ctrl_gpu::ZBCQueryTable(IoctlZbcQueryTable& params) {
switch (static_cast<ZBCTypes>(params.type)) {
case ZBCTypes::color: {
if (params.index_size >= zbc_colors.size()) {
LOG_ERROR(Service_NVDRV, "ZBCQueryTable: invalid color index {:#X}", params.index_size);
LOG_ERROR(Service_NVDRV, "ZBCQueryTable: invalid color index {:#x}", params.index_size);
return NvResult::BadParameter;
}
@@ -347,7 +347,7 @@ NvResult nvhost_ctrl_gpu::ZBCQueryTable(IoctlZbcQueryTable& params) {
}
case ZBCTypes::depth: {
if (params.index_size >= zbc_depths.size()) {
LOG_ERROR(Service_NVDRV, "ZBCQueryTable: invalid depth index {:#X}", params.index_size);
LOG_ERROR(Service_NVDRV, "ZBCQueryTable: invalid depth index {:#x}", params.index_size);
return NvResult::BadParameter;
}
@@ -365,7 +365,7 @@ NvResult nvhost_ctrl_gpu::ZBCQueryTable(IoctlZbcQueryTable& params) {
}
NvResult nvhost_ctrl_gpu::FlushL2(IoctlFlushL2& params) {
LOG_DEBUG(Service_NVDRV, "called {:#X}", params.flush);
LOG_DEBUG(Service_NVDRV, "called {:#x}", params.flush);
// if ((params.flush & 0x01) != 0) //l2 flush
// /* we dont emulate l2 */;
// if ((params.flush & 0x04) != 0) //fb flush
@@ -259,7 +259,7 @@ s32_le nvhost_gpu::GetObjectContextClassNumberIndex(CtxClasses class_number) {
}
NvResult nvhost_gpu::AllocateObjectContext(IoctlAllocObjCtx& params) {
LOG_DEBUG(Service_NVDRV, "called, class_num={:#X}, flags={:#X}, obj_id={:#X}", params.class_num,
LOG_DEBUG(Service_NVDRV, "called, class_num={:#x}, flags={:#x}, obj_id={:#x}", params.class_num,
params.flags, params.obj_id);
// Do not require channel initialization here: some clients allocate contexts before binding.
@@ -271,7 +271,7 @@ NvResult nvhost_gpu::AllocateObjectContext(IoctlAllocObjCtx& params) {
std::scoped_lock lk(channel_mutex);
if (params.flags) {
LOG_WARNING(Service_NVDRV, "non-zero flags={:#X} for class={:#X}", params.flags,
LOG_WARNING(Service_NVDRV, "non-zero flags={:#x} for class={:#x}", params.flags,
params.class_num);
constexpr u32 allowed_mask{};
@@ -281,13 +281,13 @@ NvResult nvhost_gpu::AllocateObjectContext(IoctlAllocObjCtx& params) {
s32_le ctx_class_number_index =
GetObjectContextClassNumberIndex(static_cast<CtxClasses>(params.class_num));
if (ctx_class_number_index < 0) {
LOG_ERROR(Service_NVDRV, "Invalid class number for object context: {:#X}",
LOG_ERROR(Service_NVDRV, "Invalid class number for object context: {:#x}",
params.class_num);
return NvResult::BadParameter;
}
if (ctxObjs[ctx_class_number_index].has_value()) {
LOG_WARNING(Service_NVDRV, "Object context for class {:#X} already allocated on this channel",
LOG_WARNING(Service_NVDRV, "Object context for class {:#x} already allocated on this channel",
params.class_num);
return NvResult::AlreadyAllocated;
}
@@ -420,20 +420,20 @@ NvResult nvhost_gpu::SubmitGPFIFOBase2(IoctlSubmitGpfifo& params,
}
NvResult nvhost_gpu::GetWaitbase(IoctlGetWaitbase& params) {
LOG_INFO(Service_NVDRV, "called, unknown={:#X}", params.unknown);
LOG_INFO(Service_NVDRV, "called, unknown={:#x}", params.unknown);
params.value = 0; // Seems to be hard coded at 0
return NvResult::Success;
}
NvResult nvhost_gpu::ChannelSetTimeout(IoctlChannelSetTimeout& params) {
LOG_INFO(Service_NVDRV, "called, timeout={:#X}", params.timeout);
LOG_INFO(Service_NVDRV, "called, timeout={:#x}", params.timeout);
return NvResult::Success;
}
NvResult nvhost_gpu::ChannelSetTimeslice(IoctlSetTimeslice& params) {
LOG_INFO(Service_NVDRV, "called, timeslice={:#X}", params.timeslice);
LOG_INFO(Service_NVDRV, "called, timeslice={:#x}", params.timeslice);
if (params.timeslice < 1000 || params.timeslice > 5000) {
return NvResult::BadParameter;
+2 -2
View File
@@ -81,7 +81,7 @@ void nvmap::OnClose(DeviceFD fd) {
}
NvResult nvmap::IocCreate(IocCreateParams& params) {
LOG_DEBUG(Service_NVDRV, "called, size=0x{:08X}", params.size);
LOG_DEBUG(Service_NVDRV, "called, size={:#08x}", params.size);
std::shared_ptr<NvCore::NvMap::Handle> handle_description{};
auto result =
@@ -92,7 +92,7 @@ NvResult nvmap::IocCreate(IocCreateParams& params) {
}
handle_description->orig_size = params.size; // Orig size is the unaligned size
params.handle = handle_description->id;
LOG_DEBUG(Service_NVDRV, "handle: {}, size: {:#X}", handle_description->id, params.size);
LOG_DEBUG(Service_NVDRV, "handle: {}, size: {:#x}", handle_description->id, params.size);
return NvResult::Success;
}
+104 -1
View File
@@ -1,12 +1,17 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: 2021 yuzu Emulator Project
// SPDX-FileCopyrightText: 2021 Skyline Team and Contributors
// SPDX-License-Identifier: GPL-3.0-or-later
#include <algorithm>
#include <utility>
#include <vector>
#include <fmt/ranges.h>
#include "core/core.h"
#include "core/hle/kernel/k_event.h"
#include "core/hle/kernel/k_process.h"
#include "core/hle/service/ipc_helpers.h"
#include "core/hle/service/nvdrv/core/container.h"
#include "core/hle/service/nvdrv/devices/nvdevice.h"
@@ -133,6 +138,9 @@ DeviceFD Module::Open(const std::string& device_name, NvCore::SessionId session_
auto device = builder(fd)->second;
device->OnOpen(session_id, fd);
if (container.IsSessionActive(session_id)) {
open_file_sessions.emplace(fd, session_id);
}
return fd;
}
@@ -204,6 +212,7 @@ NvResult Module::Close(DeviceFD fd) {
itr->second->OnClose(fd);
open_files.erase(itr);
open_file_sessions.erase(fd);
return NvResult::Success;
}
@@ -228,4 +237,98 @@ NvResult Module::QueryEvent(DeviceFD fd, u32 event_id, Kernel::KEvent*& event) {
return NvResult::Success;
}
static bool ContainsSession(std::span<const NvCore::SessionId> session_ids,
NvCore::SessionId session_id) {
return std::ranges::any_of(session_ids, [session_id](const auto candidate) {
return candidate.id == session_id.id;
});
}
static void AppendUniqueSession(std::vector<NvCore::SessionId>& session_ids,
NvCore::SessionId session_id) {
if (!ContainsSession(session_ids, session_id)) {
session_ids.push_back(session_id);
}
}
size_t Module::CloseFilesForSessions(std::span<const NvCore::SessionId> session_ids) {
std::vector<DeviceFD> fds;
fds.reserve(open_file_sessions.size());
for (const auto& [fd, session_id] : open_file_sessions) {
if (ContainsSession(session_ids, session_id)) {
fds.push_back(fd);
}
}
for (const auto fd : fds) {
Close(fd);
}
return fds.size();
}
void Module::CloseSession(NvCore::SessionId session_id) {
container.CloseSession(session_id);
}
void Module::TrackSessionAruid(NvCore::SessionId session_id, u64 aruid) {
const bool active = container.IsSessionActive(session_id);
if (active) {
session_aruids[session_id.id] = aruid;
}
}
std::vector<NvCore::SessionId> Module::GetSessionIdsForAruid(u64 aruid) const {
std::vector<NvCore::SessionId> session_ids;
for (const auto& [session_id, session_aruid] : session_aruids) {
if (session_aruid == aruid) {
session_ids.push_back(NvCore::SessionId{session_id});
}
}
return session_ids;
}
size_t Module::ResetForProcess(Kernel::KProcess* process) {
const auto process_id = process != nullptr ? process->GetProcessId() : 0;
auto session_ids = container.GetSessionIdsForProcess(process);
if (process_id != 0) {
for (const auto session_id : GetSessionIdsForAruid(process_id)) {
AppendUniqueSession(session_ids, session_id);
}
}
const auto active_session_ids = container.GetActiveSessionIds();
const auto active_before = active_session_ids.size();
const bool has_active_candidate =
std::ranges::any_of(session_ids, [this](const auto session_id) {
return container.IsSessionActive(session_id);
});
bool used_active_sessions = false;
if (!has_active_candidate && !active_session_ids.empty()) {
for (const auto session_id : active_session_ids) {
AppendUniqueSession(session_ids, session_id);
}
used_active_sessions = true;
}
const auto closed_files = CloseFilesForSessions(session_ids);
const auto closed_sessions = container.CloseSessions(session_ids);
for (const auto session_id : session_ids) {
if (!container.IsSessionActive(session_id)) {
session_aruids.erase(session_id.id);
}
}
if (used_active_sessions) {
LOG_WARNING(Service_NVDRV,
"NextLoad: NVDRV reset used active sessions because process-owned "
"sessions were not found, process_id={}, sessions={}, files={}, active_before={}",
process_id, closed_sessions, closed_files, active_before);
}
return closed_sessions;
}
} // namespace Service::Nvidia
+10
View File
@@ -12,6 +12,7 @@
#include <memory>
#include <span>
#include <string>
#include <vector>
#include <ankerl/unordered_dense.h>
#include "common/common_types.h"
@@ -26,6 +27,7 @@ class System;
namespace Kernel {
class KEvent;
class KProcess;
}
namespace Service::Nvidia {
@@ -89,6 +91,9 @@ public:
NvResult Close(DeviceFD fd);
NvResult QueryEvent(DeviceFD fd, u32 event_id, Kernel::KEvent*& event);
void CloseSession(NvCore::SessionId session_id);
void TrackSessionAruid(NvCore::SessionId session_id, u64 aruid);
size_t ResetForProcess(Kernel::KProcess* process);
NvCore::Container& GetContainer() {
return container;
@@ -106,12 +111,17 @@ private:
using FilesContainerType = ankerl::unordered_dense::map<DeviceFD, std::shared_ptr<Devices::nvdevice>>;
/// Mapping of file descriptors to the devices they reference.
FilesContainerType open_files;
ankerl::unordered_dense::map<DeviceFD, NvCore::SessionId> open_file_sessions;
ankerl::unordered_dense::map<size_t, u64> session_aruids;
KernelHelpers::ServiceContext service_context;
EventInterface events_interface;
ankerl::unordered_dense::map<std::string, std::function<FilesContainerType::iterator(DeviceFD)>> builders;
size_t CloseFilesForSessions(std::span<const NvCore::SessionId> session_ids);
std::vector<NvCore::SessionId> GetSessionIdsForAruid(u64 aruid) const;
};
void LoopProcess(Core::System& system);
@@ -59,7 +59,7 @@ void NVDRV::Ioctl1(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto fd = rp.Pop<DeviceFD>();
const auto command = rp.PopRaw<Ioctl>();
LOG_DEBUG(Service_NVDRV, "called fd={}, ioctl=0x{:08X}", fd, command.raw);
LOG_DEBUG(Service_NVDRV, "called fd={}, ioctl={:#08x}", fd, command.raw);
if (!is_initialized) {
ServiceError(ctx, NvResult::NotInitialized);
@@ -85,7 +85,7 @@ void NVDRV::Ioctl2(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto fd = rp.Pop<DeviceFD>();
const auto command = rp.PopRaw<Ioctl>();
LOG_DEBUG(Service_NVDRV, "called fd={}, ioctl=0x{:08X}", fd, command.raw);
LOG_DEBUG(Service_NVDRV, "called fd={}, ioctl={:#08x}", fd, command.raw);
if (!is_initialized) {
ServiceError(ctx, NvResult::NotInitialized);
@@ -112,7 +112,7 @@ void NVDRV::Ioctl3(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto fd = rp.Pop<DeviceFD>();
const auto command = rp.PopRaw<Ioctl>();
LOG_DEBUG(Service_NVDRV, "called fd={}, ioctl=0x{:08X}", fd, command.raw);
LOG_DEBUG(Service_NVDRV, "called fd={}, ioctl={:#08x}", fd, command.raw);
if (!is_initialized) {
ServiceError(ctx, NvResult::NotInitialized);
@@ -213,6 +213,9 @@ void NVDRV::SetAruid(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
pid = rp.Pop<u64>();
LOG_WARNING(Service_NVDRV, "(STUBBED) called, pid={:#X}", pid);
if (is_initialized) {
nvdrv->TrackSessionAruid(session_id, pid);
}
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
@@ -91,7 +91,7 @@ Result SteadyClock::IsRtcResetDetected(Out<bool> out_is_detected) {
Result SteadyClock::GetSetupResultValue(Out<Result> out_result) {
SCOPE_EXIT {
LOG_DEBUG(Service_Time, "called. out_result={:#X}", out_result->raw);
LOG_DEBUG(Service_Time, "called. out_result={:#x}", out_result->raw);
};
R_UNLESS(m_can_write_uninitialized_clock || m_clock_core.IsInitialized(),
+3 -3
View File
@@ -25,7 +25,7 @@ namespace Service {
int num_params = (cmd_buf[0] & 0x3F) + ((cmd_buf[0] >> 6) & 0x3F);
std::string function_string = fmt::format("fn '{}': port={}", name, port_name);
for (int i = 1; i <= num_params; ++i)
function_string += fmt::format(", cmd_buf[{}]={:#X}", i, cmd_buf[i]);
function_string += fmt::format(", cmd_buf[{}]={:#x}", i, cmd_buf[i]);
return function_string;
}
@@ -62,9 +62,9 @@ void ServiceFrameworkBase::ReportUnimplementedFunction(HLERequestContext& ctx,
std::string function_name = info == nullptr ? "<unknown>" : info->name;
fmt::memory_buffer buf;
fmt::format_to(std::back_inserter(buf), "function '{}({})': port='{}' cmd_buf={{[0]={:#X}", ctx.GetCommand(), function_name, service_name, cmd_buf[0]);
fmt::format_to(std::back_inserter(buf), "function '{}({})': port='{}' cmd_buf={{[0]={:#x}", ctx.GetCommand(), function_name, service_name, cmd_buf[0]);
for (int i = 1; i <= 8; ++i)
fmt::format_to(std::back_inserter(buf), ", [{}]={:#X}", i, cmd_buf[i]);
fmt::format_to(std::back_inserter(buf), ", [{}]={:#x}", i, cmd_buf[i]);
buf.push_back('}');
system.GetReporter().SaveUnimplementedFunctionReport(ctx, ctx.GetCommand(), function_name, service_name);
+3 -3
View File
@@ -89,9 +89,7 @@ Services::Services(std::shared_ptr<SM::ServiceManager>& sm, Core::System& system
for (auto const& e : std::vector<std::pair<std::string_view, void (*)(Core::System&)>>{
{"audio", &Audio::LoopProcess},
{"FS", &FileSystem::LoopProcess},
{"jit", &JIT::LoopProcess},
{"ldn", &LDN::LoopProcess},
{"Loader", &LDR::LoopProcess},
{"nvservices", &Nvidia::LoopProcess},
{"bsdsocket", &Sockets::LoopProcess},
})
@@ -120,19 +118,21 @@ Services::Services(std::shared_ptr<SM::ServiceManager>& sm, Core::System& system
{"glue", &Glue::LoopProcess},
{"grc", &GRC::LoopProcess},
{"hid", &HID::LoopProcess},
{"jit", &JIT::LoopProcess},
{"lbl", &LBL::LoopProcess},
{"Loader", &LDR::LoopProcess},
{"LogManager.Prod", &LM::LoopProcess},
{"mig", &Migration::LoopProcess},
{"mii", &Mii::LoopProcess},
{"mm", &MM::LoopProcess},
{"mnpp", &MNPP::LoopProcess},
{"nvnflinger", &Nvnflinger::LoopProcess},
{"NCM", &NCM::LoopProcess},
{"nfc", &NFC::LoopProcess},
{"nfp", &NFP::LoopProcess},
{"ngc", &NGC::LoopProcess},
{"nifm", &NIFM::LoopProcess},
{"nim", &NIM::LoopProcess},
{"nvnflinger", &Nvnflinger::LoopProcess},
{"npns", &NPNS::LoopProcess},
{"ns", &NS::LoopProcess},
{"olsc", &OLSC::LoopProcess},
+1 -1
View File
@@ -196,7 +196,7 @@ Result SM::GetServiceImpl(Kernel::KClientSession** out_client_session, HLEReques
// Create a new session.
Kernel::KClientSession* session{};
if (const auto result = client_port->CreateSession(kernel, &session); result.IsError()) {
LOG_ERROR(Service_SM, "called service={} -> error 0x{:08X}", name, result.raw);
LOG_ERROR(Service_SM, "called service={} -> error {:#08x}", name, result.raw);
return result;
}
+5 -5
View File
@@ -283,7 +283,7 @@ void BSD::GetSockOpt(HLERequestContext& ctx) {
std::vector<u8> optval(ctx.GetWriteBufferSize());
LOG_DEBUG(Service, "called. fd={} level={} optname=0x{:x} len=0x{:x}", fd, level, optname,
LOG_DEBUG(Service, "called. fd={} level={} optname={:#x} len={:#x}", fd, level, optname,
optval.size());
const Errno err = GetSockOptImpl(fd, level, optname, optval);
@@ -331,7 +331,7 @@ void BSD::SetSockOpt(HLERequestContext& ctx) {
const OptName optname = static_cast<OptName>(rp.Pop<u32>());
const auto optval = ctx.ReadBuffer();
LOG_DEBUG(Service, "called. fd={} level={} optname=0x{:x} optlen={}", fd, level,
LOG_DEBUG(Service, "called. fd={} level={} optname={:#x} optlen={}", fd, level,
static_cast<u32>(optname), optval.size());
BuildErrnoResponse(ctx, SetSockOptImpl(fd, level, optname, optval));
@@ -354,7 +354,7 @@ void BSD::Recv(HLERequestContext& ctx) {
const s32 fd = rp.Pop<s32>();
const u32 flags = rp.Pop<u32>();
LOG_DEBUG(Service, "called. fd={} flags=0x{:x} len={}", fd, flags, ctx.GetWriteBufferSize());
LOG_DEBUG(Service, "called. fd={} flags={:#x} len={}", fd, flags, ctx.GetWriteBufferSize());
ExecuteWork(ctx, RecvWork{
.fd = fd,
@@ -369,7 +369,7 @@ void BSD::RecvFrom(HLERequestContext& ctx) {
const s32 fd = rp.Pop<s32>();
const u32 flags = rp.Pop<u32>();
LOG_DEBUG(Service, "called. fd={} flags=0x{:x} len={} addrlen={}", fd, flags,
LOG_DEBUG(Service, "called. fd={} flags={:#x} len={} addrlen={}", fd, flags,
ctx.GetWriteBufferSize(0), ctx.GetWriteBufferSize(1));
ExecuteWork(ctx, RecvFromWork{
@@ -386,7 +386,7 @@ void BSD::Send(HLERequestContext& ctx) {
const s32 fd = rp.Pop<s32>();
const u32 flags = rp.Pop<u32>();
LOG_DEBUG(Service, "called. fd={} flags=0x{:x} len={}", fd, flags, ctx.GetReadBufferSize());
LOG_DEBUG(Service, "called. fd={} flags={:#x} len={}", fd, flags, ctx.GetReadBufferSize());
ExecuteWork(ctx, SendWork{
.fd = fd,
@@ -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
@@ -141,7 +141,7 @@ Result IApplicationDisplayService::GetDisplayResolution(Out<s64> out_width, Out<
}
Result IApplicationDisplayService::SetLayerScalingMode(NintendoScaleMode scale_mode, u64 layer_id) {
LOG_DEBUG(Service_VI, "called. scale_mode={}, unknown=0x{:016X}", scale_mode, layer_id);
LOG_DEBUG(Service_VI, "called. scale_mode={}, unknown={:#016x}", scale_mode, layer_id);
if (scale_mode > NintendoScaleMode::PreserveAspectRatio) {
LOG_ERROR(Service_VI, "Invalid scaling mode provided.");
+7 -5
View File
@@ -40,6 +40,7 @@ namespace Network {
namespace {
enum class CallType {
Connect,
Send,
Other,
};
@@ -131,7 +132,7 @@ Errno TranslateNativeError(int e, CallType call_type = CallType::Other) {
case WSAENOTCONN:
return Errno::NOTCONN;
case WSAEWOULDBLOCK:
return Errno::AGAIN;
return call_type == CallType::Connect ? Errno::INPROGRESS : Errno::AGAIN;
case WSAECONNREFUSED:
return Errno::CONNREFUSED;
case WSAECONNABORTED:
@@ -563,6 +564,7 @@ int TranslateTypeToNative(Type type) {
NETWORK_PROTOCOL_TRANSLATE_ELEM(UDPLITE)
#elif defined(_WIN32)
#define NETWORK_PROTOCOL_TRANSLATE_LIST \
NETWORK_PROTOCOL_TRANSLATE_ELEM(IP) \
/*NETWORK_PROTOCOL_TRANSLATE_ELEM(HOPOPTS)*/ \
NETWORK_PROTOCOL_TRANSLATE_ELEM(ICMP) \
NETWORK_PROTOCOL_TRANSLATE_ELEM(IGMP) \
@@ -652,13 +654,13 @@ short TranslatePollEvents(PollEvents events) {
// Unlike poll on other OSes, WSAPoll will complain if any other flags are set on input.
if (result & ~allowed_events) {
LOG_DEBUG(Network,
"Removing WSAPoll input events 0x{:x} because Windows doesn't support them",
"Removing WSAPoll input events {:#x} because Windows doesn't support them",
result & ~allowed_events);
}
result &= allowed_events;
#endif
UNIMPLEMENTED_IF_MSG((u16)events != 0, "Unhandled guest events=0x{:x}", (u16)events);
UNIMPLEMENTED_IF_MSG((u16)events != 0, "Unhandled guest events={:#x}", (u16)events);
return result;
}
@@ -682,7 +684,7 @@ PollEvents TranslatePollRevents(short revents) {
translate(POLLRDBAND, PollEvents::RdBand);
translate(POLLWRBAND, PollEvents::WrBand);
UNIMPLEMENTED_IF_MSG(revents != 0, "Unhandled host revents=0x{:x}", revents);
UNIMPLEMENTED_IF_MSG(revents != 0, "Unhandled host revents={:#x}", revents);
return result;
}
@@ -888,7 +890,7 @@ Errno Socket::Connect(SockAddrIn addr_in) {
return Errno::SUCCESS;
}
return GetAndLogLastError();
return GetAndLogLastError(CallType::Connect);
}
std::pair<SockAddrIn, Errno> Socket::GetPeerName() {
@@ -263,7 +263,7 @@ AppLoader_DeconstructedRomDirectory::LoadResult AppLoader_DeconstructedRomDirect
next_load_addr = *tentative_next_load_addr;
modules.insert_or_assign(load_addr, module);
LOG_DEBUG(Loader, "loaded module {} @ {:#X}", module, load_addr);
LOG_DEBUG(Loader, "loaded module {} @ {:#x}", module, load_addr);
}
is_loaded = true;

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