code:
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later

#pragma once

#include <array>
#include <memory>
#include <optional>
#include <span>

#include "core/hle/service/kernel_helpers.h"
#include "core/hle/service/nfc/mifare_types.h"
#include "core/hle/service/nfc/nfc_types.h"
#include "core/hle/service/nfp/nfp_types.h"
#include "core/hle/service/service.h"
#include "hid_core/hid_types.h"

namespace Service::Set {
class ISystemSettingsServer;
}

namespace Service::NFC {
class NfcDevice;

class DeviceManager {
public:
    explicit DeviceManager(Core::System& system_, KernelHelpers::ServiceContext& service_context_);
    ~DeviceManager();

    // Nfc device manager
    Result Initialize();
    Result Finalize();
    Result ListDevices(std::vector<u64>& nfp_devices, std::size_t max_allowed_devices,
                       bool skip_fatal_errors) const;
    DeviceState GetDeviceState(u64 device_handle) const;
    Result GetNpadId(u64 device_handle, Core::HID::NpadIdType& npad_id);
    Kernel::KReadableEvent& AttachAvailabilityChangeEvent() const;
    Result StartDetection(u64 device_handle, NfcProtocol tag_protocol);
    Result StopDetection(u64 device_handle);
    Result GetTagInfo(u64 device_handle, NFP::TagInfo& tag_info);
    Result AttachActivateEvent(Kernel::KReadableEvent** event, u64 device_handle) const;
    Result AttachDeactivateEvent(Kernel::KReadableEvent** event, u64 device_handle) const;
    Result ReadMifare(u64 device_handle,
                      const std::span<const MifareReadBlockParameter> read_parameters,
                      std::span<MifareReadBlockData> read_data);
    Result WriteMifare(u64 device_handle,
                       std::span<const MifareWriteBlockParameter> write_parameters);
    Result SendCommandByPassThrough(u64 device_handle, const s64& timeout,
                                    std::span<const u8> command_data, std::span<u8> out_data);

    // Nfp device manager
    Result Mount(u64 device_handle, NFP::ModelType model_type, NFP::MountTarget mount_target);
    Result Unmount(u64 device_handle);
    Result OpenApplicationArea(u64 device_handle, u32 access_id);
    Result GetApplicationArea(u64 device_handle, std::span<u8> data);
    Result SetApplicationArea(u64 device_handle, std::span<const u8> data);
    Result Flush(u64 device_handle);
    Result Restore(u64 device_handle);
    Result CreateApplicationArea(u64 device_handle, u32 access_id, std::span<const u8> data);
    Result GetRegisterInfo(u64 device_handle, NFP::RegisterInfo& register_info);
    Result GetCommonInfo(u64 device_handle, NFP::CommonInfo& common_info);
    Result GetModelInfo(u64 device_handle, NFP::ModelInfo& model_info);
    u32 GetApplicationAreaSize() const;
    Result RecreateApplicationArea(u64 device_handle, u32 access_id, std::span<const u8> data);
    Result Format(u64 device_handle);
    Result GetAdminInfo(u64 device_handle, NFP::AdminInfo& admin_info);
    Result GetRegisterInfoPrivate(u64 device_handle, NFP::RegisterInfoPrivate& register_info);
    Result SetRegisterInfoPrivate(u64 device_handle, const NFP::RegisterInfoPrivate& register_info);
    Result DeleteRegisterInfo(u64 device_handle);
    Result DeleteApplicationArea(u64 device_handle);
    Result ExistsApplicationArea(u64 device_handle, bool& has_application_area);
    Result GetAll(u64 device_handle, NFP::NfpData& nfp_data);
    Result SetAll(u64 device_handle, const NFP::NfpData& nfp_data);
    Result FlushDebug(u64 device_handle);
    Result BreakTag(u64 device_handle, NFP::BreakType break_type);
    Result ReadBackupData(u64 device_handle, std::span<u8> data);
    Result WriteBackupData(u64 device_handle, std::span<const u8> data);
    Result WriteNtf(u64 device_handle, NFP::WriteType, std::span<const u8> data);

private:
    Result IsNfcEnabled() const;
    Result IsNfcParameterSet() const;
    Result IsNfcInitialized() const;

    Result CheckHandleOnList(u64 device_handle, std::span<const u64> device_list) const;

    Result GetDeviceFromHandle(u64 handle, std::shared_ptr<NfcDevice>& device,
                               bool check_state) const;

    Result GetDeviceHandle(u64 handle, std::shared_ptr<NfcDevice>& device) const;
    Result VerifyDeviceResult(std::shared_ptr<NfcDevice> device, Result operation_result);
    Result CheckDeviceState(std::shared_ptr<NfcDevice> device) const;

    std::optional<std::shared_ptr<NfcDevice>> GetNfcDevice(u64 handle);
    const std::optional<std::shared_ptr<NfcDevice>> GetNfcDevice(u64 handle) const;

    bool is_initialized = false;
    s64 time_since_last_error = 0;
    mutable std::mutex mutex;
    std::array<std::shared_ptr<NfcDevice>, 10> devices{};

    Core::System& system;
    KernelHelpers::ServiceContext service_context;
    Kernel::KEvent* availability_change_event;
    std::shared_ptr<Service::Set::ISystemSettingsServer> m_set_sys;
};

} // namespace Service::NFC


code:
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later

#pragma once

#include "core/hle/result.h"

namespace Service::NFC::Mifare {

constexpr Result ResultDeviceNotFound(ErrorModule::NFCMifare, 64);
constexpr Result ResultInvalidArgument(ErrorModule::NFCMifare, 65);
constexpr Result ResultWrongDeviceState(ErrorModule::NFCMifare, 73);
constexpr Result ResultNfcDisabled(ErrorModule::NFCMifare, 80);
constexpr Result ResultTagRemoved(ErrorModule::NFCMifare, 97);
constexpr Result ResultNotAMifare(ErrorModule::NFCMifare, 288);

} // namespace Service::NFC::Mifare

repo_name: litucks/torzu

license: mit


code:
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later

#pragma once

#include <array>

#include "common/common_funcs.h"
#include "common/common_types.h"

namespace Service::NFC {

enum class MifareCmd : u8 {
    None = 0x00,
    Read = 0x30,
    AuthA = 0x60,
    AuthB = 0x61,
    Write = 0xA0,
    Transfer = 0xB0,
    Decrement = 0xC0,
    Increment = 0xC1,
    Store = 0xC2
};

using DataBlock = std::array<u8, 0x10>;
using KeyData = std::array<u8, 0x6>;

struct SectorKey {
    MifareCmd command;
    u8 unknown; // Usually 1
    INSERT_PADDING_BYTES(0x6);
    KeyData sector_key;
    INSERT_PADDING_BYTES(0x2);
};
static_assert(sizeof(SectorKey) == 0x10, "SectorKey is an invalid size");

// This is nn::nfc::MifareReadBlockParameter
struct MifareReadBlockParameter {
    u8 sector_number{};
    INSERT_PADDING_BYTES(0x7);
    SectorKey sector_key{};
};
static_assert(sizeof(MifareReadBlockParameter) == 0x18,
              "MifareReadBlockParameter is an invalid size");

// This is nn::nfc::MifareReadBlockData
struct MifareReadBlockData {
    DataBlock data{};
    u8 sector_number{};
    INSERT_PADDING_BYTES(0x7);
};
static_assert(sizeof(MifareReadBlockData) == 0x18, "MifareReadBlockData is an invalid size");

// This is nn::nfc::MifareWriteBlockParameter
struct MifareWriteBlockParameter {
    DataBlock data;
    u8 sector_number;
    INSERT_PADDING_BYTES(0x7);
    SectorKey sector_key;
};
static_assert(sizeof(MifareWriteBlockParameter) == 0x28,
              "MifareWriteBlockParameter is an invalid size");

} // namespace Service::NFC


code:
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#include <memory>

#include "common/logging/log.h"
#include "common/settings.h"
#include "core/hle/service/ipc_helpers.h"
#include "core/hle/service/nfc/nfc.h"
#include "core/hle/service/nfc/nfc_interface.h"
#include "core/hle/service/server_manager.h"
#include "core/hle/service/service.h"

namespace Service::NFC {

class IUser final : public NfcInterface {
public:
    explicit IUser(Core::System& system_) : NfcInterface(system_, "NFC::IUser", BackendType::Nfc) {
        // clang-format off
        static const FunctionInfo functions[] = {
            {0, &NfcInterface::Initialize, "InitializeOld"},
            {1, &NfcInterface::Finalize, "FinalizeOld"},
            {2, &NfcInterface::GetState, "GetStateOld"},
            {3, &NfcInterface::IsNfcEnabled, "IsNfcEnabledOld"},
            {400, &NfcInterface::Initialize, "Initialize"},
            {401, &NfcInterface::Finalize, "Finalize"},
            {402, &NfcInterface::GetState, "GetState"},
            {403, &NfcInterface::IsNfcEnabled, "IsNfcEnabled"},
            {404, &NfcInterface::ListDevices, "ListDevices"},
            {405, &NfcInterface::GetDeviceState, "GetDeviceState"},
            {406, &NfcInterface::GetNpadId, "GetNpadId"},
            {407, &NfcInterface::AttachAvailabilityChangeEvent, "AttachAvailabilityChangeEvent"},
            {408, &NfcInterface::StartDetection, "StartDetection"},
            {409, &NfcInterface::StopDetection, "StopDetection"},
            {410, &NfcInterface::GetTagInfo, "GetTagInfo"},
            {411, &NfcInterface::AttachActivateEvent, "AttachActivateEvent"},
            {412, &NfcInterface::AttachDeactivateEvent, "AttachDeactivateEvent"},
            {1000, &NfcInterface::ReadMifare, "ReadMifare"},
            {1001, &NfcInterface::WriteMifare ,"WriteMifare"},
            {1300, &NfcInterface::SendCommandByPassThrough, "SendCommandByPassThrough"},
            {1301, nullptr, "KeepPassThroughSession"},
            {1302, nullptr, "ReleasePassThroughSession"},
        };
        // clang-format on

        RegisterHandlers(functions);
    }
};

class ISystem final : public NfcInterface {
public:
    explicit ISystem(Core::System& system_)
        : NfcInterface{system_, "NFC::ISystem", BackendType::Nfc} {
        // clang-format off
        static const FunctionInfo functions[] = {
            {0, &NfcInterface::Initialize, "InitializeOld"},
            {1, &NfcInterface::Finalize, "FinalizeOld"},
            {2, &NfcInterface::GetState, "GetStateOld"},
            {3, &NfcInterface::IsNfcEnabled, "IsNfcEnabledOld"},
            {100, &NfcInterface::SetNfcEnabled, "SetNfcEnabledOld"},
            {400, &NfcInterface::Initialize, "Initialize"},
            {401, &NfcInterface::Finalize, "Finalize"},
            {402, &NfcInterface::GetState, "GetState"},
            {403, &NfcInterface::IsNfcEnabled, "IsNfcEnabled"},
            {404, &NfcInterface::ListDevices, "ListDevices"},
            {405, &NfcInterface::GetDeviceState, "GetDeviceState"},
            {406, &NfcInterface::GetNpadId, "GetNpadId"},
            {407, &NfcInterface::AttachAvailabilityChangeEvent, "AttachAvailabilityChangeEvent"},
            {408, &NfcInterface::StartDetection, "StartDetection"},
            {409, &NfcInterface::StopDetection, "StopDetection"},
            {410, &NfcInterface::GetTagInfo, "GetTagInfo"},
            {411, &NfcInterface::AttachActivateEvent, "AttachActivateEvent"},
            {412, &NfcInterface::AttachDeactivateEvent, "AttachDeactivateEvent"},
            {500, &NfcInterface::SetNfcEnabled, "SetNfcEnabled"},
            {510, nullptr, "OutputTestWave"},
            {1000, &NfcInterface::ReadMifare, "ReadMifare"},
            {1001, &NfcInterface::WriteMifare, "WriteMifare"},
            {1300, &NfcInterface::SendCommandByPassThrough, "SendCommandByPassThrough"},
            {1301, nullptr, "KeepPassThroughSession"},
            {1302, nullptr, "ReleasePassThroughSession"},
        };
        // clang-format on

        RegisterHandlers(functions);
    }
};

// MFInterface has an unique interface but it's identical to NfcInterface so we can keep the code
// simpler
using MFInterface = NfcInterface;
class MFIUser final : public MFInterface {
public:
    explicit MFIUser(Core::System& system_)
        : MFInterface{system_, "NFC::MFInterface", BackendType::Mifare} {
        // clang-format off
        static const FunctionInfoTyped<MFIUser> functions[] = {
            {0, &MFIUser::Initialize, "Initialize"},
            {1, &MFIUser::Finalize, "Finalize"},
            {2, &MFIUser::ListDevices, "ListDevices"},
            {3, &MFIUser::StartDetection, "StartDetection"},
            {4, &MFIUser::StopDetection, "StopDetection"},
            {5, &MFIUser::ReadMifare, "Read"},
            {6, &MFIUser::WriteMifare, "Write"},
            {7, &MFIUser::GetTagInfo, "GetTagInfo"},
            {8, &MFIUser::AttachActivateEvent, "GetActivateEventHandle"},
            {9, &MFIUser::AttachDeactivateEvent, "GetDeactivateEventHandle"},
            {10, &MFIUser::GetState, "GetState"},
            {11, &MFIUser::GetDeviceState, "GetDeviceState"},
            {12, &MFIUser::GetNpadId, "GetNpadId"},
            {13, &MFIUser::AttachAvailabilityChangeEvent, "GetAvailabilityChangeEventHandle"},
        };
        // clang-format on

        RegisterHandlers(functions);
    }
};

class IAm final : public ServiceFramework<IAm> {
public:
    explicit IAm(Core::System& system_) : ServiceFramework{system_, "NFC::IAm"} {
        // clang-format off
        static const FunctionInfo functions[] = {
            {0, nullptr, "Initialize"},
            {1, nullptr, "Finalize"},
            {2, nullptr, "NotifyForegroundApplet"},
        };
        // clang-format on

        RegisterHandlers(functions);
    }
};

class NFC_AM final : public ServiceFramework<NFC_AM> {
public:
    explicit NFC_AM(Core::System& system_) : ServiceFramework{system_, "nfc:am"} {
        // clang-format off
        static const FunctionInfo functions[] = {
            {0, &NFC_AM::CreateAmNfcInterface, "CreateAmNfcInterface"},
        };
        // clang-format on

        RegisterHandlers(functions);
    }

private:
    void CreateAmNfcInterface(HLERequestContext& ctx) {
        LOG_DEBUG(Service_NFC, "called");

        IPC::ResponseBuilder rb{ctx, 2, 0, 1};
        rb.Push(ResultSuccess);
        rb.PushIpcInterface<IAm>(system);
    }
};

class NFC_MF_U final : public ServiceFramework<NFC_MF_U> {
public:
    explicit NFC_MF_U(Core::System& system_) : ServiceFramework{system_, "nfc:mf:u"} {
        // clang-format off
        static const FunctionInfo functions[] = {
            {0, &NFC_MF_U::CreateUserNfcInterface, "CreateUserNfcInterface"},
        };
        // clang-format on

        RegisterHandlers(functions);
    }

private:
    void CreateUserNfcInterface(HLERequestContext& ctx) {
        LOG_DEBUG(Service_NFC, "called");

        IPC::ResponseBuilder rb{ctx, 2, 0, 1};
        rb.Push(ResultSuccess);
        rb.PushIpcInterface<MFIUser>(system);
    }
};

class NFC_U final : public ServiceFramework<NFC_U> {
public:
    explicit NFC_U(Core::System& system_) : ServiceFramework{system_, "nfc:user"} {
        // clang-format off
        static const FunctionInfo functions[] = {
            {0, &NFC_U::CreateUserNfcInterface, "CreateUserNfcInterface"},
        };
        // clang-format on

        RegisterHandlers(functions);
    }

private:
    void CreateUserNfcInterface(HLERequestContext& ctx) {
        LOG_DEBUG(Service_NFC, "called");

        IPC::ResponseBuilder rb{ctx, 2, 0, 1};
        rb.Push(ResultSuccess);
        rb.PushIpcInterface<IUser>(system);
    }
};

class NFC_SYS final : public ServiceFramework<NFC_SYS> {
public:
    explicit NFC_SYS(Core::System& system_) : ServiceFramework{system_, "nfc:sys"} {
        // clang-format off
        static const FunctionInfo functions[] = {
            {0, &NFC_SYS::CreateSystemNfcInterface, "CreateSystemNfcInterface"},
        };
        // clang-format on

        RegisterHandlers(functions);
    }

private:
    void CreateSystemNfcInterface(HLERequestContext& ctx) {
        LOG_DEBUG(Service_NFC, "called");

        IPC::ResponseBuilder rb{ctx, 2, 0, 1};
        rb.Push(ResultSuccess);
        rb.PushIpcInterface<ISystem>(system);
    }
};

void LoopProcess(Core::System& system) {
    auto server_manager = std::make_unique<ServerManager>(system);

    server_manager->RegisterNamedService("nfc:am", std::make_shared<NFC_AM>(system));
    server_manager->RegisterNamedService("nfc:mf:u", std::make_shared<NFC_MF_U>(system));
    server_manager->RegisterNamedService("nfc:user", std::make_shared<NFC_U>(system));
    server_manager->RegisterNamedService("nfc:sys", std::make_shared<NFC_SYS>(system));

    ServerManager::RunServer(std::move(server_manager));
}

} // namespace Service::NFC


code:
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#pragma once

namespace Core {
class System;
}

namespace Service::NFC {

void LoopProcess(Core::System& system);

} // namespace Service::NFC


code:
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#include "common/logging/log.h"
#include "core/core.h"
#include "core/hle/kernel/k_event.h"
#include "core/hle/service/ipc_helpers.h"
#include "core/hle/service/nfc/common/device.h"
#include "core/hle/service/nfc/common/device_manager.h"
#include "core/hle/service/nfc/mifare_result.h"
#include "core/hle/service/nfc/mifare_types.h"
#include "core/hle/service/nfc/nfc_interface.h"
#include "core/hle/service/nfc/nfc_result.h"
#include "core/hle/service/nfc/nfc_types.h"
#include "core/hle/service/nfp/nfp_result.h"
#include "core/hle/service/set/system_settings_server.h"
#include "core/hle/service/sm/sm.h"
#include "hid_core/hid_types.h"

