diff options
Diffstat (limited to '')
-rw-r--r-- | src/core/hle/service/nfp/nfp_device.cpp | 235 |
1 files changed, 200 insertions, 35 deletions
diff --git a/src/core/hle/service/nfp/nfp_device.cpp b/src/core/hle/service/nfp/nfp_device.cpp index 7a6bbbba7..268337d2e 100644 --- a/src/core/hle/service/nfp/nfp_device.cpp +++ b/src/core/hle/service/nfp/nfp_device.cpp @@ -3,6 +3,17 @@ #include <array> +#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 "common/input.h" #include "common/logging/log.h" #include "common/string_util.h" @@ -11,8 +22,8 @@ #include "core/hid/emulated_controller.h" #include "core/hid/hid_core.h" #include "core/hid/hid_types.h" -#include "core/hle/ipc_helpers.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/nfp/amiibo_crypto.h" @@ -174,8 +185,8 @@ Result NfpDevice::StopDetection() { if (device_state == DeviceState::TagFound || device_state == DeviceState::TagMounted) { CloseAmiibo(); - return ResultSuccess; } + if (device_state == DeviceState::SearchingForTag || device_state == DeviceState::TagRemoved) { device_state = DeviceState::Initialized; return ResultSuccess; @@ -204,9 +215,7 @@ Result NfpDevice::Flush() { const auto& current_date = GetAmiiboDate(current_posix_time); if (settings.write_date.raw_date != current_date.raw_date) { settings.write_date = current_date; - settings.crc_counter++; - // TODO: Find how to calculate the crc check - // settings.crc = CalculateCRC(settings); + UpdateSettingsCrc(); } tag_data.write_counter++; @@ -318,7 +327,7 @@ Result NfpDevice::GetCommonInfo(CommonInfo& common_info) const { common_info = { .last_write_date = settings.write_date.GetWriteDate(), .write_counter = tag_data.write_counter, - .version = 0, + .version = tag_data.amiibo_version, .application_area_size = sizeof(ApplicationArea), }; return ResultSuccess; @@ -370,13 +379,95 @@ Result NfpDevice::GetRegisterInfo(RegisterInfo& register_info) const { .mii_char_info = manager.ConvertV3ToCharInfo(tag_data.owner_mii), .creation_date = settings.init_date.GetWriteDate(), .amiibo_name = GetAmiiboName(settings), - .font_region = {}, + .font_region = settings.settings.font_region, }; return ResultSuccess; } -Result NfpDevice::SetNicknameAndOwner(const AmiiboName& amiibo_name) { +Result NfpDevice::GetAdminInfo(AdminInfo& admin_info) const { + if (device_state != DeviceState::TagMounted) { + LOG_ERROR(Service_NFC, "Wrong device state {}", device_state); + if (device_state == DeviceState::TagRemoved) { + return TagRemoved; + } + return WrongDeviceState; + } + + if (mount_target == MountTarget::None || mount_target == MountTarget::Rom) { + LOG_ERROR(Service_NFC, "Amiibo is read only", device_state); + return WrongDeviceState; + } + + 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; + AppAreaVersion app_area_version = AppAreaVersion::NotSet; + if (tag_data.settings.settings.appdata_initialized != 0) { + application_id = tag_data.application_id; + app_area_version = + static_cast<AppAreaVersion>(application_id >> 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) << 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 NfpDevice::DeleteRegisterInfo() { + if (device_state != DeviceState::TagMounted) { + LOG_ERROR(Service_NFC, "Wrong device state {}", device_state); + if (device_state == DeviceState::TagRemoved) { + return TagRemoved; + } + return WrongDeviceState; + } + + if (mount_target == MountTarget::None || mount_target == MountTarget::Rom) { + LOG_ERROR(Service_NFC, "Amiibo is read only", device_state); + return WrongDeviceState; + } + + if (tag_data.settings.settings.amiibo_initialized == 0) { + return RegistrationIsNotInitialized; + } + + Common::TinyMT rng{}; + 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 NfpDevice::SetRegisterInfoPrivate(const AmiiboName& amiibo_name) { if (device_state != DeviceState::TagMounted) { LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); if (device_state == DeviceState::TagRemoved) { @@ -391,18 +482,25 @@ Result NfpDevice::SetNicknameAndOwner(const AmiiboName& amiibo_name) { } Service::Mii::MiiManager manager; + const auto mii = manager.BuildDefault(0); auto& settings = tag_data.settings; - settings.init_date = GetAmiiboDate(current_posix_time); - settings.write_date = GetAmiiboDate(current_posix_time); - settings.crc_counter++; - // TODO: Find how to calculate the crc check - // settings.crc = CalculateCRC(settings); + if (tag_data.settings.settings.amiibo_initialized == 0) { + settings.init_date = GetAmiiboDate(current_posix_time); + settings.write_date.raw_date = 0; + } SetAmiiboName(settings, amiibo_name); - tag_data.owner_mii = manager.ConvertCharInfoToV3(manager.BuildDefault(0)); + 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(); } @@ -425,23 +523,17 @@ Result NfpDevice::RestoreAmiibo() { return ResultSuccess; } -Result NfpDevice::DeleteAllData() { - const auto result = DeleteApplicationArea(); - if (result.IsError()) { - return result; - } +Result NfpDevice::Format() { + auto result1 = DeleteApplicationArea(); + auto result2 = DeleteRegisterInfo(); - if (device_state != DeviceState::TagMounted) { - LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); - if (device_state == DeviceState::TagRemoved) { - return TagRemoved; - } - return WrongDeviceState; + if (result1.IsError()) { + return result1; } - Common::TinyMT rng{}; - rng.GenerateRandomBytes(&tag_data.owner_mii, sizeof(tag_data.owner_mii)); - tag_data.settings.settings.amiibo_initialized.Assign(0); + if (result2.IsError()) { + return result2; + } return Flush(); } @@ -569,7 +661,10 @@ Result NfpDevice::SetApplicationArea(std::span<const u8> data) { rng.GenerateRandomBytes(tag_data.application_area.data() + data.size(), sizeof(ApplicationArea) - data.size()); - tag_data.applicaton_write_counter++; + if (tag_data.application_write_counter != counter_limit) { + tag_data.application_write_counter++; + } + is_data_moddified = true; return ResultSuccess; @@ -601,6 +696,11 @@ Result NfpDevice::RecreateApplicationArea(u32 access_id, std::span<const u8> dat return WrongDeviceState; } + if (is_app_area_open) { + LOG_ERROR(Service_NFP, "Application area is open"); + return WrongDeviceState; + } + if (mount_target == MountTarget::None || mount_target == MountTarget::Rom) { LOG_ERROR(Service_NFP, "Amiibo is read only", device_state); return WrongDeviceState; @@ -617,13 +717,23 @@ Result NfpDevice::RecreateApplicationArea(u32 access_id, std::span<const u8> dat rng.GenerateRandomBytes(tag_data.application_area.data() + data.size(), sizeof(ApplicationArea) - data.size()); - // TODO: Investigate why the title id needs to be moddified - tag_data.title_id = system.GetApplicationProcessProgramID(); - tag_data.title_id = tag_data.title_id | 0x30000000ULL; + if (tag_data.application_write_counter != counter_limit) { + tag_data.application_write_counter++; + } + + const u64 application_id = system.GetApplicationProcessProgramID(); + + tag_data.application_id_byte = + static_cast<u8>(application_id >> application_id_version_offset & 0xf); + tag_data.application_id = + RemoveVersionByte(application_id) | + (static_cast<u64>(AppAreaVersion::NintendoSwitch) << application_id_version_offset); tag_data.settings.settings.appdata_initialized.Assign(1); tag_data.application_area_id = access_id; - tag_data.applicaton_write_counter++; tag_data.unknown = {}; + tag_data.unknown2 = {}; + + UpdateRegisterInfoCrc(); return Flush(); } @@ -642,13 +752,25 @@ Result NfpDevice::DeleteApplicationArea() { return WrongDeviceState; } + if (tag_data.settings.settings.appdata_initialized == 0) { + return ApplicationAreaIsNotInitialized; + } + + if (tag_data.application_write_counter != counter_limit) { + tag_data.application_write_counter++; + } + Common::TinyMT rng{}; rng.GenerateRandomBytes(tag_data.application_area.data(), sizeof(ApplicationArea)); - rng.GenerateRandomBytes(&tag_data.title_id, sizeof(u64)); + 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.applicaton_write_counter++; tag_data.unknown = {}; + tag_data.unknown2 = {}; + is_app_area_open = false; + + UpdateRegisterInfoCrc(); return Flush(); } @@ -719,4 +841,47 @@ AmiiboDate NfpDevice::GetAmiiboDate(s64 posix_time) const { return amiibo_date; } +u64 NfpDevice::RemoveVersionByte(u64 application_id) const { + return application_id & ~(0xfULL << application_id_version_offset); +} + +void NfpDevice::UpdateSettingsCrc() { + auto& settings = tag_data.settings; + + if (settings.crc_counter != 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 NfpDevice::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(); +} + } // namespace Service::NFP |