APT/Applet: move applet managing into its own class
This commit is contained in:
parent
44d07574b1
commit
92f0064b47
|
@ -164,6 +164,8 @@ add_library(core STATIC
|
||||||
hle/service/am/am_sys.h
|
hle/service/am/am_sys.h
|
||||||
hle/service/am/am_u.cpp
|
hle/service/am/am_u.cpp
|
||||||
hle/service/am/am_u.h
|
hle/service/am/am_u.h
|
||||||
|
hle/service/apt/applet_manager.cpp
|
||||||
|
hle/service/apt/applet_manager.h
|
||||||
hle/service/apt/apt.cpp
|
hle/service/apt/apt.cpp
|
||||||
hle/service/apt/apt.h
|
hle/service/apt/apt.h
|
||||||
hle/service/apt/apt_a.cpp
|
hle/service/apt/apt_a.cpp
|
||||||
|
@ -174,6 +176,7 @@ add_library(core STATIC
|
||||||
hle/service/apt/apt_u.h
|
hle/service/apt/apt_u.h
|
||||||
hle/service/apt/bcfnt/bcfnt.cpp
|
hle/service/apt/bcfnt/bcfnt.cpp
|
||||||
hle/service/apt/bcfnt/bcfnt.h
|
hle/service/apt/bcfnt/bcfnt.h
|
||||||
|
hle/service/apt/errors.h
|
||||||
hle/service/boss/boss.cpp
|
hle/service/boss/boss.cpp
|
||||||
hle/service/boss/boss.h
|
hle/service/boss/boss.h
|
||||||
hle/service/boss/boss_p.cpp
|
hle/service/boss/boss_p.cpp
|
||||||
|
|
|
@ -15,7 +15,6 @@
|
||||||
#include "core/hle/applets/mint.h"
|
#include "core/hle/applets/mint.h"
|
||||||
#include "core/hle/applets/swkbd.h"
|
#include "core/hle/applets/swkbd.h"
|
||||||
#include "core/hle/result.h"
|
#include "core/hle/result.h"
|
||||||
#include "core/hle/service/apt/apt.h"
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
@ -43,23 +42,24 @@ static CoreTiming::EventType* applet_update_event = nullptr;
|
||||||
/// The interval at which the Applet update callback will be called, 16.6ms
|
/// The interval at which the Applet update callback will be called, 16.6ms
|
||||||
static const u64 applet_update_interval_us = 16666;
|
static const u64 applet_update_interval_us = 16666;
|
||||||
|
|
||||||
ResultCode Applet::Create(Service::APT::AppletId id) {
|
ResultCode Applet::Create(Service::APT::AppletId id,
|
||||||
|
std::weak_ptr<Service::APT::AppletManager> manager) {
|
||||||
switch (id) {
|
switch (id) {
|
||||||
case Service::APT::AppletId::SoftwareKeyboard1:
|
case Service::APT::AppletId::SoftwareKeyboard1:
|
||||||
case Service::APT::AppletId::SoftwareKeyboard2:
|
case Service::APT::AppletId::SoftwareKeyboard2:
|
||||||
applets[id] = std::make_shared<SoftwareKeyboard>(id);
|
applets[id] = std::make_shared<SoftwareKeyboard>(id, std::move(manager));
|
||||||
break;
|
break;
|
||||||
case Service::APT::AppletId::Ed1:
|
case Service::APT::AppletId::Ed1:
|
||||||
case Service::APT::AppletId::Ed2:
|
case Service::APT::AppletId::Ed2:
|
||||||
applets[id] = std::make_shared<MiiSelector>(id);
|
applets[id] = std::make_shared<MiiSelector>(id, std::move(manager));
|
||||||
break;
|
break;
|
||||||
case Service::APT::AppletId::Error:
|
case Service::APT::AppletId::Error:
|
||||||
case Service::APT::AppletId::Error2:
|
case Service::APT::AppletId::Error2:
|
||||||
applets[id] = std::make_shared<ErrEula>(id);
|
applets[id] = std::make_shared<ErrEula>(id, std::move(manager));
|
||||||
break;
|
break;
|
||||||
case Service::APT::AppletId::Mint:
|
case Service::APT::AppletId::Mint:
|
||||||
case Service::APT::AppletId::Mint2:
|
case Service::APT::AppletId::Mint2:
|
||||||
applets[id] = std::make_shared<Mint>(id);
|
applets[id] = std::make_shared<Mint>(id, std::move(manager));
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
LOG_ERROR(Service_APT, "Could not create applet %u", static_cast<u32>(id));
|
LOG_ERROR(Service_APT, "Could not create applet %u", static_cast<u32>(id));
|
||||||
|
@ -110,6 +110,14 @@ bool Applet::IsRunning() const {
|
||||||
return is_running;
|
return is_running;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Applet::SendParameter(const Service::APT::MessageParameter& parameter) {
|
||||||
|
if (auto locked = manager.lock()) {
|
||||||
|
locked->CancelAndSendParameter(parameter);
|
||||||
|
} else {
|
||||||
|
LOG_ERROR(Service_APT, "called after destructing applet manager");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bool IsLibraryAppletRunning() {
|
bool IsLibraryAppletRunning() {
|
||||||
// Check the applets map for instances of any applet
|
// Check the applets map for instances of any applet
|
||||||
for (auto itr = applets.begin(); itr != applets.end(); ++itr)
|
for (auto itr = applets.begin(); itr != applets.end(); ++itr)
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include "core/hle/result.h"
|
#include "core/hle/result.h"
|
||||||
#include "core/hle/service/apt/apt.h"
|
#include "core/hle/service/apt/applet_manager.h"
|
||||||
|
|
||||||
namespace HLE {
|
namespace HLE {
|
||||||
namespace Applets {
|
namespace Applets {
|
||||||
|
@ -21,7 +21,8 @@ public:
|
||||||
* @param id Id of the applet to create.
|
* @param id Id of the applet to create.
|
||||||
* @returns ResultCode Whether the operation was successful or not.
|
* @returns ResultCode Whether the operation was successful or not.
|
||||||
*/
|
*/
|
||||||
static ResultCode Create(Service::APT::AppletId id);
|
static ResultCode Create(Service::APT::AppletId id,
|
||||||
|
std::weak_ptr<Service::APT::AppletManager> manager);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieves the Applet instance identified by the specified id.
|
* Retrieves the Applet instance identified by the specified id.
|
||||||
|
@ -55,7 +56,8 @@ public:
|
||||||
virtual void Update() = 0;
|
virtual void Update() = 0;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
explicit Applet(Service::APT::AppletId id) : id(id) {}
|
Applet(Service::APT::AppletId id, std::weak_ptr<Service::APT::AppletManager> manager)
|
||||||
|
: id(id), manager(std::move(manager)) {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handles the Applet start event, triggered from the application.
|
* Handles the Applet start event, triggered from the application.
|
||||||
|
@ -69,6 +71,11 @@ protected:
|
||||||
|
|
||||||
/// Whether this applet is currently running instead of the host application or not.
|
/// Whether this applet is currently running instead of the host application or not.
|
||||||
bool is_running = false;
|
bool is_running = false;
|
||||||
|
|
||||||
|
void SendParameter(const Service::APT::MessageParameter& parameter);
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::weak_ptr<Service::APT::AppletManager> manager;
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Returns whether a library applet is currently running
|
/// Returns whether a library applet is currently running
|
||||||
|
|
|
@ -10,8 +10,8 @@ namespace HLE {
|
||||||
namespace Applets {
|
namespace Applets {
|
||||||
|
|
||||||
ResultCode ErrEula::ReceiveParameter(const Service::APT::MessageParameter& parameter) {
|
ResultCode ErrEula::ReceiveParameter(const Service::APT::MessageParameter& parameter) {
|
||||||
if (parameter.signal != static_cast<u32>(Service::APT::SignalType::Request)) {
|
if (parameter.signal != Service::APT::SignalType::Request) {
|
||||||
LOG_ERROR(Service_APT, "unsupported signal %u", parameter.signal);
|
LOG_ERROR(Service_APT, "unsupported signal %u", static_cast<u32>(parameter.signal));
|
||||||
UNIMPLEMENTED();
|
UNIMPLEMENTED();
|
||||||
// TODO(Subv): Find the right error code
|
// TODO(Subv): Find the right error code
|
||||||
return ResultCode(-1);
|
return ResultCode(-1);
|
||||||
|
@ -36,13 +36,13 @@ ResultCode ErrEula::ReceiveParameter(const Service::APT::MessageParameter& param
|
||||||
|
|
||||||
// Send the response message with the newly created SharedMemory
|
// Send the response message with the newly created SharedMemory
|
||||||
Service::APT::MessageParameter result;
|
Service::APT::MessageParameter result;
|
||||||
result.signal = static_cast<u32>(Service::APT::SignalType::Response);
|
result.signal = Service::APT::SignalType::Response;
|
||||||
result.buffer.clear();
|
result.buffer.clear();
|
||||||
result.destination_id = static_cast<u32>(Service::APT::AppletId::Application);
|
result.destination_id = Service::APT::AppletId::Application;
|
||||||
result.sender_id = static_cast<u32>(id);
|
result.sender_id = id;
|
||||||
result.object = framebuffer_memory;
|
result.object = framebuffer_memory;
|
||||||
|
|
||||||
Service::APT::SendParameter(result);
|
SendParameter(result);
|
||||||
return RESULT_SUCCESS;
|
return RESULT_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -57,10 +57,10 @@ ResultCode ErrEula::StartImpl(const Service::APT::AppletStartupParameter& parame
|
||||||
Service::APT::MessageParameter message;
|
Service::APT::MessageParameter message;
|
||||||
message.buffer.resize(parameter.buffer.size());
|
message.buffer.resize(parameter.buffer.size());
|
||||||
std::fill(message.buffer.begin(), message.buffer.end(), 0);
|
std::fill(message.buffer.begin(), message.buffer.end(), 0);
|
||||||
message.signal = static_cast<u32>(Service::APT::SignalType::WakeupByExit);
|
message.signal = Service::APT::SignalType::WakeupByExit;
|
||||||
message.destination_id = static_cast<u32>(Service::APT::AppletId::Application);
|
message.destination_id = Service::APT::AppletId::Application;
|
||||||
message.sender_id = static_cast<u32>(id);
|
message.sender_id = id;
|
||||||
Service::APT::SendParameter(message);
|
SendParameter(message);
|
||||||
|
|
||||||
is_running = false;
|
is_running = false;
|
||||||
return RESULT_SUCCESS;
|
return RESULT_SUCCESS;
|
||||||
|
|
|
@ -12,7 +12,8 @@ namespace Applets {
|
||||||
|
|
||||||
class ErrEula final : public Applet {
|
class ErrEula final : public Applet {
|
||||||
public:
|
public:
|
||||||
explicit ErrEula(Service::APT::AppletId id) : Applet(id) {}
|
explicit ErrEula(Service::APT::AppletId id, std::weak_ptr<Service::APT::AppletManager> manager)
|
||||||
|
: Applet(id, std::move(manager)) {}
|
||||||
|
|
||||||
ResultCode ReceiveParameter(const Service::APT::MessageParameter& parameter) override;
|
ResultCode ReceiveParameter(const Service::APT::MessageParameter& parameter) override;
|
||||||
ResultCode StartImpl(const Service::APT::AppletStartupParameter& parameter) override;
|
ResultCode StartImpl(const Service::APT::AppletStartupParameter& parameter) override;
|
||||||
|
|
|
@ -18,8 +18,8 @@ namespace HLE {
|
||||||
namespace Applets {
|
namespace Applets {
|
||||||
|
|
||||||
ResultCode MiiSelector::ReceiveParameter(const Service::APT::MessageParameter& parameter) {
|
ResultCode MiiSelector::ReceiveParameter(const Service::APT::MessageParameter& parameter) {
|
||||||
if (parameter.signal != static_cast<u32>(Service::APT::SignalType::Request)) {
|
if (parameter.signal != Service::APT::SignalType::Request) {
|
||||||
LOG_ERROR(Service_APT, "unsupported signal %u", parameter.signal);
|
LOG_ERROR(Service_APT, "unsupported signal %u", static_cast<u32>(parameter.signal));
|
||||||
UNIMPLEMENTED();
|
UNIMPLEMENTED();
|
||||||
// TODO(Subv): Find the right error code
|
// TODO(Subv): Find the right error code
|
||||||
return ResultCode(-1);
|
return ResultCode(-1);
|
||||||
|
@ -43,13 +43,13 @@ ResultCode MiiSelector::ReceiveParameter(const Service::APT::MessageParameter& p
|
||||||
|
|
||||||
// Send the response message with the newly created SharedMemory
|
// Send the response message with the newly created SharedMemory
|
||||||
Service::APT::MessageParameter result;
|
Service::APT::MessageParameter result;
|
||||||
result.signal = static_cast<u32>(Service::APT::SignalType::Response);
|
result.signal = Service::APT::SignalType::Response;
|
||||||
result.buffer.clear();
|
result.buffer.clear();
|
||||||
result.destination_id = static_cast<u32>(Service::APT::AppletId::Application);
|
result.destination_id = Service::APT::AppletId::Application;
|
||||||
result.sender_id = static_cast<u32>(id);
|
result.sender_id = id;
|
||||||
result.object = framebuffer_memory;
|
result.object = framebuffer_memory;
|
||||||
|
|
||||||
Service::APT::SendParameter(result);
|
SendParameter(result);
|
||||||
return RESULT_SUCCESS;
|
return RESULT_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -72,10 +72,10 @@ ResultCode MiiSelector::StartImpl(const Service::APT::AppletStartupParameter& pa
|
||||||
Service::APT::MessageParameter message;
|
Service::APT::MessageParameter message;
|
||||||
message.buffer.resize(sizeof(MiiResult));
|
message.buffer.resize(sizeof(MiiResult));
|
||||||
std::memcpy(message.buffer.data(), &result, message.buffer.size());
|
std::memcpy(message.buffer.data(), &result, message.buffer.size());
|
||||||
message.signal = static_cast<u32>(Service::APT::SignalType::WakeupByExit);
|
message.signal = Service::APT::SignalType::WakeupByExit;
|
||||||
message.destination_id = static_cast<u32>(Service::APT::AppletId::Application);
|
message.destination_id = Service::APT::AppletId::Application;
|
||||||
message.sender_id = static_cast<u32>(id);
|
message.sender_id = id;
|
||||||
Service::APT::SendParameter(message);
|
SendParameter(message);
|
||||||
|
|
||||||
is_running = false;
|
is_running = false;
|
||||||
return RESULT_SUCCESS;
|
return RESULT_SUCCESS;
|
||||||
|
|
|
@ -60,7 +60,8 @@ ASSERT_REG_POSITION(guest_mii_name, 0x6C);
|
||||||
|
|
||||||
class MiiSelector final : public Applet {
|
class MiiSelector final : public Applet {
|
||||||
public:
|
public:
|
||||||
MiiSelector(Service::APT::AppletId id) : Applet(id) {}
|
MiiSelector(Service::APT::AppletId id, std::weak_ptr<Service::APT::AppletManager> manager)
|
||||||
|
: Applet(id, std::move(manager)) {}
|
||||||
|
|
||||||
ResultCode ReceiveParameter(const Service::APT::MessageParameter& parameter) override;
|
ResultCode ReceiveParameter(const Service::APT::MessageParameter& parameter) override;
|
||||||
ResultCode StartImpl(const Service::APT::AppletStartupParameter& parameter) override;
|
ResultCode StartImpl(const Service::APT::AppletStartupParameter& parameter) override;
|
||||||
|
|
|
@ -10,8 +10,8 @@ namespace HLE {
|
||||||
namespace Applets {
|
namespace Applets {
|
||||||
|
|
||||||
ResultCode Mint::ReceiveParameter(const Service::APT::MessageParameter& parameter) {
|
ResultCode Mint::ReceiveParameter(const Service::APT::MessageParameter& parameter) {
|
||||||
if (parameter.signal != static_cast<u32>(Service::APT::SignalType::Request)) {
|
if (parameter.signal != Service::APT::SignalType::Request) {
|
||||||
LOG_ERROR(Service_APT, "unsupported signal %u", parameter.signal);
|
LOG_ERROR(Service_APT, "unsupported signal %u", static_cast<u32>(parameter.signal));
|
||||||
UNIMPLEMENTED();
|
UNIMPLEMENTED();
|
||||||
// TODO(Subv): Find the right error code
|
// TODO(Subv): Find the right error code
|
||||||
return ResultCode(-1);
|
return ResultCode(-1);
|
||||||
|
@ -36,13 +36,13 @@ ResultCode Mint::ReceiveParameter(const Service::APT::MessageParameter& paramete
|
||||||
|
|
||||||
// Send the response message with the newly created SharedMemory
|
// Send the response message with the newly created SharedMemory
|
||||||
Service::APT::MessageParameter result;
|
Service::APT::MessageParameter result;
|
||||||
result.signal = static_cast<u32>(Service::APT::SignalType::Response);
|
result.signal = Service::APT::SignalType::Response;
|
||||||
result.buffer.clear();
|
result.buffer.clear();
|
||||||
result.destination_id = static_cast<u32>(Service::APT::AppletId::Application);
|
result.destination_id = Service::APT::AppletId::Application;
|
||||||
result.sender_id = static_cast<u32>(id);
|
result.sender_id = id;
|
||||||
result.object = framebuffer_memory;
|
result.object = framebuffer_memory;
|
||||||
|
|
||||||
Service::APT::SendParameter(result);
|
SendParameter(result);
|
||||||
return RESULT_SUCCESS;
|
return RESULT_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -57,10 +57,10 @@ ResultCode Mint::StartImpl(const Service::APT::AppletStartupParameter& parameter
|
||||||
Service::APT::MessageParameter message;
|
Service::APT::MessageParameter message;
|
||||||
message.buffer.resize(parameter.buffer.size());
|
message.buffer.resize(parameter.buffer.size());
|
||||||
std::fill(message.buffer.begin(), message.buffer.end(), 0);
|
std::fill(message.buffer.begin(), message.buffer.end(), 0);
|
||||||
message.signal = static_cast<u32>(Service::APT::SignalType::WakeupByExit);
|
message.signal = Service::APT::SignalType::WakeupByExit;
|
||||||
message.destination_id = static_cast<u32>(Service::APT::AppletId::Application);
|
message.destination_id = Service::APT::AppletId::Application;
|
||||||
message.sender_id = static_cast<u32>(id);
|
message.sender_id = id;
|
||||||
Service::APT::SendParameter(message);
|
SendParameter(message);
|
||||||
|
|
||||||
is_running = false;
|
is_running = false;
|
||||||
return RESULT_SUCCESS;
|
return RESULT_SUCCESS;
|
||||||
|
|
|
@ -12,7 +12,8 @@ namespace Applets {
|
||||||
|
|
||||||
class Mint final : public Applet {
|
class Mint final : public Applet {
|
||||||
public:
|
public:
|
||||||
explicit Mint(Service::APT::AppletId id) : Applet(id) {}
|
explicit Mint(Service::APT::AppletId id, std::weak_ptr<Service::APT::AppletManager> manager)
|
||||||
|
: Applet(id, std::move(manager)) {}
|
||||||
|
|
||||||
ResultCode ReceiveParameter(const Service::APT::MessageParameter& parameter) override;
|
ResultCode ReceiveParameter(const Service::APT::MessageParameter& parameter) override;
|
||||||
ResultCode StartImpl(const Service::APT::AppletStartupParameter& parameter) override;
|
ResultCode StartImpl(const Service::APT::AppletStartupParameter& parameter) override;
|
||||||
|
|
|
@ -21,8 +21,8 @@ namespace HLE {
|
||||||
namespace Applets {
|
namespace Applets {
|
||||||
|
|
||||||
ResultCode SoftwareKeyboard::ReceiveParameter(Service::APT::MessageParameter const& parameter) {
|
ResultCode SoftwareKeyboard::ReceiveParameter(Service::APT::MessageParameter const& parameter) {
|
||||||
if (parameter.signal != static_cast<u32>(Service::APT::SignalType::Request)) {
|
if (parameter.signal != Service::APT::SignalType::Request) {
|
||||||
LOG_ERROR(Service_APT, "unsupported signal %u", parameter.signal);
|
LOG_ERROR(Service_APT, "unsupported signal %u", static_cast<u32>(parameter.signal));
|
||||||
UNIMPLEMENTED();
|
UNIMPLEMENTED();
|
||||||
// TODO(Subv): Find the right error code
|
// TODO(Subv): Find the right error code
|
||||||
return ResultCode(-1);
|
return ResultCode(-1);
|
||||||
|
@ -46,13 +46,13 @@ ResultCode SoftwareKeyboard::ReceiveParameter(Service::APT::MessageParameter con
|
||||||
|
|
||||||
// Send the response message with the newly created SharedMemory
|
// Send the response message with the newly created SharedMemory
|
||||||
Service::APT::MessageParameter result;
|
Service::APT::MessageParameter result;
|
||||||
result.signal = static_cast<u32>(Service::APT::SignalType::Response);
|
result.signal = Service::APT::SignalType::Response;
|
||||||
result.buffer.clear();
|
result.buffer.clear();
|
||||||
result.destination_id = static_cast<u32>(Service::APT::AppletId::Application);
|
result.destination_id = Service::APT::AppletId::Application;
|
||||||
result.sender_id = static_cast<u32>(id);
|
result.sender_id = id;
|
||||||
result.object = framebuffer_memory;
|
result.object = framebuffer_memory;
|
||||||
|
|
||||||
Service::APT::SendParameter(result);
|
SendParameter(result);
|
||||||
return RESULT_SUCCESS;
|
return RESULT_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -107,10 +107,10 @@ void SoftwareKeyboard::Finalize() {
|
||||||
Service::APT::MessageParameter message;
|
Service::APT::MessageParameter message;
|
||||||
message.buffer.resize(sizeof(SoftwareKeyboardConfig));
|
message.buffer.resize(sizeof(SoftwareKeyboardConfig));
|
||||||
std::memcpy(message.buffer.data(), &config, message.buffer.size());
|
std::memcpy(message.buffer.data(), &config, message.buffer.size());
|
||||||
message.signal = static_cast<u32>(Service::APT::SignalType::WakeupByExit);
|
message.signal = Service::APT::SignalType::WakeupByExit;
|
||||||
message.destination_id = static_cast<u32>(Service::APT::AppletId::Application);
|
message.destination_id = Service::APT::AppletId::Application;
|
||||||
message.sender_id = static_cast<u32>(id);
|
message.sender_id = id;
|
||||||
Service::APT::SendParameter(message);
|
SendParameter(message);
|
||||||
|
|
||||||
is_running = false;
|
is_running = false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -52,7 +52,8 @@ static_assert(sizeof(SoftwareKeyboardConfig) == 0x400, "Software Keyboard Config
|
||||||
|
|
||||||
class SoftwareKeyboard final : public Applet {
|
class SoftwareKeyboard final : public Applet {
|
||||||
public:
|
public:
|
||||||
SoftwareKeyboard(Service::APT::AppletId id) : Applet(id) {}
|
SoftwareKeyboard(Service::APT::AppletId id, std::weak_ptr<Service::APT::AppletManager> manager)
|
||||||
|
: Applet(id, std::move(manager)) {}
|
||||||
|
|
||||||
ResultCode ReceiveParameter(const Service::APT::MessageParameter& parameter) override;
|
ResultCode ReceiveParameter(const Service::APT::MessageParameter& parameter) override;
|
||||||
ResultCode StartImpl(const Service::APT::AppletStartupParameter& parameter) override;
|
ResultCode StartImpl(const Service::APT::AppletStartupParameter& parameter) override;
|
||||||
|
|
|
@ -0,0 +1,440 @@
|
||||||
|
// Copyright 2018 Citra Emulator Project
|
||||||
|
// Licensed under GPLv2 or any later version
|
||||||
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
#include "common/common_paths.h"
|
||||||
|
#include "core/hle/applets/applet.h"
|
||||||
|
#include "core/hle/service/apt/applet_manager.h"
|
||||||
|
#include "core/hle/service/apt/errors.h"
|
||||||
|
#include "core/hle/service/cfg/cfg.h"
|
||||||
|
#include "core/hle/service/ns/ns.h"
|
||||||
|
|
||||||
|
namespace Service {
|
||||||
|
namespace APT {
|
||||||
|
|
||||||
|
enum class AppletPos { Application = 0, Library = 1, System = 2, SysLibrary = 3, Resident = 4 };
|
||||||
|
|
||||||
|
struct AppletTitleData {
|
||||||
|
// There are two possible applet ids for each applet.
|
||||||
|
std::array<AppletId, 2> applet_ids;
|
||||||
|
|
||||||
|
// There's a specific TitleId per region for each applet.
|
||||||
|
static constexpr size_t NumRegions = 7;
|
||||||
|
std::array<u64, NumRegions> title_ids;
|
||||||
|
};
|
||||||
|
|
||||||
|
static constexpr size_t NumApplets = 29;
|
||||||
|
static constexpr std::array<AppletTitleData, NumApplets> applet_titleids = {{
|
||||||
|
{AppletId::HomeMenu, AppletId::None, 0x4003000008202, 0x4003000008F02, 0x4003000009802,
|
||||||
|
0x4003000008202, 0x400300000A102, 0x400300000A902, 0x400300000B102},
|
||||||
|
{AppletId::AlternateMenu, AppletId::None, 0x4003000008102, 0x4003000008102, 0x4003000008102,
|
||||||
|
0x4003000008102, 0x4003000008102, 0x4003000008102, 0x4003000008102},
|
||||||
|
{AppletId::Camera, AppletId::None, 0x4003000008402, 0x4003000009002, 0x4003000009902,
|
||||||
|
0x4003000008402, 0x400300000A202, 0x400300000AA02, 0x400300000B202},
|
||||||
|
{AppletId::FriendList, AppletId::None, 0x4003000008D02, 0x4003000009602, 0x4003000009F02,
|
||||||
|
0x4003000008D02, 0x400300000A702, 0x400300000AF02, 0x400300000B702},
|
||||||
|
{AppletId::GameNotes, AppletId::None, 0x4003000008702, 0x4003000009302, 0x4003000009C02,
|
||||||
|
0x4003000008702, 0x400300000A502, 0x400300000AD02, 0x400300000B502},
|
||||||
|
{AppletId::InternetBrowser, AppletId::None, 0x4003000008802, 0x4003000009402, 0x4003000009D02,
|
||||||
|
0x4003000008802, 0x400300000A602, 0x400300000AE02, 0x400300000B602},
|
||||||
|
{AppletId::InstructionManual, AppletId::None, 0x4003000008602, 0x4003000009202, 0x4003000009B02,
|
||||||
|
0x4003000008602, 0x400300000A402, 0x400300000AC02, 0x400300000B402},
|
||||||
|
{AppletId::Notifications, AppletId::None, 0x4003000008E02, 0x4003000009702, 0x400300000A002,
|
||||||
|
0x4003000008E02, 0x400300000A802, 0x400300000B002, 0x400300000B802},
|
||||||
|
{AppletId::Miiverse, AppletId::None, 0x400300000BC02, 0x400300000BD02, 0x400300000BE02,
|
||||||
|
0x400300000BC02, 0x4003000009E02, 0x4003000009502, 0x400300000B902},
|
||||||
|
// These values obtained from an older NS dump firmware 4.5
|
||||||
|
{AppletId::MiiversePost, AppletId::None, 0x400300000BA02, 0x400300000BA02, 0x400300000BA02,
|
||||||
|
0x400300000BA02, 0x400300000BA02, 0x400300000BA02, 0x400300000BA02},
|
||||||
|
// {AppletId::MiiversePost, AppletId::None, 0x4003000008302, 0x4003000008B02, 0x400300000BA02,
|
||||||
|
// 0x4003000008302, 0x0, 0x0, 0x0},
|
||||||
|
{AppletId::AmiiboSettings, AppletId::None, 0x4003000009502, 0x4003000009E02, 0x400300000B902,
|
||||||
|
0x4003000009502, 0x0, 0x4003000008C02, 0x400300000BF02},
|
||||||
|
{AppletId::SoftwareKeyboard1, AppletId::SoftwareKeyboard2, 0x400300000C002, 0x400300000C802,
|
||||||
|
0x400300000D002, 0x400300000C002, 0x400300000D802, 0x400300000DE02, 0x400300000E402},
|
||||||
|
{AppletId::Ed1, AppletId::Ed2, 0x400300000C102, 0x400300000C902, 0x400300000D102,
|
||||||
|
0x400300000C102, 0x400300000D902, 0x400300000DF02, 0x400300000E502},
|
||||||
|
{AppletId::PnoteApp, AppletId::PnoteApp2, 0x400300000C302, 0x400300000CB02, 0x400300000D302,
|
||||||
|
0x400300000C302, 0x400300000DB02, 0x400300000E102, 0x400300000E702},
|
||||||
|
{AppletId::SnoteApp, AppletId::SnoteApp2, 0x400300000C402, 0x400300000CC02, 0x400300000D402,
|
||||||
|
0x400300000C402, 0x400300000DC02, 0x400300000E202, 0x400300000E802},
|
||||||
|
{AppletId::Error, AppletId::Error2, 0x400300000C502, 0x400300000C502, 0x400300000C502,
|
||||||
|
0x400300000C502, 0x400300000CF02, 0x400300000CF02, 0x400300000CF02},
|
||||||
|
{AppletId::Mint, AppletId::Mint2, 0x400300000C602, 0x400300000CE02, 0x400300000D602,
|
||||||
|
0x400300000C602, 0x400300000DD02, 0x400300000E302, 0x400300000E902},
|
||||||
|
{AppletId::Extrapad, AppletId::Extrapad2, 0x400300000CD02, 0x400300000CD02, 0x400300000CD02,
|
||||||
|
0x400300000CD02, 0x400300000D502, 0x400300000D502, 0x400300000D502},
|
||||||
|
{AppletId::Memolib, AppletId::Memolib2, 0x400300000F602, 0x400300000F602, 0x400300000F602,
|
||||||
|
0x400300000F602, 0x400300000F602, 0x400300000F602, 0x400300000F602},
|
||||||
|
// TODO(Subv): Fill in the rest of the titleids
|
||||||
|
}};
|
||||||
|
|
||||||
|
static u64 GetTitleIdForApplet(AppletId id) {
|
||||||
|
ASSERT_MSG(id != AppletId::None, "Invalid applet id");
|
||||||
|
|
||||||
|
auto itr = std::find_if(applet_titleids.begin(), applet_titleids.end(),
|
||||||
|
[id](const AppletTitleData& data) {
|
||||||
|
return data.applet_ids[0] == id || data.applet_ids[1] == id;
|
||||||
|
});
|
||||||
|
|
||||||
|
ASSERT_MSG(itr != applet_titleids.end(), "Unknown applet id 0x%03X", static_cast<u32>(id));
|
||||||
|
|
||||||
|
return itr->title_ids[CFG::GetRegionValue()];
|
||||||
|
}
|
||||||
|
|
||||||
|
AppletManager::AppletSlotData* AppletManager::GetAppletSlotData(AppletId id) {
|
||||||
|
auto GetSlot = [this](AppletSlot slot) -> AppletSlotData* {
|
||||||
|
return &applet_slots[static_cast<size_t>(slot)];
|
||||||
|
};
|
||||||
|
|
||||||
|
if (id == AppletId::Application) {
|
||||||
|
auto* slot = GetSlot(AppletSlot::Application);
|
||||||
|
if (slot->applet_id != AppletId::None)
|
||||||
|
return slot;
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (id == AppletId::AnySystemApplet) {
|
||||||
|
auto* system_slot = GetSlot(AppletSlot::SystemApplet);
|
||||||
|
if (system_slot->applet_id != AppletId::None)
|
||||||
|
return system_slot;
|
||||||
|
|
||||||
|
// The Home Menu is also a system applet, but it lives in its own slot to be able to run
|
||||||
|
// concurrently with other system applets.
|
||||||
|
auto* home_slot = GetSlot(AppletSlot::HomeMenu);
|
||||||
|
if (home_slot->applet_id != AppletId::None)
|
||||||
|
return home_slot;
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (id == AppletId::AnyLibraryApplet || id == AppletId::AnySysLibraryApplet) {
|
||||||
|
auto* slot = GetSlot(AppletSlot::LibraryApplet);
|
||||||
|
if (slot->applet_id == AppletId::None)
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
u32 applet_pos = slot->attributes.applet_pos;
|
||||||
|
|
||||||
|
if (id == AppletId::AnyLibraryApplet && applet_pos == static_cast<u32>(AppletPos::Library))
|
||||||
|
return slot;
|
||||||
|
|
||||||
|
if (id == AppletId::AnySysLibraryApplet &&
|
||||||
|
applet_pos == static_cast<u32>(AppletPos::SysLibrary))
|
||||||
|
return slot;
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (id == AppletId::HomeMenu || id == AppletId::AlternateMenu) {
|
||||||
|
auto* slot = GetSlot(AppletSlot::HomeMenu);
|
||||||
|
if (slot->applet_id != AppletId::None)
|
||||||
|
return slot;
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto& slot : applet_slots) {
|
||||||
|
if (slot.applet_id == id)
|
||||||
|
return &slot;
|
||||||
|
}
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
AppletManager::AppletSlotData* AppletManager::GetAppletSlotData(AppletAttributes attributes) {
|
||||||
|
// Mapping from AppletPos to AppletSlot
|
||||||
|
static constexpr std::array<AppletSlot, 6> applet_position_slots = {
|
||||||
|
AppletSlot::Application, AppletSlot::LibraryApplet, AppletSlot::SystemApplet,
|
||||||
|
AppletSlot::LibraryApplet, AppletSlot::Error, AppletSlot::LibraryApplet};
|
||||||
|
|
||||||
|
u32 applet_pos = attributes.applet_pos;
|
||||||
|
if (applet_pos >= applet_position_slots.size())
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
AppletSlot slot = applet_position_slots[applet_pos];
|
||||||
|
|
||||||
|
if (slot == AppletSlot::Error)
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
// The Home Menu is a system applet, however, it has its own applet slot so that it can run
|
||||||
|
// concurrently with other system applets.
|
||||||
|
if (slot == AppletSlot::SystemApplet && attributes.is_home_menu)
|
||||||
|
return &applet_slots[static_cast<size_t>(AppletSlot::HomeMenu)];
|
||||||
|
|
||||||
|
return &applet_slots[static_cast<size_t>(slot)];
|
||||||
|
}
|
||||||
|
|
||||||
|
void AppletManager::CancelAndSendParameter(const MessageParameter& parameter) {
|
||||||
|
next_parameter = parameter;
|
||||||
|
// Signal the event to let the receiver know that a new parameter is ready to be read
|
||||||
|
auto* const slot_data = GetAppletSlotData(static_cast<AppletId>(parameter.destination_id));
|
||||||
|
if (slot_data == nullptr) {
|
||||||
|
LOG_DEBUG(Service_APT, "No applet was registered with the id %03X",
|
||||||
|
static_cast<u32>(parameter.destination_id));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
slot_data->parameter_event->Signal();
|
||||||
|
}
|
||||||
|
|
||||||
|
ResultCode AppletManager::SendParameter(const MessageParameter& parameter) {
|
||||||
|
// A new parameter can not be sent if the previous one hasn't been consumed yet
|
||||||
|
if (next_parameter) {
|
||||||
|
return ResultCode(ErrCodes::ParameterPresent, ErrorModule::Applet,
|
||||||
|
ErrorSummary::InvalidState, ErrorLevel::Status);
|
||||||
|
}
|
||||||
|
CancelAndSendParameter(parameter);
|
||||||
|
if (auto dest_applet = HLE::Applets::Applet::Get(parameter.destination_id)) {
|
||||||
|
return dest_applet->ReceiveParameter(parameter);
|
||||||
|
} else {
|
||||||
|
return RESULT_SUCCESS;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ResultVal<MessageParameter> AppletManager::GlanceParameter(AppletId app_id) {
|
||||||
|
if (!next_parameter) {
|
||||||
|
return ResultCode(ErrorDescription::NoData, ErrorModule::Applet, ErrorSummary::InvalidState,
|
||||||
|
ErrorLevel::Status);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (next_parameter->destination_id != app_id) {
|
||||||
|
return ResultCode(ErrorDescription::NotFound, ErrorModule::Applet, ErrorSummary::NotFound,
|
||||||
|
ErrorLevel::Status);
|
||||||
|
}
|
||||||
|
|
||||||
|
MessageParameter parameter = *next_parameter;
|
||||||
|
|
||||||
|
// Note: The NS module always clears the DSPSleep and DSPWakeup signals even in GlanceParameter.
|
||||||
|
if (next_parameter->signal == SignalType::DspSleep ||
|
||||||
|
next_parameter->signal == SignalType::DspWakeup)
|
||||||
|
next_parameter = boost::none;
|
||||||
|
|
||||||
|
return MakeResult<MessageParameter>(parameter);
|
||||||
|
}
|
||||||
|
|
||||||
|
ResultVal<MessageParameter> AppletManager::ReceiveParameter(AppletId app_id) {
|
||||||
|
auto result = GlanceParameter(app_id);
|
||||||
|
if (result.Succeeded()) {
|
||||||
|
// Clear the parameter
|
||||||
|
next_parameter = boost::none;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AppletManager::CancelParameter(bool check_sender, AppletId sender_appid, bool check_receiver,
|
||||||
|
AppletId receiver_appid) {
|
||||||
|
bool cancellation_success = true;
|
||||||
|
|
||||||
|
if (!next_parameter) {
|
||||||
|
cancellation_success = false;
|
||||||
|
} else {
|
||||||
|
if (check_sender && next_parameter->sender_id != sender_appid)
|
||||||
|
cancellation_success = false;
|
||||||
|
|
||||||
|
if (check_receiver && next_parameter->destination_id != receiver_appid)
|
||||||
|
cancellation_success = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cancellation_success)
|
||||||
|
next_parameter = boost::none;
|
||||||
|
|
||||||
|
return cancellation_success;
|
||||||
|
}
|
||||||
|
|
||||||
|
ResultVal<AppletManager::InitializeResult> AppletManager::Initialize(AppletId app_id,
|
||||||
|
AppletAttributes attributes) {
|
||||||
|
auto* const slot_data = GetAppletSlotData(attributes);
|
||||||
|
|
||||||
|
// Note: The real NS service does not check if the attributes value is valid before accessing
|
||||||
|
// the data in the array
|
||||||
|
ASSERT_MSG(slot_data, "Invalid application attributes");
|
||||||
|
|
||||||
|
if (slot_data->registered) {
|
||||||
|
return ResultCode(ErrorDescription::AlreadyExists, ErrorModule::Applet,
|
||||||
|
ErrorSummary::InvalidState, ErrorLevel::Status);
|
||||||
|
}
|
||||||
|
|
||||||
|
slot_data->applet_id = static_cast<AppletId>(app_id);
|
||||||
|
slot_data->attributes.raw = attributes.raw;
|
||||||
|
|
||||||
|
if (slot_data->applet_id == AppletId::Application ||
|
||||||
|
slot_data->applet_id == AppletId::HomeMenu) {
|
||||||
|
// Initialize the APT parameter to wake up the application.
|
||||||
|
next_parameter.emplace();
|
||||||
|
next_parameter->signal = SignalType::Wakeup;
|
||||||
|
next_parameter->sender_id = AppletId::None;
|
||||||
|
next_parameter->destination_id = app_id;
|
||||||
|
// Not signaling the parameter event will cause the application (or Home Menu) to hang
|
||||||
|
// during startup. In the real console, it is usually the Kernel and Home Menu who cause NS
|
||||||
|
// to signal the HomeMenu and Application parameter events, respectively.
|
||||||
|
slot_data->parameter_event->Signal();
|
||||||
|
}
|
||||||
|
|
||||||
|
return MakeResult<InitializeResult>(
|
||||||
|
{slot_data->notification_event, slot_data->parameter_event});
|
||||||
|
}
|
||||||
|
|
||||||
|
ResultCode AppletManager::Enable(AppletAttributes attributes) {
|
||||||
|
auto* const slot_data = GetAppletSlotData(attributes);
|
||||||
|
|
||||||
|
if (!slot_data) {
|
||||||
|
return ResultCode(ErrCodes::InvalidAppletSlot, ErrorModule::Applet,
|
||||||
|
ErrorSummary::InvalidState, ErrorLevel::Status);
|
||||||
|
}
|
||||||
|
|
||||||
|
slot_data->registered = true;
|
||||||
|
|
||||||
|
return RESULT_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AppletManager::IsRegistered(AppletId app_id) {
|
||||||
|
const auto* slot_data = GetAppletSlotData(app_id);
|
||||||
|
|
||||||
|
// Check if an LLE applet was registered first, then fallback to HLE applets
|
||||||
|
bool is_registered = slot_data && slot_data->registered;
|
||||||
|
|
||||||
|
if (!is_registered) {
|
||||||
|
if (app_id == AppletId::AnyLibraryApplet) {
|
||||||
|
is_registered = HLE::Applets::IsLibraryAppletRunning();
|
||||||
|
} else if (auto applet = HLE::Applets::Applet::Get(app_id)) {
|
||||||
|
// The applet exists, set it as registered.
|
||||||
|
is_registered = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return is_registered;
|
||||||
|
}
|
||||||
|
|
||||||
|
ResultCode AppletManager::PrepareToStartLibraryApplet(AppletId applet_id) {
|
||||||
|
// The real APT service returns an error if there's a pending APT parameter when this function
|
||||||
|
// is called.
|
||||||
|
if (next_parameter) {
|
||||||
|
return ResultCode(ErrCodes::ParameterPresent, ErrorModule::Applet,
|
||||||
|
ErrorSummary::InvalidState, ErrorLevel::Status);
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto& slot = applet_slots[static_cast<size_t>(AppletSlot::LibraryApplet)];
|
||||||
|
|
||||||
|
if (slot.registered) {
|
||||||
|
return ResultCode(ErrorDescription::AlreadyExists, ErrorModule::Applet,
|
||||||
|
ErrorSummary::InvalidState, ErrorLevel::Status);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto process = NS::LaunchTitle(FS::MediaType::NAND, GetTitleIdForApplet(applet_id));
|
||||||
|
if (process) {
|
||||||
|
return RESULT_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we weren't able to load the native applet title, try to fallback to an HLE implementation.
|
||||||
|
auto applet = HLE::Applets::Applet::Get(applet_id);
|
||||||
|
if (applet) {
|
||||||
|
LOG_WARNING(Service_APT, "applet has already been started id=%08X",
|
||||||
|
static_cast<u32>(applet_id));
|
||||||
|
return RESULT_SUCCESS;
|
||||||
|
} else {
|
||||||
|
return HLE::Applets::Applet::Create(applet_id, shared_from_this());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ResultCode AppletManager::PreloadLibraryApplet(AppletId applet_id) {
|
||||||
|
const auto& slot = applet_slots[static_cast<size_t>(AppletSlot::LibraryApplet)];
|
||||||
|
|
||||||
|
if (slot.registered) {
|
||||||
|
return ResultCode(ErrorDescription::AlreadyExists, ErrorModule::Applet,
|
||||||
|
ErrorSummary::InvalidState, ErrorLevel::Status);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto process = NS::LaunchTitle(FS::MediaType::NAND, GetTitleIdForApplet(applet_id));
|
||||||
|
if (process) {
|
||||||
|
return RESULT_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we weren't able to load the native applet title, try to fallback to an HLE implementation.
|
||||||
|
auto applet = HLE::Applets::Applet::Get(applet_id);
|
||||||
|
if (applet) {
|
||||||
|
LOG_WARNING(Service_APT, "applet has already been started id=%08X",
|
||||||
|
static_cast<u32>(applet_id));
|
||||||
|
return RESULT_SUCCESS;
|
||||||
|
} else {
|
||||||
|
return HLE::Applets::Applet::Create(applet_id, shared_from_this());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ResultCode AppletManager::FinishPreloadingLibraryApplet(AppletId applet_id) {
|
||||||
|
// TODO(Subv): This function should fail depending on the applet preparation state.
|
||||||
|
auto& slot = applet_slots[static_cast<size_t>(AppletSlot::LibraryApplet)];
|
||||||
|
slot.loaded = true;
|
||||||
|
return RESULT_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
ResultCode AppletManager::StartLibraryApplet(AppletId applet_id,
|
||||||
|
Kernel::SharedPtr<Kernel::Object> object,
|
||||||
|
const std::vector<u8>& buffer) {
|
||||||
|
MessageParameter param;
|
||||||
|
param.destination_id = applet_id;
|
||||||
|
param.sender_id = AppletId::Application;
|
||||||
|
param.object = object;
|
||||||
|
param.signal = SignalType::Wakeup;
|
||||||
|
param.buffer = buffer;
|
||||||
|
CancelAndSendParameter(param);
|
||||||
|
|
||||||
|
// In case the applet is being HLEd, attempt to communicate with it.
|
||||||
|
if (auto applet = HLE::Applets::Applet::Get(applet_id)) {
|
||||||
|
AppletStartupParameter parameter;
|
||||||
|
parameter.object = object;
|
||||||
|
parameter.buffer = buffer;
|
||||||
|
return applet->Start(parameter);
|
||||||
|
} else {
|
||||||
|
return RESULT_SUCCESS;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ResultVal<AppletManager::AppletInfo> AppletManager::GetAppletInfo(AppletId app_id) {
|
||||||
|
const auto* slot = GetAppletSlotData(app_id);
|
||||||
|
|
||||||
|
if (slot == nullptr || !slot->registered) {
|
||||||
|
// See if there's an HLE applet and try to use it before erroring out.
|
||||||
|
auto hle_applet = HLE::Applets::Applet::Get(app_id);
|
||||||
|
if (hle_applet == nullptr) {
|
||||||
|
return ResultCode(ErrorDescription::NotFound, ErrorModule::Applet,
|
||||||
|
ErrorSummary::NotFound, ErrorLevel::Status);
|
||||||
|
}
|
||||||
|
LOG_WARNING(Service_APT, "Using HLE applet info for applet %03X", static_cast<u32>(app_id));
|
||||||
|
// TODO(Subv): Get the title id for the current applet and write it in the response[2-3]
|
||||||
|
return MakeResult<AppletInfo>({0, Service::FS::MediaType::NAND, true, true, 0});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (app_id == AppletId::Application) {
|
||||||
|
// TODO(Subv): Implement this once Application launching is implemented
|
||||||
|
LOG_ERROR(Service_APT, "Unimplemented GetAppletInfo(Application)");
|
||||||
|
return ResultCode(ErrorDescription::NotFound, ErrorModule::Applet, ErrorSummary::NotFound,
|
||||||
|
ErrorLevel::Status);
|
||||||
|
}
|
||||||
|
|
||||||
|
return MakeResult<AppletInfo>({GetTitleIdForApplet(app_id), Service::FS::MediaType::NAND,
|
||||||
|
slot->registered, slot->loaded, slot->attributes.raw});
|
||||||
|
}
|
||||||
|
|
||||||
|
AppletManager::AppletManager() {
|
||||||
|
for (size_t slot = 0; slot < applet_slots.size(); ++slot) {
|
||||||
|
auto& slot_data = applet_slots[slot];
|
||||||
|
slot_data.slot = static_cast<AppletSlot>(slot);
|
||||||
|
slot_data.applet_id = AppletId::None;
|
||||||
|
slot_data.attributes.raw = 0;
|
||||||
|
slot_data.registered = false;
|
||||||
|
slot_data.loaded = false;
|
||||||
|
slot_data.notification_event =
|
||||||
|
Kernel::Event::Create(Kernel::ResetType::OneShot, "APT:Notification");
|
||||||
|
slot_data.parameter_event =
|
||||||
|
Kernel::Event::Create(Kernel::ResetType::OneShot, "APT:Parameter");
|
||||||
|
}
|
||||||
|
HLE::Applets::Init();
|
||||||
|
}
|
||||||
|
|
||||||
|
AppletManager::~AppletManager() {
|
||||||
|
HLE::Applets::Shutdown();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace APT
|
||||||
|
} // namespace Service
|
|
@ -0,0 +1,178 @@
|
||||||
|
// Copyright 2018 Citra Emulator Project
|
||||||
|
// Licensed under GPLv2 or any later version
|
||||||
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <array>
|
||||||
|
#include <vector>
|
||||||
|
#include <boost/optional.hpp>
|
||||||
|
#include "core/hle/kernel/event.h"
|
||||||
|
#include "core/hle/result.h"
|
||||||
|
#include "core/hle/service/fs/archive.h"
|
||||||
|
|
||||||
|
namespace Service {
|
||||||
|
namespace APT {
|
||||||
|
|
||||||
|
/// Signals used by APT functions
|
||||||
|
enum class SignalType : u32 {
|
||||||
|
None = 0x0,
|
||||||
|
Wakeup = 0x1,
|
||||||
|
Request = 0x2,
|
||||||
|
Response = 0x3,
|
||||||
|
Exit = 0x4,
|
||||||
|
Message = 0x5,
|
||||||
|
HomeButtonSingle = 0x6,
|
||||||
|
HomeButtonDouble = 0x7,
|
||||||
|
DspSleep = 0x8,
|
||||||
|
DspWakeup = 0x9,
|
||||||
|
WakeupByExit = 0xA,
|
||||||
|
WakeupByPause = 0xB,
|
||||||
|
WakeupByCancel = 0xC,
|
||||||
|
WakeupByCancelAll = 0xD,
|
||||||
|
WakeupByPowerButtonClick = 0xE,
|
||||||
|
WakeupToJumpHome = 0xF,
|
||||||
|
RequestForSysApplet = 0x10,
|
||||||
|
WakeupToLaunchApplication = 0x11,
|
||||||
|
};
|
||||||
|
|
||||||
|
/// App Id's used by APT functions
|
||||||
|
enum class AppletId : u32 {
|
||||||
|
None = 0,
|
||||||
|
AnySystemApplet = 0x100,
|
||||||
|
HomeMenu = 0x101,
|
||||||
|
AlternateMenu = 0x103,
|
||||||
|
Camera = 0x110,
|
||||||
|
FriendList = 0x112,
|
||||||
|
GameNotes = 0x113,
|
||||||
|
InternetBrowser = 0x114,
|
||||||
|
InstructionManual = 0x115,
|
||||||
|
Notifications = 0x116,
|
||||||
|
Miiverse = 0x117,
|
||||||
|
MiiversePost = 0x118,
|
||||||
|
AmiiboSettings = 0x119,
|
||||||
|
AnySysLibraryApplet = 0x200,
|
||||||
|
SoftwareKeyboard1 = 0x201,
|
||||||
|
Ed1 = 0x202,
|
||||||
|
PnoteApp = 0x204,
|
||||||
|
SnoteApp = 0x205,
|
||||||
|
Error = 0x206,
|
||||||
|
Mint = 0x207,
|
||||||
|
Extrapad = 0x208,
|
||||||
|
Memolib = 0x209,
|
||||||
|
Application = 0x300,
|
||||||
|
Tiger = 0x301,
|
||||||
|
AnyLibraryApplet = 0x400,
|
||||||
|
SoftwareKeyboard2 = 0x401,
|
||||||
|
Ed2 = 0x402,
|
||||||
|
PnoteApp2 = 0x404,
|
||||||
|
SnoteApp2 = 0x405,
|
||||||
|
Error2 = 0x406,
|
||||||
|
Mint2 = 0x407,
|
||||||
|
Extrapad2 = 0x408,
|
||||||
|
Memolib2 = 0x409,
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Holds information about the parameters used in Send/Glance/ReceiveParameter
|
||||||
|
struct MessageParameter {
|
||||||
|
AppletId sender_id = AppletId::None;
|
||||||
|
AppletId destination_id = AppletId::None;
|
||||||
|
SignalType signal = SignalType::None;
|
||||||
|
Kernel::SharedPtr<Kernel::Object> object = nullptr;
|
||||||
|
std::vector<u8> buffer;
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Holds information about the parameters used in StartLibraryApplet
|
||||||
|
struct AppletStartupParameter {
|
||||||
|
Kernel::SharedPtr<Kernel::Object> object = nullptr;
|
||||||
|
std::vector<u8> buffer;
|
||||||
|
};
|
||||||
|
|
||||||
|
union AppletAttributes {
|
||||||
|
u32 raw;
|
||||||
|
|
||||||
|
BitField<0, 3, u32> applet_pos;
|
||||||
|
BitField<29, 1, u32> is_home_menu;
|
||||||
|
|
||||||
|
AppletAttributes() : raw(0) {}
|
||||||
|
AppletAttributes(u32 attributes) : raw(attributes) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
class AppletManager : public std::enable_shared_from_this<AppletManager> {
|
||||||
|
public:
|
||||||
|
AppletManager();
|
||||||
|
~AppletManager();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clears any existing parameter and places a new one. This function is currently only used by
|
||||||
|
* HLE Applets and should be likely removed in the future
|
||||||
|
*/
|
||||||
|
void CancelAndSendParameter(const MessageParameter& parameter);
|
||||||
|
|
||||||
|
ResultCode SendParameter(const MessageParameter& parameter);
|
||||||
|
ResultVal<MessageParameter> GlanceParameter(AppletId app_id);
|
||||||
|
ResultVal<MessageParameter> ReceiveParameter(AppletId app_id);
|
||||||
|
bool CancelParameter(bool check_sender, AppletId sender_appid, bool check_receiver,
|
||||||
|
AppletId receiver_appid);
|
||||||
|
|
||||||
|
struct InitializeResult {
|
||||||
|
Kernel::SharedPtr<Kernel::Event> notification_event;
|
||||||
|
Kernel::SharedPtr<Kernel::Event> parameter_event;
|
||||||
|
};
|
||||||
|
|
||||||
|
ResultVal<InitializeResult> Initialize(AppletId app_id, AppletAttributes attributes);
|
||||||
|
ResultCode Enable(AppletAttributes attributes);
|
||||||
|
bool IsRegistered(AppletId app_id);
|
||||||
|
ResultCode PrepareToStartLibraryApplet(AppletId applet_id);
|
||||||
|
ResultCode PreloadLibraryApplet(AppletId applet_id);
|
||||||
|
ResultCode FinishPreloadingLibraryApplet(AppletId applet_id);
|
||||||
|
ResultCode StartLibraryApplet(AppletId applet_id, Kernel::SharedPtr<Kernel::Object> object,
|
||||||
|
const std::vector<u8>& buffer);
|
||||||
|
|
||||||
|
struct AppletInfo {
|
||||||
|
u64 title_id;
|
||||||
|
Service::FS::MediaType media_type;
|
||||||
|
bool registered;
|
||||||
|
bool loaded;
|
||||||
|
u32 attributes;
|
||||||
|
};
|
||||||
|
|
||||||
|
ResultVal<AppletInfo> GetAppletInfo(AppletId app_id);
|
||||||
|
|
||||||
|
private:
|
||||||
|
/// Parameter data to be returned in the next call to Glance/ReceiveParameter.
|
||||||
|
/// TODO(Subv): Use std::optional once we migrate to C++17.
|
||||||
|
boost::optional<MessageParameter> next_parameter;
|
||||||
|
|
||||||
|
static constexpr size_t NumAppletSlot = 4;
|
||||||
|
|
||||||
|
enum class AppletSlot : u8 {
|
||||||
|
Application,
|
||||||
|
SystemApplet,
|
||||||
|
HomeMenu,
|
||||||
|
LibraryApplet,
|
||||||
|
|
||||||
|
// An invalid tag
|
||||||
|
Error,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct AppletSlotData {
|
||||||
|
AppletId applet_id;
|
||||||
|
AppletSlot slot;
|
||||||
|
bool registered;
|
||||||
|
bool loaded;
|
||||||
|
AppletAttributes attributes;
|
||||||
|
Kernel::SharedPtr<Kernel::Event> notification_event;
|
||||||
|
Kernel::SharedPtr<Kernel::Event> parameter_event;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Holds data about the concurrently running applets in the system.
|
||||||
|
std::array<AppletSlotData, NumAppletSlot> applet_slots = {};
|
||||||
|
|
||||||
|
// This overload returns nullptr if no applet with the specified id has been started.
|
||||||
|
AppletSlotData* GetAppletSlotData(AppletId id);
|
||||||
|
AppletSlotData* GetAppletSlotData(AppletAttributes attributes);
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace APT
|
||||||
|
} // namespace Service
|
|
@ -2,18 +2,17 @@
|
||||||
// Licensed under GPLv2 or any later version
|
// Licensed under GPLv2 or any later version
|
||||||
// Refer to the license.txt file included.
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
#include <boost/optional.hpp>
|
|
||||||
#include "common/common_paths.h"
|
#include "common/common_paths.h"
|
||||||
#include "common/file_util.h"
|
#include "common/file_util.h"
|
||||||
#include "common/logging/log.h"
|
#include "common/logging/log.h"
|
||||||
#include "core/core.h"
|
#include "core/core.h"
|
||||||
#include "core/file_sys/file_backend.h"
|
#include "core/file_sys/file_backend.h"
|
||||||
#include "core/hle/applets/applet.h"
|
#include "core/hle/applets/applet.h"
|
||||||
#include "core/hle/kernel/event.h"
|
|
||||||
#include "core/hle/kernel/mutex.h"
|
#include "core/hle/kernel/mutex.h"
|
||||||
#include "core/hle/kernel/process.h"
|
#include "core/hle/kernel/process.h"
|
||||||
#include "core/hle/kernel/shared_memory.h"
|
#include "core/hle/kernel/shared_memory.h"
|
||||||
#include "core/hle/romfs.h"
|
#include "core/hle/romfs.h"
|
||||||
|
#include "core/hle/service/apt/applet_manager.h"
|
||||||
#include "core/hle/service/apt/apt.h"
|
#include "core/hle/service/apt/apt.h"
|
||||||
#include "core/hle/service/apt/apt_a.h"
|
#include "core/hle/service/apt/apt_a.h"
|
||||||
#include "core/hle/service/apt/apt_s.h"
|
#include "core/hle/service/apt/apt_s.h"
|
||||||
|
@ -21,7 +20,6 @@
|
||||||
#include "core/hle/service/apt/bcfnt/bcfnt.h"
|
#include "core/hle/service/apt/bcfnt/bcfnt.h"
|
||||||
#include "core/hle/service/cfg/cfg.h"
|
#include "core/hle/service/cfg/cfg.h"
|
||||||
#include "core/hle/service/fs/archive.h"
|
#include "core/hle/service/fs/archive.h"
|
||||||
#include "core/hle/service/ns/ns.h"
|
|
||||||
#include "core/hle/service/ptm/ptm.h"
|
#include "core/hle/service/ptm/ptm.h"
|
||||||
#include "core/hle/service/service.h"
|
#include "core/hle/service/service.h"
|
||||||
#include "core/hw/aes/ccm.h"
|
#include "core/hw/aes/ccm.h"
|
||||||
|
@ -44,251 +42,26 @@ static u8 unknown_ns_state_field;
|
||||||
|
|
||||||
static ScreencapPostPermission screen_capture_post_permission;
|
static ScreencapPostPermission screen_capture_post_permission;
|
||||||
|
|
||||||
/// Parameter data to be returned in the next call to Glance/ReceiveParameter.
|
static std::shared_ptr<AppletManager> applet_manager;
|
||||||
/// TODO(Subv): Use std::optional once we migrate to C++17.
|
|
||||||
static boost::optional<MessageParameter> next_parameter;
|
|
||||||
|
|
||||||
enum class AppletPos { Application = 0, Library = 1, System = 2, SysLibrary = 3, Resident = 4 };
|
|
||||||
|
|
||||||
static constexpr size_t NumAppletSlot = 4;
|
|
||||||
|
|
||||||
enum class AppletSlot : u8 {
|
|
||||||
Application,
|
|
||||||
SystemApplet,
|
|
||||||
HomeMenu,
|
|
||||||
LibraryApplet,
|
|
||||||
|
|
||||||
// An invalid tag
|
|
||||||
Error,
|
|
||||||
};
|
|
||||||
|
|
||||||
union AppletAttributes {
|
|
||||||
u32 raw;
|
|
||||||
|
|
||||||
BitField<0, 3, u32> applet_pos;
|
|
||||||
BitField<29, 1, u32> is_home_menu;
|
|
||||||
|
|
||||||
AppletAttributes() : raw(0) {}
|
|
||||||
AppletAttributes(u32 attributes) : raw(attributes) {}
|
|
||||||
};
|
|
||||||
|
|
||||||
struct AppletSlotData {
|
|
||||||
AppletId applet_id;
|
|
||||||
AppletSlot slot;
|
|
||||||
bool registered;
|
|
||||||
bool loaded;
|
|
||||||
AppletAttributes attributes;
|
|
||||||
Kernel::SharedPtr<Kernel::Event> notification_event;
|
|
||||||
Kernel::SharedPtr<Kernel::Event> parameter_event;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Holds data about the concurrently running applets in the system.
|
|
||||||
static std::array<AppletSlotData, NumAppletSlot> applet_slots = {};
|
|
||||||
|
|
||||||
struct AppletTitleData {
|
|
||||||
// There are two possible applet ids for each applet.
|
|
||||||
std::array<AppletId, 2> applet_ids;
|
|
||||||
|
|
||||||
// There's a specific TitleId per region for each applet.
|
|
||||||
static constexpr size_t NumRegions = 7;
|
|
||||||
std::array<u64, NumRegions> title_ids;
|
|
||||||
};
|
|
||||||
|
|
||||||
static constexpr size_t NumApplets = 29;
|
|
||||||
static constexpr std::array<AppletTitleData, NumApplets> applet_titleids = {{
|
|
||||||
{AppletId::HomeMenu, AppletId::None, 0x4003000008202, 0x4003000008F02, 0x4003000009802,
|
|
||||||
0x4003000008202, 0x400300000A102, 0x400300000A902, 0x400300000B102},
|
|
||||||
{AppletId::AlternateMenu, AppletId::None, 0x4003000008102, 0x4003000008102, 0x4003000008102,
|
|
||||||
0x4003000008102, 0x4003000008102, 0x4003000008102, 0x4003000008102},
|
|
||||||
{AppletId::Camera, AppletId::None, 0x4003000008402, 0x4003000009002, 0x4003000009902,
|
|
||||||
0x4003000008402, 0x400300000A202, 0x400300000AA02, 0x400300000B202},
|
|
||||||
{AppletId::FriendList, AppletId::None, 0x4003000008D02, 0x4003000009602, 0x4003000009F02,
|
|
||||||
0x4003000008D02, 0x400300000A702, 0x400300000AF02, 0x400300000B702},
|
|
||||||
{AppletId::GameNotes, AppletId::None, 0x4003000008702, 0x4003000009302, 0x4003000009C02,
|
|
||||||
0x4003000008702, 0x400300000A502, 0x400300000AD02, 0x400300000B502},
|
|
||||||
{AppletId::InternetBrowser, AppletId::None, 0x4003000008802, 0x4003000009402, 0x4003000009D02,
|
|
||||||
0x4003000008802, 0x400300000A602, 0x400300000AE02, 0x400300000B602},
|
|
||||||
{AppletId::InstructionManual, AppletId::None, 0x4003000008602, 0x4003000009202, 0x4003000009B02,
|
|
||||||
0x4003000008602, 0x400300000A402, 0x400300000AC02, 0x400300000B402},
|
|
||||||
{AppletId::Notifications, AppletId::None, 0x4003000008E02, 0x4003000009702, 0x400300000A002,
|
|
||||||
0x4003000008E02, 0x400300000A802, 0x400300000B002, 0x400300000B802},
|
|
||||||
{AppletId::Miiverse, AppletId::None, 0x400300000BC02, 0x400300000BD02, 0x400300000BE02,
|
|
||||||
0x400300000BC02, 0x4003000009E02, 0x4003000009502, 0x400300000B902},
|
|
||||||
// These values obtained from an older NS dump firmware 4.5
|
|
||||||
{AppletId::MiiversePost, AppletId::None, 0x400300000BA02, 0x400300000BA02, 0x400300000BA02,
|
|
||||||
0x400300000BA02, 0x400300000BA02, 0x400300000BA02, 0x400300000BA02},
|
|
||||||
// {AppletId::MiiversePost, AppletId::None, 0x4003000008302, 0x4003000008B02, 0x400300000BA02,
|
|
||||||
// 0x4003000008302, 0x0, 0x0, 0x0},
|
|
||||||
{AppletId::AmiiboSettings, AppletId::None, 0x4003000009502, 0x4003000009E02, 0x400300000B902,
|
|
||||||
0x4003000009502, 0x0, 0x4003000008C02, 0x400300000BF02},
|
|
||||||
{AppletId::SoftwareKeyboard1, AppletId::SoftwareKeyboard2, 0x400300000C002, 0x400300000C802,
|
|
||||||
0x400300000D002, 0x400300000C002, 0x400300000D802, 0x400300000DE02, 0x400300000E402},
|
|
||||||
{AppletId::Ed1, AppletId::Ed2, 0x400300000C102, 0x400300000C902, 0x400300000D102,
|
|
||||||
0x400300000C102, 0x400300000D902, 0x400300000DF02, 0x400300000E502},
|
|
||||||
{AppletId::PnoteApp, AppletId::PnoteApp2, 0x400300000C302, 0x400300000CB02, 0x400300000D302,
|
|
||||||
0x400300000C302, 0x400300000DB02, 0x400300000E102, 0x400300000E702},
|
|
||||||
{AppletId::SnoteApp, AppletId::SnoteApp2, 0x400300000C402, 0x400300000CC02, 0x400300000D402,
|
|
||||||
0x400300000C402, 0x400300000DC02, 0x400300000E202, 0x400300000E802},
|
|
||||||
{AppletId::Error, AppletId::Error2, 0x400300000C502, 0x400300000C502, 0x400300000C502,
|
|
||||||
0x400300000C502, 0x400300000CF02, 0x400300000CF02, 0x400300000CF02},
|
|
||||||
{AppletId::Mint, AppletId::Mint2, 0x400300000C602, 0x400300000CE02, 0x400300000D602,
|
|
||||||
0x400300000C602, 0x400300000DD02, 0x400300000E302, 0x400300000E902},
|
|
||||||
{AppletId::Extrapad, AppletId::Extrapad2, 0x400300000CD02, 0x400300000CD02, 0x400300000CD02,
|
|
||||||
0x400300000CD02, 0x400300000D502, 0x400300000D502, 0x400300000D502},
|
|
||||||
{AppletId::Memolib, AppletId::Memolib2, 0x400300000F602, 0x400300000F602, 0x400300000F602,
|
|
||||||
0x400300000F602, 0x400300000F602, 0x400300000F602, 0x400300000F602},
|
|
||||||
// TODO(Subv): Fill in the rest of the titleids
|
|
||||||
}};
|
|
||||||
|
|
||||||
static u64 GetTitleIdForApplet(AppletId id) {
|
|
||||||
ASSERT_MSG(id != AppletId::None, "Invalid applet id");
|
|
||||||
|
|
||||||
auto itr = std::find_if(applet_titleids.begin(), applet_titleids.end(),
|
|
||||||
[id](const AppletTitleData& data) {
|
|
||||||
return data.applet_ids[0] == id || data.applet_ids[1] == id;
|
|
||||||
});
|
|
||||||
|
|
||||||
ASSERT_MSG(itr != applet_titleids.end(), "Unknown applet id 0x%03X", static_cast<u32>(id));
|
|
||||||
|
|
||||||
return itr->title_ids[CFG::GetRegionValue()];
|
|
||||||
}
|
|
||||||
|
|
||||||
// This overload returns nullptr if no applet with the specified id has been started.
|
|
||||||
static AppletSlotData* GetAppletSlotData(AppletId id) {
|
|
||||||
auto GetSlot = [](AppletSlot slot) -> AppletSlotData* {
|
|
||||||
return &applet_slots[static_cast<size_t>(slot)];
|
|
||||||
};
|
|
||||||
|
|
||||||
if (id == AppletId::Application) {
|
|
||||||
auto* slot = GetSlot(AppletSlot::Application);
|
|
||||||
if (slot->applet_id != AppletId::None)
|
|
||||||
return slot;
|
|
||||||
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (id == AppletId::AnySystemApplet) {
|
|
||||||
auto* system_slot = GetSlot(AppletSlot::SystemApplet);
|
|
||||||
if (system_slot->applet_id != AppletId::None)
|
|
||||||
return system_slot;
|
|
||||||
|
|
||||||
// The Home Menu is also a system applet, but it lives in its own slot to be able to run
|
|
||||||
// concurrently with other system applets.
|
|
||||||
auto* home_slot = GetSlot(AppletSlot::HomeMenu);
|
|
||||||
if (home_slot->applet_id != AppletId::None)
|
|
||||||
return home_slot;
|
|
||||||
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (id == AppletId::AnyLibraryApplet || id == AppletId::AnySysLibraryApplet) {
|
|
||||||
auto* slot = GetSlot(AppletSlot::LibraryApplet);
|
|
||||||
if (slot->applet_id == AppletId::None)
|
|
||||||
return nullptr;
|
|
||||||
|
|
||||||
u32 applet_pos = slot->attributes.applet_pos;
|
|
||||||
|
|
||||||
if (id == AppletId::AnyLibraryApplet && applet_pos == static_cast<u32>(AppletPos::Library))
|
|
||||||
return slot;
|
|
||||||
|
|
||||||
if (id == AppletId::AnySysLibraryApplet &&
|
|
||||||
applet_pos == static_cast<u32>(AppletPos::SysLibrary))
|
|
||||||
return slot;
|
|
||||||
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (id == AppletId::HomeMenu || id == AppletId::AlternateMenu) {
|
|
||||||
auto* slot = GetSlot(AppletSlot::HomeMenu);
|
|
||||||
if (slot->applet_id != AppletId::None)
|
|
||||||
return slot;
|
|
||||||
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (auto& slot : applet_slots) {
|
|
||||||
if (slot.applet_id == id)
|
|
||||||
return &slot;
|
|
||||||
}
|
|
||||||
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
static AppletSlotData* GetAppletSlotData(AppletAttributes attributes) {
|
|
||||||
// Mapping from AppletPos to AppletSlot
|
|
||||||
static constexpr std::array<AppletSlot, 6> applet_position_slots = {
|
|
||||||
AppletSlot::Application, AppletSlot::LibraryApplet, AppletSlot::SystemApplet,
|
|
||||||
AppletSlot::LibraryApplet, AppletSlot::Error, AppletSlot::LibraryApplet};
|
|
||||||
|
|
||||||
u32 applet_pos = attributes.applet_pos;
|
|
||||||
if (applet_pos >= applet_position_slots.size())
|
|
||||||
return nullptr;
|
|
||||||
|
|
||||||
AppletSlot slot = applet_position_slots[applet_pos];
|
|
||||||
|
|
||||||
if (slot == AppletSlot::Error)
|
|
||||||
return nullptr;
|
|
||||||
|
|
||||||
// The Home Menu is a system applet, however, it has its own applet slot so that it can run
|
|
||||||
// concurrently with other system applets.
|
|
||||||
if (slot == AppletSlot::SystemApplet && attributes.is_home_menu)
|
|
||||||
return &applet_slots[static_cast<size_t>(AppletSlot::HomeMenu)];
|
|
||||||
|
|
||||||
return &applet_slots[static_cast<size_t>(slot)];
|
|
||||||
}
|
|
||||||
|
|
||||||
void SendParameter(const MessageParameter& parameter) {
|
|
||||||
next_parameter = parameter;
|
|
||||||
// Signal the event to let the receiver know that a new parameter is ready to be read
|
|
||||||
auto* const slot_data = GetAppletSlotData(static_cast<AppletId>(parameter.destination_id));
|
|
||||||
if (slot_data == nullptr) {
|
|
||||||
LOG_DEBUG(Service_APT, "No applet was registered with the id %03X",
|
|
||||||
parameter.destination_id);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
slot_data->parameter_event->Signal();
|
|
||||||
}
|
|
||||||
|
|
||||||
void Initialize(Service::Interface* self) {
|
void Initialize(Service::Interface* self) {
|
||||||
IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x2, 2, 0); // 0x20080
|
IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x2, 2, 0); // 0x20080
|
||||||
u32 app_id = rp.Pop<u32>();
|
AppletId app_id = rp.PopEnum<AppletId>();
|
||||||
u32 attributes = rp.Pop<u32>();
|
u32 attributes = rp.Pop<u32>();
|
||||||
|
|
||||||
LOG_DEBUG(Service_APT, "called app_id=0x%08X, attributes=0x%08X", app_id, attributes);
|
LOG_DEBUG(Service_APT, "called app_id=0x%08X, attributes=0x%08X", static_cast<u32>(app_id),
|
||||||
|
attributes);
|
||||||
|
|
||||||
auto* const slot_data = GetAppletSlotData(attributes);
|
auto result = applet_manager->Initialize(app_id, attributes);
|
||||||
|
if (result.Failed()) {
|
||||||
// Note: The real NS service does not check if the attributes value is valid before accessing
|
|
||||||
// the data in the array
|
|
||||||
ASSERT_MSG(slot_data, "Invalid application attributes");
|
|
||||||
|
|
||||||
if (slot_data->registered) {
|
|
||||||
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
|
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
|
||||||
rb.Push(ResultCode(ErrorDescription::AlreadyExists, ErrorModule::Applet,
|
rb.Push(result.Code());
|
||||||
ErrorSummary::InvalidState, ErrorLevel::Status));
|
} else {
|
||||||
return;
|
auto events = std::move(result).Unwrap();
|
||||||
}
|
IPC::RequestBuilder rb = rp.MakeBuilder(1, 3);
|
||||||
|
rb.Push(RESULT_SUCCESS);
|
||||||
slot_data->applet_id = static_cast<AppletId>(app_id);
|
rb.PushCopyHandles(Kernel::g_handle_table.Create(events.notification_event).Unwrap(),
|
||||||
slot_data->attributes.raw = attributes;
|
Kernel::g_handle_table.Create(events.parameter_event).Unwrap());
|
||||||
|
|
||||||
IPC::RequestBuilder rb = rp.MakeBuilder(1, 3);
|
|
||||||
rb.Push(RESULT_SUCCESS);
|
|
||||||
rb.PushCopyHandles(Kernel::g_handle_table.Create(slot_data->notification_event).Unwrap(),
|
|
||||||
Kernel::g_handle_table.Create(slot_data->parameter_event).Unwrap());
|
|
||||||
|
|
||||||
if (slot_data->applet_id == AppletId::Application ||
|
|
||||||
slot_data->applet_id == AppletId::HomeMenu) {
|
|
||||||
// Initialize the APT parameter to wake up the application.
|
|
||||||
next_parameter.emplace();
|
|
||||||
next_parameter->signal = static_cast<u32>(SignalType::Wakeup);
|
|
||||||
next_parameter->sender_id = static_cast<u32>(AppletId::None);
|
|
||||||
next_parameter->destination_id = app_id;
|
|
||||||
// Not signaling the parameter event will cause the application (or Home Menu) to hang
|
|
||||||
// during startup. In the real console, it is usually the Kernel and Home Menu who cause NS
|
|
||||||
// to signal the HomeMenu and Application parameter events, respectively.
|
|
||||||
slot_data->parameter_event->Signal();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -508,18 +281,7 @@ void Enable(Service::Interface* self) {
|
||||||
LOG_DEBUG(Service_APT, "called attributes=0x%08X", attributes);
|
LOG_DEBUG(Service_APT, "called attributes=0x%08X", attributes);
|
||||||
|
|
||||||
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
|
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
|
||||||
|
rb.Push(applet_manager->Enable(attributes));
|
||||||
auto* const slot_data = GetAppletSlotData(attributes);
|
|
||||||
|
|
||||||
if (!slot_data) {
|
|
||||||
rb.Push(ResultCode(ErrCodes::InvalidAppletSlot, ErrorModule::Applet,
|
|
||||||
ErrorSummary::InvalidState, ErrorLevel::Status));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
slot_data->registered = true;
|
|
||||||
|
|
||||||
rb.Push(RESULT_SUCCESS);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void GetAppletManInfo(Service::Interface* self) {
|
void GetAppletManInfo(Service::Interface* self) {
|
||||||
|
@ -540,22 +302,7 @@ void IsRegistered(Service::Interface* self) {
|
||||||
AppletId app_id = static_cast<AppletId>(rp.Pop<u32>());
|
AppletId app_id = static_cast<AppletId>(rp.Pop<u32>());
|
||||||
IPC::RequestBuilder rb = rp.MakeBuilder(2, 0);
|
IPC::RequestBuilder rb = rp.MakeBuilder(2, 0);
|
||||||
rb.Push(RESULT_SUCCESS); // No error
|
rb.Push(RESULT_SUCCESS); // No error
|
||||||
|
rb.Push(applet_manager->IsRegistered(app_id));
|
||||||
const auto* slot_data = GetAppletSlotData(app_id);
|
|
||||||
|
|
||||||
// Check if an LLE applet was registered first, then fallback to HLE applets
|
|
||||||
bool is_registered = slot_data && slot_data->registered;
|
|
||||||
|
|
||||||
if (!is_registered) {
|
|
||||||
if (app_id == AppletId::AnyLibraryApplet) {
|
|
||||||
is_registered = HLE::Applets::IsLibraryAppletRunning();
|
|
||||||
} else if (auto applet = HLE::Applets::Applet::Get(app_id)) {
|
|
||||||
// The applet exists, set it as registered.
|
|
||||||
is_registered = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
rb.Push(is_registered);
|
|
||||||
|
|
||||||
LOG_DEBUG(Service_APT, "called app_id=0x%08X", static_cast<u32>(app_id));
|
LOG_DEBUG(Service_APT, "called app_id=0x%08X", static_cast<u32>(app_id));
|
||||||
}
|
}
|
||||||
|
@ -571,9 +318,9 @@ void InquireNotification(Service::Interface* self) {
|
||||||
|
|
||||||
void SendParameter(Service::Interface* self) {
|
void SendParameter(Service::Interface* self) {
|
||||||
IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0xC, 4, 4); // 0xC0104
|
IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0xC, 4, 4); // 0xC0104
|
||||||
u32 src_app_id = rp.Pop<u32>();
|
AppletId src_app_id = rp.PopEnum<AppletId>();
|
||||||
u32 dst_app_id = rp.Pop<u32>();
|
AppletId dst_app_id = rp.PopEnum<AppletId>();
|
||||||
u32 signal_type = rp.Pop<u32>();
|
SignalType signal_type = rp.PopEnum<SignalType>();
|
||||||
u32 buffer_size = rp.Pop<u32>();
|
u32 buffer_size = rp.Pop<u32>();
|
||||||
Kernel::Handle handle = rp.PopHandle();
|
Kernel::Handle handle = rp.PopHandle();
|
||||||
size_t size;
|
size_t size;
|
||||||
|
@ -582,17 +329,11 @@ void SendParameter(Service::Interface* self) {
|
||||||
LOG_DEBUG(Service_APT,
|
LOG_DEBUG(Service_APT,
|
||||||
"called src_app_id=0x%08X, dst_app_id=0x%08X, signal_type=0x%08X,"
|
"called src_app_id=0x%08X, dst_app_id=0x%08X, signal_type=0x%08X,"
|
||||||
"buffer_size=0x%08X, handle=0x%08X, size=0x%08zX, in_param_buffer_ptr=0x%08X",
|
"buffer_size=0x%08X, handle=0x%08X, size=0x%08zX, in_param_buffer_ptr=0x%08X",
|
||||||
src_app_id, dst_app_id, signal_type, buffer_size, handle, size, buffer);
|
static_cast<u32>(src_app_id), static_cast<u32>(dst_app_id),
|
||||||
|
static_cast<u32>(signal_type), buffer_size, handle, size, buffer);
|
||||||
|
|
||||||
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
|
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
|
||||||
|
|
||||||
// A new parameter can not be sent if the previous one hasn't been consumed yet
|
|
||||||
if (next_parameter) {
|
|
||||||
rb.Push(ResultCode(ErrCodes::ParameterPresent, ErrorModule::Applet,
|
|
||||||
ErrorSummary::InvalidState, ErrorLevel::Status));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
MessageParameter param;
|
MessageParameter param;
|
||||||
param.destination_id = dst_app_id;
|
param.destination_id = dst_app_id;
|
||||||
param.sender_id = src_app_id;
|
param.sender_id = src_app_id;
|
||||||
|
@ -601,19 +342,12 @@ void SendParameter(Service::Interface* self) {
|
||||||
param.buffer.resize(buffer_size);
|
param.buffer.resize(buffer_size);
|
||||||
Memory::ReadBlock(buffer, param.buffer.data(), param.buffer.size());
|
Memory::ReadBlock(buffer, param.buffer.data(), param.buffer.size());
|
||||||
|
|
||||||
SendParameter(param);
|
rb.Push(applet_manager->SendParameter(param));
|
||||||
|
|
||||||
// If the applet is running in HLE mode, use the HLE interface to communicate with it.
|
|
||||||
if (auto dest_applet = HLE::Applets::Applet::Get(static_cast<AppletId>(dst_app_id))) {
|
|
||||||
rb.Push(dest_applet->ReceiveParameter(param));
|
|
||||||
} else {
|
|
||||||
rb.Push(RESULT_SUCCESS);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ReceiveParameter(Service::Interface* self) {
|
void ReceiveParameter(Service::Interface* self) {
|
||||||
IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0xD, 2, 0); // 0xD0080
|
IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0xD, 2, 0); // 0xD0080
|
||||||
u32 app_id = rp.Pop<u32>();
|
AppletId app_id = rp.PopEnum<AppletId>();
|
||||||
u32 buffer_size = rp.Pop<u32>();
|
u32 buffer_size = rp.Pop<u32>();
|
||||||
|
|
||||||
size_t static_buff_size;
|
size_t static_buff_size;
|
||||||
|
@ -624,27 +358,22 @@ void ReceiveParameter(Service::Interface* self) {
|
||||||
"buffer_size is bigger than the size in the buffer descriptor (0x%08X > 0x%08zX)",
|
"buffer_size is bigger than the size in the buffer descriptor (0x%08X > 0x%08zX)",
|
||||||
buffer_size, static_buff_size);
|
buffer_size, static_buff_size);
|
||||||
|
|
||||||
LOG_DEBUG(Service_APT, "called app_id=0x%08X, buffer_size=0x%08X", app_id, buffer_size);
|
LOG_DEBUG(Service_APT, "called app_id=0x%08X, buffer_size=0x%08X", static_cast<u32>(app_id),
|
||||||
|
buffer_size);
|
||||||
|
|
||||||
if (!next_parameter) {
|
auto next_parameter = applet_manager->ReceiveParameter(app_id);
|
||||||
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
|
|
||||||
rb.Push(ResultCode(ErrorDescription::NoData, ErrorModule::Applet,
|
|
||||||
ErrorSummary::InvalidState, ErrorLevel::Status));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (next_parameter->destination_id != app_id) {
|
if (next_parameter.Failed()) {
|
||||||
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
|
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
|
||||||
rb.Push(ResultCode(ErrorDescription::NotFound, ErrorModule::Applet, ErrorSummary::NotFound,
|
rb.Push(next_parameter.Code());
|
||||||
ErrorLevel::Status));
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
IPC::RequestBuilder rb = rp.MakeBuilder(4, 4);
|
IPC::RequestBuilder rb = rp.MakeBuilder(4, 4);
|
||||||
|
|
||||||
rb.Push(RESULT_SUCCESS); // No error
|
rb.Push(RESULT_SUCCESS); // No error
|
||||||
rb.Push(next_parameter->sender_id);
|
rb.PushEnum(next_parameter->sender_id);
|
||||||
rb.Push(next_parameter->signal); // Signal type
|
rb.PushEnum(next_parameter->signal); // Signal type
|
||||||
ASSERT_MSG(next_parameter->buffer.size() <= buffer_size, "Input static buffer is too small !");
|
ASSERT_MSG(next_parameter->buffer.size() <= buffer_size, "Input static buffer is too small !");
|
||||||
rb.Push(static_cast<u32>(next_parameter->buffer.size())); // Parameter buffer size
|
rb.Push(static_cast<u32>(next_parameter->buffer.size())); // Parameter buffer size
|
||||||
|
|
||||||
|
@ -655,14 +384,11 @@ void ReceiveParameter(Service::Interface* self) {
|
||||||
rb.PushStaticBuffer(buffer, next_parameter->buffer.size(), 0);
|
rb.PushStaticBuffer(buffer, next_parameter->buffer.size(), 0);
|
||||||
|
|
||||||
Memory::WriteBlock(buffer, next_parameter->buffer.data(), next_parameter->buffer.size());
|
Memory::WriteBlock(buffer, next_parameter->buffer.data(), next_parameter->buffer.size());
|
||||||
|
|
||||||
// Clear the parameter
|
|
||||||
next_parameter = boost::none;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void GlanceParameter(Service::Interface* self) {
|
void GlanceParameter(Service::Interface* self) {
|
||||||
IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0xE, 2, 0); // 0xE0080
|
IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0xE, 2, 0); // 0xE0080
|
||||||
u32 app_id = rp.Pop<u32>();
|
AppletId app_id = rp.PopEnum<AppletId>();
|
||||||
u32 buffer_size = rp.Pop<u32>();
|
u32 buffer_size = rp.Pop<u32>();
|
||||||
|
|
||||||
size_t static_buff_size;
|
size_t static_buff_size;
|
||||||
|
@ -673,26 +399,21 @@ void GlanceParameter(Service::Interface* self) {
|
||||||
"buffer_size is bigger than the size in the buffer descriptor (0x%08X > 0x%08zX)",
|
"buffer_size is bigger than the size in the buffer descriptor (0x%08X > 0x%08zX)",
|
||||||
buffer_size, static_buff_size);
|
buffer_size, static_buff_size);
|
||||||
|
|
||||||
LOG_DEBUG(Service_APT, "called app_id=0x%08X, buffer_size=0x%08X", app_id, buffer_size);
|
LOG_DEBUG(Service_APT, "called app_id=0x%08X, buffer_size=0x%08X", static_cast<u32>(app_id),
|
||||||
|
buffer_size);
|
||||||
|
|
||||||
if (!next_parameter) {
|
auto next_parameter = applet_manager->GlanceParameter(app_id);
|
||||||
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
|
|
||||||
rb.Push(ResultCode(ErrorDescription::NoData, ErrorModule::Applet,
|
|
||||||
ErrorSummary::InvalidState, ErrorLevel::Status));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (next_parameter->destination_id != app_id) {
|
if (next_parameter.Failed()) {
|
||||||
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
|
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
|
||||||
rb.Push(ResultCode(ErrorDescription::NotFound, ErrorModule::Applet, ErrorSummary::NotFound,
|
rb.Push(next_parameter.Code());
|
||||||
ErrorLevel::Status));
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
IPC::RequestBuilder rb = rp.MakeBuilder(4, 4);
|
IPC::RequestBuilder rb = rp.MakeBuilder(4, 4);
|
||||||
rb.Push(RESULT_SUCCESS); // No error
|
rb.Push(RESULT_SUCCESS); // No error
|
||||||
rb.Push(next_parameter->sender_id);
|
rb.PushEnum(next_parameter->sender_id);
|
||||||
rb.Push(next_parameter->signal); // Signal type
|
rb.PushEnum(next_parameter->signal); // Signal type
|
||||||
ASSERT_MSG(next_parameter->buffer.size() <= buffer_size, "Input static buffer is too small !");
|
ASSERT_MSG(next_parameter->buffer.size() <= buffer_size, "Input static buffer is too small !");
|
||||||
rb.Push(static_cast<u32>(next_parameter->buffer.size())); // Parameter buffer size
|
rb.Push(static_cast<u32>(next_parameter->buffer.size())); // Parameter buffer size
|
||||||
|
|
||||||
|
@ -703,44 +424,26 @@ void GlanceParameter(Service::Interface* self) {
|
||||||
rb.PushStaticBuffer(buffer, next_parameter->buffer.size(), 0);
|
rb.PushStaticBuffer(buffer, next_parameter->buffer.size(), 0);
|
||||||
|
|
||||||
Memory::WriteBlock(buffer, next_parameter->buffer.data(), next_parameter->buffer.size());
|
Memory::WriteBlock(buffer, next_parameter->buffer.data(), next_parameter->buffer.size());
|
||||||
|
|
||||||
// Note: The NS module always clears the DSPSleep and DSPWakeup signals even in GlanceParameter.
|
|
||||||
if (next_parameter->signal == static_cast<u32>(SignalType::DspSleep) ||
|
|
||||||
next_parameter->signal == static_cast<u32>(SignalType::DspWakeup))
|
|
||||||
next_parameter = boost::none;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void CancelParameter(Service::Interface* self) {
|
void CancelParameter(Service::Interface* self) {
|
||||||
IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0xF, 4, 0); // 0xF0100
|
IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0xF, 4, 0); // 0xF0100
|
||||||
|
|
||||||
bool check_sender = rp.Pop<bool>();
|
bool check_sender = rp.Pop<bool>();
|
||||||
u32 sender_appid = rp.Pop<u32>();
|
AppletId sender_appid = rp.PopEnum<AppletId>();
|
||||||
bool check_receiver = rp.Pop<bool>();
|
bool check_receiver = rp.Pop<bool>();
|
||||||
u32 receiver_appid = rp.Pop<u32>();
|
AppletId receiver_appid = rp.PopEnum<AppletId>();
|
||||||
|
|
||||||
bool cancellation_success = true;
|
|
||||||
|
|
||||||
if (!next_parameter) {
|
|
||||||
cancellation_success = false;
|
|
||||||
} else {
|
|
||||||
if (check_sender && next_parameter->sender_id != sender_appid)
|
|
||||||
cancellation_success = false;
|
|
||||||
|
|
||||||
if (check_receiver && next_parameter->destination_id != receiver_appid)
|
|
||||||
cancellation_success = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (cancellation_success)
|
|
||||||
next_parameter = boost::none;
|
|
||||||
|
|
||||||
IPC::RequestBuilder rb = rp.MakeBuilder(2, 0);
|
IPC::RequestBuilder rb = rp.MakeBuilder(2, 0);
|
||||||
|
|
||||||
rb.Push(RESULT_SUCCESS); // No error
|
rb.Push(RESULT_SUCCESS); // No error
|
||||||
rb.Push(cancellation_success);
|
rb.Push(applet_manager->CancelParameter(check_sender, sender_appid, check_receiver,
|
||||||
|
receiver_appid));
|
||||||
|
|
||||||
LOG_DEBUG(Service_APT, "called check_sender=%u, sender_appid=0x%08X, "
|
LOG_DEBUG(Service_APT, "called check_sender=%u, sender_appid=0x%08X, "
|
||||||
"check_receiver=%u, receiver_appid=0x%08X",
|
"check_receiver=%u, receiver_appid=0x%08X",
|
||||||
check_sender, sender_appid, check_receiver, receiver_appid);
|
check_sender, static_cast<u32>(sender_appid), check_receiver,
|
||||||
|
static_cast<u32>(receiver_appid));
|
||||||
}
|
}
|
||||||
|
|
||||||
void PrepareToStartApplication(Service::Interface* self) {
|
void PrepareToStartApplication(Service::Interface* self) {
|
||||||
|
@ -840,38 +543,7 @@ void PrepareToStartLibraryApplet(Service::Interface* self) {
|
||||||
LOG_DEBUG(Service_APT, "called applet_id=%08X", static_cast<u32>(applet_id));
|
LOG_DEBUG(Service_APT, "called applet_id=%08X", static_cast<u32>(applet_id));
|
||||||
|
|
||||||
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
|
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
|
||||||
|
rb.Push(applet_manager->PrepareToStartLibraryApplet(applet_id));
|
||||||
// The real APT service returns an error if there's a pending APT parameter when this function
|
|
||||||
// is called.
|
|
||||||
if (next_parameter) {
|
|
||||||
rb.Push(ResultCode(ErrCodes::ParameterPresent, ErrorModule::Applet,
|
|
||||||
ErrorSummary::InvalidState, ErrorLevel::Status));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const auto& slot = applet_slots[static_cast<size_t>(AppletSlot::LibraryApplet)];
|
|
||||||
|
|
||||||
if (slot.registered) {
|
|
||||||
rb.Push(ResultCode(ErrorDescription::AlreadyExists, ErrorModule::Applet,
|
|
||||||
ErrorSummary::InvalidState, ErrorLevel::Status));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto process = NS::LaunchTitle(FS::MediaType::NAND, GetTitleIdForApplet(applet_id));
|
|
||||||
if (process) {
|
|
||||||
rb.Push(RESULT_SUCCESS);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If we weren't able to load the native applet title, try to fallback to an HLE implementation.
|
|
||||||
auto applet = HLE::Applets::Applet::Get(applet_id);
|
|
||||||
if (applet) {
|
|
||||||
LOG_WARNING(Service_APT, "applet has already been started id=%08X",
|
|
||||||
static_cast<u32>(applet_id));
|
|
||||||
rb.Push(RESULT_SUCCESS);
|
|
||||||
} else {
|
|
||||||
rb.Push(HLE::Applets::Applet::Create(applet_id));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void PrepareToStartNewestHomeMenu(Service::Interface* self) {
|
void PrepareToStartNewestHomeMenu(Service::Interface* self) {
|
||||||
|
@ -895,42 +567,15 @@ void PreloadLibraryApplet(Service::Interface* self) {
|
||||||
LOG_DEBUG(Service_APT, "called applet_id=%08X", static_cast<u32>(applet_id));
|
LOG_DEBUG(Service_APT, "called applet_id=%08X", static_cast<u32>(applet_id));
|
||||||
|
|
||||||
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
|
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
|
||||||
|
rb.Push(applet_manager->PreloadLibraryApplet(applet_id));
|
||||||
const auto& slot = applet_slots[static_cast<size_t>(AppletSlot::LibraryApplet)];
|
|
||||||
|
|
||||||
if (slot.registered) {
|
|
||||||
rb.Push(ResultCode(ErrorDescription::AlreadyExists, ErrorModule::Applet,
|
|
||||||
ErrorSummary::InvalidState, ErrorLevel::Status));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto process = NS::LaunchTitle(FS::MediaType::NAND, GetTitleIdForApplet(applet_id));
|
|
||||||
if (process) {
|
|
||||||
rb.Push(RESULT_SUCCESS);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If we weren't able to load the native applet title, try to fallback to an HLE implementation.
|
|
||||||
auto applet = HLE::Applets::Applet::Get(applet_id);
|
|
||||||
if (applet) {
|
|
||||||
LOG_WARNING(Service_APT, "applet has already been started id=%08X",
|
|
||||||
static_cast<u32>(applet_id));
|
|
||||||
rb.Push(RESULT_SUCCESS);
|
|
||||||
} else {
|
|
||||||
rb.Push(HLE::Applets::Applet::Create(applet_id));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void FinishPreloadingLibraryApplet(Service::Interface* self) {
|
void FinishPreloadingLibraryApplet(Service::Interface* self) {
|
||||||
IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x17, 1, 0); // 0x00170040
|
IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x17, 1, 0); // 0x00170040
|
||||||
AppletId applet_id = static_cast<AppletId>(rp.Pop<u32>());
|
AppletId applet_id = static_cast<AppletId>(rp.Pop<u32>());
|
||||||
|
|
||||||
// TODO(Subv): This function should fail depending on the applet preparation state.
|
|
||||||
auto& slot = applet_slots[static_cast<size_t>(AppletSlot::LibraryApplet)];
|
|
||||||
slot.loaded = true;
|
|
||||||
|
|
||||||
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
|
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
|
||||||
rb.Push(RESULT_SUCCESS);
|
rb.Push(applet_manager->FinishPreloadingLibraryApplet(applet_id));
|
||||||
|
|
||||||
LOG_WARNING(Service_APT, "(STUBBED) called applet_id=%03X", static_cast<u32>(applet_id));
|
LOG_WARNING(Service_APT, "(STUBBED) called applet_id=%03X", static_cast<u32>(applet_id));
|
||||||
}
|
}
|
||||||
|
@ -947,26 +592,11 @@ void StartLibraryApplet(Service::Interface* self) {
|
||||||
|
|
||||||
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
|
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
|
||||||
|
|
||||||
// Send the Wakeup signal to the applet
|
std::vector<u8> buffer(buffer_size);
|
||||||
MessageParameter param;
|
Memory::ReadBlock(buffer_addr, buffer.data(), buffer.size());
|
||||||
param.destination_id = static_cast<u32>(applet_id);
|
|
||||||
param.sender_id = static_cast<u32>(AppletId::Application);
|
|
||||||
param.object = Kernel::g_handle_table.GetGeneric(handle);
|
|
||||||
param.signal = static_cast<u32>(SignalType::Wakeup);
|
|
||||||
param.buffer.resize(buffer_size);
|
|
||||||
Memory::ReadBlock(buffer_addr, param.buffer.data(), param.buffer.size());
|
|
||||||
SendParameter(param);
|
|
||||||
|
|
||||||
// In case the applet is being HLEd, attempt to communicate with it.
|
rb.Push(applet_manager->StartLibraryApplet(applet_id, Kernel::g_handle_table.GetGeneric(handle),
|
||||||
if (auto applet = HLE::Applets::Applet::Get(applet_id)) {
|
buffer));
|
||||||
AppletStartupParameter parameter;
|
|
||||||
parameter.object = Kernel::g_handle_table.GetGeneric(handle);
|
|
||||||
parameter.buffer.resize(buffer_size);
|
|
||||||
Memory::ReadBlock(buffer_addr, parameter.buffer.data(), parameter.buffer.size());
|
|
||||||
rb.Push(applet->Start(parameter));
|
|
||||||
} else {
|
|
||||||
rb.Push(RESULT_SUCCESS);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void CancelLibraryApplet(Service::Interface* self) {
|
void CancelLibraryApplet(Service::Interface* self) {
|
||||||
|
@ -1006,48 +636,19 @@ void GetAppletInfo(Service::Interface* self) {
|
||||||
|
|
||||||
LOG_DEBUG(Service_APT, "called appid=%u", static_cast<u32>(app_id));
|
LOG_DEBUG(Service_APT, "called appid=%u", static_cast<u32>(app_id));
|
||||||
|
|
||||||
const auto* slot = GetAppletSlotData(app_id);
|
auto info = applet_manager->GetAppletInfo(app_id);
|
||||||
|
if (info.Failed()) {
|
||||||
if (slot == nullptr || !slot->registered) {
|
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
|
||||||
// See if there's an HLE applet and try to use it before erroring out.
|
rb.Push(info.Code());
|
||||||
auto hle_applet = HLE::Applets::Applet::Get(app_id);
|
} else {
|
||||||
if (hle_applet == nullptr) {
|
|
||||||
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
|
|
||||||
rb.Push(ResultCode(ErrorDescription::NotFound, ErrorModule::Applet,
|
|
||||||
ErrorSummary::NotFound, ErrorLevel::Status));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO(Subv): Get the title id for the current applet and write it in the response[2-3]
|
|
||||||
IPC::RequestBuilder rb = rp.MakeBuilder(7, 0);
|
IPC::RequestBuilder rb = rp.MakeBuilder(7, 0);
|
||||||
rb.Push(RESULT_SUCCESS);
|
rb.Push(RESULT_SUCCESS);
|
||||||
u64 title_id = 0;
|
rb.Push(info->title_id);
|
||||||
rb.Push(title_id);
|
rb.Push(static_cast<u8>(info->media_type));
|
||||||
rb.Push(static_cast<u32>(Service::FS::MediaType::NAND));
|
rb.Push(info->registered);
|
||||||
rb.Push(true); // Registered
|
rb.Push(info->loaded);
|
||||||
rb.Push(true); // Loaded
|
rb.Push(info->attributes);
|
||||||
rb.Push<u32>(0); // Applet Attributes
|
|
||||||
LOG_WARNING(Service_APT, "Using HLE applet info for applet %03X", static_cast<u32>(app_id));
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (app_id == AppletId::Application) {
|
|
||||||
// TODO(Subv): Implement this once Application launching is implemented
|
|
||||||
LOG_ERROR(Service_APT, "Unimplemented GetAppletInfo(Application)");
|
|
||||||
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
|
|
||||||
rb.Push(ResultCode(ErrorDescription::NotFound, ErrorModule::Applet, ErrorSummary::NotFound,
|
|
||||||
ErrorLevel::Status));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
IPC::RequestBuilder rb = rp.MakeBuilder(7, 0);
|
|
||||||
rb.Push(RESULT_SUCCESS);
|
|
||||||
rb.Push(GetTitleIdForApplet(app_id));
|
|
||||||
// Note: The NS service hardcodes this to NAND for all applets except the Application applet.
|
|
||||||
rb.Push(static_cast<u8>(Service::FS::MediaType::NAND));
|
|
||||||
rb.Push(slot->registered);
|
|
||||||
rb.Push(slot->loaded);
|
|
||||||
rb.Push(slot->attributes.raw);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void GetStartupArgument(Service::Interface* self) {
|
void GetStartupArgument(Service::Interface* self) {
|
||||||
|
@ -1213,7 +814,7 @@ void Init() {
|
||||||
AddService(new APT_S_Interface);
|
AddService(new APT_S_Interface);
|
||||||
AddService(new APT_U_Interface);
|
AddService(new APT_U_Interface);
|
||||||
|
|
||||||
HLE::Applets::Init();
|
applet_manager = std::make_shared<AppletManager>();
|
||||||
|
|
||||||
using Kernel::MemoryPermission;
|
using Kernel::MemoryPermission;
|
||||||
shared_font_mem =
|
shared_font_mem =
|
||||||
|
@ -1227,19 +828,6 @@ void Init() {
|
||||||
unknown_ns_state_field = 0;
|
unknown_ns_state_field = 0;
|
||||||
screen_capture_post_permission =
|
screen_capture_post_permission =
|
||||||
ScreencapPostPermission::CleanThePermission; // TODO(JamePeng): verify the initial value
|
ScreencapPostPermission::CleanThePermission; // TODO(JamePeng): verify the initial value
|
||||||
|
|
||||||
for (size_t slot = 0; slot < applet_slots.size(); ++slot) {
|
|
||||||
auto& slot_data = applet_slots[slot];
|
|
||||||
slot_data.slot = static_cast<AppletSlot>(slot);
|
|
||||||
slot_data.applet_id = AppletId::None;
|
|
||||||
slot_data.attributes.raw = 0;
|
|
||||||
slot_data.registered = false;
|
|
||||||
slot_data.loaded = false;
|
|
||||||
slot_data.notification_event =
|
|
||||||
Kernel::Event::Create(Kernel::ResetType::OneShot, "APT:Notification");
|
|
||||||
slot_data.parameter_event =
|
|
||||||
Kernel::Event::Create(Kernel::ResetType::OneShot, "APT:Parameter");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Shutdown() {
|
void Shutdown() {
|
||||||
|
@ -1248,18 +836,7 @@ void Shutdown() {
|
||||||
shared_font_relocated = false;
|
shared_font_relocated = false;
|
||||||
lock = nullptr;
|
lock = nullptr;
|
||||||
|
|
||||||
for (auto& slot : applet_slots) {
|
applet_manager = nullptr;
|
||||||
slot.registered = false;
|
|
||||||
slot.notification_event = nullptr;
|
|
||||||
slot.parameter_event = nullptr;
|
|
||||||
slot.loaded = false;
|
|
||||||
slot.attributes.raw = 0;
|
|
||||||
slot.applet_id = AppletId::None;
|
|
||||||
}
|
|
||||||
|
|
||||||
next_parameter = boost::none;
|
|
||||||
|
|
||||||
HLE::Applets::Shutdown();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace APT
|
} // namespace APT
|
||||||
|
|
|
@ -19,21 +19,6 @@ namespace APT {
|
||||||
/// Each APT service can only have up to 2 sessions connected at the same time.
|
/// Each APT service can only have up to 2 sessions connected at the same time.
|
||||||
static const u32 MaxAPTSessions = 2;
|
static const u32 MaxAPTSessions = 2;
|
||||||
|
|
||||||
/// Holds information about the parameters used in Send/Glance/ReceiveParameter
|
|
||||||
struct MessageParameter {
|
|
||||||
u32 sender_id = 0;
|
|
||||||
u32 destination_id = 0;
|
|
||||||
u32 signal = 0;
|
|
||||||
Kernel::SharedPtr<Kernel::Object> object = nullptr;
|
|
||||||
std::vector<u8> buffer;
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Holds information about the parameters used in StartLibraryApplet
|
|
||||||
struct AppletStartupParameter {
|
|
||||||
Kernel::SharedPtr<Kernel::Object> object = nullptr;
|
|
||||||
std::vector<u8> buffer;
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Used by the application to pass information about the current framebuffer to applets.
|
/// Used by the application to pass information about the current framebuffer to applets.
|
||||||
struct CaptureBufferInfo {
|
struct CaptureBufferInfo {
|
||||||
u32_le size;
|
u32_le size;
|
||||||
|
@ -48,65 +33,6 @@ struct CaptureBufferInfo {
|
||||||
};
|
};
|
||||||
static_assert(sizeof(CaptureBufferInfo) == 0x20, "CaptureBufferInfo struct has incorrect size");
|
static_assert(sizeof(CaptureBufferInfo) == 0x20, "CaptureBufferInfo struct has incorrect size");
|
||||||
|
|
||||||
/// Signals used by APT functions
|
|
||||||
enum class SignalType : u32 {
|
|
||||||
None = 0x0,
|
|
||||||
Wakeup = 0x1,
|
|
||||||
Request = 0x2,
|
|
||||||
Response = 0x3,
|
|
||||||
Exit = 0x4,
|
|
||||||
Message = 0x5,
|
|
||||||
HomeButtonSingle = 0x6,
|
|
||||||
HomeButtonDouble = 0x7,
|
|
||||||
DspSleep = 0x8,
|
|
||||||
DspWakeup = 0x9,
|
|
||||||
WakeupByExit = 0xA,
|
|
||||||
WakeupByPause = 0xB,
|
|
||||||
WakeupByCancel = 0xC,
|
|
||||||
WakeupByCancelAll = 0xD,
|
|
||||||
WakeupByPowerButtonClick = 0xE,
|
|
||||||
WakeupToJumpHome = 0xF,
|
|
||||||
RequestForSysApplet = 0x10,
|
|
||||||
WakeupToLaunchApplication = 0x11,
|
|
||||||
};
|
|
||||||
|
|
||||||
/// App Id's used by APT functions
|
|
||||||
enum class AppletId : u32 {
|
|
||||||
None = 0,
|
|
||||||
AnySystemApplet = 0x100,
|
|
||||||
HomeMenu = 0x101,
|
|
||||||
AlternateMenu = 0x103,
|
|
||||||
Camera = 0x110,
|
|
||||||
FriendList = 0x112,
|
|
||||||
GameNotes = 0x113,
|
|
||||||
InternetBrowser = 0x114,
|
|
||||||
InstructionManual = 0x115,
|
|
||||||
Notifications = 0x116,
|
|
||||||
Miiverse = 0x117,
|
|
||||||
MiiversePost = 0x118,
|
|
||||||
AmiiboSettings = 0x119,
|
|
||||||
AnySysLibraryApplet = 0x200,
|
|
||||||
SoftwareKeyboard1 = 0x201,
|
|
||||||
Ed1 = 0x202,
|
|
||||||
PnoteApp = 0x204,
|
|
||||||
SnoteApp = 0x205,
|
|
||||||
Error = 0x206,
|
|
||||||
Mint = 0x207,
|
|
||||||
Extrapad = 0x208,
|
|
||||||
Memolib = 0x209,
|
|
||||||
Application = 0x300,
|
|
||||||
Tiger = 0x301,
|
|
||||||
AnyLibraryApplet = 0x400,
|
|
||||||
SoftwareKeyboard2 = 0x401,
|
|
||||||
Ed2 = 0x402,
|
|
||||||
PnoteApp2 = 0x404,
|
|
||||||
SnoteApp2 = 0x405,
|
|
||||||
Error2 = 0x406,
|
|
||||||
Mint2 = 0x407,
|
|
||||||
Extrapad2 = 0x408,
|
|
||||||
Memolib2 = 0x409,
|
|
||||||
};
|
|
||||||
|
|
||||||
enum class StartupArgumentType : u32 {
|
enum class StartupArgumentType : u32 {
|
||||||
OtherApp = 0,
|
OtherApp = 0,
|
||||||
Restart = 1,
|
Restart = 1,
|
||||||
|
@ -120,16 +46,6 @@ enum class ScreencapPostPermission : u32 {
|
||||||
DisableScreenshotPostingToMiiverse = 3
|
DisableScreenshotPostingToMiiverse = 3
|
||||||
};
|
};
|
||||||
|
|
||||||
namespace ErrCodes {
|
|
||||||
enum {
|
|
||||||
ParameterPresent = 2,
|
|
||||||
InvalidAppletSlot = 4,
|
|
||||||
};
|
|
||||||
} // namespace ErrCodes
|
|
||||||
|
|
||||||
/// Send a parameter to the currently-running application, which will read it via ReceiveParameter
|
|
||||||
void SendParameter(const MessageParameter& parameter);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* APT::Initialize service function
|
* APT::Initialize service function
|
||||||
* Service function that initializes the APT process for the running application
|
* Service function that initializes the APT process for the running application
|
||||||
|
|
|
@ -0,0 +1,16 @@
|
||||||
|
// Copyright 2018 Citra Emulator Project
|
||||||
|
// Licensed under GPLv2 or any later version
|
||||||
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
namespace Service {
|
||||||
|
namespace APT {
|
||||||
|
namespace ErrCodes {
|
||||||
|
enum {
|
||||||
|
ParameterPresent = 2,
|
||||||
|
InvalidAppletSlot = 4,
|
||||||
|
};
|
||||||
|
} // namespace ErrCodes
|
||||||
|
} // namespace APT
|
||||||
|
} // namespace Service
|
|
@ -5,6 +5,7 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "core/hle/kernel/process.h"
|
#include "core/hle/kernel/process.h"
|
||||||
|
#include "core/hle/service/fs/archive.h"
|
||||||
#include "core/hle/service/service.h"
|
#include "core/hle/service/service.h"
|
||||||
|
|
||||||
namespace Service {
|
namespace Service {
|
||||||
|
|
Reference in New Issue