From 626ea63cbc0e8a74da462c1758a7345833862cff Mon Sep 17 00:00:00 2001 From: Zephyron Date: Wed, 28 Jan 2026 16:42:35 +1000 Subject: [PATCH] feat(hle): implement misc service stubs for QLaunch compatibility - audctl: Add GetAudioController (cmd 5000) returning IAudioController - vi: Improve display service with better non-default display handling - psc/ovln: Add GetReceiveEventHandle for overlay notifications - omm: Add GetNotificationMessageEventHandle for power state - olsc: Implement GetNativeHandle returning valid event - prepo: Add SaveSystemReport2 (cmd 20102) stub --- .../hle/service/audio/audio_controller.cpp | 11 ++++- src/core/hle/service/audio/audio_controller.h | 3 +- .../hle/service/olsc/native_handle_holder.cpp | 14 ++++-- .../hle/service/olsc/native_handle_holder.h | 8 ++++ .../hle/service/omm/power_state_interface.cpp | 18 +++++-- .../hle/service/omm/power_state_interface.h | 13 +++++ src/core/hle/service/prepo/prepo.cpp | 8 ++++ src/core/hle/service/psc/ovln/receiver.cpp | 18 +++++-- src/core/hle/service/psc/ovln/receiver.h | 13 +++++ .../vi/application_display_service.cpp | 48 +++++++++++++++---- 10 files changed, 133 insertions(+), 21 deletions(-) diff --git a/src/core/hle/service/audio/audio_controller.cpp b/src/core/hle/service/audio/audio_controller.cpp index 1dbcacbb9..a7a50c093 100644 --- a/src/core/hle/service/audio/audio_controller.cpp +++ b/src/core/hle/service/audio/audio_controller.cpp @@ -1,5 +1,5 @@ // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project -// SPDX-FileCopyrightText: Copyright 2025 citron Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 citron Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later #include "common/logging/log.h" @@ -72,7 +72,7 @@ IAudioController::IAudioController(Core::System& system_) {50001, D<&IAudioController::OverrideDefaultTargetForDebug>, "OverrideDefaultTargetForDebug"}, // [19.0.0-19.0.1] {50003, D<&IAudioController::SetForceOverrideExternalDeviceNameForDebug>, "SetForceOverrideExternalDeviceNameForDebug"}, // [19.0.0+] {50004, D<&IAudioController::ClearForceOverrideExternalDeviceNameForDebug>, "ClearForceOverrideExternalDeviceNameForDebug"}, // [19.0.0+] - {5000, nullptr, "Unknown5000"}, // [19.0.0+] + {5000, D<&IAudioController::GetAudioController>, "GetAudioController"}, // [19.0.0+] {10200, D<&IAudioController::Unknown10200>, "Unknown10200"}, // [20.0.0+] }; // clang-format on @@ -407,4 +407,11 @@ Result IAudioController::Unknown10200() { R_SUCCEED(); } +Result IAudioController::GetAudioController( + Out> out_audio_controller) { + LOG_DEBUG(Audio, "called GetAudioController [19.0.0+]"); + *out_audio_controller = std::make_shared(system); + R_SUCCEED(); +} + } // namespace Service::Audio diff --git a/src/core/hle/service/audio/audio_controller.h b/src/core/hle/service/audio/audio_controller.h index 9a4e06e5e..5ef671e5c 100644 --- a/src/core/hle/service/audio/audio_controller.h +++ b/src/core/hle/service/audio/audio_controller.h @@ -1,5 +1,5 @@ // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project -// SPDX-FileCopyrightText: Copyright 2025 citron Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 citron Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later #pragma once @@ -90,6 +90,7 @@ private: Result OverrideDefaultTargetForDebug(u32 target); // [19.0.0-19.0.1] Result SetForceOverrideExternalDeviceNameForDebug(InLargeData, BufferAttr_HipcMapAlias> device_name); // [19.0.0+] Result ClearForceOverrideExternalDeviceNameForDebug(); // [19.0.0+] + Result GetAudioController(Out> out_audio_controller); // [19.0.0+] Result Unknown10200(); // [20.0.0+] KernelHelpers::ServiceContext service_context; diff --git a/src/core/hle/service/olsc/native_handle_holder.cpp b/src/core/hle/service/olsc/native_handle_holder.cpp index 3cb5d7b11..f0625b1b2 100644 --- a/src/core/hle/service/olsc/native_handle_holder.cpp +++ b/src/core/hle/service/olsc/native_handle_holder.cpp @@ -1,4 +1,5 @@ // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 citron Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later #include "core/hle/service/cmif_serialization.h" @@ -7,7 +8,8 @@ namespace Service::OLSC { INativeHandleHolder::INativeHandleHolder(Core::System& system_) - : ServiceFramework{system_, "INativeHandleHolder"} { + : ServiceFramework{system_, "INativeHandleHolder"}, + service_context{system_, "INativeHandleHolder"} { // clang-format off static const FunctionInfo functions[] = { {0, D<&INativeHandleHolder::GetNativeHandle>, "GetNativeHandle"}, @@ -15,13 +17,17 @@ INativeHandleHolder::INativeHandleHolder(Core::System& system_) // clang-format on RegisterHandlers(functions); + + event = service_context.CreateEvent("INativeHandleHolder:Event"); } -INativeHandleHolder::~INativeHandleHolder() = default; +INativeHandleHolder::~INativeHandleHolder() { + service_context.CloseEvent(event); +} Result INativeHandleHolder::GetNativeHandle(OutCopyHandle out_event) { - LOG_WARNING(Service_OLSC, "(STUBBED) called"); - *out_event = nullptr; + LOG_DEBUG(Service_OLSC, "called"); + *out_event = &event->GetReadableEvent(); R_SUCCEED(); } diff --git a/src/core/hle/service/olsc/native_handle_holder.h b/src/core/hle/service/olsc/native_handle_holder.h index a44754c20..fad6e1006 100644 --- a/src/core/hle/service/olsc/native_handle_holder.h +++ b/src/core/hle/service/olsc/native_handle_holder.h @@ -1,11 +1,16 @@ // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 citron Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later +#pragma once + #include "core/hle/service/cmif_types.h" +#include "core/hle/service/kernel_helpers.h" #include "core/hle/service/service.h" namespace Kernel { class KReadableEvent; +class KEvent; } namespace Service::OLSC { @@ -17,6 +22,9 @@ public: private: Result GetNativeHandle(OutCopyHandle out_event); + + KernelHelpers::ServiceContext service_context; + Kernel::KEvent* event; }; } // namespace Service::OLSC diff --git a/src/core/hle/service/omm/power_state_interface.cpp b/src/core/hle/service/omm/power_state_interface.cpp index 22cac8259..08d9d96e7 100644 --- a/src/core/hle/service/omm/power_state_interface.cpp +++ b/src/core/hle/service/omm/power_state_interface.cpp @@ -1,19 +1,21 @@ // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later +#include "core/hle/kernel/k_event.h" +#include "core/hle/service/cmif_serialization.h" #include "core/hle/service/omm/power_state_interface.h" namespace Service::OMM { IPowerStateInterface::IPowerStateInterface(Core::System& system_) - : ServiceFramework{system_, "spsm"} { + : ServiceFramework{system_, "spsm"}, service_context{system_, "IPowerStateInterface"} { // clang-format off static const FunctionInfo functions[] = { {0, nullptr, "GetState"}, {1, nullptr, "EnterSleep"}, {2, nullptr, "GetLastWakeReason"}, {3, nullptr, "Shutdown"}, - {4, nullptr, "GetNotificationMessageEventHandle"}, + {4, D<&IPowerStateInterface::GetNotificationMessageEventHandle>, "GetNotificationMessageEventHandle"}, {5, nullptr, "ReceiveNotificationMessage"}, {6, nullptr, "AnalyzeLogForLastSleepWakeSequence"}, {7, nullptr, "ResetEventLog"}, @@ -25,8 +27,18 @@ IPowerStateInterface::IPowerStateInterface(Core::System& system_) // clang-format on RegisterHandlers(functions); + notification_event = service_context.CreateEvent("IPowerStateInterface:NotificationEvent"); } -IPowerStateInterface::~IPowerStateInterface() = default; +IPowerStateInterface::~IPowerStateInterface() { + service_context.CloseEvent(notification_event); +} + +Result IPowerStateInterface::GetNotificationMessageEventHandle( + OutCopyHandle out_event) { + LOG_DEBUG(Service, "called"); + *out_event = ¬ification_event->GetReadableEvent(); + R_SUCCEED(); +} } // namespace Service::OMM diff --git a/src/core/hle/service/omm/power_state_interface.h b/src/core/hle/service/omm/power_state_interface.h index 825a6512d..6f5c19516 100644 --- a/src/core/hle/service/omm/power_state_interface.h +++ b/src/core/hle/service/omm/power_state_interface.h @@ -3,8 +3,15 @@ #pragma once +#include "core/hle/service/cmif_types.h" +#include "core/hle/service/kernel_helpers.h" #include "core/hle/service/service.h" +namespace Kernel { +class KEvent; +class KReadableEvent; +} // namespace Kernel + namespace Core { class System; } @@ -15,6 +22,12 @@ class IPowerStateInterface final : public ServiceFramework public: explicit IPowerStateInterface(Core::System& system_); ~IPowerStateInterface() override; + +private: + Result GetNotificationMessageEventHandle(OutCopyHandle out_event); + + KernelHelpers::ServiceContext service_context; + Kernel::KEvent* notification_event; }; } // namespace Service::OMM diff --git a/src/core/hle/service/prepo/prepo.cpp b/src/core/hle/service/prepo/prepo.cpp index fd4614298..ad00374ce 100644 --- a/src/core/hle/service/prepo/prepo.cpp +++ b/src/core/hle/service/prepo/prepo.cpp @@ -30,6 +30,7 @@ public: {10500, &PlayReport::SendReportWithUser, "SendReportWithUser"}, {20100, &PlayReport::SaveSystemReport, "SaveSystemReport"}, {20101, &PlayReport::SaveSystemReportWithUser, "SaveSystemReportWithUser"}, + {20102, &PlayReport::SaveSystemReport2, "SaveSystemReport2"}, {20200, &PlayReport::SetOperationMode, "SetOperationMode"}, {30100, &PlayReport::ClearStorage, "ClearStorage"}, {30200, &PlayReport::ClearStatistics, "ClearStatistics"}, @@ -160,6 +161,13 @@ private: rb.Push(ResultSuccess); } + void SaveSystemReport2(HLERequestContext& ctx) { + LOG_WARNING(Service_PREPO, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); + } + void SetOperationMode(HLERequestContext& ctx) { LOG_WARNING(Service_PREPO, "(STUBBED) called"); diff --git a/src/core/hle/service/psc/ovln/receiver.cpp b/src/core/hle/service/psc/ovln/receiver.cpp index 85f62816d..371ee5f83 100644 --- a/src/core/hle/service/psc/ovln/receiver.cpp +++ b/src/core/hle/service/psc/ovln/receiver.cpp @@ -1,24 +1,36 @@ // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later +#include "core/hle/kernel/k_event.h" +#include "core/hle/service/cmif_serialization.h" #include "core/hle/service/psc/ovln/receiver.h" namespace Service::PSC { -IReceiver::IReceiver(Core::System& system_) : ServiceFramework{system_, "IReceiver"} { +IReceiver::IReceiver(Core::System& system_) + : ServiceFramework{system_, "IReceiver"}, service_context{system_, "IReceiver"} { // clang-format off static const FunctionInfo functions[] = { {0, nullptr, "AddSource"}, {1, nullptr, "RemoveSource"}, - {2, nullptr, "GetReceiveEventHandle"}, + {2, D<&IReceiver::GetReceiveEventHandle>, "GetReceiveEventHandle"}, {3, nullptr, "Receive"}, {4, nullptr, "ReceiveWithTick"}, }; // clang-format on RegisterHandlers(functions); + event = service_context.CreateEvent("IReceiver:Event"); } -IReceiver::~IReceiver() = default; +IReceiver::~IReceiver() { + service_context.CloseEvent(event); +} + +Result IReceiver::GetReceiveEventHandle(OutCopyHandle out_event) { + LOG_DEBUG(Service_PSC, "called"); + *out_event = &event->GetReadableEvent(); + R_SUCCEED(); +} } // namespace Service::PSC diff --git a/src/core/hle/service/psc/ovln/receiver.h b/src/core/hle/service/psc/ovln/receiver.h index c47a4ff7e..9155decb7 100644 --- a/src/core/hle/service/psc/ovln/receiver.h +++ b/src/core/hle/service/psc/ovln/receiver.h @@ -3,14 +3,27 @@ #pragma once +#include "core/hle/service/cmif_types.h" +#include "core/hle/service/kernel_helpers.h" #include "core/hle/service/service.h" +namespace Kernel { +class KEvent; +class KReadableEvent; +} // namespace Kernel + namespace Service::PSC { class IReceiver final : public ServiceFramework { public: explicit IReceiver(Core::System& system_); ~IReceiver() override; + +private: + Result GetReceiveEventHandle(OutCopyHandle out_event); + + KernelHelpers::ServiceContext service_context; + Kernel::KEvent* event; }; } // namespace Service::PSC diff --git a/src/core/hle/service/vi/application_display_service.cpp b/src/core/hle/service/vi/application_display_service.cpp index 6b0bcb536..c2a1c4329 100644 --- a/src/core/hle/service/vi/application_display_service.cpp +++ b/src/core/hle/service/vi/application_display_service.cpp @@ -1,6 +1,9 @@ // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 citron Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later +#include + #include "core/hle/service/cmif_serialization.h" #include "core/hle/service/nvnflinger/hos_binder_driver.h" #include "core/hle/service/nvnflinger/parcel.h" @@ -88,8 +91,12 @@ Result IApplicationDisplayService::OpenDisplay(Out out_display_id, DisplayN LOG_WARNING(Service_VI, "(STUBBED) called"); display_name[display_name.size() - 1] = '\0'; - ASSERT_MSG(strcmp(display_name.data(), "Default") == 0, - "Non-default displays aren't supported yet"); + if (strcmp(display_name.data(), "Default") != 0) { + LOG_WARNING(Service_VI, "Non-default display '{}' requested, using Default display", + display_name.data()); + // Use Default display for non-default display requests + R_RETURN(m_container->OpenDisplay(out_display_id, DisplayName{"Default"})); + } R_RETURN(m_container->OpenDisplay(out_display_id, display_name)); } @@ -142,14 +149,39 @@ Result IApplicationDisplayService::SetLayerScalingMode(NintendoScaleMode scale_m Result IApplicationDisplayService::ListDisplays( Out out_count, OutArray out_displays) { - LOG_WARNING(Service_VI, "(STUBBED) called"); + LOG_DEBUG(Service_VI, "called"); - if (out_displays.size() > 0) { - out_displays[0] = DisplayInfo{}; - *out_count = 1; - } else { - *out_count = 0; + // QLaunch expects multiple displays: Default, Edid, Internal, External, Null + struct DisplayEntry { + const char* name; + u8 has_limited_layers; + u64 max_layers; + u64 width; + u64 height; + }; + + static constexpr std::array display_entries{{ + {"Default", 1, 1, 1920, 1080}, + {"Edid", 1, 1, 1920, 1080}, + {"Internal", 1, 1, 1280, 720}, + {"External", 1, 1, 1920, 1080}, + {"Null", 0, 0, 0, 0}, + }}; + + const u64 display_count = + std::min(static_cast(display_entries.size()), out_displays.size()); + + for (u64 i = 0; i < display_count; ++i) { + DisplayInfo info{}; + std::strncpy(info.display_name.data(), display_entries[i].name, + info.display_name.size() - 1); + info.has_limited_layers = display_entries[i].has_limited_layers; + info.max_layers = display_entries[i].max_layers; + info.width = display_entries[i].width; + info.height = display_entries[i].height; + out_displays[i] = info; } + *out_count = display_count; R_SUCCEED(); }