Merge pull request #4306 from Subv/apt_jump

Services/APT: Better implementation of PrepareToDoApplicationJump and DoApplicationJump.
This commit is contained in:
Sebastian Valle 2018-10-08 15:44:38 -05:00 committed by GitHub
commit f405134913
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
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"},