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.
This commit is contained in:
Subv 2018-10-05 12:03:27 -05:00
parent b163502744
commit 8ec2a9817c
7 changed files with 193 additions and 51 deletions

View file

@ -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::InitializeResult> AppletManager::Initialize(AppletId ap
}
slot_data->applet_id = static_cast<AppletId>(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::AppletInfo> 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<size_t>(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<size_t>(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<size_t>(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<size_t>(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<AppletSlot>(slot);

View file

@ -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<AppletManager> {
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<Kernel::Object> object, std::vector<u8> 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<AppletInfo> 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<AppletSlotData, NumAppletSlot> 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

View file

@ -394,6 +394,54 @@ void Module::Interface::CancelParameter(Kernel::HLERequestContext& ctx) {
static_cast<u32>(receiver_appid));
}
void Module::Interface::PrepareToDoApplicationJump(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp(ctx, 0x31, 4, 0); // 0x00310100
auto flags = rp.PopEnum<ApplicationJumpFlags>();
u64 title_id = rp.Pop<u64>();
u8 media_type = rp.Pop<u8>();
LOG_WARNING(Service_APT, "(STUBBED) called title_id={:016X}, media_type={:#01X}, flags={:#08X}",
title_id, media_type, static_cast<u8>(flags));
ResultCode result = apt->applet_manager->PrepareToDoApplicationJump(
title_id, static_cast<FS::MediaType>(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>();
u32 hmac_size = rp.Pop<u32>();
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<u64>(parameters.current_title_id);
rb.Push(static_cast<u8>(parameters.current_media_type));
rb.Push<u64>(parameters.next_title_id);
rb.Push(static_cast<u8>(parameters.next_media_type));
}
void Module::Interface::PrepareToStartApplication(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp(ctx, 0x15, 5, 0); // 0x00150140
u32 title_info1 = rp.Pop<u32>();
@ -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<u8>();
u32 program_id_low = rp.Pop<u32>();
u32 program_id_high = rp.Pop<u32>();
Service::FS::MediaType media_type = static_cast<Service::FS::MediaType>(rp.Pop<u8>());
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<u8>(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>();
u32 hmac_size = rp.Pop<u32>();
std::vector<u8> parameter = rp.PopStaticBuffer();
std::vector<u8> 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<bool>();
@ -844,7 +847,7 @@ Module::Interface::Interface(std::shared_ptr<Module> apt, const char* name, u32
Module::Interface::~Interface() = default;
Module::Module(Core::System& system) : system(system) {
applet_manager = std::make_shared<AppletManager>();
applet_manager = std::make_shared<AppletManager>(system);
using Kernel::MemoryPermission;
shared_font_mem =

View file

@ -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:

View file

@ -59,7 +59,7 @@ APT_A::APT_A(std::shared_ptr<Module> 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"},

View file

@ -59,7 +59,7 @@ APT_S::APT_S(std::shared_ptr<Module> 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"},

View file

@ -59,7 +59,7 @@ APT_U::APT_U(std::shared_ptr<Module> 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"},