early-access version 4091

This commit is contained in:
pineappleEA 2024-01-26 18:45:02 +01:00
parent 07632da5b9
commit 025481aeed
148 changed files with 11777 additions and 3914 deletions

View File

@ -1,7 +1,7 @@
yuzu emulator early access
=============
This is the source code for early-access 4090.
This is the source code for early-access 4091.
## Legal Notice

View File

@ -153,7 +153,13 @@ class HomeSettingsFragment : Fragment() {
cancellable = true
) { progressCallback, _ ->
val result = NativeLibrary.verifyInstalledContents(progressCallback)
return@newInstance if (result.isEmpty()) {
return@newInstance if (progressCallback.invoke(100, 100)) {
// Invoke the progress callback to check if the process was cancelled
MessageDialogFragment.newInstance(
titleId = R.string.verify_no_result,
descriptionId = R.string.verify_no_result_description
)
} else if (result.isEmpty()) {
MessageDialogFragment.newInstance(
titleId = R.string.verify_success,
descriptionId = R.string.operation_completed_successfully

View File

@ -82,7 +82,7 @@ AndroidKeyboard::ResultData AndroidKeyboard::ResultData::CreateFromFrontend(jobj
const jstring string = reinterpret_cast<jstring>(env->GetObjectField(
object, env->GetFieldID(s_keyboard_data_class, "text", "Ljava/lang/String;")));
return ResultData{GetJString(env, string),
static_cast<Service::AM::Applets::SwkbdResult>(env->GetIntField(
static_cast<Service::AM::Frontend::SwkbdResult>(env->GetIntField(
object, env->GetFieldID(s_keyboard_data_class, "result", "I")))};
}
@ -149,7 +149,7 @@ void AndroidKeyboard::ShowNormalKeyboard() const {
}
void AndroidKeyboard::ShowTextCheckDialog(
Service::AM::Applets::SwkbdTextCheckResult text_check_result,
Service::AM::Frontend::SwkbdTextCheckResult text_check_result,
std::u16string text_check_message) const {
LOG_WARNING(Frontend, "(STUBBED) called, backend requested to show the text check dialog.");
}
@ -204,7 +204,7 @@ void AndroidKeyboard::InlineTextChanged(
"\ncursor_position={}",
Common::UTF16ToUTF8(text_parameters.input_text), text_parameters.cursor_position);
submit_inline_callback(Service::AM::Applets::SwkbdReplyType::ChangedString,
submit_inline_callback(Service::AM::Frontend::SwkbdReplyType::ChangedString,
text_parameters.input_text, text_parameters.cursor_position);
}
@ -219,7 +219,7 @@ void AndroidKeyboard::SubmitInlineKeyboardText(std::u16string submitted_text) {
m_current_text += submitted_text;
submit_inline_callback(Service::AM::Applets::SwkbdReplyType::ChangedString, m_current_text,
submit_inline_callback(Service::AM::Frontend::SwkbdReplyType::ChangedString, m_current_text,
m_current_text.size());
}
@ -236,12 +236,12 @@ void AndroidKeyboard::SubmitInlineKeyboardInput(int key_code) {
case KEYCODE_BACK:
case KEYCODE_ENTER:
m_is_inline_active = false;
submit_inline_callback(Service::AM::Applets::SwkbdReplyType::DecidedEnter, m_current_text,
submit_inline_callback(Service::AM::Frontend::SwkbdReplyType::DecidedEnter, m_current_text,
static_cast<s32>(m_current_text.size()));
break;
case KEYCODE_DEL:
m_current_text.pop_back();
submit_inline_callback(Service::AM::Applets::SwkbdReplyType::ChangedString, m_current_text,
submit_inline_callback(Service::AM::Frontend::SwkbdReplyType::ChangedString, m_current_text,
m_current_text.size());
break;
}

View File

@ -24,7 +24,7 @@ public:
void ShowNormalKeyboard() const override;
void ShowTextCheckDialog(Service::AM::Applets::SwkbdTextCheckResult text_check_result,
void ShowTextCheckDialog(Service::AM::Frontend::SwkbdTextCheckResult text_check_result,
std::u16string text_check_message) const override;
void ShowInlineKeyboard(
@ -45,7 +45,7 @@ private:
static ResultData CreateFromFrontend(jobject object);
std::string text;
Service::AM::Applets::SwkbdResult result{};
Service::AM::Frontend::SwkbdResult result{};
};
void SubmitNormalText(const ResultData& result) const;

View File

@ -42,14 +42,15 @@
#include "core/frontend/applets/cabinet.h"
#include "core/frontend/applets/controller.h"
#include "core/frontend/applets/error.h"
#include "core/frontend/applets/general_frontend.h"
#include "core/frontend/applets/general.h"
#include "core/frontend/applets/mii_edit.h"
#include "core/frontend/applets/profile_select.h"
#include "core/frontend/applets/software_keyboard.h"
#include "core/frontend/applets/web_browser.h"
#include "core/hle/service/am/applet_ae.h"
#include "core/hle/service/am/applet_manager.h"
#include "core/hle/service/am/applet_oe.h"
#include "core/hle/service/am/applets/applets.h"
#include "core/hle/service/am/frontend/applets.h"
#include "core/hle/service/filesystem/filesystem.h"
#include "core/loader/loader.h"
#include "frontend_common/config.h"
@ -222,7 +223,7 @@ Core::SystemResultStatus EmulationSession::InitializeEmulation(const std::string
m_system.ApplySettings();
Settings::LogSettings();
m_system.HIDCore().ReloadInputDevices();
m_system.SetAppletFrontendSet({
m_system.SetFrontendAppletSet({
nullptr, // Amiibo Settings
nullptr, // Controller Selector
nullptr, // Error Display
@ -238,7 +239,10 @@ Core::SystemResultStatus EmulationSession::InitializeEmulation(const std::string
ConfigureFilesystemProvider(filepath);
// Load the ROM.
m_load_result = m_system.Load(EmulationSession::GetInstance().Window(), filepath);
Service::AM::FrontendAppletParameters params{
.applet_id = Service::AM::AppletId::Application,
};
m_load_result = m_system.Load(EmulationSession::GetInstance().Window(), filepath, params);
if (m_load_result != Core::SystemResultStatus::Success) {
return m_load_result;
}
@ -755,13 +759,13 @@ jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getAppletLaunchPath(JNIEnv* env, j
void Java_org_yuzu_yuzu_1emu_NativeLibrary_setCurrentAppletId(JNIEnv* env, jclass clazz,
jint jappletId) {
EmulationSession::GetInstance().System().GetAppletManager().SetCurrentAppletId(
static_cast<Service::AM::Applets::AppletId>(jappletId));
EmulationSession::GetInstance().System().GetFrontendAppletHolder().SetCurrentAppletId(
static_cast<Service::AM::AppletId>(jappletId));
}
void Java_org_yuzu_yuzu_1emu_NativeLibrary_setCabinetMode(JNIEnv* env, jclass clazz,
jint jcabinetMode) {
EmulationSession::GetInstance().System().GetAppletManager().SetCabinetMode(
EmulationSession::GetInstance().System().GetFrontendAppletHolder().SetCabinetMode(
static_cast<Service::NFP::CabinetMode>(jcabinetMode));
}

View File

@ -176,8 +176,8 @@ add_library(core STATIC
frontend/applets/controller.h
frontend/applets/error.cpp
frontend/applets/error.h
frontend/applets/general_frontend.cpp
frontend/applets/general_frontend.h
frontend/applets/general.cpp
frontend/applets/general.h
frontend/applets/mii_edit.cpp
frontend/applets/mii_edit.h
frontend/applets/profile_select.cpp
@ -390,39 +390,101 @@ add_library(core STATIC
hle/service/acc/errors.h
hle/service/acc/profile_manager.cpp
hle/service/acc/profile_manager.h
hle/service/am/frontend/applet_cabinet.cpp
hle/service/am/frontend/applet_cabinet.h
hle/service/am/frontend/applet_controller.cpp
hle/service/am/frontend/applet_controller.h
hle/service/am/frontend/applet_error.cpp
hle/service/am/frontend/applet_error.h
hle/service/am/frontend/applet_general.cpp
hle/service/am/frontend/applet_general.h
hle/service/am/frontend/applet_mii_edit.cpp
hle/service/am/frontend/applet_mii_edit.h
hle/service/am/frontend/applet_mii_edit_types.h
hle/service/am/frontend/applet_profile_select.cpp
hle/service/am/frontend/applet_profile_select.h
hle/service/am/frontend/applet_software_keyboard.cpp
hle/service/am/frontend/applet_software_keyboard.h
hle/service/am/frontend/applet_software_keyboard_types.h
hle/service/am/frontend/applet_web_browser.cpp
hle/service/am/frontend/applet_web_browser.h
hle/service/am/frontend/applet_web_browser_types.h
hle/service/am/frontend/applets.cpp
hle/service/am/frontend/applets.h
hle/service/am/am.cpp
hle/service/am/am.h
hle/service/am/am_results.h
hle/service/am/am_types.h
hle/service/am/applet.cpp
hle/service/am/applet.h
hle/service/am/applet_ae.cpp
hle/service/am/applet_ae.h
hle/service/am/applet_manager.cpp
hle/service/am/applet_data_broker.cpp
hle/service/am/applet_data_broker.h
hle/service/am/applet_manager.h
hle/service/am/applet_oe.cpp
hle/service/am/applet_oe.h
hle/service/am/applets/applet_cabinet.cpp
hle/service/am/applets/applet_cabinet.h
hle/service/am/applets/applet_controller.cpp
hle/service/am/applets/applet_controller.h
hle/service/am/applets/applet_error.cpp
hle/service/am/applets/applet_error.h
hle/service/am/applets/applet_general_backend.cpp
hle/service/am/applets/applet_general_backend.h
hle/service/am/applets/applet_mii_edit.cpp
hle/service/am/applets/applet_mii_edit.h
hle/service/am/applets/applet_mii_edit_types.h
hle/service/am/applets/applet_profile_select.cpp
hle/service/am/applets/applet_profile_select.h
hle/service/am/applets/applet_software_keyboard.cpp
hle/service/am/applets/applet_software_keyboard.h
hle/service/am/applets/applet_software_keyboard_types.h
hle/service/am/applets/applet_web_browser.cpp
hle/service/am/applets/applet_web_browser.h
hle/service/am/applets/applet_web_browser_types.h
hle/service/am/applets/applets.cpp
hle/service/am/applets/applets.h
hle/service/am/applet_common_functions.cpp
hle/service/am/applet_common_functions.h
hle/service/am/applet_message_queue.cpp
hle/service/am/applet_message_queue.h
hle/service/am/application_creator.cpp
hle/service/am/application_creator.h
hle/service/am/application_functions.cpp
hle/service/am/application_functions.h
hle/service/am/application_proxy.cpp
hle/service/am/application_proxy.h
hle/service/am/audio_controller.cpp
hle/service/am/audio_controller.h
hle/service/am/common_state_getter.cpp
hle/service/am/common_state_getter.h
hle/service/am/debug_functions.cpp
hle/service/am/debug_functions.h
hle/service/am/display_controller.cpp
hle/service/am/display_controller.h
hle/service/am/global_state_controller.cpp
hle/service/am/global_state_controller.h
hle/service/am/hid_registration.cpp
hle/service/am/hid_registration.h
hle/service/am/home_menu_functions.cpp
hle/service/am/home_menu_functions.h
hle/service/am/idle.cpp
hle/service/am/idle.h
hle/service/am/library_applet_accessor.cpp
hle/service/am/library_applet_accessor.h
hle/service/am/library_applet_creator.cpp
hle/service/am/library_applet_creator.h
hle/service/am/library_applet_proxy.cpp
hle/service/am/library_applet_proxy.h
hle/service/am/library_applet_self_accessor.cpp
hle/service/am/library_applet_self_accessor.h
hle/service/am/library_applet_storage.cpp
hle/service/am/library_applet_storage.h
hle/service/am/lock_accessor.cpp
hle/service/am/lock_accessor.h
hle/service/am/managed_layer_holder.cpp
hle/service/am/managed_layer_holder.h
hle/service/am/omm.cpp
hle/service/am/omm.h
hle/service/am/process_winding_controller.cpp
hle/service/am/process_winding_controller.h
hle/service/am/process.cpp
hle/service/am/process.h
hle/service/am/self_controller.cpp
hle/service/am/self_controller.h
hle/service/am/system_applet_proxy.cpp
hle/service/am/system_applet_proxy.h
hle/service/am/system_buffer_manager.cpp
hle/service/am/system_buffer_manager.h
hle/service/am/spsm.cpp
hle/service/am/spsm.h
hle/service/am/storage_accessor.cpp
hle/service/am/storage_accessor.h
hle/service/am/storage.cpp
hle/service/am/storage.h
hle/service/am/window_controller.cpp
hle/service/am/window_controller.h
hle/service/aoc/aoc_u.cpp
hle/service/aoc/aoc_u.h
hle/service/apm/apm.cpp
@ -486,6 +548,8 @@ add_library(core STATIC
hle/service/es/es.h
hle/service/eupld/eupld.cpp
hle/service/eupld/eupld.h
hle/service/event.cpp
hle/service/event.h
hle/service/fatal/fatal.cpp
hle/service/fatal/fatal.h
hle/service/fatal/fatal_p.cpp

View File

@ -36,7 +36,8 @@
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/physical_core.h"
#include "core/hle/service/acc/profile_manager.h"
#include "core/hle/service/am/applets/applets.h"
#include "core/hle/service/am/applet_manager.h"
#include "core/hle/service/am/frontend/applets.h"
#include "core/hle/service/apm/apm_controller.h"
#include "core/hle/service/filesystem/filesystem.h"
#include "core/hle/service/glue/glue_manager.h"
@ -135,8 +136,8 @@ FileSys::VirtualFile GetGameFileFromPath(const FileSys::VirtualFilesystem& vfs,
struct System::Impl {
explicit Impl(System& system)
: kernel{system}, fs_controller{system}, hid_core{}, room_network{},
cpu_manager{system}, reporter{system}, applet_manager{system}, profile_manager{} {}
: kernel{system}, fs_controller{system}, hid_core{}, room_network{}, cpu_manager{system},
reporter{system}, applet_manager{system}, frontend_applets{system}, profile_manager{} {}
void Initialize(System& system) {
device_memory = std::make_unique<Core::DeviceMemory>();
@ -157,7 +158,7 @@ struct System::Impl {
}
// Create default implementations of applets if one is not provided.
applet_manager.SetDefaultAppletsIfMissing();
frontend_applets.SetDefaultAppletsIfMissing();
is_async_gpu = Settings::values.use_asynchronous_gpu_emulation.GetValue();
@ -330,16 +331,27 @@ struct System::Impl {
}
SystemResultStatus Load(System& system, Frontend::EmuWindow& emu_window,
const std::string& filepath, u64 program_id,
std::size_t program_index) {
const std::string& filepath,
Service::AM::FrontendAppletParameters& params) {
app_loader = Loader::GetLoader(system, GetGameFileFromPath(virtual_filesystem, filepath),
program_id, program_index);
params.program_id, params.program_index);
if (!app_loader) {
LOG_CRITICAL(Core, "Failed to obtain loader for {}!", filepath);
return SystemResultStatus::ErrorGetLoader;
}
if (app_loader->ReadProgramId(params.program_id) != Loader::ResultStatus::Success) {
LOG_ERROR(Core, "Failed to find title id for ROM!");
}
std::string name = "Unknown program";
if (app_loader->ReadTitle(name) != Loader::ResultStatus::Success) {
LOG_ERROR(Core, "Failed to read title for ROM!");
}
LOG_INFO(Core, "Loading {} ({})", name, params.program_id);
InitializeKernel(system);
// Create the application process.
@ -373,9 +385,14 @@ struct System::Impl {
cheat_engine->Initialize();
}
// Register with applet manager.
applet_manager.CreateAndInsertByFrontendAppletParameters(main_process->GetProcessId(),
params);
// All threads are started, begin main process execution, now that we're in the clear.
main_process->Run(load_parameters->main_thread_priority,
load_parameters->main_thread_stack_size);
main_process->Close();
if (Settings::values.gamecard_inserted) {
if (Settings::values.gamecard_current_game) {
@ -386,21 +403,13 @@ struct System::Impl {
}
}
if (app_loader->ReadProgramId(program_id) != Loader::ResultStatus::Success) {
LOG_ERROR(Core, "Failed to find title id for ROM (Error {})", load_result);
}
perf_stats = std::make_unique<PerfStats>(program_id);
perf_stats = std::make_unique<PerfStats>(params.program_id);
// Reset counters and set time origin to current frame
GetAndResetPerfStats();
perf_stats->BeginSystemFrame();
std::string name = "Unknown Game";
if (app_loader->ReadTitle(name) != Loader::ResultStatus::Success) {
LOG_ERROR(Core, "Failed to read title for ROM (Error {})", load_result);
}
std::string title_version;
const FileSys::PatchManager pm(program_id, system.GetFileSystemController(),
const FileSys::PatchManager pm(params.program_id, system.GetFileSystemController(),
system.GetContentProvider());
const auto metadata = pm.GetControlMetadata();
if (metadata.first != nullptr) {
@ -409,14 +418,15 @@ struct System::Impl {
if (auto room_member = room_network.GetRoomMember().lock()) {
Network::GameInfo game_info;
game_info.name = name;
game_info.id = program_id;
game_info.id = params.program_id;
game_info.version = title_version;
room_member->SendGameInfo(game_info);
}
// Workarounds:
// Activate this in Super Smash Brothers Ultimate, it only affects AMD cards using AMDVLK
Settings::values.renderer_amdvlk_depth_bias_workaround = program_id == 0x1006A800016E000ULL;
Settings::values.renderer_amdvlk_depth_bias_workaround =
params.program_id == 0x1006A800016E000ULL;
status = SystemResultStatus::Success;
return status;
@ -455,6 +465,7 @@ struct System::Impl {
}
kernel.CloseServices();
kernel.ShutdownCores();
applet_manager.Reset();
services.reset();
service_manager.reset();
fs_controller.Reset();
@ -566,8 +577,9 @@ struct System::Impl {
std::unique_ptr<Tools::RenderdocAPI> renderdoc_api;
/// Frontend applets
Service::AM::Applets::AppletManager applet_manager;
/// Applets
Service::AM::AppletManager applet_manager;
Service::AM::Frontend::FrontendAppletHolder frontend_applets;
/// APM (Performance) services
Service::APM::Controller apm_controller{core_timing};
@ -680,8 +692,8 @@ void System::InitializeDebugger() {
}
SystemResultStatus System::Load(Frontend::EmuWindow& emu_window, const std::string& filepath,
u64 program_id, std::size_t program_index) {
return impl->Load(*this, emu_window, filepath, program_id, program_index);
Service::AM::FrontendAppletParameters& params) {
return impl->Load(*this, emu_window, filepath, params);
}
bool System::IsPoweredOn() const {
@ -871,19 +883,19 @@ void System::RegisterCheatList(const std::vector<Memory::CheatEntry>& list,
impl->cheat_engine->SetMainMemoryParameters(main_region_begin, main_region_size);
}
void System::SetAppletFrontendSet(Service::AM::Applets::AppletFrontendSet&& set) {
impl->applet_manager.SetAppletFrontendSet(std::move(set));
void System::SetFrontendAppletSet(Service::AM::Frontend::FrontendAppletSet&& set) {
impl->frontend_applets.SetFrontendAppletSet(std::move(set));
}
void System::SetDefaultAppletFrontendSet() {
impl->applet_manager.SetDefaultAppletFrontendSet();
Service::AM::Frontend::FrontendAppletHolder& System::GetFrontendAppletHolder() {
return impl->frontend_applets;
}
Service::AM::Applets::AppletManager& System::GetAppletManager() {
return impl->applet_manager;
const Service::AM::Frontend::FrontendAppletHolder& System::GetFrontendAppletHolder() const {
return impl->frontend_applets;
}
const Service::AM::Applets::AppletManager& System::GetAppletManager() const {
Service::AM::AppletManager& System::GetAppletManager() {
return impl->applet_manager;
}

View File

@ -50,10 +50,15 @@ namespace Account {
class ProfileManager;
} // namespace Account
namespace AM::Applets {
struct AppletFrontendSet;
namespace AM {
struct FrontendAppletParameters;
class AppletManager;
} // namespace AM::Applets
} // namespace AM
namespace AM::Frontend {
struct FrontendAppletSet;
class FrontendAppletHolder;
} // namespace AM::Frontend
namespace APM {
class Controller;
@ -203,8 +208,8 @@ public:
* @returns SystemResultStatus code, indicating if the operation succeeded.
*/
[[nodiscard]] SystemResultStatus Load(Frontend::EmuWindow& emu_window,
const std::string& filepath, u64 program_id = 0,
std::size_t program_index = 0);
const std::string& filepath,
Service::AM::FrontendAppletParameters& params);
/**
* Indicates if the emulated system is powered on (all subsystems initialized and able to run an
@ -344,11 +349,13 @@ public:
const std::array<u8, 0x20>& build_id, u64 main_region_begin,
u64 main_region_size);
void SetAppletFrontendSet(Service::AM::Applets::AppletFrontendSet&& set);
void SetDefaultAppletFrontendSet();
void SetFrontendAppletSet(Service::AM::Frontend::FrontendAppletSet&& set);
[[nodiscard]] Service::AM::Applets::AppletManager& GetAppletManager();
[[nodiscard]] const Service::AM::Applets::AppletManager& GetAppletManager() const;
[[nodiscard]] Service::AM::Frontend::FrontendAppletHolder& GetFrontendAppletHolder();
[[nodiscard]] const Service::AM::Frontend::FrontendAppletHolder& GetFrontendAppletHolder()
const;
[[nodiscard]] Service::AM::AppletManager& GetAppletManager();
void SetContentProvider(std::unique_ptr<FileSys::ContentProviderUnion> provider);

View File

@ -20,6 +20,10 @@
#define stat _stat64
#endif
#ifdef ANDROID
#include "common/fs/fs_android.h"
#endif
namespace FileSys {
namespace FS = Common::FS;
@ -274,6 +278,11 @@ RealVfsFile::~RealVfsFile() {
}
std::string RealVfsFile::GetName() const {
#ifdef ANDROID
if (path[0] != '/') {
return FS::Android::GetFilename(path);
}
#endif
return path_components.empty() ? "" : std::string(path_components.back());
}

View File

@ -0,0 +1,59 @@
// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "common/logging/log.h"
#include "core/frontend/applets/general.h"
namespace Core::Frontend {
ParentalControlsApplet::~ParentalControlsApplet() = default;
DefaultParentalControlsApplet::~DefaultParentalControlsApplet() = default;
void DefaultParentalControlsApplet::Close() const {}
void DefaultParentalControlsApplet::VerifyPIN(std::function<void(bool)> finished,
bool suspend_future_verification_temporarily) {
LOG_INFO(Service_AM,
"Application requested frontend to verify PIN (normal), "
"suspend_future_verification_temporarily={}, verifying as correct.",
suspend_future_verification_temporarily);
finished(true);
}
void DefaultParentalControlsApplet::VerifyPINForSettings(std::function<void(bool)> finished) {
LOG_INFO(Service_AM,
"Application requested frontend to verify PIN (settings), verifying as correct.");
finished(true);
}
void DefaultParentalControlsApplet::RegisterPIN(std::function<void()> finished) {
LOG_INFO(Service_AM, "Application requested frontend to register new PIN");
finished();
}
void DefaultParentalControlsApplet::ChangePIN(std::function<void()> finished) {
LOG_INFO(Service_AM, "Application requested frontend to change PIN to new value");
finished();
}
PhotoViewerApplet::~PhotoViewerApplet() = default;
DefaultPhotoViewerApplet::~DefaultPhotoViewerApplet() = default;
void DefaultPhotoViewerApplet::Close() const {}
void DefaultPhotoViewerApplet::ShowPhotosForApplication(u64 title_id,
std::function<void()> finished) const {
LOG_INFO(Service_AM,
"Application requested frontend to display stored photos for title_id={:016X}",
title_id);
finished();
}
void DefaultPhotoViewerApplet::ShowAllPhotos(std::function<void()> finished) const {
LOG_INFO(Service_AM, "Application requested frontend to display all stored photos.");
finished();
}
} // namespace Core::Frontend

View File

@ -0,0 +1,63 @@
// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <functional>
#include "common/common_types.h"
#include "core/frontend/applets/applet.h"
namespace Core::Frontend {
class ParentalControlsApplet : public Applet {
public:
virtual ~ParentalControlsApplet();
// Prompts the user to enter a PIN and calls the callback with whether or not it matches the
// correct PIN. If the bool is passed, and the PIN was recently entered correctly, the frontend
// should not prompt and simply return true.
virtual void VerifyPIN(std::function<void(bool)> finished,
bool suspend_future_verification_temporarily) = 0;
// Prompts the user to enter a PIN and calls the callback for correctness. Frontends can
// optionally alert the user that this is to change parental controls settings.
virtual void VerifyPINForSettings(std::function<void(bool)> finished) = 0;
// Prompts the user to create a new PIN for pctl and stores it with the service.
virtual void RegisterPIN(std::function<void()> finished) = 0;
// Prompts the user to verify the current PIN and then store a new one into pctl.
virtual void ChangePIN(std::function<void()> finished) = 0;
};
class DefaultParentalControlsApplet final : public ParentalControlsApplet {
public:
~DefaultParentalControlsApplet() override;
void Close() const override;
void VerifyPIN(std::function<void(bool)> finished,
bool suspend_future_verification_temporarily) override;
void VerifyPINForSettings(std::function<void(bool)> finished) override;
void RegisterPIN(std::function<void()> finished) override;
void ChangePIN(std::function<void()> finished) override;
};
class PhotoViewerApplet : public Applet {
public:
virtual ~PhotoViewerApplet();
virtual void ShowPhotosForApplication(u64 title_id, std::function<void()> finished) const = 0;
virtual void ShowAllPhotos(std::function<void()> finished) const = 0;
};
class DefaultPhotoViewerApplet final : public PhotoViewerApplet {
public:
~DefaultPhotoViewerApplet() override;
void Close() const override;
void ShowPhotosForApplication(u64 title_id, std::function<void()> finished) const override;
void ShowAllPhotos(std::function<void()> finished) const override;
};
} // namespace Core::Frontend

View File

@ -8,15 +8,15 @@
#include "common/uuid.h"
#include "core/frontend/applets/applet.h"
#include "core/hle/service/am/applets/applet_profile_select.h"
#include "core/hle/service/am/frontend/applet_profile_select.h"
namespace Core::Frontend {
struct ProfileSelectParameters {
Service::AM::Applets::UiMode mode;
Service::AM::Frontend::UiMode mode;
std::array<Common::UUID, 8> invalid_uid_list;
Service::AM::Applets::UiSettingsDisplayOptions display_options;
Service::AM::Applets::UserSelectionPurpose purpose;
Service::AM::Frontend::UiSettingsDisplayOptions display_options;
Service::AM::Frontend::UserSelectionPurpose purpose;
};
class ProfileSelectApplet : public Applet {

View File

@ -69,7 +69,7 @@ void DefaultSoftwareKeyboardApplet::ShowNormalKeyboard() const {
}
void DefaultSoftwareKeyboardApplet::ShowTextCheckDialog(
Service::AM::Applets::SwkbdTextCheckResult text_check_result,
Service::AM::Frontend::SwkbdTextCheckResult text_check_result,
std::u16string text_check_message) const {
LOG_WARNING(Service_AM, "(STUBBED) called, backend requested to show the text check dialog.");
}
@ -118,7 +118,7 @@ void DefaultSoftwareKeyboardApplet::InlineTextChanged(InlineTextParameters text_
"\ncursor_position={}",
Common::UTF16ToUTF8(text_parameters.input_text), text_parameters.cursor_position);
submit_inline_callback(Service::AM::Applets::SwkbdReplyType::ChangedString,
submit_inline_callback(Service::AM::Frontend::SwkbdReplyType::ChangedString,
text_parameters.input_text, text_parameters.cursor_position);
}
@ -127,22 +127,22 @@ void DefaultSoftwareKeyboardApplet::ExitKeyboard() const {
}
void DefaultSoftwareKeyboardApplet::SubmitNormalText(std::u16string text) const {
submit_normal_callback(Service::AM::Applets::SwkbdResult::Ok, text, true);
submit_normal_callback(Service::AM::Frontend::SwkbdResult::Ok, text, true);
}
void DefaultSoftwareKeyboardApplet::SubmitInlineText(std::u16string_view text) const {
std::this_thread::sleep_for(std::chrono::milliseconds(500));
for (std::size_t index = 0; index < text.size(); ++index) {
submit_inline_callback(Service::AM::Applets::SwkbdReplyType::ChangedString,
submit_inline_callback(Service::AM::Frontend::SwkbdReplyType::ChangedString,
std::u16string(text.data(), text.data() + index + 1),
static_cast<s32>(index) + 1);
std::this_thread::sleep_for(std::chrono::milliseconds(250));
}
submit_inline_callback(Service::AM::Applets::SwkbdReplyType::DecidedEnter, std::u16string(text),
static_cast<s32>(text.size()));
submit_inline_callback(Service::AM::Frontend::SwkbdReplyType::DecidedEnter,
std::u16string(text), static_cast<s32>(text.size()));
}
} // namespace Core::Frontend

View File

@ -8,7 +8,7 @@
#include "common/common_types.h"
#include "core/frontend/applets/applet.h"
#include "core/hle/service/am/applets/applet_software_keyboard_types.h"
#include "core/hle/service/am/frontend/applet_software_keyboard_types.h"
namespace Core::Frontend {
@ -23,10 +23,10 @@ struct KeyboardInitializeParameters {
u32 max_text_length;
u32 min_text_length;
s32 initial_cursor_position;
Service::AM::Applets::SwkbdType type;
Service::AM::Applets::SwkbdPasswordMode password_mode;
Service::AM::Applets::SwkbdTextDrawType text_draw_type;
Service::AM::Applets::SwkbdKeyDisableFlags key_disable_flags;
Service::AM::Frontend::SwkbdType type;
Service::AM::Frontend::SwkbdPasswordMode password_mode;
Service::AM::Frontend::SwkbdTextDrawType text_draw_type;
Service::AM::Frontend::SwkbdKeyDisableFlags key_disable_flags;
bool use_blur_background;
bool enable_backspace_button;
bool enable_return_button;
@ -40,8 +40,8 @@ struct InlineAppearParameters {
f32 key_top_scale_y;
f32 key_top_translate_x;
f32 key_top_translate_y;
Service::AM::Applets::SwkbdType type;
Service::AM::Applets::SwkbdKeyDisableFlags key_disable_flags;
Service::AM::Frontend::SwkbdType type;
Service::AM::Frontend::SwkbdKeyDisableFlags key_disable_flags;
bool key_top_as_floating;
bool enable_backspace_button;
bool enable_return_button;
@ -56,9 +56,9 @@ struct InlineTextParameters {
class SoftwareKeyboardApplet : public Applet {
public:
using SubmitInlineCallback =
std::function<void(Service::AM::Applets::SwkbdReplyType, std::u16string, s32)>;
std::function<void(Service::AM::Frontend::SwkbdReplyType, std::u16string, s32)>;
using SubmitNormalCallback =
std::function<void(Service::AM::Applets::SwkbdResult, std::u16string, bool)>;
std::function<void(Service::AM::Frontend::SwkbdResult, std::u16string, bool)>;
virtual ~SoftwareKeyboardApplet();
@ -69,7 +69,7 @@ public:
virtual void ShowNormalKeyboard() const = 0;
virtual void ShowTextCheckDialog(Service::AM::Applets::SwkbdTextCheckResult text_check_result,
virtual void ShowTextCheckDialog(Service::AM::Frontend::SwkbdTextCheckResult text_check_result,
std::u16string text_check_message) const = 0;
virtual void ShowInlineKeyboard(InlineAppearParameters appear_parameters) const = 0;
@ -93,7 +93,7 @@ public:
void ShowNormalKeyboard() const override;
void ShowTextCheckDialog(Service::AM::Applets::SwkbdTextCheckResult text_check_result,
void ShowTextCheckDialog(Service::AM::Frontend::SwkbdTextCheckResult text_check_result,
std::u16string text_check_message) const override;
void ShowInlineKeyboard(InlineAppearParameters appear_parameters) const override;

View File

@ -18,7 +18,7 @@ void DefaultWebBrowserApplet::OpenLocalWebPage(const std::string& local_url,
LOG_WARNING(Service_AM, "(STUBBED) called, backend requested to open local web page at {}",
local_url);
callback(Service::AM::Applets::WebExitReason::WindowClosed, "http://localhost/");
callback(Service::AM::Frontend::WebExitReason::WindowClosed, "http://localhost/");
}
void DefaultWebBrowserApplet::OpenExternalWebPage(const std::string& external_url,
@ -26,7 +26,7 @@ void DefaultWebBrowserApplet::OpenExternalWebPage(const std::string& external_ur
LOG_WARNING(Service_AM, "(STUBBED) called, backend requested to open external web page at {}",
external_url);
callback(Service::AM::Applets::WebExitReason::WindowClosed, "http://localhost/");
callback(Service::AM::Frontend::WebExitReason::WindowClosed, "http://localhost/");
}
} // namespace Core::Frontend

View File

@ -6,7 +6,7 @@
#include <functional>
#include "core/frontend/applets/applet.h"
#include "core/hle/service/am/applets/applet_web_browser_types.h"
#include "core/hle/service/am/frontend/applet_web_browser_types.h"
namespace Core::Frontend {
@ -14,7 +14,7 @@ class WebBrowserApplet : public Applet {
public:
using ExtractROMFSCallback = std::function<void()>;
using OpenWebPageCallback =
std::function<void(Service::AM::Applets::WebExitReason, std::string)>;
std::function<void(Service::AM::Frontend::WebExitReason, std::string)>;
virtual ~WebBrowserApplet();

View File

@ -97,8 +97,14 @@ struct KernelCore::Impl {
RegisterHostThread(nullptr);
}
void TerminateApplicationProcess() {
application_process.load()->Terminate();
void TerminateAllProcesses() {
std::scoped_lock lk{process_list_lock};
for (auto& process : process_list) {
process->Terminate();
process->Close();
process = nullptr;
}
process_list.clear();
}
void Shutdown() {
@ -107,18 +113,9 @@ struct KernelCore::Impl {
CloseServices();
auto* old_process = application_process.exchange(nullptr);
if (old_process) {
old_process->Close();
}
{
std::scoped_lock lk{process_list_lock};
for (auto* const process : process_list) {
process->Terminate();
process->Close();
}
process_list.clear();
if (application_process) {
application_process->Close();
application_process = nullptr;
}
next_object_id = 0;
@ -354,6 +351,7 @@ struct KernelCore::Impl {
void MakeApplicationProcess(KProcess* process) {
application_process = process;
application_process->Open();
}
static inline thread_local u8 host_thread_id = UINT8_MAX;
@ -779,7 +777,7 @@ struct KernelCore::Impl {
// Lists all processes that exist in the current session.
std::mutex process_list_lock;
std::vector<KProcess*> process_list;
std::atomic<KProcess*> application_process{};
KProcess* application_process{};
std::unique_ptr<Kernel::GlobalSchedulerContext> global_scheduler_context;
std::unique_ptr<Kernel::KHardwareTimer> hardware_timer;
@ -1243,7 +1241,7 @@ void KernelCore::SuspendApplication(bool suspended) {
}
void KernelCore::ShutdownCores() {
impl->TerminateApplicationProcess();
impl->TerminateAllProcesses();
KScopedSchedulerLock lk{*this};

File diff suppressed because it is too large Load Diff

View File

@ -1,20 +1,11 @@
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <chrono>
#include <memory>
#include <queue>
#include "core/hle/service/kernel_helpers.h"
#include "core/hle/service/service.h"
namespace Kernel {
class KernelCore;
class KReadableEvent;
class KTransferMemory;
} // namespace Kernel
namespace Core {
class System;
}
namespace Service::Nvnflinger {
class Nvnflinger;
@ -22,443 +13,6 @@ class Nvnflinger;
namespace Service::AM {
class AppletMessageQueue {
public:
// This is nn::am::AppletMessage
enum class AppletMessage : u32 {
None = 0,
ChangeIntoForeground = 1,
ChangeIntoBackground = 2,
Exit = 4,
ApplicationExited = 6,
FocusStateChanged = 15,
Resume = 16,
DetectShortPressingHomeButton = 20,
DetectLongPressingHomeButton = 21,
DetectShortPressingPowerButton = 22,
DetectMiddlePressingPowerButton = 23,
DetectLongPressingPowerButton = 24,
RequestToPrepareSleep = 25,
FinishedSleepSequence = 26,
SleepRequiredByHighTemperature = 27,
SleepRequiredByLowBattery = 28,
AutoPowerDown = 29,
OperationModeChanged = 30,
PerformanceModeChanged = 31,
DetectReceivingCecSystemStandby = 32,
SdCardRemoved = 33,
LaunchApplicationRequested = 50,
RequestToDisplay = 51,
ShowApplicationLogo = 55,
HideApplicationLogo = 56,
ForceHideApplicationLogo = 57,
FloatingApplicationDetected = 60,
DetectShortPressingCaptureButton = 90,
AlbumScreenShotTaken = 92,
AlbumRecordingSaved = 93,
};
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;
std::queue<AppletMessage> messages;
};
class IWindowController final : public ServiceFramework<IWindowController> {
public:
explicit IWindowController(Core::System& system_);
~IWindowController() override;
private:
void GetAppletResourceUserId(HLERequestContext& ctx);
void GetAppletResourceUserIdOfCallerApplet(HLERequestContext& ctx);
void AcquireForegroundRights(HLERequestContext& ctx);
};
class IAudioController final : public ServiceFramework<IAudioController> {
public:
explicit IAudioController(Core::System& system_);
~IAudioController() override;
private:
void SetExpectedMasterVolume(HLERequestContext& ctx);
void GetMainAppletExpectedMasterVolume(HLERequestContext& ctx);
void GetLibraryAppletExpectedMasterVolume(HLERequestContext& ctx);
void ChangeMainAppletMasterVolume(HLERequestContext& ctx);
void SetTransparentAudioRate(HLERequestContext& ctx);
static constexpr float min_allowed_volume = 0.0f;
static constexpr float max_allowed_volume = 1.0f;
float main_applet_volume{0.25f};
float library_applet_volume{max_allowed_volume};
float transparent_volume_rate{min_allowed_volume};
// Volume transition fade time in nanoseconds.
// e.g. If the main applet volume was 0% and was changed to 50%
// with a fade of 50ns, then over the course of 50ns,
// the volume will gradually fade up to 50%
std::chrono::nanoseconds fade_time_ns{0};
};
class IDisplayController final : public ServiceFramework<IDisplayController> {
public:
explicit IDisplayController(Core::System& system_);
~IDisplayController() override;
private:
void GetCallerAppletCaptureImageEx(HLERequestContext& ctx);
void TakeScreenShotOfOwnLayer(HLERequestContext& ctx);
void AcquireLastForegroundCaptureSharedBuffer(HLERequestContext& ctx);
void ReleaseLastForegroundCaptureSharedBuffer(HLERequestContext& ctx);
void AcquireCallerAppletCaptureSharedBuffer(HLERequestContext& ctx);
void ReleaseCallerAppletCaptureSharedBuffer(HLERequestContext& ctx);
};
class IDebugFunctions final : public ServiceFramework<IDebugFunctions> {
public:
explicit IDebugFunctions(Core::System& system_);
~IDebugFunctions() override;
};
class ISelfController final : public ServiceFramework<ISelfController> {
public:
explicit ISelfController(Core::System& system_, Nvnflinger::Nvnflinger& nvnflinger_);
~ISelfController() override;
private:
void Exit(HLERequestContext& ctx);
void LockExit(HLERequestContext& ctx);
void UnlockExit(HLERequestContext& ctx);
void EnterFatalSection(HLERequestContext& ctx);
void LeaveFatalSection(HLERequestContext& ctx);
void GetLibraryAppletLaunchableEvent(HLERequestContext& ctx);
void SetScreenShotPermission(HLERequestContext& ctx);
void SetOperationModeChangedNotification(HLERequestContext& ctx);
void SetPerformanceModeChangedNotification(HLERequestContext& ctx);
void SetFocusHandlingMode(HLERequestContext& ctx);
void SetRestartMessageEnabled(HLERequestContext& ctx);
void SetOutOfFocusSuspendingEnabled(HLERequestContext& ctx);
void SetAlbumImageOrientation(HLERequestContext& ctx);
void IsSystemBufferSharingEnabled(HLERequestContext& ctx);
void GetSystemSharedBufferHandle(HLERequestContext& ctx);
void GetSystemSharedLayerHandle(HLERequestContext& ctx);
void CreateManagedDisplayLayer(HLERequestContext& ctx);
void CreateManagedDisplaySeparableLayer(HLERequestContext& ctx);
void SetHandlesRequestToDisplay(HLERequestContext& ctx);
void ApproveToDisplay(HLERequestContext& ctx);
void SetIdleTimeDetectionExtension(HLERequestContext& ctx);
void GetIdleTimeDetectionExtension(HLERequestContext& ctx);
void ReportUserIsActive(HLERequestContext& ctx);
void SetAutoSleepDisabled(HLERequestContext& ctx);
void IsAutoSleepDisabled(HLERequestContext& ctx);
void GetAccumulatedSuspendedTickValue(HLERequestContext& ctx);
void GetAccumulatedSuspendedTickChangedEvent(HLERequestContext& ctx);
void SetAlbumImageTakenNotificationEnabled(HLERequestContext& ctx);
void SaveCurrentScreenshot(HLERequestContext& ctx);
void SetRecordVolumeMuted(HLERequestContext& ctx);
Result EnsureBufferSharingEnabled();
enum class ScreenshotPermission : u32 {
Inherit = 0,
Enable = 1,
Disable = 2,
};
Nvnflinger::Nvnflinger& nvnflinger;
KernelHelpers::ServiceContext service_context;
Kernel::KEvent* launchable_event;
Kernel::KEvent* accumulated_suspended_tick_changed_event;
u32 idle_time_detection_extension = 0;
u64 num_fatal_sections_entered = 0;
u64 system_shared_buffer_id = 0;
u64 system_shared_layer_id = 0;
bool is_auto_sleep_disabled = false;
bool buffer_sharing_enabled = false;
ScreenshotPermission screenshot_permission = ScreenshotPermission::Inherit;
};
class ILockAccessor final : public ServiceFramework<ILockAccessor> {
public:
explicit ILockAccessor(Core::System& system_);
~ILockAccessor() override;
private:
void TryLock(HLERequestContext& ctx);
void Unlock(HLERequestContext& ctx);
void GetEvent(HLERequestContext& ctx);
void IsLocked(HLERequestContext& ctx);
bool is_locked{};
Kernel::KEvent* lock_event;
KernelHelpers::ServiceContext service_context;
};
class ICommonStateGetter final : public ServiceFramework<ICommonStateGetter> {
public:
explicit ICommonStateGetter(Core::System& system_,
std::shared_ptr<AppletMessageQueue> msg_queue_);
~ICommonStateGetter() override;
private:
// This is nn::oe::FocusState
enum class FocusState : u8 {
InFocus = 1,
NotInFocus = 2,
Background = 3,
};
// This is nn::oe::OperationMode
enum class OperationMode : u8 {
Handheld = 0,
Docked = 1,
};
// This is nn::am::service::SystemButtonType
enum class SystemButtonType {
None,
HomeButtonShortPressing,
HomeButtonLongPressing,
PowerButtonShortPressing,
PowerButtonLongPressing,
ShutdownSystem,
CaptureButtonShortPressing,
CaptureButtonLongPressing,
};
enum class SysPlatformRegion : s32 {
Global = 1,
Terra = 2,
};
void GetEventHandle(HLERequestContext& ctx);
void ReceiveMessage(HLERequestContext& ctx);
void GetCurrentFocusState(HLERequestContext& ctx);
void RequestToAcquireSleepLock(HLERequestContext& ctx);
void GetAcquiredSleepLockEvent(HLERequestContext& ctx);
void GetReaderLockAccessorEx(HLERequestContext& ctx);
void GetDefaultDisplayResolutionChangeEvent(HLERequestContext& ctx);
void GetOperationMode(HLERequestContext& ctx);
void GetPerformanceMode(HLERequestContext& ctx);
void GetBootMode(HLERequestContext& ctx);
void IsVrModeEnabled(HLERequestContext& ctx);
void SetVrModeEnabled(HLERequestContext& ctx);
void SetLcdBacklighOffEnabled(HLERequestContext& ctx);
void BeginVrModeEx(HLERequestContext& ctx);
void EndVrModeEx(HLERequestContext& ctx);
void GetDefaultDisplayResolution(HLERequestContext& ctx);
void SetCpuBoostMode(HLERequestContext& ctx);
void GetBuiltInDisplayType(HLERequestContext& ctx);
void PerformSystemButtonPressingIfInFocus(HLERequestContext& ctx);
void GetSettingsPlatformRegion(HLERequestContext& ctx);
void SetRequestExitToLibraryAppletAtExecuteNextProgramEnabled(HLERequestContext& ctx);
std::shared_ptr<AppletMessageQueue> msg_queue;
bool vr_mode_state{};
Kernel::KEvent* sleep_lock_event;
KernelHelpers::ServiceContext service_context;
};
class IStorageImpl {
public:
virtual ~IStorageImpl();
virtual std::vector<u8>& GetData() = 0;
virtual const std::vector<u8>& GetData() const = 0;
virtual std::size_t GetSize() const = 0;
};
class IStorage final : public ServiceFramework<IStorage> {
public:
explicit IStorage(Core::System& system_, std::vector<u8>&& buffer);
~IStorage() override;
std::vector<u8>& GetData() {
return impl->GetData();
}
const std::vector<u8>& GetData() const {
return impl->GetData();
}
std::size_t GetSize() const {
return impl->GetSize();
}
private:
void Register();
void Open(HLERequestContext& ctx);
std::shared_ptr<IStorageImpl> impl;
};
class IStorageAccessor final : public ServiceFramework<IStorageAccessor> {
public:
explicit IStorageAccessor(Core::System& system_, IStorage& backing_);
~IStorageAccessor() override;
private:
void GetSize(HLERequestContext& ctx);
void Write(HLERequestContext& ctx);
void Read(HLERequestContext& ctx);
IStorage& backing;
};
class ILibraryAppletCreator final : public ServiceFramework<ILibraryAppletCreator> {
public:
explicit ILibraryAppletCreator(Core::System& system_);
~ILibraryAppletCreator() override;
private:
void CreateLibraryApplet(HLERequestContext& ctx);
void CreateStorage(HLERequestContext& ctx);
void CreateTransferMemoryStorage(HLERequestContext& ctx);
void CreateHandleStorage(HLERequestContext& ctx);
};
class ILibraryAppletSelfAccessor final : public ServiceFramework<ILibraryAppletSelfAccessor> {
public:
explicit ILibraryAppletSelfAccessor(Core::System& system_);
~ILibraryAppletSelfAccessor() override;
private:
void PopInData(HLERequestContext& ctx);
void PushOutData(HLERequestContext& ctx);
void GetLibraryAppletInfo(HLERequestContext& ctx);
void GetMainAppletIdentityInfo(HLERequestContext& ctx);
void ExitProcessAndReturn(HLERequestContext& ctx);
void GetCallerAppletIdentityInfo(HLERequestContext& ctx);
void GetDesirableKeyboardLayout(HLERequestContext& ctx);
void GetMainAppletAvailableUsers(HLERequestContext& ctx);
void ShouldSetGpuTimeSliceManually(HLERequestContext& ctx);
void PushInShowAlbum();
void PushInShowCabinetData();
void PushInShowMiiEditData();
void PushInShowSoftwareKeyboard();
void PushInShowController();
std::deque<std::vector<u8>> queue_data;
};
class IAppletCommonFunctions final : public ServiceFramework<IAppletCommonFunctions> {
public:
explicit IAppletCommonFunctions(Core::System& system_);
~IAppletCommonFunctions() override;
private:
void SetCpuBoostRequestPriority(HLERequestContext& ctx);
};
class IApplicationFunctions final : public ServiceFramework<IApplicationFunctions> {
public:
explicit IApplicationFunctions(Core::System& system_);
~IApplicationFunctions() override;
private:
void PopLaunchParameter(HLERequestContext& ctx);
void CreateApplicationAndRequestToStartForQuest(HLERequestContext& ctx);
void EnsureSaveData(HLERequestContext& ctx);
void SetTerminateResult(HLERequestContext& ctx);
void GetDisplayVersion(HLERequestContext& ctx);
void GetDesiredLanguage(HLERequestContext& ctx);
void IsGamePlayRecordingSupported(HLERequestContext& ctx);
void InitializeGamePlayRecording(HLERequestContext& ctx);
void SetGamePlayRecordingState(HLERequestContext& ctx);
void NotifyRunning(HLERequestContext& ctx);
void GetPseudoDeviceId(HLERequestContext& ctx);
void ExtendSaveData(HLERequestContext& ctx);
void GetSaveDataSize(HLERequestContext& ctx);
void CreateCacheStorage(HLERequestContext& ctx);
void GetSaveDataSizeMax(HLERequestContext& ctx);
void BeginBlockingHomeButtonShortAndLongPressed(HLERequestContext& ctx);
void EndBlockingHomeButtonShortAndLongPressed(HLERequestContext& ctx);
void BeginBlockingHomeButton(HLERequestContext& ctx);
void EndBlockingHomeButton(HLERequestContext& ctx);
void EnableApplicationCrashReport(HLERequestContext& ctx);
void InitializeApplicationCopyrightFrameBuffer(HLERequestContext& ctx);
void SetApplicationCopyrightImage(HLERequestContext& ctx);
void SetApplicationCopyrightVisibility(HLERequestContext& ctx);
void QueryApplicationPlayStatistics(HLERequestContext& ctx);
void QueryApplicationPlayStatisticsByUid(HLERequestContext& ctx);
void ExecuteProgram(HLERequestContext& ctx);
void ClearUserChannel(HLERequestContext& ctx);
void UnpopToUserChannel(HLERequestContext& ctx);
void GetPreviousProgramIndex(HLERequestContext& ctx);
void GetGpuErrorDetectedSystemEvent(HLERequestContext& ctx);
void GetFriendInvitationStorageChannelEvent(HLERequestContext& ctx);
void TryPopFromFriendInvitationStorageChannel(HLERequestContext& ctx);
void GetNotificationStorageChannelEvent(HLERequestContext& ctx);
void GetHealthWarningDisappearedSystemEvent(HLERequestContext& ctx);
void PrepareForJit(HLERequestContext& ctx);
KernelHelpers::ServiceContext service_context;
bool launch_popped_account_preselect = false;
s32 previous_program_index{-1};
Kernel::KEvent* gpu_error_detected_event;
Kernel::KEvent* friend_invitation_storage_channel_event;
Kernel::KEvent* notification_storage_channel_event;
Kernel::KEvent* health_warning_disappeared_system_event;
};
class IHomeMenuFunctions final : public ServiceFramework<IHomeMenuFunctions> {
public:
explicit IHomeMenuFunctions(Core::System& system_);
~IHomeMenuFunctions() override;
private:
void RequestToGetForeground(HLERequestContext& ctx);
void GetPopFromGeneralChannelEvent(HLERequestContext& ctx);
KernelHelpers::ServiceContext service_context;
Kernel::KEvent* pop_from_general_channel_event;
};
class IGlobalStateController final : public ServiceFramework<IGlobalStateController> {
public:
explicit IGlobalStateController(Core::System& system_);
~IGlobalStateController() override;
};
class IApplicationCreator final : public ServiceFramework<IApplicationCreator> {
public:
explicit IApplicationCreator(Core::System& system_);
~IApplicationCreator() override;
};
class IProcessWindingController final : public ServiceFramework<IProcessWindingController> {
public:
explicit IProcessWindingController(Core::System& system_);
~IProcessWindingController() override;
private:
void GetLaunchReason(HLERequestContext& ctx);
void OpenCallingLibraryApplet(HLERequestContext& ctx);
};
void LoopProcess(Nvnflinger::Nvnflinger& nvnflinger, Core::System& system);
} // namespace Service::AM

View File

@ -0,0 +1,16 @@
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "core/hle/result.h"
namespace Service::AM {
constexpr Result ResultNoDataInChannel{ErrorModule::AM, 2};
constexpr Result ResultNoMessages{ErrorModule::AM, 3};
constexpr Result ResultInvalidOffset{ErrorModule::AM, 503};
constexpr Result ResultInvalidStorageType{ErrorModule::AM, 511};
constexpr Result ResultFatalSectionCountImbalance{ErrorModule::AM, 512};
} // namespace Service::AM

View File

@ -0,0 +1,178 @@
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "common/common_funcs.h"
#include "common/common_types.h"
namespace Service::AM {
namespace Frontend {
class FrontendApplet;
}
enum class AppletType {
Application,
LibraryApplet,
SystemApplet,
};
enum class GameplayRecordingState : u32 {
Disabled,
Enabled,
};
// This is nn::oe::FocusState
enum class FocusState : u8 {
InFocus = 1,
NotInFocus = 2,
Background = 3,
};
// This is nn::oe::OperationMode
enum class OperationMode : u8 {
Handheld = 0,
Docked = 1,
};
// This is nn::am::service::SystemButtonType
enum class SystemButtonType {
None,
HomeButtonShortPressing,
HomeButtonLongPressing,
PowerButtonShortPressing,
PowerButtonLongPressing,
ShutdownSystem,
CaptureButtonShortPressing,
CaptureButtonLongPressing,
};
enum class SysPlatformRegion : s32 {
Global = 1,
Terra = 2,
};
struct AppletProcessLaunchReason {
u8 flag;
INSERT_PADDING_BYTES(3);
};
static_assert(sizeof(AppletProcessLaunchReason) == 0x4,
"AppletProcessLaunchReason is an invalid size");
enum class ScreenshotPermission : u32 {
Inherit = 0,
Enable = 1,
Disable = 2,
};
struct FocusHandlingMode {
bool unknown0;
bool unknown1;
bool unknown2;
bool unknown3;
};
enum class IdleTimeDetectionExtension : u32 {
Disabled = 0,
Extended = 1,
ExtendedUnsafe = 2,
};
enum class AppletId : u32 {
None = 0x00,
Application = 0x01,
OverlayDisplay = 0x02,
QLaunch = 0x03,
Starter = 0x04,
Auth = 0x0A,
Cabinet = 0x0B,
Controller = 0x0C,
DataErase = 0x0D,
Error = 0x0E,
NetConnect = 0x0F,
ProfileSelect = 0x10,
SoftwareKeyboard = 0x11,
MiiEdit = 0x12,
Web = 0x13,
Shop = 0x14,
PhotoViewer = 0x15,
Settings = 0x16,
OfflineWeb = 0x17,
LoginShare = 0x18,
WebAuth = 0x19,
MyPage = 0x1A,
};
enum class AppletProgramId : u64 {
QLaunch = 0x0100000000001000ull,
Auth = 0x0100000000001001ull,
Cabinet = 0x0100000000001002ull,
Controller = 0x0100000000001003ull,
DataErase = 0x0100000000001004ull,
Error = 0x0100000000001005ull,
NetConnect = 0x0100000000001006ull,
ProfileSelect = 0x0100000000001007ull,
SoftwareKeyboard = 0x0100000000001008ull,
MiiEdit = 0x0100000000001009ull,
Web = 0x010000000000100Aull,
Shop = 0x010000000000100Bull,
OverlayDisplay = 0x010000000000100Cull,
PhotoViewer = 0x010000000000100Dull,
Settings = 0x010000000000100Eull,
OfflineWeb = 0x010000000000100Full,
LoginShare = 0x0100000000001010ull,
WebAuth = 0x0100000000001011ull,
Starter = 0x0100000000001012ull,
MyPage = 0x0100000000001013ull,
MaxProgramId = 0x0100000000001FFFull,
};
enum class LibraryAppletMode : u32 {
AllForeground = 0,
Background = 1,
NoUI = 2,
BackgroundIndirectDisplay = 3,
AllForegroundInitiallyHidden = 4,
};
enum class CommonArgumentVersion : u32 {
Version0,
Version1,
Version2,
Version3,
};
enum class CommonArgumentSize : u32 {
Version3 = 0x20,
};
enum class ThemeColor : u32 {
BasicWhite = 0,
BasicBlack = 3,
};
struct CommonArguments {
CommonArgumentVersion arguments_version;
CommonArgumentSize size;
u32 library_version;
ThemeColor theme_color;
bool play_startup_sound;
u64 system_tick;
};
static_assert(sizeof(CommonArguments) == 0x20, "CommonArguments has incorrect size.");
struct AppletIdentityInfo {
AppletId applet_id;
INSERT_PADDING_BYTES(0x4);
u64 application_id;
};
static_assert(sizeof(AppletIdentityInfo) == 0x10, "AppletIdentityInfo has incorrect size.");
using AppletResourceUserId = u64;
using ProgramId = u64;
struct Applet;
class AppletDataBroker;
} // namespace Service::AM

View File

@ -0,0 +1,27 @@
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "common/scope_exit.h"
#include "core/core.h"
#include "core/hle/service/am/am_results.h"
#include "core/hle/service/am/applet.h"
#include "core/hle/service/am/applet_manager.h"
namespace Service::AM {
Applet::Applet(Core::System& system, std::unique_ptr<Process> process_)
: context(system, "Applet"), message_queue(system), process(std::move(process_)),
hid_registration(system, *process), gpu_error_detected_event(context),
friend_invitation_storage_channel_event(context), notification_storage_channel_event(context),
health_warning_disappeared_system_event(context), acquired_sleep_lock_event(context),
pop_from_general_channel_event(context), library_applet_launchable_event(context),
accumulated_suspended_tick_changed_event(context), sleep_lock_event(context) {
aruid = process->GetProcessId();
program_id = process->GetProgramId();
}
Applet::~Applet() = default;
} // namespace Service::AM

133
src/core/hle/service/am/applet.h Executable file
View File

@ -0,0 +1,133 @@
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <list>
#include <mutex>
#include "common/math_util.h"
#include "core/hle/service/apm/apm_controller.h"
#include "core/hle/service/caps/caps_types.h"
#include "core/hle/service/event.h"
#include "core/hle/service/kernel_helpers.h"
#include "core/hle/service/service.h"
#include "core/hle/service/am/am_types.h"
#include "core/hle/service/am/applet_message_queue.h"
#include "core/hle/service/am/hid_registration.h"
#include "core/hle/service/am/managed_layer_holder.h"
#include "core/hle/service/am/process.h"
#include "core/hle/service/am/storage.h"
#include "core/hle/service/am/system_buffer_manager.h"
namespace Service::AM {
struct Applet {
explicit Applet(Core::System& system, std::unique_ptr<Process> process_);
~Applet();
// Lock
std::mutex lock{};
// Event creation helper
KernelHelpers::ServiceContext context;
// Applet message queue
AppletMessageQueue message_queue;
// Process
std::unique_ptr<Process> process;
// Creation state
AppletId applet_id{};
AppletResourceUserId aruid{};
AppletProcessLaunchReason launch_reason{};
AppletType type{};
ProgramId program_id{};
LibraryAppletMode library_applet_mode{};
s32 previous_program_index{-1};
ScreenshotPermission previous_screenshot_permission{ScreenshotPermission::Enable};
// TODO: some fields above can be AppletIdentityInfo
AppletIdentityInfo screen_shot_identity;
// hid state
HidRegistration hid_registration;
// vi state
SystemBufferManager system_buffer_manager{};
ManagedLayerHolder managed_layer_holder{};
// Applet common functions
Result terminate_result{};
s32 display_logical_width{};
s32 display_logical_height{};
Common::Rectangle<f32> display_magnification{0, 0, 1, 1};
bool home_button_double_click_enabled{};
bool home_button_short_pressed_blocked{};
bool home_button_long_pressed_blocked{};
bool vr_mode_curtain_required{};
bool sleep_required_by_high_temperature{};
bool sleep_required_by_low_battery{};
s32 cpu_boost_request_priority{-1};
bool handling_capture_button_short_pressed_message_enabled_for_applet{};
bool handling_capture_button_long_pressed_message_enabled_for_applet{};
u32 application_core_usage_mode{};
// Application functions
bool gameplay_recording_supported{};
GameplayRecordingState gameplay_recording_state{GameplayRecordingState::Disabled};
bool jit_service_launched{};
bool is_running{};
bool application_crash_report_enabled{};
// Common state
FocusState focus_state{};
bool sleep_lock_enabled{};
bool vr_mode_enabled{};
bool lcd_backlight_off_enabled{};
APM::CpuBoostMode boost_mode{};
bool request_exit_to_library_applet_at_execute_next_program_enabled{};
// Channels
std::deque<std::vector<u8>> user_channel_launch_parameter{};
std::deque<std::vector<u8>> preselected_user_launch_parameter{};
// Caller applet
std::weak_ptr<Applet> caller_applet{};
std::shared_ptr<AppletDataBroker> caller_applet_broker{};
// Self state
bool exit_locked{};
s32 fatal_section_count{};
bool operation_mode_changed_notification_enabled{true};
bool performance_mode_changed_notification_enabled{true};
FocusHandlingMode focus_handling_mode{};
bool restart_message_enabled{};
bool out_of_focus_suspension_enabled{true};
Capture::AlbumImageOrientation album_image_orientation{};
bool handles_request_to_display{};
ScreenshotPermission screenshot_permission{};
IdleTimeDetectionExtension idle_time_detection_extension{};
bool auto_sleep_disabled{};
u64 suspended_ticks{};
bool album_image_taken_notification_enabled{};
bool record_volume_muted{};
// Events
Event gpu_error_detected_event;
Event friend_invitation_storage_channel_event;
Event notification_storage_channel_event;
Event health_warning_disappeared_system_event;
Event acquired_sleep_lock_event;
Event pop_from_general_channel_event;
Event library_applet_launchable_event;
Event accumulated_suspended_tick_changed_event;
Event sleep_lock_event;
// Frontend state
std::shared_ptr<Frontend::FrontendApplet> frontend{};
};
} // namespace Service::AM

View File

@ -1,291 +1,16 @@
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "common/logging/log.h"
#include "core/core.h"
#include "core/hle/service/am/am.h"
#include "core/hle/service/am/applet_ae.h"
#include "core/hle/service/am/applet_manager.h"
#include "core/hle/service/am/library_applet_proxy.h"
#include "core/hle/service/am/system_applet_proxy.h"
#include "core/hle/service/ipc_helpers.h"
#include "core/hle/service/nvnflinger/nvnflinger.h"
namespace Service::AM {
class ILibraryAppletProxy final : public ServiceFramework<ILibraryAppletProxy> {
public:
explicit ILibraryAppletProxy(Nvnflinger::Nvnflinger& nvnflinger_,
std::shared_ptr<AppletMessageQueue> msg_queue_,
Core::System& system_)
: ServiceFramework{system_, "ILibraryAppletProxy"},
nvnflinger{nvnflinger_}, msg_queue{std::move(msg_queue_)} {
// clang-format off
static const FunctionInfo functions[] = {
{0, &ILibraryAppletProxy::GetCommonStateGetter, "GetCommonStateGetter"},
{1, &ILibraryAppletProxy::GetSelfController, "GetSelfController"},
{2, &ILibraryAppletProxy::GetWindowController, "GetWindowController"},
{3, &ILibraryAppletProxy::GetAudioController, "GetAudioController"},
{4, &ILibraryAppletProxy::GetDisplayController, "GetDisplayController"},
{10, &ILibraryAppletProxy::GetProcessWindingController, "GetProcessWindingController"},
{11, &ILibraryAppletProxy::GetLibraryAppletCreator, "GetLibraryAppletCreator"},
{20, &ILibraryAppletProxy::OpenLibraryAppletSelfAccessor, "OpenLibraryAppletSelfAccessor"},
{21, &ILibraryAppletProxy::GetAppletCommonFunctions, "GetAppletCommonFunctions"},
{22, &ILibraryAppletProxy::GetHomeMenuFunctions, "GetHomeMenuFunctions"},
{23, &ILibraryAppletProxy::GetGlobalStateController, "GetGlobalStateController"},
{1000, &ILibraryAppletProxy::GetDebugFunctions, "GetDebugFunctions"},
};
// clang-format on
RegisterHandlers(functions);
}
private:
void GetCommonStateGetter(HLERequestContext& ctx) {
LOG_DEBUG(Service_AM, "called");
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess);
rb.PushIpcInterface<ICommonStateGetter>(system, msg_queue);
}
void GetSelfController(HLERequestContext& ctx) {
LOG_DEBUG(Service_AM, "called");
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess);
rb.PushIpcInterface<ISelfController>(system, nvnflinger);
}
void GetWindowController(HLERequestContext& ctx) {
LOG_DEBUG(Service_AM, "called");
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess);
rb.PushIpcInterface<IWindowController>(system);
}
void GetAudioController(HLERequestContext& ctx) {
LOG_DEBUG(Service_AM, "called");
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess);
rb.PushIpcInterface<IAudioController>(system);
}
void GetDisplayController(HLERequestContext& ctx) {
LOG_DEBUG(Service_AM, "called");
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess);
rb.PushIpcInterface<IDisplayController>(system);
}
void GetProcessWindingController(HLERequestContext& ctx) {
LOG_DEBUG(Service_AM, "called");
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess);
rb.PushIpcInterface<IProcessWindingController>(system);
}
void GetLibraryAppletCreator(HLERequestContext& ctx) {
LOG_DEBUG(Service_AM, "called");
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess);
rb.PushIpcInterface<ILibraryAppletCreator>(system);
}
void OpenLibraryAppletSelfAccessor(HLERequestContext& ctx) {
LOG_DEBUG(Service_AM, "called");
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess);
rb.PushIpcInterface<ILibraryAppletSelfAccessor>(system);
}
void GetAppletCommonFunctions(HLERequestContext& ctx) {
LOG_DEBUG(Service_AM, "called");
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess);
rb.PushIpcInterface<IAppletCommonFunctions>(system);
}
void GetHomeMenuFunctions(HLERequestContext& ctx) {
LOG_DEBUG(Service_AM, "called");
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess);
rb.PushIpcInterface<IHomeMenuFunctions>(system);
}
void GetGlobalStateController(HLERequestContext& ctx) {
LOG_DEBUG(Service_AM, "called");
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess);
rb.PushIpcInterface<IGlobalStateController>(system);
}
void GetDebugFunctions(HLERequestContext& ctx) {
LOG_DEBUG(Service_AM, "called");
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess);
rb.PushIpcInterface<IDebugFunctions>(system);
}
Nvnflinger::Nvnflinger& nvnflinger;
std::shared_ptr<AppletMessageQueue> msg_queue;
};
class ISystemAppletProxy final : public ServiceFramework<ISystemAppletProxy> {
public:
explicit ISystemAppletProxy(Nvnflinger::Nvnflinger& nvnflinger_,
std::shared_ptr<AppletMessageQueue> msg_queue_,
Core::System& system_)
: ServiceFramework{system_, "ISystemAppletProxy"},
nvnflinger{nvnflinger_}, msg_queue{std::move(msg_queue_)} {
// clang-format off
static const FunctionInfo functions[] = {
{0, &ISystemAppletProxy::GetCommonStateGetter, "GetCommonStateGetter"},
{1, &ISystemAppletProxy::GetSelfController, "GetSelfController"},
{2, &ISystemAppletProxy::GetWindowController, "GetWindowController"},
{3, &ISystemAppletProxy::GetAudioController, "GetAudioController"},
{4, &ISystemAppletProxy::GetDisplayController, "GetDisplayController"},
{10, nullptr, "GetProcessWindingController"},
{11, &ISystemAppletProxy::GetLibraryAppletCreator, "GetLibraryAppletCreator"},
{20, &ISystemAppletProxy::GetHomeMenuFunctions, "GetHomeMenuFunctions"},
{21, &ISystemAppletProxy::GetGlobalStateController, "GetGlobalStateController"},
{22, &ISystemAppletProxy::GetApplicationCreator, "GetApplicationCreator"},
{23, &ISystemAppletProxy::GetAppletCommonFunctions, "GetAppletCommonFunctions"},
{1000, &ISystemAppletProxy::GetDebugFunctions, "GetDebugFunctions"},
};
// clang-format on
RegisterHandlers(functions);
}
private:
void GetCommonStateGetter(HLERequestContext& ctx) {
LOG_DEBUG(Service_AM, "called");
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess);
rb.PushIpcInterface<ICommonStateGetter>(system, msg_queue);
}
void GetSelfController(HLERequestContext& ctx) {
LOG_DEBUG(Service_AM, "called");
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess);
rb.PushIpcInterface<ISelfController>(system, nvnflinger);
}
void GetWindowController(HLERequestContext& ctx) {
LOG_DEBUG(Service_AM, "called");
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess);
rb.PushIpcInterface<IWindowController>(system);
}
void GetAudioController(HLERequestContext& ctx) {
LOG_DEBUG(Service_AM, "called");
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess);
rb.PushIpcInterface<IAudioController>(system);
}
void GetDisplayController(HLERequestContext& ctx) {
LOG_DEBUG(Service_AM, "called");
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess);
rb.PushIpcInterface<IDisplayController>(system);
}
void GetLibraryAppletCreator(HLERequestContext& ctx) {
LOG_DEBUG(Service_AM, "called");
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess);
rb.PushIpcInterface<ILibraryAppletCreator>(system);
}
void GetHomeMenuFunctions(HLERequestContext& ctx) {
LOG_DEBUG(Service_AM, "called");
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess);
rb.PushIpcInterface<IHomeMenuFunctions>(system);
}
void GetGlobalStateController(HLERequestContext& ctx) {
LOG_DEBUG(Service_AM, "called");
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess);
rb.PushIpcInterface<IGlobalStateController>(system);
}
void GetApplicationCreator(HLERequestContext& ctx) {
LOG_DEBUG(Service_AM, "called");
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess);
rb.PushIpcInterface<IApplicationCreator>(system);
}
void GetAppletCommonFunctions(HLERequestContext& ctx) {
LOG_DEBUG(Service_AM, "called");
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess);
rb.PushIpcInterface<IAppletCommonFunctions>(system);
}
void GetDebugFunctions(HLERequestContext& ctx) {
LOG_DEBUG(Service_AM, "called");
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess);
rb.PushIpcInterface<IDebugFunctions>(system);
}
Nvnflinger::Nvnflinger& nvnflinger;
std::shared_ptr<AppletMessageQueue> msg_queue;
};
void AppletAE::OpenSystemAppletProxy(HLERequestContext& ctx) {
LOG_DEBUG(Service_AM, "called");
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess);
rb.PushIpcInterface<ISystemAppletProxy>(nvnflinger, msg_queue, system);
}
void AppletAE::OpenLibraryAppletProxy(HLERequestContext& ctx) {
LOG_DEBUG(Service_AM, "called");
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess);
rb.PushIpcInterface<ILibraryAppletProxy>(nvnflinger, msg_queue, system);
}
void AppletAE::OpenLibraryAppletProxyOld(HLERequestContext& ctx) {
LOG_DEBUG(Service_AM, "called");
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess);
rb.PushIpcInterface<ILibraryAppletProxy>(nvnflinger, msg_queue, system);
}
AppletAE::AppletAE(Nvnflinger::Nvnflinger& nvnflinger_,
std::shared_ptr<AppletMessageQueue> msg_queue_, Core::System& system_)
: ServiceFramework{system_, "appletAE"}, nvnflinger{nvnflinger_}, msg_queue{
std::move(msg_queue_)} {
AppletAE::AppletAE(Nvnflinger::Nvnflinger& nvnflinger_, Core::System& system_)
: ServiceFramework{system_, "appletAE"}, nvnflinger{nvnflinger_} {
// clang-format off
static const FunctionInfo functions[] = {
{100, &AppletAE::OpenSystemAppletProxy, "OpenSystemAppletProxy"},
@ -304,8 +29,45 @@ AppletAE::AppletAE(Nvnflinger::Nvnflinger& nvnflinger_,
AppletAE::~AppletAE() = default;
const std::shared_ptr<AppletMessageQueue>& AppletAE::GetMessageQueue() const {
return msg_queue;
void AppletAE::OpenSystemAppletProxy(HLERequestContext& ctx) {
LOG_DEBUG(Service_AM, "called");
if (const auto applet = GetAppletFromContext(ctx)) {
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess);
rb.PushIpcInterface<ISystemAppletProxy>(nvnflinger, applet, system);
} else {
UNIMPLEMENTED();
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultUnknown);
}
}
void AppletAE::OpenLibraryAppletProxy(HLERequestContext& ctx) {
LOG_DEBUG(Service_AM, "called");
if (const auto applet = GetAppletFromContext(ctx)) {
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess);
rb.PushIpcInterface<ILibraryAppletProxy>(nvnflinger, applet, system);
} else {
UNIMPLEMENTED();
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultUnknown);
}
}
void AppletAE::OpenLibraryAppletProxyOld(HLERequestContext& ctx) {
LOG_DEBUG(Service_AM, "called");
return OpenLibraryAppletProxy(ctx);
}
std::shared_ptr<Applet> AppletAE::GetAppletFromContext(HLERequestContext& ctx) {
const auto aruid = ctx.GetPID();
return system.GetAppletManager().GetByAppletResourceUserId(aruid);
}
} // namespace Service::AM

View File

@ -18,23 +18,21 @@ class Nvnflinger;
namespace AM {
class AppletMessageQueue;
struct Applet;
class AppletAE final : public ServiceFramework<AppletAE> {
public:
explicit AppletAE(Nvnflinger::Nvnflinger& nvnflinger_,
std::shared_ptr<AppletMessageQueue> msg_queue_, Core::System& system_);
explicit AppletAE(Nvnflinger::Nvnflinger& nvnflinger_, Core::System& system_);
~AppletAE() override;
const std::shared_ptr<AppletMessageQueue>& GetMessageQueue() const;
private:
void OpenSystemAppletProxy(HLERequestContext& ctx);
void OpenLibraryAppletProxy(HLERequestContext& ctx);
void OpenLibraryAppletProxyOld(HLERequestContext& ctx);
std::shared_ptr<Applet> GetAppletFromContext(HLERequestContext& ctx);
Nvnflinger::Nvnflinger& nvnflinger;
std::shared_ptr<AppletMessageQueue> msg_queue;
};
} // namespace AM

View File

@ -0,0 +1,63 @@
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "core/hle/service/am/applet.h"
#include "core/hle/service/am/applet_common_functions.h"
#include "core/hle/service/ipc_helpers.h"
namespace Service::AM {
IAppletCommonFunctions::IAppletCommonFunctions(Core::System& system_,
std::shared_ptr<Applet> applet_)
: ServiceFramework{system_, "IAppletCommonFunctions"}, applet{std::move(applet_)} {
// clang-format off
static const FunctionInfo functions[] = {
{0, nullptr, "SetTerminateResult"},
{10, nullptr, "ReadThemeStorage"},
{11, nullptr, "WriteThemeStorage"},
{20, nullptr, "PushToAppletBoundChannel"},
{21, nullptr, "TryPopFromAppletBoundChannel"},
{40, nullptr, "GetDisplayLogicalResolution"},
{42, nullptr, "SetDisplayMagnification"},
{50, nullptr, "SetHomeButtonDoubleClickEnabled"},
{51, nullptr, "GetHomeButtonDoubleClickEnabled"},
{52, nullptr, "IsHomeButtonShortPressedBlocked"},
{60, nullptr, "IsVrModeCurtainRequired"},
{61, nullptr, "IsSleepRequiredByHighTemperature"},
{62, nullptr, "IsSleepRequiredByLowBattery"},
{70, &IAppletCommonFunctions::SetCpuBoostRequestPriority, "SetCpuBoostRequestPriority"},
{80, nullptr, "SetHandlingCaptureButtonShortPressedMessageEnabledForApplet"},
{81, nullptr, "SetHandlingCaptureButtonLongPressedMessageEnabledForApplet"},
{90, nullptr, "OpenNamedChannelAsParent"},
{91, nullptr, "OpenNamedChannelAsChild"},
{100, nullptr, "SetApplicationCoreUsageMode"},
{300, &IAppletCommonFunctions::GetCurrentApplicationId, "GetCurrentApplicationId"},
};
// clang-format on
RegisterHandlers(functions);
}
IAppletCommonFunctions::~IAppletCommonFunctions() = default;
void IAppletCommonFunctions::SetCpuBoostRequestPriority(HLERequestContext& ctx) {
LOG_WARNING(Service_AM, "(STUBBED) called");
IPC::RequestParser rp{ctx};
std::scoped_lock lk{applet->lock};
applet->cpu_boost_request_priority = rp.Pop<s32>();
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
void IAppletCommonFunctions::GetCurrentApplicationId(HLERequestContext& ctx) {
LOG_WARNING(Service_AM, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(ResultSuccess);
rb.Push<u64>(system.GetApplicationProcessProgramID() & ~0xFFFULL);
}
} // namespace Service::AM

View File

@ -0,0 +1,24 @@
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "core/hle/service/service.h"
namespace Service::AM {
struct Applet;
class IAppletCommonFunctions final : public ServiceFramework<IAppletCommonFunctions> {
public:
explicit IAppletCommonFunctions(Core::System& system_, std::shared_ptr<Applet> applet_);
~IAppletCommonFunctions() override;
private:
void SetCpuBoostRequestPriority(HLERequestContext& ctx);
void GetCurrentApplicationId(HLERequestContext& ctx);
const std::shared_ptr<Applet> applet;
};
} // namespace Service::AM

View File

@ -0,0 +1,67 @@
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "common/scope_exit.h"
#include "core/core.h"
#include "core/hle/service/am/am_results.h"
#include "core/hle/service/am/applet_data_broker.h"
#include "core/hle/service/am/applet_manager.h"
namespace Service::AM {
AppletStorageChannel::AppletStorageChannel(KernelHelpers::ServiceContext& context)
: m_event(context) {}
AppletStorageChannel::~AppletStorageChannel() = default;
void AppletStorageChannel::Push(std::shared_ptr<IStorage> storage) {
std::scoped_lock lk{m_lock};
m_data.emplace_back(std::move(storage));
m_event.Signal();
}
Result AppletStorageChannel::Pop(std::shared_ptr<IStorage>* out_storage) {
std::scoped_lock lk{m_lock};
SCOPE_EXIT({
if (m_data.empty()) {
m_event.Clear();
}
});
R_UNLESS(!m_data.empty(), AM::ResultNoDataInChannel);
*out_storage = std::move(m_data.front());
m_data.pop_front();
R_SUCCEED();
}
Kernel::KReadableEvent* AppletStorageChannel::GetEvent() {
return m_event.GetHandle();
}
AppletDataBroker::AppletDataBroker(Core::System& system_)
: system(system_), context(system_, "AppletDataBroker"), in_data(context),
interactive_in_data(context), out_data(context), interactive_out_data(context),
state_changed_event(context), is_completed(false) {}
AppletDataBroker::~AppletDataBroker() = default;
void AppletDataBroker::SignalCompletion() {
{
std::scoped_lock lk{lock};
if (is_completed) {
return;
}
is_completed = true;
state_changed_event.Signal();
}
system.GetAppletManager().FocusStateChanged();
}
} // namespace Service::AM

View File

@ -0,0 +1,80 @@
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <deque>
#include <memory>
#include <mutex>
#include "core/hle/service/event.h"
#include "core/hle/service/kernel_helpers.h"
union Result;
namespace Service::AM {
struct Applet;
class IStorage;
class AppletStorageChannel {
public:
explicit AppletStorageChannel(KernelHelpers::ServiceContext& ctx);
~AppletStorageChannel();
void Push(std::shared_ptr<IStorage> storage);
Result Pop(std::shared_ptr<IStorage>* out_storage);
Kernel::KReadableEvent* GetEvent();
private:
std::mutex m_lock{};
std::deque<std::shared_ptr<IStorage>> m_data{};
Event m_event;
};
class AppletDataBroker {
public:
explicit AppletDataBroker(Core::System& system_);
~AppletDataBroker();
AppletStorageChannel& GetInData() {
return in_data;
}
AppletStorageChannel& GetInteractiveInData() {
return interactive_in_data;
}
AppletStorageChannel& GetOutData() {
return out_data;
}
AppletStorageChannel& GetInteractiveOutData() {
return interactive_out_data;
}
Event& GetStateChangedEvent() {
return state_changed_event;
}
bool IsCompleted() const {
return is_completed;
}
void SignalCompletion();
private:
Core::System& system;
KernelHelpers::ServiceContext context;
AppletStorageChannel in_data;
AppletStorageChannel interactive_in_data;
AppletStorageChannel out_data;
AppletStorageChannel interactive_out_data;
Event state_changed_event;
std::mutex lock;
bool is_completed;
};
} // namespace Service::AM

View File

@ -0,0 +1,353 @@
// 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;
{
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);
}
// Terminate process.
applet->process->Terminate();
}
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::FocusStateChanged);
applet->message_queue.PushMessage(AppletMessageQueue::AppletMessage::ChangeIntoForeground);
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

View File

@ -0,0 +1,59 @@
// 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

View File

@ -0,0 +1,73 @@
// 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();
}
AppletMessageQueue::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

View File

@ -0,0 +1,76 @@
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <queue>
#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:
// This is nn::am::AppletMessage
enum class AppletMessage : u32 {
None = 0,
ChangeIntoForeground = 1,
ChangeIntoBackground = 2,
Exit = 4,
ApplicationExited = 6,
FocusStateChanged = 15,
Resume = 16,
DetectShortPressingHomeButton = 20,
DetectLongPressingHomeButton = 21,
DetectShortPressingPowerButton = 22,
DetectMiddlePressingPowerButton = 23,
DetectLongPressingPowerButton = 24,
RequestToPrepareSleep = 25,
FinishedSleepSequence = 26,
SleepRequiredByHighTemperature = 27,
SleepRequiredByLowBattery = 28,
AutoPowerDown = 29,
OperationModeChanged = 30,
PerformanceModeChanged = 31,
DetectReceivingCecSystemStandby = 32,
SdCardRemoved = 33,
LaunchApplicationRequested = 50,
RequestToDisplay = 51,
ShowApplicationLogo = 55,
HideApplicationLogo = 56,
ForceHideApplicationLogo = 57,
FloatingApplicationDetected = 60,
DetectShortPressingCaptureButton = 90,
AlbumScreenShotTaken = 92,
AlbumRecordingSaved = 93,
};
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

View File

@ -1,119 +1,16 @@
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "common/logging/log.h"
#include "core/hle/service/am/am.h"
#include "core/hle/service/am/applet_manager.h"
#include "core/hle/service/am/applet_oe.h"
#include "core/hle/service/am/application_proxy.h"
#include "core/hle/service/ipc_helpers.h"
#include "core/hle/service/nvnflinger/nvnflinger.h"
namespace Service::AM {
class IApplicationProxy final : public ServiceFramework<IApplicationProxy> {
public:
explicit IApplicationProxy(Nvnflinger::Nvnflinger& nvnflinger_,
std::shared_ptr<AppletMessageQueue> msg_queue_,
Core::System& system_)
: ServiceFramework{system_, "IApplicationProxy"},
nvnflinger{nvnflinger_}, msg_queue{std::move(msg_queue_)} {
// clang-format off
static const FunctionInfo functions[] = {
{0, &IApplicationProxy::GetCommonStateGetter, "GetCommonStateGetter"},
{1, &IApplicationProxy::GetSelfController, "GetSelfController"},
{2, &IApplicationProxy::GetWindowController, "GetWindowController"},
{3, &IApplicationProxy::GetAudioController, "GetAudioController"},
{4, &IApplicationProxy::GetDisplayController, "GetDisplayController"},
{10, nullptr, "GetProcessWindingController"},
{11, &IApplicationProxy::GetLibraryAppletCreator, "GetLibraryAppletCreator"},
{20, &IApplicationProxy::GetApplicationFunctions, "GetApplicationFunctions"},
{1000, &IApplicationProxy::GetDebugFunctions, "GetDebugFunctions"},
};
// clang-format on
RegisterHandlers(functions);
}
private:
void GetAudioController(HLERequestContext& ctx) {
LOG_DEBUG(Service_AM, "called");
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess);
rb.PushIpcInterface<IAudioController>(system);
}
void GetDisplayController(HLERequestContext& ctx) {
LOG_DEBUG(Service_AM, "called");
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess);
rb.PushIpcInterface<IDisplayController>(system);
}
void GetDebugFunctions(HLERequestContext& ctx) {
LOG_DEBUG(Service_AM, "called");
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess);
rb.PushIpcInterface<IDebugFunctions>(system);
}
void GetWindowController(HLERequestContext& ctx) {
LOG_DEBUG(Service_AM, "called");
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess);
rb.PushIpcInterface<IWindowController>(system);
}
void GetSelfController(HLERequestContext& ctx) {
LOG_DEBUG(Service_AM, "called");
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess);
rb.PushIpcInterface<ISelfController>(system, nvnflinger);
}
void GetCommonStateGetter(HLERequestContext& ctx) {
LOG_DEBUG(Service_AM, "called");
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess);
rb.PushIpcInterface<ICommonStateGetter>(system, msg_queue);
}
void GetLibraryAppletCreator(HLERequestContext& ctx) {
LOG_DEBUG(Service_AM, "called");
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess);
rb.PushIpcInterface<ILibraryAppletCreator>(system);
}
void GetApplicationFunctions(HLERequestContext& ctx) {
LOG_DEBUG(Service_AM, "called");
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess);
rb.PushIpcInterface<IApplicationFunctions>(system);
}
Nvnflinger::Nvnflinger& nvnflinger;
std::shared_ptr<AppletMessageQueue> msg_queue;
};
void AppletOE::OpenApplicationProxy(HLERequestContext& ctx) {
LOG_DEBUG(Service_AM, "called");
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess);
rb.PushIpcInterface<IApplicationProxy>(nvnflinger, msg_queue, system);
}
AppletOE::AppletOE(Nvnflinger::Nvnflinger& nvnflinger_,
std::shared_ptr<AppletMessageQueue> msg_queue_, Core::System& system_)
: ServiceFramework{system_, "appletOE"}, nvnflinger{nvnflinger_}, msg_queue{
std::move(msg_queue_)} {
AppletOE::AppletOE(Nvnflinger::Nvnflinger& nvnflinger_, Core::System& system_)
: ServiceFramework{system_, "appletOE"}, nvnflinger{nvnflinger_} {
static const FunctionInfo functions[] = {
{0, &AppletOE::OpenApplicationProxy, "OpenApplicationProxy"},
};
@ -122,8 +19,24 @@ AppletOE::AppletOE(Nvnflinger::Nvnflinger& nvnflinger_,
AppletOE::~AppletOE() = default;
const std::shared_ptr<AppletMessageQueue>& AppletOE::GetMessageQueue() const {
return msg_queue;
void AppletOE::OpenApplicationProxy(HLERequestContext& ctx) {
LOG_DEBUG(Service_AM, "called");
if (const auto applet = GetAppletFromContext(ctx)) {
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess);
rb.PushIpcInterface<IApplicationProxy>(nvnflinger, applet, system);
} else {
UNIMPLEMENTED();
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultUnknown);
}
}
std::shared_ptr<Applet> AppletOE::GetAppletFromContext(HLERequestContext& ctx) {
const auto aruid = ctx.GetPID();
return system.GetAppletManager().GetByAppletResourceUserId(aruid);
}
} // namespace Service::AM

View File

@ -18,21 +18,19 @@ class Nvnflinger;
namespace AM {
class AppletMessageQueue;
struct Applet;
class AppletOE final : public ServiceFramework<AppletOE> {
public:
explicit AppletOE(Nvnflinger::Nvnflinger& nvnflinger_,
std::shared_ptr<AppletMessageQueue> msg_queue_, Core::System& system_);
explicit AppletOE(Nvnflinger::Nvnflinger& nvnflinger_, Core::System& system_);
~AppletOE() override;
const std::shared_ptr<AppletMessageQueue>& GetMessageQueue() const;
private:
void OpenApplicationProxy(HLERequestContext& ctx);
std::shared_ptr<Applet> GetAppletFromContext(HLERequestContext& ctx);
Nvnflinger::Nvnflinger& nvnflinger;
std::shared_ptr<AppletMessageQueue> msg_queue;
};
} // namespace AM

View File

@ -0,0 +1,25 @@
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "core/hle/service/am/application_creator.h"
#include "core/hle/service/ipc_helpers.h"
namespace Service::AM {
IApplicationCreator::IApplicationCreator(Core::System& system_)
: ServiceFramework{system_, "IApplicationCreator"} {
// clang-format off
static const FunctionInfo functions[] = {
{0, nullptr, "CreateApplication"},
{1, nullptr, "PopLaunchRequestedApplication"},
{10, nullptr, "CreateSystemApplication"},
{100, nullptr, "PopFloatingApplicationForDevelopment"},
};
// clang-format on
RegisterHandlers(functions);
}
IApplicationCreator::~IApplicationCreator() = default;
} // namespace Service::AM

View File

@ -0,0 +1,16 @@
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "core/hle/service/service.h"
namespace Service::AM {
class IApplicationCreator final : public ServiceFramework<IApplicationCreator> {
public:
explicit IApplicationCreator(Core::System& system_);
~IApplicationCreator() override;
};
} // namespace Service::AM

View File

@ -0,0 +1,594 @@
// 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/file_sys/control_metadata.h"
#include "core/file_sys/patch_manager.h"
#include "core/file_sys/registered_cache.h"
#include "core/file_sys/savedata_factory.h"
#include "core/hle/service/acc/profile_manager.h"
#include "core/hle/service/am/am_results.h"
#include "core/hle/service/am/applet.h"
#include "core/hle/service/am/application_functions.h"
#include "core/hle/service/am/storage.h"
#include "core/hle/service/filesystem/filesystem.h"
#include "core/hle/service/filesystem/save_data_controller.h"
#include "core/hle/service/ipc_helpers.h"
#include "core/hle/service/ns/ns.h"
#include "core/hle/service/sm/sm.h"
namespace Service::AM {
enum class LaunchParameterKind : u32 {
UserChannel = 1,
AccountPreselectedUser = 2,
};
IApplicationFunctions::IApplicationFunctions(Core::System& system_, std::shared_ptr<Applet> applet_)
: ServiceFramework{system_, "IApplicationFunctions"}, applet{std::move(applet_)} {
// clang-format off
static const FunctionInfo functions[] = {
{1, &IApplicationFunctions::PopLaunchParameter, "PopLaunchParameter"},
{10, nullptr, "CreateApplicationAndPushAndRequestToStart"},
{11, nullptr, "CreateApplicationAndPushAndRequestToStartForQuest"},
{12, nullptr, "CreateApplicationAndRequestToStart"},
{13, &IApplicationFunctions::CreateApplicationAndRequestToStartForQuest, "CreateApplicationAndRequestToStartForQuest"},
{14, nullptr, "CreateApplicationWithAttributeAndPushAndRequestToStartForQuest"},
{15, nullptr, "CreateApplicationWithAttributeAndRequestToStartForQuest"},
{20, &IApplicationFunctions::EnsureSaveData, "EnsureSaveData"},
{21, &IApplicationFunctions::GetDesiredLanguage, "GetDesiredLanguage"},
{22, &IApplicationFunctions::SetTerminateResult, "SetTerminateResult"},
{23, &IApplicationFunctions::GetDisplayVersion, "GetDisplayVersion"},
{24, nullptr, "GetLaunchStorageInfoForDebug"},
{25, &IApplicationFunctions::ExtendSaveData, "ExtendSaveData"},
{26, &IApplicationFunctions::GetSaveDataSize, "GetSaveDataSize"},
{27, &IApplicationFunctions::CreateCacheStorage, "CreateCacheStorage"},
{28, &IApplicationFunctions::GetSaveDataSizeMax, "GetSaveDataSizeMax"},
{29, nullptr, "GetCacheStorageMax"},
{30, &IApplicationFunctions::BeginBlockingHomeButtonShortAndLongPressed, "BeginBlockingHomeButtonShortAndLongPressed"},
{31, &IApplicationFunctions::EndBlockingHomeButtonShortAndLongPressed, "EndBlockingHomeButtonShortAndLongPressed"},
{32, &IApplicationFunctions::BeginBlockingHomeButton, "BeginBlockingHomeButton"},
{33, &IApplicationFunctions::EndBlockingHomeButton, "EndBlockingHomeButton"},
{34, nullptr, "SelectApplicationLicense"},
{35, nullptr, "GetDeviceSaveDataSizeMax"},
{36, nullptr, "GetLimitedApplicationLicense"},
{37, nullptr, "GetLimitedApplicationLicenseUpgradableEvent"},
{40, &IApplicationFunctions::NotifyRunning, "NotifyRunning"},
{50, &IApplicationFunctions::GetPseudoDeviceId, "GetPseudoDeviceId"},
{60, nullptr, "SetMediaPlaybackStateForApplication"},
{65, &IApplicationFunctions::IsGamePlayRecordingSupported, "IsGamePlayRecordingSupported"},
{66, &IApplicationFunctions::InitializeGamePlayRecording, "InitializeGamePlayRecording"},
{67, &IApplicationFunctions::SetGamePlayRecordingState, "SetGamePlayRecordingState"},
{68, nullptr, "RequestFlushGamePlayingMovieForDebug"},
{70, nullptr, "RequestToShutdown"},
{71, nullptr, "RequestToReboot"},
{72, nullptr, "RequestToSleep"},
{80, nullptr, "ExitAndRequestToShowThanksMessage"},
{90, &IApplicationFunctions::EnableApplicationCrashReport, "EnableApplicationCrashReport"},
{100, &IApplicationFunctions::InitializeApplicationCopyrightFrameBuffer, "InitializeApplicationCopyrightFrameBuffer"},
{101, &IApplicationFunctions::SetApplicationCopyrightImage, "SetApplicationCopyrightImage"},
{102, &IApplicationFunctions::SetApplicationCopyrightVisibility, "SetApplicationCopyrightVisibility"},
{110, &IApplicationFunctions::QueryApplicationPlayStatistics, "QueryApplicationPlayStatistics"},
{111, &IApplicationFunctions::QueryApplicationPlayStatisticsByUid, "QueryApplicationPlayStatisticsByUid"},
{120, &IApplicationFunctions::ExecuteProgram, "ExecuteProgram"},
{121, &IApplicationFunctions::ClearUserChannel, "ClearUserChannel"},
{122, &IApplicationFunctions::UnpopToUserChannel, "UnpopToUserChannel"},
{123, &IApplicationFunctions::GetPreviousProgramIndex, "GetPreviousProgramIndex"},
{124, nullptr, "EnableApplicationAllThreadDumpOnCrash"},
{130, &IApplicationFunctions::GetGpuErrorDetectedSystemEvent, "GetGpuErrorDetectedSystemEvent"},
{131, nullptr, "SetDelayTimeToAbortOnGpuError"},
{140, &IApplicationFunctions::GetFriendInvitationStorageChannelEvent, "GetFriendInvitationStorageChannelEvent"},
{141, &IApplicationFunctions::TryPopFromFriendInvitationStorageChannel, "TryPopFromFriendInvitationStorageChannel"},
{150, &IApplicationFunctions::GetNotificationStorageChannelEvent, "GetNotificationStorageChannelEvent"},
{151, nullptr, "TryPopFromNotificationStorageChannel"},
{160, &IApplicationFunctions::GetHealthWarningDisappearedSystemEvent, "GetHealthWarningDisappearedSystemEvent"},
{170, nullptr, "SetHdcpAuthenticationActivated"},
{180, nullptr, "GetLaunchRequiredVersion"},
{181, nullptr, "UpgradeLaunchRequiredVersion"},
{190, nullptr, "SendServerMaintenanceOverlayNotification"},
{200, nullptr, "GetLastApplicationExitReason"},
{500, nullptr, "StartContinuousRecordingFlushForDebug"},
{1000, nullptr, "CreateMovieMaker"},
{1001, &IApplicationFunctions::PrepareForJit, "PrepareForJit"},
};
// clang-format on
RegisterHandlers(functions);
}
IApplicationFunctions::~IApplicationFunctions() = default;
void IApplicationFunctions::EnableApplicationCrashReport(HLERequestContext& ctx) {
LOG_WARNING(Service_AM, "(STUBBED) called");
std::scoped_lock lk{applet->lock};
applet->application_crash_report_enabled = true;
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
void IApplicationFunctions::InitializeApplicationCopyrightFrameBuffer(HLERequestContext& ctx) {
LOG_WARNING(Service_AM, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
void IApplicationFunctions::SetApplicationCopyrightImage(HLERequestContext& ctx) {
LOG_WARNING(Service_AM, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
void IApplicationFunctions::SetApplicationCopyrightVisibility(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto is_visible = rp.Pop<bool>();
LOG_WARNING(Service_AM, "(STUBBED) called, is_visible={}", is_visible);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
void IApplicationFunctions::BeginBlockingHomeButtonShortAndLongPressed(HLERequestContext& ctx) {
LOG_WARNING(Service_AM, "(STUBBED) called");
std::scoped_lock lk{applet->lock};
applet->home_button_long_pressed_blocked = true;
applet->home_button_short_pressed_blocked = true;
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
void IApplicationFunctions::EndBlockingHomeButtonShortAndLongPressed(HLERequestContext& ctx) {
LOG_WARNING(Service_AM, "(STUBBED) called");
std::scoped_lock lk{applet->lock};
applet->home_button_long_pressed_blocked = false;
applet->home_button_short_pressed_blocked = false;
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
void IApplicationFunctions::BeginBlockingHomeButton(HLERequestContext& ctx) {
LOG_WARNING(Service_AM, "(STUBBED) called");
std::scoped_lock lk{applet->lock};
applet->home_button_long_pressed_blocked = true;
applet->home_button_short_pressed_blocked = true;
applet->home_button_double_click_enabled = true;
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
void IApplicationFunctions::EndBlockingHomeButton(HLERequestContext& ctx) {
LOG_WARNING(Service_AM, "(STUBBED) called");
std::scoped_lock lk{applet->lock};
applet->home_button_long_pressed_blocked = false;
applet->home_button_short_pressed_blocked = false;
applet->home_button_double_click_enabled = false;
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
void IApplicationFunctions::PopLaunchParameter(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto kind = rp.PopEnum<LaunchParameterKind>();
LOG_INFO(Service_AM, "called, kind={:08X}", kind);
std::scoped_lock lk{applet->lock};
auto& channel = kind == LaunchParameterKind::UserChannel
? applet->user_channel_launch_parameter
: applet->preselected_user_launch_parameter;
if (channel.empty()) {
LOG_WARNING(Service_AM, "Attempted to pop parameter {} but none was found!", kind);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(AM::ResultNoDataInChannel);
return;
}
auto data = channel.back();
channel.pop_back();
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess);
rb.PushIpcInterface<IStorage>(system, std::move(data));
}
void IApplicationFunctions::CreateApplicationAndRequestToStartForQuest(HLERequestContext& ctx) {
LOG_WARNING(Service_AM, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
void IApplicationFunctions::EnsureSaveData(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
u128 user_id = rp.PopRaw<u128>();
LOG_DEBUG(Service_AM, "called, uid={:016X}{:016X}", user_id[1], user_id[0]);
FileSys::SaveDataAttribute attribute{};
attribute.title_id = applet->program_id;
attribute.user_id = user_id;
attribute.type = FileSys::SaveDataType::SaveData;
FileSys::VirtualDir save_data{};
const auto res = system.GetFileSystemController().OpenSaveDataController()->CreateSaveData(
&save_data, FileSys::SaveDataSpaceId::NandUser, attribute);
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(res);
rb.Push<u64>(0);
}
void IApplicationFunctions::SetTerminateResult(HLERequestContext& ctx) {
// Takes an input u32 Result, no output.
// For example, in some cases official apps use this with error 0x2A2 then
// uses svcBreak.
IPC::RequestParser rp{ctx};
u32 result = rp.Pop<u32>();
LOG_WARNING(Service_AM, "(STUBBED) called, result=0x{:08X}", result);
std::scoped_lock lk{applet->lock};
applet->terminate_result = Result(result);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
void IApplicationFunctions::GetDisplayVersion(HLERequestContext& ctx) {
LOG_DEBUG(Service_AM, "called");
std::array<u8, 0x10> version_string{};
const auto res = [this] {
const FileSys::PatchManager pm{applet->program_id, system.GetFileSystemController(),
system.GetContentProvider()};
auto metadata = pm.GetControlMetadata();
if (metadata.first != nullptr) {
return metadata;
}
const FileSys::PatchManager pm_update{FileSys::GetUpdateTitleID(applet->program_id),
system.GetFileSystemController(),
system.GetContentProvider()};
return pm_update.GetControlMetadata();
}();
if (res.first != nullptr) {
const auto& version = res.first->GetVersionString();
std::copy(version.begin(), version.end(), version_string.begin());
} else {
static constexpr char default_version[]{"1.0.0"};
std::memcpy(version_string.data(), default_version, sizeof(default_version));
}
IPC::ResponseBuilder rb{ctx, 6};
rb.Push(ResultSuccess);
rb.PushRaw(version_string);
}
void IApplicationFunctions::GetDesiredLanguage(HLERequestContext& ctx) {
// TODO(bunnei): This should be configurable
LOG_DEBUG(Service_AM, "called");
// Get supported languages from NACP, if possible
// Default to 0 (all languages supported)
u32 supported_languages = 0;
const auto res = [this] {
const FileSys::PatchManager pm{applet->program_id, system.GetFileSystemController(),
system.GetContentProvider()};
auto metadata = pm.GetControlMetadata();
if (metadata.first != nullptr) {
return metadata;
}
const FileSys::PatchManager pm_update{FileSys::GetUpdateTitleID(applet->program_id),
system.GetFileSystemController(),
system.GetContentProvider()};
return pm_update.GetControlMetadata();
}();
if (res.first != nullptr) {
supported_languages = res.first->GetSupportedLanguages();
}
// Call IApplicationManagerInterface implementation.
auto& service_manager = system.ServiceManager();
auto ns_am2 = service_manager.GetService<NS::NS>("ns:am2");
auto app_man = ns_am2->GetApplicationManagerInterface();
// Get desired application language
u8 desired_language{};
const auto res_lang =
app_man->GetApplicationDesiredLanguage(&desired_language, supported_languages);
if (res_lang != ResultSuccess) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(res_lang);
return;
}
// Convert to settings language code.
u64 language_code{};
const auto res_code =
app_man->ConvertApplicationLanguageToLanguageCode(&language_code, desired_language);
if (res_code != ResultSuccess) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(res_code);
return;
}
LOG_DEBUG(Service_AM, "got desired_language={:016X}", language_code);
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(ResultSuccess);
rb.Push(language_code);
}
void IApplicationFunctions::IsGamePlayRecordingSupported(HLERequestContext& ctx) {
LOG_WARNING(Service_AM, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
rb.Push(applet->gameplay_recording_supported);
}
void IApplicationFunctions::InitializeGamePlayRecording(HLERequestContext& ctx) {
LOG_WARNING(Service_AM, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
void IApplicationFunctions::SetGamePlayRecordingState(HLERequestContext& ctx) {
LOG_WARNING(Service_AM, "(STUBBED) called");
IPC::RequestParser rp{ctx};
std::scoped_lock lk{applet->lock};
applet->gameplay_recording_state = rp.PopRaw<GameplayRecordingState>();
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
void IApplicationFunctions::NotifyRunning(HLERequestContext& ctx) {
LOG_WARNING(Service_AM, "(STUBBED) called");
std::scoped_lock lk{applet->lock};
applet->is_running = true;
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
rb.Push<u8>(0); // Unknown, seems to be ignored by official processes
}
void IApplicationFunctions::GetPseudoDeviceId(HLERequestContext& ctx) {
LOG_WARNING(Service_AM, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 6};
rb.Push(ResultSuccess);
// Returns a 128-bit UUID
rb.Push<u64>(0);
rb.Push<u64>(0);
}
void IApplicationFunctions::ExtendSaveData(HLERequestContext& ctx) {
struct Parameters {
FileSys::SaveDataType type;
u128 user_id;
u64 new_normal_size;
u64 new_journal_size;
};
static_assert(sizeof(Parameters) == 40);
IPC::RequestParser rp{ctx};
const auto [type, user_id, new_normal_size, new_journal_size] = rp.PopRaw<Parameters>();
LOG_DEBUG(Service_AM,
"called with type={:02X}, user_id={:016X}{:016X}, new_normal={:016X}, "
"new_journal={:016X}",
static_cast<u8>(type), user_id[1], user_id[0], new_normal_size, new_journal_size);
system.GetFileSystemController().OpenSaveDataController()->WriteSaveDataSize(
type, applet->program_id, user_id, {new_normal_size, new_journal_size});
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(ResultSuccess);
// The following value is used upon failure to help the system recover.
// Since we always succeed, this should be 0.
rb.Push<u64>(0);
}
void IApplicationFunctions::GetSaveDataSize(HLERequestContext& ctx) {
struct Parameters {
FileSys::SaveDataType type;
u128 user_id;
};
static_assert(sizeof(Parameters) == 24);
IPC::RequestParser rp{ctx};
const auto [type, user_id] = rp.PopRaw<Parameters>();
LOG_DEBUG(Service_AM, "called with type={:02X}, user_id={:016X}{:016X}", type, user_id[1],
user_id[0]);
const auto size = system.GetFileSystemController().OpenSaveDataController()->ReadSaveDataSize(
type, applet->program_id, user_id);
IPC::ResponseBuilder rb{ctx, 6};
rb.Push(ResultSuccess);
rb.Push(size.normal);
rb.Push(size.journal);
}
void IApplicationFunctions::CreateCacheStorage(HLERequestContext& ctx) {
struct InputParameters {
u16 index;
s64 size;
s64 journal_size;
};
static_assert(sizeof(InputParameters) == 24);
struct OutputParameters {
u32 storage_target;
u64 required_size;
};
static_assert(sizeof(OutputParameters) == 16);
IPC::RequestParser rp{ctx};
const auto params = rp.PopRaw<InputParameters>();
LOG_WARNING(Service_AM, "(STUBBED) called with index={}, size={:#x}, journal_size={:#x}",
params.index, params.size, params.journal_size);
const OutputParameters resp{
.storage_target = 1,
.required_size = 0,
};
IPC::ResponseBuilder rb{ctx, 6};
rb.Push(ResultSuccess);
rb.PushRaw(resp);
}
void IApplicationFunctions::GetSaveDataSizeMax(HLERequestContext& ctx) {
LOG_WARNING(Service_AM, "(STUBBED) called");
constexpr u64 size_max_normal = 0xFFFFFFF;
constexpr u64 size_max_journal = 0xFFFFFFF;
IPC::ResponseBuilder rb{ctx, 6};
rb.Push(ResultSuccess);
rb.Push(size_max_normal);
rb.Push(size_max_journal);
}
void IApplicationFunctions::QueryApplicationPlayStatistics(HLERequestContext& ctx) {
LOG_WARNING(Service_AM, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
rb.Push<u32>(0);
}
void IApplicationFunctions::QueryApplicationPlayStatisticsByUid(HLERequestContext& ctx) {
LOG_WARNING(Service_AM, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
rb.Push<u32>(0);
}
void IApplicationFunctions::ExecuteProgram(HLERequestContext& ctx) {
LOG_WARNING(Service_AM, "(STUBBED) called");
IPC::RequestParser rp{ctx};
[[maybe_unused]] const auto unk_1 = rp.Pop<u32>();
[[maybe_unused]] const auto unk_2 = rp.Pop<u32>();
const auto program_index = rp.Pop<u64>();
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
// Swap user channel ownership into the system so that it will be preserved
system.GetUserChannel().swap(applet->user_channel_launch_parameter);
system.ExecuteProgram(program_index);
}
void IApplicationFunctions::ClearUserChannel(HLERequestContext& ctx) {
LOG_DEBUG(Service_AM, "called");
applet->user_channel_launch_parameter.clear();
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
void IApplicationFunctions::UnpopToUserChannel(HLERequestContext& ctx) {
LOG_DEBUG(Service_AM, "called");
IPC::RequestParser rp{ctx};
const auto storage = rp.PopIpcInterface<IStorage>().lock();
if (storage) {
applet->user_channel_launch_parameter.push_back(storage->GetData());
}
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
void IApplicationFunctions::GetPreviousProgramIndex(HLERequestContext& ctx) {
LOG_WARNING(Service_AM, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
rb.Push<s32>(applet->previous_program_index);
}
void IApplicationFunctions::GetGpuErrorDetectedSystemEvent(HLERequestContext& ctx) {
LOG_WARNING(Service_AM, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 2, 1};
rb.Push(ResultSuccess);
rb.PushCopyObjects(applet->gpu_error_detected_event.GetHandle());
}
void IApplicationFunctions::GetFriendInvitationStorageChannelEvent(HLERequestContext& ctx) {
LOG_DEBUG(Service_AM, "called");
IPC::ResponseBuilder rb{ctx, 2, 1};
rb.Push(ResultSuccess);
rb.PushCopyObjects(applet->friend_invitation_storage_channel_event.GetHandle());
}
void IApplicationFunctions::TryPopFromFriendInvitationStorageChannel(HLERequestContext& ctx) {
LOG_DEBUG(Service_AM, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(AM::ResultNoDataInChannel);
}
void IApplicationFunctions::GetNotificationStorageChannelEvent(HLERequestContext& ctx) {
LOG_DEBUG(Service_AM, "called");
IPC::ResponseBuilder rb{ctx, 2, 1};
rb.Push(ResultSuccess);
rb.PushCopyObjects(applet->notification_storage_channel_event.GetHandle());
}
void IApplicationFunctions::GetHealthWarningDisappearedSystemEvent(HLERequestContext& ctx) {
LOG_DEBUG(Service_AM, "called");
IPC::ResponseBuilder rb{ctx, 2, 1};
rb.Push(ResultSuccess);
rb.PushCopyObjects(applet->health_warning_disappeared_system_event.GetHandle());
}
void IApplicationFunctions::PrepareForJit(HLERequestContext& ctx) {
LOG_WARNING(Service_AM, "(STUBBED) called");
std::scoped_lock lk{applet->lock};
applet->jit_service_launched = true;
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
} // namespace Service::AM

View File

@ -0,0 +1,58 @@
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "core/hle/service/kernel_helpers.h"
#include "core/hle/service/service.h"
namespace Service::AM {
struct Applet;
class IApplicationFunctions final : public ServiceFramework<IApplicationFunctions> {
public:
explicit IApplicationFunctions(Core::System& system_, std::shared_ptr<Applet> applet_);
~IApplicationFunctions() override;
private:
void PopLaunchParameter(HLERequestContext& ctx);
void CreateApplicationAndRequestToStartForQuest(HLERequestContext& ctx);
void EnsureSaveData(HLERequestContext& ctx);
void SetTerminateResult(HLERequestContext& ctx);
void GetDisplayVersion(HLERequestContext& ctx);
void GetDesiredLanguage(HLERequestContext& ctx);
void IsGamePlayRecordingSupported(HLERequestContext& ctx);
void InitializeGamePlayRecording(HLERequestContext& ctx);
void SetGamePlayRecordingState(HLERequestContext& ctx);
void NotifyRunning(HLERequestContext& ctx);
void GetPseudoDeviceId(HLERequestContext& ctx);
void ExtendSaveData(HLERequestContext& ctx);
void GetSaveDataSize(HLERequestContext& ctx);
void CreateCacheStorage(HLERequestContext& ctx);
void GetSaveDataSizeMax(HLERequestContext& ctx);
void BeginBlockingHomeButtonShortAndLongPressed(HLERequestContext& ctx);
void EndBlockingHomeButtonShortAndLongPressed(HLERequestContext& ctx);
void BeginBlockingHomeButton(HLERequestContext& ctx);
void EndBlockingHomeButton(HLERequestContext& ctx);
void EnableApplicationCrashReport(HLERequestContext& ctx);
void InitializeApplicationCopyrightFrameBuffer(HLERequestContext& ctx);
void SetApplicationCopyrightImage(HLERequestContext& ctx);
void SetApplicationCopyrightVisibility(HLERequestContext& ctx);
void QueryApplicationPlayStatistics(HLERequestContext& ctx);
void QueryApplicationPlayStatisticsByUid(HLERequestContext& ctx);
void ExecuteProgram(HLERequestContext& ctx);
void ClearUserChannel(HLERequestContext& ctx);
void UnpopToUserChannel(HLERequestContext& ctx);
void GetPreviousProgramIndex(HLERequestContext& ctx);
void GetGpuErrorDetectedSystemEvent(HLERequestContext& ctx);
void GetFriendInvitationStorageChannelEvent(HLERequestContext& ctx);
void TryPopFromFriendInvitationStorageChannel(HLERequestContext& ctx);
void GetNotificationStorageChannelEvent(HLERequestContext& ctx);
void GetHealthWarningDisappearedSystemEvent(HLERequestContext& ctx);
void PrepareForJit(HLERequestContext& ctx);
const std::shared_ptr<Applet> applet;
};
} // namespace Service::AM

View File

@ -0,0 +1,115 @@
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "core/hle/service/am/applet_common_functions.h"
#include "core/hle/service/am/application_functions.h"
#include "core/hle/service/am/application_proxy.h"
#include "core/hle/service/am/audio_controller.h"
#include "core/hle/service/am/common_state_getter.h"
#include "core/hle/service/am/debug_functions.h"
#include "core/hle/service/am/display_controller.h"
#include "core/hle/service/am/library_applet_creator.h"
#include "core/hle/service/am/library_applet_self_accessor.h"
#include "core/hle/service/am/process_winding_controller.h"
#include "core/hle/service/am/self_controller.h"
#include "core/hle/service/am/window_controller.h"
#include "core/hle/service/ipc_helpers.h"
namespace Service::AM {
IApplicationProxy::IApplicationProxy(Nvnflinger::Nvnflinger& nvnflinger_,
std::shared_ptr<Applet> applet_, Core::System& system_)
: ServiceFramework{system_, "IApplicationProxy"}, nvnflinger{nvnflinger_}, applet{std::move(
applet_)} {
// clang-format off
static const FunctionInfo functions[] = {
{0, &IApplicationProxy::GetCommonStateGetter, "GetCommonStateGetter"},
{1, &IApplicationProxy::GetSelfController, "GetSelfController"},
{2, &IApplicationProxy::GetWindowController, "GetWindowController"},
{3, &IApplicationProxy::GetAudioController, "GetAudioController"},
{4, &IApplicationProxy::GetDisplayController, "GetDisplayController"},
{10, &IApplicationProxy::GetProcessWindingController, "GetProcessWindingController"},
{11, &IApplicationProxy::GetLibraryAppletCreator, "GetLibraryAppletCreator"},
{20, &IApplicationProxy::GetApplicationFunctions, "GetApplicationFunctions"},
{1000, &IApplicationProxy::GetDebugFunctions, "GetDebugFunctions"},
};
// clang-format on
RegisterHandlers(functions);
}
IApplicationProxy::~IApplicationProxy() = default;
void IApplicationProxy::GetAudioController(HLERequestContext& ctx) {
LOG_DEBUG(Service_AM, "called");
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess);
rb.PushIpcInterface<IAudioController>(system);
}
void IApplicationProxy::GetDisplayController(HLERequestContext& ctx) {
LOG_DEBUG(Service_AM, "called");
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess);
rb.PushIpcInterface<IDisplayController>(system, applet);
}
void IApplicationProxy::GetProcessWindingController(HLERequestContext& ctx) {
LOG_DEBUG(Service_AM, "called");
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess);
rb.PushIpcInterface<IProcessWindingController>(system, applet);
}
void IApplicationProxy::GetDebugFunctions(HLERequestContext& ctx) {
LOG_DEBUG(Service_AM, "called");
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess);
rb.PushIpcInterface<IDebugFunctions>(system);
}
void IApplicationProxy::GetWindowController(HLERequestContext& ctx) {
LOG_DEBUG(Service_AM, "called");
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess);
rb.PushIpcInterface<IWindowController>(system, applet);
}
void IApplicationProxy::GetSelfController(HLERequestContext& ctx) {
LOG_DEBUG(Service_AM, "called");
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess);
rb.PushIpcInterface<ISelfController>(system, applet, nvnflinger);
}
void IApplicationProxy::GetCommonStateGetter(HLERequestContext& ctx) {
LOG_DEBUG(Service_AM, "called");
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess);
rb.PushIpcInterface<ICommonStateGetter>(system, applet);
}
void IApplicationProxy::GetLibraryAppletCreator(HLERequestContext& ctx) {
LOG_DEBUG(Service_AM, "called");
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess);
rb.PushIpcInterface<ILibraryAppletCreator>(system, applet);
}
void IApplicationProxy::GetApplicationFunctions(HLERequestContext& ctx) {
LOG_DEBUG(Service_AM, "called");
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess);
rb.PushIpcInterface<IApplicationFunctions>(system, applet);
}
} // namespace Service::AM

View File

@ -0,0 +1,33 @@
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "core/hle/service/service.h"
namespace Service::AM {
struct Applet;
class IApplicationProxy final : public ServiceFramework<IApplicationProxy> {
public:
explicit IApplicationProxy(Nvnflinger::Nvnflinger& nvnflinger_,
std::shared_ptr<Applet> msg_queue_, Core::System& system_);
~IApplicationProxy();
private:
void GetAudioController(HLERequestContext& ctx);
void GetDisplayController(HLERequestContext& ctx);
void GetProcessWindingController(HLERequestContext& ctx);
void GetDebugFunctions(HLERequestContext& ctx);
void GetWindowController(HLERequestContext& ctx);
void GetSelfController(HLERequestContext& ctx);
void GetCommonStateGetter(HLERequestContext& ctx);
void GetLibraryAppletCreator(HLERequestContext& ctx);
void GetApplicationFunctions(HLERequestContext& ctx);
Nvnflinger::Nvnflinger& nvnflinger;
std::shared_ptr<Applet> applet;
};
} // namespace Service::AM

View File

@ -0,0 +1,91 @@
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "core/hle/service/am/audio_controller.h"
#include "core/hle/service/ipc_helpers.h"
namespace Service::AM {
IAudioController::IAudioController(Core::System& system_)
: ServiceFramework{system_, "IAudioController"} {
// clang-format off
static const FunctionInfo functions[] = {
{0, &IAudioController::SetExpectedMasterVolume, "SetExpectedMasterVolume"},
{1, &IAudioController::GetMainAppletExpectedMasterVolume, "GetMainAppletExpectedMasterVolume"},
{2, &IAudioController::GetLibraryAppletExpectedMasterVolume, "GetLibraryAppletExpectedMasterVolume"},
{3, &IAudioController::ChangeMainAppletMasterVolume, "ChangeMainAppletMasterVolume"},
{4, &IAudioController::SetTransparentAudioRate, "SetTransparentVolumeRate"},
};
// clang-format on
RegisterHandlers(functions);
}
IAudioController::~IAudioController() = default;
void IAudioController::SetExpectedMasterVolume(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const float main_applet_volume_tmp = rp.Pop<float>();
const float library_applet_volume_tmp = rp.Pop<float>();
LOG_DEBUG(Service_AM, "called. main_applet_volume={}, library_applet_volume={}",
main_applet_volume_tmp, library_applet_volume_tmp);
// Ensure the volume values remain within the 0-100% range
main_applet_volume = std::clamp(main_applet_volume_tmp, min_allowed_volume, max_allowed_volume);
library_applet_volume =
std::clamp(library_applet_volume_tmp, min_allowed_volume, max_allowed_volume);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
void IAudioController::GetMainAppletExpectedMasterVolume(HLERequestContext& ctx) {
LOG_DEBUG(Service_AM, "called. main_applet_volume={}", main_applet_volume);
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
rb.Push(main_applet_volume);
}
void IAudioController::GetLibraryAppletExpectedMasterVolume(HLERequestContext& ctx) {
LOG_DEBUG(Service_AM, "called. library_applet_volume={}", library_applet_volume);
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
rb.Push(library_applet_volume);
}
void IAudioController::ChangeMainAppletMasterVolume(HLERequestContext& ctx) {
struct Parameters {
float volume;
s64 fade_time_ns;
};
static_assert(sizeof(Parameters) == 16);
IPC::RequestParser rp{ctx};
const auto parameters = rp.PopRaw<Parameters>();
LOG_DEBUG(Service_AM, "called. volume={}, fade_time_ns={}", parameters.volume,
parameters.fade_time_ns);
main_applet_volume = std::clamp(parameters.volume, min_allowed_volume, max_allowed_volume);
fade_time_ns = std::chrono::nanoseconds{parameters.fade_time_ns};
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
void IAudioController::SetTransparentAudioRate(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const float transparent_volume_rate_tmp = rp.Pop<float>();
LOG_DEBUG(Service_AM, "called. transparent_volume_rate={}", transparent_volume_rate_tmp);
// Clamp volume range to 0-100%.
transparent_volume_rate =
std::clamp(transparent_volume_rate_tmp, min_allowed_volume, max_allowed_volume);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
} // namespace Service::AM

View File

@ -0,0 +1,36 @@
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "core/hle/service/service.h"
namespace Service::AM {
class IAudioController final : public ServiceFramework<IAudioController> {
public:
explicit IAudioController(Core::System& system_);
~IAudioController() override;
private:
void SetExpectedMasterVolume(HLERequestContext& ctx);
void GetMainAppletExpectedMasterVolume(HLERequestContext& ctx);
void GetLibraryAppletExpectedMasterVolume(HLERequestContext& ctx);
void ChangeMainAppletMasterVolume(HLERequestContext& ctx);
void SetTransparentAudioRate(HLERequestContext& ctx);
static constexpr float min_allowed_volume = 0.0f;
static constexpr float max_allowed_volume = 1.0f;
float main_applet_volume{0.25f};
float library_applet_volume{max_allowed_volume};
float transparent_volume_rate{min_allowed_volume};
// Volume transition fade time in nanoseconds.
// e.g. If the main applet volume was 0% and was changed to 50%
// with a fade of 50ns, then over the course of 50ns,
// the volume will gradually fade up to 50%
std::chrono::nanoseconds fade_time_ns{0};
};
} // namespace Service::AM

View File

@ -0,0 +1,314 @@
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "common/settings.h"
#include "core/hle/service/am/am_results.h"
#include "core/hle/service/am/applet.h"
#include "core/hle/service/am/common_state_getter.h"
#include "core/hle/service/am/lock_accessor.h"
#include "core/hle/service/apm/apm_controller.h"
#include "core/hle/service/apm/apm_interface.h"
#include "core/hle/service/ipc_helpers.h"
#include "core/hle/service/pm/pm.h"
#include "core/hle/service/sm/sm.h"
#include "core/hle/service/vi/vi.h"
namespace Service::AM {
ICommonStateGetter::ICommonStateGetter(Core::System& system_, std::shared_ptr<Applet> applet_)
: ServiceFramework{system_, "ICommonStateGetter"}, applet{std::move(applet_)} {
// clang-format off
static const FunctionInfo functions[] = {
{0, &ICommonStateGetter::GetEventHandle, "GetEventHandle"},
{1, &ICommonStateGetter::ReceiveMessage, "ReceiveMessage"},
{2, nullptr, "GetThisAppletKind"},
{3, nullptr, "AllowToEnterSleep"},
{4, nullptr, "DisallowToEnterSleep"},
{5, &ICommonStateGetter::GetOperationMode, "GetOperationMode"},
{6, &ICommonStateGetter::GetPerformanceMode, "GetPerformanceMode"},
{7, nullptr, "GetCradleStatus"},
{8, &ICommonStateGetter::GetBootMode, "GetBootMode"},
{9, &ICommonStateGetter::GetCurrentFocusState, "GetCurrentFocusState"},
{10, &ICommonStateGetter::RequestToAcquireSleepLock, "RequestToAcquireSleepLock"},
{11, nullptr, "ReleaseSleepLock"},
{12, nullptr, "ReleaseSleepLockTransiently"},
{13, &ICommonStateGetter::GetAcquiredSleepLockEvent, "GetAcquiredSleepLockEvent"},
{14, nullptr, "GetWakeupCount"},
{20, nullptr, "PushToGeneralChannel"},
{30, nullptr, "GetHomeButtonReaderLockAccessor"},
{31, &ICommonStateGetter::GetReaderLockAccessorEx, "GetReaderLockAccessorEx"},
{32, nullptr, "GetWriterLockAccessorEx"},
{40, nullptr, "GetCradleFwVersion"},
{50, &ICommonStateGetter::IsVrModeEnabled, "IsVrModeEnabled"},
{51, &ICommonStateGetter::SetVrModeEnabled, "SetVrModeEnabled"},
{52, &ICommonStateGetter::SetLcdBacklighOffEnabled, "SetLcdBacklighOffEnabled"},
{53, &ICommonStateGetter::BeginVrModeEx, "BeginVrModeEx"},
{54, &ICommonStateGetter::EndVrModeEx, "EndVrModeEx"},
{55, nullptr, "IsInControllerFirmwareUpdateSection"},
{59, nullptr, "SetVrPositionForDebug"},
{60, &ICommonStateGetter::GetDefaultDisplayResolution, "GetDefaultDisplayResolution"},
{61, &ICommonStateGetter::GetDefaultDisplayResolutionChangeEvent, "GetDefaultDisplayResolutionChangeEvent"},
{62, nullptr, "GetHdcpAuthenticationState"},
{63, nullptr, "GetHdcpAuthenticationStateChangeEvent"},
{64, nullptr, "SetTvPowerStateMatchingMode"},
{65, nullptr, "GetApplicationIdByContentActionName"},
{66, &ICommonStateGetter::SetCpuBoostMode, "SetCpuBoostMode"},
{67, nullptr, "CancelCpuBoostMode"},
{68, &ICommonStateGetter::GetBuiltInDisplayType, "GetBuiltInDisplayType"},
{80, &ICommonStateGetter::PerformSystemButtonPressingIfInFocus, "PerformSystemButtonPressingIfInFocus"},
{90, nullptr, "SetPerformanceConfigurationChangedNotification"},
{91, nullptr, "GetCurrentPerformanceConfiguration"},
{100, nullptr, "SetHandlingHomeButtonShortPressedEnabled"},
{110, nullptr, "OpenMyGpuErrorHandler"},
{120, &ICommonStateGetter::GetAppletLaunchedHistory, "GetAppletLaunchedHistory"},
{200, nullptr, "GetOperationModeSystemInfo"},
{300, &ICommonStateGetter::GetSettingsPlatformRegion, "GetSettingsPlatformRegion"},
{400, nullptr, "ActivateMigrationService"},
{401, nullptr, "DeactivateMigrationService"},
{500, nullptr, "DisableSleepTillShutdown"},
{501, nullptr, "SuppressDisablingSleepTemporarily"},
{502, nullptr, "IsSleepEnabled"},
{503, nullptr, "IsDisablingSleepSuppressed"},
{900, &ICommonStateGetter::SetRequestExitToLibraryAppletAtExecuteNextProgramEnabled, "SetRequestExitToLibraryAppletAtExecuteNextProgramEnabled"},
};
// clang-format on
RegisterHandlers(functions);
}
ICommonStateGetter::~ICommonStateGetter() = default;
void ICommonStateGetter::GetBootMode(HLERequestContext& ctx) {
LOG_DEBUG(Service_AM, "called");
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
rb.Push<u8>(static_cast<u8>(Service::PM::SystemBootMode::Normal)); // Normal boot mode
}
void ICommonStateGetter::GetEventHandle(HLERequestContext& ctx) {
LOG_DEBUG(Service_AM, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 2, 1};
rb.Push(ResultSuccess);
rb.PushCopyObjects(applet->message_queue.GetMessageReceiveEvent());
}
void ICommonStateGetter::ReceiveMessage(HLERequestContext& ctx) {
LOG_DEBUG(Service_AM, "called");
const auto message = applet->message_queue.PopMessage();
IPC::ResponseBuilder rb{ctx, 3};
if (message == AppletMessageQueue::AppletMessage::None) {
LOG_ERROR(Service_AM, "Message queue is empty");
rb.Push(AM::ResultNoMessages);
rb.PushEnum<AppletMessageQueue::AppletMessage>(message);
return;
}
rb.Push(ResultSuccess);
rb.PushEnum<AppletMessageQueue::AppletMessage>(message);
}
void ICommonStateGetter::GetCurrentFocusState(HLERequestContext& ctx) {
LOG_DEBUG(Service_AM, "(STUBBED) called");
std::scoped_lock lk{applet->lock};
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
rb.Push(static_cast<u8>(applet->focus_state));
}
void ICommonStateGetter::GetOperationMode(HLERequestContext& ctx) {
const bool use_docked_mode{Settings::IsDockedMode()};
LOG_DEBUG(Service_AM, "called, use_docked_mode={}", use_docked_mode);
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
rb.Push(static_cast<u8>(use_docked_mode ? OperationMode::Docked : OperationMode::Handheld));
}
void ICommonStateGetter::GetPerformanceMode(HLERequestContext& ctx) {
LOG_DEBUG(Service_AM, "called");
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
rb.PushEnum(system.GetAPMController().GetCurrentPerformanceMode());
}
void ICommonStateGetter::RequestToAcquireSleepLock(HLERequestContext& ctx) {
LOG_WARNING(Service_AM, "(STUBBED) called");
// Sleep lock is acquired immediately.
applet->sleep_lock_event.Signal();
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
void ICommonStateGetter::GetReaderLockAccessorEx(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto unknown = rp.Pop<u32>();
LOG_INFO(Service_AM, "called, unknown={}", unknown);
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess);
rb.PushIpcInterface<ILockAccessor>(system);
}
void ICommonStateGetter::GetAcquiredSleepLockEvent(HLERequestContext& ctx) {
LOG_WARNING(Service_AM, "called");
IPC::ResponseBuilder rb{ctx, 2, 1};
rb.Push(ResultSuccess);
rb.PushCopyObjects(applet->sleep_lock_event.GetHandle());
}
void ICommonStateGetter::IsVrModeEnabled(HLERequestContext& ctx) {
LOG_DEBUG(Service_AM, "called");
std::scoped_lock lk{applet->lock};
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
rb.Push(applet->vr_mode_enabled);
}
void ICommonStateGetter::SetVrModeEnabled(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
std::scoped_lock lk{applet->lock};
applet->vr_mode_enabled = rp.Pop<bool>();
LOG_WARNING(Service_AM, "VR Mode is {}", applet->vr_mode_enabled ? "on" : "off");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
void ICommonStateGetter::SetLcdBacklighOffEnabled(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto is_lcd_backlight_off_enabled = rp.Pop<bool>();
LOG_WARNING(Service_AM, "(STUBBED) called. is_lcd_backlight_off_enabled={}",
is_lcd_backlight_off_enabled);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
void ICommonStateGetter::BeginVrModeEx(HLERequestContext& ctx) {
LOG_WARNING(Service_AM, "(STUBBED) called");
std::scoped_lock lk{applet->lock};
applet->vr_mode_enabled = true;
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
void ICommonStateGetter::EndVrModeEx(HLERequestContext& ctx) {
LOG_WARNING(Service_AM, "(STUBBED) called");
std::scoped_lock lk{applet->lock};
applet->vr_mode_enabled = false;
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
void ICommonStateGetter::GetDefaultDisplayResolutionChangeEvent(HLERequestContext& ctx) {
LOG_DEBUG(Service_AM, "called");
IPC::ResponseBuilder rb{ctx, 2, 1};
rb.Push(ResultSuccess);
rb.PushCopyObjects(applet->message_queue.GetOperationModeChangedEvent());
}
void ICommonStateGetter::GetDefaultDisplayResolution(HLERequestContext& ctx) {
LOG_DEBUG(Service_AM, "called");
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(ResultSuccess);
if (Settings::IsDockedMode()) {
rb.Push(static_cast<u32>(Service::VI::DisplayResolution::DockedWidth));
rb.Push(static_cast<u32>(Service::VI::DisplayResolution::DockedHeight));
} else {
rb.Push(static_cast<u32>(Service::VI::DisplayResolution::UndockedWidth));
rb.Push(static_cast<u32>(Service::VI::DisplayResolution::UndockedHeight));
}
}
void ICommonStateGetter::SetCpuBoostMode(HLERequestContext& ctx) {
LOG_DEBUG(Service_AM, "called, forwarding to APM:SYS");
const auto& sm = system.ServiceManager();
const auto apm_sys = sm.GetService<APM::APM_Sys>("apm:sys");
ASSERT(apm_sys != nullptr);
apm_sys->SetCpuBoostMode(ctx);
}
void ICommonStateGetter::GetBuiltInDisplayType(HLERequestContext& ctx) {
LOG_WARNING(Service_AM, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
rb.Push(0);
}
void ICommonStateGetter::PerformSystemButtonPressingIfInFocus(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto system_button{rp.PopEnum<SystemButtonType>()};
LOG_WARNING(Service_AM, "(STUBBED) called, system_button={}", system_button);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
void ICommonStateGetter::GetAppletLaunchedHistory(HLERequestContext& ctx) {
LOG_WARNING(Service_AM, "(STUBBED) called");
std::shared_ptr<Applet> current_applet = applet;
std::vector<AppletId> result;
const size_t count = ctx.GetWriteBufferNumElements<AppletId>();
size_t i;
for (i = 0; i < count && current_applet != nullptr; i++) {
result.push_back(current_applet->applet_id);
current_applet = current_applet->caller_applet.lock();
}
ctx.WriteBuffer(result);
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
rb.Push(static_cast<u32>(i));
}
void ICommonStateGetter::GetSettingsPlatformRegion(HLERequestContext& ctx) {
LOG_WARNING(Service_AM, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
rb.PushEnum(SysPlatformRegion::Global);
}
void ICommonStateGetter::SetRequestExitToLibraryAppletAtExecuteNextProgramEnabled(
HLERequestContext& ctx) {
LOG_WARNING(Service_AM, "(STUBBED) called");
std::scoped_lock lk{applet->lock};
applet->request_exit_to_library_applet_at_execute_next_program_enabled = true;
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
} // namespace Service::AM

View File

@ -0,0 +1,77 @@
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "core/hle/service/kernel_helpers.h"
#include "core/hle/service/service.h"
#include "core/hle/service/am/applet_message_queue.h"
namespace Service::AM {
struct Applet;
class ICommonStateGetter final : public ServiceFramework<ICommonStateGetter> {
public:
explicit ICommonStateGetter(Core::System& system_, std::shared_ptr<Applet> applet_);
~ICommonStateGetter() override;
private:
// This is nn::oe::FocusState
enum class FocusState : u8 {
InFocus = 1,
NotInFocus = 2,
Background = 3,
};
// This is nn::oe::OperationMode
enum class OperationMode : u8 {
Handheld = 0,
Docked = 1,
};
// This is nn::am::service::SystemButtonType
enum class SystemButtonType {
None,
HomeButtonShortPressing,
HomeButtonLongPressing,
PowerButtonShortPressing,
PowerButtonLongPressing,
ShutdownSystem,
CaptureButtonShortPressing,
CaptureButtonLongPressing,
};
enum class SysPlatformRegion : s32 {
Global = 1,
Terra = 2,
};
void GetEventHandle(HLERequestContext& ctx);
void ReceiveMessage(HLERequestContext& ctx);
void GetCurrentFocusState(HLERequestContext& ctx);
void RequestToAcquireSleepLock(HLERequestContext& ctx);
void GetAcquiredSleepLockEvent(HLERequestContext& ctx);
void GetReaderLockAccessorEx(HLERequestContext& ctx);
void GetDefaultDisplayResolutionChangeEvent(HLERequestContext& ctx);
void GetOperationMode(HLERequestContext& ctx);
void GetPerformanceMode(HLERequestContext& ctx);
void GetBootMode(HLERequestContext& ctx);
void IsVrModeEnabled(HLERequestContext& ctx);
void SetVrModeEnabled(HLERequestContext& ctx);
void SetLcdBacklighOffEnabled(HLERequestContext& ctx);
void BeginVrModeEx(HLERequestContext& ctx);
void EndVrModeEx(HLERequestContext& ctx);
void GetDefaultDisplayResolution(HLERequestContext& ctx);
void SetCpuBoostMode(HLERequestContext& ctx);
void GetBuiltInDisplayType(HLERequestContext& ctx);
void PerformSystemButtonPressingIfInFocus(HLERequestContext& ctx);
void GetAppletLaunchedHistory(HLERequestContext& ctx);
void GetSettingsPlatformRegion(HLERequestContext& ctx);
void SetRequestExitToLibraryAppletAtExecuteNextProgramEnabled(HLERequestContext& ctx);
const std::shared_ptr<Applet> applet;
};
} // namespace Service::AM

View File

@ -0,0 +1,44 @@
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "core/hle/service/am/debug_functions.h"
#include "core/hle/service/ipc_helpers.h"
namespace Service::AM {
IDebugFunctions::IDebugFunctions(Core::System& system_)
: ServiceFramework{system_, "IDebugFunctions"} {
// clang-format off
static const FunctionInfo functions[] = {
{0, nullptr, "NotifyMessageToHomeMenuForDebug"},
{1, nullptr, "OpenMainApplication"},
{10, nullptr, "PerformSystemButtonPressing"},
{20, nullptr, "InvalidateTransitionLayer"},
{30, nullptr, "RequestLaunchApplicationWithUserAndArgumentForDebug"},
{31, nullptr, "RequestLaunchApplicationByApplicationLaunchInfoForDebug"},
{40, nullptr, "GetAppletResourceUsageInfo"},
{50, nullptr, "AddSystemProgramIdAndAppletIdForDebug"},
{51, nullptr, "AddOperationConfirmedLibraryAppletIdForDebug"},
{100, nullptr, "SetCpuBoostModeForApplet"},
{101, nullptr, "CancelCpuBoostModeForApplet"},
{110, nullptr, "PushToAppletBoundChannelForDebug"},
{111, nullptr, "TryPopFromAppletBoundChannelForDebug"},
{120, nullptr, "AlarmSettingNotificationEnableAppEventReserve"},
{121, nullptr, "AlarmSettingNotificationDisableAppEventReserve"},
{122, nullptr, "AlarmSettingNotificationPushAppEventNotify"},
{130, nullptr, "FriendInvitationSetApplicationParameter"},
{131, nullptr, "FriendInvitationClearApplicationParameter"},
{132, nullptr, "FriendInvitationPushApplicationParameter"},
{140, nullptr, "RestrictPowerOperationForSecureLaunchModeForDebug"},
{200, nullptr, "CreateFloatingLibraryAppletAccepterForDebug"},
{300, nullptr, "TerminateAllRunningApplicationsForDebug"},
{900, nullptr, "GetGrcProcessLaunchedSystemEvent"},
};
// clang-format on
RegisterHandlers(functions);
}
IDebugFunctions::~IDebugFunctions() = default;
} // namespace Service::AM

View File

@ -0,0 +1,16 @@
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "core/hle/service/service.h"
namespace Service::AM {
class IDebugFunctions final : public ServiceFramework<IDebugFunctions> {
public:
explicit IDebugFunctions(Core::System& system_);
~IDebugFunctions() override;
};
} // namespace Service::AM

View File

@ -0,0 +1,139 @@
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "core/hle/service/am/applet.h"
#include "core/hle/service/am/display_controller.h"
#include "core/hle/service/ipc_helpers.h"
namespace Service::AM {
namespace {
struct OutputParameters {
bool was_written;
s32 fbshare_layer_index;
};
static_assert(sizeof(OutputParameters) == 8, "OutputParameters has wrong size");
} // namespace
IDisplayController::IDisplayController(Core::System& system_, std::shared_ptr<Applet> applet_)
: ServiceFramework{system_, "IDisplayController"}, applet(std::move(applet_)) {
// clang-format off
static const FunctionInfo functions[] = {
{0, nullptr, "GetLastForegroundCaptureImage"},
{1, nullptr, "UpdateLastForegroundCaptureImage"},
{2, nullptr, "GetLastApplicationCaptureImage"},
{3, nullptr, "GetCallerAppletCaptureImage"},
{4, nullptr, "UpdateCallerAppletCaptureImage"},
{5, nullptr, "GetLastForegroundCaptureImageEx"},
{6, nullptr, "GetLastApplicationCaptureImageEx"},
{7, &IDisplayController::GetCallerAppletCaptureImageEx, "GetCallerAppletCaptureImageEx"},
{8, &IDisplayController::TakeScreenShotOfOwnLayer, "TakeScreenShotOfOwnLayer"},
{9, nullptr, "CopyBetweenCaptureBuffers"},
{10, nullptr, "AcquireLastApplicationCaptureBuffer"},
{11, nullptr, "ReleaseLastApplicationCaptureBuffer"},
{12, nullptr, "AcquireLastForegroundCaptureBuffer"},
{13, nullptr, "ReleaseLastForegroundCaptureBuffer"},
{14, nullptr, "AcquireCallerAppletCaptureBuffer"},
{15, nullptr, "ReleaseCallerAppletCaptureBuffer"},
{16, nullptr, "AcquireLastApplicationCaptureBufferEx"},
{17, nullptr, "AcquireLastForegroundCaptureBufferEx"},
{18, nullptr, "AcquireCallerAppletCaptureBufferEx"},
{20, nullptr, "ClearCaptureBuffer"},
{21, nullptr, "ClearAppletTransitionBuffer"},
{22, &IDisplayController::AcquireLastApplicationCaptureSharedBuffer, "AcquireLastApplicationCaptureSharedBuffer"},
{23, &IDisplayController::ReleaseLastApplicationCaptureSharedBuffer, "ReleaseLastApplicationCaptureSharedBuffer"},
{24, &IDisplayController::AcquireLastForegroundCaptureSharedBuffer, "AcquireLastForegroundCaptureSharedBuffer"},
{25, &IDisplayController::ReleaseLastForegroundCaptureSharedBuffer, "ReleaseLastForegroundCaptureSharedBuffer"},
{26, &IDisplayController::AcquireCallerAppletCaptureSharedBuffer, "AcquireCallerAppletCaptureSharedBuffer"},
{27, &IDisplayController::ReleaseCallerAppletCaptureSharedBuffer, "ReleaseCallerAppletCaptureSharedBuffer"},
{28, nullptr, "TakeScreenShotOfOwnLayerEx"},
};
// clang-format on
RegisterHandlers(functions);
}
IDisplayController::~IDisplayController() = default;
void IDisplayController::GetCallerAppletCaptureImageEx(HLERequestContext& ctx) {
LOG_WARNING(Service_AM, "(STUBBED) called");
OutputParameters params{};
const auto res = applet->system_buffer_manager.WriteApplicationCaptureBuffer(
&params.was_written, &params.fbshare_layer_index);
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(res);
rb.Push(params.was_written);
rb.Push(params.fbshare_layer_index);
}
void IDisplayController::TakeScreenShotOfOwnLayer(HLERequestContext& ctx) {
LOG_WARNING(Service_AM, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
void IDisplayController::AcquireLastApplicationCaptureSharedBuffer(HLERequestContext& ctx) {
LOG_WARNING(Service_AM, "(STUBBED) called");
OutputParameters params{};
const auto res = applet->system_buffer_manager.WriteApplicationCaptureBuffer(
&params.was_written, &params.fbshare_layer_index);
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(res);
rb.Push(params.was_written);
rb.Push(params.fbshare_layer_index);
}
void IDisplayController::ReleaseLastApplicationCaptureSharedBuffer(HLERequestContext& ctx) {
LOG_WARNING(Service_AM, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
void IDisplayController::AcquireLastForegroundCaptureSharedBuffer(HLERequestContext& ctx) {
LOG_WARNING(Service_AM, "(STUBBED) called");
OutputParameters params{};
const auto res = applet->system_buffer_manager.WriteApplicationCaptureBuffer(
&params.was_written, &params.fbshare_layer_index);
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(res);
rb.Push(params.was_written);
rb.Push(params.fbshare_layer_index);
}
void IDisplayController::ReleaseLastForegroundCaptureSharedBuffer(HLERequestContext& ctx) {
LOG_WARNING(Service_AM, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
void IDisplayController::AcquireCallerAppletCaptureSharedBuffer(HLERequestContext& ctx) {
LOG_WARNING(Service_AM, "(STUBBED) called");
OutputParameters params{};
const auto res = applet->system_buffer_manager.WriteApplicationCaptureBuffer(
&params.was_written, &params.fbshare_layer_index);
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(res);
rb.Push(params.was_written);
rb.Push(params.fbshare_layer_index);
}
void IDisplayController::ReleaseCallerAppletCaptureSharedBuffer(HLERequestContext& ctx) {
LOG_WARNING(Service_AM, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
} // namespace Service::AM

View File

@ -0,0 +1,30 @@
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "core/hle/service/service.h"
namespace Service::AM {
struct Applet;
class IDisplayController final : public ServiceFramework<IDisplayController> {
public:
explicit IDisplayController(Core::System& system_, std::shared_ptr<Applet> applet_);
~IDisplayController() override;
private:
void GetCallerAppletCaptureImageEx(HLERequestContext& ctx);
void TakeScreenShotOfOwnLayer(HLERequestContext& ctx);
void AcquireLastForegroundCaptureSharedBuffer(HLERequestContext& ctx);
void ReleaseLastForegroundCaptureSharedBuffer(HLERequestContext& ctx);
void AcquireCallerAppletCaptureSharedBuffer(HLERequestContext& ctx);
void ReleaseCallerAppletCaptureSharedBuffer(HLERequestContext& ctx);
void AcquireLastApplicationCaptureSharedBuffer(HLERequestContext& ctx);
void ReleaseLastApplicationCaptureSharedBuffer(HLERequestContext& ctx);
const std::shared_ptr<Applet> applet;
};
} // namespace Service::AM

View File

@ -0,0 +1,184 @@
// 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/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

View File

@ -0,0 +1,113 @@
// 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

View File

@ -0,0 +1,271 @@
// 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/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

View File

@ -0,0 +1,156 @@
// 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

View File

@ -0,0 +1,219 @@
// 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/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>()));
Exit();
}
Result Error::RequestExit() {
frontend.Close();
R_SUCCEED();
}
} // namespace Service::AM::Frontend

View File

@ -0,0 +1,51 @@
// 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

View File

@ -0,0 +1,250 @@
// 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/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

View File

@ -0,0 +1,89 @@
// 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

View File

@ -0,0 +1,177 @@
// 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/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::MiiDBModule>("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

View File

@ -0,0 +1,51 @@
// 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

View File

@ -0,0 +1,83 @@
// 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

View File

@ -0,0 +1,123 @@
// 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/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

View File

@ -0,0 +1,142 @@
// 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

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,186 @@
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "common/common_types.h"
#include "core/hle/result.h"
#include "core/hle/service/am/frontend/applet_software_keyboard_types.h"
#include "core/hle/service/am/frontend/applets.h"
namespace Core {
class System;
}
namespace Core::Frontend {
struct KeyboardInitializeParameters;
struct InlineAppearParameters;
} // namespace Core::Frontend
namespace Service::AM::Frontend {
class SoftwareKeyboard final : public FrontendApplet {
public:
explicit SoftwareKeyboard(Core::System& system_, std::shared_ptr<Applet> applet_,
LibraryAppletMode applet_mode_,
Core::Frontend::SoftwareKeyboardApplet& frontend_);
~SoftwareKeyboard() override;
void Initialize() override;
Result GetStatus() const override;
void ExecuteInteractive() override;
void Execute() override;
Result RequestExit() override;
/**
* Submits the input text to the application.
* If text checking is enabled, the application will verify the input text.
* If use_utf8 is enabled, the input text will be converted to UTF-8 prior to being submitted.
* This should only be used by the normal software keyboard.
*
* @param result SwkbdResult enum
* @param submitted_text UTF-16 encoded string
* @param confirmed Whether the text has been confirmed after TextCheckResult::Confirm
*/
void SubmitTextNormal(SwkbdResult result, std::u16string submitted_text, bool confirmed);
/**
* Submits the input text to the application.
* If utf8_mode is enabled, the input text will be converted to UTF-8 prior to being submitted.
* This should only be used by the inline software keyboard.
*
* @param reply_type SwkbdReplyType enum
* @param submitted_text UTF-16 encoded string
* @param cursor_position The current position of the text cursor
*/
void SubmitTextInline(SwkbdReplyType reply_type, std::u16string submitted_text,
s32 cursor_position);
private:
/// Initializes the normal software keyboard.
void InitializeForeground();
/// Initializes the inline software keyboard.
void InitializeBackground(LibraryAppletMode library_applet_mode);
/// Processes the text check sent by the application.
void ProcessTextCheck();
/// Processes the inline software keyboard request command sent by the application.
void ProcessInlineKeyboardRequest();
/// Submits the input text and exits the applet.
void SubmitNormalOutputAndExit(SwkbdResult result, std::u16string submitted_text);
/// Submits the input text for text checking.
void SubmitForTextCheck(std::u16string submitted_text);
/// Sends a reply to the application after processing a request command.
void SendReply(SwkbdReplyType reply_type);
/// Changes the inline keyboard state.
void ChangeState(SwkbdState state);
/**
* Signals the frontend to initialize the normal software keyboard with common parameters.
* Note that this does not cause the keyboard to appear.
* Use the ShowNormalKeyboard() functions to cause the keyboard to appear.
*/
void InitializeFrontendNormalKeyboard();
/**
* Signals the frontend to initialize the inline software keyboard with common parameters.
* Note that this does not cause the keyboard to appear.
* Use the ShowInlineKeyboard() to cause the keyboard to appear.
*/
void InitializeFrontendInlineKeyboard(
Core::Frontend::KeyboardInitializeParameters initialize_parameters);
void InitializeFrontendInlineKeyboardOld();
void InitializeFrontendInlineKeyboardNew();
/// Signals the frontend to show the normal software keyboard.
void ShowNormalKeyboard();
/// Signals the frontend to show the text check dialog.
void ShowTextCheckDialog(SwkbdTextCheckResult text_check_result,
std::u16string text_check_message);
/// Signals the frontend to show the inline software keyboard.
void ShowInlineKeyboard(Core::Frontend::InlineAppearParameters appear_parameters);
void ShowInlineKeyboardOld();
void ShowInlineKeyboardNew();
/// Signals the frontend to hide the inline software keyboard.
void HideInlineKeyboard();
/// Signals the frontend that the current inline keyboard text has changed.
void InlineTextChanged();
/// Signals both the frontend and application that the software keyboard is exiting.
void ExitKeyboard();
// Inline Software Keyboard Requests
void RequestFinalize(const std::vector<u8>& request_data);
void RequestSetUserWordInfo(const std::vector<u8>& request_data);
void RequestSetCustomizeDic(const std::vector<u8>& request_data);
void RequestCalc(const std::vector<u8>& request_data);
void RequestCalcOld();
void RequestCalcNew();
void RequestSetCustomizedDictionaries(const std::vector<u8>& request_data);
void RequestUnsetCustomizedDictionaries(const std::vector<u8>& request_data);
void RequestSetChangedStringV2Flag(const std::vector<u8>& request_data);
void RequestSetMovedCursorV2Flag(const std::vector<u8>& request_data);
// Inline Software Keyboard Replies
void ReplyFinishedInitialize();
void ReplyDefault();
void ReplyChangedString();
void ReplyMovedCursor();
void ReplyMovedTab();
void ReplyDecidedEnter();
void ReplyDecidedCancel();
void ReplyChangedStringUtf8();
void ReplyMovedCursorUtf8();
void ReplyDecidedEnterUtf8();
void ReplyUnsetCustomizeDic();
void ReplyReleasedUserWordInfo();
void ReplyUnsetCustomizedDictionaries();
void ReplyChangedStringV2();
void ReplyMovedCursorV2();
void ReplyChangedStringUtf8V2();
void ReplyMovedCursorUtf8V2();
Core::Frontend::SoftwareKeyboardApplet& frontend;
SwkbdAppletVersion swkbd_applet_version;
SwkbdConfigCommon swkbd_config_common;
SwkbdConfigOld swkbd_config_old;
SwkbdConfigOld2 swkbd_config_old2;
SwkbdConfigNew swkbd_config_new;
std::u16string initial_text;
SwkbdState swkbd_state{SwkbdState::NotInitialized};
SwkbdInitializeArg swkbd_initialize_arg;
SwkbdCalcArgCommon swkbd_calc_arg_common;
SwkbdCalcArgOld swkbd_calc_arg_old;
SwkbdCalcArgNew swkbd_calc_arg_new;
bool use_changed_string_v2{false};
bool use_moved_cursor_v2{false};
bool inline_use_utf8{false};
s32 current_cursor_position{};
std::u16string current_text;
bool is_background{false};
bool complete{false};
Result status{ResultSuccess};
};
} // namespace Service::AM::Frontend

View File

@ -0,0 +1,354 @@
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <array>
#include "common/bit_field.h"
#include "common/common_funcs.h"
#include "common/common_types.h"
#include "common/swap.h"
#include "common/uuid.h"
namespace Service::AM::Frontend {
constexpr std::size_t MAX_OK_TEXT_LENGTH = 8;
constexpr std::size_t MAX_HEADER_TEXT_LENGTH = 64;
constexpr std::size_t MAX_SUB_TEXT_LENGTH = 128;
constexpr std::size_t MAX_GUIDE_TEXT_LENGTH = 256;
constexpr std::size_t STRING_BUFFER_SIZE = 0x7D4;
enum class SwkbdAppletVersion : u32_le {
Version5 = 0x5, // 1.0.0
Version65542 = 0x10006, // 2.0.0 - 2.3.0
Version196615 = 0x30007, // 3.0.0 - 3.0.2
Version262152 = 0x40008, // 4.0.0 - 4.1.0
Version327689 = 0x50009, // 5.0.0 - 5.1.0
Version393227 = 0x6000B, // 6.0.0 - 7.0.1
Version524301 = 0x8000D, // 8.0.0+
};
enum class SwkbdType : u32 {
Normal,
NumberPad,
Qwerty,
Unknown3,
Latin,
SimplifiedChinese,
TraditionalChinese,
Korean,
};
enum class SwkbdInitialCursorPosition : u32 {
Start,
End,
};
enum class SwkbdPasswordMode : u32 {
Disabled,
Enabled,
};
enum class SwkbdTextDrawType : u32 {
Line,
Box,
DownloadCode,
};
enum class SwkbdResult : u32 {
Ok,
Cancel,
};
enum class SwkbdTextCheckResult : u32 {
Success,
Failure,
Confirm,
Silent,
};
enum class SwkbdState : u32 {
NotInitialized = 0x0,
InitializedIsHidden = 0x1,
InitializedIsAppearing = 0x2,
InitializedIsShown = 0x3,
InitializedIsDisappearing = 0x4,
};
enum class SwkbdRequestCommand : u32 {
Finalize = 0x4,
SetUserWordInfo = 0x6,
SetCustomizeDic = 0x7,
Calc = 0xA,
SetCustomizedDictionaries = 0xB,
UnsetCustomizedDictionaries = 0xC,
SetChangedStringV2Flag = 0xD,
SetMovedCursorV2Flag = 0xE,
};
enum class SwkbdReplyType : u32 {
FinishedInitialize = 0x0,
Default = 0x1,
ChangedString = 0x2,
MovedCursor = 0x3,
MovedTab = 0x4,
DecidedEnter = 0x5,
DecidedCancel = 0x6,
ChangedStringUtf8 = 0x7,
MovedCursorUtf8 = 0x8,
DecidedEnterUtf8 = 0x9,
UnsetCustomizeDic = 0xA,
ReleasedUserWordInfo = 0xB,
UnsetCustomizedDictionaries = 0xC,
ChangedStringV2 = 0xD,
MovedCursorV2 = 0xE,
ChangedStringUtf8V2 = 0xF,
MovedCursorUtf8V2 = 0x10,
};
struct SwkbdKeyDisableFlags {
union {
u32 raw{};
BitField<1, 1, u32> space;
BitField<2, 1, u32> at;
BitField<3, 1, u32> percent;
BitField<4, 1, u32> slash;
BitField<5, 1, u32> backslash;
BitField<6, 1, u32> numbers;
BitField<7, 1, u32> download_code;
BitField<8, 1, u32> username;
};
};
static_assert(sizeof(SwkbdKeyDisableFlags) == 0x4, "SwkbdKeyDisableFlags has incorrect size.");
struct SwkbdConfigCommon {
SwkbdType type{};
std::array<char16_t, MAX_OK_TEXT_LENGTH + 1> ok_text{};
char16_t left_optional_symbol_key{};
char16_t right_optional_symbol_key{};
bool use_prediction{};
INSERT_PADDING_BYTES(1);
SwkbdKeyDisableFlags key_disable_flags{};
SwkbdInitialCursorPosition initial_cursor_position{};
std::array<char16_t, MAX_HEADER_TEXT_LENGTH + 1> header_text{};
std::array<char16_t, MAX_SUB_TEXT_LENGTH + 1> sub_text{};
std::array<char16_t, MAX_GUIDE_TEXT_LENGTH + 1> guide_text{};
u32 max_text_length{};
u32 min_text_length{};
SwkbdPasswordMode password_mode{};
SwkbdTextDrawType text_draw_type{};
bool enable_return_button{};
bool use_utf8{};
bool use_blur_background{};
INSERT_PADDING_BYTES(1);
u32 initial_string_offset{};
u32 initial_string_length{};
u32 user_dictionary_offset{};
u32 user_dictionary_entries{};
bool use_text_check{};
INSERT_PADDING_BYTES(3);
};
static_assert(sizeof(SwkbdConfigCommon) == 0x3D4, "SwkbdConfigCommon has incorrect size.");
#pragma pack(push, 4)
// SwkbdAppletVersion 0x5, 0x10006
struct SwkbdConfigOld {
INSERT_PADDING_WORDS(1);
VAddr text_check_callback{};
};
static_assert(sizeof(SwkbdConfigOld) == 0x3E0 - sizeof(SwkbdConfigCommon),
"SwkbdConfigOld has incorrect size.");
// SwkbdAppletVersion 0x30007, 0x40008, 0x50009
struct SwkbdConfigOld2 {
INSERT_PADDING_WORDS(1);
VAddr text_check_callback{};
std::array<u32, 8> text_grouping{};
};
static_assert(sizeof(SwkbdConfigOld2) == 0x400 - sizeof(SwkbdConfigCommon),
"SwkbdConfigOld2 has incorrect size.");
// SwkbdAppletVersion 0x6000B, 0x8000D
struct SwkbdConfigNew {
std::array<u32, 8> text_grouping{};
std::array<u64, 24> customized_dictionary_set_entries{};
u8 total_customized_dictionary_set_entries{};
bool disable_cancel_button{};
INSERT_PADDING_BYTES(18);
};
static_assert(sizeof(SwkbdConfigNew) == 0x4C8 - sizeof(SwkbdConfigCommon),
"SwkbdConfigNew has incorrect size.");
#pragma pack(pop)
struct SwkbdTextCheck {
SwkbdTextCheckResult text_check_result{};
std::array<char16_t, STRING_BUFFER_SIZE / 2> text_check_message{};
};
static_assert(sizeof(SwkbdTextCheck) == 0x7D8, "SwkbdTextCheck has incorrect size.");
struct SwkbdCalcArgFlags {
union {
u64 raw{};
BitField<0, 1, u64> set_initialize_arg;
BitField<1, 1, u64> set_volume;
BitField<2, 1, u64> appear;
BitField<3, 1, u64> set_input_text;
BitField<4, 1, u64> set_cursor_position;
BitField<5, 1, u64> set_utf8_mode;
BitField<6, 1, u64> unset_customize_dic;
BitField<7, 1, u64> disappear;
BitField<8, 1, u64> unknown;
BitField<9, 1, u64> set_key_top_translate_scale;
BitField<10, 1, u64> unset_user_word_info;
BitField<11, 1, u64> set_disable_hardware_keyboard;
};
};
static_assert(sizeof(SwkbdCalcArgFlags) == 0x8, "SwkbdCalcArgFlags has incorrect size.");
struct SwkbdInitializeArg {
u32 unknown{};
bool library_applet_mode_flag{};
bool is_above_hos_500{};
INSERT_PADDING_BYTES(2);
};
static_assert(sizeof(SwkbdInitializeArg) == 0x8, "SwkbdInitializeArg has incorrect size.");
struct SwkbdAppearArgOld {
SwkbdType type{};
std::array<char16_t, MAX_OK_TEXT_LENGTH + 1> ok_text{};
char16_t left_optional_symbol_key{};
char16_t right_optional_symbol_key{};
bool use_prediction{};
bool disable_cancel_button{};
SwkbdKeyDisableFlags key_disable_flags{};
u32 max_text_length{};
u32 min_text_length{};
bool enable_return_button{};
INSERT_PADDING_BYTES(3);
u32 flags{};
bool is_use_save_data{};
INSERT_PADDING_BYTES(7);
Common::UUID user_id{};
};
static_assert(sizeof(SwkbdAppearArgOld) == 0x48, "SwkbdAppearArg has incorrect size.");
struct SwkbdAppearArgNew {
SwkbdType type{};
std::array<char16_t, MAX_OK_TEXT_LENGTH + 1> ok_text{};
char16_t left_optional_symbol_key{};
char16_t right_optional_symbol_key{};
bool use_prediction{};
bool disable_cancel_button{};
SwkbdKeyDisableFlags key_disable_flags{};
u32 max_text_length{};
u32 min_text_length{};
bool enable_return_button{};
INSERT_PADDING_BYTES(3);
u32 flags{};
bool is_use_save_data{};
INSERT_PADDING_BYTES(7);
Common::UUID user_id{};
u64 start_sampling_number{};
INSERT_PADDING_WORDS(8);
};
static_assert(sizeof(SwkbdAppearArgNew) == 0x70, "SwkbdAppearArg has incorrect size.");
struct SwkbdCalcArgCommon {
u32 unknown{};
u16 calc_arg_size{};
INSERT_PADDING_BYTES(2);
SwkbdCalcArgFlags flags{};
SwkbdInitializeArg initialize_arg{};
};
static_assert(sizeof(SwkbdCalcArgCommon) == 0x18, "SwkbdCalcArgCommon has incorrect size.");
struct SwkbdCalcArgOld {
f32 volume{};
s32 cursor_position{};
SwkbdAppearArgOld appear_arg{};
std::array<char16_t, 0x1FA> input_text{};
bool utf8_mode{};
INSERT_PADDING_BYTES(1);
bool enable_backspace_button{};
INSERT_PADDING_BYTES(3);
bool key_top_as_floating{};
bool footer_scalable{};
bool alpha_enabled_in_input_mode{};
u8 input_mode_fade_type{};
bool disable_touch{};
bool disable_hardware_keyboard{};
INSERT_PADDING_BYTES(8);
f32 key_top_scale_x{};
f32 key_top_scale_y{};
f32 key_top_translate_x{};
f32 key_top_translate_y{};
f32 key_top_bg_alpha{};
f32 footer_bg_alpha{};
f32 balloon_scale{};
INSERT_PADDING_WORDS(4);
u8 se_group{};
INSERT_PADDING_BYTES(3);
};
static_assert(sizeof(SwkbdCalcArgOld) == 0x4A0 - sizeof(SwkbdCalcArgCommon),
"SwkbdCalcArgOld has incorrect size.");
struct SwkbdCalcArgNew {
SwkbdAppearArgNew appear_arg{};
f32 volume{};
s32 cursor_position{};
std::array<char16_t, 0x1FA> input_text{};
bool utf8_mode{};
INSERT_PADDING_BYTES(1);
bool enable_backspace_button{};
INSERT_PADDING_BYTES(3);
bool key_top_as_floating{};
bool footer_scalable{};
bool alpha_enabled_in_input_mode{};
u8 input_mode_fade_type{};
bool disable_touch{};
bool disable_hardware_keyboard{};
INSERT_PADDING_BYTES(8);
f32 key_top_scale_x{};
f32 key_top_scale_y{};
f32 key_top_translate_x{};
f32 key_top_translate_y{};
f32 key_top_bg_alpha{};
f32 footer_bg_alpha{};
f32 balloon_scale{};
INSERT_PADDING_WORDS(4);
u8 se_group{};
INSERT_PADDING_BYTES(3);
INSERT_PADDING_WORDS(8);
};
static_assert(sizeof(SwkbdCalcArgNew) == 0x4E8 - sizeof(SwkbdCalcArgCommon),
"SwkbdCalcArgNew has incorrect size.");
struct SwkbdChangedStringArg {
u32 text_length{};
s32 dictionary_start_cursor_position{};
s32 dictionary_end_cursor_position{};
s32 cursor_position{};
};
static_assert(sizeof(SwkbdChangedStringArg) == 0x10, "SwkbdChangedStringArg has incorrect size.");
struct SwkbdMovedCursorArg {
u32 text_length{};
s32 cursor_position{};
};
static_assert(sizeof(SwkbdMovedCursorArg) == 0x8, "SwkbdMovedCursorArg has incorrect size.");
struct SwkbdMovedTabArg {
u32 text_length{};
s32 cursor_position{};
};
static_assert(sizeof(SwkbdMovedTabArg) == 0x8, "SwkbdMovedTabArg has incorrect size.");
struct SwkbdDecidedEnterArg {
u32 text_length{};
};
static_assert(sizeof(SwkbdDecidedEnterArg) == 0x4, "SwkbdDecidedEnterArg has incorrect size.");
} // namespace Service::AM::Frontend

View File

@ -0,0 +1,505 @@
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "common/assert.h"
#include "common/fs/file.h"
#include "common/fs/fs.h"
#include "common/fs/path_util.h"
#include "common/logging/log.h"
#include "common/string_util.h"
#include "core/core.h"
#include "core/file_sys/content_archive.h"
#include "core/file_sys/fs_filesystem.h"
#include "core/file_sys/nca_metadata.h"
#include "core/file_sys/patch_manager.h"
#include "core/file_sys/registered_cache.h"
#include "core/file_sys/romfs.h"
#include "core/file_sys/system_archive/system_archive.h"
#include "core/file_sys/vfs/vfs_vector.h"
#include "core/frontend/applets/web_browser.h"
#include "core/hle/result.h"
#include "core/hle/service/am/am.h"
#include "core/hle/service/am/frontend/applet_web_browser.h"
#include "core/hle/service/am/storage.h"
#include "core/hle/service/filesystem/filesystem.h"
#include "core/hle/service/ns/iplatform_service_manager.h"
#include "core/loader/loader.h"
namespace Service::AM::Frontend {
namespace {
template <typename T>
void ParseRawValue(T& value, const std::vector<u8>& data) {
static_assert(std::is_trivially_copyable_v<T>,
"It's undefined behavior to use memcpy with non-trivially copyable objects");
std::memcpy(&value, data.data(), data.size());
}
template <typename T>
T ParseRawValue(const std::vector<u8>& data) {
T value;
ParseRawValue(value, data);
return value;
}
std::string ParseStringValue(const std::vector<u8>& data) {
return Common::StringFromFixedZeroTerminatedBuffer(reinterpret_cast<const char*>(data.data()),
data.size());
}
std::string GetMainURL(const std::string& url) {
const auto index = url.find('?');
if (index == std::string::npos) {
return url;
}
return url.substr(0, index);
}
std::string ResolveURL(const std::string& url) {
const auto index = url.find_first_of('%');
if (index == std::string::npos) {
return url;
}
return url.substr(0, index) + "lp1" + url.substr(index + 1);
}
WebArgInputTLVMap ReadWebArgs(const std::vector<u8>& web_arg, WebArgHeader& web_arg_header) {
std::memcpy(&web_arg_header, web_arg.data(), sizeof(WebArgHeader));
if (web_arg.size() == sizeof(WebArgHeader)) {
return {};
}
WebArgInputTLVMap input_tlv_map;
u64 current_offset = sizeof(WebArgHeader);
for (std::size_t i = 0; i < web_arg_header.total_tlv_entries; ++i) {
if (web_arg.size() < current_offset + sizeof(WebArgInputTLV)) {
return input_tlv_map;
}
WebArgInputTLV input_tlv;
std::memcpy(&input_tlv, web_arg.data() + current_offset, sizeof(WebArgInputTLV));
current_offset += sizeof(WebArgInputTLV);
if (web_arg.size() < current_offset + input_tlv.arg_data_size) {
return input_tlv_map;
}
std::vector<u8> data(input_tlv.arg_data_size);
std::memcpy(data.data(), web_arg.data() + current_offset, input_tlv.arg_data_size);
current_offset += input_tlv.arg_data_size;
input_tlv_map.insert_or_assign(input_tlv.input_tlv_type, std::move(data));
}
return input_tlv_map;
}
FileSys::VirtualFile GetOfflineRomFS(Core::System& system, u64 title_id,
FileSys::ContentRecordType nca_type) {
if (nca_type == FileSys::ContentRecordType::Data) {
const auto nca =
system.GetFileSystemController().GetSystemNANDContents()->GetEntry(title_id, nca_type);
if (nca == nullptr) {
LOG_ERROR(Service_AM,
"NCA of type={} with title_id={:016X} is not found in the System NAND!",
nca_type, title_id);
return FileSys::SystemArchive::SynthesizeSystemArchive(title_id);
}
return nca->GetRomFS();
} else {
const auto nca = system.GetContentProvider().GetEntry(title_id, nca_type);
if (nca == nullptr) {
if (nca_type == FileSys::ContentRecordType::HtmlDocument) {
LOG_WARNING(Service_AM, "Falling back to AppLoader to get the RomFS.");
FileSys::VirtualFile romfs;
system.GetAppLoader().ReadManualRomFS(romfs);
if (romfs != nullptr) {
return romfs;
}
}
LOG_ERROR(Service_AM,
"NCA of type={} with title_id={:016X} is not found in the ContentProvider!",
nca_type, title_id);
return nullptr;
}
const FileSys::PatchManager pm{title_id, system.GetFileSystemController(),
system.GetContentProvider()};
return pm.PatchRomFS(nca.get(), nca->GetRomFS(), nca_type);
}
}
void ExtractSharedFonts(Core::System& system) {
static constexpr std::array<const char*, 7> DECRYPTED_SHARED_FONTS{
"FontStandard.ttf",
"FontChineseSimplified.ttf",
"FontExtendedChineseSimplified.ttf",
"FontChineseTraditional.ttf",
"FontKorean.ttf",
"FontNintendoExtended.ttf",
"FontNintendoExtended2.ttf",
};
const auto fonts_dir = Common::FS::GetYuzuPath(Common::FS::YuzuPath::CacheDir) / "fonts";
for (std::size_t i = 0; i < NS::SHARED_FONTS.size(); ++i) {
const auto font_file_path = fonts_dir / DECRYPTED_SHARED_FONTS[i];
if (Common::FS::Exists(font_file_path)) {
continue;
}
const auto font = NS::SHARED_FONTS[i];
const auto font_title_id = static_cast<u64>(font.first);
const auto nca = system.GetFileSystemController().GetSystemNANDContents()->GetEntry(
font_title_id, FileSys::ContentRecordType::Data);
FileSys::VirtualFile romfs;
if (!nca) {
romfs = FileSys::SystemArchive::SynthesizeSystemArchive(font_title_id);
} else {
romfs = nca->GetRomFS();
}
if (!romfs) {
LOG_ERROR(Service_AM, "SharedFont RomFS with title_id={:016X} cannot be extracted!",
font_title_id);
continue;
}
const auto extracted_romfs = FileSys::ExtractRomFS(romfs);
if (!extracted_romfs) {
LOG_ERROR(Service_AM, "SharedFont RomFS with title_id={:016X} failed to extract!",
font_title_id);
continue;
}
const auto font_file = extracted_romfs->GetFile(font.second);
if (!font_file) {
LOG_ERROR(Service_AM, "SharedFont RomFS with title_id={:016X} has no font file \"{}\"!",
font_title_id, font.second);
continue;
}
std::vector<u32> font_data_u32(font_file->GetSize() / sizeof(u32));
font_file->ReadBytes<u32>(font_data_u32.data(), font_file->GetSize());
std::transform(font_data_u32.begin(), font_data_u32.end(), font_data_u32.begin(),
Common::swap32);
std::vector<u8> decrypted_data(font_file->GetSize() - 8);
NS::DecryptSharedFontToTTF(font_data_u32, decrypted_data);
FileSys::VirtualFile decrypted_font = std::make_shared<FileSys::VectorVfsFile>(
std::move(decrypted_data), DECRYPTED_SHARED_FONTS[i]);
const auto temp_dir = system.GetFilesystem()->CreateDirectory(
Common::FS::PathToUTF8String(fonts_dir), FileSys::OpenMode::ReadWrite);
const auto out_file = temp_dir->CreateFile(DECRYPTED_SHARED_FONTS[i]);
FileSys::VfsRawCopy(decrypted_font, out_file);
}
}
} // namespace
WebBrowser::WebBrowser(Core::System& system_, std::shared_ptr<Applet> applet_,
LibraryAppletMode applet_mode_,
const Core::Frontend::WebBrowserApplet& frontend_)
: FrontendApplet{system_, applet_, applet_mode_}, frontend(frontend_) {}
WebBrowser::~WebBrowser() = default;
void WebBrowser::Initialize() {
FrontendApplet::Initialize();
LOG_INFO(Service_AM, "Initializing Web Browser Applet.");
LOG_DEBUG(Service_AM,
"Initializing Applet with common_args: arg_version={}, lib_version={}, "
"play_startup_sound={}, size={}, system_tick={}, theme_color={}",
common_args.arguments_version, common_args.library_version,
common_args.play_startup_sound, common_args.size, common_args.system_tick,
common_args.theme_color);
web_applet_version = WebAppletVersion{common_args.library_version};
const auto web_arg_storage = PopInData();
ASSERT(web_arg_storage != nullptr);
const auto& web_arg = web_arg_storage->GetData();
ASSERT_OR_EXECUTE(web_arg.size() >= sizeof(WebArgHeader), { return; });
web_arg_input_tlv_map = ReadWebArgs(web_arg, web_arg_header);
LOG_DEBUG(Service_AM, "WebArgHeader: total_tlv_entries={}, shim_kind={}",
web_arg_header.total_tlv_entries, web_arg_header.shim_kind);
ExtractSharedFonts(system);
switch (web_arg_header.shim_kind) {
case ShimKind::Shop:
InitializeShop();
break;
case ShimKind::Login:
InitializeLogin();
break;
case ShimKind::Offline:
InitializeOffline();
break;
case ShimKind::Share:
InitializeShare();
break;
case ShimKind::Web:
InitializeWeb();
break;
case ShimKind::Wifi:
InitializeWifi();
break;
case ShimKind::Lobby:
InitializeLobby();
break;
default:
ASSERT_MSG(false, "Invalid ShimKind={}", web_arg_header.shim_kind);
break;
}
}
Result WebBrowser::GetStatus() const {
return status;
}
void WebBrowser::ExecuteInteractive() {
UNIMPLEMENTED_MSG("WebSession is not implemented");
}
void WebBrowser::Execute() {
switch (web_arg_header.shim_kind) {
case ShimKind::Shop:
ExecuteShop();
break;
case ShimKind::Login:
ExecuteLogin();
break;
case ShimKind::Offline:
ExecuteOffline();
break;
case ShimKind::Share:
ExecuteShare();
break;
case ShimKind::Web:
ExecuteWeb();
break;
case ShimKind::Wifi:
ExecuteWifi();
break;
case ShimKind::Lobby:
ExecuteLobby();
break;
default:
ASSERT_MSG(false, "Invalid ShimKind={}", web_arg_header.shim_kind);
WebBrowserExit(WebExitReason::EndButtonPressed);
break;
}
}
void WebBrowser::ExtractOfflineRomFS() {
LOG_DEBUG(Service_AM, "Extracting RomFS to {}",
Common::FS::PathToUTF8String(offline_cache_dir));
const auto extracted_romfs_dir = FileSys::ExtractRomFS(offline_romfs);
const auto temp_dir = system.GetFilesystem()->CreateDirectory(
Common::FS::PathToUTF8String(offline_cache_dir), FileSys::OpenMode::ReadWrite);
FileSys::VfsRawCopyD(extracted_romfs_dir, temp_dir);
}
void WebBrowser::WebBrowserExit(WebExitReason exit_reason, std::string last_url) {
if ((web_arg_header.shim_kind == ShimKind::Share &&
web_applet_version >= WebAppletVersion::Version196608) ||
(web_arg_header.shim_kind == ShimKind::Web &&
web_applet_version >= WebAppletVersion::Version524288)) {
// TODO: Push Output TLVs instead of a WebCommonReturnValue
}
WebCommonReturnValue web_common_return_value;
web_common_return_value.exit_reason = exit_reason;
std::memcpy(&web_common_return_value.last_url, last_url.data(), last_url.size());
web_common_return_value.last_url_size = last_url.size();
LOG_DEBUG(Service_AM, "WebCommonReturnValue: exit_reason={}, last_url={}, last_url_size={}",
exit_reason, last_url, last_url.size());
complete = true;
std::vector<u8> out_data(sizeof(WebCommonReturnValue));
std::memcpy(out_data.data(), &web_common_return_value, out_data.size());
PushOutData(std::make_shared<IStorage>(system, std::move(out_data)));
Exit();
}
Result WebBrowser::RequestExit() {
frontend.Close();
R_SUCCEED();
}
bool WebBrowser::InputTLVExistsInMap(WebArgInputTLVType input_tlv_type) const {
return web_arg_input_tlv_map.find(input_tlv_type) != web_arg_input_tlv_map.end();
}
std::optional<std::vector<u8>> WebBrowser::GetInputTLVData(WebArgInputTLVType input_tlv_type) {
const auto map_it = web_arg_input_tlv_map.find(input_tlv_type);
if (map_it == web_arg_input_tlv_map.end()) {
return std::nullopt;
}
return map_it->second;
}
void WebBrowser::InitializeShop() {}
void WebBrowser::InitializeLogin() {}
void WebBrowser::InitializeOffline() {
const auto document_path =
ParseStringValue(GetInputTLVData(WebArgInputTLVType::DocumentPath).value());
const auto document_kind =
ParseRawValue<DocumentKind>(GetInputTLVData(WebArgInputTLVType::DocumentKind).value());
std::string additional_paths;
switch (document_kind) {
case DocumentKind::OfflineHtmlPage:
default:
title_id = system.GetApplicationProcessProgramID();
nca_type = FileSys::ContentRecordType::HtmlDocument;
additional_paths = "html-document";
break;
case DocumentKind::ApplicationLegalInformation:
title_id = ParseRawValue<u64>(GetInputTLVData(WebArgInputTLVType::ApplicationID).value());
nca_type = FileSys::ContentRecordType::LegalInformation;
break;
case DocumentKind::SystemDataPage:
title_id = ParseRawValue<u64>(GetInputTLVData(WebArgInputTLVType::SystemDataID).value());
nca_type = FileSys::ContentRecordType::Data;
break;
}
static constexpr std::array<const char*, 3> RESOURCE_TYPES{
"manual",
"legal_information",
"system_data",
};
offline_cache_dir = Common::FS::GetYuzuPath(Common::FS::YuzuPath::CacheDir) /
fmt::format("offline_web_applet_{}/{:016X}",
RESOURCE_TYPES[static_cast<u32>(document_kind) - 1], title_id);
offline_document = Common::FS::ConcatPathSafe(
offline_cache_dir, fmt::format("{}/{}", additional_paths, document_path));
}
void WebBrowser::InitializeShare() {}
void WebBrowser::InitializeWeb() {
external_url = ParseStringValue(GetInputTLVData(WebArgInputTLVType::InitialURL).value());
// Resolve Nintendo CDN URLs.
external_url = ResolveURL(external_url);
}
void WebBrowser::InitializeWifi() {}
void WebBrowser::InitializeLobby() {}
void WebBrowser::ExecuteShop() {
LOG_WARNING(Service_AM, "(STUBBED) called, Shop Applet is not implemented");
WebBrowserExit(WebExitReason::EndButtonPressed);
}
void WebBrowser::ExecuteLogin() {
LOG_WARNING(Service_AM, "(STUBBED) called, Login Applet is not implemented");
WebBrowserExit(WebExitReason::EndButtonPressed);
}
void WebBrowser::ExecuteOffline() {
// TODO (Morph): This is a hack for WebSession foreground web applets such as those used by
// Super Mario 3D All-Stars.
// TODO (Morph): Implement WebSession.
if (applet_mode == LibraryAppletMode::AllForegroundInitiallyHidden) {
LOG_WARNING(Service_AM, "WebSession is not implemented");
return;
}
const auto main_url = GetMainURL(Common::FS::PathToUTF8String(offline_document));
if (!Common::FS::Exists(main_url)) {
offline_romfs = GetOfflineRomFS(system, title_id, nca_type);
if (offline_romfs == nullptr) {
LOG_ERROR(Service_AM,
"RomFS with title_id={:016X} and nca_type={} cannot be extracted!", title_id,
nca_type);
WebBrowserExit(WebExitReason::WindowClosed);
return;
}
}
LOG_INFO(Service_AM, "Opening offline document at {}",
Common::FS::PathToUTF8String(offline_document));
frontend.OpenLocalWebPage(
Common::FS::PathToUTF8String(offline_document), [this] { ExtractOfflineRomFS(); },
[this](WebExitReason exit_reason, std::string last_url) {
WebBrowserExit(exit_reason, last_url);
});
}
void WebBrowser::ExecuteShare() {
LOG_WARNING(Service_AM, "(STUBBED) called, Share Applet is not implemented");
WebBrowserExit(WebExitReason::EndButtonPressed);
}
void WebBrowser::ExecuteWeb() {
LOG_INFO(Service_AM, "Opening external URL at {}", external_url);
frontend.OpenExternalWebPage(external_url,
[this](WebExitReason exit_reason, std::string last_url) {
WebBrowserExit(exit_reason, last_url);
});
}
void WebBrowser::ExecuteWifi() {
LOG_WARNING(Service_AM, "(STUBBED) called, Wifi Applet is not implemented");
WebBrowserExit(WebExitReason::EndButtonPressed);
}
void WebBrowser::ExecuteLobby() {
LOG_WARNING(Service_AM, "(STUBBED) called, Lobby Applet is not implemented");
WebBrowserExit(WebExitReason::EndButtonPressed);
}
} // namespace Service::AM::Frontend

View File

@ -0,0 +1,84 @@
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <filesystem>
#include <optional>
#include "common/common_types.h"
#include "core/file_sys/vfs/vfs_types.h"
#include "core/hle/result.h"
#include "core/hle/service/am/frontend/applet_web_browser_types.h"
#include "core/hle/service/am/frontend/applets.h"
namespace Core {
class System;
}
namespace FileSys {
enum class ContentRecordType : u8;
}
namespace Service::AM::Frontend {
class WebBrowser final : public FrontendApplet {
public:
WebBrowser(Core::System& system_, std::shared_ptr<Applet> applet_,
LibraryAppletMode applet_mode_, const Core::Frontend::WebBrowserApplet& frontend_);
~WebBrowser() override;
void Initialize() override;
Result GetStatus() const override;
void ExecuteInteractive() override;
void Execute() override;
Result RequestExit() override;
void ExtractOfflineRomFS();
void WebBrowserExit(WebExitReason exit_reason, std::string last_url = "");
private:
bool InputTLVExistsInMap(WebArgInputTLVType input_tlv_type) const;
std::optional<std::vector<u8>> GetInputTLVData(WebArgInputTLVType input_tlv_type);
// Initializers for the various types of browser applets
void InitializeShop();
void InitializeLogin();
void InitializeOffline();
void InitializeShare();
void InitializeWeb();
void InitializeWifi();
void InitializeLobby();
// Executors for the various types of browser applets
void ExecuteShop();
void ExecuteLogin();
void ExecuteOffline();
void ExecuteShare();
void ExecuteWeb();
void ExecuteWifi();
void ExecuteLobby();
const Core::Frontend::WebBrowserApplet& frontend;
bool complete{false};
Result status{ResultSuccess};
WebAppletVersion web_applet_version{};
WebArgHeader web_arg_header{};
WebArgInputTLVMap web_arg_input_tlv_map;
u64 title_id{};
FileSys::ContentRecordType nca_type{};
std::filesystem::path offline_cache_dir;
std::filesystem::path offline_document;
FileSys::VirtualFile offline_romfs;
std::string external_url;
};
} // namespace Service::AM::Frontend

View File

@ -0,0 +1,177 @@
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <array>
#include <unordered_map>
#include <vector>
#include "common/common_funcs.h"
#include "common/common_types.h"
#include "common/swap.h"
namespace Service::AM::Frontend {
enum class WebAppletVersion : u32_le {
Version0 = 0x0, // Only used by WifiWebAuthApplet
Version131072 = 0x20000, // 1.0.0 - 2.3.0
Version196608 = 0x30000, // 3.0.0 - 4.1.0
Version327680 = 0x50000, // 5.0.0 - 5.1.0
Version393216 = 0x60000, // 6.0.0 - 7.0.1
Version524288 = 0x80000, // 8.0.0+
};
enum class ShimKind : u32 {
Shop = 1,
Login = 2,
Offline = 3,
Share = 4,
Web = 5,
Wifi = 6,
Lobby = 7,
};
enum class WebExitReason : u32 {
EndButtonPressed = 0,
BackButtonPressed = 1,
ExitRequested = 2,
CallbackURL = 3,
WindowClosed = 4,
ErrorDialog = 7,
};
enum class WebArgInputTLVType : u16 {
InitialURL = 0x1,
CallbackURL = 0x3,
CallbackableURL = 0x4,
ApplicationID = 0x5,
DocumentPath = 0x6,
DocumentKind = 0x7,
SystemDataID = 0x8,
ShareStartPage = 0x9,
Whitelist = 0xA,
News = 0xB,
UserID = 0xE,
AlbumEntry0 = 0xF,
ScreenShotEnabled = 0x10,
EcClientCertEnabled = 0x11,
PlayReportEnabled = 0x13,
BootDisplayKind = 0x17,
BackgroundKind = 0x18,
FooterEnabled = 0x19,
PointerEnabled = 0x1A,
LeftStickMode = 0x1B,
KeyRepeatFrame1 = 0x1C,
KeyRepeatFrame2 = 0x1D,
BootAsMediaPlayerInverted = 0x1E,
DisplayURLKind = 0x1F,
BootAsMediaPlayer = 0x21,
ShopJumpEnabled = 0x22,
MediaAutoPlayEnabled = 0x23,
LobbyParameter = 0x24,
ApplicationAlbumEntry = 0x26,
JsExtensionEnabled = 0x27,
AdditionalCommentText = 0x28,
TouchEnabledOnContents = 0x29,
UserAgentAdditionalString = 0x2A,
AdditionalMediaData0 = 0x2B,
MediaPlayerAutoCloseEnabled = 0x2C,
PageCacheEnabled = 0x2D,
WebAudioEnabled = 0x2E,
YouTubeVideoWhitelist = 0x31,
FooterFixedKind = 0x32,
PageFadeEnabled = 0x33,
MediaCreatorApplicationRatingAge = 0x34,
BootLoadingIconEnabled = 0x35,
PageScrollIndicatorEnabled = 0x36,
MediaPlayerSpeedControlEnabled = 0x37,
AlbumEntry1 = 0x38,
AlbumEntry2 = 0x39,
AlbumEntry3 = 0x3A,
AdditionalMediaData1 = 0x3B,
AdditionalMediaData2 = 0x3C,
AdditionalMediaData3 = 0x3D,
BootFooterButton = 0x3E,
OverrideWebAudioVolume = 0x3F,
OverrideMediaAudioVolume = 0x40,
BootMode = 0x41,
WebSessionEnabled = 0x42,
MediaPlayerOfflineEnabled = 0x43,
};
enum class WebArgOutputTLVType : u16 {
ShareExitReason = 0x1,
LastURL = 0x2,
LastURLSize = 0x3,
SharePostResult = 0x4,
PostServiceName = 0x5,
PostServiceNameSize = 0x6,
PostID = 0x7,
PostIDSize = 0x8,
MediaPlayerAutoClosedByCompletion = 0x9,
};
enum class DocumentKind : u32 {
OfflineHtmlPage = 1,
ApplicationLegalInformation = 2,
SystemDataPage = 3,
};
enum class ShareStartPage : u32 {
Default,
Settings,
};
enum class BootDisplayKind : u32 {
Default,
White,
Black,
};
enum class BackgroundKind : u32 {
Default,
};
enum class LeftStickMode : u32 {
Pointer,
Cursor,
};
enum class WebSessionBootMode : u32 {
AllForeground,
AllForegroundInitiallyHidden,
};
struct WebArgHeader {
u16 total_tlv_entries{};
INSERT_PADDING_BYTES(2);
ShimKind shim_kind{};
};
static_assert(sizeof(WebArgHeader) == 0x8, "WebArgHeader has incorrect size.");
struct WebArgInputTLV {
WebArgInputTLVType input_tlv_type{};
u16 arg_data_size{};
INSERT_PADDING_WORDS(1);
};
static_assert(sizeof(WebArgInputTLV) == 0x8, "WebArgInputTLV has incorrect size.");
struct WebArgOutputTLV {
WebArgOutputTLVType output_tlv_type{};
u16 arg_data_size{};
INSERT_PADDING_WORDS(1);
};
static_assert(sizeof(WebArgOutputTLV) == 0x8, "WebArgOutputTLV has incorrect size.");
struct WebCommonReturnValue {
WebExitReason exit_reason{};
INSERT_PADDING_WORDS(1);
std::array<char, 0x1000> last_url{};
u64 last_url_size{};
};
static_assert(sizeof(WebCommonReturnValue) == 0x1010, "WebCommonReturnValue has incorrect size.");
using WebArgInputTLVMap = std::unordered_map<WebArgInputTLVType, std::vector<u8>>;
} // namespace Service::AM::Frontend

View File

@ -0,0 +1,240 @@
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <cstring>
#include "common/assert.h"
#include "core/core.h"
#include "core/frontend/applets/cabinet.h"
#include "core/frontend/applets/controller.h"
#include "core/frontend/applets/error.h"
#include "core/frontend/applets/general.h"
#include "core/frontend/applets/mii_edit.h"
#include "core/frontend/applets/profile_select.h"
#include "core/frontend/applets/software_keyboard.h"
#include "core/frontend/applets/web_browser.h"
#include "core/hle/kernel/k_event.h"
#include "core/hle/service/am/am.h"
#include "core/hle/service/am/applet_ae.h"
#include "core/hle/service/am/applet_data_broker.h"
#include "core/hle/service/am/applet_manager.h"
#include "core/hle/service/am/applet_message_queue.h"
#include "core/hle/service/am/applet_oe.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_error.h"
#include "core/hle/service/am/frontend/applet_general.h"
#include "core/hle/service/am/frontend/applet_mii_edit.h"
#include "core/hle/service/am/frontend/applet_profile_select.h"
#include "core/hle/service/am/frontend/applet_software_keyboard.h"
#include "core/hle/service/am/frontend/applet_web_browser.h"
#include "core/hle/service/am/frontend/applets.h"
#include "core/hle/service/am/storage.h"
#include "core/hle/service/sm/sm.h"
namespace Service::AM::Frontend {
FrontendApplet::FrontendApplet(Core::System& system_, std::shared_ptr<Applet> applet_,
LibraryAppletMode applet_mode_)
: system{system_}, applet{std::move(applet_)}, applet_mode{applet_mode_} {}
FrontendApplet::~FrontendApplet() = default;
void FrontendApplet::Initialize() {
std::shared_ptr<IStorage> common = PopInData();
ASSERT(common != nullptr);
const auto common_data = common->GetData();
ASSERT(common_data.size() >= sizeof(CommonArguments));
std::memcpy(&common_args, common_data.data(), sizeof(CommonArguments));
initialized = true;
}
std::shared_ptr<IStorage> FrontendApplet::PopInData() {
std::shared_ptr<IStorage> ret;
applet.lock()->caller_applet_broker->GetInData().Pop(&ret);
return ret;
}
std::shared_ptr<IStorage> FrontendApplet::PopInteractiveInData() {
std::shared_ptr<IStorage> ret;
applet.lock()->caller_applet_broker->GetInteractiveInData().Pop(&ret);
return ret;
}
void FrontendApplet::PushOutData(std::shared_ptr<IStorage> storage) {
applet.lock()->caller_applet_broker->GetOutData().Push(storage);
}
void FrontendApplet::PushInteractiveOutData(std::shared_ptr<IStorage> storage) {
applet.lock()->caller_applet_broker->GetInteractiveOutData().Push(storage);
}
void FrontendApplet::Exit() {
applet.lock()->caller_applet_broker->SignalCompletion();
}
FrontendAppletSet::FrontendAppletSet() = default;
FrontendAppletSet::FrontendAppletSet(CabinetApplet cabinet_applet,
ControllerApplet controller_applet, ErrorApplet error_applet,
MiiEdit mii_edit_,
ParentalControlsApplet parental_controls_applet,
PhotoViewer photo_viewer_, ProfileSelect profile_select_,
SoftwareKeyboard software_keyboard_, WebBrowser web_browser_)
: cabinet{std::move(cabinet_applet)}, controller{std::move(controller_applet)},
error{std::move(error_applet)}, mii_edit{std::move(mii_edit_)},
parental_controls{std::move(parental_controls_applet)},
photo_viewer{std::move(photo_viewer_)}, profile_select{std::move(profile_select_)},
software_keyboard{std::move(software_keyboard_)}, web_browser{std::move(web_browser_)} {}
FrontendAppletSet::~FrontendAppletSet() = default;
FrontendAppletSet::FrontendAppletSet(FrontendAppletSet&&) noexcept = default;
FrontendAppletSet& FrontendAppletSet::operator=(FrontendAppletSet&&) noexcept = default;
FrontendAppletHolder::FrontendAppletHolder(Core::System& system_) : system{system_} {}
FrontendAppletHolder::~FrontendAppletHolder() = default;
const FrontendAppletSet& FrontendAppletHolder::GetFrontendAppletSet() const {
return frontend;
}
NFP::CabinetMode FrontendAppletHolder::GetCabinetMode() const {
return cabinet_mode;
}
AppletId FrontendAppletHolder::GetCurrentAppletId() const {
return current_applet_id;
}
void FrontendAppletHolder::SetFrontendAppletSet(FrontendAppletSet set) {
if (set.cabinet != nullptr) {
frontend.cabinet = std::move(set.cabinet);
}
if (set.controller != nullptr) {
frontend.controller = std::move(set.controller);
}
if (set.error != nullptr) {
frontend.error = std::move(set.error);
}
if (set.mii_edit != nullptr) {
frontend.mii_edit = std::move(set.mii_edit);
}
if (set.parental_controls != nullptr) {
frontend.parental_controls = std::move(set.parental_controls);
}
if (set.photo_viewer != nullptr) {
frontend.photo_viewer = std::move(set.photo_viewer);
}
if (set.profile_select != nullptr) {
frontend.profile_select = std::move(set.profile_select);
}
if (set.software_keyboard != nullptr) {
frontend.software_keyboard = std::move(set.software_keyboard);
}
if (set.web_browser != nullptr) {
frontend.web_browser = std::move(set.web_browser);
}
}
void FrontendAppletHolder::SetCabinetMode(NFP::CabinetMode mode) {
cabinet_mode = mode;
}
void FrontendAppletHolder::SetCurrentAppletId(AppletId applet_id) {
current_applet_id = applet_id;
}
void FrontendAppletHolder::SetDefaultAppletsIfMissing() {
if (frontend.cabinet == nullptr) {
frontend.cabinet = std::make_unique<Core::Frontend::DefaultCabinetApplet>();
}
if (frontend.controller == nullptr) {
frontend.controller =
std::make_unique<Core::Frontend::DefaultControllerApplet>(system.HIDCore());
}
if (frontend.error == nullptr) {
frontend.error = std::make_unique<Core::Frontend::DefaultErrorApplet>();
}
if (frontend.mii_edit == nullptr) {
frontend.mii_edit = std::make_unique<Core::Frontend::DefaultMiiEditApplet>();
}
if (frontend.parental_controls == nullptr) {
frontend.parental_controls =
std::make_unique<Core::Frontend::DefaultParentalControlsApplet>();
}
if (frontend.photo_viewer == nullptr) {
frontend.photo_viewer = std::make_unique<Core::Frontend::DefaultPhotoViewerApplet>();
}
if (frontend.profile_select == nullptr) {
frontend.profile_select = std::make_unique<Core::Frontend::DefaultProfileSelectApplet>();
}
if (frontend.software_keyboard == nullptr) {
frontend.software_keyboard =
std::make_unique<Core::Frontend::DefaultSoftwareKeyboardApplet>();
}
if (frontend.web_browser == nullptr) {
frontend.web_browser = std::make_unique<Core::Frontend::DefaultWebBrowserApplet>();
}
}
void FrontendAppletHolder::ClearAll() {
frontend = {};
}
std::shared_ptr<FrontendApplet> FrontendAppletHolder::GetApplet(std::shared_ptr<Applet> applet,
AppletId id,
LibraryAppletMode mode) const {
switch (id) {
case AppletId::Auth:
return std::make_shared<Auth>(system, applet, mode, *frontend.parental_controls);
case AppletId::Cabinet:
return std::make_shared<Cabinet>(system, applet, mode, *frontend.cabinet);
case AppletId::Controller:
return std::make_shared<Controller>(system, applet, mode, *frontend.controller);
case AppletId::Error:
return std::make_shared<Error>(system, applet, mode, *frontend.error);
case AppletId::ProfileSelect:
return std::make_shared<ProfileSelect>(system, applet, mode, *frontend.profile_select);
case AppletId::SoftwareKeyboard:
return std::make_shared<SoftwareKeyboard>(system, applet, mode,
*frontend.software_keyboard);
case AppletId::MiiEdit:
return std::make_shared<MiiEdit>(system, applet, mode, *frontend.mii_edit);
case AppletId::Web:
case AppletId::Shop:
case AppletId::OfflineWeb:
case AppletId::LoginShare:
case AppletId::WebAuth:
return std::make_shared<WebBrowser>(system, applet, mode, *frontend.web_browser);
case AppletId::PhotoViewer:
return std::make_shared<PhotoViewer>(system, applet, mode, *frontend.photo_viewer);
default:
UNIMPLEMENTED_MSG(
"No backend implementation exists for applet_id={:02X}! Falling back to stub applet.",
static_cast<u8>(id));
return std::make_shared<StubApplet>(system, applet, id, mode);
}
}
} // namespace Service::AM::Frontend

View File

@ -0,0 +1,146 @@
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <memory>
#include <queue>
#include "common/swap.h"
#include "core/hle/service/am/applet.h"
union Result;
namespace Core {
class System;
}
namespace Core::Frontend {
class CabinetApplet;
class ControllerApplet;
class ECommerceApplet;
class ErrorApplet;
class MiiEditApplet;
class ParentalControlsApplet;
class PhotoViewerApplet;
class ProfileSelectApplet;
class SoftwareKeyboardApplet;
class WebBrowserApplet;
} // namespace Core::Frontend
namespace Kernel {
class KernelCore;
class KEvent;
class KReadableEvent;
} // namespace Kernel
namespace Service::NFP {
enum class CabinetMode : u8;
} // namespace Service::NFP
namespace Service::AM {
class IStorage;
namespace Frontend {
class FrontendApplet {
public:
explicit FrontendApplet(Core::System& system_, std::shared_ptr<Applet> applet_,
LibraryAppletMode applet_mode_);
virtual ~FrontendApplet();
virtual void Initialize();
virtual Result GetStatus() const = 0;
virtual void ExecuteInteractive() = 0;
virtual void Execute() = 0;
virtual Result RequestExit() = 0;
LibraryAppletMode GetLibraryAppletMode() const {
return applet_mode;
}
bool IsInitialized() const {
return initialized;
}
protected:
std::shared_ptr<IStorage> PopInData();
std::shared_ptr<IStorage> PopInteractiveInData();
void PushOutData(std::shared_ptr<IStorage> storage);
void PushInteractiveOutData(std::shared_ptr<IStorage> storage);
void Exit();
protected:
Core::System& system;
CommonArguments common_args{};
std::weak_ptr<Applet> applet{};
LibraryAppletMode applet_mode{};
bool initialized{false};
};
struct FrontendAppletSet {
using CabinetApplet = std::unique_ptr<Core::Frontend::CabinetApplet>;
using ControllerApplet = std::unique_ptr<Core::Frontend::ControllerApplet>;
using ErrorApplet = std::unique_ptr<Core::Frontend::ErrorApplet>;
using MiiEdit = std::unique_ptr<Core::Frontend::MiiEditApplet>;
using ParentalControlsApplet = std::unique_ptr<Core::Frontend::ParentalControlsApplet>;
using PhotoViewer = std::unique_ptr<Core::Frontend::PhotoViewerApplet>;
using ProfileSelect = std::unique_ptr<Core::Frontend::ProfileSelectApplet>;
using SoftwareKeyboard = std::unique_ptr<Core::Frontend::SoftwareKeyboardApplet>;
using WebBrowser = std::unique_ptr<Core::Frontend::WebBrowserApplet>;
FrontendAppletSet();
FrontendAppletSet(CabinetApplet cabinet_applet, ControllerApplet controller_applet,
ErrorApplet error_applet, MiiEdit mii_edit_,
ParentalControlsApplet parental_controls_applet, PhotoViewer photo_viewer_,
ProfileSelect profile_select_, SoftwareKeyboard software_keyboard_,
WebBrowser web_browser_);
~FrontendAppletSet();
FrontendAppletSet(const FrontendAppletSet&) = delete;
FrontendAppletSet& operator=(const FrontendAppletSet&) = delete;
FrontendAppletSet(FrontendAppletSet&&) noexcept;
FrontendAppletSet& operator=(FrontendAppletSet&&) noexcept;
CabinetApplet cabinet;
ControllerApplet controller;
ErrorApplet error;
MiiEdit mii_edit;
ParentalControlsApplet parental_controls;
PhotoViewer photo_viewer;
ProfileSelect profile_select;
SoftwareKeyboard software_keyboard;
WebBrowser web_browser;
};
class FrontendAppletHolder {
public:
explicit FrontendAppletHolder(Core::System& system_);
~FrontendAppletHolder();
const FrontendAppletSet& GetFrontendAppletSet() const;
NFP::CabinetMode GetCabinetMode() const;
AppletId GetCurrentAppletId() const;
void SetFrontendAppletSet(FrontendAppletSet set);
void SetCabinetMode(NFP::CabinetMode mode);
void SetCurrentAppletId(AppletId applet_id);
void SetDefaultAppletsIfMissing();
void ClearAll();
std::shared_ptr<FrontendApplet> GetApplet(std::shared_ptr<Applet> applet, AppletId id,
LibraryAppletMode mode) const;
private:
AppletId current_applet_id{};
NFP::CabinetMode cabinet_mode{};
FrontendAppletSet frontend;
Core::System& system;
};
} // namespace Frontend
} // namespace Service::AM

View File

@ -0,0 +1,34 @@
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "core/hle/service/am/global_state_controller.h"
#include "core/hle/service/ipc_helpers.h"
namespace Service::AM {
IGlobalStateController::IGlobalStateController(Core::System& system_)
: ServiceFramework{system_, "IGlobalStateController"} {
// clang-format off
static const FunctionInfo functions[] = {
{0, nullptr, "RequestToEnterSleep"},
{1, nullptr, "EnterSleep"},
{2, nullptr, "StartSleepSequence"},
{3, nullptr, "StartShutdownSequence"},
{4, nullptr, "StartRebootSequence"},
{9, nullptr, "IsAutoPowerDownRequested"},
{10, nullptr, "LoadAndApplyIdlePolicySettings"},
{11, nullptr, "NotifyCecSettingsChanged"},
{12, nullptr, "SetDefaultHomeButtonLongPressTime"},
{13, nullptr, "UpdateDefaultDisplayResolution"},
{14, nullptr, "ShouldSleepOnBoot"},
{15, nullptr, "GetHdcpAuthenticationFailedEvent"},
{30, nullptr, "OpenCradleFirmwareUpdater"},
};
// clang-format on
RegisterHandlers(functions);
}
IGlobalStateController::~IGlobalStateController() = default;
} // namespace Service::AM

View File

@ -0,0 +1,16 @@
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "core/hle/service/service.h"
namespace Service::AM {
class IGlobalStateController final : public ServiceFramework<IGlobalStateController> {
public:
explicit IGlobalStateController(Core::System& system_);
~IGlobalStateController() override;
};
} // namespace Service::AM

View File

@ -0,0 +1,35 @@
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "core/core.h"
#include "core/hle/service/am/hid_registration.h"
#include "core/hle/service/am/process.h"
#include "core/hle/service/hid/hid_server.h"
#include "core/hle/service/sm/sm.h"
#include "hid_core/resource_manager.h"
namespace Service::AM {
HidRegistration::HidRegistration(Core::System& system, Process& process) : m_process(process) {
m_hid_server = system.ServiceManager().GetService<HID::IHidServer>("hid");
if (m_process.IsInitialized()) {
m_hid_server->GetResourceManager()->RegisterAppletResourceUserId(m_process.GetProcessId(),
true);
}
}
HidRegistration::~HidRegistration() {
if (m_process.IsInitialized()) {
m_hid_server->GetResourceManager()->UnregisterAppletResourceUserId(
m_process.GetProcessId());
}
}
void HidRegistration::EnableAppletToGetInput(bool enable) {
if (m_process.IsInitialized()) {
m_hid_server->GetResourceManager()->EnableInput(m_process.GetProcessId(), enable);
}
}
} // namespace Service::AM

View File

@ -0,0 +1,32 @@
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <memory>
namespace Core {
class System;
}
namespace Service::HID {
class IHidServer;
}
namespace Service::AM {
class Process;
class HidRegistration {
public:
explicit HidRegistration(Core::System& system, Process& process);
~HidRegistration();
void EnableAppletToGetInput(bool enable);
private:
Process& m_process;
std::shared_ptr<Service::HID::IHidServer> m_hid_server;
};
} // namespace Service::AM

View File

@ -0,0 +1,57 @@
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "core/hle/service/am/home_menu_functions.h"
#include "core/hle/service/ipc_helpers.h"
namespace Service::AM {
IHomeMenuFunctions::IHomeMenuFunctions(Core::System& system_)
: ServiceFramework{system_, "IHomeMenuFunctions"}, service_context{system,
"IHomeMenuFunctions"} {
// clang-format off
static const FunctionInfo functions[] = {
{10, &IHomeMenuFunctions::RequestToGetForeground, "RequestToGetForeground"},
{11, nullptr, "LockForeground"},
{12, nullptr, "UnlockForeground"},
{20, nullptr, "PopFromGeneralChannel"},
{21, &IHomeMenuFunctions::GetPopFromGeneralChannelEvent, "GetPopFromGeneralChannelEvent"},
{30, nullptr, "GetHomeButtonWriterLockAccessor"},
{31, nullptr, "GetWriterLockAccessorEx"},
{40, nullptr, "IsSleepEnabled"},
{41, nullptr, "IsRebootEnabled"},
{50, nullptr, "LaunchSystemApplet"},
{51, nullptr, "LaunchStarter"},
{100, nullptr, "PopRequestLaunchApplicationForDebug"},
{110, nullptr, "IsForceTerminateApplicationDisabledForDebug"},
{200, nullptr, "LaunchDevMenu"},
{1000, nullptr, "SetLastApplicationExitReason"},
};
// clang-format on
RegisterHandlers(functions);
pop_from_general_channel_event =
service_context.CreateEvent("IHomeMenuFunctions:PopFromGeneralChannelEvent");
}
IHomeMenuFunctions::~IHomeMenuFunctions() {
service_context.CloseEvent(pop_from_general_channel_event);
}
void IHomeMenuFunctions::RequestToGetForeground(HLERequestContext& ctx) {
LOG_WARNING(Service_AM, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
void IHomeMenuFunctions::GetPopFromGeneralChannelEvent(HLERequestContext& ctx) {
LOG_WARNING(Service_AM, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 2, 1};
rb.Push(ResultSuccess);
rb.PushCopyObjects(pop_from_general_channel_event->GetReadableEvent());
}
} // namespace Service::AM

View File

@ -0,0 +1,25 @@
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "core/hle/service/kernel_helpers.h"
#include "core/hle/service/service.h"
namespace Service::AM {
class IHomeMenuFunctions final : public ServiceFramework<IHomeMenuFunctions> {
public:
explicit IHomeMenuFunctions(Core::System& system_);
~IHomeMenuFunctions() override;
private:
void RequestToGetForeground(HLERequestContext& ctx);
void GetPopFromGeneralChannelEvent(HLERequestContext& ctx);
KernelHelpers::ServiceContext service_context;
Kernel::KEvent* pop_from_general_channel_event;
};
} // namespace Service::AM

View File

@ -0,0 +1,202 @@
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "common/scope_exit.h"
#include "core/hle/service/am/am_results.h"
#include "core/hle/service/am/applet_data_broker.h"
#include "core/hle/service/am/frontend/applets.h"
#include "core/hle/service/am/library_applet_accessor.h"
#include "core/hle/service/am/storage.h"
#include "core/hle/service/ipc_helpers.h"
namespace Service::AM {
ILibraryAppletAccessor::ILibraryAppletAccessor(Core::System& system_,
std::shared_ptr<AppletDataBroker> broker_,
std::shared_ptr<Applet> applet_)
: ServiceFramework{system_, "ILibraryAppletAccessor"}, broker{std::move(broker_)},
applet{std::move(applet_)} {
// clang-format off
static const FunctionInfo functions[] = {
{0, &ILibraryAppletAccessor::GetAppletStateChangedEvent, "GetAppletStateChangedEvent"},
{1, &ILibraryAppletAccessor::IsCompleted, "IsCompleted"},
{10, &ILibraryAppletAccessor::Start, "Start"},
{20, &ILibraryAppletAccessor::RequestExit, "RequestExit"},
{25, nullptr, "Terminate"},
{30, &ILibraryAppletAccessor::GetResult, "GetResult"},
{50, nullptr, "SetOutOfFocusApplicationSuspendingEnabled"},
{60, &ILibraryAppletAccessor::PresetLibraryAppletGpuTimeSliceZero, "PresetLibraryAppletGpuTimeSliceZero"},
{100, &ILibraryAppletAccessor::PushInData, "PushInData"},
{101, &ILibraryAppletAccessor::PopOutData, "PopOutData"},
{102, nullptr, "PushExtraStorage"},
{103, &ILibraryAppletAccessor::PushInteractiveInData, "PushInteractiveInData"},
{104, &ILibraryAppletAccessor::PopInteractiveOutData, "PopInteractiveOutData"},
{105, &ILibraryAppletAccessor::GetPopOutDataEvent, "GetPopOutDataEvent"},
{106, &ILibraryAppletAccessor::GetPopInteractiveOutDataEvent, "GetPopInteractiveOutDataEvent"},
{110, nullptr, "NeedsToExitProcess"},
{120, nullptr, "GetLibraryAppletInfo"},
{150, nullptr, "RequestForAppletToGetForeground"},
{160, &ILibraryAppletAccessor::GetIndirectLayerConsumerHandle, "GetIndirectLayerConsumerHandle"},
};
// clang-format on
RegisterHandlers(functions);
}
ILibraryAppletAccessor::~ILibraryAppletAccessor() = default;
void ILibraryAppletAccessor::GetAppletStateChangedEvent(HLERequestContext& ctx) {
LOG_DEBUG(Service_AM, "called");
IPC::ResponseBuilder rb{ctx, 2, 1};
rb.Push(ResultSuccess);
rb.PushCopyObjects(broker->GetStateChangedEvent().GetHandle());
}
void ILibraryAppletAccessor::IsCompleted(HLERequestContext& ctx) {
LOG_DEBUG(Service_AM, "called");
std::scoped_lock lk{applet->lock};
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
rb.Push<u32>(broker->IsCompleted());
}
void ILibraryAppletAccessor::GetResult(HLERequestContext& ctx) {
LOG_DEBUG(Service_AM, "called");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(applet->terminate_result);
}
void ILibraryAppletAccessor::PresetLibraryAppletGpuTimeSliceZero(HLERequestContext& ctx) {
LOG_WARNING(Service_AM, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
void ILibraryAppletAccessor::Start(HLERequestContext& ctx) {
LOG_DEBUG(Service_AM, "called");
applet->process->Run();
FrontendExecute();
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
void ILibraryAppletAccessor::RequestExit(HLERequestContext& ctx) {
LOG_DEBUG(Service_AM, "called");
ASSERT(applet != nullptr);
applet->message_queue.RequestExit();
FrontendRequestExit();
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
void ILibraryAppletAccessor::PushInData(HLERequestContext& ctx) {
LOG_DEBUG(Service_AM, "called");
IPC::RequestParser rp{ctx};
broker->GetInData().Push(rp.PopIpcInterface<IStorage>().lock());
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
void ILibraryAppletAccessor::PopOutData(HLERequestContext& ctx) {
LOG_DEBUG(Service_AM, "called");
std::shared_ptr<IStorage> data;
const auto res = broker->GetOutData().Pop(&data);
if (res.IsSuccess()) {
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(res);
rb.PushIpcInterface(std::move(data));
} else {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(res);
}
}
void ILibraryAppletAccessor::PushInteractiveInData(HLERequestContext& ctx) {
LOG_DEBUG(Service_AM, "called");
IPC::RequestParser rp{ctx};
broker->GetInteractiveInData().Push(rp.PopIpcInterface<IStorage>().lock());
FrontendExecuteInteractive();
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
void ILibraryAppletAccessor::PopInteractiveOutData(HLERequestContext& ctx) {
LOG_DEBUG(Service_AM, "called");
std::shared_ptr<IStorage> data;
const auto res = broker->GetInteractiveOutData().Pop(&data);
if (res.IsSuccess()) {
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(res);
rb.PushIpcInterface(std::move(data));
} else {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(res);
}
}
void ILibraryAppletAccessor::GetPopOutDataEvent(HLERequestContext& ctx) {
LOG_DEBUG(Service_AM, "called");
IPC::ResponseBuilder rb{ctx, 2, 1};
rb.Push(ResultSuccess);
rb.PushCopyObjects(broker->GetOutData().GetEvent());
}
void ILibraryAppletAccessor::GetPopInteractiveOutDataEvent(HLERequestContext& ctx) {
LOG_DEBUG(Service_AM, "called");
IPC::ResponseBuilder rb{ctx, 2, 1};
rb.Push(ResultSuccess);
rb.PushCopyObjects(broker->GetInteractiveOutData().GetEvent());
}
void ILibraryAppletAccessor::GetIndirectLayerConsumerHandle(HLERequestContext& ctx) {
LOG_WARNING(Service_AM, "(STUBBED) called");
// We require a non-zero handle to be valid. Using 0xdeadbeef allows us to trace if this is
// actually used anywhere
constexpr u64 handle = 0xdeadbeef;
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(ResultSuccess);
rb.Push(handle);
}
void ILibraryAppletAccessor::FrontendExecute() {
if (applet->frontend) {
applet->frontend->Initialize();
applet->frontend->Execute();
}
}
void ILibraryAppletAccessor::FrontendExecuteInteractive() {
if (applet->frontend) {
applet->frontend->ExecuteInteractive();
applet->frontend->Execute();
}
}
void ILibraryAppletAccessor::FrontendRequestExit() {
if (applet->frontend) {
applet->frontend->RequestExit();
}
}
} // namespace Service::AM

View File

@ -0,0 +1,43 @@
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "core/hle/service/service.h"
namespace Service::AM {
class AppletDataBroker;
struct Applet;
class ILibraryAppletAccessor final : public ServiceFramework<ILibraryAppletAccessor> {
public:
explicit ILibraryAppletAccessor(Core::System& system_,
std::shared_ptr<AppletDataBroker> broker_,
std::shared_ptr<Applet> applet_);
~ILibraryAppletAccessor();
protected:
void GetAppletStateChangedEvent(HLERequestContext& ctx);
void IsCompleted(HLERequestContext& ctx);
void GetResult(HLERequestContext& ctx);
void PresetLibraryAppletGpuTimeSliceZero(HLERequestContext& ctx);
void Start(HLERequestContext& ctx);
void RequestExit(HLERequestContext& ctx);
void PushInData(HLERequestContext& ctx);
void PopOutData(HLERequestContext& ctx);
void PushInteractiveInData(HLERequestContext& ctx);
void PopInteractiveOutData(HLERequestContext& ctx);
void GetPopOutDataEvent(HLERequestContext& ctx);
void GetPopInteractiveOutDataEvent(HLERequestContext& ctx);
void GetIndirectLayerConsumerHandle(HLERequestContext& ctx);
void FrontendExecute();
void FrontendExecuteInteractive();
void FrontendRequestExit();
const std::shared_ptr<AppletDataBroker> broker;
const std::shared_ptr<Applet> applet;
};
} // namespace Service::AM

View File

@ -0,0 +1,274 @@
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "core/hle/kernel/k_transfer_memory.h"
#include "core/hle/service/am/applet_data_broker.h"
#include "core/hle/service/am/applet_manager.h"
#include "core/hle/service/am/frontend/applets.h"
#include "core/hle/service/am/library_applet_accessor.h"
#include "core/hle/service/am/library_applet_creator.h"
#include "core/hle/service/am/library_applet_storage.h"
#include "core/hle/service/am/storage.h"
#include "core/hle/service/ipc_helpers.h"
#include "core/hle/service/sm/sm.h"
namespace Service::AM {
namespace {
AppletProgramId AppletIdToProgramId(AppletId applet_id) {
switch (applet_id) {
case AppletId::OverlayDisplay:
return AppletProgramId::OverlayDisplay;
case AppletId::QLaunch:
return AppletProgramId::QLaunch;
case AppletId::Starter:
return AppletProgramId::Starter;
case AppletId::Auth:
return AppletProgramId::Auth;
case AppletId::Cabinet:
return AppletProgramId::Cabinet;
case AppletId::Controller:
return AppletProgramId::Controller;
case AppletId::DataErase:
return AppletProgramId::DataErase;
case AppletId::Error:
return AppletProgramId::Error;
case AppletId::NetConnect:
return AppletProgramId::NetConnect;
case AppletId::ProfileSelect:
return AppletProgramId::ProfileSelect;
case AppletId::SoftwareKeyboard:
return AppletProgramId::SoftwareKeyboard;
case AppletId::MiiEdit:
return AppletProgramId::MiiEdit;
case AppletId::Web:
return AppletProgramId::Web;
case AppletId::Shop:
return AppletProgramId::Shop;
case AppletId::PhotoViewer:
return AppletProgramId::PhotoViewer;
case AppletId::Settings:
return AppletProgramId::Settings;
case AppletId::OfflineWeb:
return AppletProgramId::OfflineWeb;
case AppletId::LoginShare:
return AppletProgramId::LoginShare;
case AppletId::WebAuth:
return AppletProgramId::WebAuth;
case AppletId::MyPage:
return AppletProgramId::MyPage;
default:
return static_cast<AppletProgramId>(0);
}
}
[[maybe_unused]] std::shared_ptr<ILibraryAppletAccessor> CreateGuestApplet(
Core::System& system, std::shared_ptr<Applet> caller_applet, AppletId applet_id,
LibraryAppletMode mode) {
const auto program_id = static_cast<u64>(AppletIdToProgramId(applet_id));
if (program_id == 0) {
// Unknown applet
return {};
}
auto process = std::make_unique<Process>(system);
if (!process->Initialize(program_id)) {
// Couldn't initialize the guest process
return {};
}
const auto applet = std::make_shared<Applet>(system, std::move(process));
applet->program_id = program_id;
applet->applet_id = applet_id;
applet->type = AppletType::LibraryApplet;
applet->library_applet_mode = mode;
// Set focus state
applet->message_queue.PushMessage(AppletMessageQueue::AppletMessage::FocusStateChanged);
switch (mode) {
case LibraryAppletMode::AllForeground:
case LibraryAppletMode::NoUI:
applet->message_queue.PushMessage(AppletMessageQueue::AppletMessage::ChangeIntoForeground);
applet->focus_state = FocusState::InFocus;
applet->hid_registration.EnableAppletToGetInput(true);
break;
case LibraryAppletMode::AllForegroundInitiallyHidden:
applet->system_buffer_manager.SetWindowVisibility(false);
applet->focus_state = FocusState::NotInFocus;
applet->hid_registration.EnableAppletToGetInput(false);
break;
case LibraryAppletMode::Background:
case LibraryAppletMode::BackgroundIndirectDisplay:
default:
applet->focus_state = FocusState::Background;
applet->hid_registration.EnableAppletToGetInput(true);
break;
}
auto broker = std::make_shared<AppletDataBroker>(system);
applet->caller_applet = caller_applet;
applet->caller_applet_broker = broker;
system.GetAppletManager().InsertApplet(applet);
return std::make_shared<ILibraryAppletAccessor>(system, broker, applet);
}
std::shared_ptr<ILibraryAppletAccessor> CreateFrontendApplet(Core::System& system,
std::shared_ptr<Applet> caller_applet,
AppletId applet_id,
LibraryAppletMode mode) {
const auto program_id = static_cast<u64>(AppletIdToProgramId(applet_id));
if (program_id == 0) {
// Unknown applet
return {};
}
auto process = std::make_unique<Process>(system);
auto applet = std::make_shared<Applet>(system, std::move(process));
applet->program_id = program_id;
applet->applet_id = applet_id;
applet->type = AppletType::LibraryApplet;
applet->library_applet_mode = mode;
auto storage = std::make_shared<AppletDataBroker>(system);
applet->caller_applet = caller_applet;
applet->caller_applet_broker = storage;
applet->frontend = system.GetFrontendAppletHolder().GetApplet(applet, applet_id, mode);
return std::make_shared<ILibraryAppletAccessor>(system, storage, applet);
}
} // namespace
ILibraryAppletCreator::ILibraryAppletCreator(Core::System& system_, std::shared_ptr<Applet> applet_)
: ServiceFramework{system_, "ILibraryAppletCreator"}, applet{std::move(applet_)} {
static const FunctionInfo functions[] = {
{0, &ILibraryAppletCreator::CreateLibraryApplet, "CreateLibraryApplet"},
{1, nullptr, "TerminateAllLibraryApplets"},
{2, nullptr, "AreAnyLibraryAppletsLeft"},
{10, &ILibraryAppletCreator::CreateStorage, "CreateStorage"},
{11, &ILibraryAppletCreator::CreateTransferMemoryStorage, "CreateTransferMemoryStorage"},
{12, &ILibraryAppletCreator::CreateHandleStorage, "CreateHandleStorage"},
};
RegisterHandlers(functions);
}
ILibraryAppletCreator::~ILibraryAppletCreator() = default;
void ILibraryAppletCreator::CreateLibraryApplet(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto applet_id = rp.PopRaw<AppletId>();
const auto applet_mode = rp.PopRaw<LibraryAppletMode>();
LOG_DEBUG(Service_AM, "called with applet_id={:08X}, applet_mode={:08X}", applet_id,
applet_mode);
auto library_applet = CreateFrontendApplet(system, applet, applet_id, applet_mode);
if (!library_applet) {
LOG_ERROR(Service_AM, "Applet doesn't exist! applet_id={}", applet_id);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultUnknown);
return;
}
// Applet is created, can now be launched.
applet->library_applet_launchable_event.Signal();
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess);
rb.PushIpcInterface<ILibraryAppletAccessor>(library_applet);
}
void ILibraryAppletCreator::CreateStorage(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const s64 size{rp.Pop<s64>()};
LOG_DEBUG(Service_AM, "called, size={}", size);
if (size <= 0) {
LOG_ERROR(Service_AM, "size is less than or equal to 0");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultUnknown);
return;
}
std::vector<u8> data(size);
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess);
rb.PushIpcInterface<IStorage>(system, AM::CreateStorage(std::move(data)));
}
void ILibraryAppletCreator::CreateTransferMemoryStorage(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
struct Parameters {
bool is_writable;
s64 size;
};
const auto params{rp.PopRaw<Parameters>()};
const auto handle{ctx.GetCopyHandle(0)};
LOG_DEBUG(Service_AM, "called, is_writable={}, size={}, handle={:08X}", params.is_writable,
params.size, handle);
if (params.size <= 0) {
LOG_ERROR(Service_AM, "size is less than or equal to 0");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultUnknown);
return;
}
auto transfer_mem = ctx.GetObjectFromHandle<Kernel::KTransferMemory>(handle);
if (transfer_mem.IsNull()) {
LOG_ERROR(Service_AM, "transfer_mem is a nullptr for handle={:08X}", handle);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultUnknown);
return;
}
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess);
rb.PushIpcInterface<IStorage>(
system, AM::CreateTransferMemoryStorage(ctx.GetMemory(), transfer_mem.GetPointerUnsafe(),
params.is_writable, params.size));
}
void ILibraryAppletCreator::CreateHandleStorage(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const s64 size{rp.Pop<s64>()};
const auto handle{ctx.GetCopyHandle(0)};
LOG_DEBUG(Service_AM, "called, size={}, handle={:08X}", size, handle);
if (size <= 0) {
LOG_ERROR(Service_AM, "size is less than or equal to 0");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultUnknown);
return;
}
auto transfer_mem = ctx.GetObjectFromHandle<Kernel::KTransferMemory>(handle);
if (transfer_mem.IsNull()) {
LOG_ERROR(Service_AM, "transfer_mem is a nullptr for handle={:08X}", handle);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultUnknown);
return;
}
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess);
rb.PushIpcInterface<IStorage>(
system, AM::CreateHandleStorage(ctx.GetMemory(), transfer_mem.GetPointerUnsafe(), size));
}
} // namespace Service::AM

View File

@ -0,0 +1,26 @@
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "core/hle/service/service.h"
namespace Service::AM {
struct Applet;
class ILibraryAppletCreator final : public ServiceFramework<ILibraryAppletCreator> {
public:
explicit ILibraryAppletCreator(Core::System& system_, std::shared_ptr<Applet> applet_);
~ILibraryAppletCreator() override;
private:
void CreateLibraryApplet(HLERequestContext& ctx);
void CreateStorage(HLERequestContext& ctx);
void CreateTransferMemoryStorage(HLERequestContext& ctx);
void CreateHandleStorage(HLERequestContext& ctx);
const std::shared_ptr<Applet> applet;
};
} // namespace Service::AM

View File

@ -0,0 +1,143 @@
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "core/hle/service/am/applet_common_functions.h"
#include "core/hle/service/am/audio_controller.h"
#include "core/hle/service/am/common_state_getter.h"
#include "core/hle/service/am/debug_functions.h"
#include "core/hle/service/am/display_controller.h"
#include "core/hle/service/am/global_state_controller.h"
#include "core/hle/service/am/home_menu_functions.h"
#include "core/hle/service/am/library_applet_creator.h"
#include "core/hle/service/am/library_applet_proxy.h"
#include "core/hle/service/am/library_applet_self_accessor.h"
#include "core/hle/service/am/process_winding_controller.h"
#include "core/hle/service/am/self_controller.h"
#include "core/hle/service/am/window_controller.h"
#include "core/hle/service/ipc_helpers.h"
namespace Service::AM {
ILibraryAppletProxy::ILibraryAppletProxy(Nvnflinger::Nvnflinger& nvnflinger_,
std::shared_ptr<Applet> applet_, Core::System& system_)
: ServiceFramework{system_, "ILibraryAppletProxy"}, nvnflinger{nvnflinger_}, applet{std::move(
applet_)} {
// clang-format off
static const FunctionInfo functions[] = {
{0, &ILibraryAppletProxy::GetCommonStateGetter, "GetCommonStateGetter"},
{1, &ILibraryAppletProxy::GetSelfController, "GetSelfController"},
{2, &ILibraryAppletProxy::GetWindowController, "GetWindowController"},
{3, &ILibraryAppletProxy::GetAudioController, "GetAudioController"},
{4, &ILibraryAppletProxy::GetDisplayController, "GetDisplayController"},
{10, &ILibraryAppletProxy::GetProcessWindingController, "GetProcessWindingController"},
{11, &ILibraryAppletProxy::GetLibraryAppletCreator, "GetLibraryAppletCreator"},
{20, &ILibraryAppletProxy::OpenLibraryAppletSelfAccessor, "OpenLibraryAppletSelfAccessor"},
{21, &ILibraryAppletProxy::GetAppletCommonFunctions, "GetAppletCommonFunctions"},
{22, &ILibraryAppletProxy::GetHomeMenuFunctions, "GetHomeMenuFunctions"},
{23, &ILibraryAppletProxy::GetGlobalStateController, "GetGlobalStateController"},
{1000, &ILibraryAppletProxy::GetDebugFunctions, "GetDebugFunctions"},
};
// clang-format on
RegisterHandlers(functions);
}
ILibraryAppletProxy::~ILibraryAppletProxy() = default;
void ILibraryAppletProxy::GetCommonStateGetter(HLERequestContext& ctx) {
LOG_DEBUG(Service_AM, "called");
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess);
rb.PushIpcInterface<ICommonStateGetter>(system, applet);
}
void ILibraryAppletProxy::GetSelfController(HLERequestContext& ctx) {
LOG_DEBUG(Service_AM, "called");
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess);
rb.PushIpcInterface<ISelfController>(system, applet, nvnflinger);
}
void ILibraryAppletProxy::GetWindowController(HLERequestContext& ctx) {
LOG_DEBUG(Service_AM, "called");
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess);
rb.PushIpcInterface<IWindowController>(system, applet);
}
void ILibraryAppletProxy::GetAudioController(HLERequestContext& ctx) {
LOG_DEBUG(Service_AM, "called");
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess);
rb.PushIpcInterface<IAudioController>(system);
}
void ILibraryAppletProxy::GetDisplayController(HLERequestContext& ctx) {
LOG_DEBUG(Service_AM, "called");
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess);
rb.PushIpcInterface<IDisplayController>(system, applet);
}
void ILibraryAppletProxy::GetProcessWindingController(HLERequestContext& ctx) {
LOG_DEBUG(Service_AM, "called");
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess);
rb.PushIpcInterface<IProcessWindingController>(system, applet);
}
void ILibraryAppletProxy::GetLibraryAppletCreator(HLERequestContext& ctx) {
LOG_DEBUG(Service_AM, "called");
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess);
rb.PushIpcInterface<ILibraryAppletCreator>(system, applet);
}
void ILibraryAppletProxy::OpenLibraryAppletSelfAccessor(HLERequestContext& ctx) {
LOG_DEBUG(Service_AM, "called");
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess);
rb.PushIpcInterface<ILibraryAppletSelfAccessor>(system, applet);
}
void ILibraryAppletProxy::GetAppletCommonFunctions(HLERequestContext& ctx) {
LOG_DEBUG(Service_AM, "called");
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess);
rb.PushIpcInterface<IAppletCommonFunctions>(system, applet);
}
void ILibraryAppletProxy::GetHomeMenuFunctions(HLERequestContext& ctx) {
LOG_DEBUG(Service_AM, "called");
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess);
rb.PushIpcInterface<IHomeMenuFunctions>(system);
}
void ILibraryAppletProxy::GetGlobalStateController(HLERequestContext& ctx) {
LOG_DEBUG(Service_AM, "called");
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess);
rb.PushIpcInterface<IGlobalStateController>(system);
}
void ILibraryAppletProxy::GetDebugFunctions(HLERequestContext& ctx) {
LOG_DEBUG(Service_AM, "called");
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess);
rb.PushIpcInterface<IDebugFunctions>(system);
}
} // namespace Service::AM

View File

@ -0,0 +1,36 @@
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "core/hle/service/service.h"
namespace Service::AM {
struct Applet;
class ILibraryAppletProxy final : public ServiceFramework<ILibraryAppletProxy> {
public:
explicit ILibraryAppletProxy(Nvnflinger::Nvnflinger& nvnflinger_,
std::shared_ptr<Applet> applet_, Core::System& system_);
~ILibraryAppletProxy();
private:
void GetCommonStateGetter(HLERequestContext& ctx);
void GetSelfController(HLERequestContext& ctx);
void GetWindowController(HLERequestContext& ctx);
void GetAudioController(HLERequestContext& ctx);
void GetDisplayController(HLERequestContext& ctx);
void GetProcessWindingController(HLERequestContext& ctx);
void GetLibraryAppletCreator(HLERequestContext& ctx);
void OpenLibraryAppletSelfAccessor(HLERequestContext& ctx);
void GetAppletCommonFunctions(HLERequestContext& ctx);
void GetHomeMenuFunctions(HLERequestContext& ctx);
void GetGlobalStateController(HLERequestContext& ctx);
void GetDebugFunctions(HLERequestContext& ctx);
Nvnflinger::Nvnflinger& nvnflinger;
std::shared_ptr<Applet> applet;
};
} // namespace Service::AM

View File

@ -0,0 +1,338 @@
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "common/scope_exit.h"
#include "core/core_timing.h"
#include "core/file_sys/control_metadata.h"
#include "core/file_sys/patch_manager.h"
#include "core/file_sys/registered_cache.h"
#include "core/hle/service/acc/profile_manager.h"
#include "core/hle/service/am/am_results.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/frontend/applets.h"
#include "core/hle/service/am/library_applet_self_accessor.h"
#include "core/hle/service/am/storage.h"
#include "core/hle/service/ipc_helpers.h"
#include "core/hle/service/ns/ns.h"
#include "core/hle/service/sm/sm.h"
#include "hid_core/hid_types.h"
namespace Service::AM {
namespace {
AppletIdentityInfo GetCallerIdentity(std::shared_ptr<Applet> applet) {
if (const auto caller_applet = applet->caller_applet.lock(); caller_applet) {
// TODO: is this actually the application ID?
return {
.applet_id = caller_applet->applet_id,
.application_id = caller_applet->program_id,
};
} else {
return {
.applet_id = AppletId::QLaunch,
.application_id = 0x0100000000001000ull,
};
}
}
} // namespace
ILibraryAppletSelfAccessor::ILibraryAppletSelfAccessor(Core::System& system_,
std::shared_ptr<Applet> applet_)
: ServiceFramework{system_, "ILibraryAppletSelfAccessor"}, applet{std::move(applet_)},
broker{applet->caller_applet_broker} {
// clang-format off
static const FunctionInfo functions[] = {
{0, &ILibraryAppletSelfAccessor::PopInData, "PopInData"},
{1, &ILibraryAppletSelfAccessor::PushOutData, "PushOutData"},
{2, &ILibraryAppletSelfAccessor::PopInteractiveInData, "PopInteractiveInData"},
{3, &ILibraryAppletSelfAccessor::PushInteractiveOutData, "PushInteractiveOutData"},
{5, &ILibraryAppletSelfAccessor::GetPopInDataEvent, "GetPopInDataEvent"},
{6, &ILibraryAppletSelfAccessor::GetPopInteractiveInDataEvent, "GetPopInteractiveInDataEvent"},
{10, &ILibraryAppletSelfAccessor::ExitProcessAndReturn, "ExitProcessAndReturn"},
{11, &ILibraryAppletSelfAccessor::GetLibraryAppletInfo, "GetLibraryAppletInfo"},
{12, &ILibraryAppletSelfAccessor::GetMainAppletIdentityInfo, "GetMainAppletIdentityInfo"},
{13, &ILibraryAppletSelfAccessor::CanUseApplicationCore, "CanUseApplicationCore"},
{14, &ILibraryAppletSelfAccessor::GetCallerAppletIdentityInfo, "GetCallerAppletIdentityInfo"},
{15, nullptr, "GetMainAppletApplicationControlProperty"},
{16, nullptr, "GetMainAppletStorageId"},
{17, nullptr, "GetCallerAppletIdentityInfoStack"},
{18, nullptr, "GetNextReturnDestinationAppletIdentityInfo"},
{19, &ILibraryAppletSelfAccessor::GetDesirableKeyboardLayout, "GetDesirableKeyboardLayout"},
{20, nullptr, "PopExtraStorage"},
{25, nullptr, "GetPopExtraStorageEvent"},
{30, nullptr, "UnpopInData"},
{31, nullptr, "UnpopExtraStorage"},
{40, nullptr, "GetIndirectLayerProducerHandle"},
{50, nullptr, "ReportVisibleError"},
{51, nullptr, "ReportVisibleErrorWithErrorContext"},
{60, &ILibraryAppletSelfAccessor::GetMainAppletApplicationDesiredLanguage, "GetMainAppletApplicationDesiredLanguage"},
{70, &ILibraryAppletSelfAccessor::GetCurrentApplicationId, "GetCurrentApplicationId"},
{80, nullptr, "RequestExitToSelf"},
{90, nullptr, "CreateApplicationAndPushAndRequestToLaunch"},
{100, nullptr, "CreateGameMovieTrimmer"},
{101, nullptr, "ReserveResourceForMovieOperation"},
{102, nullptr, "UnreserveResourceForMovieOperation"},
{110, &ILibraryAppletSelfAccessor::GetMainAppletAvailableUsers, "GetMainAppletAvailableUsers"},
{120, nullptr, "GetLaunchStorageInfoForDebug"},
{130, nullptr, "GetGpuErrorDetectedSystemEvent"},
{140, nullptr, "SetApplicationMemoryReservation"},
{150, &ILibraryAppletSelfAccessor::ShouldSetGpuTimeSliceManually, "ShouldSetGpuTimeSliceManually"},
{160, &ILibraryAppletSelfAccessor::Cmd160, "Cmd160"},
};
// clang-format on
RegisterHandlers(functions);
}
ILibraryAppletSelfAccessor::~ILibraryAppletSelfAccessor() = default;
void ILibraryAppletSelfAccessor::PopInData(HLERequestContext& ctx) {
LOG_INFO(Service_AM, "called");
std::shared_ptr<IStorage> data;
const auto res = broker->GetInData().Pop(&data);
if (res.IsSuccess()) {
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(res);
rb.PushIpcInterface(std::move(data));
} else {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(res);
}
}
void ILibraryAppletSelfAccessor::PushOutData(HLERequestContext& ctx) {
LOG_INFO(Service_AM, "called");
IPC::RequestParser rp{ctx};
broker->GetOutData().Push(rp.PopIpcInterface<IStorage>().lock());
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
void ILibraryAppletSelfAccessor::PopInteractiveInData(HLERequestContext& ctx) {
LOG_INFO(Service_AM, "called");
std::shared_ptr<IStorage> data;
const auto res = broker->GetInteractiveInData().Pop(&data);
if (res.IsSuccess()) {
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(res);
rb.PushIpcInterface(std::move(data));
} else {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(res);
}
}
void ILibraryAppletSelfAccessor::PushInteractiveOutData(HLERequestContext& ctx) {
LOG_INFO(Service_AM, "called");
IPC::RequestParser rp{ctx};
broker->GetInteractiveOutData().Push(rp.PopIpcInterface<IStorage>().lock());
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
void ILibraryAppletSelfAccessor::GetPopInDataEvent(HLERequestContext& ctx) {
LOG_INFO(Service_AM, "called");
IPC::ResponseBuilder rb{ctx, 2, 1};
rb.Push(ResultSuccess);
rb.PushCopyObjects(broker->GetInData().GetEvent());
}
void ILibraryAppletSelfAccessor::GetPopInteractiveInDataEvent(HLERequestContext& ctx) {
LOG_INFO(Service_AM, "called");
IPC::ResponseBuilder rb{ctx, 2, 1};
rb.Push(ResultSuccess);
rb.PushCopyObjects(broker->GetInteractiveInData().GetEvent());
}
void ILibraryAppletSelfAccessor::ExitProcessAndReturn(HLERequestContext& ctx) {
LOG_INFO(Service_AM, "called");
system.GetAppletManager().TerminateAndRemoveApplet(applet->aruid);
broker->SignalCompletion();
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
void ILibraryAppletSelfAccessor::GetLibraryAppletInfo(HLERequestContext& ctx) {
struct LibraryAppletInfo {
AppletId applet_id;
LibraryAppletMode library_applet_mode;
};
LOG_WARNING(Service_AM, "(STUBBED) called");
const LibraryAppletInfo applet_info{
.applet_id = applet->applet_id,
.library_applet_mode = applet->library_applet_mode,
};
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(ResultSuccess);
rb.PushRaw(applet_info);
}
void ILibraryAppletSelfAccessor::GetMainAppletIdentityInfo(HLERequestContext& ctx) {
LOG_WARNING(Service_AM, "(STUBBED) called");
const AppletIdentityInfo applet_info{
.applet_id = AppletId::QLaunch,
.application_id = 0x0100000000001000ull,
};
IPC::ResponseBuilder rb{ctx, 6};
rb.Push(ResultSuccess);
rb.PushRaw(applet_info);
}
void ILibraryAppletSelfAccessor::CanUseApplicationCore(HLERequestContext& ctx) {
LOG_WARNING(Service_AM, "(STUBBED) called");
// TODO: This appears to read the NPDM from state and check the core mask of the applet.
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
rb.Push<u8>(0);
}
void ILibraryAppletSelfAccessor::GetCallerAppletIdentityInfo(HLERequestContext& ctx) {
LOG_WARNING(Service_AM, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 6};
rb.Push(ResultSuccess);
rb.PushRaw(GetCallerIdentity(applet));
}
void ILibraryAppletSelfAccessor::GetDesirableKeyboardLayout(HLERequestContext& ctx) {
LOG_WARNING(Service_AM, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
rb.Push<u32>(0);
}
void ILibraryAppletSelfAccessor::GetMainAppletApplicationDesiredLanguage(HLERequestContext& ctx) {
// FIXME: this is copied from IApplicationFunctions::GetDesiredLanguage
auto identity = GetCallerIdentity(applet);
// TODO(bunnei): This should be configurable
LOG_DEBUG(Service_AM, "called");
// Get supported languages from NACP, if possible
// Default to 0 (all languages supported)
u32 supported_languages = 0;
const auto res = [this, identity] {
const FileSys::PatchManager pm{identity.application_id, system.GetFileSystemController(),
system.GetContentProvider()};
auto metadata = pm.GetControlMetadata();
if (metadata.first != nullptr) {
return metadata;
}
const FileSys::PatchManager pm_update{FileSys::GetUpdateTitleID(identity.application_id),
system.GetFileSystemController(),
system.GetContentProvider()};
return pm_update.GetControlMetadata();
}();
if (res.first != nullptr) {
supported_languages = res.first->GetSupportedLanguages();
}
// Call IApplicationManagerInterface implementation.
auto& service_manager = system.ServiceManager();
auto ns_am2 = service_manager.GetService<NS::NS>("ns:am2");
auto app_man = ns_am2->GetApplicationManagerInterface();
// Get desired application language
u8 desired_language{};
const auto res_lang =
app_man->GetApplicationDesiredLanguage(&desired_language, supported_languages);
if (res_lang != ResultSuccess) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(res_lang);
return;
}
// Convert to settings language code.
u64 language_code{};
const auto res_code =
app_man->ConvertApplicationLanguageToLanguageCode(&language_code, desired_language);
if (res_code != ResultSuccess) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(res_code);
return;
}
LOG_DEBUG(Service_AM, "got desired_language={:016X}", language_code);
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(ResultSuccess);
rb.Push(language_code);
}
void ILibraryAppletSelfAccessor::GetCurrentApplicationId(HLERequestContext& ctx) {
LOG_WARNING(Service_AM, "(STUBBED) called");
u64 application_id = 0;
if (auto caller_applet = applet->caller_applet.lock(); caller_applet) {
application_id = caller_applet->program_id;
}
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(ResultSuccess);
rb.Push(application_id);
}
void ILibraryAppletSelfAccessor::GetMainAppletAvailableUsers(HLERequestContext& ctx) {
const Service::Account::ProfileManager manager{};
bool is_empty{true};
s32 user_count{-1};
LOG_INFO(Service_AM, "called");
if (manager.GetUserCount() > 0) {
is_empty = false;
user_count = static_cast<s32>(manager.GetUserCount());
ctx.WriteBuffer(manager.GetAllUsers());
}
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(ResultSuccess);
rb.Push<u8>(is_empty);
rb.Push(user_count);
}
void ILibraryAppletSelfAccessor::ShouldSetGpuTimeSliceManually(HLERequestContext& ctx) {
LOG_WARNING(Service_AM, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
rb.Push<u8>(0);
}
void ILibraryAppletSelfAccessor::Cmd160(HLERequestContext& ctx) {
LOG_WARNING(Service_AM, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(ResultSuccess);
rb.Push<u64>(0);
}
} // namespace Service::AM

View File

@ -0,0 +1,44 @@
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <deque>
#include <vector>
#include "core/hle/service/service.h"
namespace Service::AM {
class AppletDataBroker;
struct Applet;
class ILibraryAppletSelfAccessor final : public ServiceFramework<ILibraryAppletSelfAccessor> {
public:
explicit ILibraryAppletSelfAccessor(Core::System& system_, std::shared_ptr<Applet> applet_);
~ILibraryAppletSelfAccessor() override;
private:
void PopInData(HLERequestContext& ctx);
void PushOutData(HLERequestContext& ctx);
void PopInteractiveInData(HLERequestContext& ctx);
void PushInteractiveOutData(HLERequestContext& ctx);
void GetPopInDataEvent(HLERequestContext& ctx);
void GetPopInteractiveInDataEvent(HLERequestContext& ctx);
void GetLibraryAppletInfo(HLERequestContext& ctx);
void GetMainAppletIdentityInfo(HLERequestContext& ctx);
void CanUseApplicationCore(HLERequestContext& ctx);
void ExitProcessAndReturn(HLERequestContext& ctx);
void GetCallerAppletIdentityInfo(HLERequestContext& ctx);
void GetDesirableKeyboardLayout(HLERequestContext& ctx);
void GetMainAppletApplicationDesiredLanguage(HLERequestContext& ctx);
void GetCurrentApplicationId(HLERequestContext& ctx);
void GetMainAppletAvailableUsers(HLERequestContext& ctx);
void ShouldSetGpuTimeSliceManually(HLERequestContext& ctx);
void Cmd160(HLERequestContext& ctx);
const std::shared_ptr<Applet> applet;
const std::shared_ptr<AppletDataBroker> broker;
};
} // namespace Service::AM

View File

@ -0,0 +1,140 @@
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "core/hle/kernel/k_transfer_memory.h"
#include "core/hle/service/am/am_results.h"
#include "core/hle/service/am/library_applet_storage.h"
#include "core/memory.h"
namespace Service::AM {
namespace {
Result ValidateOffset(s64 offset, size_t size, size_t data_size) {
R_UNLESS(offset >= 0, AM::ResultInvalidOffset);
const size_t begin = offset;
const size_t end = begin + size;
R_UNLESS(begin <= end && end <= data_size, AM::ResultInvalidOffset);
R_SUCCEED();
}
class BufferLibraryAppletStorage final : public LibraryAppletStorage {
public:
explicit BufferLibraryAppletStorage(std::vector<u8>&& data) : m_data(std::move(data)) {}
~BufferLibraryAppletStorage() = default;
Result Read(s64 offset, void* buffer, size_t size) override {
R_TRY(ValidateOffset(offset, size, m_data.size()));
std::memcpy(buffer, m_data.data() + offset, size);
R_SUCCEED();
}
Result Write(s64 offset, const void* buffer, size_t size) override {
R_TRY(ValidateOffset(offset, size, m_data.size()));
std::memcpy(m_data.data() + offset, buffer, size);
R_SUCCEED();
}
s64 GetSize() override {
return m_data.size();
}
Kernel::KTransferMemory* GetHandle() override {
return nullptr;
}
private:
std::vector<u8> m_data;
};
class TransferMemoryLibraryAppletStorage : public LibraryAppletStorage {
public:
explicit TransferMemoryLibraryAppletStorage(Core::Memory::Memory& memory,
Kernel::KTransferMemory* trmem, bool is_writable,
s64 size)
: m_memory(memory), m_trmem(trmem), m_is_writable(is_writable), m_size(size) {
m_trmem->Open();
}
~TransferMemoryLibraryAppletStorage() {
m_trmem->Close();
m_trmem = nullptr;
}
Result Read(s64 offset, void* buffer, size_t size) override {
R_TRY(ValidateOffset(offset, size, m_size));
m_memory.ReadBlock(m_trmem->GetSourceAddress(), buffer, size);
R_SUCCEED();
}
Result Write(s64 offset, const void* buffer, size_t size) override {
R_UNLESS(m_is_writable, ResultUnknown);
R_TRY(ValidateOffset(offset, size, m_size));
m_memory.WriteBlock(m_trmem->GetSourceAddress(), buffer, size);
R_SUCCEED();
}
s64 GetSize() override {
return m_size;
}
Kernel::KTransferMemory* GetHandle() override {
return nullptr;
}
protected:
Core::Memory::Memory& m_memory;
Kernel::KTransferMemory* m_trmem;
bool m_is_writable;
s64 m_size;
};
class HandleLibraryAppletStorage : public TransferMemoryLibraryAppletStorage {
public:
explicit HandleLibraryAppletStorage(Core::Memory::Memory& memory,
Kernel::KTransferMemory* trmem, s64 size)
: TransferMemoryLibraryAppletStorage(memory, trmem, true, size) {}
~HandleLibraryAppletStorage() = default;
Kernel::KTransferMemory* GetHandle() override {
return m_trmem;
}
};
} // namespace
LibraryAppletStorage::~LibraryAppletStorage() = default;
std::vector<u8> LibraryAppletStorage::GetData() {
std::vector<u8> data(this->GetSize());
this->Read(0, data.data(), data.size());
return data;
}
std::shared_ptr<LibraryAppletStorage> CreateStorage(std::vector<u8>&& data) {
return std::make_shared<BufferLibraryAppletStorage>(std::move(data));
}
std::shared_ptr<LibraryAppletStorage> CreateTransferMemoryStorage(Core::Memory::Memory& memory,
Kernel::KTransferMemory* trmem,
bool is_writable, s64 size) {
return std::make_shared<TransferMemoryLibraryAppletStorage>(memory, trmem, is_writable, size);
}
std::shared_ptr<LibraryAppletStorage> CreateHandleStorage(Core::Memory::Memory& memory,
Kernel::KTransferMemory* trmem,
s64 size) {
return std::make_shared<HandleLibraryAppletStorage>(memory, trmem, size);
}
} // namespace Service::AM

View File

@ -0,0 +1,36 @@
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "core/hle/service/service.h"
namespace Core::Memory {
class Memory;
}
namespace Kernel {
class KTransferMemory;
}
namespace Service::AM {
class LibraryAppletStorage {
public:
virtual ~LibraryAppletStorage();
virtual Result Read(s64 offset, void* buffer, size_t size) = 0;
virtual Result Write(s64 offset, const void* buffer, size_t size) = 0;
virtual s64 GetSize() = 0;
virtual Kernel::KTransferMemory* GetHandle() = 0;
std::vector<u8> GetData();
};
std::shared_ptr<LibraryAppletStorage> CreateStorage(std::vector<u8>&& data);
std::shared_ptr<LibraryAppletStorage> CreateTransferMemoryStorage(Core::Memory::Memory& memory,
Kernel::KTransferMemory* trmem,
bool is_writable, s64 size);
std::shared_ptr<LibraryAppletStorage> CreateHandleStorage(Core::Memory::Memory& memory,
Kernel::KTransferMemory* trmem, s64 size);
} // namespace Service::AM

View File

@ -0,0 +1,71 @@
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "core/hle/service/am/lock_accessor.h"
#include "core/hle/service/ipc_helpers.h"
namespace Service::AM {
ILockAccessor::ILockAccessor(Core::System& system_)
: ServiceFramework{system_, "ILockAccessor"}, service_context{system_, "ILockAccessor"} {
// clang-format off
static const FunctionInfo functions[] = {
{1, &ILockAccessor::TryLock, "TryLock"},
{2, &ILockAccessor::Unlock, "Unlock"},
{3, &ILockAccessor::GetEvent, "GetEvent"},
{4,&ILockAccessor::IsLocked, "IsLocked"},
};
// clang-format on
RegisterHandlers(functions);
lock_event = service_context.CreateEvent("ILockAccessor::LockEvent");
}
ILockAccessor::~ILockAccessor() {
service_context.CloseEvent(lock_event);
};
void ILockAccessor::TryLock(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto return_handle = rp.Pop<bool>();
LOG_WARNING(Service_AM, "(STUBBED) called, return_handle={}", return_handle);
// TODO: When return_handle is true this function should return the lock handle
is_locked = true;
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
rb.Push<u8>(is_locked);
}
void ILockAccessor::Unlock(HLERequestContext& ctx) {
LOG_INFO(Service_AM, "called");
is_locked = false;
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
void ILockAccessor::GetEvent(HLERequestContext& ctx) {
LOG_INFO(Service_AM, "called");
lock_event->Signal();
IPC::ResponseBuilder rb{ctx, 2, 1};
rb.Push(ResultSuccess);
rb.PushCopyObjects(lock_event->GetReadableEvent());
}
void ILockAccessor::IsLocked(HLERequestContext& ctx) {
LOG_INFO(Service_AM, "called");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
rb.Push<u8>(is_locked);
}
} // namespace Service::AM

View File

@ -0,0 +1,28 @@
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "core/hle/service/kernel_helpers.h"
#include "core/hle/service/service.h"
namespace Service::AM {
class ILockAccessor final : public ServiceFramework<ILockAccessor> {
public:
explicit ILockAccessor(Core::System& system_);
~ILockAccessor() override;
private:
void TryLock(HLERequestContext& ctx);
void Unlock(HLERequestContext& ctx);
void GetEvent(HLERequestContext& ctx);
void IsLocked(HLERequestContext& ctx);
bool is_locked{};
Kernel::KEvent* lock_event;
KernelHelpers::ServiceContext service_context;
};
} // namespace Service::AM

View File

@ -0,0 +1,59 @@
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "core/hle/service/am/managed_layer_holder.h"
#include "core/hle/service/nvnflinger/nvnflinger.h"
namespace Service::AM {
ManagedLayerHolder::ManagedLayerHolder() = default;
ManagedLayerHolder::~ManagedLayerHolder() {
if (!m_nvnflinger) {
return;
}
for (const auto& layer : m_managed_display_layers) {
m_nvnflinger->DestroyLayer(layer);
}
for (const auto& layer : m_managed_display_recording_layers) {
m_nvnflinger->DestroyLayer(layer);
}
m_nvnflinger = nullptr;
}
void ManagedLayerHolder::Initialize(Nvnflinger::Nvnflinger* nvnflinger) {
m_nvnflinger = nvnflinger;
}
void ManagedLayerHolder::CreateManagedDisplayLayer(u64* out_layer) {
// TODO(Subv): Find out how AM determines the display to use, for now just
// create the layer in the Default display.
const auto display_id = m_nvnflinger->OpenDisplay("Default");
const auto layer_id = m_nvnflinger->CreateLayer(*display_id);
m_managed_display_layers.emplace(*layer_id);
*out_layer = *layer_id;
}
void ManagedLayerHolder::CreateManagedDisplaySeparableLayer(u64* out_layer,
u64* out_recording_layer) {
// 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.
// TODO: Support multiple layers
const auto display_id = m_nvnflinger->OpenDisplay("Default");
const auto layer_id = m_nvnflinger->CreateLayer(*display_id);
m_managed_display_layers.emplace(*layer_id);
*out_layer = *layer_id;
*out_recording_layer = 0;
}
} // namespace Service::AM

View File

@ -0,0 +1,32 @@
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <set>
#include "common/common_funcs.h"
#include "common/common_types.h"
namespace Service::Nvnflinger {
class Nvnflinger;
}
namespace Service::AM {
class ManagedLayerHolder {
public:
ManagedLayerHolder();
~ManagedLayerHolder();
void Initialize(Nvnflinger::Nvnflinger* nvnflinger);
void CreateManagedDisplayLayer(u64* out_layer);
void CreateManagedDisplaySeparableLayer(u64* out_layer, u64* out_recording_layer);
private:
Nvnflinger::Nvnflinger* m_nvnflinger{};
std::set<u64> m_managed_display_layers{};
std::set<u64> m_managed_display_recording_layers{};
};
} // namespace Service::AM

View File

@ -0,0 +1,138 @@
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "common/scope_exit.h"
#include "core/file_sys/nca_metadata.h"
#include "core/file_sys/registered_cache.h"
#include "core/hle/kernel/k_process.h"
#include "core/hle/service/am/process.h"
#include "core/hle/service/filesystem/filesystem.h"
#include "core/loader/loader.h"
namespace Service::AM {
Process::Process(Core::System& system)
: m_system(system), m_process(), m_main_thread_priority(), m_main_thread_stack_size(),
m_program_id(), m_process_started() {}
Process::~Process() {
this->Finalize();
}
bool Process::Initialize(u64 program_id) {
// First, ensure we are not holding another process.
this->Finalize();
// Get the filesystem controller.
auto& fsc = m_system.GetFileSystemController();
// Attempt to load program NCA.
const FileSys::RegisteredCache* bis_system{};
FileSys::VirtualFile nca{};
// Get the program NCA from built-in storage.
bis_system = fsc.GetSystemNANDContents();
if (bis_system) {
nca = bis_system->GetEntryRaw(program_id, FileSys::ContentRecordType::Program);
}
// Ensure we retrieved a program NCA.
if (!nca) {
return false;
}
// Get the appropriate loader to parse this NCA.
auto app_loader = Loader::GetLoader(m_system, nca, program_id, 0);
// Ensure we have a loader which can parse the NCA.
if (!app_loader) {
return false;
}
// Create the process.
auto* const process = Kernel::KProcess::Create(m_system.Kernel());
Kernel::KProcess::Register(m_system.Kernel(), process);
// On exit, ensure we free the additional reference to the process.
SCOPE_EXIT({ process->Close(); });
// Insert process modules into memory.
const auto [load_result, load_parameters] = app_loader->Load(*process, m_system);
// Ensure loading was successful.
if (load_result != Loader::ResultStatus::Success) {
return false;
}
// TODO: remove this, kernel already tracks this
m_system.Kernel().AppendNewProcess(process);
// Note the load parameters from NPDM.
m_main_thread_priority = load_parameters->main_thread_priority;
m_main_thread_stack_size = load_parameters->main_thread_stack_size;
// This process has not started yet.
m_process_started = false;
// Take ownership of the process object.
m_process = process;
m_process->Open();
// We succeeded.
return true;
}
void Process::Finalize() {
// Terminate, if we are currently holding a process.
this->Terminate();
// Close the process.
if (m_process) {
m_process->Close();
// TODO: remove this, kernel already tracks this
m_system.Kernel().RemoveProcess(m_process);
}
// Clean up.
m_process = nullptr;
m_main_thread_priority = 0;
m_main_thread_stack_size = 0;
m_program_id = 0;
m_process_started = false;
}
bool Process::Run() {
// If we already started the process, don't start again.
if (m_process_started) {
return false;
}
// Start.
if (m_process) {
m_process->Run(m_main_thread_priority, m_main_thread_stack_size);
}
// Mark as started.
m_process_started = true;
// We succeeded.
return true;
}
void Process::Terminate() {
if (m_process) {
m_process->Terminate();
}
}
u64 Process::GetProcessId() const {
if (m_process) {
return m_process->GetProcessId();
}
return 0;
}
} // namespace Service::AM

View File

@ -0,0 +1,50 @@
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "common/common_funcs.h"
#include "common/common_types.h"
namespace Kernel {
class KProcess;
}
namespace Core {
class System;
}
namespace Service::AM {
class Process {
public:
explicit Process(Core::System& system);
~Process();
bool Initialize(u64 program_id);
void Finalize();
bool Run();
void Terminate();
bool IsInitialized() const {
return m_process != nullptr;
}
u64 GetProcessId() const;
u64 GetProgramId() const {
return m_program_id;
}
Kernel::KProcess* GetProcess() const {
return m_process;
}
private:
Core::System& m_system;
Kernel::KProcess* m_process{};
s32 m_main_thread_priority{};
u64 m_main_thread_stack_size{};
u64 m_program_id{};
bool m_process_started{};
};
} // namespace Service::AM

View File

@ -0,0 +1,56 @@
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "core/hle/service/am/frontend/applets.h"
#include "core/hle/service/am/library_applet_accessor.h"
#include "core/hle/service/am/process_winding_controller.h"
#include "core/hle/service/ipc_helpers.h"
namespace Service::AM {
IProcessWindingController::IProcessWindingController(Core::System& system_,
std::shared_ptr<Applet> applet_)
: ServiceFramework{system_, "IProcessWindingController"}, applet{std::move(applet_)} {
// clang-format off
static const FunctionInfo functions[] = {
{0, &IProcessWindingController::GetLaunchReason, "GetLaunchReason"},
{11, &IProcessWindingController::OpenCallingLibraryApplet, "OpenCallingLibraryApplet"},
{21, nullptr, "PushContext"},
{22, nullptr, "PopContext"},
{23, nullptr, "CancelWindingReservation"},
{30, nullptr, "WindAndDoReserved"},
{40, nullptr, "ReserveToStartAndWaitAndUnwindThis"},
{41, nullptr, "ReserveToStartAndWait"},
};
// clang-format on
RegisterHandlers(functions);
}
IProcessWindingController::~IProcessWindingController() = default;
void IProcessWindingController::GetLaunchReason(HLERequestContext& ctx) {
LOG_WARNING(Service_AM, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
rb.PushRaw(applet->launch_reason);
}
void IProcessWindingController::OpenCallingLibraryApplet(HLERequestContext& ctx) {
const auto caller_applet = applet->caller_applet.lock();
if (caller_applet == nullptr) {
LOG_ERROR(Service_AM, "No calling applet available");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultUnknown);
return;
}
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess);
rb.PushIpcInterface<ILibraryAppletAccessor>(system, applet->caller_applet_broker,
caller_applet);
}
} // namespace Service::AM

View File

@ -0,0 +1,24 @@
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "core/hle/service/service.h"
namespace Service::AM {
struct Applet;
class IProcessWindingController final : public ServiceFramework<IProcessWindingController> {
public:
explicit IProcessWindingController(Core::System& system_, std::shared_ptr<Applet> applet_);
~IProcessWindingController() override;
private:
void GetLaunchReason(HLERequestContext& ctx);
void OpenCallingLibraryApplet(HLERequestContext& ctx);
const std::shared_ptr<Applet> applet;
};
} // namespace Service::AM

View File

@ -0,0 +1,456 @@
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "core/hle/service/am/am_results.h"
#include "core/hle/service/am/frontend/applets.h"
#include "core/hle/service/am/self_controller.h"
#include "core/hle/service/caps/caps_su.h"
#include "core/hle/service/ipc_helpers.h"
#include "core/hle/service/nvnflinger/fb_share_buffer_manager.h"
#include "core/hle/service/nvnflinger/nvnflinger.h"
#include "core/hle/service/sm/sm.h"
#include "core/hle/service/vi/vi_results.h"
namespace Service::AM {
ISelfController::ISelfController(Core::System& system_, std::shared_ptr<Applet> applet_,
Nvnflinger::Nvnflinger& nvnflinger_)
: ServiceFramework{system_, "ISelfController"}, nvnflinger{nvnflinger_}, applet{std::move(
applet_)} {
// clang-format off
static const FunctionInfo functions[] = {
{0, &ISelfController::Exit, "Exit"},
{1, &ISelfController::LockExit, "LockExit"},
{2, &ISelfController::UnlockExit, "UnlockExit"},
{3, &ISelfController::EnterFatalSection, "EnterFatalSection"},
{4, &ISelfController::LeaveFatalSection, "LeaveFatalSection"},
{9, &ISelfController::GetLibraryAppletLaunchableEvent, "GetLibraryAppletLaunchableEvent"},
{10, &ISelfController::SetScreenShotPermission, "SetScreenShotPermission"},
{11, &ISelfController::SetOperationModeChangedNotification, "SetOperationModeChangedNotification"},
{12, &ISelfController::SetPerformanceModeChangedNotification, "SetPerformanceModeChangedNotification"},
{13, &ISelfController::SetFocusHandlingMode, "SetFocusHandlingMode"},
{14, &ISelfController::SetRestartMessageEnabled, "SetRestartMessageEnabled"},
{15, &ISelfController::SetScreenShotAppletIdentityInfo, "SetScreenShotAppletIdentityInfo"},
{16, &ISelfController::SetOutOfFocusSuspendingEnabled, "SetOutOfFocusSuspendingEnabled"},
{17, nullptr, "SetControllerFirmwareUpdateSection"},
{18, nullptr, "SetRequiresCaptureButtonShortPressedMessage"},
{19, &ISelfController::SetAlbumImageOrientation, "SetAlbumImageOrientation"},
{20, nullptr, "SetDesirableKeyboardLayout"},
{21, nullptr, "GetScreenShotProgramId"},
{40, &ISelfController::CreateManagedDisplayLayer, "CreateManagedDisplayLayer"},
{41, &ISelfController::IsSystemBufferSharingEnabled, "IsSystemBufferSharingEnabled"},
{42, &ISelfController::GetSystemSharedLayerHandle, "GetSystemSharedLayerHandle"},
{43, &ISelfController::GetSystemSharedBufferHandle, "GetSystemSharedBufferHandle"},
{44, &ISelfController::CreateManagedDisplaySeparableLayer, "CreateManagedDisplaySeparableLayer"},
{45, nullptr, "SetManagedDisplayLayerSeparationMode"},
{46, nullptr, "SetRecordingLayerCompositionEnabled"},
{50, &ISelfController::SetHandlesRequestToDisplay, "SetHandlesRequestToDisplay"},
{51, &ISelfController::ApproveToDisplay, "ApproveToDisplay"},
{60, nullptr, "OverrideAutoSleepTimeAndDimmingTime"},
{61, nullptr, "SetMediaPlaybackState"},
{62, &ISelfController::SetIdleTimeDetectionExtension, "SetIdleTimeDetectionExtension"},
{63, &ISelfController::GetIdleTimeDetectionExtension, "GetIdleTimeDetectionExtension"},
{64, nullptr, "SetInputDetectionSourceSet"},
{65, &ISelfController::ReportUserIsActive, "ReportUserIsActive"},
{66, nullptr, "GetCurrentIlluminance"},
{67, nullptr, "IsIlluminanceAvailable"},
{68, &ISelfController::SetAutoSleepDisabled, "SetAutoSleepDisabled"},
{69, &ISelfController::IsAutoSleepDisabled, "IsAutoSleepDisabled"},
{70, nullptr, "ReportMultimediaError"},
{71, nullptr, "GetCurrentIlluminanceEx"},
{72, nullptr, "SetInputDetectionPolicy"},
{80, nullptr, "SetWirelessPriorityMode"},
{90, &ISelfController::GetAccumulatedSuspendedTickValue, "GetAccumulatedSuspendedTickValue"},
{91, &ISelfController::GetAccumulatedSuspendedTickChangedEvent, "GetAccumulatedSuspendedTickChangedEvent"},
{100, &ISelfController::SetAlbumImageTakenNotificationEnabled, "SetAlbumImageTakenNotificationEnabled"},
{110, nullptr, "SetApplicationAlbumUserData"},
{120, &ISelfController::SaveCurrentScreenshot, "SaveCurrentScreenshot"},
{130, &ISelfController::SetRecordVolumeMuted, "SetRecordVolumeMuted"},
{1000, nullptr, "GetDebugStorageChannel"},
};
// clang-format on
RegisterHandlers(functions);
}
ISelfController::~ISelfController() = default;
void ISelfController::Exit(HLERequestContext& ctx) {
LOG_DEBUG(Service_AM, "called");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
// TODO
system.Exit();
}
void ISelfController::LockExit(HLERequestContext& ctx) {
LOG_DEBUG(Service_AM, "called");
system.SetExitLocked(true);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
void ISelfController::UnlockExit(HLERequestContext& ctx) {
LOG_DEBUG(Service_AM, "called");
system.SetExitLocked(false);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
if (system.GetExitRequested()) {
system.Exit();
}
}
void ISelfController::EnterFatalSection(HLERequestContext& ctx) {
std::scoped_lock lk{applet->lock};
applet->fatal_section_count++;
LOG_DEBUG(Service_AM, "called. Num fatal sections entered: {}", applet->fatal_section_count);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
void ISelfController::LeaveFatalSection(HLERequestContext& ctx) {
LOG_DEBUG(Service_AM, "called.");
// Entry and exit of fatal sections must be balanced.
std::scoped_lock lk{applet->lock};
if (applet->fatal_section_count == 0) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(AM::ResultFatalSectionCountImbalance);
return;
}
applet->fatal_section_count--;
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
void ISelfController::GetLibraryAppletLaunchableEvent(HLERequestContext& ctx) {
LOG_WARNING(Service_AM, "(STUBBED) called");
applet->library_applet_launchable_event.Signal();
IPC::ResponseBuilder rb{ctx, 2, 1};
rb.Push(ResultSuccess);
rb.PushCopyObjects(applet->library_applet_launchable_event.GetHandle());
}
void ISelfController::SetScreenShotPermission(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto permission = rp.PopEnum<ScreenshotPermission>();
LOG_DEBUG(Service_AM, "called, permission={}", permission);
std::scoped_lock lk{applet->lock};
applet->screenshot_permission = permission;
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
void ISelfController::SetOperationModeChangedNotification(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const bool notification_enabled = rp.Pop<bool>();
LOG_WARNING(Service_AM, "(STUBBED) called notification_enabled={}", notification_enabled);
std::scoped_lock lk{applet->lock};
applet->operation_mode_changed_notification_enabled = notification_enabled;
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
void ISelfController::SetPerformanceModeChangedNotification(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const bool notification_enabled = rp.Pop<bool>();
LOG_WARNING(Service_AM, "(STUBBED) called notification_enabled={}", notification_enabled);
std::scoped_lock lk{applet->lock};
applet->performance_mode_changed_notification_enabled = notification_enabled;
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
void ISelfController::SetFocusHandlingMode(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto flags = rp.PopRaw<FocusHandlingMode>();
LOG_WARNING(Service_AM, "(STUBBED) called. unknown0={}, unknown1={}, unknown2={}",
flags.unknown0, flags.unknown1, flags.unknown2);
std::scoped_lock lk{applet->lock};
applet->focus_handling_mode = flags;
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
void ISelfController::SetRestartMessageEnabled(HLERequestContext& ctx) {
LOG_WARNING(Service_AM, "(STUBBED) called");
std::scoped_lock lk{applet->lock};
applet->restart_message_enabled = true;
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
void ISelfController::SetScreenShotAppletIdentityInfo(HLERequestContext& ctx) {
LOG_WARNING(Service_AM, "(STUBBED) called");
IPC::RequestParser rp{ctx};
std::scoped_lock lk{applet->lock};
applet->screen_shot_identity = rp.PopRaw<AppletIdentityInfo>();
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
void ISelfController::SetOutOfFocusSuspendingEnabled(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const bool enabled = rp.Pop<bool>();
LOG_WARNING(Service_AM, "(STUBBED) called enabled={}", enabled);
std::scoped_lock lk{applet->lock};
ASSERT(applet->type == AppletType::Application);
applet->out_of_focus_suspension_enabled = enabled;
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
void ISelfController::SetAlbumImageOrientation(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto orientation = rp.PopRaw<Capture::AlbumImageOrientation>();
LOG_WARNING(Service_AM, "(STUBBED) called, orientation={}", static_cast<s32>(orientation));
std::scoped_lock lk{applet->lock};
applet->album_image_orientation = orientation;
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
void ISelfController::CreateManagedDisplayLayer(HLERequestContext& ctx) {
LOG_WARNING(Service_AM, "(STUBBED) called");
u64 layer_id{};
applet->managed_layer_holder.Initialize(&nvnflinger);
applet->managed_layer_holder.CreateManagedDisplayLayer(&layer_id);
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(ResultSuccess);
rb.Push(layer_id);
}
void ISelfController::IsSystemBufferSharingEnabled(HLERequestContext& ctx) {
LOG_WARNING(Service_AM, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(this->EnsureBufferSharingEnabled(ctx.GetThread().GetOwnerProcess()));
}
void ISelfController::GetSystemSharedLayerHandle(HLERequestContext& ctx) {
LOG_WARNING(Service_AM, "(STUBBED) called");
u64 buffer_id, layer_id;
applet->system_buffer_manager.GetSystemSharedLayerHandle(&buffer_id, &layer_id);
IPC::ResponseBuilder rb{ctx, 6};
rb.Push(this->EnsureBufferSharingEnabled(ctx.GetThread().GetOwnerProcess()));
rb.Push<s64>(buffer_id);
rb.Push<s64>(layer_id);
}
void ISelfController::GetSystemSharedBufferHandle(HLERequestContext& ctx) {
LOG_WARNING(Service_AM, "(STUBBED) called");
u64 buffer_id, layer_id;
applet->system_buffer_manager.GetSystemSharedLayerHandle(&buffer_id, &layer_id);
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(this->EnsureBufferSharingEnabled(ctx.GetThread().GetOwnerProcess()));
rb.Push<s64>(buffer_id);
}
Result ISelfController::EnsureBufferSharingEnabled(Kernel::KProcess* process) {
if (applet->system_buffer_manager.Initialize(&nvnflinger, process, applet->applet_id)) {
return ResultSuccess;
}
return VI::ResultOperationFailed;
}
void ISelfController::CreateManagedDisplaySeparableLayer(HLERequestContext& ctx) {
LOG_WARNING(Service_AM, "(STUBBED) called");
u64 layer_id{};
u64 recording_layer_id{};
applet->managed_layer_holder.Initialize(&nvnflinger);
applet->managed_layer_holder.CreateManagedDisplaySeparableLayer(&layer_id, &recording_layer_id);
IPC::ResponseBuilder rb{ctx, 6};
rb.Push(ResultSuccess);
rb.Push(layer_id);
rb.Push(recording_layer_id);
}
void ISelfController::SetHandlesRequestToDisplay(HLERequestContext& ctx) {
LOG_WARNING(Service_AM, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
void ISelfController::ApproveToDisplay(HLERequestContext& ctx) {
LOG_WARNING(Service_AM, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
void ISelfController::SetIdleTimeDetectionExtension(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto extension = rp.PopRaw<IdleTimeDetectionExtension>();
LOG_DEBUG(Service_AM, "(STUBBED) called extension={}", extension);
std::scoped_lock lk{applet->lock};
applet->idle_time_detection_extension = extension;
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
void ISelfController::GetIdleTimeDetectionExtension(HLERequestContext& ctx) {
LOG_WARNING(Service_AM, "(STUBBED) called");
std::scoped_lock lk{applet->lock};
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
rb.PushRaw<IdleTimeDetectionExtension>(applet->idle_time_detection_extension);
}
void ISelfController::ReportUserIsActive(HLERequestContext& ctx) {
LOG_WARNING(Service_AM, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
void ISelfController::SetAutoSleepDisabled(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
std::scoped_lock lk{applet->lock};
applet->auto_sleep_disabled = rp.Pop<bool>();
// On the system itself, if the previous state of is_auto_sleep_disabled
// differed from the current value passed in, it'd signify the internal
// window manager to update (and also increment some statistics like update counts)
//
// It'd also indicate this change to an idle handling context.
//
// However, given we're emulating this behavior, most of this can be ignored
// and it's sufficient to simply set the member variable for querying via
// IsAutoSleepDisabled().
LOG_DEBUG(Service_AM, "called. is_auto_sleep_disabled={}", applet->auto_sleep_disabled);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
void ISelfController::IsAutoSleepDisabled(HLERequestContext& ctx) {
LOG_DEBUG(Service_AM, "called.");
std::scoped_lock lk{applet->lock};
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
rb.Push(applet->auto_sleep_disabled);
}
void ISelfController::GetAccumulatedSuspendedTickValue(HLERequestContext& ctx) {
LOG_DEBUG(Service_AM, "called.");
std::scoped_lock lk{applet->lock};
// This command returns the total number of system ticks since ISelfController creation
// where the game was suspended. Since Yuzu doesn't implement game suspension, this command
// can just always return 0 ticks.
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(ResultSuccess);
rb.Push<u64>(applet->suspended_ticks);
}
void ISelfController::GetAccumulatedSuspendedTickChangedEvent(HLERequestContext& ctx) {
LOG_DEBUG(Service_AM, "called.");
IPC::ResponseBuilder rb{ctx, 2, 1};
rb.Push(ResultSuccess);
rb.PushCopyObjects(applet->accumulated_suspended_tick_changed_event.GetHandle());
}
void ISelfController::SetAlbumImageTakenNotificationEnabled(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
// This service call sets an internal flag whether a notification is shown when an image is
// captured. Currently we do not support capturing images via the capture button, so this can be
// stubbed for now.
const bool enabled = rp.Pop<bool>();
LOG_WARNING(Service_AM, "(STUBBED) called. enabled={}", enabled);
std::scoped_lock lk{applet->lock};
applet->album_image_taken_notification_enabled = enabled;
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
void ISelfController::SaveCurrentScreenshot(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto report_option = rp.PopEnum<Capture::AlbumReportOption>();
LOG_INFO(Service_AM, "called, report_option={}", report_option);
const auto screenshot_service =
system.ServiceManager().GetService<Service::Capture::IScreenShotApplicationService>(
"caps:su");
if (screenshot_service) {
screenshot_service->CaptureAndSaveScreenshot(report_option);
}
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
void ISelfController::SetRecordVolumeMuted(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto enabled = rp.Pop<bool>();
LOG_WARNING(Service_AM, "(STUBBED) called. enabled={}", enabled);
std::scoped_lock lk{applet->lock};
applet->record_volume_muted = enabled;
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
} // namespace Service::AM

View File

@ -0,0 +1,58 @@
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "core/hle/service/kernel_helpers.h"
#include "core/hle/service/service.h"
namespace Service::AM {
struct Applet;
class ISelfController final : public ServiceFramework<ISelfController> {
public:
explicit ISelfController(Core::System& system_, std::shared_ptr<Applet> applet_,
Nvnflinger::Nvnflinger& nvnflinger_);
~ISelfController() override;
private:
void Exit(HLERequestContext& ctx);
void LockExit(HLERequestContext& ctx);
void UnlockExit(HLERequestContext& ctx);
void EnterFatalSection(HLERequestContext& ctx);
void LeaveFatalSection(HLERequestContext& ctx);
void GetLibraryAppletLaunchableEvent(HLERequestContext& ctx);
void SetScreenShotPermission(HLERequestContext& ctx);
void SetOperationModeChangedNotification(HLERequestContext& ctx);
void SetPerformanceModeChangedNotification(HLERequestContext& ctx);
void SetFocusHandlingMode(HLERequestContext& ctx);
void SetRestartMessageEnabled(HLERequestContext& ctx);
void SetScreenShotAppletIdentityInfo(HLERequestContext& ctx);
void SetOutOfFocusSuspendingEnabled(HLERequestContext& ctx);
void SetAlbumImageOrientation(HLERequestContext& ctx);
void IsSystemBufferSharingEnabled(HLERequestContext& ctx);
void GetSystemSharedBufferHandle(HLERequestContext& ctx);
void GetSystemSharedLayerHandle(HLERequestContext& ctx);
void CreateManagedDisplayLayer(HLERequestContext& ctx);
void CreateManagedDisplaySeparableLayer(HLERequestContext& ctx);
void SetHandlesRequestToDisplay(HLERequestContext& ctx);
void ApproveToDisplay(HLERequestContext& ctx);
void SetIdleTimeDetectionExtension(HLERequestContext& ctx);
void GetIdleTimeDetectionExtension(HLERequestContext& ctx);
void ReportUserIsActive(HLERequestContext& ctx);
void SetAutoSleepDisabled(HLERequestContext& ctx);
void IsAutoSleepDisabled(HLERequestContext& ctx);
void GetAccumulatedSuspendedTickValue(HLERequestContext& ctx);
void GetAccumulatedSuspendedTickChangedEvent(HLERequestContext& ctx);
void SetAlbumImageTakenNotificationEnabled(HLERequestContext& ctx);
void SaveCurrentScreenshot(HLERequestContext& ctx);
void SetRecordVolumeMuted(HLERequestContext& ctx);
Result EnsureBufferSharingEnabled(Kernel::KProcess* process);
Nvnflinger::Nvnflinger& nvnflinger;
const std::shared_ptr<Applet> applet;
};
} // namespace Service::AM

View File

@ -0,0 +1,59 @@
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "core/hle/service/am/am_results.h"
#include "core/hle/service/am/library_applet_storage.h"
#include "core/hle/service/am/storage.h"
#include "core/hle/service/am/storage_accessor.h"
#include "core/hle/service/ipc_helpers.h"
namespace Service::AM {
IStorage::IStorage(Core::System& system_, std::shared_ptr<LibraryAppletStorage> impl_)
: ServiceFramework{system_, "IStorage"}, impl{std::move(impl_)} {
static const FunctionInfo functions[] = {
{0, &IStorage::Open, "Open"},
{1, &IStorage::OpenTransferStorage, "OpenTransferStorage"},
};
RegisterHandlers(functions);
}
IStorage::IStorage(Core::System& system_, std::vector<u8>&& data)
: IStorage(system_, CreateStorage(std::move(data))) {}
IStorage::~IStorage() = default;
void IStorage::Open(HLERequestContext& ctx) {
LOG_DEBUG(Service_AM, "called");
if (impl->GetHandle() != nullptr) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(AM::ResultInvalidStorageType);
return;
}
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess);
rb.PushIpcInterface<IStorageAccessor>(system, impl);
}
void IStorage::OpenTransferStorage(HLERequestContext& ctx) {
LOG_DEBUG(Service_AM, "called");
if (impl->GetHandle() == nullptr) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(AM::ResultInvalidStorageType);
return;
}
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess);
rb.PushIpcInterface<ITransferStorageAccessor>(system, impl);
}
std::vector<u8> IStorage::GetData() const {
return impl->GetData();
}
} // namespace Service::AM

View File

@ -0,0 +1,31 @@
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "core/hle/service/service.h"
namespace Service::AM {
class LibraryAppletStorage;
class IStorage final : public ServiceFramework<IStorage> {
public:
explicit IStorage(Core::System& system_, std::shared_ptr<LibraryAppletStorage> impl_);
explicit IStorage(Core::System& system_, std::vector<u8>&& buffer);
~IStorage() override;
std::shared_ptr<LibraryAppletStorage> GetImpl() const {
return impl;
}
std::vector<u8> GetData() const;
private:
void Open(HLERequestContext& ctx);
void OpenTransferStorage(HLERequestContext& ctx);
const std::shared_ptr<LibraryAppletStorage> impl;
};
} // namespace Service::AM

View File

@ -0,0 +1,90 @@
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "core/hle/kernel/k_transfer_memory.h"
#include "core/hle/service/am/am_results.h"
#include "core/hle/service/am/library_applet_storage.h"
#include "core/hle/service/am/storage_accessor.h"
#include "core/hle/service/ipc_helpers.h"
namespace Service::AM {
IStorageAccessor::IStorageAccessor(Core::System& system_,
std::shared_ptr<LibraryAppletStorage> impl_)
: ServiceFramework{system_, "IStorageAccessor"}, impl{std::move(impl_)} {
static const FunctionInfo functions[] = {
{0, &IStorageAccessor::GetSize, "GetSize"},
{10, &IStorageAccessor::Write, "Write"},
{11, &IStorageAccessor::Read, "Read"},
};
RegisterHandlers(functions);
}
IStorageAccessor::~IStorageAccessor() = default;
void IStorageAccessor::GetSize(HLERequestContext& ctx) {
LOG_DEBUG(Service_AM, "called");
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(ResultSuccess);
rb.Push(impl->GetSize());
}
void IStorageAccessor::Write(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const s64 offset{rp.Pop<s64>()};
const auto data{ctx.ReadBuffer()};
LOG_DEBUG(Service_AM, "called, offset={}, size={}", offset, data.size());
const auto res{impl->Write(offset, data.data(), data.size())};
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(res);
}
void IStorageAccessor::Read(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const s64 offset{rp.Pop<s64>()};
std::vector<u8> data(ctx.GetWriteBufferSize());
LOG_DEBUG(Service_AM, "called, offset={}, size={}", offset, data.size());
const auto res{impl->Read(offset, data.data(), data.size())};
ctx.WriteBuffer(data);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(res);
}
ITransferStorageAccessor::ITransferStorageAccessor(Core::System& system_,
std::shared_ptr<LibraryAppletStorage> impl_)
: ServiceFramework{system_, "ITransferStorageAccessor"}, impl{std::move(impl_)} {
static const FunctionInfo functions[] = {
{0, &ITransferStorageAccessor::GetSize, "GetSize"},
{1, &ITransferStorageAccessor::GetHandle, "GetHandle"},
};
RegisterHandlers(functions);
}
ITransferStorageAccessor::~ITransferStorageAccessor() = default;
void ITransferStorageAccessor::GetSize(HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(ResultSuccess);
rb.Push(impl->GetSize());
}
void ITransferStorageAccessor::GetHandle(HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 4, 1};
rb.Push(ResultSuccess);
rb.Push(impl->GetSize());
rb.PushCopyObjects(impl->GetHandle());
}
} // namespace Service::AM

View File

@ -0,0 +1,37 @@
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "core/hle/service/am/storage.h"
#include "core/hle/service/service.h"
namespace Service::AM {
class IStorageAccessor final : public ServiceFramework<IStorageAccessor> {
public:
explicit IStorageAccessor(Core::System& system_, std::shared_ptr<LibraryAppletStorage> impl_);
~IStorageAccessor() override;
private:
void GetSize(HLERequestContext& ctx);
void Write(HLERequestContext& ctx);
void Read(HLERequestContext& ctx);
const std::shared_ptr<LibraryAppletStorage> impl;
};
class ITransferStorageAccessor final : public ServiceFramework<ITransferStorageAccessor> {
public:
explicit ITransferStorageAccessor(Core::System& system_,
std::shared_ptr<LibraryAppletStorage> impl_);
~ITransferStorageAccessor() override;
private:
void GetSize(HLERequestContext& ctx);
void GetHandle(HLERequestContext& ctx);
const std::shared_ptr<LibraryAppletStorage> impl;
};
} // namespace Service::AM

Some files were not shown because too many files have changed in this diff Show More