|
|
|
@ -7,15 +7,132 @@ |
|
|
|
#include <array> |
|
|
|
#include <vector> |
|
|
|
|
|
|
|
#include "common/common_funcs.h" |
|
|
|
#include "core/hle/service/kernel_helpers.h" |
|
|
|
#include "core/hle/service/mii/mii_manager.h" |
|
|
|
#include "core/hle/service/service.h" |
|
|
|
|
|
|
|
namespace Kernel { |
|
|
|
class KEvent; |
|
|
|
} |
|
|
|
class KReadableEvent; |
|
|
|
} // namespace Kernel |
|
|
|
|
|
|
|
namespace Core::HID { |
|
|
|
enum class NpadIdType : u32; |
|
|
|
} // namespace Core::HID |
|
|
|
|
|
|
|
namespace Service::NFP { |
|
|
|
|
|
|
|
enum class ServiceType : u32 { |
|
|
|
User, |
|
|
|
Debug, |
|
|
|
System, |
|
|
|
}; |
|
|
|
|
|
|
|
enum class State : u32 { |
|
|
|
NonInitialized, |
|
|
|
Initialized, |
|
|
|
}; |
|
|
|
|
|
|
|
enum class DeviceState : u32 { |
|
|
|
Initialized, |
|
|
|
SearchingForTag, |
|
|
|
TagFound, |
|
|
|
TagRemoved, |
|
|
|
TagMounted, |
|
|
|
Unaviable, |
|
|
|
Finalized, |
|
|
|
}; |
|
|
|
|
|
|
|
enum class ModelType : u32 { |
|
|
|
Amiibo, |
|
|
|
}; |
|
|
|
|
|
|
|
enum class MountTarget : u32 { |
|
|
|
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 |
|
|
|
}; |
|
|
|
|
|
|
|
using TagUuid = std::array<u8, 10>; |
|
|
|
|
|
|
|
struct TagInfo { |
|
|
|
TagUuid uuid; |
|
|
|
u8 uuid_length; |
|
|
|
INSERT_PADDING_BYTES(0x15); |
|
|
|
s32 protocol; |
|
|
|
u32 tag_type; |
|
|
|
INSERT_PADDING_BYTES(0x30); |
|
|
|
}; |
|
|
|
static_assert(sizeof(TagInfo) == 0x58, "TagInfo is an invalid size"); |
|
|
|
|
|
|
|
struct CommonInfo { |
|
|
|
u16 last_write_year; |
|
|
|
u8 last_write_month; |
|
|
|
u8 last_write_day; |
|
|
|
u16 write_counter; |
|
|
|
u16 version; |
|
|
|
u32 application_area_size; |
|
|
|
INSERT_PADDING_BYTES(0x34); |
|
|
|
}; |
|
|
|
static_assert(sizeof(CommonInfo) == 0x40, "CommonInfo is an invalid size"); |
|
|
|
|
|
|
|
struct ModelInfo { |
|
|
|
u16 character_id; |
|
|
|
u8 character_variant; |
|
|
|
AmiiboType amiibo_type; |
|
|
|
u16 model_number; |
|
|
|
AmiiboSeries series; |
|
|
|
u8 fixed; // Must be 02 |
|
|
|
INSERT_PADDING_BYTES(0x4); // Unknown |
|
|
|
INSERT_PADDING_BYTES(0x20); // Probably a SHA256-(HMAC?) hash |
|
|
|
INSERT_PADDING_BYTES(0x14); // SHA256-HMAC |
|
|
|
}; |
|
|
|
static_assert(sizeof(ModelInfo) == 0x40, "ModelInfo is an invalid size"); |
|
|
|
|
|
|
|
struct RegisterInfo { |
|
|
|
Service::Mii::MiiInfo mii_char_info; |
|
|
|
u16 first_write_year; |
|
|
|
u8 first_write_month; |
|
|
|
u8 first_write_day; |
|
|
|
std::array<u8, 11> amiibo_name; |
|
|
|
u8 unknown; |
|
|
|
INSERT_PADDING_BYTES(0x98); |
|
|
|
}; |
|
|
|
static_assert(sizeof(RegisterInfo) == 0x100, "RegisterInfo is an invalid size"); |
|
|
|
|
|
|
|
class Module final { |
|
|
|
public: |
|
|
|
class Interface : public ServiceFramework<Interface> { |
|
|
|
@ -24,34 +141,131 @@ public: |
|
|
|
const char* name); |
|
|
|
~Interface() override; |
|
|
|
|
|
|
|
struct ModelInfo { |
|
|
|
std::array<u8, 0x8> amiibo_identification_block; |
|
|
|
INSERT_PADDING_BYTES(0x38); |
|
|
|
struct EncryptedAmiiboFile { |
|
|
|
u16 crypto_init; // Must be A5 XX |
|
|
|
u16 write_count; // Number of times the amiibo has been written? |
|
|
|
INSERT_PADDING_BYTES(0x20); // System crypts |
|
|
|
INSERT_PADDING_BYTES(0x20); // SHA256-(HMAC?) hash |
|
|
|
ModelInfo model_info; // This struct is bigger than documentation |
|
|
|
INSERT_PADDING_BYTES(0xC); // SHA256-HMAC |
|
|
|
INSERT_PADDING_BYTES(0x114); // section 1 encrypted buffer |
|
|
|
INSERT_PADDING_BYTES(0x54); // section 2 encrypted buffer |
|
|
|
}; |
|
|
|
static_assert(sizeof(EncryptedAmiiboFile) == 0x1F8, "AmiiboFile 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(ModelInfo) == 0x40, "ModelInfo is an invalid size"); |
|
|
|
static_assert(sizeof(NTAG215Password) == 0x8, "NTAG215Password is an invalid size"); |
|
|
|
|
|
|
|
struct AmiiboFile { |
|
|
|
std::array<u8, 10> uuid; |
|
|
|
INSERT_PADDING_BYTES(0x4a); |
|
|
|
ModelInfo model_info; |
|
|
|
struct NTAG215File { |
|
|
|
TagUuid uuid; // Unique serial number |
|
|
|
u16 lock_bytes; // 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(AmiiboFile) == 0x94, "AmiiboFile is an invalid size"); |
|
|
|
static_assert(sizeof(NTAG215File) == 0x21C, "NTAG215File is an invalid size"); |
|
|
|
|
|
|
|
void CreateUserInterface(Kernel::HLERequestContext& ctx); |
|
|
|
bool LoadAmiibo(const std::vector<u8>& buffer); |
|
|
|
Kernel::KReadableEvent& GetNFCEvent(); |
|
|
|
const AmiiboFile& GetAmiiboBuffer() const; |
|
|
|
void CloseAmiibo(); |
|
|
|
|
|
|
|
void Initialize(); |
|
|
|
void Finalize(); |
|
|
|
|
|
|
|
ResultCode StartDetection(s32 protocol_); |
|
|
|
ResultCode StopDetection(); |
|
|
|
ResultCode Mount(); |
|
|
|
ResultCode Unmount(); |
|
|
|
|
|
|
|
ResultCode GetTagInfo(TagInfo& tag_info) const; |
|
|
|
ResultCode GetCommonInfo(CommonInfo& common_info) const; |
|
|
|
ResultCode GetModelInfo(ModelInfo& model_info) const; |
|
|
|
ResultCode GetRegisterInfo(RegisterInfo& register_info) const; |
|
|
|
|
|
|
|
ResultCode OpenApplicationArea(u32 access_id); |
|
|
|
ResultCode GetApplicationArea(std::vector<u8>& data) const; |
|
|
|
ResultCode SetApplicationArea(const std::vector<u8>& data); |
|
|
|
ResultCode CreateApplicationArea(u32 access_id, const std::vector<u8>& data); |
|
|
|
|
|
|
|
u64 GetHandle() const; |
|
|
|
DeviceState GetCurrentState() const; |
|
|
|
Core::HID::NpadIdType GetNpadId() const; |
|
|
|
|
|
|
|
Kernel::KReadableEvent& GetActivateEvent() const; |
|
|
|
Kernel::KReadableEvent& GetDeactivateEvent() const; |
|
|
|
|
|
|
|
protected: |
|
|
|
std::shared_ptr<Module> module; |
|
|
|
|
|
|
|
private: |
|
|
|
/// Validates that the amiibo file is not corrupted |
|
|
|
bool IsAmiiboValid() const; |
|
|
|
|
|
|
|
bool AmiiboApplicationDataExist(u32 access_id) const; |
|
|
|
std::vector<u8> LoadAmiiboApplicationData(u32 access_id) const; |
|
|
|
void SaveAmiiboApplicationData(u32 access_id, const std::vector<u8>& data) const; |
|
|
|
|
|
|
|
/// return password needed to allow write access to protected memory |
|
|
|
u32 GetTagPassword(const TagUuid& uuid) const; |
|
|
|
|
|
|
|
const Core::HID::NpadIdType npad_id; |
|
|
|
|
|
|
|
DeviceState device_state{DeviceState::Unaviable}; |
|
|
|
KernelHelpers::ServiceContext service_context; |
|
|
|
Kernel::KEvent* nfc_tag_load; |
|
|
|
AmiiboFile amiibo{}; |
|
|
|
Kernel::KEvent* activate_event; |
|
|
|
Kernel::KEvent* deactivate_event; |
|
|
|
NTAG215File tag_data{}; |
|
|
|
s32 protocol; |
|
|
|
bool is_application_area_initialized{}; |
|
|
|
u32 application_area_id; |
|
|
|
std::vector<u8> application_area_data; |
|
|
|
}; |
|
|
|
}; |
|
|
|
|
|
|
|
class IUser final : public ServiceFramework<IUser> { |
|
|
|
public: |
|
|
|
explicit IUser(Module::Interface& nfp_interface_, Core::System& system_); |
|
|
|
|
|
|
|
private: |
|
|
|
void Initialize(Kernel::HLERequestContext& ctx); |
|
|
|
void Finalize(Kernel::HLERequestContext& ctx); |
|
|
|
void ListDevices(Kernel::HLERequestContext& ctx); |
|
|
|
void StartDetection(Kernel::HLERequestContext& ctx); |
|
|
|
void StopDetection(Kernel::HLERequestContext& ctx); |
|
|
|
void Mount(Kernel::HLERequestContext& ctx); |
|
|
|
void Unmount(Kernel::HLERequestContext& ctx); |
|
|
|
void OpenApplicationArea(Kernel::HLERequestContext& ctx); |
|
|
|
void GetApplicationArea(Kernel::HLERequestContext& ctx); |
|
|
|
void SetApplicationArea(Kernel::HLERequestContext& ctx); |
|
|
|
void CreateApplicationArea(Kernel::HLERequestContext& ctx); |
|
|
|
void GetTagInfo(Kernel::HLERequestContext& ctx); |
|
|
|
void GetRegisterInfo(Kernel::HLERequestContext& ctx); |
|
|
|
void GetCommonInfo(Kernel::HLERequestContext& ctx); |
|
|
|
void GetModelInfo(Kernel::HLERequestContext& ctx); |
|
|
|
void AttachActivateEvent(Kernel::HLERequestContext& ctx); |
|
|
|
void AttachDeactivateEvent(Kernel::HLERequestContext& ctx); |
|
|
|
void GetState(Kernel::HLERequestContext& ctx); |
|
|
|
void GetDeviceState(Kernel::HLERequestContext& ctx); |
|
|
|
void GetNpadId(Kernel::HLERequestContext& ctx); |
|
|
|
void GetApplicationAreaSize(Kernel::HLERequestContext& ctx); |
|
|
|
void AttachAvailabilityChangeEvent(Kernel::HLERequestContext& ctx); |
|
|
|
|
|
|
|
KernelHelpers::ServiceContext service_context; |
|
|
|
|
|
|
|
// TODO(german77): We should have a vector of interfaces |
|
|
|
Module::Interface& nfp_interface; |
|
|
|
|
|
|
|
State state{State::NonInitialized}; |
|
|
|
Kernel::KEvent* availability_change_event; |
|
|
|
}; |
|
|
|
|
|
|
|
void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system); |
|
|
|
|
|
|
|
} // namespace Service::NFP |