From e2ebc37cd013c051a96d8d1875cb0949d405dc63 Mon Sep 17 00:00:00 2001 From: lizzie Date: Tue, 24 Mar 2026 22:05:19 +0000 Subject: [PATCH 01/18] [hle/sm] implement sm:AtmosphereHasService Signed-off-by: lizzie --- src/core/hle/service/sm/sm.cpp | 35 ++++++++++++++++++++++++++++++++-- 1 file changed, 33 insertions(+), 2 deletions(-) diff --git a/src/core/hle/service/sm/sm.cpp b/src/core/hle/service/sm/sm.cpp index 1095dcf6c3..6606e185aa 100644 --- a/src/core/hle/service/sm/sm.cpp +++ b/src/core/hle/service/sm/sm.cpp @@ -250,15 +250,37 @@ void SM::UnregisterService(HLERequestContext& ctx) { rb.Push(service_manager.UnregisterService(name)); } +void SM::AtmosphereHasService(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + std::string name(PopServiceName(rp)); + LOG_WARNING(Service_SM, "(stubbed) called with name={}", name); + IPC::ResponseBuilder rb{ctx, 3}; + Kernel::KClientPort* out_client_port = nullptr; + rb.Push(ResultSuccess); + rb.Push(service_manager.GetServicePort(&out_client_port, name) == ResultSuccess); +} + SM::SM(ServiceManager& service_manager_, Core::System& system_) - : ServiceFramework{system_, "sm:", 4}, - service_manager{service_manager_}, kernel{system_.Kernel()} { + : ServiceFramework{system_, "sm:", 4} + , service_manager{service_manager_} + , kernel{system_.Kernel()} +{ RegisterHandlers({ {0, &SM::Initialize, "Initialize"}, {1, &SM::GetServiceCmif, "GetService"}, {2, &SM::RegisterServiceCmif, "RegisterService"}, {3, &SM::UnregisterService, "UnregisterService"}, {4, nullptr, "DetachClient"}, + // TODO: are these non-TIPC as well? + {65000, nullptr, "AtmosphereInstallMitm"}, + {65001, nullptr, "AtmosphereUninstallMitm"}, + {65002, nullptr, "Deprecated_AtmosphereAssociatePidTidForMitm"}, + {65003, nullptr, "AtmosphereAcknowledgeMitmSession"}, + {65004, nullptr, "AtmosphereHasMitm"}, + {65005, nullptr, "AtmosphereWaitMitm"}, + {65006, nullptr, "AtmosphereDeclareFutureMitm"}, + {65100, &SM::AtmosphereHasService, "AtmosphereHasService"}, + {65101, nullptr, "AtmosphereWaitService"}, }); RegisterHandlersTipc({ {0, &SM::Initialize, "Initialize"}, @@ -266,6 +288,15 @@ SM::SM(ServiceManager& service_manager_, Core::System& system_) {2, &SM::RegisterServiceTipc, "RegisterService"}, {3, &SM::UnregisterService, "UnregisterService"}, {4, nullptr, "DetachClient"}, + {65000, nullptr, "AtmosphereInstallMitm"}, + {65001, nullptr, "AtmosphereUninstallMitm"}, + {65002, nullptr, "Deprecated_AtmosphereAssociatePidTidForMitm"}, + {65003, nullptr, "AtmosphereAcknowledgeMitmSession"}, + {65004, nullptr, "AtmosphereHasMitm"}, + {65005, nullptr, "AtmosphereWaitMitm"}, + {65006, nullptr, "AtmosphereDeclareFutureMitm"}, + {65100, &SM::AtmosphereHasService, "AtmosphereHasService"}, + {65101, nullptr, "AtmosphereWaitService"}, }); } From cd38b261a4db3a10be4ee12bd34aa0327c5421d6 Mon Sep 17 00:00:00 2001 From: lizzie Date: Tue, 24 Mar 2026 22:05:41 +0000 Subject: [PATCH 02/18] oops, fix --- src/core/hle/service/sm/sm.cpp | 4 ++++ src/core/hle/service/sm/sm.h | 1 + 2 files changed, 5 insertions(+) diff --git a/src/core/hle/service/sm/sm.cpp b/src/core/hle/service/sm/sm.cpp index 6606e185aa..5332fa3615 100644 --- a/src/core/hle/service/sm/sm.cpp +++ b/src/core/hle/service/sm/sm.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -11,6 +14,7 @@ #include "core/hle/kernel/k_scoped_resource_reservation.h" #include "core/hle/kernel/k_server_port.h" #include "core/hle/result.h" +#include "core/hle/service/cmif_types.h" #include "core/hle/service/ipc_helpers.h" #include "core/hle/service/server_manager.h" #include "core/hle/service/sm/sm.h" diff --git a/src/core/hle/service/sm/sm.h b/src/core/hle/service/sm/sm.h index 65e6876926..b566452ad7 100644 --- a/src/core/hle/service/sm/sm.h +++ b/src/core/hle/service/sm/sm.h @@ -47,6 +47,7 @@ private: void RegisterServiceCmif(HLERequestContext& ctx); void RegisterServiceTipc(HLERequestContext& ctx); void UnregisterService(HLERequestContext& ctx); + void AtmosphereHasService(HLERequestContext& ctx); Result GetServiceImpl(Kernel::KClientSession** out_client_session, HLERequestContext& ctx); void RegisterServiceImpl(HLERequestContext& ctx, std::string name, u32 max_session_count, From 5ab562a9c02175ed9743850bdcc61dab8830a5ad Mon Sep 17 00:00:00 2001 From: lizzie Date: Wed, 25 Mar 2026 01:50:23 +0000 Subject: [PATCH 03/18] [hle] attempt support ULaunch Signed-off-by: lizzie --- src/core/CMakeLists.txt | 2 ++ src/core/hle/service/ulsf/ulsf.cpp | 39 ++++++++++++++++++++++++++++++ src/core/hle/service/ulsf/ulsf.h | 28 +++++++++++++++++++++ 3 files changed, 69 insertions(+) create mode 100644 src/core/hle/service/ulsf/ulsf.cpp create mode 100644 src/core/hle/service/ulsf/ulsf.h diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 39aebd5f48..615080ec43 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -1089,6 +1089,8 @@ add_library(core STATIC hle/service/ssl/ssl.h hle/service/ssl/ssl_backend.h hle/service/ssl/ssl_types.h + hle/service/ulsf/ulsf.cpp + hle/service/ulsf/ulsf.h hle/service/usb/usb.cpp hle/service/usb/usb.h hle/service/vi/application_display_service.cpp diff --git a/src/core/hle/service/ulsf/ulsf.cpp b/src/core/hle/service/ulsf/ulsf.cpp new file mode 100644 index 0000000000..d218511f4a --- /dev/null +++ b/src/core/hle/service/ulsf/ulsf.cpp @@ -0,0 +1,39 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#include +#include "core/hle/service/ipc_helpers.h" +#include "core/hle/service/ulsf/ulsf.h" +#include "core/hle/service/server_manager.h" + +namespace Service::ULSF { + +ULSF_U::ULSF_U(Core::System& system_) : ServiceFramework{system_, "ulsf:u"} { + static const FunctionInfo functions[] = { + {0, &ULSF_U::GetVersion, "GetVersion"}, + }; + RegisterHandlers(functions); +} +ULSF_U::~ULSF_U() = default; + +// Result ULSF_U::GetVersion(Out out_version) { +// LOG_WARNING(Service_SM, "(stubbed)"); +// *out_version = {}; +// R_SUCCEED(); +// } + +void ULSF_U::GetVersion(HLERequestContext& ctx) { + LOG_WARNING(Service_SM, "(stubbed)"); + ULauncherVersion version{1, 6, 7}; + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); + rb.PushRaw(version); +} + +void LoopProcess(Core::System& system) { + auto server_manager = std::make_unique(system); + server_manager->RegisterNamedService("ulsf:u", std::make_shared(system)); + ServerManager::RunServer(std::move(server_manager)); +} + +} diff --git a/src/core/hle/service/ulsf/ulsf.h b/src/core/hle/service/ulsf/ulsf.h new file mode 100644 index 0000000000..61fca1ddc4 --- /dev/null +++ b/src/core/hle/service/ulsf/ulsf.h @@ -0,0 +1,28 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include "common/common_types.h" +#include "core/hle/service/cmif_types.h" +#include "core/hle/service/service.h" + +namespace Service::ULSF { + +struct ULauncherVersion { + u8 major; + u8 minor; + u8 micro; +}; + +class ULSF_U final : public ServiceFramework { +public: + explicit ULSF_U(Core::System& system_); + ~ULSF_U(); + //Result GetVersion(Out out_version); + void GetVersion(HLERequestContext& ctx); +}; + +void LoopProcess(Core::System& system); + +} From 5d60dc79bb8c9216e78cce4ae8503e6c6c97ad43 Mon Sep 17 00:00:00 2001 From: lizzie Date: Wed, 25 Mar 2026 01:54:06 +0000 Subject: [PATCH 04/18] register ulsf as guest process --- src/core/hle/service/services.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/core/hle/service/services.cpp b/src/core/hle/service/services.cpp index 636f54ad49..a2fe5b6cb3 100644 --- a/src/core/hle/service/services.cpp +++ b/src/core/hle/service/services.cpp @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project @@ -62,6 +62,7 @@ #include "core/hle/service/sockets/sockets.h" #include "core/hle/service/spl/spl_module.h" #include "core/hle/service/ssl/ssl.h" +#include "core/hle/service/ulsf/ulsf.h" #include "core/hle/service/usb/usb.h" #include "core/hle/service/vi/vi.h" From fb4385f7ea2d6ee124aaf8688f63950550cbd127 Mon Sep 17 00:00:00 2001 From: lizzie Date: Wed, 25 Mar 2026 02:15:27 +0000 Subject: [PATCH 05/18] I FORGOT LOOP PROCESS FFS --- src/core/hle/service/services.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/core/hle/service/services.cpp b/src/core/hle/service/services.cpp index a2fe5b6cb3..da55f9dc65 100644 --- a/src/core/hle/service/services.cpp +++ b/src/core/hle/service/services.cpp @@ -145,7 +145,8 @@ Services::Services(std::shared_ptr& sm, Core::System& system {"ro", &RO::LoopProcess}, {"spl", &SPL::LoopProcess}, {"ssl", &SSL::LoopProcess}, - {"usb", &USB::LoopProcess} + {"usb", &USB::LoopProcess}, + {"ulsf", &ULSF::LoopProcess}, }) kernel.RunOnGuestCoreProcess(std::string(e.first), [&system, f = e.second] { f(system); }); } From 610c75759245dbdcd6ce6215bb7fe3cbe1073580 Mon Sep 17 00:00:00 2001 From: lizzie Date: Wed, 20 May 2026 00:35:45 +0000 Subject: [PATCH 06/18] Fix NSO loading --- .../loader/deconstructed_rom_directory.cpp | 4 +-- src/core/loader/nso.cpp | 36 +++++++++++++++---- src/core/loader/nso.h | 11 +++--- src/yuzu/main_window.cpp | 2 +- 4 files changed, 39 insertions(+), 14 deletions(-) diff --git a/src/core/loader/deconstructed_rom_directory.cpp b/src/core/loader/deconstructed_rom_directory.cpp index afa5cdbc36..9154c6d971 100644 --- a/src/core/loader/deconstructed_rom_directory.cpp +++ b/src/core/loader/deconstructed_rom_directory.cpp @@ -204,7 +204,7 @@ AppLoader_DeconstructedRomDirectory::LoadResult AppLoader_DeconstructedRomDirect const bool should_pass_arguments = std::strcmp(module, "rtld") == 0; const auto tentative_next_load_addr = AppLoader_NSO::LoadModule( - process, system, *module_file, code_size, should_pass_arguments, false, {}, + process, system, *module_file, {}, code_size, should_pass_arguments, false, nullptr, {}, patch_ctx.GetPatchers(), patch_ctx.GetLastIndex()); if (!tentative_next_load_addr) { return {ResultStatus::ErrorLoadingNSO, {}}; @@ -252,7 +252,7 @@ AppLoader_DeconstructedRomDirectory::LoadResult AppLoader_DeconstructedRomDirect const VAddr load_addr{next_load_addr}; const bool should_pass_arguments = std::strcmp(module, "rtld") == 0; const auto tentative_next_load_addr = AppLoader_NSO::LoadModule( - process, system, *module_file, load_addr, should_pass_arguments, true, pm, + process, system, *module_file, {}, load_addr, should_pass_arguments, true, nullptr, pm, patch_ctx.GetPatchers(), patch_ctx.GetIndex(i)); if (!tentative_next_load_addr) { return {ResultStatus::ErrorLoadingNSO, {}}; diff --git a/src/core/loader/nso.cpp b/src/core/loader/nso.cpp index 482c853542..6e6733223d 100644 --- a/src/core/loader/nso.cpp +++ b/src/core/loader/nso.cpp @@ -17,6 +17,7 @@ #include "common/swap.h" #include "core/core.h" #include "core/file_sys/patch_manager.h" +#include "core/file_sys/vfs/vfs_types.h" #include "core/hle/kernel/code_set.h" #include "core/hle/kernel/k_page_table.h" #include "core/hle/kernel/k_process.h" @@ -66,7 +67,7 @@ FileType AppLoader_NSO::IdentifyType(const FileSys::VirtualFile& in_file) { return FileType::NSO; } -std::optional AppLoader_NSO::LoadModule(Kernel::KProcess& process, Core::System& system, const FileSys::VfsFile& nso_file, VAddr load_base, bool should_pass_arguments, bool load_into_process, std::optional pm, std::vector* patches, s32 patch_index) { +std::optional AppLoader_NSO::LoadModule(Kernel::KProcess& process, Core::System& system, const FileSys::VfsFile& nso_file, FileSys::VirtualFile npdm_file, const VAddr load_base, const bool should_pass_arguments, const bool load_into_process, VAddr* out_load_base, std::optional pm, std::vector* patches, s32 patch_index) { if (nso_file.GetSize() < sizeof(NSOHeader)) return std::nullopt; NSOHeader nso_header{}; @@ -216,9 +217,29 @@ std::optional AppLoader_NSO::LoadModule(Kernel::KProcess& process, Core:: } } + FileSys::ProgramMetadata metadata = FileSys::ProgramMetadata::GetDefault(); + if (npdm_file) { + LOG_DEBUG(Loader, "loading associated npdm file {}", npdm_file->GetName()); + metadata.Load(npdm_file); + } else { + LOG_WARNING(Loader, "no associated npdm for {}, using default", nso_file.GetName()); + } + + const bool is_hbl = true; + if (process + .LoadFromMetadata(metadata, image_size, 0, 0, is_hbl) + .IsError()) { + return false; + } + + auto const new_load_base = npdm_file ? process.GetEntryPoint().GetValue() : load_base; + if (npdm_file) + LOG_WARNING(Loader, "overriden load base with {:#016x}", new_load_base); + if (out_load_base) + *out_load_base = load_base; // no change // Load codeset for current process - process.LoadModule(std::move(codeset), load_base); - return load_base + image_size; + process.LoadModule(std::move(codeset), new_load_base); + return new_load_base + image_size; } AppLoader_NSO::LoadResult AppLoader_NSO::Load(Kernel::KProcess& process, Core::System& system) { @@ -226,11 +247,14 @@ AppLoader_NSO::LoadResult AppLoader_NSO::Load(Kernel::KProcess& process, Core::S return {ResultStatus::ErrorAlreadyLoaded, {}}; } - modules.clear(); + FileSys::VirtualFile npdm_file{}; + if (auto const dir = file->GetContainingDirectory()) + npdm_file = dir->GetFile("main.npdm"); + modules.clear(); // Load module - const VAddr base_address = GetInteger(process.GetEntryPoint()); - if (!LoadModule(process, system, *file, base_address, true, true)) { + VAddr base_address = GetInteger(process.GetEntryPoint()); + if (!LoadModule(process, system, *file, npdm_file, base_address, true, true, &base_address)) { return {ResultStatus::ErrorLoadingNSO, {}}; } diff --git a/src/core/loader/nso.h b/src/core/loader/nso.h index 6356697e33..8b96e1c8ee 100644 --- a/src/core/loader/nso.h +++ b/src/core/loader/nso.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -90,11 +93,9 @@ public: } static std::optional LoadModule(Kernel::KProcess& process, Core::System& system, - const FileSys::VfsFile& nso_file, VAddr load_base, - bool should_pass_arguments, bool load_into_process, - std::optional pm = {}, - std::vector* patches = nullptr, - s32 patch_index = -1); + const FileSys::VfsFile& nso_file, FileSys::VirtualFile npdm_file, const VAddr load_base, + const bool should_pass_arguments, const bool load_into_process, VAddr* out_load_base, + std::optional pm = {}, std::vector* patches = nullptr, s32 patch_index = -1); LoadResult Load(Kernel::KProcess& process, Core::System& system) override; diff --git a/src/yuzu/main_window.cpp b/src/yuzu/main_window.cpp index 4d5a3c43f9..4e175c7a91 100644 --- a/src/yuzu/main_window.cpp +++ b/src/yuzu/main_window.cpp @@ -2779,7 +2779,7 @@ void MainWindow::OnMenuLoadFolder() { const QDir dir{dir_path}; const QStringList matching_main = dir.entryList({QStringLiteral("main")}, QDir::Files); - if (matching_main.size() == 1) { + if (matching_main.size() > 0) { BootGame(dir.path() + QDir::separator() + matching_main[0], ApplicationAppletParameters()); } else { QMessageBox::warning(this, tr("Invalid Directory Selected"), From 2f90bd6de8d34417af121ae749e9ab994614cd5c Mon Sep 17 00:00:00 2001 From: lizzie Date: Wed, 20 May 2026 02:10:43 +0000 Subject: [PATCH 07/18] NSO load NPDM and retrofit to use -ulaunch --- .../loader/deconstructed_rom_directory.cpp | 6 ++- src/core/loader/nso.cpp | 52 +++++++++++++------ src/core/loader/nso.h | 7 ++- src/yuzu/main_window.cpp | 30 ++++++++++- 4 files changed, 73 insertions(+), 22 deletions(-) diff --git a/src/core/loader/deconstructed_rom_directory.cpp b/src/core/loader/deconstructed_rom_directory.cpp index 9154c6d971..073c1e1584 100644 --- a/src/core/loader/deconstructed_rom_directory.cpp +++ b/src/core/loader/deconstructed_rom_directory.cpp @@ -12,6 +12,7 @@ #include "core/file_sys/content_archive.h" #include "core/file_sys/control_metadata.h" #include "core/file_sys/patch_manager.h" +#include "core/file_sys/program_metadata.h" #include "core/file_sys/romfs_factory.h" #include "core/hle/kernel/k_page_table.h" #include "core/hle/kernel/k_process.h" @@ -204,7 +205,7 @@ AppLoader_DeconstructedRomDirectory::LoadResult AppLoader_DeconstructedRomDirect const bool should_pass_arguments = std::strcmp(module, "rtld") == 0; const auto tentative_next_load_addr = AppLoader_NSO::LoadModule( - process, system, *module_file, {}, code_size, should_pass_arguments, false, nullptr, {}, + process, system, *module_file, code_size, should_pass_arguments, false, nullptr, metadata, {}, patch_ctx.GetPatchers(), patch_ctx.GetLastIndex()); if (!tentative_next_load_addr) { return {ResultStatus::ErrorLoadingNSO, {}}; @@ -251,8 +252,9 @@ AppLoader_DeconstructedRomDirectory::LoadResult AppLoader_DeconstructedRomDirect const VAddr load_addr{next_load_addr}; const bool should_pass_arguments = std::strcmp(module, "rtld") == 0; + FileSys::ProgramMetadata tmp_metadata{}; const auto tentative_next_load_addr = AppLoader_NSO::LoadModule( - process, system, *module_file, {}, load_addr, should_pass_arguments, true, nullptr, pm, + process, system, *module_file, load_addr, should_pass_arguments, true, nullptr, metadata, pm, patch_ctx.GetPatchers(), patch_ctx.GetIndex(i)); if (!tentative_next_load_addr) { return {ResultStatus::ErrorLoadingNSO, {}}; diff --git a/src/core/loader/nso.cpp b/src/core/loader/nso.cpp index 6e6733223d..e6574ee398 100644 --- a/src/core/loader/nso.cpp +++ b/src/core/loader/nso.cpp @@ -17,11 +17,14 @@ #include "common/swap.h" #include "core/core.h" #include "core/file_sys/patch_manager.h" +#include "core/file_sys/romfs_factory.h" #include "core/file_sys/vfs/vfs_types.h" #include "core/hle/kernel/code_set.h" #include "core/hle/kernel/k_page_table.h" #include "core/hle/kernel/k_process.h" #include "core/hle/kernel/k_thread.h" +#include "core/hle/service/filesystem/filesystem.h" +#include "core/loader/loader.h" #include "core/loader/nso.h" #include "core/memory.h" @@ -67,7 +70,7 @@ FileType AppLoader_NSO::IdentifyType(const FileSys::VirtualFile& in_file) { return FileType::NSO; } -std::optional AppLoader_NSO::LoadModule(Kernel::KProcess& process, Core::System& system, const FileSys::VfsFile& nso_file, FileSys::VirtualFile npdm_file, const VAddr load_base, const bool should_pass_arguments, const bool load_into_process, VAddr* out_load_base, std::optional pm, std::vector* patches, s32 patch_index) { +std::optional AppLoader_NSO::LoadModule(Kernel::KProcess& process, Core::System& system, const FileSys::VfsFile& nso_file, const VAddr load_base, const bool should_pass_arguments, const bool load_into_process, VAddr* out_load_base, FileSys::ProgramMetadata metadata, std::optional pm, std::vector* patches, s32 patch_index) { if (nso_file.GetSize() < sizeof(NSOHeader)) return std::nullopt; NSOHeader nso_header{}; @@ -217,14 +220,6 @@ std::optional AppLoader_NSO::LoadModule(Kernel::KProcess& process, Core:: } } - FileSys::ProgramMetadata metadata = FileSys::ProgramMetadata::GetDefault(); - if (npdm_file) { - LOG_DEBUG(Loader, "loading associated npdm file {}", npdm_file->GetName()); - metadata.Load(npdm_file); - } else { - LOG_WARNING(Loader, "no associated npdm for {}, using default", nso_file.GetName()); - } - const bool is_hbl = true; if (process .LoadFromMetadata(metadata, image_size, 0, 0, is_hbl) @@ -232,11 +227,9 @@ std::optional AppLoader_NSO::LoadModule(Kernel::KProcess& process, Core:: return false; } - auto const new_load_base = npdm_file ? process.GetEntryPoint().GetValue() : load_base; - if (npdm_file) - LOG_WARNING(Loader, "overriden load base with {:#016x}", new_load_base); + auto const new_load_base = process.GetEntryPoint().GetValue(); if (out_load_base) - *out_load_base = load_base; // no change + *out_load_base = new_load_base; // no change // Load codeset for current process process.LoadModule(std::move(codeset), new_load_base); return new_load_base + image_size; @@ -248,22 +241,33 @@ AppLoader_NSO::LoadResult AppLoader_NSO::Load(Kernel::KProcess& process, Core::S } FileSys::VirtualFile npdm_file{}; - if (auto const dir = file->GetContainingDirectory()) + metadata = FileSys::ProgramMetadata::GetDefault(); + if (auto const dir = file->GetContainingDirectory()) { npdm_file = dir->GetFile("main.npdm"); + if (npdm_file) { + metadata.Load(npdm_file); + } + } modules.clear(); // Load module VAddr base_address = GetInteger(process.GetEntryPoint()); - if (!LoadModule(process, system, *file, npdm_file, base_address, true, true, &base_address)) { + if (!LoadModule(process, system, *file, base_address, true, true, &base_address, metadata)) { return {ResultStatus::ErrorLoadingNSO, {}}; } modules.insert_or_assign(base_address, file->GetName()); LOG_DEBUG(Loader, "loaded module {} @ {:#X}", file->GetName(), base_address); + if (npdm_file) { + LOG_WARNING(Loader, "creating associated rom-fs factories for likely standalone NSO"); + u64 program_id{}; + ReadProgramId(program_id); + system.GetFileSystemController().RegisterProcess(process.GetProcessId(), program_id, std::make_unique(*this, system.GetContentProvider(), system.GetFileSystemController())); + } + is_loaded = true; - return {ResultStatus::Success, LoadParameters{Kernel::KThread::DefaultThreadPriority, - Core::Memory::DEFAULT_STACK_SIZE}}; + return {ResultStatus::Success, LoadParameters{Kernel::KThread::DefaultThreadPriority, Core::Memory::DEFAULT_STACK_SIZE}}; } ResultStatus AppLoader_NSO::ReadNSOModules(Modules& out_modules) { @@ -271,4 +275,18 @@ ResultStatus AppLoader_NSO::ReadNSOModules(Modules& out_modules) { return ResultStatus::Success; } +ResultStatus AppLoader_NSO::ReadProgramId(u64& out_program_id) { + if (metadata.GetTitleID() == 0) + return ResultStatus::ErrorNoControl; + out_program_id = metadata.GetTitleID(); + return ResultStatus::Success; +} + +ResultStatus AppLoader_NSO::ReadTitle(std::string& out_title) { + auto const raw_name = metadata.GetName(); + out_title.resize(raw_name.size()); + std::memcpy(out_title.data(), raw_name.data(), raw_name.size()); + return ResultStatus::Success; +} + } // namespace Loader diff --git a/src/core/loader/nso.h b/src/core/loader/nso.h index 8b96e1c8ee..fec9045469 100644 --- a/src/core/loader/nso.h +++ b/src/core/loader/nso.h @@ -12,6 +12,7 @@ #include "common/common_types.h" #include "common/swap.h" #include "core/file_sys/patch_manager.h" +#include "core/file_sys/program_metadata.h" #include "core/loader/loader.h" namespace Core { @@ -93,16 +94,20 @@ public: } static std::optional LoadModule(Kernel::KProcess& process, Core::System& system, - const FileSys::VfsFile& nso_file, FileSys::VirtualFile npdm_file, const VAddr load_base, + const FileSys::VfsFile& nso_file, const VAddr load_base, const bool should_pass_arguments, const bool load_into_process, VAddr* out_load_base, + FileSys::ProgramMetadata metadata, std::optional pm = {}, std::vector* patches = nullptr, s32 patch_index = -1); LoadResult Load(Kernel::KProcess& process, Core::System& system) override; ResultStatus ReadNSOModules(Modules& out_modules) override; + ResultStatus ReadProgramId(u64& out_program_id) override; + ResultStatus ReadTitle(std::string& title) override; private: Modules modules; + FileSys::ProgramMetadata metadata; }; } // namespace Loader diff --git a/src/yuzu/main_window.cpp b/src/yuzu/main_window.cpp index 4e175c7a91..e2b18dde4c 100644 --- a/src/yuzu/main_window.cpp +++ b/src/yuzu/main_window.cpp @@ -515,6 +515,7 @@ MainWindow::MainWindow(bool has_broken_vulkan) QString game_path; bool should_launch_qlaunch = false; bool should_launch_hlaunch = false; + bool should_launch_ulaunch = false; bool should_launch_setup = false; bool has_gamepath = false; bool is_fullscreen = false; @@ -558,6 +559,8 @@ MainWindow::MainWindow(bool has_broken_vulkan) should_launch_qlaunch = true; } else if (args[i] == QStringLiteral("-hlaunch")) { should_launch_hlaunch = true; + } else if (args[i] == QStringLiteral("-ulaunch")) { + should_launch_ulaunch = true; } else if (args[i] == QStringLiteral("-setup")) { should_launch_setup = true; } else { @@ -580,8 +583,31 @@ MainWindow::MainWindow(bool has_broken_vulkan) LaunchFirmwareApplet(u64(Service::AM::AppletProgramId::QLaunch), std::nullopt); } else if (should_launch_hlaunch) { std::filesystem::path const sd_dir = Common::FS::GetEdenPathString(Common::FS::EdenPath::SDMCDir); - auto const hbl_path = (sd_dir / "atmosphere" / "hbl.nsp").string(); - BootGame(QString::fromStdString(hbl_path), ApplicationAppletParameters()); + auto const path = (sd_dir / "atmosphere" / "hbl.nsp").string(); + BootGame(QString::fromStdString(path), ApplicationAppletParameters()); + } else if (should_launch_ulaunch) { + constexpr size_t NroPathSize = 512; + constexpr size_t NroArgvSize = 2048; + constexpr size_t MenuCaptionSize = 1024; + struct UlauncherTargetInput { + u32 magic; + bool target_once; + bool is_auto_game_recording; + std::array unused; + std::array nro_path; + std::array nro_argv; + std::array menu_caption; + } target_ipt = {}; + + target_ipt.magic = 0x49444C55; // "ULDI" + QtCommon::system->GetUserChannel().resize(sizeof(target_ipt)); + std::memcpy(QtCommon::system->GetUserChannel().data(), &target_ipt, sizeof(target_ipt)); + + std::filesystem::path const sd_dir = Common::FS::GetEdenPathString(Common::FS::EdenPath::SDMCDir); + auto const path = (sd_dir / "ulaunch" / "bin" / "uLoader" / "application" / "main").string(); + auto params = ApplicationAppletParameters(); + params.launch_type = Service::AM::LaunchType::ApplicationInitiated; + BootGame(QString::fromStdString(path), params); } } } From 41283a8fac844c4c30875baa5a185408c0f71248 Mon Sep 17 00:00:00 2001 From: lizzie Date: Wed, 20 May 2026 04:00:54 +0000 Subject: [PATCH 08/18] properly give targetinput? --- src/core/hle/service/am/applet_manager.cpp | 24 ++++++++++++++++++++++ src/core/hle/service/am/applet_manager.h | 2 ++ src/yuzu/main_window.cpp | 19 +---------------- 3 files changed, 27 insertions(+), 18 deletions(-) diff --git a/src/core/hle/service/am/applet_manager.cpp b/src/core/hle/service/am/applet_manager.cpp index c2920f91ae..51a371f2cf 100644 --- a/src/core/hle/service/am/applet_manager.cpp +++ b/src/core/hle/service/am/applet_manager.cpp @@ -292,8 +292,32 @@ void AppletManager::SetWindowSystem(WindowSystem* window_system) { applet->previous_program_index = params.previous_program_index; // Push UserChannel data from previous application + // Or ulaunch initialization where we push parameters willingly! if (params.launch_type == LaunchType::ApplicationInitiated) { applet->user_channel_launch_parameter.swap(m_system.GetUserChannel()); + } else if (params.launch_type == LaunchType::FrontendUlaunchInitiated) { + constexpr size_t NroPathSize = 512; + constexpr size_t NroArgvSize = 2048; + constexpr size_t MenuCaptionSize = 1024; + struct UlauncherTargetInput { + u32 magic; + bool target_once; + bool is_auto_game_recording; + std::array unused; + std::array nro_path; + std::array nro_argv; + std::array menu_caption; + } target_ipt = {}; + static_assert(sizeof(target_ipt) == 3592); + + target_ipt.magic = 0x49444C55; // "ULDI" + target_ipt.nro_path = {"sdmc:/hbmenu.nro"}; + target_ipt.menu_caption = {"Loaded by uLoader v1.2.4 - uLaunch's custom hbloader replacement ;)"}; + + std::vector v(sizeof(target_ipt)); + std::memcpy(v.data(), &target_ipt, sizeof(target_ipt)); + applet->user_channel_launch_parameter.clear(); + applet->user_channel_launch_parameter.push_back(std::move(v)); } // 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 2b0714adf7..cd97a6e157 100644 --- a/src/core/hle/service/am/applet_manager.h +++ b/src/core/hle/service/am/applet_manager.h @@ -26,6 +26,8 @@ class WindowSystem; enum class LaunchType { FrontendInitiated, ApplicationInitiated, + // Special masquerade for AMS + uLaunch CFW + FrontendUlaunchInitiated = 0x800, }; struct FrontendAppletParameters { diff --git a/src/yuzu/main_window.cpp b/src/yuzu/main_window.cpp index e2b18dde4c..49d1ce03ec 100644 --- a/src/yuzu/main_window.cpp +++ b/src/yuzu/main_window.cpp @@ -586,27 +586,10 @@ MainWindow::MainWindow(bool has_broken_vulkan) auto const path = (sd_dir / "atmosphere" / "hbl.nsp").string(); BootGame(QString::fromStdString(path), ApplicationAppletParameters()); } else if (should_launch_ulaunch) { - constexpr size_t NroPathSize = 512; - constexpr size_t NroArgvSize = 2048; - constexpr size_t MenuCaptionSize = 1024; - struct UlauncherTargetInput { - u32 magic; - bool target_once; - bool is_auto_game_recording; - std::array unused; - std::array nro_path; - std::array nro_argv; - std::array menu_caption; - } target_ipt = {}; - - target_ipt.magic = 0x49444C55; // "ULDI" - QtCommon::system->GetUserChannel().resize(sizeof(target_ipt)); - std::memcpy(QtCommon::system->GetUserChannel().data(), &target_ipt, sizeof(target_ipt)); - std::filesystem::path const sd_dir = Common::FS::GetEdenPathString(Common::FS::EdenPath::SDMCDir); auto const path = (sd_dir / "ulaunch" / "bin" / "uLoader" / "application" / "main").string(); auto params = ApplicationAppletParameters(); - params.launch_type = Service::AM::LaunchType::ApplicationInitiated; + params.launch_type = Service::AM::LaunchType::FrontendUlaunchInitiated; BootGame(QString::fromStdString(path), params); } } From daee174a87cecf0eedc7acb814fd98e96476eb88 Mon Sep 17 00:00:00 2001 From: lizzie Date: Wed, 20 May 2026 05:20:59 +0000 Subject: [PATCH 09/18] provide proper load params --- src/core/hle/service/am/applet_manager.cpp | 48 +++++++++++++++++----- src/core/hle/service/am/applet_manager.h | 3 +- src/yuzu/main_window.cpp | 12 +++++- 3 files changed, 50 insertions(+), 13 deletions(-) diff --git a/src/core/hle/service/am/applet_manager.cpp b/src/core/hle/service/am/applet_manager.cpp index 51a371f2cf..8a6dc2d033 100644 --- a/src/core/hle/service/am/applet_manager.cpp +++ b/src/core/hle/service/am/applet_manager.cpp @@ -295,11 +295,12 @@ void AppletManager::SetWindowSystem(WindowSystem* window_system) { // Or ulaunch initialization where we push parameters willingly! if (params.launch_type == LaunchType::ApplicationInitiated) { applet->user_channel_launch_parameter.swap(m_system.GetUserChannel()); - } else if (params.launch_type == LaunchType::FrontendUlaunchInitiated) { + } else if (params.launch_type == LaunchType::FrontendUlaunchInitiated + || params.launch_type == LaunchType::FrontendUmenuInitiated) { constexpr size_t NroPathSize = 512; constexpr size_t NroArgvSize = 2048; constexpr size_t MenuCaptionSize = 1024; - struct UlauncherTargetInput { + struct UloaderTargetInput { u32 magic; bool target_once; bool is_auto_game_recording; @@ -307,17 +308,42 @@ void AppletManager::SetWindowSystem(WindowSystem* window_system) { std::array nro_path; std::array nro_argv; std::array menu_caption; - } target_ipt = {}; - static_assert(sizeof(target_ipt) == 3592); + }; + static_assert(sizeof(UloaderTargetInput) == 3592); - target_ipt.magic = 0x49444C55; // "ULDI" - target_ipt.nro_path = {"sdmc:/hbmenu.nro"}; - target_ipt.menu_caption = {"Loaded by uLoader v1.2.4 - uLaunch's custom hbloader replacement ;)"}; + if (params.launch_type == LaunchType::FrontendUlaunchInitiated) { + UloaderTargetInput target_ipt = {}; + target_ipt.magic = 0x49444C55; // "ULDI" + target_ipt.nro_path = {"sdmc:/hbmenu.nro"}; + target_ipt.menu_caption = {"Loaded by uLoader v1.2.4 - uLaunch's custom hbloader replacement ;)"}; - std::vector v(sizeof(target_ipt)); - std::memcpy(v.data(), &target_ipt, sizeof(target_ipt)); - applet->user_channel_launch_parameter.clear(); - applet->user_channel_launch_parameter.push_back(std::move(v)); + std::vector v(sizeof(target_ipt)); + std::memcpy(v.data(), std::addressof(target_ipt), sizeof(target_ipt)); + applet->user_channel_launch_parameter.clear(); + applet->user_channel_launch_parameter.push_back(std::move(v)); + } else { + typedef u64 AccountUid; + struct UmenuInput { + AccountUid selected_user; + UloaderTargetInput suspended_hb_target_ipt; // Set if homebrew (launched as an application) is currently suspended + u64 suspended_app_id; // Set if any normal application is suspended + std::array last_menu_fs_path; //FS_MAX_PATH + std::array last_menu_path; + u32 last_menu_index; + bool reload_theme_cache; + bool warned_about_outdated_theme; + u32 last_added_app_count; + u32 last_deleted_app_count; + u32 in_verify_app_count; + }; + + UmenuInput target_umenu_ipt = {}; + + std::vector v(sizeof(target_umenu_ipt)); + std::memcpy(v.data(), std::addressof(target_umenu_ipt), sizeof(target_umenu_ipt)); + applet->user_channel_launch_parameter.clear(); + applet->user_channel_launch_parameter.push_back(std::move(v)); + } } // 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 cd97a6e157..8c2378aca3 100644 --- a/src/core/hle/service/am/applet_manager.h +++ b/src/core/hle/service/am/applet_manager.h @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project @@ -28,6 +28,7 @@ enum class LaunchType { ApplicationInitiated, // Special masquerade for AMS + uLaunch CFW FrontendUlaunchInitiated = 0x800, + FrontendUmenuInitiated, }; struct FrontendAppletParameters { diff --git a/src/yuzu/main_window.cpp b/src/yuzu/main_window.cpp index 49d1ce03ec..76ae228913 100644 --- a/src/yuzu/main_window.cpp +++ b/src/yuzu/main_window.cpp @@ -515,6 +515,7 @@ MainWindow::MainWindow(bool has_broken_vulkan) QString game_path; bool should_launch_qlaunch = false; bool should_launch_hlaunch = false; + bool should_launch_hlaunch_uloader = false; bool should_launch_ulaunch = false; bool should_launch_setup = false; bool has_gamepath = false; @@ -559,6 +560,8 @@ MainWindow::MainWindow(bool has_broken_vulkan) should_launch_qlaunch = true; } else if (args[i] == QStringLiteral("-hlaunch")) { should_launch_hlaunch = true; + } else if (args[i] == QStringLiteral("-hlaunch-uloader")) { + should_launch_hlaunch_uloader = true; } else if (args[i] == QStringLiteral("-ulaunch")) { should_launch_ulaunch = true; } else if (args[i] == QStringLiteral("-setup")) { @@ -585,12 +588,19 @@ MainWindow::MainWindow(bool has_broken_vulkan) std::filesystem::path const sd_dir = Common::FS::GetEdenPathString(Common::FS::EdenPath::SDMCDir); auto const path = (sd_dir / "atmosphere" / "hbl.nsp").string(); BootGame(QString::fromStdString(path), ApplicationAppletParameters()); - } else if (should_launch_ulaunch) { + } else if (should_launch_hlaunch_uloader) { std::filesystem::path const sd_dir = Common::FS::GetEdenPathString(Common::FS::EdenPath::SDMCDir); auto const path = (sd_dir / "ulaunch" / "bin" / "uLoader" / "application" / "main").string(); auto params = ApplicationAppletParameters(); params.launch_type = Service::AM::LaunchType::FrontendUlaunchInitiated; BootGame(QString::fromStdString(path), params); + } else if (should_launch_ulaunch) { + std::filesystem::path const sd_dir = Common::FS::GetEdenPathString(Common::FS::EdenPath::SDMCDir); + auto const path = (sd_dir / "ulaunch" / "bin" / "uMenu" / "main").string(); + auto const program_id = 0x0100000000010000; + auto params = LibraryAppletParameters(u64(program_id), Service::AM::AppletId::Cabinet); + params.launch_type = Service::AM::LaunchType::FrontendUmenuInitiated; + BootGame(QString::fromStdString(path), params); } } } From aa2875e1e994159e2afa503ee137f51696902b28 Mon Sep 17 00:00:00 2001 From: lizzie Date: Wed, 20 May 2026 08:25:10 +0000 Subject: [PATCH 10/18] ulsf:p, proper launch of --- src/core/hle/service/am/am_types.h | 4 +- src/core/hle/service/am/applet_manager.cpp | 113 ++++++++++-------- .../am/service/library_applet_creator.cpp | 1 + src/core/hle/service/ulsf/ulsf.cpp | 2 + src/yuzu/main_window.cpp | 14 ++- 5 files changed, 81 insertions(+), 53 deletions(-) diff --git a/src/core/hle/service/am/am_types.h b/src/core/hle/service/am/am_types.h index beb52b74dd..1e150f02bf 100644 --- a/src/core/hle/service/am/am_types.h +++ b/src/core/hle/service/am/am_types.h @@ -94,7 +94,9 @@ enum class AppletId : u32 { LoginShare = 0x18, WebAuth = 0x19, MyPage = 0x1A, - Lhub = 0x35 + Lhub = 0x35, + // Homebrew -- uses same ProgramId as qlaunch + UlauncherUmenu = 0xF000'0000, }; enum class AppletProgramId : u64 { diff --git a/src/core/hle/service/am/applet_manager.cpp b/src/core/hle/service/am/applet_manager.cpp index 8a6dc2d033..badf776082 100644 --- a/src/core/hle/service/am/applet_manager.cpp +++ b/src/core/hle/service/am/applet_manager.cpp @@ -24,6 +24,20 @@ namespace Service::AM { namespace { +constexpr size_t NroPathSize = 512; +constexpr size_t NroArgvSize = 2048; +constexpr size_t MenuCaptionSize = 1024; +struct UloaderTargetInput { + u32 magic; + bool target_once; + bool is_auto_game_recording; + std::array unused; + std::array nro_path; + std::array nro_argv; + std::array menu_caption; +}; +static_assert(sizeof(UloaderTargetInput) == 3592); + constexpr u32 LaunchParameterAccountPreselectedUserMagic = 0xC79497CA; struct LaunchParameterAccountPreselectedUser { @@ -121,6 +135,44 @@ void PushInShowController(Core::System& system, AppletStorageChannel& channel) { channel.Push(std::make_shared(system, std::move(user_args_data))); } +void PushInShowUlauncherUmenu(Core::System& system, AppletStorageChannel& channel) { + typedef std::array AccountUid; + const CommonArguments common_args = { + .arguments_version = CommonArgumentVersion::Version3, + .size = CommonArgumentSize::Version3, + .library_version = 2, + .theme_color = ThemeColor::BasicBlack, + .play_startup_sound = true, + .system_tick = system.CoreTiming().GetClockTicks(), + }; + struct UmenuInput { + AccountUid selected_user; + UloaderTargetInput suspended_hb_target_ipt; // Set if homebrew (launched as an application) is currently suspended + u64 suspended_app_id; // Set if any normal application is suspended + std::array last_menu_fs_path; //FS_MAX_PATH + std::array last_menu_path; + u32 last_menu_index; + bool reload_theme_cache; + bool warned_about_outdated_theme; + u32 last_added_app_count; + u32 last_deleted_app_count; + u32 in_verify_app_count; + } user_args = {}; + static_assert(sizeof(UmenuInput) == 5176); + + auto const user = system.GetProfileManager().GetUser(0); + user_args.selected_user[0] = user->uuid[0]; + user_args.selected_user[1] = user->uuid[1]; + user_args.warned_about_outdated_theme = true; + + std::vector common_args_data(sizeof(common_args)); + std::vector user_args_data(sizeof(user_args)); + std::memcpy(common_args_data.data(), std::addressof(common_args), sizeof(common_args)); + std::memcpy(user_args_data.data(), std::addressof(user_args), sizeof(user_args)); + channel.Push(std::make_shared(system, std::move(common_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, @@ -295,55 +347,15 @@ void AppletManager::SetWindowSystem(WindowSystem* window_system) { // Or ulaunch initialization where we push parameters willingly! if (params.launch_type == LaunchType::ApplicationInitiated) { applet->user_channel_launch_parameter.swap(m_system.GetUserChannel()); - } else if (params.launch_type == LaunchType::FrontendUlaunchInitiated - || params.launch_type == LaunchType::FrontendUmenuInitiated) { - constexpr size_t NroPathSize = 512; - constexpr size_t NroArgvSize = 2048; - constexpr size_t MenuCaptionSize = 1024; - struct UloaderTargetInput { - u32 magic; - bool target_once; - bool is_auto_game_recording; - std::array unused; - std::array nro_path; - std::array nro_argv; - std::array menu_caption; - }; - static_assert(sizeof(UloaderTargetInput) == 3592); - - if (params.launch_type == LaunchType::FrontendUlaunchInitiated) { - UloaderTargetInput target_ipt = {}; - target_ipt.magic = 0x49444C55; // "ULDI" - target_ipt.nro_path = {"sdmc:/hbmenu.nro"}; - target_ipt.menu_caption = {"Loaded by uLoader v1.2.4 - uLaunch's custom hbloader replacement ;)"}; - - std::vector v(sizeof(target_ipt)); - std::memcpy(v.data(), std::addressof(target_ipt), sizeof(target_ipt)); - applet->user_channel_launch_parameter.clear(); - applet->user_channel_launch_parameter.push_back(std::move(v)); - } else { - typedef u64 AccountUid; - struct UmenuInput { - AccountUid selected_user; - UloaderTargetInput suspended_hb_target_ipt; // Set if homebrew (launched as an application) is currently suspended - u64 suspended_app_id; // Set if any normal application is suspended - std::array last_menu_fs_path; //FS_MAX_PATH - std::array last_menu_path; - u32 last_menu_index; - bool reload_theme_cache; - bool warned_about_outdated_theme; - u32 last_added_app_count; - u32 last_deleted_app_count; - u32 in_verify_app_count; - }; - - UmenuInput target_umenu_ipt = {}; - - std::vector v(sizeof(target_umenu_ipt)); - std::memcpy(v.data(), std::addressof(target_umenu_ipt), sizeof(target_umenu_ipt)); - applet->user_channel_launch_parameter.clear(); - applet->user_channel_launch_parameter.push_back(std::move(v)); - } + } else if (params.launch_type == LaunchType::FrontendUlaunchInitiated) { + UloaderTargetInput target_ipt = {}; + target_ipt.magic = 0x49444C55; // "ULDI" + target_ipt.nro_path = {"sdmc:/hbmenu.nro"}; + target_ipt.menu_caption = {"Loaded by uLoader v1.2.4 - uLaunch's custom hbloader replacement ;)"}; + std::vector v(sizeof(target_ipt)); + std::memcpy(v.data(), std::addressof(target_ipt), sizeof(target_ipt)); + applet->user_channel_launch_parameter.clear(); + applet->user_channel_launch_parameter.push_back(std::move(v)); } // TODO: Read whether we need a preselected user from NACP? @@ -385,6 +397,9 @@ void AppletManager::SetWindowSystem(WindowSystem* window_system) { case AppletId::Controller: PushInShowController(m_system, InitializeFakeCallerApplet(m_system, applet)); break; + case AppletId::UlauncherUmenu: + PushInShowUlauncherUmenu(m_system, InitializeFakeCallerApplet(m_system, applet)); + break; default: break; } diff --git a/src/core/hle/service/am/service/library_applet_creator.cpp b/src/core/hle/service/am/service/library_applet_creator.cpp index 3de5740237..764264f81b 100644 --- a/src/core/hle/service/am/service/library_applet_creator.cpp +++ b/src/core/hle/service/am/service/library_applet_creator.cpp @@ -55,6 +55,7 @@ AppletProgramId AppletIdToProgramId(AppletId applet_id) { case AppletId::OverlayDisplay: return AppletProgramId::OverlayDisplay; case AppletId::QLaunch: + case AppletId::UlauncherUmenu: //reuses same id as Qlaunch return AppletProgramId::QLaunch; case AppletId::Starter: return AppletProgramId::Starter; diff --git a/src/core/hle/service/ulsf/ulsf.cpp b/src/core/hle/service/ulsf/ulsf.cpp index d218511f4a..bff0e8c847 100644 --- a/src/core/hle/service/ulsf/ulsf.cpp +++ b/src/core/hle/service/ulsf/ulsf.cpp @@ -33,6 +33,8 @@ void ULSF_U::GetVersion(HLERequestContext& ctx) { void LoopProcess(Core::System& system) { auto server_manager = std::make_unique(system); server_manager->RegisterNamedService("ulsf:u", std::make_shared(system)); + server_manager->RegisterNamedService("ulsf:p", std::make_shared(system)); + ServerManager::RunServer(std::move(server_manager)); } diff --git a/src/yuzu/main_window.cpp b/src/yuzu/main_window.cpp index 76ae228913..01859aee18 100644 --- a/src/yuzu/main_window.cpp +++ b/src/yuzu/main_window.cpp @@ -516,6 +516,7 @@ MainWindow::MainWindow(bool has_broken_vulkan) bool should_launch_qlaunch = false; bool should_launch_hlaunch = false; bool should_launch_hlaunch_uloader = false; + bool should_launch_umenu = false; bool should_launch_ulaunch = false; bool should_launch_setup = false; bool has_gamepath = false; @@ -563,7 +564,10 @@ MainWindow::MainWindow(bool has_broken_vulkan) } else if (args[i] == QStringLiteral("-hlaunch-uloader")) { should_launch_hlaunch_uloader = true; } else if (args[i] == QStringLiteral("-ulaunch")) { - should_launch_ulaunch = true; + //should_launch_ulaunch = true; + should_launch_umenu = true; + } else if (args[i] == QStringLiteral("-umenu")) { + should_launch_umenu = true; } else if (args[i] == QStringLiteral("-setup")) { should_launch_setup = true; } else { @@ -595,10 +599,14 @@ MainWindow::MainWindow(bool has_broken_vulkan) params.launch_type = Service::AM::LaunchType::FrontendUlaunchInitiated; BootGame(QString::fromStdString(path), params); } else if (should_launch_ulaunch) { + std::filesystem::path const sd_dir = Common::FS::GetEdenPathString(Common::FS::EdenPath::SDMCDir); + auto const path = (sd_dir / "ulaunch" / "bin" / "uSystem" / "exefs.nsp").string(); + BootGame(QString::fromStdString(path), ApplicationAppletParameters()); + } else if (should_launch_umenu) { std::filesystem::path const sd_dir = Common::FS::GetEdenPathString(Common::FS::EdenPath::SDMCDir); auto const path = (sd_dir / "ulaunch" / "bin" / "uMenu" / "main").string(); - auto const program_id = 0x0100000000010000; - auto params = LibraryAppletParameters(u64(program_id), Service::AM::AppletId::Cabinet); + auto const program_id = 0x010000000000FFFF; + auto params = LibraryAppletParameters(program_id, Service::AM::AppletId::UlauncherUmenu); params.launch_type = Service::AM::LaunchType::FrontendUmenuInitiated; BootGame(QString::fromStdString(path), params); } From 12af978b3efbe8c6eb0920b82311817f8ebcb71e Mon Sep 17 00:00:00 2001 From: lizzie Date: Wed, 20 May 2026 08:39:07 +0000 Subject: [PATCH 11/18] + stubbed services --- src/core/hle/service/btm/btm_system_core.cpp | 7 ++- src/core/hle/service/btm/btm_system_core.h | 5 +- .../service/set/system_settings_server.cpp | 55 ++++++++++++++++--- .../hle/service/set/system_settings_server.h | 6 ++ 4 files changed, 61 insertions(+), 12 deletions(-) diff --git a/src/core/hle/service/btm/btm_system_core.cpp b/src/core/hle/service/btm/btm_system_core.cpp index f1e623be16..318657fc32 100644 --- a/src/core/hle/service/btm/btm_system_core.cpp +++ b/src/core/hle/service/btm/btm_system_core.cpp @@ -29,7 +29,7 @@ IBtmSystemCore::IBtmSystemCore(Core::System& system_) {10, nullptr, "StartAudioDeviceDiscovery"}, {11, nullptr, "StopAudioDeviceDiscovery"}, {12, nullptr, "IsDiscoveryingAudioDevice"}, - {13, nullptr, "GetDiscoveredAudioDevice"}, + {13, C<&IBtmSystemCore::GetDiscoveredAudioDevice>, "GetDiscoveredAudioDevice"}, {14, C<&IBtmSystemCore::AcquireAudioDeviceConnectionEvent>, "AcquireAudioDeviceConnectionEvent"}, {15, nullptr, "ConnectAudioDevice"}, {16, nullptr, "IsConnectingAudioDevice"}, @@ -93,6 +93,11 @@ Result IBtmSystemCore::AcquireRadioEvent(Out out_is_valid, R_SUCCEED(); } +Result IBtmSystemCore::GetDiscoveredAudioDevice(OutArray, BufferAttr_HipcPointer> out_audio_devices, s32 count, Out out_total) { + LOG_WARNING(Service_BTM, "(STUBBED) called"); + R_SUCCEED(); +} + Result IBtmSystemCore::AcquireAudioDeviceConnectionEvent( OutCopyHandle out_event) { LOG_WARNING(Service_BTM, "(STUBBED) called"); diff --git a/src/core/hle/service/btm/btm_system_core.h b/src/core/hle/service/btm/btm_system_core.h index 06498b21ea..c390627f28 100644 --- a/src/core/hle/service/btm/btm_system_core.h +++ b/src/core/hle/service/btm/btm_system_core.h @@ -34,9 +34,8 @@ private: Result DisableRadio(); Result IsRadioEnabled(Out out_is_enabled); - Result AcquireRadioEvent(Out out_is_valid, - OutCopyHandle out_event); - + Result AcquireRadioEvent(Out out_is_valid, OutCopyHandle out_event); + Result GetDiscoveredAudioDevice(OutArray, BufferAttr_HipcPointer> out_audio_devices, s32 count, Out out_total); Result AcquireAudioDeviceConnectionEvent(OutCopyHandle out_event); Result GetConnectedAudioDevices( diff --git a/src/core/hle/service/set/system_settings_server.cpp b/src/core/hle/service/set/system_settings_server.cpp index 0ed6d28dd4..7fa74e6689 100644 --- a/src/core/hle/service/set/system_settings_server.cpp +++ b/src/core/hle/service/set/system_settings_server.cpp @@ -142,10 +142,10 @@ ISystemSettingsServer::ISystemSettingsServer(Core::System& system_) {22, C<&ISystemSettingsServer::SetEulaVersions>, "SetEulaVersions"}, {23, C<&ISystemSettingsServer::GetColorSetId>, "GetColorSetId"}, {24, C<&ISystemSettingsServer::SetColorSetId>, "SetColorSetId"}, - {25, nullptr, "GetConsoleInformationUploadFlag"}, - {26, nullptr, "SetConsoleInformationUploadFlag"}, - {27, nullptr, "GetAutomaticApplicationDownloadFlag"}, - {28, nullptr, "SetAutomaticApplicationDownloadFlag"}, + {25, C<&ISystemSettingsServer::GetConsoleInformationUploadFlag>, "GetConsoleInformationUploadFlag"}, + {26, C<&ISystemSettingsServer::SetConsoleInformationUploadFlag>, "SetConsoleInformationUploadFlag"}, + {27, C<&ISystemSettingsServer::GetAutomaticApplicationDownloadFlag>, "GetAutomaticApplicationDownloadFlag"}, + {28, C<&ISystemSettingsServer::SetAutomaticApplicationDownloadFlag>, "SetAutomaticApplicationDownloadFlag"}, {29, C<&ISystemSettingsServer::GetNotificationSettings>, "GetNotificationSettings"}, {30, C<&ISystemSettingsServer::SetNotificationSettings>, "SetNotificationSettings"}, {31, C<&ISystemSettingsServer::GetAccountNotificationSettings>, "GetAccountNotificationSettings"}, @@ -160,8 +160,8 @@ ISystemSettingsServer::ISystemSettingsServer(Core::System& system_) {42, nullptr, "SetEdid"}, {43, C<&ISystemSettingsServer::GetAudioOutputMode>, "GetAudioOutputMode"}, {44, C<&ISystemSettingsServer::SetAudioOutputMode>, "SetAudioOutputMode"}, - {45, C<&ISystemSettingsServer::GetSpeakerAutoMuteFlag> , "GetSpeakerAutoMuteFlag"}, - {46, C<&ISystemSettingsServer::SetSpeakerAutoMuteFlag> , "SetSpeakerAutoMuteFlag"}, + {45, C<&ISystemSettingsServer::GetSpeakerAutoMuteFlag>, "GetSpeakerAutoMuteFlag"}, + {46, C<&ISystemSettingsServer::SetSpeakerAutoMuteFlag>, "SetSpeakerAutoMuteFlag"}, {47, C<&ISystemSettingsServer::GetQuestFlag>, "GetQuestFlag"}, {48, C<&ISystemSettingsServer::SetQuestFlag>, "SetQuestFlag"}, {49, nullptr, "GetDataDeletionSettings"}, @@ -180,8 +180,8 @@ ISystemSettingsServer::ISystemSettingsServer(Core::System& system_) {62, C<&ISystemSettingsServer::GetDebugModeFlag>, "GetDebugModeFlag"}, {63, C<&ISystemSettingsServer::GetPrimaryAlbumStorage>, "GetPrimaryAlbumStorage"}, {64, C<&ISystemSettingsServer::SetPrimaryAlbumStorage>, "SetPrimaryAlbumStorage"}, - {65, nullptr, "GetUsb30EnableFlag"}, - {66, nullptr, "SetUsb30EnableFlag"}, + {65, C<&ISystemSettingsServer::GetUsb30EnableFlag>, "GetUsb30EnableFlag"}, + {66, C<&ISystemSettingsServer::SetUsb30EnableFlag>, "SetUsb30EnableFlag"}, {67, C<&ISystemSettingsServer::GetBatteryLot>, "GetBatteryLot"}, {68, C<&ISystemSettingsServer::GetSerialNumber>, "GetSerialNumber"}, {69, C<&ISystemSettingsServer::GetNfcEnableFlag>, "GetNfcEnableFlag"}, @@ -1074,6 +1074,45 @@ Result ISystemSettingsServer::SetNfcEnableFlag(bool nfc_enable_flag) { R_SUCCEED(); } +Result ISystemSettingsServer::GetConsoleInformationUploadFlag(Out out_flag) { + LOG_INFO(Service_SET, "called {}", m_system_settings.console_information_upload_flag); + *out_flag = m_system_settings.console_information_upload_flag; + R_SUCCEED(); +} + +Result ISystemSettingsServer::SetConsoleInformationUploadFlag(bool flag) { + LOG_INFO(Service_SET, "called {}", flag); + m_system_settings.usb_30_enable_flag = flag; + SetSaveNeeded(); + R_SUCCEED(); +} + +Result ISystemSettingsServer::GetAutomaticApplicationDownloadFlag(Out out_flag) { + LOG_INFO(Service_SET, "called {}", m_system_settings.usb_30_enable_flag); + *out_flag = m_system_settings.automatic_application_download_flag; + R_SUCCEED(); +} + +Result ISystemSettingsServer::SetAutomaticApplicationDownloadFlag(bool flag) { + LOG_INFO(Service_SET, "called {}", flag); + m_system_settings.automatic_application_download_flag = flag; + SetSaveNeeded(); + R_SUCCEED(); +} + +Result ISystemSettingsServer::GetUsb30EnableFlag(Out out_usb30_enable_flag) { + LOG_INFO(Service_SET, "called, usb30_enable_flag={}", m_system_settings.usb_30_enable_flag); + *out_usb30_enable_flag = m_system_settings.usb_30_enable_flag; + R_SUCCEED(); +} + +Result ISystemSettingsServer::SetUsb30EnableFlag(bool usb30_enable_flag) { + LOG_INFO(Service_SET, "called, usb30_enable_flag={}", usb30_enable_flag); + m_system_settings.usb_30_enable_flag = usb30_enable_flag; + SetSaveNeeded(); + R_SUCCEED(); +} + Result ISystemSettingsServer::GetSleepSettings(Out out_sleep_settings) { LOG_INFO(Service_SET, "called, flags={}, handheld_sleep_plan={}, console_sleep_plan={}", m_system_settings.sleep_settings.flags.raw, diff --git a/src/core/hle/service/set/system_settings_server.h b/src/core/hle/service/set/system_settings_server.h index 2a160dfc6b..1d56e61f88 100644 --- a/src/core/hle/service/set/system_settings_server.h +++ b/src/core/hle/service/set/system_settings_server.h @@ -109,6 +109,12 @@ public: Result SetPrimaryAlbumStorage(PrimaryAlbumStorage primary_album_storage); Result GetBatteryLot(Out out_battery_lot); Result GetSerialNumber(Out out_console_serial); + Result GetConsoleInformationUploadFlag(Out out_flag); + Result SetConsoleInformationUploadFlag(bool flag); + Result GetAutomaticApplicationDownloadFlag(Out out_flag); + Result SetAutomaticApplicationDownloadFlag(bool flag); + Result GetUsb30EnableFlag(Out out_usb30_enable_flag); + Result SetUsb30EnableFlag(bool usb30_enable_flag); Result GetNfcEnableFlag(Out out_nfc_enable_flag); Result SetNfcEnableFlag(bool nfc_enable_flag); Result GetSleepSettings(Out out_sleep_settings); From 84db20b9f0df5c43f9496a6f9b61b162dbbff108 Mon Sep 17 00:00:00 2001 From: lizzie Date: Wed, 20 May 2026 08:39:21 +0000 Subject: [PATCH 12/18] AMV + WLAN stubs --- src/core/hle/service/ulsf/ulsf.cpp | 96 ++++++++++++++++++++++++++++++ 1 file changed, 96 insertions(+) diff --git a/src/core/hle/service/ulsf/ulsf.cpp b/src/core/hle/service/ulsf/ulsf.cpp index bff0e8c847..cac4958ecf 100644 --- a/src/core/hle/service/ulsf/ulsf.cpp +++ b/src/core/hle/service/ulsf/ulsf.cpp @@ -2,6 +2,9 @@ // SPDX-License-Identifier: GPL-3.0-or-later #include +#include "core/hle/result.h" +#include "core/hle/service/cmif_serialization.h" +#include "core/hle/service/cmif_types.h" #include "core/hle/service/ipc_helpers.h" #include "core/hle/service/ulsf/ulsf.h" #include "core/hle/service/server_manager.h" @@ -30,11 +33,104 @@ void ULSF_U::GetVersion(HLERequestContext& ctx) { rb.PushRaw(version); } +enum class MenuMessage : u32 { + Invalid, + HomeRequest, + SdCardEjected, + GameCardMountFailure, + PreviousLaunchFailure, + ChosenHomebrew, + FinishedSleep, + ApplicationRecordsChanged, + ApplicationVerifyProgress, + ApplicationVerifyResult +}; +struct MenuMessageContext { + MenuMessage msg; + union { + struct { + Result mount_rc; + } gc_mount_failure; + struct { + char nro_path[0x301]; + } chosen_hb; + struct { + bool records_added_or_deleted; + } app_records_changed; + struct { + u64 app_id; + u64 done; + u64 total; + } app_verify_progress; + struct { + u64 app_id; + Result rc; + Result detail_rc; + } app_verify_rc; + }; +}; + +class ULSF_P final : public ServiceFramework { +public: + explicit ULSF_P(Core::System& system_) : ServiceFramework{system_, "ulsf:p"} { + static const FunctionInfo functions[] = { + {0, C<&ULSF_P::Initialize>, "Initialize"}, + {1, C<&ULSF_P::TryPopMessageContext>, "TryPopMessageContext"}, + }; + RegisterHandlers(functions); + } + ~ULSF_P() = default; + + Result Initialize(u64 pid) { + LOG_WARNING(Service_SM, "(stubbed) pid={}", pid); + R_SUCCEED(); + } + Result TryPopMessageContext(OutLargeData out_menu_message) { + //LOG_WARNING(Service_SM, "(stubbed)"); +// *out_menu_message = {}; +// R_SUCCEED(); + R_THROW(Kernel::ResultInvalidAddress); + } +}; + +class AVM final : public ServiceFramework { +public: + explicit AVM(Core::System& system_): ServiceFramework{system_, "avm"} { + static const FunctionInfo functions[] = { + {0, &AVM::Cmd0, "Cmd0"}, + }; + RegisterHandlers(functions); + } + ~AVM() = default; + void Cmd0(HLERequestContext& ctx) { + LOG_WARNING(Service_SM, "(stubbed)"); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); + } +}; +class WLAN final : public ServiceFramework { +public: + explicit WLAN(Core::System& system_): ServiceFramework{system_, "wlan"} { + static const FunctionInfo functions[] = { + {0, &WLAN::Cmd0, "Cmd0"}, + }; + RegisterHandlers(functions); + } + ~WLAN() = default; + void Cmd0(HLERequestContext& ctx) { + LOG_WARNING(Service_SM, "(stubbed)"); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); + } +}; + void LoopProcess(Core::System& system) { auto server_manager = std::make_unique(system); server_manager->RegisterNamedService("ulsf:u", std::make_shared(system)); server_manager->RegisterNamedService("ulsf:p", std::make_shared(system)); + server_manager->RegisterNamedService("avm", std::make_shared(system)); + server_manager->RegisterNamedService("wlan", std::make_shared(system)); ServerManager::RunServer(std::move(server_manager)); } From 272a71d27dffa266d78fea8298488511460e0002 Mon Sep 17 00:00:00 2001 From: lizzie Date: Wed, 20 May 2026 08:45:54 +0000 Subject: [PATCH 13/18] license --- src/core/hle/service/am/am_types.h | 2 +- src/core/hle/service/btm/btm_system_core.h | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/core/hle/service/am/am_types.h b/src/core/hle/service/am/am_types.h index 1e150f02bf..dfd5ab49d2 100644 --- a/src/core/hle/service/am/am_types.h +++ b/src/core/hle/service/am/am_types.h @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project diff --git a/src/core/hle/service/btm/btm_system_core.h b/src/core/hle/service/btm/btm_system_core.h index c390627f28..3fc4c0d56c 100644 --- a/src/core/hle/service/btm/btm_system_core.h +++ b/src/core/hle/service/btm/btm_system_core.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later From 20f1a2ef02466c13e069eda98984b124cf42d69a Mon Sep 17 00:00:00 2001 From: lizzie Date: Wed, 20 May 2026 10:27:37 +0000 Subject: [PATCH 14/18] ulauncher ipc emu --- .../service/library_applet_self_accessor.cpp | 17 +++++++++++++++++ src/yuzu/main_window.cpp | 19 ++++--------------- 2 files changed, 21 insertions(+), 15 deletions(-) diff --git a/src/core/hle/service/am/service/library_applet_self_accessor.cpp b/src/core/hle/service/am/service/library_applet_self_accessor.cpp index 4a1d1f97d3..3716f71e8f 100644 --- a/src/core/hle/service/am/service/library_applet_self_accessor.cpp +++ b/src/core/hle/service/am/service/library_applet_self_accessor.cpp @@ -8,6 +8,7 @@ #include "core/file_sys/control_metadata.h" #include "core/file_sys/patch_manager.h" #include "core/file_sys/registered_cache.h" +#include "core/hle/result.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" @@ -99,6 +100,22 @@ Result ILibraryAppletSelfAccessor::PopInData(Out> out_st Result ILibraryAppletSelfAccessor::PushOutData(SharedPointer storage) { LOG_INFO(Service_AM, "called"); m_broker->GetOutData().Push(storage); + + if (m_applet->applet_id == AppletId::UlauncherUmenu) { + LOG_WARNING(Service_AM, "emulating uLauncher IPC"); + std::shared_ptr tmp_storage; + m_broker->GetOutData().Pop(&tmp_storage); + std::vector result_data(0x8000); + struct CommandCommonHeader { + u32 magic; + u32 val; + } cmd; + cmd.magic = 0x21494D53; + cmd.val = u32(ResultSuccess.raw); + std::memcpy(result_data.data(), &cmd, sizeof(cmd)); + m_broker->GetInData().Push(std::make_shared(system, std::move(result_data))); + } + R_SUCCEED(); } diff --git a/src/yuzu/main_window.cpp b/src/yuzu/main_window.cpp index 01859aee18..7ed0289fb0 100644 --- a/src/yuzu/main_window.cpp +++ b/src/yuzu/main_window.cpp @@ -512,14 +512,12 @@ MainWindow::MainWindow(bool has_broken_vulkan) return; } - QString game_path; + QString game_path{}; bool should_launch_qlaunch = false; bool should_launch_hlaunch = false; bool should_launch_hlaunch_uloader = false; - bool should_launch_umenu = false; bool should_launch_ulaunch = false; bool should_launch_setup = false; - bool has_gamepath = false; bool is_fullscreen = false; // Preserves drag/drop functionality @@ -553,7 +551,6 @@ MainWindow::MainWindow(bool has_broken_vulkan) } else if (args[i] == QStringLiteral("-g") && i < args.size() - 1) { // Launch game at path game_path = args[++i]; - has_gamepath = true; } else if (args[i] == QStringLiteral("-input-profile") && i < args.size() - 1) { auto& players = Settings::values.players.GetValue(); players[0].profile_name = args[++i].toStdString(); @@ -564,20 +561,16 @@ MainWindow::MainWindow(bool has_broken_vulkan) } else if (args[i] == QStringLiteral("-hlaunch-uloader")) { should_launch_hlaunch_uloader = true; } else if (args[i] == QStringLiteral("-ulaunch")) { - //should_launch_ulaunch = true; - should_launch_umenu = true; - } else if (args[i] == QStringLiteral("-umenu")) { - should_launch_umenu = true; + should_launch_ulaunch = true; } else if (args[i] == QStringLiteral("-setup")) { should_launch_setup = true; } else { game_path = args[i]; - has_gamepath = true; } } // Override fullscreen setting if gamepath or argument is provided - if (has_gamepath || is_fullscreen) { + if (!game_path.isEmpty() || is_fullscreen) { ui->action_Fullscreen->setChecked(is_fullscreen); } @@ -599,15 +592,11 @@ MainWindow::MainWindow(bool has_broken_vulkan) params.launch_type = Service::AM::LaunchType::FrontendUlaunchInitiated; BootGame(QString::fromStdString(path), params); } else if (should_launch_ulaunch) { - std::filesystem::path const sd_dir = Common::FS::GetEdenPathString(Common::FS::EdenPath::SDMCDir); - auto const path = (sd_dir / "ulaunch" / "bin" / "uSystem" / "exefs.nsp").string(); - BootGame(QString::fromStdString(path), ApplicationAppletParameters()); - } else if (should_launch_umenu) { std::filesystem::path const sd_dir = Common::FS::GetEdenPathString(Common::FS::EdenPath::SDMCDir); auto const path = (sd_dir / "ulaunch" / "bin" / "uMenu" / "main").string(); auto const program_id = 0x010000000000FFFF; auto params = LibraryAppletParameters(program_id, Service::AM::AppletId::UlauncherUmenu); - params.launch_type = Service::AM::LaunchType::FrontendUmenuInitiated; + params.launch_type = Service::AM::LaunchType::FrontendInitiated; BootGame(QString::fromStdString(path), params); } } From ea9f43d987f4766b5682c6c9efbef4eac10bf2c9 Mon Sep 17 00:00:00 2001 From: lizzie Date: Wed, 20 May 2026 10:46:05 +0000 Subject: [PATCH 15/18] enumerate --- .../service/library_applet_self_accessor.cpp | 81 +++++++++++++++++-- 1 file changed, 73 insertions(+), 8 deletions(-) diff --git a/src/core/hle/service/am/service/library_applet_self_accessor.cpp b/src/core/hle/service/am/service/library_applet_self_accessor.cpp index 3716f71e8f..017b1dc5a6 100644 --- a/src/core/hle/service/am/service/library_applet_self_accessor.cpp +++ b/src/core/hle/service/am/service/library_applet_self_accessor.cpp @@ -103,17 +103,82 @@ Result ILibraryAppletSelfAccessor::PushOutData(SharedPointer storage) if (m_applet->applet_id == AppletId::UlauncherUmenu) { LOG_WARNING(Service_AM, "emulating uLauncher IPC"); - std::shared_ptr tmp_storage; - m_broker->GetOutData().Pop(&tmp_storage); - std::vector result_data(0x8000); + + enum class SystemMessage : u32 { + Invalid, + SetSelectedUser, + LaunchApplication, + ResumeApplication, + TerminateApplication, + LaunchHomebrewLibraryApplet, + LaunchHomebrewApplication, + ChooseHomebrew, + OpenWebPage, + OpenAlbum, + RestartMenu, + ReloadConfig, + UpdateMenuPaths, + UpdateMenuIndex, + OpenUserPage, + OpenMiiEdit, + OpenAddUser, + OpenNetConnect, + ListAddedApplications, + ListDeletedApplications, + OpenCabinet, + StartVerifyApplication, + ListInVerifyApplications, + NotifyWarnedAboutOutdatedTheme, + TerminateMenu, + OpenControllerKeyRemapping + }; struct CommandCommonHeader { u32 magic; u32 val; - } cmd; - cmd.magic = 0x21494D53; - cmd.val = u32(ResultSuccess.raw); - std::memcpy(result_data.data(), &cmd, sizeof(cmd)); - m_broker->GetInData().Push(std::make_shared(system, std::move(result_data))); + }; + std::shared_ptr req_storage; + m_broker->GetOutData().Pop(&req_storage); + auto req_data = req_storage->GetData(); + CommandCommonHeader req_cmd{}; + std::memcpy(req_data.data(), &req_cmd, sizeof(req_cmd)); + + switch (SystemMessage(req_cmd.val)) { + case SystemMessage::SetSelectedUser: + case SystemMessage::LaunchApplication: + case SystemMessage::ResumeApplication: + case SystemMessage::TerminateApplication: + case SystemMessage::LaunchHomebrewLibraryApplet: + case SystemMessage::LaunchHomebrewApplication: + case SystemMessage::ChooseHomebrew: + case SystemMessage::OpenWebPage: + case SystemMessage::OpenAlbum: + case SystemMessage::RestartMenu: + case SystemMessage::ReloadConfig: + case SystemMessage::UpdateMenuPaths: + case SystemMessage::UpdateMenuIndex: + case SystemMessage::OpenUserPage: + case SystemMessage::OpenMiiEdit: + case SystemMessage::OpenAddUser: + case SystemMessage::OpenNetConnect: + case SystemMessage::ListAddedApplications: + case SystemMessage::ListDeletedApplications: + case SystemMessage::OpenCabinet: + case SystemMessage::StartVerifyApplication: + case SystemMessage::ListInVerifyApplications: + case SystemMessage::NotifyWarnedAboutOutdatedTheme: + case SystemMessage::TerminateMenu: + case SystemMessage::OpenControllerKeyRemapping: + break; + case SystemMessage::Invalid: + break; + } + + CommandCommonHeader res_cmd{}; + std::vector res_data(0x8000); + res_cmd.magic = 0x21494D53; + res_cmd.val = u32(ResultSuccess.raw); + std::memcpy(res_data.data(), &res_cmd, sizeof(res_cmd)); + m_broker->GetInData().Push(std::make_shared(system, std::move(res_data))); } R_SUCCEED(); From ea4d36ad068ab894774b92d6efebb3ee57c18b90 Mon Sep 17 00:00:00 2001 From: lizzie Date: Thu, 21 May 2026 04:11:16 +0000 Subject: [PATCH 16/18] try make work --- .../service/library_applet_self_accessor.cpp | 45 +++++++++++++++++-- 1 file changed, 41 insertions(+), 4 deletions(-) diff --git a/src/core/hle/service/am/service/library_applet_self_accessor.cpp b/src/core/hle/service/am/service/library_applet_self_accessor.cpp index 017b1dc5a6..fc427173f4 100644 --- a/src/core/hle/service/am/service/library_applet_self_accessor.cpp +++ b/src/core/hle/service/am/service/library_applet_self_accessor.cpp @@ -13,6 +13,7 @@ #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/process_creation.h" #include "core/hle/service/am/service/library_applet_self_accessor.h" #include "core/hle/service/am/service/storage.h" #include "core/hle/service/cmif_serialization.h" @@ -20,7 +21,9 @@ #include "core/hle/service/glue/glue_manager.h" #include "core/hle/service/ns/application_manager_interface.h" #include "core/hle/service/ns/service_getter_interface.h" +#include "core/hle/service/server_manager.h" #include "core/hle/service/sm/sm.h" +#include "core/loader/loader.h" namespace Service::AM { @@ -97,13 +100,33 @@ Result ILibraryAppletSelfAccessor::PopInData(Out> out_st R_RETURN(m_broker->GetInData().Pop(out_storage)); } +// uLauncher emulation +static Result UloaderCreateApplication(Core::System& system, u64 program_id) { + FileSys::VirtualFile nca_raw{}; + // Get the program NCA from storage. + auto& storage = system.GetContentProviderUnion(); + nca_raw = storage.GetEntryRaw(program_id, FileSys::ContentRecordType::Program); + // Ensure we retrieved a program NCA. + R_UNLESS(nca_raw != nullptr, ResultUnknown); + std::vector control; + std::unique_ptr loader; + Loader::ResultStatus result; + auto process = CreateApplicationProcess(control, loader, result, system, nca_raw, program_id, 0); + R_UNLESS(process != nullptr, ResultUnknown); + const auto applet = std::make_shared(system, std::move(process), true); + applet->program_id = program_id; + applet->applet_id = AppletId::Application; + applet->type = AppletType::Application; + applet->library_applet_mode = LibraryAppletMode::AllForeground; + //window_system.TrackApplet(applet, true); + R_SUCCEED(); +} + Result ILibraryAppletSelfAccessor::PushOutData(SharedPointer storage) { LOG_INFO(Service_AM, "called"); m_broker->GetOutData().Push(storage); if (m_applet->applet_id == AppletId::UlauncherUmenu) { - LOG_WARNING(Service_AM, "emulating uLauncher IPC"); - enum class SystemMessage : u32 { Invalid, SetSelectedUser, @@ -140,11 +163,23 @@ Result ILibraryAppletSelfAccessor::PushOutData(SharedPointer storage) m_broker->GetOutData().Pop(&req_storage); auto req_data = req_storage->GetData(); CommandCommonHeader req_cmd{}; - std::memcpy(req_data.data(), &req_cmd, sizeof(req_cmd)); + std::memcpy(&req_cmd, req_data.data(), sizeof(req_cmd)); + LOG_WARNING(Service_AM, "uLauncher:IPC {}, {}", req_cmd.magic, req_cmd.val); switch (SystemMessage(req_cmd.val)) { case SystemMessage::SetSelectedUser: - case SystemMessage::LaunchApplication: + break; + case SystemMessage::LaunchApplication: { + // all applet proxies OpenSystemAppletProxy + // applet proxy GetApplicationCreator + // application creator CreateApplication + u64 args_value{}; + std::memcpy(std::addressof(args_value), req_data.data() + sizeof(req_cmd), sizeof(args_value)); + LOG_WARNING(Service_AM, "program_id={:016x}", args_value); + m_applet->process->Terminate(); + UloaderCreateApplication(system, args_value); + R_SUCCEED(); + } case SystemMessage::ResumeApplication: case SystemMessage::TerminateApplication: case SystemMessage::LaunchHomebrewLibraryApplet: @@ -166,7 +201,9 @@ Result ILibraryAppletSelfAccessor::PushOutData(SharedPointer storage) case SystemMessage::StartVerifyApplication: case SystemMessage::ListInVerifyApplications: case SystemMessage::NotifyWarnedAboutOutdatedTheme: + break; case SystemMessage::TerminateMenu: + break; case SystemMessage::OpenControllerKeyRemapping: break; case SystemMessage::Invalid: From a8d6b4fe60bf5bc15ceeefe1f6b43a985dd1820c Mon Sep 17 00:00:00 2001 From: lizzie Date: Thu, 21 May 2026 05:52:31 +0000 Subject: [PATCH 17/18] finalize process when requested by uMenu --- src/core/hle/kernel/init/init_slab_setup.cpp | 4 +++- src/core/hle/service/am/applet_manager.cpp | 2 +- .../am/service/library_applet_self_accessor.cpp | 11 ++++++----- src/core/hle/service/am/window_system.cpp | 2 +- src/yuzu/main_window.cpp | 5 +++-- 5 files changed, 14 insertions(+), 10 deletions(-) diff --git a/src/core/hle/kernel/init/init_slab_setup.cpp b/src/core/hle/kernel/init/init_slab_setup.cpp index a0e20bbbb4..a685a142f5 100644 --- a/src/core/hle/kernel/init/init_slab_setup.cpp +++ b/src/core/hle/kernel/init/init_slab_setup.cpp @@ -83,7 +83,9 @@ constexpr size_t SlabCountKDeviceAddressSpace = 300; constexpr size_t SlabCountKSession = 1133; constexpr size_t SlabCountKLightSession = 100; constexpr size_t SlabCountKObjectName = 7; -constexpr size_t SlabCountKResourceLimit = 5; +// Slight divergence to allow for uLaunch to work properly +// TODO(lizzie): This should be 5, shouldn't it? +constexpr size_t SlabCountKResourceLimit = 5 + 8; constexpr size_t SlabCountKDebug = Core::Hardware::NUM_CPU_CORES; constexpr size_t SlabCountKIoPool = 1; constexpr size_t SlabCountKIoRegion = 6; diff --git a/src/core/hle/service/am/applet_manager.cpp b/src/core/hle/service/am/applet_manager.cpp index badf776082..6d27285b05 100644 --- a/src/core/hle/service/am/applet_manager.cpp +++ b/src/core/hle/service/am/applet_manager.cpp @@ -407,7 +407,7 @@ void AppletManager::SetWindowSystem(WindowSystem* window_system) { // Applet was started by frontend, so it is foreground. applet->lifecycle_manager.SetFocusState(FocusState::InFocus); - if (applet->applet_id == AppletId::QLaunch) { + if (applet->applet_id == AppletId::QLaunch || applet->applet_id == AppletId::UlauncherUmenu) { applet->lifecycle_manager.SetFocusHandlingMode(false); applet->lifecycle_manager.SetOutOfFocusSuspendingEnabled(false); m_window_system->TrackApplet(applet, false); diff --git a/src/core/hle/service/am/service/library_applet_self_accessor.cpp b/src/core/hle/service/am/service/library_applet_self_accessor.cpp index fc427173f4..d571bec3ba 100644 --- a/src/core/hle/service/am/service/library_applet_self_accessor.cpp +++ b/src/core/hle/service/am/service/library_applet_self_accessor.cpp @@ -16,6 +16,7 @@ #include "core/hle/service/am/process_creation.h" #include "core/hle/service/am/service/library_applet_self_accessor.h" #include "core/hle/service/am/service/storage.h" +#include "core/hle/service/am/window_system.h" #include "core/hle/service/cmif_serialization.h" #include "core/hle/service/filesystem/filesystem.h" #include "core/hle/service/glue/glue_manager.h" @@ -102,10 +103,9 @@ Result ILibraryAppletSelfAccessor::PopInData(Out> out_st // uLauncher emulation static Result UloaderCreateApplication(Core::System& system, u64 program_id) { - FileSys::VirtualFile nca_raw{}; // Get the program NCA from storage. auto& storage = system.GetContentProviderUnion(); - nca_raw = storage.GetEntryRaw(program_id, FileSys::ContentRecordType::Program); + FileSys::VirtualFile nca_raw = storage.GetEntryRaw(program_id, FileSys::ContentRecordType::Program); // Ensure we retrieved a program NCA. R_UNLESS(nca_raw != nullptr, ResultUnknown); std::vector control; @@ -118,7 +118,7 @@ static Result UloaderCreateApplication(Core::System& system, u64 program_id) { applet->applet_id = AppletId::Application; applet->type = AppletType::Application; applet->library_applet_mode = LibraryAppletMode::AllForeground; - //window_system.TrackApplet(applet, true); + system.GetAppletManager().GetWindowSystem()->TrackApplet(applet, true); R_SUCCEED(); } @@ -176,9 +176,8 @@ Result ILibraryAppletSelfAccessor::PushOutData(SharedPointer storage) u64 args_value{}; std::memcpy(std::addressof(args_value), req_data.data() + sizeof(req_cmd), sizeof(args_value)); LOG_WARNING(Service_AM, "program_id={:016x}", args_value); - m_applet->process->Terminate(); UloaderCreateApplication(system, args_value); - R_SUCCEED(); + break; } case SystemMessage::ResumeApplication: case SystemMessage::TerminateApplication: @@ -203,6 +202,8 @@ Result ILibraryAppletSelfAccessor::PushOutData(SharedPointer storage) case SystemMessage::NotifyWarnedAboutOutdatedTheme: break; case SystemMessage::TerminateMenu: + m_applet->process->Terminate(); + system.GetAppletManager().GetWindowSystem()->RequestApplicationToGetForeground(); break; case SystemMessage::OpenControllerKeyRemapping: break; diff --git a/src/core/hle/service/am/window_system.cpp b/src/core/hle/service/am/window_system.cpp index 5f20a98322..c40011f702 100644 --- a/src/core/hle/service/am/window_system.cpp +++ b/src/core/hle/service/am/window_system.cpp @@ -59,7 +59,7 @@ void WindowSystem::Update() { void WindowSystem::TrackApplet(std::shared_ptr applet, bool is_application) { std::scoped_lock lk{m_lock}; - if (applet->applet_id == AppletId::QLaunch) { + if (applet->applet_id == AppletId::QLaunch || applet->applet_id == AppletId::UlauncherUmenu) { ASSERT(m_home_menu == nullptr); m_home_menu = applet.get(); } else if (applet->applet_id == AppletId::OverlayDisplay) { diff --git a/src/yuzu/main_window.cpp b/src/yuzu/main_window.cpp index 7ed0289fb0..c2e6473c51 100644 --- a/src/yuzu/main_window.cpp +++ b/src/yuzu/main_window.cpp @@ -595,8 +595,9 @@ MainWindow::MainWindow(bool has_broken_vulkan) std::filesystem::path const sd_dir = Common::FS::GetEdenPathString(Common::FS::EdenPath::SDMCDir); auto const path = (sd_dir / "ulaunch" / "bin" / "uMenu" / "main").string(); auto const program_id = 0x010000000000FFFF; - auto params = LibraryAppletParameters(program_id, Service::AM::AppletId::UlauncherUmenu); - params.launch_type = Service::AM::LaunchType::FrontendInitiated; + auto const applet_id = Service::AM::AppletId::UlauncherUmenu; + auto params = LibraryAppletParameters(program_id, applet_id); + QtCommon::system->GetFrontendAppletHolder().SetCurrentAppletId(applet_id); BootGame(QString::fromStdString(path), params); } } From 6b94984041e6cf866ddfecb65c7a9479b2aa478a Mon Sep 17 00:00:00 2001 From: lizzie Date: Thu, 21 May 2026 16:58:18 +0000 Subject: [PATCH 18/18] properly start some stuff? --- src/core/hle/kernel/init/init_slab_setup.cpp | 5 ++--- .../service/library_applet_self_accessor.cpp | 19 +++++++++++++------ 2 files changed, 15 insertions(+), 9 deletions(-) diff --git a/src/core/hle/kernel/init/init_slab_setup.cpp b/src/core/hle/kernel/init/init_slab_setup.cpp index a685a142f5..da5e90df6e 100644 --- a/src/core/hle/kernel/init/init_slab_setup.cpp +++ b/src/core/hle/kernel/init/init_slab_setup.cpp @@ -83,9 +83,8 @@ constexpr size_t SlabCountKDeviceAddressSpace = 300; constexpr size_t SlabCountKSession = 1133; constexpr size_t SlabCountKLightSession = 100; constexpr size_t SlabCountKObjectName = 7; -// Slight divergence to allow for uLaunch to work properly -// TODO(lizzie): This should be 5, shouldn't it? -constexpr size_t SlabCountKResourceLimit = 5 + 8; +// TODO(lizzie): divergence that allows ulauncher to work +constexpr size_t SlabCountKResourceLimit = 5 + 1; constexpr size_t SlabCountKDebug = Core::Hardware::NUM_CPU_CORES; constexpr size_t SlabCountKIoPool = 1; constexpr size_t SlabCountKIoRegion = 6; diff --git a/src/core/hle/service/am/service/library_applet_self_accessor.cpp b/src/core/hle/service/am/service/library_applet_self_accessor.cpp index d571bec3ba..248c6fa03f 100644 --- a/src/core/hle/service/am/service/library_applet_self_accessor.cpp +++ b/src/core/hle/service/am/service/library_applet_self_accessor.cpp @@ -49,8 +49,9 @@ AppletIdentityInfo GetCallerIdentity(Applet& applet) { ILibraryAppletSelfAccessor::ILibraryAppletSelfAccessor(Core::System& system_, std::shared_ptr applet) - : ServiceFramework{system_, "ILibraryAppletSelfAccessor"}, m_applet{std::move(applet)}, - m_broker{m_applet->caller_applet_broker} { + : ServiceFramework{system_, "ILibraryAppletSelfAccessor"}, m_applet{std::move(applet)} + , m_broker{m_applet->caller_applet_broker} +{ // clang-format off static const FunctionInfo functions[] = { {0, D<&ILibraryAppletSelfAccessor::PopInData>, "PopInData"}, @@ -102,7 +103,7 @@ Result ILibraryAppletSelfAccessor::PopInData(Out> out_st } // uLauncher emulation -static Result UloaderCreateApplication(Core::System& system, u64 program_id) { +static Result UloaderCreateApplication(Core::System& system, u64 program_id, std::shared_ptr caller_applet) { // Get the program NCA from storage. auto& storage = system.GetContentProviderUnion(); FileSys::VirtualFile nca_raw = storage.GetEntryRaw(program_id, FileSys::ContentRecordType::Program); @@ -118,6 +119,12 @@ static Result UloaderCreateApplication(Core::System& system, u64 program_id) { applet->applet_id = AppletId::Application; applet->type = AppletType::Application; applet->library_applet_mode = LibraryAppletMode::AllForeground; + + applet->caller_applet = caller_applet; + applet->caller_applet_broker = std::make_shared(system); + applet->frontend = caller_applet->frontend; + caller_applet->child_applets.push_back(applet); + system.GetAppletManager().GetWindowSystem()->TrackApplet(applet, true); R_SUCCEED(); } @@ -176,7 +183,7 @@ Result ILibraryAppletSelfAccessor::PushOutData(SharedPointer storage) u64 args_value{}; std::memcpy(std::addressof(args_value), req_data.data() + sizeof(req_cmd), sizeof(args_value)); LOG_WARNING(Service_AM, "program_id={:016x}", args_value); - UloaderCreateApplication(system, args_value); + UloaderCreateApplication(system, args_value, m_applet); break; } case SystemMessage::ResumeApplication: @@ -202,8 +209,8 @@ Result ILibraryAppletSelfAccessor::PushOutData(SharedPointer storage) case SystemMessage::NotifyWarnedAboutOutdatedTheme: break; case SystemMessage::TerminateMenu: - m_applet->process->Terminate(); - system.GetAppletManager().GetWindowSystem()->RequestApplicationToGetForeground(); + system.GetUserChannel() = m_applet->user_channel_launch_parameter; + system.ExecuteProgram(0); break; case SystemMessage::OpenControllerKeyRemapping: break;