mirror of
https://git.eden-emu.dev/eden-emu/eden.git
synced 2026-09-24 18:42:07 +00:00
Compare commits
36 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 3ef1736ac0 | |||
| 0404aea0db | |||
| dbeb73ee01 | |||
| cb73a4dcc7 | |||
| 278f411dad | |||
| 3d37a816c6 | |||
| a25e32676f | |||
| 38df54edfe | |||
| 3266c20e3f | |||
| 9cd24e85c2 | |||
| 74b5e10dc5 | |||
| a277b62fe4 | |||
| 908b1e9a37 | |||
| 847e91c3a8 | |||
| 2d1eb0dab9 | |||
| 9a30172e45 | |||
| 07f40d5cac | |||
| 3c38afad74 | |||
| 0ce29be608 | |||
| 1203082a8f | |||
| 14235dc0d0 | |||
| 6374f7f51f | |||
| 73e004de6e | |||
| f7b8ade40e | |||
| 8c1194474e | |||
| 90aafeedc1 | |||
| 4fe5f62c38 | |||
| c3f1e6562b | |||
| ac35358b3f | |||
| 7bf95be2c2 | |||
| 4ce45b3b37 | |||
| fd34024f0e | |||
| d76f8f91c4 | |||
| a7061eb4c8 | |||
| fa4e7c6992 | |||
| b77308ced6 |
Vendored
+628
-583
File diff suppressed because it is too large
Load Diff
Vendored
+547
-502
File diff suppressed because it is too large
Load Diff
Vendored
+547
-502
File diff suppressed because it is too large
Load Diff
Vendored
+547
-502
File diff suppressed because it is too large
Load Diff
Vendored
+547
-502
File diff suppressed because it is too large
Load Diff
Vendored
+547
-502
File diff suppressed because it is too large
Load Diff
Vendored
+548
-503
File diff suppressed because it is too large
Load Diff
Vendored
+547
-502
File diff suppressed because it is too large
Load Diff
Vendored
+547
-502
File diff suppressed because it is too large
Load Diff
Vendored
+547
-502
File diff suppressed because it is too large
Load Diff
Vendored
+547
-502
File diff suppressed because it is too large
Load Diff
Vendored
+547
-502
File diff suppressed because it is too large
Load Diff
Vendored
+547
-502
File diff suppressed because it is too large
Load Diff
Vendored
+547
-502
File diff suppressed because it is too large
Load Diff
Vendored
+547
-502
File diff suppressed because it is too large
Load Diff
Vendored
+547
-502
File diff suppressed because it is too large
Load Diff
Vendored
+547
-502
File diff suppressed because it is too large
Load Diff
Vendored
+547
-502
File diff suppressed because it is too large
Load Diff
Vendored
+547
-502
File diff suppressed because it is too large
Load Diff
Vendored
+547
-502
File diff suppressed because it is too large
Load Diff
Vendored
+550
-508
File diff suppressed because it is too large
Load Diff
Vendored
+547
-502
File diff suppressed because it is too large
Load Diff
Vendored
+547
-502
File diff suppressed because it is too large
Load Diff
Vendored
+547
-502
File diff suppressed because it is too large
Load Diff
Vendored
+547
-502
File diff suppressed because it is too large
Load Diff
Vendored
+573
-527
File diff suppressed because it is too large
Load Diff
Vendored
+547
-502
File diff suppressed because it is too large
Load Diff
+33
-21
@@ -1,32 +1,44 @@
|
||||
# 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.
|
||||
|
||||
## eden
|
||||
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 <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.
|
||||
- `-f`: Use fullscreen.
|
||||
- `-u <number>`: Select the index of the user to load as.
|
||||
- `-input-profile <name>`: Specifies input profile name to use (for player #0 only).
|
||||
- `--debug/-d`: Enter debug mode, allow gdb stub at port `1234`
|
||||
- `--config/-c`: Specify alternate configuration file.
|
||||
- `--fullscreen/-f`: Use fullscreen.
|
||||
- `--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.
|
||||
- `-hlaunch`: Launch homebrew launcher `nx-hbloader`.
|
||||
- Requires a copy of Atmosphere to be extracted onto `sdmc`.
|
||||
- This is a shorthand for `<eden folder>/sdmc/atmosphere/hbl.nsp`.
|
||||
- `-setup`: Launch setup applet.
|
||||
|
||||
## 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`
|
||||
- `--config/-c`: Specify alternate configuration file.
|
||||
- `--fullscreen/-f`: Set fullscreen.
|
||||
- `--help/-h`: Display help.
|
||||
- `--game/-g`: Specify the game to run.
|
||||
- `--multiplayer/-m`: Specify multiplayer options.
|
||||
- `--program/-p`: Specify the program arguments to pass (optional).
|
||||
- `--user/-u`: Specify the user index.
|
||||
- `--version/-v`: Display version and quit.
|
||||
- `--input-profile/-i`: Specifies input profile name to use (for player #0 only).
|
||||
- `--null-render/-n`: Forces the usage of the "Null" render backend irrespective of settings.
|
||||
- `--filter/-x`: Sets the debug log filter irrespective of settings.
|
||||
- `--singlecore/-s`: Forces single-core regardless of settings.
|
||||
- `-n/--room-name`: The name of the room.
|
||||
- `-d/--room-description`: The room description.
|
||||
- `-s/--bind-address`: The bind address for the room.
|
||||
- `-p/--port`: The port used for the room.
|
||||
- `-m/--max-members`: The maximum number of players for this room.
|
||||
- `-w/--password`: The password for the room.
|
||||
- `-g/--preferred-game`: The preferred game for this room.
|
||||
- `-i/--preferred-game-id`: The preferred game-id for this room.
|
||||
- `-u/--username`: The username used for announce.
|
||||
- `-t/--token`: The token used for announce.
|
||||
- `-a/--web-api-url`: yuzu Web API url.
|
||||
- `-b/--ban-list-file`: The file for storing the room ban list.
|
||||
- `-l/--log-file`: The file for storing the room log.
|
||||
- `-h/--help`: Display this help and exit.
|
||||
- `-v/--version`: Output version information and exit.
|
||||
|
||||
-1
@@ -549,7 +549,6 @@ class SettingsFragmentPresenter(
|
||||
add(BooleanSetting.RENDERER_FORCE_MAX_CLOCK.key)
|
||||
add(BooleanSetting.RENDERER_REACTIVE_FLUSHING.key)
|
||||
add(BooleanSetting.ENABLE_BUFFER_HISTORY.key)
|
||||
add(BooleanSetting.ENABLE_GPU_BUFFER_READBACK.key)
|
||||
add(BooleanSetting.USE_OPTIMIZED_VERTEX_BUFFERS.key)
|
||||
|
||||
add(HeaderSetting(R.string.hacks))
|
||||
|
||||
@@ -788,7 +788,7 @@ int Java_org_yuzu_yuzu_1emu_NativeLibrary_installFileToNand(JNIEnv* env, jobject
|
||||
jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_doesUpdateMatchProgram(JNIEnv* env, jobject jobj,
|
||||
jstring jprogramId,
|
||||
jstring jupdatePath) {
|
||||
u64 program_id = EmulationSession::GetProgramId(env, jprogramId);
|
||||
const u64 program_id = FileSys::GetBaseTitleID(EmulationSession::GetProgramId(env, jprogramId));
|
||||
std::string updatePath = Common::Android::GetJString(env, jupdatePath);
|
||||
std::shared_ptr<FileSys::NSP> nsp = std::make_shared<FileSys::NSP>(
|
||||
EmulationSession::GetInstance().System().GetFilesystem()->OpenFile(
|
||||
@@ -796,7 +796,7 @@ jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_doesUpdateMatchProgram(JNIEnv* en
|
||||
for (const auto& item : nsp->GetNCAs()) {
|
||||
for (const auto& nca_details : item.second) {
|
||||
if (nca_details.second->GetName().ends_with(".cnmt.nca")) {
|
||||
auto update_id = nca_details.second->GetTitleId() & ~0xFFFULL;
|
||||
const auto update_id = FileSys::GetBaseTitleID(nca_details.second->GetTitleId());
|
||||
if (update_id == program_id) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -580,8 +580,6 @@
|
||||
<string name="renderer_reactive_flushing_description">يحسن دقة العرض في بعض الألعاب على حساب الأداء.</string>
|
||||
<string name="enable_buffer_history">تمكين سجل التخزين المؤقت</string>
|
||||
<string name="enable_buffer_history_description">يُتيح هذا الخيار الوصول إلى حالات التخزين المؤقت السابقة. وقد يُحسّن جودة العرض وثبات الأداء في بعض الألعاب.</string>
|
||||
<string name="enable_gpu_buffer_readback">تفعيل قراءة مخزن وحدة معالجة الرسومات</string>
|
||||
<string name="enable_gpu_buffer_readback_description">يحافظ هذا النظام على بيانات المخزن المؤقت المُعدّلة بواسطة وحدة معالجة الرسومات عن طريق قراءتها مرة أخرى قبل التحميل. تتطلب بعض الألعاب ذلك لعرض بعض التأثيرات بشكل صحيح. قد يُسبب ذلك مشاكل إذا لم يتمكن الجهاز من التعامل مع عبء العمل الإضافي.</string>
|
||||
<string name="use_optimized_vertex_buffers">مخازن الرؤوس المُحسّنة</string>
|
||||
<string name="use_optimized_vertex_buffers_description">يُتيح ربطًا مُحسَّنًا لمخازن الرؤوس لتحسين الأداء. يتطلب برامج تشغيل Mesa 26.0+ Turnip/ برامج تشغيل QCOM. قد يتعطل على برامج تشغيل Turnip القديمة (25.3 وما دون).</string>
|
||||
|
||||
@@ -1099,7 +1097,6 @@
|
||||
<string name="gpu_fence_behavior_immediate">فوري</string>
|
||||
<string name="gpu_fence_behavior_balanced">متوازن</string>
|
||||
<string name="gpu_fence_behavior_accurate">دقيق</string>
|
||||
<string name="gpu_fence_behavior_strict">صارم</string>
|
||||
|
||||
<string name="vram_usage_conservative">محافظ</string>
|
||||
<string name="vram_usage_aggressive">عدواني</string>
|
||||
|
||||
@@ -967,7 +967,6 @@ Wirklich fortfahren?</string>
|
||||
<string name="gpu_fence_behavior_immediate">Direkt</string>
|
||||
<string name="gpu_fence_behavior_balanced">Ausgewogen</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_aggressive">Aggressiv</string>
|
||||
|
||||
@@ -289,6 +289,7 @@
|
||||
<string name="gpu_driver_fetcher">Obtenedor de controladores de la GPU</string>
|
||||
<string name="gpu_driver_manager">Gestor de controladores de la GPU</string>
|
||||
<string name="install_gpu_driver_description">Instale los controladores alternativos para obtener un posible mejor rendimiento o precisión</string>
|
||||
<string name="post_processing_add">Añadir efecto</string>
|
||||
<string name="post_processing_open_list">Abrir lista</string>
|
||||
<string name="post_processing_close_list">Cerrar lista</string>
|
||||
<string name="post_processing_reset">Restablecer a predeterminado</string>
|
||||
@@ -523,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="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_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_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>
|
||||
|
||||
@@ -1037,7 +1036,6 @@
|
||||
<string name="gpu_fence_behavior_immediate">Inmediato</string>
|
||||
<string name="gpu_fence_behavior_balanced">Equilibrado</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_aggressive">Agresivo</string>
|
||||
|
||||
@@ -318,6 +318,13 @@
|
||||
<string name="frame_gen_target_rate">Целевая частота кадров</string>
|
||||
<string name="frame_gen_target_rate_description">Выберите значение под ваш дисплей. Множитель подстроится сам, чтобы его держать, и откатит изменения, если игра начнёт тормозить.</string>
|
||||
<string name="frame_gen_target_rate_off">Использовать фиксированный множитель</string>
|
||||
<string name="frame_gen_on">Вкл.</string>
|
||||
<string name="frame_gen_off">Выкл.</string>
|
||||
<string name="frame_gen_fixed">Фикс.</string>
|
||||
<string name="frame_gen_flow_auto">Авто</string>
|
||||
<string name="frame_gen_quick_description">Включите или выключите генерацию кадров и выберите множитель кадров.</string>
|
||||
<string name="frame_gen_target_rate_quick_description">Генерация кадров с заданной частотой кадров. Множитель регулируется автоматически.</string>
|
||||
<string name="frame_gen_flow_scale_quick_description">Разрешение, используемое для оценки движения между кадрами. Меньшие значения экономят время ГПУ.</string>
|
||||
<string name="frame_gen_queue_target">Целевой размер очереди кадров</string>
|
||||
<string name="frame_gen_queue_target_description">Сколько готовых кадров может ожидать перед выводом на экран. Более длинные очереди сглаживают скачки ГПУ ценой задержки ввода.</string>
|
||||
<string name="frame_gen_queue_target_0">Минимальная задержка (Без буферизации)</string>
|
||||
@@ -328,12 +335,14 @@
|
||||
<string name="frame_gen_flow_scale">Разрешение оценки движения</string>
|
||||
<string name="frame_gen_flow_scale_description">Разрешение прохода оптического потока в долях от выходного разрешения. Его понижение — самый дешёвый способ вернуть производительность.</string>
|
||||
<string name="frame_gen_unsupported">Генерация кадров недоступна</string>
|
||||
<string name="frame_gen_unsupported_description">В этом ГПУ драйвере отсутствует поддержка модели памяти Vulkan или половинной точности (float16), которая требуется шейдерам Lossless Scaling.</string>
|
||||
<string name="lossless_scaling_setup_description">Опционально. Укажите свой Lossless.dll для включения генерации кадров позже.</string>
|
||||
<string name="lossless_scaling_install">Установить Lossless.dll</string>
|
||||
<string name="lossless_scaling_install_description">Для генерации кадров требуется ваша собственная легальная копия Lossless.dll из Lossless Scaling</string>
|
||||
<string name="lossless_scaling_replace_description">Выбрать другой файл Lossless.dll</string>
|
||||
<string name="frame_generation_support">Генерация кадров</string>
|
||||
<string name="frame_generation_supported">Поддерживается</string>
|
||||
<string name="frame_generation_unsupported">Не поддерживается (нет модели памяти Vulkan или float16)</string>
|
||||
<string name="lossless_scaling_description">Предоставьте свою копию Lossless.dll для включения генерации кадров</string>
|
||||
<string name="lossless_scaling_installed">Установлена</string>
|
||||
<string name="lossless_scaling_not_installed">Не установлена</string>
|
||||
@@ -560,8 +569,6 @@
|
||||
<string name="renderer_reactive_flushing_description">Повышение точности рендеринга в некоторых играх за счет снижения производительности.</string>
|
||||
<string name="enable_buffer_history">Включить историю буфера</string>
|
||||
<string name="enable_buffer_history_description">Позволяет обращаться к предыдущим состояниям буфера. Эта опция может повысить качество рендеринга и стабильность производительности в некоторых играх.</string>
|
||||
<string name="enable_gpu_buffer_readback">Включить обратное чтение буфера ГПУ</string>
|
||||
<string name="enable_gpu_buffer_readback_description">Сохраняет измененные ГПУ данные буфера путем чтения их обратно перед выгрузками. Некоторые игры требуют этого, чтобы рендерить определенные эффекты правильно. Может вызывать проблемы если оборудование не может обработать дополнительную рабочую нагрузку.</string>
|
||||
<string name="use_optimized_vertex_buffers">Оптимизированные вершинные буферы</string>
|
||||
<string name="use_optimized_vertex_buffers_description">Включает оптимизированную привязку вершинного буфера для повышения производительности. Требует Mesa Turnip 26.0+ / QCOM. Приводит к вылету на старых версиях драйверов Turnip (25.3 и ниже).</string>
|
||||
|
||||
@@ -1079,7 +1086,6 @@
|
||||
<string name="gpu_fence_behavior_immediate">Мгновенный</string>
|
||||
<string name="gpu_fence_behavior_balanced">Сбалансированный</string>
|
||||
<string name="gpu_fence_behavior_accurate">Точный</string>
|
||||
<string name="gpu_fence_behavior_strict">Строгий</string>
|
||||
|
||||
<string name="vram_usage_conservative">Консервативный</string>
|
||||
<string name="vram_usage_aggressive">Агрессивный</string>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation">
|
||||
|
||||
<string name="app_disclaimer">本软件可以运行用于 Nintendo Switch 游戏机的游戏。不包含任何游戏或密钥。<br /><br />在您开始之前, 请定位您设备存储上的 <![CDATA[<b> prod.keys </b>]]> 文件。<br /><br /><![CDATA[<a href=\"https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/user/QuickStart.md\">了解更多</a>]]></string>
|
||||
<string name="notice_notification_channel_name">错误与注意事项</string>
|
||||
<string name="notice_notification_channel_description">当发生错误时显示通知。</string>
|
||||
<string name="notification_permission_not_granted">未授予通知权限!</string>
|
||||
@@ -290,6 +291,25 @@
|
||||
<string name="gpu_driver_fetcher">GPU驱动获取器</string>
|
||||
<string name="gpu_driver_manager">GPU 驱动管理器</string>
|
||||
<string name="install_gpu_driver_description">安装替代的驱动程序以获得更好的性能和精度</string>
|
||||
<string name="post_processing">后处理效果</string>
|
||||
<string name="post_processing_description">在渲染完成后应用 ReShade FX 效果</string>
|
||||
<string name="post_processing_per_game_description">配置此游戏的效果链</string>
|
||||
<string name="post_processing_add">添加效果</string>
|
||||
<string name="post_processing_remove">移除</string>
|
||||
<string name="post_processing_open_list">打开列表</string>
|
||||
<string name="post_processing_close_list">关闭列表</string>
|
||||
<string name="post_processing_remove_all">移除效果</string>
|
||||
<string name="post_processing_preset_locked">预设已激活。在开始游戏前可以在后处理效果中编辑它。</string>
|
||||
<string name="post_processing_preset_modified">数值已从原始预设被更改。</string>
|
||||
<string name="post_processing_presets">预设</string>
|
||||
<string name="post_processing_preset_new">新建预设</string>
|
||||
<string name="post_processing_preset_new_description">把您加载的效果及当前数值保存为一个预设,以便今后选用。</string>
|
||||
<string name="post_processing_preset_name_invalid">请给预设起一个不含等号的名称。</string>
|
||||
<string name="post_processing_preset_none">无预设</string>
|
||||
<string name="post_processing_preset_delete">删除预设</string>
|
||||
<string name="post_processing_preset_reset">重置预设值</string>
|
||||
<string name="post_processing_reset">重置为默认值</string>
|
||||
<string name="post_processing_empty">找不到效果。请在此文件中放置 .fx 文件:</string>
|
||||
<string name="frame_gen">帧生成</string>
|
||||
<string name="frame_gen_per_game_description">配置针对此游戏的帧生成设定</string>
|
||||
<string name="frame_gen_description">设定要在使用无损缩放渲染的帧之间应用的插帧。启用后强制采用FIFO呈现模式 。</string>
|
||||
@@ -304,6 +324,13 @@
|
||||
<string name="frame_gen_target_rate_60">60 FPS</string>
|
||||
<string name="frame_gen_target_rate_90">90 FPS</string>
|
||||
<string name="frame_gen_target_rate_120">120 FPS</string>
|
||||
<string name="frame_gen_on">开</string>
|
||||
<string name="frame_gen_off">关</string>
|
||||
<string name="frame_gen_fixed">固定</string>
|
||||
<string name="frame_gen_flow_auto">自动</string>
|
||||
<string name="frame_gen_quick_description">打开或关闭帧生成并选择帧生成倍数。</string>
|
||||
<string name="frame_gen_target_rate_quick_description">生成帧到目标帧率。倍率会自动调整。</string>
|
||||
<string name="frame_gen_flow_scale_quick_description">用来估算帧间运动的分辨率。较低的值可以节省 GPU 时间。</string>
|
||||
<string name="frame_gen_queue_target">帧队列目标</string>
|
||||
<string name="frame_gen_queue_target_description">在显示之前有多少已完成渲染的帧正在等待。较大的队列可以缓解 GPU 突发的压力,但会增加输入延迟。</string>
|
||||
<string name="frame_gen_queue_target_0">最低延迟 (无缓冲)</string>
|
||||
@@ -314,12 +341,14 @@
|
||||
<string name="frame_gen_flow_scale">运动预估分辨率</string>
|
||||
<string name="frame_gen_flow_scale_description">光流通道的分辨率,以输出的比例表示。降低它是提升性能最经济的做法。</string>
|
||||
<string name="frame_gen_unsupported">帧生成不可用</string>
|
||||
<string name="frame_gen_unsupported_description">这个 GPU 驱动缺少 Lossless Scaling 着色器所需的 Vulkan 内存模型或半精度 (float16) 支持。</string>
|
||||
<string name="lossless_scaling_setup_description">作为可选项。请提供您自己的 Lossless.dll 以便在之后可以启用帧生成。</string>
|
||||
<string name="lossless_scaling_install">安装 Lossless.dll</string>
|
||||
<string name="lossless_scaling_install_description">帧生成需要您自己从 Lossless Scaling 获得合法的 Lossless.dll 副本</string>
|
||||
<string name="lossless_scaling_replace_description">选择其它 Lossless.dll 副本</string>
|
||||
<string name="frame_generation_support">帧生成</string>
|
||||
<string name="frame_generation_supported">支持</string>
|
||||
<string name="frame_generation_unsupported">不支持 (无 Vulkan 内存模型或 float16)</string>
|
||||
<string name="lossless_scaling">无损缩放</string>
|
||||
<string name="lossless_scaling_description">提供您自己的 Lossless.dll 文件以启用帧生成</string>
|
||||
<string name="lossless_scaling_installed">已安装</string>
|
||||
@@ -541,8 +570,6 @@
|
||||
<string name="renderer_reactive_flushing_description">通过牺牲性能来提升某些游戏的渲染精度。</string>
|
||||
<string name="enable_buffer_history">启用缓冲区历史</string>
|
||||
<string name="enable_buffer_history_description">启用对先前缓冲区状态的访问。此选项可在某些游戏中提升渲染质量并保持性能的一致性。</string>
|
||||
<string name="enable_gpu_buffer_readback">启用 GPU 缓冲区回读</string>
|
||||
<string name="enable_gpu_buffer_readback_description">在上传前回读经由 GPU 修改过的缓冲区数据,以将其保留。一些游戏会用到这项设定以正确渲染某些效果。如果硬件无法处理额外的工作负载,则可能会导致问题。</string>
|
||||
<string name="use_optimized_vertex_buffers">优化顶点缓冲区</string>
|
||||
<string name="use_optimized_vertex_buffers_description">启用经过优化的顶点缓冲区绑定以提升性能。需要 Mesa 26.0 及以上版本的 Turnip 或 QCOM 驱动程序。若使用较旧版本的 Turnip 驱动 (25.3 及以下版本) 则会导致崩溃。</string>
|
||||
|
||||
@@ -611,6 +638,11 @@
|
||||
<string name="log">日志记录</string>
|
||||
<string name="flush_by_line">按行刷新调试日志</string>
|
||||
<string name="flush_by_line_description">在每行写入时刷新调试日志,使在崩溃或冻结时调试更容易。</string>
|
||||
<string name="extended_logging">开启扩展日志</string>
|
||||
<string name="extended_logging_description">将最大日志文件大小从 100 MiB 提升至 1GiB。</string>
|
||||
<string name="log_filter">日志筛选器</string>
|
||||
<string name="log_filter_description">控制 Eden 的日志类别。例如: *:Info Service.LM:Debug</string>
|
||||
|
||||
<!-- GPU Logging strings -->
|
||||
<string name="gpu_logging_header">GPU 日志</string>
|
||||
<string name="gpu_log_level">日志等级</string>
|
||||
@@ -894,6 +926,8 @@
|
||||
<string name="loader_error_file_not_found">ROM 文件不存在</string>
|
||||
|
||||
<string name="loader_requires_firmware">游戏需要固件</string>
|
||||
<string name="loader_requires_firmware_description"><![CDATA[这个游戏可能需要固件才能正常运行,但您还没有安装任何固件。请在启动前安装固件,或者按 “确定” 继续启动。]]></string>
|
||||
|
||||
<!-- Intent Launch strings -->
|
||||
<string name="searching_for_game">正在搜索游戏...</string>
|
||||
<string name="game_not_found_for_title_id">未找到标题ID的游戏: %1$s</string>
|
||||
@@ -1053,7 +1087,6 @@
|
||||
<string name="gpu_fence_behavior_immediate">即时</string>
|
||||
<string name="gpu_fence_behavior_balanced">均衡</string>
|
||||
<string name="gpu_fence_behavior_accurate">精确</string>
|
||||
<string name="gpu_fence_behavior_strict">严格</string>
|
||||
|
||||
<string name="vram_usage_conservative">保守式</string>
|
||||
<string name="vram_usage_aggressive">主动式</string>
|
||||
|
||||
@@ -229,7 +229,7 @@
|
||||
<string name="add_directory_success">遊戲目錄新增成功</string>
|
||||
<string name="enable_update_checks">檢查更新</string>
|
||||
<string name="enable_update_checks_description">在Eden啟動時檢查有無新版本並選擇是否要下載與安裝更新</string>
|
||||
<string name="update_available">偵測到新版本</string>
|
||||
<string name="update_available">發現新版本</string>
|
||||
<string name="update_available_description">有更新可供安裝:%1$s\n\n您要進行更新嗎?</string>
|
||||
<string name="downloading_update">下載更新中</string>
|
||||
<string name="update_download_failed">更新下載失敗</string>
|
||||
@@ -561,8 +561,6 @@
|
||||
<string name="renderer_reactive_flushing_description">犧牲效能,以改善部分遊戲的轉譯準確度</string>
|
||||
<string name="enable_buffer_history">啟用緩衝區歷史</string>
|
||||
<string name="enable_buffer_history_description">允許存取先前的緩衝區狀態。此選項可能會改善部分遊戲的渲染品質與效能穩定性</string>
|
||||
<string name="enable_gpu_buffer_readback">啟用 GPU 緩衝區讀回</string>
|
||||
<string name="enable_gpu_buffer_readback_description">透過在上傳之前先將 GPU 修改過的緩衝區資料讀回來保存資料,部分遊戲需要啟用此功能才能正常渲染遊戲特效。如果硬體無法負荷可能會導致錯誤</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>
|
||||
|
||||
@@ -1020,9 +1018,40 @@
|
||||
<string name="memory_6gb">6GB (不穩定)</string>
|
||||
<string name="memory_8gb">8GB (不穩定)</string>
|
||||
|
||||
<!-- CPU clock levels -->
|
||||
<string name="clock_normal">正常</string>
|
||||
<string name="clock_boost">加速</string>
|
||||
<string name="clock_fast">超頻</string>
|
||||
|
||||
<!-- GPU clock levels -->
|
||||
<string name="fast_gpu_normal">正常</string>
|
||||
<string name="fast_gpu_medium">加速</string>
|
||||
<string name="fast_gpu_high">超頻</string>
|
||||
|
||||
<!-- GPU swizzle texture size -->
|
||||
<string name="gpu_texturesizeswizzle_verysmall">極小 (16MB)</string>
|
||||
<string name="gpu_texturesizeswizzle_small">小(32MB)</string>
|
||||
<string name="gpu_texturesizeswizzle_normal">正常(128MB)</string>
|
||||
<string name="gpu_texturesizeswizzle_large">大(256MB)</string>
|
||||
<string name="gpu_texturesizeswizzle_verylarge">極大(512MB)</string>
|
||||
|
||||
<!-- GPU swizzle streams -->
|
||||
<string name="gpu_swizzle_verylow">極小(4MB)</string>
|
||||
<string name="gpu_swizzle_low">小(8MB)</string>
|
||||
<string name="gpu_swizzle_normal">正常(16MB)</string>
|
||||
<string name="gpu_swizzle_medium">中(32MB)</string>
|
||||
<string name="gpu_swizzle_high">大(64MB)</string>
|
||||
|
||||
<!-- GPU swizzle chunks -->
|
||||
<string name="gpu_swizzlechunk_verylow">極小(32)</string>
|
||||
<string name="gpu_swizzlechunk_low">小(64)</string>
|
||||
<string name="gpu_swizzlechunk_normal">正常(128)</string>
|
||||
<string name="gpu_swizzlechunk_medium">中(256)</string>
|
||||
<string name="gpu_swizzlechunk_high">大(512)</string>
|
||||
|
||||
<!-- Temperature Units -->
|
||||
<string name="temperature_celsius">攝氏度</string>
|
||||
<string name="temperature_fahrenheit">華氏度</string>
|
||||
<string name="temperature_celsius">攝氏</string>
|
||||
<string name="temperature_fahrenheit">華氏</string>
|
||||
|
||||
<!-- Memory Sizes -->
|
||||
<string name="memory_byte_shorthand">B</string>
|
||||
@@ -1035,6 +1064,8 @@
|
||||
|
||||
<string name="renderer_none">無</string>
|
||||
|
||||
<!-- Renderer Accuracy -->
|
||||
<string name="renderer_accuracy_low">效能</string>
|
||||
<string name="renderer_accuracy_high">準確</string>
|
||||
|
||||
<!-- DMA Accuracy -->
|
||||
|
||||
@@ -558,7 +558,6 @@
|
||||
<item>@string/gpu_fence_behavior_immediate</item>
|
||||
<item>@string/gpu_fence_behavior_balanced</item>
|
||||
<item>@string/gpu_fence_behavior_accurate</item>
|
||||
<item>@string/gpu_fence_behavior_strict</item>
|
||||
</string-array>
|
||||
<integer-array name="gpuFenceBehaviorValues">
|
||||
<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="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_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_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_balanced">Balanced</string>
|
||||
<string name="gpu_fence_behavior_accurate">Accurate</string>
|
||||
<string name="gpu_fence_behavior_strict">Strict</string>
|
||||
|
||||
<!-- ASTC Decoding Method Choices -->
|
||||
<string name="accelerate_astc_cpu" translatable="false">CPU</string>
|
||||
|
||||
@@ -110,7 +110,6 @@ add_library(
|
||||
socket_types.h
|
||||
sparse_large_vector.cpp
|
||||
sparse_large_vector.h
|
||||
spin_lock.h
|
||||
stb.cpp
|
||||
stb.h
|
||||
steady_clock.cpp
|
||||
|
||||
@@ -244,7 +244,8 @@ WallClock::WallClock(bool invariant_, u64 rdtsc_frequency_) noexcept
|
||||
, ns_rdtsc_factor{invariant_ ? GetFixedPoint64Factor(NsRatio::den, rdtsc_frequency_) : 0}
|
||||
, us_rdtsc_factor{invariant_ ? GetFixedPoint64Factor(UsRatio::den, rdtsc_frequency_) : 0}
|
||||
, ms_rdtsc_factor{invariant_ ? GetFixedPoint64Factor(MsRatio::den, rdtsc_frequency_) : 0}
|
||||
, rdtsc_ns_factor{invariant_ ? GetFixedPoint64Factor(rdtsc_frequency_, NsRatio::den) : 1}
|
||||
, rdtsc_ns_integer{invariant_ ? rdtsc_frequency_ / NsRatio::den : 1}
|
||||
, rdtsc_ns_factor{invariant_ ? GetFixedPoint64Factor(rdtsc_frequency_ % NsRatio::den, NsRatio::den) : 0}
|
||||
, cntpct_rdtsc_factor{invariant_ ? GetFixedPoint64Factor(CNTFRQ, rdtsc_frequency_) : 0}
|
||||
, gputick_rdtsc_factor{invariant_ ? GetFixedPoint64Factor(GPUTickFreq, rdtsc_frequency_) : 0}
|
||||
, invariant{invariant_}
|
||||
@@ -291,7 +292,7 @@ bool WallClock::IsNative() const {
|
||||
}
|
||||
|
||||
u64 WallClock::NsToTicks(std::chrono::nanoseconds ns) const {
|
||||
return invariant ? MultiplyHigh(ns.count(), rdtsc_ns_factor) : ns.count();
|
||||
return ns.count() * rdtsc_ns_integer + MultiplyHigh(ns.count(), rdtsc_ns_factor);
|
||||
}
|
||||
#elif defined(HAS_NCE)
|
||||
namespace {
|
||||
@@ -416,7 +417,7 @@ u64 WallClock::NsToTicks(std::chrono::nanoseconds ns) const {
|
||||
const WallClock g_wall_clock = [] {
|
||||
#if defined(ARCHITECTURE_x86_64)
|
||||
auto const& caps = Common::g_cpu_caps;
|
||||
return WallClock(caps.invariant_tsc && caps.tsc_frequency >= std::nano::den, caps.tsc_frequency);
|
||||
return WallClock(caps.invariant_tsc && caps.tsc_frequency > std::nano::den, caps.tsc_frequency);
|
||||
#elif defined(HAS_NCE)
|
||||
return WallClock(false, 1);
|
||||
#else
|
||||
|
||||
@@ -96,6 +96,7 @@ public:
|
||||
u64 ns_rdtsc_factor;
|
||||
u64 us_rdtsc_factor;
|
||||
u64 ms_rdtsc_factor;
|
||||
u64 rdtsc_ns_integer;
|
||||
u64 rdtsc_ns_factor;
|
||||
u64 cntpct_rdtsc_factor;
|
||||
u64 gputick_rdtsc_factor;
|
||||
|
||||
+18
-20
@@ -17,35 +17,33 @@
|
||||
|
||||
namespace Common {
|
||||
|
||||
// glibc, mlibc, musl, and newlib all define their own variants of strerror_r
|
||||
// We don't need to use the preprocessor, we can just select depending on return type
|
||||
template<typename T> std::string HandleStrerrorR(T r, char *err_str);
|
||||
template<> std::string HandleStrerrorR(char* r, char *) { return std::string{r}; }
|
||||
template<> std::string HandleStrerrorR(const char* r, char *) { return std::string{r}; }
|
||||
template<> std::string HandleStrerrorR(int r, char *err_str) {
|
||||
return std::string{r != 0
|
||||
? "(strerror_r failed to format error)"
|
||||
: err_str};
|
||||
}
|
||||
|
||||
std::string NativeErrorToString(int e) {
|
||||
#ifdef _WIN32
|
||||
LPSTR err_str;
|
||||
|
||||
DWORD res = FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER |
|
||||
FORMAT_MESSAGE_IGNORE_INSERTS,
|
||||
nullptr, e, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
|
||||
reinterpret_cast<LPSTR>(&err_str), 1, nullptr);
|
||||
if (!res) {
|
||||
return "(FormatMessageA failed to format error)";
|
||||
LPSTR(&err_str), 1, nullptr);
|
||||
if (res) {
|
||||
std::string ret(err_str);
|
||||
LocalFree(err_str);
|
||||
return ret;
|
||||
}
|
||||
std::string ret(err_str);
|
||||
LocalFree(err_str);
|
||||
return ret;
|
||||
return "(FormatMessageA failed to format error)";
|
||||
#else
|
||||
char err_str[255];
|
||||
#if defined(__ANDROID__) || \
|
||||
(defined(__GLIBC__) && (_GNU_SOURCE || (_POSIX_C_SOURCE < 200112L && _XOPEN_SOURCE < 600)))
|
||||
// Thread safe (GNU-specific)
|
||||
const char* str = strerror_r(e, err_str, sizeof(err_str));
|
||||
return std::string(str);
|
||||
#else
|
||||
// Thread safe (XSI-compliant)
|
||||
int second_err = strerror_r(e, err_str, sizeof(err_str));
|
||||
if (second_err != 0) {
|
||||
return "(strerror_r failed to format error)";
|
||||
}
|
||||
return std::string(err_str);
|
||||
#endif // GLIBC etc.
|
||||
return HandleStrerrorR(strerror_r(e, err_str, sizeof(err_str)), err_str);
|
||||
#endif // _WIN32
|
||||
}
|
||||
|
||||
|
||||
+66
-13
@@ -5,6 +5,7 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <algorithm>
|
||||
#include <cctype>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include "common/container/unordered_map.h"
|
||||
@@ -480,28 +481,80 @@ std::string SanitizePath(std::string_view path_, DirectorySeparator directory_se
|
||||
[type2](char c1, char c2) { return c1 == type2 && c2 == type2; }),
|
||||
path.end());
|
||||
|
||||
const bool absolute = !path.empty() && path[0] == type2;
|
||||
std::vector<std::string_view> parts;
|
||||
std::string root;
|
||||
std::string_view components{path};
|
||||
bool drive_relative = false;
|
||||
|
||||
for (const auto part : SplitPathComponents(path))
|
||||
{
|
||||
if (part.empty() || part == ".")
|
||||
continue;
|
||||
if (part == ".." && !parts.empty() && parts.back() != "..")
|
||||
parts.pop_back();
|
||||
else if (part != "..") parts.push_back(part);
|
||||
#ifdef _WIN32
|
||||
const bool network = path.size() > 1 && path[0] == type2 && path[1] == type2;
|
||||
const bool drive =
|
||||
path.size() > 1 && std::isalpha(static_cast<unsigned char>(path[0])) && path[1] == ':';
|
||||
|
||||
if (network) {
|
||||
root.assign(2, type2);
|
||||
components.remove_prefix(2);
|
||||
} else if (drive) {
|
||||
root.assign(path.data(), 2);
|
||||
components.remove_prefix(2);
|
||||
if (!components.empty() && components.front() == type2) {
|
||||
root += type2;
|
||||
components.remove_prefix(1);
|
||||
} else {
|
||||
drive_relative = true;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if (root.empty() && !components.empty() && components.front() == type2) {
|
||||
root += type2;
|
||||
components.remove_prefix(1);
|
||||
}
|
||||
|
||||
std::string resolved = absolute ? std::string(1, type2) : std::string{};
|
||||
for (std::size_t i = 0; i < parts.size(); ++i)
|
||||
{
|
||||
if (i != 0)
|
||||
const auto path_parts = SplitPathComponents(components);
|
||||
std::size_t root_component_count = 0;
|
||||
#ifdef _WIN32
|
||||
if (network) {
|
||||
root_component_count = 2;
|
||||
|
||||
const auto is_unc = [](std::string_view part) {
|
||||
return part.size() == 3 && (part[0] == 'U' || part[0] == 'u') &&
|
||||
(part[1] == 'N' || part[1] == 'n') && (part[2] == 'C' || part[2] == 'c');
|
||||
};
|
||||
if (path_parts.size() >= 2 && path_parts[0] == "?" && is_unc(path_parts[1])) {
|
||||
root_component_count = 4;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
std::vector<std::string_view> parts;
|
||||
for (std::size_t i = 0; i < path_parts.size(); ++i) {
|
||||
const auto part = path_parts[i];
|
||||
if (i < root_component_count) {
|
||||
parts.push_back(part);
|
||||
} else if (part.empty() || part == ".") {
|
||||
continue;
|
||||
} else if (part == "..") {
|
||||
if (parts.size() > root_component_count) {
|
||||
parts.pop_back();
|
||||
}
|
||||
} else {
|
||||
parts.push_back(part);
|
||||
}
|
||||
}
|
||||
|
||||
const std::size_t root_length = root.size();
|
||||
std::string resolved = std::move(root);
|
||||
for (std::size_t i = 0; i < parts.size(); ++i) {
|
||||
if (i != 0 || (!resolved.empty() && resolved.back() != type2 && !drive_relative))
|
||||
resolved += type2;
|
||||
resolved.append(parts[i].data(), parts[i].size());
|
||||
}
|
||||
|
||||
path = std::move(resolved);
|
||||
|
||||
if (!path.empty() && path.size() == root_length) {
|
||||
return path;
|
||||
}
|
||||
return std::string(RemoveTrailingSlash(path));
|
||||
}
|
||||
|
||||
|
||||
@@ -347,8 +347,9 @@ enum class DirectorySeparator {
|
||||
// i.e. "C:\Users\Yuzu\Documents\save.bin" becomes {"C:", "Users", "Yuzu", "Documents", "save.bin" }
|
||||
[[nodiscard]] std::vector<std::string> SplitPathComponentsCopy(std::string_view filename);
|
||||
|
||||
// Removes trailing slash, makes all '\\' into '/', and removes duplicate '/'. Makes '/' into '\\'
|
||||
// depending if directory_separator is BackwardSlash or PlatformDefault and running on windows
|
||||
// Normalizes directory separators, removes duplicate and non-root trailing separators, and resolves
|
||||
// '.' and '..' components without traversing above the path root. Windows drive and UNC roots are
|
||||
// preserved.
|
||||
[[nodiscard]] std::string SanitizePath(
|
||||
std::string_view path,
|
||||
DirectorySeparator directory_separator = DirectorySeparator::ForwardSlash);
|
||||
|
||||
@@ -96,15 +96,15 @@ struct PageTable {
|
||||
}
|
||||
|
||||
/// Write page info atomically
|
||||
constexpr void Store(bool marked, PageType type, u16 block, uintptr_t pointer) noexcept {
|
||||
inline void Store(bool marked, PageType type, u16 block, uintptr_t pointer) noexcept {
|
||||
data_raw.store(std::bit_cast<u64>(Data{marked, type, block, pointer}));
|
||||
}
|
||||
|
||||
constexpr void MarkRasterizerCached() noexcept {
|
||||
inline void MarkRasterizerCached() noexcept {
|
||||
data_raw.fetch_or(0b111);
|
||||
}
|
||||
|
||||
constexpr void MarkDebug(u64 ptr, u16 block) noexcept {
|
||||
inline void MarkDebug(u64 ptr, u16 block) noexcept {
|
||||
Store(true, PageType::DebugMemory, block, ptr);
|
||||
}
|
||||
|
||||
|
||||
@@ -178,10 +178,6 @@ bool IsGPUFenceBehaviorAccurate() {
|
||||
return values.gpu_fence_behavior.GetValue() == GpuFenceBehavior::Accurate;
|
||||
}
|
||||
|
||||
bool IsGPUFenceBehaviorStrict() {
|
||||
return values.gpu_fence_behavior.GetValue() == GpuFenceBehavior::Strict;
|
||||
}
|
||||
|
||||
bool IsFastmemEnabled() {
|
||||
if (values.cpu_accuracy.GetValue() == Settings::CpuAccuracy::Debugging)
|
||||
return bool(values.cpuopt_fastmem);
|
||||
|
||||
@@ -525,7 +525,7 @@ struct Values {
|
||||
SwitchableSetting<GpuFenceBehavior, true> gpu_fence_behavior{linkage,
|
||||
GpuFenceBehavior::Default,
|
||||
GpuFenceBehavior::Default,
|
||||
GpuFenceBehavior::Strict,
|
||||
GpuFenceBehavior::Accurate,
|
||||
"gpu_fence_behavior",
|
||||
Category::RendererAdvanced,
|
||||
Specialization::Default,
|
||||
@@ -655,13 +655,6 @@ struct Values {
|
||||
|
||||
SwitchableSetting<bool> rescale_hack{linkage, false, "rescale_hack",
|
||||
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",
|
||||
Category::RendererHacks};
|
||||
@@ -979,7 +972,6 @@ bool IsDMALevelSafe();
|
||||
bool IsGPUFenceBehaviorDefault();
|
||||
bool IsGPUFenceBehaviorBalanced();
|
||||
bool IsGPUFenceBehaviorAccurate();
|
||||
bool IsGPUFenceBehaviorStrict();
|
||||
|
||||
bool IsFastmemEnabled();
|
||||
void SetNceEnabled(bool is_64bit);
|
||||
|
||||
@@ -137,7 +137,7 @@ ENUM(VramUsageMode, Conservative, Aggressive);
|
||||
ENUM(RendererBackend, OpenGL_GLSL, Vulkan, Null, OpenGL_GLASM, OpenGL_SPIRV);
|
||||
ENUM(GpuAccuracy, Low, High);
|
||||
ENUM(DmaAccuracy, Default, Unsafe, Safe);
|
||||
ENUM(GpuFenceBehavior, Default, Immediate, Balanced, Accurate, Strict);
|
||||
ENUM(GpuFenceBehavior, Default, Immediate, Balanced, Accurate);
|
||||
ENUM(CpuBackend, Dynarmic, Nce);
|
||||
ENUM(CpuAccuracy, Auto, Accurate, Unsafe, Paranoid, Debugging);
|
||||
ENUM(CpuClock, Normal, Boost, Overclock)
|
||||
|
||||
@@ -96,6 +96,21 @@ bool CommitVectorPage(uintptr_t addr, bool write) noexcept {
|
||||
#ifndef MAP_NOCORE
|
||||
#define MAP_NOCORE 0
|
||||
#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 {
|
||||
if (auto page = HostPageSize; size % page != 0) {
|
||||
|
||||
@@ -35,10 +35,12 @@ const u64 HostPageMask = ~(HostPageSize - 1);
|
||||
|
||||
void* AllocateMemoryPages(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.
|
||||
template <typename T>
|
||||
requires std::is_trivially_copyable_v<T>
|
||||
// MSVC doesn't regard structs with atomics as trivially copyable
|
||||
// requires std::is_trivially_copyable_v<T>
|
||||
class SparseLargeVector final {
|
||||
public:
|
||||
constexpr SparseLargeVector() = default;
|
||||
@@ -113,7 +115,7 @@ public:
|
||||
const u64 end_page = AlignUp(base, HostPageSize);
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -123,11 +125,16 @@ public:
|
||||
base = end_page;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -182,6 +189,14 @@ private:
|
||||
committed_pages[page_index >> 6].fetch_or(1ULL << (page_index & 63), std::memory_order_release);
|
||||
}
|
||||
|
||||
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{};
|
||||
T* base_ptr{};
|
||||
|
||||
|
||||
@@ -1,50 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#include <intrin.h>
|
||||
#elif defined(ARCHITECTURE_x86_64)
|
||||
#include <xmmintrin.h>
|
||||
#endif
|
||||
#include <atomic>
|
||||
|
||||
namespace Common {
|
||||
|
||||
/// @brief A lock similar to mutex that forces a thread to spin wait instead calling the
|
||||
/// supervisor. Should be used on short sequences of code.
|
||||
struct SpinLock {
|
||||
SpinLock() noexcept = default;
|
||||
SpinLock(const SpinLock&) noexcept = delete;
|
||||
SpinLock& operator=(const SpinLock&) noexcept = delete;
|
||||
SpinLock(SpinLock&&) noexcept = delete;
|
||||
SpinLock& operator=(SpinLock&&) noexcept = delete;
|
||||
|
||||
inline void lock() noexcept {
|
||||
while (lck.test_and_set(std::memory_order_acquire)) {
|
||||
#if defined(ARCHITECTURE_x86_64)
|
||||
_mm_pause();
|
||||
#elif defined(ARCHITECTURE_arm64) && defined(_MSC_VER)
|
||||
__yield();
|
||||
#elif defined(ARCHITECTURE_arm64)
|
||||
asm("yield");
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
inline void unlock() noexcept {
|
||||
lck.clear(std::memory_order_release);
|
||||
}
|
||||
|
||||
[[nodiscard]] inline bool try_lock() noexcept {
|
||||
return !lck.test_and_set(std::memory_order_acquire);
|
||||
}
|
||||
|
||||
std::atomic_flag lck = ATOMIC_FLAG_INIT;
|
||||
};
|
||||
|
||||
} // namespace Common
|
||||
+2
-2
@@ -208,9 +208,9 @@ UUID UUID::MakeRandomRFC4122V4() {
|
||||
return uuid;
|
||||
}
|
||||
|
||||
UUID UUID::MakeRFC4122V5(std::span<u8, 20> sha1) {
|
||||
UUID UUID::MakeRFC4122V5(std::span<u8, 16> sha1) {
|
||||
UUID uuid{};
|
||||
std::memcpy(&uuid.uuid, sha1.data(), sizeof(UUID));
|
||||
std::memcpy(&uuid.uuid, sha1.data(), sha1.size());
|
||||
uuid.uuid[8] = 0x80 | (uuid.uuid[8] & 0x3F);
|
||||
uuid.uuid[6] = 0x50 | (uuid.uuid[6] & 0xF);
|
||||
return uuid;
|
||||
|
||||
+1
-1
@@ -104,7 +104,7 @@ struct UUID {
|
||||
/// @returns A random UUID that is RFC 4122 Version 4 compliant.
|
||||
[[nodiscard]] static UUID MakeRandomRFC4122V4();
|
||||
|
||||
[[nodiscard]] static UUID MakeRFC4122V5(std::span<u8, 20> sha1);
|
||||
[[nodiscard]] static UUID MakeRFC4122V5(std::span<u8, 16> sha1);
|
||||
|
||||
friend constexpr bool operator==(const UUID& lhs, const UUID& rhs) = default;
|
||||
};
|
||||
|
||||
@@ -23,6 +23,8 @@ add_library(core STATIC
|
||||
core_timing.h
|
||||
cpu_manager.cpp
|
||||
cpu_manager.h
|
||||
launch_params.cpp
|
||||
launch_params.h
|
||||
crypto/aes_util.cpp
|
||||
crypto/aes_util.h
|
||||
crypto/ctr_encryption_layer.cpp
|
||||
@@ -796,6 +798,8 @@ add_library(core STATIC
|
||||
hle/service/ns/application_manager_interface.h
|
||||
hle/service/ns/application_version_interface.cpp
|
||||
hle/service/ns/application_version_interface.h
|
||||
hle/service/ns/async_result.cpp
|
||||
hle/service/ns/async_result.h
|
||||
hle/service/ns/content_management_interface.cpp
|
||||
hle/service/ns/content_management_interface.h
|
||||
hle/service/ns/develop_interface.cpp
|
||||
@@ -810,8 +814,6 @@ add_library(core STATIC
|
||||
hle/service/ns/ecommerce_interface.h
|
||||
hle/service/ns/factory_reset_interface.cpp
|
||||
hle/service/ns/factory_reset_interface.h
|
||||
hle/service/ns/i_async_result.cpp
|
||||
hle/service/ns/i_async_result.h
|
||||
hle/service/ns/language.cpp
|
||||
hle/service/ns/language.h
|
||||
hle/service/ns/ns.cpp
|
||||
@@ -1203,6 +1205,9 @@ endif()
|
||||
|
||||
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)
|
||||
if (MSVC)
|
||||
target_link_libraries(core PRIVATE getopt)
|
||||
endif()
|
||||
|
||||
if (BOOST_NO_HEADERS)
|
||||
target_link_libraries(core PUBLIC Boost::container Boost::heap Boost::asio Boost::process Boost::crc)
|
||||
|
||||
@@ -22,21 +22,15 @@ DynarmicCallbacks32::DynarmicCallbacks32(ArmDynarmic32& parent, Kernel::KProcess
|
||||
, m_check_memory_access{m_debugger_enabled || !Settings::values.cpuopt_ignore_memory_aborts.GetValue()}
|
||||
{}
|
||||
|
||||
u8 DynarmicCallbacks32::MemoryRead8(u32 vaddr) {
|
||||
CheckMemoryAccess(vaddr, 1, Kernel::DebugWatchpointType::Read);
|
||||
return m_memory.Read8(vaddr);
|
||||
}
|
||||
u16 DynarmicCallbacks32::MemoryRead16(u32 vaddr) {
|
||||
CheckMemoryAccess(vaddr, 2, Kernel::DebugWatchpointType::Read);
|
||||
return m_memory.Read16(vaddr);
|
||||
}
|
||||
u32 DynarmicCallbacks32::MemoryRead32(u32 vaddr) {
|
||||
CheckMemoryAccess(vaddr, 4, Kernel::DebugWatchpointType::Read);
|
||||
return m_memory.Read32(vaddr);
|
||||
}
|
||||
u64 DynarmicCallbacks32::MemoryRead64(u32 vaddr) {
|
||||
CheckMemoryAccess(vaddr, 8, Kernel::DebugWatchpointType::Read);
|
||||
return m_memory.Read64(vaddr);
|
||||
u64 DynarmicCallbacks32::MemoryRead(u32 vaddr, size_t size) {
|
||||
CheckMemoryAccess(vaddr, size, Kernel::DebugWatchpointType::Read);
|
||||
switch (size) {
|
||||
case sizeof(u64): return m_memory.Read64(vaddr);
|
||||
case sizeof(u32): return m_memory.Read32(vaddr);
|
||||
case sizeof(u16): return m_memory.Read16(vaddr);
|
||||
case sizeof(u8): return m_memory.Read8(vaddr);
|
||||
default: UNREACHABLE();
|
||||
}
|
||||
}
|
||||
|
||||
std::optional<u32> DynarmicCallbacks32::MemoryReadCode(u32 vaddr) {
|
||||
@@ -50,27 +44,17 @@ std::optional<u32> DynarmicCallbacks32::MemoryReadCode(u32 vaddr) {
|
||||
return cached_code_page.inst[(vaddr & Core::Memory::YUZU_PAGEMASK) / sizeof(u32)];
|
||||
}
|
||||
|
||||
void DynarmicCallbacks32::MemoryWrite8(u32 vaddr, u8 value) {
|
||||
if (CheckMemoryAccess(vaddr, 1, Kernel::DebugWatchpointType::Write)) {
|
||||
m_memory.Write8(vaddr, value);
|
||||
void DynarmicCallbacks32::MemoryWrite(Dynarmic::A32::VAddr vaddr, u64 value, size_t size) {
|
||||
if (CheckMemoryAccess(vaddr, size, Kernel::DebugWatchpointType::Write)) {
|
||||
switch (size) {
|
||||
case sizeof(u64): return m_memory.Write64(vaddr, value);
|
||||
case sizeof(u32): return m_memory.Write32(vaddr, u32(value));
|
||||
case sizeof(u16): return m_memory.Write16(vaddr, u16(value));
|
||||
case sizeof(u8): return m_memory.Write8(vaddr, u8(value));
|
||||
default: UNREACHABLE();
|
||||
}
|
||||
}
|
||||
}
|
||||
void DynarmicCallbacks32::MemoryWrite16(u32 vaddr, u16 value) {
|
||||
if (CheckMemoryAccess(vaddr, 2, Kernel::DebugWatchpointType::Write)) {
|
||||
m_memory.Write16(vaddr, value);
|
||||
}
|
||||
}
|
||||
void DynarmicCallbacks32::MemoryWrite32(u32 vaddr, u32 value) {
|
||||
if (CheckMemoryAccess(vaddr, 4, Kernel::DebugWatchpointType::Write)) {
|
||||
m_memory.Write32(vaddr, value);
|
||||
}
|
||||
}
|
||||
void DynarmicCallbacks32::MemoryWrite64(u32 vaddr, u64 value) {
|
||||
if (CheckMemoryAccess(vaddr, 8, Kernel::DebugWatchpointType::Write)) {
|
||||
m_memory.Write64(vaddr, value);
|
||||
}
|
||||
}
|
||||
|
||||
bool DynarmicCallbacks32::MemoryWriteExclusive8(u32 vaddr, u8 value, u8 expected) {
|
||||
return CheckMemoryAccess(vaddr, 1, Kernel::DebugWatchpointType::Write) &&
|
||||
m_memory.WriteExclusive8(vaddr, value, expected);
|
||||
@@ -177,7 +161,7 @@ void ArmDynarmic32::MakeJit(Common::PageTable* page_table) {
|
||||
config.page_table = reinterpret_cast<std::array<std::uint8_t*, NumPageTableEntries>*>(
|
||||
const_cast<Common::PageTable::PageEntryData*>(page_table->entries.data()));
|
||||
config.page_table_pointer_mask = Common::PageTable::ATTRIBUTE_MASK;
|
||||
config.page_table_marked_bit = 0;
|
||||
config.page_table_marked_bit = uint8_t(0);
|
||||
config.absolute_offset_page_table = true;
|
||||
config.detect_misaligned_access_via_page_table = 16 | 32 | 64 | 128;
|
||||
config.only_detect_misalignment_via_page_table_on_page_boundary = true;
|
||||
@@ -193,7 +177,7 @@ void ArmDynarmic32::MakeJit(Common::PageTable* page_table) {
|
||||
Kernel::Board::Nintendo::Nx::KSystemControl::Init::GetIntendedMemorySize()) < (1ULL << 39)) {
|
||||
// Systems like FreeBSD allocate memory really low by default, and since we pack our page table entries,
|
||||
// we have to manually sign extend when our actual pointer is negative.
|
||||
config.page_table_sign_extension = Common::PageTable::SIGN_BIT;
|
||||
config.page_table_sign_extension = std::uint8_t(Common::PageTable::SIGN_BIT);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -30,18 +30,12 @@ class System;
|
||||
class DynarmicCallbacks32 : public Dynarmic::A32::UserCallbacks {
|
||||
public:
|
||||
explicit DynarmicCallbacks32(ArmDynarmic32& parent, Kernel::KProcess* process);
|
||||
u8 MemoryRead8(u32 vaddr) override;
|
||||
u16 MemoryRead16(u32 vaddr) override;
|
||||
u32 MemoryRead32(u32 vaddr) override;
|
||||
u64 MemoryRead64(u32 vaddr) override;
|
||||
u64 MemoryRead(u32 vaddr, size_t size) override;
|
||||
std::optional<u32> MemoryReadCode(u32 vaddr) override;
|
||||
void InstructionSynchronizationBarrierRaised() override {
|
||||
last_code_addr = u64(-1); //reset back, force refetch
|
||||
}
|
||||
void MemoryWrite8(u32 vaddr, u8 value) override;
|
||||
void MemoryWrite16(u32 vaddr, u16 value) override;
|
||||
void MemoryWrite32(u32 vaddr, u32 value) override;
|
||||
void MemoryWrite64(u32 vaddr, u64 value) override;
|
||||
void MemoryWrite(Dynarmic::A32::VAddr vaddr, u64 value, size_t size) override;
|
||||
bool MemoryWriteExclusive8(u32 vaddr, u8 value, u8 expected) override;
|
||||
bool MemoryWriteExclusive16(u32 vaddr, u16 value, u16 expected) override;
|
||||
bool MemoryWriteExclusive32(u32 vaddr, u32 value, u32 expected) override;
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#include "core/arm/dynarmic/dynarmic_exclusive_monitor.h"
|
||||
#include "core/core_timing.h"
|
||||
#include "core/hle/kernel/k_process.h"
|
||||
#include "dynarmic/interface/A64/config.h"
|
||||
|
||||
namespace Core {
|
||||
|
||||
@@ -21,21 +22,15 @@ DynarmicCallbacks64::DynarmicCallbacks64(ArmDynarmic64& parent, Kernel::KProcess
|
||||
, m_check_memory_access{m_debugger_enabled || !Settings::values.cpuopt_ignore_memory_aborts.GetValue()}
|
||||
{}
|
||||
|
||||
u8 DynarmicCallbacks64::MemoryRead8(u64 vaddr) {
|
||||
CheckMemoryAccess(vaddr, 1, Kernel::DebugWatchpointType::Read);
|
||||
return m_memory.Read8(vaddr);
|
||||
}
|
||||
u16 DynarmicCallbacks64::MemoryRead16(u64 vaddr) {
|
||||
CheckMemoryAccess(vaddr, 2, Kernel::DebugWatchpointType::Read);
|
||||
return m_memory.Read16(vaddr);
|
||||
}
|
||||
u32 DynarmicCallbacks64::MemoryRead32(u64 vaddr) {
|
||||
CheckMemoryAccess(vaddr, 4, Kernel::DebugWatchpointType::Read);
|
||||
return m_memory.Read32(vaddr);
|
||||
}
|
||||
u64 DynarmicCallbacks64::MemoryRead64(u64 vaddr) {
|
||||
CheckMemoryAccess(vaddr, 8, Kernel::DebugWatchpointType::Read);
|
||||
return m_memory.Read64(vaddr);
|
||||
u64 DynarmicCallbacks64::MemoryRead(u64 vaddr, size_t size) {
|
||||
CheckMemoryAccess(vaddr, size, Kernel::DebugWatchpointType::Read);
|
||||
switch (size) {
|
||||
case sizeof(u64): return m_memory.Read64(vaddr);
|
||||
case sizeof(u32): return m_memory.Read32(vaddr);
|
||||
case sizeof(u16): return m_memory.Read16(vaddr);
|
||||
case sizeof(u8): return m_memory.Read8(vaddr);
|
||||
default: UNREACHABLE();
|
||||
}
|
||||
}
|
||||
Dynarmic::A64::Vector DynarmicCallbacks64::MemoryRead128(u64 vaddr) {
|
||||
CheckMemoryAccess(vaddr, 16, Kernel::DebugWatchpointType::Read);
|
||||
@@ -53,24 +48,15 @@ std::optional<u32> DynarmicCallbacks64::MemoryReadCode(u64 vaddr) {
|
||||
return cached_code_page.inst[(vaddr & Core::Memory::YUZU_PAGEMASK) / sizeof(u32)];
|
||||
}
|
||||
|
||||
void DynarmicCallbacks64::MemoryWrite8(u64 vaddr, u8 value) {
|
||||
if (CheckMemoryAccess(vaddr, 1, Kernel::DebugWatchpointType::Write)) {
|
||||
m_memory.Write8(vaddr, value);
|
||||
}
|
||||
}
|
||||
void DynarmicCallbacks64::MemoryWrite16(u64 vaddr, u16 value) {
|
||||
if (CheckMemoryAccess(vaddr, 2, Kernel::DebugWatchpointType::Write)) {
|
||||
m_memory.Write16(vaddr, value);
|
||||
}
|
||||
}
|
||||
void DynarmicCallbacks64::MemoryWrite32(u64 vaddr, u32 value) {
|
||||
if (CheckMemoryAccess(vaddr, 4, Kernel::DebugWatchpointType::Write)) {
|
||||
m_memory.Write32(vaddr, value);
|
||||
}
|
||||
}
|
||||
void DynarmicCallbacks64::MemoryWrite64(u64 vaddr, u64 value) {
|
||||
if (CheckMemoryAccess(vaddr, 8, Kernel::DebugWatchpointType::Write)) {
|
||||
m_memory.Write64(vaddr, value);
|
||||
void DynarmicCallbacks64::MemoryWrite(Dynarmic::A64::VAddr vaddr, u64 value, std::size_t size) {
|
||||
if (CheckMemoryAccess(vaddr, size, Kernel::DebugWatchpointType::Write)) {
|
||||
switch (size) {
|
||||
case sizeof(u64): return m_memory.Write64(vaddr, u64(value));
|
||||
case sizeof(u32): return m_memory.Write32(vaddr, u32(value));
|
||||
case sizeof(u16): return m_memory.Write16(vaddr, u16(value));
|
||||
case sizeof(u8): return m_memory.Write8(vaddr, u8(value));
|
||||
default: UNREACHABLE();
|
||||
}
|
||||
}
|
||||
}
|
||||
void DynarmicCallbacks64::MemoryWrite128(u64 vaddr, Dynarmic::A64::Vector value) {
|
||||
@@ -216,7 +202,7 @@ void ArmDynarmic64::MakeJit(Common::PageTable* page_table, std::size_t address_s
|
||||
const_cast<Common::PageTable::PageEntryData*>(page_table->entries.data()));
|
||||
config.page_table_address_space_bits = std::uint32_t(address_space_bits);
|
||||
config.page_table_pointer_mask = Common::PageTable::ATTRIBUTE_MASK;
|
||||
config.page_table_marked_bit = 0;
|
||||
config.page_table_marked_bit = uint8_t(0);
|
||||
config.silently_mirror_page_table = false;
|
||||
config.absolute_offset_page_table = true;
|
||||
config.detect_misaligned_access_via_page_table = 16 | 32 | 64 | 128;
|
||||
@@ -235,7 +221,7 @@ void ArmDynarmic64::MakeJit(Common::PageTable* page_table, std::size_t address_s
|
||||
Kernel::Board::Nintendo::Nx::KSystemControl::Init::GetIntendedMemorySize()) < (1ULL << 39)) {
|
||||
// Systems like FreeBSD allocate memory really low by default, and since we pack our page table entries,
|
||||
// we have to manually sign extend when our actual pointer is negative.
|
||||
config.page_table_sign_extension = Common::PageTable::SIGN_BIT;
|
||||
config.page_table_sign_extension = std::uint8_t(Common::PageTable::SIGN_BIT);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
#include "common/hash.h"
|
||||
#include "core/arm/arm_interface.h"
|
||||
#include "core/arm/dynarmic/dynarmic_exclusive_monitor.h"
|
||||
#include "dynarmic/interface/A64/config.h"
|
||||
|
||||
namespace Core::Memory {
|
||||
class Memory;
|
||||
@@ -36,19 +37,13 @@ class DynarmicCallbacks64 : public Dynarmic::A64::UserCallbacks {
|
||||
public:
|
||||
explicit DynarmicCallbacks64(ArmDynarmic64& parent, Kernel::KProcess* process);
|
||||
|
||||
u8 MemoryRead8(u64 vaddr) override;
|
||||
u16 MemoryRead16(u64 vaddr) override;
|
||||
u32 MemoryRead32(u64 vaddr) override;
|
||||
u64 MemoryRead64(u64 vaddr) override;
|
||||
u64 MemoryRead(u64 vaddr, size_t size) override;
|
||||
Dynarmic::A64::Vector MemoryRead128(u64 vaddr) override;
|
||||
std::optional<u32> MemoryReadCode(u64 vaddr) override;
|
||||
void InstructionSynchronizationBarrierRaised() override {
|
||||
last_code_addr = u64(-1); //reset back, force refetch
|
||||
}
|
||||
void MemoryWrite8(u64 vaddr, u8 value) override;
|
||||
void MemoryWrite16(u64 vaddr, u16 value) override;
|
||||
void MemoryWrite32(u64 vaddr, u32 value) override;
|
||||
void MemoryWrite64(u64 vaddr, u64 value) override;
|
||||
void MemoryWrite(Dynarmic::A64::VAddr vaddr, u64 value, std::size_t size) override;
|
||||
void MemoryWrite128(u64 vaddr, Dynarmic::A64::Vector value) override;
|
||||
bool MemoryWriteExclusive8(u64 vaddr, std::uint8_t value, std::uint8_t expected) override;
|
||||
bool MemoryWriteExclusive16(u64 vaddr, std::uint16_t value, std::uint16_t expected) override;
|
||||
|
||||
@@ -35,19 +35,22 @@ static IPSFileType IdentifyMagic(std::span<const u8> magic) {
|
||||
}
|
||||
|
||||
static bool IsEOF(IPSFileType type, std::span<const u8> magic) {
|
||||
return (type == IPSFileType::IPS && magic.size() > 3 && std::memcmp(magic.data(), "EOF", 3) == 0)
|
||||
|| (type == IPSFileType::IPS32 && magic.size() > 4 && std::memcmp(magic.data(), "EEOF", 4) == 0);
|
||||
return (type == IPSFileType::IPS && magic.size() >= 3 && std::memcmp(magic.data(), "EOF", 3) == 0)
|
||||
|| (type == IPSFileType::IPS32 && magic.size() >= 4 && std::memcmp(magic.data(), "EEOF", 4) == 0);
|
||||
}
|
||||
|
||||
VirtualFile PatchIPS(const VirtualFile& in, const VirtualFile& ips) {
|
||||
if (in == nullptr || ips == nullptr)
|
||||
return nullptr;
|
||||
|
||||
auto in_data = in->ReadAllBytes();
|
||||
auto const type = IdentifyMagic(in_data);
|
||||
const auto type = IdentifyMagic(ips->ReadBytes(0x5));
|
||||
if (type == IPSFileType::Error)
|
||||
return nullptr;
|
||||
|
||||
auto in_data = in->ReadAllBytes();
|
||||
if (in_data.size() == 0)
|
||||
return nullptr;
|
||||
|
||||
std::vector<u8> temp(type == IPSFileType::IPS ? 3 : 4);
|
||||
u64 offset = 5; // After header
|
||||
while (ips->Read(temp.data(), temp.size(), offset) == temp.size()) {
|
||||
@@ -100,8 +103,7 @@ VirtualFile PatchIPS(const VirtualFile& in, const VirtualFile& ips) {
|
||||
|
||||
|
||||
struct IPSwitchRecord {
|
||||
std::array<uint8_t, 256 - sizeof(size_t)> data;
|
||||
size_t count;
|
||||
std::vector<uint8_t> data;
|
||||
};
|
||||
struct IPSwitchCompiler::IPSwitchPatch {
|
||||
::Common::unordered_map<u32, IPSwitchRecord> records;
|
||||
@@ -122,22 +124,23 @@ static IPSwitchRecord EscapeStringSequences(std::string_view sv) {
|
||||
IPSwitchRecord r{};
|
||||
for (auto it = sv.cbegin(); it < sv.cend(); ) {
|
||||
if (*it == '\\' && it + 1 < sv.cend()) {
|
||||
switch (it[1]) {
|
||||
case 'a': r.data[r.count] = '\a'; break;
|
||||
case 'b': r.data[r.count] = '\b'; break;
|
||||
case 'e': r.data[r.count] = '\e'; break;
|
||||
case 'f': r.data[r.count] = '\f'; break;
|
||||
case 'n': r.data[r.count] = '\n'; break;
|
||||
case 'r': r.data[r.count] = '\r'; break;
|
||||
case 't': r.data[r.count] = '\t'; break;
|
||||
case 'v': r.data[r.count] = '\v'; break;
|
||||
case '?': r.data[r.count] = '\?'; break;
|
||||
default: r.data[r.count] = it[1]; break;
|
||||
}
|
||||
++r.count;
|
||||
r.data.push_back([it]() {
|
||||
switch (it[1]) {
|
||||
case 'a': return '\a';
|
||||
case 'b': return '\b';
|
||||
case 'e': return '\e';
|
||||
case 'f': return '\f';
|
||||
case 'n': return '\n';
|
||||
case 'r': return '\r';
|
||||
case 't': return '\t';
|
||||
case 'v': return '\v';
|
||||
case '?': return '\?';
|
||||
default: return it[1];
|
||||
}
|
||||
}());
|
||||
it += 2;
|
||||
} else {
|
||||
++r.count;
|
||||
r.data.push_back(*it);
|
||||
++it;
|
||||
}
|
||||
}
|
||||
@@ -223,8 +226,8 @@ void IPSwitchCompiler::Parse(std::span<u8 const> bytes) {
|
||||
if (start <= line.cend() && end <= line.cend()) {
|
||||
// Actually IPS wants ordering from {lsb, ..., msb} -- so LE and BE are inverted, fun!
|
||||
auto const hs = Common::HexStringToVector({start, end}, is_little_endian);
|
||||
r.data.resize(hs.size());
|
||||
std::memcpy(r.data.data(), hs.data(), hs.size());
|
||||
r.count = hs.size();
|
||||
LOG_INFO(Loader, "[H] value @ {:#08X}", offset);
|
||||
patches.back().records.insert_or_assign(u32(offset), std::move(r));
|
||||
} else {
|
||||
@@ -293,7 +296,7 @@ VirtualFile IPSwitchCompiler::Apply(const VirtualFile& in) const {
|
||||
if (patch.enabled) {
|
||||
for (const auto& record : patch.records) {
|
||||
if (record.first < in_data.size()) {
|
||||
auto replace_size = record.second.count;
|
||||
auto replace_size = record.second.data.size();
|
||||
if (record.first + replace_size > in_data.size())
|
||||
replace_size = in_data.size() - record.first;
|
||||
std::memcpy(in_data.data() + record.first, record.second.data.data(), replace_size);
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include <cstddef>
|
||||
#include <cstring>
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/hex_util.h"
|
||||
#include "common/logging.h"
|
||||
#include "common/settings.h"
|
||||
@@ -156,12 +157,15 @@ std::string GetUpdateVersionStringFromSlot(const ContentProvider* provider, u64
|
||||
NACP nacp{nacp_file};
|
||||
return nacp.GetVersionString();
|
||||
}
|
||||
YUZU_NO_INLINE std::optional<u64> GetParentApplicationId(const ContentProvider* provider, u64 title_id) {
|
||||
return provider == nullptr ? std::nullopt : provider->GetParentApplicationId(title_id);
|
||||
}
|
||||
} // Anonymous namespace
|
||||
|
||||
PatchManager::PatchManager(u64 title_id_,
|
||||
const Service::FileSystem::FileSystemController& fs_controller_,
|
||||
const ContentProvider& content_provider_)
|
||||
: title_id{title_id_}, fs_controller{fs_controller_}, content_provider{content_provider_} {}
|
||||
: title_id{title_id_}, parent_title_id{GetParentApplicationId(std::addressof(content_provider_), title_id_)}, fs_controller{fs_controller_}, content_provider{content_provider_} {}
|
||||
|
||||
PatchManager::~PatchManager() = default;
|
||||
|
||||
@@ -169,13 +173,35 @@ u64 PatchManager::GetTitleID() const {
|
||||
return title_id;
|
||||
}
|
||||
|
||||
VirtualDir PatchManager::GetModificationLoadRoot(bool sdmc) const {
|
||||
const auto get_root = [&](u64 id) {
|
||||
return sdmc ? fs_controller.GetSDMCModificationLoadRoot(id) : fs_controller.GetModificationLoadRoot(id);
|
||||
};
|
||||
auto root = get_root(title_id);
|
||||
if (!parent_title_id) {
|
||||
return root;
|
||||
}
|
||||
std::vector<VirtualDir> roots{std::move(root), get_root(*parent_title_id)};
|
||||
std::erase(roots, nullptr);
|
||||
return LayeredVfsDirectory::MakeLayeredDirectory(std::move(roots));
|
||||
}
|
||||
|
||||
std::vector<std::string> PatchManager::GetDisabledAddons() const {
|
||||
auto disabled = Settings::values.disabled_addons[title_id];
|
||||
if (parent_title_id) {
|
||||
const auto& shared = Settings::values.disabled_addons[*parent_title_id];
|
||||
disabled.insert(disabled.end(), shared.begin(), shared.end());
|
||||
}
|
||||
return disabled;
|
||||
}
|
||||
|
||||
VirtualDir PatchManager::PatchExeFS(VirtualDir exefs) const {
|
||||
LOG_INFO(Loader, "Patching ExeFS for title_id={:016X}", title_id);
|
||||
|
||||
if (exefs == nullptr)
|
||||
return exefs;
|
||||
|
||||
const auto& disabled = Settings::values.disabled_addons[title_id];
|
||||
const auto disabled = GetDisabledAddons();
|
||||
|
||||
bool update_disabled = true;
|
||||
std::optional<u32> enabled_version;
|
||||
@@ -303,8 +329,8 @@ VirtualDir PatchManager::PatchExeFS(VirtualDir exefs) const {
|
||||
}
|
||||
|
||||
// LayeredExeFS
|
||||
const auto load_dir = fs_controller.GetModificationLoadRoot(title_id);
|
||||
const auto sdmc_load_dir = fs_controller.GetSDMCModificationLoadRoot(title_id);
|
||||
const auto load_dir = GetModificationLoadRoot();
|
||||
const auto sdmc_load_dir = GetModificationLoadRoot(true);
|
||||
|
||||
std::vector<VirtualDir> patch_dirs = {sdmc_load_dir};
|
||||
if (load_dir != nullptr) {
|
||||
@@ -346,7 +372,7 @@ VirtualDir PatchManager::PatchExeFS(VirtualDir exefs) const {
|
||||
}
|
||||
|
||||
std::vector<VirtualFile> PatchManager::CollectPatches(const std::vector<VirtualDir>& patch_dirs, const std::string& build_id) const {
|
||||
const auto& disabled = Settings::values.disabled_addons[title_id];
|
||||
const auto disabled = GetDisabledAddons();
|
||||
const auto nso_build_id = fmt::format("{:0<64}", build_id);
|
||||
|
||||
std::vector<VirtualFile> out;
|
||||
@@ -405,7 +431,7 @@ std::vector<u8> PatchManager::PatchNSO(const std::vector<u8>& nso, const std::st
|
||||
|
||||
LOG_INFO(Loader, "Patching NSO for name={}, build_id={}", name, build_id);
|
||||
|
||||
const auto load_dir = fs_controller.GetModificationLoadRoot(title_id);
|
||||
const auto load_dir = GetModificationLoadRoot();
|
||||
if (load_dir == nullptr) {
|
||||
LOG_ERROR(Loader, "Cannot load mods for invalid title_id={:016X}", title_id);
|
||||
return nso;
|
||||
@@ -448,7 +474,7 @@ bool PatchManager::HasNSOPatch(const BuildID& build_id_, std::string_view name)
|
||||
|
||||
LOG_INFO(Loader, "Querying NSO patch existence for build_id={}, name={}", build_id, name);
|
||||
|
||||
const auto load_dir = fs_controller.GetModificationLoadRoot(title_id);
|
||||
const auto load_dir = GetModificationLoadRoot();
|
||||
if (load_dir == nullptr) {
|
||||
LOG_ERROR(Loader, "Cannot load mods for invalid title_id={:016X}", title_id);
|
||||
return false;
|
||||
@@ -462,13 +488,13 @@ bool PatchManager::HasNSOPatch(const BuildID& build_id_, std::string_view name)
|
||||
}
|
||||
|
||||
std::vector<Core::Memory::CheatEntry> PatchManager::CreateCheatList(const BuildID& build_id_) const {
|
||||
const auto load_dir = fs_controller.GetModificationLoadRoot(title_id);
|
||||
const auto load_dir = GetModificationLoadRoot();
|
||||
if (load_dir == nullptr) {
|
||||
LOG_ERROR(Loader, "Cannot load mods for invalid title_id={:016X}", title_id);
|
||||
return {};
|
||||
}
|
||||
|
||||
const auto& disabled = Settings::values.disabled_addons[title_id];
|
||||
const auto disabled = GetDisabledAddons();
|
||||
auto patch_dirs = load_dir->GetSubdirectories();
|
||||
std::sort(patch_dirs.begin(), patch_dirs.end(), [](auto const& l, auto const& r) { return l->GetName() < r->GetName(); });
|
||||
|
||||
@@ -502,17 +528,16 @@ std::vector<Core::Memory::CheatEntry> PatchManager::CreateCheatList(const BuildI
|
||||
return out;
|
||||
}
|
||||
|
||||
static void ApplyLayeredFS(VirtualFile& romfs, u64 title_id, ContentRecordType type,
|
||||
const Service::FileSystem::FileSystemController& fs_controller) {
|
||||
const auto load_dir = fs_controller.GetModificationLoadRoot(title_id);
|
||||
const auto sdmc_load_dir = fs_controller.GetSDMCModificationLoadRoot(title_id);
|
||||
void PatchManager::ApplyLayeredFS(VirtualFile& romfs, ContentRecordType type) const {
|
||||
const auto load_dir = GetModificationLoadRoot();
|
||||
const auto sdmc_load_dir = GetModificationLoadRoot(true);
|
||||
if ((type != ContentRecordType::Program && type != ContentRecordType::Data &&
|
||||
type != ContentRecordType::HtmlDocument) ||
|
||||
(load_dir == nullptr && sdmc_load_dir == nullptr)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto& disabled = Settings::values.disabled_addons[title_id];
|
||||
const auto disabled = GetDisabledAddons();
|
||||
std::vector<VirtualDir> patch_dirs = load_dir->GetSubdirectories();
|
||||
if (std::find(disabled.cbegin(), disabled.cend(), "SDMC") == disabled.cend()) {
|
||||
patch_dirs.push_back(sdmc_load_dir);
|
||||
@@ -591,7 +616,7 @@ VirtualFile PatchManager::PatchRomFS(const NCA* base_nca, VirtualFile base_romfs
|
||||
|
||||
// Game Updates
|
||||
const auto update_tid = GetUpdateTitleID(title_id);
|
||||
const auto& disabled = Settings::values.disabled_addons[title_id];
|
||||
const auto disabled = GetDisabledAddons();
|
||||
|
||||
bool update_disabled = true;
|
||||
std::optional<u32> enabled_version;
|
||||
@@ -698,7 +723,7 @@ VirtualFile PatchManager::PatchRomFS(const NCA* base_nca, VirtualFile base_romfs
|
||||
|
||||
// LayeredFS
|
||||
if (apply_layeredfs) {
|
||||
ApplyLayeredFS(romfs, title_id, type, fs_controller);
|
||||
ApplyLayeredFS(romfs, type);
|
||||
}
|
||||
|
||||
return romfs;
|
||||
@@ -1072,15 +1097,22 @@ std::vector<Patch> PatchManager::GetPatches(VirtualFile update_raw) const {
|
||||
std::optional<u32> PatchManager::GetGameVersion() const {
|
||||
const auto update_tid = GetUpdateTitleID(title_id);
|
||||
if (content_provider.HasEntry(update_tid, ContentRecordType::Program)) {
|
||||
return content_provider.GetEntryVersion(update_tid);
|
||||
const auto version = content_provider.GetEntryVersion(update_tid);
|
||||
return version || !parent_title_id ? version : content_provider.GetEntryVersion(GetUpdateTitleID(*parent_title_id));
|
||||
}
|
||||
|
||||
return content_provider.GetEntryVersion(title_id);
|
||||
const auto version = content_provider.GetEntryVersion(title_id);
|
||||
return version || !parent_title_id ? version : content_provider.GetEntryVersion(*parent_title_id);
|
||||
}
|
||||
|
||||
PatchManager::Metadata PatchManager::GetControlMetadata() const {
|
||||
const auto base_control_nca = content_provider.GetEntry(title_id, ContentRecordType::Control);
|
||||
if (base_control_nca == nullptr) {
|
||||
if (parent_title_id) {
|
||||
const auto control_id = content_provider.HasEntry(*parent_title_id, ContentRecordType::Control) ? *parent_title_id : GetUpdateTitleID(*parent_title_id);
|
||||
const PatchManager parent{control_id, fs_controller, content_provider};
|
||||
return parent.GetControlMetadata();
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
|
||||
@@ -109,10 +109,14 @@ public:
|
||||
[[nodiscard]] static PatchManager::Metadata GetMetadataFromBaseOrUpdate(Core::System& system, u64 application_id) noexcept;
|
||||
|
||||
private:
|
||||
[[nodiscard]] VirtualDir GetModificationLoadRoot(bool sdmc = false) const;
|
||||
[[nodiscard]] std::vector<std::string> GetDisabledAddons() const;
|
||||
void ApplyLayeredFS(VirtualFile& romfs, ContentRecordType type) const;
|
||||
[[nodiscard]] std::vector<VirtualFile> CollectPatches(const std::vector<VirtualDir>& patch_dirs,
|
||||
const std::string& build_id) const;
|
||||
|
||||
u64 title_id;
|
||||
std::optional<u64> parent_title_id;
|
||||
const Service::FileSystem::FileSystemController& fs_controller;
|
||||
const ContentProvider& content_provider;
|
||||
};
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <algorithm>
|
||||
#include <limits>
|
||||
#include <random>
|
||||
#include <regex>
|
||||
#include <openssl/evp.h>
|
||||
@@ -348,6 +349,22 @@ std::vector<ContentProviderEntry> ContentProvider::ListEntries() const {
|
||||
return ListEntriesFilter(std::nullopt, std::nullopt, std::nullopt);
|
||||
}
|
||||
|
||||
std::optional<u64> ContentProvider::GetParentApplicationId(u64 program_id) const {
|
||||
const auto application_id = GetBaseTitleID(program_id);
|
||||
const auto program_index = program_id - application_id;
|
||||
if (program_index == 0 || program_index > std::numeric_limits<u8>::max()) {
|
||||
return std::nullopt;
|
||||
}
|
||||
if (!ListEntriesFilter(TitleType::Application, ContentRecordType::Meta, program_id).empty()) {
|
||||
return std::nullopt;
|
||||
}
|
||||
if ((!ListEntriesFilter(TitleType::Application, ContentRecordType::Meta, application_id).empty() && HasEntry(program_id, ContentRecordType::Program))
|
||||
|| (!ListEntriesFilter(TitleType::Update, ContentRecordType::Meta, GetUpdateTitleID(application_id)).empty() && HasEntry(GetUpdateTitleID(program_id), ContentRecordType::Program))) {
|
||||
return application_id;
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
PlaceholderCache::PlaceholderCache(VirtualDir dir_) : dir(std::move(dir_)) {}
|
||||
|
||||
bool PlaceholderCache::Create(const NcaID& id, u64 size) const {
|
||||
|
||||
@@ -100,6 +100,8 @@ public:
|
||||
std::optional<TitleType> title_type = {}, std::optional<ContentRecordType> record_type = {},
|
||||
std::optional<u64> title_id = {}) const = 0;
|
||||
|
||||
[[nodiscard]] std::optional<u64> GetParentApplicationId(u64 program_id) const;
|
||||
|
||||
protected:
|
||||
// A single instance of KeyManager to be used by GetEntry()
|
||||
Core::Crypto::KeyManager& keys = Core::Crypto::KeyManager::Instance();
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
#include "hid_core/frontend/emulated_controller.h"
|
||||
#include "hid_core/hid_core.h"
|
||||
#include "hid_core/hid_types.h"
|
||||
#include <array>
|
||||
|
||||
namespace Core::Frontend {
|
||||
|
||||
@@ -29,23 +30,39 @@ void DefaultControllerApplet::ReconfigureControllers(ReconfigureCallback callbac
|
||||
|
||||
const std::size_t min_supported_players =
|
||||
parameters.enable_single_mode ? 1 : parameters.min_players;
|
||||
using Core::HID::NpadStyleIndex;
|
||||
const std::size_t max_supported_players = parameters.enable_single_mode ? 1 : parameters.max_players;
|
||||
std::size_t num_selected_players = 0;
|
||||
std::array<bool, HID::HIDCore::available_controllers> keep_connected{};
|
||||
|
||||
// Disconnect Handheld first.
|
||||
// reserve existing AND valid players before filling slots. include Handheld, but not other
|
||||
for (std::size_t index = 0; index < hid_core.available_controllers - 1; ++index) {
|
||||
const auto* controller = hid_core.GetEmulatedControllerByIndex(index);
|
||||
if (!parameters.keep_controllers_connected || !controller->IsConnected() || num_selected_players >= max_supported_players) continue;
|
||||
|
||||
const auto style = controller->GetNpadStyleIndex();
|
||||
keep_connected[index] =
|
||||
(style == NpadStyleIndex::Fullkey && parameters.allow_pro_controller) ||
|
||||
(style == NpadStyleIndex::JoyconDual && parameters.allow_dual_joycons) ||
|
||||
(style == NpadStyleIndex::JoyconLeft && parameters.allow_left_joycon) ||
|
||||
(style == NpadStyleIndex::JoyconRight && parameters.allow_right_joycon) ||
|
||||
(style == NpadStyleIndex::Handheld && parameters.enable_single_mode && parameters.allow_handheld && !Settings::IsDockedMode()) ||
|
||||
(style == NpadStyleIndex::GameCube && parameters.allow_gamecube_controller);
|
||||
num_selected_players += keep_connected[index];
|
||||
}
|
||||
auto* handheld = hid_core.GetEmulatedController(Core::HID::NpadIdType::Handheld);
|
||||
handheld->Disconnect();
|
||||
if (!keep_connected[hid_core.available_controllers - 2]) handheld->Disconnect();
|
||||
|
||||
// Deduce the best configuration based on the input parameters.
|
||||
for (std::size_t index = 0; index < hid_core.available_controllers - 2; ++index) {
|
||||
auto* controller = hid_core.GetEmulatedControllerByIndex(index);
|
||||
|
||||
// First, disconnect all controllers regardless of the value of keep_controllers_connected.
|
||||
// This makes it easy to connect the desired controllers.
|
||||
if (keep_connected[index]) continue;
|
||||
controller->Disconnect();
|
||||
|
||||
// Only connect the minimum number of required players.
|
||||
if (index >= min_supported_players) {
|
||||
continue;
|
||||
}
|
||||
// only add players still needed to reach the minimum
|
||||
if (num_selected_players >= min_supported_players) continue;
|
||||
++num_selected_players;
|
||||
|
||||
// Connect controllers based on the following priority list from highest to lowest priority:
|
||||
// Pro Controller -> Dual Joycons -> Left Joycon/Right Joycon -> Handheld
|
||||
|
||||
@@ -465,6 +465,10 @@ void KScheduler::ScheduleImplFiber(KernelCore& kernel) {
|
||||
// Check if we need scheduling. If we do, then we can't complete the switch and should
|
||||
// retry.
|
||||
if (m_state.needs_scheduling.load(std::memory_order_seq_cst)) {
|
||||
// Some libc++ lazily init mutex
|
||||
[[maybe_unused]] auto const can_lock = highest_priority_thread->m_context_guard.try_lock();
|
||||
DEBUG_ASSERT(!can_lock);
|
||||
|
||||
// Our switch failed.
|
||||
// We should unlock the thread context, and then retry.
|
||||
highest_priority_thread->m_context_guard.unlock();
|
||||
@@ -496,6 +500,10 @@ void KScheduler::Unload(KernelCore& kernel, KThread* thread) {
|
||||
|
||||
// Check if the thread is terminated by checking the DPC flags.
|
||||
if ((thread->GetStackParameters().dpc_flags & static_cast<u32>(DpcFlag::Terminated)) == 0) {
|
||||
// Some libc++ lazily init mutex
|
||||
[[maybe_unused]] auto const can_lock = thread->m_context_guard.try_lock();
|
||||
DEBUG_ASSERT(!can_lock);
|
||||
|
||||
// The thread isn't terminated, so we want to unlock it.
|
||||
thread->m_context_guard.unlock();
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
|
||||
@@ -12,7 +12,6 @@
|
||||
#include "common/atomic_ops.h"
|
||||
#include "common/common_funcs.h"
|
||||
#include "common/common_types.h"
|
||||
#include "common/spin_lock.h"
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
@@ -30,7 +29,7 @@ public:
|
||||
};
|
||||
|
||||
public:
|
||||
constexpr KSlabHeapImpl() = default;
|
||||
KSlabHeapImpl() = default;
|
||||
|
||||
void Initialize() {
|
||||
ASSERT(m_head == nullptr);
|
||||
@@ -68,7 +67,7 @@ public:
|
||||
|
||||
private:
|
||||
std::atomic<Node*> m_head{};
|
||||
Common::SpinLock m_lock;
|
||||
std::mutex m_lock;
|
||||
};
|
||||
|
||||
} // namespace impl
|
||||
|
||||
@@ -19,7 +19,6 @@
|
||||
|
||||
#include "common/intrusive_red_black_tree.h"
|
||||
#include "common/scratch_buffer.h"
|
||||
#include "common/spin_lock.h"
|
||||
#include "core/arm/arm_interface.h"
|
||||
#include "core/hle/kernel/k_affinity_mask.h"
|
||||
#include "core/hle/kernel/k_light_lock.h"
|
||||
@@ -920,7 +919,7 @@ private:
|
||||
bool m_resource_limit_release_hint{};
|
||||
bool m_is_kernel_address_key{};
|
||||
StackParameters m_stack_parameters{};
|
||||
Common::SpinLock m_context_guard{};
|
||||
std::mutex m_context_guard{};
|
||||
|
||||
// For emulation
|
||||
std::shared_ptr<Common::Fiber> m_host_context{};
|
||||
|
||||
@@ -23,6 +23,12 @@ constexpr std::size_t profile_username_size{32};
|
||||
using ProfileUsername = std::array<u8, profile_username_size>;
|
||||
using UserIDArray = std::array<Common::UUID, MAX_USERS>;
|
||||
|
||||
// This is nn::account::Uid
|
||||
struct Uid {
|
||||
std::array<u8, 0x10> unk0;
|
||||
};
|
||||
static_assert(sizeof(Uid) == 0x10);
|
||||
|
||||
/// Contains extra data related to a user.
|
||||
/// TODO: RE this structure
|
||||
struct UserData {
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
#include "core/hle/service/am/frontend/applet_web_browser.h"
|
||||
#include "core/hle/service/am/frontend/applets.h"
|
||||
#include "core/hle/service/am/service/storage.h"
|
||||
#include "core/hle/service/am/window_system.h"
|
||||
#include "core/hle/service/sm/sm.h"
|
||||
|
||||
namespace Service::AM::Frontend {
|
||||
@@ -72,10 +73,16 @@ void FrontendApplet::PushInteractiveOutData(std::shared_ptr<IStorage> storage) {
|
||||
|
||||
void FrontendApplet::Exit() {
|
||||
auto applet_ = applet.lock();
|
||||
|
||||
std::scoped_lock lk{applet_->lock};
|
||||
applet_->is_completed = true;
|
||||
applet_->state_changed_event.Signal(system.Kernel());
|
||||
{
|
||||
std::scoped_lock lk{applet_->lock};
|
||||
applet_->is_completed = true;
|
||||
applet_->state_changed_event.Signal(system.Kernel());
|
||||
}
|
||||
if (auto caller_applet = applet_->caller_applet.lock()) {
|
||||
std::scoped_lock lk{caller_applet->lock};
|
||||
std::erase(caller_applet->child_applets, applet_);
|
||||
}
|
||||
if (auto* window_system = system.GetAppletManager().GetWindowSystem()) window_system->RequestUpdate();
|
||||
}
|
||||
|
||||
FrontendAppletSet::FrontendAppletSet() = default;
|
||||
|
||||
@@ -92,6 +92,13 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
void RequestFocusStateChangedNotification(Kernel::KernelCore& kernel) {
|
||||
if (m_focus_state_changed_notification_enabled) {
|
||||
m_has_focus_state_changed = true;
|
||||
this->SignalSystemEventIfNeeded(kernel);
|
||||
}
|
||||
}
|
||||
|
||||
void OnOperationAndPerformanceModeChanged(Kernel::KernelCore& kernel);
|
||||
|
||||
public:
|
||||
|
||||
@@ -347,21 +347,22 @@ Result IApplicationFunctions::NotifyRunning(Out<bool> out_became_running) {
|
||||
|
||||
Result IApplicationFunctions::GetPseudoDeviceId(Out<Common::UUID> out_pseudo_device_id) {
|
||||
LOG_WARNING(Service_AM, "(stubbed)");
|
||||
R_UNLESS(out_pseudo_device_id, ResultUnknown);
|
||||
R_UNLESS(out_pseudo_device_id != nullptr, ResultUnknown);
|
||||
|
||||
// This should be hashed with the device specific hash
|
||||
// for now this will do
|
||||
const auto res = FileSys::PatchManager::GetMetadataFromBaseOrUpdate(system, m_applet->program_id);
|
||||
u8 hash[EVP_MAX_MD_SIZE];
|
||||
R_UNLESS(res.first != nullptr, ResultUnknown);
|
||||
std::array<u8, EVP_MAX_MD_SIZE> hash;
|
||||
unsigned int hash_len = 0;
|
||||
auto const seed = res.first->raw.seed_for_pseudo_device_id;
|
||||
EVP_MD_CTX *ctx = EVP_MD_CTX_new();
|
||||
auto const algorithm = EVP_sha1();
|
||||
EVP_DigestInit_ex(ctx, algorithm, nullptr);
|
||||
EVP_DigestUpdate(ctx, &seed, sizeof(seed));
|
||||
EVP_DigestFinal_ex(ctx, hash, &hash_len);
|
||||
EVP_DigestFinal_ex(ctx, hash.data(), &hash_len);
|
||||
EVP_MD_CTX_free(ctx);
|
||||
*out_pseudo_device_id = Common::UUID::MakeRFC4122V5(std::span<u8, 20>{hash, std::size(hash)});
|
||||
*out_pseudo_device_id = Common::UUID::MakeRFC4122V5(std::span<u8, 16>{hash.begin(), hash.begin() + 16});
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
|
||||
@@ -164,14 +164,14 @@ Result ILibraryAppletAccessor::PushInData(SharedPointer<IStorage> storage) {
|
||||
Result ILibraryAppletAccessor::PopOutData(Out<SharedPointer<IStorage>> out_storage) {
|
||||
LOG_DEBUG(Service_AM, "called");
|
||||
|
||||
if (auto caller_applet = m_applet->caller_applet.lock(); caller_applet) {
|
||||
caller_applet->lifecycle_manager.GetSystemEvent().Signal(system.Kernel());
|
||||
caller_applet->lifecycle_manager.RequestResumeNotification();
|
||||
caller_applet->lifecycle_manager.GetSystemEvent().Clear(system.Kernel());
|
||||
caller_applet->lifecycle_manager.UpdateRequestedFocusState();
|
||||
}
|
||||
|
||||
R_TRY(m_broker->GetOutData().Pop(system.Kernel(), out_storage.Get()));
|
||||
if (auto caller_applet = m_applet->caller_applet.lock(); caller_applet) {
|
||||
std::scoped_lock lk{caller_applet->lock};
|
||||
const bool focus_state_changed = caller_applet->lifecycle_manager.UpdateRequestedFocusState();
|
||||
const bool is_front_app = m_applet->frontend && caller_applet->lifecycle_manager.IsApplication();
|
||||
if (focus_state_changed) caller_applet->lifecycle_manager.SignalSystemEventIfNeeded(system.Kernel());
|
||||
else if (is_front_app) caller_applet->lifecycle_manager.RequestFocusStateChangedNotification(system.Kernel());
|
||||
}
|
||||
|
||||
if (m_applet->applet_id == AppletId::ProfileSelect && *out_storage) {
|
||||
auto impl = (*out_storage)->GetImpl();
|
||||
|
||||
@@ -122,7 +122,10 @@ std::shared_ptr<ILibraryAppletAccessor> CreateGuestApplet(Core::System& system,
|
||||
auto broker = std::make_shared<AppletDataBroker>(system);
|
||||
applet->caller_applet = caller_applet;
|
||||
applet->caller_applet_broker = broker;
|
||||
caller_applet->child_applets.push_back(applet);
|
||||
{
|
||||
std::scoped_lock lk{caller_applet->lock};
|
||||
caller_applet->child_applets.push_back(applet);
|
||||
}
|
||||
window_system.TrackApplet(applet, false);
|
||||
return std::make_shared<ILibraryAppletAccessor>(system, broker, applet);
|
||||
}
|
||||
@@ -148,10 +151,10 @@ std::shared_ptr<ILibraryAppletAccessor> CreateFrontendApplet(Core::System& syste
|
||||
applet->caller_applet = caller_applet;
|
||||
applet->caller_applet_broker = storage;
|
||||
applet->frontend = system.GetFrontendAppletHolder().GetApplet(applet, applet_id, mode);
|
||||
caller_applet->child_applets.push_back(applet);
|
||||
|
||||
window_system.TrackApplet(applet, false);
|
||||
|
||||
{
|
||||
std::scoped_lock lk{caller_applet->lock};
|
||||
caller_applet->child_applets.push_back(applet);
|
||||
}
|
||||
return std::make_shared<ILibraryAppletAccessor>(system, storage, applet);
|
||||
}
|
||||
|
||||
|
||||
@@ -191,7 +191,7 @@ void LoopProcess(Core::System& system) {
|
||||
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("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:a", std::make_shared<IAudioInManagerForApplet>(system), 30);
|
||||
|
||||
@@ -154,6 +154,9 @@ FSP_SRV::FSP_SRV(Core::System& system_)
|
||||
{720, nullptr, "AbandonAccessFailure"},
|
||||
{800, nullptr, "GetAndClearFileSystemProxyErrorInfo"},
|
||||
{810, nullptr, "RegisterProgramIndexMapInfo"},
|
||||
{820, nullptr, "GetContentStorageInfoIndex"},
|
||||
{830, nullptr, "EncryptStreamPlaySaveData"},
|
||||
{831, nullptr, "DecryptStreamPlaySaveData"},
|
||||
{1000, nullptr, "SetBisRootForHost"},
|
||||
{1001, nullptr, "SetSaveDataSize"},
|
||||
{1002, nullptr, "SetSaveDataRootPath"},
|
||||
|
||||
@@ -121,7 +121,7 @@ IHidSystemServer::IHidSystemServer(Core::System& system_, std::shared_ptr<Resour
|
||||
{547, nullptr, "GetAllowedBluetoothLinksCount"},
|
||||
{548, &IHidSystemServer::GetRegisteredDevices, "GetRegisteredDevices"},
|
||||
{549, nullptr, "GetConnectableRegisteredDevices"},
|
||||
{551, nullptr, "GetRegisteredDevicesForControllerSupport"}, //20.0.0+
|
||||
{551, &IHidSystemServer::GetRegisteredDevices, "GetRegisteredDevicesForControllerSupport"}, //20.0.0+ //mocked via 548 for Diablo 3 (at least)
|
||||
{700, nullptr, "ActivateUniquePad"},
|
||||
{702, &IHidSystemServer::AcquireUniquePadConnectionEventHandle, "AcquireUniquePadConnectionEventHandle"},
|
||||
{703, &IHidSystemServer::GetUniquePadIds, "GetUniquePadIds"},
|
||||
@@ -758,7 +758,7 @@ void IHidSystemServer::AcquireDeviceRegisteredEventForControllerSupport(HLEReque
|
||||
}
|
||||
|
||||
void IHidSystemServer::GetRegisteredDevices(HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_HID, "(STUBBED) called");
|
||||
LOG_WARNING(Service_HID, "(STUBBED) called, command={}", ctx.GetCommand()); //548 or 551
|
||||
|
||||
struct RegisterData {
|
||||
std::array<u8, 0x68> data;
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
|
||||
namespace IPC {
|
||||
|
||||
constexpr Result ResultNotSupported{ErrorModule::HIPC, 1};
|
||||
constexpr Result ResultSessionClosed{ErrorModule::HIPC, 301};
|
||||
|
||||
struct ResponseBuilder {
|
||||
|
||||
@@ -71,17 +71,14 @@ public:
|
||||
void InstructionSynchronizationBarrierRaised() override {
|
||||
last_code_addr = u64(-1); //reset back, force refetch
|
||||
}
|
||||
u8 MemoryRead8(u64 vaddr) override {
|
||||
return ReadMemory<u8>(vaddr);
|
||||
}
|
||||
u16 MemoryRead16(u64 vaddr) override {
|
||||
return ReadMemory<u16>(vaddr);
|
||||
}
|
||||
u32 MemoryRead32(u64 vaddr) override {
|
||||
return ReadMemory<u32>(vaddr);
|
||||
}
|
||||
u64 MemoryRead64(u64 vaddr) override {
|
||||
return ReadMemory<u64>(vaddr);
|
||||
u64 MemoryRead(u64 vaddr, size_t size) override {
|
||||
switch (size) {
|
||||
case sizeof(u64): return ReadMemory<u64>(vaddr);
|
||||
case sizeof(u32): return ReadMemory<u32>(vaddr);
|
||||
case sizeof(u16): return ReadMemory<u16>(vaddr);
|
||||
case sizeof(u8): return ReadMemory<u8>(vaddr);
|
||||
default: UNREACHABLE();
|
||||
}
|
||||
}
|
||||
u128 MemoryRead128(u64 vaddr) override {
|
||||
return ReadMemory<u128>(vaddr);
|
||||
@@ -89,22 +86,19 @@ public:
|
||||
std::string MemoryReadCString(u64 vaddr) {
|
||||
std::string result{};
|
||||
u8 next;
|
||||
while ((next = MemoryRead8(vaddr++)) != 0)
|
||||
while ((next = u8(MemoryRead(vaddr++, sizeof(u8)))) != 0)
|
||||
result += char(next);
|
||||
return result;
|
||||
}
|
||||
|
||||
void MemoryWrite8(u64 vaddr, u8 value) override {
|
||||
WriteMemory<u8>(vaddr, value);
|
||||
}
|
||||
void MemoryWrite16(u64 vaddr, u16 value) override {
|
||||
WriteMemory<u16>(vaddr, value);
|
||||
}
|
||||
void MemoryWrite32(u64 vaddr, u32 value) override {
|
||||
WriteMemory<u32>(vaddr, value);
|
||||
}
|
||||
void MemoryWrite64(u64 vaddr, u64 value) override {
|
||||
WriteMemory<u64>(vaddr, value);
|
||||
void MemoryWrite(u64 vaddr, u64 value, size_t size) override {
|
||||
switch (size) {
|
||||
case sizeof(u64): WriteMemory<u64>(vaddr, u64(value)); break;
|
||||
case sizeof(u32): WriteMemory<u32>(vaddr, u32(value)); break;
|
||||
case sizeof(u16): WriteMemory<u16>(vaddr, u16(value)); break;
|
||||
case sizeof(u8): WriteMemory<u8>(vaddr, u8(value)); break;
|
||||
default: UNREACHABLE();
|
||||
}
|
||||
}
|
||||
void MemoryWrite128(u64 vaddr, u128 value) override {
|
||||
WriteMemory<u128>(vaddr, value);
|
||||
@@ -193,14 +187,14 @@ public:
|
||||
// The loaded NRO file has ELF relocations that must be processed before it can run.
|
||||
// Normally this would be processed by RTLD, but in HLE context, we don't have
|
||||
// the linker available, so we have to do it ourselves.
|
||||
const VAddr mod_offset{callbacks->MemoryRead32(4)};
|
||||
if (callbacks->MemoryRead32(mod_offset) != Common::MakeMagic('M', 'O', 'D', '0'))
|
||||
const VAddr mod_offset{callbacks->MemoryRead(4, sizeof(u32))};
|
||||
if (callbacks->MemoryRead(mod_offset, sizeof(u32)) != Common::MakeMagic('M', 'O', 'D', '0'))
|
||||
return false;
|
||||
|
||||
// For more info about dynamic entries, see the ELF ABI specification:
|
||||
// https://refspecs.linuxbase.org/elf/gabi4+/ch5.dynamic.html
|
||||
// https://refspecs.linuxbase.org/elf/gabi4+/ch4.reloc.html
|
||||
VAddr dynamic_offset{mod_offset + callbacks->MemoryRead32(mod_offset + 4)};
|
||||
VAddr dynamic_offset{mod_offset + callbacks->MemoryRead(mod_offset + 4, sizeof(u32))};
|
||||
VAddr rela_dyn = 0, relr_dyn = 0;
|
||||
size_t num_rela = 0, num_relr = 0;
|
||||
while (true) {
|
||||
@@ -222,8 +216,8 @@ public:
|
||||
for (size_t i = 0; i < num_rela; i++) {
|
||||
const auto rela{callbacks->ReadMemory<Elf64_Rela>(rela_dyn + i * sizeof(Elf64_Rela))};
|
||||
if (Elf64RelType(rela.r_info) == ElfAArch64Relative) {
|
||||
const VAddr contents{callbacks->MemoryRead64(rela.r_offset)};
|
||||
callbacks->MemoryWrite64(rela.r_offset, contents + rela.r_addend);
|
||||
const VAddr contents{callbacks->MemoryRead(rela.r_offset, sizeof(u64))};
|
||||
callbacks->MemoryWrite(rela.r_offset, contents + rela.r_addend, sizeof(u64));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -231,7 +225,7 @@ public:
|
||||
for (size_t i = 0; i < num_relr; i++) {
|
||||
const auto relr = callbacks->ReadMemory<Elf64_Relr>(relr_dyn + i * sizeof(Elf64_Relr));
|
||||
const auto incr = [&](VAddr where) {
|
||||
callbacks->MemoryWrite64(where, callbacks->MemoryRead64(where) + relocbase);
|
||||
callbacks->MemoryWrite(where, callbacks->MemoryRead(where, sizeof(u64)) + relocbase, sizeof(u64));
|
||||
};
|
||||
if ((relr & 1) == 0) {
|
||||
// where pointer
|
||||
@@ -294,7 +288,7 @@ public:
|
||||
if (argument_stack.size() > 8) {
|
||||
const VAddr new_sp = Common::AlignDown(top_of_stack - (argument_stack.size() - 8) * sizeof(u64), STACK_ALIGN);
|
||||
for (size_t i = 8; i < argument_stack.size(); i++)
|
||||
callbacks->MemoryWrite64(new_sp + (i - 8) * sizeof(u64), argument_stack[i]);
|
||||
callbacks->MemoryWrite(new_sp + (i - 8) * sizeof(u64), argument_stack[i], sizeof(u64));
|
||||
jit->SetSP(new_sp);
|
||||
}
|
||||
// Reset the call state for the next invocation
|
||||
@@ -385,17 +379,17 @@ void DynarmicCallbacks64::CallSVC(u32 swi) {
|
||||
|
||||
if (dest < src) {
|
||||
for (size_t i = 0; i < n; i++)
|
||||
MemoryWrite8(dest + i, MemoryRead8(src + i));
|
||||
MemoryWrite(dest + i, u8(MemoryRead(src + i, sizeof(u8))), sizeof(u8));
|
||||
} else {
|
||||
for (size_t i = n; i > 0; i--)
|
||||
MemoryWrite8(dest + i - 1, MemoryRead8(src + i - 1));
|
||||
MemoryWrite(dest + i - 1, u8(MemoryRead(src + i - 1, sizeof(u8))), sizeof(u8));
|
||||
}
|
||||
} else if (pc == parent.helpers[size_t(HelperFn::Memset)]) {
|
||||
const VAddr dest{parent.jit->GetRegister(0)};
|
||||
const u64 c{parent.jit->GetRegister(1)};
|
||||
const size_t n{parent.jit->GetRegister(2)};
|
||||
for (size_t i = 0; i < n; i++)
|
||||
MemoryWrite8(dest + i, u8(c));
|
||||
MemoryWrite(dest + i, u8(c), sizeof(u8));
|
||||
} else if (pc == parent.helpers[size_t(HelperFn::Resolve)]) {
|
||||
// X0 contains a char* for a symbol to resolve
|
||||
const auto name{MemoryReadCString(parent.jit->GetRegister(0))};
|
||||
@@ -422,7 +416,7 @@ void DynarmicCallbacks64::CallSVC(u32 swi) {
|
||||
}
|
||||
|
||||
void DynarmicCallbacks64::ExceptionRaised(u64 pc, Dynarmic::A64::Exception exception) {
|
||||
auto const inst = MemoryRead32(pc);
|
||||
auto const inst = MemoryRead(pc, sizeof(u32));
|
||||
LOG_CRITICAL(Service_JIT, "{} PC @ {:08x}, data = {:08x}", exception, pc, inst);
|
||||
parent.jit->HaltExecution();
|
||||
}
|
||||
|
||||
@@ -6,10 +6,16 @@
|
||||
|
||||
#include "common/string_util.h"
|
||||
#include "core/core.h"
|
||||
#include "core/hle/kernel/k_client_session.h"
|
||||
#include "core/hle/result.h"
|
||||
#include "core/hle/service/acc/profile_manager.h"
|
||||
#include "core/hle/service/cmif_types.h"
|
||||
#include "core/hle/service/cmif_serialization.h"
|
||||
#include "core/hle/service/ipc_helpers.h"
|
||||
#include "core/hle/service/ngc/ngc.h"
|
||||
#include "core/hle/service/server_manager.h"
|
||||
#include "core/hle/service/service.h"
|
||||
#include "frontend_common/firmware_manager.h"
|
||||
|
||||
namespace Service::NGC {
|
||||
|
||||
@@ -166,12 +172,123 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
struct SaveDataHandle {
|
||||
u64 unk0;
|
||||
};
|
||||
static_assert(sizeof(SaveDataHandle) == 0x08);
|
||||
|
||||
class IUserShimScopedObject final : public ServiceFramework<IUserShimScopedObject> {
|
||||
public:
|
||||
explicit IUserShimScopedObject(Core::System& system_) : ServiceFramework(system_, "IUserShimScopedObject") {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{450, nullptr, "InitializeForSaveData"},
|
||||
{451, nullptr, "FinalizeForSaveData"},
|
||||
{452, D<&IUserShimScopedObject::OpenSaveData>, "OpenSaveData"},
|
||||
{453, nullptr, "CloseSaveData"},
|
||||
{454, D<&IUserShimScopedObject::ReadSaveSlot>, "ReadSaveSlot"},
|
||||
{455, D<&IUserShimScopedObject::WriteSaveSlot>, "WriteSaveSlot"},
|
||||
{456, nullptr, "FlushSaveSlot"},
|
||||
{457, nullptr, "CommitSaveData"},
|
||||
};
|
||||
// clang-format on
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
|
||||
Result OpenSaveData(Account::Uid unk0, Out<SaveDataHandle> unk1) {
|
||||
LOG_WARNING(Service_NGC, "stubbed");
|
||||
R_THROW(IPC::ResultNotSupported);
|
||||
}
|
||||
|
||||
Result ReadSaveSlot(s32 offset, SaveDataHandle handle, OutBuffer<BufferAttr_HipcAutoSelect> out_data, Out<u32> out_size) {
|
||||
LOG_WARNING(Service_NGC, "stubbed");
|
||||
R_THROW(IPC::ResultNotSupported);
|
||||
}
|
||||
|
||||
Result WriteSaveSlot(s32 offset, SaveDataHandle handle, InBuffer<BufferAttr_HipcAutoSelect> out_data) {
|
||||
LOG_WARNING(Service_NGC, "stubbed");
|
||||
// to implement
|
||||
R_SUCCEED();
|
||||
}
|
||||
};
|
||||
|
||||
class IUserService final : public ServiceFramework<IUserService> {
|
||||
public:
|
||||
explicit IUserService(Core::System& system_) : ServiceFramework(system_, "stpl:u") {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0 , D<&IUserService::Cmd0>, "Cmd0"},
|
||||
};
|
||||
// clang-format on
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
Result Cmd0(u32 unk0, OutInterface<IUserShimScopedObject> out_interface) {
|
||||
LOG_WARNING(Service_NGC, "stubbed");
|
||||
*out_interface = std::make_shared<IUserShimScopedObject>(system);
|
||||
R_SUCCEED();
|
||||
}
|
||||
};
|
||||
|
||||
class ISystemShimScopedObject final : public ServiceFramework<ISystemShimScopedObject> {
|
||||
public:
|
||||
explicit ISystemShimScopedObject(Core::System& system_) : ServiceFramework(system_, "ISystemShimScopedObject") {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{106, nullptr, "Cmd106"},
|
||||
{107, nullptr, "Cmd107"},
|
||||
{108, D<&ISystemShimScopedObject::Cmd108>, "Cmd108"},
|
||||
{207, nullptr, "Cmd207"},
|
||||
{208, D<&ISystemShimScopedObject::Cmd208>, "Cmd208"},
|
||||
{209, nullptr, "Cmd209"},
|
||||
{210, nullptr, "Cmd210"},
|
||||
{211, nullptr, "Cmd211"},
|
||||
{212, nullptr, "Cmd212"},
|
||||
};
|
||||
// clang-format on
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
|
||||
Result Cmd108() {
|
||||
LOG_WARNING(Service_NGC, "stubbed");
|
||||
R_THROW(IPC::ResultNotSupported);
|
||||
}
|
||||
|
||||
Result Cmd208(Out<std::array<u8, 0x20>> unk0) {
|
||||
LOG_WARNING(Service_NGC, "stubbed");
|
||||
R_THROW(IPC::ResultNotSupported);
|
||||
}
|
||||
};
|
||||
|
||||
class ISystemService final : public ServiceFramework<ISystemService> {
|
||||
public:
|
||||
explicit ISystemService(Core::System& system_) : ServiceFramework(system_, "stpl:sys") {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0 , D<&ISystemService::Cmd0>, "Cmd0"},
|
||||
};
|
||||
// clang-format on
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
Result Cmd0(OutInterface<ISystemShimScopedObject> out_interface) {
|
||||
LOG_WARNING(Service_NGC, "stubbed");
|
||||
*out_interface = std::make_shared<ISystemShimScopedObject>(system);
|
||||
R_SUCCEED();
|
||||
}
|
||||
};
|
||||
|
||||
void LoopProcess(Core::System& system) {
|
||||
auto server_manager = std::make_unique<ServerManager>(system);
|
||||
|
||||
server_manager->RegisterNamedService("ngct:u", std::make_shared<IService>(system), 4);
|
||||
server_manager->RegisterNamedService("ngct:s", std::make_shared<IServiceWithManagementApi>(system), 4);
|
||||
server_manager->RegisterNamedService("ngc:u", std::make_shared<NgcServiceImpl>(system), 4);
|
||||
|
||||
// +23.0.0
|
||||
if (FirmwareManager::GetFirmwareVersion(system).first.major >= 23) {
|
||||
server_manager->RegisterNamedService("stpl:u", std::make_shared<IUserService>(system), 4);
|
||||
server_manager->RegisterNamedService("stpl:sys", std::make_shared<ISystemService>(system), 4);
|
||||
}
|
||||
|
||||
ServerManager::RunServer(std::move(server_manager));
|
||||
}
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "core/hle/service/cmif_types.h"
|
||||
#include "core/hle/service/ns/i_async_result.h"
|
||||
#include "core/hle/service/ns/async_result.h"
|
||||
#include "core/hle/service/ns/language.h"
|
||||
#include "core/hle/service/ns/ns_types.h"
|
||||
#include "core/hle/service/os/event.h"
|
||||
|
||||
+2
-2
@@ -2,7 +2,7 @@
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#include "core/hle/service/cmif_serialization.h"
|
||||
#include "core/hle/service/ns/i_async_result.h"
|
||||
#include "core/hle/service/ns/async_result.h"
|
||||
|
||||
#include <cstring>
|
||||
|
||||
@@ -32,4 +32,4 @@ Result IAsyncResult::Cancel() {
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
} // namespace Service::NS
|
||||
} // namespace Service::NS
|
||||
@@ -311,27 +311,30 @@ void IReadOnlyApplicationControlDataInterface::ListApplicationIcon(HLERequestCon
|
||||
// u64 - app count
|
||||
memory.WriteBlock(t_mem_address + out_length, &app_count, sizeof(u64));
|
||||
out_length += sizeof(u64);
|
||||
ASSERT(out_length <= t_mem->GetSize());
|
||||
// [list of u64] - size of icons
|
||||
for (size_t i = 0; i < app_count; ++i) {
|
||||
const u64 app_id = app_ids_buffer[i];
|
||||
const FileSys::PatchManager pm{app_id, system.GetFileSystemController(), system.GetContentProvider()};
|
||||
const auto control = pm.GetControlMetadata();
|
||||
u64 full_size = control.second->GetSize();
|
||||
memory.WriteBlock(t_mem_address + out_length, &full_size, sizeof(u64));
|
||||
if (const auto control = pm.GetControlMetadata(); control.second) {
|
||||
u64 full_size = control.second->GetSize();
|
||||
memory.WriteBlock(t_mem_address + out_length, &full_size, sizeof(u64));
|
||||
}
|
||||
out_length += sizeof(u64);
|
||||
ASSERT(out_length <= t_mem->GetSize());
|
||||
}
|
||||
// [list of raw icon data]
|
||||
std::vector<u8> full_icon_data;
|
||||
for (size_t i = 0; i < app_count; ++i) {
|
||||
const u64 app_id = app_ids_buffer[i];
|
||||
const FileSys::PatchManager pm{app_id, system.GetFileSystemController(), system.GetContentProvider()};
|
||||
const auto control = pm.GetControlMetadata();
|
||||
auto const full_size = control.second->GetSize();
|
||||
if (full_size > 0) {
|
||||
full_icon_data.resize(full_size);
|
||||
control.second->Read(full_icon_data.data(), full_size, 0);
|
||||
memory.WriteBlock(t_mem_address + out_length, full_icon_data.data(), full_size);
|
||||
out_length += full_size;
|
||||
if (const auto control = pm.GetControlMetadata(); control.second) {
|
||||
if (auto const full_size = control.second->GetSize(); full_size > 0) {
|
||||
std::vector<u8> full_icon_data(full_size);
|
||||
control.second->Read(full_icon_data.data(), full_size, 0);
|
||||
memory.WriteBlock(t_mem_address + out_length, full_icon_data.data(), full_size);
|
||||
out_length += full_size;
|
||||
ASSERT(out_length <= t_mem->GetSize());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -345,6 +348,12 @@ void IReadOnlyApplicationControlDataInterface::ListApplicationIcon(HLERequestCon
|
||||
void IReadOnlyApplicationControlDataInterface::ListApplicationTitle(HLERequestContext& ctx) {
|
||||
const auto app_ids_buffer = ctx.ReadBuffer();
|
||||
const size_t app_count = app_ids_buffer.size() / sizeof(u64);
|
||||
|
||||
std::vector<u64> application_ids(app_count);
|
||||
if (app_count > 0) {
|
||||
std::memcpy(application_ids.data(), app_ids_buffer.data(), app_count * sizeof(u64));
|
||||
}
|
||||
|
||||
auto t_mem_obj = ctx.GetObjectFromHandle<Kernel::KTransferMemory>(ctx.GetCopyHandle(0));
|
||||
auto* t_mem = t_mem_obj.GetPointerUnsafe();
|
||||
constexpr size_t title_entry_size = sizeof(FileSys::LanguageEntry);
|
||||
@@ -354,8 +363,9 @@ void IReadOnlyApplicationControlDataInterface::ListApplicationTitle(HLERequestCo
|
||||
auto& memory = system.ApplicationMemory();
|
||||
const auto t_mem_address = t_mem->GetSourceAddress();
|
||||
for (size_t i = 0; i < app_count; ++i) {
|
||||
const u64 app_id = app_ids_buffer[i];
|
||||
const FileSys::PatchManager pm{app_id, system.GetFileSystemController(), system.GetContentProvider()};
|
||||
const u64 app_id = application_ids[i];
|
||||
const FileSys::PatchManager pm{app_id, system.GetFileSystemController(),
|
||||
system.GetContentProvider()};
|
||||
const auto control = pm.GetControlMetadata();
|
||||
FileSys::LanguageEntry entry{};
|
||||
if (control.first != nullptr) {
|
||||
|
||||
+218
-244
@@ -48,6 +48,12 @@ enum class OptionType : u32 {
|
||||
EnableAlpn = 3,
|
||||
};
|
||||
|
||||
// This is nn::ssl::sf::RenegotiationMode
|
||||
enum RenegotiationMode : u32 {
|
||||
None = 0, ///< None
|
||||
Secure = 1, ///< Secure
|
||||
};
|
||||
|
||||
// This is nn::ssl::sf::SslVersion
|
||||
struct SslVersion {
|
||||
union {
|
||||
@@ -75,34 +81,34 @@ public:
|
||||
shared_data{shared_data_in}, backend{std::move(backend_in)} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, &ISslConnection::SetSocketDescriptor, "SetSocketDescriptor"},
|
||||
{1, &ISslConnection::SetHostName, "SetHostName"},
|
||||
{2, &ISslConnection::SetVerifyOption, "SetVerifyOption"},
|
||||
{3, &ISslConnection::SetIoMode, "SetIoMode"},
|
||||
{4, nullptr, "GetSocketDescriptor"},
|
||||
{5, nullptr, "GetHostName"},
|
||||
{0, D<&ISslConnection::SetSocketDescriptor>, "SetSocketDescriptor"},
|
||||
{1, D<&ISslConnection::SetHostName>, "SetHostName"},
|
||||
{2, D<&ISslConnection::SetVerifyOption>, "SetVerifyOption"},
|
||||
{3, D<&ISslConnection::SetIoMode>, "SetIoMode"},
|
||||
{4, D<&ISslConnection::GetSocketDescriptor>, "GetSocketDescriptor"},
|
||||
{5, D<&ISslConnection::GetHostName>, "GetHostName"},
|
||||
{6, nullptr, "GetVerifyOption"},
|
||||
{7, nullptr, "GetIoMode"},
|
||||
{8, &ISslConnection::DoHandshake, "DoHandshake"},
|
||||
{7, D<&ISslConnection::GetIoMode>, "GetIoMode"},
|
||||
{8, D<&ISslConnection::DoHandshake>, "DoHandshake"},
|
||||
{9, &ISslConnection::DoHandshakeGetServerCert, "DoHandshakeGetServerCert"},
|
||||
{10, &ISslConnection::Read, "Read"},
|
||||
{11, &ISslConnection::Write, "Write"},
|
||||
{12, &ISslConnection::Pending, "Pending"},
|
||||
{13, nullptr, "Peek"},
|
||||
{14, nullptr, "Poll"},
|
||||
{15, nullptr, "GetVerifyCertError"},
|
||||
{16, nullptr, "GetNeededServerCertBufferSize"},
|
||||
{17, &ISslConnection::SetSessionCacheMode, "SetSessionCacheMode"},
|
||||
{18, nullptr, "GetSessionCacheMode"},
|
||||
{19, nullptr, "FlushSessionCache"},
|
||||
{20, nullptr, "SetRenegotiationMode"},
|
||||
{21, nullptr, "GetRenegotiationMode"},
|
||||
{22, &ISslConnection::SetOption, "SetOption"},
|
||||
{23, &ISslConnection::GetOption, "GetOption"},
|
||||
{10, D<&ISslConnection::Read>, "Read"},
|
||||
{11, D<&ISslConnection::Write>, "Write"},
|
||||
{12, D<&ISslConnection::Pending>, "Pending"},
|
||||
{13, D<&ISslConnection::Peek>, "Peek"},
|
||||
{14, D<&ISslConnection::Poll>, "Poll"},
|
||||
{15, D<&ISslConnection::GetVerifyCertError>, "GetVerifyCertError"},
|
||||
{16, D<&ISslConnection::GetNeededServerCertBufferSize>, "GetNeededServerCertBufferSize"},
|
||||
{17, D<&ISslConnection::SetSessionCacheMode>, "SetSessionCacheMode"},
|
||||
{18, D<&ISslConnection::GetSessionCacheMode>, "GetSessionCacheMode"},
|
||||
{19, D<&ISslConnection::FlushSessionCache>, "FlushSessionCache"},
|
||||
{20, D<&ISslConnection::SetRenegotiationMode>, "SetRenegotiationMode"},
|
||||
{21, D<&ISslConnection::GetRenegotiationMode>, "GetRenegotiationMode"},
|
||||
{22, D<&ISslConnection::SetOption>, "SetOption"},
|
||||
{23, D<&ISslConnection::GetOption>, "GetOption"},
|
||||
{24, nullptr, "GetVerifyCertErrors"},
|
||||
{25, nullptr, "GetCipherInfo"},
|
||||
{26, &ISslConnection::SetNextAlpnProto, "SetNextAlpnProto"},
|
||||
{27, &ISslConnection::GetNextAlpnProto, "GetNextAlpnProto"},
|
||||
{26, D<&ISslConnection::SetNextAlpnProto>, "SetNextAlpnProto"},
|
||||
{27, D<&ISslConnection::GetNextAlpnProto>, "GetNextAlpnProto"},
|
||||
{28, nullptr, "SetDtlsSocketDescriptor"},
|
||||
{29, nullptr, "GetDtlsHandshakeTimeout"},
|
||||
{30, nullptr, "SetPrivateOption"},
|
||||
@@ -141,80 +147,6 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
SslVersion ssl_version;
|
||||
std::shared_ptr<SslContextSharedData> shared_data;
|
||||
std::unique_ptr<SSLConnectionBackend> backend;
|
||||
std::optional<int> fd_to_close;
|
||||
bool do_not_close_socket = false;
|
||||
bool get_server_cert_chain = false;
|
||||
bool skip_default_verify = false;
|
||||
bool enable_alpn = false;
|
||||
std::shared_ptr<Network::SocketBase> socket;
|
||||
std::vector<u8> next_alpn_proto;
|
||||
bool did_handshake = false;
|
||||
u32 verify_option = 0;
|
||||
|
||||
Result SetSocketDescriptorImpl(s32* out_fd, s32 fd) {
|
||||
LOG_DEBUG(Service_SSL, "called, fd={}", fd);
|
||||
ASSERT(!did_handshake);
|
||||
auto bsd = system.ServiceManager().GetService<Service::Sockets::BSD_USA>("bsd:u");
|
||||
ASSERT_OR_EXECUTE(bsd, { return ResultInternalError; });
|
||||
|
||||
auto const res_v = bsd->DuplicateSocketImpl(fd);
|
||||
if (auto *res = std::get_if<s32>(&res_v)) {
|
||||
const s32 duplicated_fd = *res;
|
||||
if (do_not_close_socket) {
|
||||
*out_fd = duplicated_fd;
|
||||
} else {
|
||||
*out_fd = -1;
|
||||
fd_to_close = duplicated_fd;
|
||||
}
|
||||
std::optional<std::shared_ptr<Network::SocketBase>> sock = bsd->GetSocket(duplicated_fd);
|
||||
if (!sock.has_value()) {
|
||||
LOG_ERROR(Service_SSL, "invalid socket fd {} after duplication", duplicated_fd);
|
||||
return ResultInvalidSocket;
|
||||
}
|
||||
socket = std::move(*sock);
|
||||
backend->SetSocket(socket);
|
||||
return ResultSuccess;
|
||||
}
|
||||
LOG_ERROR(Service_SSL, "Failed to duplicate socket with fd {}", fd);
|
||||
return ResultInvalidSocket;
|
||||
}
|
||||
|
||||
Result SetHostNameImpl(const std::string& hostname) {
|
||||
LOG_DEBUG(Service_SSL, "called. hostname={}", hostname);
|
||||
ASSERT(!did_handshake);
|
||||
return backend->SetHostName(hostname);
|
||||
}
|
||||
|
||||
Result SetVerifyOptionImpl(u32 option) {
|
||||
ASSERT(!did_handshake);
|
||||
LOG_DEBUG(Service_SSL, "called. option={} (forcing 0)", option);
|
||||
verify_option = 0;
|
||||
backend->SetVerifyOption(0);
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
Result SetIoModeImpl(u32 input_mode) {
|
||||
auto mode = static_cast<IoMode>(input_mode);
|
||||
ASSERT(mode == IoMode::Blocking || mode == IoMode::NonBlocking);
|
||||
ASSERT_OR_EXECUTE(socket, { return ResultNoSocket; });
|
||||
|
||||
const bool non_block = mode == IoMode::NonBlocking;
|
||||
const Network::Errno error = socket->SetNonBlock(non_block);
|
||||
if (error != Network::Errno::SUCCESS) {
|
||||
LOG_ERROR(Service_SSL, "Failed to set native socket non-block flag to {}", non_block);
|
||||
}
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
Result SetSessionCacheModeImpl(u32 mode) {
|
||||
ASSERT(!did_handshake);
|
||||
LOG_WARNING(Service_SSL, "(STUBBED) called. value={}", mode);
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
Result DoHandshakeImpl() {
|
||||
ASSERT_OR_EXECUTE(!did_handshake && socket, { return ResultNoSocket; });
|
||||
Result res = backend->DoHandshake();
|
||||
@@ -234,19 +166,17 @@ private:
|
||||
};
|
||||
if (!get_server_cert_chain) {
|
||||
// Just return the first one, unencoded.
|
||||
ASSERT_OR_EXECUTE_MSG(
|
||||
!certs.empty(), { return {}; }, "Should be at least one server cert");
|
||||
ASSERT_OR_EXECUTE_MSG(!certs.empty(), { return {}; }, "Should be at least one server cert");
|
||||
return certs[0];
|
||||
}
|
||||
std::vector<u8> ret;
|
||||
Header header{0x4E4D684374726543, static_cast<u32>(certs.size()), 0};
|
||||
Header header{0x4E4D684374726543, u32(certs.size()), 0};
|
||||
ret.insert(ret.end(), reinterpret_cast<u8*>(&header), reinterpret_cast<u8*>(&header + 1));
|
||||
size_t data_offset = sizeof(Header) + certs.size() * sizeof(EntryHeader);
|
||||
for (auto& cert : certs) {
|
||||
EntryHeader entry_header{static_cast<u32>(cert.size()), static_cast<u32>(data_offset)};
|
||||
EntryHeader entry_header{u32(cert.size()), u32(data_offset)};
|
||||
data_offset += cert.size();
|
||||
ret.insert(ret.end(), reinterpret_cast<u8*>(&entry_header),
|
||||
reinterpret_cast<u8*>(&entry_header + 1));
|
||||
ret.insert(ret.end(), reinterpret_cast<u8*>(&entry_header), reinterpret_cast<u8*>(&entry_header + 1));
|
||||
}
|
||||
for (auto& cert : certs) {
|
||||
ret.insert(ret.end(), cert.begin(), cert.end());
|
||||
@@ -254,65 +184,77 @@ private:
|
||||
return ret;
|
||||
}
|
||||
|
||||
Result ReadImpl(std::vector<u8>* out_data) {
|
||||
ASSERT_OR_EXECUTE(did_handshake, { return ResultInternalError; });
|
||||
size_t actual_size{};
|
||||
Result res = backend->Read(&actual_size, *out_data);
|
||||
if (res != ResultSuccess) {
|
||||
return res;
|
||||
Result SetSocketDescriptor(s32 in_fd, Out<s32> out_fd) {
|
||||
LOG_DEBUG(Service_SSL, "called, fd={}", in_fd);
|
||||
ASSERT(!did_handshake);
|
||||
auto bsd = system.ServiceManager().GetService<Service::Sockets::BSD_USA>("bsd:u");
|
||||
ASSERT_OR_EXECUTE(bsd, { return ResultInternalError; });
|
||||
|
||||
auto const res_v = bsd->DuplicateSocketImpl(in_fd);
|
||||
if (auto *res = std::get_if<s32>(&res_v)) {
|
||||
const s32 dup_fd = *res;
|
||||
*out_fd = do_not_close_socket ? dup_fd : -1;
|
||||
if (!do_not_close_socket)
|
||||
fd_to_close = dup_fd;
|
||||
auto const sock = bsd->GetSocket(dup_fd);
|
||||
if (!sock.has_value()) {
|
||||
LOG_ERROR(Service_SSL, "invalid socket fd {} after duplication", dup_fd);
|
||||
return ResultInvalidSocket;
|
||||
}
|
||||
socket = std::move(*sock);
|
||||
backend->SetSocket(std::move(socket));
|
||||
return ResultSuccess;
|
||||
}
|
||||
out_data->resize(actual_size);
|
||||
return res;
|
||||
LOG_ERROR(Service_SSL, "Failed to duplicate socket with fd {}", in_fd);
|
||||
return ResultInvalidSocket;
|
||||
}
|
||||
|
||||
Result WriteImpl(size_t* out_size, std::span<const u8> data) {
|
||||
ASSERT_OR_EXECUTE(did_handshake, { return ResultInternalError; });
|
||||
return backend->Write(out_size, data);
|
||||
Result SetHostName(InBuffer<BufferAttr_HipcMapAlias> buf) {
|
||||
auto const hostname = Common::StringFromBuffer(buf);
|
||||
LOG_DEBUG(Service_SSL, "called. hostname={}", hostname);
|
||||
ASSERT(!did_handshake);
|
||||
return backend->SetHostName(hostname.c_str());
|
||||
}
|
||||
|
||||
Result PendingImpl(s32* out_pending) {
|
||||
LOG_WARNING(Service_SSL, "(STUBBED) called.");
|
||||
*out_pending = 0;
|
||||
return ResultSuccess;
|
||||
Result SetVerifyOption(u32 option) {
|
||||
LOG_DEBUG(Service_SSL, "called. option={} (forcing 0)", option);
|
||||
ASSERT(!did_handshake);
|
||||
verify_option = 0;
|
||||
backend->SetVerifyOption(0);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
void SetSocketDescriptor(HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const s32 in_fd = rp.Pop<s32>();
|
||||
s32 out_fd{-1};
|
||||
const Result res = SetSocketDescriptorImpl(&out_fd, in_fd);
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(res);
|
||||
rb.Push<s32>(out_fd);
|
||||
Result SetIoMode(u32 input_mode) {
|
||||
auto mode = IoMode(input_mode);
|
||||
ASSERT(mode == IoMode::Blocking || mode == IoMode::NonBlocking);
|
||||
R_UNLESS(socket, ResultNoSocket);
|
||||
const bool non_block = mode == IoMode::NonBlocking;
|
||||
const Network::Errno error = socket->SetNonBlock(non_block);
|
||||
if (error != Network::Errno::SUCCESS) {
|
||||
LOG_ERROR(Service_SSL, "Failed to set native socket non-block flag to {}", non_block);
|
||||
}
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
void SetHostName(HLERequestContext& ctx) {
|
||||
const std::string hostname = Common::StringFromBuffer(ctx.ReadBuffer());
|
||||
const Result res = SetHostNameImpl(hostname);
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(res);
|
||||
Result GetSocketDescriptor(Out<u32> out_fd) {
|
||||
LOG_WARNING(Service_SSL, "(STUBBED)");
|
||||
*out_fd = uint32_t(socket->GetFD());
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
void SetVerifyOption(HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const u32 option = rp.Pop<u32>();
|
||||
const Result res = SetVerifyOptionImpl(option);
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(res);
|
||||
Result GetHostName(OutBuffer<BufferAttr_HipcMapAlias> data, Out<u32> out_size) {
|
||||
LOG_WARNING(Service_SSL, "(STUBBED)");
|
||||
ASSERT(!did_handshake);
|
||||
return backend->GetHostName(std::span<u8>{data.begin(), data.end()}, out_size);
|
||||
}
|
||||
|
||||
void SetIoMode(HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const u32 mode = rp.Pop<u32>();
|
||||
const Result res = SetIoModeImpl(mode);
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(res);
|
||||
Result GetIoMode(Out<u32> out_mode) {
|
||||
LOG_WARNING(Service_SSL, "(STUBBED)");
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
void DoHandshake(HLERequestContext& ctx) {
|
||||
const Result res = DoHandshakeImpl();
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(res);
|
||||
Result DoHandshake() {
|
||||
return DoHandshakeImpl();
|
||||
}
|
||||
|
||||
void DoHandshakeGetServerCert(HLERequestContext& ctx) {
|
||||
@@ -351,131 +293,158 @@ private:
|
||||
rb.PushRaw(out);
|
||||
}
|
||||
|
||||
void Read(HLERequestContext& ctx) {
|
||||
std::vector<u8> output_bytes(ctx.GetWriteBufferSize());
|
||||
const Result res = ReadImpl(&output_bytes);
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(res);
|
||||
if (res == ResultSuccess) {
|
||||
rb.Push(static_cast<u32>(output_bytes.size()));
|
||||
ctx.WriteBuffer(output_bytes);
|
||||
} else {
|
||||
rb.Push(static_cast<u32>(0));
|
||||
}
|
||||
Result Read(OutBuffer<BufferAttr_HipcMapAlias> data, Out<u32> out_size) {
|
||||
R_UNLESS(did_handshake, ResultInternalError);
|
||||
size_t tmp{};
|
||||
auto const res = backend->Read(&tmp, data);
|
||||
*out_size = u32(tmp);
|
||||
return res;
|
||||
}
|
||||
|
||||
void Write(HLERequestContext& ctx) {
|
||||
size_t write_size{0};
|
||||
const Result res = WriteImpl(&write_size, ctx.ReadBuffer());
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(res);
|
||||
rb.Push(static_cast<u32>(write_size));
|
||||
Result Write(InBuffer<BufferAttr_HipcMapAlias> data, Out<u32> out_size) {
|
||||
R_UNLESS(did_handshake, ResultInternalError);
|
||||
size_t tmp{};
|
||||
auto const res = backend->Write(&tmp, data);
|
||||
*out_size = u32(tmp);
|
||||
return res;
|
||||
}
|
||||
|
||||
void Pending(HLERequestContext& ctx) {
|
||||
s32 pending_size{0};
|
||||
const Result res = PendingImpl(&pending_size);
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(res);
|
||||
rb.Push<s32>(pending_size);
|
||||
Result Pending(Out<s32> out_pending_size) {
|
||||
LOG_WARNING(Service_SSL, "(STUBBED)");
|
||||
*out_pending_size = s32(backend->Pending());
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
void SetSessionCacheMode(HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const u32 mode = rp.Pop<u32>();
|
||||
const Result res = SetSessionCacheModeImpl(mode);
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(res);
|
||||
Result Peek(OutBuffer<BufferAttr_HipcMapAlias> data, Out<u32> out_size) {
|
||||
LOG_WARNING(Service_SSL, "(STUBBED)");
|
||||
size_t tmp{};
|
||||
auto const res = backend->Peek(&tmp, data);
|
||||
*out_size = u32(tmp);
|
||||
return res;
|
||||
}
|
||||
|
||||
void SetOption(HLERequestContext& ctx) {
|
||||
struct Parameters {
|
||||
OptionType option;
|
||||
s32 value;
|
||||
};
|
||||
static_assert(sizeof(Parameters) == 0x8, "Parameters is an invalid size");
|
||||
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto parameters = rp.PopRaw<Parameters>();
|
||||
|
||||
switch (parameters.option) {
|
||||
case OptionType::DoNotCloseSocket:
|
||||
do_not_close_socket = static_cast<bool>(parameters.value);
|
||||
break;
|
||||
case OptionType::GetServerCertChain:
|
||||
get_server_cert_chain = static_cast<bool>(parameters.value);
|
||||
break;
|
||||
case OptionType::SkipDefaultVerify:
|
||||
skip_default_verify = static_cast<bool>(parameters.value);
|
||||
break;
|
||||
case OptionType::EnableAlpn:
|
||||
enable_alpn = static_cast<bool>(parameters.value);
|
||||
break;
|
||||
default:
|
||||
LOG_WARNING(Service_SSL, "Unknown option={}, value={}", parameters.option,
|
||||
parameters.value);
|
||||
}
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ResultSuccess);
|
||||
Result Poll(u32 in_pollevent, u32 timer, Out<u32> out_pollevent) {
|
||||
LOG_WARNING(Service_SSL, "(STUBBED)");
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
void GetOption(HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto option = rp.PopRaw<OptionType>();
|
||||
Result GetVerifyCertError() {
|
||||
LOG_WARNING(Service_SSL, "(STUBBED)");
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
u8 value = 0;
|
||||
Result GetNeededServerCertBufferSize(Out<u32> out_needed_buffer_size) {
|
||||
LOG_WARNING(Service_SSL, "(STUBBED)");
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result SetSessionCacheMode(u32 mode) {
|
||||
LOG_WARNING(Service_SSL, "(STUBBED) called. value={}", mode);
|
||||
R_UNLESS(!did_handshake, ResultInternalError);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result GetSessionCacheMode(Out<u32> mode) {
|
||||
LOG_WARNING(Service_SSL, "(STUBBED)");
|
||||
R_UNLESS(!did_handshake, ResultInternalError);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result FlushSessionCache() {
|
||||
LOG_WARNING(Service_SSL, "(STUBBED)");
|
||||
R_UNLESS(!did_handshake, ResultInternalError);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result SetRenegotiationMode(RenegotiationMode mode) {
|
||||
LOG_WARNING(Service_SSL, "(STUBBED)");
|
||||
backend->SetRenegotiationMode(u32(mode));
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result GetRenegotiationMode(Out<RenegotiationMode> mode) {
|
||||
LOG_WARNING(Service_SSL, "(STUBBED)");
|
||||
u32 tmp{};
|
||||
auto const res = backend->GetRenegotiationMode(&tmp);
|
||||
*mode = RenegotiationMode(tmp);
|
||||
return res;
|
||||
}
|
||||
|
||||
Result SetOption(OptionType option, s32 value) {
|
||||
switch (option) {
|
||||
case OptionType::DoNotCloseSocket:
|
||||
value = static_cast<u8>(do_not_close_socket);
|
||||
do_not_close_socket = bool(value);
|
||||
break;
|
||||
case OptionType::GetServerCertChain:
|
||||
value = static_cast<u8>(get_server_cert_chain);
|
||||
get_server_cert_chain = bool(value);
|
||||
break;
|
||||
case OptionType::SkipDefaultVerify:
|
||||
value = static_cast<u8>(skip_default_verify);
|
||||
skip_default_verify = bool(value);
|
||||
break;
|
||||
case OptionType::EnableAlpn:
|
||||
value = static_cast<u8>(enable_alpn);
|
||||
enable_alpn = bool(value);
|
||||
break;
|
||||
default:
|
||||
LOG_WARNING(Service_SSL, "Unknown option={}", option);
|
||||
value = 0;
|
||||
LOG_WARNING(Service_SSL, "Unknown option={}, value={}", option, value);
|
||||
}
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result GetOption(OptionType option, Out<u8> value) {
|
||||
switch (option) {
|
||||
case OptionType::DoNotCloseSocket:
|
||||
*value = u8(do_not_close_socket);
|
||||
break;
|
||||
case OptionType::GetServerCertChain:
|
||||
*value = u8(get_server_cert_chain);
|
||||
break;
|
||||
case OptionType::SkipDefaultVerify:
|
||||
*value = u8(skip_default_verify);
|
||||
break;
|
||||
case OptionType::EnableAlpn:
|
||||
*value = u8(enable_alpn);
|
||||
break;
|
||||
default:
|
||||
LOG_WARNING(Service_SSL, "Unknown option={}", u32(option));
|
||||
*value = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
LOG_DEBUG(Service_SSL, "GetOption called, option={}, ret value={}", option, value);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.Push<u8>(value);
|
||||
LOG_DEBUG(Service_SSL, "GetOption called, option={}, ret value={}", u32(option), *value);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
void SetNextAlpnProto(HLERequestContext& ctx) {
|
||||
const auto data = ctx.ReadBuffer(0);
|
||||
next_alpn_proto.assign(data.begin(), data.end());
|
||||
|
||||
Result SetNextAlpnProto(InBuffer<BufferAttr_HipcMapAlias> data) {
|
||||
auto const to_write = u32((std::min)(next_alpn_proto.size(), data.size()));
|
||||
next_alpn_proto.assign(data.begin(), data.begin() + to_write);
|
||||
LOG_DEBUG(Service_SSL, "SetNextAlpnProto called, size={}", next_alpn_proto.size());
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ResultSuccess);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
void GetNextAlpnProto(HLERequestContext& ctx) {
|
||||
const size_t writable = ctx.GetWriteBufferSize();
|
||||
const size_t to_write = (std::min)(next_alpn_proto.size(), writable);
|
||||
|
||||
if (to_write != 0) {
|
||||
ctx.WriteBuffer(std::span<const u8>(next_alpn_proto.data(), to_write));
|
||||
}
|
||||
|
||||
LOG_DEBUG(Service_SSL, "GetNextAlpnProto called, size={}", to_write);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.Push<u32>(static_cast<u32>(to_write));
|
||||
Result GetNextAlpnProto(OutBuffer<BufferAttr_HipcMapAlias> data, Out<u32> to_write) {
|
||||
*to_write = u32((std::min)(next_alpn_proto.size(), data.size()));
|
||||
next_alpn_proto.assign(data.begin(), data.begin() + *to_write);
|
||||
LOG_DEBUG(Service_SSL, "GetNextAlpnProto called, size={}", *to_write);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result GetVerifyCertErrors(OutBuffer<BufferAttr_HipcMapAlias> unk0, Out<u32> unk1, Out<u32> unk2) {
|
||||
LOG_WARNING(Service_SSL, "(STUBBED)");
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
SslVersion ssl_version;
|
||||
std::shared_ptr<SslContextSharedData> shared_data;
|
||||
std::unique_ptr<SSLConnectionBackend> backend;
|
||||
std::optional<int> fd_to_close;
|
||||
std::shared_ptr<Network::SocketBase> socket;
|
||||
std::vector<u8> next_alpn_proto;
|
||||
u32 verify_option = 0;
|
||||
|
||||
bool do_not_close_socket = false;
|
||||
bool get_server_cert_chain = false;
|
||||
bool skip_default_verify = false;
|
||||
bool enable_alpn = false;
|
||||
bool did_handshake = false;
|
||||
};
|
||||
|
||||
class ISslContext final : public ServiceFramework<ISslContext> {
|
||||
@@ -492,7 +461,7 @@ public:
|
||||
{5, &ISslContext::ImportClientPki, "ImportClientPki"},
|
||||
{6, nullptr, "RemoveServerPki"},
|
||||
{7, nullptr, "RemoveClientPki"},
|
||||
{8, nullptr, "RegisterInternalPki"},
|
||||
{8, D<&ISslContext::RegisterInternalPki>, "RegisterInternalPki"},
|
||||
{9, nullptr, "AddPolicyOid"},
|
||||
{10, nullptr, "ImportCrl"},
|
||||
{11, nullptr, "RemoveCrl"},
|
||||
@@ -587,6 +556,11 @@ private:
|
||||
rb.Push(ResultSuccess);
|
||||
rb.Push(client_id);
|
||||
}
|
||||
|
||||
Result RegisterInternalPki() {
|
||||
LOG_WARNING(Service_SSL, "(STUBBED) called");
|
||||
R_SUCCEED();
|
||||
}
|
||||
};
|
||||
|
||||
class ISslService final : public ServiceFramework<ISslService> {
|
||||
|
||||
@@ -36,12 +36,17 @@ class SSLConnectionBackend {
|
||||
public:
|
||||
virtual ~SSLConnectionBackend() {}
|
||||
virtual void SetSocket(std::shared_ptr<Network::SocketBase> socket) = 0;
|
||||
virtual Result SetHostName(const std::string& hostname) = 0;
|
||||
virtual void SetVerifyOption(u32 option) = 0;
|
||||
virtual Result DoHandshake() = 0;
|
||||
virtual Result Read(size_t* out_size, std::span<u8> data) = 0;
|
||||
virtual Result Peek(size_t* out_size, std::span<u8> data) = 0;
|
||||
virtual Result Write(size_t* out_size, std::span<const u8> data) = 0;
|
||||
virtual Result GetServerCerts(std::vector<std::vector<u8>>* out_certs) = 0;
|
||||
virtual Result SetHostName(const char* hostname) = 0;
|
||||
virtual Result GetHostName(std::span<u8> hostname, u32* out_size) = 0;
|
||||
virtual int Pending() = 0;
|
||||
virtual Result SetRenegotiationMode(u32 mode) = 0;
|
||||
virtual Result GetRenegotiationMode(u32* mode) = 0;
|
||||
};
|
||||
|
||||
Result CreateSSLConnectionBackend(std::unique_ptr<SSLConnectionBackend>* out_backend);
|
||||
|
||||
@@ -157,18 +157,30 @@ public:
|
||||
socket = std::move(socket_in);
|
||||
}
|
||||
|
||||
Result SetHostName(const std::string& hostname) override {
|
||||
Result SetHostName(const char* hostname) override {
|
||||
if (!skip_cert_verification) {
|
||||
if (!SSL_set1_host(ssl, hostname.c_str())) {
|
||||
if (!SSL_set1_host(ssl, hostname)) {
|
||||
LOG_ERROR(Service_SSL, "SSL_set1_host({}) failed", hostname);
|
||||
return CheckOpenSSLErrors();
|
||||
}
|
||||
}
|
||||
if (!SSL_set_tlsext_host_name(ssl, hostname.c_str())) { // hostname for SNI
|
||||
if (!SSL_set_tlsext_host_name(ssl, hostname)) { // hostname for SNI
|
||||
LOG_ERROR(Service_SSL, "SSL_set_tlsext_host_name({}) failed", hostname);
|
||||
return CheckOpenSSLErrors();
|
||||
}
|
||||
return ResultSuccess;
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result GetHostName(std::span<u8> data, u32* out_size) override {
|
||||
auto const peer_name = SSL_get0_peername(ssl);
|
||||
if (peer_name == nullptr) {
|
||||
LOG_ERROR(Service_SSL, "SSL_get0_peername()");
|
||||
return CheckOpenSSLErrors();
|
||||
}
|
||||
auto const s = std::string{peer_name};
|
||||
*out_size = u32(s.size());
|
||||
std::memcpy(data.data(), s.data(), (std::min)(s.size(), data.size()));
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
void SetVerifyOption(u32 option) override {
|
||||
@@ -213,6 +225,13 @@ public:
|
||||
return HandleReturn("SSL_read_ex", out_size, ret);
|
||||
}
|
||||
|
||||
Result Peek(size_t* out_size, std::span<u8> data) override {
|
||||
auto const n = (std::min)(data.size(), *out_size);
|
||||
const int ret = SSL_peek(ssl, data.data(), int(n));
|
||||
*out_size = n;
|
||||
return HandleReturn("SSL_write_ex", out_size, ret);
|
||||
}
|
||||
|
||||
Result Write(size_t* out_size, std::span<const u8> data) override {
|
||||
const int ret = SSL_write_ex(ssl, data.data(), data.size(), out_size);
|
||||
return HandleReturn("SSL_write_ex", out_size, ret);
|
||||
@@ -263,7 +282,25 @@ public:
|
||||
out_certs->emplace_back(buf, buf + len);
|
||||
OPENSSL_free(buf);
|
||||
}
|
||||
return ResultSuccess;
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
int Pending() override {
|
||||
return SSL_pending(ssl);
|
||||
}
|
||||
|
||||
Result SetRenegotiationMode(u32 mode) override {
|
||||
if (mode == 0) {
|
||||
SSL_CTX_set_options(ssl_ctx, SSL_OP_NO_RENEGOTIATION);
|
||||
} else {
|
||||
SSL_CTX_set_options(ssl_ctx, SSL_OP_ALLOW_CLIENT_RENEGOTIATION);
|
||||
}
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result GetRenegotiationMode(u32* mode) override {
|
||||
*mode = SSL_get_secure_renegotiation_support(ssl) ? 1 : 0;
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
~SSLConnectionBackendOpenSSL() {
|
||||
|
||||
@@ -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;
|
||||
|
||||
}
|
||||
@@ -33,18 +33,10 @@ u64 MemoryReadWidth(Core::Memory::Memory& memory, u32 width, VAddr addr) {
|
||||
|
||||
void MemoryWriteWidth(Core::Memory::Memory& memory, u32 width, VAddr addr, u64 value) {
|
||||
switch (width) {
|
||||
case 1:
|
||||
memory.Write8(addr, static_cast<u8>(value));
|
||||
break;
|
||||
case 2:
|
||||
memory.Write16(addr, static_cast<u16>(value));
|
||||
break;
|
||||
case 4:
|
||||
memory.Write32(addr, static_cast<u32>(value));
|
||||
break;
|
||||
case 8:
|
||||
memory.Write64(addr, value);
|
||||
break;
|
||||
case sizeof(u64): return memory.Write64(addr, value);
|
||||
case sizeof(u32): return memory.Write32(addr, u32(value));
|
||||
case sizeof(u16): return memory.Write16(addr, u16(value));
|
||||
case sizeof(u8): return memory.Write8(addr, u8(value));
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
@@ -24,29 +24,65 @@
|
||||
namespace Dynarmic::Backend::Arm64 {
|
||||
|
||||
template<auto mfp, typename T>
|
||||
static void* EmitCallTrampoline(oaknut::CodeGenerator& code, T* this_) {
|
||||
static void* EmitCallReadTrampoline(oaknut::CodeGenerator& code, T* this_, size_t bitsize) {
|
||||
using namespace oaknut::util;
|
||||
|
||||
const auto info = Devirtualize<mfp>(this_);
|
||||
|
||||
oaknut::Label l_addr, l_this;
|
||||
|
||||
void* target = code.xptr<void*>();
|
||||
// params = { this, vaddr, bitsize }
|
||||
code.LDR(X0, l_this);
|
||||
// X1 = vaddr
|
||||
code.MOV(X2, bitsize);
|
||||
code.LDR(Xscratch0, l_addr);
|
||||
code.BR(Xscratch0);
|
||||
|
||||
code.align(8);
|
||||
code.l(l_this);
|
||||
code.dx(info.this_ptr);
|
||||
code.l(l_addr);
|
||||
code.dx(info.fn_ptr);
|
||||
|
||||
return target;
|
||||
}
|
||||
|
||||
template<auto mfp, typename T>
|
||||
static void* EmitWrappedReadCallTrampoline(oaknut::CodeGenerator& code, T* this_) {
|
||||
static void* EmitCallWriteTrampoline(oaknut::CodeGenerator& code, T* this_, size_t bitsize) {
|
||||
using namespace oaknut::util;
|
||||
const auto info = Devirtualize<mfp>(this_);
|
||||
oaknut::Label l_addr, l_this;
|
||||
void* target = code.xptr<void*>();
|
||||
// params = { this, vaddr, value, bitsize }
|
||||
code.LDR(X0, l_this);
|
||||
// X1 = vaddr
|
||||
// X2 = value
|
||||
code.MOV(X3, bitsize);
|
||||
code.LDR(Xscratch0, l_addr);
|
||||
code.BR(Xscratch0);
|
||||
code.align(8);
|
||||
code.l(l_this);
|
||||
code.dx(info.this_ptr);
|
||||
code.l(l_addr);
|
||||
code.dx(info.fn_ptr);
|
||||
return target;
|
||||
}
|
||||
|
||||
template<auto mfp, typename T>
|
||||
static void* EmitCallTrampoline(oaknut::CodeGenerator& code, T* this_) {
|
||||
using namespace oaknut::util;
|
||||
const auto info = Devirtualize<mfp>(this_);
|
||||
oaknut::Label l_addr, l_this;
|
||||
void* target = code.xptr<void*>();
|
||||
code.LDR(X0, l_this);
|
||||
code.LDR(Xscratch0, l_addr);
|
||||
code.BR(Xscratch0);
|
||||
code.align(8);
|
||||
code.l(l_this);
|
||||
code.dx(info.this_ptr);
|
||||
code.l(l_addr);
|
||||
code.dx(info.fn_ptr);
|
||||
return target;
|
||||
}
|
||||
|
||||
template<auto mfp, typename T>
|
||||
static void* EmitWrappedReadCallTrampoline(oaknut::CodeGenerator& code, T* this_, size_t bitsize) {
|
||||
using namespace oaknut::util;
|
||||
|
||||
const auto info = Devirtualize<mfp>(this_);
|
||||
@@ -59,6 +95,7 @@ static void* EmitWrappedReadCallTrampoline(oaknut::CodeGenerator& code, T* this_
|
||||
ABI_PushRegisters(code, save_regs, 0);
|
||||
code.LDR(X0, l_this);
|
||||
code.MOV(X1, Xscratch0);
|
||||
code.MOV(X2, bitsize);
|
||||
code.LDR(Xscratch0, l_addr);
|
||||
code.BLR(Xscratch0);
|
||||
code.MOV(Xscratch0, X0);
|
||||
@@ -82,7 +119,7 @@ static void* EmitExclusiveReadCallTrampoline(oaknut::CodeGenerator& code, const
|
||||
|
||||
auto fn = [](const A32::UserConfig& conf, A32::VAddr vaddr) -> T {
|
||||
return conf.global_monitor->ReadAndMark<T>(conf.processor_id, vaddr, [&]() -> T {
|
||||
return (conf.callbacks->*callback)(vaddr);
|
||||
return (conf.callbacks->*callback)(vaddr, sizeof(T));
|
||||
});
|
||||
};
|
||||
|
||||
@@ -101,7 +138,7 @@ static void* EmitExclusiveReadCallTrampoline(oaknut::CodeGenerator& code, const
|
||||
}
|
||||
|
||||
template<auto mfp, typename T>
|
||||
static void* EmitWrappedWriteCallTrampoline(oaknut::CodeGenerator& code, T* this_) {
|
||||
static void* EmitWrappedWriteCallTrampoline(oaknut::CodeGenerator& code, T* this_, size_t bitsize) {
|
||||
using namespace oaknut::util;
|
||||
|
||||
const auto info = Devirtualize<mfp>(this_);
|
||||
@@ -115,6 +152,7 @@ static void* EmitWrappedWriteCallTrampoline(oaknut::CodeGenerator& code, T* this
|
||||
code.LDR(X0, l_this);
|
||||
code.MOV(X1, Xscratch0);
|
||||
code.MOV(X2, Xscratch1);
|
||||
code.MOV(X3, bitsize);
|
||||
code.LDR(Xscratch0, l_addr);
|
||||
code.BLR(Xscratch0);
|
||||
ABI_PopRegisters(code, save_regs, 0);
|
||||
@@ -136,12 +174,9 @@ static void* EmitExclusiveWriteCallTrampoline(oaknut::CodeGenerator& code, const
|
||||
oaknut::Label l_addr, l_this;
|
||||
|
||||
auto fn = [](const A32::UserConfig& conf, A32::VAddr vaddr, T value) -> u32 {
|
||||
return conf.global_monitor->DoExclusiveOperation<T>(conf.processor_id, vaddr,
|
||||
[&](T expected) -> bool {
|
||||
return (conf.callbacks->*callback)(vaddr, value, expected);
|
||||
})
|
||||
? 0
|
||||
: 1;
|
||||
return conf.global_monitor->DoExclusiveOperation<T>(conf.processor_id, vaddr, [&](T expected) -> bool {
|
||||
return (conf.callbacks->*callback)(vaddr, value, expected);
|
||||
}) ? 0 : 1;
|
||||
};
|
||||
|
||||
void* target = code.xptr<void*>();
|
||||
@@ -179,26 +214,27 @@ void A32AddressSpace::EmitPrelude() {
|
||||
|
||||
UnprotectCodeMemory();
|
||||
|
||||
prelude_info.read_memory_8 = EmitCallTrampoline<&A32::UserCallbacks::MemoryRead8>(code, conf.callbacks);
|
||||
prelude_info.read_memory_16 = EmitCallTrampoline<&A32::UserCallbacks::MemoryRead16>(code, conf.callbacks);
|
||||
prelude_info.read_memory_32 = EmitCallTrampoline<&A32::UserCallbacks::MemoryRead32>(code, conf.callbacks);
|
||||
prelude_info.read_memory_64 = EmitCallTrampoline<&A32::UserCallbacks::MemoryRead64>(code, conf.callbacks);
|
||||
prelude_info.wrapped_read_memory_8 = EmitWrappedReadCallTrampoline<&A32::UserCallbacks::MemoryRead8>(code, conf.callbacks);
|
||||
prelude_info.wrapped_read_memory_16 = EmitWrappedReadCallTrampoline<&A32::UserCallbacks::MemoryRead16>(code, conf.callbacks);
|
||||
prelude_info.wrapped_read_memory_32 = EmitWrappedReadCallTrampoline<&A32::UserCallbacks::MemoryRead32>(code, conf.callbacks);
|
||||
prelude_info.wrapped_read_memory_64 = EmitWrappedReadCallTrampoline<&A32::UserCallbacks::MemoryRead64>(code, conf.callbacks);
|
||||
prelude_info.exclusive_read_memory_8 = EmitExclusiveReadCallTrampoline<&A32::UserCallbacks::MemoryRead8, u8>(code, conf);
|
||||
prelude_info.exclusive_read_memory_16 = EmitExclusiveReadCallTrampoline<&A32::UserCallbacks::MemoryRead16, u16>(code, conf);
|
||||
prelude_info.exclusive_read_memory_32 = EmitExclusiveReadCallTrampoline<&A32::UserCallbacks::MemoryRead32, u32>(code, conf);
|
||||
prelude_info.exclusive_read_memory_64 = EmitExclusiveReadCallTrampoline<&A32::UserCallbacks::MemoryRead64, u64>(code, conf);
|
||||
prelude_info.write_memory_8 = EmitCallTrampoline<&A32::UserCallbacks::MemoryWrite8>(code, conf.callbacks);
|
||||
prelude_info.write_memory_16 = EmitCallTrampoline<&A32::UserCallbacks::MemoryWrite16>(code, conf.callbacks);
|
||||
prelude_info.write_memory_32 = EmitCallTrampoline<&A32::UserCallbacks::MemoryWrite32>(code, conf.callbacks);
|
||||
prelude_info.write_memory_64 = EmitCallTrampoline<&A32::UserCallbacks::MemoryWrite64>(code, conf.callbacks);
|
||||
prelude_info.wrapped_write_memory_8 = EmitWrappedWriteCallTrampoline<&A32::UserCallbacks::MemoryWrite8>(code, conf.callbacks);
|
||||
prelude_info.wrapped_write_memory_16 = EmitWrappedWriteCallTrampoline<&A32::UserCallbacks::MemoryWrite16>(code, conf.callbacks);
|
||||
prelude_info.wrapped_write_memory_32 = EmitWrappedWriteCallTrampoline<&A32::UserCallbacks::MemoryWrite32>(code, conf.callbacks);
|
||||
prelude_info.wrapped_write_memory_64 = EmitWrappedWriteCallTrampoline<&A32::UserCallbacks::MemoryWrite64>(code, conf.callbacks);
|
||||
|
||||
prelude_info.read_memory_8 = EmitCallReadTrampoline<&A32::UserCallbacks::MemoryRead>(code, conf.callbacks, sizeof(u8));
|
||||
prelude_info.read_memory_16 = EmitCallReadTrampoline<&A32::UserCallbacks::MemoryRead>(code, conf.callbacks, sizeof(u16));
|
||||
prelude_info.read_memory_32 = EmitCallReadTrampoline<&A32::UserCallbacks::MemoryRead>(code, conf.callbacks, sizeof(u32));
|
||||
prelude_info.read_memory_64 = EmitCallReadTrampoline<&A32::UserCallbacks::MemoryRead>(code, conf.callbacks, sizeof(u64));
|
||||
prelude_info.wrapped_read_memory_8 = EmitWrappedReadCallTrampoline<&A32::UserCallbacks::MemoryRead>(code, conf.callbacks, sizeof(u8));
|
||||
prelude_info.wrapped_read_memory_16 = EmitWrappedReadCallTrampoline<&A32::UserCallbacks::MemoryRead>(code, conf.callbacks, sizeof(u16));
|
||||
prelude_info.wrapped_read_memory_32 = EmitWrappedReadCallTrampoline<&A32::UserCallbacks::MemoryRead>(code, conf.callbacks, sizeof(u32));
|
||||
prelude_info.wrapped_read_memory_64 = EmitWrappedReadCallTrampoline<&A32::UserCallbacks::MemoryRead>(code, conf.callbacks, sizeof(u64));
|
||||
prelude_info.exclusive_read_memory_8 = EmitExclusiveReadCallTrampoline<&A32::UserCallbacks::MemoryRead, u8>(code, conf);
|
||||
prelude_info.exclusive_read_memory_16 = EmitExclusiveReadCallTrampoline<&A32::UserCallbacks::MemoryRead, u16>(code, conf);
|
||||
prelude_info.exclusive_read_memory_32 = EmitExclusiveReadCallTrampoline<&A32::UserCallbacks::MemoryRead, u32>(code, conf);
|
||||
prelude_info.exclusive_read_memory_64 = EmitExclusiveReadCallTrampoline<&A32::UserCallbacks::MemoryRead, u64>(code, conf);
|
||||
prelude_info.write_memory_8 = EmitCallWriteTrampoline<&A32::UserCallbacks::MemoryWrite>(code, conf.callbacks, sizeof(u8));
|
||||
prelude_info.write_memory_16 = EmitCallWriteTrampoline<&A32::UserCallbacks::MemoryWrite>(code, conf.callbacks, sizeof(u16));
|
||||
prelude_info.write_memory_32 = EmitCallWriteTrampoline<&A32::UserCallbacks::MemoryWrite>(code, conf.callbacks, sizeof(u32));
|
||||
prelude_info.write_memory_64 = EmitCallWriteTrampoline<&A32::UserCallbacks::MemoryWrite>(code, conf.callbacks, sizeof(u64));
|
||||
prelude_info.wrapped_write_memory_8 = EmitWrappedWriteCallTrampoline<&A32::UserCallbacks::MemoryWrite>(code, conf.callbacks, sizeof(u8));
|
||||
prelude_info.wrapped_write_memory_16 = EmitWrappedWriteCallTrampoline<&A32::UserCallbacks::MemoryWrite>(code, conf.callbacks, sizeof(u16));
|
||||
prelude_info.wrapped_write_memory_32 = EmitWrappedWriteCallTrampoline<&A32::UserCallbacks::MemoryWrite>(code, conf.callbacks, sizeof(u32));
|
||||
prelude_info.wrapped_write_memory_64 = EmitWrappedWriteCallTrampoline<&A32::UserCallbacks::MemoryWrite>(code, conf.callbacks, sizeof(u64));
|
||||
prelude_info.exclusive_write_memory_8 = EmitExclusiveWriteCallTrampoline<&A32::UserCallbacks::MemoryWriteExclusive8, u8>(code, conf);
|
||||
prelude_info.exclusive_write_memory_16 = EmitExclusiveWriteCallTrampoline<&A32::UserCallbacks::MemoryWriteExclusive16, u16>(code, conf);
|
||||
prelude_info.exclusive_write_memory_32 = EmitExclusiveWriteCallTrampoline<&A32::UserCallbacks::MemoryWriteExclusive32, u32>(code, conf);
|
||||
|
||||
@@ -23,29 +23,65 @@
|
||||
namespace Dynarmic::Backend::Arm64 {
|
||||
|
||||
template<auto mfp, typename T>
|
||||
static void* EmitCallTrampoline(oaknut::CodeGenerator& code, T* this_) {
|
||||
static void* EmitCallReadTrampoline(oaknut::CodeGenerator& code, T* this_, size_t bitsize) {
|
||||
using namespace oaknut::util;
|
||||
|
||||
const auto info = Devirtualize<mfp>(this_);
|
||||
|
||||
oaknut::Label l_addr, l_this;
|
||||
|
||||
void* target = code.xptr<void*>();
|
||||
// params = { this, vaddr, bitsize }
|
||||
code.LDR(X0, l_this);
|
||||
// X1 = vaddr
|
||||
code.MOV(X2, bitsize);
|
||||
code.LDR(Xscratch0, l_addr);
|
||||
code.BR(Xscratch0);
|
||||
|
||||
code.align(8);
|
||||
code.l(l_this);
|
||||
code.dx(info.this_ptr);
|
||||
code.l(l_addr);
|
||||
code.dx(info.fn_ptr);
|
||||
|
||||
return target;
|
||||
}
|
||||
|
||||
template<auto mfp, typename T>
|
||||
static void* EmitWrappedReadCallTrampoline(oaknut::CodeGenerator& code, T* this_) {
|
||||
static void* EmitCallWriteTrampoline(oaknut::CodeGenerator& code, T* this_, size_t bitsize) {
|
||||
using namespace oaknut::util;
|
||||
const auto info = Devirtualize<mfp>(this_);
|
||||
oaknut::Label l_addr, l_this;
|
||||
void* target = code.xptr<void*>();
|
||||
// params = { this, vaddr, value, bitsize }
|
||||
code.LDR(X0, l_this);
|
||||
// X1 = vaddr
|
||||
// X2 = value
|
||||
code.MOV(X3, bitsize);
|
||||
code.LDR(Xscratch0, l_addr);
|
||||
code.BR(Xscratch0);
|
||||
code.align(8);
|
||||
code.l(l_this);
|
||||
code.dx(info.this_ptr);
|
||||
code.l(l_addr);
|
||||
code.dx(info.fn_ptr);
|
||||
return target;
|
||||
}
|
||||
|
||||
template<auto mfp, typename T>
|
||||
static void* EmitCallTrampoline(oaknut::CodeGenerator& code, T* this_) {
|
||||
using namespace oaknut::util;
|
||||
const auto info = Devirtualize<mfp>(this_);
|
||||
oaknut::Label l_addr, l_this;
|
||||
void* target = code.xptr<void*>();
|
||||
code.LDR(X0, l_this);
|
||||
code.LDR(Xscratch0, l_addr);
|
||||
code.BR(Xscratch0);
|
||||
code.align(8);
|
||||
code.l(l_this);
|
||||
code.dx(info.this_ptr);
|
||||
code.l(l_addr);
|
||||
code.dx(info.fn_ptr);
|
||||
return target;
|
||||
}
|
||||
|
||||
template<auto mfp, typename T>
|
||||
static void* EmitWrappedReadCallTrampoline(oaknut::CodeGenerator& code, T* this_, size_t bitsize) {
|
||||
using namespace oaknut::util;
|
||||
|
||||
const auto info = Devirtualize<mfp>(this_);
|
||||
@@ -58,6 +94,7 @@ static void* EmitWrappedReadCallTrampoline(oaknut::CodeGenerator& code, T* this_
|
||||
ABI_PushRegisters(code, save_regs, 0);
|
||||
code.LDR(X0, l_this);
|
||||
code.MOV(X1, Xscratch0);
|
||||
code.MOV(X2, bitsize);
|
||||
code.LDR(Xscratch0, l_addr);
|
||||
code.BLR(Xscratch0);
|
||||
code.MOV(Xscratch0, X0);
|
||||
@@ -81,7 +118,7 @@ static void* EmitExclusiveReadCallTrampoline(oaknut::CodeGenerator& code, const
|
||||
|
||||
auto fn = [](const A64::UserConfig& conf, A64::VAddr vaddr) -> T {
|
||||
return conf.global_monitor->ReadAndMark<T>(conf.processor_id, vaddr, [&]() -> T {
|
||||
return (conf.callbacks->*callback)(vaddr);
|
||||
return (conf.callbacks->*callback)(vaddr, sizeof(T));
|
||||
});
|
||||
};
|
||||
|
||||
@@ -100,7 +137,7 @@ static void* EmitExclusiveReadCallTrampoline(oaknut::CodeGenerator& code, const
|
||||
}
|
||||
|
||||
template<auto mfp, typename T>
|
||||
static void* EmitWrappedWriteCallTrampoline(oaknut::CodeGenerator& code, T* this_) {
|
||||
static void* EmitWrappedWriteCallTrampoline(oaknut::CodeGenerator& code, T* this_, size_t bitsize) {
|
||||
using namespace oaknut::util;
|
||||
|
||||
const auto info = Devirtualize<mfp>(this_);
|
||||
@@ -114,6 +151,7 @@ static void* EmitWrappedWriteCallTrampoline(oaknut::CodeGenerator& code, T* this
|
||||
code.LDR(X0, l_this);
|
||||
code.MOV(X1, Xscratch0);
|
||||
code.MOV(X2, Xscratch1);
|
||||
code.MOV(X3, bitsize);
|
||||
code.LDR(Xscratch0, l_addr);
|
||||
code.BLR(Xscratch0);
|
||||
ABI_PopRegisters(code, save_regs, 0);
|
||||
@@ -133,14 +171,10 @@ static void* EmitExclusiveWriteCallTrampoline(oaknut::CodeGenerator& code, const
|
||||
using namespace oaknut::util;
|
||||
|
||||
oaknut::Label l_addr, l_this;
|
||||
|
||||
auto fn = [](const A64::UserConfig& conf, A64::VAddr vaddr, T value) -> u32 {
|
||||
return conf.global_monitor->DoExclusiveOperation<T>(conf.processor_id, vaddr,
|
||||
[&](T expected) -> bool {
|
||||
return (conf.callbacks->*callback)(vaddr, value, expected);
|
||||
})
|
||||
? 0
|
||||
: 1;
|
||||
return conf.global_monitor->DoExclusiveOperation<T>(conf.processor_id, vaddr, [&](T expected) -> bool {
|
||||
return (conf.callbacks->*callback)(vaddr, value, expected);
|
||||
}) ? 0 : 1;
|
||||
};
|
||||
|
||||
void* target = code.xptr<void*>();
|
||||
@@ -346,30 +380,30 @@ void A64AddressSpace::EmitPrelude() {
|
||||
|
||||
UnprotectCodeMemory();
|
||||
|
||||
prelude_info.read_memory_8 = EmitCallTrampoline<&A64::UserCallbacks::MemoryRead8>(code, conf.callbacks);
|
||||
prelude_info.read_memory_16 = EmitCallTrampoline<&A64::UserCallbacks::MemoryRead16>(code, conf.callbacks);
|
||||
prelude_info.read_memory_32 = EmitCallTrampoline<&A64::UserCallbacks::MemoryRead32>(code, conf.callbacks);
|
||||
prelude_info.read_memory_64 = EmitCallTrampoline<&A64::UserCallbacks::MemoryRead64>(code, conf.callbacks);
|
||||
prelude_info.read_memory_8 = EmitCallReadTrampoline<&A64::UserCallbacks::MemoryRead>(code, conf.callbacks, sizeof(u8));
|
||||
prelude_info.read_memory_16 = EmitCallReadTrampoline<&A64::UserCallbacks::MemoryRead>(code, conf.callbacks, sizeof(u16));
|
||||
prelude_info.read_memory_32 = EmitCallReadTrampoline<&A64::UserCallbacks::MemoryRead>(code, conf.callbacks, sizeof(u32));
|
||||
prelude_info.read_memory_64 = EmitCallReadTrampoline<&A64::UserCallbacks::MemoryRead>(code, conf.callbacks, sizeof(u64));
|
||||
prelude_info.read_memory_128 = EmitRead128CallTrampoline(code, conf.callbacks);
|
||||
prelude_info.wrapped_read_memory_8 = EmitWrappedReadCallTrampoline<&A64::UserCallbacks::MemoryRead8>(code, conf.callbacks);
|
||||
prelude_info.wrapped_read_memory_16 = EmitWrappedReadCallTrampoline<&A64::UserCallbacks::MemoryRead16>(code, conf.callbacks);
|
||||
prelude_info.wrapped_read_memory_32 = EmitWrappedReadCallTrampoline<&A64::UserCallbacks::MemoryRead32>(code, conf.callbacks);
|
||||
prelude_info.wrapped_read_memory_64 = EmitWrappedReadCallTrampoline<&A64::UserCallbacks::MemoryRead64>(code, conf.callbacks);
|
||||
prelude_info.wrapped_read_memory_8 = EmitWrappedReadCallTrampoline<&A64::UserCallbacks::MemoryRead>(code, conf.callbacks, sizeof(u8));
|
||||
prelude_info.wrapped_read_memory_16 = EmitWrappedReadCallTrampoline<&A64::UserCallbacks::MemoryRead>(code, conf.callbacks, sizeof(u16));
|
||||
prelude_info.wrapped_read_memory_32 = EmitWrappedReadCallTrampoline<&A64::UserCallbacks::MemoryRead>(code, conf.callbacks, sizeof(u32));
|
||||
prelude_info.wrapped_read_memory_64 = EmitWrappedReadCallTrampoline<&A64::UserCallbacks::MemoryRead>(code, conf.callbacks, sizeof(u64));
|
||||
prelude_info.wrapped_read_memory_128 = EmitWrappedRead128CallTrampoline(code, conf.callbacks);
|
||||
prelude_info.exclusive_read_memory_8 = EmitExclusiveReadCallTrampoline<&A64::UserCallbacks::MemoryRead8, u8>(code, conf);
|
||||
prelude_info.exclusive_read_memory_16 = EmitExclusiveReadCallTrampoline<&A64::UserCallbacks::MemoryRead16, u16>(code, conf);
|
||||
prelude_info.exclusive_read_memory_32 = EmitExclusiveReadCallTrampoline<&A64::UserCallbacks::MemoryRead32, u32>(code, conf);
|
||||
prelude_info.exclusive_read_memory_64 = EmitExclusiveReadCallTrampoline<&A64::UserCallbacks::MemoryRead64, u64>(code, conf);
|
||||
prelude_info.exclusive_read_memory_8 = EmitExclusiveReadCallTrampoline<&A64::UserCallbacks::MemoryRead, u8>(code, conf);
|
||||
prelude_info.exclusive_read_memory_16 = EmitExclusiveReadCallTrampoline<&A64::UserCallbacks::MemoryRead, u16>(code, conf);
|
||||
prelude_info.exclusive_read_memory_32 = EmitExclusiveReadCallTrampoline<&A64::UserCallbacks::MemoryRead, u32>(code, conf);
|
||||
prelude_info.exclusive_read_memory_64 = EmitExclusiveReadCallTrampoline<&A64::UserCallbacks::MemoryRead, u64>(code, conf);
|
||||
prelude_info.exclusive_read_memory_128 = EmitExclusiveRead128CallTrampoline(code, conf);
|
||||
prelude_info.write_memory_8 = EmitCallTrampoline<&A64::UserCallbacks::MemoryWrite8>(code, conf.callbacks);
|
||||
prelude_info.write_memory_16 = EmitCallTrampoline<&A64::UserCallbacks::MemoryWrite16>(code, conf.callbacks);
|
||||
prelude_info.write_memory_32 = EmitCallTrampoline<&A64::UserCallbacks::MemoryWrite32>(code, conf.callbacks);
|
||||
prelude_info.write_memory_64 = EmitCallTrampoline<&A64::UserCallbacks::MemoryWrite64>(code, conf.callbacks);
|
||||
prelude_info.write_memory_8 = EmitCallWriteTrampoline<&A64::UserCallbacks::MemoryWrite>(code, conf.callbacks, sizeof(u8));
|
||||
prelude_info.write_memory_16 = EmitCallWriteTrampoline<&A64::UserCallbacks::MemoryWrite>(code, conf.callbacks, sizeof(u16));
|
||||
prelude_info.write_memory_32 = EmitCallWriteTrampoline<&A64::UserCallbacks::MemoryWrite>(code, conf.callbacks, sizeof(u32));
|
||||
prelude_info.write_memory_64 = EmitCallWriteTrampoline<&A64::UserCallbacks::MemoryWrite>(code, conf.callbacks, sizeof(u64));
|
||||
prelude_info.write_memory_128 = EmitWrite128CallTrampoline(code, conf.callbacks);
|
||||
prelude_info.wrapped_write_memory_8 = EmitWrappedWriteCallTrampoline<&A64::UserCallbacks::MemoryWrite8>(code, conf.callbacks);
|
||||
prelude_info.wrapped_write_memory_16 = EmitWrappedWriteCallTrampoline<&A64::UserCallbacks::MemoryWrite16>(code, conf.callbacks);
|
||||
prelude_info.wrapped_write_memory_32 = EmitWrappedWriteCallTrampoline<&A64::UserCallbacks::MemoryWrite32>(code, conf.callbacks);
|
||||
prelude_info.wrapped_write_memory_64 = EmitWrappedWriteCallTrampoline<&A64::UserCallbacks::MemoryWrite64>(code, conf.callbacks);
|
||||
prelude_info.wrapped_write_memory_8 = EmitWrappedWriteCallTrampoline<&A64::UserCallbacks::MemoryWrite>(code, conf.callbacks, sizeof(u8));
|
||||
prelude_info.wrapped_write_memory_16 = EmitWrappedWriteCallTrampoline<&A64::UserCallbacks::MemoryWrite>(code, conf.callbacks, sizeof(u16));
|
||||
prelude_info.wrapped_write_memory_32 = EmitWrappedWriteCallTrampoline<&A64::UserCallbacks::MemoryWrite>(code, conf.callbacks, sizeof(u32));
|
||||
prelude_info.wrapped_write_memory_64 = EmitWrappedWriteCallTrampoline<&A64::UserCallbacks::MemoryWrite>(code, conf.callbacks, sizeof(u64));
|
||||
prelude_info.wrapped_write_memory_128 = EmitWrappedWrite128CallTrampoline(code, conf.callbacks);
|
||||
prelude_info.exclusive_write_memory_8 = EmitExclusiveWriteCallTrampoline<&A64::UserCallbacks::MemoryWriteExclusive8, u8>(code, conf);
|
||||
prelude_info.exclusive_write_memory_16 = EmitExclusiveWriteCallTrampoline<&A64::UserCallbacks::MemoryWriteExclusive16, u16>(code, conf);
|
||||
|
||||
@@ -145,6 +145,7 @@ void AddressSpace::Link(EmittedBlockInfo& block_info) {
|
||||
case LinkTarget::ReturnFromRunCode:
|
||||
c.B(prelude_info.return_from_run_code);
|
||||
break;
|
||||
// { this, vaddr, size }
|
||||
case LinkTarget::ReadMemory8:
|
||||
c.BL(prelude_info.read_memory_8);
|
||||
break;
|
||||
@@ -160,6 +161,7 @@ void AddressSpace::Link(EmittedBlockInfo& block_info) {
|
||||
case LinkTarget::ReadMemory128:
|
||||
c.BL(prelude_info.read_memory_128);
|
||||
break;
|
||||
// { this, vaddr, size }
|
||||
case LinkTarget::WrappedReadMemory8:
|
||||
c.BL(prelude_info.wrapped_read_memory_8);
|
||||
break;
|
||||
@@ -175,6 +177,7 @@ void AddressSpace::Link(EmittedBlockInfo& block_info) {
|
||||
case LinkTarget::WrappedReadMemory128:
|
||||
c.BL(prelude_info.wrapped_read_memory_128);
|
||||
break;
|
||||
//
|
||||
case LinkTarget::ExclusiveReadMemory8:
|
||||
c.BL(prelude_info.exclusive_read_memory_8);
|
||||
break;
|
||||
@@ -190,6 +193,7 @@ void AddressSpace::Link(EmittedBlockInfo& block_info) {
|
||||
case LinkTarget::ExclusiveReadMemory128:
|
||||
c.BL(prelude_info.exclusive_read_memory_128);
|
||||
break;
|
||||
// { this, vaddr, value, size }
|
||||
case LinkTarget::WriteMemory8:
|
||||
c.BL(prelude_info.write_memory_8);
|
||||
break;
|
||||
@@ -205,6 +209,7 @@ void AddressSpace::Link(EmittedBlockInfo& block_info) {
|
||||
case LinkTarget::WriteMemory128:
|
||||
c.BL(prelude_info.write_memory_128);
|
||||
break;
|
||||
//
|
||||
case LinkTarget::WrappedWriteMemory8:
|
||||
c.BL(prelude_info.wrapped_write_memory_8);
|
||||
break;
|
||||
@@ -220,6 +225,7 @@ void AddressSpace::Link(EmittedBlockInfo& block_info) {
|
||||
case LinkTarget::WrappedWriteMemory128:
|
||||
c.BL(prelude_info.wrapped_write_memory_128);
|
||||
break;
|
||||
//
|
||||
case LinkTarget::ExclusiveWriteMemory8:
|
||||
c.BL(prelude_info.exclusive_write_memory_8);
|
||||
break;
|
||||
|
||||
@@ -31,16 +31,16 @@ using namespace Xbyak::util;
|
||||
void A32EmitX64::GenFastmemFallbacks() {
|
||||
const std::initializer_list<int> idxes{0, 1, 2, 3, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14};
|
||||
const std::array<std::pair<size_t, ArgCallback>, 4> read_callbacks{{
|
||||
{8, Devirtualize<&A32::UserCallbacks::MemoryRead8>(conf.callbacks)},
|
||||
{16, Devirtualize<&A32::UserCallbacks::MemoryRead16>(conf.callbacks)},
|
||||
{32, Devirtualize<&A32::UserCallbacks::MemoryRead32>(conf.callbacks)},
|
||||
{64, Devirtualize<&A32::UserCallbacks::MemoryRead64>(conf.callbacks)},
|
||||
{8, Devirtualize<&A32::UserCallbacks::MemoryRead>(conf.callbacks)},
|
||||
{16, Devirtualize<&A32::UserCallbacks::MemoryRead>(conf.callbacks)},
|
||||
{32, Devirtualize<&A32::UserCallbacks::MemoryRead>(conf.callbacks)},
|
||||
{64, Devirtualize<&A32::UserCallbacks::MemoryRead>(conf.callbacks)},
|
||||
}};
|
||||
const std::array<std::pair<size_t, ArgCallback>, 4> write_callbacks{{
|
||||
{8, Devirtualize<&A32::UserCallbacks::MemoryWrite8>(conf.callbacks)},
|
||||
{16, Devirtualize<&A32::UserCallbacks::MemoryWrite16>(conf.callbacks)},
|
||||
{32, Devirtualize<&A32::UserCallbacks::MemoryWrite32>(conf.callbacks)},
|
||||
{64, Devirtualize<&A32::UserCallbacks::MemoryWrite64>(conf.callbacks)},
|
||||
{8, Devirtualize<&A32::UserCallbacks::MemoryWrite>(conf.callbacks)},
|
||||
{16, Devirtualize<&A32::UserCallbacks::MemoryWrite>(conf.callbacks)},
|
||||
{32, Devirtualize<&A32::UserCallbacks::MemoryWrite>(conf.callbacks)},
|
||||
{64, Devirtualize<&A32::UserCallbacks::MemoryWrite>(conf.callbacks)},
|
||||
}};
|
||||
const std::array<std::pair<size_t, ArgCallback>, 4> exclusive_write_callbacks{{
|
||||
{8, Devirtualize<&A32::UserCallbacks::MemoryWriteExclusive8>(conf.callbacks)},
|
||||
@@ -56,12 +56,12 @@ void A32EmitX64::GenFastmemFallbacks() {
|
||||
code.align();
|
||||
read_fallbacks[std::make_tuple(ordered, bitsize, vaddr_idx, value_idx)] = code.getCurr<void (*)()>();
|
||||
ABI_PushCallerSaveRegistersAndAdjustStackExcept(code, HostLocRegIdx(value_idx));
|
||||
// params = { this, vaddr, size }
|
||||
if (vaddr_idx != code.ABI_PARAM2.getIdx()) {
|
||||
code.mov(code.ABI_PARAM2, Xbyak::Reg64{vaddr_idx});
|
||||
}
|
||||
if (ordered) {
|
||||
code.mfence();
|
||||
}
|
||||
code.mov(code.ABI_PARAM3, bitsize / CHAR_BIT);
|
||||
if (ordered) code.mfence();
|
||||
callback.EmitCall(code);
|
||||
if (value_idx != code.ABI_RETURN.getIdx()) {
|
||||
code.mov(Xbyak::Reg64{value_idx}, code.ABI_RETURN);
|
||||
@@ -76,6 +76,7 @@ void A32EmitX64::GenFastmemFallbacks() {
|
||||
code.align();
|
||||
write_fallbacks[std::make_tuple(ordered, bitsize, vaddr_idx, value_idx)] = code.getCurr<void (*)()>();
|
||||
ABI_PushCallerSaveRegistersAndAdjustStack(code);
|
||||
// params = { this, vaddr, value, size }
|
||||
if (vaddr_idx == code.ABI_PARAM3.getIdx() && value_idx == code.ABI_PARAM2.getIdx()) {
|
||||
code.xchg(code.ABI_PARAM2, code.ABI_PARAM3);
|
||||
} else if (vaddr_idx == code.ABI_PARAM3.getIdx()) {
|
||||
@@ -92,10 +93,9 @@ void A32EmitX64::GenFastmemFallbacks() {
|
||||
}
|
||||
}
|
||||
code.ZeroExtendFrom(bitsize, code.ABI_PARAM3);
|
||||
code.mov(code.ABI_PARAM4, bitsize / CHAR_BIT);
|
||||
callback.EmitCall(code);
|
||||
if (ordered) {
|
||||
code.mfence();
|
||||
}
|
||||
if (ordered) code.mfence();
|
||||
ABI_PopCallerSaveRegistersAndAdjustStack(code);
|
||||
code.ret();
|
||||
PerfMapRegister(write_fallbacks[std::make_tuple(ordered, bitsize, vaddr_idx, value_idx)], code.getCurr(), fmt::format("a32_write_fallback_{}", bitsize));
|
||||
@@ -138,35 +138,35 @@ void A32EmitX64::GenFastmemFallbacks() {
|
||||
#undef Axx
|
||||
|
||||
void A32EmitX64::EmitA32ReadMemory8(A32EmitContext& ctx, IR::Inst* inst) {
|
||||
EmitMemoryRead<8, &A32::UserCallbacks::MemoryRead8>(ctx, inst);
|
||||
EmitMemoryRead<8, &A32::UserCallbacks::MemoryRead>(ctx, inst);
|
||||
}
|
||||
|
||||
void A32EmitX64::EmitA32ReadMemory16(A32EmitContext& ctx, IR::Inst* inst) {
|
||||
EmitMemoryRead<16, &A32::UserCallbacks::MemoryRead16>(ctx, inst);
|
||||
EmitMemoryRead<16, &A32::UserCallbacks::MemoryRead>(ctx, inst);
|
||||
}
|
||||
|
||||
void A32EmitX64::EmitA32ReadMemory32(A32EmitContext& ctx, IR::Inst* inst) {
|
||||
EmitMemoryRead<32, &A32::UserCallbacks::MemoryRead32>(ctx, inst);
|
||||
EmitMemoryRead<32, &A32::UserCallbacks::MemoryRead>(ctx, inst);
|
||||
}
|
||||
|
||||
void A32EmitX64::EmitA32ReadMemory64(A32EmitContext& ctx, IR::Inst* inst) {
|
||||
EmitMemoryRead<64, &A32::UserCallbacks::MemoryRead64>(ctx, inst);
|
||||
EmitMemoryRead<64, &A32::UserCallbacks::MemoryRead>(ctx, inst);
|
||||
}
|
||||
|
||||
void A32EmitX64::EmitA32WriteMemory8(A32EmitContext& ctx, IR::Inst* inst) {
|
||||
EmitMemoryWrite<8, &A32::UserCallbacks::MemoryWrite8>(ctx, inst);
|
||||
EmitMemoryWrite<8, &A32::UserCallbacks::MemoryWrite>(ctx, inst);
|
||||
}
|
||||
|
||||
void A32EmitX64::EmitA32WriteMemory16(A32EmitContext& ctx, IR::Inst* inst) {
|
||||
EmitMemoryWrite<16, &A32::UserCallbacks::MemoryWrite16>(ctx, inst);
|
||||
EmitMemoryWrite<16, &A32::UserCallbacks::MemoryWrite>(ctx, inst);
|
||||
}
|
||||
|
||||
void A32EmitX64::EmitA32WriteMemory32(A32EmitContext& ctx, IR::Inst* inst) {
|
||||
EmitMemoryWrite<32, &A32::UserCallbacks::MemoryWrite32>(ctx, inst);
|
||||
EmitMemoryWrite<32, &A32::UserCallbacks::MemoryWrite>(ctx, inst);
|
||||
}
|
||||
|
||||
void A32EmitX64::EmitA32WriteMemory64(A32EmitContext& ctx, IR::Inst* inst) {
|
||||
EmitMemoryWrite<64, &A32::UserCallbacks::MemoryWrite64>(ctx, inst);
|
||||
EmitMemoryWrite<64, &A32::UserCallbacks::MemoryWrite>(ctx, inst);
|
||||
}
|
||||
|
||||
void A32EmitX64::EmitA32ClearExclusive(A32EmitContext&, IR::Inst*) {
|
||||
@@ -175,33 +175,33 @@ void A32EmitX64::EmitA32ClearExclusive(A32EmitContext&, IR::Inst*) {
|
||||
|
||||
void A32EmitX64::EmitA32ExclusiveReadMemory8(A32EmitContext& ctx, IR::Inst* inst) {
|
||||
if (conf.fastmem_exclusive_access) {
|
||||
EmitExclusiveReadMemoryInline<8, &A32::UserCallbacks::MemoryRead8>(ctx, inst);
|
||||
EmitExclusiveReadMemoryInline<8, &A32::UserCallbacks::MemoryRead>(ctx, inst);
|
||||
} else {
|
||||
EmitExclusiveReadMemory<8, &A32::UserCallbacks::MemoryRead8>(ctx, inst);
|
||||
EmitExclusiveReadMemory<8, &A32::UserCallbacks::MemoryRead>(ctx, inst);
|
||||
}
|
||||
}
|
||||
|
||||
void A32EmitX64::EmitA32ExclusiveReadMemory16(A32EmitContext& ctx, IR::Inst* inst) {
|
||||
if (conf.fastmem_exclusive_access) {
|
||||
EmitExclusiveReadMemoryInline<16, &A32::UserCallbacks::MemoryRead16>(ctx, inst);
|
||||
EmitExclusiveReadMemoryInline<16, &A32::UserCallbacks::MemoryRead>(ctx, inst);
|
||||
} else {
|
||||
EmitExclusiveReadMemory<16, &A32::UserCallbacks::MemoryRead16>(ctx, inst);
|
||||
EmitExclusiveReadMemory<16, &A32::UserCallbacks::MemoryRead>(ctx, inst);
|
||||
}
|
||||
}
|
||||
|
||||
void A32EmitX64::EmitA32ExclusiveReadMemory32(A32EmitContext& ctx, IR::Inst* inst) {
|
||||
if (conf.fastmem_exclusive_access) {
|
||||
EmitExclusiveReadMemoryInline<32, &A32::UserCallbacks::MemoryRead32>(ctx, inst);
|
||||
EmitExclusiveReadMemoryInline<32, &A32::UserCallbacks::MemoryRead>(ctx, inst);
|
||||
} else {
|
||||
EmitExclusiveReadMemory<32, &A32::UserCallbacks::MemoryRead32>(ctx, inst);
|
||||
EmitExclusiveReadMemory<32, &A32::UserCallbacks::MemoryRead>(ctx, inst);
|
||||
}
|
||||
}
|
||||
|
||||
void A32EmitX64::EmitA32ExclusiveReadMemory64(A32EmitContext& ctx, IR::Inst* inst) {
|
||||
if (conf.fastmem_exclusive_access) {
|
||||
EmitExclusiveReadMemoryInline<64, &A32::UserCallbacks::MemoryRead64>(ctx, inst);
|
||||
EmitExclusiveReadMemoryInline<64, &A32::UserCallbacks::MemoryRead>(ctx, inst);
|
||||
} else {
|
||||
EmitExclusiveReadMemory<64, &A32::UserCallbacks::MemoryRead64>(ctx, inst);
|
||||
EmitExclusiveReadMemory<64, &A32::UserCallbacks::MemoryRead>(ctx, inst);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -115,16 +115,16 @@ void A64EmitX64::GenMemory128Accessors() {
|
||||
void A64EmitX64::GenFastmemFallbacks() {
|
||||
const std::initializer_list<int> idxes{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15};
|
||||
const std::array<std::pair<size_t, ArgCallback>, 4> read_callbacks{{
|
||||
{8, Devirtualize<&A64::UserCallbacks::MemoryRead8>(conf.callbacks)},
|
||||
{16, Devirtualize<&A64::UserCallbacks::MemoryRead16>(conf.callbacks)},
|
||||
{32, Devirtualize<&A64::UserCallbacks::MemoryRead32>(conf.callbacks)},
|
||||
{64, Devirtualize<&A64::UserCallbacks::MemoryRead64>(conf.callbacks)},
|
||||
{8, Devirtualize<&A64::UserCallbacks::MemoryRead>(conf.callbacks)},
|
||||
{16, Devirtualize<&A64::UserCallbacks::MemoryRead>(conf.callbacks)},
|
||||
{32, Devirtualize<&A64::UserCallbacks::MemoryRead>(conf.callbacks)},
|
||||
{64, Devirtualize<&A64::UserCallbacks::MemoryRead>(conf.callbacks)},
|
||||
}};
|
||||
const std::array<std::pair<size_t, ArgCallback>, 4> write_callbacks{{
|
||||
{8, Devirtualize<&A64::UserCallbacks::MemoryWrite8>(conf.callbacks)},
|
||||
{16, Devirtualize<&A64::UserCallbacks::MemoryWrite16>(conf.callbacks)},
|
||||
{32, Devirtualize<&A64::UserCallbacks::MemoryWrite32>(conf.callbacks)},
|
||||
{64, Devirtualize<&A64::UserCallbacks::MemoryWrite64>(conf.callbacks)},
|
||||
{8, Devirtualize<&A64::UserCallbacks::MemoryWrite>(conf.callbacks)},
|
||||
{16, Devirtualize<&A64::UserCallbacks::MemoryWrite>(conf.callbacks)},
|
||||
{32, Devirtualize<&A64::UserCallbacks::MemoryWrite>(conf.callbacks)},
|
||||
{64, Devirtualize<&A64::UserCallbacks::MemoryWrite>(conf.callbacks)},
|
||||
}};
|
||||
const std::array<std::pair<size_t, ArgCallback>, 4> exclusive_write_callbacks{{
|
||||
{8, Devirtualize<&A64::UserCallbacks::MemoryWriteExclusive8>(conf.callbacks)},
|
||||
@@ -204,12 +204,12 @@ void A64EmitX64::GenFastmemFallbacks() {
|
||||
code.align();
|
||||
read_fallbacks[std::make_tuple(ordered, bitsize, vaddr_idx, value_idx)] = code.getCurr<void (*)()>();
|
||||
ABI_PushCallerSaveRegistersAndAdjustStackExcept(code, HostLocRegIdx(value_idx));
|
||||
// params = { this, vaddr, size }
|
||||
if (vaddr_idx != code.ABI_PARAM2.getIdx()) {
|
||||
code.mov(code.ABI_PARAM2, Xbyak::Reg64{vaddr_idx});
|
||||
}
|
||||
if (ordered) {
|
||||
code.mfence();
|
||||
}
|
||||
code.mov(code.ABI_PARAM3, bitsize / CHAR_BIT);
|
||||
if (ordered) code.mfence();
|
||||
callback.EmitCall(code);
|
||||
if (value_idx != code.ABI_RETURN.getIdx()) {
|
||||
code.mov(Xbyak::Reg64{value_idx}, code.ABI_RETURN);
|
||||
@@ -224,6 +224,7 @@ void A64EmitX64::GenFastmemFallbacks() {
|
||||
code.align();
|
||||
write_fallbacks[std::make_tuple(ordered, bitsize, vaddr_idx, value_idx)] = code.getCurr<void (*)()>();
|
||||
ABI_PushCallerSaveRegistersAndAdjustStack(code);
|
||||
// params = { this, vaddr, value, size }
|
||||
if (vaddr_idx == code.ABI_PARAM3.getIdx() && value_idx == code.ABI_PARAM2.getIdx()) {
|
||||
code.xchg(code.ABI_PARAM2, code.ABI_PARAM3);
|
||||
} else if (vaddr_idx == code.ABI_PARAM3.getIdx()) {
|
||||
@@ -240,10 +241,9 @@ void A64EmitX64::GenFastmemFallbacks() {
|
||||
}
|
||||
}
|
||||
code.ZeroExtendFrom(bitsize, code.ABI_PARAM3);
|
||||
code.mov(code.ABI_PARAM4, bitsize / CHAR_BIT);
|
||||
callback.EmitCall(code);
|
||||
if (ordered) {
|
||||
code.mfence();
|
||||
}
|
||||
if (ordered) code.mfence();
|
||||
ABI_PopCallerSaveRegistersAndAdjustStack(code);
|
||||
code.ret();
|
||||
PerfMapRegister(write_fallbacks[std::make_tuple(ordered, bitsize, vaddr_idx, value_idx)], code.getCurr(), fmt::format("a64_write_fallback_{}", bitsize));
|
||||
@@ -286,19 +286,19 @@ void A64EmitX64::GenFastmemFallbacks() {
|
||||
#undef Axx
|
||||
|
||||
void A64EmitX64::EmitA64ReadMemory8(A64EmitContext& ctx, IR::Inst* inst) {
|
||||
EmitMemoryRead<8, &A64::UserCallbacks::MemoryRead8>(ctx, inst);
|
||||
EmitMemoryRead<8, &A64::UserCallbacks::MemoryRead>(ctx, inst);
|
||||
}
|
||||
|
||||
void A64EmitX64::EmitA64ReadMemory16(A64EmitContext& ctx, IR::Inst* inst) {
|
||||
EmitMemoryRead<16, &A64::UserCallbacks::MemoryRead16>(ctx, inst);
|
||||
EmitMemoryRead<16, &A64::UserCallbacks::MemoryRead>(ctx, inst);
|
||||
}
|
||||
|
||||
void A64EmitX64::EmitA64ReadMemory32(A64EmitContext& ctx, IR::Inst* inst) {
|
||||
EmitMemoryRead<32, &A64::UserCallbacks::MemoryRead32>(ctx, inst);
|
||||
EmitMemoryRead<32, &A64::UserCallbacks::MemoryRead>(ctx, inst);
|
||||
}
|
||||
|
||||
void A64EmitX64::EmitA64ReadMemory64(A64EmitContext& ctx, IR::Inst* inst) {
|
||||
EmitMemoryRead<64, &A64::UserCallbacks::MemoryRead64>(ctx, inst);
|
||||
EmitMemoryRead<64, &A64::UserCallbacks::MemoryRead>(ctx, inst);
|
||||
}
|
||||
|
||||
void A64EmitX64::EmitA64ReadMemory128(A64EmitContext& ctx, IR::Inst* inst) {
|
||||
@@ -306,23 +306,23 @@ void A64EmitX64::EmitA64ReadMemory128(A64EmitContext& ctx, IR::Inst* inst) {
|
||||
}
|
||||
|
||||
void A64EmitX64::EmitA64WriteMemory8(A64EmitContext& ctx, IR::Inst* inst) {
|
||||
EmitMemoryWrite<8, &A64::UserCallbacks::MemoryWrite8>(ctx, inst);
|
||||
EmitMemoryWrite<8, &A64::UserCallbacks::MemoryWrite>(ctx, inst);
|
||||
}
|
||||
|
||||
void A64EmitX64::EmitA64WriteMemory16(A64EmitContext& ctx, IR::Inst* inst) {
|
||||
EmitMemoryWrite<16, &A64::UserCallbacks::MemoryWrite16>(ctx, inst);
|
||||
EmitMemoryWrite<16, &A64::UserCallbacks::MemoryWrite>(ctx, inst);
|
||||
}
|
||||
|
||||
void A64EmitX64::EmitA64WriteMemory32(A64EmitContext& ctx, IR::Inst* inst) {
|
||||
EmitMemoryWrite<32, &A64::UserCallbacks::MemoryWrite32>(ctx, inst);
|
||||
EmitMemoryWrite<32, &A64::UserCallbacks::MemoryWrite>(ctx, inst);
|
||||
}
|
||||
|
||||
void A64EmitX64::EmitA64WriteMemory64(A64EmitContext& ctx, IR::Inst* inst) {
|
||||
EmitMemoryWrite<64, &A64::UserCallbacks::MemoryWrite64>(ctx, inst);
|
||||
EmitMemoryWrite<64, &A64::UserCallbacks::MemoryWrite>(ctx, inst);
|
||||
}
|
||||
|
||||
void A64EmitX64::EmitA64WriteMemory128(A64EmitContext& ctx, IR::Inst* inst) {
|
||||
EmitMemoryWrite<128, &A64::UserCallbacks::MemoryWrite64>(ctx, inst);
|
||||
EmitMemoryWrite<128, &A64::UserCallbacks::MemoryWrite>(ctx, inst);
|
||||
}
|
||||
|
||||
void A64EmitX64::EmitA64ClearExclusive(A64EmitContext&, IR::Inst*) {
|
||||
@@ -331,33 +331,33 @@ void A64EmitX64::EmitA64ClearExclusive(A64EmitContext&, IR::Inst*) {
|
||||
|
||||
void A64EmitX64::EmitA64ExclusiveReadMemory8(A64EmitContext& ctx, IR::Inst* inst) {
|
||||
if (conf.fastmem_exclusive_access) {
|
||||
EmitExclusiveReadMemoryInline<8, &A64::UserCallbacks::MemoryRead8>(ctx, inst);
|
||||
EmitExclusiveReadMemoryInline<8, &A64::UserCallbacks::MemoryRead>(ctx, inst);
|
||||
} else {
|
||||
EmitExclusiveReadMemory<8, &A64::UserCallbacks::MemoryRead8>(ctx, inst);
|
||||
EmitExclusiveReadMemory<8, &A64::UserCallbacks::MemoryRead>(ctx, inst);
|
||||
}
|
||||
}
|
||||
|
||||
void A64EmitX64::EmitA64ExclusiveReadMemory16(A64EmitContext& ctx, IR::Inst* inst) {
|
||||
if (conf.fastmem_exclusive_access) {
|
||||
EmitExclusiveReadMemoryInline<16, &A64::UserCallbacks::MemoryRead16>(ctx, inst);
|
||||
EmitExclusiveReadMemoryInline<16, &A64::UserCallbacks::MemoryRead>(ctx, inst);
|
||||
} else {
|
||||
EmitExclusiveReadMemory<16, &A64::UserCallbacks::MemoryRead16>(ctx, inst);
|
||||
EmitExclusiveReadMemory<16, &A64::UserCallbacks::MemoryRead>(ctx, inst);
|
||||
}
|
||||
}
|
||||
|
||||
void A64EmitX64::EmitA64ExclusiveReadMemory32(A64EmitContext& ctx, IR::Inst* inst) {
|
||||
if (conf.fastmem_exclusive_access) {
|
||||
EmitExclusiveReadMemoryInline<32, &A64::UserCallbacks::MemoryRead32>(ctx, inst);
|
||||
EmitExclusiveReadMemoryInline<32, &A64::UserCallbacks::MemoryRead>(ctx, inst);
|
||||
} else {
|
||||
EmitExclusiveReadMemory<32, &A64::UserCallbacks::MemoryRead32>(ctx, inst);
|
||||
EmitExclusiveReadMemory<32, &A64::UserCallbacks::MemoryRead>(ctx, inst);
|
||||
}
|
||||
}
|
||||
|
||||
void A64EmitX64::EmitA64ExclusiveReadMemory64(A64EmitContext& ctx, IR::Inst* inst) {
|
||||
if (conf.fastmem_exclusive_access) {
|
||||
EmitExclusiveReadMemoryInline<64, &A64::UserCallbacks::MemoryRead64>(ctx, inst);
|
||||
EmitExclusiveReadMemoryInline<64, &A64::UserCallbacks::MemoryRead>(ctx, inst);
|
||||
} else {
|
||||
EmitExclusiveReadMemory<64, &A64::UserCallbacks::MemoryRead64>(ctx, inst);
|
||||
EmitExclusiveReadMemory<64, &A64::UserCallbacks::MemoryRead>(ctx, inst);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
/* This file is part of the dynarmic project.
|
||||
* Copyright (c) 2022 MerryMage
|
||||
* SPDX-License-Identifier: 0BSD
|
||||
@@ -56,16 +59,13 @@ void AxxEmitX64::EmitMemoryRead(AxxEmitContext& ctx, IR::Inst* inst) {
|
||||
// Neither fastmem nor page table: Use callbacks
|
||||
if constexpr (bitsize == 128) {
|
||||
ctx.reg_alloc.HostCall(code, nullptr, {}, args[1]);
|
||||
if (ordered) {
|
||||
code.mfence();
|
||||
}
|
||||
if (ordered) code.mfence();
|
||||
code.CallFunction(memory_read_128);
|
||||
ctx.reg_alloc.DefineValue(code, inst, xmm1);
|
||||
} else {
|
||||
ctx.reg_alloc.HostCall(code, inst, {}, args[1]);
|
||||
if (ordered) {
|
||||
code.mfence();
|
||||
}
|
||||
code.mov(code.ABI_PARAM3.cvt32(), bitsize / CHAR_BIT);
|
||||
if (ordered) code.mfence();
|
||||
Devirtualize<callback>(conf.callbacks).EmitCall(code);
|
||||
code.ZeroExtendFrom(bitsize, code.ABI_RETURN);
|
||||
}
|
||||
@@ -148,12 +148,12 @@ void AxxEmitX64::EmitMemoryWrite(AxxEmitContext& ctx, IR::Inst* inst) {
|
||||
ctx.reg_alloc.HostCall(code, nullptr);
|
||||
code.CallFunction(memory_write_128);
|
||||
} else {
|
||||
// { this, vaddr, value, size }
|
||||
ctx.reg_alloc.HostCall(code, nullptr, {}, args[1], args[2]);
|
||||
code.mov(code.ABI_PARAM4.cvt32(), bitsize / CHAR_BIT);
|
||||
Devirtualize<callback>(conf.callbacks).EmitCall(code);
|
||||
}
|
||||
if (ordered) {
|
||||
code.mfence();
|
||||
}
|
||||
if (ordered) code.mfence();
|
||||
EmitCheckMemoryAbort(ctx, inst);
|
||||
return;
|
||||
}
|
||||
@@ -230,12 +230,11 @@ void AxxEmitX64::EmitExclusiveReadMemory(AxxEmitContext& ctx, IR::Inst* inst) {
|
||||
if (ordered) {
|
||||
code.mfence();
|
||||
}
|
||||
code.CallLambda(
|
||||
[](AxxUserConfig& conf, Axx::VAddr vaddr) -> T {
|
||||
return conf.global_monitor->ReadAndMark<T>(conf.processor_id, vaddr, [&]() -> T {
|
||||
return (conf.callbacks->*callback)(vaddr);
|
||||
});
|
||||
code.CallLambda([](AxxUserConfig& conf, Axx::VAddr vaddr) -> T {
|
||||
return conf.global_monitor->ReadAndMark<T>(conf.processor_id, vaddr, [&]() -> T {
|
||||
return (conf.callbacks->*callback)(vaddr, bitsize / CHAR_BIT);
|
||||
});
|
||||
});
|
||||
code.ZeroExtendFrom(bitsize, code.ABI_RETURN);
|
||||
} else {
|
||||
const Xbyak::Xmm result = ctx.reg_alloc.ScratchXmm(code);
|
||||
@@ -250,12 +249,11 @@ void AxxEmitX64::EmitExclusiveReadMemory(AxxEmitContext& ctx, IR::Inst* inst) {
|
||||
if (ordered) {
|
||||
code.mfence();
|
||||
}
|
||||
code.CallLambda(
|
||||
[](AxxUserConfig& conf, Axx::VAddr vaddr, Vector& ret) {
|
||||
ret = conf.global_monitor->ReadAndMark<Vector>(conf.processor_id, vaddr, [&]() -> Vector {
|
||||
return (conf.callbacks->*callback)(vaddr);
|
||||
});
|
||||
code.CallLambda([](AxxUserConfig& conf, Axx::VAddr vaddr, Vector& ret) {
|
||||
ret = conf.global_monitor->ReadAndMark<Vector>(conf.processor_id, vaddr, [&]() -> Vector {
|
||||
return (conf.callbacks->*callback)(vaddr);
|
||||
});
|
||||
});
|
||||
code.movups(result, xword[rsp + ABI_SHADOW_SPACE]);
|
||||
ctx.reg_alloc.ReleaseStackSpace(code, 16 + ABI_SHADOW_SPACE);
|
||||
|
||||
|
||||
@@ -90,7 +90,7 @@ template<>
|
||||
code.shl(tmp, int(ctx.conf.page_table_log2_stride));
|
||||
code.mov(page, qword[r14 + tmp.cvt64()]);
|
||||
} else {
|
||||
code.mov(page, qword[r14 + tmp.cvt64() * int(ctx.conf.page_table_log2_stride)]);
|
||||
code.mov(page, qword[r14 + tmp.cvt64() * int(1 << ctx.conf.page_table_log2_stride)]);
|
||||
}
|
||||
|
||||
// check for marked bit, use as unmapped if marked
|
||||
@@ -161,8 +161,12 @@ template<>
|
||||
code.jnz(abort, code.T_NEAR);
|
||||
}
|
||||
|
||||
code.shl(tmp, int(ctx.conf.page_table_log2_stride));
|
||||
code.mov(page, qword[r14 + tmp]);
|
||||
if (ctx.conf.page_table_log2_stride > 3) {
|
||||
code.shl(tmp, int(ctx.conf.page_table_log2_stride));
|
||||
code.mov(page, qword[r14 + tmp.cvt64()]);
|
||||
} else {
|
||||
code.mov(page, qword[r14 + tmp.cvt64() * int(1 << ctx.conf.page_table_log2_stride)]);
|
||||
}
|
||||
|
||||
// check for marked bit, use as unmapped if marked
|
||||
if (ctx.conf.page_table_marked_bit) {
|
||||
@@ -178,6 +182,7 @@ template<>
|
||||
code.mov(tmp, ctx.conf.page_table_pointer_mask);
|
||||
code.and_(page, tmp);
|
||||
}
|
||||
// check for sign bit, apply sign extension as needed
|
||||
if (ctx.conf.page_table_sign_extension) {
|
||||
code.shl(page, *ctx.conf.page_table_sign_extension);
|
||||
code.sar(page, *ctx.conf.page_table_sign_extension);
|
||||
|
||||
@@ -66,7 +66,9 @@ struct UserCallbacks : public TranslateCallbacks {
|
||||
|
||||
// All reads through this callback are 4-byte aligned.
|
||||
// Memory must be interpreted as little endian.
|
||||
std::optional<std::uint32_t> MemoryReadCode(VAddr vaddr) override { return MemoryRead32(vaddr); }
|
||||
std::optional<std::uint32_t> MemoryReadCode(VAddr vaddr) override {
|
||||
return std::uint32_t(MemoryRead(vaddr, sizeof(std::uint32_t)));
|
||||
}
|
||||
|
||||
// This function is called before the instruction at pc is read.
|
||||
// IR code can be emitted by the callee prior to instruction handling.
|
||||
@@ -80,16 +82,10 @@ struct UserCallbacks : public TranslateCallbacks {
|
||||
|
||||
// Reads through these callbacks may not be aligned.
|
||||
// Memory must be interpreted as if ENDIANSTATE == 0, endianness will be corrected by the JIT.
|
||||
virtual std::uint8_t MemoryRead8(VAddr vaddr) = 0;
|
||||
virtual std::uint16_t MemoryRead16(VAddr vaddr) = 0;
|
||||
virtual std::uint32_t MemoryRead32(VAddr vaddr) = 0;
|
||||
virtual std::uint64_t MemoryRead64(VAddr vaddr) = 0;
|
||||
virtual std::uint64_t MemoryRead(VAddr vaddr, std::size_t size) = 0;
|
||||
|
||||
// Writes through these callbacks may not be aligned.
|
||||
virtual void MemoryWrite8(VAddr vaddr, std::uint8_t value) = 0;
|
||||
virtual void MemoryWrite16(VAddr vaddr, std::uint16_t value) = 0;
|
||||
virtual void MemoryWrite32(VAddr vaddr, std::uint32_t value) = 0;
|
||||
virtual void MemoryWrite64(VAddr vaddr, std::uint64_t value) = 0;
|
||||
virtual void MemoryWrite(VAddr vaddr, std::uint64_t value, std::size_t size) = 0;
|
||||
|
||||
// Writes through these callbacks may not be aligned.
|
||||
virtual bool MemoryWriteExclusive8(VAddr /*vaddr*/, std::uint8_t /*value*/, std::uint8_t /*expected*/) { return false; }
|
||||
|
||||
@@ -89,20 +89,16 @@ struct UserCallbacks {
|
||||
|
||||
// All reads through this callback are 4-byte aligned.
|
||||
// Memory must be interpreted as little endian.
|
||||
virtual std::optional<std::uint32_t> MemoryReadCode(VAddr vaddr) { return MemoryRead32(vaddr); }
|
||||
virtual std::optional<std::uint32_t> MemoryReadCode(VAddr vaddr) {
|
||||
return std::uint32_t(MemoryRead(vaddr, sizeof(std::uint32_t)));
|
||||
}
|
||||
|
||||
// Reads through these callbacks may not be aligned.
|
||||
virtual std::uint8_t MemoryRead8(VAddr vaddr) = 0;
|
||||
virtual std::uint16_t MemoryRead16(VAddr vaddr) = 0;
|
||||
virtual std::uint32_t MemoryRead32(VAddr vaddr) = 0;
|
||||
virtual std::uint64_t MemoryRead64(VAddr vaddr) = 0;
|
||||
virtual std::uint64_t MemoryRead(VAddr vaddr, std::size_t size) = 0;
|
||||
virtual Vector MemoryRead128(VAddr vaddr) = 0;
|
||||
|
||||
// Writes through these callbacks may not be aligned.
|
||||
virtual void MemoryWrite8(VAddr vaddr, std::uint8_t value) = 0;
|
||||
virtual void MemoryWrite16(VAddr vaddr, std::uint16_t value) = 0;
|
||||
virtual void MemoryWrite32(VAddr vaddr, std::uint32_t value) = 0;
|
||||
virtual void MemoryWrite64(VAddr vaddr, std::uint64_t value) = 0;
|
||||
virtual void MemoryWrite(VAddr vaddr, std::uint64_t value, std::size_t size) = 0;
|
||||
virtual void MemoryWrite128(VAddr vaddr, Vector value) = 0;
|
||||
|
||||
// Writes through these callbacks may not be aligned.
|
||||
|
||||
@@ -40,7 +40,7 @@ static void ConstantMemoryReads(IR::Block& block, A32::UserCallbacks* cb) {
|
||||
if (inst.AreAllArgsImmediates()) {
|
||||
const u32 vaddr = inst.GetArg(1).GetU32();
|
||||
if (cb->IsReadOnlyMemory(vaddr)) {
|
||||
const u8 value_from_memory = cb->MemoryRead8(vaddr);
|
||||
const u8 value_from_memory = u8(cb->MemoryRead(vaddr, sizeof(u8)));
|
||||
inst.ReplaceUsesWith(IR::Value{value_from_memory});
|
||||
}
|
||||
}
|
||||
@@ -51,7 +51,7 @@ static void ConstantMemoryReads(IR::Block& block, A32::UserCallbacks* cb) {
|
||||
if (inst.AreAllArgsImmediates()) {
|
||||
const u32 vaddr = inst.GetArg(1).GetU32();
|
||||
if (cb->IsReadOnlyMemory(vaddr)) {
|
||||
const u16 value_from_memory = cb->MemoryRead16(vaddr);
|
||||
const u16 value_from_memory = u16(cb->MemoryRead(vaddr, sizeof(u16)));
|
||||
inst.ReplaceUsesWith(IR::Value{value_from_memory});
|
||||
}
|
||||
}
|
||||
@@ -62,7 +62,7 @@ static void ConstantMemoryReads(IR::Block& block, A32::UserCallbacks* cb) {
|
||||
if (inst.AreAllArgsImmediates()) {
|
||||
const u32 vaddr = inst.GetArg(1).GetU32();
|
||||
if (cb->IsReadOnlyMemory(vaddr)) {
|
||||
const u32 value_from_memory = cb->MemoryRead32(vaddr);
|
||||
const u32 value_from_memory = u32(cb->MemoryRead(vaddr, sizeof(u32)));
|
||||
inst.ReplaceUsesWith(IR::Value{value_from_memory});
|
||||
}
|
||||
}
|
||||
@@ -73,7 +73,7 @@ static void ConstantMemoryReads(IR::Block& block, A32::UserCallbacks* cb) {
|
||||
if (inst.AreAllArgsImmediates()) {
|
||||
const u32 vaddr = inst.GetArg(1).GetU32();
|
||||
if (cb->IsReadOnlyMemory(vaddr)) {
|
||||
const u64 value_from_memory = cb->MemoryRead64(vaddr);
|
||||
const u64 value_from_memory = u64(cb->MemoryRead(vaddr, sizeof(u64)));
|
||||
inst.ReplaceUsesWith(IR::Value{value_from_memory});
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user