From 025481aeed48b62abbc3afd032c624da1204904a Mon Sep 17 00:00:00 2001 From: pineappleEA Date: Fri, 26 Jan 2024 18:45:02 +0100 Subject: [PATCH] early-access version 4091 --- README.md | 2 +- .../fragments/HomeSettingsFragment.kt | 8 +- .../main/jni/applets/software_keyboard.cpp | 12 +- .../src/main/jni/applets/software_keyboard.h | 4 +- src/android/app/src/main/jni/native.cpp | 18 +- src/core/CMakeLists.txt | 110 +- src/core/core.cpp | 72 +- src/core/core.h | 25 +- src/core/file_sys/vfs/vfs_real.cpp | 9 + src/core/frontend/applets/general.cpp | 59 + src/core/frontend/applets/general.h | 63 + src/core/frontend/applets/profile_select.h | 8 +- .../frontend/applets/software_keyboard.cpp | 12 +- src/core/frontend/applets/software_keyboard.h | 22 +- src/core/frontend/applets/web_browser.cpp | 4 +- src/core/frontend/applets/web_browser.h | 4 +- src/core/hle/kernel/kernel.cpp | 30 +- src/core/hle/service/am/am.cpp | 2685 +---------------- src/core/hle/service/am/am.h | 454 +-- src/core/hle/service/am/am_results.h | 16 + src/core/hle/service/am/am_types.h | 178 ++ src/core/hle/service/am/applet.cpp | 27 + src/core/hle/service/am/applet.h | 133 + src/core/hle/service/am/applet_ae.cpp | 326 +- src/core/hle/service/am/applet_ae.h | 10 +- .../service/am/applet_common_functions.cpp | 63 + .../hle/service/am/applet_common_functions.h | 24 + .../hle/service/am/applet_data_broker.cpp | 67 + src/core/hle/service/am/applet_data_broker.h | 80 + src/core/hle/service/am/applet_manager.cpp | 353 +++ src/core/hle/service/am/applet_manager.h | 59 + .../hle/service/am/applet_message_queue.cpp | 73 + .../hle/service/am/applet_message_queue.h | 76 + src/core/hle/service/am/applet_oe.cpp | 131 +- src/core/hle/service/am/applet_oe.h | 10 +- .../hle/service/am/application_creator.cpp | 25 + src/core/hle/service/am/application_creator.h | 16 + .../hle/service/am/application_functions.cpp | 594 ++++ .../hle/service/am/application_functions.h | 58 + src/core/hle/service/am/application_proxy.cpp | 115 + src/core/hle/service/am/application_proxy.h | 33 + src/core/hle/service/am/audio_controller.cpp | 91 + src/core/hle/service/am/audio_controller.h | 36 + .../hle/service/am/common_state_getter.cpp | 314 ++ src/core/hle/service/am/common_state_getter.h | 77 + src/core/hle/service/am/debug_functions.cpp | 44 + src/core/hle/service/am/debug_functions.h | 16 + .../hle/service/am/display_controller.cpp | 139 + src/core/hle/service/am/display_controller.h | 30 + .../service/am/frontend/applet_cabinet.cpp | 184 ++ .../hle/service/am/frontend/applet_cabinet.h | 113 + .../service/am/frontend/applet_controller.cpp | 271 ++ .../service/am/frontend/applet_controller.h | 156 + .../hle/service/am/frontend/applet_error.cpp | 219 ++ .../hle/service/am/frontend/applet_error.h | 51 + .../service/am/frontend/applet_general.cpp | 250 ++ .../hle/service/am/frontend/applet_general.h | 89 + .../service/am/frontend/applet_mii_edit.cpp | 177 ++ .../hle/service/am/frontend/applet_mii_edit.h | 51 + .../am/frontend/applet_mii_edit_types.h | 83 + .../am/frontend/applet_profile_select.cpp | 123 + .../am/frontend/applet_profile_select.h | 142 + .../am/frontend/applet_software_keyboard.cpp | 1274 ++++++++ .../am/frontend/applet_software_keyboard.h | 186 ++ .../frontend/applet_software_keyboard_types.h | 354 +++ .../am/frontend/applet_web_browser.cpp | 505 ++++ .../service/am/frontend/applet_web_browser.h | 84 + .../am/frontend/applet_web_browser_types.h | 177 ++ src/core/hle/service/am/frontend/applets.cpp | 240 ++ src/core/hle/service/am/frontend/applets.h | 146 + .../service/am/global_state_controller.cpp | 34 + .../hle/service/am/global_state_controller.h | 16 + src/core/hle/service/am/hid_registration.cpp | 35 + src/core/hle/service/am/hid_registration.h | 32 + .../hle/service/am/home_menu_functions.cpp | 57 + src/core/hle/service/am/home_menu_functions.h | 25 + .../service/am/library_applet_accessor.cpp | 202 ++ .../hle/service/am/library_applet_accessor.h | 43 + .../hle/service/am/library_applet_creator.cpp | 274 ++ .../hle/service/am/library_applet_creator.h | 26 + .../hle/service/am/library_applet_proxy.cpp | 143 + .../hle/service/am/library_applet_proxy.h | 36 + .../am/library_applet_self_accessor.cpp | 338 +++ .../service/am/library_applet_self_accessor.h | 44 + .../hle/service/am/library_applet_storage.cpp | 140 + .../hle/service/am/library_applet_storage.h | 36 + src/core/hle/service/am/lock_accessor.cpp | 71 + src/core/hle/service/am/lock_accessor.h | 28 + .../hle/service/am/managed_layer_holder.cpp | 59 + .../hle/service/am/managed_layer_holder.h | 32 + src/core/hle/service/am/process.cpp | 138 + src/core/hle/service/am/process.h | 50 + .../service/am/process_winding_controller.cpp | 56 + .../service/am/process_winding_controller.h | 24 + src/core/hle/service/am/self_controller.cpp | 456 +++ src/core/hle/service/am/self_controller.h | 58 + src/core/hle/service/am/storage.cpp | 59 + src/core/hle/service/am/storage.h | 31 + src/core/hle/service/am/storage_accessor.cpp | 90 + src/core/hle/service/am/storage_accessor.h | 37 + .../hle/service/am/system_applet_proxy.cpp | 136 + src/core/hle/service/am/system_applet_proxy.h | 36 + .../hle/service/am/system_buffer_manager.cpp | 69 + .../hle/service/am/system_buffer_manager.h | 51 + src/core/hle/service/am/window_controller.cpp | 86 + src/core/hle/service/am/window_controller.h | 27 + src/core/hle/service/btm/btm.cpp | 9 +- src/core/hle/service/event.cpp | 31 + src/core/hle/service/event.h | 31 + .../hle/service/filesystem/fsp/fsp_srv.cpp | 48 +- src/core/hle/service/hid/hid.cpp | 2 +- src/core/hle/service/hid/hid_server.cpp | 4 +- .../hle/service/hid/hid_system_server.cpp | 54 +- src/core/hle/service/hid/hid_system_server.h | 9 +- src/core/hle/service/nifm/nifm.cpp | 12 +- src/core/hle/service/ns/ns.cpp | 67 +- src/core/hle/service/ns/ns.h | 15 + .../nvnflinger/fb_share_buffer_manager.cpp | 2 +- .../hle/service/nvnflinger/nvnflinger.cpp | 10 + src/core/hle/service/nvnflinger/nvnflinger.h | 3 + .../set/setting_formats/system_settings.cpp | 1 + src/core/hle/service/set/settings_types.h | 9 + .../service/set/system_settings_server.cpp | 175 +- .../hle/service/set/system_settings_server.h | 36 +- .../hle/service/vi/display/vi_display.cpp | 4 +- src/core/hle/service/vi/layer/vi_layer.cpp | 2 +- src/core/hle/service/vi/layer/vi_layer.h | 9 + src/core/hle/service/vi/vi.cpp | 8 + src/hid_core/resource_manager.cpp | 12 +- .../resources/hid_firmware_settings.cpp | 12 + .../resources/hid_firmware_settings.h | 3 + src/hid_core/resources/npad/npad.cpp | 5 +- src/hid_core/resources/npad/npad.h | 7 +- .../resources/npad/npad_vibration.cpp | 36 +- src/hid_core/resources/npad/npad_vibration.h | 7 + .../resources/vibration/vibration_base.cpp | 4 + .../resources/vibration/vibration_base.h | 1 + src/yuzu/applets/qt_profile_select.cpp | 4 +- src/yuzu/applets/qt_software_keyboard.cpp | 10 +- src/yuzu/applets/qt_software_keyboard.h | 17 +- src/yuzu/applets/qt_web_browser.cpp | 12 +- src/yuzu/applets/qt_web_browser.h | 10 +- src/yuzu/configuration/configure_input.cpp | 18 +- src/yuzu/game_list.cpp | 12 +- src/yuzu/game_list.h | 3 +- src/yuzu/main.cpp | 168 +- src/yuzu/main.h | 38 +- src/yuzu_cmd/yuzu.cpp | 6 +- 148 files changed, 11777 insertions(+), 3914 deletions(-) create mode 100755 src/core/frontend/applets/general.cpp create mode 100755 src/core/frontend/applets/general.h create mode 100755 src/core/hle/service/am/am_results.h create mode 100755 src/core/hle/service/am/am_types.h create mode 100755 src/core/hle/service/am/applet.cpp create mode 100755 src/core/hle/service/am/applet.h create mode 100755 src/core/hle/service/am/applet_common_functions.cpp create mode 100755 src/core/hle/service/am/applet_common_functions.h create mode 100755 src/core/hle/service/am/applet_data_broker.cpp create mode 100755 src/core/hle/service/am/applet_data_broker.h create mode 100755 src/core/hle/service/am/applet_manager.cpp create mode 100755 src/core/hle/service/am/applet_manager.h create mode 100755 src/core/hle/service/am/applet_message_queue.cpp create mode 100755 src/core/hle/service/am/applet_message_queue.h create mode 100755 src/core/hle/service/am/application_creator.cpp create mode 100755 src/core/hle/service/am/application_creator.h create mode 100755 src/core/hle/service/am/application_functions.cpp create mode 100755 src/core/hle/service/am/application_functions.h create mode 100755 src/core/hle/service/am/application_proxy.cpp create mode 100755 src/core/hle/service/am/application_proxy.h create mode 100755 src/core/hle/service/am/audio_controller.cpp create mode 100755 src/core/hle/service/am/audio_controller.h create mode 100755 src/core/hle/service/am/common_state_getter.cpp create mode 100755 src/core/hle/service/am/common_state_getter.h create mode 100755 src/core/hle/service/am/debug_functions.cpp create mode 100755 src/core/hle/service/am/debug_functions.h create mode 100755 src/core/hle/service/am/display_controller.cpp create mode 100755 src/core/hle/service/am/display_controller.h create mode 100755 src/core/hle/service/am/frontend/applet_cabinet.cpp create mode 100755 src/core/hle/service/am/frontend/applet_cabinet.h create mode 100755 src/core/hle/service/am/frontend/applet_controller.cpp create mode 100755 src/core/hle/service/am/frontend/applet_controller.h create mode 100755 src/core/hle/service/am/frontend/applet_error.cpp create mode 100755 src/core/hle/service/am/frontend/applet_error.h create mode 100755 src/core/hle/service/am/frontend/applet_general.cpp create mode 100755 src/core/hle/service/am/frontend/applet_general.h create mode 100755 src/core/hle/service/am/frontend/applet_mii_edit.cpp create mode 100755 src/core/hle/service/am/frontend/applet_mii_edit.h create mode 100755 src/core/hle/service/am/frontend/applet_mii_edit_types.h create mode 100755 src/core/hle/service/am/frontend/applet_profile_select.cpp create mode 100755 src/core/hle/service/am/frontend/applet_profile_select.h create mode 100755 src/core/hle/service/am/frontend/applet_software_keyboard.cpp create mode 100755 src/core/hle/service/am/frontend/applet_software_keyboard.h create mode 100755 src/core/hle/service/am/frontend/applet_software_keyboard_types.h create mode 100755 src/core/hle/service/am/frontend/applet_web_browser.cpp create mode 100755 src/core/hle/service/am/frontend/applet_web_browser.h create mode 100755 src/core/hle/service/am/frontend/applet_web_browser_types.h create mode 100755 src/core/hle/service/am/frontend/applets.cpp create mode 100755 src/core/hle/service/am/frontend/applets.h create mode 100755 src/core/hle/service/am/global_state_controller.cpp create mode 100755 src/core/hle/service/am/global_state_controller.h create mode 100755 src/core/hle/service/am/hid_registration.cpp create mode 100755 src/core/hle/service/am/hid_registration.h create mode 100755 src/core/hle/service/am/home_menu_functions.cpp create mode 100755 src/core/hle/service/am/home_menu_functions.h create mode 100755 src/core/hle/service/am/library_applet_accessor.cpp create mode 100755 src/core/hle/service/am/library_applet_accessor.h create mode 100755 src/core/hle/service/am/library_applet_creator.cpp create mode 100755 src/core/hle/service/am/library_applet_creator.h create mode 100755 src/core/hle/service/am/library_applet_proxy.cpp create mode 100755 src/core/hle/service/am/library_applet_proxy.h create mode 100755 src/core/hle/service/am/library_applet_self_accessor.cpp create mode 100755 src/core/hle/service/am/library_applet_self_accessor.h create mode 100755 src/core/hle/service/am/library_applet_storage.cpp create mode 100755 src/core/hle/service/am/library_applet_storage.h create mode 100755 src/core/hle/service/am/lock_accessor.cpp create mode 100755 src/core/hle/service/am/lock_accessor.h create mode 100755 src/core/hle/service/am/managed_layer_holder.cpp create mode 100755 src/core/hle/service/am/managed_layer_holder.h create mode 100755 src/core/hle/service/am/process.cpp create mode 100755 src/core/hle/service/am/process.h create mode 100755 src/core/hle/service/am/process_winding_controller.cpp create mode 100755 src/core/hle/service/am/process_winding_controller.h create mode 100755 src/core/hle/service/am/self_controller.cpp create mode 100755 src/core/hle/service/am/self_controller.h create mode 100755 src/core/hle/service/am/storage.cpp create mode 100755 src/core/hle/service/am/storage.h create mode 100755 src/core/hle/service/am/storage_accessor.cpp create mode 100755 src/core/hle/service/am/storage_accessor.h create mode 100755 src/core/hle/service/am/system_applet_proxy.cpp create mode 100755 src/core/hle/service/am/system_applet_proxy.h create mode 100755 src/core/hle/service/am/system_buffer_manager.cpp create mode 100755 src/core/hle/service/am/system_buffer_manager.h create mode 100755 src/core/hle/service/am/window_controller.cpp create mode 100755 src/core/hle/service/am/window_controller.h create mode 100755 src/core/hle/service/event.cpp create mode 100755 src/core/hle/service/event.h diff --git a/README.md b/README.md index dfb20827c..dd838173d 100755 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ yuzu emulator early access ============= -This is the source code for early-access 4090. +This is the source code for early-access 4091. ## Legal Notice diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/HomeSettingsFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/HomeSettingsFragment.kt index aefae2938..1f3578b22 100755 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/HomeSettingsFragment.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/HomeSettingsFragment.kt @@ -153,7 +153,13 @@ class HomeSettingsFragment : Fragment() { cancellable = true ) { progressCallback, _ -> val result = NativeLibrary.verifyInstalledContents(progressCallback) - return@newInstance if (result.isEmpty()) { + return@newInstance if (progressCallback.invoke(100, 100)) { + // Invoke the progress callback to check if the process was cancelled + MessageDialogFragment.newInstance( + titleId = R.string.verify_no_result, + descriptionId = R.string.verify_no_result_description + ) + } else if (result.isEmpty()) { MessageDialogFragment.newInstance( titleId = R.string.verify_success, descriptionId = R.string.operation_completed_successfully diff --git a/src/android/app/src/main/jni/applets/software_keyboard.cpp b/src/android/app/src/main/jni/applets/software_keyboard.cpp index 74e040478..9943483e8 100755 --- a/src/android/app/src/main/jni/applets/software_keyboard.cpp +++ b/src/android/app/src/main/jni/applets/software_keyboard.cpp @@ -82,7 +82,7 @@ AndroidKeyboard::ResultData AndroidKeyboard::ResultData::CreateFromFrontend(jobj const jstring string = reinterpret_cast(env->GetObjectField( object, env->GetFieldID(s_keyboard_data_class, "text", "Ljava/lang/String;"))); return ResultData{GetJString(env, string), - static_cast(env->GetIntField( + static_cast(env->GetIntField( object, env->GetFieldID(s_keyboard_data_class, "result", "I")))}; } @@ -149,7 +149,7 @@ void AndroidKeyboard::ShowNormalKeyboard() const { } void AndroidKeyboard::ShowTextCheckDialog( - Service::AM::Applets::SwkbdTextCheckResult text_check_result, + Service::AM::Frontend::SwkbdTextCheckResult text_check_result, std::u16string text_check_message) const { LOG_WARNING(Frontend, "(STUBBED) called, backend requested to show the text check dialog."); } @@ -204,7 +204,7 @@ void AndroidKeyboard::InlineTextChanged( "\ncursor_position={}", Common::UTF16ToUTF8(text_parameters.input_text), text_parameters.cursor_position); - submit_inline_callback(Service::AM::Applets::SwkbdReplyType::ChangedString, + submit_inline_callback(Service::AM::Frontend::SwkbdReplyType::ChangedString, text_parameters.input_text, text_parameters.cursor_position); } @@ -219,7 +219,7 @@ void AndroidKeyboard::SubmitInlineKeyboardText(std::u16string submitted_text) { m_current_text += submitted_text; - submit_inline_callback(Service::AM::Applets::SwkbdReplyType::ChangedString, m_current_text, + submit_inline_callback(Service::AM::Frontend::SwkbdReplyType::ChangedString, m_current_text, m_current_text.size()); } @@ -236,12 +236,12 @@ void AndroidKeyboard::SubmitInlineKeyboardInput(int key_code) { case KEYCODE_BACK: case KEYCODE_ENTER: m_is_inline_active = false; - submit_inline_callback(Service::AM::Applets::SwkbdReplyType::DecidedEnter, m_current_text, + submit_inline_callback(Service::AM::Frontend::SwkbdReplyType::DecidedEnter, m_current_text, static_cast(m_current_text.size())); break; case KEYCODE_DEL: m_current_text.pop_back(); - submit_inline_callback(Service::AM::Applets::SwkbdReplyType::ChangedString, m_current_text, + submit_inline_callback(Service::AM::Frontend::SwkbdReplyType::ChangedString, m_current_text, m_current_text.size()); break; } diff --git a/src/android/app/src/main/jni/applets/software_keyboard.h b/src/android/app/src/main/jni/applets/software_keyboard.h index b2fb59b68..2affc01f6 100755 --- a/src/android/app/src/main/jni/applets/software_keyboard.h +++ b/src/android/app/src/main/jni/applets/software_keyboard.h @@ -24,7 +24,7 @@ public: void ShowNormalKeyboard() const override; - void ShowTextCheckDialog(Service::AM::Applets::SwkbdTextCheckResult text_check_result, + void ShowTextCheckDialog(Service::AM::Frontend::SwkbdTextCheckResult text_check_result, std::u16string text_check_message) const override; void ShowInlineKeyboard( @@ -45,7 +45,7 @@ private: static ResultData CreateFromFrontend(jobject object); std::string text; - Service::AM::Applets::SwkbdResult result{}; + Service::AM::Frontend::SwkbdResult result{}; }; void SubmitNormalText(const ResultData& result) const; diff --git a/src/android/app/src/main/jni/native.cpp b/src/android/app/src/main/jni/native.cpp index 3fd9a500c..839677c59 100755 --- a/src/android/app/src/main/jni/native.cpp +++ b/src/android/app/src/main/jni/native.cpp @@ -42,14 +42,15 @@ #include "core/frontend/applets/cabinet.h" #include "core/frontend/applets/controller.h" #include "core/frontend/applets/error.h" -#include "core/frontend/applets/general_frontend.h" +#include "core/frontend/applets/general.h" #include "core/frontend/applets/mii_edit.h" #include "core/frontend/applets/profile_select.h" #include "core/frontend/applets/software_keyboard.h" #include "core/frontend/applets/web_browser.h" #include "core/hle/service/am/applet_ae.h" +#include "core/hle/service/am/applet_manager.h" #include "core/hle/service/am/applet_oe.h" -#include "core/hle/service/am/applets/applets.h" +#include "core/hle/service/am/frontend/applets.h" #include "core/hle/service/filesystem/filesystem.h" #include "core/loader/loader.h" #include "frontend_common/config.h" @@ -222,7 +223,7 @@ Core::SystemResultStatus EmulationSession::InitializeEmulation(const std::string m_system.ApplySettings(); Settings::LogSettings(); m_system.HIDCore().ReloadInputDevices(); - m_system.SetAppletFrontendSet({ + m_system.SetFrontendAppletSet({ nullptr, // Amiibo Settings nullptr, // Controller Selector nullptr, // Error Display @@ -238,7 +239,10 @@ Core::SystemResultStatus EmulationSession::InitializeEmulation(const std::string ConfigureFilesystemProvider(filepath); // Load the ROM. - m_load_result = m_system.Load(EmulationSession::GetInstance().Window(), filepath); + Service::AM::FrontendAppletParameters params{ + .applet_id = Service::AM::AppletId::Application, + }; + m_load_result = m_system.Load(EmulationSession::GetInstance().Window(), filepath, params); if (m_load_result != Core::SystemResultStatus::Success) { return m_load_result; } @@ -755,13 +759,13 @@ jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getAppletLaunchPath(JNIEnv* env, j void Java_org_yuzu_yuzu_1emu_NativeLibrary_setCurrentAppletId(JNIEnv* env, jclass clazz, jint jappletId) { - EmulationSession::GetInstance().System().GetAppletManager().SetCurrentAppletId( - static_cast(jappletId)); + EmulationSession::GetInstance().System().GetFrontendAppletHolder().SetCurrentAppletId( + static_cast(jappletId)); } void Java_org_yuzu_yuzu_1emu_NativeLibrary_setCabinetMode(JNIEnv* env, jclass clazz, jint jcabinetMode) { - EmulationSession::GetInstance().System().GetAppletManager().SetCabinetMode( + EmulationSession::GetInstance().System().GetFrontendAppletHolder().SetCabinetMode( static_cast(jcabinetMode)); } diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index ddd3c9704..50fed69c0 100755 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -176,8 +176,8 @@ add_library(core STATIC frontend/applets/controller.h frontend/applets/error.cpp frontend/applets/error.h - frontend/applets/general_frontend.cpp - frontend/applets/general_frontend.h + frontend/applets/general.cpp + frontend/applets/general.h frontend/applets/mii_edit.cpp frontend/applets/mii_edit.h frontend/applets/profile_select.cpp @@ -390,39 +390,101 @@ add_library(core STATIC hle/service/acc/errors.h hle/service/acc/profile_manager.cpp hle/service/acc/profile_manager.h + hle/service/am/frontend/applet_cabinet.cpp + hle/service/am/frontend/applet_cabinet.h + hle/service/am/frontend/applet_controller.cpp + hle/service/am/frontend/applet_controller.h + hle/service/am/frontend/applet_error.cpp + hle/service/am/frontend/applet_error.h + hle/service/am/frontend/applet_general.cpp + hle/service/am/frontend/applet_general.h + hle/service/am/frontend/applet_mii_edit.cpp + hle/service/am/frontend/applet_mii_edit.h + hle/service/am/frontend/applet_mii_edit_types.h + hle/service/am/frontend/applet_profile_select.cpp + hle/service/am/frontend/applet_profile_select.h + hle/service/am/frontend/applet_software_keyboard.cpp + hle/service/am/frontend/applet_software_keyboard.h + hle/service/am/frontend/applet_software_keyboard_types.h + hle/service/am/frontend/applet_web_browser.cpp + hle/service/am/frontend/applet_web_browser.h + hle/service/am/frontend/applet_web_browser_types.h + hle/service/am/frontend/applets.cpp + hle/service/am/frontend/applets.h hle/service/am/am.cpp hle/service/am/am.h + hle/service/am/am_results.h + hle/service/am/am_types.h + hle/service/am/applet.cpp + hle/service/am/applet.h hle/service/am/applet_ae.cpp hle/service/am/applet_ae.h + hle/service/am/applet_manager.cpp + hle/service/am/applet_data_broker.cpp + hle/service/am/applet_data_broker.h + hle/service/am/applet_manager.h hle/service/am/applet_oe.cpp hle/service/am/applet_oe.h - hle/service/am/applets/applet_cabinet.cpp - hle/service/am/applets/applet_cabinet.h - hle/service/am/applets/applet_controller.cpp - hle/service/am/applets/applet_controller.h - hle/service/am/applets/applet_error.cpp - hle/service/am/applets/applet_error.h - hle/service/am/applets/applet_general_backend.cpp - hle/service/am/applets/applet_general_backend.h - hle/service/am/applets/applet_mii_edit.cpp - hle/service/am/applets/applet_mii_edit.h - hle/service/am/applets/applet_mii_edit_types.h - hle/service/am/applets/applet_profile_select.cpp - hle/service/am/applets/applet_profile_select.h - hle/service/am/applets/applet_software_keyboard.cpp - hle/service/am/applets/applet_software_keyboard.h - hle/service/am/applets/applet_software_keyboard_types.h - hle/service/am/applets/applet_web_browser.cpp - hle/service/am/applets/applet_web_browser.h - hle/service/am/applets/applet_web_browser_types.h - hle/service/am/applets/applets.cpp - hle/service/am/applets/applets.h + hle/service/am/applet_common_functions.cpp + hle/service/am/applet_common_functions.h + hle/service/am/applet_message_queue.cpp + hle/service/am/applet_message_queue.h + hle/service/am/application_creator.cpp + hle/service/am/application_creator.h + hle/service/am/application_functions.cpp + hle/service/am/application_functions.h + hle/service/am/application_proxy.cpp + hle/service/am/application_proxy.h + hle/service/am/audio_controller.cpp + hle/service/am/audio_controller.h + hle/service/am/common_state_getter.cpp + hle/service/am/common_state_getter.h + hle/service/am/debug_functions.cpp + hle/service/am/debug_functions.h + hle/service/am/display_controller.cpp + hle/service/am/display_controller.h + hle/service/am/global_state_controller.cpp + hle/service/am/global_state_controller.h + hle/service/am/hid_registration.cpp + hle/service/am/hid_registration.h + hle/service/am/home_menu_functions.cpp + hle/service/am/home_menu_functions.h hle/service/am/idle.cpp hle/service/am/idle.h + hle/service/am/library_applet_accessor.cpp + hle/service/am/library_applet_accessor.h + hle/service/am/library_applet_creator.cpp + hle/service/am/library_applet_creator.h + hle/service/am/library_applet_proxy.cpp + hle/service/am/library_applet_proxy.h + hle/service/am/library_applet_self_accessor.cpp + hle/service/am/library_applet_self_accessor.h + hle/service/am/library_applet_storage.cpp + hle/service/am/library_applet_storage.h + hle/service/am/lock_accessor.cpp + hle/service/am/lock_accessor.h + hle/service/am/managed_layer_holder.cpp + hle/service/am/managed_layer_holder.h hle/service/am/omm.cpp hle/service/am/omm.h + hle/service/am/process_winding_controller.cpp + hle/service/am/process_winding_controller.h + hle/service/am/process.cpp + hle/service/am/process.h + hle/service/am/self_controller.cpp + hle/service/am/self_controller.h + hle/service/am/system_applet_proxy.cpp + hle/service/am/system_applet_proxy.h + hle/service/am/system_buffer_manager.cpp + hle/service/am/system_buffer_manager.h hle/service/am/spsm.cpp hle/service/am/spsm.h + hle/service/am/storage_accessor.cpp + hle/service/am/storage_accessor.h + hle/service/am/storage.cpp + hle/service/am/storage.h + hle/service/am/window_controller.cpp + hle/service/am/window_controller.h hle/service/aoc/aoc_u.cpp hle/service/aoc/aoc_u.h hle/service/apm/apm.cpp @@ -486,6 +548,8 @@ add_library(core STATIC hle/service/es/es.h hle/service/eupld/eupld.cpp hle/service/eupld/eupld.h + hle/service/event.cpp + hle/service/event.h hle/service/fatal/fatal.cpp hle/service/fatal/fatal.h hle/service/fatal/fatal_p.cpp diff --git a/src/core/core.cpp b/src/core/core.cpp index ae6003629..f56d10e60 100755 --- a/src/core/core.cpp +++ b/src/core/core.cpp @@ -36,7 +36,8 @@ #include "core/hle/kernel/kernel.h" #include "core/hle/kernel/physical_core.h" #include "core/hle/service/acc/profile_manager.h" -#include "core/hle/service/am/applets/applets.h" +#include "core/hle/service/am/applet_manager.h" +#include "core/hle/service/am/frontend/applets.h" #include "core/hle/service/apm/apm_controller.h" #include "core/hle/service/filesystem/filesystem.h" #include "core/hle/service/glue/glue_manager.h" @@ -135,8 +136,8 @@ FileSys::VirtualFile GetGameFileFromPath(const FileSys::VirtualFilesystem& vfs, struct System::Impl { explicit Impl(System& system) - : kernel{system}, fs_controller{system}, hid_core{}, room_network{}, - cpu_manager{system}, reporter{system}, applet_manager{system}, profile_manager{} {} + : kernel{system}, fs_controller{system}, hid_core{}, room_network{}, cpu_manager{system}, + reporter{system}, applet_manager{system}, frontend_applets{system}, profile_manager{} {} void Initialize(System& system) { device_memory = std::make_unique(); @@ -157,7 +158,7 @@ struct System::Impl { } // Create default implementations of applets if one is not provided. - applet_manager.SetDefaultAppletsIfMissing(); + frontend_applets.SetDefaultAppletsIfMissing(); is_async_gpu = Settings::values.use_asynchronous_gpu_emulation.GetValue(); @@ -330,16 +331,27 @@ struct System::Impl { } SystemResultStatus Load(System& system, Frontend::EmuWindow& emu_window, - const std::string& filepath, u64 program_id, - std::size_t program_index) { + const std::string& filepath, + Service::AM::FrontendAppletParameters& params) { app_loader = Loader::GetLoader(system, GetGameFileFromPath(virtual_filesystem, filepath), - program_id, program_index); + params.program_id, params.program_index); if (!app_loader) { LOG_CRITICAL(Core, "Failed to obtain loader for {}!", filepath); return SystemResultStatus::ErrorGetLoader; } + if (app_loader->ReadProgramId(params.program_id) != Loader::ResultStatus::Success) { + LOG_ERROR(Core, "Failed to find title id for ROM!"); + } + + std::string name = "Unknown program"; + if (app_loader->ReadTitle(name) != Loader::ResultStatus::Success) { + LOG_ERROR(Core, "Failed to read title for ROM!"); + } + + LOG_INFO(Core, "Loading {} ({})", name, params.program_id); + InitializeKernel(system); // Create the application process. @@ -373,9 +385,14 @@ struct System::Impl { cheat_engine->Initialize(); } + // Register with applet manager. + applet_manager.CreateAndInsertByFrontendAppletParameters(main_process->GetProcessId(), + params); + // All threads are started, begin main process execution, now that we're in the clear. main_process->Run(load_parameters->main_thread_priority, load_parameters->main_thread_stack_size); + main_process->Close(); if (Settings::values.gamecard_inserted) { if (Settings::values.gamecard_current_game) { @@ -386,21 +403,13 @@ struct System::Impl { } } - if (app_loader->ReadProgramId(program_id) != Loader::ResultStatus::Success) { - LOG_ERROR(Core, "Failed to find title id for ROM (Error {})", load_result); - } - perf_stats = std::make_unique(program_id); + perf_stats = std::make_unique(params.program_id); // Reset counters and set time origin to current frame GetAndResetPerfStats(); perf_stats->BeginSystemFrame(); - std::string name = "Unknown Game"; - if (app_loader->ReadTitle(name) != Loader::ResultStatus::Success) { - LOG_ERROR(Core, "Failed to read title for ROM (Error {})", load_result); - } - std::string title_version; - const FileSys::PatchManager pm(program_id, system.GetFileSystemController(), + const FileSys::PatchManager pm(params.program_id, system.GetFileSystemController(), system.GetContentProvider()); const auto metadata = pm.GetControlMetadata(); if (metadata.first != nullptr) { @@ -409,14 +418,15 @@ struct System::Impl { if (auto room_member = room_network.GetRoomMember().lock()) { Network::GameInfo game_info; game_info.name = name; - game_info.id = program_id; + game_info.id = params.program_id; game_info.version = title_version; room_member->SendGameInfo(game_info); } // Workarounds: // Activate this in Super Smash Brothers Ultimate, it only affects AMD cards using AMDVLK - Settings::values.renderer_amdvlk_depth_bias_workaround = program_id == 0x1006A800016E000ULL; + Settings::values.renderer_amdvlk_depth_bias_workaround = + params.program_id == 0x1006A800016E000ULL; status = SystemResultStatus::Success; return status; @@ -455,6 +465,7 @@ struct System::Impl { } kernel.CloseServices(); kernel.ShutdownCores(); + applet_manager.Reset(); services.reset(); service_manager.reset(); fs_controller.Reset(); @@ -566,8 +577,9 @@ struct System::Impl { std::unique_ptr renderdoc_api; - /// Frontend applets - Service::AM::Applets::AppletManager applet_manager; + /// Applets + Service::AM::AppletManager applet_manager; + Service::AM::Frontend::FrontendAppletHolder frontend_applets; /// APM (Performance) services Service::APM::Controller apm_controller{core_timing}; @@ -680,8 +692,8 @@ void System::InitializeDebugger() { } SystemResultStatus System::Load(Frontend::EmuWindow& emu_window, const std::string& filepath, - u64 program_id, std::size_t program_index) { - return impl->Load(*this, emu_window, filepath, program_id, program_index); + Service::AM::FrontendAppletParameters& params) { + return impl->Load(*this, emu_window, filepath, params); } bool System::IsPoweredOn() const { @@ -871,19 +883,19 @@ void System::RegisterCheatList(const std::vector& list, impl->cheat_engine->SetMainMemoryParameters(main_region_begin, main_region_size); } -void System::SetAppletFrontendSet(Service::AM::Applets::AppletFrontendSet&& set) { - impl->applet_manager.SetAppletFrontendSet(std::move(set)); +void System::SetFrontendAppletSet(Service::AM::Frontend::FrontendAppletSet&& set) { + impl->frontend_applets.SetFrontendAppletSet(std::move(set)); } -void System::SetDefaultAppletFrontendSet() { - impl->applet_manager.SetDefaultAppletFrontendSet(); +Service::AM::Frontend::FrontendAppletHolder& System::GetFrontendAppletHolder() { + return impl->frontend_applets; } -Service::AM::Applets::AppletManager& System::GetAppletManager() { - return impl->applet_manager; +const Service::AM::Frontend::FrontendAppletHolder& System::GetFrontendAppletHolder() const { + return impl->frontend_applets; } -const Service::AM::Applets::AppletManager& System::GetAppletManager() const { +Service::AM::AppletManager& System::GetAppletManager() { return impl->applet_manager; } diff --git a/src/core/core.h b/src/core/core.h index ad5ac97e5..c61012439 100755 --- a/src/core/core.h +++ b/src/core/core.h @@ -50,10 +50,15 @@ namespace Account { class ProfileManager; } // namespace Account -namespace AM::Applets { -struct AppletFrontendSet; +namespace AM { +struct FrontendAppletParameters; class AppletManager; -} // namespace AM::Applets +} // namespace AM + +namespace AM::Frontend { +struct FrontendAppletSet; +class FrontendAppletHolder; +} // namespace AM::Frontend namespace APM { class Controller; @@ -203,8 +208,8 @@ public: * @returns SystemResultStatus code, indicating if the operation succeeded. */ [[nodiscard]] SystemResultStatus Load(Frontend::EmuWindow& emu_window, - const std::string& filepath, u64 program_id = 0, - std::size_t program_index = 0); + const std::string& filepath, + Service::AM::FrontendAppletParameters& params); /** * Indicates if the emulated system is powered on (all subsystems initialized and able to run an @@ -344,11 +349,13 @@ public: const std::array& build_id, u64 main_region_begin, u64 main_region_size); - void SetAppletFrontendSet(Service::AM::Applets::AppletFrontendSet&& set); - void SetDefaultAppletFrontendSet(); + void SetFrontendAppletSet(Service::AM::Frontend::FrontendAppletSet&& set); - [[nodiscard]] Service::AM::Applets::AppletManager& GetAppletManager(); - [[nodiscard]] const Service::AM::Applets::AppletManager& GetAppletManager() const; + [[nodiscard]] Service::AM::Frontend::FrontendAppletHolder& GetFrontendAppletHolder(); + [[nodiscard]] const Service::AM::Frontend::FrontendAppletHolder& GetFrontendAppletHolder() + const; + + [[nodiscard]] Service::AM::AppletManager& GetAppletManager(); void SetContentProvider(std::unique_ptr provider); diff --git a/src/core/file_sys/vfs/vfs_real.cpp b/src/core/file_sys/vfs/vfs_real.cpp index 627d5d251..3ad073e4a 100755 --- a/src/core/file_sys/vfs/vfs_real.cpp +++ b/src/core/file_sys/vfs/vfs_real.cpp @@ -20,6 +20,10 @@ #define stat _stat64 #endif +#ifdef ANDROID +#include "common/fs/fs_android.h" +#endif + namespace FileSys { namespace FS = Common::FS; @@ -274,6 +278,11 @@ RealVfsFile::~RealVfsFile() { } std::string RealVfsFile::GetName() const { +#ifdef ANDROID + if (path[0] != '/') { + return FS::Android::GetFilename(path); + } +#endif return path_components.empty() ? "" : std::string(path_components.back()); } diff --git a/src/core/frontend/applets/general.cpp b/src/core/frontend/applets/general.cpp new file mode 100755 index 000000000..4c299ee9c --- /dev/null +++ b/src/core/frontend/applets/general.cpp @@ -0,0 +1,59 @@ +// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "common/logging/log.h" +#include "core/frontend/applets/general.h" + +namespace Core::Frontend { + +ParentalControlsApplet::~ParentalControlsApplet() = default; + +DefaultParentalControlsApplet::~DefaultParentalControlsApplet() = default; + +void DefaultParentalControlsApplet::Close() const {} + +void DefaultParentalControlsApplet::VerifyPIN(std::function finished, + bool suspend_future_verification_temporarily) { + LOG_INFO(Service_AM, + "Application requested frontend to verify PIN (normal), " + "suspend_future_verification_temporarily={}, verifying as correct.", + suspend_future_verification_temporarily); + finished(true); +} + +void DefaultParentalControlsApplet::VerifyPINForSettings(std::function finished) { + LOG_INFO(Service_AM, + "Application requested frontend to verify PIN (settings), verifying as correct."); + finished(true); +} + +void DefaultParentalControlsApplet::RegisterPIN(std::function finished) { + LOG_INFO(Service_AM, "Application requested frontend to register new PIN"); + finished(); +} + +void DefaultParentalControlsApplet::ChangePIN(std::function finished) { + LOG_INFO(Service_AM, "Application requested frontend to change PIN to new value"); + finished(); +} + +PhotoViewerApplet::~PhotoViewerApplet() = default; + +DefaultPhotoViewerApplet::~DefaultPhotoViewerApplet() = default; + +void DefaultPhotoViewerApplet::Close() const {} + +void DefaultPhotoViewerApplet::ShowPhotosForApplication(u64 title_id, + std::function finished) const { + LOG_INFO(Service_AM, + "Application requested frontend to display stored photos for title_id={:016X}", + title_id); + finished(); +} + +void DefaultPhotoViewerApplet::ShowAllPhotos(std::function finished) const { + LOG_INFO(Service_AM, "Application requested frontend to display all stored photos."); + finished(); +} + +} // namespace Core::Frontend diff --git a/src/core/frontend/applets/general.h b/src/core/frontend/applets/general.h new file mode 100755 index 000000000..319838ac7 --- /dev/null +++ b/src/core/frontend/applets/general.h @@ -0,0 +1,63 @@ +// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include +#include "common/common_types.h" + +#include "core/frontend/applets/applet.h" + +namespace Core::Frontend { + +class ParentalControlsApplet : public Applet { +public: + virtual ~ParentalControlsApplet(); + + // Prompts the user to enter a PIN and calls the callback with whether or not it matches the + // correct PIN. If the bool is passed, and the PIN was recently entered correctly, the frontend + // should not prompt and simply return true. + virtual void VerifyPIN(std::function finished, + bool suspend_future_verification_temporarily) = 0; + + // Prompts the user to enter a PIN and calls the callback for correctness. Frontends can + // optionally alert the user that this is to change parental controls settings. + virtual void VerifyPINForSettings(std::function finished) = 0; + + // Prompts the user to create a new PIN for pctl and stores it with the service. + virtual void RegisterPIN(std::function finished) = 0; + + // Prompts the user to verify the current PIN and then store a new one into pctl. + virtual void ChangePIN(std::function finished) = 0; +}; + +class DefaultParentalControlsApplet final : public ParentalControlsApplet { +public: + ~DefaultParentalControlsApplet() override; + + void Close() const override; + void VerifyPIN(std::function finished, + bool suspend_future_verification_temporarily) override; + void VerifyPINForSettings(std::function finished) override; + void RegisterPIN(std::function finished) override; + void ChangePIN(std::function finished) override; +}; + +class PhotoViewerApplet : public Applet { +public: + virtual ~PhotoViewerApplet(); + + virtual void ShowPhotosForApplication(u64 title_id, std::function finished) const = 0; + virtual void ShowAllPhotos(std::function finished) const = 0; +}; + +class DefaultPhotoViewerApplet final : public PhotoViewerApplet { +public: + ~DefaultPhotoViewerApplet() override; + + void Close() const override; + void ShowPhotosForApplication(u64 title_id, std::function finished) const override; + void ShowAllPhotos(std::function finished) const override; +}; + +} // namespace Core::Frontend diff --git a/src/core/frontend/applets/profile_select.h b/src/core/frontend/applets/profile_select.h index 41f5a3373..0a6db38d0 100755 --- a/src/core/frontend/applets/profile_select.h +++ b/src/core/frontend/applets/profile_select.h @@ -8,15 +8,15 @@ #include "common/uuid.h" #include "core/frontend/applets/applet.h" -#include "core/hle/service/am/applets/applet_profile_select.h" +#include "core/hle/service/am/frontend/applet_profile_select.h" namespace Core::Frontend { struct ProfileSelectParameters { - Service::AM::Applets::UiMode mode; + Service::AM::Frontend::UiMode mode; std::array invalid_uid_list; - Service::AM::Applets::UiSettingsDisplayOptions display_options; - Service::AM::Applets::UserSelectionPurpose purpose; + Service::AM::Frontend::UiSettingsDisplayOptions display_options; + Service::AM::Frontend::UserSelectionPurpose purpose; }; class ProfileSelectApplet : public Applet { diff --git a/src/core/frontend/applets/software_keyboard.cpp b/src/core/frontend/applets/software_keyboard.cpp index 7f6490d78..71fd62f76 100755 --- a/src/core/frontend/applets/software_keyboard.cpp +++ b/src/core/frontend/applets/software_keyboard.cpp @@ -69,7 +69,7 @@ void DefaultSoftwareKeyboardApplet::ShowNormalKeyboard() const { } void DefaultSoftwareKeyboardApplet::ShowTextCheckDialog( - Service::AM::Applets::SwkbdTextCheckResult text_check_result, + Service::AM::Frontend::SwkbdTextCheckResult text_check_result, std::u16string text_check_message) const { LOG_WARNING(Service_AM, "(STUBBED) called, backend requested to show the text check dialog."); } @@ -118,7 +118,7 @@ void DefaultSoftwareKeyboardApplet::InlineTextChanged(InlineTextParameters text_ "\ncursor_position={}", Common::UTF16ToUTF8(text_parameters.input_text), text_parameters.cursor_position); - submit_inline_callback(Service::AM::Applets::SwkbdReplyType::ChangedString, + submit_inline_callback(Service::AM::Frontend::SwkbdReplyType::ChangedString, text_parameters.input_text, text_parameters.cursor_position); } @@ -127,22 +127,22 @@ void DefaultSoftwareKeyboardApplet::ExitKeyboard() const { } void DefaultSoftwareKeyboardApplet::SubmitNormalText(std::u16string text) const { - submit_normal_callback(Service::AM::Applets::SwkbdResult::Ok, text, true); + submit_normal_callback(Service::AM::Frontend::SwkbdResult::Ok, text, true); } void DefaultSoftwareKeyboardApplet::SubmitInlineText(std::u16string_view text) const { std::this_thread::sleep_for(std::chrono::milliseconds(500)); for (std::size_t index = 0; index < text.size(); ++index) { - submit_inline_callback(Service::AM::Applets::SwkbdReplyType::ChangedString, + submit_inline_callback(Service::AM::Frontend::SwkbdReplyType::ChangedString, std::u16string(text.data(), text.data() + index + 1), static_cast(index) + 1); std::this_thread::sleep_for(std::chrono::milliseconds(250)); } - submit_inline_callback(Service::AM::Applets::SwkbdReplyType::DecidedEnter, std::u16string(text), - static_cast(text.size())); + submit_inline_callback(Service::AM::Frontend::SwkbdReplyType::DecidedEnter, + std::u16string(text), static_cast(text.size())); } } // namespace Core::Frontend diff --git a/src/core/frontend/applets/software_keyboard.h b/src/core/frontend/applets/software_keyboard.h index bed26e247..fc5a2ef5f 100755 --- a/src/core/frontend/applets/software_keyboard.h +++ b/src/core/frontend/applets/software_keyboard.h @@ -8,7 +8,7 @@ #include "common/common_types.h" #include "core/frontend/applets/applet.h" -#include "core/hle/service/am/applets/applet_software_keyboard_types.h" +#include "core/hle/service/am/frontend/applet_software_keyboard_types.h" namespace Core::Frontend { @@ -23,10 +23,10 @@ struct KeyboardInitializeParameters { u32 max_text_length; u32 min_text_length; s32 initial_cursor_position; - Service::AM::Applets::SwkbdType type; - Service::AM::Applets::SwkbdPasswordMode password_mode; - Service::AM::Applets::SwkbdTextDrawType text_draw_type; - Service::AM::Applets::SwkbdKeyDisableFlags key_disable_flags; + Service::AM::Frontend::SwkbdType type; + Service::AM::Frontend::SwkbdPasswordMode password_mode; + Service::AM::Frontend::SwkbdTextDrawType text_draw_type; + Service::AM::Frontend::SwkbdKeyDisableFlags key_disable_flags; bool use_blur_background; bool enable_backspace_button; bool enable_return_button; @@ -40,8 +40,8 @@ struct InlineAppearParameters { f32 key_top_scale_y; f32 key_top_translate_x; f32 key_top_translate_y; - Service::AM::Applets::SwkbdType type; - Service::AM::Applets::SwkbdKeyDisableFlags key_disable_flags; + Service::AM::Frontend::SwkbdType type; + Service::AM::Frontend::SwkbdKeyDisableFlags key_disable_flags; bool key_top_as_floating; bool enable_backspace_button; bool enable_return_button; @@ -56,9 +56,9 @@ struct InlineTextParameters { class SoftwareKeyboardApplet : public Applet { public: using SubmitInlineCallback = - std::function; + std::function; using SubmitNormalCallback = - std::function; + std::function; virtual ~SoftwareKeyboardApplet(); @@ -69,7 +69,7 @@ public: virtual void ShowNormalKeyboard() const = 0; - virtual void ShowTextCheckDialog(Service::AM::Applets::SwkbdTextCheckResult text_check_result, + virtual void ShowTextCheckDialog(Service::AM::Frontend::SwkbdTextCheckResult text_check_result, std::u16string text_check_message) const = 0; virtual void ShowInlineKeyboard(InlineAppearParameters appear_parameters) const = 0; @@ -93,7 +93,7 @@ public: void ShowNormalKeyboard() const override; - void ShowTextCheckDialog(Service::AM::Applets::SwkbdTextCheckResult text_check_result, + void ShowTextCheckDialog(Service::AM::Frontend::SwkbdTextCheckResult text_check_result, std::u16string text_check_message) const override; void ShowInlineKeyboard(InlineAppearParameters appear_parameters) const override; diff --git a/src/core/frontend/applets/web_browser.cpp b/src/core/frontend/applets/web_browser.cpp index 99ab10cb4..381f454bf 100755 --- a/src/core/frontend/applets/web_browser.cpp +++ b/src/core/frontend/applets/web_browser.cpp @@ -18,7 +18,7 @@ void DefaultWebBrowserApplet::OpenLocalWebPage(const std::string& local_url, LOG_WARNING(Service_AM, "(STUBBED) called, backend requested to open local web page at {}", local_url); - callback(Service::AM::Applets::WebExitReason::WindowClosed, "http://localhost/"); + callback(Service::AM::Frontend::WebExitReason::WindowClosed, "http://localhost/"); } void DefaultWebBrowserApplet::OpenExternalWebPage(const std::string& external_url, @@ -26,7 +26,7 @@ void DefaultWebBrowserApplet::OpenExternalWebPage(const std::string& external_ur LOG_WARNING(Service_AM, "(STUBBED) called, backend requested to open external web page at {}", external_url); - callback(Service::AM::Applets::WebExitReason::WindowClosed, "http://localhost/"); + callback(Service::AM::Frontend::WebExitReason::WindowClosed, "http://localhost/"); } } // namespace Core::Frontend diff --git a/src/core/frontend/applets/web_browser.h b/src/core/frontend/applets/web_browser.h index a2d44e369..9f0b61755 100755 --- a/src/core/frontend/applets/web_browser.h +++ b/src/core/frontend/applets/web_browser.h @@ -6,7 +6,7 @@ #include #include "core/frontend/applets/applet.h" -#include "core/hle/service/am/applets/applet_web_browser_types.h" +#include "core/hle/service/am/frontend/applet_web_browser_types.h" namespace Core::Frontend { @@ -14,7 +14,7 @@ class WebBrowserApplet : public Applet { public: using ExtractROMFSCallback = std::function; using OpenWebPageCallback = - std::function; + std::function; virtual ~WebBrowserApplet(); diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp index b72344751..ec71fc5c0 100755 --- a/src/core/hle/kernel/kernel.cpp +++ b/src/core/hle/kernel/kernel.cpp @@ -97,8 +97,14 @@ struct KernelCore::Impl { RegisterHostThread(nullptr); } - void TerminateApplicationProcess() { - application_process.load()->Terminate(); + void TerminateAllProcesses() { + std::scoped_lock lk{process_list_lock}; + for (auto& process : process_list) { + process->Terminate(); + process->Close(); + process = nullptr; + } + process_list.clear(); } void Shutdown() { @@ -107,18 +113,9 @@ struct KernelCore::Impl { CloseServices(); - auto* old_process = application_process.exchange(nullptr); - if (old_process) { - old_process->Close(); - } - - { - std::scoped_lock lk{process_list_lock}; - for (auto* const process : process_list) { - process->Terminate(); - process->Close(); - } - process_list.clear(); + if (application_process) { + application_process->Close(); + application_process = nullptr; } next_object_id = 0; @@ -354,6 +351,7 @@ struct KernelCore::Impl { void MakeApplicationProcess(KProcess* process) { application_process = process; + application_process->Open(); } static inline thread_local u8 host_thread_id = UINT8_MAX; @@ -779,7 +777,7 @@ struct KernelCore::Impl { // Lists all processes that exist in the current session. std::mutex process_list_lock; std::vector process_list; - std::atomic application_process{}; + KProcess* application_process{}; std::unique_ptr global_scheduler_context; std::unique_ptr hardware_timer; @@ -1243,7 +1241,7 @@ void KernelCore::SuspendApplication(bool suspended) { } void KernelCore::ShutdownCores() { - impl->TerminateApplicationProcess(); + impl->TerminateAllProcesses(); KScopedSchedulerLock lk{*this}; diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp index 7695072e1..0081cd3ad 100755 --- a/src/core/hle/service/am/am.cpp +++ b/src/core/hle/service/am/am.cpp @@ -1,2704 +1,27 @@ // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later -#include -#include -#include -#include -#include "common/settings.h" -#include "common/settings_enums.h" -#include "core/core.h" -#include "core/core_timing.h" -#include "core/file_sys/control_metadata.h" -#include "core/file_sys/patch_manager.h" -#include "core/file_sys/registered_cache.h" -#include "core/file_sys/savedata_factory.h" -#include "core/hle/kernel/k_event.h" -#include "core/hle/kernel/k_transfer_memory.h" -#include "core/hle/result.h" -#include "core/hle/service/acc/profile_manager.h" #include "core/hle/service/am/am.h" #include "core/hle/service/am/applet_ae.h" #include "core/hle/service/am/applet_oe.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_profile_select.h" -#include "core/hle/service/am/applets/applet_software_keyboard_types.h" -#include "core/hle/service/am/applets/applet_web_browser.h" -#include "core/hle/service/am/applets/applets.h" #include "core/hle/service/am/idle.h" #include "core/hle/service/am/omm.h" #include "core/hle/service/am/spsm.h" -#include "core/hle/service/apm/apm_controller.h" -#include "core/hle/service/apm/apm_interface.h" -#include "core/hle/service/bcat/backend/backend.h" -#include "core/hle/service/caps/caps_su.h" -#include "core/hle/service/caps/caps_types.h" -#include "core/hle/service/filesystem/filesystem.h" -#include "core/hle/service/filesystem/save_data_controller.h" -#include "core/hle/service/ipc_helpers.h" -#include "core/hle/service/ns/ns.h" -#include "core/hle/service/nvnflinger/fb_share_buffer_manager.h" -#include "core/hle/service/nvnflinger/nvnflinger.h" -#include "core/hle/service/pm/pm.h" #include "core/hle/service/server_manager.h" -#include "core/hle/service/sm/sm.h" -#include "core/hle/service/vi/vi.h" -#include "core/hle/service/vi/vi_results.h" -#include "core/memory.h" -#include "hid_core/hid_types.h" -#include "hid_core/resources/npad/npad.h" namespace Service::AM { -constexpr Result ResultNoDataInChannel{ErrorModule::AM, 2}; -constexpr Result ResultNoMessages{ErrorModule::AM, 3}; -constexpr Result ResultInvalidOffset{ErrorModule::AM, 503}; - -enum class LaunchParameterKind : u32 { - UserChannel = 1, - AccountPreselectedUser = 2, -}; - -constexpr u32 LAUNCH_PARAMETER_ACCOUNT_PRESELECTED_USER_MAGIC = 0xC79497CA; - -struct LaunchParameterAccountPreselectedUser { - u32_le magic; - u32_le is_account_selected; - Common::UUID current_user; - INSERT_PADDING_BYTES(0x70); -}; -static_assert(sizeof(LaunchParameterAccountPreselectedUser) == 0x88); - -IWindowController::IWindowController(Core::System& system_) - : ServiceFramework{system_, "IWindowController"} { - // clang-format off - static const FunctionInfo functions[] = { - {0, nullptr, "CreateWindow"}, - {1, &IWindowController::GetAppletResourceUserId, "GetAppletResourceUserId"}, - {2, &IWindowController::GetAppletResourceUserIdOfCallerApplet, "GetAppletResourceUserIdOfCallerApplet"}, - {10, &IWindowController::AcquireForegroundRights, "AcquireForegroundRights"}, - {11, nullptr, "ReleaseForegroundRights"}, - {12, nullptr, "RejectToChangeIntoBackground"}, - {20, nullptr, "SetAppletWindowVisibility"}, - {21, nullptr, "SetAppletGpuTimeSlice"}, - }; - // clang-format on - - RegisterHandlers(functions); -} - -IWindowController::~IWindowController() = default; - -void IWindowController::GetAppletResourceUserId(HLERequestContext& ctx) { - const u64 process_id = system.ApplicationProcess()->GetProcessId(); - - LOG_DEBUG(Service_AM, "called. Process ID=0x{:016X}", process_id); - - IPC::ResponseBuilder rb{ctx, 4}; - rb.Push(ResultSuccess); - rb.Push(process_id); -} - -void IWindowController::GetAppletResourceUserIdOfCallerApplet(HLERequestContext& ctx) { - const u64 process_id = 0; - - LOG_WARNING(Service_AM, "(STUBBED) called"); - - IPC::ResponseBuilder rb{ctx, 4}; - rb.Push(ResultSuccess); - rb.Push(process_id); -} - -void IWindowController::AcquireForegroundRights(HLERequestContext& ctx) { - LOG_WARNING(Service_AM, "(STUBBED) called"); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -IAudioController::IAudioController(Core::System& system_) - : ServiceFramework{system_, "IAudioController"} { - // clang-format off - static const FunctionInfo functions[] = { - {0, &IAudioController::SetExpectedMasterVolume, "SetExpectedMasterVolume"}, - {1, &IAudioController::GetMainAppletExpectedMasterVolume, "GetMainAppletExpectedMasterVolume"}, - {2, &IAudioController::GetLibraryAppletExpectedMasterVolume, "GetLibraryAppletExpectedMasterVolume"}, - {3, &IAudioController::ChangeMainAppletMasterVolume, "ChangeMainAppletMasterVolume"}, - {4, &IAudioController::SetTransparentAudioRate, "SetTransparentVolumeRate"}, - }; - // clang-format on - - RegisterHandlers(functions); -} - -IAudioController::~IAudioController() = default; - -void IAudioController::SetExpectedMasterVolume(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const float main_applet_volume_tmp = rp.Pop(); - const float library_applet_volume_tmp = rp.Pop(); - - LOG_DEBUG(Service_AM, "called. main_applet_volume={}, library_applet_volume={}", - main_applet_volume_tmp, library_applet_volume_tmp); - - // Ensure the volume values remain within the 0-100% range - main_applet_volume = std::clamp(main_applet_volume_tmp, min_allowed_volume, max_allowed_volume); - library_applet_volume = - std::clamp(library_applet_volume_tmp, min_allowed_volume, max_allowed_volume); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void IAudioController::GetMainAppletExpectedMasterVolume(HLERequestContext& ctx) { - LOG_DEBUG(Service_AM, "called. main_applet_volume={}", main_applet_volume); - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(ResultSuccess); - rb.Push(main_applet_volume); -} - -void IAudioController::GetLibraryAppletExpectedMasterVolume(HLERequestContext& ctx) { - LOG_DEBUG(Service_AM, "called. library_applet_volume={}", library_applet_volume); - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(ResultSuccess); - rb.Push(library_applet_volume); -} - -void IAudioController::ChangeMainAppletMasterVolume(HLERequestContext& ctx) { - struct Parameters { - float volume; - s64 fade_time_ns; - }; - static_assert(sizeof(Parameters) == 16); - - IPC::RequestParser rp{ctx}; - const auto parameters = rp.PopRaw(); - - LOG_DEBUG(Service_AM, "called. volume={}, fade_time_ns={}", parameters.volume, - parameters.fade_time_ns); - - main_applet_volume = std::clamp(parameters.volume, min_allowed_volume, max_allowed_volume); - fade_time_ns = std::chrono::nanoseconds{parameters.fade_time_ns}; - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void IAudioController::SetTransparentAudioRate(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const float transparent_volume_rate_tmp = rp.Pop(); - - LOG_DEBUG(Service_AM, "called. transparent_volume_rate={}", transparent_volume_rate_tmp); - - // Clamp volume range to 0-100%. - transparent_volume_rate = - std::clamp(transparent_volume_rate_tmp, min_allowed_volume, max_allowed_volume); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -IDisplayController::IDisplayController(Core::System& system_) - : ServiceFramework{system_, "IDisplayController"} { - // clang-format off - static const FunctionInfo functions[] = { - {0, nullptr, "GetLastForegroundCaptureImage"}, - {1, nullptr, "UpdateLastForegroundCaptureImage"}, - {2, nullptr, "GetLastApplicationCaptureImage"}, - {3, nullptr, "GetCallerAppletCaptureImage"}, - {4, nullptr, "UpdateCallerAppletCaptureImage"}, - {5, nullptr, "GetLastForegroundCaptureImageEx"}, - {6, nullptr, "GetLastApplicationCaptureImageEx"}, - {7, &IDisplayController::GetCallerAppletCaptureImageEx, "GetCallerAppletCaptureImageEx"}, - {8, &IDisplayController::TakeScreenShotOfOwnLayer, "TakeScreenShotOfOwnLayer"}, - {9, nullptr, "CopyBetweenCaptureBuffers"}, - {10, nullptr, "AcquireLastApplicationCaptureBuffer"}, - {11, nullptr, "ReleaseLastApplicationCaptureBuffer"}, - {12, nullptr, "AcquireLastForegroundCaptureBuffer"}, - {13, nullptr, "ReleaseLastForegroundCaptureBuffer"}, - {14, nullptr, "AcquireCallerAppletCaptureBuffer"}, - {15, nullptr, "ReleaseCallerAppletCaptureBuffer"}, - {16, nullptr, "AcquireLastApplicationCaptureBufferEx"}, - {17, nullptr, "AcquireLastForegroundCaptureBufferEx"}, - {18, nullptr, "AcquireCallerAppletCaptureBufferEx"}, - {20, nullptr, "ClearCaptureBuffer"}, - {21, nullptr, "ClearAppletTransitionBuffer"}, - {22, nullptr, "AcquireLastApplicationCaptureSharedBuffer"}, - {23, nullptr, "ReleaseLastApplicationCaptureSharedBuffer"}, - {24, &IDisplayController::AcquireLastForegroundCaptureSharedBuffer, "AcquireLastForegroundCaptureSharedBuffer"}, - {25, &IDisplayController::ReleaseLastForegroundCaptureSharedBuffer, "ReleaseLastForegroundCaptureSharedBuffer"}, - {26, &IDisplayController::AcquireCallerAppletCaptureSharedBuffer, "AcquireCallerAppletCaptureSharedBuffer"}, - {27, &IDisplayController::ReleaseCallerAppletCaptureSharedBuffer, "ReleaseCallerAppletCaptureSharedBuffer"}, - {28, nullptr, "TakeScreenShotOfOwnLayerEx"}, - }; - // clang-format on - - RegisterHandlers(functions); -} - -IDisplayController::~IDisplayController() = default; - -void IDisplayController::GetCallerAppletCaptureImageEx(HLERequestContext& ctx) { - LOG_WARNING(Service_AM, "(STUBBED) called"); - - IPC::ResponseBuilder rb{ctx, 4}; - rb.Push(ResultSuccess); - rb.Push(1u); - rb.Push(0); -} - -void IDisplayController::TakeScreenShotOfOwnLayer(HLERequestContext& ctx) { - LOG_WARNING(Service_AM, "(STUBBED) called"); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void IDisplayController::AcquireLastForegroundCaptureSharedBuffer(HLERequestContext& ctx) { - LOG_WARNING(Service_AM, "(STUBBED) called"); - - IPC::ResponseBuilder rb{ctx, 4}; - rb.Push(ResultSuccess); - rb.Push(1U); - rb.Push(0); -} - -void IDisplayController::ReleaseLastForegroundCaptureSharedBuffer(HLERequestContext& ctx) { - LOG_WARNING(Service_AM, "(STUBBED) called"); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void IDisplayController::AcquireCallerAppletCaptureSharedBuffer(HLERequestContext& ctx) { - LOG_WARNING(Service_AM, "(STUBBED) called"); - - IPC::ResponseBuilder rb{ctx, 4}; - rb.Push(ResultSuccess); - rb.Push(1U); - rb.Push(0); -} - -void IDisplayController::ReleaseCallerAppletCaptureSharedBuffer(HLERequestContext& ctx) { - LOG_WARNING(Service_AM, "(STUBBED) called"); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -IDebugFunctions::IDebugFunctions(Core::System& system_) - : ServiceFramework{system_, "IDebugFunctions"} { - // clang-format off - static const FunctionInfo functions[] = { - {0, nullptr, "NotifyMessageToHomeMenuForDebug"}, - {1, nullptr, "OpenMainApplication"}, - {10, nullptr, "PerformSystemButtonPressing"}, - {20, nullptr, "InvalidateTransitionLayer"}, - {30, nullptr, "RequestLaunchApplicationWithUserAndArgumentForDebug"}, - {31, nullptr, "RequestLaunchApplicationByApplicationLaunchInfoForDebug"}, - {40, nullptr, "GetAppletResourceUsageInfo"}, - {50, nullptr, "AddSystemProgramIdAndAppletIdForDebug"}, - {51, nullptr, "AddOperationConfirmedLibraryAppletIdForDebug"}, - {100, nullptr, "SetCpuBoostModeForApplet"}, - {101, nullptr, "CancelCpuBoostModeForApplet"}, - {110, nullptr, "PushToAppletBoundChannelForDebug"}, - {111, nullptr, "TryPopFromAppletBoundChannelForDebug"}, - {120, nullptr, "AlarmSettingNotificationEnableAppEventReserve"}, - {121, nullptr, "AlarmSettingNotificationDisableAppEventReserve"}, - {122, nullptr, "AlarmSettingNotificationPushAppEventNotify"}, - {130, nullptr, "FriendInvitationSetApplicationParameter"}, - {131, nullptr, "FriendInvitationClearApplicationParameter"}, - {132, nullptr, "FriendInvitationPushApplicationParameter"}, - {140, nullptr, "RestrictPowerOperationForSecureLaunchModeForDebug"}, - {200, nullptr, "CreateFloatingLibraryAppletAccepterForDebug"}, - {300, nullptr, "TerminateAllRunningApplicationsForDebug"}, - {900, nullptr, "GetGrcProcessLaunchedSystemEvent"}, - }; - // clang-format on - - RegisterHandlers(functions); -} - -IDebugFunctions::~IDebugFunctions() = default; - -ISelfController::ISelfController(Core::System& system_, Nvnflinger::Nvnflinger& nvnflinger_) - : ServiceFramework{system_, "ISelfController"}, nvnflinger{nvnflinger_}, - service_context{system, "ISelfController"} { - // clang-format off - static const FunctionInfo functions[] = { - {0, &ISelfController::Exit, "Exit"}, - {1, &ISelfController::LockExit, "LockExit"}, - {2, &ISelfController::UnlockExit, "UnlockExit"}, - {3, &ISelfController::EnterFatalSection, "EnterFatalSection"}, - {4, &ISelfController::LeaveFatalSection, "LeaveFatalSection"}, - {9, &ISelfController::GetLibraryAppletLaunchableEvent, "GetLibraryAppletLaunchableEvent"}, - {10, &ISelfController::SetScreenShotPermission, "SetScreenShotPermission"}, - {11, &ISelfController::SetOperationModeChangedNotification, "SetOperationModeChangedNotification"}, - {12, &ISelfController::SetPerformanceModeChangedNotification, "SetPerformanceModeChangedNotification"}, - {13, &ISelfController::SetFocusHandlingMode, "SetFocusHandlingMode"}, - {14, &ISelfController::SetRestartMessageEnabled, "SetRestartMessageEnabled"}, - {15, nullptr, "SetScreenShotAppletIdentityInfo"}, - {16, &ISelfController::SetOutOfFocusSuspendingEnabled, "SetOutOfFocusSuspendingEnabled"}, - {17, nullptr, "SetControllerFirmwareUpdateSection"}, - {18, nullptr, "SetRequiresCaptureButtonShortPressedMessage"}, - {19, &ISelfController::SetAlbumImageOrientation, "SetAlbumImageOrientation"}, - {20, nullptr, "SetDesirableKeyboardLayout"}, - {21, nullptr, "GetScreenShotProgramId"}, - {40, &ISelfController::CreateManagedDisplayLayer, "CreateManagedDisplayLayer"}, - {41, &ISelfController::IsSystemBufferSharingEnabled, "IsSystemBufferSharingEnabled"}, - {42, &ISelfController::GetSystemSharedLayerHandle, "GetSystemSharedLayerHandle"}, - {43, &ISelfController::GetSystemSharedBufferHandle, "GetSystemSharedBufferHandle"}, - {44, &ISelfController::CreateManagedDisplaySeparableLayer, "CreateManagedDisplaySeparableLayer"}, - {45, nullptr, "SetManagedDisplayLayerSeparationMode"}, - {46, nullptr, "SetRecordingLayerCompositionEnabled"}, - {50, &ISelfController::SetHandlesRequestToDisplay, "SetHandlesRequestToDisplay"}, - {51, &ISelfController::ApproveToDisplay, "ApproveToDisplay"}, - {60, nullptr, "OverrideAutoSleepTimeAndDimmingTime"}, - {61, nullptr, "SetMediaPlaybackState"}, - {62, &ISelfController::SetIdleTimeDetectionExtension, "SetIdleTimeDetectionExtension"}, - {63, &ISelfController::GetIdleTimeDetectionExtension, "GetIdleTimeDetectionExtension"}, - {64, nullptr, "SetInputDetectionSourceSet"}, - {65, &ISelfController::ReportUserIsActive, "ReportUserIsActive"}, - {66, nullptr, "GetCurrentIlluminance"}, - {67, nullptr, "IsIlluminanceAvailable"}, - {68, &ISelfController::SetAutoSleepDisabled, "SetAutoSleepDisabled"}, - {69, &ISelfController::IsAutoSleepDisabled, "IsAutoSleepDisabled"}, - {70, nullptr, "ReportMultimediaError"}, - {71, nullptr, "GetCurrentIlluminanceEx"}, - {72, nullptr, "SetInputDetectionPolicy"}, - {80, nullptr, "SetWirelessPriorityMode"}, - {90, &ISelfController::GetAccumulatedSuspendedTickValue, "GetAccumulatedSuspendedTickValue"}, - {91, &ISelfController::GetAccumulatedSuspendedTickChangedEvent, "GetAccumulatedSuspendedTickChangedEvent"}, - {100, &ISelfController::SetAlbumImageTakenNotificationEnabled, "SetAlbumImageTakenNotificationEnabled"}, - {110, nullptr, "SetApplicationAlbumUserData"}, - {120, &ISelfController::SaveCurrentScreenshot, "SaveCurrentScreenshot"}, - {130, &ISelfController::SetRecordVolumeMuted, "SetRecordVolumeMuted"}, - {1000, nullptr, "GetDebugStorageChannel"}, - }; - // clang-format on - - RegisterHandlers(functions); - - launchable_event = service_context.CreateEvent("ISelfController:LaunchableEvent"); - - // This event is created by AM on the first time GetAccumulatedSuspendedTickChangedEvent() is - // called. Yuzu can just create it unconditionally, since it doesn't need to support multiple - // ISelfControllers. The event is signaled on creation, and on transition from suspended -> not - // suspended if the event has previously been created by a call to - // GetAccumulatedSuspendedTickChangedEvent. - - accumulated_suspended_tick_changed_event = - service_context.CreateEvent("ISelfController:AccumulatedSuspendedTickChangedEvent"); - accumulated_suspended_tick_changed_event->Signal(); -} - -ISelfController::~ISelfController() { - service_context.CloseEvent(launchable_event); - service_context.CloseEvent(accumulated_suspended_tick_changed_event); -} - -void ISelfController::Exit(HLERequestContext& ctx) { - LOG_DEBUG(Service_AM, "called"); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); - - system.Exit(); -} - -void ISelfController::LockExit(HLERequestContext& ctx) { - LOG_DEBUG(Service_AM, "called"); - - system.SetExitLocked(true); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void ISelfController::UnlockExit(HLERequestContext& ctx) { - LOG_DEBUG(Service_AM, "called"); - - system.SetExitLocked(false); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); - - if (system.GetExitRequested()) { - system.Exit(); - } -} - -void ISelfController::EnterFatalSection(HLERequestContext& ctx) { - ++num_fatal_sections_entered; - LOG_DEBUG(Service_AM, "called. Num fatal sections entered: {}", num_fatal_sections_entered); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void ISelfController::LeaveFatalSection(HLERequestContext& ctx) { - LOG_DEBUG(Service_AM, "called."); - - // Entry and exit of fatal sections must be balanced. - if (num_fatal_sections_entered == 0) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(Result{ErrorModule::AM, 512}); - return; - } - - --num_fatal_sections_entered; - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void ISelfController::GetLibraryAppletLaunchableEvent(HLERequestContext& ctx) { - LOG_WARNING(Service_AM, "(STUBBED) called"); - - launchable_event->Signal(); - - IPC::ResponseBuilder rb{ctx, 2, 1}; - rb.Push(ResultSuccess); - rb.PushCopyObjects(launchable_event->GetReadableEvent()); -} - -void ISelfController::SetScreenShotPermission(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto permission = rp.PopEnum(); - LOG_DEBUG(Service_AM, "called, permission={}", permission); - - screenshot_permission = permission; - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void ISelfController::SetOperationModeChangedNotification(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - - bool flag = rp.Pop(); - LOG_WARNING(Service_AM, "(STUBBED) called flag={}", flag); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void ISelfController::SetPerformanceModeChangedNotification(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - - bool flag = rp.Pop(); - LOG_WARNING(Service_AM, "(STUBBED) called flag={}", flag); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void ISelfController::SetFocusHandlingMode(HLERequestContext& ctx) { - // Takes 3 input u8s with each field located immediately after the previous - // u8, these are bool flags. No output. - IPC::RequestParser rp{ctx}; - - struct FocusHandlingModeParams { - u8 unknown0; - u8 unknown1; - u8 unknown2; - }; - const auto flags = rp.PopRaw(); - - LOG_WARNING(Service_AM, "(STUBBED) called. unknown0={}, unknown1={}, unknown2={}", - flags.unknown0, flags.unknown1, flags.unknown2); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void ISelfController::SetRestartMessageEnabled(HLERequestContext& ctx) { - LOG_WARNING(Service_AM, "(STUBBED) called"); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void ISelfController::SetOutOfFocusSuspendingEnabled(HLERequestContext& ctx) { - // Takes 3 input u8s with each field located immediately after the previous - // u8, these are bool flags. No output. - IPC::RequestParser rp{ctx}; - - bool enabled = rp.Pop(); - LOG_WARNING(Service_AM, "(STUBBED) called enabled={}", enabled); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void ISelfController::SetAlbumImageOrientation(HLERequestContext& ctx) { - LOG_WARNING(Service_AM, "(STUBBED) called"); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void ISelfController::CreateManagedDisplayLayer(HLERequestContext& ctx) { - LOG_WARNING(Service_AM, "(STUBBED) called"); - - // 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 = nvnflinger.OpenDisplay("Default"); - const auto layer_id = nvnflinger.CreateLayer(*display_id); - - IPC::ResponseBuilder rb{ctx, 4}; - rb.Push(ResultSuccess); - rb.Push(*layer_id); -} - -void ISelfController::IsSystemBufferSharingEnabled(HLERequestContext& ctx) { - LOG_WARNING(Service_AM, "(STUBBED) called"); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(this->EnsureBufferSharingEnabled()); -} - -void ISelfController::GetSystemSharedLayerHandle(HLERequestContext& ctx) { - LOG_WARNING(Service_AM, "(STUBBED) called"); - - IPC::ResponseBuilder rb{ctx, 6}; - rb.Push(this->EnsureBufferSharingEnabled()); - rb.Push(system_shared_buffer_id); - rb.Push(system_shared_layer_id); -} - -void ISelfController::GetSystemSharedBufferHandle(HLERequestContext& ctx) { - LOG_WARNING(Service_AM, "(STUBBED) called"); - - IPC::ResponseBuilder rb{ctx, 4}; - rb.Push(this->EnsureBufferSharingEnabled()); - rb.Push(system_shared_buffer_id); -} - -Result ISelfController::EnsureBufferSharingEnabled() { - if (buffer_sharing_enabled) { - return ResultSuccess; - } - - if (system.GetAppletManager().GetCurrentAppletId() <= Applets::AppletId::Application) { - return VI::ResultOperationFailed; - } - - const auto display_id = nvnflinger.OpenDisplay("Default"); - const auto result = nvnflinger.GetSystemBufferManager().Initialize( - &system_shared_buffer_id, &system_shared_layer_id, *display_id); - - if (result.IsSuccess()) { - buffer_sharing_enabled = true; - } - - return result; -} - -void ISelfController::CreateManagedDisplaySeparableLayer(HLERequestContext& ctx) { - LOG_WARNING(Service_AM, "(STUBBED) called"); - - // 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 = nvnflinger.OpenDisplay("Default"); - const auto layer_id = nvnflinger.CreateLayer(*display_id); - - IPC::ResponseBuilder rb{ctx, 4}; - rb.Push(ResultSuccess); - rb.Push(*layer_id); -} - -void ISelfController::SetHandlesRequestToDisplay(HLERequestContext& ctx) { - LOG_WARNING(Service_AM, "(STUBBED) called"); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void ISelfController::ApproveToDisplay(HLERequestContext& ctx) { - LOG_WARNING(Service_AM, "(STUBBED) called"); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void ISelfController::SetIdleTimeDetectionExtension(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - idle_time_detection_extension = rp.Pop(); - LOG_DEBUG(Service_AM, "(STUBBED) called idle_time_detection_extension={}", - idle_time_detection_extension); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void ISelfController::GetIdleTimeDetectionExtension(HLERequestContext& ctx) { - LOG_WARNING(Service_AM, "(STUBBED) called"); - - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(ResultSuccess); - rb.Push(idle_time_detection_extension); -} - -void ISelfController::ReportUserIsActive(HLERequestContext& ctx) { - LOG_WARNING(Service_AM, "(STUBBED) called"); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void ISelfController::SetAutoSleepDisabled(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - is_auto_sleep_disabled = rp.Pop(); - - // On the system itself, if the previous state of is_auto_sleep_disabled - // differed from the current value passed in, it'd signify the internal - // window manager to update (and also increment some statistics like update counts) - // - // It'd also indicate this change to an idle handling context. - // - // However, given we're emulating this behavior, most of this can be ignored - // and it's sufficient to simply set the member variable for querying via - // IsAutoSleepDisabled(). - - LOG_DEBUG(Service_AM, "called. is_auto_sleep_disabled={}", is_auto_sleep_disabled); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void ISelfController::IsAutoSleepDisabled(HLERequestContext& ctx) { - LOG_DEBUG(Service_AM, "called."); - - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(ResultSuccess); - rb.Push(is_auto_sleep_disabled); -} - -void ISelfController::GetAccumulatedSuspendedTickValue(HLERequestContext& ctx) { - LOG_DEBUG(Service_AM, "called."); - - // This command returns the total number of system ticks since ISelfController creation - // where the game was suspended. Since Yuzu doesn't implement game suspension, this command - // can just always return 0 ticks. - IPC::ResponseBuilder rb{ctx, 4}; - rb.Push(ResultSuccess); - rb.Push(0); -} - -void ISelfController::GetAccumulatedSuspendedTickChangedEvent(HLERequestContext& ctx) { - LOG_DEBUG(Service_AM, "called."); - - IPC::ResponseBuilder rb{ctx, 2, 1}; - rb.Push(ResultSuccess); - rb.PushCopyObjects(accumulated_suspended_tick_changed_event->GetReadableEvent()); -} - -void ISelfController::SetAlbumImageTakenNotificationEnabled(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - - // This service call sets an internal flag whether a notification is shown when an image is - // captured. Currently we do not support capturing images via the capture button, so this can be - // stubbed for now. - const bool album_image_taken_notification_enabled = rp.Pop(); - - LOG_WARNING(Service_AM, "(STUBBED) called. album_image_taken_notification_enabled={}", - album_image_taken_notification_enabled); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void ISelfController::SaveCurrentScreenshot(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - - const auto report_option = rp.PopEnum(); - - LOG_INFO(Service_AM, "called, report_option={}", report_option); - - const auto screenshot_service = - system.ServiceManager().GetService( - "caps:su"); - - if (screenshot_service) { - screenshot_service->CaptureAndSaveScreenshot(report_option); - } - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void ISelfController::SetRecordVolumeMuted(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - - const auto is_record_volume_muted = rp.Pop(); - - LOG_WARNING(Service_AM, "(STUBBED) called. is_record_volume_muted={}", is_record_volume_muted); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -AppletMessageQueue::AppletMessageQueue(Core::System& system) - : service_context{system, "AppletMessageQueue"} { - on_new_message = service_context.CreateEvent("AMMessageQueue:OnMessageReceived"); - on_operation_mode_changed = service_context.CreateEvent("AMMessageQueue:OperationModeChanged"); -} - -AppletMessageQueue::~AppletMessageQueue() { - service_context.CloseEvent(on_new_message); - service_context.CloseEvent(on_operation_mode_changed); -} - -Kernel::KReadableEvent& AppletMessageQueue::GetMessageReceiveEvent() { - return on_new_message->GetReadableEvent(); -} - -Kernel::KReadableEvent& AppletMessageQueue::GetOperationModeChangedEvent() { - return on_operation_mode_changed->GetReadableEvent(); -} - -void AppletMessageQueue::PushMessage(AppletMessage msg) { - messages.push(msg); - on_new_message->Signal(); -} - -AppletMessageQueue::AppletMessage AppletMessageQueue::PopMessage() { - if (messages.empty()) { - on_new_message->Clear(); - return AppletMessage::None; - } - auto msg = messages.front(); - messages.pop(); - if (messages.empty()) { - on_new_message->Clear(); - } - return msg; -} - -std::size_t AppletMessageQueue::GetMessageCount() const { - return messages.size(); -} - -void AppletMessageQueue::RequestExit() { - PushMessage(AppletMessage::Exit); -} - -void AppletMessageQueue::RequestResume() { - PushMessage(AppletMessage::Resume); -} - -void AppletMessageQueue::FocusStateChanged() { - PushMessage(AppletMessage::FocusStateChanged); -} - -void AppletMessageQueue::OperationModeChanged() { - PushMessage(AppletMessage::OperationModeChanged); - PushMessage(AppletMessage::PerformanceModeChanged); - on_operation_mode_changed->Signal(); -} - -ILockAccessor::ILockAccessor(Core::System& system_) - : ServiceFramework{system_, "ILockAccessor"}, service_context{system_, "ILockAccessor"} { - // clang-format off - static const FunctionInfo functions[] = { - {1, &ILockAccessor::TryLock, "TryLock"}, - {2, &ILockAccessor::Unlock, "Unlock"}, - {3, &ILockAccessor::GetEvent, "GetEvent"}, - {4,&ILockAccessor::IsLocked, "IsLocked"}, - }; - // clang-format on - - RegisterHandlers(functions); - - lock_event = service_context.CreateEvent("ILockAccessor::LockEvent"); -} - -ILockAccessor::~ILockAccessor() { - service_context.CloseEvent(lock_event); -}; - -void ILockAccessor::TryLock(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto return_handle = rp.Pop(); - - LOG_WARNING(Service_AM, "(STUBBED) called, return_handle={}", return_handle); - - // TODO: When return_handle is true this function should return the lock handle - - is_locked = true; - - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(ResultSuccess); - rb.Push(is_locked); -} - -void ILockAccessor::Unlock(HLERequestContext& ctx) { - LOG_INFO(Service_AM, "called"); - - is_locked = false; - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void ILockAccessor::GetEvent(HLERequestContext& ctx) { - LOG_INFO(Service_AM, "called"); - - lock_event->Signal(); - - IPC::ResponseBuilder rb{ctx, 2, 1}; - rb.Push(ResultSuccess); - rb.PushCopyObjects(lock_event->GetReadableEvent()); -} - -void ILockAccessor::IsLocked(HLERequestContext& ctx) { - LOG_INFO(Service_AM, "called"); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); - rb.Push(is_locked); -} - -ICommonStateGetter::ICommonStateGetter(Core::System& system_, - std::shared_ptr msg_queue_) - : ServiceFramework{system_, "ICommonStateGetter"}, msg_queue{std::move(msg_queue_)}, - service_context{system_, "ICommonStateGetter"} { - // clang-format off - static const FunctionInfo functions[] = { - {0, &ICommonStateGetter::GetEventHandle, "GetEventHandle"}, - {1, &ICommonStateGetter::ReceiveMessage, "ReceiveMessage"}, - {2, nullptr, "GetThisAppletKind"}, - {3, nullptr, "AllowToEnterSleep"}, - {4, nullptr, "DisallowToEnterSleep"}, - {5, &ICommonStateGetter::GetOperationMode, "GetOperationMode"}, - {6, &ICommonStateGetter::GetPerformanceMode, "GetPerformanceMode"}, - {7, nullptr, "GetCradleStatus"}, - {8, &ICommonStateGetter::GetBootMode, "GetBootMode"}, - {9, &ICommonStateGetter::GetCurrentFocusState, "GetCurrentFocusState"}, - {10, &ICommonStateGetter::RequestToAcquireSleepLock, "RequestToAcquireSleepLock"}, - {11, nullptr, "ReleaseSleepLock"}, - {12, nullptr, "ReleaseSleepLockTransiently"}, - {13, &ICommonStateGetter::GetAcquiredSleepLockEvent, "GetAcquiredSleepLockEvent"}, - {14, nullptr, "GetWakeupCount"}, - {20, nullptr, "PushToGeneralChannel"}, - {30, nullptr, "GetHomeButtonReaderLockAccessor"}, - {31, &ICommonStateGetter::GetReaderLockAccessorEx, "GetReaderLockAccessorEx"}, - {32, nullptr, "GetWriterLockAccessorEx"}, - {40, nullptr, "GetCradleFwVersion"}, - {50, &ICommonStateGetter::IsVrModeEnabled, "IsVrModeEnabled"}, - {51, &ICommonStateGetter::SetVrModeEnabled, "SetVrModeEnabled"}, - {52, &ICommonStateGetter::SetLcdBacklighOffEnabled, "SetLcdBacklighOffEnabled"}, - {53, &ICommonStateGetter::BeginVrModeEx, "BeginVrModeEx"}, - {54, &ICommonStateGetter::EndVrModeEx, "EndVrModeEx"}, - {55, nullptr, "IsInControllerFirmwareUpdateSection"}, - {59, nullptr, "SetVrPositionForDebug"}, - {60, &ICommonStateGetter::GetDefaultDisplayResolution, "GetDefaultDisplayResolution"}, - {61, &ICommonStateGetter::GetDefaultDisplayResolutionChangeEvent, "GetDefaultDisplayResolutionChangeEvent"}, - {62, nullptr, "GetHdcpAuthenticationState"}, - {63, nullptr, "GetHdcpAuthenticationStateChangeEvent"}, - {64, nullptr, "SetTvPowerStateMatchingMode"}, - {65, nullptr, "GetApplicationIdByContentActionName"}, - {66, &ICommonStateGetter::SetCpuBoostMode, "SetCpuBoostMode"}, - {67, nullptr, "CancelCpuBoostMode"}, - {68, &ICommonStateGetter::GetBuiltInDisplayType, "GetBuiltInDisplayType"}, - {80, &ICommonStateGetter::PerformSystemButtonPressingIfInFocus, "PerformSystemButtonPressingIfInFocus"}, - {90, nullptr, "SetPerformanceConfigurationChangedNotification"}, - {91, nullptr, "GetCurrentPerformanceConfiguration"}, - {100, nullptr, "SetHandlingHomeButtonShortPressedEnabled"}, - {110, nullptr, "OpenMyGpuErrorHandler"}, - {120, nullptr, "GetAppletLaunchedHistory"}, - {200, nullptr, "GetOperationModeSystemInfo"}, - {300, &ICommonStateGetter::GetSettingsPlatformRegion, "GetSettingsPlatformRegion"}, - {400, nullptr, "ActivateMigrationService"}, - {401, nullptr, "DeactivateMigrationService"}, - {500, nullptr, "DisableSleepTillShutdown"}, - {501, nullptr, "SuppressDisablingSleepTemporarily"}, - {502, nullptr, "IsSleepEnabled"}, - {503, nullptr, "IsDisablingSleepSuppressed"}, - {900, &ICommonStateGetter::SetRequestExitToLibraryAppletAtExecuteNextProgramEnabled, "SetRequestExitToLibraryAppletAtExecuteNextProgramEnabled"}, - }; - // clang-format on - - RegisterHandlers(functions); - - sleep_lock_event = service_context.CreateEvent("ICommonStateGetter::SleepLockEvent"); - - // Configure applets to be in foreground state - msg_queue->PushMessage(AppletMessageQueue::AppletMessage::FocusStateChanged); - msg_queue->PushMessage(AppletMessageQueue::AppletMessage::ChangeIntoForeground); -} - -ICommonStateGetter::~ICommonStateGetter() { - service_context.CloseEvent(sleep_lock_event); -}; - -void ICommonStateGetter::GetBootMode(HLERequestContext& ctx) { - LOG_DEBUG(Service_AM, "called"); - - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(ResultSuccess); - rb.Push(static_cast(Service::PM::SystemBootMode::Normal)); // Normal boot mode -} - -void ICommonStateGetter::GetEventHandle(HLERequestContext& ctx) { - LOG_DEBUG(Service_AM, "called"); - - IPC::ResponseBuilder rb{ctx, 2, 1}; - rb.Push(ResultSuccess); - rb.PushCopyObjects(msg_queue->GetMessageReceiveEvent()); -} - -void ICommonStateGetter::ReceiveMessage(HLERequestContext& ctx) { - LOG_DEBUG(Service_AM, "called"); - - const auto message = msg_queue->PopMessage(); - IPC::ResponseBuilder rb{ctx, 3}; - - if (message == AppletMessageQueue::AppletMessage::None) { - LOG_ERROR(Service_AM, "Message queue is empty"); - rb.Push(AM::ResultNoMessages); - rb.PushEnum(message); - return; - } - - rb.Push(ResultSuccess); - rb.PushEnum(message); -} - -void ICommonStateGetter::GetCurrentFocusState(HLERequestContext& ctx) { - LOG_DEBUG(Service_AM, "(STUBBED) called"); - - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(ResultSuccess); - rb.Push(static_cast(FocusState::InFocus)); -} - -void ICommonStateGetter::RequestToAcquireSleepLock(HLERequestContext& ctx) { - LOG_WARNING(Service_AM, "(STUBBED) called"); - - // Sleep lock is acquired immediately. - sleep_lock_event->Signal(); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void ICommonStateGetter::GetReaderLockAccessorEx(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto unknown = rp.Pop(); - - LOG_INFO(Service_AM, "called, unknown={}", unknown); - - IPC::ResponseBuilder rb{ctx, 2, 0, 1}; - - rb.Push(ResultSuccess); - rb.PushIpcInterface(system); -} - -void ICommonStateGetter::GetAcquiredSleepLockEvent(HLERequestContext& ctx) { - LOG_WARNING(Service_AM, "called"); - - IPC::ResponseBuilder rb{ctx, 2, 1}; - rb.Push(ResultSuccess); - rb.PushCopyObjects(sleep_lock_event->GetReadableEvent()); -} - -void ICommonStateGetter::IsVrModeEnabled(HLERequestContext& ctx) { - LOG_DEBUG(Service_AM, "called"); - - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(ResultSuccess); - rb.Push(vr_mode_state); -} - -void ICommonStateGetter::SetVrModeEnabled(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - vr_mode_state = rp.Pop(); - - LOG_WARNING(Service_AM, "VR Mode is {}", vr_mode_state ? "on" : "off"); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void ICommonStateGetter::SetLcdBacklighOffEnabled(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto is_lcd_backlight_off_enabled = rp.Pop(); - - LOG_WARNING(Service_AM, "(STUBBED) called. is_lcd_backlight_off_enabled={}", - is_lcd_backlight_off_enabled); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void ICommonStateGetter::BeginVrModeEx(HLERequestContext& ctx) { - LOG_WARNING(Service_AM, "(STUBBED) called"); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void ICommonStateGetter::EndVrModeEx(HLERequestContext& ctx) { - LOG_WARNING(Service_AM, "(STUBBED) called"); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void ICommonStateGetter::GetDefaultDisplayResolutionChangeEvent(HLERequestContext& ctx) { - LOG_DEBUG(Service_AM, "called"); - - IPC::ResponseBuilder rb{ctx, 2, 1}; - rb.Push(ResultSuccess); - rb.PushCopyObjects(msg_queue->GetOperationModeChangedEvent()); -} - -void ICommonStateGetter::GetDefaultDisplayResolution(HLERequestContext& ctx) { - LOG_DEBUG(Service_AM, "called"); - - IPC::ResponseBuilder rb{ctx, 4}; - rb.Push(ResultSuccess); - - if (Settings::IsDockedMode()) { - rb.Push(static_cast(Service::VI::DisplayResolution::DockedWidth)); - rb.Push(static_cast(Service::VI::DisplayResolution::DockedHeight)); - } else { - rb.Push(static_cast(Service::VI::DisplayResolution::UndockedWidth)); - rb.Push(static_cast(Service::VI::DisplayResolution::UndockedHeight)); - } -} - -void ICommonStateGetter::SetCpuBoostMode(HLERequestContext& ctx) { - LOG_DEBUG(Service_AM, "called, forwarding to APM:SYS"); - - const auto& sm = system.ServiceManager(); - const auto apm_sys = sm.GetService("apm:sys"); - ASSERT(apm_sys != nullptr); - - apm_sys->SetCpuBoostMode(ctx); -} - -void ICommonStateGetter::GetBuiltInDisplayType(HLERequestContext& ctx) { - LOG_WARNING(Service_AM, "(STUBBED) called"); - - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(ResultSuccess); - rb.Push(0); -} - -void ICommonStateGetter::PerformSystemButtonPressingIfInFocus(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto system_button{rp.PopEnum()}; - - LOG_WARNING(Service_AM, "(STUBBED) called, system_button={}", system_button); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void ICommonStateGetter::GetSettingsPlatformRegion(HLERequestContext& ctx) { - LOG_WARNING(Service_AM, "(STUBBED) called"); - - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(ResultSuccess); - rb.PushEnum(SysPlatformRegion::Global); -} - -void ICommonStateGetter::SetRequestExitToLibraryAppletAtExecuteNextProgramEnabled( - HLERequestContext& ctx) { - LOG_WARNING(Service_AM, "(STUBBED) called"); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -IStorageImpl::~IStorageImpl() = default; - -class StorageDataImpl final : public IStorageImpl { -public: - explicit StorageDataImpl(std::vector&& buffer_) : buffer{std::move(buffer_)} {} - - std::vector& GetData() override { - return buffer; - } - - const std::vector& GetData() const override { - return buffer; - } - - std::size_t GetSize() const override { - return buffer.size(); - } - -private: - std::vector buffer; -}; - -IStorage::IStorage(Core::System& system_, std::vector&& buffer) - : ServiceFramework{system_, "IStorage"}, impl{std::make_shared( - std::move(buffer))} { - Register(); -} - -void IStorage::Register() { - // clang-format off - static const FunctionInfo functions[] = { - {0, &IStorage::Open, "Open"}, - {1, nullptr, "OpenTransferStorage"}, - }; - // clang-format on - - RegisterHandlers(functions); -} - -IStorage::~IStorage() = default; - -void IStorage::Open(HLERequestContext& ctx) { - LOG_DEBUG(Service_AM, "called"); - - IPC::ResponseBuilder rb{ctx, 2, 0, 1}; - - rb.Push(ResultSuccess); - rb.PushIpcInterface(system, *this); -} - -void ICommonStateGetter::GetOperationMode(HLERequestContext& ctx) { - const bool use_docked_mode{Settings::IsDockedMode()}; - LOG_DEBUG(Service_AM, "called, use_docked_mode={}", use_docked_mode); - - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(ResultSuccess); - rb.Push(static_cast(use_docked_mode ? OperationMode::Docked : OperationMode::Handheld)); -} - -void ICommonStateGetter::GetPerformanceMode(HLERequestContext& ctx) { - LOG_DEBUG(Service_AM, "called"); - - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(ResultSuccess); - rb.PushEnum(system.GetAPMController().GetCurrentPerformanceMode()); -} - -class ILibraryAppletAccessor final : public ServiceFramework { -public: - explicit ILibraryAppletAccessor(Core::System& system_, std::shared_ptr applet_) - : ServiceFramework{system_, "ILibraryAppletAccessor"}, applet{std::move(applet_)} { - // clang-format off - static const FunctionInfo functions[] = { - {0, &ILibraryAppletAccessor::GetAppletStateChangedEvent, "GetAppletStateChangedEvent"}, - {1, &ILibraryAppletAccessor::IsCompleted, "IsCompleted"}, - {10, &ILibraryAppletAccessor::Start, "Start"}, - {20, &ILibraryAppletAccessor::RequestExit, "RequestExit"}, - {25, nullptr, "Terminate"}, - {30, &ILibraryAppletAccessor::GetResult, "GetResult"}, - {50, nullptr, "SetOutOfFocusApplicationSuspendingEnabled"}, - {60, &ILibraryAppletAccessor::PresetLibraryAppletGpuTimeSliceZero, "PresetLibraryAppletGpuTimeSliceZero"}, - {100, &ILibraryAppletAccessor::PushInData, "PushInData"}, - {101, &ILibraryAppletAccessor::PopOutData, "PopOutData"}, - {102, nullptr, "PushExtraStorage"}, - {103, &ILibraryAppletAccessor::PushInteractiveInData, "PushInteractiveInData"}, - {104, &ILibraryAppletAccessor::PopInteractiveOutData, "PopInteractiveOutData"}, - {105, &ILibraryAppletAccessor::GetPopOutDataEvent, "GetPopOutDataEvent"}, - {106, &ILibraryAppletAccessor::GetPopInteractiveOutDataEvent, "GetPopInteractiveOutDataEvent"}, - {110, nullptr, "NeedsToExitProcess"}, - {120, nullptr, "GetLibraryAppletInfo"}, - {150, nullptr, "RequestForAppletToGetForeground"}, - {160, &ILibraryAppletAccessor::GetIndirectLayerConsumerHandle, "GetIndirectLayerConsumerHandle"}, - }; - // clang-format on - - RegisterHandlers(functions); - } - -private: - void GetAppletStateChangedEvent(HLERequestContext& ctx) { - LOG_DEBUG(Service_AM, "called"); - - IPC::ResponseBuilder rb{ctx, 2, 1}; - rb.Push(ResultSuccess); - rb.PushCopyObjects(applet->GetBroker().GetStateChangedEvent()); - } - - void IsCompleted(HLERequestContext& ctx) { - LOG_DEBUG(Service_AM, "called"); - - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(ResultSuccess); - rb.Push(applet->TransactionComplete()); - } - - void GetResult(HLERequestContext& ctx) { - LOG_DEBUG(Service_AM, "called"); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(applet->GetStatus()); - } - - void PresetLibraryAppletGpuTimeSliceZero(HLERequestContext& ctx) { - LOG_WARNING(Service_AM, "(STUBBED) called"); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); - } - - void Start(HLERequestContext& ctx) { - LOG_DEBUG(Service_AM, "called"); - - ASSERT(applet != nullptr); - - applet->Initialize(); - applet->Execute(); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); - } - - void RequestExit(HLERequestContext& ctx) { - LOG_DEBUG(Service_AM, "called"); - - ASSERT(applet != nullptr); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(applet->RequestExit()); - } - - void PushInData(HLERequestContext& ctx) { - LOG_DEBUG(Service_AM, "called"); - - IPC::RequestParser rp{ctx}; - applet->GetBroker().PushNormalDataFromGame(rp.PopIpcInterface().lock()); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); - } - - void PopOutData(HLERequestContext& ctx) { - LOG_DEBUG(Service_AM, "called"); - - auto storage = applet->GetBroker().PopNormalDataToGame(); - if (storage == nullptr) { - LOG_DEBUG(Service_AM, - "storage is a nullptr. There is no data in the current normal channel"); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(AM::ResultNoDataInChannel); - return; - } - - IPC::ResponseBuilder rb{ctx, 2, 0, 1}; - rb.Push(ResultSuccess); - rb.PushIpcInterface(std::move(storage)); - } - - void PushInteractiveInData(HLERequestContext& ctx) { - LOG_DEBUG(Service_AM, "called"); - - IPC::RequestParser rp{ctx}; - applet->GetBroker().PushInteractiveDataFromGame(rp.PopIpcInterface().lock()); - - ASSERT(applet->IsInitialized()); - applet->ExecuteInteractive(); - applet->Execute(); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); - } - - void PopInteractiveOutData(HLERequestContext& ctx) { - LOG_DEBUG(Service_AM, "called"); - - auto storage = applet->GetBroker().PopInteractiveDataToGame(); - if (storage == nullptr) { - LOG_DEBUG(Service_AM, - "storage is a nullptr. There is no data in the current interactive channel"); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(AM::ResultNoDataInChannel); - return; - } - - IPC::ResponseBuilder rb{ctx, 2, 0, 1}; - rb.Push(ResultSuccess); - rb.PushIpcInterface(std::move(storage)); - } - - void GetPopOutDataEvent(HLERequestContext& ctx) { - LOG_DEBUG(Service_AM, "called"); - - IPC::ResponseBuilder rb{ctx, 2, 1}; - rb.Push(ResultSuccess); - rb.PushCopyObjects(applet->GetBroker().GetNormalDataEvent()); - } - - void GetPopInteractiveOutDataEvent(HLERequestContext& ctx) { - LOG_DEBUG(Service_AM, "called"); - - IPC::ResponseBuilder rb{ctx, 2, 1}; - rb.Push(ResultSuccess); - rb.PushCopyObjects(applet->GetBroker().GetInteractiveDataEvent()); - } - - void GetIndirectLayerConsumerHandle(HLERequestContext& ctx) { - LOG_WARNING(Service_AM, "(STUBBED) called"); - - // We require a non-zero handle to be valid. Using 0xdeadbeef allows us to trace if this is - // actually used anywhere - constexpr u64 handle = 0xdeadbeef; - - IPC::ResponseBuilder rb{ctx, 4}; - rb.Push(ResultSuccess); - rb.Push(handle); - } - - std::shared_ptr applet; -}; - -IStorageAccessor::IStorageAccessor(Core::System& system_, IStorage& backing_) - : ServiceFramework{system_, "IStorageAccessor"}, backing{backing_} { - // clang-format off - static const FunctionInfo functions[] = { - {0, &IStorageAccessor::GetSize, "GetSize"}, - {10, &IStorageAccessor::Write, "Write"}, - {11, &IStorageAccessor::Read, "Read"}, - }; - // clang-format on - - RegisterHandlers(functions); -} - -IStorageAccessor::~IStorageAccessor() = default; - -void IStorageAccessor::GetSize(HLERequestContext& ctx) { - LOG_DEBUG(Service_AM, "called"); - - IPC::ResponseBuilder rb{ctx, 4}; - - rb.Push(ResultSuccess); - rb.Push(static_cast(backing.GetSize())); -} - -void IStorageAccessor::Write(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - - const u64 offset{rp.Pop()}; - const auto data{ctx.ReadBuffer()}; - const std::size_t size{std::min(data.size(), backing.GetSize() - offset)}; - - LOG_DEBUG(Service_AM, "called, offset={}, size={}", offset, size); - - if (offset > backing.GetSize()) { - LOG_ERROR(Service_AM, - "offset is out of bounds, backing_buffer_sz={}, data_size={}, offset={}", - backing.GetSize(), size, offset); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(AM::ResultInvalidOffset); - return; - } - - std::memcpy(backing.GetData().data() + offset, data.data(), size); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void IStorageAccessor::Read(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - - const u64 offset{rp.Pop()}; - const std::size_t size{std::min(ctx.GetWriteBufferSize(), backing.GetSize() - offset)}; - - LOG_DEBUG(Service_AM, "called, offset={}, size={}", offset, size); - - if (offset > backing.GetSize()) { - LOG_ERROR(Service_AM, "offset is out of bounds, backing_buffer_sz={}, size={}, offset={}", - backing.GetSize(), size, offset); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(AM::ResultInvalidOffset); - return; - } - - ctx.WriteBuffer(backing.GetData().data() + offset, size); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -ILibraryAppletCreator::ILibraryAppletCreator(Core::System& system_) - : ServiceFramework{system_, "ILibraryAppletCreator"} { - static const FunctionInfo functions[] = { - {0, &ILibraryAppletCreator::CreateLibraryApplet, "CreateLibraryApplet"}, - {1, nullptr, "TerminateAllLibraryApplets"}, - {2, nullptr, "AreAnyLibraryAppletsLeft"}, - {10, &ILibraryAppletCreator::CreateStorage, "CreateStorage"}, - {11, &ILibraryAppletCreator::CreateTransferMemoryStorage, "CreateTransferMemoryStorage"}, - {12, &ILibraryAppletCreator::CreateHandleStorage, "CreateHandleStorage"}, - }; - RegisterHandlers(functions); -} - -ILibraryAppletCreator::~ILibraryAppletCreator() = default; - -void ILibraryAppletCreator::CreateLibraryApplet(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - - const auto applet_id = rp.PopRaw(); - const auto applet_mode = rp.PopRaw(); - - LOG_DEBUG(Service_AM, "called with applet_id={:08X}, applet_mode={:08X}", applet_id, - applet_mode); - - const auto& applet_manager{system.GetAppletManager()}; - const auto applet = applet_manager.GetApplet(applet_id, applet_mode); - - if (applet == nullptr) { - LOG_ERROR(Service_AM, "Applet doesn't exist! applet_id={}", applet_id); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultUnknown); - return; - } - - IPC::ResponseBuilder rb{ctx, 2, 0, 1}; - - rb.Push(ResultSuccess); - rb.PushIpcInterface(system, applet); -} - -void ILibraryAppletCreator::CreateStorage(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - - const s64 size{rp.Pop()}; - - LOG_DEBUG(Service_AM, "called, size={}", size); - - if (size <= 0) { - LOG_ERROR(Service_AM, "size is less than or equal to 0"); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultUnknown); - return; - } - - std::vector buffer(size); - - IPC::ResponseBuilder rb{ctx, 2, 0, 1}; - rb.Push(ResultSuccess); - rb.PushIpcInterface(system, std::move(buffer)); -} - -void ILibraryAppletCreator::CreateTransferMemoryStorage(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - - struct Parameters { - u8 permissions; - s64 size; - }; - - const auto parameters{rp.PopRaw()}; - const auto handle{ctx.GetCopyHandle(0)}; - - LOG_DEBUG(Service_AM, "called, permissions={}, size={}, handle={:08X}", parameters.permissions, - parameters.size, handle); - - if (parameters.size <= 0) { - LOG_ERROR(Service_AM, "size is less than or equal to 0"); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultUnknown); - return; - } - - auto transfer_mem = ctx.GetObjectFromHandle(handle); - - if (transfer_mem.IsNull()) { - LOG_ERROR(Service_AM, "transfer_mem is a nullptr for handle={:08X}", handle); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultUnknown); - return; - } - - std::vector memory(transfer_mem->GetSize()); - ctx.GetMemory().ReadBlock(transfer_mem->GetSourceAddress(), memory.data(), memory.size()); - - IPC::ResponseBuilder rb{ctx, 2, 0, 1}; - rb.Push(ResultSuccess); - rb.PushIpcInterface(system, std::move(memory)); -} - -void ILibraryAppletCreator::CreateHandleStorage(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - - const s64 size{rp.Pop()}; - const auto handle{ctx.GetCopyHandle(0)}; - - LOG_DEBUG(Service_AM, "called, size={}, handle={:08X}", size, handle); - - if (size <= 0) { - LOG_ERROR(Service_AM, "size is less than or equal to 0"); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultUnknown); - return; - } - - auto transfer_mem = ctx.GetObjectFromHandle(handle); - - if (transfer_mem.IsNull()) { - LOG_ERROR(Service_AM, "transfer_mem is a nullptr for handle={:08X}", handle); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultUnknown); - return; - } - - std::vector memory(transfer_mem->GetSize()); - ctx.GetMemory().ReadBlock(transfer_mem->GetSourceAddress(), memory.data(), memory.size()); - - IPC::ResponseBuilder rb{ctx, 2, 0, 1}; - rb.Push(ResultSuccess); - rb.PushIpcInterface(system, std::move(memory)); -} - -ILibraryAppletSelfAccessor::ILibraryAppletSelfAccessor(Core::System& system_) - : ServiceFramework{system_, "ILibraryAppletSelfAccessor"} { - // clang-format off - static const FunctionInfo functions[] = { - {0, &ILibraryAppletSelfAccessor::PopInData, "PopInData"}, - {1, &ILibraryAppletSelfAccessor::PushOutData, "PushOutData"}, - {2, nullptr, "PopInteractiveInData"}, - {3, nullptr, "PushInteractiveOutData"}, - {5, nullptr, "GetPopInDataEvent"}, - {6, nullptr, "GetPopInteractiveInDataEvent"}, - {10, &ILibraryAppletSelfAccessor::ExitProcessAndReturn, "ExitProcessAndReturn"}, - {11, &ILibraryAppletSelfAccessor::GetLibraryAppletInfo, "GetLibraryAppletInfo"}, - {12, &ILibraryAppletSelfAccessor::GetMainAppletIdentityInfo, "GetMainAppletIdentityInfo"}, - {13, nullptr, "CanUseApplicationCore"}, - {14, &ILibraryAppletSelfAccessor::GetCallerAppletIdentityInfo, "GetCallerAppletIdentityInfo"}, - {15, nullptr, "GetMainAppletApplicationControlProperty"}, - {16, nullptr, "GetMainAppletStorageId"}, - {17, nullptr, "GetCallerAppletIdentityInfoStack"}, - {18, nullptr, "GetNextReturnDestinationAppletIdentityInfo"}, - {19, &ILibraryAppletSelfAccessor::GetDesirableKeyboardLayout, "GetDesirableKeyboardLayout"}, - {20, nullptr, "PopExtraStorage"}, - {25, nullptr, "GetPopExtraStorageEvent"}, - {30, nullptr, "UnpopInData"}, - {31, nullptr, "UnpopExtraStorage"}, - {40, nullptr, "GetIndirectLayerProducerHandle"}, - {50, nullptr, "ReportVisibleError"}, - {51, nullptr, "ReportVisibleErrorWithErrorContext"}, - {60, nullptr, "GetMainAppletApplicationDesiredLanguage"}, - {70, nullptr, "GetCurrentApplicationId"}, - {80, nullptr, "RequestExitToSelf"}, - {90, nullptr, "CreateApplicationAndPushAndRequestToLaunch"}, - {100, nullptr, "CreateGameMovieTrimmer"}, - {101, nullptr, "ReserveResourceForMovieOperation"}, - {102, nullptr, "UnreserveResourceForMovieOperation"}, - {110, &ILibraryAppletSelfAccessor::GetMainAppletAvailableUsers, "GetMainAppletAvailableUsers"}, - {120, nullptr, "GetLaunchStorageInfoForDebug"}, - {130, nullptr, "GetGpuErrorDetectedSystemEvent"}, - {140, nullptr, "SetApplicationMemoryReservation"}, - {150, &ILibraryAppletSelfAccessor::ShouldSetGpuTimeSliceManually, "ShouldSetGpuTimeSliceManually"}, - }; - // clang-format on - RegisterHandlers(functions); - - switch (system.GetAppletManager().GetCurrentAppletId()) { - case Applets::AppletId::Cabinet: - PushInShowCabinetData(); - break; - case Applets::AppletId::MiiEdit: - PushInShowMiiEditData(); - break; - case Applets::AppletId::PhotoViewer: - PushInShowAlbum(); - break; - case Applets::AppletId::SoftwareKeyboard: - PushInShowSoftwareKeyboard(); - break; - case Applets::AppletId::Controller: - PushInShowController(); - break; - default: - break; - } -} - -ILibraryAppletSelfAccessor::~ILibraryAppletSelfAccessor() = default; -void ILibraryAppletSelfAccessor::PopInData(HLERequestContext& ctx) { - LOG_INFO(Service_AM, "called"); - - if (queue_data.empty()) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultNoDataInChannel); - return; - } - - auto data = queue_data.front(); - queue_data.pop_front(); - - IPC::ResponseBuilder rb{ctx, 2, 0, 1}; - rb.Push(ResultSuccess); - rb.PushIpcInterface(system, std::move(data)); -} - -void ILibraryAppletSelfAccessor::PushOutData(HLERequestContext& ctx) { - LOG_WARNING(Service_AM, "(STUBBED) called"); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void ILibraryAppletSelfAccessor::ExitProcessAndReturn(HLERequestContext& ctx) { - LOG_WARNING(Service_AM, "(STUBBED) called"); - - system.Exit(); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void ILibraryAppletSelfAccessor::GetLibraryAppletInfo(HLERequestContext& ctx) { - struct LibraryAppletInfo { - Applets::AppletId applet_id; - Applets::LibraryAppletMode library_applet_mode; - }; - - LOG_WARNING(Service_AM, "(STUBBED) called"); - - const LibraryAppletInfo applet_info{ - .applet_id = system.GetAppletManager().GetCurrentAppletId(), - .library_applet_mode = Applets::LibraryAppletMode::AllForeground, - }; - - IPC::ResponseBuilder rb{ctx, 4}; - rb.Push(ResultSuccess); - rb.PushRaw(applet_info); -} - -void ILibraryAppletSelfAccessor::GetMainAppletIdentityInfo(HLERequestContext& ctx) { - struct AppletIdentityInfo { - Applets::AppletId applet_id; - INSERT_PADDING_BYTES(0x4); - u64 application_id; - }; - static_assert(sizeof(AppletIdentityInfo) == 0x10, "AppletIdentityInfo has incorrect size."); - - LOG_WARNING(Service_AM, "(STUBBED) called"); - - const AppletIdentityInfo applet_info{ - .applet_id = Applets::AppletId::QLaunch, - .application_id = 0x0100000000001000ull, - }; - - IPC::ResponseBuilder rb{ctx, 6}; - rb.Push(ResultSuccess); - rb.PushRaw(applet_info); -} - -void ILibraryAppletSelfAccessor::GetCallerAppletIdentityInfo(HLERequestContext& ctx) { - struct AppletIdentityInfo { - Applets::AppletId applet_id; - INSERT_PADDING_BYTES(0x4); - u64 application_id; - }; - static_assert(sizeof(AppletIdentityInfo) == 0x10, "AppletIdentityInfo has incorrect size."); - LOG_WARNING(Service_AM, "(STUBBED) called"); - - const AppletIdentityInfo applet_info{ - .applet_id = Applets::AppletId::QLaunch, - .application_id = 0x0100000000001000ull, - }; - - IPC::ResponseBuilder rb{ctx, 6}; - rb.Push(ResultSuccess); - rb.PushRaw(applet_info); -} - -void ILibraryAppletSelfAccessor::GetDesirableKeyboardLayout(HLERequestContext& ctx) { - LOG_WARNING(Service_AM, "(STUBBED) called"); - - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(ResultSuccess); - rb.Push(0); -} - -void ILibraryAppletSelfAccessor::GetMainAppletAvailableUsers(HLERequestContext& ctx) { - const Service::Account::ProfileManager manager{}; - bool is_empty{true}; - s32 user_count{-1}; - - LOG_INFO(Service_AM, "called"); - - if (manager.GetUserCount() > 0) { - is_empty = false; - user_count = static_cast(manager.GetUserCount()); - ctx.WriteBuffer(manager.GetAllUsers()); - } - - IPC::ResponseBuilder rb{ctx, 4}; - rb.Push(ResultSuccess); - rb.Push(is_empty); - rb.Push(user_count); -} - -void ILibraryAppletSelfAccessor::ShouldSetGpuTimeSliceManually(HLERequestContext& ctx) { - LOG_WARNING(Service_AM, "(STUBBED) called"); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); - rb.Push(0); -} - -void ILibraryAppletSelfAccessor::PushInShowAlbum() { - const Applets::CommonArguments arguments{ - .arguments_version = Applets::CommonArgumentVersion::Version3, - .size = Applets::CommonArgumentSize::Version3, - .library_version = 1, - .theme_color = Applets::ThemeColor::BasicBlack, - .play_startup_sound = true, - .system_tick = system.CoreTiming().GetClockTicks(), - }; - - std::vector argument_data(sizeof(arguments)); - std::vector settings_data{2}; - std::memcpy(argument_data.data(), &arguments, sizeof(arguments)); - queue_data.emplace_back(std::move(argument_data)); - queue_data.emplace_back(std::move(settings_data)); -} - -void ILibraryAppletSelfAccessor::PushInShowController() { - const Applets::CommonArguments common_args = { - .arguments_version = Applets::CommonArgumentVersion::Version3, - .size = Applets::CommonArgumentSize::Version3, - .library_version = static_cast(Applets::ControllerAppletVersion::Version8), - .theme_color = Applets::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 common_args_data(sizeof(common_args)); - std::vector private_args_data(sizeof(private_args)); - std::vector 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)); - - queue_data.emplace_back(std::move(common_args_data)); - queue_data.emplace_back(std::move(private_args_data)); - queue_data.emplace_back(std::move(user_args_data)); -} - -void ILibraryAppletSelfAccessor::PushInShowCabinetData() { - const Applets::CommonArguments arguments{ - .arguments_version = Applets::CommonArgumentVersion::Version3, - .size = Applets::CommonArgumentSize::Version3, - .library_version = static_cast(Applets::CabinetAppletVersion::Version1), - .theme_color = Applets::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 argument_data(sizeof(arguments)); - std::vector settings_data(sizeof(amiibo_settings)); - std::memcpy(argument_data.data(), &arguments, sizeof(arguments)); - std::memcpy(settings_data.data(), &amiibo_settings, sizeof(amiibo_settings)); - queue_data.emplace_back(std::move(argument_data)); - queue_data.emplace_back(std::move(settings_data)); -} - -void ILibraryAppletSelfAccessor::PushInShowMiiEditData() { - 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 argument_data(sizeof(mii_arguments)); - std::memcpy(argument_data.data(), &mii_arguments, sizeof(mii_arguments)); - - queue_data.emplace_back(std::move(argument_data)); -} - -void ILibraryAppletSelfAccessor::PushInShowSoftwareKeyboard() { - const Applets::CommonArguments arguments{ - .arguments_version = Applets::CommonArgumentVersion::Version3, - .size = Applets::CommonArgumentSize::Version3, - .library_version = static_cast(Applets::SwkbdAppletVersion::Version524301), - .theme_color = Applets::ThemeColor::BasicBlack, - .play_startup_sound = true, - .system_tick = system.CoreTiming().GetClockTicks(), - }; - - std::vector 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(initial_string.size()), - .user_dictionary_offset{}, - .user_dictionary_entries{}, - .use_text_check = false, - }; - - Applets::SwkbdConfigNew swkbd_config_new{}; - - std::vector argument_data(sizeof(arguments)); - std::vector swkbd_data(sizeof(swkbd_config) + sizeof(swkbd_config_new)); - std::vector 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)); - - queue_data.emplace_back(std::move(argument_data)); - queue_data.emplace_back(std::move(swkbd_data)); - queue_data.emplace_back(std::move(work_buffer)); -} - -IAppletCommonFunctions::IAppletCommonFunctions(Core::System& system_) - : ServiceFramework{system_, "IAppletCommonFunctions"} { - // clang-format off - static const FunctionInfo functions[] = { - {0, nullptr, "SetTerminateResult"}, - {10, nullptr, "ReadThemeStorage"}, - {11, nullptr, "WriteThemeStorage"}, - {20, nullptr, "PushToAppletBoundChannel"}, - {21, nullptr, "TryPopFromAppletBoundChannel"}, - {40, nullptr, "GetDisplayLogicalResolution"}, - {42, nullptr, "SetDisplayMagnification"}, - {50, nullptr, "SetHomeButtonDoubleClickEnabled"}, - {51, nullptr, "GetHomeButtonDoubleClickEnabled"}, - {52, nullptr, "IsHomeButtonShortPressedBlocked"}, - {60, nullptr, "IsVrModeCurtainRequired"}, - {61, nullptr, "IsSleepRequiredByHighTemperature"}, - {62, nullptr, "IsSleepRequiredByLowBattery"}, - {70, &IAppletCommonFunctions::SetCpuBoostRequestPriority, "SetCpuBoostRequestPriority"}, - {80, nullptr, "SetHandlingCaptureButtonShortPressedMessageEnabledForApplet"}, - {81, nullptr, "SetHandlingCaptureButtonLongPressedMessageEnabledForApplet"}, - {90, nullptr, "OpenNamedChannelAsParent"}, - {91, nullptr, "OpenNamedChannelAsChild"}, - {100, nullptr, "SetApplicationCoreUsageMode"}, - }; - // clang-format on - - RegisterHandlers(functions); -} - -IAppletCommonFunctions::~IAppletCommonFunctions() = default; - -void IAppletCommonFunctions::SetCpuBoostRequestPriority(HLERequestContext& ctx) { - LOG_WARNING(Service_AM, "(STUBBED) called"); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -IApplicationFunctions::IApplicationFunctions(Core::System& system_) - : ServiceFramework{system_, "IApplicationFunctions"}, service_context{system, - "IApplicationFunctions"} { - // clang-format off - static const FunctionInfo functions[] = { - {1, &IApplicationFunctions::PopLaunchParameter, "PopLaunchParameter"}, - {10, nullptr, "CreateApplicationAndPushAndRequestToStart"}, - {11, nullptr, "CreateApplicationAndPushAndRequestToStartForQuest"}, - {12, nullptr, "CreateApplicationAndRequestToStart"}, - {13, &IApplicationFunctions::CreateApplicationAndRequestToStartForQuest, "CreateApplicationAndRequestToStartForQuest"}, - {14, nullptr, "CreateApplicationWithAttributeAndPushAndRequestToStartForQuest"}, - {15, nullptr, "CreateApplicationWithAttributeAndRequestToStartForQuest"}, - {20, &IApplicationFunctions::EnsureSaveData, "EnsureSaveData"}, - {21, &IApplicationFunctions::GetDesiredLanguage, "GetDesiredLanguage"}, - {22, &IApplicationFunctions::SetTerminateResult, "SetTerminateResult"}, - {23, &IApplicationFunctions::GetDisplayVersion, "GetDisplayVersion"}, - {24, nullptr, "GetLaunchStorageInfoForDebug"}, - {25, &IApplicationFunctions::ExtendSaveData, "ExtendSaveData"}, - {26, &IApplicationFunctions::GetSaveDataSize, "GetSaveDataSize"}, - {27, &IApplicationFunctions::CreateCacheStorage, "CreateCacheStorage"}, - {28, &IApplicationFunctions::GetSaveDataSizeMax, "GetSaveDataSizeMax"}, - {29, nullptr, "GetCacheStorageMax"}, - {30, &IApplicationFunctions::BeginBlockingHomeButtonShortAndLongPressed, "BeginBlockingHomeButtonShortAndLongPressed"}, - {31, &IApplicationFunctions::EndBlockingHomeButtonShortAndLongPressed, "EndBlockingHomeButtonShortAndLongPressed"}, - {32, &IApplicationFunctions::BeginBlockingHomeButton, "BeginBlockingHomeButton"}, - {33, &IApplicationFunctions::EndBlockingHomeButton, "EndBlockingHomeButton"}, - {34, nullptr, "SelectApplicationLicense"}, - {35, nullptr, "GetDeviceSaveDataSizeMax"}, - {36, nullptr, "GetLimitedApplicationLicense"}, - {37, nullptr, "GetLimitedApplicationLicenseUpgradableEvent"}, - {40, &IApplicationFunctions::NotifyRunning, "NotifyRunning"}, - {50, &IApplicationFunctions::GetPseudoDeviceId, "GetPseudoDeviceId"}, - {60, nullptr, "SetMediaPlaybackStateForApplication"}, - {65, &IApplicationFunctions::IsGamePlayRecordingSupported, "IsGamePlayRecordingSupported"}, - {66, &IApplicationFunctions::InitializeGamePlayRecording, "InitializeGamePlayRecording"}, - {67, &IApplicationFunctions::SetGamePlayRecordingState, "SetGamePlayRecordingState"}, - {68, nullptr, "RequestFlushGamePlayingMovieForDebug"}, - {70, nullptr, "RequestToShutdown"}, - {71, nullptr, "RequestToReboot"}, - {72, nullptr, "RequestToSleep"}, - {80, nullptr, "ExitAndRequestToShowThanksMessage"}, - {90, &IApplicationFunctions::EnableApplicationCrashReport, "EnableApplicationCrashReport"}, - {100, &IApplicationFunctions::InitializeApplicationCopyrightFrameBuffer, "InitializeApplicationCopyrightFrameBuffer"}, - {101, &IApplicationFunctions::SetApplicationCopyrightImage, "SetApplicationCopyrightImage"}, - {102, &IApplicationFunctions::SetApplicationCopyrightVisibility, "SetApplicationCopyrightVisibility"}, - {110, &IApplicationFunctions::QueryApplicationPlayStatistics, "QueryApplicationPlayStatistics"}, - {111, &IApplicationFunctions::QueryApplicationPlayStatisticsByUid, "QueryApplicationPlayStatisticsByUid"}, - {120, &IApplicationFunctions::ExecuteProgram, "ExecuteProgram"}, - {121, &IApplicationFunctions::ClearUserChannel, "ClearUserChannel"}, - {122, &IApplicationFunctions::UnpopToUserChannel, "UnpopToUserChannel"}, - {123, &IApplicationFunctions::GetPreviousProgramIndex, "GetPreviousProgramIndex"}, - {124, nullptr, "EnableApplicationAllThreadDumpOnCrash"}, - {130, &IApplicationFunctions::GetGpuErrorDetectedSystemEvent, "GetGpuErrorDetectedSystemEvent"}, - {131, nullptr, "SetDelayTimeToAbortOnGpuError"}, - {140, &IApplicationFunctions::GetFriendInvitationStorageChannelEvent, "GetFriendInvitationStorageChannelEvent"}, - {141, &IApplicationFunctions::TryPopFromFriendInvitationStorageChannel, "TryPopFromFriendInvitationStorageChannel"}, - {150, &IApplicationFunctions::GetNotificationStorageChannelEvent, "GetNotificationStorageChannelEvent"}, - {151, nullptr, "TryPopFromNotificationStorageChannel"}, - {160, &IApplicationFunctions::GetHealthWarningDisappearedSystemEvent, "GetHealthWarningDisappearedSystemEvent"}, - {170, nullptr, "SetHdcpAuthenticationActivated"}, - {180, nullptr, "GetLaunchRequiredVersion"}, - {181, nullptr, "UpgradeLaunchRequiredVersion"}, - {190, nullptr, "SendServerMaintenanceOverlayNotification"}, - {200, nullptr, "GetLastApplicationExitReason"}, - {500, nullptr, "StartContinuousRecordingFlushForDebug"}, - {1000, nullptr, "CreateMovieMaker"}, - {1001, &IApplicationFunctions::PrepareForJit, "PrepareForJit"}, - }; - // clang-format on - - RegisterHandlers(functions); - - gpu_error_detected_event = - service_context.CreateEvent("IApplicationFunctions:GpuErrorDetectedSystemEvent"); - friend_invitation_storage_channel_event = - service_context.CreateEvent("IApplicationFunctions:FriendInvitationStorageChannelEvent"); - notification_storage_channel_event = - service_context.CreateEvent("IApplicationFunctions:NotificationStorageChannelEvent"); - health_warning_disappeared_system_event = - service_context.CreateEvent("IApplicationFunctions:HealthWarningDisappearedSystemEvent"); -} - -IApplicationFunctions::~IApplicationFunctions() { - service_context.CloseEvent(gpu_error_detected_event); - service_context.CloseEvent(friend_invitation_storage_channel_event); - service_context.CloseEvent(notification_storage_channel_event); - service_context.CloseEvent(health_warning_disappeared_system_event); -} - -void IApplicationFunctions::EnableApplicationCrashReport(HLERequestContext& ctx) { - LOG_WARNING(Service_AM, "(STUBBED) called"); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void IApplicationFunctions::InitializeApplicationCopyrightFrameBuffer(HLERequestContext& ctx) { - LOG_WARNING(Service_AM, "(STUBBED) called"); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void IApplicationFunctions::SetApplicationCopyrightImage(HLERequestContext& ctx) { - LOG_WARNING(Service_AM, "(STUBBED) called"); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void IApplicationFunctions::SetApplicationCopyrightVisibility(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto is_visible = rp.Pop(); - - LOG_WARNING(Service_AM, "(STUBBED) called, is_visible={}", is_visible); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void IApplicationFunctions::BeginBlockingHomeButtonShortAndLongPressed(HLERequestContext& ctx) { - LOG_WARNING(Service_AM, "(STUBBED) called"); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void IApplicationFunctions::EndBlockingHomeButtonShortAndLongPressed(HLERequestContext& ctx) { - LOG_WARNING(Service_AM, "(STUBBED) called"); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void IApplicationFunctions::BeginBlockingHomeButton(HLERequestContext& ctx) { - LOG_WARNING(Service_AM, "(STUBBED) called"); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void IApplicationFunctions::EndBlockingHomeButton(HLERequestContext& ctx) { - LOG_WARNING(Service_AM, "(STUBBED) called"); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void IApplicationFunctions::PopLaunchParameter(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto kind = rp.PopEnum(); - - LOG_INFO(Service_AM, "called, kind={:08X}", kind); - - if (kind == LaunchParameterKind::UserChannel) { - auto channel = system.GetUserChannel(); - if (channel.empty()) { - LOG_ERROR(Service_AM, "Attempted to load launch parameter but none was found!"); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(AM::ResultNoDataInChannel); - return; - } - - auto data = channel.back(); - channel.pop_back(); - - IPC::ResponseBuilder rb{ctx, 2, 0, 1}; - rb.Push(ResultSuccess); - rb.PushIpcInterface(system, std::move(data)); - } else if (kind == LaunchParameterKind::AccountPreselectedUser && - !launch_popped_account_preselect) { - // TODO: Verify this is hw-accurate - LaunchParameterAccountPreselectedUser params{}; - - params.magic = LAUNCH_PARAMETER_ACCOUNT_PRESELECTED_USER_MAGIC; - params.is_account_selected = 1; - - Account::ProfileManager profile_manager{}; - const auto uuid = profile_manager.GetUser(static_cast(Settings::values.current_user)); - ASSERT(uuid.has_value() && uuid->IsValid()); - params.current_user = *uuid; - - IPC::ResponseBuilder rb{ctx, 2, 0, 1}; - rb.Push(ResultSuccess); - - std::vector buffer(sizeof(LaunchParameterAccountPreselectedUser)); - std::memcpy(buffer.data(), ¶ms, buffer.size()); - - rb.PushIpcInterface(system, std::move(buffer)); - launch_popped_account_preselect = true; - } else { - LOG_ERROR(Service_AM, "Unknown launch parameter kind."); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(AM::ResultNoDataInChannel); - } -} - -void IApplicationFunctions::CreateApplicationAndRequestToStartForQuest(HLERequestContext& ctx) { - LOG_WARNING(Service_AM, "(STUBBED) called"); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void IApplicationFunctions::EnsureSaveData(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - u128 user_id = rp.PopRaw(); - - LOG_DEBUG(Service_AM, "called, uid={:016X}{:016X}", user_id[1], user_id[0]); - - FileSys::SaveDataAttribute attribute{}; - attribute.title_id = system.GetApplicationProcessProgramID(); - attribute.user_id = user_id; - attribute.type = FileSys::SaveDataType::SaveData; - - FileSys::VirtualDir save_data{}; - const auto res = system.GetFileSystemController().OpenSaveDataController()->CreateSaveData( - &save_data, FileSys::SaveDataSpaceId::NandUser, attribute); - - IPC::ResponseBuilder rb{ctx, 4}; - rb.Push(res); - rb.Push(0); -} - -void IApplicationFunctions::SetTerminateResult(HLERequestContext& ctx) { - // Takes an input u32 Result, no output. - // For example, in some cases official apps use this with error 0x2A2 then - // uses svcBreak. - - IPC::RequestParser rp{ctx}; - u32 result = rp.Pop(); - LOG_WARNING(Service_AM, "(STUBBED) called, result=0x{:08X}", result); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void IApplicationFunctions::GetDisplayVersion(HLERequestContext& ctx) { - LOG_DEBUG(Service_AM, "called"); - - std::array version_string{}; - - const auto res = [this] { - const auto title_id = system.GetApplicationProcessProgramID(); - - const FileSys::PatchManager pm{title_id, system.GetFileSystemController(), - system.GetContentProvider()}; - auto metadata = pm.GetControlMetadata(); - if (metadata.first != nullptr) { - return metadata; - } - - const FileSys::PatchManager pm_update{FileSys::GetUpdateTitleID(title_id), - system.GetFileSystemController(), - system.GetContentProvider()}; - return pm_update.GetControlMetadata(); - }(); - - if (res.first != nullptr) { - const auto& version = res.first->GetVersionString(); - std::copy(version.begin(), version.end(), version_string.begin()); - } else { - static constexpr char default_version[]{"1.0.0"}; - std::memcpy(version_string.data(), default_version, sizeof(default_version)); - } - - IPC::ResponseBuilder rb{ctx, 6}; - rb.Push(ResultSuccess); - rb.PushRaw(version_string); -} - -void IApplicationFunctions::GetDesiredLanguage(HLERequestContext& ctx) { - // TODO(bunnei): This should be configurable - LOG_DEBUG(Service_AM, "called"); - - // Get supported languages from NACP, if possible - // Default to 0 (all languages supported) - u32 supported_languages = 0; - - const auto res = [this] { - const auto title_id = system.GetApplicationProcessProgramID(); - - const FileSys::PatchManager pm{title_id, system.GetFileSystemController(), - system.GetContentProvider()}; - auto metadata = pm.GetControlMetadata(); - if (metadata.first != nullptr) { - return metadata; - } - - const FileSys::PatchManager pm_update{FileSys::GetUpdateTitleID(title_id), - system.GetFileSystemController(), - system.GetContentProvider()}; - return pm_update.GetControlMetadata(); - }(); - - if (res.first != nullptr) { - supported_languages = res.first->GetSupportedLanguages(); - } - - // Call IApplicationManagerInterface implementation. - auto& service_manager = system.ServiceManager(); - auto ns_am2 = service_manager.GetService("ns:am2"); - auto app_man = ns_am2->GetApplicationManagerInterface(); - - // Get desired application language - u8 desired_language{}; - const auto res_lang = - app_man->GetApplicationDesiredLanguage(&desired_language, supported_languages); - if (res_lang != ResultSuccess) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(res_lang); - return; - } - - // Convert to settings language code. - u64 language_code{}; - const auto res_code = - app_man->ConvertApplicationLanguageToLanguageCode(&language_code, desired_language); - if (res_code != ResultSuccess) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(res_code); - return; - } - - LOG_DEBUG(Service_AM, "got desired_language={:016X}", language_code); - - IPC::ResponseBuilder rb{ctx, 4}; - rb.Push(ResultSuccess); - rb.Push(language_code); -} - -void IApplicationFunctions::IsGamePlayRecordingSupported(HLERequestContext& ctx) { - LOG_WARNING(Service_AM, "(STUBBED) called"); - - constexpr bool gameplay_recording_supported = false; - - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(ResultSuccess); - rb.Push(gameplay_recording_supported); -} - -void IApplicationFunctions::InitializeGamePlayRecording(HLERequestContext& ctx) { - LOG_WARNING(Service_AM, "(STUBBED) called"); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void IApplicationFunctions::SetGamePlayRecordingState(HLERequestContext& ctx) { - LOG_WARNING(Service_AM, "(STUBBED) called"); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void IApplicationFunctions::NotifyRunning(HLERequestContext& ctx) { - LOG_WARNING(Service_AM, "(STUBBED) called"); - - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(ResultSuccess); - rb.Push(0); // Unknown, seems to be ignored by official processes -} - -void IApplicationFunctions::GetPseudoDeviceId(HLERequestContext& ctx) { - LOG_WARNING(Service_AM, "(STUBBED) called"); - - IPC::ResponseBuilder rb{ctx, 6}; - rb.Push(ResultSuccess); - - // Returns a 128-bit UUID - rb.Push(0); - rb.Push(0); -} - -void IApplicationFunctions::ExtendSaveData(HLERequestContext& ctx) { - struct Parameters { - FileSys::SaveDataType type; - u128 user_id; - u64 new_normal_size; - u64 new_journal_size; - }; - static_assert(sizeof(Parameters) == 40); - - IPC::RequestParser rp{ctx}; - const auto [type, user_id, new_normal_size, new_journal_size] = rp.PopRaw(); - - LOG_DEBUG(Service_AM, - "called with type={:02X}, user_id={:016X}{:016X}, new_normal={:016X}, " - "new_journal={:016X}", - static_cast(type), user_id[1], user_id[0], new_normal_size, new_journal_size); - - system.GetFileSystemController().OpenSaveDataController()->WriteSaveDataSize( - type, system.GetApplicationProcessProgramID(), user_id, - {new_normal_size, new_journal_size}); - - IPC::ResponseBuilder rb{ctx, 4}; - rb.Push(ResultSuccess); - - // The following value is used upon failure to help the system recover. - // Since we always succeed, this should be 0. - rb.Push(0); -} - -void IApplicationFunctions::GetSaveDataSize(HLERequestContext& ctx) { - struct Parameters { - FileSys::SaveDataType type; - u128 user_id; - }; - static_assert(sizeof(Parameters) == 24); - - IPC::RequestParser rp{ctx}; - const auto [type, user_id] = rp.PopRaw(); - - LOG_DEBUG(Service_AM, "called with type={:02X}, user_id={:016X}{:016X}", type, user_id[1], - user_id[0]); - - const auto size = system.GetFileSystemController().OpenSaveDataController()->ReadSaveDataSize( - type, system.GetApplicationProcessProgramID(), user_id); - - IPC::ResponseBuilder rb{ctx, 6}; - rb.Push(ResultSuccess); - rb.Push(size.normal); - rb.Push(size.journal); -} - -void IApplicationFunctions::CreateCacheStorage(HLERequestContext& ctx) { - struct InputParameters { - u16 index; - s64 size; - s64 journal_size; - }; - static_assert(sizeof(InputParameters) == 24); - - struct OutputParameters { - u32 storage_target; - u64 required_size; - }; - static_assert(sizeof(OutputParameters) == 16); - - IPC::RequestParser rp{ctx}; - const auto params = rp.PopRaw(); - - LOG_WARNING(Service_AM, "(STUBBED) called with index={}, size={:#x}, journal_size={:#x}", - params.index, params.size, params.journal_size); - - const OutputParameters resp{ - .storage_target = 1, - .required_size = 0, - }; - - IPC::ResponseBuilder rb{ctx, 6}; - rb.Push(ResultSuccess); - rb.PushRaw(resp); -} - -void IApplicationFunctions::GetSaveDataSizeMax(HLERequestContext& ctx) { - LOG_WARNING(Service_AM, "(STUBBED) called"); - - constexpr u64 size_max_normal = 0xFFFFFFF; - constexpr u64 size_max_journal = 0xFFFFFFF; - - IPC::ResponseBuilder rb{ctx, 6}; - rb.Push(ResultSuccess); - rb.Push(size_max_normal); - rb.Push(size_max_journal); -} - -void IApplicationFunctions::QueryApplicationPlayStatistics(HLERequestContext& ctx) { - LOG_WARNING(Service_AM, "(STUBBED) called"); - - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(ResultSuccess); - rb.Push(0); -} - -void IApplicationFunctions::QueryApplicationPlayStatisticsByUid(HLERequestContext& ctx) { - LOG_WARNING(Service_AM, "(STUBBED) called"); - - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(ResultSuccess); - rb.Push(0); -} - -void IApplicationFunctions::ExecuteProgram(HLERequestContext& ctx) { - LOG_WARNING(Service_AM, "(STUBBED) called"); - - IPC::RequestParser rp{ctx}; - [[maybe_unused]] const auto unk_1 = rp.Pop(); - [[maybe_unused]] const auto unk_2 = rp.Pop(); - const auto program_index = rp.Pop(); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); - - system.ExecuteProgram(program_index); -} - -void IApplicationFunctions::ClearUserChannel(HLERequestContext& ctx) { - LOG_DEBUG(Service_AM, "called"); - - system.GetUserChannel().clear(); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void IApplicationFunctions::UnpopToUserChannel(HLERequestContext& ctx) { - LOG_DEBUG(Service_AM, "called"); - - IPC::RequestParser rp{ctx}; - const auto storage = rp.PopIpcInterface().lock(); - if (storage) { - system.GetUserChannel().push_back(storage->GetData()); - } - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void IApplicationFunctions::GetPreviousProgramIndex(HLERequestContext& ctx) { - LOG_WARNING(Service_AM, "(STUBBED) called"); - - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(ResultSuccess); - rb.Push(previous_program_index); -} - -void IApplicationFunctions::GetGpuErrorDetectedSystemEvent(HLERequestContext& ctx) { - LOG_WARNING(Service_AM, "(STUBBED) called"); - - IPC::ResponseBuilder rb{ctx, 2, 1}; - rb.Push(ResultSuccess); - rb.PushCopyObjects(gpu_error_detected_event->GetReadableEvent()); -} - -void IApplicationFunctions::GetFriendInvitationStorageChannelEvent(HLERequestContext& ctx) { - LOG_DEBUG(Service_AM, "called"); - - IPC::ResponseBuilder rb{ctx, 2, 1}; - rb.Push(ResultSuccess); - rb.PushCopyObjects(friend_invitation_storage_channel_event->GetReadableEvent()); -} - -void IApplicationFunctions::TryPopFromFriendInvitationStorageChannel(HLERequestContext& ctx) { - LOG_DEBUG(Service_AM, "(STUBBED) called"); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(AM::ResultNoDataInChannel); -} - -void IApplicationFunctions::GetNotificationStorageChannelEvent(HLERequestContext& ctx) { - LOG_DEBUG(Service_AM, "called"); - - IPC::ResponseBuilder rb{ctx, 2, 1}; - rb.Push(ResultSuccess); - rb.PushCopyObjects(notification_storage_channel_event->GetReadableEvent()); -} - -void IApplicationFunctions::GetHealthWarningDisappearedSystemEvent(HLERequestContext& ctx) { - LOG_DEBUG(Service_AM, "called"); - - IPC::ResponseBuilder rb{ctx, 2, 1}; - rb.Push(ResultSuccess); - rb.PushCopyObjects(health_warning_disappeared_system_event->GetReadableEvent()); -} - -void IApplicationFunctions::PrepareForJit(HLERequestContext& ctx) { - LOG_WARNING(Service_AM, "(STUBBED) called"); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - void LoopProcess(Nvnflinger::Nvnflinger& nvnflinger, Core::System& system) { - auto message_queue = std::make_shared(system); auto server_manager = std::make_unique(system); - server_manager->RegisterNamedService( - "appletAE", std::make_shared(nvnflinger, message_queue, system)); - server_manager->RegisterNamedService( - "appletOE", std::make_shared(nvnflinger, message_queue, system)); + server_manager->RegisterNamedService("appletAE", + std::make_shared(nvnflinger, system)); + server_manager->RegisterNamedService("appletOE", + std::make_shared(nvnflinger, system)); server_manager->RegisterNamedService("idle:sys", std::make_shared(system)); server_manager->RegisterNamedService("omm", std::make_shared(system)); server_manager->RegisterNamedService("spsm", std::make_shared(system)); ServerManager::RunServer(std::move(server_manager)); } -IHomeMenuFunctions::IHomeMenuFunctions(Core::System& system_) - : ServiceFramework{system_, "IHomeMenuFunctions"}, service_context{system, - "IHomeMenuFunctions"} { - // clang-format off - static const FunctionInfo functions[] = { - {10, &IHomeMenuFunctions::RequestToGetForeground, "RequestToGetForeground"}, - {11, nullptr, "LockForeground"}, - {12, nullptr, "UnlockForeground"}, - {20, nullptr, "PopFromGeneralChannel"}, - {21, &IHomeMenuFunctions::GetPopFromGeneralChannelEvent, "GetPopFromGeneralChannelEvent"}, - {30, nullptr, "GetHomeButtonWriterLockAccessor"}, - {31, nullptr, "GetWriterLockAccessorEx"}, - {40, nullptr, "IsSleepEnabled"}, - {41, nullptr, "IsRebootEnabled"}, - {50, nullptr, "LaunchSystemApplet"}, - {51, nullptr, "LaunchStarter"}, - {100, nullptr, "PopRequestLaunchApplicationForDebug"}, - {110, nullptr, "IsForceTerminateApplicationDisabledForDebug"}, - {200, nullptr, "LaunchDevMenu"}, - {1000, nullptr, "SetLastApplicationExitReason"}, - }; - // clang-format on - - RegisterHandlers(functions); - - pop_from_general_channel_event = - service_context.CreateEvent("IHomeMenuFunctions:PopFromGeneralChannelEvent"); -} - -IHomeMenuFunctions::~IHomeMenuFunctions() { - service_context.CloseEvent(pop_from_general_channel_event); -} - -void IHomeMenuFunctions::RequestToGetForeground(HLERequestContext& ctx) { - LOG_WARNING(Service_AM, "(STUBBED) called"); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void IHomeMenuFunctions::GetPopFromGeneralChannelEvent(HLERequestContext& ctx) { - LOG_WARNING(Service_AM, "(STUBBED) called"); - - IPC::ResponseBuilder rb{ctx, 2, 1}; - rb.Push(ResultSuccess); - rb.PushCopyObjects(pop_from_general_channel_event->GetReadableEvent()); -} - -IGlobalStateController::IGlobalStateController(Core::System& system_) - : ServiceFramework{system_, "IGlobalStateController"} { - // clang-format off - static const FunctionInfo functions[] = { - {0, nullptr, "RequestToEnterSleep"}, - {1, nullptr, "EnterSleep"}, - {2, nullptr, "StartSleepSequence"}, - {3, nullptr, "StartShutdownSequence"}, - {4, nullptr, "StartRebootSequence"}, - {9, nullptr, "IsAutoPowerDownRequested"}, - {10, nullptr, "LoadAndApplyIdlePolicySettings"}, - {11, nullptr, "NotifyCecSettingsChanged"}, - {12, nullptr, "SetDefaultHomeButtonLongPressTime"}, - {13, nullptr, "UpdateDefaultDisplayResolution"}, - {14, nullptr, "ShouldSleepOnBoot"}, - {15, nullptr, "GetHdcpAuthenticationFailedEvent"}, - {30, nullptr, "OpenCradleFirmwareUpdater"}, - }; - // clang-format on - - RegisterHandlers(functions); -} - -IGlobalStateController::~IGlobalStateController() = default; - -IApplicationCreator::IApplicationCreator(Core::System& system_) - : ServiceFramework{system_, "IApplicationCreator"} { - // clang-format off - static const FunctionInfo functions[] = { - {0, nullptr, "CreateApplication"}, - {1, nullptr, "PopLaunchRequestedApplication"}, - {10, nullptr, "CreateSystemApplication"}, - {100, nullptr, "PopFloatingApplicationForDevelopment"}, - }; - // clang-format on - - RegisterHandlers(functions); -} - -IApplicationCreator::~IApplicationCreator() = default; - -IProcessWindingController::IProcessWindingController(Core::System& system_) - : ServiceFramework{system_, "IProcessWindingController"} { - // clang-format off - static const FunctionInfo functions[] = { - {0, &IProcessWindingController::GetLaunchReason, "GetLaunchReason"}, - {11, &IProcessWindingController::OpenCallingLibraryApplet, "OpenCallingLibraryApplet"}, - {21, nullptr, "PushContext"}, - {22, nullptr, "PopContext"}, - {23, nullptr, "CancelWindingReservation"}, - {30, nullptr, "WindAndDoReserved"}, - {40, nullptr, "ReserveToStartAndWaitAndUnwindThis"}, - {41, nullptr, "ReserveToStartAndWait"}, - }; - // clang-format on - - RegisterHandlers(functions); -} - -IProcessWindingController::~IProcessWindingController() = default; - -void IProcessWindingController::GetLaunchReason(HLERequestContext& ctx) { - LOG_WARNING(Service_AM, "(STUBBED) called"); - - struct AppletProcessLaunchReason { - u8 flag; - INSERT_PADDING_BYTES(3); - }; - static_assert(sizeof(AppletProcessLaunchReason) == 0x4, - "AppletProcessLaunchReason is an invalid size"); - - AppletProcessLaunchReason reason{ - .flag = 0, - }; - - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(ResultSuccess); - rb.PushRaw(reason); -} - -void IProcessWindingController::OpenCallingLibraryApplet(HLERequestContext& ctx) { - const auto applet_id = system.GetAppletManager().GetCurrentAppletId(); - const auto applet_mode = Applets::LibraryAppletMode::AllForeground; - - LOG_WARNING(Service_AM, "(STUBBED) called with applet_id={:08X}, applet_mode={:08X}", applet_id, - applet_mode); - - const auto& applet_manager{system.GetAppletManager()}; - const auto applet = applet_manager.GetApplet(applet_id, applet_mode); - - if (applet == nullptr) { - LOG_ERROR(Service_AM, "Applet doesn't exist! applet_id={}", applet_id); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultUnknown); - return; - } - - IPC::ResponseBuilder rb{ctx, 2, 0, 1}; - rb.Push(ResultSuccess); - rb.PushIpcInterface(system, applet); -} - } // namespace Service::AM diff --git a/src/core/hle/service/am/am.h b/src/core/hle/service/am/am.h index f8af8f14a..28ee4c079 100755 --- a/src/core/hle/service/am/am.h +++ b/src/core/hle/service/am/am.h @@ -1,20 +1,11 @@ -// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later #pragma once -#include -#include -#include - -#include "core/hle/service/kernel_helpers.h" -#include "core/hle/service/service.h" - -namespace Kernel { -class KernelCore; -class KReadableEvent; -class KTransferMemory; -} // namespace Kernel +namespace Core { +class System; +} namespace Service::Nvnflinger { class Nvnflinger; @@ -22,443 +13,6 @@ class Nvnflinger; namespace Service::AM { -class AppletMessageQueue { -public: - // This is nn::am::AppletMessage - enum class AppletMessage : u32 { - None = 0, - ChangeIntoForeground = 1, - ChangeIntoBackground = 2, - Exit = 4, - ApplicationExited = 6, - FocusStateChanged = 15, - Resume = 16, - DetectShortPressingHomeButton = 20, - DetectLongPressingHomeButton = 21, - DetectShortPressingPowerButton = 22, - DetectMiddlePressingPowerButton = 23, - DetectLongPressingPowerButton = 24, - RequestToPrepareSleep = 25, - FinishedSleepSequence = 26, - SleepRequiredByHighTemperature = 27, - SleepRequiredByLowBattery = 28, - AutoPowerDown = 29, - OperationModeChanged = 30, - PerformanceModeChanged = 31, - DetectReceivingCecSystemStandby = 32, - SdCardRemoved = 33, - LaunchApplicationRequested = 50, - RequestToDisplay = 51, - ShowApplicationLogo = 55, - HideApplicationLogo = 56, - ForceHideApplicationLogo = 57, - FloatingApplicationDetected = 60, - DetectShortPressingCaptureButton = 90, - AlbumScreenShotTaken = 92, - AlbumRecordingSaved = 93, - }; - - explicit AppletMessageQueue(Core::System& system); - ~AppletMessageQueue(); - - Kernel::KReadableEvent& GetMessageReceiveEvent(); - Kernel::KReadableEvent& GetOperationModeChangedEvent(); - void PushMessage(AppletMessage msg); - AppletMessage PopMessage(); - std::size_t GetMessageCount() const; - void RequestExit(); - void RequestResume(); - void FocusStateChanged(); - void OperationModeChanged(); - -private: - KernelHelpers::ServiceContext service_context; - - Kernel::KEvent* on_new_message; - Kernel::KEvent* on_operation_mode_changed; - - std::queue messages; -}; - -class IWindowController final : public ServiceFramework { -public: - explicit IWindowController(Core::System& system_); - ~IWindowController() override; - -private: - void GetAppletResourceUserId(HLERequestContext& ctx); - void GetAppletResourceUserIdOfCallerApplet(HLERequestContext& ctx); - void AcquireForegroundRights(HLERequestContext& ctx); -}; - -class IAudioController final : public ServiceFramework { -public: - explicit IAudioController(Core::System& system_); - ~IAudioController() override; - -private: - void SetExpectedMasterVolume(HLERequestContext& ctx); - void GetMainAppletExpectedMasterVolume(HLERequestContext& ctx); - void GetLibraryAppletExpectedMasterVolume(HLERequestContext& ctx); - void ChangeMainAppletMasterVolume(HLERequestContext& ctx); - void SetTransparentAudioRate(HLERequestContext& ctx); - - static constexpr float min_allowed_volume = 0.0f; - static constexpr float max_allowed_volume = 1.0f; - - float main_applet_volume{0.25f}; - float library_applet_volume{max_allowed_volume}; - float transparent_volume_rate{min_allowed_volume}; - - // Volume transition fade time in nanoseconds. - // e.g. If the main applet volume was 0% and was changed to 50% - // with a fade of 50ns, then over the course of 50ns, - // the volume will gradually fade up to 50% - std::chrono::nanoseconds fade_time_ns{0}; -}; - -class IDisplayController final : public ServiceFramework { -public: - explicit IDisplayController(Core::System& system_); - ~IDisplayController() override; - -private: - void GetCallerAppletCaptureImageEx(HLERequestContext& ctx); - void TakeScreenShotOfOwnLayer(HLERequestContext& ctx); - void AcquireLastForegroundCaptureSharedBuffer(HLERequestContext& ctx); - void ReleaseLastForegroundCaptureSharedBuffer(HLERequestContext& ctx); - void AcquireCallerAppletCaptureSharedBuffer(HLERequestContext& ctx); - void ReleaseCallerAppletCaptureSharedBuffer(HLERequestContext& ctx); -}; - -class IDebugFunctions final : public ServiceFramework { -public: - explicit IDebugFunctions(Core::System& system_); - ~IDebugFunctions() override; -}; - -class ISelfController final : public ServiceFramework { -public: - explicit ISelfController(Core::System& system_, Nvnflinger::Nvnflinger& nvnflinger_); - ~ISelfController() override; - -private: - void Exit(HLERequestContext& ctx); - void LockExit(HLERequestContext& ctx); - void UnlockExit(HLERequestContext& ctx); - void EnterFatalSection(HLERequestContext& ctx); - void LeaveFatalSection(HLERequestContext& ctx); - void GetLibraryAppletLaunchableEvent(HLERequestContext& ctx); - void SetScreenShotPermission(HLERequestContext& ctx); - void SetOperationModeChangedNotification(HLERequestContext& ctx); - void SetPerformanceModeChangedNotification(HLERequestContext& ctx); - void SetFocusHandlingMode(HLERequestContext& ctx); - void SetRestartMessageEnabled(HLERequestContext& ctx); - void SetOutOfFocusSuspendingEnabled(HLERequestContext& ctx); - void SetAlbumImageOrientation(HLERequestContext& ctx); - void IsSystemBufferSharingEnabled(HLERequestContext& ctx); - void GetSystemSharedBufferHandle(HLERequestContext& ctx); - void GetSystemSharedLayerHandle(HLERequestContext& ctx); - void CreateManagedDisplayLayer(HLERequestContext& ctx); - void CreateManagedDisplaySeparableLayer(HLERequestContext& ctx); - void SetHandlesRequestToDisplay(HLERequestContext& ctx); - void ApproveToDisplay(HLERequestContext& ctx); - void SetIdleTimeDetectionExtension(HLERequestContext& ctx); - void GetIdleTimeDetectionExtension(HLERequestContext& ctx); - void ReportUserIsActive(HLERequestContext& ctx); - void SetAutoSleepDisabled(HLERequestContext& ctx); - void IsAutoSleepDisabled(HLERequestContext& ctx); - void GetAccumulatedSuspendedTickValue(HLERequestContext& ctx); - void GetAccumulatedSuspendedTickChangedEvent(HLERequestContext& ctx); - void SetAlbumImageTakenNotificationEnabled(HLERequestContext& ctx); - void SaveCurrentScreenshot(HLERequestContext& ctx); - void SetRecordVolumeMuted(HLERequestContext& ctx); - - Result EnsureBufferSharingEnabled(); - - enum class ScreenshotPermission : u32 { - Inherit = 0, - Enable = 1, - Disable = 2, - }; - - Nvnflinger::Nvnflinger& nvnflinger; - - KernelHelpers::ServiceContext service_context; - - Kernel::KEvent* launchable_event; - Kernel::KEvent* accumulated_suspended_tick_changed_event; - - u32 idle_time_detection_extension = 0; - u64 num_fatal_sections_entered = 0; - u64 system_shared_buffer_id = 0; - u64 system_shared_layer_id = 0; - bool is_auto_sleep_disabled = false; - bool buffer_sharing_enabled = false; - ScreenshotPermission screenshot_permission = ScreenshotPermission::Inherit; -}; - -class ILockAccessor final : public ServiceFramework { -public: - explicit ILockAccessor(Core::System& system_); - ~ILockAccessor() override; - -private: - void TryLock(HLERequestContext& ctx); - void Unlock(HLERequestContext& ctx); - void GetEvent(HLERequestContext& ctx); - void IsLocked(HLERequestContext& ctx); - - bool is_locked{}; - - Kernel::KEvent* lock_event; - KernelHelpers::ServiceContext service_context; -}; - -class ICommonStateGetter final : public ServiceFramework { -public: - explicit ICommonStateGetter(Core::System& system_, - std::shared_ptr msg_queue_); - ~ICommonStateGetter() override; - -private: - // This is nn::oe::FocusState - enum class FocusState : u8 { - InFocus = 1, - NotInFocus = 2, - Background = 3, - }; - - // This is nn::oe::OperationMode - enum class OperationMode : u8 { - Handheld = 0, - Docked = 1, - }; - - // This is nn::am::service::SystemButtonType - enum class SystemButtonType { - None, - HomeButtonShortPressing, - HomeButtonLongPressing, - PowerButtonShortPressing, - PowerButtonLongPressing, - ShutdownSystem, - CaptureButtonShortPressing, - CaptureButtonLongPressing, - }; - - enum class SysPlatformRegion : s32 { - Global = 1, - Terra = 2, - }; - - void GetEventHandle(HLERequestContext& ctx); - void ReceiveMessage(HLERequestContext& ctx); - void GetCurrentFocusState(HLERequestContext& ctx); - void RequestToAcquireSleepLock(HLERequestContext& ctx); - void GetAcquiredSleepLockEvent(HLERequestContext& ctx); - void GetReaderLockAccessorEx(HLERequestContext& ctx); - void GetDefaultDisplayResolutionChangeEvent(HLERequestContext& ctx); - void GetOperationMode(HLERequestContext& ctx); - void GetPerformanceMode(HLERequestContext& ctx); - void GetBootMode(HLERequestContext& ctx); - void IsVrModeEnabled(HLERequestContext& ctx); - void SetVrModeEnabled(HLERequestContext& ctx); - void SetLcdBacklighOffEnabled(HLERequestContext& ctx); - void BeginVrModeEx(HLERequestContext& ctx); - void EndVrModeEx(HLERequestContext& ctx); - void GetDefaultDisplayResolution(HLERequestContext& ctx); - void SetCpuBoostMode(HLERequestContext& ctx); - void GetBuiltInDisplayType(HLERequestContext& ctx); - void PerformSystemButtonPressingIfInFocus(HLERequestContext& ctx); - void GetSettingsPlatformRegion(HLERequestContext& ctx); - void SetRequestExitToLibraryAppletAtExecuteNextProgramEnabled(HLERequestContext& ctx); - - std::shared_ptr msg_queue; - bool vr_mode_state{}; - Kernel::KEvent* sleep_lock_event; - KernelHelpers::ServiceContext service_context; -}; - -class IStorageImpl { -public: - virtual ~IStorageImpl(); - virtual std::vector& GetData() = 0; - virtual const std::vector& GetData() const = 0; - virtual std::size_t GetSize() const = 0; -}; - -class IStorage final : public ServiceFramework { -public: - explicit IStorage(Core::System& system_, std::vector&& buffer); - ~IStorage() override; - - std::vector& GetData() { - return impl->GetData(); - } - - const std::vector& GetData() const { - return impl->GetData(); - } - - std::size_t GetSize() const { - return impl->GetSize(); - } - -private: - void Register(); - void Open(HLERequestContext& ctx); - - std::shared_ptr impl; -}; - -class IStorageAccessor final : public ServiceFramework { -public: - explicit IStorageAccessor(Core::System& system_, IStorage& backing_); - ~IStorageAccessor() override; - -private: - void GetSize(HLERequestContext& ctx); - void Write(HLERequestContext& ctx); - void Read(HLERequestContext& ctx); - - IStorage& backing; -}; - -class ILibraryAppletCreator final : public ServiceFramework { -public: - explicit ILibraryAppletCreator(Core::System& system_); - ~ILibraryAppletCreator() override; - -private: - void CreateLibraryApplet(HLERequestContext& ctx); - void CreateStorage(HLERequestContext& ctx); - void CreateTransferMemoryStorage(HLERequestContext& ctx); - void CreateHandleStorage(HLERequestContext& ctx); -}; - -class ILibraryAppletSelfAccessor final : public ServiceFramework { -public: - explicit ILibraryAppletSelfAccessor(Core::System& system_); - ~ILibraryAppletSelfAccessor() override; - -private: - void PopInData(HLERequestContext& ctx); - void PushOutData(HLERequestContext& ctx); - void GetLibraryAppletInfo(HLERequestContext& ctx); - void GetMainAppletIdentityInfo(HLERequestContext& ctx); - void ExitProcessAndReturn(HLERequestContext& ctx); - void GetCallerAppletIdentityInfo(HLERequestContext& ctx); - void GetDesirableKeyboardLayout(HLERequestContext& ctx); - void GetMainAppletAvailableUsers(HLERequestContext& ctx); - void ShouldSetGpuTimeSliceManually(HLERequestContext& ctx); - - void PushInShowAlbum(); - void PushInShowCabinetData(); - void PushInShowMiiEditData(); - void PushInShowSoftwareKeyboard(); - void PushInShowController(); - - std::deque> queue_data; -}; - -class IAppletCommonFunctions final : public ServiceFramework { -public: - explicit IAppletCommonFunctions(Core::System& system_); - ~IAppletCommonFunctions() override; - -private: - void SetCpuBoostRequestPriority(HLERequestContext& ctx); -}; - -class IApplicationFunctions final : public ServiceFramework { -public: - explicit IApplicationFunctions(Core::System& system_); - ~IApplicationFunctions() override; - -private: - void PopLaunchParameter(HLERequestContext& ctx); - void CreateApplicationAndRequestToStartForQuest(HLERequestContext& ctx); - void EnsureSaveData(HLERequestContext& ctx); - void SetTerminateResult(HLERequestContext& ctx); - void GetDisplayVersion(HLERequestContext& ctx); - void GetDesiredLanguage(HLERequestContext& ctx); - void IsGamePlayRecordingSupported(HLERequestContext& ctx); - void InitializeGamePlayRecording(HLERequestContext& ctx); - void SetGamePlayRecordingState(HLERequestContext& ctx); - void NotifyRunning(HLERequestContext& ctx); - void GetPseudoDeviceId(HLERequestContext& ctx); - void ExtendSaveData(HLERequestContext& ctx); - void GetSaveDataSize(HLERequestContext& ctx); - void CreateCacheStorage(HLERequestContext& ctx); - void GetSaveDataSizeMax(HLERequestContext& ctx); - void BeginBlockingHomeButtonShortAndLongPressed(HLERequestContext& ctx); - void EndBlockingHomeButtonShortAndLongPressed(HLERequestContext& ctx); - void BeginBlockingHomeButton(HLERequestContext& ctx); - void EndBlockingHomeButton(HLERequestContext& ctx); - void EnableApplicationCrashReport(HLERequestContext& ctx); - void InitializeApplicationCopyrightFrameBuffer(HLERequestContext& ctx); - void SetApplicationCopyrightImage(HLERequestContext& ctx); - void SetApplicationCopyrightVisibility(HLERequestContext& ctx); - void QueryApplicationPlayStatistics(HLERequestContext& ctx); - void QueryApplicationPlayStatisticsByUid(HLERequestContext& ctx); - void ExecuteProgram(HLERequestContext& ctx); - void ClearUserChannel(HLERequestContext& ctx); - void UnpopToUserChannel(HLERequestContext& ctx); - void GetPreviousProgramIndex(HLERequestContext& ctx); - void GetGpuErrorDetectedSystemEvent(HLERequestContext& ctx); - void GetFriendInvitationStorageChannelEvent(HLERequestContext& ctx); - void TryPopFromFriendInvitationStorageChannel(HLERequestContext& ctx); - void GetNotificationStorageChannelEvent(HLERequestContext& ctx); - void GetHealthWarningDisappearedSystemEvent(HLERequestContext& ctx); - void PrepareForJit(HLERequestContext& ctx); - - KernelHelpers::ServiceContext service_context; - - bool launch_popped_account_preselect = false; - s32 previous_program_index{-1}; - Kernel::KEvent* gpu_error_detected_event; - Kernel::KEvent* friend_invitation_storage_channel_event; - Kernel::KEvent* notification_storage_channel_event; - Kernel::KEvent* health_warning_disappeared_system_event; -}; - -class IHomeMenuFunctions final : public ServiceFramework { -public: - explicit IHomeMenuFunctions(Core::System& system_); - ~IHomeMenuFunctions() override; - -private: - void RequestToGetForeground(HLERequestContext& ctx); - void GetPopFromGeneralChannelEvent(HLERequestContext& ctx); - - KernelHelpers::ServiceContext service_context; - - Kernel::KEvent* pop_from_general_channel_event; -}; - -class IGlobalStateController final : public ServiceFramework { -public: - explicit IGlobalStateController(Core::System& system_); - ~IGlobalStateController() override; -}; - -class IApplicationCreator final : public ServiceFramework { -public: - explicit IApplicationCreator(Core::System& system_); - ~IApplicationCreator() override; -}; - -class IProcessWindingController final : public ServiceFramework { -public: - explicit IProcessWindingController(Core::System& system_); - ~IProcessWindingController() override; - -private: - void GetLaunchReason(HLERequestContext& ctx); - void OpenCallingLibraryApplet(HLERequestContext& ctx); -}; - void LoopProcess(Nvnflinger::Nvnflinger& nvnflinger, Core::System& system); } // namespace Service::AM diff --git a/src/core/hle/service/am/am_results.h b/src/core/hle/service/am/am_results.h new file mode 100755 index 000000000..a2afc9eec --- /dev/null +++ b/src/core/hle/service/am/am_results.h @@ -0,0 +1,16 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "core/hle/result.h" + +namespace Service::AM { + +constexpr Result ResultNoDataInChannel{ErrorModule::AM, 2}; +constexpr Result ResultNoMessages{ErrorModule::AM, 3}; +constexpr Result ResultInvalidOffset{ErrorModule::AM, 503}; +constexpr Result ResultInvalidStorageType{ErrorModule::AM, 511}; +constexpr Result ResultFatalSectionCountImbalance{ErrorModule::AM, 512}; + +} // namespace Service::AM diff --git a/src/core/hle/service/am/am_types.h b/src/core/hle/service/am/am_types.h new file mode 100755 index 000000000..a2b852b12 --- /dev/null +++ b/src/core/hle/service/am/am_types.h @@ -0,0 +1,178 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "common/common_funcs.h" +#include "common/common_types.h" + +namespace Service::AM { + +namespace Frontend { +class FrontendApplet; +} + +enum class AppletType { + Application, + LibraryApplet, + SystemApplet, +}; + +enum class GameplayRecordingState : u32 { + Disabled, + Enabled, +}; + +// This is nn::oe::FocusState +enum class FocusState : u8 { + InFocus = 1, + NotInFocus = 2, + Background = 3, +}; + +// This is nn::oe::OperationMode +enum class OperationMode : u8 { + Handheld = 0, + Docked = 1, +}; + +// This is nn::am::service::SystemButtonType +enum class SystemButtonType { + None, + HomeButtonShortPressing, + HomeButtonLongPressing, + PowerButtonShortPressing, + PowerButtonLongPressing, + ShutdownSystem, + CaptureButtonShortPressing, + CaptureButtonLongPressing, +}; + +enum class SysPlatformRegion : s32 { + Global = 1, + Terra = 2, +}; + +struct AppletProcessLaunchReason { + u8 flag; + INSERT_PADDING_BYTES(3); +}; +static_assert(sizeof(AppletProcessLaunchReason) == 0x4, + "AppletProcessLaunchReason is an invalid size"); + +enum class ScreenshotPermission : u32 { + Inherit = 0, + Enable = 1, + Disable = 2, +}; + +struct FocusHandlingMode { + bool unknown0; + bool unknown1; + bool unknown2; + bool unknown3; +}; + +enum class IdleTimeDetectionExtension : u32 { + Disabled = 0, + Extended = 1, + ExtendedUnsafe = 2, +}; + +enum class AppletId : u32 { + None = 0x00, + Application = 0x01, + OverlayDisplay = 0x02, + QLaunch = 0x03, + Starter = 0x04, + Auth = 0x0A, + Cabinet = 0x0B, + Controller = 0x0C, + DataErase = 0x0D, + Error = 0x0E, + NetConnect = 0x0F, + ProfileSelect = 0x10, + SoftwareKeyboard = 0x11, + MiiEdit = 0x12, + Web = 0x13, + Shop = 0x14, + PhotoViewer = 0x15, + Settings = 0x16, + OfflineWeb = 0x17, + LoginShare = 0x18, + WebAuth = 0x19, + MyPage = 0x1A, +}; + +enum class AppletProgramId : u64 { + QLaunch = 0x0100000000001000ull, + Auth = 0x0100000000001001ull, + Cabinet = 0x0100000000001002ull, + Controller = 0x0100000000001003ull, + DataErase = 0x0100000000001004ull, + Error = 0x0100000000001005ull, + NetConnect = 0x0100000000001006ull, + ProfileSelect = 0x0100000000001007ull, + SoftwareKeyboard = 0x0100000000001008ull, + MiiEdit = 0x0100000000001009ull, + Web = 0x010000000000100Aull, + Shop = 0x010000000000100Bull, + OverlayDisplay = 0x010000000000100Cull, + PhotoViewer = 0x010000000000100Dull, + Settings = 0x010000000000100Eull, + OfflineWeb = 0x010000000000100Full, + LoginShare = 0x0100000000001010ull, + WebAuth = 0x0100000000001011ull, + Starter = 0x0100000000001012ull, + MyPage = 0x0100000000001013ull, + MaxProgramId = 0x0100000000001FFFull, +}; + +enum class LibraryAppletMode : u32 { + AllForeground = 0, + Background = 1, + NoUI = 2, + BackgroundIndirectDisplay = 3, + AllForegroundInitiallyHidden = 4, +}; + +enum class CommonArgumentVersion : u32 { + Version0, + Version1, + Version2, + Version3, +}; + +enum class CommonArgumentSize : u32 { + Version3 = 0x20, +}; + +enum class ThemeColor : u32 { + BasicWhite = 0, + BasicBlack = 3, +}; + +struct CommonArguments { + CommonArgumentVersion arguments_version; + CommonArgumentSize size; + u32 library_version; + ThemeColor theme_color; + bool play_startup_sound; + u64 system_tick; +}; +static_assert(sizeof(CommonArguments) == 0x20, "CommonArguments has incorrect size."); + +struct AppletIdentityInfo { + AppletId applet_id; + INSERT_PADDING_BYTES(0x4); + u64 application_id; +}; +static_assert(sizeof(AppletIdentityInfo) == 0x10, "AppletIdentityInfo has incorrect size."); + +using AppletResourceUserId = u64; +using ProgramId = u64; + +struct Applet; +class AppletDataBroker; + +} // namespace Service::AM diff --git a/src/core/hle/service/am/applet.cpp b/src/core/hle/service/am/applet.cpp new file mode 100755 index 000000000..5b9056c12 --- /dev/null +++ b/src/core/hle/service/am/applet.cpp @@ -0,0 +1,27 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "common/scope_exit.h" + +#include "core/core.h" +#include "core/hle/service/am/am_results.h" +#include "core/hle/service/am/applet.h" +#include "core/hle/service/am/applet_manager.h" + +namespace Service::AM { + +Applet::Applet(Core::System& system, std::unique_ptr process_) + : 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 diff --git a/src/core/hle/service/am/applet.h b/src/core/hle/service/am/applet.h new file mode 100755 index 000000000..bce6f9050 --- /dev/null +++ b/src/core/hle/service/am/applet.h @@ -0,0 +1,133 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include +#include + +#include "common/math_util.h" +#include "core/hle/service/apm/apm_controller.h" +#include "core/hle/service/caps/caps_types.h" +#include "core/hle/service/event.h" +#include "core/hle/service/kernel_helpers.h" +#include "core/hle/service/service.h" + +#include "core/hle/service/am/am_types.h" +#include "core/hle/service/am/applet_message_queue.h" +#include "core/hle/service/am/hid_registration.h" +#include "core/hle/service/am/managed_layer_holder.h" +#include "core/hle/service/am/process.h" +#include "core/hle/service/am/storage.h" +#include "core/hle/service/am/system_buffer_manager.h" + +namespace Service::AM { + +struct Applet { + explicit Applet(Core::System& system, std::unique_ptr process_); + ~Applet(); + + // Lock + std::mutex lock{}; + + // Event creation helper + KernelHelpers::ServiceContext context; + + // Applet message queue + AppletMessageQueue message_queue; + + // Process + std::unique_ptr process; + + // Creation state + AppletId applet_id{}; + AppletResourceUserId aruid{}; + AppletProcessLaunchReason launch_reason{}; + AppletType type{}; + ProgramId program_id{}; + LibraryAppletMode library_applet_mode{}; + s32 previous_program_index{-1}; + ScreenshotPermission previous_screenshot_permission{ScreenshotPermission::Enable}; + + // TODO: some fields above can be AppletIdentityInfo + AppletIdentityInfo screen_shot_identity; + + // hid state + HidRegistration hid_registration; + + // vi state + SystemBufferManager system_buffer_manager{}; + ManagedLayerHolder managed_layer_holder{}; + + // Applet common functions + Result terminate_result{}; + s32 display_logical_width{}; + s32 display_logical_height{}; + Common::Rectangle 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> user_channel_launch_parameter{}; + std::deque> preselected_user_launch_parameter{}; + + // Caller applet + std::weak_ptr caller_applet{}; + std::shared_ptr caller_applet_broker{}; + + // Self state + bool exit_locked{}; + s32 fatal_section_count{}; + bool operation_mode_changed_notification_enabled{true}; + bool performance_mode_changed_notification_enabled{true}; + FocusHandlingMode focus_handling_mode{}; + bool restart_message_enabled{}; + bool out_of_focus_suspension_enabled{true}; + Capture::AlbumImageOrientation album_image_orientation{}; + bool handles_request_to_display{}; + ScreenshotPermission screenshot_permission{}; + IdleTimeDetectionExtension idle_time_detection_extension{}; + bool auto_sleep_disabled{}; + u64 suspended_ticks{}; + bool album_image_taken_notification_enabled{}; + bool record_volume_muted{}; + + // Events + Event gpu_error_detected_event; + Event friend_invitation_storage_channel_event; + Event notification_storage_channel_event; + Event health_warning_disappeared_system_event; + Event acquired_sleep_lock_event; + Event pop_from_general_channel_event; + Event library_applet_launchable_event; + Event accumulated_suspended_tick_changed_event; + Event sleep_lock_event; + + // Frontend state + std::shared_ptr frontend{}; +}; + +} // namespace Service::AM diff --git a/src/core/hle/service/am/applet_ae.cpp b/src/core/hle/service/am/applet_ae.cpp index 3a41b891a..5c88e5900 100755 --- a/src/core/hle/service/am/applet_ae.cpp +++ b/src/core/hle/service/am/applet_ae.cpp @@ -1,291 +1,16 @@ // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later -#include "common/logging/log.h" -#include "core/core.h" -#include "core/hle/service/am/am.h" #include "core/hle/service/am/applet_ae.h" +#include "core/hle/service/am/applet_manager.h" +#include "core/hle/service/am/library_applet_proxy.h" +#include "core/hle/service/am/system_applet_proxy.h" #include "core/hle/service/ipc_helpers.h" -#include "core/hle/service/nvnflinger/nvnflinger.h" namespace Service::AM { -class ILibraryAppletProxy final : public ServiceFramework { -public: - explicit ILibraryAppletProxy(Nvnflinger::Nvnflinger& nvnflinger_, - std::shared_ptr msg_queue_, - Core::System& system_) - : ServiceFramework{system_, "ILibraryAppletProxy"}, - nvnflinger{nvnflinger_}, msg_queue{std::move(msg_queue_)} { - // clang-format off - static const FunctionInfo functions[] = { - {0, &ILibraryAppletProxy::GetCommonStateGetter, "GetCommonStateGetter"}, - {1, &ILibraryAppletProxy::GetSelfController, "GetSelfController"}, - {2, &ILibraryAppletProxy::GetWindowController, "GetWindowController"}, - {3, &ILibraryAppletProxy::GetAudioController, "GetAudioController"}, - {4, &ILibraryAppletProxy::GetDisplayController, "GetDisplayController"}, - {10, &ILibraryAppletProxy::GetProcessWindingController, "GetProcessWindingController"}, - {11, &ILibraryAppletProxy::GetLibraryAppletCreator, "GetLibraryAppletCreator"}, - {20, &ILibraryAppletProxy::OpenLibraryAppletSelfAccessor, "OpenLibraryAppletSelfAccessor"}, - {21, &ILibraryAppletProxy::GetAppletCommonFunctions, "GetAppletCommonFunctions"}, - {22, &ILibraryAppletProxy::GetHomeMenuFunctions, "GetHomeMenuFunctions"}, - {23, &ILibraryAppletProxy::GetGlobalStateController, "GetGlobalStateController"}, - {1000, &ILibraryAppletProxy::GetDebugFunctions, "GetDebugFunctions"}, - }; - // clang-format on - - RegisterHandlers(functions); - } - -private: - void GetCommonStateGetter(HLERequestContext& ctx) { - LOG_DEBUG(Service_AM, "called"); - - IPC::ResponseBuilder rb{ctx, 2, 0, 1}; - rb.Push(ResultSuccess); - rb.PushIpcInterface(system, msg_queue); - } - - void GetSelfController(HLERequestContext& ctx) { - LOG_DEBUG(Service_AM, "called"); - - IPC::ResponseBuilder rb{ctx, 2, 0, 1}; - rb.Push(ResultSuccess); - rb.PushIpcInterface(system, nvnflinger); - } - - void GetWindowController(HLERequestContext& ctx) { - LOG_DEBUG(Service_AM, "called"); - - IPC::ResponseBuilder rb{ctx, 2, 0, 1}; - rb.Push(ResultSuccess); - rb.PushIpcInterface(system); - } - - void GetAudioController(HLERequestContext& ctx) { - LOG_DEBUG(Service_AM, "called"); - - IPC::ResponseBuilder rb{ctx, 2, 0, 1}; - rb.Push(ResultSuccess); - rb.PushIpcInterface(system); - } - - void GetDisplayController(HLERequestContext& ctx) { - LOG_DEBUG(Service_AM, "called"); - - IPC::ResponseBuilder rb{ctx, 2, 0, 1}; - rb.Push(ResultSuccess); - rb.PushIpcInterface(system); - } - - void GetProcessWindingController(HLERequestContext& ctx) { - LOG_DEBUG(Service_AM, "called"); - - IPC::ResponseBuilder rb{ctx, 2, 0, 1}; - rb.Push(ResultSuccess); - rb.PushIpcInterface(system); - } - - void GetLibraryAppletCreator(HLERequestContext& ctx) { - LOG_DEBUG(Service_AM, "called"); - - IPC::ResponseBuilder rb{ctx, 2, 0, 1}; - rb.Push(ResultSuccess); - rb.PushIpcInterface(system); - } - - void OpenLibraryAppletSelfAccessor(HLERequestContext& ctx) { - LOG_DEBUG(Service_AM, "called"); - - IPC::ResponseBuilder rb{ctx, 2, 0, 1}; - rb.Push(ResultSuccess); - rb.PushIpcInterface(system); - } - - void GetAppletCommonFunctions(HLERequestContext& ctx) { - LOG_DEBUG(Service_AM, "called"); - - IPC::ResponseBuilder rb{ctx, 2, 0, 1}; - rb.Push(ResultSuccess); - rb.PushIpcInterface(system); - } - - void GetHomeMenuFunctions(HLERequestContext& ctx) { - LOG_DEBUG(Service_AM, "called"); - - IPC::ResponseBuilder rb{ctx, 2, 0, 1}; - rb.Push(ResultSuccess); - rb.PushIpcInterface(system); - } - - void GetGlobalStateController(HLERequestContext& ctx) { - LOG_DEBUG(Service_AM, "called"); - - IPC::ResponseBuilder rb{ctx, 2, 0, 1}; - rb.Push(ResultSuccess); - rb.PushIpcInterface(system); - } - - void GetDebugFunctions(HLERequestContext& ctx) { - LOG_DEBUG(Service_AM, "called"); - - IPC::ResponseBuilder rb{ctx, 2, 0, 1}; - rb.Push(ResultSuccess); - rb.PushIpcInterface(system); - } - - Nvnflinger::Nvnflinger& nvnflinger; - std::shared_ptr msg_queue; -}; - -class ISystemAppletProxy final : public ServiceFramework { -public: - explicit ISystemAppletProxy(Nvnflinger::Nvnflinger& nvnflinger_, - std::shared_ptr msg_queue_, - Core::System& system_) - : ServiceFramework{system_, "ISystemAppletProxy"}, - nvnflinger{nvnflinger_}, msg_queue{std::move(msg_queue_)} { - // clang-format off - static const FunctionInfo functions[] = { - {0, &ISystemAppletProxy::GetCommonStateGetter, "GetCommonStateGetter"}, - {1, &ISystemAppletProxy::GetSelfController, "GetSelfController"}, - {2, &ISystemAppletProxy::GetWindowController, "GetWindowController"}, - {3, &ISystemAppletProxy::GetAudioController, "GetAudioController"}, - {4, &ISystemAppletProxy::GetDisplayController, "GetDisplayController"}, - {10, nullptr, "GetProcessWindingController"}, - {11, &ISystemAppletProxy::GetLibraryAppletCreator, "GetLibraryAppletCreator"}, - {20, &ISystemAppletProxy::GetHomeMenuFunctions, "GetHomeMenuFunctions"}, - {21, &ISystemAppletProxy::GetGlobalStateController, "GetGlobalStateController"}, - {22, &ISystemAppletProxy::GetApplicationCreator, "GetApplicationCreator"}, - {23, &ISystemAppletProxy::GetAppletCommonFunctions, "GetAppletCommonFunctions"}, - {1000, &ISystemAppletProxy::GetDebugFunctions, "GetDebugFunctions"}, - }; - // clang-format on - - RegisterHandlers(functions); - } - -private: - void GetCommonStateGetter(HLERequestContext& ctx) { - LOG_DEBUG(Service_AM, "called"); - - IPC::ResponseBuilder rb{ctx, 2, 0, 1}; - rb.Push(ResultSuccess); - rb.PushIpcInterface(system, msg_queue); - } - - void GetSelfController(HLERequestContext& ctx) { - LOG_DEBUG(Service_AM, "called"); - - IPC::ResponseBuilder rb{ctx, 2, 0, 1}; - rb.Push(ResultSuccess); - rb.PushIpcInterface(system, nvnflinger); - } - - void GetWindowController(HLERequestContext& ctx) { - LOG_DEBUG(Service_AM, "called"); - - IPC::ResponseBuilder rb{ctx, 2, 0, 1}; - rb.Push(ResultSuccess); - rb.PushIpcInterface(system); - } - - void GetAudioController(HLERequestContext& ctx) { - LOG_DEBUG(Service_AM, "called"); - - IPC::ResponseBuilder rb{ctx, 2, 0, 1}; - rb.Push(ResultSuccess); - rb.PushIpcInterface(system); - } - - void GetDisplayController(HLERequestContext& ctx) { - LOG_DEBUG(Service_AM, "called"); - - IPC::ResponseBuilder rb{ctx, 2, 0, 1}; - rb.Push(ResultSuccess); - rb.PushIpcInterface(system); - } - - void GetLibraryAppletCreator(HLERequestContext& ctx) { - LOG_DEBUG(Service_AM, "called"); - - IPC::ResponseBuilder rb{ctx, 2, 0, 1}; - rb.Push(ResultSuccess); - rb.PushIpcInterface(system); - } - - void GetHomeMenuFunctions(HLERequestContext& ctx) { - LOG_DEBUG(Service_AM, "called"); - - IPC::ResponseBuilder rb{ctx, 2, 0, 1}; - rb.Push(ResultSuccess); - rb.PushIpcInterface(system); - } - - void GetGlobalStateController(HLERequestContext& ctx) { - LOG_DEBUG(Service_AM, "called"); - - IPC::ResponseBuilder rb{ctx, 2, 0, 1}; - rb.Push(ResultSuccess); - rb.PushIpcInterface(system); - } - - void GetApplicationCreator(HLERequestContext& ctx) { - LOG_DEBUG(Service_AM, "called"); - - IPC::ResponseBuilder rb{ctx, 2, 0, 1}; - rb.Push(ResultSuccess); - rb.PushIpcInterface(system); - } - - void GetAppletCommonFunctions(HLERequestContext& ctx) { - LOG_DEBUG(Service_AM, "called"); - - IPC::ResponseBuilder rb{ctx, 2, 0, 1}; - rb.Push(ResultSuccess); - rb.PushIpcInterface(system); - } - - void GetDebugFunctions(HLERequestContext& ctx) { - LOG_DEBUG(Service_AM, "called"); - - IPC::ResponseBuilder rb{ctx, 2, 0, 1}; - rb.Push(ResultSuccess); - rb.PushIpcInterface(system); - } - - Nvnflinger::Nvnflinger& nvnflinger; - std::shared_ptr msg_queue; -}; - -void AppletAE::OpenSystemAppletProxy(HLERequestContext& ctx) { - LOG_DEBUG(Service_AM, "called"); - - IPC::ResponseBuilder rb{ctx, 2, 0, 1}; - rb.Push(ResultSuccess); - rb.PushIpcInterface(nvnflinger, msg_queue, system); -} - -void AppletAE::OpenLibraryAppletProxy(HLERequestContext& ctx) { - LOG_DEBUG(Service_AM, "called"); - - IPC::ResponseBuilder rb{ctx, 2, 0, 1}; - rb.Push(ResultSuccess); - rb.PushIpcInterface(nvnflinger, msg_queue, system); -} - -void AppletAE::OpenLibraryAppletProxyOld(HLERequestContext& ctx) { - LOG_DEBUG(Service_AM, "called"); - - IPC::ResponseBuilder rb{ctx, 2, 0, 1}; - rb.Push(ResultSuccess); - rb.PushIpcInterface(nvnflinger, msg_queue, system); -} - -AppletAE::AppletAE(Nvnflinger::Nvnflinger& nvnflinger_, - std::shared_ptr msg_queue_, Core::System& system_) - : ServiceFramework{system_, "appletAE"}, nvnflinger{nvnflinger_}, msg_queue{ - std::move(msg_queue_)} { +AppletAE::AppletAE(Nvnflinger::Nvnflinger& nvnflinger_, Core::System& system_) + : ServiceFramework{system_, "appletAE"}, nvnflinger{nvnflinger_} { // clang-format off static const FunctionInfo functions[] = { {100, &AppletAE::OpenSystemAppletProxy, "OpenSystemAppletProxy"}, @@ -304,8 +29,45 @@ AppletAE::AppletAE(Nvnflinger::Nvnflinger& nvnflinger_, AppletAE::~AppletAE() = default; -const std::shared_ptr& AppletAE::GetMessageQueue() const { - return msg_queue; +void AppletAE::OpenSystemAppletProxy(HLERequestContext& ctx) { + LOG_DEBUG(Service_AM, "called"); + + if (const auto applet = GetAppletFromContext(ctx)) { + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(ResultSuccess); + rb.PushIpcInterface(nvnflinger, applet, system); + } else { + UNIMPLEMENTED(); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultUnknown); + } +} + +void AppletAE::OpenLibraryAppletProxy(HLERequestContext& ctx) { + LOG_DEBUG(Service_AM, "called"); + + if (const auto applet = GetAppletFromContext(ctx)) { + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(ResultSuccess); + rb.PushIpcInterface(nvnflinger, applet, system); + } else { + UNIMPLEMENTED(); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultUnknown); + } +} + +void AppletAE::OpenLibraryAppletProxyOld(HLERequestContext& ctx) { + LOG_DEBUG(Service_AM, "called"); + + return OpenLibraryAppletProxy(ctx); +} + +std::shared_ptr AppletAE::GetAppletFromContext(HLERequestContext& ctx) { + const auto aruid = ctx.GetPID(); + return system.GetAppletManager().GetByAppletResourceUserId(aruid); } } // namespace Service::AM diff --git a/src/core/hle/service/am/applet_ae.h b/src/core/hle/service/am/applet_ae.h index a63e5d830..e3095448e 100755 --- a/src/core/hle/service/am/applet_ae.h +++ b/src/core/hle/service/am/applet_ae.h @@ -18,23 +18,21 @@ class Nvnflinger; namespace AM { -class AppletMessageQueue; +struct Applet; class AppletAE final : public ServiceFramework { public: - explicit AppletAE(Nvnflinger::Nvnflinger& nvnflinger_, - std::shared_ptr msg_queue_, Core::System& system_); + explicit AppletAE(Nvnflinger::Nvnflinger& nvnflinger_, Core::System& system_); ~AppletAE() override; - const std::shared_ptr& GetMessageQueue() const; - private: void OpenSystemAppletProxy(HLERequestContext& ctx); void OpenLibraryAppletProxy(HLERequestContext& ctx); void OpenLibraryAppletProxyOld(HLERequestContext& ctx); + std::shared_ptr GetAppletFromContext(HLERequestContext& ctx); + Nvnflinger::Nvnflinger& nvnflinger; - std::shared_ptr msg_queue; }; } // namespace AM diff --git a/src/core/hle/service/am/applet_common_functions.cpp b/src/core/hle/service/am/applet_common_functions.cpp new file mode 100755 index 000000000..130614ae5 --- /dev/null +++ b/src/core/hle/service/am/applet_common_functions.cpp @@ -0,0 +1,63 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/hle/service/am/applet.h" +#include "core/hle/service/am/applet_common_functions.h" +#include "core/hle/service/ipc_helpers.h" + +namespace Service::AM { + +IAppletCommonFunctions::IAppletCommonFunctions(Core::System& system_, + std::shared_ptr applet_) + : ServiceFramework{system_, "IAppletCommonFunctions"}, applet{std::move(applet_)} { + // clang-format off + static const FunctionInfo functions[] = { + {0, nullptr, "SetTerminateResult"}, + {10, nullptr, "ReadThemeStorage"}, + {11, nullptr, "WriteThemeStorage"}, + {20, nullptr, "PushToAppletBoundChannel"}, + {21, nullptr, "TryPopFromAppletBoundChannel"}, + {40, nullptr, "GetDisplayLogicalResolution"}, + {42, nullptr, "SetDisplayMagnification"}, + {50, nullptr, "SetHomeButtonDoubleClickEnabled"}, + {51, nullptr, "GetHomeButtonDoubleClickEnabled"}, + {52, nullptr, "IsHomeButtonShortPressedBlocked"}, + {60, nullptr, "IsVrModeCurtainRequired"}, + {61, nullptr, "IsSleepRequiredByHighTemperature"}, + {62, nullptr, "IsSleepRequiredByLowBattery"}, + {70, &IAppletCommonFunctions::SetCpuBoostRequestPriority, "SetCpuBoostRequestPriority"}, + {80, nullptr, "SetHandlingCaptureButtonShortPressedMessageEnabledForApplet"}, + {81, nullptr, "SetHandlingCaptureButtonLongPressedMessageEnabledForApplet"}, + {90, nullptr, "OpenNamedChannelAsParent"}, + {91, nullptr, "OpenNamedChannelAsChild"}, + {100, nullptr, "SetApplicationCoreUsageMode"}, + {300, &IAppletCommonFunctions::GetCurrentApplicationId, "GetCurrentApplicationId"}, + }; + // clang-format on + + RegisterHandlers(functions); +} + +IAppletCommonFunctions::~IAppletCommonFunctions() = default; + +void IAppletCommonFunctions::SetCpuBoostRequestPriority(HLERequestContext& ctx) { + LOG_WARNING(Service_AM, "(STUBBED) called"); + + IPC::RequestParser rp{ctx}; + + std::scoped_lock lk{applet->lock}; + applet->cpu_boost_request_priority = rp.Pop(); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void IAppletCommonFunctions::GetCurrentApplicationId(HLERequestContext& ctx) { + LOG_WARNING(Service_AM, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 4}; + rb.Push(ResultSuccess); + rb.Push(system.GetApplicationProcessProgramID() & ~0xFFFULL); +} + +} // namespace Service::AM diff --git a/src/core/hle/service/am/applet_common_functions.h b/src/core/hle/service/am/applet_common_functions.h new file mode 100755 index 000000000..b86adf5cb --- /dev/null +++ b/src/core/hle/service/am/applet_common_functions.h @@ -0,0 +1,24 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "core/hle/service/service.h" + +namespace Service::AM { + +struct Applet; + +class IAppletCommonFunctions final : public ServiceFramework { +public: + explicit IAppletCommonFunctions(Core::System& system_, std::shared_ptr applet_); + ~IAppletCommonFunctions() override; + +private: + void SetCpuBoostRequestPriority(HLERequestContext& ctx); + void GetCurrentApplicationId(HLERequestContext& ctx); + + const std::shared_ptr applet; +}; + +} // namespace Service::AM diff --git a/src/core/hle/service/am/applet_data_broker.cpp b/src/core/hle/service/am/applet_data_broker.cpp new file mode 100755 index 000000000..4d58c4db5 --- /dev/null +++ b/src/core/hle/service/am/applet_data_broker.cpp @@ -0,0 +1,67 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "common/scope_exit.h" + +#include "core/core.h" +#include "core/hle/service/am/am_results.h" +#include "core/hle/service/am/applet_data_broker.h" +#include "core/hle/service/am/applet_manager.h" + +namespace Service::AM { + +AppletStorageChannel::AppletStorageChannel(KernelHelpers::ServiceContext& context) + : m_event(context) {} +AppletStorageChannel::~AppletStorageChannel() = default; + +void AppletStorageChannel::Push(std::shared_ptr storage) { + std::scoped_lock lk{m_lock}; + + m_data.emplace_back(std::move(storage)); + m_event.Signal(); +} + +Result AppletStorageChannel::Pop(std::shared_ptr* out_storage) { + std::scoped_lock lk{m_lock}; + + SCOPE_EXIT({ + if (m_data.empty()) { + m_event.Clear(); + } + }); + + R_UNLESS(!m_data.empty(), AM::ResultNoDataInChannel); + + *out_storage = std::move(m_data.front()); + m_data.pop_front(); + + R_SUCCEED(); +} + +Kernel::KReadableEvent* AppletStorageChannel::GetEvent() { + return m_event.GetHandle(); +} + +AppletDataBroker::AppletDataBroker(Core::System& system_) + : system(system_), context(system_, "AppletDataBroker"), in_data(context), + interactive_in_data(context), out_data(context), interactive_out_data(context), + state_changed_event(context), is_completed(false) {} + +AppletDataBroker::~AppletDataBroker() = default; + +void AppletDataBroker::SignalCompletion() { + { + std::scoped_lock lk{lock}; + + if (is_completed) { + return; + } + + is_completed = true; + state_changed_event.Signal(); + } + + system.GetAppletManager().FocusStateChanged(); +} + +} // namespace Service::AM diff --git a/src/core/hle/service/am/applet_data_broker.h b/src/core/hle/service/am/applet_data_broker.h new file mode 100755 index 000000000..12326fd04 --- /dev/null +++ b/src/core/hle/service/am/applet_data_broker.h @@ -0,0 +1,80 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include +#include +#include + +#include "core/hle/service/event.h" +#include "core/hle/service/kernel_helpers.h" + +union Result; + +namespace Service::AM { + +struct Applet; +class IStorage; + +class AppletStorageChannel { +public: + explicit AppletStorageChannel(KernelHelpers::ServiceContext& ctx); + ~AppletStorageChannel(); + + void Push(std::shared_ptr storage); + Result Pop(std::shared_ptr* out_storage); + Kernel::KReadableEvent* GetEvent(); + +private: + std::mutex m_lock{}; + std::deque> m_data{}; + Event m_event; +}; + +class AppletDataBroker { +public: + explicit AppletDataBroker(Core::System& system_); + ~AppletDataBroker(); + + AppletStorageChannel& GetInData() { + return in_data; + } + + AppletStorageChannel& GetInteractiveInData() { + return interactive_in_data; + } + + AppletStorageChannel& GetOutData() { + return out_data; + } + + AppletStorageChannel& GetInteractiveOutData() { + return interactive_out_data; + } + + Event& GetStateChangedEvent() { + return state_changed_event; + } + + bool IsCompleted() const { + return is_completed; + } + + void SignalCompletion(); + +private: + Core::System& system; + KernelHelpers::ServiceContext context; + + AppletStorageChannel in_data; + AppletStorageChannel interactive_in_data; + AppletStorageChannel out_data; + AppletStorageChannel interactive_out_data; + Event state_changed_event; + + std::mutex lock; + bool is_completed; +}; + +} // namespace Service::AM diff --git a/src/core/hle/service/am/applet_manager.cpp b/src/core/hle/service/am/applet_manager.cpp new file mode 100755 index 000000000..527d78b3c --- /dev/null +++ b/src/core/hle/service/am/applet_manager.cpp @@ -0,0 +1,353 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "common/settings.h" +#include "common/uuid.h" +#include "core/core.h" +#include "core/core_timing.h" +#include "core/hle/service/acc/profile_manager.h" +#include "core/hle/service/am/applet_data_broker.h" +#include "core/hle/service/am/applet_manager.h" +#include "core/hle/service/am/frontend/applet_cabinet.h" +#include "core/hle/service/am/frontend/applet_controller.h" +#include "core/hle/service/am/frontend/applet_mii_edit_types.h" +#include "core/hle/service/am/frontend/applet_software_keyboard_types.h" +#include "hid_core/hid_types.h" + +namespace Service::AM { + +namespace { + +constexpr u32 LaunchParameterAccountPreselectedUserMagic = 0xC79497CA; + +struct LaunchParameterAccountPreselectedUser { + u32 magic; + u32 is_account_selected; + Common::UUID current_user; + INSERT_PADDING_BYTES(0x70); +}; +static_assert(sizeof(LaunchParameterAccountPreselectedUser) == 0x88); + +AppletStorageChannel& InitializeFakeCallerApplet(Core::System& system, + std::shared_ptr& applet) { + applet->caller_applet_broker = std::make_shared(system); + return applet->caller_applet_broker->GetInData(); +} + +void PushInShowAlbum(Core::System& system, AppletStorageChannel& channel) { + const CommonArguments arguments{ + .arguments_version = CommonArgumentVersion::Version3, + .size = CommonArgumentSize::Version3, + .library_version = 1, + .theme_color = ThemeColor::BasicBlack, + .play_startup_sound = true, + .system_tick = system.CoreTiming().GetClockTicks(), + }; + + std::vector argument_data(sizeof(arguments)); + std::vector settings_data{2}; + std::memcpy(argument_data.data(), &arguments, sizeof(arguments)); + channel.Push(std::make_shared(system, std::move(argument_data))); + channel.Push(std::make_shared(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(Frontend::ControllerAppletVersion::Version8), + .theme_color = ThemeColor::BasicBlack, + .play_startup_sound = true, + .system_tick = system.CoreTiming().GetClockTicks(), + }; + + Frontend::ControllerSupportArgNew user_args = { + .header = {.player_count_min = 1, + .player_count_max = 4, + .enable_take_over_connection = true, + .enable_left_justify = false, + .enable_permit_joy_dual = true, + .enable_single_mode = false, + .enable_identification_color = false}, + .identification_colors = {}, + .enable_explain_text = false, + .explain_text = {}, + }; + + Frontend::ControllerSupportArgPrivate private_args = { + .arg_private_size = sizeof(Frontend::ControllerSupportArgPrivate), + .arg_size = sizeof(Frontend::ControllerSupportArgNew), + .is_home_menu = true, + .flag_1 = true, + .mode = Frontend::ControllerSupportMode::ShowControllerSupport, + .caller = Frontend::ControllerSupportCaller:: + Application, // switchbrew: Always zero except with + // ShowControllerFirmwareUpdateForSystem/ShowControllerKeyRemappingForSystem, + // which sets this to the input param + .style_set = Core::HID::NpadStyleSet::None, + .joy_hold_type = 0, + }; + std::vector common_args_data(sizeof(common_args)); + std::vector private_args_data(sizeof(private_args)); + std::vector user_args_data(sizeof(user_args)); + + std::memcpy(common_args_data.data(), &common_args, sizeof(common_args)); + std::memcpy(private_args_data.data(), &private_args, sizeof(private_args)); + std::memcpy(user_args_data.data(), &user_args, sizeof(user_args)); + + channel.Push(std::make_shared(system, std::move(common_args_data))); + channel.Push(std::make_shared(system, std::move(private_args_data))); + channel.Push(std::make_shared(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(Frontend::CabinetAppletVersion::Version1), + .theme_color = ThemeColor::BasicBlack, + .play_startup_sound = true, + .system_tick = system.CoreTiming().GetClockTicks(), + }; + + const Frontend::StartParamForAmiiboSettings amiibo_settings{ + .param_1 = 0, + .applet_mode = system.GetFrontendAppletHolder().GetCabinetMode(), + .flags = Frontend::CabinetFlags::None, + .amiibo_settings_1 = 0, + .device_handle = 0, + .tag_info{}, + .register_info{}, + .amiibo_settings_3{}, + }; + + std::vector argument_data(sizeof(arguments)); + std::vector settings_data(sizeof(amiibo_settings)); + std::memcpy(argument_data.data(), &arguments, sizeof(arguments)); + std::memcpy(settings_data.data(), &amiibo_settings, sizeof(amiibo_settings)); + channel.Push(std::make_shared(system, std::move(argument_data))); + channel.Push(std::make_shared(system, std::move(settings_data))); +} + +void PushInShowMiiEditData(Core::System& system, AppletStorageChannel& channel) { + struct MiiEditV3 { + Frontend::MiiEditAppletInputCommon common; + Frontend::MiiEditAppletInputV3 input; + }; + static_assert(sizeof(MiiEditV3) == 0x100, "MiiEditV3 has incorrect size."); + + MiiEditV3 mii_arguments{ + .common = + { + .version = Frontend::MiiEditAppletVersion::Version3, + .applet_mode = Frontend::MiiEditAppletMode::ShowMiiEdit, + }, + .input{}, + }; + + std::vector argument_data(sizeof(mii_arguments)); + std::memcpy(argument_data.data(), &mii_arguments, sizeof(mii_arguments)); + + channel.Push(std::make_shared(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(Frontend::SwkbdAppletVersion::Version524301), + .theme_color = ThemeColor::BasicBlack, + .play_startup_sound = true, + .system_tick = system.CoreTiming().GetClockTicks(), + }; + + std::vector initial_string(0); + + const Frontend::SwkbdConfigCommon swkbd_config{ + .type = Frontend::SwkbdType::Qwerty, + .ok_text{}, + .left_optional_symbol_key{}, + .right_optional_symbol_key{}, + .use_prediction = false, + .key_disable_flags{}, + .initial_cursor_position = Frontend::SwkbdInitialCursorPosition::Start, + .header_text{}, + .sub_text{}, + .guide_text{}, + .max_text_length = 500, + .min_text_length = 0, + .password_mode = Frontend::SwkbdPasswordMode::Disabled, + .text_draw_type = Frontend::SwkbdTextDrawType::Box, + .enable_return_button = true, + .use_utf8 = false, + .use_blur_background = true, + .initial_string_offset{}, + .initial_string_length = static_cast(initial_string.size()), + .user_dictionary_offset{}, + .user_dictionary_entries{}, + .use_text_check = false, + }; + + Frontend::SwkbdConfigNew swkbd_config_new{}; + + std::vector argument_data(sizeof(arguments)); + std::vector swkbd_data(sizeof(swkbd_config) + sizeof(swkbd_config_new)); + std::vector work_buffer(swkbd_config.initial_string_length * sizeof(char16_t)); + + std::memcpy(argument_data.data(), &arguments, sizeof(arguments)); + std::memcpy(swkbd_data.data(), &swkbd_config, sizeof(swkbd_config)); + std::memcpy(swkbd_data.data() + sizeof(swkbd_config), &swkbd_config_new, + sizeof(Frontend::SwkbdConfigNew)); + std::memcpy(work_buffer.data(), initial_string.data(), + swkbd_config.initial_string_length * sizeof(char16_t)); + + channel.Push(std::make_shared(system, std::move(argument_data))); + channel.Push(std::make_shared(system, std::move(swkbd_data))); + channel.Push(std::make_shared(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) { + std::scoped_lock lk{m_lock}; + + m_applets.emplace(applet->aruid, std::move(applet)); +} + +void AppletManager::TerminateAndRemoveApplet(AppletResourceUserId aruid) { + std::shared_ptr 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(m_system, std::make_unique(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(Settings::values.current_user)); + ASSERT(uuid.has_value() && uuid->IsValid()); + lp.current_user = *uuid; + + std::vector 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 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 diff --git a/src/core/hle/service/am/applet_manager.h b/src/core/hle/service/am/applet_manager.h new file mode 100755 index 000000000..4875de309 --- /dev/null +++ b/src/core/hle/service/am/applet_manager.h @@ -0,0 +1,59 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include +#include + +#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); + void TerminateAndRemoveApplet(AppletResourceUserId aruid); + + void CreateAndInsertByFrontendAppletParameters(AppletResourceUserId aruid, + const FrontendAppletParameters& params); + std::shared_ptr 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> m_applets{}; + + // AudioController state goes here +}; + +} // namespace Service::AM diff --git a/src/core/hle/service/am/applet_message_queue.cpp b/src/core/hle/service/am/applet_message_queue.cpp new file mode 100755 index 000000000..5ed996b70 --- /dev/null +++ b/src/core/hle/service/am/applet_message_queue.cpp @@ -0,0 +1,73 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/hle/service/am/applet_message_queue.h" +#include "core/hle/service/ipc_helpers.h" + +namespace Service::AM { + +AppletMessageQueue::AppletMessageQueue(Core::System& system) + : service_context{system, "AppletMessageQueue"} { + on_new_message = service_context.CreateEvent("AMMessageQueue:OnMessageReceived"); + on_operation_mode_changed = service_context.CreateEvent("AMMessageQueue:OperationModeChanged"); +} + +AppletMessageQueue::~AppletMessageQueue() { + service_context.CloseEvent(on_new_message); + service_context.CloseEvent(on_operation_mode_changed); +} + +Kernel::KReadableEvent& AppletMessageQueue::GetMessageReceiveEvent() { + return on_new_message->GetReadableEvent(); +} + +Kernel::KReadableEvent& AppletMessageQueue::GetOperationModeChangedEvent() { + return on_operation_mode_changed->GetReadableEvent(); +} + +void AppletMessageQueue::PushMessage(AppletMessage msg) { + { + std::scoped_lock lk{lock}; + messages.push(msg); + } + on_new_message->Signal(); +} + +AppletMessageQueue::AppletMessage AppletMessageQueue::PopMessage() { + std::scoped_lock lk{lock}; + if (messages.empty()) { + on_new_message->Clear(); + return AppletMessage::None; + } + auto msg = messages.front(); + messages.pop(); + if (messages.empty()) { + on_new_message->Clear(); + } + return msg; +} + +std::size_t AppletMessageQueue::GetMessageCount() const { + std::scoped_lock lk{lock}; + return messages.size(); +} + +void AppletMessageQueue::RequestExit() { + PushMessage(AppletMessage::Exit); +} + +void AppletMessageQueue::RequestResume() { + PushMessage(AppletMessage::Resume); +} + +void AppletMessageQueue::FocusStateChanged() { + PushMessage(AppletMessage::FocusStateChanged); +} + +void AppletMessageQueue::OperationModeChanged() { + PushMessage(AppletMessage::OperationModeChanged); + PushMessage(AppletMessage::PerformanceModeChanged); + on_operation_mode_changed->Signal(); +} + +} // namespace Service::AM diff --git a/src/core/hle/service/am/applet_message_queue.h b/src/core/hle/service/am/applet_message_queue.h new file mode 100755 index 000000000..5cb236d47 --- /dev/null +++ b/src/core/hle/service/am/applet_message_queue.h @@ -0,0 +1,76 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include + +#include "core/hle/service/kernel_helpers.h" +#include "core/hle/service/service.h" + +namespace Kernel { +class KReadableEvent; +} // namespace Kernel + +namespace Service::AM { + +class AppletMessageQueue { +public: + // This is nn::am::AppletMessage + enum class AppletMessage : u32 { + None = 0, + ChangeIntoForeground = 1, + ChangeIntoBackground = 2, + Exit = 4, + ApplicationExited = 6, + FocusStateChanged = 15, + Resume = 16, + DetectShortPressingHomeButton = 20, + DetectLongPressingHomeButton = 21, + DetectShortPressingPowerButton = 22, + DetectMiddlePressingPowerButton = 23, + DetectLongPressingPowerButton = 24, + RequestToPrepareSleep = 25, + FinishedSleepSequence = 26, + SleepRequiredByHighTemperature = 27, + SleepRequiredByLowBattery = 28, + AutoPowerDown = 29, + OperationModeChanged = 30, + PerformanceModeChanged = 31, + DetectReceivingCecSystemStandby = 32, + SdCardRemoved = 33, + LaunchApplicationRequested = 50, + RequestToDisplay = 51, + ShowApplicationLogo = 55, + HideApplicationLogo = 56, + ForceHideApplicationLogo = 57, + FloatingApplicationDetected = 60, + DetectShortPressingCaptureButton = 90, + AlbumScreenShotTaken = 92, + AlbumRecordingSaved = 93, + }; + + explicit AppletMessageQueue(Core::System& system); + ~AppletMessageQueue(); + + Kernel::KReadableEvent& GetMessageReceiveEvent(); + Kernel::KReadableEvent& GetOperationModeChangedEvent(); + void PushMessage(AppletMessage msg); + AppletMessage PopMessage(); + std::size_t GetMessageCount() const; + void RequestExit(); + void RequestResume(); + void FocusStateChanged(); + void OperationModeChanged(); + +private: + KernelHelpers::ServiceContext service_context; + + Kernel::KEvent* on_new_message; + Kernel::KEvent* on_operation_mode_changed; + + mutable std::mutex lock; + std::queue messages; +}; + +} // namespace Service::AM diff --git a/src/core/hle/service/am/applet_oe.cpp b/src/core/hle/service/am/applet_oe.cpp index 6a08dbde5..89c0c00e7 100755 --- a/src/core/hle/service/am/applet_oe.cpp +++ b/src/core/hle/service/am/applet_oe.cpp @@ -1,119 +1,16 @@ // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later -#include "common/logging/log.h" #include "core/hle/service/am/am.h" +#include "core/hle/service/am/applet_manager.h" #include "core/hle/service/am/applet_oe.h" +#include "core/hle/service/am/application_proxy.h" #include "core/hle/service/ipc_helpers.h" -#include "core/hle/service/nvnflinger/nvnflinger.h" namespace Service::AM { -class IApplicationProxy final : public ServiceFramework { -public: - explicit IApplicationProxy(Nvnflinger::Nvnflinger& nvnflinger_, - std::shared_ptr msg_queue_, - Core::System& system_) - : ServiceFramework{system_, "IApplicationProxy"}, - nvnflinger{nvnflinger_}, msg_queue{std::move(msg_queue_)} { - // clang-format off - static const FunctionInfo functions[] = { - {0, &IApplicationProxy::GetCommonStateGetter, "GetCommonStateGetter"}, - {1, &IApplicationProxy::GetSelfController, "GetSelfController"}, - {2, &IApplicationProxy::GetWindowController, "GetWindowController"}, - {3, &IApplicationProxy::GetAudioController, "GetAudioController"}, - {4, &IApplicationProxy::GetDisplayController, "GetDisplayController"}, - {10, nullptr, "GetProcessWindingController"}, - {11, &IApplicationProxy::GetLibraryAppletCreator, "GetLibraryAppletCreator"}, - {20, &IApplicationProxy::GetApplicationFunctions, "GetApplicationFunctions"}, - {1000, &IApplicationProxy::GetDebugFunctions, "GetDebugFunctions"}, - }; - // clang-format on - - RegisterHandlers(functions); - } - -private: - void GetAudioController(HLERequestContext& ctx) { - LOG_DEBUG(Service_AM, "called"); - - IPC::ResponseBuilder rb{ctx, 2, 0, 1}; - rb.Push(ResultSuccess); - rb.PushIpcInterface(system); - } - - void GetDisplayController(HLERequestContext& ctx) { - LOG_DEBUG(Service_AM, "called"); - - IPC::ResponseBuilder rb{ctx, 2, 0, 1}; - rb.Push(ResultSuccess); - rb.PushIpcInterface(system); - } - - void GetDebugFunctions(HLERequestContext& ctx) { - LOG_DEBUG(Service_AM, "called"); - - IPC::ResponseBuilder rb{ctx, 2, 0, 1}; - rb.Push(ResultSuccess); - rb.PushIpcInterface(system); - } - - void GetWindowController(HLERequestContext& ctx) { - LOG_DEBUG(Service_AM, "called"); - - IPC::ResponseBuilder rb{ctx, 2, 0, 1}; - rb.Push(ResultSuccess); - rb.PushIpcInterface(system); - } - - void GetSelfController(HLERequestContext& ctx) { - LOG_DEBUG(Service_AM, "called"); - - IPC::ResponseBuilder rb{ctx, 2, 0, 1}; - rb.Push(ResultSuccess); - rb.PushIpcInterface(system, nvnflinger); - } - - void GetCommonStateGetter(HLERequestContext& ctx) { - LOG_DEBUG(Service_AM, "called"); - - IPC::ResponseBuilder rb{ctx, 2, 0, 1}; - rb.Push(ResultSuccess); - rb.PushIpcInterface(system, msg_queue); - } - - void GetLibraryAppletCreator(HLERequestContext& ctx) { - LOG_DEBUG(Service_AM, "called"); - - IPC::ResponseBuilder rb{ctx, 2, 0, 1}; - rb.Push(ResultSuccess); - rb.PushIpcInterface(system); - } - - void GetApplicationFunctions(HLERequestContext& ctx) { - LOG_DEBUG(Service_AM, "called"); - - IPC::ResponseBuilder rb{ctx, 2, 0, 1}; - rb.Push(ResultSuccess); - rb.PushIpcInterface(system); - } - - Nvnflinger::Nvnflinger& nvnflinger; - std::shared_ptr msg_queue; -}; - -void AppletOE::OpenApplicationProxy(HLERequestContext& ctx) { - LOG_DEBUG(Service_AM, "called"); - - IPC::ResponseBuilder rb{ctx, 2, 0, 1}; - rb.Push(ResultSuccess); - rb.PushIpcInterface(nvnflinger, msg_queue, system); -} - -AppletOE::AppletOE(Nvnflinger::Nvnflinger& nvnflinger_, - std::shared_ptr msg_queue_, Core::System& system_) - : ServiceFramework{system_, "appletOE"}, nvnflinger{nvnflinger_}, msg_queue{ - std::move(msg_queue_)} { +AppletOE::AppletOE(Nvnflinger::Nvnflinger& nvnflinger_, Core::System& system_) + : ServiceFramework{system_, "appletOE"}, nvnflinger{nvnflinger_} { static const FunctionInfo functions[] = { {0, &AppletOE::OpenApplicationProxy, "OpenApplicationProxy"}, }; @@ -122,8 +19,24 @@ AppletOE::AppletOE(Nvnflinger::Nvnflinger& nvnflinger_, AppletOE::~AppletOE() = default; -const std::shared_ptr& AppletOE::GetMessageQueue() const { - return msg_queue; +void AppletOE::OpenApplicationProxy(HLERequestContext& ctx) { + LOG_DEBUG(Service_AM, "called"); + + if (const auto applet = GetAppletFromContext(ctx)) { + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(ResultSuccess); + rb.PushIpcInterface(nvnflinger, applet, system); + } else { + UNIMPLEMENTED(); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultUnknown); + } +} + +std::shared_ptr AppletOE::GetAppletFromContext(HLERequestContext& ctx) { + const auto aruid = ctx.GetPID(); + return system.GetAppletManager().GetByAppletResourceUserId(aruid); } } // namespace Service::AM diff --git a/src/core/hle/service/am/applet_oe.h b/src/core/hle/service/am/applet_oe.h index 71c52be0a..fa37421d8 100755 --- a/src/core/hle/service/am/applet_oe.h +++ b/src/core/hle/service/am/applet_oe.h @@ -18,21 +18,19 @@ class Nvnflinger; namespace AM { -class AppletMessageQueue; +struct Applet; class AppletOE final : public ServiceFramework { public: - explicit AppletOE(Nvnflinger::Nvnflinger& nvnflinger_, - std::shared_ptr msg_queue_, Core::System& system_); + explicit AppletOE(Nvnflinger::Nvnflinger& nvnflinger_, Core::System& system_); ~AppletOE() override; - const std::shared_ptr& GetMessageQueue() const; - private: void OpenApplicationProxy(HLERequestContext& ctx); + std::shared_ptr GetAppletFromContext(HLERequestContext& ctx); + Nvnflinger::Nvnflinger& nvnflinger; - std::shared_ptr msg_queue; }; } // namespace AM diff --git a/src/core/hle/service/am/application_creator.cpp b/src/core/hle/service/am/application_creator.cpp new file mode 100755 index 000000000..79ea045a3 --- /dev/null +++ b/src/core/hle/service/am/application_creator.cpp @@ -0,0 +1,25 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/hle/service/am/application_creator.h" +#include "core/hle/service/ipc_helpers.h" + +namespace Service::AM { + +IApplicationCreator::IApplicationCreator(Core::System& system_) + : ServiceFramework{system_, "IApplicationCreator"} { + // clang-format off + static const FunctionInfo functions[] = { + {0, nullptr, "CreateApplication"}, + {1, nullptr, "PopLaunchRequestedApplication"}, + {10, nullptr, "CreateSystemApplication"}, + {100, nullptr, "PopFloatingApplicationForDevelopment"}, + }; + // clang-format on + + RegisterHandlers(functions); +} + +IApplicationCreator::~IApplicationCreator() = default; + +} // namespace Service::AM diff --git a/src/core/hle/service/am/application_creator.h b/src/core/hle/service/am/application_creator.h new file mode 100755 index 000000000..375a3c476 --- /dev/null +++ b/src/core/hle/service/am/application_creator.h @@ -0,0 +1,16 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "core/hle/service/service.h" + +namespace Service::AM { + +class IApplicationCreator final : public ServiceFramework { +public: + explicit IApplicationCreator(Core::System& system_); + ~IApplicationCreator() override; +}; + +} // namespace Service::AM diff --git a/src/core/hle/service/am/application_functions.cpp b/src/core/hle/service/am/application_functions.cpp new file mode 100755 index 000000000..51c5be2d1 --- /dev/null +++ b/src/core/hle/service/am/application_functions.cpp @@ -0,0 +1,594 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "common/settings.h" +#include "common/uuid.h" +#include "core/file_sys/control_metadata.h" +#include "core/file_sys/patch_manager.h" +#include "core/file_sys/registered_cache.h" +#include "core/file_sys/savedata_factory.h" +#include "core/hle/service/acc/profile_manager.h" +#include "core/hle/service/am/am_results.h" +#include "core/hle/service/am/applet.h" +#include "core/hle/service/am/application_functions.h" +#include "core/hle/service/am/storage.h" +#include "core/hle/service/filesystem/filesystem.h" +#include "core/hle/service/filesystem/save_data_controller.h" +#include "core/hle/service/ipc_helpers.h" +#include "core/hle/service/ns/ns.h" +#include "core/hle/service/sm/sm.h" + +namespace Service::AM { + +enum class LaunchParameterKind : u32 { + UserChannel = 1, + AccountPreselectedUser = 2, +}; + +IApplicationFunctions::IApplicationFunctions(Core::System& system_, std::shared_ptr applet_) + : ServiceFramework{system_, "IApplicationFunctions"}, applet{std::move(applet_)} { + // clang-format off + static const FunctionInfo functions[] = { + {1, &IApplicationFunctions::PopLaunchParameter, "PopLaunchParameter"}, + {10, nullptr, "CreateApplicationAndPushAndRequestToStart"}, + {11, nullptr, "CreateApplicationAndPushAndRequestToStartForQuest"}, + {12, nullptr, "CreateApplicationAndRequestToStart"}, + {13, &IApplicationFunctions::CreateApplicationAndRequestToStartForQuest, "CreateApplicationAndRequestToStartForQuest"}, + {14, nullptr, "CreateApplicationWithAttributeAndPushAndRequestToStartForQuest"}, + {15, nullptr, "CreateApplicationWithAttributeAndRequestToStartForQuest"}, + {20, &IApplicationFunctions::EnsureSaveData, "EnsureSaveData"}, + {21, &IApplicationFunctions::GetDesiredLanguage, "GetDesiredLanguage"}, + {22, &IApplicationFunctions::SetTerminateResult, "SetTerminateResult"}, + {23, &IApplicationFunctions::GetDisplayVersion, "GetDisplayVersion"}, + {24, nullptr, "GetLaunchStorageInfoForDebug"}, + {25, &IApplicationFunctions::ExtendSaveData, "ExtendSaveData"}, + {26, &IApplicationFunctions::GetSaveDataSize, "GetSaveDataSize"}, + {27, &IApplicationFunctions::CreateCacheStorage, "CreateCacheStorage"}, + {28, &IApplicationFunctions::GetSaveDataSizeMax, "GetSaveDataSizeMax"}, + {29, nullptr, "GetCacheStorageMax"}, + {30, &IApplicationFunctions::BeginBlockingHomeButtonShortAndLongPressed, "BeginBlockingHomeButtonShortAndLongPressed"}, + {31, &IApplicationFunctions::EndBlockingHomeButtonShortAndLongPressed, "EndBlockingHomeButtonShortAndLongPressed"}, + {32, &IApplicationFunctions::BeginBlockingHomeButton, "BeginBlockingHomeButton"}, + {33, &IApplicationFunctions::EndBlockingHomeButton, "EndBlockingHomeButton"}, + {34, nullptr, "SelectApplicationLicense"}, + {35, nullptr, "GetDeviceSaveDataSizeMax"}, + {36, nullptr, "GetLimitedApplicationLicense"}, + {37, nullptr, "GetLimitedApplicationLicenseUpgradableEvent"}, + {40, &IApplicationFunctions::NotifyRunning, "NotifyRunning"}, + {50, &IApplicationFunctions::GetPseudoDeviceId, "GetPseudoDeviceId"}, + {60, nullptr, "SetMediaPlaybackStateForApplication"}, + {65, &IApplicationFunctions::IsGamePlayRecordingSupported, "IsGamePlayRecordingSupported"}, + {66, &IApplicationFunctions::InitializeGamePlayRecording, "InitializeGamePlayRecording"}, + {67, &IApplicationFunctions::SetGamePlayRecordingState, "SetGamePlayRecordingState"}, + {68, nullptr, "RequestFlushGamePlayingMovieForDebug"}, + {70, nullptr, "RequestToShutdown"}, + {71, nullptr, "RequestToReboot"}, + {72, nullptr, "RequestToSleep"}, + {80, nullptr, "ExitAndRequestToShowThanksMessage"}, + {90, &IApplicationFunctions::EnableApplicationCrashReport, "EnableApplicationCrashReport"}, + {100, &IApplicationFunctions::InitializeApplicationCopyrightFrameBuffer, "InitializeApplicationCopyrightFrameBuffer"}, + {101, &IApplicationFunctions::SetApplicationCopyrightImage, "SetApplicationCopyrightImage"}, + {102, &IApplicationFunctions::SetApplicationCopyrightVisibility, "SetApplicationCopyrightVisibility"}, + {110, &IApplicationFunctions::QueryApplicationPlayStatistics, "QueryApplicationPlayStatistics"}, + {111, &IApplicationFunctions::QueryApplicationPlayStatisticsByUid, "QueryApplicationPlayStatisticsByUid"}, + {120, &IApplicationFunctions::ExecuteProgram, "ExecuteProgram"}, + {121, &IApplicationFunctions::ClearUserChannel, "ClearUserChannel"}, + {122, &IApplicationFunctions::UnpopToUserChannel, "UnpopToUserChannel"}, + {123, &IApplicationFunctions::GetPreviousProgramIndex, "GetPreviousProgramIndex"}, + {124, nullptr, "EnableApplicationAllThreadDumpOnCrash"}, + {130, &IApplicationFunctions::GetGpuErrorDetectedSystemEvent, "GetGpuErrorDetectedSystemEvent"}, + {131, nullptr, "SetDelayTimeToAbortOnGpuError"}, + {140, &IApplicationFunctions::GetFriendInvitationStorageChannelEvent, "GetFriendInvitationStorageChannelEvent"}, + {141, &IApplicationFunctions::TryPopFromFriendInvitationStorageChannel, "TryPopFromFriendInvitationStorageChannel"}, + {150, &IApplicationFunctions::GetNotificationStorageChannelEvent, "GetNotificationStorageChannelEvent"}, + {151, nullptr, "TryPopFromNotificationStorageChannel"}, + {160, &IApplicationFunctions::GetHealthWarningDisappearedSystemEvent, "GetHealthWarningDisappearedSystemEvent"}, + {170, nullptr, "SetHdcpAuthenticationActivated"}, + {180, nullptr, "GetLaunchRequiredVersion"}, + {181, nullptr, "UpgradeLaunchRequiredVersion"}, + {190, nullptr, "SendServerMaintenanceOverlayNotification"}, + {200, nullptr, "GetLastApplicationExitReason"}, + {500, nullptr, "StartContinuousRecordingFlushForDebug"}, + {1000, nullptr, "CreateMovieMaker"}, + {1001, &IApplicationFunctions::PrepareForJit, "PrepareForJit"}, + }; + // clang-format on + + RegisterHandlers(functions); +} + +IApplicationFunctions::~IApplicationFunctions() = default; + +void IApplicationFunctions::EnableApplicationCrashReport(HLERequestContext& ctx) { + LOG_WARNING(Service_AM, "(STUBBED) called"); + + std::scoped_lock lk{applet->lock}; + applet->application_crash_report_enabled = true; + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void IApplicationFunctions::InitializeApplicationCopyrightFrameBuffer(HLERequestContext& ctx) { + LOG_WARNING(Service_AM, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void IApplicationFunctions::SetApplicationCopyrightImage(HLERequestContext& ctx) { + LOG_WARNING(Service_AM, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void IApplicationFunctions::SetApplicationCopyrightVisibility(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto is_visible = rp.Pop(); + + LOG_WARNING(Service_AM, "(STUBBED) called, is_visible={}", is_visible); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void IApplicationFunctions::BeginBlockingHomeButtonShortAndLongPressed(HLERequestContext& ctx) { + LOG_WARNING(Service_AM, "(STUBBED) called"); + + std::scoped_lock lk{applet->lock}; + applet->home_button_long_pressed_blocked = true; + applet->home_button_short_pressed_blocked = true; + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void IApplicationFunctions::EndBlockingHomeButtonShortAndLongPressed(HLERequestContext& ctx) { + LOG_WARNING(Service_AM, "(STUBBED) called"); + + std::scoped_lock lk{applet->lock}; + applet->home_button_long_pressed_blocked = false; + applet->home_button_short_pressed_blocked = false; + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void IApplicationFunctions::BeginBlockingHomeButton(HLERequestContext& ctx) { + LOG_WARNING(Service_AM, "(STUBBED) called"); + + std::scoped_lock lk{applet->lock}; + applet->home_button_long_pressed_blocked = true; + applet->home_button_short_pressed_blocked = true; + applet->home_button_double_click_enabled = true; + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void IApplicationFunctions::EndBlockingHomeButton(HLERequestContext& ctx) { + LOG_WARNING(Service_AM, "(STUBBED) called"); + + std::scoped_lock lk{applet->lock}; + applet->home_button_long_pressed_blocked = false; + applet->home_button_short_pressed_blocked = false; + applet->home_button_double_click_enabled = false; + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void IApplicationFunctions::PopLaunchParameter(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto kind = rp.PopEnum(); + + LOG_INFO(Service_AM, "called, kind={:08X}", kind); + + std::scoped_lock lk{applet->lock}; + + auto& channel = kind == LaunchParameterKind::UserChannel + ? applet->user_channel_launch_parameter + : applet->preselected_user_launch_parameter; + + if (channel.empty()) { + LOG_WARNING(Service_AM, "Attempted to pop parameter {} but none was found!", kind); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(AM::ResultNoDataInChannel); + return; + } + + auto data = channel.back(); + channel.pop_back(); + + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(ResultSuccess); + rb.PushIpcInterface(system, std::move(data)); +} + +void IApplicationFunctions::CreateApplicationAndRequestToStartForQuest(HLERequestContext& ctx) { + LOG_WARNING(Service_AM, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void IApplicationFunctions::EnsureSaveData(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + u128 user_id = rp.PopRaw(); + + LOG_DEBUG(Service_AM, "called, uid={:016X}{:016X}", user_id[1], user_id[0]); + + FileSys::SaveDataAttribute attribute{}; + attribute.title_id = applet->program_id; + attribute.user_id = user_id; + attribute.type = FileSys::SaveDataType::SaveData; + + FileSys::VirtualDir save_data{}; + const auto res = system.GetFileSystemController().OpenSaveDataController()->CreateSaveData( + &save_data, FileSys::SaveDataSpaceId::NandUser, attribute); + + IPC::ResponseBuilder rb{ctx, 4}; + rb.Push(res); + rb.Push(0); +} + +void IApplicationFunctions::SetTerminateResult(HLERequestContext& ctx) { + // Takes an input u32 Result, no output. + // For example, in some cases official apps use this with error 0x2A2 then + // uses svcBreak. + + IPC::RequestParser rp{ctx}; + u32 result = rp.Pop(); + LOG_WARNING(Service_AM, "(STUBBED) called, result=0x{:08X}", result); + + std::scoped_lock lk{applet->lock}; + applet->terminate_result = Result(result); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void IApplicationFunctions::GetDisplayVersion(HLERequestContext& ctx) { + LOG_DEBUG(Service_AM, "called"); + + std::array version_string{}; + + const auto res = [this] { + const FileSys::PatchManager pm{applet->program_id, system.GetFileSystemController(), + system.GetContentProvider()}; + auto metadata = pm.GetControlMetadata(); + if (metadata.first != nullptr) { + return metadata; + } + + const FileSys::PatchManager pm_update{FileSys::GetUpdateTitleID(applet->program_id), + system.GetFileSystemController(), + system.GetContentProvider()}; + return pm_update.GetControlMetadata(); + }(); + + if (res.first != nullptr) { + const auto& version = res.first->GetVersionString(); + std::copy(version.begin(), version.end(), version_string.begin()); + } else { + static constexpr char default_version[]{"1.0.0"}; + std::memcpy(version_string.data(), default_version, sizeof(default_version)); + } + + IPC::ResponseBuilder rb{ctx, 6}; + rb.Push(ResultSuccess); + rb.PushRaw(version_string); +} + +void IApplicationFunctions::GetDesiredLanguage(HLERequestContext& ctx) { + // TODO(bunnei): This should be configurable + LOG_DEBUG(Service_AM, "called"); + + // Get supported languages from NACP, if possible + // Default to 0 (all languages supported) + u32 supported_languages = 0; + + const auto res = [this] { + const FileSys::PatchManager pm{applet->program_id, system.GetFileSystemController(), + system.GetContentProvider()}; + auto metadata = pm.GetControlMetadata(); + if (metadata.first != nullptr) { + return metadata; + } + + const FileSys::PatchManager pm_update{FileSys::GetUpdateTitleID(applet->program_id), + system.GetFileSystemController(), + system.GetContentProvider()}; + return pm_update.GetControlMetadata(); + }(); + + if (res.first != nullptr) { + supported_languages = res.first->GetSupportedLanguages(); + } + + // Call IApplicationManagerInterface implementation. + auto& service_manager = system.ServiceManager(); + auto ns_am2 = service_manager.GetService("ns:am2"); + auto app_man = ns_am2->GetApplicationManagerInterface(); + + // Get desired application language + u8 desired_language{}; + const auto res_lang = + app_man->GetApplicationDesiredLanguage(&desired_language, supported_languages); + if (res_lang != ResultSuccess) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(res_lang); + return; + } + + // Convert to settings language code. + u64 language_code{}; + const auto res_code = + app_man->ConvertApplicationLanguageToLanguageCode(&language_code, desired_language); + if (res_code != ResultSuccess) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(res_code); + return; + } + + LOG_DEBUG(Service_AM, "got desired_language={:016X}", language_code); + + IPC::ResponseBuilder rb{ctx, 4}; + rb.Push(ResultSuccess); + rb.Push(language_code); +} + +void IApplicationFunctions::IsGamePlayRecordingSupported(HLERequestContext& ctx) { + LOG_WARNING(Service_AM, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.Push(applet->gameplay_recording_supported); +} + +void IApplicationFunctions::InitializeGamePlayRecording(HLERequestContext& ctx) { + LOG_WARNING(Service_AM, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void IApplicationFunctions::SetGamePlayRecordingState(HLERequestContext& ctx) { + LOG_WARNING(Service_AM, "(STUBBED) called"); + + IPC::RequestParser rp{ctx}; + + std::scoped_lock lk{applet->lock}; + applet->gameplay_recording_state = rp.PopRaw(); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void IApplicationFunctions::NotifyRunning(HLERequestContext& ctx) { + LOG_WARNING(Service_AM, "(STUBBED) called"); + + std::scoped_lock lk{applet->lock}; + applet->is_running = true; + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.Push(0); // Unknown, seems to be ignored by official processes +} + +void IApplicationFunctions::GetPseudoDeviceId(HLERequestContext& ctx) { + LOG_WARNING(Service_AM, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 6}; + rb.Push(ResultSuccess); + + // Returns a 128-bit UUID + rb.Push(0); + rb.Push(0); +} + +void IApplicationFunctions::ExtendSaveData(HLERequestContext& ctx) { + struct Parameters { + FileSys::SaveDataType type; + u128 user_id; + u64 new_normal_size; + u64 new_journal_size; + }; + static_assert(sizeof(Parameters) == 40); + + IPC::RequestParser rp{ctx}; + const auto [type, user_id, new_normal_size, new_journal_size] = rp.PopRaw(); + + LOG_DEBUG(Service_AM, + "called with type={:02X}, user_id={:016X}{:016X}, new_normal={:016X}, " + "new_journal={:016X}", + static_cast(type), user_id[1], user_id[0], new_normal_size, new_journal_size); + + system.GetFileSystemController().OpenSaveDataController()->WriteSaveDataSize( + type, applet->program_id, user_id, {new_normal_size, new_journal_size}); + + IPC::ResponseBuilder rb{ctx, 4}; + rb.Push(ResultSuccess); + + // The following value is used upon failure to help the system recover. + // Since we always succeed, this should be 0. + rb.Push(0); +} + +void IApplicationFunctions::GetSaveDataSize(HLERequestContext& ctx) { + struct Parameters { + FileSys::SaveDataType type; + u128 user_id; + }; + static_assert(sizeof(Parameters) == 24); + + IPC::RequestParser rp{ctx}; + const auto [type, user_id] = rp.PopRaw(); + + LOG_DEBUG(Service_AM, "called with type={:02X}, user_id={:016X}{:016X}", type, user_id[1], + user_id[0]); + + const auto size = system.GetFileSystemController().OpenSaveDataController()->ReadSaveDataSize( + type, applet->program_id, user_id); + + IPC::ResponseBuilder rb{ctx, 6}; + rb.Push(ResultSuccess); + rb.Push(size.normal); + rb.Push(size.journal); +} + +void IApplicationFunctions::CreateCacheStorage(HLERequestContext& ctx) { + struct InputParameters { + u16 index; + s64 size; + s64 journal_size; + }; + static_assert(sizeof(InputParameters) == 24); + + struct OutputParameters { + u32 storage_target; + u64 required_size; + }; + static_assert(sizeof(OutputParameters) == 16); + + IPC::RequestParser rp{ctx}; + const auto params = rp.PopRaw(); + + LOG_WARNING(Service_AM, "(STUBBED) called with index={}, size={:#x}, journal_size={:#x}", + params.index, params.size, params.journal_size); + + const OutputParameters resp{ + .storage_target = 1, + .required_size = 0, + }; + + IPC::ResponseBuilder rb{ctx, 6}; + rb.Push(ResultSuccess); + rb.PushRaw(resp); +} + +void IApplicationFunctions::GetSaveDataSizeMax(HLERequestContext& ctx) { + LOG_WARNING(Service_AM, "(STUBBED) called"); + + constexpr u64 size_max_normal = 0xFFFFFFF; + constexpr u64 size_max_journal = 0xFFFFFFF; + + IPC::ResponseBuilder rb{ctx, 6}; + rb.Push(ResultSuccess); + rb.Push(size_max_normal); + rb.Push(size_max_journal); +} + +void IApplicationFunctions::QueryApplicationPlayStatistics(HLERequestContext& ctx) { + LOG_WARNING(Service_AM, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.Push(0); +} + +void IApplicationFunctions::QueryApplicationPlayStatisticsByUid(HLERequestContext& ctx) { + LOG_WARNING(Service_AM, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.Push(0); +} + +void IApplicationFunctions::ExecuteProgram(HLERequestContext& ctx) { + LOG_WARNING(Service_AM, "(STUBBED) called"); + + IPC::RequestParser rp{ctx}; + [[maybe_unused]] const auto unk_1 = rp.Pop(); + [[maybe_unused]] const auto unk_2 = rp.Pop(); + const auto program_index = rp.Pop(); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); + + // Swap user channel ownership into the system so that it will be preserved + system.GetUserChannel().swap(applet->user_channel_launch_parameter); + system.ExecuteProgram(program_index); +} + +void IApplicationFunctions::ClearUserChannel(HLERequestContext& ctx) { + LOG_DEBUG(Service_AM, "called"); + + applet->user_channel_launch_parameter.clear(); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void IApplicationFunctions::UnpopToUserChannel(HLERequestContext& ctx) { + LOG_DEBUG(Service_AM, "called"); + + IPC::RequestParser rp{ctx}; + const auto storage = rp.PopIpcInterface().lock(); + if (storage) { + applet->user_channel_launch_parameter.push_back(storage->GetData()); + } + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void IApplicationFunctions::GetPreviousProgramIndex(HLERequestContext& ctx) { + LOG_WARNING(Service_AM, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.Push(applet->previous_program_index); +} + +void IApplicationFunctions::GetGpuErrorDetectedSystemEvent(HLERequestContext& ctx) { + LOG_WARNING(Service_AM, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 2, 1}; + rb.Push(ResultSuccess); + rb.PushCopyObjects(applet->gpu_error_detected_event.GetHandle()); +} + +void IApplicationFunctions::GetFriendInvitationStorageChannelEvent(HLERequestContext& ctx) { + LOG_DEBUG(Service_AM, "called"); + + IPC::ResponseBuilder rb{ctx, 2, 1}; + rb.Push(ResultSuccess); + rb.PushCopyObjects(applet->friend_invitation_storage_channel_event.GetHandle()); +} + +void IApplicationFunctions::TryPopFromFriendInvitationStorageChannel(HLERequestContext& ctx) { + LOG_DEBUG(Service_AM, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(AM::ResultNoDataInChannel); +} + +void IApplicationFunctions::GetNotificationStorageChannelEvent(HLERequestContext& ctx) { + LOG_DEBUG(Service_AM, "called"); + + IPC::ResponseBuilder rb{ctx, 2, 1}; + rb.Push(ResultSuccess); + rb.PushCopyObjects(applet->notification_storage_channel_event.GetHandle()); +} + +void IApplicationFunctions::GetHealthWarningDisappearedSystemEvent(HLERequestContext& ctx) { + LOG_DEBUG(Service_AM, "called"); + + IPC::ResponseBuilder rb{ctx, 2, 1}; + rb.Push(ResultSuccess); + rb.PushCopyObjects(applet->health_warning_disappeared_system_event.GetHandle()); +} + +void IApplicationFunctions::PrepareForJit(HLERequestContext& ctx) { + LOG_WARNING(Service_AM, "(STUBBED) called"); + + std::scoped_lock lk{applet->lock}; + applet->jit_service_launched = true; + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +} // namespace Service::AM diff --git a/src/core/hle/service/am/application_functions.h b/src/core/hle/service/am/application_functions.h new file mode 100755 index 000000000..55eb21d39 --- /dev/null +++ b/src/core/hle/service/am/application_functions.h @@ -0,0 +1,58 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "core/hle/service/kernel_helpers.h" +#include "core/hle/service/service.h" + +namespace Service::AM { + +struct Applet; + +class IApplicationFunctions final : public ServiceFramework { +public: + explicit IApplicationFunctions(Core::System& system_, std::shared_ptr applet_); + ~IApplicationFunctions() override; + +private: + void PopLaunchParameter(HLERequestContext& ctx); + void CreateApplicationAndRequestToStartForQuest(HLERequestContext& ctx); + void EnsureSaveData(HLERequestContext& ctx); + void SetTerminateResult(HLERequestContext& ctx); + void GetDisplayVersion(HLERequestContext& ctx); + void GetDesiredLanguage(HLERequestContext& ctx); + void IsGamePlayRecordingSupported(HLERequestContext& ctx); + void InitializeGamePlayRecording(HLERequestContext& ctx); + void SetGamePlayRecordingState(HLERequestContext& ctx); + void NotifyRunning(HLERequestContext& ctx); + void GetPseudoDeviceId(HLERequestContext& ctx); + void ExtendSaveData(HLERequestContext& ctx); + void GetSaveDataSize(HLERequestContext& ctx); + void CreateCacheStorage(HLERequestContext& ctx); + void GetSaveDataSizeMax(HLERequestContext& ctx); + void BeginBlockingHomeButtonShortAndLongPressed(HLERequestContext& ctx); + void EndBlockingHomeButtonShortAndLongPressed(HLERequestContext& ctx); + void BeginBlockingHomeButton(HLERequestContext& ctx); + void EndBlockingHomeButton(HLERequestContext& ctx); + void EnableApplicationCrashReport(HLERequestContext& ctx); + void InitializeApplicationCopyrightFrameBuffer(HLERequestContext& ctx); + void SetApplicationCopyrightImage(HLERequestContext& ctx); + void SetApplicationCopyrightVisibility(HLERequestContext& ctx); + void QueryApplicationPlayStatistics(HLERequestContext& ctx); + void QueryApplicationPlayStatisticsByUid(HLERequestContext& ctx); + void ExecuteProgram(HLERequestContext& ctx); + void ClearUserChannel(HLERequestContext& ctx); + void UnpopToUserChannel(HLERequestContext& ctx); + void GetPreviousProgramIndex(HLERequestContext& ctx); + void GetGpuErrorDetectedSystemEvent(HLERequestContext& ctx); + void GetFriendInvitationStorageChannelEvent(HLERequestContext& ctx); + void TryPopFromFriendInvitationStorageChannel(HLERequestContext& ctx); + void GetNotificationStorageChannelEvent(HLERequestContext& ctx); + void GetHealthWarningDisappearedSystemEvent(HLERequestContext& ctx); + void PrepareForJit(HLERequestContext& ctx); + + const std::shared_ptr applet; +}; + +} // namespace Service::AM diff --git a/src/core/hle/service/am/application_proxy.cpp b/src/core/hle/service/am/application_proxy.cpp new file mode 100755 index 000000000..a6fd6d37f --- /dev/null +++ b/src/core/hle/service/am/application_proxy.cpp @@ -0,0 +1,115 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/hle/service/am/applet_common_functions.h" +#include "core/hle/service/am/application_functions.h" +#include "core/hle/service/am/application_proxy.h" +#include "core/hle/service/am/audio_controller.h" +#include "core/hle/service/am/common_state_getter.h" +#include "core/hle/service/am/debug_functions.h" +#include "core/hle/service/am/display_controller.h" +#include "core/hle/service/am/library_applet_creator.h" +#include "core/hle/service/am/library_applet_self_accessor.h" +#include "core/hle/service/am/process_winding_controller.h" +#include "core/hle/service/am/self_controller.h" +#include "core/hle/service/am/window_controller.h" +#include "core/hle/service/ipc_helpers.h" + +namespace Service::AM { + +IApplicationProxy::IApplicationProxy(Nvnflinger::Nvnflinger& nvnflinger_, + std::shared_ptr applet_, Core::System& system_) + : ServiceFramework{system_, "IApplicationProxy"}, nvnflinger{nvnflinger_}, applet{std::move( + applet_)} { + // clang-format off + static const FunctionInfo functions[] = { + {0, &IApplicationProxy::GetCommonStateGetter, "GetCommonStateGetter"}, + {1, &IApplicationProxy::GetSelfController, "GetSelfController"}, + {2, &IApplicationProxy::GetWindowController, "GetWindowController"}, + {3, &IApplicationProxy::GetAudioController, "GetAudioController"}, + {4, &IApplicationProxy::GetDisplayController, "GetDisplayController"}, + {10, &IApplicationProxy::GetProcessWindingController, "GetProcessWindingController"}, + {11, &IApplicationProxy::GetLibraryAppletCreator, "GetLibraryAppletCreator"}, + {20, &IApplicationProxy::GetApplicationFunctions, "GetApplicationFunctions"}, + {1000, &IApplicationProxy::GetDebugFunctions, "GetDebugFunctions"}, + }; + // clang-format on + + RegisterHandlers(functions); +} + +IApplicationProxy::~IApplicationProxy() = default; + +void IApplicationProxy::GetAudioController(HLERequestContext& ctx) { + LOG_DEBUG(Service_AM, "called"); + + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(ResultSuccess); + rb.PushIpcInterface(system); +} + +void IApplicationProxy::GetDisplayController(HLERequestContext& ctx) { + LOG_DEBUG(Service_AM, "called"); + + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(ResultSuccess); + rb.PushIpcInterface(system, applet); +} + +void IApplicationProxy::GetProcessWindingController(HLERequestContext& ctx) { + LOG_DEBUG(Service_AM, "called"); + + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(ResultSuccess); + rb.PushIpcInterface(system, applet); +} + +void IApplicationProxy::GetDebugFunctions(HLERequestContext& ctx) { + LOG_DEBUG(Service_AM, "called"); + + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(ResultSuccess); + rb.PushIpcInterface(system); +} + +void IApplicationProxy::GetWindowController(HLERequestContext& ctx) { + LOG_DEBUG(Service_AM, "called"); + + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(ResultSuccess); + rb.PushIpcInterface(system, applet); +} + +void IApplicationProxy::GetSelfController(HLERequestContext& ctx) { + LOG_DEBUG(Service_AM, "called"); + + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(ResultSuccess); + rb.PushIpcInterface(system, applet, nvnflinger); +} + +void IApplicationProxy::GetCommonStateGetter(HLERequestContext& ctx) { + LOG_DEBUG(Service_AM, "called"); + + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(ResultSuccess); + rb.PushIpcInterface(system, applet); +} + +void IApplicationProxy::GetLibraryAppletCreator(HLERequestContext& ctx) { + LOG_DEBUG(Service_AM, "called"); + + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(ResultSuccess); + rb.PushIpcInterface(system, applet); +} + +void IApplicationProxy::GetApplicationFunctions(HLERequestContext& ctx) { + LOG_DEBUG(Service_AM, "called"); + + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(ResultSuccess); + rb.PushIpcInterface(system, applet); +} + +} // namespace Service::AM diff --git a/src/core/hle/service/am/application_proxy.h b/src/core/hle/service/am/application_proxy.h new file mode 100755 index 000000000..eb98b095c --- /dev/null +++ b/src/core/hle/service/am/application_proxy.h @@ -0,0 +1,33 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "core/hle/service/service.h" + +namespace Service::AM { + +struct Applet; + +class IApplicationProxy final : public ServiceFramework { +public: + explicit IApplicationProxy(Nvnflinger::Nvnflinger& nvnflinger_, + std::shared_ptr msg_queue_, Core::System& system_); + ~IApplicationProxy(); + +private: + void GetAudioController(HLERequestContext& ctx); + void GetDisplayController(HLERequestContext& ctx); + void GetProcessWindingController(HLERequestContext& ctx); + void GetDebugFunctions(HLERequestContext& ctx); + void GetWindowController(HLERequestContext& ctx); + void GetSelfController(HLERequestContext& ctx); + void GetCommonStateGetter(HLERequestContext& ctx); + void GetLibraryAppletCreator(HLERequestContext& ctx); + void GetApplicationFunctions(HLERequestContext& ctx); + + Nvnflinger::Nvnflinger& nvnflinger; + std::shared_ptr applet; +}; + +} // namespace Service::AM diff --git a/src/core/hle/service/am/audio_controller.cpp b/src/core/hle/service/am/audio_controller.cpp new file mode 100755 index 000000000..ae75db174 --- /dev/null +++ b/src/core/hle/service/am/audio_controller.cpp @@ -0,0 +1,91 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/hle/service/am/audio_controller.h" +#include "core/hle/service/ipc_helpers.h" + +namespace Service::AM { + +IAudioController::IAudioController(Core::System& system_) + : ServiceFramework{system_, "IAudioController"} { + // clang-format off + static const FunctionInfo functions[] = { + {0, &IAudioController::SetExpectedMasterVolume, "SetExpectedMasterVolume"}, + {1, &IAudioController::GetMainAppletExpectedMasterVolume, "GetMainAppletExpectedMasterVolume"}, + {2, &IAudioController::GetLibraryAppletExpectedMasterVolume, "GetLibraryAppletExpectedMasterVolume"}, + {3, &IAudioController::ChangeMainAppletMasterVolume, "ChangeMainAppletMasterVolume"}, + {4, &IAudioController::SetTransparentAudioRate, "SetTransparentVolumeRate"}, + }; + // clang-format on + + RegisterHandlers(functions); +} + +IAudioController::~IAudioController() = default; + +void IAudioController::SetExpectedMasterVolume(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const float main_applet_volume_tmp = rp.Pop(); + const float library_applet_volume_tmp = rp.Pop(); + + LOG_DEBUG(Service_AM, "called. main_applet_volume={}, library_applet_volume={}", + main_applet_volume_tmp, library_applet_volume_tmp); + + // Ensure the volume values remain within the 0-100% range + main_applet_volume = std::clamp(main_applet_volume_tmp, min_allowed_volume, max_allowed_volume); + library_applet_volume = + std::clamp(library_applet_volume_tmp, min_allowed_volume, max_allowed_volume); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void IAudioController::GetMainAppletExpectedMasterVolume(HLERequestContext& ctx) { + LOG_DEBUG(Service_AM, "called. main_applet_volume={}", main_applet_volume); + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.Push(main_applet_volume); +} + +void IAudioController::GetLibraryAppletExpectedMasterVolume(HLERequestContext& ctx) { + LOG_DEBUG(Service_AM, "called. library_applet_volume={}", library_applet_volume); + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.Push(library_applet_volume); +} + +void IAudioController::ChangeMainAppletMasterVolume(HLERequestContext& ctx) { + struct Parameters { + float volume; + s64 fade_time_ns; + }; + static_assert(sizeof(Parameters) == 16); + + IPC::RequestParser rp{ctx}; + const auto parameters = rp.PopRaw(); + + LOG_DEBUG(Service_AM, "called. volume={}, fade_time_ns={}", parameters.volume, + parameters.fade_time_ns); + + main_applet_volume = std::clamp(parameters.volume, min_allowed_volume, max_allowed_volume); + fade_time_ns = std::chrono::nanoseconds{parameters.fade_time_ns}; + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void IAudioController::SetTransparentAudioRate(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const float transparent_volume_rate_tmp = rp.Pop(); + + LOG_DEBUG(Service_AM, "called. transparent_volume_rate={}", transparent_volume_rate_tmp); + + // Clamp volume range to 0-100%. + transparent_volume_rate = + std::clamp(transparent_volume_rate_tmp, min_allowed_volume, max_allowed_volume); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +} // namespace Service::AM diff --git a/src/core/hle/service/am/audio_controller.h b/src/core/hle/service/am/audio_controller.h new file mode 100755 index 000000000..a47e3bad8 --- /dev/null +++ b/src/core/hle/service/am/audio_controller.h @@ -0,0 +1,36 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "core/hle/service/service.h" + +namespace Service::AM { + +class IAudioController final : public ServiceFramework { +public: + explicit IAudioController(Core::System& system_); + ~IAudioController() override; + +private: + void SetExpectedMasterVolume(HLERequestContext& ctx); + void GetMainAppletExpectedMasterVolume(HLERequestContext& ctx); + void GetLibraryAppletExpectedMasterVolume(HLERequestContext& ctx); + void ChangeMainAppletMasterVolume(HLERequestContext& ctx); + void SetTransparentAudioRate(HLERequestContext& ctx); + + static constexpr float min_allowed_volume = 0.0f; + static constexpr float max_allowed_volume = 1.0f; + + float main_applet_volume{0.25f}; + float library_applet_volume{max_allowed_volume}; + float transparent_volume_rate{min_allowed_volume}; + + // Volume transition fade time in nanoseconds. + // e.g. If the main applet volume was 0% and was changed to 50% + // with a fade of 50ns, then over the course of 50ns, + // the volume will gradually fade up to 50% + std::chrono::nanoseconds fade_time_ns{0}; +}; + +} // namespace Service::AM diff --git a/src/core/hle/service/am/common_state_getter.cpp b/src/core/hle/service/am/common_state_getter.cpp new file mode 100755 index 000000000..937ac0beb --- /dev/null +++ b/src/core/hle/service/am/common_state_getter.cpp @@ -0,0 +1,314 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "common/settings.h" +#include "core/hle/service/am/am_results.h" +#include "core/hle/service/am/applet.h" +#include "core/hle/service/am/common_state_getter.h" +#include "core/hle/service/am/lock_accessor.h" +#include "core/hle/service/apm/apm_controller.h" +#include "core/hle/service/apm/apm_interface.h" +#include "core/hle/service/ipc_helpers.h" +#include "core/hle/service/pm/pm.h" +#include "core/hle/service/sm/sm.h" +#include "core/hle/service/vi/vi.h" + +namespace Service::AM { + +ICommonStateGetter::ICommonStateGetter(Core::System& system_, std::shared_ptr applet_) + : ServiceFramework{system_, "ICommonStateGetter"}, applet{std::move(applet_)} { + // clang-format off + static const FunctionInfo functions[] = { + {0, &ICommonStateGetter::GetEventHandle, "GetEventHandle"}, + {1, &ICommonStateGetter::ReceiveMessage, "ReceiveMessage"}, + {2, nullptr, "GetThisAppletKind"}, + {3, nullptr, "AllowToEnterSleep"}, + {4, nullptr, "DisallowToEnterSleep"}, + {5, &ICommonStateGetter::GetOperationMode, "GetOperationMode"}, + {6, &ICommonStateGetter::GetPerformanceMode, "GetPerformanceMode"}, + {7, nullptr, "GetCradleStatus"}, + {8, &ICommonStateGetter::GetBootMode, "GetBootMode"}, + {9, &ICommonStateGetter::GetCurrentFocusState, "GetCurrentFocusState"}, + {10, &ICommonStateGetter::RequestToAcquireSleepLock, "RequestToAcquireSleepLock"}, + {11, nullptr, "ReleaseSleepLock"}, + {12, nullptr, "ReleaseSleepLockTransiently"}, + {13, &ICommonStateGetter::GetAcquiredSleepLockEvent, "GetAcquiredSleepLockEvent"}, + {14, nullptr, "GetWakeupCount"}, + {20, nullptr, "PushToGeneralChannel"}, + {30, nullptr, "GetHomeButtonReaderLockAccessor"}, + {31, &ICommonStateGetter::GetReaderLockAccessorEx, "GetReaderLockAccessorEx"}, + {32, nullptr, "GetWriterLockAccessorEx"}, + {40, nullptr, "GetCradleFwVersion"}, + {50, &ICommonStateGetter::IsVrModeEnabled, "IsVrModeEnabled"}, + {51, &ICommonStateGetter::SetVrModeEnabled, "SetVrModeEnabled"}, + {52, &ICommonStateGetter::SetLcdBacklighOffEnabled, "SetLcdBacklighOffEnabled"}, + {53, &ICommonStateGetter::BeginVrModeEx, "BeginVrModeEx"}, + {54, &ICommonStateGetter::EndVrModeEx, "EndVrModeEx"}, + {55, nullptr, "IsInControllerFirmwareUpdateSection"}, + {59, nullptr, "SetVrPositionForDebug"}, + {60, &ICommonStateGetter::GetDefaultDisplayResolution, "GetDefaultDisplayResolution"}, + {61, &ICommonStateGetter::GetDefaultDisplayResolutionChangeEvent, "GetDefaultDisplayResolutionChangeEvent"}, + {62, nullptr, "GetHdcpAuthenticationState"}, + {63, nullptr, "GetHdcpAuthenticationStateChangeEvent"}, + {64, nullptr, "SetTvPowerStateMatchingMode"}, + {65, nullptr, "GetApplicationIdByContentActionName"}, + {66, &ICommonStateGetter::SetCpuBoostMode, "SetCpuBoostMode"}, + {67, nullptr, "CancelCpuBoostMode"}, + {68, &ICommonStateGetter::GetBuiltInDisplayType, "GetBuiltInDisplayType"}, + {80, &ICommonStateGetter::PerformSystemButtonPressingIfInFocus, "PerformSystemButtonPressingIfInFocus"}, + {90, nullptr, "SetPerformanceConfigurationChangedNotification"}, + {91, nullptr, "GetCurrentPerformanceConfiguration"}, + {100, nullptr, "SetHandlingHomeButtonShortPressedEnabled"}, + {110, nullptr, "OpenMyGpuErrorHandler"}, + {120, &ICommonStateGetter::GetAppletLaunchedHistory, "GetAppletLaunchedHistory"}, + {200, nullptr, "GetOperationModeSystemInfo"}, + {300, &ICommonStateGetter::GetSettingsPlatformRegion, "GetSettingsPlatformRegion"}, + {400, nullptr, "ActivateMigrationService"}, + {401, nullptr, "DeactivateMigrationService"}, + {500, nullptr, "DisableSleepTillShutdown"}, + {501, nullptr, "SuppressDisablingSleepTemporarily"}, + {502, nullptr, "IsSleepEnabled"}, + {503, nullptr, "IsDisablingSleepSuppressed"}, + {900, &ICommonStateGetter::SetRequestExitToLibraryAppletAtExecuteNextProgramEnabled, "SetRequestExitToLibraryAppletAtExecuteNextProgramEnabled"}, + }; + // clang-format on + + RegisterHandlers(functions); +} + +ICommonStateGetter::~ICommonStateGetter() = default; + +void ICommonStateGetter::GetBootMode(HLERequestContext& ctx) { + LOG_DEBUG(Service_AM, "called"); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.Push(static_cast(Service::PM::SystemBootMode::Normal)); // Normal boot mode +} + +void ICommonStateGetter::GetEventHandle(HLERequestContext& ctx) { + LOG_DEBUG(Service_AM, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 2, 1}; + rb.Push(ResultSuccess); + rb.PushCopyObjects(applet->message_queue.GetMessageReceiveEvent()); +} + +void ICommonStateGetter::ReceiveMessage(HLERequestContext& ctx) { + LOG_DEBUG(Service_AM, "called"); + + const auto message = applet->message_queue.PopMessage(); + IPC::ResponseBuilder rb{ctx, 3}; + + if (message == AppletMessageQueue::AppletMessage::None) { + LOG_ERROR(Service_AM, "Message queue is empty"); + rb.Push(AM::ResultNoMessages); + rb.PushEnum(message); + return; + } + + rb.Push(ResultSuccess); + rb.PushEnum(message); +} + +void ICommonStateGetter::GetCurrentFocusState(HLERequestContext& ctx) { + LOG_DEBUG(Service_AM, "(STUBBED) called"); + + std::scoped_lock lk{applet->lock}; + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.Push(static_cast(applet->focus_state)); +} + +void ICommonStateGetter::GetOperationMode(HLERequestContext& ctx) { + const bool use_docked_mode{Settings::IsDockedMode()}; + LOG_DEBUG(Service_AM, "called, use_docked_mode={}", use_docked_mode); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.Push(static_cast(use_docked_mode ? OperationMode::Docked : OperationMode::Handheld)); +} + +void ICommonStateGetter::GetPerformanceMode(HLERequestContext& ctx) { + LOG_DEBUG(Service_AM, "called"); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.PushEnum(system.GetAPMController().GetCurrentPerformanceMode()); +} + +void ICommonStateGetter::RequestToAcquireSleepLock(HLERequestContext& ctx) { + LOG_WARNING(Service_AM, "(STUBBED) called"); + + // Sleep lock is acquired immediately. + applet->sleep_lock_event.Signal(); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void ICommonStateGetter::GetReaderLockAccessorEx(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto unknown = rp.Pop(); + + LOG_INFO(Service_AM, "called, unknown={}", unknown); + + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + + rb.Push(ResultSuccess); + rb.PushIpcInterface(system); +} + +void ICommonStateGetter::GetAcquiredSleepLockEvent(HLERequestContext& ctx) { + LOG_WARNING(Service_AM, "called"); + + IPC::ResponseBuilder rb{ctx, 2, 1}; + rb.Push(ResultSuccess); + rb.PushCopyObjects(applet->sleep_lock_event.GetHandle()); +} + +void ICommonStateGetter::IsVrModeEnabled(HLERequestContext& ctx) { + LOG_DEBUG(Service_AM, "called"); + + std::scoped_lock lk{applet->lock}; + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.Push(applet->vr_mode_enabled); +} + +void ICommonStateGetter::SetVrModeEnabled(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + + std::scoped_lock lk{applet->lock}; + applet->vr_mode_enabled = rp.Pop(); + LOG_WARNING(Service_AM, "VR Mode is {}", applet->vr_mode_enabled ? "on" : "off"); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void ICommonStateGetter::SetLcdBacklighOffEnabled(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto is_lcd_backlight_off_enabled = rp.Pop(); + + LOG_WARNING(Service_AM, "(STUBBED) called. is_lcd_backlight_off_enabled={}", + is_lcd_backlight_off_enabled); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void ICommonStateGetter::BeginVrModeEx(HLERequestContext& ctx) { + LOG_WARNING(Service_AM, "(STUBBED) called"); + + std::scoped_lock lk{applet->lock}; + applet->vr_mode_enabled = true; + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void ICommonStateGetter::EndVrModeEx(HLERequestContext& ctx) { + LOG_WARNING(Service_AM, "(STUBBED) called"); + + std::scoped_lock lk{applet->lock}; + applet->vr_mode_enabled = false; + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void ICommonStateGetter::GetDefaultDisplayResolutionChangeEvent(HLERequestContext& ctx) { + LOG_DEBUG(Service_AM, "called"); + + IPC::ResponseBuilder rb{ctx, 2, 1}; + rb.Push(ResultSuccess); + rb.PushCopyObjects(applet->message_queue.GetOperationModeChangedEvent()); +} + +void ICommonStateGetter::GetDefaultDisplayResolution(HLERequestContext& ctx) { + LOG_DEBUG(Service_AM, "called"); + + IPC::ResponseBuilder rb{ctx, 4}; + rb.Push(ResultSuccess); + + if (Settings::IsDockedMode()) { + rb.Push(static_cast(Service::VI::DisplayResolution::DockedWidth)); + rb.Push(static_cast(Service::VI::DisplayResolution::DockedHeight)); + } else { + rb.Push(static_cast(Service::VI::DisplayResolution::UndockedWidth)); + rb.Push(static_cast(Service::VI::DisplayResolution::UndockedHeight)); + } +} + +void ICommonStateGetter::SetCpuBoostMode(HLERequestContext& ctx) { + LOG_DEBUG(Service_AM, "called, forwarding to APM:SYS"); + + const auto& sm = system.ServiceManager(); + const auto apm_sys = sm.GetService("apm:sys"); + ASSERT(apm_sys != nullptr); + + apm_sys->SetCpuBoostMode(ctx); +} + +void ICommonStateGetter::GetBuiltInDisplayType(HLERequestContext& ctx) { + LOG_WARNING(Service_AM, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.Push(0); +} + +void ICommonStateGetter::PerformSystemButtonPressingIfInFocus(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto system_button{rp.PopEnum()}; + + LOG_WARNING(Service_AM, "(STUBBED) called, system_button={}", system_button); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void ICommonStateGetter::GetAppletLaunchedHistory(HLERequestContext& ctx) { + LOG_WARNING(Service_AM, "(STUBBED) called"); + + std::shared_ptr current_applet = applet; + std::vector result; + + const size_t count = ctx.GetWriteBufferNumElements(); + size_t i; + + for (i = 0; i < count && current_applet != nullptr; i++) { + result.push_back(current_applet->applet_id); + current_applet = current_applet->caller_applet.lock(); + } + + ctx.WriteBuffer(result); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.Push(static_cast(i)); +} + +void ICommonStateGetter::GetSettingsPlatformRegion(HLERequestContext& ctx) { + LOG_WARNING(Service_AM, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.PushEnum(SysPlatformRegion::Global); +} + +void ICommonStateGetter::SetRequestExitToLibraryAppletAtExecuteNextProgramEnabled( + HLERequestContext& ctx) { + LOG_WARNING(Service_AM, "(STUBBED) called"); + + std::scoped_lock lk{applet->lock}; + applet->request_exit_to_library_applet_at_execute_next_program_enabled = true; + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +} // namespace Service::AM diff --git a/src/core/hle/service/am/common_state_getter.h b/src/core/hle/service/am/common_state_getter.h new file mode 100755 index 000000000..bf652790c --- /dev/null +++ b/src/core/hle/service/am/common_state_getter.h @@ -0,0 +1,77 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "core/hle/service/kernel_helpers.h" +#include "core/hle/service/service.h" + +#include "core/hle/service/am/applet_message_queue.h" + +namespace Service::AM { + +struct Applet; + +class ICommonStateGetter final : public ServiceFramework { +public: + explicit ICommonStateGetter(Core::System& system_, std::shared_ptr applet_); + ~ICommonStateGetter() override; + +private: + // This is nn::oe::FocusState + enum class FocusState : u8 { + InFocus = 1, + NotInFocus = 2, + Background = 3, + }; + + // This is nn::oe::OperationMode + enum class OperationMode : u8 { + Handheld = 0, + Docked = 1, + }; + + // This is nn::am::service::SystemButtonType + enum class SystemButtonType { + None, + HomeButtonShortPressing, + HomeButtonLongPressing, + PowerButtonShortPressing, + PowerButtonLongPressing, + ShutdownSystem, + CaptureButtonShortPressing, + CaptureButtonLongPressing, + }; + + enum class SysPlatformRegion : s32 { + Global = 1, + Terra = 2, + }; + + void GetEventHandle(HLERequestContext& ctx); + void ReceiveMessage(HLERequestContext& ctx); + void GetCurrentFocusState(HLERequestContext& ctx); + void RequestToAcquireSleepLock(HLERequestContext& ctx); + void GetAcquiredSleepLockEvent(HLERequestContext& ctx); + void GetReaderLockAccessorEx(HLERequestContext& ctx); + void GetDefaultDisplayResolutionChangeEvent(HLERequestContext& ctx); + void GetOperationMode(HLERequestContext& ctx); + void GetPerformanceMode(HLERequestContext& ctx); + void GetBootMode(HLERequestContext& ctx); + void IsVrModeEnabled(HLERequestContext& ctx); + void SetVrModeEnabled(HLERequestContext& ctx); + void SetLcdBacklighOffEnabled(HLERequestContext& ctx); + void BeginVrModeEx(HLERequestContext& ctx); + void EndVrModeEx(HLERequestContext& ctx); + void GetDefaultDisplayResolution(HLERequestContext& ctx); + void SetCpuBoostMode(HLERequestContext& ctx); + void GetBuiltInDisplayType(HLERequestContext& ctx); + void PerformSystemButtonPressingIfInFocus(HLERequestContext& ctx); + void GetAppletLaunchedHistory(HLERequestContext& ctx); + void GetSettingsPlatformRegion(HLERequestContext& ctx); + void SetRequestExitToLibraryAppletAtExecuteNextProgramEnabled(HLERequestContext& ctx); + + const std::shared_ptr applet; +}; + +} // namespace Service::AM diff --git a/src/core/hle/service/am/debug_functions.cpp b/src/core/hle/service/am/debug_functions.cpp new file mode 100755 index 000000000..f80b970f2 --- /dev/null +++ b/src/core/hle/service/am/debug_functions.cpp @@ -0,0 +1,44 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/hle/service/am/debug_functions.h" +#include "core/hle/service/ipc_helpers.h" + +namespace Service::AM { + +IDebugFunctions::IDebugFunctions(Core::System& system_) + : ServiceFramework{system_, "IDebugFunctions"} { + // clang-format off + static const FunctionInfo functions[] = { + {0, nullptr, "NotifyMessageToHomeMenuForDebug"}, + {1, nullptr, "OpenMainApplication"}, + {10, nullptr, "PerformSystemButtonPressing"}, + {20, nullptr, "InvalidateTransitionLayer"}, + {30, nullptr, "RequestLaunchApplicationWithUserAndArgumentForDebug"}, + {31, nullptr, "RequestLaunchApplicationByApplicationLaunchInfoForDebug"}, + {40, nullptr, "GetAppletResourceUsageInfo"}, + {50, nullptr, "AddSystemProgramIdAndAppletIdForDebug"}, + {51, nullptr, "AddOperationConfirmedLibraryAppletIdForDebug"}, + {100, nullptr, "SetCpuBoostModeForApplet"}, + {101, nullptr, "CancelCpuBoostModeForApplet"}, + {110, nullptr, "PushToAppletBoundChannelForDebug"}, + {111, nullptr, "TryPopFromAppletBoundChannelForDebug"}, + {120, nullptr, "AlarmSettingNotificationEnableAppEventReserve"}, + {121, nullptr, "AlarmSettingNotificationDisableAppEventReserve"}, + {122, nullptr, "AlarmSettingNotificationPushAppEventNotify"}, + {130, nullptr, "FriendInvitationSetApplicationParameter"}, + {131, nullptr, "FriendInvitationClearApplicationParameter"}, + {132, nullptr, "FriendInvitationPushApplicationParameter"}, + {140, nullptr, "RestrictPowerOperationForSecureLaunchModeForDebug"}, + {200, nullptr, "CreateFloatingLibraryAppletAccepterForDebug"}, + {300, nullptr, "TerminateAllRunningApplicationsForDebug"}, + {900, nullptr, "GetGrcProcessLaunchedSystemEvent"}, + }; + // clang-format on + + RegisterHandlers(functions); +} + +IDebugFunctions::~IDebugFunctions() = default; + +} // namespace Service::AM diff --git a/src/core/hle/service/am/debug_functions.h b/src/core/hle/service/am/debug_functions.h new file mode 100755 index 000000000..d55968743 --- /dev/null +++ b/src/core/hle/service/am/debug_functions.h @@ -0,0 +1,16 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "core/hle/service/service.h" + +namespace Service::AM { + +class IDebugFunctions final : public ServiceFramework { +public: + explicit IDebugFunctions(Core::System& system_); + ~IDebugFunctions() override; +}; + +} // namespace Service::AM diff --git a/src/core/hle/service/am/display_controller.cpp b/src/core/hle/service/am/display_controller.cpp new file mode 100755 index 000000000..3076f1752 --- /dev/null +++ b/src/core/hle/service/am/display_controller.cpp @@ -0,0 +1,139 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/hle/service/am/applet.h" +#include "core/hle/service/am/display_controller.h" +#include "core/hle/service/ipc_helpers.h" + +namespace Service::AM { + +namespace { +struct OutputParameters { + bool was_written; + s32 fbshare_layer_index; +}; + +static_assert(sizeof(OutputParameters) == 8, "OutputParameters has wrong size"); +} // namespace + +IDisplayController::IDisplayController(Core::System& system_, std::shared_ptr applet_) + : ServiceFramework{system_, "IDisplayController"}, applet(std::move(applet_)) { + // clang-format off + static const FunctionInfo functions[] = { + {0, nullptr, "GetLastForegroundCaptureImage"}, + {1, nullptr, "UpdateLastForegroundCaptureImage"}, + {2, nullptr, "GetLastApplicationCaptureImage"}, + {3, nullptr, "GetCallerAppletCaptureImage"}, + {4, nullptr, "UpdateCallerAppletCaptureImage"}, + {5, nullptr, "GetLastForegroundCaptureImageEx"}, + {6, nullptr, "GetLastApplicationCaptureImageEx"}, + {7, &IDisplayController::GetCallerAppletCaptureImageEx, "GetCallerAppletCaptureImageEx"}, + {8, &IDisplayController::TakeScreenShotOfOwnLayer, "TakeScreenShotOfOwnLayer"}, + {9, nullptr, "CopyBetweenCaptureBuffers"}, + {10, nullptr, "AcquireLastApplicationCaptureBuffer"}, + {11, nullptr, "ReleaseLastApplicationCaptureBuffer"}, + {12, nullptr, "AcquireLastForegroundCaptureBuffer"}, + {13, nullptr, "ReleaseLastForegroundCaptureBuffer"}, + {14, nullptr, "AcquireCallerAppletCaptureBuffer"}, + {15, nullptr, "ReleaseCallerAppletCaptureBuffer"}, + {16, nullptr, "AcquireLastApplicationCaptureBufferEx"}, + {17, nullptr, "AcquireLastForegroundCaptureBufferEx"}, + {18, nullptr, "AcquireCallerAppletCaptureBufferEx"}, + {20, nullptr, "ClearCaptureBuffer"}, + {21, nullptr, "ClearAppletTransitionBuffer"}, + {22, &IDisplayController::AcquireLastApplicationCaptureSharedBuffer, "AcquireLastApplicationCaptureSharedBuffer"}, + {23, &IDisplayController::ReleaseLastApplicationCaptureSharedBuffer, "ReleaseLastApplicationCaptureSharedBuffer"}, + {24, &IDisplayController::AcquireLastForegroundCaptureSharedBuffer, "AcquireLastForegroundCaptureSharedBuffer"}, + {25, &IDisplayController::ReleaseLastForegroundCaptureSharedBuffer, "ReleaseLastForegroundCaptureSharedBuffer"}, + {26, &IDisplayController::AcquireCallerAppletCaptureSharedBuffer, "AcquireCallerAppletCaptureSharedBuffer"}, + {27, &IDisplayController::ReleaseCallerAppletCaptureSharedBuffer, "ReleaseCallerAppletCaptureSharedBuffer"}, + {28, nullptr, "TakeScreenShotOfOwnLayerEx"}, + }; + // clang-format on + + RegisterHandlers(functions); +} + +IDisplayController::~IDisplayController() = default; + +void IDisplayController::GetCallerAppletCaptureImageEx(HLERequestContext& ctx) { + LOG_WARNING(Service_AM, "(STUBBED) called"); + + OutputParameters params{}; + const auto res = applet->system_buffer_manager.WriteApplicationCaptureBuffer( + ¶ms.was_written, ¶ms.fbshare_layer_index); + + IPC::ResponseBuilder rb{ctx, 4}; + rb.Push(res); + rb.Push(params.was_written); + rb.Push(params.fbshare_layer_index); +} + +void IDisplayController::TakeScreenShotOfOwnLayer(HLERequestContext& ctx) { + LOG_WARNING(Service_AM, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void IDisplayController::AcquireLastApplicationCaptureSharedBuffer(HLERequestContext& ctx) { + LOG_WARNING(Service_AM, "(STUBBED) called"); + + OutputParameters params{}; + const auto res = applet->system_buffer_manager.WriteApplicationCaptureBuffer( + ¶ms.was_written, ¶ms.fbshare_layer_index); + + IPC::ResponseBuilder rb{ctx, 4}; + rb.Push(res); + rb.Push(params.was_written); + rb.Push(params.fbshare_layer_index); +} + +void IDisplayController::ReleaseLastApplicationCaptureSharedBuffer(HLERequestContext& ctx) { + LOG_WARNING(Service_AM, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void IDisplayController::AcquireLastForegroundCaptureSharedBuffer(HLERequestContext& ctx) { + LOG_WARNING(Service_AM, "(STUBBED) called"); + + OutputParameters params{}; + const auto res = applet->system_buffer_manager.WriteApplicationCaptureBuffer( + ¶ms.was_written, ¶ms.fbshare_layer_index); + + IPC::ResponseBuilder rb{ctx, 4}; + rb.Push(res); + rb.Push(params.was_written); + rb.Push(params.fbshare_layer_index); +} + +void IDisplayController::ReleaseLastForegroundCaptureSharedBuffer(HLERequestContext& ctx) { + LOG_WARNING(Service_AM, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void IDisplayController::AcquireCallerAppletCaptureSharedBuffer(HLERequestContext& ctx) { + LOG_WARNING(Service_AM, "(STUBBED) called"); + + OutputParameters params{}; + const auto res = applet->system_buffer_manager.WriteApplicationCaptureBuffer( + ¶ms.was_written, ¶ms.fbshare_layer_index); + + IPC::ResponseBuilder rb{ctx, 4}; + rb.Push(res); + rb.Push(params.was_written); + rb.Push(params.fbshare_layer_index); +} + +void IDisplayController::ReleaseCallerAppletCaptureSharedBuffer(HLERequestContext& ctx) { + LOG_WARNING(Service_AM, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +} // namespace Service::AM diff --git a/src/core/hle/service/am/display_controller.h b/src/core/hle/service/am/display_controller.h new file mode 100755 index 000000000..75172580c --- /dev/null +++ b/src/core/hle/service/am/display_controller.h @@ -0,0 +1,30 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "core/hle/service/service.h" + +namespace Service::AM { + +struct Applet; + +class IDisplayController final : public ServiceFramework { +public: + explicit IDisplayController(Core::System& system_, std::shared_ptr applet_); + ~IDisplayController() override; + +private: + void GetCallerAppletCaptureImageEx(HLERequestContext& ctx); + void TakeScreenShotOfOwnLayer(HLERequestContext& ctx); + void AcquireLastForegroundCaptureSharedBuffer(HLERequestContext& ctx); + void ReleaseLastForegroundCaptureSharedBuffer(HLERequestContext& ctx); + void AcquireCallerAppletCaptureSharedBuffer(HLERequestContext& ctx); + void ReleaseCallerAppletCaptureSharedBuffer(HLERequestContext& ctx); + void AcquireLastApplicationCaptureSharedBuffer(HLERequestContext& ctx); + void ReleaseLastApplicationCaptureSharedBuffer(HLERequestContext& ctx); + + const std::shared_ptr applet; +}; + +} // namespace Service::AM diff --git a/src/core/hle/service/am/frontend/applet_cabinet.cpp b/src/core/hle/service/am/frontend/applet_cabinet.cpp new file mode 100755 index 000000000..0862c81b6 --- /dev/null +++ b/src/core/hle/service/am/frontend/applet_cabinet.cpp @@ -0,0 +1,184 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "common/assert.h" +#include "common/logging/log.h" +#include "core/core.h" +#include "core/frontend/applets/cabinet.h" +#include "core/hle/kernel/k_event.h" +#include "core/hle/kernel/k_readable_event.h" +#include "core/hle/service/am/am.h" +#include "core/hle/service/am/frontend/applet_cabinet.h" +#include "core/hle/service/am/storage.h" +#include "core/hle/service/mii/mii_manager.h" +#include "core/hle/service/nfc/common/device.h" +#include "hid_core/hid_core.h" + +namespace Service::AM::Frontend { + +Cabinet::Cabinet(Core::System& system_, std::shared_ptr applet_, + LibraryAppletMode applet_mode_, const Core::Frontend::CabinetApplet& frontend_) + : FrontendApplet{system_, applet_, applet_mode_}, frontend{frontend_}, service_context{ + system_, + "CabinetApplet"} { + + availability_change_event = + service_context.CreateEvent("CabinetApplet:AvailabilityChangeEvent"); +} + +Cabinet::~Cabinet() { + service_context.CloseEvent(availability_change_event); +}; + +void Cabinet::Initialize() { + FrontendApplet::Initialize(); + + LOG_INFO(Service_HID, "Initializing Cabinet Applet."); + + LOG_DEBUG(Service_HID, + "Initializing Applet with common_args: arg_version={}, lib_version={}, " + "play_startup_sound={}, size={}, system_tick={}, theme_color={}", + common_args.arguments_version, common_args.library_version, + common_args.play_startup_sound, common_args.size, common_args.system_tick, + common_args.theme_color); + + std::shared_ptr storage = PopInData(); + ASSERT(storage != nullptr); + + const auto applet_input_data = storage->GetData(); + ASSERT(applet_input_data.size() >= sizeof(StartParamForAmiiboSettings)); + + std::memcpy(&applet_input_common, applet_input_data.data(), + sizeof(StartParamForAmiiboSettings)); +} + +Result Cabinet::GetStatus() const { + return ResultSuccess; +} + +void Cabinet::ExecuteInteractive() { + ASSERT_MSG(false, "Attempted to call interactive execution on non-interactive applet."); +} + +void Cabinet::Execute() { + if (is_complete) { + return; + } + + const auto callback = [this](bool apply_changes, const std::string& amiibo_name) { + DisplayCompleted(apply_changes, amiibo_name); + }; + + // TODO: listen on all controllers + if (nfp_device == nullptr) { + nfp_device = std::make_shared( + system.HIDCore().GetFirstNpadId(), system, service_context, availability_change_event); + nfp_device->Initialize(); + nfp_device->StartDetection(Service::NFC::NfcProtocol::All); + } + + const Core::Frontend::CabinetParameters parameters{ + .tag_info = applet_input_common.tag_info, + .register_info = applet_input_common.register_info, + .mode = applet_input_common.applet_mode, + }; + + switch (applet_input_common.applet_mode) { + case Service::NFP::CabinetMode::StartNicknameAndOwnerSettings: + case Service::NFP::CabinetMode::StartGameDataEraser: + case Service::NFP::CabinetMode::StartRestorer: + case Service::NFP::CabinetMode::StartFormatter: + frontend.ShowCabinetApplet(callback, parameters, nfp_device); + break; + default: + UNIMPLEMENTED_MSG("Unknown CabinetMode={}", applet_input_common.applet_mode); + DisplayCompleted(false, {}); + break; + } +} + +void Cabinet::DisplayCompleted(bool apply_changes, std::string_view amiibo_name) { + Service::Mii::MiiManager manager; + ReturnValueForAmiiboSettings applet_output{}; + + if (!apply_changes) { + Cancel(); + } + + if (nfp_device->GetCurrentState() != Service::NFC::DeviceState::TagFound && + nfp_device->GetCurrentState() != Service::NFC::DeviceState::TagMounted) { + Cancel(); + } + + if (nfp_device->GetCurrentState() == Service::NFC::DeviceState::TagFound) { + nfp_device->Mount(Service::NFP::ModelType::Amiibo, Service::NFP::MountTarget::All); + } + + switch (applet_input_common.applet_mode) { + case Service::NFP::CabinetMode::StartNicknameAndOwnerSettings: { + Service::NFP::RegisterInfoPrivate register_info{}; + std::memcpy(register_info.amiibo_name.data(), amiibo_name.data(), + std::min(amiibo_name.size(), register_info.amiibo_name.size() - 1)); + register_info.mii_store_data.BuildRandom(Mii::Age::All, Mii::Gender::All, Mii::Race::All); + register_info.mii_store_data.SetNickname({u'y', u'u', u'z', u'u'}); + nfp_device->SetRegisterInfoPrivate(register_info); + break; + } + case Service::NFP::CabinetMode::StartGameDataEraser: + nfp_device->DeleteApplicationArea(); + break; + case Service::NFP::CabinetMode::StartRestorer: + nfp_device->Restore(); + break; + case Service::NFP::CabinetMode::StartFormatter: + nfp_device->Format(); + break; + default: + UNIMPLEMENTED_MSG("Unknown CabinetMode={}", applet_input_common.applet_mode); + break; + } + + applet_output.device_handle = applet_input_common.device_handle; + applet_output.result = CabinetResult::Cancel; + const auto reg_result = nfp_device->GetRegisterInfo(applet_output.register_info); + const auto tag_result = nfp_device->GetTagInfo(applet_output.tag_info); + nfp_device->Finalize(); + + if (reg_result.IsSuccess()) { + applet_output.result |= CabinetResult::RegisterInfo; + } + + if (tag_result.IsSuccess()) { + applet_output.result |= CabinetResult::TagInfo; + } + + std::vector out_data(sizeof(ReturnValueForAmiiboSettings)); + std::memcpy(out_data.data(), &applet_output, sizeof(ReturnValueForAmiiboSettings)); + + is_complete = true; + + PushOutData(std::make_shared(system, std::move(out_data))); + Exit(); +} + +void Cabinet::Cancel() { + ReturnValueForAmiiboSettings applet_output{}; + applet_output.device_handle = applet_input_common.device_handle; + applet_output.result = CabinetResult::Cancel; + nfp_device->Finalize(); + + std::vector out_data(sizeof(ReturnValueForAmiiboSettings)); + std::memcpy(out_data.data(), &applet_output, sizeof(ReturnValueForAmiiboSettings)); + + is_complete = true; + + PushOutData(std::make_shared(system, std::move(out_data))); + Exit(); +} + +Result Cabinet::RequestExit() { + frontend.Close(); + R_SUCCEED(); +} + +} // namespace Service::AM::Frontend diff --git a/src/core/hle/service/am/frontend/applet_cabinet.h b/src/core/hle/service/am/frontend/applet_cabinet.h new file mode 100755 index 000000000..3a211ed37 --- /dev/null +++ b/src/core/hle/service/am/frontend/applet_cabinet.h @@ -0,0 +1,113 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include + +#include "core/hle/result.h" +#include "core/hle/service/am/frontend/applets.h" +#include "core/hle/service/kernel_helpers.h" +#include "core/hle/service/nfp/nfp_types.h" + +namespace Kernel { +class KEvent; +class KReadableEvent; +} // namespace Kernel + +namespace Core { +class System; +} // namespace Core + +namespace Service::NFC { +class NfcDevice; +} + +namespace Service::AM::Frontend { + +enum class CabinetAppletVersion : u32 { + Version1 = 0x1, +}; + +enum class CabinetFlags : u8 { + None = 0, + DeviceHandle = 1 << 0, + TagInfo = 1 << 1, + RegisterInfo = 1 << 2, + All = DeviceHandle | TagInfo | RegisterInfo, +}; +DECLARE_ENUM_FLAG_OPERATORS(CabinetFlags) + +enum class CabinetResult : u8 { + Cancel = 0, + TagInfo = 1 << 1, + RegisterInfo = 1 << 2, + All = TagInfo | RegisterInfo, +}; +DECLARE_ENUM_FLAG_OPERATORS(CabinetResult) + +// This is nn::nfp::AmiiboSettingsStartParam +struct AmiiboSettingsStartParam { + u64 device_handle; + std::array param_1; + u8 param_2; +}; +static_assert(sizeof(AmiiboSettingsStartParam) == 0x30, + "AmiiboSettingsStartParam is an invalid size"); + +#pragma pack(push, 1) +// This is nn::nfp::StartParamForAmiiboSettings +struct StartParamForAmiiboSettings { + u8 param_1; + Service::NFP::CabinetMode applet_mode; + CabinetFlags flags; + u8 amiibo_settings_1; + u64 device_handle; + Service::NFP::TagInfo tag_info; + Service::NFP::RegisterInfo register_info; + std::array amiibo_settings_3; + INSERT_PADDING_BYTES(0x24); +}; +static_assert(sizeof(StartParamForAmiiboSettings) == 0x1A8, + "StartParamForAmiiboSettings is an invalid size"); + +// This is nn::nfp::ReturnValueForAmiiboSettings +struct ReturnValueForAmiiboSettings { + CabinetResult result; + INSERT_PADDING_BYTES(0x3); + u64 device_handle; + Service::NFP::TagInfo tag_info; + Service::NFP::RegisterInfo register_info; + INSERT_PADDING_BYTES(0x24); +}; +static_assert(sizeof(ReturnValueForAmiiboSettings) == 0x188, + "ReturnValueForAmiiboSettings is an invalid size"); +#pragma pack(pop) + +class Cabinet final : public FrontendApplet { +public: + explicit Cabinet(Core::System& system_, std::shared_ptr applet_, + LibraryAppletMode applet_mode_, + const Core::Frontend::CabinetApplet& frontend_); + ~Cabinet() override; + + void Initialize() override; + + Result GetStatus() const override; + void ExecuteInteractive() override; + void Execute() override; + void DisplayCompleted(bool apply_changes, std::string_view amiibo_name); + void Cancel(); + Result RequestExit() override; + +private: + const Core::Frontend::CabinetApplet& frontend; + + bool is_complete{false}; + std::shared_ptr nfp_device; + Kernel::KEvent* availability_change_event; + KernelHelpers::ServiceContext service_context; + StartParamForAmiiboSettings applet_input_common{}; +}; + +} // namespace Service::AM::Frontend diff --git a/src/core/hle/service/am/frontend/applet_controller.cpp b/src/core/hle/service/am/frontend/applet_controller.cpp new file mode 100755 index 000000000..bd3e49fc4 --- /dev/null +++ b/src/core/hle/service/am/frontend/applet_controller.cpp @@ -0,0 +1,271 @@ +// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include +#include + +#include "common/assert.h" +#include "common/logging/log.h" +#include "common/string_util.h" +#include "core/core.h" +#include "core/frontend/applets/controller.h" +#include "core/hle/result.h" +#include "core/hle/service/am/am.h" +#include "core/hle/service/am/frontend/applet_controller.h" +#include "core/hle/service/am/storage.h" +#include "hid_core/frontend/emulated_controller.h" +#include "hid_core/hid_core.h" +#include "hid_core/hid_types.h" +#include "hid_core/resources/npad/npad.h" + +namespace Service::AM::Frontend { + +[[maybe_unused]] constexpr Result ResultControllerSupportCanceled{ErrorModule::HID, 3101}; +[[maybe_unused]] constexpr Result ResultControllerSupportNotSupportedNpadStyle{ErrorModule::HID, + 3102}; + +static Core::Frontend::ControllerParameters ConvertToFrontendParameters( + ControllerSupportArgPrivate private_arg, ControllerSupportArgHeader header, bool enable_text, + std::vector identification_colors, std::vector text) { + Core::HID::NpadStyleTag npad_style_set; + npad_style_set.raw = private_arg.style_set; + + return { + .min_players = std::max(s8{1}, header.player_count_min), + .max_players = header.player_count_max, + .keep_controllers_connected = header.enable_take_over_connection, + .enable_single_mode = header.enable_single_mode, + .enable_border_color = header.enable_identification_color, + .border_colors = std::move(identification_colors), + .enable_explain_text = enable_text, + .explain_text = std::move(text), + .allow_pro_controller = npad_style_set.fullkey == 1, + .allow_handheld = npad_style_set.handheld == 1, + .allow_dual_joycons = npad_style_set.joycon_dual == 1, + .allow_left_joycon = npad_style_set.joycon_left == 1, + .allow_right_joycon = npad_style_set.joycon_right == 1, + }; +} + +Controller::Controller(Core::System& system_, std::shared_ptr applet_, + LibraryAppletMode applet_mode_, + const Core::Frontend::ControllerApplet& frontend_) + : FrontendApplet{system_, applet_, applet_mode_}, frontend{frontend_} {} + +Controller::~Controller() = default; + +void Controller::Initialize() { + FrontendApplet::Initialize(); + + LOG_INFO(Service_HID, "Initializing Controller Applet."); + + LOG_DEBUG(Service_HID, + "Initializing Applet with common_args: arg_version={}, lib_version={}, " + "play_startup_sound={}, size={}, system_tick={}, theme_color={}", + common_args.arguments_version, common_args.library_version, + common_args.play_startup_sound, common_args.size, common_args.system_tick, + common_args.theme_color); + + controller_applet_version = ControllerAppletVersion{common_args.library_version}; + + const std::shared_ptr private_arg_storage = PopInData(); + ASSERT(private_arg_storage != nullptr); + + const auto& private_arg = private_arg_storage->GetData(); + ASSERT(private_arg.size() == sizeof(ControllerSupportArgPrivate)); + + std::memcpy(&controller_private_arg, private_arg.data(), private_arg.size()); + ASSERT_MSG(controller_private_arg.arg_private_size == sizeof(ControllerSupportArgPrivate), + "Unknown ControllerSupportArgPrivate revision={} with size={}", + controller_applet_version, controller_private_arg.arg_private_size); + + // Some games such as Cave Story+ set invalid values for the ControllerSupportMode. + // Defer to arg_size to set the ControllerSupportMode. + if (controller_private_arg.mode >= ControllerSupportMode::MaxControllerSupportMode) { + switch (controller_private_arg.arg_size) { + case sizeof(ControllerSupportArgOld): + case sizeof(ControllerSupportArgNew): + controller_private_arg.mode = ControllerSupportMode::ShowControllerSupport; + break; + case sizeof(ControllerUpdateFirmwareArg): + controller_private_arg.mode = ControllerSupportMode::ShowControllerFirmwareUpdate; + break; + case sizeof(ControllerKeyRemappingArg): + controller_private_arg.mode = + ControllerSupportMode::ShowControllerKeyRemappingForSystem; + break; + default: + UNIMPLEMENTED_MSG("Unknown ControllerPrivateArg mode={} with arg_size={}", + controller_private_arg.mode, controller_private_arg.arg_size); + controller_private_arg.mode = ControllerSupportMode::ShowControllerSupport; + break; + } + } + + // Some games such as Cave Story+ set invalid values for the ControllerSupportCaller. + // This is always 0 (Application) except with ShowControllerFirmwareUpdateForSystem. + if (controller_private_arg.caller >= ControllerSupportCaller::MaxControllerSupportCaller) { + if (controller_private_arg.flag_1 && + (controller_private_arg.mode == ControllerSupportMode::ShowControllerFirmwareUpdate || + controller_private_arg.mode == + ControllerSupportMode::ShowControllerKeyRemappingForSystem)) { + controller_private_arg.caller = ControllerSupportCaller::System; + } else { + controller_private_arg.caller = ControllerSupportCaller::Application; + } + } + + switch (controller_private_arg.mode) { + case ControllerSupportMode::ShowControllerSupport: + case ControllerSupportMode::ShowControllerStrapGuide: { + const std::shared_ptr user_arg_storage = PopInData(); + ASSERT(user_arg_storage != nullptr); + + const auto& user_arg = user_arg_storage->GetData(); + switch (controller_applet_version) { + case ControllerAppletVersion::Version3: + case ControllerAppletVersion::Version4: + case ControllerAppletVersion::Version5: + ASSERT(user_arg.size() == sizeof(ControllerSupportArgOld)); + std::memcpy(&controller_user_arg_old, user_arg.data(), user_arg.size()); + break; + case ControllerAppletVersion::Version7: + case ControllerAppletVersion::Version8: + ASSERT(user_arg.size() == sizeof(ControllerSupportArgNew)); + std::memcpy(&controller_user_arg_new, user_arg.data(), user_arg.size()); + break; + default: + UNIMPLEMENTED_MSG("Unknown ControllerSupportArg revision={} with size={}", + controller_applet_version, controller_private_arg.arg_size); + ASSERT(user_arg.size() >= sizeof(ControllerSupportArgNew)); + std::memcpy(&controller_user_arg_new, user_arg.data(), sizeof(ControllerSupportArgNew)); + break; + } + break; + } + case ControllerSupportMode::ShowControllerFirmwareUpdate: { + const std::shared_ptr update_arg_storage = PopInData(); + ASSERT(update_arg_storage != nullptr); + + const auto& update_arg = update_arg_storage->GetData(); + ASSERT(update_arg.size() == sizeof(ControllerUpdateFirmwareArg)); + + std::memcpy(&controller_update_arg, update_arg.data(), update_arg.size()); + break; + } + case ControllerSupportMode::ShowControllerKeyRemappingForSystem: { + const std::shared_ptr remapping_arg_storage = PopInData(); + ASSERT(remapping_arg_storage != nullptr); + + const auto& remapping_arg = remapping_arg_storage->GetData(); + ASSERT(remapping_arg.size() == sizeof(ControllerKeyRemappingArg)); + + std::memcpy(&controller_key_remapping_arg, remapping_arg.data(), remapping_arg.size()); + break; + } + default: { + UNIMPLEMENTED_MSG("Unimplemented ControllerSupportMode={}", controller_private_arg.mode); + break; + } + } +} + +Result Controller::GetStatus() const { + return status; +} + +void Controller::ExecuteInteractive() { + ASSERT_MSG(false, "Attempted to call interactive execution on non-interactive applet."); +} + +void Controller::Execute() { + switch (controller_private_arg.mode) { + case ControllerSupportMode::ShowControllerSupport: { + const auto parameters = [this] { + switch (controller_applet_version) { + case ControllerAppletVersion::Version3: + case ControllerAppletVersion::Version4: + case ControllerAppletVersion::Version5: + return ConvertToFrontendParameters( + controller_private_arg, controller_user_arg_old.header, + controller_user_arg_old.enable_explain_text, + std::vector( + controller_user_arg_old.identification_colors.begin(), + controller_user_arg_old.identification_colors.end()), + std::vector(controller_user_arg_old.explain_text.begin(), + controller_user_arg_old.explain_text.end())); + case ControllerAppletVersion::Version7: + case ControllerAppletVersion::Version8: + default: + return ConvertToFrontendParameters( + controller_private_arg, controller_user_arg_new.header, + controller_user_arg_new.enable_explain_text, + std::vector( + controller_user_arg_new.identification_colors.begin(), + controller_user_arg_new.identification_colors.end()), + std::vector(controller_user_arg_new.explain_text.begin(), + controller_user_arg_new.explain_text.end())); + } + }(); + + is_single_mode = parameters.enable_single_mode; + + LOG_DEBUG(Service_HID, + "Controller Parameters: min_players={}, max_players={}, " + "keep_controllers_connected={}, enable_single_mode={}, enable_border_color={}, " + "enable_explain_text={}, allow_pro_controller={}, allow_handheld={}, " + "allow_dual_joycons={}, allow_left_joycon={}, allow_right_joycon={}", + parameters.min_players, parameters.max_players, + parameters.keep_controllers_connected, parameters.enable_single_mode, + parameters.enable_border_color, parameters.enable_explain_text, + parameters.allow_pro_controller, parameters.allow_handheld, + parameters.allow_dual_joycons, parameters.allow_left_joycon, + parameters.allow_right_joycon); + + frontend.ReconfigureControllers( + [this](bool is_success) { ConfigurationComplete(is_success); }, parameters); + break; + } + case ControllerSupportMode::ShowControllerStrapGuide: + case ControllerSupportMode::ShowControllerFirmwareUpdate: + case ControllerSupportMode::ShowControllerKeyRemappingForSystem: + UNIMPLEMENTED_MSG("ControllerSupportMode={} is not implemented", + controller_private_arg.mode); + ConfigurationComplete(true); + break; + default: { + ConfigurationComplete(true); + break; + } + } +} + +void Controller::ConfigurationComplete(bool is_success) { + ControllerSupportResultInfo result_info{}; + + // If enable_single_mode is enabled, player_count is 1 regardless of any other parameters. + // Otherwise, only count connected players from P1-P8. + result_info.player_count = is_single_mode ? 1 : system.HIDCore().GetPlayerCount(); + + result_info.selected_id = static_cast(system.HIDCore().GetFirstNpadId()); + + result_info.result = + is_success ? ControllerSupportResult::Success : ControllerSupportResult::Cancel; + + LOG_DEBUG(Service_HID, "Result Info: player_count={}, selected_id={}, result={}", + result_info.player_count, result_info.selected_id, result_info.result); + + complete = true; + out_data = std::vector(sizeof(ControllerSupportResultInfo)); + std::memcpy(out_data.data(), &result_info, out_data.size()); + + PushOutData(std::make_shared(system, std::move(out_data))); + Exit(); +} + +Result Controller::RequestExit() { + frontend.Close(); + R_SUCCEED(); +} + +} // namespace Service::AM::Frontend diff --git a/src/core/hle/service/am/frontend/applet_controller.h b/src/core/hle/service/am/frontend/applet_controller.h new file mode 100755 index 000000000..2f219429c --- /dev/null +++ b/src/core/hle/service/am/frontend/applet_controller.h @@ -0,0 +1,156 @@ +// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include +#include + +#include "common/common_funcs.h" +#include "common/common_types.h" +#include "core/hle/result.h" +#include "core/hle/service/am/frontend/applets.h" + +namespace Core { +class System; +} + +namespace Core::HID { +enum class NpadStyleSet : u32; +} + +namespace Service::AM::Frontend { + +using IdentificationColor = std::array; +using ExplainText = std::array; + +enum class ControllerAppletVersion : u32_le { + Version3 = 0x3, // 1.0.0 - 2.3.0 + Version4 = 0x4, // 3.0.0 - 5.1.0 + Version5 = 0x5, // 6.0.0 - 7.0.1 + Version7 = 0x7, // 8.0.0 - 10.2.0 + Version8 = 0x8, // 11.0.0+ +}; + +enum class ControllerSupportMode : u8 { + ShowControllerSupport, + ShowControllerStrapGuide, + ShowControllerFirmwareUpdate, + ShowControllerKeyRemappingForSystem, + + MaxControllerSupportMode, +}; + +enum class ControllerSupportCaller : u8 { + Application, + System, + + MaxControllerSupportCaller, +}; + +enum class ControllerSupportResult : u32 { + Success = 0, + Cancel = 2, +}; + +struct ControllerSupportArgPrivate { + u32 arg_private_size{}; + u32 arg_size{}; + bool is_home_menu{}; + bool flag_1{}; + ControllerSupportMode mode{}; + ControllerSupportCaller caller{}; + Core::HID::NpadStyleSet style_set{}; + u32 joy_hold_type{}; +}; +static_assert(sizeof(ControllerSupportArgPrivate) == 0x14, + "ControllerSupportArgPrivate has incorrect size."); + +struct ControllerSupportArgHeader { + s8 player_count_min{}; + s8 player_count_max{}; + bool enable_take_over_connection{}; + bool enable_left_justify{}; + bool enable_permit_joy_dual{}; + bool enable_single_mode{}; + bool enable_identification_color{}; +}; +static_assert(sizeof(ControllerSupportArgHeader) == 0x7, + "ControllerSupportArgHeader has incorrect size."); + +// LibraryAppletVersion 0x3, 0x4, 0x5 +struct ControllerSupportArgOld { + ControllerSupportArgHeader header{}; + std::array identification_colors{}; + bool enable_explain_text{}; + std::array explain_text{}; +}; +static_assert(sizeof(ControllerSupportArgOld) == 0x21C, + "ControllerSupportArgOld has incorrect size."); + +// LibraryAppletVersion 0x7, 0x8 +struct ControllerSupportArgNew { + ControllerSupportArgHeader header{}; + std::array identification_colors{}; + bool enable_explain_text{}; + std::array explain_text{}; +}; +static_assert(sizeof(ControllerSupportArgNew) == 0x430, + "ControllerSupportArgNew has incorrect size."); + +struct ControllerUpdateFirmwareArg { + bool enable_force_update{}; + INSERT_PADDING_BYTES(3); +}; +static_assert(sizeof(ControllerUpdateFirmwareArg) == 0x4, + "ControllerUpdateFirmwareArg has incorrect size."); + +struct ControllerKeyRemappingArg { + u64 unknown{}; + u32 unknown_2{}; + INSERT_PADDING_WORDS(1); +}; +static_assert(sizeof(ControllerKeyRemappingArg) == 0x10, + "ControllerKeyRemappingArg has incorrect size."); + +struct ControllerSupportResultInfo { + s8 player_count{}; + INSERT_PADDING_BYTES(3); + u32 selected_id{}; + ControllerSupportResult result{}; +}; +static_assert(sizeof(ControllerSupportResultInfo) == 0xC, + "ControllerSupportResultInfo has incorrect size."); + +class Controller final : public FrontendApplet { +public: + explicit Controller(Core::System& system_, std::shared_ptr applet_, + LibraryAppletMode applet_mode_, + const Core::Frontend::ControllerApplet& frontend_); + ~Controller() override; + + void Initialize() override; + + Result GetStatus() const override; + void ExecuteInteractive() override; + void Execute() override; + Result RequestExit() override; + + void ConfigurationComplete(bool is_success); + +private: + const Core::Frontend::ControllerApplet& frontend; + + ControllerAppletVersion controller_applet_version; + ControllerSupportArgPrivate controller_private_arg; + ControllerSupportArgOld controller_user_arg_old; + ControllerSupportArgNew controller_user_arg_new; + ControllerUpdateFirmwareArg controller_update_arg; + ControllerKeyRemappingArg controller_key_remapping_arg; + bool complete{false}; + Result status{ResultSuccess}; + bool is_single_mode{false}; + std::vector out_data; +}; + +} // namespace Service::AM::Frontend diff --git a/src/core/hle/service/am/frontend/applet_error.cpp b/src/core/hle/service/am/frontend/applet_error.cpp new file mode 100755 index 000000000..d6db345b6 --- /dev/null +++ b/src/core/hle/service/am/frontend/applet_error.cpp @@ -0,0 +1,219 @@ +// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include +#include +#include "common/assert.h" +#include "common/logging/log.h" +#include "common/string_util.h" +#include "core/core.h" +#include "core/frontend/applets/error.h" +#include "core/hle/service/am/am.h" +#include "core/hle/service/am/frontend/applet_error.h" +#include "core/hle/service/am/storage.h" +#include "core/reporter.h" + +namespace Service::AM::Frontend { + +struct ErrorCode { + u32 error_category{}; + u32 error_number{}; + + static constexpr ErrorCode FromU64(u64 error_code) { + return { + .error_category{static_cast(error_code >> 32)}, + .error_number{static_cast(error_code & 0xFFFFFFFF)}, + }; + } + + static constexpr ErrorCode FromResult(Result result) { + return { + .error_category{2000 + static_cast(result.GetModule())}, + .error_number{result.GetDescription()}, + }; + } + + constexpr Result ToResult() const { + return Result{static_cast(error_category - 2000), error_number}; + } +}; +static_assert(sizeof(ErrorCode) == 0x8, "ErrorCode has incorrect size."); + +#pragma pack(push, 4) +struct ShowError { + u8 mode; + bool jump; + INSERT_PADDING_BYTES_NOINIT(4); + bool use_64bit_error_code; + INSERT_PADDING_BYTES_NOINIT(1); + u64 error_code_64; + u32 error_code_32; +}; +static_assert(sizeof(ShowError) == 0x14, "ShowError has incorrect size."); +#pragma pack(pop) + +struct ShowErrorRecord { + u8 mode; + bool jump; + INSERT_PADDING_BYTES_NOINIT(6); + u64 error_code_64; + u64 posix_time; +}; +static_assert(sizeof(ShowErrorRecord) == 0x18, "ShowErrorRecord has incorrect size."); + +struct SystemErrorArg { + u8 mode; + bool jump; + INSERT_PADDING_BYTES_NOINIT(6); + u64 error_code_64; + std::array language_code; + std::array main_text; + std::array detail_text; +}; +static_assert(sizeof(SystemErrorArg) == 0x1018, "SystemErrorArg has incorrect size."); + +struct ApplicationErrorArg { + u8 mode; + bool jump; + INSERT_PADDING_BYTES_NOINIT(6); + u32 error_code; + std::array language_code; + std::array main_text; + std::array detail_text; +}; +static_assert(sizeof(ApplicationErrorArg) == 0x1014, "ApplicationErrorArg has incorrect size."); + +union Error::ErrorArguments { + ShowError error; + ShowErrorRecord error_record; + SystemErrorArg system_error; + ApplicationErrorArg application_error; + std::array raw{}; +}; + +namespace { +template +void CopyArgumentData(const std::vector& data, T& variable) { + ASSERT(data.size() >= sizeof(T)); + std::memcpy(&variable, data.data(), sizeof(T)); +} + +Result Decode64BitError(u64 error) { + return ErrorCode::FromU64(error).ToResult(); +} + +} // Anonymous namespace + +Error::Error(Core::System& system_, std::shared_ptr applet_, LibraryAppletMode applet_mode_, + const Core::Frontend::ErrorApplet& frontend_) + : FrontendApplet{system_, applet_, applet_mode_}, frontend{frontend_} {} + +Error::~Error() = default; + +void Error::Initialize() { + FrontendApplet::Initialize(); + args = std::make_unique(); + complete = false; + + const std::shared_ptr storage = PopInData(); + ASSERT(storage != nullptr); + const auto data = storage->GetData(); + + ASSERT(!data.empty()); + std::memcpy(&mode, data.data(), sizeof(ErrorAppletMode)); + + switch (mode) { + case ErrorAppletMode::ShowError: + CopyArgumentData(data, args->error); + if (args->error.use_64bit_error_code) { + error_code = Decode64BitError(args->error.error_code_64); + } else { + error_code = Result(args->error.error_code_32); + } + break; + case ErrorAppletMode::ShowSystemError: + CopyArgumentData(data, args->system_error); + error_code = Result(Decode64BitError(args->system_error.error_code_64)); + break; + case ErrorAppletMode::ShowApplicationError: + CopyArgumentData(data, args->application_error); + error_code = Result(args->application_error.error_code); + break; + case ErrorAppletMode::ShowErrorPctl: + CopyArgumentData(data, args->error_record); + error_code = Decode64BitError(args->error_record.error_code_64); + break; + case ErrorAppletMode::ShowErrorRecord: + CopyArgumentData(data, args->error_record); + error_code = Decode64BitError(args->error_record.error_code_64); + break; + default: + UNIMPLEMENTED_MSG("Unimplemented LibAppletError mode={:02X}!", mode); + break; + } +} + +Result Error::GetStatus() const { + return ResultSuccess; +} + +void Error::ExecuteInteractive() { + ASSERT_MSG(false, "Unexpected interactive applet data!"); +} + +void Error::Execute() { + if (complete) { + return; + } + + const auto callback = [this] { DisplayCompleted(); }; + const auto title_id = system.GetApplicationProcessProgramID(); + const auto& reporter{system.GetReporter()}; + + switch (mode) { + case ErrorAppletMode::ShowError: + reporter.SaveErrorReport(title_id, error_code); + frontend.ShowError(error_code, callback); + break; + case ErrorAppletMode::ShowSystemError: + case ErrorAppletMode::ShowApplicationError: { + const auto is_system = mode == ErrorAppletMode::ShowSystemError; + const auto& main_text = + is_system ? args->system_error.main_text : args->application_error.main_text; + const auto& detail_text = + is_system ? args->system_error.detail_text : args->application_error.detail_text; + + const auto main_text_string = + Common::StringFromFixedZeroTerminatedBuffer(main_text.data(), main_text.size()); + const auto detail_text_string = + Common::StringFromFixedZeroTerminatedBuffer(detail_text.data(), detail_text.size()); + + reporter.SaveErrorReport(title_id, error_code, main_text_string, detail_text_string); + frontend.ShowCustomErrorText(error_code, main_text_string, detail_text_string, callback); + break; + } + case ErrorAppletMode::ShowErrorPctl: + case ErrorAppletMode::ShowErrorRecord: + reporter.SaveErrorReport(title_id, error_code, + fmt::format("{:016X}", args->error_record.posix_time)); + frontend.ShowErrorWithTimestamp( + error_code, std::chrono::seconds{args->error_record.posix_time}, callback); + break; + default: + UNIMPLEMENTED_MSG("Unimplemented LibAppletError mode={:02X}!", mode); + DisplayCompleted(); + } +} + +void Error::DisplayCompleted() { + complete = true; + PushOutData(std::make_shared(system, std::vector())); + Exit(); +} + +Result Error::RequestExit() { + frontend.Close(); + R_SUCCEED(); +} + +} // namespace Service::AM::Frontend diff --git a/src/core/hle/service/am/frontend/applet_error.h b/src/core/hle/service/am/frontend/applet_error.h new file mode 100755 index 000000000..678bf33fa --- /dev/null +++ b/src/core/hle/service/am/frontend/applet_error.h @@ -0,0 +1,51 @@ +// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "core/hle/result.h" +#include "core/hle/service/am/frontend/applets.h" + +namespace Core { +class System; +} + +namespace Service::AM::Frontend { + +enum class ErrorAppletMode : u8 { + ShowError = 0, + ShowSystemError = 1, + ShowApplicationError = 2, + ShowEula = 3, + ShowErrorPctl = 4, + ShowErrorRecord = 5, + ShowUpdateEula = 8, +}; + +class Error final : public FrontendApplet { +public: + explicit Error(Core::System& system_, std::shared_ptr applet_, + LibraryAppletMode applet_mode_, const Core::Frontend::ErrorApplet& frontend_); + ~Error() override; + + void Initialize() override; + + Result GetStatus() const override; + void ExecuteInteractive() override; + void Execute() override; + Result RequestExit() override; + + void DisplayCompleted(); + +private: + union ErrorArguments; + + const Core::Frontend::ErrorApplet& frontend; + Result error_code = ResultSuccess; + ErrorAppletMode mode = ErrorAppletMode::ShowError; + std::unique_ptr args; + + bool complete = false; +}; + +} // namespace Service::AM::Frontend diff --git a/src/core/hle/service/am/frontend/applet_general.cpp b/src/core/hle/service/am/frontend/applet_general.cpp new file mode 100755 index 000000000..3c091a602 --- /dev/null +++ b/src/core/hle/service/am/frontend/applet_general.cpp @@ -0,0 +1,250 @@ +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "common/assert.h" +#include "common/hex_util.h" +#include "common/logging/log.h" +#include "core/core.h" +#include "core/frontend/applets/general.h" +#include "core/hle/result.h" +#include "core/hle/service/am/am.h" +#include "core/hle/service/am/applet_data_broker.h" +#include "core/hle/service/am/frontend/applet_general.h" +#include "core/hle/service/am/storage.h" +#include "core/reporter.h" + +namespace Service::AM::Frontend { + +constexpr Result ERROR_INVALID_PIN{ErrorModule::PCTL, 221}; + +static void LogCurrentStorage(std::shared_ptr applet, std::string_view prefix) { + std::shared_ptr storage; + while (R_SUCCEEDED(applet->caller_applet_broker->GetInData().Pop(&storage))) { + const auto data = storage->GetData(); + LOG_INFO(Service_AM, + "called (STUBBED), during {} received normal data with size={:08X}, data={}", + prefix, data.size(), Common::HexToString(data)); + } + + while (R_SUCCEEDED(applet->caller_applet_broker->GetInteractiveInData().Pop(&storage))) { + const auto data = storage->GetData(); + LOG_INFO(Service_AM, + "called (STUBBED), during {} received interactive data with size={:08X}, data={}", + prefix, data.size(), Common::HexToString(data)); + } +} + +Auth::Auth(Core::System& system_, std::shared_ptr applet_, LibraryAppletMode applet_mode_, + Core::Frontend::ParentalControlsApplet& frontend_) + : FrontendApplet{system_, applet_, applet_mode_}, frontend{frontend_} {} + +Auth::~Auth() = default; + +void Auth::Initialize() { + FrontendApplet::Initialize(); + complete = false; + + const std::shared_ptr storage = PopInData(); + ASSERT(storage != nullptr); + const auto data = storage->GetData(); + ASSERT(data.size() >= 0xC); + + struct Arg { + INSERT_PADDING_BYTES(4); + AuthAppletType type; + u8 arg0; + u8 arg1; + u8 arg2; + INSERT_PADDING_BYTES(1); + }; + static_assert(sizeof(Arg) == 0xC, "Arg (AuthApplet) has incorrect size."); + + Arg arg{}; + std::memcpy(&arg, data.data(), sizeof(Arg)); + + type = arg.type; + arg0 = arg.arg0; + arg1 = arg.arg1; + arg2 = arg.arg2; +} + +Result Auth::GetStatus() const { + return successful ? ResultSuccess : ERROR_INVALID_PIN; +} + +void Auth::ExecuteInteractive() { + ASSERT_MSG(false, "Unexpected interactive applet data."); +} + +void Auth::Execute() { + if (complete) { + return; + } + + const auto unimplemented_log = [this] { + UNIMPLEMENTED_MSG("Unimplemented Auth applet type for type={:08X}, arg0={:02X}, " + "arg1={:02X}, arg2={:02X}", + type, arg0, arg1, arg2); + }; + + switch (type) { + case AuthAppletType::ShowParentalAuthentication: { + const auto callback = [this](bool is_successful) { AuthFinished(is_successful); }; + + if (arg0 == 1 && arg1 == 0 && arg2 == 1) { + // ShowAuthenticatorForConfiguration + frontend.VerifyPINForSettings(callback); + } else if (arg1 == 0 && arg2 == 0) { + // ShowParentalAuthentication(bool) + frontend.VerifyPIN(callback, static_cast(arg0)); + } else { + unimplemented_log(); + } + break; + } + case AuthAppletType::RegisterParentalPasscode: { + const auto callback = [this] { AuthFinished(true); }; + + if (arg0 == 0 && arg1 == 0 && arg2 == 0) { + // RegisterParentalPasscode + frontend.RegisterPIN(callback); + } else { + unimplemented_log(); + } + break; + } + case AuthAppletType::ChangeParentalPasscode: { + const auto callback = [this] { AuthFinished(true); }; + + if (arg0 == 0 && arg1 == 0 && arg2 == 0) { + // ChangeParentalPasscode + frontend.ChangePIN(callback); + } else { + unimplemented_log(); + } + break; + } + default: + unimplemented_log(); + break; + } +} + +void Auth::AuthFinished(bool is_successful) { + successful = is_successful; + + struct Return { + Result result_code; + }; + static_assert(sizeof(Return) == 0x4, "Return (AuthApplet) has incorrect size."); + + Return return_{GetStatus()}; + + std::vector out(sizeof(Return)); + std::memcpy(out.data(), &return_, sizeof(Return)); + + PushOutData(std::make_shared(system, std::move(out))); + Exit(); +} + +Result Auth::RequestExit() { + frontend.Close(); + R_SUCCEED(); +} + +PhotoViewer::PhotoViewer(Core::System& system_, std::shared_ptr applet_, + LibraryAppletMode applet_mode_, + const Core::Frontend::PhotoViewerApplet& frontend_) + : FrontendApplet{system_, applet_, applet_mode_}, frontend{frontend_} {} + +PhotoViewer::~PhotoViewer() = default; + +void PhotoViewer::Initialize() { + FrontendApplet::Initialize(); + complete = false; + + const std::shared_ptr storage = PopInData(); + ASSERT(storage != nullptr); + const auto data = storage->GetData(); + ASSERT(!data.empty()); + mode = static_cast(data[0]); +} + +Result PhotoViewer::GetStatus() const { + return ResultSuccess; +} + +void PhotoViewer::ExecuteInteractive() { + ASSERT_MSG(false, "Unexpected interactive applet data."); +} + +void PhotoViewer::Execute() { + if (complete) + return; + + const auto callback = [this] { ViewFinished(); }; + switch (mode) { + case PhotoViewerAppletMode::CurrentApp: + frontend.ShowPhotosForApplication(system.GetApplicationProcessProgramID(), callback); + break; + case PhotoViewerAppletMode::AllApps: + frontend.ShowAllPhotos(callback); + break; + default: + UNIMPLEMENTED_MSG("Unimplemented PhotoViewer applet mode={:02X}!", mode); + break; + } +} + +void PhotoViewer::ViewFinished() { + PushOutData(std::make_shared(system, std::vector{})); + Exit(); +} + +Result PhotoViewer::RequestExit() { + frontend.Close(); + R_SUCCEED(); +} + +StubApplet::StubApplet(Core::System& system_, std::shared_ptr applet_, AppletId id_, + LibraryAppletMode applet_mode_) + : FrontendApplet{system_, applet_, applet_mode_}, id{id_} {} + +StubApplet::~StubApplet() = default; + +void StubApplet::Initialize() { + LOG_WARNING(Service_AM, "called (STUBBED)"); + FrontendApplet::Initialize(); + + LogCurrentStorage(applet.lock(), "Initialize"); +} + +Result StubApplet::GetStatus() const { + LOG_WARNING(Service_AM, "called (STUBBED)"); + return ResultSuccess; +} + +void StubApplet::ExecuteInteractive() { + LOG_WARNING(Service_AM, "called (STUBBED)"); + LogCurrentStorage(applet.lock(), "ExecuteInteractive"); + + PushOutData(std::make_shared(system, std::vector(0x1000))); + PushInteractiveOutData(std::make_shared(system, std::vector(0x1000))); + Exit(); +} + +void StubApplet::Execute() { + LOG_WARNING(Service_AM, "called (STUBBED)"); + LogCurrentStorage(applet.lock(), "Execute"); + + PushOutData(std::make_shared(system, std::vector(0x1000))); + PushInteractiveOutData(std::make_shared(system, std::vector(0x1000))); + Exit(); +} + +Result StubApplet::RequestExit() { + // Nothing to do. + R_SUCCEED(); +} + +} // namespace Service::AM::Frontend diff --git a/src/core/hle/service/am/frontend/applet_general.h b/src/core/hle/service/am/frontend/applet_general.h new file mode 100755 index 000000000..eaa7ae25f --- /dev/null +++ b/src/core/hle/service/am/frontend/applet_general.h @@ -0,0 +1,89 @@ +// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "core/hle/service/am/frontend/applets.h" + +namespace Core { +class System; +} + +namespace Service::AM::Frontend { + +enum class AuthAppletType : u32 { + ShowParentalAuthentication, + RegisterParentalPasscode, + ChangeParentalPasscode, +}; + +class Auth final : public FrontendApplet { +public: + explicit Auth(Core::System& system_, std::shared_ptr applet_, + LibraryAppletMode applet_mode_, + Core::Frontend::ParentalControlsApplet& frontend_); + ~Auth() override; + + void Initialize() override; + Result GetStatus() const override; + void ExecuteInteractive() override; + void Execute() override; + Result RequestExit() override; + + void AuthFinished(bool is_successful = true); + +private: + Core::Frontend::ParentalControlsApplet& frontend; + bool complete = false; + bool successful = false; + + AuthAppletType type = AuthAppletType::ShowParentalAuthentication; + u8 arg0 = 0; + u8 arg1 = 0; + u8 arg2 = 0; +}; + +enum class PhotoViewerAppletMode : u8 { + CurrentApp = 0, + AllApps = 1, +}; + +class PhotoViewer final : public FrontendApplet { +public: + explicit PhotoViewer(Core::System& system_, std::shared_ptr applet_, + LibraryAppletMode applet_mode_, + const Core::Frontend::PhotoViewerApplet& frontend_); + ~PhotoViewer() override; + + void Initialize() override; + Result GetStatus() const override; + void ExecuteInteractive() override; + void Execute() override; + Result RequestExit() override; + + void ViewFinished(); + +private: + const Core::Frontend::PhotoViewerApplet& frontend; + bool complete = false; + PhotoViewerAppletMode mode = PhotoViewerAppletMode::CurrentApp; +}; + +class StubApplet final : public FrontendApplet { +public: + explicit StubApplet(Core::System& system_, std::shared_ptr applet_, AppletId id_, + LibraryAppletMode applet_mode_); + ~StubApplet() override; + + void Initialize() override; + + Result GetStatus() const override; + void ExecuteInteractive() override; + void Execute() override; + Result RequestExit() override; + +private: + AppletId id; +}; + +} // namespace Service::AM::Frontend diff --git a/src/core/hle/service/am/frontend/applet_mii_edit.cpp b/src/core/hle/service/am/frontend/applet_mii_edit.cpp new file mode 100755 index 000000000..fa41bdb9c --- /dev/null +++ b/src/core/hle/service/am/frontend/applet_mii_edit.cpp @@ -0,0 +1,177 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "common/assert.h" +#include "common/logging/log.h" +#include "core/core.h" +#include "core/frontend/applets/mii_edit.h" +#include "core/hle/service/am/am.h" +#include "core/hle/service/am/frontend/applet_mii_edit.h" +#include "core/hle/service/am/storage.h" +#include "core/hle/service/mii/mii.h" +#include "core/hle/service/mii/mii_manager.h" +#include "core/hle/service/sm/sm.h" + +namespace Service::AM::Frontend { + +MiiEdit::MiiEdit(Core::System& system_, std::shared_ptr applet_, + LibraryAppletMode applet_mode_, const Core::Frontend::MiiEditApplet& frontend_) + : FrontendApplet{system_, applet_, applet_mode_}, frontend{frontend_} {} + +MiiEdit::~MiiEdit() = default; + +void MiiEdit::Initialize() { + // Note: MiiEdit is not initialized with common arguments. + // Instead, it is initialized by an AppletInput storage with size 0x100 bytes. + // Do NOT call Applet::Initialize() here. + + const std::shared_ptr storage = PopInData(); + ASSERT(storage != nullptr); + + const auto applet_input_data = storage->GetData(); + ASSERT(applet_input_data.size() >= sizeof(MiiEditAppletInputCommon)); + + std::memcpy(&applet_input_common, applet_input_data.data(), sizeof(MiiEditAppletInputCommon)); + + LOG_INFO(Service_AM, + "Initializing MiiEdit Applet with MiiEditAppletVersion={} and MiiEditAppletMode={}", + applet_input_common.version, applet_input_common.applet_mode); + + switch (applet_input_common.version) { + case MiiEditAppletVersion::Version3: + ASSERT(applet_input_data.size() == + sizeof(MiiEditAppletInputCommon) + sizeof(MiiEditAppletInputV3)); + std::memcpy(&applet_input_v3, applet_input_data.data() + sizeof(MiiEditAppletInputCommon), + sizeof(MiiEditAppletInputV3)); + break; + case MiiEditAppletVersion::Version4: + ASSERT(applet_input_data.size() == + sizeof(MiiEditAppletInputCommon) + sizeof(MiiEditAppletInputV4)); + std::memcpy(&applet_input_v4, applet_input_data.data() + sizeof(MiiEditAppletInputCommon), + sizeof(MiiEditAppletInputV4)); + break; + default: + UNIMPLEMENTED_MSG("Unknown MiiEditAppletVersion={} with size={}", + applet_input_common.version, applet_input_data.size()); + ASSERT(applet_input_data.size() >= + sizeof(MiiEditAppletInputCommon) + sizeof(MiiEditAppletInputV4)); + std::memcpy(&applet_input_v4, applet_input_data.data() + sizeof(MiiEditAppletInputCommon), + sizeof(MiiEditAppletInputV4)); + break; + } + + manager = system.ServiceManager().GetService("mii:e")->GetMiiManager(); + if (manager == nullptr) { + manager = std::make_shared(); + } + manager->Initialize(metadata); +} + +Result MiiEdit::GetStatus() const { + return ResultSuccess; +} + +void MiiEdit::ExecuteInteractive() { + ASSERT_MSG(false, "Attempted to call interactive execution on non-interactive applet."); +} + +void MiiEdit::Execute() { + if (is_complete) { + return; + } + + // This is a default stub for each of the MiiEdit applet modes. + switch (applet_input_common.applet_mode) { + case MiiEditAppletMode::ShowMiiEdit: + case MiiEditAppletMode::AppendMiiImage: + case MiiEditAppletMode::UpdateMiiImage: + MiiEditOutput(MiiEditResult::Success, 0); + break; + case MiiEditAppletMode::AppendMii: { + Mii::StoreData store_data{}; + store_data.BuildRandom(Mii::Age::All, Mii::Gender::All, Mii::Race::All); + store_data.SetNickname({u'y', u'u', u'z', u'u'}); + store_data.SetChecksum(); + const auto result = manager->AddOrReplace(metadata, store_data); + + if (result.IsError()) { + MiiEditOutput(MiiEditResult::Cancel, 0); + break; + } + + s32 index = manager->FindIndex(store_data.GetCreateId(), false); + + if (index == -1) { + MiiEditOutput(MiiEditResult::Cancel, 0); + break; + } + + MiiEditOutput(MiiEditResult::Success, index); + break; + } + case MiiEditAppletMode::CreateMii: { + Mii::CharInfo char_info{}; + manager->BuildRandom(char_info, Mii::Age::All, Mii::Gender::All, Mii::Race::All); + + const MiiEditCharInfo edit_char_info{ + .mii_info{char_info}, + }; + + MiiEditOutputForCharInfoEditing(MiiEditResult::Success, edit_char_info); + break; + } + case MiiEditAppletMode::EditMii: { + const MiiEditCharInfo edit_char_info{ + .mii_info{applet_input_v4.char_info.mii_info}, + }; + + MiiEditOutputForCharInfoEditing(MiiEditResult::Success, edit_char_info); + break; + } + default: + UNIMPLEMENTED_MSG("Unknown MiiEditAppletMode={}", applet_input_common.applet_mode); + + MiiEditOutput(MiiEditResult::Success, 0); + break; + } +} + +void MiiEdit::MiiEditOutput(MiiEditResult result, s32 index) { + const MiiEditAppletOutput applet_output{ + .result{result}, + .index{index}, + }; + + LOG_INFO(Input, "called, result={}, index={}", result, index); + + std::vector out_data(sizeof(MiiEditAppletOutput)); + std::memcpy(out_data.data(), &applet_output, sizeof(MiiEditAppletOutput)); + + is_complete = true; + + PushOutData(std::make_shared(system, std::move(out_data))); + Exit(); +} + +void MiiEdit::MiiEditOutputForCharInfoEditing(MiiEditResult result, + const MiiEditCharInfo& char_info) { + const MiiEditAppletOutputForCharInfoEditing applet_output{ + .result{result}, + .char_info{char_info}, + }; + + std::vector out_data(sizeof(MiiEditAppletOutputForCharInfoEditing)); + std::memcpy(out_data.data(), &applet_output, sizeof(MiiEditAppletOutputForCharInfoEditing)); + + is_complete = true; + + PushOutData(std::make_shared(system, std::move(out_data))); + Exit(); +} + +Result MiiEdit::RequestExit() { + frontend.Close(); + R_SUCCEED(); +} + +} // namespace Service::AM::Frontend diff --git a/src/core/hle/service/am/frontend/applet_mii_edit.h b/src/core/hle/service/am/frontend/applet_mii_edit.h new file mode 100755 index 000000000..5db792f7d --- /dev/null +++ b/src/core/hle/service/am/frontend/applet_mii_edit.h @@ -0,0 +1,51 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "core/hle/result.h" +#include "core/hle/service/am/frontend/applet_mii_edit_types.h" +#include "core/hle/service/am/frontend/applets.h" + +namespace Core { +class System; +} // namespace Core + +namespace Service::Mii { +struct DatabaseSessionMetadata; +class MiiManager; +} // namespace Service::Mii + +namespace Service::AM::Frontend { + +class MiiEdit final : public FrontendApplet { +public: + explicit MiiEdit(Core::System& system_, std::shared_ptr applet_, + LibraryAppletMode applet_mode_, + const Core::Frontend::MiiEditApplet& frontend_); + ~MiiEdit() override; + + void Initialize() override; + + Result GetStatus() const override; + void ExecuteInteractive() override; + void Execute() override; + Result RequestExit() override; + + void MiiEditOutput(MiiEditResult result, s32 index); + + void MiiEditOutputForCharInfoEditing(MiiEditResult result, const MiiEditCharInfo& char_info); + +private: + const Core::Frontend::MiiEditApplet& frontend; + + MiiEditAppletInputCommon applet_input_common{}; + MiiEditAppletInputV3 applet_input_v3{}; + MiiEditAppletInputV4 applet_input_v4{}; + + bool is_complete{false}; + std::shared_ptr manager = nullptr; + Mii::DatabaseSessionMetadata metadata{}; +}; + +} // namespace Service::AM::Frontend diff --git a/src/core/hle/service/am/frontend/applet_mii_edit_types.h b/src/core/hle/service/am/frontend/applet_mii_edit_types.h new file mode 100755 index 000000000..23d9d7a69 --- /dev/null +++ b/src/core/hle/service/am/frontend/applet_mii_edit_types.h @@ -0,0 +1,83 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include + +#include "common/common_funcs.h" +#include "common/common_types.h" +#include "common/uuid.h" +#include "core/hle/service/mii/types/char_info.h" + +namespace Service::AM::Frontend { + +enum class MiiEditAppletVersion : s32 { + Version3 = 0x3, // 1.0.0 - 10.1.1 + Version4 = 0x4, // 10.2.0+ +}; + +// This is nn::mii::AppletMode +enum class MiiEditAppletMode : u32 { + ShowMiiEdit = 0, + AppendMii = 1, + AppendMiiImage = 2, + UpdateMiiImage = 3, + CreateMii = 4, + EditMii = 5, +}; + +enum class MiiEditResult : u32 { + Success, + Cancel, +}; + +struct MiiEditCharInfo { + Service::Mii::CharInfo mii_info{}; +}; +static_assert(sizeof(MiiEditCharInfo) == 0x58, "MiiEditCharInfo has incorrect size."); + +struct MiiEditAppletInputCommon { + MiiEditAppletVersion version{}; + MiiEditAppletMode applet_mode{}; +}; +static_assert(sizeof(MiiEditAppletInputCommon) == 0x8, + "MiiEditAppletInputCommon has incorrect size."); + +struct MiiEditAppletInputV3 { + u32 special_mii_key_code{}; + std::array valid_uuids{}; + Common::UUID used_uuid{}; + INSERT_PADDING_BYTES(0x64); +}; +static_assert(sizeof(MiiEditAppletInputV3) == 0x100 - sizeof(MiiEditAppletInputCommon), + "MiiEditAppletInputV3 has incorrect size."); + +struct MiiEditAppletInputV4 { + u32 special_mii_key_code{}; + MiiEditCharInfo char_info{}; + INSERT_PADDING_BYTES(0x28); + Common::UUID used_uuid{}; + INSERT_PADDING_BYTES(0x64); +}; +static_assert(sizeof(MiiEditAppletInputV4) == 0x100 - sizeof(MiiEditAppletInputCommon), + "MiiEditAppletInputV4 has incorrect size."); + +// This is nn::mii::AppletOutput +struct MiiEditAppletOutput { + MiiEditResult result{}; + s32 index{}; + INSERT_PADDING_BYTES(0x18); +}; +static_assert(sizeof(MiiEditAppletOutput) == 0x20, "MiiEditAppletOutput has incorrect size."); + +// This is nn::mii::AppletOutputForCharInfoEditing +struct MiiEditAppletOutputForCharInfoEditing { + MiiEditResult result{}; + MiiEditCharInfo char_info{}; + INSERT_PADDING_BYTES(0x24); +}; +static_assert(sizeof(MiiEditAppletOutputForCharInfoEditing) == 0x80, + "MiiEditAppletOutputForCharInfoEditing has incorrect size."); + +} // namespace Service::AM::Frontend diff --git a/src/core/hle/service/am/frontend/applet_profile_select.cpp b/src/core/hle/service/am/frontend/applet_profile_select.cpp new file mode 100755 index 000000000..efb4053b8 --- /dev/null +++ b/src/core/hle/service/am/frontend/applet_profile_select.cpp @@ -0,0 +1,123 @@ +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include + +#include "common/assert.h" +#include "common/string_util.h" +#include "core/core.h" +#include "core/frontend/applets/profile_select.h" +#include "core/hle/service/acc/errors.h" +#include "core/hle/service/am/am.h" +#include "core/hle/service/am/frontend/applet_profile_select.h" +#include "core/hle/service/am/storage.h" + +namespace Service::AM::Frontend { + +ProfileSelect::ProfileSelect(Core::System& system_, std::shared_ptr applet_, + LibraryAppletMode applet_mode_, + const Core::Frontend::ProfileSelectApplet& frontend_) + : FrontendApplet{system_, applet_, applet_mode_}, frontend{frontend_} {} + +ProfileSelect::~ProfileSelect() = default; + +void ProfileSelect::Initialize() { + complete = false; + status = ResultSuccess; + final_data.clear(); + + FrontendApplet::Initialize(); + profile_select_version = ProfileSelectAppletVersion{common_args.library_version}; + + const std::shared_ptr user_config_storage = PopInData(); + ASSERT(user_config_storage != nullptr); + const auto& user_config = user_config_storage->GetData(); + + LOG_INFO(Service_AM, "Initializing Profile Select Applet with version={}", + profile_select_version); + + switch (profile_select_version) { + case ProfileSelectAppletVersion::Version1: + ASSERT(user_config.size() == sizeof(UiSettingsV1)); + std::memcpy(&config_old, user_config.data(), sizeof(UiSettingsV1)); + break; + case ProfileSelectAppletVersion::Version2: + case ProfileSelectAppletVersion::Version3: + ASSERT(user_config.size() == sizeof(UiSettings)); + std::memcpy(&config, user_config.data(), sizeof(UiSettings)); + break; + default: + UNIMPLEMENTED_MSG("Unknown profile_select_version = {}", profile_select_version); + break; + } +} + +Result ProfileSelect::GetStatus() const { + return status; +} + +void ProfileSelect::ExecuteInteractive() { + ASSERT_MSG(false, "Attempted to call interactive execution on non-interactive applet."); +} + +void ProfileSelect::Execute() { + if (complete) { + PushOutData(std::make_shared(system, std::move(final_data))); + Exit(); + return; + } + + Core::Frontend::ProfileSelectParameters parameters{}; + + switch (profile_select_version) { + case ProfileSelectAppletVersion::Version1: + parameters = { + .mode = config_old.mode, + .invalid_uid_list = config_old.invalid_uid_list, + .display_options = config_old.display_options, + .purpose = UserSelectionPurpose::General, + }; + break; + case ProfileSelectAppletVersion::Version2: + case ProfileSelectAppletVersion::Version3: + parameters = { + .mode = config.mode, + .invalid_uid_list = config.invalid_uid_list, + .display_options = config.display_options, + .purpose = config.purpose, + }; + break; + default: + UNIMPLEMENTED_MSG("Unknown profile_select_version = {}", profile_select_version); + break; + } + + frontend.SelectProfile([this](std::optional uuid) { SelectionComplete(uuid); }, + parameters); +} + +void ProfileSelect::SelectionComplete(std::optional uuid) { + UiReturnArg output{}; + + if (uuid.has_value() && uuid->IsValid()) { + output.result = 0; + output.uuid_selected = *uuid; + } else { + status = Account::ResultCancelledByUser; + output.result = Account::ResultCancelledByUser.raw; + output.uuid_selected = Common::InvalidUUID; + } + + final_data = std::vector(sizeof(UiReturnArg)); + std::memcpy(final_data.data(), &output, final_data.size()); + + PushOutData(std::make_shared(system, std::move(final_data))); + Exit(); +} + +Result ProfileSelect::RequestExit() { + frontend.Close(); + R_SUCCEED(); +} + +} // namespace Service::AM::Frontend diff --git a/src/core/hle/service/am/frontend/applet_profile_select.h b/src/core/hle/service/am/frontend/applet_profile_select.h new file mode 100755 index 000000000..674e7afe1 --- /dev/null +++ b/src/core/hle/service/am/frontend/applet_profile_select.h @@ -0,0 +1,142 @@ +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include + +#include "common/common_funcs.h" +#include "common/uuid.h" +#include "core/hle/result.h" +#include "core/hle/service/am/frontend/applets.h" + +namespace Core { +class System; +} + +namespace Service::AM::Frontend { + +enum class ProfileSelectAppletVersion : u32 { + Version1 = 0x1, // 1.0.0+ + Version2 = 0x10000, // 2.0.0+ + Version3 = 0x20000, // 6.0.0+ +}; + +// This is nn::account::UiMode +enum class UiMode { + UserSelector, + UserCreator, + EnsureNetworkServiceAccountAvailable, + UserIconEditor, + UserNicknameEditor, + UserCreatorForStarter, + NintendoAccountAuthorizationRequestContext, + IntroduceExternalNetworkServiceAccount, + IntroduceExternalNetworkServiceAccountForRegistration, + NintendoAccountNnidLinker, + LicenseRequirementsForNetworkService, + LicenseRequirementsForNetworkServiceWithUserContextImpl, + UserCreatorForImmediateNaLoginTest, + UserQualificationPromoter, +}; + +// This is nn::account::UserSelectionPurpose +enum class UserSelectionPurpose { + General, + GameCardRegistration, + EShopLaunch, + EShopItemShow, + PicturePost, + NintendoAccountLinkage, + SettingsUpdate, + SaveDataDeletion, + UserMigration, + SaveDataTransfer, +}; + +// This is nn::account::NintendoAccountStartupDialogType +enum class NintendoAccountStartupDialogType { + LoginAndCreate, + Login, + Create, +}; + +// This is nn::account::UserSelectionSettingsForSystemService +struct UserSelectionSettingsForSystemService { + UserSelectionPurpose purpose; + bool enable_user_creation; + INSERT_PADDING_BYTES(0x3); +}; +static_assert(sizeof(UserSelectionSettingsForSystemService) == 0x8, + "UserSelectionSettingsForSystemService has incorrect size."); + +struct UiSettingsDisplayOptions { + bool is_network_service_account_required; + bool is_skip_enabled; + bool is_system_or_launcher; + bool is_registration_permitted; + bool show_skip_button; + bool additional_select; + bool show_user_selector; + bool is_unqualified_user_selectable; +}; +static_assert(sizeof(UiSettingsDisplayOptions) == 0x8, + "UiSettingsDisplayOptions has incorrect size."); + +struct UiSettingsV1 { + UiMode mode; + INSERT_PADDING_BYTES(0x4); + std::array invalid_uid_list; + u64 application_id; + UiSettingsDisplayOptions display_options; +}; +static_assert(sizeof(UiSettingsV1) == 0x98, "UiSettings has incorrect size."); + +// This is nn::account::UiSettings +struct UiSettings { + UiMode mode; + INSERT_PADDING_BYTES(0x4); + std::array invalid_uid_list; + u64 application_id; + UiSettingsDisplayOptions display_options; + UserSelectionPurpose purpose; + INSERT_PADDING_BYTES(0x4); +}; +static_assert(sizeof(UiSettings) == 0xA0, "UiSettings has incorrect size."); + +// This is nn::account::UiReturnArg +struct UiReturnArg { + u64 result; + Common::UUID uuid_selected; +}; +static_assert(sizeof(UiReturnArg) == 0x18, "UiReturnArg has incorrect size."); + +class ProfileSelect final : public FrontendApplet { +public: + explicit ProfileSelect(Core::System& system_, std::shared_ptr applet_, + LibraryAppletMode applet_mode_, + const Core::Frontend::ProfileSelectApplet& frontend_); + ~ProfileSelect() override; + + void Initialize() override; + + Result GetStatus() const override; + void ExecuteInteractive() override; + void Execute() override; + Result RequestExit() override; + + void SelectionComplete(std::optional uuid); + +private: + const Core::Frontend::ProfileSelectApplet& frontend; + + UiSettings config; + UiSettingsV1 config_old; + ProfileSelectAppletVersion profile_select_version; + + bool complete = false; + Result status = ResultSuccess; + std::vector final_data; +}; + +} // namespace Service::AM::Frontend diff --git a/src/core/hle/service/am/frontend/applet_software_keyboard.cpp b/src/core/hle/service/am/frontend/applet_software_keyboard.cpp new file mode 100755 index 000000000..fbf75d379 --- /dev/null +++ b/src/core/hle/service/am/frontend/applet_software_keyboard.cpp @@ -0,0 +1,1274 @@ +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "common/string_util.h" +#include "core/core.h" +#include "core/frontend/applets/software_keyboard.h" +#include "core/hle/service/am/am.h" +#include "core/hle/service/am/frontend/applet_software_keyboard.h" +#include "core/hle/service/am/storage.h" + +namespace Service::AM::Frontend { + +namespace { + +// The maximum number of UTF-16 characters that can be input into the swkbd text field. +constexpr u32 DEFAULT_MAX_TEXT_LENGTH = 500; + +constexpr std::size_t REPLY_BASE_SIZE = sizeof(SwkbdState) + sizeof(SwkbdReplyType); +constexpr std::size_t REPLY_UTF8_SIZE = 0x7D4; +constexpr std::size_t REPLY_UTF16_SIZE = 0x3EC; + +constexpr const char* GetTextCheckResultName(SwkbdTextCheckResult text_check_result) { + switch (text_check_result) { + case SwkbdTextCheckResult::Success: + return "Success"; + case SwkbdTextCheckResult::Failure: + return "Failure"; + case SwkbdTextCheckResult::Confirm: + return "Confirm"; + case SwkbdTextCheckResult::Silent: + return "Silent"; + default: + UNIMPLEMENTED_MSG("Unknown TextCheckResult={}", text_check_result); + return "Unknown"; + } +} + +void SetReplyBase(std::vector& reply, SwkbdState state, SwkbdReplyType reply_type) { + std::memcpy(reply.data(), &state, sizeof(SwkbdState)); + std::memcpy(reply.data() + sizeof(SwkbdState), &reply_type, sizeof(SwkbdReplyType)); +} + +} // Anonymous namespace + +SoftwareKeyboard::SoftwareKeyboard(Core::System& system_, std::shared_ptr applet_, + LibraryAppletMode applet_mode_, + Core::Frontend::SoftwareKeyboardApplet& frontend_) + : FrontendApplet{system_, applet_, applet_mode_}, frontend{frontend_} {} + +SoftwareKeyboard::~SoftwareKeyboard() = default; + +void SoftwareKeyboard::Initialize() { + FrontendApplet::Initialize(); + + LOG_INFO(Service_AM, "Initializing Software Keyboard Applet with LibraryAppletMode={}", + applet_mode); + + LOG_DEBUG(Service_AM, + "Initializing Applet with common_args: arg_version={}, lib_version={}, " + "play_startup_sound={}, size={}, system_tick={}, theme_color={}", + common_args.arguments_version, common_args.library_version, + common_args.play_startup_sound, common_args.size, common_args.system_tick, + common_args.theme_color); + + swkbd_applet_version = SwkbdAppletVersion{common_args.library_version}; + + switch (applet_mode) { + case LibraryAppletMode::AllForeground: + InitializeForeground(); + break; + case LibraryAppletMode::Background: + case LibraryAppletMode::BackgroundIndirectDisplay: + InitializeBackground(applet_mode); + break; + default: + ASSERT_MSG(false, "Invalid LibraryAppletMode={}", applet_mode); + break; + } +} + +Result SoftwareKeyboard::GetStatus() const { + return status; +} + +void SoftwareKeyboard::ExecuteInteractive() { + if (complete) { + return; + } + + if (is_background) { + ProcessInlineKeyboardRequest(); + } else { + ProcessTextCheck(); + } +} + +void SoftwareKeyboard::Execute() { + if (complete) { + return; + } + + if (is_background) { + return; + } + + ShowNormalKeyboard(); +} + +void SoftwareKeyboard::SubmitTextNormal(SwkbdResult result, std::u16string submitted_text, + bool confirmed) { + if (complete) { + return; + } + + if (swkbd_config_common.use_text_check && result == SwkbdResult::Ok) { + if (confirmed) { + SubmitNormalOutputAndExit(result, submitted_text); + } else { + SubmitForTextCheck(submitted_text); + } + } else { + SubmitNormalOutputAndExit(result, submitted_text); + } +} + +void SoftwareKeyboard::SubmitTextInline(SwkbdReplyType reply_type, std::u16string submitted_text, + s32 cursor_position) { + if (complete) { + return; + } + + current_text = std::move(submitted_text); + current_cursor_position = cursor_position; + + if (inline_use_utf8) { + switch (reply_type) { + case SwkbdReplyType::ChangedString: + reply_type = SwkbdReplyType::ChangedStringUtf8; + break; + case SwkbdReplyType::MovedCursor: + reply_type = SwkbdReplyType::MovedCursorUtf8; + break; + case SwkbdReplyType::DecidedEnter: + reply_type = SwkbdReplyType::DecidedEnterUtf8; + break; + default: + break; + } + } + + if (use_changed_string_v2) { + switch (reply_type) { + case SwkbdReplyType::ChangedString: + reply_type = SwkbdReplyType::ChangedStringV2; + break; + case SwkbdReplyType::ChangedStringUtf8: + reply_type = SwkbdReplyType::ChangedStringUtf8V2; + break; + default: + break; + } + } + + if (use_moved_cursor_v2) { + switch (reply_type) { + case SwkbdReplyType::MovedCursor: + reply_type = SwkbdReplyType::MovedCursorV2; + break; + case SwkbdReplyType::MovedCursorUtf8: + reply_type = SwkbdReplyType::MovedCursorUtf8V2; + break; + default: + break; + } + } + + SendReply(reply_type); +} + +void SoftwareKeyboard::InitializeForeground() { + LOG_INFO(Service_AM, "Initializing Normal Software Keyboard Applet."); + + is_background = false; + + const auto swkbd_config_storage = PopInData(); + ASSERT(swkbd_config_storage != nullptr); + + const auto& swkbd_config_data = swkbd_config_storage->GetData(); + ASSERT(swkbd_config_data.size() >= sizeof(SwkbdConfigCommon)); + + std::memcpy(&swkbd_config_common, swkbd_config_data.data(), sizeof(SwkbdConfigCommon)); + + switch (swkbd_applet_version) { + case SwkbdAppletVersion::Version5: + case SwkbdAppletVersion::Version65542: + ASSERT(swkbd_config_data.size() == sizeof(SwkbdConfigCommon) + sizeof(SwkbdConfigOld)); + std::memcpy(&swkbd_config_old, swkbd_config_data.data() + sizeof(SwkbdConfigCommon), + sizeof(SwkbdConfigOld)); + break; + case SwkbdAppletVersion::Version196615: + case SwkbdAppletVersion::Version262152: + case SwkbdAppletVersion::Version327689: + ASSERT(swkbd_config_data.size() == sizeof(SwkbdConfigCommon) + sizeof(SwkbdConfigOld2)); + std::memcpy(&swkbd_config_old2, swkbd_config_data.data() + sizeof(SwkbdConfigCommon), + sizeof(SwkbdConfigOld2)); + break; + case SwkbdAppletVersion::Version393227: + case SwkbdAppletVersion::Version524301: + ASSERT(swkbd_config_data.size() == sizeof(SwkbdConfigCommon) + sizeof(SwkbdConfigNew)); + std::memcpy(&swkbd_config_new, swkbd_config_data.data() + sizeof(SwkbdConfigCommon), + sizeof(SwkbdConfigNew)); + break; + default: + UNIMPLEMENTED_MSG("Unknown SwkbdConfig revision={} with size={}", swkbd_applet_version, + swkbd_config_data.size()); + ASSERT(swkbd_config_data.size() >= sizeof(SwkbdConfigCommon) + sizeof(SwkbdConfigNew)); + std::memcpy(&swkbd_config_new, swkbd_config_data.data() + sizeof(SwkbdConfigCommon), + sizeof(SwkbdConfigNew)); + break; + } + + const auto work_buffer_storage = PopInData(); + ASSERT(work_buffer_storage != nullptr); + + if (swkbd_config_common.initial_string_length == 0) { + InitializeFrontendNormalKeyboard(); + return; + } + + const auto& work_buffer = work_buffer_storage->GetData(); + + std::vector initial_string(swkbd_config_common.initial_string_length); + + std::memcpy(initial_string.data(), + work_buffer.data() + swkbd_config_common.initial_string_offset, + swkbd_config_common.initial_string_length * sizeof(char16_t)); + + initial_text = Common::UTF16StringFromFixedZeroTerminatedBuffer(initial_string.data(), + initial_string.size()); + + LOG_DEBUG(Service_AM, "\nInitial Text: {}", Common::UTF16ToUTF8(initial_text)); + + InitializeFrontendNormalKeyboard(); +} + +void SoftwareKeyboard::InitializeBackground(LibraryAppletMode library_applet_mode) { + LOG_INFO(Service_AM, "Initializing Inline Software Keyboard Applet."); + + is_background = true; + + const auto swkbd_inline_initialize_arg_storage = PopInData(); + ASSERT(swkbd_inline_initialize_arg_storage != nullptr); + + const auto& swkbd_inline_initialize_arg = swkbd_inline_initialize_arg_storage->GetData(); + ASSERT(swkbd_inline_initialize_arg.size() == sizeof(SwkbdInitializeArg)); + + std::memcpy(&swkbd_initialize_arg, swkbd_inline_initialize_arg.data(), + swkbd_inline_initialize_arg.size()); + + if (swkbd_initialize_arg.library_applet_mode_flag) { + ASSERT(library_applet_mode == LibraryAppletMode::Background); + } else { + ASSERT(library_applet_mode == LibraryAppletMode::BackgroundIndirectDisplay); + } +} + +void SoftwareKeyboard::ProcessTextCheck() { + const auto text_check_storage = PopInteractiveInData(); + ASSERT(text_check_storage != nullptr); + + const auto& text_check_data = text_check_storage->GetData(); + ASSERT(text_check_data.size() == sizeof(SwkbdTextCheck)); + + SwkbdTextCheck swkbd_text_check; + + std::memcpy(&swkbd_text_check, text_check_data.data(), sizeof(SwkbdTextCheck)); + + std::u16string text_check_message = [this, &swkbd_text_check]() -> std::u16string { + if (swkbd_text_check.text_check_result == SwkbdTextCheckResult::Failure || + swkbd_text_check.text_check_result == SwkbdTextCheckResult::Confirm) { + return swkbd_config_common.use_utf8 + ? Common::UTF8ToUTF16(Common::StringFromFixedZeroTerminatedBuffer( + reinterpret_cast( + swkbd_text_check.text_check_message.data()), + swkbd_text_check.text_check_message.size() * sizeof(char16_t))) + : Common::UTF16StringFromFixedZeroTerminatedBuffer( + swkbd_text_check.text_check_message.data(), + swkbd_text_check.text_check_message.size()); + } else { + return u""; + } + }(); + + LOG_INFO(Service_AM, "\nTextCheckResult: {}\nTextCheckMessage: {}", + GetTextCheckResultName(swkbd_text_check.text_check_result), + Common::UTF16ToUTF8(text_check_message)); + + switch (swkbd_text_check.text_check_result) { + case SwkbdTextCheckResult::Success: + SubmitNormalOutputAndExit(SwkbdResult::Ok, current_text); + break; + case SwkbdTextCheckResult::Failure: + ShowTextCheckDialog(SwkbdTextCheckResult::Failure, std::move(text_check_message)); + break; + case SwkbdTextCheckResult::Confirm: + ShowTextCheckDialog(SwkbdTextCheckResult::Confirm, std::move(text_check_message)); + break; + case SwkbdTextCheckResult::Silent: + default: + break; + } +} + +void SoftwareKeyboard::ProcessInlineKeyboardRequest() { + const auto request_data_storage = PopInteractiveInData(); + ASSERT(request_data_storage != nullptr); + + const auto& request_data = request_data_storage->GetData(); + ASSERT(request_data.size() >= sizeof(SwkbdRequestCommand)); + + SwkbdRequestCommand request_command; + + std::memcpy(&request_command, request_data.data(), sizeof(SwkbdRequestCommand)); + + switch (request_command) { + case SwkbdRequestCommand::Finalize: + RequestFinalize(request_data); + break; + case SwkbdRequestCommand::SetUserWordInfo: + RequestSetUserWordInfo(request_data); + break; + case SwkbdRequestCommand::SetCustomizeDic: + RequestSetCustomizeDic(request_data); + break; + case SwkbdRequestCommand::Calc: + RequestCalc(request_data); + break; + case SwkbdRequestCommand::SetCustomizedDictionaries: + RequestSetCustomizedDictionaries(request_data); + break; + case SwkbdRequestCommand::UnsetCustomizedDictionaries: + RequestUnsetCustomizedDictionaries(request_data); + break; + case SwkbdRequestCommand::SetChangedStringV2Flag: + RequestSetChangedStringV2Flag(request_data); + break; + case SwkbdRequestCommand::SetMovedCursorV2Flag: + RequestSetMovedCursorV2Flag(request_data); + break; + default: + UNIMPLEMENTED_MSG("Unknown SwkbdRequestCommand={}", request_command); + break; + } +} + +void SoftwareKeyboard::SubmitNormalOutputAndExit(SwkbdResult result, + std::u16string submitted_text) { + std::vector out_data(sizeof(SwkbdResult) + STRING_BUFFER_SIZE); + + if (swkbd_config_common.use_utf8) { + std::string utf8_submitted_text = Common::UTF16ToUTF8(submitted_text); + + LOG_DEBUG(Service_AM, "\nSwkbdResult: {}\nUTF-8 Submitted Text: {}", result, + utf8_submitted_text); + + std::memcpy(out_data.data(), &result, sizeof(SwkbdResult)); + std::memcpy(out_data.data() + sizeof(SwkbdResult), utf8_submitted_text.data(), + utf8_submitted_text.size()); + } else { + LOG_DEBUG(Service_AM, "\nSwkbdResult: {}\nUTF-16 Submitted Text: {}", result, + Common::UTF16ToUTF8(submitted_text)); + + std::memcpy(out_data.data(), &result, sizeof(SwkbdResult)); + std::memcpy(out_data.data() + sizeof(SwkbdResult), submitted_text.data(), + submitted_text.size() * sizeof(char16_t)); + } + + PushOutData(std::make_shared(system, std::move(out_data))); + + ExitKeyboard(); +} + +void SoftwareKeyboard::SubmitForTextCheck(std::u16string submitted_text) { + current_text = std::move(submitted_text); + + std::vector out_data(sizeof(u64) + STRING_BUFFER_SIZE); + + if (swkbd_config_common.use_utf8) { + std::string utf8_submitted_text = Common::UTF16ToUTF8(current_text); + // Include the null terminator in the buffer size. + const u64 buffer_size = utf8_submitted_text.size() + 1; + + LOG_DEBUG(Service_AM, "\nBuffer Size: {}\nUTF-8 Submitted Text: {}", buffer_size, + utf8_submitted_text); + + std::memcpy(out_data.data(), &buffer_size, sizeof(u64)); + std::memcpy(out_data.data() + sizeof(u64), utf8_submitted_text.data(), + utf8_submitted_text.size()); + } else { + // Include the null terminator in the buffer size. + const u64 buffer_size = (current_text.size() + 1) * sizeof(char16_t); + + LOG_DEBUG(Service_AM, "\nBuffer Size: {}\nUTF-16 Submitted Text: {}", buffer_size, + Common::UTF16ToUTF8(current_text)); + + std::memcpy(out_data.data(), &buffer_size, sizeof(u64)); + std::memcpy(out_data.data() + sizeof(u64), current_text.data(), + current_text.size() * sizeof(char16_t)); + } + + PushInteractiveOutData(std::make_shared(system, std::move(out_data))); +} + +void SoftwareKeyboard::SendReply(SwkbdReplyType reply_type) { + switch (reply_type) { + case SwkbdReplyType::FinishedInitialize: + ReplyFinishedInitialize(); + break; + case SwkbdReplyType::Default: + ReplyDefault(); + break; + case SwkbdReplyType::ChangedString: + ReplyChangedString(); + break; + case SwkbdReplyType::MovedCursor: + ReplyMovedCursor(); + break; + case SwkbdReplyType::MovedTab: + ReplyMovedTab(); + break; + case SwkbdReplyType::DecidedEnter: + ReplyDecidedEnter(); + break; + case SwkbdReplyType::DecidedCancel: + ReplyDecidedCancel(); + break; + case SwkbdReplyType::ChangedStringUtf8: + ReplyChangedStringUtf8(); + break; + case SwkbdReplyType::MovedCursorUtf8: + ReplyMovedCursorUtf8(); + break; + case SwkbdReplyType::DecidedEnterUtf8: + ReplyDecidedEnterUtf8(); + break; + case SwkbdReplyType::UnsetCustomizeDic: + ReplyUnsetCustomizeDic(); + break; + case SwkbdReplyType::ReleasedUserWordInfo: + ReplyReleasedUserWordInfo(); + break; + case SwkbdReplyType::UnsetCustomizedDictionaries: + ReplyUnsetCustomizedDictionaries(); + break; + case SwkbdReplyType::ChangedStringV2: + ReplyChangedStringV2(); + break; + case SwkbdReplyType::MovedCursorV2: + ReplyMovedCursorV2(); + break; + case SwkbdReplyType::ChangedStringUtf8V2: + ReplyChangedStringUtf8V2(); + break; + case SwkbdReplyType::MovedCursorUtf8V2: + ReplyMovedCursorUtf8V2(); + break; + default: + UNIMPLEMENTED_MSG("Unknown SwkbdReplyType={}", reply_type); + ReplyDefault(); + break; + } +} + +void SoftwareKeyboard::ChangeState(SwkbdState state) { + swkbd_state = state; + + ReplyDefault(); +} + +void SoftwareKeyboard::InitializeFrontendNormalKeyboard() { + std::u16string ok_text = Common::UTF16StringFromFixedZeroTerminatedBuffer( + swkbd_config_common.ok_text.data(), swkbd_config_common.ok_text.size()); + + std::u16string header_text = Common::UTF16StringFromFixedZeroTerminatedBuffer( + swkbd_config_common.header_text.data(), swkbd_config_common.header_text.size()); + + std::u16string sub_text = Common::UTF16StringFromFixedZeroTerminatedBuffer( + swkbd_config_common.sub_text.data(), swkbd_config_common.sub_text.size()); + + std::u16string guide_text = Common::UTF16StringFromFixedZeroTerminatedBuffer( + swkbd_config_common.guide_text.data(), swkbd_config_common.guide_text.size()); + + const u32 max_text_length = + swkbd_config_common.max_text_length > 0 && + swkbd_config_common.max_text_length <= DEFAULT_MAX_TEXT_LENGTH + ? swkbd_config_common.max_text_length + : DEFAULT_MAX_TEXT_LENGTH; + + const u32 min_text_length = swkbd_config_common.min_text_length <= max_text_length + ? swkbd_config_common.min_text_length + : 0; + + const s32 initial_cursor_position = [this] { + switch (swkbd_config_common.initial_cursor_position) { + case SwkbdInitialCursorPosition::Start: + default: + return 0; + case SwkbdInitialCursorPosition::End: + return static_cast(initial_text.size()); + } + }(); + + const auto text_draw_type = [this, max_text_length] { + switch (swkbd_config_common.text_draw_type) { + case SwkbdTextDrawType::Line: + default: + return max_text_length <= 32 ? SwkbdTextDrawType::Line : SwkbdTextDrawType::Box; + case SwkbdTextDrawType::Box: + case SwkbdTextDrawType::DownloadCode: + return swkbd_config_common.text_draw_type; + } + }(); + + const auto enable_return_button = + text_draw_type == SwkbdTextDrawType::Box ? swkbd_config_common.enable_return_button : false; + + const auto disable_cancel_button = swkbd_applet_version >= SwkbdAppletVersion::Version393227 + ? swkbd_config_new.disable_cancel_button + : false; + + Core::Frontend::KeyboardInitializeParameters initialize_parameters{ + .ok_text{std::move(ok_text)}, + .header_text{std::move(header_text)}, + .sub_text{std::move(sub_text)}, + .guide_text{std::move(guide_text)}, + .initial_text{initial_text}, + .left_optional_symbol_key{swkbd_config_common.left_optional_symbol_key}, + .right_optional_symbol_key{swkbd_config_common.right_optional_symbol_key}, + .max_text_length{max_text_length}, + .min_text_length{min_text_length}, + .initial_cursor_position{initial_cursor_position}, + .type{swkbd_config_common.type}, + .password_mode{swkbd_config_common.password_mode}, + .text_draw_type{text_draw_type}, + .key_disable_flags{swkbd_config_common.key_disable_flags}, + .use_blur_background{swkbd_config_common.use_blur_background}, + .enable_backspace_button{true}, + .enable_return_button{enable_return_button}, + .disable_cancel_button{disable_cancel_button}, + }; + + frontend.InitializeKeyboard( + false, std::move(initialize_parameters), + [this](SwkbdResult result, std::u16string submitted_text, bool confirmed) { + SubmitTextNormal(result, submitted_text, confirmed); + }, + {}); +} + +void SoftwareKeyboard::InitializeFrontendInlineKeyboard( + Core::Frontend::KeyboardInitializeParameters initialize_parameters) { + frontend.InitializeKeyboard( + true, std::move(initialize_parameters), {}, + [this](SwkbdReplyType reply_type, std::u16string submitted_text, s32 cursor_position) { + SubmitTextInline(reply_type, submitted_text, cursor_position); + }); +} + +void SoftwareKeyboard::InitializeFrontendInlineKeyboardOld() { + const auto& appear_arg = swkbd_calc_arg_old.appear_arg; + + std::u16string ok_text = Common::UTF16StringFromFixedZeroTerminatedBuffer( + appear_arg.ok_text.data(), appear_arg.ok_text.size()); + + const u32 max_text_length = + appear_arg.max_text_length > 0 && appear_arg.max_text_length <= DEFAULT_MAX_TEXT_LENGTH + ? appear_arg.max_text_length + : DEFAULT_MAX_TEXT_LENGTH; + + const u32 min_text_length = + appear_arg.min_text_length <= max_text_length ? appear_arg.min_text_length : 0; + + const s32 initial_cursor_position = current_cursor_position > 0 ? current_cursor_position : 0; + + const auto text_draw_type = + max_text_length <= 32 ? SwkbdTextDrawType::Line : SwkbdTextDrawType::Box; + + Core::Frontend::KeyboardInitializeParameters initialize_parameters{ + .ok_text{std::move(ok_text)}, + .header_text{}, + .sub_text{}, + .guide_text{}, + .initial_text{current_text}, + .left_optional_symbol_key{appear_arg.left_optional_symbol_key}, + .right_optional_symbol_key{appear_arg.right_optional_symbol_key}, + .max_text_length{max_text_length}, + .min_text_length{min_text_length}, + .initial_cursor_position{initial_cursor_position}, + .type{appear_arg.type}, + .password_mode{SwkbdPasswordMode::Disabled}, + .text_draw_type{text_draw_type}, + .key_disable_flags{appear_arg.key_disable_flags}, + .use_blur_background{false}, + .enable_backspace_button{swkbd_calc_arg_old.enable_backspace_button}, + .enable_return_button{appear_arg.enable_return_button}, + .disable_cancel_button{appear_arg.disable_cancel_button}, + }; + + InitializeFrontendInlineKeyboard(std::move(initialize_parameters)); +} + +void SoftwareKeyboard::InitializeFrontendInlineKeyboardNew() { + const auto& appear_arg = swkbd_calc_arg_new.appear_arg; + + std::u16string ok_text = Common::UTF16StringFromFixedZeroTerminatedBuffer( + appear_arg.ok_text.data(), appear_arg.ok_text.size()); + + const u32 max_text_length = + appear_arg.max_text_length > 0 && appear_arg.max_text_length <= DEFAULT_MAX_TEXT_LENGTH + ? appear_arg.max_text_length + : DEFAULT_MAX_TEXT_LENGTH; + + const u32 min_text_length = + appear_arg.min_text_length <= max_text_length ? appear_arg.min_text_length : 0; + + const s32 initial_cursor_position = current_cursor_position > 0 ? current_cursor_position : 0; + + const auto text_draw_type = + max_text_length <= 32 ? SwkbdTextDrawType::Line : SwkbdTextDrawType::Box; + + Core::Frontend::KeyboardInitializeParameters initialize_parameters{ + .ok_text{std::move(ok_text)}, + .header_text{}, + .sub_text{}, + .guide_text{}, + .initial_text{current_text}, + .left_optional_symbol_key{appear_arg.left_optional_symbol_key}, + .right_optional_symbol_key{appear_arg.right_optional_symbol_key}, + .max_text_length{max_text_length}, + .min_text_length{min_text_length}, + .initial_cursor_position{initial_cursor_position}, + .type{appear_arg.type}, + .password_mode{SwkbdPasswordMode::Disabled}, + .text_draw_type{text_draw_type}, + .key_disable_flags{appear_arg.key_disable_flags}, + .use_blur_background{false}, + .enable_backspace_button{swkbd_calc_arg_new.enable_backspace_button}, + .enable_return_button{appear_arg.enable_return_button}, + .disable_cancel_button{appear_arg.disable_cancel_button}, + }; + + InitializeFrontendInlineKeyboard(std::move(initialize_parameters)); +} + +void SoftwareKeyboard::ShowNormalKeyboard() { + frontend.ShowNormalKeyboard(); +} + +void SoftwareKeyboard::ShowTextCheckDialog(SwkbdTextCheckResult text_check_result, + std::u16string text_check_message) { + frontend.ShowTextCheckDialog(text_check_result, std::move(text_check_message)); +} + +void SoftwareKeyboard::ShowInlineKeyboard( + Core::Frontend::InlineAppearParameters appear_parameters) { + frontend.ShowInlineKeyboard(std::move(appear_parameters)); + + ChangeState(SwkbdState::InitializedIsShown); +} + +void SoftwareKeyboard::ShowInlineKeyboardOld() { + if (swkbd_state != SwkbdState::InitializedIsHidden) { + return; + } + + ChangeState(SwkbdState::InitializedIsAppearing); + + const auto& appear_arg = swkbd_calc_arg_old.appear_arg; + + const u32 max_text_length = + appear_arg.max_text_length > 0 && appear_arg.max_text_length <= DEFAULT_MAX_TEXT_LENGTH + ? appear_arg.max_text_length + : DEFAULT_MAX_TEXT_LENGTH; + + const u32 min_text_length = + appear_arg.min_text_length <= max_text_length ? appear_arg.min_text_length : 0; + + Core::Frontend::InlineAppearParameters appear_parameters{ + .max_text_length{max_text_length}, + .min_text_length{min_text_length}, + .key_top_scale_x{swkbd_calc_arg_old.key_top_scale_x}, + .key_top_scale_y{swkbd_calc_arg_old.key_top_scale_y}, + .key_top_translate_x{swkbd_calc_arg_old.key_top_translate_x}, + .key_top_translate_y{swkbd_calc_arg_old.key_top_translate_y}, + .type{appear_arg.type}, + .key_disable_flags{appear_arg.key_disable_flags}, + .key_top_as_floating{swkbd_calc_arg_old.key_top_as_floating}, + .enable_backspace_button{swkbd_calc_arg_old.enable_backspace_button}, + .enable_return_button{appear_arg.enable_return_button}, + .disable_cancel_button{appear_arg.disable_cancel_button}, + }; + + ShowInlineKeyboard(std::move(appear_parameters)); +} + +void SoftwareKeyboard::ShowInlineKeyboardNew() { + if (swkbd_state != SwkbdState::InitializedIsHidden) { + return; + } + + ChangeState(SwkbdState::InitializedIsAppearing); + + const auto& appear_arg = swkbd_calc_arg_new.appear_arg; + + const u32 max_text_length = + appear_arg.max_text_length > 0 && appear_arg.max_text_length <= DEFAULT_MAX_TEXT_LENGTH + ? appear_arg.max_text_length + : DEFAULT_MAX_TEXT_LENGTH; + + const u32 min_text_length = + appear_arg.min_text_length <= max_text_length ? appear_arg.min_text_length : 0; + + Core::Frontend::InlineAppearParameters appear_parameters{ + .max_text_length{max_text_length}, + .min_text_length{min_text_length}, + .key_top_scale_x{swkbd_calc_arg_new.key_top_scale_x}, + .key_top_scale_y{swkbd_calc_arg_new.key_top_scale_y}, + .key_top_translate_x{swkbd_calc_arg_new.key_top_translate_x}, + .key_top_translate_y{swkbd_calc_arg_new.key_top_translate_y}, + .type{appear_arg.type}, + .key_disable_flags{appear_arg.key_disable_flags}, + .key_top_as_floating{swkbd_calc_arg_new.key_top_as_floating}, + .enable_backspace_button{swkbd_calc_arg_new.enable_backspace_button}, + .enable_return_button{appear_arg.enable_return_button}, + .disable_cancel_button{appear_arg.disable_cancel_button}, + }; + + ShowInlineKeyboard(std::move(appear_parameters)); +} + +void SoftwareKeyboard::HideInlineKeyboard() { + if (swkbd_state != SwkbdState::InitializedIsShown) { + return; + } + + ChangeState(SwkbdState::InitializedIsDisappearing); + + frontend.HideInlineKeyboard(); + + ChangeState(SwkbdState::InitializedIsHidden); +} + +void SoftwareKeyboard::InlineTextChanged() { + Core::Frontend::InlineTextParameters text_parameters{ + .input_text{current_text}, + .cursor_position{current_cursor_position}, + }; + + frontend.InlineTextChanged(std::move(text_parameters)); +} + +void SoftwareKeyboard::ExitKeyboard() { + complete = true; + status = ResultSuccess; + + frontend.ExitKeyboard(); + + Exit(); +} + +Result SoftwareKeyboard::RequestExit() { + frontend.Close(); + R_SUCCEED(); +} + +// Inline Software Keyboard Requests + +void SoftwareKeyboard::RequestFinalize(const std::vector& request_data) { + LOG_DEBUG(Service_AM, "Processing Request: Finalize"); + + ChangeState(SwkbdState::NotInitialized); + + ExitKeyboard(); +} + +void SoftwareKeyboard::RequestSetUserWordInfo(const std::vector& request_data) { + LOG_WARNING(Service_AM, "SetUserWordInfo is not implemented."); + + ReplyReleasedUserWordInfo(); +} + +void SoftwareKeyboard::RequestSetCustomizeDic(const std::vector& request_data) { + LOG_WARNING(Service_AM, "SetCustomizeDic is not implemented."); +} + +void SoftwareKeyboard::RequestCalc(const std::vector& request_data) { + LOG_DEBUG(Service_AM, "Processing Request: Calc"); + + ASSERT(request_data.size() >= sizeof(SwkbdRequestCommand) + sizeof(SwkbdCalcArgCommon)); + + std::memcpy(&swkbd_calc_arg_common, request_data.data() + sizeof(SwkbdRequestCommand), + sizeof(SwkbdCalcArgCommon)); + + switch (swkbd_calc_arg_common.calc_arg_size) { + case sizeof(SwkbdCalcArgCommon) + sizeof(SwkbdCalcArgOld): + ASSERT(request_data.size() == + sizeof(SwkbdRequestCommand) + sizeof(SwkbdCalcArgCommon) + sizeof(SwkbdCalcArgOld)); + std::memcpy(&swkbd_calc_arg_old, + request_data.data() + sizeof(SwkbdRequestCommand) + sizeof(SwkbdCalcArgCommon), + sizeof(SwkbdCalcArgOld)); + RequestCalcOld(); + break; + case sizeof(SwkbdCalcArgCommon) + sizeof(SwkbdCalcArgNew): + ASSERT(request_data.size() == + sizeof(SwkbdRequestCommand) + sizeof(SwkbdCalcArgCommon) + sizeof(SwkbdCalcArgNew)); + std::memcpy(&swkbd_calc_arg_new, + request_data.data() + sizeof(SwkbdRequestCommand) + sizeof(SwkbdCalcArgCommon), + sizeof(SwkbdCalcArgNew)); + RequestCalcNew(); + break; + default: + UNIMPLEMENTED_MSG("Unknown SwkbdCalcArg size={}", swkbd_calc_arg_common.calc_arg_size); + ASSERT(request_data.size() >= + sizeof(SwkbdRequestCommand) + sizeof(SwkbdCalcArgCommon) + sizeof(SwkbdCalcArgNew)); + std::memcpy(&swkbd_calc_arg_new, + request_data.data() + sizeof(SwkbdRequestCommand) + sizeof(SwkbdCalcArgCommon), + sizeof(SwkbdCalcArgNew)); + RequestCalcNew(); + break; + } +} + +void SoftwareKeyboard::RequestCalcOld() { + if (swkbd_calc_arg_common.flags.set_input_text) { + current_text = Common::UTF16StringFromFixedZeroTerminatedBuffer( + swkbd_calc_arg_old.input_text.data(), swkbd_calc_arg_old.input_text.size()); + } + + if (swkbd_calc_arg_common.flags.set_cursor_position) { + current_cursor_position = swkbd_calc_arg_old.cursor_position; + } + + if (swkbd_calc_arg_common.flags.set_utf8_mode) { + inline_use_utf8 = swkbd_calc_arg_old.utf8_mode; + } + + if (swkbd_state <= SwkbdState::InitializedIsHidden && + swkbd_calc_arg_common.flags.unset_customize_dic) { + ReplyUnsetCustomizeDic(); + } + + if (swkbd_state <= SwkbdState::InitializedIsHidden && + swkbd_calc_arg_common.flags.unset_user_word_info) { + ReplyReleasedUserWordInfo(); + } + + if (swkbd_state == SwkbdState::NotInitialized && + swkbd_calc_arg_common.flags.set_initialize_arg) { + InitializeFrontendInlineKeyboardOld(); + + ChangeState(SwkbdState::InitializedIsHidden); + + ReplyFinishedInitialize(); + } + + if (!swkbd_calc_arg_common.flags.set_initialize_arg && + (swkbd_calc_arg_common.flags.set_input_text || + swkbd_calc_arg_common.flags.set_cursor_position)) { + InlineTextChanged(); + } + + if (swkbd_state == SwkbdState::InitializedIsHidden && swkbd_calc_arg_common.flags.appear) { + ShowInlineKeyboardOld(); + return; + } + + if (swkbd_state == SwkbdState::InitializedIsShown && swkbd_calc_arg_common.flags.disappear) { + HideInlineKeyboard(); + return; + } +} + +void SoftwareKeyboard::RequestCalcNew() { + if (swkbd_calc_arg_common.flags.set_input_text) { + current_text = Common::UTF16StringFromFixedZeroTerminatedBuffer( + swkbd_calc_arg_new.input_text.data(), swkbd_calc_arg_new.input_text.size()); + } + + if (swkbd_calc_arg_common.flags.set_cursor_position) { + current_cursor_position = swkbd_calc_arg_new.cursor_position; + } + + if (swkbd_calc_arg_common.flags.set_utf8_mode) { + inline_use_utf8 = swkbd_calc_arg_new.utf8_mode; + } + + if (swkbd_state <= SwkbdState::InitializedIsHidden && + swkbd_calc_arg_common.flags.unset_customize_dic) { + ReplyUnsetCustomizeDic(); + } + + if (swkbd_state <= SwkbdState::InitializedIsHidden && + swkbd_calc_arg_common.flags.unset_user_word_info) { + ReplyReleasedUserWordInfo(); + } + + if (swkbd_state == SwkbdState::NotInitialized && + swkbd_calc_arg_common.flags.set_initialize_arg) { + InitializeFrontendInlineKeyboardNew(); + + ChangeState(SwkbdState::InitializedIsHidden); + + ReplyFinishedInitialize(); + } + + if (!swkbd_calc_arg_common.flags.set_initialize_arg && + (swkbd_calc_arg_common.flags.set_input_text || + swkbd_calc_arg_common.flags.set_cursor_position)) { + InlineTextChanged(); + } + + if (swkbd_state == SwkbdState::InitializedIsHidden && swkbd_calc_arg_common.flags.appear) { + ShowInlineKeyboardNew(); + return; + } + + if (swkbd_state == SwkbdState::InitializedIsShown && swkbd_calc_arg_common.flags.disappear) { + HideInlineKeyboard(); + return; + } +} + +void SoftwareKeyboard::RequestSetCustomizedDictionaries(const std::vector& request_data) { + LOG_WARNING(Service_AM, "SetCustomizedDictionaries is not implemented."); +} + +void SoftwareKeyboard::RequestUnsetCustomizedDictionaries(const std::vector& request_data) { + LOG_WARNING(Service_AM, "(STUBBED) Processing Request: UnsetCustomizedDictionaries"); + + ReplyUnsetCustomizedDictionaries(); +} + +void SoftwareKeyboard::RequestSetChangedStringV2Flag(const std::vector& request_data) { + LOG_DEBUG(Service_AM, "Processing Request: SetChangedStringV2Flag"); + + ASSERT(request_data.size() == sizeof(SwkbdRequestCommand) + 1); + + std::memcpy(&use_changed_string_v2, request_data.data() + sizeof(SwkbdRequestCommand), 1); +} + +void SoftwareKeyboard::RequestSetMovedCursorV2Flag(const std::vector& request_data) { + LOG_DEBUG(Service_AM, "Processing Request: SetMovedCursorV2Flag"); + + ASSERT(request_data.size() == sizeof(SwkbdRequestCommand) + 1); + + std::memcpy(&use_moved_cursor_v2, request_data.data() + sizeof(SwkbdRequestCommand), 1); +} + +// Inline Software Keyboard Replies + +void SoftwareKeyboard::ReplyFinishedInitialize() { + LOG_DEBUG(Service_AM, "Sending Reply: FinishedInitialize"); + + std::vector reply(REPLY_BASE_SIZE + 1); + + SetReplyBase(reply, swkbd_state, SwkbdReplyType::FinishedInitialize); + + PushInteractiveOutData(std::make_shared(system, std::move(reply))); +} + +void SoftwareKeyboard::ReplyDefault() { + LOG_DEBUG(Service_AM, "Sending Reply: Default"); + + std::vector reply(REPLY_BASE_SIZE); + + SetReplyBase(reply, swkbd_state, SwkbdReplyType::Default); + + PushInteractiveOutData(std::make_shared(system, std::move(reply))); +} + +void SoftwareKeyboard::ReplyChangedString() { + LOG_DEBUG(Service_AM, "Sending Reply: ChangedString"); + + std::vector reply(REPLY_BASE_SIZE + REPLY_UTF16_SIZE + sizeof(SwkbdChangedStringArg)); + + SetReplyBase(reply, swkbd_state, SwkbdReplyType::ChangedString); + + const SwkbdChangedStringArg changed_string_arg{ + .text_length{static_cast(current_text.size())}, + .dictionary_start_cursor_position{-1}, + .dictionary_end_cursor_position{-1}, + .cursor_position{current_cursor_position}, + }; + + std::memcpy(reply.data() + REPLY_BASE_SIZE, current_text.data(), + current_text.size() * sizeof(char16_t)); + std::memcpy(reply.data() + REPLY_BASE_SIZE + REPLY_UTF16_SIZE, &changed_string_arg, + sizeof(SwkbdChangedStringArg)); + + PushInteractiveOutData(std::make_shared(system, std::move(reply))); +} + +void SoftwareKeyboard::ReplyMovedCursor() { + LOG_DEBUG(Service_AM, "Sending Reply: MovedCursor"); + + std::vector reply(REPLY_BASE_SIZE + REPLY_UTF16_SIZE + sizeof(SwkbdMovedCursorArg)); + + SetReplyBase(reply, swkbd_state, SwkbdReplyType::MovedCursor); + + const SwkbdMovedCursorArg moved_cursor_arg{ + .text_length{static_cast(current_text.size())}, + .cursor_position{current_cursor_position}, + }; + + std::memcpy(reply.data() + REPLY_BASE_SIZE, current_text.data(), + current_text.size() * sizeof(char16_t)); + std::memcpy(reply.data() + REPLY_BASE_SIZE + REPLY_UTF16_SIZE, &moved_cursor_arg, + sizeof(SwkbdMovedCursorArg)); + + PushInteractiveOutData(std::make_shared(system, std::move(reply))); +} + +void SoftwareKeyboard::ReplyMovedTab() { + LOG_DEBUG(Service_AM, "Sending Reply: MovedTab"); + + std::vector reply(REPLY_BASE_SIZE + REPLY_UTF16_SIZE + sizeof(SwkbdMovedTabArg)); + + SetReplyBase(reply, swkbd_state, SwkbdReplyType::MovedTab); + + const SwkbdMovedTabArg moved_tab_arg{ + .text_length{static_cast(current_text.size())}, + .cursor_position{current_cursor_position}, + }; + + std::memcpy(reply.data() + REPLY_BASE_SIZE, current_text.data(), + current_text.size() * sizeof(char16_t)); + std::memcpy(reply.data() + REPLY_BASE_SIZE + REPLY_UTF16_SIZE, &moved_tab_arg, + sizeof(SwkbdMovedTabArg)); + + PushInteractiveOutData(std::make_shared(system, std::move(reply))); +} + +void SoftwareKeyboard::ReplyDecidedEnter() { + LOG_DEBUG(Service_AM, "Sending Reply: DecidedEnter"); + + std::vector reply(REPLY_BASE_SIZE + REPLY_UTF16_SIZE + sizeof(SwkbdDecidedEnterArg)); + + SetReplyBase(reply, swkbd_state, SwkbdReplyType::DecidedEnter); + + const SwkbdDecidedEnterArg decided_enter_arg{ + .text_length{static_cast(current_text.size())}, + }; + + std::memcpy(reply.data() + REPLY_BASE_SIZE, current_text.data(), + current_text.size() * sizeof(char16_t)); + std::memcpy(reply.data() + REPLY_BASE_SIZE + REPLY_UTF16_SIZE, &decided_enter_arg, + sizeof(SwkbdDecidedEnterArg)); + + PushInteractiveOutData(std::make_shared(system, std::move(reply))); + + HideInlineKeyboard(); +} + +void SoftwareKeyboard::ReplyDecidedCancel() { + LOG_DEBUG(Service_AM, "Sending Reply: DecidedCancel"); + + std::vector reply(REPLY_BASE_SIZE); + + SetReplyBase(reply, swkbd_state, SwkbdReplyType::DecidedCancel); + + PushInteractiveOutData(std::make_shared(system, std::move(reply))); + + HideInlineKeyboard(); +} + +void SoftwareKeyboard::ReplyChangedStringUtf8() { + LOG_DEBUG(Service_AM, "Sending Reply: ChangedStringUtf8"); + + std::vector reply(REPLY_BASE_SIZE + REPLY_UTF8_SIZE + sizeof(SwkbdChangedStringArg)); + + SetReplyBase(reply, swkbd_state, SwkbdReplyType::ChangedStringUtf8); + + std::string utf8_current_text = Common::UTF16ToUTF8(current_text); + + const SwkbdChangedStringArg changed_string_arg{ + .text_length{static_cast(current_text.size())}, + .dictionary_start_cursor_position{-1}, + .dictionary_end_cursor_position{-1}, + .cursor_position{current_cursor_position}, + }; + + std::memcpy(reply.data() + REPLY_BASE_SIZE, utf8_current_text.data(), utf8_current_text.size()); + std::memcpy(reply.data() + REPLY_BASE_SIZE + REPLY_UTF8_SIZE, &changed_string_arg, + sizeof(SwkbdChangedStringArg)); + + PushInteractiveOutData(std::make_shared(system, std::move(reply))); +} + +void SoftwareKeyboard::ReplyMovedCursorUtf8() { + LOG_DEBUG(Service_AM, "Sending Reply: MovedCursorUtf8"); + + std::vector reply(REPLY_BASE_SIZE + REPLY_UTF8_SIZE + sizeof(SwkbdMovedCursorArg)); + + SetReplyBase(reply, swkbd_state, SwkbdReplyType::MovedCursorUtf8); + + std::string utf8_current_text = Common::UTF16ToUTF8(current_text); + + const SwkbdMovedCursorArg moved_cursor_arg{ + .text_length{static_cast(current_text.size())}, + .cursor_position{current_cursor_position}, + }; + + std::memcpy(reply.data() + REPLY_BASE_SIZE, utf8_current_text.data(), utf8_current_text.size()); + std::memcpy(reply.data() + REPLY_BASE_SIZE + REPLY_UTF8_SIZE, &moved_cursor_arg, + sizeof(SwkbdMovedCursorArg)); + + PushInteractiveOutData(std::make_shared(system, std::move(reply))); +} + +void SoftwareKeyboard::ReplyDecidedEnterUtf8() { + LOG_DEBUG(Service_AM, "Sending Reply: DecidedEnterUtf8"); + + std::vector reply(REPLY_BASE_SIZE + REPLY_UTF8_SIZE + sizeof(SwkbdDecidedEnterArg)); + + SetReplyBase(reply, swkbd_state, SwkbdReplyType::DecidedEnterUtf8); + + std::string utf8_current_text = Common::UTF16ToUTF8(current_text); + + const SwkbdDecidedEnterArg decided_enter_arg{ + .text_length{static_cast(current_text.size())}, + }; + + std::memcpy(reply.data() + REPLY_BASE_SIZE, utf8_current_text.data(), utf8_current_text.size()); + std::memcpy(reply.data() + REPLY_BASE_SIZE + REPLY_UTF8_SIZE, &decided_enter_arg, + sizeof(SwkbdDecidedEnterArg)); + + PushInteractiveOutData(std::make_shared(system, std::move(reply))); + + HideInlineKeyboard(); +} + +void SoftwareKeyboard::ReplyUnsetCustomizeDic() { + LOG_DEBUG(Service_AM, "Sending Reply: UnsetCustomizeDic"); + + std::vector reply(REPLY_BASE_SIZE); + + SetReplyBase(reply, swkbd_state, SwkbdReplyType::UnsetCustomizeDic); + + PushInteractiveOutData(std::make_shared(system, std::move(reply))); +} + +void SoftwareKeyboard::ReplyReleasedUserWordInfo() { + LOG_DEBUG(Service_AM, "Sending Reply: ReleasedUserWordInfo"); + + std::vector reply(REPLY_BASE_SIZE); + + SetReplyBase(reply, swkbd_state, SwkbdReplyType::ReleasedUserWordInfo); + + PushInteractiveOutData(std::make_shared(system, std::move(reply))); +} + +void SoftwareKeyboard::ReplyUnsetCustomizedDictionaries() { + LOG_DEBUG(Service_AM, "Sending Reply: UnsetCustomizedDictionaries"); + + std::vector reply(REPLY_BASE_SIZE); + + SetReplyBase(reply, swkbd_state, SwkbdReplyType::UnsetCustomizedDictionaries); + + PushInteractiveOutData(std::make_shared(system, std::move(reply))); +} + +void SoftwareKeyboard::ReplyChangedStringV2() { + LOG_DEBUG(Service_AM, "Sending Reply: ChangedStringV2"); + + std::vector reply(REPLY_BASE_SIZE + REPLY_UTF16_SIZE + sizeof(SwkbdChangedStringArg) + 1); + + SetReplyBase(reply, swkbd_state, SwkbdReplyType::ChangedStringV2); + + const SwkbdChangedStringArg changed_string_arg{ + .text_length{static_cast(current_text.size())}, + .dictionary_start_cursor_position{-1}, + .dictionary_end_cursor_position{-1}, + .cursor_position{current_cursor_position}, + }; + + constexpr u8 flag = 0; + + std::memcpy(reply.data() + REPLY_BASE_SIZE, current_text.data(), + current_text.size() * sizeof(char16_t)); + std::memcpy(reply.data() + REPLY_BASE_SIZE + REPLY_UTF16_SIZE, &changed_string_arg, + sizeof(SwkbdChangedStringArg)); + std::memcpy(reply.data() + REPLY_BASE_SIZE + REPLY_UTF16_SIZE + sizeof(SwkbdChangedStringArg), + &flag, 1); + + PushInteractiveOutData(std::make_shared(system, std::move(reply))); +} + +void SoftwareKeyboard::ReplyMovedCursorV2() { + LOG_DEBUG(Service_AM, "Sending Reply: MovedCursorV2"); + + std::vector reply(REPLY_BASE_SIZE + REPLY_UTF16_SIZE + sizeof(SwkbdMovedCursorArg) + 1); + + SetReplyBase(reply, swkbd_state, SwkbdReplyType::MovedCursorV2); + + const SwkbdMovedCursorArg moved_cursor_arg{ + .text_length{static_cast(current_text.size())}, + .cursor_position{current_cursor_position}, + }; + + constexpr u8 flag = 0; + + std::memcpy(reply.data() + REPLY_BASE_SIZE, current_text.data(), + current_text.size() * sizeof(char16_t)); + std::memcpy(reply.data() + REPLY_BASE_SIZE + REPLY_UTF16_SIZE, &moved_cursor_arg, + sizeof(SwkbdMovedCursorArg)); + std::memcpy(reply.data() + REPLY_BASE_SIZE + REPLY_UTF16_SIZE + sizeof(SwkbdMovedCursorArg), + &flag, 1); + + PushInteractiveOutData(std::make_shared(system, std::move(reply))); +} + +void SoftwareKeyboard::ReplyChangedStringUtf8V2() { + LOG_DEBUG(Service_AM, "Sending Reply: ChangedStringUtf8V2"); + + std::vector reply(REPLY_BASE_SIZE + REPLY_UTF8_SIZE + sizeof(SwkbdChangedStringArg) + 1); + + SetReplyBase(reply, swkbd_state, SwkbdReplyType::ChangedStringUtf8V2); + + std::string utf8_current_text = Common::UTF16ToUTF8(current_text); + + const SwkbdChangedStringArg changed_string_arg{ + .text_length{static_cast(current_text.size())}, + .dictionary_start_cursor_position{-1}, + .dictionary_end_cursor_position{-1}, + .cursor_position{current_cursor_position}, + }; + + constexpr u8 flag = 0; + + std::memcpy(reply.data() + REPLY_BASE_SIZE, utf8_current_text.data(), utf8_current_text.size()); + std::memcpy(reply.data() + REPLY_BASE_SIZE + REPLY_UTF8_SIZE, &changed_string_arg, + sizeof(SwkbdChangedStringArg)); + std::memcpy(reply.data() + REPLY_BASE_SIZE + REPLY_UTF8_SIZE + sizeof(SwkbdChangedStringArg), + &flag, 1); + + PushInteractiveOutData(std::make_shared(system, std::move(reply))); +} + +void SoftwareKeyboard::ReplyMovedCursorUtf8V2() { + LOG_DEBUG(Service_AM, "Sending Reply: MovedCursorUtf8V2"); + + std::vector reply(REPLY_BASE_SIZE + REPLY_UTF8_SIZE + sizeof(SwkbdMovedCursorArg) + 1); + + SetReplyBase(reply, swkbd_state, SwkbdReplyType::MovedCursorUtf8V2); + + std::string utf8_current_text = Common::UTF16ToUTF8(current_text); + + const SwkbdMovedCursorArg moved_cursor_arg{ + .text_length{static_cast(current_text.size())}, + .cursor_position{current_cursor_position}, + }; + + constexpr u8 flag = 0; + + std::memcpy(reply.data() + REPLY_BASE_SIZE, utf8_current_text.data(), utf8_current_text.size()); + std::memcpy(reply.data() + REPLY_BASE_SIZE + REPLY_UTF8_SIZE, &moved_cursor_arg, + sizeof(SwkbdMovedCursorArg)); + std::memcpy(reply.data() + REPLY_BASE_SIZE + REPLY_UTF8_SIZE + sizeof(SwkbdMovedCursorArg), + &flag, 1); + + PushInteractiveOutData(std::make_shared(system, std::move(reply))); +} + +} // namespace Service::AM::Frontend diff --git a/src/core/hle/service/am/frontend/applet_software_keyboard.h b/src/core/hle/service/am/frontend/applet_software_keyboard.h new file mode 100755 index 000000000..f464b7e15 --- /dev/null +++ b/src/core/hle/service/am/frontend/applet_software_keyboard.h @@ -0,0 +1,186 @@ +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "common/common_types.h" +#include "core/hle/result.h" +#include "core/hle/service/am/frontend/applet_software_keyboard_types.h" +#include "core/hle/service/am/frontend/applets.h" + +namespace Core { +class System; +} + +namespace Core::Frontend { +struct KeyboardInitializeParameters; +struct InlineAppearParameters; +} // namespace Core::Frontend + +namespace Service::AM::Frontend { + +class SoftwareKeyboard final : public FrontendApplet { +public: + explicit SoftwareKeyboard(Core::System& system_, std::shared_ptr applet_, + LibraryAppletMode applet_mode_, + Core::Frontend::SoftwareKeyboardApplet& frontend_); + ~SoftwareKeyboard() override; + + void Initialize() override; + + Result GetStatus() const override; + void ExecuteInteractive() override; + void Execute() override; + Result RequestExit() override; + + /** + * Submits the input text to the application. + * If text checking is enabled, the application will verify the input text. + * If use_utf8 is enabled, the input text will be converted to UTF-8 prior to being submitted. + * This should only be used by the normal software keyboard. + * + * @param result SwkbdResult enum + * @param submitted_text UTF-16 encoded string + * @param confirmed Whether the text has been confirmed after TextCheckResult::Confirm + */ + void SubmitTextNormal(SwkbdResult result, std::u16string submitted_text, bool confirmed); + + /** + * Submits the input text to the application. + * If utf8_mode is enabled, the input text will be converted to UTF-8 prior to being submitted. + * This should only be used by the inline software keyboard. + * + * @param reply_type SwkbdReplyType enum + * @param submitted_text UTF-16 encoded string + * @param cursor_position The current position of the text cursor + */ + void SubmitTextInline(SwkbdReplyType reply_type, std::u16string submitted_text, + s32 cursor_position); + +private: + /// Initializes the normal software keyboard. + void InitializeForeground(); + + /// Initializes the inline software keyboard. + void InitializeBackground(LibraryAppletMode library_applet_mode); + + /// Processes the text check sent by the application. + void ProcessTextCheck(); + + /// Processes the inline software keyboard request command sent by the application. + void ProcessInlineKeyboardRequest(); + + /// Submits the input text and exits the applet. + void SubmitNormalOutputAndExit(SwkbdResult result, std::u16string submitted_text); + + /// Submits the input text for text checking. + void SubmitForTextCheck(std::u16string submitted_text); + + /// Sends a reply to the application after processing a request command. + void SendReply(SwkbdReplyType reply_type); + + /// Changes the inline keyboard state. + void ChangeState(SwkbdState state); + + /** + * Signals the frontend to initialize the normal software keyboard with common parameters. + * Note that this does not cause the keyboard to appear. + * Use the ShowNormalKeyboard() functions to cause the keyboard to appear. + */ + void InitializeFrontendNormalKeyboard(); + + /** + * Signals the frontend to initialize the inline software keyboard with common parameters. + * Note that this does not cause the keyboard to appear. + * Use the ShowInlineKeyboard() to cause the keyboard to appear. + */ + void InitializeFrontendInlineKeyboard( + Core::Frontend::KeyboardInitializeParameters initialize_parameters); + + void InitializeFrontendInlineKeyboardOld(); + void InitializeFrontendInlineKeyboardNew(); + + /// Signals the frontend to show the normal software keyboard. + void ShowNormalKeyboard(); + + /// Signals the frontend to show the text check dialog. + void ShowTextCheckDialog(SwkbdTextCheckResult text_check_result, + std::u16string text_check_message); + + /// Signals the frontend to show the inline software keyboard. + void ShowInlineKeyboard(Core::Frontend::InlineAppearParameters appear_parameters); + + void ShowInlineKeyboardOld(); + void ShowInlineKeyboardNew(); + + /// Signals the frontend to hide the inline software keyboard. + void HideInlineKeyboard(); + + /// Signals the frontend that the current inline keyboard text has changed. + void InlineTextChanged(); + + /// Signals both the frontend and application that the software keyboard is exiting. + void ExitKeyboard(); + + // Inline Software Keyboard Requests + + void RequestFinalize(const std::vector& request_data); + void RequestSetUserWordInfo(const std::vector& request_data); + void RequestSetCustomizeDic(const std::vector& request_data); + void RequestCalc(const std::vector& request_data); + void RequestCalcOld(); + void RequestCalcNew(); + void RequestSetCustomizedDictionaries(const std::vector& request_data); + void RequestUnsetCustomizedDictionaries(const std::vector& request_data); + void RequestSetChangedStringV2Flag(const std::vector& request_data); + void RequestSetMovedCursorV2Flag(const std::vector& request_data); + + // Inline Software Keyboard Replies + + void ReplyFinishedInitialize(); + void ReplyDefault(); + void ReplyChangedString(); + void ReplyMovedCursor(); + void ReplyMovedTab(); + void ReplyDecidedEnter(); + void ReplyDecidedCancel(); + void ReplyChangedStringUtf8(); + void ReplyMovedCursorUtf8(); + void ReplyDecidedEnterUtf8(); + void ReplyUnsetCustomizeDic(); + void ReplyReleasedUserWordInfo(); + void ReplyUnsetCustomizedDictionaries(); + void ReplyChangedStringV2(); + void ReplyMovedCursorV2(); + void ReplyChangedStringUtf8V2(); + void ReplyMovedCursorUtf8V2(); + + Core::Frontend::SoftwareKeyboardApplet& frontend; + + SwkbdAppletVersion swkbd_applet_version; + + SwkbdConfigCommon swkbd_config_common; + SwkbdConfigOld swkbd_config_old; + SwkbdConfigOld2 swkbd_config_old2; + SwkbdConfigNew swkbd_config_new; + std::u16string initial_text; + + SwkbdState swkbd_state{SwkbdState::NotInitialized}; + SwkbdInitializeArg swkbd_initialize_arg; + SwkbdCalcArgCommon swkbd_calc_arg_common; + SwkbdCalcArgOld swkbd_calc_arg_old; + SwkbdCalcArgNew swkbd_calc_arg_new; + bool use_changed_string_v2{false}; + bool use_moved_cursor_v2{false}; + bool inline_use_utf8{false}; + s32 current_cursor_position{}; + + std::u16string current_text; + + bool is_background{false}; + + bool complete{false}; + Result status{ResultSuccess}; +}; + +} // namespace Service::AM::Frontend diff --git a/src/core/hle/service/am/frontend/applet_software_keyboard_types.h b/src/core/hle/service/am/frontend/applet_software_keyboard_types.h new file mode 100755 index 000000000..a25ff2a6d --- /dev/null +++ b/src/core/hle/service/am/frontend/applet_software_keyboard_types.h @@ -0,0 +1,354 @@ +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include + +#include "common/bit_field.h" +#include "common/common_funcs.h" +#include "common/common_types.h" +#include "common/swap.h" +#include "common/uuid.h" + +namespace Service::AM::Frontend { + +constexpr std::size_t MAX_OK_TEXT_LENGTH = 8; +constexpr std::size_t MAX_HEADER_TEXT_LENGTH = 64; +constexpr std::size_t MAX_SUB_TEXT_LENGTH = 128; +constexpr std::size_t MAX_GUIDE_TEXT_LENGTH = 256; +constexpr std::size_t STRING_BUFFER_SIZE = 0x7D4; + +enum class SwkbdAppletVersion : u32_le { + Version5 = 0x5, // 1.0.0 + Version65542 = 0x10006, // 2.0.0 - 2.3.0 + Version196615 = 0x30007, // 3.0.0 - 3.0.2 + Version262152 = 0x40008, // 4.0.0 - 4.1.0 + Version327689 = 0x50009, // 5.0.0 - 5.1.0 + Version393227 = 0x6000B, // 6.0.0 - 7.0.1 + Version524301 = 0x8000D, // 8.0.0+ +}; + +enum class SwkbdType : u32 { + Normal, + NumberPad, + Qwerty, + Unknown3, + Latin, + SimplifiedChinese, + TraditionalChinese, + Korean, +}; + +enum class SwkbdInitialCursorPosition : u32 { + Start, + End, +}; + +enum class SwkbdPasswordMode : u32 { + Disabled, + Enabled, +}; + +enum class SwkbdTextDrawType : u32 { + Line, + Box, + DownloadCode, +}; + +enum class SwkbdResult : u32 { + Ok, + Cancel, +}; + +enum class SwkbdTextCheckResult : u32 { + Success, + Failure, + Confirm, + Silent, +}; + +enum class SwkbdState : u32 { + NotInitialized = 0x0, + InitializedIsHidden = 0x1, + InitializedIsAppearing = 0x2, + InitializedIsShown = 0x3, + InitializedIsDisappearing = 0x4, +}; + +enum class SwkbdRequestCommand : u32 { + Finalize = 0x4, + SetUserWordInfo = 0x6, + SetCustomizeDic = 0x7, + Calc = 0xA, + SetCustomizedDictionaries = 0xB, + UnsetCustomizedDictionaries = 0xC, + SetChangedStringV2Flag = 0xD, + SetMovedCursorV2Flag = 0xE, +}; + +enum class SwkbdReplyType : u32 { + FinishedInitialize = 0x0, + Default = 0x1, + ChangedString = 0x2, + MovedCursor = 0x3, + MovedTab = 0x4, + DecidedEnter = 0x5, + DecidedCancel = 0x6, + ChangedStringUtf8 = 0x7, + MovedCursorUtf8 = 0x8, + DecidedEnterUtf8 = 0x9, + UnsetCustomizeDic = 0xA, + ReleasedUserWordInfo = 0xB, + UnsetCustomizedDictionaries = 0xC, + ChangedStringV2 = 0xD, + MovedCursorV2 = 0xE, + ChangedStringUtf8V2 = 0xF, + MovedCursorUtf8V2 = 0x10, +}; + +struct SwkbdKeyDisableFlags { + union { + u32 raw{}; + + BitField<1, 1, u32> space; + BitField<2, 1, u32> at; + BitField<3, 1, u32> percent; + BitField<4, 1, u32> slash; + BitField<5, 1, u32> backslash; + BitField<6, 1, u32> numbers; + BitField<7, 1, u32> download_code; + BitField<8, 1, u32> username; + }; +}; +static_assert(sizeof(SwkbdKeyDisableFlags) == 0x4, "SwkbdKeyDisableFlags has incorrect size."); + +struct SwkbdConfigCommon { + SwkbdType type{}; + std::array ok_text{}; + char16_t left_optional_symbol_key{}; + char16_t right_optional_symbol_key{}; + bool use_prediction{}; + INSERT_PADDING_BYTES(1); + SwkbdKeyDisableFlags key_disable_flags{}; + SwkbdInitialCursorPosition initial_cursor_position{}; + std::array header_text{}; + std::array sub_text{}; + std::array guide_text{}; + u32 max_text_length{}; + u32 min_text_length{}; + SwkbdPasswordMode password_mode{}; + SwkbdTextDrawType text_draw_type{}; + bool enable_return_button{}; + bool use_utf8{}; + bool use_blur_background{}; + INSERT_PADDING_BYTES(1); + u32 initial_string_offset{}; + u32 initial_string_length{}; + u32 user_dictionary_offset{}; + u32 user_dictionary_entries{}; + bool use_text_check{}; + INSERT_PADDING_BYTES(3); +}; +static_assert(sizeof(SwkbdConfigCommon) == 0x3D4, "SwkbdConfigCommon has incorrect size."); + +#pragma pack(push, 4) +// SwkbdAppletVersion 0x5, 0x10006 +struct SwkbdConfigOld { + INSERT_PADDING_WORDS(1); + VAddr text_check_callback{}; +}; +static_assert(sizeof(SwkbdConfigOld) == 0x3E0 - sizeof(SwkbdConfigCommon), + "SwkbdConfigOld has incorrect size."); + +// SwkbdAppletVersion 0x30007, 0x40008, 0x50009 +struct SwkbdConfigOld2 { + INSERT_PADDING_WORDS(1); + VAddr text_check_callback{}; + std::array text_grouping{}; +}; +static_assert(sizeof(SwkbdConfigOld2) == 0x400 - sizeof(SwkbdConfigCommon), + "SwkbdConfigOld2 has incorrect size."); + +// SwkbdAppletVersion 0x6000B, 0x8000D +struct SwkbdConfigNew { + std::array text_grouping{}; + std::array customized_dictionary_set_entries{}; + u8 total_customized_dictionary_set_entries{}; + bool disable_cancel_button{}; + INSERT_PADDING_BYTES(18); +}; +static_assert(sizeof(SwkbdConfigNew) == 0x4C8 - sizeof(SwkbdConfigCommon), + "SwkbdConfigNew has incorrect size."); +#pragma pack(pop) + +struct SwkbdTextCheck { + SwkbdTextCheckResult text_check_result{}; + std::array text_check_message{}; +}; +static_assert(sizeof(SwkbdTextCheck) == 0x7D8, "SwkbdTextCheck has incorrect size."); + +struct SwkbdCalcArgFlags { + union { + u64 raw{}; + + BitField<0, 1, u64> set_initialize_arg; + BitField<1, 1, u64> set_volume; + BitField<2, 1, u64> appear; + BitField<3, 1, u64> set_input_text; + BitField<4, 1, u64> set_cursor_position; + BitField<5, 1, u64> set_utf8_mode; + BitField<6, 1, u64> unset_customize_dic; + BitField<7, 1, u64> disappear; + BitField<8, 1, u64> unknown; + BitField<9, 1, u64> set_key_top_translate_scale; + BitField<10, 1, u64> unset_user_word_info; + BitField<11, 1, u64> set_disable_hardware_keyboard; + }; +}; +static_assert(sizeof(SwkbdCalcArgFlags) == 0x8, "SwkbdCalcArgFlags has incorrect size."); + +struct SwkbdInitializeArg { + u32 unknown{}; + bool library_applet_mode_flag{}; + bool is_above_hos_500{}; + INSERT_PADDING_BYTES(2); +}; +static_assert(sizeof(SwkbdInitializeArg) == 0x8, "SwkbdInitializeArg has incorrect size."); + +struct SwkbdAppearArgOld { + SwkbdType type{}; + std::array ok_text{}; + char16_t left_optional_symbol_key{}; + char16_t right_optional_symbol_key{}; + bool use_prediction{}; + bool disable_cancel_button{}; + SwkbdKeyDisableFlags key_disable_flags{}; + u32 max_text_length{}; + u32 min_text_length{}; + bool enable_return_button{}; + INSERT_PADDING_BYTES(3); + u32 flags{}; + bool is_use_save_data{}; + INSERT_PADDING_BYTES(7); + Common::UUID user_id{}; +}; +static_assert(sizeof(SwkbdAppearArgOld) == 0x48, "SwkbdAppearArg has incorrect size."); + +struct SwkbdAppearArgNew { + SwkbdType type{}; + std::array ok_text{}; + char16_t left_optional_symbol_key{}; + char16_t right_optional_symbol_key{}; + bool use_prediction{}; + bool disable_cancel_button{}; + SwkbdKeyDisableFlags key_disable_flags{}; + u32 max_text_length{}; + u32 min_text_length{}; + bool enable_return_button{}; + INSERT_PADDING_BYTES(3); + u32 flags{}; + bool is_use_save_data{}; + INSERT_PADDING_BYTES(7); + Common::UUID user_id{}; + u64 start_sampling_number{}; + INSERT_PADDING_WORDS(8); +}; +static_assert(sizeof(SwkbdAppearArgNew) == 0x70, "SwkbdAppearArg has incorrect size."); + +struct SwkbdCalcArgCommon { + u32 unknown{}; + u16 calc_arg_size{}; + INSERT_PADDING_BYTES(2); + SwkbdCalcArgFlags flags{}; + SwkbdInitializeArg initialize_arg{}; +}; +static_assert(sizeof(SwkbdCalcArgCommon) == 0x18, "SwkbdCalcArgCommon has incorrect size."); + +struct SwkbdCalcArgOld { + f32 volume{}; + s32 cursor_position{}; + SwkbdAppearArgOld appear_arg{}; + std::array input_text{}; + bool utf8_mode{}; + INSERT_PADDING_BYTES(1); + bool enable_backspace_button{}; + INSERT_PADDING_BYTES(3); + bool key_top_as_floating{}; + bool footer_scalable{}; + bool alpha_enabled_in_input_mode{}; + u8 input_mode_fade_type{}; + bool disable_touch{}; + bool disable_hardware_keyboard{}; + INSERT_PADDING_BYTES(8); + f32 key_top_scale_x{}; + f32 key_top_scale_y{}; + f32 key_top_translate_x{}; + f32 key_top_translate_y{}; + f32 key_top_bg_alpha{}; + f32 footer_bg_alpha{}; + f32 balloon_scale{}; + INSERT_PADDING_WORDS(4); + u8 se_group{}; + INSERT_PADDING_BYTES(3); +}; +static_assert(sizeof(SwkbdCalcArgOld) == 0x4A0 - sizeof(SwkbdCalcArgCommon), + "SwkbdCalcArgOld has incorrect size."); + +struct SwkbdCalcArgNew { + SwkbdAppearArgNew appear_arg{}; + f32 volume{}; + s32 cursor_position{}; + std::array input_text{}; + bool utf8_mode{}; + INSERT_PADDING_BYTES(1); + bool enable_backspace_button{}; + INSERT_PADDING_BYTES(3); + bool key_top_as_floating{}; + bool footer_scalable{}; + bool alpha_enabled_in_input_mode{}; + u8 input_mode_fade_type{}; + bool disable_touch{}; + bool disable_hardware_keyboard{}; + INSERT_PADDING_BYTES(8); + f32 key_top_scale_x{}; + f32 key_top_scale_y{}; + f32 key_top_translate_x{}; + f32 key_top_translate_y{}; + f32 key_top_bg_alpha{}; + f32 footer_bg_alpha{}; + f32 balloon_scale{}; + INSERT_PADDING_WORDS(4); + u8 se_group{}; + INSERT_PADDING_BYTES(3); + INSERT_PADDING_WORDS(8); +}; +static_assert(sizeof(SwkbdCalcArgNew) == 0x4E8 - sizeof(SwkbdCalcArgCommon), + "SwkbdCalcArgNew has incorrect size."); + +struct SwkbdChangedStringArg { + u32 text_length{}; + s32 dictionary_start_cursor_position{}; + s32 dictionary_end_cursor_position{}; + s32 cursor_position{}; +}; +static_assert(sizeof(SwkbdChangedStringArg) == 0x10, "SwkbdChangedStringArg has incorrect size."); + +struct SwkbdMovedCursorArg { + u32 text_length{}; + s32 cursor_position{}; +}; +static_assert(sizeof(SwkbdMovedCursorArg) == 0x8, "SwkbdMovedCursorArg has incorrect size."); + +struct SwkbdMovedTabArg { + u32 text_length{}; + s32 cursor_position{}; +}; +static_assert(sizeof(SwkbdMovedTabArg) == 0x8, "SwkbdMovedTabArg has incorrect size."); + +struct SwkbdDecidedEnterArg { + u32 text_length{}; +}; +static_assert(sizeof(SwkbdDecidedEnterArg) == 0x4, "SwkbdDecidedEnterArg has incorrect size."); + +} // namespace Service::AM::Frontend diff --git a/src/core/hle/service/am/frontend/applet_web_browser.cpp b/src/core/hle/service/am/frontend/applet_web_browser.cpp new file mode 100755 index 000000000..6ee4caf34 --- /dev/null +++ b/src/core/hle/service/am/frontend/applet_web_browser.cpp @@ -0,0 +1,505 @@ +// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "common/assert.h" +#include "common/fs/file.h" +#include "common/fs/fs.h" +#include "common/fs/path_util.h" +#include "common/logging/log.h" +#include "common/string_util.h" +#include "core/core.h" +#include "core/file_sys/content_archive.h" +#include "core/file_sys/fs_filesystem.h" +#include "core/file_sys/nca_metadata.h" +#include "core/file_sys/patch_manager.h" +#include "core/file_sys/registered_cache.h" +#include "core/file_sys/romfs.h" +#include "core/file_sys/system_archive/system_archive.h" +#include "core/file_sys/vfs/vfs_vector.h" +#include "core/frontend/applets/web_browser.h" +#include "core/hle/result.h" +#include "core/hle/service/am/am.h" +#include "core/hle/service/am/frontend/applet_web_browser.h" +#include "core/hle/service/am/storage.h" +#include "core/hle/service/filesystem/filesystem.h" +#include "core/hle/service/ns/iplatform_service_manager.h" +#include "core/loader/loader.h" + +namespace Service::AM::Frontend { + +namespace { + +template +void ParseRawValue(T& value, const std::vector& data) { + static_assert(std::is_trivially_copyable_v, + "It's undefined behavior to use memcpy with non-trivially copyable objects"); + std::memcpy(&value, data.data(), data.size()); +} + +template +T ParseRawValue(const std::vector& data) { + T value; + ParseRawValue(value, data); + return value; +} + +std::string ParseStringValue(const std::vector& data) { + return Common::StringFromFixedZeroTerminatedBuffer(reinterpret_cast(data.data()), + data.size()); +} + +std::string GetMainURL(const std::string& url) { + const auto index = url.find('?'); + + if (index == std::string::npos) { + return url; + } + + return url.substr(0, index); +} + +std::string ResolveURL(const std::string& url) { + const auto index = url.find_first_of('%'); + + if (index == std::string::npos) { + return url; + } + + return url.substr(0, index) + "lp1" + url.substr(index + 1); +} + +WebArgInputTLVMap ReadWebArgs(const std::vector& web_arg, WebArgHeader& web_arg_header) { + std::memcpy(&web_arg_header, web_arg.data(), sizeof(WebArgHeader)); + + if (web_arg.size() == sizeof(WebArgHeader)) { + return {}; + } + + WebArgInputTLVMap input_tlv_map; + + u64 current_offset = sizeof(WebArgHeader); + + for (std::size_t i = 0; i < web_arg_header.total_tlv_entries; ++i) { + if (web_arg.size() < current_offset + sizeof(WebArgInputTLV)) { + return input_tlv_map; + } + + WebArgInputTLV input_tlv; + std::memcpy(&input_tlv, web_arg.data() + current_offset, sizeof(WebArgInputTLV)); + + current_offset += sizeof(WebArgInputTLV); + + if (web_arg.size() < current_offset + input_tlv.arg_data_size) { + return input_tlv_map; + } + + std::vector data(input_tlv.arg_data_size); + std::memcpy(data.data(), web_arg.data() + current_offset, input_tlv.arg_data_size); + + current_offset += input_tlv.arg_data_size; + + input_tlv_map.insert_or_assign(input_tlv.input_tlv_type, std::move(data)); + } + + return input_tlv_map; +} + +FileSys::VirtualFile GetOfflineRomFS(Core::System& system, u64 title_id, + FileSys::ContentRecordType nca_type) { + if (nca_type == FileSys::ContentRecordType::Data) { + const auto nca = + system.GetFileSystemController().GetSystemNANDContents()->GetEntry(title_id, nca_type); + + if (nca == nullptr) { + LOG_ERROR(Service_AM, + "NCA of type={} with title_id={:016X} is not found in the System NAND!", + nca_type, title_id); + return FileSys::SystemArchive::SynthesizeSystemArchive(title_id); + } + + return nca->GetRomFS(); + } else { + const auto nca = system.GetContentProvider().GetEntry(title_id, nca_type); + + if (nca == nullptr) { + if (nca_type == FileSys::ContentRecordType::HtmlDocument) { + LOG_WARNING(Service_AM, "Falling back to AppLoader to get the RomFS."); + FileSys::VirtualFile romfs; + system.GetAppLoader().ReadManualRomFS(romfs); + if (romfs != nullptr) { + return romfs; + } + } + + LOG_ERROR(Service_AM, + "NCA of type={} with title_id={:016X} is not found in the ContentProvider!", + nca_type, title_id); + return nullptr; + } + + const FileSys::PatchManager pm{title_id, system.GetFileSystemController(), + system.GetContentProvider()}; + + return pm.PatchRomFS(nca.get(), nca->GetRomFS(), nca_type); + } +} + +void ExtractSharedFonts(Core::System& system) { + static constexpr std::array DECRYPTED_SHARED_FONTS{ + "FontStandard.ttf", + "FontChineseSimplified.ttf", + "FontExtendedChineseSimplified.ttf", + "FontChineseTraditional.ttf", + "FontKorean.ttf", + "FontNintendoExtended.ttf", + "FontNintendoExtended2.ttf", + }; + + const auto fonts_dir = Common::FS::GetYuzuPath(Common::FS::YuzuPath::CacheDir) / "fonts"; + + for (std::size_t i = 0; i < NS::SHARED_FONTS.size(); ++i) { + const auto font_file_path = fonts_dir / DECRYPTED_SHARED_FONTS[i]; + + if (Common::FS::Exists(font_file_path)) { + continue; + } + + const auto font = NS::SHARED_FONTS[i]; + const auto font_title_id = static_cast(font.first); + + const auto nca = system.GetFileSystemController().GetSystemNANDContents()->GetEntry( + font_title_id, FileSys::ContentRecordType::Data); + + FileSys::VirtualFile romfs; + + if (!nca) { + romfs = FileSys::SystemArchive::SynthesizeSystemArchive(font_title_id); + } else { + romfs = nca->GetRomFS(); + } + + if (!romfs) { + LOG_ERROR(Service_AM, "SharedFont RomFS with title_id={:016X} cannot be extracted!", + font_title_id); + continue; + } + + const auto extracted_romfs = FileSys::ExtractRomFS(romfs); + + if (!extracted_romfs) { + LOG_ERROR(Service_AM, "SharedFont RomFS with title_id={:016X} failed to extract!", + font_title_id); + continue; + } + + const auto font_file = extracted_romfs->GetFile(font.second); + + if (!font_file) { + LOG_ERROR(Service_AM, "SharedFont RomFS with title_id={:016X} has no font file \"{}\"!", + font_title_id, font.second); + continue; + } + + std::vector font_data_u32(font_file->GetSize() / sizeof(u32)); + font_file->ReadBytes(font_data_u32.data(), font_file->GetSize()); + + std::transform(font_data_u32.begin(), font_data_u32.end(), font_data_u32.begin(), + Common::swap32); + + std::vector decrypted_data(font_file->GetSize() - 8); + + NS::DecryptSharedFontToTTF(font_data_u32, decrypted_data); + + FileSys::VirtualFile decrypted_font = std::make_shared( + std::move(decrypted_data), DECRYPTED_SHARED_FONTS[i]); + + const auto temp_dir = system.GetFilesystem()->CreateDirectory( + Common::FS::PathToUTF8String(fonts_dir), FileSys::OpenMode::ReadWrite); + + const auto out_file = temp_dir->CreateFile(DECRYPTED_SHARED_FONTS[i]); + + FileSys::VfsRawCopy(decrypted_font, out_file); + } +} + +} // namespace + +WebBrowser::WebBrowser(Core::System& system_, std::shared_ptr applet_, + LibraryAppletMode applet_mode_, + const Core::Frontend::WebBrowserApplet& frontend_) + : FrontendApplet{system_, applet_, applet_mode_}, frontend(frontend_) {} + +WebBrowser::~WebBrowser() = default; + +void WebBrowser::Initialize() { + FrontendApplet::Initialize(); + + LOG_INFO(Service_AM, "Initializing Web Browser Applet."); + + LOG_DEBUG(Service_AM, + "Initializing Applet with common_args: arg_version={}, lib_version={}, " + "play_startup_sound={}, size={}, system_tick={}, theme_color={}", + common_args.arguments_version, common_args.library_version, + common_args.play_startup_sound, common_args.size, common_args.system_tick, + common_args.theme_color); + + web_applet_version = WebAppletVersion{common_args.library_version}; + + const auto web_arg_storage = PopInData(); + ASSERT(web_arg_storage != nullptr); + + const auto& web_arg = web_arg_storage->GetData(); + ASSERT_OR_EXECUTE(web_arg.size() >= sizeof(WebArgHeader), { return; }); + + web_arg_input_tlv_map = ReadWebArgs(web_arg, web_arg_header); + + LOG_DEBUG(Service_AM, "WebArgHeader: total_tlv_entries={}, shim_kind={}", + web_arg_header.total_tlv_entries, web_arg_header.shim_kind); + + ExtractSharedFonts(system); + + switch (web_arg_header.shim_kind) { + case ShimKind::Shop: + InitializeShop(); + break; + case ShimKind::Login: + InitializeLogin(); + break; + case ShimKind::Offline: + InitializeOffline(); + break; + case ShimKind::Share: + InitializeShare(); + break; + case ShimKind::Web: + InitializeWeb(); + break; + case ShimKind::Wifi: + InitializeWifi(); + break; + case ShimKind::Lobby: + InitializeLobby(); + break; + default: + ASSERT_MSG(false, "Invalid ShimKind={}", web_arg_header.shim_kind); + break; + } +} + +Result WebBrowser::GetStatus() const { + return status; +} + +void WebBrowser::ExecuteInteractive() { + UNIMPLEMENTED_MSG("WebSession is not implemented"); +} + +void WebBrowser::Execute() { + switch (web_arg_header.shim_kind) { + case ShimKind::Shop: + ExecuteShop(); + break; + case ShimKind::Login: + ExecuteLogin(); + break; + case ShimKind::Offline: + ExecuteOffline(); + break; + case ShimKind::Share: + ExecuteShare(); + break; + case ShimKind::Web: + ExecuteWeb(); + break; + case ShimKind::Wifi: + ExecuteWifi(); + break; + case ShimKind::Lobby: + ExecuteLobby(); + break; + default: + ASSERT_MSG(false, "Invalid ShimKind={}", web_arg_header.shim_kind); + WebBrowserExit(WebExitReason::EndButtonPressed); + break; + } +} + +void WebBrowser::ExtractOfflineRomFS() { + LOG_DEBUG(Service_AM, "Extracting RomFS to {}", + Common::FS::PathToUTF8String(offline_cache_dir)); + + const auto extracted_romfs_dir = FileSys::ExtractRomFS(offline_romfs); + + const auto temp_dir = system.GetFilesystem()->CreateDirectory( + Common::FS::PathToUTF8String(offline_cache_dir), FileSys::OpenMode::ReadWrite); + + FileSys::VfsRawCopyD(extracted_romfs_dir, temp_dir); +} + +void WebBrowser::WebBrowserExit(WebExitReason exit_reason, std::string last_url) { + if ((web_arg_header.shim_kind == ShimKind::Share && + web_applet_version >= WebAppletVersion::Version196608) || + (web_arg_header.shim_kind == ShimKind::Web && + web_applet_version >= WebAppletVersion::Version524288)) { + // TODO: Push Output TLVs instead of a WebCommonReturnValue + } + + WebCommonReturnValue web_common_return_value; + + web_common_return_value.exit_reason = exit_reason; + std::memcpy(&web_common_return_value.last_url, last_url.data(), last_url.size()); + web_common_return_value.last_url_size = last_url.size(); + + LOG_DEBUG(Service_AM, "WebCommonReturnValue: exit_reason={}, last_url={}, last_url_size={}", + exit_reason, last_url, last_url.size()); + + complete = true; + std::vector out_data(sizeof(WebCommonReturnValue)); + std::memcpy(out_data.data(), &web_common_return_value, out_data.size()); + PushOutData(std::make_shared(system, std::move(out_data))); + Exit(); +} + +Result WebBrowser::RequestExit() { + frontend.Close(); + R_SUCCEED(); +} + +bool WebBrowser::InputTLVExistsInMap(WebArgInputTLVType input_tlv_type) const { + return web_arg_input_tlv_map.find(input_tlv_type) != web_arg_input_tlv_map.end(); +} + +std::optional> WebBrowser::GetInputTLVData(WebArgInputTLVType input_tlv_type) { + const auto map_it = web_arg_input_tlv_map.find(input_tlv_type); + + if (map_it == web_arg_input_tlv_map.end()) { + return std::nullopt; + } + + return map_it->second; +} + +void WebBrowser::InitializeShop() {} + +void WebBrowser::InitializeLogin() {} + +void WebBrowser::InitializeOffline() { + const auto document_path = + ParseStringValue(GetInputTLVData(WebArgInputTLVType::DocumentPath).value()); + + const auto document_kind = + ParseRawValue(GetInputTLVData(WebArgInputTLVType::DocumentKind).value()); + + std::string additional_paths; + + switch (document_kind) { + case DocumentKind::OfflineHtmlPage: + default: + title_id = system.GetApplicationProcessProgramID(); + nca_type = FileSys::ContentRecordType::HtmlDocument; + additional_paths = "html-document"; + break; + case DocumentKind::ApplicationLegalInformation: + title_id = ParseRawValue(GetInputTLVData(WebArgInputTLVType::ApplicationID).value()); + nca_type = FileSys::ContentRecordType::LegalInformation; + break; + case DocumentKind::SystemDataPage: + title_id = ParseRawValue(GetInputTLVData(WebArgInputTLVType::SystemDataID).value()); + nca_type = FileSys::ContentRecordType::Data; + break; + } + + static constexpr std::array RESOURCE_TYPES{ + "manual", + "legal_information", + "system_data", + }; + + offline_cache_dir = Common::FS::GetYuzuPath(Common::FS::YuzuPath::CacheDir) / + fmt::format("offline_web_applet_{}/{:016X}", + RESOURCE_TYPES[static_cast(document_kind) - 1], title_id); + + offline_document = Common::FS::ConcatPathSafe( + offline_cache_dir, fmt::format("{}/{}", additional_paths, document_path)); +} + +void WebBrowser::InitializeShare() {} + +void WebBrowser::InitializeWeb() { + external_url = ParseStringValue(GetInputTLVData(WebArgInputTLVType::InitialURL).value()); + + // Resolve Nintendo CDN URLs. + external_url = ResolveURL(external_url); +} + +void WebBrowser::InitializeWifi() {} + +void WebBrowser::InitializeLobby() {} + +void WebBrowser::ExecuteShop() { + LOG_WARNING(Service_AM, "(STUBBED) called, Shop Applet is not implemented"); + WebBrowserExit(WebExitReason::EndButtonPressed); +} + +void WebBrowser::ExecuteLogin() { + LOG_WARNING(Service_AM, "(STUBBED) called, Login Applet is not implemented"); + WebBrowserExit(WebExitReason::EndButtonPressed); +} + +void WebBrowser::ExecuteOffline() { + // TODO (Morph): This is a hack for WebSession foreground web applets such as those used by + // Super Mario 3D All-Stars. + // TODO (Morph): Implement WebSession. + if (applet_mode == LibraryAppletMode::AllForegroundInitiallyHidden) { + LOG_WARNING(Service_AM, "WebSession is not implemented"); + return; + } + + const auto main_url = GetMainURL(Common::FS::PathToUTF8String(offline_document)); + + if (!Common::FS::Exists(main_url)) { + offline_romfs = GetOfflineRomFS(system, title_id, nca_type); + + if (offline_romfs == nullptr) { + LOG_ERROR(Service_AM, + "RomFS with title_id={:016X} and nca_type={} cannot be extracted!", title_id, + nca_type); + WebBrowserExit(WebExitReason::WindowClosed); + return; + } + } + + LOG_INFO(Service_AM, "Opening offline document at {}", + Common::FS::PathToUTF8String(offline_document)); + + frontend.OpenLocalWebPage( + Common::FS::PathToUTF8String(offline_document), [this] { ExtractOfflineRomFS(); }, + [this](WebExitReason exit_reason, std::string last_url) { + WebBrowserExit(exit_reason, last_url); + }); +} + +void WebBrowser::ExecuteShare() { + LOG_WARNING(Service_AM, "(STUBBED) called, Share Applet is not implemented"); + WebBrowserExit(WebExitReason::EndButtonPressed); +} + +void WebBrowser::ExecuteWeb() { + LOG_INFO(Service_AM, "Opening external URL at {}", external_url); + + frontend.OpenExternalWebPage(external_url, + [this](WebExitReason exit_reason, std::string last_url) { + WebBrowserExit(exit_reason, last_url); + }); +} + +void WebBrowser::ExecuteWifi() { + LOG_WARNING(Service_AM, "(STUBBED) called, Wifi Applet is not implemented"); + WebBrowserExit(WebExitReason::EndButtonPressed); +} + +void WebBrowser::ExecuteLobby() { + LOG_WARNING(Service_AM, "(STUBBED) called, Lobby Applet is not implemented"); + WebBrowserExit(WebExitReason::EndButtonPressed); +} +} // namespace Service::AM::Frontend diff --git a/src/core/hle/service/am/frontend/applet_web_browser.h b/src/core/hle/service/am/frontend/applet_web_browser.h new file mode 100755 index 000000000..ba20b7a4c --- /dev/null +++ b/src/core/hle/service/am/frontend/applet_web_browser.h @@ -0,0 +1,84 @@ +// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include +#include + +#include "common/common_types.h" +#include "core/file_sys/vfs/vfs_types.h" +#include "core/hle/result.h" +#include "core/hle/service/am/frontend/applet_web_browser_types.h" +#include "core/hle/service/am/frontend/applets.h" + +namespace Core { +class System; +} + +namespace FileSys { +enum class ContentRecordType : u8; +} + +namespace Service::AM::Frontend { + +class WebBrowser final : public FrontendApplet { +public: + WebBrowser(Core::System& system_, std::shared_ptr applet_, + LibraryAppletMode applet_mode_, const Core::Frontend::WebBrowserApplet& frontend_); + + ~WebBrowser() override; + + void Initialize() override; + + Result GetStatus() const override; + void ExecuteInteractive() override; + void Execute() override; + Result RequestExit() override; + + void ExtractOfflineRomFS(); + + void WebBrowserExit(WebExitReason exit_reason, std::string last_url = ""); + +private: + bool InputTLVExistsInMap(WebArgInputTLVType input_tlv_type) const; + + std::optional> GetInputTLVData(WebArgInputTLVType input_tlv_type); + + // Initializers for the various types of browser applets + void InitializeShop(); + void InitializeLogin(); + void InitializeOffline(); + void InitializeShare(); + void InitializeWeb(); + void InitializeWifi(); + void InitializeLobby(); + + // Executors for the various types of browser applets + void ExecuteShop(); + void ExecuteLogin(); + void ExecuteOffline(); + void ExecuteShare(); + void ExecuteWeb(); + void ExecuteWifi(); + void ExecuteLobby(); + + const Core::Frontend::WebBrowserApplet& frontend; + + bool complete{false}; + Result status{ResultSuccess}; + + WebAppletVersion web_applet_version{}; + WebArgHeader web_arg_header{}; + WebArgInputTLVMap web_arg_input_tlv_map; + + u64 title_id{}; + FileSys::ContentRecordType nca_type{}; + std::filesystem::path offline_cache_dir; + std::filesystem::path offline_document; + FileSys::VirtualFile offline_romfs; + + std::string external_url; +}; + +} // namespace Service::AM::Frontend diff --git a/src/core/hle/service/am/frontend/applet_web_browser_types.h b/src/core/hle/service/am/frontend/applet_web_browser_types.h new file mode 100755 index 000000000..2f7c05c24 --- /dev/null +++ b/src/core/hle/service/am/frontend/applet_web_browser_types.h @@ -0,0 +1,177 @@ +// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include +#include +#include + +#include "common/common_funcs.h" +#include "common/common_types.h" +#include "common/swap.h" + +namespace Service::AM::Frontend { + +enum class WebAppletVersion : u32_le { + Version0 = 0x0, // Only used by WifiWebAuthApplet + Version131072 = 0x20000, // 1.0.0 - 2.3.0 + Version196608 = 0x30000, // 3.0.0 - 4.1.0 + Version327680 = 0x50000, // 5.0.0 - 5.1.0 + Version393216 = 0x60000, // 6.0.0 - 7.0.1 + Version524288 = 0x80000, // 8.0.0+ +}; + +enum class ShimKind : u32 { + Shop = 1, + Login = 2, + Offline = 3, + Share = 4, + Web = 5, + Wifi = 6, + Lobby = 7, +}; + +enum class WebExitReason : u32 { + EndButtonPressed = 0, + BackButtonPressed = 1, + ExitRequested = 2, + CallbackURL = 3, + WindowClosed = 4, + ErrorDialog = 7, +}; + +enum class WebArgInputTLVType : u16 { + InitialURL = 0x1, + CallbackURL = 0x3, + CallbackableURL = 0x4, + ApplicationID = 0x5, + DocumentPath = 0x6, + DocumentKind = 0x7, + SystemDataID = 0x8, + ShareStartPage = 0x9, + Whitelist = 0xA, + News = 0xB, + UserID = 0xE, + AlbumEntry0 = 0xF, + ScreenShotEnabled = 0x10, + EcClientCertEnabled = 0x11, + PlayReportEnabled = 0x13, + BootDisplayKind = 0x17, + BackgroundKind = 0x18, + FooterEnabled = 0x19, + PointerEnabled = 0x1A, + LeftStickMode = 0x1B, + KeyRepeatFrame1 = 0x1C, + KeyRepeatFrame2 = 0x1D, + BootAsMediaPlayerInverted = 0x1E, + DisplayURLKind = 0x1F, + BootAsMediaPlayer = 0x21, + ShopJumpEnabled = 0x22, + MediaAutoPlayEnabled = 0x23, + LobbyParameter = 0x24, + ApplicationAlbumEntry = 0x26, + JsExtensionEnabled = 0x27, + AdditionalCommentText = 0x28, + TouchEnabledOnContents = 0x29, + UserAgentAdditionalString = 0x2A, + AdditionalMediaData0 = 0x2B, + MediaPlayerAutoCloseEnabled = 0x2C, + PageCacheEnabled = 0x2D, + WebAudioEnabled = 0x2E, + YouTubeVideoWhitelist = 0x31, + FooterFixedKind = 0x32, + PageFadeEnabled = 0x33, + MediaCreatorApplicationRatingAge = 0x34, + BootLoadingIconEnabled = 0x35, + PageScrollIndicatorEnabled = 0x36, + MediaPlayerSpeedControlEnabled = 0x37, + AlbumEntry1 = 0x38, + AlbumEntry2 = 0x39, + AlbumEntry3 = 0x3A, + AdditionalMediaData1 = 0x3B, + AdditionalMediaData2 = 0x3C, + AdditionalMediaData3 = 0x3D, + BootFooterButton = 0x3E, + OverrideWebAudioVolume = 0x3F, + OverrideMediaAudioVolume = 0x40, + BootMode = 0x41, + WebSessionEnabled = 0x42, + MediaPlayerOfflineEnabled = 0x43, +}; + +enum class WebArgOutputTLVType : u16 { + ShareExitReason = 0x1, + LastURL = 0x2, + LastURLSize = 0x3, + SharePostResult = 0x4, + PostServiceName = 0x5, + PostServiceNameSize = 0x6, + PostID = 0x7, + PostIDSize = 0x8, + MediaPlayerAutoClosedByCompletion = 0x9, +}; + +enum class DocumentKind : u32 { + OfflineHtmlPage = 1, + ApplicationLegalInformation = 2, + SystemDataPage = 3, +}; + +enum class ShareStartPage : u32 { + Default, + Settings, +}; + +enum class BootDisplayKind : u32 { + Default, + White, + Black, +}; + +enum class BackgroundKind : u32 { + Default, +}; + +enum class LeftStickMode : u32 { + Pointer, + Cursor, +}; + +enum class WebSessionBootMode : u32 { + AllForeground, + AllForegroundInitiallyHidden, +}; + +struct WebArgHeader { + u16 total_tlv_entries{}; + INSERT_PADDING_BYTES(2); + ShimKind shim_kind{}; +}; +static_assert(sizeof(WebArgHeader) == 0x8, "WebArgHeader has incorrect size."); + +struct WebArgInputTLV { + WebArgInputTLVType input_tlv_type{}; + u16 arg_data_size{}; + INSERT_PADDING_WORDS(1); +}; +static_assert(sizeof(WebArgInputTLV) == 0x8, "WebArgInputTLV has incorrect size."); + +struct WebArgOutputTLV { + WebArgOutputTLVType output_tlv_type{}; + u16 arg_data_size{}; + INSERT_PADDING_WORDS(1); +}; +static_assert(sizeof(WebArgOutputTLV) == 0x8, "WebArgOutputTLV has incorrect size."); + +struct WebCommonReturnValue { + WebExitReason exit_reason{}; + INSERT_PADDING_WORDS(1); + std::array last_url{}; + u64 last_url_size{}; +}; +static_assert(sizeof(WebCommonReturnValue) == 0x1010, "WebCommonReturnValue has incorrect size."); + +using WebArgInputTLVMap = std::unordered_map>; + +} // namespace Service::AM::Frontend diff --git a/src/core/hle/service/am/frontend/applets.cpp b/src/core/hle/service/am/frontend/applets.cpp new file mode 100755 index 000000000..db2b04575 --- /dev/null +++ b/src/core/hle/service/am/frontend/applets.cpp @@ -0,0 +1,240 @@ +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include + +#include "common/assert.h" +#include "core/core.h" +#include "core/frontend/applets/cabinet.h" +#include "core/frontend/applets/controller.h" +#include "core/frontend/applets/error.h" +#include "core/frontend/applets/general.h" +#include "core/frontend/applets/mii_edit.h" +#include "core/frontend/applets/profile_select.h" +#include "core/frontend/applets/software_keyboard.h" +#include "core/frontend/applets/web_browser.h" +#include "core/hle/kernel/k_event.h" +#include "core/hle/service/am/am.h" +#include "core/hle/service/am/applet_ae.h" +#include "core/hle/service/am/applet_data_broker.h" +#include "core/hle/service/am/applet_manager.h" +#include "core/hle/service/am/applet_message_queue.h" +#include "core/hle/service/am/applet_oe.h" +#include "core/hle/service/am/frontend/applet_cabinet.h" +#include "core/hle/service/am/frontend/applet_controller.h" +#include "core/hle/service/am/frontend/applet_error.h" +#include "core/hle/service/am/frontend/applet_general.h" +#include "core/hle/service/am/frontend/applet_mii_edit.h" +#include "core/hle/service/am/frontend/applet_profile_select.h" +#include "core/hle/service/am/frontend/applet_software_keyboard.h" +#include "core/hle/service/am/frontend/applet_web_browser.h" +#include "core/hle/service/am/frontend/applets.h" +#include "core/hle/service/am/storage.h" +#include "core/hle/service/sm/sm.h" + +namespace Service::AM::Frontend { + +FrontendApplet::FrontendApplet(Core::System& system_, std::shared_ptr applet_, + LibraryAppletMode applet_mode_) + : system{system_}, applet{std::move(applet_)}, applet_mode{applet_mode_} {} + +FrontendApplet::~FrontendApplet() = default; + +void FrontendApplet::Initialize() { + std::shared_ptr common = PopInData(); + ASSERT(common != nullptr); + const auto common_data = common->GetData(); + + ASSERT(common_data.size() >= sizeof(CommonArguments)); + std::memcpy(&common_args, common_data.data(), sizeof(CommonArguments)); + + initialized = true; +} + +std::shared_ptr FrontendApplet::PopInData() { + std::shared_ptr ret; + applet.lock()->caller_applet_broker->GetInData().Pop(&ret); + return ret; +} + +std::shared_ptr FrontendApplet::PopInteractiveInData() { + std::shared_ptr ret; + applet.lock()->caller_applet_broker->GetInteractiveInData().Pop(&ret); + return ret; +} + +void FrontendApplet::PushOutData(std::shared_ptr storage) { + applet.lock()->caller_applet_broker->GetOutData().Push(storage); +} + +void FrontendApplet::PushInteractiveOutData(std::shared_ptr storage) { + applet.lock()->caller_applet_broker->GetInteractiveOutData().Push(storage); +} + +void FrontendApplet::Exit() { + applet.lock()->caller_applet_broker->SignalCompletion(); +} + +FrontendAppletSet::FrontendAppletSet() = default; + +FrontendAppletSet::FrontendAppletSet(CabinetApplet cabinet_applet, + ControllerApplet controller_applet, ErrorApplet error_applet, + MiiEdit mii_edit_, + ParentalControlsApplet parental_controls_applet, + PhotoViewer photo_viewer_, ProfileSelect profile_select_, + SoftwareKeyboard software_keyboard_, WebBrowser web_browser_) + : cabinet{std::move(cabinet_applet)}, controller{std::move(controller_applet)}, + error{std::move(error_applet)}, mii_edit{std::move(mii_edit_)}, + parental_controls{std::move(parental_controls_applet)}, + photo_viewer{std::move(photo_viewer_)}, profile_select{std::move(profile_select_)}, + software_keyboard{std::move(software_keyboard_)}, web_browser{std::move(web_browser_)} {} + +FrontendAppletSet::~FrontendAppletSet() = default; + +FrontendAppletSet::FrontendAppletSet(FrontendAppletSet&&) noexcept = default; + +FrontendAppletSet& FrontendAppletSet::operator=(FrontendAppletSet&&) noexcept = default; + +FrontendAppletHolder::FrontendAppletHolder(Core::System& system_) : system{system_} {} + +FrontendAppletHolder::~FrontendAppletHolder() = default; + +const FrontendAppletSet& FrontendAppletHolder::GetFrontendAppletSet() const { + return frontend; +} + +NFP::CabinetMode FrontendAppletHolder::GetCabinetMode() const { + return cabinet_mode; +} + +AppletId FrontendAppletHolder::GetCurrentAppletId() const { + return current_applet_id; +} + +void FrontendAppletHolder::SetFrontendAppletSet(FrontendAppletSet set) { + if (set.cabinet != nullptr) { + frontend.cabinet = std::move(set.cabinet); + } + + if (set.controller != nullptr) { + frontend.controller = std::move(set.controller); + } + + if (set.error != nullptr) { + frontend.error = std::move(set.error); + } + + if (set.mii_edit != nullptr) { + frontend.mii_edit = std::move(set.mii_edit); + } + + if (set.parental_controls != nullptr) { + frontend.parental_controls = std::move(set.parental_controls); + } + + if (set.photo_viewer != nullptr) { + frontend.photo_viewer = std::move(set.photo_viewer); + } + + if (set.profile_select != nullptr) { + frontend.profile_select = std::move(set.profile_select); + } + + if (set.software_keyboard != nullptr) { + frontend.software_keyboard = std::move(set.software_keyboard); + } + + if (set.web_browser != nullptr) { + frontend.web_browser = std::move(set.web_browser); + } +} + +void FrontendAppletHolder::SetCabinetMode(NFP::CabinetMode mode) { + cabinet_mode = mode; +} + +void FrontendAppletHolder::SetCurrentAppletId(AppletId applet_id) { + current_applet_id = applet_id; +} + +void FrontendAppletHolder::SetDefaultAppletsIfMissing() { + if (frontend.cabinet == nullptr) { + frontend.cabinet = std::make_unique(); + } + + if (frontend.controller == nullptr) { + frontend.controller = + std::make_unique(system.HIDCore()); + } + + if (frontend.error == nullptr) { + frontend.error = std::make_unique(); + } + + if (frontend.mii_edit == nullptr) { + frontend.mii_edit = std::make_unique(); + } + + if (frontend.parental_controls == nullptr) { + frontend.parental_controls = + std::make_unique(); + } + + if (frontend.photo_viewer == nullptr) { + frontend.photo_viewer = std::make_unique(); + } + + if (frontend.profile_select == nullptr) { + frontend.profile_select = std::make_unique(); + } + + if (frontend.software_keyboard == nullptr) { + frontend.software_keyboard = + std::make_unique(); + } + + if (frontend.web_browser == nullptr) { + frontend.web_browser = std::make_unique(); + } +} + +void FrontendAppletHolder::ClearAll() { + frontend = {}; +} + +std::shared_ptr FrontendAppletHolder::GetApplet(std::shared_ptr applet, + AppletId id, + LibraryAppletMode mode) const { + switch (id) { + case AppletId::Auth: + return std::make_shared(system, applet, mode, *frontend.parental_controls); + case AppletId::Cabinet: + return std::make_shared(system, applet, mode, *frontend.cabinet); + case AppletId::Controller: + return std::make_shared(system, applet, mode, *frontend.controller); + case AppletId::Error: + return std::make_shared(system, applet, mode, *frontend.error); + case AppletId::ProfileSelect: + return std::make_shared(system, applet, mode, *frontend.profile_select); + case AppletId::SoftwareKeyboard: + return std::make_shared(system, applet, mode, + *frontend.software_keyboard); + case AppletId::MiiEdit: + return std::make_shared(system, applet, mode, *frontend.mii_edit); + case AppletId::Web: + case AppletId::Shop: + case AppletId::OfflineWeb: + case AppletId::LoginShare: + case AppletId::WebAuth: + return std::make_shared(system, applet, mode, *frontend.web_browser); + case AppletId::PhotoViewer: + return std::make_shared(system, applet, mode, *frontend.photo_viewer); + default: + UNIMPLEMENTED_MSG( + "No backend implementation exists for applet_id={:02X}! Falling back to stub applet.", + static_cast(id)); + return std::make_shared(system, applet, id, mode); + } +} + +} // namespace Service::AM::Frontend diff --git a/src/core/hle/service/am/frontend/applets.h b/src/core/hle/service/am/frontend/applets.h new file mode 100755 index 000000000..1e1fd28b8 --- /dev/null +++ b/src/core/hle/service/am/frontend/applets.h @@ -0,0 +1,146 @@ +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include +#include + +#include "common/swap.h" +#include "core/hle/service/am/applet.h" + +union Result; + +namespace Core { +class System; +} + +namespace Core::Frontend { +class CabinetApplet; +class ControllerApplet; +class ECommerceApplet; +class ErrorApplet; +class MiiEditApplet; +class ParentalControlsApplet; +class PhotoViewerApplet; +class ProfileSelectApplet; +class SoftwareKeyboardApplet; +class WebBrowserApplet; +} // namespace Core::Frontend + +namespace Kernel { +class KernelCore; +class KEvent; +class KReadableEvent; +} // namespace Kernel + +namespace Service::NFP { +enum class CabinetMode : u8; +} // namespace Service::NFP + +namespace Service::AM { + +class IStorage; + +namespace Frontend { + +class FrontendApplet { +public: + explicit FrontendApplet(Core::System& system_, std::shared_ptr applet_, + LibraryAppletMode applet_mode_); + virtual ~FrontendApplet(); + + virtual void Initialize(); + + virtual Result GetStatus() const = 0; + virtual void ExecuteInteractive() = 0; + virtual void Execute() = 0; + virtual Result RequestExit() = 0; + + LibraryAppletMode GetLibraryAppletMode() const { + return applet_mode; + } + + bool IsInitialized() const { + return initialized; + } + +protected: + std::shared_ptr PopInData(); + std::shared_ptr PopInteractiveInData(); + void PushOutData(std::shared_ptr storage); + void PushInteractiveOutData(std::shared_ptr storage); + void Exit(); + +protected: + Core::System& system; + CommonArguments common_args{}; + std::weak_ptr applet{}; + LibraryAppletMode applet_mode{}; + bool initialized{false}; +}; + +struct FrontendAppletSet { + using CabinetApplet = std::unique_ptr; + using ControllerApplet = std::unique_ptr; + using ErrorApplet = std::unique_ptr; + using MiiEdit = std::unique_ptr; + using ParentalControlsApplet = std::unique_ptr; + using PhotoViewer = std::unique_ptr; + using ProfileSelect = std::unique_ptr; + using SoftwareKeyboard = std::unique_ptr; + using WebBrowser = std::unique_ptr; + + FrontendAppletSet(); + FrontendAppletSet(CabinetApplet cabinet_applet, ControllerApplet controller_applet, + ErrorApplet error_applet, MiiEdit mii_edit_, + ParentalControlsApplet parental_controls_applet, PhotoViewer photo_viewer_, + ProfileSelect profile_select_, SoftwareKeyboard software_keyboard_, + WebBrowser web_browser_); + ~FrontendAppletSet(); + + FrontendAppletSet(const FrontendAppletSet&) = delete; + FrontendAppletSet& operator=(const FrontendAppletSet&) = delete; + + FrontendAppletSet(FrontendAppletSet&&) noexcept; + FrontendAppletSet& operator=(FrontendAppletSet&&) noexcept; + + CabinetApplet cabinet; + ControllerApplet controller; + ErrorApplet error; + MiiEdit mii_edit; + ParentalControlsApplet parental_controls; + PhotoViewer photo_viewer; + ProfileSelect profile_select; + SoftwareKeyboard software_keyboard; + WebBrowser web_browser; +}; + +class FrontendAppletHolder { +public: + explicit FrontendAppletHolder(Core::System& system_); + ~FrontendAppletHolder(); + + const FrontendAppletSet& GetFrontendAppletSet() const; + NFP::CabinetMode GetCabinetMode() const; + AppletId GetCurrentAppletId() const; + + void SetFrontendAppletSet(FrontendAppletSet set); + void SetCabinetMode(NFP::CabinetMode mode); + void SetCurrentAppletId(AppletId applet_id); + void SetDefaultAppletsIfMissing(); + void ClearAll(); + + std::shared_ptr GetApplet(std::shared_ptr applet, AppletId id, + LibraryAppletMode mode) const; + +private: + AppletId current_applet_id{}; + NFP::CabinetMode cabinet_mode{}; + + FrontendAppletSet frontend; + Core::System& system; +}; + +} // namespace Frontend +} // namespace Service::AM diff --git a/src/core/hle/service/am/global_state_controller.cpp b/src/core/hle/service/am/global_state_controller.cpp new file mode 100755 index 000000000..ed0eb7108 --- /dev/null +++ b/src/core/hle/service/am/global_state_controller.cpp @@ -0,0 +1,34 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/hle/service/am/global_state_controller.h" +#include "core/hle/service/ipc_helpers.h" + +namespace Service::AM { + +IGlobalStateController::IGlobalStateController(Core::System& system_) + : ServiceFramework{system_, "IGlobalStateController"} { + // clang-format off + static const FunctionInfo functions[] = { + {0, nullptr, "RequestToEnterSleep"}, + {1, nullptr, "EnterSleep"}, + {2, nullptr, "StartSleepSequence"}, + {3, nullptr, "StartShutdownSequence"}, + {4, nullptr, "StartRebootSequence"}, + {9, nullptr, "IsAutoPowerDownRequested"}, + {10, nullptr, "LoadAndApplyIdlePolicySettings"}, + {11, nullptr, "NotifyCecSettingsChanged"}, + {12, nullptr, "SetDefaultHomeButtonLongPressTime"}, + {13, nullptr, "UpdateDefaultDisplayResolution"}, + {14, nullptr, "ShouldSleepOnBoot"}, + {15, nullptr, "GetHdcpAuthenticationFailedEvent"}, + {30, nullptr, "OpenCradleFirmwareUpdater"}, + }; + // clang-format on + + RegisterHandlers(functions); +} + +IGlobalStateController::~IGlobalStateController() = default; + +} // namespace Service::AM diff --git a/src/core/hle/service/am/global_state_controller.h b/src/core/hle/service/am/global_state_controller.h new file mode 100755 index 000000000..7125464a1 --- /dev/null +++ b/src/core/hle/service/am/global_state_controller.h @@ -0,0 +1,16 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "core/hle/service/service.h" + +namespace Service::AM { + +class IGlobalStateController final : public ServiceFramework { +public: + explicit IGlobalStateController(Core::System& system_); + ~IGlobalStateController() override; +}; + +} // namespace Service::AM diff --git a/src/core/hle/service/am/hid_registration.cpp b/src/core/hle/service/am/hid_registration.cpp new file mode 100755 index 000000000..8ed49bac1 --- /dev/null +++ b/src/core/hle/service/am/hid_registration.cpp @@ -0,0 +1,35 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/core.h" +#include "core/hle/service/am/hid_registration.h" +#include "core/hle/service/am/process.h" +#include "core/hle/service/hid/hid_server.h" +#include "core/hle/service/sm/sm.h" +#include "hid_core/resource_manager.h" + +namespace Service::AM { + +HidRegistration::HidRegistration(Core::System& system, Process& process) : m_process(process) { + m_hid_server = system.ServiceManager().GetService("hid"); + + if (m_process.IsInitialized()) { + m_hid_server->GetResourceManager()->RegisterAppletResourceUserId(m_process.GetProcessId(), + true); + } +} + +HidRegistration::~HidRegistration() { + if (m_process.IsInitialized()) { + m_hid_server->GetResourceManager()->UnregisterAppletResourceUserId( + m_process.GetProcessId()); + } +} + +void HidRegistration::EnableAppletToGetInput(bool enable) { + if (m_process.IsInitialized()) { + m_hid_server->GetResourceManager()->EnableInput(m_process.GetProcessId(), enable); + } +} + +} // namespace Service::AM diff --git a/src/core/hle/service/am/hid_registration.h b/src/core/hle/service/am/hid_registration.h new file mode 100755 index 000000000..67cd84961 --- /dev/null +++ b/src/core/hle/service/am/hid_registration.h @@ -0,0 +1,32 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include + +namespace Core { +class System; +} + +namespace Service::HID { +class IHidServer; +} + +namespace Service::AM { + +class Process; + +class HidRegistration { +public: + explicit HidRegistration(Core::System& system, Process& process); + ~HidRegistration(); + + void EnableAppletToGetInput(bool enable); + +private: + Process& m_process; + std::shared_ptr m_hid_server; +}; + +} // namespace Service::AM diff --git a/src/core/hle/service/am/home_menu_functions.cpp b/src/core/hle/service/am/home_menu_functions.cpp new file mode 100755 index 000000000..640e9fbb7 --- /dev/null +++ b/src/core/hle/service/am/home_menu_functions.cpp @@ -0,0 +1,57 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/hle/service/am/home_menu_functions.h" +#include "core/hle/service/ipc_helpers.h" + +namespace Service::AM { + +IHomeMenuFunctions::IHomeMenuFunctions(Core::System& system_) + : ServiceFramework{system_, "IHomeMenuFunctions"}, service_context{system, + "IHomeMenuFunctions"} { + // clang-format off + static const FunctionInfo functions[] = { + {10, &IHomeMenuFunctions::RequestToGetForeground, "RequestToGetForeground"}, + {11, nullptr, "LockForeground"}, + {12, nullptr, "UnlockForeground"}, + {20, nullptr, "PopFromGeneralChannel"}, + {21, &IHomeMenuFunctions::GetPopFromGeneralChannelEvent, "GetPopFromGeneralChannelEvent"}, + {30, nullptr, "GetHomeButtonWriterLockAccessor"}, + {31, nullptr, "GetWriterLockAccessorEx"}, + {40, nullptr, "IsSleepEnabled"}, + {41, nullptr, "IsRebootEnabled"}, + {50, nullptr, "LaunchSystemApplet"}, + {51, nullptr, "LaunchStarter"}, + {100, nullptr, "PopRequestLaunchApplicationForDebug"}, + {110, nullptr, "IsForceTerminateApplicationDisabledForDebug"}, + {200, nullptr, "LaunchDevMenu"}, + {1000, nullptr, "SetLastApplicationExitReason"}, + }; + // clang-format on + + RegisterHandlers(functions); + + pop_from_general_channel_event = + service_context.CreateEvent("IHomeMenuFunctions:PopFromGeneralChannelEvent"); +} + +IHomeMenuFunctions::~IHomeMenuFunctions() { + service_context.CloseEvent(pop_from_general_channel_event); +} + +void IHomeMenuFunctions::RequestToGetForeground(HLERequestContext& ctx) { + LOG_WARNING(Service_AM, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void IHomeMenuFunctions::GetPopFromGeneralChannelEvent(HLERequestContext& ctx) { + LOG_WARNING(Service_AM, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 2, 1}; + rb.Push(ResultSuccess); + rb.PushCopyObjects(pop_from_general_channel_event->GetReadableEvent()); +} + +} // namespace Service::AM diff --git a/src/core/hle/service/am/home_menu_functions.h b/src/core/hle/service/am/home_menu_functions.h new file mode 100755 index 000000000..e082d5d73 --- /dev/null +++ b/src/core/hle/service/am/home_menu_functions.h @@ -0,0 +1,25 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "core/hle/service/kernel_helpers.h" +#include "core/hle/service/service.h" + +namespace Service::AM { + +class IHomeMenuFunctions final : public ServiceFramework { +public: + explicit IHomeMenuFunctions(Core::System& system_); + ~IHomeMenuFunctions() override; + +private: + void RequestToGetForeground(HLERequestContext& ctx); + void GetPopFromGeneralChannelEvent(HLERequestContext& ctx); + + KernelHelpers::ServiceContext service_context; + + Kernel::KEvent* pop_from_general_channel_event; +}; + +} // namespace Service::AM diff --git a/src/core/hle/service/am/library_applet_accessor.cpp b/src/core/hle/service/am/library_applet_accessor.cpp new file mode 100755 index 000000000..6b20814f8 --- /dev/null +++ b/src/core/hle/service/am/library_applet_accessor.cpp @@ -0,0 +1,202 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "common/scope_exit.h" +#include "core/hle/service/am/am_results.h" +#include "core/hle/service/am/applet_data_broker.h" +#include "core/hle/service/am/frontend/applets.h" +#include "core/hle/service/am/library_applet_accessor.h" +#include "core/hle/service/am/storage.h" +#include "core/hle/service/ipc_helpers.h" + +namespace Service::AM { + +ILibraryAppletAccessor::ILibraryAppletAccessor(Core::System& system_, + std::shared_ptr broker_, + std::shared_ptr applet_) + : ServiceFramework{system_, "ILibraryAppletAccessor"}, broker{std::move(broker_)}, + applet{std::move(applet_)} { + // clang-format off + static const FunctionInfo functions[] = { + {0, &ILibraryAppletAccessor::GetAppletStateChangedEvent, "GetAppletStateChangedEvent"}, + {1, &ILibraryAppletAccessor::IsCompleted, "IsCompleted"}, + {10, &ILibraryAppletAccessor::Start, "Start"}, + {20, &ILibraryAppletAccessor::RequestExit, "RequestExit"}, + {25, nullptr, "Terminate"}, + {30, &ILibraryAppletAccessor::GetResult, "GetResult"}, + {50, nullptr, "SetOutOfFocusApplicationSuspendingEnabled"}, + {60, &ILibraryAppletAccessor::PresetLibraryAppletGpuTimeSliceZero, "PresetLibraryAppletGpuTimeSliceZero"}, + {100, &ILibraryAppletAccessor::PushInData, "PushInData"}, + {101, &ILibraryAppletAccessor::PopOutData, "PopOutData"}, + {102, nullptr, "PushExtraStorage"}, + {103, &ILibraryAppletAccessor::PushInteractiveInData, "PushInteractiveInData"}, + {104, &ILibraryAppletAccessor::PopInteractiveOutData, "PopInteractiveOutData"}, + {105, &ILibraryAppletAccessor::GetPopOutDataEvent, "GetPopOutDataEvent"}, + {106, &ILibraryAppletAccessor::GetPopInteractiveOutDataEvent, "GetPopInteractiveOutDataEvent"}, + {110, nullptr, "NeedsToExitProcess"}, + {120, nullptr, "GetLibraryAppletInfo"}, + {150, nullptr, "RequestForAppletToGetForeground"}, + {160, &ILibraryAppletAccessor::GetIndirectLayerConsumerHandle, "GetIndirectLayerConsumerHandle"}, + }; + // clang-format on + + RegisterHandlers(functions); +} + +ILibraryAppletAccessor::~ILibraryAppletAccessor() = default; + +void ILibraryAppletAccessor::GetAppletStateChangedEvent(HLERequestContext& ctx) { + LOG_DEBUG(Service_AM, "called"); + + IPC::ResponseBuilder rb{ctx, 2, 1}; + rb.Push(ResultSuccess); + rb.PushCopyObjects(broker->GetStateChangedEvent().GetHandle()); +} + +void ILibraryAppletAccessor::IsCompleted(HLERequestContext& ctx) { + LOG_DEBUG(Service_AM, "called"); + + std::scoped_lock lk{applet->lock}; + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.Push(broker->IsCompleted()); +} + +void ILibraryAppletAccessor::GetResult(HLERequestContext& ctx) { + LOG_DEBUG(Service_AM, "called"); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(applet->terminate_result); +} + +void ILibraryAppletAccessor::PresetLibraryAppletGpuTimeSliceZero(HLERequestContext& ctx) { + LOG_WARNING(Service_AM, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void ILibraryAppletAccessor::Start(HLERequestContext& ctx) { + LOG_DEBUG(Service_AM, "called"); + + applet->process->Run(); + FrontendExecute(); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void ILibraryAppletAccessor::RequestExit(HLERequestContext& ctx) { + LOG_DEBUG(Service_AM, "called"); + + ASSERT(applet != nullptr); + applet->message_queue.RequestExit(); + FrontendRequestExit(); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void ILibraryAppletAccessor::PushInData(HLERequestContext& ctx) { + LOG_DEBUG(Service_AM, "called"); + + IPC::RequestParser rp{ctx}; + broker->GetInData().Push(rp.PopIpcInterface().lock()); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void ILibraryAppletAccessor::PopOutData(HLERequestContext& ctx) { + LOG_DEBUG(Service_AM, "called"); + + std::shared_ptr data; + const auto res = broker->GetOutData().Pop(&data); + + if (res.IsSuccess()) { + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(res); + rb.PushIpcInterface(std::move(data)); + } else { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(res); + } +} + +void ILibraryAppletAccessor::PushInteractiveInData(HLERequestContext& ctx) { + LOG_DEBUG(Service_AM, "called"); + + IPC::RequestParser rp{ctx}; + broker->GetInteractiveInData().Push(rp.PopIpcInterface().lock()); + FrontendExecuteInteractive(); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void ILibraryAppletAccessor::PopInteractiveOutData(HLERequestContext& ctx) { + LOG_DEBUG(Service_AM, "called"); + + std::shared_ptr data; + const auto res = broker->GetInteractiveOutData().Pop(&data); + + if (res.IsSuccess()) { + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(res); + rb.PushIpcInterface(std::move(data)); + } else { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(res); + } +} + +void ILibraryAppletAccessor::GetPopOutDataEvent(HLERequestContext& ctx) { + LOG_DEBUG(Service_AM, "called"); + + IPC::ResponseBuilder rb{ctx, 2, 1}; + rb.Push(ResultSuccess); + rb.PushCopyObjects(broker->GetOutData().GetEvent()); +} + +void ILibraryAppletAccessor::GetPopInteractiveOutDataEvent(HLERequestContext& ctx) { + LOG_DEBUG(Service_AM, "called"); + + IPC::ResponseBuilder rb{ctx, 2, 1}; + rb.Push(ResultSuccess); + rb.PushCopyObjects(broker->GetInteractiveOutData().GetEvent()); +} + +void ILibraryAppletAccessor::GetIndirectLayerConsumerHandle(HLERequestContext& ctx) { + LOG_WARNING(Service_AM, "(STUBBED) called"); + + // We require a non-zero handle to be valid. Using 0xdeadbeef allows us to trace if this is + // actually used anywhere + constexpr u64 handle = 0xdeadbeef; + + IPC::ResponseBuilder rb{ctx, 4}; + rb.Push(ResultSuccess); + rb.Push(handle); +} + +void ILibraryAppletAccessor::FrontendExecute() { + if (applet->frontend) { + applet->frontend->Initialize(); + applet->frontend->Execute(); + } +} + +void ILibraryAppletAccessor::FrontendExecuteInteractive() { + if (applet->frontend) { + applet->frontend->ExecuteInteractive(); + applet->frontend->Execute(); + } +} + +void ILibraryAppletAccessor::FrontendRequestExit() { + if (applet->frontend) { + applet->frontend->RequestExit(); + } +} + +} // namespace Service::AM diff --git a/src/core/hle/service/am/library_applet_accessor.h b/src/core/hle/service/am/library_applet_accessor.h new file mode 100755 index 000000000..8be29e003 --- /dev/null +++ b/src/core/hle/service/am/library_applet_accessor.h @@ -0,0 +1,43 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "core/hle/service/service.h" + +namespace Service::AM { + +class AppletDataBroker; +struct Applet; + +class ILibraryAppletAccessor final : public ServiceFramework { +public: + explicit ILibraryAppletAccessor(Core::System& system_, + std::shared_ptr broker_, + std::shared_ptr applet_); + ~ILibraryAppletAccessor(); + +protected: + void GetAppletStateChangedEvent(HLERequestContext& ctx); + void IsCompleted(HLERequestContext& ctx); + void GetResult(HLERequestContext& ctx); + void PresetLibraryAppletGpuTimeSliceZero(HLERequestContext& ctx); + void Start(HLERequestContext& ctx); + void RequestExit(HLERequestContext& ctx); + void PushInData(HLERequestContext& ctx); + void PopOutData(HLERequestContext& ctx); + void PushInteractiveInData(HLERequestContext& ctx); + void PopInteractiveOutData(HLERequestContext& ctx); + void GetPopOutDataEvent(HLERequestContext& ctx); + void GetPopInteractiveOutDataEvent(HLERequestContext& ctx); + void GetIndirectLayerConsumerHandle(HLERequestContext& ctx); + + void FrontendExecute(); + void FrontendExecuteInteractive(); + void FrontendRequestExit(); + + const std::shared_ptr broker; + const std::shared_ptr applet; +}; + +} // namespace Service::AM diff --git a/src/core/hle/service/am/library_applet_creator.cpp b/src/core/hle/service/am/library_applet_creator.cpp new file mode 100755 index 000000000..5c3569125 --- /dev/null +++ b/src/core/hle/service/am/library_applet_creator.cpp @@ -0,0 +1,274 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/hle/kernel/k_transfer_memory.h" +#include "core/hle/service/am/applet_data_broker.h" +#include "core/hle/service/am/applet_manager.h" +#include "core/hle/service/am/frontend/applets.h" +#include "core/hle/service/am/library_applet_accessor.h" +#include "core/hle/service/am/library_applet_creator.h" +#include "core/hle/service/am/library_applet_storage.h" +#include "core/hle/service/am/storage.h" +#include "core/hle/service/ipc_helpers.h" +#include "core/hle/service/sm/sm.h" + +namespace Service::AM { + +namespace { + +AppletProgramId AppletIdToProgramId(AppletId applet_id) { + switch (applet_id) { + case AppletId::OverlayDisplay: + return AppletProgramId::OverlayDisplay; + case AppletId::QLaunch: + return AppletProgramId::QLaunch; + case AppletId::Starter: + return AppletProgramId::Starter; + case AppletId::Auth: + return AppletProgramId::Auth; + case AppletId::Cabinet: + return AppletProgramId::Cabinet; + case AppletId::Controller: + return AppletProgramId::Controller; + case AppletId::DataErase: + return AppletProgramId::DataErase; + case AppletId::Error: + return AppletProgramId::Error; + case AppletId::NetConnect: + return AppletProgramId::NetConnect; + case AppletId::ProfileSelect: + return AppletProgramId::ProfileSelect; + case AppletId::SoftwareKeyboard: + return AppletProgramId::SoftwareKeyboard; + case AppletId::MiiEdit: + return AppletProgramId::MiiEdit; + case AppletId::Web: + return AppletProgramId::Web; + case AppletId::Shop: + return AppletProgramId::Shop; + case AppletId::PhotoViewer: + return AppletProgramId::PhotoViewer; + case AppletId::Settings: + return AppletProgramId::Settings; + case AppletId::OfflineWeb: + return AppletProgramId::OfflineWeb; + case AppletId::LoginShare: + return AppletProgramId::LoginShare; + case AppletId::WebAuth: + return AppletProgramId::WebAuth; + case AppletId::MyPage: + return AppletProgramId::MyPage; + default: + return static_cast(0); + } +} + +[[maybe_unused]] std::shared_ptr CreateGuestApplet( + Core::System& system, std::shared_ptr caller_applet, AppletId applet_id, + LibraryAppletMode mode) { + const auto program_id = static_cast(AppletIdToProgramId(applet_id)); + if (program_id == 0) { + // Unknown applet + return {}; + } + + auto process = std::make_unique(system); + if (!process->Initialize(program_id)) { + // Couldn't initialize the guest process + return {}; + } + + const auto applet = std::make_shared(system, std::move(process)); + applet->program_id = program_id; + applet->applet_id = applet_id; + applet->type = AppletType::LibraryApplet; + applet->library_applet_mode = mode; + + // Set focus state + applet->message_queue.PushMessage(AppletMessageQueue::AppletMessage::FocusStateChanged); + switch (mode) { + case LibraryAppletMode::AllForeground: + case LibraryAppletMode::NoUI: + applet->message_queue.PushMessage(AppletMessageQueue::AppletMessage::ChangeIntoForeground); + applet->focus_state = FocusState::InFocus; + applet->hid_registration.EnableAppletToGetInput(true); + break; + case LibraryAppletMode::AllForegroundInitiallyHidden: + applet->system_buffer_manager.SetWindowVisibility(false); + applet->focus_state = FocusState::NotInFocus; + applet->hid_registration.EnableAppletToGetInput(false); + break; + case LibraryAppletMode::Background: + case LibraryAppletMode::BackgroundIndirectDisplay: + default: + applet->focus_state = FocusState::Background; + applet->hid_registration.EnableAppletToGetInput(true); + break; + } + + auto broker = std::make_shared(system); + applet->caller_applet = caller_applet; + applet->caller_applet_broker = broker; + + system.GetAppletManager().InsertApplet(applet); + + return std::make_shared(system, broker, applet); +} + +std::shared_ptr CreateFrontendApplet(Core::System& system, + std::shared_ptr caller_applet, + AppletId applet_id, + LibraryAppletMode mode) { + const auto program_id = static_cast(AppletIdToProgramId(applet_id)); + if (program_id == 0) { + // Unknown applet + return {}; + } + + auto process = std::make_unique(system); + auto applet = std::make_shared(system, std::move(process)); + applet->program_id = program_id; + applet->applet_id = applet_id; + applet->type = AppletType::LibraryApplet; + applet->library_applet_mode = mode; + + auto storage = std::make_shared(system); + applet->caller_applet = caller_applet; + applet->caller_applet_broker = storage; + applet->frontend = system.GetFrontendAppletHolder().GetApplet(applet, applet_id, mode); + + return std::make_shared(system, storage, applet); +} + +} // namespace + +ILibraryAppletCreator::ILibraryAppletCreator(Core::System& system_, std::shared_ptr applet_) + : ServiceFramework{system_, "ILibraryAppletCreator"}, applet{std::move(applet_)} { + static const FunctionInfo functions[] = { + {0, &ILibraryAppletCreator::CreateLibraryApplet, "CreateLibraryApplet"}, + {1, nullptr, "TerminateAllLibraryApplets"}, + {2, nullptr, "AreAnyLibraryAppletsLeft"}, + {10, &ILibraryAppletCreator::CreateStorage, "CreateStorage"}, + {11, &ILibraryAppletCreator::CreateTransferMemoryStorage, "CreateTransferMemoryStorage"}, + {12, &ILibraryAppletCreator::CreateHandleStorage, "CreateHandleStorage"}, + }; + RegisterHandlers(functions); +} + +ILibraryAppletCreator::~ILibraryAppletCreator() = default; + +void ILibraryAppletCreator::CreateLibraryApplet(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + + const auto applet_id = rp.PopRaw(); + const auto applet_mode = rp.PopRaw(); + + LOG_DEBUG(Service_AM, "called with applet_id={:08X}, applet_mode={:08X}", applet_id, + applet_mode); + + auto library_applet = CreateFrontendApplet(system, applet, applet_id, applet_mode); + if (!library_applet) { + LOG_ERROR(Service_AM, "Applet doesn't exist! applet_id={}", applet_id); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultUnknown); + return; + } + + // Applet is created, can now be launched. + applet->library_applet_launchable_event.Signal(); + + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(ResultSuccess); + rb.PushIpcInterface(library_applet); +} + +void ILibraryAppletCreator::CreateStorage(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + + const s64 size{rp.Pop()}; + + LOG_DEBUG(Service_AM, "called, size={}", size); + + if (size <= 0) { + LOG_ERROR(Service_AM, "size is less than or equal to 0"); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultUnknown); + return; + } + + std::vector data(size); + + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(ResultSuccess); + rb.PushIpcInterface(system, AM::CreateStorage(std::move(data))); +} + +void ILibraryAppletCreator::CreateTransferMemoryStorage(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + + struct Parameters { + bool is_writable; + s64 size; + }; + + const auto params{rp.PopRaw()}; + const auto handle{ctx.GetCopyHandle(0)}; + + LOG_DEBUG(Service_AM, "called, is_writable={}, size={}, handle={:08X}", params.is_writable, + params.size, handle); + + if (params.size <= 0) { + LOG_ERROR(Service_AM, "size is less than or equal to 0"); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultUnknown); + return; + } + + auto transfer_mem = ctx.GetObjectFromHandle(handle); + + if (transfer_mem.IsNull()) { + LOG_ERROR(Service_AM, "transfer_mem is a nullptr for handle={:08X}", handle); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultUnknown); + return; + } + + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(ResultSuccess); + rb.PushIpcInterface( + system, AM::CreateTransferMemoryStorage(ctx.GetMemory(), transfer_mem.GetPointerUnsafe(), + params.is_writable, params.size)); +} + +void ILibraryAppletCreator::CreateHandleStorage(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + + const s64 size{rp.Pop()}; + const auto handle{ctx.GetCopyHandle(0)}; + + LOG_DEBUG(Service_AM, "called, size={}, handle={:08X}", size, handle); + + if (size <= 0) { + LOG_ERROR(Service_AM, "size is less than or equal to 0"); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultUnknown); + return; + } + + auto transfer_mem = ctx.GetObjectFromHandle(handle); + + if (transfer_mem.IsNull()) { + LOG_ERROR(Service_AM, "transfer_mem is a nullptr for handle={:08X}", handle); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultUnknown); + return; + } + + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(ResultSuccess); + rb.PushIpcInterface( + system, AM::CreateHandleStorage(ctx.GetMemory(), transfer_mem.GetPointerUnsafe(), size)); +} + +} // namespace Service::AM diff --git a/src/core/hle/service/am/library_applet_creator.h b/src/core/hle/service/am/library_applet_creator.h new file mode 100755 index 000000000..551f287bd --- /dev/null +++ b/src/core/hle/service/am/library_applet_creator.h @@ -0,0 +1,26 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "core/hle/service/service.h" + +namespace Service::AM { + +struct Applet; + +class ILibraryAppletCreator final : public ServiceFramework { +public: + explicit ILibraryAppletCreator(Core::System& system_, std::shared_ptr applet_); + ~ILibraryAppletCreator() override; + +private: + void CreateLibraryApplet(HLERequestContext& ctx); + void CreateStorage(HLERequestContext& ctx); + void CreateTransferMemoryStorage(HLERequestContext& ctx); + void CreateHandleStorage(HLERequestContext& ctx); + + const std::shared_ptr applet; +}; + +} // namespace Service::AM diff --git a/src/core/hle/service/am/library_applet_proxy.cpp b/src/core/hle/service/am/library_applet_proxy.cpp new file mode 100755 index 000000000..d6108fba3 --- /dev/null +++ b/src/core/hle/service/am/library_applet_proxy.cpp @@ -0,0 +1,143 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/hle/service/am/applet_common_functions.h" +#include "core/hle/service/am/audio_controller.h" +#include "core/hle/service/am/common_state_getter.h" +#include "core/hle/service/am/debug_functions.h" +#include "core/hle/service/am/display_controller.h" +#include "core/hle/service/am/global_state_controller.h" +#include "core/hle/service/am/home_menu_functions.h" +#include "core/hle/service/am/library_applet_creator.h" +#include "core/hle/service/am/library_applet_proxy.h" +#include "core/hle/service/am/library_applet_self_accessor.h" +#include "core/hle/service/am/process_winding_controller.h" +#include "core/hle/service/am/self_controller.h" +#include "core/hle/service/am/window_controller.h" +#include "core/hle/service/ipc_helpers.h" + +namespace Service::AM { + +ILibraryAppletProxy::ILibraryAppletProxy(Nvnflinger::Nvnflinger& nvnflinger_, + std::shared_ptr applet_, Core::System& system_) + : ServiceFramework{system_, "ILibraryAppletProxy"}, nvnflinger{nvnflinger_}, applet{std::move( + applet_)} { + // clang-format off + static const FunctionInfo functions[] = { + {0, &ILibraryAppletProxy::GetCommonStateGetter, "GetCommonStateGetter"}, + {1, &ILibraryAppletProxy::GetSelfController, "GetSelfController"}, + {2, &ILibraryAppletProxy::GetWindowController, "GetWindowController"}, + {3, &ILibraryAppletProxy::GetAudioController, "GetAudioController"}, + {4, &ILibraryAppletProxy::GetDisplayController, "GetDisplayController"}, + {10, &ILibraryAppletProxy::GetProcessWindingController, "GetProcessWindingController"}, + {11, &ILibraryAppletProxy::GetLibraryAppletCreator, "GetLibraryAppletCreator"}, + {20, &ILibraryAppletProxy::OpenLibraryAppletSelfAccessor, "OpenLibraryAppletSelfAccessor"}, + {21, &ILibraryAppletProxy::GetAppletCommonFunctions, "GetAppletCommonFunctions"}, + {22, &ILibraryAppletProxy::GetHomeMenuFunctions, "GetHomeMenuFunctions"}, + {23, &ILibraryAppletProxy::GetGlobalStateController, "GetGlobalStateController"}, + {1000, &ILibraryAppletProxy::GetDebugFunctions, "GetDebugFunctions"}, + }; + // clang-format on + + RegisterHandlers(functions); +} + +ILibraryAppletProxy::~ILibraryAppletProxy() = default; + +void ILibraryAppletProxy::GetCommonStateGetter(HLERequestContext& ctx) { + LOG_DEBUG(Service_AM, "called"); + + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(ResultSuccess); + rb.PushIpcInterface(system, applet); +} + +void ILibraryAppletProxy::GetSelfController(HLERequestContext& ctx) { + LOG_DEBUG(Service_AM, "called"); + + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(ResultSuccess); + rb.PushIpcInterface(system, applet, nvnflinger); +} + +void ILibraryAppletProxy::GetWindowController(HLERequestContext& ctx) { + LOG_DEBUG(Service_AM, "called"); + + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(ResultSuccess); + rb.PushIpcInterface(system, applet); +} + +void ILibraryAppletProxy::GetAudioController(HLERequestContext& ctx) { + LOG_DEBUG(Service_AM, "called"); + + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(ResultSuccess); + rb.PushIpcInterface(system); +} + +void ILibraryAppletProxy::GetDisplayController(HLERequestContext& ctx) { + LOG_DEBUG(Service_AM, "called"); + + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(ResultSuccess); + rb.PushIpcInterface(system, applet); +} + +void ILibraryAppletProxy::GetProcessWindingController(HLERequestContext& ctx) { + LOG_DEBUG(Service_AM, "called"); + + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(ResultSuccess); + rb.PushIpcInterface(system, applet); +} + +void ILibraryAppletProxy::GetLibraryAppletCreator(HLERequestContext& ctx) { + LOG_DEBUG(Service_AM, "called"); + + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(ResultSuccess); + rb.PushIpcInterface(system, applet); +} + +void ILibraryAppletProxy::OpenLibraryAppletSelfAccessor(HLERequestContext& ctx) { + LOG_DEBUG(Service_AM, "called"); + + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(ResultSuccess); + rb.PushIpcInterface(system, applet); +} + +void ILibraryAppletProxy::GetAppletCommonFunctions(HLERequestContext& ctx) { + LOG_DEBUG(Service_AM, "called"); + + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(ResultSuccess); + rb.PushIpcInterface(system, applet); +} + +void ILibraryAppletProxy::GetHomeMenuFunctions(HLERequestContext& ctx) { + LOG_DEBUG(Service_AM, "called"); + + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(ResultSuccess); + rb.PushIpcInterface(system); +} + +void ILibraryAppletProxy::GetGlobalStateController(HLERequestContext& ctx) { + LOG_DEBUG(Service_AM, "called"); + + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(ResultSuccess); + rb.PushIpcInterface(system); +} + +void ILibraryAppletProxy::GetDebugFunctions(HLERequestContext& ctx) { + LOG_DEBUG(Service_AM, "called"); + + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(ResultSuccess); + rb.PushIpcInterface(system); +} + +} // namespace Service::AM diff --git a/src/core/hle/service/am/library_applet_proxy.h b/src/core/hle/service/am/library_applet_proxy.h new file mode 100755 index 000000000..8f7a25897 --- /dev/null +++ b/src/core/hle/service/am/library_applet_proxy.h @@ -0,0 +1,36 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "core/hle/service/service.h" + +namespace Service::AM { + +struct Applet; + +class ILibraryAppletProxy final : public ServiceFramework { +public: + explicit ILibraryAppletProxy(Nvnflinger::Nvnflinger& nvnflinger_, + std::shared_ptr applet_, Core::System& system_); + ~ILibraryAppletProxy(); + +private: + void GetCommonStateGetter(HLERequestContext& ctx); + void GetSelfController(HLERequestContext& ctx); + void GetWindowController(HLERequestContext& ctx); + void GetAudioController(HLERequestContext& ctx); + void GetDisplayController(HLERequestContext& ctx); + void GetProcessWindingController(HLERequestContext& ctx); + void GetLibraryAppletCreator(HLERequestContext& ctx); + void OpenLibraryAppletSelfAccessor(HLERequestContext& ctx); + void GetAppletCommonFunctions(HLERequestContext& ctx); + void GetHomeMenuFunctions(HLERequestContext& ctx); + void GetGlobalStateController(HLERequestContext& ctx); + void GetDebugFunctions(HLERequestContext& ctx); + + Nvnflinger::Nvnflinger& nvnflinger; + std::shared_ptr applet; +}; + +} // namespace Service::AM diff --git a/src/core/hle/service/am/library_applet_self_accessor.cpp b/src/core/hle/service/am/library_applet_self_accessor.cpp new file mode 100755 index 000000000..b560f580b --- /dev/null +++ b/src/core/hle/service/am/library_applet_self_accessor.cpp @@ -0,0 +1,338 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "common/scope_exit.h" +#include "core/core_timing.h" +#include "core/file_sys/control_metadata.h" +#include "core/file_sys/patch_manager.h" +#include "core/file_sys/registered_cache.h" +#include "core/hle/service/acc/profile_manager.h" +#include "core/hle/service/am/am_results.h" +#include "core/hle/service/am/applet_data_broker.h" +#include "core/hle/service/am/applet_manager.h" +#include "core/hle/service/am/frontend/applet_cabinet.h" +#include "core/hle/service/am/frontend/applet_controller.h" +#include "core/hle/service/am/frontend/applet_mii_edit_types.h" +#include "core/hle/service/am/frontend/applet_software_keyboard_types.h" +#include "core/hle/service/am/frontend/applets.h" +#include "core/hle/service/am/library_applet_self_accessor.h" +#include "core/hle/service/am/storage.h" +#include "core/hle/service/ipc_helpers.h" +#include "core/hle/service/ns/ns.h" +#include "core/hle/service/sm/sm.h" +#include "hid_core/hid_types.h" + +namespace Service::AM { + +namespace { + +AppletIdentityInfo GetCallerIdentity(std::shared_ptr applet) { + if (const auto caller_applet = applet->caller_applet.lock(); caller_applet) { + // TODO: is this actually the application ID? + return { + .applet_id = caller_applet->applet_id, + .application_id = caller_applet->program_id, + }; + } else { + return { + .applet_id = AppletId::QLaunch, + .application_id = 0x0100000000001000ull, + }; + } +} + +} // namespace + +ILibraryAppletSelfAccessor::ILibraryAppletSelfAccessor(Core::System& system_, + std::shared_ptr applet_) + : ServiceFramework{system_, "ILibraryAppletSelfAccessor"}, applet{std::move(applet_)}, + broker{applet->caller_applet_broker} { + // clang-format off + static const FunctionInfo functions[] = { + {0, &ILibraryAppletSelfAccessor::PopInData, "PopInData"}, + {1, &ILibraryAppletSelfAccessor::PushOutData, "PushOutData"}, + {2, &ILibraryAppletSelfAccessor::PopInteractiveInData, "PopInteractiveInData"}, + {3, &ILibraryAppletSelfAccessor::PushInteractiveOutData, "PushInteractiveOutData"}, + {5, &ILibraryAppletSelfAccessor::GetPopInDataEvent, "GetPopInDataEvent"}, + {6, &ILibraryAppletSelfAccessor::GetPopInteractiveInDataEvent, "GetPopInteractiveInDataEvent"}, + {10, &ILibraryAppletSelfAccessor::ExitProcessAndReturn, "ExitProcessAndReturn"}, + {11, &ILibraryAppletSelfAccessor::GetLibraryAppletInfo, "GetLibraryAppletInfo"}, + {12, &ILibraryAppletSelfAccessor::GetMainAppletIdentityInfo, "GetMainAppletIdentityInfo"}, + {13, &ILibraryAppletSelfAccessor::CanUseApplicationCore, "CanUseApplicationCore"}, + {14, &ILibraryAppletSelfAccessor::GetCallerAppletIdentityInfo, "GetCallerAppletIdentityInfo"}, + {15, nullptr, "GetMainAppletApplicationControlProperty"}, + {16, nullptr, "GetMainAppletStorageId"}, + {17, nullptr, "GetCallerAppletIdentityInfoStack"}, + {18, nullptr, "GetNextReturnDestinationAppletIdentityInfo"}, + {19, &ILibraryAppletSelfAccessor::GetDesirableKeyboardLayout, "GetDesirableKeyboardLayout"}, + {20, nullptr, "PopExtraStorage"}, + {25, nullptr, "GetPopExtraStorageEvent"}, + {30, nullptr, "UnpopInData"}, + {31, nullptr, "UnpopExtraStorage"}, + {40, nullptr, "GetIndirectLayerProducerHandle"}, + {50, nullptr, "ReportVisibleError"}, + {51, nullptr, "ReportVisibleErrorWithErrorContext"}, + {60, &ILibraryAppletSelfAccessor::GetMainAppletApplicationDesiredLanguage, "GetMainAppletApplicationDesiredLanguage"}, + {70, &ILibraryAppletSelfAccessor::GetCurrentApplicationId, "GetCurrentApplicationId"}, + {80, nullptr, "RequestExitToSelf"}, + {90, nullptr, "CreateApplicationAndPushAndRequestToLaunch"}, + {100, nullptr, "CreateGameMovieTrimmer"}, + {101, nullptr, "ReserveResourceForMovieOperation"}, + {102, nullptr, "UnreserveResourceForMovieOperation"}, + {110, &ILibraryAppletSelfAccessor::GetMainAppletAvailableUsers, "GetMainAppletAvailableUsers"}, + {120, nullptr, "GetLaunchStorageInfoForDebug"}, + {130, nullptr, "GetGpuErrorDetectedSystemEvent"}, + {140, nullptr, "SetApplicationMemoryReservation"}, + {150, &ILibraryAppletSelfAccessor::ShouldSetGpuTimeSliceManually, "ShouldSetGpuTimeSliceManually"}, + {160, &ILibraryAppletSelfAccessor::Cmd160, "Cmd160"}, + }; + // clang-format on + RegisterHandlers(functions); +} + +ILibraryAppletSelfAccessor::~ILibraryAppletSelfAccessor() = default; + +void ILibraryAppletSelfAccessor::PopInData(HLERequestContext& ctx) { + LOG_INFO(Service_AM, "called"); + + std::shared_ptr data; + const auto res = broker->GetInData().Pop(&data); + + if (res.IsSuccess()) { + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(res); + rb.PushIpcInterface(std::move(data)); + } else { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(res); + } +} + +void ILibraryAppletSelfAccessor::PushOutData(HLERequestContext& ctx) { + LOG_INFO(Service_AM, "called"); + + IPC::RequestParser rp{ctx}; + broker->GetOutData().Push(rp.PopIpcInterface().lock()); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void ILibraryAppletSelfAccessor::PopInteractiveInData(HLERequestContext& ctx) { + LOG_INFO(Service_AM, "called"); + + std::shared_ptr data; + const auto res = broker->GetInteractiveInData().Pop(&data); + + if (res.IsSuccess()) { + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(res); + rb.PushIpcInterface(std::move(data)); + } else { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(res); + } +} + +void ILibraryAppletSelfAccessor::PushInteractiveOutData(HLERequestContext& ctx) { + LOG_INFO(Service_AM, "called"); + + IPC::RequestParser rp{ctx}; + broker->GetInteractiveOutData().Push(rp.PopIpcInterface().lock()); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void ILibraryAppletSelfAccessor::GetPopInDataEvent(HLERequestContext& ctx) { + LOG_INFO(Service_AM, "called"); + + IPC::ResponseBuilder rb{ctx, 2, 1}; + rb.Push(ResultSuccess); + rb.PushCopyObjects(broker->GetInData().GetEvent()); +} + +void ILibraryAppletSelfAccessor::GetPopInteractiveInDataEvent(HLERequestContext& ctx) { + LOG_INFO(Service_AM, "called"); + + IPC::ResponseBuilder rb{ctx, 2, 1}; + rb.Push(ResultSuccess); + rb.PushCopyObjects(broker->GetInteractiveInData().GetEvent()); +} + +void ILibraryAppletSelfAccessor::ExitProcessAndReturn(HLERequestContext& ctx) { + LOG_INFO(Service_AM, "called"); + + system.GetAppletManager().TerminateAndRemoveApplet(applet->aruid); + broker->SignalCompletion(); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void ILibraryAppletSelfAccessor::GetLibraryAppletInfo(HLERequestContext& ctx) { + struct LibraryAppletInfo { + AppletId applet_id; + LibraryAppletMode library_applet_mode; + }; + + LOG_WARNING(Service_AM, "(STUBBED) called"); + + const LibraryAppletInfo applet_info{ + .applet_id = applet->applet_id, + .library_applet_mode = applet->library_applet_mode, + }; + + IPC::ResponseBuilder rb{ctx, 4}; + rb.Push(ResultSuccess); + rb.PushRaw(applet_info); +} + +void ILibraryAppletSelfAccessor::GetMainAppletIdentityInfo(HLERequestContext& ctx) { + LOG_WARNING(Service_AM, "(STUBBED) called"); + + const AppletIdentityInfo applet_info{ + .applet_id = AppletId::QLaunch, + .application_id = 0x0100000000001000ull, + }; + + IPC::ResponseBuilder rb{ctx, 6}; + rb.Push(ResultSuccess); + rb.PushRaw(applet_info); +} + +void ILibraryAppletSelfAccessor::CanUseApplicationCore(HLERequestContext& ctx) { + LOG_WARNING(Service_AM, "(STUBBED) called"); + + // TODO: This appears to read the NPDM from state and check the core mask of the applet. + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.Push(0); +} + +void ILibraryAppletSelfAccessor::GetCallerAppletIdentityInfo(HLERequestContext& ctx) { + LOG_WARNING(Service_AM, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 6}; + rb.Push(ResultSuccess); + rb.PushRaw(GetCallerIdentity(applet)); +} + +void ILibraryAppletSelfAccessor::GetDesirableKeyboardLayout(HLERequestContext& ctx) { + LOG_WARNING(Service_AM, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.Push(0); +} + +void ILibraryAppletSelfAccessor::GetMainAppletApplicationDesiredLanguage(HLERequestContext& ctx) { + // FIXME: this is copied from IApplicationFunctions::GetDesiredLanguage + auto identity = GetCallerIdentity(applet); + + // TODO(bunnei): This should be configurable + LOG_DEBUG(Service_AM, "called"); + + // Get supported languages from NACP, if possible + // Default to 0 (all languages supported) + u32 supported_languages = 0; + + const auto res = [this, identity] { + const FileSys::PatchManager pm{identity.application_id, system.GetFileSystemController(), + system.GetContentProvider()}; + auto metadata = pm.GetControlMetadata(); + if (metadata.first != nullptr) { + return metadata; + } + + const FileSys::PatchManager pm_update{FileSys::GetUpdateTitleID(identity.application_id), + system.GetFileSystemController(), + system.GetContentProvider()}; + return pm_update.GetControlMetadata(); + }(); + + if (res.first != nullptr) { + supported_languages = res.first->GetSupportedLanguages(); + } + + // Call IApplicationManagerInterface implementation. + auto& service_manager = system.ServiceManager(); + auto ns_am2 = service_manager.GetService("ns:am2"); + auto app_man = ns_am2->GetApplicationManagerInterface(); + + // Get desired application language + u8 desired_language{}; + const auto res_lang = + app_man->GetApplicationDesiredLanguage(&desired_language, supported_languages); + if (res_lang != ResultSuccess) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(res_lang); + return; + } + + // Convert to settings language code. + u64 language_code{}; + const auto res_code = + app_man->ConvertApplicationLanguageToLanguageCode(&language_code, desired_language); + if (res_code != ResultSuccess) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(res_code); + return; + } + + LOG_DEBUG(Service_AM, "got desired_language={:016X}", language_code); + + IPC::ResponseBuilder rb{ctx, 4}; + rb.Push(ResultSuccess); + rb.Push(language_code); +} + +void ILibraryAppletSelfAccessor::GetCurrentApplicationId(HLERequestContext& ctx) { + LOG_WARNING(Service_AM, "(STUBBED) called"); + + u64 application_id = 0; + if (auto caller_applet = applet->caller_applet.lock(); caller_applet) { + application_id = caller_applet->program_id; + } + + IPC::ResponseBuilder rb{ctx, 4}; + rb.Push(ResultSuccess); + rb.Push(application_id); +} + +void ILibraryAppletSelfAccessor::GetMainAppletAvailableUsers(HLERequestContext& ctx) { + const Service::Account::ProfileManager manager{}; + bool is_empty{true}; + s32 user_count{-1}; + + LOG_INFO(Service_AM, "called"); + + if (manager.GetUserCount() > 0) { + is_empty = false; + user_count = static_cast(manager.GetUserCount()); + ctx.WriteBuffer(manager.GetAllUsers()); + } + + IPC::ResponseBuilder rb{ctx, 4}; + rb.Push(ResultSuccess); + rb.Push(is_empty); + rb.Push(user_count); +} + +void ILibraryAppletSelfAccessor::ShouldSetGpuTimeSliceManually(HLERequestContext& ctx) { + LOG_WARNING(Service_AM, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.Push(0); +} + +void ILibraryAppletSelfAccessor::Cmd160(HLERequestContext& ctx) { + LOG_WARNING(Service_AM, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 4}; + rb.Push(ResultSuccess); + rb.Push(0); +} + +} // namespace Service::AM diff --git a/src/core/hle/service/am/library_applet_self_accessor.h b/src/core/hle/service/am/library_applet_self_accessor.h new file mode 100755 index 000000000..8717a989a --- /dev/null +++ b/src/core/hle/service/am/library_applet_self_accessor.h @@ -0,0 +1,44 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include +#include + +#include "core/hle/service/service.h" + +namespace Service::AM { + +class AppletDataBroker; +struct Applet; + +class ILibraryAppletSelfAccessor final : public ServiceFramework { +public: + explicit ILibraryAppletSelfAccessor(Core::System& system_, std::shared_ptr applet_); + ~ILibraryAppletSelfAccessor() override; + +private: + void PopInData(HLERequestContext& ctx); + void PushOutData(HLERequestContext& ctx); + void PopInteractiveInData(HLERequestContext& ctx); + void PushInteractiveOutData(HLERequestContext& ctx); + void GetPopInDataEvent(HLERequestContext& ctx); + void GetPopInteractiveInDataEvent(HLERequestContext& ctx); + void GetLibraryAppletInfo(HLERequestContext& ctx); + void GetMainAppletIdentityInfo(HLERequestContext& ctx); + void CanUseApplicationCore(HLERequestContext& ctx); + void ExitProcessAndReturn(HLERequestContext& ctx); + void GetCallerAppletIdentityInfo(HLERequestContext& ctx); + void GetDesirableKeyboardLayout(HLERequestContext& ctx); + void GetMainAppletApplicationDesiredLanguage(HLERequestContext& ctx); + void GetCurrentApplicationId(HLERequestContext& ctx); + void GetMainAppletAvailableUsers(HLERequestContext& ctx); + void ShouldSetGpuTimeSliceManually(HLERequestContext& ctx); + void Cmd160(HLERequestContext& ctx); + + const std::shared_ptr applet; + const std::shared_ptr broker; +}; + +} // namespace Service::AM diff --git a/src/core/hle/service/am/library_applet_storage.cpp b/src/core/hle/service/am/library_applet_storage.cpp new file mode 100755 index 000000000..46e6c0111 --- /dev/null +++ b/src/core/hle/service/am/library_applet_storage.cpp @@ -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&& 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 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 LibraryAppletStorage::GetData() { + std::vector data(this->GetSize()); + this->Read(0, data.data(), data.size()); + return data; +} + +std::shared_ptr CreateStorage(std::vector&& data) { + return std::make_shared(std::move(data)); +} + +std::shared_ptr CreateTransferMemoryStorage(Core::Memory::Memory& memory, + Kernel::KTransferMemory* trmem, + bool is_writable, s64 size) { + return std::make_shared(memory, trmem, is_writable, size); +} + +std::shared_ptr CreateHandleStorage(Core::Memory::Memory& memory, + Kernel::KTransferMemory* trmem, + s64 size) { + return std::make_shared(memory, trmem, size); +} + +} // namespace Service::AM diff --git a/src/core/hle/service/am/library_applet_storage.h b/src/core/hle/service/am/library_applet_storage.h new file mode 100755 index 000000000..7f53f3a9c --- /dev/null +++ b/src/core/hle/service/am/library_applet_storage.h @@ -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 GetData(); +}; + +std::shared_ptr CreateStorage(std::vector&& data); +std::shared_ptr CreateTransferMemoryStorage(Core::Memory::Memory& memory, + Kernel::KTransferMemory* trmem, + bool is_writable, s64 size); +std::shared_ptr CreateHandleStorage(Core::Memory::Memory& memory, + Kernel::KTransferMemory* trmem, s64 size); + +} // namespace Service::AM diff --git a/src/core/hle/service/am/lock_accessor.cpp b/src/core/hle/service/am/lock_accessor.cpp new file mode 100755 index 000000000..d0bd8d95e --- /dev/null +++ b/src/core/hle/service/am/lock_accessor.cpp @@ -0,0 +1,71 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/hle/service/am/lock_accessor.h" +#include "core/hle/service/ipc_helpers.h" + +namespace Service::AM { + +ILockAccessor::ILockAccessor(Core::System& system_) + : ServiceFramework{system_, "ILockAccessor"}, service_context{system_, "ILockAccessor"} { + // clang-format off + static const FunctionInfo functions[] = { + {1, &ILockAccessor::TryLock, "TryLock"}, + {2, &ILockAccessor::Unlock, "Unlock"}, + {3, &ILockAccessor::GetEvent, "GetEvent"}, + {4,&ILockAccessor::IsLocked, "IsLocked"}, + }; + // clang-format on + + RegisterHandlers(functions); + + lock_event = service_context.CreateEvent("ILockAccessor::LockEvent"); +} + +ILockAccessor::~ILockAccessor() { + service_context.CloseEvent(lock_event); +}; + +void ILockAccessor::TryLock(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto return_handle = rp.Pop(); + + LOG_WARNING(Service_AM, "(STUBBED) called, return_handle={}", return_handle); + + // TODO: When return_handle is true this function should return the lock handle + + is_locked = true; + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.Push(is_locked); +} + +void ILockAccessor::Unlock(HLERequestContext& ctx) { + LOG_INFO(Service_AM, "called"); + + is_locked = false; + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void ILockAccessor::GetEvent(HLERequestContext& ctx) { + LOG_INFO(Service_AM, "called"); + + lock_event->Signal(); + + IPC::ResponseBuilder rb{ctx, 2, 1}; + rb.Push(ResultSuccess); + rb.PushCopyObjects(lock_event->GetReadableEvent()); +} + +void ILockAccessor::IsLocked(HLERequestContext& ctx) { + LOG_INFO(Service_AM, "called"); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); + rb.Push(is_locked); +} + +} // namespace Service::AM diff --git a/src/core/hle/service/am/lock_accessor.h b/src/core/hle/service/am/lock_accessor.h new file mode 100755 index 000000000..626f60e07 --- /dev/null +++ b/src/core/hle/service/am/lock_accessor.h @@ -0,0 +1,28 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "core/hle/service/kernel_helpers.h" +#include "core/hle/service/service.h" + +namespace Service::AM { + +class ILockAccessor final : public ServiceFramework { +public: + explicit ILockAccessor(Core::System& system_); + ~ILockAccessor() override; + +private: + void TryLock(HLERequestContext& ctx); + void Unlock(HLERequestContext& ctx); + void GetEvent(HLERequestContext& ctx); + void IsLocked(HLERequestContext& ctx); + + bool is_locked{}; + + Kernel::KEvent* lock_event; + KernelHelpers::ServiceContext service_context; +}; + +} // namespace Service::AM diff --git a/src/core/hle/service/am/managed_layer_holder.cpp b/src/core/hle/service/am/managed_layer_holder.cpp new file mode 100755 index 000000000..61eb8641a --- /dev/null +++ b/src/core/hle/service/am/managed_layer_holder.cpp @@ -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 diff --git a/src/core/hle/service/am/managed_layer_holder.h b/src/core/hle/service/am/managed_layer_holder.h new file mode 100755 index 000000000..f7fe03f24 --- /dev/null +++ b/src/core/hle/service/am/managed_layer_holder.h @@ -0,0 +1,32 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include + +#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 m_managed_display_layers{}; + std::set m_managed_display_recording_layers{}; +}; + +} // namespace Service::AM diff --git a/src/core/hle/service/am/process.cpp b/src/core/hle/service/am/process.cpp new file mode 100755 index 000000000..16b685f86 --- /dev/null +++ b/src/core/hle/service/am/process.cpp @@ -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 diff --git a/src/core/hle/service/am/process.h b/src/core/hle/service/am/process.h new file mode 100755 index 000000000..4b908ade4 --- /dev/null +++ b/src/core/hle/service/am/process.h @@ -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 diff --git a/src/core/hle/service/am/process_winding_controller.cpp b/src/core/hle/service/am/process_winding_controller.cpp new file mode 100755 index 000000000..b48b52797 --- /dev/null +++ b/src/core/hle/service/am/process_winding_controller.cpp @@ -0,0 +1,56 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/hle/service/am/frontend/applets.h" +#include "core/hle/service/am/library_applet_accessor.h" +#include "core/hle/service/am/process_winding_controller.h" +#include "core/hle/service/ipc_helpers.h" + +namespace Service::AM { + +IProcessWindingController::IProcessWindingController(Core::System& system_, + std::shared_ptr applet_) + : ServiceFramework{system_, "IProcessWindingController"}, applet{std::move(applet_)} { + // clang-format off + static const FunctionInfo functions[] = { + {0, &IProcessWindingController::GetLaunchReason, "GetLaunchReason"}, + {11, &IProcessWindingController::OpenCallingLibraryApplet, "OpenCallingLibraryApplet"}, + {21, nullptr, "PushContext"}, + {22, nullptr, "PopContext"}, + {23, nullptr, "CancelWindingReservation"}, + {30, nullptr, "WindAndDoReserved"}, + {40, nullptr, "ReserveToStartAndWaitAndUnwindThis"}, + {41, nullptr, "ReserveToStartAndWait"}, + }; + // clang-format on + + RegisterHandlers(functions); +} + +IProcessWindingController::~IProcessWindingController() = default; + +void IProcessWindingController::GetLaunchReason(HLERequestContext& ctx) { + LOG_WARNING(Service_AM, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.PushRaw(applet->launch_reason); +} + +void IProcessWindingController::OpenCallingLibraryApplet(HLERequestContext& ctx) { + const auto caller_applet = applet->caller_applet.lock(); + if (caller_applet == nullptr) { + LOG_ERROR(Service_AM, "No calling applet available"); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultUnknown); + return; + } + + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(ResultSuccess); + rb.PushIpcInterface(system, applet->caller_applet_broker, + caller_applet); +} + +} // namespace Service::AM diff --git a/src/core/hle/service/am/process_winding_controller.h b/src/core/hle/service/am/process_winding_controller.h new file mode 100755 index 000000000..71ae4c4f5 --- /dev/null +++ b/src/core/hle/service/am/process_winding_controller.h @@ -0,0 +1,24 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "core/hle/service/service.h" + +namespace Service::AM { + +struct Applet; + +class IProcessWindingController final : public ServiceFramework { +public: + explicit IProcessWindingController(Core::System& system_, std::shared_ptr applet_); + ~IProcessWindingController() override; + +private: + void GetLaunchReason(HLERequestContext& ctx); + void OpenCallingLibraryApplet(HLERequestContext& ctx); + + const std::shared_ptr applet; +}; + +} // namespace Service::AM diff --git a/src/core/hle/service/am/self_controller.cpp b/src/core/hle/service/am/self_controller.cpp new file mode 100755 index 000000000..0289f5cf1 --- /dev/null +++ b/src/core/hle/service/am/self_controller.cpp @@ -0,0 +1,456 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/hle/service/am/am_results.h" +#include "core/hle/service/am/frontend/applets.h" +#include "core/hle/service/am/self_controller.h" +#include "core/hle/service/caps/caps_su.h" +#include "core/hle/service/ipc_helpers.h" +#include "core/hle/service/nvnflinger/fb_share_buffer_manager.h" +#include "core/hle/service/nvnflinger/nvnflinger.h" +#include "core/hle/service/sm/sm.h" +#include "core/hle/service/vi/vi_results.h" + +namespace Service::AM { + +ISelfController::ISelfController(Core::System& system_, std::shared_ptr applet_, + Nvnflinger::Nvnflinger& nvnflinger_) + : ServiceFramework{system_, "ISelfController"}, nvnflinger{nvnflinger_}, applet{std::move( + applet_)} { + // clang-format off + static const FunctionInfo functions[] = { + {0, &ISelfController::Exit, "Exit"}, + {1, &ISelfController::LockExit, "LockExit"}, + {2, &ISelfController::UnlockExit, "UnlockExit"}, + {3, &ISelfController::EnterFatalSection, "EnterFatalSection"}, + {4, &ISelfController::LeaveFatalSection, "LeaveFatalSection"}, + {9, &ISelfController::GetLibraryAppletLaunchableEvent, "GetLibraryAppletLaunchableEvent"}, + {10, &ISelfController::SetScreenShotPermission, "SetScreenShotPermission"}, + {11, &ISelfController::SetOperationModeChangedNotification, "SetOperationModeChangedNotification"}, + {12, &ISelfController::SetPerformanceModeChangedNotification, "SetPerformanceModeChangedNotification"}, + {13, &ISelfController::SetFocusHandlingMode, "SetFocusHandlingMode"}, + {14, &ISelfController::SetRestartMessageEnabled, "SetRestartMessageEnabled"}, + {15, &ISelfController::SetScreenShotAppletIdentityInfo, "SetScreenShotAppletIdentityInfo"}, + {16, &ISelfController::SetOutOfFocusSuspendingEnabled, "SetOutOfFocusSuspendingEnabled"}, + {17, nullptr, "SetControllerFirmwareUpdateSection"}, + {18, nullptr, "SetRequiresCaptureButtonShortPressedMessage"}, + {19, &ISelfController::SetAlbumImageOrientation, "SetAlbumImageOrientation"}, + {20, nullptr, "SetDesirableKeyboardLayout"}, + {21, nullptr, "GetScreenShotProgramId"}, + {40, &ISelfController::CreateManagedDisplayLayer, "CreateManagedDisplayLayer"}, + {41, &ISelfController::IsSystemBufferSharingEnabled, "IsSystemBufferSharingEnabled"}, + {42, &ISelfController::GetSystemSharedLayerHandle, "GetSystemSharedLayerHandle"}, + {43, &ISelfController::GetSystemSharedBufferHandle, "GetSystemSharedBufferHandle"}, + {44, &ISelfController::CreateManagedDisplaySeparableLayer, "CreateManagedDisplaySeparableLayer"}, + {45, nullptr, "SetManagedDisplayLayerSeparationMode"}, + {46, nullptr, "SetRecordingLayerCompositionEnabled"}, + {50, &ISelfController::SetHandlesRequestToDisplay, "SetHandlesRequestToDisplay"}, + {51, &ISelfController::ApproveToDisplay, "ApproveToDisplay"}, + {60, nullptr, "OverrideAutoSleepTimeAndDimmingTime"}, + {61, nullptr, "SetMediaPlaybackState"}, + {62, &ISelfController::SetIdleTimeDetectionExtension, "SetIdleTimeDetectionExtension"}, + {63, &ISelfController::GetIdleTimeDetectionExtension, "GetIdleTimeDetectionExtension"}, + {64, nullptr, "SetInputDetectionSourceSet"}, + {65, &ISelfController::ReportUserIsActive, "ReportUserIsActive"}, + {66, nullptr, "GetCurrentIlluminance"}, + {67, nullptr, "IsIlluminanceAvailable"}, + {68, &ISelfController::SetAutoSleepDisabled, "SetAutoSleepDisabled"}, + {69, &ISelfController::IsAutoSleepDisabled, "IsAutoSleepDisabled"}, + {70, nullptr, "ReportMultimediaError"}, + {71, nullptr, "GetCurrentIlluminanceEx"}, + {72, nullptr, "SetInputDetectionPolicy"}, + {80, nullptr, "SetWirelessPriorityMode"}, + {90, &ISelfController::GetAccumulatedSuspendedTickValue, "GetAccumulatedSuspendedTickValue"}, + {91, &ISelfController::GetAccumulatedSuspendedTickChangedEvent, "GetAccumulatedSuspendedTickChangedEvent"}, + {100, &ISelfController::SetAlbumImageTakenNotificationEnabled, "SetAlbumImageTakenNotificationEnabled"}, + {110, nullptr, "SetApplicationAlbumUserData"}, + {120, &ISelfController::SaveCurrentScreenshot, "SaveCurrentScreenshot"}, + {130, &ISelfController::SetRecordVolumeMuted, "SetRecordVolumeMuted"}, + {1000, nullptr, "GetDebugStorageChannel"}, + }; + // clang-format on + + RegisterHandlers(functions); +} + +ISelfController::~ISelfController() = default; + +void ISelfController::Exit(HLERequestContext& ctx) { + LOG_DEBUG(Service_AM, "called"); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); + + // TODO + system.Exit(); +} + +void ISelfController::LockExit(HLERequestContext& ctx) { + LOG_DEBUG(Service_AM, "called"); + + system.SetExitLocked(true); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void ISelfController::UnlockExit(HLERequestContext& ctx) { + LOG_DEBUG(Service_AM, "called"); + + system.SetExitLocked(false); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); + + if (system.GetExitRequested()) { + system.Exit(); + } +} + +void ISelfController::EnterFatalSection(HLERequestContext& ctx) { + + std::scoped_lock lk{applet->lock}; + applet->fatal_section_count++; + LOG_DEBUG(Service_AM, "called. Num fatal sections entered: {}", applet->fatal_section_count); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void ISelfController::LeaveFatalSection(HLERequestContext& ctx) { + LOG_DEBUG(Service_AM, "called."); + + // Entry and exit of fatal sections must be balanced. + std::scoped_lock lk{applet->lock}; + if (applet->fatal_section_count == 0) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(AM::ResultFatalSectionCountImbalance); + return; + } + + applet->fatal_section_count--; + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void ISelfController::GetLibraryAppletLaunchableEvent(HLERequestContext& ctx) { + LOG_WARNING(Service_AM, "(STUBBED) called"); + + applet->library_applet_launchable_event.Signal(); + + IPC::ResponseBuilder rb{ctx, 2, 1}; + rb.Push(ResultSuccess); + rb.PushCopyObjects(applet->library_applet_launchable_event.GetHandle()); +} + +void ISelfController::SetScreenShotPermission(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto permission = rp.PopEnum(); + LOG_DEBUG(Service_AM, "called, permission={}", permission); + + std::scoped_lock lk{applet->lock}; + applet->screenshot_permission = permission; + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void ISelfController::SetOperationModeChangedNotification(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + + const bool notification_enabled = rp.Pop(); + LOG_WARNING(Service_AM, "(STUBBED) called notification_enabled={}", notification_enabled); + + std::scoped_lock lk{applet->lock}; + applet->operation_mode_changed_notification_enabled = notification_enabled; + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void ISelfController::SetPerformanceModeChangedNotification(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + + const bool notification_enabled = rp.Pop(); + LOG_WARNING(Service_AM, "(STUBBED) called notification_enabled={}", notification_enabled); + + std::scoped_lock lk{applet->lock}; + applet->performance_mode_changed_notification_enabled = notification_enabled; + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void ISelfController::SetFocusHandlingMode(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + + const auto flags = rp.PopRaw(); + + LOG_WARNING(Service_AM, "(STUBBED) called. unknown0={}, unknown1={}, unknown2={}", + flags.unknown0, flags.unknown1, flags.unknown2); + + std::scoped_lock lk{applet->lock}; + applet->focus_handling_mode = flags; + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void ISelfController::SetRestartMessageEnabled(HLERequestContext& ctx) { + LOG_WARNING(Service_AM, "(STUBBED) called"); + + std::scoped_lock lk{applet->lock}; + applet->restart_message_enabled = true; + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void ISelfController::SetScreenShotAppletIdentityInfo(HLERequestContext& ctx) { + LOG_WARNING(Service_AM, "(STUBBED) called"); + + IPC::RequestParser rp{ctx}; + std::scoped_lock lk{applet->lock}; + applet->screen_shot_identity = rp.PopRaw(); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void ISelfController::SetOutOfFocusSuspendingEnabled(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + + const bool enabled = rp.Pop(); + LOG_WARNING(Service_AM, "(STUBBED) called enabled={}", enabled); + + std::scoped_lock lk{applet->lock}; + ASSERT(applet->type == AppletType::Application); + applet->out_of_focus_suspension_enabled = enabled; + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void ISelfController::SetAlbumImageOrientation(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + + const auto orientation = rp.PopRaw(); + LOG_WARNING(Service_AM, "(STUBBED) called, orientation={}", static_cast(orientation)); + + std::scoped_lock lk{applet->lock}; + applet->album_image_orientation = orientation; + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void ISelfController::CreateManagedDisplayLayer(HLERequestContext& ctx) { + LOG_WARNING(Service_AM, "(STUBBED) called"); + + u64 layer_id{}; + applet->managed_layer_holder.Initialize(&nvnflinger); + applet->managed_layer_holder.CreateManagedDisplayLayer(&layer_id); + + IPC::ResponseBuilder rb{ctx, 4}; + rb.Push(ResultSuccess); + rb.Push(layer_id); +} + +void ISelfController::IsSystemBufferSharingEnabled(HLERequestContext& ctx) { + LOG_WARNING(Service_AM, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(this->EnsureBufferSharingEnabled(ctx.GetThread().GetOwnerProcess())); +} + +void ISelfController::GetSystemSharedLayerHandle(HLERequestContext& ctx) { + LOG_WARNING(Service_AM, "(STUBBED) called"); + + u64 buffer_id, layer_id; + applet->system_buffer_manager.GetSystemSharedLayerHandle(&buffer_id, &layer_id); + + IPC::ResponseBuilder rb{ctx, 6}; + rb.Push(this->EnsureBufferSharingEnabled(ctx.GetThread().GetOwnerProcess())); + rb.Push(buffer_id); + rb.Push(layer_id); +} + +void ISelfController::GetSystemSharedBufferHandle(HLERequestContext& ctx) { + LOG_WARNING(Service_AM, "(STUBBED) called"); + + u64 buffer_id, layer_id; + applet->system_buffer_manager.GetSystemSharedLayerHandle(&buffer_id, &layer_id); + + IPC::ResponseBuilder rb{ctx, 4}; + rb.Push(this->EnsureBufferSharingEnabled(ctx.GetThread().GetOwnerProcess())); + rb.Push(buffer_id); +} + +Result ISelfController::EnsureBufferSharingEnabled(Kernel::KProcess* process) { + if (applet->system_buffer_manager.Initialize(&nvnflinger, process, applet->applet_id)) { + return ResultSuccess; + } + + return VI::ResultOperationFailed; +} + +void ISelfController::CreateManagedDisplaySeparableLayer(HLERequestContext& ctx) { + LOG_WARNING(Service_AM, "(STUBBED) called"); + + u64 layer_id{}; + u64 recording_layer_id{}; + applet->managed_layer_holder.Initialize(&nvnflinger); + applet->managed_layer_holder.CreateManagedDisplaySeparableLayer(&layer_id, &recording_layer_id); + + IPC::ResponseBuilder rb{ctx, 6}; + rb.Push(ResultSuccess); + rb.Push(layer_id); + rb.Push(recording_layer_id); +} + +void ISelfController::SetHandlesRequestToDisplay(HLERequestContext& ctx) { + LOG_WARNING(Service_AM, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void ISelfController::ApproveToDisplay(HLERequestContext& ctx) { + LOG_WARNING(Service_AM, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void ISelfController::SetIdleTimeDetectionExtension(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + + const auto extension = rp.PopRaw(); + LOG_DEBUG(Service_AM, "(STUBBED) called extension={}", extension); + + std::scoped_lock lk{applet->lock}; + applet->idle_time_detection_extension = extension; + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void ISelfController::GetIdleTimeDetectionExtension(HLERequestContext& ctx) { + LOG_WARNING(Service_AM, "(STUBBED) called"); + + std::scoped_lock lk{applet->lock}; + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.PushRaw(applet->idle_time_detection_extension); +} + +void ISelfController::ReportUserIsActive(HLERequestContext& ctx) { + LOG_WARNING(Service_AM, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void ISelfController::SetAutoSleepDisabled(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + + std::scoped_lock lk{applet->lock}; + applet->auto_sleep_disabled = rp.Pop(); + + // On the system itself, if the previous state of is_auto_sleep_disabled + // differed from the current value passed in, it'd signify the internal + // window manager to update (and also increment some statistics like update counts) + // + // It'd also indicate this change to an idle handling context. + // + // However, given we're emulating this behavior, most of this can be ignored + // and it's sufficient to simply set the member variable for querying via + // IsAutoSleepDisabled(). + + LOG_DEBUG(Service_AM, "called. is_auto_sleep_disabled={}", applet->auto_sleep_disabled); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void ISelfController::IsAutoSleepDisabled(HLERequestContext& ctx) { + LOG_DEBUG(Service_AM, "called."); + + std::scoped_lock lk{applet->lock}; + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.Push(applet->auto_sleep_disabled); +} + +void ISelfController::GetAccumulatedSuspendedTickValue(HLERequestContext& ctx) { + LOG_DEBUG(Service_AM, "called."); + + std::scoped_lock lk{applet->lock}; + // This command returns the total number of system ticks since ISelfController creation + // where the game was suspended. Since Yuzu doesn't implement game suspension, this command + // can just always return 0 ticks. + IPC::ResponseBuilder rb{ctx, 4}; + rb.Push(ResultSuccess); + rb.Push(applet->suspended_ticks); +} + +void ISelfController::GetAccumulatedSuspendedTickChangedEvent(HLERequestContext& ctx) { + LOG_DEBUG(Service_AM, "called."); + + IPC::ResponseBuilder rb{ctx, 2, 1}; + rb.Push(ResultSuccess); + rb.PushCopyObjects(applet->accumulated_suspended_tick_changed_event.GetHandle()); +} + +void ISelfController::SetAlbumImageTakenNotificationEnabled(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + + // This service call sets an internal flag whether a notification is shown when an image is + // captured. Currently we do not support capturing images via the capture button, so this can be + // stubbed for now. + const bool enabled = rp.Pop(); + LOG_WARNING(Service_AM, "(STUBBED) called. enabled={}", enabled); + + std::scoped_lock lk{applet->lock}; + applet->album_image_taken_notification_enabled = enabled; + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void ISelfController::SaveCurrentScreenshot(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + + const auto report_option = rp.PopEnum(); + + LOG_INFO(Service_AM, "called, report_option={}", report_option); + + const auto screenshot_service = + system.ServiceManager().GetService( + "caps:su"); + + if (screenshot_service) { + screenshot_service->CaptureAndSaveScreenshot(report_option); + } + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void ISelfController::SetRecordVolumeMuted(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + + const auto enabled = rp.Pop(); + LOG_WARNING(Service_AM, "(STUBBED) called. enabled={}", enabled); + + std::scoped_lock lk{applet->lock}; + applet->record_volume_muted = enabled; + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +} // namespace Service::AM diff --git a/src/core/hle/service/am/self_controller.h b/src/core/hle/service/am/self_controller.h new file mode 100755 index 000000000..a63bc2e74 --- /dev/null +++ b/src/core/hle/service/am/self_controller.h @@ -0,0 +1,58 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "core/hle/service/kernel_helpers.h" +#include "core/hle/service/service.h" + +namespace Service::AM { + +struct Applet; + +class ISelfController final : public ServiceFramework { +public: + explicit ISelfController(Core::System& system_, std::shared_ptr applet_, + Nvnflinger::Nvnflinger& nvnflinger_); + ~ISelfController() override; + +private: + void Exit(HLERequestContext& ctx); + void LockExit(HLERequestContext& ctx); + void UnlockExit(HLERequestContext& ctx); + void EnterFatalSection(HLERequestContext& ctx); + void LeaveFatalSection(HLERequestContext& ctx); + void GetLibraryAppletLaunchableEvent(HLERequestContext& ctx); + void SetScreenShotPermission(HLERequestContext& ctx); + void SetOperationModeChangedNotification(HLERequestContext& ctx); + void SetPerformanceModeChangedNotification(HLERequestContext& ctx); + void SetFocusHandlingMode(HLERequestContext& ctx); + void SetRestartMessageEnabled(HLERequestContext& ctx); + void SetScreenShotAppletIdentityInfo(HLERequestContext& ctx); + void SetOutOfFocusSuspendingEnabled(HLERequestContext& ctx); + void SetAlbumImageOrientation(HLERequestContext& ctx); + void IsSystemBufferSharingEnabled(HLERequestContext& ctx); + void GetSystemSharedBufferHandle(HLERequestContext& ctx); + void GetSystemSharedLayerHandle(HLERequestContext& ctx); + void CreateManagedDisplayLayer(HLERequestContext& ctx); + void CreateManagedDisplaySeparableLayer(HLERequestContext& ctx); + void SetHandlesRequestToDisplay(HLERequestContext& ctx); + void ApproveToDisplay(HLERequestContext& ctx); + void SetIdleTimeDetectionExtension(HLERequestContext& ctx); + void GetIdleTimeDetectionExtension(HLERequestContext& ctx); + void ReportUserIsActive(HLERequestContext& ctx); + void SetAutoSleepDisabled(HLERequestContext& ctx); + void IsAutoSleepDisabled(HLERequestContext& ctx); + void GetAccumulatedSuspendedTickValue(HLERequestContext& ctx); + void GetAccumulatedSuspendedTickChangedEvent(HLERequestContext& ctx); + void SetAlbumImageTakenNotificationEnabled(HLERequestContext& ctx); + void SaveCurrentScreenshot(HLERequestContext& ctx); + void SetRecordVolumeMuted(HLERequestContext& ctx); + + Result EnsureBufferSharingEnabled(Kernel::KProcess* process); + + Nvnflinger::Nvnflinger& nvnflinger; + const std::shared_ptr applet; +}; + +} // namespace Service::AM diff --git a/src/core/hle/service/am/storage.cpp b/src/core/hle/service/am/storage.cpp new file mode 100755 index 000000000..4e82afd1c --- /dev/null +++ b/src/core/hle/service/am/storage.cpp @@ -0,0 +1,59 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/hle/service/am/am_results.h" +#include "core/hle/service/am/library_applet_storage.h" +#include "core/hle/service/am/storage.h" +#include "core/hle/service/am/storage_accessor.h" +#include "core/hle/service/ipc_helpers.h" + +namespace Service::AM { + +IStorage::IStorage(Core::System& system_, std::shared_ptr impl_) + : ServiceFramework{system_, "IStorage"}, impl{std::move(impl_)} { + static const FunctionInfo functions[] = { + {0, &IStorage::Open, "Open"}, + {1, &IStorage::OpenTransferStorage, "OpenTransferStorage"}, + }; + + RegisterHandlers(functions); +} + +IStorage::IStorage(Core::System& system_, std::vector&& data) + : IStorage(system_, CreateStorage(std::move(data))) {} + +IStorage::~IStorage() = default; + +void IStorage::Open(HLERequestContext& ctx) { + LOG_DEBUG(Service_AM, "called"); + + if (impl->GetHandle() != nullptr) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(AM::ResultInvalidStorageType); + return; + } + + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(ResultSuccess); + rb.PushIpcInterface(system, impl); +} + +void IStorage::OpenTransferStorage(HLERequestContext& ctx) { + LOG_DEBUG(Service_AM, "called"); + + if (impl->GetHandle() == nullptr) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(AM::ResultInvalidStorageType); + return; + } + + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(ResultSuccess); + rb.PushIpcInterface(system, impl); +} + +std::vector IStorage::GetData() const { + return impl->GetData(); +} + +} // namespace Service::AM diff --git a/src/core/hle/service/am/storage.h b/src/core/hle/service/am/storage.h new file mode 100755 index 000000000..10d00b141 --- /dev/null +++ b/src/core/hle/service/am/storage.h @@ -0,0 +1,31 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "core/hle/service/service.h" + +namespace Service::AM { + +class LibraryAppletStorage; + +class IStorage final : public ServiceFramework { +public: + explicit IStorage(Core::System& system_, std::shared_ptr impl_); + explicit IStorage(Core::System& system_, std::vector&& buffer); + ~IStorage() override; + + std::shared_ptr GetImpl() const { + return impl; + } + + std::vector GetData() const; + +private: + void Open(HLERequestContext& ctx); + void OpenTransferStorage(HLERequestContext& ctx); + + const std::shared_ptr impl; +}; + +} // namespace Service::AM diff --git a/src/core/hle/service/am/storage_accessor.cpp b/src/core/hle/service/am/storage_accessor.cpp new file mode 100755 index 000000000..a1184b065 --- /dev/null +++ b/src/core/hle/service/am/storage_accessor.cpp @@ -0,0 +1,90 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/hle/kernel/k_transfer_memory.h" +#include "core/hle/service/am/am_results.h" +#include "core/hle/service/am/library_applet_storage.h" +#include "core/hle/service/am/storage_accessor.h" +#include "core/hle/service/ipc_helpers.h" + +namespace Service::AM { + +IStorageAccessor::IStorageAccessor(Core::System& system_, + std::shared_ptr impl_) + : ServiceFramework{system_, "IStorageAccessor"}, impl{std::move(impl_)} { + static const FunctionInfo functions[] = { + {0, &IStorageAccessor::GetSize, "GetSize"}, + {10, &IStorageAccessor::Write, "Write"}, + {11, &IStorageAccessor::Read, "Read"}, + }; + + RegisterHandlers(functions); +} + +IStorageAccessor::~IStorageAccessor() = default; + +void IStorageAccessor::GetSize(HLERequestContext& ctx) { + LOG_DEBUG(Service_AM, "called"); + + IPC::ResponseBuilder rb{ctx, 4}; + + rb.Push(ResultSuccess); + rb.Push(impl->GetSize()); +} + +void IStorageAccessor::Write(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + + const s64 offset{rp.Pop()}; + const auto data{ctx.ReadBuffer()}; + LOG_DEBUG(Service_AM, "called, offset={}, size={}", offset, data.size()); + + const auto res{impl->Write(offset, data.data(), data.size())}; + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(res); +} + +void IStorageAccessor::Read(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + + const s64 offset{rp.Pop()}; + std::vector data(ctx.GetWriteBufferSize()); + + LOG_DEBUG(Service_AM, "called, offset={}, size={}", offset, data.size()); + + const auto res{impl->Read(offset, data.data(), data.size())}; + + ctx.WriteBuffer(data); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(res); +} + +ITransferStorageAccessor::ITransferStorageAccessor(Core::System& system_, + std::shared_ptr impl_) + : ServiceFramework{system_, "ITransferStorageAccessor"}, impl{std::move(impl_)} { + static const FunctionInfo functions[] = { + {0, &ITransferStorageAccessor::GetSize, "GetSize"}, + {1, &ITransferStorageAccessor::GetHandle, "GetHandle"}, + }; + + RegisterHandlers(functions); +} + +ITransferStorageAccessor::~ITransferStorageAccessor() = default; + +void ITransferStorageAccessor::GetSize(HLERequestContext& ctx) { + IPC::ResponseBuilder rb{ctx, 4}; + rb.Push(ResultSuccess); + rb.Push(impl->GetSize()); +} + +void ITransferStorageAccessor::GetHandle(HLERequestContext& ctx) { + IPC::ResponseBuilder rb{ctx, 4, 1}; + rb.Push(ResultSuccess); + rb.Push(impl->GetSize()); + rb.PushCopyObjects(impl->GetHandle()); +} + +} // namespace Service::AM diff --git a/src/core/hle/service/am/storage_accessor.h b/src/core/hle/service/am/storage_accessor.h new file mode 100755 index 000000000..b9aa85a66 --- /dev/null +++ b/src/core/hle/service/am/storage_accessor.h @@ -0,0 +1,37 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "core/hle/service/am/storage.h" +#include "core/hle/service/service.h" + +namespace Service::AM { + +class IStorageAccessor final : public ServiceFramework { +public: + explicit IStorageAccessor(Core::System& system_, std::shared_ptr impl_); + ~IStorageAccessor() override; + +private: + void GetSize(HLERequestContext& ctx); + void Write(HLERequestContext& ctx); + void Read(HLERequestContext& ctx); + + const std::shared_ptr impl; +}; + +class ITransferStorageAccessor final : public ServiceFramework { +public: + explicit ITransferStorageAccessor(Core::System& system_, + std::shared_ptr impl_); + ~ITransferStorageAccessor() override; + +private: + void GetSize(HLERequestContext& ctx); + void GetHandle(HLERequestContext& ctx); + + const std::shared_ptr impl; +}; + +} // namespace Service::AM diff --git a/src/core/hle/service/am/system_applet_proxy.cpp b/src/core/hle/service/am/system_applet_proxy.cpp new file mode 100755 index 000000000..38643408e --- /dev/null +++ b/src/core/hle/service/am/system_applet_proxy.cpp @@ -0,0 +1,136 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/hle/service/am/applet_common_functions.h" +#include "core/hle/service/am/application_creator.h" +#include "core/hle/service/am/audio_controller.h" +#include "core/hle/service/am/common_state_getter.h" +#include "core/hle/service/am/debug_functions.h" +#include "core/hle/service/am/display_controller.h" +#include "core/hle/service/am/global_state_controller.h" +#include "core/hle/service/am/home_menu_functions.h" +#include "core/hle/service/am/library_applet_creator.h" +#include "core/hle/service/am/library_applet_self_accessor.h" +#include "core/hle/service/am/process_winding_controller.h" +#include "core/hle/service/am/self_controller.h" +#include "core/hle/service/am/system_applet_proxy.h" +#include "core/hle/service/am/window_controller.h" +#include "core/hle/service/ipc_helpers.h" + +namespace Service::AM { + +ISystemAppletProxy::ISystemAppletProxy(Nvnflinger::Nvnflinger& nvnflinger_, + std::shared_ptr applet_, Core::System& system_) + : ServiceFramework{system_, "ISystemAppletProxy"}, nvnflinger{nvnflinger_}, applet{std::move( + applet_)} { + // clang-format off + static const FunctionInfo functions[] = { + {0, &ISystemAppletProxy::GetCommonStateGetter, "GetCommonStateGetter"}, + {1, &ISystemAppletProxy::GetSelfController, "GetSelfController"}, + {2, &ISystemAppletProxy::GetWindowController, "GetWindowController"}, + {3, &ISystemAppletProxy::GetAudioController, "GetAudioController"}, + {4, &ISystemAppletProxy::GetDisplayController, "GetDisplayController"}, + {10, nullptr, "GetProcessWindingController"}, + {11, &ISystemAppletProxy::GetLibraryAppletCreator, "GetLibraryAppletCreator"}, + {20, &ISystemAppletProxy::GetHomeMenuFunctions, "GetHomeMenuFunctions"}, + {21, &ISystemAppletProxy::GetGlobalStateController, "GetGlobalStateController"}, + {22, &ISystemAppletProxy::GetApplicationCreator, "GetApplicationCreator"}, + {23, &ISystemAppletProxy::GetAppletCommonFunctions, "GetAppletCommonFunctions"}, + {1000, &ISystemAppletProxy::GetDebugFunctions, "GetDebugFunctions"}, + }; + // clang-format on + + RegisterHandlers(functions); +} + +ISystemAppletProxy::~ISystemAppletProxy() = default; + +void ISystemAppletProxy::GetCommonStateGetter(HLERequestContext& ctx) { + LOG_DEBUG(Service_AM, "called"); + + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(ResultSuccess); + rb.PushIpcInterface(system, applet); +} + +void ISystemAppletProxy::GetSelfController(HLERequestContext& ctx) { + LOG_DEBUG(Service_AM, "called"); + + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(ResultSuccess); + rb.PushIpcInterface(system, applet, nvnflinger); +} + +void ISystemAppletProxy::GetWindowController(HLERequestContext& ctx) { + LOG_DEBUG(Service_AM, "called"); + + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(ResultSuccess); + rb.PushIpcInterface(system, applet); +} + +void ISystemAppletProxy::GetAudioController(HLERequestContext& ctx) { + LOG_DEBUG(Service_AM, "called"); + + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(ResultSuccess); + rb.PushIpcInterface(system); +} + +void ISystemAppletProxy::GetDisplayController(HLERequestContext& ctx) { + LOG_DEBUG(Service_AM, "called"); + + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(ResultSuccess); + rb.PushIpcInterface(system, applet); +} + +void ISystemAppletProxy::GetLibraryAppletCreator(HLERequestContext& ctx) { + LOG_DEBUG(Service_AM, "called"); + + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(ResultSuccess); + rb.PushIpcInterface(system, applet); +} + +void ISystemAppletProxy::GetHomeMenuFunctions(HLERequestContext& ctx) { + LOG_DEBUG(Service_AM, "called"); + + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(ResultSuccess); + rb.PushIpcInterface(system); +} + +void ISystemAppletProxy::GetGlobalStateController(HLERequestContext& ctx) { + LOG_DEBUG(Service_AM, "called"); + + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(ResultSuccess); + rb.PushIpcInterface(system); +} + +void ISystemAppletProxy::GetApplicationCreator(HLERequestContext& ctx) { + LOG_DEBUG(Service_AM, "called"); + + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(ResultSuccess); + rb.PushIpcInterface(system); +} + +void ISystemAppletProxy::GetAppletCommonFunctions(HLERequestContext& ctx) { + LOG_DEBUG(Service_AM, "called"); + + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(ResultSuccess); + rb.PushIpcInterface(system, applet); +} + +void ISystemAppletProxy::GetDebugFunctions(HLERequestContext& ctx) { + LOG_DEBUG(Service_AM, "called"); + + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(ResultSuccess); + rb.PushIpcInterface(system); +} + +} // namespace Service::AM diff --git a/src/core/hle/service/am/system_applet_proxy.h b/src/core/hle/service/am/system_applet_proxy.h new file mode 100755 index 000000000..0390cd1e5 --- /dev/null +++ b/src/core/hle/service/am/system_applet_proxy.h @@ -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/am/applet_message_queue.h" +#include "core/hle/service/service.h" + +namespace Service::AM { + +struct Applet; + +class ISystemAppletProxy final : public ServiceFramework { +public: + explicit ISystemAppletProxy(Nvnflinger::Nvnflinger& nvnflinger_, + std::shared_ptr applet_, Core::System& system_); + ~ISystemAppletProxy(); + +private: + void GetCommonStateGetter(HLERequestContext& ctx); + void GetSelfController(HLERequestContext& ctx); + void GetWindowController(HLERequestContext& ctx); + void GetAudioController(HLERequestContext& ctx); + void GetDisplayController(HLERequestContext& ctx); + void GetLibraryAppletCreator(HLERequestContext& ctx); + void GetHomeMenuFunctions(HLERequestContext& ctx); + void GetGlobalStateController(HLERequestContext& ctx); + void GetApplicationCreator(HLERequestContext& ctx); + void GetAppletCommonFunctions(HLERequestContext& ctx); + void GetDebugFunctions(HLERequestContext& ctx); + + Nvnflinger::Nvnflinger& nvnflinger; + std::shared_ptr applet; +}; + +} // namespace Service::AM diff --git a/src/core/hle/service/am/system_buffer_manager.cpp b/src/core/hle/service/am/system_buffer_manager.cpp new file mode 100755 index 000000000..3d998a334 --- /dev/null +++ b/src/core/hle/service/am/system_buffer_manager.cpp @@ -0,0 +1,69 @@ +// 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" +#include "core/hle/service/vi/vi_results.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; + m_nvnflinger->SetLayerVisibility(m_system_shared_layer_id, m_visible); + } + + return m_buffer_sharing_enabled; +} + +void SystemBufferManager::SetWindowVisibility(bool visible) { + if (m_visible == visible) { + return; + } + + m_visible = visible; + + if (m_nvnflinger) { + m_nvnflinger->SetLayerVisibility(m_system_shared_layer_id, m_visible); + } +} + +Result SystemBufferManager::WriteApplicationCaptureBuffer(bool* out_was_written, + s32* out_fbshare_layer_index) { + // TODO + R_SUCCEED(); +} + +} // namespace Service::AM diff --git a/src/core/hle/service/am/system_buffer_manager.h b/src/core/hle/service/am/system_buffer_manager.h new file mode 100755 index 000000000..87627ce1b --- /dev/null +++ b/src/core/hle/service/am/system_buffer_manager.h @@ -0,0 +1,51 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include + +#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; +} + +union Result; + +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; + } + + void SetWindowVisibility(bool visible); + + Result WriteApplicationCaptureBuffer(bool* out_was_written, s32* out_fbshare_layer_index); + +private: + Kernel::KProcess* m_process{}; + Nvnflinger::Nvnflinger* m_nvnflinger{}; + bool m_buffer_sharing_enabled{}; + bool m_visible{true}; + u64 m_system_shared_buffer_id{}; + u64 m_system_shared_layer_id{}; +}; + +} // namespace Service::AM diff --git a/src/core/hle/service/am/window_controller.cpp b/src/core/hle/service/am/window_controller.cpp new file mode 100755 index 000000000..f00957f83 --- /dev/null +++ b/src/core/hle/service/am/window_controller.cpp @@ -0,0 +1,86 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/hle/service/am/applet.h" +#include "core/hle/service/am/window_controller.h" +#include "core/hle/service/ipc_helpers.h" + +namespace Service::AM { + +IWindowController::IWindowController(Core::System& system_, std::shared_ptr applet_) + : ServiceFramework{system_, "IWindowController"}, applet{std::move(applet_)} { + // clang-format off + static const FunctionInfo functions[] = { + {0, nullptr, "CreateWindow"}, + {1, &IWindowController::GetAppletResourceUserId, "GetAppletResourceUserId"}, + {2, &IWindowController::GetAppletResourceUserIdOfCallerApplet, "GetAppletResourceUserIdOfCallerApplet"}, + {10, &IWindowController::AcquireForegroundRights, "AcquireForegroundRights"}, + {11, nullptr, "ReleaseForegroundRights"}, + {12, nullptr, "RejectToChangeIntoBackground"}, + {20, &IWindowController::SetAppletWindowVisibility, "SetAppletWindowVisibility"}, + {21, &IWindowController::SetAppletGpuTimeSlice, "SetAppletGpuTimeSlice"}, + }; + // clang-format on + + RegisterHandlers(functions); +} + +IWindowController::~IWindowController() = default; + +void IWindowController::GetAppletResourceUserId(HLERequestContext& ctx) { + IPC::ResponseBuilder rb{ctx, 4}; + rb.Push(ResultSuccess); + rb.Push(applet->aruid); +} + +void IWindowController::GetAppletResourceUserIdOfCallerApplet(HLERequestContext& ctx) { + u64 aruid = 0; + if (auto caller = applet->caller_applet.lock(); caller) { + aruid = caller->aruid; + } + + LOG_WARNING(Service_AM, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 4}; + rb.Push(ResultSuccess); + rb.Push(aruid); +} + +void IWindowController::AcquireForegroundRights(HLERequestContext& ctx) { + LOG_WARNING(Service_AM, "(STUBBED) called"); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void IWindowController::SetAppletWindowVisibility(HLERequestContext& ctx) { + LOG_INFO(Service_AM, "called"); + + IPC::RequestParser rp{ctx}; + const bool visible = rp.Pop(); + + applet->system_buffer_manager.SetWindowVisibility(visible); + applet->hid_registration.EnableAppletToGetInput(visible); + + if (visible) { + applet->message_queue.PushMessage(AppletMessageQueue::AppletMessage::ChangeIntoForeground); + applet->focus_state = FocusState::InFocus; + } else { + applet->focus_state = FocusState::NotInFocus; + } + applet->message_queue.PushMessage(AppletMessageQueue::AppletMessage::FocusStateChanged); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void IWindowController::SetAppletGpuTimeSlice(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto time_slice = rp.Pop(); + + LOG_WARNING(Service_AM, "(STUBBED) called, time_slice={}", time_slice); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +} // namespace Service::AM diff --git a/src/core/hle/service/am/window_controller.h b/src/core/hle/service/am/window_controller.h new file mode 100755 index 000000000..a28219abe --- /dev/null +++ b/src/core/hle/service/am/window_controller.h @@ -0,0 +1,27 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "core/hle/service/service.h" + +namespace Service::AM { + +struct Applet; + +class IWindowController final : public ServiceFramework { +public: + explicit IWindowController(Core::System& system_, std::shared_ptr applet_); + ~IWindowController() override; + +private: + void GetAppletResourceUserId(HLERequestContext& ctx); + void GetAppletResourceUserIdOfCallerApplet(HLERequestContext& ctx); + void AcquireForegroundRights(HLERequestContext& ctx); + void SetAppletWindowVisibility(HLERequestContext& ctx); + void SetAppletGpuTimeSlice(HLERequestContext& ctx); + + const std::shared_ptr applet; +}; + +} // namespace Service::AM diff --git a/src/core/hle/service/btm/btm.cpp b/src/core/hle/service/btm/btm.cpp index 964b62cc7..ebebedeb5 100755 --- a/src/core/hle/service/btm/btm.cpp +++ b/src/core/hle/service/btm/btm.cpp @@ -283,7 +283,7 @@ public: {17, &IBtmSystemCore::GetConnectedAudioDevices, "GetConnectedAudioDevices"}, {18, nullptr, "DisconnectAudioDevice"}, {19, nullptr, "AcquirePairedAudioDeviceInfoChangedEvent"}, - {20, nullptr, "GetPairedAudioDevices"}, + {20, &IBtmSystemCore::GetPairedAudioDevices, "GetPairedAudioDevices"}, {21, nullptr, "RemoveAudioDevicePairing"}, {22, &IBtmSystemCore::RequestAudioDeviceConnectionRejection, "RequestAudioDeviceConnectionRejection"}, {23, &IBtmSystemCore::CancelAudioDeviceConnectionRejection, "CancelAudioDeviceConnectionRejection"} @@ -327,6 +327,13 @@ private: rb.Push(0); } + void GetPairedAudioDevices(HLERequestContext& ctx) { + LOG_WARNING(Service_BTM, "(STUBBED) called"); + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.Push(0); + } + void RequestAudioDeviceConnectionRejection(HLERequestContext& ctx) { LOG_WARNING(Service_BTM, "(STUBBED) called"); IPC::ResponseBuilder rb{ctx, 2}; diff --git a/src/core/hle/service/event.cpp b/src/core/hle/service/event.cpp new file mode 100755 index 000000000..375660d72 --- /dev/null +++ b/src/core/hle/service/event.cpp @@ -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 diff --git a/src/core/hle/service/event.h b/src/core/hle/service/event.h new file mode 100755 index 000000000..cdbc4635a --- /dev/null +++ b/src/core/hle/service/event.h @@ -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 diff --git a/src/core/hle/service/filesystem/fsp/fsp_srv.cpp b/src/core/hle/service/filesystem/fsp/fsp_srv.cpp index 2be72b021..5fe534c73 100755 --- a/src/core/hle/service/filesystem/fsp/fsp_srv.cpp +++ b/src/core/hle/service/filesystem/fsp/fsp_srv.cpp @@ -15,11 +15,13 @@ #include "common/settings.h" #include "common/string_util.h" #include "core/core.h" +#include "core/file_sys/content_archive.h" #include "core/file_sys/errors.h" #include "core/file_sys/fs_directory.h" #include "core/file_sys/fs_filesystem.h" #include "core/file_sys/nca_metadata.h" #include "core/file_sys/patch_manager.h" +#include "core/file_sys/romfs.h" #include "core/file_sys/romfs_factory.h" #include "core/file_sys/savedata_factory.h" #include "core/file_sys/system_archive/system_archive.h" @@ -33,18 +35,20 @@ #include "core/hle/service/filesystem/save_data_controller.h" #include "core/hle/service/hle_ipc.h" #include "core/hle/service/ipc_helpers.h" +#include "core/loader/loader.h" #include "core/reporter.h" namespace Service::FileSystem { -enum class FileSystemType : u8 { - Invalid0 = 0, - Invalid1 = 1, +enum class FileSystemProxyType : u8 { + Code = 0, + Rom = 1, Logo = 2, - ContentControl = 3, - ContentManual = 4, - ContentMeta = 5, - ContentData = 6, - ApplicationPackage = 7, + Control = 3, + Manual = 4, + Meta = 5, + Data = 6, + Package = 7, + RegisteredUpdate = 8, }; class ISaveDataInfoReader final : public ServiceFramework { @@ -357,12 +361,30 @@ void FSP_SRV::SetCurrentProcess(HLERequestContext& ctx) { void FSP_SRV::OpenFileSystemWithPatch(HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; - const auto type = rp.PopRaw(); - const auto title_id = rp.PopRaw(); - LOG_WARNING(Service_FS, "(STUBBED) called with type={}, title_id={:016X}", type, title_id); + struct InputParameters { + FileSystemProxyType type; + u64 program_id; + }; + static_assert(sizeof(InputParameters) == 0x10, "InputParameters has wrong size"); - IPC::ResponseBuilder rb{ctx, 2, 0, 0}; - rb.Push(ResultUnknown); + const auto params = rp.PopRaw(); + LOG_ERROR(Service_FS, "(STUBBED) called with type={}, program_id={:016X}", params.type, + params.program_id); + + // FIXME: many issues with this + ASSERT(params.type == FileSystemProxyType::Manual); + const auto manual_romfs = romfs_controller->OpenPatchedRomFS( + params.program_id, FileSys::ContentRecordType::HtmlDocument); + + ASSERT(manual_romfs != nullptr); + + const auto extracted_romfs = FileSys::ExtractRomFS(manual_romfs); + ASSERT(extracted_romfs != nullptr); + + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(ResultSuccess); + rb.PushIpcInterface(system, extracted_romfs, + SizeGetter::FromStorageId(fsc, FileSys::StorageId::NandUser)); } void FSP_SRV::OpenSdCardFileSystem(HLERequestContext& ctx) { diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp index 1c9f5883b..c9743a0c9 100755 --- a/src/core/hle/service/hid/hid.cpp +++ b/src/core/hle/service/hid/hid.cpp @@ -33,7 +33,7 @@ void LoopProcess(Core::System& system) { server_manager->RegisterNamedService( "hid:dbg", std::make_shared(system, resource_manager)); server_manager->RegisterNamedService( - "hid:sys", std::make_shared(system, resource_manager)); + "hid:sys", std::make_shared(system, resource_manager, firmware_settings)); server_manager->RegisterNamedService("hidbus", std::make_shared(system)); diff --git a/src/core/hle/service/hid/hid_server.cpp b/src/core/hle/service/hid/hid_server.cpp index 30afed812..09c47b5e3 100755 --- a/src/core/hle/service/hid/hid_server.cpp +++ b/src/core/hle/service/hid/hid_server.cpp @@ -1419,8 +1419,8 @@ void IHidServer::EnableUnintendedHomeButtonInputProtection(HLERequestContext& ct const auto parameters{rp.PopRaw()}; - LOG_INFO(Service_HID, "called, is_enabled={}, npad_id={}, applet_resource_user_id={}", - parameters.is_enabled, parameters.npad_id, parameters.applet_resource_user_id); + LOG_DEBUG(Service_HID, "called, is_enabled={}, npad_id={}, applet_resource_user_id={}", + parameters.is_enabled, parameters.npad_id, parameters.applet_resource_user_id); if (!IsNpadIdValid(parameters.npad_id)) { IPC::ResponseBuilder rb{ctx, 3}; diff --git a/src/core/hle/service/hid/hid_system_server.cpp b/src/core/hle/service/hid/hid_system_server.cpp index bf27ddfbf..d1ec42edc 100755 --- a/src/core/hle/service/hid/hid_system_server.cpp +++ b/src/core/hle/service/hid/hid_system_server.cpp @@ -3,8 +3,10 @@ #include "core/hle/service/hid/hid_system_server.h" #include "core/hle/service/ipc_helpers.h" +#include "core/hle/service/set/settings_types.h" #include "hid_core/hid_result.h" #include "hid_core/resource_manager.h" +#include "hid_core/resources/hid_firmware_settings.h" #include "hid_core/resources/npad/npad.h" #include "hid_core/resources/npad/npad_types.h" #include "hid_core/resources/npad/npad_vibration.h" @@ -13,9 +15,10 @@ namespace Service::HID { -IHidSystemServer::IHidSystemServer(Core::System& system_, std::shared_ptr resource) +IHidSystemServer::IHidSystemServer(Core::System& system_, std::shared_ptr resource, + std::shared_ptr settings) : ServiceFramework{system_, "hid:sys"}, service_context{system_, service_name}, - resource_manager{resource} { + resource_manager{resource}, firmware_settings{settings} { // clang-format off static const FunctionInfo functions[] = { {31, nullptr, "SendKeyboardLockKeyEvent"}, @@ -25,7 +28,7 @@ IHidSystemServer::IHidSystemServer(Core::System& system_, std::shared_ptrGetPlatformConfig(); + + LOG_INFO(Service_HID, "called, platform_config={}", platform_config.raw); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.PushRaw(platform_config); +} + void IHidSystemServer::ApplyNpadSystemCommonPolicy(HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; const auto applet_resource_user_id{rp.Pop()}; @@ -674,6 +687,16 @@ void IHidSystemServer::EndPermitVibrationSession(HLERequestContext& ctx) { rb.Push(result); } +void IHidSystemServer::IsJoyConRailEnabled(HLERequestContext& ctx) { + const bool is_attached = true; + + LOG_WARNING(Service_HID, "(STUBBED) called, is_attached={}", is_attached); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.Push(is_attached); +} + void IHidSystemServer::IsJoyConAttachedOnAllRail(HLERequestContext& ctx) { const bool is_attached = true; @@ -727,7 +750,7 @@ void IHidSystemServer::AcquireUniquePadConnectionEventHandle(HLERequestContext& } void IHidSystemServer::GetUniquePadIds(HLERequestContext& ctx) { - LOG_WARNING(Service_HID, "(STUBBED) called"); + LOG_DEBUG(Service_HID, "(STUBBED) called"); IPC::ResponseBuilder rb{ctx, 4}; rb.Push(ResultSuccess); @@ -752,6 +775,16 @@ void IHidSystemServer::IsUsbFullKeyControllerEnabled(HLERequestContext& ctx) { rb.Push(is_enabled); } +void IHidSystemServer::EnableUsbFullKeyController(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto is_enabled{rp.Pop()}; + + LOG_WARNING(Service_HID, "(STUBBED) called, is_enabled={}", is_enabled); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + void IHidSystemServer::IsHandheldButtonPressedOnConsoleMode(HLERequestContext& ctx) { const bool button_pressed = false; @@ -798,6 +831,13 @@ void IHidSystemServer::FinalizeUsbFirmwareUpdate(HLERequestContext& ctx) { rb.Push(ResultSuccess); } +void IHidSystemServer::CheckUsbFirmwareUpdateRequired(HLERequestContext& ctx) { + LOG_WARNING(Service_HID, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + void IHidSystemServer::InitializeUsbFirmwareUpdateWithoutMemory(HLERequestContext& ctx) { LOG_WARNING(Service_HID, "(STUBBED) called"); diff --git a/src/core/hle/service/hid/hid_system_server.h b/src/core/hle/service/hid/hid_system_server.h index 90a719f02..4ab4d3931 100755 --- a/src/core/hle/service/hid/hid_system_server.h +++ b/src/core/hle/service/hid/hid_system_server.h @@ -16,13 +16,16 @@ class KEvent; namespace Service::HID { class ResourceManager; +class HidFirmwareSettings; class IHidSystemServer final : public ServiceFramework { public: - explicit IHidSystemServer(Core::System& system_, std::shared_ptr resource); + explicit IHidSystemServer(Core::System& system_, std::shared_ptr resource, + std::shared_ptr settings); ~IHidSystemServer() override; private: + void GetPlatformConfig(HLERequestContext& ctx); void ApplyNpadSystemCommonPolicy(HLERequestContext& ctx); void EnableAssigningSingleOnSlSrPress(HLERequestContext& ctx); void DisableAssigningSingleOnSlSrPress(HLERequestContext& ctx); @@ -50,6 +53,7 @@ private: void GetVibrationMasterVolume(HLERequestContext& ctx); void BeginPermitVibrationSession(HLERequestContext& ctx); void EndPermitVibrationSession(HLERequestContext& ctx); + void IsJoyConRailEnabled(HLERequestContext& ctx); void IsJoyConAttachedOnAllRail(HLERequestContext& ctx); void AcquireConnectionTriggerTimeoutEvent(HLERequestContext& ctx); void AcquireDeviceRegisteredEventForControllerSupport(HLERequestContext& ctx); @@ -58,12 +62,14 @@ private: void GetUniquePadIds(HLERequestContext& ctx); void AcquireJoyDetachOnBluetoothOffEventHandle(HLERequestContext& ctx); void IsUsbFullKeyControllerEnabled(HLERequestContext& ctx); + void EnableUsbFullKeyController(HLERequestContext& ctx); void IsHandheldButtonPressedOnConsoleMode(HLERequestContext& ctx); void InitializeFirmwareUpdate(HLERequestContext& ctx); void CheckFirmwareUpdateRequired(HLERequestContext& ctx); void SetFirmwareHotfixUpdateSkipEnabled(HLERequestContext& ctx); void InitializeUsbFirmwareUpdate(HLERequestContext& ctx); void FinalizeUsbFirmwareUpdate(HLERequestContext& ctx); + void CheckUsbFirmwareUpdateRequired(HLERequestContext& ctx); void InitializeUsbFirmwareUpdateWithoutMemory(HLERequestContext& ctx); void GetTouchScreenDefaultConfiguration(HLERequestContext& ctx); void SetForceHandheldStyleVibration(HLERequestContext& ctx); @@ -77,6 +83,7 @@ private: Kernel::KEvent* unique_pad_connection_event; KernelHelpers::ServiceContext service_context; std::shared_ptr resource_manager; + std::shared_ptr firmware_settings; }; } // namespace Service::HID diff --git a/src/core/hle/service/nifm/nifm.cpp b/src/core/hle/service/nifm/nifm.cpp index d8895a9b5..3769d5024 100755 --- a/src/core/hle/service/nifm/nifm.cpp +++ b/src/core/hle/service/nifm/nifm.cpp @@ -185,7 +185,7 @@ public: {3, &IRequest::Cancel, "Cancel"}, {4, &IRequest::Submit, "Submit"}, {5, nullptr, "SetRequirement"}, - {6, nullptr, "SetRequirementPreset"}, + {6, &IRequest::SetRequirementPreset, "SetRequirementPreset"}, {8, nullptr, "SetPriority"}, {9, nullptr, "SetNetworkProfileId"}, {10, nullptr, "SetRejectable"}, @@ -237,6 +237,16 @@ private: rb.PushEnum(state); } + void SetRequirementPreset(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto param_1 = rp.Pop(); + + LOG_WARNING(Service_NIFM, "(STUBBED) called, param_1={}", param_1); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); + } + void GetResult(HLERequestContext& ctx) { LOG_DEBUG(Service_NIFM, "(STUBBED) called"); diff --git a/src/core/hle/service/ns/ns.cpp b/src/core/hle/service/ns/ns.cpp index 20ea7aa43..85a2ec345 100755 --- a/src/core/hle/service/ns/ns.cpp +++ b/src/core/hle/service/ns/ns.cpp @@ -3,6 +3,7 @@ #include "common/logging/log.h" #include "common/settings.h" +#include "core/arm/debug.h" #include "core/core.h" #include "core/file_sys/control_metadata.h" #include "core/file_sys/patch_manager.h" @@ -544,8 +545,8 @@ IDocumentInterface::IDocumentInterface(Core::System& system_) // clang-format off static const FunctionInfo functions[] = { {21, nullptr, "GetApplicationContentPath"}, - {23, nullptr, "ResolveApplicationContentPath"}, - {93, nullptr, "GetRunningApplicationProgramId"}, + {23, &IDocumentInterface::ResolveApplicationContentPath, "ResolveApplicationContentPath"}, + {92, &IDocumentInterface::GetRunningApplicationProgramId, "GetRunningApplicationProgramId"}, }; // clang-format on @@ -554,6 +555,32 @@ IDocumentInterface::IDocumentInterface(Core::System& system_) IDocumentInterface::~IDocumentInterface() = default; +void IDocumentInterface::ResolveApplicationContentPath(HLERequestContext& ctx) { + struct ContentPath { + u8 file_system_proxy_type; + u64 program_id; + }; + static_assert(sizeof(ContentPath) == 0x10, "ContentPath has wrong size"); + + IPC::RequestParser rp{ctx}; + auto content_path = rp.PopRaw(); + LOG_WARNING(Service_NS, "(STUBBED) called, file_system_proxy_type={}, program_id={:016X}", + content_path.file_system_proxy_type, content_path.program_id); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void IDocumentInterface::GetRunningApplicationProgramId(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto caller_program_id = rp.PopRaw(); + LOG_WARNING(Service_NS, "(STUBBED) called, caller_program_id={:016X}", caller_program_id); + + IPC::ResponseBuilder rb{ctx, 4}; + rb.Push(ResultSuccess); + rb.Push(system.GetApplicationProcessProgramID()); +} + IDownloadTaskInterface::IDownloadTaskInterface(Core::System& system_) : ServiceFramework{system_, "IDownloadTaskInterface"} { // clang-format off @@ -613,6 +640,40 @@ IFactoryResetInterface::IFactoryResetInterface(Core::System& system_) IFactoryResetInterface::~IFactoryResetInterface() = default; +IReadOnlyApplicationRecordInterface::IReadOnlyApplicationRecordInterface(Core::System& system_) + : ServiceFramework{system_, "IReadOnlyApplicationRecordInterface"} { + static const FunctionInfo functions[] = { + {0, &IReadOnlyApplicationRecordInterface::HasApplicationRecord, "HasApplicationRecord"}, + {1, nullptr, "NotifyApplicationFailure"}, + {2, &IReadOnlyApplicationRecordInterface::IsDataCorruptedResult, "IsDataCorruptedResult"}, + }; + // clang-format on + + RegisterHandlers(functions); +} + +IReadOnlyApplicationRecordInterface::~IReadOnlyApplicationRecordInterface() = default; + +void IReadOnlyApplicationRecordInterface::HasApplicationRecord(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const u64 program_id = rp.PopRaw(); + LOG_WARNING(Service_NS, "(STUBBED) called, program_id={:X}", program_id); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.Push(1); +} + +void IReadOnlyApplicationRecordInterface::IsDataCorruptedResult(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto result = rp.PopRaw(); + LOG_WARNING(Service_NS, "(STUBBED) called, result={:#x}", result.GetInnerValue()); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.Push(0); +} + IReadOnlyApplicationControlDataInterface::IReadOnlyApplicationControlDataInterface( Core::System& system_) : ServiceFramework{system_, "IReadOnlyApplicationControlDataInterface"} { @@ -663,7 +724,7 @@ NS::NS(const char* name, Core::System& system_) : ServiceFramework{system_, name static const FunctionInfo functions[] = { {7988, nullptr, "GetDynamicRightsInterface"}, {7989, &NS::PushInterface, "GetReadOnlyApplicationControlDataInterface"}, - {7991, nullptr, "GetReadOnlyApplicationRecordInterface"}, + {7991, &NS::PushInterface, "GetReadOnlyApplicationRecordInterface"}, {7992, &NS::PushInterface, "GetECommerceInterface"}, {7993, &NS::PushInterface, "GetApplicationVersionInterface"}, {7994, &NS::PushInterface, "GetFactoryResetInterface"}, diff --git a/src/core/hle/service/ns/ns.h b/src/core/hle/service/ns/ns.h index e5014f842..e6bf349ba 100755 --- a/src/core/hle/service/ns/ns.h +++ b/src/core/hle/service/ns/ns.h @@ -58,6 +58,10 @@ class IDocumentInterface final : public ServiceFramework { public: explicit IDocumentInterface(Core::System& system_); ~IDocumentInterface() override; + +private: + void ResolveApplicationContentPath(HLERequestContext& ctx); + void GetRunningApplicationProgramId(HLERequestContext& ctx); }; class IDownloadTaskInterface final : public ServiceFramework { @@ -78,6 +82,17 @@ public: ~IFactoryResetInterface() override; }; +class IReadOnlyApplicationRecordInterface final + : public ServiceFramework { +public: + explicit IReadOnlyApplicationRecordInterface(Core::System& system_); + ~IReadOnlyApplicationRecordInterface() override; + +private: + void HasApplicationRecord(HLERequestContext& ctx); + void IsDataCorruptedResult(HLERequestContext& ctx); +}; + class IReadOnlyApplicationControlDataInterface final : public ServiceFramework { public: diff --git a/src/core/hle/service/nvnflinger/fb_share_buffer_manager.cpp b/src/core/hle/service/nvnflinger/fb_share_buffer_manager.cpp index 86e272b41..e71652cdf 100755 --- a/src/core/hle/service/nvnflinger/fb_share_buffer_manager.cpp +++ b/src/core/hle/service/nvnflinger/fb_share_buffer_manager.cpp @@ -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. diff --git a/src/core/hle/service/nvnflinger/nvnflinger.cpp b/src/core/hle/service/nvnflinger/nvnflinger.cpp index 71d6fdb0c..51133853c 100755 --- a/src/core/hle/service/nvnflinger/nvnflinger.cpp +++ b/src/core/hle/service/nvnflinger/nvnflinger.cpp @@ -198,6 +198,16 @@ bool Nvnflinger::CloseLayer(u64 layer_id) { return false; } +void Nvnflinger::SetLayerVisibility(u64 layer_id, bool visible) { + const auto lock_guard = Lock(); + + for (auto& display : displays) { + if (auto* layer = display.FindLayer(layer_id); layer) { + layer->SetVisibility(visible); + } + } +} + void Nvnflinger::DestroyLayer(u64 layer_id) { const auto lock_guard = Lock(); diff --git a/src/core/hle/service/nvnflinger/nvnflinger.h b/src/core/hle/service/nvnflinger/nvnflinger.h index a60e0ae6b..369439142 100755 --- a/src/core/hle/service/nvnflinger/nvnflinger.h +++ b/src/core/hle/service/nvnflinger/nvnflinger.h @@ -79,6 +79,9 @@ public: /// Closes a layer on all displays for the given layer ID. bool CloseLayer(u64 layer_id); + /// Makes a layer visible on all displays for the given layer ID. + void SetLayerVisibility(u64 layer_id, bool visible); + /// Destroys the given layer ID. void DestroyLayer(u64 layer_id); diff --git a/src/core/hle/service/set/setting_formats/system_settings.cpp b/src/core/hle/service/set/setting_formats/system_settings.cpp index ec00b90a6..88a305f03 100755 --- a/src/core/hle/service/set/setting_formats/system_settings.cpp +++ b/src/core/hle/service/set/setting_formats/system_settings.cpp @@ -50,6 +50,7 @@ SystemSettings DefaultSystemSettings() { settings.primary_album_storage = PrimaryAlbumStorage::SdCard; settings.battery_percentage_flag = true; settings.chinese_traditional_input_method = ChineseTraditionalInputMethod::Unknown0; + settings.vibration_master_volume = 1.0f; return settings; } diff --git a/src/core/hle/service/set/settings_types.h b/src/core/hle/service/set/settings_types.h index f6f227fde..968425319 100755 --- a/src/core/hle/service/set/settings_types.h +++ b/src/core/hle/service/set/settings_types.h @@ -323,6 +323,15 @@ struct NotificationFlag { }; static_assert(sizeof(NotificationFlag) == 4, "NotificationFlag is an invalid size"); +struct PlatformConfig { + union { + u32 raw{}; + BitField<0, 1, u32> has_rail_interface; + BitField<1, 1, u32> has_sio_mcu; + }; +}; +static_assert(sizeof(PlatformConfig) == 0x4, "PlatformConfig is an invalid size"); + /// This is nn::settings::system::TvFlag struct TvFlag { union { diff --git a/src/core/hle/service/set/system_settings_server.cpp b/src/core/hle/service/set/system_settings_server.cpp index b527c39a9..e907b57b6 100755 --- a/src/core/hle/service/set/system_settings_server.cpp +++ b/src/core/hle/service/set/system_settings_server.cpp @@ -123,8 +123,8 @@ ISystemSettingsServer::ISystemSettingsServer(Core::System& system_) {30, &ISystemSettingsServer::SetNotificationSettings, "SetNotificationSettings"}, {31, &ISystemSettingsServer::GetAccountNotificationSettings, "GetAccountNotificationSettings"}, {32, &ISystemSettingsServer::SetAccountNotificationSettings, "SetAccountNotificationSettings"}, - {35, nullptr, "GetVibrationMasterVolume"}, - {36, nullptr, "SetVibrationMasterVolume"}, + {35, &ISystemSettingsServer::GetVibrationMasterVolume, "GetVibrationMasterVolume"}, + {36, &ISystemSettingsServer::SetVibrationMasterVolume, "SetVibrationMasterVolume"}, {37, &ISystemSettingsServer::GetSettingsItemValueSize, "GetSettingsItemValueSize"}, {38, &ISystemSettingsServer::GetSettingsItemValue, "GetSettingsItemValue"}, {39, &ISystemSettingsServer::GetTvSettings, "GetTvSettings"}, @@ -133,10 +133,10 @@ ISystemSettingsServer::ISystemSettingsServer(Core::System& system_) {42, nullptr, "SetEdid"}, {43, nullptr, "GetAudioOutputMode"}, {44, nullptr, "SetAudioOutputMode"}, - {45, nullptr, "IsForceMuteOnHeadphoneRemoved"}, - {46, nullptr, "SetForceMuteOnHeadphoneRemoved"}, + {45, &ISystemSettingsServer::IsForceMuteOnHeadphoneRemoved, "IsForceMuteOnHeadphoneRemoved"}, + {46, &ISystemSettingsServer::SetForceMuteOnHeadphoneRemoved, "SetForceMuteOnHeadphoneRemoved"}, {47, &ISystemSettingsServer::GetQuestFlag, "GetQuestFlag"}, - {48, nullptr, "SetQuestFlag"}, + {48, &ISystemSettingsServer::SetQuestFlag, "SetQuestFlag"}, {49, nullptr, "GetDataDeletionSettings"}, {50, nullptr, "SetDataDeletionSettings"}, {51, nullptr, "GetInitialSystemAppletProgramId"}, @@ -152,7 +152,7 @@ ISystemSettingsServer::ISystemSettingsServer(Core::System& system_) {61, &ISystemSettingsServer::SetUserSystemClockAutomaticCorrectionEnabled, "SetUserSystemClockAutomaticCorrectionEnabled"}, {62, &ISystemSettingsServer::GetDebugModeFlag, "GetDebugModeFlag"}, {63, &ISystemSettingsServer::GetPrimaryAlbumStorage, "GetPrimaryAlbumStorage"}, - {64, nullptr, "SetPrimaryAlbumStorage"}, + {64, &ISystemSettingsServer::SetPrimaryAlbumStorage, "SetPrimaryAlbumStorage"}, {65, nullptr, "GetUsb30EnableFlag"}, {66, nullptr, "SetUsb30EnableFlag"}, {67, nullptr, "GetBatteryLot"}, @@ -467,7 +467,7 @@ void ISystemSettingsServer::GetExternalSteadyClockSourceId(HLERequestContext& ct LOG_INFO(Service_SET, "called"); Common::UUID id{}; - auto res = GetExternalSteadyClockSourceId(id); + const auto res = GetExternalSteadyClockSourceId(id); IPC::ResponseBuilder rb{ctx, 2 + sizeof(Common::UUID) / sizeof(u32)}; rb.Push(res); @@ -478,9 +478,9 @@ void ISystemSettingsServer::SetExternalSteadyClockSourceId(HLERequestContext& ct LOG_INFO(Service_SET, "called"); IPC::RequestParser rp{ctx}; - auto id{rp.PopRaw()}; + const auto id{rp.PopRaw()}; - auto res = SetExternalSteadyClockSourceId(id); + const auto res = SetExternalSteadyClockSourceId(id); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(res); @@ -490,7 +490,7 @@ void ISystemSettingsServer::GetUserSystemClockContext(HLERequestContext& ctx) { LOG_INFO(Service_SET, "called"); Service::PSC::Time::SystemClockContext context{}; - auto res = GetUserSystemClockContext(context); + const auto res = GetUserSystemClockContext(context); IPC::ResponseBuilder rb{ctx, 2 + sizeof(Service::PSC::Time::SystemClockContext) / sizeof(u32)}; rb.Push(res); @@ -501,9 +501,9 @@ void ISystemSettingsServer::SetUserSystemClockContext(HLERequestContext& ctx) { LOG_INFO(Service_SET, "called"); IPC::RequestParser rp{ctx}; - auto context{rp.PopRaw()}; + const auto context{rp.PopRaw()}; - auto res = SetUserSystemClockContext(context); + const auto res = SetUserSystemClockContext(context); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(res); @@ -652,6 +652,29 @@ void ISystemSettingsServer::SetAccountNotificationSettings(HLERequestContext& ct rb.Push(ResultSuccess); } +void ISystemSettingsServer::GetVibrationMasterVolume(HLERequestContext& ctx) { + f32 vibration_master_volume = {}; + const auto result = GetVibrationMasterVolume(vibration_master_volume); + + LOG_INFO(Service_SET, "called, master_volume={}", vibration_master_volume); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(result); + rb.Push(vibration_master_volume); +} + +void ISystemSettingsServer::SetVibrationMasterVolume(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto vibration_master_volume = rp.PopRaw(); + + LOG_INFO(Service_SET, "called, elements={}", m_system_settings.vibration_master_volume); + + const auto result = SetVibrationMasterVolume(vibration_master_volume); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(result); +} + // FIXME: implement support for the real system_settings.ini template @@ -683,6 +706,8 @@ static Settings GetSettings() { ret["time"]["standard_user_clock_initial_year"] = ToBytes(s32{2023}); // HID + ret["hid"]["has_rail_interface"] = ToBytes(bool{true}); + ret["hid"]["has_sio_mcu"] = ToBytes(bool{true}); ret["hid_debug"]["enables_debugpad"] = ToBytes(bool{true}); ret["hid_debug"]["manages_devices"] = ToBytes(bool{true}); ret["hid_debug"]["manages_touch_ic_i2c"] = ToBytes(bool{true}); @@ -700,6 +725,9 @@ static Settings GetSettings() { // Settings ret["settings_debug"]["is_debug_mode_enabled"] = ToBytes(bool{false}); + // Error + ret["err"]["applet_auto_close"] = ToBytes(bool{false}); + return ret; } @@ -786,15 +814,25 @@ void ISystemSettingsServer::SetTvSettings(HLERequestContext& ctx) { rb.Push(ResultSuccess); } -void ISystemSettingsServer::GetDebugModeFlag(HLERequestContext& ctx) { - bool is_debug_mode_enabled = false; - GetSettingsItemValue(is_debug_mode_enabled, "settings_debug", "is_debug_mode_enabled"); - - LOG_DEBUG(Service_SET, "called, is_debug_mode_enabled={}", is_debug_mode_enabled); +void ISystemSettingsServer::IsForceMuteOnHeadphoneRemoved(HLERequestContext& ctx) { + LOG_INFO(Service_SET, "called, force_mute_on_headphone_removed={}", + m_system_settings.force_mute_on_headphone_removed); IPC::ResponseBuilder rb{ctx, 3}; rb.Push(ResultSuccess); - rb.Push(is_debug_mode_enabled); + rb.PushRaw(m_system_settings.force_mute_on_headphone_removed); +} + +void ISystemSettingsServer::SetForceMuteOnHeadphoneRemoved(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + m_system_settings.force_mute_on_headphone_removed = rp.PopRaw(); + SetSaveNeeded(); + + LOG_INFO(Service_SET, "called, force_mute_on_headphone_removed={}", + m_system_settings.force_mute_on_headphone_removed); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); } void ISystemSettingsServer::GetQuestFlag(HLERequestContext& ctx) { @@ -805,11 +843,22 @@ void ISystemSettingsServer::GetQuestFlag(HLERequestContext& ctx) { rb.PushEnum(m_system_settings.quest_flag); } +void ISystemSettingsServer::SetQuestFlag(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + m_system_settings.quest_flag = rp.PopEnum(); + SetSaveNeeded(); + + LOG_INFO(Service_SET, "called, quest_flag={}", m_system_settings.quest_flag); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + void ISystemSettingsServer::GetDeviceTimeZoneLocationName(HLERequestContext& ctx) { LOG_INFO(Service_SET, "called"); Service::PSC::Time::LocationName name{}; - auto res = GetDeviceTimeZoneLocationName(name); + const auto res = GetDeviceTimeZoneLocationName(name); IPC::ResponseBuilder rb{ctx, 2 + sizeof(Service::PSC::Time::LocationName) / sizeof(u32)}; rb.Push(res); @@ -822,7 +871,7 @@ void ISystemSettingsServer::SetDeviceTimeZoneLocationName(HLERequestContext& ctx IPC::RequestParser rp{ctx}; auto name{rp.PopRaw()}; - auto res = SetDeviceTimeZoneLocationName(name); + const auto res = SetDeviceTimeZoneLocationName(name); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(res); @@ -843,7 +892,7 @@ void ISystemSettingsServer::GetNetworkSystemClockContext(HLERequestContext& ctx) LOG_INFO(Service_SET, "called"); Service::PSC::Time::SystemClockContext context{}; - auto res = GetNetworkSystemClockContext(context); + const auto res = GetNetworkSystemClockContext(context); IPC::ResponseBuilder rb{ctx, 2 + sizeof(Service::PSC::Time::SystemClockContext) / sizeof(u32)}; rb.Push(res); @@ -854,9 +903,9 @@ void ISystemSettingsServer::SetNetworkSystemClockContext(HLERequestContext& ctx) LOG_INFO(Service_SET, "called"); IPC::RequestParser rp{ctx}; - auto context{rp.PopRaw()}; + const auto context{rp.PopRaw()}; - auto res = SetNetworkSystemClockContext(context); + const auto res = SetNetworkSystemClockContext(context); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(res); @@ -866,7 +915,7 @@ void ISystemSettingsServer::IsUserSystemClockAutomaticCorrectionEnabled(HLEReque LOG_INFO(Service_SET, "called"); bool enabled{}; - auto res = IsUserSystemClockAutomaticCorrectionEnabled(enabled); + const auto res = IsUserSystemClockAutomaticCorrectionEnabled(enabled); IPC::ResponseBuilder rb{ctx, 3}; rb.Push(res); @@ -879,12 +928,23 @@ void ISystemSettingsServer::SetUserSystemClockAutomaticCorrectionEnabled(HLERequ IPC::RequestParser rp{ctx}; auto enabled{rp.Pop()}; - auto res = SetUserSystemClockAutomaticCorrectionEnabled(enabled); + const auto res = SetUserSystemClockAutomaticCorrectionEnabled(enabled); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(res); } +void ISystemSettingsServer::GetDebugModeFlag(HLERequestContext& ctx) { + bool is_debug_mode_enabled = false; + GetSettingsItemValue(is_debug_mode_enabled, "settings_debug", "is_debug_mode_enabled"); + + LOG_DEBUG(Service_SET, "called, is_debug_mode_enabled={}", is_debug_mode_enabled); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.Push(is_debug_mode_enabled); +} + void ISystemSettingsServer::GetPrimaryAlbumStorage(HLERequestContext& ctx) { LOG_INFO(Service_SET, "called, primary_album_storage={}", m_system_settings.primary_album_storage); @@ -894,6 +954,18 @@ void ISystemSettingsServer::GetPrimaryAlbumStorage(HLERequestContext& ctx) { rb.PushEnum(m_system_settings.primary_album_storage); } +void ISystemSettingsServer::SetPrimaryAlbumStorage(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + m_system_settings.primary_album_storage = rp.PopEnum(); + SetSaveNeeded(); + + LOG_INFO(Service_SET, "called, primary_album_storage={}", + m_system_settings.primary_album_storage); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + void ISystemSettingsServer::GetNfcEnableFlag(HLERequestContext& ctx) { LOG_INFO(Service_SET, "called, nfc_enable_flag={}", m_system_settings.nfc_enable_flag); @@ -1072,7 +1144,7 @@ void ISystemSettingsServer::SetExternalSteadyClockInternalOffset(HLERequestConte IPC::RequestParser rp{ctx}; auto offset{rp.Pop()}; - auto res = SetExternalSteadyClockInternalOffset(offset); + const auto res = SetExternalSteadyClockInternalOffset(offset); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(res); @@ -1082,7 +1154,7 @@ void ISystemSettingsServer::GetExternalSteadyClockInternalOffset(HLERequestConte LOG_DEBUG(Service_SET, "called."); s64 offset{}; - auto res = GetExternalSteadyClockInternalOffset(offset); + const auto res = GetExternalSteadyClockInternalOffset(offset); IPC::ResponseBuilder rb{ctx, 4}; rb.Push(res); @@ -1140,7 +1212,7 @@ void ISystemSettingsServer::GetDeviceTimeZoneLocationUpdatedTime(HLERequestConte LOG_INFO(Service_SET, "called"); Service::PSC::Time::SteadyClockTimePoint time_point{}; - auto res = GetDeviceTimeZoneLocationUpdatedTime(time_point); + const auto res = GetDeviceTimeZoneLocationUpdatedTime(time_point); IPC::ResponseBuilder rb{ctx, 4}; rb.Push(res); @@ -1153,7 +1225,7 @@ void ISystemSettingsServer::SetDeviceTimeZoneLocationUpdatedTime(HLERequestConte IPC::RequestParser rp{ctx}; auto time_point{rp.PopRaw()}; - auto res = SetDeviceTimeZoneLocationUpdatedTime(time_point); + const auto res = SetDeviceTimeZoneLocationUpdatedTime(time_point); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(res); @@ -1164,7 +1236,7 @@ void ISystemSettingsServer::GetUserSystemClockAutomaticCorrectionUpdatedTime( LOG_INFO(Service_SET, "called"); Service::PSC::Time::SteadyClockTimePoint time_point{}; - auto res = GetUserSystemClockAutomaticCorrectionUpdatedTime(time_point); + const auto res = GetUserSystemClockAutomaticCorrectionUpdatedTime(time_point); IPC::ResponseBuilder rb{ctx, 4}; rb.Push(res); @@ -1176,9 +1248,9 @@ void ISystemSettingsServer::SetUserSystemClockAutomaticCorrectionUpdatedTime( LOG_INFO(Service_SET, "called"); IPC::RequestParser rp{ctx}; - auto time_point{rp.PopRaw()}; + const auto time_point{rp.PopRaw()}; - auto res = SetUserSystemClockAutomaticCorrectionUpdatedTime(time_point); + const auto res = SetUserSystemClockAutomaticCorrectionUpdatedTime(time_point); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(res); @@ -1304,57 +1376,68 @@ Result ISystemSettingsServer::GetSettingsItemValue(std::vector& out_value, R_SUCCEED(); } -Result ISystemSettingsServer::GetExternalSteadyClockSourceId(Common::UUID& out_id) { +Result ISystemSettingsServer::GetVibrationMasterVolume(f32& out_volume) const { + out_volume = m_system_settings.vibration_master_volume; + R_SUCCEED(); +} + +Result ISystemSettingsServer::SetVibrationMasterVolume(f32 volume) { + m_system_settings.vibration_master_volume = volume; + SetSaveNeeded(); + R_SUCCEED(); +} + +Result ISystemSettingsServer::GetExternalSteadyClockSourceId(Common::UUID& out_id) const { out_id = m_private_settings.external_clock_source_id; R_SUCCEED(); } -Result ISystemSettingsServer::SetExternalSteadyClockSourceId(Common::UUID id) { +Result ISystemSettingsServer::SetExternalSteadyClockSourceId(const Common::UUID& id) { m_private_settings.external_clock_source_id = id; SetSaveNeeded(); R_SUCCEED(); } Result ISystemSettingsServer::GetUserSystemClockContext( - Service::PSC::Time::SystemClockContext& out_context) { + Service::PSC::Time::SystemClockContext& out_context) const { out_context = m_system_settings.user_system_clock_context; R_SUCCEED(); } Result ISystemSettingsServer::SetUserSystemClockContext( - Service::PSC::Time::SystemClockContext& context) { + const Service::PSC::Time::SystemClockContext& context) { m_system_settings.user_system_clock_context = context; SetSaveNeeded(); R_SUCCEED(); } Result ISystemSettingsServer::GetDeviceTimeZoneLocationName( - Service::PSC::Time::LocationName& out_name) { + Service::PSC::Time::LocationName& out_name) const { out_name = m_system_settings.device_time_zone_location_name; R_SUCCEED(); } Result ISystemSettingsServer::SetDeviceTimeZoneLocationName( - Service::PSC::Time::LocationName& name) { + const Service::PSC::Time::LocationName& name) { m_system_settings.device_time_zone_location_name = name; SetSaveNeeded(); R_SUCCEED(); } Result ISystemSettingsServer::GetNetworkSystemClockContext( - Service::PSC::Time::SystemClockContext& out_context) { + Service::PSC::Time::SystemClockContext& out_context) const { out_context = m_system_settings.network_system_clock_context; R_SUCCEED(); } Result ISystemSettingsServer::SetNetworkSystemClockContext( - Service::PSC::Time::SystemClockContext& context) { + const Service::PSC::Time::SystemClockContext& context) { m_system_settings.network_system_clock_context = context; SetSaveNeeded(); R_SUCCEED(); } -Result ISystemSettingsServer::IsUserSystemClockAutomaticCorrectionEnabled(bool& out_enabled) { +Result ISystemSettingsServer::IsUserSystemClockAutomaticCorrectionEnabled(bool& out_enabled) const { out_enabled = m_system_settings.user_system_clock_automatic_correction_enabled; R_SUCCEED(); } @@ -1371,32 +1454,32 @@ Result ISystemSettingsServer::SetExternalSteadyClockInternalOffset(s64 offset) { R_SUCCEED(); } -Result ISystemSettingsServer::GetExternalSteadyClockInternalOffset(s64& out_offset) { +Result ISystemSettingsServer::GetExternalSteadyClockInternalOffset(s64& out_offset) const { out_offset = m_private_settings.external_steady_clock_internal_offset; R_SUCCEED(); } Result ISystemSettingsServer::GetDeviceTimeZoneLocationUpdatedTime( - Service::PSC::Time::SteadyClockTimePoint& out_time_point) { + Service::PSC::Time::SteadyClockTimePoint& out_time_point) const { out_time_point = m_system_settings.device_time_zone_location_updated_time; R_SUCCEED(); } Result ISystemSettingsServer::SetDeviceTimeZoneLocationUpdatedTime( - Service::PSC::Time::SteadyClockTimePoint& time_point) { + const Service::PSC::Time::SteadyClockTimePoint& time_point) { m_system_settings.device_time_zone_location_updated_time = time_point; SetSaveNeeded(); R_SUCCEED(); } Result ISystemSettingsServer::GetUserSystemClockAutomaticCorrectionUpdatedTime( - Service::PSC::Time::SteadyClockTimePoint& out_time_point) { + Service::PSC::Time::SteadyClockTimePoint& out_time_point) const { out_time_point = m_system_settings.user_system_clock_automatic_correction_updated_time_point; R_SUCCEED(); } Result ISystemSettingsServer::SetUserSystemClockAutomaticCorrectionUpdatedTime( - Service::PSC::Time::SteadyClockTimePoint out_time_point) { + const Service::PSC::Time::SteadyClockTimePoint& out_time_point) { m_system_settings.user_system_clock_automatic_correction_updated_time_point = out_time_point; SetSaveNeeded(); R_SUCCEED(); diff --git a/src/core/hle/service/set/system_settings_server.h b/src/core/hle/service/set/system_settings_server.h index a2258d16d..acbda8b8c 100755 --- a/src/core/hle/service/set/system_settings_server.h +++ b/src/core/hle/service/set/system_settings_server.h @@ -48,26 +48,28 @@ public: return result; } - Result GetExternalSteadyClockSourceId(Common::UUID& out_id); - Result SetExternalSteadyClockSourceId(Common::UUID id); - Result GetUserSystemClockContext(Service::PSC::Time::SystemClockContext& out_context); - Result SetUserSystemClockContext(Service::PSC::Time::SystemClockContext& context); - Result GetDeviceTimeZoneLocationName(Service::PSC::Time::LocationName& out_name); - Result SetDeviceTimeZoneLocationName(Service::PSC::Time::LocationName& name); - Result GetNetworkSystemClockContext(Service::PSC::Time::SystemClockContext& out_context); - Result SetNetworkSystemClockContext(Service::PSC::Time::SystemClockContext& context); - Result IsUserSystemClockAutomaticCorrectionEnabled(bool& out_enabled); + Result GetVibrationMasterVolume(f32& out_volume) const; + Result SetVibrationMasterVolume(f32 volume); + Result GetExternalSteadyClockSourceId(Common::UUID& out_id) const; + Result SetExternalSteadyClockSourceId(const Common::UUID& id); + Result GetUserSystemClockContext(Service::PSC::Time::SystemClockContext& out_context) const; + Result SetUserSystemClockContext(const Service::PSC::Time::SystemClockContext& context); + Result GetDeviceTimeZoneLocationName(Service::PSC::Time::LocationName& out_name) const; + Result SetDeviceTimeZoneLocationName(const Service::PSC::Time::LocationName& name); + Result GetNetworkSystemClockContext(Service::PSC::Time::SystemClockContext& out_context) const; + Result SetNetworkSystemClockContext(const Service::PSC::Time::SystemClockContext& context); + Result IsUserSystemClockAutomaticCorrectionEnabled(bool& out_enabled) const; Result SetUserSystemClockAutomaticCorrectionEnabled(bool enabled); Result SetExternalSteadyClockInternalOffset(s64 offset); - Result GetExternalSteadyClockInternalOffset(s64& out_offset); + Result GetExternalSteadyClockInternalOffset(s64& out_offset) const; Result GetDeviceTimeZoneLocationUpdatedTime( - Service::PSC::Time::SteadyClockTimePoint& out_time_point); + Service::PSC::Time::SteadyClockTimePoint& out_time_point) const; Result SetDeviceTimeZoneLocationUpdatedTime( - Service::PSC::Time::SteadyClockTimePoint& time_point); + const Service::PSC::Time::SteadyClockTimePoint& time_point); Result GetUserSystemClockAutomaticCorrectionUpdatedTime( - Service::PSC::Time::SteadyClockTimePoint& out_time_point); + Service::PSC::Time::SteadyClockTimePoint& out_time_point) const; Result SetUserSystemClockAutomaticCorrectionUpdatedTime( - Service::PSC::Time::SteadyClockTimePoint time_point); + const Service::PSC::Time::SteadyClockTimePoint& time_point); private: void SetLanguageCode(HLERequestContext& ctx); @@ -89,12 +91,17 @@ private: void SetNotificationSettings(HLERequestContext& ctx); void GetAccountNotificationSettings(HLERequestContext& ctx); void SetAccountNotificationSettings(HLERequestContext& ctx); + void GetVibrationMasterVolume(HLERequestContext& ctx); + void SetVibrationMasterVolume(HLERequestContext& ctx); void GetSettingsItemValueSize(HLERequestContext& ctx); void GetSettingsItemValue(HLERequestContext& ctx); void GetTvSettings(HLERequestContext& ctx); void SetTvSettings(HLERequestContext& ctx); + void IsForceMuteOnHeadphoneRemoved(HLERequestContext& ctx); + void SetForceMuteOnHeadphoneRemoved(HLERequestContext& ctx); void GetDebugModeFlag(HLERequestContext& ctx); void GetQuestFlag(HLERequestContext& ctx); + void SetQuestFlag(HLERequestContext& ctx); void GetDeviceTimeZoneLocationName(HLERequestContext& ctx); void SetDeviceTimeZoneLocationName(HLERequestContext& ctx); void SetRegionCode(HLERequestContext& ctx); @@ -103,6 +110,7 @@ private: void IsUserSystemClockAutomaticCorrectionEnabled(HLERequestContext& ctx); void SetUserSystemClockAutomaticCorrectionEnabled(HLERequestContext& ctx); void GetPrimaryAlbumStorage(HLERequestContext& ctx); + void SetPrimaryAlbumStorage(HLERequestContext& ctx); void GetNfcEnableFlag(HLERequestContext& ctx); void SetNfcEnableFlag(HLERequestContext& ctx); void GetSleepSettings(HLERequestContext& ctx); diff --git a/src/core/hle/service/vi/display/vi_display.cpp b/src/core/hle/service/vi/display/vi_display.cpp index 0f49db4ab..181f5cc84 100755 --- a/src/core/hle/service/vi/display/vi_display.cpp +++ b/src/core/hle/service/vi/display/vi_display.cpp @@ -53,7 +53,7 @@ Display::~Display() { Layer& Display::GetLayer(std::size_t index) { size_t i = 0; for (auto& layer : layers) { - if (!layer->IsOpen()) { + if (!layer->IsOpen() || !layer->IsVisible()) { continue; } @@ -68,7 +68,7 @@ Layer& Display::GetLayer(std::size_t index) { } size_t Display::GetNumLayers() const { - return std::ranges::count_if(layers, [](auto& l) { return l->IsOpen(); }); + return std::ranges::count_if(layers, [](auto& l) { return l->IsOpen() && l->IsVisible(); }); } Kernel::KReadableEvent* Display::GetVSyncEvent() { diff --git a/src/core/hle/service/vi/layer/vi_layer.cpp b/src/core/hle/service/vi/layer/vi_layer.cpp index 2947e74ed..0185d187d 100755 --- a/src/core/hle/service/vi/layer/vi_layer.cpp +++ b/src/core/hle/service/vi/layer/vi_layer.cpp @@ -9,7 +9,7 @@ Layer::Layer(u64 layer_id_, u32 binder_id_, android::BufferQueueCore& core_, android::BufferQueueProducer& binder_, std::shared_ptr&& consumer_) : layer_id{layer_id_}, binder_id{binder_id_}, core{core_}, binder{binder_}, - consumer{std::move(consumer_)}, open{false} {} + consumer{std::move(consumer_)}, open{false}, visible{true} {} Layer::~Layer() = default; diff --git a/src/core/hle/service/vi/layer/vi_layer.h b/src/core/hle/service/vi/layer/vi_layer.h index 51e859398..859346eab 100755 --- a/src/core/hle/service/vi/layer/vi_layer.h +++ b/src/core/hle/service/vi/layer/vi_layer.h @@ -72,6 +72,14 @@ public: return core; } + bool IsVisible() const { + return visible; + } + + void SetVisibility(bool v) { + visible = v; + } + bool IsOpen() const { return open; } @@ -91,6 +99,7 @@ private: android::BufferQueueProducer& binder; std::shared_ptr consumer; bool open; + bool visible; }; } // namespace Service::VI diff --git a/src/core/hle/service/vi/vi.cpp b/src/core/hle/service/vi/vi.cpp index cfa076c6d..9b4393972 100755 --- a/src/core/hle/service/vi/vi.cpp +++ b/src/core/hle/service/vi/vi.cpp @@ -535,6 +535,12 @@ public: RegisterHandlers(functions); } + ~IApplicationDisplayService() { + for (const auto layer_id : stray_layer_ids) { + nvnflinger.DestroyLayer(layer_id); + } + } + private: enum class ConvertedScaleMode : u64 { Freeze = 0, @@ -770,6 +776,7 @@ private: return; } + stray_layer_ids.push_back(*layer_id); const auto buffer_queue_id = nvnflinger.FindBufferQueueId(display_id, *layer_id); if (!buffer_queue_id) { LOG_ERROR(Service_VI, "Buffer queue id not found! display_id={}", display_id); @@ -916,6 +923,7 @@ private: Nvnflinger::Nvnflinger& nvnflinger; Nvnflinger::HosBinderDriverServer& hos_binder_driver_server; + std::vector stray_layer_ids; bool vsync_event_fetched{false}; }; diff --git a/src/hid_core/resource_manager.cpp b/src/hid_core/resource_manager.cpp index ca824b4a3..a2295219a 100755 --- a/src/hid_core/resource_manager.cpp +++ b/src/hid_core/resource_manager.cpp @@ -6,6 +6,8 @@ #include "core/core_timing.h" #include "core/hle/kernel/k_shared_memory.h" #include "core/hle/service/ipc_helpers.h" +#include "core/hle/service/set/system_settings_server.h" +#include "core/hle/service/sm/sm.h" #include "hid_core/hid_core.h" #include "hid_core/hid_util.h" #include "hid_core/resource_manager.h" @@ -180,7 +182,11 @@ void ResourceManager::InitializeHidCommonSampler() { debug_pad->SetAppletResource(applet_resource, &shared_mutex); digitizer->SetAppletResource(applet_resource, &shared_mutex); keyboard->SetAppletResource(applet_resource, &shared_mutex); - npad->SetNpadExternals(applet_resource, &shared_mutex, handheld_config); + + const auto settings = + system.ServiceManager().GetService("set:sys"); + npad->SetNpadExternals(applet_resource, &shared_mutex, handheld_config, settings); + six_axis->SetAppletResource(applet_resource, &shared_mutex); mouse->SetAppletResource(applet_resource, &shared_mutex); debug_mouse->SetAppletResource(applet_resource, &shared_mutex); @@ -373,6 +379,10 @@ Result ResourceManager::SendVibrationValue(u64 aruid, device = GetNSVibrationDevice(handle); } if (device != nullptr) { + // Prevent sending vibrations to an inactive vibration handle + if (!device->IsActive()) { + return ResultSuccess; + } result = device->SendVibrationValue(value); } return result; diff --git a/src/hid_core/resources/hid_firmware_settings.cpp b/src/hid_core/resources/hid_firmware_settings.cpp index 00ceff7e6..9c9019e8f 100755 --- a/src/hid_core/resources/hid_firmware_settings.cpp +++ b/src/hid_core/resources/hid_firmware_settings.cpp @@ -40,6 +40,13 @@ void HidFirmwareSettings::LoadSettings(bool reload_config) { m_set_sys->GetSettingsItemValue(is_touch_firmware_auto_update_disabled, "hid_debug", "touch_firmware_auto_update_disabled"); + bool has_rail_interface{}; + bool has_sio_mcu{}; + m_set_sys->GetSettingsItemValue(has_rail_interface, "hid", "has_rail_interface"); + m_set_sys->GetSettingsItemValue(has_sio_mcu, "hid", "has_sio_mcu"); + platform_config.has_rail_interface.Assign(has_rail_interface); + platform_config.has_sio_mcu.Assign(has_sio_mcu); + is_initialized = true; } @@ -103,4 +110,9 @@ HidFirmwareSettings::FeaturesPerId HidFirmwareSettings::FeaturesDisabledPerId() return features_per_id_disabled; } +Set::PlatformConfig HidFirmwareSettings::GetPlatformConfig() { + LoadSettings(false); + return platform_config; +} + } // namespace Service::HID diff --git a/src/hid_core/resources/hid_firmware_settings.h b/src/hid_core/resources/hid_firmware_settings.h index 3694fa9a3..7f146f1e6 100755 --- a/src/hid_core/resources/hid_firmware_settings.h +++ b/src/hid_core/resources/hid_firmware_settings.h @@ -4,6 +4,7 @@ #pragma once #include "common/common_types.h" +#include "core/hle/service/set/settings_types.h" namespace Core { class System; @@ -39,6 +40,7 @@ public: FirmwareSetting GetFirmwareUpdateFailure(); FeaturesPerId FeaturesDisabledPerId(); + Set::PlatformConfig GetPlatformConfig(); private: bool is_initialized{}; @@ -57,6 +59,7 @@ private: bool is_touch_firmware_auto_update_disabled{}; FirmwareSetting is_firmware_update_failure{}; FeaturesPerId features_per_id_disabled{}; + Set::PlatformConfig platform_config{}; std::shared_ptr m_set_sys; }; diff --git a/src/hid_core/resources/npad/npad.cpp b/src/hid_core/resources/npad/npad.cpp index d13a489c9..cde84b1bb 100755 --- a/src/hid_core/resources/npad/npad.cpp +++ b/src/hid_core/resources/npad/npad.cpp @@ -1080,12 +1080,15 @@ void NPad::UnregisterAppletResourceUserId(u64 aruid) { void NPad::SetNpadExternals(std::shared_ptr resource, std::recursive_mutex* shared_mutex, - std::shared_ptr handheld_config) { + std::shared_ptr handheld_config, + std::shared_ptr settings) { applet_resource_holder.applet_resource = resource; applet_resource_holder.shared_mutex = shared_mutex; applet_resource_holder.shared_npad_resource = &npad_resource; applet_resource_holder.handheld_config = handheld_config; + vibration_handler.SetSettingsService(settings); + for (auto& abstract_pad : abstracted_pads) { abstract_pad.SetExternals(&applet_resource_holder, nullptr, nullptr, nullptr, nullptr, &vibration_handler, &hid_core); diff --git a/src/hid_core/resources/npad/npad.h b/src/hid_core/resources/npad/npad.h index 88289fa2b..502cb9b55 100755 --- a/src/hid_core/resources/npad/npad.h +++ b/src/hid_core/resources/npad/npad.h @@ -34,6 +34,10 @@ namespace Service::KernelHelpers { class ServiceContext; } // namespace Service::KernelHelpers +namespace Service::Set { +class ISystemSettingsServer; +} + union Result; namespace Service::HID { @@ -128,7 +132,8 @@ public: void UnregisterAppletResourceUserId(u64 aruid); void SetNpadExternals(std::shared_ptr resource, std::recursive_mutex* shared_mutex, - std::shared_ptr handheld_config); + std::shared_ptr handheld_config, + std::shared_ptr settings); AppletDetailedUiType GetAppletDetailedUiType(Core::HID::NpadIdType npad_id); diff --git a/src/hid_core/resources/npad/npad_vibration.cpp b/src/hid_core/resources/npad/npad_vibration.cpp index 05aad4c54..02b1f0290 100755 --- a/src/hid_core/resources/npad/npad_vibration.cpp +++ b/src/hid_core/resources/npad/npad_vibration.cpp @@ -1,6 +1,7 @@ // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later +#include "core/hle/service/set/system_settings_server.h" #include "hid_core/hid_result.h" #include "hid_core/resources/npad/npad_vibration.h" @@ -13,10 +14,11 @@ NpadVibration::~NpadVibration() = default; Result NpadVibration::Activate() { std::scoped_lock lock{mutex}; - const f32 master_volume = 1.0f; // nn::settings::system::GetVibrationMasterVolume(); - // if (master_volume < 0.0f || master_volume > 1.0f) { - // return ResultVibrationStrengthOutOfRange; - // } + f32 master_volume = 1.0f; + m_set_sys->GetVibrationMasterVolume(master_volume); + if (master_volume < 0.0f || master_volume > 1.0f) { + return ResultVibrationStrengthOutOfRange; + } volume = master_volume; return ResultSuccess; @@ -26,6 +28,12 @@ Result NpadVibration::Deactivate() { return ResultSuccess; } +Result NpadVibration::SetSettingsService( + std::shared_ptr settings) { + m_set_sys = settings; + return ResultSuccess; +} + Result NpadVibration::SetVibrationMasterVolume(f32 master_volume) { std::scoped_lock lock{mutex}; @@ -34,7 +42,7 @@ Result NpadVibration::SetVibrationMasterVolume(f32 master_volume) { } volume = master_volume; - // nn::settings::system::SetVibrationMasterVolume(master_volume); + m_set_sys->SetVibrationMasterVolume(master_volume); return ResultSuccess; } @@ -48,10 +56,11 @@ Result NpadVibration::GetVibrationVolume(f32& out_volume) const { Result NpadVibration::GetVibrationMasterVolume(f32& out_volume) const { std::scoped_lock lock{mutex}; - const f32 master_volume = 1.0f; // nn::settings::system::GetVibrationMasterVolume(); - // if (master_volume < 0.0f || master_volume > 1.0f) { - // return ResultVibrationStrengthOutOfRange; - // } + f32 master_volume = 1.0f; + m_set_sys->GetVibrationMasterVolume(master_volume); + if (master_volume < 0.0f || master_volume > 1.0f) { + return ResultVibrationStrengthOutOfRange; + } out_volume = master_volume; return ResultSuccess; @@ -67,10 +76,11 @@ Result NpadVibration::BeginPermitVibrationSession(u64 aruid) { Result NpadVibration::EndPermitVibrationSession() { std::scoped_lock lock{mutex}; - const f32 master_volume = 1.0f; // nn::settings::system::GetVibrationMasterVolume(); - // if (master_volume < 0.0f || master_volume > 1.0f) { - // return ResultVibrationStrengthOutOfRange; - // } + f32 master_volume = 1.0f; + m_set_sys->GetVibrationMasterVolume(master_volume); + if (master_volume < 0.0f || master_volume > 1.0f) { + return ResultVibrationStrengthOutOfRange; + } volume = master_volume; session_aruid = 0; diff --git a/src/hid_core/resources/npad/npad_vibration.h b/src/hid_core/resources/npad/npad_vibration.h index d5a95f2a0..6412ca4ab 100755 --- a/src/hid_core/resources/npad/npad_vibration.h +++ b/src/hid_core/resources/npad/npad_vibration.h @@ -8,6 +8,10 @@ #include "common/common_types.h" #include "core/hle/result.h" +namespace Service::Set { +class ISystemSettingsServer; +} + namespace Service::HID { class NpadVibration final { @@ -18,6 +22,7 @@ public: Result Activate(); Result Deactivate(); + Result SetSettingsService(std::shared_ptr settings); Result SetVibrationMasterVolume(f32 master_volume); Result GetVibrationVolume(f32& out_volume) const; Result GetVibrationMasterVolume(f32& out_volume) const; @@ -31,6 +36,8 @@ private: f32 volume{}; u64 session_aruid{}; mutable std::mutex mutex; + + std::shared_ptr m_set_sys; }; } // namespace Service::HID diff --git a/src/hid_core/resources/vibration/vibration_base.cpp b/src/hid_core/resources/vibration/vibration_base.cpp index f28d30406..90bff88f4 100755 --- a/src/hid_core/resources/vibration/vibration_base.cpp +++ b/src/hid_core/resources/vibration/vibration_base.cpp @@ -23,6 +23,10 @@ Result NpadVibrationBase::Deactivate() { return ResultSuccess; } +bool NpadVibrationBase::IsActive() const { + return ref_counter > 0; +} + bool NpadVibrationBase::IsVibrationMounted() const { return is_mounted; } diff --git a/src/hid_core/resources/vibration/vibration_base.h b/src/hid_core/resources/vibration/vibration_base.h index 69c26e669..8fe35634d 100755 --- a/src/hid_core/resources/vibration/vibration_base.h +++ b/src/hid_core/resources/vibration/vibration_base.h @@ -21,6 +21,7 @@ public: virtual Result Activate(); virtual Result Deactivate(); + bool IsActive() const; bool IsVibrationMounted() const; protected: diff --git a/src/yuzu/applets/qt_profile_select.cpp b/src/yuzu/applets/qt_profile_select.cpp index 75ddb4924..c686766ec 100755 --- a/src/yuzu/applets/qt_profile_select.cpp +++ b/src/yuzu/applets/qt_profile_select.cpp @@ -162,7 +162,7 @@ void QtProfileSelectionDialog::SelectUser(const QModelIndex& index) { void QtProfileSelectionDialog::SetWindowTitle( const Core::Frontend::ProfileSelectParameters& parameters) { - using Service::AM::Applets::UiMode; + using Service::AM::Frontend::UiMode; switch (parameters.mode) { case UiMode::UserCreator: case UiMode::UserCreatorForStarter: @@ -193,7 +193,7 @@ void QtProfileSelectionDialog::SetWindowTitle( void QtProfileSelectionDialog::SetDialogPurpose( const Core::Frontend::ProfileSelectParameters& parameters) { - using Service::AM::Applets::UserSelectionPurpose; + using Service::AM::Frontend::UserSelectionPurpose; switch (parameters.purpose) { case UserSelectionPurpose::GameCardRegistration: diff --git a/src/yuzu/applets/qt_software_keyboard.cpp b/src/yuzu/applets/qt_software_keyboard.cpp index 53bcef7c2..443c612a4 100755 --- a/src/yuzu/applets/qt_software_keyboard.cpp +++ b/src/yuzu/applets/qt_software_keyboard.cpp @@ -20,7 +20,7 @@ namespace { -using namespace Service::AM::Applets; +using namespace Service::AM::Frontend; constexpr float BASE_HEADER_FONT_SIZE = 23.0f; constexpr float BASE_SUB_FONT_SIZE = 17.0f; @@ -389,7 +389,7 @@ void QtSoftwareKeyboardDialog::ShowNormalKeyboard(QPoint pos, QSize size) { } void QtSoftwareKeyboardDialog::ShowTextCheckDialog( - Service::AM::Applets::SwkbdTextCheckResult text_check_result, + Service::AM::Frontend::SwkbdTextCheckResult text_check_result, std::u16string text_check_message) { switch (text_check_result) { case SwkbdTextCheckResult::Success: @@ -1612,7 +1612,7 @@ void QtSoftwareKeyboard::ShowNormalKeyboard() const { } void QtSoftwareKeyboard::ShowTextCheckDialog( - Service::AM::Applets::SwkbdTextCheckResult text_check_result, + Service::AM::Frontend::SwkbdTextCheckResult text_check_result, std::u16string text_check_message) const { emit MainWindowShowTextCheckDialog(text_check_result, std::move(text_check_message)); } @@ -1662,12 +1662,12 @@ void QtSoftwareKeyboard::ExitKeyboard() const { emit MainWindowExitKeyboard(); } -void QtSoftwareKeyboard::SubmitNormalText(Service::AM::Applets::SwkbdResult result, +void QtSoftwareKeyboard::SubmitNormalText(Service::AM::Frontend::SwkbdResult result, std::u16string submitted_text, bool confirmed) const { submit_normal_callback(result, submitted_text, confirmed); } -void QtSoftwareKeyboard::SubmitInlineText(Service::AM::Applets::SwkbdReplyType reply_type, +void QtSoftwareKeyboard::SubmitInlineText(Service::AM::Frontend::SwkbdReplyType reply_type, std::u16string submitted_text, s32 cursor_position) const { submit_inline_callback(reply_type, submitted_text, cursor_position); diff --git a/src/yuzu/applets/qt_software_keyboard.h b/src/yuzu/applets/qt_software_keyboard.h index e5848b8ed..61265714a 100755 --- a/src/yuzu/applets/qt_software_keyboard.h +++ b/src/yuzu/applets/qt_software_keyboard.h @@ -39,7 +39,7 @@ public: void ShowNormalKeyboard(QPoint pos, QSize size); - void ShowTextCheckDialog(Service::AM::Applets::SwkbdTextCheckResult text_check_result, + void ShowTextCheckDialog(Service::AM::Frontend::SwkbdTextCheckResult text_check_result, std::u16string text_check_message); void ShowInlineKeyboard(Core::Frontend::InlineAppearParameters appear_parameters, QPoint pos, @@ -52,10 +52,10 @@ public: void ExitKeyboard(); signals: - void SubmitNormalText(Service::AM::Applets::SwkbdResult result, std::u16string submitted_text, + void SubmitNormalText(Service::AM::Frontend::SwkbdResult result, std::u16string submitted_text, bool confirmed = false) const; - void SubmitInlineText(Service::AM::Applets::SwkbdReplyType reply_type, + void SubmitInlineText(Service::AM::Frontend::SwkbdReplyType reply_type, std::u16string submitted_text, s32 cursor_position) const; public slots: @@ -244,7 +244,7 @@ public: void ShowNormalKeyboard() const override; - void ShowTextCheckDialog(Service::AM::Applets::SwkbdTextCheckResult text_check_result, + void ShowTextCheckDialog(Service::AM::Frontend::SwkbdTextCheckResult text_check_result, std::u16string text_check_message) const override; void ShowInlineKeyboard( @@ -262,8 +262,9 @@ signals: void MainWindowShowNormalKeyboard() const; - void MainWindowShowTextCheckDialog(Service::AM::Applets::SwkbdTextCheckResult text_check_result, - std::u16string text_check_message) const; + void MainWindowShowTextCheckDialog( + Service::AM::Frontend::SwkbdTextCheckResult text_check_result, + std::u16string text_check_message) const; void MainWindowShowInlineKeyboard( Core::Frontend::InlineAppearParameters appear_parameters) const; @@ -275,10 +276,10 @@ signals: void MainWindowExitKeyboard() const; private: - void SubmitNormalText(Service::AM::Applets::SwkbdResult result, std::u16string submitted_text, + void SubmitNormalText(Service::AM::Frontend::SwkbdResult result, std::u16string submitted_text, bool confirmed) const; - void SubmitInlineText(Service::AM::Applets::SwkbdReplyType reply_type, + void SubmitInlineText(Service::AM::Frontend::SwkbdReplyType reply_type, std::u16string submitted_text, s32 cursor_position) const; mutable SubmitNormalCallback submit_normal_callback; diff --git a/src/yuzu/applets/qt_web_browser.cpp b/src/yuzu/applets/qt_web_browser.cpp index 0146b21fe..e4226e870 100755 --- a/src/yuzu/applets/qt_web_browser.cpp +++ b/src/yuzu/applets/qt_web_browser.cpp @@ -96,7 +96,7 @@ QtNXWebEngineView::QtNXWebEngineView(QWidget* parent, Core::System& system, [this] { if (page()->url() == url_interceptor->GetRequestedURL()) { SetFinished(true); - SetExitReason(Service::AM::Applets::WebExitReason::WindowClosed); + SetExitReason(Service::AM::Frontend::WebExitReason::WindowClosed); } }, Qt::QueuedConnection); @@ -115,7 +115,7 @@ void QtNXWebEngineView::LoadLocalWebPage(const std::string& main_url, FocusFirstLinkElement(); SetUserAgent(UserAgent::WebApplet); SetFinished(false); - SetExitReason(Service::AM::Applets::WebExitReason::EndButtonPressed); + SetExitReason(Service::AM::Frontend::WebExitReason::EndButtonPressed); SetLastURL("http://localhost/"); StartInputThread(); @@ -130,7 +130,7 @@ void QtNXWebEngineView::LoadExternalWebPage(const std::string& main_url, FocusFirstLinkElement(); SetUserAgent(UserAgent::WebApplet); SetFinished(false); - SetExitReason(Service::AM::Applets::WebExitReason::EndButtonPressed); + SetExitReason(Service::AM::Frontend::WebExitReason::EndButtonPressed); SetLastURL("http://localhost/"); StartInputThread(); @@ -170,11 +170,11 @@ void QtNXWebEngineView::SetFinished(bool finished_) { finished = finished_; } -Service::AM::Applets::WebExitReason QtNXWebEngineView::GetExitReason() const { +Service::AM::Frontend::WebExitReason QtNXWebEngineView::GetExitReason() const { return exit_reason; } -void QtNXWebEngineView::SetExitReason(Service::AM::Applets::WebExitReason exit_reason_) { +void QtNXWebEngineView::SetExitReason(Service::AM::Frontend::WebExitReason exit_reason_) { exit_reason = exit_reason_; } @@ -441,7 +441,7 @@ void QtWebBrowser::MainWindowExtractOfflineRomFS() { extract_romfs_callback(); } -void QtWebBrowser::MainWindowWebBrowserClosed(Service::AM::Applets::WebExitReason exit_reason, +void QtWebBrowser::MainWindowWebBrowserClosed(Service::AM::Frontend::WebExitReason exit_reason, std::string last_url) { if (callback) { callback(exit_reason, last_url); diff --git a/src/yuzu/applets/qt_web_browser.h b/src/yuzu/applets/qt_web_browser.h index 6b3071f9b..2b85de1eb 100755 --- a/src/yuzu/applets/qt_web_browser.h +++ b/src/yuzu/applets/qt_web_browser.h @@ -85,8 +85,8 @@ public: [[nodiscard]] bool IsFinished() const; void SetFinished(bool finished_); - [[nodiscard]] Service::AM::Applets::WebExitReason GetExitReason() const; - void SetExitReason(Service::AM::Applets::WebExitReason exit_reason_); + [[nodiscard]] Service::AM::Frontend::WebExitReason GetExitReason() const; + void SetExitReason(Service::AM::Frontend::WebExitReason exit_reason_); [[nodiscard]] const std::string& GetLastURL() const; void SetLastURL(std::string last_url_); @@ -176,8 +176,8 @@ private: std::atomic finished{}; - Service::AM::Applets::WebExitReason exit_reason{ - Service::AM::Applets::WebExitReason::EndButtonPressed}; + Service::AM::Frontend::WebExitReason exit_reason{ + Service::AM::Frontend::WebExitReason::EndButtonPressed}; std::string last_url{"http://localhost/"}; @@ -212,7 +212,7 @@ signals: private: void MainWindowExtractOfflineRomFS(); - void MainWindowWebBrowserClosed(Service::AM::Applets::WebExitReason exit_reason, + void MainWindowWebBrowserClosed(Service::AM::Frontend::WebExitReason exit_reason, std::string last_url); mutable ExtractROMFSCallback extract_romfs_callback; diff --git a/src/yuzu/configuration/configure_input.cpp b/src/yuzu/configuration/configure_input.cpp index 895d066f4..5ec8296a6 100755 --- a/src/yuzu/configuration/configure_input.cpp +++ b/src/yuzu/configuration/configure_input.cpp @@ -9,6 +9,8 @@ #include "core/core.h" #include "core/hle/service/am/am.h" #include "core/hle/service/am/applet_ae.h" +#include "core/hle/service/am/applet_manager.h" +#include "core/hle/service/am/applet_message_queue.h" #include "core/hle/service/am/applet_oe.h" #include "core/hle/service/sm/sm.h" #include "hid_core/frontend/emulated_controller.h" @@ -47,22 +49,8 @@ void OnDockedModeChanged(bool last_state, bool new_state, Core::System& system) if (!system.IsPoweredOn()) { return; } - Service::SM::ServiceManager& sm = system.ServiceManager(); - // Message queue is shared between these services, we just need to signal an operation - // change to one and it will handle both automatically - auto applet_oe = sm.GetService("appletOE"); - auto applet_ae = sm.GetService("appletAE"); - bool has_signalled = false; - - if (applet_oe != nullptr) { - applet_oe->GetMessageQueue()->OperationModeChanged(); - has_signalled = true; - } - - if (applet_ae != nullptr && !has_signalled) { - applet_ae->GetMessageQueue()->OperationModeChanged(); - } + system.GetAppletManager().OperationModeChanged(); } ConfigureInput::ConfigureInput(Core::System& system_, QWidget* parent) diff --git a/src/yuzu/game_list.cpp b/src/yuzu/game_list.cpp index 545b8aa02..9bd59998b 100755 --- a/src/yuzu/game_list.cpp +++ b/src/yuzu/game_list.cpp @@ -596,14 +596,10 @@ void GameList::AddGamePopup(QMenu& context_menu, u64 program_id, const std::stri connect(open_save_location, &QAction::triggered, [this, program_id, path]() { emit OpenFolderRequested(program_id, GameListOpenTarget::SaveData, path); }); - connect(start_game, &QAction::triggered, [this, path]() { - emit BootGame(QString::fromStdString(path), 0, 0, StartGameType::Normal, - AmLaunchType::UserInitiated); - }); - connect(start_game_global, &QAction::triggered, [this, path]() { - emit BootGame(QString::fromStdString(path), 0, 0, StartGameType::Global, - AmLaunchType::UserInitiated); - }); + connect(start_game, &QAction::triggered, + [this, path]() { emit BootGame(QString::fromStdString(path), StartGameType::Normal); }); + connect(start_game_global, &QAction::triggered, + [this, path]() { emit BootGame(QString::fromStdString(path), StartGameType::Global); }); connect(open_mod_location, &QAction::triggered, [this, program_id, path]() { emit OpenFolderRequested(program_id, GameListOpenTarget::ModData, path); }); diff --git a/src/yuzu/game_list.h b/src/yuzu/game_list.h index 2009e30b8..c555f6f36 100755 --- a/src/yuzu/game_list.h +++ b/src/yuzu/game_list.h @@ -106,8 +106,7 @@ public: static const QStringList supported_file_extensions; signals: - void BootGame(const QString& game_path, u64 program_id, std::size_t program_index, - StartGameType type, AmLaunchType launch_type); + void BootGame(const QString& game_path, StartGameType type); void GameChosen(const QString& game_path, const u64 title_id = 0); void OpenFolderRequested(u64 program_id, GameListOpenTarget target, const std::string& game_path); diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index 62ece7369..13ae6bc6e 100755 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -8,6 +8,7 @@ #include #include #include +#include "core/hle/service/am/applet_manager.h" #include "core/loader/nca.h" #include "core/tools/renderdoc.h" @@ -39,13 +40,14 @@ #include "core/file_sys/vfs/vfs_real.h" #include "core/frontend/applets/cabinet.h" #include "core/frontend/applets/controller.h" -#include "core/frontend/applets/general_frontend.h" +#include "core/frontend/applets/general.h" #include "core/frontend/applets/mii_edit.h" #include "core/frontend/applets/software_keyboard.h" #include "core/hle/service/acc/profile_manager.h" #include "core/hle/service/am/applet_ae.h" +#include "core/hle/service/am/applet_message_queue.h" #include "core/hle/service/am/applet_oe.h" -#include "core/hle/service/am/applets/applets.h" +#include "core/hle/service/am/frontend/applets.h" #include "core/hle/service/set/system_settings_server.h" #include "frontend_common/content_manager.h" #include "hid_core/frontend/emulated_controller.h" @@ -568,7 +570,7 @@ GMainWindow::GMainWindow(std::unique_ptr config_, bool has_broken_vulk } if (!game_path.isEmpty()) { - BootGame(game_path); + BootGame(game_path, ApplicationAppletParameters()); } } @@ -630,13 +632,14 @@ void GMainWindow::RegisterMetaTypes() { qRegisterMetaType( "Core::Frontend::InlineAppearParameters"); qRegisterMetaType("Core::Frontend::InlineTextParameters"); - qRegisterMetaType("Service::AM::Applets::SwkbdResult"); - qRegisterMetaType( - "Service::AM::Applets::SwkbdTextCheckResult"); - qRegisterMetaType("Service::AM::Applets::SwkbdReplyType"); + qRegisterMetaType("Service::AM::Frontend::SwkbdResult"); + qRegisterMetaType( + "Service::AM::Frontend::SwkbdTextCheckResult"); + qRegisterMetaType( + "Service::AM::Frontend::SwkbdReplyType"); // Web Browser Applet - qRegisterMetaType("Service::AM::Applets::WebExitReason"); + qRegisterMetaType("Service::AM::Frontend::WebExitReason"); // Register loader types qRegisterMetaType("Core::SystemResultStatus"); @@ -746,7 +749,7 @@ void GMainWindow::SoftwareKeyboardInitialize( if (is_inline) { connect( software_keyboard, &QtSoftwareKeyboardDialog::SubmitInlineText, this, - [this](Service::AM::Applets::SwkbdReplyType reply_type, std::u16string submitted_text, + [this](Service::AM::Frontend::SwkbdReplyType reply_type, std::u16string submitted_text, s32 cursor_position) { emit SoftwareKeyboardSubmitInlineText(reply_type, submitted_text, cursor_position); }, @@ -754,7 +757,7 @@ void GMainWindow::SoftwareKeyboardInitialize( } else { connect( software_keyboard, &QtSoftwareKeyboardDialog::SubmitNormalText, this, - [this](Service::AM::Applets::SwkbdResult result, std::u16string submitted_text, + [this](Service::AM::Frontend::SwkbdResult result, std::u16string submitted_text, bool confirmed) { emit SoftwareKeyboardSubmitNormalText(result, submitted_text, confirmed); }, @@ -781,7 +784,7 @@ void GMainWindow::SoftwareKeyboardShowNormal() { } void GMainWindow::SoftwareKeyboardShowTextCheck( - Service::AM::Applets::SwkbdTextCheckResult text_check_result, + Service::AM::Frontend::SwkbdTextCheckResult text_check_result, std::u16string text_check_message) { if (!software_keyboard) { LOG_ERROR(Frontend, "The software keyboard is not initialized!"); @@ -852,7 +855,7 @@ void GMainWindow::WebBrowserOpenWebPage(const std::string& main_url, // Raw input breaks with the web applet, Disable web applets if enabled if (UISettings::values.disable_web_applet || Settings::values.enable_raw_input) { - emit WebBrowserClosed(Service::AM::Applets::WebExitReason::WindowClosed, + emit WebBrowserClosed(Service::AM::Frontend::WebExitReason::WindowClosed, "http://localhost/"); return; } @@ -940,7 +943,7 @@ void GMainWindow::WebBrowserOpenWebPage(const std::string& main_url, if (variant.toBool()) { web_applet->SetFinished(true); web_applet->SetExitReason( - Service::AM::Applets::WebExitReason::EndButtonPressed); + Service::AM::Frontend::WebExitReason::EndButtonPressed); } }); @@ -950,7 +953,7 @@ void GMainWindow::WebBrowserOpenWebPage(const std::string& main_url, if (web_applet->GetCurrentURL().contains(QStringLiteral("localhost"))) { if (!web_applet->IsFinished()) { web_applet->SetFinished(true); - web_applet->SetExitReason(Service::AM::Applets::WebExitReason::CallbackURL); + web_applet->SetExitReason(Service::AM::Frontend::WebExitReason::CallbackURL); } web_applet->SetLastURL(web_applet->GetCurrentURL().toStdString()); @@ -983,7 +986,7 @@ void GMainWindow::WebBrowserOpenWebPage(const std::string& main_url, #else // Utilize the same fallback as the default web browser applet. - emit WebBrowserClosed(Service::AM::Applets::WebExitReason::WindowClosed, "http://localhost/"); + emit WebBrowserClosed(Service::AM::Frontend::WebExitReason::WindowClosed, "http://localhost/"); #endif } @@ -991,7 +994,7 @@ void GMainWindow::WebBrowserOpenWebPage(const std::string& main_url, void GMainWindow::WebBrowserRequestExit() { #ifdef YUZU_USE_QT_WEB_ENGINE if (web_applet) { - web_applet->SetExitReason(Service::AM::Applets::WebExitReason::ExitRequested); + web_applet->SetExitReason(Service::AM::Frontend::WebExitReason::ExitRequested); web_applet->SetFinished(true); } #endif @@ -1472,7 +1475,7 @@ void GMainWindow::OnAppFocusStateChanged(Qt::ApplicationState state) { } void GMainWindow::ConnectWidgetEvents() { - connect(game_list, &GameList::BootGame, this, &GMainWindow::BootGame); + connect(game_list, &GameList::BootGame, this, &GMainWindow::BootGameFromList); connect(game_list, &GameList::GameChosen, this, &GMainWindow::OnGameListLoadFile); connect(game_list, &GameList::OpenDirectory, this, &GMainWindow::OnGameListOpenDirectory); connect(game_list, &GameList::OpenFolderRequested, this, &GMainWindow::OnGameListOpenFolder); @@ -1760,8 +1763,7 @@ void GMainWindow::AllowOSSleep() { #endif } -bool GMainWindow::LoadROM(const QString& filename, u64 program_id, std::size_t program_index, - AmLaunchType launch_type) { +bool GMainWindow::LoadROM(const QString& filename, Service::AM::FrontendAppletParameters params) { // Shutdown previous session if the emu thread is still active... if (emu_thread != nullptr) { ShutdownGame(); @@ -1773,11 +1775,11 @@ bool GMainWindow::LoadROM(const QString& filename, u64 program_id, std::size_t p system->SetFilesystem(vfs); - if (launch_type == AmLaunchType::UserInitiated) { + if (params.launch_type == Service::AM::LaunchType::FrontendInitiated) { system->GetUserChannel().clear(); } - system->SetAppletFrontendSet({ + system->SetFrontendAppletSet({ std::make_unique(*this), // Amiibo Settings (UISettings::values.controller_applet_disabled.GetValue() == true) ? nullptr @@ -1792,7 +1794,7 @@ bool GMainWindow::LoadROM(const QString& filename, u64 program_id, std::size_t p }); const Core::SystemResultStatus result{ - system->Load(*render_window, filename.toStdString(), program_id, program_index)}; + system->Load(*render_window, filename.toStdString(), params)}; const auto drd_callout = (UISettings::values.callout_flags.GetValue() & static_cast(CalloutFlag::DRDDeprecation)) == 0; @@ -1915,12 +1917,12 @@ void GMainWindow::ConfigureFilesystemProvider(const std::string& filepath) { } } -void GMainWindow::BootGame(const QString& filename, u64 program_id, std::size_t program_index, - StartGameType type, AmLaunchType launch_type) { +void GMainWindow::BootGame(const QString& filename, Service::AM::FrontendAppletParameters params, + StartGameType type) { LOG_INFO(Frontend, "yuzu starting..."); - if (program_id == 0 || - program_id > static_cast(Service::AM::Applets::AppletProgramId::MaxProgramId)) { + if (params.program_id == 0 || + params.program_id > static_cast(Service::AM::AppletProgramId::MaxProgramId)) { StoreRecentFile(filename); // Put the filename on top of the list } @@ -1935,7 +1937,7 @@ void GMainWindow::BootGame(const QString& filename, u64 program_id, std::size_t ConfigureFilesystemProvider(filename.toStdString()); const auto v_file = Core::GetGameFileFromPath(vfs, filename.toUtf8().constData()); - const auto loader = Loader::GetLoader(*system, v_file, program_id, program_index); + const auto loader = Loader::GetLoader(*system, v_file, params.program_id, params.program_index); if (loader != nullptr && loader->ReadProgramId(title_id) == Loader::ResultStatus::Success && type == StartGameType::Normal) { @@ -1954,10 +1956,10 @@ void GMainWindow::BootGame(const QString& filename, u64 program_id, std::size_t if (UISettings::values.select_user_on_boot && !user_flag_cmd_line) { const Core::Frontend::ProfileSelectParameters parameters{ - .mode = Service::AM::Applets::UiMode::UserSelector, + .mode = Service::AM::Frontend::UiMode::UserSelector, .invalid_uid_list = {}, .display_options = {}, - .purpose = Service::AM::Applets::UserSelectionPurpose::General, + .purpose = Service::AM::Frontend::UserSelectionPurpose::General, }; if (SelectAndSetCurrentUser(parameters) == false) { return; @@ -1969,7 +1971,7 @@ void GMainWindow::BootGame(const QString& filename, u64 program_id, std::size_t // behavior of asking. user_flag_cmd_line = false; - if (!LoadROM(filename, program_id, program_index, launch_type)) { + if (!LoadROM(filename, params)) { return; } @@ -2059,6 +2061,10 @@ void GMainWindow::BootGame(const QString& filename, u64 program_id, std::size_t OnStartGame(); } +void GMainWindow::BootGameFromList(const QString& filename, StartGameType with_config) { + BootGame(filename, ApplicationAppletParameters(), with_config); +} + bool GMainWindow::OnShutdownBegin() { if (!emulation_running) { return false; @@ -2160,7 +2166,7 @@ void GMainWindow::OnEmulationStopped() { OnTasStateChanged(); render_window->FinalizeCamera(); - system->GetAppletManager().SetCurrentAppletId(Service::AM::Applets::AppletId::None); + system->GetFrontendAppletHolder().SetCurrentAppletId(Service::AM::AppletId::None); // Enable all controllers system->HIDCore().SetSupportedStyleTag({Core::HID::NpadStyleSet::All}); @@ -2239,7 +2245,10 @@ void GMainWindow::UpdateRecentFiles() { } void GMainWindow::OnGameListLoadFile(QString game_path, u64 program_id) { - BootGame(game_path, program_id); + auto params = ApplicationAppletParameters(); + params.program_id = program_id; + + BootGame(game_path, params); } void GMainWindow::OnGameListOpenFolder(u64 program_id, GameListOpenTarget target, @@ -2280,10 +2289,10 @@ void GMainWindow::OnGameListOpenFolder(u64 program_id, GameListOpenTarget target // User save data const auto select_profile = [this] { const Core::Frontend::ProfileSelectParameters parameters{ - .mode = Service::AM::Applets::UiMode::UserSelector, + .mode = Service::AM::Frontend::UiMode::UserSelector, .invalid_uid_list = {}, .display_options = {}, - .purpose = Service::AM::Applets::UserSelectionPurpose::General, + .purpose = Service::AM::Frontend::UserSelectionPurpose::General, }; QtProfileSelectionDialog dialog(*system, this, parameters); dialog.setWindowFlags(Qt::Dialog | Qt::CustomizeWindowHint | Qt::WindowTitleHint | @@ -3171,7 +3180,7 @@ void GMainWindow::OnMenuLoadFile() { } UISettings::values.roms_path = QFileInfo(filename).path().toStdString(); - BootGame(filename); + BootGame(filename, ApplicationAppletParameters()); } void GMainWindow::OnMenuLoadFolder() { @@ -3185,7 +3194,7 @@ void GMainWindow::OnMenuLoadFolder() { const QDir dir{dir_path}; const QStringList matching_main = dir.entryList({QStringLiteral("main")}, QDir::Files); if (matching_main.size() == 1) { - BootGame(dir.path() + QDir::separator() + matching_main[0]); + BootGame(dir.path() + QDir::separator() + matching_main[0], ApplicationAppletParameters()); } else { QMessageBox::warning(this, tr("Invalid Directory Selected"), tr("The directory you have selected does not contain a 'main' file.")); @@ -3379,7 +3388,7 @@ void GMainWindow::OnMenuRecentFile() { const QString filename = action->data().toString(); if (QFileInfo::exists(filename)) { - BootGame(filename); + BootGame(filename, ApplicationAppletParameters()); } else { // Display an error message and remove the file from the list. QMessageBox::information(this, tr("File not found"), @@ -3417,7 +3426,7 @@ void GMainWindow::OnRestartGame() { // Make a copy since ShutdownGame edits game_path const auto current_game = QString(current_game_path); ShutdownGame(); - BootGame(current_game); + BootGame(current_game, ApplicationAppletParameters()); } } @@ -3485,8 +3494,11 @@ void GMainWindow::OnLoadComplete() { void GMainWindow::OnExecuteProgram(std::size_t program_index) { ShutdownGame(); - BootGame(last_filename_booted, 0, program_index, StartGameType::Normal, - AmLaunchType::ApplicationInitiated); + + auto params = ApplicationAppletParameters(); + params.program_index = static_cast(program_index); + params.launch_type = Service::AM::LaunchType::ApplicationInitiated; + BootGame(last_filename_booted, params); } void GMainWindow::OnExit() { @@ -4153,7 +4165,7 @@ void GMainWindow::OnToggleStatusBar() { } void GMainWindow::OnAlbum() { - constexpr u64 AlbumId = static_cast(Service::AM::Applets::AppletProgramId::PhotoViewer); + constexpr u64 AlbumId = static_cast(Service::AM::AppletProgramId::PhotoViewer); auto bis_system = system->GetFileSystemController().GetSystemNANDContents(); if (!bis_system) { QMessageBox::warning(this, tr("No firmware available"), @@ -4168,15 +4180,15 @@ void GMainWindow::OnAlbum() { return; } - system->GetAppletManager().SetCurrentAppletId(Service::AM::Applets::AppletId::PhotoViewer); + system->GetFrontendAppletHolder().SetCurrentAppletId(Service::AM::AppletId::PhotoViewer); const auto filename = QString::fromStdString(album_nca->GetFullPath()); UISettings::values.roms_path = QFileInfo(filename).path().toStdString(); - BootGame(filename, AlbumId); + BootGame(filename, LibraryAppletParameters(AlbumId, Service::AM::AppletId::PhotoViewer)); } void GMainWindow::OnCabinet(Service::NFP::CabinetMode mode) { - constexpr u64 CabinetId = static_cast(Service::AM::Applets::AppletProgramId::Cabinet); + constexpr u64 CabinetId = static_cast(Service::AM::AppletProgramId::Cabinet); auto bis_system = system->GetFileSystemController().GetSystemNANDContents(); if (!bis_system) { QMessageBox::warning(this, tr("No firmware available"), @@ -4191,16 +4203,16 @@ void GMainWindow::OnCabinet(Service::NFP::CabinetMode mode) { return; } - system->GetAppletManager().SetCurrentAppletId(Service::AM::Applets::AppletId::Cabinet); - system->GetAppletManager().SetCabinetMode(mode); + system->GetFrontendAppletHolder().SetCurrentAppletId(Service::AM::AppletId::Cabinet); + system->GetFrontendAppletHolder().SetCabinetMode(mode); const auto filename = QString::fromStdString(cabinet_nca->GetFullPath()); UISettings::values.roms_path = QFileInfo(filename).path().toStdString(); - BootGame(filename, CabinetId); + BootGame(filename, LibraryAppletParameters(CabinetId, Service::AM::AppletId::Cabinet)); } void GMainWindow::OnMiiEdit() { - constexpr u64 MiiEditId = static_cast(Service::AM::Applets::AppletProgramId::MiiEdit); + constexpr u64 MiiEditId = static_cast(Service::AM::AppletProgramId::MiiEdit); auto bis_system = system->GetFileSystemController().GetSystemNANDContents(); if (!bis_system) { QMessageBox::warning(this, tr("No firmware available"), @@ -4215,16 +4227,15 @@ void GMainWindow::OnMiiEdit() { return; } - system->GetAppletManager().SetCurrentAppletId(Service::AM::Applets::AppletId::MiiEdit); + system->GetFrontendAppletHolder().SetCurrentAppletId(Service::AM::AppletId::MiiEdit); const auto filename = QString::fromStdString((mii_applet_nca->GetFullPath())); UISettings::values.roms_path = QFileInfo(filename).path().toStdString(); - BootGame(filename, MiiEditId); + BootGame(filename, LibraryAppletParameters(MiiEditId, Service::AM::AppletId::MiiEdit)); } void GMainWindow::OnOpenControllerMenu() { - constexpr u64 ControllerAppletId = - static_cast(Service::AM::Applets::AppletProgramId::Controller); + constexpr u64 ControllerAppletId = static_cast(Service::AM::AppletProgramId::Controller); auto bis_system = system->GetFileSystemController().GetSystemNANDContents(); if (!bis_system) { QMessageBox::warning(this, tr("No firmware available"), @@ -4240,11 +4251,12 @@ void GMainWindow::OnOpenControllerMenu() { return; } - system->GetAppletManager().SetCurrentAppletId(Service::AM::Applets::AppletId::Controller); + system->GetFrontendAppletHolder().SetCurrentAppletId(Service::AM::AppletId::Controller); const auto filename = QString::fromStdString((controller_applet_nca->GetFullPath())); UISettings::values.roms_path = QFileInfo(filename).path().toStdString(); - BootGame(filename, ControllerAppletId); + BootGame(filename, + LibraryAppletParameters(ControllerAppletId, Service::AM::AppletId::Controller)); } void GMainWindow::OnCaptureScreenshot() { @@ -4564,7 +4576,7 @@ void GMainWindow::OnCheckFirmwareDecryption() { } bool GMainWindow::CheckFirmwarePresence() { - constexpr u64 MiiEditId = static_cast(Service::AM::Applets::AppletProgramId::MiiEdit); + constexpr u64 MiiEditId = static_cast(Service::AM::AppletProgramId::MiiEdit); auto bis_system = system->GetFileSystemController().GetSystemNANDContents(); if (!bis_system) { @@ -4727,7 +4739,7 @@ bool GMainWindow::DropAction(QDropEvent* event) { } else { // Game if (ConfirmChangeGame()) { - BootGame(filename); + BootGame(filename, ApplicationAppletParameters()); } } return true; @@ -4771,36 +4783,12 @@ void GMainWindow::RequestGameExit() { return; } - auto& sm{system->ServiceManager()}; - auto applet_oe = sm.GetService("appletOE"); - auto applet_ae = sm.GetService("appletAE"); - bool has_signalled = false; - system->SetExitRequested(true); - - if (applet_oe != nullptr) { - applet_oe->GetMessageQueue()->RequestExit(); - has_signalled = true; - } - - if (applet_ae != nullptr && !has_signalled) { - applet_ae->GetMessageQueue()->RequestExit(); - } + system->GetAppletManager().RequestExit(); } void GMainWindow::RequestGameResume() { - auto& sm{system->ServiceManager()}; - auto applet_oe = sm.GetService("appletOE"); - auto applet_ae = sm.GetService("appletAE"); - - if (applet_oe != nullptr) { - applet_oe->GetMessageQueue()->RequestResume(); - return; - } - - if (applet_ae != nullptr) { - applet_ae->GetMessageQueue()->RequestResume(); - } + system->GetAppletManager().RequestResume(); } void GMainWindow::filterBarSetChecked(bool state) { @@ -4942,6 +4930,22 @@ void GMainWindow::changeEvent(QEvent* event) { QWidget::changeEvent(event); } +Service::AM::FrontendAppletParameters GMainWindow::ApplicationAppletParameters() { + return Service::AM::FrontendAppletParameters{ + .applet_id = Service::AM::AppletId::Application, + .applet_type = Service::AM::AppletType::Application, + }; +} + +Service::AM::FrontendAppletParameters GMainWindow::LibraryAppletParameters( + u64 program_id, Service::AM::AppletId applet_id) { + return Service::AM::FrontendAppletParameters{ + .program_id = program_id, + .applet_id = applet_id, + .applet_type = Service::AM::AppletType::LibraryApplet, + }; +} + void VolumeButton::wheelEvent(QWheelEvent* event) { int num_degrees = event->angleDelta().y() / 8; diff --git a/src/yuzu/main.h b/src/yuzu/main.h index 106908013..f1376e3a3 100755 --- a/src/yuzu/main.h +++ b/src/yuzu/main.h @@ -64,11 +64,6 @@ enum class StartGameType { Global, // Only uses global configuration }; -enum class AmLaunchType { - UserInitiated, - ApplicationInitiated, -}; - namespace Core { enum class SystemResultStatus : u32; class System; @@ -101,12 +96,17 @@ namespace InputCommon { class InputSubsystem; } -namespace Service::AM::Applets { +namespace Service::AM { +struct FrontendAppletParameters; +enum class AppletId : u32; +} // namespace Service::AM + +namespace Service::AM::Frontend { enum class SwkbdResult : u32; enum class SwkbdTextCheckResult : u32; enum class SwkbdReplyType : u32; enum class WebExitReason : u32; -} // namespace Service::AM::Applets +} // namespace Service::AM::Frontend namespace Service::NFC { class NfcDevice; @@ -204,13 +204,13 @@ signals: void ProfileSelectorFinishedSelection(std::optional uuid); - void SoftwareKeyboardSubmitNormalText(Service::AM::Applets::SwkbdResult result, + void SoftwareKeyboardSubmitNormalText(Service::AM::Frontend::SwkbdResult result, std::u16string submitted_text, bool confirmed); - void SoftwareKeyboardSubmitInlineText(Service::AM::Applets::SwkbdReplyType reply_type, + void SoftwareKeyboardSubmitInlineText(Service::AM::Frontend::SwkbdReplyType reply_type, std::u16string submitted_text, s32 cursor_position); void WebBrowserExtractOfflineRomFS(); - void WebBrowserClosed(Service::AM::Applets::WebExitReason exit_reason, std::string last_url); + void WebBrowserClosed(Service::AM::Frontend::WebExitReason exit_reason, std::string last_url); void SigInterrupt(); @@ -228,8 +228,9 @@ public slots: void SoftwareKeyboardInitialize( bool is_inline, Core::Frontend::KeyboardInitializeParameters initialize_parameters); void SoftwareKeyboardShowNormal(); - void SoftwareKeyboardShowTextCheck(Service::AM::Applets::SwkbdTextCheckResult text_check_result, - std::u16string text_check_message); + void SoftwareKeyboardShowTextCheck( + Service::AM::Frontend::SwkbdTextCheckResult text_check_result, + std::u16string text_check_message); void SoftwareKeyboardShowInline(Core::Frontend::InlineAppearParameters appear_parameters); void SoftwareKeyboardHideInline(); void SoftwareKeyboardInlineTextChanged(Core::Frontend::InlineTextParameters text_parameters); @@ -267,11 +268,10 @@ private: void PreventOSSleep(); void AllowOSSleep(); - bool LoadROM(const QString& filename, u64 program_id, std::size_t program_index, - AmLaunchType launch_type); - void BootGame(const QString& filename, u64 program_id = 0, std::size_t program_index = 0, - StartGameType with_config = StartGameType::Normal, - AmLaunchType launch_type = AmLaunchType::UserInitiated); + bool LoadROM(const QString& filename, Service::AM::FrontendAppletParameters params); + void BootGame(const QString& filename, Service::AM::FrontendAppletParameters params, + StartGameType with_config = StartGameType::Normal); + void BootGameFromList(const QString& filename, StartGameType with_config); void ShutdownGame(); void ShowTelemetryCallout(); @@ -324,6 +324,10 @@ private: void SetGamemodeEnabled(bool state); #endif + Service::AM::FrontendAppletParameters ApplicationAppletParameters(); + Service::AM::FrontendAppletParameters LibraryAppletParameters(u64 program_id, + Service::AM::AppletId applet_id); + private slots: void OnStartGame(); void OnRestartGame(); diff --git a/src/yuzu_cmd/yuzu.cpp b/src/yuzu_cmd/yuzu.cpp index e062d7d41..8e117745c 100755 --- a/src/yuzu_cmd/yuzu.cpp +++ b/src/yuzu_cmd/yuzu.cpp @@ -26,6 +26,7 @@ #include "core/crypto/key_manager.h" #include "core/file_sys/registered_cache.h" #include "core/file_sys/vfs/vfs_real.h" +#include "core/hle/service/am/applet_manager.h" #include "core/hle/service/filesystem/filesystem.h" #include "core/loader/loader.h" #include "core/telemetry_session.h" @@ -366,7 +367,10 @@ int main(int argc, char** argv) { system.GetFileSystemController().CreateFactories(*system.GetFilesystem()); system.GetUserChannel().clear(); - const Core::SystemResultStatus load_result{system.Load(*emu_window, filepath)}; + Service::AM::FrontendAppletParameters load_parameters{ + .applet_id = Service::AM::AppletId::Application, + }; + const Core::SystemResultStatus load_result{system.Load(*emu_window, filepath, load_parameters)}; switch (load_result) { case Core::SystemResultStatus::ErrorGetLoader: