mirror of
				https://git.suyu.dev/suyu/suyu.git
				synced 2025-10-31 14:56:40 +08:00 
			
		
		
		
	Merge pull request #9238 from german77/cabinet_applet
service: am: Implement cabinet applet
This commit is contained in:
		
						commit
						57a05b1653
					
				| @ -120,6 +120,8 @@ add_library(core STATIC | |||||||
|     file_sys/vfs_vector.h |     file_sys/vfs_vector.h | ||||||
|     file_sys/xts_archive.cpp |     file_sys/xts_archive.cpp | ||||||
|     file_sys/xts_archive.h |     file_sys/xts_archive.h | ||||||
|  |     frontend/applets/cabinet.cpp | ||||||
|  |     frontend/applets/cabinet.h | ||||||
|     frontend/applets/controller.cpp |     frontend/applets/controller.cpp | ||||||
|     frontend/applets/controller.h |     frontend/applets/controller.h | ||||||
|     frontend/applets/error.cpp |     frontend/applets/error.cpp | ||||||
| @ -312,6 +314,8 @@ add_library(core STATIC | |||||||
|     hle/service/am/applet_ae.h |     hle/service/am/applet_ae.h | ||||||
|     hle/service/am/applet_oe.cpp |     hle/service/am/applet_oe.cpp | ||||||
|     hle/service/am/applet_oe.h |     hle/service/am/applet_oe.h | ||||||
|  |     hle/service/am/applets/applet_cabinet.cpp | ||||||
|  |     hle/service/am/applets/applet_cabinet.h | ||||||
|     hle/service/am/applets/applet_controller.cpp |     hle/service/am/applets/applet_controller.cpp | ||||||
|     hle/service/am/applets/applet_controller.h |     hle/service/am/applets/applet_controller.h | ||||||
|     hle/service/am/applets/applet_error.cpp |     hle/service/am/applets/applet_error.cpp | ||||||
|  | |||||||
							
								
								
									
										20
									
								
								src/core/frontend/applets/cabinet.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								src/core/frontend/applets/cabinet.cpp
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,20 @@ | |||||||
|  | // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
 | ||||||
|  | // SPDX-License-Identifier: GPL-2.0-or-later
 | ||||||
|  | 
 | ||||||
|  | #include "common/logging/log.h" | ||||||
|  | #include "core/frontend/applets/cabinet.h" | ||||||
|  | 
 | ||||||
|  | #include <thread> | ||||||
|  | 
 | ||||||
|  | namespace Core::Frontend { | ||||||
|  | 
 | ||||||
|  | CabinetApplet::~CabinetApplet() = default; | ||||||
|  | 
 | ||||||
|  | void DefaultCabinetApplet::ShowCabinetApplet( | ||||||
|  |     const CabinetCallback& callback, const CabinetParameters& parameters, | ||||||
|  |     std::shared_ptr<Service::NFP::NfpDevice> nfp_device) const { | ||||||
|  |     LOG_WARNING(Service_AM, "(STUBBED) called"); | ||||||
|  |     callback(false, {}); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | } // namespace Core::Frontend
 | ||||||
							
								
								
									
										37
									
								
								src/core/frontend/applets/cabinet.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										37
									
								
								src/core/frontend/applets/cabinet.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,37 @@ | |||||||
|  | // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
 | ||||||
|  | // SPDX-License-Identifier: GPL-2.0-or-later
 | ||||||
|  | 
 | ||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | #include <functional> | ||||||
|  | #include "core/hle/service/nfp/nfp_types.h" | ||||||
|  | 
 | ||||||
|  | namespace Service::NFP { | ||||||
|  | class NfpDevice; | ||||||
|  | } // namespace Service::NFP
 | ||||||
|  | 
 | ||||||
|  | namespace Core::Frontend { | ||||||
|  | 
 | ||||||
|  | struct CabinetParameters { | ||||||
|  |     Service::NFP::TagInfo tag_info; | ||||||
|  |     Service::NFP::RegisterInfo register_info; | ||||||
|  |     Service::NFP::CabinetMode mode; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | using CabinetCallback = std::function<void(bool, const std::string&)>; | ||||||
|  | 
 | ||||||
|  | class CabinetApplet { | ||||||
|  | public: | ||||||
|  |     virtual ~CabinetApplet(); | ||||||
|  |     virtual void ShowCabinetApplet(const CabinetCallback& callback, | ||||||
|  |                                    const CabinetParameters& parameters, | ||||||
|  |                                    std::shared_ptr<Service::NFP::NfpDevice> nfp_device) const = 0; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | class DefaultCabinetApplet final : public CabinetApplet { | ||||||
|  | public: | ||||||
|  |     void ShowCabinetApplet(const CabinetCallback& callback, const CabinetParameters& parameters, | ||||||
|  |                            std::shared_ptr<Service::NFP::NfpDevice> nfp_device) const override; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | } // namespace Core::Frontend
 | ||||||
							
								
								
									
										177
									
								
								src/core/hle/service/am/applets/applet_cabinet.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										177
									
								
								src/core/hle/service/am/applets/applet_cabinet.cpp
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,177 @@ | |||||||
|  | // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
 | ||||||
|  | // SPDX-License-Identifier: GPL-2.0-or-later
 | ||||||
|  | 
 | ||||||
|  | #include "common/assert.h" | ||||||
|  | #include "common/logging/log.h" | ||||||
|  | #include "core/core.h" | ||||||
|  | #include "core/frontend/applets/cabinet.h" | ||||||
|  | #include "core/hid/hid_core.h" | ||||||
|  | #include "core/hle/kernel/k_event.h" | ||||||
|  | #include "core/hle/kernel/k_readable_event.h" | ||||||
|  | #include "core/hle/service/am/am.h" | ||||||
|  | #include "core/hle/service/am/applets/applet_cabinet.h" | ||||||
|  | #include "core/hle/service/mii/mii_manager.h" | ||||||
|  | #include "core/hle/service/nfp/nfp_device.h" | ||||||
|  | 
 | ||||||
|  | namespace Service::AM::Applets { | ||||||
|  | 
 | ||||||
|  | Cabinet::Cabinet(Core::System& system_, LibraryAppletMode applet_mode_, | ||||||
|  |                  const Core::Frontend::CabinetApplet& frontend_) | ||||||
|  |     : Applet{system_, applet_mode_}, frontend{frontend_}, system{system_}, service_context{ | ||||||
|  |                                                                                system_, | ||||||
|  |                                                                                "CabinetApplet"} { | ||||||
|  | 
 | ||||||
|  |     availability_change_event = | ||||||
|  |         service_context.CreateEvent("CabinetApplet:AvailabilityChangeEvent"); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | Cabinet::~Cabinet() = default; | ||||||
|  | 
 | ||||||
|  | void Cabinet::Initialize() { | ||||||
|  |     Applet::Initialize(); | ||||||
|  | 
 | ||||||
|  |     LOG_INFO(Service_HID, "Initializing Cabinet Applet."); | ||||||
|  | 
 | ||||||
|  |     LOG_DEBUG(Service_HID, | ||||||
|  |               "Initializing Applet with common_args: arg_version={}, lib_version={}, " | ||||||
|  |               "play_startup_sound={}, size={}, system_tick={}, theme_color={}", | ||||||
|  |               common_args.arguments_version, common_args.library_version, | ||||||
|  |               common_args.play_startup_sound, common_args.size, common_args.system_tick, | ||||||
|  |               common_args.theme_color); | ||||||
|  | 
 | ||||||
|  |     const auto storage = broker.PopNormalDataToApplet(); | ||||||
|  |     ASSERT(storage != nullptr); | ||||||
|  | 
 | ||||||
|  |     const auto applet_input_data = storage->GetData(); | ||||||
|  |     ASSERT(applet_input_data.size() >= sizeof(StartParamForAmiiboSettings)); | ||||||
|  | 
 | ||||||
|  |     std::memcpy(&applet_input_common, applet_input_data.data(), | ||||||
|  |                 sizeof(StartParamForAmiiboSettings)); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool Cabinet::TransactionComplete() const { | ||||||
|  |     return is_complete; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | Result Cabinet::GetStatus() const { | ||||||
|  |     return ResultSuccess; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void Cabinet::ExecuteInteractive() { | ||||||
|  |     ASSERT_MSG(false, "Attempted to call interactive execution on non-interactive applet."); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void Cabinet::Execute() { | ||||||
|  |     if (is_complete) { | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     const auto callback = [this](bool apply_changes, const std::string& amiibo_name) { | ||||||
|  |         DisplayCompleted(apply_changes, amiibo_name); | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     // TODO: listen on all controllers
 | ||||||
|  |     if (nfp_device == nullptr) { | ||||||
|  |         nfp_device = std::make_shared<Service::NFP::NfpDevice>( | ||||||
|  |             system.HIDCore().GetFirstNpadId(), system, service_context, availability_change_event); | ||||||
|  |         nfp_device->Initialize(); | ||||||
|  |         nfp_device->StartDetection(Service::NFP::TagProtocol::All); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     const Core::Frontend::CabinetParameters parameters{ | ||||||
|  |         .tag_info = applet_input_common.tag_info, | ||||||
|  |         .register_info = applet_input_common.register_info, | ||||||
|  |         .mode = applet_input_common.applet_mode, | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     switch (applet_input_common.applet_mode) { | ||||||
|  |     case Service::NFP::CabinetMode::StartNicknameAndOwnerSettings: | ||||||
|  |     case Service::NFP::CabinetMode::StartGameDataEraser: | ||||||
|  |     case Service::NFP::CabinetMode::StartRestorer: | ||||||
|  |     case Service::NFP::CabinetMode::StartFormatter: | ||||||
|  |         frontend.ShowCabinetApplet(callback, parameters, nfp_device); | ||||||
|  |         break; | ||||||
|  |     default: | ||||||
|  |         UNIMPLEMENTED_MSG("Unknown CabinetMode={}", applet_input_common.applet_mode); | ||||||
|  |         DisplayCompleted(false, {}); | ||||||
|  |         break; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void Cabinet::DisplayCompleted(bool apply_changes, std::string_view amiibo_name) { | ||||||
|  |     Service::Mii::MiiManager manager; | ||||||
|  |     ReturnValueForAmiiboSettings applet_output{}; | ||||||
|  | 
 | ||||||
|  |     if (!apply_changes) { | ||||||
|  |         Cancel(); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if (nfp_device->GetCurrentState() != Service::NFP::DeviceState::TagFound && | ||||||
|  |         nfp_device->GetCurrentState() != Service::NFP::DeviceState::TagMounted) { | ||||||
|  |         Cancel(); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if (nfp_device->GetCurrentState() == Service::NFP::DeviceState::TagFound) { | ||||||
|  |         nfp_device->Mount(Service::NFP::MountTarget::All); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     switch (applet_input_common.applet_mode) { | ||||||
|  |     case Service::NFP::CabinetMode::StartNicknameAndOwnerSettings: { | ||||||
|  |         Service::NFP::AmiiboName name{}; | ||||||
|  |         std::memcpy(name.data(), amiibo_name.data(), std::min(amiibo_name.size(), name.size() - 1)); | ||||||
|  |         nfp_device->SetNicknameAndOwner(name); | ||||||
|  |         break; | ||||||
|  |     } | ||||||
|  |     case Service::NFP::CabinetMode::StartGameDataEraser: | ||||||
|  |         nfp_device->DeleteApplicationArea(); | ||||||
|  |         break; | ||||||
|  |     case Service::NFP::CabinetMode::StartRestorer: | ||||||
|  |         nfp_device->RestoreAmiibo(); | ||||||
|  |         break; | ||||||
|  |     case Service::NFP::CabinetMode::StartFormatter: | ||||||
|  |         nfp_device->DeleteAllData(); | ||||||
|  |         break; | ||||||
|  |     default: | ||||||
|  |         UNIMPLEMENTED_MSG("Unknown CabinetMode={}", applet_input_common.applet_mode); | ||||||
|  |         break; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     applet_output.device_handle = applet_input_common.device_handle; | ||||||
|  |     applet_output.result = CabinetResult::Cancel; | ||||||
|  |     const auto reg_result = nfp_device->GetRegisterInfo(applet_output.register_info); | ||||||
|  |     const auto tag_result = nfp_device->GetTagInfo(applet_output.tag_info); | ||||||
|  |     nfp_device->Finalize(); | ||||||
|  | 
 | ||||||
|  |     if (reg_result.IsSuccess()) { | ||||||
|  |         applet_output.result |= CabinetResult::RegisterInfo; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if (tag_result.IsSuccess()) { | ||||||
|  |         applet_output.result |= CabinetResult::TagInfo; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     std::vector<u8> out_data(sizeof(ReturnValueForAmiiboSettings)); | ||||||
|  |     std::memcpy(out_data.data(), &applet_output, sizeof(ReturnValueForAmiiboSettings)); | ||||||
|  | 
 | ||||||
|  |     is_complete = true; | ||||||
|  | 
 | ||||||
|  |     broker.PushNormalDataFromApplet(std::make_shared<IStorage>(system, std::move(out_data))); | ||||||
|  |     broker.SignalStateChanged(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void Cabinet::Cancel() { | ||||||
|  |     ReturnValueForAmiiboSettings applet_output{}; | ||||||
|  |     applet_output.device_handle = applet_input_common.device_handle; | ||||||
|  |     applet_output.result = CabinetResult::Cancel; | ||||||
|  |     nfp_device->Finalize(); | ||||||
|  | 
 | ||||||
|  |     std::vector<u8> out_data(sizeof(ReturnValueForAmiiboSettings)); | ||||||
|  |     std::memcpy(out_data.data(), &applet_output, sizeof(ReturnValueForAmiiboSettings)); | ||||||
|  | 
 | ||||||
|  |     is_complete = true; | ||||||
|  | 
 | ||||||
|  |     broker.PushNormalDataFromApplet(std::make_shared<IStorage>(system, std::move(out_data))); | ||||||
|  |     broker.SignalStateChanged(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | } // namespace Service::AM::Applets
 | ||||||
							
								
								
									
										104
									
								
								src/core/hle/service/am/applets/applet_cabinet.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										104
									
								
								src/core/hle/service/am/applets/applet_cabinet.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,104 @@ | |||||||
|  | // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
 | ||||||
|  | // SPDX-License-Identifier: GPL-2.0-or-later
 | ||||||
|  | 
 | ||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | #include <array> | ||||||
|  | 
 | ||||||
|  | #include "core/hle/result.h" | ||||||
|  | #include "core/hle/service/am/applets/applets.h" | ||||||
|  | #include "core/hle/service/kernel_helpers.h" | ||||||
|  | #include "core/hle/service/nfp/nfp_types.h" | ||||||
|  | 
 | ||||||
|  | namespace Kernel { | ||||||
|  | class KEvent; | ||||||
|  | class KReadableEvent; | ||||||
|  | } // namespace Kernel
 | ||||||
|  | 
 | ||||||
|  | namespace Core { | ||||||
|  | class System; | ||||||
|  | } // namespace Core
 | ||||||
|  | 
 | ||||||
|  | namespace Service::NFP { | ||||||
|  | class NfpDevice; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | namespace Service::AM::Applets { | ||||||
|  | 
 | ||||||
|  | enum class CabinetAppletVersion : u32 { | ||||||
|  |     Version1 = 0x1, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | enum class CabinetResult : u8 { | ||||||
|  |     Cancel = 0, | ||||||
|  |     TagInfo = 1 << 1, | ||||||
|  |     RegisterInfo = 1 << 2, | ||||||
|  |     All = TagInfo | RegisterInfo, | ||||||
|  | }; | ||||||
|  | DECLARE_ENUM_FLAG_OPERATORS(CabinetResult) | ||||||
|  | 
 | ||||||
|  | // This is nn::nfp::AmiiboSettingsStartParam
 | ||||||
|  | struct AmiiboSettingsStartParam { | ||||||
|  |     u64 device_handle; | ||||||
|  |     std::array<u8, 0x20> param_1; | ||||||
|  |     u8 param_2; | ||||||
|  | }; | ||||||
|  | static_assert(sizeof(AmiiboSettingsStartParam) == 0x30, | ||||||
|  |               "AmiiboSettingsStartParam is an invalid size"); | ||||||
|  | 
 | ||||||
|  | #pragma pack(push, 1) | ||||||
|  | // This is nn::nfp::StartParamForAmiiboSettings
 | ||||||
|  | struct StartParamForAmiiboSettings { | ||||||
|  |     u8 param_1; | ||||||
|  |     Service::NFP::CabinetMode applet_mode; | ||||||
|  |     u8 flags; | ||||||
|  |     u8 amiibo_settings_1; | ||||||
|  |     u64 device_handle; | ||||||
|  |     Service::NFP::TagInfo tag_info; | ||||||
|  |     Service::NFP::RegisterInfo register_info; | ||||||
|  |     std::array<u8, 0x20> amiibo_settings_3; | ||||||
|  |     INSERT_PADDING_BYTES(0x24); | ||||||
|  | }; | ||||||
|  | static_assert(sizeof(StartParamForAmiiboSettings) == 0x1A8, | ||||||
|  |               "StartParamForAmiiboSettings is an invalid size"); | ||||||
|  | 
 | ||||||
|  | // This is nn::nfp::ReturnValueForAmiiboSettings
 | ||||||
|  | struct ReturnValueForAmiiboSettings { | ||||||
|  |     CabinetResult result; | ||||||
|  |     INSERT_PADDING_BYTES(0x3); | ||||||
|  |     u64 device_handle; | ||||||
|  |     Service::NFP::TagInfo tag_info; | ||||||
|  |     Service::NFP::RegisterInfo register_info; | ||||||
|  |     INSERT_PADDING_BYTES(0x24); | ||||||
|  | }; | ||||||
|  | static_assert(sizeof(ReturnValueForAmiiboSettings) == 0x188, | ||||||
|  |               "ReturnValueForAmiiboSettings is an invalid size"); | ||||||
|  | #pragma pack(pop) | ||||||
|  | 
 | ||||||
|  | class Cabinet final : public Applet { | ||||||
|  | public: | ||||||
|  |     explicit Cabinet(Core::System& system_, LibraryAppletMode applet_mode_, | ||||||
|  |                      const Core::Frontend::CabinetApplet& frontend_); | ||||||
|  |     ~Cabinet() override; | ||||||
|  | 
 | ||||||
|  |     void Initialize() override; | ||||||
|  | 
 | ||||||
|  |     bool TransactionComplete() const override; | ||||||
|  |     Result GetStatus() const override; | ||||||
|  |     void ExecuteInteractive() override; | ||||||
|  |     void Execute() override; | ||||||
|  |     void DisplayCompleted(bool apply_changes, std::string_view amiibo_name); | ||||||
|  |     void Cancel(); | ||||||
|  | 
 | ||||||
|  | private: | ||||||
|  |     const Core::Frontend::CabinetApplet& frontend; | ||||||
|  |     Core::System& system; | ||||||
|  | 
 | ||||||
|  |     bool is_complete{false}; | ||||||
|  |     std::shared_ptr<Service::NFP::NfpDevice> nfp_device; | ||||||
|  |     Kernel::KEvent* availability_change_event; | ||||||
|  |     KernelHelpers::ServiceContext service_context; | ||||||
|  |     StartParamForAmiiboSettings applet_input_common{}; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | } // namespace Service::AM::Applets
 | ||||||
| @ -5,6 +5,7 @@ | |||||||
| 
 | 
 | ||||||
| #include "common/assert.h" | #include "common/assert.h" | ||||||
| #include "core/core.h" | #include "core/core.h" | ||||||
|  | #include "core/frontend/applets/cabinet.h" | ||||||
| #include "core/frontend/applets/controller.h" | #include "core/frontend/applets/controller.h" | ||||||
| #include "core/frontend/applets/error.h" | #include "core/frontend/applets/error.h" | ||||||
| #include "core/frontend/applets/general_frontend.h" | #include "core/frontend/applets/general_frontend.h" | ||||||
| @ -16,6 +17,7 @@ | |||||||
| #include "core/hle/service/am/am.h" | #include "core/hle/service/am/am.h" | ||||||
| #include "core/hle/service/am/applet_ae.h" | #include "core/hle/service/am/applet_ae.h" | ||||||
| #include "core/hle/service/am/applet_oe.h" | #include "core/hle/service/am/applet_oe.h" | ||||||
|  | #include "core/hle/service/am/applets/applet_cabinet.h" | ||||||
| #include "core/hle/service/am/applets/applet_controller.h" | #include "core/hle/service/am/applets/applet_controller.h" | ||||||
| #include "core/hle/service/am/applets/applet_error.h" | #include "core/hle/service/am/applets/applet_error.h" | ||||||
| #include "core/hle/service/am/applets/applet_general_backend.h" | #include "core/hle/service/am/applets/applet_general_backend.h" | ||||||
| @ -171,13 +173,15 @@ void Applet::Initialize() { | |||||||
| 
 | 
 | ||||||
| AppletFrontendSet::AppletFrontendSet() = default; | AppletFrontendSet::AppletFrontendSet() = default; | ||||||
| 
 | 
 | ||||||
| AppletFrontendSet::AppletFrontendSet(ControllerApplet controller_applet, ErrorApplet error_applet, | AppletFrontendSet::AppletFrontendSet(CabinetApplet cabinet_applet, | ||||||
|  |                                      ControllerApplet controller_applet, ErrorApplet error_applet, | ||||||
|                                      MiiEdit mii_edit_, |                                      MiiEdit mii_edit_, | ||||||
|                                      ParentalControlsApplet parental_controls_applet, |                                      ParentalControlsApplet parental_controls_applet, | ||||||
|                                      PhotoViewer photo_viewer_, ProfileSelect profile_select_, |                                      PhotoViewer photo_viewer_, ProfileSelect profile_select_, | ||||||
|                                      SoftwareKeyboard software_keyboard_, WebBrowser web_browser_) |                                      SoftwareKeyboard software_keyboard_, WebBrowser web_browser_) | ||||||
|     : controller{std::move(controller_applet)}, error{std::move(error_applet)}, |     : cabinet{std::move(cabinet_applet)}, controller{std::move(controller_applet)}, | ||||||
|       mii_edit{std::move(mii_edit_)}, parental_controls{std::move(parental_controls_applet)}, |       error{std::move(error_applet)}, mii_edit{std::move(mii_edit_)}, | ||||||
|  |       parental_controls{std::move(parental_controls_applet)}, | ||||||
|       photo_viewer{std::move(photo_viewer_)}, profile_select{std::move(profile_select_)}, |       photo_viewer{std::move(photo_viewer_)}, profile_select{std::move(profile_select_)}, | ||||||
|       software_keyboard{std::move(software_keyboard_)}, web_browser{std::move(web_browser_)} {} |       software_keyboard{std::move(software_keyboard_)}, web_browser{std::move(web_browser_)} {} | ||||||
| 
 | 
 | ||||||
| @ -196,6 +200,10 @@ const AppletFrontendSet& AppletManager::GetAppletFrontendSet() const { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void AppletManager::SetAppletFrontendSet(AppletFrontendSet set) { | void AppletManager::SetAppletFrontendSet(AppletFrontendSet set) { | ||||||
|  |     if (set.cabinet != nullptr) { | ||||||
|  |         frontend.cabinet = std::move(set.cabinet); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     if (set.controller != nullptr) { |     if (set.controller != nullptr) { | ||||||
|         frontend.controller = std::move(set.controller); |         frontend.controller = std::move(set.controller); | ||||||
|     } |     } | ||||||
| @ -235,6 +243,10 @@ void AppletManager::SetDefaultAppletFrontendSet() { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void AppletManager::SetDefaultAppletsIfMissing() { | void AppletManager::SetDefaultAppletsIfMissing() { | ||||||
|  |     if (frontend.cabinet == nullptr) { | ||||||
|  |         frontend.cabinet = std::make_unique<Core::Frontend::DefaultCabinetApplet>(); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     if (frontend.controller == nullptr) { |     if (frontend.controller == nullptr) { | ||||||
|         frontend.controller = |         frontend.controller = | ||||||
|             std::make_unique<Core::Frontend::DefaultControllerApplet>(system.HIDCore()); |             std::make_unique<Core::Frontend::DefaultControllerApplet>(system.HIDCore()); | ||||||
| @ -279,6 +291,8 @@ std::shared_ptr<Applet> AppletManager::GetApplet(AppletId id, LibraryAppletMode | |||||||
|     switch (id) { |     switch (id) { | ||||||
|     case AppletId::Auth: |     case AppletId::Auth: | ||||||
|         return std::make_shared<Auth>(system, mode, *frontend.parental_controls); |         return std::make_shared<Auth>(system, mode, *frontend.parental_controls); | ||||||
|  |     case AppletId::Cabinet: | ||||||
|  |         return std::make_shared<Cabinet>(system, mode, *frontend.cabinet); | ||||||
|     case AppletId::Controller: |     case AppletId::Controller: | ||||||
|         return std::make_shared<Controller>(system, mode, *frontend.controller); |         return std::make_shared<Controller>(system, mode, *frontend.controller); | ||||||
|     case AppletId::Error: |     case AppletId::Error: | ||||||
|  | |||||||
| @ -16,6 +16,7 @@ class System; | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| namespace Core::Frontend { | namespace Core::Frontend { | ||||||
|  | class CabinetApplet; | ||||||
| class ControllerApplet; | class ControllerApplet; | ||||||
| class ECommerceApplet; | class ECommerceApplet; | ||||||
| class ErrorApplet; | class ErrorApplet; | ||||||
| @ -176,6 +177,7 @@ protected: | |||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| struct AppletFrontendSet { | struct AppletFrontendSet { | ||||||
|  |     using CabinetApplet = std::unique_ptr<Core::Frontend::CabinetApplet>; | ||||||
|     using ControllerApplet = std::unique_ptr<Core::Frontend::ControllerApplet>; |     using ControllerApplet = std::unique_ptr<Core::Frontend::ControllerApplet>; | ||||||
|     using ErrorApplet = std::unique_ptr<Core::Frontend::ErrorApplet>; |     using ErrorApplet = std::unique_ptr<Core::Frontend::ErrorApplet>; | ||||||
|     using MiiEdit = std::unique_ptr<Core::Frontend::MiiEditApplet>; |     using MiiEdit = std::unique_ptr<Core::Frontend::MiiEditApplet>; | ||||||
| @ -186,10 +188,11 @@ struct AppletFrontendSet { | |||||||
|     using WebBrowser = std::unique_ptr<Core::Frontend::WebBrowserApplet>; |     using WebBrowser = std::unique_ptr<Core::Frontend::WebBrowserApplet>; | ||||||
| 
 | 
 | ||||||
|     AppletFrontendSet(); |     AppletFrontendSet(); | ||||||
|     AppletFrontendSet(ControllerApplet controller_applet, ErrorApplet error_applet, |     AppletFrontendSet(CabinetApplet cabinet_applet, ControllerApplet controller_applet, | ||||||
|                       MiiEdit mii_edit_, ParentalControlsApplet parental_controls_applet, |                       ErrorApplet error_applet, MiiEdit mii_edit_, | ||||||
|                       PhotoViewer photo_viewer_, ProfileSelect profile_select_, |                       ParentalControlsApplet parental_controls_applet, PhotoViewer photo_viewer_, | ||||||
|                       SoftwareKeyboard software_keyboard_, WebBrowser web_browser_); |                       ProfileSelect profile_select_, SoftwareKeyboard software_keyboard_, | ||||||
|  |                       WebBrowser web_browser_); | ||||||
|     ~AppletFrontendSet(); |     ~AppletFrontendSet(); | ||||||
| 
 | 
 | ||||||
|     AppletFrontendSet(const AppletFrontendSet&) = delete; |     AppletFrontendSet(const AppletFrontendSet&) = delete; | ||||||
| @ -198,6 +201,7 @@ struct AppletFrontendSet { | |||||||
|     AppletFrontendSet(AppletFrontendSet&&) noexcept; |     AppletFrontendSet(AppletFrontendSet&&) noexcept; | ||||||
|     AppletFrontendSet& operator=(AppletFrontendSet&&) noexcept; |     AppletFrontendSet& operator=(AppletFrontendSet&&) noexcept; | ||||||
| 
 | 
 | ||||||
|  |     CabinetApplet cabinet; | ||||||
|     ControllerApplet controller; |     ControllerApplet controller; | ||||||
|     ErrorApplet error; |     ErrorApplet error; | ||||||
|     MiiEdit mii_edit; |     MiiEdit mii_edit; | ||||||
|  | |||||||
| @ -77,6 +77,9 @@ void NfpDevice::NpadUpdate(Core::HID::ControllerTriggerType type) { | |||||||
|         LoadAmiibo(nfc_status.data); |         LoadAmiibo(nfc_status.data); | ||||||
|         break; |         break; | ||||||
|     case Common::Input::NfcState::AmiiboRemoved: |     case Common::Input::NfcState::AmiiboRemoved: | ||||||
|  |         if (device_state == DeviceState::Initialized || device_state == DeviceState::TagRemoved) { | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|         if (device_state != DeviceState::SearchingForTag) { |         if (device_state != DeviceState::SearchingForTag) { | ||||||
|             CloseAmiibo(); |             CloseAmiibo(); | ||||||
|         } |         } | ||||||
| @ -97,6 +100,8 @@ bool NfpDevice::LoadAmiibo(std::span<const u8> data) { | |||||||
|         return false; |         return false; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     // TODO: Filter by allowed_protocols here
 | ||||||
|  | 
 | ||||||
|     memcpy(&encrypted_tag_data, data.data(), sizeof(EncryptedNTAG215File)); |     memcpy(&encrypted_tag_data, data.data(), sizeof(EncryptedNTAG215File)); | ||||||
| 
 | 
 | ||||||
|     device_state = DeviceState::TagFound; |     device_state = DeviceState::TagFound; | ||||||
| @ -143,7 +148,7 @@ void NfpDevice::Finalize() { | |||||||
|     device_state = DeviceState::Unavailable; |     device_state = DeviceState::Unavailable; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| Result NfpDevice::StartDetection(s32 protocol_) { | Result NfpDevice::StartDetection(TagProtocol allowed_protocol) { | ||||||
|     if (device_state != DeviceState::Initialized && device_state != DeviceState::TagRemoved) { |     if (device_state != DeviceState::Initialized && device_state != DeviceState::TagRemoved) { | ||||||
|         LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); |         LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); | ||||||
|         return WrongDeviceState; |         return WrongDeviceState; | ||||||
| @ -155,7 +160,7 @@ Result NfpDevice::StartDetection(s32 protocol_) { | |||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     device_state = DeviceState::SearchingForTag; |     device_state = DeviceState::SearchingForTag; | ||||||
|     protocol = protocol_; |     allowed_protocols = allowed_protocol; | ||||||
|     return ResultSuccess; |     return ResultSuccess; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @ -469,6 +474,32 @@ Result NfpDevice::OpenApplicationArea(u32 access_id) { | |||||||
|     return ResultSuccess; |     return ResultSuccess; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | Result NfpDevice::GetApplicationAreaId(u32& application_area_id) const { | ||||||
|  |     application_area_id = {}; | ||||||
|  | 
 | ||||||
|  |     if (device_state != DeviceState::TagMounted) { | ||||||
|  |         LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); | ||||||
|  |         if (device_state == DeviceState::TagRemoved) { | ||||||
|  |             return TagRemoved; | ||||||
|  |         } | ||||||
|  |         return WrongDeviceState; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if (mount_target == MountTarget::None || mount_target == MountTarget::Rom) { | ||||||
|  |         LOG_ERROR(Service_NFP, "Amiibo is read only", device_state); | ||||||
|  |         return WrongDeviceState; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if (tag_data.settings.settings.appdata_initialized.Value() == 0) { | ||||||
|  |         LOG_WARNING(Service_NFP, "Application area is not initialized"); | ||||||
|  |         return ApplicationAreaIsNotInitialized; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     application_area_id = tag_data.application_area_id; | ||||||
|  | 
 | ||||||
|  |     return ResultSuccess; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| Result NfpDevice::GetApplicationArea(std::vector<u8>& data) const { | Result NfpDevice::GetApplicationArea(std::vector<u8>& data) const { | ||||||
|     if (device_state != DeviceState::TagMounted) { |     if (device_state != DeviceState::TagMounted) { | ||||||
|         LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); |         LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); | ||||||
|  | |||||||
| @ -4,6 +4,7 @@ | |||||||
| #pragma once | #pragma once | ||||||
| 
 | 
 | ||||||
| #include <array> | #include <array> | ||||||
|  | #include <span> | ||||||
| #include <vector> | #include <vector> | ||||||
| 
 | 
 | ||||||
| #include "common/common_funcs.h" | #include "common/common_funcs.h" | ||||||
| @ -37,7 +38,7 @@ public: | |||||||
|     void Initialize(); |     void Initialize(); | ||||||
|     void Finalize(); |     void Finalize(); | ||||||
| 
 | 
 | ||||||
|     Result StartDetection(s32 protocol_); |     Result StartDetection(TagProtocol allowed_protocol); | ||||||
|     Result StopDetection(); |     Result StopDetection(); | ||||||
|     Result Mount(MountTarget mount_target); |     Result Mount(MountTarget mount_target); | ||||||
|     Result Unmount(); |     Result Unmount(); | ||||||
| @ -53,6 +54,7 @@ public: | |||||||
|     Result DeleteAllData(); |     Result DeleteAllData(); | ||||||
| 
 | 
 | ||||||
|     Result OpenApplicationArea(u32 access_id); |     Result OpenApplicationArea(u32 access_id); | ||||||
|  |     Result GetApplicationAreaId(u32& application_area_id) const; | ||||||
|     Result GetApplicationArea(std::vector<u8>& data) const; |     Result GetApplicationArea(std::vector<u8>& data) const; | ||||||
|     Result SetApplicationArea(std::span<const u8> data); |     Result SetApplicationArea(std::span<const u8> data); | ||||||
|     Result CreateApplicationArea(u32 access_id, std::span<const u8> data); |     Result CreateApplicationArea(u32 access_id, std::span<const u8> data); | ||||||
| @ -88,7 +90,7 @@ private: | |||||||
| 
 | 
 | ||||||
|     bool is_data_moddified{}; |     bool is_data_moddified{}; | ||||||
|     bool is_app_area_open{}; |     bool is_app_area_open{}; | ||||||
|     s32 protocol{}; |     TagProtocol allowed_protocols{}; | ||||||
|     s64 current_posix_time{}; |     s64 current_posix_time{}; | ||||||
|     MountTarget mount_target{MountTarget::None}; |     MountTarget mount_target{MountTarget::None}; | ||||||
|     DeviceState device_state{DeviceState::Unavailable}; |     DeviceState device_state{DeviceState::Unavailable}; | ||||||
|  | |||||||
| @ -88,11 +88,22 @@ enum class PackedTagType : u8 { | |||||||
|     Type5, // ISO15693 RW/RO 540 bytes 106kbit/s
 |     Type5, // ISO15693 RW/RO 540 bytes 106kbit/s
 | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  | // Verify this enum. It might be completely wrong default protocol is 0x48
 | ||||||
| enum class TagProtocol : u32 { | enum class TagProtocol : u32 { | ||||||
|     None, |     None, | ||||||
|     TypeA, // ISO14443A
 |     TypeA = 1U << 0, // ISO14443A
 | ||||||
|     TypeB, // ISO14443B
 |     TypeB = 1U << 1, // ISO14443B
 | ||||||
|     TypeF, // Sony Felica
 |     TypeF = 1U << 2, // Sony Felica
 | ||||||
|  |     Unknown1 = 1U << 3, | ||||||
|  |     Unknown2 = 1U << 5, | ||||||
|  |     All = 0xFFFFFFFFU, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | enum class CabinetMode : u8 { | ||||||
|  |     StartNicknameAndOwnerSettings, | ||||||
|  |     StartGameDataEraser, | ||||||
|  |     StartRestorer, | ||||||
|  |     StartFormatter, | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| using UniqueSerialNumber = std::array<u8, 7>; | using UniqueSerialNumber = std::array<u8, 7>; | ||||||
|  | |||||||
| @ -130,7 +130,7 @@ void IUser::ListDevices(Kernel::HLERequestContext& ctx) { | |||||||
| void IUser::StartDetection(Kernel::HLERequestContext& ctx) { | void IUser::StartDetection(Kernel::HLERequestContext& ctx) { | ||||||
|     IPC::RequestParser rp{ctx}; |     IPC::RequestParser rp{ctx}; | ||||||
|     const auto device_handle{rp.Pop<u64>()}; |     const auto device_handle{rp.Pop<u64>()}; | ||||||
|     const auto nfp_protocol{rp.Pop<s32>()}; |     const auto nfp_protocol{rp.PopEnum<TagProtocol>()}; | ||||||
|     LOG_INFO(Service_NFP, "called, device_handle={}, nfp_protocol={}", device_handle, nfp_protocol); |     LOG_INFO(Service_NFP, "called, device_handle={}, nfp_protocol={}", device_handle, nfp_protocol); | ||||||
| 
 | 
 | ||||||
|     if (state == State::NonInitialized) { |     if (state == State::NonInitialized) { | ||||||
|  | |||||||
| @ -60,6 +60,8 @@ Common::Input::NfcState VirtualAmiibo::WriteNfcData( | |||||||
|         return Common::Input::NfcState::WriteFailed; |         return Common::Input::NfcState::WriteFailed; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     amiibo_data = data; | ||||||
|  | 
 | ||||||
|     return Common::Input::NfcState::Success; |     return Common::Input::NfcState::Success; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @ -91,6 +93,15 @@ VirtualAmiibo::Info VirtualAmiibo::LoadAmiibo(const std::string& filename) { | |||||||
|     return Info::Success; |     return Info::Success; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | VirtualAmiibo::Info VirtualAmiibo::ReloadAmiibo() { | ||||||
|  |     if (state == State::AmiiboIsOpen) { | ||||||
|  |         SetNfc(identifier, {Common::Input::NfcState::NewAmiibo, amiibo_data}); | ||||||
|  |         return Info::Success; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return LoadAmiibo(file_path); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| VirtualAmiibo::Info VirtualAmiibo::CloseAmiibo() { | VirtualAmiibo::Info VirtualAmiibo::CloseAmiibo() { | ||||||
|     state = polling_mode == Common::Input::PollingMode::NFC ? State::WaitingForAmiibo |     state = polling_mode == Common::Input::PollingMode::NFC ? State::WaitingForAmiibo | ||||||
|                                                             : State::Initialized; |                                                             : State::Initialized; | ||||||
| @ -98,4 +109,8 @@ VirtualAmiibo::Info VirtualAmiibo::CloseAmiibo() { | |||||||
|     return Info::Success; |     return Info::Success; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | std::string VirtualAmiibo::GetLastFilePath() const { | ||||||
|  |     return file_path; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| } // namespace InputCommon
 | } // namespace InputCommon
 | ||||||
|  | |||||||
| @ -47,8 +47,11 @@ public: | |||||||
|     State GetCurrentState() const; |     State GetCurrentState() const; | ||||||
| 
 | 
 | ||||||
|     Info LoadAmiibo(const std::string& amiibo_file); |     Info LoadAmiibo(const std::string& amiibo_file); | ||||||
|  |     Info ReloadAmiibo(); | ||||||
|     Info CloseAmiibo(); |     Info CloseAmiibo(); | ||||||
| 
 | 
 | ||||||
|  |     std::string GetLastFilePath() const; | ||||||
|  | 
 | ||||||
| private: | private: | ||||||
|     static constexpr std::size_t amiibo_size = 0x21C; |     static constexpr std::size_t amiibo_size = 0x21C; | ||||||
|     static constexpr std::size_t amiibo_size_without_password = amiibo_size - 0x8; |     static constexpr std::size_t amiibo_size_without_password = amiibo_size - 0x8; | ||||||
|  | |||||||
| @ -133,7 +133,7 @@ public: | |||||||
|         return Common::Input::CameraError::NotSupported; |         return Common::Input::CameraError::NotSupported; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     // Request nfc data from a controller
 |     // Returns success if nfc is supported
 | ||||||
|     virtual Common::Input::NfcState SupportsNfc( |     virtual Common::Input::NfcState SupportsNfc( | ||||||
|         [[maybe_unused]] const PadIdentifier& identifier) const { |         [[maybe_unused]] const PadIdentifier& identifier) const { | ||||||
|         return Common::Input::NfcState::NotSupported; |         return Common::Input::NfcState::NotSupported; | ||||||
|  | |||||||
| @ -18,6 +18,9 @@ add_executable(yuzu | |||||||
|     about_dialog.cpp |     about_dialog.cpp | ||||||
|     about_dialog.h |     about_dialog.h | ||||||
|     aboutdialog.ui |     aboutdialog.ui | ||||||
|  |     applets/qt_amiibo_settings.cpp | ||||||
|  |     applets/qt_amiibo_settings.h | ||||||
|  |     applets/qt_amiibo_settings.ui | ||||||
|     applets/qt_controller.cpp |     applets/qt_controller.cpp | ||||||
|     applets/qt_controller.h |     applets/qt_controller.h | ||||||
|     applets/qt_controller.ui |     applets/qt_controller.ui | ||||||
|  | |||||||
							
								
								
									
										260
									
								
								src/yuzu/applets/qt_amiibo_settings.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										260
									
								
								src/yuzu/applets/qt_amiibo_settings.cpp
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,260 @@ | |||||||
|  | // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
 | ||||||
|  | // SPDX-License-Identifier: GPL-2.0-or-later
 | ||||||
|  | 
 | ||||||
|  | #include <algorithm> | ||||||
|  | #include <thread> | ||||||
|  | #include <fmt/format.h> | ||||||
|  | #include <nlohmann/json.hpp> | ||||||
|  | 
 | ||||||
|  | #include "common/assert.h" | ||||||
|  | #include "common/string_util.h" | ||||||
|  | #include "core/hle/service/nfp/nfp_device.h" | ||||||
|  | #include "core/hle/service/nfp/nfp_result.h" | ||||||
|  | #include "input_common/drivers/virtual_amiibo.h" | ||||||
|  | #include "input_common/main.h" | ||||||
|  | #include "ui_qt_amiibo_settings.h" | ||||||
|  | #include "web_service/web_backend.h" | ||||||
|  | #include "yuzu/applets/qt_amiibo_settings.h" | ||||||
|  | #include "yuzu/main.h" | ||||||
|  | 
 | ||||||
|  | QtAmiiboSettingsDialog::QtAmiiboSettingsDialog(QWidget* parent, | ||||||
|  |                                                Core::Frontend::CabinetParameters parameters_, | ||||||
|  |                                                InputCommon::InputSubsystem* input_subsystem_, | ||||||
|  |                                                std::shared_ptr<Service::NFP::NfpDevice> nfp_device_) | ||||||
|  |     : QDialog(parent), ui(std::make_unique<Ui::QtAmiiboSettingsDialog>()), | ||||||
|  |       input_subsystem{input_subsystem_}, nfp_device{std::move(nfp_device_)}, | ||||||
|  |       parameters(std::move(parameters_)) { | ||||||
|  |     ui->setupUi(this); | ||||||
|  | 
 | ||||||
|  |     LoadInfo(); | ||||||
|  | 
 | ||||||
|  |     resize(0, 0); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | QtAmiiboSettingsDialog::~QtAmiiboSettingsDialog() = default; | ||||||
|  | 
 | ||||||
|  | int QtAmiiboSettingsDialog::exec() { | ||||||
|  |     if (!is_initalized) { | ||||||
|  |         return QDialog::Rejected; | ||||||
|  |     } | ||||||
|  |     return QDialog::exec(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | std::string QtAmiiboSettingsDialog::GetName() const { | ||||||
|  |     return ui->amiiboCustomNameValue->text().toStdString(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void QtAmiiboSettingsDialog::LoadInfo() { | ||||||
|  |     if (input_subsystem->GetVirtualAmiibo()->ReloadAmiibo() != | ||||||
|  |         InputCommon::VirtualAmiibo::Info::Success) { | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if (nfp_device->GetCurrentState() != Service::NFP::DeviceState::TagFound && | ||||||
|  |         nfp_device->GetCurrentState() != Service::NFP::DeviceState::TagMounted) { | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  |     nfp_device->Mount(Service::NFP::MountTarget::All); | ||||||
|  | 
 | ||||||
|  |     LoadAmiiboInfo(); | ||||||
|  |     LoadAmiiboData(); | ||||||
|  |     LoadAmiiboGameInfo(); | ||||||
|  | 
 | ||||||
|  |     ui->amiiboDirectoryValue->setText( | ||||||
|  |         QString::fromStdString(input_subsystem->GetVirtualAmiibo()->GetLastFilePath())); | ||||||
|  | 
 | ||||||
|  |     SetSettingsDescription(); | ||||||
|  |     is_initalized = true; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void QtAmiiboSettingsDialog::LoadAmiiboInfo() { | ||||||
|  |     Service::NFP::ModelInfo model_info{}; | ||||||
|  |     const auto model_result = nfp_device->GetModelInfo(model_info); | ||||||
|  | 
 | ||||||
|  |     if (model_result.IsFailure()) { | ||||||
|  |         ui->amiiboImageLabel->setVisible(false); | ||||||
|  |         ui->amiiboInfoGroup->setVisible(false); | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     const auto amiibo_id = | ||||||
|  |         fmt::format("{:04x}{:02x}{:02x}{:04x}{:02x}02", Common::swap16(model_info.character_id), | ||||||
|  |                     model_info.character_variant, model_info.amiibo_type, model_info.model_number, | ||||||
|  |                     model_info.series); | ||||||
|  | 
 | ||||||
|  |     LOG_DEBUG(Frontend, "Loading amiibo id {}", amiibo_id); | ||||||
|  |     // Note: This function is not being used until we host the images on our server
 | ||||||
|  |     // LoadAmiiboApiInfo(amiibo_id);
 | ||||||
|  |     ui->amiiboImageLabel->setVisible(false); | ||||||
|  |     ui->amiiboInfoGroup->setVisible(false); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void QtAmiiboSettingsDialog::LoadAmiiboApiInfo(std::string_view amiibo_id) { | ||||||
|  |     // TODO: Host this data on our website
 | ||||||
|  |     WebService::Client client{"https://amiiboapi.com", {}, {}}; | ||||||
|  |     WebService::Client image_client{"https://raw.githubusercontent.com", {}, {}}; | ||||||
|  |     const auto url_path = fmt::format("/api/amiibo/?id={}", amiibo_id); | ||||||
|  | 
 | ||||||
|  |     const auto amiibo_json = client.GetJson(url_path, true).returned_data; | ||||||
|  |     if (amiibo_json.empty()) { | ||||||
|  |         ui->amiiboImageLabel->setVisible(false); | ||||||
|  |         ui->amiiboInfoGroup->setVisible(false); | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     std::string amiibo_series{}; | ||||||
|  |     std::string amiibo_name{}; | ||||||
|  |     std::string amiibo_image_url{}; | ||||||
|  |     std::string amiibo_type{}; | ||||||
|  | 
 | ||||||
|  |     const auto parsed_amiibo_json_json = nlohmann::json::parse(amiibo_json).at("amiibo"); | ||||||
|  |     parsed_amiibo_json_json.at("amiiboSeries").get_to(amiibo_series); | ||||||
|  |     parsed_amiibo_json_json.at("name").get_to(amiibo_name); | ||||||
|  |     parsed_amiibo_json_json.at("image").get_to(amiibo_image_url); | ||||||
|  |     parsed_amiibo_json_json.at("type").get_to(amiibo_type); | ||||||
|  | 
 | ||||||
|  |     ui->amiiboSeriesValue->setText(QString::fromStdString(amiibo_series)); | ||||||
|  |     ui->amiiboNameValue->setText(QString::fromStdString(amiibo_name)); | ||||||
|  |     ui->amiiboTypeValue->setText(QString::fromStdString(amiibo_type)); | ||||||
|  | 
 | ||||||
|  |     if (amiibo_image_url.size() < 34) { | ||||||
|  |         ui->amiiboImageLabel->setVisible(false); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     const auto image_url_path = amiibo_image_url.substr(34, amiibo_image_url.size() - 34); | ||||||
|  |     const auto image_data = image_client.GetImage(image_url_path, true).returned_data; | ||||||
|  | 
 | ||||||
|  |     if (image_data.empty()) { | ||||||
|  |         ui->amiiboImageLabel->setVisible(false); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     QPixmap pixmap; | ||||||
|  |     pixmap.loadFromData(reinterpret_cast<const u8*>(image_data.data()), | ||||||
|  |                         static_cast<uint>(image_data.size())); | ||||||
|  |     pixmap = pixmap.scaled(250, 350, Qt::AspectRatioMode::KeepAspectRatio, | ||||||
|  |                            Qt::TransformationMode::SmoothTransformation); | ||||||
|  |     ui->amiiboImageLabel->setPixmap(pixmap); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void QtAmiiboSettingsDialog::LoadAmiiboData() { | ||||||
|  |     Service::NFP::RegisterInfo register_info{}; | ||||||
|  |     Service::NFP::CommonInfo common_info{}; | ||||||
|  |     const auto register_result = nfp_device->GetRegisterInfo(register_info); | ||||||
|  |     const auto common_result = nfp_device->GetCommonInfo(common_info); | ||||||
|  | 
 | ||||||
|  |     if (register_result.IsFailure()) { | ||||||
|  |         ui->creationDateValue->setDisabled(true); | ||||||
|  |         ui->modificationDateValue->setDisabled(true); | ||||||
|  |         ui->amiiboCustomNameValue->setReadOnly(false); | ||||||
|  |         ui->amiiboOwnerValue->setReadOnly(false); | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if (parameters.mode == Service::NFP::CabinetMode::StartNicknameAndOwnerSettings) { | ||||||
|  |         ui->creationDateValue->setDisabled(true); | ||||||
|  |         ui->modificationDateValue->setDisabled(true); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     const auto amiibo_name = std::string(register_info.amiibo_name.data()); | ||||||
|  |     const auto owner_name = Common::UTF16ToUTF8(register_info.mii_char_info.name.data()); | ||||||
|  |     const auto creation_date = | ||||||
|  |         QDate(register_info.creation_date.year, register_info.creation_date.month, | ||||||
|  |               register_info.creation_date.day); | ||||||
|  | 
 | ||||||
|  |     ui->amiiboCustomNameValue->setText(QString::fromStdString(amiibo_name)); | ||||||
|  |     ui->amiiboOwnerValue->setText(QString::fromStdString(owner_name)); | ||||||
|  |     ui->amiiboCustomNameValue->setReadOnly(true); | ||||||
|  |     ui->amiiboOwnerValue->setReadOnly(true); | ||||||
|  |     ui->creationDateValue->setDate(creation_date); | ||||||
|  | 
 | ||||||
|  |     if (common_result.IsFailure()) { | ||||||
|  |         ui->modificationDateValue->setDisabled(true); | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     const auto modification_date = | ||||||
|  |         QDate(common_info.last_write_date.year, common_info.last_write_date.month, | ||||||
|  |               common_info.last_write_date.day); | ||||||
|  |     ui->modificationDateValue->setDate(modification_date); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void QtAmiiboSettingsDialog::LoadAmiiboGameInfo() { | ||||||
|  |     u32 application_area_id{}; | ||||||
|  |     const auto application_result = nfp_device->GetApplicationAreaId(application_area_id); | ||||||
|  | 
 | ||||||
|  |     if (application_result.IsFailure()) { | ||||||
|  |         ui->gameIdValue->setVisible(false); | ||||||
|  |         ui->gameIdLabel->setText(tr("No game data present")); | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     SetGameDataName(application_area_id); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void QtAmiiboSettingsDialog::SetGameDataName(u32 application_area_id) { | ||||||
|  |     static constexpr std::array<std::pair<u32, const char*>, 12> game_name_list = { | ||||||
|  |         // 3ds, wii u
 | ||||||
|  |         std::pair<u32, const char*>{0x10110E00, "Super Smash Bros (3DS/WiiU)"}, | ||||||
|  |         {0x00132600, "Mario & Luigi: Paper Jam"}, | ||||||
|  |         {0x0014F000, "Animal Crossing: Happy Home Designer"}, | ||||||
|  |         {0x00152600, "Chibi-Robo!: Zip Lash"}, | ||||||
|  |         {0x10161f00, "Mario Party 10"}, | ||||||
|  |         {0x1019C800, "The Legend of Zelda: Twilight Princess HD"}, | ||||||
|  |         // switch
 | ||||||
|  |         {0x10162B00, "Splatoon 2"}, | ||||||
|  |         {0x1016e100, "Shovel Knight: Treasure Trove"}, | ||||||
|  |         {0x1019C800, "The Legend of Zelda: Breath of the Wild"}, | ||||||
|  |         {0x34F80200, "Super Smash Bros. Ultimate"}, | ||||||
|  |         {0x38600500, "Splatoon 3"}, | ||||||
|  |         {0x3B440400, "The Legend of Zelda: Link's Awakening"}, | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     for (const auto& [game_id, game_name] : game_name_list) { | ||||||
|  |         if (application_area_id == game_id) { | ||||||
|  |             ui->gameIdValue->setText(QString::fromStdString(game_name)); | ||||||
|  |             return; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     const auto application_area_string = fmt::format("{:016x}", application_area_id); | ||||||
|  |     ui->gameIdValue->setText(QString::fromStdString(application_area_string)); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void QtAmiiboSettingsDialog::SetSettingsDescription() { | ||||||
|  |     switch (parameters.mode) { | ||||||
|  |     case Service::NFP::CabinetMode::StartFormatter: | ||||||
|  |         ui->cabinetActionDescriptionLabel->setText( | ||||||
|  |             tr("The following amiibo data will be formatted:")); | ||||||
|  |         break; | ||||||
|  |     case Service::NFP::CabinetMode::StartGameDataEraser: | ||||||
|  |         ui->cabinetActionDescriptionLabel->setText(tr("The following game data will removed:")); | ||||||
|  |         break; | ||||||
|  |     case Service::NFP::CabinetMode::StartNicknameAndOwnerSettings: | ||||||
|  |         ui->cabinetActionDescriptionLabel->setText(tr("Set nickname and owner:")); | ||||||
|  |         break; | ||||||
|  |     case Service::NFP::CabinetMode::StartRestorer: | ||||||
|  |         ui->cabinetActionDescriptionLabel->setText(tr("Do you wish to restore this amiibo?")); | ||||||
|  |         break; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | QtAmiiboSettings::QtAmiiboSettings(GMainWindow& parent) { | ||||||
|  |     connect(this, &QtAmiiboSettings::MainWindowShowAmiiboSettings, &parent, | ||||||
|  |             &GMainWindow::AmiiboSettingsShowDialog, Qt::QueuedConnection); | ||||||
|  |     connect(&parent, &GMainWindow::AmiiboSettingsFinished, this, | ||||||
|  |             &QtAmiiboSettings::MainWindowFinished, Qt::QueuedConnection); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | QtAmiiboSettings::~QtAmiiboSettings() = default; | ||||||
|  | 
 | ||||||
|  | void QtAmiiboSettings::ShowCabinetApplet( | ||||||
|  |     const Core::Frontend::CabinetCallback& callback_, | ||||||
|  |     const Core::Frontend::CabinetParameters& parameters, | ||||||
|  |     std::shared_ptr<Service::NFP::NfpDevice> nfp_device) const { | ||||||
|  |     callback = std::move(callback_); | ||||||
|  |     emit MainWindowShowAmiiboSettings(parameters, nfp_device); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void QtAmiiboSettings::MainWindowFinished(bool is_success, const std::string& name) { | ||||||
|  |     callback(is_success, name); | ||||||
|  | } | ||||||
							
								
								
									
										83
									
								
								src/yuzu/applets/qt_amiibo_settings.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										83
									
								
								src/yuzu/applets/qt_amiibo_settings.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,83 @@ | |||||||
|  | // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
 | ||||||
|  | // SPDX-License-Identifier: GPL-2.0-or-later
 | ||||||
|  | 
 | ||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | #include <array> | ||||||
|  | #include <memory> | ||||||
|  | #include <QDialog> | ||||||
|  | #include "core/frontend/applets/cabinet.h" | ||||||
|  | 
 | ||||||
|  | class GMainWindow; | ||||||
|  | class QCheckBox; | ||||||
|  | class QComboBox; | ||||||
|  | class QDialogButtonBox; | ||||||
|  | class QGroupBox; | ||||||
|  | class QLabel; | ||||||
|  | 
 | ||||||
|  | namespace InputCommon { | ||||||
|  | class InputSubsystem; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | namespace Ui { | ||||||
|  | class QtAmiiboSettingsDialog; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | namespace Service::NFP { | ||||||
|  | class NfpDevice; | ||||||
|  | } // namespace Service::NFP
 | ||||||
|  | 
 | ||||||
|  | class QtAmiiboSettingsDialog final : public QDialog { | ||||||
|  |     Q_OBJECT | ||||||
|  | 
 | ||||||
|  | public: | ||||||
|  |     explicit QtAmiiboSettingsDialog(QWidget* parent, Core::Frontend::CabinetParameters parameters_, | ||||||
|  |                                     InputCommon::InputSubsystem* input_subsystem_, | ||||||
|  |                                     std::shared_ptr<Service::NFP::NfpDevice> nfp_device_); | ||||||
|  |     ~QtAmiiboSettingsDialog() override; | ||||||
|  | 
 | ||||||
|  |     int exec() override; | ||||||
|  | 
 | ||||||
|  |     std::string GetName() const; | ||||||
|  | 
 | ||||||
|  | private: | ||||||
|  |     void LoadInfo(); | ||||||
|  |     void LoadAmiiboInfo(); | ||||||
|  |     void LoadAmiiboApiInfo(std::string_view amiibo_id); | ||||||
|  |     void LoadAmiiboData(); | ||||||
|  |     void LoadAmiiboGameInfo(); | ||||||
|  |     void SetGameDataName(u32 application_area_id); | ||||||
|  |     void SetSettingsDescription(); | ||||||
|  | 
 | ||||||
|  |     std::unique_ptr<Ui::QtAmiiboSettingsDialog> ui; | ||||||
|  | 
 | ||||||
|  |     InputCommon::InputSubsystem* input_subsystem; | ||||||
|  |     std::shared_ptr<Service::NFP::NfpDevice> nfp_device; | ||||||
|  | 
 | ||||||
|  |     // Parameters sent in from the backend HLE applet.
 | ||||||
|  |     Core::Frontend::CabinetParameters parameters; | ||||||
|  | 
 | ||||||
|  |     // If false amiibo settings failed to load
 | ||||||
|  |     bool is_initalized{}; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | class QtAmiiboSettings final : public QObject, public Core::Frontend::CabinetApplet { | ||||||
|  |     Q_OBJECT | ||||||
|  | 
 | ||||||
|  | public: | ||||||
|  |     explicit QtAmiiboSettings(GMainWindow& parent); | ||||||
|  |     ~QtAmiiboSettings() override; | ||||||
|  | 
 | ||||||
|  |     void ShowCabinetApplet(const Core::Frontend::CabinetCallback& callback_, | ||||||
|  |                            const Core::Frontend::CabinetParameters& parameters, | ||||||
|  |                            std::shared_ptr<Service::NFP::NfpDevice> nfp_device) const override; | ||||||
|  | 
 | ||||||
|  | signals: | ||||||
|  |     void MainWindowShowAmiiboSettings(const Core::Frontend::CabinetParameters& parameters, | ||||||
|  |                                       std::shared_ptr<Service::NFP::NfpDevice> nfp_device) const; | ||||||
|  | 
 | ||||||
|  | private: | ||||||
|  |     void MainWindowFinished(bool is_success, const std::string& name); | ||||||
|  | 
 | ||||||
|  |     mutable Core::Frontend::CabinetCallback callback; | ||||||
|  | }; | ||||||
							
								
								
									
										494
									
								
								src/yuzu/applets/qt_amiibo_settings.ui
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										494
									
								
								src/yuzu/applets/qt_amiibo_settings.ui
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,494 @@ | |||||||
|  | <?xml version="1.0" encoding="UTF-8"?> | ||||||
|  | <ui version="4.0"> | ||||||
|  |  <class>QtAmiiboSettingsDialog</class> | ||||||
|  |  <widget class="QDialog" name="QtAmiiboSettingsDialog"> | ||||||
|  |   <property name="geometry"> | ||||||
|  |    <rect> | ||||||
|  |     <x>0</x> | ||||||
|  |     <y>0</y> | ||||||
|  |     <width>839</width> | ||||||
|  |     <height>500</height> | ||||||
|  |    </rect> | ||||||
|  |   </property> | ||||||
|  |   <property name="windowTitle"> | ||||||
|  |    <string>Amiibo Settings</string> | ||||||
|  |   </property> | ||||||
|  |   <property name="styleSheet"> | ||||||
|  |    <string notr="true"/> | ||||||
|  |   </property> | ||||||
|  |   <layout class="QVBoxLayout" name="verticalLayout" stretch="0"> | ||||||
|  |    <property name="leftMargin"> | ||||||
|  |     <number>0</number> | ||||||
|  |    </property> | ||||||
|  |    <property name="topMargin"> | ||||||
|  |     <number>0</number> | ||||||
|  |    </property> | ||||||
|  |    <property name="rightMargin"> | ||||||
|  |     <number>0</number> | ||||||
|  |    </property> | ||||||
|  |    <property name="bottomMargin"> | ||||||
|  |     <number>0</number> | ||||||
|  |    </property> | ||||||
|  |    <item> | ||||||
|  |     <widget class="QWidget" name="mainControllerApplet" native="true"> | ||||||
|  |      <layout class="QVBoxLayout" name="verticalLayout_1" stretch="0,3,0"> | ||||||
|  |       <property name="spacing"> | ||||||
|  |        <number>0</number> | ||||||
|  |       </property> | ||||||
|  |       <property name="leftMargin"> | ||||||
|  |        <number>0</number> | ||||||
|  |       </property> | ||||||
|  |       <property name="topMargin"> | ||||||
|  |        <number>0</number> | ||||||
|  |       </property> | ||||||
|  |       <property name="rightMargin"> | ||||||
|  |        <number>0</number> | ||||||
|  |       </property> | ||||||
|  |       <property name="bottomMargin"> | ||||||
|  |        <number>0</number> | ||||||
|  |       </property> | ||||||
|  |       <item> | ||||||
|  |        <widget class="QWidget" name="topControllerApplet" native="true"> | ||||||
|  |         <layout class="QHBoxLayout" name="horizontalLayout"> | ||||||
|  |          <property name="spacing"> | ||||||
|  |           <number>10</number> | ||||||
|  |          </property> | ||||||
|  |          <property name="leftMargin"> | ||||||
|  |           <number>20</number> | ||||||
|  |          </property> | ||||||
|  |          <property name="topMargin"> | ||||||
|  |           <number>15</number> | ||||||
|  |          </property> | ||||||
|  |          <property name="rightMargin"> | ||||||
|  |           <number>0</number> | ||||||
|  |          </property> | ||||||
|  |          <property name="bottomMargin"> | ||||||
|  |           <number>15</number> | ||||||
|  |          </property> | ||||||
|  |          <item> | ||||||
|  |           <widget class="QLabel" name="cabinetActionDescriptionLabel"> | ||||||
|  |            <property name="font"> | ||||||
|  |             <font> | ||||||
|  |              <pointsize>12</pointsize> | ||||||
|  |              <weight>75</weight> | ||||||
|  |              <bold>true</bold> | ||||||
|  |             </font> | ||||||
|  |            </property> | ||||||
|  |            <property name="text"> | ||||||
|  |             <string/> | ||||||
|  |            </property> | ||||||
|  |            <property name="alignment"> | ||||||
|  |             <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set> | ||||||
|  |            </property> | ||||||
|  |            <property name="wordWrap"> | ||||||
|  |             <bool>false</bool> | ||||||
|  |            </property> | ||||||
|  |           </widget> | ||||||
|  |          </item> | ||||||
|  |          <item> | ||||||
|  |           <spacer name="horizontalSpacer_2"> | ||||||
|  |            <property name="orientation"> | ||||||
|  |             <enum>Qt::Horizontal</enum> | ||||||
|  |            </property> | ||||||
|  |            <property name="sizeHint" stdset="0"> | ||||||
|  |             <size> | ||||||
|  |              <width>40</width> | ||||||
|  |              <height>20</height> | ||||||
|  |             </size> | ||||||
|  |            </property> | ||||||
|  |           </spacer> | ||||||
|  |          </item> | ||||||
|  |         </layout> | ||||||
|  |        </widget> | ||||||
|  |       </item> | ||||||
|  |       <item> | ||||||
|  |        <widget class="QWidget" name="middleControllerApplet" native="true"> | ||||||
|  |         <layout class="QVBoxLayout" name="verticalLayout_3"> | ||||||
|  |          <property name="spacing"> | ||||||
|  |           <number>0</number> | ||||||
|  |          </property> | ||||||
|  |          <property name="leftMargin"> | ||||||
|  |           <number>0</number> | ||||||
|  |          </property> | ||||||
|  |          <property name="topMargin"> | ||||||
|  |           <number>0</number> | ||||||
|  |          </property> | ||||||
|  |          <property name="rightMargin"> | ||||||
|  |           <number>0</number> | ||||||
|  |          </property> | ||||||
|  |          <property name="bottomMargin"> | ||||||
|  |           <number>0</number> | ||||||
|  |          </property> | ||||||
|  |          <item> | ||||||
|  |           <layout class="QHBoxLayout" name="horizontalLayout_2"> | ||||||
|  |            <property name="spacing"> | ||||||
|  |             <number>20</number> | ||||||
|  |            </property> | ||||||
|  |            <property name="leftMargin"> | ||||||
|  |             <number>15</number> | ||||||
|  |            </property> | ||||||
|  |            <property name="rightMargin"> | ||||||
|  |             <number>15</number> | ||||||
|  |            </property> | ||||||
|  |            <item> | ||||||
|  |             <widget class="QLabel" name="amiiboImageLabel"> | ||||||
|  |              <property name="minimumSize"> | ||||||
|  |               <size> | ||||||
|  |                <width>250</width> | ||||||
|  |                <height>350</height> | ||||||
|  |               </size> | ||||||
|  |              </property> | ||||||
|  |              <property name="maximumSize"> | ||||||
|  |               <size> | ||||||
|  |                <width>236</width> | ||||||
|  |                <height>350</height> | ||||||
|  |               </size> | ||||||
|  |              </property> | ||||||
|  |              <property name="text"> | ||||||
|  |               <string/> | ||||||
|  |              </property> | ||||||
|  |              <property name="alignment"> | ||||||
|  |               <set>Qt::AlignCenter</set> | ||||||
|  |              </property> | ||||||
|  |             </widget> | ||||||
|  |            </item> | ||||||
|  |            <item> | ||||||
|  |             <layout class="QVBoxLayout" name="verticalLayout_4"> | ||||||
|  |              <property name="leftMargin"> | ||||||
|  |               <number>0</number> | ||||||
|  |              </property> | ||||||
|  |              <property name="topMargin"> | ||||||
|  |               <number>8</number> | ||||||
|  |              </property> | ||||||
|  |              <property name="bottomMargin"> | ||||||
|  |               <number>15</number> | ||||||
|  |              </property> | ||||||
|  |              <item> | ||||||
|  |               <widget class="QGroupBox" name="amiiboInfoGroup"> | ||||||
|  |                <property name="title"> | ||||||
|  |                 <string>Amiibo Info</string> | ||||||
|  |                </property> | ||||||
|  |                <layout class="QVBoxLayout" name="verticalLayout_5"> | ||||||
|  |                 <item> | ||||||
|  |                  <layout class="QGridLayout" name="gridLayout_1"> | ||||||
|  |                   <item row="0" column="0"> | ||||||
|  |                    <widget class="QLabel" name="amiiboSeriesLabel"> | ||||||
|  |                     <property name="text"> | ||||||
|  |                      <string>Series</string> | ||||||
|  |                     </property> | ||||||
|  |                    </widget> | ||||||
|  |                   </item> | ||||||
|  |                   <item row="0" column="1"> | ||||||
|  |                    <widget class="QLineEdit" name="amiiboSeriesValue"> | ||||||
|  |                     <property name="sizePolicy"> | ||||||
|  |                      <sizepolicy hsizetype="Minimum" vsizetype="Fixed"> | ||||||
|  |                       <horstretch>0</horstretch> | ||||||
|  |                       <verstretch>0</verstretch> | ||||||
|  |                      </sizepolicy> | ||||||
|  |                     </property> | ||||||
|  |                     <property name="readOnly"> | ||||||
|  |                      <bool>true</bool> | ||||||
|  |                     </property> | ||||||
|  |                    </widget> | ||||||
|  |                   </item> | ||||||
|  |                   <item row="1" column="0"> | ||||||
|  |                    <widget class="QLabel" name="amiiboTypeLabel"> | ||||||
|  |                     <property name="text"> | ||||||
|  |                      <string>Type</string> | ||||||
|  |                     </property> | ||||||
|  |                    </widget> | ||||||
|  |                   </item> | ||||||
|  |                   <item row="1" column="1"> | ||||||
|  |                    <widget class="QLineEdit" name="amiiboTypeValue"> | ||||||
|  |                     <property name="sizePolicy"> | ||||||
|  |                      <sizepolicy hsizetype="Minimum" vsizetype="Fixed"> | ||||||
|  |                       <horstretch>0</horstretch> | ||||||
|  |                       <verstretch>0</verstretch> | ||||||
|  |                      </sizepolicy> | ||||||
|  |                     </property> | ||||||
|  |                     <property name="readOnly"> | ||||||
|  |                      <bool>true</bool> | ||||||
|  |                     </property> | ||||||
|  |                    </widget> | ||||||
|  |                   </item> | ||||||
|  |                   <item row="2" column="0"> | ||||||
|  |                    <widget class="QLabel" name="amiiboNameLabel"> | ||||||
|  |                     <property name="text"> | ||||||
|  |                      <string>Name</string> | ||||||
|  |                     </property> | ||||||
|  |                    </widget> | ||||||
|  |                   </item> | ||||||
|  |                   <item row="2" column="1"> | ||||||
|  |                    <widget class="QLineEdit" name="amiiboNameValue"> | ||||||
|  |                     <property name="sizePolicy"> | ||||||
|  |                      <sizepolicy hsizetype="Minimum" vsizetype="Fixed"> | ||||||
|  |                       <horstretch>0</horstretch> | ||||||
|  |                       <verstretch>0</verstretch> | ||||||
|  |                      </sizepolicy> | ||||||
|  |                     </property> | ||||||
|  |                     <property name="readOnly"> | ||||||
|  |                      <bool>true</bool> | ||||||
|  |                     </property> | ||||||
|  |                    </widget> | ||||||
|  |                   </item> | ||||||
|  |                  </layout> | ||||||
|  |                 </item> | ||||||
|  |                </layout> | ||||||
|  |               </widget> | ||||||
|  |              </item> | ||||||
|  |              <item> | ||||||
|  |               <widget class="QGroupBox" name="amiiboDataGroup"> | ||||||
|  |                <property name="title"> | ||||||
|  |                 <string>Amiibo Data</string> | ||||||
|  |                </property> | ||||||
|  |                <layout class="QVBoxLayout" name="verticalLayout_6"> | ||||||
|  |                 <item> | ||||||
|  |                  <layout class="QGridLayout" name="gridLayout_2"> | ||||||
|  |                   <item row="0" column="0"> | ||||||
|  |                    <widget class="QLabel" name="amiiboCustomNameLabel"> | ||||||
|  |                     <property name="text"> | ||||||
|  |                      <string>Custom Name</string> | ||||||
|  |                     </property> | ||||||
|  |                    </widget> | ||||||
|  |                   </item> | ||||||
|  |                   <item row="0" column="1"> | ||||||
|  |                    <widget class="QLineEdit" name="amiiboCustomNameValue"> | ||||||
|  |                     <property name="sizePolicy"> | ||||||
|  |                      <sizepolicy hsizetype="Minimum" vsizetype="Fixed"> | ||||||
|  |                       <horstretch>0</horstretch> | ||||||
|  |                       <verstretch>0</verstretch> | ||||||
|  |                      </sizepolicy> | ||||||
|  |                     </property> | ||||||
|  |                     <property name="maxLength"> | ||||||
|  |                      <number>10</number> | ||||||
|  |                     </property> | ||||||
|  |                    </widget> | ||||||
|  |                   </item> | ||||||
|  |                   <item row="1" column="0"> | ||||||
|  |                    <widget class="QLabel" name="amiiboOwnerLabel"> | ||||||
|  |                     <property name="text"> | ||||||
|  |                      <string>Owner</string> | ||||||
|  |                     </property> | ||||||
|  |                    </widget> | ||||||
|  |                   </item> | ||||||
|  |                   <item row="1" column="1"> | ||||||
|  |                    <widget class="QLineEdit" name="amiiboOwnerValue"> | ||||||
|  |                     <property name="sizePolicy"> | ||||||
|  |                      <sizepolicy hsizetype="Minimum" vsizetype="Fixed"> | ||||||
|  |                       <horstretch>0</horstretch> | ||||||
|  |                       <verstretch>0</verstretch> | ||||||
|  |                      </sizepolicy> | ||||||
|  |                     </property> | ||||||
|  |                     <property name="maxLength"> | ||||||
|  |                      <number>10</number> | ||||||
|  |                     </property> | ||||||
|  |                    </widget> | ||||||
|  |                   </item> | ||||||
|  |                   <item row="2" column="0"> | ||||||
|  |                    <widget class="QLabel" name="creationDateLabel"> | ||||||
|  |                     <property name="text"> | ||||||
|  |                      <string>Creation Date</string> | ||||||
|  |                     </property> | ||||||
|  |                    </widget> | ||||||
|  |                   </item> | ||||||
|  |                   <item row="2" column="1"> | ||||||
|  |                    <widget class="QDateTimeEdit" name="creationDateValue"> | ||||||
|  |                     <property name="readOnly"> | ||||||
|  |                      <bool>true</bool> | ||||||
|  |                     </property> | ||||||
|  |                     <property name="minimumDate"> | ||||||
|  |                      <date> | ||||||
|  |                       <year>1970</year> | ||||||
|  |                       <month>1</month> | ||||||
|  |                       <day>1</day> | ||||||
|  |                      </date> | ||||||
|  |                     </property> | ||||||
|  |                     <property name="displayFormat"> | ||||||
|  |                      <string>dd/MM/yyyy</string> | ||||||
|  |                     </property> | ||||||
|  |                    </widget> | ||||||
|  |                   </item> | ||||||
|  |                   <item row="3" column="0"> | ||||||
|  |                    <widget class="QLabel" name="modificationDateLabel"> | ||||||
|  |                     <property name="text"> | ||||||
|  |                      <string>Modification Date</string> | ||||||
|  |                     </property> | ||||||
|  |                    </widget> | ||||||
|  |                   </item> | ||||||
|  |                   <item row="3" column="1"> | ||||||
|  |                    <widget class="QDateTimeEdit" name="modificationDateValue"> | ||||||
|  |                     <property name="readOnly"> | ||||||
|  |                      <bool>true</bool> | ||||||
|  |                     </property> | ||||||
|  |                     <property name="minimumDate"> | ||||||
|  |                      <date> | ||||||
|  |                       <year>1970</year> | ||||||
|  |                       <month>1</month> | ||||||
|  |                       <day>1</day> | ||||||
|  |                      </date> | ||||||
|  |                     </property> | ||||||
|  |                     <property name="displayFormat"> | ||||||
|  |                      <string>dd/MM/yyyy </string> | ||||||
|  |                     </property> | ||||||
|  |                    </widget> | ||||||
|  |                   </item> | ||||||
|  |                  </layout> | ||||||
|  |                 </item> | ||||||
|  |                </layout> | ||||||
|  |               </widget> | ||||||
|  |              </item> | ||||||
|  |              <item> | ||||||
|  |               <widget class="QGroupBox" name="gameDataGroup"> | ||||||
|  |                <property name="minimumSize"> | ||||||
|  |                 <size> | ||||||
|  |                  <width>500</width> | ||||||
|  |                  <height>0</height> | ||||||
|  |                 </size> | ||||||
|  |                </property> | ||||||
|  |                <property name="title"> | ||||||
|  |                 <string>Game Data</string> | ||||||
|  |                </property> | ||||||
|  |                <layout class="QGridLayout" name="gridLayout_3"> | ||||||
|  |                 <item row="0" column="0"> | ||||||
|  |                  <widget class="QLabel" name="gameIdLabel"> | ||||||
|  |                   <property name="text"> | ||||||
|  |                    <string>Game Id</string> | ||||||
|  |                   </property> | ||||||
|  |                  </widget> | ||||||
|  |                 </item> | ||||||
|  |                 <item row="0" column="1"> | ||||||
|  |                  <widget class="QLineEdit" name="gameIdValue"> | ||||||
|  |                   <property name="sizePolicy"> | ||||||
|  |                    <sizepolicy hsizetype="Minimum" vsizetype="Fixed"> | ||||||
|  |                     <horstretch>0</horstretch> | ||||||
|  |                     <verstretch>0</verstretch> | ||||||
|  |                    </sizepolicy> | ||||||
|  |                   </property> | ||||||
|  |                   <property name="readOnly"> | ||||||
|  |                    <bool>true</bool> | ||||||
|  |                   </property> | ||||||
|  |                  </widget> | ||||||
|  |                 </item> | ||||||
|  |                </layout> | ||||||
|  |               </widget> | ||||||
|  |              </item> | ||||||
|  |              <item> | ||||||
|  |               <widget class="QGroupBox" name="MountAmiiboGroup"> | ||||||
|  |                <property name="minimumSize"> | ||||||
|  |                 <size> | ||||||
|  |                  <width>500</width> | ||||||
|  |                  <height>0</height> | ||||||
|  |                 </size> | ||||||
|  |                </property> | ||||||
|  |                <property name="title"> | ||||||
|  |                 <string>Mount Amiibo</string> | ||||||
|  |                </property> | ||||||
|  |                <layout class="QGridLayout" name="gridLayout_4"> | ||||||
|  |                 <item row="0" column="3"> | ||||||
|  |                  <widget class="QToolButton" name="amiiboDirectoryButton"> | ||||||
|  |                   <property name="text"> | ||||||
|  |                    <string>...</string> | ||||||
|  |                   </property> | ||||||
|  |                  </widget> | ||||||
|  |                 </item> | ||||||
|  |                 <item row="0" column="1"> | ||||||
|  |                  <spacer name="horizontalSpacer"> | ||||||
|  |                   <property name="orientation"> | ||||||
|  |                    <enum>Qt::Horizontal</enum> | ||||||
|  |                   </property> | ||||||
|  |                   <property name="sizeType"> | ||||||
|  |                    <enum>QSizePolicy::Maximum</enum> | ||||||
|  |                   </property> | ||||||
|  |                   <property name="sizeHint" stdset="0"> | ||||||
|  |                    <size> | ||||||
|  |                     <width>60</width> | ||||||
|  |                     <height>20</height> | ||||||
|  |                    </size> | ||||||
|  |                   </property> | ||||||
|  |                  </spacer> | ||||||
|  |                 </item> | ||||||
|  |                 <item row="0" column="0"> | ||||||
|  |                  <widget class="QLabel" name="amiiboDirectoryLabel"> | ||||||
|  |                   <property name="text"> | ||||||
|  |                    <string>File Path</string> | ||||||
|  |                   </property> | ||||||
|  |                  </widget> | ||||||
|  |                 </item> | ||||||
|  |                 <item row="0" column="2"> | ||||||
|  |                  <widget class="QLineEdit" name="amiiboDirectoryValue"/> | ||||||
|  |                 </item> | ||||||
|  |                </layout> | ||||||
|  |               </widget> | ||||||
|  |              </item> | ||||||
|  |              <item> | ||||||
|  |               <spacer name="verticalSpacer"> | ||||||
|  |                <property name="orientation"> | ||||||
|  |                 <enum>Qt::Vertical</enum> | ||||||
|  |                </property> | ||||||
|  |                <property name="sizeHint" stdset="0"> | ||||||
|  |                 <size> | ||||||
|  |                  <width>20</width> | ||||||
|  |                  <height>40</height> | ||||||
|  |                 </size> | ||||||
|  |                </property> | ||||||
|  |               </spacer> | ||||||
|  |              </item> | ||||||
|  |             </layout> | ||||||
|  |            </item> | ||||||
|  |           </layout> | ||||||
|  |          </item> | ||||||
|  |         </layout> | ||||||
|  |        </widget> | ||||||
|  |       </item> | ||||||
|  |       <item> | ||||||
|  |        <widget class="QWidget" name="bottomControllerApplet" native="true"> | ||||||
|  |         <layout class="QHBoxLayout" name="horizontalLayout_6"> | ||||||
|  |          <property name="spacing"> | ||||||
|  |           <number>15</number> | ||||||
|  |          </property> | ||||||
|  |          <property name="leftMargin"> | ||||||
|  |           <number>15</number> | ||||||
|  |          </property> | ||||||
|  |          <property name="topMargin"> | ||||||
|  |           <number>8</number> | ||||||
|  |          </property> | ||||||
|  |          <property name="rightMargin"> | ||||||
|  |           <number>20</number> | ||||||
|  |          </property> | ||||||
|  |          <property name="bottomMargin"> | ||||||
|  |           <number>8</number> | ||||||
|  |          </property> | ||||||
|  |          <item alignment="Qt::AlignBottom"> | ||||||
|  |           <widget class="QDialogButtonBox" name="buttonBox"> | ||||||
|  |            <property name="enabled"> | ||||||
|  |             <bool>true</bool> | ||||||
|  |            </property> | ||||||
|  |            <property name="standardButtons"> | ||||||
|  |             <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set> | ||||||
|  |            </property> | ||||||
|  |           </widget> | ||||||
|  |          </item> | ||||||
|  |         </layout> | ||||||
|  |        </widget> | ||||||
|  |       </item> | ||||||
|  |      </layout> | ||||||
|  |     </widget> | ||||||
|  |    </item> | ||||||
|  |   </layout> | ||||||
|  |  </widget> | ||||||
|  |  <resources/> | ||||||
|  |  <connections> | ||||||
|  |   <connection> | ||||||
|  |    <sender>buttonBox</sender> | ||||||
|  |    <signal>accepted()</signal> | ||||||
|  |    <receiver>QtAmiiboSettingsDialog</receiver> | ||||||
|  |    <slot>accept()</slot> | ||||||
|  |   </connection> | ||||||
|  |   <connection> | ||||||
|  |    <sender>buttonBox</sender> | ||||||
|  |    <signal>rejected()</signal> | ||||||
|  |    <receiver>QtAmiiboSettingsDialog</receiver> | ||||||
|  |    <slot>reject()</slot> | ||||||
|  |   </connection> | ||||||
|  |  </connections> | ||||||
|  | </ui> | ||||||
| @ -15,6 +15,7 @@ | |||||||
| #endif | #endif | ||||||
| 
 | 
 | ||||||
| // VFS includes must be before glad as they will conflict with Windows file api, which uses defines.
 | // VFS includes must be before glad as they will conflict with Windows file api, which uses defines.
 | ||||||
|  | #include "applets/qt_amiibo_settings.h" | ||||||
| #include "applets/qt_controller.h" | #include "applets/qt_controller.h" | ||||||
| #include "applets/qt_error.h" | #include "applets/qt_error.h" | ||||||
| #include "applets/qt_profile_select.h" | #include "applets/qt_profile_select.h" | ||||||
| @ -26,6 +27,7 @@ | |||||||
| #include "configuration/configure_tas.h" | #include "configuration/configure_tas.h" | ||||||
| #include "core/file_sys/vfs.h" | #include "core/file_sys/vfs.h" | ||||||
| #include "core/file_sys/vfs_real.h" | #include "core/file_sys/vfs_real.h" | ||||||
|  | #include "core/frontend/applets/cabinet.h" | ||||||
| #include "core/frontend/applets/controller.h" | #include "core/frontend/applets/controller.h" | ||||||
| #include "core/frontend/applets/general_frontend.h" | #include "core/frontend/applets/general_frontend.h" | ||||||
| #include "core/frontend/applets/mii_edit.h" | #include "core/frontend/applets/mii_edit.h" | ||||||
| @ -548,6 +550,11 @@ void GMainWindow::RegisterMetaTypes() { | |||||||
| 
 | 
 | ||||||
|     // Register applet types
 |     // Register applet types
 | ||||||
| 
 | 
 | ||||||
|  |     // Cabinet Applet
 | ||||||
|  |     qRegisterMetaType<Core::Frontend::CabinetParameters>("Core::Frontend::CabinetParameters"); | ||||||
|  |     qRegisterMetaType<std::shared_ptr<Service::NFP::NfpDevice>>( | ||||||
|  |         "std::shared_ptr<Service::NFP::NfpDevice>"); | ||||||
|  | 
 | ||||||
|     // Controller Applet
 |     // Controller Applet
 | ||||||
|     qRegisterMetaType<Core::Frontend::ControllerParameters>("Core::Frontend::ControllerParameters"); |     qRegisterMetaType<Core::Frontend::ControllerParameters>("Core::Frontend::ControllerParameters"); | ||||||
| 
 | 
 | ||||||
| @ -569,6 +576,21 @@ void GMainWindow::RegisterMetaTypes() { | |||||||
|     qRegisterMetaType<Core::SystemResultStatus>("Core::SystemResultStatus"); |     qRegisterMetaType<Core::SystemResultStatus>("Core::SystemResultStatus"); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | void GMainWindow::AmiiboSettingsShowDialog(const Core::Frontend::CabinetParameters& parameters, | ||||||
|  |                                            std::shared_ptr<Service::NFP::NfpDevice> nfp_device) { | ||||||
|  |     QtAmiiboSettingsDialog dialog(this, parameters, input_subsystem.get(), nfp_device); | ||||||
|  | 
 | ||||||
|  |     dialog.setWindowFlags(Qt::Dialog | Qt::CustomizeWindowHint | Qt::WindowStaysOnTopHint | | ||||||
|  |                           Qt::WindowTitleHint | Qt::WindowSystemMenuHint); | ||||||
|  |     dialog.setWindowModality(Qt::WindowModal); | ||||||
|  |     if (dialog.exec() == QDialog::Rejected) { | ||||||
|  |         emit AmiiboSettingsFinished(false, {}); | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     emit AmiiboSettingsFinished(true, dialog.GetName()); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| void GMainWindow::ControllerSelectorReconfigureControllers( | void GMainWindow::ControllerSelectorReconfigureControllers( | ||||||
|     const Core::Frontend::ControllerParameters& parameters) { |     const Core::Frontend::ControllerParameters& parameters) { | ||||||
|     QtControllerSelectorDialog dialog(this, parameters, input_subsystem.get(), *system); |     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 | |||||||
|     system->SetFilesystem(vfs); |     system->SetFilesystem(vfs); | ||||||
| 
 | 
 | ||||||
|     system->SetAppletFrontendSet({ |     system->SetAppletFrontendSet({ | ||||||
|  |         std::make_unique<QtAmiiboSettings>(*this),     // Amiibo Settings
 | ||||||
|         std::make_unique<QtControllerSelector>(*this), // Controller Selector
 |         std::make_unique<QtControllerSelector>(*this), // Controller Selector
 | ||||||
|         std::make_unique<QtErrorDisplay>(*this),       // Error Display
 |         std::make_unique<QtErrorDisplay>(*this),       // Error Display
 | ||||||
|         nullptr,                                       // Mii Editor
 |         nullptr,                                       // Mii Editor
 | ||||||
|  | |||||||
| @ -55,6 +55,7 @@ class System; | |||||||
| } // namespace Core
 | } // namespace Core
 | ||||||
| 
 | 
 | ||||||
| namespace Core::Frontend { | namespace Core::Frontend { | ||||||
|  | struct CabinetParameters; | ||||||
| struct ControllerParameters; | struct ControllerParameters; | ||||||
| struct InlineAppearParameters; | struct InlineAppearParameters; | ||||||
| struct InlineTextParameters; | struct InlineTextParameters; | ||||||
| @ -82,6 +83,10 @@ enum class SwkbdReplyType : u32; | |||||||
| enum class WebExitReason : u32; | enum class WebExitReason : u32; | ||||||
| } // namespace Service::AM::Applets
 | } // namespace Service::AM::Applets
 | ||||||
| 
 | 
 | ||||||
|  | namespace Service::NFP { | ||||||
|  | class NfpDevice; | ||||||
|  | } // namespace Service::NFP
 | ||||||
|  | 
 | ||||||
| namespace Ui { | namespace Ui { | ||||||
| class MainWindow; | class MainWindow; | ||||||
| } | } | ||||||
| @ -149,6 +154,8 @@ signals: | |||||||
| 
 | 
 | ||||||
|     void UpdateInstallProgress(); |     void UpdateInstallProgress(); | ||||||
| 
 | 
 | ||||||
|  |     void AmiiboSettingsFinished(bool is_success, const std::string& name); | ||||||
|  | 
 | ||||||
|     void ControllerSelectorReconfigureFinished(); |     void ControllerSelectorReconfigureFinished(); | ||||||
| 
 | 
 | ||||||
|     void ErrorDisplayFinished(); |     void ErrorDisplayFinished(); | ||||||
| @ -170,6 +177,8 @@ public slots: | |||||||
|     void OnExecuteProgram(std::size_t program_index); |     void OnExecuteProgram(std::size_t program_index); | ||||||
|     void OnExit(); |     void OnExit(); | ||||||
|     void OnSaveConfig(); |     void OnSaveConfig(); | ||||||
|  |     void AmiiboSettingsShowDialog(const Core::Frontend::CabinetParameters& parameters, | ||||||
|  |                                   std::shared_ptr<Service::NFP::NfpDevice> nfp_device); | ||||||
|     void ControllerSelectorReconfigureControllers( |     void ControllerSelectorReconfigureControllers( | ||||||
|         const Core::Frontend::ControllerParameters& parameters); |         const Core::Frontend::ControllerParameters& parameters); | ||||||
|     void SoftwareKeyboardInitialize( |     void SoftwareKeyboardInitialize( | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user