code:
// 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 "core/hle/service/am/service/storage.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 PushInShowQlaunch(Core::System& system, AppletStorageChannel& channel) {
    const CommonArguments arguments{
        .arguments_version = CommonArgumentVersion::Version3,
        .size = CommonArgumentSize::Version3,
        .library_version = 0,
        .theme_color = ThemeColor::BasicBlack,
        .play_startup_sound = true,
        .system_tick = system.CoreTiming().GetClockTicks(),
    };

    std::vector<u8> argument_data(sizeof(arguments));
    std::memcpy(argument_data.data(), &arguments, sizeof(arguments));
    channel.Push(std::make_shared<IStorage>(system, std::move(argument_data)));
}

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::QLaunch:
        PushInShowQlaunch(m_system, InitializeFakeCallerApplet(m_system, applet));
        break;
    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(AppletMessage::ChangeIntoForeground);
    applet->message_queue.PushMessage(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


code:
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#pragma once

#include <map>
#include <mutex>

#include "core/hle/service/am/applet.h"

namespace Core {
class System;
}

namespace Service::AM {

enum class LaunchType {
    FrontendInitiated,
    ApplicationInitiated,
};

struct FrontendAppletParameters {
    ProgramId program_id{};
    AppletId applet_id{};
    AppletType applet_type{};
    LaunchType launch_type{};
    s32 program_index{};
    s32 previous_program_index{-1};
};

class AppletManager {
public:
    explicit AppletManager(Core::System& system);
    ~AppletManager();

    void InsertApplet(std::shared_ptr<Applet> applet);
    void TerminateAndRemoveApplet(AppletResourceUserId aruid);

    void CreateAndInsertByFrontendAppletParameters(AppletResourceUserId aruid,
                                                   const FrontendAppletParameters& params);
    std::shared_ptr<Applet> GetByAppletResourceUserId(AppletResourceUserId aruid) const;

    void Reset();

    void RequestExit();
    void RequestResume();
    void OperationModeChanged();
    void FocusStateChanged();

private:
    Core::System& m_system;

    mutable std::mutex m_lock{};
    std::map<AppletResourceUserId, std::shared_ptr<Applet>> m_applets{};

    // AudioController state goes here
};

} // namespace Service::AM

repo_name: litucks/torzu

license: mit


code:
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#include "core/hle/service/am/applet_message_queue.h"
#include "core/hle/service/ipc_helpers.h"

namespace Service::AM {

AppletMessageQueue::AppletMessageQueue(Core::System& system)
    : service_context{system, "AppletMessageQueue"} {
    on_new_message = service_context.CreateEvent("AMMessageQueue:OnMessageReceived");
    on_operation_mode_changed = service_context.CreateEvent("AMMessageQueue:OperationModeChanged");
}

AppletMessageQueue::~AppletMessageQueue() {
    service_context.CloseEvent(on_new_message);
    service_context.CloseEvent(on_operation_mode_changed);
}

Kernel::KReadableEvent& AppletMessageQueue::GetMessageReceiveEvent() {
    return on_new_message->GetReadableEvent();
}

Kernel::KReadableEvent& AppletMessageQueue::GetOperationModeChangedEvent() {
    return on_operation_mode_changed->GetReadableEvent();
}

void AppletMessageQueue::PushMessage(AppletMessage msg) {
    {
        std::scoped_lock lk{lock};
        messages.push(msg);
    }
    on_new_message->Signal();
}

AppletMessage AppletMessageQueue::PopMessage() {
    std::scoped_lock lk{lock};
    if (messages.empty()) {
        on_new_message->Clear();
        return AppletMessage::None;
    }
    auto msg = messages.front();
    messages.pop();
    if (messages.empty()) {
        on_new_message->Clear();
    }
    return msg;
}

std::size_t AppletMessageQueue::GetMessageCount() const {
    std::scoped_lock lk{lock};
    return messages.size();
}

void AppletMessageQueue::RequestExit() {
    PushMessage(AppletMessage::Exit);
}

void AppletMessageQueue::RequestResume() {
    PushMessage(AppletMessage::Resume);
}

void AppletMessageQueue::FocusStateChanged() {
    PushMessage(AppletMessage::FocusStateChanged);
}

void AppletMessageQueue::OperationModeChanged() {
    PushMessage(AppletMessage::OperationModeChanged);
    PushMessage(AppletMessage::PerformanceModeChanged);
    on_operation_mode_changed->Signal();
}

} // namespace Service::AM


code:
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#pragma once

#include <queue>

#include "core/hle/service/am/am_types.h"
#include "core/hle/service/kernel_helpers.h"
#include "core/hle/service/service.h"

namespace Kernel {
class KReadableEvent;
} // namespace Kernel

namespace Service::AM {

class AppletMessageQueue {
public:
    explicit AppletMessageQueue(Core::System& system);
    ~AppletMessageQueue();

    Kernel::KReadableEvent& GetMessageReceiveEvent();
    Kernel::KReadableEvent& GetOperationModeChangedEvent();
    void PushMessage(AppletMessage msg);
    AppletMessage PopMessage();
    std::size_t GetMessageCount() const;
    void RequestExit();
    void RequestResume();
    void FocusStateChanged();
    void OperationModeChanged();

private:
    KernelHelpers::ServiceContext service_context;

    Kernel::KEvent* on_new_message;
    Kernel::KEvent* on_operation_mode_changed;

    mutable std::mutex lock;
    std::queue<AppletMessage> messages;
};

} // namespace Service::AM


code:
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#include "core/core.h"
#include "core/hle/service/am/display_layer_manager.h"
#include "core/hle/service/sm/sm.h"
#include "core/hle/service/vi/application_display_service.h"
#include "core/hle/service/vi/container.h"
#include "core/hle/service/vi/manager_display_service.h"
#include "core/hle/service/vi/manager_root_service.h"
#include "core/hle/service/vi/shared_buffer_manager.h"
#include "core/hle/service/vi/vi_results.h"
#include "core/hle/service/vi/vi_types.h"

namespace Service::AM {

DisplayLayerManager::DisplayLayerManager() = default;
DisplayLayerManager::~DisplayLayerManager() {
    this->Finalize();
}

void DisplayLayerManager::Initialize(Core::System& system, Kernel::KProcess* process,
                                     AppletId applet_id, LibraryAppletMode mode) {
    R_ASSERT(system.ServiceManager()
                 .GetService<VI::IManagerRootService>("vi:m", true)
                 ->GetDisplayService(&m_display_service, VI::Policy::Compositor));
    R_ASSERT(m_display_service->GetManagerDisplayService(&m_manager_display_service));

    m_process = process;
    m_system_shared_buffer_id = 0;
    m_system_shared_layer_id = 0;
    m_applet_id = applet_id;
    m_buffer_sharing_enabled = false;
    m_blending_enabled = mode == LibraryAppletMode::PartialForeground ||
                         mode == LibraryAppletMode::PartialForegroundIndirectDisplay;
}

void DisplayLayerManager::Finalize() {
    if (!m_manager_display_service) {
        return;
    }

    // Clean up managed layers.
    for (const auto& layer : m_managed_display_layers) {
        m_manager_display_service->DestroyManagedLayer(layer);
    }

    for (const auto& layer : m_managed_display_recording_layers) {
        m_manager_display_service->DestroyManagedLayer(layer);
    }

    // Clean up shared layers.
    if (m_buffer_sharing_enabled) {
        m_manager_display_service->DestroySharedLayerSession(m_process);
    }

    m_manager_display_service = nullptr;
    m_display_service = nullptr;
}

Result DisplayLayerManager::CreateManagedDisplayLayer(u64* out_layer_id) {
    R_UNLESS(m_manager_display_service != nullptr, VI::ResultOperationFailed);

    // TODO(Subv): Find out how AM determines the display to use, for now just
    // create the layer in the Default display.
    u64 display_id;
    R_TRY(m_display_service->OpenDisplay(&display_id, VI::DisplayName{"Default"}));
    R_TRY(m_manager_display_service->CreateManagedLayer(
        out_layer_id, 0, display_id, Service::AppletResourceUserId{m_process->GetProcessId()}));

    m_manager_display_service->SetLayerVisibility(m_visible, *out_layer_id);
    m_managed_display_layers.emplace(*out_layer_id);

    R_SUCCEED();
}

Result DisplayLayerManager::CreateManagedDisplaySeparableLayer(u64* out_layer_id,
                                                               u64* out_recording_layer_id) {
    R_UNLESS(m_manager_display_service != nullptr, VI::ResultOperationFailed);

    // TODO(Subv): Find out how AM determines the display to use, for now just
    // create the layer in the Default display.
    // This calls nn::vi::CreateRecordingLayer() which creates another layer.
    // Currently we do not support more than 1 layer per display, output 1 layer id for now.
    // Outputting 1 layer id instead of the expected 2 has not been observed to cause any adverse
    // side effects.
    *out_recording_layer_id = 0;
    R_RETURN(this->CreateManagedDisplayLayer(out_layer_id));
}

Result DisplayLayerManager::IsSystemBufferSharingEnabled() {
    // Succeed if already enabled.
    R_SUCCEED_IF(m_buffer_sharing_enabled);

    // Ensure we can access shared layers.
    R_UNLESS(m_manager_display_service != nullptr, VI::ResultOperationFailed);
    R_UNLESS(m_applet_id != AppletId::Application, VI::ResultPermissionDenied);

    // Create the shared layer.
    u64 display_id;
    R_TRY(m_display_service->OpenDisplay(&display_id, VI::DisplayName{"Default"}));
    R_TRY(m_manager_display_service->CreateSharedLayerSession(m_process, &m_system_shared_buffer_id,
                                                              &m_system_shared_layer_id, display_id,
                                                              m_blending_enabled));

    // We succeeded, so set up remaining state.
    m_buffer_sharing_enabled = true;
    m_manager_display_service->SetLayerVisibility(m_visible, m_system_shared_layer_id);
    R_SUCCEED();
}

Result DisplayLayerManager::GetSystemSharedLayerHandle(u64* out_system_shared_buffer_id,
                                                       u64* out_system_shared_layer_id) {
    R_TRY(this->IsSystemBufferSharingEnabled());

    *out_system_shared_buffer_id = m_system_shared_buffer_id;
    *out_system_shared_layer_id = m_system_shared_layer_id;

    R_SUCCEED();
}

void DisplayLayerManager::SetWindowVisibility(bool visible) {
    if (m_visible == visible) {
        return;
    }

    m_visible = visible;

    if (m_manager_display_service) {
        if (m_system_shared_layer_id) {
            m_manager_display_service->SetLayerVisibility(m_visible, m_system_shared_layer_id);
        }

        for (const auto layer_id : m_managed_display_layers) {
            m_manager_display_service->SetLayerVisibility(m_visible, layer_id);
        }
    }
}

bool DisplayLayerManager::GetWindowVisibility() const {
    return m_visible;
}

Result DisplayLayerManager::WriteAppletCaptureBuffer(bool* out_was_written,
                                                     s32* out_fbshare_layer_index) {
    R_UNLESS(m_buffer_sharing_enabled, VI::ResultPermissionDenied);
    R_RETURN(m_display_service->GetContainer()->GetSharedBufferManager()->WriteAppletCaptureBuffer(
        out_was_written, out_fbshare_layer_index));
}

} // namespace Service::AM


code:
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#pragma once

#include <set>

#include "common/common_types.h"
#include "core/hle/result.h"
#include "core/hle/service/am/am_types.h"

namespace Core {
class System;
}

namespace Kernel {
class KProcess;
}

namespace Service::VI {
class IApplicationDisplayService;
class IManagerDisplayService;
} // namespace Service::VI

namespace Service::AM {

class DisplayLayerManager {
public:
    explicit DisplayLayerManager();
    ~DisplayLayerManager();

    void Initialize(Core::System& system, Kernel::KProcess* process, AppletId applet_id,
                    LibraryAppletMode mode);
    void Finalize();

    Result CreateManagedDisplayLayer(u64* out_layer_id);
    Result CreateManagedDisplaySeparableLayer(u64* out_layer_id, u64* out_recording_layer_id);

    Result IsSystemBufferSharingEnabled();
    Result GetSystemSharedLayerHandle(u64* out_system_shared_buffer_id,
                                      u64* out_system_shared_layer_id);

    void SetWindowVisibility(bool visible);
    bool GetWindowVisibility() const;

    Result WriteAppletCaptureBuffer(bool* out_was_written, s32* out_fbshare_layer_index);

private:
    Kernel::KProcess* m_process{};
    std::shared_ptr<VI::IApplicationDisplayService> m_display_service{};
    std::shared_ptr<VI::IManagerDisplayService> m_manager_display_service{};
    std::set<u64> m_managed_display_layers{};
    std::set<u64> m_managed_display_recording_layers{};
    u64 m_system_shared_buffer_id{};
    u64 m_system_shared_layer_id{};
    AppletId m_applet_id{};
    bool m_buffer_sharing_enabled{};
    bool m_blending_enabled{};
    bool m_visible{true};
};

} // namespace Service::AM


code:
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#include "common/assert.h"
#include "common/logging/log.h"
#include "core/core.h"
#include "core/frontend/applets/cabinet.h"
#include "core/hle/kernel/k_event.h"
#include "core/hle/kernel/k_readable_event.h"
#include "core/hle/service/am/am.h"
#include "core/hle/service/am/frontend/applet_cabinet.h"
#include "core/hle/service/am/service/storage.h"
#include "core/hle/service/mii/mii_manager.h"
#include "core/hle/service/nfc/common/device.h"
#include "hid_core/hid_core.h"

namespace Service::AM::Frontend {

Cabinet::Cabinet(Core::System& system_, std::shared_ptr<Applet> applet_,
                 LibraryAppletMode applet_mode_, const Core::Frontend::CabinetApplet& frontend_)
    : FrontendApplet{system_, applet_, applet_mode_}, frontend{frontend_}, service_context{
                                                                               system_,
                                                                               "CabinetApplet"} {

    availability_change_event =
        service_context.CreateEvent("CabinetApplet:AvailabilityChangeEvent");
}

Cabinet::~Cabinet() {
    service_context.CloseEvent(availability_change_event);
};

void Cabinet::Initialize() {
    FrontendApplet::Initialize();

    LOG_INFO(Service_HID, "Initializing Cabinet 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);

    std::shared_ptr<IStorage> storage = PopInData();
    ASSERT(storage != nullptr);

    const auto applet_input_data = storage->GetData();
    ASSERT(applet_input_data.size() >= sizeof(StartParamForAmiiboSettings));

    std::memcpy(&applet_input_common, applet_input_data.data(),
                sizeof(StartParamForAmiiboSettings));
}

Result Cabinet::GetStatus() const {
    return ResultSuccess;
}

void Cabinet::ExecuteInteractive() {
    ASSERT_MSG(false, "Attempted to call interactive execution on non-interactive applet.");
}

void Cabinet::Execute() {
    if (is_complete) {
        return;
    }

    const auto callback = [this](bool apply_changes, const std::string& amiibo_name) {
        DisplayCompleted(apply_changes, amiibo_name);
    };

    // TODO: listen on all controllers
    if (nfp_device == nullptr) {
        nfp_device = std::make_shared<Service::NFC::NfcDevice>(
            system.HIDCore().GetFirstNpadId(), system, service_context, availability_change_event);
        nfp_device->Initialize();
        nfp_device->StartDetection(Service::NFC::NfcProtocol::All);
    }

    const Core::Frontend::CabinetParameters parameters{
        .tag_info = applet_input_common.tag_info,
        .register_info = applet_input_common.register_info,
        .mode = applet_input_common.applet_mode,
    };

    switch (applet_input_common.applet_mode) {
    case Service::NFP::CabinetMode::StartNicknameAndOwnerSettings:
    case Service::NFP::CabinetMode::StartGameDataEraser:
    case Service::NFP::CabinetMode::StartRestorer:
    case Service::NFP::CabinetMode::StartFormatter:
        frontend.ShowCabinetApplet(callback, parameters, nfp_device);
        break;
    default:
        UNIMPLEMENTED_MSG("Unknown CabinetMode={}", applet_input_common.applet_mode);
        DisplayCompleted(false, {});
        break;
    }
}

void Cabinet::DisplayCompleted(bool apply_changes, std::string_view amiibo_name) {
    Service::Mii::MiiManager manager;
    ReturnValueForAmiiboSettings applet_output{};

    if (!apply_changes) {
        Cancel();
    }

    if (nfp_device->GetCurrentState() != Service::NFC::DeviceState::TagFound &&
        nfp_device->GetCurrentState() != Service::NFC::DeviceState::TagMounted) {
        Cancel();
    }

    if (nfp_device->GetCurrentState() == Service::NFC::DeviceState::TagFound) {
        nfp_device->Mount(Service::NFP::ModelType::Amiibo, Service::NFP::MountTarget::All);
    }

    switch (applet_input_common.applet_mode) {
    case Service::NFP::CabinetMode::StartNicknameAndOwnerSettings: {
        Service::NFP::RegisterInfoPrivate register_info{};
        std::memcpy(register_info.amiibo_name.data(), amiibo_name.data(),
                    std::min(amiibo_name.size(), register_info.amiibo_name.size() - 1));
        register_info.mii_store_data.BuildRandom(Mii::Age::All, Mii::Gender::All, Mii::Race::All);
        register_info.mii_store_data.SetNickname({u'y', u'u', u'z', u'u'});
        nfp_device->SetRegisterInfoPrivate(register_info);
        break;
    }
    case Service::NFP::CabinetMode::StartGameDataEraser:
        nfp_device->DeleteApplicationArea();
        break;
    case Service::NFP::CabinetMode::StartRestorer:
        nfp_device->Restore();
        break;
    case Service::NFP::CabinetMode::StartFormatter:
        nfp_device->Format();
        break;
    default:
        UNIMPLEMENTED_MSG("Unknown CabinetMode={}", applet_input_common.applet_mode);
        break;
    }

    applet_output.device_handle = applet_input_common.device_handle;
    applet_output.result = CabinetResult::Cancel;
    const auto reg_result = nfp_device->GetRegisterInfo(applet_output.register_info);
    const auto tag_result = nfp_device->GetTagInfo(applet_output.tag_info);
    nfp_device->Finalize();

    if (reg_result.IsSuccess()) {
        applet_output.result |= CabinetResult::RegisterInfo;
    }

    if (tag_result.IsSuccess()) {
        applet_output.result |= CabinetResult::TagInfo;
    }

    std::vector<u8> out_data(sizeof(ReturnValueForAmiiboSettings));
    std::memcpy(out_data.data(), &applet_output, sizeof(ReturnValueForAmiiboSettings));

    is_complete = true;

    PushOutData(std::make_shared<IStorage>(system, std::move(out_data)));
    Exit();
}

void Cabinet::Cancel() {
    ReturnValueForAmiiboSettings applet_output{};
    applet_output.device_handle = applet_input_common.device_handle;
    applet_output.result = CabinetResult::Cancel;
    nfp_device->Finalize();

    std::vector<u8> out_data(sizeof(ReturnValueForAmiiboSettings));
    std::memcpy(out_data.data(), &applet_output, sizeof(ReturnValueForAmiiboSettings));

    is_complete = true;

    PushOutData(std::make_shared<IStorage>(system, std::move(out_data)));
    Exit();
}

Result Cabinet::RequestExit() {
    frontend.Close();
    R_SUCCEED();
}

} // namespace Service::AM::Frontend


code:
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#pragma once

#include <array>

#include "core/hle/result.h"
#include "core/hle/service/am/frontend/applets.h"
#include "core/hle/service/kernel_helpers.h"
#include "core/hle/service/nfp/nfp_types.h"

namespace Kernel {
class KEvent;
class KReadableEvent;
} // namespace Kernel

namespace Core {
class System;
} // namespace Core

namespace Service::NFC {
class NfcDevice;
}

namespace Service::AM::Frontend {

enum class CabinetAppletVersion : u32 {
    Version1 = 0x1,
};

enum class CabinetFlags : u8 {
    None = 0,
    DeviceHandle = 1 << 0,
    TagInfo = 1 << 1,
    RegisterInfo = 1 << 2,
    All = DeviceHandle | TagInfo | RegisterInfo,
};
DECLARE_ENUM_FLAG_OPERATORS(CabinetFlags)

enum class CabinetResult : u8 {
    Cancel = 0,
    TagInfo = 1 << 1,
    RegisterInfo = 1 << 2,
    All = TagInfo | RegisterInfo,
};
DECLARE_ENUM_FLAG_OPERATORS(CabinetResult)

// This is nn::nfp::AmiiboSettingsStartParam
struct AmiiboSettingsStartParam {
    u64 device_handle;
    std::array<u8, 0x20> param_1;
    u8 param_2;
};
static_assert(sizeof(AmiiboSettingsStartParam) == 0x30,
              "AmiiboSettingsStartParam is an invalid size");

#pragma pack(push, 1)
// This is nn::nfp::StartParamForAmiiboSettings
struct StartParamForAmiiboSettings {
    u8 param_1;
    Service::NFP::CabinetMode applet_mode;
    CabinetFlags flags;
    u8 amiibo_settings_1;
    u64 device_handle;
    Service::NFP::TagInfo tag_info;
    Service::NFP::RegisterInfo register_info;
    std::array<u8, 0x20> amiibo_settings_3;
    INSERT_PADDING_BYTES(0x24);
};
static_assert(sizeof(StartParamForAmiiboSettings) == 0x1A8,
              "StartParamForAmiiboSettings is an invalid size");

// This is nn::nfp::ReturnValueForAmiiboSettings
struct ReturnValueForAmiiboSettings {
    CabinetResult result;
    INSERT_PADDING_BYTES(0x3);
    u64 device_handle;
    Service::NFP::TagInfo tag_info;
    Service::NFP::RegisterInfo register_info;
    INSERT_PADDING_BYTES(0x24);
};
static_assert(sizeof(ReturnValueForAmiiboSettings) == 0x188,
              "ReturnValueForAmiiboSettings is an invalid size");
#pragma pack(pop)

class Cabinet final : public FrontendApplet {
public:
    explicit Cabinet(Core::System& system_, std::shared_ptr<Applet> applet_,
                     LibraryAppletMode applet_mode_,
                     const Core::Frontend::CabinetApplet& frontend_);
    ~Cabinet() override;

    void Initialize() override;

    Result GetStatus() const override;
    void ExecuteInteractive() override;
    void Execute() override;
    void DisplayCompleted(bool apply_changes, std::string_view amiibo_name);
    void Cancel();
    Result RequestExit() override;

private:
    const Core::Frontend::CabinetApplet& frontend;

    bool is_complete{false};
    std::shared_ptr<Service::NFC::NfcDevice> nfp_device;
    Kernel::KEvent* availability_change_event;
    KernelHelpers::ServiceContext service_context;
    StartParamForAmiiboSettings applet_input_common{};
};

} // namespace Service::AM::Frontend


code:
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#include <algorithm>
#include <cstring>

#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/frontend/applet_controller.h"
#include "core/hle/service/am/service/storage.h"
#include "hid_core/frontend/emulated_controller.h"
#include "hid_core/hid_core.h"
#include "hid_core/hid_types.h"
#include "hid_core/resources/npad/npad.h"

namespace Service::AM::Frontend {

[[maybe_unused]] constexpr Result ResultControllerSupportCanceled{ErrorModule::HID, 3101};
[[maybe_unused]] constexpr Result ResultControllerSupportNotSupportedNpadStyle{ErrorModule::HID,
                                                                               3102};

static Core::Frontend::ControllerParameters ConvertToFrontendParameters(
    ControllerSupportArgPrivate private_arg, ControllerSupportArgHeader header, bool enable_text,
    std::vector<IdentificationColor> identification_colors, std::vector<ExplainText> text) {
    Core::HID::NpadStyleTag 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_, std::shared_ptr<Applet> applet_,
                       LibraryAppletMode applet_mode_,
                       const Core::Frontend::ControllerApplet& frontend_)
    : FrontendApplet{system_, applet_, applet_mode_}, frontend{frontend_} {}

Controller::~Controller() = default;

void Controller::Initialize() {
    FrontendApplet::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 std::shared_ptr<IStorage> private_arg_storage = PopInData();
    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;
        case sizeof(ControllerKeyRemappingArg):
            controller_private_arg.mode =
                ControllerSupportMode::ShowControllerKeyRemappingForSystem;
            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.mode ==
                 ControllerSupportMode::ShowControllerKeyRemappingForSystem)) {
            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 std::shared_ptr<IStorage> user_arg_storage = PopInData();
        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:
        case ControllerAppletVersion::Version8:
            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 std::shared_ptr<IStorage> update_arg_storage = PopInData();
        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;
    }
    case ControllerSupportMode::ShowControllerKeyRemappingForSystem: {
        const std::shared_ptr<IStorage> remapping_arg_storage = PopInData();
        ASSERT(remapping_arg_storage != nullptr);

        const auto& remapping_arg = remapping_arg_storage->GetData();
        ASSERT(remapping_arg.size() == sizeof(ControllerKeyRemappingArg));

        std::memcpy(&controller_key_remapping_arg, remapping_arg.data(), remapping_arg.size());
        break;
    }
    default: {
        UNIMPLEMENTED_MSG("Unimplemented ControllerSupportMode={}", controller_private_arg.mode);
        break;
    }
    }
}

