input_common: Implement native mifare support
This commit is contained in:
parent
ec423c6919
commit
84d43489c5
25 changed files with 1170 additions and 198 deletions
|
@ -195,8 +195,8 @@ void Joycons::RegisterNewDevice(SDL_hid_device_info* device_info) {
|
|||
OnMotionUpdate(port, type, id, value);
|
||||
}},
|
||||
.on_ring_data = {[this](f32 ring_data) { OnRingConUpdate(ring_data); }},
|
||||
.on_amiibo_data = {[this, port, type](const std::vector<u8>& amiibo_data) {
|
||||
OnAmiiboUpdate(port, type, amiibo_data);
|
||||
.on_amiibo_data = {[this, port, type](const Joycon::TagInfo& tag_info) {
|
||||
OnAmiiboUpdate(port, type, tag_info);
|
||||
}},
|
||||
.on_camera_data = {[this, port](const std::vector<u8>& camera_data,
|
||||
Joycon::IrsResolution format) {
|
||||
|
@ -291,13 +291,105 @@ Common::Input::NfcState Joycons::SupportsNfc(const PadIdentifier& identifier_) c
|
|||
return Common::Input::NfcState::Success;
|
||||
};
|
||||
|
||||
Common::Input::NfcState Joycons::StartNfcPolling(const PadIdentifier& identifier) {
|
||||
auto handle = GetHandle(identifier);
|
||||
if (handle == nullptr) {
|
||||
return Common::Input::NfcState::Unknown;
|
||||
}
|
||||
return TranslateDriverResult(handle->StartNfcPolling());
|
||||
};
|
||||
|
||||
Common::Input::NfcState Joycons::StopNfcPolling(const PadIdentifier& identifier) {
|
||||
auto handle = GetHandle(identifier);
|
||||
if (handle == nullptr) {
|
||||
return Common::Input::NfcState::Unknown;
|
||||
}
|
||||
return TranslateDriverResult(handle->StopNfcPolling());
|
||||
};
|
||||
|
||||
Common::Input::NfcState Joycons::ReadAmiiboData(const PadIdentifier& identifier,
|
||||
std::vector<u8>& out_data) {
|
||||
auto handle = GetHandle(identifier);
|
||||
if (handle == nullptr) {
|
||||
return Common::Input::NfcState::Unknown;
|
||||
}
|
||||
return TranslateDriverResult(handle->ReadAmiiboData(out_data));
|
||||
}
|
||||
|
||||
Common::Input::NfcState Joycons::WriteNfcData(const PadIdentifier& identifier,
|
||||
const std::vector<u8>& data) {
|
||||
auto handle = GetHandle(identifier);
|
||||
if (handle->WriteNfcData(data) != Joycon::DriverResult::Success) {
|
||||
return Common::Input::NfcState::WriteFailed;
|
||||
if (handle == nullptr) {
|
||||
return Common::Input::NfcState::Unknown;
|
||||
}
|
||||
return Common::Input::NfcState::Success;
|
||||
return TranslateDriverResult(handle->WriteNfcData(data));
|
||||
};
|
||||
|
||||
Common::Input::NfcState Joycons::ReadMifareData(const PadIdentifier& identifier,
|
||||
const Common::Input::MifareRequest& request,
|
||||
Common::Input::MifareRequest& data) {
|
||||
auto handle = GetHandle(identifier);
|
||||
if (handle == nullptr) {
|
||||
return Common::Input::NfcState::Unknown;
|
||||
}
|
||||
|
||||
const auto command = static_cast<Joycon::MifareCmd>(request.data[0].command);
|
||||
std::vector<Joycon::MifareReadChunk> read_request{};
|
||||
for (const auto& request_data : request.data) {
|
||||
if (request_data.command == 0) {
|
||||
continue;
|
||||
}
|
||||
Joycon::MifareReadChunk chunk = {
|
||||
.command = command,
|
||||
.sector_key = {},
|
||||
.sector = request_data.sector,
|
||||
};
|
||||
memcpy(chunk.sector_key.data(), request_data.key.data(),
|
||||
sizeof(Joycon::MifareReadChunk::sector_key));
|
||||
read_request.emplace_back(chunk);
|
||||
}
|
||||
|
||||
std::vector<Joycon::MifareReadData> read_data(read_request.size());
|
||||
const auto result = handle->ReadMifareData(read_request, read_data);
|
||||
if (result == Joycon::DriverResult::Success) {
|
||||
for (std::size_t i = 0; i < read_request.size(); i++) {
|
||||
data.data[i] = {
|
||||
.command = static_cast<u8>(command),
|
||||
.sector = read_data[i].sector,
|
||||
.key = {},
|
||||
.data = read_data[i].data,
|
||||
};
|
||||
}
|
||||
}
|
||||
return TranslateDriverResult(result);
|
||||
};
|
||||
|
||||
Common::Input::NfcState Joycons::WriteMifareData(const PadIdentifier& identifier,
|
||||
const Common::Input::MifareRequest& request) {
|
||||
auto handle = GetHandle(identifier);
|
||||
if (handle == nullptr) {
|
||||
return Common::Input::NfcState::Unknown;
|
||||
}
|
||||
|
||||
const auto command = static_cast<Joycon::MifareCmd>(request.data[0].command);
|
||||
std::vector<Joycon::MifareWriteChunk> write_request{};
|
||||
for (const auto& request_data : request.data) {
|
||||
if (request_data.command == 0) {
|
||||
continue;
|
||||
}
|
||||
Joycon::MifareWriteChunk chunk = {
|
||||
.command = command,
|
||||
.sector_key = {},
|
||||
.sector = request_data.sector,
|
||||
.data = {},
|
||||
};
|
||||
memcpy(chunk.sector_key.data(), request_data.key.data(),
|
||||
sizeof(Joycon::MifareReadChunk::sector_key));
|
||||
memcpy(chunk.data.data(), request_data.data.data(), sizeof(Joycon::MifareWriteChunk::data));
|
||||
write_request.emplace_back(chunk);
|
||||
}
|
||||
|
||||
return TranslateDriverResult(handle->WriteMifareData(write_request));
|
||||
};
|
||||
|
||||
Common::Input::DriverResult Joycons::SetPollingMode(const PadIdentifier& identifier,
|
||||
|
@ -403,11 +495,20 @@ void Joycons::OnRingConUpdate(f32 ring_data) {
|
|||
}
|
||||
|
||||
void Joycons::OnAmiiboUpdate(std::size_t port, Joycon::ControllerType type,
|
||||
const std::vector<u8>& amiibo_data) {
|
||||
const Joycon::TagInfo& tag_info) {
|
||||
const auto identifier = GetIdentifier(port, type);
|
||||
const auto nfc_state = amiibo_data.empty() ? Common::Input::NfcState::AmiiboRemoved
|
||||
: Common::Input::NfcState::NewAmiibo;
|
||||
SetNfc(identifier, {nfc_state, amiibo_data});
|
||||
const auto nfc_state = tag_info.uuid_length == 0 ? Common::Input::NfcState::AmiiboRemoved
|
||||
: Common::Input::NfcState::NewAmiibo;
|
||||
|
||||
const Common::Input::NfcStatus nfc_status{
|
||||
.state = nfc_state,
|
||||
.uuid_length = tag_info.uuid_length,
|
||||
.protocol = tag_info.protocol,
|
||||
.tag_type = tag_info.tag_type,
|
||||
.uuid = tag_info.uuid,
|
||||
};
|
||||
|
||||
SetNfc(identifier, nfc_status);
|
||||
}
|
||||
|
||||
void Joycons::OnCameraUpdate(std::size_t port, const std::vector<u8>& camera_data,
|
||||
|
@ -726,4 +827,18 @@ std::string Joycons::JoyconName(Joycon::ControllerType type) const {
|
|||
return "Unknown Switch Controller";
|
||||
}
|
||||
}
|
||||
|
||||
Common::Input::NfcState Joycons::TranslateDriverResult(Joycon::DriverResult result) const {
|
||||
switch (result) {
|
||||
case Joycon::DriverResult::Success:
|
||||
return Common::Input::NfcState::Success;
|
||||
case Joycon::DriverResult::Disabled:
|
||||
return Common::Input::NfcState::WrongDeviceState;
|
||||
case Joycon::DriverResult::NotSupported:
|
||||
return Common::Input::NfcState::NotSupported;
|
||||
default:
|
||||
return Common::Input::NfcState::Unknown;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace InputCommon
|
||||
|
|
|
@ -15,6 +15,7 @@ using SerialNumber = std::array<u8, 15>;
|
|||
struct Battery;
|
||||
struct Color;
|
||||
struct MotionData;
|
||||
struct TagInfo;
|
||||
enum class ControllerType : u8;
|
||||
enum class DriverResult;
|
||||
enum class IrsResolution;
|
||||
|
@ -39,9 +40,18 @@ public:
|
|||
Common::Input::DriverResult SetCameraFormat(const PadIdentifier& identifier,
|
||||
Common::Input::CameraFormat camera_format) override;
|
||||
|
||||
Common::Input::NfcState SupportsNfc(const PadIdentifier& identifier_) const override;
|
||||
Common::Input::NfcState WriteNfcData(const PadIdentifier& identifier_,
|
||||
Common::Input::NfcState SupportsNfc(const PadIdentifier& identifier) const override;
|
||||
Common::Input::NfcState StartNfcPolling(const PadIdentifier& identifier) override;
|
||||
Common::Input::NfcState StopNfcPolling(const PadIdentifier& identifier) override;
|
||||
Common::Input::NfcState ReadAmiiboData(const PadIdentifier& identifier,
|
||||
std::vector<u8>& out_data) override;
|
||||
Common::Input::NfcState WriteNfcData(const PadIdentifier& identifier,
|
||||
const std::vector<u8>& data) override;
|
||||
Common::Input::NfcState ReadMifareData(const PadIdentifier& identifier,
|
||||
const Common::Input::MifareRequest& request,
|
||||
Common::Input::MifareRequest& out_data) override;
|
||||
Common::Input::NfcState WriteMifareData(const PadIdentifier& identifier,
|
||||
const Common::Input::MifareRequest& request) override;
|
||||
|
||||
Common::Input::DriverResult SetPollingMode(
|
||||
const PadIdentifier& identifier, const Common::Input::PollingMode polling_mode) override;
|
||||
|
@ -82,7 +92,7 @@ private:
|
|||
const Joycon::MotionData& value);
|
||||
void OnRingConUpdate(f32 ring_data);
|
||||
void OnAmiiboUpdate(std::size_t port, Joycon::ControllerType type,
|
||||
const std::vector<u8>& amiibo_data);
|
||||
const Joycon::TagInfo& amiibo_data);
|
||||
void OnCameraUpdate(std::size_t port, const std::vector<u8>& camera_data,
|
||||
Joycon::IrsResolution format);
|
||||
|
||||
|
@ -102,6 +112,8 @@ private:
|
|||
/// Returns the name of the device in text format
|
||||
std::string JoyconName(Joycon::ControllerType type) const;
|
||||
|
||||
Common::Input::NfcState TranslateDriverResult(Joycon::DriverResult result) const;
|
||||
|
||||
std::jthread scan_thread;
|
||||
|
||||
// Joycon types are split by type to ease supporting dualjoycon configurations
|
||||
|
|
|
@ -29,14 +29,13 @@ Common::Input::DriverResult VirtualAmiibo::SetPollingMode(
|
|||
|
||||
switch (polling_mode) {
|
||||
case Common::Input::PollingMode::NFC:
|
||||
if (state == State::Initialized) {
|
||||
state = State::WaitingForAmiibo;
|
||||
}
|
||||
state = State::Initialized;
|
||||
return Common::Input::DriverResult::Success;
|
||||
default:
|
||||
if (state == State::AmiiboIsOpen) {
|
||||
if (state == State::TagNearby) {
|
||||
CloseAmiibo();
|
||||
}
|
||||
state = State::Disabled;
|
||||
return Common::Input::DriverResult::NotSupported;
|
||||
}
|
||||
}
|
||||
|
@ -45,6 +44,39 @@ Common::Input::NfcState VirtualAmiibo::SupportsNfc(
|
|||
[[maybe_unused]] const PadIdentifier& identifier_) const {
|
||||
return Common::Input::NfcState::Success;
|
||||
}
|
||||
Common::Input::NfcState VirtualAmiibo::StartNfcPolling(const PadIdentifier& identifier_) {
|
||||
if (state != State::Initialized) {
|
||||
return Common::Input::NfcState::WrongDeviceState;
|
||||
}
|
||||
state = State::WaitingForAmiibo;
|
||||
return Common::Input::NfcState::Success;
|
||||
}
|
||||
|
||||
Common::Input::NfcState VirtualAmiibo::StopNfcPolling(const PadIdentifier& identifier_) {
|
||||
if (state == State::Disabled) {
|
||||
return Common::Input::NfcState::WrongDeviceState;
|
||||
}
|
||||
if (state == State::TagNearby) {
|
||||
CloseAmiibo();
|
||||
}
|
||||
state = State::Initialized;
|
||||
return Common::Input::NfcState::Success;
|
||||
}
|
||||
|
||||
Common::Input::NfcState VirtualAmiibo::ReadAmiiboData(const PadIdentifier& identifier_,
|
||||
std::vector<u8>& out_data) {
|
||||
if (state != State::TagNearby) {
|
||||
return Common::Input::NfcState::WrongDeviceState;
|
||||
}
|
||||
|
||||
if (status.tag_type != 1U << 1) {
|
||||
return Common::Input::NfcState::InvalidTagType;
|
||||
}
|
||||
|
||||
out_data.resize(nfc_data.size());
|
||||
memcpy(out_data.data(), nfc_data.data(), nfc_data.size());
|
||||
return Common::Input::NfcState::Success;
|
||||
}
|
||||
|
||||
Common::Input::NfcState VirtualAmiibo::WriteNfcData(
|
||||
[[maybe_unused]] const PadIdentifier& identifier_, const std::vector<u8>& data) {
|
||||
|
@ -66,6 +98,69 @@ Common::Input::NfcState VirtualAmiibo::WriteNfcData(
|
|||
return Common::Input::NfcState::Success;
|
||||
}
|
||||
|
||||
Common::Input::NfcState VirtualAmiibo::ReadMifareData(const PadIdentifier& identifier_,
|
||||
const Common::Input::MifareRequest& request,
|
||||
Common::Input::MifareRequest& out_data) {
|
||||
if (state != State::TagNearby) {
|
||||
return Common::Input::NfcState::WrongDeviceState;
|
||||
}
|
||||
|
||||
if (status.tag_type != 1U << 6) {
|
||||
return Common::Input::NfcState::InvalidTagType;
|
||||
}
|
||||
|
||||
for (std::size_t i = 0; i < request.data.size(); i++) {
|
||||
if (request.data[i].command == 0) {
|
||||
continue;
|
||||
}
|
||||
out_data.data[i].command = request.data[i].command;
|
||||
out_data.data[i].sector = request.data[i].sector;
|
||||
|
||||
const std::size_t sector_index =
|
||||
request.data[i].sector * sizeof(Common::Input::MifareData::data);
|
||||
|
||||
if (nfc_data.size() < sector_index + sizeof(Common::Input::MifareData::data)) {
|
||||
return Common::Input::NfcState::WriteFailed;
|
||||
}
|
||||
|
||||
// Ignore the sector key as we don't support it
|
||||
memcpy(out_data.data[i].data.data(), nfc_data.data() + sector_index,
|
||||
sizeof(Common::Input::MifareData::data));
|
||||
}
|
||||
|
||||
return Common::Input::NfcState::Success;
|
||||
}
|
||||
|
||||
Common::Input::NfcState VirtualAmiibo::WriteMifareData(
|
||||
const PadIdentifier& identifier_, const Common::Input::MifareRequest& request) {
|
||||
if (state != State::TagNearby) {
|
||||
return Common::Input::NfcState::WrongDeviceState;
|
||||
}
|
||||
|
||||
if (status.tag_type != 1U << 6) {
|
||||
return Common::Input::NfcState::InvalidTagType;
|
||||
}
|
||||
|
||||
for (std::size_t i = 0; i < request.data.size(); i++) {
|
||||
if (request.data[i].command == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const std::size_t sector_index =
|
||||
request.data[i].sector * sizeof(Common::Input::MifareData::data);
|
||||
|
||||
if (nfc_data.size() < sector_index + sizeof(Common::Input::MifareData::data)) {
|
||||
return Common::Input::NfcState::WriteFailed;
|
||||
}
|
||||
|
||||
// Ignore the sector key as we don't support it
|
||||
memcpy(nfc_data.data() + sector_index, request.data[i].data.data(),
|
||||
sizeof(Common::Input::MifareData::data));
|
||||
}
|
||||
|
||||
return Common::Input::NfcState::Success;
|
||||
}
|
||||
|
||||
VirtualAmiibo::State VirtualAmiibo::GetCurrentState() const {
|
||||
return state;
|
||||
}
|
||||
|
@ -112,23 +207,31 @@ VirtualAmiibo::Info VirtualAmiibo::LoadAmiibo(std::span<u8> data) {
|
|||
case AmiiboSizeWithoutPassword:
|
||||
case AmiiboSizeWithSignature:
|
||||
nfc_data.resize(AmiiboSize);
|
||||
status.tag_type = 1U << 1;
|
||||
status.uuid_length = 7;
|
||||
break;
|
||||
case MifareSize:
|
||||
nfc_data.resize(MifareSize);
|
||||
status.tag_type = 1U << 6;
|
||||
status.uuid_length = 4;
|
||||
break;
|
||||
default:
|
||||
return Info::NotAnAmiibo;
|
||||
}
|
||||
|
||||
state = State::AmiiboIsOpen;
|
||||
status.uuid = {};
|
||||
status.protocol = 1;
|
||||
state = State::TagNearby;
|
||||
status.state = Common::Input::NfcState::NewAmiibo,
|
||||
memcpy(nfc_data.data(), data.data(), data.size_bytes());
|
||||
SetNfc(identifier, {Common::Input::NfcState::NewAmiibo, nfc_data});
|
||||
memcpy(status.uuid.data(), nfc_data.data(), status.uuid_length);
|
||||
SetNfc(identifier, status);
|
||||
return Info::Success;
|
||||
}
|
||||
|
||||
VirtualAmiibo::Info VirtualAmiibo::ReloadAmiibo() {
|
||||
if (state == State::AmiiboIsOpen) {
|
||||
SetNfc(identifier, {Common::Input::NfcState::NewAmiibo, nfc_data});
|
||||
if (state == State::TagNearby) {
|
||||
SetNfc(identifier, status);
|
||||
return Info::Success;
|
||||
}
|
||||
|
||||
|
@ -136,9 +239,14 @@ VirtualAmiibo::Info VirtualAmiibo::ReloadAmiibo() {
|
|||
}
|
||||
|
||||
VirtualAmiibo::Info VirtualAmiibo::CloseAmiibo() {
|
||||
state = polling_mode == Common::Input::PollingMode::NFC ? State::WaitingForAmiibo
|
||||
: State::Initialized;
|
||||
SetNfc(identifier, {Common::Input::NfcState::AmiiboRemoved, {}});
|
||||
if (state != State::TagNearby) {
|
||||
return Info::Success;
|
||||
}
|
||||
|
||||
state = State::WaitingForAmiibo;
|
||||
status.state = Common::Input::NfcState::AmiiboRemoved;
|
||||
SetNfc(identifier, status);
|
||||
status.tag_type = 0;
|
||||
return Info::Success;
|
||||
}
|
||||
|
||||
|
|
|
@ -20,9 +20,10 @@ namespace InputCommon {
|
|||
class VirtualAmiibo final : public InputEngine {
|
||||
public:
|
||||
enum class State {
|
||||
Disabled,
|
||||
Initialized,
|
||||
WaitingForAmiibo,
|
||||
AmiiboIsOpen,
|
||||
TagNearby,
|
||||
};
|
||||
|
||||
enum class Info {
|
||||
|
@ -41,9 +42,17 @@ public:
|
|||
const PadIdentifier& identifier_, const Common::Input::PollingMode polling_mode_) override;
|
||||
|
||||
Common::Input::NfcState SupportsNfc(const PadIdentifier& identifier_) const override;
|
||||
|
||||
Common::Input::NfcState StartNfcPolling(const PadIdentifier& identifier_) override;
|
||||
Common::Input::NfcState StopNfcPolling(const PadIdentifier& identifier_) override;
|
||||
Common::Input::NfcState ReadAmiiboData(const PadIdentifier& identifier_,
|
||||
std::vector<u8>& out_data) override;
|
||||
Common::Input::NfcState WriteNfcData(const PadIdentifier& identifier_,
|
||||
const std::vector<u8>& data) override;
|
||||
Common::Input::NfcState ReadMifareData(const PadIdentifier& identifier_,
|
||||
const Common::Input::MifareRequest& data,
|
||||
Common::Input::MifareRequest& out_data) override;
|
||||
Common::Input::NfcState WriteMifareData(const PadIdentifier& identifier_,
|
||||
const Common::Input::MifareRequest& data) override;
|
||||
|
||||
State GetCurrentState() const;
|
||||
|
||||
|
@ -61,8 +70,9 @@ private:
|
|||
static constexpr std::size_t MifareSize = 0x400;
|
||||
|
||||
std::string file_path{};
|
||||
State state{State::Initialized};
|
||||
State state{State::Disabled};
|
||||
std::vector<u8> nfc_data;
|
||||
Common::Input::NfcStatus status;
|
||||
Common::Input::PollingMode polling_mode{Common::Input::PollingMode::Passive};
|
||||
};
|
||||
} // namespace InputCommon
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue