From 32775125b7af14cf488fdcbc4a61c00507c2d4a5 Mon Sep 17 00:00:00 2001 From: Zach Hilman Date: Mon, 19 Nov 2018 14:24:36 -0500 Subject: [PATCH] applet: Add AppletDataBroker to manage HLE to AM service interaction This cleans up most of the callbacks and such in the Applets::Applet interface, while also properly implementing all four data channels. --- src/core/hle/service/am/am.cpp | 92 +++++++----------- src/core/hle/service/am/applets/applets.cpp | 95 ++++++++++++++++++- src/core/hle/service/am/applets/applets.h | 56 ++++++++--- .../service/am/applets/software_keyboard.cpp | 43 ++++----- .../service/am/applets/software_keyboard.h | 12 +-- 5 files changed, 194 insertions(+), 104 deletions(-) diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp index d92a46b00f..fd14af1e7e 100644 --- a/src/core/hle/service/am/am.cpp +++ b/src/core/hle/service/am/am.cpp @@ -32,6 +32,9 @@ namespace Service::AM { +constexpr ResultCode ERR_NO_DATA_IN_CHANNEL{ErrorModule::AM, 0x2}; +constexpr ResultCode ERR_SIZE_OUT_OF_BOUNDS{ErrorModule::AM, 0x1F7}; + enum class AppletId : u32 { SoftwareKeyboard = 0x11, }; @@ -529,7 +532,8 @@ void ICommonStateGetter::GetPerformanceMode(Kernel::HLERequestContext& ctx) { class ILibraryAppletAccessor final : public ServiceFramework { public: explicit ILibraryAppletAccessor(std::shared_ptr applet) - : ServiceFramework("ILibraryAppletAccessor"), applet(std::move(applet)) { + : ServiceFramework("ILibraryAppletAccessor"), applet(std::move(applet)), + broker(std::make_shared()) { // clang-format off static const FunctionInfo functions[] = { {0, &ILibraryAppletAccessor::GetAppletStateChangedEvent, "GetAppletStateChangedEvent"}, @@ -554,34 +558,16 @@ public: // clang-format on RegisterHandlers(functions); - - auto& kernel = Core::System::GetInstance().Kernel(); - state_changed_event = Kernel::Event::Create(kernel, Kernel::ResetType::OneShot, - "ILibraryAppletAccessor:StateChangedEvent"); - pop_out_data_event = Kernel::Event::Create(kernel, Kernel::ResetType::OneShot, - "ILibraryAppletAccessor:PopDataOutEvent"); - pop_interactive_out_data_event = - Kernel::Event::Create(kernel, Kernel::ResetType::OneShot, - "ILibraryAppletAccessor:PopInteractiveDataOutEvent"); } private: - void AppletStorageProxyOutData(IStorage storage) { - storage_stack.push(std::make_shared(storage)); - pop_out_data_event->Signal(); - } - - void AppletStorageProxyOutInteractiveData(IStorage storage) { - interactive_storage_stack.push(std::make_shared(storage)); - pop_interactive_out_data_event->Signal(); - } - void GetAppletStateChangedEvent(Kernel::HLERequestContext& ctx) { - state_changed_event->Signal(); + const auto event = broker->GetStateChangedEvent(); + event->Signal(); IPC::ResponseBuilder rb{ctx, 2, 1}; rb.Push(RESULT_SUCCESS); - rb.PushCopyObjects(state_changed_event); + rb.PushCopyObjects(event); LOG_DEBUG(Service_AM, "called"); } @@ -604,14 +590,8 @@ private: void Start(Kernel::HLERequestContext& ctx) { ASSERT(applet != nullptr); - applet->Initialize(storage_stack); - while (!storage_stack.empty()) - storage_stack.pop(); - while (!interactive_storage_stack.empty()) - interactive_storage_stack.pop(); - applet->Execute([this](IStorage storage) { AppletStorageProxyOutData(storage); }, - [this](IStorage storage) { AppletStorageProxyOutInteractiveData(storage); }, - [this] { state_changed_event->Signal(); }); + applet->Initialize(broker); + applet->Execute(); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); @@ -621,7 +601,7 @@ private: void PushInData(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; - storage_stack.push(rp.PopIpcInterface()); + broker->PushNormalDataFromGame(*rp.PopIpcInterface()); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); @@ -632,28 +612,25 @@ private: void PopOutData(Kernel::HLERequestContext& ctx) { IPC::ResponseBuilder rb{ctx, 2, 0, 1}; - if (storage_stack.empty()) { - rb.Push(ResultCode(-1)); + const auto storage = broker->PopNormalDataToGame(); + if (storage == nullptr) { + rb.Push(ERR_NO_DATA_IN_CHANNEL); return; } rb.Push(RESULT_SUCCESS); - rb.PushIpcInterface(std::move(storage_stack.front())); - - storage_stack.pop(); + rb.PushIpcInterface(std::move(*storage)); LOG_DEBUG(Service_AM, "called"); } void PushInteractiveInData(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; - interactive_storage_stack.push(rp.PopIpcInterface()); + broker->PushInteractiveDataFromGame(*rp.PopIpcInterface()); ASSERT(applet->IsInitialized()); - applet->ReceiveInteractiveData(interactive_storage_stack.back()); - applet->Execute([this](IStorage storage) { AppletStorageProxyOutData(storage); }, - [this](IStorage storage) { AppletStorageProxyOutInteractiveData(storage); }, - [this] { state_changed_event->Signal(); }); + applet->ExecuteInteractive(); + applet->Execute(); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); @@ -664,15 +641,14 @@ private: void PopInteractiveOutData(Kernel::HLERequestContext& ctx) { IPC::ResponseBuilder rb{ctx, 2, 0, 1}; - if (interactive_storage_stack.empty()) { - rb.Push(ResultCode(-1)); + const auto storage = broker->PopInteractiveDataToGame(); + if (storage == nullptr) { + rb.Push(ERR_NO_DATA_IN_CHANNEL); return; } rb.Push(RESULT_SUCCESS); - rb.PushIpcInterface(std::move(interactive_storage_stack.front())); - - interactive_storage_stack.pop(); + rb.PushIpcInterface(std::move(*storage)); LOG_DEBUG(Service_AM, "called"); } @@ -680,7 +656,7 @@ private: void GetPopOutDataEvent(Kernel::HLERequestContext& ctx) { IPC::ResponseBuilder rb{ctx, 2, 1}; rb.Push(RESULT_SUCCESS); - rb.PushCopyObjects(pop_out_data_event); + rb.PushCopyObjects(broker->GetNormalDataEvent()); LOG_DEBUG(Service_AM, "called"); } @@ -688,17 +664,13 @@ private: void GetPopInteractiveOutDataEvent(Kernel::HLERequestContext& ctx) { IPC::ResponseBuilder rb{ctx, 2, 1}; rb.Push(RESULT_SUCCESS); - rb.PushCopyObjects(pop_interactive_out_data_event); + rb.PushCopyObjects(broker->GetInteractiveDataEvent()); LOG_DEBUG(Service_AM, "called"); } std::shared_ptr applet; - std::queue> storage_stack; - std::queue> interactive_storage_stack; - Kernel::SharedPtr state_changed_event; - Kernel::SharedPtr pop_out_data_event; - Kernel::SharedPtr pop_interactive_out_data_event; + std::shared_ptr broker; }; void IStorage::Open(Kernel::HLERequestContext& ctx) { @@ -740,9 +712,12 @@ void IStorageAccessor::Write(Kernel::HLERequestContext& ctx) { const u64 offset{rp.Pop()}; const std::vector data{ctx.ReadBuffer()}; - const auto size = std::min(data.size(), backing.buffer.size() - offset); + if (data.size() > backing.buffer.size() - offset) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ERR_SIZE_OUT_OF_BOUNDS); + } - std::memcpy(&backing.buffer[offset], data.data(), size); + std::memcpy(backing.buffer.data() + offset, data.data(), data.size()); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); @@ -754,9 +729,12 @@ void IStorageAccessor::Read(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; const u64 offset{rp.Pop()}; - std::size_t size{ctx.GetWriteBufferSize()}; + const std::size_t size{ctx.GetWriteBufferSize()}; - size = std::min(size, backing.buffer.size() - offset); + if (size > backing.buffer.size() - offset) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ERR_SIZE_OUT_OF_BOUNDS); + } ctx.WriteBuffer(backing.buffer.data() + offset, size); diff --git a/src/core/hle/service/am/applets/applets.cpp b/src/core/hle/service/am/applets/applets.cpp index be950d320c..c81bd59b28 100644 --- a/src/core/hle/service/am/applets/applets.cpp +++ b/src/core/hle/service/am/applets/applets.cpp @@ -4,21 +4,108 @@ #include #include "common/assert.h" +#include "core/core.h" #include "core/frontend/applets/software_keyboard.h" +#include "core/hle/kernel/event.h" +#include "core/hle/kernel/server_port.h" #include "core/hle/service/am/am.h" #include "core/hle/service/am/applets/applets.h" namespace Service::AM::Applets { +AppletDataBroker::AppletDataBroker() { + auto& kernel = Core::System::GetInstance().Kernel(); + state_changed_event = Kernel::Event::Create(kernel, Kernel::ResetType::OneShot, + "ILibraryAppletAccessor:StateChangedEvent"); + pop_out_data_event = Kernel::Event::Create(kernel, Kernel::ResetType::OneShot, + "ILibraryAppletAccessor:PopDataOutEvent"); + pop_interactive_out_data_event = Kernel::Event::Create( + kernel, Kernel::ResetType::OneShot, "ILibraryAppletAccessor:PopInteractiveDataOutEvent"); +} + +AppletDataBroker::~AppletDataBroker() = default; + +std::unique_ptr AppletDataBroker::PopNormalDataToGame() { + if (out_channel.empty()) + return nullptr; + + auto out = std::move(out_channel.front()); + out_channel.pop(); + return out; +} + +std::unique_ptr AppletDataBroker::PopNormalDataToApplet() { + if (in_channel.empty()) + return nullptr; + + auto out = std::move(in_channel.front()); + in_channel.pop(); + return out; +} + +std::unique_ptr AppletDataBroker::PopInteractiveDataToGame() { + if (out_interactive_channel.empty()) + return nullptr; + + auto out = std::move(out_interactive_channel.front()); + out_interactive_channel.pop(); + return out; +} + +std::unique_ptr AppletDataBroker::PopInteractiveDataToApplet() { + if (in_interactive_channel.empty()) + return nullptr; + + auto out = std::move(in_interactive_channel.front()); + in_interactive_channel.pop(); + return out; +} + +void AppletDataBroker::PushNormalDataFromGame(IStorage storage) { + in_channel.push(std::make_unique(storage)); +} + +void AppletDataBroker::PushNormalDataFromApplet(IStorage storage) { + out_channel.push(std::make_unique(storage)); + pop_out_data_event->Signal(); +} + +void AppletDataBroker::PushInteractiveDataFromGame(IStorage storage) { + in_interactive_channel.push(std::make_unique(storage)); +} + +void AppletDataBroker::PushInteractiveDataFromApplet(IStorage storage) { + out_interactive_channel.push(std::make_unique(storage)); + pop_interactive_out_data_event->Signal(); +} + +void AppletDataBroker::SignalStateChanged() const { + state_changed_event->Signal(); +} + +Kernel::SharedPtr AppletDataBroker::GetNormalDataEvent() const { + return pop_out_data_event; +} + +Kernel::SharedPtr AppletDataBroker::GetInteractiveDataEvent() const { + return pop_interactive_out_data_event; +} + +Kernel::SharedPtr AppletDataBroker::GetStateChangedEvent() const { + return state_changed_event; +} + Applet::Applet() = default; Applet::~Applet() = default; -void Applet::Initialize(std::queue> storage) { - storage_stack = std::move(storage); +void Applet::Initialize(std::shared_ptr broker_) { + broker = std::move(broker_); - const auto common_data = storage_stack.front()->GetData(); - storage_stack.pop(); + const auto common = broker->PopNormalDataToApplet(); + ASSERT(common != nullptr); + + const auto common_data = common->GetData(); ASSERT(common_data.size() >= sizeof(CommonArguments)); std::memcpy(&common_args, common_data.data(), sizeof(CommonArguments)); diff --git a/src/core/hle/service/am/applets/applets.h b/src/core/hle/service/am/applets/applets.h index a6a9bf77b2..1364456496 100644 --- a/src/core/hle/service/am/applets/applets.h +++ b/src/core/hle/service/am/applets/applets.h @@ -8,35 +8,67 @@ #include #include #include "common/swap.h" +#include "core/hle/kernel/event.h" union ResultCode; -namespace Frontend { -class SoftwareKeyboardApplet; -} - namespace Service::AM { class IStorage; namespace Applets { -using AppletStorageProxyFunction = std::function; -using AppletStateProxyFunction = std::function; +class AppletDataBroker final { +public: + AppletDataBroker(); + ~AppletDataBroker(); + + std::unique_ptr PopNormalDataToGame(); + std::unique_ptr PopNormalDataToApplet(); + + std::unique_ptr PopInteractiveDataToGame(); + std::unique_ptr PopInteractiveDataToApplet(); + + void PushNormalDataFromGame(IStorage storage); + void PushNormalDataFromApplet(IStorage storage); + + void PushInteractiveDataFromGame(IStorage storage); + void PushInteractiveDataFromApplet(IStorage storage); + + void SignalStateChanged() const; + + Kernel::SharedPtr GetNormalDataEvent() const; + Kernel::SharedPtr GetInteractiveDataEvent() const; + Kernel::SharedPtr GetStateChangedEvent() const; + +private: + // Queues are named from applet's perspective + std::queue> + in_channel; // PopNormalDataToApplet and PushNormalDataFromGame + std::queue> + out_channel; // PopNormalDataToGame and PushNormalDataFromApplet + std::queue> + in_interactive_channel; // PopInteractiveDataToApplet and PushInteractiveDataFromGame + std::queue> + out_interactive_channel; // PopInteractiveDataToGame and PushInteractiveDataFromApplet + + Kernel::SharedPtr state_changed_event; + Kernel::SharedPtr pop_out_data_event; // Signaled on PushNormalDataFromApplet + Kernel::SharedPtr + pop_interactive_out_data_event; // Signaled on PushInteractiveDataFromApplet +}; class Applet { public: Applet(); virtual ~Applet(); - virtual void Initialize(std::queue> storage); + virtual void Initialize(std::shared_ptr broker); virtual bool TransactionComplete() const = 0; virtual ResultCode GetStatus() const = 0; - virtual void ReceiveInteractiveData(std::shared_ptr storage) = 0; - virtual void Execute(AppletStorageProxyFunction out_data, - AppletStorageProxyFunction out_interactive_data, - AppletStateProxyFunction state) = 0; + virtual void ExecuteInteractive() = 0; + virtual void Execute() = 0; bool IsInitialized() const { return initialized; @@ -54,7 +86,7 @@ protected: static_assert(sizeof(CommonArguments) == 0x20, "CommonArguments has incorrect size."); CommonArguments common_args; - std::queue> storage_stack; + std::shared_ptr broker; bool initialized = false; }; diff --git a/src/core/hle/service/am/applets/software_keyboard.cpp b/src/core/hle/service/am/applets/software_keyboard.cpp index 816b5fb5fa..ca9ef7e7d4 100644 --- a/src/core/hle/service/am/applets/software_keyboard.cpp +++ b/src/core/hle/service/am/applets/software_keyboard.cpp @@ -42,22 +42,23 @@ SoftwareKeyboard::SoftwareKeyboard() = default; SoftwareKeyboard::~SoftwareKeyboard() = default; -void SoftwareKeyboard::Initialize(std::queue> storage_) { +void SoftwareKeyboard::Initialize(std::shared_ptr broker_) { complete = false; initial_text.clear(); final_data.clear(); - Applet::Initialize(std::move(storage_)); + Applet::Initialize(std::move(broker_)); - ASSERT(storage_stack.size() >= 2); - const auto& keyboard_config = storage_stack.front()->GetData(); - storage_stack.pop(); + const auto keyboard_config_storage = broker->PopNormalDataToApplet(); + ASSERT(keyboard_config_storage != nullptr); + const auto& keyboard_config = keyboard_config_storage->GetData(); ASSERT(keyboard_config.size() >= sizeof(KeyboardConfig)); std::memcpy(&config, keyboard_config.data(), sizeof(KeyboardConfig)); - const auto& work_buffer = storage_stack.front()->GetData(); - storage_stack.pop(); + const auto work_buffer_storage = broker->PopNormalDataToApplet(); + ASSERT(work_buffer_storage != nullptr); + const auto& work_buffer = work_buffer_storage->GetData(); if (config.initial_string_size == 0) return; @@ -76,10 +77,12 @@ ResultCode SoftwareKeyboard::GetStatus() const { return status; } -void SoftwareKeyboard::ReceiveInteractiveData(std::shared_ptr storage) { +void SoftwareKeyboard::ExecuteInteractive() { if (complete) return; + const auto storage = broker->PopInteractiveDataToApplet(); + ASSERT(storage != nullptr); const auto data = storage->GetData(); const auto status = static_cast(data[0]); @@ -91,15 +94,14 @@ void SoftwareKeyboard::ReceiveInteractiveData(std::shared_ptr storage) std::array string; std::memcpy(string.data(), data.data() + 4, string.size() * 2); frontend.SendTextCheckDialog( - Common::UTF16StringFromFixedZeroTerminatedBuffer(string.data(), string.size()), state); + Common::UTF16StringFromFixedZeroTerminatedBuffer(string.data(), string.size()), + [this] { broker->SignalStateChanged(); }); } } -void SoftwareKeyboard::Execute(AppletStorageProxyFunction out_data, - AppletStorageProxyFunction out_interactive_data, - AppletStateProxyFunction state) { +void SoftwareKeyboard::Execute() { if (complete) { - out_data(IStorage{final_data}); + broker->PushNormalDataFromApplet(IStorage{final_data}); return; } @@ -107,9 +109,6 @@ void SoftwareKeyboard::Execute(AppletStorageProxyFunction out_data, const auto parameters = ConvertToFrontendParameters(config, initial_text); - this->out_data = out_data; - this->out_interactive_data = out_interactive_data; - this->state = state; frontend.RequestText([this](std::optional text) { WriteText(text); }, parameters); } @@ -147,19 +146,19 @@ void SoftwareKeyboard::WriteText(std::optional text) { final_data = output_main; if (complete) { - out_data(IStorage{output_main}); + broker->PushNormalDataFromApplet(IStorage{output_main}); } else { - out_data(IStorage{output_main}); - out_interactive_data(IStorage{output_sub}); + broker->PushNormalDataFromApplet(IStorage{output_main}); + broker->PushInteractiveDataFromApplet(IStorage{output_sub}); } - state(); + broker->SignalStateChanged(); } else { status = ResultCode(-1); output_main[0] = 1; complete = true; - out_data(IStorage{output_main}); - state(); + broker->PushNormalDataFromApplet(IStorage{output_main}); + broker->SignalStateChanged(); } } } // namespace Service::AM::Applets diff --git a/src/core/hle/service/am/applets/software_keyboard.h b/src/core/hle/service/am/applets/software_keyboard.h index e0a9479c2f..405c58851f 100644 --- a/src/core/hle/service/am/applets/software_keyboard.h +++ b/src/core/hle/service/am/applets/software_keyboard.h @@ -50,14 +50,12 @@ public: SoftwareKeyboard(); ~SoftwareKeyboard() override; - void Initialize(std::queue> storage) override; + void Initialize(std::shared_ptr broker) override; bool TransactionComplete() const override; ResultCode GetStatus() const override; - void ReceiveInteractiveData(std::shared_ptr storage) override; - void Execute(AppletStorageProxyFunction out_data, - AppletStorageProxyFunction out_interactive_data, - AppletStateProxyFunction state) override; + void ExecuteInteractive() override; + void Execute() override; void WriteText(std::optional text); @@ -67,10 +65,6 @@ private: bool complete = false; std::vector final_data; ResultCode status = ResultCode(-1); - - AppletStorageProxyFunction out_data; - AppletStorageProxyFunction out_interactive_data; - AppletStateProxyFunction state; }; } // namespace Service::AM::Applets