mirror of
				https://git.zaroz.cloud/nintendo-back-up/yuzu/yuzu-mainline.git
				synced 2025-03-21 01:53:15 +00:00 
			
		
		
		
	Merge pull request #2548 from DarkLordZach/applet-shopn
applets: Implement backend and default frontend for Parental Controls and EShop (ShopN) applets
This commit is contained in:
		
						commit
						5829ba1ccc
					
				| @ -7,9 +7,38 @@ | ||||
| 
 | ||||
| namespace Core::Frontend { | ||||
| 
 | ||||
| ParentalControlsApplet::~ParentalControlsApplet() = default; | ||||
| 
 | ||||
| DefaultParentalControlsApplet::~DefaultParentalControlsApplet() = default; | ||||
| 
 | ||||
| void DefaultParentalControlsApplet::VerifyPIN(std::function<void(bool)> finished, | ||||
|                                               bool suspend_future_verification_temporarily) { | ||||
|     LOG_INFO(Service_AM, | ||||
|              "Application requested frontend to verify PIN (normal), " | ||||
|              "suspend_future_verification_temporarily={}, verifying as correct.", | ||||
|              suspend_future_verification_temporarily); | ||||
|     finished(true); | ||||
| } | ||||
| 
 | ||||
| void DefaultParentalControlsApplet::VerifyPINForSettings(std::function<void(bool)> finished) { | ||||
|     LOG_INFO(Service_AM, | ||||
|              "Application requested frontend to verify PIN (settings), verifying as correct."); | ||||
|     finished(true); | ||||
| } | ||||
| 
 | ||||
| void DefaultParentalControlsApplet::RegisterPIN(std::function<void()> finished) { | ||||
|     LOG_INFO(Service_AM, "Application requested frontend to register new PIN"); | ||||
|     finished(); | ||||
| } | ||||
| 
 | ||||
| void DefaultParentalControlsApplet::ChangePIN(std::function<void()> finished) { | ||||
|     LOG_INFO(Service_AM, "Application requested frontend to change PIN to new value"); | ||||
|     finished(); | ||||
| } | ||||
| 
 | ||||
| PhotoViewerApplet::~PhotoViewerApplet() = default; | ||||
| 
 | ||||
| DefaultPhotoViewerApplet::~DefaultPhotoViewerApplet() {} | ||||
| DefaultPhotoViewerApplet::~DefaultPhotoViewerApplet() = default; | ||||
| 
 | ||||
| void DefaultPhotoViewerApplet::ShowPhotosForApplication(u64 title_id, | ||||
|                                                         std::function<void()> finished) const { | ||||
| @ -24,4 +53,72 @@ void DefaultPhotoViewerApplet::ShowAllPhotos(std::function<void()> finished) con | ||||
|     finished(); | ||||
| } | ||||
| 
 | ||||
| ECommerceApplet::~ECommerceApplet() = default; | ||||
| 
 | ||||
| DefaultECommerceApplet::~DefaultECommerceApplet() = default; | ||||
| 
 | ||||
| void DefaultECommerceApplet::ShowApplicationInformation( | ||||
|     std::function<void()> finished, u64 title_id, std::optional<u128> user_id, | ||||
|     std::optional<bool> full_display, std::optional<std::string> extra_parameter) { | ||||
|     const auto value = user_id.value_or(u128{}); | ||||
|     LOG_INFO(Service_AM, | ||||
|              "Application requested frontend show application information for EShop, " | ||||
|              "title_id={:016X}, user_id={:016X}{:016X}, full_display={}, extra_parameter={}", | ||||
|              title_id, value[1], value[0], | ||||
|              full_display.has_value() ? fmt::format("{}", *full_display) : "null", | ||||
|              extra_parameter.value_or("null")); | ||||
|     finished(); | ||||
| } | ||||
| 
 | ||||
| void DefaultECommerceApplet::ShowAddOnContentList(std::function<void()> finished, u64 title_id, | ||||
|                                                   std::optional<u128> user_id, | ||||
|                                                   std::optional<bool> full_display) { | ||||
|     const auto value = user_id.value_or(u128{}); | ||||
|     LOG_INFO(Service_AM, | ||||
|              "Application requested frontend show add on content list for EShop, " | ||||
|              "title_id={:016X}, user_id={:016X}{:016X}, full_display={}", | ||||
|              title_id, value[1], value[0], | ||||
|              full_display.has_value() ? fmt::format("{}", *full_display) : "null"); | ||||
|     finished(); | ||||
| } | ||||
| 
 | ||||
| void DefaultECommerceApplet::ShowSubscriptionList(std::function<void()> finished, u64 title_id, | ||||
|                                                   std::optional<u128> user_id) { | ||||
|     const auto value = user_id.value_or(u128{}); | ||||
|     LOG_INFO(Service_AM, | ||||
|              "Application requested frontend show subscription list for EShop, title_id={:016X}, " | ||||
|              "user_id={:016X}{:016X}", | ||||
|              title_id, value[1], value[0]); | ||||
|     finished(); | ||||
| } | ||||
| 
 | ||||
| void DefaultECommerceApplet::ShowConsumableItemList(std::function<void()> finished, u64 title_id, | ||||
|                                                     std::optional<u128> user_id) { | ||||
|     const auto value = user_id.value_or(u128{}); | ||||
|     LOG_INFO( | ||||
|         Service_AM, | ||||
|         "Application requested frontend show consumable item list for EShop, title_id={:016X}, " | ||||
|         "user_id={:016X}{:016X}", | ||||
|         title_id, value[1], value[0]); | ||||
|     finished(); | ||||
| } | ||||
| 
 | ||||
| void DefaultECommerceApplet::ShowShopHome(std::function<void()> finished, u128 user_id, | ||||
|                                           bool full_display) { | ||||
|     LOG_INFO(Service_AM, | ||||
|              "Application requested frontend show home menu for EShop, user_id={:016X}{:016X}, " | ||||
|              "full_display={}", | ||||
|              user_id[1], user_id[0], full_display); | ||||
|     finished(); | ||||
| } | ||||
| 
 | ||||
| void DefaultECommerceApplet::ShowSettings(std::function<void()> finished, u128 user_id, | ||||
|                                           bool full_display) { | ||||
|     LOG_INFO(Service_AM, | ||||
|              "Application requested frontend show settings menu for EShop, user_id={:016X}{:016X}, " | ||||
|              "full_display={}", | ||||
|              user_id[1], user_id[0], full_display); | ||||
|     finished(); | ||||
| } | ||||
| 
 | ||||
| } // namespace Core::Frontend
 | ||||
