mirror of
https://git.eden-emu.dev/eden-emu/eden.git
synced 2026-09-27 19:26:52 +00:00
Compare commits
16 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| d336341c8d | |||
| a4fd5c6b0b | |||
| ef7e9c00b0 | |||
| 3be0db1266 | |||
| 815325ccec | |||
| 37fe911952 | |||
| f273423b2b | |||
| 87d2f03c39 | |||
| bebc19da32 | |||
| 99bf8cf51a | |||
| dbeb73ee01 | |||
| cb73a4dcc7 | |||
| 278f411dad | |||
| 3d37a816c6 | |||
| a25e32676f | |||
| 38df54edfe |
@@ -333,6 +333,11 @@
|
|||||||
"repo": "herumi/xbyak",
|
"repo": "herumi/xbyak",
|
||||||
"version": "v7.40.1"
|
"version": "v7.40.1"
|
||||||
},
|
},
|
||||||
|
"zbic": {
|
||||||
|
"hash": "fbe2f37986377d7f0d96ae3224c80b5971df6e8b6961f68061f1975ebb2e8cb78f07b90f014b007be4023dbf5513581504e7f7d61a5237cb2a8a7a61ae11e482",
|
||||||
|
"repo": "kinnay/zbic",
|
||||||
|
"version": "11b08f2712264bbed731545085cbd9702096ceb7"
|
||||||
|
},
|
||||||
"zlib": {
|
"zlib": {
|
||||||
"hash": "16fea4df307a68cf0035858abe2fd550250618a97590e202037acd18a666f57afc10f8836cbbd472d54a0e76539d0e558cb26f059d53de52ff90634bbf4f47d4",
|
"hash": "16fea4df307a68cf0035858abe2fd550250618a97590e202037acd18a666f57afc10f8836cbbd472d54a0e76539d0e558cb26f059d53de52ff90634bbf4f47d4",
|
||||||
"min_version": "1.2",
|
"min_version": "1.2",
|
||||||
|
|||||||
+33
-21
@@ -1,32 +1,44 @@
|
|||||||
# User Handbook - Command Line
|
# User Handbook - Command Line
|
||||||
|
|
||||||
There are two main applications, an SDL-based app (`eden-cli`) and a Qt based app (`eden`); both accept command line arguments.
|
There are two main applications, an SDL-based app (`eden-cli`) and a Qt based app (`eden`); both accept the same command line arguments.
|
||||||
|
|
||||||
## eden
|
|
||||||
|
|
||||||
- `./eden <path>`: Running with a single argument and nothing else, will make the emulator look for the given file and load it, this behavior is similar to `eden-cli`; allows dragging and dropping games into the application.
|
- `./eden <path>`: Running with a single argument and nothing else, will make the emulator look for the given file and load it, this behavior is similar to `eden-cli`; allows dragging and dropping games into the application.
|
||||||
- `-g <path>`: Alternate way to specify what to load, overrides. However let it be noted that arguments that use `-` will be treated as options/ignored, if your game, for some reason, starts with `-`, in order to safely handle it you may need to specify it as an argument.
|
- `--debug/-d`: Enter debug mode, allow gdb stub at port `1234`
|
||||||
- `-f`: Use fullscreen.
|
- `--config/-c`: Specify alternate configuration file.
|
||||||
- `-u <number>`: Select the index of the user to load as.
|
- `--fullscreen/-f`: Use fullscreen.
|
||||||
- `-input-profile <name>`: Specifies input profile name to use (for player #0 only).
|
- `--help/-h`: Display help.
|
||||||
|
- `--game/-g <path>`: Alternate way to specify what to load, overrides. However let it be noted that arguments that use `-` will be treated as options/ignored, if your game, for some reason, starts with `-`, in order to safely handle it you may need to specify it as an argument.
|
||||||
|
- `--multiplayer/-m`: Specify multiplayer options.
|
||||||
|
- `--program/-p`: Specify the program arguments to pass (optional).
|
||||||
|
- `--user/-u <number>`: Specify the user index.
|
||||||
|
- `--version/-v`: Display version and quit.
|
||||||
|
- `--input-profile/-i <name>`: Specifies input profile name to use (for player #0 only).
|
||||||
|
- `--null-render/-n`: Forces the usage of the "Null" render backend irrespective of settings.
|
||||||
|
- `--filter/-x`: Sets the debug log filter irrespective of settings.
|
||||||
|
- `--singlecore/-s`: Forces single-core regardless of settings.
|
||||||
|
|
||||||
|
Only the Qt frontend supports the following arguments:
|
||||||
|
|
||||||
- `-qlaunch`: Launch QLaunch.
|
- `-qlaunch`: Launch QLaunch.
|
||||||
- `-hlaunch`: Launch homebrew launcher `nx-hbloader`.
|
- `-hlaunch`: Launch homebrew launcher `nx-hbloader`.
|
||||||
- Requires a copy of Atmosphere to be extracted onto `sdmc`.
|
- Requires a copy of Atmosphere to be extracted onto `sdmc`.
|
||||||
- This is a shorthand for `<eden folder>/sdmc/atmosphere/hbl.nsp`.
|
- This is a shorthand for `<eden folder>/sdmc/atmosphere/hbl.nsp`.
|
||||||
- `-setup`: Launch setup applet.
|
- `-setup`: Launch setup applet.
|
||||||
|
|
||||||
## eden-cli
|
`eden` (provided with `--room`), and `eden-room` supports the following options as well:
|
||||||
|
|
||||||
- `--debug/-d`: Enter debug mode, allow gdb stub at port `1234`
|
- `-n/--room-name`: The name of the room.
|
||||||
- `--config/-c`: Specify alternate configuration file.
|
- `-d/--room-description`: The room description.
|
||||||
- `--fullscreen/-f`: Set fullscreen.
|
- `-s/--bind-address`: The bind address for the room.
|
||||||
- `--help/-h`: Display help.
|
- `-p/--port`: The port used for the room.
|
||||||
- `--game/-g`: Specify the game to run.
|
- `-m/--max-members`: The maximum number of players for this room.
|
||||||
- `--multiplayer/-m`: Specify multiplayer options.
|
- `-w/--password`: The password for the room.
|
||||||
- `--program/-p`: Specify the program arguments to pass (optional).
|
- `-g/--preferred-game`: The preferred game for this room.
|
||||||
- `--user/-u`: Specify the user index.
|
- `-i/--preferred-game-id`: The preferred game-id for this room.
|
||||||
- `--version/-v`: Display version and quit.
|
- `-u/--username`: The username used for announce.
|
||||||
- `--input-profile/-i`: Specifies input profile name to use (for player #0 only).
|
- `-t/--token`: The token used for announce.
|
||||||
- `--null-render/-n`: Forces the usage of the "Null" render backend irrespective of settings.
|
- `-a/--web-api-url`: yuzu Web API url.
|
||||||
- `--filter/-x`: Sets the debug log filter irrespective of settings.
|
- `-b/--ban-list-file`: The file for storing the room ban list.
|
||||||
- `--singlecore/-s`: Forces single-core regardless of settings.
|
- `-l/--log-file`: The file for storing the room log.
|
||||||
|
- `-h/--help`: Display this help and exit.
|
||||||
|
- `-v/--version`: Output version information and exit.
|
||||||
|
|||||||
Vendored
+5
@@ -48,6 +48,11 @@ if (NOT TARGET stb::headers)
|
|||||||
add_library(stb::headers ALIAS stb)
|
add_library(stb::headers ALIAS stb)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
AddJsonPackage(NAME zbic DOWNLOAD_ONLY)
|
||||||
|
set(ZBIC_INCLUDE_DIR
|
||||||
|
"${zbic_SOURCE_DIR}/src"
|
||||||
|
PARENT_SCOPE)
|
||||||
|
|
||||||
# ItaniumDemangle (Windows only)
|
# ItaniumDemangle (Windows only)
|
||||||
if (WIN32 AND NOT TARGET LLVM::Demangle)
|
if (WIN32 AND NOT TARGET LLVM::Demangle)
|
||||||
add_library(demangle demangle/ItaniumDemangle.cpp)
|
add_library(demangle demangle/ItaniumDemangle.cpp)
|
||||||
|
|||||||
-1
@@ -30,7 +30,6 @@ enum class BooleanSetting(override val key: String) : AbstractBooleanSetting {
|
|||||||
RENDERER_REACTIVE_FLUSHING("use_reactive_flushing"),
|
RENDERER_REACTIVE_FLUSHING("use_reactive_flushing"),
|
||||||
ENABLE_BUFFER_HISTORY("enable_buffer_history"),
|
ENABLE_BUFFER_HISTORY("enable_buffer_history"),
|
||||||
USE_OPTIMIZED_VERTEX_BUFFERS("use_optimized_vertex_buffers"),
|
USE_OPTIMIZED_VERTEX_BUFFERS("use_optimized_vertex_buffers"),
|
||||||
ENABLE_GPU_BUFFER_READBACK("enable_gpu_buffer_readback"),
|
|
||||||
SYNC_MEMORY_OPERATIONS("sync_memory_operations"),
|
SYNC_MEMORY_OPERATIONS("sync_memory_operations"),
|
||||||
BUFFER_REORDER_DISABLE("disable_buffer_reorder"),
|
BUFFER_REORDER_DISABLE("disable_buffer_reorder"),
|
||||||
RENDERER_DEBUG("debug"),
|
RENDERER_DEBUG("debug"),
|
||||||
|
|||||||
-7
@@ -908,13 +908,6 @@ abstract class SettingsItem(
|
|||||||
descriptionId = R.string.enable_buffer_history_description
|
descriptionId = R.string.enable_buffer_history_description
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
put(
|
|
||||||
SwitchSetting(
|
|
||||||
BooleanSetting.ENABLE_GPU_BUFFER_READBACK,
|
|
||||||
titleId = R.string.enable_gpu_buffer_readback,
|
|
||||||
descriptionId = R.string.enable_gpu_buffer_readback_description
|
|
||||||
)
|
|
||||||
)
|
|
||||||
put(
|
put(
|
||||||
SwitchSetting(
|
SwitchSetting(
|
||||||
BooleanSetting.USE_OPTIMIZED_VERTEX_BUFFERS,
|
BooleanSetting.USE_OPTIMIZED_VERTEX_BUFFERS,
|
||||||
|
|||||||
-1
@@ -549,7 +549,6 @@ class SettingsFragmentPresenter(
|
|||||||
add(BooleanSetting.RENDERER_FORCE_MAX_CLOCK.key)
|
add(BooleanSetting.RENDERER_FORCE_MAX_CLOCK.key)
|
||||||
add(BooleanSetting.RENDERER_REACTIVE_FLUSHING.key)
|
add(BooleanSetting.RENDERER_REACTIVE_FLUSHING.key)
|
||||||
add(BooleanSetting.ENABLE_BUFFER_HISTORY.key)
|
add(BooleanSetting.ENABLE_BUFFER_HISTORY.key)
|
||||||
add(BooleanSetting.ENABLE_GPU_BUFFER_READBACK.key)
|
|
||||||
add(BooleanSetting.USE_OPTIMIZED_VERTEX_BUFFERS.key)
|
add(BooleanSetting.USE_OPTIMIZED_VERTEX_BUFFERS.key)
|
||||||
|
|
||||||
add(HeaderSetting(R.string.hacks))
|
add(HeaderSetting(R.string.hacks))
|
||||||
|
|||||||
@@ -580,8 +580,6 @@
|
|||||||
<string name="renderer_reactive_flushing_description">يحسن دقة العرض في بعض الألعاب على حساب الأداء.</string>
|
<string name="renderer_reactive_flushing_description">يحسن دقة العرض في بعض الألعاب على حساب الأداء.</string>
|
||||||
<string name="enable_buffer_history">تمكين سجل التخزين المؤقت</string>
|
<string name="enable_buffer_history">تمكين سجل التخزين المؤقت</string>
|
||||||
<string name="enable_buffer_history_description">يُتيح هذا الخيار الوصول إلى حالات التخزين المؤقت السابقة. وقد يُحسّن جودة العرض وثبات الأداء في بعض الألعاب.</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">مخازن الرؤوس المُحسّنة</string>
|
||||||
<string name="use_optimized_vertex_buffers_description">يُتيح ربطًا مُحسَّنًا لمخازن الرؤوس لتحسين الأداء. يتطلب برامج تشغيل Mesa 26.0+ Turnip/ برامج تشغيل QCOM. قد يتعطل على برامج تشغيل Turnip القديمة (25.3 وما دون).</string>
|
<string name="use_optimized_vertex_buffers_description">يُتيح ربطًا مُحسَّنًا لمخازن الرؤوس لتحسين الأداء. يتطلب برامج تشغيل Mesa 26.0+ Turnip/ برامج تشغيل QCOM. قد يتعطل على برامج تشغيل Turnip القديمة (25.3 وما دون).</string>
|
||||||
|
|
||||||
@@ -1099,7 +1097,6 @@
|
|||||||
<string name="gpu_fence_behavior_immediate">فوري</string>
|
<string name="gpu_fence_behavior_immediate">فوري</string>
|
||||||
<string name="gpu_fence_behavior_balanced">متوازن</string>
|
<string name="gpu_fence_behavior_balanced">متوازن</string>
|
||||||
<string name="gpu_fence_behavior_accurate">دقيق</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_conservative">محافظ</string>
|
||||||
<string name="vram_usage_aggressive">عدواني</string>
|
<string name="vram_usage_aggressive">عدواني</string>
|
||||||
|
|||||||
@@ -967,7 +967,6 @@ Wirklich fortfahren?</string>
|
|||||||
<string name="gpu_fence_behavior_immediate">Direkt</string>
|
<string name="gpu_fence_behavior_immediate">Direkt</string>
|
||||||
<string name="gpu_fence_behavior_balanced">Ausgewogen</string>
|
<string name="gpu_fence_behavior_balanced">Ausgewogen</string>
|
||||||
<string name="gpu_fence_behavior_accurate">Genau</string>
|
<string name="gpu_fence_behavior_accurate">Genau</string>
|
||||||
<string name="gpu_fence_behavior_strict">Strikt</string>
|
|
||||||
|
|
||||||
<string name="vram_usage_conservative">Konservativ</string>
|
<string name="vram_usage_conservative">Konservativ</string>
|
||||||
<string name="vram_usage_aggressive">Aggressiv</string>
|
<string name="vram_usage_aggressive">Aggressiv</string>
|
||||||
|
|||||||
@@ -524,8 +524,6 @@
|
|||||||
<string name="renderer_reactive_flushing_description">Mejora la precisión de renderizado en algunos juegos, pero reduce el rendimiento.</string>
|
<string name="renderer_reactive_flushing_description">Mejora la precisión de renderizado en algunos juegos, pero reduce el rendimiento.</string>
|
||||||
<string name="enable_buffer_history">Activar el historial del búfer</string>
|
<string name="enable_buffer_history">Activar el historial del búfer</string>
|
||||||
<string name="enable_buffer_history_description">Permite el acceso al estado del búfer anterior. Esta opción puede mejorar la calidad de renderizado y la consistencia en el rendimiento de algunos juegos.</string>
|
<string name="enable_buffer_history_description">Permite el acceso al estado del búfer anterior. Esta opción puede mejorar la calidad de renderizado y la consistencia en el rendimiento de algunos juegos.</string>
|
||||||
<string name="enable_gpu_buffer_readback">Activar la lectura del buffer de la GPU</string>
|
|
||||||
<string name="enable_gpu_buffer_readback_description">Conserva los datos del búfer modificados por la GPU leyéndolos antes de subirlos.\nAlgunos juegos requieren esto para renderizar correctamente ciertos efectos.\nPuede causar problemas si el hardware no puede soportar la carga de trabajo adicional.</string>
|
|
||||||
<string name="use_optimized_vertex_buffers">Búferes de vértices optimizados</string>
|
<string name="use_optimized_vertex_buffers">Búferes de vértices optimizados</string>
|
||||||
<string name="use_optimized_vertex_buffers_description">Permite la optimización del enlace del búfer de vértices para un mejor rendimiento. Requiere controladores Mesa 26.0+ Turnip/ controladores QCOM. Fallará con controladores Turnip más antiguos (versión 25.3 o inferior).</string>
|
<string name="use_optimized_vertex_buffers_description">Permite la optimización del enlace del búfer de vértices para un mejor rendimiento. Requiere controladores Mesa 26.0+ Turnip/ controladores QCOM. Fallará con controladores Turnip más antiguos (versión 25.3 o inferior).</string>
|
||||||
|
|
||||||
@@ -1038,7 +1036,6 @@
|
|||||||
<string name="gpu_fence_behavior_immediate">Inmediato</string>
|
<string name="gpu_fence_behavior_immediate">Inmediato</string>
|
||||||
<string name="gpu_fence_behavior_balanced">Equilibrado</string>
|
<string name="gpu_fence_behavior_balanced">Equilibrado</string>
|
||||||
<string name="gpu_fence_behavior_accurate">Preciso</string>
|
<string name="gpu_fence_behavior_accurate">Preciso</string>
|
||||||
<string name="gpu_fence_behavior_strict">Estricto</string>
|
|
||||||
|
|
||||||
<string name="vram_usage_conservative">Conservador</string>
|
<string name="vram_usage_conservative">Conservador</string>
|
||||||
<string name="vram_usage_aggressive">Agresivo</string>
|
<string name="vram_usage_aggressive">Agresivo</string>
|
||||||
|
|||||||
@@ -569,8 +569,6 @@
|
|||||||
<string name="renderer_reactive_flushing_description">Повышение точности рендеринга в некоторых играх за счет снижения производительности.</string>
|
<string name="renderer_reactive_flushing_description">Повышение точности рендеринга в некоторых играх за счет снижения производительности.</string>
|
||||||
<string name="enable_buffer_history">Включить историю буфера</string>
|
<string name="enable_buffer_history">Включить историю буфера</string>
|
||||||
<string name="enable_buffer_history_description">Позволяет обращаться к предыдущим состояниям буфера. Эта опция может повысить качество рендеринга и стабильность производительности в некоторых играх.</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">Оптимизированные вершинные буферы</string>
|
||||||
<string name="use_optimized_vertex_buffers_description">Включает оптимизированную привязку вершинного буфера для повышения производительности. Требует Mesa Turnip 26.0+ / QCOM. Приводит к вылету на старых версиях драйверов Turnip (25.3 и ниже).</string>
|
<string name="use_optimized_vertex_buffers_description">Включает оптимизированную привязку вершинного буфера для повышения производительности. Требует Mesa Turnip 26.0+ / QCOM. Приводит к вылету на старых версиях драйверов Turnip (25.3 и ниже).</string>
|
||||||
|
|
||||||
@@ -1088,7 +1086,6 @@
|
|||||||
<string name="gpu_fence_behavior_immediate">Мгновенный</string>
|
<string name="gpu_fence_behavior_immediate">Мгновенный</string>
|
||||||
<string name="gpu_fence_behavior_balanced">Сбалансированный</string>
|
<string name="gpu_fence_behavior_balanced">Сбалансированный</string>
|
||||||
<string name="gpu_fence_behavior_accurate">Точный</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_conservative">Консервативный</string>
|
||||||
<string name="vram_usage_aggressive">Агрессивный</string>
|
<string name="vram_usage_aggressive">Агрессивный</string>
|
||||||
|
|||||||
@@ -570,8 +570,6 @@
|
|||||||
<string name="renderer_reactive_flushing_description">通过牺牲性能来提升某些游戏的渲染精度。</string>
|
<string name="renderer_reactive_flushing_description">通过牺牲性能来提升某些游戏的渲染精度。</string>
|
||||||
<string name="enable_buffer_history">启用缓冲区历史</string>
|
<string name="enable_buffer_history">启用缓冲区历史</string>
|
||||||
<string name="enable_buffer_history_description">启用对先前缓冲区状态的访问。此选项可在某些游戏中提升渲染质量并保持性能的一致性。</string>
|
<string name="enable_buffer_history_description">启用对先前缓冲区状态的访问。此选项可在某些游戏中提升渲染质量并保持性能的一致性。</string>
|
||||||
<string name="enable_gpu_buffer_readback">启用 GPU 缓冲区回读</string>
|
|
||||||
<string name="enable_gpu_buffer_readback_description">在上传前回读经由 GPU 修改过的缓冲区数据,以将其保留。一些游戏会用到这项设定以正确渲染某些效果。如果硬件无法处理额外的工作负载,则可能会导致问题。</string>
|
|
||||||
<string name="use_optimized_vertex_buffers">优化顶点缓冲区</string>
|
<string name="use_optimized_vertex_buffers">优化顶点缓冲区</string>
|
||||||
<string name="use_optimized_vertex_buffers_description">启用经过优化的顶点缓冲区绑定以提升性能。需要 Mesa 26.0 及以上版本的 Turnip 或 QCOM 驱动程序。若使用较旧版本的 Turnip 驱动 (25.3 及以下版本) 则会导致崩溃。</string>
|
<string name="use_optimized_vertex_buffers_description">启用经过优化的顶点缓冲区绑定以提升性能。需要 Mesa 26.0 及以上版本的 Turnip 或 QCOM 驱动程序。若使用较旧版本的 Turnip 驱动 (25.3 及以下版本) 则会导致崩溃。</string>
|
||||||
|
|
||||||
@@ -1089,7 +1087,6 @@
|
|||||||
<string name="gpu_fence_behavior_immediate">即时</string>
|
<string name="gpu_fence_behavior_immediate">即时</string>
|
||||||
<string name="gpu_fence_behavior_balanced">均衡</string>
|
<string name="gpu_fence_behavior_balanced">均衡</string>
|
||||||
<string name="gpu_fence_behavior_accurate">精确</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_conservative">保守式</string>
|
||||||
<string name="vram_usage_aggressive">主动式</string>
|
<string name="vram_usage_aggressive">主动式</string>
|
||||||
|
|||||||
@@ -561,8 +561,6 @@
|
|||||||
<string name="renderer_reactive_flushing_description">犧牲效能,以改善部分遊戲的轉譯準確度</string>
|
<string name="renderer_reactive_flushing_description">犧牲效能,以改善部分遊戲的轉譯準確度</string>
|
||||||
<string name="enable_buffer_history">啟用緩衝區歷史</string>
|
<string name="enable_buffer_history">啟用緩衝區歷史</string>
|
||||||
<string name="enable_buffer_history_description">允許存取先前的緩衝區狀態。此選項可能會改善部分遊戲的渲染品質與效能穩定性</string>
|
<string name="enable_buffer_history_description">允許存取先前的緩衝區狀態。此選項可能會改善部分遊戲的渲染品質與效能穩定性</string>
|
||||||
<string name="enable_gpu_buffer_readback">啟用 GPU 緩衝區讀回</string>
|
|
||||||
<string name="enable_gpu_buffer_readback_description">透過在上傳之前先將 GPU 修改過的緩衝區資料讀回來保存資料,部分遊戲需要啟用此功能才能正常渲染遊戲特效。如果硬體無法負荷可能會導致錯誤</string>
|
|
||||||
<string name="use_optimized_vertex_buffers">最佳化頂點緩衝區</string>
|
<string name="use_optimized_vertex_buffers">最佳化頂點緩衝區</string>
|
||||||
<string name="use_optimized_vertex_buffers_description">啟用最佳化的頂點緩衝區綁定。需要安裝 Mesa 26.0+ Turnip drivers/Qualcomm drivers。使用舊版 Turnip drivers 會導致當機 (25.3版和更低的版本)</string>
|
<string name="use_optimized_vertex_buffers_description">啟用最佳化的頂點緩衝區綁定。需要安裝 Mesa 26.0+ Turnip drivers/Qualcomm drivers。使用舊版 Turnip drivers 會導致當機 (25.3版和更低的版本)</string>
|
||||||
|
|
||||||
|
|||||||
@@ -558,7 +558,6 @@
|
|||||||
<item>@string/gpu_fence_behavior_immediate</item>
|
<item>@string/gpu_fence_behavior_immediate</item>
|
||||||
<item>@string/gpu_fence_behavior_balanced</item>
|
<item>@string/gpu_fence_behavior_balanced</item>
|
||||||
<item>@string/gpu_fence_behavior_accurate</item>
|
<item>@string/gpu_fence_behavior_accurate</item>
|
||||||
<item>@string/gpu_fence_behavior_strict</item>
|
|
||||||
</string-array>
|
</string-array>
|
||||||
<integer-array name="gpuFenceBehaviorValues">
|
<integer-array name="gpuFenceBehaviorValues">
|
||||||
<item>0</item>
|
<item>0</item>
|
||||||
|
|||||||
@@ -586,8 +586,6 @@
|
|||||||
<string name="renderer_reactive_flushing_description">Improves rendering accuracy in some games at the cost of performance.</string>
|
<string name="renderer_reactive_flushing_description">Improves rendering accuracy in some games at the cost of performance.</string>
|
||||||
<string name="enable_buffer_history">Enable buffer history</string>
|
<string name="enable_buffer_history">Enable buffer history</string>
|
||||||
<string name="enable_buffer_history_description">Enables access to previous buffer states. This option may improve rendering quality and performance consistency in some games.</string>
|
<string name="enable_buffer_history_description">Enables access to previous buffer states. This option may improve rendering quality and performance consistency in some games.</string>
|
||||||
<string name="enable_gpu_buffer_readback">Enable GPU Buffer Readback</string>
|
|
||||||
<string name="enable_gpu_buffer_readback_description">Preserves GPU-modified buffer data by reading it back before uploads. Some games require this to render certain effects properly. May cause issues if the hardware cannot handle the additional workload.</string>
|
|
||||||
<string name="use_optimized_vertex_buffers">Optimized Vertex Buffers</string>
|
<string name="use_optimized_vertex_buffers">Optimized Vertex Buffers</string>
|
||||||
<string name="use_optimized_vertex_buffers_description">Enables optimized vertex buffer binding for improved performance. Requires Mesa 26.0+ Turnip drivers/ QCOM drivers. Will crash on older Turnip drivers (25.3 and below).</string>
|
<string name="use_optimized_vertex_buffers_description">Enables optimized vertex buffer binding for improved performance. Requires Mesa 26.0+ Turnip drivers/ QCOM drivers. Will crash on older Turnip drivers (25.3 and below).</string>
|
||||||
|
|
||||||
@@ -1138,7 +1136,6 @@
|
|||||||
<string name="gpu_fence_behavior_immediate">Immediate</string>
|
<string name="gpu_fence_behavior_immediate">Immediate</string>
|
||||||
<string name="gpu_fence_behavior_balanced">Balanced</string>
|
<string name="gpu_fence_behavior_balanced">Balanced</string>
|
||||||
<string name="gpu_fence_behavior_accurate">Accurate</string>
|
<string name="gpu_fence_behavior_accurate">Accurate</string>
|
||||||
<string name="gpu_fence_behavior_strict">Strict</string>
|
|
||||||
|
|
||||||
<!-- ASTC Decoding Method Choices -->
|
<!-- ASTC Decoding Method Choices -->
|
||||||
<string name="accelerate_astc_cpu" translatable="false">CPU</string>
|
<string name="accelerate_astc_cpu" translatable="false">CPU</string>
|
||||||
|
|||||||
@@ -62,7 +62,6 @@ add_library(
|
|||||||
fs/fs_util.h
|
fs/fs_util.h
|
||||||
fs/path_util.cpp
|
fs/path_util.cpp
|
||||||
fs/path_util.h
|
fs/path_util.h
|
||||||
hash.h
|
|
||||||
heap_tracker.cpp
|
heap_tracker.cpp
|
||||||
heap_tracker.h
|
heap_tracker.h
|
||||||
hex_util.cpp
|
hex_util.cpp
|
||||||
@@ -76,7 +75,6 @@ add_library(
|
|||||||
logging.h
|
logging.h
|
||||||
lz4_compression.cpp
|
lz4_compression.cpp
|
||||||
lz4_compression.h
|
lz4_compression.h
|
||||||
make_unique_for_overwrite.h
|
|
||||||
math_util.h
|
math_util.h
|
||||||
memory_detect.cpp
|
memory_detect.cpp
|
||||||
memory_detect.h
|
memory_detect.h
|
||||||
@@ -138,6 +136,8 @@ add_library(
|
|||||||
uuid.cpp
|
uuid.cpp
|
||||||
uuid.h
|
uuid.h
|
||||||
vector_math.h
|
vector_math.h
|
||||||
|
zbic_compression.cpp
|
||||||
|
zbic_compression.h
|
||||||
zstd_compression.cpp
|
zstd_compression.cpp
|
||||||
zstd_compression.h
|
zstd_compression.h
|
||||||
fs/ryujinx_compat.h fs/ryujinx_compat.cpp
|
fs/ryujinx_compat.h fs/ryujinx_compat.cpp
|
||||||
@@ -148,6 +148,10 @@ add_library(
|
|||||||
net/net.h net/net.cpp
|
net/net.h net/net.cpp
|
||||||
container/unordered_map.h container/unordered_set.h)
|
container/unordered_map.h container/unordered_set.h)
|
||||||
|
|
||||||
|
set_source_files_properties(zbic_compression.cpp PROPERTIES
|
||||||
|
INCLUDE_DIRECTORIES "${ZBIC_INCLUDE_DIR}"
|
||||||
|
COMPILE_OPTIONS "$<$<CXX_COMPILER_ID:Clang,GNU>:-Wno-unused-function;-Wno-missing-declarations;-Wno-shadow>")
|
||||||
|
|
||||||
if(WIN32)
|
if(WIN32)
|
||||||
target_sources(common PRIVATE windows/timer_resolution.cpp
|
target_sources(common PRIVATE windows/timer_resolution.cpp
|
||||||
windows/timer_resolution.h)
|
windows/timer_resolution.h)
|
||||||
|
|||||||
+252
-251
@@ -12,93 +12,94 @@
|
|||||||
#include "common/android/multiplayer/multiplayer.h"
|
#include "common/android/multiplayer/multiplayer.h"
|
||||||
#include <network/network.h>
|
#include <network/network.h>
|
||||||
|
|
||||||
|
static struct {
|
||||||
|
JavaVM *java_vm;
|
||||||
|
jclass native_library_class;
|
||||||
|
jclass disk_cache_progress_class;
|
||||||
|
jclass load_callback_stage_class;
|
||||||
|
jclass game_dir_class;
|
||||||
|
jmethodID game_dir_constructor;
|
||||||
|
jmethodID exit_emulation_activity;
|
||||||
|
jmethodID disk_cache_load_progress;
|
||||||
|
jmethodID on_emulation_started;
|
||||||
|
jmethodID on_emulation_stopped;
|
||||||
|
jmethodID on_program_changed;
|
||||||
|
jmethodID copy_to_storage;
|
||||||
|
jmethodID file_exists;
|
||||||
|
jmethodID file_extension;
|
||||||
|
|
||||||
static JavaVM *s_java_vm;
|
jclass game_class;
|
||||||
static jclass s_native_library_class;
|
jmethodID game_constructor;
|
||||||
static jclass s_disk_cache_progress_class;
|
jfieldID game_title_field;
|
||||||
static jclass s_load_callback_stage_class;
|
jfieldID game_path_field;
|
||||||
static jclass s_game_dir_class;
|
jfieldID game_program_id_field;
|
||||||
static jmethodID s_game_dir_constructor;
|
jfieldID game_developer_field;
|
||||||
static jmethodID s_exit_emulation_activity;
|
jfieldID game_version_field;
|
||||||
static jmethodID s_disk_cache_load_progress;
|
jfieldID game_is_homebrew_field;
|
||||||
static jmethodID s_on_emulation_started;
|
|
||||||
static jmethodID s_on_emulation_stopped;
|
|
||||||
static jmethodID s_on_program_changed;
|
|
||||||
static jmethodID s_copy_to_storage;
|
|
||||||
static jmethodID s_file_exists;
|
|
||||||
static jmethodID s_file_extension;
|
|
||||||
|
|
||||||
static jclass s_game_class;
|
jclass string_class;
|
||||||
static jmethodID s_game_constructor;
|
jclass pair_class;
|
||||||
static jfieldID s_game_title_field;
|
jmethodID pair_constructor;
|
||||||
static jfieldID s_game_path_field;
|
jfieldID pair_first_field;
|
||||||
static jfieldID s_game_program_id_field;
|
jfieldID pair_second_field;
|
||||||
static jfieldID s_game_developer_field;
|
|
||||||
static jfieldID s_game_version_field;
|
|
||||||
static jfieldID s_game_is_homebrew_field;
|
|
||||||
|
|
||||||
static jclass s_string_class;
|
jclass overlay_control_data_class;
|
||||||
static jclass s_pair_class;
|
jmethodID overlay_control_data_constructor;
|
||||||
static jmethodID s_pair_constructor;
|
jfieldID overlay_control_data_id_field;
|
||||||
static jfieldID s_pair_first_field;
|
jfieldID overlay_control_data_enabled_field;
|
||||||
static jfieldID s_pair_second_field;
|
jfieldID overlay_control_data_individual_scale_field;
|
||||||
|
jfieldID overlay_control_data_landscape_position_field;
|
||||||
|
jfieldID overlay_control_data_portrait_position_field;
|
||||||
|
jfieldID overlay_control_data_foldable_position_field;
|
||||||
|
|
||||||
static jclass s_overlay_control_data_class;
|
jclass patch_class;
|
||||||
static jmethodID s_overlay_control_data_constructor;
|
jmethodID patch_constructor;
|
||||||
static jfieldID s_overlay_control_data_id_field;
|
jfieldID patch_enabled_field;
|
||||||
static jfieldID s_overlay_control_data_enabled_field;
|
jfieldID patch_name_field;
|
||||||
static jfieldID s_overlay_control_data_individual_scale_field;
|
jfieldID patch_version_field;
|
||||||
static jfieldID s_overlay_control_data_landscape_position_field;
|
jfieldID patch_type_field;
|
||||||
static jfieldID s_overlay_control_data_portrait_position_field;
|
jfieldID patch_program_id_field;
|
||||||
static jfieldID s_overlay_control_data_foldable_position_field;
|
jfieldID patch_title_id_field;
|
||||||
|
|
||||||
static jclass s_patch_class;
|
jclass double_class;
|
||||||
static jmethodID s_patch_constructor;
|
jmethodID double_constructor;
|
||||||
static jfieldID s_patch_enabled_field;
|
jmethodID double_value_method;
|
||||||
static jfieldID s_patch_name_field;
|
|
||||||
static jfieldID s_patch_version_field;
|
|
||||||
static jfieldID s_patch_type_field;
|
|
||||||
static jfieldID s_patch_program_id_field;
|
|
||||||
static jfieldID s_patch_title_id_field;
|
|
||||||
|
|
||||||
static jclass s_double_class;
|
jclass integer_class;
|
||||||
static jmethodID s_double_constructor;
|
jmethodID integer_constructor;
|
||||||
static jmethodID s_double_value_method;
|
jmethodID integer_value_method;
|
||||||
|
|
||||||
static jclass s_integer_class;
|
jclass boolean_class;
|
||||||
static jmethodID s_integer_constructor;
|
jmethodID boolean_constructor;
|
||||||
static jmethodID s_integer_value_method;
|
jmethodID boolean_value_method;
|
||||||
|
|
||||||
static jclass s_boolean_class;
|
jclass player_input_class;
|
||||||
static jmethodID s_boolean_constructor;
|
jmethodID player_input_constructor;
|
||||||
static jmethodID s_boolean_value_method;
|
jfieldID player_input_connected_field;
|
||||||
|
jfieldID player_input_buttons_field;
|
||||||
|
jfieldID player_input_analogs_field;
|
||||||
|
jfieldID player_input_motions_field;
|
||||||
|
jfieldID player_input_vibration_enabled_field;
|
||||||
|
jfieldID player_input_vibration_strength_field;
|
||||||
|
jfieldID player_input_body_color_left_field;
|
||||||
|
jfieldID player_input_body_color_right_field;
|
||||||
|
jfieldID player_input_button_color_left_field;
|
||||||
|
jfieldID player_input_button_color_right_field;
|
||||||
|
jfieldID player_input_profile_name_field;
|
||||||
|
jfieldID player_input_use_system_vibrator_field;
|
||||||
|
|
||||||
static jclass s_player_input_class;
|
jclass yuzu_input_device_interface;
|
||||||
static jmethodID s_player_input_constructor;
|
jmethodID yuzu_input_device_get_name;
|
||||||
static jfieldID s_player_input_connected_field;
|
jmethodID yuzu_input_device_get_guid;
|
||||||
static jfieldID s_player_input_buttons_field;
|
jmethodID yuzu_input_device_get_port;
|
||||||
static jfieldID s_player_input_analogs_field;
|
jmethodID yuzu_input_device_get_supports_vibration;
|
||||||
static jfieldID s_player_input_motions_field;
|
jmethodID yuzu_input_device_vibrate;
|
||||||
static jfieldID s_player_input_vibration_enabled_field;
|
jmethodID yuzu_input_device_get_axes;
|
||||||
static jfieldID s_player_input_vibration_strength_field;
|
jmethodID yuzu_input_device_has_keys;
|
||||||
static jfieldID s_player_input_body_color_left_field;
|
|
||||||
static jfieldID s_player_input_body_color_right_field;
|
|
||||||
static jfieldID s_player_input_button_color_left_field;
|
|
||||||
static jfieldID s_player_input_button_color_right_field;
|
|
||||||
static jfieldID s_player_input_profile_name_field;
|
|
||||||
static jfieldID s_player_input_use_system_vibrator_field;
|
|
||||||
|
|
||||||
static jclass s_yuzu_input_device_interface;
|
jmethodID add_netplay_message;
|
||||||
static jmethodID s_yuzu_input_device_get_name;
|
jmethodID clear_chat;
|
||||||
static jmethodID s_yuzu_input_device_get_guid;
|
} state;
|
||||||
static jmethodID s_yuzu_input_device_get_port;
|
|
||||||
static jmethodID s_yuzu_input_device_get_supports_vibration;
|
|
||||||
static jmethodID s_yuzu_input_device_vibrate;
|
|
||||||
static jmethodID s_yuzu_input_device_get_axes;
|
|
||||||
static jmethodID s_yuzu_input_device_has_keys;
|
|
||||||
|
|
||||||
static jmethodID s_add_netplay_message;
|
|
||||||
static jmethodID s_clear_chat;
|
|
||||||
|
|
||||||
static constexpr jint JNI_VERSION = JNI_VERSION_1_6;
|
static constexpr jint JNI_VERSION = JNI_VERSION_1_6;
|
||||||
|
|
||||||
@@ -106,14 +107,14 @@ namespace Common::Android {
|
|||||||
JNIEnv *GetEnvForThread() {
|
JNIEnv *GetEnvForThread() {
|
||||||
thread_local static struct OwnedEnv {
|
thread_local static struct OwnedEnv {
|
||||||
OwnedEnv() {
|
OwnedEnv() {
|
||||||
status = s_java_vm->GetEnv(reinterpret_cast<void **>(&env), JNI_VERSION_1_6);
|
status = state.java_vm->GetEnv(reinterpret_cast<void **>(&env), JNI_VERSION_1_6);
|
||||||
if (status == JNI_EDETACHED)
|
if (status == JNI_EDETACHED)
|
||||||
s_java_vm->AttachCurrentThread(&env, nullptr);
|
state.java_vm->AttachCurrentThread(&env, nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
~OwnedEnv() {
|
~OwnedEnv() {
|
||||||
if (status == JNI_EDETACHED)
|
if (status == JNI_EDETACHED)
|
||||||
s_java_vm->DetachCurrentThread();
|
state.java_vm->DetachCurrentThread();
|
||||||
}
|
}
|
||||||
|
|
||||||
int status;
|
int status;
|
||||||
@@ -123,303 +124,303 @@ namespace Common::Android {
|
|||||||
}
|
}
|
||||||
|
|
||||||
jclass GetNativeLibraryClass() {
|
jclass GetNativeLibraryClass() {
|
||||||
return s_native_library_class;
|
return state.native_library_class;
|
||||||
}
|
}
|
||||||
|
|
||||||
jclass GetDiskCacheProgressClass() {
|
jclass GetDiskCacheProgressClass() {
|
||||||
return s_disk_cache_progress_class;
|
return state.disk_cache_progress_class;
|
||||||
}
|
}
|
||||||
|
|
||||||
jclass GetDiskCacheLoadCallbackStageClass() {
|
jclass GetDiskCacheLoadCallbackStageClass() {
|
||||||
return s_load_callback_stage_class;
|
return state.load_callback_stage_class;
|
||||||
}
|
}
|
||||||
|
|
||||||
jclass GetGameDirClass() {
|
jclass GetGameDirClass() {
|
||||||
return s_game_dir_class;
|
return state.game_dir_class;
|
||||||
}
|
}
|
||||||
|
|
||||||
jmethodID GetGameDirConstructor() {
|
jmethodID GetGameDirConstructor() {
|
||||||
return s_game_dir_constructor;
|
return state.game_dir_constructor;
|
||||||
}
|
}
|
||||||
|
|
||||||
jmethodID GetExitEmulationActivity() {
|
jmethodID GetExitEmulationActivity() {
|
||||||
return s_exit_emulation_activity;
|
return state.exit_emulation_activity;
|
||||||
}
|
}
|
||||||
|
|
||||||
jmethodID GetDiskCacheLoadProgress() {
|
jmethodID GetDiskCacheLoadProgress() {
|
||||||
return s_disk_cache_load_progress;
|
return state.disk_cache_load_progress;
|
||||||
}
|
}
|
||||||
|
|
||||||
jmethodID GetCopyToStorage() {
|
jmethodID GetCopyToStorage() {
|
||||||
return s_copy_to_storage;
|
return state.copy_to_storage;
|
||||||
}
|
}
|
||||||
|
|
||||||
jmethodID GetFileExists() {
|
jmethodID GetFileExists() {
|
||||||
return s_file_exists;
|
return state.file_exists;
|
||||||
}
|
}
|
||||||
|
|
||||||
jmethodID GetFileExtension() {
|
jmethodID GetFileExtension() {
|
||||||
return s_file_extension;
|
return state.file_extension;
|
||||||
}
|
}
|
||||||
|
|
||||||
jmethodID GetOnEmulationStarted() {
|
jmethodID GetOnEmulationStarted() {
|
||||||
return s_on_emulation_started;
|
return state.on_emulation_started;
|
||||||
}
|
}
|
||||||
|
|
||||||
jmethodID GetOnEmulationStopped() {
|
jmethodID GetOnEmulationStopped() {
|
||||||
return s_on_emulation_stopped;
|
return state.on_emulation_stopped;
|
||||||
}
|
}
|
||||||
|
|
||||||
jmethodID GetOnProgramChanged() {
|
jmethodID GetOnProgramChanged() {
|
||||||
return s_on_program_changed;
|
return state.on_program_changed;
|
||||||
}
|
}
|
||||||
|
|
||||||
jclass GetGameClass() {
|
jclass GetGameClass() {
|
||||||
return s_game_class;
|
return state.game_class;
|
||||||
}
|
}
|
||||||
|
|
||||||
jmethodID GetGameConstructor() {
|
jmethodID GetGameConstructor() {
|
||||||
return s_game_constructor;
|
return state.game_constructor;
|
||||||
}
|
}
|
||||||
|
|
||||||
jfieldID GetGameTitleField() {
|
jfieldID GetGameTitleField() {
|
||||||
return s_game_title_field;
|
return state.game_title_field;
|
||||||
}
|
}
|
||||||
|
|
||||||
jfieldID GetGamePathField() {
|
jfieldID GetGamePathField() {
|
||||||
return s_game_path_field;
|
return state.game_path_field;
|
||||||
}
|
}
|
||||||
|
|
||||||
jfieldID GetGameProgramIdField() {
|
jfieldID GetGameProgramIdField() {
|
||||||
return s_game_program_id_field;
|
return state.game_program_id_field;
|
||||||
}
|
}
|
||||||
|
|
||||||
jfieldID GetGameDeveloperField() {
|
jfieldID GetGameDeveloperField() {
|
||||||
return s_game_developer_field;
|
return state.game_developer_field;
|
||||||
}
|
}
|
||||||
|
|
||||||
jfieldID GetGameVersionField() {
|
jfieldID GetGameVersionField() {
|
||||||
return s_game_version_field;
|
return state.game_version_field;
|
||||||
}
|
}
|
||||||
|
|
||||||
jfieldID GetGameIsHomebrewField() {
|
jfieldID GetGameIsHomebrewField() {
|
||||||
return s_game_is_homebrew_field;
|
return state.game_is_homebrew_field;
|
||||||
}
|
}
|
||||||
|
|
||||||
jclass GetStringClass() {
|
jclass GetStringClass() {
|
||||||
return s_string_class;
|
return state.string_class;
|
||||||
}
|
}
|
||||||
|
|
||||||
jclass GetPairClass() {
|
jclass GetPairClass() {
|
||||||
return s_pair_class;
|
return state.pair_class;
|
||||||
}
|
}
|
||||||
|
|
||||||
jmethodID GetPairConstructor() {
|
jmethodID GetPairConstructor() {
|
||||||
return s_pair_constructor;
|
return state.pair_constructor;
|
||||||
}
|
}
|
||||||
|
|
||||||
jfieldID GetPairFirstField() {
|
jfieldID GetPairFirstField() {
|
||||||
return s_pair_first_field;
|
return state.pair_first_field;
|
||||||
}
|
}
|
||||||
|
|
||||||
jfieldID GetPairSecondField() {
|
jfieldID GetPairSecondField() {
|
||||||
return s_pair_second_field;
|
return state.pair_second_field;
|
||||||
}
|
}
|
||||||
|
|
||||||
jclass GetOverlayControlDataClass() {
|
jclass GetOverlayControlDataClass() {
|
||||||
return s_overlay_control_data_class;
|
return state.overlay_control_data_class;
|
||||||
}
|
}
|
||||||
|
|
||||||
jmethodID GetOverlayControlDataConstructor() {
|
jmethodID GetOverlayControlDataConstructor() {
|
||||||
return s_overlay_control_data_constructor;
|
return state.overlay_control_data_constructor;
|
||||||
}
|
}
|
||||||
|
|
||||||
jfieldID GetOverlayControlDataIdField() {
|
jfieldID GetOverlayControlDataIdField() {
|
||||||
return s_overlay_control_data_id_field;
|
return state.overlay_control_data_id_field;
|
||||||
}
|
}
|
||||||
|
|
||||||
jfieldID GetOverlayControlDataEnabledField() {
|
jfieldID GetOverlayControlDataEnabledField() {
|
||||||
return s_overlay_control_data_enabled_field;
|
return state.overlay_control_data_enabled_field;
|
||||||
}
|
}
|
||||||
|
|
||||||
jfieldID GetOverlayControlDataIndividualScaleField() {
|
jfieldID GetOverlayControlDataIndividualScaleField() {
|
||||||
return s_overlay_control_data_individual_scale_field;
|
return state.overlay_control_data_individual_scale_field;
|
||||||
}
|
}
|
||||||
|
|
||||||
jfieldID GetOverlayControlDataLandscapePositionField() {
|
jfieldID GetOverlayControlDataLandscapePositionField() {
|
||||||
return s_overlay_control_data_landscape_position_field;
|
return state.overlay_control_data_landscape_position_field;
|
||||||
}
|
}
|
||||||
|
|
||||||
jfieldID GetOverlayControlDataPortraitPositionField() {
|
jfieldID GetOverlayControlDataPortraitPositionField() {
|
||||||
return s_overlay_control_data_portrait_position_field;
|
return state.overlay_control_data_portrait_position_field;
|
||||||
}
|
}
|
||||||
|
|
||||||
jfieldID GetOverlayControlDataFoldablePositionField() {
|
jfieldID GetOverlayControlDataFoldablePositionField() {
|
||||||
return s_overlay_control_data_foldable_position_field;
|
return state.overlay_control_data_foldable_position_field;
|
||||||
}
|
}
|
||||||
|
|
||||||
jclass GetPatchClass() {
|
jclass GetPatchClass() {
|
||||||
return s_patch_class;
|
return state.patch_class;
|
||||||
}
|
}
|
||||||
|
|
||||||
jmethodID GetPatchConstructor() {
|
jmethodID GetPatchConstructor() {
|
||||||
return s_patch_constructor;
|
return state.patch_constructor;
|
||||||
}
|
}
|
||||||
|
|
||||||
jfieldID GetPatchEnabledField() {
|
jfieldID GetPatchEnabledField() {
|
||||||
return s_patch_enabled_field;
|
return state.patch_enabled_field;
|
||||||
}
|
}
|
||||||
|
|
||||||
jfieldID GetPatchNameField() {
|
jfieldID GetPatchNameField() {
|
||||||
return s_patch_name_field;
|
return state.patch_name_field;
|
||||||
}
|
}
|
||||||
|
|
||||||
jfieldID GetPatchVersionField() {
|
jfieldID GetPatchVersionField() {
|
||||||
return s_patch_version_field;
|
return state.patch_version_field;
|
||||||
}
|
}
|
||||||
|
|
||||||
jfieldID GetPatchTypeField() {
|
jfieldID GetPatchTypeField() {
|
||||||
return s_patch_type_field;
|
return state.patch_type_field;
|
||||||
}
|
}
|
||||||
|
|
||||||
jfieldID GetPatchProgramIdField() {
|
jfieldID GetPatchProgramIdField() {
|
||||||
return s_patch_program_id_field;
|
return state.patch_program_id_field;
|
||||||
}
|
}
|
||||||
|
|
||||||
jfieldID GetPatchTitleIdField() {
|
jfieldID GetPatchTitleIdField() {
|
||||||
return s_patch_title_id_field;
|
return state.patch_title_id_field;
|
||||||
}
|
}
|
||||||
|
|
||||||
jclass GetDoubleClass() {
|
jclass GetDoubleClass() {
|
||||||
return s_double_class;
|
return state.double_class;
|
||||||
}
|
}
|
||||||
|
|
||||||
jmethodID GetDoubleConstructor() {
|
jmethodID GetDoubleConstructor() {
|
||||||
return s_double_constructor;
|
return state.double_constructor;
|
||||||
}
|
}
|
||||||
|
|
||||||
jmethodID GetDoubleValueMethod() {
|
jmethodID GetDoubleValueMethod() {
|
||||||
return s_double_value_method;
|
return state.double_value_method;
|
||||||
}
|
}
|
||||||
|
|
||||||
jclass GetIntegerClass() {
|
jclass GetIntegerClass() {
|
||||||
return s_integer_class;
|
return state.integer_class;
|
||||||
}
|
}
|
||||||
|
|
||||||
jmethodID GetIntegerConstructor() {
|
jmethodID GetIntegerConstructor() {
|
||||||
return s_integer_constructor;
|
return state.integer_constructor;
|
||||||
}
|
}
|
||||||
|
|
||||||
jmethodID GetIntegerValueMethod() {
|
jmethodID GetIntegerValueMethod() {
|
||||||
return s_integer_value_method;
|
return state.integer_value_method;
|
||||||
}
|
}
|
||||||
|
|
||||||
jclass GetBooleanClass() {
|
jclass GetBooleanClass() {
|
||||||
return s_boolean_class;
|
return state.boolean_class;
|
||||||
}
|
}
|
||||||
|
|
||||||
jmethodID GetBooleanConstructor() {
|
jmethodID GetBooleanConstructor() {
|
||||||
return s_boolean_constructor;
|
return state.boolean_constructor;
|
||||||
}
|
}
|
||||||
|
|
||||||
jmethodID GetBooleanValueMethod() {
|
jmethodID GetBooleanValueMethod() {
|
||||||
return s_boolean_value_method;
|
return state.boolean_value_method;
|
||||||
}
|
}
|
||||||
|
|
||||||
jclass GetPlayerInputClass() {
|
jclass GetPlayerInputClass() {
|
||||||
return s_player_input_class;
|
return state.player_input_class;
|
||||||
}
|
}
|
||||||
|
|
||||||
jmethodID GetPlayerInputConstructor() {
|
jmethodID GetPlayerInputConstructor() {
|
||||||
return s_player_input_constructor;
|
return state.player_input_constructor;
|
||||||
}
|
}
|
||||||
|
|
||||||
jfieldID GetPlayerInputConnectedField() {
|
jfieldID GetPlayerInputConnectedField() {
|
||||||
return s_player_input_connected_field;
|
return state.player_input_connected_field;
|
||||||
}
|
}
|
||||||
|
|
||||||
jfieldID GetPlayerInputButtonsField() {
|
jfieldID GetPlayerInputButtonsField() {
|
||||||
return s_player_input_buttons_field;
|
return state.player_input_buttons_field;
|
||||||
}
|
}
|
||||||
|
|
||||||
jfieldID GetPlayerInputAnalogsField() {
|
jfieldID GetPlayerInputAnalogsField() {
|
||||||
return s_player_input_analogs_field;
|
return state.player_input_analogs_field;
|
||||||
}
|
}
|
||||||
|
|
||||||
jfieldID GetPlayerInputMotionsField() {
|
jfieldID GetPlayerInputMotionsField() {
|
||||||
return s_player_input_motions_field;
|
return state.player_input_motions_field;
|
||||||
}
|
}
|
||||||
|
|
||||||
jfieldID GetPlayerInputVibrationEnabledField() {
|
jfieldID GetPlayerInputVibrationEnabledField() {
|
||||||
return s_player_input_vibration_enabled_field;
|
return state.player_input_vibration_enabled_field;
|
||||||
}
|
}
|
||||||
|
|
||||||
jfieldID GetPlayerInputVibrationStrengthField() {
|
jfieldID GetPlayerInputVibrationStrengthField() {
|
||||||
return s_player_input_vibration_strength_field;
|
return state.player_input_vibration_strength_field;
|
||||||
}
|
}
|
||||||
|
|
||||||
jfieldID GetPlayerInputBodyColorLeftField() {
|
jfieldID GetPlayerInputBodyColorLeftField() {
|
||||||
return s_player_input_body_color_left_field;
|
return state.player_input_body_color_left_field;
|
||||||
}
|
}
|
||||||
|
|
||||||
jfieldID GetPlayerInputBodyColorRightField() {
|
jfieldID GetPlayerInputBodyColorRightField() {
|
||||||
return s_player_input_body_color_right_field;
|
return state.player_input_body_color_right_field;
|
||||||
}
|
}
|
||||||
|
|
||||||
jfieldID GetPlayerInputButtonColorLeftField() {
|
jfieldID GetPlayerInputButtonColorLeftField() {
|
||||||
return s_player_input_button_color_left_field;
|
return state.player_input_button_color_left_field;
|
||||||
}
|
}
|
||||||
|
|
||||||
jfieldID GetPlayerInputButtonColorRightField() {
|
jfieldID GetPlayerInputButtonColorRightField() {
|
||||||
return s_player_input_button_color_right_field;
|
return state.player_input_button_color_right_field;
|
||||||
}
|
}
|
||||||
|
|
||||||
jfieldID GetPlayerInputProfileNameField() {
|
jfieldID GetPlayerInputProfileNameField() {
|
||||||
return s_player_input_profile_name_field;
|
return state.player_input_profile_name_field;
|
||||||
}
|
}
|
||||||
|
|
||||||
jfieldID GetPlayerInputUseSystemVibratorField() {
|
jfieldID GetPlayerInputUseSystemVibratorField() {
|
||||||
return s_player_input_use_system_vibrator_field;
|
return state.player_input_use_system_vibrator_field;
|
||||||
}
|
}
|
||||||
|
|
||||||
jclass GetYuzuInputDeviceInterface() {
|
jclass GetYuzuInputDeviceInterface() {
|
||||||
return s_yuzu_input_device_interface;
|
return state.yuzu_input_device_interface;
|
||||||
}
|
}
|
||||||
|
|
||||||
jmethodID GetYuzuDeviceGetName() {
|
jmethodID GetYuzuDeviceGetName() {
|
||||||
return s_yuzu_input_device_get_name;
|
return state.yuzu_input_device_get_name;
|
||||||
}
|
}
|
||||||
|
|
||||||
jmethodID GetYuzuDeviceGetGUID() {
|
jmethodID GetYuzuDeviceGetGUID() {
|
||||||
return s_yuzu_input_device_get_guid;
|
return state.yuzu_input_device_get_guid;
|
||||||
}
|
}
|
||||||
|
|
||||||
jmethodID GetYuzuDeviceGetPort() {
|
jmethodID GetYuzuDeviceGetPort() {
|
||||||
return s_yuzu_input_device_get_port;
|
return state.yuzu_input_device_get_port;
|
||||||
}
|
}
|
||||||
|
|
||||||
jmethodID GetYuzuDeviceGetSupportsVibration() {
|
jmethodID GetYuzuDeviceGetSupportsVibration() {
|
||||||
return s_yuzu_input_device_get_supports_vibration;
|
return state.yuzu_input_device_get_supports_vibration;
|
||||||
}
|
}
|
||||||
|
|
||||||
jmethodID GetYuzuDeviceVibrate() {
|
jmethodID GetYuzuDeviceVibrate() {
|
||||||
return s_yuzu_input_device_vibrate;
|
return state.yuzu_input_device_vibrate;
|
||||||
}
|
}
|
||||||
|
|
||||||
jmethodID GetYuzuDeviceGetAxes() {
|
jmethodID GetYuzuDeviceGetAxes() {
|
||||||
return s_yuzu_input_device_get_axes;
|
return state.yuzu_input_device_get_axes;
|
||||||
}
|
}
|
||||||
|
|
||||||
jmethodID GetYuzuDeviceHasKeys() {
|
jmethodID GetYuzuDeviceHasKeys() {
|
||||||
return s_yuzu_input_device_has_keys;
|
return state.yuzu_input_device_has_keys;
|
||||||
}
|
}
|
||||||
|
|
||||||
jmethodID GetAddNetPlayMessage() {
|
jmethodID GetAddNetPlayMessage() {
|
||||||
return s_add_netplay_message;
|
return state.add_netplay_message;
|
||||||
}
|
}
|
||||||
|
|
||||||
jmethodID ClearChat() {
|
jmethodID ClearChat() {
|
||||||
return s_clear_chat;
|
return state.clear_chat;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
@@ -436,20 +437,20 @@ namespace Common::Android {
|
|||||||
|
|
||||||
// UnInitialize Android Storage
|
// UnInitialize Android Storage
|
||||||
Common::FS::Android::UnRegisterCallbacks();
|
Common::FS::Android::UnRegisterCallbacks();
|
||||||
env->DeleteGlobalRef(s_native_library_class);
|
env->DeleteGlobalRef(state.native_library_class);
|
||||||
env->DeleteGlobalRef(s_disk_cache_progress_class);
|
env->DeleteGlobalRef(state.disk_cache_progress_class);
|
||||||
env->DeleteGlobalRef(s_load_callback_stage_class);
|
env->DeleteGlobalRef(state.load_callback_stage_class);
|
||||||
env->DeleteGlobalRef(s_game_dir_class);
|
env->DeleteGlobalRef(state.game_dir_class);
|
||||||
env->DeleteGlobalRef(s_game_class);
|
env->DeleteGlobalRef(state.game_class);
|
||||||
env->DeleteGlobalRef(s_string_class);
|
env->DeleteGlobalRef(state.string_class);
|
||||||
env->DeleteGlobalRef(s_pair_class);
|
env->DeleteGlobalRef(state.pair_class);
|
||||||
env->DeleteGlobalRef(s_overlay_control_data_class);
|
env->DeleteGlobalRef(state.overlay_control_data_class);
|
||||||
env->DeleteGlobalRef(s_patch_class);
|
env->DeleteGlobalRef(state.patch_class);
|
||||||
env->DeleteGlobalRef(s_double_class);
|
env->DeleteGlobalRef(state.double_class);
|
||||||
env->DeleteGlobalRef(s_integer_class);
|
env->DeleteGlobalRef(state.integer_class);
|
||||||
env->DeleteGlobalRef(s_boolean_class);
|
env->DeleteGlobalRef(state.boolean_class);
|
||||||
env->DeleteGlobalRef(s_player_input_class);
|
env->DeleteGlobalRef(state.player_input_class);
|
||||||
env->DeleteGlobalRef(s_yuzu_input_device_interface);
|
env->DeleteGlobalRef(state.yuzu_input_device_interface);
|
||||||
|
|
||||||
// UnInitialize applets
|
// UnInitialize applets
|
||||||
SoftwareKeyboard::CleanupJNI(env);
|
SoftwareKeyboard::CleanupJNI(env);
|
||||||
@@ -463,7 +464,7 @@ namespace Common::Android {
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
void Initialize(JavaVM* vm, JNIEnv *env) {
|
void Initialize(JavaVM* vm, JNIEnv *env) {
|
||||||
s_java_vm = vm;
|
state.java_vm = vm;
|
||||||
InitFFmpegOnLoad(vm);
|
InitFFmpegOnLoad(vm);
|
||||||
|
|
||||||
if (env->ExceptionCheck()) {
|
if (env->ExceptionCheck()) {
|
||||||
@@ -472,169 +473,169 @@ void Initialize(JavaVM* vm, JNIEnv *env) {
|
|||||||
|
|
||||||
// Initialize Java classes
|
// Initialize Java classes
|
||||||
const jclass native_library_class = env->FindClass("org/yuzu/yuzu_emu/NativeLibrary");
|
const jclass native_library_class = env->FindClass("org/yuzu/yuzu_emu/NativeLibrary");
|
||||||
s_native_library_class = reinterpret_cast<jclass>(env->NewGlobalRef(native_library_class));
|
state.native_library_class = reinterpret_cast<jclass>(env->NewGlobalRef(native_library_class));
|
||||||
s_disk_cache_progress_class = reinterpret_cast<jclass>(env->NewGlobalRef(
|
state.disk_cache_progress_class = reinterpret_cast<jclass>(env->NewGlobalRef(
|
||||||
env->FindClass("org/yuzu/yuzu_emu/disk_shader_cache/DiskShaderCacheProgress")));
|
env->FindClass("org/yuzu/yuzu_emu/disk_shader_cache/DiskShaderCacheProgress")));
|
||||||
s_load_callback_stage_class = reinterpret_cast<jclass>(env->NewGlobalRef(env->FindClass(
|
state.load_callback_stage_class = reinterpret_cast<jclass>(env->NewGlobalRef(env->FindClass(
|
||||||
"org/yuzu/yuzu_emu/disk_shader_cache/DiskShaderCacheProgress$LoadCallbackStage")));
|
"org/yuzu/yuzu_emu/disk_shader_cache/DiskShaderCacheProgress$LoadCallbackStage")));
|
||||||
|
|
||||||
const jclass game_dir_class = env->FindClass("org/yuzu/yuzu_emu/model/GameDir");
|
const jclass game_dir_class = env->FindClass("org/yuzu/yuzu_emu/model/GameDir");
|
||||||
s_game_dir_class = reinterpret_cast<jclass>(env->NewGlobalRef(game_dir_class));
|
state.game_dir_class = reinterpret_cast<jclass>(env->NewGlobalRef(game_dir_class));
|
||||||
s_game_dir_constructor = env->GetMethodID(game_dir_class, "<init>",
|
state.game_dir_constructor = env->GetMethodID(game_dir_class, "<init>",
|
||||||
"(Ljava/lang/String;Z)V");
|
"(Ljava/lang/String;Z)V");
|
||||||
env->DeleteLocalRef(game_dir_class);
|
env->DeleteLocalRef(game_dir_class);
|
||||||
|
|
||||||
// Initialize methods
|
// Initialize methods
|
||||||
s_exit_emulation_activity =
|
state.exit_emulation_activity =
|
||||||
env->GetStaticMethodID(s_native_library_class, "exitEmulationActivity", "(I)V");
|
env->GetStaticMethodID(state.native_library_class, "exitEmulationActivity", "(I)V");
|
||||||
s_disk_cache_load_progress =
|
state.disk_cache_load_progress =
|
||||||
env->GetStaticMethodID(s_disk_cache_progress_class, "loadProgress", "(III)V");
|
env->GetStaticMethodID(state.disk_cache_progress_class, "loadProgress", "(III)V");
|
||||||
s_copy_to_storage = env->GetStaticMethodID(s_native_library_class, "copyFileToStorage",
|
state.copy_to_storage = env->GetStaticMethodID(state.native_library_class, "copyFileToStorage",
|
||||||
"(Ljava/lang/String;Ljava/lang/String;)Z");
|
"(Ljava/lang/String;Ljava/lang/String;)Z");
|
||||||
s_file_exists = env->GetStaticMethodID(s_native_library_class, "exists",
|
state.file_exists = env->GetStaticMethodID(state.native_library_class, "exists",
|
||||||
"(Ljava/lang/String;)Z");
|
"(Ljava/lang/String;)Z");
|
||||||
s_file_extension = env->GetStaticMethodID(s_native_library_class, "getFileExtension",
|
state.file_extension = env->GetStaticMethodID(state.native_library_class, "getFileExtension",
|
||||||
"(Ljava/lang/String;)Ljava/lang/String;");
|
"(Ljava/lang/String;)Ljava/lang/String;");
|
||||||
s_on_emulation_started =
|
state.on_emulation_started =
|
||||||
env->GetStaticMethodID(s_native_library_class, "onEmulationStarted", "()V");
|
env->GetStaticMethodID(state.native_library_class, "onEmulationStarted", "()V");
|
||||||
s_on_emulation_stopped =
|
state.on_emulation_stopped =
|
||||||
env->GetStaticMethodID(s_native_library_class, "onEmulationStopped", "(I)V");
|
env->GetStaticMethodID(state.native_library_class, "onEmulationStopped", "(I)V");
|
||||||
s_on_program_changed =
|
state.on_program_changed =
|
||||||
env->GetStaticMethodID(s_native_library_class, "onProgramChanged", "(I)V");
|
env->GetStaticMethodID(state.native_library_class, "onProgramChanged", "(I)V");
|
||||||
|
|
||||||
const jclass game_class = env->FindClass("org/yuzu/yuzu_emu/model/Game");
|
const jclass game_class = env->FindClass("org/yuzu/yuzu_emu/model/Game");
|
||||||
s_game_class = reinterpret_cast<jclass>(env->NewGlobalRef(game_class));
|
state.game_class = reinterpret_cast<jclass>(env->NewGlobalRef(game_class));
|
||||||
s_game_constructor = env->GetMethodID(game_class, "<init>",
|
state.game_constructor = env->GetMethodID(game_class, "<init>",
|
||||||
"(Ljava/lang/String;Ljava/lang/String;Ljava/lang/"
|
"(Ljava/lang/String;Ljava/lang/String;Ljava/lang/"
|
||||||
"String;Ljava/lang/String;Ljava/lang/String;Z)V");
|
"String;Ljava/lang/String;Ljava/lang/String;Z)V");
|
||||||
s_game_title_field = env->GetFieldID(game_class, "title", "Ljava/lang/String;");
|
state.game_title_field = env->GetFieldID(game_class, "title", "Ljava/lang/String;");
|
||||||
s_game_path_field = env->GetFieldID(game_class, "path", "Ljava/lang/String;");
|
state.game_path_field = env->GetFieldID(game_class, "path", "Ljava/lang/String;");
|
||||||
s_game_program_id_field = env->GetFieldID(game_class, "programId", "Ljava/lang/String;");
|
state.game_program_id_field = env->GetFieldID(game_class, "programId", "Ljava/lang/String;");
|
||||||
s_game_developer_field = env->GetFieldID(game_class, "developer", "Ljava/lang/String;");
|
state.game_developer_field = env->GetFieldID(game_class, "developer", "Ljava/lang/String;");
|
||||||
s_game_version_field = env->GetFieldID(game_class, "version", "Ljava/lang/String;");
|
state.game_version_field = env->GetFieldID(game_class, "version", "Ljava/lang/String;");
|
||||||
s_game_is_homebrew_field = env->GetFieldID(game_class, "isHomebrew", "Z");
|
state.game_is_homebrew_field = env->GetFieldID(game_class, "isHomebrew", "Z");
|
||||||
env->DeleteLocalRef(game_class);
|
env->DeleteLocalRef(game_class);
|
||||||
|
|
||||||
const jclass string_class = env->FindClass("java/lang/String");
|
const jclass string_class = env->FindClass("java/lang/String");
|
||||||
s_string_class = reinterpret_cast<jclass>(env->NewGlobalRef(string_class));
|
state.string_class = reinterpret_cast<jclass>(env->NewGlobalRef(string_class));
|
||||||
env->DeleteLocalRef(string_class);
|
env->DeleteLocalRef(string_class);
|
||||||
|
|
||||||
const jclass pair_class = env->FindClass("kotlin/Pair");
|
const jclass pair_class = env->FindClass("kotlin/Pair");
|
||||||
s_pair_class = reinterpret_cast<jclass>(env->NewGlobalRef(pair_class));
|
state.pair_class = reinterpret_cast<jclass>(env->NewGlobalRef(pair_class));
|
||||||
s_pair_constructor =
|
state.pair_constructor =
|
||||||
env->GetMethodID(pair_class, "<init>", "(Ljava/lang/Object;Ljava/lang/Object;)V");
|
env->GetMethodID(pair_class, "<init>", "(Ljava/lang/Object;Ljava/lang/Object;)V");
|
||||||
s_pair_first_field = env->GetFieldID(pair_class, "first", "Ljava/lang/Object;");
|
state.pair_first_field = env->GetFieldID(pair_class, "first", "Ljava/lang/Object;");
|
||||||
s_pair_second_field = env->GetFieldID(pair_class, "second", "Ljava/lang/Object;");
|
state.pair_second_field = env->GetFieldID(pair_class, "second", "Ljava/lang/Object;");
|
||||||
env->DeleteLocalRef(pair_class);
|
env->DeleteLocalRef(pair_class);
|
||||||
|
|
||||||
const jclass overlay_control_data_class =
|
const jclass overlay_control_data_class =
|
||||||
env->FindClass("org/yuzu/yuzu_emu/overlay/model/OverlayControlData");
|
env->FindClass("org/yuzu/yuzu_emu/overlay/model/OverlayControlData");
|
||||||
s_overlay_control_data_class =
|
state.overlay_control_data_class =
|
||||||
reinterpret_cast<jclass>(env->NewGlobalRef(overlay_control_data_class));
|
reinterpret_cast<jclass>(env->NewGlobalRef(overlay_control_data_class));
|
||||||
s_overlay_control_data_constructor =
|
state.overlay_control_data_constructor =
|
||||||
env->GetMethodID(overlay_control_data_class, "<init>",
|
env->GetMethodID(overlay_control_data_class, "<init>",
|
||||||
"(Ljava/lang/String;ZLkotlin/Pair;Lkotlin/Pair;Lkotlin/Pair;F)V");
|
"(Ljava/lang/String;ZLkotlin/Pair;Lkotlin/Pair;Lkotlin/Pair;F)V");
|
||||||
s_overlay_control_data_id_field =
|
state.overlay_control_data_id_field =
|
||||||
env->GetFieldID(overlay_control_data_class, "id", "Ljava/lang/String;");
|
env->GetFieldID(overlay_control_data_class, "id", "Ljava/lang/String;");
|
||||||
s_overlay_control_data_enabled_field =
|
state.overlay_control_data_enabled_field =
|
||||||
env->GetFieldID(overlay_control_data_class, "enabled", "Z");
|
env->GetFieldID(overlay_control_data_class, "enabled", "Z");
|
||||||
s_overlay_control_data_landscape_position_field =
|
state.overlay_control_data_landscape_position_field =
|
||||||
env->GetFieldID(overlay_control_data_class, "landscapePosition", "Lkotlin/Pair;");
|
env->GetFieldID(overlay_control_data_class, "landscapePosition", "Lkotlin/Pair;");
|
||||||
s_overlay_control_data_portrait_position_field =
|
state.overlay_control_data_portrait_position_field =
|
||||||
env->GetFieldID(overlay_control_data_class, "portraitPosition", "Lkotlin/Pair;");
|
env->GetFieldID(overlay_control_data_class, "portraitPosition", "Lkotlin/Pair;");
|
||||||
s_overlay_control_data_foldable_position_field =
|
state.overlay_control_data_foldable_position_field =
|
||||||
env->GetFieldID(overlay_control_data_class, "foldablePosition", "Lkotlin/Pair;");
|
env->GetFieldID(overlay_control_data_class, "foldablePosition", "Lkotlin/Pair;");
|
||||||
s_overlay_control_data_individual_scale_field =
|
state.overlay_control_data_individual_scale_field =
|
||||||
env->GetFieldID(overlay_control_data_class, "individualScale", "F");
|
env->GetFieldID(overlay_control_data_class, "individualScale", "F");
|
||||||
env->DeleteLocalRef(overlay_control_data_class);
|
env->DeleteLocalRef(overlay_control_data_class);
|
||||||
|
|
||||||
const jclass patch_class = env->FindClass("org/yuzu/yuzu_emu/model/Patch");
|
const jclass patch_class = env->FindClass("org/yuzu/yuzu_emu/model/Patch");
|
||||||
s_patch_class = reinterpret_cast<jclass>(env->NewGlobalRef(patch_class));
|
state.patch_class = reinterpret_cast<jclass>(env->NewGlobalRef(patch_class));
|
||||||
s_patch_constructor = env->GetMethodID(
|
state.patch_constructor = env->GetMethodID(
|
||||||
patch_class, "<init>",
|
patch_class, "<init>",
|
||||||
"(ZLjava/lang/String;Ljava/lang/String;ILjava/lang/String;Ljava/lang/String;JI)V");
|
"(ZLjava/lang/String;Ljava/lang/String;ILjava/lang/String;Ljava/lang/String;JI)V");
|
||||||
s_patch_enabled_field = env->GetFieldID(patch_class, "enabled", "Z");
|
state.patch_enabled_field = env->GetFieldID(patch_class, "enabled", "Z");
|
||||||
s_patch_name_field = env->GetFieldID(patch_class, "name", "Ljava/lang/String;");
|
state.patch_name_field = env->GetFieldID(patch_class, "name", "Ljava/lang/String;");
|
||||||
s_patch_version_field = env->GetFieldID(patch_class, "version", "Ljava/lang/String;");
|
state.patch_version_field = env->GetFieldID(patch_class, "version", "Ljava/lang/String;");
|
||||||
s_patch_type_field = env->GetFieldID(patch_class, "type", "I");
|
state.patch_type_field = env->GetFieldID(patch_class, "type", "I");
|
||||||
s_patch_program_id_field = env->GetFieldID(patch_class, "programId", "Ljava/lang/String;");
|
state.patch_program_id_field = env->GetFieldID(patch_class, "programId", "Ljava/lang/String;");
|
||||||
s_patch_title_id_field = env->GetFieldID(patch_class, "titleId", "Ljava/lang/String;");
|
state.patch_title_id_field = env->GetFieldID(patch_class, "titleId", "Ljava/lang/String;");
|
||||||
env->DeleteLocalRef(patch_class);
|
env->DeleteLocalRef(patch_class);
|
||||||
|
|
||||||
const jclass double_class = env->FindClass("java/lang/Double");
|
const jclass double_class = env->FindClass("java/lang/Double");
|
||||||
s_double_class = reinterpret_cast<jclass>(env->NewGlobalRef(double_class));
|
state.double_class = reinterpret_cast<jclass>(env->NewGlobalRef(double_class));
|
||||||
s_double_constructor = env->GetMethodID(double_class, "<init>", "(D)V");
|
state.double_constructor = env->GetMethodID(double_class, "<init>", "(D)V");
|
||||||
s_double_value_method = env->GetMethodID(double_class, "doubleValue", "()D");
|
state.double_value_method = env->GetMethodID(double_class, "doubleValue", "()D");
|
||||||
env->DeleteLocalRef(double_class);
|
env->DeleteLocalRef(double_class);
|
||||||
|
|
||||||
const jclass int_class = env->FindClass("java/lang/Integer");
|
const jclass int_class = env->FindClass("java/lang/Integer");
|
||||||
s_integer_class = reinterpret_cast<jclass>(env->NewGlobalRef(int_class));
|
state.integer_class = reinterpret_cast<jclass>(env->NewGlobalRef(int_class));
|
||||||
s_integer_constructor = env->GetMethodID(int_class, "<init>", "(I)V");
|
state.integer_constructor = env->GetMethodID(int_class, "<init>", "(I)V");
|
||||||
s_integer_value_method = env->GetMethodID(int_class, "intValue", "()I");
|
state.integer_value_method = env->GetMethodID(int_class, "intValue", "()I");
|
||||||
env->DeleteLocalRef(int_class);
|
env->DeleteLocalRef(int_class);
|
||||||
|
|
||||||
const jclass boolean_class = env->FindClass("java/lang/Boolean");
|
const jclass boolean_class = env->FindClass("java/lang/Boolean");
|
||||||
s_boolean_class = reinterpret_cast<jclass>(env->NewGlobalRef(boolean_class));
|
state.boolean_class = reinterpret_cast<jclass>(env->NewGlobalRef(boolean_class));
|
||||||
s_boolean_constructor = env->GetMethodID(boolean_class, "<init>", "(Z)V");
|
state.boolean_constructor = env->GetMethodID(boolean_class, "<init>", "(Z)V");
|
||||||
s_boolean_value_method = env->GetMethodID(boolean_class, "booleanValue", "()Z");
|
state.boolean_value_method = env->GetMethodID(boolean_class, "booleanValue", "()Z");
|
||||||
env->DeleteLocalRef(boolean_class);
|
env->DeleteLocalRef(boolean_class);
|
||||||
|
|
||||||
const jclass player_input_class =
|
const jclass player_input_class =
|
||||||
env->FindClass("org/yuzu/yuzu_emu/features/input/model/PlayerInput");
|
env->FindClass("org/yuzu/yuzu_emu/features/input/model/PlayerInput");
|
||||||
s_player_input_class = reinterpret_cast<jclass>(env->NewGlobalRef(player_input_class));
|
state.player_input_class = reinterpret_cast<jclass>(env->NewGlobalRef(player_input_class));
|
||||||
s_player_input_constructor = env->GetMethodID(
|
state.player_input_constructor = env->GetMethodID(
|
||||||
player_input_class, "<init>",
|
player_input_class, "<init>",
|
||||||
"(Z[Ljava/lang/String;[Ljava/lang/String;[Ljava/lang/String;ZIJJJJLjava/lang/String;Z)V");
|
"(Z[Ljava/lang/String;[Ljava/lang/String;[Ljava/lang/String;ZIJJJJLjava/lang/String;Z)V");
|
||||||
s_player_input_connected_field = env->GetFieldID(player_input_class, "connected", "Z");
|
state.player_input_connected_field = env->GetFieldID(player_input_class, "connected", "Z");
|
||||||
s_player_input_buttons_field =
|
state.player_input_buttons_field =
|
||||||
env->GetFieldID(player_input_class, "buttons", "[Ljava/lang/String;");
|
env->GetFieldID(player_input_class, "buttons", "[Ljava/lang/String;");
|
||||||
s_player_input_analogs_field =
|
state.player_input_analogs_field =
|
||||||
env->GetFieldID(player_input_class, "analogs", "[Ljava/lang/String;");
|
env->GetFieldID(player_input_class, "analogs", "[Ljava/lang/String;");
|
||||||
s_player_input_motions_field =
|
state.player_input_motions_field =
|
||||||
env->GetFieldID(player_input_class, "motions", "[Ljava/lang/String;");
|
env->GetFieldID(player_input_class, "motions", "[Ljava/lang/String;");
|
||||||
s_player_input_vibration_enabled_field =
|
state.player_input_vibration_enabled_field =
|
||||||
env->GetFieldID(player_input_class, "vibrationEnabled", "Z");
|
env->GetFieldID(player_input_class, "vibrationEnabled", "Z");
|
||||||
s_player_input_vibration_strength_field =
|
state.player_input_vibration_strength_field =
|
||||||
env->GetFieldID(player_input_class, "vibrationStrength", "I");
|
env->GetFieldID(player_input_class, "vibrationStrength", "I");
|
||||||
s_player_input_body_color_left_field =
|
state.player_input_body_color_left_field =
|
||||||
env->GetFieldID(player_input_class, "bodyColorLeft", "J");
|
env->GetFieldID(player_input_class, "bodyColorLeft", "J");
|
||||||
s_player_input_body_color_right_field =
|
state.player_input_body_color_right_field =
|
||||||
env->GetFieldID(player_input_class, "bodyColorRight", "J");
|
env->GetFieldID(player_input_class, "bodyColorRight", "J");
|
||||||
s_player_input_button_color_left_field =
|
state.player_input_button_color_left_field =
|
||||||
env->GetFieldID(player_input_class, "buttonColorLeft", "J");
|
env->GetFieldID(player_input_class, "buttonColorLeft", "J");
|
||||||
s_player_input_button_color_right_field =
|
state.player_input_button_color_right_field =
|
||||||
env->GetFieldID(player_input_class, "buttonColorRight", "J");
|
env->GetFieldID(player_input_class, "buttonColorRight", "J");
|
||||||
s_player_input_profile_name_field =
|
state.player_input_profile_name_field =
|
||||||
env->GetFieldID(player_input_class, "profileName", "Ljava/lang/String;");
|
env->GetFieldID(player_input_class, "profileName", "Ljava/lang/String;");
|
||||||
s_player_input_use_system_vibrator_field =
|
state.player_input_use_system_vibrator_field =
|
||||||
env->GetFieldID(player_input_class, "useSystemVibrator", "Z");
|
env->GetFieldID(player_input_class, "useSystemVibrator", "Z");
|
||||||
env->DeleteLocalRef(player_input_class);
|
env->DeleteLocalRef(player_input_class);
|
||||||
|
|
||||||
const jclass yuzu_input_device_interface =
|
const jclass yuzu_input_device_interface =
|
||||||
env->FindClass("org/yuzu/yuzu_emu/features/input/YuzuInputDevice");
|
env->FindClass("org/yuzu/yuzu_emu/features/input/YuzuInputDevice");
|
||||||
s_yuzu_input_device_interface =
|
state.yuzu_input_device_interface =
|
||||||
reinterpret_cast<jclass>(env->NewGlobalRef(yuzu_input_device_interface));
|
reinterpret_cast<jclass>(env->NewGlobalRef(yuzu_input_device_interface));
|
||||||
s_yuzu_input_device_get_name =
|
state.yuzu_input_device_get_name =
|
||||||
env->GetMethodID(yuzu_input_device_interface, "getName", "()Ljava/lang/String;");
|
env->GetMethodID(yuzu_input_device_interface, "getName", "()Ljava/lang/String;");
|
||||||
s_yuzu_input_device_get_guid =
|
state.yuzu_input_device_get_guid =
|
||||||
env->GetMethodID(yuzu_input_device_interface, "getGUID", "()Ljava/lang/String;");
|
env->GetMethodID(yuzu_input_device_interface, "getGUID", "()Ljava/lang/String;");
|
||||||
s_yuzu_input_device_get_port = env->GetMethodID(yuzu_input_device_interface, "getPort",
|
state.yuzu_input_device_get_port = env->GetMethodID(yuzu_input_device_interface, "getPort",
|
||||||
"()I");
|
"()I");
|
||||||
s_yuzu_input_device_get_supports_vibration =
|
state.yuzu_input_device_get_supports_vibration =
|
||||||
env->GetMethodID(yuzu_input_device_interface, "getSupportsVibration", "()Z");
|
env->GetMethodID(yuzu_input_device_interface, "getSupportsVibration", "()Z");
|
||||||
s_yuzu_input_device_vibrate = env->GetMethodID(yuzu_input_device_interface, "vibrate",
|
state.yuzu_input_device_vibrate = env->GetMethodID(yuzu_input_device_interface, "vibrate",
|
||||||
"(F)V");
|
"(F)V");
|
||||||
s_yuzu_input_device_get_axes =
|
state.yuzu_input_device_get_axes =
|
||||||
env->GetMethodID(yuzu_input_device_interface, "getAxes", "()[Ljava/lang/Integer;");
|
env->GetMethodID(yuzu_input_device_interface, "getAxes", "()[Ljava/lang/Integer;");
|
||||||
s_yuzu_input_device_has_keys =
|
state.yuzu_input_device_has_keys =
|
||||||
env->GetMethodID(yuzu_input_device_interface, "hasKeys", "([I)[Z");
|
env->GetMethodID(yuzu_input_device_interface, "hasKeys", "([I)[Z");
|
||||||
env->DeleteLocalRef(yuzu_input_device_interface);
|
env->DeleteLocalRef(yuzu_input_device_interface);
|
||||||
s_add_netplay_message = env->GetStaticMethodID(s_native_library_class, "addNetPlayMessage",
|
state.add_netplay_message = env->GetStaticMethodID(state.native_library_class, "addNetPlayMessage",
|
||||||
"(ILjava/lang/String;)V");
|
"(ILjava/lang/String;)V");
|
||||||
s_clear_chat = env->GetStaticMethodID(s_native_library_class, "clearChat", "()V");
|
state.clear_chat = env->GetStaticMethodID(state.native_library_class, "clearChat", "()V");
|
||||||
|
|
||||||
// Initialize Android Storage
|
// Initialize Android Storage
|
||||||
Common::FS::Android::RegisterCallbacks(env, s_native_library_class);
|
Common::FS::Android::RegisterCallbacks(env, state.native_library_class);
|
||||||
|
|
||||||
// Initialize applets
|
// Initialize applets
|
||||||
Common::Android::SoftwareKeyboard::InitJNI(env);
|
Common::Android::SoftwareKeyboard::InitJNI(env);
|
||||||
|
|||||||
+4
-18
@@ -22,36 +22,22 @@ template <typename T>
|
|||||||
return std::size_t(sizeof(T) * CHAR_BIT);
|
return std::size_t(sizeof(T) * CHAR_BIT);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
requires std::is_integral_v<T>
|
|
||||||
[[nodiscard]] constexpr u32 MostSignificantBit(const T value) {
|
|
||||||
return u32(sizeof(T) * CHAR_BIT - 1 - std::countl_zero(value));
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
requires std::is_integral_v<T>
|
requires std::is_integral_v<T>
|
||||||
[[nodiscard]] constexpr T Log2Floor(const T value) {
|
[[nodiscard]] constexpr T Log2Floor(const T value) {
|
||||||
return T(MostSignificantBit<T>(value));
|
return std::bit_width(value) - 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
requires std::is_integral_v<T>
|
requires std::is_integral_v<T>
|
||||||
[[nodiscard]] constexpr T Log2Ceil(const T value) {
|
[[nodiscard]] constexpr T Log2Ceil(const T value) {
|
||||||
const T log2_f = Log2Floor<T>(value);
|
return std::bit_width(value - 1);
|
||||||
return T(log2_f + T((value ^ (T(1ULL) << log2_f)) != T(0ULL)));
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
requires std::is_integral_v<T>
|
|
||||||
[[nodiscard]] T NextPow2(T value) {
|
|
||||||
return T(1ULL << (sizeof(T) * CHAR_BIT - std::countl_zero(value - 1U)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template <size_t bit_index, typename T>
|
template <size_t bit_index, typename T>
|
||||||
requires std::is_integral_v<T>
|
requires (std::is_integral_v<T> && bit_index < BitSize<T>())
|
||||||
[[nodiscard]] constexpr bool Bit(const T value) {
|
[[nodiscard]] constexpr bool Bit(const T value) {
|
||||||
static_assert(bit_index < BitSize<T>(), "bit_index must be smaller than size of T");
|
return (T(value >> bit_index) & T(1)) == T(1);
|
||||||
return ((value >> bit_index) & T(1)) == T(1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace Common
|
} // namespace Common
|
||||||
|
|||||||
@@ -25,26 +25,18 @@ template <typename T>
|
|||||||
requires std::is_unsigned_v<T>
|
requires std::is_unsigned_v<T>
|
||||||
inline std::size_t HashValue(T val) {
|
inline std::size_t HashValue(T val) {
|
||||||
const unsigned int size_t_bits = std::numeric_limits<std::size_t>::digits;
|
const unsigned int size_t_bits = std::numeric_limits<std::size_t>::digits;
|
||||||
const unsigned int length =
|
const unsigned int length = (std::numeric_limits<T>::digits - 1) / static_cast<unsigned int>(size_t_bits);
|
||||||
(std::numeric_limits<T>::digits - 1) / static_cast<unsigned int>(size_t_bits);
|
|
||||||
|
|
||||||
std::size_t seed = 0;
|
std::size_t seed = 0;
|
||||||
|
for (unsigned int i = length * size_t_bits; i > 0; i -= size_t_bits)
|
||||||
for (unsigned int i = length * size_t_bits; i > 0; i -= size_t_bits) {
|
seed ^= std::size_t(val >> i) + (seed << 6) + (seed >> 2);
|
||||||
seed ^= static_cast<size_t>(val >> i) + (seed << 6) + (seed >> 2);
|
return seed ^= std::size_t(val) + (seed << 6) + (seed >> 2);
|
||||||
}
|
|
||||||
|
|
||||||
seed ^= static_cast<size_t>(val) + (seed << 6) + (seed >> 2);
|
|
||||||
|
|
||||||
return seed;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template <size_t Bits>
|
template <size_t Bits>
|
||||||
struct HashCombineImpl {
|
struct HashCombineImpl {
|
||||||
template <typename T>
|
template <typename T>
|
||||||
static inline T fn(T seed, T value) {
|
static inline T fn(T seed, T value) {
|
||||||
seed ^= value + 0x9e3779b9 + (seed << 6) + (seed >> 2);
|
return seed ^= value + 0x9e3779b9 + (seed << 6) + (seed >> 2);
|
||||||
return seed;
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,6 @@
|
|||||||
|
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
@@ -12,14 +15,14 @@ namespace Common {
|
|||||||
template <typename N, typename D>
|
template <typename N, typename D>
|
||||||
requires std::is_integral_v<N> && std::is_unsigned_v<D>
|
requires std::is_integral_v<N> && std::is_unsigned_v<D>
|
||||||
[[nodiscard]] constexpr N DivCeil(N number, D divisor) {
|
[[nodiscard]] constexpr N DivCeil(N number, D divisor) {
|
||||||
return static_cast<N>((static_cast<D>(number) + divisor - 1) / divisor);
|
return N((D(number) + divisor - 1) / divisor);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Ceiled integer division with logarithmic divisor in base 2
|
/// Ceiled integer division with logarithmic divisor in base 2
|
||||||
template <typename N, typename D>
|
template <typename N, typename D>
|
||||||
requires std::is_integral_v<N> && std::is_unsigned_v<D>
|
requires std::is_integral_v<N> && std::is_unsigned_v<D>
|
||||||
[[nodiscard]] constexpr N DivCeilLog2(N value, D alignment_log2) {
|
[[nodiscard]] constexpr N DivCeilLog2(N value, D alignment_log2) {
|
||||||
return static_cast<N>((static_cast<D>(value) + (D(1) << alignment_log2) - 1) >> alignment_log2);
|
return N((D(value) + (D(1) << alignment_log2) - 1) >> alignment_log2);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace Common
|
} // namespace Common
|
||||||
|
|||||||
@@ -1,28 +0,0 @@
|
|||||||
// SPDX-FileCopyrightText: 2015 Citra Emulator Project
|
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <cstddef>
|
|
||||||
#include <utility>
|
|
||||||
#include <boost/functional/hash.hpp>
|
|
||||||
|
|
||||||
namespace Common {
|
|
||||||
|
|
||||||
struct PairHash {
|
|
||||||
template <class T1, class T2>
|
|
||||||
std::size_t operator()(const std::pair<T1, T2>& pair) const noexcept {
|
|
||||||
std::size_t seed = std::hash<T1>()(pair.first);
|
|
||||||
boost::hash_combine(seed, std::hash<T2>()(pair.second));
|
|
||||||
return seed;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
struct IdentityHash {
|
|
||||||
[[nodiscard]] size_t operator()(T value) const noexcept {
|
|
||||||
return static_cast<size_t>(value);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace Common
|
|
||||||
+10
-16
@@ -1,3 +1,6 @@
|
|||||||
|
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: 2013 Dolphin Emulator Project
|
// SPDX-FileCopyrightText: 2013 Dolphin Emulator Project
|
||||||
// SPDX-FileCopyrightText: 2014 Citra Emulator Project
|
// SPDX-FileCopyrightText: 2014 Citra Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
@@ -16,14 +19,12 @@ namespace Common {
|
|||||||
|
|
||||||
[[nodiscard]] constexpr u8 ToHexNibble(char c) {
|
[[nodiscard]] constexpr u8 ToHexNibble(char c) {
|
||||||
if (c >= 65 && c <= 70) {
|
if (c >= 65 && c <= 70) {
|
||||||
return static_cast<u8>(c - 55);
|
return u8(c - 55);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (c >= 97 && c <= 102) {
|
if (c >= 97 && c <= 102) {
|
||||||
return static_cast<u8>(c - 87);
|
return u8(c - 87);
|
||||||
}
|
}
|
||||||
|
return u8(c - 48);
|
||||||
return static_cast<u8>(c - 48);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] std::vector<u8> HexStringToVector(std::string_view str, bool little_endian);
|
[[nodiscard]] std::vector<u8> HexStringToVector(std::string_view str, bool little_endian);
|
||||||
@@ -31,35 +32,28 @@ namespace Common {
|
|||||||
template <std::size_t Size, bool le = false>
|
template <std::size_t Size, bool le = false>
|
||||||
[[nodiscard]] constexpr std::array<u8, Size> HexStringToArray(std::string_view str) {
|
[[nodiscard]] constexpr std::array<u8, Size> HexStringToArray(std::string_view str) {
|
||||||
ASSERT_MSG(Size * 2 <= str.size(), "Invalid string size");
|
ASSERT_MSG(Size * 2 <= str.size(), "Invalid string size");
|
||||||
|
|
||||||
std::array<u8, Size> out{};
|
std::array<u8, Size> out{};
|
||||||
if constexpr (le) {
|
if constexpr (le) {
|
||||||
for (std::size_t i = 2 * Size - 2; i <= 2 * Size; i -= 2) {
|
for (std::size_t i = 2 * Size - 2; i <= 2 * Size; i -= 2) {
|
||||||
out[i / 2] = static_cast<u8>((ToHexNibble(str[i]) << 4) | ToHexNibble(str[i + 1]));
|
out[i / 2] = u8((ToHexNibble(str[i]) << 4) | ToHexNibble(str[i + 1]));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
for (std::size_t i = 0; i < 2 * Size; i += 2) {
|
for (std::size_t i = 0; i < 2 * Size; i += 2) {
|
||||||
out[i / 2] = static_cast<u8>((ToHexNibble(str[i]) << 4) | ToHexNibble(str[i + 1]));
|
out[i / 2] = u8((ToHexNibble(str[i]) << 4) | ToHexNibble(str[i + 1]));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename ContiguousContainer>
|
template <typename ContiguousContainer>
|
||||||
|
requires std::is_same_v<typename ContiguousContainer::value_type, u8>
|
||||||
[[nodiscard]] std::string HexToString(const ContiguousContainer& data, bool upper = true) {
|
[[nodiscard]] std::string HexToString(const ContiguousContainer& data, bool upper = true) {
|
||||||
static_assert(std::is_same_v<typename ContiguousContainer::value_type, u8>,
|
|
||||||
"Underlying type within the contiguous container must be u8.");
|
|
||||||
|
|
||||||
constexpr std::size_t pad_width = 2;
|
constexpr std::size_t pad_width = 2;
|
||||||
|
|
||||||
std::string out;
|
std::string out;
|
||||||
out.reserve(std::size(data) * pad_width);
|
out.reserve(std::size(data) * pad_width);
|
||||||
|
|
||||||
const auto format_str = fmt::runtime(upper ? "{:02X}" : "{:02x}");
|
const auto format_str = fmt::runtime(upper ? "{:02X}" : "{:02x}");
|
||||||
for (const u8 c : data) {
|
for (const u8 c : data)
|
||||||
out += fmt::format(format_str, c);
|
out += fmt::format(format_str, c);
|
||||||
}
|
|
||||||
|
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+10
-6
@@ -10,6 +10,7 @@
|
|||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
#include <regex>
|
#include <regex>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
|
#include <boost/algorithm/string/predicate.hpp>
|
||||||
|
|
||||||
#if defined(__ANDROID__)
|
#if defined(__ANDROID__)
|
||||||
#include <android/log.h>
|
#include <android/log.h>
|
||||||
@@ -95,9 +96,10 @@ std::string FormatLogMessage(const Entry& entry) noexcept {
|
|||||||
|
|
||||||
template <typename It>
|
template <typename It>
|
||||||
Level GetLevelByName(const It begin, const It end) {
|
Level GetLevelByName(const It begin, const It end) {
|
||||||
|
std::string_view const sv{begin, end};
|
||||||
for (u32 i = 0; i < u32(Level::Count); ++i) {
|
for (u32 i = 0; i < u32(Level::Count); ++i) {
|
||||||
const char* level_name = GetLevelName(Level(i));
|
auto const level_name = GetLevelName(Level(i));
|
||||||
if (Common::ComparePartialString(begin, end, level_name))
|
if (boost::iequals(sv, level_name))
|
||||||
return Level(i);
|
return Level(i);
|
||||||
}
|
}
|
||||||
return Level::Count;
|
return Level::Count;
|
||||||
@@ -105,9 +107,10 @@ Level GetLevelByName(const It begin, const It end) {
|
|||||||
|
|
||||||
template <typename It>
|
template <typename It>
|
||||||
Class GetClassByName(const It begin, const It end) {
|
Class GetClassByName(const It begin, const It end) {
|
||||||
|
std::string_view const sv{begin, end};
|
||||||
for (u32 i = 0; i < u32(Class::Count); ++i) {
|
for (u32 i = 0; i < u32(Class::Count); ++i) {
|
||||||
const char* level_name = GetLogClassName(Class(i));
|
auto const level_name = GetLogClassName(Class(i));
|
||||||
if (Common::ComparePartialString(begin, end, level_name))
|
if (boost::iequals(sv, level_name))
|
||||||
return Class(i);
|
return Class(i);
|
||||||
}
|
}
|
||||||
return Class::Count;
|
return Class::Count;
|
||||||
@@ -120,12 +123,13 @@ bool ParseFilterRule(Filter& instance, Iterator begin, Iterator end) {
|
|||||||
LOG_ERROR(Log, "Invalid log filter. Must specify a log level after `:`: {}", std::string(begin, end));
|
LOG_ERROR(Log, "Invalid log filter. Must specify a log level after `:`: {}", std::string(begin, end));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
const Level level = GetLevelByName(level_separator + 1, end);
|
auto const sv = std::string_view{begin, level_separator};
|
||||||
|
auto const level = GetLevelByName(level_separator + 1, end);
|
||||||
if (level == Level::Count) {
|
if (level == Level::Count) {
|
||||||
LOG_ERROR(Log, "Unknown log level in filter: {}", std::string(begin, end));
|
LOG_ERROR(Log, "Unknown log level in filter: {}", std::string(begin, end));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (Common::ComparePartialString(begin, level_separator, "*")) {
|
if (boost::iequals(sv, "*")) {
|
||||||
instance.class_levels.fill(level);
|
instance.class_levels.fill(level);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,27 +0,0 @@
|
|||||||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <memory>
|
|
||||||
#include <type_traits>
|
|
||||||
|
|
||||||
namespace Common {
|
|
||||||
|
|
||||||
template <class T>
|
|
||||||
requires(!std::is_array_v<T>)
|
|
||||||
std::unique_ptr<T> make_unique_for_overwrite() {
|
|
||||||
return std::unique_ptr<T>(new T);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <class T>
|
|
||||||
requires std::is_unbounded_array_v<T>
|
|
||||||
std::unique_ptr<T> make_unique_for_overwrite(std::size_t n) {
|
|
||||||
return std::unique_ptr<T>(new std::remove_extent_t<T>[n]);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <class T, class... Args>
|
|
||||||
requires std::is_bounded_array_v<T>
|
|
||||||
void make_unique_for_overwrite(Args&&...) = delete;
|
|
||||||
|
|
||||||
} // namespace Common
|
|
||||||
@@ -8,6 +8,9 @@
|
|||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
#include <boost/algorithm/string/classification.hpp>
|
||||||
|
#include <boost/algorithm/string/replace.hpp>
|
||||||
|
#include <boost/algorithm/string/split.hpp>
|
||||||
|
|
||||||
#include "common/logging.h"
|
#include "common/logging.h"
|
||||||
#include "common/param_package.h"
|
#include "common/param_package.h"
|
||||||
@@ -15,17 +18,16 @@
|
|||||||
|
|
||||||
namespace Common {
|
namespace Common {
|
||||||
|
|
||||||
constexpr char KEY_VALUE_SEPARATOR = ':';
|
constexpr auto KEY_VALUE_SEPARATOR = ":";
|
||||||
constexpr char PARAM_SEPARATOR = ',';
|
constexpr auto PARAM_SEPARATOR = ",";
|
||||||
|
constexpr auto ESCAPE_CHARACTER = "$";
|
||||||
constexpr char ESCAPE_CHARACTER = '$';
|
constexpr auto KEY_VALUE_SEPARATOR_ESCAPE = "$0";
|
||||||
constexpr char KEY_VALUE_SEPARATOR_ESCAPE[] = "$0";
|
constexpr auto PARAM_SEPARATOR_ESCAPE = "$1";
|
||||||
constexpr char PARAM_SEPARATOR_ESCAPE[] = "$1";
|
constexpr auto ESCAPE_CHARACTER_ESCAPE = "$2";
|
||||||
constexpr char ESCAPE_CHARACTER_ESCAPE[] = "$2";
|
|
||||||
|
|
||||||
/// A placeholder for empty param packages to avoid empty strings
|
/// A placeholder for empty param packages to avoid empty strings
|
||||||
/// (they may be recognized as "not set" by some frontend libraries like qt)
|
/// (they may be recognized as "not set" by some frontend libraries like qt)
|
||||||
constexpr char EMPTY_PLACEHOLDER[] = "[empty]";
|
constexpr auto EMPTY_PLACEHOLDER = "[empty]";
|
||||||
|
|
||||||
ParamPackage::ParamPackage(const std::string& serialized) {
|
ParamPackage::ParamPackage(const std::string& serialized) {
|
||||||
if (serialized == EMPTY_PLACEHOLDER) {
|
if (serialized == EMPTY_PLACEHOLDER) {
|
||||||
@@ -33,20 +35,20 @@ ParamPackage::ParamPackage(const std::string& serialized) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
std::vector<std::string> pairs;
|
std::vector<std::string> pairs;
|
||||||
Common::SplitString(serialized, PARAM_SEPARATOR, pairs);
|
boost::split(pairs, serialized, boost::is_any_of(PARAM_SEPARATOR));
|
||||||
|
|
||||||
for (const std::string& pair : pairs) {
|
for (const std::string& pair : pairs) {
|
||||||
std::vector<std::string> key_value;
|
std::vector<std::string> key_value;
|
||||||
Common::SplitString(pair, KEY_VALUE_SEPARATOR, key_value);
|
boost::split(key_value, pair, boost::is_any_of(KEY_VALUE_SEPARATOR));
|
||||||
if (key_value.size() != 2) {
|
if (key_value.size() != 2) {
|
||||||
LOG_ERROR(Common, "invalid key pair {}", pair);
|
LOG_ERROR(Common, "invalid key pair {}", pair);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (std::string& part : key_value) {
|
for (auto& part : key_value) {
|
||||||
part = Common::ReplaceAll(part, KEY_VALUE_SEPARATOR_ESCAPE, {KEY_VALUE_SEPARATOR});
|
boost::replace_all(part, KEY_VALUE_SEPARATOR_ESCAPE, KEY_VALUE_SEPARATOR);
|
||||||
part = Common::ReplaceAll(part, PARAM_SEPARATOR_ESCAPE, {PARAM_SEPARATOR});
|
boost::replace_all(part, PARAM_SEPARATOR_ESCAPE, PARAM_SEPARATOR);
|
||||||
part = Common::ReplaceAll(part, ESCAPE_CHARACTER_ESCAPE, {ESCAPE_CHARACTER});
|
boost::replace_all(part, ESCAPE_CHARACTER_ESCAPE, ESCAPE_CHARACTER);
|
||||||
}
|
}
|
||||||
|
|
||||||
Set(key_value[0], std::move(key_value[1]));
|
Set(key_value[0], std::move(key_value[1]));
|
||||||
@@ -63,10 +65,10 @@ std::string ParamPackage::Serialize() const {
|
|||||||
|
|
||||||
for (const auto& pair : data) {
|
for (const auto& pair : data) {
|
||||||
std::array<std::string, 2> key_value{{pair.first, pair.second}};
|
std::array<std::string, 2> key_value{{pair.first, pair.second}};
|
||||||
for (std::string& part : key_value) {
|
for (auto& part : key_value) {
|
||||||
part = Common::ReplaceAll(part, {ESCAPE_CHARACTER}, ESCAPE_CHARACTER_ESCAPE);
|
boost::replace_all(part, ESCAPE_CHARACTER, ESCAPE_CHARACTER_ESCAPE);
|
||||||
part = Common::ReplaceAll(part, {PARAM_SEPARATOR}, PARAM_SEPARATOR_ESCAPE);
|
boost::replace_all(part, PARAM_SEPARATOR, PARAM_SEPARATOR_ESCAPE);
|
||||||
part = Common::ReplaceAll(part, {KEY_VALUE_SEPARATOR}, KEY_VALUE_SEPARATOR_ESCAPE);
|
boost::replace_all(part, KEY_VALUE_SEPARATOR, KEY_VALUE_SEPARATOR_ESCAPE);
|
||||||
}
|
}
|
||||||
result += key_value[0] + KEY_VALUE_SEPARATOR + key_value[1] + PARAM_SEPARATOR;
|
result += key_value[0] + KEY_VALUE_SEPARATOR + key_value[1] + PARAM_SEPARATOR;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,8 +8,7 @@
|
|||||||
|
|
||||||
#include <iterator>
|
#include <iterator>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
#include <memory>
|
||||||
#include "common/make_unique_for_overwrite.h"
|
|
||||||
|
|
||||||
namespace Common {
|
namespace Common {
|
||||||
|
|
||||||
@@ -38,8 +37,9 @@ public:
|
|||||||
ScratchBuffer() = default;
|
ScratchBuffer() = default;
|
||||||
|
|
||||||
explicit ScratchBuffer(size_type initial_capacity)
|
explicit ScratchBuffer(size_type initial_capacity)
|
||||||
: last_requested_size{initial_capacity}, buffer_capacity{initial_capacity},
|
: last_requested_size{initial_capacity}
|
||||||
buffer{Common::make_unique_for_overwrite<T[]>(initial_capacity)} {}
|
, buffer_capacity{initial_capacity}
|
||||||
|
, buffer{std::make_unique_for_overwrite<T[]>(initial_capacity)} {}
|
||||||
|
|
||||||
~ScratchBuffer() = default;
|
~ScratchBuffer() = default;
|
||||||
ScratchBuffer(const ScratchBuffer&) = delete;
|
ScratchBuffer(const ScratchBuffer&) = delete;
|
||||||
@@ -64,7 +64,7 @@ public:
|
|||||||
/// The previously held data will remain intact.
|
/// The previously held data will remain intact.
|
||||||
void resize(size_type size) {
|
void resize(size_type size) {
|
||||||
if (size > buffer_capacity) {
|
if (size > buffer_capacity) {
|
||||||
auto new_buffer = Common::make_unique_for_overwrite<T[]>(size);
|
auto new_buffer = std::make_unique_for_overwrite<T[]>(size);
|
||||||
std::memcpy(new_buffer.get(), buffer.get(), buffer_capacity * sizeof(T));
|
std::memcpy(new_buffer.get(), buffer.get(), buffer_capacity * sizeof(T));
|
||||||
buffer = std::move(new_buffer);
|
buffer = std::move(new_buffer);
|
||||||
buffer_capacity = size;
|
buffer_capacity = size;
|
||||||
@@ -77,7 +77,7 @@ public:
|
|||||||
void resize_destructive(size_type size) {
|
void resize_destructive(size_type size) {
|
||||||
if (size > buffer_capacity) {
|
if (size > buffer_capacity) {
|
||||||
buffer_capacity = size;
|
buffer_capacity = size;
|
||||||
buffer = Common::make_unique_for_overwrite<T[]>(buffer_capacity);
|
buffer = std::make_unique_for_overwrite<T[]>(buffer_capacity);
|
||||||
}
|
}
|
||||||
last_requested_size = size;
|
last_requested_size = size;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -178,10 +178,6 @@ bool IsGPUFenceBehaviorAccurate() {
|
|||||||
return values.gpu_fence_behavior.GetValue() == GpuFenceBehavior::Accurate;
|
return values.gpu_fence_behavior.GetValue() == GpuFenceBehavior::Accurate;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool IsGPUFenceBehaviorStrict() {
|
|
||||||
return values.gpu_fence_behavior.GetValue() == GpuFenceBehavior::Strict;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool IsFastmemEnabled() {
|
bool IsFastmemEnabled() {
|
||||||
if (values.cpu_accuracy.GetValue() == Settings::CpuAccuracy::Debugging)
|
if (values.cpu_accuracy.GetValue() == Settings::CpuAccuracy::Debugging)
|
||||||
return bool(values.cpuopt_fastmem);
|
return bool(values.cpuopt_fastmem);
|
||||||
|
|||||||
@@ -525,7 +525,7 @@ struct Values {
|
|||||||
SwitchableSetting<GpuFenceBehavior, true> gpu_fence_behavior{linkage,
|
SwitchableSetting<GpuFenceBehavior, true> gpu_fence_behavior{linkage,
|
||||||
GpuFenceBehavior::Default,
|
GpuFenceBehavior::Default,
|
||||||
GpuFenceBehavior::Default,
|
GpuFenceBehavior::Default,
|
||||||
GpuFenceBehavior::Strict,
|
GpuFenceBehavior::Accurate,
|
||||||
"gpu_fence_behavior",
|
"gpu_fence_behavior",
|
||||||
Category::RendererAdvanced,
|
Category::RendererAdvanced,
|
||||||
Specialization::Default,
|
Specialization::Default,
|
||||||
@@ -655,13 +655,6 @@ struct Values {
|
|||||||
|
|
||||||
SwitchableSetting<bool> rescale_hack{linkage, false, "rescale_hack",
|
SwitchableSetting<bool> rescale_hack{linkage, false, "rescale_hack",
|
||||||
Category::RendererHacks};
|
Category::RendererHacks};
|
||||||
SwitchableSetting<bool> enable_gpu_buffer_readback{linkage,
|
|
||||||
false,
|
|
||||||
"enable_gpu_buffer_readback",
|
|
||||||
Category::RendererAdvanced,
|
|
||||||
Specialization::Default,
|
|
||||||
true,
|
|
||||||
true};
|
|
||||||
|
|
||||||
SwitchableSetting<bool> use_asynchronous_shaders{linkage, false, "use_asynchronous_shaders",
|
SwitchableSetting<bool> use_asynchronous_shaders{linkage, false, "use_asynchronous_shaders",
|
||||||
Category::RendererHacks};
|
Category::RendererHacks};
|
||||||
@@ -979,7 +972,6 @@ bool IsDMALevelSafe();
|
|||||||
bool IsGPUFenceBehaviorDefault();
|
bool IsGPUFenceBehaviorDefault();
|
||||||
bool IsGPUFenceBehaviorBalanced();
|
bool IsGPUFenceBehaviorBalanced();
|
||||||
bool IsGPUFenceBehaviorAccurate();
|
bool IsGPUFenceBehaviorAccurate();
|
||||||
bool IsGPUFenceBehaviorStrict();
|
|
||||||
|
|
||||||
bool IsFastmemEnabled();
|
bool IsFastmemEnabled();
|
||||||
void SetNceEnabled(bool is_64bit);
|
void SetNceEnabled(bool is_64bit);
|
||||||
|
|||||||
@@ -137,7 +137,7 @@ ENUM(VramUsageMode, Conservative, Aggressive);
|
|||||||
ENUM(RendererBackend, OpenGL_GLSL, Vulkan, Null, OpenGL_GLASM, OpenGL_SPIRV);
|
ENUM(RendererBackend, OpenGL_GLSL, Vulkan, Null, OpenGL_GLASM, OpenGL_SPIRV);
|
||||||
ENUM(GpuAccuracy, Low, High);
|
ENUM(GpuAccuracy, Low, High);
|
||||||
ENUM(DmaAccuracy, Default, Unsafe, Safe);
|
ENUM(DmaAccuracy, Default, Unsafe, Safe);
|
||||||
ENUM(GpuFenceBehavior, Default, Immediate, Balanced, Accurate, Strict);
|
ENUM(GpuFenceBehavior, Default, Immediate, Balanced, Accurate);
|
||||||
ENUM(CpuBackend, Dynarmic, Nce);
|
ENUM(CpuBackend, Dynarmic, Nce);
|
||||||
ENUM(CpuAccuracy, Auto, Accurate, Unsafe, Paranoid, Debugging);
|
ENUM(CpuAccuracy, Auto, Accurate, Unsafe, Paranoid, Debugging);
|
||||||
ENUM(CpuClock, Normal, Boost, Overclock)
|
ENUM(CpuClock, Normal, Boost, Overclock)
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
|
#include <algorithm>
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
#else
|
#else
|
||||||
@@ -20,19 +21,22 @@ namespace Common {
|
|||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
static std::vector<std::pair<u64, u64>> vector_regions {};
|
static std::vector<std::pair<u64, u64>> vector_regions {};
|
||||||
|
static std::mutex vector_regions_mutex {};
|
||||||
|
|
||||||
// Workaround for handling non-commited memory accessed by Dynarmic; usually result of an error
|
// Workaround for handling non-commited memory accessed by Dynarmic; usually result of an error
|
||||||
static LONG WINAPI FakePageFaultHandler(PEXCEPTION_POINTERS info) {
|
static LONG WINAPI FakePageFaultHandler(PEXCEPTION_POINTERS info) {
|
||||||
DWORD code = info->ExceptionRecord->ExceptionCode;
|
DWORD code = info->ExceptionRecord->ExceptionCode;
|
||||||
u64 exception_addr = reinterpret_cast<u64>(info->ExceptionRecord->ExceptionAddress);
|
u64 exception_addr = reinterpret_cast<u64>(info->ExceptionRecord->ExceptionInformation[1]);
|
||||||
|
|
||||||
if (code != EXCEPTION_ACCESS_VIOLATION) {
|
if (code != EXCEPTION_ACCESS_VIOLATION || info->ExceptionRecord->ExceptionInformation[0] == 1) {
|
||||||
// Not our problem
|
// Not our problem
|
||||||
return EXCEPTION_CONTINUE_SEARCH;
|
return EXCEPTION_CONTINUE_SEARCH;
|
||||||
}
|
}
|
||||||
|
|
||||||
u64 addr = 0, addr2 = 0;
|
u64 addr = 0, addr2 = 0;
|
||||||
|
|
||||||
|
{
|
||||||
|
std::lock_guard lock(vector_regions_mutex);
|
||||||
for (auto region: vector_regions) {
|
for (auto region: vector_regions) {
|
||||||
auto addr_shifted = exception_addr >> HostPageBits;
|
auto addr_shifted = exception_addr >> HostPageBits;
|
||||||
if (region.first <= addr_shifted && addr_shifted <= region.second) {
|
if (region.first <= addr_shifted && addr_shifted <= region.second) {
|
||||||
@@ -48,6 +52,7 @@ static LONG WINAPI FakePageFaultHandler(PEXCEPTION_POINTERS info) {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (addr == 0 && addr2 == 0) {
|
if (addr == 0 && addr2 == 0) {
|
||||||
// Not our problem
|
// Not our problem
|
||||||
@@ -77,8 +82,16 @@ bool CommitVectorPage(uintptr_t addr, bool write) noexcept {
|
|||||||
auto res = VirtualQuery(reinterpret_cast<void*>(addr), &info, sizeof(info));
|
auto res = VirtualQuery(reinterpret_cast<void*>(addr), &info, sizeof(info));
|
||||||
if (res == 0) {
|
if (res == 0) {
|
||||||
LOG_CRITICAL(HW_Memory, "Failed to query large buffer region at {:#x} with error {}, will try committing anyway", addr, GetLastError());
|
LOG_CRITICAL(HW_Memory, "Failed to query large buffer region at {:#x} with error {}, will try committing anyway", addr, GetLastError());
|
||||||
|
} else if (info.State == MEM_COMMIT) {
|
||||||
|
DWORD old_protect {};
|
||||||
|
auto perm = write ? PAGE_READWRITE : PAGE_READONLY;
|
||||||
|
if (!VirtualProtect(reinterpret_cast<void*>(addr), HostPageSize, perm, &old_protect)) {
|
||||||
|
LOG_ERROR(HW_Memory, "Failed to change permissions of large buffer region at {:#x}, error {}", addr, GetLastError());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
} else if (info.State != MEM_RESERVE) {
|
} else if (info.State != MEM_RESERVE) {
|
||||||
LOG_ERROR(HW_Memory, "Tried to commit an unreserved large buffer region at {:#x} that is not mapped or is already committed (state {:#x})", addr, info.State);
|
LOG_ERROR(HW_Memory, "Tried to commit an unreserved large buffer region at {:#x} that is not mapped (state {:#x})", addr, info.State);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -96,6 +109,21 @@ bool CommitVectorPage(uintptr_t addr, bool write) noexcept {
|
|||||||
#ifndef MAP_NOCORE
|
#ifndef MAP_NOCORE
|
||||||
#define MAP_NOCORE 0
|
#define MAP_NOCORE 0
|
||||||
#endif
|
#endif
|
||||||
|
#ifndef MADV_FREE
|
||||||
|
#define MADV_FREE MADV_DONTNEED
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void DecommitVectorPage(uintptr_t base) noexcept {
|
||||||
|
#if defined(_WIN32)
|
||||||
|
VirtualFree(reinterpret_cast<LPVOID>(base), HostPageSize, MEM_DECOMMIT);
|
||||||
|
#elif defined(__linux__)
|
||||||
|
// Linux's MADV_DONTNEED zeros out pages for us
|
||||||
|
madvise(reinterpret_cast<void*>(base), HostPageSize, MADV_DONTNEED);
|
||||||
|
#else
|
||||||
|
madvise(reinterpret_cast<void*>(base), HostPageSize, MADV_FREE);
|
||||||
|
std::memset(reinterpret_cast<void*>(base), 0, HostPageSize);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
void* AllocateMemoryPages(std::size_t size) noexcept {
|
void* AllocateMemoryPages(std::size_t size) noexcept {
|
||||||
if (auto page = HostPageSize; size % page != 0) {
|
if (auto page = HostPageSize; size % page != 0) {
|
||||||
@@ -108,7 +136,8 @@ void* AllocateMemoryPages(std::size_t size) noexcept {
|
|||||||
void* base = VirtualAlloc(nullptr, size, MEM_RESERVE, PAGE_READWRITE);
|
void* base = VirtualAlloc(nullptr, size, MEM_RESERVE, PAGE_READWRITE);
|
||||||
|
|
||||||
if (base != nullptr) {
|
if (base != nullptr) {
|
||||||
vector_regions.emplace_back(reinterpret_cast<u64>(base), reinterpret_cast<u64>(base) + size);
|
std::lock_guard lock(vector_regions_mutex);
|
||||||
|
vector_regions.emplace_back(reinterpret_cast<u64>(base) >> HostPageBits, (reinterpret_cast<u64>(base) + size) >> HostPageBits);
|
||||||
|
|
||||||
static std::once_flag flag;
|
static std::once_flag flag;
|
||||||
std::call_once(flag, []() { AddVectoredExceptionHandler(1, FakePageFaultHandler); });
|
std::call_once(flag, []() { AddVectoredExceptionHandler(1, FakePageFaultHandler); });
|
||||||
@@ -134,6 +163,8 @@ void FreeMemoryPages(void* base, [[maybe_unused]] std::size_t size) noexcept {
|
|||||||
if (!base)
|
if (!base)
|
||||||
return;
|
return;
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
|
std::lock_guard lock(vector_regions_mutex);
|
||||||
|
std::erase_if(vector_regions, [base](const auto& r) {return r.first == reinterpret_cast<u64>(base); });
|
||||||
ASSERT(VirtualFree(base, 0, MEM_RELEASE));
|
ASSERT(VirtualFree(base, 0, MEM_RELEASE));
|
||||||
#else
|
#else
|
||||||
ASSERT(munmap(base, size) == 0);
|
ASSERT(munmap(base, size) == 0);
|
||||||
|
|||||||
@@ -28,13 +28,14 @@ constexpr u64 HostPageBits = 12;
|
|||||||
constexpr u64 HostPageMask = ~(HostPageSize - 1);
|
constexpr u64 HostPageMask = ~(HostPageSize - 1);
|
||||||
bool CommitVectorPage(uintptr_t addr, bool write) noexcept;
|
bool CommitVectorPage(uintptr_t addr, bool write) noexcept;
|
||||||
#else
|
#else
|
||||||
const u64 HostPageSize = sysconf(_SC_PAGESIZE);
|
inline const u64 HostPageSize = sysconf(_SC_PAGESIZE);
|
||||||
const u64 HostPageBits = std::countr_zero(HostPageSize);
|
inline const u64 HostPageBits = std::countr_zero(HostPageSize);
|
||||||
const u64 HostPageMask = ~(HostPageSize - 1);
|
inline const u64 HostPageMask = ~(HostPageSize - 1);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
void* AllocateMemoryPages(std::size_t size) noexcept;
|
void* AllocateMemoryPages(std::size_t size) noexcept;
|
||||||
void FreeMemoryPages(void* base, std::size_t size) noexcept;
|
void FreeMemoryPages(void* base, std::size_t size) noexcept;
|
||||||
|
void DecommitVectorPage(uintptr_t base) noexcept;
|
||||||
|
|
||||||
/// A large page-aligned buffer that has optimized memory usage for zero-writes.
|
/// A large page-aligned buffer that has optimized memory usage for zero-writes.
|
||||||
template <typename T>
|
template <typename T>
|
||||||
@@ -80,8 +81,8 @@ public:
|
|||||||
UNREACHABLE_MSG("Out of bounds RW access on SparseLargeVector @ {}", index);
|
UNREACHABLE_MSG("Out of bounds RW access on SparseLargeVector @ {}", index);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!IsCommittedPage(index)) {
|
if (!IsCommittedPage(index) && !CommitPage(index)) {
|
||||||
CommitPage(index);
|
UNREACHABLE_MSG("Cannot access SparseLargeVector index {} with RW permission", index);
|
||||||
}
|
}
|
||||||
return base_ptr[index];
|
return base_ptr[index];
|
||||||
}
|
}
|
||||||
@@ -102,8 +103,7 @@ public:
|
|||||||
LOG_CRITICAL(Common_Memory, "Out of bounds write on SparseLargeVector @ {}", index);
|
LOG_CRITICAL(Common_Memory, "Out of bounds write on SparseLargeVector @ {}", index);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!IsCommittedPage(index))
|
if (IsCommittedPage(index) || CommitPage(index))
|
||||||
CommitPage(index);
|
|
||||||
base_ptr[index] = value;
|
base_ptr[index] = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -114,7 +114,7 @@ public:
|
|||||||
const u64 end_page = AlignUp(base, HostPageSize);
|
const u64 end_page = AlignUp(base, HostPageSize);
|
||||||
const u64 first_size = (std::min)(end_page, end) - base;
|
const u64 first_size = (std::min)(end_page, end) - base;
|
||||||
|
|
||||||
if (IsCommittedPage(start / sizeof(T))) {
|
if (IsCommittedPage(start)) {
|
||||||
std::memset(reinterpret_cast<void*>(base), 0, first_size);
|
std::memset(reinterpret_cast<void*>(base), 0, first_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -124,11 +124,16 @@ public:
|
|||||||
base = end_page;
|
base = end_page;
|
||||||
|
|
||||||
for (u64 page = base; page < end; page += HostPageSize) {
|
for (u64 page = base; page < end; page += HostPageSize) {
|
||||||
if (!IsCommittedPage((page - reinterpret_cast<u64>(base_ptr)) / sizeof(T))) {
|
auto index = (page - reinterpret_cast<u64>(base_ptr)) / sizeof(T);
|
||||||
|
if (!IsCommittedPage(index)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::memset(reinterpret_cast<void*>(page), 0, (std::min)( HostPageSize, end - page));
|
if (end - page >= HostPageSize) {
|
||||||
|
DecommitPage(index);
|
||||||
|
} else {
|
||||||
|
std::memset(reinterpret_cast<void*>(page), 0, end - page);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -171,16 +176,30 @@ private:
|
|||||||
return (val >> (page & 63)) & 1;
|
return (val >> (page & 63)) & 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
constexpr void CommitPage(std::size_t index) noexcept {
|
constexpr bool CommitPage(std::size_t index) noexcept {
|
||||||
auto page_index = (index * sizeof(T)) >> HostPageBits;
|
auto page_index = (index * sizeof(T)) >> HostPageBits;
|
||||||
auto page = reinterpret_cast<uintptr_t>(base_ptr + index) & HostPageMask;
|
auto page = reinterpret_cast<uintptr_t>(base_ptr + index) & HostPageMask;
|
||||||
#if defined(_WIN32)
|
#if defined(_WIN32)
|
||||||
CommitVectorPage(page, true);
|
if (!CommitVectorPage(page, true)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
#else
|
#else
|
||||||
mprotect(reinterpret_cast<void*>(page), HostPageSize, PROT_READ | PROT_WRITE);
|
if (mprotect(reinterpret_cast<void*>(page), HostPageSize, PROT_READ | PROT_WRITE) != 0) {
|
||||||
|
LOG_ERROR(Common_Memory, "Failed to commit large buffer region at index {}, error {}", index, strerror(errno));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
committed_pages[page_index >> 6].fetch_or(1ULL << (page_index & 63), std::memory_order_release);
|
committed_pages[page_index >> 6].fetch_or(1ULL << (page_index & 63), std::memory_order_release);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr void DecommitPage(std::size_t index) noexcept {
|
||||||
|
auto page_index = (index * sizeof(T)) >> HostPageBits;
|
||||||
|
auto page = reinterpret_cast<uintptr_t>(base_ptr + index) & HostPageMask;
|
||||||
|
|
||||||
|
committed_pages[page_index >> 6].fetch_and(~(1ULL << (page_index & 63)), std::memory_order_release);
|
||||||
|
DecommitVectorPage(page);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::size_t alloc_size{};
|
std::size_t alloc_size{};
|
||||||
|
|||||||
@@ -24,22 +24,6 @@
|
|||||||
|
|
||||||
namespace Common {
|
namespace Common {
|
||||||
|
|
||||||
/// Make a string lowercase
|
|
||||||
std::string ToLower(const std::string_view sv) {
|
|
||||||
std::string str{sv};
|
|
||||||
std::transform(str.begin(), str.end(), str.begin(),
|
|
||||||
[](auto const c) { return char(std::tolower(c)); });
|
|
||||||
return str;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Make a string uppercase
|
|
||||||
std::string ToUpper(const std::string_view sv) {
|
|
||||||
std::string str{sv};
|
|
||||||
std::transform(str.begin(), str.end(), str.begin(),
|
|
||||||
[](auto const c) { return char(std::toupper(c)); });
|
|
||||||
return str;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool SplitPath(const std::string& full_path, std::string* _pPath, std::string* _pFilename,
|
bool SplitPath(const std::string& full_path, std::string* _pPath, std::string* _pFilename,
|
||||||
std::string* _pExtension) {
|
std::string* _pExtension) {
|
||||||
if (full_path.empty())
|
if (full_path.empty())
|
||||||
@@ -80,41 +64,6 @@ bool SplitPath(const std::string& full_path, std::string* _pPath, std::string* _
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SplitString(const std::string& str, const char delim, std::vector<std::string>& output) {
|
|
||||||
std::istringstream iss(str);
|
|
||||||
output.resize(1);
|
|
||||||
|
|
||||||
while (std::getline(iss, *output.rbegin(), delim)) {
|
|
||||||
output.emplace_back();
|
|
||||||
}
|
|
||||||
|
|
||||||
output.pop_back();
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string TabsToSpaces(int tab_size, std::string in) {
|
|
||||||
std::size_t i = 0;
|
|
||||||
|
|
||||||
while ((i = in.find('\t')) != std::string::npos) {
|
|
||||||
in.replace(i, 1, tab_size, ' ');
|
|
||||||
}
|
|
||||||
|
|
||||||
return in;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string ReplaceAll(std::string result, const std::string& src, const std::string& dest) {
|
|
||||||
std::size_t pos = 0;
|
|
||||||
|
|
||||||
if (src == dest)
|
|
||||||
return result;
|
|
||||||
|
|
||||||
while ((pos = result.find(src, pos)) != std::string::npos) {
|
|
||||||
result.replace(pos, src.size(), dest);
|
|
||||||
pos += dest.length();
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string UTF16ToUTF8(std::u16string_view input) {
|
std::string UTF16ToUTF8(std::u16string_view input) {
|
||||||
std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t> convert;
|
std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t> convert;
|
||||||
return convert.to_bytes(input.data(), input.data() + input.size());
|
return convert.to_bytes(input.data(), input.data() + input.size());
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: 2013 Dolphin Emulator Project
|
// SPDX-FileCopyrightText: 2013 Dolphin Emulator Project
|
||||||
@@ -16,12 +16,6 @@
|
|||||||
|
|
||||||
namespace Common {
|
namespace Common {
|
||||||
|
|
||||||
/// Make a string lowercase
|
|
||||||
[[nodiscard]] std::string ToLower(const std::string_view sv);
|
|
||||||
|
|
||||||
/// Make a string uppercase
|
|
||||||
[[nodiscard]] std::string ToUpper(const std::string_view sv);
|
|
||||||
|
|
||||||
[[nodiscard]] inline std::string StringFromBuffer(std::span<const u8> data) noexcept {
|
[[nodiscard]] inline std::string StringFromBuffer(std::span<const u8> data) noexcept {
|
||||||
return std::string(data.begin(), std::find(data.begin(), data.end(), '\0'));
|
return std::string(data.begin(), std::find(data.begin(), data.end(), '\0'));
|
||||||
}
|
}
|
||||||
@@ -29,37 +23,8 @@ namespace Common {
|
|||||||
return std::string(data.begin(), std::find(data.begin(), data.end(), '\0'));
|
return std::string(data.begin(), std::find(data.begin(), data.end(), '\0'));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Turns " hej " into "hej". Also handles tabs.
|
|
||||||
[[nodiscard]] inline std::string StripSpaces(const std::string_view str) noexcept {
|
|
||||||
const std::size_t s = str.find_first_not_of(" \t\r\n");
|
|
||||||
if (str.npos != s)
|
|
||||||
return std::string{str.substr(s, str.find_last_not_of(" \t\r\n") - s + 1)};
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
/// "\"hello\"" is turned to "hello"
|
|
||||||
/// This one assumes that the string has already been space stripped in both
|
|
||||||
/// ends, as done by StripSpaces above, for example.
|
|
||||||
[[nodiscard]] inline std::string StripQuotes(const std::string_view s) noexcept {
|
|
||||||
if (s.size() && '\"' == s[0] && '\"' == *s.rbegin())
|
|
||||||
return std::string{s.substr(1, s.size() - 2)};
|
|
||||||
return std::string{s};
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] inline std::string StringFromBool(bool value) noexcept {
|
|
||||||
return value ? "True" : "False";
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] std::string TabsToSpaces(int tab_size, std::string in);
|
|
||||||
|
|
||||||
void SplitString(const std::string& str, char delim, std::vector<std::string>& output);
|
|
||||||
|
|
||||||
// "C:/Windows/winhelp.exe" to "C:/Windows/", "winhelp", ".exe"
|
// "C:/Windows/winhelp.exe" to "C:/Windows/", "winhelp", ".exe"
|
||||||
bool SplitPath(const std::string& full_path, std::string* _pPath, std::string* _pFilename,
|
bool SplitPath(const std::string& full_path, std::string* _pPath, std::string* _pFilename, std::string* _pExtension);
|
||||||
std::string* _pExtension);
|
|
||||||
|
|
||||||
[[nodiscard]] std::string ReplaceAll(std::string result, const std::string& src,
|
|
||||||
const std::string& dest);
|
|
||||||
|
|
||||||
[[nodiscard]] std::string UTF16ToUTF8(std::u16string_view input);
|
[[nodiscard]] std::string UTF16ToUTF8(std::u16string_view input);
|
||||||
[[nodiscard]] std::u16string UTF8ToUTF16(std::string_view input);
|
[[nodiscard]] std::u16string UTF8ToUTF16(std::string_view input);
|
||||||
@@ -73,30 +38,13 @@ bool SplitPath(const std::string& full_path, std::string* _pPath, std::string* _
|
|||||||
|
|
||||||
[[nodiscard]] std::u16string U16StringFromBuffer(const u16* input, std::size_t length);
|
[[nodiscard]] std::u16string U16StringFromBuffer(const u16* input, std::size_t length);
|
||||||
|
|
||||||
/**
|
|
||||||
* Compares the string defined by the range [`begin`, `end`) to the null-terminated C-string
|
|
||||||
* `other` for equality.
|
|
||||||
*/
|
|
||||||
template <typename InIt>
|
|
||||||
[[nodiscard]] inline bool ComparePartialString(InIt begin, InIt end, const char* other) noexcept {
|
|
||||||
for (; begin != end && *other != '\0'; ++begin, ++other) {
|
|
||||||
if (*begin != *other) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Only return true if both strings finished at the same point
|
|
||||||
return (begin == end) == (*other == '\0');
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Creates a std::string from a fixed-size NUL-terminated char buffer. If the buffer isn't
|
/// Creates a std::string from a fixed-size NUL-terminated char buffer. If the buffer isn't
|
||||||
/// NUL-terminated then the string ends at max_len characters.
|
/// NUL-terminated then the string ends at max_len characters.
|
||||||
[[nodiscard]] std::string StringFromFixedZeroTerminatedBuffer(std::string_view buffer,
|
[[nodiscard]] std::string StringFromFixedZeroTerminatedBuffer(std::string_view buffer, std::size_t max_len);
|
||||||
std::size_t max_len);
|
|
||||||
|
|
||||||
/// Creates a UTF-16 std::u16string from a fixed-size NUL-terminated char buffer. If the buffer isn't
|
/// Creates a UTF-16 std::u16string from a fixed-size NUL-terminated char buffer. If the buffer isn't
|
||||||
/// null-terminated, then the string ends at the greatest multiple of two less then or equal to
|
/// null-terminated, then the string ends at the greatest multiple of two less then or equal to
|
||||||
/// max_len_bytes.
|
/// max_len_bytes.
|
||||||
[[nodiscard]] std::u16string UTF16StringFromFixedZeroTerminatedBuffer(std::u16string_view buffer,
|
[[nodiscard]] std::u16string UTF16StringFromFixedZeroTerminatedBuffer(std::u16string_view buffer, std::size_t max_len);
|
||||||
std::size_t max_len);
|
|
||||||
|
|
||||||
} // namespace Common
|
} // namespace Common
|
||||||
|
|||||||
@@ -0,0 +1,46 @@
|
|||||||
|
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
|
#include <cstring>
|
||||||
|
|
||||||
|
#include "common/zbic_compression.h"
|
||||||
|
|
||||||
|
#define ZSTD_ZBIC_SUPPORT 1
|
||||||
|
#define ZSTDLIB_VISIBLE static
|
||||||
|
#define ZSTDLIB_HIDDEN static
|
||||||
|
#define ZSTDERRORLIB_VISIBLE static
|
||||||
|
#define ZSTDERRORLIB_HIDDEN static
|
||||||
|
#undef ZSTD_MULTITHREAD
|
||||||
|
|
||||||
|
#if defined(__ANDROID__)
|
||||||
|
#undef _GNU_SOURCE
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "zstd.h"
|
||||||
|
#define g_ZSTD_threading_useless_symbol g_ZSTD_zbic_threading_useless_symbol
|
||||||
|
#include "zstd.c"
|
||||||
|
#undef g_ZSTD_threading_useless_symbol
|
||||||
|
|
||||||
|
namespace Common::Compression {
|
||||||
|
|
||||||
|
bool IsZBIC(std::span<const u8> src) {
|
||||||
|
if (src.size() < sizeof(u32)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
u32 magic = 0;
|
||||||
|
std::memcpy(&magic, src.data(), sizeof(u32));
|
||||||
|
return magic == ZSTD_MAGICNUMBER; // 0x4349425A ("ZBIC")
|
||||||
|
}
|
||||||
|
|
||||||
|
int DecompressDataZBIC(std::span<u8> dst, std::span<const u8> src) {
|
||||||
|
if (dst.empty() || src.empty()) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
const size_t res = ZSTD_decompress(dst.data(), dst.size(), src.data(), src.size());
|
||||||
|
if (ZSTD_isError(res)) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return static_cast<int>(res);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Common::Compression
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <span>
|
||||||
|
#include "common/common_types.h"
|
||||||
|
|
||||||
|
namespace Common::Compression {
|
||||||
|
|
||||||
|
[[nodiscard]] bool IsZBIC(std::span<const u8> src);
|
||||||
|
|
||||||
|
[[nodiscard]] int DecompressDataZBIC(std::span<u8> dst, std::span<const u8> src);
|
||||||
|
|
||||||
|
} // namespace Common::Compression
|
||||||
@@ -23,6 +23,8 @@ add_library(core STATIC
|
|||||||
core_timing.h
|
core_timing.h
|
||||||
cpu_manager.cpp
|
cpu_manager.cpp
|
||||||
cpu_manager.h
|
cpu_manager.h
|
||||||
|
launch_params.cpp
|
||||||
|
launch_params.h
|
||||||
crypto/aes_util.cpp
|
crypto/aes_util.cpp
|
||||||
crypto/aes_util.h
|
crypto/aes_util.h
|
||||||
crypto/ctr_encryption_layer.cpp
|
crypto/ctr_encryption_layer.cpp
|
||||||
@@ -1203,6 +1205,9 @@ endif()
|
|||||||
|
|
||||||
target_include_directories(core PRIVATE ${OPUS_INCLUDE_DIRS})
|
target_include_directories(core PRIVATE ${OPUS_INCLUDE_DIRS})
|
||||||
target_link_libraries(core PUBLIC common PRIVATE audio_core hid_core network video_core nx_tzdb tz)
|
target_link_libraries(core PUBLIC common PRIVATE audio_core hid_core network video_core nx_tzdb tz)
|
||||||
|
if (MSVC)
|
||||||
|
target_link_libraries(core PRIVATE getopt)
|
||||||
|
endif()
|
||||||
|
|
||||||
if (BOOST_NO_HEADERS)
|
if (BOOST_NO_HEADERS)
|
||||||
target_link_libraries(core PUBLIC Boost::container Boost::heap Boost::asio Boost::process Boost::crc)
|
target_link_libraries(core PUBLIC Boost::container Boost::heap Boost::asio Boost::process Boost::crc)
|
||||||
|
|||||||
@@ -13,7 +13,6 @@
|
|||||||
#include <dynarmic/interface/A64/a64.h>
|
#include <dynarmic/interface/A64/a64.h>
|
||||||
#include <dynarmic/interface/code_page.h>
|
#include <dynarmic/interface/code_page.h>
|
||||||
#include "common/common_types.h"
|
#include "common/common_types.h"
|
||||||
#include "common/hash.h"
|
|
||||||
#include "core/arm/arm_interface.h"
|
#include "core/arm/arm_interface.h"
|
||||||
#include "core/arm/dynarmic/dynarmic_exclusive_monitor.h"
|
#include "core/arm/dynarmic/dynarmic_exclusive_monitor.h"
|
||||||
#include "dynarmic/interface/A64/config.h"
|
#include "dynarmic/interface/A64/config.h"
|
||||||
|
|||||||
@@ -168,7 +168,7 @@ void CpuManager::ShutdownThread(Kernel::KernelCore& kernel) {
|
|||||||
UNREACHABLE();
|
UNREACHABLE();
|
||||||
}
|
}
|
||||||
|
|
||||||
void CpuManager::RunThread(std::stop_token token, std::size_t core) {
|
void CpuManager::RunThread(std::stop_token stop_token, std::size_t core) {
|
||||||
/// Initialization
|
/// Initialization
|
||||||
system.RegisterCoreThread(core);
|
system.RegisterCoreThread(core);
|
||||||
std::string name = is_multicore ? ("CPUCore_" + std::to_string(core)) : std::string{"CPUThread"};
|
std::string name = is_multicore ? ("CPUCore_" + std::to_string(core)) : std::string{"CPUThread"};
|
||||||
@@ -178,26 +178,19 @@ void CpuManager::RunThread(std::stop_token token, std::size_t core) {
|
|||||||
auto& data = core_data[core];
|
auto& data = core_data[core];
|
||||||
data.host_context = Common::Fiber::ThreadToFiber();
|
data.host_context = Common::Fiber::ThreadToFiber();
|
||||||
|
|
||||||
// Cleanup
|
|
||||||
SCOPE_EXIT {
|
|
||||||
data.host_context->Exit();
|
|
||||||
};
|
|
||||||
|
|
||||||
// Running
|
// Running
|
||||||
if (!gpu_barrier->Sync(token)) {
|
gpu_barrier->arrive_and_wait();
|
||||||
return;
|
if (!stop_token.stop_requested()) {
|
||||||
}
|
|
||||||
|
|
||||||
if (!is_async_gpu && !is_multicore) {
|
if (!is_async_gpu && !is_multicore) {
|
||||||
system.GPU().ObtainContext();
|
system.GPU().ObtainContext();
|
||||||
}
|
}
|
||||||
|
|
||||||
auto& kernel = system.Kernel();
|
auto& kernel = system.Kernel();
|
||||||
auto& scheduler = *kernel.CurrentScheduler();
|
auto& scheduler = *kernel.CurrentScheduler();
|
||||||
auto* thread = scheduler.GetSchedulerCurrentThread();
|
auto* thread = scheduler.GetSchedulerCurrentThread();
|
||||||
Kernel::SetCurrentThread(kernel, thread);
|
Kernel::SetCurrentThread(kernel, thread);
|
||||||
|
|
||||||
Common::Fiber::YieldTo(data.host_context, *thread->GetHostContext());
|
Common::Fiber::YieldTo(data.host_context, *thread->GetHostContext());
|
||||||
|
}
|
||||||
|
data.host_context->Exit();
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace Core
|
} // namespace Core
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
|
|
||||||
#include <array>
|
#include <array>
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
|
#include <barrier>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
@@ -52,7 +53,7 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
void OnGpuReady() {
|
void OnGpuReady() {
|
||||||
gpu_barrier->Sync();
|
gpu_barrier->arrive_and_wait();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Initialize();
|
void Initialize();
|
||||||
@@ -95,7 +96,7 @@ private:
|
|||||||
|
|
||||||
static constexpr std::size_t max_cycle_runs = 5;
|
static constexpr std::size_t max_cycle_runs = 5;
|
||||||
|
|
||||||
std::optional<Common::Barrier> gpu_barrier{};
|
std::optional<std::barrier<>> gpu_barrier{};
|
||||||
struct CoreData {
|
struct CoreData {
|
||||||
std::shared_ptr<Common::Fiber> host_context;
|
std::shared_ptr<Common::Fiber> host_context;
|
||||||
std::jthread host_thread;
|
std::jthread host_thread;
|
||||||
|
|||||||
@@ -14,6 +14,7 @@
|
|||||||
#include <tuple>
|
#include <tuple>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
#include <boost/algorithm/string/case_conv.hpp>
|
||||||
#include <openssl/evp.h>
|
#include <openssl/evp.h>
|
||||||
|
|
||||||
#include "common/fs/file.h"
|
#include "common/fs/file.h"
|
||||||
@@ -622,7 +623,7 @@ void KeyManager::LoadFromFile(const std::filesystem::path& file_path, bool is_ti
|
|||||||
Key128 key = Common::HexStringToArray<16>(out[1]);
|
Key128 key = Common::HexStringToArray<16>(out[1]);
|
||||||
s128_keys[{S128KeyType::Titlekey, rights_id[1], rights_id[0]}] = key;
|
s128_keys[{S128KeyType::Titlekey, rights_id[1], rights_id[0]}] = key;
|
||||||
} else {
|
} else {
|
||||||
out[0] = Common::ToLower(out[0]);
|
boost::algorithm::to_lower(out[0]);
|
||||||
if (const auto iter128 = Find128ByName(out[0]); iter128 != s128_file_id.end()) {
|
if (const auto iter128 = Find128ByName(out[0]); iter128 != s128_file_id.end()) {
|
||||||
const auto& index = iter128->second;
|
const auto& index = iter128->second;
|
||||||
const Key128 key = Common::HexStringToArray<16>(out[1]);
|
const Key128 key = Common::HexStringToArray<16>(out[1]);
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
|
|
||||||
#include <array>
|
#include <array>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
#include <boost/algorithm/string/case_conv.hpp>
|
||||||
#include "common/common_funcs.h"
|
#include "common/common_funcs.h"
|
||||||
#include "common/common_types.h"
|
#include "common/common_types.h"
|
||||||
#include "common/hex_util.h"
|
#include "common/hex_util.h"
|
||||||
@@ -41,16 +42,13 @@ static_assert(sizeof(Package2Header) == 0x200, "Package2Header has incorrect siz
|
|||||||
|
|
||||||
const u8 PartitionDataManager::MAX_KEYBLOB_SOURCE_HASH = 32;
|
const u8 PartitionDataManager::MAX_KEYBLOB_SOURCE_HASH = 32;
|
||||||
|
|
||||||
static FileSys::VirtualFile FindFileInDirWithNames(const FileSys::VirtualDir& dir,
|
static FileSys::VirtualFile FindFileInDirWithNames(const FileSys::VirtualDir& dir, const std::string& name) {
|
||||||
const std::string& name) {
|
const auto upper = boost::algorithm::to_upper_copy(name);
|
||||||
const auto upper = Common::ToUpper(name);
|
|
||||||
|
|
||||||
for (const auto& fname : {name, name + ".bin", upper, upper + ".BIN"}) {
|
for (const auto& fname : {name, name + ".bin", upper, upper + ".BIN"}) {
|
||||||
if (dir->GetFile(fname) != nullptr) {
|
if (dir->GetFile(fname) != nullptr) {
|
||||||
return dir->GetFile(fname);
|
return dir->GetFile(fname);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
#include <array>
|
#include <array>
|
||||||
#include <cstddef>
|
#include <cstddef>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
#include <boost/algorithm/string/case_conv.hpp>
|
||||||
|
|
||||||
#include "common/assert.h"
|
#include "common/assert.h"
|
||||||
#include "common/hex_util.h"
|
#include "common/hex_util.h"
|
||||||
@@ -73,12 +74,10 @@ VirtualDir FindSubdirectoryCaseless(const VirtualDir dir, std::string_view name)
|
|||||||
#else
|
#else
|
||||||
const auto subdirs = dir->GetSubdirectories();
|
const auto subdirs = dir->GetSubdirectories();
|
||||||
for (const auto& subdir : subdirs) {
|
for (const auto& subdir : subdirs) {
|
||||||
std::string dir_name = Common::ToLower(subdir->GetName());
|
if (name == boost::algorithm::to_lower_copy(subdir->GetName())) {
|
||||||
if (dir_name == name) {
|
|
||||||
return subdir;
|
return subdir;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return nullptr;
|
return nullptr;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
#include <limits>
|
#include <limits>
|
||||||
#include <random>
|
#include <random>
|
||||||
#include <regex>
|
#include <regex>
|
||||||
|
#include <boost/algorithm/string/case_conv.hpp>
|
||||||
#include <openssl/evp.h>
|
#include <openssl/evp.h>
|
||||||
#include "common/assert.h"
|
#include "common/assert.h"
|
||||||
#include "common/fs/path_util.h"
|
#include "common/fs/path_util.h"
|
||||||
@@ -1414,11 +1415,10 @@ void ExternalContentProvider::ScanDirectory(const VirtualDir& dir) {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto extension = Common::ToLower(filename.substr(dot_pos + 1));
|
const auto ext = boost::to_lower_copy(filename.substr(dot_pos + 1));
|
||||||
|
if (ext == "nsp") {
|
||||||
if (extension == "nsp") {
|
|
||||||
ProcessNSP(file);
|
ProcessNSP(file);
|
||||||
} else if (extension == "xci") {
|
} else if (ext == "xci") {
|
||||||
ProcessXCI(file);
|
ProcessXCI(file);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -109,8 +109,7 @@ VirtualFile RealVfsFilesystem::OpenFileFromEntry(std::string_view path_, std::op
|
|||||||
auto reference = std::make_unique<FileReference>();
|
auto reference = std::make_unique<FileReference>();
|
||||||
this->InsertReferenceIntoListLocked(*reference);
|
this->InsertReferenceIntoListLocked(*reference);
|
||||||
|
|
||||||
auto file = std::shared_ptr<RealVfsFile>(
|
auto file = std::make_shared<RealVfsFile>(*this, std::move(reference), path, perms, size, std::move(parent_path));
|
||||||
new RealVfsFile(*this, std::move(reference), path, perms, size, std::move(parent_path)));
|
|
||||||
cache[path] = file;
|
cache[path] = file;
|
||||||
|
|
||||||
return file;
|
return file;
|
||||||
@@ -177,7 +176,7 @@ bool RealVfsFilesystem::DeleteFile(std::string_view path_) {
|
|||||||
|
|
||||||
VirtualDir RealVfsFilesystem::OpenDirectory(std::string_view path_, OpenMode perms) {
|
VirtualDir RealVfsFilesystem::OpenDirectory(std::string_view path_, OpenMode perms) {
|
||||||
const auto path = FS::SanitizePath(path_, FS::DirectorySeparator::PlatformDefault);
|
const auto path = FS::SanitizePath(path_, FS::DirectorySeparator::PlatformDefault);
|
||||||
return std::shared_ptr<RealVfsDirectory>(new RealVfsDirectory(*this, path, perms));
|
return std::make_shared<RealVfsDirectory>(*this, path, perms);
|
||||||
}
|
}
|
||||||
|
|
||||||
VirtualDir RealVfsFilesystem::CreateDirectory(std::string_view path_, OpenMode perms) {
|
VirtualDir RealVfsFilesystem::CreateDirectory(std::string_view path_, OpenMode perms) {
|
||||||
@@ -185,7 +184,7 @@ VirtualDir RealVfsFilesystem::CreateDirectory(std::string_view path_, OpenMode p
|
|||||||
if (!FS::CreateDirs(path)) {
|
if (!FS::CreateDirs(path)) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
return std::shared_ptr<RealVfsDirectory>(new RealVfsDirectory(*this, path, perms));
|
return std::make_shared<RealVfsDirectory>(*this, path, perms);
|
||||||
}
|
}
|
||||||
|
|
||||||
VirtualDir RealVfsFilesystem::CopyDirectory(std::string_view old_path_,
|
VirtualDir RealVfsFilesystem::CopyDirectory(std::string_view old_path_,
|
||||||
|
|||||||
@@ -82,6 +82,9 @@ class RealVfsFile : public VfsFile {
|
|||||||
friend class RealVfsFilesystem;
|
friend class RealVfsFilesystem;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
RealVfsFile(RealVfsFilesystem& base, std::unique_ptr<FileReference> reference,
|
||||||
|
const std::string& path, OpenMode perms = OpenMode::Read,
|
||||||
|
std::optional<u64> size = {}, std::optional<std::string> parent_path = {});
|
||||||
~RealVfsFile() override;
|
~RealVfsFile() override;
|
||||||
|
|
||||||
std::string GetName() const override;
|
std::string GetName() const override;
|
||||||
@@ -95,9 +98,6 @@ public:
|
|||||||
bool Rename(std::string_view name) override;
|
bool Rename(std::string_view name) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
RealVfsFile(RealVfsFilesystem& base, std::unique_ptr<FileReference> reference,
|
|
||||||
const std::string& path, OpenMode perms = OpenMode::Read,
|
|
||||||
std::optional<u64> size = {}, std::optional<std::string> parent_path = {});
|
|
||||||
|
|
||||||
RealVfsFilesystem& base;
|
RealVfsFilesystem& base;
|
||||||
std::unique_ptr<FileReference> reference;
|
std::unique_ptr<FileReference> reference;
|
||||||
@@ -113,6 +113,8 @@ class RealVfsDirectory : public VfsDirectory {
|
|||||||
friend class RealVfsFilesystem;
|
friend class RealVfsFilesystem;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
RealVfsDirectory(RealVfsFilesystem& base, const std::string& path,
|
||||||
|
OpenMode perms = OpenMode::Read);
|
||||||
~RealVfsDirectory() override;
|
~RealVfsDirectory() override;
|
||||||
|
|
||||||
VirtualFile GetFileRelative(std::string_view relative_path) const override;
|
VirtualFile GetFileRelative(std::string_view relative_path) const override;
|
||||||
@@ -138,9 +140,6 @@ public:
|
|||||||
std::map<std::string, VfsEntryType, std::less<>> GetEntries() const override;
|
std::map<std::string, VfsEntryType, std::less<>> GetEntries() const override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
RealVfsDirectory(RealVfsFilesystem& base, const std::string& path,
|
|
||||||
OpenMode perms = OpenMode::Read);
|
|
||||||
|
|
||||||
template <typename T, typename R>
|
template <typename T, typename R>
|
||||||
std::vector<std::shared_ptr<R>> IterateEntries() const;
|
std::vector<std::shared_ptr<R>> IterateEntries() const;
|
||||||
|
|
||||||
|
|||||||
@@ -9,6 +9,7 @@
|
|||||||
#include <regex>
|
#include <regex>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
|
#include <boost/algorithm/string/case_conv.hpp>
|
||||||
#include <openssl/err.h>
|
#include <openssl/err.h>
|
||||||
#include <openssl/evp.h>
|
#include <openssl/evp.h>
|
||||||
|
|
||||||
@@ -63,8 +64,8 @@ NAX::NAX(VirtualFile file_)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const std::string two_dir = Common::ToUpper(std::string{match[1]});
|
const std::string two_dir = boost::algorithm::to_upper_copy(std::string{match[1]});
|
||||||
const std::string nca_id = Common::ToLower(std::string{match[2]});
|
const std::string nca_id = boost::algorithm::to_lower_copy(std::string{match[2]});
|
||||||
status = Parse(fmt::format("/registered/{}/{}.nca", two_dir, nca_id));
|
status = Parse(fmt::format("/registered/{}/{}.nca", two_dir, nca_id));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,6 @@
|
|||||||
|
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
@@ -19,8 +22,14 @@ constexpr u32 NUM_CPU_CORES = 4; // Number of CPU Cores - sync wit
|
|||||||
|
|
||||||
// Virtual to Physical core map.
|
// Virtual to Physical core map.
|
||||||
constexpr std::array<s32, Common::BitSize<u64>()> VirtualToPhysicalCoreMap{
|
constexpr std::array<s32, Common::BitSize<u64>()> VirtualToPhysicalCoreMap{
|
||||||
0, 1, 2, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
0, 1, 2, 3, 0, 0, 0, 0,
|
||||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3,
|
0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 3,
|
||||||
};
|
};
|
||||||
|
|
||||||
static constexpr inline size_t NumVirtualCores = Common::BitSize<u64>();
|
static constexpr inline size_t NumVirtualCores = Common::BitSize<u64>();
|
||||||
|
|||||||
@@ -517,7 +517,7 @@ void WindowSystem::UpdateAppletStateLocked(Applet* applet, bool is_foreground, b
|
|||||||
// Layer ordering. Composition sorts back-to-front. Now with enums for calrity.
|
// Layer ordering. Composition sorts back-to-front. Now with enums for calrity.
|
||||||
s32 z_index = Background;
|
s32 z_index = Background;
|
||||||
if (is_overlay) {
|
if (is_overlay) {
|
||||||
z_index = Overlay;
|
z_index = this->IsOverlayOpenLocked(*applet) ? Overlay : Background;
|
||||||
} else if (inherited_foreground) {
|
} else if (inherited_foreground) {
|
||||||
z_index = is_obscured ? Foreground : ForegroundVisible;
|
z_index = is_obscured ? Foreground : ForegroundVisible;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -191,7 +191,7 @@ void LoopProcess(Core::System& system) {
|
|||||||
server_manager->RegisterNamedService("audout:d", std::make_shared<IAudioOutManagerForDebugger>(system), 30);
|
server_manager->RegisterNamedService("audout:d", std::make_shared<IAudioOutManagerForDebugger>(system), 30);
|
||||||
server_manager->RegisterNamedService("audin:d", std::make_shared<IAudioInManagerForDebugger>(system), 30);
|
server_manager->RegisterNamedService("audin:d", std::make_shared<IAudioInManagerForDebugger>(system), 30);
|
||||||
server_manager->RegisterNamedService("audrec:d", std::make_shared<IFinalOutputRecorderManagerForDebugger>(system), 30);
|
server_manager->RegisterNamedService("audrec:d", std::make_shared<IFinalOutputRecorderManagerForDebugger>(system), 30);
|
||||||
server_manager->RegisterNamedService("audren:d", std::make_shared<IAudioInManager>(system), 30);
|
server_manager->RegisterNamedService("audren:d", std::make_shared<IAudioRendererManagerForDebugger>(system), 30);
|
||||||
|
|
||||||
server_manager->RegisterNamedService("audin:u", std::make_shared<IAudioInManager>(system), 30);
|
server_manager->RegisterNamedService("audin:u", std::make_shared<IAudioInManager>(system), 30);
|
||||||
server_manager->RegisterNamedService("audin:a", std::make_shared<IAudioInManagerForApplet>(system), 30);
|
server_manager->RegisterNamedService("audin:a", std::make_shared<IAudioInManagerForApplet>(system), 30);
|
||||||
|
|||||||
@@ -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-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||||
@@ -24,7 +24,7 @@ IReceiverService::~IReceiverService() = default;
|
|||||||
|
|
||||||
Result IReceiverService::OpenReceiver(Out<SharedPointer<IReceiver>> out_receiver) {
|
Result IReceiverService::OpenReceiver(Out<SharedPointer<IReceiver>> out_receiver) {
|
||||||
LOG_DEBUG(Service_PSC, "called");
|
LOG_DEBUG(Service_PSC, "called");
|
||||||
*out_receiver = std::shared_ptr<IReceiver>(new IReceiver(system));
|
*out_receiver = std::make_shared<IReceiver>(system);
|
||||||
R_SUCCEED();
|
R_SUCCEED();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,205 @@
|
|||||||
|
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
|
#include <regex>
|
||||||
|
#include <cctype>
|
||||||
|
|
||||||
|
#include "common/assert.h"
|
||||||
|
#include "common/logging.h"
|
||||||
|
#include "common/settings.h"
|
||||||
|
#include "common/string_util.h"
|
||||||
|
#include "common/scm_rev.h"
|
||||||
|
#include "core/core.h"
|
||||||
|
#include "core/hle/service/acc/profile_manager.h"
|
||||||
|
#include "core/launch_params.h"
|
||||||
|
|
||||||
|
#undef _UNICODE
|
||||||
|
#include <getopt.h>
|
||||||
|
#ifndef _MSC_VER
|
||||||
|
#include <unistd.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace Core {
|
||||||
|
LaunchParams ParseLaunchParams(Core::System& system, int argc, char *argv[], wchar_t *argv_w[]) noexcept {
|
||||||
|
LaunchParams p{};
|
||||||
|
|
||||||
|
int option_index = 0;
|
||||||
|
static struct option long_options[] = {
|
||||||
|
// clang-format off
|
||||||
|
{"debug", no_argument, 0, 'd'},
|
||||||
|
{"config", required_argument, 0, 'c'},
|
||||||
|
{"fullscreen", no_argument, 0, 'f'},
|
||||||
|
{"help", no_argument, 0, 'h'},
|
||||||
|
{"game", required_argument, 0, 'g'},
|
||||||
|
{"multiplayer", required_argument, 0, 'm'},
|
||||||
|
{"program", optional_argument, 0, 'p'},
|
||||||
|
{"user", required_argument, 0, 'u'},
|
||||||
|
{"version", no_argument, 0, 'v'},
|
||||||
|
{"input-profile", no_argument, 0, 'i'},
|
||||||
|
{"null-render", no_argument, 0, 'n'},
|
||||||
|
{"singlecore", no_argument, 0, 's'},
|
||||||
|
{"filter", no_argument, 0, 'x'},
|
||||||
|
{0, 0, 0, 0},
|
||||||
|
// clang-format on
|
||||||
|
};
|
||||||
|
|
||||||
|
while (optind < argc) {
|
||||||
|
if (int arg = getopt_long(argc, argv, "dc:fhg:m:p:u:vinsx", long_options, &option_index); arg != -1) {
|
||||||
|
switch (char(arg)) {
|
||||||
|
case 'd':
|
||||||
|
p.override_gdb_port = uint16_t(atoi(optarg));
|
||||||
|
break;
|
||||||
|
case 'c':
|
||||||
|
p.config_path = optarg;
|
||||||
|
break;
|
||||||
|
case 'f':
|
||||||
|
p.fullscreen = true;
|
||||||
|
LOG_INFO(Frontend, "Starting in fullscreen mode...");
|
||||||
|
break;
|
||||||
|
case 'h':
|
||||||
|
p.print_help = true;
|
||||||
|
break;
|
||||||
|
case 'g':
|
||||||
|
p.filepath = std::string(optarg);
|
||||||
|
break;
|
||||||
|
case 'i': {
|
||||||
|
p.input_profile = std::string(optarg);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'm': {
|
||||||
|
p.use_multiplayer = true;
|
||||||
|
const std::string str_arg(optarg);
|
||||||
|
// regex to check if the format is nickname:password@ip:port
|
||||||
|
// with optional :password
|
||||||
|
const std::regex re("^([^:]+)(?::(.+))?@([^:]+)(?::([0-9]+))?$");
|
||||||
|
if (std::regex_match(str_arg, re)) {
|
||||||
|
std::smatch match;
|
||||||
|
std::regex_search(str_arg, match, re);
|
||||||
|
ASSERT(match.size() == 5);
|
||||||
|
p.nickname = match[1];
|
||||||
|
p.password = match[2];
|
||||||
|
p.address = match[3];
|
||||||
|
if (!match[4].str().empty()) {
|
||||||
|
p.port = u16(std::strtoul(match[4].str().c_str(), nullptr, 0));
|
||||||
|
}
|
||||||
|
std::regex nickname_re("^[a-zA-Z0-9._\\- ]+$");
|
||||||
|
ASSERT(std::regex_match(p.nickname, nickname_re) && "Nickname is not valid. Must be 4 to 20 alphanumeric characters");
|
||||||
|
ASSERT(!p.address.empty() && "Address is empty");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'p':
|
||||||
|
p.program_args = argv[optind];
|
||||||
|
++optind;
|
||||||
|
break;
|
||||||
|
case 'u': {
|
||||||
|
// Launch game with a specific user
|
||||||
|
bool argument_ok = isdigit(optarg[0]);
|
||||||
|
p.selected_user = atoi(optarg);
|
||||||
|
if (!argument_ok) {
|
||||||
|
// try to look it up by username, only finds the first username that matches.
|
||||||
|
auto const user_idx = system.GetProfileManager().GetUserIndex(optarg);
|
||||||
|
if (user_idx != std::nullopt) {
|
||||||
|
p.selected_user = user_idx.value();
|
||||||
|
} else {
|
||||||
|
LOG_ERROR(Frontend, "Invalid user argument '{}'", optarg);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (system.GetProfileManager().UserExistsIndex(*p.selected_user)) {
|
||||||
|
Settings::values.current_user = s32(*p.selected_user);
|
||||||
|
} else {
|
||||||
|
LOG_ERROR(Frontend, "Selected user {} doesn't exist", *p.selected_user);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'v':
|
||||||
|
p.print_version = true;
|
||||||
|
break;
|
||||||
|
case 'n':
|
||||||
|
p.force_null_render = true;
|
||||||
|
break;
|
||||||
|
case 's':
|
||||||
|
p.force_single_core = true;
|
||||||
|
break;
|
||||||
|
case 'x':
|
||||||
|
p.log_filter = argv[optind];
|
||||||
|
++optind;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// only kept due to shortcuts made by Qt frontend which use "-qlaunch"
|
||||||
|
if (strcmp(argv[optind], "-hlaunch") == 0) {
|
||||||
|
p.launch_hlaunch = true;
|
||||||
|
} else if (strcmp(argv[optind], "-qlaunch") == 0) {
|
||||||
|
p.launch_qlaunch = true;
|
||||||
|
} else if (strcmp(argv[optind], "-setup") == 0) {
|
||||||
|
p.launch_setup = true;
|
||||||
|
} else {
|
||||||
|
// qt feeds utf8 data, sdl frontend feeds raw utf16 data
|
||||||
|
#ifdef _WIN32
|
||||||
|
p.filepath = argv_w != nullptr
|
||||||
|
? Common::UTF16ToUTF8(argv_w[optind])
|
||||||
|
: argv[optind];
|
||||||
|
#else
|
||||||
|
p.filepath = argv[optind];
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
optind++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
p.argv0 = argv[0];
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ApplyLaunchParams(LaunchParams const& lp) noexcept {
|
||||||
|
// apply the log_filter setting
|
||||||
|
// the logger was initialized before and doesn't pick up the filter on its own
|
||||||
|
Common::Log::Filter filter{};
|
||||||
|
filter.ParseFilterString(lp.log_filter.value_or(Settings::values.log_filter.GetValue()));
|
||||||
|
Common::Log::SetGlobalFilter(filter);
|
||||||
|
|
||||||
|
if (!lp.program_args.empty()) {
|
||||||
|
Settings::values.program_args = lp.program_args;
|
||||||
|
}
|
||||||
|
if (!lp.input_profile.empty()) {
|
||||||
|
auto& players = Settings::values.players.GetValue();
|
||||||
|
players[0].profile_name = lp.input_profile;
|
||||||
|
}
|
||||||
|
if (lp.selected_user.has_value()) {
|
||||||
|
Settings::values.current_user = std::clamp(*lp.selected_user, 0, 7);
|
||||||
|
}
|
||||||
|
if (lp.override_gdb_port.has_value()) {
|
||||||
|
Settings::values.use_gdbstub = true;
|
||||||
|
Settings::values.gdbstub_port = *lp.override_gdb_port;
|
||||||
|
}
|
||||||
|
if (lp.force_single_core) {
|
||||||
|
Settings::values.use_multi_core = false;
|
||||||
|
}
|
||||||
|
if (lp.force_null_render) {
|
||||||
|
Settings::values.renderer_backend = Settings::RendererBackend::Null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (lp.print_version) {
|
||||||
|
LOG_INFO(Frontend, "Eden {} {}", Common::g_scm_branch, Common::g_scm_desc);
|
||||||
|
}
|
||||||
|
if (lp.print_help) {
|
||||||
|
LOG_INFO(Frontend,
|
||||||
|
"Usage: {}"
|
||||||
|
" [options] <filename>\n"
|
||||||
|
"-c, --config Load the specified configuration file\n"
|
||||||
|
"-f, --fullscreen Start in fullscreen mode\n"
|
||||||
|
"-g, --game File path of the game to load\n"
|
||||||
|
"-h, --help Display this help and exit\n"
|
||||||
|
"-m, --multiplayer=nick:password@address:port"
|
||||||
|
" Nickname, password, address and port for multiplayer\n"
|
||||||
|
"-p, --program Pass following string as arguments to executable\n"
|
||||||
|
"-u, --user Select a specific user profile from 0 to 7\n"
|
||||||
|
"-d, --debug Run the GDB stub on a port from 1 to 65535\n"
|
||||||
|
"-v, --version Output version information and exit\n",
|
||||||
|
lp.argv0
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <optional>
|
||||||
|
#include "common/common_types.h"
|
||||||
|
#include "network/room.h"
|
||||||
|
|
||||||
|
namespace Core {
|
||||||
|
|
||||||
|
class System;
|
||||||
|
struct LaunchParams {
|
||||||
|
std::string argv0{};
|
||||||
|
std::string filepath{};
|
||||||
|
std::string nickname{};
|
||||||
|
std::string password{};
|
||||||
|
std::string address{};
|
||||||
|
std::string input_profile{};
|
||||||
|
std::string program_args{};
|
||||||
|
std::optional<std::string> config_path{};
|
||||||
|
std::optional<int> selected_user{};
|
||||||
|
std::optional<u16> override_gdb_port{};
|
||||||
|
std::optional<std::string> log_filter{};
|
||||||
|
u16 port = Network::DefaultRoomPort;
|
||||||
|
bool use_multiplayer = false;
|
||||||
|
bool fullscreen = false;
|
||||||
|
bool force_null_render = false;
|
||||||
|
bool force_single_core = false;
|
||||||
|
// print
|
||||||
|
bool print_version = false;
|
||||||
|
bool print_help = false;
|
||||||
|
// qt
|
||||||
|
bool launch_hlaunch = false;
|
||||||
|
bool launch_qlaunch = false;
|
||||||
|
bool launch_setup = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
LaunchParams ParseLaunchParams(Core::System& system, int argc, char *argv[], wchar_t *argv_w[]) noexcept;
|
||||||
|
void ApplyLaunchParams(LaunchParams const& lp) noexcept;
|
||||||
|
|
||||||
|
}
|
||||||
@@ -10,6 +10,7 @@
|
|||||||
#include <string>
|
#include <string>
|
||||||
#include <concepts>
|
#include <concepts>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
#include <boost/algorithm/string/case_conv.hpp>
|
||||||
#include "common/concepts.h"
|
#include "common/concepts.h"
|
||||||
#include "common/fs/path_util.h"
|
#include "common/fs/path_util.h"
|
||||||
#include "common/logging.h"
|
#include "common/logging.h"
|
||||||
@@ -170,8 +171,7 @@ FileType GuessFromFilename(const std::string& name) {
|
|||||||
else if (name == "00")
|
else if (name == "00")
|
||||||
return FileType::NCA;
|
return FileType::NCA;
|
||||||
|
|
||||||
auto const extension =
|
auto const extension = boost::algorithm::to_lower_copy(std::string(Common::FS::GetExtensionFromFilename(name)));
|
||||||
Common::ToLower(std::string(Common::FS::GetExtensionFromFilename(name)));
|
|
||||||
if (extension == "nro")
|
if (extension == "nro")
|
||||||
return FileType::NRO;
|
return FileType::NRO;
|
||||||
else if (extension == "nso")
|
else if (extension == "nso")
|
||||||
|
|||||||
+31
-4
@@ -7,12 +7,14 @@
|
|||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <cinttypes>
|
#include <cinttypes>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
#include <span>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "common/common_funcs.h"
|
#include "common/common_funcs.h"
|
||||||
#include "common/hex_util.h"
|
#include "common/hex_util.h"
|
||||||
#include "common/logging.h"
|
#include "common/logging.h"
|
||||||
#include "common/lz4_compression.h"
|
#include "common/lz4_compression.h"
|
||||||
|
#include "common/zbic_compression.h"
|
||||||
#include "common/settings.h"
|
#include "common/settings.h"
|
||||||
#include "common/swap.h"
|
#include "common/swap.h"
|
||||||
#include "core/core.h"
|
#include "core/core.h"
|
||||||
@@ -104,11 +106,36 @@ std::optional<VAddr> AppLoader_NSO::LoadModule(Kernel::KProcess& process, Core::
|
|||||||
for (std::size_t i = 0; i < nso_header.segments.size(); ++i) {
|
for (std::size_t i = 0; i < nso_header.segments.size(); ++i) {
|
||||||
nso_file.Read(compressed_data.data(), nso_header.segments_compressed_size[i], nso_header.segments[i].offset);
|
nso_file.Read(compressed_data.data(), nso_header.segments_compressed_size[i], nso_header.segments[i].offset);
|
||||||
if (nso_header.IsSegmentCompressed(i)) {
|
if (nso_header.IsSegmentCompressed(i)) {
|
||||||
int r = Common::Compression::DecompressDataLZ4(decompressed_size.data(), nso_header.segments[i].size, compressed_data.data(), nso_header.segments_compressed_size[i]);
|
if (nso_header.IsZBICCompressed()) {
|
||||||
ASSERT(r == int(nso_header.segments[i].size));
|
// ZBIC compression
|
||||||
std::memcpy(codeset.memory.data() + module_start + nso_header.segments[i].location, decompressed_size.data(), nso_header.segments[i].size);
|
const int r = Common::Compression::DecompressDataZBIC(
|
||||||
|
std::span<u8>{decompressed_size}.first(nso_header.segments[i].size),
|
||||||
|
std::span<const u8>{compressed_data}.first(nso_header.segments_compressed_size[i])
|
||||||
|
);
|
||||||
|
ASSERT(r > 0);
|
||||||
} else {
|
} else {
|
||||||
std::memcpy(codeset.memory.data() + module_start + nso_header.segments[i].location, compressed_data.data(), nso_header.segments[i].size);
|
// LZ4 compression
|
||||||
|
int r = Common::Compression::DecompressDataLZ4(
|
||||||
|
decompressed_size.data(),
|
||||||
|
nso_header.segments[i].size,
|
||||||
|
compressed_data.data(),
|
||||||
|
nso_header.segments_compressed_size[i]
|
||||||
|
);
|
||||||
|
ASSERT(r == int(nso_header.segments[i].size));
|
||||||
|
}
|
||||||
|
|
||||||
|
std::memcpy(
|
||||||
|
codeset.memory.data() + module_start + nso_header.segments[i].location,
|
||||||
|
decompressed_size.data(),
|
||||||
|
nso_header.segments[i].size
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
// Not compressed
|
||||||
|
std::memcpy(
|
||||||
|
codeset.memory.data() + module_start + nso_header.segments[i].location,
|
||||||
|
compressed_data.data(),
|
||||||
|
nso_header.segments[i].size
|
||||||
|
);
|
||||||
}
|
}
|
||||||
codeset.segments[i].addr = module_start + nso_header.segments[i].location;
|
codeset.segments[i].addr = module_start + nso_header.segments[i].location;
|
||||||
codeset.segments[i].offset = module_start + nso_header.segments[i].location;
|
codeset.segments[i].offset = module_start + nso_header.segments[i].location;
|
||||||
|
|||||||
@@ -1,3 +1,6 @@
|
|||||||
|
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
@@ -58,6 +61,9 @@ struct NSOHeader {
|
|||||||
std::array<SHA256Hash, 3> segment_hashes;
|
std::array<SHA256Hash, 3> segment_hashes;
|
||||||
|
|
||||||
bool IsSegmentCompressed(size_t segment_num) const;
|
bool IsSegmentCompressed(size_t segment_num) const;
|
||||||
|
bool IsZBICCompressed() const {
|
||||||
|
return ((flags >> 7) & 1) != 0;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
static_assert(sizeof(NSOHeader) == 0x100, "NSOHeader has incorrect size.");
|
static_assert(sizeof(NSOHeader) == 0x100, "NSOHeader has incorrect size.");
|
||||||
static_assert(std::is_trivially_copyable_v<NSOHeader>, "NSOHeader must be trivially copyable.");
|
static_assert(std::is_trivially_copyable_v<NSOHeader>, "NSOHeader must be trivially copyable.");
|
||||||
|
|||||||
@@ -14,6 +14,7 @@
|
|||||||
#include <regex>
|
#include <regex>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
|
#include <boost/algorithm/string/trim.hpp>
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
// windows.h needs to be included before shellapi.h
|
// windows.h needs to be included before shellapi.h
|
||||||
@@ -132,7 +133,7 @@ static Network::Room::BanList LoadBanList(const std::string& path) {
|
|||||||
std::string line;
|
std::string line;
|
||||||
std::getline(file, line);
|
std::getline(file, line);
|
||||||
line.erase(std::remove(line.begin(), line.end(), '\0'), line.end());
|
line.erase(std::remove(line.begin(), line.end(), '\0'), line.end());
|
||||||
line = Common::StripSpaces(line);
|
boost::trim(line);
|
||||||
if (line.empty()) {
|
if (line.empty()) {
|
||||||
// An empty line marks start of the IP ban list
|
// An empty line marks start of the IP ban list
|
||||||
ban_list_type = true;
|
ban_list_type = true;
|
||||||
|
|||||||
@@ -761,9 +761,6 @@ void EmulatedController::StartMotionCalibration() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void EmulatedController::SetButton(const Common::Input::CallbackStatus& callback, std::size_t index, Common::UUID uuid) {
|
void EmulatedController::SetButton(const Common::Input::CallbackStatus& callback, std::size_t index, Common::UUID uuid) {
|
||||||
const auto player_index = Service::HID::NpadIdTypeToIndex(npad_id_type);
|
|
||||||
const auto& player = Settings::values.players.GetValue()[player_index];
|
|
||||||
|
|
||||||
if (index >= controller.button_values.size()) {
|
if (index >= controller.button_values.size()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -916,21 +913,10 @@ void EmulatedController::SetButton(const Common::Input::CallbackStatus& callback
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!is_connected) {
|
const auto player_index = Service::HID::NpadIdTypeToIndex(npad_id_type);
|
||||||
if (npad_type == NpadStyleIndex::Handheld) {
|
const auto& player = Settings::values.players.GetValue()[player_index];
|
||||||
if (npad_id_type == NpadIdType::Handheld) {
|
if (player.connected) {
|
||||||
Connect();
|
Connect();
|
||||||
controller_connected[player_index] = true;
|
|
||||||
}
|
|
||||||
} else if (npad_type != NpadStyleIndex::Handheld) {
|
|
||||||
if (npad_id_type == NpadIdType::Player1) {
|
|
||||||
Connect();
|
|
||||||
controller_connected[player_index] = true;
|
|
||||||
} else if (player.connected && !controller_connected[player_index]) {
|
|
||||||
Connect();
|
|
||||||
controller_connected[player_index] = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TriggerOnChange(ControllerTriggerType::Button, true);
|
TriggerOnChange(ControllerTriggerType::Button, true);
|
||||||
|
|||||||
@@ -22,7 +22,6 @@
|
|||||||
#include "common/settings.h"
|
#include "common/settings.h"
|
||||||
#include "common/vector_math.h"
|
#include "common/vector_math.h"
|
||||||
#include "hid_core/frontend/motion_input.h"
|
#include "hid_core/frontend/motion_input.h"
|
||||||
#include "hid_core/hid_core.h"
|
|
||||||
#include "hid_core/hid_types.h"
|
#include "hid_core/hid_types.h"
|
||||||
#include "hid_core/irsensor/irs_types.h"
|
#include "hid_core/irsensor/irs_types.h"
|
||||||
|
|
||||||
@@ -585,7 +584,6 @@ private:
|
|||||||
std::array<VibrationValue, 2> last_vibration_value{DEFAULT_VIBRATION_VALUE,
|
std::array<VibrationValue, 2> last_vibration_value{DEFAULT_VIBRATION_VALUE,
|
||||||
DEFAULT_VIBRATION_VALUE};
|
DEFAULT_VIBRATION_VALUE};
|
||||||
std::array<std::chrono::steady_clock::time_point, 2> last_vibration_timepoint{};
|
std::array<std::chrono::steady_clock::time_point, 2> last_vibration_timepoint{};
|
||||||
std::array<bool, HIDCore::available_controllers> controller_connected{};
|
|
||||||
|
|
||||||
// Atomically synched values
|
// Atomically synched values
|
||||||
std::atomic<HID::NpadStyleIndex> npad_type{HID::NpadStyleIndex::None};
|
std::atomic<HID::NpadStyleIndex> npad_type{HID::NpadStyleIndex::None};
|
||||||
|
|||||||
@@ -45,6 +45,19 @@ NPad::NPad(Core::HID::HIDCore& hid_core_, KernelHelpers::ServiceContext& service
|
|||||||
AbstractPad{hid_core_.kernel},
|
AbstractPad{hid_core_.kernel},
|
||||||
}}
|
}}
|
||||||
{
|
{
|
||||||
|
for (std::size_t aruid_index = 0; aruid_index < AruidIndexMax; ++aruid_index) {
|
||||||
|
for (std::size_t i = 0; i < controller_data[aruid_index].size(); ++i) {
|
||||||
|
auto& controller = controller_data[aruid_index][i];
|
||||||
|
controller.device = hid_core.GetEmulatedControllerByIndex(i);
|
||||||
|
Core::HID::ControllerUpdateCallback engine_callback{
|
||||||
|
.on_change = [this, i, kernel = &hid_core.kernel](Core::HID::ControllerTriggerType type) {
|
||||||
|
ControllerUpdate(*kernel, type, i);
|
||||||
|
},
|
||||||
|
.is_npad_service = true,
|
||||||
|
};
|
||||||
|
controller.callback_key = controller.device->SetCallback(engine_callback);
|
||||||
|
}
|
||||||
|
}
|
||||||
for (std::size_t i = 0; i < abstracted_pads.size(); ++i) {
|
for (std::size_t i = 0; i < abstracted_pads.size(); ++i) {
|
||||||
abstracted_pads[i].SetNpadId(IndexToNpadIdType(i));
|
abstracted_pads[i].SetNpadId(IndexToNpadIdType(i));
|
||||||
}
|
}
|
||||||
@@ -93,16 +106,6 @@ Result NPad::Activate(u64 aruid) {
|
|||||||
for (std::size_t i = 0; i < controller_data[aruid_index].size(); ++i) {
|
for (std::size_t i = 0; i < controller_data[aruid_index].size(); ++i) {
|
||||||
auto& controller = controller_data[aruid_index][i];
|
auto& controller = controller_data[aruid_index][i];
|
||||||
controller.shared_memory = &data->shared_memory_format->npad.npad_entry[i].internal_state;
|
controller.shared_memory = &data->shared_memory_format->npad.npad_entry[i].internal_state;
|
||||||
controller.device = hid_core.GetEmulatedControllerByIndex(i);
|
|
||||||
if (!controller.callback_key) {
|
|
||||||
Core::HID::ControllerUpdateCallback engine_callback{
|
|
||||||
.on_change = [this, i](Core::HID::ControllerTriggerType type) {
|
|
||||||
ControllerUpdate(hid_core.kernel, type, i);
|
|
||||||
},
|
|
||||||
.is_npad_service = true,
|
|
||||||
};
|
|
||||||
controller.callback_key = controller.device->SetCallback(engine_callback);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Prefill controller buffers
|
// Prefill controller buffers
|
||||||
|
|||||||
@@ -226,9 +226,7 @@ std::unique_ptr<TranslationMap> InitializeTranslations(QObject* parent) {
|
|||||||
INSERT(Settings, dma_accuracy, tr("DMA Accuracy:"),
|
INSERT(Settings, dma_accuracy, tr("DMA Accuracy:"),
|
||||||
tr("Controls the DMA read mode.\nUnsafe is faster, while Safe is more stable and can fix issues in some games.\nDefault follows the GPU Accuracy setting."));
|
tr("Controls the DMA read mode.\nUnsafe is faster, while Safe is more stable and can fix issues in some games.\nDefault follows the GPU Accuracy setting."));
|
||||||
INSERT(Settings, gpu_fence_behavior, tr("GPU Fence Behavior:"),
|
INSERT(Settings, gpu_fence_behavior, tr("GPU Fence Behavior:"),
|
||||||
tr("Controls the GPU fence synchronization behavior.\nImmediate is the fastest option, but can introduce some issues.\nBalanced offers better compatibility and may fix issues in some games.\nAccurate further improves compatibility at the cost of some performance.\nStrict is the slowest option, but can fix issues that require stricter synchronization.\nDefault follows the GPU Accuracy setting."));
|
tr("Controls the GPU fence synchronization behavior.\nImmediate is the fastest option, but can introduce some issues.\nBalanced offers better compatibility and may fix issues in some games.\nAccurate further improves compatibility at the cost of some performance.\nDefault follows the GPU Mode setting."));
|
||||||
INSERT(Settings, enable_gpu_buffer_readback, tr("Enable GPU buffer readback"),
|
|
||||||
tr("Preserves GPU-modified data by reading it back before uploading.\nSome games require this to render certain effects properly."));
|
|
||||||
INSERT(Settings, use_asynchronous_shaders, tr("Enable asynchronous shader compilation"),
|
INSERT(Settings, use_asynchronous_shaders, tr("Enable asynchronous shader compilation"),
|
||||||
tr("May reduce shader stutter."));
|
tr("May reduce shader stutter."));
|
||||||
INSERT(Settings, gpu_clock, tr("GPU Clocks"),
|
INSERT(Settings, gpu_clock, tr("GPU Clocks"),
|
||||||
@@ -442,7 +440,6 @@ std::unique_ptr<ComboboxTranslationMap> ComboboxEnumeration(QObject* parent) {
|
|||||||
PAIR(GpuFenceBehavior, Immediate, tr("Immediate")),
|
PAIR(GpuFenceBehavior, Immediate, tr("Immediate")),
|
||||||
PAIR(GpuFenceBehavior, Balanced, tr("Balanced")),
|
PAIR(GpuFenceBehavior, Balanced, tr("Balanced")),
|
||||||
PAIR(GpuFenceBehavior, Accurate, tr("Accurate")),
|
PAIR(GpuFenceBehavior, Accurate, tr("Accurate")),
|
||||||
PAIR(GpuFenceBehavior, Strict, tr("Strict")),
|
|
||||||
}});
|
}});
|
||||||
translations->insert(
|
translations->insert(
|
||||||
{Settings::EnumMetadata<Settings::CpuAccuracy>::Index(),
|
{Settings::EnumMetadata<Settings::CpuAccuracy>::Index(),
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
#include <QEventLoop>
|
#include <QEventLoop>
|
||||||
|
#include <boost/algorithm/string/case_conv.hpp>
|
||||||
#include <boost/algorithm/string/replace.hpp>
|
#include <boost/algorithm/string/replace.hpp>
|
||||||
#include "common/httplib.h"
|
#include "common/httplib.h"
|
||||||
|
|
||||||
@@ -45,7 +46,7 @@ void DiscordImpl::Pause() {
|
|||||||
|
|
||||||
std::string DiscordImpl::GetGameString(const std::string& title) {
|
std::string DiscordImpl::GetGameString(const std::string& title) {
|
||||||
// Convert to lowercase
|
// Convert to lowercase
|
||||||
std::string icon_name = Common::ToLower(title);
|
std::string icon_name = boost::algorithm::to_lower_copy(title);
|
||||||
|
|
||||||
// Replace spaces with dashes
|
// Replace spaces with dashes
|
||||||
std::replace(icon_name.begin(), icon_name.end(), ' ', '-');
|
std::replace(icon_name.begin(), icon_name.end(), ' ', '-');
|
||||||
|
|||||||
@@ -1,3 +1,6 @@
|
|||||||
|
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
@@ -28,11 +31,6 @@ struct FuncTraits<ReturnType_ (*)(Args...)> {
|
|||||||
using ArgType = std::tuple_element_t<I, std::tuple<Args...>>;
|
using ArgType = std::tuple_element_t<I, std::tuple<Args...>>;
|
||||||
};
|
};
|
||||||
|
|
||||||
template <auto func, typename... Args>
|
|
||||||
void SetDefinition(EmitContext& ctx, IR::Inst* inst, Args... args) {
|
|
||||||
inst->SetDefinition<Id>(func(ctx, std::forward<Args>(args)...));
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename ArgType>
|
template <typename ArgType>
|
||||||
auto Arg(EmitContext& ctx, const IR::Value& arg) {
|
auto Arg(EmitContext& ctx, const IR::Value& arg) {
|
||||||
if constexpr (std::is_same_v<ArgType, std::string_view>) {
|
if constexpr (std::is_same_v<ArgType, std::string_view>) {
|
||||||
@@ -53,22 +51,11 @@ auto Arg(EmitContext& ctx, const IR::Value& arg) {
|
|||||||
template <auto func, bool is_first_arg_inst, size_t... I>
|
template <auto func, bool is_first_arg_inst, size_t... I>
|
||||||
void Invoke(EmitContext& ctx, IR::Inst* inst, std::index_sequence<I...>) {
|
void Invoke(EmitContext& ctx, IR::Inst* inst, std::index_sequence<I...>) {
|
||||||
using Traits = FuncTraits<decltype(func)>;
|
using Traits = FuncTraits<decltype(func)>;
|
||||||
if constexpr (std::is_same_v<typename Traits::ReturnType, Id>) {
|
|
||||||
if constexpr (is_first_arg_inst) {
|
|
||||||
SetDefinition<func>(
|
|
||||||
ctx, inst, *inst,
|
|
||||||
Arg<typename Traits::template ArgType<I + 2>>(ctx, inst->Arg(I))...);
|
|
||||||
} else {
|
|
||||||
SetDefinition<func>(
|
|
||||||
ctx, inst, Arg<typename Traits::template ArgType<I + 1>>(ctx, inst->Arg(I))...);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if constexpr (is_first_arg_inst) {
|
if constexpr (is_first_arg_inst) {
|
||||||
func(ctx, *inst, Arg<typename Traits::template ArgType<I + 2>>(ctx, inst->Arg(I))...);
|
func(ctx, *inst, Arg<typename Traits::template ArgType<I + 2>>(ctx, inst->Arg(I))...);
|
||||||
} else {
|
} else {
|
||||||
func(ctx, Arg<typename Traits::template ArgType<I + 1>>(ctx, inst->Arg(I))...);
|
func(ctx, Arg<typename Traits::template ArgType<I + 1>>(ctx, inst->Arg(I))...);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template <auto func>
|
template <auto func>
|
||||||
|
|||||||
@@ -121,7 +121,7 @@ void BufferCache<P>::UnmapGPUMemory(size_t as_id, GPUVAddr gpu_addr, size_t size
|
|||||||
|
|
||||||
template <class P>
|
template <class P>
|
||||||
void BufferCache<P>::WriteMemory(DAddr device_addr, u64 size) {
|
void BufferCache<P>::WriteMemory(DAddr device_addr, u64 size) {
|
||||||
if (memory_tracker.IsRegionGpuModified(device_addr, size)) {
|
if (IsRegionGpuModified(device_addr, size)) {
|
||||||
ClearDownload(device_addr, size);
|
ClearDownload(device_addr, size);
|
||||||
gpu_modified_ranges.Subtract(device_addr, size);
|
gpu_modified_ranges.Subtract(device_addr, size);
|
||||||
}
|
}
|
||||||
@@ -249,6 +249,10 @@ bool BufferCache<P>::DMACopy(GPUVAddr src_address, GPUVAddr dest_address, u64 am
|
|||||||
runtime.CopyBuffer(dest_buffer, src_buffer, copies, true);
|
runtime.CopyBuffer(dest_buffer, src_buffer, copies, true);
|
||||||
if (has_new_downloads) {
|
if (has_new_downloads) {
|
||||||
memory_tracker.MarkRegionAsGpuModified(*cpu_dest_address, amount);
|
memory_tracker.MarkRegionAsGpuModified(*cpu_dest_address, amount);
|
||||||
|
const bool should_sync = Settings::IsGPUFenceBehaviorBalanced() || Settings::IsGPUFenceBehaviorAccurate();
|
||||||
|
if (should_sync) {
|
||||||
|
runtime.Finish();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Tegra::Memory::DeviceGuestMemoryScoped<u8, Tegra::Memory::GuestMemoryFlags::UnsafeReadWrite>
|
Tegra::Memory::DeviceGuestMemoryScoped<u8, Tegra::Memory::GuestMemoryFlags::UnsafeReadWrite>
|
||||||
@@ -311,11 +315,8 @@ std::pair<typename P::Buffer*, u32> BufferCache<P>::ObtainCPUBuffer(
|
|||||||
MarkWrittenBuffer(buffer_id, device_addr, size);
|
MarkWrittenBuffer(buffer_id, device_addr, size);
|
||||||
break;
|
break;
|
||||||
case ObtainBufferOperation::DiscardWrite: {
|
case ObtainBufferOperation::DiscardWrite: {
|
||||||
const DAddr device_addr_start = Common::AlignDown(device_addr, 64);
|
ClearDownload(device_addr, size);
|
||||||
const DAddr device_addr_end = Common::AlignUp(device_addr + size, 64);
|
gpu_modified_ranges.Subtract(device_addr, size);
|
||||||
const size_t new_size = device_addr_end - device_addr_start;
|
|
||||||
ClearDownload(device_addr_start, new_size);
|
|
||||||
gpu_modified_ranges.Subtract(device_addr_start, new_size);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
@@ -1233,7 +1234,7 @@ void BufferCache<P>::BindHostComputeStorageBuffers() {
|
|||||||
buffer.MarkUsage(offset, size);
|
buffer.MarkUsage(offset, size);
|
||||||
|
|
||||||
if (is_written) {
|
if (is_written) {
|
||||||
MarkWrittenBuffer(binding.buffer_id, binding.device_addr, size);
|
MarkWrittenBuffer(binding.buffer_id, binding.device_addr, size, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
if constexpr (NEEDS_BIND_STORAGE_INDEX) {
|
if constexpr (NEEDS_BIND_STORAGE_INDEX) {
|
||||||
@@ -1519,11 +1520,13 @@ void BufferCache<P>::UpdateComputeTextureBuffers() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
template <class P>
|
template <class P>
|
||||||
void BufferCache<P>::MarkWrittenBuffer(BufferId buffer_id, DAddr device_addr, u32 size) {
|
void BufferCache<P>::MarkWrittenBuffer(BufferId buffer_id, DAddr device_addr, u32 size, bool needs_sync) {
|
||||||
if constexpr (!IS_OPENGL) {
|
if constexpr (!IS_OPENGL) {
|
||||||
|
if (needs_sync) {
|
||||||
Buffer& buffer = slot_buffers[buffer_id];
|
Buffer& buffer = slot_buffers[buffer_id];
|
||||||
buffer.setWriteTick(runtime.CurrentTick());
|
buffer.setWriteTick(runtime.CurrentTick());
|
||||||
}
|
}
|
||||||
|
}
|
||||||
memory_tracker.MarkRegionAsGpuModified(device_addr, size);
|
memory_tracker.MarkRegionAsGpuModified(device_addr, size);
|
||||||
gpu_modified_ranges.Add(device_addr, size);
|
gpu_modified_ranges.Add(device_addr, size);
|
||||||
uncommitted_gpu_modified_ranges.Add(device_addr, size);
|
uncommitted_gpu_modified_ranges.Add(device_addr, size);
|
||||||
@@ -1538,8 +1541,11 @@ BufferId BufferCache<P>::FindBuffer(DAddr device_addr, u32 size, bool sparse_com
|
|||||||
const BufferId buffer_id = page_table[page];
|
const BufferId buffer_id = page_table[page];
|
||||||
if (buffer_id) {
|
if (buffer_id) {
|
||||||
Buffer& buffer = slot_buffers[buffer_id];
|
Buffer& buffer = slot_buffers[buffer_id];
|
||||||
WaitForGpuFenceIfNeeded(buffer);
|
|
||||||
if (buffer.IsInBounds(device_addr, size)) {
|
if (buffer.IsInBounds(device_addr, size)) {
|
||||||
|
const bool should_sync = Settings::IsGPUFenceBehaviorAccurate();
|
||||||
|
if (should_sync) {
|
||||||
|
SynchronizeBufferWrites(buffer);
|
||||||
|
}
|
||||||
bool usable = true;
|
bool usable = true;
|
||||||
if constexpr (requires { buffer.IsSparseCompatible(); }) {
|
if constexpr (requires { buffer.IsSparseCompatible(); }) {
|
||||||
if (sparse_compatible && !buffer.IsSparseCompatible()) {
|
if (sparse_compatible && !buffer.IsSparseCompatible()) {
|
||||||
@@ -1555,19 +1561,13 @@ BufferId BufferCache<P>::FindBuffer(DAddr device_addr, u32 size, bool sparse_com
|
|||||||
}
|
}
|
||||||
|
|
||||||
template <class P>
|
template <class P>
|
||||||
void BufferCache<P>::WaitForGpuFenceIfNeeded(Buffer& buffer) {
|
void BufferCache<P>::SynchronizeBufferWrites(Buffer& buffer) {
|
||||||
if constexpr (!IS_OPENGL) {
|
if constexpr (!IS_OPENGL) {
|
||||||
const bool gpu_fence_accurate = Settings::IsGPUFenceBehaviorAccurate();
|
|
||||||
const bool gpu_fence_strict = Settings::IsGPUFenceBehaviorStrict();
|
|
||||||
if (gpu_fence_accurate || gpu_fence_strict) {
|
|
||||||
const u64 gpu_tick_delay = gpu_fence_strict ? 0 : 3;
|
|
||||||
const u64 buffer_tick = buffer.getWriteTick();
|
const u64 buffer_tick = buffer.getWriteTick();
|
||||||
const u64 gpu_tick = runtime.KnownGpuTick();
|
if (!runtime.IsFree(buffer_tick)) {
|
||||||
if (buffer_tick > gpu_tick + gpu_tick_delay) {
|
|
||||||
runtime.Wait(buffer_tick);
|
runtime.Wait(buffer_tick);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template <class P>
|
template <class P>
|
||||||
@@ -1742,14 +1742,24 @@ bool BufferCache<P>::SynchronizeBuffer(Buffer& buffer, DAddr device_addr, u32 si
|
|||||||
u64 total_size_bytes = 0;
|
u64 total_size_bytes = 0;
|
||||||
u64 largest_copy = 0;
|
u64 largest_copy = 0;
|
||||||
const DAddr buffer_start = buffer.cpu_addr_cached;
|
const DAddr buffer_start = buffer.cpu_addr_cached;
|
||||||
memory_tracker.ForEachUploadRange(device_addr, size, [&](u64 device_addr_out, u64 range_size) {
|
const auto add_upload = [&](DAddr start, DAddr end) {
|
||||||
|
if (start == end) return;
|
||||||
|
const u64 range_size = end - start;
|
||||||
upload_copies.push_back(BufferCopy{
|
upload_copies.push_back(BufferCopy{
|
||||||
.src_offset = total_size_bytes,
|
.src_offset = total_size_bytes,
|
||||||
.dst_offset = device_addr_out - buffer_start,
|
.dst_offset = start - buffer_start,
|
||||||
.size = range_size,
|
.size = range_size,
|
||||||
});
|
});
|
||||||
total_size_bytes += range_size;
|
total_size_bytes += range_size;
|
||||||
largest_copy = (std::max)(largest_copy, range_size);
|
largest_copy = (std::max)(largest_copy, range_size);
|
||||||
|
};
|
||||||
|
memory_tracker.ForEachUploadRange(device_addr, size, [&](u64 device_addr_out, u64 range_size) {
|
||||||
|
DAddr upload_start = device_addr_out;
|
||||||
|
gpu_modified_ranges.ForEachInRange(device_addr_out, range_size, [&](DAddr gpu_start, DAddr gpu_end) {
|
||||||
|
add_upload(upload_start, gpu_start);
|
||||||
|
upload_start = gpu_end;
|
||||||
|
});
|
||||||
|
add_upload(upload_start, device_addr_out + range_size);
|
||||||
});
|
});
|
||||||
if (total_size_bytes == 0) {
|
if (total_size_bytes == 0) {
|
||||||
return true;
|
return true;
|
||||||
@@ -1788,9 +1798,6 @@ void BufferCache<P>::ImmediateUploadMemory([[maybe_unused]] Buffer& buffer,
|
|||||||
if (immediate_buffer.empty()) {
|
if (immediate_buffer.empty()) {
|
||||||
immediate_buffer = ImmediateBuffer(largest_copy);
|
immediate_buffer = ImmediateBuffer(largest_copy);
|
||||||
}
|
}
|
||||||
if (Settings::values.enable_gpu_buffer_readback.GetValue()) {
|
|
||||||
DownloadBufferMemory(buffer, device_addr, copy.size);
|
|
||||||
}
|
|
||||||
device_memory.ReadBlockUnsafe(device_addr, immediate_buffer.data(), copy.size);
|
device_memory.ReadBlockUnsafe(device_addr, immediate_buffer.data(), copy.size);
|
||||||
upload_span = immediate_buffer.subspan(0, copy.size);
|
upload_span = immediate_buffer.subspan(0, copy.size);
|
||||||
}
|
}
|
||||||
@@ -1809,9 +1816,6 @@ void BufferCache<P>::MappedUploadMemory([[maybe_unused]] Buffer& buffer,
|
|||||||
for (BufferCopy& copy : copies) {
|
for (BufferCopy& copy : copies) {
|
||||||
u8* const src_pointer = staging_pointer.data() + copy.src_offset;
|
u8* const src_pointer = staging_pointer.data() + copy.src_offset;
|
||||||
const DAddr device_addr = buffer.CpuAddr() + copy.dst_offset;
|
const DAddr device_addr = buffer.CpuAddr() + copy.dst_offset;
|
||||||
if (Settings::values.enable_gpu_buffer_readback.GetValue()) {
|
|
||||||
DownloadBufferMemory(buffer, device_addr, copy.size);
|
|
||||||
}
|
|
||||||
device_memory.ReadBlockUnsafe(device_addr, src_pointer, copy.size);
|
device_memory.ReadBlockUnsafe(device_addr, src_pointer, copy.size);
|
||||||
// Apply the staging offset
|
// Apply the staging offset
|
||||||
copy.src_offset += upload_staging.offset;
|
copy.src_offset += upload_staging.offset;
|
||||||
|
|||||||
@@ -430,11 +430,11 @@ private:
|
|||||||
|
|
||||||
void UpdateComputeTextureBuffers();
|
void UpdateComputeTextureBuffers();
|
||||||
|
|
||||||
void MarkWrittenBuffer(BufferId buffer_id, DAddr device_addr, u32 size);
|
void MarkWrittenBuffer(BufferId buffer_id, DAddr device_addr, u32 size, bool needs_sync = false);
|
||||||
|
|
||||||
[[nodiscard]] BufferId FindBuffer(DAddr device_addr, u32 size, bool sparse_compatible);
|
[[nodiscard]] BufferId FindBuffer(DAddr device_addr, u32 size, bool sparse_compatible);
|
||||||
|
|
||||||
void WaitForGpuFenceIfNeeded(Buffer& buffer);
|
void SynchronizeBufferWrites(Buffer& buffer);
|
||||||
|
|
||||||
[[nodiscard]] OverlapResult ResolveOverlaps(DAddr device_addr, u32 wanted_size);
|
[[nodiscard]] OverlapResult ResolveOverlaps(DAddr device_addr, u32 wanted_size);
|
||||||
|
|
||||||
|
|||||||
@@ -16,7 +16,7 @@
|
|||||||
namespace Tegra {
|
namespace Tegra {
|
||||||
|
|
||||||
constexpr u32 MacroRegistersStart = 0xE00;
|
constexpr u32 MacroRegistersStart = 0xE00;
|
||||||
[[maybe_unused]] constexpr u32 ComputeInline = 0x6D;
|
constexpr u32 ComputeInline = 0x6D;
|
||||||
|
|
||||||
DmaPusher::DmaPusher(Core::System& system_, MemoryManager& memory_manager_, Control::ChannelState& channel_state_)
|
DmaPusher::DmaPusher(Core::System& system_, MemoryManager& memory_manager_, Control::ChannelState& channel_state_)
|
||||||
: system{system_}
|
: system{system_}
|
||||||
@@ -73,11 +73,16 @@ bool DmaPusher::Step() {
|
|||||||
synced = false;
|
synced = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (header.size > 0 && dma_state.method >= MacroRegistersStart && subchannels[dma_state.subchannel]) {
|
|
||||||
subchannels[dma_state.subchannel]->current_dirty = memory_manager.IsMemoryDirty(dma_state.dma_get, header.size * sizeof(u32));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (header.size > 0) {
|
if (header.size > 0) {
|
||||||
|
if (subchannels[dma_state.subchannel] && dma_state.method_count) {
|
||||||
|
const auto engine = subchannel_type[dma_state.subchannel];
|
||||||
|
const bool kepler_payload = engine == Engines::EngineTypes::KeplerCompute && dma_state.method == ComputeInline && dma_state.non_incrementing;
|
||||||
|
const bool macro_payload = engine == Engines::EngineTypes::Maxwell3D && dma_state.method >= MacroRegistersStart;
|
||||||
|
if (kepler_payload || macro_payload) {
|
||||||
|
const size_t words = std::min<size_t>(dma_state.method_count, header.size);
|
||||||
|
subchannels[dma_state.subchannel]->current_dirty = memory_manager.IsMemoryDirty(dma_state.dma_get, words * sizeof(u32));
|
||||||
|
}
|
||||||
|
}
|
||||||
const bool use_safe = Settings::IsDMALevelDefault() ? Settings::IsGPULevelHigh() : Settings::IsDMALevelSafe();
|
const bool use_safe = Settings::IsDMALevelDefault() ? Settings::IsGPULevelHigh() : Settings::IsDMALevelSafe();
|
||||||
if (use_safe) {
|
if (use_safe) {
|
||||||
Tegra::Memory::GpuGuestMemory<Tegra::CommandHeader, Tegra::Memory::GuestMemoryFlags::SafeRead>headers(memory_manager, dma_state.dma_get, header.size, &command_headers);
|
Tegra::Memory::GpuGuestMemory<Tegra::CommandHeader, Tegra::Memory::GuestMemoryFlags::SafeRead>headers(memory_manager, dma_state.dma_get, header.size, &command_headers);
|
||||||
|
|||||||
@@ -49,13 +49,16 @@ void KeplerCompute::CallMethod(Core::System& system, u32 method, u32 method_argu
|
|||||||
case KEPLER_COMPUTE_REG_INDEX(exec_upload): {
|
case KEPLER_COMPUTE_REG_INDEX(exec_upload): {
|
||||||
UploadInfo info{.upload_address = upload_address,
|
UploadInfo info{.upload_address = upload_address,
|
||||||
.exec_address = upload_state.ExecTargetAddress(),
|
.exec_address = upload_state.ExecTargetAddress(),
|
||||||
.copy_size = upload_state.GetUploadSize()};
|
.copy_size = upload_state.GetUploadSize(),
|
||||||
|
.was_dirty = upload_dirty};
|
||||||
uploads.push_back(info);
|
uploads.push_back(info);
|
||||||
upload_state.ProcessExec(regs.exec_upload.linear != 0);
|
upload_state.ProcessExec(regs.exec_upload.linear != 0);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case KEPLER_COMPUTE_REG_INDEX(data_upload): {
|
case KEPLER_COMPUTE_REG_INDEX(data_upload): {
|
||||||
upload_address = current_dma_segment;
|
upload_address = current_dma_segment;
|
||||||
|
upload_dirty = current_dirty;
|
||||||
|
current_dirty = false;
|
||||||
upload_state.ProcessData(method_argument, is_last_call);
|
upload_state.ProcessData(method_argument, is_last_call);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -64,11 +67,13 @@ void KeplerCompute::CallMethod(Core::System& system, u32 method, u32 method_argu
|
|||||||
|
|
||||||
for (auto& data : uploads) {
|
for (auto& data : uploads) {
|
||||||
const GPUVAddr offset = data.exec_address - launch_desc_loc;
|
const GPUVAddr offset = data.exec_address - launch_desc_loc;
|
||||||
if (offset / sizeof(u32) == LAUNCH_REG_INDEX(grid_dim_x) &&
|
if (offset / sizeof(u32) == LAUNCH_REG_INDEX(grid_dim_x)) {
|
||||||
memory_manager.IsMemoryDirty(data.upload_address, data.copy_size)) {
|
const bool source_dirty = memory_manager.IsMemoryDirty(data.upload_address, data.copy_size);
|
||||||
|
if (data.was_dirty || source_dirty) {
|
||||||
indirect_compute = {data.upload_address};
|
indirect_compute = {data.upload_address};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
uploads.clear();
|
uploads.clear();
|
||||||
ProcessLaunch();
|
ProcessLaunch();
|
||||||
indirect_compute = std::nullopt;
|
indirect_compute = std::nullopt;
|
||||||
@@ -83,6 +88,8 @@ void KeplerCompute::CallMultiMethod(Core::System& system, u32 method, const u32*
|
|||||||
switch (method) {
|
switch (method) {
|
||||||
case KEPLER_COMPUTE_REG_INDEX(data_upload):
|
case KEPLER_COMPUTE_REG_INDEX(data_upload):
|
||||||
upload_address = current_dma_segment;
|
upload_address = current_dma_segment;
|
||||||
|
upload_dirty = current_dirty;
|
||||||
|
current_dirty = false;
|
||||||
upload_state.ProcessData(base_start, amount);
|
upload_state.ProcessData(base_start, amount);
|
||||||
return;
|
return;
|
||||||
default:
|
default:
|
||||||
|
|||||||
@@ -226,11 +226,13 @@ private:
|
|||||||
VideoCore::RasterizerInterface* rasterizer = nullptr;
|
VideoCore::RasterizerInterface* rasterizer = nullptr;
|
||||||
Upload::State upload_state;
|
Upload::State upload_state;
|
||||||
GPUVAddr upload_address;
|
GPUVAddr upload_address;
|
||||||
|
bool upload_dirty{};
|
||||||
|
|
||||||
struct UploadInfo {
|
struct UploadInfo {
|
||||||
GPUVAddr upload_address;
|
GPUVAddr upload_address;
|
||||||
GPUVAddr exec_address;
|
GPUVAddr exec_address;
|
||||||
u32 copy_size;
|
u32 copy_size;
|
||||||
|
bool was_dirty;
|
||||||
};
|
};
|
||||||
std::vector<UploadInfo> uploads;
|
std::vector<UploadInfo> uploads;
|
||||||
std::optional<GPUVAddr> indirect_compute{};
|
std::optional<GPUVAddr> indirect_compute{};
|
||||||
|
|||||||
@@ -72,7 +72,7 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
void SignalFence(std::function<void()>&& func) {
|
void SignalFence(std::function<void()>&& func) {
|
||||||
const bool delay_fence = Settings::IsGPUFenceBehaviorDefault() ? Settings::IsGPULevelHigh() : Settings::IsGPUFenceBehaviorBalanced() || Settings::IsGPUFenceBehaviorAccurate() || Settings::IsGPUFenceBehaviorStrict();
|
const bool delay_fence = Settings::IsGPUFenceBehaviorDefault() ? Settings::IsGPULevelHigh() : Settings::IsGPUFenceBehaviorBalanced() || Settings::IsGPUFenceBehaviorAccurate();
|
||||||
const bool should_flush = ShouldFlush();
|
const bool should_flush = ShouldFlush();
|
||||||
if constexpr (!can_async_check) {
|
if constexpr (!can_async_check) {
|
||||||
TryReleasePendingFences<false>();
|
TryReleasePendingFences<false>();
|
||||||
|
|||||||
@@ -260,7 +260,7 @@ void QueryCacheBase<Traits>::CounterReport(GPUVAddr addr, QueryType counter_type
|
|||||||
};
|
};
|
||||||
u8* pointer = impl->device_memory.template GetPointer<u8>(cpu_addr);
|
u8* pointer = impl->device_memory.template GetPointer<u8>(cpu_addr);
|
||||||
u8* pointer_timestamp = impl->device_memory.template GetPointer<u8>(cpu_addr + 8);
|
u8* pointer_timestamp = impl->device_memory.template GetPointer<u8>(cpu_addr + 8);
|
||||||
bool is_synced = (Settings::IsGPUFenceBehaviorDefault() ? !Settings::IsGPULevelHigh() : !Settings::IsGPUFenceBehaviorBalanced() && !Settings::IsGPUFenceBehaviorAccurate() && !Settings::IsGPUFenceBehaviorStrict()) && is_fence;
|
bool is_synced = (Settings::IsGPUFenceBehaviorDefault() ? !Settings::IsGPULevelHigh() : !Settings::IsGPUFenceBehaviorBalanced() && !Settings::IsGPUFenceBehaviorAccurate()) && is_fence;
|
||||||
std::function<void()> operation([this, is_synced, streamer, query_base = query, query_location,
|
std::function<void()> operation([this, is_synced, streamer, query_base = query, query_location,
|
||||||
pointer, pointer_timestamp] {
|
pointer, pointer_timestamp] {
|
||||||
if (True(query_base->flags & QueryFlagBits::IsInvalidated)) {
|
if (True(query_base->flags & QueryFlagBits::IsInvalidated)) {
|
||||||
|
|||||||
@@ -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-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||||
@@ -55,7 +55,7 @@ size_t StagingBuffers::RequestBuffer(size_t requested_size) {
|
|||||||
}
|
}
|
||||||
StagingBufferAlloc alloc;
|
StagingBufferAlloc alloc;
|
||||||
alloc.buffer.Create();
|
alloc.buffer.Create();
|
||||||
const auto next_pow2_size = Common::NextPow2(requested_size);
|
const auto next_pow2_size = std::bit_ceil(requested_size);
|
||||||
glNamedBufferStorage(alloc.buffer.handle, next_pow2_size, nullptr,
|
glNamedBufferStorage(alloc.buffer.handle, next_pow2_size, nullptr,
|
||||||
storage_flags | GL_MAP_PERSISTENT_BIT);
|
storage_flags | GL_MAP_PERSISTENT_BIT);
|
||||||
alloc.map = static_cast<u8*>(glMapNamedBufferRange(alloc.buffer.handle, 0, next_pow2_size,
|
alloc.map = static_cast<u8*>(glMapNamedBufferRange(alloc.buffer.handle, 0, next_pow2_size,
|
||||||
|
|||||||
@@ -1404,7 +1404,7 @@ void FormatConversionPass::ConvertImage(Image& dst_image, Image& src_image,
|
|||||||
const u32 copy_size = region.width * region.height * region.depth * img_bpp;
|
const u32 copy_size = region.width * region.height * region.depth * img_bpp;
|
||||||
if (pbo_size < copy_size) {
|
if (pbo_size < copy_size) {
|
||||||
intermediate_pbo.Create();
|
intermediate_pbo.Create();
|
||||||
pbo_size = Common::NextPow2(copy_size);
|
pbo_size = std::bit_ceil(copy_size);
|
||||||
glNamedBufferData(intermediate_pbo.handle, pbo_size, nullptr, GL_STREAM_COPY);
|
glNamedBufferData(intermediate_pbo.handle, pbo_size, nullptr, GL_STREAM_COPY);
|
||||||
}
|
}
|
||||||
// Copy from source to PBO
|
// Copy from source to PBO
|
||||||
|
|||||||
@@ -94,11 +94,14 @@ void Layer::ConfigureDraw(const Device& device, PresentPushConstants* out_push_c
|
|||||||
const u32 scaled_width = texture_info ? texture_info->scaled_width : texture_width;
|
const u32 scaled_width = texture_info ? texture_info->scaled_width : texture_width;
|
||||||
const u32 scaled_height = texture_info ? texture_info->scaled_height : texture_height;
|
const u32 scaled_height = texture_info ? texture_info->scaled_height : texture_height;
|
||||||
const bool use_accelerated = texture_info.has_value();
|
const bool use_accelerated = texture_info.has_value();
|
||||||
|
const bool is_applet =
|
||||||
|
(framebuffer.layer_stack_mask & Service::Nvnflinger::LayerStackBit(
|
||||||
|
Service::Nvnflinger::LayerStackId::Recording)) == 0;
|
||||||
|
|
||||||
RefreshResources(device, framebuffer);
|
RefreshResources(device, framebuffer);
|
||||||
SetAntiAliasPass(device);
|
SetAntiAliasPass(device);
|
||||||
#ifdef HAS_RESHADE
|
#ifdef HAS_RESHADE
|
||||||
SetPostProcessPass(device);
|
SetPostProcessPass(device, is_applet);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Finish any pending renderpass
|
// Finish any pending renderpass
|
||||||
@@ -138,9 +141,12 @@ void Layer::ConfigureDraw(const Device& device, PresentPushConstants* out_push_c
|
|||||||
source_image_view = fsr->Draw(device, scheduler, image_index, source_image, source_image_view, render_extent, crop_rect);
|
source_image_view = fsr->Draw(device, scheduler, image_index, source_image, source_image_view, render_extent, crop_rect);
|
||||||
crop_rect = {0, 0, 1, 1};
|
crop_rect = {0, 0, 1, 1};
|
||||||
} else if (auto* sgsr = std::get_if<SGSR>(&sr_filter)) {
|
} else if (auto* sgsr = std::get_if<SGSR>(&sr_filter)) {
|
||||||
source_image_view = sgsr->Draw(device, scheduler, image_index, source_image, source_image_view, render_extent, crop_rect);
|
if (!is_applet) {
|
||||||
|
source_image_view = sgsr->Draw(device, scheduler, image_index, source_image,
|
||||||
|
source_image_view, render_extent, crop_rect);
|
||||||
crop_rect = {0, 0, 1, 1};
|
crop_rect = {0, 0, 1, 1};
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
SetMatrixData(device, *out_push_constants, layout);
|
SetMatrixData(device, *out_push_constants, layout);
|
||||||
SetVertexData(device, *out_push_constants, layout, crop_rect);
|
SetVertexData(device, *out_push_constants, layout, crop_rect);
|
||||||
@@ -228,7 +234,11 @@ void Layer::SetAntiAliasPass(const Device& device) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#ifdef HAS_RESHADE
|
#ifdef HAS_RESHADE
|
||||||
void Layer::SetPostProcessPass(const Device& device) {
|
void Layer::SetPostProcessPass(const Device& device, bool is_applet) {
|
||||||
|
if (is_applet) {
|
||||||
|
post_process.reset();
|
||||||
|
return;
|
||||||
|
}
|
||||||
const VkExtent2D render_area{
|
const VkExtent2D render_area{
|
||||||
.width = Settings::values.resolution_info.ScaleUp(raw_width),
|
.width = Settings::values.resolution_info.ScaleUp(raw_width),
|
||||||
.height = Settings::values.resolution_info.ScaleUp(raw_height),
|
.height = Settings::values.resolution_info.ScaleUp(raw_height),
|
||||||
|
|||||||
@@ -70,7 +70,7 @@ private:
|
|||||||
void RefreshResources(const Device& device, const Tegra::FramebufferConfig& framebuffer);
|
void RefreshResources(const Device& device, const Tegra::FramebufferConfig& framebuffer);
|
||||||
void SetAntiAliasPass(const Device& device);
|
void SetAntiAliasPass(const Device& device);
|
||||||
#ifdef HAS_RESHADE
|
#ifdef HAS_RESHADE
|
||||||
void SetPostProcessPass(const Device& device);
|
void SetPostProcessPass(const Device& device, bool is_applet);
|
||||||
#endif
|
#endif
|
||||||
void ReleaseRawImages();
|
void ReleaseRawImages();
|
||||||
|
|
||||||
|
|||||||
@@ -419,15 +419,15 @@ void BufferCacheRuntime::TickFrame(Common::SlotVector<Buffer>& slot_buffers) noe
|
|||||||
}
|
}
|
||||||
|
|
||||||
u64 BufferCacheRuntime::CurrentTick() {
|
u64 BufferCacheRuntime::CurrentTick() {
|
||||||
return scheduler.GetMasterSemaphore().CurrentTick();
|
return scheduler.CurrentTick();
|
||||||
}
|
}
|
||||||
|
|
||||||
u64 BufferCacheRuntime::KnownGpuTick() {
|
bool BufferCacheRuntime::IsFree(u64 tick) {
|
||||||
return scheduler.GetMasterSemaphore().KnownGpuTick();
|
return scheduler.IsFree(tick);
|
||||||
}
|
}
|
||||||
|
|
||||||
void BufferCacheRuntime::Wait(u64 buffer_tick) {
|
void BufferCacheRuntime::Wait(u64 tick) {
|
||||||
scheduler.Wait(buffer_tick);
|
scheduler.Wait(tick);
|
||||||
}
|
}
|
||||||
|
|
||||||
void BufferCacheRuntime::Finish() {
|
void BufferCacheRuntime::Finish() {
|
||||||
|
|||||||
@@ -112,9 +112,9 @@ public:
|
|||||||
|
|
||||||
u64 CurrentTick();
|
u64 CurrentTick();
|
||||||
|
|
||||||
u64 KnownGpuTick();
|
bool IsFree(u64 tick);
|
||||||
|
|
||||||
void Wait(u64 buffer_tick);
|
void Wait(u64 tick);
|
||||||
|
|
||||||
void Finish();
|
void Finish();
|
||||||
|
|
||||||
|
|||||||
@@ -1011,7 +1011,7 @@ VkBuffer TextureCacheRuntime::GetTemporaryBuffer(size_t needed_size) {
|
|||||||
if (buffers[level]) {
|
if (buffers[level]) {
|
||||||
return *buffers[level];
|
return *buffers[level];
|
||||||
}
|
}
|
||||||
const auto new_size = Common::NextPow2(needed_size);
|
const auto new_size = std::bit_ceil(needed_size);
|
||||||
static constexpr VkBufferUsageFlags flags =
|
static constexpr VkBufferUsageFlags flags =
|
||||||
VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT |
|
VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT |
|
||||||
VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT | VK_BUFFER_USAGE_STORAGE_TEXEL_BUFFER_BIT;
|
VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT | VK_BUFFER_USAGE_STORAGE_TEXEL_BUFFER_BIT;
|
||||||
|
|||||||
@@ -2190,7 +2190,7 @@ void TextureCache<P>::UnregisterImage(ImageId image_id) {
|
|||||||
image.flags &= ~ImageFlagBits::BadOverlap;
|
image.flags &= ~ImageFlagBits::BadOverlap;
|
||||||
lru_cache.Free(image.lru_index);
|
lru_cache.Free(image.lru_index);
|
||||||
const auto& clear_page_table =
|
const auto& clear_page_table =
|
||||||
[image_id](u64 page, ::Common::unordered_map<u64, std::vector<ImageId>, Common::IdentityHash<u64>>& selected_page_table) {
|
[image_id](u64 page, ::Common::unordered_map<u64, std::vector<ImageId>>& selected_page_table) {
|
||||||
const auto page_it = selected_page_table.find(page);
|
const auto page_it = selected_page_table.find(page);
|
||||||
if (page_it == selected_page_table.end()) {
|
if (page_it == selected_page_table.end()) {
|
||||||
ASSERT_MSG(false, "Unregistering unregistered page={:#x}", page << YUZU_PAGEBITS);
|
ASSERT_MSG(false, "Unregistering unregistered page={:#x}", page << YUZU_PAGEBITS);
|
||||||
|
|||||||
@@ -22,7 +22,6 @@
|
|||||||
#include <queue>
|
#include <queue>
|
||||||
|
|
||||||
#include "common/common_types.h"
|
#include "common/common_types.h"
|
||||||
#include "common/hash.h"
|
|
||||||
#include "common/literals.h"
|
#include "common/literals.h"
|
||||||
#include "common/lru_cache.h"
|
#include "common/lru_cache.h"
|
||||||
#include <ranges>
|
#include <ranges>
|
||||||
@@ -69,7 +68,7 @@ struct AsyncDecodeContext {
|
|||||||
std::atomic_bool complete;
|
std::atomic_bool complete;
|
||||||
};
|
};
|
||||||
|
|
||||||
using TextureCacheGPUMap = ::Common::unordered_map<u64, std::vector<ImageId>, Common::IdentityHash<u64>>;
|
using TextureCacheGPUMap = ::Common::unordered_map<u64, std::vector<ImageId>>;
|
||||||
|
|
||||||
class TextureCacheChannelInfo : public ChannelInfo {
|
class TextureCacheChannelInfo : public ChannelInfo {
|
||||||
public:
|
public:
|
||||||
@@ -443,7 +442,7 @@ private:
|
|||||||
u64 last_framebuffer_serial = 0;
|
u64 last_framebuffer_serial = 0;
|
||||||
|
|
||||||
::Common::unordered_map<RenderTargets, FramebufferId> framebuffers;
|
::Common::unordered_map<RenderTargets, FramebufferId> framebuffers;
|
||||||
::Common::unordered_map<u64, std::vector<ImageMapId>, Common::IdentityHash<u64>> page_table;
|
::Common::unordered_map<u64, std::vector<ImageMapId>> page_table;
|
||||||
::Common::unordered_map<ImageId, boost::container::small_vector<ImageViewId, 16>> sparse_views;
|
::Common::unordered_map<ImageId, boost::container::small_vector<ImageViewId, 16>> sparse_views;
|
||||||
|
|
||||||
DAddr virtual_invalid_space{};
|
DAddr virtual_invalid_space{};
|
||||||
|
|||||||
+16
-66
@@ -3,6 +3,7 @@
|
|||||||
|
|
||||||
// Static Qt on macOS doesn't use Vulkan
|
// Static Qt on macOS doesn't use Vulkan
|
||||||
// Other platforms do, and thus have conflicting VulkanMemoryAllocator symbols
|
// Other platforms do, and thus have conflicting VulkanMemoryAllocator symbols
|
||||||
|
#include "core/launch_params.h"
|
||||||
#if defined(QT_STATICPLUGIN) && !defined(__APPLE__)
|
#if defined(QT_STATICPLUGIN) && !defined(__APPLE__)
|
||||||
#undef VMA_IMPLEMENTATION
|
#undef VMA_IMPLEMENTATION
|
||||||
#endif
|
#endif
|
||||||
@@ -515,79 +516,28 @@ MainWindow::MainWindow(bool has_broken_vulkan)
|
|||||||
// to prevent the UI from blowing up.
|
// to prevent the UI from blowing up.
|
||||||
UpdateUITheme();
|
UpdateUITheme();
|
||||||
|
|
||||||
QStringList args = QApplication::arguments();
|
if (QStringList args = QApplication::arguments(); args.size() >= 2) {
|
||||||
|
QList<QByteArray> qba{};
|
||||||
|
for (auto const& e : args)
|
||||||
|
qba.push_back(e.toUtf8());
|
||||||
|
std::vector<char*> args_cstr{};
|
||||||
|
for (auto& e : qba)
|
||||||
|
args_cstr.push_back(e.data());
|
||||||
|
|
||||||
if (args.size() < 2) {
|
auto const lp = Core::ParseLaunchParams(*QtCommon::system, args_cstr.size(), args_cstr.data(), nullptr);
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
QString game_path;
|
|
||||||
bool should_launch_qlaunch = false;
|
|
||||||
bool should_launch_hlaunch = false;
|
|
||||||
bool should_launch_setup = false;
|
|
||||||
bool has_gamepath = false;
|
|
||||||
bool is_fullscreen = false;
|
|
||||||
|
|
||||||
// Preserves drag/drop functionality
|
|
||||||
for (int i = 1; i < args.size(); ++i) {
|
|
||||||
if (args[i] == QStringLiteral("-f")) {
|
|
||||||
// Launch game in fullscreen mode
|
|
||||||
is_fullscreen = true;
|
|
||||||
} else if (args[i] == QStringLiteral("-u") && i < args.size() - 1) {
|
|
||||||
// Launch game with a specific user
|
|
||||||
int user_arg_idx = ++i;
|
|
||||||
bool argument_ok;
|
|
||||||
std::size_t selected_user = args[user_arg_idx].toUInt(&argument_ok);
|
|
||||||
if (!argument_ok) {
|
|
||||||
// try to look it up by username, only finds the first username that matches.
|
|
||||||
std::string const user_arg_str = args[user_arg_idx].toStdString();
|
|
||||||
auto const user_idx =
|
|
||||||
QtCommon::system->GetProfileManager().GetUserIndex(user_arg_str);
|
|
||||||
if (user_idx != std::nullopt) {
|
|
||||||
selected_user = user_idx.value();
|
|
||||||
} else {
|
|
||||||
LOG_ERROR(Frontend, "Invalid user argument '{}'", user_arg_str);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (QtCommon::system->GetProfileManager().UserExistsIndex(selected_user)) {
|
|
||||||
Settings::values.current_user = s32(selected_user);
|
|
||||||
user_flag_cmd_line = true;
|
|
||||||
} else {
|
|
||||||
LOG_ERROR(Frontend, "Selected user {} doesn't exist", selected_user);
|
|
||||||
}
|
|
||||||
} else if (args[i] == QStringLiteral("-g") && i < args.size() - 1) {
|
|
||||||
// Launch game at path
|
|
||||||
game_path = args[++i];
|
|
||||||
has_gamepath = true;
|
|
||||||
} else if (args[i] == QStringLiteral("-input-profile") && i < args.size() - 1) {
|
|
||||||
auto& players = Settings::values.players.GetValue();
|
|
||||||
players[0].profile_name = args[++i].toStdString();
|
|
||||||
} else if (args[i] == QStringLiteral("-qlaunch")) {
|
|
||||||
should_launch_qlaunch = true;
|
|
||||||
} else if (args[i] == QStringLiteral("-hlaunch")) {
|
|
||||||
should_launch_hlaunch = true;
|
|
||||||
} else if (args[i] == QStringLiteral("-setup")) {
|
|
||||||
should_launch_setup = true;
|
|
||||||
} else {
|
|
||||||
game_path = args[i];
|
|
||||||
has_gamepath = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Override fullscreen setting if gamepath or argument is provided
|
// Override fullscreen setting if gamepath or argument is provided
|
||||||
if (has_gamepath || is_fullscreen) {
|
if (!lp.filepath.empty() || lp.fullscreen) {
|
||||||
ui->action_Fullscreen->setChecked(is_fullscreen);
|
ui->action_Fullscreen->setChecked(lp.fullscreen);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (should_launch_setup) {
|
if (!lp.filepath.empty()) {
|
||||||
|
BootGame(QString::fromStdString(lp.filepath), ApplicationAppletParameters());
|
||||||
|
} else if (lp.launch_setup) {
|
||||||
LaunchFirmwareApplet(u64(Service::AM::AppletProgramId::Starter), std::nullopt);
|
LaunchFirmwareApplet(u64(Service::AM::AppletProgramId::Starter), std::nullopt);
|
||||||
} else {
|
} else if (lp.launch_qlaunch) {
|
||||||
if (!game_path.isEmpty()) {
|
|
||||||
BootGame(game_path, ApplicationAppletParameters());
|
|
||||||
} else if (should_launch_qlaunch) {
|
|
||||||
LaunchFirmwareApplet(u64(Service::AM::AppletProgramId::QLaunch), std::nullopt);
|
LaunchFirmwareApplet(u64(Service::AM::AppletProgramId::QLaunch), std::nullopt);
|
||||||
} else if (should_launch_hlaunch) {
|
} else if (lp.launch_hlaunch) {
|
||||||
std::filesystem::path const sd_dir =
|
std::filesystem::path const sd_dir =
|
||||||
Common::FS::GetEdenPathString(Common::FS::EdenPath::SDMCDir);
|
Common::FS::GetEdenPathString(Common::FS::EdenPath::SDMCDir);
|
||||||
auto const hbl_path = (sd_dir / "atmosphere" / "hbl.nsp").string();
|
auto const hbl_path = (sd_dir / "atmosphere" / "hbl.nsp").string();
|
||||||
|
|||||||
@@ -42,9 +42,6 @@ target_link_libraries(yuzu-cmd PRIVATE common core input_common frontend_common
|
|||||||
if (ENABLE_OPENGL)
|
if (ENABLE_OPENGL)
|
||||||
target_link_libraries(yuzu-cmd PRIVATE glad)
|
target_link_libraries(yuzu-cmd PRIVATE glad)
|
||||||
endif()
|
endif()
|
||||||
if (MSVC)
|
|
||||||
target_link_libraries(yuzu-cmd PRIVATE getopt)
|
|
||||||
endif()
|
|
||||||
target_link_libraries(yuzu-cmd PRIVATE ${PLATFORM_LIBRARIES} Threads::Threads)
|
target_link_libraries(yuzu-cmd PRIVATE ${PLATFORM_LIBRARIES} Threads::Threads)
|
||||||
|
|
||||||
create_resource("../../dist/eden.bmp" "yuzu_cmd/yuzu_icon.h" "yuzu_icon")
|
create_resource("../../dist/eden.bmp" "yuzu_cmd/yuzu_icon.h" "yuzu_icon")
|
||||||
|
|||||||
+15
-189
@@ -10,6 +10,7 @@
|
|||||||
#include <regex>
|
#include <regex>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include "common/settings_enums.h"
|
#include "common/settings_enums.h"
|
||||||
|
#include "core/launch_params.h"
|
||||||
#ifdef __EMSCRIPTEN__
|
#ifdef __EMSCRIPTEN__
|
||||||
#include <emscripten.h>
|
#include <emscripten.h>
|
||||||
#endif
|
#endif
|
||||||
@@ -52,12 +53,6 @@
|
|||||||
#include "common/windows/timer_resolution.h"
|
#include "common/windows/timer_resolution.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#undef _UNICODE
|
|
||||||
#include <getopt.h>
|
|
||||||
#ifndef _MSC_VER
|
|
||||||
#include <unistd.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
extern "C" {
|
extern "C" {
|
||||||
// tells Nvidia and AMD drivers to use the dedicated GPU by default on laptops with switchable
|
// tells Nvidia and AMD drivers to use the dedicated GPU by default on laptops with switchable
|
||||||
@@ -67,25 +62,6 @@ __declspec(dllexport) int AmdPowerXpressRequestHighPerformance = 1;
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static void PrintHelp(const char* argv0) {
|
|
||||||
std::cout << "Usage: " << argv0
|
|
||||||
<< " [options] <filename>\n"
|
|
||||||
"-c, --config Load the specified configuration file\n"
|
|
||||||
"-f, --fullscreen Start in fullscreen mode\n"
|
|
||||||
"-g, --game File path of the game to load\n"
|
|
||||||
"-h, --help Display this help and exit\n"
|
|
||||||
"-m, --multiplayer=nick:password@address:port"
|
|
||||||
" Nickname, password, address and port for multiplayer\n"
|
|
||||||
"-p, --program Pass following string as arguments to executable\n"
|
|
||||||
"-u, --user Select a specific user profile from 0 to 7\n"
|
|
||||||
"-d, --debug Run the GDB stub on a port from 1 to 65535\n"
|
|
||||||
"-v, --version Output version information and exit\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
static void PrintVersion() {
|
|
||||||
std::cout << "Eden " << Common::g_scm_branch << " " << Common::g_scm_desc << std::endl;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void OnStateChanged(const Network::RoomMember::State& state) {
|
static void OnStateChanged(const Network::RoomMember::State& state) {
|
||||||
switch (state) {
|
switch (state) {
|
||||||
case Network::RoomMember::State::Idle:
|
case Network::RoomMember::State::Idle:
|
||||||
@@ -208,168 +184,18 @@ extern "C" SDL_AppResult SDL_AppInit(void **appstate, int argc, char **argv) {
|
|||||||
LOG_CRITICAL(Frontend, "Failed to get command line arguments");
|
LOG_CRITICAL(Frontend, "Failed to get command line arguments");
|
||||||
return SDL_APP_FAILURE;
|
return SDL_APP_FAILURE;
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
std::string filepath;
|
|
||||||
std::optional<std::string> config_path{};
|
|
||||||
std::string program_args;
|
|
||||||
std::optional<int> selected_user{};
|
|
||||||
std::optional<u16> override_gdb_port{};
|
|
||||||
bool use_multiplayer = false;
|
|
||||||
bool fullscreen = false;
|
|
||||||
bool force_null_render = false;
|
|
||||||
bool force_single_core = false;
|
|
||||||
std::string nickname{};
|
|
||||||
std::string password{};
|
|
||||||
std::string address{};
|
|
||||||
std::string input_profile{};
|
|
||||||
std::optional<std::string> log_filter{};
|
|
||||||
u16 port = Network::DefaultRoomPort;
|
|
||||||
|
|
||||||
static struct option long_options[] = {
|
|
||||||
// clang-format off
|
|
||||||
{"debug", no_argument, 0, 'd'},
|
|
||||||
{"config", required_argument, 0, 'c'},
|
|
||||||
{"fullscreen", no_argument, 0, 'f'},
|
|
||||||
{"help", no_argument, 0, 'h'},
|
|
||||||
{"game", required_argument, 0, 'g'},
|
|
||||||
{"multiplayer", required_argument, 0, 'm'},
|
|
||||||
{"program", optional_argument, 0, 'p'},
|
|
||||||
{"user", required_argument, 0, 'u'},
|
|
||||||
{"version", no_argument, 0, 'v'},
|
|
||||||
{"input-profile", no_argument, 0, 'i'},
|
|
||||||
{"null-render", no_argument, 0, 'n'},
|
|
||||||
{"singlecore", no_argument, 0, 's'},
|
|
||||||
{"filter", no_argument, 0, 'x'},
|
|
||||||
{0, 0, 0, 0},
|
|
||||||
// clang-format on
|
|
||||||
};
|
|
||||||
|
|
||||||
while (optind < argc) {
|
|
||||||
int arg = getopt_long(argc, argv, "g:fhvcip::c:u:d:", long_options, &option_index);
|
|
||||||
if (arg != -1) {
|
|
||||||
switch (char(arg)) {
|
|
||||||
case 'd':
|
|
||||||
override_gdb_port = uint16_t(atoi(optarg));
|
|
||||||
break;
|
|
||||||
case 'c':
|
|
||||||
config_path = optarg;
|
|
||||||
break;
|
|
||||||
case 'f':
|
|
||||||
fullscreen = true;
|
|
||||||
LOG_INFO(Frontend, "Starting in fullscreen mode...");
|
|
||||||
break;
|
|
||||||
case 'h':
|
|
||||||
PrintHelp(argv[0]);
|
|
||||||
return SDL_APP_FAILURE;
|
|
||||||
case 'g':
|
|
||||||
filepath = std::string(optarg);
|
|
||||||
break;
|
|
||||||
case 'i': {
|
|
||||||
input_profile = std::string(optarg);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 'm': {
|
|
||||||
use_multiplayer = true;
|
|
||||||
const std::string str_arg(optarg);
|
|
||||||
// regex to check if the format is nickname:password@ip:port
|
|
||||||
// with optional :password
|
|
||||||
const std::regex re("^([^:]+)(?::(.+))?@([^:]+)(?::([0-9]+))?$");
|
|
||||||
if (!std::regex_match(str_arg, re)) {
|
|
||||||
std::cout << "Wrong format for option --multiplayer\n";
|
|
||||||
PrintHelp(argv[0]);
|
|
||||||
return SDL_APP_FAILURE;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::smatch match;
|
|
||||||
std::regex_search(str_arg, match, re);
|
|
||||||
ASSERT(match.size() == 5);
|
|
||||||
nickname = match[1];
|
|
||||||
password = match[2];
|
|
||||||
address = match[3];
|
|
||||||
if (!match[4].str().empty()) {
|
|
||||||
port = u16(std::strtoul(match[4].str().c_str(), nullptr, 0));
|
|
||||||
}
|
|
||||||
std::regex nickname_re("^[a-zA-Z0-9._\\- ]+$");
|
|
||||||
if (!std::regex_match(nickname, nickname_re)) {
|
|
||||||
LOG_ERROR(Frontend, "Nickname is not valid. Must be 4 to 20 alphanumeric characters");
|
|
||||||
return SDL_APP_FAILURE;
|
|
||||||
}
|
|
||||||
if (address.empty()) {
|
|
||||||
LOG_ERROR(Frontend, "Address to room must not be empty");
|
|
||||||
return SDL_APP_FAILURE;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 'p':
|
|
||||||
program_args = argv[optind];
|
|
||||||
++optind;
|
|
||||||
break;
|
|
||||||
case 'u':
|
|
||||||
selected_user = atoi(optarg);
|
|
||||||
break;
|
|
||||||
case 'v':
|
|
||||||
PrintVersion();
|
|
||||||
return SDL_APP_FAILURE;
|
|
||||||
case 'n':
|
|
||||||
force_null_render = true;
|
|
||||||
break;
|
|
||||||
case 's':
|
|
||||||
force_single_core = true;
|
|
||||||
break;
|
|
||||||
case 'x':
|
|
||||||
log_filter = argv[optind];
|
|
||||||
++optind;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
#ifdef _WIN32
|
|
||||||
filepath = Common::UTF16ToUTF8(argv_w[optind]);
|
|
||||||
#else
|
#else
|
||||||
filepath = argv[optind];
|
wchar_t **argv_w = nullptr;
|
||||||
#endif
|
#endif
|
||||||
optind++;
|
Core::LaunchParams lp = Core::ParseLaunchParams(state->system, argc, argv, argv_w);
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
SdlConfig config{config_path};
|
|
||||||
|
|
||||||
// apply the log_filter setting
|
|
||||||
// the logger was initialized before and doesn't pick up the filter on its own
|
|
||||||
Common::Log::Filter filter;
|
|
||||||
filter.ParseFilterString(log_filter.value_or(Settings::values.log_filter.GetValue()));
|
|
||||||
Common::Log::SetGlobalFilter(filter);
|
|
||||||
|
|
||||||
if (!program_args.empty()) {
|
|
||||||
Settings::values.program_args = program_args;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!input_profile.empty()) {
|
|
||||||
auto& players = Settings::values.players.GetValue();
|
|
||||||
players[0].profile_name = input_profile;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (selected_user.has_value()) {
|
|
||||||
Settings::values.current_user = std::clamp(*selected_user, 0, 7);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (override_gdb_port.has_value()) {
|
|
||||||
Settings::values.use_gdbstub = true;
|
|
||||||
Settings::values.gdbstub_port = *override_gdb_port;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (force_single_core) {
|
|
||||||
Settings::values.use_multi_core = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (force_null_render) {
|
|
||||||
Settings::values.renderer_backend = Settings::RendererBackend::Null;
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
LocalFree(argv_w);
|
LocalFree(argv_w);
|
||||||
#endif
|
#endif
|
||||||
|
SdlConfig config{lp.config_path};
|
||||||
|
|
||||||
if (filepath.empty()) {
|
Core::ApplyLaunchParams(lp);
|
||||||
|
|
||||||
|
if (lp.filepath.empty()) {
|
||||||
LOG_CRITICAL(Frontend, "Failed to load ROM: No ROM specified");
|
LOG_CRITICAL(Frontend, "Failed to load ROM: No ROM specified");
|
||||||
return SDL_APP_FAILURE;
|
return SDL_APP_FAILURE;
|
||||||
}
|
}
|
||||||
@@ -386,14 +212,14 @@ extern "C" SDL_AppResult SDL_AppInit(void **appstate, int argc, char **argv) {
|
|||||||
case Settings::RendererBackend::OpenGL_GLSL:
|
case Settings::RendererBackend::OpenGL_GLSL:
|
||||||
case Settings::RendererBackend::OpenGL_GLASM:
|
case Settings::RendererBackend::OpenGL_GLASM:
|
||||||
case Settings::RendererBackend::OpenGL_SPIRV:
|
case Settings::RendererBackend::OpenGL_SPIRV:
|
||||||
state->emu_window = std::make_unique<EmuWindow_SDL3_GL>(&input_subsystem, state->system, fullscreen);
|
state->emu_window = std::make_unique<EmuWindow_SDL3_GL>(&input_subsystem, state->system, lp.fullscreen);
|
||||||
break;
|
break;
|
||||||
#endif
|
#endif
|
||||||
case Settings::RendererBackend::Vulkan:
|
case Settings::RendererBackend::Vulkan:
|
||||||
state->emu_window = std::make_unique<EmuWindow_SDL3_VK>(&input_subsystem, state->system, fullscreen);
|
state->emu_window = std::make_unique<EmuWindow_SDL3_VK>(&input_subsystem, state->system, lp.fullscreen);
|
||||||
break;
|
break;
|
||||||
case Settings::RendererBackend::Null:
|
case Settings::RendererBackend::Null:
|
||||||
state->emu_window = std::make_unique<EmuWindow_SDL3_Null>(&input_subsystem, state->system, fullscreen);
|
state->emu_window = std::make_unique<EmuWindow_SDL3_Null>(&input_subsystem, state->system, lp.fullscreen);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
LOG_CRITICAL(Frontend, "Invalid renderer backend");
|
LOG_CRITICAL(Frontend, "Invalid renderer backend");
|
||||||
@@ -413,12 +239,12 @@ extern "C" SDL_AppResult SDL_AppInit(void **appstate, int argc, char **argv) {
|
|||||||
Service::AM::FrontendAppletParameters load_parameters{
|
Service::AM::FrontendAppletParameters load_parameters{
|
||||||
.applet_id = Service::AM::AppletId::Application,
|
.applet_id = Service::AM::AppletId::Application,
|
||||||
};
|
};
|
||||||
const Core::SystemResultStatus load_result = state->system.Load(*state->emu_window, filepath, load_parameters);
|
const Core::SystemResultStatus load_result = state->system.Load(*state->emu_window, lp.filepath, load_parameters);
|
||||||
switch (load_result) {
|
switch (load_result) {
|
||||||
case Core::SystemResultStatus::Success:
|
case Core::SystemResultStatus::Success:
|
||||||
break; // Expected case
|
break; // Expected case
|
||||||
case Core::SystemResultStatus::ErrorGetLoader:
|
case Core::SystemResultStatus::ErrorGetLoader:
|
||||||
LOG_CRITICAL(Frontend, "Failed to obtain loader for {}!", filepath);
|
LOG_CRITICAL(Frontend, "Failed to obtain loader for {}!", lp.filepath);
|
||||||
return SDL_APP_FAILURE;
|
return SDL_APP_FAILURE;
|
||||||
case Core::SystemResultStatus::ErrorLoader:
|
case Core::SystemResultStatus::ErrorLoader:
|
||||||
LOG_CRITICAL(Frontend, "Failed to load ROM!");
|
LOG_CRITICAL(Frontend, "Failed to load ROM!");
|
||||||
@@ -440,14 +266,14 @@ extern "C" SDL_AppResult SDL_AppInit(void **appstate, int argc, char **argv) {
|
|||||||
return SDL_APP_FAILURE;
|
return SDL_APP_FAILURE;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (use_multiplayer) {
|
if (lp.use_multiplayer) {
|
||||||
if (auto member = Network::GetRoomMember().lock()) {
|
if (auto member = Network::GetRoomMember().lock()) {
|
||||||
member->BindOnChatMessageReceived(OnMessageReceived);
|
member->BindOnChatMessageReceived(OnMessageReceived);
|
||||||
member->BindOnStatusMessageReceived(OnStatusMessageReceived);
|
member->BindOnStatusMessageReceived(OnStatusMessageReceived);
|
||||||
member->BindOnStateChanged(OnStateChanged);
|
member->BindOnStateChanged(OnStateChanged);
|
||||||
member->BindOnError(OnNetworkError);
|
member->BindOnError(OnNetworkError);
|
||||||
LOG_DEBUG(Network, "Start connection to {}:{} with nickname {}", address, port, nickname);
|
LOG_DEBUG(Network, "Start connection to {}:{} with nickname {}", lp.address, lp.port, lp.nickname);
|
||||||
member->Join(nickname, address.c_str(), port, 0, Network::NoPreferredIP, password);
|
member->Join(lp.nickname, lp.address.c_str(), lp.port, 0, Network::NoPreferredIP, lp.password);
|
||||||
} else {
|
} else {
|
||||||
LOG_ERROR(Network, "Could not access RoomMember");
|
LOG_ERROR(Network, "Could not access RoomMember");
|
||||||
return SDL_APP_FAILURE;
|
return SDL_APP_FAILURE;
|
||||||
|
|||||||
Reference in New Issue
Block a user