Compare commits

..

122 Commits

Author SHA1 Message Date
PavelBARABANOV 3cfe9f04e1 fix totk purple 2026-08-02 02:22:22 +03:00
PavelBARABANOV a074268eb0 fix slow compilation 2026-08-02 02:20:21 +03:00
CamilleLaVey 50763528a2 MEOW 2026-08-01 03:41:13 -04:00
CamilleLaVey 4b3608b58d debug uma impl 2026-08-01 03:07:57 -04:00
CamilleLaVey b76d04e47d ASTC adjustments 2026-08-01 02:41:09 -04:00
CamilleLaVey 71afaff6b3 Another try 2026-08-01 00:03:29 -04:00
CamilleLaVey dcbfa3c2d3 Meow gidolio 2.0 2026-07-31 23:14:58 -04:00
CamilleLaVey 70ff43f2f8 Meow gidolio 2026-07-31 23:10:31 -04:00
CamilleLaVey 76faac9252 shader quad control impl 2026-07-31 23:06:39 -04:00
CamilleLaVey 6346371725 Some memory corrections 2026-07-31 22:52:42 -04:00
CamilleLaVey 4162f86a11 debug overlap images 2 2026-07-31 21:33:49 -04:00
CamilleLaVey 86224e49ef Test on depth bias 2026-07-31 20:46:07 -04:00
CamilleLaVey 6665a89dd2 Fix build 2026-07-31 19:55:28 -04:00
CamilleLaVey 515afa2959 Initial Implementation AHB 2026-07-31 19:46:04 -04:00
CamilleLaVey 8c8c7c6162 Adjustments on UMA 2026-07-31 19:30:31 -04:00
CamilleLaVey fb65aa3c33 2nd step on the bindless implementation 2026-07-31 18:35:17 -04:00
CamilleLaVey a24ad03ff3 Android toggle for uma 2026-07-31 18:10:45 -04:00
CamilleLaVey 4884a0c002 Initial implementation on unified memory access 2026-07-31 18:03:49 -04:00
CamilleLaVey ab1d99d218 some debugs tools 2026-07-31 17:31:43 -04:00
CamilleLaVey e6ee576f80 Quick shenanigan 2026-07-31 05:45:27 -04:00
CamilleLaVey 1682b532da relax memory and draws dispatchs 2026-07-31 05:09:14 -04:00
CamilleLaVey 40fe050954 Quick change for cheats 2026-07-31 04:58:17 -04:00
CamilleLaVey 24ff23f2ea Quick change on pipeline barriers 2026-07-31 04:17:38 -04:00
CamilleLaVey 87ebbfb9b1 Coalescing memory entries on MLP 2026-07-31 04:11:40 -04:00
CamilleLaVey 471778c850 WEML toggle 2026-07-31 03:12:22 -04:00
CamilleLaVey 095f27fd34 Another try with 3D BCn 2026-07-31 03:05:52 -04:00
CamilleLaVey a6c423cafb Smolio jenkins 2026-07-31 02:19:41 -04:00
CamilleLaVey 514507b674 fix build 2026-07-31 02:13:19 -04:00
CamilleLaVey 10935f8d46 playing with isberd 2026-07-31 02:11:28 -04:00
CamilleLaVey a0dae2d490 Compute shader 3D BCn to VK 2026-07-31 01:41:00 -04:00
CamilleLaVey 5e40e3c9c0 GIDOLIO THE KOREAN MENACE 2026-07-31 00:57:05 -04:00
CamilleLaVey f61d05f8da HERE WE GO AGAIN. 2026-07-31 00:45:18 -04:00
CamilleLaVey b04207fdfc Adjustments on the plain data for vertex buffer optimization 2026-07-30 23:54:26 -04:00
CamilleLaVey 91791bd5fe Meow Gidolius Galacticus 2026-07-30 20:36:44 -04:00
CamilleLaVey 9928885a5d DynamicRendering + Pipeline Worker Thread toggles 2026-07-30 20:24:50 -04:00
CamilleLaVey 81fa91b400 Another funny change for pipelines 2026-07-30 20:15:11 -04:00
CamilleLaVey eb8706e366 Funny experiment for pipelines 2026-07-30 19:53:34 -04:00
CamilleLaVey b9d55c9448 2nd step for the buffer address device implementation 2026-07-30 19:29:19 -04:00
CamilleLaVey 1188f285b7 Pipeline Creation Feedback impl 2026-07-30 18:57:15 -04:00
CamilleLaVey 200b81b087 Initial step for the buffer address device implementation 2026-07-30 18:28:24 -04:00
CamilleLaVey 742cadde1d Keep conditional rendering inside a single render pass instance
Begin and end were landing in different scopes: the clear and draw
texture paths opened it with no pass bound, the compare helpers reopened
it after their resolve pass had closed one, and the pass teardown could
return before pausing, leaving it active at submit.
2026-07-30 18:13:56 -04:00
CamilleLaVey c3effdf558 Use per-barrier stage masks on render pass end
Adds a native VkDependencyInfo entry point and splits the end of pass
barrier so color and depth/stencil attachments no longer wait on each
other's stages.
2026-07-30 17:09:21 -04:00
CamilleLaVey b9428acf12 Cut per-draw descriptor set work
Only query the timeline semaphore when no set looks free, and skip the
update plus rebind when the payload matches the previous draw.
2026-07-30 17:09:21 -04:00
CamilleLaVey b027dc42ce Restore conditional rendering workaround for Adreno, Mali and Turnip 2026-07-30 16:54:30 -04:00
CamilleLaVey dc9c99b22d Revert "[TEST] Keep tuning the drain/ memory allocation"
This reverts commit 7f4529b15d.
2026-07-30 16:53:25 -04:00
CamilleLaVey 5346e77c0a Revert "[TEST] Adjust mutable formats usage"
This reverts commit 328a2c1081.
2026-07-30 16:45:08 -04:00
CamilleLaVey 914b78495c Revert "[TEST] Change on storage views per request instead of always granted"
This reverts commit d1eb08c036.
2026-07-30 16:45:03 -04:00
CamilleLaVey a17b4cbb04 Revert "[TEST] New optimal image layouts access"
This reverts commit 236696b0ff.
2026-07-30 16:44:37 -04:00
CamilleLaVey a7b5910eaa Revert "[TEST] Adjustments on storage view + general image layouts"
This reverts commit 88fcb7d338.
2026-07-30 16:44:10 -04:00
CamilleLaVey f98f8b65de Some trivial tests 2026-07-30 15:57:59 -04:00
CamilleLaVey 88fcb7d338 [TEST] Adjustments on storage view + general image layouts 2026-07-30 15:13:04 -04:00
CamilleLaVey 4a4fd056ff enable msaa depth resolve 2026-07-29 20:57:20 -04:00
CamilleLaVey 236696b0ff [TEST] New optimal image layouts access 2026-07-29 20:56:56 -04:00
CamilleLaVey d1eb08c036 [TEST] Change on storage views per request instead of always granted 2026-07-29 20:49:36 -04:00
CamilleLaVey 84a4149db2 Presync fix again 2026-07-29 19:46:51 -04:00
CamilleLaVey de9b1071a5 [TEST] VMA budget fix 2026-07-29 19:41:38 -04:00
CamilleLaVey 85049c2c78 [TEST] Presync + CR workaround removal from QCOM 2026-07-29 19:09:22 -04:00
CamilleLaVey 7fcf88cb10 [TEST] DR Adjustment 2 2026-07-29 17:28:09 -04:00
CamilleLaVey e4f29899d2 [TEST] Adjustment memory reclamation 2 2026-07-29 16:32:54 -04:00
CamilleLaVey 41d2bf4a3e Fix build 2026-07-29 14:52:06 -04:00
CamilleLaVey 328a2c1081 [TEST] Adjust mutable formats usage 2026-07-29 14:41:36 -04:00
CamilleLaVey 6e48eeb09c [TEST] depth stencil resolve implementation + fixes 2026-07-29 14:11:30 -04:00
CamilleLaVey 2cdd7173a9 [TEST] Line/ conservative rasterization adjustment 2026-07-29 13:27:31 -04:00
CamilleLaVey 69342044ef [TEST] Quick corrections 2026-07-29 13:27:31 -04:00
CamilleLaVey 1f41d809b0 [TEST] depth clip enable implementation 2026-07-29 13:27:31 -04:00
CamilleLaVey 250b545d75 [TEST] Debug viewport geometry 2026-07-29 13:27:31 -04:00
CamilleLaVey 5a9322af80 [TEST] Debug depths on viewports 2026-07-29 13:27:31 -04:00
CamilleLaVey bbee229d07 [TEST] Adjustments on memory reclamations 2026-07-29 13:27:31 -04:00
CamilleLaVey cfe1c8b16c [TEST] Rework Android CPU affinity/ threading 2 2026-07-29 13:27:31 -04:00
CamilleLaVey dd5d09e3ae [TEST] Rework Android CPU affinity/ threading 2026-07-29 13:27:25 -04:00
CamilleLaVey 25af655e48 [TEST] Adjustments on TransformFeedback 2026-07-29 13:26:55 -04:00
CamilleLaVey 0cd5dd1e70 [TEST] Adjustments on framepacing submit calls 2026-07-29 13:26:23 -04:00
CamilleLaVey 8c828e78f2 [TEST] Refinements on RAII 2026-07-29 13:26:23 -04:00
CamilleLaVey b1adbd8e6b [TEST] ASTC fixes 2026-07-29 13:26:23 -04:00
CamilleLaVey 0fb0592a5f [TEST] Miscellaneous changes 2026-07-29 13:26:23 -04:00
CamilleLaVey b59dda3af6 Lizzie get meowed, please. 2026-07-29 13:26:23 -04:00
CamilleLaVey aec20586be [TEST] Adjusments on deferred destruction implementation 2026-07-29 13:26:23 -04:00
CamilleLaVey 6918f034e2 [TEST] Overlap image on non implemented path 2026-07-29 13:26:23 -04:00
CamilleLaVey 4b1e2d1d6a [TEST] Debug 3D image copy 2026-07-29 13:26:23 -04:00
CamilleLaVey 9318a508f7 [TEST] Fallback on TICEntry out of bounds 2026-07-29 13:26:23 -04:00
CamilleLaVey f95a9f3901 Meow 2026-07-29 13:26:23 -04:00
CamilleLaVey 1169533a74 [TEST] Debug TICEntry shader pass 2026-07-29 13:26:23 -04:00
CamilleLaVey 991ff44bd6 [TEST] Adjust ternary on pipeline barriers 2026-07-29 13:26:23 -04:00
CamilleLaVey 624f955821 [TEST] Upload on depth MSAACopy pass 2026-07-29 13:26:23 -04:00
CamilleLaVey 5c0fe5da06 [TEST] Adjustments on descriptors payloads 2026-07-29 13:26:23 -04:00
CamilleLaVey 18630fa739 [TEST] Smolios forever 2026-07-29 13:26:23 -04:00
CamilleLaVey 9752908030 [TEST] Test anti-smolios 2026-07-29 13:26:23 -04:00
CamilleLaVey a711fd99a6 [TEST] debug MSAA texture missing handling 2026-07-29 13:26:23 -04:00
CamilleLaVey f6c1404e09 Revert "[TEST] Some miscellaneous changes within buffers queue and RAII deallocations" 2026-07-29 13:26:23 -04:00
CamilleLaVey 77ffefbbbf Revert "[TEST] Another miscellaneous change 2" 2026-07-29 13:26:23 -04:00
CamilleLaVey bf13e7ce1d [TEST] Another miscellaneous change 2 2026-07-29 13:26:23 -04:00
CamilleLaVey 3cd17ec392 [TEST] Some miscellaneous changes within buffers queue and RAII deallocations 2026-07-29 13:26:22 -04:00
CamilleLaVey 7f4529b15d [TEST] Keep tuning the drain/ memory allocation 2026-07-29 13:26:22 -04:00
CamilleLaVey f649730cb1 Fix build 2026-07-29 13:26:22 -04:00
CamilleLaVey 40c91abdad Fix license headers 2026-07-29 13:26:22 -04:00
CamilleLaVey d21bb9a1b7 [TEST] Adjustments on memory collection 2026-07-29 13:26:22 -04:00
CamilleLaVey 94e814ecf1 [TEST] Adjustments on memory collection + removal dead code 2026-07-29 13:26:22 -04:00
CamilleLaVey b8135a3e47 [TEST] Refactor on memory collections 2026-07-29 13:26:22 -04:00
CamilleLaVey 8d7f992792 Enable DR 2026-07-29 13:22:20 -04:00
CamilleLaVey f199597158 [TEST] DR Off 2026-07-17 21:03:17 -04:00
CamilleLaVey 9c313fb787 [TEST] Caching for texture + pages on NCE 2026-07-17 20:15:19 -04:00
CamilleLaVey eec29b83f3 [TEST] Coalesce NCE fault write. 2026-07-17 19:56:16 -04:00
CamilleLaVey a57041d62f [TEST] Discards on MSAA depth/stencil 2026-07-17 17:57:55 -04:00
CamilleLaVey 4956bc86c3 [TEST] Hunting down recursive mutex 7 2026-07-17 17:44:24 -04:00
CamilleLaVey e8b1dc7c0b [TEST] Adjustments on CommandPools + ResetQueryPool 2026-07-17 07:52:45 -04:00
CamilleLaVey e81d170458 [TEST] Remove unnecessary memory upload 2026-07-17 07:28:30 -04:00
CamilleLaVey 7ed7e5e31d [TEST] Remove unnecessary submit 2026-07-17 03:56:02 -04:00
CamilleLaVey eb32b8766a [TEST] Hunting down recursive mutex 6 2026-07-17 03:13:55 -04:00
CamilleLaVey a484e6c34b [TEST] Hunting down recursive mutex 5 2026-07-17 03:00:53 -04:00
CamilleLaVey 84490a7d6f [TEST] Hunting down recursive mutex 4 2026-07-17 02:32:14 -04:00
CamilleLaVey 933f79af95 [TEST] Hunting down recursive mutex 3 2026-07-17 02:15:10 -04:00
CamilleLaVey f532357793 [TEST] Hunting down recursive mutex 2 2026-07-17 01:55:28 -04:00
CamilleLaVey ab92e5fa52 [TEST] Hunting down recursive mutex 1 2026-07-17 01:13:15 -04:00
CamilleLaVey 791880f9bf [TEST] Adjust records/commandBuffer 2026-07-16 22:50:08 -04:00
CamilleLaVey e058a15074 [TEST] Wire MSAA resolve to dynamic rendering native resolve 2026-07-16 20:29:18 -04:00
CamilleLaVey 6313800aee [TEST] Check on color components for masks/blending 2026-07-16 20:02:59 -04:00
CamilleLaVey 5428dbbd14 [TEST] Adjustments on blit/clears per blending object 2026-07-16 17:04:32 -04:00
CamilleLaVey 9694216ad7 [TEST] 2nd stage on dynamic rendering implementation 2026-07-16 16:53:22 -04:00
CamilleLaVey 4fbdc133dd [vulkan] Add resume/supend bits on renderpass 2026-07-16 15:50:36 -04:00
CamilleLaVey f210f16e8c [TEST] Debug on clears made by driver 2026-07-16 15:47:23 -04:00
CamilleLaVey 20bf6bc282 [vulkan] Adjustment on the framebuffer use vs non used attachments 2026-07-16 15:45:49 -04:00
CamilleLaVey 99e95eebb1 [vulkan] Initial dynamic rendering implementation 2026-07-16 15:44:06 -04:00
235 changed files with 6942 additions and 3223 deletions
-5
View File
@@ -1,5 +0,0 @@
- [ ] I have read and followed the [Contribution Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/CONTRIBUTING.md#code-contributions).
- [ ] I have read and followed the [AI Policy](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/AI.md)
- [ ] 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.
-------------------
+1 -3
View File
@@ -804,9 +804,7 @@ 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>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>
<translation type="unfinished"/>
</message>
<message>
<location filename="../../src/qt_common/config/shared_translation.cpp" line="227"/>
+1 -3
View File
@@ -793,9 +793,7 @@ 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>Керує режимом емуляції ГП.
Більшість ігор добре візуалізуються з режимом «Швидко», але деякі ігри можуть потребувати режиму «Точно».
Частинки зазвичай правильно візуалізуються лише з режимом «Точно».</translation>
<translation type="unfinished"/>
</message>
<message>
<location filename="../../src/qt_common/config/shared_translation.cpp" line="225"/>
-3
View File
@@ -27,6 +27,3 @@ 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.
+1 -1
View File
@@ -65,7 +65,7 @@ android {
defaultConfig {
applicationId = "dev.eden.eden_emulator"
minSdk = 24
minSdk = 33
targetSdk = 36
versionName = getGitVersion()
versionCode = autoVersion
@@ -27,6 +27,7 @@ enum class BooleanSetting(override val key: String) : AbstractBooleanSetting {
RENDERER_ASYNCHRONOUS_GPU_EMULATION("use_asynchronous_gpu_emulation"),
RENDERER_ASYNC_PRESENTATION("async_presentation"),
RENDERER_ASYNCHRONOUS_SHADERS("use_asynchronous_shaders"),
RENDERER_UNIFIED_MEMORY("use_unified_memory"),
RENDERER_REACTIVE_FLUSHING("use_reactive_flushing"),
ENABLE_BUFFER_HISTORY("enable_buffer_history"),
USE_OPTIMIZED_VERTEX_BUFFERS("use_optimized_vertex_buffers"),
@@ -36,6 +37,8 @@ enum class BooleanSetting(override val key: String) : AbstractBooleanSetting {
RENDERER_DEBUG("debug"),
RENDERER_PATCH_OLD_QCOM_DRIVERS("patch_old_qcom_drivers"),
RENDERER_VERTEX_INPUT_DYNAMIC_STATE("vertex_input_dynamic_state"),
RENDERER_DYNAMIC_RENDERING("dynamic_rendering"),
RENDERER_WORKGROUP_MEMORY_EXPLICIT_LAYOUT("workgroup_memory_explicit_layout"),
RENDERER_SAMPLE_SHADING("sample_shading"),
GPU_UNSWIZZLE_ENABLED("gpu_unswizzle_enabled"),
PICTURE_IN_PICTURE("picture_in_picture"),
@@ -155,6 +155,20 @@ abstract class SettingsItem(
descriptionId = R.string.vertex_input_dynamic_state_description
)
)
put(
SwitchSetting(
BooleanSetting.RENDERER_DYNAMIC_RENDERING,
titleId = R.string.dynamic_rendering,
descriptionId = R.string.dynamic_rendering_description
)
)
put(
SwitchSetting(
BooleanSetting.RENDERER_WORKGROUP_MEMORY_EXPLICIT_LAYOUT,
titleId = R.string.workgroup_memory_explicit_layout,
descriptionId = R.string.workgroup_memory_explicit_layout_description
)
)
put(
SliderSetting(
IntSetting.RENDERER_SAMPLE_SHADING,
@@ -594,7 +608,7 @@ abstract class SettingsItem(
IntSetting.ANDROID_PIPELINE_WORKERS,
titleId = R.string.pipeline_worker_cores,
descriptionId = R.string.pipeline_worker_cores_description,
min = 4,
min = 2,
max = 8,
units = "cores"
)
@@ -685,6 +699,13 @@ abstract class SettingsItem(
descriptionId = R.string.renderer_asynchronous_shaders_description
)
)
put(
SwitchSetting(
BooleanSetting.RENDERER_UNIFIED_MEMORY,
titleId = R.string.renderer_unified_memory,
descriptionId = R.string.renderer_unified_memory_description
)
)
put(
SingleChoiceSetting(
IntSetting.FAST_GPU_TIME,
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-FileCopyrightText: Copyright 2025 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(),
InputHandler.getButtonIdFromEvent(event),
event.keyCode,
action
)
onInputReceived(event.device)
@@ -304,6 +304,7 @@ class SettingsFragmentPresenter(
add(BooleanSetting.EMULATE_BGR565.key)
add(BooleanSetting.RESCALE_HACK.key)
add(BooleanSetting.RENDERER_ASYNCHRONOUS_SHADERS.key)
add(BooleanSetting.RENDERER_UNIFIED_MEMORY.key)
add(IntSetting.ANDROID_PIPELINE_WORKERS.key)
add(BooleanSetting.RENDERER_ASYNCHRONOUS_GPU_EMULATION.key)
add(BooleanSetting.RENDERER_ASYNC_PRESENTATION.key)
@@ -313,6 +314,8 @@ class SettingsFragmentPresenter(
add(IntSetting.RENDERER_DYNA_STATE.key)
add(BooleanSetting.RENDERER_VERTEX_INPUT_DYNAMIC_STATE.key)
add(BooleanSetting.RENDERER_DYNAMIC_RENDERING.key)
add(BooleanSetting.RENDERER_WORKGROUP_MEMORY_EXPLICIT_LAYOUT.key)
add(IntSetting.RENDERER_SAMPLE_SHADING.key)
add(HeaderSetting(R.string.display))
@@ -49,12 +49,6 @@ 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
@@ -93,25 +87,12 @@ object InputHandler {
NativeInput.onGamePadButtonEvent(
controllerData.getGUID(),
controllerData.getPort(),
getButtonIdFromEvent(event),
event.keyCode,
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
@@ -147,13 +147,6 @@ namespace AndroidSettings {
&show_performance_overlay};
Settings::Setting<s32> pipeline_worker_count{linkage, 4, "pipeline_worker_count",
Settings::Category::Android,
Settings::Specialization::Default,
true,
true};
Settings::Setting<bool> show_input_overlay{linkage, true, "show_input_overlay",
Settings::Category::Overlay};
Settings::Setting<bool> overlay_snap_to_grid{linkage, false, "overlay_snap_to_grid",
@@ -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>
@@ -1000,6 +1000,7 @@
<!-- Renderer Accuracy -->
<string name="renderer_accuracy_low">سريع</string>
<string name="renderer_accuracy_medium">متوازن</string>
<string name="renderer_accuracy_high">دقيق</string>
<!-- DMA Accuracy -->
@@ -445,6 +445,7 @@
<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>
@@ -706,6 +707,7 @@
<!-- Renderer Accuracy -->
<string name="renderer_accuracy_low">Rychlý</string>
<string name="renderer_accuracy_medium">Vyvážený</string>
<string name="renderer_accuracy_high">Přesný</string>
<!-- DMA Accuracy -->
@@ -457,6 +457,7 @@ 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>
@@ -926,6 +927,7 @@ Wirklich fortfahren?</string>
<!-- Renderer Accuracy -->
<string name="renderer_accuracy_low">Schnell</string>
<string name="renderer_accuracy_medium">Ausgeglichen</string>
<string name="renderer_accuracy_high">Genau</string>
<!-- DMA Accuracy -->
@@ -467,6 +467,7 @@
<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>
@@ -992,6 +993,7 @@
<!-- Renderer Accuracy -->
<string name="renderer_accuracy_low">Rápido</string>
<string name="renderer_accuracy_medium">Equilibrado</string>
<string name="renderer_accuracy_high">Preciso</string>
<!-- DMA Accuracy -->
@@ -939,6 +939,7 @@
<!-- Renderer Accuracy -->
<string name="renderer_accuracy_low">Rapide</string>
<string name="renderer_accuracy_medium">Moyen</string>
<string name="renderer_accuracy_high">Précis</string>
<!-- DMA Accuracy -->
@@ -445,6 +445,7 @@
<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>
@@ -892,6 +893,7 @@
<!-- Renderer Accuracy -->
<string name="renderer_accuracy_low">Szybkie</string>
<string name="renderer_accuracy_medium">Zrównoważony</string>
<string name="renderer_accuracy_high">Dokładny</string>
<!-- DMA Accuracy -->
@@ -840,6 +840,7 @@
<string name="renderer_none">Nenhum</string>
<string name="renderer_accuracy_medium">Média</string>
<string name="renderer_accuracy_high">Alta</string>
<!-- DMA Accuracy -->
@@ -424,9 +424,6 @@
<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>
@@ -469,11 +466,9 @@
<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>
@@ -495,8 +490,6 @@
<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>
@@ -571,12 +564,6 @@
<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>
@@ -996,6 +983,7 @@
<!-- Renderer Accuracy -->
<string name="renderer_accuracy_low">Быстрый</string>
<string name="renderer_accuracy_medium">Сбалансированный</string>
<string name="renderer_accuracy_high">Точный</string>
<!-- DMA Accuracy -->
@@ -1003,13 +991,6 @@
<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,6 +469,7 @@
<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>
@@ -985,6 +986,7 @@
<!-- Renderer Accuracy -->
<string name="renderer_accuracy_low">Швидко</string>
<string name="renderer_accuracy_medium">Збалансовано</string>
<string name="renderer_accuracy_high">Точно</string>
<!-- DMA Accuracy -->
@@ -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>
@@ -990,6 +990,7 @@
<!-- Renderer Accuracy -->
<string name="renderer_accuracy_low">快速</string>
<string name="renderer_accuracy_medium">均衡</string>
<string name="renderer_accuracy_high">精确</string>
<!-- DMA Accuracy -->
@@ -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,20 +54,18 @@
<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>
@@ -80,10 +78,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>
@@ -94,10 +92,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>
@@ -173,7 +171,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>
@@ -192,7 +190,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>
@@ -246,21 +244,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>
@@ -287,7 +285,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>
@@ -302,14 +300,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>
@@ -319,15 +317,12 @@
<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>
@@ -336,12 +331,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>
@@ -352,30 +347,28 @@
<string name="verify_installed_content">驗證已安裝內容的完整性</string>
<string name="verify_installed_content_description">檢查所有已安裝的內容是否有損壞</string>
<string name="keys_missing">缺少 prod.keys</string>
<string name="keys_missing">缺少解密金鑰</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">Switch系統主畫面啟動應用程式(目前僅支援英文)</string>
<string name="qlaunch_description">從系統主畫面啟動應用程式</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>
@@ -384,58 +377,47 @@
<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="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="frame_limit_slider_description">指定限制模擬速度的百分比。100% 為標準速度,更高或更低的值將會增加或減少速度限制</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>
@@ -446,41 +428,33 @@
<!-- 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="fsr_sharpness">FSR/SGSR 銳化度</string>
<string name="fsr_sharpness_description">使用 FSR/SGSR 時圖片的銳化程度</string>
<string name="renderer_scaling_filter">視窗適應濾器</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="gpu_fence_behavior">GPU 同步柵欄模式</string>
<string name="gpu_fence_behavior_description">控制 GPU 同步柵欄模式。即時是速度最快的選項,但可能會導致一些問題。使用平衡可以提供更好的相容性並修復某些遊戲中的錯誤。準確則是在犧牲部分效能的情況下進一步提升相容性。嚴格是最慢的選項,但可以修復那些對同步要求更嚴格的遊戲中的問題。預設則會依照 GPU 準確度設定</string>
<string name="dma_accuracy_description">控制 DMA 準確度。安全準確度可以修復某些遊戲中的問題,但在某些情況下也可能影響效能。如果不確定,請保留為預設」。</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>
@@ -629,7 +603,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>
@@ -847,8 +821,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>
@@ -869,12 +843,13 @@
<string name="renderer_none"></string>
<string name="renderer_accuracy_medium">平衡</string>
<string name="renderer_accuracy_high">準確</string>
<!-- 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>
@@ -900,7 +875,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>
@@ -103,12 +103,14 @@
<string-array name="rendererAccuracyNames">
<item>@string/renderer_accuracy_low</item>
<item>@string/renderer_accuracy_medium</item>
<item>@string/renderer_accuracy_high</item>
</string-array>
<integer-array name="rendererAccuracyValues">
<item>0</item>
<item>1</item>
<item>2</item>
</integer-array>
<!-- VRAM USAGE MODE CHOICES -->
@@ -479,7 +479,7 @@
<string name="advanced">Advanced</string>
<string name="renderer_accuracy">GPU Mode</string>
<string name="renderer_accuracy_description">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.</string>
<string name="renderer_accuracy_description">Controls the GPU emulation mode. Most games render fine with Fast or Balanced modes, but Accurate is still required for some. Particles tend to only render correctly with Accurate mode.</string>
<string name="dma_accuracy">DMA Accuracy</string>
<string name="dma_accuracy_description">Controls the DMA precision accuracy. Safe precision can fix issues in some games, but it can also impact performance in some cases. If unsure, leave this on Default.</string>
<string name="gpu_fence_behavior">GPU Fence Behavior</string>
@@ -524,6 +524,8 @@
<string name="rescale_hack_description">Enables a legacy handling for the rescale configuration pass for games by using a quick rescale path</string>
<string name="renderer_asynchronous_shaders">Use asynchronous shaders</string>
<string name="renderer_asynchronous_shaders_description">Compiles shaders asynchronously. This may reduce stutters but may also introduce glitches.</string>
<string name="renderer_unified_memory">Unified memory access (UMA)</string>
<string name="renderer_unified_memory_description">Allows GPU write buffer readbacks directly into guest memory, skipping the CPU staging copy.</string>
<string name="gpu_unswizzle_settings">GPU Unswizzle Settings</string>
<string name="gpu_unswizzle_settings_description">Configure GPU-based texture unswizzling parameters or disable it entirely. Adjust these settings to balance performance and texture loading quality.</string>
<string name="gpu_unswizzle_enable">Enable GPU Unswizzle</string>
@@ -544,6 +546,10 @@
<string name="disabled">Disabled</string>
<string name="vertex_input_dynamic_state">Vertex Input Dynamic State</string>
<string name="vertex_input_dynamic_state_description">Enabling this feature allows for more flexible vertex input handling, potentially reducing pipeline compilation time in vertex/buffer.</string>
<string name="dynamic_rendering">Dynamic Rendering</string>
<string name="dynamic_rendering_description">Render without render pass and framebuffer objects. Results vary by driver: some gain performance, others lose it.</string>
<string name="workgroup_memory_explicit_layout">Workgroup Memory Explicit Layout</string>
<string name="workgroup_memory_explicit_layout_description">Let shaders declare explicit layouts for workgroup memory. Disabled by default: some Qualcomm drivers are unstable with it.</string>
<string name="sample_shading_fraction">Sample Shading</string>
<string name="sample_shading_fraction_description">Allows the fragment shader to execute per sample in a multi-sampled fragment instead once per fragment. Improves graphics quality at the cost of some performance.</string>
@@ -1039,6 +1045,7 @@
<!-- Renderer Accuracy -->
<string name="renderer_accuracy_low">Fast</string>
<string name="renderer_accuracy_medium">Balanced</string>
<string name="renderer_accuracy_high">Accurate</string>
<!-- DMA Accuracy -->
+2 -2
View File
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-FileCopyrightText: Copyright 2025 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 {:#x} in_data size {:#x}",
LOG_TRACE(Service_Audio, "header size {:#X} input data size 0x{:X} in_data size 0x{: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={:#x}, expected_size={}",
"Invalid mix count from dirty parameter: count={}, magic=0x{:X}, expected_size={}",
mix_count, in_dirty_params->magic, in_header->mix_size);
return Service::Audio::ResultInvalidUpdateInfo;
}
+6 -9
View File
@@ -1,6 +1,3 @@
// 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
@@ -46,13 +43,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: {:#x}, va_limit: {:#x}",
"Trying to map a block past the VA limit: virt_end: 0x{:X}, va_limit: 0x{: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: {:#x}", virt_end);
ASSERT_MSG(false, "Trying to map a block before the VA start: virt_end: 0x{:X}", virt_end);
}
auto block_end_predecessor{std::prev(block_end_successor)};
@@ -127,7 +124,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: {:#x}", block_start_successor->virt);
ASSERT_MSG(false, "Unsorted block in AS map: virt: 0x{: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));
@@ -153,13 +150,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: {:#x}, va_limit: {:#x}",
"Trying to map a block past the VA limit: virt_end: 0x{:X}, va_limit: 0x{: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: {:#x}",
ASSERT_MSG(false, "Trying to unmap a block before the VA start: virt_end: 0x{:X}",
virt_end);
}
@@ -260,7 +257,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: {:#x}", block_start_successor->virt);
ASSERT_MSG(false, "Unsorted block in AS map: virt: 0x{: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
+164 -286
View File
@@ -7,25 +7,18 @@
#include <vector>
#include "common/assert.h"
#include "common/bit_util.h"
#include "common/fs/file.h"
#include "common/fs/fs.h"
#include "common/fs/fs_types.h"
#ifdef __ANDROID__
#include "common/fs/fs_android.h"
#endif
#include "common/logging.h"
#include "common/literals.h"
#ifdef _WIN32
#include <io.h>
#include <share.h>
#include <windows.h>
#else
#include <unistd.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
#endif
#ifdef _MSC_VER
@@ -43,13 +36,13 @@ namespace {
#ifdef _WIN32
/**
* Converts the file access mode and file type enums to a file access mode wide string.
*
* @param mode File access mode
* @param type File type
*
* @returns A pointer to a wide string representing the file access mode.
*/
* Converts the file access mode and file type enums to a file access mode wide string.
*
* @param mode File access mode
* @param type File type
*
* @returns A pointer to a wide string representing the file access mode.
*/
[[nodiscard]] constexpr const wchar_t* AccessModeToWStr(FileAccessMode mode, FileType type) {
switch (type) {
case FileType::BinaryFile:
@@ -86,12 +79,12 @@ namespace {
}
/**
* Converts the file-share access flag enum to a Windows defined file-share access flag.
*
* @param flag File-share access flag
*
* @returns Windows defined file-share access flag.
*/
* Converts the file-share access flag enum to a Windows defined file-share access flag.
*
* @param flag File-share access flag
*
* @returns Windows defined file-share access flag.
*/
[[nodiscard]] constexpr int ToWindowsFileShareFlag(FileShareFlag flag) {
switch (flag) {
case FileShareFlag::ShareNone:
@@ -109,13 +102,13 @@ namespace {
#else
/**
* Converts the file access mode and file type enums to a file access mode string.
*
* @param mode File access mode
* @param type File type
*
* @returns A pointer to a string representing the file access mode.
*/
* Converts the file access mode and file type enums to a file access mode string.
*
* @param mode File access mode
* @param type File type
*
* @returns A pointer to a string representing the file access mode.
*/
[[nodiscard]] constexpr const char* AccessModeToStr(FileAccessMode mode, FileType type) {
switch (type) {
case FileType::BinaryFile:
@@ -154,12 +147,12 @@ namespace {
#endif
/**
* Converts the seek origin enum to a seek origin integer.
*
* @param origin Seek origin
*
* @returns Seek origin integer.
*/
* Converts the seek origin enum to a seek origin integer.
*
* @param origin Seek origin
*
* @returns Seek origin integer.
*/
[[nodiscard]] constexpr int ToSeekOrigin(SeekOrigin origin) {
switch (origin) {
case SeekOrigin::SetOrigin:
@@ -185,7 +178,7 @@ std::string ReadStringFromFile(const std::filesystem::path& path, FileType type)
}
size_t WriteStringToFile(const std::filesystem::path& path, FileType type,
std::string_view string) {
std::string_view string) {
if (Exists(path) && !IsFile(path)) {
return 0;
}
@@ -196,7 +189,7 @@ size_t WriteStringToFile(const std::filesystem::path& path, FileType type,
}
size_t AppendStringToFile(const std::filesystem::path& path, FileType type,
std::string_view string) {
std::string_view string) {
if (Exists(path) && !IsFile(path)) {
return 0;
}
@@ -251,189 +244,69 @@ FileType IOFile::GetType() const {
return file_type;
}
#if defined(__unix__)
static int PlatformMapReadOnly(IOFile& io, const char* path) {
io.mmap_fd = open(path, O_RDONLY);
if (io.mmap_fd > 0) {
struct stat st;
fstat(io.mmap_fd, &st);
io.mmap_size = st.st_size;
int map_flags = MAP_PRIVATE;
#ifdef MAP_PREFAULT_READ
// Prefaults reads so the final resulting pagetable from this big stupid mmap()
// isn't comically lazily loaded, we just coalesce everything in-place for our
// lovely mmap flags; if we didn't prefault the reads the page table will be
// constructed in-place (i.e on a read-by-read basis) causing lovely soft-faults
// which would nuke any performance gains.
//
// This of course incurs a cost in the initial mmap(2) call, but that is fine.
map_flags |= MAP_PREFAULT_READ;
#endif
#ifdef MAP_NOSYNC
// This causes physical media to not be synched to our file/memory
// This means that if the read-only file is written to, we won't see changes
// or we may see changes which are just funnily scattered, in any case
// this presumes the files won't be changed during execution
//
// Do not ever use this on write files (if we ever support that); this will create
// a fun amount of fragmentation on the disk.
map_flags |= MAP_NOSYNC;
#endif
#if defined(NDEBUG) && defined(MAP_NOCORE)
map_flags |= MAP_NOCORE;
#endif
#ifdef MAP_HUGE_2MB
map_flags |= MAP_HUGE_2MB; //2mb pages
#elif defined(MAP_ALIGNED)
// File must be big enough that it's worth to super align. We can't just super-align every
// file otherwise we will run out of alignments for actually important files :)
// System doesn't guarantee a super alignment, but if it's available it will delete
// about 3 layers(?) of the TLB tree for each read/write.
// Again the cost of faults may make this negligible gains, but hey, we gotta work
// what we gotta work with.
map_flags |= MAP_ALIGNED(21); //2^21 = 2mb use 2MB pages
#endif
io.mmap_base = (u8*)mmap(nullptr, io.mmap_size, PROT_READ, map_flags, io.mmap_fd, 0);
if (io.mmap_base == MAP_FAILED) {
close(io.mmap_fd);
io.mmap_fd = -1;
} else {
using namespace Common::Literals;
// For small files it is acceptable to use a full readahead
// See https://github.com/torvalds/linux/blob/e80d033851b3bc94c3d254ac66660ddd0a49d72c/include/linux/pagemap.h#L1392
if (u64(st.st_size) >= 256_MiB) {
posix_madvise(io.mmap_base, io.mmap_size, POSIX_MADV_RANDOM);
} else {
posix_madvise(io.mmap_base, io.mmap_size, POSIX_MADV_SEQUENTIAL);
}
}
}
return io.mmap_fd;
}
static void PlatformUnmap(IOFile& io) {
if (io.mmap_fd != -1) {
munmap(io.mmap_base, io.mmap_size);
close(io.mmap_fd);
io.mmap_fd = -1;
}
}
#elif defined(__APPLE__)
// NO IMPLEMENTATION YET
#else
static int PlatformMapReadOnly(IOFile& io, const wchar_t* path) {
io.file_handle = CreateFileW(path, GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, nullptr);
if (HANDLE(io.file_handle) != INVALID_HANDLE_VALUE) {
io.mapping_handle = CreateFileMappingW(HANDLE(io.file_handle), nullptr, PAGE_READONLY, 0, 0, nullptr);
if (io.mapping_handle) {
io.mmap_base = (u8*)MapViewOfFile(HANDLE(io.mapping_handle), FILE_MAP_READ, 0, 0, 0);
if (io.mmap_base) {
_LARGE_INTEGER pvalue;
GetFileSizeEx(io.file_handle, &pvalue);
io.mmap_size = uint32_t(pvalue.QuadPart);
} else {
CloseHandle(io.mapping_handle);
CloseHandle(io.file_handle);
return -1;
}
} else {
CloseHandle(io.file_handle);
return -1;
}
}
return 0;
}
static void PlatformUnmap(IOFile& io) {
if(io.mapping_handle) {
if(io.mmap_base)
UnmapViewOfFile(HANDLE(io.mmap_base));
CloseHandle(HANDLE(io.mapping_handle));
}
if(io.file_handle != INVALID_HANDLE_VALUE)
CloseHandle(HANDLE(io.file_handle));
}
#endif
void IOFile::Open(const fs::path& path, FileAccessMode mode, FileType type, FileShareFlag flag) {
Close();
file_path = path;
file_access_mode = mode;
file_type = type;
errno = 0;
#ifdef _WIN32
// TODO: this probably can use better logic but oh well I'm not a windowser
file_handle = nullptr;
if (type == FileType::BinaryFile && mode == FileAccessMode::Read) {
if (PlatformMapReadOnly(*this, path.c_str()) == -1) {
LOG_ERROR(Common_Filesystem, "Error mmap'ing file"); //: {}", path.c_str());
}
}
if (file_handle == nullptr) {
if (flag != FileShareFlag::ShareNone) {
file = _wfsopen(path.c_str(), AccessModeToWStr(mode, type), ToWindowsFileShareFlag(flag));
} else {
_wfopen_s(&file, path.c_str(), AccessModeToWStr(mode, type));
}
if (flag != FileShareFlag::ShareNone) {
file = _wfsopen(path.c_str(), AccessModeToWStr(mode, type), ToWindowsFileShareFlag(flag));
} else {
_wfopen_s(&file, path.c_str(), AccessModeToWStr(mode, type));
}
#elif __ANDROID__
if (Android::IsContentUri(path)) {
ASSERT_MSG(mode == FileAccessMode::Read, "Content URI file access is for read-only!");
if (PlatformMapReadOnly(*this, path.c_str()) == -1) {
LOG_ERROR(Common_Filesystem, "Error mmap'ing file: {}", path.c_str());
int const fd = Android::OpenContentUri(path, Android::OpenMode::Read);
if (fd != -1) {
file = fdopen(fd, "r");
if (errno != 0 && file == nullptr)
LOG_ERROR(Common_Filesystem, "Error opening file: {}, error: {}", path.c_str(), strerror(errno));
} else {
LOG_ERROR(Common_Filesystem, "Error opening file: {}", path.c_str());
const auto fd = Android::OpenContentUri(path, Android::OpenMode::Read);
if (fd != -1) {
file = fdopen(fd, "r");
const auto error_num = errno;
if (error_num != 0 && file == nullptr) {
LOG_ERROR(Common_Filesystem, "Error opening file: {}, error: {}", path.c_str(),
strerror(error_num));
}
} else {
LOG_ERROR(Common_Filesystem, "Error opening file: {}", path.c_str());
}
} else {
file = std::fopen(path.c_str(), AccessModeToStr(mode, type));
}
#elif defined(__HAIKU__) || defined(__managarm__) || defined(__OPENORBIS__) || defined(__APPLE__)
file = std::fopen(path.c_str(), AccessModeToStr(mode, type));
#elif defined(__unix__)
if (type == FileType::BinaryFile && mode == FileAccessMode::Read) {
if (PlatformMapReadOnly(*this, path.c_str()) == -1) {
LOG_ERROR(Common_Filesystem, "Error mmap'ing file: {}", path.c_str());
}
}
if (mmap_fd == -1) {
file = std::fopen(path.c_str(), AccessModeToStr(mode, type)); // mmap(2) failed or simply we can't use it
}
#else
// Some other fancy OS (ahem fucking Darwin/Mac OSX)
file = std::fopen(path.c_str(), AccessModeToStr(mode, type));
#endif
if (!IsOpen()) {
const auto ec = std::error_code{errno, std::generic_category()};
LOG_ERROR(Common_Filesystem, "Failed to open the file at path={}, ec_message={}",
PathToUTF8String(file_path), ec.message());
PathToUTF8String(file_path), ec.message());
}
}
void IOFile::Close() {
#if defined(__APPLE__)
// NO IMPLEMENTATION YET
#else
PlatformUnmap(*this);
#endif
if (file) {
errno = 0;
const auto close_result = std::fclose(file) == 0;
if (!close_result) {
const auto ec = std::error_code{errno, std::generic_category()};
LOG_ERROR(Common_Filesystem, "Failed to close the file at path={}, ec_message={}",
PathToUTF8String(file_path), ec.message());
}
file = nullptr;
if (!IsOpen()) {
return;
}
errno = 0;
const auto close_result = std::fclose(file) == 0;
if (!close_result) {
const auto ec = std::error_code{errno, std::generic_category()};
LOG_ERROR(Common_Filesystem, "Failed to close the file at path={}, ec_message={}",
PathToUTF8String(file_path), ec.message());
}
file = nullptr;
}
bool IOFile::IsOpen() const {
return file != nullptr || IsMappedFile();
return file != nullptr;
}
std::string IOFile::ReadString(size_t length) const {
@@ -450,132 +323,137 @@ size_t IOFile::WriteString(std::span<const char> string) const {
}
bool IOFile::Flush() const {
ASSERT(!IsMappedFile());
if (file) {
errno = 0;
auto const flush_result = std::fflush(file) == 0;
if (!flush_result) {
const auto ec = std::error_code{errno, std::generic_category()};
LOG_ERROR(Common_Filesystem, "Failed to flush the file at path={}, ec_message={}",
PathToUTF8String(file_path), ec.message());
}
return flush_result;
if (!IsOpen()) {
return false;
}
return false;
errno = 0;
#ifdef _WIN32
const auto flush_result = std::fflush(file) == 0;
#else
const auto flush_result = std::fflush(file) == 0;
#endif
if (!flush_result) {
const auto ec = std::error_code{errno, std::generic_category()};
LOG_ERROR(Common_Filesystem, "Failed to flush the file at path={}, ec_message={}",
PathToUTF8String(file_path), ec.message());
}
return flush_result;
}
bool IOFile::Commit() const {
ASSERT(!IsMappedFile());
if (file) {
errno = 0;
#ifdef _WIN32
const auto commit_result = std::fflush(file) == 0 && _commit(fileno(file)) == 0;
#else
const auto commit_result = std::fflush(file) == 0 && fsync(fileno(file)) == 0;
#endif
if (!commit_result) {
const auto ec = std::error_code{errno, std::generic_category()};
LOG_ERROR(Common_Filesystem, "Failed to commit the file at path={}, ec_message={}",
PathToUTF8String(file_path), ec.message());
}
return commit_result;
if (!IsOpen()) {
return false;
}
return false;
errno = 0;
#ifdef _WIN32
const auto commit_result = std::fflush(file) == 0 && _commit(fileno(file)) == 0;
#else
const auto commit_result = std::fflush(file) == 0 && fsync(fileno(file)) == 0;
#endif
if (!commit_result) {
const auto ec = std::error_code{errno, std::generic_category()};
LOG_ERROR(Common_Filesystem, "Failed to commit the file at path={}, ec_message={}",
PathToUTF8String(file_path), ec.message());
}
return commit_result;
}
bool IOFile::SetSize(u64 size) const {
ASSERT(!IsMappedFile());
if (file) {
errno = 0;
#ifdef _WIN32
const auto set_size_result = _chsize_s(fileno(file), s64(size)) == 0;
#else
const auto set_size_result = ftruncate(fileno(file), s64(size)) == 0;
#endif
if (!set_size_result) {
const auto ec = std::error_code{errno, std::generic_category()};
LOG_ERROR(Common_Filesystem, "Failed to resize the file at path={}, size={}, ec_message={}",
PathToUTF8String(file_path), size, ec.message());
}
return set_size_result;
if (!IsOpen()) {
return false;
}
return false;
errno = 0;
#ifdef _WIN32
const auto set_size_result = _chsize_s(fileno(file), static_cast<s64>(size)) == 0;
#else
const auto set_size_result = ftruncate(fileno(file), static_cast<s64>(size)) == 0;
#endif
if (!set_size_result) {
const auto ec = std::error_code{errno, std::generic_category()};
LOG_ERROR(Common_Filesystem, "Failed to resize the file at path={}, size={}, ec_message={}",
PathToUTF8String(file_path), size, ec.message());
}
return set_size_result;
}
u64 IOFile::GetSize() const {
if (IsMappedFile())
return mmap_size;
if (file) {
// Flush any unwritten buffered data into the file prior to retrieving the file mmap_size.
std::fflush(file);
#if __ANDROID__
u64 file_size = 0;
if (Android::IsContentUri(file_path)) {
file_size = Android::GetSize(file_path);
} else {
std::error_code ec;
if (!IsOpen()) {
return 0;
}
file_size = fs::file_size(file_path, ec);
// Flush any unwritten buffered data into the file prior to retrieving the file size.
std::fflush(file);
if (ec) {
LOG_ERROR(Common_Filesystem, "Failed to retrieve the file mmap_size of path={}, ec_message={}",
PathToUTF8String(file_path), ec.message());
return 0;
}
}
#else
#ifdef __ANDROID__
u64 file_size = 0;
if (Android::IsContentUri(file_path)) {
file_size = Android::GetSize(file_path);
} else {
std::error_code ec;
auto const file_size = fs::file_size(file_path, ec);
file_size = fs::file_size(file_path, ec);
if (ec) {
LOG_ERROR(Common_Filesystem, "Failed to retrieve the file mmap_size of path={}, ec_message={}",
PathToUTF8String(file_path), ec.message());
LOG_ERROR(Common_Filesystem,
"Failed to retrieve the file size of path={}, ec_message={}",
PathToUTF8String(file_path), ec.message());
return 0;
}
#endif
return file_size;
}
return 0;
#else
std::error_code ec;
const auto file_size = fs::file_size(file_path, ec);
if (ec) {
LOG_ERROR(Common_Filesystem, "Failed to retrieve the file size of path={}, ec_message={}",
PathToUTF8String(file_path), ec.message());
return 0;
}
#endif
return file_size;
}
bool IOFile::Seek(s64 offset, SeekOrigin origin) const {
if (IsMappedFile()) {
// fuck you to whoever made this method const
switch (origin) {
case SeekOrigin::SetOrigin:
mmap_offset = s64(offset);
break;
case SeekOrigin::CurrentPosition:
mmap_offset += s64(offset);
break;
case SeekOrigin::End:
mmap_offset = s64(mmap_size) + s64(offset);
break;
}
return true;
if (!IsOpen()) {
return false;
}
if (file) {
errno = 0;
const auto seek_result = fseeko(file, offset, ToSeekOrigin(origin)) == 0;
if (!seek_result) {
const auto ec = std::error_code{errno, std::generic_category()};
LOG_ERROR(Common_Filesystem, "Failed to seek the file at path={}, offset={}, origin={}, ec_message={}",
PathToUTF8String(file_path), offset, origin, ec.message());
}
return seek_result;
errno = 0;
const auto seek_result = fseeko(file, offset, ToSeekOrigin(origin)) == 0;
if (!seek_result) {
const auto ec = std::error_code{errno, std::generic_category()};
LOG_ERROR(Common_Filesystem,
"Failed to seek the file at path={}, offset={}, origin={}, ec_message={}",
PathToUTF8String(file_path), offset, origin, ec.message());
}
return false;
return seek_result;
}
s64 IOFile::Tell() const {
if (IsMappedFile()) {
errno = 0;
return s64(mmap_offset);
if (!IsOpen()) {
return 0;
}
if (file) {
errno = 0;
return ftello(file);
}
return 0;
errno = 0;
return ftello(file);
}
} // namespace Common::FS
+247 -255
View File
@@ -7,7 +7,6 @@
#pragma once
#include <cstdio>
#include <cstring>
#include <filesystem>
#include <span>
#include <type_traits>
@@ -25,12 +24,12 @@ enum class SeekOrigin {
};
/**
* Opens a file stream at path with the specified open mode.
*
* @param file_stream Reference to file stream
* @param path Filesystem path
* @param open_mode File stream open mode
*/
* Opens a file stream at path with the specified open mode.
*
* @param file_stream Reference to file stream
* @param path Filesystem path
* @param open_mode File stream open mode
*/
template <typename FileStream>
void OpenFileStream(FileStream& file_stream, const std::filesystem::path& path,
std::ios_base::openmode open_mode) {
@@ -49,14 +48,14 @@ void OpenFileStream(FileStream& file_stream, const Path& path, std::ios_base::op
#endif
/**
* Reads an entire file at path and returns a string of the contents read from the file.
* If the filesystem object at path is not a regular file, this function returns an empty string.
*
* @param path Filesystem path
* @param type File type
*
* @returns A string of the contents read from the file.
*/
* Reads an entire file at path and returns a string of the contents read from the file.
* If the filesystem object at path is not a regular file, this function returns an empty string.
*
* @param path Filesystem path
* @param type File type
*
* @returns A string of the contents read from the file.
*/
[[nodiscard]] std::string ReadStringFromFile(const std::filesystem::path& path, FileType type);
#ifdef _WIN32
@@ -71,18 +70,18 @@ template <typename Path>
#endif
/**
* Writes a string to a file at path and returns the number of characters successfully written.
* If a file already exists at path, its contents will be erased.
* If a file does not exist at path, it creates and opens a new empty file for writing.
* If the filesystem object at path exists and is not a regular file, this function returns 0.
*
* @param path Filesystem path
* @param type File type
*
* @returns Number of characters successfully written.
*/
* Writes a string to a file at path and returns the number of characters successfully written.
* If a file already exists at path, its contents will be erased.
* If a file does not exist at path, it creates and opens a new empty file for writing.
* If the filesystem object at path exists and is not a regular file, this function returns 0.
*
* @param path Filesystem path
* @param type File type
*
* @returns Number of characters successfully written.
*/
[[nodiscard]] size_t WriteStringToFile(const std::filesystem::path& path, FileType type,
std::string_view string);
std::string_view string);
#ifdef _WIN32
template <typename Path>
@@ -96,15 +95,15 @@ template <typename Path>
#endif
/**
* Appends a string to a file at path and returns the number of characters successfully written.
* If a file does not exist at path, it creates and opens a new empty file for appending.
* If the filesystem object at path exists and is not a regular file, this function returns 0.
*
* @param path Filesystem path
* @param type File type
*
* @returns Number of characters successfully written.
*/
* Appends a string to a file at path and returns the number of characters successfully written.
* If a file does not exist at path, it creates and opens a new empty file for appending.
* If the filesystem object at path exists and is not a regular file, this function returns 0.
*
* @param path Filesystem path
* @param type File type
*
* @returns Number of characters successfully written.
*/
[[nodiscard]] size_t AppendStringToFile(const std::filesystem::path& path, FileType type,
std::string_view string);
@@ -132,14 +131,14 @@ public:
FileShareFlag flag = FileShareFlag::ShareReadOnly);
/**
* An IOFile is a lightweight wrapper on C Library file operations.
* Automatically closes an open file on the destruction of an IOFile object.
*
* @param path Filesystem path
* @param mode File access mode
* @param type File type, default is BinaryFile. Use TextFile to open the file as a text file
* @param flag (Windows only) File-share access flag, default is ShareReadOnly
*/
* An IOFile is a lightweight wrapper on C Library file operations.
* Automatically closes an open file on the destruction of an IOFile object.
*
* @param path Filesystem path
* @param mode File access mode
* @param type File type, default is BinaryFile. Use TextFile to open the file as a text file
* @param flag (Windows only) File-share access flag, default is ShareReadOnly
*/
explicit IOFile(const std::filesystem::path& path, FileAccessMode mode,
FileType type = FileType::BinaryFile,
FileShareFlag flag = FileShareFlag::ShareReadOnly);
@@ -153,70 +152,84 @@ public:
IOFile& operator=(IOFile&& other) noexcept;
/**
* Gets the path of the file.
*
* @returns The path of the file.
*/
* Gets the path of the file.
*
* @returns The path of the file.
*/
[[nodiscard]] std::filesystem::path GetPath() const;
/**
* Gets the access mode of the file.
*
* @returns The access mode of the file.
*/
* Gets the access mode of the file.
*
* @returns The access mode of the file.
*/
[[nodiscard]] FileAccessMode GetAccessMode() const;
/**
* Gets the type of the file.
*
* @returns The type of the file.
*/
* Gets the type of the file.
*
* @returns The type of the file.
*/
[[nodiscard]] FileType GetType() const;
/**
* Opens a file at path with the specified file access mode.
* This function behaves differently depending on the FileAccessMode.
* These behaviors are documented in each enum value of FileAccessMode.
*
* @param path Filesystem path
* @param mode File access mode
* @param type File type, default is BinaryFile. Use TextFile to open the file as a text file
* @param flag (Windows only) File-share access flag, default is ShareReadOnly
*/
* Opens a file at path with the specified file access mode.
* This function behaves differently depending on the FileAccessMode.
* These behaviors are documented in each enum value of FileAccessMode.
*
* @param path Filesystem path
* @param mode File access mode
* @param type File type, default is BinaryFile. Use TextFile to open the file as a text file
* @param flag (Windows only) File-share access flag, default is ShareReadOnly
*/
void Open(const std::filesystem::path& path, FileAccessMode mode,
FileType type = FileType::BinaryFile,
FileShareFlag flag = FileShareFlag::ShareReadOnly);
FileType type = FileType::BinaryFile,
FileShareFlag flag = FileShareFlag::ShareReadOnly);
// #ifdef _WIN32
// template <typename Path>
// void Open(const Path& path, FileAccessMode mode, FileType type = FileType::BinaryFile,
// FileShareFlag flag = FileShareFlag::ShareReadOnly) {
// using ValueType = typename Path::value_type;
// if constexpr (IsChar<ValueType>) {
// Open(ToU8String(path), mode, type, flag);
// } else {
// Open(std::filesystem::path{path}, mode, type, flag);
// }
// }
// #endif
/// Closes the file if it is opened.
void Close();
/**
* Checks whether the file is open.
* Use this to check whether the calls to Open() or Close() succeeded.
*
* @returns True if the file is open, false otherwise.
*/
* Checks whether the file is open.
* Use this to check whether the calls to Open() or Close() succeeded.
*
* @returns True if the file is open, false otherwise.
*/
[[nodiscard]] bool IsOpen() const;
/**
* Helper function which deduces the value type of a contiguous STL container used in ReadSpan.
* If T is not a contiguous container as defined by the concept IsContiguousContainer, this
* calls ReadObject and T must be a trivially copyable object.
*
* See ReadSpan for more details if T is a contiguous container.
* See ReadObject for more details if T is a trivially copyable object.
*
* @tparam T Contiguous container or trivially copyable object
*
* @param data Container of T::value_type data or reference to object
*
* @returns Count of T::value_type data or objects successfully read.
*/
* Helper function which deduces the value type of a contiguous STL container used in ReadSpan.
* If T is not a contiguous container as defined by the concept IsContiguousContainer, this
* calls ReadObject and T must be a trivially copyable object.
*
* See ReadSpan for more details if T is a contiguous container.
* See ReadObject for more details if T is a trivially copyable object.
*
* @tparam T Contiguous container or trivially copyable object
*
* @param data Container of T::value_type data or reference to object
*
* @returns Count of T::value_type data or objects successfully read.
*/
template <typename T>
[[nodiscard]] size_t Read(T& data) const {
if constexpr (IsContiguousContainer<T>) {
using ContiguousType = typename T::value_type;
static_assert(std::is_trivially_copyable_v<ContiguousType>, "Data type must be trivially copyable.");
static_assert(std::is_trivially_copyable_v<ContiguousType>,
"Data type must be trivially copyable.");
return ReadSpan<ContiguousType>(data);
} else {
return ReadObject(data) ? 1 : 0;
@@ -224,24 +237,25 @@ public:
}
/**
* Helper function which deduces the value type of a contiguous STL container used in WriteSpan.
* If T is not a contiguous STL container as defined by the concept IsContiguousContainer, this
* calls WriteObject and T must be a trivially copyable object.
*
* See WriteSpan for more details if T is a contiguous container.
* See WriteObject for more details if T is a trivially copyable object.
*
* @tparam T Contiguous container or trivially copyable object
*
* @param data Container of T::value_type data or const reference to object
*
* @returns Count of T::value_type data or objects successfully written.
*/
* Helper function which deduces the value type of a contiguous STL container used in WriteSpan.
* If T is not a contiguous STL container as defined by the concept IsContiguousContainer, this
* calls WriteObject and T must be a trivially copyable object.
*
* See WriteSpan for more details if T is a contiguous container.
* See WriteObject for more details if T is a trivially copyable object.
*
* @tparam T Contiguous container or trivially copyable object
*
* @param data Container of T::value_type data or const reference to object
*
* @returns Count of T::value_type data or objects successfully written.
*/
template <typename T>
[[nodiscard]] size_t Write(const T& data) const {
if constexpr (IsContiguousContainer<T>) {
using ContiguousType = typename T::value_type;
static_assert(std::is_trivially_copyable_v<ContiguousType>, "Data type must be trivially copyable.");
static_assert(std::is_trivially_copyable_v<ContiguousType>,
"Data type must be trivially copyable.");
return WriteSpan<ContiguousType>(data);
} else {
static_assert(std::is_trivially_copyable_v<T>, "Data type must be trivially copyable.");
@@ -250,219 +264,197 @@ public:
}
/**
* Reads a span of T data from a file sequentially.
* This function reads from the current position of the file pointer and
* advances it by the (count of T * sizeof(T)) bytes successfully read.
*
* Failures occur when:
* - The file is not open
* - The opened file lacks read permissions
* - Attempting to read beyond the end-of-file
*
* @tparam T Data type
*
* @param data Span of T data
*
* @returns Count of T data successfully read.
*/
* Reads a span of T data from a file sequentially.
* This function reads from the current position of the file pointer and
* advances it by the (count of T * sizeof(T)) bytes successfully read.
*
* Failures occur when:
* - The file is not open
* - The opened file lacks read permissions
* - Attempting to read beyond the end-of-file
*
* @tparam T Data type
*
* @param data Span of T data
*
* @returns Count of T data successfully read.
*/
template <typename T>
requires std::is_trivially_copyable_v<T>
[[nodiscard]] size_t ReadSpan(std::span<T> data) const {
if (IsMappedFile()) {
std::memcpy(data.data(), mmap_base + mmap_offset, sizeof(T) * data.size());
return data.size();
if (!IsOpen()) {
return 0;
}
return IsOpen() ? std::fread(data.data(), sizeof(T), data.size(), file) : 0;
return std::fread(data.data(), sizeof(T), data.size(), file);
}
/**
* Writes a span of T data to a file sequentially.
* This function writes from the current position of the file pointer and
* advances it by the (count of T * sizeof(T)) bytes successfully written.
*
* Failures occur when:
* - The file is not open
* - The opened file lacks write permissions
*
* @tparam T Data type
*
* @param data Span of T data
*
* @returns Count of T data successfully written.
*/
* Writes a span of T data to a file sequentially.
* This function writes from the current position of the file pointer and
* advances it by the (count of T * sizeof(T)) bytes successfully written.
*
* Failures occur when:
* - The file is not open
* - The opened file lacks write permissions
*
* @tparam T Data type
*
* @param data Span of T data
*
* @returns Count of T data successfully written.
*/
template <typename T>
[[nodiscard]] size_t WriteSpan(std::span<const T> data) const {
static_assert(std::is_trivially_copyable_v<T>, "Data type must be trivially copyable.");
if (IsMappedFile()) {
std::memcpy(mmap_base + mmap_offset, data.data(), sizeof(T) * data.size());
return data.size();
if (!IsOpen()) {
return 0;
}
return IsOpen() ? std::fwrite(data.data(), sizeof(T), data.size(), file) : 0;
return std::fwrite(data.data(), sizeof(T), data.size(), file);
}
/**
* Reads a T object from a file sequentially.
* This function reads from the current position of the file pointer and
* advances it by the sizeof(T) bytes successfully read.
*
* Failures occur when:
* - The file is not open
* - The opened file lacks read permissions
* - Attempting to read beyond the end-of-file
*
* @tparam T Data type
*
* @param object Reference to object
*
* @returns True if the object is successfully read from the file, false otherwise.
*/
* Reads a T object from a file sequentially.
* This function reads from the current position of the file pointer and
* advances it by the sizeof(T) bytes successfully read.
*
* Failures occur when:
* - The file is not open
* - The opened file lacks read permissions
* - Attempting to read beyond the end-of-file
*
* @tparam T Data type
*
* @param object Reference to object
*
* @returns True if the object is successfully read from the file, false otherwise.
*/
template <typename T>
[[nodiscard]] bool ReadObject(T& object) const {
static_assert(std::is_trivially_copyable_v<T>, "Data type must be trivially copyable.");
static_assert(!std::is_pointer_v<T>, "T must not be a pointer to an object.");
if (IsMappedFile()) {
std::memcpy(&object, mmap_base + mmap_offset, sizeof(T));
#ifdef _WIN32
return bool(sizeof(T));
#else
return sizeof(T);
#endif
if (!IsOpen()) {
return false;
}
return IsOpen() ? std::fread(&object, sizeof(T), 1, file) == 1 : false;
return std::fread(&object, sizeof(T), 1, file) == 1;
}
/**
* Writes a T object to a file sequentially.
* This function writes from the current position of the file pointer and
* advances it by the sizeof(T) bytes successfully written.
*
* Failures occur when:
* - The file is not open
* - The opened file lacks write permissions
*
* @tparam T Data type
*
* @param object Const reference to object
*
* @returns True if the object is successfully written to the file, false otherwise.
*/
* Writes a T object to a file sequentially.
* This function writes from the current position of the file pointer and
* advances it by the sizeof(T) bytes successfully written.
*
* Failures occur when:
* - The file is not open
* - The opened file lacks write permissions
*
* @tparam T Data type
*
* @param object Const reference to object
*
* @returns True if the object is successfully written to the file, false otherwise.
*/
template <typename T>
[[nodiscard]] bool WriteObject(const T& object) const {
static_assert(std::is_trivially_copyable_v<T>, "Data type must be trivially copyable.");
static_assert(!std::is_pointer_v<T>, "T must not be a pointer to an object.");
if (IsMappedFile()) {
std::memcpy(mmap_base + mmap_offset, &object, sizeof(T));
#ifdef _WIN32
return sizeof(T) != 0;
#else
return sizeof(T);
#endif
if (!IsOpen()) {
return false;
}
return IsOpen() ? std::fwrite(&object, sizeof(T), 1, file) == 1 : false;
return std::fwrite(&object, sizeof(T), 1, file) == 1;
}
/**
* Specialized function to read a string of a given length from a file sequentially.
* This function writes from the current position of the file pointer and
* advances it by the number of characters successfully read.
* The size of the returned string may not match length if not all bytes are successfully read.
*
* @param length Length of the string
*
* @returns A string read from the file.
*/
* Specialized function to read a string of a given length from a file sequentially.
* This function writes from the current position of the file pointer and
* advances it by the number of characters successfully read.
* The size of the returned string may not match length if not all bytes are successfully read.
*
* @param length Length of the string
*
* @returns A string read from the file.
*/
[[nodiscard]] std::string ReadString(size_t length) const;
/**
* Specialized function to write a string to a file sequentially.
* This function writes from the current position of the file pointer and
* advances it by the number of characters successfully written.
*
* @param string Span of const char backed std::string or std::string_view
*
* @returns Number of characters successfully written.
*/
* Specialized function to write a string to a file sequentially.
* This function writes from the current position of the file pointer and
* advances it by the number of characters successfully written.
*
* @param string Span of const char backed std::string or std::string_view
*
* @returns Number of characters successfully written.
*/
[[nodiscard]] size_t WriteString(std::span<const char> string) const;
/**
* Attempts to flush any unwritten buffered data into the file.
*
* @returns True if the flush was successful, false otherwise.
*/
* Attempts to flush any unwritten buffered data into the file.
*
* @returns True if the flush was successful, false otherwise.
*/
bool Flush() const;
/**
* Attempts to commit the file into the disk.
* Note that this is an expensive operation as this forces the operating system to write
* the contents of the file associated with the file descriptor into the disk.
*
* @returns True if the commit was successful, false otherwise.
*/
* Attempts to commit the file into the disk.
* Note that this is an expensive operation as this forces the operating system to write
* the contents of the file associated with the file descriptor into the disk.
*
* @returns True if the commit was successful, false otherwise.
*/
bool Commit() const;
/**
* Resizes the file to a given size.
* If the file is resized to a smaller size, the remainder of the file is discarded.
* If the file is resized to a larger size, the new area appears as if zero-filled.
*
* Failures occur when:
* - The file is not open
*
* @param size File size in bytes
*
* @returns True if the file resize succeeded, false otherwise.
*/
* Resizes the file to a given size.
* If the file is resized to a smaller size, the remainder of the file is discarded.
* If the file is resized to a larger size, the new area appears as if zero-filled.
*
* Failures occur when:
* - The file is not open
*
* @param size File size in bytes
*
* @returns True if the file resize succeeded, false otherwise.
*/
[[nodiscard]] bool SetSize(u64 size) const;
/**
* Gets the size of the file.
*
* Failures occur when:
* - The file is not open
*
* @returns The file size in bytes of the file. Returns 0 on failure.
*/
* Gets the size of the file.
*
* Failures occur when:
* - The file is not open
*
* @returns The file size in bytes of the file. Returns 0 on failure.
*/
[[nodiscard]] u64 GetSize() const;
/**
* Moves the current position of the file pointer with the specified offset and seek origin.
*
* @param offset Offset from seek origin
* @param origin Seek origin
*
* @returns True if the file pointer has moved to the specified offset, false otherwise.
*/
* Moves the current position of the file pointer with the specified offset and seek origin.
*
* @param offset Offset from seek origin
* @param origin Seek origin
*
* @returns True if the file pointer has moved to the specified offset, false otherwise.
*/
[[nodiscard]] bool Seek(s64 offset, SeekOrigin origin = SeekOrigin::SetOrigin) const;
/**
* Gets the current position of the file pointer.
*
* @returns The current position of the file pointer.
*/
* Gets the current position of the file pointer.
*
* @returns The current position of the file pointer.
*/
[[nodiscard]] s64 Tell() const;
#ifdef _WIN32
inline bool IsMappedFile() const noexcept { return mapping_handle != nullptr; }
#else // POSIX
inline bool IsMappedFile() const noexcept { return mmap_fd != -1; }
#endif
private:
std::filesystem::path file_path;
FileAccessMode file_access_mode{};
FileType file_type{};
std::FILE* file = nullptr;
// Any decent system should have mmap() for files
// Systems with artifical mmap() limitations should simply change the logic within file.cpp
// and reduce the threshold for which the mmap() is set to
#ifdef _WIN32
void *mapping_handle = nullptr;
void *file_handle = nullptr;
#else // POSIX
int mmap_fd = -1;
#endif
u8* mmap_base = nullptr;
size_t mmap_size = 0;
mutable s64 mmap_offset = 0; // fuck you
};
} // namespace Common::FS
-23
View File
@@ -476,29 +476,6 @@ std::string SanitizePath(std::string_view path_, DirectorySeparator directory_se
path.erase(std::unique(start, path.end(),
[type2](char c1, char c2) { return c1 == type2 && c2 == type2; }),
path.end());
const bool absolute = !path.empty() && path[0] == type2;
std::vector<std::string_view> parts;
for (const auto part : SplitPathComponents(path))
{
if (part.empty() || part == ".")
continue;
if (part == ".." && !parts.empty() && parts.back() != "..")
parts.pop_back();
else if (part != "..") parts.push_back(part);
}
std::string resolved = absolute ? std::string(1, type2) : std::string{};
for (std::size_t i = 0; i < parts.size(); ++i)
{
if (i != 0)
resolved += type2;
resolved.append(parts[i].data(), parts[i].size());
}
path = std::move(resolved);
return std::string(RemoveTrailingSlash(path));
}
+1 -4
View File
@@ -1,6 +1,3 @@
// 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
@@ -15,7 +12,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 + 1 < str.size(); i += 2)
for (std::size_t i = 0; i < str.size(); i += 2)
out[i / 2] = (ToHexNibble(str[i]) << 4) | ToHexNibble(str[i + 1]);
}
return out;
+290 -1
View File
@@ -51,14 +51,60 @@
#endif // ^^^ POSIX ^^^
#include <atomic>
#include <mutex>
#include <random>
#include <vector>
#include "common/alignment.h"
#include "common/assert.h"
#include "common/free_region_manager.h"
#include "common/host_memory.h"
#include "common/logging.h"
#include "common/memory_detect.h"
#include "common/settings.h"
#ifdef __ANDROID__
#include <dlfcn.h>
#include <android/hardware_buffer.h>
namespace {
struct NativeHandle {
int version;
int numFds;
int numInts;
int data[1];
};
using PFN_AHardwareBuffer_getNativeHandle = const NativeHandle* (*)(const AHardwareBuffer*);
using PFN_AHardwareBuffer_isSupported = int (*)(const AHardwareBuffer_Desc*);
void* NativeWindowLibrary() {
static void* const lib = dlopen("libnativewindow.so", RTLD_NOW);
return lib;
}
PFN_AHardwareBuffer_getNativeHandle ResolveGetNativeHandle() {
void* const lib = NativeWindowLibrary();
if (lib == nullptr) {
return nullptr;
}
return reinterpret_cast<PFN_AHardwareBuffer_getNativeHandle>(
dlsym(lib, "AHardwareBuffer_getNativeHandle"));
}
PFN_AHardwareBuffer_isSupported ResolveIsSupported() {
void* const lib = NativeWindowLibrary();
if (lib == nullptr) {
return nullptr;
}
return reinterpret_cast<PFN_AHardwareBuffer_isSupported>(
dlsym(lib, "AHardwareBuffer_isSupported"));
}
} // namespace
#endif
#if defined(__ANDROID__) && __ANDROID_API__ < 30
#include <sys/syscall.h>
@@ -75,6 +121,12 @@ namespace Common {
[[maybe_unused]] constexpr size_t PageAlignment = 0x1000;
[[maybe_unused]] constexpr size_t HugePageSize = 0x200000;
static std::atomic<u64> committed_backing_size{};
u64 GetCommittedBackingSize() noexcept {
return committed_backing_size.load(std::memory_order_relaxed);
}
#ifdef _WIN32
// Manually imported for MinGW compatibility
@@ -509,6 +561,11 @@ public:
bool Init() {
long page_size = sysconf(_SC_PAGESIZE);
ASSERT_MSG(page_size == 0x1000, "page size {:#x} is incompatible with 4K paging", page_size);
#ifdef __ANDROID__
if (InitAhbBacking()) {
return InitVirtual();
}
#endif
// Backing memory initialization
#if defined(__sun__) || defined(__HAIKU__) || defined(__NetBSD__) || defined(__DragonFly__)
fd = shm_open_anon(O_RDWR | O_CREAT | O_EXCL | O_NOFOLLOW, 0600);
@@ -554,7 +611,10 @@ public:
return false;
}
// Virtual memory initialization
return InitVirtual();
}
bool InitVirtual() {
virtual_base = virtual_map_base = static_cast<u8*>(ChooseVirtualBase(virtual_size));
if (virtual_base == MAP_FAILED) {
LOG_CRITICAL(HW_Memory, "mmap failed: {}", strerror(errno));
@@ -567,6 +627,180 @@ public:
return true;
}
#ifdef __ANDROID__
static AHardwareBuffer_Desc MakeBlobDesc(size_t len) {
return AHardwareBuffer_Desc{
.width = static_cast<u32>(len),
.height = 1,
.layers = 1,
.format = AHARDWAREBUFFER_FORMAT_BLOB,
.usage = AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN |
AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN |
AHARDWAREBUFFER_USAGE_GPU_DATA_BUFFER,
.stride = 0,
.rfu0 = 0,
.rfu1 = 0,
};
}
static bool ProbeAhbBacking(PFN_AHardwareBuffer_getNativeHandle get_native_handle) {
const AHardwareBuffer_Desc desc = MakeBlobDesc(PageAlignment * 2);
AHardwareBuffer* buffer{};
if (AHardwareBuffer_allocate(&desc, &buffer) != 0 || buffer == nullptr) {
LOG_WARNING(HW_Memory, "Hardware buffer probe allocation failed");
return false;
}
const NativeHandle* const handle = get_native_handle(buffer);
if (handle == nullptr || handle->numFds < 1) {
LOG_WARNING(HW_Memory, "Hardware buffer has no mappable file descriptor");
AHardwareBuffer_release(buffer);
return false;
}
const int probe_fd = handle->data[0];
bool ok = true;
const auto try_map = [&](int prot, off_t offset, const char* what) {
if (!ok) {
return;
}
void* const ptr = mmap(nullptr, PageAlignment, prot, MAP_SHARED, probe_fd, offset);
if (ptr == MAP_FAILED) {
LOG_WARNING(HW_Memory, "Hardware buffer backing rejects {}: {}", what,
strerror(errno));
ok = false;
return;
}
munmap(ptr, PageAlignment);
};
try_map(PROT_READ | PROT_WRITE, 0, "shared mappings");
try_map(PROT_READ | PROT_WRITE, static_cast<off_t>(PageAlignment), "mappings at an offset");
#ifdef ARCHITECTURE_arm64
try_map(PROT_READ | PROT_EXEC, 0, "executable mappings");
#endif
AHardwareBuffer_release(buffer);
return ok;
}
bool InitAhbBacking() {
if (!Settings::values.use_unified_memory.GetValue()) {
return false;
}
static const PFN_AHardwareBuffer_getNativeHandle get_native_handle =
ResolveGetNativeHandle();
if (get_native_handle == nullptr) {
LOG_WARNING(HW_Memory, "AHardwareBuffer_getNativeHandle is not available");
return false;
}
static const PFN_AHardwareBuffer_isSupported is_supported = ResolveIsSupported();
if (is_supported == nullptr) {
LOG_WARNING(HW_Memory, "AHardwareBuffer_isSupported is not available");
return false;
}
const u64 total_physical = Common::GetMemInfo().TotalPhysicalMemory;
if (total_physical != 0 && backing_size > total_physical / 2) {
LOG_WARNING(HW_Memory,
"Hardware buffer backing would commit {} MiB on a {} MiB system, keeping "
"lazily committed memory",
backing_size >> 20, total_physical >> 20);
return false;
}
if (!ProbeAhbBacking(get_native_handle)) {
return false;
}
const auto try_window_size = [&](size_t window_size) -> bool {
const size_t num_windows = (backing_size + window_size - 1) / window_size;
std::vector<AHardwareBuffer*> buffers;
std::vector<int> buffer_fds;
const auto cleanup = [&] {
for (AHardwareBuffer* buffer : buffers) {
AHardwareBuffer_release(buffer);
}
buffers.clear();
buffer_fds.clear();
};
for (size_t i = 0; i < num_windows; ++i) {
const size_t len = (std::min)(window_size, backing_size - i * window_size);
const AHardwareBuffer_Desc desc = MakeBlobDesc(len);
AHardwareBuffer* buffer{};
if (AHardwareBuffer_allocate(&desc, &buffer) != 0 || buffer == nullptr) {
LOG_WARNING(HW_Memory, "Hardware buffer allocation failed for window {}", i);
cleanup();
return false;
}
buffers.push_back(buffer);
const NativeHandle* const handle = get_native_handle(buffer);
if (handle == nullptr || handle->numFds < 1) {
LOG_WARNING(HW_Memory, "Hardware buffer has no mappable file descriptor");
cleanup();
return false;
}
const int buffer_fd = handle->data[0];
const off_t buffer_len = lseek(buffer_fd, 0, SEEK_END);
if (buffer_len < static_cast<off_t>(len)) {
LOG_WARNING(HW_Memory, "Hardware buffer descriptor smaller than requested");
cleanup();
return false;
}
buffer_fds.push_back(buffer_fd);
}
u8* const base =
static_cast<u8*>(mmap(nullptr, backing_size, PROT_NONE,
MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE, -1, 0));
if (base == MAP_FAILED) {
cleanup();
return false;
}
for (size_t i = 0; i < num_windows; ++i) {
const size_t len = (std::min)(window_size, backing_size - i * window_size);
if (mmap(base + i * window_size, len, PROT_READ | PROT_WRITE,
MAP_SHARED | MAP_FIXED, buffer_fds[i], 0) == MAP_FAILED) {
LOG_WARNING(HW_Memory, "Hardware buffer mmap failed: {}", strerror(errno));
munmap(base, backing_size);
cleanup();
return false;
}
}
backing_base = base;
ahb_windows = std::move(buffers);
ahb_fds = std::move(buffer_fds);
ahb_window_size = window_size;
ahb_backing = true;
committed_backing_size.store(backing_size, std::memory_order_relaxed);
LOG_INFO(HW_Memory,
"Guest memory backed by {} hardware buffer windows of {} MiB, {} MiB committed",
ahb_windows.size(), window_size >> 20, backing_size >> 20);
return true;
};
static constexpr size_t candidate_window_sizes[] = {
1024ULL << 20,
512ULL << 20,
256ULL << 20,
128ULL << 20,
};
for (const size_t candidate : candidate_window_sizes) {
const AHardwareBuffer_Desc window_desc = MakeBlobDesc(candidate);
if (is_supported(&window_desc) == 0) {
LOG_DEBUG(HW_Memory, "Allocator rejects {} MiB hardware buffer windows",
candidate >> 20);
continue;
}
if (try_window_size(candidate)) {
return true;
}
LOG_WARNING(HW_Memory, "Could not back guest memory with {} MiB windows",
candidate >> 20);
}
return false;
}
std::span<AHardwareBuffer* const> AhbWindows() const noexcept {
return ahb_windows;
}
size_t AhbWindowSize() const noexcept {
return ahb_backing ? ahb_window_size : 0;
}
#endif
~Impl() {
Release();
}
@@ -587,6 +821,26 @@ public:
#ifdef ARCHITECTURE_arm64
if (True(perms & MemoryPermission::Execute))
prot_flags |= PROT_EXEC;
#endif
#ifdef __ANDROID__
if (ahb_backing) {
size_t voff = virtual_offset;
size_t hoff = host_offset;
size_t remaining = length;
while (remaining > 0) {
const size_t window = hoff / ahb_window_size;
const size_t local = hoff % ahb_window_size;
const size_t chunk = (std::min)(remaining, ahb_window_size - local);
void* const ret =
mmap(virtual_base + voff, chunk, prot_flags, MAP_SHARED | MAP_FIXED,
ahb_fds[window], static_cast<off_t>(local));
ASSERT_MSG(ret != MAP_FAILED, "mmap: {}", strerror(errno));
voff += chunk;
hoff += chunk;
remaining -= chunk;
}
return;
}
#endif
int flags = (fd >= 0 ? MAP_SHARED : MAP_PRIVATE) | MAP_FIXED;
void* ret = mmap(virtual_base + virtual_offset, length, prot_flags, flags, fd, host_offset);
@@ -656,6 +910,18 @@ private:
int ret = close(fd);
ASSERT_MSG(ret == 0, "close failed: {}", strerror(errno));
}
#ifdef __ANDROID__
for (AHardwareBuffer* buffer : ahb_windows) {
AHardwareBuffer_release(buffer);
}
ahb_windows.clear();
ahb_fds.clear();
if (ahb_backing) {
committed_backing_size.store(0, std::memory_order_relaxed);
ahb_backing = false;
}
#endif
}
void AdjustMap(size_t* virtual_offset, size_t* length) {
@@ -681,6 +947,13 @@ private:
int fd{-1}; // memfd file descriptor, -1 is the error value of memfd_create
FreeRegionManager free_manager{};
#ifdef __ANDROID__
bool ahb_backing{};
std::vector<AHardwareBuffer*> ahb_windows;
std::vector<int> ahb_fds;
size_t ahb_window_size{};
#endif
};
#endif // ^^^ POSIX ^^^
@@ -767,6 +1040,22 @@ void HostMemory::ClearBackingRegion(size_t physical_offset, size_t length, u32 f
std::memset(backing_base + physical_offset, fill_value, length);
}
std::span<AHardwareBuffer* const> HostMemory::BackingHardwareBuffers() const noexcept {
#ifdef __ANDROID__
return impl ? impl->AhbWindows() : std::span<AHardwareBuffer* const>{};
#else
return {};
#endif
}
size_t HostMemory::BackingHardwareBufferWindowSize() const noexcept {
#ifdef __ANDROID__
return impl ? impl->AhbWindowSize() : 0;
#else
return 0;
#endif
}
void HostMemory::EnableDirectMappedAddress() {
#if !(defined(__OPENORBIS__) || defined(__managarm__))
if (impl) {
+13
View File
@@ -8,12 +8,17 @@
#include <memory>
#include <optional>
#include <span>
#include "common/common_funcs.h"
#include "common/common_types.h"
#include "common/virtual_buffer.h"
struct AHardwareBuffer;
namespace Common {
[[nodiscard]] u64 GetCommittedBackingSize() noexcept;
enum class MemoryPermission : u32 {
Read = 1 << 0,
Write = 1 << 1,
@@ -62,6 +67,14 @@ public:
return backing_base;
}
[[nodiscard]] size_t BackingSize() const noexcept {
return backing_size;
}
[[nodiscard]] std::span<AHardwareBuffer* const> BackingHardwareBuffers() const noexcept;
[[nodiscard]] size_t BackingHardwareBufferWindowSize() const noexcept;
[[nodiscard]] u8* VirtualBasePointer() noexcept {
return virtual_base;
}
+13
View File
@@ -587,6 +587,9 @@ struct Values {
SwitchableSetting<bool> use_asynchronous_shaders{linkage, false, "use_asynchronous_shaders",
Category::RendererHacks};
SwitchableSetting<bool> use_unified_memory{linkage, false, "use_unified_memory",
Category::RendererHacks};
SwitchableSetting<GpuUnswizzleSize> gpu_unswizzle_texture_size{linkage,
GpuUnswizzleSize::Large,
"gpu_unswizzle_texture_size",
@@ -635,6 +638,16 @@ struct Values {
#endif
"vertex_input_dynamic_state", Category::RendererExtensions};
SwitchableSetting<bool> dynamic_rendering{linkage, true, "dynamic_rendering",
Category::RendererExtensions};
SwitchableSetting<bool> workgroup_memory_explicit_layout{
linkage, false, "workgroup_memory_explicit_layout", Category::RendererExtensions};
SwitchableSetting<s32, true> pipeline_worker_count{
linkage, 2, 2, 8, "pipeline_worker_count", Category::RendererAdvanced,
Specialization::Scalar};
Setting<bool> renderer_debug{linkage, false, "debug", Category::RendererDebug};
Setting<bool> renderer_shader_feedback{linkage, false, "shader_feedback",
Category::RendererDebug};
+140 -21
View File
@@ -1,5 +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
@@ -39,6 +40,110 @@
#include <unistd.h>
#endif
#ifdef __ANDROID__
#include <sys/resource.h>
#include <algorithm>
#include <fstream>
#include <utility>
#include <vector>
namespace {
[[maybe_unused]] constexpr int ANDROID_THREAD_PRIORITY_URGENT_AUDIO = -19;
[[maybe_unused]] constexpr int ANDROID_THREAD_PRIORITY_AUDIO = -16;
[[maybe_unused]] constexpr int ANDROID_THREAD_PRIORITY_URGENT_DISPLAY = -8;
[[maybe_unused]] constexpr int ANDROID_THREAD_PRIORITY_DISPLAY = -4;
[[maybe_unused]] constexpr int ANDROID_THREAD_PRIORITY_FOREGROUND = -2;
[[maybe_unused]] constexpr int ANDROID_THREAD_PRIORITY_MORE_FAVORABLE = -1;
[[maybe_unused]] constexpr int ANDROID_THREAD_PRIORITY_DEFAULT = 0;
[[maybe_unused]] constexpr int ANDROID_THREAD_PRIORITY_LESS_FAVORABLE = 1;
[[maybe_unused]] constexpr int ANDROID_THREAD_PRIORITY_BACKGROUND = 10;
[[maybe_unused]] constexpr int ANDROID_THREAD_PRIORITY_LOWEST = 19;
constexpr size_t ANDROID_MINIMUM_PERFORMANCE_CORES = 4;
cpu_set_t ComputePerformanceCoreMask() {
cpu_set_t mask;
CPU_ZERO(&mask);
cpu_set_t allowed;
CPU_ZERO(&allowed);
if (sched_getaffinity(gettid(), sizeof(allowed), &allowed) != 0) {
return mask;
}
std::vector<std::pair<long, int>> cores;
const int total = static_cast<int>(std::thread::hardware_concurrency());
for (int cpu = 0; cpu < total; ++cpu) {
if (!CPU_ISSET(cpu, &allowed)) {
continue;
}
long max_frequency = 0;
std::ifstream file("/sys/devices/system/cpu/cpu" + std::to_string(cpu) +
"/cpufreq/cpuinfo_max_freq");
if (!file || !(file >> max_frequency) || max_frequency <= 0) {
CPU_ZERO(&mask);
return mask;
}
cores.emplace_back(max_frequency, cpu);
}
if (cores.empty()) {
return mask;
}
std::sort(cores.begin(), cores.end(),
[](const auto& lhs, const auto& rhs) { return lhs.first > rhs.first; });
size_t taken = 0;
long cluster_frequency = cores.front().first;
for (const auto& [frequency, cpu] : cores) {
if (frequency != cluster_frequency) {
if (taken >= ANDROID_MINIMUM_PERFORMANCE_CORES) {
break;
}
cluster_frequency = frequency;
}
CPU_SET(cpu, &mask);
++taken;
}
return mask;
}
const cpu_set_t& PerformanceCoreMask() {
static const cpu_set_t mask = ComputePerformanceCoreMask();
return mask;
}
cpu_set_t ComputeEfficiencyCoreMask() {
cpu_set_t mask;
CPU_ZERO(&mask);
const cpu_set_t& performance = PerformanceCoreMask();
if (CPU_COUNT(&performance) == 0) {
return mask;
}
cpu_set_t allowed;
CPU_ZERO(&allowed);
if (sched_getaffinity(gettid(), sizeof(allowed), &allowed) != 0) {
return mask;
}
const int total = static_cast<int>(std::thread::hardware_concurrency());
for (int cpu = 0; cpu < total; ++cpu) {
if (CPU_ISSET(cpu, &allowed) && !CPU_ISSET(cpu, &performance)) {
CPU_SET(cpu, &mask);
}
}
return mask;
}
const cpu_set_t& EfficiencyCoreMask() {
static const cpu_set_t mask = ComputeEfficiencyCoreMask();
return mask;
}
} // Anonymous namespace
#endif
#include "common/cpu_features.h"
#ifdef ARCHITECTURE_x86_64
#ifdef _MSC_VER
@@ -78,6 +183,21 @@ void SetCurrentThreadPriority(ThreadPriority new_priority) {
}
}();
set_thread_priority(find_thread(NULL), priority);
#elif defined(__ANDROID__)
const int nice_value = [&]() {
switch (new_priority) {
case ThreadPriority::Low: return ANDROID_THREAD_PRIORITY_BACKGROUND;
case ThreadPriority::Normal: return ANDROID_THREAD_PRIORITY_DEFAULT;
case ThreadPriority::High: return ANDROID_THREAD_PRIORITY_DISPLAY;
case ThreadPriority::VeryHigh: return ANDROID_THREAD_PRIORITY_URGENT_DISPLAY;
case ThreadPriority::Critical: return ANDROID_THREAD_PRIORITY_AUDIO;
default: return ANDROID_THREAD_PRIORITY_DEFAULT;
}
}();
if (setpriority(PRIO_PROCESS, static_cast<id_t>(gettid()), nice_value) != 0) {
LOG_DEBUG(Common, "Could not set thread nice value to {}: {}", nice_value,
GetLastErrorMsg());
}
#else
pthread_t this_thread = pthread_self();
const auto scheduling_type = SCHED_OTHER;
@@ -132,29 +252,28 @@ void SetCurrentThreadName(const char* name) {
#endif
}
void PinCurrentThreadToPerformanceCore(size_t core_id) {
ASSERT(core_id < 4);
// If we set a flag for a CPU that doesn't exist, the thread may not be allowed to
// run in ANY processor!
auto const total_cores = std::thread::hardware_concurrency();
if (core_id < total_cores) {
void SetCurrentThreadToPerformanceCores() {
#if defined(__ANDROID__)
cpu_set_t set;
CPU_ZERO(&set);
CPU_SET(core_id, &set);
sched_setaffinity(pthread_self(), sizeof(set), &set);
#elif defined(__linux__) || defined(__FreeBSD__)
cpu_set_t set;
CPU_ZERO(&set);
CPU_SET(core_id, &set);
pthread_setaffinity_np(pthread_self(), sizeof(set), &set);
#elif defined(_WIN32)
DWORD set = 1UL << core_id;
SetThreadAffinityMask(GetCurrentThread(), set);
#else
// No pin functionality implemented
#endif
const cpu_set_t& mask = PerformanceCoreMask();
if (CPU_COUNT(&mask) == 0) {
return;
}
if (sched_setaffinity(gettid(), sizeof(mask), &mask) != 0) {
LOG_DEBUG(Common, "Could not restrict thread to performance cores: {}", GetLastErrorMsg());
}
#endif
}
void SetCurrentThreadToEfficiencyCores() {
#if defined(__ANDROID__)
const cpu_set_t& mask = EfficiencyCoreMask();
if (CPU_COUNT(&mask) == 0) {
return;
}
if (sched_setaffinity(gettid(), sizeof(mask), &mask) != 0) {
LOG_DEBUG(Common, "Could not restrict thread to efficiency cores: {}", GetLastErrorMsg());
}
#endif
}
#ifdef ARCHITECTURE_x86_64
+7 -1
View File
@@ -99,8 +99,14 @@ enum class ThreadPriority : u32 {
Critical = 4,
};
enum class ThreadPlacement : u32 {
Default = 0,
Background = 1,
};
void SetCurrentThreadPriority(ThreadPriority new_priority);
void SetCurrentThreadName(const char* name);
void PinCurrentThreadToPerformanceCore(size_t core_id);
void SetCurrentThreadToPerformanceCores();
void SetCurrentThreadToEfficiencyCores();
} // namespace Common
+8 -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 2020 yuzu Emulator Project
@@ -37,10 +37,15 @@ class StatefulThreadWorker {
using StateMaker = std::conditional_t<with_state, std::function<StateType()>, DummyCallable>;
public:
explicit StatefulThreadWorker(size_t num_workers, std::string name, StateMaker func = {})
explicit StatefulThreadWorker(size_t num_workers, std::string name, StateMaker func = {},
ThreadPlacement placement = ThreadPlacement::Default)
: workers_queued{num_workers}, thread_name{std::move(name)} {
const auto lambda = [this, func](std::stop_token stop_token) {
const auto lambda = [this, func, placement](std::stop_token stop_token) {
Common::SetCurrentThreadName(thread_name.c_str());
if (placement == ThreadPlacement::Background) {
Common::SetCurrentThreadPriority(ThreadPriority::Low);
Common::SetCurrentThreadToEfficiencyCores();
}
{
[[maybe_unused]] std::conditional_t<with_state, StateType, int> state{func()};
while (!stop_token.stop_requested()) {
+3 -1
View File
@@ -157,6 +157,8 @@ bool ArmNce::HandleGuestAlignmentFault(GuestContext* guest_ctx, void* raw_info,
return HandleFailedGuestFault(guest_ctx, raw_info, raw_context);
}
constexpr size_t NCE_WRITE_FAULT_CLUSTER_PAGES = 4;
bool ArmNce::HandleGuestAccessFault(GuestContext* guest_ctx, void* raw_info, void* raw_context) {
auto* info = static_cast<siginfo_t*>(raw_info);
@@ -165,7 +167,7 @@ bool ArmNce::HandleGuestAccessFault(GuestContext* guest_ctx, void* raw_info, voi
const Common::ProcessAddress addr =
(reinterpret_cast<u64>(info->si_addr) & ~Memory::YUZU_PAGEMASK);
auto& memory = guest_ctx->parent->m_running_thread->GetOwnerProcess()->GetMemory();
if (memory.InvalidateNCE(addr, Memory::YUZU_PAGESIZE)) {
if (memory.InvalidateNCE(addr, Memory::YUZU_PAGESIZE * NCE_WRITE_FAULT_CLUSTER_PAGES)) {
// We handled the access successfully and are returning to guest code.
return true;
}
+1 -18
View File
@@ -145,14 +145,7 @@ bool Patcher::PatchText(std::span<const u8> program_image, const Kernel::CodeSet
// MRS Xn, CNTFRQ_EL0
if (auto mrs = MRS{inst}; mrs.Verify() && mrs.GetSystemReg() == CntfrqEl0) {
bool pre_buffer = false;
auto ret = AddRelocations(pre_buffer);
if (pre_buffer) {
WriteCntfrqHandler(ret, oaknut::XReg{static_cast<int>(mrs.GetRt())}, c_pre);
} else {
WriteCntfrqHandler(ret, oaknut::XReg{static_cast<int>(mrs.GetRt())}, c);
}
continue;
UNREACHABLE();
}
// MSR TPIDR_EL0, Xn
@@ -584,16 +577,6 @@ void Patcher::WriteMsrHandler(ModuleDestLabel module_dest, oaknut::XReg src_reg,
this->BranchToModule(module_dest);
}
void Patcher::WriteCntfrqHandler(ModuleDestLabel module_dest, oaknut::XReg dest_reg, oaknut::VectorCodeGenerator& cg) {
cg.MOV(dest_reg, Common::WallClock::CNTFRQ);
// Jump back to the instruction after the emulated MRS.
if (&cg == &c_pre)
this->BranchToModulePre(module_dest);
else
this->BranchToModule(module_dest);
}
void Patcher::WriteCntpctHandler(ModuleDestLabel module_dest, oaknut::XReg dest_reg, oaknut::VectorCodeGenerator& cg) {
#if defined(HAS_NCE)
static Common::WallClock clock(false, 1);
-2
View File
@@ -80,7 +80,6 @@ private:
void WriteSvcTrampoline(ModuleDestLabel module_dest, u32 svc_id, oaknut::VectorCodeGenerator& code, oaknut::Label& save_ctx, oaknut::Label& load_ctx);
void WriteMrsHandler(ModuleDestLabel module_dest, oaknut::XReg dest_reg, oaknut::SystemReg src_reg, oaknut::VectorCodeGenerator& code);
void WriteMsrHandler(ModuleDestLabel module_dest, oaknut::XReg src_reg, oaknut::VectorCodeGenerator& code);
void WriteCntfrqHandler(ModuleDestLabel module_dest, oaknut::XReg dest_reg, oaknut::VectorCodeGenerator& code);
void WriteCntpctHandler(ModuleDestLabel module_dest, oaknut::XReg dest_reg, oaknut::VectorCodeGenerator& code);
// Convenience wrappers using default code generator
@@ -91,7 +90,6 @@ private:
void WriteSvcTrampoline(ModuleDestLabel module_dest, u32 svc_id) { WriteSvcTrampoline(module_dest, svc_id, c, m_save_context, m_load_context); }
void WriteMrsHandler(ModuleDestLabel module_dest, oaknut::XReg dest_reg, oaknut::SystemReg src_reg) { WriteMrsHandler(module_dest, dest_reg, src_reg, c); }
void WriteMsrHandler(ModuleDestLabel module_dest, oaknut::XReg src_reg) { WriteMsrHandler(module_dest, src_reg, c); }
void WriteCntfrqHandler(ModuleDestLabel module_dest, oaknut::XReg dest_reg) { WriteCntfrqHandler(module_dest, dest_reg, c); }
void WriteCntpctHandler(ModuleDestLabel module_dest, oaknut::XReg dest_reg) { WriteCntpctHandler(module_dest, dest_reg, c); }
private:
+10 -4
View File
@@ -118,6 +118,7 @@ struct System::Impl {
is_multicore = Settings::values.use_multi_core.GetValue();
extended_memory_layout = Settings::values.memory_layout_mode.GetValue() != Settings::MemoryLayout::Memory_4Gb;
unified_memory = Settings::values.use_unified_memory.GetValue();
core_timing.SetMulticore(is_multicore);
core_timing.Initialize([&system]() { system.RegisterHostThread(); });
@@ -145,7 +146,8 @@ struct System::Impl {
!device_memory.has_value() ||
is_multicore != Settings::values.use_multi_core.GetValue() ||
extended_memory_layout != (Settings::values.memory_layout_mode.GetValue() !=
Settings::MemoryLayout::Memory_4Gb);
Settings::MemoryLayout::Memory_4Gb) ||
unified_memory != Settings::values.use_unified_memory.GetValue();
if (!must_reinitialize) {
return;
@@ -156,6 +158,7 @@ struct System::Impl {
is_multicore = Settings::values.use_multi_core.GetValue();
extended_memory_layout =
Settings::values.memory_layout_mode.GetValue() != Settings::MemoryLayout::Memory_4Gb;
unified_memory = Settings::values.use_unified_memory.GetValue();
Initialize(system);
}
@@ -264,7 +267,9 @@ 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) {
@@ -291,7 +296,9 @@ 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);
@@ -338,8 +345,6 @@ struct System::Impl {
ShutdownMainProcess();
return init_result;
}
// Waiting for GPU before initializing CPU
cpu_manager.Initialize();
// Initialize cheat engine
if (cheat_engine) {
@@ -501,6 +506,7 @@ struct System::Impl {
std::atomic_bool is_powered_on{};
bool is_multicore : 1 = false;
bool extended_memory_layout : 1 = false;
bool unified_memory : 1 = false;
bool exit_locked : 1 = false;
bool exit_requested : 1 = false;
bool nvdec_active : 1 = false;
+1 -6
View File
@@ -174,12 +174,7 @@ void CpuManager::RunThread(std::stop_token token, std::size_t core) {
std::string name = is_multicore ? ("CPUCore_" + std::to_string(core)) : std::string{"CPUThread"};
Common::SetCurrentThreadName(name.c_str());
Common::SetCurrentThreadPriority(Common::ThreadPriority::Critical);
#ifdef __ANDROID__
// Aimed specifically for Snapdragon 8 Elite devices
// This kills performance on desktop, but boosts perf for UMA devices
// like the S8E. Mediatek and Mali likely won't suffer.
Common::PinCurrentThreadToPerformanceCore(core);
#endif
Common::SetCurrentThreadToPerformanceCores();
auto& data = core_data[core];
data.host_context = Common::Fiber::ThreadToFiber();
+30
View File
@@ -20,6 +20,8 @@
#include "common/scratch_buffer.h"
#include "common/virtual_buffer.h"
struct AHardwareBuffer;
namespace Core {
constexpr size_t DEVICE_PAGEBITS = 12ULL;
@@ -95,6 +97,26 @@ public:
ApplyOpOnPAddr(address, buffer, operation);
}
u8* GetPhysicalBase() noexcept {
return reinterpret_cast<u8*>(physical_base);
}
const u8* GetPhysicalBase() const noexcept {
return reinterpret_cast<const u8*>(physical_base);
}
size_t GetPhysicalSize() const noexcept {
return physical_size;
}
std::span<AHardwareBuffer* const> GetBackingHardwareBuffers() const noexcept {
return ahb_windows;
}
size_t GetBackingHardwareBufferWindowSize() const noexcept {
return ahb_window_size;
}
PAddr GetPhysicalRawAddressFromDAddr(DAddr address) const {
PAddr subbits = PAddr(address & page_mask);
auto paddr = tracked_entries[(address >> page_bits)].compressed_physical_ptr;
@@ -126,6 +148,10 @@ public:
// New batch API to update multiple ranges with a single lock acquisition.
void UpdatePagesCachedBatch(std::span<const std::pair<DAddr, size_t>> ranges, s32 delta);
void UpdateTexturePagesCount(DAddr addr, size_t size, s32 delta);
[[nodiscard]] bool IsRegionTextureCached(DAddr addr, size_t size) const noexcept;
private:
struct TranslationEntry {
DAddr guest_page{};
@@ -171,6 +197,9 @@ private:
std::unique_ptr<DeviceMemoryManagerAllocator<Traits>> impl;
const uintptr_t physical_base;
const size_t physical_size;
const std::span<AHardwareBuffer* const> ahb_windows;
const size_t ahb_window_size;
DeviceInterface* device_inter;
struct TrackedEntry {
@@ -234,6 +263,7 @@ private:
(1ULL << (device_virtual_bits - page_bits)) / subentries;
using CachedPages = std::array<CounterEntry, num_counter_entries>;
std::unique_ptr<CachedPages> cached_pages;
std::unique_ptr<CachedPages> texture_cached_pages;
Common::RangeMutex counter_guard;
std::mutex mapping_guard;
+30 -4
View File
@@ -171,12 +171,16 @@ struct DeviceMemoryManagerAllocator {
template <typename Traits>
DeviceMemoryManager<Traits>::DeviceMemoryManager(const DeviceMemory& device_memory_)
: physical_base{uintptr_t(device_memory_.buffer.BackingBasePointer())}
, physical_size{device_memory_.buffer.BackingSize()}
, ahb_windows{device_memory_.buffer.BackingHardwareBuffers()}
, ahb_window_size{device_memory_.buffer.BackingHardwareBufferWindowSize()}
, device_inter{nullptr}
, compressed_device_addr(1ULL << ((Settings::values.memory_layout_mode.GetValue() == Settings::MemoryLayout::Memory_4Gb ? physical_min_bits : physical_max_bits) - Memory::YUZU_PAGEBITS))
, tracked_entries(device_as_size >> Memory::YUZU_PAGEBITS)
{
impl = std::make_unique<DeviceMemoryManagerAllocator<Traits>>();
cached_pages = std::make_unique<CachedPages>();
texture_cached_pages = std::make_unique<CachedPages>();
const size_t total_virtual = device_as_size >> Memory::YUZU_PAGEBITS;
for (size_t i = 0; i < total_virtual; i++) {
@@ -431,7 +435,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 @ {:#016x} (start address = {:#016x}, size = {})",
"Unmapped Device ReadBlock @ 0x{:016X} (start address = 0x{:016X}, size = {})",
current_vaddr, address, size);
std::memset(dest_pointer, 0, copy_amount);
},
@@ -450,7 +454,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 @ {:#016x} (start address = {:#016x}, size = {})",
"Unmapped Device WriteBlock @ 0x{:016X} (start address = 0x{:016X}, size = {})",
current_vaddr, address, size);
},
[&](size_t copy_amount, u8* const dst_ptr) {
@@ -489,7 +493,7 @@ void DeviceMemoryManager<Traits>::ReadBlockUnsafe(DAddr address, void* dest_poin
[&](size_t copy_amount, DAddr current_vaddr) {
LOG_ERROR(
HW_Memory,
"Unmapped Device ReadBlock @ {:#016x} (start address = {:#016x}, size = {})",
"Unmapped Device ReadBlock @ 0x{:016X} (start address = 0x{:016X}, size = {})",
current_vaddr, address, size);
std::memset(dest_pointer, 0, copy_amount);
},
@@ -509,7 +513,7 @@ void DeviceMemoryManager<Traits>::WriteBlockUnsafe(DAddr address, const void* sr
[&](size_t copy_amount, DAddr current_vaddr) {
LOG_ERROR(
HW_Memory,
"Unmapped Device WriteBlock @ {:#016x} (start address = {:#016x}, size = {})",
"Unmapped Device WriteBlock @ 0x{:016X} (start address = 0x{:016X}, size = {})",
current_vaddr, address, size);
},
[&](size_t copy_amount, u8* const dst_ptr) {
@@ -625,6 +629,28 @@ void DeviceMemoryManager<Traits>::UpdatePagesCachedCount(DAddr addr, size_t size
UpdatePagesCachedCountNoLock(addr, size, delta);
}
template <typename Traits>
void DeviceMemoryManager<Traits>::UpdateTexturePagesCount(DAddr addr, size_t size, s32 delta) {
Common::ScopedRangeLock lk(counter_guard, addr, size);
const size_t page_end = Common::DivCeil(addr + size, Memory::YUZU_PAGESIZE);
for (size_t page = addr >> Memory::YUZU_PAGEBITS; page != page_end; ++page) {
CounterAtomicType& count = texture_cached_pages->at(page >> subentries_shift).Count(page);
count.fetch_add(static_cast<CounterType>(delta), std::memory_order_release);
}
}
template <typename Traits>
bool DeviceMemoryManager<Traits>::IsRegionTextureCached(DAddr addr, size_t size) const noexcept {
const size_t page_end = Common::DivCeil(addr + size, Memory::YUZU_PAGESIZE);
for (size_t page = addr >> Memory::YUZU_PAGEBITS; page != page_end; ++page) {
if (texture_cached_pages->at(page >> subentries_shift).Count(page).load(
std::memory_order_acquire) != 0) {
return true;
}
}
return false;
}
template <typename Traits>
void DeviceMemoryManager<Traits>::UpdatePagesCachedBatch(std::span<const std::pair<DAddr, size_t>> ranges, s32 delta) {
if (ranges.empty()) {
+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, "//") || StartsWith(patch_line, "#")) {
if (StartsWith(patch_line, "//")) {
continue;
}
@@ -304,7 +304,7 @@ void IPSwitchCompiler::Parse() {
if (print_values) {
LOG_INFO(Loader,
"[IPSwitchCompiler ('{}')] - Patching value at offset {:#08x} "
"[IPSwitchCompiler ('{}')] - Patching value at offset 0x{: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: {:#02x}", npdm_header.main_thread_priority);
LOG_DEBUG(Service_FS, "Main thread priority: 0x{: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: {:#02x}", npdm_header.flags);
LOG_DEBUG(Service_FS, "Flags: 0x{: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: {:#02x}", acid_header.flags);
LOG_DEBUG(Service_FS, "Flags: 0x{:02X}", acid_header.flags);
LOG_DEBUG(Service_FS, " > Is Retail: {}", acid_header.production_flag ? "YES" : "NO");
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);
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);
// Begin ACI0 printing (actual perms, unsigned)
LOG_DEBUG(Service_FS, "Magic: {:.4}", aci_header.magic.data());
LOG_DEBUG(Service_FS, "Title ID: {:#016x}", aci_header.title_id);
LOG_DEBUG(Service_FS, "Filesystem Access: {:#016x}\n", aci_file_access.permissions);
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);
}
} // namespace FileSys
-16
View File
@@ -984,22 +984,6 @@ bool RegisteredCache::RemoveExistingEntry(u64 title_id) const {
return removed_data;
}
bool RegisteredCache::Delete(const NcaID& id) const {
const auto path = GetRelativePathFromNcaID(id, false, true, false);
const bool is_file = dir->GetFileRelative(path) != nullptr;
const bool is_dir = dir->GetDirectoryRelative(path) != nullptr;
if (is_file) {
return dir->DeleteFile(path);
}
if (is_dir) {
return dir->DeleteSubdirectoryRecursive(path);
}
return true;
}
InstallResult RegisteredCache::RawInstallNCA(const NCA& nca, const VfsCopyFunction& copy,
bool overwrite_if_exists,
std::optional<NcaID> override_id) {
-1
View File
@@ -188,7 +188,6 @@ public:
// Removes an existing entry based on title id
bool RemoveExistingEntry(u64 title_id) const;
bool Delete(const NcaID& id) const;
private:
template <typename T>
@@ -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 '{}' ({:#016x}).", desc.name, title_id);
LOG_INFO(Service_FS, "Synthesizing system archive '{}' (0x{: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) {
+5 -17
View File
@@ -35,16 +35,6 @@ namespace {
constexpr size_t MaxOpenFiles = 8192;
bool IsWithinRoot(std::string_view root, std::string_view full_path) {
if (root.empty())
return true;
if (full_path.size() < root.size() || full_path.substr(0, root.size()) != root)
return false;
return full_path.size() == root.size() || full_path[root.size()] == '/' || full_path[root.size()] == '\\';
}
constexpr FS::FileAccessMode ModeFlagsToFileAccessMode(OpenMode mode) {
switch (mode) {
case OpenMode::Read:
@@ -413,8 +403,7 @@ RealVfsDirectory::~RealVfsDirectory() = default;
VirtualFile RealVfsDirectory::GetFileRelative(std::string_view relative_path) 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)) {
if (!FS::Exists(full_path) || FS::IsDir(full_path)) {
return nullptr;
}
return base.OpenFile(full_path, perms);
@@ -422,8 +411,7 @@ VirtualFile RealVfsDirectory::GetFileRelative(std::string_view relative_path) co
VirtualDir RealVfsDirectory::GetDirectoryRelative(std::string_view relative_path) 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)) {
if (!FS::Exists(full_path) || !FS::IsDir(full_path)) {
return nullptr;
}
return base.OpenDirectory(full_path, perms);
@@ -439,7 +427,7 @@ VirtualDir RealVfsDirectory::GetSubdirectory(std::string_view name) const {
VirtualFile RealVfsDirectory::CreateFileRelative(std::string_view relative_path) {
const auto full_path = FS::SanitizePath(path + '/' + std::string(relative_path));
if (!FS::CreateParentDirs(full_path) || !IsWithinRoot(FS::SanitizePath(path), full_path)) {
if (!FS::CreateParentDirs(full_path)) {
return nullptr;
}
return base.CreateFile(full_path, perms);
@@ -452,7 +440,7 @@ VirtualDir RealVfsDirectory::CreateDirectoryRelative(std::string_view relative_p
bool RealVfsDirectory::DeleteSubdirectoryRecursive(std::string_view name) {
const auto full_path = FS::SanitizePath(this->path + '/' + std::string(name));
return FS::RemoveDirRecursively(full_path);
return base.DeleteDirectory(full_path);
}
std::vector<VirtualFile> RealVfsDirectory::GetFiles() const {
@@ -518,7 +506,7 @@ VirtualFile RealVfsDirectory::CreateFile(std::string_view name) {
bool RealVfsDirectory::DeleteSubdirectory(std::string_view name) {
const std::string subdir_path = (path + '/').append(name);
return FS::RemoveDir(subdir_path);
return base.DeleteDirectory(subdir_path);
}
bool RealVfsDirectory::DeleteFile(std::string_view name) {
+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={:#08x}, activity={:#08x}", thread_handle,
LOG_DEBUG(Kernel_SVC, "called, handle=0x{:08X}, activity=0x{:08X}", thread_handle,
thread_activity);
// Validate the activity.
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-FileCopyrightText: Copyright 2025 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={:#x}, value={:#x}, timeout_ns={}",
LOG_TRACE(Kernel_SVC, "called, address={:#X}, arb_type=0x{:X}, value=0x{: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={:#x}, value={:#x}, count={:#x}",
LOG_TRACE(Kernel_SVC, "called, address={:#X}, signal_type=0x{:X}, value=0x{:X}, count=0x{: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={:#x}", address, size);
LOG_TRACE(Kernel_SVC, "called, address={:#X}, size=0x{: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={:#x}, address={:#x}, size={:#x}, "
"permission={:#x}",
"called, code_memory_handle={:#X}, operation=0x{:X}, address=0x{:X}, size=0x{:X}, "
"permission={:#X}",
code_memory_handle, operation, address, size, perm);
// Validate the address / size.
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-FileCopyrightText: Copyright 2025 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={:#08x}, timeout_ns={}", address,
LOG_TRACE(Kernel_SVC, "called address={:X}, cv_key={:X}, tag=0x{: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={:#08x}", cv_key, count);
LOG_TRACE(Kernel_SVC, "called, cv_key={:#X}, count=0x{: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={:#08x}", event_handle);
LOG_DEBUG(Kernel_SVC, "called, event_handle=0x{: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={:#08x} (ignored)",
LOG_WARNING(Kernel_SVC, "SignalEvent best-effort unknown handle=0x{: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={:#08x}", event_handle);
LOG_TRACE(Kernel_SVC, "called, event_handle=0x{:08X}", event_handle);
// Get the current handle table.
const auto& handle_table = GetCurrentProcess(system.Kernel()).GetHandleTable();
+10 -11
View File
@@ -4,7 +4,6 @@
// 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"
@@ -42,7 +41,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 + 1) % 32 == 0) {
if (i != 0 && i % 16 == 0) {
hexdump += '\n';
}
}
@@ -52,35 +51,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={:#016x}, info2={:#016x}", info1,
LOG_CRITICAL(Debug_Emulated, "Userspace PANIC! info1=0x{:016X}, info2=0x{:016X}", info1,
info2);
handle_debug_buffer(info1, info2);
break;
case BreakReason::Assert:
LOG_CRITICAL(Debug_Emulated, "Userspace Assertion failed! info1={:#016x}, info2={:#016x}",
LOG_CRITICAL(Debug_Emulated, "Userspace Assertion failed! info1=0x{:016X}, info2=0x{:016X}",
info1, info2);
handle_debug_buffer(info1, info2);
break;
case BreakReason::User:
LOG_WARNING(Debug_Emulated, "Userspace Break! {:#016x} with size {:#016x}", info1, info2);
LOG_WARNING(Debug_Emulated, "Userspace Break! 0x{:016X} with size 0x{:016X}", info1, info2);
handle_debug_buffer(info1, info2);
break;
case BreakReason::PreLoadDll:
LOG_INFO(Debug_Emulated,
"Userspace Attempting to load an NRO at {:#016x} with size {:#016x}", info1,
"Userspace Attempting to load an NRO at 0x{:016X} with size 0x{:016X}", info1,
info2);
break;
case BreakReason::PostLoadDll:
LOG_INFO(Debug_Emulated, "Userspace Loaded an NRO at {:#016x} with size {:#016x}", info1,
LOG_INFO(Debug_Emulated, "Userspace Loaded an NRO at 0x{:016X} with size 0x{:016X}", info1,
info2);
break;
case BreakReason::PreUnloadDll:
LOG_INFO(Debug_Emulated,
"Userspace Attempting to unload an NRO at {:#016x} with size {:#016x}", info1,
"Userspace Attempting to unload an NRO at 0x{:016X} with size 0x{:016X}", info1,
info2);
break;
case BreakReason::PostUnloadDll:
LOG_INFO(Debug_Emulated, "Userspace Unloaded an NRO at {:#016x} with size {:#016x}",
LOG_INFO(Debug_Emulated, "Userspace Unloaded an NRO at 0x{:016X} with size 0x{:016X}",
info1, info2);
break;
case BreakReason::CppException:
@@ -89,7 +88,7 @@ void Break(Core::System& system, BreakReason reason, u64 info1, u64 info2) {
default:
LOG_WARNING(
Debug_Emulated,
"Signalling debugger, Unknown break reason {:#x}, info1={:#016x}, info2={:#016x}",
"Signalling debugger, Unknown break reason {:#X}, info1=0x{:016X}, info2=0x{:016X}",
reason, info1, info2);
handle_debug_buffer(info1, info2);
break;
@@ -102,7 +101,7 @@ void Break(Core::System& system, BreakReason reason, u64 info1, u64 info2) {
if (!notification_only) {
LOG_CRITICAL(
Debug_Emulated,
"Emulated program broke execution! reason={:#016x}, info1={:#016x}, info2={:#016x}",
"Emulated program broke execution! reason=0x{:016X}, info1=0x{:016X}, info2=0x{: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={:#x}, handle={:#08x}",
LOG_TRACE(Kernel_SVC, "called info_id={:#X}, info_sub_id=0x{:X}, handle=0x{: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={:#016x}", info_id);
LOG_ERROR(Kernel_SVC, "Unimplemented svcGetInfo id=0x{: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={:#08x}",
LOG_ERROR(Kernel_SVC, "Thread handle does not exist, handle=0x{: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={:#016x}", info_id);
LOG_ERROR(Kernel_SVC, "Unimplemented svcGetInfo id=0x{:016X}", info_id);
R_THROW(ResultInvalidEnumValue);
}
}
+3 -3
View File
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-FileCopyrightText: Copyright 2025 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={:#08x}, address={:#x}, tag={:#08x}",
LOG_TRACE(Kernel_SVC, "called thread_handle=0x{:08X}, address={:#X}, tag=0x{: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);
+10 -10
View File
@@ -34,12 +34,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, {:#016X}", dst_addr);
LOG_ERROR(Kernel_SVC, "Destination address is not aligned to 4KB, 0x{: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, {:#016X}", src_addr);
LOG_ERROR(Kernel_SVC, "Source address is not aligned to 4KB, 0x{:016X}", src_addr);
R_THROW(ResultInvalidSize);
}
@@ -49,26 +49,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, {:#016X}", size);
LOG_ERROR(Kernel_SVC, "Size is not aligned to 4KB, 0x{:016X}", size);
R_THROW(ResultInvalidSize);
}
if (!IsValidAddressRange(dst_addr, size)) {
LOG_ERROR(Kernel_SVC,
"Destination is not a valid address range, addr={:#016x}, size={:#016x}",
"Destination is not a valid address range, addr=0x{:016X}, size=0x{:016X}",
dst_addr, size);
R_THROW(ResultInvalidCurrentMemory);
}
if (!IsValidAddressRange(src_addr, size)) {
LOG_ERROR(Kernel_SVC, "Source is not a valid address range, addr={:#016x}, size={:#016x}",
LOG_ERROR(Kernel_SVC, "Source is not a valid address range, addr=0x{:016X}, size=0x{: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={:#016x}, size={:#016x}",
"Source is not within the address space, addr=0x{:016X}, size=0x{:016X}",
src_addr, size);
R_THROW(ResultInvalidCurrentMemory);
}
@@ -79,7 +79,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={:#016x}, size={:#x}, perm={:#08x}", address, size,
LOG_DEBUG(Kernel_SVC, "called, address=0x{:016X}, size={:#X}, perm=0x{:08X}", address, size,
perm);
// Validate address / size.
@@ -101,7 +101,7 @@ Result SetMemoryPermission(Core::System& system, u64 address, u64 size, MemoryPe
Result SetMemoryAttribute(Core::System& system, u64 address, u64 size, u32 mask, u32 attr) {
LOG_DEBUG(Kernel_SVC,
"called, address={:#016x}, size={:#x}, mask={:#08x}, attribute={:#08x}", address,
"called, address=0x{:016X}, size={:#X}, mask=0x{:08X}, attribute=0x{:08X}", address,
size, mask, attr);
// Validate address / size.
@@ -132,7 +132,7 @@ 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={:#x}, size={:#x}", dst_addr,
LOG_TRACE(Kernel_SVC, "called, dst_addr={:#X}, src_addr=0x{:X}, size=0x{:X}", dst_addr,
src_addr, size);
auto& page_table{GetCurrentProcess(system.Kernel()).GetPageTable()};
@@ -147,7 +147,7 @@ Result MapMemory(Core::System& system, u64 dst_addr, u64 src_addr, u64 size) {
/// 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={:#x}, size={:#x}", dst_addr,
LOG_TRACE(Kernel_SVC, "called, dst_addr={:#X}, src_addr=0x{:X}, size=0x{:X}", dst_addr,
src_addr, size);
auto& page_table{GetCurrentProcess(system.Kernel()).GetPageTable()};
@@ -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={:#016x}, size={:#x}", addr, size);
LOG_DEBUG(Kernel_SVC, "called, addr=0x{:016X}, size={:#X}", addr, size);
if (!Common::IsAligned(addr, Core::Memory::YUZU_PAGESIZE)) {
LOG_ERROR(Kernel_SVC, "Address is not aligned to 4KB, {:#016X}", addr);
LOG_ERROR(Kernel_SVC, "Address is not aligned to 4KB, 0x{: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={:#016x}, size={:#016x}", addr,
"Address is not within the address space, addr=0x{:016X}, size=0x{:016X}", addr,
size);
R_THROW(ResultInvalidMemoryRegion);
}
if (!page_table.IsInAliasRegion(addr, size)) {
LOG_ERROR(Kernel_SVC,
"Address is not within the alias region, addr={:#016x}, size={:#016x}", addr,
"Address is not within the alias region, addr=0x{:016X}, size=0x{: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={:#016x}, size={:#x}", addr, size);
LOG_DEBUG(Kernel_SVC, "called, addr=0x{:016X}, size={:#X}", addr, size);
if (!Common::IsAligned(addr, Core::Memory::YUZU_PAGESIZE)) {
LOG_ERROR(Kernel_SVC, "Address is not aligned to 4KB, {:#016X}", addr);
LOG_ERROR(Kernel_SVC, "Address is not aligned to 4KB, 0x{: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={:#016x}, size={:#016x}", addr,
"Address is not within the address space, addr=0x{:016X}, size=0x{:016X}", addr,
size);
R_THROW(ResultInvalidMemoryRegion);
}
if (!page_table.IsInAliasRegion(addr, size)) {
LOG_ERROR(Kernel_SVC,
"Address is not within the alias region, addr={:#016x}, size={:#016x}", addr,
"Address is not within the alias region, addr=0x{:016X}, size=0x{:016X}", addr,
size);
R_THROW(ResultInvalidMemoryRegion);
}
+5 -5
View File
@@ -23,7 +23,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={:#08x}", handle);
LOG_DEBUG(Kernel_SVC, "called handle=0x{:08X}", handle);
// Get the object from the handle table.
KScopedAutoObject obj = GetCurrentProcess(system.Kernel())
@@ -55,7 +55,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={:#016x}, out_process_ids_size={}",
LOG_DEBUG(Kernel_SVC, "called. out_process_ids=0x{: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 +71,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={:#016x}, end={:#016x}",
LOG_ERROR(Kernel_SVC, "Address range outside address space. begin=0x{:016X}, end=0x{:016X}",
out_process_ids, out_process_ids + total_copy_size);
R_THROW(ResultInvalidCurrentMemory);
}
@@ -95,12 +95,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={:#08x}, type={:#x}", process_handle, info_type);
LOG_DEBUG(Kernel_SVC, "called, handle=0x{: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={:#08x}",
LOG_ERROR(Kernel_SVC, "Process handle does not exist, process_handle=0x{:08X}",
process_handle);
R_THROW(ResultInvalidHandle);
}
+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={:#x}, size={:#x}, permissions={:#08x}",
"called, process_handle={:#X}, addr=0x{:X}, size=0x{:X}, permissions=0x{: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={:#x}, src_address={:#x}, size={:#x}",
"called, dst_address={:#X}, process_handle=0x{:X}, src_address=0x{:X}, size=0x{: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={:#x}, src_address={:#x}, size={:#x}",
"called, dst_address={:#X}, process_handle=0x{:X}, src_address=0x{:X}, size=0x{: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={:#08x}, dst_address={:#016x}, "
"src_address={:#016x}, size={:#016x}",
"called. process_handle=0x{:08X}, dst_address=0x{:016X}, "
"src_address=0x{:016X}, size=0x{: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={:#016X}).",
LOG_ERROR(Kernel_SVC, "src_address is not page-aligned (src_address=0x{: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={:#016X}).",
LOG_ERROR(Kernel_SVC, "dst_address is not page-aligned (dst_address=0x{: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={:#016X})", size);
LOG_ERROR(Kernel_SVC, "Size is zero or not page-aligned (size=0x{:016X})", size);
R_THROW(ResultInvalidSize);
}
if (!IsValidAddressRange(dst_address, size)) {
LOG_ERROR(Kernel_SVC,
"Destination address range overflows the address space (dst_address={:#016x}, "
"size={:#016x}).",
"Destination address range overflows the address space (dst_address=0x{:016X}, "
"size=0x{: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={:#016x}, "
"size={:#016x}).",
"Source address range overflows the address space (src_address=0x{:016X}, "
"size=0x{: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={:#08x}).",
LOG_ERROR(Kernel_SVC, "Invalid process handle specified (handle=0x{: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={:#016x}, "
"size={:#016x}).",
"Source address range is not within the address space (src_address=0x{:016X}, "
"size=0x{: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={:#08x}, dst_address={:#016x}, src_address={:#016x}, "
"size={:#016x}",
"called. process_handle=0x{:08X}, dst_address=0x{:016X}, src_address=0x{:016X}, "
"size=0x{: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={:#016X}).",
LOG_ERROR(Kernel_SVC, "dst_address is not page-aligned (dst_address=0x{: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={:#016X}).",
LOG_ERROR(Kernel_SVC, "src_address is not page-aligned (src_address=0x{: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={:#016X}).", size);
LOG_ERROR(Kernel_SVC, "Size is zero or not page-aligned (size=0x{:016X}).", size);
R_THROW(ResultInvalidSize);
}
if (!IsValidAddressRange(dst_address, size)) {
LOG_ERROR(Kernel_SVC,
"Destination address range overflows the address space (dst_address={:#016x}, "
"size={:#016x}).",
"Destination address range overflows the address space (dst_address=0x{:016X}, "
"size=0x{: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={:#016x}, "
"size={:#016x}).",
"Source address range overflows the address space (src_address=0x{:016X}, "
"size=0x{: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={:#08x}).",
LOG_ERROR(Kernel_SVC, "Invalid process handle specified (handle=0x{: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={:#016x}, "
"size={:#016x}).",
"Source address range is not within the address space (src_address=0x{:016X}, "
"size=0x{: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={:#016x}, "
"query_address={:#016x}",
"called, out_memory_info=0x{:016X}, "
"query_address=0x{: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={:#08x} address={:X}", process_handle, address);
LOG_TRACE(Kernel_SVC, "called process=0x{: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={:#08x}",
LOG_ERROR(Kernel_SVC, "Process handle does not exist, process_handle=0x{: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={:#x}, size={:#x}, permissions={:#08x}",
"called, shared_memory_handle={:#X}, addr=0x{:X}, size=0x{:X}, permissions=0x{: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 {:#08x}", handle);
LOG_TRACE(Kernel_SVC, "Closing handle 0x{: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 {:#08x}", handle);
LOG_DEBUG(Kernel_SVC, "called handle 0x{: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={:#08x}, arg={:#08x}, stack_bottom={:#08x}, "
"priority={:#08x}, core_id={:#08x}",
"called entry_point=0x{:08X}, arg=0x{:08X}, stack_bottom=0x{:08X}, "
"priority=0x{:08X}, core_id=0x{: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={:#08x}", thread_handle);
LOG_DEBUG(Kernel_SVC, "called thread=0x{: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={:#08x}, thread_handle={:#x}", out_context, thread_handle);
LOG_DEBUG(Kernel_SVC, "called, out_context=0x{: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={:#016x}, out_thread_ids_size={}",
LOG_DEBUG(Kernel_SVC, "called. out_thread_ids=0x{: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={:#016x}, end={:#016x}",
LOG_ERROR(Kernel_SVC, "Address range outside address space. begin=0x{:016X}, end=0x{: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={:#08x}", thread_handle);
LOG_TRACE(Kernel_SVC, "called, handle=0x{: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,6 +41,7 @@ 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);
}
},
@@ -51,11 +52,29 @@ 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,6 +33,9 @@ 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,12 +26,13 @@ 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::jthread([this](std::stop_token stop_token) {
m_thread = std::thread([this] {
Common::SetCurrentThreadName("am:EventObserver");
while (!stop_token.stop_requested()) {
auto* signaled_holder = this->WaitSignaled(stop_token);
if (!signaled_holder)
while (true) {
auto* signaled_holder = this->WaitSignaled();
if (!signaled_holder) {
break;
}
this->Process(signaled_holder);
}
});
@@ -39,12 +40,9 @@ EventObserver::EventObserver(Core::System& system, WindowSystem& window_system)
EventObserver::~EventObserver() {
// Signal thread and wait for processing to finish.
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();
}
m_stop_source.request_stop();
m_wakeup_event.Signal(m_system.Kernel());
m_thread.join();
// Free remaining owned sessions.
auto it = m_process_holder_list.begin();
@@ -90,12 +88,12 @@ void EventObserver::LinkDeferred() {
m_multi_wait.MoveAll(std::addressof(m_deferred_wait_list));
}
MultiWaitHolder* EventObserver::WaitSignaled(std::stop_token stop_token) {
MultiWaitHolder* EventObserver::WaitSignaled() {
while (true) {
this->LinkDeferred();
// If we're done, return before we start waiting.
if (stop_token.stop_requested()) {
if (m_stop_source.stop_requested()) {
return nullptr;
}
@@ -133,6 +131,7 @@ 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()) {
@@ -157,6 +156,7 @@ 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;
}
+8 -2
View File
@@ -32,14 +32,19 @@ public:
private:
void LinkDeferred();
MultiWaitHolder* WaitSignaled(std::stop_token stop_token);
MultiWaitHolder* WaitSignaled();
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;
@@ -62,7 +67,8 @@ private:
MultiWait m_deferred_wait_list;
// Processing thread.
std::jthread m_thread{};
std::thread m_thread{};
std::stop_source m_stop_source{};
};
} // namespace Service::AM
@@ -57,7 +57,7 @@ ISelfController::ISelfController(Core::System& system_, std::shared_ptr<Applet>
{64, nullptr, "SetInputDetectionSourceSet"},
{65, D<&ISelfController::ReportUserIsActive>, "ReportUserIsActive"},
{66, nullptr, "GetCurrentIlluminance"},
{67, D<&ISelfController::IsIlluminanceAvailable>, "IsIlluminanceAvailable"},
{67, nullptr, "IsIlluminanceAvailable"},
{68, D<&ISelfController::SetAutoSleepDisabled>, "SetAutoSleepDisabled"},
{69, D<&ISelfController::IsAutoSleepDisabled>, "IsAutoSleepDisabled"},
{70, nullptr, "ReportMultimediaError"},
@@ -347,12 +347,6 @@ Result ISelfController::IsAutoSleepDisabled(Out<bool> out_is_auto_sleep_disabled
R_SUCCEED();
}
Result ISelfController::IsIlluminanceAvailable(Out<bool> out_is_illuminance_available) {
LOG_WARNING(Service_AM, "(stubbed)");
*out_is_illuminance_available = false;
R_SUCCEED();
}
Result ISelfController::SetInputDetectionPolicy(InputDetectionPolicy input_detection_policy) {
LOG_WARNING(Service_AM, "(STUBBED) called");
R_SUCCEED();
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
@@ -60,7 +60,6 @@ private:
Result ReportUserIsActive();
Result SetAutoSleepDisabled(bool is_auto_sleep_disabled);
Result IsAutoSleepDisabled(Out<bool> out_is_auto_sleep_disabled);
Result IsIlluminanceAvailable(Out<bool> out_is_illuminance_available);
Result SetInputDetectionPolicy(InputDetectionPolicy input_detection_policy);
Result GetAccumulatedSuspendedTickValue(Out<u64> out_accumulated_suspended_tick_value);
Result GetAccumulatedSuspendedTickChangedEvent(OutCopyHandle<Kernel::KReadableEvent> out_event);
@@ -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 {:#02x}!", result.GetDescription());
LOG_ERROR(Service_Audio, "RequestUpdate failed error 0x{: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}, storage={}, type={}",
LOG_INFO(Service_Capture, "called, application_id=0x{: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}, storage={}, type={}, flags={}",
LOG_INFO(Service_Capture, "called, application_id=0x{: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}, storage={}, type={}, flags={}",
LOG_INFO(Service_Capture, "called, application_id=0x{: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={:#08x}\n"
"Service={} (TIPC={} CmdType={} Cmd=0x{: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: {:#16X}\n"
"Program entry point: {:#16X}\n"
"Result: {:#X} ({:04}-{:04d})\n"
"Set flags: 0x{:16X}\n"
"Program entry point: 0x{: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: {:#016x}\n", info.unk10);
crash_report += fmt::format("Unknown 10: 0x{: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) {
+10 -42
View File
@@ -43,16 +43,6 @@ static FileSys::VirtualDir GetDirectoryRelativeWrapped(FileSys::VirtualDir base,
return base->GetDirectoryRelative(dir_name);
}
static std::string_view GetGuestParentPath(std::string_view path) {
const auto name_index = path.find_last_of("\\/");
return name_index == std::string_view::npos ? std::string_view{} : path.substr(0, name_index);
}
static std::string_view GetGuestFilename(std::string_view path) {
const auto name_index = path.find_last_of("\\/");
return name_index == std::string_view::npos ? path : path.substr(name_index + 1);
}
VfsDirectoryServiceWrapper::VfsDirectoryServiceWrapper(FileSys::VirtualDir backing_)
: backing(std::move(backing_)) {}
@@ -93,12 +83,11 @@ Result VfsDirectoryServiceWrapper::DeleteFile(const std::string& path_) const {
return ResultSuccess;
}
const auto filename = GetGuestFilename(path);
auto dir = GetDirectoryRelativeWrapped(backing, GetGuestParentPath(path));
if (filename.empty() || dir == nullptr || dir->GetFile(filename) == nullptr) {
auto dir = GetDirectoryRelativeWrapped(backing, Common::FS::GetParentPath(path));
if (dir == nullptr || dir->GetFile(Common::FS::GetFilename(path)) == nullptr) {
return FileSys::ResultPathNotFound;
}
if (!dir->DeleteFile(filename)) {
if (!dir->DeleteFile(Common::FS::GetFilename(path))) {
// TODO(DarkLordZach): Find a better error code for this
return ResultUnknown;
}
@@ -108,9 +97,7 @@ Result VfsDirectoryServiceWrapper::DeleteFile(const std::string& path_) const {
Result VfsDirectoryServiceWrapper::CreateDirectory(const std::string& path_) const {
std::string path(Common::FS::SanitizePath(path_));
if (GetDirectoryRelativeWrapped(backing, path) != nullptr) {
return FileSys::ResultPathAlreadyExists;
}
// NOTE: This is inaccurate behavior. CreateDirectory is not recursive.
// CreateDirectory should return PathNotFound if the parent directory does not exist.
// This is here temporarily in order to have UMM "work" in the meantime.
@@ -130,19 +117,8 @@ Result VfsDirectoryServiceWrapper::CreateDirectory(const std::string& path_) con
Result VfsDirectoryServiceWrapper::DeleteDirectory(const std::string& path_) const {
std::string path(Common::FS::SanitizePath(path_));
const auto dirname = GetGuestFilename(path);
auto dir = GetDirectoryRelativeWrapped(backing, GetGuestParentPath(path));
FileSys::VirtualDir target{};
if (!dirname.empty() && dir != nullptr) {
target = dir->GetSubdirectory(dirname);
}
if (target == nullptr) {
return FileSys::ResultPathNotFound;
}
if (!target->GetFiles().empty() || !target->GetSubdirectories().empty()) {
return ResultUnknown;
}
if (!dir->DeleteSubdirectory(dirname)) {
auto dir = GetDirectoryRelativeWrapped(backing, Common::FS::GetParentPath(path));
if (!dir->DeleteSubdirectory(Common::FS::GetFilename(path))) {
// TODO(DarkLordZach): Find a better error code for this
return ResultUnknown;
}
@@ -151,12 +127,8 @@ Result VfsDirectoryServiceWrapper::DeleteDirectory(const std::string& path_) con
Result VfsDirectoryServiceWrapper::DeleteDirectoryRecursively(const std::string& path_) const {
std::string path(Common::FS::SanitizePath(path_));
const auto dirname = GetGuestFilename(path);
auto dir = GetDirectoryRelativeWrapped(backing, GetGuestParentPath(path));
if (dirname.empty() || dir == nullptr || dir->GetSubdirectory(dirname) == nullptr) {
return FileSys::ResultPathNotFound;
}
if (!dir->DeleteSubdirectoryRecursive(dirname)) {
auto dir = GetDirectoryRelativeWrapped(backing, Common::FS::GetParentPath(path));
if (!dir->DeleteSubdirectoryRecursive(Common::FS::GetFilename(path))) {
// TODO(DarkLordZach): Find a better error code for this
return ResultUnknown;
}
@@ -165,13 +137,9 @@ Result VfsDirectoryServiceWrapper::DeleteDirectoryRecursively(const std::string&
Result VfsDirectoryServiceWrapper::CleanDirectoryRecursively(const std::string& path) const {
const std::string sanitized_path(Common::FS::SanitizePath(path));
const auto dirname = GetGuestFilename(sanitized_path);
auto dir = GetDirectoryRelativeWrapped(backing, GetGuestParentPath(sanitized_path));
auto dir = GetDirectoryRelativeWrapped(backing, Common::FS::GetParentPath(sanitized_path));
if (dirname.empty() || dir == nullptr || dir->GetSubdirectory(dirname) == nullptr) {
return FileSys::ResultPathNotFound;
}
if (!dir->CleanSubdirectoryRecursive(dirname)) {
if (!dir->CleanSubdirectoryRecursive(Common::FS::GetFilename(sanitized_path))) {
// TODO(DarkLordZach): Find a better error code for this
return ResultUnknown;
}
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-FileCopyrightText: Copyright 2025 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));
@@ -41,7 +41,7 @@ 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={:#08x}", path->str, option, size);
LOG_DEBUG(Service_FS, "called. file={}, option={:#X}, size=0x{:08X}", path->str, option, size);
R_RETURN(backend->CreateFile(FileSys::Path(path->str), size));
}
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-FileCopyrightText: Copyright 2025 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,7 +190,7 @@ FSP_SRV::~FSP_SRV() = default;
Result FSP_SRV::SetCurrentProcess(ClientProcessId pid) {
current_process_id = *pid;
LOG_DEBUG(Service_FS, "called. current_process_id={:#016x}", current_process_id);
LOG_DEBUG(Service_FS, "called. current_process_id=0x{:016X}", current_process_id);
R_RETURN(
fsc.OpenProcess(&program_id, &save_data_controller, &romfs_controller, current_process_id));
+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);
}
+3 -9
View File
@@ -237,7 +237,7 @@ IHidServer::IHidServer(Core::System& system_, std::shared_ptr<ResourceManager> r
{3013, nullptr, "SetDebugPadGenericPadMap"}, //21.0.0+
{3014, nullptr, "GetDebugPadKeyboardMap"}, //21.0.0+
{3015, nullptr, "SetDebugPadKeyboardMap"}, //21.0.0+
{3150, C<&IHidServer::SetMouseLibraryVersion>, "SetMouseLibraryVersion"}, //21.0.0+
{3150, nullptr, "SetMouseLibraryVersion"}, //21.0.0+
// What? -- {12010, nullptr, "SetButtonConfigLeft"},
};
// clang-format on
@@ -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={:#08x}, t_mem_2_size={:#08x}, "
"called, t_mem_1_size=0x{:08X}, t_mem_2_size=0x{:08X}, "
"applet_resource_user_id={}",
t_mem_1_size, t_mem_2_size, aruid.pid);
@@ -1471,12 +1471,6 @@ Result IHidServer::SetTouchScreenResolution(u32 width, u32 height,
R_SUCCEED();
}
Result IHidServer::SetMouseLibraryVersion(ClientAppletResourceUserId aruid) {
LOG_INFO(Service_HID, "(STUBBED) called, applet_resource_user_id={}", aruid.pid);
R_SUCCEED();
}
std::shared_ptr<ResourceManager> IHidServer::GetResourceManager() {
resource_manager->Initialize();
return resource_manager;
+1 -2
View File
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
@@ -263,7 +263,6 @@ private:
Result IsFirmwareUpdateNeededForNotification(Out<bool> out_is_firmware_update_needed,
s32 unknown, ClientAppletResourceUserId aruid);
Result SetTouchScreenResolution(u32 width, u32 height, ClientAppletResourceUserId aruid);
Result SetMouseLibraryVersion(ClientAppletResourceUserId aruid);
std::shared_ptr<ResourceManager> resource_manager;
std::shared_ptr<HidFirmwareSettings> firmware_settings;
+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={:#08x}", object_id);
LOG_DEBUG(IPC, "CloseVirtualHandle, object_id=0x{: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 @ {:#016x}", vaddr);
LOG_CRITICAL(Service_JIT, "plugin: unmapped read @ 0x{: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 @ {:#016x}", vaddr);
LOG_CRITICAL(Service_JIT, "plugin: unmapped write @ 0x{: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 {:#x}", pc);
LOG_CRITICAL(Service_JIT, "plugin issued syscall at unknown address 0x{: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}", result.raw);
LOG_ERROR(Service_Mii, "Mii database is corrupted 0x{:0x}", result.raw);
database.CleanDatabase();
return ResultSuccess;
}
+2 -297
View File
@@ -1,19 +1,9 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <algorithm>
#include <array>
#include <memory>
#include <vector>
#include "core/core.h"
#include "core/file_sys/registered_cache.h"
#include "core/file_sys/romfs_factory.h"
#include "core/hle/api_version.h"
#include "core/hle/service/filesystem/filesystem.h"
#include "core/hle/service/ipc_helpers.h"
#include "core/hle/service/ncm/ncm.h"
#include "core/hle/service/server_manager.h"
@@ -98,268 +88,6 @@ public:
}
};
class IContentStorage final : public ServiceFramework<IContentStorage> {
public:
explicit IContentStorage(Core::System& system_, FileSys::StorageId id)
: ServiceFramework{system_, "IContentStorage"}, storage{id} {
// clang-format off
static const FunctionInfo functions[] = {
{0, &IContentStorage::GeneratePlaceHolderId, "GeneratePlaceHolderId"},
{1, &IContentStorage::CreatePlaceHolder, "CreatePlaceHolder"},
{2, &IContentStorage::DeletePlaceHolder, "DeletePlaceHolder"},
{4, &IContentStorage::WritePlaceHolder, "WritePlaceHolder"},
{5, &IContentStorage::Register, "Register"},
{6, &IContentStorage::Delete, "Delete"},
};
// clang-format on
RegisterHandlers(functions);
}
private:
void GeneratePlaceHolderId(HLERequestContext& ctx) {
LOG_DEBUG(Service_NCM, "called");
IPC::ResponseBuilder rb{ctx, 6};
rb.Push(ResultSuccess);
rb.PushRaw(FileSys::PlaceholderCache::Generate());
}
void CreatePlaceHolder(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
[[maybe_unused]] FileSys::NcaID content_id{};
FileSys::NcaID placeholder_id{};
if constexpr (HLE::ApiVersion::HOS_VERSION_MAJOR >= 16) {
placeholder_id = rp.PopRaw<FileSys::NcaID>();
content_id = rp.PopRaw<FileSys::NcaID>();
} else {
content_id = rp.PopRaw<FileSys::NcaID>();
placeholder_id = rp.PopRaw<FileSys::NcaID>();
}
const auto size = rp.Pop<s64>();
auto* const placeholder_cache =
system.GetFileSystemController().GetPlaceholderCacheForStorage(storage);
const bool succeeded =
placeholder_cache != nullptr && size >= 0 &&
(placeholder_cache->Exists(placeholder_id) ||
placeholder_cache->Create(placeholder_id, static_cast<u64>(size)));
if (succeeded) {
LOG_DEBUG(Service_NCM, "called, storage_id={}, size={}", static_cast<u32>(storage),
size);
} else {
LOG_WARNING(Service_NCM, "failed, storage_id={}, size={}", static_cast<u32>(storage),
size);
}
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(succeeded ? ResultSuccess : ResultUnknown);
}
void DeletePlaceHolder(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto placeholder_id = rp.PopRaw<FileSys::NcaID>();
auto* const placeholder_cache =
system.GetFileSystemController().GetPlaceholderCacheForStorage(storage);
const bool succeeded =
placeholder_cache != nullptr &&
(!placeholder_cache->Exists(placeholder_id) || placeholder_cache->Delete(placeholder_id));
if (succeeded) {
LOG_DEBUG(Service_NCM, "called, storage_id={}", static_cast<u32>(storage));
} else {
LOG_WARNING(Service_NCM, "failed, storage_id={}", static_cast<u32>(storage));
}
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(succeeded ? ResultSuccess : ResultUnknown);
}
void WritePlaceHolder(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto placeholder_id = rp.PopRaw<FileSys::NcaID>();
const auto offset = rp.Pop<u64>();
const auto data = ctx.ReadBuffer();
auto* const placeholder_cache =
system.GetFileSystemController().GetPlaceholderCacheForStorage(storage);
const std::vector<u8> write_data{data.begin(), data.end()};
const bool succeeded =
placeholder_cache != nullptr &&
placeholder_cache->Write(placeholder_id, offset, write_data);
if (succeeded) {
LOG_DEBUG(Service_NCM, "called, storage_id={}, offset={}, size={}",
static_cast<u32>(storage), offset, data.size());
} else {
LOG_WARNING(Service_NCM, "failed, storage_id={}, offset={}, size={}",
static_cast<u32>(storage), offset, data.size());
}
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(succeeded ? ResultSuccess : ResultUnknown);
}
void Register(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
FileSys::NcaID content_id{};
FileSys::NcaID placeholder_id{};
if constexpr (HLE::ApiVersion::HOS_VERSION_MAJOR >= 16) {
placeholder_id = rp.PopRaw<FileSys::NcaID>();
content_id = rp.PopRaw<FileSys::NcaID>();
} else {
content_id = rp.PopRaw<FileSys::NcaID>();
placeholder_id = rp.PopRaw<FileSys::NcaID>();
}
auto& fsc = system.GetFileSystemController();
auto* const placeholder_cache = fsc.GetPlaceholderCacheForStorage(storage);
auto* const registered_cache = fsc.GetRegisteredCacheForStorage(storage);
const bool succeeded =
placeholder_cache != nullptr && registered_cache != nullptr &&
placeholder_cache->Register(registered_cache, placeholder_id, content_id);
if (succeeded) {
LOG_DEBUG(Service_NCM, "called, storage_id={}", static_cast<u32>(storage));
} else {
LOG_WARNING(Service_NCM, "failed, storage_id={}", static_cast<u32>(storage));
}
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(succeeded ? ResultSuccess : ResultUnknown);
}
void Delete(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto content_id = rp.PopRaw<FileSys::NcaID>();
auto* const registered_cache =
system.GetFileSystemController().GetRegisteredCacheForStorage(storage);
const bool succeeded = registered_cache != nullptr && registered_cache->Delete(content_id);
if (succeeded) {
registered_cache->Refresh();
}
if (succeeded) {
LOG_DEBUG(Service_NCM, "called, storage_id={}", static_cast<u32>(storage));
} else {
LOG_WARNING(Service_NCM, "failed, storage_id={}", static_cast<u32>(storage));
}
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(succeeded ? ResultSuccess : ResultUnknown);
}
FileSys::StorageId storage;
};
class IContentMetaDatabase final : public ServiceFramework<IContentMetaDatabase> {
public:
explicit IContentMetaDatabase(Core::System& system_, FileSys::StorageId id)
: ServiceFramework{system_, "IContentMetaDatabase"}, storage{id} {
// clang-format off
static const FunctionInfo functions[] = {
{0, &IContentMetaDatabase::Set, "Set"},
{2, &IContentMetaDatabase::Remove, "Remove"},
{8, &IContentMetaDatabase::Has, "Has"},
{15, &IContentMetaDatabase::Commit, "Commit"},
};
// clang-format on
RegisterHandlers(functions);
}
private:
struct ContentMetaKey {
u64 id;
u32 version;
FileSys::TitleType type;
u8 install_type;
std::array<u8, 2> padding;
};
static_assert(sizeof(ContentMetaKey) == 0x10);
void Set(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto key = rp.PopRaw<ContentMetaKey>();
const auto entry_matches = [&key](const ContentMetaKey& entry) {
return entry.id == key.id && entry.version == key.version && entry.type == key.type &&
entry.install_type == key.install_type;
};
if (std::find_if(entries.begin(), entries.end(), entry_matches) == entries.end()) {
entries.push_back(key);
}
LOG_DEBUG(Service_NCM,
"called, storage_id={}, title_id={:016X}, version={}, type={}, size={}",
static_cast<u32>(storage), key.id, key.version, static_cast<u8>(key.type),
ctx.GetReadBufferSize());
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
void Remove(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto key = rp.PopRaw<ContentMetaKey>();
std::erase_if(entries, [&key](const ContentMetaKey& entry) {
return entry.id == key.id && entry.version == key.version && entry.type == key.type &&
entry.install_type == key.install_type;
});
LOG_DEBUG(Service_NCM, "called, storage_id={}, title_id={:016X}, version={}, type={}",
static_cast<u32>(storage), key.id, key.version, static_cast<u8>(key.type));
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
void Has(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto key = rp.PopRaw<ContentMetaKey>();
const bool has_pending =
std::find_if(entries.begin(), entries.end(), [&key](const ContentMetaKey& entry) {
return entry.id == key.id && entry.version == key.version &&
entry.type == key.type && entry.install_type == key.install_type;
}) != entries.end();
auto* const registered_cache =
system.GetFileSystemController().GetRegisteredCacheForStorage(storage);
const bool has_registered =
registered_cache != nullptr &&
registered_cache->HasEntry(key.id, FileSys::ContentRecordType::Meta);
LOG_DEBUG(Service_NCM, "called, storage_id={}, title_id={:016X}, version={}, type={}, has={}",
static_cast<u32>(storage), key.id, key.version, static_cast<u8>(key.type),
has_pending || has_registered);
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
rb.Push(has_pending || has_registered);
}
void Commit(HLERequestContext& ctx) {
auto* const registered_cache =
system.GetFileSystemController().GetRegisteredCacheForStorage(storage);
if (registered_cache != nullptr) {
registered_cache->Refresh();
}
LOG_DEBUG(Service_NCM, "called, storage_id={}", static_cast<u32>(storage));
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
FileSys::StorageId storage;
std::vector<ContentMetaKey> entries;
};
class LR final : public ServiceFramework<LR> {
public:
explicit LR(Core::System& system_) : ServiceFramework{system_, "lr"} {
@@ -385,8 +113,8 @@ public:
{1, nullptr, "CreateContentMetaDatabase"},
{2, nullptr, "VerifyContentStorage"},
{3, nullptr, "VerifyContentMetaDatabase"},
{4, &NCM::OpenContentStorage, "OpenContentStorage"},
{5, &NCM::OpenContentMetaDatabase, "OpenContentMetaDatabase"},
{4, nullptr, "OpenContentStorage"},
{5, nullptr, "OpenContentMetaDatabase"},
{6, nullptr, "CloseContentStorageForcibly"},
{7, nullptr, "CloseContentMetaDatabaseForcibly"},
{8, nullptr, "CleanupContentMetaDatabase"},
@@ -402,29 +130,6 @@ public:
RegisterHandlers(functions);
}
private:
void OpenContentStorage(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto storage_id = rp.PopEnum<FileSys::StorageId>();
LOG_DEBUG(Service_NCM, "called, storage_id={}", static_cast<u32>(storage_id));
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess);
rb.PushIpcInterface<IContentStorage>(ctx, system, storage_id);
}
void OpenContentMetaDatabase(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto storage_id = rp.PopEnum<FileSys::StorageId>();
LOG_DEBUG(Service_NCM, "called, storage_id={}", static_cast<u32>(storage_id));
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess);
rb.PushIpcInterface<IContentMetaDatabase>(ctx, system, storage_id);
}
};
void LoopProcess(Core::System& system) {
@@ -9,7 +9,6 @@
#include "core/file_sys/registered_cache.h"
#include "core/hle/service/cmif_serialization.h"
#include "core/hle/service/filesystem/filesystem.h"
#include "core/hle/service/ipc_helpers.h"
#include "core/hle/service/ns/application_manager_interface.h"
#include "core/file_sys/content_archive.h"
@@ -20,7 +19,6 @@
#include "core/launch_timestamp_cache.h"
#include <algorithm>
#include <cstring>
#include <vector>
namespace Service::NS {
@@ -38,14 +36,14 @@ IApplicationManagerInterface::IApplicationManagerInterface(Core::System& system_
{1, nullptr, "GenerateApplicationRecordCount"},
{2, D<&IApplicationManagerInterface::GetApplicationRecordUpdateSystemEvent>, "GetApplicationRecordUpdateSystemEvent"},
{3, nullptr, "GetApplicationViewDeprecated"},
{4, D<&IApplicationManagerInterface::DeleteApplicationEntity>, "DeleteApplicationEntity"},
{5, D<&IApplicationManagerInterface::DeleteApplicationCompletely>, "DeleteApplicationCompletely"},
{4, nullptr, "DeleteApplicationEntity"},
{5, nullptr, "DeleteApplicationCompletely"},
{6, nullptr, "IsAnyApplicationEntityRedundant"},
{7, nullptr, "DeleteRedundantApplicationEntity"},
{8, nullptr, "IsApplicationEntityMovable"},
{9, nullptr, "MoveApplicationEntity"},
{11, nullptr, "CalculateApplicationOccupiedSize"},
{16, &IApplicationManagerInterface::PushApplicationRecord, "PushApplicationRecord"},
{16, nullptr, "PushApplicationRecord"},
{17, nullptr, "ListApplicationRecordContentMeta"},
{19, nullptr, "LaunchApplicationOld"},
{21, nullptr, "GetApplicationContentPath"},
@@ -645,27 +643,6 @@ Result IApplicationManagerInterface::IsAnyApplicationEntityInstalled(
R_SUCCEED();
}
Result IApplicationManagerInterface::DeleteApplicationEntity(u64 application_id) {
LOG_DEBUG(Service_NS, "called, application_id={:016X}", application_id);
auto& fsc = system.GetFileSystemController();
if (auto* const user_cache = fsc.GetUserNANDContents(); user_cache != nullptr) {
user_cache->RemoveExistingEntry(application_id);
user_cache->Refresh();
}
if (auto* const sdmc_cache = fsc.GetSDMCContents(); sdmc_cache != nullptr) {
sdmc_cache->RemoveExistingEntry(application_id);
sdmc_cache->Refresh();
}
record_update_system_event.Signal(system.Kernel());
R_SUCCEED();
}
Result IApplicationManagerInterface::DeleteApplicationCompletely(u64 application_id) {
R_RETURN(DeleteApplicationEntity(application_id));
}
Result IApplicationManagerInterface::GetApplicationViewDeprecated(
OutArray<ApplicationViewV19, BufferAttr_HipcMapAlias> out_application_views,
InArray<u64, BufferAttr_HipcMapAlias> application_ids) {
@@ -866,29 +843,6 @@ Result IApplicationManagerInterface::Unknown4053() {
R_SUCCEED();
}
void IApplicationManagerInterface::PushApplicationRecord(HLERequestContext& ctx) {
const auto record = ctx.ReadBuffer();
u64 application_id{};
if (record.size() >= sizeof(application_id)) {
std::memcpy(&application_id, record.data(), sizeof(application_id));
}
LOG_DEBUG(Service_NS, "called, application_id={:016X}, size={}", application_id, record.size());
auto& fsc = system.GetFileSystemController();
if (auto* const user_cache = fsc.GetUserNANDContents(); user_cache != nullptr) {
user_cache->Refresh();
}
if (auto* const sdmc_cache = fsc.GetSDMCContents(); sdmc_cache != nullptr) {
sdmc_cache->Refresh();
}
record_update_system_event.Signal(system.Kernel());
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
void IApplicationManagerInterface::ListApplicationTitle(HLERequestContext& ctx) {
LOG_DEBUG(Service_NS, "called");
IReadOnlyApplicationControlDataInterface(system).ListApplicationTitle(ctx);
@@ -56,8 +56,6 @@ public:
Result ResumeAll();
Result IsQualificationTransitionSupportedByProcessId(Out<bool> out_is_supported,
u64 process_id);
Result DeleteApplicationEntity(u64 application_id);
Result DeleteApplicationCompletely(u64 application_id);
Result GetStorageSize(Out<s64> out_total_space_size, Out<s64> out_free_space_size,
FileSys::StorageId storage_id);
Result TouchApplication(u64 application_id);
@@ -76,7 +74,6 @@ public:
Result RequestDownloadApplicationControlDataInBackground(u64 control_source,
u64 application_id);
void PushApplicationRecord(HLERequestContext& ctx);
void ListApplicationTitle(HLERequestContext& ctx);
private:
@@ -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={:#08x}", params.size);
LOG_DEBUG(Service_NVDRV, "called, size=0x{: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;
}
@@ -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={:#08x}", fd, command.raw);
LOG_DEBUG(Service_NVDRV, "called fd={}, ioctl=0x{: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={:#08x}", fd, command.raw);
LOG_DEBUG(Service_NVDRV, "called fd={}, ioctl=0x{: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={:#08x}", fd, command.raw);
LOG_DEBUG(Service_NVDRV, "called fd={}, ioctl=0x{:08X}", fd, command.raw);
if (!is_initialized) {
ServiceError(ctx, NvResult::NotInitialized);
@@ -212,7 +212,7 @@ void NVDRV::QueryEvent(HLERequestContext& ctx) {
void NVDRV::SetAruid(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
pid = rp.Pop<u64>();
LOG_WARNING(Service_NVDRV, "(STUBBED) called, pid={:#x}", pid);
LOG_WARNING(Service_NVDRV, "(STUBBED) called, pid={:#X}", 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(),

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