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
This commit is contained in:
Zephyron
2026-01-28 16:42:35 +10:00
parent 5fa71ce5b0
commit 626ea63cbc
10 changed files with 133 additions and 21 deletions

View File

@@ -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<SharedPointer<IAudioController>> out_audio_controller) {
LOG_DEBUG(Audio, "called GetAudioController [19.0.0+]");
*out_audio_controller = std::make_shared<IAudioController>(system);
R_SUCCEED();
}
} // namespace Service::Audio

View File

@@ -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<std::array<u8, 0x80>, BufferAttr_HipcMapAlias> device_name); // [19.0.0+]
Result ClearForceOverrideExternalDeviceNameForDebug(); // [19.0.0+]
Result GetAudioController(Out<SharedPointer<IAudioController>> out_audio_controller); // [19.0.0+]
Result Unknown10200(); // [20.0.0+]
KernelHelpers::ServiceContext service_context;

View File

@@ -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<Kernel::KReadableEvent> out_event) {
LOG_WARNING(Service_OLSC, "(STUBBED) called");
*out_event = nullptr;
LOG_DEBUG(Service_OLSC, "called");
*out_event = &event->GetReadableEvent();
R_SUCCEED();
}

View File

@@ -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<Kernel::KReadableEvent> out_event);
KernelHelpers::ServiceContext service_context;
Kernel::KEvent* event;
};
} // namespace Service::OLSC

View File

@@ -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<Kernel::KReadableEvent> out_event) {
LOG_DEBUG(Service, "called");
*out_event = &notification_event->GetReadableEvent();
R_SUCCEED();
}
} // namespace Service::OMM

View File

@@ -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<IPowerStateInterface>
public:
explicit IPowerStateInterface(Core::System& system_);
~IPowerStateInterface() override;
private:
Result GetNotificationMessageEventHandle(OutCopyHandle<Kernel::KReadableEvent> out_event);
KernelHelpers::ServiceContext service_context;
Kernel::KEvent* notification_event;
};
} // namespace Service::OMM

View File

@@ -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");

View File

@@ -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<Kernel::KReadableEvent> out_event) {
LOG_DEBUG(Service_PSC, "called");
*out_event = &event->GetReadableEvent();
R_SUCCEED();
}
} // namespace Service::PSC

View File

@@ -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<IReceiver> {
public:
explicit IReceiver(Core::System& system_);
~IReceiver() override;
private:
Result GetReceiveEventHandle(OutCopyHandle<Kernel::KReadableEvent> out_event);
KernelHelpers::ServiceContext service_context;
Kernel::KEvent* event;
};
} // namespace Service::PSC

View File

@@ -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 <cstring>
#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<u64> 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<u64> out_count, OutArray<DisplayInfo, BufferAttr_HipcMapAlias> 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<DisplayEntry, 5> 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<u64>(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();
}