diff options
Diffstat (limited to 'src/core/hle/service/am/applet_manager.cpp')
-rw-r--r-- | src/core/hle/service/am/applet_manager.cpp | 361 |
1 files changed, 361 insertions, 0 deletions
diff --git a/src/core/hle/service/am/applet_manager.cpp b/src/core/hle/service/am/applet_manager.cpp new file mode 100644 index 000000000..52200d5b2 --- /dev/null +++ b/src/core/hle/service/am/applet_manager.cpp @@ -0,0 +1,361 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "common/settings.h" +#include "common/uuid.h" +#include "core/core.h" +#include "core/core_timing.h" +#include "core/hle/service/acc/profile_manager.h" +#include "core/hle/service/am/applet_data_broker.h" +#include "core/hle/service/am/applet_manager.h" +#include "core/hle/service/am/frontend/applet_cabinet.h" +#include "core/hle/service/am/frontend/applet_controller.h" +#include "core/hle/service/am/frontend/applet_mii_edit_types.h" +#include "core/hle/service/am/frontend/applet_software_keyboard_types.h" +#include "hid_core/hid_types.h" + +namespace Service::AM { + +namespace { + +constexpr u32 LaunchParameterAccountPreselectedUserMagic = 0xC79497CA; + +struct LaunchParameterAccountPreselectedUser { + u32 magic; + u32 is_account_selected; + Common::UUID current_user; + INSERT_PADDING_BYTES(0x70); +}; +static_assert(sizeof(LaunchParameterAccountPreselectedUser) == 0x88); + +AppletStorageChannel& InitializeFakeCallerApplet(Core::System& system, + std::shared_ptr<Applet>& applet) { + applet->caller_applet_broker = std::make_shared<AppletDataBroker>(system); + return applet->caller_applet_broker->GetInData(); +} + +void PushInShowAlbum(Core::System& system, AppletStorageChannel& channel) { + const CommonArguments arguments{ + .arguments_version = CommonArgumentVersion::Version3, + .size = CommonArgumentSize::Version3, + .library_version = 1, + .theme_color = ThemeColor::BasicBlack, + .play_startup_sound = true, + .system_tick = system.CoreTiming().GetClockTicks(), + }; + + std::vector<u8> argument_data(sizeof(arguments)); + std::vector<u8> settings_data{2}; + std::memcpy(argument_data.data(), &arguments, sizeof(arguments)); + channel.Push(std::make_shared<IStorage>(system, std::move(argument_data))); + channel.Push(std::make_shared<IStorage>(system, std::move(settings_data))); +} + +void PushInShowController(Core::System& system, AppletStorageChannel& channel) { + const CommonArguments common_args = { + .arguments_version = CommonArgumentVersion::Version3, + .size = CommonArgumentSize::Version3, + .library_version = static_cast<u32>(Frontend::ControllerAppletVersion::Version8), + .theme_color = ThemeColor::BasicBlack, + .play_startup_sound = true, + .system_tick = system.CoreTiming().GetClockTicks(), + }; + + Frontend::ControllerSupportArgNew user_args = { + .header = {.player_count_min = 1, + .player_count_max = 4, + .enable_take_over_connection = true, + .enable_left_justify = false, + .enable_permit_joy_dual = true, + .enable_single_mode = false, + .enable_identification_color = false}, + .identification_colors = {}, + .enable_explain_text = false, + .explain_text = {}, + }; + + Frontend::ControllerSupportArgPrivate private_args = { + .arg_private_size = sizeof(Frontend::ControllerSupportArgPrivate), + .arg_size = sizeof(Frontend::ControllerSupportArgNew), + .is_home_menu = true, + .flag_1 = true, + .mode = Frontend::ControllerSupportMode::ShowControllerSupport, + .caller = Frontend::ControllerSupportCaller:: + Application, // switchbrew: Always zero except with + // ShowControllerFirmwareUpdateForSystem/ShowControllerKeyRemappingForSystem, + // which sets this to the input param + .style_set = Core::HID::NpadStyleSet::None, + .joy_hold_type = 0, + }; + std::vector<u8> common_args_data(sizeof(common_args)); + std::vector<u8> private_args_data(sizeof(private_args)); + std::vector<u8> user_args_data(sizeof(user_args)); + + std::memcpy(common_args_data.data(), &common_args, sizeof(common_args)); + std::memcpy(private_args_data.data(), &private_args, sizeof(private_args)); + std::memcpy(user_args_data.data(), &user_args, sizeof(user_args)); + + channel.Push(std::make_shared<IStorage>(system, std::move(common_args_data))); + channel.Push(std::make_shared<IStorage>(system, std::move(private_args_data))); + channel.Push(std::make_shared<IStorage>(system, std::move(user_args_data))); +} + +void PushInShowCabinetData(Core::System& system, AppletStorageChannel& channel) { + const CommonArguments arguments{ + .arguments_version = CommonArgumentVersion::Version3, + .size = CommonArgumentSize::Version3, + .library_version = static_cast<u32>(Frontend::CabinetAppletVersion::Version1), + .theme_color = ThemeColor::BasicBlack, + .play_startup_sound = true, + .system_tick = system.CoreTiming().GetClockTicks(), + }; + + const Frontend::StartParamForAmiiboSettings amiibo_settings{ + .param_1 = 0, + .applet_mode = system.GetFrontendAppletHolder().GetCabinetMode(), + .flags = Frontend::CabinetFlags::None, + .amiibo_settings_1 = 0, + .device_handle = 0, + .tag_info{}, + .register_info{}, + .amiibo_settings_3{}, + }; + + std::vector<u8> argument_data(sizeof(arguments)); + std::vector<u8> settings_data(sizeof(amiibo_settings)); + std::memcpy(argument_data.data(), &arguments, sizeof(arguments)); + std::memcpy(settings_data.data(), &amiibo_settings, sizeof(amiibo_settings)); + channel.Push(std::make_shared<IStorage>(system, std::move(argument_data))); + channel.Push(std::make_shared<IStorage>(system, std::move(settings_data))); +} + +void PushInShowMiiEditData(Core::System& system, AppletStorageChannel& channel) { + struct MiiEditV3 { + Frontend::MiiEditAppletInputCommon common; + Frontend::MiiEditAppletInputV3 input; + }; + static_assert(sizeof(MiiEditV3) == 0x100, "MiiEditV3 has incorrect size."); + + MiiEditV3 mii_arguments{ + .common = + { + .version = Frontend::MiiEditAppletVersion::Version3, + .applet_mode = Frontend::MiiEditAppletMode::ShowMiiEdit, + }, + .input{}, + }; + + std::vector<u8> argument_data(sizeof(mii_arguments)); + std::memcpy(argument_data.data(), &mii_arguments, sizeof(mii_arguments)); + + channel.Push(std::make_shared<IStorage>(system, std::move(argument_data))); +} + +void PushInShowSoftwareKeyboard(Core::System& system, AppletStorageChannel& channel) { + const CommonArguments arguments{ + .arguments_version = CommonArgumentVersion::Version3, + .size = CommonArgumentSize::Version3, + .library_version = static_cast<u32>(Frontend::SwkbdAppletVersion::Version524301), + .theme_color = ThemeColor::BasicBlack, + .play_startup_sound = true, + .system_tick = system.CoreTiming().GetClockTicks(), + }; + + std::vector<char16_t> initial_string(0); + + const Frontend::SwkbdConfigCommon swkbd_config{ + .type = Frontend::SwkbdType::Qwerty, + .ok_text{}, + .left_optional_symbol_key{}, + .right_optional_symbol_key{}, + .use_prediction = false, + .key_disable_flags{}, + .initial_cursor_position = Frontend::SwkbdInitialCursorPosition::Start, + .header_text{}, + .sub_text{}, + .guide_text{}, + .max_text_length = 500, + .min_text_length = 0, + .password_mode = Frontend::SwkbdPasswordMode::Disabled, + .text_draw_type = Frontend::SwkbdTextDrawType::Box, + .enable_return_button = true, + .use_utf8 = false, + .use_blur_background = true, + .initial_string_offset{}, + .initial_string_length = static_cast<u32>(initial_string.size()), + .user_dictionary_offset{}, + .user_dictionary_entries{}, + .use_text_check = false, + }; + + Frontend::SwkbdConfigNew swkbd_config_new{}; + + std::vector<u8> argument_data(sizeof(arguments)); + std::vector<u8> swkbd_data(sizeof(swkbd_config) + sizeof(swkbd_config_new)); + std::vector<u8> work_buffer(swkbd_config.initial_string_length * sizeof(char16_t)); + + std::memcpy(argument_data.data(), &arguments, sizeof(arguments)); + std::memcpy(swkbd_data.data(), &swkbd_config, sizeof(swkbd_config)); + std::memcpy(swkbd_data.data() + sizeof(swkbd_config), &swkbd_config_new, + sizeof(Frontend::SwkbdConfigNew)); + std::memcpy(work_buffer.data(), initial_string.data(), + swkbd_config.initial_string_length * sizeof(char16_t)); + + channel.Push(std::make_shared<IStorage>(system, std::move(argument_data))); + channel.Push(std::make_shared<IStorage>(system, std::move(swkbd_data))); + channel.Push(std::make_shared<IStorage>(system, std::move(work_buffer))); +} + +} // namespace + +AppletManager::AppletManager(Core::System& system) : m_system(system) {} +AppletManager::~AppletManager() { + this->Reset(); +} + +void AppletManager::InsertApplet(std::shared_ptr<Applet> applet) { + std::scoped_lock lk{m_lock}; + + m_applets.emplace(applet->aruid, std::move(applet)); +} + +void AppletManager::TerminateAndRemoveApplet(AppletResourceUserId aruid) { + std::shared_ptr<Applet> applet; + bool should_stop = false; + { + std::scoped_lock lk{m_lock}; + + const auto it = m_applets.find(aruid); + if (it == m_applets.end()) { + return; + } + + applet = it->second; + m_applets.erase(it); + + should_stop = m_applets.empty(); + } + + // Terminate process. + applet->process->Terminate(); + + // If there were no applets left, stop emulation. + if (should_stop) { + m_system.Exit(); + } +} + +void AppletManager::CreateAndInsertByFrontendAppletParameters( + AppletResourceUserId aruid, const FrontendAppletParameters& params) { + // TODO: this should be run inside AM so that the events will have a parent process + // TODO: have am create the guest process + auto applet = std::make_shared<Applet>(m_system, std::make_unique<Process>(m_system)); + + applet->aruid = aruid; + applet->program_id = params.program_id; + applet->applet_id = params.applet_id; + applet->type = params.applet_type; + applet->previous_program_index = params.previous_program_index; + + // Push UserChannel data from previous application + if (params.launch_type == LaunchType::ApplicationInitiated) { + applet->user_channel_launch_parameter.swap(m_system.GetUserChannel()); + } + + // TODO: Read whether we need a preselected user from NACP? + // TODO: This can be done quite easily from loader + { + LaunchParameterAccountPreselectedUser lp{}; + + lp.magic = LaunchParameterAccountPreselectedUserMagic; + lp.is_account_selected = 1; + + Account::ProfileManager profile_manager{}; + const auto uuid = profile_manager.GetUser(static_cast<s32>(Settings::values.current_user)); + ASSERT(uuid.has_value() && uuid->IsValid()); + lp.current_user = *uuid; + + std::vector<u8> buffer(sizeof(LaunchParameterAccountPreselectedUser)); + std::memcpy(buffer.data(), &lp, buffer.size()); + + applet->preselected_user_launch_parameter.push_back(std::move(buffer)); + } + + // Starting from frontend, some applets require input data. + switch (applet->applet_id) { + case AppletId::Cabinet: + PushInShowCabinetData(m_system, InitializeFakeCallerApplet(m_system, applet)); + break; + case AppletId::MiiEdit: + PushInShowMiiEditData(m_system, InitializeFakeCallerApplet(m_system, applet)); + break; + case AppletId::PhotoViewer: + PushInShowAlbum(m_system, InitializeFakeCallerApplet(m_system, applet)); + break; + case AppletId::SoftwareKeyboard: + PushInShowSoftwareKeyboard(m_system, InitializeFakeCallerApplet(m_system, applet)); + break; + case AppletId::Controller: + PushInShowController(m_system, InitializeFakeCallerApplet(m_system, applet)); + break; + default: + break; + } + + // Applet was started by frontend, so it is foreground. + applet->message_queue.PushMessage(AppletMessageQueue::AppletMessage::ChangeIntoForeground); + applet->message_queue.PushMessage(AppletMessageQueue::AppletMessage::FocusStateChanged); + applet->focus_state = FocusState::InFocus; + + this->InsertApplet(std::move(applet)); +} + +std::shared_ptr<Applet> AppletManager::GetByAppletResourceUserId(AppletResourceUserId aruid) const { + std::scoped_lock lk{m_lock}; + + if (const auto it = m_applets.find(aruid); it != m_applets.end()) { + return it->second; + } + + return {}; +} + +void AppletManager::Reset() { + std::scoped_lock lk{m_lock}; + + m_applets.clear(); +} + +void AppletManager::RequestExit() { + std::scoped_lock lk{m_lock}; + + for (const auto& [aruid, applet] : m_applets) { + applet->message_queue.RequestExit(); + } +} + +void AppletManager::RequestResume() { + std::scoped_lock lk{m_lock}; + + for (const auto& [aruid, applet] : m_applets) { + applet->message_queue.RequestResume(); + } +} + +void AppletManager::OperationModeChanged() { + std::scoped_lock lk{m_lock}; + + for (const auto& [aruid, applet] : m_applets) { + applet->message_queue.OperationModeChanged(); + } +} + +void AppletManager::FocusStateChanged() { + std::scoped_lock lk{m_lock}; + + for (const auto& [aruid, applet] : m_applets) { + applet->message_queue.FocusStateChanged(); + } +} + +} // namespace Service::AM |