namespace Service::NFC {

NfcInterface::NfcInterface(Core::System& system_, const char* name, BackendType service_backend)
    : ServiceFramework{system_, name}, service_context{system_, service_name},
      backend_type{service_backend} {
    m_set_sys =
        system.ServiceManager().GetService<Service::Set::ISystemSettingsServer>("set:sys", true);
}

NfcInterface ::~NfcInterface() = default;

void NfcInterface::Initialize(HLERequestContext& ctx) {
    LOG_INFO(Service_NFC, "called");

    auto manager = GetManager();
    auto result = manager->Initialize();

    if (result.IsSuccess()) {
        state = State::Initialized;
    } else {
        manager->Finalize();
    }

    IPC::ResponseBuilder rb{ctx, 2, 0};
    rb.Push(result);
}

void NfcInterface::Finalize(HLERequestContext& ctx) {
    LOG_INFO(Service_NFC, "called");

    if (state != State::NonInitialized) {
        if (GetBackendType() != BackendType::None) {
            GetManager()->Finalize();
        }
        device_manager = nullptr;
        state = State::NonInitialized;
    }

    IPC::ResponseBuilder rb{ctx, 2};
    rb.Push(ResultSuccess);
}

void NfcInterface::GetState(HLERequestContext& ctx) {
    LOG_DEBUG(Service_NFC, "called");

    IPC::ResponseBuilder rb{ctx, 3};
    rb.Push(ResultSuccess);
    rb.PushEnum(state);
}

void NfcInterface::IsNfcEnabled(HLERequestContext& ctx) {
    LOG_DEBUG(Service_NFC, "called");

    bool is_enabled{};
    const auto result = m_set_sys->GetNfcEnableFlag(&is_enabled);

    IPC::ResponseBuilder rb{ctx, 3};
    rb.Push(result);
    rb.Push(is_enabled);
}

void NfcInterface::ListDevices(HLERequestContext& ctx) {
    std::vector<u64> nfp_devices;
    const std::size_t max_allowed_devices = ctx.GetWriteBufferNumElements<u64>();
    LOG_DEBUG(Service_NFC, "called");

    auto result = GetManager()->ListDevices(nfp_devices, max_allowed_devices, true);
    result = TranslateResultToServiceError(result);

    if (result.IsError()) {
        IPC::ResponseBuilder rb{ctx, 2};
        rb.Push(result);
        return;
    }

    ctx.WriteBuffer(nfp_devices);

    IPC::ResponseBuilder rb{ctx, 3};
    rb.Push(ResultSuccess);
    rb.Push(static_cast<s32>(nfp_devices.size()));
}

void NfcInterface::GetDeviceState(HLERequestContext& ctx) {
    IPC::RequestParser rp{ctx};
    const auto device_handle{rp.Pop<u64>()};
    LOG_DEBUG(Service_NFC, "called, device_handle={}", device_handle);

    const auto device_state = GetManager()->GetDeviceState(device_handle);

    if (device_state > DeviceState::Finalized) {
        ASSERT_MSG(false, "Invalid device state");
    }

    IPC::ResponseBuilder rb{ctx, 3};
    rb.Push(ResultSuccess);
    rb.PushEnum(device_state);
}

void NfcInterface::GetNpadId(HLERequestContext& ctx) {
    IPC::RequestParser rp{ctx};
    const auto device_handle{rp.Pop<u64>()};
    LOG_DEBUG(Service_NFC, "called, device_handle={}", device_handle);

    Core::HID::NpadIdType npad_id{};
    auto result = GetManager()->GetNpadId(device_handle, npad_id);
    result = TranslateResultToServiceError(result);

    if (result.IsError()) {
        IPC::ResponseBuilder rb{ctx, 2};
        rb.Push(result);
        return;
    }

    IPC::ResponseBuilder rb{ctx, 3};
    rb.Push(ResultSuccess);
    rb.PushEnum(npad_id);
}

void NfcInterface::AttachAvailabilityChangeEvent(HLERequestContext& ctx) {
    LOG_INFO(Service_NFC, "called");

    IPC::ResponseBuilder rb{ctx, 2, 1};
    rb.Push(ResultSuccess);
    rb.PushCopyObjects(GetManager()->AttachAvailabilityChangeEvent());
}

void NfcInterface::StartDetection(HLERequestContext& ctx) {
    IPC::RequestParser rp{ctx};
    const auto device_handle{rp.Pop<u64>()};
    auto tag_protocol{NfcProtocol::All};

    if (backend_type == BackendType::Nfc) {
        tag_protocol = rp.PopEnum<NfcProtocol>();
    }

    LOG_INFO(Service_NFC, "called, device_handle={}, nfp_protocol={}", device_handle, tag_protocol);
    auto result = GetManager()->StartDetection(device_handle, tag_protocol);
    result = TranslateResultToServiceError(result);

    IPC::ResponseBuilder rb{ctx, 2};
    rb.Push(result);
}

void NfcInterface::StopDetection(HLERequestContext& ctx) {
    IPC::RequestParser rp{ctx};
    const auto device_handle{rp.Pop<u64>()};
    LOG_INFO(Service_NFC, "called, device_handle={}", device_handle);

    auto result = GetManager()->StopDetection(device_handle);
    result = TranslateResultToServiceError(result);

    IPC::ResponseBuilder rb{ctx, 2};
    rb.Push(result);
}

void NfcInterface::GetTagInfo(HLERequestContext& ctx) {
    IPC::RequestParser rp{ctx};
    const auto device_handle{rp.Pop<u64>()};
    LOG_INFO(Service_NFC, "called, device_handle={}", device_handle);

    TagInfo tag_info{};
    auto result = GetManager()->GetTagInfo(device_handle, tag_info);
    result = TranslateResultToServiceError(result);

    if (result.IsSuccess()) {
        ctx.WriteBuffer(tag_info);
    }

    IPC::ResponseBuilder rb{ctx, 2};
    rb.Push(result);
}

void NfcInterface::AttachActivateEvent(HLERequestContext& ctx) {
    IPC::RequestParser rp{ctx};
    const auto device_handle{rp.Pop<u64>()};
    LOG_DEBUG(Service_NFC, "called, device_handle={}", device_handle);

    Kernel::KReadableEvent* out_event = nullptr;
    auto result = GetManager()->AttachActivateEvent(&out_event, device_handle);
    result = TranslateResultToServiceError(result);

    IPC::ResponseBuilder rb{ctx, 2, 1};
    rb.Push(result);
    rb.PushCopyObjects(out_event);
}

void NfcInterface::AttachDeactivateEvent(HLERequestContext& ctx) {
    IPC::RequestParser rp{ctx};
    const auto device_handle{rp.Pop<u64>()};
    LOG_DEBUG(Service_NFC, "called, device_handle={}", device_handle);

    Kernel::KReadableEvent* out_event = nullptr;
    auto result = GetManager()->AttachDeactivateEvent(&out_event, device_handle);
    result = TranslateResultToServiceError(result);

    IPC::ResponseBuilder rb{ctx, 2, 1};
    rb.Push(result);
    rb.PushCopyObjects(out_event);
}

void NfcInterface::SetNfcEnabled(HLERequestContext& ctx) {
    IPC::RequestParser rp{ctx};
    const auto is_enabled{rp.Pop<bool>()};
    LOG_DEBUG(Service_NFC, "called, is_enabled={}", is_enabled);

    const auto result = m_set_sys->SetNfcEnableFlag(is_enabled);

    IPC::ResponseBuilder rb{ctx, 2};
    rb.Push(result);
}

void NfcInterface::ReadMifare(HLERequestContext& ctx) {
    IPC::RequestParser rp{ctx};
    const auto device_handle{rp.Pop<u64>()};
    const auto buffer{ctx.ReadBuffer()};
    const auto number_of_commands{ctx.GetReadBufferNumElements<MifareReadBlockParameter>()};
    std::vector<MifareReadBlockParameter> read_commands(number_of_commands);

    memcpy(read_commands.data(), buffer.data(),
           number_of_commands * sizeof(MifareReadBlockParameter));

    LOG_INFO(Service_NFC, "called, device_handle={}, read_commands_size={}", device_handle,
             number_of_commands);

    std::vector<MifareReadBlockData> out_data(number_of_commands);
    auto result = GetManager()->ReadMifare(device_handle, read_commands, out_data);
    result = TranslateResultToServiceError(result);

    if (result.IsSuccess()) {
        ctx.WriteBuffer(out_data);
    }

    IPC::ResponseBuilder rb{ctx, 2};
    rb.Push(result);
}

void NfcInterface::WriteMifare(HLERequestContext& ctx) {
    IPC::RequestParser rp{ctx};
    const auto device_handle{rp.Pop<u64>()};
    const auto buffer{ctx.ReadBuffer()};
    const auto number_of_commands{ctx.GetReadBufferNumElements<MifareWriteBlockParameter>()};
    std::vector<MifareWriteBlockParameter> write_commands(number_of_commands);

    memcpy(write_commands.data(), buffer.data(),
           number_of_commands * sizeof(MifareWriteBlockParameter));

    LOG_INFO(Service_NFC, "(STUBBED) called, device_handle={}, write_commands_size={}",
             device_handle, number_of_commands);

    auto result = GetManager()->WriteMifare(device_handle, write_commands);
    result = TranslateResultToServiceError(result);

    IPC::ResponseBuilder rb{ctx, 2};
    rb.Push(result);
}

void NfcInterface::SendCommandByPassThrough(HLERequestContext& ctx) {
    IPC::RequestParser rp{ctx};
    const auto device_handle{rp.Pop<u64>()};
    const auto timeout{rp.PopRaw<s64>()};
    const auto command_data{ctx.ReadBuffer()};
    LOG_INFO(Service_NFC, "(STUBBED) called, device_handle={}, timeout={}, data_size={}",
             device_handle, timeout, command_data.size());

    std::vector<u8> out_data(1);
    auto result =
        GetManager()->SendCommandByPassThrough(device_handle, timeout, command_data, out_data);
    result = TranslateResultToServiceError(result);

    if (result.IsError()) {
        IPC::ResponseBuilder rb{ctx, 2};
        rb.Push(result);
        return;
    }

    ctx.WriteBuffer(out_data);

    IPC::ResponseBuilder rb{ctx, 3};
    rb.Push(ResultSuccess);
    rb.Push(static_cast<u32>(out_data.size()));
}

std::shared_ptr<DeviceManager> NfcInterface::GetManager() {
    if (device_manager == nullptr) {
        device_manager = std::make_shared<DeviceManager>(system, service_context);
    }
    return device_manager;
}

BackendType NfcInterface::GetBackendType() const {
    return backend_type;
}

Result NfcInterface::TranslateResultToServiceError(Result result) const {
    const auto backend = GetBackendType();

    if (result.IsSuccess()) {
        return result;
    }

    if (result.GetModule() != ErrorModule::NFC) {
        return result;
    }

    switch (backend) {
    case BackendType::Mifare:
        return TranslateResultToNfp(result);
    case BackendType::Nfp: {
        return TranslateResultToNfp(result);
    }
    default:
        if (result != ResultBackupPathAlreadyExist) {
            return result;
        }
        return ResultUnknown74;
    }
}

Result NfcInterface::TranslateResultToNfp(Result result) const {
    if (result == ResultDeviceNotFound) {
        return NFP::ResultDeviceNotFound;
    }
    if (result == ResultInvalidArgument) {
        return NFP::ResultInvalidArgument;
    }
    if (result == ResultWrongApplicationAreaSize) {
        return NFP::ResultWrongApplicationAreaSize;
    }
    if (result == ResultWrongDeviceState) {
        return NFP::ResultWrongDeviceState;
    }
    if (result == ResultUnknown74) {
        return NFP::ResultUnknown74;
    }
    if (result == ResultNfcDisabled) {
        return NFP::ResultNfcDisabled;
    }
    if (result == ResultNfcNotInitialized) {
        return NFP::ResultNfcDisabled;
    }
    if (result == ResultWriteAmiiboFailed) {
        return NFP::ResultWriteAmiiboFailed;
    }
    if (result == ResultTagRemoved) {
        return NFP::ResultTagRemoved;
    }
    if (result == ResultRegistrationIsNotInitialized) {
        return NFP::ResultRegistrationIsNotInitialized;
    }
    if (result == ResultApplicationAreaIsNotInitialized) {
        return NFP::ResultApplicationAreaIsNotInitialized;
    }
    if (result == ResultCorruptedDataWithBackup) {
        return NFP::ResultCorruptedDataWithBackup;
    }
    if (result == ResultCorruptedData) {
        return NFP::ResultCorruptedData;
    }
    if (result == ResultWrongApplicationAreaId) {
        return NFP::ResultWrongApplicationAreaId;
    }
    if (result == ResultApplicationAreaExist) {
        return NFP::ResultApplicationAreaExist;
    }
    if (result == ResultInvalidTagType) {
        return NFP::ResultNotAnAmiibo;
    }
    if (result == ResultUnableToAccessBackupFile) {
        return NFP::ResultUnableToAccessBackupFile;
    }
    LOG_WARNING(Service_NFC, "Result conversion not handled");
    return result;
}

Result NfcInterface::TranslateResultToMifare(Result result) const {
    if (result == ResultDeviceNotFound) {
        return Mifare::ResultDeviceNotFound;
    }
    if (result == ResultInvalidArgument) {
        return Mifare::ResultInvalidArgument;
    }
    if (result == ResultWrongDeviceState) {
        return Mifare::ResultWrongDeviceState;
    }
    if (result == ResultNfcDisabled) {
        return Mifare::ResultNfcDisabled;
    }
    if (result == ResultTagRemoved) {
        return Mifare::ResultTagRemoved;
    }
    if (result == ResultInvalidTagType) {
        return Mifare::ResultNotAMifare;
    }
    LOG_WARNING(Service_NFC, "Result conversion not handled");
    return result;
}

} // namespace Service::NFC