|  | ||||
| @ -5,10 +5,43 @@ | ||||
| #pragma once | ||||
| 
 | ||||
| #include <functional> | ||||
| #include <optional> | ||||
| #include "common/common_types.h" | ||||
| 
 | ||||
| namespace Core::Frontend { | ||||
| 
 | ||||
| class ParentalControlsApplet { | ||||
| public: | ||||
|     virtual ~ParentalControlsApplet(); | ||||
| 
 | ||||
|     // Prompts the user to enter a PIN and calls the callback with whether or not it matches the
 | ||||
|     // correct PIN. If the bool is passed, and the PIN was recently entered correctly, the frontend
 | ||||
|     // should not prompt and simply return true.
 | ||||
|     virtual void VerifyPIN(std::function<void(bool)> finished, | ||||
|                            bool suspend_future_verification_temporarily) = 0; | ||||
| 
 | ||||
|     // Prompts the user to enter a PIN and calls the callback for correctness. Frontends can
 | ||||
|     // optionally alert the user that this is to change parental controls settings.
 | ||||
|     virtual void VerifyPINForSettings(std::function<void(bool)> finished) = 0; | ||||
| 
 | ||||
|     // Prompts the user to create a new PIN for pctl and stores it with the service.
 | ||||
|     virtual void RegisterPIN(std::function<void()> finished) = 0; | ||||
| 
 | ||||
|     // Prompts the user to verify the current PIN and then store a new one into pctl.
 | ||||
|     virtual void ChangePIN(std::function<void()> finished) = 0; | ||||
| }; | ||||
| 
 | ||||
| class DefaultParentalControlsApplet final : public ParentalControlsApplet { | ||||
| public: | ||||
|     ~DefaultParentalControlsApplet() override; | ||||
| 
 | ||||
|     void VerifyPIN(std::function<void(bool)> finished, | ||||
|                    bool suspend_future_verification_temporarily) override; | ||||
|     void VerifyPINForSettings(std::function<void(bool)> finished) override; | ||||
|     void RegisterPIN(std::function<void()> finished) override; | ||||
|     void ChangePIN(std::function<void()> finished) override; | ||||
| }; | ||||
| 
 | ||||
| class PhotoViewerApplet { | ||||
| public: | ||||
|     virtual ~PhotoViewerApplet(); | ||||
| @ -25,4 +58,55 @@ public: | ||||
|     void ShowAllPhotos(std::function<void()> finished) const override; | ||||
| }; | ||||
| 
 | ||||
| class ECommerceApplet { | ||||
| public: | ||||
|     virtual ~ECommerceApplet(); | ||||
| 
 | ||||
|     // Shows a page with application icons, description, name, and price.
 | ||||
|     virtual void ShowApplicationInformation(std::function<void()> finished, u64 title_id, | ||||
|                                             std::optional<u128> user_id = {}, | ||||
|                                             std::optional<bool> full_display = {}, | ||||
|                                             std::optional<std::string> extra_parameter = {}) = 0; | ||||
| 
 | ||||
|     // Shows a page with all of the add on content available for a game, with name, description, and
 | ||||
|     // price.
 | ||||
|     virtual void ShowAddOnContentList(std::function<void()> finished, u64 title_id, | ||||
|                                       std::optional<u128> user_id = {}, | ||||
|                                       std::optional<bool> full_display = {}) = 0; | ||||
| 
 | ||||
|     // Shows a page with all of the subscriptions (recurring payments) for a game, with name,
 | ||||
|     // description, price, and renewal period.
 | ||||
|     virtual void ShowSubscriptionList(std::function<void()> finished, u64 title_id, | ||||
|                                       std::optional<u128> user_id = {}) = 0; | ||||
| 
 | ||||
|     // Shows a page with a list of any additional game related purchasable items (DLC,
 | ||||
|     // subscriptions, etc) for a particular game, with name, description, type, and price.
 | ||||
|     virtual void ShowConsumableItemList(std::function<void()> finished, u64 title_id, | ||||
|                                         std::optional<u128> user_id = {}) = 0; | ||||
| 
 | ||||
|     // Shows the home page of the shop.
 | ||||
|     virtual void ShowShopHome(std::function<void()> finished, u128 user_id, bool full_display) = 0; | ||||
| 
 | ||||
|     // Shows the user settings page of the shop.
 | ||||
|     virtual void ShowSettings(std::function<void()> finished, u128 user_id, bool full_display) = 0; | ||||
| }; | ||||
| 
 | ||||
| class DefaultECommerceApplet : public ECommerceApplet { | ||||
| public: | ||||
|     ~DefaultECommerceApplet() override; | ||||
| 
 | ||||
|     void ShowApplicationInformation(std::function<void()> finished, u64 title_id, | ||||
|                                     std::optional<u128> user_id, std::optional<bool> full_display, | ||||
|                                     std::optional<std::string> extra_parameter) override; | ||||
|     void ShowAddOnContentList(std::function<void()> finished, u64 title_id, | ||||
|                               std::optional<u128> user_id, | ||||
|                               std::optional<bool> full_display) override; | ||||
|     void ShowSubscriptionList(std::function<void()> finished, u64 title_id, | ||||
|                               std::optional<u128> user_id) override; | ||||
|     void ShowConsumableItemList(std::function<void()> finished, u64 title_id, | ||||
|                                 std::optional<u128> user_id) override; | ||||
|     void ShowShopHome(std::function<void()> finished, u128 user_id, bool full_display) override; | ||||
|     void ShowSettings(std::function<void()> finished, u128 user_id, bool full_display) override; | ||||
| }; | ||||
| 
 | ||||
| } // namespace Core::Frontend
 | ||||
