| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492 |
- // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
- // SPDX-License-Identifier: GPL-2.0-or-later
- #ifdef _MSC_VER
- #pragma warning(push)
- #pragma warning(disable : 4701) // Potentially uninitialized local variable 'result' used
- #endif
- #include <boost/crc.hpp>
- #ifdef _MSC_VER
- #pragma warning(pop)
- #endif
- #include <fmt/format.h>
- #include "common/fs/file.h"
- #include "common/fs/fs.h"
- #include "common/fs/path_util.h"
- #include "common/input.h"
- #include "common/logging/log.h"
- #include "common/string_util.h"
- #include "common/tiny_mt.h"
- #include "core/core.h"
- #include "core/hid/emulated_controller.h"
- #include "core/hid/hid_core.h"
- #include "core/hid/hid_types.h"
- #include "core/hle/kernel/k_event.h"
- #include "core/hle/service/ipc_helpers.h"
- #include "core/hle/service/mii/mii_manager.h"
- #include "core/hle/service/mii/types.h"
- #include "core/hle/service/nfc/common/amiibo_crypto.h"
- #include "core/hle/service/nfc/common/device.h"
- #include "core/hle/service/nfc/mifare_result.h"
- #include "core/hle/service/nfc/nfc_result.h"
- #include "core/hle/service/time/time_manager.h"
- namespace Service::NFC {
- NfcDevice::NfcDevice(Core::HID::NpadIdType npad_id_, Core::System& system_,
- KernelHelpers::ServiceContext& service_context_,
- Kernel::KEvent* availability_change_event_)
- : npad_id{npad_id_}, system{system_}, service_context{service_context_},
- availability_change_event{availability_change_event_} {
- activate_event = service_context.CreateEvent("NFC:ActivateEvent");
- deactivate_event = service_context.CreateEvent("NFC:DeactivateEvent");
- npad_device = system.HIDCore().GetEmulatedController(npad_id);
- Core::HID::ControllerUpdateCallback engine_callback{
- .on_change = [this](Core::HID::ControllerTriggerType type) { NpadUpdate(type); },
- .is_npad_service = false,
- };
- is_controller_set = true;
- callback_key = npad_device->SetCallback(engine_callback);
- }
- NfcDevice::~NfcDevice() {
- service_context.CloseEvent(activate_event);
- service_context.CloseEvent(deactivate_event);
- if (!is_controller_set) {
- return;
- }
- npad_device->DeleteCallback(callback_key);
- is_controller_set = false;
- };
- void NfcDevice::NpadUpdate(Core::HID::ControllerTriggerType type) {
- if (type == Core::HID::ControllerTriggerType::Connected) {
- Initialize();
- availability_change_event->Signal();
- return;
- }
- if (type == Core::HID::ControllerTriggerType::Disconnected) {
- Finalize();
- availability_change_event->Signal();
- return;
- }
- if (!is_initalized) {
- return;
- }
- if (!npad_device->IsConnected()) {
- return;
- }
- // Ensure nfc mode is always active
- if (npad_device->GetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex) ==
- Common::Input::PollingMode::Active) {
- npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex,
- Common::Input::PollingMode::NFC);
- }
- if (type != Core::HID::ControllerTriggerType::Nfc) {
- return;
- }
- const auto nfc_status = npad_device->GetNfc();
- switch (nfc_status.state) {
- case Common::Input::NfcState::NewAmiibo:
- LoadNfcTag(nfc_status.protocol, nfc_status.tag_type, nfc_status.uuid_length,
- nfc_status.uuid);
- break;
- case Common::Input::NfcState::AmiiboRemoved:
- if (device_state == DeviceState::Initialized || device_state == DeviceState::TagRemoved) {
- break;
- }
- if (device_state != DeviceState::SearchingForTag) {
- CloseNfcTag();
- }
- break;
- default:
- break;
- }
- }
- bool NfcDevice::LoadNfcTag(u8 protocol, u8 tag_type, u8 uuid_length, UniqueSerialNumber uuid) {
- if (device_state != DeviceState::SearchingForTag) {
- LOG_ERROR(Service_NFC, "Game is not looking for nfc tag, current state {}", device_state);
- return false;
- }
- if ((protocol & static_cast<u8>(allowed_protocols)) == 0) {
- LOG_ERROR(Service_NFC, "Protocol not supported {}", protocol);
- return false;
- }
- real_tag_info = {
- .uuid = uuid,
- .uuid_length = uuid_length,
- .protocol = static_cast<NfcProtocol>(protocol),
- .tag_type = static_cast<TagType>(tag_type),
- };
- device_state = DeviceState::TagFound;
- deactivate_event->GetReadableEvent().Clear();
- activate_event->Signal();
- return true;
- }
- bool NfcDevice::LoadAmiiboData() {
- std::vector<u8> data{};
- if (!npad_device->ReadAmiiboData(data)) {
- return false;
- }
- if (data.size() < sizeof(NFP::EncryptedNTAG215File)) {
- LOG_ERROR(Service_NFC, "Not an amiibo, size={}", data.size());
- return false;
- }
- memcpy(&tag_data, data.data(), sizeof(NFP::EncryptedNTAG215File));
- is_plain_amiibo = NFP::AmiiboCrypto::IsAmiiboValid(tag_data);
- is_write_protected = false;
- // Fallback for plain amiibos
- if (is_plain_amiibo) {
- LOG_INFO(Service_NFP, "Using plain amiibo");
- encrypted_tag_data = NFP::AmiiboCrypto::EncodedDataToNfcData(tag_data);
- return true;
- }
- // Fallback for encrypted amiibos without keys
- if (!NFP::AmiiboCrypto::IsKeyAvailable()) {
- LOG_INFO(Service_NFC, "Loading amiibo without keys");
- memcpy(&encrypted_tag_data, data.data(), sizeof(NFP::EncryptedNTAG215File));
- BuildAmiiboWithoutKeys(tag_data, encrypted_tag_data);
- is_plain_amiibo = true;
- is_write_protected = true;
- return true;
- }
- LOG_INFO(Service_NFP, "Using encrypted amiibo");
- tag_data = {};
- memcpy(&encrypted_tag_data, data.data(), sizeof(NFP::EncryptedNTAG215File));
- return true;
- }
- void NfcDevice::CloseNfcTag() {
- LOG_INFO(Service_NFC, "Remove nfc tag");
- if (device_state == DeviceState::TagMounted) {
- Unmount();
- }
- device_state = DeviceState::TagRemoved;
- encrypted_tag_data = {};
- tag_data = {};
- activate_event->GetReadableEvent().Clear();
- deactivate_event->Signal();
- }
- Kernel::KReadableEvent& NfcDevice::GetActivateEvent() const {
- return activate_event->GetReadableEvent();
- }
- Kernel::KReadableEvent& NfcDevice::GetDeactivateEvent() const {
- return deactivate_event->GetReadableEvent();
- }
- void NfcDevice::Initialize() {
- device_state = npad_device->HasNfc() ? DeviceState::Initialized : DeviceState::Unavailable;
- encrypted_tag_data = {};
- tag_data = {};
- if (device_state != DeviceState::Initialized) {
- return;
- }
- is_initalized = npad_device->AddNfcHandle();
- }
- void NfcDevice::Finalize() {
- if (npad_device->IsConnected()) {
- if (device_state == DeviceState::TagMounted) {
- Unmount();
- }
- if (device_state == DeviceState::SearchingForTag ||
- device_state == DeviceState::TagRemoved) {
- StopDetection();
- }
- }
- if (device_state != DeviceState::Unavailable) {
- npad_device->RemoveNfcHandle();
- }
- device_state = DeviceState::Unavailable;
- is_initalized = false;
- }
- Result NfcDevice::StartDetection(NfcProtocol allowed_protocol) {
- if (device_state != DeviceState::Initialized && device_state != DeviceState::TagRemoved) {
- LOG_ERROR(Service_NFC, "Wrong device state {}", device_state);
- return ResultWrongDeviceState;
- }
- if (!npad_device->StartNfcPolling()) {
- LOG_ERROR(Service_NFC, "Nfc polling not supported");
- return ResultNfcDisabled;
- }
- device_state = DeviceState::SearchingForTag;
- allowed_protocols = allowed_protocol;
- return ResultSuccess;
- }
- Result NfcDevice::StopDetection() {
- if (device_state == DeviceState::Initialized) {
- return ResultSuccess;
- }
- if (device_state == DeviceState::TagFound || device_state == DeviceState::TagMounted) {
- CloseNfcTag();
- }
- if (device_state == DeviceState::SearchingForTag || device_state == DeviceState::TagRemoved) {
- npad_device->StopNfcPolling();
- device_state = DeviceState::Initialized;
- return ResultSuccess;
- }
- LOG_ERROR(Service_NFC, "Wrong device state {}", device_state);
- return ResultWrongDeviceState;
- }
- Result NfcDevice::GetTagInfo(NFP::TagInfo& tag_info) const {
- if (device_state != DeviceState::TagFound && device_state != DeviceState::TagMounted) {
- LOG_ERROR(Service_NFC, "Wrong device state {}", device_state);
- if (device_state == DeviceState::TagRemoved) {
- return ResultTagRemoved;
- }
- return ResultWrongDeviceState;
- }
- tag_info = real_tag_info;
- // Generate random UUID to bypass amiibo load limits
- if (real_tag_info.tag_type == TagType::Type2 && Settings::values.random_amiibo_id) {
- Common::TinyMT rng{};
- rng.Initialize(static_cast<u32>(GetCurrentPosixTime()));
- rng.GenerateRandomBytes(tag_info.uuid.data(), tag_info.uuid_length);
- }
- return ResultSuccess;
- }
- Result NfcDevice::ReadMifare(std::span<const MifareReadBlockParameter> parameters,
- std::span<MifareReadBlockData> read_block_data) const {
- if (device_state != DeviceState::TagFound && device_state != DeviceState::TagMounted) {
- LOG_ERROR(Service_NFC, "Wrong device state {}", device_state);
- if (device_state == DeviceState::TagRemoved) {
- return ResultTagRemoved;
- }
- return ResultWrongDeviceState;
- }
- Result result = ResultSuccess;
- TagInfo tag_info{};
- result = GetTagInfo(tag_info);
- if (result.IsError()) {
- return result;
- }
- if (tag_info.protocol != NfcProtocol::TypeA || tag_info.tag_type != TagType::Mifare) {
- return ResultInvalidTagType;
- }
- if (parameters.size() == 0) {
- return ResultInvalidArgument;
- }
- Common::Input::MifareRequest request{};
- Common::Input::MifareRequest out_data{};
- const auto unknown = parameters[0].sector_key.unknown;
- for (std::size_t i = 0; i < parameters.size(); i++) {
- if (unknown != parameters[i].sector_key.unknown) {
- return ResultInvalidArgument;
- }
- }
- for (std::size_t i = 0; i < parameters.size(); i++) {
- if (parameters[i].sector_key.command == MifareCmd::None) {
- continue;
- }
- request.data[i].command = static_cast<u8>(parameters[i].sector_key.command);
- request.data[i].sector = parameters[i].sector_number;
- memcpy(request.data[i].key.data(), parameters[i].sector_key.sector_key.data(),
- sizeof(KeyData));
- }
- if (!npad_device->ReadMifareData(request, out_data)) {
- return ResultMifareError288;
- }
- for (std::size_t i = 0; i < read_block_data.size(); i++) {
- if (static_cast<MifareCmd>(out_data.data[i].command) == MifareCmd::None) {
- continue;
- }
- read_block_data[i] = {
- .data = out_data.data[i].data,
- .sector_number = out_data.data[i].sector,
- };
- }
- return ResultSuccess;
- }
- Result NfcDevice::WriteMifare(std::span<const MifareWriteBlockParameter> parameters) {
- Result result = ResultSuccess;
- TagInfo tag_info{};
- result = GetTagInfo(tag_info);
- if (result.IsError()) {
- return result;
- }
- if (tag_info.protocol != NfcProtocol::TypeA || tag_info.tag_type != TagType::Mifare) {
- return ResultInvalidTagType;
- }
- if (parameters.size() == 0) {
- return ResultInvalidArgument;
- }
- const auto unknown = parameters[0].sector_key.unknown;
- for (std::size_t i = 0; i < parameters.size(); i++) {
- if (unknown != parameters[i].sector_key.unknown) {
- return ResultInvalidArgument;
- }
- }
- Common::Input::MifareRequest request{};
- for (std::size_t i = 0; i < parameters.size(); i++) {
- if (parameters[i].sector_key.command == MifareCmd::None) {
- continue;
- }
- request.data[i].command = static_cast<u8>(parameters[i].sector_key.command);
- request.data[i].sector = parameters[i].sector_number;
- memcpy(request.data[i].key.data(), parameters[i].sector_key.sector_key.data(),
- sizeof(KeyData));
- memcpy(request.data[i].data.data(), parameters[i].data.data(), sizeof(KeyData));
- }
- if (!npad_device->WriteMifareData(request)) {
- return ResultMifareError288;
- }
- return result;
- }
- Result NfcDevice::SendCommandByPassThrough(const Time::Clock::TimeSpanType& timeout,
- std::span<const u8> command_data,
- std::span<u8> out_data) {
- // Not implemented
- return ResultSuccess;
- }
- Result NfcDevice::Mount(NFP::ModelType model_type, NFP::MountTarget mount_target_) {
- if (device_state != DeviceState::TagFound) {
- LOG_ERROR(Service_NFP, "Wrong device state {}", device_state);
- return ResultWrongDeviceState;
- }
- if (!LoadAmiiboData()) {
- LOG_ERROR(Service_NFP, "Not an amiibo");
- return ResultInvalidTagType;
- }
- if (!NFP::AmiiboCrypto::IsAmiiboValid(encrypted_tag_data)) {
- LOG_ERROR(Service_NFP, "Not an amiibo");
- return ResultInvalidTagType;
- }
- // The loaded amiibo is not encrypted
- if (is_plain_amiibo) {
- std::vector<u8> data(sizeof(NFP::NTAG215File));
- memcpy(data.data(), &tag_data, sizeof(tag_data));
- WriteBackupData(tag_data.uid, data);
- device_state = DeviceState::TagMounted;
- mount_target = mount_target_;
- return ResultSuccess;
- }
- if (!NFP::AmiiboCrypto::DecodeAmiibo(encrypted_tag_data, tag_data)) {
- bool has_backup = HasBackup(encrypted_tag_data.uuid).IsSuccess();
- LOG_ERROR(Service_NFP, "Can't decode amiibo, has_backup= {}", has_backup);
- return has_backup ? ResultCorruptedDataWithBackup : ResultCorruptedData;
- }
- std::vector<u8> data(sizeof(NFP::EncryptedNTAG215File));
- memcpy(data.data(), &encrypted_tag_data, sizeof(encrypted_tag_data));
- WriteBackupData(encrypted_tag_data.uuid, data);
- device_state = DeviceState::TagMounted;
- mount_target = mount_target_;
- return ResultSuccess;
- }
- Result NfcDevice::Unmount() {
- if (device_state != DeviceState::TagMounted) {
- LOG_ERROR(Service_NFP, "Wrong device state {}", device_state);
- if (device_state == DeviceState::TagRemoved) {
- return ResultTagRemoved;
- }
- return ResultWrongDeviceState;
- }
- // Save data before unloading the amiibo
- if (is_data_moddified) {
- Flush();
- }
- device_state = DeviceState::TagFound;
- mount_target = NFP::MountTarget::None;
- is_app_area_open = false;
- return ResultSuccess;
- }
- Result NfcDevice::Flush() {
- if (device_state != DeviceState::TagMounted) {
- LOG_ERROR(Service_NFP, "Wrong device state {}", device_state);
- if (device_state == DeviceState::TagRemoved) {
- return ResultTagRemoved;
- }
- return ResultWrongDeviceState;
- }
- if (mount_target == NFP::MountTarget::None || mount_target == NFP::MountTarget::Rom) {
- LOG_ERROR(Service_NFP, "Amiibo is read only", device_state);
- return ResultWrongDeviceState;
- }
- auto& settings = tag_data.settings;
- const auto& current_date = GetAmiiboDate(GetCurrentPosixTime());
- if (settings.write_date.raw_date != current_date.raw_date) {
- settings.write_date = current_date;
- UpdateSettingsCrc();
- }
- tag_data.write_counter++;
- const auto result = FlushWithBreak(NFP::BreakType::Normal);
- is_data_moddified = false;
- return result;
- }
- Result NfcDevice::FlushDebug() {
- if (device_state != DeviceState::TagMounted) {
- LOG_ERROR(Service_NFC, "Wrong device state {}", device_state);
- if (device_state == DeviceState::TagRemoved) {
- return ResultTagRemoved;
- }
- return ResultWrongDeviceState;
- }
- if (mount_target == NFP::MountTarget::None || mount_target == NFP::MountTarget::Rom) {
- LOG_ERROR(Service_NFC, "Amiibo is read only", device_state);
- return ResultWrongDeviceState;
- }
- tag_data.write_counter++;
- const auto result = FlushWithBreak(NFP::BreakType::Normal);
- is_data_moddified = false;
- return result;
- }
- Result NfcDevice::FlushWithBreak(NFP::BreakType break_type) {
- if (break_type != NFP::BreakType::Normal) {
- LOG_ERROR(Service_NFC, "Break type not implemented {}", break_type);
- return ResultWrongDeviceState;
- }
- if (is_write_protected) {
- LOG_ERROR(Service_NFP, "No keys available skipping write request");
- return ResultSuccess;
- }
- std::vector<u8> data(sizeof(NFP::EncryptedNTAG215File));
- if (is_plain_amiibo) {
- memcpy(data.data(), &tag_data, sizeof(tag_data));
- WriteBackupData(tag_data.uid, data);
- } else {
- if (!NFP::AmiiboCrypto::EncodeAmiibo(tag_data, encrypted_tag_data)) {
- LOG_ERROR(Service_NFP, "Failed to encode data");
- return ResultWriteAmiiboFailed;
- }
- memcpy(data.data(), &encrypted_tag_data, sizeof(encrypted_tag_data));
- WriteBackupData(encrypted_tag_data.uuid, data);
- }
- if (!npad_device->WriteNfc(data)) {
- LOG_ERROR(Service_NFP, "Error writing to file");
- return ResultWriteAmiiboFailed;
- }
- return ResultSuccess;
- }
- Result NfcDevice::Restore() {
- if (device_state != DeviceState::TagFound) {
- LOG_ERROR(Service_NFP, "Wrong device state {}", device_state);
- if (device_state == DeviceState::TagRemoved) {
- return ResultTagRemoved;
- }
- return ResultWrongDeviceState;
- }
- NFC::TagInfo tag_info{};
- std::array<u8, sizeof(NFP::EncryptedNTAG215File)> data{};
- Result result = GetTagInfo(tag_info);
- if (result.IsError()) {
- return result;
- }
- result = ReadBackupData(tag_info.uuid, tag_info.uuid_length, data);
- if (result.IsError()) {
- return result;
- }
- NFP::NTAG215File temporary_tag_data{};
- NFP::EncryptedNTAG215File temporary_encrypted_tag_data{};
- // Fallback for encrypted amiibos without keys
- if (is_write_protected) {
- return ResultWriteAmiiboFailed;
- }
- // Fallback for plain amiibos
- if (is_plain_amiibo) {
- LOG_INFO(Service_NFP, "Restoring backup of plain amiibo");
- memcpy(&temporary_tag_data, data.data(), sizeof(NFP::EncryptedNTAG215File));
- temporary_encrypted_tag_data = NFP::AmiiboCrypto::EncodedDataToNfcData(temporary_tag_data);
- }
- if (!is_plain_amiibo) {
- LOG_INFO(Service_NFP, "Restoring backup of encrypted amiibo");
- temporary_tag_data = {};
- memcpy(&temporary_encrypted_tag_data, data.data(), sizeof(NFP::EncryptedNTAG215File));
- }
- if (!NFP::AmiiboCrypto::IsAmiiboValid(temporary_encrypted_tag_data)) {
- return ResultInvalidTagType;
- }
- if (!is_plain_amiibo) {
- if (!NFP::AmiiboCrypto::DecodeAmiibo(temporary_encrypted_tag_data, temporary_tag_data)) {
- LOG_ERROR(Service_NFP, "Can't decode amiibo");
- return ResultCorruptedData;
- }
- }
- // Overwrite tag contents with backup and mount the tag
- tag_data = temporary_tag_data;
- encrypted_tag_data = temporary_encrypted_tag_data;
- device_state = DeviceState::TagMounted;
- mount_target = NFP::MountTarget::All;
- is_data_moddified = true;
- return ResultSuccess;
- }
- Result NfcDevice::GetCommonInfo(NFP::CommonInfo& common_info) const {
- if (device_state != DeviceState::TagMounted) {
- LOG_ERROR(Service_NFP, "Wrong device state {}", device_state);
- if (device_state == DeviceState::TagRemoved) {
- return ResultTagRemoved;
- }
- return ResultWrongDeviceState;
- }
- if (mount_target == NFP::MountTarget::None || mount_target == NFP::MountTarget::Rom) {
- LOG_ERROR(Service_NFP, "Amiibo is read only", device_state);
- return ResultWrongDeviceState;
- }
- const auto& settings = tag_data.settings;
- // TODO: Validate this data
- common_info = {
- .last_write_date = settings.write_date.GetWriteDate(),
- .write_counter = tag_data.application_write_counter,
- .version = tag_data.amiibo_version,
- .application_area_size = sizeof(NFP::ApplicationArea),
- };
- return ResultSuccess;
- }
- Result NfcDevice::GetModelInfo(NFP::ModelInfo& model_info) const {
- if (device_state != DeviceState::TagMounted) {
- LOG_ERROR(Service_NFP, "Wrong device state {}", device_state);
- if (device_state == DeviceState::TagRemoved) {
- return ResultTagRemoved;
- }
- return ResultWrongDeviceState;
- }
- const auto& model_info_data = encrypted_tag_data.user_memory.model_info;
- model_info = {
- .character_id = model_info_data.character_id,
- .character_variant = model_info_data.character_variant,
- .amiibo_type = model_info_data.amiibo_type,
- .model_number = model_info_data.model_number,
- .series = model_info_data.series,
- };
- return ResultSuccess;
- }
- Result NfcDevice::GetRegisterInfo(NFP::RegisterInfo& register_info) const {
- if (device_state != DeviceState::TagMounted) {
- LOG_ERROR(Service_NFP, "Wrong device state {}", device_state);
- if (device_state == DeviceState::TagRemoved) {
- return ResultTagRemoved;
- }
- return ResultWrongDeviceState;
- }
- if (mount_target == NFP::MountTarget::None || mount_target == NFP::MountTarget::Rom) {
- LOG_ERROR(Service_NFP, "Amiibo is read only", device_state);
- return ResultWrongDeviceState;
- }
- if (tag_data.settings.settings.amiibo_initialized == 0) {
- return ResultRegistrationIsNotInitialized;
- }
- Service::Mii::MiiManager manager;
- const auto& settings = tag_data.settings;
- // TODO: Validate this data
- register_info = {
- .mii_char_info = manager.ConvertV3ToCharInfo(tag_data.owner_mii),
- .creation_date = settings.init_date.GetWriteDate(),
- .amiibo_name = GetAmiiboName(settings),
- .font_region = settings.settings.font_region,
- };
- return ResultSuccess;
- }
- Result NfcDevice::GetRegisterInfoPrivate(NFP::RegisterInfoPrivate& register_info) const {
- if (device_state != DeviceState::TagMounted) {
- LOG_ERROR(Service_NFP, "Wrong device state {}", device_state);
- if (device_state == DeviceState::TagRemoved) {
- return ResultTagRemoved;
- }
- return ResultWrongDeviceState;
- }
- if (mount_target == NFP::MountTarget::None || mount_target == NFP::MountTarget::Rom) {
- LOG_ERROR(Service_NFP, "Amiibo is read only", device_state);
- return ResultWrongDeviceState;
- }
- if (tag_data.settings.settings.amiibo_initialized == 0) {
- return ResultRegistrationIsNotInitialized;
- }
- Service::Mii::MiiManager manager;
- const auto& settings = tag_data.settings;
- // TODO: Validate and complete this data
- register_info = {
- .mii_store_data = {},
- .creation_date = settings.init_date.GetWriteDate(),
- .amiibo_name = GetAmiiboName(settings),
- .font_region = settings.settings.font_region,
- };
- return ResultSuccess;
- }
- Result NfcDevice::GetAdminInfo(NFP::AdminInfo& admin_info) const {
- if (device_state != DeviceState::TagMounted) {
- LOG_ERROR(Service_NFC, "Wrong device state {}", device_state);
- if (device_state == DeviceState::TagRemoved) {
- return ResultTagRemoved;
- }
- return ResultWrongDeviceState;
- }
- if (mount_target == NFP::MountTarget::None || mount_target == NFP::MountTarget::Rom) {
- LOG_ERROR(Service_NFC, "Amiibo is read only", device_state);
- return ResultWrongDeviceState;
- }
- u8 flags = static_cast<u8>(tag_data.settings.settings.raw >> 0x4);
- if (tag_data.settings.settings.amiibo_initialized == 0) {
- flags = flags & 0xfe;
- }
- u64 application_id = 0;
- u32 application_area_id = 0;
- NFP::AppAreaVersion app_area_version = NFP::AppAreaVersion::NotSet;
- if (tag_data.settings.settings.appdata_initialized != 0) {
- application_id = tag_data.application_id;
- app_area_version = static_cast<NFP::AppAreaVersion>(
- application_id >> NFP::application_id_version_offset & 0xf);
- // Restore application id to original value
- if (application_id >> 0x38 != 0) {
- const u8 application_byte = tag_data.application_id_byte & 0xf;
- application_id =
- RemoveVersionByte(application_id) |
- (static_cast<u64>(application_byte) << NFP::application_id_version_offset);
- }
- application_area_id = tag_data.application_area_id;
- }
- // TODO: Validate this data
- admin_info = {
- .application_id = application_id,
- .application_area_id = application_area_id,
- .crc_change_counter = tag_data.settings.crc_counter,
- .flags = flags,
- .tag_type = PackedTagType::Type2,
- .app_area_version = app_area_version,
- };
- return ResultSuccess;
- }
- Result NfcDevice::DeleteRegisterInfo() {
- if (device_state != DeviceState::TagMounted) {
- LOG_ERROR(Service_NFC, "Wrong device state {}", device_state);
- if (device_state == DeviceState::TagRemoved) {
- return ResultTagRemoved;
- }
- return ResultWrongDeviceState;
- }
- if (mount_target == NFP::MountTarget::None || mount_target == NFP::MountTarget::Rom) {
- LOG_ERROR(Service_NFC, "Amiibo is read only", device_state);
- return ResultWrongDeviceState;
- }
- if (tag_data.settings.settings.amiibo_initialized == 0) {
- return ResultRegistrationIsNotInitialized;
- }
- Common::TinyMT rng{};
- rng.Initialize(static_cast<u32>(GetCurrentPosixTime()));
- rng.GenerateRandomBytes(&tag_data.owner_mii, sizeof(tag_data.owner_mii));
- rng.GenerateRandomBytes(&tag_data.settings.amiibo_name, sizeof(tag_data.settings.amiibo_name));
- rng.GenerateRandomBytes(&tag_data.unknown, sizeof(u8));
- rng.GenerateRandomBytes(&tag_data.unknown2[0], sizeof(u32));
- rng.GenerateRandomBytes(&tag_data.unknown2[1], sizeof(u32));
- rng.GenerateRandomBytes(&tag_data.register_info_crc, sizeof(u32));
- rng.GenerateRandomBytes(&tag_data.settings.init_date, sizeof(u32));
- tag_data.settings.settings.font_region.Assign(0);
- tag_data.settings.settings.amiibo_initialized.Assign(0);
- return Flush();
- }
- Result NfcDevice::SetRegisterInfoPrivate(const NFP::RegisterInfoPrivate& register_info) {
- if (device_state != DeviceState::TagMounted) {
- LOG_ERROR(Service_NFP, "Wrong device state {}", device_state);
- if (device_state == DeviceState::TagRemoved) {
- return ResultTagRemoved;
- }
- return ResultWrongDeviceState;
- }
- if (mount_target == NFP::MountTarget::None || mount_target == NFP::MountTarget::Rom) {
- LOG_ERROR(Service_NFP, "Amiibo is read only", device_state);
- return ResultWrongDeviceState;
- }
- Service::Mii::MiiManager manager;
- const auto mii = manager.BuildBase(Mii::Gender::Male);
- auto& settings = tag_data.settings;
- if (tag_data.settings.settings.amiibo_initialized == 0) {
- settings.init_date = GetAmiiboDate(GetCurrentPosixTime());
- settings.write_date.raw_date = 0;
- }
- SetAmiiboName(settings, register_info.amiibo_name);
- tag_data.owner_mii = manager.BuildFromStoreData(mii);
- tag_data.mii_extension = manager.SetFromStoreData(mii);
- tag_data.unknown = 0;
- tag_data.unknown2 = {};
- settings.country_code_id = 0;
- settings.settings.font_region.Assign(0);
- settings.settings.amiibo_initialized.Assign(1);
- UpdateRegisterInfoCrc();
- return Flush();
- }
- Result NfcDevice::RestoreAmiibo() {
- if (device_state != DeviceState::TagMounted) {
- LOG_ERROR(Service_NFP, "Wrong device state {}", device_state);
- if (device_state == DeviceState::TagRemoved) {
- return ResultTagRemoved;
- }
- return ResultWrongDeviceState;
- }
- if (mount_target == NFP::MountTarget::None || mount_target == NFP::MountTarget::Rom) {
- LOG_ERROR(Service_NFP, "Amiibo is read only", device_state);
- return ResultWrongDeviceState;
- }
- // TODO: Load amiibo from backup on system
- LOG_ERROR(Service_NFP, "Not Implemented");
- return ResultSuccess;
- }
- Result NfcDevice::Format() {
- auto result1 = DeleteApplicationArea();
- auto result2 = DeleteRegisterInfo();
- if (result1.IsError()) {
- return result1;
- }
- if (result2.IsError()) {
- return result2;
- }
- return Flush();
- }
- Result NfcDevice::OpenApplicationArea(u32 access_id) {
- if (device_state != DeviceState::TagMounted) {
- LOG_ERROR(Service_NFP, "Wrong device state {}", device_state);
- if (device_state == DeviceState::TagRemoved) {
- return ResultTagRemoved;
- }
- return ResultWrongDeviceState;
- }
- if (mount_target == NFP::MountTarget::None || mount_target == NFP::MountTarget::Rom) {
- LOG_ERROR(Service_NFP, "Amiibo is read only", device_state);
- return ResultWrongDeviceState;
- }
- if (tag_data.settings.settings.appdata_initialized.Value() == 0) {
- LOG_WARNING(Service_NFP, "Application area is not initialized");
- return ResultApplicationAreaIsNotInitialized;
- }
- if (tag_data.application_area_id != access_id) {
- LOG_WARNING(Service_NFP, "Wrong application area id");
- return ResultWrongApplicationAreaId;
- }
- is_app_area_open = true;
- return ResultSuccess;
- }
- Result NfcDevice::GetApplicationAreaId(u32& application_area_id) const {
- application_area_id = {};
- if (device_state != DeviceState::TagMounted) {
- LOG_ERROR(Service_NFP, "Wrong device state {}", device_state);
- if (device_state == DeviceState::TagRemoved) {
- return ResultTagRemoved;
- }
- return ResultWrongDeviceState;
- }
- if (mount_target == NFP::MountTarget::None || mount_target == NFP::MountTarget::Rom) {
- LOG_ERROR(Service_NFP, "Amiibo is read only", device_state);
- return ResultWrongDeviceState;
- }
- if (tag_data.settings.settings.appdata_initialized.Value() == 0) {
- LOG_WARNING(Service_NFP, "Application area is not initialized");
- return ResultApplicationAreaIsNotInitialized;
- }
- application_area_id = tag_data.application_area_id;
- return ResultSuccess;
- }
- Result NfcDevice::GetApplicationArea(std::span<u8> data) const {
- if (device_state != DeviceState::TagMounted) {
- LOG_ERROR(Service_NFP, "Wrong device state {}", device_state);
- if (device_state == DeviceState::TagRemoved) {
- return ResultTagRemoved;
- }
- return ResultWrongDeviceState;
- }
- if (mount_target == NFP::MountTarget::None || mount_target == NFP::MountTarget::Rom) {
- LOG_ERROR(Service_NFP, "Amiibo is read only", device_state);
- return ResultWrongDeviceState;
- }
- if (!is_app_area_open) {
- LOG_ERROR(Service_NFP, "Application area is not open");
- return ResultWrongDeviceState;
- }
- if (tag_data.settings.settings.appdata_initialized.Value() == 0) {
- LOG_ERROR(Service_NFP, "Application area is not initialized");
- return ResultApplicationAreaIsNotInitialized;
- }
- memcpy(data.data(), tag_data.application_area.data(),
- std::min(data.size(), sizeof(NFP::ApplicationArea)));
- return ResultSuccess;
- }
- Result NfcDevice::SetApplicationArea(std::span<const u8> data) {
- if (device_state != DeviceState::TagMounted) {
- LOG_ERROR(Service_NFP, "Wrong device state {}", device_state);
- if (device_state == DeviceState::TagRemoved) {
- return ResultTagRemoved;
- }
- return ResultWrongDeviceState;
- }
- if (mount_target == NFP::MountTarget::None || mount_target == NFP::MountTarget::Rom) {
- LOG_ERROR(Service_NFP, "Amiibo is read only", device_state);
- return ResultWrongDeviceState;
- }
- if (!is_app_area_open) {
- LOG_ERROR(Service_NFP, "Application area is not open");
- return ResultWrongDeviceState;
- }
- if (tag_data.settings.settings.appdata_initialized.Value() == 0) {
- LOG_ERROR(Service_NFP, "Application area is not initialized");
- return ResultApplicationAreaIsNotInitialized;
- }
- if (data.size() > sizeof(NFP::ApplicationArea)) {
- LOG_ERROR(Service_NFP, "Wrong data size {}", data.size());
- return ResultUnknown;
- }
- Common::TinyMT rng{};
- rng.Initialize(static_cast<u32>(GetCurrentPosixTime()));
- std::memcpy(tag_data.application_area.data(), data.data(), data.size());
- // Fill remaining data with random numbers
- rng.GenerateRandomBytes(tag_data.application_area.data() + data.size(),
- sizeof(NFP::ApplicationArea) - data.size());
- if (tag_data.application_write_counter != NFP::counter_limit) {
- tag_data.application_write_counter++;
- }
- is_data_moddified = true;
- return ResultSuccess;
- }
- Result NfcDevice::CreateApplicationArea(u32 access_id, std::span<const u8> data) {
- if (device_state != DeviceState::TagMounted) {
- LOG_ERROR(Service_NFP, "Wrong device state {}", device_state);
- if (device_state == DeviceState::TagRemoved) {
- return ResultTagRemoved;
- }
- return ResultWrongDeviceState;
- }
- if (tag_data.settings.settings.appdata_initialized.Value() != 0) {
- LOG_ERROR(Service_NFP, "Application area already exist");
- return ResultApplicationAreaExist;
- }
- return RecreateApplicationArea(access_id, data);
- }
- Result NfcDevice::RecreateApplicationArea(u32 access_id, std::span<const u8> data) {
- if (device_state != DeviceState::TagMounted) {
- LOG_ERROR(Service_NFP, "Wrong device state {}", device_state);
- if (device_state == DeviceState::TagRemoved) {
- return ResultTagRemoved;
- }
- return ResultWrongDeviceState;
- }
- if (is_app_area_open) {
- LOG_ERROR(Service_NFP, "Application area is open");
- return ResultWrongDeviceState;
- }
- if (mount_target == NFP::MountTarget::None || mount_target == NFP::MountTarget::Rom) {
- LOG_ERROR(Service_NFP, "Amiibo is read only", device_state);
- return ResultWrongDeviceState;
- }
- if (data.size() > sizeof(NFP::ApplicationArea)) {
- LOG_ERROR(Service_NFP, "Wrong data size {}", data.size());
- return ResultWrongApplicationAreaSize;
- }
- Common::TinyMT rng{};
- rng.Initialize(static_cast<u32>(GetCurrentPosixTime()));
- std::memcpy(tag_data.application_area.data(), data.data(), data.size());
- // Fill remaining data with random numbers
- rng.GenerateRandomBytes(tag_data.application_area.data() + data.size(),
- sizeof(NFP::ApplicationArea) - data.size());
- if (tag_data.application_write_counter != NFP::counter_limit) {
- tag_data.application_write_counter++;
- }
- const u64 application_id = system.GetApplicationProcessProgramID();
- tag_data.application_id_byte =
- static_cast<u8>(application_id >> NFP::application_id_version_offset & 0xf);
- tag_data.application_id =
- RemoveVersionByte(application_id) | (static_cast<u64>(NFP::AppAreaVersion::NintendoSwitch)
- << NFP::application_id_version_offset);
- tag_data.settings.settings.appdata_initialized.Assign(1);
- tag_data.application_area_id = access_id;
- tag_data.unknown = {};
- tag_data.unknown2 = {};
- UpdateRegisterInfoCrc();
- return Flush();
- }
- Result NfcDevice::DeleteApplicationArea() {
- if (device_state != DeviceState::TagMounted) {
- LOG_ERROR(Service_NFP, "Wrong device state {}", device_state);
- if (device_state == DeviceState::TagRemoved) {
- return ResultTagRemoved;
- }
- return ResultWrongDeviceState;
- }
- if (mount_target == NFP::MountTarget::None || mount_target == NFP::MountTarget::Rom) {
- LOG_ERROR(Service_NFP, "Amiibo is read only", device_state);
- return ResultWrongDeviceState;
- }
- if (tag_data.settings.settings.appdata_initialized == 0) {
- return ResultApplicationAreaIsNotInitialized;
- }
- if (tag_data.application_write_counter != NFP::counter_limit) {
- tag_data.application_write_counter++;
- }
- Common::TinyMT rng{};
- rng.Initialize(static_cast<u32>(GetCurrentPosixTime()));
- rng.GenerateRandomBytes(tag_data.application_area.data(), sizeof(NFP::ApplicationArea));
- rng.GenerateRandomBytes(&tag_data.application_id, sizeof(u64));
- rng.GenerateRandomBytes(&tag_data.application_area_id, sizeof(u32));
- rng.GenerateRandomBytes(&tag_data.application_id_byte, sizeof(u8));
- tag_data.settings.settings.appdata_initialized.Assign(0);
- tag_data.unknown = {};
- tag_data.unknown2 = {};
- is_app_area_open = false;
- UpdateRegisterInfoCrc();
- return Flush();
- }
- Result NfcDevice::ExistsApplicationArea(bool& has_application_area) const {
- if (device_state != DeviceState::TagMounted) {
- LOG_ERROR(Service_NFC, "Wrong device state {}", device_state);
- if (device_state == DeviceState::TagRemoved) {
- return ResultTagRemoved;
- }
- return ResultWrongDeviceState;
- }
- if (mount_target == NFP::MountTarget::None || mount_target == NFP::MountTarget::Rom) {
- LOG_ERROR(Service_NFC, "Amiibo is read only", device_state);
- return ResultWrongDeviceState;
- }
- has_application_area = tag_data.settings.settings.appdata_initialized.Value() != 0;
- return ResultSuccess;
- }
- Result NfcDevice::GetAll(NFP::NfpData& data) const {
- if (device_state != DeviceState::TagMounted) {
- LOG_ERROR(Service_NFC, "Wrong device state {}", device_state);
- if (device_state == DeviceState::TagRemoved) {
- return ResultTagRemoved;
- }
- return ResultWrongDeviceState;
- }
- if (mount_target == NFP::MountTarget::None || mount_target == NFP::MountTarget::Rom) {
- LOG_ERROR(Service_NFC, "Amiibo is read only", device_state);
- return ResultWrongDeviceState;
- }
- NFP::CommonInfo common_info{};
- const u64 application_id = tag_data.application_id;
- GetCommonInfo(common_info);
- data = {
- .magic = tag_data.constant_value,
- .write_counter = tag_data.write_counter,
- .settings_crc = tag_data.settings.crc,
- .common_info = common_info,
- .mii_char_info = tag_data.owner_mii,
- .mii_store_data_extension = tag_data.mii_extension,
- .creation_date = tag_data.settings.init_date.GetWriteDate(),
- .amiibo_name = tag_data.settings.amiibo_name,
- .amiibo_name_null_terminated = 0,
- .settings = tag_data.settings.settings,
- .unknown1 = tag_data.unknown,
- .register_info_crc = tag_data.register_info_crc,
- .unknown2 = tag_data.unknown2,
- .application_id = application_id,
- .access_id = tag_data.application_area_id,
- .settings_crc_counter = tag_data.settings.crc_counter,
- .font_region = tag_data.settings.settings.font_region,
- .tag_type = PackedTagType::Type2,
- .console_type = static_cast<NFP::AppAreaVersion>(
- application_id >> NFP::application_id_version_offset & 0xf),
- .application_id_byte = tag_data.application_id_byte,
- .application_area = tag_data.application_area,
- };
- return ResultSuccess;
- }
- Result NfcDevice::SetAll(const NFP::NfpData& data) {
- if (device_state != DeviceState::TagMounted) {
- LOG_ERROR(Service_NFC, "Wrong device state {}", device_state);
- if (device_state == DeviceState::TagRemoved) {
- return ResultTagRemoved;
- }
- return ResultWrongDeviceState;
- }
- if (mount_target == NFP::MountTarget::None || mount_target == NFP::MountTarget::Rom) {
- LOG_ERROR(Service_NFC, "Amiibo is read only", device_state);
- return ResultWrongDeviceState;
- }
- tag_data.constant_value = data.magic;
- tag_data.write_counter = data.write_counter;
- tag_data.settings.crc = data.settings_crc;
- tag_data.settings.write_date.SetWriteDate(data.common_info.last_write_date);
- tag_data.write_counter = data.common_info.write_counter;
- tag_data.amiibo_version = data.common_info.version;
- tag_data.owner_mii = data.mii_char_info;
- tag_data.mii_extension = data.mii_store_data_extension;
- tag_data.settings.init_date.SetWriteDate(data.creation_date);
- tag_data.settings.amiibo_name = data.amiibo_name;
- tag_data.settings.settings = data.settings;
- tag_data.unknown = data.unknown1;
- tag_data.register_info_crc = data.register_info_crc;
- tag_data.unknown2 = data.unknown2;
- tag_data.application_id = data.application_id;
- tag_data.application_area_id = data.access_id;
- tag_data.settings.crc_counter = data.settings_crc_counter;
- tag_data.settings.settings.font_region.Assign(data.font_region);
- tag_data.application_id_byte = data.application_id_byte;
- tag_data.application_area = data.application_area;
- return ResultSuccess;
- }
- Result NfcDevice::BreakTag(NFP::BreakType break_type) {
- if (device_state != DeviceState::TagMounted) {
- LOG_ERROR(Service_NFC, "Wrong device state {}", device_state);
- if (device_state == DeviceState::TagRemoved) {
- return ResultTagRemoved;
- }
- return ResultWrongDeviceState;
- }
- if (mount_target == NFP::MountTarget::None || mount_target == NFP::MountTarget::Rom) {
- LOG_ERROR(Service_NFC, "Amiibo is read only", device_state);
- return ResultWrongDeviceState;
- }
- // TODO: Complete this implementation
- return FlushWithBreak(break_type);
- }
- Result NfcDevice::HasBackup(const UniqueSerialNumber& uid, std::size_t uuid_size) const {
- ASSERT_MSG(uuid_size < sizeof(UniqueSerialNumber), "Invalid UUID size");
- constexpr auto backup_dir = "backup";
- const auto yuzu_amiibo_dir = Common::FS::GetYuzuPath(Common::FS::YuzuPath::AmiiboDir);
- const auto file_name =
- fmt::format("{0:02x}.bin", fmt::join(uid.begin(), uid.begin() + uuid_size, ""));
- if (!Common::FS::Exists(yuzu_amiibo_dir / backup_dir / file_name)) {
- return ResultUnableToAccessBackupFile;
- }
- return ResultSuccess;
- }
- Result NfcDevice::HasBackup(const NFP::TagUuid& tag_uid) const {
- UniqueSerialNumber uuid{};
- memcpy(uuid.data(), &tag_uid, sizeof(NFP::TagUuid));
- return HasBackup(uuid, sizeof(NFP::TagUuid));
- }
- Result NfcDevice::ReadBackupData(const UniqueSerialNumber& uid, std::size_t uuid_size,
- std::span<u8> data) const {
- ASSERT_MSG(uuid_size < sizeof(UniqueSerialNumber), "Invalid UUID size");
- constexpr auto backup_dir = "backup";
- const auto yuzu_amiibo_dir = Common::FS::GetYuzuPath(Common::FS::YuzuPath::AmiiboDir);
- const auto file_name =
- fmt::format("{0:02x}.bin", fmt::join(uid.begin(), uid.begin() + uuid_size, ""));
- const Common::FS::IOFile keys_file{yuzu_amiibo_dir / backup_dir / file_name,
- Common::FS::FileAccessMode::Read,
- Common::FS::FileType::BinaryFile};
- if (!keys_file.IsOpen()) {
- LOG_ERROR(Service_NFP, "Failed to open amiibo backup");
- return ResultUnableToAccessBackupFile;
- }
- if (keys_file.Read(data) != data.size()) {
- LOG_ERROR(Service_NFP, "Failed to read amiibo backup");
- return ResultUnableToAccessBackupFile;
- }
- return ResultSuccess;
- }
- Result NfcDevice::ReadBackupData(const NFP::TagUuid& tag_uid, std::span<u8> data) const {
- UniqueSerialNumber uuid{};
- memcpy(uuid.data(), &tag_uid, sizeof(NFP::TagUuid));
- return ReadBackupData(uuid, sizeof(NFP::TagUuid), data);
- }
- Result NfcDevice::WriteBackupData(const UniqueSerialNumber& uid, std::size_t uuid_size,
- std::span<const u8> data) {
- ASSERT_MSG(uuid_size < sizeof(UniqueSerialNumber), "Invalid UUID size");
- constexpr auto backup_dir = "backup";
- const auto yuzu_amiibo_dir = Common::FS::GetYuzuPath(Common::FS::YuzuPath::AmiiboDir);
- const auto file_name =
- fmt::format("{0:02x}.bin", fmt::join(uid.begin(), uid.begin() + uuid_size, ""));
- if (HasBackup(uid, uuid_size).IsError()) {
- if (!Common::FS::CreateDir(yuzu_amiibo_dir / backup_dir)) {
- return ResultBackupPathAlreadyExist;
- }
- if (!Common::FS::NewFile(yuzu_amiibo_dir / backup_dir / file_name)) {
- return ResultBackupPathAlreadyExist;
- }
- }
- const Common::FS::IOFile keys_file{yuzu_amiibo_dir / backup_dir / file_name,
- Common::FS::FileAccessMode::ReadWrite,
- Common::FS::FileType::BinaryFile};
- if (!keys_file.IsOpen()) {
- LOG_ERROR(Service_NFP, "Failed to open amiibo backup");
- return ResultUnableToAccessBackupFile;
- }
- if (keys_file.Write(data) != data.size()) {
- LOG_ERROR(Service_NFP, "Failed to write amiibo backup");
- return ResultUnableToAccessBackupFile;
- }
- return ResultSuccess;
- }
- Result NfcDevice::WriteBackupData(const NFP::TagUuid& tag_uid, std::span<const u8> data) {
- UniqueSerialNumber uuid{};
- memcpy(uuid.data(), &tag_uid, sizeof(NFP::TagUuid));
- return WriteBackupData(uuid, sizeof(NFP::TagUuid), data);
- }
- Result NfcDevice::WriteNtf(std::span<const u8> data) {
- if (device_state != DeviceState::TagMounted) {
- LOG_ERROR(Service_NFC, "Wrong device state {}", device_state);
- if (device_state == DeviceState::TagRemoved) {
- return ResultTagRemoved;
- }
- return ResultWrongDeviceState;
- }
- if (mount_target == NFP::MountTarget::None || mount_target == NFP::MountTarget::Rom) {
- LOG_ERROR(Service_NFC, "Amiibo is read only", device_state);
- return ResultWrongDeviceState;
- }
- // Not implemented
- return ResultSuccess;
- }
- NFP::AmiiboName NfcDevice::GetAmiiboName(const NFP::AmiiboSettings& settings) const {
- std::array<char16_t, NFP::amiibo_name_length> settings_amiibo_name{};
- NFP::AmiiboName amiibo_name{};
- // Convert from big endian to little endian
- for (std::size_t i = 0; i < NFP::amiibo_name_length; i++) {
- settings_amiibo_name[i] = static_cast<u16>(settings.amiibo_name[i]);
- }
- // Convert from utf16 to utf8
- const auto amiibo_name_utf8 = Common::UTF16ToUTF8(settings_amiibo_name.data());
- memcpy(amiibo_name.data(), amiibo_name_utf8.data(), amiibo_name_utf8.size());
- return amiibo_name;
- }
- void NfcDevice::SetAmiiboName(NFP::AmiiboSettings& settings,
- const NFP::AmiiboName& amiibo_name) const {
- std::array<char16_t, NFP::amiibo_name_length> settings_amiibo_name{};
- // Convert from utf8 to utf16
- const auto amiibo_name_utf16 = Common::UTF8ToUTF16(amiibo_name.data());
- memcpy(settings_amiibo_name.data(), amiibo_name_utf16.data(),
- amiibo_name_utf16.size() * sizeof(char16_t));
- // Convert from little endian to big endian
- for (std::size_t i = 0; i < NFP::amiibo_name_length; i++) {
- settings.amiibo_name[i] = static_cast<u16_be>(settings_amiibo_name[i]);
- }
- }
- NFP::AmiiboDate NfcDevice::GetAmiiboDate(s64 posix_time) const {
- const auto& time_zone_manager =
- system.GetTimeManager().GetTimeZoneContentManager().GetTimeZoneManager();
- Time::TimeZone::CalendarInfo calendar_info{};
- NFP::AmiiboDate amiibo_date{};
- amiibo_date.SetYear(2000);
- amiibo_date.SetMonth(1);
- amiibo_date.SetDay(1);
- if (time_zone_manager.ToCalendarTime({}, posix_time, calendar_info) == ResultSuccess) {
- amiibo_date.SetYear(calendar_info.time.year);
- amiibo_date.SetMonth(calendar_info.time.month);
- amiibo_date.SetDay(calendar_info.time.day);
- }
- return amiibo_date;
- }
- u64 NfcDevice::GetCurrentPosixTime() const {
- auto& standard_steady_clock{system.GetTimeManager().GetStandardSteadyClockCore()};
- return standard_steady_clock.GetCurrentTimePoint(system).time_point;
- }
- u64 NfcDevice::RemoveVersionByte(u64 application_id) const {
- return application_id & ~(0xfULL << NFP::application_id_version_offset);
- }
- void NfcDevice::UpdateSettingsCrc() {
- auto& settings = tag_data.settings;
- if (settings.crc_counter != NFP::counter_limit) {
- settings.crc_counter++;
- }
- // TODO: this reads data from a global, find what it is
- std::array<u8, 8> unknown_input{};
- boost::crc_32_type crc;
- crc.process_bytes(&unknown_input, sizeof(unknown_input));
- settings.crc = crc.checksum();
- }
- void NfcDevice::UpdateRegisterInfoCrc() {
- #pragma pack(push, 1)
- struct CrcData {
- Mii::Ver3StoreData mii;
- u8 application_id_byte;
- u8 unknown;
- Mii::NfpStoreDataExtension mii_extension;
- std::array<u32, 0x5> unknown2;
- };
- static_assert(sizeof(CrcData) == 0x7e, "CrcData is an invalid size");
- #pragma pack(pop)
- const CrcData crc_data{
- .mii = tag_data.owner_mii,
- .application_id_byte = tag_data.application_id_byte,
- .unknown = tag_data.unknown,
- .mii_extension = tag_data.mii_extension,
- .unknown2 = tag_data.unknown2,
- };
- boost::crc_32_type crc;
- crc.process_bytes(&crc_data, sizeof(CrcData));
- tag_data.register_info_crc = crc.checksum();
- }
- void NfcDevice::BuildAmiiboWithoutKeys(NFP::NTAG215File& stubbed_tag_data,
- const NFP::EncryptedNTAG215File& encrypted_file) const {
- Service::Mii::MiiManager manager;
- auto& settings = stubbed_tag_data.settings;
- stubbed_tag_data = NFP::AmiiboCrypto::NfcDataToEncodedData(encrypted_file);
- // Common info
- stubbed_tag_data.write_counter = 0;
- stubbed_tag_data.amiibo_version = 0;
- settings.write_date = GetAmiiboDate(GetCurrentPosixTime());
- // Register info
- SetAmiiboName(settings, {'y', 'u', 'z', 'u', 'A', 'm', 'i', 'i', 'b', 'o'});
- settings.settings.font_region.Assign(0);
- settings.init_date = GetAmiiboDate(GetCurrentPosixTime());
- stubbed_tag_data.owner_mii = manager.BuildFromStoreData(manager.BuildBase(Mii::Gender::Male));
- // Admin info
- settings.settings.amiibo_initialized.Assign(1);
- settings.settings.appdata_initialized.Assign(0);
- }
- u64 NfcDevice::GetHandle() const {
- // Generate a handle based of the npad id
- return static_cast<u64>(npad_id);
- }
- DeviceState NfcDevice::GetCurrentState() const {
- return device_state;
- }
- Result NfcDevice::GetNpadId(Core::HID::NpadIdType& out_npad_id) const {
- // TODO: This should get the npad id from nn::hid::system::GetXcdHandleForNpadWithNfc
- out_npad_id = npad_id;
- return ResultSuccess;
- }
- } // namespace Service::NFC
|