19 changed files with 1496 additions and 1 deletions
-
17src/core/CMakeLists.txt
-
171src/core/hle/service/am/am_types.h
-
63src/core/hle/service/am/applet.cpp
-
164src/core/hle/service/am/applet.h
-
352src/core/hle/service/am/applet_manager.cpp
-
59src/core/hle/service/am/applet_manager.h
-
29src/core/hle/service/am/hid_registration.cpp
-
30src/core/hle/service/am/hid_registration.h
-
140src/core/hle/service/am/library_applet_storage.cpp
-
36src/core/hle/service/am/library_applet_storage.h
-
59src/core/hle/service/am/managed_layer_holder.cpp
-
32src/core/hle/service/am/managed_layer_holder.h
-
138src/core/hle/service/am/process.cpp
-
50src/core/hle/service/am/process.h
-
49src/core/hle/service/am/system_buffer_manager.cpp
-
44src/core/hle/service/am/system_buffer_manager.h
-
31src/core/hle/service/event.cpp
-
31src/core/hle/service/event.h
-
2src/core/hle/service/nvnflinger/fb_share_buffer_manager.cpp
@ -0,0 +1,171 @@ |
|||||
|
// 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."); |
||||
|
|
||||
|
using AppletResourceUserId = u64; |
||||
|
using ProgramId = u64; |
||||
|
|
||||
|
struct Applet; |
||||
|
struct AppletStorageHolder; |
||||
|
|
||||
|
} // namespace Service::AM |
||||
@ -0,0 +1,63 @@ |
|||||
|
// 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.h"
|
||||
|
|
||||
|
namespace Service::AM { |
||||
|
|
||||
|
AppletStorageChannel::AppletStorageChannel(KernelHelpers::ServiceContext& context) |
||||
|
: m_event(context) {} |
||||
|
AppletStorageChannel::~AppletStorageChannel() = default; |
||||
|
|
||||
|
void AppletStorageChannel::PushData(std::shared_ptr<IStorage> storage) { |
||||
|
std::scoped_lock lk{m_lock}; |
||||
|
|
||||
|
m_data.emplace_back(std::move(storage)); |
||||
|
m_event.Signal(); |
||||
|
} |
||||
|
|
||||
|
Result AppletStorageChannel::PopData(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(); |
||||
|
} |
||||
|
|
||||
|
AppletStorageHolder::AppletStorageHolder(Core::System& system) |
||||
|
: context(system, "AppletStorageHolder"), in_data(context), interactive_in_data(context), |
||||
|
out_data(context), interactive_out_data(context), state_changed_event(context) {} |
||||
|
|
||||
|
AppletStorageHolder::~AppletStorageHolder() = default; |
||||
|
|
||||
|
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
|
||||
@ -0,0 +1,164 @@ |
|||||
|
// 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::Nvnflinger { |
||||
|
class FbShareBufferManager; |
||||
|
class Nvnflinger; |
||||
|
} // namespace Service::Nvnflinger |
||||
|
|
||||
|
namespace Service::AM { |
||||
|
|
||||
|
class AppletStorageChannel { |
||||
|
public: |
||||
|
explicit AppletStorageChannel(KernelHelpers::ServiceContext& ctx); |
||||
|
~AppletStorageChannel(); |
||||
|
|
||||
|
void PushData(std::shared_ptr<IStorage> storage); |
||||
|
Result PopData(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; |
||||
|
}; |
||||
|
|
||||
|
struct AppletStorageHolder { |
||||
|
explicit AppletStorageHolder(Core::System& system); |
||||
|
~AppletStorageHolder(); |
||||
|
|
||||
|
KernelHelpers::ServiceContext context; |
||||
|
|
||||
|
AppletStorageChannel in_data; |
||||
|
AppletStorageChannel interactive_in_data; |
||||
|
AppletStorageChannel out_data; |
||||
|
AppletStorageChannel interactive_out_data; |
||||
|
Event state_changed_event; |
||||
|
}; |
||||
|
|
||||
|
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}; |
||||
|
|
||||
|
// 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<AppletStorageHolder> caller_applet_storage{}; |
||||
|
bool is_completed{}; |
||||
|
|
||||
|
// 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 |
||||
@ -0,0 +1,352 @@ |
|||||
|
// 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_manager.h"
|
||||
|
#include "core/hle/service/am/applets/applet_cabinet.h"
|
||||
|
#include "core/hle/service/am/applets/applet_controller.h"
|
||||
|
#include "core/hle/service/am/applets/applet_mii_edit_types.h"
|
||||
|
#include "core/hle/service/am/applets/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_storage = std::make_shared<AppletStorageHolder>(system); |
||||
|
return applet->caller_applet_storage->in_data; |
||||
|
} |
||||
|
|
||||
|
void PushInShowAlbum(Core::System& system, AppletStorageChannel& channel) { |
||||
|
const CommonArguments arguments{ |
||||
|
.arguments_version = CommonArgumentVersion::Version3, |
||||
|
.size = CommonArgumentSize::Version3, |
||||
|
.library_version = 1, |
||||
|
.theme_color = ThemeColor::BasicBlack, |
||||
|
.play_startup_sound = true, |
||||
|
.system_tick = system.CoreTiming().GetClockTicks(), |
||||
|
}; |
||||
|
|
||||
|
std::vector<u8> argument_data(sizeof(arguments)); |
||||
|
std::vector<u8> settings_data{2}; |
||||
|
std::memcpy(argument_data.data(), &arguments, sizeof(arguments)); |
||||
|
channel.PushData(std::make_shared<IStorage>(system, std::move(argument_data))); |
||||
|
channel.PushData(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>(Applets::ControllerAppletVersion::Version8), |
||||
|
.theme_color = ThemeColor::BasicBlack, |
||||
|
.play_startup_sound = true, |
||||
|
.system_tick = system.CoreTiming().GetClockTicks(), |
||||
|
}; |
||||
|
|
||||
|
Applets::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 = {}, |
||||
|
}; |
||||
|
|
||||
|
Applets::ControllerSupportArgPrivate private_args = { |
||||
|
.arg_private_size = sizeof(Applets::ControllerSupportArgPrivate), |
||||
|
.arg_size = sizeof(Applets::ControllerSupportArgNew), |
||||
|
.is_home_menu = true, |
||||
|
.flag_1 = true, |
||||
|
.mode = Applets::ControllerSupportMode::ShowControllerSupport, |
||||
|
.caller = Applets::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.PushData(std::make_shared<IStorage>(system, std::move(common_args_data))); |
||||
|
channel.PushData(std::make_shared<IStorage>(system, std::move(private_args_data))); |
||||
|
channel.PushData(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>(Applets::CabinetAppletVersion::Version1), |
||||
|
.theme_color = ThemeColor::BasicBlack, |
||||
|
.play_startup_sound = true, |
||||
|
.system_tick = system.CoreTiming().GetClockTicks(), |
||||
|
}; |
||||
|
|
||||
|
const Applets::StartParamForAmiiboSettings amiibo_settings{ |
||||
|
.param_1 = 0, |
||||
|
.applet_mode = system.GetAppletManager().GetCabinetMode(), |
||||
|
.flags = Applets::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.PushData(std::make_shared<IStorage>(system, std::move(argument_data))); |
||||
|
channel.PushData(std::make_shared<IStorage>(system, std::move(settings_data))); |
||||
|
} |
||||
|
|
||||
|
void PushInShowMiiEditData(Core::System& system, AppletStorageChannel& channel) { |
||||
|
struct MiiEditV3 { |
||||
|
Applets::MiiEditAppletInputCommon common; |
||||
|
Applets::MiiEditAppletInputV3 input; |
||||
|
}; |
||||
|
static_assert(sizeof(MiiEditV3) == 0x100, "MiiEditV3 has incorrect size."); |
||||
|
|
||||
|
MiiEditV3 mii_arguments{ |
||||
|
.common = |
||||
|
{ |
||||
|
.version = Applets::MiiEditAppletVersion::Version3, |
||||
|
.applet_mode = Applets::MiiEditAppletMode::ShowMiiEdit, |
||||
|
}, |
||||
|
.input{}, |
||||
|
}; |
||||
|
|
||||
|
std::vector<u8> argument_data(sizeof(mii_arguments)); |
||||
|
std::memcpy(argument_data.data(), &mii_arguments, sizeof(mii_arguments)); |
||||
|
|
||||
|
channel.PushData(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>(Applets::SwkbdAppletVersion::Version524301), |
||||
|
.theme_color = ThemeColor::BasicBlack, |
||||
|
.play_startup_sound = true, |
||||
|
.system_tick = system.CoreTiming().GetClockTicks(), |
||||
|
}; |
||||
|
|
||||
|
std::vector<char16_t> initial_string(0); |
||||
|
|
||||
|
const Applets::SwkbdConfigCommon swkbd_config{ |
||||
|
.type = Applets::SwkbdType::Qwerty, |
||||
|
.ok_text{}, |
||||
|
.left_optional_symbol_key{}, |
||||
|
.right_optional_symbol_key{}, |
||||
|
.use_prediction = false, |
||||
|
.key_disable_flags{}, |
||||
|
.initial_cursor_position = Applets::SwkbdInitialCursorPosition::Start, |
||||
|
.header_text{}, |
||||
|
.sub_text{}, |
||||
|
.guide_text{}, |
||||
|
.max_text_length = 500, |
||||
|
.min_text_length = 0, |
||||
|
.password_mode = Applets::SwkbdPasswordMode::Disabled, |
||||
|
.text_draw_type = Applets::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, |
||||
|
}; |
||||
|
|
||||
|
Applets::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(Applets::SwkbdConfigNew)); |
||||
|
std::memcpy(work_buffer.data(), initial_string.data(), |
||||
|
swkbd_config.initial_string_length * sizeof(char16_t)); |
||||
|
|
||||
|
channel.PushData(std::make_shared<IStorage>(system, std::move(argument_data))); |
||||
|
channel.PushData(std::make_shared<IStorage>(system, std::move(swkbd_data))); |
||||
|
channel.PushData(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
|
||||
@ -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 |
||||
@ -0,0 +1,29 @@ |
|||||
|
// 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()); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
} // namespace Service::AM
|
||||
@ -0,0 +1,30 @@ |
|||||
|
// 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(); |
||||
|
|
||||
|
private: |
||||
|
Process& m_process; |
||||
|
std::shared_ptr<Service::HID::IHidServer> m_hid_server; |
||||
|
}; |
||||
|
|
||||
|
} // namespace Service::AM |
||||
@ -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
|
||||
@ -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 |
||||
@ -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
|
||||
@ -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 |
||||
@ -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
|
||||
@ -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 |
||||
@ -0,0 +1,49 @@ |
|||||
|
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
|
||||
|
#include "core/hle/service/am/system_buffer_manager.h"
|
||||
|
#include "core/hle/service/nvnflinger/fb_share_buffer_manager.h"
|
||||
|
#include "core/hle/service/nvnflinger/nvnflinger.h"
|
||||
|
|
||||
|
namespace Service::AM { |
||||
|
|
||||
|
SystemBufferManager::SystemBufferManager() = default; |
||||
|
|
||||
|
SystemBufferManager::~SystemBufferManager() { |
||||
|
if (!m_nvnflinger) { |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
// Clean up shared layers.
|
||||
|
if (m_buffer_sharing_enabled) { |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
bool SystemBufferManager::Initialize(Nvnflinger::Nvnflinger* nvnflinger, Kernel::KProcess* process, |
||||
|
AppletId applet_id) { |
||||
|
if (m_nvnflinger) { |
||||
|
return m_buffer_sharing_enabled; |
||||
|
} |
||||
|
|
||||
|
m_process = process; |
||||
|
m_nvnflinger = nvnflinger; |
||||
|
m_buffer_sharing_enabled = false; |
||||
|
m_system_shared_buffer_id = 0; |
||||
|
m_system_shared_layer_id = 0; |
||||
|
|
||||
|
if (applet_id <= AppletId::Application) { |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
const auto display_id = m_nvnflinger->OpenDisplay("Default").value(); |
||||
|
const auto res = m_nvnflinger->GetSystemBufferManager().Initialize( |
||||
|
&m_system_shared_buffer_id, &m_system_shared_layer_id, display_id); |
||||
|
|
||||
|
if (res.IsSuccess()) { |
||||
|
m_buffer_sharing_enabled = true; |
||||
|
} |
||||
|
|
||||
|
return m_buffer_sharing_enabled; |
||||
|
} |
||||
|
|
||||
|
} // namespace Service::AM
|
||||
@ -0,0 +1,44 @@ |
|||||
|
// 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" |
||||
|
|
||||
|
#include "core/hle/service/am/am_types.h" |
||||
|
|
||||
|
namespace Kernel { |
||||
|
class KProcess; |
||||
|
} |
||||
|
|
||||
|
namespace Service::Nvnflinger { |
||||
|
class Nvnflinger; |
||||
|
} |
||||
|
|
||||
|
namespace Service::AM { |
||||
|
|
||||
|
class SystemBufferManager { |
||||
|
public: |
||||
|
SystemBufferManager(); |
||||
|
~SystemBufferManager(); |
||||
|
|
||||
|
bool Initialize(Nvnflinger::Nvnflinger* flinger, Kernel::KProcess* process, AppletId applet_id); |
||||
|
|
||||
|
void GetSystemSharedLayerHandle(u64* out_system_shared_buffer_id, |
||||
|
u64* out_system_shared_layer_id) { |
||||
|
*out_system_shared_buffer_id = m_system_shared_buffer_id; |
||||
|
*out_system_shared_layer_id = m_system_shared_layer_id; |
||||
|
} |
||||
|
|
||||
|
private: |
||||
|
Kernel::KProcess* m_process{}; |
||||
|
Nvnflinger::Nvnflinger* m_nvnflinger{}; |
||||
|
bool m_buffer_sharing_enabled{}; |
||||
|
u64 m_system_shared_buffer_id{}; |
||||
|
u64 m_system_shared_layer_id{}; |
||||
|
}; |
||||
|
|
||||
|
} // namespace Service::AM |
||||
@ -0,0 +1,31 @@ |
|||||
|
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
|
||||
|
#include "core/hle/kernel/k_event.h"
|
||||
|
#include "core/hle/service/event.h"
|
||||
|
#include "core/hle/service/kernel_helpers.h"
|
||||
|
|
||||
|
namespace Service { |
||||
|
|
||||
|
Event::Event(KernelHelpers::ServiceContext& ctx) { |
||||
|
m_event = ctx.CreateEvent("Event"); |
||||
|
} |
||||
|
|
||||
|
Event::~Event() { |
||||
|
m_event->GetReadableEvent().Close(); |
||||
|
m_event->Close(); |
||||
|
} |
||||
|
|
||||
|
void Event::Signal() { |
||||
|
m_event->Signal(); |
||||
|
} |
||||
|
|
||||
|
void Event::Clear() { |
||||
|
m_event->Clear(); |
||||
|
} |
||||
|
|
||||
|
Kernel::KReadableEvent* Event::GetHandle() { |
||||
|
return &m_event->GetReadableEvent(); |
||||
|
} |
||||
|
|
||||
|
} // namespace Service
|
||||
@ -0,0 +1,31 @@ |
|||||
|
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project |
||||
|
// SPDX-License-Identifier: GPL-2.0-or-later |
||||
|
|
||||
|
#pragma once |
||||
|
|
||||
|
namespace Kernel { |
||||
|
class KEvent; |
||||
|
class KReadableEvent; |
||||
|
} // namespace Kernel |
||||
|
|
||||
|
namespace Service { |
||||
|
|
||||
|
namespace KernelHelpers { |
||||
|
class ServiceContext; |
||||
|
} |
||||
|
|
||||
|
class Event { |
||||
|
public: |
||||
|
explicit Event(KernelHelpers::ServiceContext& ctx); |
||||
|
~Event(); |
||||
|
|
||||
|
void Signal(); |
||||
|
void Clear(); |
||||
|
|
||||
|
Kernel::KReadableEvent* GetHandle(); |
||||
|
|
||||
|
private: |
||||
|
Kernel::KEvent* m_event; |
||||
|
}; |
||||
|
|
||||
|
} // namespace Service |
||||
Write
Preview
Loading…
Cancel
Save
Reference in new issue