|  | ||||
| @ -11,9 +11,9 @@ WebBrowserApplet::~WebBrowserApplet() = default; | ||||
| 
 | ||||
| DefaultWebBrowserApplet::~DefaultWebBrowserApplet() = default; | ||||
| 
 | ||||
| void DefaultWebBrowserApplet::OpenPage(std::string_view filename, | ||||
|                                        std::function<void()> unpack_romfs_callback, | ||||
|                                        std::function<void()> finished_callback) { | ||||
| void DefaultWebBrowserApplet::OpenPageLocal(std::string_view filename, | ||||
|                                             std::function<void()> unpack_romfs_callback, | ||||
|                                             std::function<void()> finished_callback) { | ||||
|     LOG_INFO(Service_AM, | ||||
|              "(STUBBED) called - No suitable web browser implementation found to open website page " | ||||
|              "at '{}'!", | ||||
|  | ||||
| @ -13,16 +13,16 @@ class WebBrowserApplet { | ||||
| public: | ||||
|     virtual ~WebBrowserApplet(); | ||||
| 
 | ||||
|     virtual void OpenPage(std::string_view url, std::function<void()> unpack_romfs_callback, | ||||
|                           std::function<void()> finished_callback) = 0; | ||||
|     virtual void OpenPageLocal(std::string_view url, std::function<void()> unpack_romfs_callback, | ||||
|                                std::function<void()> finished_callback) = 0; | ||||
| }; | ||||
| 
 | ||||
| class DefaultWebBrowserApplet final : public WebBrowserApplet { | ||||
| public: | ||||
|     ~DefaultWebBrowserApplet() override; | ||||
| 
 | ||||
|     void OpenPage(std::string_view url, std::function<void()> unpack_romfs_callback, | ||||
|                   std::function<void()> finished_callback) override; | ||||
|     void OpenPageLocal(std::string_view url, std::function<void()> unpack_romfs_callback, | ||||
|                        std::function<void()> finished_callback) override; | ||||
| }; | ||||
| 
 | ||||
| } // namespace Core::Frontend
 | ||||
|  | ||||
| @ -887,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"}, | ||||
| @ -910,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)); | ||||
| @ -1234,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); | ||||
|  | ||||
| @ -201,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> { | ||||
| @ -264,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
 | ||||
|  | ||||
| @ -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"}, | ||||
|  | ||||
| @ -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
 | ||||
|  | ||||
| @ -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"}, | ||||
|     }; | ||||
|  | ||||
| @ -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
 | ||||
