[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 <maranbr@eden-emu.dev> Reviewed-by: DraVee <dravee@eden-emu.dev> Co-authored-by: Maufeat <sahyno1996@gmail.com> Co-committed-by: Maufeat <sahyno1996@gmail.com>
This commit is contained in:
parent
de449d2caa
commit
f74c590a8e
|
|
@ -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-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
|
||||||
|
|
||||||
|
#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_data_broker.h"
|
||||||
#include "core/hle/service/am/applet_manager.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/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/library_applet_accessor.h"
|
||||||
#include "core/hle/service/am/service/storage.h"
|
#include "core/hle/service/am/service/storage.h"
|
||||||
#include "core/hle/service/cmif_serialization.h"
|
#include "core/hle/service/cmif_serialization.h"
|
||||||
|
|
||||||
namespace Service::AM {
|
namespace Service::AM {
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
void EnableSingleUserPlay(const std::shared_ptr<LibraryAppletStorage>& 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<LibraryAppletStorage>& 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_,
|
ILibraryAppletAccessor::ILibraryAppletAccessor(Core::System& system_,
|
||||||
std::shared_ptr<AppletDataBroker> broker,
|
std::shared_ptr<AppletDataBroker> broker,
|
||||||
std::shared_ptr<Applet> applet)
|
std::shared_ptr<Applet> applet)
|
||||||
|
|
@ -106,19 +142,45 @@ Result ILibraryAppletAccessor::Unknown90() {
|
||||||
|
|
||||||
Result ILibraryAppletAccessor::PushInData(SharedPointer<IStorage> storage) {
|
Result ILibraryAppletAccessor::PushInData(SharedPointer<IStorage> storage) {
|
||||||
LOG_DEBUG(Service_AM, "called");
|
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);
|
m_broker->GetInData().Push(storage);
|
||||||
R_SUCCEED();
|
R_SUCCEED();
|
||||||
}
|
}
|
||||||
|
|
||||||
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) {
|
if (auto caller_applet = m_applet->caller_applet.lock(); caller_applet) {
|
||||||
caller_applet->lifecycle_manager.GetSystemEvent().Signal();
|
caller_applet->lifecycle_manager.GetSystemEvent().Signal();
|
||||||
caller_applet->lifecycle_manager.RequestResumeNotification();
|
caller_applet->lifecycle_manager.RequestResumeNotification();
|
||||||
caller_applet->lifecycle_manager.GetSystemEvent().Clear();
|
caller_applet->lifecycle_manager.GetSystemEvent().Clear();
|
||||||
caller_applet->lifecycle_manager.UpdateRequestedFocusState();
|
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<IStorage> storage) {
|
Result ILibraryAppletAccessor::PushInteractiveInData(SharedPointer<IStorage> storage) {
|
||||||
|
|
|
||||||
|
|
@ -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 2024 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||||
|
|
@ -83,7 +83,7 @@ ILibraryAppletSelfAccessor::ILibraryAppletSelfAccessor(Core::System& system_,
|
||||||
{130, nullptr, "GetGpuErrorDetectedSystemEvent"},
|
{130, nullptr, "GetGpuErrorDetectedSystemEvent"},
|
||||||
{140, nullptr, "SetApplicationMemoryReservation"},
|
{140, nullptr, "SetApplicationMemoryReservation"},
|
||||||
{150, D<&ILibraryAppletSelfAccessor::ShouldSetGpuTimeSliceManually>, "ShouldSetGpuTimeSliceManually"},
|
{150, D<&ILibraryAppletSelfAccessor::ShouldSetGpuTimeSliceManually>, "ShouldSetGpuTimeSliceManually"},
|
||||||
{160, D<&ILibraryAppletSelfAccessor::Cmd160>, "Cmd160"},
|
{160, D<&ILibraryAppletSelfAccessor::GetLibraryAppletInfoEx>, "GetLibraryAppletInfoEx"},
|
||||||
};
|
};
|
||||||
// clang-format on
|
// clang-format on
|
||||||
RegisterHandlers(functions);
|
RegisterHandlers(functions);
|
||||||
|
|
@ -323,9 +323,13 @@ Result ILibraryAppletSelfAccessor::ShouldSetGpuTimeSliceManually(
|
||||||
R_SUCCEED();
|
R_SUCCEED();
|
||||||
}
|
}
|
||||||
|
|
||||||
Result ILibraryAppletSelfAccessor::Cmd160(Out<u64> out_unknown0) {
|
Result ILibraryAppletSelfAccessor::GetLibraryAppletInfoEx(
|
||||||
LOG_WARNING(Service_AM, "(STUBBED) called");
|
Out<LibraryAppletInfo> out_library_applet_info) {
|
||||||
*out_unknown0 = 0;
|
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();
|
R_SUCCEED();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
||||||
|
|
@ -75,7 +78,7 @@ private:
|
||||||
Result GetMainAppletAvailableUsers(Out<bool> out_can_select_any_user, Out<s32> out_users_count,
|
Result GetMainAppletAvailableUsers(Out<bool> out_can_select_any_user, Out<s32> out_users_count,
|
||||||
OutArray<Common::UUID, BufferAttr_HipcMapAlias> out_users);
|
OutArray<Common::UUID, BufferAttr_HipcMapAlias> out_users);
|
||||||
Result ShouldSetGpuTimeSliceManually(Out<bool> out_should_set_gpu_time_slice_manually);
|
Result ShouldSetGpuTimeSliceManually(Out<bool> out_should_set_gpu_time_slice_manually);
|
||||||
Result Cmd160(Out<u64> out_unknown0);
|
Result GetLibraryAppletInfoEx(Out<LibraryAppletInfo> out_library_applet_info);
|
||||||
|
|
||||||
const std::shared_ptr<Applet> m_applet;
|
const std::shared_ptr<Applet> m_applet;
|
||||||
const std::shared_ptr<AppletDataBroker> m_broker;
|
const std::shared_ptr<AppletDataBroker> m_broker;
|
||||||
|
|
|
||||||
|
|
@ -361,7 +361,7 @@ IApplicationManagerInterface::IApplicationManagerInterface(Core::System& system_
|
||||||
{2517, nullptr, "CreateApplicationInstance"},
|
{2517, nullptr, "CreateApplicationInstance"},
|
||||||
{2518, nullptr, "UpdateQualificationForDebug"},
|
{2518, nullptr, "UpdateQualificationForDebug"},
|
||||||
{2519, nullptr, "IsQualificationTransitionSupported"},
|
{2519, nullptr, "IsQualificationTransitionSupported"},
|
||||||
{2520, nullptr, "IsQualificationTransitionSupportedByProcessId"},
|
{2520, D<&IApplicationManagerInterface::IsQualificationTransitionSupportedByProcessId>, "IsQualificationTransitionSupportedByProcessId"},
|
||||||
{2521, nullptr, "GetRightsUserChangedEvent"},
|
{2521, nullptr, "GetRightsUserChangedEvent"},
|
||||||
{2522, nullptr, "IsRomRedirectionAvailable"},
|
{2522, nullptr, "IsRomRedirectionAvailable"},
|
||||||
{2523, nullptr, "GetProgramId"}, //17.0.0+
|
{2523, nullptr, "GetProgramId"}, //17.0.0+
|
||||||
|
|
@ -769,6 +769,13 @@ Result IApplicationManagerInterface::ResumeAll() {
|
||||||
R_SUCCEED();
|
R_SUCCEED();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Result IApplicationManagerInterface::IsQualificationTransitionSupportedByProcessId(
|
||||||
|
Out<bool> 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<s64> out_total_space_size,
|
Result IApplicationManagerInterface::GetStorageSize(Out<s64> out_total_space_size,
|
||||||
Out<s64> out_free_space_size,
|
Out<s64> out_free_space_size,
|
||||||
FileSys::StorageId storage_id) {
|
FileSys::StorageId storage_id) {
|
||||||
|
|
|
||||||
|
|
@ -53,6 +53,8 @@ public:
|
||||||
Result GetFreeSpaceSize(Out<s64> out_free_space_size, FileSys::StorageId storage_id);
|
Result GetFreeSpaceSize(Out<s64> out_free_space_size, FileSys::StorageId storage_id);
|
||||||
Result GetGameCardUpdateDetectionEvent(OutCopyHandle<Kernel::KReadableEvent> out_event);
|
Result GetGameCardUpdateDetectionEvent(OutCopyHandle<Kernel::KReadableEvent> out_event);
|
||||||
Result ResumeAll();
|
Result ResumeAll();
|
||||||
|
Result IsQualificationTransitionSupportedByProcessId(Out<bool> out_is_supported,
|
||||||
|
u64 process_id);
|
||||||
Result GetStorageSize(Out<s64> out_total_space_size, Out<s64> out_free_space_size,
|
Result GetStorageSize(Out<s64> out_total_space_size, Out<s64> out_free_space_size,
|
||||||
FileSys::StorageId storage_id);
|
FileSys::StorageId storage_id);
|
||||||
Result TouchApplication(u64 application_id);
|
Result TouchApplication(u64 application_id);
|
||||||
|
|
|
||||||
|
|
@ -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 2024 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||||
|
|
@ -68,9 +68,9 @@ Result IDynamicRightsInterface::VerifyActivatedRightsOwners(u64 rights_handle) {
|
||||||
}
|
}
|
||||||
|
|
||||||
Result IDynamicRightsInterface::HasAccountRestrictedRightsInRunningApplications(
|
Result IDynamicRightsInterface::HasAccountRestrictedRightsInRunningApplications(
|
||||||
Out<u32> out_status, u64 rights_handle) {
|
Out<bool> out_is_restricted) {
|
||||||
LOG_WARNING(Service_NS, "(STUBBED) called, rights_handle={:#x}", rights_handle);
|
LOG_WARNING(Service_NS, "(STUBBED) called");
|
||||||
*out_status = 0;
|
*out_is_restricted = 0;
|
||||||
R_SUCCEED();
|
R_SUCCEED();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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 2024 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||||
|
|
@ -20,8 +20,7 @@ private:
|
||||||
Result NotifyApplicationRightsCheckStart();
|
Result NotifyApplicationRightsCheckStart();
|
||||||
Result GetRunningApplicationStatus(Out<u32> out_status, u64 rights_handle);
|
Result GetRunningApplicationStatus(Out<u32> out_status, u64 rights_handle);
|
||||||
Result VerifyActivatedRightsOwners(u64 rights_handle);
|
Result VerifyActivatedRightsOwners(u64 rights_handle);
|
||||||
Result HasAccountRestrictedRightsInRunningApplications(Out<u32> out_status,
|
Result HasAccountRestrictedRightsInRunningApplications(Out<bool> out_is_restricted);
|
||||||
u64 rights_handle);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace Service::NS
|
} // namespace Service::NS
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue