diff options
| author | bunnei <bunneidev@gmail.com> | 2022-02-19 00:57:47 -0700 | 
|---|---|---|
| committer | GitHub <noreply@github.com> | 2022-02-19 00:57:47 -0700 | 
| commit | 298469526501b3bd576ff1ab53d089097506a2e7 (patch) | |
| tree | 269412e42ab54fdef3132c77f173a098867aceb7 | |
| parent | 83a84f1c2dce5a9f90404f2486da569e236f96fa (diff) | |
| parent | b57d61010f347e74875f0c8a1003b4f84fa7b062 (diff) | |
Merge pull request #7867 from german77/amiibo
nfp: Improve amiibo support
| -rw-r--r-- | src/core/hid/emulated_controller.cpp | 6 | ||||
| -rw-r--r-- | src/core/hid/emulated_controller.h | 11 | ||||
| -rw-r--r-- | src/core/hle/service/nfp/nfp.cpp | 916 | ||||
| -rw-r--r-- | src/core/hle/service/nfp/nfp.h | 242 | ||||
| -rw-r--r-- | src/yuzu/configuration/config.cpp | 6 | ||||
| -rw-r--r-- | src/yuzu/main.cpp | 20 | ||||
| -rw-r--r-- | src/yuzu/main.ui | 2 | 
7 files changed, 949 insertions, 254 deletions
| diff --git a/src/core/hid/emulated_controller.cpp b/src/core/hid/emulated_controller.cpp index 2bee173b3..7e05666d6 100644 --- a/src/core/hid/emulated_controller.cpp +++ b/src/core/hid/emulated_controller.cpp @@ -885,6 +885,12 @@ bool EmulatedController::TestVibration(std::size_t device_index) {      return SetVibration(device_index, DEFAULT_VIBRATION_VALUE);  } +bool EmulatedController::SetPollingMode(Common::Input::PollingMode polling_mode) { +    LOG_INFO(Service_HID, "Set polling mode {}", polling_mode); +    auto& output_device = output_devices[static_cast<std::size_t>(DeviceIndex::Right)]; +    return output_device->SetPollingMode(polling_mode) == Common::Input::PollingError::None; +} +  void EmulatedController::SetLedPattern() {      for (auto& device : output_devices) {          if (!device) { diff --git a/src/core/hid/emulated_controller.h b/src/core/hid/emulated_controller.h index d8642c5b3..aa52f9572 100644 --- a/src/core/hid/emulated_controller.h +++ b/src/core/hid/emulated_controller.h @@ -299,16 +299,23 @@ public:      /**       * Sends a specific vibration to the output device -     * @return returns true if vibration had no errors +     * @return true if vibration had no errors       */      bool SetVibration(std::size_t device_index, VibrationValue vibration);      /**       * Sends a small vibration to the output device -     * @return returns true if SetVibration was successfull +     * @return true if SetVibration was successfull       */      bool TestVibration(std::size_t device_index); +    /** +     * Sets the desired data to be polled from a controller +     * @param polling_mode type of input desired buttons, gyro, nfc, ir, etc. +     * @return true if SetPollingMode was successfull +     */ +    bool SetPollingMode(Common::Input::PollingMode polling_mode); +      /// Returns the led pattern corresponding to this emulated controller      LedPattern GetLedPattern() const; diff --git a/src/core/hle/service/nfp/nfp.cpp b/src/core/hle/service/nfp/nfp.cpp index 761d0d3c6..513107715 100644 --- a/src/core/hle/service/nfp/nfp.cpp +++ b/src/core/hle/service/nfp/nfp.cpp @@ -7,6 +7,9 @@  #include "common/logging/log.h"  #include "core/core.h" +#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/nfp/nfp.h" @@ -14,343 +17,790 @@  namespace Service::NFP {  namespace ErrCodes { -constexpr ResultCode ERR_NO_APPLICATION_AREA(ErrorModule::NFP, 152); +constexpr ResultCode DeviceNotFound(ErrorModule::NFP, 64); +constexpr ResultCode WrongDeviceState(ErrorModule::NFP, 73); +constexpr ResultCode ApplicationAreaIsNotInitialized(ErrorModule::NFP, 128); +constexpr ResultCode ApplicationAreaExist(ErrorModule::NFP, 168);  } // namespace ErrCodes -Module::Interface::Interface(std::shared_ptr<Module> module_, Core::System& system_, -                             const char* name) -    : ServiceFramework{system_, name}, module{std::move(module_)}, service_context{system_, -                                                                                   "NFP::IUser"} { -    nfc_tag_load = service_context.CreateEvent("NFP::IUser:NFCTagDetected"); -} - -Module::Interface::~Interface() { -    service_context.CloseEvent(nfc_tag_load); -} - -class IUser final : public ServiceFramework<IUser> { -public: -    explicit IUser(Module::Interface& nfp_interface_, Core::System& system_, -                   KernelHelpers::ServiceContext& service_context_) -        : ServiceFramework{system_, "NFP::IUser"}, nfp_interface{nfp_interface_}, -          service_context{service_context_} { -        static const FunctionInfo functions[] = { -            {0, &IUser::Initialize, "Initialize"}, -            {1, &IUser::Finalize, "Finalize"}, -            {2, &IUser::ListDevices, "ListDevices"}, -            {3, &IUser::StartDetection, "StartDetection"}, -            {4, &IUser::StopDetection, "StopDetection"}, -            {5, &IUser::Mount, "Mount"}, -            {6, &IUser::Unmount, "Unmount"}, -            {7, &IUser::OpenApplicationArea, "OpenApplicationArea"}, -            {8, &IUser::GetApplicationArea, "GetApplicationArea"}, -            {9, nullptr, "SetApplicationArea"}, -            {10, nullptr, "Flush"}, -            {11, nullptr, "Restore"}, -            {12, nullptr, "CreateApplicationArea"}, -            {13, &IUser::GetTagInfo, "GetTagInfo"}, -            {14, &IUser::GetRegisterInfo, "GetRegisterInfo"}, -            {15, &IUser::GetCommonInfo, "GetCommonInfo"}, -            {16, &IUser::GetModelInfo, "GetModelInfo"}, -            {17, &IUser::AttachActivateEvent, "AttachActivateEvent"}, -            {18, &IUser::AttachDeactivateEvent, "AttachDeactivateEvent"}, -            {19, &IUser::GetState, "GetState"}, -            {20, &IUser::GetDeviceState, "GetDeviceState"}, -            {21, &IUser::GetNpadId, "GetNpadId"}, -            {22, &IUser::GetApplicationAreaSize, "GetApplicationAreaSize"}, -            {23, &IUser::AttachAvailabilityChangeEvent, "AttachAvailabilityChangeEvent"}, -            {24, nullptr, "RecreateApplicationArea"}, -        }; -        RegisterHandlers(functions); +constexpr u32 ApplicationAreaSize = 0xD8; + +IUser::IUser(Module::Interface& nfp_interface_, Core::System& system_) +    : ServiceFramework{system_, "NFP::IUser"}, service_context{system_, service_name}, +      nfp_interface{nfp_interface_} { +    static const FunctionInfo functions[] = { +        {0, &IUser::Initialize, "Initialize"}, +        {1, &IUser::Finalize, "Finalize"}, +        {2, &IUser::ListDevices, "ListDevices"}, +        {3, &IUser::StartDetection, "StartDetection"}, +        {4, &IUser::StopDetection, "StopDetection"}, +        {5, &IUser::Mount, "Mount"}, +        {6, &IUser::Unmount, "Unmount"}, +        {7, &IUser::OpenApplicationArea, "OpenApplicationArea"}, +        {8, &IUser::GetApplicationArea, "GetApplicationArea"}, +        {9, &IUser::SetApplicationArea, "SetApplicationArea"}, +        {10, nullptr, "Flush"}, +        {11, nullptr, "Restore"}, +        {12, &IUser::CreateApplicationArea, "CreateApplicationArea"}, +        {13, &IUser::GetTagInfo, "GetTagInfo"}, +        {14, &IUser::GetRegisterInfo, "GetRegisterInfo"}, +        {15, &IUser::GetCommonInfo, "GetCommonInfo"}, +        {16, &IUser::GetModelInfo, "GetModelInfo"}, +        {17, &IUser::AttachActivateEvent, "AttachActivateEvent"}, +        {18, &IUser::AttachDeactivateEvent, "AttachDeactivateEvent"}, +        {19, &IUser::GetState, "GetState"}, +        {20, &IUser::GetDeviceState, "GetDeviceState"}, +        {21, &IUser::GetNpadId, "GetNpadId"}, +        {22, &IUser::GetApplicationAreaSize, "GetApplicationAreaSize"}, +        {23, &IUser::AttachAvailabilityChangeEvent, "AttachAvailabilityChangeEvent"}, +        {24, nullptr, "RecreateApplicationArea"}, +    }; +    RegisterHandlers(functions); -        deactivate_event = service_context.CreateEvent("NFP::IUser:DeactivateEvent"); -        availability_change_event = -            service_context.CreateEvent("NFP::IUser:AvailabilityChangeEvent"); -    } +    availability_change_event = service_context.CreateEvent("IUser:AvailabilityChangeEvent"); +} -    ~IUser() override { -        service_context.CloseEvent(deactivate_event); -        service_context.CloseEvent(availability_change_event); -    } +void IUser::Initialize(Kernel::HLERequestContext& ctx) { +    LOG_INFO(Service_NFC, "called"); -private: -    struct TagInfo { -        std::array<u8, 10> uuid; -        u8 uuid_length; // TODO(ogniK): Figure out if this is actual the uuid length or does it -                        // mean something else -        std::array<u8, 0x15> padding_1; -        u32_le protocol; -        u32_le tag_type; -        std::array<u8, 0x2c> padding_2; -    }; -    static_assert(sizeof(TagInfo) == 0x54, "TagInfo is an invalid size"); +    state = State::Initialized; -    enum class State : u32 { -        NonInitialized = 0, -        Initialized = 1, -    }; +    // TODO(german77): Loop through all interfaces +    nfp_interface.Initialize(); -    enum class DeviceState : u32 { -        Initialized = 0, -        SearchingForTag = 1, -        TagFound = 2, -        TagRemoved = 3, -        TagNearby = 4, -        Unknown5 = 5, -        Finalized = 6 -    }; +    IPC::ResponseBuilder rb{ctx, 2, 0}; +    rb.Push(ResultSuccess); +} -    struct CommonInfo { -        u16_be last_write_year; -        u8 last_write_month; -        u8 last_write_day; -        u16_be write_counter; -        u16_be version; -        u32_be application_area_size; -        INSERT_PADDING_BYTES(0x34); -    }; -    static_assert(sizeof(CommonInfo) == 0x40, "CommonInfo is an invalid size"); +void IUser::Finalize(Kernel::HLERequestContext& ctx) { +    LOG_INFO(Service_NFP, "called"); -    void Initialize(Kernel::HLERequestContext& ctx) { -        LOG_DEBUG(Service_NFC, "called"); +    state = State::NonInitialized; -        IPC::ResponseBuilder rb{ctx, 2, 0}; -        rb.Push(ResultSuccess); +    // TODO(german77): Loop through all interfaces +    nfp_interface.Finalize(); -        state = State::Initialized; -    } +    IPC::ResponseBuilder rb{ctx, 2}; +    rb.Push(ResultSuccess); +} -    void GetState(Kernel::HLERequestContext& ctx) { -        LOG_DEBUG(Service_NFC, "called"); +void IUser::ListDevices(Kernel::HLERequestContext& ctx) { +    LOG_INFO(Service_NFP, "called"); -        IPC::ResponseBuilder rb{ctx, 3, 0}; -        rb.Push(ResultSuccess); -        rb.PushRaw<u32>(static_cast<u32>(state)); +    std::vector<u64> devices; + +    // TODO(german77): Loop through all interfaces +    devices.push_back(nfp_interface.GetHandle()); + +    ctx.WriteBuffer(devices); + +    IPC::ResponseBuilder rb{ctx, 3}; +    rb.Push(ResultSuccess); +    rb.Push(static_cast<s32>(devices.size())); +} + +void IUser::StartDetection(Kernel::HLERequestContext& ctx) { +    IPC::RequestParser rp{ctx}; +    const auto device_handle{rp.Pop<u64>()}; +    const auto nfp_protocol{rp.Pop<s32>()}; +    LOG_INFO(Service_NFP, "called, device_handle={}, nfp_protocol={}", device_handle, nfp_protocol); + +    // TODO(german77): Loop through all interfaces +    if (device_handle == nfp_interface.GetHandle()) { +        const auto result = nfp_interface.StartDetection(nfp_protocol); +        IPC::ResponseBuilder rb{ctx, 2}; +        rb.Push(result); +        return;      } -    void ListDevices(Kernel::HLERequestContext& ctx) { -        IPC::RequestParser rp{ctx}; -        const u32 array_size = rp.Pop<u32>(); -        LOG_DEBUG(Service_NFP, "called, array_size={}", array_size); +    LOG_ERROR(Service_NFP, "Handle not found, device_handle={}", device_handle); -        ctx.WriteBuffer(device_handle); +    IPC::ResponseBuilder rb{ctx, 2}; +    rb.Push(ErrCodes::DeviceNotFound); +} -        IPC::ResponseBuilder rb{ctx, 3}; -        rb.Push(ResultSuccess); -        rb.Push<u32>(1); +void IUser::StopDetection(Kernel::HLERequestContext& ctx) { +    IPC::RequestParser rp{ctx}; +    const auto device_handle{rp.Pop<u64>()}; +    LOG_INFO(Service_NFP, "called, device_handle={}", device_handle); + +    // TODO(german77): Loop through all interfaces +    if (device_handle == nfp_interface.GetHandle()) { +        const auto result = nfp_interface.StopDetection(); +        IPC::ResponseBuilder rb{ctx, 2}; +        rb.Push(result); +        return;      } -    void GetNpadId(Kernel::HLERequestContext& ctx) { -        IPC::RequestParser rp{ctx}; -        const u64 dev_handle = rp.Pop<u64>(); -        LOG_DEBUG(Service_NFP, "called, dev_handle=0x{:X}", dev_handle); +    LOG_ERROR(Service_NFP, "Handle not found, device_handle={}", device_handle); -        IPC::ResponseBuilder rb{ctx, 3}; -        rb.Push(ResultSuccess); -        rb.Push<u32>(npad_id); +    IPC::ResponseBuilder rb{ctx, 2}; +    rb.Push(ErrCodes::DeviceNotFound); +} + +void IUser::Mount(Kernel::HLERequestContext& ctx) { +    IPC::RequestParser rp{ctx}; +    const auto device_handle{rp.Pop<u64>()}; +    const auto model_type{rp.PopEnum<ModelType>()}; +    const auto mount_target{rp.PopEnum<MountTarget>()}; +    LOG_INFO(Service_NFP, "called, device_handle={}, model_type={}, mount_target={}", device_handle, +             model_type, mount_target); + +    // TODO(german77): Loop through all interfaces +    if (device_handle == nfp_interface.GetHandle()) { +        const auto result = nfp_interface.Mount(); +        IPC::ResponseBuilder rb{ctx, 2}; +        rb.Push(result); +        return;      } -    void AttachActivateEvent(Kernel::HLERequestContext& ctx) { -        IPC::RequestParser rp{ctx}; -        const u64 dev_handle = rp.Pop<u64>(); -        LOG_DEBUG(Service_NFP, "called, dev_handle=0x{:X}", dev_handle); +    LOG_ERROR(Service_NFP, "Handle not found, device_handle={}", device_handle); -        IPC::ResponseBuilder rb{ctx, 2, 1}; -        rb.Push(ResultSuccess); -        rb.PushCopyObjects(nfp_interface.GetNFCEvent()); -        has_attached_handle = true; +    IPC::ResponseBuilder rb{ctx, 2}; +    rb.Push(ErrCodes::DeviceNotFound); +} + +void IUser::Unmount(Kernel::HLERequestContext& ctx) { +    IPC::RequestParser rp{ctx}; +    const auto device_handle{rp.Pop<u64>()}; +    LOG_INFO(Service_NFP, "called, device_handle={}", device_handle); + +    // TODO(german77): Loop through all interfaces +    if (device_handle == nfp_interface.GetHandle()) { +        const auto result = nfp_interface.Unmount(); +        IPC::ResponseBuilder rb{ctx, 2}; +        rb.Push(result); +        return;      } -    void AttachDeactivateEvent(Kernel::HLERequestContext& ctx) { -        IPC::RequestParser rp{ctx}; -        const u64 dev_handle = rp.Pop<u64>(); -        LOG_DEBUG(Service_NFP, "called, dev_handle=0x{:X}", dev_handle); +    LOG_ERROR(Service_NFP, "Handle not found, device_handle={}", device_handle); -        IPC::ResponseBuilder rb{ctx, 2, 1}; -        rb.Push(ResultSuccess); -        rb.PushCopyObjects(deactivate_event->GetReadableEvent()); -    } - -    void StopDetection(Kernel::HLERequestContext& ctx) { -        LOG_DEBUG(Service_NFP, "called"); - -        switch (device_state) { -        case DeviceState::TagFound: -        case DeviceState::TagNearby: -            deactivate_event->GetWritableEvent().Signal(); -            device_state = DeviceState::Initialized; -            break; -        case DeviceState::SearchingForTag: -        case DeviceState::TagRemoved: -            device_state = DeviceState::Initialized; -            break; -        default: -            break; -        } +    IPC::ResponseBuilder rb{ctx, 2}; +    rb.Push(ErrCodes::DeviceNotFound); +} + +void IUser::OpenApplicationArea(Kernel::HLERequestContext& ctx) { +    IPC::RequestParser rp{ctx}; +    const auto device_handle{rp.Pop<u64>()}; +    const auto access_id{rp.Pop<u32>()}; +    LOG_WARNING(Service_NFP, "(STUBBED) called, device_handle={}, access_id={}", device_handle, +                access_id); + +    // TODO(german77): Loop through all interfaces +    if (device_handle == nfp_interface.GetHandle()) { +        const auto result = nfp_interface.OpenApplicationArea(access_id);          IPC::ResponseBuilder rb{ctx, 2}; -        rb.Push(ResultSuccess); +        rb.Push(result); +        return;      } -    void GetDeviceState(Kernel::HLERequestContext& ctx) { -        LOG_DEBUG(Service_NFP, "called"); +    LOG_ERROR(Service_NFP, "Handle not found, device_handle={}", device_handle); + +    IPC::ResponseBuilder rb{ctx, 2}; +    rb.Push(ErrCodes::DeviceNotFound); +} + +void IUser::GetApplicationArea(Kernel::HLERequestContext& ctx) { +    IPC::RequestParser rp{ctx}; +    const auto device_handle{rp.Pop<u64>()}; +    LOG_INFO(Service_NFP, "called, device_handle={}", device_handle); +    // TODO(german77): Loop through all interfaces +    if (device_handle == nfp_interface.GetHandle()) { +        std::vector<u8> data{}; +        const auto result = nfp_interface.GetApplicationArea(data); +        ctx.WriteBuffer(data);          IPC::ResponseBuilder rb{ctx, 3}; -        rb.Push(ResultSuccess); -        rb.Push<u32>(static_cast<u32>(device_state)); +        rb.Push(result); +        rb.Push(static_cast<u32>(data.size())); +        return;      } -    void StartDetection(Kernel::HLERequestContext& ctx) { -        LOG_DEBUG(Service_NFP, "called"); +    LOG_ERROR(Service_NFP, "Handle not found, device_handle={}", device_handle); -        if (device_state == DeviceState::Initialized || device_state == DeviceState::TagRemoved) { -            device_state = DeviceState::SearchingForTag; -        } +    IPC::ResponseBuilder rb{ctx, 2}; +    rb.Push(ErrCodes::DeviceNotFound); +} + +void IUser::SetApplicationArea(Kernel::HLERequestContext& ctx) { +    IPC::RequestParser rp{ctx}; +    const auto device_handle{rp.Pop<u64>()}; +    const auto data{ctx.ReadBuffer()}; +    LOG_WARNING(Service_NFP, "(STUBBED) called, device_handle={}, data_size={}", device_handle, +                data.size()); + +    // TODO(german77): Loop through all interfaces +    if (device_handle == nfp_interface.GetHandle()) { +        const auto result = nfp_interface.SetApplicationArea(data);          IPC::ResponseBuilder rb{ctx, 2}; -        rb.Push(ResultSuccess); +        rb.Push(result); +        return;      } -    void GetTagInfo(Kernel::HLERequestContext& ctx) { -        LOG_DEBUG(Service_NFP, "called"); +    LOG_ERROR(Service_NFP, "Handle not found, device_handle={}", device_handle); +    IPC::ResponseBuilder rb{ctx, 2}; +    rb.Push(ErrCodes::DeviceNotFound); +} + +void IUser::CreateApplicationArea(Kernel::HLERequestContext& ctx) { +    IPC::RequestParser rp{ctx}; +    const auto device_handle{rp.Pop<u64>()}; +    const auto access_id{rp.Pop<u32>()}; +    const auto data{ctx.ReadBuffer()}; +    LOG_WARNING(Service_NFP, "(STUBBED) called, device_handle={}, data_size={}, access_id={}", +                device_handle, access_id, data.size()); + +    // TODO(german77): Loop through all interfaces +    if (device_handle == nfp_interface.GetHandle()) { +        const auto result = nfp_interface.CreateApplicationArea(access_id, data);          IPC::ResponseBuilder rb{ctx, 2}; -        const auto& amiibo = nfp_interface.GetAmiiboBuffer(); -        const TagInfo tag_info{ -            .uuid = amiibo.uuid, -            .uuid_length = static_cast<u8>(amiibo.uuid.size()), -            .padding_1 = {}, -            .protocol = 1, // TODO(ogniK): Figure out actual values -            .tag_type = 2, -            .padding_2 = {}, -        }; -        ctx.WriteBuffer(tag_info); -        rb.Push(ResultSuccess); +        rb.Push(result); +        return;      } -    void Mount(Kernel::HLERequestContext& ctx) { -        LOG_DEBUG(Service_NFP, "called"); +    LOG_ERROR(Service_NFP, "Handle not found, device_handle={}", device_handle); + +    IPC::ResponseBuilder rb{ctx, 2}; +    rb.Push(ErrCodes::DeviceNotFound); +} + +void IUser::GetTagInfo(Kernel::HLERequestContext& ctx) { +    IPC::RequestParser rp{ctx}; +    const auto device_handle{rp.Pop<u64>()}; +    LOG_INFO(Service_NFP, "called, device_handle={}", device_handle); -        device_state = DeviceState::TagNearby; +    // TODO(german77): Loop through all interfaces +    if (device_handle == nfp_interface.GetHandle()) { +        TagInfo tag_info{}; +        const auto result = nfp_interface.GetTagInfo(tag_info); +        ctx.WriteBuffer(tag_info);          IPC::ResponseBuilder rb{ctx, 2}; -        rb.Push(ResultSuccess); +        rb.Push(result); +        return;      } -    void GetModelInfo(Kernel::HLERequestContext& ctx) { -        LOG_DEBUG(Service_NFP, "called"); +    LOG_ERROR(Service_NFP, "Handle not found, device_handle={}", device_handle); +    IPC::ResponseBuilder rb{ctx, 2}; +    rb.Push(ErrCodes::DeviceNotFound); +} + +void IUser::GetRegisterInfo(Kernel::HLERequestContext& ctx) { +    IPC::RequestParser rp{ctx}; +    const auto device_handle{rp.Pop<u64>()}; +    LOG_INFO(Service_NFP, "called, device_handle={}", device_handle); + +    // TODO(german77): Loop through all interfaces +    if (device_handle == nfp_interface.GetHandle()) { +        RegisterInfo register_info{}; +        const auto result = nfp_interface.GetRegisterInfo(register_info); +        ctx.WriteBuffer(register_info);          IPC::ResponseBuilder rb{ctx, 2}; -        const auto& amiibo = nfp_interface.GetAmiiboBuffer(); -        ctx.WriteBuffer(amiibo.model_info); -        rb.Push(ResultSuccess); +        rb.Push(result); +        return;      } -    void Unmount(Kernel::HLERequestContext& ctx) { -        LOG_DEBUG(Service_NFP, "called"); +    LOG_ERROR(Service_NFP, "Handle not found, device_handle={}", device_handle); -        device_state = DeviceState::TagFound; +    IPC::ResponseBuilder rb{ctx, 2}; +    rb.Push(ErrCodes::DeviceNotFound); +} +void IUser::GetCommonInfo(Kernel::HLERequestContext& ctx) { +    IPC::RequestParser rp{ctx}; +    const auto device_handle{rp.Pop<u64>()}; +    LOG_INFO(Service_NFP, "called, device_handle={}", device_handle); + +    // TODO(german77): Loop through all interfaces +    if (device_handle == nfp_interface.GetHandle()) { +        CommonInfo common_info{}; +        const auto result = nfp_interface.GetCommonInfo(common_info); +        ctx.WriteBuffer(common_info);          IPC::ResponseBuilder rb{ctx, 2}; -        rb.Push(ResultSuccess); +        rb.Push(result); +        return;      } -    void Finalize(Kernel::HLERequestContext& ctx) { -        LOG_DEBUG(Service_NFP, "called"); +    LOG_ERROR(Service_NFP, "Handle not found, device_handle={}", device_handle); + +    IPC::ResponseBuilder rb{ctx, 2}; +    rb.Push(ErrCodes::DeviceNotFound); +} -        device_state = DeviceState::Finalized; +void IUser::GetModelInfo(Kernel::HLERequestContext& ctx) { +    IPC::RequestParser rp{ctx}; +    const auto device_handle{rp.Pop<u64>()}; +    LOG_INFO(Service_NFP, "called, device_handle={}", device_handle); +    // TODO(german77): Loop through all interfaces +    if (device_handle == nfp_interface.GetHandle()) { +        ModelInfo model_info{}; +        const auto result = nfp_interface.GetModelInfo(model_info); +        ctx.WriteBuffer(model_info);          IPC::ResponseBuilder rb{ctx, 2}; -        rb.Push(ResultSuccess); +        rb.Push(result); +        return;      } -    void AttachAvailabilityChangeEvent(Kernel::HLERequestContext& ctx) { -        LOG_WARNING(Service_NFP, "(STUBBED) called"); +    LOG_ERROR(Service_NFP, "Handle not found, device_handle={}", device_handle); + +    IPC::ResponseBuilder rb{ctx, 2}; +    rb.Push(ErrCodes::DeviceNotFound); +} +void IUser::AttachActivateEvent(Kernel::HLERequestContext& ctx) { +    IPC::RequestParser rp{ctx}; +    const auto device_handle{rp.Pop<u64>()}; +    LOG_DEBUG(Service_NFP, "called, device_handle={}", device_handle); + +    // TODO(german77): Loop through all interfaces +    if (device_handle == nfp_interface.GetHandle()) {          IPC::ResponseBuilder rb{ctx, 2, 1};          rb.Push(ResultSuccess); -        rb.PushCopyObjects(availability_change_event->GetReadableEvent()); +        rb.PushCopyObjects(nfp_interface.GetActivateEvent()); +        return;      } -    void GetRegisterInfo(Kernel::HLERequestContext& ctx) { -        LOG_WARNING(Service_NFP, "(STUBBED) called"); +    LOG_ERROR(Service_NFP, "Handle not found, device_handle={}", device_handle); -        // TODO(ogniK): Pull Mii and owner data from amiibo +    IPC::ResponseBuilder rb{ctx, 2}; +    rb.Push(ErrCodes::DeviceNotFound); +} -        IPC::ResponseBuilder rb{ctx, 2}; +void IUser::AttachDeactivateEvent(Kernel::HLERequestContext& ctx) { +    IPC::RequestParser rp{ctx}; +    const auto device_handle{rp.Pop<u64>()}; +    LOG_DEBUG(Service_NFP, "called, device_handle={}", device_handle); + +    // TODO(german77): Loop through all interfaces +    if (device_handle == nfp_interface.GetHandle()) { +        IPC::ResponseBuilder rb{ctx, 2, 1};          rb.Push(ResultSuccess); +        rb.PushCopyObjects(nfp_interface.GetDeactivateEvent()); +        return;      } -    void GetCommonInfo(Kernel::HLERequestContext& ctx) { -        LOG_WARNING(Service_NFP, "(STUBBED) called"); +    LOG_ERROR(Service_NFP, "Handle not found, device_handle={}", device_handle); -        // TODO(ogniK): Pull common information from amiibo +    IPC::ResponseBuilder rb{ctx, 2}; +    rb.Push(ErrCodes::DeviceNotFound); +} -        CommonInfo common_info{}; -        common_info.application_area_size = 0; -        ctx.WriteBuffer(common_info); +void IUser::GetState(Kernel::HLERequestContext& ctx) { +    LOG_DEBUG(Service_NFC, "called"); -        IPC::ResponseBuilder rb{ctx, 2}; +    IPC::ResponseBuilder rb{ctx, 3, 0}; +    rb.Push(ResultSuccess); +    rb.PushEnum(state); +} + +void IUser::GetDeviceState(Kernel::HLERequestContext& ctx) { +    IPC::RequestParser rp{ctx}; +    const auto device_handle{rp.Pop<u64>()}; +    LOG_DEBUG(Service_NFP, "called, device_handle={}", device_handle); + +    // TODO(german77): Loop through all interfaces +    if (device_handle == nfp_interface.GetHandle()) { +        IPC::ResponseBuilder rb{ctx, 3};          rb.Push(ResultSuccess); +        rb.PushEnum(nfp_interface.GetCurrentState()); +        return;      } -    void OpenApplicationArea(Kernel::HLERequestContext& ctx) { -        LOG_WARNING(Service_NFP, "(STUBBED) called"); -        IPC::ResponseBuilder rb{ctx, 2}; -        rb.Push(ErrCodes::ERR_NO_APPLICATION_AREA); -    } +    LOG_ERROR(Service_NFP, "Handle not found, device_handle={}", device_handle); -    void GetApplicationAreaSize(Kernel::HLERequestContext& ctx) { -        LOG_WARNING(Service_NFP, "(STUBBED) called"); -        // We don't need to worry about this since we can just open the file +    IPC::ResponseBuilder rb{ctx, 2}; +    rb.Push(ErrCodes::DeviceNotFound); +} + +void IUser::GetNpadId(Kernel::HLERequestContext& ctx) { +    IPC::RequestParser rp{ctx}; +    const auto device_handle{rp.Pop<u64>()}; +    LOG_DEBUG(Service_NFP, "called, device_handle={}", device_handle); + +    // TODO(german77): Loop through all interfaces +    if (device_handle == nfp_interface.GetHandle()) {          IPC::ResponseBuilder rb{ctx, 3};          rb.Push(ResultSuccess); -        rb.PushRaw<u32>(0); // This is from the GetCommonInfo stub +        rb.PushEnum(nfp_interface.GetNpadId()); +        return;      } -    void GetApplicationArea(Kernel::HLERequestContext& ctx) { -        LOG_WARNING(Service_NFP, "(STUBBED) called"); +    LOG_ERROR(Service_NFP, "Handle not found, device_handle={}", device_handle); + +    IPC::ResponseBuilder rb{ctx, 2}; +    rb.Push(ErrCodes::DeviceNotFound); +} -        // TODO(ogniK): Pull application area from amiibo +void IUser::GetApplicationAreaSize(Kernel::HLERequestContext& ctx) { +    IPC::RequestParser rp{ctx}; +    const auto device_handle{rp.Pop<u64>()}; +    LOG_DEBUG(Service_NFP, "called, device_handle={}", device_handle); +    // TODO(german77): Loop through all interfaces +    if (device_handle == nfp_interface.GetHandle()) {          IPC::ResponseBuilder rb{ctx, 3};          rb.Push(ResultSuccess); -        rb.PushRaw<u32>(0); // This is from the GetCommonInfo stub +        rb.Push(ApplicationAreaSize); +        return;      } -    Module::Interface& nfp_interface; -    KernelHelpers::ServiceContext& service_context; +    LOG_ERROR(Service_NFP, "Handle not found, device_handle={}", device_handle); + +    IPC::ResponseBuilder rb{ctx, 2}; +    rb.Push(ErrCodes::DeviceNotFound); +} + +void IUser::AttachAvailabilityChangeEvent(Kernel::HLERequestContext& ctx) { +    LOG_DEBUG(Service_NFP, "(STUBBED) called"); -    bool has_attached_handle{}; -    const u64 device_handle{0}; // Npad device 1 -    const u32 npad_id{0};       // Player 1 controller -    State state{State::NonInitialized}; -    DeviceState device_state{DeviceState::Initialized}; -    Kernel::KEvent* deactivate_event; -    Kernel::KEvent* availability_change_event; -}; +    IPC::ResponseBuilder rb{ctx, 2, 1}; +    rb.Push(ResultSuccess); +    rb.PushCopyObjects(availability_change_event->GetReadableEvent()); +} + +Module::Interface::Interface(std::shared_ptr<Module> module_, Core::System& system_, +                             const char* name) +    : ServiceFramework{system_, name}, module{std::move(module_)}, +      npad_id{Core::HID::NpadIdType::Player1}, service_context{system_, service_name} { +    activate_event = service_context.CreateEvent("IUser:NFPActivateEvent"); +    deactivate_event = service_context.CreateEvent("IUser:NFPDeactivateEvent"); +} + +Module::Interface::~Interface() = default;  void Module::Interface::CreateUserInterface(Kernel::HLERequestContext& ctx) {      LOG_DEBUG(Service_NFP, "called");      IPC::ResponseBuilder rb{ctx, 2, 0, 1};      rb.Push(ResultSuccess); -    rb.PushIpcInterface<IUser>(*this, system, service_context); +    rb.PushIpcInterface<IUser>(*this, system);  }  bool Module::Interface::LoadAmiibo(const std::vector<u8>& buffer) { -    if (buffer.size() < sizeof(AmiiboFile)) { +    if (device_state != DeviceState::SearchingForTag) { +        LOG_ERROR(Service_NFP, "Game is not looking for amiibos, current state {}", device_state); +        return false; +    } + +    constexpr auto tag_size = sizeof(NTAG215File); +    constexpr auto tag_size_without_password = sizeof(NTAG215File) - sizeof(NTAG215Password); + +    std::vector<u8> amiibo_buffer = buffer; + +    if (amiibo_buffer.size() < tag_size_without_password) { +        LOG_ERROR(Service_NFP, "Wrong file size {}", buffer.size()); +        return false; +    } + +    // Ensure it has the correct size +    if (amiibo_buffer.size() != tag_size) { +        amiibo_buffer.resize(tag_size, 0); +    } + +    LOG_INFO(Service_NFP, "Amiibo detected"); +    std::memcpy(&tag_data, buffer.data(), tag_size); + +    if (!IsAmiiboValid()) {          return false;      } -    std::memcpy(&amiibo, buffer.data(), sizeof(amiibo)); -    nfc_tag_load->GetWritableEvent().Signal(); +    // This value can't be dumped from a tag. Generate it +    tag_data.password.PWD = GetTagPassword(tag_data.uuid); + +    device_state = DeviceState::TagFound; +    activate_event->GetWritableEvent().Signal();      return true;  } -Kernel::KReadableEvent& Module::Interface::GetNFCEvent() { -    return nfc_tag_load->GetReadableEvent(); +void Module::Interface::CloseAmiibo() { +    LOG_INFO(Service_NFP, "Remove amiibo"); +    device_state = DeviceState::TagRemoved; +    is_application_area_initialized = false; +    application_area_id = 0; +    application_area_data.clear(); +    deactivate_event->GetWritableEvent().Signal(); +} + +bool Module::Interface::IsAmiiboValid() const { +    const auto& amiibo_data = tag_data.user_memory; +    LOG_DEBUG(Service_NFP, "uuid_lock=0x{0:x}", tag_data.lock_bytes); +    LOG_DEBUG(Service_NFP, "compability_container=0x{0:x}", tag_data.compability_container); +    LOG_DEBUG(Service_NFP, "crypto_init=0x{0:x}", amiibo_data.crypto_init); +    LOG_DEBUG(Service_NFP, "write_count={}", amiibo_data.write_count); + +    LOG_DEBUG(Service_NFP, "character_id=0x{0:x}", amiibo_data.model_info.character_id); +    LOG_DEBUG(Service_NFP, "character_variant={}", amiibo_data.model_info.character_variant); +    LOG_DEBUG(Service_NFP, "amiibo_type={}", amiibo_data.model_info.amiibo_type); +    LOG_DEBUG(Service_NFP, "model_number=0x{0:x}", amiibo_data.model_info.model_number); +    LOG_DEBUG(Service_NFP, "series={}", amiibo_data.model_info.series); +    LOG_DEBUG(Service_NFP, "fixed_value=0x{0:x}", amiibo_data.model_info.fixed); + +    LOG_DEBUG(Service_NFP, "tag_dynamic_lock=0x{0:x}", tag_data.dynamic_lock); +    LOG_DEBUG(Service_NFP, "tag_CFG0=0x{0:x}", tag_data.CFG0); +    LOG_DEBUG(Service_NFP, "tag_CFG1=0x{0:x}", tag_data.CFG1); + +    // Check against all know constants on an amiibo binary +    if (tag_data.lock_bytes != 0xE00F) { +        return false; +    } +    if (tag_data.compability_container != 0xEEFF10F1U) { +        return false; +    } +    if ((amiibo_data.crypto_init & 0xFF) != 0xA5) { +        return false; +    } +    if (amiibo_data.model_info.fixed != 0x02) { +        return false; +    } +    if ((tag_data.dynamic_lock & 0xFFFFFF) != 0x0F0001) { +        return false; +    } +    if (tag_data.CFG0 != 0x04000000U) { +        return false; +    } +    if (tag_data.CFG1 != 0x5F) { +        return false; +    } +    return true; +} + +Kernel::KReadableEvent& Module::Interface::GetActivateEvent() const { +    return activate_event->GetReadableEvent(); +} + +Kernel::KReadableEvent& Module::Interface::GetDeactivateEvent() const { +    return deactivate_event->GetReadableEvent(); +} + +void Module::Interface::Initialize() { +    device_state = DeviceState::Initialized; +} + +void Module::Interface::Finalize() { +    device_state = DeviceState::Unaviable; +    is_application_area_initialized = false; +    application_area_id = 0; +    application_area_data.clear(); +} + +ResultCode Module::Interface::StartDetection(s32 protocol_) { +    auto npad_device = system.HIDCore().GetEmulatedController(npad_id); + +    // TODO(german77): Add callback for when nfc data is available + +    if (device_state == DeviceState::Initialized || device_state == DeviceState::TagRemoved) { +        npad_device->SetPollingMode(Common::Input::PollingMode::NFC); +        device_state = DeviceState::SearchingForTag; +        protocol = protocol_; +        return ResultSuccess; +    } + +    LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); +    return ErrCodes::WrongDeviceState; +} + +ResultCode Module::Interface::StopDetection() { +    auto npad_device = system.HIDCore().GetEmulatedController(npad_id); +    npad_device->SetPollingMode(Common::Input::PollingMode::Active); + +    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; +    } + +    LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); +    return ErrCodes::WrongDeviceState; +} + +ResultCode Module::Interface::Mount() { +    if (device_state == DeviceState::TagFound) { +        device_state = DeviceState::TagMounted; +        return ResultSuccess; +    } + +    LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); +    return ErrCodes::WrongDeviceState; +} + +ResultCode Module::Interface::Unmount() { +    if (device_state == DeviceState::TagMounted) { +        is_application_area_initialized = false; +        application_area_id = 0; +        application_area_data.clear(); +        device_state = DeviceState::TagFound; +        return ResultSuccess; +    } + +    LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); +    return ErrCodes::WrongDeviceState; +} + +ResultCode Module::Interface::GetTagInfo(TagInfo& tag_info) const { +    if (device_state == DeviceState::TagFound || device_state == DeviceState::TagMounted) { +        tag_info = { +            .uuid = tag_data.uuid, +            .uuid_length = static_cast<u8>(tag_data.uuid.size()), +            .protocol = protocol, +            .tag_type = static_cast<u32>(tag_data.user_memory.model_info.amiibo_type), +        }; +        return ResultSuccess; +    } + +    LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); +    return ErrCodes::WrongDeviceState; +} + +ResultCode Module::Interface::GetCommonInfo(CommonInfo& common_info) const { +    if (device_state != DeviceState::TagMounted) { +        LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); +        return ErrCodes::WrongDeviceState; +    } + +    // Read this data from the amiibo save file +    common_info = { +        .last_write_year = 2022, +        .last_write_month = 2, +        .last_write_day = 7, +        .write_counter = tag_data.user_memory.write_count, +        .version = 1, +        .application_area_size = ApplicationAreaSize, +    }; +    return ResultSuccess; +} + +ResultCode Module::Interface::GetModelInfo(ModelInfo& model_info) const { +    if (device_state != DeviceState::TagMounted) { +        LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); +        return ErrCodes::WrongDeviceState; +    } + +    model_info = tag_data.user_memory.model_info; +    return ResultSuccess; +} + +ResultCode Module::Interface::GetRegisterInfo(RegisterInfo& register_info) const { +    if (device_state != DeviceState::TagMounted) { +        LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); +        return ErrCodes::WrongDeviceState; +    } + +    Service::Mii::MiiManager manager; + +    // Read this data from the amiibo save file +    register_info = { +        .mii_char_info = manager.BuildDefault(0), +        .first_write_year = 2022, +        .first_write_month = 2, +        .first_write_day = 7, +        .amiibo_name = {'Y', 'u', 'z', 'u', 'A', 'm', 'i', 'i', 'b', 'o', 0}, +        .unknown = {}, +    }; +    return ResultSuccess; +} + +ResultCode Module::Interface::OpenApplicationArea(u32 access_id) { +    if (device_state != DeviceState::TagMounted) { +        LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); +        return ErrCodes::WrongDeviceState; +    } +    if (AmiiboApplicationDataExist(access_id)) { +        application_area_data = LoadAmiiboApplicationData(access_id); +        application_area_id = access_id; +        is_application_area_initialized = true; +    } +    if (!is_application_area_initialized) { +        LOG_WARNING(Service_NFP, "Application area is not initialized"); +        return ErrCodes::ApplicationAreaIsNotInitialized; +    } +    return ResultSuccess; +} + +ResultCode Module::Interface::GetApplicationArea(std::vector<u8>& data) const { +    if (device_state != DeviceState::TagMounted) { +        LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); +        return ErrCodes::WrongDeviceState; +    } +    if (!is_application_area_initialized) { +        LOG_ERROR(Service_NFP, "Application area is not initialized"); +        return ErrCodes::ApplicationAreaIsNotInitialized; +    } + +    data = application_area_data; + +    return ResultSuccess; +} + +ResultCode Module::Interface::SetApplicationArea(const std::vector<u8>& data) { +    if (device_state != DeviceState::TagMounted) { +        LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); +        return ErrCodes::WrongDeviceState; +    } +    if (!is_application_area_initialized) { +        LOG_ERROR(Service_NFP, "Application area is not initialized"); +        return ErrCodes::ApplicationAreaIsNotInitialized; +    } +    application_area_data = data; +    SaveAmiiboApplicationData(application_area_id, application_area_data); +    return ResultSuccess; +} + +ResultCode Module::Interface::CreateApplicationArea(u32 access_id, const std::vector<u8>& data) { +    if (device_state != DeviceState::TagMounted) { +        LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); +        return ErrCodes::WrongDeviceState; +    } +    if (AmiiboApplicationDataExist(access_id)) { +        LOG_ERROR(Service_NFP, "Application area already exist"); +        return ErrCodes::ApplicationAreaExist; +    } +    application_area_data = data; +    application_area_id = access_id; +    SaveAmiiboApplicationData(application_area_id, application_area_data); +    return ResultSuccess; +} + +bool Module::Interface::AmiiboApplicationDataExist(u32 access_id) const { +    // TODO(german77): Check if file exist +    return false; +} + +std::vector<u8> Module::Interface::LoadAmiiboApplicationData(u32 access_id) const { +    // TODO(german77): Read file +    std::vector<u8> data(ApplicationAreaSize); +    return data; +} + +void Module::Interface::SaveAmiiboApplicationData(u32 access_id, +                                                  const std::vector<u8>& data) const { +    // TODO(german77): Save file +} + +u64 Module::Interface::GetHandle() const { +    // Generate a handle based of the npad id +    return static_cast<u64>(npad_id); +} + +DeviceState Module::Interface::GetCurrentState() const { +    return device_state; +} + +Core::HID::NpadIdType Module::Interface::GetNpadId() const { +    return npad_id;  } -const Module::Interface::AmiiboFile& Module::Interface::GetAmiiboBuffer() const { -    return amiibo; +u32 Module::Interface::GetTagPassword(const TagUuid& uuid) const { +    // Verifiy that the generated password is correct +    u32 password = 0xAA ^ (uuid[1] ^ uuid[3]); +    password &= (0x55 ^ (uuid[2] ^ uuid[4])) << 8; +    password &= (0xAA ^ (uuid[3] ^ uuid[5])) << 16; +    password &= (0x55 ^ (uuid[4] ^ uuid[6])) << 24; +    return password;  }  void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system) { diff --git a/src/core/hle/service/nfp/nfp.h b/src/core/hle/service/nfp/nfp.h index 95c127efb..022f13b29 100644 --- a/src/core/hle/service/nfp/nfp.h +++ b/src/core/hle/service/nfp/nfp.h @@ -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 diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp index 9ee7992e7..d5ba86c03 100644 --- a/src/yuzu/configuration/config.cpp +++ b/src/yuzu/configuration/config.cpp @@ -77,13 +77,13 @@ const std::array<UISettings::Shortcut, 22> Config::default_hotkeys{{      {QStringLiteral("Exit Fullscreen"),          QStringLiteral("Main Window"), {QStringLiteral("Esc"),     QStringLiteral(""), Qt::WindowShortcut}},      {QStringLiteral("Exit yuzu"),                QStringLiteral("Main Window"), {QStringLiteral("Ctrl+Q"),  QStringLiteral("Home+Minus"), Qt::WindowShortcut}},      {QStringLiteral("Fullscreen"),               QStringLiteral("Main Window"), {QStringLiteral("F11"),     QStringLiteral("Home+B"), Qt::WindowShortcut}}, -    {QStringLiteral("Load Amiibo"),              QStringLiteral("Main Window"), {QStringLiteral("F2"),      QStringLiteral("Home+A"), Qt::WidgetWithChildrenShortcut}},      {QStringLiteral("Load File"),                QStringLiteral("Main Window"), {QStringLiteral("Ctrl+O"),  QStringLiteral(""), Qt::WidgetWithChildrenShortcut}}, +    {QStringLiteral("Load/Remove Amiibo"),       QStringLiteral("Main Window"), {QStringLiteral("F2"),      QStringLiteral("Home+A"), Qt::WidgetWithChildrenShortcut}},      {QStringLiteral("Restart Emulation"),        QStringLiteral("Main Window"), {QStringLiteral("F6"),      QStringLiteral(""), Qt::WindowShortcut}},      {QStringLiteral("Stop Emulation"),           QStringLiteral("Main Window"), {QStringLiteral("F5"),      QStringLiteral(""), Qt::WindowShortcut}}, -    {QStringLiteral("TAS Start/Stop"),           QStringLiteral("Main Window"), {QStringLiteral("Ctrl+F5"), QStringLiteral(""), Qt::ApplicationShortcut}}, -    {QStringLiteral("TAS Reset"),                QStringLiteral("Main Window"), {QStringLiteral("Ctrl+F6"), QStringLiteral(""), Qt::ApplicationShortcut}},      {QStringLiteral("TAS Record"),               QStringLiteral("Main Window"), {QStringLiteral("Ctrl+F7"), QStringLiteral(""), Qt::ApplicationShortcut}}, +    {QStringLiteral("TAS Reset"),                QStringLiteral("Main Window"), {QStringLiteral("Ctrl+F6"), QStringLiteral(""), Qt::ApplicationShortcut}}, +    {QStringLiteral("TAS Start/Stop"),           QStringLiteral("Main Window"), {QStringLiteral("Ctrl+F5"), QStringLiteral(""), Qt::ApplicationShortcut}},      {QStringLiteral("Toggle Filter Bar"),        QStringLiteral("Main Window"), {QStringLiteral("Ctrl+F"),  QStringLiteral(""), Qt::WindowShortcut}},      {QStringLiteral("Toggle Framerate Limit"),   QStringLiteral("Main Window"), {QStringLiteral("Ctrl+U"),  QStringLiteral("Home+Y"), Qt::ApplicationShortcut}},      {QStringLiteral("Toggle Mouse Panning"),     QStringLiteral("Main Window"), {QStringLiteral("Ctrl+F9"), QStringLiteral(""), Qt::ApplicationShortcut}}, diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index 3c2d7d080..e3fd38a02 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -934,7 +934,7 @@ void GMainWindow::InitializeHotkeys() {      hotkey_registry.LoadHotkeys();      LinkActionShortcut(ui->action_Load_File, QStringLiteral("Load File")); -    LinkActionShortcut(ui->action_Load_Amiibo, QStringLiteral("Load Amiibo")); +    LinkActionShortcut(ui->action_Load_Amiibo, QStringLiteral("Load/Remove Amiibo"));      LinkActionShortcut(ui->action_Exit, QStringLiteral("Exit yuzu"));      LinkActionShortcut(ui->action_Restart, QStringLiteral("Restart Emulation"));      LinkActionShortcut(ui->action_Pause, QStringLiteral("Continue/Pause Emulation")); @@ -2927,6 +2927,24 @@ void GMainWindow::OnLoadAmiibo() {          return;      } +    Service::SM::ServiceManager& sm = system->ServiceManager(); +    auto nfc = sm.GetService<Service::NFP::Module::Interface>("nfp:user"); +    if (nfc == nullptr) { +        QMessageBox::warning(this, tr("Error"), tr("The current game is not looking for amiibos")); +        return; +    } +    const auto nfc_state = nfc->GetCurrentState(); +    if (nfc_state == Service::NFP::DeviceState::TagFound || +        nfc_state == Service::NFP::DeviceState::TagMounted) { +        nfc->CloseAmiibo(); +        return; +    } + +    if (nfc_state != Service::NFP::DeviceState::SearchingForTag) { +        QMessageBox::warning(this, tr("Error"), tr("The current game is not looking for amiibos")); +        return; +    } +      is_amiibo_file_select_active = true;      const QString extensions{QStringLiteral("*.bin")};      const QString file_filter = tr("Amiibo File (%1);; All Files (*.*)").arg(extensions); diff --git a/src/yuzu/main.ui b/src/yuzu/main.ui index 5719b2ee4..6ab95b9a5 100644 --- a/src/yuzu/main.ui +++ b/src/yuzu/main.ui @@ -266,7 +266,7 @@      <bool>false</bool>     </property>     <property name="text"> -    <string>Load &Amiibo...</string> +    <string>Load/Remove &Amiibo...</string>     </property>    </action>    <action name="action_Report_Compatibility"> | 