|  | ||||
| @ -139,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; | ||||
| 
 | ||||
| @ -157,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) | ||||
| @ -167,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>(); | ||||
|     } | ||||
| @ -198,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: | ||||
| @ -214,8 +228,11 @@ 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.", | ||||
|  | ||||
| @ -13,7 +13,9 @@ | ||||
| union ResultCode; | ||||
| 
 | ||||
| namespace Core::Frontend { | ||||
| class ECommerceApplet; | ||||
| class ErrorApplet; | ||||
| class ParentalControlsApplet; | ||||
| class PhotoViewerApplet; | ||||
| class ProfileSelectApplet; | ||||
| class SoftwareKeyboardApplet; | ||||
| @ -145,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; | ||||
| @ -162,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 { | ||||
| @ -179,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; | ||||
|  | ||||
| @ -17,6 +17,8 @@ | ||||
| 
 | ||||
| namespace Service::AM::Applets { | ||||
| 
 | ||||
| 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()) { | ||||
| @ -35,6 +37,120 @@ static void LogCurrentStorage(AppletDataBroker& broker, std::string_view prefix) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| 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; | ||||
|  | ||||
| @ -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, | ||||
|  | ||||
| @ -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; | ||||
| 
 | ||||
| struct WebBufferHeader { | ||||
|     u16 count; | ||||
|     INSERT_PADDING_BYTES(6); | ||||
| 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, | ||||
| }; | ||||
| static_assert(sizeof(WebBufferHeader) == 0x8, "WebBufferHeader has incorrect size."); | ||||
| 
 | ||||
| struct WebArgumentHeader { | ||||
|     u16 type; | ||||
| 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 { | ||||
| 
 | ||||
| constexpr std::size_t SHIM_KIND_COUNT = 0x8; | ||||
| 
 | ||||
| struct WebArgHeader { | ||||
|     u16 count; | ||||
|     INSERT_PADDING_BYTES(2); | ||||
|     ShimKind kind; | ||||
| }; | ||||
| static_assert(sizeof(WebArgHeader) == 0x8, "WebArgHeader has incorrect size."); | ||||
| 
 | ||||
| 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_assert(sizeof(WebCommonReturnValue) == 0x1010, "WebCommonReturnValue 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)); | ||||
| 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."); | ||||
| 
 | ||||
|     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); | ||||
| struct WebWifiReturnValue { | ||||
|     INSERT_PADDING_BYTES(4); | ||||
|     u32 result; | ||||
| }; | ||||
| static_assert(sizeof(WebWifiReturnValue) == 0x8, "WebWifiReturnValue has incorrect size."); | ||||
| 
 | ||||
|         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()); | ||||
| 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); | ||||
| 
 | ||||
|         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; | ||||
| 
 | ||||
|         out.insert_or_assign(tlv.type, data); | ||||
|     } | ||||
| 
 | ||||
|     return {}; | ||||
|     return out; | ||||
| } | ||||
| 
 | ||||
| static FileSys::VirtualFile GetManualRomFS() { | ||||
|     auto& loader{Core::System::GetInstance().GetAppLoader()}; | ||||
| 
 | ||||
|     FileSys::VirtualFile out; | ||||
|     if (loader.ReadManualRomFS(out) == Loader::ResultStatus::Success) | ||||
|         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
 | ||||
|  | ||||
| @ -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
 | ||||
|  | ||||
| @ -204,7 +204,7 @@ void Init(std::shared_ptr<SM::ServiceManager>& sm, Core::System& system, | ||||
|     SM::ServiceManager::InstallInterfaces(sm); | ||||
| 
 | ||||
|     Account::InstallInterfaces(system); | ||||
|     AM::InstallInterfaces(*sm, nv_flinger); | ||||
|     AM::InstallInterfaces(*sm, nv_flinger, system); | ||||
|     AOC::InstallInterfaces(*sm); | ||||
|     APM::InstallInterfaces(*sm); | ||||
|     Audio::InstallInterfaces(*sm); | ||||
|  | ||||
| @ -87,8 +87,8 @@ QtWebBrowser::QtWebBrowser(GMainWindow& main_window) { | ||||
| 
 | ||||
| QtWebBrowser::~QtWebBrowser() = default; | ||||
| 
 | ||||
| void QtWebBrowser::OpenPage(std::string_view url, std::function<void()> unpack_romfs_callback, | ||||
|                             std::function<void()> finished_callback) { | ||||
| void QtWebBrowser::OpenPageLocal(std::string_view url, std::function<void()> unpack_romfs_callback, | ||||
|                                  std::function<void()> finished_callback) { | ||||
|     this->unpack_romfs_callback = std::move(unpack_romfs_callback); | ||||
|     this->finished_callback = std::move(finished_callback); | ||||
| 
 | ||||
|  | ||||
| @ -37,8 +37,8 @@ public: | ||||
|     explicit QtWebBrowser(GMainWindow& main_window); | ||||
|     ~QtWebBrowser() override; | ||||
| 
 | ||||
|     void OpenPage(std::string_view url, std::function<void()> unpack_romfs_callback, | ||||
|                   std::function<void()> finished_callback) override; | ||||
|     void OpenPageLocal(std::string_view url, std::function<void()> unpack_romfs_callback, | ||||
|                        std::function<void()> finished_callback) override; | ||||
| 
 | ||||
| signals: | ||||
|     void MainWindowOpenPage(std::string_view filename, std::string_view additional_args) const; | ||||
|  | ||||
| @ -814,11 +814,13 @@ bool GMainWindow::LoadROM(const QString& filename) { | ||||
|     system.SetGPUDebugContext(debug_context); | ||||
| 
 | ||||
|     system.SetAppletFrontendSet({ | ||||
|         std::make_unique<QtErrorDisplay>(*this), | ||||
|         nullptr, | ||||
|         std::make_unique<QtProfileSelector>(*this), | ||||
|         std::make_unique<QtSoftwareKeyboard>(*this), | ||||
|         std::make_unique<QtWebBrowser>(*this), | ||||
|         nullptr,                                     // Parental Controls
 | ||||
|         std::make_unique<QtErrorDisplay>(*this),     //
 | ||||
|         nullptr,                                     // Photo Viewer
 | ||||
|         std::make_unique<QtProfileSelector>(*this),  //
 | ||||
|         std::make_unique<QtSoftwareKeyboard>(*this), //
 | ||||
|         std::make_unique<QtWebBrowser>(*this),       //
 | ||||
|         nullptr,                                     // E-Commerce
 | ||||
|     }); | ||||
| 
 | ||||
|     const Core::System::ResultStatus result{system.Load(*render_window, filename.toStdString())}; | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user
	 bunnei
						bunnei