Result Controller::GetStatus() const {
    return status;
}

void Controller::ExecuteInteractive() {
    ASSERT_MSG(false, "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<IdentificationColor>(
                        controller_user_arg_old.identification_colors.begin(),
                        controller_user_arg_old.identification_colors.end()),
                    std::vector<ExplainText>(controller_user_arg_old.explain_text.begin(),
                                             controller_user_arg_old.explain_text.end()));
            case ControllerAppletVersion::Version7:
            case ControllerAppletVersion::Version8:
            default:
                return ConvertToFrontendParameters(
                    controller_private_arg, controller_user_arg_new.header,
                    controller_user_arg_new.enable_explain_text,
                    std::vector<IdentificationColor>(
                        controller_user_arg_new.identification_colors.begin(),
                        controller_user_arg_new.identification_colors.end()),
                    std::vector<ExplainText>(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](bool is_success) { ConfigurationComplete(is_success); }, parameters);
        break;
    }
    case ControllerSupportMode::ShowControllerStrapGuide:
    case ControllerSupportMode::ShowControllerFirmwareUpdate:
    case ControllerSupportMode::ShowControllerKeyRemappingForSystem:
        UNIMPLEMENTED_MSG("ControllerSupportMode={} is not implemented",
                          controller_private_arg.mode);
        ConfigurationComplete(true);
        break;
    default: {
        ConfigurationComplete(true);
        break;
    }
    }
}

void Controller::ConfigurationComplete(bool is_success) {
    ControllerSupportResultInfo result_info{};

    // 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 : system.HIDCore().GetPlayerCount();

    result_info.selected_id = static_cast<u32>(system.HIDCore().GetFirstNpadId());

    result_info.result =
        is_success ? ControllerSupportResult::Success : ControllerSupportResult::Cancel;

    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<u8>(sizeof(ControllerSupportResultInfo));
    std::memcpy(out_data.data(), &result_info, out_data.size());

    PushOutData(std::make_shared<IStorage>(system, std::move(out_data)));
    Exit();
}

Result Controller::RequestExit() {
    frontend.Close();
    R_SUCCEED();
}

} // namespace Service::AM::Frontend


code:
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#pragma once

#include <array>
#include <vector>

#include "common/common_funcs.h"
#include "common/common_types.h"
#include "core/hle/result.h"
#include "core/hle/service/am/frontend/applets.h"

namespace Core {
class System;
}

namespace Core::HID {
enum class NpadStyleSet : u32;
}

namespace Service::AM::Frontend {

using IdentificationColor = std::array<u8, 4>;
using ExplainText = std::array<char, 0x81>;

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 - 10.2.0
    Version8 = 0x8, // 11.0.0+
};

enum class ControllerSupportMode : u8 {
    ShowControllerSupport,
    ShowControllerStrapGuide,
    ShowControllerFirmwareUpdate,
    ShowControllerKeyRemappingForSystem,

