From 307371e01d403c376a030d3a708d808659394bf5 Mon Sep 17 00:00:00 2001 From: Narr the Reg Date: Thu, 13 Apr 2023 22:23:49 -0600 Subject: [PATCH] service: nfp: Implement debug Interface --- src/core/hle/service/nfp/nfp.cpp | 12 +- src/core/hle/service/nfp/nfp_device.cpp | 172 ++++++++++++++++- src/core/hle/service/nfp/nfp_device.h | 10 + src/core/hle/service/nfp/nfp_interface.cpp | 204 +++++++++++++++++++++ src/core/hle/service/nfp/nfp_interface.h | 9 + src/core/hle/service/nfp/nfp_types.h | 45 +++++ 6 files changed, 444 insertions(+), 8 deletions(-) diff --git a/src/core/hle/service/nfp/nfp.cpp b/src/core/hle/service/nfp/nfp.cpp index 8a7365f176..2714f4bea0 100644 --- a/src/core/hle/service/nfp/nfp.cpp +++ b/src/core/hle/service/nfp/nfp.cpp @@ -90,8 +90,8 @@ public: explicit IDebug(Core::System& system_) : Interface(system_, "NFP:IDebug") { // clang-format off static const FunctionInfo functions[] = { - {0, nullptr, "InitializeDebug"}, - {1, nullptr, "FinalizeDebug"}, + {0, &IDebug::InitializeDebug, "InitializeDebug"}, + {1, &IDebug::FinalizeDebug, "FinalizeDebug"}, {2, &IDebug::ListDevices, "ListDevices"}, {3, &IDebug::StartDetection, "StartDetection"}, {4, &IDebug::StopDetection, "StopDetection"}, @@ -122,10 +122,10 @@ public: {104, &IDebug::DeleteRegisterInfo, "DeleteRegisterInfo"}, {105, &IDebug::DeleteApplicationArea, "DeleteApplicationArea"}, {106, &IDebug::ExistsApplicationArea, "ExistsApplicationArea"}, - {200, nullptr, "GetAll"}, - {201, nullptr, "SetAll"}, - {202, nullptr, "FlushDebug"}, - {203, nullptr, "BreakTag"}, + {200, &IDebug::GetAll, "GetAll"}, + {201, &IDebug::SetAll, "SetAll"}, + {202, &IDebug::FlushDebug, "FlushDebug"}, + {203, &IDebug::BreakTag, "BreakTag"}, {204, nullptr, "ReadBackupData"}, {205, nullptr, "WriteBackupData"}, {206, nullptr, "WriteNtf"}, diff --git a/src/core/hle/service/nfp/nfp_device.cpp b/src/core/hle/service/nfp/nfp_device.cpp index 5ed6d4181f..3f9af53c8f 100644 --- a/src/core/hle/service/nfp/nfp_device.cpp +++ b/src/core/hle/service/nfp/nfp_device.cpp @@ -240,6 +240,42 @@ Result NfpDevice::Flush() { tag_data.write_counter++; + FlushWithBreak(BreakType::Normal); + + is_data_moddified = false; + + return ResultSuccess; +} + +Result NfpDevice::FlushDebug() { + 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; + } + + tag_data.write_counter++; + + FlushWithBreak(BreakType::Normal); + + is_data_moddified = false; + + return ResultSuccess; +} + +Result NfpDevice::FlushWithBreak(BreakType break_type) { + if (break_type != BreakType::Normal) { + LOG_ERROR(Service_NFC, "Break type not implemented {}", break_type); + return WrongDeviceState; + } + std::vector data(sizeof(EncryptedNTAG215File)); if (is_plain_amiibo) { memcpy(data.data(), &tag_data, sizeof(tag_data)); @@ -257,8 +293,6 @@ Result NfpDevice::Flush() { return WriteAmiiboFailed; } - is_data_moddified = false; - return ResultSuccess; } @@ -857,6 +891,140 @@ Result NfpDevice::ExistApplicationArea(bool& has_application_area) { return ResultSuccess; } +Result NfpDevice::GetAll(NfpData& data) 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; + } + + CommonInfo common_info{}; + Service::Mii::MiiManager manager; + 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(application_id >> application_id_version_offset & 0xf), + .application_id_byte = tag_data.application_id_byte, + .application_area = tag_data.application_area, + }; + + return ResultSuccess; +} + +Result NfpDevice::SetAll(const NfpData& data) { + 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; + } + + 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 NfpDevice::BreakTag(BreakType break_type) { + 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; + } + + // TODO: Complete this implementation + + return FlushWithBreak(break_type); +} + +Result NfpDevice::ReadBackupData() { + // Not implemented + return ResultSuccess; +} + +Result NfpDevice::WriteBackupData() { + // Not implemented + return ResultSuccess; +} + +Result NfpDevice::WriteNtf() { + 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; + } + + // Not implemented + + return ResultSuccess; +} + u64 NfpDevice::GetHandle() const { // Generate a handle based of the npad id return static_cast(npad_id); diff --git a/src/core/hle/service/nfp/nfp_device.h b/src/core/hle/service/nfp/nfp_device.h index 0d778c1d7b..bab05538ad 100644 --- a/src/core/hle/service/nfp/nfp_device.h +++ b/src/core/hle/service/nfp/nfp_device.h @@ -41,7 +41,10 @@ public: Result StopDetection(); Result Mount(MountTarget mount_target); Result Unmount(); + Result Flush(); + Result FlushDebug(); + Result FlushWithBreak(BreakType break_type); Result GetTagInfo(TagInfo& tag_info) const; Result GetCommonInfo(CommonInfo& common_info) const; @@ -64,6 +67,13 @@ public: Result DeleteApplicationArea(); Result ExistApplicationArea(bool& has_application_area); + Result GetAll(NfpData& data) const; + Result SetAll(const NfpData& data); + Result BreakTag(BreakType break_type); + Result ReadBackupData(); + Result WriteBackupData(); + Result WriteNtf(); + u64 GetHandle() const; u32 GetApplicationAreaSize() const; DeviceState GetCurrentState() const; diff --git a/src/core/hle/service/nfp/nfp_interface.cpp b/src/core/hle/service/nfp/nfp_interface.cpp index d60f3cb97f..2ed8bb1baa 100644 --- a/src/core/hle/service/nfp/nfp_interface.cpp +++ b/src/core/hle/service/nfp/nfp_interface.cpp @@ -53,6 +53,19 @@ void Interface::InitializeSystem(HLERequestContext& ctx) { rb.Push(ResultSuccess); } +void Interface::InitializeDebug(HLERequestContext& ctx) { + LOG_INFO(Service_NFP, "called"); + + state = State::Initialized; + + for (auto& device : devices) { + device->Initialize(); + } + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + void Interface::Finalize(HLERequestContext& ctx) { LOG_INFO(Service_NFP, "called"); @@ -79,6 +92,19 @@ void Interface::FinalizeSystem(HLERequestContext& ctx) { rb.Push(ResultSuccess); } +void Interface::FinalizeDebug(HLERequestContext& ctx) { + LOG_INFO(Service_NFP, "called"); + + state = State::NonInitialized; + + for (auto& device : devices) { + device->Finalize(); + } + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + void Interface::ListDevices(HLERequestContext& ctx) { LOG_DEBUG(Service_NFP, "called"); @@ -833,6 +859,184 @@ void Interface::ExistsApplicationArea(HLERequestContext& ctx) { rb.Push(has_application_area); } +void Interface::GetAll(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto device_handle{rp.Pop()}; + LOG_DEBUG(Service_NFP, "called, device_handle={}", device_handle); + + if (state == State::NonInitialized) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(NfcDisabled); + return; + } + + auto device = GetNfpDevice(device_handle); + + if (!device.has_value()) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(DeviceNotFound); + return; + } + + NfpData data{}; + const auto result = device.value()->GetAll(data); + + ctx.WriteBuffer(data); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(result); +} + +void Interface::SetAll(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto device_handle{rp.Pop()}; + const auto nfp_data{ctx.ReadBuffer()}; + + LOG_DEBUG(Service_NFP, "called, device_handle={}", device_handle); + + if (state == State::NonInitialized) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(NfcDisabled); + return; + } + + auto device = GetNfpDevice(device_handle); + + if (!device.has_value()) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(DeviceNotFound); + return; + } + + NfpData data{}; + memcpy(&data, nfp_data.data(), sizeof(NfpData)); + + const auto result = device.value()->SetAll(data); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(result); +} + +void Interface::FlushDebug(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto device_handle{rp.Pop()}; + LOG_DEBUG(Service_NFP, "called, device_handle={}", device_handle); + + if (state == State::NonInitialized) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(NfcDisabled); + return; + } + + auto device = GetNfpDevice(device_handle); + + if (!device.has_value()) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(DeviceNotFound); + return; + } + + const auto result = device.value()->FlushDebug(); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(result); +} + +void Interface::BreakTag(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto device_handle{rp.Pop()}; + const auto break_type{rp.PopEnum()}; + LOG_DEBUG(Service_NFP, "called, device_handle={}, break_type={}", device_handle, break_type); + + if (state == State::NonInitialized) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(NfcDisabled); + return; + } + + auto device = GetNfpDevice(device_handle); + + if (!device.has_value()) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(DeviceNotFound); + return; + } + + const auto result = device.value()->BreakTag(break_type); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(result); +} + +void Interface::ReadBackupData(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto device_handle{rp.Pop()}; + LOG_DEBUG(Service_NFP, "called, device_handle={}", device_handle); + + if (state == State::NonInitialized) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(NfcDisabled); + return; + } + + auto device = GetNfpDevice(device_handle); + + if (!device.has_value()) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(DeviceNotFound); + return; + } + + const auto result = device.value()->ReadBackupData(); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(result); +} + +void Interface::WriteBackupData(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto device_handle{rp.Pop()}; + LOG_DEBUG(Service_NFP, "called, device_handle={}", device_handle); + + if (state == State::NonInitialized) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(NfcDisabled); + return; + } + + auto device = GetNfpDevice(device_handle); + + if (!device.has_value()) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(DeviceNotFound); + return; + } + + const auto result = device.value()->WriteBackupData(); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(result); +} + +void Interface::WriteNtf(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto device_handle{rp.Pop()}; + LOG_DEBUG(Service_NFP, "called, device_handle={}", device_handle); + + if (state == State::NonInitialized) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(NfcDisabled); + return; + } + + auto device = GetNfpDevice(device_handle); + + if (!device.has_value()) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(DeviceNotFound); + return; + } + + const auto result = device.value()->WriteNtf(); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(result); +} + std::optional> Interface::GetNfpDevice(u64 handle) { for (auto& device : devices) { if (device->GetHandle() == handle) { diff --git a/src/core/hle/service/nfp/nfp_interface.h b/src/core/hle/service/nfp/nfp_interface.h index 4affdbc0c9..616c94b061 100644 --- a/src/core/hle/service/nfp/nfp_interface.h +++ b/src/core/hle/service/nfp/nfp_interface.h @@ -20,8 +20,10 @@ public: void Initialize(HLERequestContext& ctx); void InitializeSystem(HLERequestContext& ctx); + void InitializeDebug(HLERequestContext& ctx); void Finalize(HLERequestContext& ctx); void FinalizeSystem(HLERequestContext& ctx); + void FinalizeDebug(HLERequestContext& ctx); void ListDevices(HLERequestContext& ctx); void StartDetection(HLERequestContext& ctx); void StopDetection(HLERequestContext& ctx); @@ -52,6 +54,13 @@ public: void DeleteRegisterInfo(HLERequestContext& ctx); void DeleteApplicationArea(HLERequestContext& ctx); void ExistsApplicationArea(HLERequestContext& ctx); + void GetAll(HLERequestContext& ctx); + void SetAll(HLERequestContext& ctx); + void FlushDebug(HLERequestContext& ctx); + void BreakTag(HLERequestContext& ctx); + void ReadBackupData(HLERequestContext& ctx); + void WriteBackupData(HLERequestContext& ctx); + void WriteNtf(HLERequestContext& ctx); private: enum class State : u32 { diff --git a/src/core/hle/service/nfp/nfp_types.h b/src/core/hle/service/nfp/nfp_types.h index 90429baa25..1ef047cee6 100644 --- a/src/core/hle/service/nfp/nfp_types.h +++ b/src/core/hle/service/nfp/nfp_types.h @@ -109,6 +109,12 @@ enum class AppAreaVersion : u8 { NotSet = 0xFF, }; +enum class BreakType : u32 { + Normal, + Unknown1, + Unknown2, +}; + enum class CabinetMode : u8 { StartNicknameAndOwnerSettings, StartGameDataEraser, @@ -181,6 +187,12 @@ struct AmiiboDate { }; } + void SetWriteDate(const WriteDate& write_date) { + SetYear(write_date.year); + SetMonth(write_date.month); + SetDay(write_date.day); + } + void SetYear(u16 year) { const u16 year_converted = static_cast((year - 2000) << 9); raw_date = Common::swap16((GetValue() & ~0xFE00) | year_converted); @@ -375,6 +387,39 @@ struct AdminInfo { }; static_assert(sizeof(AdminInfo) == 0x40, "AdminInfo is an invalid size"); +#pragma pack(1) +// This is nn::nfp::NfpData +struct NfpData { + u8 magic; + INSERT_PADDING_BYTES(0x1); + u8 write_counter; + INSERT_PADDING_BYTES(0x1); + u32 settings_crc; + INSERT_PADDING_BYTES(0x38); + CommonInfo common_info; + Service::Mii::Ver3StoreData mii_char_info; + Service::Mii::NfpStoreDataExtension mii_store_data_extension; + WriteDate creation_date; + std::array amiibo_name; + u16 amiibo_name_null_terminated; + Settings settings; + u8 unknown1; + u32 register_info_crc; + std::array unknown2; + INSERT_PADDING_BYTES(0x64); + u64 application_id; + u32 access_id; + u16 settings_crc_counter; + u8 font_region; + PackedTagType tag_type; + AppAreaVersion console_type; + u8 application_id_byte; + INSERT_PADDING_BYTES(0x2E); + ApplicationArea application_area; +}; +static_assert(sizeof(NfpData) == 0x298, "NfpData is an invalid size"); +#pragma pack() + struct SectorKey { MifareCmd command; u8 unknown; // Usually 1