diff --git a/src/citra_qt/main.cpp b/src/citra_qt/main.cpp index af33515c1..83fd6a86a 100644 --- a/src/citra_qt/main.cpp +++ b/src/citra_qt/main.cpp @@ -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("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("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()) { diff --git a/src/citra_qt/main.h b/src/citra_qt/main.h index fc1382ddd..684a9e332 100644 --- a/src/citra_qt/main.h +++ b/src/citra_qt/main.h @@ -166,6 +166,8 @@ private slots: void OnCIAInstallFinished(); void OnMenuRecentFile(); void OnConfigure(); + void OnLoadAmiibo(); + void OnRemoveAmiibo(); void OnToggleFilterBar(); void OnDisplayTitleBars(bool); void ToggleFullscreen(); diff --git a/src/citra_qt/main.ui b/src/citra_qt/main.ui index c2c58300d..b5810715f 100644 --- a/src/citra_qt/main.ui +++ b/src/citra_qt/main.ui @@ -57,11 +57,20 @@ Recent Files + + + Amiibo + + + + + + @@ -415,6 +424,22 @@ Restart + + + false + + + Load... + + + + + false + + + Remove + + diff --git a/src/core/hle/service/nfc/nfc.cpp b/src/core/hle/service/nfc/nfc.cpp index e76c7b3ee..cadb0fa77 100644 --- a/src/core/hle/service/nfc/nfc.cpp +++ b/src/core/hle/service/nfc/nfc.cpp @@ -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 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 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(); + IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); + if (nfc->nfc_tag_state != TagState::NotInitialized) { + LOG_ERROR(Service_NFC, "Invalid TagState {}", static_cast(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(); - 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(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(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(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(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(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(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(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(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(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(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(identification_block_reply); + LOG_DEBUG(Service_NFC, "called"); +} + +std::shared_ptr 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 nfc, const char* name, u32 max_session) : ServiceFramework(name, max_session), nfc(std::move(nfc)) {} diff --git a/src/core/hle/service/nfc/nfc.h b/src/core/hle/service/nfc/nfc.h index bafdcf0fa..afa05d60e 100644 --- a/src/core/hle/service/nfc/nfc.h +++ b/src/core/hle/service/nfc/nfc.h @@ -4,6 +4,7 @@ #pragma once +#include #include #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 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 nfc, const char* name, u32 max_session); ~Interface(); + std::shared_ptr 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 nfc; }; @@ -174,8 +234,10 @@ public: private: Kernel::SharedPtr tag_in_range_event; Kernel::SharedPtr tag_out_of_range_event; - TagState nfc_tag_state = TagState::NotInitialized; + std::atomic nfc_tag_state = TagState::NotInitialized; CommunicationStatus nfc_status = CommunicationStatus::NfcInitialized; + + AmiiboData amiibo_data{}; }; void InstallInterfaces(Core::System& system); diff --git a/src/core/hle/service/nfc/nfc_m.cpp b/src/core/hle/service/nfc/nfc_m.cpp index d91b6a31a..310490b8f 100644 --- a/src/core/hle/service/nfc/nfc_m.cpp +++ b/src/core/hle/service/nfc/nfc_m.cpp @@ -24,15 +24,17 @@ NFC_M::NFC_M(std::shared_ptr 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 diff --git a/src/core/hle/service/nfc/nfc_u.cpp b/src/core/hle/service/nfc/nfc_u.cpp index 8cdcca287..a6e99ace2 100644 --- a/src/core/hle/service/nfc/nfc_u.cpp +++ b/src/core/hle/service/nfc/nfc_u.cpp @@ -23,15 +23,17 @@ NFC_U::NFC_U(std::shared_ptr 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);