    MaxControllerSupportMode,
};

enum class ControllerSupportCaller : u8 {
    Application,
    System,

    MaxControllerSupportCaller,
};

enum class ControllerSupportResult : u32 {
    Success = 0,
    Cancel = 2,
};

struct ControllerSupportArgPrivate {
    u32 arg_private_size{};
    u32 arg_size{};
    bool is_home_menu{};
    bool flag_1{};
    ControllerSupportMode mode{};
    ControllerSupportCaller caller{};
    Core::HID::NpadStyleSet 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<IdentificationColor, 4> identification_colors{};
    bool enable_explain_text{};
    std::array<ExplainText, 4> explain_text{};
};
static_assert(sizeof(ControllerSupportArgOld) == 0x21C,
              "ControllerSupportArgOld has incorrect size.");

// LibraryAppletVersion 0x7, 0x8
struct ControllerSupportArgNew {
    ControllerSupportArgHeader header{};
    std::array<IdentificationColor, 8> identification_colors{};
    bool enable_explain_text{};
    std::array<ExplainText, 8> 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 ControllerKeyRemappingArg {
    u64 unknown{};
    u32 unknown_2{};
    INSERT_PADDING_WORDS(1);
};
static_assert(sizeof(ControllerKeyRemappingArg) == 0x10,
              "ControllerKeyRemappingArg has incorrect size.");

struct ControllerSupportResultInfo {
    s8 player_count{};
    INSERT_PADDING_BYTES(3);
    u32 selected_id{};
    ControllerSupportResult result{};
};
static_assert(sizeof(ControllerSupportResultInfo) == 0xC,
              "ControllerSupportResultInfo has incorrect size.");

class Controller final : public FrontendApplet {
public:
    explicit Controller(Core::System& system_, std::shared_ptr<Applet> applet_,
                        LibraryAppletMode applet_mode_,
                        const Core::Frontend::ControllerApplet& frontend_);
    ~Controller() override;

    void Initialize() override;

    Result GetStatus() const override;
    void ExecuteInteractive() override;
    void Execute() override;
    Result RequestExit() override;

    void ConfigurationComplete(bool is_success);

private:
    const Core::Frontend::ControllerApplet& frontend;

    ControllerAppletVersion controller_applet_version;
    ControllerSupportArgPrivate controller_private_arg;
    ControllerSupportArgOld controller_user_arg_old;
    ControllerSupportArgNew controller_user_arg_new;
    ControllerUpdateFirmwareArg controller_update_arg;
    ControllerKeyRemappingArg controller_key_remapping_arg;
    bool complete{false};
    Result status{ResultSuccess};
    bool is_single_mode{false};
    std::vector<u8> out_data;
};

} // namespace Service::AM::Frontend


