diff --git a/src/common/logging/backend.cpp b/src/common/logging/backend.cpp index 4b83eeb28..0bc611649 100644 --- a/src/common/logging/backend.cpp +++ b/src/common/logging/backend.cpp @@ -47,6 +47,7 @@ namespace Log { SUB(Service, NDM) \ SUB(Service, NFC) \ SUB(Service, NIM) \ + SUB(Service, NS) \ SUB(Service, NWM) \ SUB(Service, CAM) \ SUB(Service, CECD) \ diff --git a/src/common/logging/log.h b/src/common/logging/log.h index fe4dfed69..f36642c38 100644 --- a/src/common/logging/log.h +++ b/src/common/logging/log.h @@ -64,6 +64,7 @@ enum class Class : ClassType { Service_NDM, ///< The NDM (Network daemon manager) service Service_NFC, ///< The NFC service Service_NIM, ///< The NIM (Network interface manager) service + Service_NS, ///< The NS (Nintendo User Interface Shell) service Service_NWM, ///< The NWM (Network wlan manager) service Service_CAM, ///< The CAM (Camera) service Service_CECD, ///< The CECD (StreetPass) service diff --git a/src/core/hle/service/apt/apt.cpp b/src/core/hle/service/apt/apt.cpp index cbebc6c9d..fbc080d76 100644 --- a/src/core/hle/service/apt/apt.cpp +++ b/src/core/hle/service/apt/apt.cpp @@ -21,6 +21,7 @@ #include "core/hle/service/apt/bcfnt/bcfnt.h" #include "core/hle/service/cfg/cfg.h" #include "core/hle/service/fs/archive.h" +#include "core/hle/service/ns/ns.h" #include "core/hle/service/ptm/ptm.h" #include "core/hle/service/service.h" #include "core/hw/aes/ccm.h" @@ -83,6 +84,41 @@ struct AppletSlotData { // Holds data about the concurrently running applets in the system. static std::array applet_slots = {}; +struct AppletTitleData { + // There are two possible applet ids for each applet. + std::array applet_ids; + + // There's a specific TitleId per region for each applet. + static constexpr size_t NumRegions = 7; + std::array title_ids; +}; + +static constexpr size_t NumApplets = 29; +static constexpr std::array applet_titleids = {{ + {AppletId::HomeMenu, AppletId::None, 0x4003000008202, 0x4003000008F02, 0x4003000009802, + 0x4003000008202, 0x400300000A102, 0x400300000A902, 0x400300000B102}, + {AppletId::SoftwareKeyboard1, AppletId::SoftwareKeyboard2, 0x400300000C002, 0x400300000C802, + 0x400300000D002, 0x400300000C002, 0x400300000D802, 0x400300000DE02, 0x400300000E402}, + {AppletId::Error, AppletId::Error2, 0x400300000C502, 0x400300000C502, 0x400300000C502, + 0x400300000C502, 0x400300000CF02, 0x400300000CF02, 0x400300000CF02}, + {AppletId::Ed1, AppletId::Ed2, 0x400300000C102, 0x400300000C902, 0x400300000D102, + 0x400300000C102, 0x400300000D902, 0x400300000DF02, 0x400300000E502}, + // TODO(Subv): Fill in the rest of the titleids +}}; + +static u64 GetTitleIdForApplet(AppletId id) { + ASSERT_MSG(id != AppletId::None, "Invalid applet id"); + + auto itr = std::find_if(applet_titleids.begin(), applet_titleids.end(), + [id](const AppletTitleData& data) { + return data.applet_ids[0] == id || data.applet_ids[1] == id; + }); + + ASSERT_MSG(itr != applet_titleids.end(), "Unknown applet id"); + + return itr->title_ids[CFG::GetRegionValue()]; +} + // This overload returns nullptr if no applet with the specified id has been started. static AppletSlotData* GetAppletSlotData(AppletId id) { auto GetSlot = [](AppletSlot slot) -> AppletSlotData* { @@ -771,8 +807,29 @@ void PrepareToStartLibraryApplet(Service::Interface* self) { IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); - // TODO(Subv): Launch the requested applet application. + // The real APT service returns an error if there's a pending APT parameter when this function + // is called. + if (next_parameter) { + rb.Push(ResultCode(ErrCodes::ParameterPresent, ErrorModule::Applet, + ErrorSummary::InvalidState, ErrorLevel::Status)); + return; + } + const auto& slot = applet_slots[static_cast(AppletSlot::LibraryApplet)]; + + if (slot.registered) { + rb.Push(ResultCode(ErrorDescription::AlreadyExists, ErrorModule::Applet, + ErrorSummary::InvalidState, ErrorLevel::Status)); + return; + } + + auto process = NS::LaunchTitle(FS::MediaType::NAND, GetTitleIdForApplet(applet_id)); + if (process) { + rb.Push(RESULT_SUCCESS); + return; + } + + // If we weren't able to load the native applet title, try to fallback to an HLE implementation. auto applet = HLE::Applets::Applet::Get(applet_id); if (applet) { LOG_WARNING(Service_APT, "applet has already been started id=%08X", @@ -805,8 +862,21 @@ void PreloadLibraryApplet(Service::Interface* self) { IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); - // TODO(Subv): Launch the requested applet application. + const auto& slot = applet_slots[static_cast(AppletSlot::LibraryApplet)]; + if (slot.registered) { + rb.Push(ResultCode(ErrorDescription::AlreadyExists, ErrorModule::Applet, + ErrorSummary::InvalidState, ErrorLevel::Status)); + return; + } + + auto process = NS::LaunchTitle(FS::MediaType::NAND, GetTitleIdForApplet(applet_id)); + if (process) { + rb.Push(RESULT_SUCCESS); + return; + } + + // If we weren't able to load the native applet title, try to fallback to an HLE implementation. auto applet = HLE::Applets::Applet::Get(applet_id); if (applet) { LOG_WARNING(Service_APT, "applet has already been started id=%08X", diff --git a/src/core/hle/service/ns/ns.cpp b/src/core/hle/service/ns/ns.cpp index 9e19c38bf..9616cc8f6 100644 --- a/src/core/hle/service/ns/ns.cpp +++ b/src/core/hle/service/ns/ns.cpp @@ -2,12 +2,35 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include +#include "core/hle/service/am/am.h" #include "core/hle/service/ns/ns.h" #include "core/hle/service/ns/ns_s.h" +#include "core/loader/loader.h" namespace Service { namespace NS { +Kernel::SharedPtr LaunchTitle(FS::MediaType media_type, u64 title_id) { + std::string path = AM::GetTitleContentPath(media_type, title_id); + auto loader = Loader::GetLoader(path); + + if (!loader) { + LOG_WARNING(Service_NS, "Could not find .app for title 0x%016" PRIx64, title_id); + return nullptr; + } + + Kernel::SharedPtr process; + Loader::ResultStatus result = loader->Load(process); + + if (result != Loader::ResultStatus::Success) { + LOG_WARNING(Service_NS, "Error loading .app for title 0x%016" PRIx64, title_id); + return nullptr; + } + + return process; +} + void InstallInterfaces(SM::ServiceManager& service_manager) { std::make_shared()->InstallAsService(service_manager); } diff --git a/src/core/hle/service/ns/ns.h b/src/core/hle/service/ns/ns.h index c3d67d98c..6fb171c1a 100644 --- a/src/core/hle/service/ns/ns.h +++ b/src/core/hle/service/ns/ns.h @@ -4,11 +4,15 @@ #pragma once +#include "core/hle/kernel/process.h" #include "core/hle/service/service.h" namespace Service { namespace NS { +/// Loads and launches the title identified by title_id in the specified media type. +Kernel::SharedPtr LaunchTitle(FS::MediaType media_type, u64 title_id); + /// Registers all NS services with the specified service manager. void InstallInterfaces(SM::ServiceManager& service_manager);