code:
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#pragma once

#include "core/hle/service/kernel_helpers.h"
#include "core/hle/service/nfc/nfc_types.h"
#include "core/hle/service/service.h"

namespace Service::Set {
class ISystemSettingsServer;
}

namespace Service::NFC {
class DeviceManager;

class NfcInterface : public ServiceFramework<NfcInterface> {
public:
    explicit NfcInterface(Core::System& system_, const char* name, BackendType service_backend);
    ~NfcInterface();

    void Initialize(HLERequestContext& ctx);
    void Finalize(HLERequestContext& ctx);
    void GetState(HLERequestContext& ctx);
    void IsNfcEnabled(HLERequestContext& ctx);
    void ListDevices(HLERequestContext& ctx);
    void GetDeviceState(HLERequestContext& ctx);
    void GetNpadId(HLERequestContext& ctx);
    void AttachAvailabilityChangeEvent(HLERequestContext& ctx);
    void StartDetection(HLERequestContext& ctx);
    void StopDetection(HLERequestContext& ctx);
    void GetTagInfo(HLERequestContext& ctx);
    void AttachActivateEvent(HLERequestContext& ctx);
    void AttachDeactivateEvent(HLERequestContext& ctx);
    void ReadMifare(HLERequestContext& ctx);
    void SetNfcEnabled(HLERequestContext& ctx);
    void WriteMifare(HLERequestContext& ctx);
    void SendCommandByPassThrough(HLERequestContext& ctx);

protected:
    std::shared_ptr<DeviceManager> GetManager();
    BackendType GetBackendType() const;
    Result TranslateResultToServiceError(Result result) const;
    Result TranslateResultToNfp(Result result) const;
    Result TranslateResultToMifare(Result result) const;

    KernelHelpers::ServiceContext service_context;

    BackendType backend_type;
    State state{State::NonInitialized};
    std::shared_ptr<DeviceManager> device_manager = nullptr;
    std::shared_ptr<Service::Set::ISystemSettingsServer> m_set_sys;
};

} // namespace Service::NFC


code:
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later

#pragma once

#include "core/hle/result.h"

namespace Service::NFC {

constexpr Result ResultDeviceNotFound(ErrorModule::NFC, 64);
constexpr Result ResultInvalidArgument(ErrorModule::NFC, 65);
constexpr Result ResultWrongApplicationAreaSize(ErrorModule::NFC, 68);
constexpr Result ResultWrongDeviceState(ErrorModule::NFC, 73);
constexpr Result ResultUnknown74(ErrorModule::NFC, 74);
constexpr Result ResultUnknown76(ErrorModule::NFC, 76);
constexpr Result ResultNfcNotInitialized(ErrorModule::NFC, 77);
constexpr Result ResultNfcDisabled(ErrorModule::NFC, 80);
constexpr Result ResultWriteAmiiboFailed(ErrorModule::NFC, 88);
constexpr Result ResultTagRemoved(ErrorModule::NFC, 97);
constexpr Result ResultUnknown112(ErrorModule::NFC, 112);
constexpr Result ResultUnableToAccessBackupFile(ErrorModule::NFC, 113);
constexpr Result ResultUnknown114(ErrorModule::NFC, 114);
constexpr Result ResultUnknown115(ErrorModule::NFC, 115);
constexpr Result ResultRegistrationIsNotInitialized(ErrorModule::NFC, 120);
constexpr Result ResultApplicationAreaIsNotInitialized(ErrorModule::NFC, 128);
constexpr Result ResultCorruptedDataWithBackup(ErrorModule::NFC, 136);
constexpr Result ResultCorruptedData(ErrorModule::NFC, 144);
constexpr Result ResultWrongApplicationAreaId(ErrorModule::NFC, 152);
constexpr Result ResultApplicationAreaExist(ErrorModule::NFC, 168);
constexpr Result ResultInvalidTagType(ErrorModule::NFC, 178);
constexpr Result ResultBackupPathAlreadyExist(ErrorModule::NFC, 216);
constexpr Result ResultMifareError288(ErrorModule::NFC, 288);

} // namespace Service::NFC


code:
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later

#pragma once

#include <array>

#include "common/common_funcs.h"
#include "common/common_types.h"

namespace Service::NFC {
enum class BackendType : u32 {
    None,
    Nfc,
    Nfp,
    Mifare,
};

// This is nn::nfc::DeviceState
enum class DeviceState : u32 {
    Initialized,
    SearchingForTag,
    TagFound,
    TagRemoved,
    TagMounted,
    Unavailable,
    Finalized,
};

// This is nn::nfc::State
enum class State : u32 {
    NonInitialized,
    Initialized,
};

// This is nn::nfc::TagType
enum class TagType : u32 {
    None = 0,
    Type1 = 1U << 0,  // ISO14443A RW. Topaz
    Type2 = 1U << 1,  // ISO14443A RW. Ultralight, NTAGX, ST25TN
    Type3 = 1U << 2,  // ISO14443A RW/RO. Sony FeliCa
    Type4A = 1U << 3, // ISO14443A RW/RO. DESFire
    Type4B = 1U << 4, // ISO14443B RW/RO. DESFire
    Type5 = 1U << 5,  // ISO15693 RW/RO. SLI, SLIX, ST25TV
    Mifare = 1U << 6, // Mifare classic. Skylanders
    All = 0xFFFFFFFF,
};

enum class PackedTagType : u8 {
    None = 0,
    Type1 = 1U << 0,  // ISO14443A RW. Topaz
    Type2 = 1U << 1,  // ISO14443A RW. Ultralight, NTAGX, ST25TN
    Type3 = 1U << 2,  // ISO14443A RW/RO. Sony FeliCa
    Type4A = 1U << 3, // ISO14443A RW/RO. DESFire
    Type4B = 1U << 4, // ISO14443B RW/RO. DESFire
    Type5 = 1U << 5,  // ISO15693 RW/RO. SLI, SLIX, ST25TV
    Mifare = 1U << 6, // Mifare classic. Skylanders
    All = 0xFF,
};

// This is nn::nfc::NfcProtocol
enum class NfcProtocol : u32 {
    None,
    TypeA = 1U << 0, // ISO14443A
    TypeB = 1U << 1, // ISO14443B
    TypeF = 1U << 2, // Sony FeliCa
    All = 0xFFFFFFFFU,
};

// this is nn::nfc::TestWaveType
enum class TestWaveType : u32 {
    Unknown,
};

using UniqueSerialNumber = std::array<u8, 10>;

// This is nn::nfc::DeviceHandle
using DeviceHandle = u64;

// This is nn::nfc::TagInfo
struct TagInfo {
    UniqueSerialNumber uuid;
    u8 uuid_length;
    INSERT_PADDING_BYTES(0x15);
    NfcProtocol protocol;
    TagType tag_type;
    INSERT_PADDING_BYTES(0x30);
};
static_assert(sizeof(TagInfo) == 0x58, "TagInfo is an invalid size");

} // namespace Service::NFC


code:
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#include "common/logging/log.h"
#include "core/hle/service/ipc_helpers.h"
#include "core/hle/service/nfp/nfp.h"
#include "core/hle/service/nfp/nfp_interface.h"
#include "core/hle/service/server_manager.h"

namespace Service::NFP {

class IUser final : public Interface {
public:
    explicit IUser(Core::System& system_) : Interface(system_, "NFP:IUser") {
        // clang-format off
        static const FunctionInfoTyped<IUser> 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, &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"},
        };
        // clang-format on

        RegisterHandlers(functions);
    }
};

class ISystem final : public Interface {
public:
    explicit ISystem(Core::System& system_) : Interface(system_, "NFP:ISystem") {
        // clang-format off
        static const FunctionInfoTyped<ISystem> functions[] = {
            {0, &ISystem::InitializeSystem, "InitializeSystem"},
            {1, &ISystem::FinalizeSystem, "FinalizeSystem"},
            {2, &ISystem::ListDevices, "ListDevices"},
            {3, &ISystem::StartDetection, "StartDetection"},
            {4, &ISystem::StopDetection, "StopDetection"},
            {5, &ISystem::Mount, "Mount"},
            {6, &ISystem::Unmount, "Unmount"},
            {10, &ISystem::Flush, "Flush"},
            {11, &ISystem::Restore, "Restore"},
            {12, &ISystem::CreateApplicationArea, "CreateApplicationArea"},
            {13, &ISystem::GetTagInfo, "GetTagInfo"},
            {14, &ISystem::GetRegisterInfo, "GetRegisterInfo"},
            {15, &ISystem::GetCommonInfo, "GetCommonInfo"},
            {16, &ISystem::GetModelInfo, "GetModelInfo"},
            {17, &ISystem::AttachActivateEvent, "AttachActivateEvent"},
            {18, &ISystem::AttachDeactivateEvent, "AttachDeactivateEvent"},
            {19, &ISystem::GetState, "GetState"},
            {20, &ISystem::GetDeviceState, "GetDeviceState"},
            {21, &ISystem::GetNpadId, "GetNpadId"},
            {23, &ISystem::AttachAvailabilityChangeEvent, "AttachAvailabilityChangeEvent"},
            {100, &ISystem::Format, "Format"},
            {101, &ISystem::GetAdminInfo, "GetAdminInfo"},
            {102, &ISystem::GetRegisterInfoPrivate, "GetRegisterInfoPrivate"},
            {103, &ISystem::SetRegisterInfoPrivate, "SetRegisterInfoPrivate"},
            {104, &ISystem::DeleteRegisterInfo, "DeleteRegisterInfo"},
            {105, &ISystem::DeleteApplicationArea, "DeleteApplicationArea"},
            {106, &ISystem::ExistsApplicationArea, "ExistsApplicationArea"},
        };
        // clang-format on

        RegisterHandlers(functions);
    }
};

