| | |
| | |
| |
|
| | #include <cstring> |
| | #include <fmt/format.h> |
| |
|
| | #include "common/fs/file.h" |
| | #include "common/fs/fs.h" |
| | #include "common/fs/path_util.h" |
| | #include "common/logging/log.h" |
| | #include "common/settings.h" |
| | #include "input_common/drivers/virtual_amiibo.h" |
| |
|
| | namespace InputCommon { |
| | constexpr PadIdentifier identifier = { |
| | .guid = Common::UUID{}, |
| | .port = 0, |
| | .pad = 0, |
| | }; |
| |
|
| | VirtualAmiibo::VirtualAmiibo(std::string input_engine_) : InputEngine(std::move(input_engine_)) {} |
| |
|
| | VirtualAmiibo::~VirtualAmiibo() = default; |
| |
|
| | Common::Input::DriverResult VirtualAmiibo::SetPollingMode( |
| | [[maybe_unused]] const PadIdentifier& identifier_, |
| | const Common::Input::PollingMode polling_mode_) { |
| | polling_mode = polling_mode_; |
| |
|
| | switch (polling_mode) { |
| | case Common::Input::PollingMode::NFC: |
| | state = State::Initialized; |
| | return Common::Input::DriverResult::Success; |
| | default: |
| | if (state == State::TagNearby) { |
| | CloseAmiibo(); |
| | } |
| | state = State::Disabled; |
| | return Common::Input::DriverResult::NotSupported; |
| | } |
| | } |
| |
|
| | 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) { |
| | const Common::FS::IOFile nfc_file{file_path, Common::FS::FileAccessMode::ReadWrite, |
| | Common::FS::FileType::BinaryFile}; |
| |
|
| | if (!nfc_file.IsOpen()) { |
| | LOG_ERROR(Core, "Amiibo is already on use"); |
| | return Common::Input::NfcState::WriteFailed; |
| | } |
| |
|
| | if (!nfc_file.Write(data)) { |
| | LOG_ERROR(Service_NFP, "Error writing to file"); |
| | return Common::Input::NfcState::WriteFailed; |
| | } |
| |
|
| | nfc_data = data; |
| |
|
| | 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; |
| | } |
| |
|
| | |
| | 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; |
| | } |
| |
|
| | |
| | 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; |
| | } |
| |
|
| | VirtualAmiibo::Info VirtualAmiibo::LoadAmiibo(const std::string& filename) { |
| | const Common::FS::IOFile nfc_file{filename, Common::FS::FileAccessMode::Read, |
| | Common::FS::FileType::BinaryFile}; |
| | std::vector<u8> data{}; |
| |
|
| | if (!nfc_file.IsOpen()) { |
| | return Info::UnableToLoad; |
| | } |
| |
|
| | switch (nfc_file.GetSize()) { |
| | case AmiiboSize: |
| | case AmiiboSizeWithoutPassword: |
| | case AmiiboSizeWithSignature: |
| | data.resize(AmiiboSize); |
| | if (nfc_file.Read(data) < AmiiboSizeWithoutPassword) { |
| | return Info::NotAnAmiibo; |
| | } |
| | break; |
| | case MifareSize: |
| | data.resize(MifareSize); |
| | if (nfc_file.Read(data) < MifareSize) { |
| | return Info::NotAnAmiibo; |
| | } |
| | break; |
| | default: |
| | return Info::NotAnAmiibo; |
| | } |
| |
|
| | file_path = filename; |
| | return LoadAmiibo(data); |
| | } |
| |
|
| | VirtualAmiibo::Info VirtualAmiibo::LoadAmiibo(std::span<u8> data) { |
| | if (state != State::WaitingForAmiibo) { |
| | return Info::WrongDeviceState; |
| | } |
| |
|
| | switch (data.size_bytes()) { |
| | case AmiiboSize: |
| | 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; |
| | } |
| |
|
| | status.uuid = {}; |
| | status.protocol = 1; |
| | state = State::TagNearby; |
| | status.state = Common::Input::NfcState::NewAmiibo, |
| | memcpy(nfc_data.data(), data.data(), data.size_bytes()); |
| | memcpy(status.uuid.data(), nfc_data.data(), status.uuid_length); |
| | SetNfc(identifier, status); |
| | return Info::Success; |
| | } |
| |
|
| | VirtualAmiibo::Info VirtualAmiibo::ReloadAmiibo() { |
| | if (state == State::TagNearby) { |
| | SetNfc(identifier, status); |
| | return Info::Success; |
| | } |
| |
|
| | return LoadAmiibo(file_path); |
| | } |
| |
|
| | VirtualAmiibo::Info VirtualAmiibo::CloseAmiibo() { |
| | 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; |
| | } |
| |
|
| | std::string VirtualAmiibo::GetLastFilePath() const { |
| | return file_path; |
| | } |
| |
|
| | } |
| |
|