From c108da5e026b293d7efab583224fd7dd98be9bcf Mon Sep 17 00:00:00 2001 From: wheremyfoodat <44909372+wheremyfoodat@users.noreply.github.com> Date: Wed, 24 Jan 2024 18:51:44 +0200 Subject: [PATCH 1/9] Moar appletting --- include/applets/applet.hpp | 9 ++++--- include/kernel/handles.hpp | 1 + include/memory.hpp | 3 ++- include/services/gsp_gpu.hpp | 18 +++++++++++++ src/core/applets/mii_selector.cpp | 12 +++++++++ src/core/applets/software_keyboard.cpp | 3 +++ src/core/kernel/memory_management.cpp | 1 + src/core/services/apt.cpp | 4 +-- src/core/services/gsp_gpu.cpp | 36 ++++++++++++++++++++++++-- 9 files changed, 78 insertions(+), 9 deletions(-) diff --git a/include/applets/applet.hpp b/include/applets/applet.hpp index 79fba1cb..8087ab82 100644 --- a/include/applets/applet.hpp +++ b/include/applets/applet.hpp @@ -65,10 +65,11 @@ namespace Applets { }; struct Parameter { - u32 senderID; - u32 destID; - u32 signal; - std::vector data; + u32 senderID; // ID of the parameter sender + u32 destID; // ID of the app to receive parameter + u32 signal; // Signal type (eg request) + u32 object; // Some applets will also respond with shared memory handles for transferring data between the sender and called + std::vector data; // Misc data }; class AppletBase { diff --git a/include/kernel/handles.hpp b/include/kernel/handles.hpp index b038049f..fe746b65 100644 --- a/include/kernel/handles.hpp +++ b/include/kernel/handles.hpp @@ -53,6 +53,7 @@ namespace KernelHandles { GSPSharedMemHandle = MaxServiceHandle + 1, // Handle for the GSP shared memory FontSharedMemHandle, CSNDSharedMemHandle, + APTCaptureSharedMemHandle, // Shared memory for display capture info, HIDSharedMemHandle, MinSharedMemHandle = GSPSharedMemHandle, diff --git a/include/memory.hpp b/include/memory.hpp index 3ddd00cc..640ae5f0 100644 --- a/include/memory.hpp +++ b/include/memory.hpp @@ -112,11 +112,12 @@ class Memory { // This tracks our OS' memory allocations std::vector memoryInfo; - std::array sharedMemBlocks = { + std::array sharedMemBlocks = { SharedMemoryBlock(0, 0, KernelHandles::FontSharedMemHandle), // Shared memory for the system font (size is 0 because we read the size from the cmrc filesystem SharedMemoryBlock(0, 0x1000, KernelHandles::GSPSharedMemHandle), // GSP shared memory SharedMemoryBlock(0, 0x1000, KernelHandles::HIDSharedMemHandle), // HID shared memory SharedMemoryBlock(0, 0x3000, KernelHandles::CSNDSharedMemHandle), // CSND shared memory + SharedMemoryBlock(0, 0xE7000, KernelHandles::APTCaptureSharedMemHandle), // APT Capture Buffer memory }; public: diff --git a/include/services/gsp_gpu.hpp b/include/services/gsp_gpu.hpp index 68e24580..0a8f8a0a 100644 --- a/include/services/gsp_gpu.hpp +++ b/include/services/gsp_gpu.hpp @@ -60,6 +60,15 @@ class GPUService { }; static_assert(sizeof(FramebufferUpdate) == 64, "GSP::GPU::FramebufferUpdate has the wrong size"); + // Used for saving and restoring GPU state via ImportDisplayCaptureInfo + struct CaptureInfo { + u32 leftFramebuffer; // Left framebuffer VA + u32 rightFramebuffer; // Right framebuffer VA (Top screen only) + u32 format; + u32 stride; + }; + static_assert(sizeof(CaptureInfo) == 16, "GSP::GPU::CaptureInfo has the wrong size"); + // Service commands void acquireRight(u32 messagePointer); void flushDataCache(u32 messagePointer); @@ -86,6 +95,15 @@ class GPUService { void setBufferSwapImpl(u32 screen_id, const FramebufferInfo& info); + // Get the framebuffer info in shared memory for a given screen + FramebufferUpdate* getFramebufferInfo(int screen) { + // TODO: Offset depends on GSP thread being triggered + return reinterpret_cast(&sharedMem[0x200 + screen * sizeof(FramebufferUpdate)]); + } + + FramebufferUpdate* getTopFramebufferInfo() { return getFramebufferInfo(0); } + FramebufferUpdate* getBottomFramebufferInfo() { return getFramebufferInfo(1); } + public: GPUService(Memory& mem, GPU& gpu, Kernel& kernel, u32& currentPID) : mem(mem), gpu(gpu), kernel(kernel), currentPID(currentPID) {} diff --git a/src/core/applets/mii_selector.cpp b/src/core/applets/mii_selector.cpp index f392d846..6a06a01a 100644 --- a/src/core/applets/mii_selector.cpp +++ b/src/core/applets/mii_selector.cpp @@ -1,5 +1,7 @@ #include "applets/mii_selector.hpp" +#include "kernel/handles.hpp" + using namespace Applets; void MiiSelectorApplet::reset() {} @@ -7,5 +9,15 @@ Result::HorizonResult MiiSelectorApplet::start() { return Result::Success; } Result::HorizonResult MiiSelectorApplet::receiveParameter(const Applets::Parameter& parameter) { Helpers::warn("Mii Selector: Unimplemented ReceiveParameter"); + + Applets::Parameter param = Applets::Parameter{ + .senderID = parameter.destID, + .destID = AppletIDs::Application, + .signal = static_cast(APTSignal::Response), + .object = KernelHandles::APTCaptureSharedMemHandle, + .data = {}, + }; + + nextParameter = param; return Result::Success; } \ No newline at end of file diff --git a/src/core/applets/software_keyboard.cpp b/src/core/applets/software_keyboard.cpp index be5a9e21..fd5f4afe 100644 --- a/src/core/applets/software_keyboard.cpp +++ b/src/core/applets/software_keyboard.cpp @@ -1,5 +1,7 @@ #include "applets/software_keyboard.hpp" +#include "kernel/handles.hpp" + using namespace Applets; void SoftwareKeyboardApplet::reset() {} @@ -12,6 +14,7 @@ Result::HorizonResult SoftwareKeyboardApplet::receiveParameter(const Applets::Pa .senderID = parameter.destID, .destID = AppletIDs::Application, .signal = static_cast(APTSignal::Response), + .object = KernelHandles::APTCaptureSharedMemHandle, .data = {}, }; diff --git a/src/core/kernel/memory_management.cpp b/src/core/kernel/memory_management.cpp index e90a5697..7acc749b 100644 --- a/src/core/kernel/memory_management.cpp +++ b/src/core/kernel/memory_management.cpp @@ -144,6 +144,7 @@ void Kernel::mapMemoryBlock() { printf("Mapping CSND memory block\n"); break; + case KernelHandles::APTCaptureSharedMemHandle: break; default: Helpers::panic("Mapping unknown shared memory block: %X", block); } } else { diff --git a/src/core/services/apt.cpp b/src/core/services/apt.cpp index 830b8377..25d063c1 100644 --- a/src/core/services/apt.cpp +++ b/src/core/services/apt.cpp @@ -274,7 +274,7 @@ void APTService::receiveParameter(u32 messagePointer) { // Size of parameter data mem.write32(messagePointer + 16, parameter.data.size()); mem.write32(messagePointer + 20, 0x10); - mem.write32(messagePointer + 24, 0); + mem.write32(messagePointer + 24, parameter.object); mem.write32(messagePointer + 28, 0); } @@ -296,7 +296,7 @@ void APTService::glanceParameter(u32 messagePointer) { // Size of parameter data mem.write32(messagePointer + 16, parameter.data.size()); mem.write32(messagePointer + 20, 0); - mem.write32(messagePointer + 24, 0); + mem.write32(messagePointer + 24, parameter.object); mem.write32(messagePointer + 28, 0); } diff --git a/src/core/services/gsp_gpu.cpp b/src/core/services/gsp_gpu.cpp index 861dfb0a..a698f7cc 100644 --- a/src/core/services/gsp_gpu.cpp +++ b/src/core/services/gsp_gpu.cpp @@ -143,8 +143,7 @@ void GPUService::requestInterrupt(GPUInterrupt type) { // Not emulating this causes Yoshi's Wooly World, Captain Toad, Metroid 2 et al to hang if (type == GPUInterrupt::VBlank0 || type == GPUInterrupt::VBlank1) { int screen = static_cast(type) - static_cast(GPUInterrupt::VBlank0); // 0 for top screen, 1 for bottom - // TODO: Offset depends on GSP thread being triggered - FramebufferUpdate* update = reinterpret_cast(&sharedMem[0x200 + screen * sizeof(FramebufferUpdate)]); + FramebufferUpdate* update = getFramebufferInfo(screen); if (update->dirtyFlag & 1) { setBufferSwapImpl(screen, update->framebufferInfo[update->index]); @@ -488,4 +487,37 @@ void GPUService::importDisplayCaptureInfo(u32 messagePointer) { mem.write32(messagePointer, IPC::responseHeader(0x18, 9, 0)); mem.write32(messagePointer + 4, Result::Success); + + if (sharedMem == nullptr) { + Helpers::warn("GSP::GPU::ImportDisplayCaptureInfo called without GSP module being properly initialized!"); + return; + } + + FramebufferUpdate* topScreen = getTopFramebufferInfo(); + FramebufferUpdate* bottomScreen = getBottomFramebufferInfo(); + + // Capture the relevant data for both screens and return them to the caller + CaptureInfo topScreenCapture = { + .leftFramebuffer = topScreen->framebufferInfo[topScreen->index].leftFramebufferVaddr, + .rightFramebuffer = topScreen->framebufferInfo[topScreen->index].rightFramebufferVaddr, + .format = topScreen->framebufferInfo[topScreen->index].format, + .stride = topScreen->framebufferInfo[topScreen->index].stride, + }; + + CaptureInfo bottomScreenCapture = { + .leftFramebuffer = bottomScreen->framebufferInfo[bottomScreen->index].leftFramebufferVaddr, + .rightFramebuffer = bottomScreen->framebufferInfo[bottomScreen->index].rightFramebufferVaddr, + .format = bottomScreen->framebufferInfo[bottomScreen->index].format, + .stride = bottomScreen->framebufferInfo[bottomScreen->index].stride, + }; + + mem.write32(messagePointer + 8, topScreenCapture.leftFramebuffer); + mem.write32(messagePointer + 12, topScreenCapture.rightFramebuffer); + mem.write32(messagePointer + 16, topScreenCapture.format); + mem.write32(messagePointer + 20, topScreenCapture.stride); + + mem.write32(messagePointer + 24, bottomScreenCapture.leftFramebuffer); + mem.write32(messagePointer + 28, bottomScreenCapture.rightFramebuffer); + mem.write32(messagePointer + 32, bottomScreenCapture.format); + mem.write32(messagePointer + 36, bottomScreenCapture.stride); } \ No newline at end of file From 85a2ca1bf4de5698a026a937393ffc6923979bb3 Mon Sep 17 00:00:00 2001 From: wheremyfoodat <44909372+wheremyfoodat@users.noreply.github.com> Date: Wed, 24 Jan 2024 19:04:55 +0200 Subject: [PATCH 2/9] Stub svcUnmapMemoryBlock --- include/kernel/kernel.hpp | 1 + src/core/kernel/kernel.cpp | 1 + src/core/kernel/memory_management.cpp | 9 +++++++++ 3 files changed, 11 insertions(+) diff --git a/include/kernel/kernel.hpp b/include/kernel/kernel.hpp index 217dd148..d35d9031 100644 --- a/include/kernel/kernel.hpp +++ b/include/kernel/kernel.hpp @@ -137,6 +137,7 @@ public: void duplicateHandle(); void exitThread(); void mapMemoryBlock(); + void unmapMemoryBlock(); void queryMemory(); void getCurrentProcessorNumber(); void getProcessID(); diff --git a/src/core/kernel/kernel.cpp b/src/core/kernel/kernel.cpp index 2e4fffff..392b87fd 100644 --- a/src/core/kernel/kernel.cpp +++ b/src/core/kernel/kernel.cpp @@ -50,6 +50,7 @@ void Kernel::serviceSVC(u32 svc) { case 0x1D: svcClearTimer(); break; case 0x1E: createMemoryBlock(); break; case 0x1F: mapMemoryBlock(); break; + case 0x20: unmapMemoryBlock(); break; case 0x21: createAddressArbiter(); break; case 0x22: arbitrateAddress(); break; case 0x23: svcCloseHandle(); break; diff --git a/src/core/kernel/memory_management.cpp b/src/core/kernel/memory_management.cpp index 7acc749b..0d234be5 100644 --- a/src/core/kernel/memory_management.cpp +++ b/src/core/kernel/memory_management.cpp @@ -207,3 +207,12 @@ void Kernel::createMemoryBlock() { regs[0] = Result::Success; regs[1] = makeMemoryBlock(addr, size, myPermission, otherPermission); } + +void Kernel::unmapMemoryBlock() { + Handle block = regs[0]; + u32 addr = regs[1]; + logSVC("Unmap memory block (block handle = %X, addr = %08X)\n", block, addr); + + Helpers::warn("Stubbed svcUnmapMemoryBlock!"); + regs[0] = Result::Success; +} From 5fe1637e172771b5f870961efdc7277818427c60 Mon Sep 17 00:00:00 2001 From: wheremyfoodat <44909372+wheremyfoodat@users.noreply.github.com> Date: Wed, 24 Jan 2024 20:28:16 +0200 Subject: [PATCH 3/9] Add APT::StartLibraryApplet --- include/applets/applet.hpp | 15 +++++---- include/applets/mii_selector.hpp | 2 +- include/applets/software_keyboard.hpp | 2 +- src/core/applets/mii_selector.cpp | 2 +- src/core/applets/software_keyboard.cpp | 27 ++++++++++------ src/core/services/apt.cpp | 44 ++++++++++++++++++++++++-- 6 files changed, 71 insertions(+), 21 deletions(-) diff --git a/include/applets/applet.hpp b/include/applets/applet.hpp index 8087ab82..98fb6c20 100644 --- a/include/applets/applet.hpp +++ b/include/applets/applet.hpp @@ -1,6 +1,9 @@ #pragma once +#include + #include "helpers.hpp" +#include "kernel/kernel_types.hpp" #include "memory.hpp" #include "result/result.hpp" @@ -65,11 +68,11 @@ namespace Applets { }; struct Parameter { - u32 senderID; // ID of the parameter sender - u32 destID; // ID of the app to receive parameter - u32 signal; // Signal type (eg request) - u32 object; // Some applets will also respond with shared memory handles for transferring data between the sender and called - std::vector data; // Misc data + u32 senderID; // ID of the parameter sender + u32 destID; // ID of the app to receive parameter + u32 signal; // Signal type (eg request) + u32 object; // Some applets will also respond with shared memory handles for transferring data between the sender and called + std::vector data; // Misc data }; class AppletBase { @@ -81,7 +84,7 @@ namespace Applets { virtual const char* name() = 0; // Called by APT::StartLibraryApplet and similar - virtual Result::HorizonResult start() = 0; + virtual Result::HorizonResult start(const MemoryBlock& sharedMem, const std::vector& parameters) = 0; // Transfer parameters from application -> applet virtual Result::HorizonResult receiveParameter(const Parameter& parameter) = 0; virtual void reset() = 0; diff --git a/include/applets/mii_selector.hpp b/include/applets/mii_selector.hpp index c90d4b59..8bd757dd 100644 --- a/include/applets/mii_selector.hpp +++ b/include/applets/mii_selector.hpp @@ -4,7 +4,7 @@ namespace Applets { class MiiSelectorApplet final : public AppletBase { public: virtual const char* name() override { return "Mii Selector"; } - virtual Result::HorizonResult start() override; + virtual Result::HorizonResult start(const MemoryBlock& sharedMem, const std::vector& parameters) override; virtual Result::HorizonResult receiveParameter(const Applets::Parameter& parameter) override; virtual void reset() override; diff --git a/include/applets/software_keyboard.hpp b/include/applets/software_keyboard.hpp index f4179012..785ba20a 100644 --- a/include/applets/software_keyboard.hpp +++ b/include/applets/software_keyboard.hpp @@ -4,7 +4,7 @@ namespace Applets { class SoftwareKeyboardApplet final : public AppletBase { public: virtual const char* name() override { return "Software Keyboard"; } - virtual Result::HorizonResult start() override; + virtual Result::HorizonResult start(const MemoryBlock& sharedMem, const std::vector& parameters) override; virtual Result::HorizonResult receiveParameter(const Applets::Parameter& parameter) override; virtual void reset() override; diff --git a/src/core/applets/mii_selector.cpp b/src/core/applets/mii_selector.cpp index 6a06a01a..ed1c4ad2 100644 --- a/src/core/applets/mii_selector.cpp +++ b/src/core/applets/mii_selector.cpp @@ -5,7 +5,7 @@ using namespace Applets; void MiiSelectorApplet::reset() {} -Result::HorizonResult MiiSelectorApplet::start() { return Result::Success; } +Result::HorizonResult MiiSelectorApplet::start(const MemoryBlock& sharedMem, const std::vector& parameters) { return Result::Success; } Result::HorizonResult MiiSelectorApplet::receiveParameter(const Applets::Parameter& parameter) { Helpers::warn("Mii Selector: Unimplemented ReceiveParameter"); diff --git a/src/core/applets/software_keyboard.cpp b/src/core/applets/software_keyboard.cpp index fd5f4afe..07259d0d 100644 --- a/src/core/applets/software_keyboard.cpp +++ b/src/core/applets/software_keyboard.cpp @@ -5,19 +5,28 @@ using namespace Applets; void SoftwareKeyboardApplet::reset() {} -Result::HorizonResult SoftwareKeyboardApplet::start() { return Result::Success; } +Result::HorizonResult SoftwareKeyboardApplet::start(const MemoryBlock& sharedMem, const std::vector& parameters) { return Result::Success; } Result::HorizonResult SoftwareKeyboardApplet::receiveParameter(const Applets::Parameter& parameter) { Helpers::warn("Software keyboard: Unimplemented ReceiveParameter"); - Applets::Parameter param = Applets::Parameter{ - .senderID = parameter.destID, - .destID = AppletIDs::Application, - .signal = static_cast(APTSignal::Response), - .object = KernelHandles::APTCaptureSharedMemHandle, - .data = {}, - }; + switch (parameter.signal) { + // Signal == request -> Applet is asking swkbd for a shared memory handle for backing up the framebuffer before opening the applet + case u32(APTSignal::Request): { + Applets::Parameter param = Applets::Parameter{ + .senderID = parameter.destID, + .destID = AppletIDs::Application, + .signal = static_cast(APTSignal::Response), + .object = KernelHandles::APTCaptureSharedMemHandle, + .data = {}, + }; + + nextParameter = param; + break; + } + + default: Helpers::panic("SWKBD SIGNAL %d\n", parameter.signal); + } - nextParameter = param; return Result::Success; } \ No newline at end of file diff --git a/src/core/services/apt.cpp b/src/core/services/apt.cpp index 25d063c1..b2f3e849 100644 --- a/src/core/services/apt.cpp +++ b/src/core/services/apt.cpp @@ -64,6 +64,7 @@ void APTService::handleSyncRequest(u32 messagePointer) { case APTCommands::NotifyToWait: notifyToWait(messagePointer); break; case APTCommands::PreloadLibraryApplet: preloadLibraryApplet(messagePointer); break; case APTCommands::PrepareToStartLibraryApplet: prepareToStartLibraryApplet(messagePointer); break; + case APTCommands::StartLibraryApplet: startLibraryApplet(messagePointer); break; case APTCommands::ReceiveParameter: [[likely]] receiveParameter(messagePointer); break; case APTCommands::ReplySleepQuery: replySleepQuery(messagePointer); break; case APTCommands::SetApplicationCpuTimeLimit: setApplicationCpuTimeLimit(messagePointer); break; @@ -140,6 +141,43 @@ void APTService::prepareToStartLibraryApplet(u32 messagePointer) { mem.write32(messagePointer + 4, Result::Success); } +void APTService::startLibraryApplet(u32 messagePointer) { + const u32 appID = mem.read32(messagePointer + 4); + const u32 bufferSize = mem.read32(messagePointer + 8); + const Handle parameters = mem.read32(messagePointer + 16); + const u32 buffer = mem.read32(messagePointer + 24); + log("APT::StartLibraryApplet (app ID = %X)\n", appID); + + mem.write32(messagePointer, IPC::responseHeader(0x16, 1, 0)); + mem.write32(messagePointer + 4, Result::Success); + + Applets::AppletBase* destApplet = appletManager.getApplet(appID); + if (destApplet == nullptr) { + Helpers::warn("APT::StartLibraryApplet: Unimplemented dest applet ID"); + } else { + KernelObject* sharedMemObject = kernel.getObject(parameters); + if (sharedMemObject == nullptr) { + Helpers::warn("Couldn't find shared memory block\n"); + return; + } + + const MemoryBlock* sharedMem = sharedMemObject->getData(); + std::vector data; + data.reserve(bufferSize); + + for (u32 i = 0; i < bufferSize; i++) { + data.push_back(mem.read8(buffer + i)); + } + + Helpers::warn("DONE STARTED DAT STUFF"); + destApplet->start(*sharedMem, data); + + if (resumeEvent.has_value()) { + kernel.signalEvent(resumeEvent.value()); + } + } +} + void APTService::checkNew3DS(u32 messagePointer) { log("APT::CheckNew3DS\n"); mem.write32(messagePointer, IPC::responseHeader(0x102, 2, 0)); @@ -222,7 +260,7 @@ void APTService::sendParameter(u32 messagePointer) { const u32 parameterHandle = mem.read32(messagePointer + 24); // What dis? const u32 parameterPointer = mem.read32(messagePointer + 32); - log("APT::SendParameter (source app = %X, dest app = %X, cmd = %X, size = %X) (Stubbed)", sourceAppID, destAppID, cmd, paramSize); + log("APT::SendParameter (source app = %X, dest app = %X, cmd = %X, size = %X)", sourceAppID, destAppID, cmd, paramSize); mem.write32(messagePointer, IPC::responseHeader(0x0C, 1, 0)); mem.write32(messagePointer + 4, Result::Success); @@ -260,7 +298,7 @@ void APTService::sendParameter(u32 messagePointer) { void APTService::receiveParameter(u32 messagePointer) { const u32 app = mem.read32(messagePointer + 4); const u32 size = mem.read32(messagePointer + 8); - log("APT::ReceiveParameter(app ID = %X, size = %04X) (STUBBED)\n", app, size); + log("APT::ReceiveParameter(app ID = %X, size = %04X)\n", app, size); if (size > 0x1000) Helpers::panic("APT::ReceiveParameter with size > 0x1000"); auto parameter = appletManager.receiveParameter(); @@ -281,7 +319,7 @@ void APTService::receiveParameter(u32 messagePointer) { void APTService::glanceParameter(u32 messagePointer) { const u32 app = mem.read32(messagePointer + 4); const u32 size = mem.read32(messagePointer + 8); - log("APT::GlanceParameter(app ID = %X, size = %04X) (STUBBED)\n", app, size); + log("APT::GlanceParameter(app ID = %X, size = %04X)\n", app, size); if (size > 0x1000) Helpers::panic("APT::GlanceParameter with size > 0x1000"); auto parameter = appletManager.glanceParameter(); From d09254a8ae1d61160a20d3706b0815a7601f5c95 Mon Sep 17 00:00:00 2001 From: wheremyfoodat <44909372+wheremyfoodat@users.noreply.github.com> Date: Wed, 24 Jan 2024 21:47:18 +0200 Subject: [PATCH 4/9] Implement software keyboard kinda --- include/applets/applet.hpp | 2 +- include/applets/mii_selector.hpp | 2 +- include/applets/software_keyboard.hpp | 151 ++++++++++++++++++++++++- src/core/applets/mii_selector.cpp | 2 +- src/core/applets/software_keyboard.cpp | 44 ++++++- src/core/services/apt.cpp | 9 +- 6 files changed, 201 insertions(+), 9 deletions(-) diff --git a/include/applets/applet.hpp b/include/applets/applet.hpp index 98fb6c20..d9ba6143 100644 --- a/include/applets/applet.hpp +++ b/include/applets/applet.hpp @@ -84,7 +84,7 @@ namespace Applets { virtual const char* name() = 0; // Called by APT::StartLibraryApplet and similar - virtual Result::HorizonResult start(const MemoryBlock& sharedMem, const std::vector& parameters) = 0; + virtual Result::HorizonResult start(const MemoryBlock& sharedMem, const std::vector& parameters, u32 appID) = 0; // Transfer parameters from application -> applet virtual Result::HorizonResult receiveParameter(const Parameter& parameter) = 0; virtual void reset() = 0; diff --git a/include/applets/mii_selector.hpp b/include/applets/mii_selector.hpp index 8bd757dd..3b6cf2ec 100644 --- a/include/applets/mii_selector.hpp +++ b/include/applets/mii_selector.hpp @@ -4,7 +4,7 @@ namespace Applets { class MiiSelectorApplet final : public AppletBase { public: virtual const char* name() override { return "Mii Selector"; } - virtual Result::HorizonResult start(const MemoryBlock& sharedMem, const std::vector& parameters) override; + virtual Result::HorizonResult start(const MemoryBlock& sharedMem, const std::vector& parameters, u32 appID) override; virtual Result::HorizonResult receiveParameter(const Applets::Parameter& parameter) override; virtual void reset() override; diff --git a/include/applets/software_keyboard.hpp b/include/applets/software_keyboard.hpp index 785ba20a..3a167ad0 100644 --- a/include/applets/software_keyboard.hpp +++ b/include/applets/software_keyboard.hpp @@ -1,13 +1,162 @@ +#include + #include "applets/applet.hpp" +#include "swap.hpp" namespace Applets { + // Software keyboard definitions adapted from libctru/Citra + // Keyboard input filtering flags. Allows the caller to specify what input is explicitly not allowed + namespace SoftwareKeyboardFilter { + enum Filter : u32 { + Digits = 1, // Disallow the use of more than a certain number of digits (0 or more) + At = 1 << 1, // Disallow the use of the @ sign. + Percent = 1 << 2, // Disallow the use of the % sign. + Backslash = 1 << 3, // Disallow the use of the \ sign. + Profanity = 1 << 4, // Disallow profanity using Nintendo's profanity filter. + Callback = 1 << 5, // Use a callback in order to check the input. + }; + } // namespace SoftwareKeyboardFilter + + // Keyboard features. + namespace SoftwareKeyboardFeature { + enum Feature { + Parental = 1, // Parental PIN mode. + DarkenTopScreen = 1 << 1, // Darken the top screen when the keyboard is shown. + PredictiveInput = 1 << 2, // Enable predictive input (necessary for Kanji input in JPN systems). + Multiline = 1 << 3, // Enable multiline input. + FixedWidth = 1 << 4, // Enable fixed-width mode. + AllowHome = 1 << 5, // Allow the usage of the HOME button. + AllowReset = 1 << 6, // Allow the usage of a software-reset combination. + AllowPower = 1 << 7, // Allow the usage of the POWER button. + DefaultQWERTY = 1 << 9, // Default to the QWERTY page when the keyboard is shown. + }; + } // namespace SoftwareKeyboardFeature + class SoftwareKeyboardApplet final : public AppletBase { public: + static constexpr int MAX_BUTTON = 3; // Maximum number of buttons that can be in the keyboard. + static constexpr int MAX_BUTTON_TEXT_LEN = 16; // Maximum button text length, in UTF-16 code units. + static constexpr int MAX_HINT_TEXT_LEN = 64; // Maximum hint text length, in UTF-16 code units. + static constexpr int MAX_CALLBACK_MSG_LEN = 256; // Maximum filter callback error message length, in UTF-16 code units. + + // Keyboard types + enum class SoftwareKeyboardType : u32 { + Normal, // Normal keyboard with several pages (QWERTY/accents/symbol/mobile) + QWERTY, // QWERTY keyboard only. + NumPad, // Number pad. + Western, // On JPN systems, a text keyboard without Japanese input capabilities, otherwise same as SWKBD_TYPE_NORMAL. + }; + + // Keyboard dialog buttons. + enum class SoftwareKeyboardButtonConfig : u32 { + SingleButton, // Ok button + DualButton, // Cancel | Ok buttons + TripleButton, // Cancel | I Forgot | Ok buttons + NoButton, // No button (returned by swkbdInputText in special cases) + }; + + // Accepted input types. + enum class SoftwareKeyboardValidInput : u32 { + Anything, // All inputs are accepted. + NotEmpty, // Empty inputs are not accepted. + NotEmptyNotBlank, // Empty or blank inputs (consisting solely of whitespace) are not accepted. + NotBlank, // Blank inputs (consisting solely of whitespace) are not accepted, but empty inputs are. + FixedLen, // The input must have a fixed length (specified by maxTextLength in swkbdInit) + }; + + // Keyboard password modes. + enum class SoftwareKeyboardPasswordMode : u32 { + None, // Characters are not concealed. + Hide, // Characters are concealed immediately. + HideDelay, // Characters are concealed a second after they've been typed. + }; + + // Keyboard filter callback return values. + enum class SoftwareKeyboardCallbackResult : u32 { + OK, // Specifies that the input is valid. + Close, // Displays an error message, then closes the keyboard. + Continue, // Displays an error message and continues displaying the keyboard. + }; + + // Keyboard return values. + enum class SoftwareKeyboardResult : s32 { + None = -1, // Dummy/unused. + InvalidInput = -2, // Invalid parameters to swkbd. + OutOfMem = -3, // Out of memory. + + D0Click = 0, // The button was clicked in 1-button dialogs. + D1Click0, // The left button was clicked in 2-button dialogs. + D1Click1, // The right button was clicked in 2-button dialogs. + D2Click0, // The left button was clicked in 3-button dialogs. + D2Click1, // The middle button was clicked in 3-button dialogs. + D2Click2, // The right button was clicked in 3-button dialogs. + + HomePressed = 10, // The HOME button was pressed. + ResetPressed, // The soft-reset key combination was pressed. + PowerPressed, // The POWER button was pressed. + + ParentalOK = 20, // The parental PIN was verified successfully. + ParentalFail, // The parental PIN was incorrect. + + BannedInput = 30, // The filter callback returned SoftwareKeyboardCallback::CLOSE. + }; + + struct SoftwareKeyboardConfig { + enum_le type; + enum_le numButtonsM1; + enum_le validInput; + enum_le passwordMode; + s32_le isParentalScreen; + s32_le darkenTopScreen; + u32_le filterFlags; + u32_le saveStateFlags; + u16_le maxTextLength; + u16_le dictWordCount; + u16_le maxDigits; + std::array, MAX_BUTTON> buttonText; + std::array numpadKeys; + std::array hintText; // Text to display when asking the user for input + bool predictiveInput; + bool multiline; + bool fixedWidth; + bool allowHome; + bool allowReset; + bool allowPower; + bool unknown; + bool defaultQwerty; + std::array buttonSubmitsText; + u16_le language; + + u32_le initialTextOffset; // Offset of the default text in the output SharedMemory + u32_le dictOffset; + u32_le initialStatusOffset; + u32_le initialLearningOffset; + u32_le sharedMemorySize; // Size of the SharedMemory + u32_le version; + + enum_le returnCode; + + u32_le statusOffset; + u32_le learningOffset; + + u32_le textOffset; // Offset in the SharedMemory where the output text starts + u16_le textLength; // Length in characters of the output text + + enum_le callbackResult; + std::array callbackMessage; + bool skipAtCheck; + std::array pad; + }; + static_assert(sizeof(SoftwareKeyboardConfig) == 0x400, "Software keyboard config size is wrong"); + virtual const char* name() override { return "Software Keyboard"; } - virtual Result::HorizonResult start(const MemoryBlock& sharedMem, const std::vector& parameters) override; + virtual Result::HorizonResult start(const MemoryBlock& sharedMem, const std::vector& parameters, u32 appID) override; virtual Result::HorizonResult receiveParameter(const Applets::Parameter& parameter) override; virtual void reset() override; SoftwareKeyboardApplet(Memory& memory, std::optional& nextParam) : AppletBase(memory, nextParam) {} + void closeKeyboard(u32 appID); + + SoftwareKeyboardConfig config; }; } // namespace Applets \ No newline at end of file diff --git a/src/core/applets/mii_selector.cpp b/src/core/applets/mii_selector.cpp index ed1c4ad2..ede8de28 100644 --- a/src/core/applets/mii_selector.cpp +++ b/src/core/applets/mii_selector.cpp @@ -5,7 +5,7 @@ using namespace Applets; void MiiSelectorApplet::reset() {} -Result::HorizonResult MiiSelectorApplet::start(const MemoryBlock& sharedMem, const std::vector& parameters) { return Result::Success; } +Result::HorizonResult MiiSelectorApplet::start(const MemoryBlock& sharedMem, const std::vector& parameters, u32 appID) { return Result::Success; } Result::HorizonResult MiiSelectorApplet::receiveParameter(const Applets::Parameter& parameter) { Helpers::warn("Mii Selector: Unimplemented ReceiveParameter"); diff --git a/src/core/applets/software_keyboard.cpp b/src/core/applets/software_keyboard.cpp index 07259d0d..2f3e2e35 100644 --- a/src/core/applets/software_keyboard.cpp +++ b/src/core/applets/software_keyboard.cpp @@ -1,11 +1,12 @@ #include "applets/software_keyboard.hpp" +#include + #include "kernel/handles.hpp" using namespace Applets; void SoftwareKeyboardApplet::reset() {} -Result::HorizonResult SoftwareKeyboardApplet::start(const MemoryBlock& sharedMem, const std::vector& parameters) { return Result::Success; } Result::HorizonResult SoftwareKeyboardApplet::receiveParameter(const Applets::Parameter& parameter) { Helpers::warn("Software keyboard: Unimplemented ReceiveParameter"); @@ -25,8 +26,47 @@ Result::HorizonResult SoftwareKeyboardApplet::receiveParameter(const Applets::Pa break; } - default: Helpers::panic("SWKBD SIGNAL %d\n", parameter.signal); + default: Helpers::panic("Unimplemented swkbd signal %d\n", parameter.signal); } return Result::Success; +} + +Result::HorizonResult SoftwareKeyboardApplet::start(const MemoryBlock& sharedMem, const std::vector& parameters, u32 appID) { + if (parameters.size() < sizeof(SoftwareKeyboardConfig)) { + Helpers::warn("SoftwareKeyboard::Start: Invalid size for keyboard configuration"); + return Result::Success; + } + + std::memcpy(&config, ¶meters[0], sizeof(config)); + + // Temporarily hardcode the pressed button to be the firs tone + switch (config.numButtonsM1) { + case SoftwareKeyboardButtonConfig::SingleButton: config.returnCode = SoftwareKeyboardResult::D0Click; break; + case SoftwareKeyboardButtonConfig::DualButton: config.returnCode = SoftwareKeyboardResult::D1Click0; break; + case SoftwareKeyboardButtonConfig::TripleButton: config.returnCode = SoftwareKeyboardResult::D2Click0; break; + case SoftwareKeyboardButtonConfig::NoButton: config.returnCode = SoftwareKeyboardResult::None; break; + } + + if (config.filterFlags & SoftwareKeyboardFilter::Callback) { + Helpers::warn("Unimplemented software keyboard profanity callback"); + } + + closeKeyboard(appID); + return Result::Success; +} + +void SoftwareKeyboardApplet::closeKeyboard(u32 appID) { + Applets::Parameter param = Applets::Parameter{ + .senderID = appID, + .destID = AppletIDs::Application, + .signal = static_cast(APTSignal::WakeupByExit), + .object = 0, + }; + + // Copy software keyboard configuration into the response parameter + param.data.resize(sizeof(config)); + std::memcpy(¶m.data[0], &config, sizeof(config)); + + nextParameter = param; } \ No newline at end of file diff --git a/src/core/services/apt.cpp b/src/core/services/apt.cpp index b2f3e849..f98c1bb5 100644 --- a/src/core/services/apt.cpp +++ b/src/core/services/apt.cpp @@ -154,6 +154,8 @@ void APTService::startLibraryApplet(u32 messagePointer) { Applets::AppletBase* destApplet = appletManager.getApplet(appID); if (destApplet == nullptr) { Helpers::warn("APT::StartLibraryApplet: Unimplemented dest applet ID"); + mem.write32(messagePointer, IPC::responseHeader(0x1E, 1, 0)); + mem.write32(messagePointer + 4, Result::Success); } else { KernelObject* sharedMemObject = kernel.getObject(parameters); if (sharedMemObject == nullptr) { @@ -169,12 +171,13 @@ void APTService::startLibraryApplet(u32 messagePointer) { data.push_back(mem.read8(buffer + i)); } - Helpers::warn("DONE STARTED DAT STUFF"); - destApplet->start(*sharedMem, data); - + Result::HorizonResult result = destApplet->start(*sharedMem, data, appID); if (resumeEvent.has_value()) { kernel.signalEvent(resumeEvent.value()); } + + mem.write32(messagePointer, IPC::responseHeader(0x1E, 1, 0)); + mem.write32(messagePointer + 4, result); } } From 29494efd94377121b380da6f91f9eb3a17f1996b Mon Sep 17 00:00:00 2001 From: wheremyfoodat <44909372+wheremyfoodat@users.noreply.github.com> Date: Wed, 24 Jan 2024 22:21:17 +0200 Subject: [PATCH 5/9] Stub GSP::GPU::RestoreVramSysArea --- include/services/gsp_gpu.hpp | 1 + src/core/applets/software_keyboard.cpp | 15 +++++++++++++++ src/core/services/gsp_gpu.cpp | 9 +++++++++ 3 files changed, 25 insertions(+) diff --git a/include/services/gsp_gpu.hpp b/include/services/gsp_gpu.hpp index 0a8f8a0a..1e48190c 100644 --- a/include/services/gsp_gpu.hpp +++ b/include/services/gsp_gpu.hpp @@ -75,6 +75,7 @@ class GPUService { void importDisplayCaptureInfo(u32 messagePointer); void registerInterruptRelayQueue(u32 messagePointer); void releaseRight(u32 messagePointer); + void restoreVramSysArea(u32 messagePointer); void saveVramSysArea(u32 messagePointer); void setAxiConfigQoSMode(u32 messagePointer); void setBufferSwap(u32 messagePointer); diff --git a/src/core/applets/software_keyboard.cpp b/src/core/applets/software_keyboard.cpp index 2f3e2e35..432d74c6 100644 --- a/src/core/applets/software_keyboard.cpp +++ b/src/core/applets/software_keyboard.cpp @@ -1,6 +1,7 @@ #include "applets/software_keyboard.hpp" #include +#include #include "kernel/handles.hpp" @@ -38,8 +39,19 @@ Result::HorizonResult SoftwareKeyboardApplet::start(const MemoryBlock& sharedMem return Result::Success; } + // Get keyboard configuration from the application std::memcpy(&config, ¶meters[0], sizeof(config)); + const std::u16string text = u"Pander"; + u32 textAddress = sharedMem.addr; + + // Copy text to shared memory the app gave us + for (u32 i = 0; i < text.size(); i++) { + mem.write16(textAddress, u16(text[i])); + textAddress += sizeof(u16); + } + mem.write16(textAddress, 0); // Write UTF-16 null terminator + // Temporarily hardcode the pressed button to be the firs tone switch (config.numButtonsM1) { case SoftwareKeyboardButtonConfig::SingleButton: config.returnCode = SoftwareKeyboardResult::D0Click; break; @@ -48,6 +60,9 @@ Result::HorizonResult SoftwareKeyboardApplet::start(const MemoryBlock& sharedMem case SoftwareKeyboardButtonConfig::NoButton: config.returnCode = SoftwareKeyboardResult::None; break; } + config.textOffset = 0; + config.textLength = static_cast(text.size()); + if (config.filterFlags & SoftwareKeyboardFilter::Callback) { Helpers::warn("Unimplemented software keyboard profanity callback"); } diff --git a/src/core/services/gsp_gpu.cpp b/src/core/services/gsp_gpu.cpp index a698f7cc..8dff6a79 100644 --- a/src/core/services/gsp_gpu.cpp +++ b/src/core/services/gsp_gpu.cpp @@ -18,6 +18,7 @@ namespace ServiceCommands { ReleaseRight = 0x00170000, ImportDisplayCaptureInfo = 0x00180000, SaveVramSysArea = 0x00190000, + RestoreVramSysArea = 0x001A0000, SetInternalPriorities = 0x001E0080, StoreDataCache = 0x001F0082 }; @@ -51,6 +52,7 @@ void GPUService::handleSyncRequest(u32 messagePointer) { case ServiceCommands::ImportDisplayCaptureInfo: importDisplayCaptureInfo(messagePointer); break; case ServiceCommands::RegisterInterruptRelayQueue: registerInterruptRelayQueue(messagePointer); break; case ServiceCommands::ReleaseRight: releaseRight(messagePointer); break; + case ServiceCommands::RestoreVramSysArea: restoreVramSysArea(messagePointer); break; case ServiceCommands::SaveVramSysArea: saveVramSysArea(messagePointer); break; case ServiceCommands::SetAxiConfigQoSMode: setAxiConfigQoSMode(messagePointer); break; case ServiceCommands::SetBufferSwap: setBufferSwap(messagePointer); break; @@ -481,6 +483,13 @@ void GPUService::saveVramSysArea(u32 messagePointer) { mem.write32(messagePointer + 4, Result::Success); } +void GPUService::restoreVramSysArea(u32 messagePointer) { + Helpers::warn("GSP::GPU::RestoreVramSysArea (stubbed)"); + + mem.write32(messagePointer, IPC::responseHeader(0x1A, 1, 0)); + mem.write32(messagePointer + 4, Result::Success); +} + // Used in similar fashion to the SaveVramSysArea function void GPUService::importDisplayCaptureInfo(u32 messagePointer) { Helpers::warn("GSP::GPU::ImportDisplayCaptureInfo (stubbed)"); From ef3bd0281999f77cc7abe939724ec1873d6b590b Mon Sep 17 00:00:00 2001 From: wheremyfoodat <44909372+wheremyfoodat@users.noreply.github.com> Date: Thu, 25 Jan 2024 02:09:14 +0200 Subject: [PATCH 6/9] Properly transfer data between apps and applets --- src/core/applets/software_keyboard.cpp | 9 ++++++--- src/core/services/apt.cpp | 17 ++++++++++++++--- 2 files changed, 20 insertions(+), 6 deletions(-) diff --git a/src/core/applets/software_keyboard.cpp b/src/core/applets/software_keyboard.cpp index 432d74c6..dfe68b60 100644 --- a/src/core/applets/software_keyboard.cpp +++ b/src/core/applets/software_keyboard.cpp @@ -42,7 +42,7 @@ Result::HorizonResult SoftwareKeyboardApplet::start(const MemoryBlock& sharedMem // Get keyboard configuration from the application std::memcpy(&config, ¶meters[0], sizeof(config)); - const std::u16string text = u"Pander"; + const std::u16string text = u"Pand"; u32 textAddress = sharedMem.addr; // Copy text to shared memory the app gave us @@ -55,13 +55,16 @@ Result::HorizonResult SoftwareKeyboardApplet::start(const MemoryBlock& sharedMem // Temporarily hardcode the pressed button to be the firs tone switch (config.numButtonsM1) { case SoftwareKeyboardButtonConfig::SingleButton: config.returnCode = SoftwareKeyboardResult::D0Click; break; - case SoftwareKeyboardButtonConfig::DualButton: config.returnCode = SoftwareKeyboardResult::D1Click0; break; - case SoftwareKeyboardButtonConfig::TripleButton: config.returnCode = SoftwareKeyboardResult::D2Click0; break; + case SoftwareKeyboardButtonConfig::DualButton: config.returnCode = SoftwareKeyboardResult::D1Click1; break; + case SoftwareKeyboardButtonConfig::TripleButton: config.returnCode = SoftwareKeyboardResult::D2Click2; break; case SoftwareKeyboardButtonConfig::NoButton: config.returnCode = SoftwareKeyboardResult::None; break; + default: Helpers::warn("Software keyboard: Invalid button mode specification"); break; } config.textOffset = 0; config.textLength = static_cast(text.size()); + static_assert(offsetof(SoftwareKeyboardConfig, textOffset) == 324); + static_assert(offsetof(SoftwareKeyboardConfig, textLength) == 328); if (config.filterFlags & SoftwareKeyboardFilter::Callback) { Helpers::warn("Unimplemented software keyboard profanity callback"); diff --git a/src/core/services/apt.cpp b/src/core/services/apt.cpp index f98c1bb5..b475b89d 100644 --- a/src/core/services/apt.cpp +++ b/src/core/services/apt.cpp @@ -148,9 +148,6 @@ void APTService::startLibraryApplet(u32 messagePointer) { const u32 buffer = mem.read32(messagePointer + 24); log("APT::StartLibraryApplet (app ID = %X)\n", appID); - mem.write32(messagePointer, IPC::responseHeader(0x16, 1, 0)); - mem.write32(messagePointer + 4, Result::Success); - Applets::AppletBase* destApplet = appletManager.getApplet(appID); if (destApplet == nullptr) { Helpers::warn("APT::StartLibraryApplet: Unimplemented dest applet ID"); @@ -301,6 +298,8 @@ void APTService::sendParameter(u32 messagePointer) { void APTService::receiveParameter(u32 messagePointer) { const u32 app = mem.read32(messagePointer + 4); const u32 size = mem.read32(messagePointer + 8); + // Parameter data pointer is in the thread static buffer, which starts 0x100 bytes after the command buffer + const u32 buffer = mem.read32(messagePointer + 0x100 + 4); log("APT::ReceiveParameter(app ID = %X, size = %04X)\n", app, size); if (size > 0x1000) Helpers::panic("APT::ReceiveParameter with size > 0x1000"); @@ -317,11 +316,18 @@ void APTService::receiveParameter(u32 messagePointer) { mem.write32(messagePointer + 20, 0x10); mem.write32(messagePointer + 24, parameter.object); mem.write32(messagePointer + 28, 0); + + const u32 transferSize = std::min(size, parameter.data.size()); + for (u32 i = 0; i < transferSize; i++) { + mem.write8(buffer + i, parameter.data[i]); + } } void APTService::glanceParameter(u32 messagePointer) { const u32 app = mem.read32(messagePointer + 4); const u32 size = mem.read32(messagePointer + 8); + // Parameter data pointer is in the thread static buffer, which starts 0x100 bytes after the command buffer + const u32 buffer = mem.read32(messagePointer + 0x100 + 4); log("APT::GlanceParameter(app ID = %X, size = %04X)\n", app, size); if (size > 0x1000) Helpers::panic("APT::GlanceParameter with size > 0x1000"); @@ -339,6 +345,11 @@ void APTService::glanceParameter(u32 messagePointer) { mem.write32(messagePointer + 20, 0); mem.write32(messagePointer + 24, parameter.object); mem.write32(messagePointer + 28, 0); + + const u32 transferSize = std::min(size, parameter.data.size()); + for (u32 i = 0; i < transferSize; i++) { + mem.write8(buffer + i, parameter.data[i]); + } } void APTService::replySleepQuery(u32 messagePointer) { From 8fc61769abb6377e0a87c88fc12e24f1f4e65471 Mon Sep 17 00:00:00 2001 From: wheremyfoodat <44909372+wheremyfoodat@users.noreply.github.com> Date: Thu, 25 Jan 2024 02:31:01 +0200 Subject: [PATCH 7/9] Implement PTM::GetPedometerState --- include/services/boss.hpp | 1 + include/services/ptm.hpp | 1 + src/core/services/boss.cpp | 9 +++++++++ src/core/services/ptm.cpp | 13 ++++++++++++- 4 files changed, 23 insertions(+), 1 deletion(-) diff --git a/include/services/boss.hpp b/include/services/boss.hpp index 57452e2d..769184e5 100644 --- a/include/services/boss.hpp +++ b/include/services/boss.hpp @@ -14,6 +14,7 @@ class BOSSService { void cancelTask(u32 messagePointer); void initializeSession(u32 messagePointer); void getErrorCode(u32 messagePointer); + void getNewArrivalFlag(u32 messagePointer); void getNsDataIdList(u32 messagePointer, u32 commandWord); void getOptoutFlag(u32 messagePointer); void getStorageEntryInfo(u32 messagePointer); // Unknown what this is, name taken from Citra diff --git a/include/services/ptm.hpp b/include/services/ptm.hpp index ae845725..f752839b 100644 --- a/include/services/ptm.hpp +++ b/include/services/ptm.hpp @@ -17,6 +17,7 @@ class PTMService { void getAdapterState(u32 messagePointer); void getBatteryChargeState(u32 messagePointer); void getBatteryLevel(u32 messagePointer); + void getPedometerState(u32 messagePointer); void getStepHistory(u32 messagePointer); void getStepHistoryAll(u32 messagePointer); void getTotalStepCount(u32 messagePointer); diff --git a/src/core/services/boss.cpp b/src/core/services/boss.cpp index 30190cd3..a8f7194b 100644 --- a/src/core/services/boss.cpp +++ b/src/core/services/boss.cpp @@ -6,6 +6,7 @@ namespace BOSSCommands { InitializeSession = 0x00010082, UnregisterStorage = 0x00030000, GetTaskStorageInfo = 0x00040000, + GetNewArrivalFlag = 0x00070000, RegisterNewArrivalEvent = 0x00080002, SetOptoutFlag = 0x00090040, GetOptoutFlag = 0x000A0000, @@ -37,6 +38,7 @@ void BOSSService::handleSyncRequest(u32 messagePointer) { switch (command) { case BOSSCommands::CancelTask: cancelTask(messagePointer); break; case BOSSCommands::GetErrorCode: getErrorCode(messagePointer); break; + case BOSSCommands::GetNewArrivalFlag: getNewArrivalFlag(messagePointer); break; case BOSSCommands::GetNsDataIdList: case BOSSCommands::GetNsDataIdList1: getNsDataIdList(messagePointer, command); break; @@ -240,4 +242,11 @@ void BOSSService::unregisterStorage(u32 messagePointer) { log("BOSS::UnregisterStorage (stubbed)\n"); mem.write32(messagePointer, IPC::responseHeader(0x3, 1, 0)); mem.write32(messagePointer + 4, Result::Success); +} + +void BOSSService::getNewArrivalFlag(u32 messagePointer) { + log("BOSS::GetNewArrivalFlag (stubbed)\n"); + mem.write32(messagePointer, IPC::responseHeader(0x7, 2, 0)); + mem.write32(messagePointer + 4, Result::Success); + mem.write8(messagePointer + 8, 0); // Flag } \ No newline at end of file diff --git a/src/core/services/ptm.cpp b/src/core/services/ptm.cpp index 071fa012..4cd7e095 100644 --- a/src/core/services/ptm.cpp +++ b/src/core/services/ptm.cpp @@ -6,6 +6,7 @@ namespace PTMCommands { GetAdapterState = 0x00050000, GetBatteryLevel = 0x00070000, GetBatteryChargeState = 0x00080000, + GetPedometerState = 0x00090000, GetStepHistory = 0x000B00C2, GetTotalStepCount = 0x000C0000, GetStepHistoryAll = 0x000F0084, @@ -30,6 +31,7 @@ void PTMService::handleSyncRequest(u32 messagePointer, PTMService::Type type) { case PTMCommands::GetAdapterState: getAdapterState(messagePointer); break; case PTMCommands::GetBatteryChargeState: getBatteryChargeState(messagePointer); break; case PTMCommands::GetBatteryLevel: getBatteryLevel(messagePointer); break; + case PTMCommands::GetPedometerState: getPedometerState(messagePointer); break; case PTMCommands::GetStepHistory: getStepHistory(messagePointer); break; case PTMCommands::GetStepHistoryAll: getStepHistoryAll(messagePointer); break; case PTMCommands::GetTotalStepCount: getTotalStepCount(messagePointer); break; @@ -67,11 +69,20 @@ void PTMService::getBatteryChargeState(u32 messagePointer) { // We're only charging if the battery is not already full const bool charging = config.chargerPlugged && (config.batteryPercentage < 100); - mem.write32(messagePointer, IPC::responseHeader(0x7, 2, 0)); + mem.write32(messagePointer, IPC::responseHeader(0x8, 2, 0)); mem.write32(messagePointer + 4, Result::Success); mem.write8(messagePointer + 8, charging ? 1 : 0); } +void PTMService::getPedometerState(u32 messagePointer) { + log("PTM::GetPedometerState"); + const bool countingSteps = true; + + mem.write32(messagePointer, IPC::responseHeader(0x9, 2, 0)); + mem.write32(messagePointer + 4, Result::Success); + mem.write8(messagePointer + 8, countingSteps ? 1 : 0); +} + void PTMService::getBatteryLevel(u32 messagePointer) { log("PTM::GetBatteryLevel"); From 6e6c84eebba714c8f21f9e5ae77a36a53f240079 Mon Sep 17 00:00:00 2001 From: wheremyfoodat <44909372+wheremyfoodat@users.noreply.github.com> Date: Thu, 25 Jan 2024 03:39:48 +0200 Subject: [PATCH 8/9] Add Mii Selector --- include/applets/applet.hpp | 2 +- include/applets/mii_selector.hpp | 72 +++++++++++++++++++++++++- include/applets/software_keyboard.hpp | 2 +- src/core/applets/mii_selector.cpp | 68 +++++++++++++++++++++++- src/core/applets/software_keyboard.cpp | 9 +++- src/core/services/apt.cpp | 8 +-- src/core/services/ptm.cpp | 2 +- 7 files changed, 150 insertions(+), 13 deletions(-) diff --git a/include/applets/applet.hpp b/include/applets/applet.hpp index d9ba6143..48f20b03 100644 --- a/include/applets/applet.hpp +++ b/include/applets/applet.hpp @@ -84,7 +84,7 @@ namespace Applets { virtual const char* name() = 0; // Called by APT::StartLibraryApplet and similar - virtual Result::HorizonResult start(const MemoryBlock& sharedMem, const std::vector& parameters, u32 appID) = 0; + virtual Result::HorizonResult start(const MemoryBlock* sharedMem, const std::vector& parameters, u32 appID) = 0; // Transfer parameters from application -> applet virtual Result::HorizonResult receiveParameter(const Parameter& parameter) = 0; virtual void reset() = 0; diff --git a/include/applets/mii_selector.hpp b/include/applets/mii_selector.hpp index 3b6cf2ec..36f9fe79 100644 --- a/include/applets/mii_selector.hpp +++ b/include/applets/mii_selector.hpp @@ -1,13 +1,83 @@ +#include + #include "applets/applet.hpp" +#include "swap.hpp" namespace Applets { + struct MiiConfig { + u8 enableCancelButton; + u8 enableGuestMii; + u8 showOnTopScreen; + std::array pad1; + std::array title; + std::array pad2; + u8 showGuestMiis; + std::array pad3; + u32 initiallySelectedIndex; + std::array guestMiiWhitelist; + std::array userMiiWhitelist; + std::array pad4; + u32 magicValue; + }; + static_assert(sizeof(MiiConfig) == 0x104, "Mii config size is wrong"); + + // Some members of this struct are not properly aligned so we need pragma pack +#pragma pack(push, 1) + struct MiiData { + u8 version; + u8 miiOptions; + u8 miiPos; + u8 consoleID; + + u64_be systemID; + u32_be miiID; + std::array creatorMAC; + u16 padding; + + u16_be miiDetails; + std::array miiName; + u8 height; + u8 width; + + u8 faceStyle; + u8 faceDetails; + u8 hairStyle; + u8 hairDetails; + u32_be eyeDetails; + u32_be eyebrowDetails; + u16_be noseDetails; + u16_be mouthDetails; + u16_be moustacheDetails; + u16_be beardDetails; + u16_be glassesDetails; + u16_be moleDetails; + + std::array authorName; + }; +#pragma pack(pop) + static_assert(sizeof(MiiData) == 0x5C, "MiiData structure has incorrect size"); + + struct MiiResult { + u32_be returnCode; + u32_be isGuestMiiSelected; + u32_be selectedGuestMiiIndex; + MiiData selectedMiiData; + u16_be unknown1; + u16_be miiChecksum; + std::array guestMiiName; + }; + static_assert(sizeof(MiiResult) == 0x84, "MiiResult structure has incorrect size"); + class MiiSelectorApplet final : public AppletBase { public: virtual const char* name() override { return "Mii Selector"; } - virtual Result::HorizonResult start(const MemoryBlock& sharedMem, const std::vector& parameters, u32 appID) override; + virtual Result::HorizonResult start(const MemoryBlock* sharedMem, const std::vector& parameters, u32 appID) override; virtual Result::HorizonResult receiveParameter(const Applets::Parameter& parameter) override; virtual void reset() override; + MiiResult output; + MiiConfig config; + MiiResult getDefaultMii(); MiiSelectorApplet(Memory& memory, std::optional& nextParam) : AppletBase(memory, nextParam) {} }; } // namespace Applets \ No newline at end of file diff --git a/include/applets/software_keyboard.hpp b/include/applets/software_keyboard.hpp index 3a167ad0..f753566d 100644 --- a/include/applets/software_keyboard.hpp +++ b/include/applets/software_keyboard.hpp @@ -150,7 +150,7 @@ namespace Applets { static_assert(sizeof(SoftwareKeyboardConfig) == 0x400, "Software keyboard config size is wrong"); virtual const char* name() override { return "Software Keyboard"; } - virtual Result::HorizonResult start(const MemoryBlock& sharedMem, const std::vector& parameters, u32 appID) override; + virtual Result::HorizonResult start(const MemoryBlock* sharedMem, const std::vector& parameters, u32 appID) override; virtual Result::HorizonResult receiveParameter(const Applets::Parameter& parameter) override; virtual void reset() override; diff --git a/src/core/applets/mii_selector.cpp b/src/core/applets/mii_selector.cpp index ede8de28..5b7637e3 100644 --- a/src/core/applets/mii_selector.cpp +++ b/src/core/applets/mii_selector.cpp @@ -1,11 +1,38 @@ #include "applets/mii_selector.hpp" +#include +#include + #include "kernel/handles.hpp" using namespace Applets; void MiiSelectorApplet::reset() {} -Result::HorizonResult MiiSelectorApplet::start(const MemoryBlock& sharedMem, const std::vector& parameters, u32 appID) { return Result::Success; } +Result::HorizonResult MiiSelectorApplet::start(const MemoryBlock* sharedMem, const std::vector& parameters, u32 appID) { + // Get mii configuration from the application + std::memcpy(&config, ¶meters[0], sizeof(config)); + + Applets::Parameter param = Applets::Parameter{ + .senderID = appID, + .destID = AppletIDs::Application, + .signal = static_cast(APTSignal::WakeupByExit), + .object = 0, + }; + + // Thanks to Citra devs as always for the default mii data and other applet help + output = getDefaultMii(); + output.returnCode = 0; // Success + // output.selectedMiiData = miiData; + output.selectedGuestMiiIndex = std::numeric_limits::max(); + output.miiChecksum = boost::crc<16, 0x1021, 0, 0, false, false>(&output.selectedMiiData, sizeof(MiiData) + sizeof(output.unknown1)); + + // Copy output into the response parameter + param.data.resize(sizeof(output)); + std::memcpy(¶m.data[0], &output, sizeof(output)); + + nextParameter = param; + return Result::Success; +} Result::HorizonResult MiiSelectorApplet::receiveParameter(const Applets::Parameter& parameter) { Helpers::warn("Mii Selector: Unimplemented ReceiveParameter"); @@ -20,4 +47,43 @@ Result::HorizonResult MiiSelectorApplet::receiveParameter(const Applets::Paramet nextParameter = param; return Result::Success; +} + +MiiResult MiiSelectorApplet::getDefaultMii() { + // This data was obtained from Citra + MiiData miiData; + miiData.version = 0x03; + miiData.miiOptions = 0x00; + miiData.miiPos = 0x10; + miiData.consoleID = 0x30; + miiData.systemID = 0xD285B6B300C8850A; + miiData.miiID = 0x98391EE4; + miiData.creatorMAC = {0x40, 0xF4, 0x07, 0xB7, 0x37, 0x10}; + miiData.padding = 0x0000; + miiData.miiDetails = 0xA600; + miiData.miiName = {'P', 'a', 'n', 'd', 'a', '3', 'D', 'S', 0x0, 0x0}; + miiData.height = 0x40; + miiData.width = 0x40; + miiData.faceStyle = 0x00; + miiData.faceDetails = 0x00; + miiData.hairStyle = 0x21; + miiData.hairDetails = 0x01; + miiData.eyeDetails = 0x02684418; + miiData.eyebrowDetails = 0x26344614; + miiData.noseDetails = 0x8112; + miiData.mouthDetails = 0x1768; + miiData.moustacheDetails = 0x0D00; + miiData.beardDetails = 0x0029; + miiData.glassesDetails = 0x0052; + miiData.moleDetails = 0x4850; + miiData.authorName = {u'B', u'O', u'N', u'K', u'E', u'R'}; + + MiiResult result; + result.returnCode = 0x0; + result.isGuestMiiSelected = 0x0; + result.selectedGuestMiiIndex = std::numeric_limits::max(); + result.selectedMiiData = miiData; + result.guestMiiName.fill(0x0); + + return result; } \ No newline at end of file diff --git a/src/core/applets/software_keyboard.cpp b/src/core/applets/software_keyboard.cpp index dfe68b60..520e89c1 100644 --- a/src/core/applets/software_keyboard.cpp +++ b/src/core/applets/software_keyboard.cpp @@ -33,17 +33,22 @@ Result::HorizonResult SoftwareKeyboardApplet::receiveParameter(const Applets::Pa return Result::Success; } -Result::HorizonResult SoftwareKeyboardApplet::start(const MemoryBlock& sharedMem, const std::vector& parameters, u32 appID) { +Result::HorizonResult SoftwareKeyboardApplet::start(const MemoryBlock* sharedMem, const std::vector& parameters, u32 appID) { if (parameters.size() < sizeof(SoftwareKeyboardConfig)) { Helpers::warn("SoftwareKeyboard::Start: Invalid size for keyboard configuration"); return Result::Success; } + if (sharedMem == nullptr) { + Helpers::warn("SoftwareKeyboard: Missing shared memory"); + return Result::Success; + } + // Get keyboard configuration from the application std::memcpy(&config, ¶meters[0], sizeof(config)); const std::u16string text = u"Pand"; - u32 textAddress = sharedMem.addr; + u32 textAddress = sharedMem->addr; // Copy text to shared memory the app gave us for (u32 i = 0; i < text.size(); i++) { diff --git a/src/core/services/apt.cpp b/src/core/services/apt.cpp index b475b89d..404a0e59 100644 --- a/src/core/services/apt.cpp +++ b/src/core/services/apt.cpp @@ -155,12 +155,8 @@ void APTService::startLibraryApplet(u32 messagePointer) { mem.write32(messagePointer + 4, Result::Success); } else { KernelObject* sharedMemObject = kernel.getObject(parameters); - if (sharedMemObject == nullptr) { - Helpers::warn("Couldn't find shared memory block\n"); - return; - } - const MemoryBlock* sharedMem = sharedMemObject->getData(); + const MemoryBlock* sharedMem = sharedMemObject ? sharedMemObject->getData() : nullptr; std::vector data; data.reserve(bufferSize); @@ -168,7 +164,7 @@ void APTService::startLibraryApplet(u32 messagePointer) { data.push_back(mem.read8(buffer + i)); } - Result::HorizonResult result = destApplet->start(*sharedMem, data, appID); + Result::HorizonResult result = destApplet->start(sharedMem, data, appID); if (resumeEvent.has_value()) { kernel.signalEvent(resumeEvent.value()); } diff --git a/src/core/services/ptm.cpp b/src/core/services/ptm.cpp index 4cd7e095..67451cc2 100644 --- a/src/core/services/ptm.cpp +++ b/src/core/services/ptm.cpp @@ -76,7 +76,7 @@ void PTMService::getBatteryChargeState(u32 messagePointer) { void PTMService::getPedometerState(u32 messagePointer) { log("PTM::GetPedometerState"); - const bool countingSteps = true; + constexpr bool countingSteps = true; mem.write32(messagePointer, IPC::responseHeader(0x9, 2, 0)); mem.write32(messagePointer + 4, Result::Success); From f0c20d70bc82abdcd4182992fb688a44243828b2 Mon Sep 17 00:00:00 2001 From: wheremyfoodat <44909372+wheremyfoodat@users.noreply.github.com> Date: Thu, 25 Jan 2024 17:31:17 +0200 Subject: [PATCH 9/9] Add error/EULA applet --- CMakeLists.txt | 6 +++-- include/applets/applet_manager.hpp | 2 ++ include/applets/error_applet.hpp | 15 ++++++++++++ src/core/applets/applet_manager.cpp | 6 ++++- src/core/applets/error_applet.cpp | 32 ++++++++++++++++++++++++++ src/core/applets/mii_selector.cpp | 2 -- src/core/applets/software_keyboard.cpp | 2 -- 7 files changed, 58 insertions(+), 7 deletions(-) create mode 100644 include/applets/error_applet.hpp create mode 100644 src/core/applets/error_applet.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index e557c5c8..baa37466 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -188,7 +188,9 @@ set(FS_SOURCE_FILES src/core/fs/archive_self_ncch.cpp src/core/fs/archive_save_d src/core/fs/ivfc.cpp src/core/fs/archive_user_save_data.cpp src/core/fs/archive_system_save_data.cpp ) -set(APPLET_SOURCE_FILES src/core/applets/applet.cpp src/core/applets/mii_selector.cpp src/core/applets/software_keyboard.cpp src/core/applets/applet_manager.cpp) +set(APPLET_SOURCE_FILES src/core/applets/applet.cpp src/core/applets/mii_selector.cpp src/core/applets/software_keyboard.cpp src/core/applets/applet_manager.cpp + src/core/applets/error_applet.cpp +) set(RENDERER_SW_SOURCE_FILES src/core/renderer_sw/renderer_sw.cpp) # Frontend source files @@ -244,7 +246,7 @@ set(HEADER_FILES include/emulator.hpp include/helpers.hpp include/termcolor.hpp include/services/news_u.hpp include/applets/software_keyboard.hpp include/applets/applet_manager.hpp include/fs/archive_user_save_data.hpp include/services/amiibo_device.hpp include/services/nfc_types.hpp include/swap.hpp include/services/csnd.hpp include/services/nwm_uds.hpp include/fs/archive_system_save_data.hpp include/lua_manager.hpp include/memory_mapped_file.hpp include/hydra_icon.hpp - include/PICA/dynapica/shader_rec_emitter_arm64.hpp include/scheduler.hpp + include/PICA/dynapica/shader_rec_emitter_arm64.hpp include/scheduler.hpp include/applets/error_applet.hpp ) cmrc_add_resource_library( diff --git a/include/applets/applet_manager.hpp b/include/applets/applet_manager.hpp index e75e1268..d8cfff12 100644 --- a/include/applets/applet_manager.hpp +++ b/include/applets/applet_manager.hpp @@ -1,6 +1,7 @@ #pragma once #include +#include "applets/error_applet.hpp" #include "applets/mii_selector.hpp" #include "applets/software_keyboard.hpp" #include "helpers.hpp" @@ -11,6 +12,7 @@ namespace Applets { class AppletManager { MiiSelectorApplet miiSelector; SoftwareKeyboardApplet swkbd; + ErrorApplet error; std::optional nextParameter = std::nullopt; public: diff --git a/include/applets/error_applet.hpp b/include/applets/error_applet.hpp new file mode 100644 index 00000000..4dcc319d --- /dev/null +++ b/include/applets/error_applet.hpp @@ -0,0 +1,15 @@ +#include + +#include "applets/applet.hpp" + +namespace Applets { + class ErrorApplet final : public AppletBase { + public: + virtual const char* name() override { return "Error/EULA Agreement"; } + virtual Result::HorizonResult start(const MemoryBlock* sharedMem, const std::vector& parameters, u32 appID) override; + virtual Result::HorizonResult receiveParameter(const Applets::Parameter& parameter) override; + virtual void reset() override; + + ErrorApplet(Memory& memory, std::optional& nextParam) : AppletBase(memory, nextParam) {} + }; +} // namespace Applets \ No newline at end of file diff --git a/src/core/applets/applet_manager.cpp b/src/core/applets/applet_manager.cpp index c2791777..cdb19319 100644 --- a/src/core/applets/applet_manager.cpp +++ b/src/core/applets/applet_manager.cpp @@ -4,13 +4,14 @@ using namespace Applets; -AppletManager::AppletManager(Memory& mem) : miiSelector(mem, nextParameter), swkbd(mem, nextParameter) {} +AppletManager::AppletManager(Memory& mem) : miiSelector(mem, nextParameter), swkbd(mem, nextParameter), error(mem, nextParameter) {} void AppletManager::reset() { nextParameter = std::nullopt; miiSelector.reset(); swkbd.reset(); + error.reset(); } AppletBase* AppletManager::getApplet(u32 id) { @@ -21,6 +22,9 @@ AppletBase* AppletManager::getApplet(u32 id) { case AppletIDs::SoftwareKeyboard: case AppletIDs::SoftwareKeyboard2: return &swkbd; + case AppletIDs::ErrDisp: + case AppletIDs::ErrDisp2: return &error; + default: return nullptr; } } diff --git a/src/core/applets/error_applet.cpp b/src/core/applets/error_applet.cpp new file mode 100644 index 00000000..5acbcbba --- /dev/null +++ b/src/core/applets/error_applet.cpp @@ -0,0 +1,32 @@ +#include "applets/error_applet.hpp" +#include "kernel/handles.hpp" + +using namespace Applets; + +void ErrorApplet::reset() {} + +Result::HorizonResult ErrorApplet::start(const MemoryBlock* sharedMem, const std::vector& parameters, u32 appID) { + Applets::Parameter param = Applets::Parameter{ + .senderID = appID, + .destID = AppletIDs::Application, + .signal = static_cast(APTSignal::WakeupByExit), + .object = 0, + .data = parameters, // TODO: Figure out how the data format for this applet + }; + + nextParameter = param; + return Result::Success; +} + +Result::HorizonResult ErrorApplet::receiveParameter(const Applets::Parameter& parameter) { + Applets::Parameter param = Applets::Parameter{ + .senderID = parameter.destID, + .destID = AppletIDs::Application, + .signal = static_cast(APTSignal::Response), + .object = KernelHandles::APTCaptureSharedMemHandle, + .data = {}, + }; + + nextParameter = param; + return Result::Success; +} \ No newline at end of file diff --git a/src/core/applets/mii_selector.cpp b/src/core/applets/mii_selector.cpp index 5b7637e3..d6dd79da 100644 --- a/src/core/applets/mii_selector.cpp +++ b/src/core/applets/mii_selector.cpp @@ -35,8 +35,6 @@ Result::HorizonResult MiiSelectorApplet::start(const MemoryBlock* sharedMem, con } Result::HorizonResult MiiSelectorApplet::receiveParameter(const Applets::Parameter& parameter) { - Helpers::warn("Mii Selector: Unimplemented ReceiveParameter"); - Applets::Parameter param = Applets::Parameter{ .senderID = parameter.destID, .destID = AppletIDs::Application, diff --git a/src/core/applets/software_keyboard.cpp b/src/core/applets/software_keyboard.cpp index 520e89c1..4a91b790 100644 --- a/src/core/applets/software_keyboard.cpp +++ b/src/core/applets/software_keyboard.cpp @@ -10,8 +10,6 @@ using namespace Applets; void SoftwareKeyboardApplet::reset() {} Result::HorizonResult SoftwareKeyboardApplet::receiveParameter(const Applets::Parameter& parameter) { - Helpers::warn("Software keyboard: Unimplemented ReceiveParameter"); - switch (parameter.signal) { // Signal == request -> Applet is asking swkbd for a shared memory handle for backing up the framebuffer before opening the applet case u32(APTSignal::Request): {