summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/core/CMakeLists.txt4
-rw-r--r--src/core/frontend/applets/cabinet.cpp20
-rw-r--r--src/core/frontend/applets/cabinet.h37
-rw-r--r--src/core/hle/service/am/applets/applet_cabinet.cpp177
-rw-r--r--src/core/hle/service/am/applets/applet_cabinet.h104
-rw-r--r--src/core/hle/service/am/applets/applets.cpp20
-rw-r--r--src/core/hle/service/am/applets/applets.h12
-rw-r--r--src/core/hle/service/nfp/nfp_device.cpp35
-rw-r--r--src/core/hle/service/nfp/nfp_device.h6
-rw-r--r--src/core/hle/service/nfp/nfp_types.h17
-rw-r--r--src/core/hle/service/nfp/nfp_user.cpp2
-rw-r--r--src/input_common/drivers/virtual_amiibo.cpp15
-rw-r--r--src/input_common/drivers/virtual_amiibo.h3
-rw-r--r--src/input_common/input_engine.h2
-rw-r--r--src/yuzu/CMakeLists.txt3
-rw-r--r--src/yuzu/applets/qt_amiibo_settings.cpp260
-rw-r--r--src/yuzu/applets/qt_amiibo_settings.h83
-rw-r--r--src/yuzu/applets/qt_amiibo_settings.ui494
-rw-r--r--src/yuzu/main.cpp23
-rw-r--r--src/yuzu/main.h9
20 files changed, 1310 insertions, 16 deletions
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index f67f1ce92..740c5b0fd 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -120,6 +120,8 @@ add_library(core STATIC
120 file_sys/vfs_vector.h 120 file_sys/vfs_vector.h
121 file_sys/xts_archive.cpp 121 file_sys/xts_archive.cpp
122 file_sys/xts_archive.h 122 file_sys/xts_archive.h
123 frontend/applets/cabinet.cpp
124 frontend/applets/cabinet.h
123 frontend/applets/controller.cpp 125 frontend/applets/controller.cpp
124 frontend/applets/controller.h 126 frontend/applets/controller.h
125 frontend/applets/error.cpp 127 frontend/applets/error.cpp
@@ -312,6 +314,8 @@ add_library(core STATIC
312 hle/service/am/applet_ae.h 314 hle/service/am/applet_ae.h
313 hle/service/am/applet_oe.cpp 315 hle/service/am/applet_oe.cpp
314 hle/service/am/applet_oe.h 316 hle/service/am/applet_oe.h
317 hle/service/am/applets/applet_cabinet.cpp
318 hle/service/am/applets/applet_cabinet.h
315 hle/service/am/applets/applet_controller.cpp 319 hle/service/am/applets/applet_controller.cpp
316 hle/service/am/applets/applet_controller.h 320 hle/service/am/applets/applet_controller.h
317 hle/service/am/applets/applet_error.cpp 321 hle/service/am/applets/applet_error.cpp
diff --git a/src/core/frontend/applets/cabinet.cpp b/src/core/frontend/applets/cabinet.cpp
new file mode 100644
index 000000000..26c7fefe3
--- /dev/null
+++ b/src/core/frontend/applets/cabinet.cpp
@@ -0,0 +1,20 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "common/logging/log.h"
5#include "core/frontend/applets/cabinet.h"
6
7#include <thread>
8
9namespace Core::Frontend {
10
11CabinetApplet::~CabinetApplet() = default;
12
13void DefaultCabinetApplet::ShowCabinetApplet(
14 const CabinetCallback& callback, const CabinetParameters& parameters,
15 std::shared_ptr<Service::NFP::NfpDevice> nfp_device) const {
16 LOG_WARNING(Service_AM, "(STUBBED) called");
17 callback(false, {});
18}
19
20} // namespace Core::Frontend
diff --git a/src/core/frontend/applets/cabinet.h b/src/core/frontend/applets/cabinet.h
new file mode 100644
index 000000000..c28a235c1
--- /dev/null
+++ b/src/core/frontend/applets/cabinet.h
@@ -0,0 +1,37 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <functional>
7#include "core/hle/service/nfp/nfp_types.h"
8
9namespace Service::NFP {
10class NfpDevice;
11} // namespace Service::NFP
12
13namespace Core::Frontend {
14
15struct CabinetParameters {
16 Service::NFP::TagInfo tag_info;
17 Service::NFP::RegisterInfo register_info;
18 Service::NFP::CabinetMode mode;
19};
20
21using CabinetCallback = std::function<void(bool, const std::string&)>;
22
23class CabinetApplet {
24public:
25 virtual ~CabinetApplet();
26 virtual void ShowCabinetApplet(const CabinetCallback& callback,
27 const CabinetParameters& parameters,
28 std::shared_ptr<Service::NFP::NfpDevice> nfp_device) const = 0;
29};
30
31class DefaultCabinetApplet final : public CabinetApplet {
32public:
33 void ShowCabinetApplet(const CabinetCallback& callback, const CabinetParameters& parameters,
34 std::shared_ptr<Service::NFP::NfpDevice> nfp_device) const override;
35};
36
37} // namespace Core::Frontend
diff --git a/src/core/hle/service/am/applets/applet_cabinet.cpp b/src/core/hle/service/am/applets/applet_cabinet.cpp
new file mode 100644
index 000000000..d0969b0f1
--- /dev/null
+++ b/src/core/hle/service/am/applets/applet_cabinet.cpp
@@ -0,0 +1,177 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "common/assert.h"
5#include "common/logging/log.h"
6#include "core/core.h"
7#include "core/frontend/applets/cabinet.h"
8#include "core/hid/hid_core.h"
9#include "core/hle/kernel/k_event.h"
10#include "core/hle/kernel/k_readable_event.h"
11#include "core/hle/service/am/am.h"
12#include "core/hle/service/am/applets/applet_cabinet.h"
13#include "core/hle/service/mii/mii_manager.h"
14#include "core/hle/service/nfp/nfp_device.h"
15
16namespace Service::AM::Applets {
17
18Cabinet::Cabinet(Core::System& system_, LibraryAppletMode applet_mode_,
19 const Core::Frontend::CabinetApplet& frontend_)
20 : Applet{system_, applet_mode_}, frontend{frontend_}, system{system_}, service_context{
21 system_,
22 "CabinetApplet"} {
23
24 availability_change_event =
25 service_context.CreateEvent("CabinetApplet:AvailabilityChangeEvent");
26}
27
28Cabinet::~Cabinet() = default;
29
30void Cabinet::Initialize() {
31 Applet::Initialize();
32
33 LOG_INFO(Service_HID, "Initializing Cabinet Applet.");
34
35 LOG_DEBUG(Service_HID,
36 "Initializing Applet with common_args: arg_version={}, lib_version={}, "
37 "play_startup_sound={}, size={}, system_tick={}, theme_color={}",
38 common_args.arguments_version, common_args.library_version,
39 common_args.play_startup_sound, common_args.size, common_args.system_tick,
40 common_args.theme_color);
41
42 const auto storage = broker.PopNormalDataToApplet();
43 ASSERT(storage != nullptr);
44
45 const auto applet_input_data = storage->GetData();
46 ASSERT(applet_input_data.size() >= sizeof(StartParamForAmiiboSettings));
47
48 std::memcpy(&applet_input_common, applet_input_data.data(),
49 sizeof(StartParamForAmiiboSettings));
50}
51
52bool Cabinet::TransactionComplete() const {
53 return is_complete;
54}
55
56Result Cabinet::GetStatus() const {
57 return ResultSuccess;
58}
59
60void Cabinet::ExecuteInteractive() {
61 ASSERT_MSG(false, "Attempted to call interactive execution on non-interactive applet.");
62}
63
64void Cabinet::Execute() {
65 if (is_complete) {
66 return;
67 }
68
69 const auto callback = [this](bool apply_changes, const std::string& amiibo_name) {
70 DisplayCompleted(apply_changes, amiibo_name);
71 };
72
73 // TODO: listen on all controllers
74 if (nfp_device == nullptr) {
75 nfp_device = std::make_shared<Service::NFP::NfpDevice>(
76 system.HIDCore().GetFirstNpadId(), system, service_context, availability_change_event);
77 nfp_device->Initialize();
78 nfp_device->StartDetection(Service::NFP::TagProtocol::All);
79 }
80
81 const Core::Frontend::CabinetParameters parameters{
82 .tag_info = applet_input_common.tag_info,
83 .register_info = applet_input_common.register_info,
84 .mode = applet_input_common.applet_mode,
85 };
86
87 switch (applet_input_common.applet_mode) {
88 case Service::NFP::CabinetMode::StartNicknameAndOwnerSettings:
89 case Service::NFP::CabinetMode::StartGameDataEraser:
90 case Service::NFP::CabinetMode::StartRestorer:
91 case Service::NFP::CabinetMode::StartFormatter:
92 frontend.ShowCabinetApplet(callback, parameters, nfp_device);
93 break;
94 default:
95 UNIMPLEMENTED_MSG("Unknown CabinetMode={}", applet_input_common.applet_mode);
96 DisplayCompleted(false, {});
97 break;
98 }
99}
100
101void Cabinet::DisplayCompleted(bool apply_changes, std::string_view amiibo_name) {
102 Service::Mii::MiiManager manager;
103 ReturnValueForAmiiboSettings applet_output{};
104
105 if (!apply_changes) {
106 Cancel();
107 }
108
109 if (nfp_device->GetCurrentState() != Service::NFP::DeviceState::TagFound &&
110 nfp_device->GetCurrentState() != Service::NFP::DeviceState::TagMounted) {
111 Cancel();
112 }
113
114 if (nfp_device->GetCurrentState() == Service::NFP::DeviceState::TagFound) {
115 nfp_device->Mount(Service::NFP::MountTarget::All);
116 }
117
118 switch (applet_input_common.applet_mode) {
119 case Service::NFP::CabinetMode::StartNicknameAndOwnerSettings: {
120 Service::NFP::AmiiboName name{};
121 std::memcpy(name.data(), amiibo_name.data(), std::min(amiibo_name.size(), name.size() - 1));
122 nfp_device->SetNicknameAndOwner(name);
123 break;
124 }
125 case Service::NFP::CabinetMode::StartGameDataEraser:
126 nfp_device->DeleteApplicationArea();
127 break;
128 case Service::NFP::CabinetMode::StartRestorer:
129 nfp_device->RestoreAmiibo();
130 break;
131 case Service::NFP::CabinetMode::StartFormatter:
132 nfp_device->DeleteAllData();
133 break;
134 default:
135 UNIMPLEMENTED_MSG("Unknown CabinetMode={}", applet_input_common.applet_mode);
136 break;
137 }
138
139 applet_output.device_handle = applet_input_common.device_handle;
140 applet_output.result = CabinetResult::Cancel;
141 const auto reg_result = nfp_device->GetRegisterInfo(applet_output.register_info);
142 const auto tag_result = nfp_device->GetTagInfo(applet_output.tag_info);
143 nfp_device->Finalize();
144
145 if (reg_result.IsSuccess()) {
146 applet_output.result |= CabinetResult::RegisterInfo;
147 }
148
149 if (tag_result.IsSuccess()) {
150 applet_output.result |= CabinetResult::TagInfo;
151 }
152
153 std::vector<u8> out_data(sizeof(ReturnValueForAmiiboSettings));
154 std::memcpy(out_data.data(), &applet_output, sizeof(ReturnValueForAmiiboSettings));
155
156 is_complete = true;
157
158 broker.PushNormalDataFromApplet(std::make_shared<IStorage>(system, std::move(out_data)));
159 broker.SignalStateChanged();
160}
161
162void Cabinet::Cancel() {
163 ReturnValueForAmiiboSettings applet_output{};
164 applet_output.device_handle = applet_input_common.device_handle;
165 applet_output.result = CabinetResult::Cancel;
166 nfp_device->Finalize();
167
168 std::vector<u8> out_data(sizeof(ReturnValueForAmiiboSettings));
169 std::memcpy(out_data.data(), &applet_output, sizeof(ReturnValueForAmiiboSettings));
170
171 is_complete = true;
172
173 broker.PushNormalDataFromApplet(std::make_shared<IStorage>(system, std::move(out_data)));
174 broker.SignalStateChanged();
175}
176
177} // namespace Service::AM::Applets
diff --git a/src/core/hle/service/am/applets/applet_cabinet.h b/src/core/hle/service/am/applets/applet_cabinet.h
new file mode 100644
index 000000000..84197a807
--- /dev/null
+++ b/src/core/hle/service/am/applets/applet_cabinet.h
@@ -0,0 +1,104 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <array>
7
8#include "core/hle/result.h"
9#include "core/hle/service/am/applets/applets.h"
10#include "core/hle/service/kernel_helpers.h"
11#include "core/hle/service/nfp/nfp_types.h"
12
13namespace Kernel {
14class KEvent;
15class KReadableEvent;
16} // namespace Kernel
17
18namespace Core {
19class System;
20} // namespace Core
21
22namespace Service::NFP {
23class NfpDevice;
24}
25
26namespace Service::AM::Applets {
27
28enum class CabinetAppletVersion : u32 {
29 Version1 = 0x1,
30};
31
32enum class CabinetResult : u8 {
33 Cancel = 0,
34 TagInfo = 1 << 1,
35 RegisterInfo = 1 << 2,
36 All = TagInfo | RegisterInfo,
37};
38DECLARE_ENUM_FLAG_OPERATORS(CabinetResult)
39
40// This is nn::nfp::AmiiboSettingsStartParam
41struct AmiiboSettingsStartParam {
42 u64 device_handle;
43 std::array<u8, 0x20> param_1;
44 u8 param_2;
45};
46static_assert(sizeof(AmiiboSettingsStartParam) == 0x30,
47 "AmiiboSettingsStartParam is an invalid size");
48
49#pragma pack(push, 1)
50// This is nn::nfp::StartParamForAmiiboSettings
51struct StartParamForAmiiboSettings {
52 u8 param_1;
53 Service::NFP::CabinetMode applet_mode;
54 u8 flags;
55 u8 amiibo_settings_1;
56 u64 device_handle;
57 Service::NFP::TagInfo tag_info;
58 Service::NFP::RegisterInfo register_info;
59 std::array<u8, 0x20> amiibo_settings_3;
60 INSERT_PADDING_BYTES(0x24);
61};
62static_assert(sizeof(StartParamForAmiiboSettings) == 0x1A8,
63 "StartParamForAmiiboSettings is an invalid size");
64
65// This is nn::nfp::ReturnValueForAmiiboSettings
66struct ReturnValueForAmiiboSettings {
67 CabinetResult result;
68 INSERT_PADDING_BYTES(0x3);
69 u64 device_handle;
70 Service::NFP::TagInfo tag_info;
71 Service::NFP::RegisterInfo register_info;
72 INSERT_PADDING_BYTES(0x24);
73};
74static_assert(sizeof(ReturnValueForAmiiboSettings) == 0x188,
75 "ReturnValueForAmiiboSettings is an invalid size");
76#pragma pack(pop)
77
78class Cabinet final : public Applet {
79public:
80 explicit Cabinet(Core::System& system_, LibraryAppletMode applet_mode_,
81 const Core::Frontend::CabinetApplet& frontend_);
82 ~Cabinet() override;
83
84 void Initialize() override;
85
86 bool TransactionComplete() const override;
87 Result GetStatus() const override;
88 void ExecuteInteractive() override;
89 void Execute() override;
90 void DisplayCompleted(bool apply_changes, std::string_view amiibo_name);
91 void Cancel();
92
93private:
94 const Core::Frontend::CabinetApplet& frontend;
95 Core::System& system;
96
97 bool is_complete{false};
98 std::shared_ptr<Service::NFP::NfpDevice> nfp_device;
99 Kernel::KEvent* availability_change_event;
100 KernelHelpers::ServiceContext service_context;
101 StartParamForAmiiboSettings applet_input_common{};
102};
103
104} // namespace Service::AM::Applets
diff --git a/src/core/hle/service/am/applets/applets.cpp b/src/core/hle/service/am/applets/applets.cpp
index 7062df21c..10afbc2da 100644
--- a/src/core/hle/service/am/applets/applets.cpp
+++ b/src/core/hle/service/am/applets/applets.cpp
@@ -5,6 +5,7 @@
5 5
6#include "common/assert.h" 6#include "common/assert.h"
7#include "core/core.h" 7#include "core/core.h"
8#include "core/frontend/applets/cabinet.h"
8#include "core/frontend/applets/controller.h" 9#include "core/frontend/applets/controller.h"
9#include "core/frontend/applets/error.h" 10#include "core/frontend/applets/error.h"
10#include "core/frontend/applets/general_frontend.h" 11#include "core/frontend/applets/general_frontend.h"
@@ -16,6 +17,7 @@
16#include "core/hle/service/am/am.h" 17#include "core/hle/service/am/am.h"
17#include "core/hle/service/am/applet_ae.h" 18#include "core/hle/service/am/applet_ae.h"
18#include "core/hle/service/am/applet_oe.h" 19#include "core/hle/service/am/applet_oe.h"
20#include "core/hle/service/am/applets/applet_cabinet.h"
19#include "core/hle/service/am/applets/applet_controller.h" 21#include "core/hle/service/am/applets/applet_controller.h"
20#include "core/hle/service/am/applets/applet_error.h" 22#include "core/hle/service/am/applets/applet_error.h"
21#include "core/hle/service/am/applets/applet_general_backend.h" 23#include "core/hle/service/am/applets/applet_general_backend.h"
@@ -171,13 +173,15 @@ void Applet::Initialize() {
171 173
172AppletFrontendSet::AppletFrontendSet() = default; 174AppletFrontendSet::AppletFrontendSet() = default;
173 175
174AppletFrontendSet::AppletFrontendSet(ControllerApplet controller_applet, ErrorApplet error_applet, 176AppletFrontendSet::AppletFrontendSet(CabinetApplet cabinet_applet,
177 ControllerApplet controller_applet, ErrorApplet error_applet,
175 MiiEdit mii_edit_, 178 MiiEdit mii_edit_,
176 ParentalControlsApplet parental_controls_applet, 179 ParentalControlsApplet parental_controls_applet,
177 PhotoViewer photo_viewer_, ProfileSelect profile_select_, 180 PhotoViewer photo_viewer_, ProfileSelect profile_select_,
178 SoftwareKeyboard software_keyboard_, WebBrowser web_browser_) 181 SoftwareKeyboard software_keyboard_, WebBrowser web_browser_)
179 : controller{std::move(controller_applet)}, error{std::move(error_applet)}, 182 : cabinet{std::move(cabinet_applet)}, controller{std::move(controller_applet)},
180 mii_edit{std::move(mii_edit_)}, parental_controls{std::move(parental_controls_applet)}, 183 error{std::move(error_applet)}, mii_edit{std::move(mii_edit_)},
184 parental_controls{std::move(parental_controls_applet)},
181 photo_viewer{std::move(photo_viewer_)}, profile_select{std::move(profile_select_)}, 185 photo_viewer{std::move(photo_viewer_)}, profile_select{std::move(profile_select_)},
182 software_keyboard{std::move(software_keyboard_)}, web_browser{std::move(web_browser_)} {} 186 software_keyboard{std::move(software_keyboard_)}, web_browser{std::move(web_browser_)} {}
183 187
@@ -196,6 +200,10 @@ const AppletFrontendSet& AppletManager::GetAppletFrontendSet() const {
196} 200}
197 201
198void AppletManager::SetAppletFrontendSet(AppletFrontendSet set) { 202void AppletManager::SetAppletFrontendSet(AppletFrontendSet set) {
203 if (set.cabinet != nullptr) {
204 frontend.cabinet = std::move(set.cabinet);
205 }
206
199 if (set.controller != nullptr) { 207 if (set.controller != nullptr) {
200 frontend.controller = std::move(set.controller); 208 frontend.controller = std::move(set.controller);
201 } 209 }
@@ -235,6 +243,10 @@ void AppletManager::SetDefaultAppletFrontendSet() {
235} 243}
236 244
237void AppletManager::SetDefaultAppletsIfMissing() { 245void AppletManager::SetDefaultAppletsIfMissing() {
246 if (frontend.cabinet == nullptr) {
247 frontend.cabinet = std::make_unique<Core::Frontend::DefaultCabinetApplet>();
248 }
249
238 if (frontend.controller == nullptr) { 250 if (frontend.controller == nullptr) {
239 frontend.controller = 251 frontend.controller =
240 std::make_unique<Core::Frontend::DefaultControllerApplet>(system.HIDCore()); 252 std::make_unique<Core::Frontend::DefaultControllerApplet>(system.HIDCore());
@@ -279,6 +291,8 @@ std::shared_ptr<Applet> AppletManager::GetApplet(AppletId id, LibraryAppletMode
279 switch (id) { 291 switch (id) {
280 case AppletId::Auth: 292 case AppletId::Auth:
281 return std::make_shared<Auth>(system, mode, *frontend.parental_controls); 293 return std::make_shared<Auth>(system, mode, *frontend.parental_controls);
294 case AppletId::Cabinet:
295 return std::make_shared<Cabinet>(system, mode, *frontend.cabinet);
282 case AppletId::Controller: 296 case AppletId::Controller:
283 return std::make_shared<Controller>(system, mode, *frontend.controller); 297 return std::make_shared<Controller>(system, mode, *frontend.controller);
284 case AppletId::Error: 298 case AppletId::Error:
diff --git a/src/core/hle/service/am/applets/applets.h b/src/core/hle/service/am/applets/applets.h
index 12c6a5b1a..a22eb62a8 100644
--- a/src/core/hle/service/am/applets/applets.h
+++ b/src/core/hle/service/am/applets/applets.h
@@ -16,6 +16,7 @@ class System;
16} 16}
17 17
18namespace Core::Frontend { 18namespace Core::Frontend {
19class CabinetApplet;
19class ControllerApplet; 20class ControllerApplet;
20class ECommerceApplet; 21class ECommerceApplet;
21class ErrorApplet; 22class ErrorApplet;
@@ -176,6 +177,7 @@ protected:
176}; 177};
177 178
178struct AppletFrontendSet { 179struct AppletFrontendSet {
180 using CabinetApplet = std::unique_ptr<Core::Frontend::CabinetApplet>;
179 using ControllerApplet = std::unique_ptr<Core::Frontend::ControllerApplet>; 181 using ControllerApplet = std::unique_ptr<Core::Frontend::ControllerApplet>;
180 using ErrorApplet = std::unique_ptr<Core::Frontend::ErrorApplet>; 182 using ErrorApplet = std::unique_ptr<Core::Frontend::ErrorApplet>;
181 using MiiEdit = std::unique_ptr<Core::Frontend::MiiEditApplet>; 183 using MiiEdit = std::unique_ptr<Core::Frontend::MiiEditApplet>;
@@ -186,10 +188,11 @@ struct AppletFrontendSet {
186 using WebBrowser = std::unique_ptr<Core::Frontend::WebBrowserApplet>; 188 using WebBrowser = std::unique_ptr<Core::Frontend::WebBrowserApplet>;
187 189
188 AppletFrontendSet(); 190 AppletFrontendSet();
189 AppletFrontendSet(ControllerApplet controller_applet, ErrorApplet error_applet, 191 AppletFrontendSet(CabinetApplet cabinet_applet, ControllerApplet controller_applet,
190 MiiEdit mii_edit_, ParentalControlsApplet parental_controls_applet, 192 ErrorApplet error_applet, MiiEdit mii_edit_,
191 PhotoViewer photo_viewer_, ProfileSelect profile_select_, 193 ParentalControlsApplet parental_controls_applet, PhotoViewer photo_viewer_,
192 SoftwareKeyboard software_keyboard_, WebBrowser web_browser_); 194 ProfileSelect profile_select_, SoftwareKeyboard software_keyboard_,
195 WebBrowser web_browser_);
193 ~AppletFrontendSet(); 196 ~AppletFrontendSet();
194 197
195 AppletFrontendSet(const AppletFrontendSet&) = delete; 198 AppletFrontendSet(const AppletFrontendSet&) = delete;
@@ -198,6 +201,7 @@ struct AppletFrontendSet {
198 AppletFrontendSet(AppletFrontendSet&&) noexcept; 201 AppletFrontendSet(AppletFrontendSet&&) noexcept;
199 AppletFrontendSet& operator=(AppletFrontendSet&&) noexcept; 202 AppletFrontendSet& operator=(AppletFrontendSet&&) noexcept;
200 203
204 CabinetApplet cabinet;
201 ControllerApplet controller; 205 ControllerApplet controller;
202 ErrorApplet error; 206 ErrorApplet error;
203 MiiEdit mii_edit; 207 MiiEdit mii_edit;
diff --git a/src/core/hle/service/nfp/nfp_device.cpp b/src/core/hle/service/nfp/nfp_device.cpp
index b19672560..e1bf90d7c 100644
--- a/src/core/hle/service/nfp/nfp_device.cpp
+++ b/src/core/hle/service/nfp/nfp_device.cpp
@@ -77,6 +77,9 @@ void NfpDevice::NpadUpdate(Core::HID::ControllerTriggerType type) {
77 LoadAmiibo(nfc_status.data); 77 LoadAmiibo(nfc_status.data);
78 break; 78 break;
79 case Common::Input::NfcState::AmiiboRemoved: 79 case Common::Input::NfcState::AmiiboRemoved:
80 if (device_state == DeviceState::Initialized || device_state == DeviceState::TagRemoved) {
81 break;
82 }
80 if (device_state != DeviceState::SearchingForTag) { 83 if (device_state != DeviceState::SearchingForTag) {
81 CloseAmiibo(); 84 CloseAmiibo();
82 } 85 }
@@ -97,6 +100,8 @@ bool NfpDevice::LoadAmiibo(std::span<const u8> data) {
97 return false; 100 return false;
98 } 101 }
99 102
103 // TODO: Filter by allowed_protocols here
104
100 memcpy(&encrypted_tag_data, data.data(), sizeof(EncryptedNTAG215File)); 105 memcpy(&encrypted_tag_data, data.data(), sizeof(EncryptedNTAG215File));
101 106
102 device_state = DeviceState::TagFound; 107 device_state = DeviceState::TagFound;
@@ -143,7 +148,7 @@ void NfpDevice::Finalize() {
143 device_state = DeviceState::Unavailable; 148 device_state = DeviceState::Unavailable;
144} 149}
145 150
146Result NfpDevice::StartDetection(s32 protocol_) { 151Result NfpDevice::StartDetection(TagProtocol allowed_protocol) {
147 if (device_state != DeviceState::Initialized && device_state != DeviceState::TagRemoved) { 152 if (device_state != DeviceState::Initialized && device_state != DeviceState::TagRemoved) {
148 LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); 153 LOG_ERROR(Service_NFP, "Wrong device state {}", device_state);
149 return WrongDeviceState; 154 return WrongDeviceState;
@@ -155,7 +160,7 @@ Result NfpDevice::StartDetection(s32 protocol_) {
155 } 160 }
156 161
157 device_state = DeviceState::SearchingForTag; 162 device_state = DeviceState::SearchingForTag;
158 protocol = protocol_; 163 allowed_protocols = allowed_protocol;
159 return ResultSuccess; 164 return ResultSuccess;
160} 165}
161 166
@@ -469,6 +474,32 @@ Result NfpDevice::OpenApplicationArea(u32 access_id) {
469 return ResultSuccess; 474 return ResultSuccess;
470} 475}
471 476
477Result NfpDevice::GetApplicationAreaId(u32& application_area_id) const {
478 application_area_id = {};
479
480 if (device_state != DeviceState::TagMounted) {
481 LOG_ERROR(Service_NFP, "Wrong device state {}", device_state);
482 if (device_state == DeviceState::TagRemoved) {
483 return TagRemoved;
484 }
485 return WrongDeviceState;
486 }
487
488 if (mount_target == MountTarget::None || mount_target == MountTarget::Rom) {
489 LOG_ERROR(Service_NFP, "Amiibo is read only", device_state);
490 return WrongDeviceState;
491 }
492
493 if (tag_data.settings.settings.appdata_initialized.Value() == 0) {
494 LOG_WARNING(Service_NFP, "Application area is not initialized");
495 return ApplicationAreaIsNotInitialized;
496 }
497
498 application_area_id = tag_data.application_area_id;
499
500 return ResultSuccess;
501}
502
472Result NfpDevice::GetApplicationArea(std::vector<u8>& data) const { 503Result NfpDevice::GetApplicationArea(std::vector<u8>& data) const {
473 if (device_state != DeviceState::TagMounted) { 504 if (device_state != DeviceState::TagMounted) {
474 LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); 505 LOG_ERROR(Service_NFP, "Wrong device state {}", device_state);
diff --git a/src/core/hle/service/nfp/nfp_device.h b/src/core/hle/service/nfp/nfp_device.h
index 76d0e9ae4..3d1cb4609 100644
--- a/src/core/hle/service/nfp/nfp_device.h
+++ b/src/core/hle/service/nfp/nfp_device.h
@@ -4,6 +4,7 @@
4#pragma once 4#pragma once
5 5
6#include <array> 6#include <array>
7#include <span>
7#include <vector> 8#include <vector>
8 9
9#include "common/common_funcs.h" 10#include "common/common_funcs.h"
@@ -37,7 +38,7 @@ public:
37 void Initialize(); 38 void Initialize();
38 void Finalize(); 39 void Finalize();
39 40
40 Result StartDetection(s32 protocol_); 41 Result StartDetection(TagProtocol allowed_protocol);
41 Result StopDetection(); 42 Result StopDetection();
42 Result Mount(MountTarget mount_target); 43 Result Mount(MountTarget mount_target);
43 Result Unmount(); 44 Result Unmount();
@@ -53,6 +54,7 @@ public:
53 Result DeleteAllData(); 54 Result DeleteAllData();
54 55
55 Result OpenApplicationArea(u32 access_id); 56 Result OpenApplicationArea(u32 access_id);
57 Result GetApplicationAreaId(u32& application_area_id) const;
56 Result GetApplicationArea(std::vector<u8>& data) const; 58 Result GetApplicationArea(std::vector<u8>& data) const;
57 Result SetApplicationArea(std::span<const u8> data); 59 Result SetApplicationArea(std::span<const u8> data);
58 Result CreateApplicationArea(u32 access_id, std::span<const u8> data); 60 Result CreateApplicationArea(u32 access_id, std::span<const u8> data);
@@ -88,7 +90,7 @@ private:
88 90
89 bool is_data_moddified{}; 91 bool is_data_moddified{};
90 bool is_app_area_open{}; 92 bool is_app_area_open{};
91 s32 protocol{}; 93 TagProtocol allowed_protocols{};
92 s64 current_posix_time{}; 94 s64 current_posix_time{};
93 MountTarget mount_target{MountTarget::None}; 95 MountTarget mount_target{MountTarget::None};
94 DeviceState device_state{DeviceState::Unavailable}; 96 DeviceState device_state{DeviceState::Unavailable};
diff --git a/src/core/hle/service/nfp/nfp_types.h b/src/core/hle/service/nfp/nfp_types.h
index 63d5917cb..69858096a 100644
--- a/src/core/hle/service/nfp/nfp_types.h
+++ b/src/core/hle/service/nfp/nfp_types.h
@@ -88,11 +88,22 @@ enum class PackedTagType : u8 {
88 Type5, // ISO15693 RW/RO 540 bytes 106kbit/s 88 Type5, // ISO15693 RW/RO 540 bytes 106kbit/s
89}; 89};
90 90
91// Verify this enum. It might be completely wrong default protocol is 0x48
91enum class TagProtocol : u32 { 92enum class TagProtocol : u32 {
92 None, 93 None,
93 TypeA, // ISO14443A 94 TypeA = 1U << 0, // ISO14443A
94 TypeB, // ISO14443B 95 TypeB = 1U << 1, // ISO14443B
95 TypeF, // Sony Felica 96 TypeF = 1U << 2, // Sony Felica
97 Unknown1 = 1U << 3,
98 Unknown2 = 1U << 5,
99 All = 0xFFFFFFFFU,
100};
101
102enum class CabinetMode : u8 {
103 StartNicknameAndOwnerSettings,
104 StartGameDataEraser,
105 StartRestorer,
106 StartFormatter,
96}; 107};
97 108
98using UniqueSerialNumber = std::array<u8, 7>; 109using UniqueSerialNumber = std::array<u8, 7>;
diff --git a/src/core/hle/service/nfp/nfp_user.cpp b/src/core/hle/service/nfp/nfp_user.cpp
index 33e2ef518..ac492cc27 100644
--- a/src/core/hle/service/nfp/nfp_user.cpp
+++ b/src/core/hle/service/nfp/nfp_user.cpp
@@ -130,7 +130,7 @@ void IUser::ListDevices(Kernel::HLERequestContext& ctx) {
130void IUser::StartDetection(Kernel::HLERequestContext& ctx) { 130void IUser::StartDetection(Kernel::HLERequestContext& ctx) {
131 IPC::RequestParser rp{ctx}; 131 IPC::RequestParser rp{ctx};
132 const auto device_handle{rp.Pop<u64>()}; 132 const auto device_handle{rp.Pop<u64>()};
133 const auto nfp_protocol{rp.Pop<s32>()}; 133 const auto nfp_protocol{rp.PopEnum<TagProtocol>()};
134 LOG_INFO(Service_NFP, "called, device_handle={}, nfp_protocol={}", device_handle, nfp_protocol); 134 LOG_INFO(Service_NFP, "called, device_handle={}, nfp_protocol={}", device_handle, nfp_protocol);
135 135
136 if (state == State::NonInitialized) { 136 if (state == State::NonInitialized) {
diff --git a/src/input_common/drivers/virtual_amiibo.cpp b/src/input_common/drivers/virtual_amiibo.cpp
index 0cd5129da..564a188e5 100644
--- a/src/input_common/drivers/virtual_amiibo.cpp
+++ b/src/input_common/drivers/virtual_amiibo.cpp
@@ -60,6 +60,8 @@ Common::Input::NfcState VirtualAmiibo::WriteNfcData(
60 return Common::Input::NfcState::WriteFailed; 60 return Common::Input::NfcState::WriteFailed;
61 } 61 }
62 62
63 amiibo_data = data;
64
63 return Common::Input::NfcState::Success; 65 return Common::Input::NfcState::Success;
64} 66}
65 67
@@ -91,6 +93,15 @@ VirtualAmiibo::Info VirtualAmiibo::LoadAmiibo(const std::string& filename) {
91 return Info::Success; 93 return Info::Success;
92} 94}
93 95
96VirtualAmiibo::Info VirtualAmiibo::ReloadAmiibo() {
97 if (state == State::AmiiboIsOpen) {
98 SetNfc(identifier, {Common::Input::NfcState::NewAmiibo, amiibo_data});
99 return Info::Success;
100 }
101
102 return LoadAmiibo(file_path);
103}
104
94VirtualAmiibo::Info VirtualAmiibo::CloseAmiibo() { 105VirtualAmiibo::Info VirtualAmiibo::CloseAmiibo() {
95 state = polling_mode == Common::Input::PollingMode::NFC ? State::WaitingForAmiibo 106 state = polling_mode == Common::Input::PollingMode::NFC ? State::WaitingForAmiibo
96 : State::Initialized; 107 : State::Initialized;
@@ -98,4 +109,8 @@ VirtualAmiibo::Info VirtualAmiibo::CloseAmiibo() {
98 return Info::Success; 109 return Info::Success;
99} 110}
100 111
112std::string VirtualAmiibo::GetLastFilePath() const {
113 return file_path;
114}
115
101} // namespace InputCommon 116} // namespace InputCommon
diff --git a/src/input_common/drivers/virtual_amiibo.h b/src/input_common/drivers/virtual_amiibo.h
index 9eac07544..9baeb3997 100644
--- a/src/input_common/drivers/virtual_amiibo.h
+++ b/src/input_common/drivers/virtual_amiibo.h
@@ -47,8 +47,11 @@ public:
47 State GetCurrentState() const; 47 State GetCurrentState() const;
48 48
49 Info LoadAmiibo(const std::string& amiibo_file); 49 Info LoadAmiibo(const std::string& amiibo_file);
50 Info ReloadAmiibo();
50 Info CloseAmiibo(); 51 Info CloseAmiibo();
51 52
53 std::string GetLastFilePath() const;
54
52private: 55private:
53 static constexpr std::size_t amiibo_size = 0x21C; 56 static constexpr std::size_t amiibo_size = 0x21C;
54 static constexpr std::size_t amiibo_size_without_password = amiibo_size - 0x8; 57 static constexpr std::size_t amiibo_size_without_password = amiibo_size - 0x8;
diff --git a/src/input_common/input_engine.h b/src/input_common/input_engine.h
index d4c264a8e..6cbcf5207 100644
--- a/src/input_common/input_engine.h
+++ b/src/input_common/input_engine.h
@@ -133,7 +133,7 @@ public:
133 return Common::Input::CameraError::NotSupported; 133 return Common::Input::CameraError::NotSupported;
134 } 134 }
135 135
136 // Request nfc data from a controller 136 // Returns success if nfc is supported
137 virtual Common::Input::NfcState SupportsNfc( 137 virtual Common::Input::NfcState SupportsNfc(
138 [[maybe_unused]] const PadIdentifier& identifier) const { 138 [[maybe_unused]] const PadIdentifier& identifier) const {
139 return Common::Input::NfcState::NotSupported; 139 return Common::Input::NfcState::NotSupported;
diff --git a/src/yuzu/CMakeLists.txt b/src/yuzu/CMakeLists.txt
index 5cc1fbf32..adad36221 100644
--- a/src/yuzu/CMakeLists.txt
+++ b/src/yuzu/CMakeLists.txt
@@ -18,6 +18,9 @@ add_executable(yuzu
18 about_dialog.cpp 18 about_dialog.cpp
19 about_dialog.h 19 about_dialog.h
20 aboutdialog.ui 20 aboutdialog.ui
21 applets/qt_amiibo_settings.cpp
22 applets/qt_amiibo_settings.h
23 applets/qt_amiibo_settings.ui
21 applets/qt_controller.cpp 24 applets/qt_controller.cpp
22 applets/qt_controller.h 25 applets/qt_controller.h
23 applets/qt_controller.ui 26 applets/qt_controller.ui
diff --git a/src/yuzu/applets/qt_amiibo_settings.cpp b/src/yuzu/applets/qt_amiibo_settings.cpp
new file mode 100644
index 000000000..efb7f6ecc
--- /dev/null
+++ b/src/yuzu/applets/qt_amiibo_settings.cpp
@@ -0,0 +1,260 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include <algorithm>
5#include <thread>
6#include <fmt/format.h>
7#include <nlohmann/json.hpp>
8
9#include "common/assert.h"
10#include "common/string_util.h"
11#include "core/hle/service/nfp/nfp_device.h"
12#include "core/hle/service/nfp/nfp_result.h"
13#include "input_common/drivers/virtual_amiibo.h"
14#include "input_common/main.h"
15#include "ui_qt_amiibo_settings.h"
16#include "web_service/web_backend.h"
17#include "yuzu/applets/qt_amiibo_settings.h"
18#include "yuzu/main.h"
19
20QtAmiiboSettingsDialog::QtAmiiboSettingsDialog(QWidget* parent,
21 Core::Frontend::CabinetParameters parameters_,
22 InputCommon::InputSubsystem* input_subsystem_,
23 std::shared_ptr<Service::NFP::NfpDevice> nfp_device_)
24 : QDialog(parent), ui(std::make_unique<Ui::QtAmiiboSettingsDialog>()),
25 input_subsystem{input_subsystem_}, nfp_device{std::move(nfp_device_)},
26 parameters(std::move(parameters_)) {
27 ui->setupUi(this);
28
29 LoadInfo();
30
31 resize(0, 0);
32}
33
34QtAmiiboSettingsDialog::~QtAmiiboSettingsDialog() = default;
35
36int QtAmiiboSettingsDialog::exec() {
37 if (!is_initalized) {
38 return QDialog::Rejected;
39 }
40 return QDialog::exec();
41}
42
43std::string QtAmiiboSettingsDialog::GetName() const {
44 return ui->amiiboCustomNameValue->text().toStdString();
45}
46
47void QtAmiiboSettingsDialog::LoadInfo() {
48 if (input_subsystem->GetVirtualAmiibo()->ReloadAmiibo() !=
49 InputCommon::VirtualAmiibo::Info::Success) {
50 return;
51 }
52
53 if (nfp_device->GetCurrentState() != Service::NFP::DeviceState::TagFound &&
54 nfp_device->GetCurrentState() != Service::NFP::DeviceState::TagMounted) {
55 return;
56 }
57 nfp_device->Mount(Service::NFP::MountTarget::All);
58
59 LoadAmiiboInfo();
60 LoadAmiiboData();
61 LoadAmiiboGameInfo();
62
63 ui->amiiboDirectoryValue->setText(
64 QString::fromStdString(input_subsystem->GetVirtualAmiibo()->GetLastFilePath()));
65
66 SetSettingsDescription();
67 is_initalized = true;
68}
69
70void QtAmiiboSettingsDialog::LoadAmiiboInfo() {
71 Service::NFP::ModelInfo model_info{};
72 const auto model_result = nfp_device->GetModelInfo(model_info);
73
74 if (model_result.IsFailure()) {
75 ui->amiiboImageLabel->setVisible(false);
76 ui->amiiboInfoGroup->setVisible(false);
77 return;
78 }
79
80 const auto amiibo_id =
81 fmt::format("{:04x}{:02x}{:02x}{:04x}{:02x}02", Common::swap16(model_info.character_id),
82 model_info.character_variant, model_info.amiibo_type, model_info.model_number,
83 model_info.series);
84
85 LOG_DEBUG(Frontend, "Loading amiibo id {}", amiibo_id);
86 // Note: This function is not being used until we host the images on our server
87 // LoadAmiiboApiInfo(amiibo_id);
88 ui->amiiboImageLabel->setVisible(false);
89 ui->amiiboInfoGroup->setVisible(false);
90}
91
92void QtAmiiboSettingsDialog::LoadAmiiboApiInfo(std::string_view amiibo_id) {
93 // TODO: Host this data on our website
94 WebService::Client client{"https://amiiboapi.com", {}, {}};
95 WebService::Client image_client{"https://raw.githubusercontent.com", {}, {}};
96 const auto url_path = fmt::format("/api/amiibo/?id={}", amiibo_id);
97
98 const auto amiibo_json = client.GetJson(url_path, true).returned_data;
99 if (amiibo_json.empty()) {
100 ui->amiiboImageLabel->setVisible(false);
101 ui->amiiboInfoGroup->setVisible(false);
102 return;
103 }
104
105 std::string amiibo_series{};
106 std::string amiibo_name{};
107 std::string amiibo_image_url{};
108 std::string amiibo_type{};
109
110 const auto parsed_amiibo_json_json = nlohmann::json::parse(amiibo_json).at("amiibo");
111 parsed_amiibo_json_json.at("amiiboSeries").get_to(amiibo_series);
112 parsed_amiibo_json_json.at("name").get_to(amiibo_name);
113 parsed_amiibo_json_json.at("image").get_to(amiibo_image_url);
114 parsed_amiibo_json_json.at("type").get_to(amiibo_type);
115
116 ui->amiiboSeriesValue->setText(QString::fromStdString(amiibo_series));
117 ui->amiiboNameValue->setText(QString::fromStdString(amiibo_name));
118 ui->amiiboTypeValue->setText(QString::fromStdString(amiibo_type));
119
120 if (amiibo_image_url.size() < 34) {
121 ui->amiiboImageLabel->setVisible(false);
122 }
123
124 const auto image_url_path = amiibo_image_url.substr(34, amiibo_image_url.size() - 34);
125 const auto image_data = image_client.GetImage(image_url_path, true).returned_data;
126
127 if (image_data.empty()) {
128 ui->amiiboImageLabel->setVisible(false);
129 }
130
131 QPixmap pixmap;
132 pixmap.loadFromData(reinterpret_cast<const u8*>(image_data.data()),
133 static_cast<uint>(image_data.size()));
134 pixmap = pixmap.scaled(250, 350, Qt::AspectRatioMode::KeepAspectRatio,
135 Qt::TransformationMode::SmoothTransformation);
136 ui->amiiboImageLabel->setPixmap(pixmap);
137}
138
139void QtAmiiboSettingsDialog::LoadAmiiboData() {
140 Service::NFP::RegisterInfo register_info{};
141 Service::NFP::CommonInfo common_info{};
142 const auto register_result = nfp_device->GetRegisterInfo(register_info);
143 const auto common_result = nfp_device->GetCommonInfo(common_info);
144
145 if (register_result.IsFailure()) {
146 ui->creationDateValue->setDisabled(true);
147 ui->modificationDateValue->setDisabled(true);
148 ui->amiiboCustomNameValue->setReadOnly(false);
149 ui->amiiboOwnerValue->setReadOnly(false);
150 return;
151 }
152
153 if (parameters.mode == Service::NFP::CabinetMode::StartNicknameAndOwnerSettings) {
154 ui->creationDateValue->setDisabled(true);
155 ui->modificationDateValue->setDisabled(true);
156 }
157
158 const auto amiibo_name = std::string(register_info.amiibo_name.data());
159 const auto owner_name = Common::UTF16ToUTF8(register_info.mii_char_info.name.data());
160 const auto creation_date =
161 QDate(register_info.creation_date.year, register_info.creation_date.month,
162 register_info.creation_date.day);
163
164 ui->amiiboCustomNameValue->setText(QString::fromStdString(amiibo_name));
165 ui->amiiboOwnerValue->setText(QString::fromStdString(owner_name));
166 ui->amiiboCustomNameValue->setReadOnly(true);
167 ui->amiiboOwnerValue->setReadOnly(true);
168 ui->creationDateValue->setDate(creation_date);
169
170 if (common_result.IsFailure()) {
171 ui->modificationDateValue->setDisabled(true);
172 return;
173 }
174
175 const auto modification_date =
176 QDate(common_info.last_write_date.year, common_info.last_write_date.month,
177 common_info.last_write_date.day);
178 ui->modificationDateValue->setDate(modification_date);
179}
180
181void QtAmiiboSettingsDialog::LoadAmiiboGameInfo() {
182 u32 application_area_id{};
183 const auto application_result = nfp_device->GetApplicationAreaId(application_area_id);
184
185 if (application_result.IsFailure()) {
186 ui->gameIdValue->setVisible(false);
187 ui->gameIdLabel->setText(tr("No game data present"));
188 return;
189 }
190
191 SetGameDataName(application_area_id);
192}
193
194void QtAmiiboSettingsDialog::SetGameDataName(u32 application_area_id) {
195 static constexpr std::array<std::pair<u32, const char*>, 12> game_name_list = {
196 // 3ds, wii u
197 std::pair<u32, const char*>{0x10110E00, "Super Smash Bros (3DS/WiiU)"},
198 {0x00132600, "Mario & Luigi: Paper Jam"},
199 {0x0014F000, "Animal Crossing: Happy Home Designer"},
200 {0x00152600, "Chibi-Robo!: Zip Lash"},
201 {0x10161f00, "Mario Party 10"},
202 {0x1019C800, "The Legend of Zelda: Twilight Princess HD"},
203 // switch
204 {0x10162B00, "Splatoon 2"},
205 {0x1016e100, "Shovel Knight: Treasure Trove"},
206 {0x1019C800, "The Legend of Zelda: Breath of the Wild"},
207 {0x34F80200, "Super Smash Bros. Ultimate"},
208 {0x38600500, "Splatoon 3"},
209 {0x3B440400, "The Legend of Zelda: Link's Awakening"},
210 };
211
212 for (const auto& [game_id, game_name] : game_name_list) {
213 if (application_area_id == game_id) {
214 ui->gameIdValue->setText(QString::fromStdString(game_name));
215 return;
216 }
217 }
218
219 const auto application_area_string = fmt::format("{:016x}", application_area_id);
220 ui->gameIdValue->setText(QString::fromStdString(application_area_string));
221}
222
223void QtAmiiboSettingsDialog::SetSettingsDescription() {
224 switch (parameters.mode) {
225 case Service::NFP::CabinetMode::StartFormatter:
226 ui->cabinetActionDescriptionLabel->setText(
227 tr("The following amiibo data will be formatted:"));
228 break;
229 case Service::NFP::CabinetMode::StartGameDataEraser:
230 ui->cabinetActionDescriptionLabel->setText(tr("The following game data will removed:"));
231 break;
232 case Service::NFP::CabinetMode::StartNicknameAndOwnerSettings:
233 ui->cabinetActionDescriptionLabel->setText(tr("Set nickname and owner:"));
234 break;
235 case Service::NFP::CabinetMode::StartRestorer:
236 ui->cabinetActionDescriptionLabel->setText(tr("Do you wish to restore this amiibo?"));
237 break;
238 }
239}
240
241QtAmiiboSettings::QtAmiiboSettings(GMainWindow& parent) {
242 connect(this, &QtAmiiboSettings::MainWindowShowAmiiboSettings, &parent,
243 &GMainWindow::AmiiboSettingsShowDialog, Qt::QueuedConnection);
244 connect(&parent, &GMainWindow::AmiiboSettingsFinished, this,
245 &QtAmiiboSettings::MainWindowFinished, Qt::QueuedConnection);
246}
247
248QtAmiiboSettings::~QtAmiiboSettings() = default;
249
250void QtAmiiboSettings::ShowCabinetApplet(
251 const Core::Frontend::CabinetCallback& callback_,
252 const Core::Frontend::CabinetParameters& parameters,
253 std::shared_ptr<Service::NFP::NfpDevice> nfp_device) const {
254 callback = std::move(callback_);
255 emit MainWindowShowAmiiboSettings(parameters, nfp_device);
256}
257
258void QtAmiiboSettings::MainWindowFinished(bool is_success, const std::string& name) {
259 callback(is_success, name);
260}
diff --git a/src/yuzu/applets/qt_amiibo_settings.h b/src/yuzu/applets/qt_amiibo_settings.h
new file mode 100644
index 000000000..930c96739
--- /dev/null
+++ b/src/yuzu/applets/qt_amiibo_settings.h
@@ -0,0 +1,83 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <array>
7#include <memory>
8#include <QDialog>
9#include "core/frontend/applets/cabinet.h"
10
11class GMainWindow;
12class QCheckBox;
13class QComboBox;
14class QDialogButtonBox;
15class QGroupBox;
16class QLabel;
17
18namespace InputCommon {
19class InputSubsystem;
20}
21
22namespace Ui {
23class QtAmiiboSettingsDialog;
24}
25
26namespace Service::NFP {
27class NfpDevice;
28} // namespace Service::NFP
29
30class QtAmiiboSettingsDialog final : public QDialog {
31 Q_OBJECT
32
33public:
34 explicit QtAmiiboSettingsDialog(QWidget* parent, Core::Frontend::CabinetParameters parameters_,
35 InputCommon::InputSubsystem* input_subsystem_,
36 std::shared_ptr<Service::NFP::NfpDevice> nfp_device_);
37 ~QtAmiiboSettingsDialog() override;
38
39 int exec() override;
40
41 std::string GetName() const;
42
43private:
44 void LoadInfo();
45 void LoadAmiiboInfo();
46 void LoadAmiiboApiInfo(std::string_view amiibo_id);
47 void LoadAmiiboData();
48 void LoadAmiiboGameInfo();
49 void SetGameDataName(u32 application_area_id);
50 void SetSettingsDescription();
51
52 std::unique_ptr<Ui::QtAmiiboSettingsDialog> ui;
53
54 InputCommon::InputSubsystem* input_subsystem;
55 std::shared_ptr<Service::NFP::NfpDevice> nfp_device;
56
57 // Parameters sent in from the backend HLE applet.
58 Core::Frontend::CabinetParameters parameters;
59
60 // If false amiibo settings failed to load
61 bool is_initalized{};
62};
63
64class QtAmiiboSettings final : public QObject, public Core::Frontend::CabinetApplet {
65 Q_OBJECT
66
67public:
68 explicit QtAmiiboSettings(GMainWindow& parent);
69 ~QtAmiiboSettings() override;
70
71 void ShowCabinetApplet(const Core::Frontend::CabinetCallback& callback_,
72 const Core::Frontend::CabinetParameters& parameters,
73 std::shared_ptr<Service::NFP::NfpDevice> nfp_device) const override;
74
75signals:
76 void MainWindowShowAmiiboSettings(const Core::Frontend::CabinetParameters& parameters,
77 std::shared_ptr<Service::NFP::NfpDevice> nfp_device) const;
78
79private:
80 void MainWindowFinished(bool is_success, const std::string& name);
81
82 mutable Core::Frontend::CabinetCallback callback;
83};
diff --git a/src/yuzu/applets/qt_amiibo_settings.ui b/src/yuzu/applets/qt_amiibo_settings.ui
new file mode 100644
index 000000000..f377a6e61
--- /dev/null
+++ b/src/yuzu/applets/qt_amiibo_settings.ui
@@ -0,0 +1,494 @@
1<?xml version="1.0" encoding="UTF-8"?>
2<ui version="4.0">
3 <class>QtAmiiboSettingsDialog</class>
4 <widget class="QDialog" name="QtAmiiboSettingsDialog">
5 <property name="geometry">
6 <rect>
7 <x>0</x>
8 <y>0</y>
9 <width>839</width>
10 <height>500</height>
11 </rect>
12 </property>
13 <property name="windowTitle">
14 <string>Amiibo Settings</string>
15 </property>
16 <property name="styleSheet">
17 <string notr="true"/>
18 </property>
19 <layout class="QVBoxLayout" name="verticalLayout" stretch="0">
20 <property name="leftMargin">
21 <number>0</number>
22 </property>
23 <property name="topMargin">
24 <number>0</number>
25 </property>
26 <property name="rightMargin">
27 <number>0</number>
28 </property>
29 <property name="bottomMargin">
30 <number>0</number>
31 </property>
32 <item>
33 <widget class="QWidget" name="mainControllerApplet" native="true">
34 <layout class="QVBoxLayout" name="verticalLayout_1" stretch="0,3,0">
35 <property name="spacing">
36 <number>0</number>
37 </property>
38 <property name="leftMargin">
39 <number>0</number>
40 </property>
41 <property name="topMargin">
42 <number>0</number>
43 </property>
44 <property name="rightMargin">
45 <number>0</number>
46 </property>
47 <property name="bottomMargin">
48 <number>0</number>
49 </property>
50 <item>
51 <widget class="QWidget" name="topControllerApplet" native="true">
52 <layout class="QHBoxLayout" name="horizontalLayout">
53 <property name="spacing">
54 <number>10</number>
55 </property>
56 <property name="leftMargin">
57 <number>20</number>
58 </property>
59 <property name="topMargin">
60 <number>15</number>
61 </property>
62 <property name="rightMargin">
63 <number>0</number>
64 </property>
65 <property name="bottomMargin">
66 <number>15</number>
67 </property>
68 <item>
69 <widget class="QLabel" name="cabinetActionDescriptionLabel">
70 <property name="font">
71 <font>
72 <pointsize>12</pointsize>
73 <weight>75</weight>
74 <bold>true</bold>
75 </font>
76 </property>
77 <property name="text">
78 <string/>
79 </property>
80 <property name="alignment">
81 <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
82 </property>
83 <property name="wordWrap">
84 <bool>false</bool>
85 </property>
86 </widget>
87 </item>
88 <item>
89 <spacer name="horizontalSpacer_2">
90 <property name="orientation">
91 <enum>Qt::Horizontal</enum>
92 </property>
93 <property name="sizeHint" stdset="0">
94 <size>
95 <width>40</width>
96 <height>20</height>
97 </size>
98 </property>
99 </spacer>
100 </item>
101 </layout>
102 </widget>
103 </item>
104 <item>
105 <widget class="QWidget" name="middleControllerApplet" native="true">
106 <layout class="QVBoxLayout" name="verticalLayout_3">
107 <property name="spacing">
108 <number>0</number>
109 </property>
110 <property name="leftMargin">
111 <number>0</number>
112 </property>
113 <property name="topMargin">
114 <number>0</number>
115 </property>
116 <property name="rightMargin">
117 <number>0</number>
118 </property>
119 <property name="bottomMargin">
120 <number>0</number>
121 </property>
122 <item>
123 <layout class="QHBoxLayout" name="horizontalLayout_2">
124 <property name="spacing">
125 <number>20</number>
126 </property>
127 <property name="leftMargin">
128 <number>15</number>
129 </property>
130 <property name="rightMargin">
131 <number>15</number>
132 </property>
133 <item>
134 <widget class="QLabel" name="amiiboImageLabel">
135 <property name="minimumSize">
136 <size>
137 <width>250</width>
138 <height>350</height>
139 </size>
140 </property>
141 <property name="maximumSize">
142 <size>
143 <width>236</width>
144 <height>350</height>
145 </size>
146 </property>
147 <property name="text">
148 <string/>
149 </property>
150 <property name="alignment">
151 <set>Qt::AlignCenter</set>
152 </property>
153 </widget>
154 </item>
155 <item>
156 <layout class="QVBoxLayout" name="verticalLayout_4">
157 <property name="leftMargin">
158 <number>0</number>
159 </property>
160 <property name="topMargin">
161 <number>8</number>
162 </property>
163 <property name="bottomMargin">
164 <number>15</number>
165 </property>
166 <item>
167 <widget class="QGroupBox" name="amiiboInfoGroup">
168 <property name="title">
169 <string>Amiibo Info</string>
170 </property>
171 <layout class="QVBoxLayout" name="verticalLayout_5">
172 <item>
173 <layout class="QGridLayout" name="gridLayout_1">
174 <item row="0" column="0">
175 <widget class="QLabel" name="amiiboSeriesLabel">
176 <property name="text">
177 <string>Series</string>
178 </property>
179 </widget>
180 </item>
181 <item row="0" column="1">
182 <widget class="QLineEdit" name="amiiboSeriesValue">
183 <property name="sizePolicy">
184 <sizepolicy hsizetype="Minimum" vsizetype="Fixed">
185 <horstretch>0</horstretch>
186 <verstretch>0</verstretch>
187 </sizepolicy>
188 </property>
189 <property name="readOnly">
190 <bool>true</bool>
191 </property>
192 </widget>
193 </item>
194 <item row="1" column="0">
195 <widget class="QLabel" name="amiiboTypeLabel">
196 <property name="text">
197 <string>Type</string>
198 </property>
199 </widget>
200 </item>
201 <item row="1" column="1">
202 <widget class="QLineEdit" name="amiiboTypeValue">
203 <property name="sizePolicy">
204 <sizepolicy hsizetype="Minimum" vsizetype="Fixed">
205 <horstretch>0</horstretch>
206 <verstretch>0</verstretch>
207 </sizepolicy>
208 </property>
209 <property name="readOnly">
210 <bool>true</bool>
211 </property>
212 </widget>
213 </item>
214 <item row="2" column="0">
215 <widget class="QLabel" name="amiiboNameLabel">
216 <property name="text">
217 <string>Name</string>
218 </property>
219 </widget>
220 </item>
221 <item row="2" column="1">
222 <widget class="QLineEdit" name="amiiboNameValue">
223 <property name="sizePolicy">
224 <sizepolicy hsizetype="Minimum" vsizetype="Fixed">
225 <horstretch>0</horstretch>
226 <verstretch>0</verstretch>
227 </sizepolicy>
228 </property>
229 <property name="readOnly">
230 <bool>true</bool>
231 </property>
232 </widget>
233 </item>
234 </layout>
235 </item>
236 </layout>
237 </widget>
238 </item>
239 <item>
240 <widget class="QGroupBox" name="amiiboDataGroup">
241 <property name="title">
242 <string>Amiibo Data</string>
243 </property>
244 <layout class="QVBoxLayout" name="verticalLayout_6">
245 <item>
246 <layout class="QGridLayout" name="gridLayout_2">
247 <item row="0" column="0">
248 <widget class="QLabel" name="amiiboCustomNameLabel">
249 <property name="text">
250 <string>Custom Name</string>
251 </property>
252 </widget>
253 </item>
254 <item row="0" column="1">
255 <widget class="QLineEdit" name="amiiboCustomNameValue">
256 <property name="sizePolicy">
257 <sizepolicy hsizetype="Minimum" vsizetype="Fixed">
258 <horstretch>0</horstretch>
259 <verstretch>0</verstretch>
260 </sizepolicy>
261 </property>
262 <property name="maxLength">
263 <number>10</number>
264 </property>
265 </widget>
266 </item>
267 <item row="1" column="0">
268 <widget class="QLabel" name="amiiboOwnerLabel">
269 <property name="text">
270 <string>Owner</string>
271 </property>
272 </widget>
273 </item>
274 <item row="1" column="1">
275 <widget class="QLineEdit" name="amiiboOwnerValue">
276 <property name="sizePolicy">
277 <sizepolicy hsizetype="Minimum" vsizetype="Fixed">
278 <horstretch>0</horstretch>
279 <verstretch>0</verstretch>
280 </sizepolicy>
281 </property>
282 <property name="maxLength">
283 <number>10</number>
284 </property>
285 </widget>
286 </item>
287 <item row="2" column="0">
288 <widget class="QLabel" name="creationDateLabel">
289 <property name="text">
290 <string>Creation Date</string>
291 </property>
292 </widget>
293 </item>
294 <item row="2" column="1">
295 <widget class="QDateTimeEdit" name="creationDateValue">
296 <property name="readOnly">
297 <bool>true</bool>
298 </property>
299 <property name="minimumDate">
300 <date>
301 <year>1970</year>
302 <month>1</month>
303 <day>1</day>
304 </date>
305 </property>
306 <property name="displayFormat">
307 <string>dd/MM/yyyy</string>
308 </property>
309 </widget>
310 </item>
311 <item row="3" column="0">
312 <widget class="QLabel" name="modificationDateLabel">
313 <property name="text">
314 <string>Modification Date</string>
315 </property>
316 </widget>
317 </item>
318 <item row="3" column="1">
319 <widget class="QDateTimeEdit" name="modificationDateValue">
320 <property name="readOnly">
321 <bool>true</bool>
322 </property>
323 <property name="minimumDate">
324 <date>
325 <year>1970</year>
326 <month>1</month>
327 <day>1</day>
328 </date>
329 </property>
330 <property name="displayFormat">
331 <string>dd/MM/yyyy </string>
332 </property>
333 </widget>
334 </item>
335 </layout>
336 </item>
337 </layout>
338 </widget>
339 </item>
340 <item>
341 <widget class="QGroupBox" name="gameDataGroup">
342 <property name="minimumSize">
343 <size>
344 <width>500</width>
345 <height>0</height>
346 </size>
347 </property>
348 <property name="title">
349 <string>Game Data</string>
350 </property>
351 <layout class="QGridLayout" name="gridLayout_3">
352 <item row="0" column="0">
353 <widget class="QLabel" name="gameIdLabel">
354 <property name="text">
355 <string>Game Id</string>
356 </property>
357 </widget>
358 </item>
359 <item row="0" column="1">
360 <widget class="QLineEdit" name="gameIdValue">
361 <property name="sizePolicy">
362 <sizepolicy hsizetype="Minimum" vsizetype="Fixed">
363 <horstretch>0</horstretch>
364 <verstretch>0</verstretch>
365 </sizepolicy>
366 </property>
367 <property name="readOnly">
368 <bool>true</bool>
369 </property>
370 </widget>
371 </item>
372 </layout>
373 </widget>
374 </item>
375 <item>
376 <widget class="QGroupBox" name="MountAmiiboGroup">
377 <property name="minimumSize">
378 <size>
379 <width>500</width>
380 <height>0</height>
381 </size>
382 </property>
383 <property name="title">
384 <string>Mount Amiibo</string>
385 </property>
386 <layout class="QGridLayout" name="gridLayout_4">
387 <item row="0" column="3">
388 <widget class="QToolButton" name="amiiboDirectoryButton">
389 <property name="text">
390 <string>...</string>
391 </property>
392 </widget>
393 </item>
394 <item row="0" column="1">
395 <spacer name="horizontalSpacer">
396 <property name="orientation">
397 <enum>Qt::Horizontal</enum>
398 </property>
399 <property name="sizeType">
400 <enum>QSizePolicy::Maximum</enum>
401 </property>
402 <property name="sizeHint" stdset="0">
403 <size>
404 <width>60</width>
405 <height>20</height>
406 </size>
407 </property>
408 </spacer>
409 </item>
410 <item row="0" column="0">
411 <widget class="QLabel" name="amiiboDirectoryLabel">
412 <property name="text">
413 <string>File Path</string>
414 </property>
415 </widget>
416 </item>
417 <item row="0" column="2">
418 <widget class="QLineEdit" name="amiiboDirectoryValue"/>
419 </item>
420 </layout>
421 </widget>
422 </item>
423 <item>
424 <spacer name="verticalSpacer">
425 <property name="orientation">
426 <enum>Qt::Vertical</enum>
427 </property>
428 <property name="sizeHint" stdset="0">
429 <size>
430 <width>20</width>
431 <height>40</height>
432 </size>
433 </property>
434 </spacer>
435 </item>
436 </layout>
437 </item>
438 </layout>
439 </item>
440 </layout>
441 </widget>
442 </item>
443 <item>
444 <widget class="QWidget" name="bottomControllerApplet" native="true">
445 <layout class="QHBoxLayout" name="horizontalLayout_6">
446 <property name="spacing">
447 <number>15</number>
448 </property>
449 <property name="leftMargin">
450 <number>15</number>
451 </property>
452 <property name="topMargin">
453 <number>8</number>
454 </property>
455 <property name="rightMargin">
456 <number>20</number>
457 </property>
458 <property name="bottomMargin">
459 <number>8</number>
460 </property>
461 <item alignment="Qt::AlignBottom">
462 <widget class="QDialogButtonBox" name="buttonBox">
463 <property name="enabled">
464 <bool>true</bool>
465 </property>
466 <property name="standardButtons">
467 <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
468 </property>
469 </widget>
470 </item>
471 </layout>
472 </widget>
473 </item>
474 </layout>
475 </widget>
476 </item>
477 </layout>
478 </widget>
479 <resources/>
480 <connections>
481 <connection>
482 <sender>buttonBox</sender>
483 <signal>accepted()</signal>
484 <receiver>QtAmiiboSettingsDialog</receiver>
485 <slot>accept()</slot>
486 </connection>
487 <connection>
488 <sender>buttonBox</sender>
489 <signal>rejected()</signal>
490 <receiver>QtAmiiboSettingsDialog</receiver>
491 <slot>reject()</slot>
492 </connection>
493 </connections>
494</ui>
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp
index 7ee2302cc..4081af391 100644
--- a/src/yuzu/main.cpp
+++ b/src/yuzu/main.cpp
@@ -15,6 +15,7 @@
15#endif 15#endif
16 16
17// VFS includes must be before glad as they will conflict with Windows file api, which uses defines. 17// VFS includes must be before glad as they will conflict with Windows file api, which uses defines.
18#include "applets/qt_amiibo_settings.h"
18#include "applets/qt_controller.h" 19#include "applets/qt_controller.h"
19#include "applets/qt_error.h" 20#include "applets/qt_error.h"
20#include "applets/qt_profile_select.h" 21#include "applets/qt_profile_select.h"
@@ -26,6 +27,7 @@
26#include "configuration/configure_tas.h" 27#include "configuration/configure_tas.h"
27#include "core/file_sys/vfs.h" 28#include "core/file_sys/vfs.h"
28#include "core/file_sys/vfs_real.h" 29#include "core/file_sys/vfs_real.h"
30#include "core/frontend/applets/cabinet.h"
29#include "core/frontend/applets/controller.h" 31#include "core/frontend/applets/controller.h"
30#include "core/frontend/applets/general_frontend.h" 32#include "core/frontend/applets/general_frontend.h"
31#include "core/frontend/applets/mii_edit.h" 33#include "core/frontend/applets/mii_edit.h"
@@ -548,6 +550,11 @@ void GMainWindow::RegisterMetaTypes() {
548 550
549 // Register applet types 551 // Register applet types
550 552
553 // Cabinet Applet
554 qRegisterMetaType<Core::Frontend::CabinetParameters>("Core::Frontend::CabinetParameters");
555 qRegisterMetaType<std::shared_ptr<Service::NFP::NfpDevice>>(
556 "std::shared_ptr<Service::NFP::NfpDevice>");
557
551 // Controller Applet 558 // Controller Applet
552 qRegisterMetaType<Core::Frontend::ControllerParameters>("Core::Frontend::ControllerParameters"); 559 qRegisterMetaType<Core::Frontend::ControllerParameters>("Core::Frontend::ControllerParameters");
553 560
@@ -569,6 +576,21 @@ void GMainWindow::RegisterMetaTypes() {
569 qRegisterMetaType<Core::SystemResultStatus>("Core::SystemResultStatus"); 576 qRegisterMetaType<Core::SystemResultStatus>("Core::SystemResultStatus");
570} 577}
571 578
579void GMainWindow::AmiiboSettingsShowDialog(const Core::Frontend::CabinetParameters& parameters,
580 std::shared_ptr<Service::NFP::NfpDevice> nfp_device) {
581 QtAmiiboSettingsDialog dialog(this, parameters, input_subsystem.get(), nfp_device);
582
583 dialog.setWindowFlags(Qt::Dialog | Qt::CustomizeWindowHint | Qt::WindowStaysOnTopHint |
584 Qt::WindowTitleHint | Qt::WindowSystemMenuHint);
585 dialog.setWindowModality(Qt::WindowModal);
586 if (dialog.exec() == QDialog::Rejected) {
587 emit AmiiboSettingsFinished(false, {});
588 return;
589 }
590
591 emit AmiiboSettingsFinished(true, dialog.GetName());
592}
593
572void GMainWindow::ControllerSelectorReconfigureControllers( 594void GMainWindow::ControllerSelectorReconfigureControllers(
573 const Core::Frontend::ControllerParameters& parameters) { 595 const Core::Frontend::ControllerParameters& parameters) {
574 QtControllerSelectorDialog dialog(this, parameters, input_subsystem.get(), *system); 596 QtControllerSelectorDialog dialog(this, parameters, input_subsystem.get(), *system);
@@ -1546,6 +1568,7 @@ bool GMainWindow::LoadROM(const QString& filename, u64 program_id, std::size_t p
1546 system->SetFilesystem(vfs); 1568 system->SetFilesystem(vfs);
1547 1569
1548 system->SetAppletFrontendSet({ 1570 system->SetAppletFrontendSet({
1571 std::make_unique<QtAmiiboSettings>(*this), // Amiibo Settings
1549 std::make_unique<QtControllerSelector>(*this), // Controller Selector 1572 std::make_unique<QtControllerSelector>(*this), // Controller Selector
1550 std::make_unique<QtErrorDisplay>(*this), // Error Display 1573 std::make_unique<QtErrorDisplay>(*this), // Error Display
1551 nullptr, // Mii Editor 1574 nullptr, // Mii Editor
diff --git a/src/yuzu/main.h b/src/yuzu/main.h
index b73f550dd..6a9992d05 100644
--- a/src/yuzu/main.h
+++ b/src/yuzu/main.h
@@ -55,6 +55,7 @@ class System;
55} // namespace Core 55} // namespace Core
56 56
57namespace Core::Frontend { 57namespace Core::Frontend {
58struct CabinetParameters;
58struct ControllerParameters; 59struct ControllerParameters;
59struct InlineAppearParameters; 60struct InlineAppearParameters;
60struct InlineTextParameters; 61struct InlineTextParameters;
@@ -82,6 +83,10 @@ enum class SwkbdReplyType : u32;
82enum class WebExitReason : u32; 83enum class WebExitReason : u32;
83} // namespace Service::AM::Applets 84} // namespace Service::AM::Applets
84 85
86namespace Service::NFP {
87class NfpDevice;
88} // namespace Service::NFP
89
85namespace Ui { 90namespace Ui {
86class MainWindow; 91class MainWindow;
87} 92}
@@ -149,6 +154,8 @@ signals:
149 154
150 void UpdateInstallProgress(); 155 void UpdateInstallProgress();
151 156
157 void AmiiboSettingsFinished(bool is_success, const std::string& name);
158
152 void ControllerSelectorReconfigureFinished(); 159 void ControllerSelectorReconfigureFinished();
153 160
154 void ErrorDisplayFinished(); 161 void ErrorDisplayFinished();
@@ -170,6 +177,8 @@ public slots:
170 void OnExecuteProgram(std::size_t program_index); 177 void OnExecuteProgram(std::size_t program_index);
171 void OnExit(); 178 void OnExit();
172 void OnSaveConfig(); 179 void OnSaveConfig();
180 void AmiiboSettingsShowDialog(const Core::Frontend::CabinetParameters& parameters,
181 std::shared_ptr<Service::NFP::NfpDevice> nfp_device);
173 void ControllerSelectorReconfigureControllers( 182 void ControllerSelectorReconfigureControllers(
174 const Core::Frontend::ControllerParameters& parameters); 183 const Core::Frontend::ControllerParameters& parameters);
175 void SoftwareKeyboardInitialize( 184 void SoftwareKeyboardInitialize(