diff options
| -rw-r--r-- | src/core/CMakeLists.txt | 5 | ||||
| -rw-r--r-- | src/core/hle/service/mii/mii_manager.cpp | 80 | ||||
| -rw-r--r-- | src/core/hle/service/mii/mii_manager.h | 3 | ||||
| -rw-r--r-- | src/core/hle/service/nfp/amiibo_crypto.cpp | 12 | ||||
| -rw-r--r-- | src/core/hle/service/nfp/amiibo_crypto.h | 4 | ||||
| -rw-r--r-- | src/core/hle/service/nfp/nfp.cpp | 1093 | ||||
| -rw-r--r-- | src/core/hle/service/nfp/nfp.h | 161 | ||||
| -rw-r--r-- | src/core/hle/service/nfp/nfp_device.cpp | 572 | ||||
| -rw-r--r-- | src/core/hle/service/nfp/nfp_device.h | 97 | ||||
| -rw-r--r-- | src/core/hle/service/nfp/nfp_result.h | 21 | ||||
| -rw-r--r-- | src/core/hle/service/nfp/nfp_types.h (renamed from src/core/hle/service/nfp/amiibo_types.h) | 79 | ||||
| -rw-r--r-- | src/core/hle/service/nfp/nfp_user.cpp | 634 | ||||
| -rw-r--r-- | src/core/hle/service/nfp/nfp_user.h | 44 | 
13 files changed, 1542 insertions, 1263 deletions
| diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index c17662323..7fd2d0276 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -523,9 +523,12 @@ add_library(core STATIC      hle/service/nfc/nfc.h      hle/service/nfp/amiibo_crypto.cpp      hle/service/nfp/amiibo_crypto.h -    hle/service/nfp/amiibo_types.h      hle/service/nfp/nfp.cpp      hle/service/nfp/nfp.h +    hle/service/nfp/nfp_device.cpp +    hle/service/nfp/nfp_device.h +    hle/service/nfp/nfp_result.h +    hle/service/nfp/nfp_types.h      hle/service/nfp/nfp_user.cpp      hle/service/nfp/nfp_user.h      hle/service/ngct/ngct.cpp diff --git a/src/core/hle/service/mii/mii_manager.cpp b/src/core/hle/service/mii/mii_manager.cpp index c484a9c8d..3e92152ec 100644 --- a/src/core/hle/service/mii/mii_manager.cpp +++ b/src/core/hle/service/mii/mii_manager.cpp @@ -427,12 +427,12 @@ CharInfo MiiManager::BuildDefault(std::size_t index) {      return ConvertStoreDataToInfo(BuildDefaultStoreData(RawData::DefaultMii.at(index), user_id));  } -CharInfo MiiManager::ConvertV3ToCharInfo(Ver3StoreData mii_v3) const { +CharInfo MiiManager::ConvertV3ToCharInfo(const Ver3StoreData& mii_v3) const {      Service::Mii::MiiManager manager;      auto mii = manager.BuildDefault(0);      // Check if mii data exist -    if (mii_v3.mii_name[0] == 0) { +    if (mii_v3.version == 0) {          return mii;      } @@ -443,8 +443,8 @@ CharInfo MiiManager::ConvertV3ToCharInfo(Ver3StoreData mii_v3) const {      mii.height = mii_v3.height;      mii.build = mii_v3.build; -    memset(mii.name.data(), 0, sizeof(mii.name)); -    memcpy(mii.name.data(), mii_v3.mii_name.data(), sizeof(mii_v3.mii_name)); +    memset(mii.name.data(), 0, mii.name.size()); +    memcpy(mii.name.data(), mii_v3.mii_name.data(), mii_v3.mii_name.size());      mii.font_region = mii_v3.region_information.character_set;      mii.faceline_type = mii_v3.appearance_bits1.face_shape; @@ -504,6 +504,78 @@ CharInfo MiiManager::ConvertV3ToCharInfo(Ver3StoreData mii_v3) const {      return mii;  } +Ver3StoreData MiiManager::ConvertCharInfoToV3(const CharInfo& mii) const { +    Service::Mii::MiiManager manager; +    Ver3StoreData mii_v3{}; + +    // TODO: We are ignoring a bunch of data from the mii_v3 + +    mii_v3.version = 1; +    mii_v3.mii_information.gender.Assign(mii.gender); +    mii_v3.mii_information.favorite_color.Assign(mii.favorite_color); +    mii_v3.height = mii.height; +    mii_v3.build = mii.build; + +    memcpy(mii_v3.mii_name.data(), mii.name.data(), mii.name.size()); +    mii_v3.region_information.character_set.Assign(mii.font_region); + +    mii_v3.appearance_bits1.face_shape.Assign(mii.faceline_type); +    mii_v3.appearance_bits1.skin_color.Assign(mii.faceline_color); +    mii_v3.appearance_bits2.wrinkles.Assign(mii.faceline_wrinkle); +    mii_v3.appearance_bits2.makeup.Assign(mii.faceline_make); + +    mii_v3.hair_style = mii.hair_type; +    mii_v3.appearance_bits3.hair_color.Assign(mii.hair_color); +    mii_v3.appearance_bits3.flip_hair.Assign(mii.hair_flip); + +    mii_v3.appearance_bits4.eye_type.Assign(mii.eye_type); +    mii_v3.appearance_bits4.eye_color.Assign(mii.eye_color); +    mii_v3.appearance_bits4.eye_scale.Assign(mii.eye_scale); +    mii_v3.appearance_bits4.eye_vertical_stretch.Assign(mii.eye_aspect); +    mii_v3.appearance_bits4.eye_rotation.Assign(mii.eye_rotate); +    mii_v3.appearance_bits4.eye_spacing.Assign(mii.eye_x); +    mii_v3.appearance_bits4.eye_y_position.Assign(mii.eye_y); + +    mii_v3.appearance_bits5.eyebrow_style.Assign(mii.eyebrow_type); +    mii_v3.appearance_bits5.eyebrow_color.Assign(mii.eyebrow_color); +    mii_v3.appearance_bits5.eyebrow_scale.Assign(mii.eyebrow_scale); +    mii_v3.appearance_bits5.eyebrow_yscale.Assign(mii.eyebrow_aspect); +    mii_v3.appearance_bits5.eyebrow_rotation.Assign(mii.eyebrow_rotate); +    mii_v3.appearance_bits5.eyebrow_spacing.Assign(mii.eyebrow_x); +    mii_v3.appearance_bits5.eyebrow_y_position.Assign(mii.eyebrow_y); + +    mii_v3.appearance_bits6.nose_type.Assign(mii.nose_type); +    mii_v3.appearance_bits6.nose_scale.Assign(mii.nose_scale); +    mii_v3.appearance_bits6.nose_y_position.Assign(mii.nose_y); + +    mii_v3.appearance_bits7.mouth_type.Assign(mii.mouth_type); +    mii_v3.appearance_bits7.mouth_color.Assign(mii.mouth_color); +    mii_v3.appearance_bits7.mouth_scale.Assign(mii.mouth_scale); +    mii_v3.appearance_bits7.mouth_horizontal_stretch.Assign(mii.mouth_aspect); +    mii_v3.appearance_bits8.mouth_y_position.Assign(mii.mouth_y); + +    mii_v3.appearance_bits8.mustache_type.Assign(mii.mustache_type); +    mii_v3.appearance_bits9.mustache_scale.Assign(mii.mustache_scale); +    mii_v3.appearance_bits9.mustache_y_position.Assign(mii.mustache_y); + +    mii_v3.appearance_bits9.bear_type.Assign(mii.beard_type); +    mii_v3.appearance_bits9.facial_hair_color.Assign(mii.beard_color); + +    mii_v3.appearance_bits10.glasses_type.Assign(mii.glasses_type); +    mii_v3.appearance_bits10.glasses_color.Assign(mii.glasses_color); +    mii_v3.appearance_bits10.glasses_scale.Assign(mii.glasses_scale); +    mii_v3.appearance_bits10.glasses_y_position.Assign(mii.glasses_y); + +    mii_v3.appearance_bits11.mole_enabled.Assign(mii.mole_type); +    mii_v3.appearance_bits11.mole_scale.Assign(mii.mole_scale); +    mii_v3.appearance_bits11.mole_x_position.Assign(mii.mole_x); +    mii_v3.appearance_bits11.mole_y_position.Assign(mii.mole_y); + +    // TODO: Validate mii_v3 data + +    return mii_v3; +} +  ResultVal<std::vector<MiiInfoElement>> MiiManager::GetDefault(SourceFlag source_flag) {      std::vector<MiiInfoElement> result; diff --git a/src/core/hle/service/mii/mii_manager.h b/src/core/hle/service/mii/mii_manager.h index d847de0bd..b68fdd54c 100644 --- a/src/core/hle/service/mii/mii_manager.h +++ b/src/core/hle/service/mii/mii_manager.h @@ -22,7 +22,8 @@ public:      ResultVal<CharInfo> UpdateLatest(const CharInfo& info, SourceFlag source_flag);      CharInfo BuildRandom(Age age, Gender gender, Race race);      CharInfo BuildDefault(std::size_t index); -    CharInfo ConvertV3ToCharInfo(Ver3StoreData mii_v3) const; +    CharInfo ConvertV3ToCharInfo(const Ver3StoreData& mii_v3) const; +    Ver3StoreData ConvertCharInfoToV3(const CharInfo& mii) const;      ResultVal<std::vector<MiiInfoElement>> GetDefault(SourceFlag source_flag);      Result GetIndex(const CharInfo& info, u32& index); diff --git a/src/core/hle/service/nfp/amiibo_crypto.cpp b/src/core/hle/service/nfp/amiibo_crypto.cpp index 31dd3a307..c87da5ae4 100644 --- a/src/core/hle/service/nfp/amiibo_crypto.cpp +++ b/src/core/hle/service/nfp/amiibo_crypto.cpp @@ -20,13 +20,13 @@ bool IsAmiiboValid(const EncryptedNTAG215File& ntag_file) {      const auto& amiibo_data = ntag_file.user_memory;      LOG_DEBUG(Service_NFP, "uuid_lock=0x{0:x}", ntag_file.static_lock);      LOG_DEBUG(Service_NFP, "compability_container=0x{0:x}", ntag_file.compability_container); -    LOG_INFO(Service_NFP, "write_count={}", amiibo_data.write_counter); +    LOG_DEBUG(Service_NFP, "write_count={}", static_cast<u16>(amiibo_data.write_counter)); -    LOG_INFO(Service_NFP, "character_id=0x{0:x}", amiibo_data.model_info.character_id); -    LOG_INFO(Service_NFP, "character_variant={}", amiibo_data.model_info.character_variant); -    LOG_INFO(Service_NFP, "amiibo_type={}", amiibo_data.model_info.amiibo_type); -    LOG_INFO(Service_NFP, "model_number=0x{0:x}", amiibo_data.model_info.model_number); -    LOG_INFO(Service_NFP, "series={}", amiibo_data.model_info.series); +    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.constant_value);      LOG_DEBUG(Service_NFP, "tag_dynamic_lock=0x{0:x}", ntag_file.dynamic_lock); diff --git a/src/core/hle/service/nfp/amiibo_crypto.h b/src/core/hle/service/nfp/amiibo_crypto.h index af7335912..e3fa3d07e 100644 --- a/src/core/hle/service/nfp/amiibo_crypto.h +++ b/src/core/hle/service/nfp/amiibo_crypto.h @@ -5,7 +5,7 @@  #include <array> -#include "core/hle/service/nfp/amiibo_types.h" +#include "core/hle/service/nfp/nfp_types.h"  struct mbedtls_md_context_t; @@ -22,7 +22,7 @@ using HmacKey = std::array<u8, 0x10>;  using DrgbOutput = std::array<u8, 0x20>;  struct HashSeed { -    u16 magic; +    u16_be magic;      std::array<u8, 0xE> padding;      std::array<u8, 0x8> uuid1;      std::array<u8, 0x8> uuid2; diff --git a/src/core/hle/service/nfp/nfp.cpp b/src/core/hle/service/nfp/nfp.cpp index 037b86653..0cb55ca49 100644 --- a/src/core/hle/service/nfp/nfp.cpp +++ b/src/core/hle/service/nfp/nfp.cpp @@ -1,1098 +1,43 @@  // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project  // SPDX-License-Identifier: GPL-2.0-or-later -#include <array> -#include <atomic> - -#include "common/fs/file.h" -#include "common/fs/path_util.h"  #include "common/logging/log.h" -#include "common/string_util.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/mii/mii_manager.h" -#include "core/hle/service/nfp/amiibo_crypto.h"  #include "core/hle/service/nfp/nfp.h"  #include "core/hle/service/nfp/nfp_user.h"  namespace Service::NFP { -namespace ErrCodes { -constexpr Result DeviceNotFound(ErrorModule::NFP, 64); -constexpr Result WrongDeviceState(ErrorModule::NFP, 73); -constexpr Result NfcDisabled(ErrorModule::NFP, 80); -constexpr Result WriteAmiiboFailed(ErrorModule::NFP, 88); -constexpr Result TagRemoved(ErrorModule::NFP, 97); -constexpr Result ApplicationAreaIsNotInitialized(ErrorModule::NFP, 128); -constexpr Result WrongApplicationAreaId(ErrorModule::NFP, 152); -constexpr Result ApplicationAreaExist(ErrorModule::NFP, 168); -} // namespace ErrCodes - -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, &IUser::Flush, "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, &IUser::RecreateApplicationArea, "RecreateApplicationArea"}, -    }; -    RegisterHandlers(functions); - -    availability_change_event = service_context.CreateEvent("IUser:AvailabilityChangeEvent"); -} - -void IUser::Initialize(Kernel::HLERequestContext& ctx) { -    LOG_INFO(Service_NFC, "called"); - -    state = State::Initialized; - -    // TODO(german77): Loop through all interfaces -    nfp_interface.Initialize(); - -    IPC::ResponseBuilder rb{ctx, 2, 0}; -    rb.Push(ResultSuccess); -} - -void IUser::Finalize(Kernel::HLERequestContext& ctx) { -    LOG_INFO(Service_NFP, "called"); - -    state = State::NonInitialized; - -    // TODO(german77): Loop through all interfaces -    nfp_interface.Finalize(); - -    IPC::ResponseBuilder rb{ctx, 2}; -    rb.Push(ResultSuccess); -} - -void IUser::ListDevices(Kernel::HLERequestContext& ctx) { -    LOG_INFO(Service_NFP, "called"); - -    if (state == State::NonInitialized) { -        IPC::ResponseBuilder rb{ctx, 2}; -        rb.Push(ErrCodes::NfcDisabled); -        return; -    } - -    std::vector<u64> devices; - -    // TODO(german77): Loop through all interfaces -    devices.push_back(nfp_interface.GetHandle()); - -    if (devices.size() == 0) { -        IPC::ResponseBuilder rb{ctx, 2}; -        rb.Push(ErrCodes::DeviceNotFound); -        return; -    } - -    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); - -    if (state == State::NonInitialized) { -        IPC::ResponseBuilder rb{ctx, 2}; -        rb.Push(ErrCodes::NfcDisabled); -        return; -    } - -    // 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; -    } - -    LOG_ERROR(Service_NFP, "Handle not found, device_handle={}", device_handle); - -    IPC::ResponseBuilder rb{ctx, 2}; -    rb.Push(ErrCodes::DeviceNotFound); -} - -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); - -    if (state == State::NonInitialized) { -        IPC::ResponseBuilder rb{ctx, 2}; -        rb.Push(ErrCodes::NfcDisabled); -        return; -    } - -    // 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; -    } - -    LOG_ERROR(Service_NFP, "Handle not found, device_handle={}", device_handle); - -    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); - -    if (state == State::NonInitialized) { -        IPC::ResponseBuilder rb{ctx, 2}; -        rb.Push(ErrCodes::NfcDisabled); -        return; -    } - -    // 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; -    } - -    LOG_ERROR(Service_NFP, "Handle not found, device_handle={}", device_handle); - -    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); - -    if (state == State::NonInitialized) { -        IPC::ResponseBuilder rb{ctx, 2}; -        rb.Push(ErrCodes::NfcDisabled); -        return; -    } - -    // 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; -    } - -    LOG_ERROR(Service_NFP, "Handle not found, device_handle={}", device_handle); - -    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); - -    if (state == State::NonInitialized) { -        IPC::ResponseBuilder rb{ctx, 2}; -        rb.Push(ErrCodes::NfcDisabled); -        return; -    } - -    // 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(result); -        return; -    } - -    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); - -    if (state == State::NonInitialized) { -        IPC::ResponseBuilder rb{ctx, 2}; -        rb.Push(ErrCodes::NfcDisabled); -        return; -    } - -    // TODO(german77): Loop through all interfaces -    if (device_handle == nfp_interface.GetHandle()) { -        ApplicationArea data{}; -        const auto result = nfp_interface.GetApplicationArea(data); -        ctx.WriteBuffer(data); -        IPC::ResponseBuilder rb{ctx, 3}; -        rb.Push(result); -        rb.Push(static_cast<u32>(data.size())); -        return; -    } - -    LOG_ERROR(Service_NFP, "Handle not found, device_handle={}", device_handle); - -    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()); - -    if (state == State::NonInitialized) { -        IPC::ResponseBuilder rb{ctx, 2}; -        rb.Push(ErrCodes::NfcDisabled); -        return; -    } - -    // 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(result); -        return; -    } - -    LOG_ERROR(Service_NFP, "Handle not found, device_handle={}", device_handle); - -    IPC::ResponseBuilder rb{ctx, 2}; -    rb.Push(ErrCodes::DeviceNotFound); -} - -void IUser::Flush(Kernel::HLERequestContext& ctx) { -    IPC::RequestParser rp{ctx}; -    const auto device_handle{rp.Pop<u64>()}; -    LOG_WARNING(Service_NFP, "(STUBBED) called, device_handle={}", device_handle); - -    if (state == State::NonInitialized) { -        IPC::ResponseBuilder rb{ctx, 2}; -        rb.Push(ErrCodes::NfcDisabled); -        return; -    } - -    // TODO(german77): Loop through all interfaces -    if (device_handle == nfp_interface.GetHandle()) { -        const auto result = nfp_interface.Flush(); -        IPC::ResponseBuilder rb{ctx, 2}; -        rb.Push(result); -        return; -    } - -    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()); - -    if (state == State::NonInitialized) { -        IPC::ResponseBuilder rb{ctx, 2}; -        rb.Push(ErrCodes::NfcDisabled); -        return; -    } - -    // 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}; -        rb.Push(result); -        return; -    } - -    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); - -    if (state == State::NonInitialized) { -        IPC::ResponseBuilder rb{ctx, 2}; -        rb.Push(ErrCodes::NfcDisabled); -        return; -    } - -    // 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(result); -        return; -    } - -    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); - -    if (state == State::NonInitialized) { -        IPC::ResponseBuilder rb{ctx, 2}; -        rb.Push(ErrCodes::NfcDisabled); -        return; -    } - -    // 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}; -        rb.Push(result); -        return; -    } - -    LOG_ERROR(Service_NFP, "Handle not found, device_handle={}", device_handle); - -    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); - -    if (state == State::NonInitialized) { -        IPC::ResponseBuilder rb{ctx, 2}; -        rb.Push(ErrCodes::NfcDisabled); -        return; -    } - -    // 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(result); -        return; -    } - -    LOG_ERROR(Service_NFP, "Handle not found, device_handle={}", device_handle); - -    IPC::ResponseBuilder rb{ctx, 2}; -    rb.Push(ErrCodes::DeviceNotFound); -} - -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); - -    if (state == State::NonInitialized) { -        IPC::ResponseBuilder rb{ctx, 2}; -        rb.Push(ErrCodes::NfcDisabled); -        return; -    } - -    // 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(result); -        return; -    } - -    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); - -    if (state == State::NonInitialized) { -        IPC::ResponseBuilder rb{ctx, 2}; -        rb.Push(ErrCodes::NfcDisabled); -        return; -    } - -    // 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.GetActivateEvent()); -        return; -    } - -    LOG_ERROR(Service_NFP, "Handle not found, device_handle={}", device_handle); - -    IPC::ResponseBuilder rb{ctx, 2}; -    rb.Push(ErrCodes::DeviceNotFound); -} - -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); - -    if (state == State::NonInitialized) { -        IPC::ResponseBuilder rb{ctx, 2}; -        rb.Push(ErrCodes::NfcDisabled); -        return; -    } - -    // 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; -    } - -    LOG_ERROR(Service_NFP, "Handle not found, device_handle={}", device_handle); - -    IPC::ResponseBuilder rb{ctx, 2}; -    rb.Push(ErrCodes::DeviceNotFound); -} - -void IUser::GetState(Kernel::HLERequestContext& ctx) { -    LOG_DEBUG(Service_NFC, "called"); - -    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; -    } - -    LOG_ERROR(Service_NFP, "Handle not found, device_handle={}", device_handle); - -    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); - -    if (state == State::NonInitialized) { -        IPC::ResponseBuilder rb{ctx, 2}; -        rb.Push(ErrCodes::NfcDisabled); -        return; -    } - -    // TODO(german77): Loop through all interfaces -    if (device_handle == nfp_interface.GetHandle()) { -        IPC::ResponseBuilder rb{ctx, 3}; -        rb.Push(ResultSuccess); -        rb.PushEnum(nfp_interface.GetNpadId()); -        return; -    } - -    LOG_ERROR(Service_NFP, "Handle not found, device_handle={}", device_handle); - -    IPC::ResponseBuilder rb{ctx, 2}; -    rb.Push(ErrCodes::DeviceNotFound); -} - -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.Push(sizeof(ApplicationArea)); -        return; -    } - -    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"); - -    if (state == State::NonInitialized) { -        IPC::ResponseBuilder rb{ctx, 2}; -        rb.Push(ErrCodes::NfcDisabled); -        return; -    } - -    IPC::ResponseBuilder rb{ctx, 2, 1}; -    rb.Push(ResultSuccess); -    rb.PushCopyObjects(availability_change_event->GetReadableEvent()); -} - -void IUser::RecreateApplicationArea(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()); - -    if (state == State::NonInitialized) { -        IPC::ResponseBuilder rb{ctx, 2}; -        rb.Push(ErrCodes::NfcDisabled); -        return; -    } - -    // TODO(german77): Loop through all interfaces -    if (device_handle == nfp_interface.GetHandle()) { -        const auto result = nfp_interface.RecreateApplicationArea(access_id, data); -        IPC::ResponseBuilder rb{ctx, 2}; -        rb.Push(result); -        return; -    } - -    LOG_ERROR(Service_NFP, "Handle not found, device_handle={}", device_handle); - -    IPC::ResponseBuilder rb{ctx, 2}; -    rb.Push(ErrCodes::DeviceNotFound); -} - -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); -} - -bool Module::Interface::LoadAmiiboFile(const std::string& filename) { -    constexpr auto tag_size_without_password = sizeof(NTAG215File) - sizeof(NTAG215Password); -    const Common::FS::IOFile amiibo_file{filename, Common::FS::FileAccessMode::Read, -                                         Common::FS::FileType::BinaryFile}; - -    if (!amiibo_file.IsOpen()) { -        LOG_ERROR(Service_NFP, "Amiibo is already on use"); -        return false; -    } - -    // Workaround for files with missing password data -    std::array<u8, sizeof(EncryptedNTAG215File)> buffer{}; -    if (amiibo_file.Read(buffer) < tag_size_without_password) { -        LOG_ERROR(Service_NFP, "Failed to read amiibo file"); -        return false; -    } -    memcpy(&encrypted_tag_data, buffer.data(), sizeof(EncryptedNTAG215File)); - -    if (!AmiiboCrypto::IsAmiiboValid(encrypted_tag_data)) { -        LOG_INFO(Service_NFP, "Invalid amiibo"); -        return false; -    } - -    file_path = filename; -    return true; -} - -bool Module::Interface::LoadAmiibo(const std::string& filename) { -    if (device_state != DeviceState::SearchingForTag) { -        LOG_ERROR(Service_NFP, "Game is not looking for amiibos, current state {}", device_state); -        return false; -    } - -    if (!LoadAmiiboFile(filename)) { -        return false; -    } - -    device_state = DeviceState::TagFound; -    activate_event->GetWritableEvent().Signal(); -    return true; -} - -void Module::Interface::CloseAmiibo() { -    LOG_INFO(Service_NFP, "Remove amiibo"); -    device_state = DeviceState::TagRemoved; -    is_data_decoded = false; -    is_application_area_initialized = false; -    encrypted_tag_data = {}; -    tag_data = {}; -    deactivate_event->GetWritableEvent().Signal(); -} - -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; -    is_data_decoded = false; -    is_application_area_initialized = false; -    encrypted_tag_data = {}; -    tag_data = {}; -} - -void Module::Interface::Finalize() { -    if (device_state == DeviceState::TagMounted) { -        Unmount(); -    } -    if (device_state == DeviceState::SearchingForTag || device_state == DeviceState::TagRemoved) { -        StopDetection(); -    } -    device_state = DeviceState::Unaviable; -} - -Result 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; -} - -Result 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; -} - -Result Module::Interface::Flush() { -    // Ignore write command if we can't encrypt the data -    if (!is_data_decoded) { -        return ResultSuccess; -    } - -    constexpr auto tag_size_without_password = sizeof(NTAG215File) - sizeof(NTAG215Password); -    EncryptedNTAG215File tmp_encrypted_tag_data{}; -    const Common::FS::IOFile amiibo_file{file_path, Common::FS::FileAccessMode::ReadWrite, -                                         Common::FS::FileType::BinaryFile}; - -    if (!amiibo_file.IsOpen()) { -        LOG_ERROR(Core, "Amiibo is already on use"); -        return ErrCodes::WriteAmiiboFailed; -    } - -    // Workaround for files with missing password data -    std::array<u8, sizeof(EncryptedNTAG215File)> buffer{}; -    if (amiibo_file.Read(buffer) < tag_size_without_password) { -        LOG_ERROR(Core, "Failed to read amiibo file"); -        return ErrCodes::WriteAmiiboFailed; -    } -    memcpy(&tmp_encrypted_tag_data, buffer.data(), sizeof(EncryptedNTAG215File)); - -    if (!AmiiboCrypto::IsAmiiboValid(tmp_encrypted_tag_data)) { -        LOG_INFO(Service_NFP, "Invalid amiibo"); -        return ErrCodes::WriteAmiiboFailed; -    } - -    bool is_uuid_equal = memcmp(tmp_encrypted_tag_data.uuid.data(), tag_data.uuid.data(), 8) == 0; -    bool is_character_equal = tmp_encrypted_tag_data.user_memory.model_info.character_id == -                              tag_data.model_info.character_id; -    if (!is_uuid_equal || !is_character_equal) { -        LOG_ERROR(Service_NFP, "Not the same amiibo"); -        return ErrCodes::WriteAmiiboFailed; -    } - -    if (!AmiiboCrypto::EncodeAmiibo(tag_data, encrypted_tag_data)) { -        LOG_ERROR(Service_NFP, "Failed to encode data"); -        return ErrCodes::WriteAmiiboFailed; -    } - -    // Return to the start of the file -    if (!amiibo_file.Seek(0)) { -        LOG_ERROR(Service_NFP, "Error writing to file"); -        return ErrCodes::WriteAmiiboFailed; -    } - -    if (!amiibo_file.Write(encrypted_tag_data)) { -        LOG_ERROR(Service_NFP, "Error writing to file"); -        return ErrCodes::WriteAmiiboFailed; -    } - -    return ResultSuccess; -} - -Result Module::Interface::Mount() { -    if (device_state != DeviceState::TagFound) { -        LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); -        return ErrCodes::WrongDeviceState; -    } -    is_data_decoded = AmiiboCrypto::DecodeAmiibo(encrypted_tag_data, tag_data); -    LOG_INFO(Service_NFP, "Is amiibo decoded {}", is_data_decoded); - -    is_application_area_initialized = false; -    device_state = DeviceState::TagMounted; -    return ResultSuccess; -} - -Result Module::Interface::Unmount() { -    if (device_state != DeviceState::TagMounted) { -        LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); -        return ErrCodes::WrongDeviceState; -    } - -    is_data_decoded = false; -    is_application_area_initialized = false; -    device_state = DeviceState::TagFound; -    return ResultSuccess; -} - -Result Module::Interface::GetTagInfo(TagInfo& tag_info) const { -    if (device_state != DeviceState::TagFound && device_state != DeviceState::TagMounted) { -        LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); -        return ErrCodes::WrongDeviceState; -    } - -    tag_info = { -        .uuid = encrypted_tag_data.uuid, -        .uuid_length = static_cast<u8>(encrypted_tag_data.uuid.size()), -        .protocol = protocol, -        .tag_type = static_cast<u32>(encrypted_tag_data.user_memory.model_info.amiibo_type), -    }; - -    return ResultSuccess; -} - -Result Module::Interface::GetCommonInfo(CommonInfo& common_info) const { -    if (device_state != DeviceState::TagMounted) { -        LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); -        return ErrCodes::WrongDeviceState; -    } - -    if (is_data_decoded && tag_data.settings.settings.amiibo_initialized != 0) { -        const auto& settings = tag_data.settings; -        // TODO: Validate this data -        common_info = { -            .last_write_year = settings.write_date.GetYear(), -            .last_write_month = settings.write_date.GetMonth(), -            .last_write_day = settings.write_date.GetDay(), -            .write_counter = settings.crc_counter, -            .version = 1, -            .application_area_size = sizeof(ApplicationArea), -        }; -        return ResultSuccess; -    } - -    // Generate a generic answer -    common_info = { -        .last_write_year = 2022, -        .last_write_month = 2, -        .last_write_day = 7, -        .write_counter = 0, -        .version = 1, -        .application_area_size = sizeof(ApplicationArea), -    }; -    return ResultSuccess; -} - -Result Module::Interface::GetModelInfo(ModelInfo& model_info) const { -    if (device_state != DeviceState::TagMounted) { -        LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); -        return ErrCodes::WrongDeviceState; -    } - -    const auto& model_info_data = encrypted_tag_data.user_memory.model_info; -    model_info = { -        .character_id = model_info_data.character_id, -        .character_variant = model_info_data.character_variant, -        .amiibo_type = model_info_data.amiibo_type, -        .model_number = model_info_data.model_number, -        .series = model_info_data.series, -        .constant_value = model_info_data.constant_value, -    }; -    return ResultSuccess; -} - -Result Module::Interface::GetRegisterInfo(RegisterInfo& register_info) const { -    if (device_state != DeviceState::TagMounted) { -        LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); -        if (device_state == DeviceState::TagRemoved) { -            return ErrCodes::TagRemoved; -        } -        return ErrCodes::WrongDeviceState; -    } - -    Service::Mii::MiiManager manager; - -    if (is_data_decoded && tag_data.settings.settings.amiibo_initialized != 0) { -        const auto& settings = tag_data.settings; - -        // TODO: Validate this data -        register_info = { -            .mii_char_info = manager.ConvertV3ToCharInfo(tag_data.owner_mii), -            .first_write_year = settings.init_date.GetYear(), -            .first_write_month = settings.init_date.GetMonth(), -            .first_write_day = settings.init_date.GetDay(), -            .amiibo_name = GetAmiiboName(settings), -            .font_region = {}, +class IUserManager final : public ServiceFramework<IUserManager> { +public: +    explicit IUserManager(Core::System& system_) : ServiceFramework{system_, "nfp:user"} { +        // clang-format off +        static const FunctionInfo functions[] = { +            {0, &IUserManager::CreateUserInterface, "CreateUserInterface"},          }; +        // clang-format on -        return ResultSuccess; +        RegisterHandlers(functions);      } -    // Generate a generic answer -    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}, -        .font_region = {}, -    }; -    return ResultSuccess; -} +private: +    void CreateUserInterface(Kernel::HLERequestContext& ctx) { +        LOG_DEBUG(Service_NFP, "called"); -Result Module::Interface::OpenApplicationArea(u32 access_id) { -    if (device_state != DeviceState::TagMounted) { -        LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); -        if (device_state == DeviceState::TagRemoved) { -            return ErrCodes::TagRemoved; +        if (user_interface == nullptr) { +            user_interface = std::make_shared<IUser>(system);          } -        return ErrCodes::WrongDeviceState; -    } - -    // Fallback for lack of amiibo keys -    if (!is_data_decoded) { -        LOG_WARNING(Service_NFP, "Application area is not initialized"); -        return ErrCodes::ApplicationAreaIsNotInitialized; -    } -    if (tag_data.settings.settings.appdata_initialized == 0) { -        LOG_WARNING(Service_NFP, "Application area is not initialized"); -        return ErrCodes::ApplicationAreaIsNotInitialized; -    } - -    if (tag_data.application_area_id != access_id) { -        LOG_WARNING(Service_NFP, "Wrong application area id"); -        return ErrCodes::WrongApplicationAreaId; -    } - -    is_application_area_initialized = true; -    return ResultSuccess; -} - -Result Module::Interface::GetApplicationArea(ApplicationArea& data) const { -    if (device_state != DeviceState::TagMounted) { -        LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); -        if (device_state == DeviceState::TagRemoved) { -            return ErrCodes::TagRemoved; -        } -        return ErrCodes::WrongDeviceState; -    } - -    if (!is_application_area_initialized) { -        LOG_ERROR(Service_NFP, "Application area is not initialized"); -        return ErrCodes::ApplicationAreaIsNotInitialized; -    } - -    data = tag_data.application_area; - -    return ResultSuccess; -} - -Result Module::Interface::SetApplicationArea(const std::vector<u8>& data) { -    if (device_state != DeviceState::TagMounted) { -        LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); -        if (device_state == DeviceState::TagRemoved) { -            return ErrCodes::TagRemoved; -        } -        return ErrCodes::WrongDeviceState; -    } - -    if (!is_application_area_initialized) { -        LOG_ERROR(Service_NFP, "Application area is not initialized"); -        return ErrCodes::ApplicationAreaIsNotInitialized; -    } - -    if (data.size() != sizeof(ApplicationArea)) { -        LOG_ERROR(Service_NFP, "Wrong data size {}", data.size()); -        return ResultUnknown; -    } - -    std::memcpy(&tag_data.application_area, data.data(), sizeof(ApplicationArea)); -    return ResultSuccess; -} - -Result 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); -        if (device_state == DeviceState::TagRemoved) { -            return ErrCodes::TagRemoved; -        } -        return ErrCodes::WrongDeviceState; -    } - -    if (tag_data.settings.settings.appdata_initialized != 0) { -        LOG_ERROR(Service_NFP, "Application area already exist"); -        return ErrCodes::ApplicationAreaExist; -    } - -    if (data.size() != sizeof(ApplicationArea)) { -        LOG_ERROR(Service_NFP, "Wrong data size {}", data.size()); -        return ResultUnknown; -    } - -    std::memcpy(&tag_data.application_area, data.data(), sizeof(ApplicationArea)); -    tag_data.application_area_id = access_id; - -    return ResultSuccess; -} - -Result Module::Interface::RecreateApplicationArea(u32 access_id, const std::vector<u8>& data) { -    if (device_state != DeviceState::TagMounted) { -        LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); -        if (device_state == DeviceState::TagRemoved) { -            return ErrCodes::TagRemoved; -        } -        return ErrCodes::WrongDeviceState; -    } - -    if (data.size() != sizeof(ApplicationArea)) { -        LOG_ERROR(Service_NFP, "Wrong data size {}", data.size()); -        return ResultUnknown; -    } - -    std::memcpy(&tag_data.application_area, data.data(), sizeof(ApplicationArea)); -    tag_data.application_area_id = access_id; - -    return ResultSuccess; -} - -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 first connected npad id as a workaround for lack of a single nfc interface per -    // controller -    return system.HIDCore().GetFirstNpadId(); -} - -AmiiboName Module::Interface::GetAmiiboName(const AmiiboSettings& settings) const { -    std::array<char16_t, amiibo_name_length> settings_amiibo_name{}; -    AmiiboName amiibo_name{}; - -    // Convert from big endian to little endian -    for (std::size_t i = 0; i < amiibo_name_length; i++) { -        settings_amiibo_name[i] = static_cast<u16>(settings.amiibo_name[i]); +        IPC::ResponseBuilder rb{ctx, 2, 0, 1}; +        rb.Push(ResultSuccess); +        rb.PushIpcInterface<IUser>(user_interface);      } -    // Convert from utf16 to utf8 -    const auto amiibo_name_utf8 = Common::UTF16ToUTF8(settings_amiibo_name.data()); -    memcpy(amiibo_name.data(), amiibo_name_utf8.data(), amiibo_name_utf8.size()); - -    return amiibo_name; -} +    std::shared_ptr<IUser> user_interface; +};  void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system) { -    auto module = std::make_shared<Module>(); -    std::make_shared<NFP_User>(module, system)->InstallAsService(service_manager); +    std::make_shared<IUserManager>(system)->InstallAsService(service_manager);  }  } // namespace Service::NFP diff --git a/src/core/hle/service/nfp/nfp.h b/src/core/hle/service/nfp/nfp.h index 0de0b48e7..a25c362b8 100644 --- a/src/core/hle/service/nfp/nfp.h +++ b/src/core/hle/service/nfp/nfp.h @@ -3,170 +3,9 @@  #pragma once -#include <array> -#include <vector> - -#include "common/common_funcs.h" -#include "core/hle/service/kernel_helpers.h" -#include "core/hle/service/mii/types.h" -#include "core/hle/service/nfp/amiibo_types.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 { -using AmiiboName = std::array<char, (amiibo_name_length * 4) + 1>; - -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 constant_value;          // Must be 02 -    INSERT_PADDING_BYTES(0x38); // Unknown -}; -static_assert(sizeof(ModelInfo) == 0x40, "ModelInfo is an invalid size"); - -struct RegisterInfo { -    Service::Mii::CharInfo mii_char_info; -    u16 first_write_year; -    u8 first_write_month; -    u8 first_write_day; -    AmiiboName amiibo_name; -    u8 font_region; -    INSERT_PADDING_BYTES(0x7A); -}; -static_assert(sizeof(RegisterInfo) == 0x100, "RegisterInfo is an invalid size"); - -class Module final { -public: -    class Interface : public ServiceFramework<Interface> { -    public: -        explicit Interface(std::shared_ptr<Module> module_, Core::System& system_, -                           const char* name); -        ~Interface() override; - -        void CreateUserInterface(Kernel::HLERequestContext& ctx); -        bool LoadAmiibo(const std::string& filename); -        bool LoadAmiiboFile(const std::string& filename); -        void CloseAmiibo(); - -        void Initialize(); -        void Finalize(); - -        Result StartDetection(s32 protocol_); -        Result StopDetection(); -        Result Mount(); -        Result Unmount(); -        Result Flush(); - -        Result GetTagInfo(TagInfo& tag_info) const; -        Result GetCommonInfo(CommonInfo& common_info) const; -        Result GetModelInfo(ModelInfo& model_info) const; -        Result GetRegisterInfo(RegisterInfo& register_info) const; - -        Result OpenApplicationArea(u32 access_id); -        Result GetApplicationArea(ApplicationArea& data) const; -        Result SetApplicationArea(const std::vector<u8>& data); -        Result CreateApplicationArea(u32 access_id, const std::vector<u8>& data); -        Result RecreateApplicationArea(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: -        AmiiboName GetAmiiboName(const AmiiboSettings& settings) const; - -        const Core::HID::NpadIdType npad_id; - -        bool is_data_decoded{}; -        bool is_application_area_initialized{}; -        s32 protocol; -        std::string file_path{}; -        Kernel::KEvent* activate_event; -        Kernel::KEvent* deactivate_event; -        DeviceState device_state{DeviceState::Unaviable}; -        KernelHelpers::ServiceContext service_context; - -        NTAG215File tag_data{}; -        EncryptedNTAG215File encrypted_tag_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 Flush(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); -    void RecreateApplicationArea(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); diff --git a/src/core/hle/service/nfp/nfp_device.cpp b/src/core/hle/service/nfp/nfp_device.cpp new file mode 100644 index 000000000..da7daae88 --- /dev/null +++ b/src/core/hle/service/nfp/nfp_device.cpp @@ -0,0 +1,572 @@ +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include <array> +#include <atomic> + +#include "common/fs/file.h" +#include "common/fs/path_util.h" +#include "common/input.h" +#include "common/logging/log.h" +#include "common/string_util.h" +#include "common/tiny_mt.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/mii/mii_manager.h" +#include "core/hle/service/nfp/amiibo_crypto.h" +#include "core/hle/service/nfp/nfp.h" +#include "core/hle/service/nfp/nfp_device.h" +#include "core/hle/service/nfp/nfp_result.h" +#include "core/hle/service/nfp/nfp_user.h" + +namespace Service::NFP { +NfpDevice::NfpDevice(Core::HID::NpadIdType npad_id_, Core::System& system_, +                     KernelHelpers::ServiceContext& service_context_, +                     Kernel::KEvent* availability_change_event_) +    : npad_id{npad_id_}, system{system_}, service_context{service_context_}, +      availability_change_event{availability_change_event_} { +    activate_event = service_context.CreateEvent("IUser:NFPActivateEvent"); +    deactivate_event = service_context.CreateEvent("IUser:NFPDeactivateEvent"); +    npad_device = system.HIDCore().GetEmulatedController(npad_id); + +    Core::HID::ControllerUpdateCallback engine_callback{ +        .on_change = [this](Core::HID::ControllerTriggerType type) { NpadUpdate(type); }, +        .is_npad_service = false, +    }; +    is_controller_set = true; +    callback_key = npad_device->SetCallback(engine_callback); +} + +NfpDevice::~NfpDevice() { +    if (is_controller_set) { +        npad_device->DeleteCallback(callback_key); +        is_controller_set = false; +    } +}; + +void NfpDevice::NpadUpdate(Core::HID::ControllerTriggerType type) { +    if (type == Core::HID::ControllerTriggerType::Connected || +        type == Core::HID::ControllerTriggerType::Disconnected) { +        availability_change_event->GetWritableEvent().Signal(); +        return; +    } + +    if (type != Core::HID::ControllerTriggerType::Nfc) { +        return; +    } + +    if (!npad_device->IsConnected()) { +        return; +    } + +    const auto nfc_status = npad_device->GetNfc(); +    switch (nfc_status.state) { +    case Common::Input::NfcState::NewAmiibo: +        LoadAmiibo(nfc_status.data); +        break; +    case Common::Input::NfcState::AmiiboRemoved: +        if (device_state != DeviceState::SearchingForTag) { +            CloseAmiibo(); +        } +        break; +    default: +        break; +    } +} + +bool NfpDevice::LoadAmiibo(const std::vector<u8>& data) { +    if (device_state != DeviceState::SearchingForTag) { +        LOG_ERROR(Service_NFP, "Game is not looking for amiibos, current state {}", device_state); +        return false; +    } + +    if (data.size() != sizeof(EncryptedNTAG215File)) { +        LOG_ERROR(Service_NFP, "Not an amiibo, size={}", data.size()); +        return false; +    } + +    memcpy(&encrypted_tag_data, data.data(), sizeof(EncryptedNTAG215File)); + +    if (!AmiiboCrypto::IsAmiiboValid(encrypted_tag_data)) { +        LOG_INFO(Service_NFP, "Invalid amiibo"); +        return false; +    } + +    device_state = DeviceState::TagFound; +    activate_event->GetWritableEvent().Signal(); +    return true; +} + +void NfpDevice::CloseAmiibo() { +    LOG_INFO(Service_NFP, "Remove amiibo"); + +    if (device_state == DeviceState::TagMounted) { +        Unmount(); +    } + +    device_state = DeviceState::TagRemoved; +    encrypted_tag_data = {}; +    tag_data = {}; +    deactivate_event->GetWritableEvent().Signal(); +} + +Kernel::KReadableEvent& NfpDevice::GetActivateEvent() const { +    return activate_event->GetReadableEvent(); +} + +Kernel::KReadableEvent& NfpDevice::GetDeactivateEvent() const { +    return deactivate_event->GetReadableEvent(); +} + +void NfpDevice::Initialize() { +    device_state = npad_device->HasNfc() ? DeviceState::Initialized : DeviceState::Unavailable; +    encrypted_tag_data = {}; +    tag_data = {}; +} + +void NfpDevice::Finalize() { +    if (device_state == DeviceState::TagMounted) { +        Unmount(); +    } +    if (device_state == DeviceState::SearchingForTag || device_state == DeviceState::TagRemoved) { +        StopDetection(); +    } +    device_state = DeviceState::Unavailable; +} + +Result NfpDevice::StartDetection(s32 protocol_) { +    // 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 WrongDeviceState; +} + +Result NfpDevice::StopDetection() { +    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 WrongDeviceState; +} + +Result NfpDevice::Flush() { +    auto& settings = tag_data.settings; + +    if (settings.write_date.raw_date != settings.write_date.raw_date) { +        // TODO: Read current system date +        settings.write_date.SetYear(2022); +        settings.write_date.SetMonth(9); +        settings.write_date.SetDay(9); +        settings.crc_counter++; +        // TODO: Find how to calculate the crc check +        // settings.crc = CalculateCRC(settings); +    } + +    tag_data.write_counter++; + +    if (!AmiiboCrypto::EncodeAmiibo(tag_data, encrypted_tag_data)) { +        LOG_ERROR(Service_NFP, "Failed to encode data"); +        return WriteAmiiboFailed; +    } + +    std::vector<u8> data(sizeof(encrypted_tag_data)); +    memcpy(data.data(), &encrypted_tag_data, sizeof(encrypted_tag_data)); + +    if (!npad_device->WriteNfc(data)) { +        LOG_ERROR(Service_NFP, "Error writing to file"); +        return WriteAmiiboFailed; +    } + +    is_data_moddified = false; + +    return ResultSuccess; +} + +Result NfpDevice::Mount() { +    if (device_state != DeviceState::TagFound) { +        LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); +        return WrongDeviceState; +    } + +    if (!AmiiboCrypto::DecodeAmiibo(encrypted_tag_data, tag_data)) { +        LOG_ERROR(Service_NFP, "Can't decode amiibo {}", device_state); +        return CorruptedData; +    } + +    device_state = DeviceState::TagMounted; +    return ResultSuccess; +} + +Result NfpDevice::Unmount() { +    if (device_state != DeviceState::TagMounted) { +        LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); +        return WrongDeviceState; +    } + +    // Save data before unloading the amiibo +    if (is_data_moddified) { +        Flush(); +    } + +    device_state = DeviceState::TagFound; +    return ResultSuccess; +} + +Result NfpDevice::GetTagInfo(TagInfo& tag_info) const { +    if (device_state != DeviceState::TagFound && device_state != DeviceState::TagMounted) { +        LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); +        return WrongDeviceState; +    } + +    tag_info = { +        .uuid = encrypted_tag_data.uuid, +        .uuid_length = static_cast<u8>(encrypted_tag_data.uuid.size()), +        .protocol = protocol, +        .tag_type = static_cast<u32>(encrypted_tag_data.user_memory.model_info.amiibo_type), +    }; + +    return ResultSuccess; +} + +Result NfpDevice::GetCommonInfo(CommonInfo& common_info) const { +    if (device_state != DeviceState::TagMounted) { +        LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); +        return WrongDeviceState; +    } + +    const auto& settings = tag_data.settings; +    const u32 application_area_size = +        tag_data.settings.settings.appdata_initialized == 0 ? 0 : sizeof(ApplicationArea); + +    // TODO: Validate this data +    common_info = { +        .last_write_date = +            { +                settings.write_date.GetYear(), +                settings.write_date.GetMonth(), +                settings.write_date.GetDay(), +            }, +        .write_counter = tag_data.write_counter, +        .version = 1, +        .application_area_size = application_area_size, +    }; +    return ResultSuccess; +} + +Result NfpDevice::GetModelInfo(ModelInfo& model_info) const { +    if (device_state != DeviceState::TagMounted) { +        LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); +        return WrongDeviceState; +    } + +    const auto& model_info_data = encrypted_tag_data.user_memory.model_info; +    model_info = { +        .character_id = model_info_data.character_id, +        .character_variant = model_info_data.character_variant, +        .amiibo_type = model_info_data.amiibo_type, +        .model_number = model_info_data.model_number, +        .series = model_info_data.series, +    }; +    return ResultSuccess; +} + +Result NfpDevice::GetRegisterInfo(RegisterInfo& register_info) const { +    if (device_state != DeviceState::TagMounted) { +        LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); +        if (device_state == DeviceState::TagRemoved) { +            return TagRemoved; +        } +        return WrongDeviceState; +    } + +    if (tag_data.settings.settings.amiibo_initialized == 0) { +        return RegistrationIsNotInitialized; +    } + +    Service::Mii::MiiManager manager; +    const auto& settings = tag_data.settings; + +    // TODO: Validate this data +    register_info = { +        .mii_char_info = manager.ConvertV3ToCharInfo(tag_data.owner_mii), +        .creation_date = +            { +                settings.init_date.GetYear(), +                settings.init_date.GetMonth(), +                settings.init_date.GetDay(), +            }, +        .amiibo_name = GetAmiiboName(settings), +        .font_region = {}, +    }; + +    return ResultSuccess; +} + +Result NfpDevice::SetNicknameAndOwner(const AmiiboName& amiibo_name) { +    if (device_state != DeviceState::TagMounted) { +        LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); +        if (device_state == DeviceState::TagRemoved) { +            return TagRemoved; +        } +        return WrongDeviceState; +    } + +    Service::Mii::MiiManager manager; +    auto& settings = tag_data.settings; + +    // TODO: Read current system date +    settings.init_date.SetYear(2022); +    settings.init_date.SetMonth(9); +    settings.init_date.SetDay(9); +    settings.write_date.SetYear(2022); +    settings.write_date.SetMonth(9); +    settings.write_date.SetDay(9); +    settings.crc_counter++; +    // TODO: Find how to calculate the crc check +    // settings.crc = CalculateCRC(settings); + +    SetAmiiboName(settings, amiibo_name); +    tag_data.owner_mii = manager.ConvertCharInfoToV3(manager.BuildDefault(0)); +    settings.settings.amiibo_initialized.Assign(1); + +    return Flush(); +} + +Result NfpDevice::RestoreAmiibo() { +    // TODO: Load amiibo from backup on system +    LOG_ERROR(Service_NFP, "Not Implemented"); +    return ResultSuccess; +} + +Result NfpDevice::DeleteAllData() { +    const auto result = DeleteApplicationArea(); +    if (result.IsError()) { +        return result; +    } + +    if (device_state != DeviceState::TagMounted) { +        LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); +        if (device_state == DeviceState::TagRemoved) { +            return TagRemoved; +        } +        return WrongDeviceState; +    } + +    Common::TinyMT rng{}; +    rng.GenerateRandomBytes(&tag_data.owner_mii, sizeof(tag_data.owner_mii)); +    tag_data.settings.settings.amiibo_initialized.Assign(0); + +    return Flush(); +} + +Result NfpDevice::OpenApplicationArea(u32 access_id) { +    if (device_state != DeviceState::TagMounted) { +        LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); +        if (device_state == DeviceState::TagRemoved) { +            return TagRemoved; +        } +        return WrongDeviceState; +    } + +    if (tag_data.settings.settings.appdata_initialized.Value() == 0) { +        LOG_WARNING(Service_NFP, "Application area is not initialized"); +        return ApplicationAreaIsNotInitialized; +    } + +    if (tag_data.application_area_id != access_id) { +        LOG_WARNING(Service_NFP, "Wrong application area id"); +        return WrongApplicationAreaId; +    } + +    return ResultSuccess; +} + +Result NfpDevice::GetApplicationArea(std::vector<u8>& data) const { +    if (device_state != DeviceState::TagMounted) { +        LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); +        if (device_state == DeviceState::TagRemoved) { +            return TagRemoved; +        } +        return WrongDeviceState; +    } + +    if (tag_data.settings.settings.appdata_initialized.Value() == 0) { +        LOG_ERROR(Service_NFP, "Application area is not initialized"); +        return ApplicationAreaIsNotInitialized; +    } + +    if (data.size() > sizeof(ApplicationArea)) { +        LOG_ERROR(Service_NFP, "Wrong data size {}", data.size()); +        return ResultUnknown; +    } + +    memcpy(data.data(), tag_data.application_area.data(), data.size()); + +    return ResultSuccess; +} + +Result NfpDevice::SetApplicationArea(const std::vector<u8>& data) { +    if (device_state != DeviceState::TagMounted) { +        LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); +        if (device_state == DeviceState::TagRemoved) { +            return TagRemoved; +        } +        return WrongDeviceState; +    } + +    if (tag_data.settings.settings.appdata_initialized.Value() == 0) { +        LOG_ERROR(Service_NFP, "Application area is not initialized"); +        return ApplicationAreaIsNotInitialized; +    } + +    if (data.size() > sizeof(ApplicationArea)) { +        LOG_ERROR(Service_NFP, "Wrong data size {}", data.size()); +        return ResultUnknown; +    } + +    Common::TinyMT rng{}; +    rng.GenerateRandomBytes(tag_data.application_area.data(), sizeof(ApplicationArea)); +    std::memcpy(tag_data.application_area.data(), data.data(), data.size()); + +    tag_data.applicaton_write_counter++; +    is_data_moddified = true; + +    return ResultSuccess; +} + +Result NfpDevice::CreateApplicationArea(u32 access_id, const std::vector<u8>& data) { +    if (device_state != DeviceState::TagMounted) { +        LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); +        if (device_state == DeviceState::TagRemoved) { +            return TagRemoved; +        } +        return WrongDeviceState; +    } + +    if (tag_data.settings.settings.appdata_initialized.Value() != 0) { +        LOG_ERROR(Service_NFP, "Application area already exist"); +        return ApplicationAreaExist; +    } + +    return RecreateApplicationArea(access_id, data); +} + +Result NfpDevice::RecreateApplicationArea(u32 access_id, const std::vector<u8>& data) { +    if (device_state != DeviceState::TagMounted) { +        LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); +        if (device_state == DeviceState::TagRemoved) { +            return TagRemoved; +        } +        return WrongDeviceState; +    } + +    if (data.size() > sizeof(ApplicationArea)) { +        LOG_ERROR(Service_NFP, "Wrong data size {}", data.size()); +        return ResultUnknown; +    } + +    Common::TinyMT rng{}; +    std::memcpy(tag_data.application_area.data(), data.data(), data.size()); +    // HW seems to fill excess data with garbage +    rng.GenerateRandomBytes(tag_data.application_area.data() + data.size(), +                            sizeof(ApplicationArea) - data.size()); + +    // TODO: Investigate why the title id needs to be moddified +    tag_data.title_id = system.GetCurrentProcessProgramID(); +    tag_data.title_id = tag_data.title_id | 0x30000000ULL; +    tag_data.settings.settings.appdata_initialized.Assign(1); +    tag_data.application_area_id = access_id; +    tag_data.applicaton_write_counter++; +    tag_data.unknown = {}; + +    return Flush(); +} + +Result NfpDevice::DeleteApplicationArea() { +    if (device_state != DeviceState::TagMounted) { +        LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); +        if (device_state == DeviceState::TagRemoved) { +            return TagRemoved; +        } +        return WrongDeviceState; +    } + +    Common::TinyMT rng{}; +    rng.GenerateRandomBytes(tag_data.application_area.data(), sizeof(ApplicationArea)); +    rng.GenerateRandomBytes(&tag_data.title_id, sizeof(u64)); +    rng.GenerateRandomBytes(&tag_data.application_area_id, sizeof(u32)); +    tag_data.settings.settings.appdata_initialized.Assign(0); +    tag_data.applicaton_write_counter++; +    tag_data.unknown = {}; + +    return Flush(); +} + +u64 NfpDevice::GetHandle() const { +    // Generate a handle based of the npad id +    return static_cast<u64>(npad_id); +} + +u32 NfpDevice::GetApplicationAreaSize() const { +    // Investigate if this value is really constant +    return sizeof(ApplicationArea); +} + +DeviceState NfpDevice::GetCurrentState() const { +    return device_state; +} + +Core::HID::NpadIdType NfpDevice::GetNpadId() const { +    return npad_id; +} + +AmiiboName NfpDevice::GetAmiiboName(const AmiiboSettings& settings) const { +    std::array<char16_t, amiibo_name_length> settings_amiibo_name{}; +    AmiiboName amiibo_name{}; + +    // Convert from big endian to little endian +    for (std::size_t i = 0; i < amiibo_name_length; i++) { +        settings_amiibo_name[i] = static_cast<u16>(settings.amiibo_name[i]); +    } + +    // Convert from utf16 to utf8 +    const auto amiibo_name_utf8 = Common::UTF16ToUTF8(settings_amiibo_name.data()); +    memcpy(amiibo_name.data(), amiibo_name_utf8.data(), amiibo_name_utf8.size()); + +    return amiibo_name; +} + +void NfpDevice::SetAmiiboName(AmiiboSettings& settings, const AmiiboName& amiibo_name) { +    std::array<char16_t, amiibo_name_length> settings_amiibo_name{}; + +    // Convert from utf8 to utf16 +    const auto amiibo_name_utf16 = Common::UTF8ToUTF16(amiibo_name.data()); +    memcpy(settings_amiibo_name.data(), amiibo_name_utf16.data(), +           amiibo_name_utf16.size() * sizeof(char16_t)); + +    // Convert from little endian to big endian +    for (std::size_t i = 0; i < amiibo_name_length; i++) { +        settings.amiibo_name[i] = static_cast<u16_be>(settings_amiibo_name[i]); +    } +} + +} // namespace Service::NFP diff --git a/src/core/hle/service/nfp/nfp_device.h b/src/core/hle/service/nfp/nfp_device.h new file mode 100644 index 000000000..53cc0833f --- /dev/null +++ b/src/core/hle/service/nfp/nfp_device.h @@ -0,0 +1,97 @@ +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include <array> +#include <vector> + +#include "common/common_funcs.h" +#include "core/hle/service/kernel_helpers.h" +#include "core/hle/service/mii/types.h" +#include "core/hle/service/nfp/nfp_types.h" +#include "core/hle/service/service.h" + +namespace Kernel { +class KEvent; +class KReadableEvent; +} // namespace Kernel + +namespace Core { +class System; +} // namespace Core + +namespace Core::HID { +class EmulatedController; +enum class ControllerTriggerType; +enum class NpadIdType : u32; +} // namespace Core::HID + +namespace Service::NFP { +class NfpDevice { +public: +    NfpDevice(Core::HID::NpadIdType npad_id_, Core::System& system_, +              KernelHelpers::ServiceContext& service_context_, +              Kernel::KEvent* availability_change_event_); +    ~NfpDevice(); + +    void Initialize(); +    void Finalize(); + +    Result StartDetection(s32 protocol_); +    Result StopDetection(); +    Result Mount(); +    Result Unmount(); +    Result Flush(); + +    Result GetTagInfo(TagInfo& tag_info) const; +    Result GetCommonInfo(CommonInfo& common_info) const; +    Result GetModelInfo(ModelInfo& model_info) const; +    Result GetRegisterInfo(RegisterInfo& register_info) const; + +    Result SetNicknameAndOwner(const AmiiboName& amiibo_name); +    Result RestoreAmiibo(); +    Result DeleteAllData(); + +    Result OpenApplicationArea(u32 access_id); +    Result GetApplicationArea(std::vector<u8>& data) const; +    Result SetApplicationArea(const std::vector<u8>& data); +    Result CreateApplicationArea(u32 access_id, const std::vector<u8>& data); +    Result RecreateApplicationArea(u32 access_id, const std::vector<u8>& data); +    Result DeleteApplicationArea(); + +    u64 GetHandle() const; +    u32 GetApplicationAreaSize() const; +    DeviceState GetCurrentState() const; +    Core::HID::NpadIdType GetNpadId() const; + +    Kernel::KReadableEvent& GetActivateEvent() const; +    Kernel::KReadableEvent& GetDeactivateEvent() const; + +private: +    void NpadUpdate(Core::HID::ControllerTriggerType type); +    bool LoadAmiibo(const std::vector<u8>& data); +    void CloseAmiibo(); + +    AmiiboName GetAmiiboName(const AmiiboSettings& settings) const; +    void SetAmiiboName(AmiiboSettings& settings, const AmiiboName& amiibo_name); + +    bool is_controller_set{}; +    int callback_key; +    const Core::HID::NpadIdType npad_id; +    Core::System& system; +    Core::HID::EmulatedController* npad_device = nullptr; +    KernelHelpers::ServiceContext& service_context; +    Kernel::KEvent* activate_event = nullptr; +    Kernel::KEvent* deactivate_event = nullptr; +    Kernel::KEvent* availability_change_event = nullptr; + +    bool is_data_moddified{}; +    s32 protocol{}; +    DeviceState device_state{DeviceState::Unavailable}; + +    NTAG215File tag_data{}; +    EncryptedNTAG215File encrypted_tag_data{}; +}; + +} // namespace Service::NFP diff --git a/src/core/hle/service/nfp/nfp_result.h b/src/core/hle/service/nfp/nfp_result.h new file mode 100644 index 000000000..15bc02b15 --- /dev/null +++ b/src/core/hle/service/nfp/nfp_result.h @@ -0,0 +1,21 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include "core/hle/result.h" + +namespace Service::NFP { + +constexpr Result DeviceNotFound(ErrorModule::NFP, 64); +constexpr Result WrongDeviceState(ErrorModule::NFP, 73); +constexpr Result NfcDisabled(ErrorModule::NFP, 80); +constexpr Result WriteAmiiboFailed(ErrorModule::NFP, 88); +constexpr Result TagRemoved(ErrorModule::NFP, 97); +constexpr Result RegistrationIsNotInitialized(ErrorModule::NFP, 120); +constexpr Result ApplicationAreaIsNotInitialized(ErrorModule::NFP, 128); +constexpr Result CorruptedData(ErrorModule::NFP, 144); +constexpr Result WrongApplicationAreaId(ErrorModule::NFP, 152); +constexpr Result ApplicationAreaExist(ErrorModule::NFP, 168); + +} // namespace Service::NFP diff --git a/src/core/hle/service/nfp/amiibo_types.h b/src/core/hle/service/nfp/nfp_types.h index bf2de811a..d58657a21 100644 --- a/src/core/hle/service/nfp/amiibo_types.h +++ b/src/core/hle/service/nfp/nfp_types.h @@ -27,7 +27,7 @@ enum class DeviceState : u32 {      TagFound,      TagRemoved,      TagMounted, -    Unaviable, +    Unavailable,      Finalized,  }; @@ -36,6 +36,7 @@ enum class ModelType : u32 {  };  enum class MountTarget : u32 { +    None,      Rom,      Ram,      All, @@ -76,18 +77,36 @@ enum class AmiiboSeries : u8 {  using TagUuid = std::array<u8, 10>;  using HashData = std::array<u8, 0x20>;  using ApplicationArea = std::array<u8, 0xD8>; +using AmiiboName = std::array<char, (amiibo_name_length * 4) + 1>;  struct AmiiboDate { -    u16 raw_date{}; +    u16_be raw_date{}; + +    u16 DateRaw() const { +        return static_cast<u16>(raw_date); +    }      u16 GetYear() const { -        return static_cast<u16>(((raw_date & 0xFE00) >> 9) + 2000); +        return static_cast<u16>(((DateRaw() & 0xFE00) >> 9) + 2000);      }      u8 GetMonth() const { -        return static_cast<u8>(((raw_date & 0x01E0) >> 5) - 1); +        return static_cast<u8>(((DateRaw() & 0x01E0) >> 5) - 1);      }      u8 GetDay() const { -        return static_cast<u8>(raw_date & 0x001F); +        return static_cast<u8>(DateRaw() & 0x001F); +    } + +    void SetYear(u16 year) { +        raw_date = DateRaw() & ~0xFE00; +        raw_date |= static_cast<u16_be>((year - 2000) << 9); +    } +    void SetMonth(u8 month) { +        raw_date = DateRaw() & ~0x01E0; +        raw_date |= static_cast<u16_be>((month + 1) << 5); +    } +    void SetDay(u8 day) { +        raw_date = DateRaw() & ~0x001F; +        raw_date |= static_cast<u16_be>(day);      }  };  static_assert(sizeof(AmiiboDate) == 2, "AmiiboDate is an invalid size"); @@ -134,7 +153,7 @@ static_assert(sizeof(NTAG215Password) == 0x8, "NTAG215Password is an invalid siz  #pragma pack(1)  struct EncryptedAmiiboFile {      u8 constant_value;                     // Must be A5 -    u16 write_counter;                     // Number of times the amiibo has been written? +    u16_be write_counter;                  // Number of times the amiibo has been written?      INSERT_PADDING_BYTES(0x1);             // Unknown 1      AmiiboSettings settings;               // Encrypted amiibo settings      HashData hmac_tag;                     // Hash @@ -157,7 +176,7 @@ struct NTAG215File {      u32 compability_container; // Defines available memory      HashData hmac_data;        // Hash      u8 constant_value;         // Must be A5 -    u16 write_counter;         // Number of times the amiibo has been written? +    u16_be write_counter;      // Number of times the amiibo has been written?      INSERT_PADDING_BYTES(0x1); // Unknown 1      AmiiboSettings settings;      Service::Mii::Ver3StoreData owner_mii; // Encrypted Mii data @@ -194,4 +213,50 @@ static_assert(sizeof(EncryptedNTAG215File) == 0x21C, "EncryptedNTAG215File is an  static_assert(std::is_trivially_copyable_v<EncryptedNTAG215File>,                "EncryptedNTAG215File must be trivially copyable."); +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 WriteDate { +    u16 year; +    u8 month; +    u8 day; +}; +static_assert(sizeof(WriteDate) == 0x4, "WriteDate is an invalid size"); + +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"); + +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"); + +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"); +  } // namespace Service::NFP diff --git a/src/core/hle/service/nfp/nfp_user.cpp b/src/core/hle/service/nfp/nfp_user.cpp index 2d7b156cf..f8f1975db 100644 --- a/src/core/hle/service/nfp/nfp_user.cpp +++ b/src/core/hle/service/nfp/nfp_user.cpp @@ -1,18 +1,644 @@  // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project  // SPDX-License-Identifier: GPL-2.0-or-later +#include <array> +#include <atomic> + +#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/mii/mii_manager.h" +#include "core/hle/service/nfp/nfp_device.h" +#include "core/hle/service/nfp/nfp_result.h"  #include "core/hle/service/nfp/nfp_user.h"  namespace Service::NFP { -NFP_User::NFP_User(std::shared_ptr<Module> module_, Core::System& system_) -    : Interface(std::move(module_), system_, "nfp:user") { +IUser::IUser(Core::System& system_) +    : ServiceFramework{system_, "NFP::IUser"}, service_context{system_, service_name} {      static const FunctionInfo functions[] = { -        {0, &NFP_User::CreateUserInterface, "CreateUserInterface"}, +        {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, &IUser::Flush, "Flush"}, +        {11, &IUser::Restore, "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, &IUser::RecreateApplicationArea, "RecreateApplicationArea"},      };      RegisterHandlers(functions); + +    availability_change_event = service_context.CreateEvent("IUser:AvailabilityChangeEvent"); + +    for (u32 device_index = 0; device_index < 10; device_index++) { +        devices[device_index] = +            std::make_shared<NfpDevice>(Core::HID::IndexToNpadIdType(device_index), system, +                                        service_context, availability_change_event); +    } +} + +void IUser::Initialize(Kernel::HLERequestContext& ctx) { +    LOG_INFO(Service_NFC, "called"); + +    state = State::Initialized; + +    for (auto& device : devices) { +        device->Initialize(); +    } + +    IPC::ResponseBuilder rb{ctx, 2, 0}; +    rb.Push(ResultSuccess); +} + +void IUser::Finalize(Kernel::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 IUser::ListDevices(Kernel::HLERequestContext& ctx) { +    LOG_INFO(Service_NFP, "called"); + +    if (state == State::NonInitialized) { +        IPC::ResponseBuilder rb{ctx, 2}; +        rb.Push(NfcDisabled); +        return; +    } + +    std::vector<u64> nfp_devices; +    const std::size_t max_allowed_devices = ctx.GetWriteBufferSize() / sizeof(u64); + +    for (auto& device : devices) { +        if (nfp_devices.size() >= max_allowed_devices) { +            continue; +        } +        if (device->GetCurrentState() != DeviceState::Unavailable) { +            nfp_devices.push_back(device->GetHandle()); +        } +    } + +    if (nfp_devices.size() == 0) { +        IPC::ResponseBuilder rb{ctx, 2}; +        rb.Push(DeviceNotFound); +        return; +    } + +    ctx.WriteBuffer(nfp_devices); + +    IPC::ResponseBuilder rb{ctx, 3}; +    rb.Push(ResultSuccess); +    rb.Push(static_cast<s32>(nfp_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); + +    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()->StartDetection(nfp_protocol); +    IPC::ResponseBuilder rb{ctx, 2}; +    rb.Push(result); +} + +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); + +    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()->StopDetection(); +    IPC::ResponseBuilder rb{ctx, 2}; +    rb.Push(result); +} + +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); + +    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()->Mount(); +    IPC::ResponseBuilder rb{ctx, 2}; +    rb.Push(result); +} + +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); + +    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()->Unmount(); +    IPC::ResponseBuilder rb{ctx, 2}; +    rb.Push(result);  } -NFP_User::~NFP_User() = default; +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_INFO(Service_NFP, "called, device_handle={}, access_id={}", device_handle, access_id); + +    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()->OpenApplicationArea(access_id); +    IPC::ResponseBuilder rb{ctx, 2}; +    rb.Push(result); +} + +void IUser::GetApplicationArea(Kernel::HLERequestContext& ctx) { +    IPC::RequestParser rp{ctx}; +    const auto device_handle{rp.Pop<u64>()}; +    const auto data_size = ctx.GetWriteBufferSize(); +    LOG_INFO(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; +    } + +    std::vector<u8> data(data_size); +    const auto result = device.value()->GetApplicationArea(data); +    ctx.WriteBuffer(data); +    IPC::ResponseBuilder rb{ctx, 3}; +    rb.Push(result); +    rb.Push(static_cast<u32>(data_size)); +} + +void IUser::SetApplicationArea(Kernel::HLERequestContext& ctx) { +    IPC::RequestParser rp{ctx}; +    const auto device_handle{rp.Pop<u64>()}; +    const auto data{ctx.ReadBuffer()}; +    LOG_INFO(Service_NFP, "called, device_handle={}, data_size={}", device_handle, data.size()); + +    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()->SetApplicationArea(data); +    IPC::ResponseBuilder rb{ctx, 2}; +    rb.Push(result); +} + +void IUser::Flush(Kernel::HLERequestContext& ctx) { +    IPC::RequestParser rp{ctx}; +    const auto device_handle{rp.Pop<u64>()}; +    LOG_INFO(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()->Flush(); +    IPC::ResponseBuilder rb{ctx, 2}; +    rb.Push(result); +} + +void IUser::Restore(Kernel::HLERequestContext& ctx) { +    IPC::RequestParser rp{ctx}; +    const auto device_handle{rp.Pop<u64>()}; +    LOG_WARNING(Service_NFP, "(STUBBED) 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()->RestoreAmiibo(); +    IPC::ResponseBuilder rb{ctx, 2}; +    rb.Push(result); +} + +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_INFO(Service_NFP, "called, device_handle={}, data_size={}, access_id={}", device_handle, +             access_id, data.size()); + +    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()->CreateApplicationArea(access_id, data); +    IPC::ResponseBuilder rb{ctx, 2}; +    rb.Push(result); +} + +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); + +    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; +    } + +    TagInfo tag_info{}; +    const auto result = device.value()->GetTagInfo(tag_info); +    ctx.WriteBuffer(tag_info); +    IPC::ResponseBuilder rb{ctx, 2}; +    rb.Push(result); +} + +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); + +    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; +    } + +    RegisterInfo register_info{}; +    const auto result = device.value()->GetRegisterInfo(register_info); +    ctx.WriteBuffer(register_info); +    IPC::ResponseBuilder rb{ctx, 2}; +    rb.Push(result); +} + +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); + +    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; +    } + +    CommonInfo common_info{}; +    const auto result = device.value()->GetCommonInfo(common_info); +    ctx.WriteBuffer(common_info); +    IPC::ResponseBuilder rb{ctx, 2}; +    rb.Push(result); +} + +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); + +    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; +    } + +    ModelInfo model_info{}; +    const auto result = device.value()->GetModelInfo(model_info); +    ctx.WriteBuffer(model_info); +    IPC::ResponseBuilder rb{ctx, 2}; +    rb.Push(result); +} + +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); + +    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; +    } + +    IPC::ResponseBuilder rb{ctx, 2, 1}; +    rb.Push(ResultSuccess); +    rb.PushCopyObjects(device.value()->GetActivateEvent()); +} + +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); + +    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; +    } + +    IPC::ResponseBuilder rb{ctx, 2, 1}; +    rb.Push(ResultSuccess); +    rb.PushCopyObjects(device.value()->GetDeactivateEvent()); +} + +void IUser::GetState(Kernel::HLERequestContext& ctx) { +    LOG_DEBUG(Service_NFC, "called"); + +    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); + +    auto device = GetNfpDevice(device_handle); + +    if (!device.has_value()) { +        IPC::ResponseBuilder rb{ctx, 2}; +        rb.Push(DeviceNotFound); +        return; +    } + +    IPC::ResponseBuilder rb{ctx, 3}; +    rb.Push(ResultSuccess); +    rb.PushEnum(device.value()->GetCurrentState()); +} + +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); + +    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; +    } + +    IPC::ResponseBuilder rb{ctx, 3}; +    rb.Push(ResultSuccess); +    rb.PushEnum(device.value()->GetNpadId()); +} + +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); + +    auto device = GetNfpDevice(device_handle); + +    if (!device.has_value()) { +        IPC::ResponseBuilder rb{ctx, 2}; +        rb.Push(DeviceNotFound); +        return; +    } + +    IPC::ResponseBuilder rb{ctx, 3}; +    rb.Push(ResultSuccess); +    rb.Push(device.value()->GetApplicationAreaSize()); +} + +void IUser::AttachAvailabilityChangeEvent(Kernel::HLERequestContext& ctx) { +    LOG_INFO(Service_NFP, "called"); + +    if (state == State::NonInitialized) { +        IPC::ResponseBuilder rb{ctx, 2}; +        rb.Push(NfcDisabled); +        return; +    } + +    IPC::ResponseBuilder rb{ctx, 2, 1}; +    rb.Push(ResultSuccess); +    rb.PushCopyObjects(availability_change_event->GetReadableEvent()); +} + +void IUser::RecreateApplicationArea(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_INFO(Service_NFP, "called, device_handle={}, data_size={}, access_id={}", device_handle, +             access_id, data.size()); + +    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()->RecreateApplicationArea(access_id, data); +    IPC::ResponseBuilder rb{ctx, 2}; +    rb.Push(result); +} + +std::optional<std::shared_ptr<NfpDevice>> IUser::GetNfpDevice(u64 handle) { +    for (auto& device : devices) { +        if (device->GetHandle() == handle) { +            return device; +        } +    } +    return std::nullopt; +}  } // namespace Service::NFP diff --git a/src/core/hle/service/nfp/nfp_user.h b/src/core/hle/service/nfp/nfp_user.h index 519ff56ee..68c60ae82 100644 --- a/src/core/hle/service/nfp/nfp_user.h +++ b/src/core/hle/service/nfp/nfp_user.h @@ -3,14 +3,52 @@  #pragma once +#include "core/hle/service/kernel_helpers.h"  #include "core/hle/service/nfp/nfp.h" +#include "core/hle/service/nfp/nfp_types.h"  namespace Service::NFP { +class NfpDevice; -class NFP_User final : public Module::Interface { +class IUser final : public ServiceFramework<IUser> {  public: -    explicit NFP_User(std::shared_ptr<Module> module_, Core::System& system_); -    ~NFP_User() override; +    explicit IUser(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 Flush(Kernel::HLERequestContext& ctx); +    void Restore(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); +    void RecreateApplicationArea(Kernel::HLERequestContext& ctx); + +    std::optional<std::shared_ptr<NfpDevice>> GetNfpDevice(u64 handle); + +    KernelHelpers::ServiceContext service_context; + +    std::array<std::shared_ptr<NfpDevice>, 10> devices{}; + +    State state{State::NonInitialized}; +    Kernel::KEvent* availability_change_event;  };  } // namespace Service::NFP | 
