summaryrefslogtreecommitdiffstats
path: root/src/core/hle/service/am
diff options
context:
space:
mode:
Diffstat (limited to 'src/core/hle/service/am')
-rw-r--r--src/core/hle/service/am/am.cpp358
-rw-r--r--src/core/hle/service/am/am.h30
-rw-r--r--src/core/hle/service/am/applets/applets.cpp113
-rw-r--r--src/core/hle/service/am/applets/applets.h112
-rw-r--r--src/core/hle/service/am/applets/software_keyboard.cpp161
-rw-r--r--src/core/hle/service/am/applets/software_keyboard.h74
-rw-r--r--src/core/hle/service/am/applets/stub_applet.cpp70
-rw-r--r--src/core/hle/service/am/applets/stub_applet.h24
8 files changed, 829 insertions, 113 deletions
diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp
index b7fe39bc9..4f17b52f9 100644
--- a/src/core/hle/service/am/am.cpp
+++ b/src/core/hle/service/am/am.cpp
@@ -6,14 +6,19 @@
#include <cinttypes>
#include <cstring>
#include <stack>
+#include "audio_core/audio_renderer.h"
#include "core/core.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/kernel/event.h"
#include "core/hle/kernel/process.h"
+#include "core/hle/kernel/shared_memory.h"
#include "core/hle/service/acc/profile_manager.h"
#include "core/hle/service/am/am.h"
#include "core/hle/service/am/applet_ae.h"
#include "core/hle/service/am/applet_oe.h"
+#include "core/hle/service/am/applets/applets.h"
+#include "core/hle/service/am/applets/software_keyboard.h"
+#include "core/hle/service/am/applets/stub_applet.h"
#include "core/hle/service/am/idle.h"
#include "core/hle/service/am/omm.h"
#include "core/hle/service/am/spsm.h"
@@ -28,6 +33,13 @@
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,
+};
+
constexpr u32 POP_LAUNCH_PARAMETER_MAGIC = 0xC79497CA;
struct LaunchParameters {
@@ -203,8 +215,8 @@ ISelfController::ISelfController(std::shared_ptr<NVFlinger::NVFlinger> nvflinger
ISelfController::~ISelfController() = default;
void ISelfController::SetFocusHandlingMode(Kernel::HLERequestContext& ctx) {
- // Takes 3 input u8s with each field located immediately after the previous u8, these are
- // bool flags. No output.
+ // Takes 3 input u8s with each field located immediately after the previous
+ // u8, these are bool flags. No output.
IPC::RequestParser rp{ctx};
@@ -258,8 +270,8 @@ void ISelfController::SetOperationModeChangedNotification(Kernel::HLERequestCont
}
void ISelfController::SetOutOfFocusSuspendingEnabled(Kernel::HLERequestContext& ctx) {
- // Takes 3 input u8s with each field located immediately after the previous u8, these are
- // bool flags. No output.
+ // Takes 3 input u8s with each field located immediately after the previous
+ // u8, these are bool flags. No output.
IPC::RequestParser rp{ctx};
bool enabled = rp.Pop<bool>();
@@ -302,8 +314,8 @@ void ISelfController::SetScreenShotImageOrientation(Kernel::HLERequestContext& c
}
void ISelfController::CreateManagedDisplayLayer(Kernel::HLERequestContext& ctx) {
- // TODO(Subv): Find out how AM determines the display to use, for now just create the layer
- // in the Default display.
+ // TODO(Subv): Find out how AM determines the display to use, for now just
+ // create the layer in the Default display.
u64 display_id = nvflinger->OpenDisplay("Default");
u64 layer_id = nvflinger->CreateLayer(display_id);
@@ -485,6 +497,24 @@ void ICommonStateGetter::GetDefaultDisplayResolution(Kernel::HLERequestContext&
LOG_DEBUG(Service_AM, "called");
}
+IStorage::IStorage(std::vector<u8> buffer)
+ : ServiceFramework("IStorage"), buffer(std::move(buffer)) {
+ // clang-format off
+ static const FunctionInfo functions[] = {
+ {0, &IStorage::Open, "Open"},
+ {1, nullptr, "OpenTransferStorage"},
+ };
+ // clang-format on
+
+ RegisterHandlers(functions);
+}
+
+IStorage::~IStorage() = default;
+
+const std::vector<u8>& IStorage::GetData() const {
+ return buffer;
+}
+
void ICommonStateGetter::GetOperationMode(Kernel::HLERequestContext& ctx) {
const bool use_docked_mode{Settings::values.use_docked_mode};
IPC::ResponseBuilder rb{ctx, 3};
@@ -504,15 +534,30 @@ void ICommonStateGetter::GetPerformanceMode(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_AM, "called");
}
-class IStorageAccessor final : public ServiceFramework<IStorageAccessor> {
+class ILibraryAppletAccessor final : public ServiceFramework<ILibraryAppletAccessor> {
public:
- explicit IStorageAccessor(std::vector<u8> buffer)
- : ServiceFramework("IStorageAccessor"), buffer(std::move(buffer)) {
+ explicit ILibraryAppletAccessor(std::shared_ptr<Applets::Applet> applet)
+ : ServiceFramework("ILibraryAppletAccessor"), applet(std::move(applet)) {
// clang-format off
static const FunctionInfo functions[] = {
- {0, &IStorageAccessor::GetSize, "GetSize"},
- {10, &IStorageAccessor::Write, "Write"},
- {11, &IStorageAccessor::Read, "Read"},
+ {0, &ILibraryAppletAccessor::GetAppletStateChangedEvent, "GetAppletStateChangedEvent"},
+ {1, &ILibraryAppletAccessor::IsCompleted, "IsCompleted"},
+ {10, &ILibraryAppletAccessor::Start, "Start"},
+ {20, nullptr, "RequestExit"},
+ {25, nullptr, "Terminate"},
+ {30, &ILibraryAppletAccessor::GetResult, "GetResult"},
+ {50, nullptr, "SetOutOfFocusApplicationSuspendingEnabled"},
+ {100, &ILibraryAppletAccessor::PushInData, "PushInData"},
+ {101, &ILibraryAppletAccessor::PopOutData, "PopOutData"},
+ {102, nullptr, "PushExtraStorage"},
+ {103, &ILibraryAppletAccessor::PushInteractiveInData, "PushInteractiveInData"},
+ {104, &ILibraryAppletAccessor::PopInteractiveOutData, "PopInteractiveOutData"},
+ {105, &ILibraryAppletAccessor::GetPopOutDataEvent, "GetPopOutDataEvent"},
+ {106, &ILibraryAppletAccessor::GetPopInteractiveOutDataEvent, "GetPopInteractiveOutDataEvent"},
+ {110, nullptr, "NeedsToExitProcess"},
+ {120, nullptr, "GetLibraryAppletInfo"},
+ {150, nullptr, "RequestForAppletToGetForeground"},
+ {160, nullptr, "GetIndirectLayerConsumerHandle"},
};
// clang-format on
@@ -520,158 +565,187 @@ public:
}
private:
- std::vector<u8> buffer;
+ void GetAppletStateChangedEvent(Kernel::HLERequestContext& ctx) {
+ const auto event = applet->GetBroker().GetStateChangedEvent();
+ event->Signal();
- void GetSize(Kernel::HLERequestContext& ctx) {
- IPC::ResponseBuilder rb{ctx, 4};
+ IPC::ResponseBuilder rb{ctx, 2, 1};
+ rb.Push(RESULT_SUCCESS);
+ rb.PushCopyObjects(event);
+
+ LOG_DEBUG(Service_AM, "called");
+ }
+ void IsCompleted(Kernel::HLERequestContext& ctx) {
+ IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
- rb.Push(static_cast<u64>(buffer.size()));
+ rb.Push<u32>(applet->TransactionComplete());
LOG_DEBUG(Service_AM, "called");
}
- void Write(Kernel::HLERequestContext& ctx) {
- IPC::RequestParser rp{ctx};
+ void GetResult(Kernel::HLERequestContext& ctx) {
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(applet->GetStatus());
- const u64 offset{rp.Pop<u64>()};
- const std::vector<u8> data{ctx.ReadBuffer()};
+ LOG_DEBUG(Service_AM, "called");
+ }
- ASSERT(offset + data.size() <= buffer.size());
+ void Start(Kernel::HLERequestContext& ctx) {
+ ASSERT(applet != nullptr);
- std::memcpy(&buffer[offset], data.data(), data.size());
+ applet->Initialize();
+ applet->Execute();
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
- LOG_DEBUG(Service_AM, "called, offset={}", offset);
+ LOG_DEBUG(Service_AM, "called");
}
- void Read(Kernel::HLERequestContext& ctx) {
+ void PushInData(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
+ applet->GetBroker().PushNormalDataFromGame(*rp.PopIpcInterface<IStorage>());
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(RESULT_SUCCESS);
+
+ LOG_DEBUG(Service_AM, "called");
+ }
- const u64 offset{rp.Pop<u64>()};
- const std::size_t size{ctx.GetWriteBufferSize()};
+ void PopOutData(Kernel::HLERequestContext& ctx) {
+ IPC::ResponseBuilder rb{ctx, 2, 0, 1};
- ASSERT(offset + size <= buffer.size());
+ const auto storage = applet->GetBroker().PopNormalDataToGame();
+ if (storage == nullptr) {
+ rb.Push(ERR_NO_DATA_IN_CHANNEL);
+ return;
+ }
- ctx.WriteBuffer(buffer.data() + offset, size);
+ rb.Push(RESULT_SUCCESS);
+ rb.PushIpcInterface<IStorage>(std::move(*storage));
+
+ LOG_DEBUG(Service_AM, "called");
+ }
+
+ void PushInteractiveInData(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ applet->GetBroker().PushInteractiveDataFromGame(*rp.PopIpcInterface<IStorage>());
+
+ ASSERT(applet->IsInitialized());
+ applet->ExecuteInteractive();
+ applet->Execute();
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
- LOG_DEBUG(Service_AM, "called, offset={}", offset);
+ LOG_DEBUG(Service_AM, "called");
}
-};
-class IStorage final : public ServiceFramework<IStorage> {
-public:
- explicit IStorage(std::vector<u8> buffer)
- : ServiceFramework("IStorage"), buffer(std::move(buffer)) {
- // clang-format off
- static const FunctionInfo functions[] = {
- {0, &IStorage::Open, "Open"},
- {1, nullptr, "OpenTransferStorage"},
- };
- // clang-format on
+ void PopInteractiveOutData(Kernel::HLERequestContext& ctx) {
+ IPC::ResponseBuilder rb{ctx, 2, 0, 1};
- RegisterHandlers(functions);
+ const auto storage = applet->GetBroker().PopInteractiveDataToGame();
+ if (storage == nullptr) {
+ rb.Push(ERR_NO_DATA_IN_CHANNEL);
+ return;
+ }
+
+ rb.Push(RESULT_SUCCESS);
+ rb.PushIpcInterface<IStorage>(std::move(*storage));
+
+ LOG_DEBUG(Service_AM, "called");
}
-private:
- std::vector<u8> buffer;
+ void GetPopOutDataEvent(Kernel::HLERequestContext& ctx) {
+ IPC::ResponseBuilder rb{ctx, 2, 1};
+ rb.Push(RESULT_SUCCESS);
+ rb.PushCopyObjects(applet->GetBroker().GetNormalDataEvent());
- void Open(Kernel::HLERequestContext& ctx) {
- IPC::ResponseBuilder rb{ctx, 2, 0, 1};
+ LOG_DEBUG(Service_AM, "called");
+ }
+ void GetPopInteractiveOutDataEvent(Kernel::HLERequestContext& ctx) {
+ IPC::ResponseBuilder rb{ctx, 2, 1};
rb.Push(RESULT_SUCCESS);
- rb.PushIpcInterface<AM::IStorageAccessor>(buffer);
+ rb.PushCopyObjects(applet->GetBroker().GetInteractiveDataEvent());
LOG_DEBUG(Service_AM, "called");
}
+
+ std::shared_ptr<Applets::Applet> applet;
};
-class ILibraryAppletAccessor final : public ServiceFramework<ILibraryAppletAccessor> {
-public:
- explicit ILibraryAppletAccessor() : ServiceFramework("ILibraryAppletAccessor") {
- // clang-format off
+void IStorage::Open(Kernel::HLERequestContext& ctx) {
+ IPC::ResponseBuilder rb{ctx, 2, 0, 1};
+
+ rb.Push(RESULT_SUCCESS);
+ rb.PushIpcInterface<IStorageAccessor>(*this);
+
+ LOG_DEBUG(Service_AM, "called");
+}
+
+IStorageAccessor::IStorageAccessor(IStorage& storage)
+ : ServiceFramework("IStorageAccessor"), backing(storage) {
+ // clang-format off
static const FunctionInfo functions[] = {
- {0, &ILibraryAppletAccessor::GetAppletStateChangedEvent, "GetAppletStateChangedEvent"},
- {1, nullptr, "IsCompleted"},
- {10, &ILibraryAppletAccessor::Start, "Start"},
- {20, nullptr, "RequestExit"},
- {25, nullptr, "Terminate"},
- {30, &ILibraryAppletAccessor::GetResult, "GetResult"},
- {50, nullptr, "SetOutOfFocusApplicationSuspendingEnabled"},
- {100, &ILibraryAppletAccessor::PushInData, "PushInData"},
- {101, &ILibraryAppletAccessor::PopOutData, "PopOutData"},
- {102, nullptr, "PushExtraStorage"},
- {103, nullptr, "PushInteractiveInData"},
- {104, nullptr, "PopInteractiveOutData"},
- {105, nullptr, "GetPopOutDataEvent"},
- {106, nullptr, "GetPopInteractiveOutDataEvent"},
- {110, nullptr, "NeedsToExitProcess"},
- {120, nullptr, "GetLibraryAppletInfo"},
- {150, nullptr, "RequestForAppletToGetForeground"},
- {160, nullptr, "GetIndirectLayerConsumerHandle"},
+ {0, &IStorageAccessor::GetSize, "GetSize"},
+ {10, &IStorageAccessor::Write, "Write"},
+ {11, &IStorageAccessor::Read, "Read"},
};
- // clang-format on
+ // clang-format on
- RegisterHandlers(functions);
+ RegisterHandlers(functions);
+}
- auto& kernel = Core::System::GetInstance().Kernel();
- state_changed_event = Kernel::Event::Create(kernel, Kernel::ResetType::OneShot,
- "ILibraryAppletAccessor:StateChangedEvent");
- }
+IStorageAccessor::~IStorageAccessor() = default;
-private:
- void GetAppletStateChangedEvent(Kernel::HLERequestContext& ctx) {
- state_changed_event->Signal();
+void IStorageAccessor::GetSize(Kernel::HLERequestContext& ctx) {
+ IPC::ResponseBuilder rb{ctx, 4};
- IPC::ResponseBuilder rb{ctx, 2, 1};
- rb.Push(RESULT_SUCCESS);
- rb.PushCopyObjects(state_changed_event);
+ rb.Push(RESULT_SUCCESS);
+ rb.Push(static_cast<u64>(backing.buffer.size()));
- LOG_WARNING(Service_AM, "(STUBBED) called");
- }
+ LOG_DEBUG(Service_AM, "called");
+}
- void GetResult(Kernel::HLERequestContext& ctx) {
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(RESULT_SUCCESS);
+void IStorageAccessor::Write(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
- LOG_WARNING(Service_AM, "(STUBBED) called");
- }
+ const u64 offset{rp.Pop<u64>()};
+ const std::vector<u8> data{ctx.ReadBuffer()};
- void Start(Kernel::HLERequestContext& ctx) {
+ if (data.size() > backing.buffer.size() - offset) {
IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(RESULT_SUCCESS);
-
- LOG_WARNING(Service_AM, "(STUBBED) called");
+ rb.Push(ERR_SIZE_OUT_OF_BOUNDS);
}
- void PushInData(Kernel::HLERequestContext& ctx) {
- IPC::RequestParser rp{ctx};
- storage_stack.push(rp.PopIpcInterface<AM::IStorage>());
+ std::memcpy(backing.buffer.data() + offset, data.data(), data.size());
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(RESULT_SUCCESS);
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(RESULT_SUCCESS);
- LOG_DEBUG(Service_AM, "called");
- }
+ LOG_DEBUG(Service_AM, "called, offset={}", offset);
+}
- void PopOutData(Kernel::HLERequestContext& ctx) {
- IPC::ResponseBuilder rb{ctx, 2, 0, 1};
- rb.Push(RESULT_SUCCESS);
- rb.PushIpcInterface<AM::IStorage>(std::move(storage_stack.top()));
+void IStorageAccessor::Read(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
- storage_stack.pop();
+ const u64 offset{rp.Pop<u64>()};
+ const std::size_t size{ctx.GetWriteBufferSize()};
- LOG_DEBUG(Service_AM, "called");
+ if (size > backing.buffer.size() - offset) {
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ERR_SIZE_OUT_OF_BOUNDS);
}
- std::stack<std::shared_ptr<AM::IStorage>> storage_stack;
- Kernel::SharedPtr<Kernel::Event> state_changed_event;
-};
+ ctx.WriteBuffer(backing.buffer.data() + offset, size);
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(RESULT_SUCCESS);
+
+ LOG_DEBUG(Service_AM, "called, offset={}", offset);
+}
ILibraryAppletCreator::ILibraryAppletCreator() : ServiceFramework("ILibraryAppletCreator") {
static const FunctionInfo functions[] = {
@@ -679,7 +753,7 @@ ILibraryAppletCreator::ILibraryAppletCreator() : ServiceFramework("ILibraryApple
{1, nullptr, "TerminateAllLibraryApplets"},
{2, nullptr, "AreAnyLibraryAppletsLeft"},
{10, &ILibraryAppletCreator::CreateStorage, "CreateStorage"},
- {11, nullptr, "CreateTransferMemoryStorage"},
+ {11, &ILibraryAppletCreator::CreateTransferMemoryStorage, "CreateTransferMemoryStorage"},
{12, nullptr, "CreateHandleStorage"},
};
RegisterHandlers(functions);
@@ -687,11 +761,37 @@ ILibraryAppletCreator::ILibraryAppletCreator() : ServiceFramework("ILibraryApple
ILibraryAppletCreator::~ILibraryAppletCreator() = default;
+static std::shared_ptr<Applets::Applet> GetAppletFromId(AppletId id) {
+ switch (id) {
+ case AppletId::SoftwareKeyboard:
+ return std::make_shared<Applets::SoftwareKeyboard>();
+ default:
+ LOG_ERROR(Service_AM, "Unimplemented AppletId [{:08X}]! -- Falling back to stub!",
+ static_cast<u32>(id));
+ return std::make_shared<Applets::StubApplet>();
+ }
+}
+
void ILibraryAppletCreator::CreateLibraryApplet(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const auto applet_id = rp.PopRaw<AppletId>();
+ const auto applet_mode = rp.PopRaw<u32>();
+
+ LOG_DEBUG(Service_AM, "called with applet_id={:08X}, applet_mode={:08X}",
+ static_cast<u32>(applet_id), applet_mode);
+
+ const auto applet = GetAppletFromId(applet_id);
+
+ if (applet == nullptr) {
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ResultCode(-1));
+ return;
+ }
+
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
- rb.PushIpcInterface<AM::ILibraryAppletAccessor>();
+ rb.PushIpcInterface<AM::ILibraryAppletAccessor>(applet);
LOG_DEBUG(Service_AM, "called");
}
@@ -708,6 +808,31 @@ void ILibraryAppletCreator::CreateStorage(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_AM, "called, size={}", size);
}
+void ILibraryAppletCreator::CreateTransferMemoryStorage(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+
+ rp.SetCurrentOffset(3);
+ const auto handle{rp.Pop<Kernel::Handle>()};
+
+ const auto shared_mem =
+ Core::System::GetInstance().CurrentProcess()->GetHandleTable().Get<Kernel::SharedMemory>(
+ handle);
+
+ if (shared_mem == nullptr) {
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ResultCode(-1));
+ return;
+ }
+
+ const u8* mem_begin = shared_mem->GetPointer();
+ const u8* mem_end = mem_begin + shared_mem->GetSize();
+ std::vector<u8> memory{mem_begin, mem_end};
+
+ IPC::ResponseBuilder rb{ctx, 2, 0, 1};
+ rb.Push(RESULT_SUCCESS);
+ rb.PushIpcInterface(std::make_shared<IStorage>(std::move(memory)));
+}
+
IApplicationFunctions::IApplicationFunctions() : ServiceFramework("IApplicationFunctions") {
// clang-format off
static const FunctionInfo functions[] = {
@@ -737,7 +862,7 @@ IApplicationFunctions::IApplicationFunctions() : ServiceFramework("IApplicationF
{70, nullptr, "RequestToShutdown"},
{71, nullptr, "RequestToReboot"},
{80, nullptr, "ExitAndRequestToShowThanksMessage"},
- {90, nullptr, "EnableApplicationCrashReport"},
+ {90, &IApplicationFunctions::EnableApplicationCrashReport, "EnableApplicationCrashReport"},
{100, nullptr, "InitializeApplicationCopyrightFrameBuffer"},
{101, nullptr, "SetApplicationCopyrightImage"},
{102, nullptr, "SetApplicationCopyrightVisibility"},
@@ -756,6 +881,12 @@ IApplicationFunctions::IApplicationFunctions() : ServiceFramework("IApplicationF
IApplicationFunctions::~IApplicationFunctions() = default;
+void IApplicationFunctions::EnableApplicationCrashReport(Kernel::HLERequestContext& ctx) {
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(RESULT_SUCCESS);
+ LOG_WARNING(Service_AM, "(STUBBED) called");
+}
+
void IApplicationFunctions::BeginBlockingHomeButtonShortAndLongPressed(
Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2};
@@ -825,7 +956,8 @@ void IApplicationFunctions::EnsureSaveData(Kernel::HLERequestContext& ctx) {
void IApplicationFunctions::SetTerminateResult(Kernel::HLERequestContext& ctx) {
// Takes an input u32 Result, no output.
- // For example, in some cases official apps use this with error 0x2A2 then uses svcBreak.
+ // For example, in some cases official apps use this with error 0x2A2 then
+ // uses svcBreak.
IPC::RequestParser rp{ctx};
u32 result = rp.Pop<u32>();
@@ -888,8 +1020,8 @@ void IApplicationFunctions::GetPseudoDeviceId(Kernel::HLERequestContext& ctx) {
void InstallInterfaces(SM::ServiceManager& service_manager,
std::shared_ptr<NVFlinger::NVFlinger> nvflinger) {
auto message_queue = std::make_shared<AppletMessageQueue>();
- message_queue->PushMessage(
- AppletMessageQueue::AppletMessage::FocusStateChanged); // Needed on game boot
+ message_queue->PushMessage(AppletMessageQueue::AppletMessage::FocusStateChanged); // Needed on
+ // game boot
std::make_shared<AppletAE>(nvflinger, message_queue)->InstallAsService(service_manager);
std::make_shared<AppletOE>(nvflinger, message_queue)->InstallAsService(service_manager);
diff --git a/src/core/hle/service/am/am.h b/src/core/hle/service/am/am.h
index 2f1c20bce..44c1bcde5 100644
--- a/src/core/hle/service/am/am.h
+++ b/src/core/hle/service/am/am.h
@@ -155,6 +155,34 @@ private:
std::shared_ptr<AppletMessageQueue> msg_queue;
};
+class IStorage final : public ServiceFramework<IStorage> {
+public:
+ explicit IStorage(std::vector<u8> buffer);
+ ~IStorage() override;
+
+ const std::vector<u8>& GetData() const;
+
+private:
+ void Open(Kernel::HLERequestContext& ctx);
+
+ std::vector<u8> buffer;
+
+ friend class IStorageAccessor;
+};
+
+class IStorageAccessor final : public ServiceFramework<IStorageAccessor> {
+public:
+ explicit IStorageAccessor(IStorage& backing);
+ ~IStorageAccessor() override;
+
+private:
+ void GetSize(Kernel::HLERequestContext& ctx);
+ void Write(Kernel::HLERequestContext& ctx);
+ void Read(Kernel::HLERequestContext& ctx);
+
+ IStorage& backing;
+};
+
class ILibraryAppletCreator final : public ServiceFramework<ILibraryAppletCreator> {
public:
ILibraryAppletCreator();
@@ -163,6 +191,7 @@ public:
private:
void CreateLibraryApplet(Kernel::HLERequestContext& ctx);
void CreateStorage(Kernel::HLERequestContext& ctx);
+ void CreateTransferMemoryStorage(Kernel::HLERequestContext& ctx);
};
class IApplicationFunctions final : public ServiceFramework<IApplicationFunctions> {
@@ -185,6 +214,7 @@ private:
void EndBlockingHomeButtonShortAndLongPressed(Kernel::HLERequestContext& ctx);
void BeginBlockingHomeButton(Kernel::HLERequestContext& ctx);
void EndBlockingHomeButton(Kernel::HLERequestContext& ctx);
+ void EnableApplicationCrashReport(Kernel::HLERequestContext& ctx);
};
class IHomeMenuFunctions final : public ServiceFramework<IHomeMenuFunctions> {
diff --git a/src/core/hle/service/am/applets/applets.cpp b/src/core/hle/service/am/applets/applets.cpp
new file mode 100644
index 000000000..becbadd06
--- /dev/null
+++ b/src/core/hle/service/am/applets/applets.cpp
@@ -0,0 +1,113 @@
+// Copyright 2018 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include <cstring>
+#include "common/assert.h"
+#include "core/core.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<IStorage> AppletDataBroker::PopNormalDataToGame() {
+ if (out_channel.empty())
+ return nullptr;
+
+ auto out = std::move(out_channel.front());
+ out_channel.pop();
+ return out;
+}
+
+std::unique_ptr<IStorage> AppletDataBroker::PopNormalDataToApplet() {
+ if (in_channel.empty())
+ return nullptr;
+
+ auto out = std::move(in_channel.front());
+ in_channel.pop();
+ return out;
+}
+
+std::unique_ptr<IStorage> 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<IStorage> 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<IStorage>(storage));
+}
+
+void AppletDataBroker::PushNormalDataFromApplet(IStorage storage) {
+ out_channel.push(std::make_unique<IStorage>(storage));
+ pop_out_data_event->Signal();
+}
+
+void AppletDataBroker::PushInteractiveDataFromGame(IStorage storage) {
+ in_interactive_channel.push(std::make_unique<IStorage>(storage));
+}
+
+void AppletDataBroker::PushInteractiveDataFromApplet(IStorage storage) {
+ out_interactive_channel.push(std::make_unique<IStorage>(storage));
+ pop_interactive_out_data_event->Signal();
+}
+
+void AppletDataBroker::SignalStateChanged() const {
+ state_changed_event->Signal();
+}
+
+Kernel::SharedPtr<Kernel::Event> AppletDataBroker::GetNormalDataEvent() const {
+ return pop_out_data_event;
+}
+
+Kernel::SharedPtr<Kernel::Event> AppletDataBroker::GetInteractiveDataEvent() const {
+ return pop_interactive_out_data_event;
+}
+
+Kernel::SharedPtr<Kernel::Event> AppletDataBroker::GetStateChangedEvent() const {
+ return state_changed_event;
+}
+
+Applet::Applet() = default;
+
+Applet::~Applet() = default;
+
+void Applet::Initialize() {
+ 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));
+
+ initialized = true;
+}
+
+} // namespace Service::AM::Applets
diff --git a/src/core/hle/service/am/applets/applets.h b/src/core/hle/service/am/applets/applets.h
new file mode 100644
index 000000000..f65ea119c
--- /dev/null
+++ b/src/core/hle/service/am/applets/applets.h
@@ -0,0 +1,112 @@
+// Copyright 2018 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <memory>
+#include <queue>
+#include "common/swap.h"
+#include "core/hle/kernel/kernel.h"
+
+union ResultCode;
+
+namespace Kernel {
+class Event;
+}
+
+namespace Service::AM {
+
+class IStorage;
+
+namespace Applets {
+
+class AppletDataBroker final {
+public:
+ AppletDataBroker();
+ ~AppletDataBroker();
+
+ std::unique_ptr<IStorage> PopNormalDataToGame();
+ std::unique_ptr<IStorage> PopNormalDataToApplet();
+
+ std::unique_ptr<IStorage> PopInteractiveDataToGame();
+ std::unique_ptr<IStorage> PopInteractiveDataToApplet();
+
+ void PushNormalDataFromGame(IStorage storage);
+ void PushNormalDataFromApplet(IStorage storage);
+
+ void PushInteractiveDataFromGame(IStorage storage);
+ void PushInteractiveDataFromApplet(IStorage storage);
+
+ void SignalStateChanged() const;
+
+ Kernel::SharedPtr<Kernel::Event> GetNormalDataEvent() const;
+ Kernel::SharedPtr<Kernel::Event> GetInteractiveDataEvent() const;
+ Kernel::SharedPtr<Kernel::Event> GetStateChangedEvent() const;
+
+private:
+ // Queues are named from applet's perspective
+
+ // PopNormalDataToApplet and PushNormalDataFromGame
+ std::queue<std::unique_ptr<IStorage>> in_channel;
+
+ // PopNormalDataToGame and PushNormalDataFromApplet
+ std::queue<std::unique_ptr<IStorage>> out_channel;
+
+ // PopInteractiveDataToApplet and PushInteractiveDataFromGame
+ std::queue<std::unique_ptr<IStorage>> in_interactive_channel;
+
+ // PopInteractiveDataToGame and PushInteractiveDataFromApplet
+ std::queue<std::unique_ptr<IStorage>> out_interactive_channel;
+
+ Kernel::SharedPtr<Kernel::Event> state_changed_event;
+
+ // Signaled on PushNormalDataFromApplet
+ Kernel::SharedPtr<Kernel::Event> pop_out_data_event;
+
+ // Signaled on PushInteractiveDataFromApplet
+ Kernel::SharedPtr<Kernel::Event> pop_interactive_out_data_event;
+};
+
+class Applet {
+public:
+ Applet();
+ virtual ~Applet();
+
+ virtual void Initialize();
+
+ virtual bool TransactionComplete() const = 0;
+ virtual ResultCode GetStatus() const = 0;
+ virtual void ExecuteInteractive() = 0;
+ virtual void Execute() = 0;
+
+ bool IsInitialized() const {
+ return initialized;
+ }
+
+ AppletDataBroker& GetBroker() {
+ return broker;
+ }
+
+ const AppletDataBroker& GetBroker() const {
+ return broker;
+ }
+
+protected:
+ struct CommonArguments {
+ u32_le arguments_version;
+ u32_le size;
+ u32_le library_version;
+ u32_le theme_color;
+ u8 play_startup_sound;
+ u64_le system_tick;
+ };
+ static_assert(sizeof(CommonArguments) == 0x20, "CommonArguments has incorrect size.");
+
+ CommonArguments common_args{};
+ AppletDataBroker broker;
+ bool initialized = false;
+};
+
+} // namespace Applets
+} // namespace Service::AM
diff --git a/src/core/hle/service/am/applets/software_keyboard.cpp b/src/core/hle/service/am/applets/software_keyboard.cpp
new file mode 100644
index 000000000..981bdec51
--- /dev/null
+++ b/src/core/hle/service/am/applets/software_keyboard.cpp
@@ -0,0 +1,161 @@
+// Copyright 2018 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include <cstring>
+#include "common/assert.h"
+#include "common/string_util.h"
+#include "core/core.h"
+#include "core/frontend/applets/software_keyboard.h"
+#include "core/hle/service/am/am.h"
+#include "core/hle/service/am/applets/software_keyboard.h"
+
+namespace Service::AM::Applets {
+
+constexpr std::size_t SWKBD_OUTPUT_BUFFER_SIZE = 0x7D8;
+constexpr std::size_t SWKBD_OUTPUT_INTERACTIVE_BUFFER_SIZE = 0x7D4;
+constexpr std::size_t DEFAULT_MAX_LENGTH = 500;
+constexpr bool INTERACTIVE_STATUS_OK = false;
+
+static Core::Frontend::SoftwareKeyboardParameters ConvertToFrontendParameters(
+ KeyboardConfig config, std::u16string initial_text) {
+ Core::Frontend::SoftwareKeyboardParameters params{};
+
+ params.submit_text = Common::UTF16StringFromFixedZeroTerminatedBuffer(
+ config.submit_text.data(), config.submit_text.size());
+ params.header_text = Common::UTF16StringFromFixedZeroTerminatedBuffer(
+ config.header_text.data(), config.header_text.size());
+ params.sub_text = Common::UTF16StringFromFixedZeroTerminatedBuffer(config.sub_text.data(),
+ config.sub_text.size());
+ params.guide_text = Common::UTF16StringFromFixedZeroTerminatedBuffer(config.guide_text.data(),
+ config.guide_text.size());
+ params.initial_text = initial_text;
+ params.max_length = config.length_limit == 0 ? DEFAULT_MAX_LENGTH : config.length_limit;
+ params.password = static_cast<bool>(config.is_password);
+ params.cursor_at_beginning = static_cast<bool>(config.initial_cursor_position);
+ params.value = static_cast<u8>(config.keyset_disable_bitmask);
+
+ return params;
+}
+
+SoftwareKeyboard::SoftwareKeyboard() = default;
+
+SoftwareKeyboard::~SoftwareKeyboard() = default;
+
+void SoftwareKeyboard::Initialize() {
+ complete = false;
+ initial_text.clear();
+ final_data.clear();
+
+ Applet::Initialize();
+
+ 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 = broker.PopNormalDataToApplet();
+ ASSERT(work_buffer_storage != nullptr);
+ const auto& work_buffer = work_buffer_storage->GetData();
+
+ if (config.initial_string_size == 0)
+ return;
+
+ std::vector<char16_t> string(config.initial_string_size);
+ std::memcpy(string.data(), work_buffer.data() + config.initial_string_offset,
+ string.size() * 2);
+ initial_text = Common::UTF16StringFromFixedZeroTerminatedBuffer(string.data(), string.size());
+}
+
+bool SoftwareKeyboard::TransactionComplete() const {
+ return complete;
+}
+
+ResultCode SoftwareKeyboard::GetStatus() const {
+ return RESULT_SUCCESS;
+}
+
+void SoftwareKeyboard::ExecuteInteractive() {
+ if (complete)
+ return;
+
+ const auto storage = broker.PopInteractiveDataToApplet();
+ ASSERT(storage != nullptr);
+ const auto data = storage->GetData();
+ const auto status = static_cast<bool>(data[0]);
+
+ if (status == INTERACTIVE_STATUS_OK) {
+ complete = true;
+ } else {
+ const auto& frontend{Core::System::GetInstance().GetSoftwareKeyboard()};
+
+ std::array<char16_t, SWKBD_OUTPUT_INTERACTIVE_BUFFER_SIZE / 2 - 2> string;
+ std::memcpy(string.data(), data.data() + 4, string.size() * 2);
+ frontend.SendTextCheckDialog(
+ Common::UTF16StringFromFixedZeroTerminatedBuffer(string.data(), string.size()),
+ [this] { broker.SignalStateChanged(); });
+ }
+}
+
+void SoftwareKeyboard::Execute() {
+ if (complete) {
+ broker.PushNormalDataFromApplet(IStorage{final_data});
+ return;
+ }
+
+ const auto& frontend{Core::System::GetInstance().GetSoftwareKeyboard()};
+
+ const auto parameters = ConvertToFrontendParameters(config, initial_text);
+
+ frontend.RequestText([this](std::optional<std::u16string> text) { WriteText(text); },
+ parameters);
+}
+
+void SoftwareKeyboard::WriteText(std::optional<std::u16string> text) {
+ std::vector<u8> output_main(SWKBD_OUTPUT_BUFFER_SIZE);
+
+ if (text.has_value()) {
+ std::vector<u8> output_sub(SWKBD_OUTPUT_BUFFER_SIZE);
+
+ if (config.utf_8) {
+ const u64 size = text->size() + 8;
+ const auto new_text = Common::UTF16ToUTF8(*text);
+
+ std::memcpy(output_sub.data(), &size, sizeof(u64));
+ std::memcpy(output_sub.data() + 8, new_text.data(),
+ std::min(new_text.size(), SWKBD_OUTPUT_BUFFER_SIZE - 8));
+
+ output_main[0] = INTERACTIVE_STATUS_OK;
+ std::memcpy(output_main.data() + 4, new_text.data(),
+ std::min(new_text.size(), SWKBD_OUTPUT_BUFFER_SIZE - 4));
+ } else {
+ const u64 size = text->size() * 2 + 8;
+ std::memcpy(output_sub.data(), &size, sizeof(u64));
+ std::memcpy(output_sub.data() + 8, text->data(),
+ std::min(text->size() * 2, SWKBD_OUTPUT_BUFFER_SIZE - 8));
+
+ output_main[0] = INTERACTIVE_STATUS_OK;
+ std::memcpy(output_main.data() + 4, text->data(),
+ std::min(text->size() * 2, SWKBD_OUTPUT_BUFFER_SIZE - 4));
+ }
+
+ complete = !config.text_check;
+ final_data = output_main;
+
+ if (complete) {
+ broker.PushNormalDataFromApplet(IStorage{output_main});
+ } else {
+ broker.PushInteractiveDataFromApplet(IStorage{output_sub});
+ }
+
+ broker.SignalStateChanged();
+ } else {
+ output_main[0] = 1;
+ complete = true;
+ 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
new file mode 100644
index 000000000..efd5753a1
--- /dev/null
+++ b/src/core/hle/service/am/applets/software_keyboard.h
@@ -0,0 +1,74 @@
+// Copyright 2018 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <array>
+#include <string>
+#include <vector>
+
+#include "common/common_funcs.h"
+#include "common/swap.h"
+#include "core/hle/service/am/am.h"
+#include "core/hle/service/am/applets/applets.h"
+
+namespace Service::AM::Applets {
+
+enum class KeysetDisable : u32 {
+ Space = 0x02,
+ Address = 0x04,
+ Percent = 0x08,
+ Slashes = 0x10,
+ Numbers = 0x40,
+ DownloadCode = 0x80,
+};
+
+struct KeyboardConfig {
+ INSERT_PADDING_BYTES(4);
+ std::array<char16_t, 9> submit_text;
+ u16_le left_symbol_key;
+ u16_le right_symbol_key;
+ INSERT_PADDING_BYTES(1);
+ KeysetDisable keyset_disable_bitmask;
+ u32_le initial_cursor_position;
+ std::array<char16_t, 65> header_text;
+ std::array<char16_t, 129> sub_text;
+ std::array<char16_t, 257> guide_text;
+ u32_le length_limit;
+ INSERT_PADDING_BYTES(4);
+ u32_le is_password;
+ INSERT_PADDING_BYTES(5);
+ bool utf_8;
+ bool draw_background;
+ u32_le initial_string_offset;
+ u32_le initial_string_size;
+ u32_le user_dictionary_offset;
+ u32_le user_dictionary_size;
+ bool text_check;
+ u64_le text_check_callback;
+};
+static_assert(sizeof(KeyboardConfig) == 0x3E0, "KeyboardConfig has incorrect size.");
+
+class SoftwareKeyboard final : public Applet {
+public:
+ SoftwareKeyboard();
+ ~SoftwareKeyboard() override;
+
+ void Initialize() override;
+
+ bool TransactionComplete() const override;
+ ResultCode GetStatus() const override;
+ void ExecuteInteractive() override;
+ void Execute() override;
+
+ void WriteText(std::optional<std::u16string> text);
+
+private:
+ KeyboardConfig config;
+ std::u16string initial_text;
+ bool complete = false;
+ std::vector<u8> final_data;
+};
+
+} // namespace Service::AM::Applets
diff --git a/src/core/hle/service/am/applets/stub_applet.cpp b/src/core/hle/service/am/applets/stub_applet.cpp
new file mode 100644
index 000000000..ed166b87d
--- /dev/null
+++ b/src/core/hle/service/am/applets/stub_applet.cpp
@@ -0,0 +1,70 @@
+// Copyright 2018 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include <string>
+
+#include "common/hex_util.h"
+#include "common/logging/log.h"
+#include "core/hle/result.h"
+#include "core/hle/service/am/am.h"
+#include "core/hle/service/am/applets/stub_applet.h"
+
+namespace Service::AM::Applets {
+
+static void LogCurrentStorage(AppletDataBroker& broker, std::string prefix) {
+ std::unique_ptr<IStorage> storage = broker.PopNormalDataToApplet();
+ for (; storage != nullptr; storage = broker.PopNormalDataToApplet()) {
+ const auto data = storage->GetData();
+ LOG_INFO(Service_AM,
+ "called (STUBBED), during {} recieved normal data with size={:08X}, data={}",
+ prefix, data.size(), Common::HexVectorToString(data));
+ }
+
+ storage = broker.PopInteractiveDataToApplet();
+ for (; storage != nullptr; storage = broker.PopInteractiveDataToApplet()) {
+ const auto data = storage->GetData();
+ LOG_INFO(Service_AM,
+ "called (STUBBED), during {} recieved interactive data with size={:08X}, data={}",
+ prefix, data.size(), Common::HexVectorToString(data));
+ }
+}
+
+StubApplet::StubApplet() = default;
+
+StubApplet::~StubApplet() = default;
+
+void StubApplet::Initialize() {
+ LOG_WARNING(Service_AM, "called (STUBBED)");
+ Applet::Initialize();
+ LogCurrentStorage(broker, "Initialize");
+}
+
+bool StubApplet::TransactionComplete() const {
+ LOG_WARNING(Service_AM, "called (STUBBED)");
+ return true;
+}
+
+ResultCode StubApplet::GetStatus() const {
+ LOG_WARNING(Service_AM, "called (STUBBED)");
+ return RESULT_SUCCESS;
+}
+
+void StubApplet::ExecuteInteractive() {
+ LOG_WARNING(Service_AM, "called (STUBBED)");
+ LogCurrentStorage(broker, "ExecuteInteractive");
+
+ broker.PushNormalDataFromApplet(IStorage{std::vector<u8>(0x1000)});
+ broker.PushInteractiveDataFromApplet(IStorage{std::vector<u8>(0x1000)});
+ broker.SignalStateChanged();
+}
+
+void StubApplet::Execute() {
+ LOG_WARNING(Service_AM, "called (STUBBED)");
+ LogCurrentStorage(broker, "Execute");
+
+ broker.PushNormalDataFromApplet(IStorage{std::vector<u8>(0x1000)});
+ broker.PushInteractiveDataFromApplet(IStorage{std::vector<u8>(0x1000)});
+ broker.SignalStateChanged();
+}
+} // namespace Service::AM::Applets
diff --git a/src/core/hle/service/am/applets/stub_applet.h b/src/core/hle/service/am/applets/stub_applet.h
new file mode 100644
index 000000000..7d8dc968d
--- /dev/null
+++ b/src/core/hle/service/am/applets/stub_applet.h
@@ -0,0 +1,24 @@
+// Copyright 2018 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "core/hle/service/am/applets/applets.h"
+
+namespace Service::AM::Applets {
+
+class StubApplet final : public Applet {
+public:
+ StubApplet();
+ ~StubApplet() override;
+
+ void Initialize() override;
+
+ bool TransactionComplete() const override;
+ ResultCode GetStatus() const override;
+ void ExecuteInteractive() override;
+ void Execute() override;
+};
+
+} // namespace Service::AM::Applets