mirror of https://git.suyu.dev/suyu/suyu
Merge pull request #10396 from german77/amiibo_write
input_common: Implement amiibo writing
This commit is contained in:
commit
ffa1fba7d6
|
@ -1283,11 +1283,16 @@ bool EmulatedController::HasNfc() const {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool EmulatedController::WriteNfc(const std::vector<u8>& data) {
|
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() {
|
void EmulatedController::SetLedPattern() {
|
||||||
for (auto& device : output_devices) {
|
for (auto& device : output_devices) {
|
||||||
if (!device) {
|
if (!device) {
|
||||||
|
|
|
@ -426,11 +426,11 @@ Result NfcDevice::Flush() {
|
||||||
|
|
||||||
tag_data.write_counter++;
|
tag_data.write_counter++;
|
||||||
|
|
||||||
FlushWithBreak(NFP::BreakType::Normal);
|
const auto result = FlushWithBreak(NFP::BreakType::Normal);
|
||||||
|
|
||||||
is_data_moddified = false;
|
is_data_moddified = false;
|
||||||
|
|
||||||
return ResultSuccess;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
Result NfcDevice::FlushDebug() {
|
Result NfcDevice::FlushDebug() {
|
||||||
|
@ -449,11 +449,11 @@ Result NfcDevice::FlushDebug() {
|
||||||
|
|
||||||
tag_data.write_counter++;
|
tag_data.write_counter++;
|
||||||
|
|
||||||
FlushWithBreak(NFP::BreakType::Normal);
|
const auto result = FlushWithBreak(NFP::BreakType::Normal);
|
||||||
|
|
||||||
is_data_moddified = false;
|
is_data_moddified = false;
|
||||||
|
|
||||||
return ResultSuccess;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
Result NfcDevice::FlushWithBreak(NFP::BreakType break_type) {
|
Result NfcDevice::FlushWithBreak(NFP::BreakType break_type) {
|
||||||
|
|
|
@ -291,9 +291,13 @@ Common::Input::NfcState Joycons::SupportsNfc(const PadIdentifier& identifier_) c
|
||||||
return Common::Input::NfcState::Success;
|
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) {
|
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,
|
Common::Input::DriverResult Joycons::SetPollingMode(const PadIdentifier& identifier,
|
||||||
|
|
|
@ -492,6 +492,26 @@ DriverResult JoyconDriver::SetRingConMode() {
|
||||||
return result;
|
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 {
|
bool JoyconDriver::IsConnected() const {
|
||||||
std::scoped_lock lock{mutex};
|
std::scoped_lock lock{mutex};
|
||||||
return is_connected.load();
|
return is_connected.load();
|
||||||
|
|
|
@ -49,6 +49,7 @@ public:
|
||||||
DriverResult SetIrMode();
|
DriverResult SetIrMode();
|
||||||
DriverResult SetNfcMode();
|
DriverResult SetNfcMode();
|
||||||
DriverResult SetRingConMode();
|
DriverResult SetRingConMode();
|
||||||
|
DriverResult WriteNfcData(std::span<const u8> data);
|
||||||
|
|
||||||
void SetCallbacks(const JoyconCallbacks& callbacks);
|
void SetCallbacks(const JoyconCallbacks& callbacks);
|
||||||
|
|
||||||
|
|
|
@ -23,6 +23,7 @@ constexpr std::array<u8, 8> DefaultVibrationBuffer{0x0, 0x1, 0x40, 0x40, 0x0, 0x
|
||||||
|
|
||||||
using MacAddress = std::array<u8, 6>;
|
using MacAddress = std::array<u8, 6>;
|
||||||
using SerialNumber = std::array<u8, 15>;
|
using SerialNumber = std::array<u8, 15>;
|
||||||
|
using TagUUID = std::array<u8, 7>;
|
||||||
|
|
||||||
enum class ControllerType : u8 {
|
enum class ControllerType : u8 {
|
||||||
None = 0x00,
|
None = 0x00,
|
||||||
|
@ -276,12 +277,13 @@ enum class MCUPacketFlag : u8 {
|
||||||
LastCommandPacket = 0x08,
|
LastCommandPacket = 0x08,
|
||||||
};
|
};
|
||||||
|
|
||||||
enum class NFCReadCommand : u8 {
|
enum class NFCCommand : u8 {
|
||||||
CancelAll = 0x00,
|
CancelAll = 0x00,
|
||||||
StartPolling = 0x01,
|
StartPolling = 0x01,
|
||||||
StopPolling = 0x02,
|
StopPolling = 0x02,
|
||||||
StartWaitingRecieve = 0x04,
|
StartWaitingRecieve = 0x04,
|
||||||
Ntag = 0x06,
|
ReadNtag = 0x06,
|
||||||
|
WriteNtag = 0x08,
|
||||||
Mifare = 0x0F,
|
Mifare = 0x0F,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -292,14 +294,19 @@ enum class NFCTagType : u8 {
|
||||||
|
|
||||||
enum class NFCPages {
|
enum class NFCPages {
|
||||||
Block0 = 0,
|
Block0 = 0,
|
||||||
|
Block3 = 3,
|
||||||
Block45 = 45,
|
Block45 = 45,
|
||||||
Block135 = 135,
|
Block135 = 135,
|
||||||
Block231 = 231,
|
Block231 = 231,
|
||||||
};
|
};
|
||||||
|
|
||||||
enum class NFCStatus : u8 {
|
enum class NFCStatus : u8 {
|
||||||
|
Ready = 0x00,
|
||||||
|
Polling = 0x01,
|
||||||
LastPackage = 0x04,
|
LastPackage = 0x04,
|
||||||
|
WriteDone = 0x05,
|
||||||
TagLost = 0x07,
|
TagLost = 0x07,
|
||||||
|
WriteReady = 0x09,
|
||||||
};
|
};
|
||||||
|
|
||||||
enum class IrsMode : u8 {
|
enum class IrsMode : u8 {
|
||||||
|
@ -559,13 +566,32 @@ static_assert(sizeof(NFCReadBlockCommand) == 0x9, "NFCReadBlockCommand is an inv
|
||||||
struct NFCReadCommandData {
|
struct NFCReadCommandData {
|
||||||
u8 unknown;
|
u8 unknown;
|
||||||
u8 uuid_length;
|
u8 uuid_length;
|
||||||
u8 unknown_2;
|
TagUUID uid;
|
||||||
std::array<u8, 6> uid;
|
|
||||||
NFCTagType tag_type;
|
NFCTagType tag_type;
|
||||||
NFCReadBlockCommand read_block;
|
NFCReadBlockCommand read_block;
|
||||||
};
|
};
|
||||||
static_assert(sizeof(NFCReadCommandData) == 0x13, "NFCReadCommandData is an invalid size");
|
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 {
|
struct NFCPollingCommandData {
|
||||||
u8 enable_mifare;
|
u8 enable_mifare;
|
||||||
u8 unknown_1;
|
u8 unknown_1;
|
||||||
|
@ -576,8 +602,8 @@ struct NFCPollingCommandData {
|
||||||
static_assert(sizeof(NFCPollingCommandData) == 0x05, "NFCPollingCommandData is an invalid size");
|
static_assert(sizeof(NFCPollingCommandData) == 0x05, "NFCPollingCommandData is an invalid size");
|
||||||
|
|
||||||
struct NFCRequestState {
|
struct NFCRequestState {
|
||||||
NFCReadCommand command_argument;
|
NFCCommand command_argument;
|
||||||
INSERT_PADDING_BYTES(0x1);
|
u8 block_id;
|
||||||
u8 packet_id;
|
u8 packet_id;
|
||||||
MCUPacketFlag packet_flag;
|
MCUPacketFlag packet_flag;
|
||||||
u8 data_length;
|
u8 data_length;
|
||||||
|
@ -591,6 +617,18 @@ struct NFCRequestState {
|
||||||
};
|
};
|
||||||
static_assert(sizeof(NFCRequestState) == 0x26, "NFCRequestState is an invalid size");
|
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 {
|
struct IrsConfigure {
|
||||||
MCUCommand command;
|
MCUCommand command;
|
||||||
MCUSubCommand sub_command;
|
MCUSubCommand sub_command;
|
||||||
|
|
|
@ -34,6 +34,12 @@ DriverResult NfcProtocol::EnableNfc() {
|
||||||
|
|
||||||
result = ConfigureMCU(config);
|
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;
|
return result;
|
||||||
}
|
}
|
||||||
|
@ -56,27 +62,20 @@ DriverResult NfcProtocol::StartNFCPollingMode() {
|
||||||
LOG_DEBUG(Input, "Start NFC pooling Mode");
|
LOG_DEBUG(Input, "Start NFC pooling Mode");
|
||||||
ScopedSetBlocking sb(this);
|
ScopedSetBlocking sb(this);
|
||||||
DriverResult result{DriverResult::Success};
|
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) {
|
if (result == DriverResult::Success) {
|
||||||
MCUCommandResponse output{};
|
MCUCommandResponse output{};
|
||||||
result = SendStopPollingRequest(output);
|
result = SendStopPollingRequest(output);
|
||||||
}
|
}
|
||||||
if (result == DriverResult::Success) {
|
if (result == DriverResult::Success) {
|
||||||
result = WaitUntilNfcIsReady();
|
result = WaitUntilNfcIs(NFCStatus::Ready);
|
||||||
}
|
}
|
||||||
if (result == DriverResult::Success) {
|
if (result == DriverResult::Success) {
|
||||||
MCUCommandResponse output{};
|
MCUCommandResponse output{};
|
||||||
result = SendStartPollingRequest(output);
|
result = SendStartPollingRequest(output);
|
||||||
}
|
}
|
||||||
if (result == DriverResult::Success) {
|
if (result == DriverResult::Success) {
|
||||||
result = WaitUntilNfcIsPolling();
|
result = WaitUntilNfcIs(NFCStatus::Polling);
|
||||||
}
|
}
|
||||||
if (result == DriverResult::Success) {
|
if (result == DriverResult::Success) {
|
||||||
is_enabled = true;
|
is_enabled = true;
|
||||||
|
@ -112,6 +111,49 @@ DriverResult NfcProtocol::ScanAmiibo(std::vector<u8>& data) {
|
||||||
return result;
|
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() {
|
bool NfcProtocol::HasAmiibo() {
|
||||||
if (update_counter++ < AMIIBO_UPDATE_DELAY) {
|
if (update_counter++ < AMIIBO_UPDATE_DELAY) {
|
||||||
return true;
|
return true;
|
||||||
|
@ -129,7 +171,7 @@ bool NfcProtocol::HasAmiibo() {
|
||||||
return result == DriverResult::Success;
|
return result == DriverResult::Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
DriverResult NfcProtocol::WaitUntilNfcIsReady() {
|
DriverResult NfcProtocol::WaitUntilNfcIs(NFCStatus status) {
|
||||||
constexpr std::size_t timeout_limit = 10;
|
constexpr std::size_t timeout_limit = 10;
|
||||||
MCUCommandResponse output{};
|
MCUCommandResponse output{};
|
||||||
std::size_t tries = 0;
|
std::size_t tries = 0;
|
||||||
|
@ -145,28 +187,7 @@ DriverResult NfcProtocol::WaitUntilNfcIsReady() {
|
||||||
}
|
}
|
||||||
} while (output.mcu_report != MCUReport::NFCState ||
|
} while (output.mcu_report != MCUReport::NFCState ||
|
||||||
(output.mcu_data[1] << 8) + output.mcu_data[0] != 0x0500 ||
|
(output.mcu_data[1] << 8) + output.mcu_data[0] != 0x0500 ||
|
||||||
output.mcu_data[5] != 0x31 || output.mcu_data[6] != 0x00);
|
output.mcu_data[5] != 0x31 || output.mcu_data[6] != static_cast<u8>(status));
|
||||||
|
|
||||||
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);
|
|
||||||
|
|
||||||
return DriverResult::Success;
|
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));
|
(output.mcu_data[6] != 0x09 && output.mcu_data[6] != 0x04));
|
||||||
|
|
||||||
data.type = output.mcu_data[12];
|
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());
|
memcpy(data.uuid.data(), output.mcu_data.data() + 15, data.uuid.size());
|
||||||
|
|
||||||
return DriverResult::Success;
|
return DriverResult::Success;
|
||||||
|
@ -245,17 +266,94 @@ DriverResult NfcProtocol::GetAmiiboData(std::vector<u8>& ntag_data) {
|
||||||
return DriverResult::Timeout;
|
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{
|
NFCRequestState request{
|
||||||
.command_argument = NFCReadCommand::StartPolling,
|
.command_argument = NFCCommand::StartPolling,
|
||||||
.packet_id = 0x0,
|
.block_id = {},
|
||||||
|
.packet_id = {},
|
||||||
.packet_flag = MCUPacketFlag::LastCommandPacket,
|
.packet_flag = MCUPacketFlag::LastCommandPacket,
|
||||||
.data_length = sizeof(NFCPollingCommandData),
|
.data_length = sizeof(NFCPollingCommandData),
|
||||||
.nfc_polling =
|
.nfc_polling =
|
||||||
{
|
{
|
||||||
.enable_mifare = 0x01,
|
.enable_mifare = 0x00,
|
||||||
.unknown_1 = 0x00,
|
.unknown_1 = static_cast<u8>(is_second_attempt ? 0xe8 : 0x00),
|
||||||
.unknown_2 = 0x00,
|
.unknown_2 = static_cast<u8>(is_second_attempt ? 0x03 : 0x00),
|
||||||
.unknown_3 = 0x2c,
|
.unknown_3 = 0x2c,
|
||||||
.unknown_4 = 0x01,
|
.unknown_4 = 0x01,
|
||||||
},
|
},
|
||||||
|
@ -271,10 +369,11 @@ DriverResult NfcProtocol::SendStartPollingRequest(MCUCommandResponse& output) {
|
||||||
|
|
||||||
DriverResult NfcProtocol::SendStopPollingRequest(MCUCommandResponse& output) {
|
DriverResult NfcProtocol::SendStopPollingRequest(MCUCommandResponse& output) {
|
||||||
NFCRequestState request{
|
NFCRequestState request{
|
||||||
.command_argument = NFCReadCommand::StopPolling,
|
.command_argument = NFCCommand::StopPolling,
|
||||||
.packet_id = 0x0,
|
.block_id = {},
|
||||||
|
.packet_id = {},
|
||||||
.packet_flag = MCUPacketFlag::LastCommandPacket,
|
.packet_flag = MCUPacketFlag::LastCommandPacket,
|
||||||
.data_length = 0,
|
.data_length = {},
|
||||||
.raw_data = {},
|
.raw_data = {},
|
||||||
.crc = {},
|
.crc = {},
|
||||||
};
|
};
|
||||||
|
@ -288,10 +387,11 @@ DriverResult NfcProtocol::SendStopPollingRequest(MCUCommandResponse& output) {
|
||||||
|
|
||||||
DriverResult NfcProtocol::SendNextPackageRequest(MCUCommandResponse& output, u8 packet_id) {
|
DriverResult NfcProtocol::SendNextPackageRequest(MCUCommandResponse& output, u8 packet_id) {
|
||||||
NFCRequestState request{
|
NFCRequestState request{
|
||||||
.command_argument = NFCReadCommand::StartWaitingRecieve,
|
.command_argument = NFCCommand::StartWaitingRecieve,
|
||||||
|
.block_id = {},
|
||||||
.packet_id = packet_id,
|
.packet_id = packet_id,
|
||||||
.packet_flag = MCUPacketFlag::LastCommandPacket,
|
.packet_flag = MCUPacketFlag::LastCommandPacket,
|
||||||
.data_length = 0,
|
.data_length = {},
|
||||||
.raw_data = {},
|
.raw_data = {},
|
||||||
.crc = {},
|
.crc = {},
|
||||||
};
|
};
|
||||||
|
@ -305,17 +405,17 @@ DriverResult NfcProtocol::SendNextPackageRequest(MCUCommandResponse& output, u8
|
||||||
|
|
||||||
DriverResult NfcProtocol::SendReadAmiiboRequest(MCUCommandResponse& output, NFCPages ntag_pages) {
|
DriverResult NfcProtocol::SendReadAmiiboRequest(MCUCommandResponse& output, NFCPages ntag_pages) {
|
||||||
NFCRequestState request{
|
NFCRequestState request{
|
||||||
.command_argument = NFCReadCommand::Ntag,
|
.command_argument = NFCCommand::ReadNtag,
|
||||||
.packet_id = 0x0,
|
.block_id = {},
|
||||||
|
.packet_id = {},
|
||||||
.packet_flag = MCUPacketFlag::LastCommandPacket,
|
.packet_flag = MCUPacketFlag::LastCommandPacket,
|
||||||
.data_length = sizeof(NFCReadCommandData),
|
.data_length = sizeof(NFCReadCommandData),
|
||||||
.nfc_read =
|
.nfc_read =
|
||||||
{
|
{
|
||||||
.unknown = 0xd0,
|
.unknown = 0xd0,
|
||||||
.uuid_length = 0x07,
|
.uuid_length = sizeof(NFCReadCommandData::uid),
|
||||||
.unknown_2 = 0x00,
|
|
||||||
.uid = {},
|
.uid = {},
|
||||||
.tag_type = NFCTagType::AllTags,
|
.tag_type = NFCTagType::Ntag215,
|
||||||
.read_block = GetReadBlockCommand(ntag_pages),
|
.read_block = GetReadBlockCommand(ntag_pages),
|
||||||
},
|
},
|
||||||
.crc = {},
|
.crc = {},
|
||||||
|
@ -328,12 +428,135 @@ DriverResult NfcProtocol::SendReadAmiiboRequest(MCUCommandResponse& output, NFCP
|
||||||
output);
|
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 {
|
NFCReadBlockCommand NfcProtocol::GetReadBlockCommand(NFCPages pages) const {
|
||||||
switch (pages) {
|
switch (pages) {
|
||||||
case NFCPages::Block0:
|
case NFCPages::Block0:
|
||||||
return {
|
return {
|
||||||
.block_count = 1,
|
.block_count = 1,
|
||||||
};
|
};
|
||||||
|
case NFCPages::Block3:
|
||||||
|
return {
|
||||||
|
.block_count = 1,
|
||||||
|
.blocks =
|
||||||
|
{
|
||||||
|
NFCReadBlock{0x03, 0x03},
|
||||||
|
},
|
||||||
|
};
|
||||||
case NFCPages::Block45:
|
case NFCPages::Block45:
|
||||||
return {
|
return {
|
||||||
.block_count = 1,
|
.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 {
|
bool NfcProtocol::IsEnabled() const {
|
||||||
return is_enabled;
|
return is_enabled;
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,6 +27,8 @@ public:
|
||||||
|
|
||||||
DriverResult ScanAmiibo(std::vector<u8>& data);
|
DriverResult ScanAmiibo(std::vector<u8>& data);
|
||||||
|
|
||||||
|
DriverResult WriteAmiibo(std::span<const u8> data);
|
||||||
|
|
||||||
bool HasAmiibo();
|
bool HasAmiibo();
|
||||||
|
|
||||||
bool IsEnabled() const;
|
bool IsEnabled() const;
|
||||||
|
@ -37,18 +39,20 @@ private:
|
||||||
|
|
||||||
struct TagFoundData {
|
struct TagFoundData {
|
||||||
u8 type;
|
u8 type;
|
||||||
std::vector<u8> uuid;
|
u8 uuid_size;
|
||||||
|
TagUUID uuid;
|
||||||
};
|
};
|
||||||
|
|
||||||
DriverResult WaitUntilNfcIsReady();
|
DriverResult WaitUntilNfcIs(NFCStatus status);
|
||||||
|
|
||||||
DriverResult WaitUntilNfcIsPolling();
|
|
||||||
|
|
||||||
DriverResult IsTagInRange(TagFoundData& data, std::size_t timeout_limit = 1);
|
DriverResult IsTagInRange(TagFoundData& data, std::size_t timeout_limit = 1);
|
||||||
|
|
||||||
DriverResult GetAmiiboData(std::vector<u8>& data);
|
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);
|
DriverResult SendStopPollingRequest(MCUCommandResponse& output);
|
||||||
|
|
||||||
|
@ -56,8 +60,21 @@ private:
|
||||||
|
|
||||||
DriverResult SendReadAmiiboRequest(MCUCommandResponse& output, NFCPages ntag_pages);
|
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;
|
NFCReadBlockCommand GetReadBlockCommand(NFCPages pages) const;
|
||||||
|
|
||||||
|
TagUUID GetTagUUID(std::span<const u8> data) const;
|
||||||
|
|
||||||
bool is_enabled{};
|
bool is_enabled{};
|
||||||
std::size_t update_counter{};
|
std::size_t update_counter{};
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in New Issue