mirror of
				https://github.com/PabloMK7/citra.git
				synced 2025-10-31 13:50:03 +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 "citra_qt/util/clickable_label.h" | ||||||
| #include "common/common_paths.h" | #include "common/common_paths.h" | ||||||
| #include "common/detached_tasks.h" | #include "common/detached_tasks.h" | ||||||
|  | #include "common/file_util.h" | ||||||
| #include "common/logging/backend.h" | #include "common/logging/backend.h" | ||||||
| #include "common/logging/filter.h" | #include "common/logging/filter.h" | ||||||
| #include "common/logging/log.h" | #include "common/logging/log.h" | ||||||
|  | @ -58,6 +59,7 @@ | ||||||
| #include "core/frontend/applets/default_applets.h" | #include "core/frontend/applets/default_applets.h" | ||||||
| #include "core/gdbstub/gdbstub.h" | #include "core/gdbstub/gdbstub.h" | ||||||
| #include "core/hle/service/fs/archive.h" | #include "core/hle/service/fs/archive.h" | ||||||
|  | #include "core/hle/service/nfc/nfc.h" | ||||||
| #include "core/loader/loader.h" | #include "core/loader/loader.h" | ||||||
| #include "core/movie.h" | #include "core/movie.h" | ||||||
| #include "core/settings.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_Load_File, &QAction::triggered, this, &GMainWindow::OnMenuLoadFile); | ||||||
|     connect(ui.action_Install_CIA, &QAction::triggered, this, &GMainWindow::OnMenuInstallCIA); |     connect(ui.action_Install_CIA, &QAction::triggered, this, &GMainWindow::OnMenuInstallCIA); | ||||||
|     connect(ui.action_Exit, &QAction::triggered, this, &QMainWindow::close); |     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
 |     // Emulation
 | ||||||
