service/apt: Implement Deliver Args (#5611)
* service/apt: Add GetModule and GetAppletManager These will be used to retrieve and set deliver args across system resets (which are currently implemented as complete restarts) * applet_manager: Implement DeliverArg `flags` was added to `ApplicationJumpParameters` as flags 0x2 is handled differently from 0x0. * service/apt: Add ReceiveDeliverArg, implement GetStartupArgument Some based on guesses. * Address review comments
This commit is contained in:
parent
182ffa4243
commit
21fb9d63f4
7 changed files with 177 additions and 20 deletions
|
@ -31,6 +31,8 @@
|
|||
#include "core/hle/kernel/kernel.h"
|
||||
#include "core/hle/kernel/process.h"
|
||||
#include "core/hle/kernel/thread.h"
|
||||
#include "core/hle/service/apt/applet_manager.h"
|
||||
#include "core/hle/service/apt/apt.h"
|
||||
#include "core/hle/service/fs/archive.h"
|
||||
#include "core/hle/service/gsp/gsp.h"
|
||||
#include "core/hle/service/pm/pm_app.h"
|
||||
|
@ -561,9 +563,20 @@ void System::Reset() {
|
|||
// reloading.
|
||||
// TODO: Properly implement the reset
|
||||
|
||||
// Since the system is completely reinitialized, we'll have to store the deliver arg manually.
|
||||
boost::optional<Service::APT::AppletManager::DeliverArg> deliver_arg;
|
||||
if (auto apt = Service::APT::GetModule(*this)) {
|
||||
deliver_arg = apt->GetAppletManager()->ReceiveDeliverArg();
|
||||
}
|
||||
|
||||
Shutdown();
|
||||
// Reload the system with the same setting
|
||||
Load(*m_emu_window, m_filepath);
|
||||
|
||||
// Restore the deliver arg.
|
||||
if (auto apt = Service::APT::GetModule(*this)) {
|
||||
apt->GetAppletManager()->SetDeliverArg(std::move(deliver_arg));
|
||||
}
|
||||
}
|
||||
|
||||
template <class Archive>
|
||||
|
|
|
@ -498,6 +498,7 @@ ResultCode AppletManager::PrepareToDoApplicationJump(u64 title_id, FS::MediaType
|
|||
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;
|
||||
app_jump_parameters.flags = flags;
|
||||
|
||||
// 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
|
||||
|
@ -505,16 +506,23 @@ ResultCode AppletManager::PrepareToDoApplicationJump(u64 title_id, FS::MediaType
|
|||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
ResultCode AppletManager::DoApplicationJump() {
|
||||
ResultCode AppletManager::DoApplicationJump(DeliverArg arg) {
|
||||
// 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)];
|
||||
|
||||
if (app_jump_parameters.flags != ApplicationJumpFlags::UseCurrentParameters) {
|
||||
// The source program ID is not updated when using flags 0x2.
|
||||
arg.source_program_id = application_slot.title_id;
|
||||
}
|
||||
|
||||
application_slot.Reset();
|
||||
|
||||
// TODO(Subv): Set the delivery parameters.
|
||||
// Set the delivery parameters.
|
||||
deliver_arg = std::move(arg);
|
||||
|
||||
// TODO(Subv): Terminate the current Application.
|
||||
|
||||
|
|
|
@ -5,7 +5,9 @@
|
|||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <limits>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <vector>
|
||||
#include <boost/serialization/array.hpp>
|
||||
#include <boost/serialization/optional.hpp>
|
||||
|
@ -158,7 +160,30 @@ public:
|
|||
|
||||
ResultCode PrepareToDoApplicationJump(u64 title_id, FS::MediaType media_type,
|
||||
ApplicationJumpFlags flags);
|
||||
ResultCode DoApplicationJump();
|
||||
|
||||
struct DeliverArg {
|
||||
std::vector<u8> param;
|
||||
std::vector<u8> hmac;
|
||||
u64 source_program_id = std::numeric_limits<u64>::max();
|
||||
|
||||
private:
|
||||
template <class Archive>
|
||||
void serialize(Archive& ar, const unsigned int) {
|
||||
ar& param;
|
||||
ar& hmac;
|
||||
ar& source_program_id;
|
||||
}
|
||||
friend class boost::serialization::access;
|
||||
};
|
||||
|
||||
ResultCode DoApplicationJump(DeliverArg arg);
|
||||
|
||||
boost::optional<DeliverArg> ReceiveDeliverArg() const {
|
||||
return deliver_arg;
|
||||
}
|
||||
void SetDeliverArg(boost::optional<DeliverArg> arg) {
|
||||
deliver_arg = std::move(arg);
|
||||
}
|
||||
|
||||
struct AppletInfo {
|
||||
u64 title_id;
|
||||
|
@ -173,15 +198,19 @@ public:
|
|||
struct ApplicationJumpParameters {
|
||||
u64 next_title_id;
|
||||
FS::MediaType next_media_type;
|
||||
ApplicationJumpFlags flags;
|
||||
|
||||
u64 current_title_id;
|
||||
FS::MediaType current_media_type;
|
||||
|
||||
private:
|
||||
template <class Archive>
|
||||
void serialize(Archive& ar, const unsigned int) {
|
||||
void serialize(Archive& ar, const unsigned int file_version) {
|
||||
ar& next_title_id;
|
||||
ar& next_media_type;
|
||||
if (file_version > 0) {
|
||||
ar& flags;
|
||||
}
|
||||
ar& current_title_id;
|
||||
ar& current_media_type;
|
||||
}
|
||||
|
@ -242,6 +271,7 @@ private:
|
|||
};
|
||||
|
||||
ApplicationJumpParameters app_jump_parameters{};
|
||||
boost::optional<DeliverArg> deliver_arg{};
|
||||
|
||||
// Holds data about the concurrently running applets in the system.
|
||||
std::array<AppletSlotData, NumAppletSlot> applet_slots = {};
|
||||
|
@ -259,9 +289,12 @@ private:
|
|||
|
||||
private:
|
||||
template <class Archive>
|
||||
void serialize(Archive& ar, const unsigned int) {
|
||||
void serialize(Archive& ar, const unsigned int file_version) {
|
||||
ar& next_parameter;
|
||||
ar& app_jump_parameters;
|
||||
if (file_version > 0) {
|
||||
ar& deliver_arg;
|
||||
}
|
||||
ar& applet_slots;
|
||||
ar& library_applet_closing_command;
|
||||
}
|
||||
|
@ -270,4 +303,7 @@ private:
|
|||
|
||||
} // namespace Service::APT
|
||||
|
||||
BOOST_CLASS_VERSION(Service::APT::AppletManager::ApplicationJumpParameters, 1)
|
||||
BOOST_CLASS_VERSION(Service::APT::AppletManager, 1)
|
||||
|
||||
SERVICE_CONSTRUCT(Service::APT::AppletManager)
|
||||
|
|
|
@ -56,6 +56,10 @@ Module::NSInterface::NSInterface(std::shared_ptr<Module> apt, const char* name,
|
|||
|
||||
Module::NSInterface::~NSInterface() = default;
|
||||
|
||||
std::shared_ptr<Module> Module::NSInterface::GetModule() const {
|
||||
return apt;
|
||||
}
|
||||
|
||||
void Module::NSInterface::SetWirelessRebootInfo(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp(ctx, 0x06, 1, 2); // 0x00060042
|
||||
u32 size = rp.Pop<u32>();
|
||||
|
@ -476,19 +480,32 @@ void Module::APTInterface::PrepareToDoApplicationJump(Kernel::HLERequestContext&
|
|||
|
||||
void Module::APTInterface::DoApplicationJump(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp(ctx, 0x32, 2, 4); // 0x00320084
|
||||
const auto param_size = rp.Pop<u32>();
|
||||
const auto hmac_size = rp.Pop<u32>();
|
||||
auto param_size = rp.Pop<u32>();
|
||||
auto hmac_size = rp.Pop<u32>();
|
||||
|
||||
[[maybe_unused]] const auto param = rp.PopStaticBuffer();
|
||||
[[maybe_unused]] const auto hmac = rp.PopStaticBuffer();
|
||||
constexpr u32 max_param_size{0x300};
|
||||
constexpr u32 max_hmac_size{0x20};
|
||||
if (param_size > max_param_size) {
|
||||
LOG_ERROR(Service_APT,
|
||||
"Param size is outside the valid range (capped to {:#010X}): param_size={:#010X}",
|
||||
max_param_size, param_size);
|
||||
param_size = max_param_size;
|
||||
}
|
||||
if (hmac_size > max_hmac_size) {
|
||||
LOG_ERROR(Service_APT,
|
||||
"HMAC size is outside the valid range (capped to {:#010X}): hmac_size={:#010X}",
|
||||
max_hmac_size, hmac_size);
|
||||
hmac_size = max_hmac_size;
|
||||
}
|
||||
|
||||
LOG_WARNING(Service_APT, "(STUBBED) called param_size={:08X}, hmac_size={:08X}", param_size,
|
||||
hmac_size);
|
||||
auto param = rp.PopStaticBuffer();
|
||||
auto hmac = rp.PopStaticBuffer();
|
||||
|
||||
// TODO(Subv): Set the delivery parameters before starting the new application.
|
||||
LOG_INFO(Service_APT, "called param_size={:08X}, hmac_size={:08X}", param_size, hmac_size);
|
||||
|
||||
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
|
||||
rb.Push(apt->applet_manager->DoApplicationJump());
|
||||
rb.Push(apt->applet_manager->DoApplicationJump(
|
||||
AppletManager::DeliverArg{std::move(param), std::move(hmac)}));
|
||||
}
|
||||
|
||||
void Module::APTInterface::GetProgramIdOnApplicationJump(Kernel::HLERequestContext& ctx) {
|
||||
|
@ -506,6 +523,25 @@ void Module::APTInterface::GetProgramIdOnApplicationJump(Kernel::HLERequestConte
|
|||
rb.Push(static_cast<u8>(parameters.next_media_type));
|
||||
}
|
||||
|
||||
void Module::APTInterface::ReceiveDeliverArg(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp(ctx, 0x35, 2, 4); // 0x00350080
|
||||
const auto param_size = rp.Pop<u32>();
|
||||
const auto hmac_size = rp.Pop<u32>();
|
||||
|
||||
LOG_DEBUG(Service_APT, "called param_size={:08X}, hmac_size={:08X}", param_size, hmac_size);
|
||||
|
||||
auto arg = apt->applet_manager->ReceiveDeliverArg().value_or(AppletManager::DeliverArg{});
|
||||
arg.param.resize(param_size);
|
||||
arg.hmac.resize(std::max<std::size_t>(hmac_size, 0x20));
|
||||
|
||||
IPC::RequestBuilder rb = rp.MakeBuilder(4, 4);
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.Push(arg.source_program_id);
|
||||
rb.Push<u8>(1);
|
||||
rb.PushStaticBuffer(std::move(arg.param), 0);
|
||||
rb.PushStaticBuffer(std::move(arg.hmac), 1);
|
||||
}
|
||||
|
||||
void Module::APTInterface::PrepareToStartApplication(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp(ctx, 0x15, 5, 0); // 0x00150140
|
||||
u32 title_info1 = rp.Pop<u32>();
|
||||
|
@ -809,6 +845,9 @@ void Module::APTInterface::GetStartupArgument(Kernel::HLERequestContext& ctx) {
|
|||
constexpr u32 max_parameter_size{0x1000};
|
||||
const auto startup_argument_type = static_cast<StartupArgumentType>(rp.Pop<u8>());
|
||||
|
||||
LOG_WARNING(Service_APT, "called, startup_argument_type={}, parameter_size={:#010X}",
|
||||
static_cast<u32>(startup_argument_type), parameter_size);
|
||||
|
||||
if (parameter_size > max_parameter_size) {
|
||||
LOG_ERROR(Service_APT,
|
||||
"Parameter size is outside the valid range (capped to {:#010X}): "
|
||||
|
@ -817,15 +856,36 @@ void Module::APTInterface::GetStartupArgument(Kernel::HLERequestContext& ctx) {
|
|||
parameter_size = max_parameter_size;
|
||||
}
|
||||
|
||||
std::vector<u8> parameter(parameter_size);
|
||||
std::vector<u8> param;
|
||||
bool exists = false;
|
||||
|
||||
LOG_WARNING(Service_APT, "(STUBBED) called, startup_argument_type={}, parameter_size={:#010X}",
|
||||
static_cast<u32>(startup_argument_type), parameter_size);
|
||||
if (auto arg = apt->applet_manager->ReceiveDeliverArg()) {
|
||||
param = std::move(arg->param);
|
||||
|
||||
// TODO: This is a complete guess based on observations. It is unknown how the OtherMedia
|
||||
// type is handled and how it interacts with the OtherApp type, and it is unknown if
|
||||
// this (checking the jump parameters) is indeed the way the 3DS checks the types.
|
||||
const auto& jump_parameters = apt->applet_manager->GetApplicationJumpParameters();
|
||||
switch (startup_argument_type) {
|
||||
case StartupArgumentType::OtherApp:
|
||||
exists = jump_parameters.current_title_id != jump_parameters.next_title_id &&
|
||||
jump_parameters.current_media_type == jump_parameters.next_media_type;
|
||||
break;
|
||||
case StartupArgumentType::Restart:
|
||||
exists = jump_parameters.current_title_id == jump_parameters.next_title_id;
|
||||
break;
|
||||
case StartupArgumentType::OtherMedia:
|
||||
exists = jump_parameters.current_media_type != jump_parameters.next_media_type;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
param.resize(parameter_size);
|
||||
|
||||
IPC::RequestBuilder rb = rp.MakeBuilder(2, 2);
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.Push<u32>(0);
|
||||
rb.PushStaticBuffer(std::move(parameter), 0);
|
||||
rb.Push(exists);
|
||||
rb.PushStaticBuffer(std::move(param), 0);
|
||||
}
|
||||
|
||||
void Module::APTInterface::Wrap(Kernel::HLERequestContext& ctx) {
|
||||
|
@ -967,6 +1027,10 @@ Module::APTInterface::APTInterface(std::shared_ptr<Module> apt, const char* name
|
|||
|
||||
Module::APTInterface::~APTInterface() = default;
|
||||
|
||||
std::shared_ptr<Module> Module::APTInterface::GetModule() const {
|
||||
return apt;
|
||||
}
|
||||
|
||||
Module::Module(Core::System& system) : system(system) {
|
||||
applet_manager = std::make_shared<AppletManager>(system);
|
||||
|
||||
|
@ -982,6 +1046,17 @@ Module::Module(Core::System& system) : system(system) {
|
|||
|
||||
Module::~Module() {}
|
||||
|
||||
std::shared_ptr<AppletManager> Module::GetAppletManager() const {
|
||||
return applet_manager;
|
||||
}
|
||||
|
||||
std::shared_ptr<Module> GetModule(Core::System& system) {
|
||||
auto apt = system.ServiceManager().GetService<Service::APT::Module::APTInterface>("APT:A");
|
||||
if (!apt)
|
||||
return nullptr;
|
||||
return apt->GetModule();
|
||||
}
|
||||
|
||||
void InstallInterfaces(Core::System& system) {
|
||||
auto& service_manager = system.ServiceManager();
|
||||
auto apt = std::make_shared<Module>(system);
|
||||
|
|
|
@ -65,11 +65,15 @@ public:
|
|||
explicit Module(Core::System& system);
|
||||
~Module();
|
||||
|
||||
std::shared_ptr<AppletManager> GetAppletManager() const;
|
||||
|
||||
class NSInterface : public ServiceFramework<NSInterface> {
|
||||
public:
|
||||
NSInterface(std::shared_ptr<Module> apt, const char* name, u32 max_session);
|
||||
~NSInterface();
|
||||
|
||||
std::shared_ptr<Module> GetModule() const;
|
||||
|
||||
protected:
|
||||
std::shared_ptr<Module> apt;
|
||||
|
||||
|
@ -90,6 +94,8 @@ public:
|
|||
APTInterface(std::shared_ptr<Module> apt, const char* name, u32 max_session);
|
||||
~APTInterface();
|
||||
|
||||
std::shared_ptr<Module> GetModule() const;
|
||||
|
||||
protected:
|
||||
/**
|
||||
* APT::Initialize service function
|
||||
|
@ -505,6 +511,23 @@ public:
|
|||
*/
|
||||
void GetProgramIdOnApplicationJump(Kernel::HLERequestContext& ctx);
|
||||
|
||||
/**
|
||||
* APT::ReceiveDeliverArg service function
|
||||
* Inputs:
|
||||
* 0 : Command header [0x00350080]
|
||||
* 1 : Parameter Size (capped to 0x300)
|
||||
* 2 : HMAC Size (capped to 0x20)
|
||||
* 64 : (Parameter Size << 14) | 2
|
||||
* 65 : Output buffer for Parameter
|
||||
* 66 : (HMAC Size << 14) | 0x802
|
||||
* 67 : Output buffer for HMAC
|
||||
* Outputs:
|
||||
* 1 : Result of function, 0 on success, otherwise error code
|
||||
* 2-3 : Source program id
|
||||
* 4 : u8, whether the arg is received (0 = not received, 1 = received)
|
||||
*/
|
||||
void ReceiveDeliverArg(Kernel::HLERequestContext& ctx);
|
||||
|
||||
/**
|
||||
* APT::CancelLibraryApplet service function
|
||||
* Inputs:
|
||||
|
@ -725,6 +748,8 @@ private:
|
|||
friend class boost::serialization::access;
|
||||
};
|
||||
|
||||
std::shared_ptr<Module> GetModule(Core::System& system);
|
||||
|
||||
void InstallInterfaces(Core::System& system);
|
||||
|
||||
} // namespace Service::APT
|
||||
|
|
|
@ -62,7 +62,7 @@ APT_A::APT_A(std::shared_ptr<Module> apt)
|
|||
{0x00320084, &APT_A::DoApplicationJump, "DoApplicationJump"},
|
||||
{0x00330000, &APT_A::GetProgramIdOnApplicationJump, "GetProgramIdOnApplicationJump"},
|
||||
{0x00340084, nullptr, "SendDeliverArg"},
|
||||
{0x00350080, nullptr, "ReceiveDeliverArg"},
|
||||
{0x00350080, &APT_A::ReceiveDeliverArg, "ReceiveDeliverArg"},
|
||||
{0x00360040, nullptr, "LoadSysMenuArg"},
|
||||
{0x00370042, nullptr, "StoreSysMenuArg"},
|
||||
{0x00380040, nullptr, "PreloadResidentApplet"},
|
||||
|
|
|
@ -62,7 +62,7 @@ APT_U::APT_U(std::shared_ptr<Module> apt)
|
|||
{0x00320084, &APT_U::DoApplicationJump, "DoApplicationJump"},
|
||||
{0x00330000, &APT_U::GetProgramIdOnApplicationJump, "GetProgramIdOnApplicationJump"},
|
||||
{0x00340084, nullptr, "SendDeliverArg"},
|
||||
{0x00350080, nullptr, "ReceiveDeliverArg"},
|
||||
{0x00350080, &APT_U::ReceiveDeliverArg, "ReceiveDeliverArg"},
|
||||
{0x00360040, &APT_U::LoadSysMenuArg, "LoadSysMenuArg"},
|
||||
{0x00370042, &APT_U::StoreSysMenuArg, "StoreSysMenuArg"},
|
||||
{0x00380040, nullptr, "PreloadResidentApplet"},
|
||||
|
|
Loading…
Reference in a new issue