mirror of https://git.suyu.dev/suyu/suyu
Merge pull request #8955 from german77/amiibo-rewrite
core: nfp: Rewrite implementation to remove direct access from the frontendmerge-requests/60/head
commit
61399de5db
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,676 @@
|
|||||||
|
// 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"
|
||||||
|
#include "core/hle/service/time/time_manager.h"
|
||||||
|
#include "core/hle/service/time/time_zone_content_manager.h"
|
||||||
|
#include "core/hle/service/time/time_zone_types.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);
|
||||||
|
|
||||||
|
auto& standard_steady_clock{system.GetTimeManager().GetStandardSteadyClockCore()};
|
||||||
|
current_posix_time = standard_steady_clock.GetCurrentTimePoint(system).time_point;
|
||||||
|
}
|
||||||
|
|
||||||
|
NfpDevice::~NfpDevice() {
|
||||||
|
if (!is_controller_set) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
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(std::span<const 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;
|
||||||
|
deactivate_event->GetReadableEvent().Clear();
|
||||||
|
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 = {};
|
||||||
|
activate_event->GetReadableEvent().Clear();
|
||||||
|
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_) {
|
||||||
|
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() {
|
||||||
|
if (device_state != DeviceState::TagMounted) {
|
||||||
|
LOG_ERROR(Service_NFP, "Wrong device state {}", device_state);
|
||||||
|
if (device_state == DeviceState::TagRemoved) {
|
||||||
|
return TagRemoved;
|
||||||
|
}
|
||||||
|
return WrongDeviceState;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mount_target == MountTarget::None || mount_target == MountTarget::Rom) {
|
||||||
|
LOG_ERROR(Service_NFP, "Amiibo is read only", device_state);
|
||||||
|
return WrongDeviceState;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto& settings = tag_data.settings;
|
||||||
|
|
||||||
|
const auto& current_date = GetAmiiboDate(current_posix_time);
|
||||||
|
if (settings.write_date.raw_date != current_date.raw_date) {
|
||||||
|
settings.write_date = current_date;
|
||||||
|
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(MountTarget mount_target_) {
|
||||||
|
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;
|
||||||
|
mount_target = mount_target_;
|
||||||
|
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;
|
||||||
|
mount_target = MountTarget::None;
|
||||||
|
is_app_area_open = false;
|
||||||
|
|
||||||
|
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.uid,
|
||||||
|
.uuid_length = static_cast<u8>(encrypted_tag_data.uuid.uid.size()),
|
||||||
|
.protocol = TagProtocol::TypeA,
|
||||||
|
.tag_type = TagType::Type2,
|
||||||
|
};
|
||||||
|
|
||||||
|
return ResultSuccess;
|
||||||
|
}
|
||||||
|
|
||||||
|
Result NfpDevice::GetCommonInfo(CommonInfo& common_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 (mount_target == MountTarget::None || mount_target == MountTarget::Rom) {
|
||||||
|
LOG_ERROR(Service_NFP, "Amiibo is read only", device_state);
|
||||||
|
return WrongDeviceState;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto& settings = tag_data.settings;
|
||||||
|
|
||||||
|
// 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 = 0,
|
||||||
|
.application_area_size = sizeof(ApplicationArea),
|
||||||
|
};
|
||||||
|
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 (mount_target == MountTarget::None || mount_target == MountTarget::Rom) {
|
||||||
|
LOG_ERROR(Service_NFP, "Amiibo is read only", device_state);
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mount_target == MountTarget::None || mount_target == MountTarget::Rom) {
|
||||||
|
LOG_ERROR(Service_NFP, "Amiibo is read only", device_state);
|
||||||
|
return WrongDeviceState;
|
||||||
|
}
|
||||||
|
|
||||||
|
Service::Mii::MiiManager manager;
|
||||||
|
auto& settings = tag_data.settings;
|
||||||
|
|
||||||
|
settings.init_date = GetAmiiboDate(current_posix_time);
|
||||||
|
settings.write_date = GetAmiiboDate(current_posix_time);
|
||||||
|
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() {
|
||||||
|
if (device_state != DeviceState::TagMounted) {
|
||||||
|
LOG_ERROR(Service_NFP, "Wrong device state {}", device_state);
|
||||||
|
if (device_state == DeviceState::TagRemoved) {
|
||||||
|
return TagRemoved;
|
||||||
|
}
|
||||||
|
return WrongDeviceState;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mount_target == MountTarget::None || mount_target == MountTarget::Rom) {
|
||||||
|
LOG_ERROR(Service_NFP, "Amiibo is read only", device_state);
|
||||||
|
return WrongDeviceState;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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 (mount_target == MountTarget::None || mount_target == MountTarget::Rom) {
|
||||||
|
LOG_ERROR(Service_NFP, "Amiibo is read only", device_state);
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
is_app_area_open = true;
|
||||||
|
|
||||||
|
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 (mount_target == MountTarget::None || mount_target == MountTarget::Rom) {
|
||||||
|
LOG_ERROR(Service_NFP, "Amiibo is read only", device_state);
|
||||||
|
return WrongDeviceState;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!is_app_area_open) {
|
||||||
|
LOG_ERROR(Service_NFP, "Application area is not open");
|
||||||
|
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(std::span<const 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 (mount_target == MountTarget::None || mount_target == MountTarget::Rom) {
|
||||||
|
LOG_ERROR(Service_NFP, "Amiibo is read only", device_state);
|
||||||
|
return WrongDeviceState;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!is_app_area_open) {
|
||||||
|
LOG_ERROR(Service_NFP, "Application area is not open");
|
||||||
|
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{};
|
||||||
|
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());
|
||||||
|
|
||||||
|
tag_data.applicaton_write_counter++;
|
||||||
|
is_data_moddified = true;
|
||||||
|
|
||||||
|
return ResultSuccess;
|
||||||
|
}
|
||||||
|
|
||||||
|
Result NfpDevice::CreateApplicationArea(u32 access_id, std::span<const 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, std::span<const 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 (mount_target == MountTarget::None || mount_target == MountTarget::Rom) {
|
||||||
|
LOG_ERROR(Service_NFP, "Amiibo is read only", device_state);
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mount_target == MountTarget::None || mount_target == MountTarget::Rom) {
|
||||||
|
LOG_ERROR(Service_NFP, "Amiibo is read only", device_state);
|
||||||
|
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]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
AmiiboDate NfpDevice::GetAmiiboDate(s64 posix_time) const {
|
||||||
|
const auto& time_zone_manager =
|
||||||
|
system.GetTimeManager().GetTimeZoneContentManager().GetTimeZoneManager();
|
||||||
|
Time::TimeZone::CalendarInfo calendar_info{};
|
||||||
|
AmiiboDate amiibo_date{};
|
||||||
|
|
||||||
|
amiibo_date.SetYear(2000);
|
||||||
|
amiibo_date.SetMonth(1);
|
||||||
|
amiibo_date.SetDay(1);
|
||||||
|
|
||||||
|
if (time_zone_manager.ToCalendarTime({}, posix_time, calendar_info) == ResultSuccess) {
|
||||||
|
amiibo_date.SetYear(calendar_info.time.year);
|
||||||
|
amiibo_date.SetMonth(calendar_info.time.month);
|
||||||
|
amiibo_date.SetDay(calendar_info.time.day);
|
||||||
|
}
|
||||||
|
|
||||||
|
return amiibo_date;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Service::NFP
|
@ -0,0 +1,101 @@
|
|||||||
|
// 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(MountTarget mount_target);
|
||||||
|
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(std::span<const u8> data);
|
||||||
|
Result CreateApplicationArea(u32 access_id, std::span<const u8> data);
|
||||||
|
Result RecreateApplicationArea(u32 access_id, std::span<const 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(std::span<const u8> data);
|
||||||
|
void CloseAmiibo();
|
||||||
|
|
||||||
|
AmiiboName GetAmiiboName(const AmiiboSettings& settings) const;
|
||||||
|
void SetAmiiboName(AmiiboSettings& settings, const AmiiboName& amiibo_name);
|
||||||
|
AmiiboDate GetAmiiboDate(s64 posix_time) const;
|
||||||
|
|
||||||
|
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{};
|
||||||
|
bool is_app_area_open{};
|
||||||
|
s32 protocol{};
|
||||||
|
s64 current_posix_time{};
|
||||||
|
MountTarget mount_target{MountTarget::None};
|
||||||
|
DeviceState device_state{DeviceState::Unavailable};
|
||||||
|
|
||||||
|
NTAG215File tag_data{};
|
||||||
|
EncryptedNTAG215File encrypted_tag_data{};
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Service::NFP
|
@ -0,0 +1,22 @@
|
|||||||
|
// 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);
|
||||||
|
constexpr Result NotAnAmiibo(ErrorModule::NFP, 178);
|
||||||
|
|
||||||
|
} // namespace Service::NFP
|
@ -1,18 +1,644 @@
|
|||||||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// 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"
|
#include "core/hle/service/nfp/nfp_user.h"
|
||||||
|
|
||||||
namespace Service::NFP {
|
namespace Service::NFP {
|
||||||
|
|
||||||
NFP_User::NFP_User(std::shared_ptr<Module> module_, Core::System& system_)
|
IUser::IUser(Core::System& system_)
|
||||||
: Interface(std::move(module_), system_, "nfp:user") {
|
: ServiceFramework{system_, "NFP::IUser"}, service_context{system_, service_name} {
|
||||||
static const FunctionInfo functions[] = {
|
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);
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
NFP_User::~NFP_User() = default;
|
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(mount_target);
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
} // namespace Service::NFP
|
||||||
|
@ -0,0 +1,101 @@
|
|||||||
|
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
|
#include <cstring>
|
||||||
|
#include <fmt/format.h>
|
||||||
|
|
||||||
|
#include "common/fs/file.h"
|
||||||
|
#include "common/fs/fs.h"
|
||||||
|
#include "common/fs/path_util.h"
|
||||||
|
#include "common/logging/log.h"
|
||||||
|
#include "common/settings.h"
|
||||||
|
#include "input_common/drivers/virtual_amiibo.h"
|
||||||
|
|
||||||
|
namespace InputCommon {
|
||||||
|
constexpr PadIdentifier identifier = {
|
||||||
|
.guid = Common::UUID{},
|
||||||
|
.port = 0,
|
||||||
|
.pad = 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
VirtualAmiibo::VirtualAmiibo(std::string input_engine_) : InputEngine(std::move(input_engine_)) {}
|
||||||
|
|
||||||
|
VirtualAmiibo::~VirtualAmiibo() = default;
|
||||||
|
|
||||||
|
Common::Input::PollingError VirtualAmiibo::SetPollingMode(
|
||||||
|
[[maybe_unused]] const PadIdentifier& identifier_,
|
||||||
|
const Common::Input::PollingMode polling_mode_) {
|
||||||
|
polling_mode = polling_mode_;
|
||||||
|
|
||||||
|
if (polling_mode == Common::Input::PollingMode::NFC) {
|
||||||
|
if (state == State::Initialized) {
|
||||||
|
state = State::WaitingForAmiibo;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (state == State::AmiiboIsOpen) {
|
||||||
|
CloseAmiibo();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Common::Input::PollingError::None;
|
||||||
|
}
|
||||||
|
|
||||||
|
Common::Input::NfcState VirtualAmiibo::SupportsNfc(
|
||||||
|
[[maybe_unused]] const PadIdentifier& identifier_) const {
|
||||||
|
return Common::Input::NfcState::Success;
|
||||||
|
}
|
||||||
|
|
||||||
|
Common::Input::NfcState VirtualAmiibo::WriteNfcData(
|
||||||
|
[[maybe_unused]] const PadIdentifier& identifier_, const std::vector<u8>& 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 Common::Input::NfcState::WriteFailed;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!amiibo_file.Write(data)) {
|
||||||
|
LOG_ERROR(Service_NFP, "Error writting to file");
|
||||||
|
return Common::Input::NfcState::WriteFailed;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Common::Input::NfcState::Success;
|
||||||
|
}
|
||||||
|
|
||||||
|
VirtualAmiibo::State VirtualAmiibo::GetCurrentState() const {
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
|
||||||
|
VirtualAmiibo::Info VirtualAmiibo::LoadAmiibo(const std::string& filename) {
|
||||||
|
const Common::FS::IOFile amiibo_file{filename, Common::FS::FileAccessMode::Read,
|
||||||
|
Common::FS::FileType::BinaryFile};
|
||||||
|
|
||||||
|
if (state != State::WaitingForAmiibo) {
|
||||||
|
return Info::WrongDeviceState;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!amiibo_file.IsOpen()) {
|
||||||
|
return Info::UnableToLoad;
|
||||||
|
}
|
||||||
|
|
||||||
|
amiibo_data.resize(amiibo_size);
|
||||||
|
|
||||||
|
if (amiibo_file.Read(amiibo_data) < amiibo_size_without_password) {
|
||||||
|
return Info::NotAnAmiibo;
|
||||||
|
}
|
||||||
|
|
||||||
|
file_path = filename;
|
||||||
|
state = State::AmiiboIsOpen;
|
||||||
|
SetNfc(identifier, {Common::Input::NfcState::NewAmiibo, amiibo_data});
|
||||||
|
return Info::Success;
|
||||||
|
}
|
||||||
|
|
||||||
|
VirtualAmiibo::Info VirtualAmiibo::CloseAmiibo() {
|
||||||
|
state = polling_mode == Common::Input::PollingMode::NFC ? State::WaitingForAmiibo
|
||||||
|
: State::Initialized;
|
||||||
|
SetNfc(identifier, {Common::Input::NfcState::AmiiboRemoved, {}});
|
||||||
|
return Info::Success;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace InputCommon
|
@ -0,0 +1,61 @@
|
|||||||
|
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <array>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "common/common_types.h"
|
||||||
|
#include "input_common/input_engine.h"
|
||||||
|
|
||||||
|
namespace Common::FS {
|
||||||
|
class IOFile;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace InputCommon {
|
||||||
|
|
||||||
|
class VirtualAmiibo final : public InputEngine {
|
||||||
|
public:
|
||||||
|
enum class State {
|
||||||
|
Initialized,
|
||||||
|
WaitingForAmiibo,
|
||||||
|
AmiiboIsOpen,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class Info {
|
||||||
|
Success,
|
||||||
|
UnableToLoad,
|
||||||
|
NotAnAmiibo,
|
||||||
|
WrongDeviceState,
|
||||||
|
Unknown,
|
||||||
|
};
|
||||||
|
|
||||||
|
explicit VirtualAmiibo(std::string input_engine_);
|
||||||
|
~VirtualAmiibo() override;
|
||||||
|
|
||||||
|
// Sets polling mode to a controller
|
||||||
|
Common::Input::PollingError SetPollingMode(
|
||||||
|
const PadIdentifier& identifier_, const Common::Input::PollingMode polling_mode_) override;
|
||||||
|
|
||||||
|
Common::Input::NfcState SupportsNfc(const PadIdentifier& identifier_) const override;
|
||||||
|
|
||||||
|
Common::Input::NfcState WriteNfcData(const PadIdentifier& identifier_,
|
||||||
|
const std::vector<u8>& data) override;
|
||||||
|
|
||||||
|
State GetCurrentState() const;
|
||||||
|
|
||||||
|
Info LoadAmiibo(const std::string& amiibo_file);
|
||||||
|
Info CloseAmiibo();
|
||||||
|
|
||||||
|
private:
|
||||||
|
static constexpr std::size_t amiibo_size = 0x21C;
|
||||||
|
static constexpr std::size_t amiibo_size_without_password = amiibo_size - 0x8;
|
||||||
|
|
||||||
|
std::string file_path{};
|
||||||
|
State state{State::Initialized};
|
||||||
|
std::vector<u8> amiibo_data;
|
||||||
|
Common::Input::PollingMode polling_mode{Common::Input::PollingMode::Pasive};
|
||||||
|
};
|
||||||
|
} // namespace InputCommon
|
Loading…
Reference in New Issue