summaryrefslogtreecommitdiff
path: root/src/core
diff options
context:
space:
mode:
authorGravatar bunnei2020-12-21 23:47:10 -0800
committerGravatar GitHub2020-12-21 23:47:10 -0800
commit29ccc7673fc10dee39880fd7bad0ff264765dd56 (patch)
tree234c2a3fca543e580480ed72e37c1ee9f201aa93 /src/core
parentMerge pull request #5131 from bunnei/scheduler-rewrite (diff)
parentapplets/web: Implement the online web browser applet (diff)
downloadyuzu-29ccc7673fc10dee39880fd7bad0ff264765dd56.tar.gz
yuzu-29ccc7673fc10dee39880fd7bad0ff264765dd56.tar.xz
yuzu-29ccc7673fc10dee39880fd7bad0ff264765dd56.zip
Merge pull request #5042 from Morph1984/project-aether
Project Aether: Reimplementation of the Web Browser Applet
Diffstat (limited to 'src/core')
-rw-r--r--src/core/CMakeLists.txt2
-rw-r--r--src/core/frontend/applets/general_frontend.cpp68
-rw-r--r--src/core/frontend/applets/general_frontend.h51
-rw-r--r--src/core/frontend/applets/web_browser.cpp24
-rw-r--r--src/core/frontend/applets/web_browser.h20
-rw-r--r--src/core/frontend/input_interpreter.cpp45
-rw-r--r--src/core/frontend/input_interpreter.h120
-rw-r--r--src/core/hle/service/am/applets/applets.cpp35
-rw-r--r--src/core/hle/service/am/applets/applets.h20
-rw-r--r--src/core/hle/service/am/applets/web_browser.cpp792
-rw-r--r--src/core/hle/service/am/applets/web_browser.h80
-rw-r--r--src/core/hle/service/am/applets/web_types.h178
-rw-r--r--src/core/hle/service/hid/controllers/npad.cpp2
-rw-r--r--src/core/hle/service/hid/controllers/npad.h3
-rw-r--r--src/core/hle/service/ns/ns.cpp11
-rw-r--r--src/core/hle/service/ns/pl_u.cpp30
-rw-r--r--src/core/hle/service/ns/pl_u.h19
17 files changed, 842 insertions, 658 deletions
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index 2dad18e4d..59bd3d2a6 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -135,6 +135,8 @@ add_library(core STATIC
135 frontend/emu_window.h 135 frontend/emu_window.h
136 frontend/framebuffer_layout.cpp 136 frontend/framebuffer_layout.cpp
137 frontend/framebuffer_layout.h 137 frontend/framebuffer_layout.h
138 frontend/input_interpreter.cpp
139 frontend/input_interpreter.h
138 frontend/input.h 140 frontend/input.h
139 hardware_interrupt_manager.cpp 141 hardware_interrupt_manager.cpp
140 hardware_interrupt_manager.h 142 hardware_interrupt_manager.h
diff --git a/src/core/frontend/applets/general_frontend.cpp b/src/core/frontend/applets/general_frontend.cpp
index c30b36de7..7483ffb76 100644
--- a/src/core/frontend/applets/general_frontend.cpp
+++ b/src/core/frontend/applets/general_frontend.cpp
@@ -53,72 +53,4 @@ void DefaultPhotoViewerApplet::ShowAllPhotos(std::function<void()> finished) con
53 finished(); 53 finished();
54} 54}
55 55
56ECommerceApplet::~ECommerceApplet() = default;
57
58DefaultECommerceApplet::~DefaultECommerceApplet() = default;
59
60void DefaultECommerceApplet::ShowApplicationInformation(
61 std::function<void()> finished, u64 title_id, std::optional<u128> user_id,
62 std::optional<bool> full_display, std::optional<std::string> extra_parameter) {
63 const auto value = user_id.value_or(u128{});
64 LOG_INFO(Service_AM,
65 "Application requested frontend show application information for EShop, "
66 "title_id={:016X}, user_id={:016X}{:016X}, full_display={}, extra_parameter={}",
67 title_id, value[1], value[0],
68 full_display.has_value() ? fmt::format("{}", *full_display) : "null",
69 extra_parameter.value_or("null"));
70 finished();
71}
72
73void DefaultECommerceApplet::ShowAddOnContentList(std::function<void()> finished, u64 title_id,
74 std::optional<u128> user_id,
75 std::optional<bool> full_display) {
76 const auto value = user_id.value_or(u128{});
77 LOG_INFO(Service_AM,
78 "Application requested frontend show add on content list for EShop, "
79 "title_id={:016X}, user_id={:016X}{:016X}, full_display={}",
80 title_id, value[1], value[0],
81 full_display.has_value() ? fmt::format("{}", *full_display) : "null");
82 finished();
83}
84
85void DefaultECommerceApplet::ShowSubscriptionList(std::function<void()> finished, u64 title_id,
86 std::optional<u128> user_id) {
87 const auto value = user_id.value_or(u128{});
88 LOG_INFO(Service_AM,
89 "Application requested frontend show subscription list for EShop, title_id={:016X}, "
90 "user_id={:016X}{:016X}",
91 title_id, value[1], value[0]);
92 finished();
93}
94
95void DefaultECommerceApplet::ShowConsumableItemList(std::function<void()> finished, u64 title_id,
96 std::optional<u128> user_id) {
97 const auto value = user_id.value_or(u128{});
98 LOG_INFO(
99 Service_AM,
100 "Application requested frontend show consumable item list for EShop, title_id={:016X}, "
101 "user_id={:016X}{:016X}",
102 title_id, value[1], value[0]);
103 finished();
104}
105
106void DefaultECommerceApplet::ShowShopHome(std::function<void()> finished, u128 user_id,
107 bool full_display) {
108 LOG_INFO(Service_AM,
109 "Application requested frontend show home menu for EShop, user_id={:016X}{:016X}, "
110 "full_display={}",
111 user_id[1], user_id[0], full_display);
112 finished();
113}
114
115void DefaultECommerceApplet::ShowSettings(std::function<void()> finished, u128 user_id,
116 bool full_display) {
117 LOG_INFO(Service_AM,
118 "Application requested frontend show settings menu for EShop, user_id={:016X}{:016X}, "
119 "full_display={}",
120 user_id[1], user_id[0], full_display);
121 finished();
122}
123
124} // namespace Core::Frontend 56} // namespace Core::Frontend
diff --git a/src/core/frontend/applets/general_frontend.h b/src/core/frontend/applets/general_frontend.h
index 4b63f828e..b713b14ee 100644
--- a/src/core/frontend/applets/general_frontend.h
+++ b/src/core/frontend/applets/general_frontend.h
@@ -58,55 +58,4 @@ public:
58 void ShowAllPhotos(std::function<void()> finished) const override; 58 void ShowAllPhotos(std::function<void()> finished) const override;
59}; 59};
60 60
61class ECommerceApplet {
62public:
63 virtual ~ECommerceApplet();
64
65 // Shows a page with application icons, description, name, and price.
66 virtual void ShowApplicationInformation(std::function<void()> finished, u64 title_id,
67 std::optional<u128> user_id = {},
68 std::optional<bool> full_display = {},
69 std::optional<std::string> extra_parameter = {}) = 0;
70
71 // Shows a page with all of the add on content available for a game, with name, description, and
72 // price.
73 virtual void ShowAddOnContentList(std::function<void()> finished, u64 title_id,
74 std::optional<u128> user_id = {},
75 std::optional<bool> full_display = {}) = 0;
76
77 // Shows a page with all of the subscriptions (recurring payments) for a game, with name,
78 // description, price, and renewal period.
79 virtual void ShowSubscriptionList(std::function<void()> finished, u64 title_id,
80 std::optional<u128> user_id = {}) = 0;
81
82 // Shows a page with a list of any additional game related purchasable items (DLC,
83 // subscriptions, etc) for a particular game, with name, description, type, and price.
84 virtual void ShowConsumableItemList(std::function<void()> finished, u64 title_id,
85 std::optional<u128> user_id = {}) = 0;
86
87 // Shows the home page of the shop.
88 virtual void ShowShopHome(std::function<void()> finished, u128 user_id, bool full_display) = 0;
89
90 // Shows the user settings page of the shop.
91 virtual void ShowSettings(std::function<void()> finished, u128 user_id, bool full_display) = 0;
92};
93
94class DefaultECommerceApplet : public ECommerceApplet {
95public:
96 ~DefaultECommerceApplet() override;
97
98 void ShowApplicationInformation(std::function<void()> finished, u64 title_id,
99 std::optional<u128> user_id, std::optional<bool> full_display,
100 std::optional<std::string> extra_parameter) override;
101 void ShowAddOnContentList(std::function<void()> finished, u64 title_id,
102 std::optional<u128> user_id,
103 std::optional<bool> full_display) override;
104 void ShowSubscriptionList(std::function<void()> finished, u64 title_id,
105 std::optional<u128> user_id) override;
106 void ShowConsumableItemList(std::function<void()> finished, u64 title_id,
107 std::optional<u128> user_id) override;
108 void ShowShopHome(std::function<void()> finished, u128 user_id, bool full_display) override;
109 void ShowSettings(std::function<void()> finished, u128 user_id, bool full_display) override;
110};
111
112} // namespace Core::Frontend 61} // namespace Core::Frontend
diff --git a/src/core/frontend/applets/web_browser.cpp b/src/core/frontend/applets/web_browser.cpp
index 528295ffc..50db6a654 100644
--- a/src/core/frontend/applets/web_browser.cpp
+++ b/src/core/frontend/applets/web_browser.cpp
@@ -11,14 +11,22 @@ WebBrowserApplet::~WebBrowserApplet() = default;
11 11
12DefaultWebBrowserApplet::~DefaultWebBrowserApplet() = default; 12DefaultWebBrowserApplet::~DefaultWebBrowserApplet() = default;
13 13
14void DefaultWebBrowserApplet::OpenPageLocal(std::string_view filename, 14void DefaultWebBrowserApplet::OpenLocalWebPage(
15 std::function<void()> unpack_romfs_callback, 15 std::string_view local_url, std::function<void()> extract_romfs_callback,
16 std::function<void()> finished_callback) { 16 std::function<void(Service::AM::Applets::WebExitReason, std::string)> callback) const {
17 LOG_INFO(Service_AM, 17 LOG_WARNING(Service_AM, "(STUBBED) called, backend requested to open local web page at {}",
18 "(STUBBED) called - No suitable web browser implementation found to open website page " 18 local_url);
19 "at '{}'!", 19
20 filename); 20 callback(Service::AM::Applets::WebExitReason::WindowClosed, "http://localhost/");
21 finished_callback(); 21}
22
23void DefaultWebBrowserApplet::OpenExternalWebPage(
24 std::string_view external_url,
25 std::function<void(Service::AM::Applets::WebExitReason, std::string)> callback) const {
26 LOG_WARNING(Service_AM, "(STUBBED) called, backend requested to open external web page at {}",
27 external_url);
28
29 callback(Service::AM::Applets::WebExitReason::WindowClosed, "http://localhost/");
22} 30}
23 31
24} // namespace Core::Frontend 32} // namespace Core::Frontend
diff --git a/src/core/frontend/applets/web_browser.h b/src/core/frontend/applets/web_browser.h
index 110e33bc4..1c5ef19a9 100644
--- a/src/core/frontend/applets/web_browser.h
+++ b/src/core/frontend/applets/web_browser.h
@@ -7,22 +7,34 @@
7#include <functional> 7#include <functional>
8#include <string_view> 8#include <string_view>
9 9
10#include "core/hle/service/am/applets/web_types.h"
11
10namespace Core::Frontend { 12namespace Core::Frontend {
11 13
12class WebBrowserApplet { 14class WebBrowserApplet {
13public: 15public:
14 virtual ~WebBrowserApplet(); 16 virtual ~WebBrowserApplet();
15 17
16 virtual void OpenPageLocal(std::string_view url, std::function<void()> unpack_romfs_callback, 18 virtual void OpenLocalWebPage(
17 std::function<void()> finished_callback) = 0; 19 std::string_view local_url, std::function<void()> extract_romfs_callback,
20 std::function<void(Service::AM::Applets::WebExitReason, std::string)> callback) const = 0;
21
22 virtual void OpenExternalWebPage(
23 std::string_view external_url,
24 std::function<void(Service::AM::Applets::WebExitReason, std::string)> callback) const = 0;
18}; 25};
19 26
20class DefaultWebBrowserApplet final : public WebBrowserApplet { 27class DefaultWebBrowserApplet final : public WebBrowserApplet {
21public: 28public:
22 ~DefaultWebBrowserApplet() override; 29 ~DefaultWebBrowserApplet() override;
23 30
24 void OpenPageLocal(std::string_view url, std::function<void()> unpack_romfs_callback, 31 void OpenLocalWebPage(std::string_view local_url, std::function<void()> extract_romfs_callback,
25 std::function<void()> finished_callback) override; 32 std::function<void(Service::AM::Applets::WebExitReason, std::string)>
33 callback) const override;
34
35 void OpenExternalWebPage(std::string_view external_url,
36 std::function<void(Service::AM::Applets::WebExitReason, std::string)>
37 callback) const override;
26}; 38};
27 39
28} // namespace Core::Frontend 40} // namespace Core::Frontend
diff --git a/src/core/frontend/input_interpreter.cpp b/src/core/frontend/input_interpreter.cpp
new file mode 100644
index 000000000..66ae506cd
--- /dev/null
+++ b/src/core/frontend/input_interpreter.cpp
@@ -0,0 +1,45 @@
1// Copyright 2020 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "core/core.h"
6#include "core/frontend/input_interpreter.h"
7#include "core/hle/service/hid/controllers/npad.h"
8#include "core/hle/service/hid/hid.h"
9#include "core/hle/service/sm/sm.h"
10
11InputInterpreter::InputInterpreter(Core::System& system)
12 : npad{system.ServiceManager()
13 .GetService<Service::HID::Hid>("hid")
14 ->GetAppletResource()
15 ->GetController<Service::HID::Controller_NPad>(Service::HID::HidController::NPad)} {}
16
17InputInterpreter::~InputInterpreter() = default;
18
19void InputInterpreter::PollInput() {
20 const u32 button_state = npad.GetAndResetPressState();
21
22 previous_index = current_index;
23 current_index = (current_index + 1) % button_states.size();
24
25 button_states[current_index] = button_state;
26}
27
28bool InputInterpreter::IsButtonPressedOnce(HIDButton button) const {
29 const bool current_press =
30 (button_states[current_index] & (1U << static_cast<u8>(button))) != 0;
31 const bool previous_press =
32 (button_states[previous_index] & (1U << static_cast<u8>(button))) != 0;
33
34 return current_press && !previous_press;
35}
36
37bool InputInterpreter::IsButtonHeld(HIDButton button) const {
38 u32 held_buttons{button_states[0]};
39
40 for (std::size_t i = 1; i < button_states.size(); ++i) {
41 held_buttons &= button_states[i];
42 }
43
44 return (held_buttons & (1U << static_cast<u8>(button))) != 0;
45}
diff --git a/src/core/frontend/input_interpreter.h b/src/core/frontend/input_interpreter.h
new file mode 100644
index 000000000..fea9aebe6
--- /dev/null
+++ b/src/core/frontend/input_interpreter.h
@@ -0,0 +1,120 @@
1// Copyright 2020 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <array>
8
9#include "common/common_types.h"
10
11namespace Core {
12class System;
13}
14
15namespace Service::HID {
16class Controller_NPad;
17}
18
19enum class HIDButton : u8 {
20 A,
21 B,
22 X,
23 Y,
24 LStick,
25 RStick,
26 L,
27 R,
28 ZL,
29 ZR,
30 Plus,
31 Minus,
32
33 DLeft,
34 DUp,
35 DRight,
36 DDown,
37
38 LStickLeft,
39 LStickUp,
40 LStickRight,
41 LStickDown,
42
43 RStickLeft,
44 RStickUp,
45 RStickRight,
46 RStickDown,
47
48 LeftSL,
49 LeftSR,
50
51 RightSL,
52 RightSR,
53};
54
55/**
56 * The InputInterpreter class interfaces with HID to retrieve button press states.
57 * Input is intended to be polled every 50ms so that a button is considered to be
58 * held down after 400ms has elapsed since the initial button press and subsequent
59 * repeated presses occur every 50ms.
60 */
61class InputInterpreter {
62public:
63 explicit InputInterpreter(Core::System& system);
64 virtual ~InputInterpreter();
65
66 /// Gets a button state from HID and inserts it into the array of button states.
67 void PollInput();
68
69 /**
70 * The specified button is considered to be pressed once
71 * if it is currently pressed and not pressed previously.
72 *
73 * @param button The button to check.
74 *
75 * @returns True when the button is pressed once.
76 */
77 [[nodiscard]] bool IsButtonPressedOnce(HIDButton button) const;
78
79 /**
80 * Checks whether any of the buttons in the parameter list is pressed once.
81 *
82 * @tparam HIDButton The buttons to check.
83 *
84 * @returns True when at least one of the buttons is pressed once.
85 */
86 template <HIDButton... T>
87 [[nodiscard]] bool IsAnyButtonPressedOnce() {
88 return (IsButtonPressedOnce(T) || ...);
89 }
90
91 /**
92 * The specified button is considered to be held down if it is pressed in all 9 button states.
93 *
94 * @param button The button to check.
95 *
96 * @returns True when the button is held down.
97 */
98 [[nodiscard]] bool IsButtonHeld(HIDButton button) const;
99
100 /**
101 * Checks whether any of the buttons in the parameter list is held down.
102 *
103 * @tparam HIDButton The buttons to check.
104 *
105 * @returns True when at least one of the buttons is held down.
106 */
107 template <HIDButton... T>
108 [[nodiscard]] bool IsAnyButtonHeld() {
109 return (IsButtonHeld(T) || ...);
110 }
111
112private:
113 Service::HID::Controller_NPad& npad;
114
115 /// Stores 9 consecutive button states polled from HID.
116 std::array<u32, 9> button_states{};
117
118 std::size_t previous_index{};
119 std::size_t current_index{};
120};
diff --git a/src/core/hle/service/am/applets/applets.cpp b/src/core/hle/service/am/applets/applets.cpp
index 2b626bb40..08676c3fc 100644
--- a/src/core/hle/service/am/applets/applets.cpp
+++ b/src/core/hle/service/am/applets/applets.cpp
@@ -142,14 +142,14 @@ void Applet::Initialize() {
142 142
143AppletFrontendSet::AppletFrontendSet() = default; 143AppletFrontendSet::AppletFrontendSet() = default;
144 144
145AppletFrontendSet::AppletFrontendSet(ControllerApplet controller, ECommerceApplet e_commerce, 145AppletFrontendSet::AppletFrontendSet(ControllerApplet controller_applet, ErrorApplet error_applet,
146 ErrorApplet error, ParentalControlsApplet parental_controls, 146 ParentalControlsApplet parental_controls_applet,
147 PhotoViewer photo_viewer, ProfileSelect profile_select, 147 PhotoViewer photo_viewer_, ProfileSelect profile_select_,
148 SoftwareKeyboard software_keyboard, WebBrowser web_browser) 148 SoftwareKeyboard software_keyboard_, WebBrowser web_browser_)
149 : controller{std::move(controller)}, e_commerce{std::move(e_commerce)}, error{std::move(error)}, 149 : controller{std::move(controller_applet)}, error{std::move(error_applet)},
150 parental_controls{std::move(parental_controls)}, photo_viewer{std::move(photo_viewer)}, 150 parental_controls{std::move(parental_controls_applet)},
151 profile_select{std::move(profile_select)}, software_keyboard{std::move(software_keyboard)}, 151 photo_viewer{std::move(photo_viewer_)}, profile_select{std::move(profile_select_)},
152 web_browser{std::move(web_browser)} {} 152 software_keyboard{std::move(software_keyboard_)}, web_browser{std::move(web_browser_)} {}
153 153
154AppletFrontendSet::~AppletFrontendSet() = default; 154AppletFrontendSet::~AppletFrontendSet() = default;
155 155
@@ -170,10 +170,6 @@ void AppletManager::SetAppletFrontendSet(AppletFrontendSet set) {
170 frontend.controller = std::move(set.controller); 170 frontend.controller = std::move(set.controller);
171 } 171 }
172 172
173 if (set.e_commerce != nullptr) {
174 frontend.e_commerce = std::move(set.e_commerce);
175 }
176
177 if (set.error != nullptr) { 173 if (set.error != nullptr) {
178 frontend.error = std::move(set.error); 174 frontend.error = std::move(set.error);
179 } 175 }
@@ -210,10 +206,6 @@ void AppletManager::SetDefaultAppletsIfMissing() {
210 std::make_unique<Core::Frontend::DefaultControllerApplet>(system.ServiceManager()); 206 std::make_unique<Core::Frontend::DefaultControllerApplet>(system.ServiceManager());
211 } 207 }
212 208
213 if (frontend.e_commerce == nullptr) {
214 frontend.e_commerce = std::make_unique<Core::Frontend::DefaultECommerceApplet>();
215 }
216
217 if (frontend.error == nullptr) { 209 if (frontend.error == nullptr) {
218 frontend.error = std::make_unique<Core::Frontend::DefaultErrorApplet>(); 210 frontend.error = std::make_unique<Core::Frontend::DefaultErrorApplet>();
219 } 211 }
@@ -257,13 +249,14 @@ std::shared_ptr<Applet> AppletManager::GetApplet(AppletId id) const {
257 return std::make_shared<ProfileSelect>(system, *frontend.profile_select); 249 return std::make_shared<ProfileSelect>(system, *frontend.profile_select);
258 case AppletId::SoftwareKeyboard: 250 case AppletId::SoftwareKeyboard:
259 return std::make_shared<SoftwareKeyboard>(system, *frontend.software_keyboard); 251 return std::make_shared<SoftwareKeyboard>(system, *frontend.software_keyboard);
252 case AppletId::Web:
253 case AppletId::Shop:
254 case AppletId::OfflineWeb:
255 case AppletId::LoginShare:
256 case AppletId::WebAuth:
257 return std::make_shared<WebBrowser>(system, *frontend.web_browser);
260 case AppletId::PhotoViewer: 258 case AppletId::PhotoViewer:
261 return std::make_shared<PhotoViewer>(system, *frontend.photo_viewer); 259 return std::make_shared<PhotoViewer>(system, *frontend.photo_viewer);
262 case AppletId::LibAppletShop:
263 return std::make_shared<WebBrowser>(system, *frontend.web_browser,
264 frontend.e_commerce.get());
265 case AppletId::LibAppletOff:
266 return std::make_shared<WebBrowser>(system, *frontend.web_browser);
267 default: 260 default:
268 UNIMPLEMENTED_MSG( 261 UNIMPLEMENTED_MSG(
269 "No backend implementation exists for applet_id={:02X}! Falling back to stub applet.", 262 "No backend implementation exists for applet_id={:02X}! Falling back to stub applet.",
diff --git a/src/core/hle/service/am/applets/applets.h b/src/core/hle/service/am/applets/applets.h
index a1f4cf897..4fd792c05 100644
--- a/src/core/hle/service/am/applets/applets.h
+++ b/src/core/hle/service/am/applets/applets.h
@@ -50,13 +50,13 @@ enum class AppletId : u32 {
50 ProfileSelect = 0x10, 50 ProfileSelect = 0x10,
51 SoftwareKeyboard = 0x11, 51 SoftwareKeyboard = 0x11,
52 MiiEdit = 0x12, 52 MiiEdit = 0x12,
53 LibAppletWeb = 0x13, 53 Web = 0x13,
54 LibAppletShop = 0x14, 54 Shop = 0x14,
55 PhotoViewer = 0x15, 55 PhotoViewer = 0x15,
56 Settings = 0x16, 56 Settings = 0x16,
57 LibAppletOff = 0x17, 57 OfflineWeb = 0x17,
58 LibAppletWhitelisted = 0x18, 58 LoginShare = 0x18,
59 LibAppletAuth = 0x19, 59 WebAuth = 0x19,
60 MyPage = 0x1A, 60 MyPage = 0x1A,
61}; 61};
62 62
@@ -157,7 +157,6 @@ protected:
157 157
158struct AppletFrontendSet { 158struct AppletFrontendSet {
159 using ControllerApplet = std::unique_ptr<Core::Frontend::ControllerApplet>; 159 using ControllerApplet = std::unique_ptr<Core::Frontend::ControllerApplet>;
160 using ECommerceApplet = std::unique_ptr<Core::Frontend::ECommerceApplet>;
161 using ErrorApplet = std::unique_ptr<Core::Frontend::ErrorApplet>; 160 using ErrorApplet = std::unique_ptr<Core::Frontend::ErrorApplet>;
162 using ParentalControlsApplet = std::unique_ptr<Core::Frontend::ParentalControlsApplet>; 161 using ParentalControlsApplet = std::unique_ptr<Core::Frontend::ParentalControlsApplet>;
163 using PhotoViewer = std::unique_ptr<Core::Frontend::PhotoViewerApplet>; 162 using PhotoViewer = std::unique_ptr<Core::Frontend::PhotoViewerApplet>;
@@ -166,10 +165,10 @@ struct AppletFrontendSet {
166 using WebBrowser = std::unique_ptr<Core::Frontend::WebBrowserApplet>; 165 using WebBrowser = std::unique_ptr<Core::Frontend::WebBrowserApplet>;
167 166
168 AppletFrontendSet(); 167 AppletFrontendSet();
169 AppletFrontendSet(ControllerApplet controller, ECommerceApplet e_commerce, ErrorApplet error, 168 AppletFrontendSet(ControllerApplet controller_applet, ErrorApplet error_applet,
170 ParentalControlsApplet parental_controls, PhotoViewer photo_viewer, 169 ParentalControlsApplet parental_controls_applet, PhotoViewer photo_viewer_,
171 ProfileSelect profile_select, SoftwareKeyboard software_keyboard, 170 ProfileSelect profile_select_, SoftwareKeyboard software_keyboard_,
172 WebBrowser web_browser); 171 WebBrowser web_browser_);
173 ~AppletFrontendSet(); 172 ~AppletFrontendSet();
174 173
175 AppletFrontendSet(const AppletFrontendSet&) = delete; 174 AppletFrontendSet(const AppletFrontendSet&) = delete;
@@ -179,7 +178,6 @@ struct AppletFrontendSet {
179 AppletFrontendSet& operator=(AppletFrontendSet&&) noexcept; 178 AppletFrontendSet& operator=(AppletFrontendSet&&) noexcept;
180 179
181 ControllerApplet controller; 180 ControllerApplet controller;
182 ECommerceApplet e_commerce;
183 ErrorApplet error; 181 ErrorApplet error;
184 ParentalControlsApplet parental_controls; 182 ParentalControlsApplet parental_controls;
185 PhotoViewer photo_viewer; 183 PhotoViewer photo_viewer;
diff --git a/src/core/hle/service/am/applets/web_browser.cpp b/src/core/hle/service/am/applets/web_browser.cpp
index c3b6b706a..2ab420789 100644
--- a/src/core/hle/service/am/applets/web_browser.cpp
+++ b/src/core/hle/service/am/applets/web_browser.cpp
@@ -1,558 +1,478 @@
1// Copyright 2018 yuzu emulator team 1// Copyright 2020 yuzu Emulator Project
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <array>
6#include <cstring>
7#include <vector>
8
9#include "common/assert.h" 5#include "common/assert.h"
10#include "common/common_funcs.h"
11#include "common/common_paths.h" 6#include "common/common_paths.h"
12#include "common/file_util.h" 7#include "common/file_util.h"
13#include "common/hex_util.h"
14#include "common/logging/log.h" 8#include "common/logging/log.h"
15#include "common/string_util.h" 9#include "common/string_util.h"
16#include "core/core.h" 10#include "core/core.h"
17#include "core/file_sys/content_archive.h" 11#include "core/file_sys/content_archive.h"
18#include "core/file_sys/mode.h" 12#include "core/file_sys/mode.h"
19#include "core/file_sys/nca_metadata.h" 13#include "core/file_sys/nca_metadata.h"
14#include "core/file_sys/patch_manager.h"
20#include "core/file_sys/registered_cache.h" 15#include "core/file_sys/registered_cache.h"
21#include "core/file_sys/romfs.h" 16#include "core/file_sys/romfs.h"
22#include "core/file_sys/system_archive/system_archive.h" 17#include "core/file_sys/system_archive/system_archive.h"
23#include "core/file_sys/vfs_types.h" 18#include "core/file_sys/vfs_vector.h"
24#include "core/frontend/applets/general_frontend.h"
25#include "core/frontend/applets/web_browser.h" 19#include "core/frontend/applets/web_browser.h"
26#include "core/hle/kernel/process.h" 20#include "core/hle/kernel/process.h"
21#include "core/hle/result.h"
22#include "core/hle/service/am/am.h"
27#include "core/hle/service/am/applets/web_browser.h" 23#include "core/hle/service/am/applets/web_browser.h"
28#include "core/hle/service/filesystem/filesystem.h" 24#include "core/hle/service/filesystem/filesystem.h"
29#include "core/loader/loader.h" 25#include "core/hle/service/ns/pl_u.h"
30 26
31namespace Service::AM::Applets { 27namespace Service::AM::Applets {
32 28
33enum class WebArgTLVType : u16 {
34 InitialURL = 0x1,
35 ShopArgumentsURL = 0x2, ///< TODO(DarkLordZach): This is not the official name.
36 CallbackURL = 0x3,
37 CallbackableURL = 0x4,
38 ApplicationID = 0x5,
39 DocumentPath = 0x6,
40 DocumentKind = 0x7,
41 SystemDataID = 0x8,
42 ShareStartPage = 0x9,
43 Whitelist = 0xA,
44 News = 0xB,
45 UserID = 0xE,
46 AlbumEntry0 = 0xF,
47 ScreenShotEnabled = 0x10,
48 EcClientCertEnabled = 0x11,
49 Unk12 = 0x12,
50 PlayReportEnabled = 0x13,
51 Unk14 = 0x14,
52 Unk15 = 0x15,
53 BootDisplayKind = 0x17,
54 BackgroundKind = 0x18,
55 FooterEnabled = 0x19,
56 PointerEnabled = 0x1A,
57 LeftStickMode = 0x1B,
58 KeyRepeatFrame1 = 0x1C,
59 KeyRepeatFrame2 = 0x1D,
60 BootAsMediaPlayerInv = 0x1E,
61 DisplayUrlKind = 0x1F,
62 BootAsMediaPlayer = 0x21,
63 ShopJumpEnabled = 0x22,
64 MediaAutoPlayEnabled = 0x23,
65 LobbyParameter = 0x24,
66 ApplicationAlbumEntry = 0x26,
67 JsExtensionEnabled = 0x27,
68 AdditionalCommentText = 0x28,
69 TouchEnabledOnContents = 0x29,
70 UserAgentAdditionalString = 0x2A,
71 AdditionalMediaData0 = 0x2B,
72 MediaPlayerAutoCloseEnabled = 0x2C,
73 PageCacheEnabled = 0x2D,
74 WebAudioEnabled = 0x2E,
75 Unk2F = 0x2F,
76 YouTubeVideoWhitelist = 0x31,
77 FooterFixedKind = 0x32,
78 PageFadeEnabled = 0x33,
79 MediaCreatorApplicationRatingAge = 0x34,
80 BootLoadingIconEnabled = 0x35,
81 PageScrollIndicationEnabled = 0x36,
82 MediaPlayerSpeedControlEnabled = 0x37,
83 AlbumEntry1 = 0x38,
84 AlbumEntry2 = 0x39,
85 AlbumEntry3 = 0x3A,
86 AdditionalMediaData1 = 0x3B,
87 AdditionalMediaData2 = 0x3C,
88 AdditionalMediaData3 = 0x3D,
89 BootFooterButton = 0x3E,
90 OverrideWebAudioVolume = 0x3F,
91 OverrideMediaAudioVolume = 0x40,
92 BootMode = 0x41,
93 WebSessionEnabled = 0x42,
94};
95
96enum class ShimKind : u32 {
97 Shop = 1,
98 Login = 2,
99 Offline = 3,
100 Share = 4,
101 Web = 5,
102 Wifi = 6,
103 Lobby = 7,
104};
105
106enum class ShopWebTarget {
107 ApplicationInfo,
108 AddOnContentList,
109 SubscriptionList,
110 ConsumableItemList,
111 Home,
112 Settings,
113};
114
115namespace { 29namespace {
116 30
117constexpr std::size_t SHIM_KIND_COUNT = 0x8; 31template <typename T>
118 32void ParseRawValue(T& value, const std::vector<u8>& data) {
119struct WebArgHeader { 33 static_assert(std::is_trivially_copyable_v<T>,
120 u16 count; 34 "It's undefined behavior to use memcpy with non-trivially copyable objects");
121 INSERT_PADDING_BYTES(2); 35 std::memcpy(&value, data.data(), data.size());
122 ShimKind kind; 36}
123};
124static_assert(sizeof(WebArgHeader) == 0x8, "WebArgHeader has incorrect size.");
125
126struct WebArgTLV {
127 WebArgTLVType type;
128 u16 size;
129 u32 offset;
130};
131static_assert(sizeof(WebArgTLV) == 0x8, "WebArgTLV has incorrect size.");
132
133struct WebCommonReturnValue {
134 u32 result_code;
135 INSERT_PADDING_BYTES(0x4);
136 std::array<char, 0x1000> last_url;
137 u64 last_url_size;
138};
139static_assert(sizeof(WebCommonReturnValue) == 0x1010, "WebCommonReturnValue has incorrect size.");
140
141struct WebWifiPageArg {
142 INSERT_PADDING_BYTES(4);
143 std::array<char, 0x100> connection_test_url;
144 std::array<char, 0x400> initial_url;
145 std::array<u8, 0x10> nifm_network_uuid;
146 u32 nifm_requirement;
147};
148static_assert(sizeof(WebWifiPageArg) == 0x518, "WebWifiPageArg has incorrect size.");
149
150struct WebWifiReturnValue {
151 INSERT_PADDING_BYTES(4);
152 u32 result;
153};
154static_assert(sizeof(WebWifiReturnValue) == 0x8, "WebWifiReturnValue has incorrect size.");
155
156enum class OfflineWebSource : u32 {
157 OfflineHtmlPage = 0x1,
158 ApplicationLegalInformation = 0x2,
159 SystemDataPage = 0x3,
160};
161
162std::map<WebArgTLVType, std::vector<u8>> GetWebArguments(const std::vector<u8>& arg) {
163 if (arg.size() < sizeof(WebArgHeader))
164 return {};
165
166 WebArgHeader header{};
167 std::memcpy(&header, arg.data(), sizeof(WebArgHeader));
168
169 std::map<WebArgTLVType, std::vector<u8>> out;
170 u64 offset = sizeof(WebArgHeader);
171 for (std::size_t i = 0; i < header.count; ++i) {
172 if (arg.size() < (offset + sizeof(WebArgTLV)))
173 return out;
174 37
175 WebArgTLV tlv{}; 38template <typename T>
176 std::memcpy(&tlv, arg.data() + offset, sizeof(WebArgTLV)); 39T ParseRawValue(const std::vector<u8>& data) {
177 offset += sizeof(WebArgTLV); 40 T value;
41 ParseRawValue(value, data);
42 return value;
43}
178 44
179 offset += tlv.offset; 45std::string ParseStringValue(const std::vector<u8>& data) {
180 if (arg.size() < (offset + tlv.size)) 46 return Common::StringFromFixedZeroTerminatedBuffer(reinterpret_cast<const char*>(data.data()),
181 return out; 47 data.size());
48}
182 49
183 std::vector<u8> data(tlv.size); 50std::string GetMainURL(const std::string& url) {
184 std::memcpy(data.data(), arg.data() + offset, tlv.size); 51 const auto index = url.find('?');
185 offset += tlv.size;
186 52
187 out.insert_or_assign(tlv.type, data); 53 if (index == std::string::npos) {
54 return url;
188 } 55 }
189 56
190 return out; 57 return url.substr(0, index);
191} 58}
192 59
193FileSys::VirtualFile GetApplicationRomFS(const Core::System& system, u64 title_id, 60WebArgInputTLVMap ReadWebArgs(const std::vector<u8>& web_arg, WebArgHeader& web_arg_header) {
194 FileSys::ContentRecordType type) { 61 std::memcpy(&web_arg_header, web_arg.data(), sizeof(WebArgHeader));
195 const auto& installed{system.GetContentProvider()};
196 const auto res = installed.GetEntry(title_id, type);
197 62
198 if (res != nullptr) { 63 if (web_arg.size() == sizeof(WebArgHeader)) {
199 return res->GetRomFS(); 64 return {};
200 } 65 }
201 66
202 if (type == FileSys::ContentRecordType::Data) { 67 WebArgInputTLVMap input_tlv_map;
203 return FileSys::SystemArchive::SynthesizeSystemArchive(title_id); 68
69 u64 current_offset = sizeof(WebArgHeader);
70
71 for (std::size_t i = 0; i < web_arg_header.total_tlv_entries; ++i) {
72 if (web_arg.size() < current_offset + sizeof(WebArgInputTLV)) {
73 return input_tlv_map;
74 }
75
76 WebArgInputTLV input_tlv;
77 std::memcpy(&input_tlv, web_arg.data() + current_offset, sizeof(WebArgInputTLV));
78
79 current_offset += sizeof(WebArgInputTLV);
80
81 if (web_arg.size() < current_offset + input_tlv.arg_data_size) {
82 return input_tlv_map;
83 }
84
85 std::vector<u8> data(input_tlv.arg_data_size);
86 std::memcpy(data.data(), web_arg.data() + current_offset, input_tlv.arg_data_size);
87
88 current_offset += input_tlv.arg_data_size;
89
90 input_tlv_map.insert_or_assign(input_tlv.input_tlv_type, std::move(data));
204 } 91 }
205 92
206 return nullptr; 93 return input_tlv_map;
207} 94}
208 95
209} // Anonymous namespace 96FileSys::VirtualFile GetOfflineRomFS(Core::System& system, u64 title_id,
97 FileSys::ContentRecordType nca_type) {
98 if (nca_type == FileSys::ContentRecordType::Data) {
99 const auto nca =
100 system.GetFileSystemController().GetSystemNANDContents()->GetEntry(title_id, nca_type);
101
102 if (nca == nullptr) {
103 LOG_ERROR(Service_AM,
104 "NCA of type={} with title_id={:016X} is not found in the System NAND!",
105 nca_type, title_id);
106 return FileSys::SystemArchive::SynthesizeSystemArchive(title_id);
107 }
210 108
211WebBrowser::WebBrowser(Core::System& system_, Core::Frontend::WebBrowserApplet& frontend_, 109 return nca->GetRomFS();
212 Core::Frontend::ECommerceApplet* frontend_e_commerce_) 110 } else {
213 : Applet{system_.Kernel()}, frontend(frontend_), 111 const auto nca = system.GetContentProvider().GetEntry(title_id, nca_type);
214 frontend_e_commerce(frontend_e_commerce_), system{system_} {}
215 112
216WebBrowser::~WebBrowser() = default; 113 if (nca == nullptr) {
114 LOG_ERROR(Service_AM,
115 "NCA of type={} with title_id={:016X} is not found in the ContentProvider!",
116 nca_type, title_id);
117 return nullptr;
118 }
217 119
218void WebBrowser::Initialize() { 120 const FileSys::PatchManager pm{title_id, system.GetFileSystemController(),
219 Applet::Initialize(); 121 system.GetContentProvider()};
220 122
221 complete = false; 123 return pm.PatchRomFS(nca->GetRomFS(), nca->GetBaseIVFCOffset(), nca_type);
222 temporary_dir.clear(); 124 }
223 filename.clear(); 125}
224 status = RESULT_SUCCESS;
225 126
226 const auto web_arg_storage = broker.PopNormalDataToApplet(); 127void ExtractSharedFonts(Core::System& system) {
227 ASSERT(web_arg_storage != nullptr); 128 static constexpr std::array<const char*, 7> DECRYPTED_SHARED_FONTS{
228 const auto& web_arg = web_arg_storage->GetData(); 129 "FontStandard.ttf",
130 "FontChineseSimplified.ttf",
131 "FontExtendedChineseSimplified.ttf",
132 "FontChineseTraditional.ttf",
133 "FontKorean.ttf",
134 "FontNintendoExtended.ttf",
135 "FontNintendoExtended2.ttf",
136 };
229 137
230 ASSERT(web_arg.size() >= 0x8); 138 for (std::size_t i = 0; i < NS::SHARED_FONTS.size(); ++i) {
231 std::memcpy(&kind, web_arg.data() + 0x4, sizeof(ShimKind)); 139 const auto fonts_dir = Common::FS::SanitizePath(
140 fmt::format("{}/fonts", Common::FS::GetUserPath(Common::FS::UserPath::CacheDir)),
141 Common::FS::DirectorySeparator::PlatformDefault);
232 142
233 args = GetWebArguments(web_arg); 143 const auto font_file_path =
144 Common::FS::SanitizePath(fmt::format("{}/{}", fonts_dir, DECRYPTED_SHARED_FONTS[i]),
145 Common::FS::DirectorySeparator::PlatformDefault);
234 146
235 InitializeInternal(); 147 if (Common::FS::Exists(font_file_path)) {
236} 148 continue;
149 }
237 150
238bool WebBrowser::TransactionComplete() const { 151 const auto font = NS::SHARED_FONTS[i];
239 return complete; 152 const auto font_title_id = static_cast<u64>(font.first);
240}
241 153
242ResultCode WebBrowser::GetStatus() const { 154 const auto nca = system.GetFileSystemController().GetSystemNANDContents()->GetEntry(
243 return status; 155 font_title_id, FileSys::ContentRecordType::Data);
244}
245 156
246void WebBrowser::ExecuteInteractive() { 157 FileSys::VirtualFile romfs;
247 UNIMPLEMENTED_MSG("Unexpected interactive data recieved!");
248}
249 158
250void WebBrowser::Execute() { 159 if (!nca) {
251 if (complete) { 160 romfs = FileSys::SystemArchive::SynthesizeSystemArchive(font_title_id);
252 return; 161 } else {
253 } 162 romfs = nca->GetRomFS();
163 }
254 164
255 if (status != RESULT_SUCCESS) { 165 if (!romfs) {
256 complete = true; 166 LOG_ERROR(Service_AM, "SharedFont RomFS with title_id={:016X} cannot be extracted!",
167 font_title_id);
168 continue;
169 }
257 170
258 // This is a workaround in order not to softlock yuzu when an error happens during the 171 const auto extracted_romfs = FileSys::ExtractRomFS(romfs);
259 // webapplet init. In order to avoid an svcBreak, the status is set to RESULT_SUCCESS
260 Finalize();
261 status = RESULT_SUCCESS;
262 172
263 return; 173 if (!extracted_romfs) {
264 } 174 LOG_ERROR(Service_AM, "SharedFont RomFS with title_id={:016X} failed to extract!",
175 font_title_id);
176 continue;
177 }
265 178
266 ExecuteInternal(); 179 const auto font_file = extracted_romfs->GetFile(font.second);
267}
268 180
269void WebBrowser::UnpackRomFS() { 181 if (!font_file) {
270 if (unpacked) 182 LOG_ERROR(Service_AM, "SharedFont RomFS with title_id={:016X} has no font file \"{}\"!",
271 return; 183 font_title_id, font.second);
184 continue;
185 }
272 186
273 ASSERT(offline_romfs != nullptr); 187 std::vector<u32> font_data_u32(font_file->GetSize() / sizeof(u32));
274 const auto dir = 188 font_file->ReadBytes<u32>(font_data_u32.data(), font_file->GetSize());
275 FileSys::ExtractRomFS(offline_romfs, FileSys::RomFSExtractionType::SingleDiscard);
276 const auto& vfs{system.GetFilesystem()};
277 const auto temp_dir = vfs->CreateDirectory(temporary_dir, FileSys::Mode::ReadWrite);
278 FileSys::VfsRawCopyD(dir, temp_dir);
279 189
280 unpacked = true; 190 std::transform(font_data_u32.begin(), font_data_u32.end(), font_data_u32.begin(),
281} 191 Common::swap32);
282 192
283void WebBrowser::Finalize() { 193 std::vector<u8> decrypted_data(font_file->GetSize() - 8);
284 complete = true;
285 194
286 WebCommonReturnValue out{}; 195 NS::DecryptSharedFontToTTF(font_data_u32, decrypted_data);
287 out.result_code = 0;
288 out.last_url_size = 0;
289 196
290 std::vector<u8> data(sizeof(WebCommonReturnValue)); 197 FileSys::VirtualFile decrypted_font = std::make_shared<FileSys::VectorVfsFile>(
291 std::memcpy(data.data(), &out, sizeof(WebCommonReturnValue)); 198 std::move(decrypted_data), DECRYPTED_SHARED_FONTS[i]);
292 199
293 broker.PushNormalDataFromApplet(std::make_shared<IStorage>(system, std::move(data))); 200 const auto temp_dir =
294 broker.SignalStateChanged(); 201 system.GetFilesystem()->CreateDirectory(fonts_dir, FileSys::Mode::ReadWrite);
202
203 const auto out_file = temp_dir->CreateFile(DECRYPTED_SHARED_FONTS[i]);
295 204
296 if (!temporary_dir.empty() && Common::FS::IsDirectory(temporary_dir)) { 205 FileSys::VfsRawCopy(decrypted_font, out_file);
297 Common::FS::DeleteDirRecursively(temporary_dir);
298 } 206 }
299} 207}
300 208
301void WebBrowser::InitializeInternal() { 209} // namespace
302 using WebAppletInitializer = void (WebBrowser::*)();
303 210
304 constexpr std::array<WebAppletInitializer, SHIM_KIND_COUNT> functions{ 211WebBrowser::WebBrowser(Core::System& system_, const Core::Frontend::WebBrowserApplet& frontend_)
305 nullptr, &WebBrowser::InitializeShop, 212 : Applet{system_.Kernel()}, frontend(frontend_), system{system_} {}
306 nullptr, &WebBrowser::InitializeOffline,
307 nullptr, nullptr,
308 nullptr, nullptr,
309 };
310 213
311 const auto index = static_cast<u32>(kind); 214WebBrowser::~WebBrowser() = default;
312 215
313 if (index > functions.size() || functions[index] == nullptr) { 216void WebBrowser::Initialize() {
314 LOG_ERROR(Service_AM, "Invalid shim_kind={:08X}", index); 217 Applet::Initialize();
315 return;
316 }
317 218
318 const auto function = functions[index]; 219 LOG_INFO(Service_AM, "Initializing Web Browser Applet.");
319 (this->*function)();
320}
321 220
322void WebBrowser::ExecuteInternal() { 221 LOG_DEBUG(Service_AM,
323 using WebAppletExecutor = void (WebBrowser::*)(); 222 "Initializing Applet with common_args: arg_version={}, lib_version={}, "
223 "play_startup_sound={}, size={}, system_tick={}, theme_color={}",
224 common_args.arguments_version, common_args.library_version,
225 common_args.play_startup_sound, common_args.size, common_args.system_tick,
226 common_args.theme_color);
324 227
325 constexpr std::array<WebAppletExecutor, SHIM_KIND_COUNT> functions{ 228 web_applet_version = WebAppletVersion{common_args.library_version};
326 nullptr, &WebBrowser::ExecuteShop,
327 nullptr, &WebBrowser::ExecuteOffline,
328 nullptr, nullptr,
329 nullptr, nullptr,
330 };
331 229
332 const auto index = static_cast<u32>(kind); 230 const auto web_arg_storage = broker.PopNormalDataToApplet();
231 ASSERT(web_arg_storage != nullptr);
333 232
334 if (index > functions.size() || functions[index] == nullptr) { 233 const auto& web_arg = web_arg_storage->GetData();
335 LOG_ERROR(Service_AM, "Invalid shim_kind={:08X}", index); 234 ASSERT_OR_EXECUTE(web_arg.size() >= sizeof(WebArgHeader), { return; });
336 return;
337 }
338 235
339 const auto function = functions[index]; 236 web_arg_input_tlv_map = ReadWebArgs(web_arg, web_arg_header);
340 (this->*function)();
341}
342 237
343void WebBrowser::InitializeShop() { 238 LOG_DEBUG(Service_AM, "WebArgHeader: total_tlv_entries={}, shim_kind={}",
344 if (frontend_e_commerce == nullptr) { 239 web_arg_header.total_tlv_entries, web_arg_header.shim_kind);
345 LOG_ERROR(Service_AM, "Missing ECommerce Applet frontend!");
346 status = RESULT_UNKNOWN;
347 return;
348 }
349 240
350 const auto user_id_data = args.find(WebArgTLVType::UserID); 241 ExtractSharedFonts(system);
351 242
352 user_id = std::nullopt; 243 switch (web_arg_header.shim_kind) {
353 if (user_id_data != args.end()) { 244 case ShimKind::Shop:
354 user_id = u128{}; 245 InitializeShop();
355 std::memcpy(user_id->data(), user_id_data->second.data(), sizeof(u128)); 246 break;
247 case ShimKind::Login:
248 InitializeLogin();
249 break;
250 case ShimKind::Offline:
251 InitializeOffline();
252 break;
253 case ShimKind::Share:
254 InitializeShare();
255 break;
256 case ShimKind::Web:
257 InitializeWeb();
258 break;
259 case ShimKind::Wifi:
260 InitializeWifi();
261 break;
262 case ShimKind::Lobby:
263 InitializeLobby();
264 break;
265 default:
266 UNREACHABLE_MSG("Invalid ShimKind={}", web_arg_header.shim_kind);
267 break;
356 } 268 }
269}
357 270
358 const auto url = args.find(WebArgTLVType::ShopArgumentsURL); 271bool WebBrowser::TransactionComplete() const {
272 return complete;
273}
359 274
360 if (url == args.end()) { 275ResultCode WebBrowser::GetStatus() const {
361 LOG_ERROR(Service_AM, "Missing EShop Arguments URL for initialization!"); 276 return status;
362 status = RESULT_UNKNOWN; 277}
363 return;
364 }
365 278
366 std::vector<std::string> split_query; 279void WebBrowser::ExecuteInteractive() {
367 Common::SplitString(Common::StringFromFixedZeroTerminatedBuffer( 280 UNIMPLEMENTED_MSG("WebSession is not implemented");
368 reinterpret_cast<const char*>(url->second.data()), url->second.size()), 281}
369 '?', split_query);
370
371 // 2 -> Main URL '?' Query Parameters
372 // Less is missing info, More is malformed
373 if (split_query.size() != 2) {
374 LOG_ERROR(Service_AM, "EShop Arguments has more than one question mark, malformed");
375 status = RESULT_UNKNOWN;
376 return;
377 }
378 282
379 std::vector<std::string> queries; 283void WebBrowser::Execute() {
380 Common::SplitString(split_query[1], '&', queries); 284 switch (web_arg_header.shim_kind) {
285 case ShimKind::Shop:
286 ExecuteShop();
287 break;
288 case ShimKind::Login:
289 ExecuteLogin();
290 break;
291 case ShimKind::Offline:
292 ExecuteOffline();
293 break;
294 case ShimKind::Share:
295 ExecuteShare();
296 break;
297 case ShimKind::Web:
298 ExecuteWeb();
299 break;
300 case ShimKind::Wifi:
301 ExecuteWifi();
302 break;
303 case ShimKind::Lobby:
304 ExecuteLobby();
305 break;
306 default:
307 UNREACHABLE_MSG("Invalid ShimKind={}", web_arg_header.shim_kind);
308 WebBrowserExit(WebExitReason::EndButtonPressed);
309 break;
310 }
311}
381 312
382 const auto split_single_query = 313void WebBrowser::ExtractOfflineRomFS() {
383 [](const std::string& in) -> std::pair<std::string, std::string> { 314 LOG_DEBUG(Service_AM, "Extracting RomFS to {}", offline_cache_dir);
384 const auto index = in.find('=');
385 if (index == std::string::npos || index == in.size() - 1) {
386 return {in, ""};
387 }
388 315
389 return {in.substr(0, index), in.substr(index + 1)}; 316 const auto extracted_romfs_dir =
390 }; 317 FileSys::ExtractRomFS(offline_romfs, FileSys::RomFSExtractionType::SingleDiscard);
391 318
392 std::transform(queries.begin(), queries.end(), 319 const auto temp_dir =
393 std::inserter(shop_query, std::next(shop_query.begin())), split_single_query); 320 system.GetFilesystem()->CreateDirectory(offline_cache_dir, FileSys::Mode::ReadWrite);
394 321
395 const auto scene = shop_query.find("scene"); 322 FileSys::VfsRawCopyD(extracted_romfs_dir, temp_dir);
323}
396 324
397 if (scene == shop_query.end()) { 325void WebBrowser::WebBrowserExit(WebExitReason exit_reason, std::string last_url) {
398 LOG_ERROR(Service_AM, "No scene parameter was passed via shop query!"); 326 if ((web_arg_header.shim_kind == ShimKind::Share &&
399 status = RESULT_UNKNOWN; 327 web_applet_version >= WebAppletVersion::Version196608) ||
400 return; 328 (web_arg_header.shim_kind == ShimKind::Web &&
329 web_applet_version >= WebAppletVersion::Version524288)) {
330 // TODO: Push Output TLVs instead of a WebCommonReturnValue
401 } 331 }
402 332
403 const std::map<std::string, ShopWebTarget, std::less<>> target_map{ 333 WebCommonReturnValue web_common_return_value;
404 {"product_detail", ShopWebTarget::ApplicationInfo},
405 {"aocs", ShopWebTarget::AddOnContentList},
406 {"subscriptions", ShopWebTarget::SubscriptionList},
407 {"consumption", ShopWebTarget::ConsumableItemList},
408 {"settings", ShopWebTarget::Settings},
409 {"top", ShopWebTarget::Home},
410 };
411 334
412 const auto target = target_map.find(scene->second); 335 web_common_return_value.exit_reason = exit_reason;
413 if (target == target_map.end()) { 336 std::memcpy(&web_common_return_value.last_url, last_url.data(), last_url.size());
414 LOG_ERROR(Service_AM, "Scene for shop query is invalid! (scene={})", scene->second); 337 web_common_return_value.last_url_size = last_url.size();
415 status = RESULT_UNKNOWN;
416 return;
417 }
418 338
419 shop_web_target = target->second; 339 LOG_DEBUG(Service_AM, "WebCommonReturnValue: exit_reason={}, last_url={}, last_url_size={}",
340 exit_reason, last_url, last_url.size());
420 341
421 const auto title_id_data = shop_query.find("dst_app_id"); 342 complete = true;
422 if (title_id_data != shop_query.end()) { 343 std::vector<u8> out_data(sizeof(WebCommonReturnValue));
423 title_id = std::stoull(title_id_data->second, nullptr, 0x10); 344 std::memcpy(out_data.data(), &web_common_return_value, out_data.size());
424 } 345 broker.PushNormalDataFromApplet(std::make_shared<IStorage>(system, std::move(out_data)));
346 broker.SignalStateChanged();
347}
425 348
426 const auto mode_data = shop_query.find("mode"); 349bool WebBrowser::InputTLVExistsInMap(WebArgInputTLVType input_tlv_type) const {
427 if (mode_data != shop_query.end()) { 350 return web_arg_input_tlv_map.find(input_tlv_type) != web_arg_input_tlv_map.end();
428 shop_full_display = mode_data->second == "full";
429 }
430} 351}
431 352
432void WebBrowser::InitializeOffline() { 353std::optional<std::vector<u8>> WebBrowser::GetInputTLVData(WebArgInputTLVType input_tlv_type) {
433 if (args.find(WebArgTLVType::DocumentPath) == args.end() || 354 const auto map_it = web_arg_input_tlv_map.find(input_tlv_type);
434 args.find(WebArgTLVType::DocumentKind) == args.end() || 355
435 args.find(WebArgTLVType::ApplicationID) == args.end()) { 356 if (map_it == web_arg_input_tlv_map.end()) {
436 status = RESULT_UNKNOWN; 357 return std::nullopt;
437 LOG_ERROR(Service_AM, "Missing necessary parameters for initialization!");
438 } 358 }
439 359
440 const auto url_data = args[WebArgTLVType::DocumentPath]; 360 return map_it->second;
441 filename = Common::StringFromFixedZeroTerminatedBuffer( 361}
442 reinterpret_cast<const char*>(url_data.data()), url_data.size());
443 362
444 OfflineWebSource source; 363void WebBrowser::InitializeShop() {}
445 ASSERT(args[WebArgTLVType::DocumentKind].size() >= 4);
446 std::memcpy(&source, args[WebArgTLVType::DocumentKind].data(), sizeof(OfflineWebSource));
447 364
448 constexpr std::array<const char*, 3> WEB_SOURCE_NAMES{ 365void WebBrowser::InitializeLogin() {}
449 "manual", 366
450 "legal", 367void WebBrowser::InitializeOffline() {
451 "system", 368 const auto document_path =
452 }; 369 ParseStringValue(GetInputTLVData(WebArgInputTLVType::DocumentPath).value());
370
371 const auto document_kind =
372 ParseRawValue<DocumentKind>(GetInputTLVData(WebArgInputTLVType::DocumentKind).value());
373
374 std::string additional_paths;
453 375
454 temporary_dir = 376 switch (document_kind) {
455 Common::FS::SanitizePath(Common::FS::GetUserPath(Common::FS::UserPath::CacheDir) + 377 case DocumentKind::OfflineHtmlPage:
456 "web_applet_" + WEB_SOURCE_NAMES[static_cast<u32>(source) - 1], 378 default:
457 Common::FS::DirectorySeparator::PlatformDefault); 379 title_id = system.CurrentProcess()->GetTitleID();
458 Common::FS::DeleteDirRecursively(temporary_dir); 380 nca_type = FileSys::ContentRecordType::HtmlDocument;
459 381 additional_paths = "html-document";
460 u64 title_id = 0; // 0 corresponds to current process
461 ASSERT(args[WebArgTLVType::ApplicationID].size() >= 0x8);
462 std::memcpy(&title_id, args[WebArgTLVType::ApplicationID].data(), sizeof(u64));
463 FileSys::ContentRecordType type = FileSys::ContentRecordType::Data;
464
465 switch (source) {
466 case OfflineWebSource::OfflineHtmlPage:
467 // While there is an AppID TLV field, in official SW this is always ignored.
468 title_id = 0;
469 type = FileSys::ContentRecordType::HtmlDocument;
470 break; 382 break;
471 case OfflineWebSource::ApplicationLegalInformation: 383 case DocumentKind::ApplicationLegalInformation:
472 type = FileSys::ContentRecordType::LegalInformation; 384 title_id = ParseRawValue<u64>(GetInputTLVData(WebArgInputTLVType::ApplicationID).value());
385 nca_type = FileSys::ContentRecordType::LegalInformation;
473 break; 386 break;
474 case OfflineWebSource::SystemDataPage: 387 case DocumentKind::SystemDataPage:
475 type = FileSys::ContentRecordType::Data; 388 title_id = ParseRawValue<u64>(GetInputTLVData(WebArgInputTLVType::SystemDataID).value());
389 nca_type = FileSys::ContentRecordType::Data;
476 break; 390 break;
477 } 391 }
478 392
479 if (title_id == 0) { 393 static constexpr std::array<const char*, 3> RESOURCE_TYPES{
480 title_id = system.CurrentProcess()->GetTitleID(); 394 "manual",
481 } 395 "legal_information",
396 "system_data",
397 };
482 398
483 offline_romfs = GetApplicationRomFS(system, title_id, type); 399 offline_cache_dir = Common::FS::SanitizePath(
484 if (offline_romfs == nullptr) { 400 fmt::format("{}/offline_web_applet_{}/{:016X}",
485 status = RESULT_UNKNOWN; 401 Common::FS::GetUserPath(Common::FS::UserPath::CacheDir),
486 LOG_ERROR(Service_AM, "Failed to find offline data for request!"); 402 RESOURCE_TYPES[static_cast<u32>(document_kind) - 1], title_id),
487 } 403 Common::FS::DirectorySeparator::PlatformDefault);
488 404
489 std::string path_additional_directory; 405 offline_document = Common::FS::SanitizePath(
490 if (source == OfflineWebSource::OfflineHtmlPage) { 406 fmt::format("{}/{}/{}", offline_cache_dir, additional_paths, document_path),
491 path_additional_directory = std::string(DIR_SEP).append("html-document"); 407 Common::FS::DirectorySeparator::PlatformDefault);
492 } 408}
409
410void WebBrowser::InitializeShare() {}
493 411
494 filename = 412void WebBrowser::InitializeWeb() {
495 Common::FS::SanitizePath(temporary_dir + path_additional_directory + DIR_SEP + filename, 413 external_url = ParseStringValue(GetInputTLVData(WebArgInputTLVType::InitialURL).value());
496 Common::FS::DirectorySeparator::PlatformDefault);
497} 414}
498 415
416void WebBrowser::InitializeWifi() {}
417
418void WebBrowser::InitializeLobby() {}
419
499void WebBrowser::ExecuteShop() { 420void WebBrowser::ExecuteShop() {
500 const auto callback = [this]() { Finalize(); }; 421 LOG_WARNING(Service_AM, "(STUBBED) called, Shop Applet is not implemented");
422 WebBrowserExit(WebExitReason::EndButtonPressed);
423}
501 424
502 const auto check_optional_parameter = [this](const auto& p) { 425void WebBrowser::ExecuteLogin() {
503 if (!p.has_value()) { 426 LOG_WARNING(Service_AM, "(STUBBED) called, Login Applet is not implemented");
504 LOG_ERROR(Service_AM, "Missing one or more necessary parameters for execution!"); 427 WebBrowserExit(WebExitReason::EndButtonPressed);
505 status = RESULT_UNKNOWN; 428}
506 return false;
507 }
508 429
509 return true; 430void WebBrowser::ExecuteOffline() {
510 }; 431 const auto main_url = Common::FS::SanitizePath(GetMainURL(offline_document),
432 Common::FS::DirectorySeparator::PlatformDefault);
511 433
512 switch (shop_web_target) { 434 if (!Common::FS::Exists(main_url)) {
513 case ShopWebTarget::ApplicationInfo: 435 offline_romfs = GetOfflineRomFS(system, title_id, nca_type);
514 if (!check_optional_parameter(title_id)) 436
515 return; 437 if (offline_romfs == nullptr) {
516 frontend_e_commerce->ShowApplicationInformation(callback, *title_id, user_id, 438 LOG_ERROR(Service_AM,
517 shop_full_display, shop_extra_parameter); 439 "RomFS with title_id={:016X} and nca_type={} cannot be extracted!", title_id,
518 break; 440 nca_type);
519 case ShopWebTarget::AddOnContentList: 441 WebBrowserExit(WebExitReason::WindowClosed);
520 if (!check_optional_parameter(title_id))
521 return;
522 frontend_e_commerce->ShowAddOnContentList(callback, *title_id, user_id, shop_full_display);
523 break;
524 case ShopWebTarget::ConsumableItemList:
525 if (!check_optional_parameter(title_id))
526 return;
527 frontend_e_commerce->ShowConsumableItemList(callback, *title_id, user_id);
528 break;
529 case ShopWebTarget::Home:
530 if (!check_optional_parameter(user_id))
531 return;
532 if (!check_optional_parameter(shop_full_display))
533 return;
534 frontend_e_commerce->ShowShopHome(callback, *user_id, *shop_full_display);
535 break;
536 case ShopWebTarget::Settings:
537 if (!check_optional_parameter(user_id))
538 return;
539 if (!check_optional_parameter(shop_full_display))
540 return;
541 frontend_e_commerce->ShowSettings(callback, *user_id, *shop_full_display);
542 break;
543 case ShopWebTarget::SubscriptionList:
544 if (!check_optional_parameter(title_id))
545 return; 442 return;
546 frontend_e_commerce->ShowSubscriptionList(callback, *title_id, user_id); 443 }
547 break;
548 default:
549 UNREACHABLE();
550 } 444 }
445
446 LOG_INFO(Service_AM, "Opening offline document at {}", offline_document);
447
448 frontend.OpenLocalWebPage(
449 offline_document, [this] { ExtractOfflineRomFS(); },
450 [this](WebExitReason exit_reason, std::string last_url) {
451 WebBrowserExit(exit_reason, last_url);
452 });
551} 453}
552 454
553void WebBrowser::ExecuteOffline() { 455void WebBrowser::ExecuteShare() {
554 frontend.OpenPageLocal( 456 LOG_WARNING(Service_AM, "(STUBBED) called, Share Applet is not implemented");
555 filename, [this] { UnpackRomFS(); }, [this] { Finalize(); }); 457 WebBrowserExit(WebExitReason::EndButtonPressed);
458}
459
460void WebBrowser::ExecuteWeb() {
461 LOG_INFO(Service_AM, "Opening external URL at {}", external_url);
462
463 frontend.OpenExternalWebPage(external_url,
464 [this](WebExitReason exit_reason, std::string last_url) {
465 WebBrowserExit(exit_reason, last_url);
466 });
556} 467}
557 468
469void WebBrowser::ExecuteWifi() {
470 LOG_WARNING(Service_AM, "(STUBBED) called, Wifi Applet is not implemented");
471 WebBrowserExit(WebExitReason::EndButtonPressed);
472}
473
474void WebBrowser::ExecuteLobby() {
475 LOG_WARNING(Service_AM, "(STUBBED) called, Lobby Applet is not implemented");
476 WebBrowserExit(WebExitReason::EndButtonPressed);
477}
558} // namespace Service::AM::Applets 478} // 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 8d4027411..04c274754 100644
--- a/src/core/hle/service/am/applets/web_browser.h
+++ b/src/core/hle/service/am/applets/web_browser.h
@@ -1,28 +1,31 @@
1// Copyright 2018 yuzu emulator team 1// Copyright 2020 yuzu Emulator Project
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#pragma once 5#pragma once
6 6
7#include <map> 7#include <optional>
8
9#include "common/common_funcs.h"
10#include "common/common_types.h"
8#include "core/file_sys/vfs_types.h" 11#include "core/file_sys/vfs_types.h"
9#include "core/hle/service/am/am.h" 12#include "core/hle/result.h"
10#include "core/hle/service/am/applets/applets.h" 13#include "core/hle/service/am/applets/applets.h"
14#include "core/hle/service/am/applets/web_types.h"
11 15
12namespace Core { 16namespace Core {
13class System; 17class System;
14} 18}
15 19
16namespace Service::AM::Applets { 20namespace FileSys {
21enum class ContentRecordType : u8;
22}
17 23
18enum class ShimKind : u32; 24namespace Service::AM::Applets {
19enum class ShopWebTarget;
20enum class WebArgTLVType : u16;
21 25
22class WebBrowser final : public Applet { 26class WebBrowser final : public Applet {
23public: 27public:
24 WebBrowser(Core::System& system_, Core::Frontend::WebBrowserApplet& frontend_, 28 WebBrowser(Core::System& system_, const Core::Frontend::WebBrowserApplet& frontend_);
25 Core::Frontend::ECommerceApplet* frontend_e_commerce_ = nullptr);
26 29
27 ~WebBrowser() override; 30 ~WebBrowser() override;
28 31
@@ -33,49 +36,50 @@ public:
33 void ExecuteInteractive() override; 36 void ExecuteInteractive() override;
34 void Execute() override; 37 void Execute() override;
35 38
36 // Callback to be fired when the frontend needs the manual RomFS unpacked to temporary 39 void ExtractOfflineRomFS();
37 // directory. This is a blocking call and may take a while as some manuals can be up to 100MB in
38 // size. Attempting to access files at filename before invocation is likely to not work.
39 void UnpackRomFS();
40 40
41 // Callback to be fired when the frontend is finished browsing. This will delete the temporary 41 void WebBrowserExit(WebExitReason exit_reason, std::string last_url = "");
42 // manual RomFS extracted files, so ensure this is only called at actual finalization.
43 void Finalize();
44 42
45private: 43private:
46 void InitializeInternal(); 44 bool InputTLVExistsInMap(WebArgInputTLVType input_tlv_type) const;
47 void ExecuteInternal();
48 45
49 // Specific initializers for the types of web applets 46 std::optional<std::vector<u8>> GetInputTLVData(WebArgInputTLVType input_tlv_type);
47
48 // Initializers for the various types of browser applets
50 void InitializeShop(); 49 void InitializeShop();
50 void InitializeLogin();
51 void InitializeOffline(); 51 void InitializeOffline();
52 void InitializeShare();
53 void InitializeWeb();
54 void InitializeWifi();
55 void InitializeLobby();
52 56
53 // Specific executors for the types of web applets 57 // Executors for the various types of browser applets
54 void ExecuteShop(); 58 void ExecuteShop();
59 void ExecuteLogin();
55 void ExecuteOffline(); 60 void ExecuteOffline();
61 void ExecuteShare();
62 void ExecuteWeb();
63 void ExecuteWifi();
64 void ExecuteLobby();
56 65
57 Core::Frontend::WebBrowserApplet& frontend; 66 const Core::Frontend::WebBrowserApplet& frontend;
58
59 // Extra frontends for specialized functions
60 Core::Frontend::ECommerceApplet* frontend_e_commerce;
61 67
62 bool complete = false; 68 bool complete{false};
63 bool unpacked = false; 69 ResultCode status{RESULT_SUCCESS};
64 ResultCode status = RESULT_SUCCESS;
65 70
66 ShimKind kind; 71 WebAppletVersion web_applet_version;
67 std::map<WebArgTLVType, std::vector<u8>> args; 72 WebExitReason web_exit_reason;
73 WebArgHeader web_arg_header;
74 WebArgInputTLVMap web_arg_input_tlv_map;
68 75
76 u64 title_id;
77 FileSys::ContentRecordType nca_type;
78 std::string offline_cache_dir;
79 std::string offline_document;
69 FileSys::VirtualFile offline_romfs; 80 FileSys::VirtualFile offline_romfs;
70 std::string temporary_dir; 81
71 std::string filename; 82 std::string external_url;
72
73 ShopWebTarget shop_web_target;
74 std::map<std::string, std::string, std::less<>> shop_query;
75 std::optional<u64> title_id = 0;
76 std::optional<u128> user_id;
77 std::optional<bool> shop_full_display;
78 std::string shop_extra_parameter;
79 83
80 Core::System& system; 84 Core::System& system;
81}; 85};
diff --git a/src/core/hle/service/am/applets/web_types.h b/src/core/hle/service/am/applets/web_types.h
new file mode 100644
index 000000000..419c2bf79
--- /dev/null
+++ b/src/core/hle/service/am/applets/web_types.h
@@ -0,0 +1,178 @@
1// Copyright 2020 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <array>
8#include <unordered_map>
9#include <vector>
10
11#include "common/common_funcs.h"
12#include "common/common_types.h"
13#include "common/swap.h"
14
15namespace Service::AM::Applets {
16
17enum class WebAppletVersion : u32_le {
18 Version0 = 0x0, // Only used by WifiWebAuthApplet
19 Version131072 = 0x20000, // 1.0.0 - 2.3.0
20 Version196608 = 0x30000, // 3.0.0 - 4.1.0
21 Version327680 = 0x50000, // 5.0.0 - 5.1.0
22 Version393216 = 0x60000, // 6.0.0 - 7.0.1
23 Version524288 = 0x80000, // 8.0.0+
24};
25
26enum class ShimKind : u32 {
27 Shop = 1,
28 Login = 2,
29 Offline = 3,
30 Share = 4,
31 Web = 5,
32 Wifi = 6,
33 Lobby = 7,
34};
35
36enum class WebExitReason : u32 {
37 EndButtonPressed = 0,
38 BackButtonPressed = 1,
39 ExitRequested = 2,
40 CallbackURL = 3,
41 WindowClosed = 4,
42 ErrorDialog = 7,
43};
44
45enum class WebArgInputTLVType : u16 {
46 InitialURL = 0x1,
47 CallbackURL = 0x3,
48 CallbackableURL = 0x4,
49 ApplicationID = 0x5,
50 DocumentPath = 0x6,
51 DocumentKind = 0x7,
52 SystemDataID = 0x8,
53 ShareStartPage = 0x9,
54 Whitelist = 0xA,
55 News = 0xB,
56 UserID = 0xE,
57 AlbumEntry0 = 0xF,
58 ScreenShotEnabled = 0x10,
59 EcClientCertEnabled = 0x11,
60 PlayReportEnabled = 0x13,
61 BootDisplayKind = 0x17,
62 BackgroundKind = 0x18,
63 FooterEnabled = 0x19,
64 PointerEnabled = 0x1A,
65 LeftStickMode = 0x1B,
66 KeyRepeatFrame1 = 0x1C,
67 KeyRepeatFrame2 = 0x1D,
68 BootAsMediaPlayerInverted = 0x1E,
69 DisplayURLKind = 0x1F,
70 BootAsMediaPlayer = 0x21,
71 ShopJumpEnabled = 0x22,
72 MediaAutoPlayEnabled = 0x23,
73 LobbyParameter = 0x24,
74 ApplicationAlbumEntry = 0x26,
75 JsExtensionEnabled = 0x27,
76 AdditionalCommentText = 0x28,
77 TouchEnabledOnContents = 0x29,
78 UserAgentAdditionalString = 0x2A,
79 AdditionalMediaData0 = 0x2B,
80 MediaPlayerAutoCloseEnabled = 0x2C,
81 PageCacheEnabled = 0x2D,
82 WebAudioEnabled = 0x2E,
83 YouTubeVideoWhitelist = 0x31,
84 FooterFixedKind = 0x32,
85 PageFadeEnabled = 0x33,
86 MediaCreatorApplicationRatingAge = 0x34,
87 BootLoadingIconEnabled = 0x35,
88 PageScrollIndicatorEnabled = 0x36,
89 MediaPlayerSpeedControlEnabled = 0x37,
90 AlbumEntry1 = 0x38,
91 AlbumEntry2 = 0x39,
92 AlbumEntry3 = 0x3A,
93 AdditionalMediaData1 = 0x3B,
94 AdditionalMediaData2 = 0x3C,
95 AdditionalMediaData3 = 0x3D,
96 BootFooterButton = 0x3E,
97 OverrideWebAudioVolume = 0x3F,
98 OverrideMediaAudioVolume = 0x40,
99 BootMode = 0x41,
100 WebSessionEnabled = 0x42,
101 MediaPlayerOfflineEnabled = 0x43,
102};
103
104enum class WebArgOutputTLVType : u16 {
105 ShareExitReason = 0x1,
106 LastURL = 0x2,
107 LastURLSize = 0x3,
108 SharePostResult = 0x4,
109 PostServiceName = 0x5,
110 PostServiceNameSize = 0x6,
111 PostID = 0x7,
112 PostIDSize = 0x8,
113 MediaPlayerAutoClosedByCompletion = 0x9,
114};
115
116enum class DocumentKind : u32 {
117 OfflineHtmlPage = 1,
118 ApplicationLegalInformation = 2,
119 SystemDataPage = 3,
120};
121
122enum class ShareStartPage : u32 {
123 Default,
124 Settings,
125};
126
127enum class BootDisplayKind : u32 {
128 Default,
129 White,
130 Black,
131};
132
133enum class BackgroundKind : u32 {
134 Default,
135};
136
137enum class LeftStickMode : u32 {
138 Pointer,
139 Cursor,
140};
141
142enum class WebSessionBootMode : u32 {
143 AllForeground,
144 AllForegroundInitiallyHidden,
145};
146
147struct WebArgHeader {
148 u16 total_tlv_entries{};
149 INSERT_PADDING_BYTES(2);
150 ShimKind shim_kind{};
151};
152static_assert(sizeof(WebArgHeader) == 0x8, "WebArgHeader has incorrect size.");
153
154struct WebArgInputTLV {
155 WebArgInputTLVType input_tlv_type{};
156 u16 arg_data_size{};
157 INSERT_PADDING_WORDS(1);
158};
159static_assert(sizeof(WebArgInputTLV) == 0x8, "WebArgInputTLV has incorrect size.");
160
161struct WebArgOutputTLV {
162 WebArgOutputTLVType output_tlv_type{};
163 u16 arg_data_size{};
164 INSERT_PADDING_WORDS(1);
165};
166static_assert(sizeof(WebArgOutputTLV) == 0x8, "WebArgOutputTLV has incorrect size.");
167
168struct WebCommonReturnValue {
169 WebExitReason exit_reason{};
170 INSERT_PADDING_WORDS(1);
171 std::array<char, 0x1000> last_url{};
172 u64 last_url_size{};
173};
174static_assert(sizeof(WebCommonReturnValue) == 0x1010, "WebCommonReturnValue has incorrect size.");
175
176using WebArgInputTLVMap = std::unordered_map<WebArgInputTLVType, std::vector<u8>>;
177
178} // namespace Service::AM::Applets
diff --git a/src/core/hle/service/hid/controllers/npad.cpp b/src/core/hle/service/hid/controllers/npad.cpp
index f6a0770bf..d280e7caf 100644
--- a/src/core/hle/service/hid/controllers/npad.cpp
+++ b/src/core/hle/service/hid/controllers/npad.cpp
@@ -1058,7 +1058,7 @@ void Controller_NPad::ClearAllControllers() {
1058} 1058}
1059 1059
1060u32 Controller_NPad::GetAndResetPressState() { 1060u32 Controller_NPad::GetAndResetPressState() {
1061 return std::exchange(press_state, 0); 1061 return press_state.exchange(0);
1062} 1062}
1063 1063
1064bool Controller_NPad::IsControllerSupported(NPadControllerType controller) const { 1064bool Controller_NPad::IsControllerSupported(NPadControllerType controller) const {
diff --git a/src/core/hle/service/hid/controllers/npad.h b/src/core/hle/service/hid/controllers/npad.h
index 9fac00231..e2e826623 100644
--- a/src/core/hle/service/hid/controllers/npad.h
+++ b/src/core/hle/service/hid/controllers/npad.h
@@ -5,6 +5,7 @@
5#pragma once 5#pragma once
6 6
7#include <array> 7#include <array>
8#include <atomic>
8#include "common/bit_field.h" 9#include "common/bit_field.h"
9#include "common/common_types.h" 10#include "common/common_types.h"
10#include "core/frontend/input.h" 11#include "core/frontend/input.h"
@@ -415,7 +416,7 @@ private:
415 bool IsControllerSupported(NPadControllerType controller) const; 416 bool IsControllerSupported(NPadControllerType controller) const;
416 void RequestPadStateUpdate(u32 npad_id); 417 void RequestPadStateUpdate(u32 npad_id);
417 418
418 u32 press_state{}; 419 std::atomic<u32> press_state{};
419 420
420 NpadStyleSet style{}; 421 NpadStyleSet style{};
421 std::array<NPadEntry, 10> shared_memory_entries{}; 422 std::array<NPadEntry, 10> shared_memory_entries{};
diff --git a/src/core/hle/service/ns/ns.cpp b/src/core/hle/service/ns/ns.cpp
index ef7584641..6ccf8995c 100644
--- a/src/core/hle/service/ns/ns.cpp
+++ b/src/core/hle/service/ns/ns.cpp
@@ -673,7 +673,7 @@ public:
673 explicit NS_VM(Core::System& system_) : ServiceFramework{system_, "ns:vm"} { 673 explicit NS_VM(Core::System& system_) : ServiceFramework{system_, "ns:vm"} {
674 // clang-format off 674 // clang-format off
675 static const FunctionInfo functions[] = { 675 static const FunctionInfo functions[] = {
676 {1200, nullptr, "NeedsUpdateVulnerability"}, 676 {1200, &NS_VM::NeedsUpdateVulnerability, "NeedsUpdateVulnerability"},
677 {1201, nullptr, "UpdateSafeSystemVersionForDebug"}, 677 {1201, nullptr, "UpdateSafeSystemVersionForDebug"},
678 {1202, nullptr, "GetSafeSystemVersion"}, 678 {1202, nullptr, "GetSafeSystemVersion"},
679 }; 679 };
@@ -681,6 +681,15 @@ public:
681 681
682 RegisterHandlers(functions); 682 RegisterHandlers(functions);
683 } 683 }
684
685private:
686 void NeedsUpdateVulnerability(Kernel::HLERequestContext& ctx) {
687 LOG_WARNING(Service_NS, "(STUBBED) called");
688
689 IPC::ResponseBuilder rb{ctx, 3};
690 rb.Push(RESULT_SUCCESS);
691 rb.Push(false);
692 }
684}; 693};
685 694
686void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system) { 695void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system) {
diff --git a/src/core/hle/service/ns/pl_u.cpp b/src/core/hle/service/ns/pl_u.cpp
index c8a215845..71c7587db 100644
--- a/src/core/hle/service/ns/pl_u.cpp
+++ b/src/core/hle/service/ns/pl_u.cpp
@@ -27,29 +27,11 @@
27 27
28namespace Service::NS { 28namespace Service::NS {
29 29
30enum class FontArchives : u64 {
31 Extension = 0x0100000000000810,
32 Standard = 0x0100000000000811,
33 Korean = 0x0100000000000812,
34 ChineseTraditional = 0x0100000000000813,
35 ChineseSimple = 0x0100000000000814,
36};
37
38struct FontRegion { 30struct FontRegion {
39 u32 offset; 31 u32 offset;
40 u32 size; 32 u32 size;
41}; 33};
42 34
43constexpr std::array<std::pair<FontArchives, const char*>, 7> SHARED_FONTS{
44 std::make_pair(FontArchives::Standard, "nintendo_udsg-r_std_003.bfttf"),
45 std::make_pair(FontArchives::ChineseSimple, "nintendo_udsg-r_org_zh-cn_003.bfttf"),
46 std::make_pair(FontArchives::ChineseSimple, "nintendo_udsg-r_ext_zh-cn_003.bfttf"),
47 std::make_pair(FontArchives::ChineseTraditional, "nintendo_udjxh-db_zh-tw_003.bfttf"),
48 std::make_pair(FontArchives::Korean, "nintendo_udsg-r_ko_003.bfttf"),
49 std::make_pair(FontArchives::Extension, "nintendo_ext_003.bfttf"),
50 std::make_pair(FontArchives::Extension, "nintendo_ext2_003.bfttf"),
51};
52
53// The below data is specific to shared font data dumped from Switch on f/w 2.2 35// The below data is specific to shared font data dumped from Switch on f/w 2.2
54// Virtual address and offsets/sizes likely will vary by dump 36// Virtual address and offsets/sizes likely will vary by dump
55[[maybe_unused]] constexpr VAddr SHARED_FONT_MEM_VADDR{0x00000009d3016000ULL}; 37[[maybe_unused]] constexpr VAddr SHARED_FONT_MEM_VADDR{0x00000009d3016000ULL};
@@ -80,6 +62,18 @@ static void DecryptSharedFont(const std::vector<u32>& input, Kernel::PhysicalMem
80 offset += transformed_font.size() * sizeof(u32); 62 offset += transformed_font.size() * sizeof(u32);
81} 63}
82 64
65void DecryptSharedFontToTTF(const std::vector<u32>& input, std::vector<u8>& output) {
66 ASSERT_MSG(input[0] == EXPECTED_MAGIC, "Failed to derive key, unexpected magic number");
67
68 const u32 KEY = input[0] ^ EXPECTED_RESULT; // Derive key using an inverse xor
69 std::vector<u32> transformed_font(input.size());
70 // TODO(ogniK): Figure out a better way to do this
71 std::transform(input.begin(), input.end(), transformed_font.begin(),
72 [&KEY](u32 font_data) { return Common::swap32(font_data ^ KEY); });
73 transformed_font[1] = Common::swap32(transformed_font[1]) ^ KEY; // "re-encrypt" the size
74 std::memcpy(output.data(), transformed_font.data() + 2, transformed_font.size() * sizeof(u32));
75}
76
83void EncryptSharedFont(const std::vector<u32>& input, std::vector<u8>& output, 77void EncryptSharedFont(const std::vector<u32>& input, std::vector<u8>& output,
84 std::size_t& offset) { 78 std::size_t& offset) {
85 ASSERT_MSG(offset + (input.size() * sizeof(u32)) < SHARED_FONT_MEM_SIZE, 79 ASSERT_MSG(offset + (input.size() * sizeof(u32)) < SHARED_FONT_MEM_SIZE,
diff --git a/src/core/hle/service/ns/pl_u.h b/src/core/hle/service/ns/pl_u.h
index 224dcb997..f920c7f69 100644
--- a/src/core/hle/service/ns/pl_u.h
+++ b/src/core/hle/service/ns/pl_u.h
@@ -16,6 +16,25 @@ class FileSystemController;
16 16
17namespace NS { 17namespace NS {
18 18
19enum class FontArchives : u64 {
20 Extension = 0x0100000000000810,
21 Standard = 0x0100000000000811,
22 Korean = 0x0100000000000812,
23 ChineseTraditional = 0x0100000000000813,
24 ChineseSimple = 0x0100000000000814,
25};
26
27constexpr std::array<std::pair<FontArchives, const char*>, 7> SHARED_FONTS{
28 std::make_pair(FontArchives::Standard, "nintendo_udsg-r_std_003.bfttf"),
29 std::make_pair(FontArchives::ChineseSimple, "nintendo_udsg-r_org_zh-cn_003.bfttf"),
30 std::make_pair(FontArchives::ChineseSimple, "nintendo_udsg-r_ext_zh-cn_003.bfttf"),
31 std::make_pair(FontArchives::ChineseTraditional, "nintendo_udjxh-db_zh-tw_003.bfttf"),
32 std::make_pair(FontArchives::Korean, "nintendo_udsg-r_ko_003.bfttf"),
33 std::make_pair(FontArchives::Extension, "nintendo_ext_003.bfttf"),
34 std::make_pair(FontArchives::Extension, "nintendo_ext2_003.bfttf"),
35};
36
37void DecryptSharedFontToTTF(const std::vector<u32>& input, std::vector<u8>& output);
19void EncryptSharedFont(const std::vector<u32>& input, std::vector<u8>& output, std::size_t& offset); 38void EncryptSharedFont(const std::vector<u32>& input, std::vector<u8>& output, std::size_t& offset);
20 39
21class PL_U final : public ServiceFramework<PL_U> { 40class PL_U final : public ServiceFramework<PL_U> {