// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later #pragma once #include #include "common/swap.h" #include "core/hle/service/mii/types/char_info.h" #include "core/hle/service/mii/types/store_data.h" #include "core/hle/service/mii/types/ver3_store_data.h" #include "core/hle/service/nfc/nfc_types.h" namespace Service::NFP { static constexpr std::size_t amiibo_name_length = 0xA; static constexpr std::size_t application_id_version_offset = 0x1c; static constexpr std::size_t counter_limit = 0xffff; // This is nn::nfp::ModelType enum class ModelType : u32 { Amiibo, }; // This is nn::nfp::MountTarget enum class MountTarget : u32 { None, Rom, Ram, All, }; enum class AmiiboType : u8 { Figure, Card, Yarn, }; enum class AmiiboSeries : u8 { SuperSmashBros, SuperMario, ChibiRobo, YoshiWoollyWorld, Splatoon, AnimalCrossing, EightBitMario, Skylanders, Unknown8, TheLegendOfZelda, ShovelKnight, Unknown11, Kiby, Pokemon, MarioSportsSuperstars, MonsterHunter, BoxBoy, Pikmin, FireEmblem, Metroid, Others, MegaMan, Diablo, }; enum class AppAreaVersion : u8 { Nintendo3DS = 0, NintendoWiiU = 1, Nintendo3DSv2 = 2, NintendoSwitch = 3, NotSet = 0xFF, }; enum class BreakType : u32 { Normal, Unknown1, Unknown2, }; enum class WriteType : u32 { Unknown0, Unknown1, }; enum class CabinetMode : u8 { StartNicknameAndOwnerSettings, StartGameDataEraser, StartRestorer, StartFormatter, }; using UuidPart = std::array; using HashData = std::array; using ApplicationArea = std::array; using AmiiboName = std::array; // This is nn::nfp::TagInfo using TagInfo = NFC::TagInfo; struct NtagTagUuid { UuidPart part1; UuidPart part2; u8 nintendo_id; }; static_assert(sizeof(NtagTagUuid) == 7, "NtagTagUuid is an invalid size"); struct TagUuid { UuidPart part1; u8 crc_check1; UuidPart part2; u8 nintendo_id; }; static_assert(sizeof(TagUuid) == 8, "TagUuid is an invalid size"); struct WriteDate { u16 year; u8 month; u8 day; }; static_assert(sizeof(WriteDate) == 0x4, "WriteDate is an invalid size"); struct AmiiboDate { u16 raw_date{}; u16 GetValue() const { return Common::swap16(raw_date); } u16 GetYear() const { return static_cast(((GetValue() & 0xFE00) >> 9) + 2000); } u8 GetMonth() const { return static_cast((GetValue() & 0x01E0) >> 5); } u8 GetDay() const { return static_cast(GetValue() & 0x001F); } WriteDate GetWriteDate() const { if (!IsValidDate()) { return { .year = 2000, .month = 1, .day = 1, }; } return { .year = GetYear(), .month = GetMonth(), .day = GetDay(), }; } 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); } void SetMonth(u8 month) { const u16 month_converted = static_cast(month << 5); raw_date = Common::swap16((GetValue() & ~0x01E0) | month_converted); } void SetDay(u8 day) { const u16 day_converted = static_cast(day); raw_date = Common::swap16((GetValue() & ~0x001F) | day_converted); } bool IsValidDate() const { const bool is_day_valid = GetDay() > 0 && GetDay() < 32; const bool is_month_valid = GetMonth() > 0 && GetMonth() < 13; const bool is_year_valid = GetYear() >= 2000; return is_year_valid && is_month_valid && is_day_valid; } }; static_assert(sizeof(AmiiboDate) == 2, "AmiiboDate is an invalid size"); struct Settings { union { u8 raw{}; BitField<0, 4, u8> font_region; BitField<4, 1, u8> amiibo_initialized; BitField<5, 1, u8> appdata_initialized; }; }; static_assert(sizeof(Settings) == 1, "AmiiboDate is an invalid size"); struct AmiiboSettings { Settings settings; u8 country_code_id; u16_be crc_counter; // Incremented each time crc is changed AmiiboDate init_date; AmiiboDate write_date; u32_be crc; std::array amiibo_name; // UTF-16 text }; static_assert(sizeof(AmiiboSettings) == 0x20, "AmiiboSettings is an invalid size"); struct AmiiboModelInfo { u16 character_id; u8 character_variant; AmiiboType amiibo_type; u16_be model_number; AmiiboSeries series; NFC::PackedTagType tag_type; INSERT_PADDING_BYTES(0x4); // Unknown }; static_assert(sizeof(AmiiboModelInfo) == 0xC, "AmiiboModelInfo is an invalid size"); struct NTAG215Password { u32 PWD; // Password to allow write access u16 PACK; // Password acknowledge reply u16 RFUI; // Reserved for future use }; static_assert(sizeof(NTAG215Password) == 0x8, "NTAG215Password is an invalid size"); #pragma pack(1) struct EncryptedAmiiboFile { u8 constant_value; // Must be A5 u16_be write_counter; // Number of times the amiibo has been written? u8 amiibo_version; // Amiibo file version AmiiboSettings settings; // Encrypted amiibo settings HashData hmac_tag; // Hash AmiiboModelInfo model_info; // Encrypted amiibo model info HashData keygen_salt; // Salt HashData hmac_data; // Hash Service::Mii::Ver3StoreData owner_mii; // Encrypted Mii data u64_be application_id; // Encrypted Game id u16_be application_write_counter; // Encrypted Counter u32_be application_area_id; // Encrypted Game id u8 application_id_byte; u8 unknown; Service::Mii::NfpStoreDataExtension mii_extension; std::array unknown2; u32_be register_info_crc; ApplicationArea application_area; // Encrypted Game data }; static_assert(sizeof(EncryptedAmiiboFile) == 0x1F8, "AmiiboFile is an invalid size"); struct NTAG215File { u8 uid_crc_check2; u8 internal_number; u16 static_lock; // Set defined pages as read only u32 compability_container; // Defines available memory HashData hmac_data; // Hash u8 constant_value; // Must be A5 u16_be write_counter; // Number of times the amiibo has been written? u8 amiibo_version; // Amiibo file version AmiiboSettings settings; Service::Mii::Ver3StoreData owner_mii; // Mii data u64_be application_id; // Game id u16_be application_write_counter; // Counter u32_be application_area_id; u8 application_id_byte; u8 unknown; Service::Mii::NfpStoreDataExtension mii_extension; std::array unknown2; u32_be register_info_crc; ApplicationArea application_area; // Encrypted Game data HashData hmac_tag; // Hash TagUuid uid; AmiiboModelInfo model_info; HashData keygen_salt; // Salt u32 dynamic_lock; // Dynamic lock u32 CFG0; // Defines memory protected by password u32 CFG1; // Defines number of verification attempts NTAG215Password password; // Password data }; static_assert(sizeof(NTAG215File) == 0x21C, "NTAG215File is an invalid size"); static_assert(std::is_trivially_copyable_v, "NTAG215File must be trivially copyable."); #pragma pack() struct EncryptedNTAG215File { TagUuid uuid; u8 uuid_crc_check2; u8 internal_number; u16 static_lock; // Set defined pages as read only u32 compability_container; // Defines available memory EncryptedAmiiboFile user_memory; // Writable data u32 dynamic_lock; // Dynamic lock u32 CFG0; // Defines memory protected by password u32 CFG1; // Defines number of verification attempts NTAG215Password password; // Password data }; static_assert(sizeof(EncryptedNTAG215File) == sizeof(NTAG215File), "EncryptedNTAG215File is an invalid size"); static_assert(std::is_trivially_copyable_v, "EncryptedNTAG215File must be trivially copyable."); // This is nn::nfp::CommonInfo struct CommonInfo { WriteDate last_write_date; u16 write_counter; u8 version; INSERT_PADDING_BYTES(0x1); u32 application_area_size; INSERT_PADDING_BYTES(0x34); }; static_assert(sizeof(CommonInfo) == 0x40, "CommonInfo is an invalid size"); // This is nn::nfp::ModelInfo struct ModelInfo { u16 character_id; u8 character_variant; AmiiboType amiibo_type; u16 model_number; AmiiboSeries series; INSERT_PADDING_BYTES(0x39); // Unknown }; static_assert(sizeof(ModelInfo) == 0x40, "ModelInfo is an invalid size"); // This is nn::nfp::RegisterInfo struct RegisterInfo { Service::Mii::CharInfo mii_char_info; WriteDate creation_date; AmiiboName amiibo_name; u8 font_region; INSERT_PADDING_BYTES(0x7A); }; static_assert(sizeof(RegisterInfo) == 0x100, "RegisterInfo is an invalid size"); // This is nn::nfp::RegisterInfoPrivate struct RegisterInfoPrivate { Service::Mii::StoreData mii_store_data; WriteDate creation_date; AmiiboName amiibo_name; u8 font_region; INSERT_PADDING_BYTES(0x8E); }; static_assert(sizeof(RegisterInfoPrivate) == 0x100, "RegisterInfoPrivate is an invalid size"); // This is nn::nfp::AdminInfo struct AdminInfo { u64 application_id; u32 application_area_id; u16 crc_change_counter; u8 flags; NFC::PackedTagType tag_type; AppAreaVersion app_area_version; INSERT_PADDING_BYTES(0x7); INSERT_PADDING_BYTES(0x28); }; 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; NFC::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() } // namespace Service::NFP