mirror of
				https://github.com/PabloMK7/citra.git
				synced 2025-10-31 05:40:04 +00:00 
			
		
		
		
	nfc: Add Amiibo support (REOPENED) (#4337)
* Initial implementation * Various fixes and new features * Address some review comments * Fixes * Address more comments * Use g_hle_lock * Add more state checking, remove unneeded include * Minor changes
This commit is contained in:
		
							parent
							
								
									6742472133
								
							
						
					
					
						commit
						dec3fb2dcf
					
				
					 7 changed files with 332 additions and 26 deletions
				
			
		|  | @ -45,6 +45,7 @@ | |||
| #include "citra_qt/util/clickable_label.h" | ||||
| #include "common/common_paths.h" | ||||
| #include "common/detached_tasks.h" | ||||
| #include "common/file_util.h" | ||||
| #include "common/logging/backend.h" | ||||
| #include "common/logging/filter.h" | ||||
| #include "common/logging/log.h" | ||||
|  | @ -58,6 +59,7 @@ | |||
| #include "core/frontend/applets/default_applets.h" | ||||
| #include "core/gdbstub/gdbstub.h" | ||||
| #include "core/hle/service/fs/archive.h" | ||||
| #include "core/hle/service/nfc/nfc.h" | ||||
| #include "core/loader/loader.h" | ||||
| #include "core/movie.h" | ||||
| #include "core/settings.h" | ||||
|  | @ -496,6 +498,8 @@ void GMainWindow::ConnectMenuEvents() { | |||
|     connect(ui.action_Load_File, &QAction::triggered, this, &GMainWindow::OnMenuLoadFile); | ||||
|     connect(ui.action_Install_CIA, &QAction::triggered, this, &GMainWindow::OnMenuInstallCIA); | ||||
|     connect(ui.action_Exit, &QAction::triggered, this, &QMainWindow::close); | ||||
|     connect(ui.action_Load_Amiibo, &QAction::triggered, this, &GMainWindow::OnLoadAmiibo); | ||||
|     connect(ui.action_Remove_Amiibo, &QAction::triggered, this, &GMainWindow::OnRemoveAmiibo); | ||||
| 
 | ||||
|     // Emulation
 | ||||
|     connect(ui.action_Start, &QAction::triggered, this, &GMainWindow::OnStartGame); | ||||
|  | @ -848,6 +852,8 @@ void GMainWindow::ShutdownGame() { | |||
|     ui.action_Pause->setEnabled(false); | ||||
|     ui.action_Stop->setEnabled(false); | ||||
|     ui.action_Restart->setEnabled(false); | ||||
|     ui.action_Load_Amiibo->setEnabled(false); | ||||
|     ui.action_Remove_Amiibo->setEnabled(false); | ||||
|     ui.action_Report_Compatibility->setEnabled(false); | ||||
|     ui.action_Enable_Frame_Advancing->setEnabled(false); | ||||
|     ui.action_Enable_Frame_Advancing->setChecked(false); | ||||
|  | @ -1135,6 +1141,7 @@ void GMainWindow::OnStartGame() { | |||
|     ui.action_Pause->setEnabled(true); | ||||
|     ui.action_Stop->setEnabled(true); | ||||
|     ui.action_Restart->setEnabled(true); | ||||
|     ui.action_Load_Amiibo->setEnabled(true); | ||||
|     ui.action_Report_Compatibility->setEnabled(true); | ||||
|     ui.action_Enable_Frame_Advancing->setEnabled(true); | ||||
| 
 | ||||
|  | @ -1289,6 +1296,40 @@ void GMainWindow::OnConfigure() { | |||
|     } | ||||
| } | ||||
| 
 | ||||
| void GMainWindow::OnLoadAmiibo() { | ||||
|     const QString extensions{"*.bin"}; | ||||
|     const QString file_filter = tr("Amiibo File (%1);; All Files (*.*)").arg(extensions); | ||||
|     const QString filename = QFileDialog::getOpenFileName(this, tr("Load Amiibo"), "", file_filter); | ||||
| 
 | ||||
|     if (!filename.isEmpty()) { | ||||
|         Core::System& system{Core::System::GetInstance()}; | ||||
|         Service::SM::ServiceManager& sm = system.ServiceManager(); | ||||
|         auto nfc = sm.GetService<Service::NFC::Module::Interface>("nfc:u"); | ||||
|         if (nfc != nullptr) { | ||||
|             Service::NFC::AmiiboData amiibo_data{}; | ||||
|             auto nfc_file = FileUtil::IOFile(filename.toStdString(), "rb"); | ||||
|             std::size_t read_length = | ||||
|                 nfc_file.ReadBytes(&amiibo_data, sizeof(Service::NFC::AmiiboData)); | ||||
|             if (read_length != sizeof(Service::NFC::AmiiboData)) { | ||||
|                 LOG_ERROR(Frontend, "Amiibo file size is incorrect"); | ||||
|                 return; | ||||
|             } | ||||
|             nfc->LoadAmiibo(amiibo_data); | ||||
|             ui.action_Remove_Amiibo->setEnabled(true); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void GMainWindow::OnRemoveAmiibo() { | ||||
|     Core::System& system{Core::System::GetInstance()}; | ||||
|     Service::SM::ServiceManager& sm = system.ServiceManager(); | ||||
|     auto nfc = sm.GetService<Service::NFC::Module::Interface>("nfc:u"); | ||||
|     if (nfc != nullptr) { | ||||
|         nfc->RemoveAmiibo(); | ||||
|         ui.action_Remove_Amiibo->setEnabled(false); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void GMainWindow::OnToggleFilterBar() { | ||||
|     game_list->setFilterVisible(ui.action_Show_Filter_Bar->isChecked()); | ||||
|     if (ui.action_Show_Filter_Bar->isChecked()) { | ||||
|  |  | |||
|  | @ -166,6 +166,8 @@ private slots: | |||
|     void OnCIAInstallFinished(); | ||||
|     void OnMenuRecentFile(); | ||||
|     void OnConfigure(); | ||||
|     void OnLoadAmiibo(); | ||||
|     void OnRemoveAmiibo(); | ||||
|     void OnToggleFilterBar(); | ||||
|     void OnDisplayTitleBars(bool); | ||||
|     void ToggleFullscreen(); | ||||
|  |  | |||
|  | @ -57,11 +57,20 @@ | |||
|       <string>Recent Files</string> | ||||
|      </property> | ||||
|     </widget> | ||||
|     <widget class="QMenu" name="menu_Amiibo"> | ||||
|      <property name="title"> | ||||
|       <string>Amiibo</string> | ||||
|      </property> | ||||
|      <addaction name="action_Load_Amiibo"/> | ||||
|      <addaction name="action_Remove_Amiibo"/> | ||||
|     </widget> | ||||
|     <addaction name="action_Load_File"/> | ||||
|     <addaction name="action_Install_CIA"/> | ||||
|     <addaction name="separator"/> | ||||
|     <addaction name="menu_recent_files"/> | ||||
|     <addaction name="separator"/> | ||||
|     <addaction name="menu_Amiibo"/> | ||||
|     <addaction name="separator"/> | ||||
|     <addaction name="action_Exit"/> | ||||
|    </widget> | ||||
|    <widget class="QMenu" name="menu_Emulation"> | ||||
|  | @ -415,6 +424,22 @@ | |||
|     <string>Restart</string> | ||||
|    </property> | ||||
|   </action> | ||||
|   <action name="action_Load_Amiibo"> | ||||
|    <property name="enabled"> | ||||
|     <bool>false</bool> | ||||
|    </property> | ||||
|    <property name="text"> | ||||
|     <string>Load...</string> | ||||
|    </property> | ||||
|   </action> | ||||
|   <action name="action_Remove_Amiibo"> | ||||
|    <property name="enabled"> | ||||
|     <bool>false</bool> | ||||
|    </property> | ||||
|    <property name="text"> | ||||
|     <string>Remove</string> | ||||
|    </property> | ||||
|   </action> | ||||
|  </widget> | ||||
|  <resources/> | ||||
|  <connections/> | ||||
|  |  | |||
|  | @ -5,19 +5,61 @@ | |||
| #include "core/core.h" | ||||
| #include "core/hle/ipc_helpers.h" | ||||
| #include "core/hle/kernel/event.h" | ||||
| #include "core/hle/lock.h" | ||||
| #include "core/hle/service/nfc/nfc.h" | ||||
| #include "core/hle/service/nfc/nfc_m.h" | ||||
| #include "core/hle/service/nfc/nfc_u.h" | ||||
| 
 | ||||
| namespace Service::NFC { | ||||
| 
 | ||||
| struct TagInfo { | ||||
|     u16_le id_offset_size; | ||||
|     u8 unk1; | ||||
|     u8 unk2; | ||||
|     std::array<u8, 7> uuid; | ||||
|     INSERT_PADDING_BYTES(0x20); | ||||
| }; | ||||
| static_assert(sizeof(TagInfo) == 0x2C, "TagInfo is an invalid size"); | ||||
| 
 | ||||
| struct AmiiboConfig { | ||||
|     u16_le lastwritedate_year; | ||||
|     u8 lastwritedate_month; | ||||
|     u8 lastwritedate_day; | ||||
|     u16_le write_counter; | ||||
|     std::array<u8, 3> characterID; | ||||
|     u16_le amiiboID; | ||||
|     u8 type; | ||||
|     u8 pagex4_byte3; | ||||
|     u16_le appdata_size; | ||||
|     INSERT_PADDING_BYTES(0x30); | ||||
| }; | ||||
| static_assert(sizeof(AmiiboConfig) == 0x40, "AmiiboConfig is an invalid size"); | ||||
| 
 | ||||
| struct IdentificationBlockReply { | ||||
|     u16_le char_id; | ||||
|     u8 char_variant; | ||||
|     u8 series; | ||||
|     u16_le model_number; | ||||
|     u8 figure_type; | ||||
|     INSERT_PADDING_BYTES(0x2F); | ||||
| }; | ||||
| static_assert(sizeof(IdentificationBlockReply) == 0x36, | ||||
|               "IdentificationBlockReply is an invalid size"); | ||||
| 
 | ||||
| void Module::Interface::Initialize(Kernel::HLERequestContext& ctx) { | ||||
|     IPC::RequestParser rp(ctx, 0x01, 1, 0); | ||||
|     u8 param = rp.Pop<u8>(); | ||||
| 
 | ||||
|     IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); | ||||
|     if (nfc->nfc_tag_state != TagState::NotInitialized) { | ||||
|         LOG_ERROR(Service_NFC, "Invalid TagState {}", static_cast<int>(nfc->nfc_tag_state.load())); | ||||
|         rb.Push(ResultCode(ErrCodes::CommandInvalidForState, ErrorModule::NFC, | ||||
|                            ErrorSummary::InvalidState, ErrorLevel::Status)); | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     nfc->nfc_tag_state = TagState::NotScanning; | ||||
| 
 | ||||
|     IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); | ||||
|     rb.Push(RESULT_SUCCESS); | ||||
|     LOG_WARNING(Service_NFC, "(STUBBED) called, param={}", param); | ||||
| } | ||||
|  | @ -53,35 +95,83 @@ void Module::Interface::StartTagScanning(Kernel::HLERequestContext& ctx) { | |||
|     IPC::RequestParser rp(ctx, 0x05, 1, 0); // 0x00050040
 | ||||
|     u16 in_val = rp.Pop<u16>(); | ||||
| 
 | ||||
|     ResultCode result = RESULT_SUCCESS; | ||||
| 
 | ||||
|     // TODO(shinyquagsire23): Implement NFC tag detection, for now stub result
 | ||||
|     result = ResultCode(ErrCodes::CommandInvalidForState, ErrorModule::NFC, | ||||
|                         ErrorSummary::InvalidState, ErrorLevel::Status); | ||||
| 
 | ||||
|     if (result == RESULT_SUCCESS) { | ||||
|         nfc->nfc_tag_state = TagState::TagInRange; | ||||
|         nfc->tag_in_range_event->Signal(); | ||||
|     IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); | ||||
|     if (nfc->nfc_tag_state != TagState::NotScanning && | ||||
|         nfc->nfc_tag_state != TagState::TagOutOfRange) { | ||||
|         LOG_ERROR(Service_NFC, "Invalid TagState {}", static_cast<int>(nfc->nfc_tag_state.load())); | ||||
|         rb.Push(ResultCode(ErrCodes::CommandInvalidForState, ErrorModule::NFC, | ||||
|                            ErrorSummary::InvalidState, ErrorLevel::Status)); | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); | ||||
|     rb.Push(result); | ||||
|     nfc->nfc_tag_state = TagState::Scanning; | ||||
| 
 | ||||
|     rb.Push(RESULT_SUCCESS); | ||||
|     LOG_WARNING(Service_NFC, "(STUBBED) called, in_val={:04x}", in_val); | ||||
| } | ||||
| 
 | ||||
| void Module::Interface::GetTagInfo(Kernel::HLERequestContext& ctx) { | ||||
|     IPC::RequestParser rp(ctx, 0x11, 0, 0); | ||||
| 
 | ||||
|     if (nfc->nfc_tag_state != TagState::TagInRange && | ||||
|         nfc->nfc_tag_state != TagState::TagDataLoaded && nfc->nfc_tag_state != TagState::Unknown6) { | ||||
|         LOG_ERROR(Service_NFC, "Invalid TagState {}", static_cast<int>(nfc->nfc_tag_state.load())); | ||||
|         IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); | ||||
|         rb.Push(ResultCode(ErrCodes::CommandInvalidForState, ErrorModule::NFC, | ||||
|                            ErrorSummary::InvalidState, ErrorLevel::Status)); | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     TagInfo tag_info{}; | ||||
|     tag_info.uuid = nfc->amiibo_data.uuid; | ||||
|     tag_info.id_offset_size = tag_info.uuid.size(); | ||||
|     tag_info.unk1 = 0x0; | ||||
|     tag_info.unk2 = 0x2; | ||||
| 
 | ||||
|     IPC::RequestBuilder rb = rp.MakeBuilder(12, 0); | ||||
|     rb.Push(RESULT_SUCCESS); | ||||
|     rb.PushRaw<TagInfo>(tag_info); | ||||
|     LOG_WARNING(Service_NFC, "(STUBBED) called"); | ||||
| } | ||||
| 
 | ||||
| void Module::Interface::GetAmiiboConfig(Kernel::HLERequestContext& ctx) { | ||||
|     IPC::RequestParser rp(ctx, 0x18, 0, 0); | ||||
| 
 | ||||
|     AmiiboConfig amiibo_config{}; | ||||
|     amiibo_config.lastwritedate_year = 2017; | ||||
|     amiibo_config.lastwritedate_month = 10; | ||||
|     amiibo_config.lastwritedate_day = 10; | ||||
|     // TODO(FearlessTobi): Find the right values for the struct
 | ||||
| 
 | ||||
|     IPC::RequestBuilder rb = rp.MakeBuilder(17, 0); | ||||
|     rb.Push(RESULT_SUCCESS); | ||||
|     rb.PushRaw<AmiiboConfig>(amiibo_config); | ||||
|     LOG_WARNING(Service_NFC, "(STUBBED) called"); | ||||
| } | ||||
| 
 | ||||
| void Module::Interface::StopTagScanning(Kernel::HLERequestContext& ctx) { | ||||
|     IPC::RequestParser rp(ctx, 0x06, 0, 0); | ||||
| 
 | ||||
|     IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); | ||||
|     if (nfc->nfc_tag_state == TagState::NotInitialized || | ||||
|         nfc->nfc_tag_state == TagState::NotScanning) { | ||||
|         LOG_ERROR(Service_NFC, "Invalid TagState {}", static_cast<int>(nfc->nfc_tag_state.load())); | ||||
|         rb.Push(ResultCode(ErrCodes::CommandInvalidForState, ErrorModule::NFC, | ||||
|                            ErrorSummary::InvalidState, ErrorLevel::Status)); | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     nfc->nfc_tag_state = TagState::NotScanning; | ||||
| 
 | ||||
|     IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); | ||||
|     rb.Push(RESULT_SUCCESS); | ||||
|     LOG_WARNING(Service_NFC, "(STUBBED) called"); | ||||
|     LOG_DEBUG(Service_NFC, "called"); | ||||
| } | ||||
| 
 | ||||
| void Module::Interface::LoadAmiiboData(Kernel::HLERequestContext& ctx) { | ||||
|     IPC::RequestParser rp(ctx, 0x07, 0, 0); | ||||
| 
 | ||||
|     // TODO(FearlessTobi): Add state checking when this function gets properly implemented
 | ||||
| 
 | ||||
|     nfc->nfc_tag_state = TagState::TagDataLoaded; | ||||
| 
 | ||||
|     IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); | ||||
|  | @ -92,29 +182,52 @@ void Module::Interface::LoadAmiiboData(Kernel::HLERequestContext& ctx) { | |||
| void Module::Interface::ResetTagScanState(Kernel::HLERequestContext& ctx) { | ||||
|     IPC::RequestParser rp(ctx, 0x08, 0, 0); | ||||
| 
 | ||||
|     nfc->nfc_tag_state = TagState::NotScanning; | ||||
| 
 | ||||
|     IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); | ||||
|     if (nfc->nfc_tag_state != TagState::TagDataLoaded && nfc->nfc_tag_state != TagState::Unknown6) { | ||||
|         LOG_ERROR(Service_NFC, "Invalid TagState {}", static_cast<int>(nfc->nfc_tag_state.load())); | ||||
|         rb.Push(ResultCode(ErrCodes::CommandInvalidForState, ErrorModule::NFC, | ||||
|                            ErrorSummary::InvalidState, ErrorLevel::Status)); | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     nfc->nfc_tag_state = TagState::TagInRange; | ||||
| 
 | ||||
|     rb.Push(RESULT_SUCCESS); | ||||
|     LOG_WARNING(Service_NFC, "(STUBBED) called"); | ||||
|     LOG_DEBUG(Service_NFC, "called"); | ||||
| } | ||||
| 
 | ||||
| void Module::Interface::GetTagInRangeEvent(Kernel::HLERequestContext& ctx) { | ||||
|     IPC::RequestParser rp(ctx, 0x0B, 0, 0); | ||||
| 
 | ||||
|     if (nfc->nfc_tag_state != TagState::NotScanning) { | ||||
|         LOG_ERROR(Service_NFC, "Invalid TagState {}", static_cast<int>(nfc->nfc_tag_state.load())); | ||||
|         IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); | ||||
|         rb.Push(ResultCode(ErrCodes::CommandInvalidForState, ErrorModule::NFC, | ||||
|                            ErrorSummary::InvalidState, ErrorLevel::Status)); | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     IPC::RequestBuilder rb = rp.MakeBuilder(1, 2); | ||||
|     rb.Push(RESULT_SUCCESS); | ||||
|     rb.PushCopyObjects(nfc->tag_in_range_event); | ||||
|     LOG_WARNING(Service_NFC, "(STUBBED) called"); | ||||
|     LOG_DEBUG(Service_NFC, "called"); | ||||
| } | ||||
| 
 | ||||
| void Module::Interface::GetTagOutOfRangeEvent(Kernel::HLERequestContext& ctx) { | ||||
|     IPC::RequestParser rp(ctx, 0x0C, 0, 0); | ||||
| 
 | ||||
|     if (nfc->nfc_tag_state != TagState::NotScanning) { | ||||
|         LOG_ERROR(Service_NFC, "Invalid TagState {}", static_cast<int>(nfc->nfc_tag_state.load())); | ||||
|         IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); | ||||
|         rb.Push(ResultCode(ErrCodes::CommandInvalidForState, ErrorModule::NFC, | ||||
|                            ErrorSummary::InvalidState, ErrorLevel::Status)); | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     IPC::RequestBuilder rb = rp.MakeBuilder(1, 2); | ||||
|     rb.Push(RESULT_SUCCESS); | ||||
|     rb.PushCopyObjects(nfc->tag_out_of_range_event); | ||||
|     LOG_WARNING(Service_NFC, "(STUBBED) called"); | ||||
|     LOG_DEBUG(Service_NFC, "called"); | ||||
| } | ||||
| 
 | ||||
| void Module::Interface::GetTagState(Kernel::HLERequestContext& ctx) { | ||||
|  | @ -122,8 +235,8 @@ void Module::Interface::GetTagState(Kernel::HLERequestContext& ctx) { | |||
| 
 | ||||
|     IPC::RequestBuilder rb = rp.MakeBuilder(2, 0); | ||||
|     rb.Push(RESULT_SUCCESS); | ||||
|     rb.PushEnum(nfc->nfc_tag_state); | ||||
|     LOG_DEBUG(Service_NFC, "(STUBBED) called"); | ||||
|     rb.PushEnum(nfc->nfc_tag_state.load()); | ||||
|     LOG_DEBUG(Service_NFC, "called"); | ||||
| } | ||||
| 
 | ||||
| void Module::Interface::CommunicationGetStatus(Kernel::HLERequestContext& ctx) { | ||||
|  | @ -135,6 +248,65 @@ void Module::Interface::CommunicationGetStatus(Kernel::HLERequestContext& ctx) { | |||
|     LOG_DEBUG(Service_NFC, "(STUBBED) called"); | ||||
| } | ||||
| 
 | ||||
| void Module::Interface::Unknown0x1A(Kernel::HLERequestContext& ctx) { | ||||
|     IPC::RequestParser rp(ctx, 0x1A, 0, 0); | ||||
| 
 | ||||
|     IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); | ||||
|     if (nfc->nfc_tag_state != TagState::TagInRange) { | ||||
|         LOG_ERROR(Service_NFC, "Invalid TagState {}", static_cast<int>(nfc->nfc_tag_state.load())); | ||||
|         rb.Push(ResultCode(ErrCodes::CommandInvalidForState, ErrorModule::NFC, | ||||
|                            ErrorSummary::InvalidState, ErrorLevel::Status)); | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     nfc->nfc_tag_state = TagState::Unknown6; | ||||
| 
 | ||||
|     rb.Push(RESULT_SUCCESS); | ||||
|     LOG_DEBUG(Service_NFC, "called"); | ||||
| } | ||||
| 
 | ||||
| void Module::Interface::GetIdentificationBlock(Kernel::HLERequestContext& ctx) { | ||||
|     IPC::RequestParser rp(ctx, 0x1B, 0, 0); | ||||
| 
 | ||||
|     if (nfc->nfc_tag_state != TagState::TagDataLoaded && nfc->nfc_tag_state != TagState::Unknown6) { | ||||
|         LOG_ERROR(Service_NFC, "Invalid TagState {}", static_cast<int>(nfc->nfc_tag_state.load())); | ||||
|         IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); | ||||
|         rb.Push(ResultCode(ErrCodes::CommandInvalidForState, ErrorModule::NFC, | ||||
|                            ErrorSummary::InvalidState, ErrorLevel::Status)); | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     IdentificationBlockReply identification_block_reply{}; | ||||
|     identification_block_reply.char_id = nfc->amiibo_data.char_id; | ||||
|     identification_block_reply.char_variant = nfc->amiibo_data.char_variant; | ||||
|     identification_block_reply.series = nfc->amiibo_data.series; | ||||
|     identification_block_reply.model_number = nfc->amiibo_data.model_number; | ||||
|     identification_block_reply.figure_type = nfc->amiibo_data.figure_type; | ||||
| 
 | ||||
|     IPC::RequestBuilder rb = rp.MakeBuilder(0x1F, 0); | ||||
|     rb.Push(RESULT_SUCCESS); | ||||
|     rb.PushRaw<IdentificationBlockReply>(identification_block_reply); | ||||
|     LOG_DEBUG(Service_NFC, "called"); | ||||
| } | ||||
| 
 | ||||
| std::shared_ptr<Module> Module::Interface::GetModule() const { | ||||
|     return nfc; | ||||
| } | ||||
| 
 | ||||
| void Module::Interface::LoadAmiibo(const AmiiboData& amiibo_data) { | ||||
|     std::lock_guard lock(HLE::g_hle_lock); | ||||
|     nfc->amiibo_data = amiibo_data; | ||||
|     nfc->nfc_tag_state = Service::NFC::TagState::TagInRange; | ||||
|     nfc->tag_in_range_event->Signal(); | ||||
| } | ||||
| 
 | ||||
| void Module::Interface::RemoveAmiibo() { | ||||
|     std::lock_guard lock(HLE::g_hle_lock); | ||||
|     nfc->nfc_tag_state = Service::NFC::TagState::TagOutOfRange; | ||||
|     nfc->tag_out_of_range_event->Signal(); | ||||
|     nfc->amiibo_data = {}; | ||||
| } | ||||
| 
 | ||||
| Module::Interface::Interface(std::shared_ptr<Module> nfc, const char* name, u32 max_session) | ||||
|     : ServiceFramework(name, max_session), nfc(std::move(nfc)) {} | ||||
| 
 | ||||
|  |  | |||
|  | @ -4,6 +4,7 @@ | |||
| 
 | ||||
| #pragma once | ||||
| 
 | ||||
| #include <atomic> | ||||
| #include <memory> | ||||
| #include "common/common_types.h" | ||||
| #include "core/hle/kernel/kernel.h" | ||||
|  | @ -25,6 +26,19 @@ enum { | |||
| }; | ||||
| } // namespace ErrCodes
 | ||||
| 
 | ||||
| // TODO(FearlessTobi): Add more members to this struct
 | ||||
| struct AmiiboData { | ||||
|     std::array<u8, 7> uuid; | ||||
|     INSERT_PADDING_BYTES(0x4D); | ||||
|     u16_le char_id; | ||||
|     u8 char_variant; | ||||
|     u8 figure_type; | ||||
|     u16_be model_number; | ||||
|     u8 series; | ||||
|     INSERT_PADDING_BYTES(0x1C1); | ||||
| }; | ||||
| static_assert(sizeof(AmiiboData) == 0x21C, "AmiiboData is an invalid size"); | ||||
| 
 | ||||
| enum class TagState : u8 { | ||||
|     NotInitialized = 0, | ||||
|     NotScanning = 1, | ||||
|  | @ -32,6 +46,7 @@ enum class TagState : u8 { | |||
|     TagInRange = 3, | ||||
|     TagOutOfRange = 4, | ||||
|     TagDataLoaded = 5, | ||||
|     Unknown6 = 6, | ||||
| }; | ||||
| 
 | ||||
| enum class CommunicationStatus : u8 { | ||||
|  | @ -49,6 +64,12 @@ public: | |||
|         Interface(std::shared_ptr<Module> nfc, const char* name, u32 max_session); | ||||
|         ~Interface(); | ||||
| 
 | ||||
|         std::shared_ptr<Module> GetModule() const; | ||||
| 
 | ||||
|         void LoadAmiibo(const AmiiboData& amiibo_data); | ||||
| 
 | ||||
|         void RemoveAmiibo(); | ||||
| 
 | ||||
|     protected: | ||||
|         /**
 | ||||
|          * NFC::Initialize service function | ||||
|  | @ -167,6 +188,45 @@ public: | |||
|          */ | ||||
|         void CommunicationGetStatus(Kernel::HLERequestContext& ctx); | ||||
| 
 | ||||
|         /**
 | ||||
|          * NFC::GetTagInfo service function | ||||
|          *  Inputs: | ||||
|          *      0 : Header code [0x00110000] | ||||
|          *  Outputs: | ||||
|          *      1 : Result of function, 0 on success, otherwise error code | ||||
|          *   2-12 : 0x2C-byte struct | ||||
|          */ | ||||
|         void GetTagInfo(Kernel::HLERequestContext& ctx); | ||||
| 
 | ||||
|         /**
 | ||||
|          * NFC::GetAmiiboConfig service function | ||||
|          *  Inputs: | ||||
|          *      0 : Header code [0x00180000] | ||||
|          *  Outputs: | ||||
|          *      1 : Result of function, 0 on success, otherwise error code | ||||
|          *   2-17 : 0x40-byte config struct | ||||
|          */ | ||||
|         void GetAmiiboConfig(Kernel::HLERequestContext& ctx); | ||||
| 
 | ||||
|         /**
 | ||||
|          * NFC::Unknown0x1A service function | ||||
|          *  Inputs: | ||||
|          *      0 : Header code [0x001A0000] | ||||
|          *  Outputs: | ||||
|          *      1 : Result of function, 0 on success, otherwise error code | ||||
|          */ | ||||
|         void Unknown0x1A(Kernel::HLERequestContext& ctx); | ||||
| 
 | ||||
|         /**
 | ||||
|          * NFC::GetIdentificationBlock service function | ||||
|          *  Inputs: | ||||
|          *      0 : Header code [0x001B0000] | ||||
|          *  Outputs: | ||||
|          *      1 : Result of function, 0 on success, otherwise error code | ||||
|          *   2-31 : 0x36-byte struct | ||||
|          */ | ||||
|         void GetIdentificationBlock(Kernel::HLERequestContext& ctx); | ||||
| 
 | ||||
|     private: | ||||
|         std::shared_ptr<Module> nfc; | ||||
|     }; | ||||
|  | @ -174,8 +234,10 @@ public: | |||
| private: | ||||
|     Kernel::SharedPtr<Kernel::Event> tag_in_range_event; | ||||
|     Kernel::SharedPtr<Kernel::Event> tag_out_of_range_event; | ||||
|     TagState nfc_tag_state = TagState::NotInitialized; | ||||
|     std::atomic<TagState> nfc_tag_state = TagState::NotInitialized; | ||||
|     CommunicationStatus nfc_status = CommunicationStatus::NfcInitialized; | ||||
| 
 | ||||
|     AmiiboData amiibo_data{}; | ||||
| }; | ||||
| 
 | ||||
| void InstallInterfaces(Core::System& system); | ||||
|  |  | |||
|  | @ -24,15 +24,17 @@ NFC_M::NFC_M(std::shared_ptr<Module> nfc) : Module::Interface(std::move(nfc), "n | |||
|         {0x000D0000, &NFC_M::GetTagState, "GetTagState"}, | ||||
|         {0x000F0000, &NFC_M::CommunicationGetStatus, "CommunicationGetStatus"}, | ||||
|         {0x00100000, nullptr, "GetTagInfo2"}, | ||||
|         {0x00110000, nullptr, "GetTagInfo"}, | ||||
|         {0x00110000, &NFC_M::GetTagInfo, "GetTagInfo"}, | ||||
|         {0x00120000, nullptr, "CommunicationGetResult"}, | ||||
|         {0x00130040, nullptr, "OpenAppData"}, | ||||
|         {0x00140384, nullptr, "InitializeWriteAppData"}, | ||||
|         {0x00150040, nullptr, "ReadAppData"}, | ||||
|         {0x00160242, nullptr, "WriteAppData"}, | ||||
|         {0x00170000, nullptr, "GetAmiiboSettings"}, | ||||
|         {0x00180000, nullptr, "GetAmiiboConfig"}, | ||||
|         {0x00180000, &NFC_M::GetAmiiboConfig, "GetAmiiboConfig"}, | ||||
|         {0x00190000, nullptr, "GetAppDataInitStruct"}, | ||||
|         {0x001A0000, &NFC_M::Unknown0x1A, "Unknown0x1A"}, | ||||
|         {0x001B0000, &NFC_M::GetIdentificationBlock, "GetIdentificationBlock"}, | ||||
|         // nfc:m
 | ||||
|         {0x04040A40, nullptr, "SetAmiiboSettings"} | ||||
|         // clang-format on
 | ||||
|  |  | |||
|  | @ -23,15 +23,17 @@ NFC_U::NFC_U(std::shared_ptr<Module> nfc) : Module::Interface(std::move(nfc), "n | |||
|         {0x000D0000, &NFC_U::GetTagState, "GetTagState"}, | ||||
|         {0x000F0000, &NFC_U::CommunicationGetStatus, "CommunicationGetStatus"}, | ||||
|         {0x00100000, nullptr, "GetTagInfo2"}, | ||||
|         {0x00110000, nullptr, "GetTagInfo"}, | ||||
|         {0x00110000, &NFC_U::GetTagInfo, "GetTagInfo"}, | ||||
|         {0x00120000, nullptr, "CommunicationGetResult"}, | ||||
|         {0x00130040, nullptr, "OpenAppData"}, | ||||
|         {0x00140384, nullptr, "InitializeWriteAppData"}, | ||||
|         {0x00150040, nullptr, "ReadAppData"}, | ||||
|         {0x00160242, nullptr, "WriteAppData"}, | ||||
|         {0x00170000, nullptr, "GetAmiiboSettings"}, | ||||
|         {0x00180000, nullptr, "GetAmiiboConfig"}, | ||||
|         {0x00180000, &NFC_U::GetAmiiboConfig, "GetAmiiboConfig"}, | ||||
|         {0x00190000, nullptr, "GetAppDataInitStruct"}, | ||||
|         {0x001A0000, &NFC_U::Unknown0x1A, "Unknown0x1A"}, | ||||
|         {0x001B0000, &NFC_U::GetIdentificationBlock, "GetIdentificationBlock"}, | ||||
|         // clang-format on
 | ||||
|     }; | ||||
|     RegisterHandlers(functions); | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue