diff --git a/src/core/hle/service/apt/applet_manager.cpp b/src/core/hle/service/apt/applet_manager.cpp index 73d4e6d89..9ba7e8110 100644 --- a/src/core/hle/service/apt/applet_manager.cpp +++ b/src/core/hle/service/apt/applet_manager.cpp @@ -283,8 +283,15 @@ ResultVal AppletManager::Initialize(AppletId ap slot_data->title_id = system.Kernel().GetCurrentProcess()->codeset->program_id; slot_data->attributes.raw = attributes.raw; - if (slot_data->applet_id == AppletId::Application || - slot_data->applet_id == AppletId::HomeMenu) { + const auto* home_menu_slot = GetAppletSlotData(AppletId::HomeMenu); + + // Applications need to receive a Wakeup signal to actually start up, this signal is usually + // sent by the Home Menu after starting the app by way of APT::WakeupApplication. In some cases + // such as when starting a game directly or the Home Menu itself, we have to send the signal + // ourselves since the Home Menu is not running yet. We detect if the Home Menu is running by + // checking if there's an applet registered in the HomeMenu slot. + if (slot_data->applet_id == AppletId::HomeMenu || + (slot_data->applet_id == AppletId::Application && !home_menu_slot)) { // Initialize the APT parameter to wake up the application. next_parameter.emplace(); next_parameter->signal = SignalType::Wakeup; @@ -310,6 +317,12 @@ ResultCode AppletManager::Enable(AppletAttributes attributes) { slot_data->registered = true; + // Send any outstanding parameters to the newly-registered application + if (delayed_parameter && delayed_parameter->destination_id == slot_data->applet_id) { + CancelAndSendParameter(*delayed_parameter); + delayed_parameter.reset(); + } + return RESULT_SUCCESS; } @@ -580,6 +593,89 @@ ResultCode AppletManager::DoApplicationJump(DeliverArg arg) { */ } +ResultCode AppletManager::PrepareToStartApplication(u64 title_id, FS::MediaType media_type) { + // TODO(Subv): This should check that the current applet is of System type and return 0xc8a0cc04 + // if not. + + // TODO(Subv): This should return 0xc8a0cff0 if the applet preparation state is already set + + const auto& application_slot = applet_slots[static_cast(AppletSlot::Application)]; + + if (application_slot.registered) { + // TODO(Subv): Convert this to the enum constructor of ResultCode + return ResultCode(0xc8a0cffc); + } + + ASSERT_MSG(!app_start_parameters, + "Trying to prepare an application when another is already prepared"); + + app_start_parameters.emplace(); + app_start_parameters->next_title_id = title_id; + app_start_parameters->next_media_type = media_type; + + return RESULT_SUCCESS; +} + +ResultCode AppletManager::StartApplication(const std::vector& parameter, + const std::vector& hmac, bool paused) { + // The delivery argument is always unconditionally set. + deliver_arg.emplace(DeliverArg{parameter, hmac}); + + // Note: APT first checks if we can launch the application via AM::CheckDemoLaunchRights and + // returns 0xc8a12403 if we can't. We intentionally do not implement that check. + + // TODO(Subv): The APT service performs several checks here related to the exheader flags of the + // process we're launching and other things like title id blacklists. We do not yet implement + // any of that. + + // TODO(Subv): The real APT service doesn't seem to check whether the titleid to launch is set + // or not, it either launches NATIVE_FIRM if some internal state is set, or fails when calling + // PM::LaunchTitle. We should research more about that. + ASSERT_MSG(app_start_parameters, "Trying to start an application without preparing it first."); + + // Launch the title directly. + const auto process = + NS::LaunchTitle(app_start_parameters->next_media_type, app_start_parameters->next_title_id); + if (!process) { + LOG_CRITICAL(Service_APT, "Failed to launch title during application start, exiting."); + system.RequestShutdown(); + } + + app_start_parameters.reset(); + + if (!paused) { + return WakeupApplication(); + } + + return RESULT_SUCCESS; +} + +ResultCode AppletManager::WakeupApplication() { + // Send a Wakeup signal via the apt parameter to the application once it registers itself. + // The real APT service does this by spinwaiting on another thread until the application is + // registered. + MessageParameter wakeup_parameter{}; + wakeup_parameter.signal = SignalType::Wakeup; + wakeup_parameter.sender_id = AppletId::HomeMenu; + wakeup_parameter.destination_id = AppletId::Application; + SendApplicationParameterAfterRegistration(wakeup_parameter); + + return RESULT_SUCCESS; +} + +void AppletManager::SendApplicationParameterAfterRegistration(const MessageParameter& parameter) { + const auto* slot = GetAppletSlotData(AppletId::Application); + + // If the application is already registered, immediately send the parameter + if (slot && slot->registered) { + CancelAndSendParameter(parameter); + return; + } + + // Otherwise queue it until the Application calls APT::Enable + delayed_parameter = parameter; +} + void AppletManager::EnsureHomeMenuLoaded() { const auto& system_slot = applet_slots[static_cast(AppletSlot::SystemApplet)]; // TODO(Subv): The real APT service sends signal 12 (WakeupByCancel) to the currently running diff --git a/src/core/hle/service/apt/applet_manager.h b/src/core/hle/service/apt/applet_manager.h index 830efa20f..78b6ed32c 100644 --- a/src/core/hle/service/apt/applet_manager.h +++ b/src/core/hle/service/apt/applet_manager.h @@ -185,6 +185,11 @@ public: deliver_arg = std::move(arg); } + ResultCode PrepareToStartApplication(u64 title_id, FS::MediaType media_type); + ResultCode StartApplication(const std::vector& parameter, const std::vector& hmac, + bool paused); + ResultCode WakeupApplication(); + struct AppletInfo { u64 title_id; Service::FS::MediaType media_type; @@ -221,11 +226,28 @@ public: return app_jump_parameters; } + struct ApplicationStartParameters { + u64 next_title_id; + FS::MediaType next_media_type; + + private: + template + void serialize(Archive& ar, const unsigned int) { + ar& next_title_id; + ar& next_media_type; + } + friend class boost::serialization::access; + }; + private: /// Parameter data to be returned in the next call to Glance/ReceiveParameter. // NOTE: A bug in gcc prevents serializing std::optional boost::optional next_parameter; + /// This parameter will be sent to the application/applet once they register themselves by using + /// APT::Initialize. + boost::optional delayed_parameter; + static constexpr std::size_t NumAppletSlot = 4; enum class AppletSlot : u8 { @@ -271,6 +293,7 @@ private: }; ApplicationJumpParameters app_jump_parameters{}; + boost::optional app_start_parameters{}; boost::optional deliver_arg{}; // Holds data about the concurrently running applets in the system. @@ -280,6 +303,10 @@ private: AppletSlotData* GetAppletSlotData(AppletId id); AppletSlotData* GetAppletSlotData(AppletAttributes attributes); + /// Checks if the Application slot has already been registered and sends the parameter to it, + /// otherwise it queues for sending when the application registers itself with APT::Enable. + void SendApplicationParameterAfterRegistration(const MessageParameter& parameter); + void EnsureHomeMenuLoaded(); // Command that will be sent to the application when a library applet calls CloseLibraryApplet. @@ -293,6 +320,8 @@ private: ar& next_parameter; ar& app_jump_parameters; if (file_version > 0) { + ar& delayed_parameter; + ar& app_start_parameters; ar& deliver_arg; } ar& applet_slots; diff --git a/src/core/hle/service/apt/apt.cpp b/src/core/hle/service/apt/apt.cpp index ce094a8fe..1983622ae 100644 --- a/src/core/hle/service/apt/apt.cpp +++ b/src/core/hle/service/apt/apt.cpp @@ -540,39 +540,40 @@ void Module::APTInterface::ReceiveDeliverArg(Kernel::HLERequestContext& ctx) { void Module::APTInterface::PrepareToStartApplication(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp(ctx, 0x15, 5, 0); // 0x00150140 - u32 title_info1 = rp.Pop(); - u32 title_info2 = rp.Pop(); - u32 title_info3 = rp.Pop(); - u32 title_info4 = rp.Pop(); - u32 flags = rp.Pop(); - - if (flags & 0x00000100) { - apt->unknown_ns_state_field = 1; - } + const u64 title_id = rp.Pop(); + const auto media_type = rp.PopEnum(); + rp.Skip(1, false); // Padding + const u32 flags = rp.Pop(); IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); - rb.Push(RESULT_SUCCESS); // No error + rb.Push(apt->applet_manager->PrepareToStartApplication(title_id, media_type)); - LOG_WARNING(Service_APT, - "(STUBBED) called title_info1={:#010X}, title_info2={:#010X}, title_info3={:#010X}," - "title_info4={:#010X}, flags={:#010X}", - title_info1, title_info2, title_info3, title_info4, flags); + LOG_INFO(Service_APT, "called title_id={:#010X} media_type={} flags={:#010X}", title_id, + media_type, flags); } void Module::APTInterface::StartApplication(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp(ctx, 0x1B, 3, 4); // 0x001B00C4 - const auto buffer1_size = rp.Pop(); - const auto buffer2_size = rp.Pop(); - const auto flag = rp.Pop(); - [[maybe_unused]] const std::vector buffer1 = rp.PopStaticBuffer(); - [[maybe_unused]] const std::vector buffer2 = rp.PopStaticBuffer(); + const u32 parameter_size = rp.Pop(); + const u32 hmac_size = rp.Pop(); + const bool paused = rp.Pop(); + const std::vector parameter = rp.PopStaticBuffer(); + const std::vector hmac = rp.PopStaticBuffer(); IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); - rb.Push(RESULT_SUCCESS); // No error + rb.Push(apt->applet_manager->StartApplication(parameter, hmac, paused)); - LOG_WARNING(Service_APT, - "(STUBBED) called buffer1_size={:#010X}, buffer2_size={:#010X}, flag={:#010X}", - buffer1_size, buffer2_size, flag); + LOG_INFO(Service_APT, "called parameter_size={:#010X}, hmac_size={:#010X}, paused={}", + parameter_size, hmac_size, paused); +} + +void Module::APTInterface::WakeupApplication(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp(ctx, 0x1C, 0, 0); // 0x001C0000 + + IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); + rb.Push(apt->applet_manager->WakeupApplication()); + + LOG_DEBUG(Service_APT, "called"); } void Module::APTInterface::AppletUtility(Kernel::HLERequestContext& ctx) { diff --git a/src/core/hle/service/apt/apt.h b/src/core/hle/service/apt/apt.h index 851136ad0..78aa636a5 100644 --- a/src/core/hle/service/apt/apt.h +++ b/src/core/hle/service/apt/apt.h @@ -361,6 +361,16 @@ public: */ void StartApplication(Kernel::HLERequestContext& ctx); + /** + * APT::WakeupApplication service function. + * Inputs: + * 0 : Command header [0x001C0000] + * Outputs: + * 0 : Return Header + * 1 : Result of function, 0 on success, otherwise error code + */ + void WakeupApplication(Kernel::HLERequestContext& ctx); + /** * APT::AppletUtility service function * Inputs: diff --git a/src/core/hle/service/apt/apt_a.cpp b/src/core/hle/service/apt/apt_a.cpp index 87e454f1c..98bee76d4 100644 --- a/src/core/hle/service/apt/apt_a.cpp +++ b/src/core/hle/service/apt/apt_a.cpp @@ -36,8 +36,8 @@ APT_A::APT_A(std::shared_ptr apt) {0x00180040, &APT_A::PrepareToStartLibraryApplet, "PrepareToStartLibraryApplet"}, {0x00190040, nullptr, "PrepareToStartSystemApplet"}, {0x001A0000, nullptr, "PrepareToStartNewestHomeMenu"}, - {0x001B00C4, nullptr, "StartApplication"}, - {0x001C0000, nullptr, "WakeupApplication"}, + {0x001B00C4, &APT_A::StartApplication, "StartApplication"}, + {0x001C0000, &APT_A::WakeupApplication, "WakeupApplication"}, {0x001D0000, nullptr, "CancelApplication"}, {0x001E0084, &APT_A::StartLibraryApplet, "StartLibraryApplet"}, {0x001F0084, nullptr, "StartSystemApplet"}, diff --git a/src/core/hle/service/apt/apt_s.cpp b/src/core/hle/service/apt/apt_s.cpp index 732b55a23..9cb6f1b0f 100644 --- a/src/core/hle/service/apt/apt_s.cpp +++ b/src/core/hle/service/apt/apt_s.cpp @@ -36,8 +36,8 @@ APT_S::APT_S(std::shared_ptr apt) {0x00180040, &APT_S::PrepareToStartLibraryApplet, "PrepareToStartLibraryApplet"}, {0x00190040, nullptr, "PrepareToStartSystemApplet"}, {0x001A0000, &APT_S::PrepareToStartNewestHomeMenu, "PrepareToStartNewestHomeMenu"}, - {0x001B00C4, nullptr, "StartApplication"}, - {0x001C0000, nullptr, "WakeupApplication"}, + {0x001B00C4, &APT_S::StartApplication, "StartApplication"}, + {0x001C0000, &APT_S::WakeupApplication, "WakeupApplication"}, {0x001D0000, nullptr, "CancelApplication"}, {0x001E0084, &APT_S::StartLibraryApplet, "StartLibraryApplet"}, {0x001F0084, nullptr, "StartSystemApplet"}, diff --git a/src/core/hle/service/apt/apt_u.cpp b/src/core/hle/service/apt/apt_u.cpp index ec1dbe291..da119a177 100644 --- a/src/core/hle/service/apt/apt_u.cpp +++ b/src/core/hle/service/apt/apt_u.cpp @@ -36,8 +36,8 @@ APT_U::APT_U(std::shared_ptr apt) {0x00180040, &APT_U::PrepareToStartLibraryApplet, "PrepareToStartLibraryApplet"}, {0x00190040, nullptr, "PrepareToStartSystemApplet"}, {0x001A0000, nullptr, "PrepareToStartNewestHomeMenu"}, - {0x001B00C4, nullptr, "StartApplication"}, - {0x001C0000, nullptr, "WakeupApplication"}, + {0x001B00C4, &APT_U::StartApplication, "StartApplication"}, + {0x001C0000, &APT_U::WakeupApplication, "WakeupApplication"}, {0x001D0000, nullptr, "CancelApplication"}, {0x001E0084, &APT_U::StartLibraryApplet, "StartLibraryApplet"}, {0x001F0084, nullptr, "StartSystemApplet"},