code:
// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#include <array>
#include <cstring>
#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/service/am/am.h"
#include "core/hle/service/am/frontend/applet_error.h"
#include "core/hle/service/am/service/storage.h"
#include "core/reporter.h"

namespace Service::AM::Frontend {

struct ErrorCode {
    u32 error_category{};
    u32 error_number{};

    static constexpr ErrorCode FromU64(u64 error_code) {
        return {
            .error_category{static_cast<u32>(error_code >> 32)},
            .error_number{static_cast<u32>(error_code & 0xFFFFFFFF)},
        };
    }

    static constexpr ErrorCode FromResult(Result result) {
        return {
            .error_category{2000 + static_cast<u32>(result.GetModule())},
            .error_number{result.GetDescription()},
        };
    }

    constexpr Result ToResult() const {
        return Result{static_cast<ErrorModule>(error_category - 2000), error_number};
    }
};
static_assert(sizeof(ErrorCode) == 0x8, "ErrorCode has incorrect size.");

#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<char, 8> language_code;
    std::array<char, 0x800> main_text;
    std::array<char, 0x800> 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<char, 8> language_code;
    std::array<char, 0x800> main_text;
    std::array<char, 0x800> 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<u8, 0x1018> raw{};
};

namespace {
template <typename T>
void CopyArgumentData(const std::vector<u8>& data, T& variable) {
    ASSERT(data.size() >= sizeof(T));
    std::memcpy(&variable, data.data(), sizeof(T));
}

Result Decode64BitError(u64 error) {
    return ErrorCode::FromU64(error).ToResult();
}

} // Anonymous namespace

Error::Error(Core::System& system_, std::shared_ptr<Applet> applet_, LibraryAppletMode applet_mode_,
             const Core::Frontend::ErrorApplet& frontend_)
    : FrontendApplet{system_, applet_, applet_mode_}, frontend{frontend_} {}

Error::~Error() = default;

void Error::Initialize() {
    FrontendApplet::Initialize();
    args = std::make_unique<ErrorArguments>();
    complete = false;

    const std::shared_ptr<IStorage> storage = PopInData();
    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 = Result(args->error.error_code_32);
        }
        break;
    case ErrorAppletMode::ShowSystemError:
        CopyArgumentData(data, args->system_error);
        error_code = Result(Decode64BitError(args->system_error.error_code_64));
        break;
    case ErrorAppletMode::ShowApplicationError:
        CopyArgumentData(data, args->application_error);
        error_code = Result(args->application_error.error_code);
        break;
    case ErrorAppletMode::ShowErrorPctl:
        CopyArgumentData(data, args->error_record);
        error_code = Decode64BitError(args->error_record.error_code_64);
        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);
        break;
    }
}

Result Error::GetStatus() const {
    return ResultSuccess;
}

void Error::ExecuteInteractive() {
    ASSERT_MSG(false, "Unexpected interactive applet data!");
}

void Error::Execute() {
    if (complete) {
        return;
    }

    const auto callback = [this] { DisplayCompleted(); };
    const auto title_id = system.GetApplicationProcessProgramID();
    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::ShowErrorPctl:
    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;
    PushOutData(std::make_shared<IStorage>(system, std::vector<u8>(0x1000)));
    Exit();
}

Result Error::RequestExit() {
    frontend.Close();
    R_SUCCEED();
}

} // namespace Service::AM::Frontend


code:
// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#pragma once

#include "core/hle/result.h"
#include "core/hle/service/am/frontend/applets.h"

namespace Core {
class System;
}

namespace Service::AM::Frontend {

enum class ErrorAppletMode : u8 {
    ShowError = 0,
    ShowSystemError = 1,
    ShowApplicationError = 2,
    ShowEula = 3,
    ShowErrorPctl = 4,
    ShowErrorRecord = 5,
    ShowUpdateEula = 8,
};

class Error final : public FrontendApplet {
public:
    explicit Error(Core::System& system_, std::shared_ptr<Applet> applet_,
                   LibraryAppletMode applet_mode_, const Core::Frontend::ErrorApplet& frontend_);
    ~Error() override;

    void Initialize() override;

    Result GetStatus() const override;
    void ExecuteInteractive() override;
    void Execute() override;
    Result RequestExit() override;

    void DisplayCompleted();

private:
    union ErrorArguments;

    const Core::Frontend::ErrorApplet& frontend;
    Result error_code = ResultSuccess;
    ErrorAppletMode mode = ErrorAppletMode::ShowError;
    std::unique_ptr<ErrorArguments> args;

    bool complete = false;
};

} // namespace Service::AM::Frontend


code:
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#include "common/assert.h"
#include "common/hex_util.h"
#include "common/logging/log.h"
#include "core/core.h"
#include "core/frontend/applets/general.h"
#include "core/hle/result.h"
#include "core/hle/service/am/am.h"
#include "core/hle/service/am/applet_data_broker.h"
#include "core/hle/service/am/frontend/applet_general.h"
#include "core/hle/service/am/service/storage.h"
#include "core/reporter.h"

namespace Service::AM::Frontend {

constexpr Result ERROR_INVALID_PIN{ErrorModule::PCTL, 221};

static void LogCurrentStorage(std::shared_ptr<Applet> applet, std::string_view prefix) {
    std::shared_ptr<IStorage> storage;
    while (R_SUCCEEDED(applet->caller_applet_broker->GetInData().Pop(&storage))) {
        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));
    }

    while (R_SUCCEEDED(applet->caller_applet_broker->GetInteractiveInData().Pop(&storage))) {
        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_, std::shared_ptr<Applet> applet_, LibraryAppletMode applet_mode_,
           Core::Frontend::ParentalControlsApplet& frontend_)
    : FrontendApplet{system_, applet_, applet_mode_}, frontend{frontend_} {}

Auth::~Auth() = default;

void Auth::Initialize() {
    FrontendApplet::Initialize();
    complete = false;

    const std::shared_ptr<IStorage> storage = PopInData();
    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;
}

Result Auth::GetStatus() const {
    return successful ? ResultSuccess : ERROR_INVALID_PIN;
}

void Auth::ExecuteInteractive() {
    ASSERT_MSG(false, "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<bool>(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();
        break;
    }
}

void Auth::AuthFinished(bool is_successful) {
    successful = is_successful;

    struct Return {
        Result result_code;
    };
    static_assert(sizeof(Return) == 0x4, "Return (AuthApplet) has incorrect size.");

    Return return_{GetStatus()};

    std::vector<u8> out(sizeof(Return));
    std::memcpy(out.data(), &return_, sizeof(Return));

    PushOutData(std::make_shared<IStorage>(system, std::move(out)));
    Exit();
}

Result Auth::RequestExit() {
    frontend.Close();
    R_SUCCEED();
}

PhotoViewer::PhotoViewer(Core::System& system_, std::shared_ptr<Applet> applet_,
                         LibraryAppletMode applet_mode_,
                         const Core::Frontend::PhotoViewerApplet& frontend_)
    : FrontendApplet{system_, applet_, applet_mode_}, frontend{frontend_} {}

PhotoViewer::~PhotoViewer() = default;

void PhotoViewer::Initialize() {
    FrontendApplet::Initialize();
    complete = false;

    const std::shared_ptr<IStorage> storage = PopInData();
    ASSERT(storage != nullptr);
    const auto data = storage->GetData();
    ASSERT(!data.empty());
    mode = static_cast<PhotoViewerAppletMode>(data[0]);
}

Result PhotoViewer::GetStatus() const {
    return ResultSuccess;
}

void PhotoViewer::ExecuteInteractive() {
    ASSERT_MSG(false, "Unexpected interactive applet data.");
}

