diff options
| author | Narr the Reg <juangerman-13@hotmail.com> | 2023-05-17 22:17:16 -0600 | 
|---|---|---|
| committer | german77 <juangerman-13@hotmail.com> | 2023-05-21 21:09:20 -0600 | 
| commit | fdb2002f77de6af19cc7f526b2e7540c329161c3 (patch) | |
| tree | 6bcb2ca55810c05d15a561e2fa0bc0a6c1a9175a | |
| parent | 1e398e6c36bf8839ca530497fee3772f51d62126 (diff) | |
input_common: Implement amiibo writting
| -rw-r--r-- | src/core/hid/emulated_controller.cpp | 9 | ||||
| -rw-r--r-- | src/core/hle/service/nfc/common/device.cpp | 8 | ||||
| -rw-r--r-- | src/input_common/drivers/joycon.cpp | 8 | ||||
| -rw-r--r-- | src/input_common/helpers/joycon_driver.cpp | 20 | ||||
| -rw-r--r-- | src/input_common/helpers/joycon_driver.h | 1 | ||||
| -rw-r--r-- | src/input_common/helpers/joycon_protocol/joycon_types.h | 50 | ||||
| -rw-r--r-- | src/input_common/helpers/joycon_protocol/nfc.cpp | 332 | ||||
| -rw-r--r-- | src/input_common/helpers/joycon_protocol/nfc.h | 27 | 
8 files changed, 387 insertions, 68 deletions
diff --git a/src/core/hid/emulated_controller.cpp b/src/core/hid/emulated_controller.cpp index 366880711..bbfea7117 100644 --- a/src/core/hid/emulated_controller.cpp +++ b/src/core/hid/emulated_controller.cpp @@ -1283,9 +1283,14 @@ bool EmulatedController::HasNfc() const {  }  bool EmulatedController::WriteNfc(const std::vector<u8>& data) { -    auto& nfc_output_device = output_devices[3]; +    auto& nfc_output_device = output_devices[static_cast<std::size_t>(DeviceIndex::Right)]; +    auto& nfc_virtual_output_device = output_devices[3]; + +    if (nfc_output_device->SupportsNfc() != Common::Input::NfcState::NotSupported) { +        return nfc_output_device->WriteNfcData(data) == Common::Input::NfcState::Success; +    } -    return nfc_output_device->WriteNfcData(data) == Common::Input::NfcState::Success; +    return nfc_virtual_output_device->WriteNfcData(data) == Common::Input::NfcState::Success;  }  void EmulatedController::SetLedPattern() { diff --git a/src/core/hle/service/nfc/common/device.cpp b/src/core/hle/service/nfc/common/device.cpp index 322bde2ed..8a7e9edac 100644 --- a/src/core/hle/service/nfc/common/device.cpp +++ b/src/core/hle/service/nfc/common/device.cpp @@ -421,11 +421,11 @@ Result NfcDevice::Flush() {      tag_data.write_counter++; -    FlushWithBreak(NFP::BreakType::Normal); +    const auto result = FlushWithBreak(NFP::BreakType::Normal);      is_data_moddified = false; -    return ResultSuccess; +    return result;  }  Result NfcDevice::FlushDebug() { @@ -444,11 +444,11 @@ Result NfcDevice::FlushDebug() {      tag_data.write_counter++; -    FlushWithBreak(NFP::BreakType::Normal); +    const auto result = FlushWithBreak(NFP::BreakType::Normal);      is_data_moddified = false; -    return ResultSuccess; +    return result;  }  Result NfcDevice::FlushWithBreak(NFP::BreakType break_type) { diff --git a/src/input_common/drivers/joycon.cpp b/src/input_common/drivers/joycon.cpp index 653862a72..b2b5677c8 100644 --- a/src/input_common/drivers/joycon.cpp +++ b/src/input_common/drivers/joycon.cpp @@ -291,9 +291,13 @@ Common::Input::NfcState Joycons::SupportsNfc(const PadIdentifier& identifier_) c      return Common::Input::NfcState::Success;  }; -Common::Input::NfcState Joycons::WriteNfcData(const PadIdentifier& identifier_, +Common::Input::NfcState Joycons::WriteNfcData(const PadIdentifier& identifier,                                                const std::vector<u8>& data) { -    return Common::Input::NfcState::NotSupported; +    auto handle = GetHandle(identifier); +    if (handle->WriteNfcData(data) != Joycon::DriverResult::Success) { +        return Common::Input::NfcState::WriteFailed; +    } +    return Common::Input::NfcState::Success;  };  Common::Input::DriverResult Joycons::SetPollingMode(const PadIdentifier& identifier, diff --git a/src/input_common/helpers/joycon_driver.cpp b/src/input_common/helpers/joycon_driver.cpp index 83429a336..95106f16d 100644 --- a/src/input_common/helpers/joycon_driver.cpp +++ b/src/input_common/helpers/joycon_driver.cpp @@ -492,6 +492,26 @@ DriverResult JoyconDriver::SetRingConMode() {      return result;  } +DriverResult JoyconDriver::WriteNfcData(std::span<const u8> data) { +    std::scoped_lock lock{mutex}; +    disable_input_thread = true; + +    if (!supported_features.nfc) { +        return DriverResult::NotSupported; +    } +    if (!nfc_protocol->IsEnabled()) { +        return DriverResult::Disabled; +    } +    if (!amiibo_detected) { +        return DriverResult::ErrorWritingData; +    } + +    const auto result = nfc_protocol->WriteAmiibo(data); + +    disable_input_thread = false; +    return result; +} +  bool JoyconDriver::IsConnected() const {      std::scoped_lock lock{mutex};      return is_connected.load(); diff --git a/src/input_common/helpers/joycon_driver.h b/src/input_common/helpers/joycon_driver.h index 72a9e71dc..e9b2fccbb 100644 --- a/src/input_common/helpers/joycon_driver.h +++ b/src/input_common/helpers/joycon_driver.h @@ -49,6 +49,7 @@ public:      DriverResult SetIrMode();      DriverResult SetNfcMode();      DriverResult SetRingConMode(); +    DriverResult WriteNfcData(std::span<const u8> data);      void SetCallbacks(const JoyconCallbacks& callbacks); diff --git a/src/input_common/helpers/joycon_protocol/joycon_types.h b/src/input_common/helpers/joycon_protocol/joycon_types.h index 353dc744d..5007b0e18 100644 --- a/src/input_common/helpers/joycon_protocol/joycon_types.h +++ b/src/input_common/helpers/joycon_protocol/joycon_types.h @@ -23,6 +23,7 @@ constexpr std::array<u8, 8> DefaultVibrationBuffer{0x0, 0x1, 0x40, 0x40, 0x0, 0x  using MacAddress = std::array<u8, 6>;  using SerialNumber = std::array<u8, 15>; +using TagUUID = std::array<u8, 7>;  enum class ControllerType : u8 {      None = 0x00, @@ -276,12 +277,13 @@ enum class MCUPacketFlag : u8 {      LastCommandPacket = 0x08,  }; -enum class NFCReadCommand : u8 { +enum class NFCCommand : u8 {      CancelAll = 0x00,      StartPolling = 0x01,      StopPolling = 0x02,      StartWaitingRecieve = 0x04, -    Ntag = 0x06, +    ReadNtag = 0x06, +    WriteNtag = 0x08,      Mifare = 0x0F,  }; @@ -292,14 +294,19 @@ enum class NFCTagType : u8 {  enum class NFCPages {      Block0 = 0, +    Block3 = 3,      Block45 = 45,      Block135 = 135,      Block231 = 231,  };  enum class NFCStatus : u8 { +    Ready = 0x00, +    Polling = 0x01,      LastPackage = 0x04, +    WriteDone = 0x05,      TagLost = 0x07, +    WriteReady = 0x09,  };  enum class IrsMode : u8 { @@ -559,13 +566,32 @@ static_assert(sizeof(NFCReadBlockCommand) == 0x9, "NFCReadBlockCommand is an inv  struct NFCReadCommandData {      u8 unknown;      u8 uuid_length; -    u8 unknown_2; -    std::array<u8, 6> uid; +    TagUUID uid;      NFCTagType tag_type;      NFCReadBlockCommand read_block;  };  static_assert(sizeof(NFCReadCommandData) == 0x13, "NFCReadCommandData is an invalid size"); +#pragma pack(push, 1) +struct NFCWriteCommandData { +    u8 unknown; +    u8 uuid_length; +    TagUUID uid; +    NFCTagType tag_type; +    u8 unknown2; +    u8 unknown3; +    u8 unknown4; +    u8 unknown5; +    u8 unknown6; +    u8 unknown7; +    u8 unknown8; +    u8 magic; +    u16_be write_count; +    u8 amiibo_version; +}; +static_assert(sizeof(NFCWriteCommandData) == 0x15, "NFCWriteCommandData is an invalid size"); +#pragma pack(pop) +  struct NFCPollingCommandData {      u8 enable_mifare;      u8 unknown_1; @@ -576,8 +602,8 @@ struct NFCPollingCommandData {  static_assert(sizeof(NFCPollingCommandData) == 0x05, "NFCPollingCommandData is an invalid size");  struct NFCRequestState { -    NFCReadCommand command_argument; -    INSERT_PADDING_BYTES(0x1); +    NFCCommand command_argument; +    u8 block_id;      u8 packet_id;      MCUPacketFlag packet_flag;      u8 data_length; @@ -591,6 +617,18 @@ struct NFCRequestState {  };  static_assert(sizeof(NFCRequestState) == 0x26, "NFCRequestState is an invalid size"); +struct NFCDataChunk { +    u8 nfc_page; +    u8 data_size; +    std::array<u8, 0xFF> data; +}; + +struct NFCWritePackage { +    NFCWriteCommandData command_data; +    u8 number_of_chunks; +    std::array<NFCDataChunk, 4> data_chunks; +}; +  struct IrsConfigure {      MCUCommand command;      MCUSubCommand sub_command; diff --git a/src/input_common/helpers/joycon_protocol/nfc.cpp b/src/input_common/helpers/joycon_protocol/nfc.cpp index 46c9e9489..3b7a628e5 100644 --- a/src/input_common/helpers/joycon_protocol/nfc.cpp +++ b/src/input_common/helpers/joycon_protocol/nfc.cpp @@ -34,6 +34,12 @@ DriverResult NfcProtocol::EnableNfc() {          result = ConfigureMCU(config);      } +    if (result == DriverResult::Success) { +        result = WaitSetMCUMode(ReportMode::NFC_IR_MODE_60HZ, MCUMode::NFC); +    } +    if (result == DriverResult::Success) { +        result = WaitUntilNfcIs(NFCStatus::Ready); +    }      return result;  } @@ -56,27 +62,20 @@ DriverResult NfcProtocol::StartNFCPollingMode() {      LOG_DEBUG(Input, "Start NFC pooling Mode");      ScopedSetBlocking sb(this);      DriverResult result{DriverResult::Success}; -    TagFoundData tag_data{};      if (result == DriverResult::Success) { -        result = WaitSetMCUMode(ReportMode::NFC_IR_MODE_60HZ, MCUMode::NFC); -    } -    if (result == DriverResult::Success) { -        result = WaitUntilNfcIsReady(); -    } -    if (result == DriverResult::Success) {          MCUCommandResponse output{};          result = SendStopPollingRequest(output);      }      if (result == DriverResult::Success) { -        result = WaitUntilNfcIsReady(); +        result = WaitUntilNfcIs(NFCStatus::Ready);      }      if (result == DriverResult::Success) {          MCUCommandResponse output{};          result = SendStartPollingRequest(output);      }      if (result == DriverResult::Success) { -        result = WaitUntilNfcIsPolling(); +        result = WaitUntilNfcIs(NFCStatus::Polling);      }      if (result == DriverResult::Success) {          is_enabled = true; @@ -112,6 +111,49 @@ DriverResult NfcProtocol::ScanAmiibo(std::vector<u8>& data) {      return result;  } +DriverResult NfcProtocol::WriteAmiibo(std::span<const u8> data) { +    LOG_DEBUG(Input, "Write amiibo"); +    ScopedSetBlocking sb(this); +    DriverResult result{DriverResult::Success}; +    TagUUID tag_uuid = GetTagUUID(data); +    TagFoundData tag_data{}; + +    if (result == DriverResult::Success) { +        result = IsTagInRange(tag_data, 7); +    } +    if (result == DriverResult::Success) { +        if (tag_data.uuid != tag_uuid) { +            result = DriverResult::InvalidParameters; +        } +    } +    if (result == DriverResult::Success) { +        MCUCommandResponse output{}; +        result = SendStopPollingRequest(output); +    } +    if (result == DriverResult::Success) { +        result = WaitUntilNfcIs(NFCStatus::Ready); +    } +    if (result == DriverResult::Success) { +        MCUCommandResponse output{}; +        result = SendStartPollingRequest(output, true); +    } +    if (result == DriverResult::Success) { +        result = WaitUntilNfcIs(NFCStatus::WriteReady); +    } +    if (result == DriverResult::Success) { +        result = WriteAmiiboData(tag_uuid, data); +    } +    if (result == DriverResult::Success) { +        result = WaitUntilNfcIs(NFCStatus::WriteDone); +    } +    if (result == DriverResult::Success) { +        MCUCommandResponse output{}; +        result = SendStopPollingRequest(output); +    } + +    return result; +} +  bool NfcProtocol::HasAmiibo() {      if (update_counter++ < AMIIBO_UPDATE_DELAY) {          return true; @@ -129,7 +171,7 @@ bool NfcProtocol::HasAmiibo() {      return result == DriverResult::Success;  } -DriverResult NfcProtocol::WaitUntilNfcIsReady() { +DriverResult NfcProtocol::WaitUntilNfcIs(NFCStatus status) {      constexpr std::size_t timeout_limit = 10;      MCUCommandResponse output{};      std::size_t tries = 0; @@ -145,28 +187,7 @@ DriverResult NfcProtocol::WaitUntilNfcIsReady() {          }      } while (output.mcu_report != MCUReport::NFCState ||               (output.mcu_data[1] << 8) + output.mcu_data[0] != 0x0500 || -             output.mcu_data[5] != 0x31 || output.mcu_data[6] != 0x00); - -    return DriverResult::Success; -} - -DriverResult NfcProtocol::WaitUntilNfcIsPolling() { -    constexpr std::size_t timeout_limit = 10; -    MCUCommandResponse output{}; -    std::size_t tries = 0; - -    do { -        auto result = SendNextPackageRequest(output, {}); - -        if (result != DriverResult::Success) { -            return result; -        } -        if (tries++ > timeout_limit) { -            return DriverResult::Timeout; -        } -    } while (output.mcu_report != MCUReport::NFCState || -             (output.mcu_data[1] << 8) + output.mcu_data[0] != 0x0500 || -             output.mcu_data[5] != 0x31 || output.mcu_data[6] != 0x01); +             output.mcu_data[5] != 0x31 || output.mcu_data[6] != static_cast<u8>(status));      return DriverResult::Success;  } @@ -188,7 +209,7 @@ DriverResult NfcProtocol::IsTagInRange(TagFoundData& data, std::size_t timeout_l               (output.mcu_data[6] != 0x09 && output.mcu_data[6] != 0x04));      data.type = output.mcu_data[12]; -    data.uuid.resize(output.mcu_data[14]); +    data.uuid_size = std::min(output.mcu_data[14], static_cast<u8>(sizeof(TagUUID)));      memcpy(data.uuid.data(), output.mcu_data.data() + 15, data.uuid.size());      return DriverResult::Success; @@ -245,17 +266,94 @@ DriverResult NfcProtocol::GetAmiiboData(std::vector<u8>& ntag_data) {      return DriverResult::Timeout;  } -DriverResult NfcProtocol::SendStartPollingRequest(MCUCommandResponse& output) { +DriverResult NfcProtocol::WriteAmiiboData(const TagUUID& tag_uuid, std::span<const u8> data) { +    constexpr std::size_t timeout_limit = 60; +    const auto nfc_data = MakeAmiiboWritePackage(tag_uuid, data); +    const std::vector<u8> nfc_buffer_data = SerializeWritePackage(nfc_data); +    std::span<const u8> buffer(nfc_buffer_data); +    MCUCommandResponse output{}; +    u8 block_id = 1; +    u8 package_index = 0; +    std::size_t tries = 0; +    std::size_t current_position = 0; + +    LOG_INFO(Input, "Writing amiibo data"); + +    auto result = SendWriteAmiiboRequest(output, tag_uuid); + +    if (result != DriverResult::Success) { +        return result; +    } + +    // Read Tag data but ignore the actual sent data +    while (tries++ < timeout_limit) { +        result = SendNextPackageRequest(output, package_index); +        const auto nfc_status = static_cast<NFCStatus>(output.mcu_data[6]); + +        if (result != DriverResult::Success) { +            return result; +        } + +        if ((output.mcu_report == MCUReport::NFCReadData || +             output.mcu_report == MCUReport::NFCState) && +            nfc_status == NFCStatus::TagLost) { +            return DriverResult::ErrorReadingData; +        } + +        if (output.mcu_report == MCUReport::NFCReadData && output.mcu_data[1] == 0x07) { +            package_index++; +            continue; +        } + +        if (output.mcu_report == MCUReport::NFCState && nfc_status == NFCStatus::LastPackage) { +            LOG_INFO(Input, "Finished reading amiibo"); +            break; +        } +    } + +    // Send Data. Nfc buffer size is 31, Send the data in smaller packages +    while (current_position < buffer.size() && tries++ < timeout_limit) { +        const std::size_t next_position = +            std::min(current_position + sizeof(NFCRequestState::raw_data), buffer.size()); +        const std::size_t block_size = next_position - current_position; +        const bool is_last_packet = block_size < sizeof(NFCRequestState::raw_data); + +        SendWriteDataAmiiboRequest(output, block_id, is_last_packet, +                                   buffer.subspan(current_position, block_size)); + +        const auto nfc_status = static_cast<NFCStatus>(output.mcu_data[6]); + +        if ((output.mcu_report == MCUReport::NFCReadData || +             output.mcu_report == MCUReport::NFCState) && +            nfc_status == NFCStatus::TagLost) { +            return DriverResult::ErrorReadingData; +        } + +        // Increase position when data is confirmed by the joycon +        if (output.mcu_report == MCUReport::NFCState && +            (output.mcu_data[1] << 8) + output.mcu_data[0] == 0x0500 && +            output.mcu_data[3] == block_id) { +            block_id++; +            current_position = next_position; +        } +    } + +    return result; +} + +DriverResult NfcProtocol::SendStartPollingRequest(MCUCommandResponse& output, +                                                  bool is_second_attempt) {      NFCRequestState request{ -        .command_argument = NFCReadCommand::StartPolling, -        .packet_id = 0x0, +        .command_argument = NFCCommand::StartPolling, +        .block_id = {}, +        .packet_id = {},          .packet_flag = MCUPacketFlag::LastCommandPacket,          .data_length = sizeof(NFCPollingCommandData),          .nfc_polling =              { -                .enable_mifare = 0x01, -                .unknown_1 = 0x00, -                .unknown_2 = 0x00, +                .enable_mifare = 0x00, +                .unknown_1 = static_cast<u8>(is_second_attempt ? 0xe8 : 0x00), +                .unknown_2 = static_cast<u8>(is_second_attempt ? 0x03 : 0x00),                  .unknown_3 = 0x2c,                  .unknown_4 = 0x01,              }, @@ -271,10 +369,11 @@ DriverResult NfcProtocol::SendStartPollingRequest(MCUCommandResponse& output) {  DriverResult NfcProtocol::SendStopPollingRequest(MCUCommandResponse& output) {      NFCRequestState request{ -        .command_argument = NFCReadCommand::StopPolling, -        .packet_id = 0x0, +        .command_argument = NFCCommand::StopPolling, +        .block_id = {}, +        .packet_id = {},          .packet_flag = MCUPacketFlag::LastCommandPacket, -        .data_length = 0, +        .data_length = {},          .raw_data = {},          .crc = {},      }; @@ -288,10 +387,11 @@ DriverResult NfcProtocol::SendStopPollingRequest(MCUCommandResponse& output) {  DriverResult NfcProtocol::SendNextPackageRequest(MCUCommandResponse& output, u8 packet_id) {      NFCRequestState request{ -        .command_argument = NFCReadCommand::StartWaitingRecieve, +        .command_argument = NFCCommand::StartWaitingRecieve, +        .block_id = {},          .packet_id = packet_id,          .packet_flag = MCUPacketFlag::LastCommandPacket, -        .data_length = 0, +        .data_length = {},          .raw_data = {},          .crc = {},      }; @@ -305,17 +405,17 @@ DriverResult NfcProtocol::SendNextPackageRequest(MCUCommandResponse& output, u8  DriverResult NfcProtocol::SendReadAmiiboRequest(MCUCommandResponse& output, NFCPages ntag_pages) {      NFCRequestState request{ -        .command_argument = NFCReadCommand::Ntag, -        .packet_id = 0x0, +        .command_argument = NFCCommand::ReadNtag, +        .block_id = {}, +        .packet_id = {},          .packet_flag = MCUPacketFlag::LastCommandPacket,          .data_length = sizeof(NFCReadCommandData),          .nfc_read =              {                  .unknown = 0xd0, -                .uuid_length = 0x07, -                .unknown_2 = 0x00, +                .uuid_length = sizeof(NFCReadCommandData::uid),                  .uid = {}, -                .tag_type = NFCTagType::AllTags, +                .tag_type = NFCTagType::Ntag215,                  .read_block = GetReadBlockCommand(ntag_pages),              },          .crc = {}, @@ -328,12 +428,135 @@ DriverResult NfcProtocol::SendReadAmiiboRequest(MCUCommandResponse& output, NFCP                         output);  } +DriverResult NfcProtocol::SendWriteAmiiboRequest(MCUCommandResponse& output, +                                                 const TagUUID& tag_uuid) { +    NFCRequestState request{ +        .command_argument = NFCCommand::ReadNtag, +        .block_id = {}, +        .packet_id = {}, +        .packet_flag = MCUPacketFlag::LastCommandPacket, +        .data_length = sizeof(NFCReadCommandData), +        .nfc_read = +            { +                .unknown = 0xd0, +                .uuid_length = sizeof(NFCReadCommandData::uid), +                .uid = tag_uuid, +                .tag_type = NFCTagType::Ntag215, +                .read_block = GetReadBlockCommand(NFCPages::Block3), +            }, +        .crc = {}, +    }; + +    std::array<u8, sizeof(NFCRequestState)> request_data{}; +    memcpy(request_data.data(), &request, sizeof(NFCRequestState)); +    request_data[36] = CalculateMCU_CRC8(request_data.data(), 36); +    return SendMCUData(ReportMode::NFC_IR_MODE_60HZ, MCUSubCommand::ReadDeviceMode, request_data, +                       output); +} + +DriverResult NfcProtocol::SendWriteDataAmiiboRequest(MCUCommandResponse& output, u8 block_id, +                                                     bool is_last_packet, +                                                     std::span<const u8> data) { +    const auto data_size = std::min(data.size(), sizeof(NFCRequestState::raw_data)); +    NFCRequestState request{ +        .command_argument = NFCCommand::WriteNtag, +        .block_id = block_id, +        .packet_id = {}, +        .packet_flag = +            is_last_packet ? MCUPacketFlag::LastCommandPacket : MCUPacketFlag::MorePacketsRemaining, +        .data_length = static_cast<u8>(data_size), +        .raw_data = {}, +        .crc = {}, +    }; +    memcpy(request.raw_data.data(), data.data(), data_size); + +    std::array<u8, sizeof(NFCRequestState)> request_data{}; +    memcpy(request_data.data(), &request, sizeof(NFCRequestState)); +    request_data[36] = CalculateMCU_CRC8(request_data.data(), 36); +    return SendMCUData(ReportMode::NFC_IR_MODE_60HZ, MCUSubCommand::ReadDeviceMode, request_data, +                       output); +} + +std::vector<u8> NfcProtocol::SerializeWritePackage(const NFCWritePackage& package) const { +    const std::size_t header_size = +        sizeof(NFCWriteCommandData) + sizeof(NFCWritePackage::number_of_chunks); +    std::vector<u8> serialized_data(header_size); +    std::size_t start_index = 0; + +    memcpy(serialized_data.data(), &package, header_size); +    start_index += header_size; + +    for (const auto& data_chunk : package.data_chunks) { +        const std::size_t chunk_size = +            sizeof(NFCDataChunk::nfc_page) + sizeof(NFCDataChunk::data_size) + data_chunk.data_size; + +        serialized_data.resize(start_index + chunk_size); +        memcpy(serialized_data.data() + start_index, &data_chunk, chunk_size); +        start_index += chunk_size; +    } + +    return serialized_data; +} + +NFCWritePackage NfcProtocol::MakeAmiiboWritePackage(const TagUUID& tag_uuid, +                                                    std::span<const u8> data) const { +    return { +        .command_data{ +            .unknown = 0xd0, +            .uuid_length = sizeof(NFCReadCommandData::uid), +            .uid = tag_uuid, +            .tag_type = NFCTagType::Ntag215, +            .unknown2 = 0x00, +            .unknown3 = 0x01, +            .unknown4 = 0x04, +            .unknown5 = 0xff, +            .unknown6 = 0xff, +            .unknown7 = 0xff, +            .unknown8 = 0xff, +            .magic = data[16], +            .write_count = static_cast<u16>((data[17] << 8) + data[18]), +            .amiibo_version = data[19], +        }, +        .number_of_chunks = 3, +        .data_chunks = +            { +                MakeAmiiboChunk(0x05, 0x20, data), +                MakeAmiiboChunk(0x20, 0xf0, data), +                MakeAmiiboChunk(0x5c, 0x98, data), +            }, +    }; +} + +NFCDataChunk NfcProtocol::MakeAmiiboChunk(u8 page, u8 size, std::span<const u8> data) const { +    constexpr u8 PAGE_SIZE = 4; + +    if (static_cast<std::size_t>(page * PAGE_SIZE) + size >= data.size()) { +        return {}; +    } + +    NFCDataChunk chunk{ +        .nfc_page = page, +        .data_size = size, +        .data = {}, +    }; +    std::memcpy(chunk.data.data(), data.data() + (page * PAGE_SIZE), size); +    return chunk; +} +  NFCReadBlockCommand NfcProtocol::GetReadBlockCommand(NFCPages pages) const {      switch (pages) {      case NFCPages::Block0:          return {              .block_count = 1,          }; +    case NFCPages::Block3: +        return { +            .block_count = 1, +            .blocks = +                { +                    NFCReadBlock{0x03, 0x03}, +                }, +        };      case NFCPages::Block45:          return {              .block_count = 1, @@ -368,6 +591,17 @@ NFCReadBlockCommand NfcProtocol::GetReadBlockCommand(NFCPages pages) const {      };  } +TagUUID NfcProtocol::GetTagUUID(std::span<const u8> data) const { +    if (data.size() < 10) { +        return {}; +    } + +    // crc byte 3 is omitted in this operation +    return { +        data[0], data[1], data[2], data[4], data[5], data[6], data[7], +    }; +} +  bool NfcProtocol::IsEnabled() const {      return is_enabled;  } diff --git a/src/input_common/helpers/joycon_protocol/nfc.h b/src/input_common/helpers/joycon_protocol/nfc.h index c9e9af03f..eb58c427d 100644 --- a/src/input_common/helpers/joycon_protocol/nfc.h +++ b/src/input_common/helpers/joycon_protocol/nfc.h @@ -27,6 +27,8 @@ public:      DriverResult ScanAmiibo(std::vector<u8>& data); +    DriverResult WriteAmiibo(std::span<const u8> data); +      bool HasAmiibo();      bool IsEnabled() const; @@ -37,18 +39,20 @@ private:      struct TagFoundData {          u8 type; -        std::vector<u8> uuid; +        u8 uuid_size; +        TagUUID uuid;      }; -    DriverResult WaitUntilNfcIsReady(); - -    DriverResult WaitUntilNfcIsPolling(); +    DriverResult WaitUntilNfcIs(NFCStatus status);      DriverResult IsTagInRange(TagFoundData& data, std::size_t timeout_limit = 1);      DriverResult GetAmiiboData(std::vector<u8>& data); -    DriverResult SendStartPollingRequest(MCUCommandResponse& output); +    DriverResult WriteAmiiboData(const TagUUID& tag_uuid, std::span<const u8> data); + +    DriverResult SendStartPollingRequest(MCUCommandResponse& output, +                                         bool is_second_attempt = false);      DriverResult SendStopPollingRequest(MCUCommandResponse& output); @@ -56,8 +60,21 @@ private:      DriverResult SendReadAmiiboRequest(MCUCommandResponse& output, NFCPages ntag_pages); +    DriverResult SendWriteAmiiboRequest(MCUCommandResponse& output, const TagUUID& tag_uuid); + +    DriverResult SendWriteDataAmiiboRequest(MCUCommandResponse& output, u8 block_id, +                                            bool is_last_packet, std::span<const u8> data); + +    std::vector<u8> SerializeWritePackage(const NFCWritePackage& package) const; + +    NFCWritePackage MakeAmiiboWritePackage(const TagUUID& tag_uuid, std::span<const u8> data) const; + +    NFCDataChunk MakeAmiiboChunk(u8 page, u8 size, std::span<const u8> data) const; +      NFCReadBlockCommand GetReadBlockCommand(NFCPages pages) const; +    TagUUID GetTagUUID(std::span<const u8> data) const; +      bool is_enabled{};      std::size_t update_counter{};  };  | 
