diff --git a/src/core/hle/service/am/applet_manager.cpp b/src/core/hle/service/am/applet_manager.cpp index 2ed79880a..bd1b430eb 100644 --- a/src/core/hle/service/am/applet_manager.cpp +++ b/src/core/hle/service/am/applet_manager.cpp @@ -5,18 +5,22 @@ #include "common/uuid.h" #include "core/core.h" #include "core/core_timing.h" +#include "core/file_sys/content_archive.h" +#include "core/file_sys/nca_metadata.h" #include "core/hle/service/acc/profile_manager.h" -#include "core/hle/service/am/process_creation.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/process_creation.h" #include "core/hle/service/am/service/storage.h" #include "core/hle/service/am/window_system.h" +#include "core/hle/service/filesystem/filesystem.h" #include "hid_core/hid_types.h" + namespace Service::AM { namespace { @@ -264,8 +268,10 @@ void AppletManager::SetWindowSystem(WindowSystem* window_system) { m_cv.wait(lk, [&] { return m_pending_process != nullptr; }); if (true && m_window_system->GetOverlayDisplayApplet() == nullptr) { - if (auto overlay_process = CreateProcess(m_system, static_cast(AppletProgramId::OverlayDisplay), 0, 0)) { - auto overlay_applet = std::make_shared(m_system, std::move(overlay_process), false); + if (auto overlay_process = + CreateProcess(m_system, static_cast(AppletProgramId::OverlayDisplay), 0, 0)) { + auto overlay_applet = + std::make_shared(m_system, std::move(overlay_process), false); overlay_applet->program_id = static_cast(AppletProgramId::OverlayDisplay); overlay_applet->applet_id = AppletId::OverlayDisplay; overlay_applet->type = AppletType::OverlayApplet; @@ -275,7 +281,8 @@ void AppletManager::SetWindowSystem(WindowSystem* window_system) { overlay_applet->home_button_long_pressed_blocked = false; m_window_system->TrackApplet(overlay_applet, false); overlay_applet->process->Run(); - LOG_INFO(Service_AM, "called, Overlay applet launched before application (initially hidden, watching home button)"); + LOG_INFO(Service_AM, "called, Overlay applet launched before application (initially " + "hidden, watching home button)"); } } @@ -291,6 +298,37 @@ void AppletManager::SetWindowSystem(WindowSystem* window_system) { // Push UserChannel data from previous application if (params.launch_type == LaunchType::ApplicationInitiated) { applet->user_channel_launch_parameter.swap(m_system.GetUserChannel()); + + // Register game NCAs for QLaunch DLC support + m_manual_provider.ClearAllEntries(); + const auto title_id = params.program_id; + auto& system_provider = m_system.GetContentProviderUnion(); + + LOG_INFO(Service_AM, "QLaunch Support: Registering NCAs for title_id={:016X}", title_id); + + // Register Program NCA + auto game_nca = system_provider.GetEntry(title_id, FileSys::ContentRecordType::Program); + if (game_nca) { + m_manual_provider.AddEntry(FileSys::TitleType::Application, + FileSys::ContentRecordType::Program, title_id, + game_nca->GetBaseFile()); + LOG_DEBUG(Service_AM, "Registered Program NCA"); + } else { + LOG_WARNING(Service_AM, "Program NCA not found for title_id={:016X}", title_id); + } + + // Register Control NCA + auto control_nca = system_provider.GetEntry(title_id, FileSys::ContentRecordType::Control); + if (control_nca) { + m_manual_provider.AddEntry(FileSys::TitleType::Application, + FileSys::ContentRecordType::Control, title_id, + control_nca->GetBaseFile()); + LOG_DEBUG(Service_AM, "Registered Control NCA"); + } + + // Update the system's manual content provider slot to point to our populated provider + system_provider.SetSlot(FileSys::ContentProviderUnionSlot::FrontendManual, + &m_manual_provider); } // TODO: Read whether we need a preselected user from NACP? diff --git a/src/core/hle/service/am/applet_manager.h b/src/core/hle/service/am/applet_manager.h index 893de2eb7..406074e0e 100644 --- a/src/core/hle/service/am/applet_manager.h +++ b/src/core/hle/service/am/applet_manager.h @@ -7,6 +7,7 @@ #include #include +#include "core/file_sys/registered_cache.h" #include "core/hle/service/am/am_types.h" namespace Core { @@ -63,6 +64,8 @@ private: FrontendAppletParameters m_pending_parameters{}; std::unique_ptr m_pending_process{}; + + FileSys::ManualContentProvider m_manual_provider; }; } // namespace Service::AM diff --git a/src/core/hle/service/am/frontend/applet_web_browser.cpp b/src/core/hle/service/am/frontend/applet_web_browser.cpp index 0c86273a3..90c280ce4 100644 --- a/src/core/hle/service/am/frontend/applet_web_browser.cpp +++ b/src/core/hle/service/am/frontend/applet_web_browser.cpp @@ -280,6 +280,9 @@ void WebBrowser::Initialize() { case ShimKind::Lobby: InitializeLobby(); break; + case ShimKind::Unknown8: + LOG_WARNING(Service_AM, "(STUBBED) called, Unknown8 Applet is not implemented"); + break; default: ASSERT_MSG(false, "Invalid ShimKind={}", web_arg_header.shim_kind); break; @@ -317,6 +320,9 @@ void WebBrowser::Execute() { case ShimKind::Lobby: ExecuteLobby(); break; + case ShimKind::Unknown8: + WebBrowserExit(WebExitReason::EndButtonPressed); + break; default: ASSERT_MSG(false, "Invalid ShimKind={}", web_arg_header.shim_kind); WebBrowserExit(WebExitReason::EndButtonPressed); 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 index 2f7c05c24..6ff8d4722 100644 --- a/src/core/hle/service/am/frontend/applet_web_browser_types.h +++ b/src/core/hle/service/am/frontend/applet_web_browser_types.h @@ -30,6 +30,7 @@ enum class ShimKind : u32 { Web = 5, Wifi = 6, Lobby = 7, + Unknown8 = 8, }; enum class WebExitReason : u32 { diff --git a/src/core/hle/service/filesystem/filesystem.cpp b/src/core/hle/service/filesystem/filesystem.cpp index fb50a2698..351ed1e34 100644 --- a/src/core/hle/service/filesystem/filesystem.cpp +++ b/src/core/hle/service/filesystem/filesystem.cpp @@ -29,7 +29,6 @@ #include "core/hle/service/filesystem/save_data_controller.h" #include "core/hle/service/server_manager.h" #include "core/loader/loader.h" - namespace Service::FileSystem { static FileSys::VirtualDir GetDirectoryRelativeWrapped(FileSys::VirtualDir base, @@ -226,7 +225,8 @@ Result VfsDirectoryServiceWrapper::RenameDirectory(const std::string& src_path_, // Different parent directories - need to move by copying then deleting. // Based on LibHac's approach: create dest, copy contents recursively, delete source. - LOG_DEBUG(Service_FS, "Moving directory across tree from \"{}\" to \"{}\"", src_path, dest_path); + LOG_DEBUG(Service_FS, "Moving directory across tree from \"{}\" to \"{}\"", src_path, + dest_path); // Create the destination directory auto dest_parent = GetDirectoryRelativeWrapped(backing, Common::FS::GetParentPath(dest_path)); @@ -429,7 +429,8 @@ std::shared_ptr FileSystemController::CreateSaveDataFa !Settings::values.global_custom_save_path.GetValue().empty()) { base_save_path_str = Settings::values.global_custom_save_path.GetValue(); - LOG_INFO(Service_FS, "Save Path: Using Global Custom Save Path as the base: {}", base_save_path_str); + LOG_INFO(Service_FS, "Save Path: Using Global Custom Save Path as the base: {}", + base_save_path_str); } else { base_save_path_str = Common::FS::GetCitronPathString(CitronPath::NANDDir); LOG_INFO(Service_FS, "Save Path: Using default NAND as the base."); @@ -440,10 +441,11 @@ std::shared_ptr FileSystemController::CreateSaveDataFa // 2. Check for Mirroring. if (Settings::values.mirrored_save_paths.count(program_id)) { LOG_INFO(Service_FS, - "Save Path: Mirroring detected for Program ID {:016X}. Syncing against the determined base directory.", + "Save Path: Mirroring detected for Program ID {:016X}. Syncing against the " + "determined base directory.", program_id); return std::make_shared(system, program_id, - std::move(base_directory)); + std::move(base_directory)); } // 3. Check for Per-Game Custom Path override. @@ -466,7 +468,6 @@ std::shared_ptr FileSystemController::CreateSaveDataFa LOG_INFO(Service_FS, "Save Path: No overrides found. Using the determined base directory."); return std::make_shared(system, program_id, std::move(base_directory)); - } Result FileSystemController::OpenSDMC(FileSys::VirtualDir* out_sdmc) const { @@ -616,7 +617,6 @@ FileSys::RegisteredCache* FileSystemController::GetSDMCContents() const { return sdmc_factory->GetSDMCContents(); } - FileSys::PlaceholderCache* FileSystemController::GetSystemNANDPlaceholder() const { LOG_TRACE(Service_FS, "Opening System NAND Placeholder"); diff --git a/src/core/hle/service/filesystem/filesystem.h b/src/core/hle/service/filesystem/filesystem.h index 6dc60254f..665d03ebc 100644 --- a/src/core/hle/service/filesystem/filesystem.h +++ b/src/core/hle/service/filesystem/filesystem.h @@ -92,7 +92,6 @@ public: FileSys::RegisteredCache* GetUserNANDContents() const; FileSys::RegisteredCache* GetSDMCContents() const; FileSys::RegisteredCache* GetGameCardContents() const; - FileSys::PlaceholderCache* GetSystemNANDPlaceholder() const; FileSys::PlaceholderCache* GetUserNANDPlaceholder() const; FileSys::PlaceholderCache* GetSDMCPlaceholder() const; @@ -122,7 +121,9 @@ public: void CreateFactories(FileSys::VfsFilesystem& vfs, bool overwrite = true); // getter for main.cpp to trigger the sync between custom game paths for separate emulators - FileSys::SaveDataFactory& GetSaveDataFactory() { return *global_save_data_factory; } + FileSys::SaveDataFactory& GetSaveDataFactory() { + return *global_save_data_factory; + } void Reset(); diff --git a/src/core/hle/service/olsc/daemon_controller.cpp b/src/core/hle/service/olsc/daemon_controller.cpp index 7823780a8..cc6f8d75b 100644 --- a/src/core/hle/service/olsc/daemon_controller.cpp +++ b/src/core/hle/service/olsc/daemon_controller.cpp @@ -1,4 +1,5 @@ // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 Citron Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later #include "core/hle/service/cmif_serialization.h" @@ -12,14 +13,14 @@ IDaemonController::IDaemonController(Core::System& system_) static const FunctionInfo functions[] = { {0, D<&IDaemonController::GetAutoTransferEnabledForAccountAndApplication>, "GetAutoTransferEnabledForAccountAndApplication"}, {1, nullptr, "SetAutoTransferEnabledForAccountAndApplication"}, - {2, nullptr, "GetGlobalUploadEnabledForAccount"}, - {3, nullptr, "SetGlobalUploadEnabledForAccount"}, + {2, D<&IDaemonController::GetGlobalUploadEnabledForAccount>, "GetGlobalUploadEnabledForAccount"}, + {3, D<&IDaemonController::SetGlobalUploadEnabledForAccount>, "SetGlobalUploadEnabledForAccount"}, {4, nullptr, "TouchAccount"}, - {5, nullptr, "GetGlobalDownloadEnabledForAccount"}, - {6, nullptr, "SetGlobalDownloadEnabledForAccount"}, + {5, D<&IDaemonController::GetGlobalDownloadEnabledForAccount>, "GetGlobalDownloadEnabledForAccount"}, + {6, D<&IDaemonController::SetGlobalDownloadEnabledForAccount>, "SetGlobalDownloadEnabledForAccount"}, {10, nullptr, "GetForbiddenSaveDataIndication"}, {11, nullptr, "GetStopperObject"}, - {12, nullptr, "GetState"}, + {12, D<&IDaemonController::GetAutonomyTaskStatus>, "GetAutonomyTaskStatus"}, }; // clang-format on @@ -37,4 +38,37 @@ Result IDaemonController::GetAutoTransferEnabledForAccountAndApplication(Out out_is_enabled, + Common::UUID user_id) { + LOG_WARNING(Service_OLSC, "(STUBBED) called, user_id={}", user_id.FormattedString()); + *out_is_enabled = false; + R_SUCCEED(); +} + +Result IDaemonController::SetGlobalUploadEnabledForAccount(bool is_enabled, Common::UUID user_id) { + LOG_WARNING(Service_OLSC, "(STUBBED) called, is_enabled={} user_id={}", is_enabled, + user_id.FormattedString()); + R_SUCCEED(); +} + +Result IDaemonController::GetGlobalDownloadEnabledForAccount(Out out_is_enabled, + Common::UUID user_id) { + LOG_WARNING(Service_OLSC, "(STUBBED) called, user_id={}", user_id.FormattedString()); + *out_is_enabled = false; + R_SUCCEED(); +} + +Result IDaemonController::SetGlobalDownloadEnabledForAccount(bool is_enabled, + Common::UUID user_id) { + LOG_WARNING(Service_OLSC, "(STUBBED) called, is_enabled={} user_id={}", is_enabled, + user_id.FormattedString()); + R_SUCCEED(); +} + +Result IDaemonController::GetAutonomyTaskStatus(Out out_status, Common::UUID user_id) { + LOG_WARNING(Service_OLSC, "(STUBBED) called, user_id={}", user_id.FormattedString()); + *out_status = 0; // Status: Idle + R_SUCCEED(); +} + } // namespace Service::OLSC diff --git a/src/core/hle/service/olsc/daemon_controller.h b/src/core/hle/service/olsc/daemon_controller.h index dfad7f52a..9d8b7db71 100644 --- a/src/core/hle/service/olsc/daemon_controller.h +++ b/src/core/hle/service/olsc/daemon_controller.h @@ -1,4 +1,5 @@ // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 Citron Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later #include "common/uuid.h" @@ -15,6 +16,11 @@ public: private: Result GetAutoTransferEnabledForAccountAndApplication(Out out_is_enabled, Common::UUID user_id, u64 application_id); + Result GetGlobalUploadEnabledForAccount(Out out_is_enabled, Common::UUID user_id); + Result SetGlobalUploadEnabledForAccount(bool is_enabled, Common::UUID user_id); + Result GetGlobalDownloadEnabledForAccount(Out out_is_enabled, Common::UUID user_id); + Result SetGlobalDownloadEnabledForAccount(bool is_enabled, Common::UUID user_id); + Result GetAutonomyTaskStatus(Out out_status, Common::UUID user_id); }; } // namespace Service::OLSC diff --git a/src/core/hle/service/olsc/remote_storage_controller.cpp b/src/core/hle/service/olsc/remote_storage_controller.cpp index 181fff1bf..63aa9ede0 100644 --- a/src/core/hle/service/olsc/remote_storage_controller.cpp +++ b/src/core/hle/service/olsc/remote_storage_controller.cpp @@ -1,4 +1,5 @@ // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 Citron Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later #include "core/hle/service/cmif_serialization.h" @@ -21,11 +22,11 @@ IRemoteStorageController::IRemoteStorageController(Core::System& system_) {11, nullptr, "CreateDeleteDataTask"}, {12, nullptr, "DeleteSeriesInfo"}, {13, nullptr, "CreateRegisterNotificationTokenTask"}, - {14, nullptr, "UpdateSeriesInfo"}, + {14, D<&IRemoteStorageController::GetDataNewnessByApplicationId>, "GetDataNewnessByApplicationId"}, {15, nullptr, "RegisterUploadSaveDataTransferTaskForAutonomyRegistration"}, {16, nullptr, "CreateCleanupToDeleteSaveDataArchiveInfoTask"}, {17, nullptr, "ListDataInfo"}, - {18, nullptr, "GetDataInfo"}, + {18, D<&IRemoteStorageController::GetDataInfo>, "GetDataInfo"}, {19, nullptr, "Unknown19"}, {20, nullptr, "CreateSaveDataArchiveInfoCacheForSaveDataBackupUpdationTask"}, {21, nullptr, "ListSecondarySaves"}, @@ -33,6 +34,7 @@ IRemoteStorageController::IRemoteStorageController(Core::System& system_) {23, nullptr, "TouchSecondarySave"}, {24, nullptr, "GetSecondarySaveDataInfo"}, {25, nullptr, "RegisterDownloadSaveDataTransferTaskForAutonomyRegistration"}, + {27, D<&IRemoteStorageController::GetDataInfo>, "GetDataInfoV2"}, // [20.0.0+] {28, D<&IRemoteStorageController::Unknown28>, "Unknown28"}, // [20.2.0+] {900, nullptr, "Unknown900"}, {901, D<&IRemoteStorageController::Unknown901>, "Unknown901"}, // [20.2.0+] @@ -44,6 +46,20 @@ IRemoteStorageController::IRemoteStorageController(Core::System& system_) IRemoteStorageController::~IRemoteStorageController() = default; +Result IRemoteStorageController::GetDataNewnessByApplicationId(Out out_newness, + u64 application_id) { + LOG_WARNING(Service_OLSC, "(STUBBED) called, application_id={:016X}", application_id); + *out_newness = 0; + R_SUCCEED(); +} + +Result IRemoteStorageController::GetDataInfo(Out> out_data, + u64 application_id) { + LOG_WARNING(Service_OLSC, "(STUBBED) called, application_id={:016X}", application_id); + out_data->fill(0); + R_SUCCEED(); +} + Result IRemoteStorageController::GetSecondarySave(Out out_has_secondary_save, Out> out_unknown, u64 application_id) { diff --git a/src/core/hle/service/olsc/remote_storage_controller.h b/src/core/hle/service/olsc/remote_storage_controller.h index 59869322b..65c47dced 100644 --- a/src/core/hle/service/olsc/remote_storage_controller.h +++ b/src/core/hle/service/olsc/remote_storage_controller.h @@ -1,4 +1,5 @@ // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 Citron Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later #include "core/hle/service/cmif_types.h" @@ -12,9 +13,11 @@ public: ~IRemoteStorageController() override; private: + Result GetDataNewnessByApplicationId(Out out_newness, u64 application_id); + Result GetDataInfo(Out> out_data, u64 application_id); Result GetSecondarySave(Out out_has_secondary_save, Out> out_unknown, u64 application_id); - Result Unknown28(); // [20.2.0+] + Result Unknown28(); // [20.2.0+] Result Unknown901(); // [20.2.0+] }; diff --git a/src/core/hle/service/olsc/transfer_task_list_controller.cpp b/src/core/hle/service/olsc/transfer_task_list_controller.cpp index 8ea9a0f1e..4d1ef4644 100644 --- a/src/core/hle/service/olsc/transfer_task_list_controller.cpp +++ b/src/core/hle/service/olsc/transfer_task_list_controller.cpp @@ -1,8 +1,10 @@ // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 Citron Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later #include "core/hle/service/cmif_serialization.h" #include "core/hle/service/olsc/native_handle_holder.h" +#include "core/hle/service/olsc/remote_storage_controller.h" #include "core/hle/service/olsc/transfer_task_list_controller.h" namespace Service::OLSC { @@ -19,7 +21,7 @@ ITransferTaskListController::ITransferTaskListController(Core::System& system_) {5, D<&ITransferTaskListController::GetNativeHandleHolder>, "GetNativeHandleHolder"}, {6, nullptr, "Unknown6"}, {7, nullptr, "Unknown7"}, - {8, nullptr, "GetRemoteStorageController"}, + {8, D<&ITransferTaskListController::GetRemoteStorageController>, "GetRemoteStorageController"}, {9, D<&ITransferTaskListController::GetNativeHandleHolder>, "GetNativeHandleHolder2"}, {10, nullptr, "Unknown10"}, {11, nullptr, "Unknown11"}, @@ -31,12 +33,12 @@ ITransferTaskListController::ITransferTaskListController(Core::System& system_) {17, nullptr, "Unknown17"}, {18, nullptr, "Unknown18"}, {19, nullptr, "Unknown19"}, - {20, nullptr, "Unknown20"}, + {20, D<&ITransferTaskListController::Unknown20>, "Unknown20"}, {21, nullptr, "Unknown21"}, {22, nullptr, "Unknown22"}, {23, nullptr, "Unknown23"}, - {24, nullptr, "Unknown24"}, - {25, nullptr, "Unknown25"}, + {24, D<&ITransferTaskListController::GetCurrentTransferTaskInfo>, "GetCurrentTransferTaskInfo"}, + {25, D<&ITransferTaskListController::FindTransferTaskInfo>, "FindTransferTaskInfo"}, }; // clang-format on @@ -52,4 +54,30 @@ Result ITransferTaskListController::GetNativeHandleHolder( R_SUCCEED(); } +Result ITransferTaskListController::GetRemoteStorageController( + Out> out_controller) { + LOG_WARNING(Service_OLSC, "(STUBBED) called"); + *out_controller = std::make_shared(system); + R_SUCCEED(); +} + +Result ITransferTaskListController::Unknown20() { + LOG_WARNING(Service_OLSC, "(STUBBED) called"); + R_SUCCEED(); +} + +Result ITransferTaskListController::GetCurrentTransferTaskInfo(Out> out_info, + u8 unknown) { + LOG_WARNING(Service_OLSC, "(STUBBED) called, unknown={:#x}", unknown); + out_info->fill(0); + R_SUCCEED(); +} + +Result ITransferTaskListController::FindTransferTaskInfo(Out> out_info, + InBuffer in) { + LOG_WARNING(Service_OLSC, "(STUBBED) called, in_size={}", in.size()); + out_info->fill(0); + R_SUCCEED(); +} + } // namespace Service::OLSC diff --git a/src/core/hle/service/olsc/transfer_task_list_controller.h b/src/core/hle/service/olsc/transfer_task_list_controller.h index f10a71375..23d555a27 100644 --- a/src/core/hle/service/olsc/transfer_task_list_controller.h +++ b/src/core/hle/service/olsc/transfer_task_list_controller.h @@ -1,4 +1,5 @@ // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 Citron Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later #include "core/hle/service/cmif_types.h" @@ -7,6 +8,7 @@ namespace Service::OLSC { class INativeHandleHolder; +class IRemoteStorageController; class ITransferTaskListController final : public ServiceFramework { public: @@ -15,6 +17,11 @@ public: private: Result GetNativeHandleHolder(Out> out_holder); + Result GetRemoteStorageController(Out> out_controller); + Result Unknown20(); + Result GetCurrentTransferTaskInfo(Out> out_info, u8 unknown); + Result FindTransferTaskInfo(Out> out_info, + InBuffer in); }; } // namespace Service::OLSC