class IDebug final : public Interface {
public:
    explicit IDebug(Core::System& system_) : Interface(system_, "NFP:IDebug") {
        // clang-format off
        static const FunctionInfoTyped<IDebug> functions[] = {
            {0, &IDebug::InitializeDebug, "InitializeDebug"},
            {1, &IDebug::FinalizeDebug, "FinalizeDebug"},
            {2, &IDebug::ListDevices, "ListDevices"},
            {3, &IDebug::StartDetection, "StartDetection"},
            {4, &IDebug::StopDetection, "StopDetection"},
            {5, &IDebug::Mount, "Mount"},
            {6, &IDebug::Unmount, "Unmount"},
            {7, &IDebug::OpenApplicationArea, "OpenApplicationArea"},
            {8, &IDebug::GetApplicationArea, "GetApplicationArea"},
            {9, &IDebug::SetApplicationArea, "SetApplicationArea"},
            {10, &IDebug::Flush, "Flush"},
            {11, &IDebug::Restore, "Restore"},
            {12, &IDebug::CreateApplicationArea, "CreateApplicationArea"},
            {13, &IDebug::GetTagInfo, "GetTagInfo"},
            {14, &IDebug::GetRegisterInfo, "GetRegisterInfo"},
            {15, &IDebug::GetCommonInfo, "GetCommonInfo"},
            {16, &IDebug::GetModelInfo, "GetModelInfo"},
            {17, &IDebug::AttachActivateEvent, "AttachActivateEvent"},
            {18, &IDebug::AttachDeactivateEvent, "AttachDeactivateEvent"},
            {19, &IDebug::GetState, "GetState"},
            {20, &IDebug::GetDeviceState, "GetDeviceState"},
            {21, &IDebug::GetNpadId, "GetNpadId"},
            {22, &IDebug::GetApplicationAreaSize, "GetApplicationAreaSize"},
            {23, &IDebug::AttachAvailabilityChangeEvent, "AttachAvailabilityChangeEvent"},
            {24, &IDebug::RecreateApplicationArea, "RecreateApplicationArea"},
            {100, &IDebug::Format, "Format"},
            {101, &IDebug::GetAdminInfo, "GetAdminInfo"},
            {102, &IDebug::GetRegisterInfoPrivate, "GetRegisterInfoPrivate"},
            {103, &IDebug::SetRegisterInfoPrivate, "SetRegisterInfoPrivate"},
            {104, &IDebug::DeleteRegisterInfo, "DeleteRegisterInfo"},
            {105, &IDebug::DeleteApplicationArea, "DeleteApplicationArea"},
            {106, &IDebug::ExistsApplicationArea, "ExistsApplicationArea"},
            {200, &IDebug::GetAll, "GetAll"},
            {201, &IDebug::SetAll, "SetAll"},
            {202, &IDebug::FlushDebug, "FlushDebug"},
            {203, &IDebug::BreakTag, "BreakTag"},
            {204, &IDebug::ReadBackupData, "ReadBackupData"},
            {205, &IDebug::WriteBackupData, "WriteBackupData"},
            {206, &IDebug::WriteNtf, "WriteNtf"},
        };
        // clang-format on

        RegisterHandlers(functions);
    }
};

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

        RegisterHandlers(functions);
    }

private:
    void CreateUserInterface(HLERequestContext& ctx) {
        LOG_DEBUG(Service_NFP, "called");

        IPC::ResponseBuilder rb{ctx, 2, 0, 1};
        rb.Push(ResultSuccess);
        rb.PushIpcInterface<IUser>(system);
    }
};

class ISystemManager final : public ServiceFramework<ISystemManager> {
public:
    explicit ISystemManager(Core::System& system_) : ServiceFramework{system_, "nfp:sys"} {
        // clang-format off
        static const FunctionInfo functions[] = {
            {0, &ISystemManager::CreateSystemInterface, "CreateSystemInterface"},
        };
        // clang-format on

        RegisterHandlers(functions);
    }

private:
    void CreateSystemInterface(HLERequestContext& ctx) {
        LOG_DEBUG(Service_NFP, "called");

        IPC::ResponseBuilder rb{ctx, 2, 0, 1};
        rb.Push(ResultSuccess);
        rb.PushIpcInterface<ISystem>(system);
    }
};

class IDebugManager final : public ServiceFramework<IDebugManager> {
public:
    explicit IDebugManager(Core::System& system_) : ServiceFramework{system_, "nfp:dbg"} {
        // clang-format off
        static const FunctionInfo functions[] = {
            {0, &IDebugManager::CreateDebugInterface, "CreateDebugInterface"},
        };
        // clang-format on

        RegisterHandlers(functions);
    }

private:
    void CreateDebugInterface(HLERequestContext& ctx) {
        LOG_DEBUG(Service_NFP, "called");

        IPC::ResponseBuilder rb{ctx, 2, 0, 1};
        rb.Push(ResultSuccess);
        rb.PushIpcInterface<IDebug>(system);
    }
};

void LoopProcess(Core::System& system) {
    auto server_manager = std::make_unique<ServerManager>(system);

    server_manager->RegisterNamedService("nfp:user", std::make_shared<IUserManager>(system));
    server_manager->RegisterNamedService("nfp:sys", std::make_shared<ISystemManager>(system));
    server_manager->RegisterNamedService("nfp:dbg", std::make_shared<IDebugManager>(system));
    ServerManager::RunServer(std::move(server_manager));
}

} // namespace Service::NFP


code:
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#pragma once

#include "core/hle/service/service.h"

namespace Service::NFP {

void LoopProcess(Core::System& system);

} // namespace Service::NFP


code:
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#include "common/logging/log.h"
#include "core/core.h"
#include "core/hle/kernel/k_event.h"
#include "core/hle/service/ipc_helpers.h"
#include "core/hle/service/nfc/common/device.h"
#include "core/hle/service/nfc/common/device_manager.h"
#include "core/hle/service/nfc/nfc_types.h"
#include "core/hle/service/nfp/nfp_interface.h"
#include "core/hle/service/nfp/nfp_result.h"
#include "core/hle/service/nfp/nfp_types.h"
#include "hid_core/hid_types.h"

namespace Service::NFP {

Interface::Interface(Core::System& system_, const char* name)
    : NfcInterface{system_, name, NFC::BackendType::Nfp} {}

Interface::~Interface() = default;

void Interface::InitializeSystem(HLERequestContext& ctx) {
    Initialize(ctx);
}

void Interface::InitializeDebug(HLERequestContext& ctx) {
    Initialize(ctx);
}

void Interface::FinalizeSystem(HLERequestContext& ctx) {
    Finalize(ctx);
}

void Interface::FinalizeDebug(HLERequestContext& ctx) {
    Finalize(ctx);
}

void Interface::Mount(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);

    auto result = GetManager()->Mount(device_handle, model_type, mount_target);
    result = TranslateResultToServiceError(result);

    IPC::ResponseBuilder rb{ctx, 2};
    rb.Push(result);
}

void Interface::Unmount(HLERequestContext& ctx) {
    IPC::RequestParser rp{ctx};
    const auto device_handle{rp.Pop<u64>()};
    LOG_INFO(Service_NFP, "called, device_handle={}", device_handle);

    auto result = GetManager()->Unmount(device_handle);
    result = TranslateResultToServiceError(result);

    IPC::ResponseBuilder rb{ctx, 2};
    rb.Push(result);
}

void Interface::OpenApplicationArea(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);

    auto result = GetManager()->OpenApplicationArea(device_handle, access_id);
    result = TranslateResultToServiceError(result);

    IPC::ResponseBuilder rb{ctx, 2};
    rb.Push(result);
}

void Interface::GetApplicationArea(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);

    std::vector<u8> data(data_size);
    auto result = GetManager()->GetApplicationArea(device_handle, data);
    result = TranslateResultToServiceError(result);

    if (result.IsError()) {
        IPC::ResponseBuilder rb{ctx, 2};
        rb.Push(result);
        return;
    }

    ctx.WriteBuffer(data);
    IPC::ResponseBuilder rb{ctx, 3};
    rb.Push(result);
    rb.Push(static_cast<u32>(data_size));
}

void Interface::SetApplicationArea(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());

    auto result = GetManager()->SetApplicationArea(device_handle, data);
    result = TranslateResultToServiceError(result);

    IPC::ResponseBuilder rb{ctx, 2};
    rb.Push(result);
}

void Interface::Flush(HLERequestContext& ctx) {
    IPC::RequestParser rp{ctx};
    const auto device_handle{rp.Pop<u64>()};
    LOG_INFO(Service_NFP, "called, device_handle={}", device_handle);

    auto result = GetManager()->Flush(device_handle);
    result = TranslateResultToServiceError(result);

    IPC::ResponseBuilder rb{ctx, 2};
    rb.Push(result);
}

void Interface::Restore(HLERequestContext& ctx) {
    IPC::RequestParser rp{ctx};
    const auto device_handle{rp.Pop<u64>()};
    LOG_INFO(Service_NFP, "called, device_handle={}", device_handle);

    auto result = GetManager()->Restore(device_handle);
    result = TranslateResultToServiceError(result);

    IPC::ResponseBuilder rb{ctx, 2};
    rb.Push(result);
}

void Interface::CreateApplicationArea(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());

    auto result = GetManager()->CreateApplicationArea(device_handle, access_id, data);
    result = TranslateResultToServiceError(result);

    IPC::ResponseBuilder rb{ctx, 2};
    rb.Push(result);
}

void Interface::GetRegisterInfo(HLERequestContext& ctx) {
    IPC::RequestParser rp{ctx};
    const auto device_handle{rp.Pop<u64>()};
    LOG_INFO(Service_NFP, "called, device_handle={}", device_handle);

    RegisterInfo register_info{};
    auto result = GetManager()->GetRegisterInfo(device_handle, register_info);
    result = TranslateResultToServiceError(result);

    if (result.IsSuccess()) {
        ctx.WriteBuffer(register_info);
    }

    IPC::ResponseBuilder rb{ctx, 2};
    rb.Push(result);
}

void Interface::GetCommonInfo(HLERequestContext& ctx) {
    IPC::RequestParser rp{ctx};
    const auto device_handle{rp.Pop<u64>()};
    LOG_INFO(Service_NFP, "called, device_handle={}", device_handle);

    CommonInfo common_info{};
    auto result = GetManager()->GetCommonInfo(device_handle, common_info);
    result = TranslateResultToServiceError(result);

    if (result.IsSuccess()) {
        ctx.WriteBuffer(common_info);
    }

    IPC::ResponseBuilder rb{ctx, 2};
    rb.Push(result);
}

void Interface::GetModelInfo(HLERequestContext& ctx) {
    IPC::RequestParser rp{ctx};
    const auto device_handle{rp.Pop<u64>()};
    LOG_INFO(Service_NFP, "called, device_handle={}", device_handle);

    ModelInfo model_info{};
    auto result = GetManager()->GetModelInfo(device_handle, model_info);
    result = TranslateResultToServiceError(result);

    if (result.IsSuccess()) {
        ctx.WriteBuffer(model_info);
    }

    IPC::ResponseBuilder rb{ctx, 2};
    rb.Push(result);
}

void Interface::GetApplicationAreaSize(HLERequestContext& ctx) {
    IPC::RequestParser rp{ctx};
    const auto device_handle{rp.Pop<u64>()};
    LOG_DEBUG(Service_NFP, "called, device_handle={}", device_handle);

    IPC::ResponseBuilder rb{ctx, 3};
    rb.Push(ResultSuccess);
    rb.Push(GetManager()->GetApplicationAreaSize());
}

void Interface::RecreateApplicationArea(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());

    auto result = GetManager()->RecreateApplicationArea(device_handle, access_id, data);
    result = TranslateResultToServiceError(result);

    IPC::ResponseBuilder rb{ctx, 2};
    rb.Push(result);
}

void Interface::Format(HLERequestContext& ctx) {
    IPC::RequestParser rp{ctx};
    const auto device_handle{rp.Pop<u64>()};
    LOG_INFO(Service_NFP, "called, device_handle={}", device_handle);

    auto result = GetManager()->Format(device_handle);
    result = TranslateResultToServiceError(result);

    IPC::ResponseBuilder rb{ctx, 2};
    rb.Push(result);
}

void Interface::GetAdminInfo(HLERequestContext& ctx) {
    IPC::RequestParser rp{ctx};
    const auto device_handle{rp.Pop<u64>()};
    LOG_INFO(Service_NFP, "called, device_handle={}", device_handle);

    AdminInfo admin_info{};
    auto result = GetManager()->GetAdminInfo(device_handle, admin_info);
    result = TranslateResultToServiceError(result);

    if (result.IsSuccess()) {
        ctx.WriteBuffer(admin_info);
    }

    IPC::ResponseBuilder rb{ctx, 2};
    rb.Push(result);
}

void Interface::GetRegisterInfoPrivate(HLERequestContext& ctx) {
    IPC::RequestParser rp{ctx};
    const auto device_handle{rp.Pop<u64>()};
    LOG_INFO(Service_NFP, "called, device_handle={}", device_handle);

    RegisterInfoPrivate register_info{};
    auto result = GetManager()->GetRegisterInfoPrivate(device_handle, register_info);
    result = TranslateResultToServiceError(result);

    if (result.IsSuccess()) {
        ctx.WriteBuffer(register_info);
    }

    IPC::ResponseBuilder rb{ctx, 2};
    rb.Push(result);
}

void Interface::SetRegisterInfoPrivate(HLERequestContext& ctx) {
    IPC::RequestParser rp{ctx};
    const auto device_handle{rp.Pop<u64>()};
    const auto register_info_buffer{ctx.ReadBuffer()};
    LOG_INFO(Service_NFP, "called, device_handle={}, buffer_size={}", device_handle,
             register_info_buffer.size());

    RegisterInfoPrivate register_info{};
    memcpy(&register_info, register_info_buffer.data(), sizeof(RegisterInfoPrivate));
    auto result = GetManager()->SetRegisterInfoPrivate(device_handle, register_info);
    result = TranslateResultToServiceError(result);

    IPC::ResponseBuilder rb{ctx, 2};
    rb.Push(result);
}

void Interface::DeleteRegisterInfo(HLERequestContext& ctx) {
    IPC::RequestParser rp{ctx};
    const auto device_handle{rp.Pop<u64>()};
    LOG_INFO(Service_NFP, "called, device_handle={}", device_handle);

    auto result = GetManager()->DeleteRegisterInfo(device_handle);
    result = TranslateResultToServiceError(result);

    IPC::ResponseBuilder rb{ctx, 2};
    rb.Push(result);
}

void Interface::DeleteApplicationArea(HLERequestContext& ctx) {
    IPC::RequestParser rp{ctx};
    const auto device_handle{rp.Pop<u64>()};
    LOG_INFO(Service_NFP, "called, device_handle={}", device_handle);

    auto result = GetManager()->DeleteApplicationArea(device_handle);
    result = TranslateResultToServiceError(result);

    IPC::ResponseBuilder rb{ctx, 2};
    rb.Push(result);
}

void Interface::ExistsApplicationArea(HLERequestContext& ctx) {
    IPC::RequestParser rp{ctx};
    const auto device_handle{rp.Pop<u64>()};
    LOG_INFO(Service_NFP, "called, device_handle={}", device_handle);

    bool has_application_area = false;
    auto result = GetManager()->ExistsApplicationArea(device_handle, has_application_area);
    result = TranslateResultToServiceError(result);

    if (result.IsError()) {
        IPC::ResponseBuilder rb{ctx, 2};
        rb.Push(result);
        return;
    }

    IPC::ResponseBuilder rb{ctx, 3};
    rb.Push(result);
    rb.Push(has_application_area);
}

void Interface::GetAll(HLERequestContext& ctx) {
    IPC::RequestParser rp{ctx};
    const auto device_handle{rp.Pop<u64>()};
    LOG_INFO(Service_NFP, "called, device_handle={}", device_handle);

    NfpData nfp_data{};
    auto result = GetManager()->GetAll(device_handle, nfp_data);
    result = TranslateResultToServiceError(result);

    if (result.IsSuccess()) {
        ctx.WriteBuffer(nfp_data);
    }

    IPC::ResponseBuilder rb{ctx, 2};
    rb.Push(result);
}

void Interface::SetAll(HLERequestContext& ctx) {
    IPC::RequestParser rp{ctx};
    const auto device_handle{rp.Pop<u64>()};
    const auto nfp_data_buffer{ctx.ReadBuffer()};

    LOG_INFO(Service_NFP, "called, device_handle={}", device_handle);

    NfpData nfp_data{};
    memcpy(&nfp_data, nfp_data_buffer.data(), sizeof(NfpData));
    auto result = GetManager()->SetAll(device_handle, nfp_data);
    result = TranslateResultToServiceError(result);

    IPC::ResponseBuilder rb{ctx, 2};
    rb.Push(result);
}

void Interface::FlushDebug(HLERequestContext& ctx) {
    IPC::RequestParser rp{ctx};
    const auto device_handle{rp.Pop<u64>()};
    LOG_INFO(Service_NFP, "called, device_handle={}", device_handle);

    auto result = GetManager()->FlushDebug(device_handle);
    result = TranslateResultToServiceError(result);

    IPC::ResponseBuilder rb{ctx, 2};
    rb.Push(result);
}

void Interface::BreakTag(HLERequestContext& ctx) {
    IPC::RequestParser rp{ctx};
    const auto device_handle{rp.Pop<u64>()};
    const auto break_type{rp.PopEnum<BreakType>()};
    LOG_WARNING(Service_NFP, "(STUBBED) called, device_handle={}, break_type={}", device_handle,
                break_type);

    auto result = GetManager()->BreakTag(device_handle, break_type);
    result = TranslateResultToServiceError(result);

    IPC::ResponseBuilder rb{ctx, 2};
    rb.Push(result);
}

void Interface::ReadBackupData(HLERequestContext& ctx) {
    IPC::RequestParser rp{ctx};
    const auto device_handle{rp.Pop<u64>()};
    LOG_INFO(Service_NFP, "called, device_handle={}", device_handle);

    std::vector<u8> backup_data{};
    auto result = GetManager()->ReadBackupData(device_handle, backup_data);
    result = TranslateResultToServiceError(result);

    if (result.IsSuccess()) {
        ctx.WriteBuffer(backup_data);
    }

    IPC::ResponseBuilder rb{ctx, 2};
    rb.Push(result);
}

void Interface::WriteBackupData(HLERequestContext& ctx) {
    IPC::RequestParser rp{ctx};
    const auto device_handle{rp.Pop<u64>()};
    const auto backup_data_buffer{ctx.ReadBuffer()};
    LOG_INFO(Service_NFP, "called, device_handle={}", device_handle);

    auto result = GetManager()->WriteBackupData(device_handle, backup_data_buffer);
    result = TranslateResultToServiceError(result);

    IPC::ResponseBuilder rb{ctx, 2};
    rb.Push(result);
}

void Interface::WriteNtf(HLERequestContext& ctx) {
    IPC::RequestParser rp{ctx};
    const auto device_handle{rp.Pop<u64>()};
    const auto write_type{rp.PopEnum<WriteType>()};
    const auto ntf_data_buffer{ctx.ReadBuffer()};
    LOG_WARNING(Service_NFP, "(STUBBED) called, device_handle={}", device_handle);

    auto result = GetManager()->WriteNtf(device_handle, write_type, ntf_data_buffer);
    result = TranslateResultToServiceError(result);

    IPC::ResponseBuilder rb{ctx, 2};
    rb.Push(result);
}

} // namespace Service::NFP


code:
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#pragma once

#include "core/hle/service/kernel_helpers.h"
#include "core/hle/service/nfc/nfc_interface.h"
#include "core/hle/service/service.h"

namespace Service::NFP {

class Interface : public NFC::NfcInterface {
public:
    explicit Interface(Core::System& system_, const char* name);
    ~Interface() override;