void PhotoViewer::Execute() {
    if (complete)
        return;

    const auto callback = [this] { ViewFinished(); };
    switch (mode) {
    case PhotoViewerAppletMode::CurrentApp:
        frontend.ShowPhotosForApplication(system.GetApplicationProcessProgramID(), callback);
        break;
    case PhotoViewerAppletMode::AllApps:
        frontend.ShowAllPhotos(callback);
        break;
    default:
        UNIMPLEMENTED_MSG("Unimplemented PhotoViewer applet mode={:02X}!", mode);
        break;
    }
}

void PhotoViewer::ViewFinished() {
    PushOutData(std::make_shared<IStorage>(system, std::vector<u8>{}));
    Exit();
}

Result PhotoViewer::RequestExit() {
    frontend.Close();
    R_SUCCEED();
}

StubApplet::StubApplet(Core::System& system_, std::shared_ptr<Applet> applet_, AppletId id_,
                       LibraryAppletMode applet_mode_)
    : FrontendApplet{system_, applet_, applet_mode_}, id{id_} {}

StubApplet::~StubApplet() = default;

void StubApplet::Initialize() {
    LOG_WARNING(Service_AM, "called (STUBBED)");
    FrontendApplet::Initialize();

    LogCurrentStorage(applet.lock(), "Initialize");
}

Result StubApplet::GetStatus() const {
    LOG_WARNING(Service_AM, "called (STUBBED)");
    return ResultSuccess;
}

void StubApplet::ExecuteInteractive() {
    LOG_WARNING(Service_AM, "called (STUBBED)");
    LogCurrentStorage(applet.lock(), "ExecuteInteractive");

    PushOutData(std::make_shared<IStorage>(system, std::vector<u8>(0x1000)));
    PushInteractiveOutData(std::make_shared<IStorage>(system, std::vector<u8>(0x1000)));
    Exit();
}

void StubApplet::Execute() {
    LOG_WARNING(Service_AM, "called (STUBBED)");
    LogCurrentStorage(applet.lock(), "Execute");

    PushOutData(std::make_shared<IStorage>(system, std::vector<u8>(0x1000)));
    PushInteractiveOutData(std::make_shared<IStorage>(system, std::vector<u8>(0x1000)));
    Exit();
}

Result StubApplet::RequestExit() {
    // Nothing to do.
    R_SUCCEED();
}

} // namespace Service::AM::Frontend


code:
// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#pragma once

#include "core/hle/service/am/frontend/applets.h"

namespace Core {
class System;
}

namespace Service::AM::Frontend {

enum class AuthAppletType : u32 {
    ShowParentalAuthentication,
    RegisterParentalPasscode,
    ChangeParentalPasscode,
};

class Auth final : public FrontendApplet {
public:
    explicit Auth(Core::System& system_, std::shared_ptr<Applet> applet_,
                  LibraryAppletMode applet_mode_,
                  Core::Frontend::ParentalControlsApplet& frontend_);
    ~Auth() override;

    void Initialize() override;
    Result GetStatus() const override;
    void ExecuteInteractive() override;
    void Execute() override;
    Result RequestExit() override;

    void AuthFinished(bool is_successful = true);

private:
    Core::Frontend::ParentalControlsApplet& frontend;
    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 FrontendApplet {
public:
    explicit PhotoViewer(Core::System& system_, std::shared_ptr<Applet> applet_,
                         LibraryAppletMode applet_mode_,
                         const Core::Frontend::PhotoViewerApplet& frontend_);
    ~PhotoViewer() override;

    void Initialize() override;
    Result GetStatus() const override;
    void ExecuteInteractive() override;
    void Execute() override;
    Result RequestExit() override;

    void ViewFinished();

private:
    const Core::Frontend::PhotoViewerApplet& frontend;
    bool complete = false;
    PhotoViewerAppletMode mode = PhotoViewerAppletMode::CurrentApp;
};

class StubApplet final : public FrontendApplet {
public:
    explicit StubApplet(Core::System& system_, std::shared_ptr<Applet> applet_, AppletId id_,
                        LibraryAppletMode applet_mode_);
    ~StubApplet() override;

    void Initialize() override;

    Result GetStatus() const override;
    void ExecuteInteractive() override;
    void Execute() override;
    Result RequestExit() override;

private:
    AppletId id;
};

} // namespace Service::AM::Frontend


code:
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#include "common/assert.h"
#include "common/logging/log.h"
#include "core/core.h"
#include "core/frontend/applets/mii_edit.h"
#include "core/hle/service/am/am.h"
#include "core/hle/service/am/frontend/applet_mii_edit.h"
#include "core/hle/service/am/service/storage.h"
#include "core/hle/service/mii/mii.h"
#include "core/hle/service/mii/mii_manager.h"
#include "core/hle/service/sm/sm.h"

namespace Service::AM::Frontend {

MiiEdit::MiiEdit(Core::System& system_, std::shared_ptr<Applet> applet_,
                 LibraryAppletMode applet_mode_, const Core::Frontend::MiiEditApplet& frontend_)
    : FrontendApplet{system_, applet_, applet_mode_}, frontend{frontend_} {}

MiiEdit::~MiiEdit() = default;

void MiiEdit::Initialize() {
    // Note: MiiEdit is not initialized with common arguments.
    //       Instead, it is initialized by an AppletInput storage with size 0x100 bytes.
    //       Do NOT call Applet::Initialize() here.

    const std::shared_ptr<IStorage> storage = PopInData();
    ASSERT(storage != nullptr);

    const auto applet_input_data = storage->GetData();
    ASSERT(applet_input_data.size() >= sizeof(MiiEditAppletInputCommon));

    std::memcpy(&applet_input_common, applet_input_data.data(), sizeof(MiiEditAppletInputCommon));

    LOG_INFO(Service_AM,
             "Initializing MiiEdit Applet with MiiEditAppletVersion={} and MiiEditAppletMode={}",
             applet_input_common.version, applet_input_common.applet_mode);

    switch (applet_input_common.version) {
    case MiiEditAppletVersion::Version3:
        ASSERT(applet_input_data.size() ==
               sizeof(MiiEditAppletInputCommon) + sizeof(MiiEditAppletInputV3));
        std::memcpy(&applet_input_v3, applet_input_data.data() + sizeof(MiiEditAppletInputCommon),
                    sizeof(MiiEditAppletInputV3));
        break;
    case MiiEditAppletVersion::Version4:
        ASSERT(applet_input_data.size() ==
               sizeof(MiiEditAppletInputCommon) + sizeof(MiiEditAppletInputV4));
        std::memcpy(&applet_input_v4, applet_input_data.data() + sizeof(MiiEditAppletInputCommon),
                    sizeof(MiiEditAppletInputV4));
        break;
    default:
        UNIMPLEMENTED_MSG("Unknown MiiEditAppletVersion={} with size={}",
                          applet_input_common.version, applet_input_data.size());
        ASSERT(applet_input_data.size() >=
               sizeof(MiiEditAppletInputCommon) + sizeof(MiiEditAppletInputV4));
        std::memcpy(&applet_input_v4, applet_input_data.data() + sizeof(MiiEditAppletInputCommon),
                    sizeof(MiiEditAppletInputV4));
        break;
    }

    manager = system.ServiceManager().GetService<Mii::IStaticService>("mii:e")->GetMiiManager();
    if (manager == nullptr) {
        manager = std::make_shared<Mii::MiiManager>();
    }
    manager->Initialize(metadata);
}

Result MiiEdit::GetStatus() const {
    return ResultSuccess;
}

void MiiEdit::ExecuteInteractive() {
    ASSERT_MSG(false, "Attempted to call interactive execution on non-interactive applet.");
}

void MiiEdit::Execute() {
    if (is_complete) {
        return;
    }

    // This is a default stub for each of the MiiEdit applet modes.
    switch (applet_input_common.applet_mode) {
    case MiiEditAppletMode::ShowMiiEdit:
    case MiiEditAppletMode::AppendMiiImage:
    case MiiEditAppletMode::UpdateMiiImage:
        MiiEditOutput(MiiEditResult::Success, 0);
        break;
    case MiiEditAppletMode::AppendMii: {
        Mii::StoreData store_data{};
        store_data.BuildRandom(Mii::Age::All, Mii::Gender::All, Mii::Race::All);
        store_data.SetNickname({u'y', u'u', u'z', u'u'});
        store_data.SetChecksum();
        const auto result = manager->AddOrReplace(metadata, store_data);

        if (result.IsError()) {
            MiiEditOutput(MiiEditResult::Cancel, 0);
            break;
        }

        s32 index = manager->FindIndex(store_data.GetCreateId(), false);

        if (index == -1) {
            MiiEditOutput(MiiEditResult::Cancel, 0);
            break;
        }

        MiiEditOutput(MiiEditResult::Success, index);
        break;
    }
    case MiiEditAppletMode::CreateMii: {
        Mii::CharInfo char_info{};
        manager->BuildRandom(char_info, Mii::Age::All, Mii::Gender::All, Mii::Race::All);

        const MiiEditCharInfo edit_char_info{
            .mii_info{char_info},
        };

        MiiEditOutputForCharInfoEditing(MiiEditResult::Success, edit_char_info);
        break;
    }
    case MiiEditAppletMode::EditMii: {
        const MiiEditCharInfo edit_char_info{
            .mii_info{applet_input_v4.char_info.mii_info},
        };

        MiiEditOutputForCharInfoEditing(MiiEditResult::Success, edit_char_info);
        break;
    }
    default:
        UNIMPLEMENTED_MSG("Unknown MiiEditAppletMode={}", applet_input_common.applet_mode);

        MiiEditOutput(MiiEditResult::Success, 0);
        break;
    }
}

void MiiEdit::MiiEditOutput(MiiEditResult result, s32 index) {
    const MiiEditAppletOutput applet_output{
        .result{result},
        .index{index},
    };

    LOG_INFO(Input, "called, result={}, index={}", result, index);

    std::vector<u8> out_data(sizeof(MiiEditAppletOutput));
    std::memcpy(out_data.data(), &applet_output, sizeof(MiiEditAppletOutput));

    is_complete = true;

    PushOutData(std::make_shared<IStorage>(system, std::move(out_data)));
    Exit();
}

void MiiEdit::MiiEditOutputForCharInfoEditing(MiiEditResult result,
                                              const MiiEditCharInfo& char_info) {
    const MiiEditAppletOutputForCharInfoEditing applet_output{
        .result{result},
        .char_info{char_info},
    };

    std::vector<u8> out_data(sizeof(MiiEditAppletOutputForCharInfoEditing));
    std::memcpy(out_data.data(), &applet_output, sizeof(MiiEditAppletOutputForCharInfoEditing));

    is_complete = true;

    PushOutData(std::make_shared<IStorage>(system, std::move(out_data)));
    Exit();
}

Result MiiEdit::RequestExit() {
    frontend.Close();
    R_SUCCEED();
}

} // namespace Service::AM::Frontend


code:
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#pragma once

#include "core/hle/result.h"
#include "core/hle/service/am/frontend/applet_mii_edit_types.h"
#include "core/hle/service/am/frontend/applets.h"

namespace Core {
class System;
} // namespace Core

namespace Service::Mii {
struct DatabaseSessionMetadata;
class MiiManager;
} // namespace Service::Mii

namespace Service::AM::Frontend {

class MiiEdit final : public FrontendApplet {
public:
    explicit MiiEdit(Core::System& system_, std::shared_ptr<Applet> applet_,
                     LibraryAppletMode applet_mode_,
                     const Core::Frontend::MiiEditApplet& frontend_);
    ~MiiEdit() override;

