am: add new datatypes for per-applet state
This commit is contained in:
		| @@ -393,8 +393,13 @@ add_library(core STATIC | ||||
|     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_manager.h | ||||
|     hle/service/am/applet_oe.cpp | ||||
|     hle/service/am/applet_oe.h | ||||
|     hle/service/am/applets/applet_cabinet.cpp | ||||
| @@ -438,6 +443,8 @@ add_library(core STATIC | ||||
|     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 | ||||
| @@ -450,16 +457,24 @@ add_library(core STATIC | ||||
|     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 | ||||
| @@ -531,6 +546,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 | ||||
|   | ||||
							
								
								
									
										171
									
								
								src/core/hle/service/am/am_types.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										171
									
								
								src/core/hle/service/am/am_types.h
									
									
									
									
									
										Normal file
									
								
							| @@ -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 | ||||
							
								
								
									
										63
									
								
								src/core/hle/service/am/applet.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										63
									
								
								src/core/hle/service/am/applet.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -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 | ||||
							
								
								
									
										164
									
								
								src/core/hle/service/am/applet.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										164
									
								
								src/core/hle/service/am/applet.h
									
									
									
									
									
										Normal file
									
								
							| @@ -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 | ||||
							
								
								
									
										352
									
								
								src/core/hle/service/am/applet_manager.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										352
									
								
								src/core/hle/service/am/applet_manager.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -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 | ||||
							
								
								
									
										59
									
								
								src/core/hle/service/am/applet_manager.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										59
									
								
								src/core/hle/service/am/applet_manager.h
									
									
									
									
									
										Normal 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 | ||||
							
								
								
									
										29
									
								
								src/core/hle/service/am/hid_registration.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								src/core/hle/service/am/hid_registration.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -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 | ||||
							
								
								
									
										30
									
								
								src/core/hle/service/am/hid_registration.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								src/core/hle/service/am/hid_registration.h
									
									
									
									
									
										Normal file
									
								
							| @@ -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 | ||||
							
								
								
									
										140
									
								
								src/core/hle/service/am/library_applet_storage.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										140
									
								
								src/core/hle/service/am/library_applet_storage.cpp
									
									
									
									
									
										Normal 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 | ||||
							
								
								
									
										36
									
								
								src/core/hle/service/am/library_applet_storage.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										36
									
								
								src/core/hle/service/am/library_applet_storage.h
									
									
									
									
									
										Normal 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 | ||||
							
								
								
									
										59
									
								
								src/core/hle/service/am/managed_layer_holder.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										59
									
								
								src/core/hle/service/am/managed_layer_holder.cpp
									
									
									
									
									
										Normal 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 | ||||
							
								
								
									
										32
									
								
								src/core/hle/service/am/managed_layer_holder.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								src/core/hle/service/am/managed_layer_holder.h
									
									
									
									
									
										Normal 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 | ||||
							
								
								
									
										138
									
								
								src/core/hle/service/am/process.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										138
									
								
								src/core/hle/service/am/process.cpp
									
									
									
									
									
										Normal 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 | ||||
							
								
								
									
										50
									
								
								src/core/hle/service/am/process.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										50
									
								
								src/core/hle/service/am/process.h
									
									
									
									
									
										Normal 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 | ||||
							
								
								
									
										49
									
								
								src/core/hle/service/am/system_buffer_manager.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										49
									
								
								src/core/hle/service/am/system_buffer_manager.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -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 | ||||
							
								
								
									
										44
									
								
								src/core/hle/service/am/system_buffer_manager.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										44
									
								
								src/core/hle/service/am/system_buffer_manager.h
									
									
									
									
									
										Normal file
									
								
							| @@ -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 | ||||
							
								
								
									
										31
									
								
								src/core/hle/service/event.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								src/core/hle/service/event.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -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 | ||||
							
								
								
									
										31
									
								
								src/core/hle/service/event.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								src/core/hle/service/event.h
									
									
									
									
									
										Normal file
									
								
							| @@ -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 | ||||
| @@ -128,7 +128,7 @@ Result AllocateHandleForBuffer(u32* out_handle, Nvidia::Module& nvdrv, Nvidia::D | ||||
|  | ||||
|     // Ensure we maintain a clean state on failure. | ||||
|     ON_RESULT_FAILURE { | ||||
|         ASSERT(R_SUCCEEDED(FreeNvMapHandle(*nvmap, *out_handle, nvmap_fd))); | ||||
|         R_ASSERT(FreeNvMapHandle(*nvmap, *out_handle, nvmap_fd)); | ||||
|     }; | ||||
|  | ||||
|     // Assign the allocated memory to the handle. | ||||
|   | ||||
		Reference in New Issue
	
	Block a user