// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later #include "common/settings.h" #include "core/hle/kernel/k_transfer_memory.h" #include "core/hle/service/am/applet_data_broker.h" #include "core/hle/service/am/applet_manager.h" #include "core/hle/service/am/frontend/applets.h" #include "core/hle/service/am/library_applet_accessor.h" #include "core/hle/service/am/library_applet_creator.h" #include "core/hle/service/am/library_applet_storage.h" #include "core/hle/service/am/storage.h" #include "core/hle/service/ipc_helpers.h" #include "core/hle/service/sm/sm.h" namespace Service::AM { namespace { bool ShouldCreateGuestApplet(AppletId applet_id) { #define X(Name, name) \ if (applet_id == AppletId::Name && \ Settings::values.name##_applet_mode.GetValue() != Settings::AppletMode::LLE) { \ return false; \ } X(Cabinet, cabinet) X(Controller, controller) X(DataErase, data_erase) X(Error, error) X(NetConnect, net_connect) X(ProfileSelect, player_select) X(SoftwareKeyboard, swkbd) X(MiiEdit, mii_edit) X(Web, web) X(Shop, shop) X(PhotoViewer, photo_viewer) X(OfflineWeb, offline_web) X(LoginShare, login_share) X(WebAuth, wifi_web_auth) X(MyPage, my_page) #undef X return true; } AppletProgramId AppletIdToProgramId(AppletId applet_id) { switch (applet_id) { case AppletId::OverlayDisplay: return AppletProgramId::OverlayDisplay; case AppletId::QLaunch: return AppletProgramId::QLaunch; case AppletId::Starter: return AppletProgramId::Starter; case AppletId::Auth: return AppletProgramId::Auth; case AppletId::Cabinet: return AppletProgramId::Cabinet; case AppletId::Controller: return AppletProgramId::Controller; case AppletId::DataErase: return AppletProgramId::DataErase; case AppletId::Error: return AppletProgramId::Error; case AppletId::NetConnect: return AppletProgramId::NetConnect; case AppletId::ProfileSelect: return AppletProgramId::ProfileSelect; case AppletId::SoftwareKeyboard: return AppletProgramId::SoftwareKeyboard; case AppletId::MiiEdit: return AppletProgramId::MiiEdit; case AppletId::Web: return AppletProgramId::Web; case AppletId::Shop: return AppletProgramId::Shop; case AppletId::PhotoViewer: return AppletProgramId::PhotoViewer; case AppletId::Settings: return AppletProgramId::Settings; case AppletId::OfflineWeb: return AppletProgramId::OfflineWeb; case AppletId::LoginShare: return AppletProgramId::LoginShare; case AppletId::WebAuth: return AppletProgramId::WebAuth; case AppletId::MyPage: return AppletProgramId::MyPage; default: return static_cast(0); } } std::shared_ptr CreateGuestApplet(Core::System& system, std::shared_ptr caller_applet, AppletId applet_id, LibraryAppletMode mode) { const auto program_id = static_cast(AppletIdToProgramId(applet_id)); if (program_id == 0) { // Unknown applet return {}; } auto process = std::make_unique(system); if (!process->Initialize(program_id)) { // Couldn't initialize the guest process return {}; } const auto applet = std::make_shared(system, std::move(process)); applet->program_id = program_id; applet->applet_id = applet_id; applet->type = AppletType::LibraryApplet; applet->library_applet_mode = mode; // Set focus state switch (mode) { case LibraryAppletMode::AllForeground: case LibraryAppletMode::NoUi: applet->focus_state = FocusState::InFocus; applet->hid_registration.EnableAppletToGetInput(true); applet->message_queue.PushMessage(AppletMessageQueue::AppletMessage::ChangeIntoForeground); applet->message_queue.PushMessage(AppletMessageQueue::AppletMessage::FocusStateChanged); break; case LibraryAppletMode::AllForegroundInitiallyHidden: applet->system_buffer_manager.SetWindowVisibility(false); applet->focus_state = FocusState::NotInFocus; applet->hid_registration.EnableAppletToGetInput(false); applet->message_queue.PushMessage(AppletMessageQueue::AppletMessage::FocusStateChanged); break; case LibraryAppletMode::PartialForeground: case LibraryAppletMode::PartialForegroundIndirectDisplay: default: applet->focus_state = FocusState::Background; applet->hid_registration.EnableAppletToGetInput(true); applet->message_queue.PushMessage(AppletMessageQueue::AppletMessage::FocusStateChanged); break; } auto broker = std::make_shared(system); applet->caller_applet = caller_applet; applet->caller_applet_broker = broker; system.GetAppletManager().InsertApplet(applet); return std::make_shared(system, broker, applet); } std::shared_ptr CreateFrontendApplet(Core::System& system, std::shared_ptr caller_applet, AppletId applet_id, LibraryAppletMode mode) { const auto program_id = static_cast(AppletIdToProgramId(applet_id)); auto process = std::make_unique(system); auto applet = std::make_shared(system, std::move(process)); applet->program_id = program_id; applet->applet_id = applet_id; applet->type = AppletType::LibraryApplet; applet->library_applet_mode = mode; auto storage = std::make_shared(system); applet->caller_applet = caller_applet; applet->caller_applet_broker = storage; applet->frontend = system.GetFrontendAppletHolder().GetApplet(applet, applet_id, mode); return std::make_shared(system, storage, applet); } } // namespace ILibraryAppletCreator::ILibraryAppletCreator(Core::System& system_, std::shared_ptr applet_) : ServiceFramework{system_, "ILibraryAppletCreator"}, applet{std::move(applet_)} { static const FunctionInfo functions[] = { {0, &ILibraryAppletCreator::CreateLibraryApplet, "CreateLibraryApplet"}, {1, nullptr, "TerminateAllLibraryApplets"}, {2, nullptr, "AreAnyLibraryAppletsLeft"}, {10, &ILibraryAppletCreator::CreateStorage, "CreateStorage"}, {11, &ILibraryAppletCreator::CreateTransferMemoryStorage, "CreateTransferMemoryStorage"}, {12, &ILibraryAppletCreator::CreateHandleStorage, "CreateHandleStorage"}, }; RegisterHandlers(functions); } ILibraryAppletCreator::~ILibraryAppletCreator() = default; void ILibraryAppletCreator::CreateLibraryApplet(HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; const auto applet_id = rp.PopRaw(); const auto applet_mode = rp.PopRaw(); LOG_DEBUG(Service_AM, "called with applet_id={:08X}, applet_mode={:08X}", applet_id, applet_mode); std::shared_ptr library_applet; if (ShouldCreateGuestApplet(applet_id)) { library_applet = CreateGuestApplet(system, applet, applet_id, applet_mode); } if (!library_applet) { library_applet = CreateFrontendApplet(system, applet, applet_id, applet_mode); } if (!library_applet) { LOG_ERROR(Service_AM, "Applet doesn't exist! applet_id={}", applet_id); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(ResultUnknown); return; } // Applet is created, can now be launched. applet->library_applet_launchable_event.Signal(); IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(ResultSuccess); rb.PushIpcInterface(library_applet); } void ILibraryAppletCreator::CreateStorage(HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; const s64 size{rp.Pop()}; LOG_DEBUG(Service_AM, "called, size={}", size); if (size <= 0) { LOG_ERROR(Service_AM, "size is less than or equal to 0"); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(ResultUnknown); return; } std::vector data(size); IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(ResultSuccess); rb.PushIpcInterface(system, AM::CreateStorage(std::move(data))); } void ILibraryAppletCreator::CreateTransferMemoryStorage(HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; struct Parameters { bool is_writable; s64 size; }; const auto params{rp.PopRaw()}; const auto handle{ctx.GetCopyHandle(0)}; LOG_DEBUG(Service_AM, "called, is_writable={}, size={}, handle={:08X}", params.is_writable, params.size, handle); if (params.size <= 0) { LOG_ERROR(Service_AM, "size is less than or equal to 0"); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(ResultUnknown); return; } auto transfer_mem = ctx.GetObjectFromHandle(handle); if (transfer_mem.IsNull()) { LOG_ERROR(Service_AM, "transfer_mem is a nullptr for handle={:08X}", handle); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(ResultUnknown); return; } IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(ResultSuccess); rb.PushIpcInterface( system, AM::CreateTransferMemoryStorage(ctx.GetMemory(), transfer_mem.GetPointerUnsafe(), params.is_writable, params.size)); } void ILibraryAppletCreator::CreateHandleStorage(HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; const s64 size{rp.Pop()}; const auto handle{ctx.GetCopyHandle(0)}; LOG_DEBUG(Service_AM, "called, size={}, handle={:08X}", size, handle); if (size <= 0) { LOG_ERROR(Service_AM, "size is less than or equal to 0"); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(ResultUnknown); return; } auto transfer_mem = ctx.GetObjectFromHandle(handle); if (transfer_mem.IsNull()) { LOG_ERROR(Service_AM, "transfer_mem is a nullptr for handle={:08X}", handle); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(ResultUnknown); return; } IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(ResultSuccess); rb.PushIpcInterface( system, AM::CreateHandleStorage(ctx.GetMemory(), transfer_mem.GetPointerUnsafe(), size)); } } // namespace Service::AM