From 79824d7d1b69382571e5c05fe3e66485212d06f5 Mon Sep 17 00:00:00 2001 From: Morph <39850852+Morph1984@users.noreply.github.com> Date: Wed, 14 Jul 2021 00:38:24 -0400 Subject: applets: Append applet_ prefix to backend applets --- src/core/CMakeLists.txt | 28 +- src/core/frontend/applets/software_keyboard.h | 2 +- src/core/frontend/applets/web_browser.h | 2 +- src/core/hle/service/am/am.cpp | 6 +- .../hle/service/am/applets/applet_controller.cpp | 253 +++++ .../hle/service/am/applets/applet_controller.h | 137 +++ src/core/hle/service/am/applets/applet_error.cpp | 194 ++++ src/core/hle/service/am/applets/applet_error.h | 53 + .../service/am/applets/applet_general_backend.cpp | 255 +++++ .../service/am/applets/applet_general_backend.h | 90 ++ .../service/am/applets/applet_profile_select.cpp | 78 ++ .../hle/service/am/applets/applet_profile_select.h | 59 ++ .../am/applets/applet_software_keyboard.cpp | 1082 ++++++++++++++++++++ .../service/am/applets/applet_software_keyboard.h | 166 +++ .../am/applets/applet_software_keyboard_types.h | 295 ++++++ .../hle/service/am/applets/applet_web_browser.cpp | 474 +++++++++ .../hle/service/am/applets/applet_web_browser.h | 88 ++ .../service/am/applets/applet_web_browser_types.h | 178 ++++ src/core/hle/service/am/applets/applets.cpp | 12 +- src/core/hle/service/am/applets/controller.cpp | 253 ----- src/core/hle/service/am/applets/controller.h | 137 --- src/core/hle/service/am/applets/error.cpp | 194 ---- src/core/hle/service/am/applets/error.h | 53 - .../hle/service/am/applets/general_backend.cpp | 255 ----- src/core/hle/service/am/applets/general_backend.h | 90 -- src/core/hle/service/am/applets/profile_select.cpp | 78 -- src/core/hle/service/am/applets/profile_select.h | 59 -- .../hle/service/am/applets/software_keyboard.cpp | 1082 -------------------- .../hle/service/am/applets/software_keyboard.h | 166 --- .../service/am/applets/software_keyboard_types.h | 295 ------ src/core/hle/service/am/applets/web_browser.cpp | 474 --------- src/core/hle/service/am/applets/web_browser.h | 88 -- src/core/hle/service/am/applets/web_types.h | 178 ---- 33 files changed, 3427 insertions(+), 3427 deletions(-) create mode 100644 src/core/hle/service/am/applets/applet_controller.cpp create mode 100644 src/core/hle/service/am/applets/applet_controller.h create mode 100644 src/core/hle/service/am/applets/applet_error.cpp create mode 100644 src/core/hle/service/am/applets/applet_error.h create mode 100644 src/core/hle/service/am/applets/applet_general_backend.cpp create mode 100644 src/core/hle/service/am/applets/applet_general_backend.h create mode 100644 src/core/hle/service/am/applets/applet_profile_select.cpp create mode 100644 src/core/hle/service/am/applets/applet_profile_select.h create mode 100644 src/core/hle/service/am/applets/applet_software_keyboard.cpp create mode 100644 src/core/hle/service/am/applets/applet_software_keyboard.h create mode 100644 src/core/hle/service/am/applets/applet_software_keyboard_types.h create mode 100644 src/core/hle/service/am/applets/applet_web_browser.cpp create mode 100644 src/core/hle/service/am/applets/applet_web_browser.h create mode 100644 src/core/hle/service/am/applets/applet_web_browser_types.h delete mode 100644 src/core/hle/service/am/applets/controller.cpp delete mode 100644 src/core/hle/service/am/applets/controller.h delete mode 100644 src/core/hle/service/am/applets/error.cpp delete mode 100644 src/core/hle/service/am/applets/error.h delete mode 100644 src/core/hle/service/am/applets/general_backend.cpp delete mode 100644 src/core/hle/service/am/applets/general_backend.h delete mode 100644 src/core/hle/service/am/applets/profile_select.cpp delete mode 100644 src/core/hle/service/am/applets/profile_select.h delete mode 100644 src/core/hle/service/am/applets/software_keyboard.cpp delete mode 100644 src/core/hle/service/am/applets/software_keyboard.h delete mode 100644 src/core/hle/service/am/applets/software_keyboard_types.h delete mode 100644 src/core/hle/service/am/applets/web_browser.cpp delete mode 100644 src/core/hle/service/am/applets/web_browser.h delete mode 100644 src/core/hle/service/am/applets/web_types.h diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index b2b0dbe05..48b16f8f7 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -272,22 +272,22 @@ add_library(core STATIC hle/service/am/applet_ae.h hle/service/am/applet_oe.cpp hle/service/am/applet_oe.h + hle/service/am/applets/applet_controller.cpp + hle/service/am/applets/applet_controller.h + hle/service/am/applets/applet_error.cpp + hle/service/am/applets/applet_error.h + hle/service/am/applets/applet_general_backend.cpp + hle/service/am/applets/applet_general_backend.h + hle/service/am/applets/applet_profile_select.cpp + hle/service/am/applets/applet_profile_select.h + hle/service/am/applets/applet_software_keyboard.cpp + hle/service/am/applets/applet_software_keyboard.h + hle/service/am/applets/applet_software_keyboard_types.h + hle/service/am/applets/applet_web_browser.cpp + hle/service/am/applets/applet_web_browser.h + hle/service/am/applets/applet_web_browser_types.h hle/service/am/applets/applets.cpp hle/service/am/applets/applets.h - hle/service/am/applets/controller.cpp - hle/service/am/applets/controller.h - hle/service/am/applets/error.cpp - hle/service/am/applets/error.h - hle/service/am/applets/general_backend.cpp - hle/service/am/applets/general_backend.h - hle/service/am/applets/profile_select.cpp - hle/service/am/applets/profile_select.h - hle/service/am/applets/software_keyboard.cpp - hle/service/am/applets/software_keyboard.h - hle/service/am/applets/software_keyboard_types.h - hle/service/am/applets/web_browser.cpp - hle/service/am/applets/web_browser.h - hle/service/am/applets/web_types.h hle/service/am/idle.cpp hle/service/am/idle.h hle/service/am/omm.cpp diff --git a/src/core/frontend/applets/software_keyboard.h b/src/core/frontend/applets/software_keyboard.h index 506eb35bb..228a548d4 100644 --- a/src/core/frontend/applets/software_keyboard.h +++ b/src/core/frontend/applets/software_keyboard.h @@ -9,7 +9,7 @@ #include "common/common_types.h" -#include "core/hle/service/am/applets/software_keyboard_types.h" +#include "core/hle/service/am/applets/applet_software_keyboard_types.h" namespace Core::Frontend { diff --git a/src/core/frontend/applets/web_browser.h b/src/core/frontend/applets/web_browser.h index d7bd44c27..915dde677 100644 --- a/src/core/frontend/applets/web_browser.h +++ b/src/core/frontend/applets/web_browser.h @@ -7,7 +7,7 @@ #include #include -#include "core/hle/service/am/applets/web_types.h" +#include "core/hle/service/am/applets/applet_web_browser_types.h" namespace Core::Frontend { diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp index 23ebc1138..41f1e6b68 100644 --- a/src/core/hle/service/am/am.cpp +++ b/src/core/hle/service/am/am.cpp @@ -24,10 +24,10 @@ #include "core/hle/service/am/am.h" #include "core/hle/service/am/applet_ae.h" #include "core/hle/service/am/applet_oe.h" +#include "core/hle/service/am/applets/applet_profile_select.h" +#include "core/hle/service/am/applets/applet_software_keyboard.h" +#include "core/hle/service/am/applets/applet_web_browser.h" #include "core/hle/service/am/applets/applets.h" -#include "core/hle/service/am/applets/profile_select.h" -#include "core/hle/service/am/applets/software_keyboard.h" -#include "core/hle/service/am/applets/web_browser.h" #include "core/hle/service/am/idle.h" #include "core/hle/service/am/omm.h" #include "core/hle/service/am/spsm.h" diff --git a/src/core/hle/service/am/applets/applet_controller.cpp b/src/core/hle/service/am/applets/applet_controller.cpp new file mode 100644 index 000000000..12682effe --- /dev/null +++ b/src/core/hle/service/am/applets/applet_controller.cpp @@ -0,0 +1,253 @@ +// Copyright 2020 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include +#include + +#include "common/assert.h" +#include "common/logging/log.h" +#include "common/string_util.h" +#include "core/core.h" +#include "core/frontend/applets/controller.h" +#include "core/hle/result.h" +#include "core/hle/service/am/am.h" +#include "core/hle/service/am/applets/applet_controller.h" +#include "core/hle/service/hid/controllers/npad.h" + +namespace Service::AM::Applets { + +// This error code (0x183ACA) is thrown when the applet fails to initialize. +[[maybe_unused]] constexpr ResultCode ERR_CONTROLLER_APPLET_3101{ErrorModule::HID, 3101}; +// This error code (0x183CCA) is thrown when the u32 result in ControllerSupportResultInfo is 2. +[[maybe_unused]] constexpr ResultCode ERR_CONTROLLER_APPLET_3102{ErrorModule::HID, 3102}; + +static Core::Frontend::ControllerParameters ConvertToFrontendParameters( + ControllerSupportArgPrivate private_arg, ControllerSupportArgHeader header, bool enable_text, + std::vector identification_colors, std::vector text) { + HID::Controller_NPad::NpadStyleSet npad_style_set; + npad_style_set.raw = private_arg.style_set; + + return { + .min_players = std::max(s8{1}, header.player_count_min), + .max_players = header.player_count_max, + .keep_controllers_connected = header.enable_take_over_connection, + .enable_single_mode = header.enable_single_mode, + .enable_border_color = header.enable_identification_color, + .border_colors = std::move(identification_colors), + .enable_explain_text = enable_text, + .explain_text = std::move(text), + .allow_pro_controller = npad_style_set.fullkey == 1, + .allow_handheld = npad_style_set.handheld == 1, + .allow_dual_joycons = npad_style_set.joycon_dual == 1, + .allow_left_joycon = npad_style_set.joycon_left == 1, + .allow_right_joycon = npad_style_set.joycon_right == 1, + }; +} + +Controller::Controller(Core::System& system_, LibraryAppletMode applet_mode_, + const Core::Frontend::ControllerApplet& frontend_) + : Applet{system_, applet_mode_}, frontend{frontend_}, system{system_} {} + +Controller::~Controller() = default; + +void Controller::Initialize() { + Applet::Initialize(); + + LOG_INFO(Service_HID, "Initializing Controller Applet."); + + LOG_DEBUG(Service_HID, + "Initializing Applet with common_args: arg_version={}, lib_version={}, " + "play_startup_sound={}, size={}, system_tick={}, theme_color={}", + common_args.arguments_version, common_args.library_version, + common_args.play_startup_sound, common_args.size, common_args.system_tick, + common_args.theme_color); + + controller_applet_version = ControllerAppletVersion{common_args.library_version}; + + const auto private_arg_storage = broker.PopNormalDataToApplet(); + ASSERT(private_arg_storage != nullptr); + + const auto& private_arg = private_arg_storage->GetData(); + ASSERT(private_arg.size() == sizeof(ControllerSupportArgPrivate)); + + std::memcpy(&controller_private_arg, private_arg.data(), private_arg.size()); + ASSERT_MSG(controller_private_arg.arg_private_size == sizeof(ControllerSupportArgPrivate), + "Unknown ControllerSupportArgPrivate revision={} with size={}", + controller_applet_version, controller_private_arg.arg_private_size); + + // Some games such as Cave Story+ set invalid values for the ControllerSupportMode. + // Defer to arg_size to set the ControllerSupportMode. + if (controller_private_arg.mode >= ControllerSupportMode::MaxControllerSupportMode) { + switch (controller_private_arg.arg_size) { + case sizeof(ControllerSupportArgOld): + case sizeof(ControllerSupportArgNew): + controller_private_arg.mode = ControllerSupportMode::ShowControllerSupport; + break; + case sizeof(ControllerUpdateFirmwareArg): + controller_private_arg.mode = ControllerSupportMode::ShowControllerFirmwareUpdate; + break; + default: + UNIMPLEMENTED_MSG("Unknown ControllerPrivateArg mode={} with arg_size={}", + controller_private_arg.mode, controller_private_arg.arg_size); + controller_private_arg.mode = ControllerSupportMode::ShowControllerSupport; + break; + } + } + + // Some games such as Cave Story+ set invalid values for the ControllerSupportCaller. + // This is always 0 (Application) except with ShowControllerFirmwareUpdateForSystem. + if (controller_private_arg.caller >= ControllerSupportCaller::MaxControllerSupportCaller) { + if (controller_private_arg.flag_1 && + controller_private_arg.mode == ControllerSupportMode::ShowControllerFirmwareUpdate) { + controller_private_arg.caller = ControllerSupportCaller::System; + } else { + controller_private_arg.caller = ControllerSupportCaller::Application; + } + } + + switch (controller_private_arg.mode) { + case ControllerSupportMode::ShowControllerSupport: + case ControllerSupportMode::ShowControllerStrapGuide: { + const auto user_arg_storage = broker.PopNormalDataToApplet(); + ASSERT(user_arg_storage != nullptr); + + const auto& user_arg = user_arg_storage->GetData(); + switch (controller_applet_version) { + case ControllerAppletVersion::Version3: + case ControllerAppletVersion::Version4: + case ControllerAppletVersion::Version5: + ASSERT(user_arg.size() == sizeof(ControllerSupportArgOld)); + std::memcpy(&controller_user_arg_old, user_arg.data(), user_arg.size()); + break; + case ControllerAppletVersion::Version7: + ASSERT(user_arg.size() == sizeof(ControllerSupportArgNew)); + std::memcpy(&controller_user_arg_new, user_arg.data(), user_arg.size()); + break; + default: + UNIMPLEMENTED_MSG("Unknown ControllerSupportArg revision={} with size={}", + controller_applet_version, controller_private_arg.arg_size); + ASSERT(user_arg.size() >= sizeof(ControllerSupportArgNew)); + std::memcpy(&controller_user_arg_new, user_arg.data(), sizeof(ControllerSupportArgNew)); + break; + } + break; + } + case ControllerSupportMode::ShowControllerFirmwareUpdate: { + const auto update_arg_storage = broker.PopNormalDataToApplet(); + ASSERT(update_arg_storage != nullptr); + + const auto& update_arg = update_arg_storage->GetData(); + ASSERT(update_arg.size() == sizeof(ControllerUpdateFirmwareArg)); + + std::memcpy(&controller_update_arg, update_arg.data(), update_arg.size()); + break; + } + default: { + UNIMPLEMENTED_MSG("Unimplemented ControllerSupportMode={}", controller_private_arg.mode); + break; + } + } +} + +bool Controller::TransactionComplete() const { + return complete; +} + +ResultCode Controller::GetStatus() const { + return status; +} + +void Controller::ExecuteInteractive() { + UNREACHABLE_MSG("Attempted to call interactive execution on non-interactive applet."); +} + +void Controller::Execute() { + switch (controller_private_arg.mode) { + case ControllerSupportMode::ShowControllerSupport: { + const auto parameters = [this] { + switch (controller_applet_version) { + case ControllerAppletVersion::Version3: + case ControllerAppletVersion::Version4: + case ControllerAppletVersion::Version5: + return ConvertToFrontendParameters( + controller_private_arg, controller_user_arg_old.header, + controller_user_arg_old.enable_explain_text, + std::vector( + controller_user_arg_old.identification_colors.begin(), + controller_user_arg_old.identification_colors.end()), + std::vector(controller_user_arg_old.explain_text.begin(), + controller_user_arg_old.explain_text.end())); + case ControllerAppletVersion::Version7: + default: + return ConvertToFrontendParameters( + controller_private_arg, controller_user_arg_new.header, + controller_user_arg_new.enable_explain_text, + std::vector( + controller_user_arg_new.identification_colors.begin(), + controller_user_arg_new.identification_colors.end()), + std::vector(controller_user_arg_new.explain_text.begin(), + controller_user_arg_new.explain_text.end())); + } + }(); + + is_single_mode = parameters.enable_single_mode; + + LOG_DEBUG(Service_HID, + "Controller Parameters: min_players={}, max_players={}, " + "keep_controllers_connected={}, enable_single_mode={}, enable_border_color={}, " + "enable_explain_text={}, allow_pro_controller={}, allow_handheld={}, " + "allow_dual_joycons={}, allow_left_joycon={}, allow_right_joycon={}", + parameters.min_players, parameters.max_players, + parameters.keep_controllers_connected, parameters.enable_single_mode, + parameters.enable_border_color, parameters.enable_explain_text, + parameters.allow_pro_controller, parameters.allow_handheld, + parameters.allow_dual_joycons, parameters.allow_left_joycon, + parameters.allow_right_joycon); + + frontend.ReconfigureControllers([this] { ConfigurationComplete(); }, parameters); + break; + } + case ControllerSupportMode::ShowControllerStrapGuide: + case ControllerSupportMode::ShowControllerFirmwareUpdate: + UNIMPLEMENTED_MSG("ControllerSupportMode={} is not implemented", + controller_private_arg.mode); + ConfigurationComplete(); + break; + default: { + ConfigurationComplete(); + break; + } + } +} + +void Controller::ConfigurationComplete() { + ControllerSupportResultInfo result_info{}; + + const auto& players = Settings::values.players.GetValue(); + + // If enable_single_mode is enabled, player_count is 1 regardless of any other parameters. + // Otherwise, only count connected players from P1-P8. + result_info.player_count = + is_single_mode + ? 1 + : static_cast(std::count_if(players.begin(), players.end() - 2, + [](const auto& player) { return player.connected; })); + + result_info.selected_id = HID::Controller_NPad::IndexToNPad(std::distance( + players.begin(), std::find_if(players.begin(), players.end(), + [](const auto& player) { return player.connected; }))); + + result_info.result = 0; + + LOG_DEBUG(Service_HID, "Result Info: player_count={}, selected_id={}, result={}", + result_info.player_count, result_info.selected_id, result_info.result); + + complete = true; + out_data = std::vector(sizeof(ControllerSupportResultInfo)); + std::memcpy(out_data.data(), &result_info, out_data.size()); + broker.PushNormalDataFromApplet(std::make_shared(system, std::move(out_data))); + broker.SignalStateChanged(); +} + +} // namespace Service::AM::Applets diff --git a/src/core/hle/service/am/applets/applet_controller.h b/src/core/hle/service/am/applets/applet_controller.h new file mode 100644 index 000000000..20617e91f --- /dev/null +++ b/src/core/hle/service/am/applets/applet_controller.h @@ -0,0 +1,137 @@ +// Copyright 2020 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include +#include + +#include "common/common_funcs.h" +#include "common/common_types.h" +#include "core/hle/result.h" +#include "core/hle/service/am/applets/applets.h" + +namespace Core { +class System; +} + +namespace Service::AM::Applets { + +using IdentificationColor = std::array; +using ExplainText = std::array; + +enum class ControllerAppletVersion : u32_le { + Version3 = 0x3, // 1.0.0 - 2.3.0 + Version4 = 0x4, // 3.0.0 - 5.1.0 + Version5 = 0x5, // 6.0.0 - 7.0.1 + Version7 = 0x7, // 8.0.0+ +}; + +enum class ControllerSupportMode : u8 { + ShowControllerSupport, + ShowControllerStrapGuide, + ShowControllerFirmwareUpdate, + + MaxControllerSupportMode, +}; + +enum class ControllerSupportCaller : u8 { + Application, + System, + + MaxControllerSupportCaller, +}; + +struct ControllerSupportArgPrivate { + u32 arg_private_size{}; + u32 arg_size{}; + bool flag_0{}; + bool flag_1{}; + ControllerSupportMode mode{}; + ControllerSupportCaller caller{}; + u32 style_set{}; + u32 joy_hold_type{}; +}; +static_assert(sizeof(ControllerSupportArgPrivate) == 0x14, + "ControllerSupportArgPrivate has incorrect size."); + +struct ControllerSupportArgHeader { + s8 player_count_min{}; + s8 player_count_max{}; + bool enable_take_over_connection{}; + bool enable_left_justify{}; + bool enable_permit_joy_dual{}; + bool enable_single_mode{}; + bool enable_identification_color{}; +}; +static_assert(sizeof(ControllerSupportArgHeader) == 0x7, + "ControllerSupportArgHeader has incorrect size."); + +// LibraryAppletVersion 0x3, 0x4, 0x5 +struct ControllerSupportArgOld { + ControllerSupportArgHeader header{}; + std::array identification_colors{}; + bool enable_explain_text{}; + std::array explain_text{}; +}; +static_assert(sizeof(ControllerSupportArgOld) == 0x21C, + "ControllerSupportArgOld has incorrect size."); + +// LibraryAppletVersion 0x7 +struct ControllerSupportArgNew { + ControllerSupportArgHeader header{}; + std::array identification_colors{}; + bool enable_explain_text{}; + std::array explain_text{}; +}; +static_assert(sizeof(ControllerSupportArgNew) == 0x430, + "ControllerSupportArgNew has incorrect size."); + +struct ControllerUpdateFirmwareArg { + bool enable_force_update{}; + INSERT_PADDING_BYTES(3); +}; +static_assert(sizeof(ControllerUpdateFirmwareArg) == 0x4, + "ControllerUpdateFirmwareArg has incorrect size."); + +struct ControllerSupportResultInfo { + s8 player_count{}; + INSERT_PADDING_BYTES(3); + u32 selected_id{}; + u32 result{}; +}; +static_assert(sizeof(ControllerSupportResultInfo) == 0xC, + "ControllerSupportResultInfo has incorrect size."); + +class Controller final : public Applet { +public: + explicit Controller(Core::System& system_, LibraryAppletMode applet_mode_, + const Core::Frontend::ControllerApplet& frontend_); + ~Controller() override; + + void Initialize() override; + + bool TransactionComplete() const override; + ResultCode GetStatus() const override; + void ExecuteInteractive() override; + void Execute() override; + + void ConfigurationComplete(); + +private: + const Core::Frontend::ControllerApplet& frontend; + Core::System& system; + + ControllerAppletVersion controller_applet_version; + ControllerSupportArgPrivate controller_private_arg; + ControllerSupportArgOld controller_user_arg_old; + ControllerSupportArgNew controller_user_arg_new; + ControllerUpdateFirmwareArg controller_update_arg; + bool complete{false}; + ResultCode status{ResultSuccess}; + bool is_single_mode{false}; + std::vector out_data; +}; + +} // namespace Service::AM::Applets diff --git a/src/core/hle/service/am/applets/applet_error.cpp b/src/core/hle/service/am/applets/applet_error.cpp new file mode 100644 index 000000000..ef6854d62 --- /dev/null +++ b/src/core/hle/service/am/applets/applet_error.cpp @@ -0,0 +1,194 @@ +// Copyright 2019 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include +#include +#include "common/assert.h" +#include "common/logging/log.h" +#include "common/string_util.h" +#include "core/core.h" +#include "core/frontend/applets/error.h" +#include "core/hle/kernel/k_process.h" +#include "core/hle/service/am/am.h" +#include "core/hle/service/am/applets/applet_error.h" +#include "core/reporter.h" + +namespace Service::AM::Applets { + +#pragma pack(push, 4) +struct ShowError { + u8 mode; + bool jump; + INSERT_PADDING_BYTES_NOINIT(4); + bool use_64bit_error_code; + INSERT_PADDING_BYTES_NOINIT(1); + u64 error_code_64; + u32 error_code_32; +}; +static_assert(sizeof(ShowError) == 0x14, "ShowError has incorrect size."); +#pragma pack(pop) + +struct ShowErrorRecord { + u8 mode; + bool jump; + INSERT_PADDING_BYTES_NOINIT(6); + u64 error_code_64; + u64 posix_time; +}; +static_assert(sizeof(ShowErrorRecord) == 0x18, "ShowErrorRecord has incorrect size."); + +struct SystemErrorArg { + u8 mode; + bool jump; + INSERT_PADDING_BYTES_NOINIT(6); + u64 error_code_64; + std::array language_code; + std::array main_text; + std::array detail_text; +}; +static_assert(sizeof(SystemErrorArg) == 0x1018, "SystemErrorArg has incorrect size."); + +struct ApplicationErrorArg { + u8 mode; + bool jump; + INSERT_PADDING_BYTES_NOINIT(6); + u32 error_code; + std::array language_code; + std::array main_text; + std::array detail_text; +}; +static_assert(sizeof(ApplicationErrorArg) == 0x1014, "ApplicationErrorArg has incorrect size."); + +union Error::ErrorArguments { + ShowError error; + ShowErrorRecord error_record; + SystemErrorArg system_error; + ApplicationErrorArg application_error; + std::array raw{}; +}; + +namespace { +template +void CopyArgumentData(const std::vector& data, T& variable) { + ASSERT(data.size() >= sizeof(T)); + std::memcpy(&variable, data.data(), sizeof(T)); +} + +ResultCode Decode64BitError(u64 error) { + const auto description = (error >> 32) & 0x1FFF; + auto module = error & 0x3FF; + if (module >= 2000) + module -= 2000; + module &= 0x1FF; + return {static_cast(module), static_cast(description)}; +} + +} // Anonymous namespace + +Error::Error(Core::System& system_, LibraryAppletMode applet_mode_, + const Core::Frontend::ErrorApplet& frontend_) + : Applet{system_, applet_mode_}, frontend{frontend_}, system{system_} {} + +Error::~Error() = default; + +void Error::Initialize() { + Applet::Initialize(); + args = std::make_unique(); + complete = false; + + const auto storage = broker.PopNormalDataToApplet(); + ASSERT(storage != nullptr); + const auto data = storage->GetData(); + + ASSERT(!data.empty()); + std::memcpy(&mode, data.data(), sizeof(ErrorAppletMode)); + + switch (mode) { + case ErrorAppletMode::ShowError: + CopyArgumentData(data, args->error); + if (args->error.use_64bit_error_code) { + error_code = Decode64BitError(args->error.error_code_64); + } else { + error_code = ResultCode(args->error.error_code_32); + } + break; + case ErrorAppletMode::ShowSystemError: + CopyArgumentData(data, args->system_error); + error_code = ResultCode(Decode64BitError(args->system_error.error_code_64)); + break; + case ErrorAppletMode::ShowApplicationError: + CopyArgumentData(data, args->application_error); + error_code = ResultCode(args->application_error.error_code); + break; + case ErrorAppletMode::ShowErrorRecord: + CopyArgumentData(data, args->error_record); + error_code = Decode64BitError(args->error_record.error_code_64); + break; + default: + UNIMPLEMENTED_MSG("Unimplemented LibAppletError mode={:02X}!", mode); + } +} + +bool Error::TransactionComplete() const { + return complete; +} + +ResultCode Error::GetStatus() const { + return ResultSuccess; +} + +void Error::ExecuteInteractive() { + UNREACHABLE_MSG("Unexpected interactive applet data!"); +} + +void Error::Execute() { + if (complete) { + return; + } + + const auto callback = [this] { DisplayCompleted(); }; + const auto title_id = system.CurrentProcess()->GetTitleID(); + const auto& reporter{system.GetReporter()}; + + switch (mode) { + case ErrorAppletMode::ShowError: + reporter.SaveErrorReport(title_id, error_code); + frontend.ShowError(error_code, callback); + break; + case ErrorAppletMode::ShowSystemError: + case ErrorAppletMode::ShowApplicationError: { + const auto is_system = mode == ErrorAppletMode::ShowSystemError; + const auto& main_text = + is_system ? args->system_error.main_text : args->application_error.main_text; + const auto& detail_text = + is_system ? args->system_error.detail_text : args->application_error.detail_text; + + const auto main_text_string = + Common::StringFromFixedZeroTerminatedBuffer(main_text.data(), main_text.size()); + const auto detail_text_string = + Common::StringFromFixedZeroTerminatedBuffer(detail_text.data(), detail_text.size()); + + reporter.SaveErrorReport(title_id, error_code, main_text_string, detail_text_string); + frontend.ShowCustomErrorText(error_code, main_text_string, detail_text_string, callback); + break; + } + case ErrorAppletMode::ShowErrorRecord: + reporter.SaveErrorReport(title_id, error_code, + fmt::format("{:016X}", args->error_record.posix_time)); + frontend.ShowErrorWithTimestamp( + error_code, std::chrono::seconds{args->error_record.posix_time}, callback); + break; + default: + UNIMPLEMENTED_MSG("Unimplemented LibAppletError mode={:02X}!", mode); + DisplayCompleted(); + } +} + +void Error::DisplayCompleted() { + complete = true; + broker.PushNormalDataFromApplet(std::make_shared(system, std::vector{})); + broker.SignalStateChanged(); +} + +} // namespace Service::AM::Applets diff --git a/src/core/hle/service/am/applets/applet_error.h b/src/core/hle/service/am/applets/applet_error.h new file mode 100644 index 000000000..8aa9046a5 --- /dev/null +++ b/src/core/hle/service/am/applets/applet_error.h @@ -0,0 +1,53 @@ +// Copyright 2019 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include "core/hle/result.h" +#include "core/hle/service/am/applets/applets.h" + +namespace Core { +class System; +} + +namespace Service::AM::Applets { + +enum class ErrorAppletMode : u8 { + ShowError = 0, + ShowSystemError = 1, + ShowApplicationError = 2, + ShowEula = 3, + ShowErrorPctl = 4, + ShowErrorRecord = 5, + ShowUpdateEula = 8, +}; + +class Error final : public Applet { +public: + explicit Error(Core::System& system_, LibraryAppletMode applet_mode_, + const Core::Frontend::ErrorApplet& frontend_); + ~Error() override; + + void Initialize() override; + + bool TransactionComplete() const override; + ResultCode GetStatus() const override; + void ExecuteInteractive() override; + void Execute() override; + + void DisplayCompleted(); + +private: + union ErrorArguments; + + const Core::Frontend::ErrorApplet& frontend; + ResultCode error_code = ResultSuccess; + ErrorAppletMode mode = ErrorAppletMode::ShowError; + std::unique_ptr args; + + bool complete = false; + Core::System& system; +}; + +} // namespace Service::AM::Applets diff --git a/src/core/hle/service/am/applets/applet_general_backend.cpp b/src/core/hle/service/am/applets/applet_general_backend.cpp new file mode 100644 index 000000000..0f413f9a0 --- /dev/null +++ b/src/core/hle/service/am/applets/applet_general_backend.cpp @@ -0,0 +1,255 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include + +#include "common/assert.h" +#include "common/hex_util.h" +#include "common/logging/log.h" +#include "core/core.h" +#include "core/frontend/applets/general_frontend.h" +#include "core/hle/kernel/k_process.h" +#include "core/hle/result.h" +#include "core/hle/service/am/am.h" +#include "core/hle/service/am/applets/applet_general_backend.h" +#include "core/reporter.h" + +namespace Service::AM::Applets { + +constexpr ResultCode ERROR_INVALID_PIN{ErrorModule::PCTL, 221}; + +static void LogCurrentStorage(AppletDataBroker& broker, std::string_view prefix) { + std::shared_ptr storage = broker.PopNormalDataToApplet(); + for (; storage != nullptr; storage = broker.PopNormalDataToApplet()) { + const auto data = storage->GetData(); + LOG_INFO(Service_AM, + "called (STUBBED), during {} received normal data with size={:08X}, data={}", + prefix, data.size(), Common::HexToString(data)); + } + + storage = broker.PopInteractiveDataToApplet(); + for (; storage != nullptr; storage = broker.PopInteractiveDataToApplet()) { + const auto data = storage->GetData(); + LOG_INFO(Service_AM, + "called (STUBBED), during {} received interactive data with size={:08X}, data={}", + prefix, data.size(), Common::HexToString(data)); + } +} + +Auth::Auth(Core::System& system_, LibraryAppletMode applet_mode_, + Core::Frontend::ParentalControlsApplet& frontend_) + : Applet{system_, applet_mode_}, frontend{frontend_}, system{system_} {} + +Auth::~Auth() = default; + +void Auth::Initialize() { + Applet::Initialize(); + complete = false; + + const auto storage = broker.PopNormalDataToApplet(); + ASSERT(storage != nullptr); + const auto data = storage->GetData(); + ASSERT(data.size() >= 0xC); + + struct Arg { + INSERT_PADDING_BYTES(4); + AuthAppletType type; + u8 arg0; + u8 arg1; + u8 arg2; + INSERT_PADDING_BYTES(1); + }; + static_assert(sizeof(Arg) == 0xC, "Arg (AuthApplet) has incorrect size."); + + Arg arg{}; + std::memcpy(&arg, data.data(), sizeof(Arg)); + + type = arg.type; + arg0 = arg.arg0; + arg1 = arg.arg1; + arg2 = arg.arg2; +} + +bool Auth::TransactionComplete() const { + return complete; +} + +ResultCode Auth::GetStatus() const { + return successful ? ResultSuccess : ERROR_INVALID_PIN; +} + +void Auth::ExecuteInteractive() { + UNREACHABLE_MSG("Unexpected interactive applet data."); +} + +void Auth::Execute() { + if (complete) { + return; + } + + const auto unimplemented_log = [this] { + UNIMPLEMENTED_MSG("Unimplemented Auth applet type for type={:08X}, arg0={:02X}, " + "arg1={:02X}, arg2={:02X}", + type, arg0, arg1, arg2); + }; + + switch (type) { + case AuthAppletType::ShowParentalAuthentication: { + const auto callback = [this](bool is_successful) { AuthFinished(is_successful); }; + + if (arg0 == 1 && arg1 == 0 && arg2 == 1) { + // ShowAuthenticatorForConfiguration + frontend.VerifyPINForSettings(callback); + } else if (arg1 == 0 && arg2 == 0) { + // ShowParentalAuthentication(bool) + frontend.VerifyPIN(callback, static_cast(arg0)); + } else { + unimplemented_log(); + } + break; + } + case AuthAppletType::RegisterParentalPasscode: { + const auto callback = [this] { AuthFinished(true); }; + + if (arg0 == 0 && arg1 == 0 && arg2 == 0) { + // RegisterParentalPasscode + frontend.RegisterPIN(callback); + } else { + unimplemented_log(); + } + break; + } + case AuthAppletType::ChangeParentalPasscode: { + const auto callback = [this] { AuthFinished(true); }; + + if (arg0 == 0 && arg1 == 0 && arg2 == 0) { + // ChangeParentalPasscode + frontend.ChangePIN(callback); + } else { + unimplemented_log(); + } + break; + } + default: + unimplemented_log(); + } +} + +void Auth::AuthFinished(bool is_successful) { + successful = is_successful; + + struct Return { + ResultCode result_code; + }; + static_assert(sizeof(Return) == 0x4, "Return (AuthApplet) has incorrect size."); + + Return return_{GetStatus()}; + + std::vector out(sizeof(Return)); + std::memcpy(out.data(), &return_, sizeof(Return)); + + broker.PushNormalDataFromApplet(std::make_shared(system, std::move(out))); + broker.SignalStateChanged(); +} + +PhotoViewer::PhotoViewer(Core::System& system_, LibraryAppletMode applet_mode_, + const Core::Frontend::PhotoViewerApplet& frontend_) + : Applet{system_, applet_mode_}, frontend{frontend_}, system{system_} {} + +PhotoViewer::~PhotoViewer() = default; + +void PhotoViewer::Initialize() { + Applet::Initialize(); + complete = false; + + const auto storage = broker.PopNormalDataToApplet(); + ASSERT(storage != nullptr); + const auto data = storage->GetData(); + ASSERT(!data.empty()); + mode = static_cast(data[0]); +} + +bool PhotoViewer::TransactionComplete() const { + return complete; +} + +ResultCode PhotoViewer::GetStatus() const { + return ResultSuccess; +} + +void PhotoViewer::ExecuteInteractive() { + UNREACHABLE_MSG("Unexpected interactive applet data."); +} + +void PhotoViewer::Execute() { + if (complete) + return; + + const auto callback = [this] { ViewFinished(); }; + switch (mode) { + case PhotoViewerAppletMode::CurrentApp: + frontend.ShowPhotosForApplication(system.CurrentProcess()->GetTitleID(), callback); + break; + case PhotoViewerAppletMode::AllApps: + frontend.ShowAllPhotos(callback); + break; + default: + UNIMPLEMENTED_MSG("Unimplemented PhotoViewer applet mode={:02X}!", mode); + } +} + +void PhotoViewer::ViewFinished() { + broker.PushNormalDataFromApplet(std::make_shared(system, std::vector{})); + broker.SignalStateChanged(); +} + +StubApplet::StubApplet(Core::System& system_, AppletId id_, LibraryAppletMode applet_mode_) + : Applet{system_, applet_mode_}, id{id_}, system{system_} {} + +StubApplet::~StubApplet() = default; + +void StubApplet::Initialize() { + LOG_WARNING(Service_AM, "called (STUBBED)"); + Applet::Initialize(); + + const auto data = broker.PeekDataToAppletForDebug(); + system.GetReporter().SaveUnimplementedAppletReport( + static_cast(id), common_args.arguments_version, common_args.library_version, + common_args.theme_color, common_args.play_startup_sound, common_args.system_tick, + data.normal, data.interactive); + + LogCurrentStorage(broker, "Initialize"); +} + +bool StubApplet::TransactionComplete() const { + LOG_WARNING(Service_AM, "called (STUBBED)"); + return true; +} + +ResultCode StubApplet::GetStatus() const { + LOG_WARNING(Service_AM, "called (STUBBED)"); + return ResultSuccess; +} + +void StubApplet::ExecuteInteractive() { + LOG_WARNING(Service_AM, "called (STUBBED)"); + LogCurrentStorage(broker, "ExecuteInteractive"); + + broker.PushNormalDataFromApplet(std::make_shared(system, std::vector(0x1000))); + broker.PushInteractiveDataFromApplet( + std::make_shared(system, std::vector(0x1000))); + broker.SignalStateChanged(); +} + +void StubApplet::Execute() { + LOG_WARNING(Service_AM, "called (STUBBED)"); + LogCurrentStorage(broker, "Execute"); + + broker.PushNormalDataFromApplet(std::make_shared(system, std::vector(0x1000))); + broker.PushInteractiveDataFromApplet( + std::make_shared(system, std::vector(0x1000))); + broker.SignalStateChanged(); +} + +} // namespace Service::AM::Applets diff --git a/src/core/hle/service/am/applets/applet_general_backend.h b/src/core/hle/service/am/applets/applet_general_backend.h new file mode 100644 index 000000000..7496ded88 --- /dev/null +++ b/src/core/hle/service/am/applets/applet_general_backend.h @@ -0,0 +1,90 @@ +// Copyright 2019 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include "core/hle/service/am/applets/applets.h" + +namespace Core { +class System; +} + +namespace Service::AM::Applets { + +enum class AuthAppletType : u32 { + ShowParentalAuthentication, + RegisterParentalPasscode, + ChangeParentalPasscode, +}; + +class Auth final : public Applet { +public: + explicit Auth(Core::System& system_, LibraryAppletMode applet_mode_, + Core::Frontend::ParentalControlsApplet& frontend_); + ~Auth() override; + + void Initialize() override; + bool TransactionComplete() const override; + ResultCode GetStatus() const override; + void ExecuteInteractive() override; + void Execute() override; + + void AuthFinished(bool is_successful = true); + +private: + Core::Frontend::ParentalControlsApplet& frontend; + Core::System& system; + bool complete = false; + bool successful = false; + + AuthAppletType type = AuthAppletType::ShowParentalAuthentication; + u8 arg0 = 0; + u8 arg1 = 0; + u8 arg2 = 0; +}; + +enum class PhotoViewerAppletMode : u8 { + CurrentApp = 0, + AllApps = 1, +}; + +class PhotoViewer final : public Applet { +public: + explicit PhotoViewer(Core::System& system_, LibraryAppletMode applet_mode_, + const Core::Frontend::PhotoViewerApplet& frontend_); + ~PhotoViewer() override; + + void Initialize() override; + bool TransactionComplete() const override; + ResultCode GetStatus() const override; + void ExecuteInteractive() override; + void Execute() override; + + void ViewFinished(); + +private: + const Core::Frontend::PhotoViewerApplet& frontend; + bool complete = false; + PhotoViewerAppletMode mode = PhotoViewerAppletMode::CurrentApp; + Core::System& system; +}; + +class StubApplet final : public Applet { +public: + explicit StubApplet(Core::System& system_, AppletId id_, LibraryAppletMode applet_mode_); + ~StubApplet() override; + + void Initialize() override; + + bool TransactionComplete() const override; + ResultCode GetStatus() const override; + void ExecuteInteractive() override; + void Execute() override; + +private: + AppletId id; + Core::System& system; +}; + +} // namespace Service::AM::Applets diff --git a/src/core/hle/service/am/applets/applet_profile_select.cpp b/src/core/hle/service/am/applets/applet_profile_select.cpp new file mode 100644 index 000000000..bdc21778e --- /dev/null +++ b/src/core/hle/service/am/applets/applet_profile_select.cpp @@ -0,0 +1,78 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include + +#include "common/assert.h" +#include "common/string_util.h" +#include "core/core.h" +#include "core/frontend/applets/profile_select.h" +#include "core/hle/service/am/am.h" +#include "core/hle/service/am/applets/applet_profile_select.h" + +namespace Service::AM::Applets { + +constexpr ResultCode ERR_USER_CANCELLED_SELECTION{ErrorModule::Account, 1}; + +ProfileSelect::ProfileSelect(Core::System& system_, LibraryAppletMode applet_mode_, + const Core::Frontend::ProfileSelectApplet& frontend_) + : Applet{system_, applet_mode_}, frontend{frontend_}, system{system_} {} + +ProfileSelect::~ProfileSelect() = default; + +void ProfileSelect::Initialize() { + complete = false; + status = ResultSuccess; + final_data.clear(); + + Applet::Initialize(); + + const auto user_config_storage = broker.PopNormalDataToApplet(); + ASSERT(user_config_storage != nullptr); + const auto& user_config = user_config_storage->GetData(); + + ASSERT(user_config.size() >= sizeof(UserSelectionConfig)); + std::memcpy(&config, user_config.data(), sizeof(UserSelectionConfig)); +} + +bool ProfileSelect::TransactionComplete() const { + return complete; +} + +ResultCode ProfileSelect::GetStatus() const { + return status; +} + +void ProfileSelect::ExecuteInteractive() { + UNREACHABLE_MSG("Attempted to call interactive execution on non-interactive applet."); +} + +void ProfileSelect::Execute() { + if (complete) { + broker.PushNormalDataFromApplet(std::make_shared(system, std::move(final_data))); + return; + } + + frontend.SelectProfile([this](std::optional uuid) { SelectionComplete(uuid); }); +} + +void ProfileSelect::SelectionComplete(std::optional uuid) { + UserSelectionOutput output{}; + + if (uuid.has_value() && uuid->uuid != Common::INVALID_UUID) { + output.result = 0; + output.uuid_selected = uuid->uuid; + } else { + status = ERR_USER_CANCELLED_SELECTION; + output.result = ERR_USER_CANCELLED_SELECTION.raw; + output.uuid_selected = Common::INVALID_UUID; + } + + final_data = std::vector(sizeof(UserSelectionOutput)); + std::memcpy(final_data.data(), &output, final_data.size()); + broker.PushNormalDataFromApplet(std::make_shared(system, std::move(final_data))); + broker.SignalStateChanged(); +} + +} // namespace Service::AM::Applets diff --git a/src/core/hle/service/am/applets/applet_profile_select.h b/src/core/hle/service/am/applets/applet_profile_select.h new file mode 100644 index 000000000..8fb76e6c4 --- /dev/null +++ b/src/core/hle/service/am/applets/applet_profile_select.h @@ -0,0 +1,59 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include + +#include "common/common_funcs.h" +#include "common/uuid.h" +#include "core/hle/result.h" +#include "core/hle/service/am/applets/applets.h" + +namespace Core { +class System; +} + +namespace Service::AM::Applets { + +struct UserSelectionConfig { + // TODO(DarkLordZach): RE this structure + // It seems to be flags and the like that determine the UI of the applet on the switch... from + // my research this is safe to ignore for now. + INSERT_PADDING_BYTES(0xA0); +}; +static_assert(sizeof(UserSelectionConfig) == 0xA0, "UserSelectionConfig has incorrect size."); + +struct UserSelectionOutput { + u64 result; + u128 uuid_selected; +}; +static_assert(sizeof(UserSelectionOutput) == 0x18, "UserSelectionOutput has incorrect size."); + +class ProfileSelect final : public Applet { +public: + explicit ProfileSelect(Core::System& system_, LibraryAppletMode applet_mode_, + const Core::Frontend::ProfileSelectApplet& frontend_); + ~ProfileSelect() override; + + void Initialize() override; + + bool TransactionComplete() const override; + ResultCode GetStatus() const override; + void ExecuteInteractive() override; + void Execute() override; + + void SelectionComplete(std::optional uuid); + +private: + const Core::Frontend::ProfileSelectApplet& frontend; + + UserSelectionConfig config; + bool complete = false; + ResultCode status = ResultSuccess; + std::vector final_data; + Core::System& system; +}; + +} // namespace Service::AM::Applets diff --git a/src/core/hle/service/am/applets/applet_software_keyboard.cpp b/src/core/hle/service/am/applets/applet_software_keyboard.cpp new file mode 100644 index 000000000..7cae90609 --- /dev/null +++ b/src/core/hle/service/am/applets/applet_software_keyboard.cpp @@ -0,0 +1,1082 @@ +// Copyright 2021 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "common/string_util.h" +#include "core/core.h" +#include "core/frontend/applets/software_keyboard.h" +#include "core/hle/service/am/am.h" +#include "core/hle/service/am/applets/applet_software_keyboard.h" + +namespace Service::AM::Applets { + +namespace { + +// The maximum number of UTF-16 characters that can be input into the swkbd text field. +constexpr u32 DEFAULT_MAX_TEXT_LENGTH = 500; + +constexpr std::size_t REPLY_BASE_SIZE = sizeof(SwkbdState) + sizeof(SwkbdReplyType); +constexpr std::size_t REPLY_UTF8_SIZE = 0x7D4; +constexpr std::size_t REPLY_UTF16_SIZE = 0x3EC; + +constexpr const char* GetTextCheckResultName(SwkbdTextCheckResult text_check_result) { + switch (text_check_result) { + case SwkbdTextCheckResult::Success: + return "Success"; + case SwkbdTextCheckResult::Failure: + return "Failure"; + case SwkbdTextCheckResult::Confirm: + return "Confirm"; + case SwkbdTextCheckResult::Silent: + return "Silent"; + default: + UNIMPLEMENTED_MSG("Unknown TextCheckResult={}", text_check_result); + return "Unknown"; + } +} + +void SetReplyBase(std::vector& reply, SwkbdState state, SwkbdReplyType reply_type) { + std::memcpy(reply.data(), &state, sizeof(SwkbdState)); + std::memcpy(reply.data() + sizeof(SwkbdState), &reply_type, sizeof(SwkbdReplyType)); +} + +} // Anonymous namespace + +SoftwareKeyboard::SoftwareKeyboard(Core::System& system_, LibraryAppletMode applet_mode_, + Core::Frontend::SoftwareKeyboardApplet& frontend_) + : Applet{system_, applet_mode_}, frontend{frontend_}, system{system_} {} + +SoftwareKeyboard::~SoftwareKeyboard() = default; + +void SoftwareKeyboard::Initialize() { + Applet::Initialize(); + + LOG_INFO(Service_AM, "Initializing Software Keyboard Applet with LibraryAppletMode={}", + applet_mode); + + LOG_DEBUG(Service_AM, + "Initializing Applet with common_args: arg_version={}, lib_version={}, " + "play_startup_sound={}, size={}, system_tick={}, theme_color={}", + common_args.arguments_version, common_args.library_version, + common_args.play_startup_sound, common_args.size, common_args.system_tick, + common_args.theme_color); + + swkbd_applet_version = SwkbdAppletVersion{common_args.library_version}; + + switch (applet_mode) { + case LibraryAppletMode::AllForeground: + InitializeForeground(); + break; + case LibraryAppletMode::Background: + case LibraryAppletMode::BackgroundIndirectDisplay: + InitializeBackground(applet_mode); + break; + default: + UNREACHABLE_MSG("Invalid LibraryAppletMode={}", applet_mode); + break; + } +} + +bool SoftwareKeyboard::TransactionComplete() const { + return complete; +} + +ResultCode SoftwareKeyboard::GetStatus() const { + return status; +} + +void SoftwareKeyboard::ExecuteInteractive() { + if (complete) { + return; + } + + if (is_background) { + ProcessInlineKeyboardRequest(); + } else { + ProcessTextCheck(); + } +} + +void SoftwareKeyboard::Execute() { + if (complete) { + return; + } + + if (is_background) { + return; + } + + ShowNormalKeyboard(); +} + +void SoftwareKeyboard::SubmitTextNormal(SwkbdResult result, std::u16string submitted_text) { + if (complete) { + return; + } + + if (swkbd_config_common.use_text_check && result == SwkbdResult::Ok) { + SubmitForTextCheck(submitted_text); + } else { + SubmitNormalOutputAndExit(result, submitted_text); + } +} + +void SoftwareKeyboard::SubmitTextInline(SwkbdReplyType reply_type, std::u16string submitted_text, + s32 cursor_position) { + if (complete) { + return; + } + + current_text = std::move(submitted_text); + current_cursor_position = cursor_position; + + if (inline_use_utf8) { + switch (reply_type) { + case SwkbdReplyType::ChangedString: + reply_type = SwkbdReplyType::ChangedStringUtf8; + break; + case SwkbdReplyType::MovedCursor: + reply_type = SwkbdReplyType::MovedCursorUtf8; + break; + case SwkbdReplyType::DecidedEnter: + reply_type = SwkbdReplyType::DecidedEnterUtf8; + break; + default: + break; + } + } + + if (use_changed_string_v2) { + switch (reply_type) { + case SwkbdReplyType::ChangedString: + reply_type = SwkbdReplyType::ChangedStringV2; + break; + case SwkbdReplyType::ChangedStringUtf8: + reply_type = SwkbdReplyType::ChangedStringUtf8V2; + break; + default: + break; + } + } + + if (use_moved_cursor_v2) { + switch (reply_type) { + case SwkbdReplyType::MovedCursor: + reply_type = SwkbdReplyType::MovedCursorV2; + break; + case SwkbdReplyType::MovedCursorUtf8: + reply_type = SwkbdReplyType::MovedCursorUtf8V2; + break; + default: + break; + } + } + + SendReply(reply_type); +} + +void SoftwareKeyboard::InitializeForeground() { + LOG_INFO(Service_AM, "Initializing Normal Software Keyboard Applet."); + + is_background = false; + + const auto swkbd_config_storage = broker.PopNormalDataToApplet(); + ASSERT(swkbd_config_storage != nullptr); + + const auto& swkbd_config_data = swkbd_config_storage->GetData(); + ASSERT(swkbd_config_data.size() >= sizeof(SwkbdConfigCommon)); + + std::memcpy(&swkbd_config_common, swkbd_config_data.data(), sizeof(SwkbdConfigCommon)); + + switch (swkbd_applet_version) { + case SwkbdAppletVersion::Version5: + case SwkbdAppletVersion::Version65542: + ASSERT(swkbd_config_data.size() == sizeof(SwkbdConfigCommon) + sizeof(SwkbdConfigOld)); + std::memcpy(&swkbd_config_old, swkbd_config_data.data() + sizeof(SwkbdConfigCommon), + sizeof(SwkbdConfigOld)); + break; + case SwkbdAppletVersion::Version196615: + case SwkbdAppletVersion::Version262152: + case SwkbdAppletVersion::Version327689: + ASSERT(swkbd_config_data.size() == sizeof(SwkbdConfigCommon) + sizeof(SwkbdConfigOld2)); + std::memcpy(&swkbd_config_old2, swkbd_config_data.data() + sizeof(SwkbdConfigCommon), + sizeof(SwkbdConfigOld2)); + break; + case SwkbdAppletVersion::Version393227: + case SwkbdAppletVersion::Version524301: + ASSERT(swkbd_config_data.size() == sizeof(SwkbdConfigCommon) + sizeof(SwkbdConfigNew)); + std::memcpy(&swkbd_config_new, swkbd_config_data.data() + sizeof(SwkbdConfigCommon), + sizeof(SwkbdConfigNew)); + break; + default: + UNIMPLEMENTED_MSG("Unknown SwkbdConfig revision={} with size={}", swkbd_applet_version, + swkbd_config_data.size()); + ASSERT(swkbd_config_data.size() >= sizeof(SwkbdConfigCommon) + sizeof(SwkbdConfigNew)); + std::memcpy(&swkbd_config_new, swkbd_config_data.data() + sizeof(SwkbdConfigCommon), + sizeof(SwkbdConfigNew)); + break; + } + + const auto work_buffer_storage = broker.PopNormalDataToApplet(); + ASSERT(work_buffer_storage != nullptr); + + if (swkbd_config_common.initial_string_length == 0) { + InitializeFrontendKeyboard(); + return; + } + + const auto& work_buffer = work_buffer_storage->GetData(); + + std::vector initial_string(swkbd_config_common.initial_string_length); + + std::memcpy(initial_string.data(), + work_buffer.data() + swkbd_config_common.initial_string_offset, + swkbd_config_common.initial_string_length * sizeof(char16_t)); + + initial_text = Common::UTF16StringFromFixedZeroTerminatedBuffer(initial_string.data(), + initial_string.size()); + + LOG_DEBUG(Service_AM, "\nInitial Text: {}", Common::UTF16ToUTF8(initial_text)); + + InitializeFrontendKeyboard(); +} + +void SoftwareKeyboard::InitializeBackground(LibraryAppletMode library_applet_mode) { + LOG_INFO(Service_AM, "Initializing Inline Software Keyboard Applet."); + + is_background = true; + + const auto swkbd_inline_initialize_arg_storage = broker.PopNormalDataToApplet(); + ASSERT(swkbd_inline_initialize_arg_storage != nullptr); + + const auto& swkbd_inline_initialize_arg = swkbd_inline_initialize_arg_storage->GetData(); + ASSERT(swkbd_inline_initialize_arg.size() == sizeof(SwkbdInitializeArg)); + + std::memcpy(&swkbd_initialize_arg, swkbd_inline_initialize_arg.data(), + swkbd_inline_initialize_arg.size()); + + if (swkbd_initialize_arg.library_applet_mode_flag) { + ASSERT(library_applet_mode == LibraryAppletMode::Background); + } else { + ASSERT(library_applet_mode == LibraryAppletMode::BackgroundIndirectDisplay); + } +} + +void SoftwareKeyboard::ProcessTextCheck() { + const auto text_check_storage = broker.PopInteractiveDataToApplet(); + ASSERT(text_check_storage != nullptr); + + const auto& text_check_data = text_check_storage->GetData(); + ASSERT(text_check_data.size() == sizeof(SwkbdTextCheck)); + + SwkbdTextCheck swkbd_text_check; + + std::memcpy(&swkbd_text_check, text_check_data.data(), sizeof(SwkbdTextCheck)); + + std::u16string text_check_message = + swkbd_text_check.text_check_result == SwkbdTextCheckResult::Failure || + swkbd_text_check.text_check_result == SwkbdTextCheckResult::Confirm + ? Common::UTF16StringFromFixedZeroTerminatedBuffer( + swkbd_text_check.text_check_message.data(), + swkbd_text_check.text_check_message.size()) + : u""; + + LOG_INFO(Service_AM, "\nTextCheckResult: {}\nTextCheckMessage: {}", + GetTextCheckResultName(swkbd_text_check.text_check_result), + Common::UTF16ToUTF8(text_check_message)); + + switch (swkbd_text_check.text_check_result) { + case SwkbdTextCheckResult::Success: + SubmitNormalOutputAndExit(SwkbdResult::Ok, current_text); + break; + case SwkbdTextCheckResult::Failure: + ShowTextCheckDialog(SwkbdTextCheckResult::Failure, std::move(text_check_message)); + break; + case SwkbdTextCheckResult::Confirm: + ShowTextCheckDialog(SwkbdTextCheckResult::Confirm, std::move(text_check_message)); + break; + case SwkbdTextCheckResult::Silent: + default: + break; + } +} + +void SoftwareKeyboard::ProcessInlineKeyboardRequest() { + const auto request_data_storage = broker.PopInteractiveDataToApplet(); + ASSERT(request_data_storage != nullptr); + + const auto& request_data = request_data_storage->GetData(); + ASSERT(request_data.size() >= sizeof(SwkbdRequestCommand)); + + SwkbdRequestCommand request_command; + + std::memcpy(&request_command, request_data.data(), sizeof(SwkbdRequestCommand)); + + switch (request_command) { + case SwkbdRequestCommand::Finalize: + RequestFinalize(request_data); + break; + case SwkbdRequestCommand::SetUserWordInfo: + RequestSetUserWordInfo(request_data); + break; + case SwkbdRequestCommand::SetCustomizeDic: + RequestSetCustomizeDic(request_data); + break; + case SwkbdRequestCommand::Calc: + RequestCalc(request_data); + break; + case SwkbdRequestCommand::SetCustomizedDictionaries: + RequestSetCustomizedDictionaries(request_data); + break; + case SwkbdRequestCommand::UnsetCustomizedDictionaries: + RequestUnsetCustomizedDictionaries(request_data); + break; + case SwkbdRequestCommand::SetChangedStringV2Flag: + RequestSetChangedStringV2Flag(request_data); + break; + case SwkbdRequestCommand::SetMovedCursorV2Flag: + RequestSetMovedCursorV2Flag(request_data); + break; + default: + UNIMPLEMENTED_MSG("Unknown SwkbdRequestCommand={}", request_command); + break; + } +} + +void SoftwareKeyboard::SubmitNormalOutputAndExit(SwkbdResult result, + std::u16string submitted_text) { + std::vector out_data(sizeof(SwkbdResult) + STRING_BUFFER_SIZE); + + if (swkbd_config_common.use_utf8) { + std::string utf8_submitted_text = Common::UTF16ToUTF8(submitted_text); + + LOG_DEBUG(Service_AM, "\nSwkbdResult: {}\nUTF-8 Submitted Text: {}", result, + utf8_submitted_text); + + std::memcpy(out_data.data(), &result, sizeof(SwkbdResult)); + std::memcpy(out_data.data() + sizeof(SwkbdResult), utf8_submitted_text.data(), + utf8_submitted_text.size()); + } else { + LOG_DEBUG(Service_AM, "\nSwkbdResult: {}\nUTF-16 Submitted Text: {}", result, + Common::UTF16ToUTF8(submitted_text)); + + std::memcpy(out_data.data(), &result, sizeof(SwkbdResult)); + std::memcpy(out_data.data() + sizeof(SwkbdResult), submitted_text.data(), + submitted_text.size() * sizeof(char16_t)); + } + + broker.PushNormalDataFromApplet(std::make_shared(system, std::move(out_data))); + + ExitKeyboard(); +} + +void SoftwareKeyboard::SubmitForTextCheck(std::u16string submitted_text) { + current_text = std::move(submitted_text); + + std::vector out_data(sizeof(u64) + STRING_BUFFER_SIZE); + + if (swkbd_config_common.use_utf8) { + std::string utf8_submitted_text = Common::UTF16ToUTF8(current_text); + const u64 buffer_size = sizeof(u64) + utf8_submitted_text.size(); + + LOG_DEBUG(Service_AM, "\nBuffer Size: {}\nUTF-8 Submitted Text: {}", buffer_size, + utf8_submitted_text); + + std::memcpy(out_data.data(), &buffer_size, sizeof(u64)); + std::memcpy(out_data.data() + sizeof(u64), utf8_submitted_text.data(), + utf8_submitted_text.size()); + } else { + const u64 buffer_size = sizeof(u64) + current_text.size() * sizeof(char16_t); + + LOG_DEBUG(Service_AM, "\nBuffer Size: {}\nUTF-16 Submitted Text: {}", buffer_size, + Common::UTF16ToUTF8(current_text)); + + std::memcpy(out_data.data(), &buffer_size, sizeof(u64)); + std::memcpy(out_data.data() + sizeof(u64), current_text.data(), + current_text.size() * sizeof(char16_t)); + } + + broker.PushInteractiveDataFromApplet(std::make_shared(system, std::move(out_data))); +} + +void SoftwareKeyboard::SendReply(SwkbdReplyType reply_type) { + switch (reply_type) { + case SwkbdReplyType::FinishedInitialize: + ReplyFinishedInitialize(); + break; + case SwkbdReplyType::Default: + ReplyDefault(); + break; + case SwkbdReplyType::ChangedString: + ReplyChangedString(); + break; + case SwkbdReplyType::MovedCursor: + ReplyMovedCursor(); + break; + case SwkbdReplyType::MovedTab: + ReplyMovedTab(); + break; + case SwkbdReplyType::DecidedEnter: + ReplyDecidedEnter(); + break; + case SwkbdReplyType::DecidedCancel: + ReplyDecidedCancel(); + break; + case SwkbdReplyType::ChangedStringUtf8: + ReplyChangedStringUtf8(); + break; + case SwkbdReplyType::MovedCursorUtf8: + ReplyMovedCursorUtf8(); + break; + case SwkbdReplyType::DecidedEnterUtf8: + ReplyDecidedEnterUtf8(); + break; + case SwkbdReplyType::UnsetCustomizeDic: + ReplyUnsetCustomizeDic(); + break; + case SwkbdReplyType::ReleasedUserWordInfo: + ReplyReleasedUserWordInfo(); + break; + case SwkbdReplyType::UnsetCustomizedDictionaries: + ReplyUnsetCustomizedDictionaries(); + break; + case SwkbdReplyType::ChangedStringV2: + ReplyChangedStringV2(); + break; + case SwkbdReplyType::MovedCursorV2: + ReplyMovedCursorV2(); + break; + case SwkbdReplyType::ChangedStringUtf8V2: + ReplyChangedStringUtf8V2(); + break; + case SwkbdReplyType::MovedCursorUtf8V2: + ReplyMovedCursorUtf8V2(); + break; + default: + UNIMPLEMENTED_MSG("Unknown SwkbdReplyType={}", reply_type); + ReplyDefault(); + break; + } +} + +void SoftwareKeyboard::ChangeState(SwkbdState state) { + swkbd_state = state; + + ReplyDefault(); +} + +void SoftwareKeyboard::InitializeFrontendKeyboard() { + if (is_background) { + const auto& appear_arg = swkbd_calc_arg.appear_arg; + + std::u16string ok_text = Common::UTF16StringFromFixedZeroTerminatedBuffer( + appear_arg.ok_text.data(), appear_arg.ok_text.size()); + + const u32 max_text_length = + appear_arg.max_text_length > 0 && appear_arg.max_text_length <= DEFAULT_MAX_TEXT_LENGTH + ? appear_arg.max_text_length + : DEFAULT_MAX_TEXT_LENGTH; + + const u32 min_text_length = + appear_arg.min_text_length <= max_text_length ? appear_arg.min_text_length : 0; + + const s32 initial_cursor_position = + current_cursor_position > 0 ? current_cursor_position : 0; + + const auto text_draw_type = + max_text_length <= 32 ? SwkbdTextDrawType::Line : SwkbdTextDrawType::Box; + + Core::Frontend::KeyboardInitializeParameters initialize_parameters{ + .ok_text{std::move(ok_text)}, + .header_text{}, + .sub_text{}, + .guide_text{}, + .initial_text{current_text}, + .max_text_length{max_text_length}, + .min_text_length{min_text_length}, + .initial_cursor_position{initial_cursor_position}, + .type{appear_arg.type}, + .password_mode{SwkbdPasswordMode::Disabled}, + .text_draw_type{text_draw_type}, + .key_disable_flags{appear_arg.key_disable_flags}, + .use_blur_background{false}, + .enable_backspace_button{swkbd_calc_arg.enable_backspace_button}, + .enable_return_button{appear_arg.enable_return_button}, + .disable_cancel_button{appear_arg.disable_cancel_button}, + }; + + frontend.InitializeKeyboard( + true, std::move(initialize_parameters), {}, + [this](SwkbdReplyType reply_type, std::u16string submitted_text, s32 cursor_position) { + SubmitTextInline(reply_type, submitted_text, cursor_position); + }); + } else { + std::u16string ok_text = Common::UTF16StringFromFixedZeroTerminatedBuffer( + swkbd_config_common.ok_text.data(), swkbd_config_common.ok_text.size()); + + std::u16string header_text = Common::UTF16StringFromFixedZeroTerminatedBuffer( + swkbd_config_common.header_text.data(), swkbd_config_common.header_text.size()); + + std::u16string sub_text = Common::UTF16StringFromFixedZeroTerminatedBuffer( + swkbd_config_common.sub_text.data(), swkbd_config_common.sub_text.size()); + + std::u16string guide_text = Common::UTF16StringFromFixedZeroTerminatedBuffer( + swkbd_config_common.guide_text.data(), swkbd_config_common.guide_text.size()); + + const u32 max_text_length = + swkbd_config_common.max_text_length > 0 && + swkbd_config_common.max_text_length <= DEFAULT_MAX_TEXT_LENGTH + ? swkbd_config_common.max_text_length + : DEFAULT_MAX_TEXT_LENGTH; + + const u32 min_text_length = swkbd_config_common.min_text_length <= max_text_length + ? swkbd_config_common.min_text_length + : 0; + + const s32 initial_cursor_position = [this] { + switch (swkbd_config_common.initial_cursor_position) { + case SwkbdInitialCursorPosition::Start: + default: + return 0; + case SwkbdInitialCursorPosition::End: + return static_cast(initial_text.size()); + } + }(); + + const auto text_draw_type = [this, max_text_length] { + switch (swkbd_config_common.text_draw_type) { + case SwkbdTextDrawType::Line: + default: + return max_text_length <= 32 ? SwkbdTextDrawType::Line : SwkbdTextDrawType::Box; + case SwkbdTextDrawType::Box: + case SwkbdTextDrawType::DownloadCode: + return swkbd_config_common.text_draw_type; + } + }(); + + const auto enable_return_button = text_draw_type == SwkbdTextDrawType::Box + ? swkbd_config_common.enable_return_button + : false; + + const auto disable_cancel_button = swkbd_applet_version >= SwkbdAppletVersion::Version393227 + ? swkbd_config_new.disable_cancel_button + : false; + + Core::Frontend::KeyboardInitializeParameters initialize_parameters{ + .ok_text{std::move(ok_text)}, + .header_text{std::move(header_text)}, + .sub_text{std::move(sub_text)}, + .guide_text{std::move(guide_text)}, + .initial_text{initial_text}, + .max_text_length{max_text_length}, + .min_text_length{min_text_length}, + .initial_cursor_position{initial_cursor_position}, + .type{swkbd_config_common.type}, + .password_mode{swkbd_config_common.password_mode}, + .text_draw_type{text_draw_type}, + .key_disable_flags{swkbd_config_common.key_disable_flags}, + .use_blur_background{swkbd_config_common.use_blur_background}, + .enable_backspace_button{true}, + .enable_return_button{enable_return_button}, + .disable_cancel_button{disable_cancel_button}, + }; + + frontend.InitializeKeyboard(false, std::move(initialize_parameters), + [this](SwkbdResult result, std::u16string submitted_text) { + SubmitTextNormal(result, submitted_text); + }, + {}); + } +} + +void SoftwareKeyboard::ShowNormalKeyboard() { + frontend.ShowNormalKeyboard(); +} + +void SoftwareKeyboard::ShowTextCheckDialog(SwkbdTextCheckResult text_check_result, + std::u16string text_check_message) { + frontend.ShowTextCheckDialog(text_check_result, std::move(text_check_message)); +} + +void SoftwareKeyboard::ShowInlineKeyboard() { + if (swkbd_state != SwkbdState::InitializedIsHidden) { + return; + } + + ChangeState(SwkbdState::InitializedIsAppearing); + + const auto& appear_arg = swkbd_calc_arg.appear_arg; + + const u32 max_text_length = + appear_arg.max_text_length > 0 && appear_arg.max_text_length <= DEFAULT_MAX_TEXT_LENGTH + ? appear_arg.max_text_length + : DEFAULT_MAX_TEXT_LENGTH; + + const u32 min_text_length = + appear_arg.min_text_length <= max_text_length ? appear_arg.min_text_length : 0; + + Core::Frontend::InlineAppearParameters appear_parameters{ + .max_text_length{max_text_length}, + .min_text_length{min_text_length}, + .key_top_scale_x{swkbd_calc_arg.key_top_scale_x}, + .key_top_scale_y{swkbd_calc_arg.key_top_scale_y}, + .key_top_translate_x{swkbd_calc_arg.key_top_translate_x}, + .key_top_translate_y{swkbd_calc_arg.key_top_translate_y}, + .type{appear_arg.type}, + .key_disable_flags{appear_arg.key_disable_flags}, + .key_top_as_floating{swkbd_calc_arg.key_top_as_floating}, + .enable_backspace_button{swkbd_calc_arg.enable_backspace_button}, + .enable_return_button{appear_arg.enable_return_button}, + .disable_cancel_button{appear_arg.disable_cancel_button}, + }; + + frontend.ShowInlineKeyboard(std::move(appear_parameters)); + + ChangeState(SwkbdState::InitializedIsShown); +} + +void SoftwareKeyboard::HideInlineKeyboard() { + if (swkbd_state != SwkbdState::InitializedIsShown) { + return; + } + + ChangeState(SwkbdState::InitializedIsDisappearing); + + frontend.HideInlineKeyboard(); + + ChangeState(SwkbdState::InitializedIsHidden); +} + +void SoftwareKeyboard::InlineTextChanged() { + Core::Frontend::InlineTextParameters text_parameters{ + .input_text{current_text}, + .cursor_position{current_cursor_position}, + }; + + frontend.InlineTextChanged(std::move(text_parameters)); +} + +void SoftwareKeyboard::ExitKeyboard() { + complete = true; + status = ResultSuccess; + + frontend.ExitKeyboard(); + + broker.SignalStateChanged(); +} + +// Inline Software Keyboard Requests + +void SoftwareKeyboard::RequestFinalize(const std::vector& request_data) { + LOG_DEBUG(Service_AM, "Processing Request: Finalize"); + + ChangeState(SwkbdState::NotInitialized); + + ExitKeyboard(); +} + +void SoftwareKeyboard::RequestSetUserWordInfo(const std::vector& request_data) { + LOG_WARNING(Service_AM, "SetUserWordInfo is not implemented."); +} + +void SoftwareKeyboard::RequestSetCustomizeDic(const std::vector& request_data) { + LOG_WARNING(Service_AM, "SetCustomizeDic is not implemented."); +} + +void SoftwareKeyboard::RequestCalc(const std::vector& request_data) { + LOG_DEBUG(Service_AM, "Processing Request: Calc"); + + ASSERT(request_data.size() == sizeof(SwkbdRequestCommand) + sizeof(SwkbdCalcArg)); + + std::memcpy(&swkbd_calc_arg, request_data.data() + sizeof(SwkbdRequestCommand), + sizeof(SwkbdCalcArg)); + + if (swkbd_calc_arg.flags.set_input_text) { + current_text = Common::UTF16StringFromFixedZeroTerminatedBuffer( + swkbd_calc_arg.input_text.data(), swkbd_calc_arg.input_text.size()); + } + + if (swkbd_calc_arg.flags.set_cursor_position) { + current_cursor_position = swkbd_calc_arg.cursor_position; + } + + if (swkbd_calc_arg.flags.set_utf8_mode) { + inline_use_utf8 = swkbd_calc_arg.utf8_mode; + } + + if (swkbd_state <= SwkbdState::InitializedIsHidden && + swkbd_calc_arg.flags.unset_customize_dic) { + ReplyUnsetCustomizeDic(); + } + + if (swkbd_state <= SwkbdState::InitializedIsHidden && + swkbd_calc_arg.flags.unset_user_word_info) { + ReplyReleasedUserWordInfo(); + } + + if (swkbd_state == SwkbdState::NotInitialized && swkbd_calc_arg.flags.set_initialize_arg) { + InitializeFrontendKeyboard(); + + ChangeState(SwkbdState::InitializedIsHidden); + + ReplyFinishedInitialize(); + } + + if (!swkbd_calc_arg.flags.set_initialize_arg && + (swkbd_calc_arg.flags.set_input_text || swkbd_calc_arg.flags.set_cursor_position)) { + InlineTextChanged(); + } + + if (swkbd_state == SwkbdState::InitializedIsHidden && swkbd_calc_arg.flags.appear) { + ShowInlineKeyboard(); + return; + } + + if (swkbd_state == SwkbdState::InitializedIsShown && swkbd_calc_arg.flags.disappear) { + HideInlineKeyboard(); + return; + } +} + +void SoftwareKeyboard::RequestSetCustomizedDictionaries(const std::vector& request_data) { + LOG_WARNING(Service_AM, "SetCustomizedDictionaries is not implemented."); +} + +void SoftwareKeyboard::RequestUnsetCustomizedDictionaries(const std::vector& request_data) { + LOG_WARNING(Service_AM, "(STUBBED) Processing Request: UnsetCustomizedDictionaries"); + + ReplyUnsetCustomizedDictionaries(); +} + +void SoftwareKeyboard::RequestSetChangedStringV2Flag(const std::vector& request_data) { + LOG_DEBUG(Service_AM, "Processing Request: SetChangedStringV2Flag"); + + ASSERT(request_data.size() == sizeof(SwkbdRequestCommand) + 1); + + std::memcpy(&use_changed_string_v2, request_data.data() + sizeof(SwkbdRequestCommand), 1); +} + +void SoftwareKeyboard::RequestSetMovedCursorV2Flag(const std::vector& request_data) { + LOG_DEBUG(Service_AM, "Processing Request: SetMovedCursorV2Flag"); + + ASSERT(request_data.size() == sizeof(SwkbdRequestCommand) + 1); + + std::memcpy(&use_moved_cursor_v2, request_data.data() + sizeof(SwkbdRequestCommand), 1); +} + +// Inline Software Keyboard Replies + +void SoftwareKeyboard::ReplyFinishedInitialize() { + LOG_DEBUG(Service_AM, "Sending Reply: FinishedInitialize"); + + std::vector reply(REPLY_BASE_SIZE + 1); + + SetReplyBase(reply, swkbd_state, SwkbdReplyType::FinishedInitialize); + + broker.PushInteractiveDataFromApplet(std::make_shared(system, std::move(reply))); +} + +void SoftwareKeyboard::ReplyDefault() { + LOG_DEBUG(Service_AM, "Sending Reply: Default"); + + std::vector reply(REPLY_BASE_SIZE); + + SetReplyBase(reply, swkbd_state, SwkbdReplyType::Default); + + broker.PushInteractiveDataFromApplet(std::make_shared(system, std::move(reply))); +} + +void SoftwareKeyboard::ReplyChangedString() { + LOG_DEBUG(Service_AM, "Sending Reply: ChangedString"); + + std::vector reply(REPLY_BASE_SIZE + REPLY_UTF16_SIZE + sizeof(SwkbdChangedStringArg)); + + SetReplyBase(reply, swkbd_state, SwkbdReplyType::ChangedString); + + const SwkbdChangedStringArg changed_string_arg{ + .text_length{static_cast(current_text.size())}, + .dictionary_start_cursor_position{-1}, + .dictionary_end_cursor_position{-1}, + .cursor_position{current_cursor_position}, + }; + + std::memcpy(reply.data() + REPLY_BASE_SIZE, current_text.data(), + current_text.size() * sizeof(char16_t)); + std::memcpy(reply.data() + REPLY_BASE_SIZE + REPLY_UTF16_SIZE, &changed_string_arg, + sizeof(SwkbdChangedStringArg)); + + broker.PushInteractiveDataFromApplet(std::make_shared(system, std::move(reply))); +} + +void SoftwareKeyboard::ReplyMovedCursor() { + LOG_DEBUG(Service_AM, "Sending Reply: MovedCursor"); + + std::vector reply(REPLY_BASE_SIZE + REPLY_UTF16_SIZE + sizeof(SwkbdMovedCursorArg)); + + SetReplyBase(reply, swkbd_state, SwkbdReplyType::MovedCursor); + + const SwkbdMovedCursorArg moved_cursor_arg{ + .text_length{static_cast(current_text.size())}, + .cursor_position{current_cursor_position}, + }; + + std::memcpy(reply.data() + REPLY_BASE_SIZE, current_text.data(), + current_text.size() * sizeof(char16_t)); + std::memcpy(reply.data() + REPLY_BASE_SIZE + REPLY_UTF16_SIZE, &moved_cursor_arg, + sizeof(SwkbdMovedCursorArg)); + + broker.PushInteractiveDataFromApplet(std::make_shared(system, std::move(reply))); +} + +void SoftwareKeyboard::ReplyMovedTab() { + LOG_DEBUG(Service_AM, "Sending Reply: MovedTab"); + + std::vector reply(REPLY_BASE_SIZE + REPLY_UTF16_SIZE + sizeof(SwkbdMovedTabArg)); + + SetReplyBase(reply, swkbd_state, SwkbdReplyType::MovedTab); + + const SwkbdMovedTabArg moved_tab_arg{ + .text_length{static_cast(current_text.size())}, + .cursor_position{current_cursor_position}, + }; + + std::memcpy(reply.data() + REPLY_BASE_SIZE, current_text.data(), + current_text.size() * sizeof(char16_t)); + std::memcpy(reply.data() + REPLY_BASE_SIZE + REPLY_UTF16_SIZE, &moved_tab_arg, + sizeof(SwkbdMovedTabArg)); + + broker.PushInteractiveDataFromApplet(std::make_shared(system, std::move(reply))); +} + +void SoftwareKeyboard::ReplyDecidedEnter() { + LOG_DEBUG(Service_AM, "Sending Reply: DecidedEnter"); + + std::vector reply(REPLY_BASE_SIZE + REPLY_UTF16_SIZE + sizeof(SwkbdDecidedEnterArg)); + + SetReplyBase(reply, swkbd_state, SwkbdReplyType::DecidedEnter); + + const SwkbdDecidedEnterArg decided_enter_arg{ + .text_length{static_cast(current_text.size())}, + }; + + std::memcpy(reply.data() + REPLY_BASE_SIZE, current_text.data(), + current_text.size() * sizeof(char16_t)); + std::memcpy(reply.data() + REPLY_BASE_SIZE + REPLY_UTF16_SIZE, &decided_enter_arg, + sizeof(SwkbdDecidedEnterArg)); + + broker.PushInteractiveDataFromApplet(std::make_shared(system, std::move(reply))); + + HideInlineKeyboard(); +} + +void SoftwareKeyboard::ReplyDecidedCancel() { + LOG_DEBUG(Service_AM, "Sending Reply: DecidedCancel"); + + std::vector reply(REPLY_BASE_SIZE); + + SetReplyBase(reply, swkbd_state, SwkbdReplyType::DecidedCancel); + + broker.PushInteractiveDataFromApplet(std::make_shared(system, std::move(reply))); + + HideInlineKeyboard(); +} + +void SoftwareKeyboard::ReplyChangedStringUtf8() { + LOG_DEBUG(Service_AM, "Sending Reply: ChangedStringUtf8"); + + std::vector reply(REPLY_BASE_SIZE + REPLY_UTF8_SIZE + sizeof(SwkbdChangedStringArg)); + + SetReplyBase(reply, swkbd_state, SwkbdReplyType::ChangedStringUtf8); + + std::string utf8_current_text = Common::UTF16ToUTF8(current_text); + + const SwkbdChangedStringArg changed_string_arg{ + .text_length{static_cast(current_text.size())}, + .dictionary_start_cursor_position{-1}, + .dictionary_end_cursor_position{-1}, + .cursor_position{current_cursor_position}, + }; + + std::memcpy(reply.data() + REPLY_BASE_SIZE, utf8_current_text.data(), utf8_current_text.size()); + std::memcpy(reply.data() + REPLY_BASE_SIZE + REPLY_UTF8_SIZE, &changed_string_arg, + sizeof(SwkbdChangedStringArg)); + + broker.PushInteractiveDataFromApplet(std::make_shared(system, std::move(reply))); +} + +void SoftwareKeyboard::ReplyMovedCursorUtf8() { + LOG_DEBUG(Service_AM, "Sending Reply: MovedCursorUtf8"); + + std::vector reply(REPLY_BASE_SIZE + REPLY_UTF8_SIZE + sizeof(SwkbdMovedCursorArg)); + + SetReplyBase(reply, swkbd_state, SwkbdReplyType::MovedCursorUtf8); + + std::string utf8_current_text = Common::UTF16ToUTF8(current_text); + + const SwkbdMovedCursorArg moved_cursor_arg{ + .text_length{static_cast(current_text.size())}, + .cursor_position{current_cursor_position}, + }; + + std::memcpy(reply.data() + REPLY_BASE_SIZE, utf8_current_text.data(), utf8_current_text.size()); + std::memcpy(reply.data() + REPLY_BASE_SIZE + REPLY_UTF8_SIZE, &moved_cursor_arg, + sizeof(SwkbdMovedCursorArg)); + + broker.PushInteractiveDataFromApplet(std::make_shared(system, std::move(reply))); +} + +void SoftwareKeyboard::ReplyDecidedEnterUtf8() { + LOG_DEBUG(Service_AM, "Sending Reply: DecidedEnterUtf8"); + + std::vector reply(REPLY_BASE_SIZE + REPLY_UTF8_SIZE + sizeof(SwkbdDecidedEnterArg)); + + SetReplyBase(reply, swkbd_state, SwkbdReplyType::DecidedEnterUtf8); + + std::string utf8_current_text = Common::UTF16ToUTF8(current_text); + + const SwkbdDecidedEnterArg decided_enter_arg{ + .text_length{static_cast(current_text.size())}, + }; + + std::memcpy(reply.data() + REPLY_BASE_SIZE, utf8_current_text.data(), utf8_current_text.size()); + std::memcpy(reply.data() + REPLY_BASE_SIZE + REPLY_UTF8_SIZE, &decided_enter_arg, + sizeof(SwkbdDecidedEnterArg)); + + broker.PushInteractiveDataFromApplet(std::make_shared(system, std::move(reply))); + + HideInlineKeyboard(); +} + +void SoftwareKeyboard::ReplyUnsetCustomizeDic() { + LOG_DEBUG(Service_AM, "Sending Reply: UnsetCustomizeDic"); + + std::vector reply(REPLY_BASE_SIZE); + + SetReplyBase(reply, swkbd_state, SwkbdReplyType::UnsetCustomizeDic); + + broker.PushInteractiveDataFromApplet(std::make_shared(system, std::move(reply))); +} + +void SoftwareKeyboard::ReplyReleasedUserWordInfo() { + LOG_DEBUG(Service_AM, "Sending Reply: ReleasedUserWordInfo"); + + std::vector reply(REPLY_BASE_SIZE); + + SetReplyBase(reply, swkbd_state, SwkbdReplyType::ReleasedUserWordInfo); + + broker.PushInteractiveDataFromApplet(std::make_shared(system, std::move(reply))); +} + +void SoftwareKeyboard::ReplyUnsetCustomizedDictionaries() { + LOG_DEBUG(Service_AM, "Sending Reply: UnsetCustomizedDictionaries"); + + std::vector reply(REPLY_BASE_SIZE); + + SetReplyBase(reply, swkbd_state, SwkbdReplyType::UnsetCustomizedDictionaries); + + broker.PushInteractiveDataFromApplet(std::make_shared(system, std::move(reply))); +} + +void SoftwareKeyboard::ReplyChangedStringV2() { + LOG_DEBUG(Service_AM, "Sending Reply: ChangedStringV2"); + + std::vector reply(REPLY_BASE_SIZE + REPLY_UTF16_SIZE + sizeof(SwkbdChangedStringArg) + 1); + + SetReplyBase(reply, swkbd_state, SwkbdReplyType::ChangedStringV2); + + const SwkbdChangedStringArg changed_string_arg{ + .text_length{static_cast(current_text.size())}, + .dictionary_start_cursor_position{-1}, + .dictionary_end_cursor_position{-1}, + .cursor_position{current_cursor_position}, + }; + + constexpr u8 flag = 0; + + std::memcpy(reply.data() + REPLY_BASE_SIZE, current_text.data(), + current_text.size() * sizeof(char16_t)); + std::memcpy(reply.data() + REPLY_BASE_SIZE + REPLY_UTF16_SIZE, &changed_string_arg, + sizeof(SwkbdChangedStringArg)); + std::memcpy(reply.data() + REPLY_BASE_SIZE + REPLY_UTF16_SIZE + sizeof(SwkbdChangedStringArg), + &flag, 1); + + broker.PushInteractiveDataFromApplet(std::make_shared(system, std::move(reply))); +} + +void SoftwareKeyboard::ReplyMovedCursorV2() { + LOG_DEBUG(Service_AM, "Sending Reply: MovedCursorV2"); + + std::vector reply(REPLY_BASE_SIZE + REPLY_UTF16_SIZE + sizeof(SwkbdMovedCursorArg) + 1); + + SetReplyBase(reply, swkbd_state, SwkbdReplyType::MovedCursorV2); + + const SwkbdMovedCursorArg moved_cursor_arg{ + .text_length{static_cast(current_text.size())}, + .cursor_position{current_cursor_position}, + }; + + constexpr u8 flag = 0; + + std::memcpy(reply.data() + REPLY_BASE_SIZE, current_text.data(), + current_text.size() * sizeof(char16_t)); + std::memcpy(reply.data() + REPLY_BASE_SIZE + REPLY_UTF16_SIZE, &moved_cursor_arg, + sizeof(SwkbdMovedCursorArg)); + std::memcpy(reply.data() + REPLY_BASE_SIZE + REPLY_UTF16_SIZE + sizeof(SwkbdMovedCursorArg), + &flag, 1); + + broker.PushInteractiveDataFromApplet(std::make_shared(system, std::move(reply))); +} + +void SoftwareKeyboard::ReplyChangedStringUtf8V2() { + LOG_DEBUG(Service_AM, "Sending Reply: ChangedStringUtf8V2"); + + std::vector reply(REPLY_BASE_SIZE + REPLY_UTF8_SIZE + sizeof(SwkbdChangedStringArg) + 1); + + SetReplyBase(reply, swkbd_state, SwkbdReplyType::ChangedStringUtf8V2); + + std::string utf8_current_text = Common::UTF16ToUTF8(current_text); + + const SwkbdChangedStringArg changed_string_arg{ + .text_length{static_cast(current_text.size())}, + .dictionary_start_cursor_position{-1}, + .dictionary_end_cursor_position{-1}, + .cursor_position{current_cursor_position}, + }; + + constexpr u8 flag = 0; + + std::memcpy(reply.data() + REPLY_BASE_SIZE, utf8_current_text.data(), utf8_current_text.size()); + std::memcpy(reply.data() + REPLY_BASE_SIZE + REPLY_UTF8_SIZE, &changed_string_arg, + sizeof(SwkbdChangedStringArg)); + std::memcpy(reply.data() + REPLY_BASE_SIZE + REPLY_UTF8_SIZE + sizeof(SwkbdChangedStringArg), + &flag, 1); + + broker.PushInteractiveDataFromApplet(std::make_shared(system, std::move(reply))); +} + +void SoftwareKeyboard::ReplyMovedCursorUtf8V2() { + LOG_DEBUG(Service_AM, "Sending Reply: MovedCursorUtf8V2"); + + std::vector reply(REPLY_BASE_SIZE + REPLY_UTF8_SIZE + sizeof(SwkbdMovedCursorArg) + 1); + + SetReplyBase(reply, swkbd_state, SwkbdReplyType::MovedCursorUtf8V2); + + std::string utf8_current_text = Common::UTF16ToUTF8(current_text); + + const SwkbdMovedCursorArg moved_cursor_arg{ + .text_length{static_cast(current_text.size())}, + .cursor_position{current_cursor_position}, + }; + + constexpr u8 flag = 0; + + std::memcpy(reply.data() + REPLY_BASE_SIZE, utf8_current_text.data(), utf8_current_text.size()); + std::memcpy(reply.data() + REPLY_BASE_SIZE + REPLY_UTF8_SIZE, &moved_cursor_arg, + sizeof(SwkbdMovedCursorArg)); + std::memcpy(reply.data() + REPLY_BASE_SIZE + REPLY_UTF8_SIZE + sizeof(SwkbdMovedCursorArg), + &flag, 1); + + broker.PushInteractiveDataFromApplet(std::make_shared(system, std::move(reply))); +} + +} // namespace Service::AM::Applets diff --git a/src/core/hle/service/am/applets/applet_software_keyboard.h b/src/core/hle/service/am/applets/applet_software_keyboard.h new file mode 100644 index 000000000..9aef1bf11 --- /dev/null +++ b/src/core/hle/service/am/applets/applet_software_keyboard.h @@ -0,0 +1,166 @@ +// Copyright 2021 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include "common/common_funcs.h" +#include "common/common_types.h" +#include "core/hle/result.h" +#include "core/hle/service/am/applets/applet_software_keyboard_types.h" +#include "core/hle/service/am/applets/applets.h" + +namespace Core { +class System; +} + +namespace Service::AM::Applets { + +class SoftwareKeyboard final : public Applet { +public: + explicit SoftwareKeyboard(Core::System& system_, LibraryAppletMode applet_mode_, + Core::Frontend::SoftwareKeyboardApplet& frontend_); + ~SoftwareKeyboard() override; + + void Initialize() override; + + bool TransactionComplete() const override; + ResultCode GetStatus() const override; + void ExecuteInteractive() override; + void Execute() override; + + /** + * Submits the input text to the application. + * If text checking is enabled, the application will verify the input text. + * If use_utf8 is enabled, the input text will be converted to UTF-8 prior to being submitted. + * This should only be used by the normal software keyboard. + * + * @param result SwkbdResult enum + * @param submitted_text UTF-16 encoded string + */ + void SubmitTextNormal(SwkbdResult result, std::u16string submitted_text); + + /** + * Submits the input text to the application. + * If utf8_mode is enabled, the input text will be converted to UTF-8 prior to being submitted. + * This should only be used by the inline software keyboard. + * + * @param reply_type SwkbdReplyType enum + * @param submitted_text UTF-16 encoded string + * @param cursor_position The current position of the text cursor + */ + void SubmitTextInline(SwkbdReplyType reply_type, std::u16string submitted_text, + s32 cursor_position); + +private: + /// Initializes the normal software keyboard. + void InitializeForeground(); + + /// Initializes the inline software keyboard. + void InitializeBackground(LibraryAppletMode library_applet_mode); + + /// Processes the text check sent by the application. + void ProcessTextCheck(); + + /// Processes the inline software keyboard request command sent by the application. + void ProcessInlineKeyboardRequest(); + + /// Submits the input text and exits the applet. + void SubmitNormalOutputAndExit(SwkbdResult result, std::u16string submitted_text); + + /// Submits the input text for text checking. + void SubmitForTextCheck(std::u16string submitted_text); + + /// Sends a reply to the application after processing a request command. + void SendReply(SwkbdReplyType reply_type); + + /// Changes the inline keyboard state. + void ChangeState(SwkbdState state); + + /** + * Signals the frontend to initialize the software keyboard with common parameters. + * This initializes either the normal software keyboard or the inline software keyboard + * depending on the state of is_background. + * Note that this does not cause the keyboard to appear. + * Use the respective Show*Keyboard() functions to cause the respective keyboards to appear. + */ + void InitializeFrontendKeyboard(); + + /// Signals the frontend to show the normal software keyboard. + void ShowNormalKeyboard(); + + /// Signals the frontend to show the text check dialog. + void ShowTextCheckDialog(SwkbdTextCheckResult text_check_result, + std::u16string text_check_message); + + /// Signals the frontend to show the inline software keyboard. + void ShowInlineKeyboard(); + + /// Signals the frontend to hide the inline software keyboard. + void HideInlineKeyboard(); + + /// Signals the frontend that the current inline keyboard text has changed. + void InlineTextChanged(); + + /// Signals both the frontend and application that the software keyboard is exiting. + void ExitKeyboard(); + + // Inline Software Keyboard Requests + + void RequestFinalize(const std::vector& request_data); + void RequestSetUserWordInfo(const std::vector& request_data); + void RequestSetCustomizeDic(const std::vector& request_data); + void RequestCalc(const std::vector& request_data); + void RequestSetCustomizedDictionaries(const std::vector& request_data); + void RequestUnsetCustomizedDictionaries(const std::vector& request_data); + void RequestSetChangedStringV2Flag(const std::vector& request_data); + void RequestSetMovedCursorV2Flag(const std::vector& request_data); + + // Inline Software Keyboard Replies + + void ReplyFinishedInitialize(); + void ReplyDefault(); + void ReplyChangedString(); + void ReplyMovedCursor(); + void ReplyMovedTab(); + void ReplyDecidedEnter(); + void ReplyDecidedCancel(); + void ReplyChangedStringUtf8(); + void ReplyMovedCursorUtf8(); + void ReplyDecidedEnterUtf8(); + void ReplyUnsetCustomizeDic(); + void ReplyReleasedUserWordInfo(); + void ReplyUnsetCustomizedDictionaries(); + void ReplyChangedStringV2(); + void ReplyMovedCursorV2(); + void ReplyChangedStringUtf8V2(); + void ReplyMovedCursorUtf8V2(); + + Core::Frontend::SoftwareKeyboardApplet& frontend; + Core::System& system; + + SwkbdAppletVersion swkbd_applet_version; + + SwkbdConfigCommon swkbd_config_common; + SwkbdConfigOld swkbd_config_old; + SwkbdConfigOld2 swkbd_config_old2; + SwkbdConfigNew swkbd_config_new; + std::u16string initial_text; + + SwkbdState swkbd_state{SwkbdState::NotInitialized}; + SwkbdInitializeArg swkbd_initialize_arg; + SwkbdCalcArg swkbd_calc_arg; + bool use_changed_string_v2{false}; + bool use_moved_cursor_v2{false}; + bool inline_use_utf8{false}; + s32 current_cursor_position{}; + + std::u16string current_text; + + bool is_background{false}; + + bool complete{false}; + ResultCode status{ResultSuccess}; +}; + +} // namespace Service::AM::Applets diff --git a/src/core/hle/service/am/applets/applet_software_keyboard_types.h b/src/core/hle/service/am/applets/applet_software_keyboard_types.h new file mode 100644 index 000000000..21aa8e800 --- /dev/null +++ b/src/core/hle/service/am/applets/applet_software_keyboard_types.h @@ -0,0 +1,295 @@ +// Copyright 2021 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include + +#include "common/bit_field.h" +#include "common/common_funcs.h" +#include "common/common_types.h" +#include "common/swap.h" + +namespace Service::AM::Applets { + +constexpr std::size_t MAX_OK_TEXT_LENGTH = 8; +constexpr std::size_t MAX_HEADER_TEXT_LENGTH = 64; +constexpr std::size_t MAX_SUB_TEXT_LENGTH = 128; +constexpr std::size_t MAX_GUIDE_TEXT_LENGTH = 256; +constexpr std::size_t STRING_BUFFER_SIZE = 0x7D4; + +enum class SwkbdAppletVersion : u32_le { + Version5 = 0x5, // 1.0.0 + Version65542 = 0x10006, // 2.0.0 - 2.3.0 + Version196615 = 0x30007, // 3.0.0 - 3.0.2 + Version262152 = 0x40008, // 4.0.0 - 4.1.0 + Version327689 = 0x50009, // 5.0.0 - 5.1.0 + Version393227 = 0x6000B, // 6.0.0 - 7.0.1 + Version524301 = 0x8000D, // 8.0.0+ +}; + +enum class SwkbdType : u32 { + Normal, + NumberPad, + Qwerty, + Unknown3, + Latin, + SimplifiedChinese, + TraditionalChinese, + Korean, +}; + +enum class SwkbdInitialCursorPosition : u32 { + Start, + End, +}; + +enum class SwkbdPasswordMode : u32 { + Disabled, + Enabled, +}; + +enum class SwkbdTextDrawType : u32 { + Line, + Box, + DownloadCode, +}; + +enum class SwkbdResult : u32 { + Ok, + Cancel, +}; + +enum class SwkbdTextCheckResult : u32 { + Success, + Failure, + Confirm, + Silent, +}; + +enum class SwkbdState : u32 { + NotInitialized = 0x0, + InitializedIsHidden = 0x1, + InitializedIsAppearing = 0x2, + InitializedIsShown = 0x3, + InitializedIsDisappearing = 0x4, +}; + +enum class SwkbdRequestCommand : u32 { + Finalize = 0x4, + SetUserWordInfo = 0x6, + SetCustomizeDic = 0x7, + Calc = 0xA, + SetCustomizedDictionaries = 0xB, + UnsetCustomizedDictionaries = 0xC, + SetChangedStringV2Flag = 0xD, + SetMovedCursorV2Flag = 0xE, +}; + +enum class SwkbdReplyType : u32 { + FinishedInitialize = 0x0, + Default = 0x1, + ChangedString = 0x2, + MovedCursor = 0x3, + MovedTab = 0x4, + DecidedEnter = 0x5, + DecidedCancel = 0x6, + ChangedStringUtf8 = 0x7, + MovedCursorUtf8 = 0x8, + DecidedEnterUtf8 = 0x9, + UnsetCustomizeDic = 0xA, + ReleasedUserWordInfo = 0xB, + UnsetCustomizedDictionaries = 0xC, + ChangedStringV2 = 0xD, + MovedCursorV2 = 0xE, + ChangedStringUtf8V2 = 0xF, + MovedCursorUtf8V2 = 0x10, +}; + +struct SwkbdKeyDisableFlags { + union { + u32 raw{}; + + BitField<1, 1, u32> space; + BitField<2, 1, u32> at; + BitField<3, 1, u32> percent; + BitField<4, 1, u32> slash; + BitField<5, 1, u32> backslash; + BitField<6, 1, u32> numbers; + BitField<7, 1, u32> download_code; + BitField<8, 1, u32> username; + }; +}; +static_assert(sizeof(SwkbdKeyDisableFlags) == 0x4, "SwkbdKeyDisableFlags has incorrect size."); + +struct SwkbdConfigCommon { + SwkbdType type{}; + std::array ok_text{}; + char16_t left_optional_symbol_key{}; + char16_t right_optional_symbol_key{}; + bool use_prediction{}; + INSERT_PADDING_BYTES(1); + SwkbdKeyDisableFlags key_disable_flags{}; + SwkbdInitialCursorPosition initial_cursor_position{}; + std::array header_text{}; + std::array sub_text{}; + std::array guide_text{}; + u32 max_text_length{}; + u32 min_text_length{}; + SwkbdPasswordMode password_mode{}; + SwkbdTextDrawType text_draw_type{}; + bool enable_return_button{}; + bool use_utf8{}; + bool use_blur_background{}; + INSERT_PADDING_BYTES(1); + u32 initial_string_offset{}; + u32 initial_string_length{}; + u32 user_dictionary_offset{}; + u32 user_dictionary_entries{}; + bool use_text_check{}; + INSERT_PADDING_BYTES(3); +}; +static_assert(sizeof(SwkbdConfigCommon) == 0x3D4, "SwkbdConfigCommon has incorrect size."); + +#pragma pack(push, 4) +// SwkbdAppletVersion 0x5, 0x10006 +struct SwkbdConfigOld { + INSERT_PADDING_WORDS(1); + VAddr text_check_callback{}; +}; +static_assert(sizeof(SwkbdConfigOld) == 0x3E0 - sizeof(SwkbdConfigCommon), + "SwkbdConfigOld has incorrect size."); + +// SwkbdAppletVersion 0x30007, 0x40008, 0x50009 +struct SwkbdConfigOld2 { + INSERT_PADDING_WORDS(1); + VAddr text_check_callback{}; + std::array text_grouping{}; +}; +static_assert(sizeof(SwkbdConfigOld2) == 0x400 - sizeof(SwkbdConfigCommon), + "SwkbdConfigOld2 has incorrect size."); + +// SwkbdAppletVersion 0x6000B, 0x8000D +struct SwkbdConfigNew { + std::array text_grouping{}; + std::array customized_dictionary_set_entries{}; + u8 total_customized_dictionary_set_entries{}; + bool disable_cancel_button{}; + INSERT_PADDING_BYTES(18); +}; +static_assert(sizeof(SwkbdConfigNew) == 0x4C8 - sizeof(SwkbdConfigCommon), + "SwkbdConfigNew has incorrect size."); +#pragma pack(pop) + +struct SwkbdTextCheck { + SwkbdTextCheckResult text_check_result{}; + std::array text_check_message{}; +}; +static_assert(sizeof(SwkbdTextCheck) == 0x7D8, "SwkbdTextCheck has incorrect size."); + +struct SwkbdCalcArgFlags { + union { + u64 raw{}; + + BitField<0, 1, u64> set_initialize_arg; + BitField<1, 1, u64> set_volume; + BitField<2, 1, u64> appear; + BitField<3, 1, u64> set_input_text; + BitField<4, 1, u64> set_cursor_position; + BitField<5, 1, u64> set_utf8_mode; + BitField<6, 1, u64> unset_customize_dic; + BitField<7, 1, u64> disappear; + BitField<8, 1, u64> unknown; + BitField<9, 1, u64> set_key_top_translate_scale; + BitField<10, 1, u64> unset_user_word_info; + BitField<11, 1, u64> set_disable_hardware_keyboard; + }; +}; +static_assert(sizeof(SwkbdCalcArgFlags) == 0x8, "SwkbdCalcArgFlags has incorrect size."); + +struct SwkbdInitializeArg { + u32 unknown{}; + bool library_applet_mode_flag{}; + bool is_above_hos_500{}; + INSERT_PADDING_BYTES(2); +}; +static_assert(sizeof(SwkbdInitializeArg) == 0x8, "SwkbdInitializeArg has incorrect size."); + +struct SwkbdAppearArg { + SwkbdType type{}; + std::array ok_text{}; + char16_t left_optional_symbol_key{}; + char16_t right_optional_symbol_key{}; + bool use_prediction{}; + bool disable_cancel_button{}; + SwkbdKeyDisableFlags key_disable_flags{}; + u32 max_text_length{}; + u32 min_text_length{}; + bool enable_return_button{}; + INSERT_PADDING_BYTES(3); + u32 flags{}; + INSERT_PADDING_WORDS(6); +}; +static_assert(sizeof(SwkbdAppearArg) == 0x48, "SwkbdAppearArg has incorrect size."); + +struct SwkbdCalcArg { + u32 unknown{}; + u16 calc_arg_size{}; + INSERT_PADDING_BYTES(2); + SwkbdCalcArgFlags flags{}; + SwkbdInitializeArg initialize_arg{}; + f32 volume{}; + s32 cursor_position{}; + SwkbdAppearArg appear_arg{}; + std::array input_text{}; + bool utf8_mode{}; + INSERT_PADDING_BYTES(1); + bool enable_backspace_button{}; + INSERT_PADDING_BYTES(3); + bool key_top_as_floating{}; + bool footer_scalable{}; + bool alpha_enabled_in_input_mode{}; + u8 input_mode_fade_type{}; + bool disable_touch{}; + bool disable_hardware_keyboard{}; + INSERT_PADDING_BYTES(8); + f32 key_top_scale_x{}; + f32 key_top_scale_y{}; + f32 key_top_translate_x{}; + f32 key_top_translate_y{}; + f32 key_top_bg_alpha{}; + f32 footer_bg_alpha{}; + f32 balloon_scale{}; + INSERT_PADDING_WORDS(4); + u8 se_group{}; + INSERT_PADDING_BYTES(3); +}; +static_assert(sizeof(SwkbdCalcArg) == 0x4A0, "SwkbdCalcArg has incorrect size."); + +struct SwkbdChangedStringArg { + u32 text_length{}; + s32 dictionary_start_cursor_position{}; + s32 dictionary_end_cursor_position{}; + s32 cursor_position{}; +}; +static_assert(sizeof(SwkbdChangedStringArg) == 0x10, "SwkbdChangedStringArg has incorrect size."); + +struct SwkbdMovedCursorArg { + u32 text_length{}; + s32 cursor_position{}; +}; +static_assert(sizeof(SwkbdMovedCursorArg) == 0x8, "SwkbdMovedCursorArg has incorrect size."); + +struct SwkbdMovedTabArg { + u32 text_length{}; + s32 cursor_position{}; +}; +static_assert(sizeof(SwkbdMovedTabArg) == 0x8, "SwkbdMovedTabArg has incorrect size."); + +struct SwkbdDecidedEnterArg { + u32 text_length{}; +}; +static_assert(sizeof(SwkbdDecidedEnterArg) == 0x4, "SwkbdDecidedEnterArg has incorrect size."); + +} // namespace Service::AM::Applets diff --git a/src/core/hle/service/am/applets/applet_web_browser.cpp b/src/core/hle/service/am/applets/applet_web_browser.cpp new file mode 100644 index 000000000..f96f0fee3 --- /dev/null +++ b/src/core/hle/service/am/applets/applet_web_browser.cpp @@ -0,0 +1,474 @@ +// Copyright 2020 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "common/assert.h" +#include "common/fs/file.h" +#include "common/fs/fs.h" +#include "common/fs/path_util.h" +#include "common/logging/log.h" +#include "common/string_util.h" +#include "core/core.h" +#include "core/file_sys/content_archive.h" +#include "core/file_sys/mode.h" +#include "core/file_sys/nca_metadata.h" +#include "core/file_sys/patch_manager.h" +#include "core/file_sys/registered_cache.h" +#include "core/file_sys/romfs.h" +#include "core/file_sys/system_archive/system_archive.h" +#include "core/file_sys/vfs_vector.h" +#include "core/frontend/applets/web_browser.h" +#include "core/hle/kernel/k_process.h" +#include "core/hle/result.h" +#include "core/hle/service/am/am.h" +#include "core/hle/service/am/applets/applet_web_browser.h" +#include "core/hle/service/filesystem/filesystem.h" +#include "core/hle/service/ns/pl_u.h" + +namespace Service::AM::Applets { + +namespace { + +template +void ParseRawValue(T& value, const std::vector& data) { + static_assert(std::is_trivially_copyable_v, + "It's undefined behavior to use memcpy with non-trivially copyable objects"); + std::memcpy(&value, data.data(), data.size()); +} + +template +T ParseRawValue(const std::vector& data) { + T value; + ParseRawValue(value, data); + return value; +} + +std::string ParseStringValue(const std::vector& data) { + return Common::StringFromFixedZeroTerminatedBuffer(reinterpret_cast(data.data()), + data.size()); +} + +std::string GetMainURL(const std::string& url) { + const auto index = url.find('?'); + + if (index == std::string::npos) { + return url; + } + + return url.substr(0, index); +} + +WebArgInputTLVMap ReadWebArgs(const std::vector& web_arg, WebArgHeader& web_arg_header) { + std::memcpy(&web_arg_header, web_arg.data(), sizeof(WebArgHeader)); + + if (web_arg.size() == sizeof(WebArgHeader)) { + return {}; + } + + WebArgInputTLVMap input_tlv_map; + + u64 current_offset = sizeof(WebArgHeader); + + for (std::size_t i = 0; i < web_arg_header.total_tlv_entries; ++i) { + if (web_arg.size() < current_offset + sizeof(WebArgInputTLV)) { + return input_tlv_map; + } + + WebArgInputTLV input_tlv; + std::memcpy(&input_tlv, web_arg.data() + current_offset, sizeof(WebArgInputTLV)); + + current_offset += sizeof(WebArgInputTLV); + + if (web_arg.size() < current_offset + input_tlv.arg_data_size) { + return input_tlv_map; + } + + std::vector data(input_tlv.arg_data_size); + std::memcpy(data.data(), web_arg.data() + current_offset, input_tlv.arg_data_size); + + current_offset += input_tlv.arg_data_size; + + input_tlv_map.insert_or_assign(input_tlv.input_tlv_type, std::move(data)); + } + + return input_tlv_map; +} + +FileSys::VirtualFile GetOfflineRomFS(Core::System& system, u64 title_id, + FileSys::ContentRecordType nca_type) { + if (nca_type == FileSys::ContentRecordType::Data) { + const auto nca = + system.GetFileSystemController().GetSystemNANDContents()->GetEntry(title_id, nca_type); + + if (nca == nullptr) { + LOG_ERROR(Service_AM, + "NCA of type={} with title_id={:016X} is not found in the System NAND!", + nca_type, title_id); + return FileSys::SystemArchive::SynthesizeSystemArchive(title_id); + } + + return nca->GetRomFS(); + } else { + const auto nca = system.GetContentProvider().GetEntry(title_id, nca_type); + + if (nca == nullptr) { + LOG_ERROR(Service_AM, + "NCA of type={} with title_id={:016X} is not found in the ContentProvider!", + nca_type, title_id); + return nullptr; + } + + const FileSys::PatchManager pm{title_id, system.GetFileSystemController(), + system.GetContentProvider()}; + + return pm.PatchRomFS(nca->GetRomFS(), nca->GetBaseIVFCOffset(), nca_type); + } +} + +void ExtractSharedFonts(Core::System& system) { + static constexpr std::array DECRYPTED_SHARED_FONTS{ + "FontStandard.ttf", + "FontChineseSimplified.ttf", + "FontExtendedChineseSimplified.ttf", + "FontChineseTraditional.ttf", + "FontKorean.ttf", + "FontNintendoExtended.ttf", + "FontNintendoExtended2.ttf", + }; + + const auto fonts_dir = Common::FS::GetYuzuPath(Common::FS::YuzuPath::CacheDir) / "fonts"; + + for (std::size_t i = 0; i < NS::SHARED_FONTS.size(); ++i) { + const auto font_file_path = fonts_dir / DECRYPTED_SHARED_FONTS[i]; + + if (Common::FS::Exists(font_file_path)) { + continue; + } + + const auto font = NS::SHARED_FONTS[i]; + const auto font_title_id = static_cast(font.first); + + const auto nca = system.GetFileSystemController().GetSystemNANDContents()->GetEntry( + font_title_id, FileSys::ContentRecordType::Data); + + FileSys::VirtualFile romfs; + + if (!nca) { + romfs = FileSys::SystemArchive::SynthesizeSystemArchive(font_title_id); + } else { + romfs = nca->GetRomFS(); + } + + if (!romfs) { + LOG_ERROR(Service_AM, "SharedFont RomFS with title_id={:016X} cannot be extracted!", + font_title_id); + continue; + } + + const auto extracted_romfs = FileSys::ExtractRomFS(romfs); + + if (!extracted_romfs) { + LOG_ERROR(Service_AM, "SharedFont RomFS with title_id={:016X} failed to extract!", + font_title_id); + continue; + } + + const auto font_file = extracted_romfs->GetFile(font.second); + + if (!font_file) { + LOG_ERROR(Service_AM, "SharedFont RomFS with title_id={:016X} has no font file \"{}\"!", + font_title_id, font.second); + continue; + } + + std::vector font_data_u32(font_file->GetSize() / sizeof(u32)); + font_file->ReadBytes(font_data_u32.data(), font_file->GetSize()); + + std::transform(font_data_u32.begin(), font_data_u32.end(), font_data_u32.begin(), + Common::swap32); + + std::vector decrypted_data(font_file->GetSize() - 8); + + NS::DecryptSharedFontToTTF(font_data_u32, decrypted_data); + + FileSys::VirtualFile decrypted_font = std::make_shared( + std::move(decrypted_data), DECRYPTED_SHARED_FONTS[i]); + + const auto temp_dir = system.GetFilesystem()->CreateDirectory( + Common::FS::PathToUTF8String(fonts_dir), FileSys::Mode::ReadWrite); + + const auto out_file = temp_dir->CreateFile(DECRYPTED_SHARED_FONTS[i]); + + FileSys::VfsRawCopy(decrypted_font, out_file); + } +} + +} // namespace + +WebBrowser::WebBrowser(Core::System& system_, LibraryAppletMode applet_mode_, + const Core::Frontend::WebBrowserApplet& frontend_) + : Applet{system_, applet_mode_}, frontend(frontend_), system{system_} {} + +WebBrowser::~WebBrowser() = default; + +void WebBrowser::Initialize() { + Applet::Initialize(); + + LOG_INFO(Service_AM, "Initializing Web Browser Applet."); + + LOG_DEBUG(Service_AM, + "Initializing Applet with common_args: arg_version={}, lib_version={}, " + "play_startup_sound={}, size={}, system_tick={}, theme_color={}", + common_args.arguments_version, common_args.library_version, + common_args.play_startup_sound, common_args.size, common_args.system_tick, + common_args.theme_color); + + web_applet_version = WebAppletVersion{common_args.library_version}; + + const auto web_arg_storage = broker.PopNormalDataToApplet(); + ASSERT(web_arg_storage != nullptr); + + const auto& web_arg = web_arg_storage->GetData(); + ASSERT_OR_EXECUTE(web_arg.size() >= sizeof(WebArgHeader), { return; }); + + web_arg_input_tlv_map = ReadWebArgs(web_arg, web_arg_header); + + LOG_DEBUG(Service_AM, "WebArgHeader: total_tlv_entries={}, shim_kind={}", + web_arg_header.total_tlv_entries, web_arg_header.shim_kind); + + ExtractSharedFonts(system); + + switch (web_arg_header.shim_kind) { + case ShimKind::Shop: + InitializeShop(); + break; + case ShimKind::Login: + InitializeLogin(); + break; + case ShimKind::Offline: + InitializeOffline(); + break; + case ShimKind::Share: + InitializeShare(); + break; + case ShimKind::Web: + InitializeWeb(); + break; + case ShimKind::Wifi: + InitializeWifi(); + break; + case ShimKind::Lobby: + InitializeLobby(); + break; + default: + UNREACHABLE_MSG("Invalid ShimKind={}", web_arg_header.shim_kind); + break; + } +} + +bool WebBrowser::TransactionComplete() const { + return complete; +} + +ResultCode WebBrowser::GetStatus() const { + return status; +} + +void WebBrowser::ExecuteInteractive() { + UNIMPLEMENTED_MSG("WebSession is not implemented"); +} + +void WebBrowser::Execute() { + switch (web_arg_header.shim_kind) { + case ShimKind::Shop: + ExecuteShop(); + break; + case ShimKind::Login: + ExecuteLogin(); + break; + case ShimKind::Offline: + ExecuteOffline(); + break; + case ShimKind::Share: + ExecuteShare(); + break; + case ShimKind::Web: + ExecuteWeb(); + break; + case ShimKind::Wifi: + ExecuteWifi(); + break; + case ShimKind::Lobby: + ExecuteLobby(); + break; + default: + UNREACHABLE_MSG("Invalid ShimKind={}", web_arg_header.shim_kind); + WebBrowserExit(WebExitReason::EndButtonPressed); + break; + } +} + +void WebBrowser::ExtractOfflineRomFS() { + LOG_DEBUG(Service_AM, "Extracting RomFS to {}", + Common::FS::PathToUTF8String(offline_cache_dir)); + + const auto extracted_romfs_dir = + FileSys::ExtractRomFS(offline_romfs, FileSys::RomFSExtractionType::SingleDiscard); + + const auto temp_dir = system.GetFilesystem()->CreateDirectory( + Common::FS::PathToUTF8String(offline_cache_dir), FileSys::Mode::ReadWrite); + + FileSys::VfsRawCopyD(extracted_romfs_dir, temp_dir); +} + +void WebBrowser::WebBrowserExit(WebExitReason exit_reason, std::string last_url) { + if ((web_arg_header.shim_kind == ShimKind::Share && + web_applet_version >= WebAppletVersion::Version196608) || + (web_arg_header.shim_kind == ShimKind::Web && + web_applet_version >= WebAppletVersion::Version524288)) { + // TODO: Push Output TLVs instead of a WebCommonReturnValue + } + + WebCommonReturnValue web_common_return_value; + + web_common_return_value.exit_reason = exit_reason; + std::memcpy(&web_common_return_value.last_url, last_url.data(), last_url.size()); + web_common_return_value.last_url_size = last_url.size(); + + LOG_DEBUG(Service_AM, "WebCommonReturnValue: exit_reason={}, last_url={}, last_url_size={}", + exit_reason, last_url, last_url.size()); + + complete = true; + std::vector out_data(sizeof(WebCommonReturnValue)); + std::memcpy(out_data.data(), &web_common_return_value, out_data.size()); + broker.PushNormalDataFromApplet(std::make_shared(system, std::move(out_data))); + broker.SignalStateChanged(); +} + +bool WebBrowser::InputTLVExistsInMap(WebArgInputTLVType input_tlv_type) const { + return web_arg_input_tlv_map.find(input_tlv_type) != web_arg_input_tlv_map.end(); +} + +std::optional> WebBrowser::GetInputTLVData(WebArgInputTLVType input_tlv_type) { + const auto map_it = web_arg_input_tlv_map.find(input_tlv_type); + + if (map_it == web_arg_input_tlv_map.end()) { + return std::nullopt; + } + + return map_it->second; +} + +void WebBrowser::InitializeShop() {} + +void WebBrowser::InitializeLogin() {} + +void WebBrowser::InitializeOffline() { + const auto document_path = + ParseStringValue(GetInputTLVData(WebArgInputTLVType::DocumentPath).value()); + + const auto document_kind = + ParseRawValue(GetInputTLVData(WebArgInputTLVType::DocumentKind).value()); + + std::string additional_paths; + + switch (document_kind) { + case DocumentKind::OfflineHtmlPage: + default: + title_id = system.CurrentProcess()->GetTitleID(); + nca_type = FileSys::ContentRecordType::HtmlDocument; + additional_paths = "html-document"; + break; + case DocumentKind::ApplicationLegalInformation: + title_id = ParseRawValue(GetInputTLVData(WebArgInputTLVType::ApplicationID).value()); + nca_type = FileSys::ContentRecordType::LegalInformation; + break; + case DocumentKind::SystemDataPage: + title_id = ParseRawValue(GetInputTLVData(WebArgInputTLVType::SystemDataID).value()); + nca_type = FileSys::ContentRecordType::Data; + break; + } + + static constexpr std::array RESOURCE_TYPES{ + "manual", + "legal_information", + "system_data", + }; + + offline_cache_dir = Common::FS::GetYuzuPath(Common::FS::YuzuPath::CacheDir) / + fmt::format("offline_web_applet_{}/{:016X}", + RESOURCE_TYPES[static_cast(document_kind) - 1], title_id); + + offline_document = Common::FS::ConcatPathSafe( + offline_cache_dir, fmt::format("{}/{}", additional_paths, document_path)); +} + +void WebBrowser::InitializeShare() {} + +void WebBrowser::InitializeWeb() { + external_url = ParseStringValue(GetInputTLVData(WebArgInputTLVType::InitialURL).value()); +} + +void WebBrowser::InitializeWifi() {} + +void WebBrowser::InitializeLobby() {} + +void WebBrowser::ExecuteShop() { + LOG_WARNING(Service_AM, "(STUBBED) called, Shop Applet is not implemented"); + WebBrowserExit(WebExitReason::EndButtonPressed); +} + +void WebBrowser::ExecuteLogin() { + LOG_WARNING(Service_AM, "(STUBBED) called, Login Applet is not implemented"); + WebBrowserExit(WebExitReason::EndButtonPressed); +} + +void WebBrowser::ExecuteOffline() { + const auto main_url = GetMainURL(Common::FS::PathToUTF8String(offline_document)); + + if (!Common::FS::Exists(main_url)) { + offline_romfs = GetOfflineRomFS(system, title_id, nca_type); + + if (offline_romfs == nullptr) { + LOG_ERROR(Service_AM, + "RomFS with title_id={:016X} and nca_type={} cannot be extracted!", title_id, + nca_type); + WebBrowserExit(WebExitReason::WindowClosed); + return; + } + } + + LOG_INFO(Service_AM, "Opening offline document at {}", + Common::FS::PathToUTF8String(offline_document)); + + frontend.OpenLocalWebPage( + Common::FS::PathToUTF8String(offline_document), [this] { ExtractOfflineRomFS(); }, + [this](WebExitReason exit_reason, std::string last_url) { + WebBrowserExit(exit_reason, last_url); + }); +} + +void WebBrowser::ExecuteShare() { + LOG_WARNING(Service_AM, "(STUBBED) called, Share Applet is not implemented"); + WebBrowserExit(WebExitReason::EndButtonPressed); +} + +void WebBrowser::ExecuteWeb() { + LOG_INFO(Service_AM, "Opening external URL at {}", external_url); + + frontend.OpenExternalWebPage(external_url, + [this](WebExitReason exit_reason, std::string last_url) { + WebBrowserExit(exit_reason, last_url); + }); +} + +void WebBrowser::ExecuteWifi() { + LOG_WARNING(Service_AM, "(STUBBED) called, Wifi Applet is not implemented"); + WebBrowserExit(WebExitReason::EndButtonPressed); +} + +void WebBrowser::ExecuteLobby() { + LOG_WARNING(Service_AM, "(STUBBED) called, Lobby Applet is not implemented"); + WebBrowserExit(WebExitReason::EndButtonPressed); +} +} // namespace Service::AM::Applets diff --git a/src/core/hle/service/am/applets/applet_web_browser.h b/src/core/hle/service/am/applets/applet_web_browser.h new file mode 100644 index 000000000..4f9e81b79 --- /dev/null +++ b/src/core/hle/service/am/applets/applet_web_browser.h @@ -0,0 +1,88 @@ +// Copyright 2020 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include +#include + +#include "common/common_funcs.h" +#include "common/common_types.h" +#include "core/file_sys/vfs_types.h" +#include "core/hle/result.h" +#include "core/hle/service/am/applets/applet_web_browser_types.h" +#include "core/hle/service/am/applets/applets.h" + +namespace Core { +class System; +} + +namespace FileSys { +enum class ContentRecordType : u8; +} + +namespace Service::AM::Applets { + +class WebBrowser final : public Applet { +public: + WebBrowser(Core::System& system_, LibraryAppletMode applet_mode_, + const Core::Frontend::WebBrowserApplet& frontend_); + + ~WebBrowser() override; + + void Initialize() override; + + bool TransactionComplete() const override; + ResultCode GetStatus() const override; + void ExecuteInteractive() override; + void Execute() override; + + void ExtractOfflineRomFS(); + + void WebBrowserExit(WebExitReason exit_reason, std::string last_url = ""); + +private: + bool InputTLVExistsInMap(WebArgInputTLVType input_tlv_type) const; + + std::optional> GetInputTLVData(WebArgInputTLVType input_tlv_type); + + // Initializers for the various types of browser applets + void InitializeShop(); + void InitializeLogin(); + void InitializeOffline(); + void InitializeShare(); + void InitializeWeb(); + void InitializeWifi(); + void InitializeLobby(); + + // Executors for the various types of browser applets + void ExecuteShop(); + void ExecuteLogin(); + void ExecuteOffline(); + void ExecuteShare(); + void ExecuteWeb(); + void ExecuteWifi(); + void ExecuteLobby(); + + const Core::Frontend::WebBrowserApplet& frontend; + + bool complete{false}; + ResultCode status{ResultSuccess}; + + WebAppletVersion web_applet_version{}; + WebArgHeader web_arg_header{}; + WebArgInputTLVMap web_arg_input_tlv_map; + + u64 title_id{}; + FileSys::ContentRecordType nca_type{}; + std::filesystem::path offline_cache_dir; + std::filesystem::path offline_document; + FileSys::VirtualFile offline_romfs; + + std::string external_url; + + Core::System& system; +}; + +} // namespace Service::AM::Applets diff --git a/src/core/hle/service/am/applets/applet_web_browser_types.h b/src/core/hle/service/am/applets/applet_web_browser_types.h new file mode 100644 index 000000000..419c2bf79 --- /dev/null +++ b/src/core/hle/service/am/applets/applet_web_browser_types.h @@ -0,0 +1,178 @@ +// Copyright 2020 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include +#include +#include + +#include "common/common_funcs.h" +#include "common/common_types.h" +#include "common/swap.h" + +namespace Service::AM::Applets { + +enum class WebAppletVersion : u32_le { + Version0 = 0x0, // Only used by WifiWebAuthApplet + Version131072 = 0x20000, // 1.0.0 - 2.3.0 + Version196608 = 0x30000, // 3.0.0 - 4.1.0 + Version327680 = 0x50000, // 5.0.0 - 5.1.0 + Version393216 = 0x60000, // 6.0.0 - 7.0.1 + Version524288 = 0x80000, // 8.0.0+ +}; + +enum class ShimKind : u32 { + Shop = 1, + Login = 2, + Offline = 3, + Share = 4, + Web = 5, + Wifi = 6, + Lobby = 7, +}; + +enum class WebExitReason : u32 { + EndButtonPressed = 0, + BackButtonPressed = 1, + ExitRequested = 2, + CallbackURL = 3, + WindowClosed = 4, + ErrorDialog = 7, +}; + +enum class WebArgInputTLVType : u16 { + InitialURL = 0x1, + CallbackURL = 0x3, + CallbackableURL = 0x4, + ApplicationID = 0x5, + DocumentPath = 0x6, + DocumentKind = 0x7, + SystemDataID = 0x8, + ShareStartPage = 0x9, + Whitelist = 0xA, + News = 0xB, + UserID = 0xE, + AlbumEntry0 = 0xF, + ScreenShotEnabled = 0x10, + EcClientCertEnabled = 0x11, + PlayReportEnabled = 0x13, + BootDisplayKind = 0x17, + BackgroundKind = 0x18, + FooterEnabled = 0x19, + PointerEnabled = 0x1A, + LeftStickMode = 0x1B, + KeyRepeatFrame1 = 0x1C, + KeyRepeatFrame2 = 0x1D, + BootAsMediaPlayerInverted = 0x1E, + DisplayURLKind = 0x1F, + BootAsMediaPlayer = 0x21, + ShopJumpEnabled = 0x22, + MediaAutoPlayEnabled = 0x23, + LobbyParameter = 0x24, + ApplicationAlbumEntry = 0x26, + JsExtensionEnabled = 0x27, + AdditionalCommentText = 0x28, + TouchEnabledOnContents = 0x29, + UserAgentAdditionalString = 0x2A, + AdditionalMediaData0 = 0x2B, + MediaPlayerAutoCloseEnabled = 0x2C, + PageCacheEnabled = 0x2D, + WebAudioEnabled = 0x2E, + YouTubeVideoWhitelist = 0x31, + FooterFixedKind = 0x32, + PageFadeEnabled = 0x33, + MediaCreatorApplicationRatingAge = 0x34, + BootLoadingIconEnabled = 0x35, + PageScrollIndicatorEnabled = 0x36, + MediaPlayerSpeedControlEnabled = 0x37, + AlbumEntry1 = 0x38, + AlbumEntry2 = 0x39, + AlbumEntry3 = 0x3A, + AdditionalMediaData1 = 0x3B, + AdditionalMediaData2 = 0x3C, + AdditionalMediaData3 = 0x3D, + BootFooterButton = 0x3E, + OverrideWebAudioVolume = 0x3F, + OverrideMediaAudioVolume = 0x40, + BootMode = 0x41, + WebSessionEnabled = 0x42, + MediaPlayerOfflineEnabled = 0x43, +}; + +enum class WebArgOutputTLVType : u16 { + ShareExitReason = 0x1, + LastURL = 0x2, + LastURLSize = 0x3, + SharePostResult = 0x4, + PostServiceName = 0x5, + PostServiceNameSize = 0x6, + PostID = 0x7, + PostIDSize = 0x8, + MediaPlayerAutoClosedByCompletion = 0x9, +}; + +enum class DocumentKind : u32 { + OfflineHtmlPage = 1, + ApplicationLegalInformation = 2, + SystemDataPage = 3, +}; + +enum class ShareStartPage : u32 { + Default, + Settings, +}; + +enum class BootDisplayKind : u32 { + Default, + White, + Black, +}; + +enum class BackgroundKind : u32 { + Default, +}; + +enum class LeftStickMode : u32 { + Pointer, + Cursor, +}; + +enum class WebSessionBootMode : u32 { + AllForeground, + AllForegroundInitiallyHidden, +}; + +struct WebArgHeader { + u16 total_tlv_entries{}; + INSERT_PADDING_BYTES(2); + ShimKind shim_kind{}; +}; +static_assert(sizeof(WebArgHeader) == 0x8, "WebArgHeader has incorrect size."); + +struct WebArgInputTLV { + WebArgInputTLVType input_tlv_type{}; + u16 arg_data_size{}; + INSERT_PADDING_WORDS(1); +}; +static_assert(sizeof(WebArgInputTLV) == 0x8, "WebArgInputTLV has incorrect size."); + +struct WebArgOutputTLV { + WebArgOutputTLVType output_tlv_type{}; + u16 arg_data_size{}; + INSERT_PADDING_WORDS(1); +}; +static_assert(sizeof(WebArgOutputTLV) == 0x8, "WebArgOutputTLV has incorrect size."); + +struct WebCommonReturnValue { + WebExitReason exit_reason{}; + INSERT_PADDING_WORDS(1); + std::array last_url{}; + u64 last_url_size{}; +}; +static_assert(sizeof(WebCommonReturnValue) == 0x1010, "WebCommonReturnValue has incorrect size."); + +using WebArgInputTLVMap = std::unordered_map>; + +} // namespace Service::AM::Applets diff --git a/src/core/hle/service/am/applets/applets.cpp b/src/core/hle/service/am/applets/applets.cpp index ae995df6b..2b7685d42 100644 --- a/src/core/hle/service/am/applets/applets.cpp +++ b/src/core/hle/service/am/applets/applets.cpp @@ -17,13 +17,13 @@ #include "core/hle/service/am/am.h" #include "core/hle/service/am/applet_ae.h" #include "core/hle/service/am/applet_oe.h" +#include "core/hle/service/am/applets/applet_controller.h" +#include "core/hle/service/am/applets/applet_error.h" +#include "core/hle/service/am/applets/applet_general_backend.h" +#include "core/hle/service/am/applets/applet_profile_select.h" +#include "core/hle/service/am/applets/applet_software_keyboard.h" +#include "core/hle/service/am/applets/applet_web_browser.h" #include "core/hle/service/am/applets/applets.h" -#include "core/hle/service/am/applets/controller.h" -#include "core/hle/service/am/applets/error.h" -#include "core/hle/service/am/applets/general_backend.h" -#include "core/hle/service/am/applets/profile_select.h" -#include "core/hle/service/am/applets/software_keyboard.h" -#include "core/hle/service/am/applets/web_browser.h" #include "core/hle/service/sm/sm.h" namespace Service::AM::Applets { diff --git a/src/core/hle/service/am/applets/controller.cpp b/src/core/hle/service/am/applets/controller.cpp deleted file mode 100644 index 218c8d1e4..000000000 --- a/src/core/hle/service/am/applets/controller.cpp +++ /dev/null @@ -1,253 +0,0 @@ -// Copyright 2020 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#include -#include - -#include "common/assert.h" -#include "common/logging/log.h" -#include "common/string_util.h" -#include "core/core.h" -#include "core/frontend/applets/controller.h" -#include "core/hle/result.h" -#include "core/hle/service/am/am.h" -#include "core/hle/service/am/applets/controller.h" -#include "core/hle/service/hid/controllers/npad.h" - -namespace Service::AM::Applets { - -// This error code (0x183ACA) is thrown when the applet fails to initialize. -[[maybe_unused]] constexpr ResultCode ERR_CONTROLLER_APPLET_3101{ErrorModule::HID, 3101}; -// This error code (0x183CCA) is thrown when the u32 result in ControllerSupportResultInfo is 2. -[[maybe_unused]] constexpr ResultCode ERR_CONTROLLER_APPLET_3102{ErrorModule::HID, 3102}; - -static Core::Frontend::ControllerParameters ConvertToFrontendParameters( - ControllerSupportArgPrivate private_arg, ControllerSupportArgHeader header, bool enable_text, - std::vector identification_colors, std::vector text) { - HID::Controller_NPad::NpadStyleSet npad_style_set; - npad_style_set.raw = private_arg.style_set; - - return { - .min_players = std::max(s8{1}, header.player_count_min), - .max_players = header.player_count_max, - .keep_controllers_connected = header.enable_take_over_connection, - .enable_single_mode = header.enable_single_mode, - .enable_border_color = header.enable_identification_color, - .border_colors = std::move(identification_colors), - .enable_explain_text = enable_text, - .explain_text = std::move(text), - .allow_pro_controller = npad_style_set.fullkey == 1, - .allow_handheld = npad_style_set.handheld == 1, - .allow_dual_joycons = npad_style_set.joycon_dual == 1, - .allow_left_joycon = npad_style_set.joycon_left == 1, - .allow_right_joycon = npad_style_set.joycon_right == 1, - }; -} - -Controller::Controller(Core::System& system_, LibraryAppletMode applet_mode_, - const Core::Frontend::ControllerApplet& frontend_) - : Applet{system_, applet_mode_}, frontend{frontend_}, system{system_} {} - -Controller::~Controller() = default; - -void Controller::Initialize() { - Applet::Initialize(); - - LOG_INFO(Service_HID, "Initializing Controller Applet."); - - LOG_DEBUG(Service_HID, - "Initializing Applet with common_args: arg_version={}, lib_version={}, " - "play_startup_sound={}, size={}, system_tick={}, theme_color={}", - common_args.arguments_version, common_args.library_version, - common_args.play_startup_sound, common_args.size, common_args.system_tick, - common_args.theme_color); - - controller_applet_version = ControllerAppletVersion{common_args.library_version}; - - const auto private_arg_storage = broker.PopNormalDataToApplet(); - ASSERT(private_arg_storage != nullptr); - - const auto& private_arg = private_arg_storage->GetData(); - ASSERT(private_arg.size() == sizeof(ControllerSupportArgPrivate)); - - std::memcpy(&controller_private_arg, private_arg.data(), private_arg.size()); - ASSERT_MSG(controller_private_arg.arg_private_size == sizeof(ControllerSupportArgPrivate), - "Unknown ControllerSupportArgPrivate revision={} with size={}", - controller_applet_version, controller_private_arg.arg_private_size); - - // Some games such as Cave Story+ set invalid values for the ControllerSupportMode. - // Defer to arg_size to set the ControllerSupportMode. - if (controller_private_arg.mode >= ControllerSupportMode::MaxControllerSupportMode) { - switch (controller_private_arg.arg_size) { - case sizeof(ControllerSupportArgOld): - case sizeof(ControllerSupportArgNew): - controller_private_arg.mode = ControllerSupportMode::ShowControllerSupport; - break; - case sizeof(ControllerUpdateFirmwareArg): - controller_private_arg.mode = ControllerSupportMode::ShowControllerFirmwareUpdate; - break; - default: - UNIMPLEMENTED_MSG("Unknown ControllerPrivateArg mode={} with arg_size={}", - controller_private_arg.mode, controller_private_arg.arg_size); - controller_private_arg.mode = ControllerSupportMode::ShowControllerSupport; - break; - } - } - - // Some games such as Cave Story+ set invalid values for the ControllerSupportCaller. - // This is always 0 (Application) except with ShowControllerFirmwareUpdateForSystem. - if (controller_private_arg.caller >= ControllerSupportCaller::MaxControllerSupportCaller) { - if (controller_private_arg.flag_1 && - controller_private_arg.mode == ControllerSupportMode::ShowControllerFirmwareUpdate) { - controller_private_arg.caller = ControllerSupportCaller::System; - } else { - controller_private_arg.caller = ControllerSupportCaller::Application; - } - } - - switch (controller_private_arg.mode) { - case ControllerSupportMode::ShowControllerSupport: - case ControllerSupportMode::ShowControllerStrapGuide: { - const auto user_arg_storage = broker.PopNormalDataToApplet(); - ASSERT(user_arg_storage != nullptr); - - const auto& user_arg = user_arg_storage->GetData(); - switch (controller_applet_version) { - case ControllerAppletVersion::Version3: - case ControllerAppletVersion::Version4: - case ControllerAppletVersion::Version5: - ASSERT(user_arg.size() == sizeof(ControllerSupportArgOld)); - std::memcpy(&controller_user_arg_old, user_arg.data(), user_arg.size()); - break; - case ControllerAppletVersion::Version7: - ASSERT(user_arg.size() == sizeof(ControllerSupportArgNew)); - std::memcpy(&controller_user_arg_new, user_arg.data(), user_arg.size()); - break; - default: - UNIMPLEMENTED_MSG("Unknown ControllerSupportArg revision={} with size={}", - controller_applet_version, controller_private_arg.arg_size); - ASSERT(user_arg.size() >= sizeof(ControllerSupportArgNew)); - std::memcpy(&controller_user_arg_new, user_arg.data(), sizeof(ControllerSupportArgNew)); - break; - } - break; - } - case ControllerSupportMode::ShowControllerFirmwareUpdate: { - const auto update_arg_storage = broker.PopNormalDataToApplet(); - ASSERT(update_arg_storage != nullptr); - - const auto& update_arg = update_arg_storage->GetData(); - ASSERT(update_arg.size() == sizeof(ControllerUpdateFirmwareArg)); - - std::memcpy(&controller_update_arg, update_arg.data(), update_arg.size()); - break; - } - default: { - UNIMPLEMENTED_MSG("Unimplemented ControllerSupportMode={}", controller_private_arg.mode); - break; - } - } -} - -bool Controller::TransactionComplete() const { - return complete; -} - -ResultCode Controller::GetStatus() const { - return status; -} - -void Controller::ExecuteInteractive() { - UNREACHABLE_MSG("Attempted to call interactive execution on non-interactive applet."); -} - -void Controller::Execute() { - switch (controller_private_arg.mode) { - case ControllerSupportMode::ShowControllerSupport: { - const auto parameters = [this] { - switch (controller_applet_version) { - case ControllerAppletVersion::Version3: - case ControllerAppletVersion::Version4: - case ControllerAppletVersion::Version5: - return ConvertToFrontendParameters( - controller_private_arg, controller_user_arg_old.header, - controller_user_arg_old.enable_explain_text, - std::vector( - controller_user_arg_old.identification_colors.begin(), - controller_user_arg_old.identification_colors.end()), - std::vector(controller_user_arg_old.explain_text.begin(), - controller_user_arg_old.explain_text.end())); - case ControllerAppletVersion::Version7: - default: - return ConvertToFrontendParameters( - controller_private_arg, controller_user_arg_new.header, - controller_user_arg_new.enable_explain_text, - std::vector( - controller_user_arg_new.identification_colors.begin(), - controller_user_arg_new.identification_colors.end()), - std::vector(controller_user_arg_new.explain_text.begin(), - controller_user_arg_new.explain_text.end())); - } - }(); - - is_single_mode = parameters.enable_single_mode; - - LOG_DEBUG(Service_HID, - "Controller Parameters: min_players={}, max_players={}, " - "keep_controllers_connected={}, enable_single_mode={}, enable_border_color={}, " - "enable_explain_text={}, allow_pro_controller={}, allow_handheld={}, " - "allow_dual_joycons={}, allow_left_joycon={}, allow_right_joycon={}", - parameters.min_players, parameters.max_players, - parameters.keep_controllers_connected, parameters.enable_single_mode, - parameters.enable_border_color, parameters.enable_explain_text, - parameters.allow_pro_controller, parameters.allow_handheld, - parameters.allow_dual_joycons, parameters.allow_left_joycon, - parameters.allow_right_joycon); - - frontend.ReconfigureControllers([this] { ConfigurationComplete(); }, parameters); - break; - } - case ControllerSupportMode::ShowControllerStrapGuide: - case ControllerSupportMode::ShowControllerFirmwareUpdate: - UNIMPLEMENTED_MSG("ControllerSupportMode={} is not implemented", - controller_private_arg.mode); - ConfigurationComplete(); - break; - default: { - ConfigurationComplete(); - break; - } - } -} - -void Controller::ConfigurationComplete() { - ControllerSupportResultInfo result_info{}; - - const auto& players = Settings::values.players.GetValue(); - - // If enable_single_mode is enabled, player_count is 1 regardless of any other parameters. - // Otherwise, only count connected players from P1-P8. - result_info.player_count = - is_single_mode - ? 1 - : static_cast(std::count_if(players.begin(), players.end() - 2, - [](const auto& player) { return player.connected; })); - - result_info.selected_id = HID::Controller_NPad::IndexToNPad(std::distance( - players.begin(), std::find_if(players.begin(), players.end(), - [](const auto& player) { return player.connected; }))); - - result_info.result = 0; - - LOG_DEBUG(Service_HID, "Result Info: player_count={}, selected_id={}, result={}", - result_info.player_count, result_info.selected_id, result_info.result); - - complete = true; - out_data = std::vector(sizeof(ControllerSupportResultInfo)); - std::memcpy(out_data.data(), &result_info, out_data.size()); - broker.PushNormalDataFromApplet(std::make_shared(system, std::move(out_data))); - broker.SignalStateChanged(); -} - -} // namespace Service::AM::Applets diff --git a/src/core/hle/service/am/applets/controller.h b/src/core/hle/service/am/applets/controller.h deleted file mode 100644 index 20617e91f..000000000 --- a/src/core/hle/service/am/applets/controller.h +++ /dev/null @@ -1,137 +0,0 @@ -// Copyright 2020 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#pragma once - -#include -#include - -#include "common/common_funcs.h" -#include "common/common_types.h" -#include "core/hle/result.h" -#include "core/hle/service/am/applets/applets.h" - -namespace Core { -class System; -} - -namespace Service::AM::Applets { - -using IdentificationColor = std::array; -using ExplainText = std::array; - -enum class ControllerAppletVersion : u32_le { - Version3 = 0x3, // 1.0.0 - 2.3.0 - Version4 = 0x4, // 3.0.0 - 5.1.0 - Version5 = 0x5, // 6.0.0 - 7.0.1 - Version7 = 0x7, // 8.0.0+ -}; - -enum class ControllerSupportMode : u8 { - ShowControllerSupport, - ShowControllerStrapGuide, - ShowControllerFirmwareUpdate, - - MaxControllerSupportMode, -}; - -enum class ControllerSupportCaller : u8 { - Application, - System, - - MaxControllerSupportCaller, -}; - -struct ControllerSupportArgPrivate { - u32 arg_private_size{}; - u32 arg_size{}; - bool flag_0{}; - bool flag_1{}; - ControllerSupportMode mode{}; - ControllerSupportCaller caller{}; - u32 style_set{}; - u32 joy_hold_type{}; -}; -static_assert(sizeof(ControllerSupportArgPrivate) == 0x14, - "ControllerSupportArgPrivate has incorrect size."); - -struct ControllerSupportArgHeader { - s8 player_count_min{}; - s8 player_count_max{}; - bool enable_take_over_connection{}; - bool enable_left_justify{}; - bool enable_permit_joy_dual{}; - bool enable_single_mode{}; - bool enable_identification_color{}; -}; -static_assert(sizeof(ControllerSupportArgHeader) == 0x7, - "ControllerSupportArgHeader has incorrect size."); - -// LibraryAppletVersion 0x3, 0x4, 0x5 -struct ControllerSupportArgOld { - ControllerSupportArgHeader header{}; - std::array identification_colors{}; - bool enable_explain_text{}; - std::array explain_text{}; -}; -static_assert(sizeof(ControllerSupportArgOld) == 0x21C, - "ControllerSupportArgOld has incorrect size."); - -// LibraryAppletVersion 0x7 -struct ControllerSupportArgNew { - ControllerSupportArgHeader header{}; - std::array identification_colors{}; - bool enable_explain_text{}; - std::array explain_text{}; -}; -static_assert(sizeof(ControllerSupportArgNew) == 0x430, - "ControllerSupportArgNew has incorrect size."); - -struct ControllerUpdateFirmwareArg { - bool enable_force_update{}; - INSERT_PADDING_BYTES(3); -}; -static_assert(sizeof(ControllerUpdateFirmwareArg) == 0x4, - "ControllerUpdateFirmwareArg has incorrect size."); - -struct ControllerSupportResultInfo { - s8 player_count{}; - INSERT_PADDING_BYTES(3); - u32 selected_id{}; - u32 result{}; -}; -static_assert(sizeof(ControllerSupportResultInfo) == 0xC, - "ControllerSupportResultInfo has incorrect size."); - -class Controller final : public Applet { -public: - explicit Controller(Core::System& system_, LibraryAppletMode applet_mode_, - const Core::Frontend::ControllerApplet& frontend_); - ~Controller() override; - - void Initialize() override; - - bool TransactionComplete() const override; - ResultCode GetStatus() const override; - void ExecuteInteractive() override; - void Execute() override; - - void ConfigurationComplete(); - -private: - const Core::Frontend::ControllerApplet& frontend; - Core::System& system; - - ControllerAppletVersion controller_applet_version; - ControllerSupportArgPrivate controller_private_arg; - ControllerSupportArgOld controller_user_arg_old; - ControllerSupportArgNew controller_user_arg_new; - ControllerUpdateFirmwareArg controller_update_arg; - bool complete{false}; - ResultCode status{ResultSuccess}; - bool is_single_mode{false}; - std::vector out_data; -}; - -} // namespace Service::AM::Applets diff --git a/src/core/hle/service/am/applets/error.cpp b/src/core/hle/service/am/applets/error.cpp deleted file mode 100644 index c724e5d5b..000000000 --- a/src/core/hle/service/am/applets/error.cpp +++ /dev/null @@ -1,194 +0,0 @@ -// Copyright 2019 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#include -#include -#include "common/assert.h" -#include "common/logging/log.h" -#include "common/string_util.h" -#include "core/core.h" -#include "core/frontend/applets/error.h" -#include "core/hle/kernel/k_process.h" -#include "core/hle/service/am/am.h" -#include "core/hle/service/am/applets/error.h" -#include "core/reporter.h" - -namespace Service::AM::Applets { - -#pragma pack(push, 4) -struct ShowError { - u8 mode; - bool jump; - INSERT_PADDING_BYTES_NOINIT(4); - bool use_64bit_error_code; - INSERT_PADDING_BYTES_NOINIT(1); - u64 error_code_64; - u32 error_code_32; -}; -static_assert(sizeof(ShowError) == 0x14, "ShowError has incorrect size."); -#pragma pack(pop) - -struct ShowErrorRecord { - u8 mode; - bool jump; - INSERT_PADDING_BYTES_NOINIT(6); - u64 error_code_64; - u64 posix_time; -}; -static_assert(sizeof(ShowErrorRecord) == 0x18, "ShowErrorRecord has incorrect size."); - -struct SystemErrorArg { - u8 mode; - bool jump; - INSERT_PADDING_BYTES_NOINIT(6); - u64 error_code_64; - std::array language_code; - std::array main_text; - std::array detail_text; -}; -static_assert(sizeof(SystemErrorArg) == 0x1018, "SystemErrorArg has incorrect size."); - -struct ApplicationErrorArg { - u8 mode; - bool jump; - INSERT_PADDING_BYTES_NOINIT(6); - u32 error_code; - std::array language_code; - std::array main_text; - std::array detail_text; -}; -static_assert(sizeof(ApplicationErrorArg) == 0x1014, "ApplicationErrorArg has incorrect size."); - -union Error::ErrorArguments { - ShowError error; - ShowErrorRecord error_record; - SystemErrorArg system_error; - ApplicationErrorArg application_error; - std::array raw{}; -}; - -namespace { -template -void CopyArgumentData(const std::vector& data, T& variable) { - ASSERT(data.size() >= sizeof(T)); - std::memcpy(&variable, data.data(), sizeof(T)); -} - -ResultCode Decode64BitError(u64 error) { - const auto description = (error >> 32) & 0x1FFF; - auto module = error & 0x3FF; - if (module >= 2000) - module -= 2000; - module &= 0x1FF; - return {static_cast(module), static_cast(description)}; -} - -} // Anonymous namespace - -Error::Error(Core::System& system_, LibraryAppletMode applet_mode_, - const Core::Frontend::ErrorApplet& frontend_) - : Applet{system_, applet_mode_}, frontend{frontend_}, system{system_} {} - -Error::~Error() = default; - -void Error::Initialize() { - Applet::Initialize(); - args = std::make_unique(); - complete = false; - - const auto storage = broker.PopNormalDataToApplet(); - ASSERT(storage != nullptr); - const auto data = storage->GetData(); - - ASSERT(!data.empty()); - std::memcpy(&mode, data.data(), sizeof(ErrorAppletMode)); - - switch (mode) { - case ErrorAppletMode::ShowError: - CopyArgumentData(data, args->error); - if (args->error.use_64bit_error_code) { - error_code = Decode64BitError(args->error.error_code_64); - } else { - error_code = ResultCode(args->error.error_code_32); - } - break; - case ErrorAppletMode::ShowSystemError: - CopyArgumentData(data, args->system_error); - error_code = ResultCode(Decode64BitError(args->system_error.error_code_64)); - break; - case ErrorAppletMode::ShowApplicationError: - CopyArgumentData(data, args->application_error); - error_code = ResultCode(args->application_error.error_code); - break; - case ErrorAppletMode::ShowErrorRecord: - CopyArgumentData(data, args->error_record); - error_code = Decode64BitError(args->error_record.error_code_64); - break; - default: - UNIMPLEMENTED_MSG("Unimplemented LibAppletError mode={:02X}!", mode); - } -} - -bool Error::TransactionComplete() const { - return complete; -} - -ResultCode Error::GetStatus() const { - return ResultSuccess; -} - -void Error::ExecuteInteractive() { - UNREACHABLE_MSG("Unexpected interactive applet data!"); -} - -void Error::Execute() { - if (complete) { - return; - } - - const auto callback = [this] { DisplayCompleted(); }; - const auto title_id = system.CurrentProcess()->GetTitleID(); - const auto& reporter{system.GetReporter()}; - - switch (mode) { - case ErrorAppletMode::ShowError: - reporter.SaveErrorReport(title_id, error_code); - frontend.ShowError(error_code, callback); - break; - case ErrorAppletMode::ShowSystemError: - case ErrorAppletMode::ShowApplicationError: { - const auto is_system = mode == ErrorAppletMode::ShowSystemError; - const auto& main_text = - is_system ? args->system_error.main_text : args->application_error.main_text; - const auto& detail_text = - is_system ? args->system_error.detail_text : args->application_error.detail_text; - - const auto main_text_string = - Common::StringFromFixedZeroTerminatedBuffer(main_text.data(), main_text.size()); - const auto detail_text_string = - Common::StringFromFixedZeroTerminatedBuffer(detail_text.data(), detail_text.size()); - - reporter.SaveErrorReport(title_id, error_code, main_text_string, detail_text_string); - frontend.ShowCustomErrorText(error_code, main_text_string, detail_text_string, callback); - break; - } - case ErrorAppletMode::ShowErrorRecord: - reporter.SaveErrorReport(title_id, error_code, - fmt::format("{:016X}", args->error_record.posix_time)); - frontend.ShowErrorWithTimestamp( - error_code, std::chrono::seconds{args->error_record.posix_time}, callback); - break; - default: - UNIMPLEMENTED_MSG("Unimplemented LibAppletError mode={:02X}!", mode); - DisplayCompleted(); - } -} - -void Error::DisplayCompleted() { - complete = true; - broker.PushNormalDataFromApplet(std::make_shared(system, std::vector{})); - broker.SignalStateChanged(); -} - -} // namespace Service::AM::Applets diff --git a/src/core/hle/service/am/applets/error.h b/src/core/hle/service/am/applets/error.h deleted file mode 100644 index 8aa9046a5..000000000 --- a/src/core/hle/service/am/applets/error.h +++ /dev/null @@ -1,53 +0,0 @@ -// Copyright 2019 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#pragma once - -#include "core/hle/result.h" -#include "core/hle/service/am/applets/applets.h" - -namespace Core { -class System; -} - -namespace Service::AM::Applets { - -enum class ErrorAppletMode : u8 { - ShowError = 0, - ShowSystemError = 1, - ShowApplicationError = 2, - ShowEula = 3, - ShowErrorPctl = 4, - ShowErrorRecord = 5, - ShowUpdateEula = 8, -}; - -class Error final : public Applet { -public: - explicit Error(Core::System& system_, LibraryAppletMode applet_mode_, - const Core::Frontend::ErrorApplet& frontend_); - ~Error() override; - - void Initialize() override; - - bool TransactionComplete() const override; - ResultCode GetStatus() const override; - void ExecuteInteractive() override; - void Execute() override; - - void DisplayCompleted(); - -private: - union ErrorArguments; - - const Core::Frontend::ErrorApplet& frontend; - ResultCode error_code = ResultSuccess; - ErrorAppletMode mode = ErrorAppletMode::ShowError; - std::unique_ptr args; - - bool complete = false; - Core::System& system; -}; - -} // namespace Service::AM::Applets diff --git a/src/core/hle/service/am/applets/general_backend.cpp b/src/core/hle/service/am/applets/general_backend.cpp deleted file mode 100644 index 9fcb9f95b..000000000 --- a/src/core/hle/service/am/applets/general_backend.cpp +++ /dev/null @@ -1,255 +0,0 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#include - -#include "common/assert.h" -#include "common/hex_util.h" -#include "common/logging/log.h" -#include "core/core.h" -#include "core/frontend/applets/general_frontend.h" -#include "core/hle/kernel/k_process.h" -#include "core/hle/result.h" -#include "core/hle/service/am/am.h" -#include "core/hle/service/am/applets/general_backend.h" -#include "core/reporter.h" - -namespace Service::AM::Applets { - -constexpr ResultCode ERROR_INVALID_PIN{ErrorModule::PCTL, 221}; - -static void LogCurrentStorage(AppletDataBroker& broker, std::string_view prefix) { - std::shared_ptr storage = broker.PopNormalDataToApplet(); - for (; storage != nullptr; storage = broker.PopNormalDataToApplet()) { - const auto data = storage->GetData(); - LOG_INFO(Service_AM, - "called (STUBBED), during {} received normal data with size={:08X}, data={}", - prefix, data.size(), Common::HexToString(data)); - } - - storage = broker.PopInteractiveDataToApplet(); - for (; storage != nullptr; storage = broker.PopInteractiveDataToApplet()) { - const auto data = storage->GetData(); - LOG_INFO(Service_AM, - "called (STUBBED), during {} received interactive data with size={:08X}, data={}", - prefix, data.size(), Common::HexToString(data)); - } -} - -Auth::Auth(Core::System& system_, LibraryAppletMode applet_mode_, - Core::Frontend::ParentalControlsApplet& frontend_) - : Applet{system_, applet_mode_}, frontend{frontend_}, system{system_} {} - -Auth::~Auth() = default; - -void Auth::Initialize() { - Applet::Initialize(); - complete = false; - - const auto storage = broker.PopNormalDataToApplet(); - ASSERT(storage != nullptr); - const auto data = storage->GetData(); - ASSERT(data.size() >= 0xC); - - struct Arg { - INSERT_PADDING_BYTES(4); - AuthAppletType type; - u8 arg0; - u8 arg1; - u8 arg2; - INSERT_PADDING_BYTES(1); - }; - static_assert(sizeof(Arg) == 0xC, "Arg (AuthApplet) has incorrect size."); - - Arg arg{}; - std::memcpy(&arg, data.data(), sizeof(Arg)); - - type = arg.type; - arg0 = arg.arg0; - arg1 = arg.arg1; - arg2 = arg.arg2; -} - -bool Auth::TransactionComplete() const { - return complete; -} - -ResultCode Auth::GetStatus() const { - return successful ? ResultSuccess : ERROR_INVALID_PIN; -} - -void Auth::ExecuteInteractive() { - UNREACHABLE_MSG("Unexpected interactive applet data."); -} - -void Auth::Execute() { - if (complete) { - return; - } - - const auto unimplemented_log = [this] { - UNIMPLEMENTED_MSG("Unimplemented Auth applet type for type={:08X}, arg0={:02X}, " - "arg1={:02X}, arg2={:02X}", - type, arg0, arg1, arg2); - }; - - switch (type) { - case AuthAppletType::ShowParentalAuthentication: { - const auto callback = [this](bool is_successful) { AuthFinished(is_successful); }; - - if (arg0 == 1 && arg1 == 0 && arg2 == 1) { - // ShowAuthenticatorForConfiguration - frontend.VerifyPINForSettings(callback); - } else if (arg1 == 0 && arg2 == 0) { - // ShowParentalAuthentication(bool) - frontend.VerifyPIN(callback, static_cast(arg0)); - } else { - unimplemented_log(); - } - break; - } - case AuthAppletType::RegisterParentalPasscode: { - const auto callback = [this] { AuthFinished(true); }; - - if (arg0 == 0 && arg1 == 0 && arg2 == 0) { - // RegisterParentalPasscode - frontend.RegisterPIN(callback); - } else { - unimplemented_log(); - } - break; - } - case AuthAppletType::ChangeParentalPasscode: { - const auto callback = [this] { AuthFinished(true); }; - - if (arg0 == 0 && arg1 == 0 && arg2 == 0) { - // ChangeParentalPasscode - frontend.ChangePIN(callback); - } else { - unimplemented_log(); - } - break; - } - default: - unimplemented_log(); - } -} - -void Auth::AuthFinished(bool is_successful) { - successful = is_successful; - - struct Return { - ResultCode result_code; - }; - static_assert(sizeof(Return) == 0x4, "Return (AuthApplet) has incorrect size."); - - Return return_{GetStatus()}; - - std::vector out(sizeof(Return)); - std::memcpy(out.data(), &return_, sizeof(Return)); - - broker.PushNormalDataFromApplet(std::make_shared(system, std::move(out))); - broker.SignalStateChanged(); -} - -PhotoViewer::PhotoViewer(Core::System& system_, LibraryAppletMode applet_mode_, - const Core::Frontend::PhotoViewerApplet& frontend_) - : Applet{system_, applet_mode_}, frontend{frontend_}, system{system_} {} - -PhotoViewer::~PhotoViewer() = default; - -void PhotoViewer::Initialize() { - Applet::Initialize(); - complete = false; - - const auto storage = broker.PopNormalDataToApplet(); - ASSERT(storage != nullptr); - const auto data = storage->GetData(); - ASSERT(!data.empty()); - mode = static_cast(data[0]); -} - -bool PhotoViewer::TransactionComplete() const { - return complete; -} - -ResultCode PhotoViewer::GetStatus() const { - return ResultSuccess; -} - -void PhotoViewer::ExecuteInteractive() { - UNREACHABLE_MSG("Unexpected interactive applet data."); -} - -void PhotoViewer::Execute() { - if (complete) - return; - - const auto callback = [this] { ViewFinished(); }; - switch (mode) { - case PhotoViewerAppletMode::CurrentApp: - frontend.ShowPhotosForApplication(system.CurrentProcess()->GetTitleID(), callback); - break; - case PhotoViewerAppletMode::AllApps: - frontend.ShowAllPhotos(callback); - break; - default: - UNIMPLEMENTED_MSG("Unimplemented PhotoViewer applet mode={:02X}!", mode); - } -} - -void PhotoViewer::ViewFinished() { - broker.PushNormalDataFromApplet(std::make_shared(system, std::vector{})); - broker.SignalStateChanged(); -} - -StubApplet::StubApplet(Core::System& system_, AppletId id_, LibraryAppletMode applet_mode_) - : Applet{system_, applet_mode_}, id{id_}, system{system_} {} - -StubApplet::~StubApplet() = default; - -void StubApplet::Initialize() { - LOG_WARNING(Service_AM, "called (STUBBED)"); - Applet::Initialize(); - - const auto data = broker.PeekDataToAppletForDebug(); - system.GetReporter().SaveUnimplementedAppletReport( - static_cast(id), common_args.arguments_version, common_args.library_version, - common_args.theme_color, common_args.play_startup_sound, common_args.system_tick, - data.normal, data.interactive); - - LogCurrentStorage(broker, "Initialize"); -} - -bool StubApplet::TransactionComplete() const { - LOG_WARNING(Service_AM, "called (STUBBED)"); - return true; -} - -ResultCode StubApplet::GetStatus() const { - LOG_WARNING(Service_AM, "called (STUBBED)"); - return ResultSuccess; -} - -void StubApplet::ExecuteInteractive() { - LOG_WARNING(Service_AM, "called (STUBBED)"); - LogCurrentStorage(broker, "ExecuteInteractive"); - - broker.PushNormalDataFromApplet(std::make_shared(system, std::vector(0x1000))); - broker.PushInteractiveDataFromApplet( - std::make_shared(system, std::vector(0x1000))); - broker.SignalStateChanged(); -} - -void StubApplet::Execute() { - LOG_WARNING(Service_AM, "called (STUBBED)"); - LogCurrentStorage(broker, "Execute"); - - broker.PushNormalDataFromApplet(std::make_shared(system, std::vector(0x1000))); - broker.PushInteractiveDataFromApplet( - std::make_shared(system, std::vector(0x1000))); - broker.SignalStateChanged(); -} - -} // namespace Service::AM::Applets diff --git a/src/core/hle/service/am/applets/general_backend.h b/src/core/hle/service/am/applets/general_backend.h deleted file mode 100644 index 7496ded88..000000000 --- a/src/core/hle/service/am/applets/general_backend.h +++ /dev/null @@ -1,90 +0,0 @@ -// Copyright 2019 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#pragma once - -#include "core/hle/service/am/applets/applets.h" - -namespace Core { -class System; -} - -namespace Service::AM::Applets { - -enum class AuthAppletType : u32 { - ShowParentalAuthentication, - RegisterParentalPasscode, - ChangeParentalPasscode, -}; - -class Auth final : public Applet { -public: - explicit Auth(Core::System& system_, LibraryAppletMode applet_mode_, - Core::Frontend::ParentalControlsApplet& frontend_); - ~Auth() override; - - void Initialize() override; - bool TransactionComplete() const override; - ResultCode GetStatus() const override; - void ExecuteInteractive() override; - void Execute() override; - - void AuthFinished(bool is_successful = true); - -private: - Core::Frontend::ParentalControlsApplet& frontend; - Core::System& system; - bool complete = false; - bool successful = false; - - AuthAppletType type = AuthAppletType::ShowParentalAuthentication; - u8 arg0 = 0; - u8 arg1 = 0; - u8 arg2 = 0; -}; - -enum class PhotoViewerAppletMode : u8 { - CurrentApp = 0, - AllApps = 1, -}; - -class PhotoViewer final : public Applet { -public: - explicit PhotoViewer(Core::System& system_, LibraryAppletMode applet_mode_, - const Core::Frontend::PhotoViewerApplet& frontend_); - ~PhotoViewer() override; - - void Initialize() override; - bool TransactionComplete() const override; - ResultCode GetStatus() const override; - void ExecuteInteractive() override; - void Execute() override; - - void ViewFinished(); - -private: - const Core::Frontend::PhotoViewerApplet& frontend; - bool complete = false; - PhotoViewerAppletMode mode = PhotoViewerAppletMode::CurrentApp; - Core::System& system; -}; - -class StubApplet final : public Applet { -public: - explicit StubApplet(Core::System& system_, AppletId id_, LibraryAppletMode applet_mode_); - ~StubApplet() override; - - void Initialize() override; - - bool TransactionComplete() const override; - ResultCode GetStatus() const override; - void ExecuteInteractive() override; - void Execute() override; - -private: - AppletId id; - Core::System& system; -}; - -} // namespace Service::AM::Applets diff --git a/src/core/hle/service/am/applets/profile_select.cpp b/src/core/hle/service/am/applets/profile_select.cpp deleted file mode 100644 index 37048be26..000000000 --- a/src/core/hle/service/am/applets/profile_select.cpp +++ /dev/null @@ -1,78 +0,0 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#include - -#include "common/assert.h" -#include "common/string_util.h" -#include "core/core.h" -#include "core/frontend/applets/profile_select.h" -#include "core/hle/service/am/am.h" -#include "core/hle/service/am/applets/profile_select.h" - -namespace Service::AM::Applets { - -constexpr ResultCode ERR_USER_CANCELLED_SELECTION{ErrorModule::Account, 1}; - -ProfileSelect::ProfileSelect(Core::System& system_, LibraryAppletMode applet_mode_, - const Core::Frontend::ProfileSelectApplet& frontend_) - : Applet{system_, applet_mode_}, frontend{frontend_}, system{system_} {} - -ProfileSelect::~ProfileSelect() = default; - -void ProfileSelect::Initialize() { - complete = false; - status = ResultSuccess; - final_data.clear(); - - Applet::Initialize(); - - const auto user_config_storage = broker.PopNormalDataToApplet(); - ASSERT(user_config_storage != nullptr); - const auto& user_config = user_config_storage->GetData(); - - ASSERT(user_config.size() >= sizeof(UserSelectionConfig)); - std::memcpy(&config, user_config.data(), sizeof(UserSelectionConfig)); -} - -bool ProfileSelect::TransactionComplete() const { - return complete; -} - -ResultCode ProfileSelect::GetStatus() const { - return status; -} - -void ProfileSelect::ExecuteInteractive() { - UNREACHABLE_MSG("Attempted to call interactive execution on non-interactive applet."); -} - -void ProfileSelect::Execute() { - if (complete) { - broker.PushNormalDataFromApplet(std::make_shared(system, std::move(final_data))); - return; - } - - frontend.SelectProfile([this](std::optional uuid) { SelectionComplete(uuid); }); -} - -void ProfileSelect::SelectionComplete(std::optional uuid) { - UserSelectionOutput output{}; - - if (uuid.has_value() && uuid->uuid != Common::INVALID_UUID) { - output.result = 0; - output.uuid_selected = uuid->uuid; - } else { - status = ERR_USER_CANCELLED_SELECTION; - output.result = ERR_USER_CANCELLED_SELECTION.raw; - output.uuid_selected = Common::INVALID_UUID; - } - - final_data = std::vector(sizeof(UserSelectionOutput)); - std::memcpy(final_data.data(), &output, final_data.size()); - broker.PushNormalDataFromApplet(std::make_shared(system, std::move(final_data))); - broker.SignalStateChanged(); -} - -} // namespace Service::AM::Applets diff --git a/src/core/hle/service/am/applets/profile_select.h b/src/core/hle/service/am/applets/profile_select.h deleted file mode 100644 index 8fb76e6c4..000000000 --- a/src/core/hle/service/am/applets/profile_select.h +++ /dev/null @@ -1,59 +0,0 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#pragma once - -#include - -#include "common/common_funcs.h" -#include "common/uuid.h" -#include "core/hle/result.h" -#include "core/hle/service/am/applets/applets.h" - -namespace Core { -class System; -} - -namespace Service::AM::Applets { - -struct UserSelectionConfig { - // TODO(DarkLordZach): RE this structure - // It seems to be flags and the like that determine the UI of the applet on the switch... from - // my research this is safe to ignore for now. - INSERT_PADDING_BYTES(0xA0); -}; -static_assert(sizeof(UserSelectionConfig) == 0xA0, "UserSelectionConfig has incorrect size."); - -struct UserSelectionOutput { - u64 result; - u128 uuid_selected; -}; -static_assert(sizeof(UserSelectionOutput) == 0x18, "UserSelectionOutput has incorrect size."); - -class ProfileSelect final : public Applet { -public: - explicit ProfileSelect(Core::System& system_, LibraryAppletMode applet_mode_, - const Core::Frontend::ProfileSelectApplet& frontend_); - ~ProfileSelect() override; - - void Initialize() override; - - bool TransactionComplete() const override; - ResultCode GetStatus() const override; - void ExecuteInteractive() override; - void Execute() override; - - void SelectionComplete(std::optional uuid); - -private: - const Core::Frontend::ProfileSelectApplet& frontend; - - UserSelectionConfig config; - bool complete = false; - ResultCode status = ResultSuccess; - std::vector final_data; - Core::System& system; -}; - -} // namespace Service::AM::Applets diff --git a/src/core/hle/service/am/applets/software_keyboard.cpp b/src/core/hle/service/am/applets/software_keyboard.cpp deleted file mode 100644 index 00dfe1675..000000000 --- a/src/core/hle/service/am/applets/software_keyboard.cpp +++ /dev/null @@ -1,1082 +0,0 @@ -// Copyright 2021 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#include "common/string_util.h" -#include "core/core.h" -#include "core/frontend/applets/software_keyboard.h" -#include "core/hle/service/am/am.h" -#include "core/hle/service/am/applets/software_keyboard.h" - -namespace Service::AM::Applets { - -namespace { - -// The maximum number of UTF-16 characters that can be input into the swkbd text field. -constexpr u32 DEFAULT_MAX_TEXT_LENGTH = 500; - -constexpr std::size_t REPLY_BASE_SIZE = sizeof(SwkbdState) + sizeof(SwkbdReplyType); -constexpr std::size_t REPLY_UTF8_SIZE = 0x7D4; -constexpr std::size_t REPLY_UTF16_SIZE = 0x3EC; - -constexpr const char* GetTextCheckResultName(SwkbdTextCheckResult text_check_result) { - switch (text_check_result) { - case SwkbdTextCheckResult::Success: - return "Success"; - case SwkbdTextCheckResult::Failure: - return "Failure"; - case SwkbdTextCheckResult::Confirm: - return "Confirm"; - case SwkbdTextCheckResult::Silent: - return "Silent"; - default: - UNIMPLEMENTED_MSG("Unknown TextCheckResult={}", text_check_result); - return "Unknown"; - } -} - -void SetReplyBase(std::vector& reply, SwkbdState state, SwkbdReplyType reply_type) { - std::memcpy(reply.data(), &state, sizeof(SwkbdState)); - std::memcpy(reply.data() + sizeof(SwkbdState), &reply_type, sizeof(SwkbdReplyType)); -} - -} // Anonymous namespace - -SoftwareKeyboard::SoftwareKeyboard(Core::System& system_, LibraryAppletMode applet_mode_, - Core::Frontend::SoftwareKeyboardApplet& frontend_) - : Applet{system_, applet_mode_}, frontend{frontend_}, system{system_} {} - -SoftwareKeyboard::~SoftwareKeyboard() = default; - -void SoftwareKeyboard::Initialize() { - Applet::Initialize(); - - LOG_INFO(Service_AM, "Initializing Software Keyboard Applet with LibraryAppletMode={}", - applet_mode); - - LOG_DEBUG(Service_AM, - "Initializing Applet with common_args: arg_version={}, lib_version={}, " - "play_startup_sound={}, size={}, system_tick={}, theme_color={}", - common_args.arguments_version, common_args.library_version, - common_args.play_startup_sound, common_args.size, common_args.system_tick, - common_args.theme_color); - - swkbd_applet_version = SwkbdAppletVersion{common_args.library_version}; - - switch (applet_mode) { - case LibraryAppletMode::AllForeground: - InitializeForeground(); - break; - case LibraryAppletMode::Background: - case LibraryAppletMode::BackgroundIndirectDisplay: - InitializeBackground(applet_mode); - break; - default: - UNREACHABLE_MSG("Invalid LibraryAppletMode={}", applet_mode); - break; - } -} - -bool SoftwareKeyboard::TransactionComplete() const { - return complete; -} - -ResultCode SoftwareKeyboard::GetStatus() const { - return status; -} - -void SoftwareKeyboard::ExecuteInteractive() { - if (complete) { - return; - } - - if (is_background) { - ProcessInlineKeyboardRequest(); - } else { - ProcessTextCheck(); - } -} - -void SoftwareKeyboard::Execute() { - if (complete) { - return; - } - - if (is_background) { - return; - } - - ShowNormalKeyboard(); -} - -void SoftwareKeyboard::SubmitTextNormal(SwkbdResult result, std::u16string submitted_text) { - if (complete) { - return; - } - - if (swkbd_config_common.use_text_check && result == SwkbdResult::Ok) { - SubmitForTextCheck(submitted_text); - } else { - SubmitNormalOutputAndExit(result, submitted_text); - } -} - -void SoftwareKeyboard::SubmitTextInline(SwkbdReplyType reply_type, std::u16string submitted_text, - s32 cursor_position) { - if (complete) { - return; - } - - current_text = std::move(submitted_text); - current_cursor_position = cursor_position; - - if (inline_use_utf8) { - switch (reply_type) { - case SwkbdReplyType::ChangedString: - reply_type = SwkbdReplyType::ChangedStringUtf8; - break; - case SwkbdReplyType::MovedCursor: - reply_type = SwkbdReplyType::MovedCursorUtf8; - break; - case SwkbdReplyType::DecidedEnter: - reply_type = SwkbdReplyType::DecidedEnterUtf8; - break; - default: - break; - } - } - - if (use_changed_string_v2) { - switch (reply_type) { - case SwkbdReplyType::ChangedString: - reply_type = SwkbdReplyType::ChangedStringV2; - break; - case SwkbdReplyType::ChangedStringUtf8: - reply_type = SwkbdReplyType::ChangedStringUtf8V2; - break; - default: - break; - } - } - - if (use_moved_cursor_v2) { - switch (reply_type) { - case SwkbdReplyType::MovedCursor: - reply_type = SwkbdReplyType::MovedCursorV2; - break; - case SwkbdReplyType::MovedCursorUtf8: - reply_type = SwkbdReplyType::MovedCursorUtf8V2; - break; - default: - break; - } - } - - SendReply(reply_type); -} - -void SoftwareKeyboard::InitializeForeground() { - LOG_INFO(Service_AM, "Initializing Normal Software Keyboard Applet."); - - is_background = false; - - const auto swkbd_config_storage = broker.PopNormalDataToApplet(); - ASSERT(swkbd_config_storage != nullptr); - - const auto& swkbd_config_data = swkbd_config_storage->GetData(); - ASSERT(swkbd_config_data.size() >= sizeof(SwkbdConfigCommon)); - - std::memcpy(&swkbd_config_common, swkbd_config_data.data(), sizeof(SwkbdConfigCommon)); - - switch (swkbd_applet_version) { - case SwkbdAppletVersion::Version5: - case SwkbdAppletVersion::Version65542: - ASSERT(swkbd_config_data.size() == sizeof(SwkbdConfigCommon) + sizeof(SwkbdConfigOld)); - std::memcpy(&swkbd_config_old, swkbd_config_data.data() + sizeof(SwkbdConfigCommon), - sizeof(SwkbdConfigOld)); - break; - case SwkbdAppletVersion::Version196615: - case SwkbdAppletVersion::Version262152: - case SwkbdAppletVersion::Version327689: - ASSERT(swkbd_config_data.size() == sizeof(SwkbdConfigCommon) + sizeof(SwkbdConfigOld2)); - std::memcpy(&swkbd_config_old2, swkbd_config_data.data() + sizeof(SwkbdConfigCommon), - sizeof(SwkbdConfigOld2)); - break; - case SwkbdAppletVersion::Version393227: - case SwkbdAppletVersion::Version524301: - ASSERT(swkbd_config_data.size() == sizeof(SwkbdConfigCommon) + sizeof(SwkbdConfigNew)); - std::memcpy(&swkbd_config_new, swkbd_config_data.data() + sizeof(SwkbdConfigCommon), - sizeof(SwkbdConfigNew)); - break; - default: - UNIMPLEMENTED_MSG("Unknown SwkbdConfig revision={} with size={}", swkbd_applet_version, - swkbd_config_data.size()); - ASSERT(swkbd_config_data.size() >= sizeof(SwkbdConfigCommon) + sizeof(SwkbdConfigNew)); - std::memcpy(&swkbd_config_new, swkbd_config_data.data() + sizeof(SwkbdConfigCommon), - sizeof(SwkbdConfigNew)); - break; - } - - const auto work_buffer_storage = broker.PopNormalDataToApplet(); - ASSERT(work_buffer_storage != nullptr); - - if (swkbd_config_common.initial_string_length == 0) { - InitializeFrontendKeyboard(); - return; - } - - const auto& work_buffer = work_buffer_storage->GetData(); - - std::vector initial_string(swkbd_config_common.initial_string_length); - - std::memcpy(initial_string.data(), - work_buffer.data() + swkbd_config_common.initial_string_offset, - swkbd_config_common.initial_string_length * sizeof(char16_t)); - - initial_text = Common::UTF16StringFromFixedZeroTerminatedBuffer(initial_string.data(), - initial_string.size()); - - LOG_DEBUG(Service_AM, "\nInitial Text: {}", Common::UTF16ToUTF8(initial_text)); - - InitializeFrontendKeyboard(); -} - -void SoftwareKeyboard::InitializeBackground(LibraryAppletMode library_applet_mode) { - LOG_INFO(Service_AM, "Initializing Inline Software Keyboard Applet."); - - is_background = true; - - const auto swkbd_inline_initialize_arg_storage = broker.PopNormalDataToApplet(); - ASSERT(swkbd_inline_initialize_arg_storage != nullptr); - - const auto& swkbd_inline_initialize_arg = swkbd_inline_initialize_arg_storage->GetData(); - ASSERT(swkbd_inline_initialize_arg.size() == sizeof(SwkbdInitializeArg)); - - std::memcpy(&swkbd_initialize_arg, swkbd_inline_initialize_arg.data(), - swkbd_inline_initialize_arg.size()); - - if (swkbd_initialize_arg.library_applet_mode_flag) { - ASSERT(library_applet_mode == LibraryAppletMode::Background); - } else { - ASSERT(library_applet_mode == LibraryAppletMode::BackgroundIndirectDisplay); - } -} - -void SoftwareKeyboard::ProcessTextCheck() { - const auto text_check_storage = broker.PopInteractiveDataToApplet(); - ASSERT(text_check_storage != nullptr); - - const auto& text_check_data = text_check_storage->GetData(); - ASSERT(text_check_data.size() == sizeof(SwkbdTextCheck)); - - SwkbdTextCheck swkbd_text_check; - - std::memcpy(&swkbd_text_check, text_check_data.data(), sizeof(SwkbdTextCheck)); - - std::u16string text_check_message = - swkbd_text_check.text_check_result == SwkbdTextCheckResult::Failure || - swkbd_text_check.text_check_result == SwkbdTextCheckResult::Confirm - ? Common::UTF16StringFromFixedZeroTerminatedBuffer( - swkbd_text_check.text_check_message.data(), - swkbd_text_check.text_check_message.size()) - : u""; - - LOG_INFO(Service_AM, "\nTextCheckResult: {}\nTextCheckMessage: {}", - GetTextCheckResultName(swkbd_text_check.text_check_result), - Common::UTF16ToUTF8(text_check_message)); - - switch (swkbd_text_check.text_check_result) { - case SwkbdTextCheckResult::Success: - SubmitNormalOutputAndExit(SwkbdResult::Ok, current_text); - break; - case SwkbdTextCheckResult::Failure: - ShowTextCheckDialog(SwkbdTextCheckResult::Failure, std::move(text_check_message)); - break; - case SwkbdTextCheckResult::Confirm: - ShowTextCheckDialog(SwkbdTextCheckResult::Confirm, std::move(text_check_message)); - break; - case SwkbdTextCheckResult::Silent: - default: - break; - } -} - -void SoftwareKeyboard::ProcessInlineKeyboardRequest() { - const auto request_data_storage = broker.PopInteractiveDataToApplet(); - ASSERT(request_data_storage != nullptr); - - const auto& request_data = request_data_storage->GetData(); - ASSERT(request_data.size() >= sizeof(SwkbdRequestCommand)); - - SwkbdRequestCommand request_command; - - std::memcpy(&request_command, request_data.data(), sizeof(SwkbdRequestCommand)); - - switch (request_command) { - case SwkbdRequestCommand::Finalize: - RequestFinalize(request_data); - break; - case SwkbdRequestCommand::SetUserWordInfo: - RequestSetUserWordInfo(request_data); - break; - case SwkbdRequestCommand::SetCustomizeDic: - RequestSetCustomizeDic(request_data); - break; - case SwkbdRequestCommand::Calc: - RequestCalc(request_data); - break; - case SwkbdRequestCommand::SetCustomizedDictionaries: - RequestSetCustomizedDictionaries(request_data); - break; - case SwkbdRequestCommand::UnsetCustomizedDictionaries: - RequestUnsetCustomizedDictionaries(request_data); - break; - case SwkbdRequestCommand::SetChangedStringV2Flag: - RequestSetChangedStringV2Flag(request_data); - break; - case SwkbdRequestCommand::SetMovedCursorV2Flag: - RequestSetMovedCursorV2Flag(request_data); - break; - default: - UNIMPLEMENTED_MSG("Unknown SwkbdRequestCommand={}", request_command); - break; - } -} - -void SoftwareKeyboard::SubmitNormalOutputAndExit(SwkbdResult result, - std::u16string submitted_text) { - std::vector out_data(sizeof(SwkbdResult) + STRING_BUFFER_SIZE); - - if (swkbd_config_common.use_utf8) { - std::string utf8_submitted_text = Common::UTF16ToUTF8(submitted_text); - - LOG_DEBUG(Service_AM, "\nSwkbdResult: {}\nUTF-8 Submitted Text: {}", result, - utf8_submitted_text); - - std::memcpy(out_data.data(), &result, sizeof(SwkbdResult)); - std::memcpy(out_data.data() + sizeof(SwkbdResult), utf8_submitted_text.data(), - utf8_submitted_text.size()); - } else { - LOG_DEBUG(Service_AM, "\nSwkbdResult: {}\nUTF-16 Submitted Text: {}", result, - Common::UTF16ToUTF8(submitted_text)); - - std::memcpy(out_data.data(), &result, sizeof(SwkbdResult)); - std::memcpy(out_data.data() + sizeof(SwkbdResult), submitted_text.data(), - submitted_text.size() * sizeof(char16_t)); - } - - broker.PushNormalDataFromApplet(std::make_shared(system, std::move(out_data))); - - ExitKeyboard(); -} - -void SoftwareKeyboard::SubmitForTextCheck(std::u16string submitted_text) { - current_text = std::move(submitted_text); - - std::vector out_data(sizeof(u64) + STRING_BUFFER_SIZE); - - if (swkbd_config_common.use_utf8) { - std::string utf8_submitted_text = Common::UTF16ToUTF8(current_text); - const u64 buffer_size = sizeof(u64) + utf8_submitted_text.size(); - - LOG_DEBUG(Service_AM, "\nBuffer Size: {}\nUTF-8 Submitted Text: {}", buffer_size, - utf8_submitted_text); - - std::memcpy(out_data.data(), &buffer_size, sizeof(u64)); - std::memcpy(out_data.data() + sizeof(u64), utf8_submitted_text.data(), - utf8_submitted_text.size()); - } else { - const u64 buffer_size = sizeof(u64) + current_text.size() * sizeof(char16_t); - - LOG_DEBUG(Service_AM, "\nBuffer Size: {}\nUTF-16 Submitted Text: {}", buffer_size, - Common::UTF16ToUTF8(current_text)); - - std::memcpy(out_data.data(), &buffer_size, sizeof(u64)); - std::memcpy(out_data.data() + sizeof(u64), current_text.data(), - current_text.size() * sizeof(char16_t)); - } - - broker.PushInteractiveDataFromApplet(std::make_shared(system, std::move(out_data))); -} - -void SoftwareKeyboard::SendReply(SwkbdReplyType reply_type) { - switch (reply_type) { - case SwkbdReplyType::FinishedInitialize: - ReplyFinishedInitialize(); - break; - case SwkbdReplyType::Default: - ReplyDefault(); - break; - case SwkbdReplyType::ChangedString: - ReplyChangedString(); - break; - case SwkbdReplyType::MovedCursor: - ReplyMovedCursor(); - break; - case SwkbdReplyType::MovedTab: - ReplyMovedTab(); - break; - case SwkbdReplyType::DecidedEnter: - ReplyDecidedEnter(); - break; - case SwkbdReplyType::DecidedCancel: - ReplyDecidedCancel(); - break; - case SwkbdReplyType::ChangedStringUtf8: - ReplyChangedStringUtf8(); - break; - case SwkbdReplyType::MovedCursorUtf8: - ReplyMovedCursorUtf8(); - break; - case SwkbdReplyType::DecidedEnterUtf8: - ReplyDecidedEnterUtf8(); - break; - case SwkbdReplyType::UnsetCustomizeDic: - ReplyUnsetCustomizeDic(); - break; - case SwkbdReplyType::ReleasedUserWordInfo: - ReplyReleasedUserWordInfo(); - break; - case SwkbdReplyType::UnsetCustomizedDictionaries: - ReplyUnsetCustomizedDictionaries(); - break; - case SwkbdReplyType::ChangedStringV2: - ReplyChangedStringV2(); - break; - case SwkbdReplyType::MovedCursorV2: - ReplyMovedCursorV2(); - break; - case SwkbdReplyType::ChangedStringUtf8V2: - ReplyChangedStringUtf8V2(); - break; - case SwkbdReplyType::MovedCursorUtf8V2: - ReplyMovedCursorUtf8V2(); - break; - default: - UNIMPLEMENTED_MSG("Unknown SwkbdReplyType={}", reply_type); - ReplyDefault(); - break; - } -} - -void SoftwareKeyboard::ChangeState(SwkbdState state) { - swkbd_state = state; - - ReplyDefault(); -} - -void SoftwareKeyboard::InitializeFrontendKeyboard() { - if (is_background) { - const auto& appear_arg = swkbd_calc_arg.appear_arg; - - std::u16string ok_text = Common::UTF16StringFromFixedZeroTerminatedBuffer( - appear_arg.ok_text.data(), appear_arg.ok_text.size()); - - const u32 max_text_length = - appear_arg.max_text_length > 0 && appear_arg.max_text_length <= DEFAULT_MAX_TEXT_LENGTH - ? appear_arg.max_text_length - : DEFAULT_MAX_TEXT_LENGTH; - - const u32 min_text_length = - appear_arg.min_text_length <= max_text_length ? appear_arg.min_text_length : 0; - - const s32 initial_cursor_position = - current_cursor_position > 0 ? current_cursor_position : 0; - - const auto text_draw_type = - max_text_length <= 32 ? SwkbdTextDrawType::Line : SwkbdTextDrawType::Box; - - Core::Frontend::KeyboardInitializeParameters initialize_parameters{ - .ok_text{std::move(ok_text)}, - .header_text{}, - .sub_text{}, - .guide_text{}, - .initial_text{current_text}, - .max_text_length{max_text_length}, - .min_text_length{min_text_length}, - .initial_cursor_position{initial_cursor_position}, - .type{appear_arg.type}, - .password_mode{SwkbdPasswordMode::Disabled}, - .text_draw_type{text_draw_type}, - .key_disable_flags{appear_arg.key_disable_flags}, - .use_blur_background{false}, - .enable_backspace_button{swkbd_calc_arg.enable_backspace_button}, - .enable_return_button{appear_arg.enable_return_button}, - .disable_cancel_button{appear_arg.disable_cancel_button}, - }; - - frontend.InitializeKeyboard( - true, std::move(initialize_parameters), {}, - [this](SwkbdReplyType reply_type, std::u16string submitted_text, s32 cursor_position) { - SubmitTextInline(reply_type, submitted_text, cursor_position); - }); - } else { - std::u16string ok_text = Common::UTF16StringFromFixedZeroTerminatedBuffer( - swkbd_config_common.ok_text.data(), swkbd_config_common.ok_text.size()); - - std::u16string header_text = Common::UTF16StringFromFixedZeroTerminatedBuffer( - swkbd_config_common.header_text.data(), swkbd_config_common.header_text.size()); - - std::u16string sub_text = Common::UTF16StringFromFixedZeroTerminatedBuffer( - swkbd_config_common.sub_text.data(), swkbd_config_common.sub_text.size()); - - std::u16string guide_text = Common::UTF16StringFromFixedZeroTerminatedBuffer( - swkbd_config_common.guide_text.data(), swkbd_config_common.guide_text.size()); - - const u32 max_text_length = - swkbd_config_common.max_text_length > 0 && - swkbd_config_common.max_text_length <= DEFAULT_MAX_TEXT_LENGTH - ? swkbd_config_common.max_text_length - : DEFAULT_MAX_TEXT_LENGTH; - - const u32 min_text_length = swkbd_config_common.min_text_length <= max_text_length - ? swkbd_config_common.min_text_length - : 0; - - const s32 initial_cursor_position = [this] { - switch (swkbd_config_common.initial_cursor_position) { - case SwkbdInitialCursorPosition::Start: - default: - return 0; - case SwkbdInitialCursorPosition::End: - return static_cast(initial_text.size()); - } - }(); - - const auto text_draw_type = [this, max_text_length] { - switch (swkbd_config_common.text_draw_type) { - case SwkbdTextDrawType::Line: - default: - return max_text_length <= 32 ? SwkbdTextDrawType::Line : SwkbdTextDrawType::Box; - case SwkbdTextDrawType::Box: - case SwkbdTextDrawType::DownloadCode: - return swkbd_config_common.text_draw_type; - } - }(); - - const auto enable_return_button = text_draw_type == SwkbdTextDrawType::Box - ? swkbd_config_common.enable_return_button - : false; - - const auto disable_cancel_button = swkbd_applet_version >= SwkbdAppletVersion::Version393227 - ? swkbd_config_new.disable_cancel_button - : false; - - Core::Frontend::KeyboardInitializeParameters initialize_parameters{ - .ok_text{std::move(ok_text)}, - .header_text{std::move(header_text)}, - .sub_text{std::move(sub_text)}, - .guide_text{std::move(guide_text)}, - .initial_text{initial_text}, - .max_text_length{max_text_length}, - .min_text_length{min_text_length}, - .initial_cursor_position{initial_cursor_position}, - .type{swkbd_config_common.type}, - .password_mode{swkbd_config_common.password_mode}, - .text_draw_type{text_draw_type}, - .key_disable_flags{swkbd_config_common.key_disable_flags}, - .use_blur_background{swkbd_config_common.use_blur_background}, - .enable_backspace_button{true}, - .enable_return_button{enable_return_button}, - .disable_cancel_button{disable_cancel_button}, - }; - - frontend.InitializeKeyboard(false, std::move(initialize_parameters), - [this](SwkbdResult result, std::u16string submitted_text) { - SubmitTextNormal(result, submitted_text); - }, - {}); - } -} - -void SoftwareKeyboard::ShowNormalKeyboard() { - frontend.ShowNormalKeyboard(); -} - -void SoftwareKeyboard::ShowTextCheckDialog(SwkbdTextCheckResult text_check_result, - std::u16string text_check_message) { - frontend.ShowTextCheckDialog(text_check_result, std::move(text_check_message)); -} - -void SoftwareKeyboard::ShowInlineKeyboard() { - if (swkbd_state != SwkbdState::InitializedIsHidden) { - return; - } - - ChangeState(SwkbdState::InitializedIsAppearing); - - const auto& appear_arg = swkbd_calc_arg.appear_arg; - - const u32 max_text_length = - appear_arg.max_text_length > 0 && appear_arg.max_text_length <= DEFAULT_MAX_TEXT_LENGTH - ? appear_arg.max_text_length - : DEFAULT_MAX_TEXT_LENGTH; - - const u32 min_text_length = - appear_arg.min_text_length <= max_text_length ? appear_arg.min_text_length : 0; - - Core::Frontend::InlineAppearParameters appear_parameters{ - .max_text_length{max_text_length}, - .min_text_length{min_text_length}, - .key_top_scale_x{swkbd_calc_arg.key_top_scale_x}, - .key_top_scale_y{swkbd_calc_arg.key_top_scale_y}, - .key_top_translate_x{swkbd_calc_arg.key_top_translate_x}, - .key_top_translate_y{swkbd_calc_arg.key_top_translate_y}, - .type{appear_arg.type}, - .key_disable_flags{appear_arg.key_disable_flags}, - .key_top_as_floating{swkbd_calc_arg.key_top_as_floating}, - .enable_backspace_button{swkbd_calc_arg.enable_backspace_button}, - .enable_return_button{appear_arg.enable_return_button}, - .disable_cancel_button{appear_arg.disable_cancel_button}, - }; - - frontend.ShowInlineKeyboard(std::move(appear_parameters)); - - ChangeState(SwkbdState::InitializedIsShown); -} - -void SoftwareKeyboard::HideInlineKeyboard() { - if (swkbd_state != SwkbdState::InitializedIsShown) { - return; - } - - ChangeState(SwkbdState::InitializedIsDisappearing); - - frontend.HideInlineKeyboard(); - - ChangeState(SwkbdState::InitializedIsHidden); -} - -void SoftwareKeyboard::InlineTextChanged() { - Core::Frontend::InlineTextParameters text_parameters{ - .input_text{current_text}, - .cursor_position{current_cursor_position}, - }; - - frontend.InlineTextChanged(std::move(text_parameters)); -} - -void SoftwareKeyboard::ExitKeyboard() { - complete = true; - status = ResultSuccess; - - frontend.ExitKeyboard(); - - broker.SignalStateChanged(); -} - -// Inline Software Keyboard Requests - -void SoftwareKeyboard::RequestFinalize(const std::vector& request_data) { - LOG_DEBUG(Service_AM, "Processing Request: Finalize"); - - ChangeState(SwkbdState::NotInitialized); - - ExitKeyboard(); -} - -void SoftwareKeyboard::RequestSetUserWordInfo(const std::vector& request_data) { - LOG_WARNING(Service_AM, "SetUserWordInfo is not implemented."); -} - -void SoftwareKeyboard::RequestSetCustomizeDic(const std::vector& request_data) { - LOG_WARNING(Service_AM, "SetCustomizeDic is not implemented."); -} - -void SoftwareKeyboard::RequestCalc(const std::vector& request_data) { - LOG_DEBUG(Service_AM, "Processing Request: Calc"); - - ASSERT(request_data.size() == sizeof(SwkbdRequestCommand) + sizeof(SwkbdCalcArg)); - - std::memcpy(&swkbd_calc_arg, request_data.data() + sizeof(SwkbdRequestCommand), - sizeof(SwkbdCalcArg)); - - if (swkbd_calc_arg.flags.set_input_text) { - current_text = Common::UTF16StringFromFixedZeroTerminatedBuffer( - swkbd_calc_arg.input_text.data(), swkbd_calc_arg.input_text.size()); - } - - if (swkbd_calc_arg.flags.set_cursor_position) { - current_cursor_position = swkbd_calc_arg.cursor_position; - } - - if (swkbd_calc_arg.flags.set_utf8_mode) { - inline_use_utf8 = swkbd_calc_arg.utf8_mode; - } - - if (swkbd_state <= SwkbdState::InitializedIsHidden && - swkbd_calc_arg.flags.unset_customize_dic) { - ReplyUnsetCustomizeDic(); - } - - if (swkbd_state <= SwkbdState::InitializedIsHidden && - swkbd_calc_arg.flags.unset_user_word_info) { - ReplyReleasedUserWordInfo(); - } - - if (swkbd_state == SwkbdState::NotInitialized && swkbd_calc_arg.flags.set_initialize_arg) { - InitializeFrontendKeyboard(); - - ChangeState(SwkbdState::InitializedIsHidden); - - ReplyFinishedInitialize(); - } - - if (!swkbd_calc_arg.flags.set_initialize_arg && - (swkbd_calc_arg.flags.set_input_text || swkbd_calc_arg.flags.set_cursor_position)) { - InlineTextChanged(); - } - - if (swkbd_state == SwkbdState::InitializedIsHidden && swkbd_calc_arg.flags.appear) { - ShowInlineKeyboard(); - return; - } - - if (swkbd_state == SwkbdState::InitializedIsShown && swkbd_calc_arg.flags.disappear) { - HideInlineKeyboard(); - return; - } -} - -void SoftwareKeyboard::RequestSetCustomizedDictionaries(const std::vector& request_data) { - LOG_WARNING(Service_AM, "SetCustomizedDictionaries is not implemented."); -} - -void SoftwareKeyboard::RequestUnsetCustomizedDictionaries(const std::vector& request_data) { - LOG_WARNING(Service_AM, "(STUBBED) Processing Request: UnsetCustomizedDictionaries"); - - ReplyUnsetCustomizedDictionaries(); -} - -void SoftwareKeyboard::RequestSetChangedStringV2Flag(const std::vector& request_data) { - LOG_DEBUG(Service_AM, "Processing Request: SetChangedStringV2Flag"); - - ASSERT(request_data.size() == sizeof(SwkbdRequestCommand) + 1); - - std::memcpy(&use_changed_string_v2, request_data.data() + sizeof(SwkbdRequestCommand), 1); -} - -void SoftwareKeyboard::RequestSetMovedCursorV2Flag(const std::vector& request_data) { - LOG_DEBUG(Service_AM, "Processing Request: SetMovedCursorV2Flag"); - - ASSERT(request_data.size() == sizeof(SwkbdRequestCommand) + 1); - - std::memcpy(&use_moved_cursor_v2, request_data.data() + sizeof(SwkbdRequestCommand), 1); -} - -// Inline Software Keyboard Replies - -void SoftwareKeyboard::ReplyFinishedInitialize() { - LOG_DEBUG(Service_AM, "Sending Reply: FinishedInitialize"); - - std::vector reply(REPLY_BASE_SIZE + 1); - - SetReplyBase(reply, swkbd_state, SwkbdReplyType::FinishedInitialize); - - broker.PushInteractiveDataFromApplet(std::make_shared(system, std::move(reply))); -} - -void SoftwareKeyboard::ReplyDefault() { - LOG_DEBUG(Service_AM, "Sending Reply: Default"); - - std::vector reply(REPLY_BASE_SIZE); - - SetReplyBase(reply, swkbd_state, SwkbdReplyType::Default); - - broker.PushInteractiveDataFromApplet(std::make_shared(system, std::move(reply))); -} - -void SoftwareKeyboard::ReplyChangedString() { - LOG_DEBUG(Service_AM, "Sending Reply: ChangedString"); - - std::vector reply(REPLY_BASE_SIZE + REPLY_UTF16_SIZE + sizeof(SwkbdChangedStringArg)); - - SetReplyBase(reply, swkbd_state, SwkbdReplyType::ChangedString); - - const SwkbdChangedStringArg changed_string_arg{ - .text_length{static_cast(current_text.size())}, - .dictionary_start_cursor_position{-1}, - .dictionary_end_cursor_position{-1}, - .cursor_position{current_cursor_position}, - }; - - std::memcpy(reply.data() + REPLY_BASE_SIZE, current_text.data(), - current_text.size() * sizeof(char16_t)); - std::memcpy(reply.data() + REPLY_BASE_SIZE + REPLY_UTF16_SIZE, &changed_string_arg, - sizeof(SwkbdChangedStringArg)); - - broker.PushInteractiveDataFromApplet(std::make_shared(system, std::move(reply))); -} - -void SoftwareKeyboard::ReplyMovedCursor() { - LOG_DEBUG(Service_AM, "Sending Reply: MovedCursor"); - - std::vector reply(REPLY_BASE_SIZE + REPLY_UTF16_SIZE + sizeof(SwkbdMovedCursorArg)); - - SetReplyBase(reply, swkbd_state, SwkbdReplyType::MovedCursor); - - const SwkbdMovedCursorArg moved_cursor_arg{ - .text_length{static_cast(current_text.size())}, - .cursor_position{current_cursor_position}, - }; - - std::memcpy(reply.data() + REPLY_BASE_SIZE, current_text.data(), - current_text.size() * sizeof(char16_t)); - std::memcpy(reply.data() + REPLY_BASE_SIZE + REPLY_UTF16_SIZE, &moved_cursor_arg, - sizeof(SwkbdMovedCursorArg)); - - broker.PushInteractiveDataFromApplet(std::make_shared(system, std::move(reply))); -} - -void SoftwareKeyboard::ReplyMovedTab() { - LOG_DEBUG(Service_AM, "Sending Reply: MovedTab"); - - std::vector reply(REPLY_BASE_SIZE + REPLY_UTF16_SIZE + sizeof(SwkbdMovedTabArg)); - - SetReplyBase(reply, swkbd_state, SwkbdReplyType::MovedTab); - - const SwkbdMovedTabArg moved_tab_arg{ - .text_length{static_cast(current_text.size())}, - .cursor_position{current_cursor_position}, - }; - - std::memcpy(reply.data() + REPLY_BASE_SIZE, current_text.data(), - current_text.size() * sizeof(char16_t)); - std::memcpy(reply.data() + REPLY_BASE_SIZE + REPLY_UTF16_SIZE, &moved_tab_arg, - sizeof(SwkbdMovedTabArg)); - - broker.PushInteractiveDataFromApplet(std::make_shared(system, std::move(reply))); -} - -void SoftwareKeyboard::ReplyDecidedEnter() { - LOG_DEBUG(Service_AM, "Sending Reply: DecidedEnter"); - - std::vector reply(REPLY_BASE_SIZE + REPLY_UTF16_SIZE + sizeof(SwkbdDecidedEnterArg)); - - SetReplyBase(reply, swkbd_state, SwkbdReplyType::DecidedEnter); - - const SwkbdDecidedEnterArg decided_enter_arg{ - .text_length{static_cast(current_text.size())}, - }; - - std::memcpy(reply.data() + REPLY_BASE_SIZE, current_text.data(), - current_text.size() * sizeof(char16_t)); - std::memcpy(reply.data() + REPLY_BASE_SIZE + REPLY_UTF16_SIZE, &decided_enter_arg, - sizeof(SwkbdDecidedEnterArg)); - - broker.PushInteractiveDataFromApplet(std::make_shared(system, std::move(reply))); - - HideInlineKeyboard(); -} - -void SoftwareKeyboard::ReplyDecidedCancel() { - LOG_DEBUG(Service_AM, "Sending Reply: DecidedCancel"); - - std::vector reply(REPLY_BASE_SIZE); - - SetReplyBase(reply, swkbd_state, SwkbdReplyType::DecidedCancel); - - broker.PushInteractiveDataFromApplet(std::make_shared(system, std::move(reply))); - - HideInlineKeyboard(); -} - -void SoftwareKeyboard::ReplyChangedStringUtf8() { - LOG_DEBUG(Service_AM, "Sending Reply: ChangedStringUtf8"); - - std::vector reply(REPLY_BASE_SIZE + REPLY_UTF8_SIZE + sizeof(SwkbdChangedStringArg)); - - SetReplyBase(reply, swkbd_state, SwkbdReplyType::ChangedStringUtf8); - - std::string utf8_current_text = Common::UTF16ToUTF8(current_text); - - const SwkbdChangedStringArg changed_string_arg{ - .text_length{static_cast(current_text.size())}, - .dictionary_start_cursor_position{-1}, - .dictionary_end_cursor_position{-1}, - .cursor_position{current_cursor_position}, - }; - - std::memcpy(reply.data() + REPLY_BASE_SIZE, utf8_current_text.data(), utf8_current_text.size()); - std::memcpy(reply.data() + REPLY_BASE_SIZE + REPLY_UTF8_SIZE, &changed_string_arg, - sizeof(SwkbdChangedStringArg)); - - broker.PushInteractiveDataFromApplet(std::make_shared(system, std::move(reply))); -} - -void SoftwareKeyboard::ReplyMovedCursorUtf8() { - LOG_DEBUG(Service_AM, "Sending Reply: MovedCursorUtf8"); - - std::vector reply(REPLY_BASE_SIZE + REPLY_UTF8_SIZE + sizeof(SwkbdMovedCursorArg)); - - SetReplyBase(reply, swkbd_state, SwkbdReplyType::MovedCursorUtf8); - - std::string utf8_current_text = Common::UTF16ToUTF8(current_text); - - const SwkbdMovedCursorArg moved_cursor_arg{ - .text_length{static_cast(current_text.size())}, - .cursor_position{current_cursor_position}, - }; - - std::memcpy(reply.data() + REPLY_BASE_SIZE, utf8_current_text.data(), utf8_current_text.size()); - std::memcpy(reply.data() + REPLY_BASE_SIZE + REPLY_UTF8_SIZE, &moved_cursor_arg, - sizeof(SwkbdMovedCursorArg)); - - broker.PushInteractiveDataFromApplet(std::make_shared(system, std::move(reply))); -} - -void SoftwareKeyboard::ReplyDecidedEnterUtf8() { - LOG_DEBUG(Service_AM, "Sending Reply: DecidedEnterUtf8"); - - std::vector reply(REPLY_BASE_SIZE + REPLY_UTF8_SIZE + sizeof(SwkbdDecidedEnterArg)); - - SetReplyBase(reply, swkbd_state, SwkbdReplyType::DecidedEnterUtf8); - - std::string utf8_current_text = Common::UTF16ToUTF8(current_text); - - const SwkbdDecidedEnterArg decided_enter_arg{ - .text_length{static_cast(current_text.size())}, - }; - - std::memcpy(reply.data() + REPLY_BASE_SIZE, utf8_current_text.data(), utf8_current_text.size()); - std::memcpy(reply.data() + REPLY_BASE_SIZE + REPLY_UTF8_SIZE, &decided_enter_arg, - sizeof(SwkbdDecidedEnterArg)); - - broker.PushInteractiveDataFromApplet(std::make_shared(system, std::move(reply))); - - HideInlineKeyboard(); -} - -void SoftwareKeyboard::ReplyUnsetCustomizeDic() { - LOG_DEBUG(Service_AM, "Sending Reply: UnsetCustomizeDic"); - - std::vector reply(REPLY_BASE_SIZE); - - SetReplyBase(reply, swkbd_state, SwkbdReplyType::UnsetCustomizeDic); - - broker.PushInteractiveDataFromApplet(std::make_shared(system, std::move(reply))); -} - -void SoftwareKeyboard::ReplyReleasedUserWordInfo() { - LOG_DEBUG(Service_AM, "Sending Reply: ReleasedUserWordInfo"); - - std::vector reply(REPLY_BASE_SIZE); - - SetReplyBase(reply, swkbd_state, SwkbdReplyType::ReleasedUserWordInfo); - - broker.PushInteractiveDataFromApplet(std::make_shared(system, std::move(reply))); -} - -void SoftwareKeyboard::ReplyUnsetCustomizedDictionaries() { - LOG_DEBUG(Service_AM, "Sending Reply: UnsetCustomizedDictionaries"); - - std::vector reply(REPLY_BASE_SIZE); - - SetReplyBase(reply, swkbd_state, SwkbdReplyType::UnsetCustomizedDictionaries); - - broker.PushInteractiveDataFromApplet(std::make_shared(system, std::move(reply))); -} - -void SoftwareKeyboard::ReplyChangedStringV2() { - LOG_DEBUG(Service_AM, "Sending Reply: ChangedStringV2"); - - std::vector reply(REPLY_BASE_SIZE + REPLY_UTF16_SIZE + sizeof(SwkbdChangedStringArg) + 1); - - SetReplyBase(reply, swkbd_state, SwkbdReplyType::ChangedStringV2); - - const SwkbdChangedStringArg changed_string_arg{ - .text_length{static_cast(current_text.size())}, - .dictionary_start_cursor_position{-1}, - .dictionary_end_cursor_position{-1}, - .cursor_position{current_cursor_position}, - }; - - constexpr u8 flag = 0; - - std::memcpy(reply.data() + REPLY_BASE_SIZE, current_text.data(), - current_text.size() * sizeof(char16_t)); - std::memcpy(reply.data() + REPLY_BASE_SIZE + REPLY_UTF16_SIZE, &changed_string_arg, - sizeof(SwkbdChangedStringArg)); - std::memcpy(reply.data() + REPLY_BASE_SIZE + REPLY_UTF16_SIZE + sizeof(SwkbdChangedStringArg), - &flag, 1); - - broker.PushInteractiveDataFromApplet(std::make_shared(system, std::move(reply))); -} - -void SoftwareKeyboard::ReplyMovedCursorV2() { - LOG_DEBUG(Service_AM, "Sending Reply: MovedCursorV2"); - - std::vector reply(REPLY_BASE_SIZE + REPLY_UTF16_SIZE + sizeof(SwkbdMovedCursorArg) + 1); - - SetReplyBase(reply, swkbd_state, SwkbdReplyType::MovedCursorV2); - - const SwkbdMovedCursorArg moved_cursor_arg{ - .text_length{static_cast(current_text.size())}, - .cursor_position{current_cursor_position}, - }; - - constexpr u8 flag = 0; - - std::memcpy(reply.data() + REPLY_BASE_SIZE, current_text.data(), - current_text.size() * sizeof(char16_t)); - std::memcpy(reply.data() + REPLY_BASE_SIZE + REPLY_UTF16_SIZE, &moved_cursor_arg, - sizeof(SwkbdMovedCursorArg)); - std::memcpy(reply.data() + REPLY_BASE_SIZE + REPLY_UTF16_SIZE + sizeof(SwkbdMovedCursorArg), - &flag, 1); - - broker.PushInteractiveDataFromApplet(std::make_shared(system, std::move(reply))); -} - -void SoftwareKeyboard::ReplyChangedStringUtf8V2() { - LOG_DEBUG(Service_AM, "Sending Reply: ChangedStringUtf8V2"); - - std::vector reply(REPLY_BASE_SIZE + REPLY_UTF8_SIZE + sizeof(SwkbdChangedStringArg) + 1); - - SetReplyBase(reply, swkbd_state, SwkbdReplyType::ChangedStringUtf8V2); - - std::string utf8_current_text = Common::UTF16ToUTF8(current_text); - - const SwkbdChangedStringArg changed_string_arg{ - .text_length{static_cast(current_text.size())}, - .dictionary_start_cursor_position{-1}, - .dictionary_end_cursor_position{-1}, - .cursor_position{current_cursor_position}, - }; - - constexpr u8 flag = 0; - - std::memcpy(reply.data() + REPLY_BASE_SIZE, utf8_current_text.data(), utf8_current_text.size()); - std::memcpy(reply.data() + REPLY_BASE_SIZE + REPLY_UTF8_SIZE, &changed_string_arg, - sizeof(SwkbdChangedStringArg)); - std::memcpy(reply.data() + REPLY_BASE_SIZE + REPLY_UTF8_SIZE + sizeof(SwkbdChangedStringArg), - &flag, 1); - - broker.PushInteractiveDataFromApplet(std::make_shared(system, std::move(reply))); -} - -void SoftwareKeyboard::ReplyMovedCursorUtf8V2() { - LOG_DEBUG(Service_AM, "Sending Reply: MovedCursorUtf8V2"); - - std::vector reply(REPLY_BASE_SIZE + REPLY_UTF8_SIZE + sizeof(SwkbdMovedCursorArg) + 1); - - SetReplyBase(reply, swkbd_state, SwkbdReplyType::MovedCursorUtf8V2); - - std::string utf8_current_text = Common::UTF16ToUTF8(current_text); - - const SwkbdMovedCursorArg moved_cursor_arg{ - .text_length{static_cast(current_text.size())}, - .cursor_position{current_cursor_position}, - }; - - constexpr u8 flag = 0; - - std::memcpy(reply.data() + REPLY_BASE_SIZE, utf8_current_text.data(), utf8_current_text.size()); - std::memcpy(reply.data() + REPLY_BASE_SIZE + REPLY_UTF8_SIZE, &moved_cursor_arg, - sizeof(SwkbdMovedCursorArg)); - std::memcpy(reply.data() + REPLY_BASE_SIZE + REPLY_UTF8_SIZE + sizeof(SwkbdMovedCursorArg), - &flag, 1); - - broker.PushInteractiveDataFromApplet(std::make_shared(system, std::move(reply))); -} - -} // namespace Service::AM::Applets diff --git a/src/core/hle/service/am/applets/software_keyboard.h b/src/core/hle/service/am/applets/software_keyboard.h deleted file mode 100644 index e3fc733d0..000000000 --- a/src/core/hle/service/am/applets/software_keyboard.h +++ /dev/null @@ -1,166 +0,0 @@ -// Copyright 2021 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#pragma once - -#include "common/common_funcs.h" -#include "common/common_types.h" -#include "core/hle/result.h" -#include "core/hle/service/am/applets/applets.h" -#include "core/hle/service/am/applets/software_keyboard_types.h" - -namespace Core { -class System; -} - -namespace Service::AM::Applets { - -class SoftwareKeyboard final : public Applet { -public: - explicit SoftwareKeyboard(Core::System& system_, LibraryAppletMode applet_mode_, - Core::Frontend::SoftwareKeyboardApplet& frontend_); - ~SoftwareKeyboard() override; - - void Initialize() override; - - bool TransactionComplete() const override; - ResultCode GetStatus() const override; - void ExecuteInteractive() override; - void Execute() override; - - /** - * Submits the input text to the application. - * If text checking is enabled, the application will verify the input text. - * If use_utf8 is enabled, the input text will be converted to UTF-8 prior to being submitted. - * This should only be used by the normal software keyboard. - * - * @param result SwkbdResult enum - * @param submitted_text UTF-16 encoded string - */ - void SubmitTextNormal(SwkbdResult result, std::u16string submitted_text); - - /** - * Submits the input text to the application. - * If utf8_mode is enabled, the input text will be converted to UTF-8 prior to being submitted. - * This should only be used by the inline software keyboard. - * - * @param reply_type SwkbdReplyType enum - * @param submitted_text UTF-16 encoded string - * @param cursor_position The current position of the text cursor - */ - void SubmitTextInline(SwkbdReplyType reply_type, std::u16string submitted_text, - s32 cursor_position); - -private: - /// Initializes the normal software keyboard. - void InitializeForeground(); - - /// Initializes the inline software keyboard. - void InitializeBackground(LibraryAppletMode library_applet_mode); - - /// Processes the text check sent by the application. - void ProcessTextCheck(); - - /// Processes the inline software keyboard request command sent by the application. - void ProcessInlineKeyboardRequest(); - - /// Submits the input text and exits the applet. - void SubmitNormalOutputAndExit(SwkbdResult result, std::u16string submitted_text); - - /// Submits the input text for text checking. - void SubmitForTextCheck(std::u16string submitted_text); - - /// Sends a reply to the application after processing a request command. - void SendReply(SwkbdReplyType reply_type); - - /// Changes the inline keyboard state. - void ChangeState(SwkbdState state); - - /** - * Signals the frontend to initialize the software keyboard with common parameters. - * This initializes either the normal software keyboard or the inline software keyboard - * depending on the state of is_background. - * Note that this does not cause the keyboard to appear. - * Use the respective Show*Keyboard() functions to cause the respective keyboards to appear. - */ - void InitializeFrontendKeyboard(); - - /// Signals the frontend to show the normal software keyboard. - void ShowNormalKeyboard(); - - /// Signals the frontend to show the text check dialog. - void ShowTextCheckDialog(SwkbdTextCheckResult text_check_result, - std::u16string text_check_message); - - /// Signals the frontend to show the inline software keyboard. - void ShowInlineKeyboard(); - - /// Signals the frontend to hide the inline software keyboard. - void HideInlineKeyboard(); - - /// Signals the frontend that the current inline keyboard text has changed. - void InlineTextChanged(); - - /// Signals both the frontend and application that the software keyboard is exiting. - void ExitKeyboard(); - - // Inline Software Keyboard Requests - - void RequestFinalize(const std::vector& request_data); - void RequestSetUserWordInfo(const std::vector& request_data); - void RequestSetCustomizeDic(const std::vector& request_data); - void RequestCalc(const std::vector& request_data); - void RequestSetCustomizedDictionaries(const std::vector& request_data); - void RequestUnsetCustomizedDictionaries(const std::vector& request_data); - void RequestSetChangedStringV2Flag(const std::vector& request_data); - void RequestSetMovedCursorV2Flag(const std::vector& request_data); - - // Inline Software Keyboard Replies - - void ReplyFinishedInitialize(); - void ReplyDefault(); - void ReplyChangedString(); - void ReplyMovedCursor(); - void ReplyMovedTab(); - void ReplyDecidedEnter(); - void ReplyDecidedCancel(); - void ReplyChangedStringUtf8(); - void ReplyMovedCursorUtf8(); - void ReplyDecidedEnterUtf8(); - void ReplyUnsetCustomizeDic(); - void ReplyReleasedUserWordInfo(); - void ReplyUnsetCustomizedDictionaries(); - void ReplyChangedStringV2(); - void ReplyMovedCursorV2(); - void ReplyChangedStringUtf8V2(); - void ReplyMovedCursorUtf8V2(); - - Core::Frontend::SoftwareKeyboardApplet& frontend; - Core::System& system; - - SwkbdAppletVersion swkbd_applet_version; - - SwkbdConfigCommon swkbd_config_common; - SwkbdConfigOld swkbd_config_old; - SwkbdConfigOld2 swkbd_config_old2; - SwkbdConfigNew swkbd_config_new; - std::u16string initial_text; - - SwkbdState swkbd_state{SwkbdState::NotInitialized}; - SwkbdInitializeArg swkbd_initialize_arg; - SwkbdCalcArg swkbd_calc_arg; - bool use_changed_string_v2{false}; - bool use_moved_cursor_v2{false}; - bool inline_use_utf8{false}; - s32 current_cursor_position{}; - - std::u16string current_text; - - bool is_background{false}; - - bool complete{false}; - ResultCode status{ResultSuccess}; -}; - -} // namespace Service::AM::Applets diff --git a/src/core/hle/service/am/applets/software_keyboard_types.h b/src/core/hle/service/am/applets/software_keyboard_types.h deleted file mode 100644 index 21aa8e800..000000000 --- a/src/core/hle/service/am/applets/software_keyboard_types.h +++ /dev/null @@ -1,295 +0,0 @@ -// Copyright 2021 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#pragma once - -#include - -#include "common/bit_field.h" -#include "common/common_funcs.h" -#include "common/common_types.h" -#include "common/swap.h" - -namespace Service::AM::Applets { - -constexpr std::size_t MAX_OK_TEXT_LENGTH = 8; -constexpr std::size_t MAX_HEADER_TEXT_LENGTH = 64; -constexpr std::size_t MAX_SUB_TEXT_LENGTH = 128; -constexpr std::size_t MAX_GUIDE_TEXT_LENGTH = 256; -constexpr std::size_t STRING_BUFFER_SIZE = 0x7D4; - -enum class SwkbdAppletVersion : u32_le { - Version5 = 0x5, // 1.0.0 - Version65542 = 0x10006, // 2.0.0 - 2.3.0 - Version196615 = 0x30007, // 3.0.0 - 3.0.2 - Version262152 = 0x40008, // 4.0.0 - 4.1.0 - Version327689 = 0x50009, // 5.0.0 - 5.1.0 - Version393227 = 0x6000B, // 6.0.0 - 7.0.1 - Version524301 = 0x8000D, // 8.0.0+ -}; - -enum class SwkbdType : u32 { - Normal, - NumberPad, - Qwerty, - Unknown3, - Latin, - SimplifiedChinese, - TraditionalChinese, - Korean, -}; - -enum class SwkbdInitialCursorPosition : u32 { - Start, - End, -}; - -enum class SwkbdPasswordMode : u32 { - Disabled, - Enabled, -}; - -enum class SwkbdTextDrawType : u32 { - Line, - Box, - DownloadCode, -}; - -enum class SwkbdResult : u32 { - Ok, - Cancel, -}; - -enum class SwkbdTextCheckResult : u32 { - Success, - Failure, - Confirm, - Silent, -}; - -enum class SwkbdState : u32 { - NotInitialized = 0x0, - InitializedIsHidden = 0x1, - InitializedIsAppearing = 0x2, - InitializedIsShown = 0x3, - InitializedIsDisappearing = 0x4, -}; - -enum class SwkbdRequestCommand : u32 { - Finalize = 0x4, - SetUserWordInfo = 0x6, - SetCustomizeDic = 0x7, - Calc = 0xA, - SetCustomizedDictionaries = 0xB, - UnsetCustomizedDictionaries = 0xC, - SetChangedStringV2Flag = 0xD, - SetMovedCursorV2Flag = 0xE, -}; - -enum class SwkbdReplyType : u32 { - FinishedInitialize = 0x0, - Default = 0x1, - ChangedString = 0x2, - MovedCursor = 0x3, - MovedTab = 0x4, - DecidedEnter = 0x5, - DecidedCancel = 0x6, - ChangedStringUtf8 = 0x7, - MovedCursorUtf8 = 0x8, - DecidedEnterUtf8 = 0x9, - UnsetCustomizeDic = 0xA, - ReleasedUserWordInfo = 0xB, - UnsetCustomizedDictionaries = 0xC, - ChangedStringV2 = 0xD, - MovedCursorV2 = 0xE, - ChangedStringUtf8V2 = 0xF, - MovedCursorUtf8V2 = 0x10, -}; - -struct SwkbdKeyDisableFlags { - union { - u32 raw{}; - - BitField<1, 1, u32> space; - BitField<2, 1, u32> at; - BitField<3, 1, u32> percent; - BitField<4, 1, u32> slash; - BitField<5, 1, u32> backslash; - BitField<6, 1, u32> numbers; - BitField<7, 1, u32> download_code; - BitField<8, 1, u32> username; - }; -}; -static_assert(sizeof(SwkbdKeyDisableFlags) == 0x4, "SwkbdKeyDisableFlags has incorrect size."); - -struct SwkbdConfigCommon { - SwkbdType type{}; - std::array ok_text{}; - char16_t left_optional_symbol_key{}; - char16_t right_optional_symbol_key{}; - bool use_prediction{}; - INSERT_PADDING_BYTES(1); - SwkbdKeyDisableFlags key_disable_flags{}; - SwkbdInitialCursorPosition initial_cursor_position{}; - std::array header_text{}; - std::array sub_text{}; - std::array guide_text{}; - u32 max_text_length{}; - u32 min_text_length{}; - SwkbdPasswordMode password_mode{}; - SwkbdTextDrawType text_draw_type{}; - bool enable_return_button{}; - bool use_utf8{}; - bool use_blur_background{}; - INSERT_PADDING_BYTES(1); - u32 initial_string_offset{}; - u32 initial_string_length{}; - u32 user_dictionary_offset{}; - u32 user_dictionary_entries{}; - bool use_text_check{}; - INSERT_PADDING_BYTES(3); -}; -static_assert(sizeof(SwkbdConfigCommon) == 0x3D4, "SwkbdConfigCommon has incorrect size."); - -#pragma pack(push, 4) -// SwkbdAppletVersion 0x5, 0x10006 -struct SwkbdConfigOld { - INSERT_PADDING_WORDS(1); - VAddr text_check_callback{}; -}; -static_assert(sizeof(SwkbdConfigOld) == 0x3E0 - sizeof(SwkbdConfigCommon), - "SwkbdConfigOld has incorrect size."); - -// SwkbdAppletVersion 0x30007, 0x40008, 0x50009 -struct SwkbdConfigOld2 { - INSERT_PADDING_WORDS(1); - VAddr text_check_callback{}; - std::array text_grouping{}; -}; -static_assert(sizeof(SwkbdConfigOld2) == 0x400 - sizeof(SwkbdConfigCommon), - "SwkbdConfigOld2 has incorrect size."); - -// SwkbdAppletVersion 0x6000B, 0x8000D -struct SwkbdConfigNew { - std::array text_grouping{}; - std::array customized_dictionary_set_entries{}; - u8 total_customized_dictionary_set_entries{}; - bool disable_cancel_button{}; - INSERT_PADDING_BYTES(18); -}; -static_assert(sizeof(SwkbdConfigNew) == 0x4C8 - sizeof(SwkbdConfigCommon), - "SwkbdConfigNew has incorrect size."); -#pragma pack(pop) - -struct SwkbdTextCheck { - SwkbdTextCheckResult text_check_result{}; - std::array text_check_message{}; -}; -static_assert(sizeof(SwkbdTextCheck) == 0x7D8, "SwkbdTextCheck has incorrect size."); - -struct SwkbdCalcArgFlags { - union { - u64 raw{}; - - BitField<0, 1, u64> set_initialize_arg; - BitField<1, 1, u64> set_volume; - BitField<2, 1, u64> appear; - BitField<3, 1, u64> set_input_text; - BitField<4, 1, u64> set_cursor_position; - BitField<5, 1, u64> set_utf8_mode; - BitField<6, 1, u64> unset_customize_dic; - BitField<7, 1, u64> disappear; - BitField<8, 1, u64> unknown; - BitField<9, 1, u64> set_key_top_translate_scale; - BitField<10, 1, u64> unset_user_word_info; - BitField<11, 1, u64> set_disable_hardware_keyboard; - }; -}; -static_assert(sizeof(SwkbdCalcArgFlags) == 0x8, "SwkbdCalcArgFlags has incorrect size."); - -struct SwkbdInitializeArg { - u32 unknown{}; - bool library_applet_mode_flag{}; - bool is_above_hos_500{}; - INSERT_PADDING_BYTES(2); -}; -static_assert(sizeof(SwkbdInitializeArg) == 0x8, "SwkbdInitializeArg has incorrect size."); - -struct SwkbdAppearArg { - SwkbdType type{}; - std::array ok_text{}; - char16_t left_optional_symbol_key{}; - char16_t right_optional_symbol_key{}; - bool use_prediction{}; - bool disable_cancel_button{}; - SwkbdKeyDisableFlags key_disable_flags{}; - u32 max_text_length{}; - u32 min_text_length{}; - bool enable_return_button{}; - INSERT_PADDING_BYTES(3); - u32 flags{}; - INSERT_PADDING_WORDS(6); -}; -static_assert(sizeof(SwkbdAppearArg) == 0x48, "SwkbdAppearArg has incorrect size."); - -struct SwkbdCalcArg { - u32 unknown{}; - u16 calc_arg_size{}; - INSERT_PADDING_BYTES(2); - SwkbdCalcArgFlags flags{}; - SwkbdInitializeArg initialize_arg{}; - f32 volume{}; - s32 cursor_position{}; - SwkbdAppearArg appear_arg{}; - std::array input_text{}; - bool utf8_mode{}; - INSERT_PADDING_BYTES(1); - bool enable_backspace_button{}; - INSERT_PADDING_BYTES(3); - bool key_top_as_floating{}; - bool footer_scalable{}; - bool alpha_enabled_in_input_mode{}; - u8 input_mode_fade_type{}; - bool disable_touch{}; - bool disable_hardware_keyboard{}; - INSERT_PADDING_BYTES(8); - f32 key_top_scale_x{}; - f32 key_top_scale_y{}; - f32 key_top_translate_x{}; - f32 key_top_translate_y{}; - f32 key_top_bg_alpha{}; - f32 footer_bg_alpha{}; - f32 balloon_scale{}; - INSERT_PADDING_WORDS(4); - u8 se_group{}; - INSERT_PADDING_BYTES(3); -}; -static_assert(sizeof(SwkbdCalcArg) == 0x4A0, "SwkbdCalcArg has incorrect size."); - -struct SwkbdChangedStringArg { - u32 text_length{}; - s32 dictionary_start_cursor_position{}; - s32 dictionary_end_cursor_position{}; - s32 cursor_position{}; -}; -static_assert(sizeof(SwkbdChangedStringArg) == 0x10, "SwkbdChangedStringArg has incorrect size."); - -struct SwkbdMovedCursorArg { - u32 text_length{}; - s32 cursor_position{}; -}; -static_assert(sizeof(SwkbdMovedCursorArg) == 0x8, "SwkbdMovedCursorArg has incorrect size."); - -struct SwkbdMovedTabArg { - u32 text_length{}; - s32 cursor_position{}; -}; -static_assert(sizeof(SwkbdMovedTabArg) == 0x8, "SwkbdMovedTabArg has incorrect size."); - -struct SwkbdDecidedEnterArg { - u32 text_length{}; -}; -static_assert(sizeof(SwkbdDecidedEnterArg) == 0x4, "SwkbdDecidedEnterArg has incorrect size."); - -} // namespace Service::AM::Applets diff --git a/src/core/hle/service/am/applets/web_browser.cpp b/src/core/hle/service/am/applets/web_browser.cpp deleted file mode 100644 index 3b28e829b..000000000 --- a/src/core/hle/service/am/applets/web_browser.cpp +++ /dev/null @@ -1,474 +0,0 @@ -// Copyright 2020 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#include "common/assert.h" -#include "common/fs/file.h" -#include "common/fs/fs.h" -#include "common/fs/path_util.h" -#include "common/logging/log.h" -#include "common/string_util.h" -#include "core/core.h" -#include "core/file_sys/content_archive.h" -#include "core/file_sys/mode.h" -#include "core/file_sys/nca_metadata.h" -#include "core/file_sys/patch_manager.h" -#include "core/file_sys/registered_cache.h" -#include "core/file_sys/romfs.h" -#include "core/file_sys/system_archive/system_archive.h" -#include "core/file_sys/vfs_vector.h" -#include "core/frontend/applets/web_browser.h" -#include "core/hle/kernel/k_process.h" -#include "core/hle/result.h" -#include "core/hle/service/am/am.h" -#include "core/hle/service/am/applets/web_browser.h" -#include "core/hle/service/filesystem/filesystem.h" -#include "core/hle/service/ns/pl_u.h" - -namespace Service::AM::Applets { - -namespace { - -template -void ParseRawValue(T& value, const std::vector& data) { - static_assert(std::is_trivially_copyable_v, - "It's undefined behavior to use memcpy with non-trivially copyable objects"); - std::memcpy(&value, data.data(), data.size()); -} - -template -T ParseRawValue(const std::vector& data) { - T value; - ParseRawValue(value, data); - return value; -} - -std::string ParseStringValue(const std::vector& data) { - return Common::StringFromFixedZeroTerminatedBuffer(reinterpret_cast(data.data()), - data.size()); -} - -std::string GetMainURL(const std::string& url) { - const auto index = url.find('?'); - - if (index == std::string::npos) { - return url; - } - - return url.substr(0, index); -} - -WebArgInputTLVMap ReadWebArgs(const std::vector& web_arg, WebArgHeader& web_arg_header) { - std::memcpy(&web_arg_header, web_arg.data(), sizeof(WebArgHeader)); - - if (web_arg.size() == sizeof(WebArgHeader)) { - return {}; - } - - WebArgInputTLVMap input_tlv_map; - - u64 current_offset = sizeof(WebArgHeader); - - for (std::size_t i = 0; i < web_arg_header.total_tlv_entries; ++i) { - if (web_arg.size() < current_offset + sizeof(WebArgInputTLV)) { - return input_tlv_map; - } - - WebArgInputTLV input_tlv; - std::memcpy(&input_tlv, web_arg.data() + current_offset, sizeof(WebArgInputTLV)); - - current_offset += sizeof(WebArgInputTLV); - - if (web_arg.size() < current_offset + input_tlv.arg_data_size) { - return input_tlv_map; - } - - std::vector data(input_tlv.arg_data_size); - std::memcpy(data.data(), web_arg.data() + current_offset, input_tlv.arg_data_size); - - current_offset += input_tlv.arg_data_size; - - input_tlv_map.insert_or_assign(input_tlv.input_tlv_type, std::move(data)); - } - - return input_tlv_map; -} - -FileSys::VirtualFile GetOfflineRomFS(Core::System& system, u64 title_id, - FileSys::ContentRecordType nca_type) { - if (nca_type == FileSys::ContentRecordType::Data) { - const auto nca = - system.GetFileSystemController().GetSystemNANDContents()->GetEntry(title_id, nca_type); - - if (nca == nullptr) { - LOG_ERROR(Service_AM, - "NCA of type={} with title_id={:016X} is not found in the System NAND!", - nca_type, title_id); - return FileSys::SystemArchive::SynthesizeSystemArchive(title_id); - } - - return nca->GetRomFS(); - } else { - const auto nca = system.GetContentProvider().GetEntry(title_id, nca_type); - - if (nca == nullptr) { - LOG_ERROR(Service_AM, - "NCA of type={} with title_id={:016X} is not found in the ContentProvider!", - nca_type, title_id); - return nullptr; - } - - const FileSys::PatchManager pm{title_id, system.GetFileSystemController(), - system.GetContentProvider()}; - - return pm.PatchRomFS(nca->GetRomFS(), nca->GetBaseIVFCOffset(), nca_type); - } -} - -void ExtractSharedFonts(Core::System& system) { - static constexpr std::array DECRYPTED_SHARED_FONTS{ - "FontStandard.ttf", - "FontChineseSimplified.ttf", - "FontExtendedChineseSimplified.ttf", - "FontChineseTraditional.ttf", - "FontKorean.ttf", - "FontNintendoExtended.ttf", - "FontNintendoExtended2.ttf", - }; - - const auto fonts_dir = Common::FS::GetYuzuPath(Common::FS::YuzuPath::CacheDir) / "fonts"; - - for (std::size_t i = 0; i < NS::SHARED_FONTS.size(); ++i) { - const auto font_file_path = fonts_dir / DECRYPTED_SHARED_FONTS[i]; - - if (Common::FS::Exists(font_file_path)) { - continue; - } - - const auto font = NS::SHARED_FONTS[i]; - const auto font_title_id = static_cast(font.first); - - const auto nca = system.GetFileSystemController().GetSystemNANDContents()->GetEntry( - font_title_id, FileSys::ContentRecordType::Data); - - FileSys::VirtualFile romfs; - - if (!nca) { - romfs = FileSys::SystemArchive::SynthesizeSystemArchive(font_title_id); - } else { - romfs = nca->GetRomFS(); - } - - if (!romfs) { - LOG_ERROR(Service_AM, "SharedFont RomFS with title_id={:016X} cannot be extracted!", - font_title_id); - continue; - } - - const auto extracted_romfs = FileSys::ExtractRomFS(romfs); - - if (!extracted_romfs) { - LOG_ERROR(Service_AM, "SharedFont RomFS with title_id={:016X} failed to extract!", - font_title_id); - continue; - } - - const auto font_file = extracted_romfs->GetFile(font.second); - - if (!font_file) { - LOG_ERROR(Service_AM, "SharedFont RomFS with title_id={:016X} has no font file \"{}\"!", - font_title_id, font.second); - continue; - } - - std::vector font_data_u32(font_file->GetSize() / sizeof(u32)); - font_file->ReadBytes(font_data_u32.data(), font_file->GetSize()); - - std::transform(font_data_u32.begin(), font_data_u32.end(), font_data_u32.begin(), - Common::swap32); - - std::vector decrypted_data(font_file->GetSize() - 8); - - NS::DecryptSharedFontToTTF(font_data_u32, decrypted_data); - - FileSys::VirtualFile decrypted_font = std::make_shared( - std::move(decrypted_data), DECRYPTED_SHARED_FONTS[i]); - - const auto temp_dir = system.GetFilesystem()->CreateDirectory( - Common::FS::PathToUTF8String(fonts_dir), FileSys::Mode::ReadWrite); - - const auto out_file = temp_dir->CreateFile(DECRYPTED_SHARED_FONTS[i]); - - FileSys::VfsRawCopy(decrypted_font, out_file); - } -} - -} // namespace - -WebBrowser::WebBrowser(Core::System& system_, LibraryAppletMode applet_mode_, - const Core::Frontend::WebBrowserApplet& frontend_) - : Applet{system_, applet_mode_}, frontend(frontend_), system{system_} {} - -WebBrowser::~WebBrowser() = default; - -void WebBrowser::Initialize() { - Applet::Initialize(); - - LOG_INFO(Service_AM, "Initializing Web Browser Applet."); - - LOG_DEBUG(Service_AM, - "Initializing Applet with common_args: arg_version={}, lib_version={}, " - "play_startup_sound={}, size={}, system_tick={}, theme_color={}", - common_args.arguments_version, common_args.library_version, - common_args.play_startup_sound, common_args.size, common_args.system_tick, - common_args.theme_color); - - web_applet_version = WebAppletVersion{common_args.library_version}; - - const auto web_arg_storage = broker.PopNormalDataToApplet(); - ASSERT(web_arg_storage != nullptr); - - const auto& web_arg = web_arg_storage->GetData(); - ASSERT_OR_EXECUTE(web_arg.size() >= sizeof(WebArgHeader), { return; }); - - web_arg_input_tlv_map = ReadWebArgs(web_arg, web_arg_header); - - LOG_DEBUG(Service_AM, "WebArgHeader: total_tlv_entries={}, shim_kind={}", - web_arg_header.total_tlv_entries, web_arg_header.shim_kind); - - ExtractSharedFonts(system); - - switch (web_arg_header.shim_kind) { - case ShimKind::Shop: - InitializeShop(); - break; - case ShimKind::Login: - InitializeLogin(); - break; - case ShimKind::Offline: - InitializeOffline(); - break; - case ShimKind::Share: - InitializeShare(); - break; - case ShimKind::Web: - InitializeWeb(); - break; - case ShimKind::Wifi: - InitializeWifi(); - break; - case ShimKind::Lobby: - InitializeLobby(); - break; - default: - UNREACHABLE_MSG("Invalid ShimKind={}", web_arg_header.shim_kind); - break; - } -} - -bool WebBrowser::TransactionComplete() const { - return complete; -} - -ResultCode WebBrowser::GetStatus() const { - return status; -} - -void WebBrowser::ExecuteInteractive() { - UNIMPLEMENTED_MSG("WebSession is not implemented"); -} - -void WebBrowser::Execute() { - switch (web_arg_header.shim_kind) { - case ShimKind::Shop: - ExecuteShop(); - break; - case ShimKind::Login: - ExecuteLogin(); - break; - case ShimKind::Offline: - ExecuteOffline(); - break; - case ShimKind::Share: - ExecuteShare(); - break; - case ShimKind::Web: - ExecuteWeb(); - break; - case ShimKind::Wifi: - ExecuteWifi(); - break; - case ShimKind::Lobby: - ExecuteLobby(); - break; - default: - UNREACHABLE_MSG("Invalid ShimKind={}", web_arg_header.shim_kind); - WebBrowserExit(WebExitReason::EndButtonPressed); - break; - } -} - -void WebBrowser::ExtractOfflineRomFS() { - LOG_DEBUG(Service_AM, "Extracting RomFS to {}", - Common::FS::PathToUTF8String(offline_cache_dir)); - - const auto extracted_romfs_dir = - FileSys::ExtractRomFS(offline_romfs, FileSys::RomFSExtractionType::SingleDiscard); - - const auto temp_dir = system.GetFilesystem()->CreateDirectory( - Common::FS::PathToUTF8String(offline_cache_dir), FileSys::Mode::ReadWrite); - - FileSys::VfsRawCopyD(extracted_romfs_dir, temp_dir); -} - -void WebBrowser::WebBrowserExit(WebExitReason exit_reason, std::string last_url) { - if ((web_arg_header.shim_kind == ShimKind::Share && - web_applet_version >= WebAppletVersion::Version196608) || - (web_arg_header.shim_kind == ShimKind::Web && - web_applet_version >= WebAppletVersion::Version524288)) { - // TODO: Push Output TLVs instead of a WebCommonReturnValue - } - - WebCommonReturnValue web_common_return_value; - - web_common_return_value.exit_reason = exit_reason; - std::memcpy(&web_common_return_value.last_url, last_url.data(), last_url.size()); - web_common_return_value.last_url_size = last_url.size(); - - LOG_DEBUG(Service_AM, "WebCommonReturnValue: exit_reason={}, last_url={}, last_url_size={}", - exit_reason, last_url, last_url.size()); - - complete = true; - std::vector out_data(sizeof(WebCommonReturnValue)); - std::memcpy(out_data.data(), &web_common_return_value, out_data.size()); - broker.PushNormalDataFromApplet(std::make_shared(system, std::move(out_data))); - broker.SignalStateChanged(); -} - -bool WebBrowser::InputTLVExistsInMap(WebArgInputTLVType input_tlv_type) const { - return web_arg_input_tlv_map.find(input_tlv_type) != web_arg_input_tlv_map.end(); -} - -std::optional> WebBrowser::GetInputTLVData(WebArgInputTLVType input_tlv_type) { - const auto map_it = web_arg_input_tlv_map.find(input_tlv_type); - - if (map_it == web_arg_input_tlv_map.end()) { - return std::nullopt; - } - - return map_it->second; -} - -void WebBrowser::InitializeShop() {} - -void WebBrowser::InitializeLogin() {} - -void WebBrowser::InitializeOffline() { - const auto document_path = - ParseStringValue(GetInputTLVData(WebArgInputTLVType::DocumentPath).value()); - - const auto document_kind = - ParseRawValue(GetInputTLVData(WebArgInputTLVType::DocumentKind).value()); - - std::string additional_paths; - - switch (document_kind) { - case DocumentKind::OfflineHtmlPage: - default: - title_id = system.CurrentProcess()->GetTitleID(); - nca_type = FileSys::ContentRecordType::HtmlDocument; - additional_paths = "html-document"; - break; - case DocumentKind::ApplicationLegalInformation: - title_id = ParseRawValue(GetInputTLVData(WebArgInputTLVType::ApplicationID).value()); - nca_type = FileSys::ContentRecordType::LegalInformation; - break; - case DocumentKind::SystemDataPage: - title_id = ParseRawValue(GetInputTLVData(WebArgInputTLVType::SystemDataID).value()); - nca_type = FileSys::ContentRecordType::Data; - break; - } - - static constexpr std::array RESOURCE_TYPES{ - "manual", - "legal_information", - "system_data", - }; - - offline_cache_dir = Common::FS::GetYuzuPath(Common::FS::YuzuPath::CacheDir) / - fmt::format("offline_web_applet_{}/{:016X}", - RESOURCE_TYPES[static_cast(document_kind) - 1], title_id); - - offline_document = Common::FS::ConcatPathSafe( - offline_cache_dir, fmt::format("{}/{}", additional_paths, document_path)); -} - -void WebBrowser::InitializeShare() {} - -void WebBrowser::InitializeWeb() { - external_url = ParseStringValue(GetInputTLVData(WebArgInputTLVType::InitialURL).value()); -} - -void WebBrowser::InitializeWifi() {} - -void WebBrowser::InitializeLobby() {} - -void WebBrowser::ExecuteShop() { - LOG_WARNING(Service_AM, "(STUBBED) called, Shop Applet is not implemented"); - WebBrowserExit(WebExitReason::EndButtonPressed); -} - -void WebBrowser::ExecuteLogin() { - LOG_WARNING(Service_AM, "(STUBBED) called, Login Applet is not implemented"); - WebBrowserExit(WebExitReason::EndButtonPressed); -} - -void WebBrowser::ExecuteOffline() { - const auto main_url = GetMainURL(Common::FS::PathToUTF8String(offline_document)); - - if (!Common::FS::Exists(main_url)) { - offline_romfs = GetOfflineRomFS(system, title_id, nca_type); - - if (offline_romfs == nullptr) { - LOG_ERROR(Service_AM, - "RomFS with title_id={:016X} and nca_type={} cannot be extracted!", title_id, - nca_type); - WebBrowserExit(WebExitReason::WindowClosed); - return; - } - } - - LOG_INFO(Service_AM, "Opening offline document at {}", - Common::FS::PathToUTF8String(offline_document)); - - frontend.OpenLocalWebPage( - Common::FS::PathToUTF8String(offline_document), [this] { ExtractOfflineRomFS(); }, - [this](WebExitReason exit_reason, std::string last_url) { - WebBrowserExit(exit_reason, last_url); - }); -} - -void WebBrowser::ExecuteShare() { - LOG_WARNING(Service_AM, "(STUBBED) called, Share Applet is not implemented"); - WebBrowserExit(WebExitReason::EndButtonPressed); -} - -void WebBrowser::ExecuteWeb() { - LOG_INFO(Service_AM, "Opening external URL at {}", external_url); - - frontend.OpenExternalWebPage(external_url, - [this](WebExitReason exit_reason, std::string last_url) { - WebBrowserExit(exit_reason, last_url); - }); -} - -void WebBrowser::ExecuteWifi() { - LOG_WARNING(Service_AM, "(STUBBED) called, Wifi Applet is not implemented"); - WebBrowserExit(WebExitReason::EndButtonPressed); -} - -void WebBrowser::ExecuteLobby() { - LOG_WARNING(Service_AM, "(STUBBED) called, Lobby Applet is not implemented"); - WebBrowserExit(WebExitReason::EndButtonPressed); -} -} // namespace Service::AM::Applets diff --git a/src/core/hle/service/am/applets/web_browser.h b/src/core/hle/service/am/applets/web_browser.h deleted file mode 100644 index 9f81214b6..000000000 --- a/src/core/hle/service/am/applets/web_browser.h +++ /dev/null @@ -1,88 +0,0 @@ -// Copyright 2020 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#pragma once - -#include -#include - -#include "common/common_funcs.h" -#include "common/common_types.h" -#include "core/file_sys/vfs_types.h" -#include "core/hle/result.h" -#include "core/hle/service/am/applets/applets.h" -#include "core/hle/service/am/applets/web_types.h" - -namespace Core { -class System; -} - -namespace FileSys { -enum class ContentRecordType : u8; -} - -namespace Service::AM::Applets { - -class WebBrowser final : public Applet { -public: - WebBrowser(Core::System& system_, LibraryAppletMode applet_mode_, - const Core::Frontend::WebBrowserApplet& frontend_); - - ~WebBrowser() override; - - void Initialize() override; - - bool TransactionComplete() const override; - ResultCode GetStatus() const override; - void ExecuteInteractive() override; - void Execute() override; - - void ExtractOfflineRomFS(); - - void WebBrowserExit(WebExitReason exit_reason, std::string last_url = ""); - -private: - bool InputTLVExistsInMap(WebArgInputTLVType input_tlv_type) const; - - std::optional> GetInputTLVData(WebArgInputTLVType input_tlv_type); - - // Initializers for the various types of browser applets - void InitializeShop(); - void InitializeLogin(); - void InitializeOffline(); - void InitializeShare(); - void InitializeWeb(); - void InitializeWifi(); - void InitializeLobby(); - - // Executors for the various types of browser applets - void ExecuteShop(); - void ExecuteLogin(); - void ExecuteOffline(); - void ExecuteShare(); - void ExecuteWeb(); - void ExecuteWifi(); - void ExecuteLobby(); - - const Core::Frontend::WebBrowserApplet& frontend; - - bool complete{false}; - ResultCode status{ResultSuccess}; - - WebAppletVersion web_applet_version{}; - WebArgHeader web_arg_header{}; - WebArgInputTLVMap web_arg_input_tlv_map; - - u64 title_id{}; - FileSys::ContentRecordType nca_type{}; - std::filesystem::path offline_cache_dir; - std::filesystem::path offline_document; - FileSys::VirtualFile offline_romfs; - - std::string external_url; - - Core::System& system; -}; - -} // namespace Service::AM::Applets diff --git a/src/core/hle/service/am/applets/web_types.h b/src/core/hle/service/am/applets/web_types.h deleted file mode 100644 index 419c2bf79..000000000 --- a/src/core/hle/service/am/applets/web_types.h +++ /dev/null @@ -1,178 +0,0 @@ -// Copyright 2020 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#pragma once - -#include -#include -#include - -#include "common/common_funcs.h" -#include "common/common_types.h" -#include "common/swap.h" - -namespace Service::AM::Applets { - -enum class WebAppletVersion : u32_le { - Version0 = 0x0, // Only used by WifiWebAuthApplet - Version131072 = 0x20000, // 1.0.0 - 2.3.0 - Version196608 = 0x30000, // 3.0.0 - 4.1.0 - Version327680 = 0x50000, // 5.0.0 - 5.1.0 - Version393216 = 0x60000, // 6.0.0 - 7.0.1 - Version524288 = 0x80000, // 8.0.0+ -}; - -enum class ShimKind : u32 { - Shop = 1, - Login = 2, - Offline = 3, - Share = 4, - Web = 5, - Wifi = 6, - Lobby = 7, -}; - -enum class WebExitReason : u32 { - EndButtonPressed = 0, - BackButtonPressed = 1, - ExitRequested = 2, - CallbackURL = 3, - WindowClosed = 4, - ErrorDialog = 7, -}; - -enum class WebArgInputTLVType : u16 { - InitialURL = 0x1, - CallbackURL = 0x3, - CallbackableURL = 0x4, - ApplicationID = 0x5, - DocumentPath = 0x6, - DocumentKind = 0x7, - SystemDataID = 0x8, - ShareStartPage = 0x9, - Whitelist = 0xA, - News = 0xB, - UserID = 0xE, - AlbumEntry0 = 0xF, - ScreenShotEnabled = 0x10, - EcClientCertEnabled = 0x11, - PlayReportEnabled = 0x13, - BootDisplayKind = 0x17, - BackgroundKind = 0x18, - FooterEnabled = 0x19, - PointerEnabled = 0x1A, - LeftStickMode = 0x1B, - KeyRepeatFrame1 = 0x1C, - KeyRepeatFrame2 = 0x1D, - BootAsMediaPlayerInverted = 0x1E, - DisplayURLKind = 0x1F, - BootAsMediaPlayer = 0x21, - ShopJumpEnabled = 0x22, - MediaAutoPlayEnabled = 0x23, - LobbyParameter = 0x24, - ApplicationAlbumEntry = 0x26, - JsExtensionEnabled = 0x27, - AdditionalCommentText = 0x28, - TouchEnabledOnContents = 0x29, - UserAgentAdditionalString = 0x2A, - AdditionalMediaData0 = 0x2B, - MediaPlayerAutoCloseEnabled = 0x2C, - PageCacheEnabled = 0x2D, - WebAudioEnabled = 0x2E, - YouTubeVideoWhitelist = 0x31, - FooterFixedKind = 0x32, - PageFadeEnabled = 0x33, - MediaCreatorApplicationRatingAge = 0x34, - BootLoadingIconEnabled = 0x35, - PageScrollIndicatorEnabled = 0x36, - MediaPlayerSpeedControlEnabled = 0x37, - AlbumEntry1 = 0x38, - AlbumEntry2 = 0x39, - AlbumEntry3 = 0x3A, - AdditionalMediaData1 = 0x3B, - AdditionalMediaData2 = 0x3C, - AdditionalMediaData3 = 0x3D, - BootFooterButton = 0x3E, - OverrideWebAudioVolume = 0x3F, - OverrideMediaAudioVolume = 0x40, - BootMode = 0x41, - WebSessionEnabled = 0x42, - MediaPlayerOfflineEnabled = 0x43, -}; - -enum class WebArgOutputTLVType : u16 { - ShareExitReason = 0x1, - LastURL = 0x2, - LastURLSize = 0x3, - SharePostResult = 0x4, - PostServiceName = 0x5, - PostServiceNameSize = 0x6, - PostID = 0x7, - PostIDSize = 0x8, - MediaPlayerAutoClosedByCompletion = 0x9, -}; - -enum class DocumentKind : u32 { - OfflineHtmlPage = 1, - ApplicationLegalInformation = 2, - SystemDataPage = 3, -}; - -enum class ShareStartPage : u32 { - Default, - Settings, -}; - -enum class BootDisplayKind : u32 { - Default, - White, - Black, -}; - -enum class BackgroundKind : u32 { - Default, -}; - -enum class LeftStickMode : u32 { - Pointer, - Cursor, -}; - -enum class WebSessionBootMode : u32 { - AllForeground, - AllForegroundInitiallyHidden, -}; - -struct WebArgHeader { - u16 total_tlv_entries{}; - INSERT_PADDING_BYTES(2); - ShimKind shim_kind{}; -}; -static_assert(sizeof(WebArgHeader) == 0x8, "WebArgHeader has incorrect size."); - -struct WebArgInputTLV { - WebArgInputTLVType input_tlv_type{}; - u16 arg_data_size{}; - INSERT_PADDING_WORDS(1); -}; -static_assert(sizeof(WebArgInputTLV) == 0x8, "WebArgInputTLV has incorrect size."); - -struct WebArgOutputTLV { - WebArgOutputTLVType output_tlv_type{}; - u16 arg_data_size{}; - INSERT_PADDING_WORDS(1); -}; -static_assert(sizeof(WebArgOutputTLV) == 0x8, "WebArgOutputTLV has incorrect size."); - -struct WebCommonReturnValue { - WebExitReason exit_reason{}; - INSERT_PADDING_WORDS(1); - std::array last_url{}; - u64 last_url_size{}; -}; -static_assert(sizeof(WebCommonReturnValue) == 0x1010, "WebCommonReturnValue has incorrect size."); - -using WebArgInputTLVMap = std::unordered_map>; - -} // namespace Service::AM::Applets -- cgit v1.2.3