From c96f54f022eec799be26f77ab335d0e1b70692bc Mon Sep 17 00:00:00 2001 From: Steveice10 <1269164+Steveice10@users.noreply.github.com> Date: Thu, 9 Mar 2023 15:44:26 -0800 Subject: [PATCH] Implement app management support (suspend, resume, close, etc) (#6322) --- src/audio_core/hle/hle.cpp | 3 +- .../configuration/configure_input.cpp | 5 - src/citra_qt/main.cpp | 25 + src/citra_qt/main.h | 2 + src/citra_qt/main.ui | 6 + src/common/settings.cpp | 5 + src/core/hle/applets/applet.cpp | 2 +- src/core/hle/kernel/kernel.h | 6 + src/core/hle/kernel/process.cpp | 9 +- src/core/hle/kernel/svc.cpp | 3 + src/core/hle/service/am/am.cpp | 1 + src/core/hle/service/apt/applet_manager.cpp | 480 +++++++++++++++++- src/core/hle/service/apt/applet_manager.h | 127 ++++- src/core/hle/service/apt/apt.cpp | 204 ++++++-- src/core/hle/service/apt/apt.h | 137 ++++- src/core/hle/service/apt/apt_a.cpp | 14 +- src/core/hle/service/apt/apt_s.cpp | 14 +- src/core/hle/service/apt/apt_u.cpp | 14 +- src/core/hle/service/apt/ns_s.cpp | 6 +- src/core/hle/service/gsp/gsp_gpu.cpp | 52 +- src/core/hle/service/gsp/gsp_gpu.h | 38 ++ 21 files changed, 1043 insertions(+), 110 deletions(-) diff --git a/src/audio_core/hle/hle.cpp b/src/audio_core/hle/hle.cpp index cba17c5ca..652e06bf3 100644 --- a/src/audio_core/hle/hle.cpp +++ b/src/audio_core/hle/hle.cpp @@ -272,6 +272,7 @@ void DspHle::Impl::PipeWrite(DspPipe pipe_number, const std::vector& buffer) case StateChange::Sleep: LOG_INFO(Audio_DSP, "Application has requested sleep of DSP hardware"); UNIMPLEMENTED(); + AudioPipeWriteStructAddresses(); dsp_state = DspState::Sleeping; break; default: @@ -438,7 +439,7 @@ bool DspHle::Impl::Tick() { parent.OutputFrame(std::move(current_frame)); - return true; + return GetDspState() == DspState::On; } void DspHle::Impl::AudioTickCallback(s64 cycles_late) { diff --git a/src/citra_qt/configuration/configure_input.cpp b/src/citra_qt/configuration/configure_input.cpp index 62be40208..b13013c1a 100644 --- a/src/citra_qt/configuration/configure_input.cpp +++ b/src/citra_qt/configuration/configure_input.cpp @@ -423,11 +423,6 @@ void ConfigureInput::OnHotkeysChanged(QList new_key_list) { QList ConfigureInput::GetUsedKeyboardKeys() { QList list; for (int button = 0; button < Settings::NativeButton::NumButtons; button++) { - // TODO(adityaruplaha): Add home button to list when we finally emulate it - if (button == Settings::NativeButton::Home) { - continue; - } - const auto& button_param = buttons_param[button]; if (button_param.Get("engine", "") == "keyboard") { list << QKeySequence(button_param.Get("code", 0)); diff --git a/src/citra_qt/main.cpp b/src/citra_qt/main.cpp index e40883384..2905130e0 100644 --- a/src/citra_qt/main.cpp +++ b/src/citra_qt/main.cpp @@ -85,6 +85,7 @@ #include "core/frontend/applets/default_applets.h" #include "core/frontend/scope_acquire_context.h" #include "core/gdbstub/gdbstub.h" +#include "core/hle/service/cfg/cfg.h" #include "core/hle/service/fs/archive.h" #include "core/hle/service/nfc/nfc.h" #include "core/loader/loader.h" @@ -318,6 +319,8 @@ void GMainWindow::InitializeWidgets() { updater = new Updater(this); UISettings::values.updater_found = updater->HasUpdater(); + UpdateBootHomeMenuState(); + // Create status bar message_label = new QLabel(); // Configured separately for left alignment @@ -741,6 +744,7 @@ void GMainWindow::ConnectMenuEvents() { // File connect_menu(ui->action_Load_File, &GMainWindow::OnMenuLoadFile); connect_menu(ui->action_Install_CIA, &GMainWindow::OnMenuInstallCIA); + connect_menu(ui->action_Boot_Home_Menu, &GMainWindow::OnMenuBootHomeMenu); connect_menu(ui->action_Exit, &QMainWindow::close); connect_menu(ui->action_Load_Amiibo, &GMainWindow::OnLoadAmiibo); connect_menu(ui->action_Remove_Amiibo, &GMainWindow::OnRemoveAmiibo); @@ -1601,6 +1605,20 @@ void GMainWindow::OnMenuInstallCIA() { InstallCIA(filepaths); } +static std::string GetHomeMenuPath() { + static const std::array home_menu_tids = { + 0x0004003000008202, 0x0004003000008F02, 0x0004003000009802, 0x0004003000009802, + 0x000400300000A102, 0x000400300000A902, 0x000400300000B102}; + + Service::CFG::Module cfg{}; + return Service::AM::GetTitleContentPath(Service::FS::MediaType::NAND, + home_menu_tids[cfg.GetRegionValue()]); +} + +void GMainWindow::OnMenuBootHomeMenu() { + BootGame(QString::fromStdString(GetHomeMenuPath())); +} + void GMainWindow::InstallCIA(QStringList filepaths) { ui->action_Install_CIA->setEnabled(false); game_list->SetDirectoryWatcherEnabled(false); @@ -1951,6 +1969,7 @@ void GMainWindow::OnConfigure() { setMouseTracking(false); } UpdateSecondaryWindowVisibility(); + UpdateBootHomeMenuState(); } else { Settings::values.input_profiles = old_input_profiles; Settings::values.touch_from_button_maps = old_touch_from_button_maps; @@ -2244,6 +2263,12 @@ void GMainWindow::UpdateStatusBar() { emu_frametime_label->setVisible(true); } +void GMainWindow::UpdateBootHomeMenuState() { + const std::string home_menu_path = GetHomeMenuPath(); + ui->action_Boot_Home_Menu->setEnabled(!home_menu_path.empty() && + FileUtil::Exists(GetHomeMenuPath())); +} + void GMainWindow::HideMouseCursor() { if (emu_thread == nullptr || !UISettings::values.hide_mouse.GetValue()) { mouse_hide_timer.stop(); diff --git a/src/citra_qt/main.h b/src/citra_qt/main.h index 6a230fd0b..85dd95587 100644 --- a/src/citra_qt/main.h +++ b/src/citra_qt/main.h @@ -192,6 +192,7 @@ private slots: void OnConfigurePerGame(); void OnMenuLoadFile(); void OnMenuInstallCIA(); + void OnMenuBootHomeMenu(); void OnUpdateProgress(std::size_t written, std::size_t total); void OnCIAInstallReport(Service::AM::InstallStatus status, QString filepath); void OnCIAInstallFinished(); @@ -238,6 +239,7 @@ private slots: private: Q_INVOKABLE void OnMoviePlaybackCompleted(); void UpdateStatusBar(); + void UpdateBootHomeMenuState(); void LoadTranslation(); void UpdateWindowTitle(); void UpdateUISettings(); diff --git a/src/citra_qt/main.ui b/src/citra_qt/main.ui index 4c88252eb..38fff8b71 100644 --- a/src/citra_qt/main.ui +++ b/src/citra_qt/main.ui @@ -66,6 +66,7 @@ + @@ -209,6 +210,11 @@ Install CIA... + + + Boot Home Menu + + E&xit diff --git a/src/common/settings.cpp b/src/common/settings.cpp index 8e3143dbe..e2934b3f3 100644 --- a/src/common/settings.cpp +++ b/src/common/settings.cpp @@ -56,6 +56,11 @@ void Apply() { hid->ReloadInputDevices(); } + auto apt = Service::APT::GetModule(system); + if (apt) { + apt->GetAppletManager()->ReloadInputDevices(); + } + auto sm = system.ServiceManager(); auto ir_user = sm.GetService("ir:USER"); if (ir_user) diff --git a/src/core/hle/applets/applet.cpp b/src/core/hle/applets/applet.cpp index ab96cc2e2..8be1a0e7e 100644 --- a/src/core/hle/applets/applet.cpp +++ b/src/core/hle/applets/applet.cpp @@ -70,7 +70,7 @@ ResultCode Applet::Create(Service::APT::AppletId id, Service::APT::AppletId pare } Service::APT::AppletAttributes attributes; - attributes.applet_pos.Assign(static_cast(Service::APT::AppletPos::AutoLibrary)); + attributes.applet_pos.Assign(Service::APT::AppletPos::AutoLibrary); attributes.is_home_menu.Assign(false); const auto lock_handle_data = manager->GetLockHandle(attributes); diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h index ca47d591f..b807bc261 100644 --- a/src/core/hle/kernel/kernel.h +++ b/src/core/hle/kernel/kernel.h @@ -133,6 +133,12 @@ public: std::shared_ptr CreateProcess(std::shared_ptr code_set); + /** + * Removes a process from the kernel process list + * @param process Process to remove + */ + void RemoveProcess(std::shared_ptr process); + /** * Creates and returns a new thread. The new thread is immediately scheduled * @param name The friendly name desired for the thread diff --git a/src/core/hle/kernel/process.cpp b/src/core/hle/kernel/process.cpp index 6e8e25bea..89b3636c5 100644 --- a/src/core/hle/kernel/process.cpp +++ b/src/core/hle/kernel/process.cpp @@ -79,6 +79,10 @@ std::shared_ptr KernelSystem::CreateProcess(std::shared_ptr co return process; } +void KernelSystem::RemoveProcess(std::shared_ptr process) { + std::erase(process_list, process); +} + void Process::ParseKernelCaps(const u32* kernel_caps, std::size_t len) { for (std::size_t i = 0; i < len; ++i) { u32 descriptor = kernel_caps[i]; @@ -208,9 +212,6 @@ void Process::Exit() { if (plgldr) { plgldr->OnProcessExit(*this, kernel); } - - // Clear the process's open handles. - handle_table.Clear(); } VAddr Process::GetLinearHeapAreaAddress() const { @@ -474,6 +475,8 @@ Kernel::Process::Process(KernelSystem& kernel) kernel.memory.RegisterPageTable(vm_manager.page_table); } Kernel::Process::~Process() { + LOG_INFO(Kernel, "Cleaning up process {}", process_id); + // Release all objects this process owns first so that their potential destructor can do clean // up with this process before further destruction. // TODO(wwylele): explicitly destroy or invalidate objects this process owns (threads, shared diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp index 6af90edbd..666fdecd1 100644 --- a/src/core/hle/kernel/svc.cpp +++ b/src/core/hle/kernel/svc.cpp @@ -565,6 +565,9 @@ void SVC::ExitProcess() { // Kill the current thread kernel.GetCurrentThreadManager().GetCurrentThread()->Stop(); + // Remove kernel reference to process so it can be cleaned up. + kernel.RemoveProcess(current_process); + system.PrepareReschedule(); } diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp index af4532b75..dd00b71c4 100644 --- a/src/core/hle/service/am/am.cpp +++ b/src/core/hle/service/am/am.cpp @@ -1140,6 +1140,7 @@ void Module::Interface::CheckContentRights(Kernel::HLERequestContext& ctx) { // TODO(shinyquagsire23): Read tickets for this instead? bool has_rights = + FileUtil::Exists(GetTitleContentPath(Service::FS::MediaType::NAND, tid, content_index)) || FileUtil::Exists(GetTitleContentPath(Service::FS::MediaType::SDMC, tid, content_index)); IPC::RequestBuilder rb = rp.MakeBuilder(2, 0); diff --git a/src/core/hle/service/apt/applet_manager.cpp b/src/core/hle/service/apt/applet_manager.cpp index 0bcbd2ecf..b0755bfdb 100644 --- a/src/core/hle/service/apt/applet_manager.cpp +++ b/src/core/hle/service/apt/applet_manager.cpp @@ -4,17 +4,23 @@ #include "common/settings.h" #include "core/core.h" +#include "core/frontend/input.h" #include "core/hle/applets/applet.h" #include "core/hle/service/am/am.h" #include "core/hle/service/apt/applet_manager.h" #include "core/hle/service/apt/errors.h" #include "core/hle/service/apt/ns.h" #include "core/hle/service/cfg/cfg.h" +#include "core/hle/service/gsp/gsp_gpu.h" +#include "video_core/utils.h" SERVICE_CONSTRUCT_IMPL(Service::APT::AppletManager) namespace Service::APT { +/// The interval at which the home button update callback will be called, 16.6ms +static constexpr u64 home_button_update_interval_us = 16666; + struct AppletTitleData { // There are two possible applet ids for each applet. std::array applet_ids; @@ -108,6 +114,14 @@ static u64 GetTitleIdForApplet(AppletId id, u32 region_value) { return itr->title_ids[region_value]; } +static bool IsSystemAppletId(AppletId applet_id) { + return (static_cast(applet_id) & static_cast(AppletId::AnySystemApplet)) != 0; +} + +static bool IsApplicationAppletId(AppletId applet_id) { + return (static_cast(applet_id) & static_cast(AppletId::Application)) != 0; +} + AppletManager::AppletSlot AppletManager::GetAppletSlotFromId(AppletId id) { if (id == AppletId::Application) { if (GetAppletSlot(AppletSlot::Application)->applet_id != AppletId::None) @@ -133,7 +147,7 @@ AppletManager::AppletSlot AppletManager::GetAppletSlotFromId(AppletId id) { if (slot_data->applet_id == AppletId::None) return AppletSlot::Error; - auto applet_pos = static_cast(slot_data->attributes.applet_pos.Value()); + auto applet_pos = slot_data->attributes.applet_pos.Value(); if ((id == AppletId::AnyLibraryApplet && applet_pos == AppletPos::Library) || (id == AppletId::AnySysLibraryApplet && applet_pos == AppletPos::SysLibrary)) return AppletSlot::LibraryApplet; @@ -163,11 +177,11 @@ AppletManager::AppletSlot AppletManager::GetAppletSlotFromAttributes(AppletAttri AppletSlot::Application, AppletSlot::LibraryApplet, AppletSlot::SystemApplet, AppletSlot::LibraryApplet, AppletSlot::Error, AppletSlot::LibraryApplet}; - auto applet_pos = attributes.applet_pos; - if (applet_pos >= applet_position_slots.size()) + auto applet_pos_value = static_cast(attributes.applet_pos.Value()); + if (applet_pos_value >= applet_position_slots.size()) return AppletSlot::Error; - auto slot = applet_position_slots[applet_pos]; + auto slot = applet_position_slots[applet_pos_value]; if (slot == AppletSlot::Error) return AppletSlot::Error; @@ -212,13 +226,35 @@ void AppletManager::CancelAndSendParameter(const MessageParameter& parameter) { // Otherwise, send the parameter the LLE way. next_parameter = parameter; + if (parameter.signal == SignalType::RequestForSysApplet) { + // APT handles RequestForSysApplet messages itself. + LOG_DEBUG(Service_APT, "Replying to RequestForSysApplet from {:03X}", + parameter.sender_id); + + if (parameter.buffer.size() >= sizeof(CaptureBufferInfo)) { + SendCaptureBufferInfo(parameter.buffer); + CaptureFrameBuffers(); + } + + next_parameter->sender_id = parameter.destination_id; + next_parameter->destination_id = parameter.sender_id; + next_parameter->signal = SignalType::Response; + next_parameter->buffer.clear(); + next_parameter->object = nullptr; + } else if (IsSystemAppletId(parameter.sender_id) && + IsApplicationAppletId(parameter.destination_id) && parameter.object) { + // When a message is sent from a system applet to an application, APT + // replaces its object with the zero handle. + next_parameter->object = nullptr; + } + // Signal the event to let the receiver know that a new parameter is ready to be read - auto slot = GetAppletSlotFromId(parameter.destination_id); + auto slot = GetAppletSlotFromId(next_parameter->destination_id); if (slot != AppletSlot::Error) { GetAppletSlot(slot)->parameter_event->Signal(); } else { LOG_DEBUG(Service_APT, "No applet was registered with ID {:03X}", - parameter.destination_id); + next_parameter->destination_id); } } } @@ -261,6 +297,10 @@ ResultVal AppletManager::GlanceParameter(AppletId app_id) { ResultVal AppletManager::ReceiveParameter(AppletId app_id) { auto result = GlanceParameter(app_id); if (result.Succeeded()) { + LOG_DEBUG(Service_APT, + "Received parameter from {:03X} to {:03X} with signal {:08X} and size {:08X}", + result->sender_id, result->destination_id, result->signal, result->buffer.size()); + // Clear the parameter next_parameter = {}; } @@ -282,13 +322,13 @@ bool AppletManager::CancelParameter(bool check_sender, AppletId sender_appid, bo ResultVal AppletManager::GetLockHandle( AppletAttributes attributes) { auto corrected_attributes = attributes; - if (attributes.applet_pos == static_cast(AppletPos::Library) || - attributes.applet_pos == static_cast(AppletPos::SysLibrary) || - attributes.applet_pos == static_cast(AppletPos::AutoLibrary)) { + if (attributes.applet_pos == AppletPos::Library || + attributes.applet_pos == AppletPos::SysLibrary || + attributes.applet_pos == AppletPos::AutoLibrary) { auto corrected_pos = last_library_launcher_slot == AppletSlot::Application ? AppletPos::Library : AppletPos::SysLibrary; - corrected_attributes.applet_pos.Assign(static_cast(corrected_pos)); + corrected_attributes.applet_pos.Assign(corrected_pos); LOG_DEBUG(Service_APT, "Corrected applet attributes from {:08X} to {:08X}", attributes.raw, corrected_attributes.raw); } @@ -350,6 +390,13 @@ ResultCode AppletManager::Enable(AppletAttributes attributes) { auto slot_data = GetAppletSlot(slot); slot_data->registered = true; + if (slot_data->attributes.applet_pos == AppletPos::System && + slot_data->attributes.is_home_menu) { + slot_data->attributes.raw |= attributes.raw; + LOG_DEBUG(Service_APT, "Updated home menu attributes to {:08X}.", + slot_data->attributes.raw); + } + // Send any outstanding parameters to the newly-registered application if (delayed_parameter && delayed_parameter->destination_id == slot_data->applet_id) { // TODO: Real APT would loop trying to send the parameter until it succeeds, @@ -366,6 +413,35 @@ bool AppletManager::IsRegistered(AppletId app_id) { return slot != AppletSlot::Error && GetAppletSlot(slot)->registered; } +ResultVal AppletManager::InquireNotification(AppletId app_id) { + auto slot = GetAppletSlotFromId(app_id); + if (slot != AppletSlot::Error) { + auto slot_data = GetAppletSlot(slot); + if (slot_data->registered) { + auto notification = slot_data->notification; + slot_data->notification = Notification::None; + return MakeResult(notification); + } + } + + return ResultCode(ErrorDescription::NotFound, ErrorModule::Applet, ErrorSummary::NotFound, + ErrorLevel::Status); +} + +ResultCode AppletManager::SendNotification(Notification notification) { + if (active_slot != AppletSlot::Error) { + const auto slot_data = GetAppletSlot(active_slot); + if (slot_data->registered) { + slot_data->notification = notification; + slot_data->notification_event->Signal(); + return RESULT_SUCCESS; + } + } + + return {ErrorDescription::NotFound, ErrorModule::Applet, ErrorSummary::NotFound, + ErrorLevel::Status}; +} + ResultCode AppletManager::PrepareToStartLibraryApplet(AppletId applet_id) { // The real APT service returns an error if there's a pending APT parameter when this function // is called. @@ -589,18 +665,281 @@ ResultCode AppletManager::CloseSystemApplet(std::shared_ptr obje "Attempting to close a system applet from a non-system applet."); auto slot = GetAppletSlot(active_slot); + auto closed_applet_id = slot->applet_id; active_slot = last_system_launcher_slot; - - // TODO: Send a parameter to the application only if the application ordered the applet to - // close. - - // TODO: Terminate the running applet title slot->Reset(); + if (ordered_to_close_sys_applet) { + ordered_to_close_sys_applet = false; + + active_slot = AppletSlot::Application; + CancelAndSendParameter({ + .sender_id = closed_applet_id, + .destination_id = AppletId::Application, + .signal = SignalType::WakeupByExit, + .object = std::move(object), + .buffer = buffer, + }); + } + + // TODO: Terminate the running applet title return RESULT_SUCCESS; } +ResultCode AppletManager::OrderToCloseSystemApplet() { + if (active_slot == AppletSlot::Error) { + return {ErrCodes::InvalidAppletSlot, ErrorModule::Applet, ErrorSummary::InvalidState, + ErrorLevel::Status}; + } + + auto active_slot_data = GetAppletSlot(active_slot); + if (active_slot_data->applet_id == AppletId::None || + active_slot_data->attributes.applet_pos != AppletPos::Application) { + return {ErrCodes::InvalidAppletSlot, ErrorModule::Applet, ErrorSummary::InvalidState, + ErrorLevel::Status}; + } + + auto system_slot = GetAppletSlotFromPos(AppletPos::System); + if (system_slot == AppletSlot::Error) { + return {ErrorDescription::NotFound, ErrorModule::Applet, ErrorSummary::NotFound, + ErrorLevel::Status}; + } + + auto system_slot_data = GetAppletSlot(system_slot); + if (!system_slot_data->registered) { + return {ErrorDescription::NotFound, ErrorModule::Applet, ErrorSummary::NotFound, + ErrorLevel::Status}; + } + + ordered_to_close_sys_applet = true; + active_slot = system_slot; + + SendParameter({ + .sender_id = AppletId::Application, + .destination_id = system_slot_data->applet_id, + .signal = SignalType::WakeupByCancel, + }); + + return RESULT_SUCCESS; +} + +ResultCode AppletManager::PrepareToJumpToHomeMenu() { + if (next_parameter) { + return {ErrCodes::ParameterPresent, ErrorModule::Applet, ErrorSummary::InvalidState, + ErrorLevel::Status}; + } + + last_jump_to_home_slot = active_slot; + if (last_jump_to_home_slot == AppletSlot::Application) { + EnsureHomeMenuLoaded(); + } + + return RESULT_SUCCESS; +} + +ResultCode AppletManager::JumpToHomeMenu(std::shared_ptr object, + const std::vector& buffer) { + if (last_jump_to_home_slot != AppletSlot::Error) { + auto slot_data = GetAppletSlot(last_jump_to_home_slot); + if (slot_data->applet_id != AppletId::None) { + MessageParameter param; + param.object = std::move(object); + param.buffer = buffer; + + switch (slot_data->attributes.applet_pos) { + case AppletPos::Application: + active_slot = AppletSlot::HomeMenu; + + param.destination_id = AppletId::HomeMenu; + param.sender_id = AppletId::Application; + param.signal = SignalType::WakeupByPause; + SendParameter(param); + break; + case AppletPos::Library: + param.destination_id = slot_data->applet_id; + param.sender_id = slot_data->applet_id; + param.signal = SignalType::WakeupByCancel; + SendParameter(param); + break; + case AppletPos::System: + if (slot_data->attributes.is_home_menu) { + param.destination_id = slot_data->applet_id; + param.sender_id = slot_data->applet_id; + param.signal = SignalType::WakeupToJumpHome; + SendParameter(param); + } + break; + case AppletPos::SysLibrary: { + const auto system_slot_data = GetAppletSlot(AppletSlot::SystemApplet); + param.destination_id = slot_data->applet_id; + param.sender_id = slot_data->applet_id; + param.signal = system_slot_data->registered ? SignalType::WakeupByCancel + : SignalType::WakeupToJumpHome; + SendParameter(param); + break; + } + default: + break; + } + } + } + + return RESULT_SUCCESS; +} + +ResultCode AppletManager::PrepareToLeaveHomeMenu() { + if (!GetAppletSlot(AppletSlot::Application)->registered) { + return {ErrCodes::InvalidAppletSlot, ErrorModule::Applet, ErrorSummary::InvalidState, + ErrorLevel::Status}; + } + + if (next_parameter) { + return {ErrCodes::ParameterPresent, ErrorModule::Applet, ErrorSummary::InvalidState, + ErrorLevel::Status}; + } + + return RESULT_SUCCESS; +} + +ResultCode AppletManager::LeaveHomeMenu(std::shared_ptr object, + const std::vector& buffer) { + active_slot = AppletSlot::Application; + + SendParameter({ + .sender_id = AppletId::HomeMenu, + .destination_id = AppletId::Application, + .signal = SignalType::WakeupByPause, + .object = std::move(object), + .buffer = buffer, + }); + + return RESULT_SUCCESS; +} + +ResultCode AppletManager::OrderToCloseApplication() { + if (active_slot == AppletSlot::Error) { + return {ErrCodes::InvalidAppletSlot, ErrorModule::Applet, ErrorSummary::InvalidState, + ErrorLevel::Status}; + } + + auto active_slot_data = GetAppletSlot(active_slot); + if (active_slot_data->applet_id == AppletId::None || + active_slot_data->attributes.applet_pos != AppletPos::System) { + return {ErrCodes::InvalidAppletSlot, ErrorModule::Applet, ErrorSummary::InvalidState, + ErrorLevel::Status}; + } + + ordered_to_close_application = true; + active_slot = AppletSlot::Application; + + SendParameter({ + .sender_id = AppletId::HomeMenu, + .destination_id = AppletId::Application, + .signal = SignalType::WakeupByCancel, + }); + + return RESULT_SUCCESS; +} + +ResultCode AppletManager::PrepareToCloseApplication(bool return_to_sys) { + if (active_slot == AppletSlot::Error) { + return {ErrCodes::InvalidAppletSlot, ErrorModule::Applet, ErrorSummary::InvalidState, + ErrorLevel::Status}; + } + + auto active_slot_data = GetAppletSlot(active_slot); + if (active_slot_data->applet_id == AppletId::None || + active_slot_data->attributes.applet_pos != AppletPos::Application) { + return {ErrCodes::InvalidAppletSlot, ErrorModule::Applet, ErrorSummary::InvalidState, + ErrorLevel::Status}; + } + + auto system_slot_data = GetAppletSlot(AppletSlot::SystemApplet); + auto home_menu_slot_data = GetAppletSlot(AppletSlot::HomeMenu); + + if (!application_cancelled && return_to_sys) { + // TODO: Left side of the OR also includes "&& !power_button_clicked", but this isn't + // implemented yet. + if (!ordered_to_close_application || !system_slot_data->registered) { + application_close_target = AppletSlot::HomeMenu; + } else { + application_close_target = AppletSlot::SystemApplet; + } + } else { + application_close_target = AppletSlot::Error; + } + + if (application_close_target != AppletSlot::HomeMenu && !system_slot_data->registered && + !home_menu_slot_data->registered) { + return {ErrCodes::InvalidAppletSlot, ErrorModule::Applet, ErrorSummary::InvalidState, + ErrorLevel::Status}; + } + + if (next_parameter) { + return {ErrCodes::ParameterPresent, ErrorModule::Applet, ErrorSummary::InvalidState, + ErrorLevel::Status}; + } + + if (application_close_target == AppletSlot::HomeMenu) { + EnsureHomeMenuLoaded(); + } + + return RESULT_SUCCESS; +} + +ResultCode AppletManager::CloseApplication(std::shared_ptr object, + const std::vector& buffer) { + ordered_to_close_application = false; + application_cancelled = false; + + GetAppletSlot(AppletSlot::Application)->Reset(); + + if (application_close_target != AppletSlot::Error) { + active_slot = application_close_target; + + CancelAndSendParameter({ + .sender_id = AppletId::Application, + .destination_id = GetAppletSlot(application_close_target)->applet_id, + .signal = SignalType::WakeupByExit, + .object = std::move(object), + .buffer = buffer, + }); + } + + // TODO: Terminate the application process. + return RESULT_SUCCESS; +} + +ResultVal AppletManager::GetAppletManInfo( + AppletPos requested_applet_pos) { + auto active_applet_pos = AppletPos::Invalid; + auto active_applet_id = AppletId::None; + if (active_slot != AppletSlot::Error) { + auto active_slot_data = GetAppletSlot(active_slot); + if (active_slot_data->applet_id != AppletId::None) { + active_applet_pos = active_slot_data->attributes.applet_pos; + active_applet_id = active_slot_data->applet_id; + } + } + + auto requested_applet_id = AppletId::None; + auto requested_slot = GetAppletSlotFromPos(requested_applet_pos); + if (requested_slot != AppletSlot::Error) { + auto requested_slot_data = GetAppletSlot(requested_slot); + if (requested_slot_data->registered) { + requested_applet_id = requested_slot_data->applet_id; + } + } + + return MakeResult({ + .active_applet_pos = active_applet_pos, + .requested_applet_id = requested_applet_id, + .home_menu_applet_id = AppletId::HomeMenu, + .active_applet_id = active_applet_id, + }); +} + ResultVal AppletManager::GetAppletInfo(AppletId app_id) { auto slot = GetAppletSlotFromId(app_id); if (slot == AppletSlot::Error) { @@ -709,7 +1048,7 @@ ResultCode AppletManager::DoApplicationJump(const DeliverArg& arg) { ResultCode AppletManager::PrepareToStartApplication(u64 title_id, FS::MediaType media_type) { if (active_slot == AppletSlot::Error || - GetAppletSlot(active_slot)->attributes.applet_pos != static_cast(AppletPos::System)) { + GetAppletSlot(active_slot)->attributes.applet_pos != AppletPos::System) { return {ErrCodes::InvalidAppletSlot, ErrorModule::Applet, ErrorSummary::InvalidState, ErrorLevel::Status}; } @@ -771,9 +1110,30 @@ ResultCode AppletManager::WakeupApplication() { // Send a Wakeup signal via the apt parameter to the application once it registers itself. // The real APT service does this by spin waiting on another thread until the application is // registered. - SendApplicationParameterAfterRegistration({.sender_id = AppletId::HomeMenu, - .destination_id = AppletId::Application, - .signal = SignalType::Wakeup}); + SendApplicationParameterAfterRegistration({ + .sender_id = AppletId::HomeMenu, + .destination_id = AppletId::Application, + .signal = SignalType::Wakeup, + }); + + return RESULT_SUCCESS; +} + +ResultCode AppletManager::CancelApplication() { + auto application_slot_data = GetAppletSlot(AppletSlot::Application); + if (application_slot_data->applet_id == AppletId::None) { + return {ErrCodes::InvalidAppletSlot, ErrorModule::Applet, ErrorSummary::InvalidState, + ErrorLevel::Status}; + } + + application_cancelled = true; + + SendApplicationParameterAfterRegistration({ + .sender_id = active_slot != AppletSlot::Error ? GetAppletSlot(active_slot)->applet_id + : AppletId::None, + .destination_id = AppletId::Application, + .signal = SignalType::WakeupByCancel, + }); return RESULT_SUCCESS; } @@ -813,6 +1173,78 @@ void AppletManager::EnsureHomeMenuLoaded() { } } +static void CaptureFrameBuffer(Core::System& system, u32 capture_offset, VAddr src, u32 height, + u32 format) { + static constexpr auto screen_capture_base_vaddr = static_cast(0x1F500000); + static constexpr auto screen_width = 240; + static constexpr auto screen_width_pow2 = 256; + const auto bpp = format < 2 ? 3 : 2; + + Memory::RasterizerFlushVirtualRegion(src, screen_width * height * bpp, + Memory::FlushMode::Flush); + + auto dst_vaddr = screen_capture_base_vaddr + capture_offset; + auto dst_ptr = system.Memory().GetPointer(dst_vaddr); + const auto src_ptr = system.Memory().GetPointer(src); + for (auto y = 0; y < height; y++) { + for (auto x = 0; x < screen_width; x++) { + auto dst_offset = + VideoCore::GetMortonOffset(x, y, bpp) + (y & ~7) * screen_width_pow2 * bpp; + auto src_offset = bpp * (screen_width * y + x); + std::memcpy(dst_ptr + dst_offset, src_ptr + src_offset, bpp); + } + } + + Memory::RasterizerFlushVirtualRegion(dst_vaddr, screen_width_pow2 * height * bpp, + Memory::FlushMode::Invalidate); +} + +void AppletManager::CaptureFrameBuffers() { + auto gsp = + Core::System::GetInstance().ServiceManager().GetService("gsp::Gpu"); + auto active_thread_id = gsp->GetActiveThreadId(); + auto top_screen = gsp->GetFrameBufferInfo(active_thread_id, 0); + auto bottom_screen = gsp->GetFrameBufferInfo(active_thread_id, 1); + + auto top_fb = top_screen->framebuffer_info[top_screen->index]; + auto bottom_fb = bottom_screen->framebuffer_info[bottom_screen->index]; + + CaptureFrameBuffer(system, capture_info->bottom_screen_left_offset, bottom_fb.address_left, 320, + capture_info->bottom_screen_format); + CaptureFrameBuffer(system, capture_info->top_screen_left_offset, top_fb.address_left, 400, + capture_info->top_screen_format); + if (capture_info->is_3d) { + CaptureFrameBuffer(system, capture_info->top_screen_right_offset, top_fb.address_right, 400, + capture_info->top_screen_format); + } +} + +void AppletManager::LoadInputDevices() { + home_button = Input::CreateDevice( + Settings::values.current_input_profile.buttons[Settings::NativeButton::Home]); +} + +void AppletManager::HomeButtonUpdateEvent(std::uintptr_t user_data, s64 cycles_late) { + if (is_device_reload_pending.exchange(false)) { + LoadInputDevices(); + } + + const bool state = home_button->GetStatus(); + // NOTE: We technically do support loading and jumping to home menu even if it isn't + // initially registered. However since the home menu suspend is not bug-free, we don't + // want normal users who didn't launch the home menu accidentally pressing the home + // button binding and freezing their game, so for now, gate it to only environments + // where the home menu was already loaded by the user (last condition). + if (state && !last_home_button_state && GetAppletSlot(AppletSlot::HomeMenu)->registered) { + SendNotification(Notification::HomeButtonSingle); + } + last_home_button_state = state; + + // Reschedule recurrent event + Core::System::GetInstance().CoreTiming().ScheduleEvent( + usToCycles(home_button_update_interval_us) - cycles_late, home_button_update_event); +} + AppletManager::AppletManager(Core::System& system) : system(system) { lock = system.Kernel().CreateMutex(false, "APT_U:Lock"); for (std::size_t slot = 0; slot < applet_slots.size(); ++slot) { @@ -828,10 +1260,20 @@ AppletManager::AppletManager(Core::System& system) : system(system) { system.Kernel().CreateEvent(Kernel::ResetType::OneShot, "APT:Parameter"); } HLE::Applets::Init(); + home_button_update_event = Core::System::GetInstance().CoreTiming().RegisterEvent( + "Home Button Update Event", [this](std::uintptr_t user_data, s64 cycles_late) { + HomeButtonUpdateEvent(user_data, cycles_late); + }); + Core::System::GetInstance().CoreTiming().ScheduleEvent( + usToCycles(home_button_update_interval_us), home_button_update_event); } AppletManager::~AppletManager() { HLE::Applets::Shutdown(); } +void AppletManager::ReloadInputDevices() { + is_device_reload_pending.store(true); +} + } // namespace Service::APT diff --git a/src/core/hle/service/apt/applet_manager.h b/src/core/hle/service/apt/applet_manager.h index b7a11500a..1f7b041de 100644 --- a/src/core/hle/service/apt/applet_manager.h +++ b/src/core/hle/service/apt/applet_manager.h @@ -13,6 +13,7 @@ #include #include #include +#include "core/frontend/input.h" #include "core/global.h" #include "core/hle/kernel/event.h" #include "core/hle/result.h" @@ -46,6 +47,21 @@ enum class SignalType : u32 { WakeupToLaunchApplication = 0x11, }; +enum class Notification : u32 { + None = 0, + HomeButtonSingle = 1, + HomeButtonDouble = 2, + SleepQuery = 3, + SleepCancelledByOpen = 4, + SleepAccepted = 5, + SleepAwake = 6, + Shutdown = 7, + PowerButtonClick = 8, + PowerButtonClear = 9, + TrySleep = 10, + OrderToClose = 11, +}; + /// App Id's used by APT functions enum class AppletId : u32 { None = 0, @@ -103,19 +119,20 @@ private: friend class boost::serialization::access; }; -enum class AppletPos { +enum class AppletPos : u32 { Application = 0, Library = 1, System = 2, SysLibrary = 3, Resident = 4, - AutoLibrary = 5 + AutoLibrary = 5, + Invalid = 0xFF, }; union AppletAttributes { u32 raw; - BitField<0, 3, u32> applet_pos; + BitField<0, 3, AppletPos> applet_pos; BitField<29, 1, u32> is_home_menu; AppletAttributes() : raw(0) {} @@ -178,11 +195,41 @@ private: friend class boost::serialization::access; }; +/// Used by the application to pass information about the current framebuffer to applets. +struct CaptureBufferInfo { + u32_le size; + u8 is_3d; + INSERT_PADDING_BYTES(0x3); // Padding for alignment + u32_le top_screen_left_offset; + u32_le top_screen_right_offset; + u32_le top_screen_format; + u32_le bottom_screen_left_offset; + u32_le bottom_screen_right_offset; + u32_le bottom_screen_format; + +private: + template + void serialize(Archive& ar, const unsigned int) { + ar& size; + ar& is_3d; + ar& top_screen_left_offset; + ar& top_screen_right_offset; + ar& top_screen_format; + ar& bottom_screen_left_offset; + ar& bottom_screen_right_offset; + ar& bottom_screen_format; + } + friend class boost::serialization::access; +}; +static_assert(sizeof(CaptureBufferInfo) == 0x20, "CaptureBufferInfo struct has incorrect size"); + class AppletManager : public std::enable_shared_from_this { public: explicit AppletManager(Core::System& system); ~AppletManager(); + void ReloadInputDevices(); + /** * Clears any existing parameter and places a new one. This function is currently only used by * HLE Applets and should be likely removed in the future @@ -211,6 +258,9 @@ public: ResultCode Enable(AppletAttributes attributes); bool IsRegistered(AppletId app_id); + ResultVal InquireNotification(AppletId app_id); + ResultCode SendNotification(Notification notification); + ResultCode PrepareToStartLibraryApplet(AppletId applet_id); ResultCode PreloadLibraryApplet(AppletId applet_id); ResultCode FinishPreloadingLibraryApplet(AppletId applet_id); @@ -227,6 +277,18 @@ public: ResultCode PrepareToCloseSystemApplet(); ResultCode CloseSystemApplet(std::shared_ptr object, const std::vector& buffer); + ResultCode OrderToCloseSystemApplet(); + + ResultCode PrepareToJumpToHomeMenu(); + ResultCode JumpToHomeMenu(std::shared_ptr object, + const std::vector& buffer); + ResultCode PrepareToLeaveHomeMenu(); + ResultCode LeaveHomeMenu(std::shared_ptr object, const std::vector& buffer); + + ResultCode OrderToCloseApplication(); + ResultCode PrepareToCloseApplication(bool return_to_sys); + ResultCode CloseApplication(std::shared_ptr object, + const std::vector& buffer); ResultCode PrepareToDoApplicationJump(u64 title_id, FS::MediaType media_type, ApplicationJumpFlags flags); @@ -239,10 +301,40 @@ public: deliver_arg = std::move(arg); } + std::vector GetCaptureInfo() { + std::vector buffer; + if (capture_info) { + buffer.resize(sizeof(CaptureBufferInfo)); + std::memcpy(buffer.data(), &capture_info.get(), sizeof(CaptureBufferInfo)); + } + return buffer; + } + std::vector ReceiveCaptureBufferInfo() { + std::vector buffer = GetCaptureInfo(); + capture_info.reset(); + return buffer; + } + void SendCaptureBufferInfo(std::vector buffer) { + ASSERT_MSG(buffer.size() >= sizeof(CaptureBufferInfo), "CaptureBufferInfo is too small."); + + capture_info.emplace(); + std::memcpy(&capture_info.get(), buffer.data(), sizeof(CaptureBufferInfo)); + } + ResultCode PrepareToStartApplication(u64 title_id, FS::MediaType media_type); ResultCode StartApplication(const std::vector& parameter, const std::vector& hmac, bool paused); ResultCode WakeupApplication(); + ResultCode CancelApplication(); + + struct AppletManInfo { + AppletPos active_applet_pos; + AppletId requested_applet_id; + AppletId home_menu_applet_id; + AppletId active_applet_id; + }; + + ResultVal GetAppletManInfo(AppletPos requested_applet_pos); struct AppletInfo { u64 title_id; @@ -273,6 +365,8 @@ private: boost::optional app_start_parameters{}; boost::optional deliver_arg{}; + boost::optional capture_info; + static constexpr std::size_t NumAppletSlot = 4; enum class AppletSlot : u8 { @@ -292,6 +386,7 @@ private: bool registered; bool loaded; AppletAttributes attributes; + Notification notification; std::shared_ptr notification_event; std::shared_ptr parameter_event; @@ -311,6 +406,7 @@ private: ar& registered; ar& loaded; ar& attributes.raw; + ar& notification; ar& notification_event; ar& parameter_event; } @@ -325,6 +421,16 @@ private: SignalType library_applet_closing_command = SignalType::None; AppletId last_prepared_library_applet = AppletId::None; AppletSlot last_system_launcher_slot = AppletSlot::Error; + AppletSlot last_jump_to_home_slot = AppletSlot::Error; + bool ordered_to_close_sys_applet = false; + bool ordered_to_close_application = false; + bool application_cancelled = false; + AppletSlot application_close_target = AppletSlot::Error; + + Core::TimingEventType* home_button_update_event; + std::atomic is_device_reload_pending{true}; + std::unique_ptr home_button; + bool last_home_button_state = false; Core::System& system; @@ -346,6 +452,11 @@ private: void EnsureHomeMenuLoaded(); + void CaptureFrameBuffers(); + + void LoadInputDevices(); + void HomeButtonUpdateEvent(std::uintptr_t user_data, s64 cycles_late); + template void serialize(Archive& ar, const unsigned int file_version) { ar& next_parameter; @@ -358,10 +469,20 @@ private: ar& last_library_launcher_slot; ar& last_prepared_library_applet; ar& last_system_launcher_slot; + ar& last_jump_to_home_slot; + ar& ordered_to_close_sys_applet; + ar& ordered_to_close_application; + ar& application_cancelled; + ar& application_close_target; ar& lock; + ar& capture_info; } ar& applet_slots; ar& library_applet_closing_command; + + if (Archive::is_loading::value) { + LoadInputDevices(); + } } friend class boost::serialization::access; }; diff --git a/src/core/hle/service/apt/apt.cpp b/src/core/hle/service/apt/apt.cpp index bb0d19f7a..45051963e 100644 --- a/src/core/hle/service/apt/apt.cpp +++ b/src/core/hle/service/apt/apt.cpp @@ -41,7 +41,6 @@ void Module::serialize(Archive& ar, const unsigned int file_version) { ar& shared_font_relocated; ar& cpu_percent; ar& unknown_ns_state_field; - ar& screen_capture_buffer; ar& screen_capture_post_permission; ar& applet_manager; if (file_version > 0) { @@ -73,6 +72,47 @@ void Module::NSInterface::SetWirelessRebootInfo(Kernel::HLERequestContext& ctx) LOG_WARNING(Service_APT, "called size={}", size); } +void Module::NSInterface::ShutdownAsync(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp(ctx, 0xE, 0, 0); // 0xE0000 + + LOG_INFO(Service_APT, "called"); + + apt->system.RequestShutdown(); + + IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); + rb.Push(RESULT_SUCCESS); +} + +void Module::NSInterface::RebootSystem(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp(ctx, 0x10, 6, 0); // 0x100180 + const auto launch_title = rp.Pop() != 0; + const auto title_id = rp.Pop(); + const auto media_type = static_cast(rp.Pop()); + rp.Skip(1, false); // Skip padding + const auto mem_type = rp.Pop(); + + LOG_WARNING(Service_APT, + "called launch_title={}, title_id={:016X}, media_type={:02X}, mem_type={:02X}", + launch_title, title_id, media_type, mem_type); + + // TODO: Implement loading a specific title. + apt->system.RequestReset(); + + IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); + rb.Push(RESULT_SUCCESS); +} + +void Module::NSInterface::RebootSystemClean(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp(ctx, 0x16, 0, 0); // 0x160000 + + LOG_INFO(Service_APT, "called"); + + apt->system.RequestReset(); + + IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); + rb.Push(RESULT_SUCCESS); +} + void Module::APTInterface::Initialize(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp(ctx, 0x2, 2, 0); // 0x20080 const auto app_id = rp.PopEnum(); @@ -332,16 +372,22 @@ void Module::APTInterface::Enable(Kernel::HLERequestContext& ctx) { void Module::APTInterface::GetAppletManInfo(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp(ctx, 0x5, 1, 0); // 0x50040 - const auto unk = rp.Pop(); + auto applet_pos = rp.PopEnum(); - IPC::RequestBuilder rb = rp.MakeBuilder(5, 0); - rb.Push(RESULT_SUCCESS); // No error - rb.Push(0); - rb.Push(0); - rb.Push(static_cast(AppletId::HomeMenu)); // Home menu AppID - rb.Push(static_cast(AppletId::Application)); // TODO(purpasmart96): Do this correctly + LOG_DEBUG(Service_APT, "called, applet_pos={:08X}", applet_pos); - LOG_WARNING(Service_APT, "(STUBBED) called unk={:#010X}", unk); + auto info = apt->applet_manager->GetAppletManInfo(applet_pos); + if (info.Failed()) { + IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); + rb.Push(info.Code()); + } else { + IPC::RequestBuilder rb = rp.MakeBuilder(5, 0); + rb.Push(RESULT_SUCCESS); + rb.PushEnum(info->active_applet_pos); + rb.PushEnum(info->requested_applet_id); + rb.PushEnum(info->home_menu_applet_id); + rb.PushEnum(info->active_applet_id); + } } void Module::APTInterface::IsRegistered(Kernel::HLERequestContext& ctx) { @@ -357,13 +403,19 @@ void Module::APTInterface::IsRegistered(Kernel::HLERequestContext& ctx) { void Module::APTInterface::InquireNotification(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp(ctx, 0xB, 1, 0); // 0xB0040 - const auto app_id = rp.Pop(); + const auto app_id = rp.PopEnum(); - IPC::RequestBuilder rb = rp.MakeBuilder(2, 0); - rb.Push(RESULT_SUCCESS); // No error - rb.Push(static_cast(SignalType::None)); // Signal type + LOG_DEBUG(Service_APT, "called app_id={:#010X}", app_id); - LOG_WARNING(Service_APT, "(STUBBED) called app_id={:#010X}", app_id); + auto notification = apt->applet_manager->InquireNotification(app_id); + if (notification.Failed()) { + IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); + rb.Push(notification.Code()); + } else { + IPC::RequestBuilder rb = rp.MakeBuilder(2, 0); + rb.Push(RESULT_SUCCESS); + rb.Push(static_cast(notification.Unwrap())); + } } void Module::APTInterface::SendParameter(Kernel::HLERequestContext& ctx) { @@ -561,6 +613,15 @@ void Module::APTInterface::WakeupApplication(Kernel::HLERequestContext& ctx) { rb.Push(apt->applet_manager->WakeupApplication()); } +void Module::APTInterface::CancelApplication(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp(ctx, 0x1D, 0, 0); // 0x001D0000 + + LOG_DEBUG(Service_APT, "called"); + + IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); + rb.Push(apt->applet_manager->CancelApplication()); +} + void Module::APTInterface::AppletUtility(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp(ctx, 0x4B, 3, 2); // 0x004B00C2 @@ -574,8 +635,18 @@ void Module::APTInterface::AppletUtility(Kernel::HLERequestContext& ctx) { "(STUBBED) called command={:#010X}, input_size={:#010X}, output_size={:#010X}", utility_command, input_size, output_size); - IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); + std::vector out(output_size); + if (utility_command == 0x6 && output_size > 0) { + // Command 0x6 (TryLockTransition) expects a boolean return value indicating + // whether the attempt succeeded. Since we don't implement any of the transition + // locking stuff yet, fake a success result to avoid app crashes. + out[0] = true; + } + + IPC::RequestBuilder rb = rp.MakeBuilder(2, 2); rb.Push(RESULT_SUCCESS); // No error + rb.Push(RESULT_SUCCESS); // Utility function result + rb.PushStaticBuffer(out, 0); } void Module::APTInterface::SetAppCpuTimeLimit(Kernel::HLERequestContext& ctx) { @@ -688,18 +759,35 @@ void Module::APTInterface::StartSystemApplet(Kernel::HLERequestContext& ctx) { rb.Push(apt->applet_manager->StartSystemApplet(applet_id, object, buffer)); } -void Module::APTInterface::CloseApplication(Kernel::HLERequestContext& ctx) { - IPC::RequestParser rp(ctx, 0x27, 1, 4); - [[maybe_unused]] const auto parameters_size = rp.Pop(); - [[maybe_unused]] const auto object = rp.PopGenericObject(); - [[maybe_unused]] const auto buffer = rp.PopStaticBuffer(); +void Module::APTInterface::OrderToCloseApplication(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp(ctx, 0x21, 0, 0); LOG_DEBUG(Service_APT, "called"); - apt->system.RequestShutdown(); + IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); + rb.Push(apt->applet_manager->OrderToCloseApplication()); +} + +void Module::APTInterface::PrepareToCloseApplication(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp(ctx, 0x22, 1, 0); + const auto return_to_sys = rp.Pop() != 0; + + LOG_DEBUG(Service_APT, "called return_to_sys={}", return_to_sys); IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); - rb.Push(RESULT_SUCCESS); + rb.Push(apt->applet_manager->PrepareToCloseApplication(return_to_sys)); +} + +void Module::APTInterface::CloseApplication(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp(ctx, 0x27, 1, 4); + const auto parameter_size = rp.Pop(); + const auto object = rp.PopGenericObject(); + const auto buffer = rp.PopStaticBuffer(); + + LOG_DEBUG(Service_APT, "called size={}", parameter_size); + + IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); + rb.Push(apt->applet_manager->CloseApplication(object, buffer)); } void Module::APTInterface::CancelLibraryApplet(Kernel::HLERequestContext& ctx) { @@ -758,6 +846,57 @@ void Module::APTInterface::CloseSystemApplet(Kernel::HLERequestContext& ctx) { rb.Push(apt->applet_manager->CloseSystemApplet(object, buffer)); } +void Module::APTInterface::OrderToCloseSystemApplet(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp(ctx, 0x2A, 0, 0); // 0x2A0000 + + LOG_DEBUG(Service_APT, "called"); + + IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); + rb.Push(apt->applet_manager->OrderToCloseSystemApplet()); +} + +void Module::APTInterface::PrepareToJumpToHomeMenu(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp(ctx, 0x2B, 0, 0); // 0x2B0000 + + LOG_DEBUG(Service_APT, "called"); + + IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); + rb.Push(apt->applet_manager->PrepareToJumpToHomeMenu()); +} + +void Module::APTInterface::JumpToHomeMenu(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp(ctx, 0x2C, 1, 4); // 0x2C0044 + const auto parameter_size = rp.Pop(); + const auto object = rp.PopGenericObject(); + const auto buffer = rp.PopStaticBuffer(); + + LOG_DEBUG(Service_APT, "called size={}", parameter_size); + + IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); + rb.Push(apt->applet_manager->JumpToHomeMenu(object, buffer)); +} + +void Module::APTInterface::PrepareToLeaveHomeMenu(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp(ctx, 0x2D, 0, 0); // 0x2D0000 + + LOG_DEBUG(Service_APT, "called"); + + IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); + rb.Push(apt->applet_manager->PrepareToLeaveHomeMenu()); +} + +void Module::APTInterface::LeaveHomeMenu(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp(ctx, 0x2E, 1, 4); // 0x2E0044 + const auto parameter_size = rp.Pop(); + const auto object = rp.PopGenericObject(); + const auto buffer = rp.PopStaticBuffer(); + + LOG_DEBUG(Service_APT, "called size={}", parameter_size); + + IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); + rb.Push(apt->applet_manager->LeaveHomeMenu(object, buffer)); +} + void Module::APTInterface::LoadSysMenuArg(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp(ctx, 0x36, 1, 0); // 0x00360040 const auto size = std::min(std::size_t{rp.Pop()}, SysMenuArgSize); @@ -794,8 +933,7 @@ void Module::APTInterface::SendCaptureBufferInfo(Kernel::HLERequestContext& ctx) LOG_DEBUG(Service_APT, "called"); - ASSERT(size == 0x20); - apt->screen_capture_buffer = buffer; + apt->applet_manager->SendCaptureBufferInfo(buffer); IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); rb.Push(RESULT_SUCCESS); @@ -807,12 +945,14 @@ void Module::APTInterface::ReceiveCaptureBufferInfo(Kernel::HLERequestContext& c LOG_DEBUG(Service_APT, "called"); - ASSERT(size == 0x20); + auto screen_capture_buffer = apt->applet_manager->ReceiveCaptureBufferInfo(); + auto real_size = std::min(static_cast(screen_capture_buffer.size()), size); + screen_capture_buffer.resize(size); IPC::RequestBuilder rb = rp.MakeBuilder(2, 2); rb.Push(RESULT_SUCCESS); - rb.Push(static_cast(apt->screen_capture_buffer.size())); - rb.PushStaticBuffer(std::move(apt->screen_capture_buffer), 0); + rb.Push(real_size); + rb.PushStaticBuffer(std::move(screen_capture_buffer), 0); } void Module::APTInterface::GetCaptureInfo(Kernel::HLERequestContext& ctx) { @@ -820,13 +960,15 @@ void Module::APTInterface::GetCaptureInfo(Kernel::HLERequestContext& ctx) { const auto size = rp.Pop(); LOG_DEBUG(Service_APT, "called"); - ASSERT(size == 0x20); + + auto screen_capture_buffer = apt->applet_manager->GetCaptureInfo(); + auto real_size = std::min(static_cast(screen_capture_buffer.size()), size); + screen_capture_buffer.resize(size); IPC::RequestBuilder rb = rp.MakeBuilder(2, 2); rb.Push(RESULT_SUCCESS); - rb.Push(static_cast(apt->screen_capture_buffer.size())); - // This service function does not clear the capture buffer. - rb.PushStaticBuffer(apt->screen_capture_buffer, 0); + rb.Push(real_size); + rb.PushStaticBuffer(std::move(screen_capture_buffer), 0); } void Module::APTInterface::SetScreenCapPostPermission(Kernel::HLERequestContext& ctx) { diff --git a/src/core/hle/service/apt/apt.h b/src/core/hle/service/apt/apt.h index e831a7d77..90688ff85 100644 --- a/src/core/hle/service/apt/apt.h +++ b/src/core/hle/service/apt/apt.h @@ -31,20 +31,6 @@ class AppletManager; /// Each APT service can only have up to 2 sessions connected at the same time. static const u32 MaxAPTSessions = 2; -/// Used by the application to pass information about the current framebuffer to applets. -struct CaptureBufferInfo { - u32_le size; - u8 is_3d; - INSERT_PADDING_BYTES(0x3); // Padding for alignment - u32_le top_screen_left_offset; - u32_le top_screen_right_offset; - u32_le top_screen_format; - u32_le bottom_screen_left_offset; - u32_le bottom_screen_right_offset; - u32_le bottom_screen_format; -}; -static_assert(sizeof(CaptureBufferInfo) == 0x20, "CaptureBufferInfo struct has incorrect size"); - constexpr std::size_t SysMenuArgSize = 0x40; enum class StartupArgumentType : u32 { @@ -87,6 +73,37 @@ public: * 0 : Result of function, 0 on success, otherwise error code */ void SetWirelessRebootInfo(Kernel::HLERequestContext& ctx); + + /** + * NS::ShutdownAsync service function. + * Inputs: + * 1 : None + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + */ + void ShutdownAsync(Kernel::HLERequestContext& ctx); + + /** + * NS::RebootSystem service function. + * Inputs: + * 1 : Boolean indicating whether to launch a title. + * 2-3 : Title ID + * 4 : Media Type + * 5 : Padding + * 6 : Launch memory type + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + */ + void RebootSystem(Kernel::HLERequestContext& ctx); + + /** + * NS::RebootSystemClean service function. + * Inputs: + * 1 : None + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + */ + void RebootSystemClean(Kernel::HLERequestContext& ctx); }; class APTInterface : public ServiceFramework { @@ -371,6 +388,16 @@ public: */ void WakeupApplication(Kernel::HLERequestContext& ctx); + /** + * APT::CancelApplication service function. + * Inputs: + * 0 : Command header [0x001D0000] + * Outputs: + * 0 : Return Header + * 1 : Result of function, 0 on success, otherwise error code + */ + void CancelApplication(Kernel::HLERequestContext& ctx); + /** * APT::AppletUtility service function * Inputs: @@ -491,6 +518,27 @@ public: */ void StartSystemApplet(Kernel::HLERequestContext& ctx); + /** + * APT::OrderToCloseApplication service function + * Inputs: + * 0 : Command header [0x00210000] + * Outputs: + * 0 : Header code + * 1 : Result code + */ + void OrderToCloseApplication(Kernel::HLERequestContext& ctx); + + /** + * APT::PrepareToCloseApplication service function + * Inputs: + * 0 : Command header [0x00220040] + * 1 : Boolean indicating whether to cancel applet preloads. + * Outputs: + * 0 : Header code + * 1 : Result code + */ + void PrepareToCloseApplication(Kernel::HLERequestContext& ctx); + /** * APT::CloseApplication service function * Inputs: @@ -629,6 +677,66 @@ public: */ void CloseSystemApplet(Kernel::HLERequestContext& ctx); + /** + * APT::OrderToCloseSystemApplet service function + * Inputs: + * 0 : Command header [0x002A0000] + * Outputs: + * 0 : Header code + * 1 : Result code + */ + void OrderToCloseSystemApplet(Kernel::HLERequestContext& ctx); + + /** + * APT::PrepareToJumpToHomeMenu service function + * Inputs: + * 0 : Command header [0x002B0000] + * Outputs: + * 0 : Header code + * 1 : Result code + */ + void PrepareToJumpToHomeMenu(Kernel::HLERequestContext& ctx); + + /** + * APT::JumpToHomeMenu service function + * Inputs: + * 0 : Command header [0x002C0044] + * 1 : Buffer size + * 2 : 0x0 + * 3 : Object handle + * 4 : (Size << 14) | 2 + * 5 : Input buffer virtual address + * Outputs: + * 0 : Header code + * 1 : Result code + */ + void JumpToHomeMenu(Kernel::HLERequestContext& ctx); + + /** + * APT::PrepareToLeaveHomeMenu service function + * Inputs: + * 0 : Command header [0x002B0000] + * Outputs: + * 0 : Header code + * 1 : Result code + */ + void PrepareToLeaveHomeMenu(Kernel::HLERequestContext& ctx); + + /** + * APT::LeaveHomeMenu service function + * Inputs: + * 0 : Command header [0x002C0044] + * 1 : Buffer size + * 2 : 0x0 + * 3 : Object handle + * 4 : (Size << 14) | 2 + * 5 : Input buffer virtual address + * Outputs: + * 0 : Header code + * 1 : Result code + */ + void LeaveHomeMenu(Kernel::HLERequestContext& ctx); + /** * APT::LoadSysMenuArg service function * Inputs: @@ -801,7 +909,6 @@ private: // APT::CheckNew3DSApp will check this unknown_ns_state_field to determine processing mode u8 unknown_ns_state_field = 0; - std::vector screen_capture_buffer; std::array sys_menu_arg_buffer; ScreencapPostPermission screen_capture_post_permission = diff --git a/src/core/hle/service/apt/apt_a.cpp b/src/core/hle/service/apt/apt_a.cpp index 091ccaf0e..824e2b176 100644 --- a/src/core/hle/service/apt/apt_a.cpp +++ b/src/core/hle/service/apt/apt_a.cpp @@ -42,8 +42,8 @@ APT_A::APT_A(std::shared_ptr apt) {0x001E0084, &APT_A::StartLibraryApplet, "StartLibraryApplet"}, {0x001F0084, &APT_A::StartSystemApplet, "StartSystemApplet"}, {0x00200044, nullptr, "StartNewestHomeMenu"}, - {0x00210000, nullptr, "OrderToCloseApplication"}, - {0x00220040, nullptr, "PrepareToCloseApplication"}, + {0x00210000, &APT_A::OrderToCloseApplication, "OrderToCloseApplication"}, + {0x00220040, &APT_A::PrepareToCloseApplication, "PrepareToCloseApplication"}, {0x00230040, nullptr, "PrepareToJumpToApplication"}, {0x00240044, nullptr, "JumpToApplication"}, {0x002500C0, &APT_A::PrepareToCloseLibraryApplet, "PrepareToCloseLibraryApplet"}, @@ -51,11 +51,11 @@ APT_A::APT_A(std::shared_ptr apt) {0x00270044, &APT_A::CloseApplication, "CloseApplication"}, {0x00280044, &APT_A::CloseLibraryApplet, "CloseLibraryApplet"}, {0x00290044, &APT_A::CloseSystemApplet, "CloseSystemApplet"}, - {0x002A0000, nullptr, "OrderToCloseSystemApplet"}, - {0x002B0000, nullptr, "PrepareToJumpToHomeMenu"}, - {0x002C0044, nullptr, "JumpToHomeMenu"}, - {0x002D0000, nullptr, "PrepareToLeaveHomeMenu"}, - {0x002E0044, nullptr, "LeaveHomeMenu"}, + {0x002A0000, &APT_A::OrderToCloseSystemApplet, "OrderToCloseSystemApplet"}, + {0x002B0000, &APT_A::PrepareToJumpToHomeMenu, "PrepareToJumpToHomeMenu"}, + {0x002C0044, &APT_A::JumpToHomeMenu, "JumpToHomeMenu"}, + {0x002D0000, &APT_A::PrepareToLeaveHomeMenu, "PrepareToLeaveHomeMenu"}, + {0x002E0044, &APT_A::LeaveHomeMenu, "LeaveHomeMenu"}, {0x002F0040, nullptr, "PrepareToLeaveResidentApplet"}, {0x00300044, nullptr, "LeaveResidentApplet"}, {0x00310100, &APT_A::PrepareToDoApplicationJump, "PrepareToDoApplicationJump"}, diff --git a/src/core/hle/service/apt/apt_s.cpp b/src/core/hle/service/apt/apt_s.cpp index 98ee8cb61..639c0d934 100644 --- a/src/core/hle/service/apt/apt_s.cpp +++ b/src/core/hle/service/apt/apt_s.cpp @@ -42,8 +42,8 @@ APT_S::APT_S(std::shared_ptr apt) {0x001E0084, &APT_S::StartLibraryApplet, "StartLibraryApplet"}, {0x001F0084, &APT_S::StartSystemApplet, "StartSystemApplet"}, {0x00200044, nullptr, "StartNewestHomeMenu"}, - {0x00210000, nullptr, "OrderToCloseApplication"}, - {0x00220040, nullptr, "PrepareToCloseApplication"}, + {0x00210000, &APT_S::OrderToCloseApplication, "OrderToCloseApplication"}, + {0x00220040, &APT_S::PrepareToCloseApplication, "PrepareToCloseApplication"}, {0x00230040, nullptr, "PrepareToJumpToApplication"}, {0x00240044, nullptr, "JumpToApplication"}, {0x002500C0, &APT_S::PrepareToCloseLibraryApplet, "PrepareToCloseLibraryApplet"}, @@ -51,11 +51,11 @@ APT_S::APT_S(std::shared_ptr apt) {0x00270044, &APT_S::CloseApplication, "CloseApplication"}, {0x00280044, &APT_S::CloseLibraryApplet, "CloseLibraryApplet"}, {0x00290044, &APT_S::CloseSystemApplet, "CloseSystemApplet"}, - {0x002A0000, nullptr, "OrderToCloseSystemApplet"}, - {0x002B0000, nullptr, "PrepareToJumpToHomeMenu"}, - {0x002C0044, nullptr, "JumpToHomeMenu"}, - {0x002D0000, nullptr, "PrepareToLeaveHomeMenu"}, - {0x002E0044, nullptr, "LeaveHomeMenu"}, + {0x002A0000, &APT_S::OrderToCloseSystemApplet, "OrderToCloseSystemApplet"}, + {0x002B0000, &APT_S::PrepareToJumpToHomeMenu, "PrepareToJumpToHomeMenu"}, + {0x002C0044, &APT_S::JumpToHomeMenu, "JumpToHomeMenu"}, + {0x002D0000, &APT_S::PrepareToLeaveHomeMenu, "PrepareToLeaveHomeMenu"}, + {0x002E0044, &APT_S::LeaveHomeMenu, "LeaveHomeMenu"}, {0x002F0040, nullptr, "PrepareToLeaveResidentApplet"}, {0x00300044, nullptr, "LeaveResidentApplet"}, {0x00310100, &APT_S::PrepareToDoApplicationJump, "PrepareToDoApplicationJump"}, diff --git a/src/core/hle/service/apt/apt_u.cpp b/src/core/hle/service/apt/apt_u.cpp index 329187153..34e9bc491 100644 --- a/src/core/hle/service/apt/apt_u.cpp +++ b/src/core/hle/service/apt/apt_u.cpp @@ -42,8 +42,8 @@ APT_U::APT_U(std::shared_ptr apt) {0x001E0084, &APT_U::StartLibraryApplet, "StartLibraryApplet"}, {0x001F0084, &APT_U::StartSystemApplet, "StartSystemApplet"}, {0x00200044, nullptr, "StartNewestHomeMenu"}, - {0x00210000, nullptr, "OrderToCloseApplication"}, - {0x00220040, nullptr, "PrepareToCloseApplication"}, + {0x00210000, &APT_U::OrderToCloseApplication, "OrderToCloseApplication"}, + {0x00220040, &APT_U::PrepareToCloseApplication, "PrepareToCloseApplication"}, {0x00230040, nullptr, "PrepareToJumpToApplication"}, {0x00240044, nullptr, "JumpToApplication"}, {0x002500C0, &APT_U::PrepareToCloseLibraryApplet, "PrepareToCloseLibraryApplet"}, @@ -51,11 +51,11 @@ APT_U::APT_U(std::shared_ptr apt) {0x00270044, &APT_U::CloseApplication, "CloseApplication"}, {0x00280044, &APT_U::CloseLibraryApplet, "CloseLibraryApplet"}, {0x00290044, &APT_U::CloseSystemApplet, "CloseSystemApplet"}, - {0x002A0000, nullptr, "OrderToCloseSystemApplet"}, - {0x002B0000, nullptr, "PrepareToJumpToHomeMenu"}, - {0x002C0044, nullptr, "JumpToHomeMenu"}, - {0x002D0000, nullptr, "PrepareToLeaveHomeMenu"}, - {0x002E0044, nullptr, "LeaveHomeMenu"}, + {0x002A0000, &APT_U::OrderToCloseSystemApplet, "OrderToCloseSystemApplet"}, + {0x002B0000, &APT_U::PrepareToJumpToHomeMenu, "PrepareToJumpToHomeMenu"}, + {0x002C0044, &APT_U::JumpToHomeMenu, "JumpToHomeMenu"}, + {0x002D0000, &APT_U::PrepareToLeaveHomeMenu, "PrepareToLeaveHomeMenu"}, + {0x002E0044, &APT_U::LeaveHomeMenu, "LeaveHomeMenu"}, {0x002F0040, nullptr, "PrepareToLeaveResidentApplet"}, {0x00300044, nullptr, "LeaveResidentApplet"}, {0x00310100, &APT_U::PrepareToDoApplicationJump, "PrepareToDoApplicationJump"}, diff --git a/src/core/hle/service/apt/ns_s.cpp b/src/core/hle/service/apt/ns_s.cpp index 2988ea5d8..63666dcae 100644 --- a/src/core/hle/service/apt/ns_s.cpp +++ b/src/core/hle/service/apt/ns_s.cpp @@ -19,12 +19,12 @@ NS_S::NS_S(std::shared_ptr apt) {0x00070042, nullptr, "CardUpdateInitialize"}, {0x00080000, nullptr, "CardUpdateShutdown"}, {0x000D0140, nullptr, "SetTWLBannerHMAC"}, - {0x000E0000, nullptr, "ShutdownAsync"}, - {0x00100180, nullptr, "RebootSystem"}, + {0x000E0000, &NS_S::ShutdownAsync, "ShutdownAsync"}, + {0x00100180, &NS_S::RebootSystem, "RebootSystem"}, {0x00110100, nullptr, "TerminateTitle"}, {0x001200C0, nullptr, "SetApplicationCpuTimeLimit"}, {0x00150140, nullptr, "LaunchApplication"}, - {0x00160000, nullptr, "RebootSystemClean"}, + {0x00160000, &NS_S::RebootSystemClean, "RebootSystemClean"}, }; RegisterHandlers(functions); } diff --git a/src/core/hle/service/gsp/gsp_gpu.cpp b/src/core/hle/service/gsp/gsp_gpu.cpp index f1ae11615..144339e79 100644 --- a/src/core/hle/service/gsp/gsp_gpu.cpp +++ b/src/core/hle/service/gsp/gsp_gpu.cpp @@ -663,14 +663,17 @@ void GSP_GPU::TriggerCmdReqQueue(Kernel::HLERequestContext& ctx) { void GSP_GPU::ImportDisplayCaptureInfo(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp(ctx, 0x18, 0, 0); - // TODO(Subv): We're always returning the framebuffer structures for thread_id = 0, - // because we only support a single running application at a time. - // This should always return the framebuffer data that is currently displayed on the screen. + if (active_thread_id == UINT32_MAX) { + LOG_WARNING(Service_GSP, "Called without an active thread."); - u32 thread_id = 0; + // TODO: Find the right error code. + IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); + rb.Push(-1); + return; + } - FrameBufferUpdate* top_screen = GetFrameBufferInfo(thread_id, 0); - FrameBufferUpdate* bottom_screen = GetFrameBufferInfo(thread_id, 1); + FrameBufferUpdate* top_screen = GetFrameBufferInfo(active_thread_id, 0); + FrameBufferUpdate* bottom_screen = GetFrameBufferInfo(active_thread_id, 1); struct CaptureInfoEntry { u32_le address_left; @@ -700,6 +703,39 @@ void GSP_GPU::ImportDisplayCaptureInfo(Kernel::HLERequestContext& ctx) { LOG_WARNING(Service_GSP, "called"); } +void GSP_GPU::SaveVramSysArea(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp(ctx, 0x19, 0, 0); + + LOG_INFO(Service_GSP, "called"); + + // TODO: This should also DMA framebuffers into VRAM and save LCD register state. + Memory::RasterizerFlushVirtualRegion(Memory::VRAM_VADDR, Memory::VRAM_SIZE, + Memory::FlushMode::Flush); + auto vram = system.Memory().GetPointer(Memory::VRAM_VADDR); + saved_vram.emplace(std::vector(Memory::VRAM_SIZE)); + std::memcpy(saved_vram.get().data(), vram, Memory::VRAM_SIZE); + + IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); + rb.Push(RESULT_SUCCESS); +} + +void GSP_GPU::RestoreVramSysArea(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp(ctx, 0x1A, 0, 0); + + LOG_INFO(Service_GSP, "called"); + + if (saved_vram) { + // TODO: This should also restore LCD register state. + auto vram = system.Memory().GetPointer(Memory::VRAM_VADDR); + std::memcpy(vram, saved_vram.get().data(), Memory::VRAM_SIZE); + Memory::RasterizerFlushVirtualRegion(Memory::VRAM_VADDR, Memory::VRAM_SIZE, + Memory::FlushMode::Invalidate); + } + + IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); + rb.Push(RESULT_SUCCESS); +} + void GSP_GPU::AcquireRight(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp(ctx, 0x16, 1, 2); @@ -808,8 +844,8 @@ GSP_GPU::GSP_GPU(Core::System& system) : ServiceFramework("gsp::Gpu", 2), system {0x00160042, &GSP_GPU::AcquireRight, "AcquireRight"}, {0x00170000, &GSP_GPU::ReleaseRight, "ReleaseRight"}, {0x00180000, &GSP_GPU::ImportDisplayCaptureInfo, "ImportDisplayCaptureInfo"}, - {0x00190000, nullptr, "SaveVramSysArea"}, - {0x001A0000, nullptr, "RestoreVramSysArea"}, + {0x00190000, &GSP_GPU::SaveVramSysArea, "SaveVramSysArea"}, + {0x001A0000, &GSP_GPU::RestoreVramSysArea, "RestoreVramSysArea"}, {0x001B0000, nullptr, "ResetGpuCore"}, {0x001C0040, &GSP_GPU::SetLedForceOff, "SetLedForceOff"}, {0x001D0040, nullptr, "SetTestCommand"}, diff --git a/src/core/hle/service/gsp/gsp_gpu.h b/src/core/hle/service/gsp/gsp_gpu.h index 0a8517c8c..cf976654e 100644 --- a/src/core/hle/service/gsp/gsp_gpu.h +++ b/src/core/hle/service/gsp/gsp_gpu.h @@ -8,6 +8,7 @@ #include #include #include +#include #include #include "common/bit_field.h" #include "common/common_types.h" @@ -238,6 +239,13 @@ public: */ FrameBufferUpdate* GetFrameBufferInfo(u32 thread_id, u32 screen_index); + /** + * Retreives the ID of the thread with GPU rights. + */ + u32 GetActiveThreadId() { + return active_thread_id; + } + private: /** * Signals that the specified interrupt type has occurred to userland code for the specified GSP @@ -402,6 +410,32 @@ private: */ void ImportDisplayCaptureInfo(Kernel::HLERequestContext& ctx); + /** + * GSP_GPU::SaveVramSysArea service function + * + * Returns information about the current framebuffer state + * + * Inputs: + * 0: Header 0x00190000 + * Outputs: + * 0: Header Code[0x00190040] + * 1: Result code + */ + void SaveVramSysArea(Kernel::HLERequestContext& ctx); + + /** + * GSP_GPU::RestoreVramSysArea service function + * + * Returns information about the current framebuffer state + * + * Inputs: + * 0: Header 0x001A0000 + * Outputs: + * 0: Header Code[0x001A0040] + * 1: Result code + */ + void RestoreVramSysArea(Kernel::HLERequestContext& ctx); + /** * GSP_GPU::StoreDataCache service function * @@ -438,6 +472,9 @@ private: bool first_initialization = true; + /// VRAM copy saved using SaveVramSysArea. + boost::optional> saved_vram; + /// Maximum number of threads that can be registered at the same time in the GSP module. static constexpr u32 MaxGSPThreads = 4; @@ -453,6 +490,7 @@ private: ar& active_thread_id; ar& first_initialization; ar& used_thread_ids; + ar& saved_vram; } friend class boost::serialization::access;