diff options
| -rw-r--r-- | src/common/fs/fs_paths.h | 1 | ||||
| -rw-r--r-- | src/common/fs/path_util.cpp | 1 | ||||
| -rw-r--r-- | src/common/fs/path_util.h | 1 | ||||
| -rw-r--r-- | src/core/hle/service/nfc/common/device.cpp | 160 | ||||
| -rw-r--r-- | src/core/hle/service/nfc/common/device.h | 10 | ||||
| -rw-r--r-- | src/core/hle/service/nfc/common/device_manager.cpp | 14 | ||||
| -rw-r--r-- | src/core/hle/service/nfc/nfc_interface.cpp | 8 | ||||
| -rw-r--r-- | src/core/hle/service/nfc/nfc_result.h | 20 | ||||
| -rw-r--r-- | src/core/hle/service/nfp/nfp_interface.cpp | 6 | ||||
| -rw-r--r-- | 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 <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" @@ -136,7 +141,7 @@ bool NfcDevice::LoadNfcTag(std::span<const u8> 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<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)) { -        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<u8> 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<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"); @@ -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<u8, sizeof(NFP::EncryptedNTAG215File)> 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<u8> 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<const u8> data) { -    // Not implemented +Result NfcDevice::ReadBackupData(const NFC::UniqueSerialNumber& uid, std::span<u8> 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<const u8> 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<char16_t, NFP::amiibo_name_length> 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<u8> data) const; -    Result WriteBackupData(std::span<const u8> data); +    Result HasBackup(const NFC::UniqueSerialNumber& uid) const; +    Result ReadBackupData(const NFC::UniqueSerialNumber& uid, std::span<u8> data) const; +    Result WriteBackupData(const NFC::UniqueSerialNumber& uid, std::span<const u8> data);      Result WriteNtf(std::span<const u8> 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<u8> data) cons      std::shared_ptr<NfcDevice> 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<const u8> dat      std::shared_ptr<NfcDevice> 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<u64>()}; -    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<u64>()}; -    LOG_WARNING(Service_NFP, "(STUBBED) called, device_handle={}", device_handle); +    LOG_INFO(Service_NFP, "called, device_handle={}", device_handle);      std::vector<u8> 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<u64>()};      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 | 
