mirror of
https://github.com/citra-emu/citra.git
synced 2024-12-18 14:30:03 +00:00
Merge pull request #4306 from Subv/apt_jump
Services/APT: Better implementation of PrepareToDoApplicationJump and DoApplicationJump.
This commit is contained in:
commit
f405134913
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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 =
|
||||
|
@ -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:
|
||||
|
@ -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"},
|
||||
|
@ -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"},
|
||||
|
@ -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"},
|
||||
|
Loading…
Reference in New Issue
Block a user