diff --git a/src/core/hle/service/glue/glue.cpp b/src/core/hle/service/glue/glue.cpp index 993c3d21d..008e344d1 100644 --- a/src/core/hle/service/glue/glue.cpp +++ b/src/core/hle/service/glue/glue.cpp @@ -30,6 +30,7 @@ void LoopProcess(Core::System& system) { // Notification Services for application server_manager->RegisterNamedService("notif:a", std::make_shared(system)); + server_manager->RegisterNamedService("notif:s", std::make_shared(system)); ServerManager::RunServer(std::move(server_manager)); } diff --git a/src/core/hle/service/glue/notif.cpp b/src/core/hle/service/glue/notif.cpp index fec4ad86c..55584d639 100644 --- a/src/core/hle/service/glue/notif.cpp +++ b/src/core/hle/service/glue/notif.cpp @@ -8,6 +8,7 @@ #include "common/logging/log.h" #include "core/hle/service/glue/notif.h" #include "core/hle/service/ipc_helpers.h" +#include "core/hle/service/kernel_helpers.h" namespace Service::Glue { @@ -156,4 +157,210 @@ std::vector::iterator NOTIF_A::GetAlarmFromId( }); } +class INotificationSystemEventAccessor final + : public ServiceFramework { +public: + explicit INotificationSystemEventAccessor(Core::System& system_) + : ServiceFramework{system_, "INotificationSystemEventAccessor"}, service_context{ + system_, + "IBtmSystemCore"} { + // clang-format off + static const FunctionInfo functions[] = { + {0, &INotificationSystemEventAccessor::Unknown0, "Unknown0"}, + }; + // clang-format on + + RegisterHandlers(functions); + + radio_event = service_context.CreateEvent("INotificationSystemEventAccessor::RadioEvent"); + } + +private: + void Unknown0(HLERequestContext& ctx) { + LOG_WARNING(Service_NOTIF, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 2, 1}; + rb.Push(ResultSuccess); + rb.PushCopyObjects(radio_event->GetReadableEvent()); + } + + Kernel::KEvent* radio_event; + KernelHelpers::ServiceContext service_context; +}; + +NOTIF_S::NOTIF_S(Core::System& system_) : ServiceFramework{system_, "notif:s"} { + // clang-format off + static const FunctionInfo functions[] = { + {500, &NOTIF_S::RegisterAlarmSetting, "RegisterAlarmSetting"}, + {510, &NOTIF_S::UpdateAlarmSetting, "UpdateAlarmSetting"}, + {520, &NOTIF_S::ListAlarmSettings, "ListAlarmSettings"}, + {530, &NOTIF_S::LoadApplicationParameter, "LoadApplicationParameter"}, + {540, &NOTIF_S::DeleteAlarmSetting, "DeleteAlarmSetting"}, + {1000, &NOTIF_S::Initialize, "Initialize"}, + {1010, nullptr, "ListNotifications"}, + {1020, nullptr, "DeleteNotification"}, + {1030, nullptr, "ClearNotifications"}, + {1040, &NOTIF_S::OpenNotificationSystemEventAccessor, "OpenNotificationSystemEventAccessor"}, + {1500, nullptr, "SetPresentationSetting"}, + {1510, &NOTIF_S::GetPresentationSetting, "GetPresentationSetting"}, + {2000, nullptr, "GetAlarmSetting"}, + {2001, nullptr, "Unknown2001"}, + {2010, nullptr, "MuteAlarmSetting"}, + {2020, nullptr, "IsAlarmSettingReady"}, + {8000, nullptr, "Unknown8000"}, + {8010, nullptr, "Unknown8010"}, + {8999, nullptr, "Unknown8999"}, + {9000, nullptr, "GetNextAlarm"}, + + }; + // clang-format on + + RegisterHandlers(functions); +} + +NOTIF_S::~NOTIF_S() = default; + +void NOTIF_S::RegisterAlarmSetting(HLERequestContext& ctx) { + const auto alarm_setting_buffer_size = ctx.GetReadBufferSize(0); + const auto application_parameter_size = ctx.GetReadBufferSize(1); + + ASSERT_MSG(alarm_setting_buffer_size == sizeof(AlarmSetting), + "alarm_setting_buffer_size is not 0x40 bytes"); + ASSERT_MSG(application_parameter_size <= sizeof(ApplicationParameter), + "application_parameter_size is bigger than 0x400 bytes"); + + AlarmSetting new_alarm{}; + memcpy(&new_alarm, ctx.ReadBuffer(0).data(), sizeof(AlarmSetting)); + + // TODO: Count alarms per game id + if (alarms.size() >= max_alarms) { + LOG_ERROR(Service_NOTIF, "Alarm limit reached"); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultUnknown); + return; + } + + new_alarm.alarm_setting_id = last_alarm_setting_id++; + alarms.push_back(new_alarm); + + // TODO: Save application parameter data + + LOG_WARNING(Service_NOTIF, + "(STUBBED) called, application_parameter_size={}, setting_id={}, kind={}, muted={}", + application_parameter_size, new_alarm.alarm_setting_id, new_alarm.kind, + new_alarm.muted); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); + rb.Push(new_alarm.alarm_setting_id); +} + +void NOTIF_S::UpdateAlarmSetting(HLERequestContext& ctx) { + const auto alarm_setting_buffer_size = ctx.GetReadBufferSize(0); + const auto application_parameter_size = ctx.GetReadBufferSize(1); + + ASSERT_MSG(alarm_setting_buffer_size == sizeof(AlarmSetting), + "alarm_setting_buffer_size is not 0x40 bytes"); + ASSERT_MSG(application_parameter_size <= sizeof(ApplicationParameter), + "application_parameter_size is bigger than 0x400 bytes"); + + AlarmSetting alarm_setting{}; + memcpy(&alarm_setting, ctx.ReadBuffer(0).data(), sizeof(AlarmSetting)); + + const auto alarm_it = GetAlarmFromId(alarm_setting.alarm_setting_id); + if (alarm_it != alarms.end()) { + LOG_DEBUG(Service_NOTIF, "Alarm updated"); + *alarm_it = alarm_setting; + // TODO: Save application parameter data + } + + LOG_WARNING(Service_NOTIF, + "(STUBBED) called, application_parameter_size={}, setting_id={}, kind={}, muted={}", + application_parameter_size, alarm_setting.alarm_setting_id, alarm_setting.kind, + alarm_setting.muted); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void NOTIF_S::ListAlarmSettings(HLERequestContext& ctx) { + LOG_INFO(Service_NOTIF, "called, alarm_count={}", alarms.size()); + + // TODO: Only return alarms of this game id + ctx.WriteBuffer(alarms); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.Push(static_cast(alarms.size())); +} + +void NOTIF_S::LoadApplicationParameter(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto alarm_setting_id{rp.Pop()}; + + const auto alarm_it = GetAlarmFromId(alarm_setting_id); + if (alarm_it == alarms.end()) { + LOG_ERROR(Service_NOTIF, "Invalid alarm setting id={}", alarm_setting_id); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultUnknown); + return; + } + + // TODO: Read application parameter related to this setting id + ApplicationParameter application_parameter{}; + + LOG_WARNING(Service_NOTIF, "(STUBBED) called, alarm_setting_id={}", alarm_setting_id); + + ctx.WriteBuffer(application_parameter); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); + rb.Push(static_cast(application_parameter.size())); +} + +void NOTIF_S::DeleteAlarmSetting(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto alarm_setting_id{rp.Pop()}; + + std::erase_if(alarms, [alarm_setting_id](const AlarmSetting& alarm) { + return alarm.alarm_setting_id == alarm_setting_id; + }); + + LOG_INFO(Service_NOTIF, "called, alarm_setting_id={}", alarm_setting_id); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void NOTIF_S::Initialize(HLERequestContext& ctx) { + // TODO: Load previous alarms from config + + LOG_WARNING(Service_NOTIF, "(STUBBED) called"); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +std::vector::iterator NOTIF_S::GetAlarmFromId( + AlarmSettingId alarm_setting_id) { + return std::find_if(alarms.begin(), alarms.end(), + [alarm_setting_id](const AlarmSetting& alarm) { + return alarm.alarm_setting_id == alarm_setting_id; + }); +} + +void NOTIF_S::OpenNotificationSystemEventAccessor(HLERequestContext& ctx) { + LOG_ERROR(Service_NOTIF, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(ResultSuccess); + rb.PushIpcInterface(system); +} + +void NOTIF_S::GetPresentationSetting(HLERequestContext& ctx) { + LOG_INFO(Service_NOTIF, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + } // namespace Service::Glue diff --git a/src/core/hle/service/glue/notif.h b/src/core/hle/service/glue/notif.h index b1187f3a3..39bc528e5 100644 --- a/src/core/hle/service/glue/notif.h +++ b/src/core/hle/service/glue/notif.h @@ -68,5 +68,59 @@ private: std::vector alarms{}; AlarmSettingId last_alarm_setting_id{}; }; +class NOTIF_S final : public ServiceFramework { +public: + explicit NOTIF_S(Core::System& system_); + ~NOTIF_S() override; +private: + static constexpr std::size_t max_alarms = 8; + + // This is nn::notification::AlarmSettingId + using AlarmSettingId = u16; + static_assert(sizeof(AlarmSettingId) == 0x2, "AlarmSettingId is an invalid size"); + + using ApplicationParameter = std::array; + static_assert(sizeof(ApplicationParameter) == 0x400, "ApplicationParameter is an invalid size"); + + struct DailyAlarmSetting { + s8 hour; + s8 minute; + }; + static_assert(sizeof(DailyAlarmSetting) == 0x2, "DailyAlarmSetting is an invalid size"); + + struct WeeklyScheduleAlarmSetting { + INSERT_PADDING_BYTES(0xA); + std::array day_of_week; + }; + static_assert(sizeof(WeeklyScheduleAlarmSetting) == 0x18, + "WeeklyScheduleAlarmSetting is an invalid size"); + + // This is nn::notification::AlarmSetting + struct AlarmSetting { + AlarmSettingId alarm_setting_id; + u8 kind; + u8 muted; + INSERT_PADDING_BYTES(0x4); + Common::UUID account_id; + u64 application_id; + INSERT_PADDING_BYTES(0x8); + WeeklyScheduleAlarmSetting schedule; + }; + static_assert(sizeof(AlarmSetting) == 0x40, "AlarmSetting is an invalid size"); + + void RegisterAlarmSetting(HLERequestContext& ctx); + void UpdateAlarmSetting(HLERequestContext& ctx); + void ListAlarmSettings(HLERequestContext& ctx); + void LoadApplicationParameter(HLERequestContext& ctx); + void DeleteAlarmSetting(HLERequestContext& ctx); + void Initialize(HLERequestContext& ctx); + void OpenNotificationSystemEventAccessor(HLERequestContext& ctx); + void GetPresentationSetting(HLERequestContext& ctx); + + std::vector::iterator GetAlarmFromId(AlarmSettingId alarm_setting_id); + + std::vector alarms{}; + AlarmSettingId last_alarm_setting_id{}; +}; } // namespace Service::Glue