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"},