diff options
Diffstat (limited to 'src/core/hle/service/am')
-rw-r--r-- | src/core/hle/service/am/am.cpp | 155 | ||||
-rw-r--r-- | src/core/hle/service/am/am.h | 45 | ||||
-rw-r--r-- | src/core/hle/service/am/applet_ae.cpp | 40 | ||||
-rw-r--r-- | src/core/hle/service/am/applet_ae.h | 3 | ||||
-rw-r--r-- | src/core/hle/service/am/applet_oe.cpp | 21 | ||||
-rw-r--r-- | src/core/hle/service/am/applet_oe.h | 3 | ||||
-rw-r--r-- | src/core/hle/service/am/applets/applets.cpp | 90 | ||||
-rw-r--r-- | src/core/hle/service/am/applets/applets.h | 43 | ||||
-rw-r--r-- | src/core/hle/service/am/applets/error.cpp | 22 | ||||
-rw-r--r-- | src/core/hle/service/am/applets/error.h | 7 | ||||
-rw-r--r-- | src/core/hle/service/am/applets/general_backend.cpp | 145 | ||||
-rw-r--r-- | src/core/hle/service/am/applets/general_backend.h | 43 | ||||
-rw-r--r-- | src/core/hle/service/am/applets/profile_select.cpp | 5 | ||||
-rw-r--r-- | src/core/hle/service/am/applets/profile_select.h | 7 | ||||
-rw-r--r-- | src/core/hle/service/am/applets/software_keyboard.cpp | 5 | ||||
-rw-r--r-- | src/core/hle/service/am/applets/software_keyboard.h | 7 | ||||
-rw-r--r-- | src/core/hle/service/am/applets/web_browser.cpp | 499 | ||||
-rw-r--r-- | src/core/hle/service/am/applets/web_browser.h | 41 |
18 files changed, 970 insertions, 211 deletions
diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp index 3f201c821..aa2c83937 100644 --- a/src/core/hle/service/am/am.cpp +++ b/src/core/hle/service/am/am.cpp @@ -29,7 +29,8 @@ #include "core/hle/service/am/omm.h" #include "core/hle/service/am/spsm.h" #include "core/hle/service/am/tcap.h" -#include "core/hle/service/apm/apm.h" +#include "core/hle/service/apm/controller.h" +#include "core/hle/service/apm/interface.h" #include "core/hle/service/filesystem/filesystem.h" #include "core/hle/service/ns/ns.h" #include "core/hle/service/nvflinger/nvflinger.h" @@ -55,7 +56,8 @@ struct LaunchParameters { }; static_assert(sizeof(LaunchParameters) == 0x88); -IWindowController::IWindowController() : ServiceFramework("IWindowController") { +IWindowController::IWindowController(Core::System& system_) + : ServiceFramework("IWindowController"), system{system_} { // clang-format off static const FunctionInfo functions[] = { {0, nullptr, "CreateWindow"}, @@ -74,7 +76,7 @@ IWindowController::IWindowController() : ServiceFramework("IWindowController") { IWindowController::~IWindowController() = default; void IWindowController::GetAppletResourceUserId(Kernel::HLERequestContext& ctx) { - const u64 process_id = Core::System::GetInstance().Kernel().CurrentProcess()->GetProcessID(); + const u64 process_id = system.CurrentProcess()->GetProcessID(); LOG_DEBUG(Service_AM, "called. Process ID=0x{:016X}", process_id); @@ -230,8 +232,9 @@ IDebugFunctions::IDebugFunctions() : ServiceFramework{"IDebugFunctions"} { IDebugFunctions::~IDebugFunctions() = default; -ISelfController::ISelfController(std::shared_ptr<NVFlinger::NVFlinger> nvflinger) - : ServiceFramework("ISelfController"), nvflinger(std::move(nvflinger)) { +ISelfController::ISelfController(Core::System& system_, + std::shared_ptr<NVFlinger::NVFlinger> nvflinger_) + : ServiceFramework("ISelfController"), nvflinger(std::move(nvflinger_)) { // clang-format off static const FunctionInfo functions[] = { {0, nullptr, "Exit"}, @@ -265,13 +268,13 @@ ISelfController::ISelfController(std::shared_ptr<NVFlinger::NVFlinger> nvflinger {65, nullptr, "ReportUserIsActive"}, {66, nullptr, "GetCurrentIlluminance"}, {67, nullptr, "IsIlluminanceAvailable"}, - {68, nullptr, "SetAutoSleepDisabled"}, - {69, nullptr, "IsAutoSleepDisabled"}, + {68, &ISelfController::SetAutoSleepDisabled, "SetAutoSleepDisabled"}, + {69, &ISelfController::IsAutoSleepDisabled, "IsAutoSleepDisabled"}, {70, nullptr, "ReportMultimediaError"}, {71, nullptr, "GetCurrentIlluminanceEx"}, {80, nullptr, "SetWirelessPriorityMode"}, - {90, nullptr, "GetAccumulatedSuspendedTickValue"}, - {91, nullptr, "GetAccumulatedSuspendedTickChangedEvent"}, + {90, &ISelfController::GetAccumulatedSuspendedTickValue, "GetAccumulatedSuspendedTickValue"}, + {91, &ISelfController::GetAccumulatedSuspendedTickChangedEvent, "GetAccumulatedSuspendedTickChangedEvent"}, {100, nullptr, "SetAlbumImageTakenNotificationEnabled"}, {1000, nullptr, "GetDebugStorageChannel"}, }; @@ -279,9 +282,18 @@ ISelfController::ISelfController(std::shared_ptr<NVFlinger::NVFlinger> nvflinger RegisterHandlers(functions); - auto& kernel = Core::System::GetInstance().Kernel(); + auto& kernel = system_.Kernel(); launchable_event = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Manual, "ISelfController:LaunchableEvent"); + + // This event is created by AM on the first time GetAccumulatedSuspendedTickChangedEvent() is + // called. Yuzu can just create it unconditionally, since it doesn't need to support multiple + // ISelfControllers. The event is signaled on creation, and on transition from suspended -> not + // suspended if the event has previously been created by a call to + // GetAccumulatedSuspendedTickChangedEvent. + accumulated_suspended_tick_changed_event = Kernel::WritableEvent::CreateEventPair( + kernel, Kernel::ResetType::Manual, "ISelfController:AccumulatedSuspendedTickChangedEvent"); + accumulated_suspended_tick_changed_event.writable->Signal(); } ISelfController::~ISelfController() = default; @@ -444,8 +456,54 @@ void ISelfController::GetIdleTimeDetectionExtension(Kernel::HLERequestContext& c rb.Push<u32>(idle_time_detection_extension); } -AppletMessageQueue::AppletMessageQueue() { - auto& kernel = Core::System::GetInstance().Kernel(); +void ISelfController::SetAutoSleepDisabled(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + is_auto_sleep_disabled = rp.Pop<bool>(); + + // On the system itself, if the previous state of is_auto_sleep_disabled + // differed from the current value passed in, it'd signify the internal + // window manager to update (and also increment some statistics like update counts) + // + // It'd also indicate this change to an idle handling context. + // + // However, given we're emulating this behavior, most of this can be ignored + // and it's sufficient to simply set the member variable for querying via + // IsAutoSleepDisabled(). + + LOG_DEBUG(Service_AM, "called. is_auto_sleep_disabled={}", is_auto_sleep_disabled); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); +} + +void ISelfController::IsAutoSleepDisabled(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_AM, "called."); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(RESULT_SUCCESS); + rb.Push(is_auto_sleep_disabled); +} + +void ISelfController::GetAccumulatedSuspendedTickValue(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_AM, "called."); + + // This command returns the total number of system ticks since ISelfController creation + // where the game was suspended. Since Yuzu doesn't implement game suspension, this command + // can just always return 0 ticks. + IPC::ResponseBuilder rb{ctx, 4}; + rb.Push(RESULT_SUCCESS); + rb.Push<u64>(0); +} + +void ISelfController::GetAccumulatedSuspendedTickChangedEvent(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_AM, "called."); + + IPC::ResponseBuilder rb{ctx, 2, 1}; + rb.Push(RESULT_SUCCESS); + rb.PushCopyObjects(accumulated_suspended_tick_changed_event.readable); +} + +AppletMessageQueue::AppletMessageQueue(Kernel::KernelCore& kernel) { on_new_message = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Manual, "AMMessageQueue:OnMessageRecieved"); on_operation_mode_changed = Kernel::WritableEvent::CreateEventPair( @@ -492,8 +550,9 @@ void AppletMessageQueue::OperationModeChanged() { on_operation_mode_changed.writable->Signal(); } -ICommonStateGetter::ICommonStateGetter(std::shared_ptr<AppletMessageQueue> msg_queue) - : ServiceFramework("ICommonStateGetter"), msg_queue(std::move(msg_queue)) { +ICommonStateGetter::ICommonStateGetter(Core::System& system, + std::shared_ptr<AppletMessageQueue> msg_queue) + : ServiceFramework("ICommonStateGetter"), system(system), msg_queue(std::move(msg_queue)) { // clang-format off static const FunctionInfo functions[] = { {0, &ICommonStateGetter::GetEventHandle, "GetEventHandle"}, @@ -526,7 +585,7 @@ ICommonStateGetter::ICommonStateGetter(std::shared_ptr<AppletMessageQueue> msg_q {63, nullptr, "GetHdcpAuthenticationStateChangeEvent"}, {64, nullptr, "SetTvPowerStateMatchingMode"}, {65, nullptr, "GetApplicationIdByContentActionName"}, - {66, nullptr, "SetCpuBoostMode"}, + {66, &ICommonStateGetter::SetCpuBoostMode, "SetCpuBoostMode"}, {80, nullptr, "PerformSystemButtonPressingIfInFocus"}, {90, nullptr, "SetPerformanceConfigurationChangedNotification"}, {91, nullptr, "GetCurrentPerformanceConfiguration"}, @@ -607,6 +666,16 @@ void ICommonStateGetter::GetDefaultDisplayResolution(Kernel::HLERequestContext& } } +void ICommonStateGetter::SetCpuBoostMode(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_AM, "called, forwarding to APM:SYS"); + + const auto& sm = system.ServiceManager(); + const auto apm_sys = sm.GetService<APM::APM_Sys>("apm:sys"); + ASSERT(apm_sys != nullptr); + + apm_sys->SetCpuBoostMode(ctx); +} + IStorage::IStorage(std::vector<u8> buffer) : ServiceFramework("IStorage"), buffer(std::move(buffer)) { // clang-format off @@ -635,13 +704,11 @@ void ICommonStateGetter::GetOperationMode(Kernel::HLERequestContext& ctx) { } void ICommonStateGetter::GetPerformanceMode(Kernel::HLERequestContext& ctx) { - const bool use_docked_mode{Settings::values.use_docked_mode}; - LOG_DEBUG(Service_AM, "called, use_docked_mode={}", use_docked_mode); + LOG_DEBUG(Service_AM, "called"); IPC::ResponseBuilder rb{ctx, 3}; rb.Push(RESULT_SUCCESS); - rb.Push(static_cast<u32>(use_docked_mode ? APM::PerformanceMode::Docked - : APM::PerformanceMode::Handheld)); + rb.PushEnum(system.GetAPMController().GetCurrentPerformanceMode()); } class ILibraryAppletAccessor final : public ServiceFramework<ILibraryAppletAccessor> { @@ -871,7 +938,8 @@ void IStorageAccessor::Read(Kernel::HLERequestContext& ctx) { rb.Push(RESULT_SUCCESS); } -ILibraryAppletCreator::ILibraryAppletCreator() : ServiceFramework("ILibraryAppletCreator") { +ILibraryAppletCreator::ILibraryAppletCreator(Core::System& system_) + : ServiceFramework("ILibraryAppletCreator"), system{system_} { static const FunctionInfo functions[] = { {0, &ILibraryAppletCreator::CreateLibraryApplet, "CreateLibraryApplet"}, {1, nullptr, "TerminateAllLibraryApplets"}, @@ -893,7 +961,7 @@ void ILibraryAppletCreator::CreateLibraryApplet(Kernel::HLERequestContext& ctx) LOG_DEBUG(Service_AM, "called with applet_id={:08X}, applet_mode={:08X}", static_cast<u32>(applet_id), applet_mode); - const auto& applet_manager{Core::System::GetInstance().GetAppletManager()}; + const auto& applet_manager{system.GetAppletManager()}; const auto applet = applet_manager.GetApplet(applet_id); if (applet == nullptr) { @@ -931,8 +999,7 @@ void ILibraryAppletCreator::CreateTransferMemoryStorage(Kernel::HLERequestContex const auto handle{rp.Pop<Kernel::Handle>()}; const auto transfer_mem = - Core::System::GetInstance().CurrentProcess()->GetHandleTable().Get<Kernel::TransferMemory>( - handle); + system.CurrentProcess()->GetHandleTable().Get<Kernel::TransferMemory>(handle); if (transfer_mem == nullptr) { LOG_ERROR(Service_AM, "shared_mem is a nullpr for handle={:08X}", handle); @@ -950,7 +1017,8 @@ void ILibraryAppletCreator::CreateTransferMemoryStorage(Kernel::HLERequestContex rb.PushIpcInterface(std::make_shared<IStorage>(std::move(memory))); } -IApplicationFunctions::IApplicationFunctions() : ServiceFramework("IApplicationFunctions") { +IApplicationFunctions::IApplicationFunctions(Core::System& system_) + : ServiceFramework("IApplicationFunctions"), system{system_} { // clang-format off static const FunctionInfo functions[] = { {1, &IApplicationFunctions::PopLaunchParameter, "PopLaunchParameter"}, @@ -989,6 +1057,7 @@ IApplicationFunctions::IApplicationFunctions() : ServiceFramework("IApplicationF {120, nullptr, "ExecuteProgram"}, {121, nullptr, "ClearUserChannel"}, {122, nullptr, "UnpopToUserChannel"}, + {130, &IApplicationFunctions::GetGpuErrorDetectedSystemEvent, "GetGpuErrorDetectedSystemEvent"}, {500, nullptr, "StartContinuousRecordingFlushForDebug"}, {1000, nullptr, "CreateMovieMaker"}, {1001, nullptr, "PrepareForJit"}, @@ -996,6 +1065,10 @@ IApplicationFunctions::IApplicationFunctions() : ServiceFramework("IApplicationF // clang-format on RegisterHandlers(functions); + + auto& kernel = Core::System::GetInstance().Kernel(); + gpu_error_detected_event = Kernel::WritableEvent::CreateEventPair( + kernel, Kernel::ResetType::Manual, "IApplicationFunctions:GpuErrorDetectedSystemEvent"); } IApplicationFunctions::~IApplicationFunctions() = default; @@ -1107,7 +1180,7 @@ void IApplicationFunctions::GetDesiredLanguage(Kernel::HLERequestContext& ctx) { // Get supported languages from NACP, if possible // Default to 0 (all languages supported) u32 supported_languages = 0; - FileSys::PatchManager pm{Core::System::GetInstance().CurrentProcess()->GetTitleID()}; + FileSys::PatchManager pm{system.CurrentProcess()->GetTitleID()}; const auto res = pm.GetControlMetadata(); if (res.first != nullptr) { @@ -1115,8 +1188,8 @@ void IApplicationFunctions::GetDesiredLanguage(Kernel::HLERequestContext& ctx) { } // Call IApplicationManagerInterface implementation. - auto& service_manager = Core::System::GetInstance().ServiceManager(); - auto ns_am2 = service_manager.GetService<Service::NS::NS>("ns:am2"); + auto& service_manager = system.ServiceManager(); + auto ns_am2 = service_manager.GetService<NS::NS>("ns:am2"); auto app_man = ns_am2->GetApplicationManagerInterface(); // Get desired application language @@ -1188,8 +1261,8 @@ void IApplicationFunctions::ExtendSaveData(Kernel::HLERequestContext& ctx) { "new_journal={:016X}", static_cast<u8>(type), user_id[1], user_id[0], new_normal_size, new_journal_size); - FileSystem::WriteSaveDataSize(type, Core::CurrentProcess()->GetTitleID(), user_id, - {new_normal_size, new_journal_size}); + const auto title_id = system.CurrentProcess()->GetTitleID(); + FileSystem::WriteSaveDataSize(type, title_id, user_id, {new_normal_size, new_journal_size}); IPC::ResponseBuilder rb{ctx, 4}; rb.Push(RESULT_SUCCESS); @@ -1208,8 +1281,8 @@ void IApplicationFunctions::GetSaveDataSize(Kernel::HLERequestContext& ctx) { LOG_DEBUG(Service_AM, "called with type={:02X}, user_id={:016X}{:016X}", static_cast<u8>(type), user_id[1], user_id[0]); - const auto size = - FileSystem::ReadSaveDataSize(type, Core::CurrentProcess()->GetTitleID(), user_id); + const auto title_id = system.CurrentProcess()->GetTitleID(); + const auto size = FileSystem::ReadSaveDataSize(type, title_id, user_id); IPC::ResponseBuilder rb{ctx, 6}; rb.Push(RESULT_SUCCESS); @@ -1217,14 +1290,22 @@ void IApplicationFunctions::GetSaveDataSize(Kernel::HLERequestContext& ctx) { rb.Push(size.journal); } +void IApplicationFunctions::GetGpuErrorDetectedSystemEvent(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_AM, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 2, 1}; + rb.Push(RESULT_SUCCESS); + rb.PushCopyObjects(gpu_error_detected_event.readable); +} + void InstallInterfaces(SM::ServiceManager& service_manager, - std::shared_ptr<NVFlinger::NVFlinger> nvflinger) { - auto message_queue = std::make_shared<AppletMessageQueue>(); - message_queue->PushMessage(AppletMessageQueue::AppletMessage::FocusStateChanged); // Needed on - // game boot + std::shared_ptr<NVFlinger::NVFlinger> nvflinger, Core::System& system) { + auto message_queue = std::make_shared<AppletMessageQueue>(system.Kernel()); + // Needed on game boot + message_queue->PushMessage(AppletMessageQueue::AppletMessage::FocusStateChanged); - 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..28f870302 100644 --- a/src/core/hle/service/am/am.h +++ b/src/core/hle/service/am/am.h @@ -10,12 +10,15 @@ #include "core/hle/kernel/writable_event.h" #include "core/hle/service/service.h" -namespace Service { -namespace NVFlinger { +namespace Kernel { +class KernelCore; +} + +namespace Service::NVFlinger { class NVFlinger; } -namespace AM { +namespace Service::AM { enum SystemLanguage { Japanese = 0, @@ -47,7 +50,7 @@ public: PerformanceModeChanged = 31, }; - AppletMessageQueue(); + explicit AppletMessageQueue(Kernel::KernelCore& kernel); ~AppletMessageQueue(); const Kernel::SharedPtr<Kernel::ReadableEvent>& GetMesssageRecieveEvent() const; @@ -65,12 +68,14 @@ private: class IWindowController final : public ServiceFramework<IWindowController> { public: - IWindowController(); + explicit IWindowController(Core::System& system_); ~IWindowController() override; private: void GetAppletResourceUserId(Kernel::HLERequestContext& ctx); void AcquireForegroundRights(Kernel::HLERequestContext& ctx); + + Core::System& system; }; class IAudioController final : public ServiceFramework<IAudioController> { @@ -113,7 +118,8 @@ public: class ISelfController final : public ServiceFramework<ISelfController> { public: - explicit ISelfController(std::shared_ptr<NVFlinger::NVFlinger> nvflinger); + explicit ISelfController(Core::System& system_, + std::shared_ptr<NVFlinger::NVFlinger> nvflinger_); ~ISelfController() override; private: @@ -133,16 +139,24 @@ private: void SetHandlesRequestToDisplay(Kernel::HLERequestContext& ctx); void SetIdleTimeDetectionExtension(Kernel::HLERequestContext& ctx); void GetIdleTimeDetectionExtension(Kernel::HLERequestContext& ctx); + void SetAutoSleepDisabled(Kernel::HLERequestContext& ctx); + void IsAutoSleepDisabled(Kernel::HLERequestContext& ctx); + void GetAccumulatedSuspendedTickValue(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; + bool is_auto_sleep_disabled = false; }; class ICommonStateGetter final : public ServiceFramework<ICommonStateGetter> { public: - explicit ICommonStateGetter(std::shared_ptr<AppletMessageQueue> msg_queue); + explicit ICommonStateGetter(Core::System& system, + std::shared_ptr<AppletMessageQueue> msg_queue); ~ICommonStateGetter() override; private: @@ -164,7 +178,9 @@ private: void GetPerformanceMode(Kernel::HLERequestContext& ctx); void GetBootMode(Kernel::HLERequestContext& ctx); void GetDefaultDisplayResolution(Kernel::HLERequestContext& ctx); + void SetCpuBoostMode(Kernel::HLERequestContext& ctx); + Core::System& system; std::shared_ptr<AppletMessageQueue> msg_queue; }; @@ -198,18 +214,20 @@ private: class ILibraryAppletCreator final : public ServiceFramework<ILibraryAppletCreator> { public: - ILibraryAppletCreator(); + explicit ILibraryAppletCreator(Core::System& system_); ~ILibraryAppletCreator() override; private: void CreateLibraryApplet(Kernel::HLERequestContext& ctx); void CreateStorage(Kernel::HLERequestContext& ctx); void CreateTransferMemoryStorage(Kernel::HLERequestContext& ctx); + + Core::System& system; }; class IApplicationFunctions final : public ServiceFramework<IApplicationFunctions> { public: - IApplicationFunctions(); + explicit IApplicationFunctions(Core::System& system_); ~IApplicationFunctions() override; private: @@ -230,6 +248,10 @@ private: void BeginBlockingHomeButton(Kernel::HLERequestContext& ctx); void EndBlockingHomeButton(Kernel::HLERequestContext& ctx); void EnableApplicationCrashReport(Kernel::HLERequestContext& ctx); + void GetGpuErrorDetectedSystemEvent(Kernel::HLERequestContext& ctx); + + Kernel::EventPair gpu_error_detected_event; + Core::System& system; }; class IHomeMenuFunctions final : public ServiceFramework<IHomeMenuFunctions> { @@ -261,7 +283,6 @@ 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 +} // namespace Service::AM diff --git a/src/core/hle/service/am/applet_ae.cpp b/src/core/hle/service/am/applet_ae.cpp index 488add8e7..e454b77d8 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"}, @@ -40,7 +42,7 @@ private: IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); - rb.PushIpcInterface<ICommonStateGetter>(msg_queue); + rb.PushIpcInterface<ICommonStateGetter>(system, msg_queue); } void GetSelfController(Kernel::HLERequestContext& ctx) { @@ -48,7 +50,7 @@ private: IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); - rb.PushIpcInterface<ISelfController>(nvflinger); + rb.PushIpcInterface<ISelfController>(system, nvflinger); } void GetWindowController(Kernel::HLERequestContext& ctx) { @@ -56,7 +58,7 @@ private: IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); - rb.PushIpcInterface<IWindowController>(); + rb.PushIpcInterface<IWindowController>(system); } void GetAudioController(Kernel::HLERequestContext& ctx) { @@ -96,7 +98,7 @@ private: IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); - rb.PushIpcInterface<ILibraryAppletCreator>(); + rb.PushIpcInterface<ILibraryAppletCreator>(system); } void GetApplicationFunctions(Kernel::HLERequestContext& ctx) { @@ -104,19 +106,20 @@ private: IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); - rb.PushIpcInterface<IApplicationFunctions>(); + rb.PushIpcInterface<IApplicationFunctions>(system); } 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"}, @@ -143,7 +146,7 @@ private: IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); - rb.PushIpcInterface<ICommonStateGetter>(msg_queue); + rb.PushIpcInterface<ICommonStateGetter>(system, msg_queue); } void GetSelfController(Kernel::HLERequestContext& ctx) { @@ -151,7 +154,7 @@ private: IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); - rb.PushIpcInterface<ISelfController>(nvflinger); + rb.PushIpcInterface<ISelfController>(system, nvflinger); } void GetWindowController(Kernel::HLERequestContext& ctx) { @@ -159,7 +162,7 @@ private: IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); - rb.PushIpcInterface<IWindowController>(); + rb.PushIpcInterface<IWindowController>(system); } void GetAudioController(Kernel::HLERequestContext& ctx) { @@ -191,7 +194,7 @@ private: IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); - rb.PushIpcInterface<ILibraryAppletCreator>(); + rb.PushIpcInterface<ILibraryAppletCreator>(system); } 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..a2ffaa440 100644 --- a/src/core/hle/service/am/applet_oe.cpp +++ b/src/core/hle/service/am/applet_oe.cpp @@ -13,9 +13,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"}, @@ -63,7 +63,7 @@ private: IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); - rb.PushIpcInterface<IWindowController>(); + rb.PushIpcInterface<IWindowController>(system); } void GetSelfController(Kernel::HLERequestContext& ctx) { @@ -71,7 +71,7 @@ private: IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); - rb.PushIpcInterface<ISelfController>(nvflinger); + rb.PushIpcInterface<ISelfController>(system, nvflinger); } void GetCommonStateGetter(Kernel::HLERequestContext& ctx) { @@ -79,7 +79,7 @@ private: IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); - rb.PushIpcInterface<ICommonStateGetter>(msg_queue); + rb.PushIpcInterface<ICommonStateGetter>(system, msg_queue); } void GetLibraryAppletCreator(Kernel::HLERequestContext& ctx) { @@ -87,7 +87,7 @@ private: IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); - rb.PushIpcInterface<ILibraryAppletCreator>(); + rb.PushIpcInterface<ILibraryAppletCreator>(system); } void GetApplicationFunctions(Kernel::HLERequestContext& ctx) { @@ -95,11 +95,12 @@ private: IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); - rb.PushIpcInterface<IApplicationFunctions>(); + rb.PushIpcInterface<IApplicationFunctions>(system); } std::shared_ptr<NVFlinger::NVFlinger> nvflinger; std::shared_ptr<AppletMessageQueue> msg_queue; + Core::System& system; }; void AppletOE::OpenApplicationProxy(Kernel::HLERequestContext& ctx) { @@ -107,13 +108,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..d2e35362f 100644 --- a/src/core/hle/service/am/applets/applets.cpp +++ b/src/core/hle/service/am/applets/applets.cpp @@ -23,8 +23,7 @@ namespace Service::AM::Applets { -AppletDataBroker::AppletDataBroker() { - auto& kernel = Core::System::GetInstance().Kernel(); +AppletDataBroker::AppletDataBroker(Kernel::KernelCore& kernel) { state_changed_event = Kernel::WritableEvent::CreateEventPair( kernel, Kernel::ResetType::Manual, "ILibraryAppletAccessor:StateChangedEvent"); pop_out_data_event = Kernel::WritableEvent::CreateEventPair( @@ -35,12 +34,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 +64,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 +73,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 +82,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(); } @@ -105,7 +120,7 @@ Kernel::SharedPtr<Kernel::ReadableEvent> AppletDataBroker::GetStateChangedEvent( return state_changed_event.readable; } -Applet::Applet() = default; +Applet::Applet(Kernel::KernelCore& kernel_) : broker{kernel_} {} Applet::~Applet() = default; @@ -123,12 +138,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; @@ -136,11 +153,13 @@ AppletFrontendSet::AppletFrontendSet(AppletFrontendSet&&) noexcept = default; AppletFrontendSet& AppletFrontendSet::operator=(AppletFrontendSet&&) noexcept = default; -AppletManager::AppletManager() = default; +AppletManager::AppletManager(Core::System& system_) : system{system_} {} 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 +170,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,6 +205,10 @@ 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() { @@ -190,21 +217,26 @@ void AppletManager::ClearAll() { std::shared_ptr<Applet> AppletManager::GetApplet(AppletId id) const { switch (id) { + case AppletId::Auth: + return std::make_shared<Auth>(system, *frontend.parental_controls); case AppletId::Error: - return std::make_shared<Error>(*frontend.error); + return std::make_shared<Error>(system, *frontend.error); case AppletId::ProfileSelect: - return std::make_shared<ProfileSelect>(*frontend.profile_select); + return std::make_shared<ProfileSelect>(system, *frontend.profile_select); case AppletId::SoftwareKeyboard: - return std::make_shared<SoftwareKeyboard>(*frontend.software_keyboard); + return std::make_shared<SoftwareKeyboard>(system, *frontend.software_keyboard); case AppletId::PhotoViewer: - return std::make_shared<PhotoViewer>(*frontend.photo_viewer); + return std::make_shared<PhotoViewer>(system, *frontend.photo_viewer); + case AppletId::LibAppletShop: + return std::make_shared<WebBrowser>(system, *frontend.web_browser, + frontend.e_commerce.get()); case AppletId::LibAppletOff: - return std::make_shared<WebBrowser>(*frontend.web_browser); + return std::make_shared<WebBrowser>(system, *frontend.web_browser); 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>(system, id); } } diff --git a/src/core/hle/service/am/applets/applets.h b/src/core/hle/service/am/applets/applets.h index b46e10a4a..764c3418c 100644 --- a/src/core/hle/service/am/applets/applets.h +++ b/src/core/hle/service/am/applets/applets.h @@ -12,14 +12,24 @@ union ResultCode; +namespace Core { +class System; +} + namespace Core::Frontend { +class ECommerceApplet; class ErrorApplet; +class ParentalControlsApplet; class PhotoViewerApplet; class ProfileSelectApplet; class SoftwareKeyboardApplet; class WebBrowserApplet; } // namespace Core::Frontend +namespace Kernel { +class KernelCore; +} + namespace Service::AM { class IStorage; @@ -51,9 +61,17 @@ enum class AppletId : u32 { class AppletDataBroker final { public: - AppletDataBroker(); + explicit AppletDataBroker(Kernel::KernelCore& kernel_); ~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 +94,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; @@ -98,7 +116,7 @@ private: class Applet { public: - Applet(); + explicit Applet(Kernel::KernelCore& kernel_); virtual ~Applet(); virtual void Initialize(); @@ -137,15 +155,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,16 +176,18 @@ 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 { public: - AppletManager(); + explicit AppletManager(Core::System& system_); ~AppletManager(); void SetAppletFrontendSet(AppletFrontendSet set); @@ -175,6 +199,7 @@ public: private: AppletFrontendSet frontend; + Core::System& system; }; } // namespace Applets diff --git a/src/core/hle/service/am/applets/error.cpp b/src/core/hle/service/am/applets/error.cpp index 04774bedc..a7db26725 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 { @@ -83,7 +85,8 @@ ResultCode Decode64BitError(u64 error) { } // Anonymous namespace -Error::Error(const Core::Frontend::ErrorApplet& frontend) : frontend(frontend) {} +Error::Error(Core::System& system_, const Core::Frontend::ErrorApplet& frontend_) + : Applet{system_.Kernel()}, frontend(frontend_), system{system_} {} Error::~Error() = default; @@ -143,9 +146,12 @@ void Error::Execute() { } const auto callback = [this] { DisplayCompleted(); }; + const auto title_id = system.CurrentProcess()->GetTitleID(); + const auto& reporter{system.GetReporter()}; switch (mode) { case ErrorAppletMode::ShowError: + reporter.SaveErrorReport(title_id, error_code); frontend.ShowError(error_code, callback); break; case ErrorAppletMode::ShowSystemError: @@ -156,14 +162,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/error.h b/src/core/hle/service/am/applets/error.h index a3590d181..a105cdb0c 100644 --- a/src/core/hle/service/am/applets/error.h +++ b/src/core/hle/service/am/applets/error.h @@ -7,6 +7,10 @@ #include "core/hle/result.h" #include "core/hle/service/am/applets/applets.h" +namespace Core { +class System; +} + namespace Service::AM::Applets { enum class ErrorAppletMode : u8 { @@ -21,7 +25,7 @@ enum class ErrorAppletMode : u8 { class Error final : public Applet { public: - explicit Error(const Core::Frontend::ErrorApplet& frontend); + explicit Error(Core::System& system_, const Core::Frontend::ErrorApplet& frontend_); ~Error() override; void Initialize() override; @@ -42,6 +46,7 @@ private: std::unique_ptr<ErrorArguments> args; bool complete = false; + Core::System& system; }; } // namespace Service::AM::Applets diff --git a/src/core/hle/service/am/applets/general_backend.cpp b/src/core/hle/service/am/applets/general_backend.cpp index c591b9ac2..328438a1d 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,28 +13,147 @@ #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)); } } -PhotoViewer::PhotoViewer(const Core::Frontend::PhotoViewerApplet& frontend) : frontend(frontend) {} +Auth::Auth(Core::System& system_, Core::Frontend::ParentalControlsApplet& frontend_) + : Applet{system_.Kernel()}, 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(Core::System& system_, const Core::Frontend::PhotoViewerApplet& frontend_) + : Applet{system_.Kernel()}, frontend(frontend_), system{system_} {} PhotoViewer::~PhotoViewer() = default; @@ -68,7 +187,7 @@ void PhotoViewer::Execute() { const auto callback = [this] { ViewFinished(); }; switch (mode) { case PhotoViewerAppletMode::CurrentApp: - frontend.ShowPhotosForApplication(Core::CurrentProcess()->GetTitleID(), callback); + frontend.ShowPhotosForApplication(system.CurrentProcess()->GetTitleID(), callback); break; case PhotoViewerAppletMode::AllApps: frontend.ShowAllPhotos(callback); @@ -83,13 +202,21 @@ void PhotoViewer::ViewFinished() { broker.SignalStateChanged(); } -StubApplet::StubApplet() = default; +StubApplet::StubApplet(Core::System& system_, AppletId id_) + : Applet{system_.Kernel()}, id(id_), system{system_} {} StubApplet::~StubApplet() = default; void StubApplet::Initialize() { LOG_WARNING(Service_AM, "called (STUBBED)"); Applet::Initialize(); + + const auto data = broker.PeekDataToAppletForDebug(); + system.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..cfa2df369 100644 --- a/src/core/hle/service/am/applets/general_backend.h +++ b/src/core/hle/service/am/applets/general_backend.h @@ -6,8 +6,42 @@ #include "core/hle/service/am/applets/applets.h" +namespace Core { +class System; +} + namespace Service::AM::Applets { +enum class AuthAppletType : u32 { + ShowParentalAuthentication, + RegisterParentalPasscode, + ChangeParentalPasscode, +}; + +class Auth final : public Applet { +public: + explicit Auth(Core::System& system_, 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, @@ -15,7 +49,7 @@ enum class PhotoViewerAppletMode : u8 { class PhotoViewer final : public Applet { public: - explicit PhotoViewer(const Core::Frontend::PhotoViewerApplet& frontend); + explicit PhotoViewer(Core::System& system_, const Core::Frontend::PhotoViewerApplet& frontend_); ~PhotoViewer() override; void Initialize() override; @@ -30,11 +64,12 @@ private: const Core::Frontend::PhotoViewerApplet& frontend; bool complete = false; PhotoViewerAppletMode mode = PhotoViewerAppletMode::CurrentApp; + Core::System& system; }; class StubApplet final : public Applet { public: - StubApplet(); + explicit StubApplet(Core::System& system_, AppletId id_); ~StubApplet() override; void Initialize() override; @@ -43,6 +78,10 @@ public: ResultCode GetStatus() const override; void ExecuteInteractive() override; void Execute() override; + +private: + AppletId id; + Core::System& system; }; } // namespace Service::AM::Applets diff --git a/src/core/hle/service/am/applets/profile_select.cpp b/src/core/hle/service/am/applets/profile_select.cpp index 57b5419e8..3eba696ca 100644 --- a/src/core/hle/service/am/applets/profile_select.cpp +++ b/src/core/hle/service/am/applets/profile_select.cpp @@ -15,8 +15,9 @@ namespace Service::AM::Applets { constexpr ResultCode ERR_USER_CANCELLED_SELECTION{ErrorModule::Account, 1}; -ProfileSelect::ProfileSelect(const Core::Frontend::ProfileSelectApplet& frontend) - : frontend(frontend) {} +ProfileSelect::ProfileSelect(Core::System& system_, + const Core::Frontend::ProfileSelectApplet& frontend_) + : Applet{system_.Kernel()}, frontend(frontend_) {} ProfileSelect::~ProfileSelect() = default; diff --git a/src/core/hle/service/am/applets/profile_select.h b/src/core/hle/service/am/applets/profile_select.h index 563cd744a..16364ead7 100644 --- a/src/core/hle/service/am/applets/profile_select.h +++ b/src/core/hle/service/am/applets/profile_select.h @@ -11,6 +11,10 @@ #include "core/hle/result.h" #include "core/hle/service/am/applets/applets.h" +namespace Core { +class System; +} + namespace Service::AM::Applets { struct UserSelectionConfig { @@ -29,7 +33,8 @@ static_assert(sizeof(UserSelectionOutput) == 0x18, "UserSelectionOutput has inco class ProfileSelect final : public Applet { public: - explicit ProfileSelect(const Core::Frontend::ProfileSelectApplet& frontend); + explicit ProfileSelect(Core::System& system_, + const Core::Frontend::ProfileSelectApplet& frontend_); ~ProfileSelect() override; void Initialize() override; diff --git a/src/core/hle/service/am/applets/software_keyboard.cpp b/src/core/hle/service/am/applets/software_keyboard.cpp index e197990f7..748559cd0 100644 --- a/src/core/hle/service/am/applets/software_keyboard.cpp +++ b/src/core/hle/service/am/applets/software_keyboard.cpp @@ -39,8 +39,9 @@ static Core::Frontend::SoftwareKeyboardParameters ConvertToFrontendParameters( return params; } -SoftwareKeyboard::SoftwareKeyboard(const Core::Frontend::SoftwareKeyboardApplet& frontend) - : frontend(frontend) {} +SoftwareKeyboard::SoftwareKeyboard(Core::System& system_, + const Core::Frontend::SoftwareKeyboardApplet& frontend_) + : Applet{system_.Kernel()}, frontend(frontend_) {} SoftwareKeyboard::~SoftwareKeyboard() = default; diff --git a/src/core/hle/service/am/applets/software_keyboard.h b/src/core/hle/service/am/applets/software_keyboard.h index 0fbc43e51..ef4801fc6 100644 --- a/src/core/hle/service/am/applets/software_keyboard.h +++ b/src/core/hle/service/am/applets/software_keyboard.h @@ -16,6 +16,10 @@ union ResultCode; +namespace Core { +class System; +} + namespace Service::AM::Applets { enum class KeysetDisable : u32 { @@ -55,7 +59,8 @@ static_assert(sizeof(KeyboardConfig) == 0x3E0, "KeyboardConfig has incorrect siz class SoftwareKeyboard final : public Applet { public: - explicit SoftwareKeyboard(const Core::Frontend::SoftwareKeyboardApplet& frontend); + explicit SoftwareKeyboard(Core::System& system_, + const Core::Frontend::SoftwareKeyboardApplet& frontend_); ~SoftwareKeyboard() override; void Initialize() override; diff --git a/src/core/hle/service/am/applets/web_browser.cpp b/src/core/hle/service/am/applets/web_browser.cpp index 7878f5136..32283e819 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,188 @@ 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; + + WebArgTLV tlv{}; + std::memcpy(&tlv, arg.data() + offset, sizeof(WebArgTLV)); + offset += sizeof(WebArgTLV); + + offset += tlv.offset; + if (arg.size() < (offset + tlv.size)) return out; - } - offset += arg.offset + arg.size; + std::vector<u8> data(tlv.size); + std::memcpy(data.data(), arg.data() + offset, tlv.size); + offset += tlv.size; + + out.insert_or_assign(tlv.type, data); } - return {}; + return out; } -static FileSys::VirtualFile GetManualRomFS() { - auto& loader{Core::System::GetInstance().GetAppLoader()}; +FileSys::VirtualFile GetApplicationRomFS(const Core::System& system, u64 title_id, + FileSys::ContentRecordType type) { + const auto& installed{system.GetContentProvider()}; + const auto res = installed.GetEntry(title_id, type); - FileSys::VirtualFile out; - if (loader.ReadManualRomFS(out) == Loader::ResultStatus::Success) - return out; + if (res != nullptr) { + return res->GetRomFS(); + } - const auto& installed{Core::System::GetInstance().GetContentProvider()}; - const auto res = installed.GetEntry(Core::System::GetInstance().CurrentProcess()->GetTitleID(), - FileSys::ContentRecordType::Manual); + if (type == FileSys::ContentRecordType::Data) { + return FileSys::SystemArchive::SynthesizeSystemArchive(title_id); + } - if (res != nullptr) - return res->GetRomFS(); return nullptr; } -WebBrowser::WebBrowser(Core::Frontend::WebBrowserApplet& frontend) : frontend(frontend) {} +} // Anonymous namespace + +WebBrowser::WebBrowser(Core::System& system_, Core::Frontend::WebBrowserApplet& frontend_, + Core::Frontend::ECommerceApplet* frontend_e_commerce_) + : Applet{system_.Kernel()}, frontend(frontend_), + frontend_e_commerce(frontend_e_commerce_), system{system_} {} WebBrowser::~WebBrowser() = default; @@ -111,24 +227,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); - - manual_romfs = GetManualRomFS(); - if (manual_romfs == nullptr) { - status = ResultCode(-1); - LOG_ERROR(Service_AM, "Failed to find manual for current process!"); - } + args = GetWebArguments(web_arg); - filename = - FileUtil::SanitizePath(temporary_dir + DIR_SEP + "html-document" + DIR_SEP + filename, - FileUtil::DirectorySeparator::PlatformDefault); + InitializeInternal(); } bool WebBrowser::TransactionComplete() const { @@ -144,25 +248,26 @@ 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); - const auto& vfs{Core::System::GetInstance().GetFilesystem()}; + FileSys::ExtractRomFS(offline_romfs, FileSys::RomFSExtractionType::SingleDiscard); + const auto& vfs{system.GetFilesystem()}; const auto temp_dir = vfs->CreateDirectory(temporary_dir, FileSys::Mode::ReadWrite); FileSys::VfsRawCopyD(dir, temp_dir); @@ -172,17 +277,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::HtmlDocument; + break; + case OfflineWebSource::ApplicationLegalInformation: + type = FileSys::ContentRecordType::LegalInformation; + break; + case OfflineWebSource::SystemDataPage: + type = FileSys::ContentRecordType::Data; + break; + } + + if (title_id == 0) { + title_id = system.CurrentProcess()->GetTitleID(); + } + + offline_romfs = GetApplicationRomFS(system, 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..8d4027411 100644 --- a/src/core/hle/service/am/applets/web_browser.h +++ b/src/core/hle/service/am/applets/web_browser.h @@ -4,15 +4,26 @@ #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 Core { +class System; +} + 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::System& system_, Core::Frontend::WebBrowserApplet& frontend_, + Core::Frontend::ECommerceApplet* frontend_e_commerce_ = nullptr); + ~WebBrowser() override; void Initialize() override; @@ -32,15 +43,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; + 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; + + Core::System& system; }; } // namespace Service::AM::Applets |