From 50e4e81fd3a998813316cdabd05ef56b12c91fae Mon Sep 17 00:00:00 2001 From: David <25727384+ogniK5377@users.noreply.github.com> Date: Wed, 24 Oct 2018 10:28:17 +1100 Subject: Added Amiibo support (#1390) * Fixed conflict with nfp * Few fixups for nfc * Conflict 2 * Fixed AttachAvailabilityChangeEvent * Conflict 3 * Fixed byte padding * Refactored amiibo to not reside in "System" * Removed remaining references of nfc from system * used enum for Nfc GetStateOld * Added missing newline * Moved file operations to front end * Conflict 4 * Amiibos now use structs and added mutexes * Removed amiibo_path --- src/core/hle/service/nfp/nfp.cpp | 268 +++++++++++++++++++++++++++++++++------ src/core/hle/service/nfp/nfp.h | 23 ++++ 2 files changed, 249 insertions(+), 42 deletions(-) (limited to 'src/core/hle/service/nfp') diff --git a/src/core/hle/service/nfp/nfp.cpp b/src/core/hle/service/nfp/nfp.cpp index 39c0c1e63..9a4eb9301 100644 --- a/src/core/hle/service/nfp/nfp.cpp +++ b/src/core/hle/service/nfp/nfp.cpp @@ -2,56 +2,67 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include + #include "common/logging/log.h" #include "core/core.h" #include "core/hle/ipc_helpers.h" #include "core/hle/kernel/event.h" +#include "core/hle/lock.h" #include "core/hle/service/hid/hid.h" #include "core/hle/service/nfp/nfp.h" #include "core/hle/service/nfp/nfp_user.h" namespace Service::NFP { +namespace ErrCodes { +constexpr ResultCode ERR_TAG_FAILED(ErrorModule::NFP, + -1); // TODO(ogniK): Find the actual error code +} + Module::Interface::Interface(std::shared_ptr module, const char* name) - : ServiceFramework(name), module(std::move(module)) {} + : ServiceFramework(name), module(std::move(module)) { + auto& kernel = Core::System::GetInstance().Kernel(); + nfc_tag_load = + Kernel::Event::Create(kernel, Kernel::ResetType::OneShot, "IUser:NFCTagDetected"); +} Module::Interface::~Interface() = default; class IUser final : public ServiceFramework { public: - IUser() : ServiceFramework("IUser") { + IUser(Module::Interface& nfp_interface) + : ServiceFramework("NFP::IUser"), nfp_interface(nfp_interface) { static const FunctionInfo functions[] = { {0, &IUser::Initialize, "Initialize"}, - {1, nullptr, "Finalize"}, + {1, &IUser::Finalize, "Finalize"}, {2, &IUser::ListDevices, "ListDevices"}, - {3, nullptr, "StartDetection"}, - {4, nullptr, "StopDetection"}, - {5, nullptr, "Mount"}, - {6, nullptr, "Unmount"}, - {7, nullptr, "OpenApplicationArea"}, - {8, nullptr, "GetApplicationArea"}, + {3, &IUser::StartDetection, "StartDetection"}, + {4, &IUser::StopDetection, "StopDetection"}, + {5, &IUser::Mount, "Mount"}, + {6, &IUser::Unmount, "Unmount"}, + {7, &IUser::OpenApplicationArea, "OpenApplicationArea"}, + {8, &IUser::GetApplicationArea, "GetApplicationArea"}, {9, nullptr, "SetApplicationArea"}, {10, nullptr, "Flush"}, {11, nullptr, "Restore"}, {12, nullptr, "CreateApplicationArea"}, - {13, nullptr, "GetTagInfo"}, - {14, nullptr, "GetRegisterInfo"}, - {15, nullptr, "GetCommonInfo"}, - {16, nullptr, "GetModelInfo"}, + {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, nullptr, "GetApplicationArea2"}, + {22, &IUser::GetApplicationAreaSize, "GetApplicationAreaSize"}, {23, &IUser::AttachAvailabilityChangeEvent, "AttachAvailabilityChangeEvent"}, {24, nullptr, "RecreateApplicationArea"}, }; RegisterHandlers(functions); auto& kernel = Core::System::GetInstance().Kernel(); - activate_event = - Kernel::Event::Create(kernel, Kernel::ResetType::OneShot, "IUser:ActivateEvent"); deactivate_event = Kernel::Event::Create(kernel, Kernel::ResetType::OneShot, "IUser:DeactivateEvent"); availability_change_event = Kernel::Event::Create(kernel, Kernel::ResetType::OneShot, @@ -59,6 +70,17 @@ public: } private: + struct TagInfo { + std::array uuid; + u8 uuid_length; // TODO(ogniK): Figure out if this is actual the uuid length or does it + // mean something else + INSERT_PADDING_BYTES(0x15); + u32_le protocol; + u32_le tag_type; + INSERT_PADDING_BYTES(0x2c); + }; + static_assert(sizeof(TagInfo) == 0x54, "TagInfo is an invalid size"); + enum class State : u32 { NonInitialized = 0, Initialized = 1, @@ -66,15 +88,40 @@ private: enum class DeviceState : u32 { Initialized = 0, + SearchingForTag = 1, + TagFound = 2, + TagRemoved = 3, + TagNearby = 4, + Unknown5 = 5, + Finalized = 6 }; + struct CommonInfo { + u16_be last_write_year; + u8 last_write_month; + u8 last_write_day; + u16_be write_counter; + u16_be version; + u32_be application_area_size; + INSERT_PADDING_BYTES(0x34); + }; + static_assert(sizeof(CommonInfo) == 0x40, "CommonInfo is an invalid size"); + void Initialize(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service_NFP, "(STUBBED) called"); + IPC::ResponseBuilder rb{ctx, 2, 0}; + rb.Push(RESULT_SUCCESS); state = State::Initialized; - IPC::ResponseBuilder rb{ctx, 2}; + LOG_DEBUG(Service_NFC, "called"); + } + + void GetState(Kernel::HLERequestContext& ctx) { + IPC::ResponseBuilder rb{ctx, 3, 0}; rb.Push(RESULT_SUCCESS); + rb.PushRaw(static_cast(state)); + + LOG_DEBUG(Service_NFC, "called"); } void ListDevices(Kernel::HLERequestContext& ctx) { @@ -83,80 +130,217 @@ private: ctx.WriteBuffer(&device_handle, sizeof(device_handle)); - LOG_WARNING(Service_NFP, "(STUBBED) called, array_size={}", array_size); + LOG_DEBUG(Service_NFP, "called, array_size={}", array_size); IPC::ResponseBuilder rb{ctx, 3}; rb.Push(RESULT_SUCCESS); - rb.Push(0); + rb.Push(1); } - void AttachActivateEvent(Kernel::HLERequestContext& ctx) { + void GetNpadId(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; const u64 dev_handle = rp.Pop(); - LOG_WARNING(Service_NFP, "(STUBBED) called, dev_handle=0x{:X}", dev_handle); + LOG_DEBUG(Service_NFP, "called, dev_handle=0x{:X}", dev_handle); + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(RESULT_SUCCESS); + rb.Push(npad_id); + } + void AttachActivateEvent(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const u64 dev_handle = rp.Pop(); + LOG_DEBUG(Service_NFP, "called, dev_handle=0x{:X}", dev_handle); IPC::ResponseBuilder rb{ctx, 2, 1}; rb.Push(RESULT_SUCCESS); - rb.PushCopyObjects(activate_event); + rb.PushCopyObjects(nfp_interface.GetNFCEvent()); + has_attached_handle = true; } void AttachDeactivateEvent(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; const u64 dev_handle = rp.Pop(); - LOG_WARNING(Service_NFP, "(STUBBED) called, dev_handle=0x{:X}", dev_handle); + LOG_DEBUG(Service_NFP, "called, dev_handle=0x{:X}", dev_handle); IPC::ResponseBuilder rb{ctx, 2, 1}; rb.Push(RESULT_SUCCESS); rb.PushCopyObjects(deactivate_event); } - void GetState(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service_NFP, "(STUBBED) called"); - IPC::ResponseBuilder rb{ctx, 3}; + void StopDetection(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_NFP, "called"); + switch (device_state) { + case DeviceState::TagFound: + case DeviceState::TagNearby: + deactivate_event->Signal(); + device_state = DeviceState::Initialized; + break; + case DeviceState::SearchingForTag: + case DeviceState::TagRemoved: + device_state = DeviceState::Initialized; + break; + } + IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); - rb.Push(static_cast(state)); } void GetDeviceState(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service_NFP, "(STUBBED) called"); + LOG_DEBUG(Service_NFP, "called"); + auto nfc_event = nfp_interface.GetNFCEvent(); + if (!nfc_event->ShouldWait(Kernel::GetCurrentThread()) && !has_attached_handle) { + device_state = DeviceState::TagFound; + nfc_event->Clear(); + } + IPC::ResponseBuilder rb{ctx, 3}; rb.Push(RESULT_SUCCESS); rb.Push(static_cast(device_state)); } - void GetNpadId(Kernel::HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const u64 dev_handle = rp.Pop(); - LOG_WARNING(Service_NFP, "(STUBBED) called, dev_handle=0x{:X}", dev_handle); - IPC::ResponseBuilder rb{ctx, 3}; + void StartDetection(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_NFP, "called"); + + if (device_state == DeviceState::Initialized || device_state == DeviceState::TagRemoved) { + device_state = DeviceState::SearchingForTag; + } + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); + } + + void GetTagInfo(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_NFP, "called"); + + IPC::ResponseBuilder rb{ctx, 2}; + auto amiibo = nfp_interface.GetAmiiboBuffer(); + TagInfo tag_info{}; + std::memcpy(tag_info.uuid.data(), amiibo.uuid.data(), sizeof(tag_info.uuid.size())); + tag_info.uuid_length = static_cast(tag_info.uuid.size()); + + tag_info.protocol = 1; // TODO(ogniK): Figure out actual values + tag_info.tag_type = 2; + ctx.WriteBuffer(&tag_info, sizeof(TagInfo)); + rb.Push(RESULT_SUCCESS); + } + + void Mount(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_NFP, "called"); + + device_state = DeviceState::TagNearby; + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); + } + + void GetModelInfo(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_NFP, "called"); + + IPC::ResponseBuilder rb{ctx, 2}; + auto amiibo = nfp_interface.GetAmiiboBuffer(); + ctx.WriteBuffer(&amiibo.model_info, sizeof(amiibo.model_info)); + rb.Push(RESULT_SUCCESS); + } + + void Unmount(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_NFP, "called"); + + device_state = DeviceState::TagFound; + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); + } + + void Finalize(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_NFP, "called"); + + device_state = DeviceState::Finalized; + + IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); - rb.Push(npad_id); } void AttachAvailabilityChangeEvent(Kernel::HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const u64 dev_handle = rp.Pop(); - LOG_WARNING(Service_NFP, "(STUBBED) called, dev_handle=0x{:X}", dev_handle); + LOG_WARNING(Service_NFP, "(STUBBED) called"); IPC::ResponseBuilder rb{ctx, 2, 1}; rb.Push(RESULT_SUCCESS); rb.PushCopyObjects(availability_change_event); } - const u64 device_handle{0xDEAD}; - const u32 npad_id{0}; // This is the first player controller id + void GetRegisterInfo(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_NFP, "(STUBBED) called"); + + // TODO(ogniK): Pull Mii and owner data from amiibo + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); + } + + void GetCommonInfo(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_NFP, "(STUBBED) called"); + + // TODO(ogniK): Pull common information from amiibo + + CommonInfo common_info{}; + common_info.application_area_size = 0; + ctx.WriteBuffer(&common_info, sizeof(CommonInfo)); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); + } + + void OpenApplicationArea(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_NFP, "called"); + // We don't need to worry about this since we can just open the file + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); + } + + void GetApplicationAreaSize(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_NFP, "(STUBBED) called"); + // We don't need to worry about this since we can just open the file + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(RESULT_SUCCESS); + rb.PushRaw(0); // This is from the GetCommonInfo stub + } + + void GetApplicationArea(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_NFP, "(STUBBED) called"); + + // TODO(ogniK): Pull application area from amiibo + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(RESULT_SUCCESS); + rb.PushRaw(0); // This is from the GetCommonInfo stub + } + + bool has_attached_handle{}; + const u64 device_handle{Common::MakeMagic('Y', 'U', 'Z', 'U')}; + const u32 npad_id{0}; // Player 1 controller State state{State::NonInitialized}; DeviceState device_state{DeviceState::Initialized}; - Kernel::SharedPtr activate_event; Kernel::SharedPtr deactivate_event; Kernel::SharedPtr availability_change_event; + const Module::Interface& nfp_interface; }; void Module::Interface::CreateUserInterface(Kernel::HLERequestContext& ctx) { LOG_DEBUG(Service_NFP, "called"); IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); - rb.PushIpcInterface(); + rb.PushIpcInterface(*this); +} + +void Module::Interface::LoadAmiibo(const std::vector& buffer) { + std::lock_guard lock(HLE::g_hle_lock); + if (buffer.size() < sizeof(AmiiboFile)) { + return; // Failed to load file + } + std::memcpy(&amiibo, buffer.data(), sizeof(amiibo)); + nfc_tag_load->Signal(); +} +const Kernel::SharedPtr& Module::Interface::GetNFCEvent() const { + return nfc_tag_load; +} +const Module::Interface::AmiiboFile& Module::Interface::GetAmiiboBuffer() const { + return amiibo; } void InstallInterfaces(SM::ServiceManager& service_manager) { diff --git a/src/core/hle/service/nfp/nfp.h b/src/core/hle/service/nfp/nfp.h index 77df343c4..46370dedd 100644 --- a/src/core/hle/service/nfp/nfp.h +++ b/src/core/hle/service/nfp/nfp.h @@ -4,6 +4,9 @@ #pragma once +#include +#include +#include "core/hle/kernel/event.h" #include "core/hle/service/service.h" namespace Service::NFP { @@ -15,7 +18,27 @@ public: explicit Interface(std::shared_ptr module, const char* name); ~Interface() override; + struct ModelInfo { + std::array amiibo_identification_block; + INSERT_PADDING_BYTES(0x38); + }; + static_assert(sizeof(ModelInfo) == 0x40, "ModelInfo is an invalid size"); + + struct AmiiboFile { + std::array uuid; + INSERT_PADDING_BYTES(0x4a); + ModelInfo model_info; + }; + static_assert(sizeof(AmiiboFile) == 0x94, "AmiiboFile is an invalid size"); + void CreateUserInterface(Kernel::HLERequestContext& ctx); + void LoadAmiibo(const std::vector& buffer); + const Kernel::SharedPtr& GetNFCEvent() const; + const AmiiboFile& GetAmiiboBuffer() const; + + private: + Kernel::SharedPtr nfc_tag_load{}; + AmiiboFile amiibo{}; protected: std::shared_ptr module; -- cgit v1.2.3