From f74c590a8e14e910f6cee65f0954d950e377a483 Mon Sep 17 00:00:00 2001 From: Maufeat Date: Fri, 23 Jan 2026 15:19:14 +0100 Subject: [PATCH] [hle/ns/am] Hijacks PlayerSelect Data to enable player selection and fix structs and returns on ns/am (#3374) Makes games like Alien Hominid let you skip with only 1 user enabled :) Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/3374 Reviewed-by: MaranBr Reviewed-by: DraVee Co-authored-by: Maufeat Co-committed-by: Maufeat --- .../am/service/library_applet_accessor.cpp | 66 ++++++++++++++++++- .../service/library_applet_self_accessor.cpp | 14 ++-- .../am/service/library_applet_self_accessor.h | 5 +- .../ns/application_manager_interface.cpp | 9 ++- .../ns/application_manager_interface.h | 2 + .../service/ns/dynamic_rights_interface.cpp | 8 +-- .../hle/service/ns/dynamic_rights_interface.h | 5 +- 7 files changed, 93 insertions(+), 16 deletions(-) diff --git a/src/core/hle/service/am/service/library_applet_accessor.cpp b/src/core/hle/service/am/service/library_applet_accessor.cpp index 6b31fdb73c..c5b6929ab3 100644 --- a/src/core/hle/service/am/service/library_applet_accessor.cpp +++ b/src/core/hle/service/am/service/library_applet_accessor.cpp @@ -1,18 +1,54 @@ -// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later +#include "common/settings.h" +#include "core/hle/service/acc/profile_manager.h" #include "core/hle/service/am/applet_data_broker.h" #include "core/hle/service/am/applet_manager.h" +#include "core/hle/service/am/frontend/applet_profile_select.h" #include "core/hle/service/am/frontend/applets.h" +#include "core/hle/service/am/library_applet_storage.h" #include "core/hle/service/am/service/library_applet_accessor.h" #include "core/hle/service/am/service/storage.h" #include "core/hle/service/cmif_serialization.h" namespace Service::AM { +namespace { + +void EnableSingleUserPlay(const std::shared_ptr& impl) { + constexpr s64 DisplayOptionsOffset = 0x90; + constexpr s64 IsSkipEnabledOffset = 1; + constexpr s64 ShowSkipButtonOffset = 4; + constexpr bool enabled = true; + + impl->Write(DisplayOptionsOffset + IsSkipEnabledOffset, &enabled, sizeof(enabled)); + impl->Write(DisplayOptionsOffset + ShowSkipButtonOffset, &enabled, sizeof(enabled)); +} + +void ReplaceEmptyUuidWithCurrentUser(const std::shared_ptr& impl) { + Frontend::UiReturnArg return_arg{}; + impl->Read(0, &return_arg, sizeof(return_arg)); + + if (return_arg.uuid_selected.IsValid()) { + return; + } + + Service::Account::ProfileManager profile_manager; + const auto current_user_idx = Settings::values.current_user.GetValue(); + + if (auto uuid = profile_manager.GetUser(current_user_idx)) { + return_arg.result = 0; + return_arg.uuid_selected = *uuid; + impl->Write(0, &return_arg, sizeof(return_arg)); + } +} + +} // namespace + ILibraryAppletAccessor::ILibraryAppletAccessor(Core::System& system_, std::shared_ptr broker, std::shared_ptr applet) @@ -106,19 +142,45 @@ Result ILibraryAppletAccessor::Unknown90() { Result ILibraryAppletAccessor::PushInData(SharedPointer storage) { LOG_DEBUG(Service_AM, "called"); + + // Special case for ProfileSelect applet, to enable single user play as + // somehow some games want an additional user and not let you continue... + if (m_applet->applet_id == AppletId::ProfileSelect) { + auto impl = storage->GetImpl(); + const s64 size = impl->GetSize(); + + const bool is_ui_settings = size == sizeof(Frontend::UiSettings) || + size == sizeof(Frontend::UiSettingsV1); + if (is_ui_settings) { + EnableSingleUserPlay(impl); + } + } + m_broker->GetInData().Push(storage); R_SUCCEED(); } Result ILibraryAppletAccessor::PopOutData(Out> out_storage) { LOG_DEBUG(Service_AM, "called"); + if (auto caller_applet = m_applet->caller_applet.lock(); caller_applet) { caller_applet->lifecycle_manager.GetSystemEvent().Signal(); caller_applet->lifecycle_manager.RequestResumeNotification(); caller_applet->lifecycle_manager.GetSystemEvent().Clear(); caller_applet->lifecycle_manager.UpdateRequestedFocusState(); } - R_RETURN(m_broker->GetOutData().Pop(out_storage.Get())); + + R_TRY(m_broker->GetOutData().Pop(out_storage.Get())); + + if (m_applet->applet_id == AppletId::ProfileSelect && *out_storage) { + auto impl = (*out_storage)->GetImpl(); + + if (impl->GetSize() == sizeof(Frontend::UiReturnArg)) { + ReplaceEmptyUuidWithCurrentUser(impl); + } + } + + R_SUCCEED(); } Result ILibraryAppletAccessor::PushInteractiveInData(SharedPointer storage) { diff --git a/src/core/hle/service/am/service/library_applet_self_accessor.cpp b/src/core/hle/service/am/service/library_applet_self_accessor.cpp index 4d36d306bd..0f71f24b0b 100644 --- a/src/core/hle/service/am/service/library_applet_self_accessor.cpp +++ b/src/core/hle/service/am/service/library_applet_self_accessor.cpp @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project @@ -83,7 +83,7 @@ ILibraryAppletSelfAccessor::ILibraryAppletSelfAccessor(Core::System& system_, {130, nullptr, "GetGpuErrorDetectedSystemEvent"}, {140, nullptr, "SetApplicationMemoryReservation"}, {150, D<&ILibraryAppletSelfAccessor::ShouldSetGpuTimeSliceManually>, "ShouldSetGpuTimeSliceManually"}, - {160, D<&ILibraryAppletSelfAccessor::Cmd160>, "Cmd160"}, + {160, D<&ILibraryAppletSelfAccessor::GetLibraryAppletInfoEx>, "GetLibraryAppletInfoEx"}, }; // clang-format on RegisterHandlers(functions); @@ -323,9 +323,13 @@ Result ILibraryAppletSelfAccessor::ShouldSetGpuTimeSliceManually( R_SUCCEED(); } -Result ILibraryAppletSelfAccessor::Cmd160(Out out_unknown0) { - LOG_WARNING(Service_AM, "(STUBBED) called"); - *out_unknown0 = 0; +Result ILibraryAppletSelfAccessor::GetLibraryAppletInfoEx( + Out out_library_applet_info) { + LOG_INFO(Service_AM, "called"); + *out_library_applet_info = { + .applet_id = m_applet->applet_id, + .library_applet_mode = m_applet->library_applet_mode, + }; R_SUCCEED(); } diff --git a/src/core/hle/service/am/service/library_applet_self_accessor.h b/src/core/hle/service/am/service/library_applet_self_accessor.h index 8c850faa8e..78864a94eb 100644 --- a/src/core/hle/service/am/service/library_applet_self_accessor.h +++ b/src/core/hle/service/am/service/library_applet_self_accessor.h @@ -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-License-Identifier: GPL-2.0-or-later @@ -75,7 +78,7 @@ private: Result GetMainAppletAvailableUsers(Out out_can_select_any_user, Out out_users_count, OutArray out_users); Result ShouldSetGpuTimeSliceManually(Out out_should_set_gpu_time_slice_manually); - Result Cmd160(Out out_unknown0); + Result GetLibraryAppletInfoEx(Out out_library_applet_info); const std::shared_ptr m_applet; const std::shared_ptr m_broker; diff --git a/src/core/hle/service/ns/application_manager_interface.cpp b/src/core/hle/service/ns/application_manager_interface.cpp index 783ecb5e07..bed5057f37 100644 --- a/src/core/hle/service/ns/application_manager_interface.cpp +++ b/src/core/hle/service/ns/application_manager_interface.cpp @@ -361,7 +361,7 @@ IApplicationManagerInterface::IApplicationManagerInterface(Core::System& system_ {2517, nullptr, "CreateApplicationInstance"}, {2518, nullptr, "UpdateQualificationForDebug"}, {2519, nullptr, "IsQualificationTransitionSupported"}, - {2520, nullptr, "IsQualificationTransitionSupportedByProcessId"}, + {2520, D<&IApplicationManagerInterface::IsQualificationTransitionSupportedByProcessId>, "IsQualificationTransitionSupportedByProcessId"}, {2521, nullptr, "GetRightsUserChangedEvent"}, {2522, nullptr, "IsRomRedirectionAvailable"}, {2523, nullptr, "GetProgramId"}, //17.0.0+ @@ -769,6 +769,13 @@ Result IApplicationManagerInterface::ResumeAll() { R_SUCCEED(); } +Result IApplicationManagerInterface::IsQualificationTransitionSupportedByProcessId( + Out out_is_supported, u64 process_id) { + LOG_WARNING(Service_NS, "(STUBBED) called, process_id={}", process_id); + *out_is_supported = true; + R_SUCCEED(); +} + Result IApplicationManagerInterface::GetStorageSize(Out out_total_space_size, Out out_free_space_size, FileSys::StorageId storage_id) { diff --git a/src/core/hle/service/ns/application_manager_interface.h b/src/core/hle/service/ns/application_manager_interface.h index 8717b55af5..ffb744d3fa 100644 --- a/src/core/hle/service/ns/application_manager_interface.h +++ b/src/core/hle/service/ns/application_manager_interface.h @@ -53,6 +53,8 @@ public: Result GetFreeSpaceSize(Out out_free_space_size, FileSys::StorageId storage_id); Result GetGameCardUpdateDetectionEvent(OutCopyHandle out_event); Result ResumeAll(); + Result IsQualificationTransitionSupportedByProcessId(Out out_is_supported, + u64 process_id); Result GetStorageSize(Out out_total_space_size, Out out_free_space_size, FileSys::StorageId storage_id); Result TouchApplication(u64 application_id); diff --git a/src/core/hle/service/ns/dynamic_rights_interface.cpp b/src/core/hle/service/ns/dynamic_rights_interface.cpp index 22c62293a2..0301085670 100644 --- a/src/core/hle/service/ns/dynamic_rights_interface.cpp +++ b/src/core/hle/service/ns/dynamic_rights_interface.cpp @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project @@ -68,9 +68,9 @@ Result IDynamicRightsInterface::VerifyActivatedRightsOwners(u64 rights_handle) { } Result IDynamicRightsInterface::HasAccountRestrictedRightsInRunningApplications( - Out out_status, u64 rights_handle) { - LOG_WARNING(Service_NS, "(STUBBED) called, rights_handle={:#x}", rights_handle); - *out_status = 0; + Out out_is_restricted) { + LOG_WARNING(Service_NS, "(STUBBED) called"); + *out_is_restricted = 0; R_SUCCEED(); } diff --git a/src/core/hle/service/ns/dynamic_rights_interface.h b/src/core/hle/service/ns/dynamic_rights_interface.h index e9429ea915..c3270aee05 100644 --- a/src/core/hle/service/ns/dynamic_rights_interface.h +++ b/src/core/hle/service/ns/dynamic_rights_interface.h @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project @@ -20,8 +20,7 @@ private: Result NotifyApplicationRightsCheckStart(); Result GetRunningApplicationStatus(Out out_status, u64 rights_handle); Result VerifyActivatedRightsOwners(u64 rights_handle); - Result HasAccountRestrictedRightsInRunningApplications(Out out_status, - u64 rights_handle); + Result HasAccountRestrictedRightsInRunningApplications(Out out_is_restricted); }; } // namespace Service::NS