|     connect(ui.action_Start, &QAction::triggered, this, &GMainWindow::OnStartGame); |     connect(ui.action_Start, &QAction::triggered, this, &GMainWindow::OnStartGame); | ||||||
|  | @ -848,6 +852,8 @@ void GMainWindow::ShutdownGame() { | ||||||
|     ui.action_Pause->setEnabled(false); |     ui.action_Pause->setEnabled(false); | ||||||
|     ui.action_Stop->setEnabled(false); |     ui.action_Stop->setEnabled(false); | ||||||
|     ui.action_Restart->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_Report_Compatibility->setEnabled(false); | ||||||
|     ui.action_Enable_Frame_Advancing->setEnabled(false); |     ui.action_Enable_Frame_Advancing->setEnabled(false); | ||||||
|     ui.action_Enable_Frame_Advancing->setChecked(false); |     ui.action_Enable_Frame_Advancing->setChecked(false); | ||||||
|  | @ -1135,6 +1141,7 @@ void GMainWindow::OnStartGame() { | ||||||
|     ui.action_Pause->setEnabled(true); |     ui.action_Pause->setEnabled(true); | ||||||
|     ui.action_Stop->setEnabled(true); |     ui.action_Stop->setEnabled(true); | ||||||
|     ui.action_Restart->setEnabled(true); |     ui.action_Restart->setEnabled(true); | ||||||
|  |     ui.action_Load_Amiibo->setEnabled(true); | ||||||
|     ui.action_Report_Compatibility->setEnabled(true); |     ui.action_Report_Compatibility->setEnabled(true); | ||||||
|     ui.action_Enable_Frame_Advancing->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() { | void GMainWindow::OnToggleFilterBar() { | ||||||
|     game_list->setFilterVisible(ui.action_Show_Filter_Bar->isChecked()); |     game_list->setFilterVisible(ui.action_Show_Filter_Bar->isChecked()); | ||||||
|     if (ui.action_Show_Filter_Bar->isChecked()) { |     if (ui.action_Show_Filter_Bar->isChecked()) { | ||||||
|  |  | ||||||
|  | @ -166,6 +166,8 @@ private slots: | ||||||
|     void OnCIAInstallFinished(); |     void OnCIAInstallFinished(); | ||||||
|     void OnMenuRecentFile(); |     void OnMenuRecentFile(); | ||||||
|     void OnConfigure(); |     void OnConfigure(); | ||||||
|  |     void OnLoadAmiibo(); | ||||||
|  |     void OnRemoveAmiibo(); | ||||||
|     void OnToggleFilterBar(); |     void OnToggleFilterBar(); | ||||||
|     void OnDisplayTitleBars(bool); |     void OnDisplayTitleBars(bool); | ||||||
|     void ToggleFullscreen(); |     void ToggleFullscreen(); | ||||||
|  |  | ||||||
|  | @ -57,11 +57,20 @@ | ||||||
|       <string>Recent Files</string> |       <string>Recent Files</string> | ||||||
|      </property> |      </property> | ||||||
|     </widget> |     </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_Load_File"/> | ||||||
|     <addaction name="action_Install_CIA"/> |     <addaction name="action_Install_CIA"/> | ||||||
|     <addaction name="separator"/> |     <addaction name="separator"/> | ||||||
|     <addaction name="menu_recent_files"/> |     <addaction name="menu_recent_files"/> | ||||||
|     <addaction name="separator"/> |     <addaction name="separator"/> | ||||||
|  |     <addaction name="menu_Amiibo"/> | ||||||
|  |     <addaction name="separator"/> | ||||||
|     <addaction name="action_Exit"/> |     <addaction name="action_Exit"/> | ||||||
|    </widget> |    </widget> | ||||||
|    <widget class="QMenu" name="menu_Emulation"> |    <widget class="QMenu" name="menu_Emulation"> | ||||||
|  | @ -415,6 +424,22 @@ | ||||||
|     <string>Restart</string> |     <string>Restart</string> | ||||||
|    </property> |    </property> | ||||||
|   </action> |   </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> |  </widget> | ||||||
|  <resources/> |  <resources/> | ||||||
|  <connections/> |  <connections/> | ||||||
|  |  | ||||||
|  | @ -5,19 +5,61 @@ | ||||||
| #include "core/core.h" | #include "core/core.h" | ||||||
| #include "core/hle/ipc_helpers.h" | #include "core/hle/ipc_helpers.h" | ||||||
| #include "core/hle/kernel/event.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.h" | ||||||
| #include "core/hle/service/nfc/nfc_m.h" | #include "core/hle/service/nfc/nfc_m.h" | ||||||
| #include "core/hle/service/nfc/nfc_u.h" | #include "core/hle/service/nfc/nfc_u.h" | ||||||
| 
 | 
 | ||||||
| namespace Service::NFC { | 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) { | void Module::Interface::Initialize(Kernel::HLERequestContext& ctx) { | ||||||
|     IPC::RequestParser rp(ctx, 0x01, 1, 0); |     IPC::RequestParser rp(ctx, 0x01, 1, 0); | ||||||
|     u8 param = rp.Pop<u8>(); |     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; |     nfc->nfc_tag_state = TagState::NotScanning; | ||||||
| 
 | 
 | ||||||
|     IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); |  | ||||||
|     rb.Push(RESULT_SUCCESS); |     rb.Push(RESULT_SUCCESS); | ||||||
|     LOG_WARNING(Service_NFC, "(STUBBED) called, param={}", param); |     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
 |     IPC::RequestParser rp(ctx, 0x05, 1, 0); // 0x00050040
 | ||||||
|     u16 in_val = rp.Pop<u16>(); |     u16 in_val = rp.Pop<u16>(); | ||||||
| 
 | 
 | ||||||
|     ResultCode result = RESULT_SUCCESS; |     IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); | ||||||
| 
 |     if (nfc->nfc_tag_state != TagState::NotScanning && | ||||||
|     // TODO(shinyquagsire23): Implement NFC tag detection, for now stub result
 |         nfc->nfc_tag_state != TagState::TagOutOfRange) { | ||||||
|     result = ResultCode(ErrCodes::CommandInvalidForState, ErrorModule::NFC, |         LOG_ERROR(Service_NFC, "Invalid TagState {}", static_cast<int>(nfc->nfc_tag_state.load())); | ||||||
|                         ErrorSummary::InvalidState, ErrorLevel::Status); |         rb.Push(ResultCode(ErrCodes::CommandInvalidForState, ErrorModule::NFC, | ||||||
| 
 |                            ErrorSummary::InvalidState, ErrorLevel::Status)); | ||||||
|     if (result == RESULT_SUCCESS) { |         return; | ||||||
|         nfc->nfc_tag_state = TagState::TagInRange; |  | ||||||
|         nfc->tag_in_range_event->Signal(); |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); |     nfc->nfc_tag_state = TagState::Scanning; | ||||||
|     rb.Push(result); | 
 | ||||||
|  |     rb.Push(RESULT_SUCCESS); | ||||||
|     LOG_WARNING(Service_NFC, "(STUBBED) called, in_val={:04x}", in_val); |     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) { | void Module::Interface::StopTagScanning(Kernel::HLERequestContext& ctx) { | ||||||
|     IPC::RequestParser rp(ctx, 0x06, 0, 0); |     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; |     nfc->nfc_tag_state = TagState::NotScanning; | ||||||
| 
 | 
 | ||||||
|     IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); |  | ||||||
|     rb.Push(RESULT_SUCCESS); |     rb.Push(RESULT_SUCCESS); | ||||||
|     LOG_WARNING(Service_NFC, "(STUBBED) called"); |     LOG_DEBUG(Service_NFC, "called"); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void Module::Interface::LoadAmiiboData(Kernel::HLERequestContext& ctx) { | void Module::Interface::LoadAmiiboData(Kernel::HLERequestContext& ctx) { | ||||||
|     IPC::RequestParser rp(ctx, 0x07, 0, 0); |     IPC::RequestParser rp(ctx, 0x07, 0, 0); | ||||||
| 
 | 
 | ||||||
|  |     // TODO(FearlessTobi): Add state checking when this function gets properly implemented
 | ||||||
|  | 
 | ||||||
|     nfc->nfc_tag_state = TagState::TagDataLoaded; |     nfc->nfc_tag_state = TagState::TagDataLoaded; | ||||||
| 
 | 
 | ||||||
|     IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); |     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) { | void Module::Interface::ResetTagScanState(Kernel::HLERequestContext& ctx) { | ||||||
|     IPC::RequestParser rp(ctx, 0x08, 0, 0); |     IPC::RequestParser rp(ctx, 0x08, 0, 0); | ||||||
| 
 | 
 | ||||||
|     nfc->nfc_tag_state = TagState::NotScanning; |  | ||||||
| 
 |  | ||||||
|     IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); |     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); |     rb.Push(RESULT_SUCCESS); | ||||||
|     LOG_WARNING(Service_NFC, "(STUBBED) called"); |     LOG_DEBUG(Service_NFC, "called"); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void Module::Interface::GetTagInRangeEvent(Kernel::HLERequestContext& ctx) { | void Module::Interface::GetTagInRangeEvent(Kernel::HLERequestContext& ctx) { | ||||||
|     IPC::RequestParser rp(ctx, 0x0B, 0, 0); |     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); |     IPC::RequestBuilder rb = rp.MakeBuilder(1, 2); | ||||||
|     rb.Push(RESULT_SUCCESS); |     rb.Push(RESULT_SUCCESS); | ||||||
|     rb.PushCopyObjects(nfc->tag_in_range_event); |     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) { | void Module::Interface::GetTagOutOfRangeEvent(Kernel::HLERequestContext& ctx) { | ||||||
|     IPC::RequestParser rp(ctx, 0x0C, 0, 0); |     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); |     IPC::RequestBuilder rb = rp.MakeBuilder(1, 2); | ||||||
|     rb.Push(RESULT_SUCCESS); |     rb.Push(RESULT_SUCCESS); | ||||||
|     rb.PushCopyObjects(nfc->tag_out_of_range_event); |     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) { | 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); |     IPC::RequestBuilder rb = rp.MakeBuilder(2, 0); | ||||||
|     rb.Push(RESULT_SUCCESS); |     rb.Push(RESULT_SUCCESS); | ||||||
|     rb.PushEnum(nfc->nfc_tag_state); |     rb.PushEnum(nfc->nfc_tag_state.load()); | ||||||
|     LOG_DEBUG(Service_NFC, "(STUBBED) called"); |     LOG_DEBUG(Service_NFC, "called"); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void Module::Interface::CommunicationGetStatus(Kernel::HLERequestContext& ctx) { | void Module::Interface::CommunicationGetStatus(Kernel::HLERequestContext& ctx) { | ||||||
|  | @ -135,6 +248,65 @@ void Module::Interface::CommunicationGetStatus(Kernel::HLERequestContext& ctx) { | ||||||
|     LOG_DEBUG(Service_NFC, "(STUBBED) called"); |     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) | Module::Interface::Interface(std::shared_ptr<Module> nfc, const char* name, u32 max_session) | ||||||
|     : ServiceFramework(name, max_session), nfc(std::move(nfc)) {} |     : ServiceFramework(name, max_session), nfc(std::move(nfc)) {} | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -4,6 +4,7 @@ | ||||||
| 
 | 
 | ||||||
| #pragma once | #pragma once | ||||||
| 
 | 
 | ||||||
|  | #include <atomic> | ||||||
| #include <memory> | #include <memory> | ||||||
| #include "common/common_types.h" | #include "common/common_types.h" | ||||||
| #include "core/hle/kernel/kernel.h" | #include "core/hle/kernel/kernel.h" | ||||||
|  | @ -25,6 +26,19 @@ enum { | ||||||
| }; | }; | ||||||
| } // namespace ErrCodes
 | } // 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 { | enum class TagState : u8 { | ||||||
|     NotInitialized = 0, |     NotInitialized = 0, | ||||||
|     NotScanning = 1, |     NotScanning = 1, | ||||||
|  | @ -32,6 +46,7 @@ enum class TagState : u8 { | ||||||
|     TagInRange = 3, |     TagInRange = 3, | ||||||
|     TagOutOfRange = 4, |     TagOutOfRange = 4, | ||||||
|     TagDataLoaded = 5, |     TagDataLoaded = 5, | ||||||
|  |     Unknown6 = 6, | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| enum class CommunicationStatus : u8 { | 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> nfc, const char* name, u32 max_session); | ||||||
|         ~Interface(); |         ~Interface(); | ||||||
| 
 | 
 | ||||||
|  |         std::shared_ptr<Module> GetModule() const; | ||||||
|  | 
 | ||||||
|  |         void LoadAmiibo(const AmiiboData& amiibo_data); | ||||||
|  | 
 | ||||||
|  |         void RemoveAmiibo(); | ||||||
|  | 
 | ||||||
|     protected: |     protected: | ||||||
|         /**
 |         /**
 | ||||||
|          * NFC::Initialize service function |          * NFC::Initialize service function | ||||||
|  | @ -167,6 +188,45 @@ public: | ||||||
|          */ |          */ | ||||||
|         void CommunicationGetStatus(Kernel::HLERequestContext& ctx); |         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: |     private: | ||||||
|         std::shared_ptr<Module> nfc; |         std::shared_ptr<Module> nfc; | ||||||
|     }; |     }; | ||||||
|  | @ -174,8 +234,10 @@ public: | ||||||
| private: | private: | ||||||
|     Kernel::SharedPtr<Kernel::Event> tag_in_range_event; |     Kernel::SharedPtr<Kernel::Event> tag_in_range_event; | ||||||
|     Kernel::SharedPtr<Kernel::Event> tag_out_of_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; |     CommunicationStatus nfc_status = CommunicationStatus::NfcInitialized; | ||||||
|  | 
 | ||||||
|  |     AmiiboData amiibo_data{}; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| void InstallInterfaces(Core::System& system); | 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"}, |         {0x000D0000, &NFC_M::GetTagState, "GetTagState"}, | ||||||
|         {0x000F0000, &NFC_M::CommunicationGetStatus, "CommunicationGetStatus"}, |         {0x000F0000, &NFC_M::CommunicationGetStatus, "CommunicationGetStatus"}, | ||||||
|         {0x00100000, nullptr, "GetTagInfo2"}, |         {0x00100000, nullptr, "GetTagInfo2"}, | ||||||
|         {0x00110000, nullptr, "GetTagInfo"}, |         {0x00110000, &NFC_M::GetTagInfo, "GetTagInfo"}, | ||||||
|         {0x00120000, nullptr, "CommunicationGetResult"}, |         {0x00120000, nullptr, "CommunicationGetResult"}, | ||||||
|         {0x00130040, nullptr, "OpenAppData"}, |         {0x00130040, nullptr, "OpenAppData"}, | ||||||
|         {0x00140384, nullptr, "InitializeWriteAppData"}, |         {0x00140384, nullptr, "InitializeWriteAppData"}, | ||||||
|         {0x00150040, nullptr, "ReadAppData"}, |         {0x00150040, nullptr, "ReadAppData"}, | ||||||
|         {0x00160242, nullptr, "WriteAppData"}, |         {0x00160242, nullptr, "WriteAppData"}, | ||||||
|         {0x00170000, nullptr, "GetAmiiboSettings"}, |         {0x00170000, nullptr, "GetAmiiboSettings"}, | ||||||
|         {0x00180000, nullptr, "GetAmiiboConfig"}, |         {0x00180000, &NFC_M::GetAmiiboConfig, "GetAmiiboConfig"}, | ||||||
|         {0x00190000, nullptr, "GetAppDataInitStruct"}, |         {0x00190000, nullptr, "GetAppDataInitStruct"}, | ||||||
|  |         {0x001A0000, &NFC_M::Unknown0x1A, "Unknown0x1A"}, | ||||||
|  |         {0x001B0000, &NFC_M::GetIdentificationBlock, "GetIdentificationBlock"}, | ||||||
|         // nfc:m
 |         // nfc:m
 | ||||||
|         {0x04040A40, nullptr, "SetAmiiboSettings"} |         {0x04040A40, nullptr, "SetAmiiboSettings"} | ||||||
|         // clang-format on
 |         // 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"}, |         {0x000D0000, &NFC_U::GetTagState, "GetTagState"}, | ||||||
|         {0x000F0000, &NFC_U::CommunicationGetStatus, "CommunicationGetStatus"}, |         {0x000F0000, &NFC_U::CommunicationGetStatus, "CommunicationGetStatus"}, | ||||||
|         {0x00100000, nullptr, "GetTagInfo2"}, |         {0x00100000, nullptr, "GetTagInfo2"}, | ||||||
|         {0x00110000, nullptr, "GetTagInfo"}, |         {0x00110000, &NFC_U::GetTagInfo, "GetTagInfo"}, | ||||||
|         {0x00120000, nullptr, "CommunicationGetResult"}, |         {0x00120000, nullptr, "CommunicationGetResult"}, | ||||||
|         {0x00130040, nullptr, "OpenAppData"}, |         {0x00130040, nullptr, "OpenAppData"}, | ||||||
|         {0x00140384, nullptr, "InitializeWriteAppData"}, |         {0x00140384, nullptr, "InitializeWriteAppData"}, | ||||||
|         {0x00150040, nullptr, "ReadAppData"}, |         {0x00150040, nullptr, "ReadAppData"}, | ||||||
|         {0x00160242, nullptr, "WriteAppData"}, |         {0x00160242, nullptr, "WriteAppData"}, | ||||||
|         {0x00170000, nullptr, "GetAmiiboSettings"}, |         {0x00170000, nullptr, "GetAmiiboSettings"}, | ||||||
|         {0x00180000, nullptr, "GetAmiiboConfig"}, |         {0x00180000, &NFC_U::GetAmiiboConfig, "GetAmiiboConfig"}, | ||||||
|         {0x00190000, nullptr, "GetAppDataInitStruct"}, |         {0x00190000, nullptr, "GetAppDataInitStruct"}, | ||||||
|  |         {0x001A0000, &NFC_U::Unknown0x1A, "Unknown0x1A"}, | ||||||
|  |         {0x001B0000, &NFC_U::GetIdentificationBlock, "GetIdentificationBlock"}, | ||||||
|         // clang-format on
 |         // clang-format on
 | ||||||
|     }; |     }; | ||||||
|     RegisterHandlers(functions); |     RegisterHandlers(functions); | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue