Merge pull request #380 from wheremyfoodat/scheduler

Add support for software keyboard & mii selector applets, improve APT/PTM/BOSS/misc HLE
This commit is contained in:
wheremyfoodat 2024-01-25 15:52:05 +00:00 committed by GitHub
commit 2ff5bff00a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
22 changed files with 598 additions and 27 deletions

View file

@ -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(

View file

@ -1,6 +1,9 @@
#pragma once
#include <vector>
#include "helpers.hpp"
#include "kernel/kernel_types.hpp"
#include "memory.hpp"
#include "result/result.hpp"
@ -65,10 +68,11 @@ namespace Applets {
};
struct Parameter {
u32 senderID;
u32 destID;
u32 signal;
std::vector<u8> 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<u8> data; // Misc data
};
class AppletBase {
@ -80,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<u8>& parameters, u32 appID) = 0;
// Transfer parameters from application -> applet
virtual Result::HorizonResult receiveParameter(const Parameter& parameter) = 0;
virtual void reset() = 0;

View file

@ -1,6 +1,7 @@
#pragma once
#include <optional>
#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<Applets::Parameter> nextParameter = std::nullopt;
public:

View file

@ -0,0 +1,15 @@
#include <string>
#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<u8>& parameters, u32 appID) override;
virtual Result::HorizonResult receiveParameter(const Applets::Parameter& parameter) override;
virtual void reset() override;
ErrorApplet(Memory& memory, std::optional<Parameter>& nextParam) : AppletBase(memory, nextParam) {}
};
} // namespace Applets

View file

@ -1,13 +1,83 @@
#include <string>
#include "applets/applet.hpp"
#include "swap.hpp"
namespace Applets {
struct MiiConfig {
u8 enableCancelButton;
u8 enableGuestMii;
u8 showOnTopScreen;
std::array<u8, 0x5> pad1;
std::array<u16_le, 0x40> title;
std::array<u8, 0x4> pad2;
u8 showGuestMiis;
std::array<u8, 0x3> pad3;
u32 initiallySelectedIndex;
std::array<u8, 0x6> guestMiiWhitelist;
std::array<u8, 0x64> userMiiWhitelist;
std::array<u8, 0x2> 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<u8, 0x6> creatorMAC;
u16 padding;
u16_be miiDetails;
std::array<char16_t, 0xA> 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<char16_t, 0xA> 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<u16_le, 0xC> 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() override;
virtual Result::HorizonResult start(const MemoryBlock* sharedMem, const std::vector<u8>& 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<Parameter>& nextParam) : AppletBase(memory, nextParam) {}
};
} // namespace Applets

View file

@ -1,13 +1,162 @@
#include <array>
#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<SoftwareKeyboardType> type;
enum_le<SoftwareKeyboardButtonConfig> numButtonsM1;
enum_le<SoftwareKeyboardValidInput> validInput;
enum_le<SoftwareKeyboardPasswordMode> passwordMode;
s32_le isParentalScreen;
s32_le darkenTopScreen;
u32_le filterFlags;
u32_le saveStateFlags;
u16_le maxTextLength;
u16_le dictWordCount;
u16_le maxDigits;
std::array<std::array<u16_le, MAX_BUTTON_TEXT_LEN + 1>, MAX_BUTTON> buttonText;
std::array<u16_le, 2> numpadKeys;
std::array<u16_le, MAX_HINT_TEXT_LEN + 1> 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<bool, 4> 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<SoftwareKeyboardResult> 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<SoftwareKeyboardCallbackResult> callbackResult;
std::array<u16_le, MAX_CALLBACK_MSG_LEN + 1> callbackMessage;
bool skipAtCheck;
std::array<u8, 0xAB> pad;
};
static_assert(sizeof(SoftwareKeyboardConfig) == 0x400, "Software keyboard config size is wrong");
virtual const char* name() override { return "Software Keyboard"; }
virtual Result::HorizonResult start() override;
virtual Result::HorizonResult start(const MemoryBlock* sharedMem, const std::vector<u8>& parameters, u32 appID) override;
virtual Result::HorizonResult receiveParameter(const Applets::Parameter& parameter) override;
virtual void reset() override;
SoftwareKeyboardApplet(Memory& memory, std::optional<Parameter>& nextParam) : AppletBase(memory, nextParam) {}
void closeKeyboard(u32 appID);
SoftwareKeyboardConfig config;
};
} // namespace Applets

View file

@ -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,

View file

@ -137,6 +137,7 @@ public:
void duplicateHandle();
void exitThread();
void mapMemoryBlock();
void unmapMemoryBlock();
void queryMemory();
void getCurrentProcessorNumber();
void getProcessID();

View file

@ -112,11 +112,12 @@ class Memory {
// This tracks our OS' memory allocations
std::vector<KernelMemoryTypes::MemoryInfo> memoryInfo;
std::array<SharedMemoryBlock, 4> sharedMemBlocks = {
std::array<SharedMemoryBlock, 5> 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:

View file

@ -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

View file

@ -60,12 +60,22 @@ 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);
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);
@ -86,6 +96,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<FramebufferUpdate*>(&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) {}

View file

@ -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);

View file

@ -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;
}
}

View file

@ -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<u8>& parameters, u32 appID) {
Applets::Parameter param = Applets::Parameter{
.senderID = appID,
.destID = AppletIDs::Application,
.signal = static_cast<u32>(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<u32>(APTSignal::Response),
.object = KernelHandles::APTCaptureSharedMemHandle,
.data = {},
};
nextParameter = param;
return Result::Success;
}

View file

@ -1,11 +1,87 @@
#include "applets/mii_selector.hpp"
#include <boost/crc.hpp>
#include <limits>
#include "kernel/handles.hpp"
using namespace Applets;
void MiiSelectorApplet::reset() {}
Result::HorizonResult MiiSelectorApplet::start() { return Result::Success; }
Result::HorizonResult MiiSelectorApplet::start(const MemoryBlock* sharedMem, const std::vector<u8>& parameters, u32 appID) {
// Get mii configuration from the application
std::memcpy(&config, &parameters[0], sizeof(config));
Applets::Parameter param = Applets::Parameter{
.senderID = appID,
.destID = AppletIDs::Application,
.signal = static_cast<u32>(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<u32>::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(&param.data[0], &output, sizeof(output));
nextParameter = param;
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<u32>(APTSignal::Response),
.object = KernelHandles::APTCaptureSharedMemHandle,
.data = {},
};
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<u32>::max();
result.selectedMiiData = miiData;
result.guestMiiName.fill(0x0);
return result;
}

View file

@ -1,20 +1,93 @@
#include "applets/software_keyboard.hpp"
#include <cstring>
#include <string>
#include "kernel/handles.hpp"
using namespace Applets;
void SoftwareKeyboardApplet::reset() {}
Result::HorizonResult SoftwareKeyboardApplet::start() { return Result::Success; }
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): {
Applets::Parameter param = Applets::Parameter{
.senderID = parameter.destID,
.destID = AppletIDs::Application,
.signal = static_cast<u32>(APTSignal::Response),
.object = KernelHandles::APTCaptureSharedMemHandle,
.data = {},
};
nextParameter = param;
break;
}
default: Helpers::panic("Unimplemented swkbd signal %d\n", parameter.signal);
}
return Result::Success;
}
Result::HorizonResult SoftwareKeyboardApplet::start(const MemoryBlock* sharedMem, const std::vector<u8>& 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, &parameters[0], sizeof(config));
const std::u16string text = u"Pand";
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;
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<u16>(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");
}
closeKeyboard(appID);
return Result::Success;
}
void SoftwareKeyboardApplet::closeKeyboard(u32 appID) {
Applets::Parameter param = Applets::Parameter{
.senderID = parameter.destID,
.senderID = appID,
.destID = AppletIDs::Application,
.signal = static_cast<u32>(APTSignal::Response),
.data = {},
.signal = static_cast<u32>(APTSignal::WakeupByExit),
.object = 0,
};
// Copy software keyboard configuration into the response parameter
param.data.resize(sizeof(config));
std::memcpy(&param.data[0], &config, sizeof(config));
nextParameter = param;
return Result::Success;
}

View file

@ -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;

View file

@ -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 {
@ -206,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;
}

View file

@ -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,39 @@ 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);
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);
const MemoryBlock* sharedMem = sharedMemObject ? sharedMemObject->getData<MemoryBlock>() : nullptr;
std::vector<u8> data;
data.reserve(bufferSize);
for (u32 i = 0; i < bufferSize; i++) {
data.push_back(mem.read8(buffer + i));
}
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);
}
}
void APTService::checkNew3DS(u32 messagePointer) {
log("APT::CheckNew3DS\n");
mem.write32(messagePointer, IPC::responseHeader(0x102, 2, 0));
@ -222,7 +256,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 +294,9 @@ 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);
// 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");
auto parameter = appletManager.receiveParameter();
@ -274,14 +310,21 @@ 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);
const u32 transferSize = std::min<u32>(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);
log("APT::GlanceParameter(app ID = %X, size = %04X) (STUBBED)\n", app, size);
// 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");
auto parameter = appletManager.glanceParameter();
@ -296,8 +339,13 @@ 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);
const u32 transferSize = std::min<u32>(size, parameter.data.size());
for (u32 i = 0; i < transferSize; i++) {
mem.write8(buffer + i, parameter.data[i]);
}
}
void APTService::replySleepQuery(u32 messagePointer) {

View file

@ -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
}

View file

@ -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;
@ -143,8 +145,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<u32>(type) - static_cast<u32>(GPUInterrupt::VBlank0); // 0 for top screen, 1 for bottom
// TODO: Offset depends on GSP thread being triggered
FramebufferUpdate* update = reinterpret_cast<FramebufferUpdate*>(&sharedMem[0x200 + screen * sizeof(FramebufferUpdate)]);
FramebufferUpdate* update = getFramebufferInfo(screen);
if (update->dirtyFlag & 1) {
setBufferSwapImpl(screen, update->framebufferInfo[update->index]);
@ -482,10 +483,50 @@ 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)");
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);
}

View file

@ -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");
constexpr 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");