    void Initialize() override;

    Result GetStatus() const override;
    void ExecuteInteractive() override;
    void Execute() override;
    Result RequestExit() override;

    void MiiEditOutput(MiiEditResult result, s32 index);

    void MiiEditOutputForCharInfoEditing(MiiEditResult result, const MiiEditCharInfo& char_info);

private:
    const Core::Frontend::MiiEditApplet& frontend;

    MiiEditAppletInputCommon applet_input_common{};
    MiiEditAppletInputV3 applet_input_v3{};
    MiiEditAppletInputV4 applet_input_v4{};

    bool is_complete{false};
    std::shared_ptr<Mii::MiiManager> manager = nullptr;
    Mii::DatabaseSessionMetadata metadata{};
};

} // namespace Service::AM::Frontend


code:
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#pragma once

#include <array>

#include "common/common_funcs.h"
#include "common/common_types.h"
#include "common/uuid.h"
#include "core/hle/service/mii/types/char_info.h"

namespace Service::AM::Frontend {

enum class MiiEditAppletVersion : s32 {
    Version3 = 0x3, // 1.0.0 - 10.1.1
    Version4 = 0x4, // 10.2.0+
};

// This is nn::mii::AppletMode
enum class MiiEditAppletMode : u32 {
    ShowMiiEdit = 0,
    AppendMii = 1,
    AppendMiiImage = 2,
    UpdateMiiImage = 3,
    CreateMii = 4,
    EditMii = 5,
};

enum class MiiEditResult : u32 {
    Success,
    Cancel,
};

struct MiiEditCharInfo {
    Service::Mii::CharInfo mii_info{};
};
static_assert(sizeof(MiiEditCharInfo) == 0x58, "MiiEditCharInfo has incorrect size.");

struct MiiEditAppletInputCommon {
    MiiEditAppletVersion version{};
    MiiEditAppletMode applet_mode{};
};
static_assert(sizeof(MiiEditAppletInputCommon) == 0x8,
              "MiiEditAppletInputCommon has incorrect size.");

struct MiiEditAppletInputV3 {
    u32 special_mii_key_code{};
    std::array<Common::UUID, 8> valid_uuids{};
    Common::UUID used_uuid{};
    INSERT_PADDING_BYTES(0x64);
};
static_assert(sizeof(MiiEditAppletInputV3) == 0x100 - sizeof(MiiEditAppletInputCommon),
              "MiiEditAppletInputV3 has incorrect size.");

struct MiiEditAppletInputV4 {
    u32 special_mii_key_code{};
    MiiEditCharInfo char_info{};
    INSERT_PADDING_BYTES(0x28);
    Common::UUID used_uuid{};
    INSERT_PADDING_BYTES(0x64);
};
static_assert(sizeof(MiiEditAppletInputV4) == 0x100 - sizeof(MiiEditAppletInputCommon),
              "MiiEditAppletInputV4 has incorrect size.");

// This is nn::mii::AppletOutput
struct MiiEditAppletOutput {
    MiiEditResult result{};
    s32 index{};
    INSERT_PADDING_BYTES(0x18);
};
static_assert(sizeof(MiiEditAppletOutput) == 0x20, "MiiEditAppletOutput has incorrect size.");

// This is nn::mii::AppletOutputForCharInfoEditing
struct MiiEditAppletOutputForCharInfoEditing {
    MiiEditResult result{};
    MiiEditCharInfo char_info{};
    INSERT_PADDING_BYTES(0x24);
};
static_assert(sizeof(MiiEditAppletOutputForCharInfoEditing) == 0x80,
              "MiiEditAppletOutputForCharInfoEditing has incorrect size.");

} // namespace Service::AM::Frontend


code:
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#include <cstring>

#include "common/assert.h"
#include "common/string_util.h"
#include "core/core.h"
#include "core/frontend/applets/profile_select.h"
#include "core/hle/service/acc/errors.h"
#include "core/hle/service/am/am.h"
#include "core/hle/service/am/frontend/applet_profile_select.h"
#include "core/hle/service/am/service/storage.h"

namespace Service::AM::Frontend {

ProfileSelect::ProfileSelect(Core::System& system_, std::shared_ptr<Applet> applet_,
                             LibraryAppletMode applet_mode_,
                             const Core::Frontend::ProfileSelectApplet& frontend_)
    : FrontendApplet{system_, applet_, applet_mode_}, frontend{frontend_} {}

ProfileSelect::~ProfileSelect() = default;

void ProfileSelect::Initialize() {
    complete = false;
    status = ResultSuccess;
    final_data.clear();

    FrontendApplet::Initialize();
    profile_select_version = ProfileSelectAppletVersion{common_args.library_version};

    const std::shared_ptr<IStorage> user_config_storage = PopInData();
    ASSERT(user_config_storage != nullptr);
    const auto& user_config = user_config_storage->GetData();

    LOG_INFO(Service_AM, "Initializing Profile Select Applet with version={}",
             profile_select_version);

    switch (profile_select_version) {
    case ProfileSelectAppletVersion::Version1:
        ASSERT(user_config.size() == sizeof(UiSettingsV1));
        std::memcpy(&config_old, user_config.data(), sizeof(UiSettingsV1));
        break;
    case ProfileSelectAppletVersion::Version2:
    case ProfileSelectAppletVersion::Version3:
        ASSERT(user_config.size() == sizeof(UiSettings));
        std::memcpy(&config, user_config.data(), sizeof(UiSettings));
        break;
    default:
        UNIMPLEMENTED_MSG("Unknown profile_select_version = {}", profile_select_version);
        break;
    }
}

Result ProfileSelect::GetStatus() const {
    return status;
}

void ProfileSelect::ExecuteInteractive() {
    ASSERT_MSG(false, "Attempted to call interactive execution on non-interactive applet.");
}

void ProfileSelect::Execute() {
    if (complete) {
        PushOutData(std::make_shared<IStorage>(system, std::move(final_data)));
        Exit();
        return;
    }

    Core::Frontend::ProfileSelectParameters parameters{};

    switch (profile_select_version) {
    case ProfileSelectAppletVersion::Version1:
        parameters = {
            .mode = config_old.mode,
            .invalid_uid_list = config_old.invalid_uid_list,
            .display_options = config_old.display_options,
            .purpose = UserSelectionPurpose::General,
        };
        break;
    case ProfileSelectAppletVersion::Version2:
    case ProfileSelectAppletVersion::Version3:
        parameters = {
            .mode = config.mode,
            .invalid_uid_list = config.invalid_uid_list,
            .display_options = config.display_options,
            .purpose = config.purpose,
        };
        break;
    default:
        UNIMPLEMENTED_MSG("Unknown profile_select_version = {}", profile_select_version);
        break;
    }

    frontend.SelectProfile([this](std::optional<Common::UUID> uuid) { SelectionComplete(uuid); },
                           parameters);
}

void ProfileSelect::SelectionComplete(std::optional<Common::UUID> uuid) {
    UiReturnArg output{};

    if (uuid.has_value() && uuid->IsValid()) {
        output.result = 0;
        output.uuid_selected = *uuid;
    } else {
        status = Account::ResultCancelledByUser;
        output.result = Account::ResultCancelledByUser.raw;
        output.uuid_selected = Common::InvalidUUID;
    }

    final_data = std::vector<u8>(sizeof(UiReturnArg));
    std::memcpy(final_data.data(), &output, final_data.size());

    PushOutData(std::make_shared<IStorage>(system, std::move(final_data)));
    Exit();
}

Result ProfileSelect::RequestExit() {
    frontend.Close();
    R_SUCCEED();
}

} // namespace Service::AM::Frontend


code:
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#pragma once

#include <vector>

#include "common/common_funcs.h"
#include "common/uuid.h"
#include "core/hle/result.h"
#include "core/hle/service/am/frontend/applets.h"

namespace Core {
class System;
}

namespace Service::AM::Frontend {

enum class ProfileSelectAppletVersion : u32 {
    Version1 = 0x1,     // 1.0.0+
    Version2 = 0x10000, // 2.0.0+
    Version3 = 0x20000, // 6.0.0+
};

// This is nn::account::UiMode
enum class UiMode {
    UserSelector,
    UserCreator,
    EnsureNetworkServiceAccountAvailable,
    UserIconEditor,
    UserNicknameEditor,
    UserCreatorForStarter,
    NintendoAccountAuthorizationRequestContext,
    IntroduceExternalNetworkServiceAccount,
    IntroduceExternalNetworkServiceAccountForRegistration,
    NintendoAccountNnidLinker,
    LicenseRequirementsForNetworkService,
    LicenseRequirementsForNetworkServiceWithUserContextImpl,
    UserCreatorForImmediateNaLoginTest,
    UserQualificationPromoter,
};

// This is nn::account::UserSelectionPurpose
enum class UserSelectionPurpose {
    General,
    GameCardRegistration,
    EShopLaunch,
    EShopItemShow,
    PicturePost,
    NintendoAccountLinkage,
    SettingsUpdate,
    SaveDataDeletion,
    UserMigration,
    SaveDataTransfer,
};

// This is nn::account::NintendoAccountStartupDialogType
enum class NintendoAccountStartupDialogType {
    LoginAndCreate,
    Login,
    Create,
};

// This is nn::account::UserSelectionSettingsForSystemService
struct UserSelectionSettingsForSystemService {
    UserSelectionPurpose purpose;
    bool enable_user_creation;
    INSERT_PADDING_BYTES(0x3);
};
static_assert(sizeof(UserSelectionSettingsForSystemService) == 0x8,
              "UserSelectionSettingsForSystemService has incorrect size.");

struct UiSettingsDisplayOptions {
    bool is_network_service_account_required;
    bool is_skip_enabled;
    bool is_system_or_launcher;
    bool is_registration_permitted;
    bool show_skip_button;
    bool additional_select;
    bool show_user_selector;
    bool is_unqualified_user_selectable;
};
static_assert(sizeof(UiSettingsDisplayOptions) == 0x8,
              "UiSettingsDisplayOptions has incorrect size.");

struct UiSettingsV1 {
    UiMode mode;
    INSERT_PADDING_BYTES(0x4);
    std::array<Common::UUID, 8> invalid_uid_list;
    u64 application_id;
    UiSettingsDisplayOptions display_options;
};
static_assert(sizeof(UiSettingsV1) == 0x98, "UiSettings has incorrect size.");

// This is nn::account::UiSettings
struct UiSettings {
    UiMode mode;
    INSERT_PADDING_BYTES(0x4);
    std::array<Common::UUID, 8> invalid_uid_list;
    u64 application_id;
    UiSettingsDisplayOptions display_options;
    UserSelectionPurpose purpose;
    INSERT_PADDING_BYTES(0x4);
};
static_assert(sizeof(UiSettings) == 0xA0, "UiSettings has incorrect size.");

// This is nn::account::UiReturnArg
struct UiReturnArg {
    u64 result;
    Common::UUID uuid_selected;
};
static_assert(sizeof(UiReturnArg) == 0x18, "UiReturnArg has incorrect size.");

class ProfileSelect final : public FrontendApplet {
public:
    explicit ProfileSelect(Core::System& system_, std::shared_ptr<Applet> applet_,
                           LibraryAppletMode applet_mode_,
                           const Core::Frontend::ProfileSelectApplet& frontend_);
    ~ProfileSelect() override;

    void Initialize() override;

    Result GetStatus() const override;
    void ExecuteInteractive() override;
    void Execute() override;
    Result RequestExit() override;

    void SelectionComplete(std::optional<Common::UUID> uuid);

private:
    const Core::Frontend::ProfileSelectApplet& frontend;

    UiSettings config;
    UiSettingsV1 config_old;
    ProfileSelectAppletVersion profile_select_version;

    bool complete = false;
    Result status = ResultSuccess;
    std::vector<u8> final_data;
};

} // namespace Service::AM::Frontend