From 8ec2a9817c344131f7fea807e4c0c127fd4be2bf Mon Sep 17 00:00:00 2001 From: Subv Date: Fri, 5 Oct 2018 12:03:27 -0500 Subject: [PATCH] Services/APT: Better implementation of PrepareToDoApplicationJump and DoApplicationJump. The real console can't launch an Application directly from within another Application so it has to go through the Home Menu. We do not have such limitation and can directly launch the requested title. --- src/core/hle/service/apt/applet_manager.cpp | 93 +++++++++++++++++++- src/core/hle/service/apt/applet_manager.h | 36 +++++++- src/core/hle/service/apt/apt.cpp | 95 +++++++++++---------- src/core/hle/service/apt/apt.h | 14 +++ src/core/hle/service/apt/apt_a.cpp | 2 +- src/core/hle/service/apt/apt_s.cpp | 2 +- src/core/hle/service/apt/apt_u.cpp | 2 +- 7 files changed, 193 insertions(+), 51 deletions(-) diff --git a/src/core/hle/service/apt/applet_manager.cpp b/src/core/hle/service/apt/applet_manager.cpp index 708adc27f..c4f622669 100644 --- a/src/core/hle/service/apt/applet_manager.cpp +++ b/src/core/hle/service/apt/applet_manager.cpp @@ -3,6 +3,7 @@ // Refer to the license.txt file included. #include "common/common_paths.h" +#include "core/core.h" #include "core/hle/applets/applet.h" #include "core/hle/service/apt/applet_manager.h" #include "core/hle/service/apt/errors.h" @@ -255,6 +256,9 @@ ResultVal AppletManager::Initialize(AppletId ap } slot_data->applet_id = static_cast(app_id); + // Note: In the real console the title id of a given applet slot is set by the APT module when + // calling StartApplication. + slot_data->title_id = Kernel::g_current_process->codeset->program_id; slot_data->attributes.raw = attributes.raw; if (slot_data->applet_id == AppletId::Application || @@ -465,7 +469,94 @@ ResultVal AppletManager::GetAppletInfo(AppletId app_i slot->registered, slot->loaded, slot->attributes.raw}); } -AppletManager::AppletManager() { +ResultCode AppletManager::PrepareToDoApplicationJump(u64 title_id, FS::MediaType media_type, + ApplicationJumpFlags flags) { + // A running application can not launch another application directly because the applet state + // for the Application slot is already in use. The way this is implemented in hardware is to + // launch the Home Menu and tell it to launch our desired application. + + // Save the title data to send it to the Home Menu when DoApplicationJump is called. + const auto& application_slot = applet_slots[static_cast(AppletSlot::Application)]; + + ASSERT_MSG(flags != ApplicationJumpFlags::UseStoredParameters, + "Unimplemented application jump flags 1"); + + if (flags == ApplicationJumpFlags::UseCurrentParameters) { + title_id = application_slot.title_id; + } + + app_jump_parameters.current_title_id = application_slot.title_id; + // TODO(Subv): Retrieve the correct media type of the currently-running application. For now + // just assume NAND. + app_jump_parameters.current_media_type = FS::MediaType::NAND; + app_jump_parameters.next_title_id = title_id; + app_jump_parameters.next_media_type = media_type; + + // Note: The real console uses the Home Menu to perform the application jump, therefore the menu + // needs to be running. The real APT module starts the Home Menu here if it's not already + // running, we don't have to do this. See `EnsureHomeMenuLoaded` for launching the Home Menu. + return RESULT_SUCCESS; +} + +ResultCode AppletManager::DoApplicationJump() { + // Note: The real console uses the Home Menu to perform the application jump, it goes + // OldApplication->Home Menu->NewApplication. We do not need to use the Home Menu to do this so + // we launch the new application directly. In the real APT service, the Home Menu must be + // running to do this, otherwise error 0xC8A0CFF0 is returned. + + auto& application_slot = applet_slots[static_cast(AppletSlot::Application)]; + application_slot.Reset(); + + // TODO(Subv): Set the delivery parameters. + + // TODO(Subv): Terminate the current Application. + + // Note: The real console sends signal 17 (WakeupToLaunchApplication) to the Home Menu, this + // prompts it to call GetProgramIdOnApplicationJump and + // PrepareToStartApplication/StartApplication on the title to launch. + + if (app_jump_parameters.next_title_id == app_jump_parameters.current_title_id) { + // Perform a soft-reset if we're trying to relaunch the same title. + // TODO(Subv): Note that this reboots the entire emulated system, a better way would be to + // simply re-launch the title without closing all services, but this would only work for + // installed titles since we have no way of getting the file path of an arbitrary game dump + // based only on the title id. + system.RequestReset(); + return RESULT_SUCCESS; + } + + // Launch the title directly. + auto process = + NS::LaunchTitle(app_jump_parameters.next_media_type, app_jump_parameters.next_title_id); + if (!process) { + LOG_CRITICAL(Service_APT, "Failed to launch title during application jump, exiting."); + system.RequestShutdown(); + } + return RESULT_SUCCESS; +} + +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 + // System applet, waits for it to finish, and then launches the Home Menu. + ASSERT_MSG(!system_slot.registered, "A system applet is already running"); + + const auto& menu_slot = applet_slots[static_cast(AppletSlot::HomeMenu)]; + + if (menu_slot.registered) { + // The Home Menu is already running. + return; + } + + u64 menu_title_id = GetTitleIdForApplet(AppletId::HomeMenu); + auto process = NS::LaunchTitle(FS::MediaType::NAND, menu_title_id); + if (!process) { + LOG_WARNING(Service_APT, + "The Home Menu failed to launch, application jumping will not work."); + } +} + +AppletManager::AppletManager(Core::System& system) : system(system) { for (std::size_t slot = 0; slot < applet_slots.size(); ++slot) { auto& slot_data = applet_slots[slot]; slot_data.slot = static_cast(slot); diff --git a/src/core/hle/service/apt/applet_manager.h b/src/core/hle/service/apt/applet_manager.h index 32a39c213..b2748439e 100644 --- a/src/core/hle/service/apt/applet_manager.h +++ b/src/core/hle/service/apt/applet_manager.h @@ -11,6 +11,10 @@ #include "core/hle/result.h" #include "core/hle/service/fs/archive.h" +namespace Core { +class System; +} + namespace Service::APT { /// Signals used by APT functions @@ -97,9 +101,15 @@ union AppletAttributes { AppletAttributes(u32 attributes) : raw(attributes) {} }; +enum class ApplicationJumpFlags : u8 { + UseInputParameters = 0, + UseStoredParameters = 1, + UseCurrentParameters = 2 +}; + class AppletManager : public std::enable_shared_from_this { public: - AppletManager(); + explicit AppletManager(Core::System& system); ~AppletManager(); /** @@ -130,6 +140,10 @@ public: ResultCode PrepareToCloseLibraryApplet(bool not_pause, bool exiting, bool jump_home); ResultCode CloseLibraryApplet(Kernel::SharedPtr object, std::vector buffer); + ResultCode PrepareToDoApplicationJump(u64 title_id, FS::MediaType media_type, + ApplicationJumpFlags flags); + ResultCode DoApplicationJump(); + struct AppletInfo { u64 title_id; Service::FS::MediaType media_type; @@ -140,6 +154,18 @@ public: ResultVal GetAppletInfo(AppletId app_id); + struct ApplicationJumpParameters { + u64 next_title_id; + FS::MediaType next_media_type; + + u64 current_title_id; + FS::MediaType current_media_type; + }; + + ApplicationJumpParameters GetApplicationJumpParameters() const { + return app_jump_parameters; + } + private: /// Parameter data to be returned in the next call to Glance/ReceiveParameter. /// TODO(Subv): Use std::optional once we migrate to C++17. @@ -160,6 +186,7 @@ private: struct AppletSlotData { AppletId applet_id; AppletSlot slot; + u64 title_id; bool registered; bool loaded; AppletAttributes attributes; @@ -169,10 +196,13 @@ private: void Reset() { applet_id = AppletId::None; registered = false; + title_id = 0; attributes.raw = 0; } }; + ApplicationJumpParameters app_jump_parameters{}; + // Holds data about the concurrently running applets in the system. std::array applet_slots = {}; @@ -180,8 +210,12 @@ private: AppletSlotData* GetAppletSlotData(AppletId id); AppletSlotData* GetAppletSlotData(AppletAttributes attributes); + void EnsureHomeMenuLoaded(); + // Command that will be sent to the application when a library applet calls CloseLibraryApplet. SignalType library_applet_closing_command; + + Core::System& system; }; } // namespace Service::APT diff --git a/src/core/hle/service/apt/apt.cpp b/src/core/hle/service/apt/apt.cpp index a642cefb9..bd0621fc4 100644 --- a/src/core/hle/service/apt/apt.cpp +++ b/src/core/hle/service/apt/apt.cpp @@ -394,6 +394,54 @@ void Module::Interface::CancelParameter(Kernel::HLERequestContext& ctx) { static_cast(receiver_appid)); } +void Module::Interface::PrepareToDoApplicationJump(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp(ctx, 0x31, 4, 0); // 0x00310100 + auto flags = rp.PopEnum(); + u64 title_id = rp.Pop(); + u8 media_type = rp.Pop(); + + LOG_WARNING(Service_APT, "(STUBBED) called title_id={:016X}, media_type={:#01X}, flags={:#08X}", + title_id, media_type, static_cast(flags)); + + ResultCode result = apt->applet_manager->PrepareToDoApplicationJump( + title_id, static_cast(media_type), flags); + + IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); + rb.Push(result); +} + +void Module::Interface::DoApplicationJump(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp(ctx, 0x32, 2, 4); // 0x00320084 + u32 param_size = rp.Pop(); + u32 hmac_size = rp.Pop(); + + auto param = rp.PopStaticBuffer(); + auto hmac = rp.PopStaticBuffer(); + + LOG_WARNING(Service_APT, "(STUBBED) called param_size={:08X}, hmac_size={:08X}", param_size, + hmac_size); + + // TODO(Subv): Set the delivery parameters before starting the new application. + + IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); + rb.Push(apt->applet_manager->DoApplicationJump()); +} + +void Module::Interface::GetProgramIdOnApplicationJump(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp(ctx, 0x33, 0, 0); // 0x00330000 + + LOG_DEBUG(Service_APT, "called"); + + auto parameters = apt->applet_manager->GetApplicationJumpParameters(); + + IPC::RequestBuilder rb = rp.MakeBuilder(7, 0); + rb.Push(RESULT_SUCCESS); + rb.Push(parameters.current_title_id); + rb.Push(static_cast(parameters.current_media_type)); + rb.Push(parameters.next_title_id); + rb.Push(static_cast(parameters.next_media_type)); +} + void Module::Interface::PrepareToStartApplication(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp(ctx, 0x15, 5, 0); // 0x00150140 u32 title_info1 = rp.Pop(); @@ -550,51 +598,6 @@ void Module::Interface::CloseApplication(Kernel::HLERequestContext& ctx) { rb.Push(RESULT_SUCCESS); } -void Module::Interface::PrepareToDoApplicationJump(Kernel::HLERequestContext& ctx) { - IPC::RequestParser rp(ctx, 0x31, 4, 0); - u32 flags = rp.Pop(); - u32 program_id_low = rp.Pop(); - u32 program_id_high = rp.Pop(); - Service::FS::MediaType media_type = static_cast(rp.Pop()); - - LOG_WARNING(Service_APT, - "(STUBBED) called, flags={:08X}, program_id_low={:08X}, program_id_high={:08X}, " - "media_type={:08X}", - flags, program_id_low, program_id_high, static_cast(media_type)); - - if (flags == 0x2) { - // It seems that flags 0x2 means jumping to the same application, - // and ignore the parameters. This is used in Pokemon main series - // to soft reset. - application_reset_prepared = true; - } - - IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); - rb.Push(RESULT_SUCCESS); -} - -void Module::Interface::DoApplicationJump(Kernel::HLERequestContext& ctx) { - IPC::RequestParser rp(ctx, 0x32, 2, 4); - u32 parameter_size = rp.Pop(); - u32 hmac_size = rp.Pop(); - std::vector parameter = rp.PopStaticBuffer(); - std::vector hmac = rp.PopStaticBuffer(); - - LOG_WARNING(Service_APT, "(STUBBED) called"); - - if (application_reset_prepared) { - // Reset system - apt->system.RequestReset(); - } else { - // After the jump, the application should shutdown - // TODO: Actually implement the jump - apt->system.RequestShutdown(); - } - - IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); - rb.Push(RESULT_SUCCESS); -} - void Module::Interface::CancelLibraryApplet(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp(ctx, 0x3B, 1, 0); // 0x003B0040 bool exiting = rp.Pop(); @@ -844,7 +847,7 @@ Module::Interface::Interface(std::shared_ptr apt, const char* name, u32 Module::Interface::~Interface() = default; Module::Module(Core::System& system) : system(system) { - applet_manager = std::make_shared(); + applet_manager = std::make_shared(system); using Kernel::MemoryPermission; shared_font_mem = diff --git a/src/core/hle/service/apt/apt.h b/src/core/hle/service/apt/apt.h index a54025ad3..23b299e8d 100644 --- a/src/core/hle/service/apt/apt.h +++ b/src/core/hle/service/apt/apt.h @@ -455,6 +455,20 @@ public: */ void DoApplicationJump(Kernel::HLERequestContext& ctx); + /** + * APT::GetProgramIdOnApplicationJump service function + * Inputs: + * 0 : Command header [0x00330000] + * Outputs: + * 0 : Return header + * 1 : Result of function, 0 on success, otherwise error code + * 2-3 : Current Application title id + * 4 : Current Application media type + * 5-6 : Next Application title id to jump to + * 7 : Next Application media type + */ + void GetProgramIdOnApplicationJump(Kernel::HLERequestContext& ctx); + /** * APT::CancelLibraryApplet service function * Inputs: diff --git a/src/core/hle/service/apt/apt_a.cpp b/src/core/hle/service/apt/apt_a.cpp index 9558b50bf..7eb1f0f99 100644 --- a/src/core/hle/service/apt/apt_a.cpp +++ b/src/core/hle/service/apt/apt_a.cpp @@ -59,7 +59,7 @@ APT_A::APT_A(std::shared_ptr apt) {0x00300044, nullptr, "LeaveResidentApplet"}, {0x00310100, &APT_A::PrepareToDoApplicationJump, "PrepareToDoApplicationJump"}, {0x00320084, &APT_A::DoApplicationJump, "DoApplicationJump"}, - {0x00330000, nullptr, "GetProgramIdOnApplicationJump"}, + {0x00330000, &APT_A::GetProgramIdOnApplicationJump, "GetProgramIdOnApplicationJump"}, {0x00340084, nullptr, "SendDeliverArg"}, {0x00350080, nullptr, "ReceiveDeliverArg"}, {0x00360040, nullptr, "LoadSysMenuArg"}, diff --git a/src/core/hle/service/apt/apt_s.cpp b/src/core/hle/service/apt/apt_s.cpp index 820f50a65..3ffc480af 100644 --- a/src/core/hle/service/apt/apt_s.cpp +++ b/src/core/hle/service/apt/apt_s.cpp @@ -59,7 +59,7 @@ APT_S::APT_S(std::shared_ptr apt) {0x00300044, nullptr, "LeaveResidentApplet"}, {0x00310100, &APT_S::PrepareToDoApplicationJump, "PrepareToDoApplicationJump"}, {0x00320084, &APT_S::DoApplicationJump, "DoApplicationJump"}, - {0x00330000, nullptr, "GetProgramIdOnApplicationJump"}, + {0x00330000, &APT_S::GetProgramIdOnApplicationJump, "GetProgramIdOnApplicationJump"}, {0x00340084, nullptr, "SendDeliverArg"}, {0x00350080, nullptr, "ReceiveDeliverArg"}, {0x00360040, nullptr, "LoadSysMenuArg"}, diff --git a/src/core/hle/service/apt/apt_u.cpp b/src/core/hle/service/apt/apt_u.cpp index c39042c00..725bae3cb 100644 --- a/src/core/hle/service/apt/apt_u.cpp +++ b/src/core/hle/service/apt/apt_u.cpp @@ -59,7 +59,7 @@ APT_U::APT_U(std::shared_ptr apt) {0x00300044, nullptr, "LeaveResidentApplet"}, {0x00310100, &APT_U::PrepareToDoApplicationJump, "PrepareToDoApplicationJump"}, {0x00320084, &APT_U::DoApplicationJump, "DoApplicationJump"}, - {0x00330000, nullptr, "GetProgramIdOnApplicationJump"}, + {0x00330000, &APT_U::GetProgramIdOnApplicationJump, "GetProgramIdOnApplicationJump"}, {0x00340084, nullptr, "SendDeliverArg"}, {0x00350080, nullptr, "ReceiveDeliverArg"}, {0x00360040, nullptr, "LoadSysMenuArg"},