    void InitializeSystem(HLERequestContext& ctx);
    void InitializeDebug(HLERequestContext& ctx);
    void FinalizeSystem(HLERequestContext& ctx);
    void FinalizeDebug(HLERequestContext& ctx);
    void Mount(HLERequestContext& ctx);
    void Unmount(HLERequestContext& ctx);
    void OpenApplicationArea(HLERequestContext& ctx);
    void GetApplicationArea(HLERequestContext& ctx);
    void SetApplicationArea(HLERequestContext& ctx);
    void Flush(HLERequestContext& ctx);
    void Restore(HLERequestContext& ctx);
    void CreateApplicationArea(HLERequestContext& ctx);
    void GetRegisterInfo(HLERequestContext& ctx);
    void GetCommonInfo(HLERequestContext& ctx);
    void GetModelInfo(HLERequestContext& ctx);
    void GetApplicationAreaSize(HLERequestContext& ctx);
    void RecreateApplicationArea(HLERequestContext& ctx);
    void Format(HLERequestContext& ctx);
    void GetAdminInfo(HLERequestContext& ctx);
    void GetRegisterInfoPrivate(HLERequestContext& ctx);
    void SetRegisterInfoPrivate(HLERequestContext& ctx);
    void DeleteRegisterInfo(HLERequestContext& ctx);
    void DeleteApplicationArea(HLERequestContext& ctx);
    void ExistsApplicationArea(HLERequestContext& ctx);
    void GetAll(HLERequestContext& ctx);
    void SetAll(HLERequestContext& ctx);
    void FlushDebug(HLERequestContext& ctx);
    void BreakTag(HLERequestContext& ctx);
    void ReadBackupData(HLERequestContext& ctx);
    void WriteBackupData(HLERequestContext& ctx);
    void WriteNtf(HLERequestContext& ctx);
};

} // namespace Service::NFP


code:
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#pragma once

#include "core/hle/result.h"

namespace Service::NFP {

constexpr Result ResultDeviceNotFound(ErrorModule::NFP, 64);
constexpr Result ResultInvalidArgument(ErrorModule::NFP, 65);
constexpr Result ResultWrongApplicationAreaSize(ErrorModule::NFP, 68);
constexpr Result ResultWrongDeviceState(ErrorModule::NFP, 73);
constexpr Result ResultUnknown74(ErrorModule::NFC, 74);
constexpr Result ResultNfcDisabled(ErrorModule::NFP, 80);
constexpr Result ResultWriteAmiiboFailed(ErrorModule::NFP, 88);
constexpr Result ResultTagRemoved(ErrorModule::NFP, 97);
constexpr Result ResultRegistrationIsNotInitialized(ErrorModule::NFP, 120);
constexpr Result ResultApplicationAreaIsNotInitialized(ErrorModule::NFP, 128);
constexpr Result ResultCorruptedDataWithBackup(ErrorModule::NFP, 136);
constexpr Result ResultCorruptedData(ErrorModule::NFP, 144);
constexpr Result ResultWrongApplicationAreaId(ErrorModule::NFP, 152);
constexpr Result ResultApplicationAreaExist(ErrorModule::NFP, 168);
constexpr Result ResultNotAnAmiibo(ErrorModule::NFP, 178);
constexpr Result ResultUnableToAccessBackupFile(ErrorModule::NFP, 200);

} // namespace Service::NFP


code:
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later

#pragma once

#include <array>

#include "common/swap.h"
#include "core/hle/service/mii/types/char_info.h"
#include "core/hle/service/mii/types/store_data.h"
#include "core/hle/service/mii/types/ver3_store_data.h"
#include "core/hle/service/nfc/nfc_types.h"

namespace Service::NFP {
static constexpr std::size_t amiibo_name_length = 0xA;
static constexpr std::size_t application_id_version_offset = 0x1c;
static constexpr std::size_t counter_limit = 0xffff;

// This is nn::nfp::ModelType
enum class ModelType : u32 {
    Amiibo,
};

// This is nn::nfp::MountTarget
enum class MountTarget : u32 {
    None,
    Rom,
    Ram,
    All,
};

enum class AmiiboType : u8 {
    Figure,
    Card,
    Yarn,
};

enum class AmiiboSeries : u8 {
    SuperSmashBros,
    SuperMario,
    ChibiRobo,
    YoshiWoollyWorld,
    Splatoon,
    AnimalCrossing,
    EightBitMario,
    Skylanders,
    Unknown8,
    TheLegendOfZelda,
    ShovelKnight,
    Unknown11,
    Kiby,
    Pokemon,
    MarioSportsSuperstars,
    MonsterHunter,
    BoxBoy,
    Pikmin,
    FireEmblem,
    Metroid,
    Others,
    MegaMan,
    Diablo,
};

enum class AppAreaVersion : u8 {
    Nintendo3DS = 0,
    NintendoWiiU = 1,
    Nintendo3DSv2 = 2,
    NintendoSwitch = 3,
    NotSet = 0xFF,
};

enum class BreakType : u32 {
    Normal,
    Unknown1,
    Unknown2,
};

enum class WriteType : u32 {
    Unknown0,
    Unknown1,
};

enum class CabinetMode : u8 {
    StartNicknameAndOwnerSettings,
    StartGameDataEraser,
    StartRestorer,
    StartFormatter,
};

using UuidPart = std::array<u8, 3>;
using HashData = std::array<u8, 0x20>;
using ApplicationArea = std::array<u8, 0xD8>;
using AmiiboName = std::array<char, (amiibo_name_length * 4) + 1>;

// This is nn::nfp::TagInfo
using TagInfo = NFC::TagInfo;

struct NtagTagUuid {
    UuidPart part1;
    UuidPart part2;
    u8 nintendo_id;
};
static_assert(sizeof(NtagTagUuid) == 7, "NtagTagUuid is an invalid size");

struct TagUuid {
    UuidPart part1;
    u8 crc_check1;
    UuidPart part2;
    u8 nintendo_id;
};
static_assert(sizeof(TagUuid) == 8, "TagUuid is an invalid size");

struct WriteDate {
    u16 year;
    u8 month;
    u8 day;
};
static_assert(sizeof(WriteDate) == 0x4, "WriteDate is an invalid size");

struct AmiiboDate {
    u16 raw_date{};

    u16 GetValue() const {
        return Common::swap16(raw_date);
    }

    u16 GetYear() const {
        return static_cast<u16>(((GetValue() & 0xFE00) >> 9) + 2000);
    }
    u8 GetMonth() const {
        return static_cast<u8>((GetValue() & 0x01E0) >> 5);
    }
    u8 GetDay() const {
        return static_cast<u8>(GetValue() & 0x001F);
    }

    WriteDate GetWriteDate() const {
        if (!IsValidDate()) {
            return {
                .year = 2000,
                .month = 1,
                .day = 1,
            };
        }
        return {
            .year = GetYear(),
            .month = GetMonth(),
            .day = GetDay(),
        };
    }

    void SetWriteDate(const WriteDate& write_date) {
        SetYear(write_date.year);
        SetMonth(write_date.month);
        SetDay(write_date.day);
    }

    void SetYear(u16 year) {
        const u16 year_converted = static_cast<u16>((year - 2000) << 9);
        raw_date = Common::swap16((GetValue() & ~0xFE00) | year_converted);
    }
    void SetMonth(u8 month) {
        const u16 month_converted = static_cast<u16>(month << 5);
        raw_date = Common::swap16((GetValue() & ~0x01E0) | month_converted);
    }
    void SetDay(u8 day) {
        const u16 day_converted = static_cast<u16>(day);
        raw_date = Common::swap16((GetValue() & ~0x001F) | day_converted);
    }

    bool IsValidDate() const {
        const bool is_day_valid = GetDay() > 0 && GetDay() < 32;
        const bool is_month_valid = GetMonth() > 0 && GetMonth() < 13;
        const bool is_year_valid = GetYear() >= 2000;
        return is_year_valid && is_month_valid && is_day_valid;
    }
};
static_assert(sizeof(AmiiboDate) == 2, "AmiiboDate is an invalid size");

struct Settings {
    union {
        u8 raw{};

