summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/core/CMakeLists.txt11
-rw-r--r--src/core/core.cpp66
-rw-r--r--src/core/core.h8
-rw-r--r--src/core/file_sys/patch_manager.cpp10
-rw-r--r--src/core/file_sys/patch_manager.h9
-rw-r--r--src/core/file_sys/registered_cache.cpp14
-rw-r--r--src/core/file_sys/registered_cache.h3
-rw-r--r--src/core/hle/service/arp/arp.cpp75
-rw-r--r--src/core/hle/service/arp/arp.h16
-rw-r--r--src/core/hle/service/glue/arp.cpp297
-rw-r--r--src/core/hle/service/glue/arp.h43
-rw-r--r--src/core/hle/service/glue/bgtc.cpp50
-rw-r--r--src/core/hle/service/glue/bgtc.h23
-rw-r--r--src/core/hle/service/glue/errors.h16
-rw-r--r--src/core/hle/service/glue/glue.cpp25
-rw-r--r--src/core/hle/service/glue/glue.h16
-rw-r--r--src/core/hle/service/glue/manager.cpp78
-rw-r--r--src/core/hle/service/glue/manager.h63
-rw-r--r--src/core/hle/service/service.cpp4
19 files changed, 730 insertions, 97 deletions
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index cdb3bf6ab..d65659b44 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -209,8 +209,6 @@ add_library(core STATIC
hle/service/apm/apm.h
hle/service/apm/interface.cpp
hle/service/apm/interface.h
- hle/service/arp/arp.cpp
- hle/service/arp/arp.h
hle/service/audio/audctl.cpp
hle/service/audio/audctl.h
hle/service/audio/auddbg.cpp
@@ -276,6 +274,15 @@ add_library(core STATIC
hle/service/friend/friend.h
hle/service/friend/interface.cpp
hle/service/friend/interface.h
+ hle/service/glue/arp.cpp
+ hle/service/glue/arp.h
+ hle/service/glue/bgtc.cpp
+ hle/service/glue/bgtc.h
+ hle/service/glue/errors.h
+ hle/service/glue/glue.cpp
+ hle/service/glue/glue.h
+ hle/service/glue/manager.cpp
+ hle/service/glue/manager.h
hle/service/grc/grc.cpp
hle/service/grc/grc.h
hle/service/hid/hid.cpp
diff --git a/src/core/core.cpp b/src/core/core.cpp
index c00dfd33c..df26eb109 100644
--- a/src/core/core.cpp
+++ b/src/core/core.cpp
@@ -25,6 +25,7 @@
#include "core/hle/kernel/scheduler.h"
#include "core/hle/kernel/thread.h"
#include "core/hle/service/am/applets/applets.h"
+#include "core/hle/service/glue/manager.h"
#include "core/hle/service/service.h"
#include "core/hle/service/sm/sm.h"
#include "core/loader/loader.h"
@@ -33,12 +34,37 @@
#include "core/settings.h"
#include "core/telemetry_session.h"
#include "file_sys/cheat_engine.h"
+#include "file_sys/patch_manager.h"
#include "video_core/debug_utils/debug_utils.h"
#include "video_core/renderer_base.h"
#include "video_core/video_core.h"
namespace Core {
+namespace {
+
+FileSys::StorageId GetStorageIdForFrontendSlot(
+ std::optional<FileSys::ContentProviderUnionSlot> slot) {
+ if (!slot.has_value()) {
+ return FileSys::StorageId::None;
+ }
+
+ switch (*slot) {
+ case FileSys::ContentProviderUnionSlot::UserNAND:
+ return FileSys::StorageId::NandUser;
+ case FileSys::ContentProviderUnionSlot::SysNAND:
+ return FileSys::StorageId::NandSystem;
+ case FileSys::ContentProviderUnionSlot::SDMC:
+ return FileSys::StorageId::SdCard;
+ case FileSys::ContentProviderUnionSlot::FrontendManual:
+ return FileSys::StorageId::Host;
+ default:
+ return FileSys::StorageId::None;
+ }
+}
+
+} // Anonymous namespace
+
/*static*/ System System::s_instance;
FileSys::VirtualFile GetGameFileFromPath(const FileSys::VirtualFilesystem& vfs,
@@ -110,6 +136,9 @@ struct System::Impl {
/// Create default implementations of applets if one is not provided.
applet_manager.SetDefaultAppletsIfMissing();
+ /// Reset all glue registrations
+ arp_manager.ResetAll();
+
telemetry_session = std::make_unique<Core::TelemetrySession>();
service_manager = std::make_shared<Service::SM::ServiceManager>();
@@ -161,6 +190,7 @@ struct System::Impl {
return static_cast<ResultStatus>(static_cast<u32>(ResultStatus::ErrorLoader) +
static_cast<u32>(load_result));
}
+ AddGlueRegistrationForProcess(*app_loader, *main_process);
kernel.MakeCurrentProcess(main_process.get());
// Main process has been loaded and been made current.
@@ -219,6 +249,31 @@ struct System::Impl {
return app_loader->ReadTitle(out);
}
+ void AddGlueRegistrationForProcess(Loader::AppLoader& loader, Kernel::Process& process) {
+ std::vector<u8> nacp_data;
+ FileSys::NACP nacp;
+ if (loader.ReadControlData(nacp) == Loader::ResultStatus::Success) {
+ nacp_data = nacp.GetRawBytes();
+ } else {
+ nacp_data.resize(sizeof(FileSys::RawNACP));
+ }
+
+ Service::Glue::ApplicationLaunchProperty launch{};
+ launch.title_id = process.GetTitleID();
+
+ FileSys::PatchManager pm{launch.title_id};
+ launch.version = pm.GetGameVersion().value_or(0);
+
+ // TODO(DarkLordZach): When FSController/Game Card Support is added, if
+ // current_process_game_card use correct StorageId
+ launch.base_game_storage_id = GetStorageIdForFrontendSlot(content_provider->GetSlotForEntry(
+ launch.title_id, FileSys::ContentRecordType::Program));
+ launch.update_storage_id = GetStorageIdForFrontendSlot(content_provider->GetSlotForEntry(
+ FileSys::GetUpdateTitleID(launch.title_id), FileSys::ContentRecordType::Program));
+
+ arp_manager.Register(launch.title_id, launch, std::move(nacp_data));
+ }
+
void SetStatus(ResultStatus new_status, const char* details = nullptr) {
status = new_status;
if (details) {
@@ -249,6 +304,9 @@ struct System::Impl {
/// Frontend applets
Service::AM::Applets::AppletManager applet_manager;
+ /// Glue services
+ Service::Glue::ARPManager arp_manager;
+
/// Service manager
std::shared_ptr<Service::SM::ServiceManager> service_manager;
@@ -500,6 +558,14 @@ const Reporter& System::GetReporter() const {
return impl->reporter;
}
+Service::Glue::ARPManager& System::GetARPManager() {
+ return impl->arp_manager;
+}
+
+const Service::Glue::ARPManager& System::GetARPManager() const {
+ return impl->arp_manager;
+}
+
System::ResultStatus System::Init(Frontend::EmuWindow& emu_window) {
return impl->Init(*this, emu_window);
}
diff --git a/src/core/core.h b/src/core/core.h
index 226ef4630..70adb7af9 100644
--- a/src/core/core.h
+++ b/src/core/core.h
@@ -43,6 +43,10 @@ struct AppletFrontendSet;
class AppletManager;
} // namespace AM::Applets
+namespace Glue {
+class ARPManager;
+}
+
namespace SM {
class ServiceManager;
} // namespace SM
@@ -288,6 +292,10 @@ public:
const Reporter& GetReporter() const;
+ Service::Glue::ARPManager& GetARPManager();
+
+ const Service::Glue::ARPManager& GetARPManager() const;
+
private:
System();
diff --git a/src/core/file_sys/patch_manager.cpp b/src/core/file_sys/patch_manager.cpp
index da823c37b..a8f80e2c6 100644
--- a/src/core/file_sys/patch_manager.cpp
+++ b/src/core/file_sys/patch_manager.cpp
@@ -493,6 +493,16 @@ std::map<std::string, std::string, std::less<>> PatchManager::GetPatchVersionNam
return out;
}
+std::optional<u32> PatchManager::GetGameVersion() const {
+ const auto& installed = Core::System::GetInstance().GetContentProvider();
+ const auto update_tid = GetUpdateTitleID(title_id);
+ if (installed.HasEntry(update_tid, ContentRecordType::Program)) {
+ return installed.GetEntryVersion(update_tid);
+ }
+
+ return installed.GetEntryVersion(title_id);
+}
+
std::pair<std::unique_ptr<NACP>, VirtualFile> PatchManager::GetControlMetadata() const {
const auto& installed = Core::System::GetInstance().GetContentProvider();
diff --git a/src/core/file_sys/patch_manager.h b/src/core/file_sys/patch_manager.h
index 769f8c6f0..a363c6577 100644
--- a/src/core/file_sys/patch_manager.h
+++ b/src/core/file_sys/patch_manager.h
@@ -66,8 +66,13 @@ public:
std::map<std::string, std::string, std::less<>> GetPatchVersionNames(
VirtualFile update_raw = nullptr) const;
- // Given title_id of the program, attempts to get the control data of the update and parse it,
- // falling back to the base control data.
+ // If the game update exists, returns the u32 version field in its Meta-type NCA. If that fails,
+ // it will fallback to the Meta-type NCA of the base game. If that fails, the result will be
+ // std::nullopt
+ std::optional<u32> GetGameVersion() const;
+
+ // Given title_id of the program, attempts to get the control data of the update and parse
+ // it, falling back to the base control data.
std::pair<std::unique_ptr<NACP>, VirtualFile> GetControlMetadata() const;
// Version of GetControlMetadata that takes an arbitrary NCA
diff --git a/src/core/file_sys/registered_cache.cpp b/src/core/file_sys/registered_cache.cpp
index 58917e094..4608490e0 100644
--- a/src/core/file_sys/registered_cache.cpp
+++ b/src/core/file_sys/registered_cache.cpp
@@ -645,6 +645,20 @@ ContentProviderUnion::ListEntriesFilterOrigin(std::optional<ContentProviderUnion
return out;
}
+std::optional<ContentProviderUnionSlot> ContentProviderUnion::GetSlotForEntry(
+ u64 title_id, ContentRecordType type) const {
+ const auto iter =
+ std::find_if(providers.begin(), providers.end(), [title_id, type](const auto& provider) {
+ return provider.second != nullptr && provider.second->HasEntry(title_id, type);
+ });
+
+ if (iter == providers.end()) {
+ return std::nullopt;
+ }
+
+ return iter->first;
+}
+
ManualContentProvider::~ManualContentProvider() = default;
void ManualContentProvider::AddEntry(TitleType title_type, ContentRecordType content_type,
diff --git a/src/core/file_sys/registered_cache.h b/src/core/file_sys/registered_cache.h
index ec9052653..4398d63e1 100644
--- a/src/core/file_sys/registered_cache.h
+++ b/src/core/file_sys/registered_cache.h
@@ -199,6 +199,9 @@ public:
std::optional<TitleType> title_type = {}, std::optional<ContentRecordType> record_type = {},
std::optional<u64> title_id = {}) const;
+ std::optional<ContentProviderUnionSlot> GetSlotForEntry(u64 title_id,
+ ContentRecordType type) const;
+
private:
std::map<ContentProviderUnionSlot, ContentProvider*> providers;
};
diff --git a/src/core/hle/service/arp/arp.cpp b/src/core/hle/service/arp/arp.cpp
deleted file mode 100644
index e675b0188..000000000
--- a/src/core/hle/service/arp/arp.cpp
+++ /dev/null
@@ -1,75 +0,0 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
-
-#include <memory>
-
-#include "common/logging/log.h"
-#include "core/hle/ipc_helpers.h"
-#include "core/hle/kernel/hle_ipc.h"
-#include "core/hle/service/arp/arp.h"
-#include "core/hle/service/service.h"
-#include "core/hle/service/sm/sm.h"
-
-namespace Service::ARP {
-
-class ARP_R final : public ServiceFramework<ARP_R> {
-public:
- explicit ARP_R() : ServiceFramework{"arp:r"} {
- // clang-format off
- static const FunctionInfo functions[] = {
- {0, nullptr, "GetApplicationLaunchProperty"},
- {1, nullptr, "GetApplicationLaunchPropertyWithApplicationId"},
- {2, nullptr, "GetApplicationControlProperty"},
- {3, nullptr, "GetApplicationControlPropertyWithApplicationId"},
- };
- // clang-format on
-
- RegisterHandlers(functions);
- }
-};
-
-class IRegistrar final : public ServiceFramework<IRegistrar> {
-public:
- explicit IRegistrar() : ServiceFramework{"IRegistrar"} {
- // clang-format off
- static const FunctionInfo functions[] = {
- {0, nullptr, "Issue"},
- {1, nullptr, "SetApplicationLaunchProperty"},
- {2, nullptr, "SetApplicationControlProperty"},
- };
- // clang-format on
-
- RegisterHandlers(functions);
- }
-};
-
-class ARP_W final : public ServiceFramework<ARP_W> {
-public:
- explicit ARP_W() : ServiceFramework{"arp:w"} {
- // clang-format off
- static const FunctionInfo functions[] = {
- {0, &ARP_W::AcquireRegistrar, "AcquireRegistrar"},
- {1, nullptr, "DeleteProperties"},
- };
- // clang-format on
-
- RegisterHandlers(functions);
- }
-
-private:
- void AcquireRegistrar(Kernel::HLERequestContext& ctx) {
- LOG_DEBUG(Service_ARP, "called");
-
- IPC::ResponseBuilder rb{ctx, 2, 0, 1};
- rb.Push(RESULT_SUCCESS);
- rb.PushIpcInterface<IRegistrar>();
- }
-};
-
-void InstallInterfaces(SM::ServiceManager& sm) {
- std::make_shared<ARP_R>()->InstallAsService(sm);
- std::make_shared<ARP_W>()->InstallAsService(sm);
-}
-
-} // namespace Service::ARP
diff --git a/src/core/hle/service/arp/arp.h b/src/core/hle/service/arp/arp.h
deleted file mode 100644
index 9d100187c..000000000
--- a/src/core/hle/service/arp/arp.h
+++ /dev/null
@@ -1,16 +0,0 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
-
-#pragma once
-
-namespace Service::SM {
-class ServiceManager;
-}
-
-namespace Service::ARP {
-
-/// Registers all ARP services with the specified service manager.
-void InstallInterfaces(SM::ServiceManager& sm);
-
-} // namespace Service::ARP
diff --git a/src/core/hle/service/glue/arp.cpp b/src/core/hle/service/glue/arp.cpp
new file mode 100644
index 000000000..b591ce31b
--- /dev/null
+++ b/src/core/hle/service/glue/arp.cpp
@@ -0,0 +1,297 @@
+// Copyright 2018 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include <memory>
+
+#include "common/logging/log.h"
+#include "core/file_sys/control_metadata.h"
+#include "core/hle/ipc_helpers.h"
+#include "core/hle/kernel/hle_ipc.h"
+#include "core/hle/kernel/kernel.h"
+#include "core/hle/kernel/process.h"
+#include "core/hle/service/glue/arp.h"
+#include "core/hle/service/glue/errors.h"
+#include "core/hle/service/glue/manager.h"
+#include "core/hle/service/service.h"
+
+namespace Service::Glue {
+
+namespace {
+std::optional<u64> GetTitleIDForProcessID(const Core::System& system, u64 process_id) {
+ const auto& list = system.Kernel().GetProcessList();
+ const auto iter = std::find_if(list.begin(), list.end(), [&process_id](const auto& process) {
+ return process->GetProcessID() == process_id;
+ });
+
+ if (iter == list.end()) {
+ return std::nullopt;
+ }
+
+ return (*iter)->GetTitleID();
+}
+} // Anonymous namespace
+
+ARP_R::ARP_R(const Core::System& system, const ARPManager& manager)
+ : ServiceFramework{"arp:r"}, system(system), manager(manager) {
+ // clang-format off
+ static const FunctionInfo functions[] = {
+ {0, &ARP_R::GetApplicationLaunchProperty, "GetApplicationLaunchProperty"},
+ {1, &ARP_R::GetApplicationLaunchPropertyWithApplicationId, "GetApplicationLaunchPropertyWithApplicationId"},
+ {2, &ARP_R::GetApplicationControlProperty, "GetApplicationControlProperty"},
+ {3, &ARP_R::GetApplicationControlPropertyWithApplicationId, "GetApplicationControlPropertyWithApplicationId"},
+ };
+ // clang-format on
+
+ RegisterHandlers(functions);
+}
+
+ARP_R::~ARP_R() = default;
+
+void ARP_R::GetApplicationLaunchProperty(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const auto process_id = rp.PopRaw<u64>();
+
+ LOG_DEBUG(Service_ARP, "called, process_id={:016X}", process_id);
+
+ const auto title_id = GetTitleIDForProcessID(system, process_id);
+ if (!title_id.has_value()) {
+ LOG_ERROR(Service_ARP, "Failed to get title ID for process ID!");
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ERR_NOT_REGISTERED);
+ return;
+ }
+
+ const auto res = manager.GetLaunchProperty(*title_id);
+
+ if (res.Failed()) {
+ LOG_ERROR(Service_ARP, "Failed to get launch property!");
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(res.Code());
+ return;
+ }
+
+ IPC::ResponseBuilder rb{ctx, 6};
+ rb.Push(RESULT_SUCCESS);
+ rb.PushRaw(*res);
+}
+
+void ARP_R::GetApplicationLaunchPropertyWithApplicationId(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const auto title_id = rp.PopRaw<u64>();
+
+ LOG_DEBUG(Service_ARP, "called, title_id={:016X}", title_id);
+
+ const auto res = manager.GetLaunchProperty(title_id);
+
+ if (res.Failed()) {
+ LOG_ERROR(Service_ARP, "Failed to get launch property!");
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(res.Code());
+ return;
+ }
+
+ IPC::ResponseBuilder rb{ctx, 6};
+ rb.Push(RESULT_SUCCESS);
+ rb.PushRaw(*res);
+}
+
+void ARP_R::GetApplicationControlProperty(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const auto process_id = rp.PopRaw<u64>();
+
+ LOG_DEBUG(Service_ARP, "called, process_id={:016X}", process_id);
+
+ const auto title_id = GetTitleIDForProcessID(system, process_id);
+ if (!title_id.has_value()) {
+ LOG_ERROR(Service_ARP, "Failed to get title ID for process ID!");
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ERR_NOT_REGISTERED);
+ return;
+ }
+
+ const auto res = manager.GetControlProperty(*title_id);
+
+ if (res.Failed()) {
+ LOG_ERROR(Service_ARP, "Failed to get control property!");
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(res.Code());
+ return;
+ }
+
+ ctx.WriteBuffer(*res);
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(RESULT_SUCCESS);
+}
+
+void ARP_R::GetApplicationControlPropertyWithApplicationId(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const auto title_id = rp.PopRaw<u64>();
+
+ LOG_DEBUG(Service_ARP, "called, title_id={:016X}", title_id);
+
+ const auto res = manager.GetControlProperty(title_id);
+
+ if (res.Failed()) {
+ LOG_ERROR(Service_ARP, "Failed to get control property!");
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(res.Code());
+ return;
+ }
+
+ ctx.WriteBuffer(*res);
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(RESULT_SUCCESS);
+}
+
+class IRegistrar final : public ServiceFramework<IRegistrar> {
+ friend class ARP_W;
+
+public:
+ explicit IRegistrar(
+ std::function<ResultCode(u64, ApplicationLaunchProperty, std::vector<u8>)> issuer)
+ : ServiceFramework{"IRegistrar"}, issue_process_id(std::move(issuer)) {
+ // clang-format off
+ static const FunctionInfo functions[] = {
+ {0, &IRegistrar::Issue, "Issue"},
+ {1, &IRegistrar::SetApplicationLaunchProperty, "SetApplicationLaunchProperty"},
+ {2, &IRegistrar::SetApplicationControlProperty, "SetApplicationControlProperty"},
+ };
+ // clang-format on
+
+ RegisterHandlers(functions);
+ }
+
+private:
+ void Issue(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const auto process_id = rp.PopRaw<u64>();
+
+ LOG_DEBUG(Service_ARP, "called, process_id={:016X}", process_id);
+
+ if (process_id == 0) {
+ LOG_ERROR(Service_ARP, "Must have non-zero process ID!");
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ERR_INVALID_PROCESS_ID);
+ return;
+ }
+
+ if (issued) {
+ LOG_ERROR(Service_ARP,
+ "Attempted to issue registrar, but registrar is already issued!");
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ERR_INVALID_ACCESS);
+ return;
+ }
+
+ issue_process_id(process_id, launch, std::move(control));
+ issued = true;
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(RESULT_SUCCESS);
+ }
+
+ void SetApplicationLaunchProperty(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_ARP, "called");
+
+ if (issued) {
+ LOG_ERROR(
+ Service_ARP,
+ "Attempted to set application launch property, but registrar is already issued!");
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ERR_INVALID_ACCESS);
+ return;
+ }
+
+ IPC::RequestParser rp{ctx};
+ launch = rp.PopRaw<ApplicationLaunchProperty>();
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(RESULT_SUCCESS);
+ }
+
+ void SetApplicationControlProperty(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_ARP, "called");
+
+ if (issued) {
+ LOG_ERROR(
+ Service_ARP,
+ "Attempted to set application control property, but registrar is already issued!");
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ERR_INVALID_ACCESS);
+ return;
+ }
+
+ control = ctx.ReadBuffer();
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(RESULT_SUCCESS);
+ }
+
+ std::function<ResultCode(u64, ApplicationLaunchProperty, std::vector<u8>)> issue_process_id;
+ bool issued = false;
+ ApplicationLaunchProperty launch;
+ std::vector<u8> control;
+};
+
+ARP_W::ARP_W(const Core::System& system, ARPManager& manager)
+ : ServiceFramework{"arp:w"}, system(system), manager(manager) {
+ // clang-format off
+ static const FunctionInfo functions[] = {
+ {0, &ARP_W::AcquireRegistrar, "AcquireRegistrar"},
+ {1, &ARP_W::DeleteProperties, "DeleteProperties"},
+ };
+ // clang-format on
+
+ RegisterHandlers(functions);
+}
+
+ARP_W::~ARP_W() = default;
+
+void ARP_W::AcquireRegistrar(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_ARP, "called");
+
+ registrar = std::make_shared<IRegistrar>(
+ [this](u64 process_id, ApplicationLaunchProperty launch, std::vector<u8> control) {
+ const auto res = GetTitleIDForProcessID(system, process_id);
+ if (!res.has_value()) {
+ return ERR_NOT_REGISTERED;
+ }
+
+ return manager.Register(*res, launch, std::move(control));
+ });
+
+ IPC::ResponseBuilder rb{ctx, 2, 0, 1};
+ rb.Push(RESULT_SUCCESS);
+ rb.PushIpcInterface(registrar);
+}
+
+void ARP_W::DeleteProperties(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const auto process_id = rp.PopRaw<u64>();
+
+ LOG_DEBUG(Service_ARP, "called, process_id={:016X}", process_id);
+
+ if (process_id == 0) {
+ LOG_ERROR(Service_ARP, "Must have non-zero process ID!");
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ERR_INVALID_PROCESS_ID);
+ return;
+ }
+
+ const auto title_id = GetTitleIDForProcessID(system, process_id);
+
+ if (!title_id.has_value()) {
+ LOG_ERROR(Service_ARP, "No title ID for process ID!");
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ERR_NOT_REGISTERED);
+ return;
+ }
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(manager.Unregister(*title_id));
+}
+
+} // namespace Service::Glue
diff --git a/src/core/hle/service/glue/arp.h b/src/core/hle/service/glue/arp.h
new file mode 100644
index 000000000..d5f8a7e7a
--- /dev/null
+++ b/src/core/hle/service/glue/arp.h
@@ -0,0 +1,43 @@
+// Copyright 2019 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "core/hle/service/service.h"
+
+namespace Service::Glue {
+
+class ARPManager;
+class IRegistrar;
+
+class ARP_R final : public ServiceFramework<ARP_R> {
+public:
+ explicit ARP_R(const Core::System& system, const ARPManager& manager);
+ ~ARP_R() override;
+
+private:
+ void GetApplicationLaunchProperty(Kernel::HLERequestContext& ctx);
+ void GetApplicationLaunchPropertyWithApplicationId(Kernel::HLERequestContext& ctx);
+ void GetApplicationControlProperty(Kernel::HLERequestContext& ctx);
+ void GetApplicationControlPropertyWithApplicationId(Kernel::HLERequestContext& ctx);
+
+ const Core::System& system;
+ const ARPManager& manager;
+};
+
+class ARP_W final : public ServiceFramework<ARP_W> {
+public:
+ explicit ARP_W(const Core::System& system, ARPManager& manager);
+ ~ARP_W() override;
+
+private:
+ void AcquireRegistrar(Kernel::HLERequestContext& ctx);
+ void DeleteProperties(Kernel::HLERequestContext& ctx);
+
+ const Core::System& system;
+ ARPManager& manager;
+ std::shared_ptr<IRegistrar> registrar;
+};
+
+} // namespace Service::Glue
diff --git a/src/core/hle/service/glue/bgtc.cpp b/src/core/hle/service/glue/bgtc.cpp
new file mode 100644
index 000000000..cd89d088f
--- /dev/null
+++ b/src/core/hle/service/glue/bgtc.cpp
@@ -0,0 +1,50 @@
+// Copyright 2019 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include "core/hle/service/glue/bgtc.h"
+
+namespace Service::Glue {
+
+BGTC_T::BGTC_T() : ServiceFramework{"bgtc:t"} {
+ // clang-format off
+ static const FunctionInfo functions[] = {
+ {1, nullptr, "NotifyTaskStarting"},
+ {2, nullptr, "NotifyTaskFinished"},
+ {3, nullptr, "GetTriggerEvent"},
+ {4, nullptr, "IsInHalfAwake"},
+ {5, nullptr, "NotifyClientName"},
+ {6, nullptr, "IsInFullAwake"},
+ {11, nullptr, "ScheduleTask"},
+ {12, nullptr, "GetScheduledTaskInterval"},
+ {13, nullptr, "UnscheduleTask"},
+ {14, nullptr, "GetScheduleEvent"},
+ {15, nullptr, "SchedulePeriodicTask"},
+ {101, nullptr, "GetOperationMode"},
+ {102, nullptr, "WillDisconnectNetworkWhenEnteringSleep"},
+ {103, nullptr, "WillStayHalfAwakeInsteadSleep"},
+ };
+ // clang-format on
+
+ RegisterHandlers(functions);
+}
+
+BGTC_T::~BGTC_T() = default;
+
+BGTC_SC::BGTC_SC() : ServiceFramework{"bgtc:sc"} {
+ // clang-format off
+ static const FunctionInfo functions[] = {
+ {1, nullptr, "GetState"},
+ {2, nullptr, "GetStateChangedEvent"},
+ {3, nullptr, "NotifyEnteringHalfAwake"},
+ {4, nullptr, "NotifyLeavingHalfAwake"},
+ {5, nullptr, "SetIsUsingSleepUnsupportedDevices"},
+ };
+ // clang-format on
+
+ RegisterHandlers(functions);
+}
+
+BGTC_SC::~BGTC_SC() = default;
+
+} // namespace Service::Glue
diff --git a/src/core/hle/service/glue/bgtc.h b/src/core/hle/service/glue/bgtc.h
new file mode 100644
index 000000000..81844f03e
--- /dev/null
+++ b/src/core/hle/service/glue/bgtc.h
@@ -0,0 +1,23 @@
+// Copyright 2019 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "core/hle/service/service.h"
+
+namespace Service::Glue {
+
+class BGTC_T final : public ServiceFramework<BGTC_T> {
+public:
+ BGTC_T();
+ ~BGTC_T() override;
+};
+
+class BGTC_SC final : public ServiceFramework<BGTC_SC> {
+public:
+ BGTC_SC();
+ ~BGTC_SC() override;
+};
+
+} // namespace Service::Glue
diff --git a/src/core/hle/service/glue/errors.h b/src/core/hle/service/glue/errors.h
new file mode 100644
index 000000000..c2874c585
--- /dev/null
+++ b/src/core/hle/service/glue/errors.h
@@ -0,0 +1,16 @@
+// Copyright 2019 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "core/hle/result.h"
+
+namespace Service::Glue {
+
+constexpr ResultCode ERR_INVALID_RESOURCE{ErrorModule::ARP, 0x1E};
+constexpr ResultCode ERR_INVALID_PROCESS_ID{ErrorModule::ARP, 0x1F};
+constexpr ResultCode ERR_INVALID_ACCESS{ErrorModule::ARP, 0x2A};
+constexpr ResultCode ERR_NOT_REGISTERED{ErrorModule::ARP, 0x66};
+
+} // namespace Service::Glue
diff --git a/src/core/hle/service/glue/glue.cpp b/src/core/hle/service/glue/glue.cpp
new file mode 100644
index 000000000..c728e815c
--- /dev/null
+++ b/src/core/hle/service/glue/glue.cpp
@@ -0,0 +1,25 @@
+// Copyright 2019 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include <memory>
+#include "core/core.h"
+#include "core/hle/service/glue/arp.h"
+#include "core/hle/service/glue/bgtc.h"
+#include "core/hle/service/glue/glue.h"
+
+namespace Service::Glue {
+
+void InstallInterfaces(Core::System& system) {
+ // ARP
+ std::make_shared<ARP_R>(system, system.GetARPManager())
+ ->InstallAsService(system.ServiceManager());
+ std::make_shared<ARP_W>(system, system.GetARPManager())
+ ->InstallAsService(system.ServiceManager());
+
+ // BackGround Task Controller
+ std::make_shared<BGTC_T>()->InstallAsService(system.ServiceManager());
+ std::make_shared<BGTC_SC>()->InstallAsService(system.ServiceManager());
+}
+
+} // namespace Service::Glue
diff --git a/src/core/hle/service/glue/glue.h b/src/core/hle/service/glue/glue.h
new file mode 100644
index 000000000..112cd238b
--- /dev/null
+++ b/src/core/hle/service/glue/glue.h
@@ -0,0 +1,16 @@
+// Copyright 2019 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+namespace Core {
+class System;
+} // namespace Core
+
+namespace Service::Glue {
+
+/// Registers all Glue services with the specified service manager.
+void InstallInterfaces(Core::System& system);
+
+} // namespace Service::Glue
diff --git a/src/core/hle/service/glue/manager.cpp b/src/core/hle/service/glue/manager.cpp
new file mode 100644
index 000000000..6da52d2d6
--- /dev/null
+++ b/src/core/hle/service/glue/manager.cpp
@@ -0,0 +1,78 @@
+// Copyright 2019 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include "core/hle/service/glue/errors.h"
+#include "core/hle/service/glue/manager.h"
+
+namespace Service::Glue {
+
+struct ARPManager::MapEntry {
+ ApplicationLaunchProperty launch;
+ std::vector<u8> control;
+};
+
+ARPManager::ARPManager() = default;
+
+ARPManager::~ARPManager() = default;
+
+ResultVal<ApplicationLaunchProperty> ARPManager::GetLaunchProperty(u64 title_id) const {
+ if (title_id == 0) {
+ return ERR_INVALID_PROCESS_ID;
+ }
+
+ const auto iter = entries.find(title_id);
+ if (iter == entries.end()) {
+ return ERR_NOT_REGISTERED;
+ }
+
+ return MakeResult<ApplicationLaunchProperty>(iter->second.launch);
+}
+
+ResultVal<std::vector<u8>> ARPManager::GetControlProperty(u64 title_id) const {
+ if (title_id == 0) {
+ return ERR_INVALID_PROCESS_ID;
+ }
+
+ const auto iter = entries.find(title_id);
+ if (iter == entries.end()) {
+ return ERR_NOT_REGISTERED;
+ }
+
+ return MakeResult<std::vector<u8>>(iter->second.control);
+}
+
+ResultCode ARPManager::Register(u64 title_id, ApplicationLaunchProperty launch,
+ std::vector<u8> control) {
+ if (title_id == 0) {
+ return ERR_INVALID_PROCESS_ID;
+ }
+
+ const auto iter = entries.find(title_id);
+ if (iter != entries.end()) {
+ return ERR_INVALID_ACCESS;
+ }
+
+ entries.insert_or_assign(title_id, MapEntry{launch, std::move(control)});
+ return RESULT_SUCCESS;
+}
+
+ResultCode ARPManager::Unregister(u64 title_id) {
+ if (title_id == 0) {
+ return ERR_INVALID_PROCESS_ID;
+ }
+
+ const auto iter = entries.find(title_id);
+ if (iter == entries.end()) {
+ return ERR_NOT_REGISTERED;
+ }
+
+ entries.erase(iter);
+ return RESULT_SUCCESS;
+}
+
+void ARPManager::ResetAll() {
+ entries.clear();
+}
+
+} // namespace Service::Glue
diff --git a/src/core/hle/service/glue/manager.h b/src/core/hle/service/glue/manager.h
new file mode 100644
index 000000000..a7f5ce3ee
--- /dev/null
+++ b/src/core/hle/service/glue/manager.h
@@ -0,0 +1,63 @@
+// Copyright 2019 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <map>
+#include <vector>
+#include "common/common_types.h"
+#include "core/file_sys/control_metadata.h"
+#include "core/file_sys/romfs_factory.h"
+#include "core/hle/result.h"
+
+namespace Service::Glue {
+
+struct ApplicationLaunchProperty {
+ u64 title_id;
+ u32 version;
+ FileSys::StorageId base_game_storage_id;
+ FileSys::StorageId update_storage_id;
+ u8 program_index;
+ u8 reserved;
+};
+static_assert(sizeof(ApplicationLaunchProperty) == 0x10,
+ "ApplicationLaunchProperty has incorrect size.");
+
+// A class to manage state related to the arp:w and arp:r services, specifically the registration
+// and unregistration of launch and control properties.
+class ARPManager {
+public:
+ ARPManager();
+ ~ARPManager();
+
+ // Returns the ApplicationLaunchProperty corresponding to the provided title ID if it was
+ // previously registered, otherwise ERR_NOT_REGISTERED if it was never registered or
+ // ERR_INVALID_PROCESS_ID if the title ID is 0.
+ ResultVal<ApplicationLaunchProperty> GetLaunchProperty(u64 title_id) const;
+
+ // Returns a vector of the raw bytes of NACP data (necessarily 0x4000 in size) corresponding to
+ // the provided title ID if it was previously registered, otherwise ERR_NOT_REGISTERED if it was
+ // never registered or ERR_INVALID_PROCESS_ID if the title ID is 0.
+ ResultVal<std::vector<u8>> GetControlProperty(u64 title_id) const;
+
+ // Adds a new entry to the internal database with the provided parameters, returning
+ // ERR_INVALID_ACCESS if attempting to re-register a title ID without an intermediate Unregister
+ // step, and ERR_INVALID_PROCESS_ID if the title ID is 0.
+ ResultCode Register(u64 title_id, ApplicationLaunchProperty launch, std::vector<u8> control);
+
+ // Removes the registration for the provided title ID from the database, returning
+ // ERR_NOT_REGISTERED if it doesn't exist in the database and ERR_INVALID_PROCESS_ID if the
+ // title ID is 0.
+ ResultCode Unregister(u64 title_id);
+
+ // Removes all entries from the database, always succeeds. Should only be used when resetting
+ // system state.
+ void ResetAll();
+
+private:
+ struct MapEntry;
+ std::map<u64, MapEntry> entries;
+};
+
+} // namespace Service::Glue
diff --git a/src/core/hle/service/service.cpp b/src/core/hle/service/service.cpp
index b2954eb34..beae9c510 100644
--- a/src/core/hle/service/service.cpp
+++ b/src/core/hle/service/service.cpp
@@ -19,7 +19,6 @@
#include "core/hle/service/am/am.h"
#include "core/hle/service/aoc/aoc_u.h"
#include "core/hle/service/apm/apm.h"
-#include "core/hle/service/arp/arp.h"
#include "core/hle/service/audio/audio.h"
#include "core/hle/service/bcat/module.h"
#include "core/hle/service/bpc/bpc.h"
@@ -33,6 +32,7 @@
#include "core/hle/service/fgm/fgm.h"
#include "core/hle/service/filesystem/filesystem.h"
#include "core/hle/service/friend/friend.h"
+#include "core/hle/service/glue/glue.h"
#include "core/hle/service/grc/grc.h"
#include "core/hle/service/hid/hid.h"
#include "core/hle/service/lbl/lbl.h"
@@ -207,7 +207,6 @@ void Init(std::shared_ptr<SM::ServiceManager>& sm, Core::System& system,
AM::InstallInterfaces(*sm, nv_flinger);
AOC::InstallInterfaces(*sm);
APM::InstallInterfaces(*sm);
- ARP::InstallInterfaces(*sm);
Audio::InstallInterfaces(*sm);
BCAT::InstallInterfaces(*sm);
BPC::InstallInterfaces(*sm);
@@ -221,6 +220,7 @@ void Init(std::shared_ptr<SM::ServiceManager>& sm, Core::System& system,
FGM::InstallInterfaces(*sm);
FileSystem::InstallInterfaces(*sm, vfs);
Friend::InstallInterfaces(*sm);
+ Glue::InstallInterfaces(system);
GRC::InstallInterfaces(*sm);
HID::InstallInterfaces(*sm);
LBL::InstallInterfaces(*sm);