summaryrefslogtreecommitdiffstats
path: root/src/core/hle/service
diff options
context:
space:
mode:
Diffstat (limited to 'src/core/hle/service')
-rw-r--r--src/core/hle/service/acc/acc.cpp57
-rw-r--r--src/core/hle/service/acc/acc.h9
-rw-r--r--src/core/hle/service/acc/acc_aa.cpp5
-rw-r--r--src/core/hle/service/acc/acc_aa.h4
-rw-r--r--src/core/hle/service/acc/acc_su.cpp5
-rw-r--r--src/core/hle/service/acc/acc_su.h4
-rw-r--r--src/core/hle/service/acc/acc_u0.cpp9
-rw-r--r--src/core/hle/service/acc/acc_u0.h4
-rw-r--r--src/core/hle/service/acc/acc_u1.cpp5
-rw-r--r--src/core/hle/service/acc/acc_u1.h4
-rw-r--r--src/core/hle/service/am/am.cpp30
-rw-r--r--src/core/hle/service/am/am.h9
-rw-r--r--src/core/hle/service/am/applet_ae.cpp26
-rw-r--r--src/core/hle/service/am/applet_ae.h3
-rw-r--r--src/core/hle/service/am/applet_oe.cpp14
-rw-r--r--src/core/hle/service/am/applet_oe.h3
-rw-r--r--src/core/hle/service/am/applets/applets.cpp77
-rw-r--r--src/core/hle/service/am/applets/applets.h30
-rw-r--r--src/core/hle/service/am/applets/error.cpp19
-rw-r--r--src/core/hle/service/am/applets/general_backend.cpp138
-rw-r--r--src/core/hle/service/am/applets/general_backend.h35
-rw-r--r--src/core/hle/service/am/applets/web_browser.cpp496
-rw-r--r--src/core/hle/service/am/applets/web_browser.h37
-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/audio/audout_u.cpp23
-rw-r--r--src/core/hle/service/fatal/fatal.cpp26
-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/ldr/ldr.cpp2
-rw-r--r--src/core/hle/service/prepo/prepo.cpp25
-rw-r--r--src/core/hle/service/service.cpp11
39 files changed, 1521 insertions, 291 deletions
diff --git a/src/core/hle/service/acc/acc.cpp b/src/core/hle/service/acc/acc.cpp
index cb66e344b..0cd8158df 100644
--- a/src/core/hle/service/acc/acc.cpp
+++ b/src/core/hle/service/acc/acc.cpp
@@ -12,13 +12,17 @@
#include "common/swap.h"
#include "core/constants.h"
#include "core/core_timing.h"
+#include "core/file_sys/control_metadata.h"
+#include "core/file_sys/patch_manager.h"
#include "core/hle/ipc_helpers.h"
+#include "core/hle/kernel/process.h"
#include "core/hle/service/acc/acc.h"
#include "core/hle/service/acc/acc_aa.h"
#include "core/hle/service/acc/acc_su.h"
#include "core/hle/service/acc/acc_u0.h"
#include "core/hle/service/acc/acc_u1.h"
#include "core/hle/service/acc/profile_manager.h"
+#include "core/loader/loader.h"
namespace Service::Account {
@@ -90,7 +94,7 @@ private:
LOG_WARNING(Service_ACC,
"Failed to load user provided image! Falling back to built-in backup...");
ctx.WriteBuffer(Core::Constants::ACCOUNT_BACKUP_JPEG);
- rb.Push<u32>(Core::Constants::ACCOUNT_BACKUP_JPEG.size());
+ rb.Push(SanitizeJPEGSize(Core::Constants::ACCOUNT_BACKUP_JPEG.size()));
return;
}
@@ -112,9 +116,9 @@ private:
if (!image.IsOpen()) {
LOG_WARNING(Service_ACC,
"Failed to load user provided image! Falling back to built-in backup...");
- rb.Push<u32>(Core::Constants::ACCOUNT_BACKUP_JPEG.size());
+ rb.Push(SanitizeJPEGSize(Core::Constants::ACCOUNT_BACKUP_JPEG.size()));
} else {
- rb.Push<u32>(SanitizeJPEGSize(image.GetSize()));
+ rb.Push(SanitizeJPEGSize(image.GetSize()));
}
}
@@ -213,7 +217,7 @@ void Module::Interface::IsUserRegistrationRequestPermitted(Kernel::HLERequestCon
rb.Push(profile_manager->CanSystemRegisterUser());
}
-void Module::Interface::InitializeApplicationInfo(Kernel::HLERequestContext& ctx) {
+void Module::Interface::InitializeApplicationInfoOld(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_ACC, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
@@ -226,6 +230,31 @@ void Module::Interface::GetBaasAccountManagerForApplication(Kernel::HLERequestCo
rb.PushIpcInterface<IManagerForApplication>();
}
+void Module::Interface::IsUserAccountSwitchLocked(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_ACC, "called");
+ FileSys::NACP nacp;
+ const auto res = system.GetAppLoader().ReadControlData(nacp);
+
+ bool is_locked = false;
+
+ if (res != Loader::ResultStatus::Success) {
+ FileSys::PatchManager pm{system.CurrentProcess()->GetTitleID()};
+ auto nacp_unique = pm.GetControlMetadata().first;
+
+ if (nacp_unique != nullptr) {
+ is_locked = nacp_unique->GetUserAccountSwitchLock();
+ } else {
+ LOG_ERROR(Service_ACC, "nacp_unique is null!");
+ }
+ } else {
+ is_locked = nacp.GetUserAccountSwitchLock();
+ }
+
+ IPC::ResponseBuilder rb{ctx, 3};
+ rb.Push(RESULT_SUCCESS);
+ rb.Push(is_locked);
+}
+
void Module::Interface::TrySelectUserWithoutInteraction(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_ACC, "called");
// A u8 is passed into this function which we can safely ignore. It's to determine if we have
@@ -251,19 +280,25 @@ void Module::Interface::TrySelectUserWithoutInteraction(Kernel::HLERequestContex
}
Module::Interface::Interface(std::shared_ptr<Module> module,
- std::shared_ptr<ProfileManager> profile_manager, const char* name)
+ std::shared_ptr<ProfileManager> profile_manager, Core::System& system,
+ const char* name)
: ServiceFramework(name), module(std::move(module)),
- profile_manager(std::move(profile_manager)) {}
+ profile_manager(std::move(profile_manager)), system(system) {}
Module::Interface::~Interface() = default;
-void InstallInterfaces(SM::ServiceManager& service_manager) {
+void InstallInterfaces(Core::System& system) {
auto module = std::make_shared<Module>();
auto profile_manager = std::make_shared<ProfileManager>();
- std::make_shared<ACC_AA>(module, profile_manager)->InstallAsService(service_manager);
- std::make_shared<ACC_SU>(module, profile_manager)->InstallAsService(service_manager);
- std::make_shared<ACC_U0>(module, profile_manager)->InstallAsService(service_manager);
- std::make_shared<ACC_U1>(module, profile_manager)->InstallAsService(service_manager);
+
+ std::make_shared<ACC_AA>(module, profile_manager, system)
+ ->InstallAsService(system.ServiceManager());
+ std::make_shared<ACC_SU>(module, profile_manager, system)
+ ->InstallAsService(system.ServiceManager());
+ std::make_shared<ACC_U0>(module, profile_manager, system)
+ ->InstallAsService(system.ServiceManager());
+ std::make_shared<ACC_U1>(module, profile_manager, system)
+ ->InstallAsService(system.ServiceManager());
}
} // namespace Service::Account
diff --git a/src/core/hle/service/acc/acc.h b/src/core/hle/service/acc/acc.h
index 89b2104fa..350f123a0 100644
--- a/src/core/hle/service/acc/acc.h
+++ b/src/core/hle/service/acc/acc.h
@@ -15,7 +15,8 @@ public:
class Interface : public ServiceFramework<Interface> {
public:
explicit Interface(std::shared_ptr<Module> module,
- std::shared_ptr<ProfileManager> profile_manager, const char* name);
+ std::shared_ptr<ProfileManager> profile_manager, Core::System& system,
+ const char* name);
~Interface() override;
void GetUserCount(Kernel::HLERequestContext& ctx);
@@ -24,18 +25,20 @@ public:
void ListOpenUsers(Kernel::HLERequestContext& ctx);
void GetLastOpenedUser(Kernel::HLERequestContext& ctx);
void GetProfile(Kernel::HLERequestContext& ctx);
- void InitializeApplicationInfo(Kernel::HLERequestContext& ctx);
+ void InitializeApplicationInfoOld(Kernel::HLERequestContext& ctx);
void GetBaasAccountManagerForApplication(Kernel::HLERequestContext& ctx);
void IsUserRegistrationRequestPermitted(Kernel::HLERequestContext& ctx);
void TrySelectUserWithoutInteraction(Kernel::HLERequestContext& ctx);
+ void IsUserAccountSwitchLocked(Kernel::HLERequestContext& ctx);
protected:
std::shared_ptr<Module> module;
std::shared_ptr<ProfileManager> profile_manager;
+ Core::System& system;
};
};
/// Registers all ACC services with the specified service manager.
-void InstallInterfaces(SM::ServiceManager& service_manager);
+void InstallInterfaces(Core::System& system);
} // namespace Service::Account
diff --git a/src/core/hle/service/acc/acc_aa.cpp b/src/core/hle/service/acc/acc_aa.cpp
index e84d9f7cf..3bac6bcd1 100644
--- a/src/core/hle/service/acc/acc_aa.cpp
+++ b/src/core/hle/service/acc/acc_aa.cpp
@@ -6,8 +6,9 @@
namespace Service::Account {
-ACC_AA::ACC_AA(std::shared_ptr<Module> module, std::shared_ptr<ProfileManager> profile_manager)
- : Module::Interface(std::move(module), std::move(profile_manager), "acc:aa") {
+ACC_AA::ACC_AA(std::shared_ptr<Module> module, std::shared_ptr<ProfileManager> profile_manager,
+ Core::System& system)
+ : Module::Interface(std::move(module), std::move(profile_manager), system, "acc:aa") {
static const FunctionInfo functions[] = {
{0, nullptr, "EnsureCacheAsync"},
{1, nullptr, "LoadCache"},
diff --git a/src/core/hle/service/acc/acc_aa.h b/src/core/hle/service/acc/acc_aa.h
index 9edb0421b..932c04890 100644
--- a/src/core/hle/service/acc/acc_aa.h
+++ b/src/core/hle/service/acc/acc_aa.h
@@ -10,8 +10,8 @@ namespace Service::Account {
class ACC_AA final : public Module::Interface {
public:
- explicit ACC_AA(std::shared_ptr<Module> module,
- std::shared_ptr<ProfileManager> profile_manager);
+ explicit ACC_AA(std::shared_ptr<Module> module, std::shared_ptr<ProfileManager> profile_manager,
+ Core::System& system);
~ACC_AA() override;
};
diff --git a/src/core/hle/service/acc/acc_su.cpp b/src/core/hle/service/acc/acc_su.cpp
index d66233cad..1b7ec3ed0 100644
--- a/src/core/hle/service/acc/acc_su.cpp
+++ b/src/core/hle/service/acc/acc_su.cpp
@@ -6,8 +6,9 @@
namespace Service::Account {
-ACC_SU::ACC_SU(std::shared_ptr<Module> module, std::shared_ptr<ProfileManager> profile_manager)
- : Module::Interface(std::move(module), std::move(profile_manager), "acc:su") {
+ACC_SU::ACC_SU(std::shared_ptr<Module> module, std::shared_ptr<ProfileManager> profile_manager,
+ Core::System& system)
+ : Module::Interface(std::move(module), std::move(profile_manager), system, "acc:su") {
// clang-format off
static const FunctionInfo functions[] = {
{0, &ACC_SU::GetUserCount, "GetUserCount"},
diff --git a/src/core/hle/service/acc/acc_su.h b/src/core/hle/service/acc/acc_su.h
index fcced063a..0a700d9bf 100644
--- a/src/core/hle/service/acc/acc_su.h
+++ b/src/core/hle/service/acc/acc_su.h
@@ -10,8 +10,8 @@ namespace Service::Account {
class ACC_SU final : public Module::Interface {
public:
- explicit ACC_SU(std::shared_ptr<Module> module,
- std::shared_ptr<ProfileManager> profile_manager);
+ explicit ACC_SU(std::shared_ptr<Module> module, std::shared_ptr<ProfileManager> profile_manager,
+ Core::System& system);
~ACC_SU() override;
};
diff --git a/src/core/hle/service/acc/acc_u0.cpp b/src/core/hle/service/acc/acc_u0.cpp
index 182f7c7e5..2f239e8c0 100644
--- a/src/core/hle/service/acc/acc_u0.cpp
+++ b/src/core/hle/service/acc/acc_u0.cpp
@@ -6,8 +6,9 @@
namespace Service::Account {
-ACC_U0::ACC_U0(std::shared_ptr<Module> module, std::shared_ptr<ProfileManager> profile_manager)
- : Module::Interface(std::move(module), std::move(profile_manager), "acc:u0") {
+ACC_U0::ACC_U0(std::shared_ptr<Module> module, std::shared_ptr<ProfileManager> profile_manager,
+ Core::System& system)
+ : Module::Interface(std::move(module), std::move(profile_manager), system, "acc:u0") {
// clang-format off
static const FunctionInfo functions[] = {
{0, &ACC_U0::GetUserCount, "GetUserCount"},
@@ -21,7 +22,7 @@ ACC_U0::ACC_U0(std::shared_ptr<Module> module, std::shared_ptr<ProfileManager> p
{51, &ACC_U0::TrySelectUserWithoutInteraction, "TrySelectUserWithoutInteraction"},
{60, nullptr, "ListOpenContextStoredUsers"},
{99, nullptr, "DebugActivateOpenContextRetention"},
- {100, &ACC_U0::InitializeApplicationInfo, "InitializeApplicationInfo"},
+ {100, &ACC_U0::InitializeApplicationInfoOld, "InitializeApplicationInfoOld"},
{101, &ACC_U0::GetBaasAccountManagerForApplication, "GetBaasAccountManagerForApplication"},
{102, nullptr, "AuthenticateApplicationAsync"},
{103, nullptr, "CheckNetworkServiceAvailabilityAsync"},
@@ -32,7 +33,7 @@ ACC_U0::ACC_U0(std::shared_ptr<Module> module, std::shared_ptr<ProfileManager> p
{131, nullptr, "ListOpenContextStoredUsers"},
{140, nullptr, "InitializeApplicationInfo"},
{141, nullptr, "ListQualifiedUsers"},
- {150, nullptr, "IsUserAccountSwitchLocked"},
+ {150, &ACC_U0::IsUserAccountSwitchLocked, "IsUserAccountSwitchLocked"},
};
// clang-format on
diff --git a/src/core/hle/service/acc/acc_u0.h b/src/core/hle/service/acc/acc_u0.h
index a1290e0bd..3bd9c3164 100644
--- a/src/core/hle/service/acc/acc_u0.h
+++ b/src/core/hle/service/acc/acc_u0.h
@@ -10,8 +10,8 @@ namespace Service::Account {
class ACC_U0 final : public Module::Interface {
public:
- explicit ACC_U0(std::shared_ptr<Module> module,
- std::shared_ptr<ProfileManager> profile_manager);
+ explicit ACC_U0(std::shared_ptr<Module> module, std::shared_ptr<ProfileManager> profile_manager,
+ Core::System& system);
~ACC_U0() override;
};
diff --git a/src/core/hle/service/acc/acc_u1.cpp b/src/core/hle/service/acc/acc_u1.cpp
index 2dd17d935..6520b3968 100644
--- a/src/core/hle/service/acc/acc_u1.cpp
+++ b/src/core/hle/service/acc/acc_u1.cpp
@@ -6,8 +6,9 @@
namespace Service::Account {
-ACC_U1::ACC_U1(std::shared_ptr<Module> module, std::shared_ptr<ProfileManager> profile_manager)
- : Module::Interface(std::move(module), std::move(profile_manager), "acc:u1") {
+ACC_U1::ACC_U1(std::shared_ptr<Module> module, std::shared_ptr<ProfileManager> profile_manager,
+ Core::System& system)
+ : Module::Interface(std::move(module), std::move(profile_manager), system, "acc:u1") {
// clang-format off
static const FunctionInfo functions[] = {
{0, &ACC_U1::GetUserCount, "GetUserCount"},
diff --git a/src/core/hle/service/acc/acc_u1.h b/src/core/hle/service/acc/acc_u1.h
index 9e79daee3..829f8a744 100644
--- a/src/core/hle/service/acc/acc_u1.h
+++ b/src/core/hle/service/acc/acc_u1.h
@@ -10,8 +10,8 @@ namespace Service::Account {
class ACC_U1 final : public Module::Interface {
public:
- explicit ACC_U1(std::shared_ptr<Module> module,
- std::shared_ptr<ProfileManager> profile_manager);
+ explicit ACC_U1(std::shared_ptr<Module> module, std::shared_ptr<ProfileManager> profile_manager,
+ Core::System& system);
~ACC_U1() override;
};
diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp
index 3f201c821..33cebb48b 100644
--- a/src/core/hle/service/am/am.cpp
+++ b/src/core/hle/service/am/am.cpp
@@ -271,7 +271,7 @@ ISelfController::ISelfController(std::shared_ptr<NVFlinger::NVFlinger> nvflinger
{71, nullptr, "GetCurrentIlluminanceEx"},
{80, nullptr, "SetWirelessPriorityMode"},
{90, nullptr, "GetAccumulatedSuspendedTickValue"},
- {91, nullptr, "GetAccumulatedSuspendedTickChangedEvent"},
+ {91, &ISelfController::GetAccumulatedSuspendedTickChangedEvent, "GetAccumulatedSuspendedTickChangedEvent"},
{100, nullptr, "SetAlbumImageTakenNotificationEnabled"},
{1000, nullptr, "GetDebugStorageChannel"},
};
@@ -282,6 +282,11 @@ ISelfController::ISelfController(std::shared_ptr<NVFlinger::NVFlinger> nvflinger
auto& kernel = Core::System::GetInstance().Kernel();
launchable_event = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Manual,
"ISelfController:LaunchableEvent");
+
+ // TODO(ogniK): Figure out where, when and why this event gets signalled
+ accumulated_suspended_tick_changed_event = Kernel::WritableEvent::CreateEventPair(
+ kernel, Kernel::ResetType::Manual, "ISelfController:AccumulatedSuspendedTickChangedEvent");
+ accumulated_suspended_tick_changed_event.writable->Signal(); // Is signalled on creation
}
ISelfController::~ISelfController() = default;
@@ -444,6 +449,17 @@ void ISelfController::GetIdleTimeDetectionExtension(Kernel::HLERequestContext& c
rb.Push<u32>(idle_time_detection_extension);
}
+void ISelfController::GetAccumulatedSuspendedTickChangedEvent(Kernel::HLERequestContext& ctx) {
+ // The implementation of this function is fine as is, the reason we're labelling it as stubbed
+ // is because we're currently unsure when and where accumulated_suspended_tick_changed_event is
+ // actually signalled for the time being.
+ LOG_WARNING(Service_AM, "(STUBBED) called");
+
+ IPC::ResponseBuilder rb{ctx, 2, 1};
+ rb.Push(RESULT_SUCCESS);
+ rb.PushCopyObjects(accumulated_suspended_tick_changed_event.readable);
+}
+
AppletMessageQueue::AppletMessageQueue() {
auto& kernel = Core::System::GetInstance().Kernel();
on_new_message = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Manual,
@@ -871,7 +887,9 @@ void IStorageAccessor::Read(Kernel::HLERequestContext& ctx) {
rb.Push(RESULT_SUCCESS);
}
-ILibraryAppletCreator::ILibraryAppletCreator() : ServiceFramework("ILibraryAppletCreator") {
+ILibraryAppletCreator::ILibraryAppletCreator(u64 current_process_title_id)
+ : ServiceFramework("ILibraryAppletCreator"),
+ current_process_title_id(current_process_title_id) {
static const FunctionInfo functions[] = {
{0, &ILibraryAppletCreator::CreateLibraryApplet, "CreateLibraryApplet"},
{1, nullptr, "TerminateAllLibraryApplets"},
@@ -894,7 +912,7 @@ void ILibraryAppletCreator::CreateLibraryApplet(Kernel::HLERequestContext& ctx)
static_cast<u32>(applet_id), applet_mode);
const auto& applet_manager{Core::System::GetInstance().GetAppletManager()};
- const auto applet = applet_manager.GetApplet(applet_id);
+ const auto applet = applet_manager.GetApplet(applet_id, current_process_title_id);
if (applet == nullptr) {
LOG_ERROR(Service_AM, "Applet doesn't exist! applet_id={}", static_cast<u32>(applet_id));
@@ -1218,13 +1236,13 @@ void IApplicationFunctions::GetSaveDataSize(Kernel::HLERequestContext& ctx) {
}
void InstallInterfaces(SM::ServiceManager& service_manager,
- std::shared_ptr<NVFlinger::NVFlinger> nvflinger) {
+ std::shared_ptr<NVFlinger::NVFlinger> nvflinger, Core::System& system) {
auto message_queue = std::make_shared<AppletMessageQueue>();
message_queue->PushMessage(AppletMessageQueue::AppletMessage::FocusStateChanged); // Needed on
// game boot
- std::make_shared<AppletAE>(nvflinger, message_queue)->InstallAsService(service_manager);
- std::make_shared<AppletOE>(nvflinger, message_queue)->InstallAsService(service_manager);
+ std::make_shared<AppletAE>(nvflinger, message_queue, system)->InstallAsService(service_manager);
+ std::make_shared<AppletOE>(nvflinger, message_queue, system)->InstallAsService(service_manager);
std::make_shared<IdleSys>()->InstallAsService(service_manager);
std::make_shared<OMM>()->InstallAsService(service_manager);
std::make_shared<SPSM>()->InstallAsService(service_manager);
diff --git a/src/core/hle/service/am/am.h b/src/core/hle/service/am/am.h
index 991b7d47c..4ea609d23 100644
--- a/src/core/hle/service/am/am.h
+++ b/src/core/hle/service/am/am.h
@@ -133,9 +133,12 @@ private:
void SetHandlesRequestToDisplay(Kernel::HLERequestContext& ctx);
void SetIdleTimeDetectionExtension(Kernel::HLERequestContext& ctx);
void GetIdleTimeDetectionExtension(Kernel::HLERequestContext& ctx);
+ void GetAccumulatedSuspendedTickChangedEvent(Kernel::HLERequestContext& ctx);
std::shared_ptr<NVFlinger::NVFlinger> nvflinger;
Kernel::EventPair launchable_event;
+ Kernel::EventPair accumulated_suspended_tick_changed_event;
+
u32 idle_time_detection_extension = 0;
u64 num_fatal_sections_entered = 0;
};
@@ -198,13 +201,15 @@ private:
class ILibraryAppletCreator final : public ServiceFramework<ILibraryAppletCreator> {
public:
- ILibraryAppletCreator();
+ ILibraryAppletCreator(u64 current_process_title_id);
~ILibraryAppletCreator() override;
private:
void CreateLibraryApplet(Kernel::HLERequestContext& ctx);
void CreateStorage(Kernel::HLERequestContext& ctx);
void CreateTransferMemoryStorage(Kernel::HLERequestContext& ctx);
+
+ u64 current_process_title_id;
};
class IApplicationFunctions final : public ServiceFramework<IApplicationFunctions> {
@@ -261,7 +266,7 @@ public:
/// Registers all AM services with the specified service manager.
void InstallInterfaces(SM::ServiceManager& service_manager,
- std::shared_ptr<NVFlinger::NVFlinger> nvflinger);
+ std::shared_ptr<NVFlinger::NVFlinger> nvflinger, Core::System& system);
} // namespace AM
} // namespace Service
diff --git a/src/core/hle/service/am/applet_ae.cpp b/src/core/hle/service/am/applet_ae.cpp
index 488add8e7..fe5beb8f9 100644
--- a/src/core/hle/service/am/applet_ae.cpp
+++ b/src/core/hle/service/am/applet_ae.cpp
@@ -4,6 +4,7 @@
#include "common/logging/log.h"
#include "core/hle/ipc_helpers.h"
+#include "core/hle/kernel/process.h"
#include "core/hle/service/am/am.h"
#include "core/hle/service/am/applet_ae.h"
#include "core/hle/service/nvflinger/nvflinger.h"
@@ -13,9 +14,10 @@ namespace Service::AM {
class ILibraryAppletProxy final : public ServiceFramework<ILibraryAppletProxy> {
public:
explicit ILibraryAppletProxy(std::shared_ptr<NVFlinger::NVFlinger> nvflinger,
- std::shared_ptr<AppletMessageQueue> msg_queue)
+ std::shared_ptr<AppletMessageQueue> msg_queue,
+ Core::System& system)
: ServiceFramework("ILibraryAppletProxy"), nvflinger(std::move(nvflinger)),
- msg_queue(std::move(msg_queue)) {
+ msg_queue(std::move(msg_queue)), system(system) {
// clang-format off
static const FunctionInfo functions[] = {
{0, &ILibraryAppletProxy::GetCommonStateGetter, "GetCommonStateGetter"},
@@ -96,7 +98,7 @@ private:
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
- rb.PushIpcInterface<ILibraryAppletCreator>();
+ rb.PushIpcInterface<ILibraryAppletCreator>(system.CurrentProcess()->GetTitleID());
}
void GetApplicationFunctions(Kernel::HLERequestContext& ctx) {
@@ -109,14 +111,15 @@ private:
std::shared_ptr<NVFlinger::NVFlinger> nvflinger;
std::shared_ptr<AppletMessageQueue> msg_queue;
+ Core::System& system;
};
class ISystemAppletProxy final : public ServiceFramework<ISystemAppletProxy> {
public:
explicit ISystemAppletProxy(std::shared_ptr<NVFlinger::NVFlinger> nvflinger,
- std::shared_ptr<AppletMessageQueue> msg_queue)
+ std::shared_ptr<AppletMessageQueue> msg_queue, Core::System& system)
: ServiceFramework("ISystemAppletProxy"), nvflinger(std::move(nvflinger)),
- msg_queue(std::move(msg_queue)) {
+ msg_queue(std::move(msg_queue)), system(system) {
// clang-format off
static const FunctionInfo functions[] = {
{0, &ISystemAppletProxy::GetCommonStateGetter, "GetCommonStateGetter"},
@@ -191,7 +194,7 @@ private:
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
- rb.PushIpcInterface<ILibraryAppletCreator>();
+ rb.PushIpcInterface<ILibraryAppletCreator>(system.CurrentProcess()->GetTitleID());
}
void GetHomeMenuFunctions(Kernel::HLERequestContext& ctx) {
@@ -219,6 +222,7 @@ private:
}
std::shared_ptr<NVFlinger::NVFlinger> nvflinger;
std::shared_ptr<AppletMessageQueue> msg_queue;
+ Core::System& system;
};
void AppletAE::OpenSystemAppletProxy(Kernel::HLERequestContext& ctx) {
@@ -226,7 +230,7 @@ void AppletAE::OpenSystemAppletProxy(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
- rb.PushIpcInterface<ISystemAppletProxy>(nvflinger, msg_queue);
+ rb.PushIpcInterface<ISystemAppletProxy>(nvflinger, msg_queue, system);
}
void AppletAE::OpenLibraryAppletProxy(Kernel::HLERequestContext& ctx) {
@@ -234,7 +238,7 @@ void AppletAE::OpenLibraryAppletProxy(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
- rb.PushIpcInterface<ILibraryAppletProxy>(nvflinger, msg_queue);
+ rb.PushIpcInterface<ILibraryAppletProxy>(nvflinger, msg_queue, system);
}
void AppletAE::OpenLibraryAppletProxyOld(Kernel::HLERequestContext& ctx) {
@@ -242,13 +246,13 @@ void AppletAE::OpenLibraryAppletProxyOld(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
- rb.PushIpcInterface<ILibraryAppletProxy>(nvflinger, msg_queue);
+ rb.PushIpcInterface<ILibraryAppletProxy>(nvflinger, msg_queue, system);
}
AppletAE::AppletAE(std::shared_ptr<NVFlinger::NVFlinger> nvflinger,
- std::shared_ptr<AppletMessageQueue> msg_queue)
+ std::shared_ptr<AppletMessageQueue> msg_queue, Core::System& system)
: ServiceFramework("appletAE"), nvflinger(std::move(nvflinger)),
- msg_queue(std::move(msg_queue)) {
+ msg_queue(std::move(msg_queue)), system(system) {
// clang-format off
static const FunctionInfo functions[] = {
{100, &AppletAE::OpenSystemAppletProxy, "OpenSystemAppletProxy"},
diff --git a/src/core/hle/service/am/applet_ae.h b/src/core/hle/service/am/applet_ae.h
index 902db2665..9e006cd9d 100644
--- a/src/core/hle/service/am/applet_ae.h
+++ b/src/core/hle/service/am/applet_ae.h
@@ -18,7 +18,7 @@ namespace AM {
class AppletAE final : public ServiceFramework<AppletAE> {
public:
explicit AppletAE(std::shared_ptr<NVFlinger::NVFlinger> nvflinger,
- std::shared_ptr<AppletMessageQueue> msg_queue);
+ std::shared_ptr<AppletMessageQueue> msg_queue, Core::System& system);
~AppletAE() override;
const std::shared_ptr<AppletMessageQueue>& GetMessageQueue() const;
@@ -30,6 +30,7 @@ private:
std::shared_ptr<NVFlinger::NVFlinger> nvflinger;
std::shared_ptr<AppletMessageQueue> msg_queue;
+ Core::System& system;
};
} // namespace AM
diff --git a/src/core/hle/service/am/applet_oe.cpp b/src/core/hle/service/am/applet_oe.cpp
index d3a0a1568..6e255fe95 100644
--- a/src/core/hle/service/am/applet_oe.cpp
+++ b/src/core/hle/service/am/applet_oe.cpp
@@ -4,6 +4,7 @@
#include "common/logging/log.h"
#include "core/hle/ipc_helpers.h"
+#include "core/hle/kernel/process.h"
#include "core/hle/service/am/am.h"
#include "core/hle/service/am/applet_oe.h"
#include "core/hle/service/nvflinger/nvflinger.h"
@@ -13,9 +14,9 @@ namespace Service::AM {
class IApplicationProxy final : public ServiceFramework<IApplicationProxy> {
public:
explicit IApplicationProxy(std::shared_ptr<NVFlinger::NVFlinger> nvflinger,
- std::shared_ptr<AppletMessageQueue> msg_queue)
+ std::shared_ptr<AppletMessageQueue> msg_queue, Core::System& system)
: ServiceFramework("IApplicationProxy"), nvflinger(std::move(nvflinger)),
- msg_queue(std::move(msg_queue)) {
+ msg_queue(std::move(msg_queue)), system(system) {
// clang-format off
static const FunctionInfo functions[] = {
{0, &IApplicationProxy::GetCommonStateGetter, "GetCommonStateGetter"},
@@ -87,7 +88,7 @@ private:
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
- rb.PushIpcInterface<ILibraryAppletCreator>();
+ rb.PushIpcInterface<ILibraryAppletCreator>(system.CurrentProcess()->GetTitleID());
}
void GetApplicationFunctions(Kernel::HLERequestContext& ctx) {
@@ -100,6 +101,7 @@ private:
std::shared_ptr<NVFlinger::NVFlinger> nvflinger;
std::shared_ptr<AppletMessageQueue> msg_queue;
+ Core::System& system;
};
void AppletOE::OpenApplicationProxy(Kernel::HLERequestContext& ctx) {
@@ -107,13 +109,13 @@ void AppletOE::OpenApplicationProxy(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
- rb.PushIpcInterface<IApplicationProxy>(nvflinger, msg_queue);
+ rb.PushIpcInterface<IApplicationProxy>(nvflinger, msg_queue, system);
}
AppletOE::AppletOE(std::shared_ptr<NVFlinger::NVFlinger> nvflinger,
- std::shared_ptr<AppletMessageQueue> msg_queue)
+ std::shared_ptr<AppletMessageQueue> msg_queue, Core::System& system)
: ServiceFramework("appletOE"), nvflinger(std::move(nvflinger)),
- msg_queue(std::move(msg_queue)) {
+ msg_queue(std::move(msg_queue)), system(system) {
static const FunctionInfo functions[] = {
{0, &AppletOE::OpenApplicationProxy, "OpenApplicationProxy"},
};
diff --git a/src/core/hle/service/am/applet_oe.h b/src/core/hle/service/am/applet_oe.h
index bbd0108ef..22c05419d 100644
--- a/src/core/hle/service/am/applet_oe.h
+++ b/src/core/hle/service/am/applet_oe.h
@@ -18,7 +18,7 @@ namespace AM {
class AppletOE final : public ServiceFramework<AppletOE> {
public:
explicit AppletOE(std::shared_ptr<NVFlinger::NVFlinger> nvflinger,
- std::shared_ptr<AppletMessageQueue> msg_queue);
+ std::shared_ptr<AppletMessageQueue> msg_queue, Core::System& system);
~AppletOE() override;
const std::shared_ptr<AppletMessageQueue>& GetMessageQueue() const;
@@ -28,6 +28,7 @@ private:
std::shared_ptr<NVFlinger::NVFlinger> nvflinger;
std::shared_ptr<AppletMessageQueue> msg_queue;
+ Core::System& system;
};
} // namespace AM
diff --git a/src/core/hle/service/am/applets/applets.cpp b/src/core/hle/service/am/applets/applets.cpp
index 14fa92318..6bdba2468 100644
--- a/src/core/hle/service/am/applets/applets.cpp
+++ b/src/core/hle/service/am/applets/applets.cpp
@@ -35,12 +35,28 @@ AppletDataBroker::AppletDataBroker() {
AppletDataBroker::~AppletDataBroker() = default;
+AppletDataBroker::RawChannelData AppletDataBroker::PeekDataToAppletForDebug() const {
+ std::vector<std::vector<u8>> out_normal;
+
+ for (const auto& storage : in_channel) {
+ out_normal.push_back(storage->GetData());
+ }
+
+ std::vector<std::vector<u8>> out_interactive;
+
+ for (const auto& storage : in_interactive_channel) {
+ out_interactive.push_back(storage->GetData());
+ }
+
+ return {std::move(out_normal), std::move(out_interactive)};
+}
+
std::unique_ptr<IStorage> AppletDataBroker::PopNormalDataToGame() {
if (out_channel.empty())
return nullptr;
auto out = std::move(out_channel.front());
- out_channel.pop();
+ out_channel.pop_front();
return out;
}
@@ -49,7 +65,7 @@ std::unique_ptr<IStorage> AppletDataBroker::PopNormalDataToApplet() {
return nullptr;
auto out = std::move(in_channel.front());
- in_channel.pop();
+ in_channel.pop_front();
return out;
}
@@ -58,7 +74,7 @@ std::unique_ptr<IStorage> AppletDataBroker::PopInteractiveDataToGame() {
return nullptr;
auto out = std::move(out_interactive_channel.front());
- out_interactive_channel.pop();
+ out_interactive_channel.pop_front();
return out;
}
@@ -67,25 +83,25 @@ std::unique_ptr<IStorage> AppletDataBroker::PopInteractiveDataToApplet() {
return nullptr;
auto out = std::move(in_interactive_channel.front());
- in_interactive_channel.pop();
+ in_interactive_channel.pop_front();
return out;
}
void AppletDataBroker::PushNormalDataFromGame(IStorage storage) {
- in_channel.push(std::make_unique<IStorage>(storage));
+ in_channel.push_back(std::make_unique<IStorage>(storage));
}
void AppletDataBroker::PushNormalDataFromApplet(IStorage storage) {
- out_channel.push(std::make_unique<IStorage>(storage));
+ out_channel.push_back(std::make_unique<IStorage>(storage));
pop_out_data_event.writable->Signal();
}
void AppletDataBroker::PushInteractiveDataFromGame(IStorage storage) {
- in_interactive_channel.push(std::make_unique<IStorage>(storage));
+ in_interactive_channel.push_back(std::make_unique<IStorage>(storage));
}
void AppletDataBroker::PushInteractiveDataFromApplet(IStorage storage) {
- out_interactive_channel.push(std::make_unique<IStorage>(storage));
+ out_interactive_channel.push_back(std::make_unique<IStorage>(storage));
pop_interactive_out_data_event.writable->Signal();
}
@@ -123,12 +139,14 @@ void Applet::Initialize() {
AppletFrontendSet::AppletFrontendSet() = default;
-AppletFrontendSet::AppletFrontendSet(ErrorApplet error, PhotoViewer photo_viewer,
- ProfileSelect profile_select,
- SoftwareKeyboard software_keyboard, WebBrowser web_browser)
- : error{std::move(error)}, photo_viewer{std::move(photo_viewer)}, profile_select{std::move(
- profile_select)},
- software_keyboard{std::move(software_keyboard)}, web_browser{std::move(web_browser)} {}
+AppletFrontendSet::AppletFrontendSet(ParentalControlsApplet parental_controls, ErrorApplet error,
+ PhotoViewer photo_viewer, ProfileSelect profile_select,
+ SoftwareKeyboard software_keyboard, WebBrowser web_browser,
+ ECommerceApplet e_commerce)
+ : parental_controls{std::move(parental_controls)}, error{std::move(error)},
+ photo_viewer{std::move(photo_viewer)}, profile_select{std::move(profile_select)},
+ software_keyboard{std::move(software_keyboard)}, web_browser{std::move(web_browser)},
+ e_commerce{std::move(e_commerce)} {}
AppletFrontendSet::~AppletFrontendSet() = default;
@@ -141,6 +159,8 @@ AppletManager::AppletManager() = default;
AppletManager::~AppletManager() = default;
void AppletManager::SetAppletFrontendSet(AppletFrontendSet set) {
+ if (set.parental_controls != nullptr)
+ frontend.parental_controls = std::move(set.parental_controls);
if (set.error != nullptr)
frontend.error = std::move(set.error);
if (set.photo_viewer != nullptr)
@@ -151,17 +171,21 @@ void AppletManager::SetAppletFrontendSet(AppletFrontendSet set) {
frontend.software_keyboard = std::move(set.software_keyboard);
if (set.web_browser != nullptr)
frontend.web_browser = std::move(set.web_browser);
+ if (set.e_commerce != nullptr)
+ frontend.e_commerce = std::move(set.e_commerce);
}
void AppletManager::SetDefaultAppletFrontendSet() {
- frontend.error = std::make_unique<Core::Frontend::DefaultErrorApplet>();
- frontend.photo_viewer = std::make_unique<Core::Frontend::DefaultPhotoViewerApplet>();
- frontend.profile_select = std::make_unique<Core::Frontend::DefaultProfileSelectApplet>();
- frontend.software_keyboard = std::make_unique<Core::Frontend::DefaultSoftwareKeyboardApplet>();
- frontend.web_browser = std::make_unique<Core::Frontend::DefaultWebBrowserApplet>();
+ ClearAll();
+ SetDefaultAppletsIfMissing();
}
void AppletManager::SetDefaultAppletsIfMissing() {
+ if (frontend.parental_controls == nullptr) {
+ frontend.parental_controls =
+ std::make_unique<Core::Frontend::DefaultParentalControlsApplet>();
+ }
+
if (frontend.error == nullptr) {
frontend.error = std::make_unique<Core::Frontend::DefaultErrorApplet>();
}
@@ -182,14 +206,20 @@ void AppletManager::SetDefaultAppletsIfMissing() {
if (frontend.web_browser == nullptr) {
frontend.web_browser = std::make_unique<Core::Frontend::DefaultWebBrowserApplet>();
}
+
+ if (frontend.e_commerce == nullptr) {
+ frontend.e_commerce = std::make_unique<Core::Frontend::DefaultECommerceApplet>();
+ }
}
void AppletManager::ClearAll() {
frontend = {};
}
-std::shared_ptr<Applet> AppletManager::GetApplet(AppletId id) const {
+std::shared_ptr<Applet> AppletManager::GetApplet(AppletId id, u64 current_process_title_id) const {
switch (id) {
+ case AppletId::Auth:
+ return std::make_shared<Auth>(*frontend.parental_controls);
case AppletId::Error:
return std::make_shared<Error>(*frontend.error);
case AppletId::ProfileSelect:
@@ -198,13 +228,16 @@ std::shared_ptr<Applet> AppletManager::GetApplet(AppletId id) const {
return std::make_shared<SoftwareKeyboard>(*frontend.software_keyboard);
case AppletId::PhotoViewer:
return std::make_shared<PhotoViewer>(*frontend.photo_viewer);
+ case AppletId::LibAppletShop:
+ return std::make_shared<WebBrowser>(*frontend.web_browser, current_process_title_id,
+ frontend.e_commerce.get());
case AppletId::LibAppletOff:
- return std::make_shared<WebBrowser>(*frontend.web_browser);
+ return std::make_shared<WebBrowser>(*frontend.web_browser, current_process_title_id);
default:
UNIMPLEMENTED_MSG(
"No backend implementation exists for applet_id={:02X}! Falling back to stub applet.",
static_cast<u8>(id));
- return std::make_shared<StubApplet>();
+ return std::make_shared<StubApplet>(id);
}
}
diff --git a/src/core/hle/service/am/applets/applets.h b/src/core/hle/service/am/applets/applets.h
index b46e10a4a..adc973dad 100644
--- a/src/core/hle/service/am/applets/applets.h
+++ b/src/core/hle/service/am/applets/applets.h
@@ -13,7 +13,9 @@
union ResultCode;
namespace Core::Frontend {
+class ECommerceApplet;
class ErrorApplet;
+class ParentalControlsApplet;
class PhotoViewerApplet;
class ProfileSelectApplet;
class SoftwareKeyboardApplet;
@@ -54,6 +56,14 @@ public:
AppletDataBroker();
~AppletDataBroker();
+ struct RawChannelData {
+ std::vector<std::vector<u8>> normal;
+ std::vector<std::vector<u8>> interactive;
+ };
+
+ // Retrieves but does not pop the data sent to applet.
+ RawChannelData PeekDataToAppletForDebug() const;
+
std::unique_ptr<IStorage> PopNormalDataToGame();
std::unique_ptr<IStorage> PopNormalDataToApplet();
@@ -76,16 +86,16 @@ private:
// Queues are named from applet's perspective
// PopNormalDataToApplet and PushNormalDataFromGame
- std::queue<std::unique_ptr<IStorage>> in_channel;
+ std::deque<std::unique_ptr<IStorage>> in_channel;
// PopNormalDataToGame and PushNormalDataFromApplet
- std::queue<std::unique_ptr<IStorage>> out_channel;
+ std::deque<std::unique_ptr<IStorage>> out_channel;
// PopInteractiveDataToApplet and PushInteractiveDataFromGame
- std::queue<std::unique_ptr<IStorage>> in_interactive_channel;
+ std::deque<std::unique_ptr<IStorage>> in_interactive_channel;
// PopInteractiveDataToGame and PushInteractiveDataFromApplet
- std::queue<std::unique_ptr<IStorage>> out_interactive_channel;
+ std::deque<std::unique_ptr<IStorage>> out_interactive_channel;
Kernel::EventPair state_changed_event;
@@ -137,15 +147,19 @@ protected:
};
struct AppletFrontendSet {
+ using ParentalControlsApplet = std::unique_ptr<Core::Frontend::ParentalControlsApplet>;
using ErrorApplet = std::unique_ptr<Core::Frontend::ErrorApplet>;
using PhotoViewer = std::unique_ptr<Core::Frontend::PhotoViewerApplet>;
using ProfileSelect = std::unique_ptr<Core::Frontend::ProfileSelectApplet>;
using SoftwareKeyboard = std::unique_ptr<Core::Frontend::SoftwareKeyboardApplet>;
using WebBrowser = std::unique_ptr<Core::Frontend::WebBrowserApplet>;
+ using ECommerceApplet = std::unique_ptr<Core::Frontend::ECommerceApplet>;
AppletFrontendSet();
- AppletFrontendSet(ErrorApplet error, PhotoViewer photo_viewer, ProfileSelect profile_select,
- SoftwareKeyboard software_keyboard, WebBrowser web_browser);
+ AppletFrontendSet(ParentalControlsApplet parental_controls, ErrorApplet error,
+ PhotoViewer photo_viewer, ProfileSelect profile_select,
+ SoftwareKeyboard software_keyboard, WebBrowser web_browser,
+ ECommerceApplet e_commerce);
~AppletFrontendSet();
AppletFrontendSet(const AppletFrontendSet&) = delete;
@@ -154,11 +168,13 @@ struct AppletFrontendSet {
AppletFrontendSet(AppletFrontendSet&&) noexcept;
AppletFrontendSet& operator=(AppletFrontendSet&&) noexcept;
+ ParentalControlsApplet parental_controls;
ErrorApplet error;
PhotoViewer photo_viewer;
ProfileSelect profile_select;
SoftwareKeyboard software_keyboard;
WebBrowser web_browser;
+ ECommerceApplet e_commerce;
};
class AppletManager {
@@ -171,7 +187,7 @@ public:
void SetDefaultAppletsIfMissing();
void ClearAll();
- std::shared_ptr<Applet> GetApplet(AppletId id) const;
+ std::shared_ptr<Applet> GetApplet(AppletId id, u64 current_process_title_id) const;
private:
AppletFrontendSet frontend;
diff --git a/src/core/hle/service/am/applets/error.cpp b/src/core/hle/service/am/applets/error.cpp
index 04774bedc..af3a900f8 100644
--- a/src/core/hle/service/am/applets/error.cpp
+++ b/src/core/hle/service/am/applets/error.cpp
@@ -9,8 +9,10 @@
#include "common/string_util.h"
#include "core/core.h"
#include "core/frontend/applets/error.h"
+#include "core/hle/kernel/process.h"
#include "core/hle/service/am/am.h"
#include "core/hle/service/am/applets/error.h"
+#include "core/reporter.h"
namespace Service::AM::Applets {
@@ -143,9 +145,12 @@ void Error::Execute() {
}
const auto callback = [this] { DisplayCompleted(); };
+ const auto title_id = Core::CurrentProcess()->GetTitleID();
+ const auto& reporter{Core::System::GetInstance().GetReporter()};
switch (mode) {
case ErrorAppletMode::ShowError:
+ reporter.SaveErrorReport(title_id, error_code);
frontend.ShowError(error_code, callback);
break;
case ErrorAppletMode::ShowSystemError:
@@ -156,14 +161,18 @@ void Error::Execute() {
const auto& detail_text =
system ? args->system_error.detail_text : args->application_error.detail_text;
- frontend.ShowCustomErrorText(
- error_code,
- Common::StringFromFixedZeroTerminatedBuffer(main_text.data(), main_text.size()),
- Common::StringFromFixedZeroTerminatedBuffer(detail_text.data(), detail_text.size()),
- callback);
+ const auto main_text_string =
+ Common::StringFromFixedZeroTerminatedBuffer(main_text.data(), main_text.size());
+ const auto detail_text_string =
+ Common::StringFromFixedZeroTerminatedBuffer(detail_text.data(), detail_text.size());
+
+ reporter.SaveErrorReport(title_id, error_code, main_text_string, detail_text_string);
+ frontend.ShowCustomErrorText(error_code, main_text_string, detail_text_string, callback);
break;
}
case ErrorAppletMode::ShowErrorRecord:
+ reporter.SaveErrorReport(title_id, error_code,
+ fmt::format("{:016X}", args->error_record.posix_time));
frontend.ShowErrorWithTimestamp(
error_code, std::chrono::seconds{args->error_record.posix_time}, callback);
break;
diff --git a/src/core/hle/service/am/applets/general_backend.cpp b/src/core/hle/service/am/applets/general_backend.cpp
index c591b9ac2..e0def8dff 100644
--- a/src/core/hle/service/am/applets/general_backend.cpp
+++ b/src/core/hle/service/am/applets/general_backend.cpp
@@ -2,7 +2,7 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
-#include <string>
+#include <string_view>
#include "common/assert.h"
#include "common/hex_util.h"
@@ -13,27 +13,144 @@
#include "core/hle/result.h"
#include "core/hle/service/am/am.h"
#include "core/hle/service/am/applets/general_backend.h"
+#include "core/reporter.h"
namespace Service::AM::Applets {
-static void LogCurrentStorage(AppletDataBroker& broker, std::string prefix) {
+constexpr ResultCode ERROR_INVALID_PIN{ErrorModule::PCTL, 221};
+
+static void LogCurrentStorage(AppletDataBroker& broker, std::string_view prefix) {
std::unique_ptr<IStorage> storage = broker.PopNormalDataToApplet();
for (; storage != nullptr; storage = broker.PopNormalDataToApplet()) {
const auto data = storage->GetData();
LOG_INFO(Service_AM,
- "called (STUBBED), during {} recieved normal data with size={:08X}, data={}",
- prefix, data.size(), Common::HexVectorToString(data));
+ "called (STUBBED), during {} received normal data with size={:08X}, data={}",
+ prefix, data.size(), Common::HexToString(data));
}
storage = broker.PopInteractiveDataToApplet();
for (; storage != nullptr; storage = broker.PopInteractiveDataToApplet()) {
const auto data = storage->GetData();
LOG_INFO(Service_AM,
- "called (STUBBED), during {} recieved interactive data with size={:08X}, data={}",
- prefix, data.size(), Common::HexVectorToString(data));
+ "called (STUBBED), during {} received interactive data with size={:08X}, data={}",
+ prefix, data.size(), Common::HexToString(data));
}
}
+Auth::Auth(Core::Frontend::ParentalControlsApplet& frontend) : frontend(frontend) {}
+
+Auth::~Auth() = default;
+
+void Auth::Initialize() {
+ Applet::Initialize();
+ complete = false;
+
+ const auto storage = broker.PopNormalDataToApplet();
+ ASSERT(storage != nullptr);
+ const auto data = storage->GetData();
+ ASSERT(data.size() >= 0xC);
+
+ struct Arg {
+ INSERT_PADDING_BYTES(4);
+ AuthAppletType type;
+ u8 arg0;
+ u8 arg1;
+ u8 arg2;
+ INSERT_PADDING_BYTES(1);
+ };
+ static_assert(sizeof(Arg) == 0xC, "Arg (AuthApplet) has incorrect size.");
+
+ Arg arg{};
+ std::memcpy(&arg, data.data(), sizeof(Arg));
+
+ type = arg.type;
+ arg0 = arg.arg0;
+ arg1 = arg.arg1;
+ arg2 = arg.arg2;
+}
+
+bool Auth::TransactionComplete() const {
+ return complete;
+}
+
+ResultCode Auth::GetStatus() const {
+ return successful ? RESULT_SUCCESS : ERROR_INVALID_PIN;
+}
+
+void Auth::ExecuteInteractive() {
+ UNREACHABLE_MSG("Unexpected interactive applet data.");
+}
+
+void Auth::Execute() {
+ if (complete) {
+ return;
+ }
+
+ const auto unimplemented_log = [this] {
+ UNIMPLEMENTED_MSG("Unimplemented Auth applet type for type={:08X}, arg0={:02X}, "
+ "arg1={:02X}, arg2={:02X}",
+ static_cast<u32>(type), arg0, arg1, arg2);
+ };
+
+ switch (type) {
+ case AuthAppletType::ShowParentalAuthentication: {
+ const auto callback = [this](bool successful) { AuthFinished(successful); };
+
+ if (arg0 == 1 && arg1 == 0 && arg2 == 1) {
+ // ShowAuthenticatorForConfiguration
+ frontend.VerifyPINForSettings(callback);
+ } else if (arg1 == 0 && arg2 == 0) {
+ // ShowParentalAuthentication(bool)
+ frontend.VerifyPIN(callback, static_cast<bool>(arg0));
+ } else {
+ unimplemented_log();
+ }
+ break;
+ }
+ case AuthAppletType::RegisterParentalPasscode: {
+ const auto callback = [this] { AuthFinished(true); };
+
+ if (arg0 == 0 && arg1 == 0 && arg2 == 0) {
+ // RegisterParentalPasscode
+ frontend.RegisterPIN(callback);
+ } else {
+ unimplemented_log();
+ }
+ break;
+ }
+ case AuthAppletType::ChangeParentalPasscode: {
+ const auto callback = [this] { AuthFinished(true); };
+
+ if (arg0 == 0 && arg1 == 0 && arg2 == 0) {
+ // ChangeParentalPasscode
+ frontend.ChangePIN(callback);
+ } else {
+ unimplemented_log();
+ }
+ break;
+ }
+ default:
+ unimplemented_log();
+ }
+}
+
+void Auth::AuthFinished(bool successful) {
+ this->successful = successful;
+
+ struct Return {
+ ResultCode result_code;
+ };
+ static_assert(sizeof(Return) == 0x4, "Return (AuthApplet) has incorrect size.");
+
+ Return return_{GetStatus()};
+
+ std::vector<u8> out(sizeof(Return));
+ std::memcpy(out.data(), &return_, sizeof(Return));
+
+ broker.PushNormalDataFromApplet(IStorage{out});
+ broker.SignalStateChanged();
+}
+
PhotoViewer::PhotoViewer(const Core::Frontend::PhotoViewerApplet& frontend) : frontend(frontend) {}
PhotoViewer::~PhotoViewer() = default;
@@ -83,13 +200,20 @@ void PhotoViewer::ViewFinished() {
broker.SignalStateChanged();
}
-StubApplet::StubApplet() = default;
+StubApplet::StubApplet(AppletId id) : id(id) {}
StubApplet::~StubApplet() = default;
void StubApplet::Initialize() {
LOG_WARNING(Service_AM, "called (STUBBED)");
Applet::Initialize();
+
+ const auto data = broker.PeekDataToAppletForDebug();
+ Core::System::GetInstance().GetReporter().SaveUnimplementedAppletReport(
+ static_cast<u32>(id), common_args.arguments_version, common_args.library_version,
+ common_args.theme_color, common_args.play_startup_sound, common_args.system_tick,
+ data.normal, data.interactive);
+
LogCurrentStorage(broker, "Initialize");
}
diff --git a/src/core/hle/service/am/applets/general_backend.h b/src/core/hle/service/am/applets/general_backend.h
index 2dd255d7c..0da252044 100644
--- a/src/core/hle/service/am/applets/general_backend.h
+++ b/src/core/hle/service/am/applets/general_backend.h
@@ -8,6 +8,36 @@
namespace Service::AM::Applets {
+enum class AuthAppletType : u32 {
+ ShowParentalAuthentication,
+ RegisterParentalPasscode,
+ ChangeParentalPasscode,
+};
+
+class Auth final : public Applet {
+public:
+ explicit Auth(Core::Frontend::ParentalControlsApplet& frontend);
+ ~Auth() override;
+
+ void Initialize() override;
+ bool TransactionComplete() const override;
+ ResultCode GetStatus() const override;
+ void ExecuteInteractive() override;
+ void Execute() override;
+
+ void AuthFinished(bool successful = true);
+
+private:
+ Core::Frontend::ParentalControlsApplet& frontend;
+ bool complete = false;
+ bool successful = false;
+
+ AuthAppletType type = AuthAppletType::ShowParentalAuthentication;
+ u8 arg0 = 0;
+ u8 arg1 = 0;
+ u8 arg2 = 0;
+};
+
enum class PhotoViewerAppletMode : u8 {
CurrentApp = 0,
AllApps = 1,
@@ -34,7 +64,7 @@ private:
class StubApplet final : public Applet {
public:
- StubApplet();
+ explicit StubApplet(AppletId id);
~StubApplet() override;
void Initialize() override;
@@ -43,6 +73,9 @@ public:
ResultCode GetStatus() const override;
void ExecuteInteractive() override;
void Execute() override;
+
+private:
+ AppletId id;
};
} // namespace Service::AM::Applets
diff --git a/src/core/hle/service/am/applets/web_browser.cpp b/src/core/hle/service/am/applets/web_browser.cpp
index 7878f5136..2762e0653 100644
--- a/src/core/hle/service/am/applets/web_browser.cpp
+++ b/src/core/hle/service/am/applets/web_browser.cpp
@@ -19,7 +19,9 @@
#include "core/file_sys/nca_metadata.h"
#include "core/file_sys/registered_cache.h"
#include "core/file_sys/romfs.h"
+#include "core/file_sys/system_archive/system_archive.h"
#include "core/file_sys/vfs_types.h"
+#include "core/frontend/applets/general_frontend.h"
#include "core/frontend/applets/web_browser.h"
#include "core/hle/kernel/process.h"
#include "core/hle/service/am/applets/web_browser.h"
@@ -28,74 +30,187 @@
namespace Service::AM::Applets {
-// TODO(DarkLordZach): There are other arguments in the WebBuffer structure that are currently not
-// parsed, for example footer mode and left stick mode. Some of these are not particularly relevant,
-// but some may be worth an implementation.
-constexpr u16 WEB_ARGUMENT_URL_TYPE = 0x6;
+enum class WebArgTLVType : u16 {
+ InitialURL = 0x1,
+ ShopArgumentsURL = 0x2, ///< TODO(DarkLordZach): This is not the official name.
+ CallbackURL = 0x3,
+ CallbackableURL = 0x4,
+ ApplicationID = 0x5,
+ DocumentPath = 0x6,
+ DocumentKind = 0x7,
+ SystemDataID = 0x8,
+ ShareStartPage = 0x9,
+ Whitelist = 0xA,
+ News = 0xB,
+ UserID = 0xE,
+ AlbumEntry0 = 0xF,
+ ScreenShotEnabled = 0x10,
+ EcClientCertEnabled = 0x11,
+ Unk12 = 0x12,
+ PlayReportEnabled = 0x13,
+ Unk14 = 0x14,
+ Unk15 = 0x15,
+ BootDisplayKind = 0x17,
+ BackgroundKind = 0x18,
+ FooterEnabled = 0x19,
+ PointerEnabled = 0x1A,
+ LeftStickMode = 0x1B,
+ KeyRepeatFrame1 = 0x1C,
+ KeyRepeatFrame2 = 0x1D,
+ BootAsMediaPlayerInv = 0x1E,
+ DisplayUrlKind = 0x1F,
+ BootAsMediaPlayer = 0x21,
+ ShopJumpEnabled = 0x22,
+ MediaAutoPlayEnabled = 0x23,
+ LobbyParameter = 0x24,
+ ApplicationAlbumEntry = 0x26,
+ JsExtensionEnabled = 0x27,
+ AdditionalCommentText = 0x28,
+ TouchEnabledOnContents = 0x29,
+ UserAgentAdditionalString = 0x2A,
+ AdditionalMediaData0 = 0x2B,
+ MediaPlayerAutoCloseEnabled = 0x2C,
+ PageCacheEnabled = 0x2D,
+ WebAudioEnabled = 0x2E,
+ Unk2F = 0x2F,
+ YouTubeVideoWhitelist = 0x31,
+ FooterFixedKind = 0x32,
+ PageFadeEnabled = 0x33,
+ MediaCreatorApplicationRatingAge = 0x34,
+ BootLoadingIconEnabled = 0x35,
+ PageScrollIndicationEnabled = 0x36,
+ MediaPlayerSpeedControlEnabled = 0x37,
+ AlbumEntry1 = 0x38,
+ AlbumEntry2 = 0x39,
+ AlbumEntry3 = 0x3A,
+ AdditionalMediaData1 = 0x3B,
+ AdditionalMediaData2 = 0x3C,
+ AdditionalMediaData3 = 0x3D,
+ BootFooterButton = 0x3E,
+ OverrideWebAudioVolume = 0x3F,
+ OverrideMediaAudioVolume = 0x40,
+ BootMode = 0x41,
+ WebSessionEnabled = 0x42,
+};
+
+enum class ShimKind : u32 {
+ Shop = 1,
+ Login = 2,
+ Offline = 3,
+ Share = 4,
+ Web = 5,
+ Wifi = 6,
+ Lobby = 7,
+};
+
+enum class ShopWebTarget {
+ ApplicationInfo,
+ AddOnContentList,
+ SubscriptionList,
+ ConsumableItemList,
+ Home,
+ Settings,
+};
+
+namespace {
-struct WebBufferHeader {
+constexpr std::size_t SHIM_KIND_COUNT = 0x8;
+
+struct WebArgHeader {
u16 count;
- INSERT_PADDING_BYTES(6);
+ INSERT_PADDING_BYTES(2);
+ ShimKind kind;
};
-static_assert(sizeof(WebBufferHeader) == 0x8, "WebBufferHeader has incorrect size.");
+static_assert(sizeof(WebArgHeader) == 0x8, "WebArgHeader has incorrect size.");
-struct WebArgumentHeader {
- u16 type;
+struct WebArgTLV {
+ WebArgTLVType type;
u16 size;
u32 offset;
};
-static_assert(sizeof(WebArgumentHeader) == 0x8, "WebArgumentHeader has incorrect size.");
+static_assert(sizeof(WebArgTLV) == 0x8, "WebArgTLV has incorrect size.");
-struct WebArgumentResult {
+struct WebCommonReturnValue {
u32 result_code;
+ INSERT_PADDING_BYTES(0x4);
std::array<char, 0x1000> last_url;
u64 last_url_size;
};
-static_assert(sizeof(WebArgumentResult) == 0x1010, "WebArgumentResult has incorrect size.");
-
-static std::vector<u8> GetArgumentDataForTagType(const std::vector<u8>& data, u16 type) {
- WebBufferHeader header;
- ASSERT(sizeof(WebBufferHeader) <= data.size());
- std::memcpy(&header, data.data(), sizeof(WebBufferHeader));
-
- u64 offset = sizeof(WebBufferHeader);
- for (u16 i = 0; i < header.count; ++i) {
- WebArgumentHeader arg;
- ASSERT(offset + sizeof(WebArgumentHeader) <= data.size());
- std::memcpy(&arg, data.data() + offset, sizeof(WebArgumentHeader));
- offset += sizeof(WebArgumentHeader);
-
- if (arg.type == type) {
- std::vector<u8> out(arg.size);
- offset += arg.offset;
- ASSERT(offset + arg.size <= data.size());
- std::memcpy(out.data(), data.data() + offset, out.size());
+static_assert(sizeof(WebCommonReturnValue) == 0x1010, "WebCommonReturnValue has incorrect size.");
+
+struct WebWifiPageArg {
+ INSERT_PADDING_BYTES(4);
+ std::array<char, 0x100> connection_test_url;
+ std::array<char, 0x400> initial_url;
+ std::array<u8, 0x10> nifm_network_uuid;
+ u32 nifm_requirement;
+};
+static_assert(sizeof(WebWifiPageArg) == 0x518, "WebWifiPageArg has incorrect size.");
+
+struct WebWifiReturnValue {
+ INSERT_PADDING_BYTES(4);
+ u32 result;
+};
+static_assert(sizeof(WebWifiReturnValue) == 0x8, "WebWifiReturnValue has incorrect size.");
+
+enum class OfflineWebSource : u32 {
+ OfflineHtmlPage = 0x1,
+ ApplicationLegalInformation = 0x2,
+ SystemDataPage = 0x3,
+};
+
+std::map<WebArgTLVType, std::vector<u8>> GetWebArguments(const std::vector<u8>& arg) {
+ if (arg.size() < sizeof(WebArgHeader))
+ return {};
+
+ WebArgHeader header{};
+ std::memcpy(&header, arg.data(), sizeof(WebArgHeader));
+
+ std::map<WebArgTLVType, std::vector<u8>> out;
+ u64 offset = sizeof(WebArgHeader);
+ for (std::size_t i = 0; i < header.count; ++i) {
+ if (arg.size() < (offset + sizeof(WebArgTLV)))
return out;
- }
- offset += arg.offset + arg.size;
- }
+ WebArgTLV tlv{};
+ std::memcpy(&tlv, arg.data() + offset, sizeof(WebArgTLV));
+ offset += sizeof(WebArgTLV);
- return {};
-}
+ offset += tlv.offset;
+ if (arg.size() < (offset + tlv.size))
+ return out;
+
+ std::vector<u8> data(tlv.size);
+ std::memcpy(data.data(), arg.data() + offset, tlv.size);
+ offset += tlv.size;
-static FileSys::VirtualFile GetManualRomFS() {
- auto& loader{Core::System::GetInstance().GetAppLoader()};
+ out.insert_or_assign(tlv.type, data);
+ }
- FileSys::VirtualFile out;
- if (loader.ReadManualRomFS(out) == Loader::ResultStatus::Success)
- return out;
+ return out;
+}
+FileSys::VirtualFile GetApplicationRomFS(u64 title_id, FileSys::ContentRecordType type) {
const auto& installed{Core::System::GetInstance().GetContentProvider()};
- const auto res = installed.GetEntry(Core::System::GetInstance().CurrentProcess()->GetTitleID(),
- FileSys::ContentRecordType::Manual);
+ const auto res = installed.GetEntry(title_id, type);
- if (res != nullptr)
+ if (res != nullptr) {
return res->GetRomFS();
+ }
+
+ if (type == FileSys::ContentRecordType::Data) {
+ return FileSys::SystemArchive::SynthesizeSystemArchive(title_id);
+ }
+
return nullptr;
}
-WebBrowser::WebBrowser(Core::Frontend::WebBrowserApplet& frontend) : frontend(frontend) {}
+} // Anonymous namespace
+
+WebBrowser::WebBrowser(Core::Frontend::WebBrowserApplet& frontend, u64 current_process_title_id,
+ Core::Frontend::ECommerceApplet* frontend_e_commerce)
+ : frontend(frontend), frontend_e_commerce(frontend_e_commerce),
+ current_process_title_id(current_process_title_id) {}
WebBrowser::~WebBrowser() = default;
@@ -111,24 +226,12 @@ void WebBrowser::Initialize() {
ASSERT(web_arg_storage != nullptr);
const auto& web_arg = web_arg_storage->GetData();
- const auto url_data = GetArgumentDataForTagType(web_arg, WEB_ARGUMENT_URL_TYPE);
- filename = Common::StringFromFixedZeroTerminatedBuffer(
- reinterpret_cast<const char*>(url_data.data()), url_data.size());
+ ASSERT(web_arg.size() >= 0x8);
+ std::memcpy(&kind, web_arg.data() + 0x4, sizeof(ShimKind));
- temporary_dir = FileUtil::SanitizePath(FileUtil::GetUserPath(FileUtil::UserPath::CacheDir) +
- "web_applet_manual",
- FileUtil::DirectorySeparator::PlatformDefault);
- FileUtil::DeleteDirRecursively(temporary_dir);
+ args = GetWebArguments(web_arg);
- manual_romfs = GetManualRomFS();
- if (manual_romfs == nullptr) {
- status = ResultCode(-1);
- LOG_ERROR(Service_AM, "Failed to find manual for current process!");
- }
-
- filename =
- FileUtil::SanitizePath(temporary_dir + DIR_SEP + "html-document" + DIR_SEP + filename,
- FileUtil::DirectorySeparator::PlatformDefault);
+ InitializeInternal();
}
bool WebBrowser::TransactionComplete() const {
@@ -144,24 +247,25 @@ void WebBrowser::ExecuteInteractive() {
}
void WebBrowser::Execute() {
- if (complete)
+ if (complete) {
return;
+ }
if (status != RESULT_SUCCESS) {
complete = true;
return;
}
- frontend.OpenPage(filename, [this] { UnpackRomFS(); }, [this] { Finalize(); });
+ ExecuteInternal();
}
void WebBrowser::UnpackRomFS() {
if (unpacked)
return;
- ASSERT(manual_romfs != nullptr);
+ ASSERT(offline_romfs != nullptr);
const auto dir =
- FileSys::ExtractRomFS(manual_romfs, FileSys::RomFSExtractionType::SingleDiscard);
+ FileSys::ExtractRomFS(offline_romfs, FileSys::RomFSExtractionType::SingleDiscard);
const auto& vfs{Core::System::GetInstance().GetFilesystem()};
const auto temp_dir = vfs->CreateDirectory(temporary_dir, FileSys::Mode::ReadWrite);
FileSys::VfsRawCopyD(dir, temp_dir);
@@ -172,17 +276,275 @@ void WebBrowser::UnpackRomFS() {
void WebBrowser::Finalize() {
complete = true;
- WebArgumentResult out{};
+ WebCommonReturnValue out{};
out.result_code = 0;
out.last_url_size = 0;
- std::vector<u8> data(sizeof(WebArgumentResult));
- std::memcpy(data.data(), &out, sizeof(WebArgumentResult));
+ std::vector<u8> data(sizeof(WebCommonReturnValue));
+ std::memcpy(data.data(), &out, sizeof(WebCommonReturnValue));
broker.PushNormalDataFromApplet(IStorage{data});
broker.SignalStateChanged();
+ if (!temporary_dir.empty() && FileUtil::IsDirectory(temporary_dir)) {
+ FileUtil::DeleteDirRecursively(temporary_dir);
+ }
+}
+
+void WebBrowser::InitializeInternal() {
+ using WebAppletInitializer = void (WebBrowser::*)();
+
+ constexpr std::array<WebAppletInitializer, SHIM_KIND_COUNT> functions{
+ nullptr, &WebBrowser::InitializeShop,
+ nullptr, &WebBrowser::InitializeOffline,
+ nullptr, nullptr,
+ nullptr, nullptr,
+ };
+
+ const auto index = static_cast<u32>(kind);
+
+ if (index > functions.size() || functions[index] == nullptr) {
+ LOG_ERROR(Service_AM, "Invalid shim_kind={:08X}", index);
+ return;
+ }
+
+ const auto function = functions[index];
+ (this->*function)();
+}
+
+void WebBrowser::ExecuteInternal() {
+ using WebAppletExecutor = void (WebBrowser::*)();
+
+ constexpr std::array<WebAppletExecutor, SHIM_KIND_COUNT> functions{
+ nullptr, &WebBrowser::ExecuteShop,
+ nullptr, &WebBrowser::ExecuteOffline,
+ nullptr, nullptr,
+ nullptr, nullptr,
+ };
+
+ const auto index = static_cast<u32>(kind);
+
+ if (index > functions.size() || functions[index] == nullptr) {
+ LOG_ERROR(Service_AM, "Invalid shim_kind={:08X}", index);
+ return;
+ }
+
+ const auto function = functions[index];
+ (this->*function)();
+}
+
+void WebBrowser::InitializeShop() {
+ if (frontend_e_commerce == nullptr) {
+ LOG_ERROR(Service_AM, "Missing ECommerce Applet frontend!");
+ status = ResultCode(-1);
+ return;
+ }
+
+ const auto user_id_data = args.find(WebArgTLVType::UserID);
+
+ user_id = std::nullopt;
+ if (user_id_data != args.end()) {
+ user_id = u128{};
+ std::memcpy(user_id->data(), user_id_data->second.data(), sizeof(u128));
+ }
+
+ const auto url = args.find(WebArgTLVType::ShopArgumentsURL);
+
+ if (url == args.end()) {
+ LOG_ERROR(Service_AM, "Missing EShop Arguments URL for initialization!");
+ status = ResultCode(-1);
+ return;
+ }
+
+ std::vector<std::string> split_query;
+ Common::SplitString(Common::StringFromFixedZeroTerminatedBuffer(
+ reinterpret_cast<const char*>(url->second.data()), url->second.size()),
+ '?', split_query);
+
+ // 2 -> Main URL '?' Query Parameters
+ // Less is missing info, More is malformed
+ if (split_query.size() != 2) {
+ LOG_ERROR(Service_AM, "EShop Arguments has more than one question mark, malformed");
+ status = ResultCode(-1);
+ return;
+ }
+
+ std::vector<std::string> queries;
+ Common::SplitString(split_query[1], '&', queries);
+
+ const auto split_single_query =
+ [](const std::string& in) -> std::pair<std::string, std::string> {
+ const auto index = in.find('=');
+ if (index == std::string::npos || index == in.size() - 1) {
+ return {in, ""};
+ }
+
+ return {in.substr(0, index), in.substr(index + 1)};
+ };
+
+ std::transform(queries.begin(), queries.end(),
+ std::inserter(shop_query, std::next(shop_query.begin())), split_single_query);
+
+ const auto scene = shop_query.find("scene");
+
+ if (scene == shop_query.end()) {
+ LOG_ERROR(Service_AM, "No scene parameter was passed via shop query!");
+ status = ResultCode(-1);
+ return;
+ }
+
+ const std::map<std::string, ShopWebTarget, std::less<>> target_map{
+ {"product_detail", ShopWebTarget::ApplicationInfo},
+ {"aocs", ShopWebTarget::AddOnContentList},
+ {"subscriptions", ShopWebTarget::SubscriptionList},
+ {"consumption", ShopWebTarget::ConsumableItemList},
+ {"settings", ShopWebTarget::Settings},
+ {"top", ShopWebTarget::Home},
+ };
+
+ const auto target = target_map.find(scene->second);
+ if (target == target_map.end()) {
+ LOG_ERROR(Service_AM, "Scene for shop query is invalid! (scene={})", scene->second);
+ status = ResultCode(-1);
+ return;
+ }
+
+ shop_web_target = target->second;
+
+ const auto title_id_data = shop_query.find("dst_app_id");
+ if (title_id_data != shop_query.end()) {
+ title_id = std::stoull(title_id_data->second, nullptr, 0x10);
+ }
+
+ const auto mode_data = shop_query.find("mode");
+ if (mode_data != shop_query.end()) {
+ shop_full_display = mode_data->second == "full";
+ }
+}
+
+void WebBrowser::InitializeOffline() {
+ if (args.find(WebArgTLVType::DocumentPath) == args.end() ||
+ args.find(WebArgTLVType::DocumentKind) == args.end() ||
+ args.find(WebArgTLVType::ApplicationID) == args.end()) {
+ status = ResultCode(-1);
+ LOG_ERROR(Service_AM, "Missing necessary parameters for initialization!");
+ }
+
+ const auto url_data = args[WebArgTLVType::DocumentPath];
+ filename = Common::StringFromFixedZeroTerminatedBuffer(
+ reinterpret_cast<const char*>(url_data.data()), url_data.size());
+
+ OfflineWebSource source;
+ ASSERT(args[WebArgTLVType::DocumentKind].size() >= 4);
+ std::memcpy(&source, args[WebArgTLVType::DocumentKind].data(), sizeof(OfflineWebSource));
+
+ constexpr std::array<const char*, 3> WEB_SOURCE_NAMES{
+ "manual",
+ "legal",
+ "system",
+ };
+
+ temporary_dir =
+ FileUtil::SanitizePath(FileUtil::GetUserPath(FileUtil::UserPath::CacheDir) + "web_applet_" +
+ WEB_SOURCE_NAMES[static_cast<u32>(source) - 1],
+ FileUtil::DirectorySeparator::PlatformDefault);
FileUtil::DeleteDirRecursively(temporary_dir);
+
+ u64 title_id = 0; // 0 corresponds to current process
+ ASSERT(args[WebArgTLVType::ApplicationID].size() >= 0x8);
+ std::memcpy(&title_id, args[WebArgTLVType::ApplicationID].data(), sizeof(u64));
+ FileSys::ContentRecordType type = FileSys::ContentRecordType::Data;
+
+ switch (source) {
+ case OfflineWebSource::OfflineHtmlPage:
+ // While there is an AppID TLV field, in official SW this is always ignored.
+ title_id = 0;
+ type = FileSys::ContentRecordType::Manual;
+ break;
+ case OfflineWebSource::ApplicationLegalInformation:
+ type = FileSys::ContentRecordType::Legal;
+ break;
+ case OfflineWebSource::SystemDataPage:
+ type = FileSys::ContentRecordType::Data;
+ break;
+ }
+
+ if (title_id == 0) {
+ title_id = current_process_title_id;
+ }
+
+ offline_romfs = GetApplicationRomFS(title_id, type);
+ if (offline_romfs == nullptr) {
+ status = ResultCode(-1);
+ LOG_ERROR(Service_AM, "Failed to find offline data for request!");
+ }
+
+ std::string path_additional_directory;
+ if (source == OfflineWebSource::OfflineHtmlPage) {
+ path_additional_directory = std::string(DIR_SEP).append("html-document");
+ }
+
+ filename =
+ FileUtil::SanitizePath(temporary_dir + path_additional_directory + DIR_SEP + filename,
+ FileUtil::DirectorySeparator::PlatformDefault);
+}
+
+void WebBrowser::ExecuteShop() {
+ const auto callback = [this]() { Finalize(); };
+
+ const auto check_optional_parameter = [this](const auto& p) {
+ if (!p.has_value()) {
+ LOG_ERROR(Service_AM, "Missing one or more necessary parameters for execution!");
+ status = ResultCode(-1);
+ return false;
+ }
+
+ return true;
+ };
+
+ switch (shop_web_target) {
+ case ShopWebTarget::ApplicationInfo:
+ if (!check_optional_parameter(title_id))
+ return;
+ frontend_e_commerce->ShowApplicationInformation(callback, *title_id, user_id,
+ shop_full_display, shop_extra_parameter);
+ break;
+ case ShopWebTarget::AddOnContentList:
+ if (!check_optional_parameter(title_id))
+ return;
+ frontend_e_commerce->ShowAddOnContentList(callback, *title_id, user_id, shop_full_display);
+ break;
+ case ShopWebTarget::ConsumableItemList:
+ if (!check_optional_parameter(title_id))
+ return;
+ frontend_e_commerce->ShowConsumableItemList(callback, *title_id, user_id);
+ break;
+ case ShopWebTarget::Home:
+ if (!check_optional_parameter(user_id))
+ return;
+ if (!check_optional_parameter(shop_full_display))
+ return;
+ frontend_e_commerce->ShowShopHome(callback, *user_id, *shop_full_display);
+ break;
+ case ShopWebTarget::Settings:
+ if (!check_optional_parameter(user_id))
+ return;
+ if (!check_optional_parameter(shop_full_display))
+ return;
+ frontend_e_commerce->ShowSettings(callback, *user_id, *shop_full_display);
+ break;
+ case ShopWebTarget::SubscriptionList:
+ if (!check_optional_parameter(title_id))
+ return;
+ frontend_e_commerce->ShowSubscriptionList(callback, *title_id, user_id);
+ break;
+ default:
+ UNREACHABLE();
+ }
+}
+
+void WebBrowser::ExecuteOffline() {
+ frontend.OpenPageLocal(filename, [this] { UnpackRomFS(); }, [this] { Finalize(); });
}
} // namespace Service::AM::Applets
diff --git a/src/core/hle/service/am/applets/web_browser.h b/src/core/hle/service/am/applets/web_browser.h
index 7e0f34c7d..870f57b64 100644
--- a/src/core/hle/service/am/applets/web_browser.h
+++ b/src/core/hle/service/am/applets/web_browser.h
@@ -4,15 +4,22 @@
#pragma once
+#include <map>
#include "core/file_sys/vfs_types.h"
#include "core/hle/service/am/am.h"
#include "core/hle/service/am/applets/applets.h"
namespace Service::AM::Applets {
+enum class ShimKind : u32;
+enum class ShopWebTarget;
+enum class WebArgTLVType : u16;
+
class WebBrowser final : public Applet {
public:
- WebBrowser(Core::Frontend::WebBrowserApplet& frontend);
+ WebBrowser(Core::Frontend::WebBrowserApplet& frontend, u64 current_process_title_id,
+ Core::Frontend::ECommerceApplet* frontend_e_commerce = nullptr);
+
~WebBrowser() override;
void Initialize() override;
@@ -32,15 +39,41 @@ public:
void Finalize();
private:
+ void InitializeInternal();
+ void ExecuteInternal();
+
+ // Specific initializers for the types of web applets
+ void InitializeShop();
+ void InitializeOffline();
+
+ // Specific executors for the types of web applets
+ void ExecuteShop();
+ void ExecuteOffline();
+
Core::Frontend::WebBrowserApplet& frontend;
+ // Extra frontends for specialized functions
+ Core::Frontend::ECommerceApplet* frontend_e_commerce;
+
bool complete = false;
bool unpacked = false;
ResultCode status = RESULT_SUCCESS;
- FileSys::VirtualFile manual_romfs;
+ u64 current_process_title_id;
+
+ ShimKind kind;
+ std::map<WebArgTLVType, std::vector<u8>> args;
+
+ FileSys::VirtualFile offline_romfs;
std::string temporary_dir;
std::string filename;
+
+ ShopWebTarget shop_web_target;
+ std::map<std::string, std::string, std::less<>> shop_query;
+ std::optional<u64> title_id = 0;
+ std::optional<u128> user_id;
+ std::optional<bool> shop_full_display;
+ std::string shop_extra_parameter;
};
} // namespace Service::AM::Applets
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/audio/audout_u.cpp b/src/core/hle/service/audio/audout_u.cpp
index 6ba41b20a..7db6eb08d 100644
--- a/src/core/hle/service/audio/audout_u.cpp
+++ b/src/core/hle/service/audio/audout_u.cpp
@@ -58,8 +58,8 @@ public:
{9, &IAudioOut::GetAudioOutBufferCount, "GetAudioOutBufferCount"},
{10, nullptr, "GetAudioOutPlayedSampleCount"},
{11, nullptr, "FlushAudioOutBuffers"},
- {12, nullptr, "SetAudioOutVolume"},
- {13, nullptr, "GetAudioOutVolume"},
+ {12, &IAudioOut::SetAudioOutVolume, "SetAudioOutVolume"},
+ {13, &IAudioOut::GetAudioOutVolume, "GetAudioOutVolume"},
};
// clang-format on
RegisterHandlers(functions);
@@ -183,6 +183,25 @@ private:
rb.Push(static_cast<u32>(stream->GetQueueSize()));
}
+ void SetAudioOutVolume(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const float volume = rp.Pop<float>();
+ LOG_DEBUG(Service_Audio, "called, volume={}", volume);
+
+ stream->SetVolume(volume);
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(RESULT_SUCCESS);
+ }
+
+ void GetAudioOutVolume(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_Audio, "called");
+
+ IPC::ResponseBuilder rb{ctx, 3};
+ rb.Push(RESULT_SUCCESS);
+ rb.Push(stream->GetVolume());
+ }
+
AudioCore::AudioOut& audio_core;
AudioCore::StreamPtr stream;
std::string device_name;
diff --git a/src/core/hle/service/fatal/fatal.cpp b/src/core/hle/service/fatal/fatal.cpp
index 2c229bcad..fe49c2161 100644
--- a/src/core/hle/service/fatal/fatal.cpp
+++ b/src/core/hle/service/fatal/fatal.cpp
@@ -16,6 +16,7 @@
#include "core/hle/service/fatal/fatal.h"
#include "core/hle/service/fatal/fatal_p.h"
#include "core/hle/service/fatal/fatal_u.h"
+#include "core/reporter.h"
namespace Service::Fatal {
@@ -100,27 +101,10 @@ static void GenerateErrorReport(ResultCode error_code, const FatalInfo& info) {
LOG_ERROR(Service_Fatal, "{}", crash_report);
- const std::string crashreport_dir =
- FileUtil::GetUserPath(FileUtil::UserPath::LogDir) + "crash_logs";
-
- if (!FileUtil::CreateFullPath(crashreport_dir)) {
- LOG_ERROR(
- Service_Fatal,
- "Unable to create crash report directory. Possible log directory permissions issue.");
- return;
- }
-
- const std::time_t t = std::time(nullptr);
- const std::string crashreport_filename =
- fmt::format("{}/{:016x}-{:%F-%H%M%S}.log", crashreport_dir, title_id, *std::localtime(&t));
-
- auto file = FileUtil::IOFile(crashreport_filename, "wb");
- if (file.IsOpen()) {
- file.WriteString(crash_report);
- LOG_ERROR(Service_Fatal, "Saving error report to {}", crashreport_filename);
- } else {
- LOG_ERROR(Service_Fatal, "Failed to save error report to {}", crashreport_filename);
- }
+ Core::System::GetInstance().GetReporter().SaveCrashReport(
+ title_id, error_code, info.set_flags, info.program_entry_point, info.sp, info.pc,
+ info.pstate, info.afsr0, info.afsr1, info.esr, info.far, info.registers, info.backtrace,
+ info.backtrace_size, info.ArchAsString(), info.unk10);
}
static void ThrowFatalError(ResultCode error_code, FatalType fatal_type, const FatalInfo& info) {
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/ldr/ldr.cpp b/src/core/hle/service/ldr/ldr.cpp
index 5af925515..b839303ac 100644
--- a/src/core/hle/service/ldr/ldr.cpp
+++ b/src/core/hle/service/ldr/ldr.cpp
@@ -310,7 +310,7 @@ public:
if (!IsValidNROHash(hash)) {
LOG_ERROR(Service_LDR,
"NRO hash is not present in any currently loaded NRRs (hash={})!",
- Common::HexArrayToString(hash));
+ Common::HexToString(hash));
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ERROR_MISSING_NRR_HASH);
return;
diff --git a/src/core/hle/service/prepo/prepo.cpp b/src/core/hle/service/prepo/prepo.cpp
index e4fcee9f8..7e134f5c1 100644
--- a/src/core/hle/service/prepo/prepo.cpp
+++ b/src/core/hle/service/prepo/prepo.cpp
@@ -2,10 +2,18 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
+#include <json.hpp>
+#include "common/file_util.h"
+#include "common/hex_util.h"
#include "common/logging/log.h"
+#include "common/scm_rev.h"
#include "core/hle/ipc_helpers.h"
+#include "core/hle/kernel/process.h"
+#include "core/hle/service/acc/profile_manager.h"
#include "core/hle/service/prepo/prepo.h"
#include "core/hle/service/service.h"
+#include "core/reporter.h"
+#include "core/settings.h"
namespace Service::PlayReport {
@@ -40,8 +48,21 @@ public:
private:
void SaveReportWithUserOld(Kernel::HLERequestContext& ctx) {
- // TODO(ogniK): Do we want to add play report?
- LOG_WARNING(Service_PREPO, "(STUBBED) called");
+ IPC::RequestParser rp{ctx};
+ const auto user_id = rp.PopRaw<u128>();
+ const auto process_id = rp.PopRaw<u64>();
+
+ const auto data1 = ctx.ReadBuffer(0);
+ const auto data2 = ctx.ReadBuffer(1);
+
+ LOG_DEBUG(
+ Service_PREPO,
+ "called, user_id={:016X}{:016X}, unk1={:016X}, data1_size={:016X}, data2_size={:016X}",
+ user_id[1], user_id[0], process_id, data1.size(), data2.size());
+
+ const auto& reporter{Core::System::GetInstance().GetReporter()};
+ reporter.SavePlayReport(Core::CurrentProcess()->GetTitleID(), process_id, {data1, data2},
+ user_id);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
diff --git a/src/core/hle/service/service.cpp b/src/core/hle/service/service.cpp
index 00806b0ed..ec9d755b7 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"
@@ -68,6 +68,7 @@
#include "core/hle/service/usb/usb.h"
#include "core/hle/service/vi/vi.h"
#include "core/hle/service/wlan/wlan.h"
+#include "core/reporter.h"
namespace Service {
@@ -148,6 +149,8 @@ void ServiceFrameworkBase::ReportUnimplementedFunction(Kernel::HLERequestContext
}
buf.push_back('}');
+ Core::System::GetInstance().GetReporter().SaveUnimplementedFunctionReport(
+ ctx, ctx.GetCommand(), function_name, service_name);
UNIMPLEMENTED_MSG("Unknown / unimplemented {}", fmt::to_string(buf));
}
@@ -200,11 +203,10 @@ void Init(std::shared_ptr<SM::ServiceManager>& sm, Core::System& system,
SM::ServiceManager::InstallInterfaces(sm);
- Account::InstallInterfaces(*sm);
- AM::InstallInterfaces(*sm, nv_flinger);
+ Account::InstallInterfaces(system);
+ AM::InstallInterfaces(*sm, nv_flinger, system);
AOC::InstallInterfaces(*sm);
APM::InstallInterfaces(*sm);
- ARP::InstallInterfaces(*sm);
Audio::InstallInterfaces(*sm);
BCAT::InstallInterfaces(*sm);
BPC::InstallInterfaces(*sm);
@@ -218,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);