From 107aa52cdbf9c6e7dc4a6e95da3ae18bc382778e Mon Sep 17 00:00:00 2001 From: german77 Date: Sun, 4 Jun 2023 15:50:44 -0600 Subject: service: nfc: Add backup support --- src/common/fs/fs_paths.h | 1 + src/common/fs/path_util.cpp | 1 + src/common/fs/path_util.h | 1 + src/core/hle/service/nfc/common/device.cpp | 160 ++++++++++++++++++--- src/core/hle/service/nfc/common/device.h | 10 +- src/core/hle/service/nfc/common/device_manager.cpp | 14 +- src/core/hle/service/nfc/nfc_interface.cpp | 8 +- src/core/hle/service/nfc/nfc_result.h | 20 +-- src/core/hle/service/nfp/nfp_interface.cpp | 6 +- src/core/hle/service/nfp/nfp_result.h | 2 + 10 files changed, 184 insertions(+), 39 deletions(-) diff --git a/src/common/fs/fs_paths.h b/src/common/fs/fs_paths.h index c77c112f1..61bac9eba 100644 --- a/src/common/fs/fs_paths.h +++ b/src/common/fs/fs_paths.h @@ -10,6 +10,7 @@ // Sub-directories contained within a yuzu data directory +#define AMIIBO_DIR "amiibo" #define CACHE_DIR "cache" #define CONFIG_DIR "config" #define DUMP_DIR "dump" diff --git a/src/common/fs/path_util.cpp b/src/common/fs/path_util.cpp index e026a13d9..d71cfacc6 100644 --- a/src/common/fs/path_util.cpp +++ b/src/common/fs/path_util.cpp @@ -114,6 +114,7 @@ public: #endif GenerateYuzuPath(YuzuPath::YuzuDir, yuzu_path); + GenerateYuzuPath(YuzuPath::AmiiboDir, yuzu_path / AMIIBO_DIR); GenerateYuzuPath(YuzuPath::CacheDir, yuzu_path_cache); GenerateYuzuPath(YuzuPath::ConfigDir, yuzu_path_config); GenerateYuzuPath(YuzuPath::DumpDir, yuzu_path / DUMP_DIR); diff --git a/src/common/fs/path_util.h b/src/common/fs/path_util.h index 7cfe85b70..ba28964d0 100644 --- a/src/common/fs/path_util.h +++ b/src/common/fs/path_util.h @@ -12,6 +12,7 @@ namespace Common::FS { enum class YuzuPath { YuzuDir, // Where yuzu stores its data. + AmiiboDir, // Where Amiibo backups are stored. CacheDir, // Where cached filesystem data is stored. ConfigDir, // Where config files are stored. DumpDir, // Where dumped data is stored. diff --git a/src/core/hle/service/nfc/common/device.cpp b/src/core/hle/service/nfc/common/device.cpp index 0bd7900e1..b14f682b5 100644 --- a/src/core/hle/service/nfc/common/device.cpp +++ b/src/core/hle/service/nfc/common/device.cpp @@ -12,6 +12,11 @@ #pragma warning(pop) #endif +#include + +#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" @@ -136,7 +141,7 @@ bool NfcDevice::LoadNfcTag(std::span data) { if (!NFP::AmiiboCrypto::IsKeyAvailable()) { LOG_INFO(Service_NFC, "Loading amiibo without keys"); memcpy(&encrypted_tag_data, data.data(), sizeof(NFP::EncryptedNTAG215File)); - BuildAmiiboWithoutKeys(); + BuildAmiiboWithoutKeys(tag_data, encrypted_tag_data); is_plain_amiibo = true; is_write_protected = true; return true; @@ -366,16 +371,25 @@ Result NfcDevice::Mount(NFP::ModelType model_type, NFP::MountTarget mount_target // The loaded amiibo is not encrypted if (is_plain_amiibo) { + std::vector 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)) { - LOG_ERROR(Service_NFP, "Can't decode amiibo {}", device_state); - return ResultCorruptedData; + bool has_backup = HasBackup(encrypted_tag_data.uuid.uid).IsSuccess(); + LOG_ERROR(Service_NFP, "Can't decode amiibo, has_backup= {}", has_backup); + return has_backup ? ResultCorruptedDataWithBackup : ResultCorruptedData; } + std::vector data(sizeof(NFP::EncryptedNTAG215File)); + memcpy(data.data(), &encrypted_tag_data, sizeof(encrypted_tag_data)); + WriteBackupData(encrypted_tag_data.uuid.uid, data); + device_state = DeviceState::TagMounted; mount_target = mount_target_; return ResultSuccess; @@ -470,6 +484,7 @@ Result NfcDevice::FlushWithBreak(NFP::BreakType break_type) { std::vector 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"); @@ -477,6 +492,7 @@ Result NfcDevice::FlushWithBreak(NFP::BreakType break_type) { } memcpy(data.data(), &encrypted_tag_data, sizeof(encrypted_tag_data)); + WriteBackupData(encrypted_tag_data.uuid.uid, data); } if (!npad_device->WriteNfc(data)) { @@ -488,7 +504,7 @@ Result NfcDevice::FlushWithBreak(NFP::BreakType break_type) { } Result NfcDevice::Restore() { - if (device_state != DeviceState::TagMounted) { + if (device_state != DeviceState::TagFound) { LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); if (device_state == DeviceState::TagRemoved) { return ResultTagRemoved; @@ -496,13 +512,59 @@ Result NfcDevice::Restore() { 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; + NFC::TagInfo tag_info{}; + std::array data{}; + Result result = GetTagInfo(tag_info, false); + + if (result.IsError()) { + return result; } - // TODO: Load amiibo from backup on system - LOG_ERROR(Service_NFP, "Not Implemented"); + result = ReadBackupData(tag_info.uuid, 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 ResultNotAnAmiibo; + } + + 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; } @@ -1132,13 +1194,69 @@ Result NfcDevice::BreakTag(NFP::BreakType break_type) { return FlushWithBreak(break_type); } -Result NfcDevice::ReadBackupData(std::span data) const { - // Not implemented +Result NfcDevice::HasBackup(const NFC::UniqueSerialNumber& uid) const { + 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, "")); + + if (!Common::FS::Exists(yuzu_amiibo_dir / backup_dir / file_name)) { + return ResultUnableToAccessBackupFile; + } + return ResultSuccess; } -Result NfcDevice::WriteBackupData(std::span data) { - // Not implemented +Result NfcDevice::ReadBackupData(const NFC::UniqueSerialNumber& uid, std::span data) const { + 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, "")); + + 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::WriteBackupData(const NFC::UniqueSerialNumber& uid, std::span data) { + 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, "")); + + if (HasBackup(uid).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; } @@ -1177,7 +1295,8 @@ NFP::AmiiboName NfcDevice::GetAmiiboName(const NFP::AmiiboSettings& settings) co return amiibo_name; } -void NfcDevice::SetAmiiboName(NFP::AmiiboSettings& settings, const NFP::AmiiboName& amiibo_name) { +void NfcDevice::SetAmiiboName(NFP::AmiiboSettings& settings, + const NFP::AmiiboName& amiibo_name) const { std::array settings_amiibo_name{}; // Convert from utf8 to utf16 @@ -1258,22 +1377,23 @@ void NfcDevice::UpdateRegisterInfoCrc() { tag_data.register_info_crc = crc.checksum(); } -void NfcDevice::BuildAmiiboWithoutKeys() { +void NfcDevice::BuildAmiiboWithoutKeys(NFP::NTAG215File& stubbed_tag_data, + const NFP::EncryptedNTAG215File& encrypted_file) const { Service::Mii::MiiManager manager; - auto& settings = tag_data.settings; + auto& settings = stubbed_tag_data.settings; - tag_data = NFP::AmiiboCrypto::NfcDataToEncodedData(encrypted_tag_data); + stubbed_tag_data = NFP::AmiiboCrypto::NfcDataToEncodedData(encrypted_file); // Common info - tag_data.write_counter = 0; - tag_data.amiibo_version = 0; + 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()); - tag_data.owner_mii = manager.BuildFromStoreData(manager.BuildDefault(0)); + stubbed_tag_data.owner_mii = manager.BuildFromStoreData(manager.BuildDefault(0)); // Admin info settings.settings.amiibo_initialized.Assign(1); diff --git a/src/core/hle/service/nfc/common/device.h b/src/core/hle/service/nfc/common/device.h index 6a37e8458..6f049b687 100644 --- a/src/core/hle/service/nfc/common/device.h +++ b/src/core/hle/service/nfc/common/device.h @@ -86,8 +86,9 @@ public: Result GetAll(NFP::NfpData& data) const; Result SetAll(const NFP::NfpData& data); Result BreakTag(NFP::BreakType break_type); - Result ReadBackupData(std::span data) const; - Result WriteBackupData(std::span data); + Result HasBackup(const NFC::UniqueSerialNumber& uid) const; + Result ReadBackupData(const NFC::UniqueSerialNumber& uid, std::span data) const; + Result WriteBackupData(const NFC::UniqueSerialNumber& uid, std::span data); Result WriteNtf(std::span data); u64 GetHandle() const; @@ -103,14 +104,15 @@ private: void CloseNfcTag(); NFP::AmiiboName GetAmiiboName(const NFP::AmiiboSettings& settings) const; - void SetAmiiboName(NFP::AmiiboSettings& settings, const NFP::AmiiboName& amiibo_name); + void SetAmiiboName(NFP::AmiiboSettings& settings, const NFP::AmiiboName& amiibo_name) const; NFP::AmiiboDate GetAmiiboDate(s64 posix_time) const; u64 GetCurrentPosixTime() const; u64 RemoveVersionByte(u64 application_id) const; void UpdateSettingsCrc(); void UpdateRegisterInfoCrc(); - void BuildAmiiboWithoutKeys(); + void BuildAmiiboWithoutKeys(NFP::NTAG215File& stubbed_tag_data, + const NFP::EncryptedNTAG215File& encrypted_file) const; bool is_controller_set{}; int callback_key; diff --git a/src/core/hle/service/nfc/common/device_manager.cpp b/src/core/hle/service/nfc/common/device_manager.cpp index d5deaaf27..cffd602df 100644 --- a/src/core/hle/service/nfc/common/device_manager.cpp +++ b/src/core/hle/service/nfc/common/device_manager.cpp @@ -543,9 +543,14 @@ Result DeviceManager::ReadBackupData(u64 device_handle, std::span data) cons std::shared_ptr device = nullptr; auto result = GetDeviceHandle(device_handle, device); + NFC::TagInfo tag_info{}; if (result.IsSuccess()) { - result = device->ReadBackupData(data); + result = device->GetTagInfo(tag_info, false); + } + + if (result.IsSuccess()) { + result = device->ReadBackupData(tag_info.uuid, data); result = VerifyDeviceResult(device, result); } @@ -557,9 +562,14 @@ Result DeviceManager::WriteBackupData(u64 device_handle, std::span dat std::shared_ptr device = nullptr; auto result = GetDeviceHandle(device_handle, device); + NFC::TagInfo tag_info{}; + + if (result.IsSuccess()) { + result = device->GetTagInfo(tag_info, false); + } if (result.IsSuccess()) { - result = device->WriteBackupData(data); + result = device->WriteBackupData(tag_info.uuid, data); result = VerifyDeviceResult(device, result); } diff --git a/src/core/hle/service/nfc/nfc_interface.cpp b/src/core/hle/service/nfc/nfc_interface.cpp index 0fa29d398..198d0f2b9 100644 --- a/src/core/hle/service/nfc/nfc_interface.cpp +++ b/src/core/hle/service/nfc/nfc_interface.cpp @@ -302,7 +302,7 @@ Result NfcInterface::TranslateResultToServiceError(Result result) const { return TranslateResultToNfp(result); } default: - if (result != ResultUnknown216) { + if (result != ResultBackupPathAlreadyExist) { return result; } return ResultUnknown74; @@ -343,6 +343,9 @@ Result NfcInterface::TranslateResultToNfp(Result result) const { if (result == ResultApplicationAreaIsNotInitialized) { return NFP::ResultApplicationAreaIsNotInitialized; } + if (result == ResultCorruptedDataWithBackup) { + return NFP::ResultCorruptedDataWithBackup; + } if (result == ResultCorruptedData) { return NFP::ResultCorruptedData; } @@ -355,6 +358,9 @@ Result NfcInterface::TranslateResultToNfp(Result result) const { if (result == ResultNotAnAmiibo) { return NFP::ResultNotAnAmiibo; } + if (result == ResultUnableToAccessBackupFile) { + return NFP::ResultUnableToAccessBackupFile; + } LOG_WARNING(Service_NFC, "Result conversion not handled"); return result; } diff --git a/src/core/hle/service/nfc/nfc_result.h b/src/core/hle/service/nfc/nfc_result.h index 917d79ef8..59a808740 100644 --- a/src/core/hle/service/nfc/nfc_result.h +++ b/src/core/hle/service/nfc/nfc_result.h @@ -9,20 +9,22 @@ namespace Service::NFC { constexpr Result ResultDeviceNotFound(ErrorModule::NFC, 64); constexpr Result ResultInvalidArgument(ErrorModule::NFC, 65); -constexpr Result ResultWrongApplicationAreaSize(ErrorModule::NFP, 68); +constexpr Result ResultWrongApplicationAreaSize(ErrorModule::NFC, 68); constexpr Result ResultWrongDeviceState(ErrorModule::NFC, 73); constexpr Result ResultUnknown74(ErrorModule::NFC, 74); constexpr Result ResultUnknown76(ErrorModule::NFC, 76); constexpr Result ResultNfcNotInitialized(ErrorModule::NFC, 77); constexpr Result ResultNfcDisabled(ErrorModule::NFC, 80); -constexpr Result ResultWriteAmiiboFailed(ErrorModule::NFP, 88); +constexpr Result ResultWriteAmiiboFailed(ErrorModule::NFC, 88); constexpr Result ResultTagRemoved(ErrorModule::NFC, 97); -constexpr Result ResultRegistrationIsNotInitialized(ErrorModule::NFP, 120); -constexpr Result ResultApplicationAreaIsNotInitialized(ErrorModule::NFP, 128); -constexpr Result ResultCorruptedData(ErrorModule::NFP, 144); -constexpr Result ResultWrongApplicationAreaId(ErrorModule::NFP, 152); -constexpr Result ResultApplicationAreaExist(ErrorModule::NFP, 168); -constexpr Result ResultNotAnAmiibo(ErrorModule::NFP, 178); -constexpr Result ResultUnknown216(ErrorModule::NFC, 216); +constexpr Result ResultUnableToAccessBackupFile(ErrorModule::NFC, 113); +constexpr Result ResultRegistrationIsNotInitialized(ErrorModule::NFC, 120); +constexpr Result ResultApplicationAreaIsNotInitialized(ErrorModule::NFC, 128); +constexpr Result ResultCorruptedDataWithBackup(ErrorModule::NFC, 136); +constexpr Result ResultCorruptedData(ErrorModule::NFC, 144); +constexpr Result ResultWrongApplicationAreaId(ErrorModule::NFC, 152); +constexpr Result ResultApplicationAreaExist(ErrorModule::NFC, 168); +constexpr Result ResultNotAnAmiibo(ErrorModule::NFC, 178); +constexpr Result ResultBackupPathAlreadyExist(ErrorModule::NFC, 216); } // namespace Service::NFC diff --git a/src/core/hle/service/nfp/nfp_interface.cpp b/src/core/hle/service/nfp/nfp_interface.cpp index 21d159154..34ef9d82d 100644 --- a/src/core/hle/service/nfp/nfp_interface.cpp +++ b/src/core/hle/service/nfp/nfp_interface.cpp @@ -126,7 +126,7 @@ void Interface::Flush(HLERequestContext& ctx) { void Interface::Restore(HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; const auto device_handle{rp.Pop()}; - LOG_WARNING(Service_NFP, "(STUBBED) called, device_handle={}", device_handle); + LOG_INFO(Service_NFP, "called, device_handle={}", device_handle); auto result = GetManager()->Restore(device_handle); result = TranslateResultToServiceError(result); @@ -394,7 +394,7 @@ void Interface::BreakTag(HLERequestContext& ctx) { void Interface::ReadBackupData(HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; const auto device_handle{rp.Pop()}; - LOG_WARNING(Service_NFP, "(STUBBED) called, device_handle={}", device_handle); + LOG_INFO(Service_NFP, "called, device_handle={}", device_handle); std::vector backup_data{}; auto result = GetManager()->ReadBackupData(device_handle, backup_data); @@ -412,7 +412,7 @@ void Interface::WriteBackupData(HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; const auto device_handle{rp.Pop()}; const auto backup_data_buffer{ctx.ReadBuffer()}; - LOG_WARNING(Service_NFP, "(STUBBED) called, device_handle={}", device_handle); + LOG_INFO(Service_NFP, "called, device_handle={}", device_handle); auto result = GetManager()->WriteBackupData(device_handle, backup_data_buffer); result = TranslateResultToServiceError(result); diff --git a/src/core/hle/service/nfp/nfp_result.h b/src/core/hle/service/nfp/nfp_result.h index 4c126cd81..618533843 100644 --- a/src/core/hle/service/nfp/nfp_result.h +++ b/src/core/hle/service/nfp/nfp_result.h @@ -17,9 +17,11 @@ constexpr Result ResultWriteAmiiboFailed(ErrorModule::NFP, 88); constexpr Result ResultTagRemoved(ErrorModule::NFP, 97); constexpr Result ResultRegistrationIsNotInitialized(ErrorModule::NFP, 120); constexpr Result ResultApplicationAreaIsNotInitialized(ErrorModule::NFP, 128); +constexpr Result ResultCorruptedDataWithBackup(ErrorModule::NFP, 136); constexpr Result ResultCorruptedData(ErrorModule::NFP, 144); constexpr Result ResultWrongApplicationAreaId(ErrorModule::NFP, 152); constexpr Result ResultApplicationAreaExist(ErrorModule::NFP, 168); constexpr Result ResultNotAnAmiibo(ErrorModule::NFP, 178); +constexpr Result ResultUnableToAccessBackupFile(ErrorModule::NFP, 200); } // namespace Service::NFP -- cgit v1.2.3