        BitField<0, 4, u8> font_region;
        BitField<4, 1, u8> amiibo_initialized;
        BitField<5, 1, u8> appdata_initialized;
    };
};
static_assert(sizeof(Settings) == 1, "AmiiboDate is an invalid size");

struct AmiiboSettings {
    Settings settings;
    u8 country_code_id;
    u16_be crc_counter; // Incremented each time crc is changed
    AmiiboDate init_date;
    AmiiboDate write_date;
    u32_be crc;
    std::array<u16_be, amiibo_name_length> amiibo_name; // UTF-16 text
};
static_assert(sizeof(AmiiboSettings) == 0x20, "AmiiboSettings is an invalid size");

struct AmiiboModelInfo {
    u16 character_id;
    u8 character_variant;
    AmiiboType amiibo_type;
    u16_be model_number;
    AmiiboSeries series;
    NFC::PackedTagType tag_type;
    INSERT_PADDING_BYTES(0x4); // Unknown
};
static_assert(sizeof(AmiiboModelInfo) == 0xC, "AmiiboModelInfo is an invalid size");

struct NTAG215Password {
    u32 PWD;  // Password to allow write access
    u16 PACK; // Password acknowledge reply
    u16 RFUI; // Reserved for future use
};
static_assert(sizeof(NTAG215Password) == 0x8, "NTAG215Password is an invalid size");

#pragma pack(1)
struct EncryptedAmiiboFile {
    u8 constant_value;                     // Must be A5
    u16_be write_counter;                  // Number of times the amiibo has been written?
    u8 amiibo_version;                     // Amiibo file version
    AmiiboSettings settings;               // Encrypted amiibo settings
    HashData hmac_tag;                     // Hash
    AmiiboModelInfo model_info;            // Encrypted amiibo model info
    HashData keygen_salt;                  // Salt
    HashData hmac_data;                    // Hash
    Service::Mii::Ver3StoreData owner_mii; // Encrypted Mii data
    u64_be application_id;                 // Encrypted Game id
    u16_be application_write_counter;      // Encrypted Counter
    u32_be application_area_id;            // Encrypted Game id
    u8 application_id_byte;
    u8 unknown;
    Service::Mii::NfpStoreDataExtension mii_extension;
    std::array<u32, 0x5> unknown2;
    u32_be register_info_crc;
    ApplicationArea application_area; // Encrypted Game data
};
static_assert(sizeof(EncryptedAmiiboFile) == 0x1F8, "AmiiboFile is an invalid size");

struct NTAG215File {
    u8 uid_crc_check2;
    u8 internal_number;
    u16 static_lock;             // Set defined pages as read only
    u32 compatibility_container; // Defines available memory
    HashData hmac_data;          // Hash
    u8 constant_value;           // Must be A5
    u16_be write_counter;        // Number of times the amiibo has been written?
    u8 amiibo_version;           // Amiibo file version
    AmiiboSettings settings;
    Service::Mii::Ver3StoreData owner_mii; // Mii data
    u64_be application_id;                 // Game id
    u16_be application_write_counter;      // Counter
    u32_be application_area_id;
    u8 application_id_byte;
    u8 unknown;
    Service::Mii::NfpStoreDataExtension mii_extension;
    std::array<u32, 0x5> unknown2;
    u32_be register_info_crc;
    ApplicationArea application_area; // Encrypted Game data
    HashData hmac_tag;                // Hash
    TagUuid uid;
    AmiiboModelInfo model_info;
    HashData keygen_salt;     // Salt
    u32 dynamic_lock;         // Dynamic lock
    u32 CFG0;                 // Defines memory protected by password
    u32 CFG1;                 // Defines number of verification attempts
    NTAG215Password password; // Password data
};
static_assert(sizeof(NTAG215File) == 0x21C, "NTAG215File is an invalid size");
static_assert(std::is_trivially_copyable_v<NTAG215File>, "NTAG215File must be trivially copyable.");
#pragma pack()

struct EncryptedNTAG215File {
    TagUuid uuid;
    u8 uuid_crc_check2;
    u8 internal_number;
    u16 static_lock;                 // Set defined pages as read only
    u32 compatibility_container;     // Defines available memory
    EncryptedAmiiboFile user_memory; // Writable data
    u32 dynamic_lock;                // Dynamic lock
    u32 CFG0;                        // Defines memory protected by password
    u32 CFG1;                        // Defines number of verification attempts
    NTAG215Password password;        // Password data
};
static_assert(sizeof(EncryptedNTAG215File) == sizeof(NTAG215File),
              "EncryptedNTAG215File is an invalid size");
static_assert(std::is_trivially_copyable_v<EncryptedNTAG215File>,
              "EncryptedNTAG215File must be trivially copyable.");

// This is nn::nfp::CommonInfo
struct CommonInfo {
    WriteDate last_write_date;
    u16 write_counter;
    u8 version;
    INSERT_PADDING_BYTES(0x1);
    u32 application_area_size;
    INSERT_PADDING_BYTES(0x34);
};
static_assert(sizeof(CommonInfo) == 0x40, "CommonInfo is an invalid size");

// This is nn::nfp::ModelInfo
struct ModelInfo {
    u16 character_id;
    u8 character_variant;
    AmiiboType amiibo_type;
    u16 model_number;
    AmiiboSeries series;
    INSERT_PADDING_BYTES(0x39); // Unknown
};
static_assert(sizeof(ModelInfo) == 0x40, "ModelInfo is an invalid size");

// This is nn::nfp::RegisterInfo
struct RegisterInfo {
    Service::Mii::CharInfo mii_char_info;
    WriteDate creation_date;
    AmiiboName amiibo_name;
    u8 font_region;
    INSERT_PADDING_BYTES(0x7A);
};
static_assert(sizeof(RegisterInfo) == 0x100, "RegisterInfo is an invalid size");

// This is nn::nfp::RegisterInfoPrivate
struct RegisterInfoPrivate {
    Service::Mii::StoreData mii_store_data;
    WriteDate creation_date;
    AmiiboName amiibo_name;
    u8 font_region;
    INSERT_PADDING_BYTES(0x8E);
};
static_assert(sizeof(RegisterInfoPrivate) == 0x100, "RegisterInfoPrivate is an invalid size");

// This is nn::nfp::AdminInfo
struct AdminInfo {
    u64 application_id;
    u32 application_area_id;
    u16 crc_change_counter;
    u8 flags;
    NFC::PackedTagType tag_type;
    AppAreaVersion app_area_version;
    INSERT_PADDING_BYTES(0x7);
    INSERT_PADDING_BYTES(0x28);
};
static_assert(sizeof(AdminInfo) == 0x40, "AdminInfo is an invalid size");

#pragma pack(1)
// This is nn::nfp::NfpData
struct NfpData {
    u8 magic;
    INSERT_PADDING_BYTES(0x1);
    u8 write_counter;
    INSERT_PADDING_BYTES(0x1);
    u32 settings_crc;
    INSERT_PADDING_BYTES(0x38);
    CommonInfo common_info;
    Service::Mii::Ver3StoreData mii_char_info;
    Service::Mii::NfpStoreDataExtension mii_store_data_extension;
    WriteDate creation_date;
    std::array<u16_be, amiibo_name_length> amiibo_name;
    u16 amiibo_name_null_terminated;
    Settings settings;
    u8 unknown1;
    u32 register_info_crc;
    std::array<u32, 5> unknown2;
    INSERT_PADDING_BYTES(0x64);
    u64 application_id;
    u32 access_id;
    u16 settings_crc_counter;
    u8 font_region;
    NFC::PackedTagType tag_type;
    AppAreaVersion console_type;
    u8 application_id_byte;
    INSERT_PADDING_BYTES(0x2E);
    ApplicationArea application_area;
};
static_assert(sizeof(NfpData) == 0x298, "NfpData is an invalid size");
#pragma pack()

} // namespace Service::NFP


code:
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#include "common/string_util.h"
#include "core/core.h"
#include "core/hle/service/ipc_helpers.h"
#include "core/hle/service/ngc/ngc.h"
#include "core/hle/service/server_manager.h"
#include "core/hle/service/service.h"

namespace Service::NGC {

class NgctServiceImpl final : public ServiceFramework<NgctServiceImpl> {
public:
    explicit NgctServiceImpl(Core::System& system_) : ServiceFramework{system_, "ngct:u"} {
        // clang-format off
        static const FunctionInfo functions[] = {
            {0, &NgctServiceImpl::Match, "Match"},
            {1, &NgctServiceImpl::Filter, "Filter"},
        };
        // clang-format on

        RegisterHandlers(functions);
    }

private:
    void Match(HLERequestContext& ctx) {
        const auto buffer = ctx.ReadBuffer();
        const auto text = Common::StringFromFixedZeroTerminatedBuffer(
            reinterpret_cast<const char*>(buffer.data()), buffer.size());

        LOG_WARNING(Service_NGC, "(STUBBED) called, text={}", text);

        IPC::ResponseBuilder rb{ctx, 3};
        rb.Push(ResultSuccess);
        // Return false since we don't censor anything
        rb.Push(false);
    }

    void Filter(HLERequestContext& ctx) {
        const auto buffer = ctx.ReadBuffer();
        const auto text = Common::StringFromFixedZeroTerminatedBuffer(
            reinterpret_cast<const char*>(buffer.data()), buffer.size());

        LOG_WARNING(Service_NGC, "(STUBBED) called, text={}", text);

        // Return the same string since we don't censor anything
        ctx.WriteBuffer(buffer);

        IPC::ResponseBuilder rb{ctx, 2};
        rb.Push(ResultSuccess);
    }
};

class NgcServiceImpl final : public ServiceFramework<NgcServiceImpl> {
public:
    explicit NgcServiceImpl(Core::System& system_) : ServiceFramework(system_, "ngc:u") {
        // clang-format off
        static const FunctionInfo functions[] = {
            {0, &NgcServiceImpl::GetContentVersion, "GetContentVersion"},
            {1, &NgcServiceImpl::Check, "Check"},
            {2, &NgcServiceImpl::Mask, "Mask"},
            {3, &NgcServiceImpl::Reload, "Reload"},
        };
        // clang-format on

        RegisterHandlers(functions);
    }

private:
    static constexpr u32 NgcContentVersion = 1;

    // This is nn::ngc::detail::ProfanityFilterOption
    struct ProfanityFilterOption {
        INSERT_PADDING_BYTES_NOINIT(0x20);
    };
    static_assert(sizeof(ProfanityFilterOption) == 0x20,
                  "ProfanityFilterOption has incorrect size");

    void GetContentVersion(HLERequestContext& ctx) {
        LOG_INFO(Service_NGC, "(STUBBED) called");

        // This calls nn::ngc::ProfanityFilter::GetContentVersion
        const u32 version = NgcContentVersion;

        IPC::ResponseBuilder rb{ctx, 3};
        rb.Push(ResultSuccess);
        rb.Push(version);
    }

    void Check(HLERequestContext& ctx) {
        LOG_INFO(Service_NGC, "(STUBBED) called");

        struct InputParameters {
            u32 flags;
            ProfanityFilterOption option;
        };

        IPC::RequestParser rp{ctx};
        [[maybe_unused]] const auto params = rp.PopRaw<InputParameters>();
        [[maybe_unused]] const auto input = ctx.ReadBuffer(0);

        // This calls nn::ngc::ProfanityFilter::CheckProfanityWords
        const u32 out_flags = 0;

        IPC::ResponseBuilder rb{ctx, 3};
        rb.Push(ResultSuccess);
        rb.Push(out_flags);
    }

    void Mask(HLERequestContext& ctx) {
        LOG_INFO(Service_NGC, "(STUBBED) called");

        struct InputParameters {
            u32 flags;
            ProfanityFilterOption option;
        };

        IPC::RequestParser rp{ctx};
        [[maybe_unused]] const auto params = rp.PopRaw<InputParameters>();
        const auto input = ctx.ReadBuffer(0);

        // This calls nn::ngc::ProfanityFilter::MaskProfanityWordsInText
        const u32 out_flags = 0;
        ctx.WriteBuffer(input);

        IPC::ResponseBuilder rb{ctx, 3};
        rb.Push(ResultSuccess);
        rb.Push(out_flags);
    }

    void Reload(HLERequestContext& ctx) {
        LOG_INFO(Service_NGC, "(STUBBED) called");

        // This reloads the database.

        IPC::ResponseBuilder rb{ctx, 2};
        rb.Push(ResultSuccess);
    }
};

void LoopProcess(Core::System& system) {
    auto server_manager = std::make_unique<ServerManager>(system);

    server_manager->RegisterNamedService("ngct:u", std::make_shared<NgctServiceImpl>(system));
    server_manager->RegisterNamedService("ngc:u", std::make_shared<NgcServiceImpl>(system));
    ServerManager::RunServer(std::move(server_manager));
}

} // namespace Service::NGC


code:
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#pragma once

namespace Core {
class System;
}

namespace Service::NGC {

void LoopProcess(Core::System& system);

} // namespace Service::NGC