From 1ab9b60a605dc27db5d754719b60d5b16ff698fa Mon Sep 17 00:00:00 2001 From: Steveice10 <1269164+Steveice10@users.noreply.github.com> Date: Sat, 11 Feb 2023 22:47:08 -0800 Subject: [PATCH] Services/APT: Implement PrepareToStartApplication, StartApplication, and WakeupApplication (#6280) * Services/APT: Implemented PrepareToStartApplication and StartApplication. This allows games to be launched from the Home Menu, however, there is still a bug with the GSP where the Home Menu doesn't release the GPU rights. It is unknown if the Home Menu should terminate itself after launching a new application. To get the Home Menu to not hang when launching stuff, you need to have config block 0xF0006 (size 40 flags 8) in your config savegame, it doesn't matter if it's filled with zeros. * Services/APT: Implement WakeupApplication. With this, the Home Menu is now able to launch games when using an LLE NIM imlementation. * Services/APT: Reset the app_start_parameters after launching the application with StartApplication. * Services/APT: Simplify the StartApplication code by directly calling WakeupApplication. --------- Co-authored-by: Subv --- src/core/hle/service/apt/applet_manager.cpp | 100 +++++++++++++++++++- src/core/hle/service/apt/applet_manager.h | 29 ++++++ src/core/hle/service/apt/apt.cpp | 47 ++++----- src/core/hle/service/apt/apt.h | 10 ++ src/core/hle/service/apt/apt_a.cpp | 4 +- src/core/hle/service/apt/apt_s.cpp | 4 +- src/core/hle/service/apt/apt_u.cpp | 4 +- 7 files changed, 167 insertions(+), 31 deletions(-) diff --git a/src/core/hle/service/apt/applet_manager.cpp b/src/core/hle/service/apt/applet_manager.cpp index 73d4e6d89c..9ba7e81107 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 830efa20f4..78b6ed32c5 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 ce094a8fe7..1983622ae8 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 851136ad09..78aa636a54 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 87e454f1c6..98bee76d48 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 732b55a234..9cb6f1b0f1 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 ec1dbe291b..da119a177e 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"},