mirror of
https://git.eden-emu.dev/eden-emu/eden.git
synced 2026-09-24 10:36:55 +00:00
Compare commits
35 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 1fe88221d1 | |||
| e9bfd97c7f | |||
| 773c29ed85 | |||
| 09f05f0313 | |||
| 3b03ade8d8 | |||
| 78cacac308 | |||
| 84f43f2c63 | |||
| 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
@@ -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,
|
jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_doesUpdateMatchProgram(JNIEnv* env, jobject jobj,
|
||||||
jstring jprogramId,
|
jstring jprogramId,
|
||||||
jstring jupdatePath) {
|
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::string updatePath = Common::Android::GetJString(env, jupdatePath);
|
||||||
std::shared_ptr<FileSys::NSP> nsp = std::make_shared<FileSys::NSP>(
|
std::shared_ptr<FileSys::NSP> nsp = std::make_shared<FileSys::NSP>(
|
||||||
EmulationSession::GetInstance().System().GetFilesystem()->OpenFile(
|
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& item : nsp->GetNCAs()) {
|
||||||
for (const auto& nca_details : item.second) {
|
for (const auto& nca_details : item.second) {
|
||||||
if (nca_details.second->GetName().ends_with(".cnmt.nca")) {
|
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) {
|
if (update_id == program_id) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -289,6 +289,7 @@
|
|||||||
<string name="gpu_driver_fetcher">Obtenedor de controladores de la GPU</string>
|
<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="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="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_open_list">Abrir lista</string>
|
||||||
<string name="post_processing_close_list">Cerrar lista</string>
|
<string name="post_processing_close_list">Cerrar lista</string>
|
||||||
<string name="post_processing_reset">Restablecer a predeterminado</string>
|
<string name="post_processing_reset">Restablecer a predeterminado</string>
|
||||||
|
|||||||
@@ -318,6 +318,13 @@
|
|||||||
<string name="frame_gen_target_rate">Целевая частота кадров</string>
|
<string name="frame_gen_target_rate">Целевая частота кадров</string>
|
||||||
<string name="frame_gen_target_rate_description">Выберите значение под ваш дисплей. Множитель подстроится сам, чтобы его держать, и откатит изменения, если игра начнёт тормозить.</string>
|
<string name="frame_gen_target_rate_description">Выберите значение под ваш дисплей. Множитель подстроится сам, чтобы его держать, и откатит изменения, если игра начнёт тормозить.</string>
|
||||||
<string name="frame_gen_target_rate_off">Использовать фиксированный множитель</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">Целевой размер очереди кадров</string>
|
||||||
<string name="frame_gen_queue_target_description">Сколько готовых кадров может ожидать перед выводом на экран. Более длинные очереди сглаживают скачки ГПУ ценой задержки ввода.</string>
|
<string name="frame_gen_queue_target_description">Сколько готовых кадров может ожидать перед выводом на экран. Более длинные очереди сглаживают скачки ГПУ ценой задержки ввода.</string>
|
||||||
<string name="frame_gen_queue_target_0">Минимальная задержка (Без буферизации)</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">Разрешение оценки движения</string>
|
||||||
<string name="frame_gen_flow_scale_description">Разрешение прохода оптического потока в долях от выходного разрешения. Его понижение — самый дешёвый способ вернуть производительность.</string>
|
<string name="frame_gen_flow_scale_description">Разрешение прохода оптического потока в долях от выходного разрешения. Его понижение — самый дешёвый способ вернуть производительность.</string>
|
||||||
<string name="frame_gen_unsupported">Генерация кадров недоступна</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_setup_description">Опционально. Укажите свой Lossless.dll для включения генерации кадров позже.</string>
|
||||||
<string name="lossless_scaling_install">Установить 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_install_description">Для генерации кадров требуется ваша собственная легальная копия Lossless.dll из Lossless Scaling</string>
|
||||||
<string name="lossless_scaling_replace_description">Выбрать другой файл Lossless.dll</string>
|
<string name="lossless_scaling_replace_description">Выбрать другой файл Lossless.dll</string>
|
||||||
<string name="frame_generation_support">Генерация кадров</string>
|
<string name="frame_generation_support">Генерация кадров</string>
|
||||||
<string name="frame_generation_supported">Поддерживается</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_description">Предоставьте свою копию Lossless.dll для включения генерации кадров</string>
|
||||||
<string name="lossless_scaling_installed">Установлена</string>
|
<string name="lossless_scaling_installed">Установлена</string>
|
||||||
<string name="lossless_scaling_not_installed">Не установлена</string>
|
<string name="lossless_scaling_not_installed">Не установлена</string>
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation">
|
<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_name">错误与注意事项</string>
|
||||||
<string name="notice_notification_channel_description">当发生错误时显示通知。</string>
|
<string name="notice_notification_channel_description">当发生错误时显示通知。</string>
|
||||||
<string name="notification_permission_not_granted">未授予通知权限!</string>
|
<string name="notification_permission_not_granted">未授予通知权限!</string>
|
||||||
@@ -290,6 +291,25 @@
|
|||||||
<string name="gpu_driver_fetcher">GPU驱动获取器</string>
|
<string name="gpu_driver_fetcher">GPU驱动获取器</string>
|
||||||
<string name="gpu_driver_manager">GPU 驱动管理器</string>
|
<string name="gpu_driver_manager">GPU 驱动管理器</string>
|
||||||
<string name="install_gpu_driver_description">安装替代的驱动程序以获得更好的性能和精度</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">帧生成</string>
|
||||||
<string name="frame_gen_per_game_description">配置针对此游戏的帧生成设定</string>
|
<string name="frame_gen_per_game_description">配置针对此游戏的帧生成设定</string>
|
||||||
<string name="frame_gen_description">设定要在使用无损缩放渲染的帧之间应用的插帧。启用后强制采用FIFO呈现模式 。</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_60">60 FPS</string>
|
||||||
<string name="frame_gen_target_rate_90">90 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_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">帧队列目标</string>
|
||||||
<string name="frame_gen_queue_target_description">在显示之前有多少已完成渲染的帧正在等待。较大的队列可以缓解 GPU 突发的压力,但会增加输入延迟。</string>
|
<string name="frame_gen_queue_target_description">在显示之前有多少已完成渲染的帧正在等待。较大的队列可以缓解 GPU 突发的压力,但会增加输入延迟。</string>
|
||||||
<string name="frame_gen_queue_target_0">最低延迟 (无缓冲)</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">运动预估分辨率</string>
|
||||||
<string name="frame_gen_flow_scale_description">光流通道的分辨率,以输出的比例表示。降低它是提升性能最经济的做法。</string>
|
<string name="frame_gen_flow_scale_description">光流通道的分辨率,以输出的比例表示。降低它是提升性能最经济的做法。</string>
|
||||||
<string name="frame_gen_unsupported">帧生成不可用</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_setup_description">作为可选项。请提供您自己的 Lossless.dll 以便在之后可以启用帧生成。</string>
|
||||||
<string name="lossless_scaling_install">安装 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_install_description">帧生成需要您自己从 Lossless Scaling 获得合法的 Lossless.dll 副本</string>
|
||||||
<string name="lossless_scaling_replace_description">选择其它 Lossless.dll 副本</string>
|
<string name="lossless_scaling_replace_description">选择其它 Lossless.dll 副本</string>
|
||||||
<string name="frame_generation_support">帧生成</string>
|
<string name="frame_generation_support">帧生成</string>
|
||||||
<string name="frame_generation_supported">支持</string>
|
<string name="frame_generation_supported">支持</string>
|
||||||
|
<string name="frame_generation_unsupported">不支持 (无 Vulkan 内存模型或 float16)</string>
|
||||||
<string name="lossless_scaling">无损缩放</string>
|
<string name="lossless_scaling">无损缩放</string>
|
||||||
<string name="lossless_scaling_description">提供您自己的 Lossless.dll 文件以启用帧生成</string>
|
<string name="lossless_scaling_description">提供您自己的 Lossless.dll 文件以启用帧生成</string>
|
||||||
<string name="lossless_scaling_installed">已安装</string>
|
<string name="lossless_scaling_installed">已安装</string>
|
||||||
@@ -611,6 +640,11 @@
|
|||||||
<string name="log">日志记录</string>
|
<string name="log">日志记录</string>
|
||||||
<string name="flush_by_line">按行刷新调试日志</string>
|
<string name="flush_by_line">按行刷新调试日志</string>
|
||||||
<string name="flush_by_line_description">在每行写入时刷新调试日志,使在崩溃或冻结时调试更容易。</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 -->
|
<!-- GPU Logging strings -->
|
||||||
<string name="gpu_logging_header">GPU 日志</string>
|
<string name="gpu_logging_header">GPU 日志</string>
|
||||||
<string name="gpu_log_level">日志等级</string>
|
<string name="gpu_log_level">日志等级</string>
|
||||||
@@ -894,6 +928,8 @@
|
|||||||
<string name="loader_error_file_not_found">ROM 文件不存在</string>
|
<string name="loader_error_file_not_found">ROM 文件不存在</string>
|
||||||
|
|
||||||
<string name="loader_requires_firmware">游戏需要固件</string>
|
<string name="loader_requires_firmware">游戏需要固件</string>
|
||||||
|
<string name="loader_requires_firmware_description"><![CDATA[这个游戏可能需要固件才能正常运行,但您还没有安装任何固件。请在启动前安装固件,或者按 “确定” 继续启动。]]></string>
|
||||||
|
|
||||||
<!-- Intent Launch strings -->
|
<!-- Intent Launch strings -->
|
||||||
<string name="searching_for_game">正在搜索游戏...</string>
|
<string name="searching_for_game">正在搜索游戏...</string>
|
||||||
<string name="game_not_found_for_title_id">未找到标题ID的游戏: %1$s</string>
|
<string name="game_not_found_for_title_id">未找到标题ID的游戏: %1$s</string>
|
||||||
|
|||||||
@@ -229,7 +229,7 @@
|
|||||||
<string name="add_directory_success">遊戲目錄新增成功</string>
|
<string name="add_directory_success">遊戲目錄新增成功</string>
|
||||||
<string name="enable_update_checks">檢查更新</string>
|
<string name="enable_update_checks">檢查更新</string>
|
||||||
<string name="enable_update_checks_description">在Eden啟動時檢查有無新版本並選擇是否要下載與安裝更新</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="update_available_description">有更新可供安裝:%1$s\n\n您要進行更新嗎?</string>
|
||||||
<string name="downloading_update">下載更新中</string>
|
<string name="downloading_update">下載更新中</string>
|
||||||
<string name="update_download_failed">更新下載失敗</string>
|
<string name="update_download_failed">更新下載失敗</string>
|
||||||
@@ -1020,9 +1020,40 @@
|
|||||||
<string name="memory_6gb">6GB (不穩定)</string>
|
<string name="memory_6gb">6GB (不穩定)</string>
|
||||||
<string name="memory_8gb">8GB (不穩定)</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 -->
|
<!-- Temperature Units -->
|
||||||
<string name="temperature_celsius">攝氏度</string>
|
<string name="temperature_celsius">攝氏</string>
|
||||||
<string name="temperature_fahrenheit">華氏度</string>
|
<string name="temperature_fahrenheit">華氏</string>
|
||||||
|
|
||||||
<!-- Memory Sizes -->
|
<!-- Memory Sizes -->
|
||||||
<string name="memory_byte_shorthand">B</string>
|
<string name="memory_byte_shorthand">B</string>
|
||||||
@@ -1035,6 +1066,8 @@
|
|||||||
|
|
||||||
<string name="renderer_none">無</string>
|
<string name="renderer_none">無</string>
|
||||||
|
|
||||||
|
<!-- Renderer Accuracy -->
|
||||||
|
<string name="renderer_accuracy_low">效能</string>
|
||||||
<string name="renderer_accuracy_high">準確</string>
|
<string name="renderer_accuracy_high">準確</string>
|
||||||
|
|
||||||
<!-- DMA Accuracy -->
|
<!-- DMA Accuracy -->
|
||||||
|
|||||||
@@ -12,7 +12,7 @@
|
|||||||
namespace AudioCore {
|
namespace AudioCore {
|
||||||
|
|
||||||
AudioCore::AudioCore(Core::System& system) {
|
AudioCore::AudioCore(Core::System& system) {
|
||||||
audio_manager.emplace();
|
audio_manager.emplace(system);
|
||||||
CreateSinks();
|
CreateSinks();
|
||||||
// Must be created after the sinks
|
// Must be created after the sinks
|
||||||
adsp.emplace(system, *output_sink);
|
adsp.emplace(system, *output_sink);
|
||||||
|
|||||||
@@ -15,12 +15,12 @@
|
|||||||
|
|
||||||
namespace AudioCore::AudioIn {
|
namespace AudioCore::AudioIn {
|
||||||
|
|
||||||
Manager::Manager(Core::System& system_) : system{system_} {
|
Manager::Manager(Core::System& system) {
|
||||||
std::iota(session_ids.begin(), session_ids.end(), 0);
|
std::iota(session_ids.begin(), session_ids.end(), 0);
|
||||||
num_free_sessions = MaxInSessions;
|
num_free_sessions = MaxInSessions;
|
||||||
}
|
}
|
||||||
|
|
||||||
Result Manager::AcquireSessionId(size_t& session_id) {
|
Result Manager::AcquireSessionId(Core::System& system, size_t& session_id) {
|
||||||
if (num_free_sessions == 0) {
|
if (num_free_sessions == 0) {
|
||||||
LOG_ERROR(Service_Audio, "All 4 AudioIn sessions are in use, cannot create any more");
|
LOG_ERROR(Service_Audio, "All 4 AudioIn sessions are in use, cannot create any more");
|
||||||
return Service::Audio::ResultOutOfSessions;
|
return Service::Audio::ResultOutOfSessions;
|
||||||
@@ -31,7 +31,7 @@ Result Manager::AcquireSessionId(size_t& session_id) {
|
|||||||
return ResultSuccess;
|
return ResultSuccess;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Manager::ReleaseSessionId(const size_t session_id) {
|
void Manager::ReleaseSessionId(Core::System& system, const size_t session_id) {
|
||||||
std::scoped_lock l{mutex};
|
std::scoped_lock l{mutex};
|
||||||
LOG_DEBUG(Service_Audio, "Freeing AudioIn session {}", session_id);
|
LOG_DEBUG(Service_Audio, "Freeing AudioIn session {}", session_id);
|
||||||
session_ids[free_session_id] = session_id;
|
session_ids[free_session_id] = session_id;
|
||||||
@@ -41,21 +41,20 @@ void Manager::ReleaseSessionId(const size_t session_id) {
|
|||||||
applet_resource_user_ids[session_id] = 0;
|
applet_resource_user_ids[session_id] = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
Result Manager::LinkToManager() {
|
Result Manager::LinkToManager(Core::System& system) {
|
||||||
std::scoped_lock l{mutex};
|
std::scoped_lock l{mutex};
|
||||||
if (!linked_to_manager) {
|
if (!linked_to_manager) {
|
||||||
system.AudioCore().GetAudioManager().SetInManager(std::bind(&Manager::BufferReleaseAndRegister, this));
|
system.AudioCore().GetAudioManager().SetInManager(this, &Manager::BufferReleaseAndRegister);
|
||||||
linked_to_manager = true;
|
linked_to_manager = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return ResultSuccess;
|
return ResultSuccess;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Manager::Start() {
|
void Manager::Start(Core::System& system) {
|
||||||
if (sessions_started) {
|
if (sessions_started) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::scoped_lock l{mutex};
|
std::scoped_lock l{mutex};
|
||||||
for (auto& session : sessions) {
|
for (auto& session : sessions) {
|
||||||
if (session) {
|
if (session) {
|
||||||
@@ -66,21 +65,19 @@ void Manager::Start() {
|
|||||||
sessions_started = true;
|
sessions_started = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Manager::BufferReleaseAndRegister() {
|
void Manager::BufferReleaseAndRegister(void *data, Core::System& system) noexcept {
|
||||||
std::scoped_lock l{mutex};
|
Manager* this_ = (Manager*)data;
|
||||||
for (auto& session : sessions) {
|
std::scoped_lock l{this_->mutex};
|
||||||
|
for (auto& session : this_->sessions) {
|
||||||
if (session != nullptr) {
|
if (session != nullptr) {
|
||||||
session->ReleaseAndRegisterBuffers();
|
session->ReleaseAndRegisterBuffers();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
u32 Manager::GetDeviceNames(std::span<Renderer::AudioDevice::AudioDeviceName> names,
|
u32 Manager::GetDeviceNames(Core::System& system, std::span<Renderer::AudioDevice::AudioDeviceName> names, [[maybe_unused]] const bool filter) {
|
||||||
[[maybe_unused]] const bool filter) {
|
|
||||||
std::scoped_lock l{mutex};
|
std::scoped_lock l{mutex};
|
||||||
|
LinkToManager(system);
|
||||||
LinkToManager();
|
|
||||||
|
|
||||||
auto input_devices{Sink::GetDeviceListForSink(Settings::values.sink_id.GetValue(), true)};
|
auto input_devices{Sink::GetDeviceListForSink(Settings::values.sink_id.GetValue(), true)};
|
||||||
if (!input_devices.empty() && !names.empty()) {
|
if (!input_devices.empty() && !names.empty()) {
|
||||||
names[0] = Renderer::AudioDevice::AudioDeviceName("Uac");
|
names[0] = Renderer::AudioDevice::AudioDeviceName("Uac");
|
||||||
|
|||||||
@@ -1,3 +1,6 @@
|
|||||||
|
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
@@ -30,31 +33,29 @@ public:
|
|||||||
* @param session_id - Output session_id.
|
* @param session_id - Output session_id.
|
||||||
* @return Result code.
|
* @return Result code.
|
||||||
*/
|
*/
|
||||||
Result AcquireSessionId(size_t& session_id);
|
Result AcquireSessionId(Core::System& system, size_t& session_id);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Release a session id on close.
|
* Release a session id on close.
|
||||||
*
|
*
|
||||||
* @param session_id - Session id to free.
|
* @param session_id - Session id to free.
|
||||||
*/
|
*/
|
||||||
void ReleaseSessionId(size_t session_id);
|
void ReleaseSessionId(Core::System& system, const size_t session_id);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Link the audio in manager to the main audio manager.
|
* Link the audio in manager to the main audio manager.
|
||||||
*
|
*
|
||||||
* @return Result code.
|
* @return Result code.
|
||||||
*/
|
*/
|
||||||
Result LinkToManager();
|
Result LinkToManager(Core::System& system);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Start the audio in manager.
|
* Start the audio in manager.
|
||||||
*/
|
*/
|
||||||
void Start();
|
void Start(Core::System& system);
|
||||||
|
|
||||||
/**
|
/// @brief Callback function, called by the audio manager when the audio in event is signalled.
|
||||||
* Callback function, called by the audio manager when the audio in event is signalled.
|
static void BufferReleaseAndRegister(void *data, Core::System& system) noexcept;
|
||||||
*/
|
|
||||||
void BufferReleaseAndRegister();
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get a list of audio in device names.
|
* Get a list of audio in device names.
|
||||||
@@ -64,10 +65,8 @@ public:
|
|||||||
*
|
*
|
||||||
* @return Number of names written.
|
* @return Number of names written.
|
||||||
*/
|
*/
|
||||||
u32 GetDeviceNames(std::span<Renderer::AudioDevice::AudioDeviceName> names, bool filter);
|
u32 GetDeviceNames(Core::System& system, std::span<Renderer::AudioDevice::AudioDeviceName> names, bool filter);
|
||||||
|
|
||||||
/// Core system
|
|
||||||
Core::System& system;
|
|
||||||
/// Array of session ids
|
/// Array of session ids
|
||||||
std::array<size_t, MaxInSessions> session_ids{};
|
std::array<size_t, MaxInSessions> session_ids{};
|
||||||
/// Array of resource user ids
|
/// Array of resource user ids
|
||||||
|
|||||||
@@ -11,8 +11,8 @@
|
|||||||
|
|
||||||
namespace AudioCore {
|
namespace AudioCore {
|
||||||
|
|
||||||
AudioManager::AudioManager() {
|
AudioManager::AudioManager(Core::System& system) {
|
||||||
thread = std::jthread([this](std::stop_token stop_token) {
|
thread = std::jthread([&](std::stop_token stop_token) {
|
||||||
Common::SetCurrentThreadName("AudioManager");
|
Common::SetCurrentThreadName("AudioManager");
|
||||||
std::unique_lock l{events.GetAudioEventLock()};
|
std::unique_lock l{events.GetAudioEventLock()};
|
||||||
events.ClearEvents();
|
events.ClearEvents();
|
||||||
@@ -25,7 +25,7 @@ AudioManager::AudioManager() {
|
|||||||
const auto event_type = Event::Type(i);
|
const auto event_type = Event::Type(i);
|
||||||
if (events.CheckAudioEventSet(event_type) || timed_out) {
|
if (events.CheckAudioEventSet(event_type) || timed_out) {
|
||||||
if (buffer_events[i]) {
|
if (buffer_events[i]) {
|
||||||
buffer_events[i]();
|
buffer_events[i](buffer_data[i], system);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
events.SetAudioEvent(event_type, false);
|
events.SetAudioEvent(event_type, false);
|
||||||
@@ -42,12 +42,13 @@ void AudioManager::Shutdown() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Result AudioManager::SetOutManager(BufferEventFunc buffer_func) {
|
Result AudioManager::SetOutManager(void *data, BufferEventFunc buffer_func) {
|
||||||
if (thread.joinable()) {
|
if (thread.joinable()) {
|
||||||
std::scoped_lock l{lock};
|
std::scoped_lock l{lock};
|
||||||
const auto index{events.GetManagerIndex(Event::Type::AudioOutManager)};
|
const auto index{events.GetManagerIndex(Event::Type::AudioOutManager)};
|
||||||
if (buffer_events[index] == nullptr) {
|
if (buffer_events[index] == nullptr) {
|
||||||
buffer_events[index] = std::move(buffer_func);
|
buffer_events[index] = std::move(buffer_func);
|
||||||
|
buffer_data[index] = data;
|
||||||
needs_update = true;
|
needs_update = true;
|
||||||
events.SetAudioEvent(Event::Type::AudioOutManager, true);
|
events.SetAudioEvent(Event::Type::AudioOutManager, true);
|
||||||
}
|
}
|
||||||
@@ -56,12 +57,13 @@ Result AudioManager::SetOutManager(BufferEventFunc buffer_func) {
|
|||||||
return Service::Audio::ResultOperationFailed;
|
return Service::Audio::ResultOperationFailed;
|
||||||
}
|
}
|
||||||
|
|
||||||
Result AudioManager::SetInManager(BufferEventFunc buffer_func) {
|
Result AudioManager::SetInManager(void *data, BufferEventFunc buffer_func) {
|
||||||
if (thread.joinable()) {
|
if (thread.joinable()) {
|
||||||
std::scoped_lock l{lock};
|
std::scoped_lock l{lock};
|
||||||
const auto index{events.GetManagerIndex(Event::Type::AudioInManager)};
|
const auto index{events.GetManagerIndex(Event::Type::AudioInManager)};
|
||||||
if (buffer_events[index] == nullptr) {
|
if (buffer_events[index] == nullptr) {
|
||||||
buffer_events[index] = std::move(buffer_func);
|
buffer_events[index] = std::move(buffer_func);
|
||||||
|
buffer_data[index] = data;
|
||||||
needs_update = true;
|
needs_update = true;
|
||||||
events.SetAudioEvent(Event::Type::AudioInManager, true);
|
events.SetAudioEvent(Event::Type::AudioInManager, true);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,6 +16,10 @@
|
|||||||
|
|
||||||
#include "audio_core/audio_event.h"
|
#include "audio_core/audio_event.h"
|
||||||
|
|
||||||
|
namespace Core {
|
||||||
|
class System;
|
||||||
|
}
|
||||||
|
|
||||||
union Result;
|
union Result;
|
||||||
|
|
||||||
namespace AudioCore {
|
namespace AudioCore {
|
||||||
@@ -34,31 +38,24 @@ namespace AudioCore {
|
|||||||
* This is only used by audio in and audio out.
|
* This is only used by audio in and audio out.
|
||||||
*/
|
*/
|
||||||
class AudioManager {
|
class AudioManager {
|
||||||
using BufferEventFunc = std::function<void()>;
|
using BufferEventFunc = void (*)(void *data, Core::System& system) noexcept;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit AudioManager();
|
explicit AudioManager(Core::System& system);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Shutdown the audio manager.
|
* Shutdown the audio manager.
|
||||||
*/
|
*/
|
||||||
void Shutdown();
|
void Shutdown();
|
||||||
|
|
||||||
/**
|
/// Register the out manager, keeping a function to be called when the out event is signalled.
|
||||||
* Register the out manager, keeping a function to be called when the out event is signalled.
|
/// @param buffer_func - Function to be called on signal.
|
||||||
*
|
/// @return Result code.
|
||||||
* @param buffer_func - Function to be called on signal.
|
Result SetOutManager(void *data, BufferEventFunc buffer_func);
|
||||||
* @return Result code.
|
|
||||||
*/
|
|
||||||
Result SetOutManager(BufferEventFunc buffer_func);
|
|
||||||
|
|
||||||
/**
|
/// Register the in manager, keeping a function to be called when the in event is signalled.
|
||||||
* Register the in manager, keeping a function to be called when the in event is signalled.
|
/// @param buffer_func - Function to be called on signal.
|
||||||
*
|
/// @return Result code.
|
||||||
* @param buffer_func - Function to be called on signal.
|
Result SetInManager(void *data, BufferEventFunc buffer_func);
|
||||||
* @return Result code.
|
|
||||||
*/
|
|
||||||
Result SetInManager(BufferEventFunc buffer_func);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set an event to signalled, and signal the thread.
|
* Set an event to signalled, and signal the thread.
|
||||||
@@ -73,8 +70,9 @@ private:
|
|||||||
bool needs_update{};
|
bool needs_update{};
|
||||||
/// Events to be set and signalled
|
/// Events to be set and signalled
|
||||||
Event events{};
|
Event events{};
|
||||||
/// Callbacks for each manager
|
/// Callbacks (and user data) for each manager
|
||||||
std::array<BufferEventFunc, 3> buffer_events{};
|
std::array<BufferEventFunc, 3> buffer_events{};
|
||||||
|
std::array<void*, 3> buffer_data{};
|
||||||
/// General lock
|
/// General lock
|
||||||
std::mutex lock{};
|
std::mutex lock{};
|
||||||
/// Main thread for waiting and callbacks
|
/// Main thread for waiting and callbacks
|
||||||
|
|||||||
@@ -14,12 +14,12 @@
|
|||||||
|
|
||||||
namespace AudioCore::AudioOut {
|
namespace AudioCore::AudioOut {
|
||||||
|
|
||||||
Manager::Manager(Core::System& system_) : system{system_} {
|
Manager::Manager(Core::System& system) {
|
||||||
std::iota(session_ids.begin(), session_ids.end(), 0);
|
std::iota(session_ids.begin(), session_ids.end(), 0);
|
||||||
num_free_sessions = MaxOutSessions;
|
num_free_sessions = MaxOutSessions;
|
||||||
}
|
}
|
||||||
|
|
||||||
Result Manager::AcquireSessionId(size_t& session_id) {
|
Result Manager::AcquireSessionId(Core::System& system, size_t& session_id) {
|
||||||
if (num_free_sessions == 0) {
|
if (num_free_sessions == 0) {
|
||||||
LOG_ERROR(Service_Audio, "All 12 Audio Out sessions are in use, cannot create any more");
|
LOG_ERROR(Service_Audio, "All 12 Audio Out sessions are in use, cannot create any more");
|
||||||
return Service::Audio::ResultOutOfSessions;
|
return Service::Audio::ResultOutOfSessions;
|
||||||
@@ -30,7 +30,7 @@ Result Manager::AcquireSessionId(size_t& session_id) {
|
|||||||
return ResultSuccess;
|
return ResultSuccess;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Manager::ReleaseSessionId(const size_t session_id) {
|
void Manager::ReleaseSessionId(Core::System& system, const size_t session_id) {
|
||||||
std::scoped_lock l{mutex};
|
std::scoped_lock l{mutex};
|
||||||
LOG_DEBUG(Service_Audio, "Freeing AudioOut session {}", session_id);
|
LOG_DEBUG(Service_Audio, "Freeing AudioOut session {}", session_id);
|
||||||
session_ids[free_session_id] = session_id;
|
session_ids[free_session_id] = session_id;
|
||||||
@@ -40,44 +40,36 @@ void Manager::ReleaseSessionId(const size_t session_id) {
|
|||||||
applet_resource_user_ids[session_id] = 0;
|
applet_resource_user_ids[session_id] = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
Result Manager::LinkToManager() {
|
Result Manager::LinkToManager(Core::System& system) {
|
||||||
std::scoped_lock l{mutex};
|
std::scoped_lock l{mutex};
|
||||||
if (!linked_to_manager) {
|
if (!linked_to_manager) {
|
||||||
system.AudioCore().GetAudioManager().SetOutManager(std::bind(&Manager::BufferReleaseAndRegister, this));
|
system.AudioCore().GetAudioManager().SetOutManager(this, &Manager::BufferReleaseAndRegister);
|
||||||
linked_to_manager = true;
|
linked_to_manager = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return ResultSuccess;
|
return ResultSuccess;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Manager::Start() {
|
void Manager::Start(Core::System& system) {
|
||||||
if (sessions_started) {
|
if (!sessions_started) {
|
||||||
return;
|
std::scoped_lock l{mutex};
|
||||||
}
|
for (auto& session : sessions) {
|
||||||
|
if (session) {
|
||||||
std::scoped_lock l{mutex};
|
session->StartSession();
|
||||||
for (auto& session : sessions) {
|
}
|
||||||
if (session) {
|
|
||||||
session->StartSession();
|
|
||||||
}
|
}
|
||||||
|
sessions_started = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
sessions_started = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Manager::BufferReleaseAndRegister() {
|
void Manager::BufferReleaseAndRegister(void *data, Core::System& system) noexcept {
|
||||||
std::scoped_lock l{mutex};
|
Manager* this_ = (Manager*)data;
|
||||||
for (auto& session : sessions) {
|
std::scoped_lock l{this_->mutex};
|
||||||
|
for (auto& session : this_->sessions) {
|
||||||
if (session != nullptr) {
|
if (session != nullptr) {
|
||||||
session->ReleaseAndRegisterBuffers();
|
session->ReleaseAndRegisterBuffers();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
u32 Manager::GetAudioOutDeviceNames(
|
|
||||||
std::vector<Renderer::AudioDevice::AudioDeviceName>& names) const {
|
|
||||||
names.emplace_back("DeviceOut");
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace AudioCore::AudioOut
|
} // namespace AudioCore::AudioOut
|
||||||
|
|||||||
@@ -1,3 +1,6 @@
|
|||||||
|
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
@@ -29,42 +32,32 @@ public:
|
|||||||
* @param session_id - Output session_id.
|
* @param session_id - Output session_id.
|
||||||
* @return Result code.
|
* @return Result code.
|
||||||
*/
|
*/
|
||||||
Result AcquireSessionId(size_t& session_id);
|
Result AcquireSessionId(Core::System& system, size_t& session_id);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Release a session id on close.
|
* Release a session id on close.
|
||||||
*
|
*
|
||||||
* @param session_id - Session id to free.
|
* @param session_id - Session id to free.
|
||||||
*/
|
*/
|
||||||
void ReleaseSessionId(size_t session_id);
|
void ReleaseSessionId(Core::System& system, const size_t session_id);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Link this manager to the main audio manager.
|
* Link this manager to the main audio manager.
|
||||||
*
|
*
|
||||||
* @return Result code.
|
* @return Result code.
|
||||||
*/
|
*/
|
||||||
Result LinkToManager();
|
Result LinkToManager(Core::System& system);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Start the audio out manager.
|
* Start the audio out manager.
|
||||||
*/
|
*/
|
||||||
void Start();
|
void Start(Core::System& system);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Callback function, called by the audio manager when the audio out event is signalled.
|
* Callback function, called by the audio manager when the audio out event is signalled.
|
||||||
*/
|
*/
|
||||||
void BufferReleaseAndRegister();
|
static void BufferReleaseAndRegister(void* data, Core::System& system) noexcept;
|
||||||
|
|
||||||
/**
|
|
||||||
* Get a list of audio out device names.
|
|
||||||
*
|
|
||||||
* @param names - Output container to write names to.
|
|
||||||
* @return Number of names written.
|
|
||||||
*/
|
|
||||||
u32 GetAudioOutDeviceNames(std::vector<Renderer::AudioDevice::AudioDeviceName>& names) const;
|
|
||||||
|
|
||||||
/// Core system
|
|
||||||
Core::System& system;
|
|
||||||
/// Array of session ids
|
/// Array of session ids
|
||||||
std::array<size_t, MaxOutSessions> session_ids{};
|
std::array<size_t, MaxOutSessions> session_ids{};
|
||||||
/// Array of resource user ids
|
/// Array of resource user ids
|
||||||
|
|||||||
@@ -6,14 +6,14 @@
|
|||||||
|
|
||||||
#include "audio_core/audio_render_manager.h"
|
#include "audio_core/audio_render_manager.h"
|
||||||
#include "audio_core/common/audio_renderer_parameter.h"
|
#include "audio_core/common/audio_renderer_parameter.h"
|
||||||
|
#include "audio_core/renderer/system_manager.h"
|
||||||
#include "audio_core/common/feature_support.h"
|
#include "audio_core/common/feature_support.h"
|
||||||
#include "core/core.h"
|
#include "core/core.h"
|
||||||
|
|
||||||
namespace AudioCore::Renderer {
|
namespace AudioCore::Renderer {
|
||||||
|
|
||||||
Manager::Manager(Core::System& system_)
|
Manager::Manager(Core::System& system_)
|
||||||
: system{system_}
|
: system_manager{std::make_unique<SystemManager>(system_)}
|
||||||
, system_manager{std::make_unique<SystemManager>(system)}
|
|
||||||
{
|
{
|
||||||
std::iota(session_ids.begin(), session_ids.end(), 0);
|
std::iota(session_ids.begin(), session_ids.end(), 0);
|
||||||
}
|
}
|
||||||
@@ -62,11 +62,11 @@ u32 Manager::GetSessionCount() const {
|
|||||||
return session_count;
|
return session_count;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Manager::AddSystem(System& system_) {
|
bool Manager::AddSystem(Renderer::System& system_) {
|
||||||
return system_manager->Add(system_);
|
return system_manager->Add(system_);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Manager::RemoveSystem(System& system_) {
|
bool Manager::RemoveSystem(Renderer::System& system_) {
|
||||||
return system_manager->Remove(system_);
|
return system_manager->Remove(system_);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,6 @@
|
|||||||
|
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
@@ -71,7 +74,7 @@ public:
|
|||||||
* @param system - The system to add.
|
* @param system - The system to add.
|
||||||
* @return True if the system was successfully added, otherwise false.
|
* @return True if the system was successfully added, otherwise false.
|
||||||
*/
|
*/
|
||||||
bool AddSystem(System& system);
|
bool AddSystem(Renderer::System& system);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Remove a renderer system from the manager.
|
* Remove a renderer system from the manager.
|
||||||
@@ -79,7 +82,7 @@ public:
|
|||||||
* @param system - The system to remove.
|
* @param system - The system to remove.
|
||||||
* @return True if the system was successfully removed, otherwise false.
|
* @return True if the system was successfully removed, otherwise false.
|
||||||
*/
|
*/
|
||||||
bool RemoveSystem(System& system);
|
bool RemoveSystem(Renderer::System& system);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Free a session id when the system wants to shut down.
|
* Free a session id when the system wants to shut down.
|
||||||
@@ -89,8 +92,6 @@ public:
|
|||||||
void ReleaseSessionId(s32 session_id);
|
void ReleaseSessionId(s32 session_id);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/// Core system
|
|
||||||
Core::System& system;
|
|
||||||
/// Session ids, -1 when in use
|
/// Session ids, -1 when in use
|
||||||
std::array<s32, MaxRendererSessions> session_ids{};
|
std::array<s32, MaxRendererSessions> session_ids{};
|
||||||
/// Number of active renderers
|
/// Number of active renderers
|
||||||
|
|||||||
@@ -1,3 +1,6 @@
|
|||||||
|
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
@@ -8,42 +11,43 @@
|
|||||||
namespace AudioCore::AudioIn {
|
namespace AudioCore::AudioIn {
|
||||||
|
|
||||||
In::In(Core::System& system_, Manager& manager_, Kernel::KEvent* event_, size_t session_id_)
|
In::In(Core::System& system_, Manager& manager_, Kernel::KEvent* event_, size_t session_id_)
|
||||||
: manager{manager_}, parent_mutex{manager.mutex}, event{event_}, system{system_, event,
|
: manager{manager_}, parent_mutex{manager.mutex}, event{event_}
|
||||||
session_id_} {}
|
, audio_system{system_, event, session_id_}
|
||||||
|
{}
|
||||||
|
|
||||||
void In::Free() {
|
void In::Free(Core::System& system) {
|
||||||
std::scoped_lock l{parent_mutex};
|
std::scoped_lock l{parent_mutex};
|
||||||
manager.ReleaseSessionId(system.GetSessionId());
|
manager.ReleaseSessionId(system, audio_system.GetSessionId());
|
||||||
}
|
}
|
||||||
|
|
||||||
System& In::GetSystem() {
|
System& In::GetSystem() {
|
||||||
return system;
|
return audio_system;
|
||||||
}
|
}
|
||||||
|
|
||||||
AudioIn::State In::GetState() {
|
AudioIn::State In::GetState() {
|
||||||
std::scoped_lock l{parent_mutex};
|
std::scoped_lock l{parent_mutex};
|
||||||
return system.GetState();
|
return audio_system.GetState();
|
||||||
}
|
}
|
||||||
|
|
||||||
Result In::StartSystem() {
|
Result In::StartSystem() {
|
||||||
std::scoped_lock l{parent_mutex};
|
std::scoped_lock l{parent_mutex};
|
||||||
return system.Start();
|
return audio_system.Start();
|
||||||
}
|
}
|
||||||
|
|
||||||
void In::StartSession() {
|
void In::StartSession() {
|
||||||
std::scoped_lock l{parent_mutex};
|
std::scoped_lock l{parent_mutex};
|
||||||
system.StartSession();
|
audio_system.StartSession();
|
||||||
}
|
}
|
||||||
|
|
||||||
Result In::StopSystem() {
|
Result In::StopSystem() {
|
||||||
std::scoped_lock l{parent_mutex};
|
std::scoped_lock l{parent_mutex};
|
||||||
return system.Stop();
|
return audio_system.Stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
Result In::AppendBuffer(const AudioInBuffer& buffer, u64 tag) {
|
Result In::AppendBuffer(const AudioInBuffer& buffer, u64 tag) {
|
||||||
std::scoped_lock l{parent_mutex};
|
std::scoped_lock l{parent_mutex};
|
||||||
|
|
||||||
if (system.AppendBuffer(buffer, tag)) {
|
if (audio_system.AppendBuffer(buffer, tag)) {
|
||||||
return ResultSuccess;
|
return ResultSuccess;
|
||||||
}
|
}
|
||||||
return Service::Audio::ResultBufferCountReached;
|
return Service::Audio::ResultBufferCountReached;
|
||||||
@@ -51,20 +55,20 @@ Result In::AppendBuffer(const AudioInBuffer& buffer, u64 tag) {
|
|||||||
|
|
||||||
void In::ReleaseAndRegisterBuffers() {
|
void In::ReleaseAndRegisterBuffers() {
|
||||||
std::scoped_lock l{parent_mutex};
|
std::scoped_lock l{parent_mutex};
|
||||||
if (system.GetState() == State::Started) {
|
if (audio_system.GetState() == State::Started) {
|
||||||
system.ReleaseBuffers();
|
audio_system.ReleaseBuffers();
|
||||||
system.RegisterBuffers();
|
audio_system.RegisterBuffers();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool In::FlushAudioInBuffers() {
|
bool In::FlushAudioInBuffers() {
|
||||||
std::scoped_lock l{parent_mutex};
|
std::scoped_lock l{parent_mutex};
|
||||||
return system.FlushAudioInBuffers();
|
return audio_system.FlushAudioInBuffers();
|
||||||
}
|
}
|
||||||
|
|
||||||
u32 In::GetReleasedBuffers(std::span<u64> tags) {
|
u32 In::GetReleasedBuffers(std::span<u64> tags) {
|
||||||
std::scoped_lock l{parent_mutex};
|
std::scoped_lock l{parent_mutex};
|
||||||
return system.GetReleasedBuffers(tags);
|
return audio_system.GetReleasedBuffers(tags);
|
||||||
}
|
}
|
||||||
|
|
||||||
Kernel::KReadableEvent& In::GetBufferEvent() {
|
Kernel::KReadableEvent& In::GetBufferEvent() {
|
||||||
@@ -74,27 +78,27 @@ Kernel::KReadableEvent& In::GetBufferEvent() {
|
|||||||
|
|
||||||
f32 In::GetVolume() const {
|
f32 In::GetVolume() const {
|
||||||
std::scoped_lock l{parent_mutex};
|
std::scoped_lock l{parent_mutex};
|
||||||
return system.GetVolume();
|
return audio_system.GetVolume();
|
||||||
}
|
}
|
||||||
|
|
||||||
void In::SetVolume(f32 volume) {
|
void In::SetVolume(f32 volume) {
|
||||||
std::scoped_lock l{parent_mutex};
|
std::scoped_lock l{parent_mutex};
|
||||||
system.SetVolume(volume);
|
audio_system.SetVolume(volume);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool In::ContainsAudioBuffer(u64 tag) const {
|
bool In::ContainsAudioBuffer(u64 tag) const {
|
||||||
std::scoped_lock l{parent_mutex};
|
std::scoped_lock l{parent_mutex};
|
||||||
return system.ContainsAudioBuffer(tag);
|
return audio_system.ContainsAudioBuffer(tag);
|
||||||
}
|
}
|
||||||
|
|
||||||
u32 In::GetBufferCount() const {
|
u32 In::GetBufferCount() const {
|
||||||
std::scoped_lock l{parent_mutex};
|
std::scoped_lock l{parent_mutex};
|
||||||
return system.GetBufferCount();
|
return audio_system.GetBufferCount();
|
||||||
}
|
}
|
||||||
|
|
||||||
u64 In::GetPlayedSampleCount() const {
|
u64 In::GetPlayedSampleCount() const {
|
||||||
std::scoped_lock l{parent_mutex};
|
std::scoped_lock l{parent_mutex};
|
||||||
return system.GetPlayedSampleCount();
|
return audio_system.GetPlayedSampleCount();
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace AudioCore::AudioIn
|
} // namespace AudioCore::AudioIn
|
||||||
|
|||||||
@@ -1,3 +1,6 @@
|
|||||||
|
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
@@ -30,7 +33,7 @@ public:
|
|||||||
/**
|
/**
|
||||||
* Free this audio in from the audio in manager.
|
* Free this audio in from the audio in manager.
|
||||||
*/
|
*/
|
||||||
void Free();
|
void Free(Core::System& system);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get this audio in's system.
|
* Get this audio in's system.
|
||||||
@@ -141,7 +144,7 @@ private:
|
|||||||
/// Buffer event, signalled when buffers are ready to be released
|
/// Buffer event, signalled when buffers are ready to be released
|
||||||
Kernel::KEvent* event;
|
Kernel::KEvent* event;
|
||||||
/// Main audio in system
|
/// Main audio in system
|
||||||
System system;
|
System audio_system;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace AudioCore::AudioIn
|
} // namespace AudioCore::AudioIn
|
||||||
|
|||||||
@@ -1,3 +1,6 @@
|
|||||||
|
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
@@ -8,42 +11,43 @@
|
|||||||
namespace AudioCore::AudioOut {
|
namespace AudioCore::AudioOut {
|
||||||
|
|
||||||
Out::Out(Core::System& system_, Manager& manager_, Kernel::KEvent* event_, size_t session_id_)
|
Out::Out(Core::System& system_, Manager& manager_, Kernel::KEvent* event_, size_t session_id_)
|
||||||
: manager{manager_}, parent_mutex{manager.mutex}, event{event_}, system{system_, event,
|
: manager{manager_}, parent_mutex{manager.mutex}, event{event_}
|
||||||
session_id_} {}
|
, audio_system{system_, event, session_id_}
|
||||||
|
{}
|
||||||
|
|
||||||
void Out::Free() {
|
void Out::Free(Core::System& system) {
|
||||||
std::scoped_lock l{parent_mutex};
|
std::scoped_lock l{parent_mutex};
|
||||||
manager.ReleaseSessionId(system.GetSessionId());
|
manager.ReleaseSessionId(system, audio_system.GetSessionId());
|
||||||
}
|
}
|
||||||
|
|
||||||
System& Out::GetSystem() {
|
System& Out::GetSystem() {
|
||||||
return system;
|
return audio_system;
|
||||||
}
|
}
|
||||||
|
|
||||||
AudioOut::State Out::GetState() {
|
AudioOut::State Out::GetState() {
|
||||||
std::scoped_lock l{parent_mutex};
|
std::scoped_lock l{parent_mutex};
|
||||||
return system.GetState();
|
return audio_system.GetState();
|
||||||
}
|
}
|
||||||
|
|
||||||
Result Out::StartSystem() {
|
Result Out::StartSystem() {
|
||||||
std::scoped_lock l{parent_mutex};
|
std::scoped_lock l{parent_mutex};
|
||||||
return system.Start();
|
return audio_system.Start();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Out::StartSession() {
|
void Out::StartSession() {
|
||||||
std::scoped_lock l{parent_mutex};
|
std::scoped_lock l{parent_mutex};
|
||||||
system.StartSession();
|
audio_system.StartSession();
|
||||||
}
|
}
|
||||||
|
|
||||||
Result Out::StopSystem() {
|
Result Out::StopSystem() {
|
||||||
std::scoped_lock l{parent_mutex};
|
std::scoped_lock l{parent_mutex};
|
||||||
return system.Stop();
|
return audio_system.Stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
Result Out::AppendBuffer(const AudioOutBuffer& buffer, const u64 tag) {
|
Result Out::AppendBuffer(const AudioOutBuffer& buffer, const u64 tag) {
|
||||||
std::scoped_lock l{parent_mutex};
|
std::scoped_lock l{parent_mutex};
|
||||||
|
|
||||||
if (system.AppendBuffer(buffer, tag)) {
|
if (audio_system.AppendBuffer(buffer, tag)) {
|
||||||
return ResultSuccess;
|
return ResultSuccess;
|
||||||
}
|
}
|
||||||
return Service::Audio::ResultBufferCountReached;
|
return Service::Audio::ResultBufferCountReached;
|
||||||
@@ -51,20 +55,20 @@ Result Out::AppendBuffer(const AudioOutBuffer& buffer, const u64 tag) {
|
|||||||
|
|
||||||
void Out::ReleaseAndRegisterBuffers() {
|
void Out::ReleaseAndRegisterBuffers() {
|
||||||
std::scoped_lock l{parent_mutex};
|
std::scoped_lock l{parent_mutex};
|
||||||
if (system.GetState() == State::Started) {
|
if (audio_system.GetState() == State::Started) {
|
||||||
system.ReleaseBuffers();
|
audio_system.ReleaseBuffers();
|
||||||
system.RegisterBuffers();
|
audio_system.RegisterBuffers();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Out::FlushAudioOutBuffers() {
|
bool Out::FlushAudioOutBuffers() {
|
||||||
std::scoped_lock l{parent_mutex};
|
std::scoped_lock l{parent_mutex};
|
||||||
return system.FlushAudioOutBuffers();
|
return audio_system.FlushAudioOutBuffers();
|
||||||
}
|
}
|
||||||
|
|
||||||
u32 Out::GetReleasedBuffers(std::span<u64> tags) {
|
u32 Out::GetReleasedBuffers(std::span<u64> tags) {
|
||||||
std::scoped_lock l{parent_mutex};
|
std::scoped_lock l{parent_mutex};
|
||||||
return system.GetReleasedBuffers(tags);
|
return audio_system.GetReleasedBuffers(tags);
|
||||||
}
|
}
|
||||||
|
|
||||||
Kernel::KReadableEvent& Out::GetBufferEvent() {
|
Kernel::KReadableEvent& Out::GetBufferEvent() {
|
||||||
@@ -74,27 +78,27 @@ Kernel::KReadableEvent& Out::GetBufferEvent() {
|
|||||||
|
|
||||||
f32 Out::GetVolume() const {
|
f32 Out::GetVolume() const {
|
||||||
std::scoped_lock l{parent_mutex};
|
std::scoped_lock l{parent_mutex};
|
||||||
return system.GetVolume();
|
return audio_system.GetVolume();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Out::SetVolume(const f32 volume) {
|
void Out::SetVolume(const f32 volume) {
|
||||||
std::scoped_lock l{parent_mutex};
|
std::scoped_lock l{parent_mutex};
|
||||||
system.SetVolume(volume);
|
audio_system.SetVolume(volume);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Out::ContainsAudioBuffer(const u64 tag) const {
|
bool Out::ContainsAudioBuffer(const u64 tag) const {
|
||||||
std::scoped_lock l{parent_mutex};
|
std::scoped_lock l{parent_mutex};
|
||||||
return system.ContainsAudioBuffer(tag);
|
return audio_system.ContainsAudioBuffer(tag);
|
||||||
}
|
}
|
||||||
|
|
||||||
u32 Out::GetBufferCount() const {
|
u32 Out::GetBufferCount() const {
|
||||||
std::scoped_lock l{parent_mutex};
|
std::scoped_lock l{parent_mutex};
|
||||||
return system.GetBufferCount();
|
return audio_system.GetBufferCount();
|
||||||
}
|
}
|
||||||
|
|
||||||
u64 Out::GetPlayedSampleCount() const {
|
u64 Out::GetPlayedSampleCount() const {
|
||||||
std::scoped_lock l{parent_mutex};
|
std::scoped_lock l{parent_mutex};
|
||||||
return system.GetPlayedSampleCount();
|
return audio_system.GetPlayedSampleCount();
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace AudioCore::AudioOut
|
} // namespace AudioCore::AudioOut
|
||||||
|
|||||||
@@ -1,3 +1,6 @@
|
|||||||
|
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
@@ -30,7 +33,7 @@ public:
|
|||||||
/**
|
/**
|
||||||
* Free this audio out from the audio out manager.
|
* Free this audio out from the audio out manager.
|
||||||
*/
|
*/
|
||||||
void Free();
|
void Free(Core::System& system);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get this audio out's system.
|
* Get this audio out's system.
|
||||||
@@ -141,7 +144,7 @@ private:
|
|||||||
/// Buffer event, signalled when buffers are ready to be released
|
/// Buffer event, signalled when buffers are ready to be released
|
||||||
Kernel::KEvent* event;
|
Kernel::KEvent* event;
|
||||||
/// Main audio out system
|
/// Main audio out system
|
||||||
System system;
|
System audio_system;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace AudioCore::AudioOut
|
} // namespace AudioCore::AudioOut
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||||
@@ -34,42 +34,37 @@ constexpr std::array output_device_names{
|
|||||||
AudioDevice::AudioDeviceName{"AudioExternalOutput"},
|
AudioDevice::AudioDeviceName{"AudioExternalOutput"},
|
||||||
};
|
};
|
||||||
|
|
||||||
AudioDevice::AudioDevice(Core::System& system, const u64 applet_resource_user_id_,
|
AudioDevice::AudioDevice(Core::System& system, const u64 applet_resource_user_id_, const u32 revision)
|
||||||
const u32 revision)
|
: applet_resource_user_id{applet_resource_user_id_}
|
||||||
: output_sink{system.AudioCore().GetOutputSink()},
|
, user_revision{revision}
|
||||||
applet_resource_user_id{applet_resource_user_id_}, user_revision{revision} {}
|
{}
|
||||||
|
|
||||||
u32 AudioDevice::ListAudioDeviceName(std::span<AudioDeviceName> out_buffer) const {
|
u32 AudioDevice::ListAudioDeviceName(std::span<AudioDeviceName> out_buffer) const {
|
||||||
std::span<const AudioDeviceName> names{};
|
std::span<const AudioDeviceName> names{};
|
||||||
|
|
||||||
if (CheckFeatureSupported(SupportTags::AudioUsbDeviceOutput, user_revision)) {
|
if (CheckFeatureSupported(SupportTags::AudioUsbDeviceOutput, user_revision)) {
|
||||||
names = usb_device_names;
|
names = usb_device_names;
|
||||||
} else {
|
} else {
|
||||||
names = device_names;
|
names = device_names;
|
||||||
}
|
}
|
||||||
|
const u32 out_count = u32((std::min)(out_buffer.size(), names.size()));
|
||||||
const u32 out_count{static_cast<u32>((std::min)(out_buffer.size(), names.size()))};
|
for (u32 i = 0; i < out_count; i++)
|
||||||
for (u32 i = 0; i < out_count; i++) {
|
|
||||||
out_buffer[i] = names[i];
|
out_buffer[i] = names[i];
|
||||||
}
|
|
||||||
return out_count;
|
return out_count;
|
||||||
}
|
}
|
||||||
|
|
||||||
u32 AudioDevice::ListAudioOutputDeviceName(std::span<AudioDeviceName> out_buffer) const {
|
u32 AudioDevice::ListAudioOutputDeviceName(std::span<AudioDeviceName> out_buffer) const {
|
||||||
const u32 out_count{static_cast<u32>((std::min)(out_buffer.size(), output_device_names.size()))};
|
const u32 out_count = u32((std::min)(out_buffer.size(), output_device_names.size()));
|
||||||
|
for (u32 i = 0; i < out_count; i++)
|
||||||
for (u32 i = 0; i < out_count; i++) {
|
|
||||||
out_buffer[i] = output_device_names[i];
|
out_buffer[i] = output_device_names[i];
|
||||||
}
|
|
||||||
return out_count;
|
return out_count;
|
||||||
}
|
}
|
||||||
|
|
||||||
void AudioDevice::SetDeviceVolumes(const f32 volume) {
|
void AudioDevice::SetDeviceVolumes(Core::System& system, const f32 volume) {
|
||||||
output_sink.SetDeviceVolume(volume);
|
system.AudioCore().GetOutputSink().SetDeviceVolume(volume);
|
||||||
}
|
}
|
||||||
|
|
||||||
f32 AudioDevice::GetDeviceVolume([[maybe_unused]] std::string_view name) const {
|
f32 AudioDevice::GetDeviceVolume(Core::System& system, [[maybe_unused]] std::string_view name) const {
|
||||||
return output_sink.GetDeviceVolume();
|
return system.AudioCore().GetOutputSink().GetDeviceVolume();
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace AudioCore::Renderer
|
} // namespace AudioCore::Renderer
|
||||||
|
|||||||
@@ -1,3 +1,6 @@
|
|||||||
|
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
@@ -54,7 +57,7 @@ public:
|
|||||||
*
|
*
|
||||||
* @param volume - Volume to set.
|
* @param volume - Volume to set.
|
||||||
*/
|
*/
|
||||||
void SetDeviceVolumes(f32 volume);
|
void SetDeviceVolumes(Core::System& system, f32 volume);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the volume for a given device name.
|
* Get the volume for a given device name.
|
||||||
@@ -63,11 +66,9 @@ public:
|
|||||||
* @param name - Name of the device to check. Unused.
|
* @param name - Name of the device to check. Unused.
|
||||||
* @return Volume of the device.
|
* @return Volume of the device.
|
||||||
*/
|
*/
|
||||||
f32 GetDeviceVolume(std::string_view name) const;
|
f32 GetDeviceVolume(Core::System& system, std::string_view name) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/// Backend output sink for the device
|
|
||||||
Sink::Sink& output_sink;
|
|
||||||
/// Resource id this device is used for
|
/// Resource id this device is used for
|
||||||
const u64 applet_resource_user_id;
|
const u64 applet_resource_user_id;
|
||||||
/// User audio renderer revision
|
/// User audio renderer revision
|
||||||
|
|||||||
@@ -1,3 +1,6 @@
|
|||||||
|
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
@@ -13,56 +16,46 @@
|
|||||||
namespace AudioCore::Renderer {
|
namespace AudioCore::Renderer {
|
||||||
|
|
||||||
Renderer::Renderer(Core::System& system_, Manager& manager_, Kernel::KEvent* rendered_event)
|
Renderer::Renderer(Core::System& system_, Manager& manager_, Kernel::KEvent* rendered_event)
|
||||||
: core{system_}, manager{manager_}, system{system_, rendered_event} {}
|
: manager{manager_}
|
||||||
|
, audio_system{system_, rendered_event}
|
||||||
|
{}
|
||||||
|
|
||||||
Result Renderer::Initialize(const AudioRendererParameterInternal& params,
|
Result Renderer::Initialize(const AudioRendererParameterInternal& params, Kernel::KTransferMemory* transfer_memory, const u64 transfer_memory_size, Kernel::KProcess* process_handle, const u64 applet_resource_user_id, const s32 session_id) {
|
||||||
Kernel::KTransferMemory* transfer_memory,
|
|
||||||
const u64 transfer_memory_size, Kernel::KProcess* process_handle,
|
|
||||||
const u64 applet_resource_user_id, const s32 session_id) {
|
|
||||||
if (params.execution_mode == ExecutionMode::Auto) {
|
if (params.execution_mode == ExecutionMode::Auto) {
|
||||||
if (!manager.AddSystem(system)) {
|
if (!manager.AddSystem(audio_system)) {
|
||||||
LOG_ERROR(Service_Audio,
|
LOG_ERROR(Service_Audio, "Both Audio Render sessions are in use, cannot create any more");
|
||||||
"Both Audio Render sessions are in use, cannot create any more");
|
|
||||||
return Service::Audio::ResultOutOfSessions;
|
return Service::Audio::ResultOutOfSessions;
|
||||||
}
|
}
|
||||||
system_registered = true;
|
system_registered = true;
|
||||||
}
|
}
|
||||||
|
audio_system.Initialize(params, transfer_memory, transfer_memory_size, process_handle, applet_resource_user_id, session_id);
|
||||||
initialized = true;
|
|
||||||
system.Initialize(params, transfer_memory, transfer_memory_size, process_handle,
|
|
||||||
applet_resource_user_id, session_id);
|
|
||||||
|
|
||||||
return ResultSuccess;
|
return ResultSuccess;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Renderer::Finalize() {
|
void Renderer::Finalize() {
|
||||||
auto session_id{system.GetSessionId()};
|
auto const session_id{audio_system.GetSessionId()};
|
||||||
|
audio_system.Finalize();
|
||||||
system.Finalize();
|
|
||||||
|
|
||||||
if (system_registered) {
|
if (system_registered) {
|
||||||
manager.RemoveSystem(system);
|
manager.RemoveSystem(audio_system);
|
||||||
system_registered = false;
|
system_registered = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
manager.ReleaseSessionId(session_id);
|
manager.ReleaseSessionId(session_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
System& Renderer::GetSystem() {
|
System& Renderer::GetSystem() {
|
||||||
return system;
|
return audio_system;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Renderer::Start() {
|
void Renderer::Start() {
|
||||||
system.Start();
|
audio_system.Start();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Renderer::Stop() {
|
void Renderer::Stop() {
|
||||||
system.Stop();
|
audio_system.Stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
Result Renderer::RequestUpdate(std::span<const u8> input, std::span<u8> performance,
|
Result Renderer::RequestUpdate(std::span<const u8> input, std::span<u8> performance, std::span<u8> output) {
|
||||||
std::span<u8> output) {
|
return audio_system.Update(input, performance, output);
|
||||||
return system.Update(input, performance, output);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace AudioCore::Renderer
|
} // namespace AudioCore::Renderer
|
||||||
|
|||||||
@@ -1,3 +1,6 @@
|
|||||||
|
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
@@ -83,16 +86,12 @@ public:
|
|||||||
std::span<u8> output);
|
std::span<u8> output);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/// System core
|
|
||||||
Core::System& core;
|
|
||||||
/// Manager this renderer is registered with
|
/// Manager this renderer is registered with
|
||||||
Manager& manager;
|
Manager& manager;
|
||||||
/// Is the audio renderer initialized?
|
|
||||||
bool initialized{};
|
|
||||||
/// Is the system registered with the manager?
|
/// Is the system registered with the manager?
|
||||||
bool system_registered{};
|
bool system_registered{};
|
||||||
/// Audio render system, main driver of audio rendering
|
/// Audio render system, main driver of audio rendering
|
||||||
System system;
|
System audio_system;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace Renderer
|
} // namespace Renderer
|
||||||
|
|||||||
@@ -100,8 +100,9 @@ u64 System::GetWorkBufferSize(const AudioRendererParameterInternal& params) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
System::System(Core::System& core_, Kernel::KEvent* adsp_rendered_event_)
|
System::System(Core::System& core_, Kernel::KEvent* adsp_rendered_event_)
|
||||||
: core{core_}, audio_renderer{core.AudioCore().ADSP().AudioRenderer()},
|
: core{core_}
|
||||||
adsp_rendered_event{adsp_rendered_event_} {}
|
, adsp_rendered_event{adsp_rendered_event_}
|
||||||
|
{}
|
||||||
|
|
||||||
Result System::Initialize(const AudioRendererParameterInternal& params,
|
Result System::Initialize(const AudioRendererParameterInternal& params,
|
||||||
Kernel::KTransferMemory* transfer_memory, u64 transfer_memory_size,
|
Kernel::KTransferMemory* transfer_memory, u64 transfer_memory_size,
|
||||||
@@ -406,7 +407,7 @@ void System::Finalize() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (active) {
|
if (IsActive()) {
|
||||||
Stop();
|
Stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -433,14 +434,12 @@ void System::Start() {
|
|||||||
std::scoped_lock l{lock};
|
std::scoped_lock l{lock};
|
||||||
frames_elapsed = 0;
|
frames_elapsed = 0;
|
||||||
state = State::Started;
|
state = State::Started;
|
||||||
active = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void System::Stop() {
|
void System::Stop() {
|
||||||
{
|
{
|
||||||
std::scoped_lock l{lock};
|
std::scoped_lock l{lock};
|
||||||
state = State::Stopped;
|
state = State::Stopped;
|
||||||
active = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (execution_mode == ExecutionMode::Auto) {
|
if (execution_mode == ExecutionMode::Auto) {
|
||||||
@@ -480,8 +479,7 @@ Result System::Update(std::span<const u8> input, std::span<u8> performance, std:
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
result = info_updater.UpdateEffects(effect_context, active, memory_pool_workbuffer,
|
result = info_updater.UpdateEffects(effect_context, IsActive(), memory_pool_workbuffer, memory_pool_count);
|
||||||
memory_pool_count);
|
|
||||||
if (result.IsError()) {
|
if (result.IsError()) {
|
||||||
LOG_ERROR(Service_Audio, "Failed to update Effects!");
|
LOG_ERROR(Service_Audio, "Failed to update Effects!");
|
||||||
return result;
|
return result;
|
||||||
@@ -582,16 +580,16 @@ u32 System::GetRenderingDevice() const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool System::IsActive() const {
|
bool System::IsActive() const {
|
||||||
return active;
|
return state == State::Started;
|
||||||
}
|
}
|
||||||
|
|
||||||
void System::SendCommandToDsp() {
|
void System::SendCommandToDsp() {
|
||||||
std::scoped_lock l{lock};
|
std::scoped_lock l{lock};
|
||||||
|
auto& audio_renderer = core.AudioCore().ADSP().AudioRenderer();
|
||||||
if (initialized) {
|
if (initialized) {
|
||||||
if (active) {
|
if (IsActive()) {
|
||||||
terminate_event.Reset();
|
terminate_event.Reset();
|
||||||
const auto remaining_command_count{audio_renderer.GetRemainCommandCount(session_id)};
|
const auto remaining_command_count = audio_renderer.GetRemainCommandCount(session_id);
|
||||||
u64 command_size{0};
|
u64 command_size{0};
|
||||||
|
|
||||||
if (remaining_command_count) {
|
if (remaining_command_count) {
|
||||||
@@ -738,7 +736,7 @@ u64 System::GenerateCommand(std::span<u8> in_command_buffer,
|
|||||||
const auto end_time{core.CoreTiming().GetGlobalTimeNs().count()};
|
const auto end_time{core.CoreTiming().GetGlobalTimeNs().count()};
|
||||||
total_ticks_elapsed += end_time - start_time;
|
total_ticks_elapsed += end_time - start_time;
|
||||||
num_command_lists_generated++;
|
num_command_lists_generated++;
|
||||||
render_start_tick = audio_renderer.GetRenderingStartTick(session_id);
|
render_start_tick = core.AudioCore().ADSP().AudioRenderer().GetRenderingStartTick(session_id);
|
||||||
frames_elapsed++;
|
frames_elapsed++;
|
||||||
|
|
||||||
return command_buffer.size;
|
return command_buffer.size;
|
||||||
|
|||||||
@@ -1,3 +1,6 @@
|
|||||||
|
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
@@ -219,12 +222,8 @@ public:
|
|||||||
private:
|
private:
|
||||||
/// Core system
|
/// Core system
|
||||||
Core::System& core;
|
Core::System& core;
|
||||||
/// Reference to the ADSP's AudioRenderer for communication
|
|
||||||
::AudioCore::ADSP::AudioRenderer::AudioRenderer& audio_renderer;
|
|
||||||
/// Is this system initialized?
|
/// Is this system initialized?
|
||||||
bool initialized{};
|
bool initialized{};
|
||||||
/// Is this system currently active?
|
|
||||||
std::atomic<bool> active{};
|
|
||||||
/// State of the system
|
/// State of the system
|
||||||
State state{State::Stopped};
|
State state{State::Stopped};
|
||||||
/// Sample rate for the system
|
/// Sample rate for the system
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||||
@@ -16,22 +16,22 @@
|
|||||||
namespace AudioCore::Renderer {
|
namespace AudioCore::Renderer {
|
||||||
|
|
||||||
SystemManager::SystemManager(Core::System& core_)
|
SystemManager::SystemManager(Core::System& core_)
|
||||||
: core{core_}, audio_renderer{core.AudioCore().ADSP().AudioRenderer()} {}
|
: core{core_}
|
||||||
|
{}
|
||||||
|
|
||||||
SystemManager::~SystemManager() {
|
SystemManager::~SystemManager() {
|
||||||
Stop();
|
Stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
void SystemManager::InitializeUnsafe() {
|
void SystemManager::InitializeUnsafe() {
|
||||||
if (!active) {
|
if (!thread.joinable()) {
|
||||||
active = true;
|
core.AudioCore().ADSP().AudioRenderer().Start();
|
||||||
audio_renderer.Start();
|
|
||||||
thread = std::jthread([this](std::stop_token stop_token) {
|
thread = std::jthread([this](std::stop_token stop_token) {
|
||||||
Common::SetCurrentThreadName("AudioRenderSystemManager");
|
Common::SetCurrentThreadName("AudioRenderSystemManager");
|
||||||
Common::SetCurrentThreadPriority(Common::ThreadPriority::High);
|
auto& audio_renderer = core.AudioCore().ADSP().AudioRenderer();
|
||||||
while (active && !stop_token.stop_requested()) {
|
while (!stop_token.stop_requested()) {
|
||||||
{
|
{
|
||||||
std::scoped_lock l{mutex1};
|
std::scoped_lock lk{mutex};
|
||||||
for (auto system : systems)
|
for (auto system : systems)
|
||||||
system->SendCommandToDsp();
|
system->SendCommandToDsp();
|
||||||
}
|
}
|
||||||
@@ -43,39 +43,31 @@ void SystemManager::InitializeUnsafe() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void SystemManager::Stop() {
|
void SystemManager::Stop() {
|
||||||
if (active) {
|
if (thread.joinable()) {
|
||||||
active = false;
|
|
||||||
thread.request_stop();
|
thread.request_stop();
|
||||||
thread.join();
|
thread.join();
|
||||||
audio_renderer.Stop();
|
core.AudioCore().ADSP().AudioRenderer().Stop();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SystemManager::Add(System& system_) {
|
bool SystemManager::Add(System& system_) {
|
||||||
std::scoped_lock l2{mutex2};
|
std::scoped_lock lk{mutex};
|
||||||
if (systems.size() + 1 > MaxRendererSessions) {
|
if (systems.size() + 1 > MaxRendererSessions) {
|
||||||
LOG_ERROR(Service_Audio, "Maximum AudioRenderer Systems active, cannot add more!");
|
LOG_ERROR(Service_Audio, "Maximum AudioRenderer Systems active, cannot add more!");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
{
|
if (systems.empty())
|
||||||
std::scoped_lock l{mutex1};
|
InitializeUnsafe();
|
||||||
if (systems.empty())
|
|
||||||
InitializeUnsafe();
|
|
||||||
}
|
|
||||||
systems.push_back(&system_);
|
systems.push_back(&system_);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SystemManager::Remove(System& system_) {
|
bool SystemManager::Remove(System& system_) {
|
||||||
std::scoped_lock l2{mutex2};
|
std::scoped_lock lk{mutex};
|
||||||
{
|
if (systems.remove(&system_) == 0) {
|
||||||
std::scoped_lock l{mutex1};
|
LOG_ERROR(Service_Audio, "Failed to remove a render system, it was not found in the list!");
|
||||||
if (systems.remove(&system_) == 0) {
|
return false;
|
||||||
LOG_ERROR(Service_Audio, "Failed to remove a render system, it was not found in the list!");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (systems.empty())
|
if (systems.empty())
|
||||||
Stop();
|
Stop();
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||||
@@ -75,14 +75,8 @@ private:
|
|||||||
std::list<System*> systems{};
|
std::list<System*> systems{};
|
||||||
/// Main worker thread for generating command lists
|
/// Main worker thread for generating command lists
|
||||||
std::jthread thread;
|
std::jthread thread;
|
||||||
/// Mutex for the systems
|
/// Mutex for the systems list
|
||||||
std::mutex mutex1{};
|
std::mutex mutex{};
|
||||||
/// Mutex for adding/removing systems
|
|
||||||
std::mutex mutex2{};
|
|
||||||
/// Is the system manager thread active?
|
|
||||||
std::atomic<bool> active{};
|
|
||||||
/// Reference to the ADSP's AudioRenderer for communication
|
|
||||||
::AudioCore::ADSP::AudioRenderer::AudioRenderer& audio_renderer;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace AudioCore::Renderer
|
} // namespace AudioCore::Renderer
|
||||||
|
|||||||
@@ -110,7 +110,6 @@ add_library(
|
|||||||
socket_types.h
|
socket_types.h
|
||||||
sparse_large_vector.cpp
|
sparse_large_vector.cpp
|
||||||
sparse_large_vector.h
|
sparse_large_vector.h
|
||||||
spin_lock.h
|
|
||||||
stb.cpp
|
stb.cpp
|
||||||
stb.h
|
stb.h
|
||||||
steady_clock.cpp
|
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}
|
, ns_rdtsc_factor{invariant_ ? GetFixedPoint64Factor(NsRatio::den, rdtsc_frequency_) : 0}
|
||||||
, us_rdtsc_factor{invariant_ ? GetFixedPoint64Factor(UsRatio::den, rdtsc_frequency_) : 0}
|
, us_rdtsc_factor{invariant_ ? GetFixedPoint64Factor(UsRatio::den, rdtsc_frequency_) : 0}
|
||||||
, ms_rdtsc_factor{invariant_ ? GetFixedPoint64Factor(MsRatio::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}
|
, cntpct_rdtsc_factor{invariant_ ? GetFixedPoint64Factor(CNTFRQ, rdtsc_frequency_) : 0}
|
||||||
, gputick_rdtsc_factor{invariant_ ? GetFixedPoint64Factor(GPUTickFreq, rdtsc_frequency_) : 0}
|
, gputick_rdtsc_factor{invariant_ ? GetFixedPoint64Factor(GPUTickFreq, rdtsc_frequency_) : 0}
|
||||||
, invariant{invariant_}
|
, invariant{invariant_}
|
||||||
@@ -291,7 +292,7 @@ bool WallClock::IsNative() const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
u64 WallClock::NsToTicks(std::chrono::nanoseconds ns) 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)
|
#elif defined(HAS_NCE)
|
||||||
namespace {
|
namespace {
|
||||||
@@ -416,7 +417,7 @@ u64 WallClock::NsToTicks(std::chrono::nanoseconds ns) const {
|
|||||||
const WallClock g_wall_clock = [] {
|
const WallClock g_wall_clock = [] {
|
||||||
#if defined(ARCHITECTURE_x86_64)
|
#if defined(ARCHITECTURE_x86_64)
|
||||||
auto const& caps = Common::g_cpu_caps;
|
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)
|
#elif defined(HAS_NCE)
|
||||||
return WallClock(false, 1);
|
return WallClock(false, 1);
|
||||||
#else
|
#else
|
||||||
|
|||||||
@@ -96,6 +96,7 @@ public:
|
|||||||
u64 ns_rdtsc_factor;
|
u64 ns_rdtsc_factor;
|
||||||
u64 us_rdtsc_factor;
|
u64 us_rdtsc_factor;
|
||||||
u64 ms_rdtsc_factor;
|
u64 ms_rdtsc_factor;
|
||||||
|
u64 rdtsc_ns_integer;
|
||||||
u64 rdtsc_ns_factor;
|
u64 rdtsc_ns_factor;
|
||||||
u64 cntpct_rdtsc_factor;
|
u64 cntpct_rdtsc_factor;
|
||||||
u64 gputick_rdtsc_factor;
|
u64 gputick_rdtsc_factor;
|
||||||
|
|||||||
+18
-20
@@ -17,35 +17,33 @@
|
|||||||
|
|
||||||
namespace Common {
|
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) {
|
std::string NativeErrorToString(int e) {
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
LPSTR err_str;
|
LPSTR err_str;
|
||||||
|
|
||||||
DWORD res = FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER |
|
DWORD res = FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER |
|
||||||
FORMAT_MESSAGE_IGNORE_INSERTS,
|
FORMAT_MESSAGE_IGNORE_INSERTS,
|
||||||
nullptr, e, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
|
nullptr, e, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
|
||||||
reinterpret_cast<LPSTR>(&err_str), 1, nullptr);
|
LPSTR(&err_str), 1, nullptr);
|
||||||
if (!res) {
|
if (res) {
|
||||||
return "(FormatMessageA failed to format error)";
|
std::string ret(err_str);
|
||||||
|
LocalFree(err_str);
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
std::string ret(err_str);
|
return "(FormatMessageA failed to format error)";
|
||||||
LocalFree(err_str);
|
|
||||||
return ret;
|
|
||||||
#else
|
#else
|
||||||
char err_str[255];
|
char err_str[255];
|
||||||
#if defined(__ANDROID__) || \
|
return HandleStrerrorR(strerror_r(e, err_str, sizeof(err_str)), err_str);
|
||||||
(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.
|
|
||||||
#endif // _WIN32
|
#endif // _WIN32
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+66
-13
@@ -5,6 +5,7 @@
|
|||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
#include <cctype>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include "common/container/unordered_map.h"
|
#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; }),
|
[type2](char c1, char c2) { return c1 == type2 && c2 == type2; }),
|
||||||
path.end());
|
path.end());
|
||||||
|
|
||||||
const bool absolute = !path.empty() && path[0] == type2;
|
std::string root;
|
||||||
std::vector<std::string_view> parts;
|
std::string_view components{path};
|
||||||
|
bool drive_relative = false;
|
||||||
|
|
||||||
for (const auto part : SplitPathComponents(path))
|
#ifdef _WIN32
|
||||||
{
|
const bool network = path.size() > 1 && path[0] == type2 && path[1] == type2;
|
||||||
if (part.empty() || part == ".")
|
const bool drive =
|
||||||
continue;
|
path.size() > 1 && std::isalpha(static_cast<unsigned char>(path[0])) && path[1] == ':';
|
||||||
if (part == ".." && !parts.empty() && parts.back() != "..")
|
|
||||||
parts.pop_back();
|
if (network) {
|
||||||
else if (part != "..") parts.push_back(part);
|
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{};
|
const auto path_parts = SplitPathComponents(components);
|
||||||
for (std::size_t i = 0; i < parts.size(); ++i)
|
std::size_t root_component_count = 0;
|
||||||
{
|
#ifdef _WIN32
|
||||||
if (i != 0)
|
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 += type2;
|
||||||
resolved.append(parts[i].data(), parts[i].size());
|
resolved.append(parts[i].data(), parts[i].size());
|
||||||
}
|
}
|
||||||
|
|
||||||
path = std::move(resolved);
|
path = std::move(resolved);
|
||||||
|
|
||||||
|
if (!path.empty() && path.size() == root_length) {
|
||||||
|
return path;
|
||||||
|
}
|
||||||
return std::string(RemoveTrailingSlash(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" }
|
// 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);
|
[[nodiscard]] std::vector<std::string> SplitPathComponentsCopy(std::string_view filename);
|
||||||
|
|
||||||
// Removes trailing slash, makes all '\\' into '/', and removes duplicate '/'. Makes '/' into '\\'
|
// Normalizes directory separators, removes duplicate and non-root trailing separators, and resolves
|
||||||
// depending if directory_separator is BackwardSlash or PlatformDefault and running on windows
|
// '.' and '..' components without traversing above the path root. Windows drive and UNC roots are
|
||||||
|
// preserved.
|
||||||
[[nodiscard]] std::string SanitizePath(
|
[[nodiscard]] std::string SanitizePath(
|
||||||
std::string_view path,
|
std::string_view path,
|
||||||
DirectorySeparator directory_separator = DirectorySeparator::ForwardSlash);
|
DirectorySeparator directory_separator = DirectorySeparator::ForwardSlash);
|
||||||
|
|||||||
@@ -96,15 +96,15 @@ struct PageTable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Write page info atomically
|
/// 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}));
|
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);
|
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);
|
Store(true, PageType::DebugMemory, block, ptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -38,7 +38,8 @@ void FreeMemoryPages(void* base, std::size_t size) noexcept;
|
|||||||
|
|
||||||
/// A large page-aligned buffer that has optimized memory usage for zero-writes.
|
/// A large page-aligned buffer that has optimized memory usage for zero-writes.
|
||||||
template <typename T>
|
template <typename T>
|
||||||
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 {
|
class SparseLargeVector final {
|
||||||
public:
|
public:
|
||||||
constexpr SparseLargeVector() = default;
|
constexpr SparseLargeVector() = default;
|
||||||
|
|||||||
@@ -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;
|
return uuid;
|
||||||
}
|
}
|
||||||
|
|
||||||
UUID UUID::MakeRFC4122V5(std::span<u8, 20> sha1) {
|
UUID UUID::MakeRFC4122V5(std::span<u8, 16> sha1) {
|
||||||
UUID uuid{};
|
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[8] = 0x80 | (uuid.uuid[8] & 0x3F);
|
||||||
uuid.uuid[6] = 0x50 | (uuid.uuid[6] & 0xF);
|
uuid.uuid[6] = 0x50 | (uuid.uuid[6] & 0xF);
|
||||||
return uuid;
|
return uuid;
|
||||||
|
|||||||
+1
-1
@@ -104,7 +104,7 @@ struct UUID {
|
|||||||
/// @returns A random UUID that is RFC 4122 Version 4 compliant.
|
/// @returns A random UUID that is RFC 4122 Version 4 compliant.
|
||||||
[[nodiscard]] static UUID MakeRandomRFC4122V4();
|
[[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;
|
friend constexpr bool operator==(const UUID& lhs, const UUID& rhs) = default;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -796,6 +796,8 @@ add_library(core STATIC
|
|||||||
hle/service/ns/application_manager_interface.h
|
hle/service/ns/application_manager_interface.h
|
||||||
hle/service/ns/application_version_interface.cpp
|
hle/service/ns/application_version_interface.cpp
|
||||||
hle/service/ns/application_version_interface.h
|
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.cpp
|
||||||
hle/service/ns/content_management_interface.h
|
hle/service/ns/content_management_interface.h
|
||||||
hle/service/ns/develop_interface.cpp
|
hle/service/ns/develop_interface.cpp
|
||||||
@@ -810,8 +812,6 @@ add_library(core STATIC
|
|||||||
hle/service/ns/ecommerce_interface.h
|
hle/service/ns/ecommerce_interface.h
|
||||||
hle/service/ns/factory_reset_interface.cpp
|
hle/service/ns/factory_reset_interface.cpp
|
||||||
hle/service/ns/factory_reset_interface.h
|
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.cpp
|
||||||
hle/service/ns/language.h
|
hle/service/ns/language.h
|
||||||
hle/service/ns/ns.cpp
|
hle/service/ns/ns.cpp
|
||||||
|
|||||||
@@ -22,21 +22,15 @@ DynarmicCallbacks32::DynarmicCallbacks32(ArmDynarmic32& parent, Kernel::KProcess
|
|||||||
, m_check_memory_access{m_debugger_enabled || !Settings::values.cpuopt_ignore_memory_aborts.GetValue()}
|
, m_check_memory_access{m_debugger_enabled || !Settings::values.cpuopt_ignore_memory_aborts.GetValue()}
|
||||||
{}
|
{}
|
||||||
|
|
||||||
u8 DynarmicCallbacks32::MemoryRead8(u32 vaddr) {
|
u64 DynarmicCallbacks32::MemoryRead(u32 vaddr, size_t size) {
|
||||||
CheckMemoryAccess(vaddr, 1, Kernel::DebugWatchpointType::Read);
|
CheckMemoryAccess(vaddr, size, Kernel::DebugWatchpointType::Read);
|
||||||
return m_memory.Read8(vaddr);
|
switch (size) {
|
||||||
}
|
case sizeof(u64): return m_memory.Read64(vaddr);
|
||||||
u16 DynarmicCallbacks32::MemoryRead16(u32 vaddr) {
|
case sizeof(u32): return m_memory.Read32(vaddr);
|
||||||
CheckMemoryAccess(vaddr, 2, Kernel::DebugWatchpointType::Read);
|
case sizeof(u16): return m_memory.Read16(vaddr);
|
||||||
return m_memory.Read16(vaddr);
|
case sizeof(u8): return m_memory.Read8(vaddr);
|
||||||
}
|
default: UNREACHABLE();
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<u32> DynarmicCallbacks32::MemoryReadCode(u32 vaddr) {
|
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)];
|
return cached_code_page.inst[(vaddr & Core::Memory::YUZU_PAGEMASK) / sizeof(u32)];
|
||||||
}
|
}
|
||||||
|
|
||||||
void DynarmicCallbacks32::MemoryWrite8(u32 vaddr, u8 value) {
|
void DynarmicCallbacks32::MemoryWrite(Dynarmic::A32::VAddr vaddr, u64 value, size_t size) {
|
||||||
if (CheckMemoryAccess(vaddr, 1, Kernel::DebugWatchpointType::Write)) {
|
if (CheckMemoryAccess(vaddr, size, Kernel::DebugWatchpointType::Write)) {
|
||||||
m_memory.Write8(vaddr, value);
|
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) {
|
bool DynarmicCallbacks32::MemoryWriteExclusive8(u32 vaddr, u8 value, u8 expected) {
|
||||||
return CheckMemoryAccess(vaddr, 1, Kernel::DebugWatchpointType::Write) &&
|
return CheckMemoryAccess(vaddr, 1, Kernel::DebugWatchpointType::Write) &&
|
||||||
m_memory.WriteExclusive8(vaddr, value, expected);
|
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>*>(
|
config.page_table = reinterpret_cast<std::array<std::uint8_t*, NumPageTableEntries>*>(
|
||||||
const_cast<Common::PageTable::PageEntryData*>(page_table->entries.data()));
|
const_cast<Common::PageTable::PageEntryData*>(page_table->entries.data()));
|
||||||
config.page_table_pointer_mask = Common::PageTable::ATTRIBUTE_MASK;
|
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.absolute_offset_page_table = true;
|
||||||
config.detect_misaligned_access_via_page_table = 16 | 32 | 64 | 128;
|
config.detect_misaligned_access_via_page_table = 16 | 32 | 64 | 128;
|
||||||
config.only_detect_misalignment_via_page_table_on_page_boundary = true;
|
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)) {
|
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,
|
// 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.
|
// 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 {
|
class DynarmicCallbacks32 : public Dynarmic::A32::UserCallbacks {
|
||||||
public:
|
public:
|
||||||
explicit DynarmicCallbacks32(ArmDynarmic32& parent, Kernel::KProcess* process);
|
explicit DynarmicCallbacks32(ArmDynarmic32& parent, Kernel::KProcess* process);
|
||||||
u8 MemoryRead8(u32 vaddr) override;
|
u64 MemoryRead(u32 vaddr, size_t size) override;
|
||||||
u16 MemoryRead16(u32 vaddr) override;
|
|
||||||
u32 MemoryRead32(u32 vaddr) override;
|
|
||||||
u64 MemoryRead64(u32 vaddr) override;
|
|
||||||
std::optional<u32> MemoryReadCode(u32 vaddr) override;
|
std::optional<u32> MemoryReadCode(u32 vaddr) override;
|
||||||
void InstructionSynchronizationBarrierRaised() override {
|
void InstructionSynchronizationBarrierRaised() override {
|
||||||
last_code_addr = u64(-1); //reset back, force refetch
|
last_code_addr = u64(-1); //reset back, force refetch
|
||||||
}
|
}
|
||||||
void MemoryWrite8(u32 vaddr, u8 value) override;
|
void MemoryWrite(Dynarmic::A32::VAddr vaddr, u64 value, size_t size) override;
|
||||||
void MemoryWrite16(u32 vaddr, u16 value) override;
|
|
||||||
void MemoryWrite32(u32 vaddr, u32 value) override;
|
|
||||||
void MemoryWrite64(u32 vaddr, u64 value) override;
|
|
||||||
bool MemoryWriteExclusive8(u32 vaddr, u8 value, u8 expected) override;
|
bool MemoryWriteExclusive8(u32 vaddr, u8 value, u8 expected) override;
|
||||||
bool MemoryWriteExclusive16(u32 vaddr, u16 value, u16 expected) override;
|
bool MemoryWriteExclusive16(u32 vaddr, u16 value, u16 expected) override;
|
||||||
bool MemoryWriteExclusive32(u32 vaddr, u32 value, u32 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/arm/dynarmic/dynarmic_exclusive_monitor.h"
|
||||||
#include "core/core_timing.h"
|
#include "core/core_timing.h"
|
||||||
#include "core/hle/kernel/k_process.h"
|
#include "core/hle/kernel/k_process.h"
|
||||||
|
#include "dynarmic/interface/A64/config.h"
|
||||||
|
|
||||||
namespace Core {
|
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()}
|
, m_check_memory_access{m_debugger_enabled || !Settings::values.cpuopt_ignore_memory_aborts.GetValue()}
|
||||||
{}
|
{}
|
||||||
|
|
||||||
u8 DynarmicCallbacks64::MemoryRead8(u64 vaddr) {
|
u64 DynarmicCallbacks64::MemoryRead(u64 vaddr, size_t size) {
|
||||||
CheckMemoryAccess(vaddr, 1, Kernel::DebugWatchpointType::Read);
|
CheckMemoryAccess(vaddr, size, Kernel::DebugWatchpointType::Read);
|
||||||
return m_memory.Read8(vaddr);
|
switch (size) {
|
||||||
}
|
case sizeof(u64): return m_memory.Read64(vaddr);
|
||||||
u16 DynarmicCallbacks64::MemoryRead16(u64 vaddr) {
|
case sizeof(u32): return m_memory.Read32(vaddr);
|
||||||
CheckMemoryAccess(vaddr, 2, Kernel::DebugWatchpointType::Read);
|
case sizeof(u16): return m_memory.Read16(vaddr);
|
||||||
return m_memory.Read16(vaddr);
|
case sizeof(u8): return m_memory.Read8(vaddr);
|
||||||
}
|
default: UNREACHABLE();
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
Dynarmic::A64::Vector DynarmicCallbacks64::MemoryRead128(u64 vaddr) {
|
Dynarmic::A64::Vector DynarmicCallbacks64::MemoryRead128(u64 vaddr) {
|
||||||
CheckMemoryAccess(vaddr, 16, Kernel::DebugWatchpointType::Read);
|
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)];
|
return cached_code_page.inst[(vaddr & Core::Memory::YUZU_PAGEMASK) / sizeof(u32)];
|
||||||
}
|
}
|
||||||
|
|
||||||
void DynarmicCallbacks64::MemoryWrite8(u64 vaddr, u8 value) {
|
void DynarmicCallbacks64::MemoryWrite(Dynarmic::A64::VAddr vaddr, u64 value, std::size_t size) {
|
||||||
if (CheckMemoryAccess(vaddr, 1, Kernel::DebugWatchpointType::Write)) {
|
if (CheckMemoryAccess(vaddr, size, Kernel::DebugWatchpointType::Write)) {
|
||||||
m_memory.Write8(vaddr, value);
|
switch (size) {
|
||||||
}
|
case sizeof(u64): return m_memory.Write64(vaddr, u64(value));
|
||||||
}
|
case sizeof(u32): return m_memory.Write32(vaddr, u32(value));
|
||||||
void DynarmicCallbacks64::MemoryWrite16(u64 vaddr, u16 value) {
|
case sizeof(u16): return m_memory.Write16(vaddr, u16(value));
|
||||||
if (CheckMemoryAccess(vaddr, 2, Kernel::DebugWatchpointType::Write)) {
|
case sizeof(u8): return m_memory.Write8(vaddr, u8(value));
|
||||||
m_memory.Write16(vaddr, value);
|
default: UNREACHABLE();
|
||||||
}
|
}
|
||||||
}
|
|
||||||
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::MemoryWrite128(u64 vaddr, Dynarmic::A64::Vector value) {
|
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()));
|
const_cast<Common::PageTable::PageEntryData*>(page_table->entries.data()));
|
||||||
config.page_table_address_space_bits = std::uint32_t(address_space_bits);
|
config.page_table_address_space_bits = std::uint32_t(address_space_bits);
|
||||||
config.page_table_pointer_mask = Common::PageTable::ATTRIBUTE_MASK;
|
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.silently_mirror_page_table = false;
|
||||||
config.absolute_offset_page_table = true;
|
config.absolute_offset_page_table = true;
|
||||||
config.detect_misaligned_access_via_page_table = 16 | 32 | 64 | 128;
|
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)) {
|
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,
|
// 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.
|
// 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 "common/hash.h"
|
||||||
#include "core/arm/arm_interface.h"
|
#include "core/arm/arm_interface.h"
|
||||||
#include "core/arm/dynarmic/dynarmic_exclusive_monitor.h"
|
#include "core/arm/dynarmic/dynarmic_exclusive_monitor.h"
|
||||||
|
#include "dynarmic/interface/A64/config.h"
|
||||||
|
|
||||||
namespace Core::Memory {
|
namespace Core::Memory {
|
||||||
class Memory;
|
class Memory;
|
||||||
@@ -36,19 +37,13 @@ class DynarmicCallbacks64 : public Dynarmic::A64::UserCallbacks {
|
|||||||
public:
|
public:
|
||||||
explicit DynarmicCallbacks64(ArmDynarmic64& parent, Kernel::KProcess* process);
|
explicit DynarmicCallbacks64(ArmDynarmic64& parent, Kernel::KProcess* process);
|
||||||
|
|
||||||
u8 MemoryRead8(u64 vaddr) override;
|
u64 MemoryRead(u64 vaddr, size_t size) override;
|
||||||
u16 MemoryRead16(u64 vaddr) override;
|
|
||||||
u32 MemoryRead32(u64 vaddr) override;
|
|
||||||
u64 MemoryRead64(u64 vaddr) override;
|
|
||||||
Dynarmic::A64::Vector MemoryRead128(u64 vaddr) override;
|
Dynarmic::A64::Vector MemoryRead128(u64 vaddr) override;
|
||||||
std::optional<u32> MemoryReadCode(u64 vaddr) override;
|
std::optional<u32> MemoryReadCode(u64 vaddr) override;
|
||||||
void InstructionSynchronizationBarrierRaised() override {
|
void InstructionSynchronizationBarrierRaised() override {
|
||||||
last_code_addr = u64(-1); //reset back, force refetch
|
last_code_addr = u64(-1); //reset back, force refetch
|
||||||
}
|
}
|
||||||
void MemoryWrite8(u64 vaddr, u8 value) override;
|
void MemoryWrite(Dynarmic::A64::VAddr vaddr, u64 value, std::size_t size) override;
|
||||||
void MemoryWrite16(u64 vaddr, u16 value) override;
|
|
||||||
void MemoryWrite32(u64 vaddr, u32 value) override;
|
|
||||||
void MemoryWrite64(u64 vaddr, u64 value) override;
|
|
||||||
void MemoryWrite128(u64 vaddr, Dynarmic::A64::Vector value) override;
|
void MemoryWrite128(u64 vaddr, Dynarmic::A64::Vector value) override;
|
||||||
bool MemoryWriteExclusive8(u64 vaddr, std::uint8_t value, std::uint8_t expected) 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;
|
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) {
|
static bool IsEOF(IPSFileType type, std::span<const u8> magic) {
|
||||||
return (type == IPSFileType::IPS && magic.size() > 3 && std::memcmp(magic.data(), "EOF", 3) == 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);
|
|| (type == IPSFileType::IPS32 && magic.size() >= 4 && std::memcmp(magic.data(), "EEOF", 4) == 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
VirtualFile PatchIPS(const VirtualFile& in, const VirtualFile& ips) {
|
VirtualFile PatchIPS(const VirtualFile& in, const VirtualFile& ips) {
|
||||||
if (in == nullptr || ips == nullptr)
|
if (in == nullptr || ips == nullptr)
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
auto in_data = in->ReadAllBytes();
|
const auto type = IdentifyMagic(ips->ReadBytes(0x5));
|
||||||
auto const type = IdentifyMagic(in_data);
|
|
||||||
if (type == IPSFileType::Error)
|
if (type == IPSFileType::Error)
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
|
auto in_data = in->ReadAllBytes();
|
||||||
|
if (in_data.size() == 0)
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
std::vector<u8> temp(type == IPSFileType::IPS ? 3 : 4);
|
std::vector<u8> temp(type == IPSFileType::IPS ? 3 : 4);
|
||||||
u64 offset = 5; // After header
|
u64 offset = 5; // After header
|
||||||
while (ips->Read(temp.data(), temp.size(), offset) == temp.size()) {
|
while (ips->Read(temp.data(), temp.size(), offset) == temp.size()) {
|
||||||
@@ -100,8 +103,7 @@ VirtualFile PatchIPS(const VirtualFile& in, const VirtualFile& ips) {
|
|||||||
|
|
||||||
|
|
||||||
struct IPSwitchRecord {
|
struct IPSwitchRecord {
|
||||||
std::array<uint8_t, 256 - sizeof(size_t)> data;
|
std::vector<uint8_t> data;
|
||||||
size_t count;
|
|
||||||
};
|
};
|
||||||
struct IPSwitchCompiler::IPSwitchPatch {
|
struct IPSwitchCompiler::IPSwitchPatch {
|
||||||
::Common::unordered_map<u32, IPSwitchRecord> records;
|
::Common::unordered_map<u32, IPSwitchRecord> records;
|
||||||
@@ -122,22 +124,23 @@ static IPSwitchRecord EscapeStringSequences(std::string_view sv) {
|
|||||||
IPSwitchRecord r{};
|
IPSwitchRecord r{};
|
||||||
for (auto it = sv.cbegin(); it < sv.cend(); ) {
|
for (auto it = sv.cbegin(); it < sv.cend(); ) {
|
||||||
if (*it == '\\' && it + 1 < sv.cend()) {
|
if (*it == '\\' && it + 1 < sv.cend()) {
|
||||||
switch (it[1]) {
|
r.data.push_back([it]() {
|
||||||
case 'a': r.data[r.count] = '\a'; break;
|
switch (it[1]) {
|
||||||
case 'b': r.data[r.count] = '\b'; break;
|
case 'a': return '\a';
|
||||||
case 'e': r.data[r.count] = '\e'; break;
|
case 'b': return '\b';
|
||||||
case 'f': r.data[r.count] = '\f'; break;
|
case 'e': return '\e';
|
||||||
case 'n': r.data[r.count] = '\n'; break;
|
case 'f': return '\f';
|
||||||
case 'r': r.data[r.count] = '\r'; break;
|
case 'n': return '\n';
|
||||||
case 't': r.data[r.count] = '\t'; break;
|
case 'r': return '\r';
|
||||||
case 'v': r.data[r.count] = '\v'; break;
|
case 't': return '\t';
|
||||||
case '?': r.data[r.count] = '\?'; break;
|
case 'v': return '\v';
|
||||||
default: r.data[r.count] = it[1]; break;
|
case '?': return '\?';
|
||||||
}
|
default: return it[1];
|
||||||
++r.count;
|
}
|
||||||
|
}());
|
||||||
it += 2;
|
it += 2;
|
||||||
} else {
|
} else {
|
||||||
++r.count;
|
r.data.push_back(*it);
|
||||||
++it;
|
++it;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -223,8 +226,8 @@ void IPSwitchCompiler::Parse(std::span<u8 const> bytes) {
|
|||||||
if (start <= line.cend() && end <= line.cend()) {
|
if (start <= line.cend() && end <= line.cend()) {
|
||||||
// Actually IPS wants ordering from {lsb, ..., msb} -- so LE and BE are inverted, fun!
|
// Actually IPS wants ordering from {lsb, ..., msb} -- so LE and BE are inverted, fun!
|
||||||
auto const hs = Common::HexStringToVector({start, end}, is_little_endian);
|
auto const hs = Common::HexStringToVector({start, end}, is_little_endian);
|
||||||
|
r.data.resize(hs.size());
|
||||||
std::memcpy(r.data.data(), hs.data(), hs.size());
|
std::memcpy(r.data.data(), hs.data(), hs.size());
|
||||||
r.count = hs.size();
|
|
||||||
LOG_INFO(Loader, "[H] value @ {:#08X}", offset);
|
LOG_INFO(Loader, "[H] value @ {:#08X}", offset);
|
||||||
patches.back().records.insert_or_assign(u32(offset), std::move(r));
|
patches.back().records.insert_or_assign(u32(offset), std::move(r));
|
||||||
} else {
|
} else {
|
||||||
@@ -293,7 +296,7 @@ VirtualFile IPSwitchCompiler::Apply(const VirtualFile& in) const {
|
|||||||
if (patch.enabled) {
|
if (patch.enabled) {
|
||||||
for (const auto& record : patch.records) {
|
for (const auto& record : patch.records) {
|
||||||
if (record.first < in_data.size()) {
|
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())
|
if (record.first + replace_size > in_data.size())
|
||||||
replace_size = in_data.size() - record.first;
|
replace_size = in_data.size() - record.first;
|
||||||
std::memcpy(in_data.data() + record.first, record.second.data.data(), replace_size);
|
std::memcpy(in_data.data() + record.first, record.second.data.data(), replace_size);
|
||||||
|
|||||||
@@ -9,6 +9,7 @@
|
|||||||
#include <cstddef>
|
#include <cstddef>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
|
||||||
|
#include "common/assert.h"
|
||||||
#include "common/hex_util.h"
|
#include "common/hex_util.h"
|
||||||
#include "common/logging.h"
|
#include "common/logging.h"
|
||||||
#include "common/settings.h"
|
#include "common/settings.h"
|
||||||
@@ -156,12 +157,15 @@ std::string GetUpdateVersionStringFromSlot(const ContentProvider* provider, u64
|
|||||||
NACP nacp{nacp_file};
|
NACP nacp{nacp_file};
|
||||||
return nacp.GetVersionString();
|
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
|
} // Anonymous namespace
|
||||||
|
|
||||||
PatchManager::PatchManager(u64 title_id_,
|
PatchManager::PatchManager(u64 title_id_,
|
||||||
const Service::FileSystem::FileSystemController& fs_controller_,
|
const Service::FileSystem::FileSystemController& fs_controller_,
|
||||||
const ContentProvider& content_provider_)
|
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;
|
PatchManager::~PatchManager() = default;
|
||||||
|
|
||||||
@@ -169,13 +173,35 @@ u64 PatchManager::GetTitleID() const {
|
|||||||
return title_id;
|
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 {
|
VirtualDir PatchManager::PatchExeFS(VirtualDir exefs) const {
|
||||||
LOG_INFO(Loader, "Patching ExeFS for title_id={:016X}", title_id);
|
LOG_INFO(Loader, "Patching ExeFS for title_id={:016X}", title_id);
|
||||||
|
|
||||||
if (exefs == nullptr)
|
if (exefs == nullptr)
|
||||||
return exefs;
|
return exefs;
|
||||||
|
|
||||||
const auto& disabled = Settings::values.disabled_addons[title_id];
|
const auto disabled = GetDisabledAddons();
|
||||||
|
|
||||||
bool update_disabled = true;
|
bool update_disabled = true;
|
||||||
std::optional<u32> enabled_version;
|
std::optional<u32> enabled_version;
|
||||||
@@ -303,8 +329,8 @@ VirtualDir PatchManager::PatchExeFS(VirtualDir exefs) const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// LayeredExeFS
|
// LayeredExeFS
|
||||||
const auto load_dir = fs_controller.GetModificationLoadRoot(title_id);
|
const auto load_dir = GetModificationLoadRoot();
|
||||||
const auto sdmc_load_dir = fs_controller.GetSDMCModificationLoadRoot(title_id);
|
const auto sdmc_load_dir = GetModificationLoadRoot(true);
|
||||||
|
|
||||||
std::vector<VirtualDir> patch_dirs = {sdmc_load_dir};
|
std::vector<VirtualDir> patch_dirs = {sdmc_load_dir};
|
||||||
if (load_dir != nullptr) {
|
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 {
|
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);
|
const auto nso_build_id = fmt::format("{:0<64}", build_id);
|
||||||
|
|
||||||
std::vector<VirtualFile> out;
|
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);
|
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) {
|
if (load_dir == nullptr) {
|
||||||
LOG_ERROR(Loader, "Cannot load mods for invalid title_id={:016X}", title_id);
|
LOG_ERROR(Loader, "Cannot load mods for invalid title_id={:016X}", title_id);
|
||||||
return nso;
|
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);
|
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) {
|
if (load_dir == nullptr) {
|
||||||
LOG_ERROR(Loader, "Cannot load mods for invalid title_id={:016X}", title_id);
|
LOG_ERROR(Loader, "Cannot load mods for invalid title_id={:016X}", title_id);
|
||||||
return false;
|
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 {
|
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) {
|
if (load_dir == nullptr) {
|
||||||
LOG_ERROR(Loader, "Cannot load mods for invalid title_id={:016X}", title_id);
|
LOG_ERROR(Loader, "Cannot load mods for invalid title_id={:016X}", title_id);
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto& disabled = Settings::values.disabled_addons[title_id];
|
const auto disabled = GetDisabledAddons();
|
||||||
auto patch_dirs = load_dir->GetSubdirectories();
|
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(); });
|
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;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void ApplyLayeredFS(VirtualFile& romfs, u64 title_id, ContentRecordType type,
|
void PatchManager::ApplyLayeredFS(VirtualFile& romfs, ContentRecordType type) const {
|
||||||
const Service::FileSystem::FileSystemController& fs_controller) {
|
const auto load_dir = GetModificationLoadRoot();
|
||||||
const auto load_dir = fs_controller.GetModificationLoadRoot(title_id);
|
const auto sdmc_load_dir = GetModificationLoadRoot(true);
|
||||||
const auto sdmc_load_dir = fs_controller.GetSDMCModificationLoadRoot(title_id);
|
|
||||||
if ((type != ContentRecordType::Program && type != ContentRecordType::Data &&
|
if ((type != ContentRecordType::Program && type != ContentRecordType::Data &&
|
||||||
type != ContentRecordType::HtmlDocument) ||
|
type != ContentRecordType::HtmlDocument) ||
|
||||||
(load_dir == nullptr && sdmc_load_dir == nullptr)) {
|
(load_dir == nullptr && sdmc_load_dir == nullptr)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto& disabled = Settings::values.disabled_addons[title_id];
|
const auto disabled = GetDisabledAddons();
|
||||||
std::vector<VirtualDir> patch_dirs = load_dir->GetSubdirectories();
|
std::vector<VirtualDir> patch_dirs = load_dir->GetSubdirectories();
|
||||||
if (std::find(disabled.cbegin(), disabled.cend(), "SDMC") == disabled.cend()) {
|
if (std::find(disabled.cbegin(), disabled.cend(), "SDMC") == disabled.cend()) {
|
||||||
patch_dirs.push_back(sdmc_load_dir);
|
patch_dirs.push_back(sdmc_load_dir);
|
||||||
@@ -591,7 +616,7 @@ VirtualFile PatchManager::PatchRomFS(const NCA* base_nca, VirtualFile base_romfs
|
|||||||
|
|
||||||
// Game Updates
|
// Game Updates
|
||||||
const auto update_tid = GetUpdateTitleID(title_id);
|
const auto update_tid = GetUpdateTitleID(title_id);
|
||||||
const auto& disabled = Settings::values.disabled_addons[title_id];
|
const auto disabled = GetDisabledAddons();
|
||||||
|
|
||||||
bool update_disabled = true;
|
bool update_disabled = true;
|
||||||
std::optional<u32> enabled_version;
|
std::optional<u32> enabled_version;
|
||||||
@@ -698,7 +723,7 @@ VirtualFile PatchManager::PatchRomFS(const NCA* base_nca, VirtualFile base_romfs
|
|||||||
|
|
||||||
// LayeredFS
|
// LayeredFS
|
||||||
if (apply_layeredfs) {
|
if (apply_layeredfs) {
|
||||||
ApplyLayeredFS(romfs, title_id, type, fs_controller);
|
ApplyLayeredFS(romfs, type);
|
||||||
}
|
}
|
||||||
|
|
||||||
return romfs;
|
return romfs;
|
||||||
@@ -1072,15 +1097,22 @@ std::vector<Patch> PatchManager::GetPatches(VirtualFile update_raw) const {
|
|||||||
std::optional<u32> PatchManager::GetGameVersion() const {
|
std::optional<u32> PatchManager::GetGameVersion() const {
|
||||||
const auto update_tid = GetUpdateTitleID(title_id);
|
const auto update_tid = GetUpdateTitleID(title_id);
|
||||||
if (content_provider.HasEntry(update_tid, ContentRecordType::Program)) {
|
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 {
|
PatchManager::Metadata PatchManager::GetControlMetadata() const {
|
||||||
const auto base_control_nca = content_provider.GetEntry(title_id, ContentRecordType::Control);
|
const auto base_control_nca = content_provider.GetEntry(title_id, ContentRecordType::Control);
|
||||||
if (base_control_nca == nullptr) {
|
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 {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -109,10 +109,14 @@ public:
|
|||||||
[[nodiscard]] static PatchManager::Metadata GetMetadataFromBaseOrUpdate(Core::System& system, u64 application_id) noexcept;
|
[[nodiscard]] static PatchManager::Metadata GetMetadataFromBaseOrUpdate(Core::System& system, u64 application_id) noexcept;
|
||||||
|
|
||||||
private:
|
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,
|
[[nodiscard]] std::vector<VirtualFile> CollectPatches(const std::vector<VirtualDir>& patch_dirs,
|
||||||
const std::string& build_id) const;
|
const std::string& build_id) const;
|
||||||
|
|
||||||
u64 title_id;
|
u64 title_id;
|
||||||
|
std::optional<u64> parent_title_id;
|
||||||
const Service::FileSystem::FileSystemController& fs_controller;
|
const Service::FileSystem::FileSystemController& fs_controller;
|
||||||
const ContentProvider& content_provider;
|
const ContentProvider& content_provider;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
#include <limits>
|
||||||
#include <random>
|
#include <random>
|
||||||
#include <regex>
|
#include <regex>
|
||||||
#include <openssl/evp.h>
|
#include <openssl/evp.h>
|
||||||
@@ -348,6 +349,22 @@ std::vector<ContentProviderEntry> ContentProvider::ListEntries() const {
|
|||||||
return ListEntriesFilter(std::nullopt, std::nullopt, std::nullopt);
|
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_)) {}
|
PlaceholderCache::PlaceholderCache(VirtualDir dir_) : dir(std::move(dir_)) {}
|
||||||
|
|
||||||
bool PlaceholderCache::Create(const NcaID& id, u64 size) const {
|
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<TitleType> title_type = {}, std::optional<ContentRecordType> record_type = {},
|
||||||
std::optional<u64> title_id = {}) const = 0;
|
std::optional<u64> title_id = {}) const = 0;
|
||||||
|
|
||||||
|
[[nodiscard]] std::optional<u64> GetParentApplicationId(u64 program_id) const;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
// A single instance of KeyManager to be used by GetEntry()
|
// A single instance of KeyManager to be used by GetEntry()
|
||||||
Core::Crypto::KeyManager& keys = Core::Crypto::KeyManager::Instance();
|
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-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
|||||||
@@ -12,6 +12,7 @@
|
|||||||
#include "hid_core/frontend/emulated_controller.h"
|
#include "hid_core/frontend/emulated_controller.h"
|
||||||
#include "hid_core/hid_core.h"
|
#include "hid_core/hid_core.h"
|
||||||
#include "hid_core/hid_types.h"
|
#include "hid_core/hid_types.h"
|
||||||
|
#include <array>
|
||||||
|
|
||||||
namespace Core::Frontend {
|
namespace Core::Frontend {
|
||||||
|
|
||||||
@@ -29,23 +30,39 @@ void DefaultControllerApplet::ReconfigureControllers(ReconfigureCallback callbac
|
|||||||
|
|
||||||
const std::size_t min_supported_players =
|
const std::size_t min_supported_players =
|
||||||
parameters.enable_single_mode ? 1 : parameters.min_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);
|
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.
|
// Deduce the best configuration based on the input parameters.
|
||||||
for (std::size_t index = 0; index < hid_core.available_controllers - 2; ++index) {
|
for (std::size_t index = 0; index < hid_core.available_controllers - 2; ++index) {
|
||||||
auto* controller = hid_core.GetEmulatedControllerByIndex(index);
|
auto* controller = hid_core.GetEmulatedControllerByIndex(index);
|
||||||
|
|
||||||
// First, disconnect all controllers regardless of the value of keep_controllers_connected.
|
if (keep_connected[index]) continue;
|
||||||
// This makes it easy to connect the desired controllers.
|
|
||||||
controller->Disconnect();
|
controller->Disconnect();
|
||||||
|
|
||||||
// Only connect the minimum number of required players.
|
// only add players still needed to reach the minimum
|
||||||
if (index >= min_supported_players) {
|
if (num_selected_players >= min_supported_players) continue;
|
||||||
continue;
|
++num_selected_players;
|
||||||
}
|
|
||||||
|
|
||||||
// Connect controllers based on the following priority list from highest to lowest priority:
|
// Connect controllers based on the following priority list from highest to lowest priority:
|
||||||
// Pro Controller -> Dual Joycons -> Left Joycon/Right Joycon -> Handheld
|
// 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
|
// Check if we need scheduling. If we do, then we can't complete the switch and should
|
||||||
// retry.
|
// retry.
|
||||||
if (m_state.needs_scheduling.load(std::memory_order_seq_cst)) {
|
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.
|
// Our switch failed.
|
||||||
// We should unlock the thread context, and then retry.
|
// We should unlock the thread context, and then retry.
|
||||||
highest_priority_thread->m_context_guard.unlock();
|
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.
|
// Check if the thread is terminated by checking the DPC flags.
|
||||||
if ((thread->GetStackParameters().dpc_flags & static_cast<u32>(DpcFlag::Terminated)) == 0) {
|
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.
|
// The thread isn't terminated, so we want to unlock it.
|
||||||
thread->m_context_guard.unlock();
|
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-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
|
||||||
@@ -12,7 +12,6 @@
|
|||||||
#include "common/atomic_ops.h"
|
#include "common/atomic_ops.h"
|
||||||
#include "common/common_funcs.h"
|
#include "common/common_funcs.h"
|
||||||
#include "common/common_types.h"
|
#include "common/common_types.h"
|
||||||
#include "common/spin_lock.h"
|
|
||||||
|
|
||||||
namespace Kernel {
|
namespace Kernel {
|
||||||
|
|
||||||
@@ -30,7 +29,7 @@ public:
|
|||||||
};
|
};
|
||||||
|
|
||||||
public:
|
public:
|
||||||
constexpr KSlabHeapImpl() = default;
|
KSlabHeapImpl() = default;
|
||||||
|
|
||||||
void Initialize() {
|
void Initialize() {
|
||||||
ASSERT(m_head == nullptr);
|
ASSERT(m_head == nullptr);
|
||||||
@@ -68,7 +67,7 @@ public:
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
std::atomic<Node*> m_head{};
|
std::atomic<Node*> m_head{};
|
||||||
Common::SpinLock m_lock;
|
std::mutex m_lock;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace impl
|
} // namespace impl
|
||||||
|
|||||||
@@ -19,7 +19,6 @@
|
|||||||
|
|
||||||
#include "common/intrusive_red_black_tree.h"
|
#include "common/intrusive_red_black_tree.h"
|
||||||
#include "common/scratch_buffer.h"
|
#include "common/scratch_buffer.h"
|
||||||
#include "common/spin_lock.h"
|
|
||||||
#include "core/arm/arm_interface.h"
|
#include "core/arm/arm_interface.h"
|
||||||
#include "core/hle/kernel/k_affinity_mask.h"
|
#include "core/hle/kernel/k_affinity_mask.h"
|
||||||
#include "core/hle/kernel/k_light_lock.h"
|
#include "core/hle/kernel/k_light_lock.h"
|
||||||
@@ -920,7 +919,7 @@ private:
|
|||||||
bool m_resource_limit_release_hint{};
|
bool m_resource_limit_release_hint{};
|
||||||
bool m_is_kernel_address_key{};
|
bool m_is_kernel_address_key{};
|
||||||
StackParameters m_stack_parameters{};
|
StackParameters m_stack_parameters{};
|
||||||
Common::SpinLock m_context_guard{};
|
std::mutex m_context_guard{};
|
||||||
|
|
||||||
// For emulation
|
// For emulation
|
||||||
std::shared_ptr<Common::Fiber> m_host_context{};
|
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 ProfileUsername = std::array<u8, profile_username_size>;
|
||||||
using UserIDArray = std::array<Common::UUID, MAX_USERS>;
|
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.
|
/// Contains extra data related to a user.
|
||||||
/// TODO: RE this structure
|
/// TODO: RE this structure
|
||||||
struct UserData {
|
struct UserData {
|
||||||
|
|||||||
@@ -29,6 +29,7 @@
|
|||||||
#include "core/hle/service/am/frontend/applet_web_browser.h"
|
#include "core/hle/service/am/frontend/applet_web_browser.h"
|
||||||
#include "core/hle/service/am/frontend/applets.h"
|
#include "core/hle/service/am/frontend/applets.h"
|
||||||
#include "core/hle/service/am/service/storage.h"
|
#include "core/hle/service/am/service/storage.h"
|
||||||
|
#include "core/hle/service/am/window_system.h"
|
||||||
#include "core/hle/service/sm/sm.h"
|
#include "core/hle/service/sm/sm.h"
|
||||||
|
|
||||||
namespace Service::AM::Frontend {
|
namespace Service::AM::Frontend {
|
||||||
@@ -72,10 +73,16 @@ void FrontendApplet::PushInteractiveOutData(std::shared_ptr<IStorage> storage) {
|
|||||||
|
|
||||||
void FrontendApplet::Exit() {
|
void FrontendApplet::Exit() {
|
||||||
auto applet_ = applet.lock();
|
auto applet_ = applet.lock();
|
||||||
|
{
|
||||||
std::scoped_lock lk{applet_->lock};
|
std::scoped_lock lk{applet_->lock};
|
||||||
applet_->is_completed = true;
|
applet_->is_completed = true;
|
||||||
applet_->state_changed_event.Signal(system.Kernel());
|
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;
|
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);
|
void OnOperationAndPerformanceModeChanged(Kernel::KernelCore& kernel);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|||||||
@@ -347,21 +347,22 @@ Result IApplicationFunctions::NotifyRunning(Out<bool> out_became_running) {
|
|||||||
|
|
||||||
Result IApplicationFunctions::GetPseudoDeviceId(Out<Common::UUID> out_pseudo_device_id) {
|
Result IApplicationFunctions::GetPseudoDeviceId(Out<Common::UUID> out_pseudo_device_id) {
|
||||||
LOG_WARNING(Service_AM, "(stubbed)");
|
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
|
// This should be hashed with the device specific hash
|
||||||
// for now this will do
|
// for now this will do
|
||||||
const auto res = FileSys::PatchManager::GetMetadataFromBaseOrUpdate(system, m_applet->program_id);
|
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;
|
unsigned int hash_len = 0;
|
||||||
auto const seed = res.first->raw.seed_for_pseudo_device_id;
|
auto const seed = res.first->raw.seed_for_pseudo_device_id;
|
||||||
EVP_MD_CTX *ctx = EVP_MD_CTX_new();
|
EVP_MD_CTX *ctx = EVP_MD_CTX_new();
|
||||||
auto const algorithm = EVP_sha1();
|
auto const algorithm = EVP_sha1();
|
||||||
EVP_DigestInit_ex(ctx, algorithm, nullptr);
|
EVP_DigestInit_ex(ctx, algorithm, nullptr);
|
||||||
EVP_DigestUpdate(ctx, &seed, sizeof(seed));
|
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);
|
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();
|
R_SUCCEED();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -164,14 +164,14 @@ Result ILibraryAppletAccessor::PushInData(SharedPointer<IStorage> storage) {
|
|||||||
Result ILibraryAppletAccessor::PopOutData(Out<SharedPointer<IStorage>> out_storage) {
|
Result ILibraryAppletAccessor::PopOutData(Out<SharedPointer<IStorage>> out_storage) {
|
||||||
LOG_DEBUG(Service_AM, "called");
|
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()));
|
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) {
|
if (m_applet->applet_id == AppletId::ProfileSelect && *out_storage) {
|
||||||
auto impl = (*out_storage)->GetImpl();
|
auto impl = (*out_storage)->GetImpl();
|
||||||
|
|||||||
@@ -122,7 +122,10 @@ std::shared_ptr<ILibraryAppletAccessor> CreateGuestApplet(Core::System& system,
|
|||||||
auto broker = std::make_shared<AppletDataBroker>(system);
|
auto broker = std::make_shared<AppletDataBroker>(system);
|
||||||
applet->caller_applet = caller_applet;
|
applet->caller_applet = caller_applet;
|
||||||
applet->caller_applet_broker = broker;
|
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);
|
window_system.TrackApplet(applet, false);
|
||||||
return std::make_shared<ILibraryAppletAccessor>(system, broker, applet);
|
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 = caller_applet;
|
||||||
applet->caller_applet_broker = storage;
|
applet->caller_applet_broker = storage;
|
||||||
applet->frontend = system.GetFrontendAppletHolder().GetApplet(applet, applet_id, mode);
|
applet->frontend = system.GetFrontendAppletHolder().GetApplet(applet, applet_id, mode);
|
||||||
caller_applet->child_applets.push_back(applet);
|
{
|
||||||
|
std::scoped_lock lk{caller_applet->lock};
|
||||||
window_system.TrackApplet(applet, false);
|
caller_applet->child_applets.push_back(applet);
|
||||||
|
}
|
||||||
return std::make_shared<ILibraryAppletAccessor>(system, storage, applet);
|
return std::make_shared<ILibraryAppletAccessor>(system, storage, applet);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -97,7 +97,7 @@ Result IAudioDevice::SetAudioDeviceOutputVolumeAuto(
|
|||||||
LOG_DEBUG(Service_Audio, "called. name={}, volume={}", device_name, volume);
|
LOG_DEBUG(Service_Audio, "called. name={}, volume={}", device_name, volume);
|
||||||
|
|
||||||
if (device_name == "AudioTvOutput") {
|
if (device_name == "AudioTvOutput") {
|
||||||
impl->SetDeviceVolumes(volume);
|
impl->SetDeviceVolumes(system, volume);
|
||||||
}
|
}
|
||||||
|
|
||||||
R_SUCCEED();
|
R_SUCCEED();
|
||||||
@@ -112,7 +112,7 @@ Result IAudioDevice::GetAudioDeviceOutputVolumeAuto(
|
|||||||
|
|
||||||
*out_volume = 1.0f;
|
*out_volume = 1.0f;
|
||||||
if (device_name == "AudioTvOutput") {
|
if (device_name == "AudioTvOutput") {
|
||||||
*out_volume = impl->GetDeviceVolume(device_name);
|
*out_volume = impl->GetDeviceVolume(system, device_name);
|
||||||
}
|
}
|
||||||
|
|
||||||
R_SUCCEED();
|
R_SUCCEED();
|
||||||
|
|||||||
@@ -50,7 +50,7 @@ IAudioIn::IAudioIn(Core::System& system_, Manager& manager, size_t session_id,
|
|||||||
}
|
}
|
||||||
|
|
||||||
IAudioIn::~IAudioIn() {
|
IAudioIn::~IAudioIn() {
|
||||||
impl->Free();
|
impl->Free(system);
|
||||||
service_context.CloseEvent(event);
|
service_context.CloseEvent(event);
|
||||||
process->Close(system.Kernel());
|
process->Close(system.Kernel());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,6 @@
|
|||||||
|
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
@@ -65,7 +68,7 @@ Result IAudioInManager::OpenAudioInAuto(
|
|||||||
Result IAudioInManager::ListAudioInsAutoFiltered(
|
Result IAudioInManager::ListAudioInsAutoFiltered(
|
||||||
OutArray<AudioDeviceName, BufferAttr_HipcAutoSelect> out_audio_ins, Out<u32> out_count) {
|
OutArray<AudioDeviceName, BufferAttr_HipcAutoSelect> out_audio_ins, Out<u32> out_count) {
|
||||||
LOG_DEBUG(Service_Audio, "called");
|
LOG_DEBUG(Service_Audio, "called");
|
||||||
*out_count = impl->GetDeviceNames(out_audio_ins, true);
|
*out_count = impl->GetDeviceNames(system, out_audio_ins, true);
|
||||||
R_SUCCEED();
|
R_SUCCEED();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -90,8 +93,8 @@ Result IAudioInManager::OpenAudioInProtocolSpecified(
|
|||||||
|
|
||||||
size_t new_session_id{};
|
size_t new_session_id{};
|
||||||
|
|
||||||
R_TRY(impl->LinkToManager());
|
R_TRY(impl->LinkToManager(system));
|
||||||
R_TRY(impl->AcquireSessionId(new_session_id));
|
R_TRY(impl->AcquireSessionId(system, new_session_id));
|
||||||
|
|
||||||
LOG_DEBUG(Service_Audio, "Opening new AudioIn, session_id={}, free sessions={}", new_session_id,
|
LOG_DEBUG(Service_Audio, "Opening new AudioIn, session_id={}, free sessions={}", new_session_id,
|
||||||
impl->num_free_sessions);
|
impl->num_free_sessions);
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ IAudioOut::IAudioOut(Core::System& system_, Manager& manager, size_t session_id,
|
|||||||
}
|
}
|
||||||
|
|
||||||
IAudioOut::~IAudioOut() {
|
IAudioOut::~IAudioOut() {
|
||||||
impl->Free();
|
impl->Free(system);
|
||||||
service_context.CloseEvent(event);
|
service_context.CloseEvent(event);
|
||||||
process->Close(system.Kernel());
|
process->Close(system.Kernel());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -77,8 +77,8 @@ Result IAudioOutManager::OpenAudioOutAuto(
|
|||||||
}
|
}
|
||||||
|
|
||||||
size_t new_session_id{};
|
size_t new_session_id{};
|
||||||
R_TRY(impl->LinkToManager());
|
R_TRY(impl->LinkToManager(system));
|
||||||
R_TRY(impl->AcquireSessionId(new_session_id));
|
R_TRY(impl->AcquireSessionId(system, new_session_id));
|
||||||
|
|
||||||
const auto device_name = Common::StringFromBuffer(name[0].name);
|
const auto device_name = Common::StringFromBuffer(name[0].name);
|
||||||
LOG_DEBUG(Service_Audio, "Opening new AudioOut, sessionid={}, free sessions={}", new_session_id,
|
LOG_DEBUG(Service_Audio, "Opening new AudioOut, sessionid={}, free sessions={}", new_session_id,
|
||||||
|
|||||||
@@ -154,6 +154,9 @@ FSP_SRV::FSP_SRV(Core::System& system_)
|
|||||||
{720, nullptr, "AbandonAccessFailure"},
|
{720, nullptr, "AbandonAccessFailure"},
|
||||||
{800, nullptr, "GetAndClearFileSystemProxyErrorInfo"},
|
{800, nullptr, "GetAndClearFileSystemProxyErrorInfo"},
|
||||||
{810, nullptr, "RegisterProgramIndexMapInfo"},
|
{810, nullptr, "RegisterProgramIndexMapInfo"},
|
||||||
|
{820, nullptr, "GetContentStorageInfoIndex"},
|
||||||
|
{830, nullptr, "EncryptStreamPlaySaveData"},
|
||||||
|
{831, nullptr, "DecryptStreamPlaySaveData"},
|
||||||
{1000, nullptr, "SetBisRootForHost"},
|
{1000, nullptr, "SetBisRootForHost"},
|
||||||
{1001, nullptr, "SetSaveDataSize"},
|
{1001, nullptr, "SetSaveDataSize"},
|
||||||
{1002, nullptr, "SetSaveDataRootPath"},
|
{1002, nullptr, "SetSaveDataRootPath"},
|
||||||
|
|||||||
@@ -121,7 +121,7 @@ IHidSystemServer::IHidSystemServer(Core::System& system_, std::shared_ptr<Resour
|
|||||||
{547, nullptr, "GetAllowedBluetoothLinksCount"},
|
{547, nullptr, "GetAllowedBluetoothLinksCount"},
|
||||||
{548, &IHidSystemServer::GetRegisteredDevices, "GetRegisteredDevices"},
|
{548, &IHidSystemServer::GetRegisteredDevices, "GetRegisteredDevices"},
|
||||||
{549, nullptr, "GetConnectableRegisteredDevices"},
|
{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"},
|
{700, nullptr, "ActivateUniquePad"},
|
||||||
{702, &IHidSystemServer::AcquireUniquePadConnectionEventHandle, "AcquireUniquePadConnectionEventHandle"},
|
{702, &IHidSystemServer::AcquireUniquePadConnectionEventHandle, "AcquireUniquePadConnectionEventHandle"},
|
||||||
{703, &IHidSystemServer::GetUniquePadIds, "GetUniquePadIds"},
|
{703, &IHidSystemServer::GetUniquePadIds, "GetUniquePadIds"},
|
||||||
@@ -758,7 +758,7 @@ void IHidSystemServer::AcquireDeviceRegisteredEventForControllerSupport(HLEReque
|
|||||||
}
|
}
|
||||||
|
|
||||||
void IHidSystemServer::GetRegisteredDevices(HLERequestContext& ctx) {
|
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 {
|
struct RegisterData {
|
||||||
std::array<u8, 0x68> data;
|
std::array<u8, 0x68> data;
|
||||||
|
|||||||
@@ -22,6 +22,7 @@
|
|||||||
|
|
||||||
namespace IPC {
|
namespace IPC {
|
||||||
|
|
||||||
|
constexpr Result ResultNotSupported{ErrorModule::HIPC, 1};
|
||||||
constexpr Result ResultSessionClosed{ErrorModule::HIPC, 301};
|
constexpr Result ResultSessionClosed{ErrorModule::HIPC, 301};
|
||||||
|
|
||||||
struct ResponseBuilder {
|
struct ResponseBuilder {
|
||||||
|
|||||||
@@ -71,17 +71,14 @@ public:
|
|||||||
void InstructionSynchronizationBarrierRaised() override {
|
void InstructionSynchronizationBarrierRaised() override {
|
||||||
last_code_addr = u64(-1); //reset back, force refetch
|
last_code_addr = u64(-1); //reset back, force refetch
|
||||||
}
|
}
|
||||||
u8 MemoryRead8(u64 vaddr) override {
|
u64 MemoryRead(u64 vaddr, size_t size) override {
|
||||||
return ReadMemory<u8>(vaddr);
|
switch (size) {
|
||||||
}
|
case sizeof(u64): return ReadMemory<u64>(vaddr);
|
||||||
u16 MemoryRead16(u64 vaddr) override {
|
case sizeof(u32): return ReadMemory<u32>(vaddr);
|
||||||
return ReadMemory<u16>(vaddr);
|
case sizeof(u16): return ReadMemory<u16>(vaddr);
|
||||||
}
|
case sizeof(u8): return ReadMemory<u8>(vaddr);
|
||||||
u32 MemoryRead32(u64 vaddr) override {
|
default: UNREACHABLE();
|
||||||
return ReadMemory<u32>(vaddr);
|
}
|
||||||
}
|
|
||||||
u64 MemoryRead64(u64 vaddr) override {
|
|
||||||
return ReadMemory<u64>(vaddr);
|
|
||||||
}
|
}
|
||||||
u128 MemoryRead128(u64 vaddr) override {
|
u128 MemoryRead128(u64 vaddr) override {
|
||||||
return ReadMemory<u128>(vaddr);
|
return ReadMemory<u128>(vaddr);
|
||||||
@@ -89,22 +86,19 @@ public:
|
|||||||
std::string MemoryReadCString(u64 vaddr) {
|
std::string MemoryReadCString(u64 vaddr) {
|
||||||
std::string result{};
|
std::string result{};
|
||||||
u8 next;
|
u8 next;
|
||||||
while ((next = MemoryRead8(vaddr++)) != 0)
|
while ((next = u8(MemoryRead(vaddr++, sizeof(u8)))) != 0)
|
||||||
result += char(next);
|
result += char(next);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
void MemoryWrite8(u64 vaddr, u8 value) override {
|
void MemoryWrite(u64 vaddr, u64 value, size_t size) override {
|
||||||
WriteMemory<u8>(vaddr, value);
|
switch (size) {
|
||||||
}
|
case sizeof(u64): WriteMemory<u64>(vaddr, u64(value)); break;
|
||||||
void MemoryWrite16(u64 vaddr, u16 value) override {
|
case sizeof(u32): WriteMemory<u32>(vaddr, u32(value)); break;
|
||||||
WriteMemory<u16>(vaddr, value);
|
case sizeof(u16): WriteMemory<u16>(vaddr, u16(value)); break;
|
||||||
}
|
case sizeof(u8): WriteMemory<u8>(vaddr, u8(value)); break;
|
||||||
void MemoryWrite32(u64 vaddr, u32 value) override {
|
default: UNREACHABLE();
|
||||||
WriteMemory<u32>(vaddr, value);
|
}
|
||||||
}
|
|
||||||
void MemoryWrite64(u64 vaddr, u64 value) override {
|
|
||||||
WriteMemory<u64>(vaddr, value);
|
|
||||||
}
|
}
|
||||||
void MemoryWrite128(u64 vaddr, u128 value) override {
|
void MemoryWrite128(u64 vaddr, u128 value) override {
|
||||||
WriteMemory<u128>(vaddr, value);
|
WriteMemory<u128>(vaddr, value);
|
||||||
@@ -193,14 +187,14 @@ public:
|
|||||||
// The loaded NRO file has ELF relocations that must be processed before it can run.
|
// 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
|
// 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.
|
// the linker available, so we have to do it ourselves.
|
||||||
const VAddr mod_offset{callbacks->MemoryRead32(4)};
|
const VAddr mod_offset{callbacks->MemoryRead(4, sizeof(u32))};
|
||||||
if (callbacks->MemoryRead32(mod_offset) != Common::MakeMagic('M', 'O', 'D', '0'))
|
if (callbacks->MemoryRead(mod_offset, sizeof(u32)) != Common::MakeMagic('M', 'O', 'D', '0'))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// For more info about dynamic entries, see the ELF ABI specification:
|
// 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+/ch5.dynamic.html
|
||||||
// https://refspecs.linuxbase.org/elf/gabi4+/ch4.reloc.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;
|
VAddr rela_dyn = 0, relr_dyn = 0;
|
||||||
size_t num_rela = 0, num_relr = 0;
|
size_t num_rela = 0, num_relr = 0;
|
||||||
while (true) {
|
while (true) {
|
||||||
@@ -222,8 +216,8 @@ public:
|
|||||||
for (size_t i = 0; i < num_rela; i++) {
|
for (size_t i = 0; i < num_rela; i++) {
|
||||||
const auto rela{callbacks->ReadMemory<Elf64_Rela>(rela_dyn + i * sizeof(Elf64_Rela))};
|
const auto rela{callbacks->ReadMemory<Elf64_Rela>(rela_dyn + i * sizeof(Elf64_Rela))};
|
||||||
if (Elf64RelType(rela.r_info) == ElfAArch64Relative) {
|
if (Elf64RelType(rela.r_info) == ElfAArch64Relative) {
|
||||||
const VAddr contents{callbacks->MemoryRead64(rela.r_offset)};
|
const VAddr contents{callbacks->MemoryRead(rela.r_offset, sizeof(u64))};
|
||||||
callbacks->MemoryWrite64(rela.r_offset, contents + rela.r_addend);
|
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++) {
|
for (size_t i = 0; i < num_relr; i++) {
|
||||||
const auto relr = callbacks->ReadMemory<Elf64_Relr>(relr_dyn + i * sizeof(Elf64_Relr));
|
const auto relr = callbacks->ReadMemory<Elf64_Relr>(relr_dyn + i * sizeof(Elf64_Relr));
|
||||||
const auto incr = [&](VAddr where) {
|
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) {
|
if ((relr & 1) == 0) {
|
||||||
// where pointer
|
// where pointer
|
||||||
@@ -294,7 +288,7 @@ public:
|
|||||||
if (argument_stack.size() > 8) {
|
if (argument_stack.size() > 8) {
|
||||||
const VAddr new_sp = Common::AlignDown(top_of_stack - (argument_stack.size() - 8) * sizeof(u64), STACK_ALIGN);
|
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++)
|
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);
|
jit->SetSP(new_sp);
|
||||||
}
|
}
|
||||||
// Reset the call state for the next invocation
|
// Reset the call state for the next invocation
|
||||||
@@ -385,17 +379,17 @@ void DynarmicCallbacks64::CallSVC(u32 swi) {
|
|||||||
|
|
||||||
if (dest < src) {
|
if (dest < src) {
|
||||||
for (size_t i = 0; i < n; i++)
|
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 {
|
} else {
|
||||||
for (size_t i = n; i > 0; i--)
|
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)]) {
|
} else if (pc == parent.helpers[size_t(HelperFn::Memset)]) {
|
||||||
const VAddr dest{parent.jit->GetRegister(0)};
|
const VAddr dest{parent.jit->GetRegister(0)};
|
||||||
const u64 c{parent.jit->GetRegister(1)};
|
const u64 c{parent.jit->GetRegister(1)};
|
||||||
const size_t n{parent.jit->GetRegister(2)};
|
const size_t n{parent.jit->GetRegister(2)};
|
||||||
for (size_t i = 0; i < n; i++)
|
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)]) {
|
} else if (pc == parent.helpers[size_t(HelperFn::Resolve)]) {
|
||||||
// X0 contains a char* for a symbol to resolve
|
// X0 contains a char* for a symbol to resolve
|
||||||
const auto name{MemoryReadCString(parent.jit->GetRegister(0))};
|
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) {
|
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);
|
LOG_CRITICAL(Service_JIT, "{} PC @ {:08x}, data = {:08x}", exception, pc, inst);
|
||||||
parent.jit->HaltExecution();
|
parent.jit->HaltExecution();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,10 +6,16 @@
|
|||||||
|
|
||||||
#include "common/string_util.h"
|
#include "common/string_util.h"
|
||||||
#include "core/core.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/ipc_helpers.h"
|
||||||
#include "core/hle/service/ngc/ngc.h"
|
#include "core/hle/service/ngc/ngc.h"
|
||||||
#include "core/hle/service/server_manager.h"
|
#include "core/hle/service/server_manager.h"
|
||||||
#include "core/hle/service/service.h"
|
#include "core/hle/service/service.h"
|
||||||
|
#include "frontend_common/firmware_manager.h"
|
||||||
|
|
||||||
namespace Service::NGC {
|
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) {
|
void LoopProcess(Core::System& system) {
|
||||||
auto server_manager = std::make_unique<ServerManager>(system);
|
auto server_manager = std::make_unique<ServerManager>(system);
|
||||||
|
|
||||||
server_manager->RegisterNamedService("ngct:u", std::make_shared<IService>(system), 4);
|
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("ngct:s", std::make_shared<IServiceWithManagementApi>(system), 4);
|
||||||
server_manager->RegisterNamedService("ngc:u", std::make_shared<NgcServiceImpl>(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));
|
ServerManager::RunServer(std::move(server_manager));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "core/hle/service/cmif_types.h"
|
#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/language.h"
|
||||||
#include "core/hle/service/ns/ns_types.h"
|
#include "core/hle/service/ns/ns_types.h"
|
||||||
#include "core/hle/service/os/event.h"
|
#include "core/hle/service/os/event.h"
|
||||||
|
|||||||
+1
-1
@@ -2,7 +2,7 @@
|
|||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
#include "core/hle/service/cmif_serialization.h"
|
#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>
|
#include <cstring>
|
||||||
|
|
||||||
@@ -311,27 +311,30 @@ void IReadOnlyApplicationControlDataInterface::ListApplicationIcon(HLERequestCon
|
|||||||
// u64 - app count
|
// u64 - app count
|
||||||
memory.WriteBlock(t_mem_address + out_length, &app_count, sizeof(u64));
|
memory.WriteBlock(t_mem_address + out_length, &app_count, sizeof(u64));
|
||||||
out_length += sizeof(u64);
|
out_length += sizeof(u64);
|
||||||
|
ASSERT(out_length <= t_mem->GetSize());
|
||||||
// [list of u64] - size of icons
|
// [list of u64] - size of icons
|
||||||
for (size_t i = 0; i < app_count; ++i) {
|
for (size_t i = 0; i < app_count; ++i) {
|
||||||
const u64 app_id = app_ids_buffer[i];
|
const u64 app_id = app_ids_buffer[i];
|
||||||
const FileSys::PatchManager pm{app_id, system.GetFileSystemController(), system.GetContentProvider()};
|
const FileSys::PatchManager pm{app_id, system.GetFileSystemController(), system.GetContentProvider()};
|
||||||
const auto control = pm.GetControlMetadata();
|
if (const auto control = pm.GetControlMetadata(); control.second) {
|
||||||
u64 full_size = control.second->GetSize();
|
u64 full_size = control.second->GetSize();
|
||||||
memory.WriteBlock(t_mem_address + out_length, &full_size, sizeof(u64));
|
memory.WriteBlock(t_mem_address + out_length, &full_size, sizeof(u64));
|
||||||
|
}
|
||||||
out_length += sizeof(u64);
|
out_length += sizeof(u64);
|
||||||
|
ASSERT(out_length <= t_mem->GetSize());
|
||||||
}
|
}
|
||||||
// [list of raw icon data]
|
// [list of raw icon data]
|
||||||
std::vector<u8> full_icon_data;
|
|
||||||
for (size_t i = 0; i < app_count; ++i) {
|
for (size_t i = 0; i < app_count; ++i) {
|
||||||
const u64 app_id = app_ids_buffer[i];
|
const u64 app_id = app_ids_buffer[i];
|
||||||
const FileSys::PatchManager pm{app_id, system.GetFileSystemController(), system.GetContentProvider()};
|
const FileSys::PatchManager pm{app_id, system.GetFileSystemController(), system.GetContentProvider()};
|
||||||
const auto control = pm.GetControlMetadata();
|
if (const auto control = pm.GetControlMetadata(); control.second) {
|
||||||
auto const full_size = control.second->GetSize();
|
if (auto const full_size = control.second->GetSize(); full_size > 0) {
|
||||||
if (full_size > 0) {
|
std::vector<u8> full_icon_data(full_size);
|
||||||
full_icon_data.resize(full_size);
|
control.second->Read(full_icon_data.data(), full_size, 0);
|
||||||
control.second->Read(full_icon_data.data(), full_size, 0);
|
memory.WriteBlock(t_mem_address + out_length, full_icon_data.data(), full_size);
|
||||||
memory.WriteBlock(t_mem_address + out_length, full_icon_data.data(), full_size);
|
out_length += 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) {
|
void IReadOnlyApplicationControlDataInterface::ListApplicationTitle(HLERequestContext& ctx) {
|
||||||
const auto app_ids_buffer = ctx.ReadBuffer();
|
const auto app_ids_buffer = ctx.ReadBuffer();
|
||||||
const size_t app_count = app_ids_buffer.size() / sizeof(u64);
|
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_obj = ctx.GetObjectFromHandle<Kernel::KTransferMemory>(ctx.GetCopyHandle(0));
|
||||||
auto* t_mem = t_mem_obj.GetPointerUnsafe();
|
auto* t_mem = t_mem_obj.GetPointerUnsafe();
|
||||||
constexpr size_t title_entry_size = sizeof(FileSys::LanguageEntry);
|
constexpr size_t title_entry_size = sizeof(FileSys::LanguageEntry);
|
||||||
@@ -354,8 +363,9 @@ void IReadOnlyApplicationControlDataInterface::ListApplicationTitle(HLERequestCo
|
|||||||
auto& memory = system.ApplicationMemory();
|
auto& memory = system.ApplicationMemory();
|
||||||
const auto t_mem_address = t_mem->GetSourceAddress();
|
const auto t_mem_address = t_mem->GetSourceAddress();
|
||||||
for (size_t i = 0; i < app_count; ++i) {
|
for (size_t i = 0; i < app_count; ++i) {
|
||||||
const u64 app_id = app_ids_buffer[i];
|
const u64 app_id = application_ids[i];
|
||||||
const FileSys::PatchManager pm{app_id, system.GetFileSystemController(), system.GetContentProvider()};
|
const FileSys::PatchManager pm{app_id, system.GetFileSystemController(),
|
||||||
|
system.GetContentProvider()};
|
||||||
const auto control = pm.GetControlMetadata();
|
const auto control = pm.GetControlMetadata();
|
||||||
FileSys::LanguageEntry entry{};
|
FileSys::LanguageEntry entry{};
|
||||||
if (control.first != nullptr) {
|
if (control.first != nullptr) {
|
||||||
|
|||||||
+218
-244
@@ -48,6 +48,12 @@ enum class OptionType : u32 {
|
|||||||
EnableAlpn = 3,
|
EnableAlpn = 3,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// This is nn::ssl::sf::RenegotiationMode
|
||||||
|
enum RenegotiationMode : u32 {
|
||||||
|
None = 0, ///< None
|
||||||
|
Secure = 1, ///< Secure
|
||||||
|
};
|
||||||
|
|
||||||
// This is nn::ssl::sf::SslVersion
|
// This is nn::ssl::sf::SslVersion
|
||||||
struct SslVersion {
|
struct SslVersion {
|
||||||
union {
|
union {
|
||||||
@@ -75,34 +81,34 @@ public:
|
|||||||
shared_data{shared_data_in}, backend{std::move(backend_in)} {
|
shared_data{shared_data_in}, backend{std::move(backend_in)} {
|
||||||
// clang-format off
|
// clang-format off
|
||||||
static const FunctionInfo functions[] = {
|
static const FunctionInfo functions[] = {
|
||||||
{0, &ISslConnection::SetSocketDescriptor, "SetSocketDescriptor"},
|
{0, D<&ISslConnection::SetSocketDescriptor>, "SetSocketDescriptor"},
|
||||||
{1, &ISslConnection::SetHostName, "SetHostName"},
|
{1, D<&ISslConnection::SetHostName>, "SetHostName"},
|
||||||
{2, &ISslConnection::SetVerifyOption, "SetVerifyOption"},
|
{2, D<&ISslConnection::SetVerifyOption>, "SetVerifyOption"},
|
||||||
{3, &ISslConnection::SetIoMode, "SetIoMode"},
|
{3, D<&ISslConnection::SetIoMode>, "SetIoMode"},
|
||||||
{4, nullptr, "GetSocketDescriptor"},
|
{4, D<&ISslConnection::GetSocketDescriptor>, "GetSocketDescriptor"},
|
||||||
{5, nullptr, "GetHostName"},
|
{5, D<&ISslConnection::GetHostName>, "GetHostName"},
|
||||||
{6, nullptr, "GetVerifyOption"},
|
{6, nullptr, "GetVerifyOption"},
|
||||||
{7, nullptr, "GetIoMode"},
|
{7, D<&ISslConnection::GetIoMode>, "GetIoMode"},
|
||||||
{8, &ISslConnection::DoHandshake, "DoHandshake"},
|
{8, D<&ISslConnection::DoHandshake>, "DoHandshake"},
|
||||||
{9, &ISslConnection::DoHandshakeGetServerCert, "DoHandshakeGetServerCert"},
|
{9, &ISslConnection::DoHandshakeGetServerCert, "DoHandshakeGetServerCert"},
|
||||||
{10, &ISslConnection::Read, "Read"},
|
{10, D<&ISslConnection::Read>, "Read"},
|
||||||
{11, &ISslConnection::Write, "Write"},
|
{11, D<&ISslConnection::Write>, "Write"},
|
||||||
{12, &ISslConnection::Pending, "Pending"},
|
{12, D<&ISslConnection::Pending>, "Pending"},
|
||||||
{13, nullptr, "Peek"},
|
{13, D<&ISslConnection::Peek>, "Peek"},
|
||||||
{14, nullptr, "Poll"},
|
{14, D<&ISslConnection::Poll>, "Poll"},
|
||||||
{15, nullptr, "GetVerifyCertError"},
|
{15, D<&ISslConnection::GetVerifyCertError>, "GetVerifyCertError"},
|
||||||
{16, nullptr, "GetNeededServerCertBufferSize"},
|
{16, D<&ISslConnection::GetNeededServerCertBufferSize>, "GetNeededServerCertBufferSize"},
|
||||||
{17, &ISslConnection::SetSessionCacheMode, "SetSessionCacheMode"},
|
{17, D<&ISslConnection::SetSessionCacheMode>, "SetSessionCacheMode"},
|
||||||
{18, nullptr, "GetSessionCacheMode"},
|
{18, D<&ISslConnection::GetSessionCacheMode>, "GetSessionCacheMode"},
|
||||||
{19, nullptr, "FlushSessionCache"},
|
{19, D<&ISslConnection::FlushSessionCache>, "FlushSessionCache"},
|
||||||
{20, nullptr, "SetRenegotiationMode"},
|
{20, D<&ISslConnection::SetRenegotiationMode>, "SetRenegotiationMode"},
|
||||||
{21, nullptr, "GetRenegotiationMode"},
|
{21, D<&ISslConnection::GetRenegotiationMode>, "GetRenegotiationMode"},
|
||||||
{22, &ISslConnection::SetOption, "SetOption"},
|
{22, D<&ISslConnection::SetOption>, "SetOption"},
|
||||||
{23, &ISslConnection::GetOption, "GetOption"},
|
{23, D<&ISslConnection::GetOption>, "GetOption"},
|
||||||
{24, nullptr, "GetVerifyCertErrors"},
|
{24, nullptr, "GetVerifyCertErrors"},
|
||||||
{25, nullptr, "GetCipherInfo"},
|
{25, nullptr, "GetCipherInfo"},
|
||||||
{26, &ISslConnection::SetNextAlpnProto, "SetNextAlpnProto"},
|
{26, D<&ISslConnection::SetNextAlpnProto>, "SetNextAlpnProto"},
|
||||||
{27, &ISslConnection::GetNextAlpnProto, "GetNextAlpnProto"},
|
{27, D<&ISslConnection::GetNextAlpnProto>, "GetNextAlpnProto"},
|
||||||
{28, nullptr, "SetDtlsSocketDescriptor"},
|
{28, nullptr, "SetDtlsSocketDescriptor"},
|
||||||
{29, nullptr, "GetDtlsHandshakeTimeout"},
|
{29, nullptr, "GetDtlsHandshakeTimeout"},
|
||||||
{30, nullptr, "SetPrivateOption"},
|
{30, nullptr, "SetPrivateOption"},
|
||||||
@@ -141,80 +147,6 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
private:
|
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() {
|
Result DoHandshakeImpl() {
|
||||||
ASSERT_OR_EXECUTE(!did_handshake && socket, { return ResultNoSocket; });
|
ASSERT_OR_EXECUTE(!did_handshake && socket, { return ResultNoSocket; });
|
||||||
Result res = backend->DoHandshake();
|
Result res = backend->DoHandshake();
|
||||||
@@ -234,19 +166,17 @@ private:
|
|||||||
};
|
};
|
||||||
if (!get_server_cert_chain) {
|
if (!get_server_cert_chain) {
|
||||||
// Just return the first one, unencoded.
|
// Just return the first one, unencoded.
|
||||||
ASSERT_OR_EXECUTE_MSG(
|
ASSERT_OR_EXECUTE_MSG(!certs.empty(), { return {}; }, "Should be at least one server cert");
|
||||||
!certs.empty(), { return {}; }, "Should be at least one server cert");
|
|
||||||
return certs[0];
|
return certs[0];
|
||||||
}
|
}
|
||||||
std::vector<u8> ret;
|
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));
|
ret.insert(ret.end(), reinterpret_cast<u8*>(&header), reinterpret_cast<u8*>(&header + 1));
|
||||||
size_t data_offset = sizeof(Header) + certs.size() * sizeof(EntryHeader);
|
size_t data_offset = sizeof(Header) + certs.size() * sizeof(EntryHeader);
|
||||||
for (auto& cert : certs) {
|
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();
|
data_offset += cert.size();
|
||||||
ret.insert(ret.end(), reinterpret_cast<u8*>(&entry_header),
|
ret.insert(ret.end(), reinterpret_cast<u8*>(&entry_header), reinterpret_cast<u8*>(&entry_header + 1));
|
||||||
reinterpret_cast<u8*>(&entry_header + 1));
|
|
||||||
}
|
}
|
||||||
for (auto& cert : certs) {
|
for (auto& cert : certs) {
|
||||||
ret.insert(ret.end(), cert.begin(), cert.end());
|
ret.insert(ret.end(), cert.begin(), cert.end());
|
||||||
@@ -254,65 +184,77 @@ private:
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
Result ReadImpl(std::vector<u8>* out_data) {
|
Result SetSocketDescriptor(s32 in_fd, Out<s32> out_fd) {
|
||||||
ASSERT_OR_EXECUTE(did_handshake, { return ResultInternalError; });
|
LOG_DEBUG(Service_SSL, "called, fd={}", in_fd);
|
||||||
size_t actual_size{};
|
ASSERT(!did_handshake);
|
||||||
Result res = backend->Read(&actual_size, *out_data);
|
auto bsd = system.ServiceManager().GetService<Service::Sockets::BSD_USA>("bsd:u");
|
||||||
if (res != ResultSuccess) {
|
ASSERT_OR_EXECUTE(bsd, { return ResultInternalError; });
|
||||||
return res;
|
|
||||||
|
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);
|
LOG_ERROR(Service_SSL, "Failed to duplicate socket with fd {}", in_fd);
|
||||||
return res;
|
return ResultInvalidSocket;
|
||||||
}
|
}
|
||||||
|
|
||||||
Result WriteImpl(size_t* out_size, std::span<const u8> data) {
|
Result SetHostName(InBuffer<BufferAttr_HipcMapAlias> buf) {
|
||||||
ASSERT_OR_EXECUTE(did_handshake, { return ResultInternalError; });
|
auto const hostname = Common::StringFromBuffer(buf);
|
||||||
return backend->Write(out_size, data);
|
LOG_DEBUG(Service_SSL, "called. hostname={}", hostname);
|
||||||
|
ASSERT(!did_handshake);
|
||||||
|
return backend->SetHostName(hostname.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
Result PendingImpl(s32* out_pending) {
|
Result SetVerifyOption(u32 option) {
|
||||||
LOG_WARNING(Service_SSL, "(STUBBED) called.");
|
LOG_DEBUG(Service_SSL, "called. option={} (forcing 0)", option);
|
||||||
*out_pending = 0;
|
ASSERT(!did_handshake);
|
||||||
return ResultSuccess;
|
verify_option = 0;
|
||||||
|
backend->SetVerifyOption(0);
|
||||||
|
R_SUCCEED();
|
||||||
}
|
}
|
||||||
|
|
||||||
void SetSocketDescriptor(HLERequestContext& ctx) {
|
Result SetIoMode(u32 input_mode) {
|
||||||
IPC::RequestParser rp{ctx};
|
auto mode = IoMode(input_mode);
|
||||||
const s32 in_fd = rp.Pop<s32>();
|
ASSERT(mode == IoMode::Blocking || mode == IoMode::NonBlocking);
|
||||||
s32 out_fd{-1};
|
R_UNLESS(socket, ResultNoSocket);
|
||||||
const Result res = SetSocketDescriptorImpl(&out_fd, in_fd);
|
const bool non_block = mode == IoMode::NonBlocking;
|
||||||
IPC::ResponseBuilder rb{ctx, 3};
|
const Network::Errno error = socket->SetNonBlock(non_block);
|
||||||
rb.Push(res);
|
if (error != Network::Errno::SUCCESS) {
|
||||||
rb.Push<s32>(out_fd);
|
LOG_ERROR(Service_SSL, "Failed to set native socket non-block flag to {}", non_block);
|
||||||
|
}
|
||||||
|
R_SUCCEED();
|
||||||
}
|
}
|
||||||
|
|
||||||
void SetHostName(HLERequestContext& ctx) {
|
Result GetSocketDescriptor(Out<u32> out_fd) {
|
||||||
const std::string hostname = Common::StringFromBuffer(ctx.ReadBuffer());
|
LOG_WARNING(Service_SSL, "(STUBBED)");
|
||||||
const Result res = SetHostNameImpl(hostname);
|
*out_fd = uint32_t(socket->GetFD());
|
||||||
IPC::ResponseBuilder rb{ctx, 2};
|
R_SUCCEED();
|
||||||
rb.Push(res);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void SetVerifyOption(HLERequestContext& ctx) {
|
Result GetHostName(OutBuffer<BufferAttr_HipcMapAlias> data, Out<u32> out_size) {
|
||||||
IPC::RequestParser rp{ctx};
|
LOG_WARNING(Service_SSL, "(STUBBED)");
|
||||||
const u32 option = rp.Pop<u32>();
|
ASSERT(!did_handshake);
|
||||||
const Result res = SetVerifyOptionImpl(option);
|
return backend->GetHostName(std::span<u8>{data.begin(), data.end()}, out_size);
|
||||||
IPC::ResponseBuilder rb{ctx, 2};
|
|
||||||
rb.Push(res);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void SetIoMode(HLERequestContext& ctx) {
|
Result GetIoMode(Out<u32> out_mode) {
|
||||||
IPC::RequestParser rp{ctx};
|
LOG_WARNING(Service_SSL, "(STUBBED)");
|
||||||
const u32 mode = rp.Pop<u32>();
|
R_SUCCEED();
|
||||||
const Result res = SetIoModeImpl(mode);
|
|
||||||
IPC::ResponseBuilder rb{ctx, 2};
|
|
||||||
rb.Push(res);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void DoHandshake(HLERequestContext& ctx) {
|
Result DoHandshake() {
|
||||||
const Result res = DoHandshakeImpl();
|
return DoHandshakeImpl();
|
||||||
IPC::ResponseBuilder rb{ctx, 2};
|
|
||||||
rb.Push(res);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void DoHandshakeGetServerCert(HLERequestContext& ctx) {
|
void DoHandshakeGetServerCert(HLERequestContext& ctx) {
|
||||||
@@ -351,131 +293,158 @@ private:
|
|||||||
rb.PushRaw(out);
|
rb.PushRaw(out);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Read(HLERequestContext& ctx) {
|
Result Read(OutBuffer<BufferAttr_HipcMapAlias> data, Out<u32> out_size) {
|
||||||
std::vector<u8> output_bytes(ctx.GetWriteBufferSize());
|
R_UNLESS(did_handshake, ResultInternalError);
|
||||||
const Result res = ReadImpl(&output_bytes);
|
size_t tmp{};
|
||||||
IPC::ResponseBuilder rb{ctx, 3};
|
auto const res = backend->Read(&tmp, data);
|
||||||
rb.Push(res);
|
*out_size = u32(tmp);
|
||||||
if (res == ResultSuccess) {
|
return res;
|
||||||
rb.Push(static_cast<u32>(output_bytes.size()));
|
|
||||||
ctx.WriteBuffer(output_bytes);
|
|
||||||
} else {
|
|
||||||
rb.Push(static_cast<u32>(0));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Write(HLERequestContext& ctx) {
|
Result Write(InBuffer<BufferAttr_HipcMapAlias> data, Out<u32> out_size) {
|
||||||
size_t write_size{0};
|
R_UNLESS(did_handshake, ResultInternalError);
|
||||||
const Result res = WriteImpl(&write_size, ctx.ReadBuffer());
|
size_t tmp{};
|
||||||
IPC::ResponseBuilder rb{ctx, 3};
|
auto const res = backend->Write(&tmp, data);
|
||||||
rb.Push(res);
|
*out_size = u32(tmp);
|
||||||
rb.Push(static_cast<u32>(write_size));
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Pending(HLERequestContext& ctx) {
|
Result Pending(Out<s32> out_pending_size) {
|
||||||
s32 pending_size{0};
|
LOG_WARNING(Service_SSL, "(STUBBED)");
|
||||||
const Result res = PendingImpl(&pending_size);
|
*out_pending_size = s32(backend->Pending());
|
||||||
IPC::ResponseBuilder rb{ctx, 3};
|
R_SUCCEED();
|
||||||
rb.Push(res);
|
|
||||||
rb.Push<s32>(pending_size);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void SetSessionCacheMode(HLERequestContext& ctx) {
|
Result Peek(OutBuffer<BufferAttr_HipcMapAlias> data, Out<u32> out_size) {
|
||||||
IPC::RequestParser rp{ctx};
|
LOG_WARNING(Service_SSL, "(STUBBED)");
|
||||||
const u32 mode = rp.Pop<u32>();
|
size_t tmp{};
|
||||||
const Result res = SetSessionCacheModeImpl(mode);
|
auto const res = backend->Peek(&tmp, data);
|
||||||
IPC::ResponseBuilder rb{ctx, 2};
|
*out_size = u32(tmp);
|
||||||
rb.Push(res);
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SetOption(HLERequestContext& ctx) {
|
Result Poll(u32 in_pollevent, u32 timer, Out<u32> out_pollevent) {
|
||||||
struct Parameters {
|
LOG_WARNING(Service_SSL, "(STUBBED)");
|
||||||
OptionType option;
|
R_SUCCEED();
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void GetOption(HLERequestContext& ctx) {
|
Result GetVerifyCertError() {
|
||||||
IPC::RequestParser rp{ctx};
|
LOG_WARNING(Service_SSL, "(STUBBED)");
|
||||||
const auto option = rp.PopRaw<OptionType>();
|
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) {
|
switch (option) {
|
||||||
case OptionType::DoNotCloseSocket:
|
case OptionType::DoNotCloseSocket:
|
||||||
value = static_cast<u8>(do_not_close_socket);
|
do_not_close_socket = bool(value);
|
||||||
break;
|
break;
|
||||||
case OptionType::GetServerCertChain:
|
case OptionType::GetServerCertChain:
|
||||||
value = static_cast<u8>(get_server_cert_chain);
|
get_server_cert_chain = bool(value);
|
||||||
break;
|
break;
|
||||||
case OptionType::SkipDefaultVerify:
|
case OptionType::SkipDefaultVerify:
|
||||||
value = static_cast<u8>(skip_default_verify);
|
skip_default_verify = bool(value);
|
||||||
break;
|
break;
|
||||||
case OptionType::EnableAlpn:
|
case OptionType::EnableAlpn:
|
||||||
value = static_cast<u8>(enable_alpn);
|
enable_alpn = bool(value);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
LOG_WARNING(Service_SSL, "Unknown option={}", option);
|
LOG_WARNING(Service_SSL, "Unknown option={}, value={}", option, value);
|
||||||
value = 0;
|
}
|
||||||
|
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;
|
break;
|
||||||
}
|
}
|
||||||
|
LOG_DEBUG(Service_SSL, "GetOption called, option={}, ret value={}", u32(option), *value);
|
||||||
LOG_DEBUG(Service_SSL, "GetOption called, option={}, ret value={}", option, value);
|
R_SUCCEED();
|
||||||
|
|
||||||
IPC::ResponseBuilder rb{ctx, 3};
|
|
||||||
rb.Push(ResultSuccess);
|
|
||||||
rb.Push<u8>(value);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void SetNextAlpnProto(HLERequestContext& ctx) {
|
Result SetNextAlpnProto(InBuffer<BufferAttr_HipcMapAlias> data) {
|
||||||
const auto data = ctx.ReadBuffer(0);
|
auto const to_write = u32((std::min)(next_alpn_proto.size(), data.size()));
|
||||||
next_alpn_proto.assign(data.begin(), data.end());
|
next_alpn_proto.assign(data.begin(), data.begin() + to_write);
|
||||||
|
|
||||||
LOG_DEBUG(Service_SSL, "SetNextAlpnProto called, size={}", next_alpn_proto.size());
|
LOG_DEBUG(Service_SSL, "SetNextAlpnProto called, size={}", next_alpn_proto.size());
|
||||||
|
R_SUCCEED();
|
||||||
IPC::ResponseBuilder rb{ctx, 2};
|
|
||||||
rb.Push(ResultSuccess);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void GetNextAlpnProto(HLERequestContext& ctx) {
|
Result GetNextAlpnProto(OutBuffer<BufferAttr_HipcMapAlias> data, Out<u32> to_write) {
|
||||||
const size_t writable = ctx.GetWriteBufferSize();
|
*to_write = u32((std::min)(next_alpn_proto.size(), data.size()));
|
||||||
const size_t to_write = (std::min)(next_alpn_proto.size(), writable);
|
next_alpn_proto.assign(data.begin(), data.begin() + *to_write);
|
||||||
|
LOG_DEBUG(Service_SSL, "GetNextAlpnProto called, size={}", *to_write);
|
||||||
if (to_write != 0) {
|
R_SUCCEED();
|
||||||
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 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> {
|
class ISslContext final : public ServiceFramework<ISslContext> {
|
||||||
@@ -492,7 +461,7 @@ public:
|
|||||||
{5, &ISslContext::ImportClientPki, "ImportClientPki"},
|
{5, &ISslContext::ImportClientPki, "ImportClientPki"},
|
||||||
{6, nullptr, "RemoveServerPki"},
|
{6, nullptr, "RemoveServerPki"},
|
||||||
{7, nullptr, "RemoveClientPki"},
|
{7, nullptr, "RemoveClientPki"},
|
||||||
{8, nullptr, "RegisterInternalPki"},
|
{8, D<&ISslContext::RegisterInternalPki>, "RegisterInternalPki"},
|
||||||
{9, nullptr, "AddPolicyOid"},
|
{9, nullptr, "AddPolicyOid"},
|
||||||
{10, nullptr, "ImportCrl"},
|
{10, nullptr, "ImportCrl"},
|
||||||
{11, nullptr, "RemoveCrl"},
|
{11, nullptr, "RemoveCrl"},
|
||||||
@@ -587,6 +556,11 @@ private:
|
|||||||
rb.Push(ResultSuccess);
|
rb.Push(ResultSuccess);
|
||||||
rb.Push(client_id);
|
rb.Push(client_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Result RegisterInternalPki() {
|
||||||
|
LOG_WARNING(Service_SSL, "(STUBBED) called");
|
||||||
|
R_SUCCEED();
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
class ISslService final : public ServiceFramework<ISslService> {
|
class ISslService final : public ServiceFramework<ISslService> {
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user