diff options
| -rw-r--r-- | src/core/hid/emulated_controller.cpp | 6 | ||||
| -rw-r--r-- | src/core/hid/emulated_controller.h | 11 | ||||
| -rw-r--r-- | src/core/hle/kernel/k_page_table.cpp | 5 | ||||
| -rw-r--r-- | src/core/hle/service/nfp/nfp.cpp | 916 | ||||
| -rw-r--r-- | src/core/hle/service/nfp/nfp.h | 242 | ||||
| -rw-r--r-- | src/yuzu/configuration/config.cpp | 6 | ||||
| -rw-r--r-- | src/yuzu/main.cpp | 20 | ||||
| -rw-r--r-- | src/yuzu/main.ui | 2 |
8 files changed, 951 insertions, 257 deletions
diff --git a/src/core/hid/emulated_controller.cpp b/src/core/hid/emulated_controller.cpp index 2bee173b3..7e05666d6 100644 --- a/src/core/hid/emulated_controller.cpp +++ b/src/core/hid/emulated_controller.cpp | |||
| @@ -885,6 +885,12 @@ bool EmulatedController::TestVibration(std::size_t device_index) { | |||
| 885 | return SetVibration(device_index, DEFAULT_VIBRATION_VALUE); | 885 | return SetVibration(device_index, DEFAULT_VIBRATION_VALUE); |
| 886 | } | 886 | } |
| 887 | 887 | ||
| 888 | bool EmulatedController::SetPollingMode(Common::Input::PollingMode polling_mode) { | ||
| 889 | LOG_INFO(Service_HID, "Set polling mode {}", polling_mode); | ||
| 890 | auto& output_device = output_devices[static_cast<std::size_t>(DeviceIndex::Right)]; | ||
| 891 | return output_device->SetPollingMode(polling_mode) == Common::Input::PollingError::None; | ||
| 892 | } | ||
| 893 | |||
| 888 | void EmulatedController::SetLedPattern() { | 894 | void EmulatedController::SetLedPattern() { |
| 889 | for (auto& device : output_devices) { | 895 | for (auto& device : output_devices) { |
| 890 | if (!device) { | 896 | if (!device) { |
diff --git a/src/core/hid/emulated_controller.h b/src/core/hid/emulated_controller.h index d8642c5b3..aa52f9572 100644 --- a/src/core/hid/emulated_controller.h +++ b/src/core/hid/emulated_controller.h | |||
| @@ -299,16 +299,23 @@ public: | |||
| 299 | 299 | ||
| 300 | /** | 300 | /** |
| 301 | * Sends a specific vibration to the output device | 301 | * Sends a specific vibration to the output device |
| 302 | * @return returns true if vibration had no errors | 302 | * @return true if vibration had no errors |
| 303 | */ | 303 | */ |
| 304 | bool SetVibration(std::size_t device_index, VibrationValue vibration); | 304 | bool SetVibration(std::size_t device_index, VibrationValue vibration); |
| 305 | 305 | ||
| 306 | /** | 306 | /** |
| 307 | * Sends a small vibration to the output device | 307 | * Sends a small vibration to the output device |
| 308 | * @return returns true if SetVibration was successfull | 308 | * @return true if SetVibration was successfull |
| 309 | */ | 309 | */ |
| 310 | bool TestVibration(std::size_t device_index); | 310 | bool TestVibration(std::size_t device_index); |
| 311 | 311 | ||
| 312 | /** | ||
| 313 | * Sets the desired data to be polled from a controller | ||
| 314 | * @param polling_mode type of input desired buttons, gyro, nfc, ir, etc. | ||
| 315 | * @return true if SetPollingMode was successfull | ||
| 316 | */ | ||
| 317 | bool SetPollingMode(Common::Input::PollingMode polling_mode); | ||
| 318 | |||
| 312 | /// Returns the led pattern corresponding to this emulated controller | 319 | /// Returns the led pattern corresponding to this emulated controller |
| 313 | LedPattern GetLedPattern() const; | 320 | LedPattern GetLedPattern() const; |
| 314 | 321 | ||
diff --git a/src/core/hle/kernel/k_page_table.cpp b/src/core/hle/kernel/k_page_table.cpp index cbb0fedde..88aa2a152 100644 --- a/src/core/hle/kernel/k_page_table.cpp +++ b/src/core/hle/kernel/k_page_table.cpp | |||
| @@ -986,9 +986,8 @@ ResultCode KPageTable::UnmapPages(VAddr addr, const KPageLinkedList& page_linked | |||
| 986 | VAddr cur_addr{addr}; | 986 | VAddr cur_addr{addr}; |
| 987 | 987 | ||
| 988 | for (const auto& node : page_linked_list.Nodes()) { | 988 | for (const auto& node : page_linked_list.Nodes()) { |
| 989 | const std::size_t num_pages{(addr - cur_addr) / PageSize}; | 989 | if (const auto result{Operate(cur_addr, node.GetNumPages(), KMemoryPermission::None, |
| 990 | if (const auto result{ | 990 | OperationType::Unmap)}; |
| 991 | Operate(addr, num_pages, KMemoryPermission::None, OperationType::Unmap)}; | ||
| 992 | result.IsError()) { | 991 | result.IsError()) { |
| 993 | return result; | 992 | return result; |
| 994 | } | 993 | } |
diff --git a/src/core/hle/service/nfp/nfp.cpp b/src/core/hle/service/nfp/nfp.cpp index 761d0d3c6..513107715 100644 --- a/src/core/hle/service/nfp/nfp.cpp +++ b/src/core/hle/service/nfp/nfp.cpp | |||
| @@ -7,6 +7,9 @@ | |||
| 7 | 7 | ||
| 8 | #include "common/logging/log.h" | 8 | #include "common/logging/log.h" |
| 9 | #include "core/core.h" | 9 | #include "core/core.h" |
| 10 | #include "core/hid/emulated_controller.h" | ||
| 11 | #include "core/hid/hid_core.h" | ||
| 12 | #include "core/hid/hid_types.h" | ||
| 10 | #include "core/hle/ipc_helpers.h" | 13 | #include "core/hle/ipc_helpers.h" |
| 11 | #include "core/hle/kernel/k_event.h" | 14 | #include "core/hle/kernel/k_event.h" |
| 12 | #include "core/hle/service/nfp/nfp.h" | 15 | #include "core/hle/service/nfp/nfp.h" |
| @@ -14,343 +17,790 @@ | |||
| 14 | 17 | ||
| 15 | namespace Service::NFP { | 18 | namespace Service::NFP { |
| 16 | namespace ErrCodes { | 19 | namespace ErrCodes { |
| 17 | constexpr ResultCode ERR_NO_APPLICATION_AREA(ErrorModule::NFP, 152); | 20 | constexpr ResultCode DeviceNotFound(ErrorModule::NFP, 64); |
| 21 | constexpr ResultCode WrongDeviceState(ErrorModule::NFP, 73); | ||
| 22 | constexpr ResultCode ApplicationAreaIsNotInitialized(ErrorModule::NFP, 128); | ||
| 23 | constexpr ResultCode ApplicationAreaExist(ErrorModule::NFP, 168); | ||
| 18 | } // namespace ErrCodes | 24 | } // namespace ErrCodes |
| 19 | 25 | ||
| 20 | Module::Interface::Interface(std::shared_ptr<Module> module_, Core::System& system_, | 26 | constexpr u32 ApplicationAreaSize = 0xD8; |
| 21 | const char* name) | 27 | |
| 22 | : ServiceFramework{system_, name}, module{std::move(module_)}, service_context{system_, | 28 | IUser::IUser(Module::Interface& nfp_interface_, Core::System& system_) |
| 23 | "NFP::IUser"} { | 29 | : ServiceFramework{system_, "NFP::IUser"}, service_context{system_, service_name}, |
| 24 | nfc_tag_load = service_context.CreateEvent("NFP::IUser:NFCTagDetected"); | 30 | nfp_interface{nfp_interface_} { |
| 25 | } | 31 | static const FunctionInfo functions[] = { |
| 26 | 32 | {0, &IUser::Initialize, "Initialize"}, | |
| 27 | Module::Interface::~Interface() { | 33 | {1, &IUser::Finalize, "Finalize"}, |
| 28 | service_context.CloseEvent(nfc_tag_load); | 34 | {2, &IUser::ListDevices, "ListDevices"}, |
| 29 | } | 35 | {3, &IUser::StartDetection, "StartDetection"}, |
| 30 | 36 | {4, &IUser::StopDetection, "StopDetection"}, | |
| 31 | class IUser final : public ServiceFramework<IUser> { | 37 | {5, &IUser::Mount, "Mount"}, |
| 32 | public: | 38 | {6, &IUser::Unmount, "Unmount"}, |
| 33 | explicit IUser(Module::Interface& nfp_interface_, Core::System& system_, | 39 | {7, &IUser::OpenApplicationArea, "OpenApplicationArea"}, |
| 34 | KernelHelpers::ServiceContext& service_context_) | 40 | {8, &IUser::GetApplicationArea, "GetApplicationArea"}, |
| 35 | : ServiceFramework{system_, "NFP::IUser"}, nfp_interface{nfp_interface_}, | 41 | {9, &IUser::SetApplicationArea, "SetApplicationArea"}, |
| 36 | service_context{service_context_} { | 42 | {10, nullptr, "Flush"}, |
| 37 | static const FunctionInfo functions[] = { | 43 | {11, nullptr, "Restore"}, |
| 38 | {0, &IUser::Initialize, "Initialize"}, | 44 | {12, &IUser::CreateApplicationArea, "CreateApplicationArea"}, |
| 39 | {1, &IUser::Finalize, "Finalize"}, | 45 | {13, &IUser::GetTagInfo, "GetTagInfo"}, |
| 40 | {2, &IUser::ListDevices, "ListDevices"}, | 46 | {14, &IUser::GetRegisterInfo, "GetRegisterInfo"}, |
| 41 | {3, &IUser::StartDetection, "StartDetection"}, | 47 | {15, &IUser::GetCommonInfo, "GetCommonInfo"}, |
| 42 | {4, &IUser::StopDetection, "StopDetection"}, | 48 | {16, &IUser::GetModelInfo, "GetModelInfo"}, |
| 43 | {5, &IUser::Mount, "Mount"}, | 49 | {17, &IUser::AttachActivateEvent, "AttachActivateEvent"}, |
| 44 | {6, &IUser::Unmount, "Unmount"}, | 50 | {18, &IUser::AttachDeactivateEvent, "AttachDeactivateEvent"}, |
| 45 | {7, &IUser::OpenApplicationArea, "OpenApplicationArea"}, | 51 | {19, &IUser::GetState, "GetState"}, |
| 46 | {8, &IUser::GetApplicationArea, "GetApplicationArea"}, | 52 | {20, &IUser::GetDeviceState, "GetDeviceState"}, |
| 47 | {9, nullptr, "SetApplicationArea"}, | 53 | {21, &IUser::GetNpadId, "GetNpadId"}, |
| 48 | {10, nullptr, "Flush"}, | 54 | {22, &IUser::GetApplicationAreaSize, "GetApplicationAreaSize"}, |
| 49 | {11, nullptr, "Restore"}, | 55 | {23, &IUser::AttachAvailabilityChangeEvent, "AttachAvailabilityChangeEvent"}, |
| 50 | {12, nullptr, "CreateApplicationArea"}, | 56 | {24, nullptr, "RecreateApplicationArea"}, |
| 51 | {13, &IUser::GetTagInfo, "GetTagInfo"}, | 57 | }; |
| 52 | {14, &IUser::GetRegisterInfo, "GetRegisterInfo"}, | 58 | RegisterHandlers(functions); |
| 53 | {15, &IUser::GetCommonInfo, "GetCommonInfo"}, | ||
| 54 | {16, &IUser::GetModelInfo, "GetModelInfo"}, | ||
| 55 | {17, &IUser::AttachActivateEvent, "AttachActivateEvent"}, | ||
| 56 | {18, &IUser::AttachDeactivateEvent, "AttachDeactivateEvent"}, | ||
| 57 | {19, &IUser::GetState, "GetState"}, | ||
| 58 | {20, &IUser::GetDeviceState, "GetDeviceState"}, | ||
| 59 | {21, &IUser::GetNpadId, "GetNpadId"}, | ||
| 60 | {22, &IUser::GetApplicationAreaSize, "GetApplicationAreaSize"}, | ||
| 61 | {23, &IUser::AttachAvailabilityChangeEvent, "AttachAvailabilityChangeEvent"}, | ||
| 62 | {24, nullptr, "RecreateApplicationArea"}, | ||
| 63 | }; | ||
| 64 | RegisterHandlers(functions); | ||
| 65 | 59 | ||
| 66 | deactivate_event = service_context.CreateEvent("NFP::IUser:DeactivateEvent"); | 60 | availability_change_event = service_context.CreateEvent("IUser:AvailabilityChangeEvent"); |
| 67 | availability_change_event = | 61 | } |
| 68 | service_context.CreateEvent("NFP::IUser:AvailabilityChangeEvent"); | ||
| 69 | } | ||
| 70 | 62 | ||
| 71 | ~IUser() override { | 63 | void IUser::Initialize(Kernel::HLERequestContext& ctx) { |
| 72 | service_context.CloseEvent(deactivate_event); | 64 | LOG_INFO(Service_NFC, "called"); |
| 73 | service_context.CloseEvent(availability_change_event); | ||
| 74 | } | ||
| 75 | 65 | ||
| 76 | private: | 66 | state = State::Initialized; |
| 77 | struct TagInfo { | ||
| 78 | std::array<u8, 10> uuid; | ||
| 79 | u8 uuid_length; // TODO(ogniK): Figure out if this is actual the uuid length or does it | ||
| 80 | // mean something else | ||
| 81 | std::array<u8, 0x15> padding_1; | ||
| 82 | u32_le protocol; | ||
| 83 | u32_le tag_type; | ||
| 84 | std::array<u8, 0x2c> padding_2; | ||
| 85 | }; | ||
| 86 | static_assert(sizeof(TagInfo) == 0x54, "TagInfo is an invalid size"); | ||
| 87 | 67 | ||
| 88 | enum class State : u32 { | 68 | // TODO(german77): Loop through all interfaces |
| 89 | NonInitialized = 0, | 69 | nfp_interface.Initialize(); |
| 90 | Initialized = 1, | ||
| 91 | }; | ||
| 92 | 70 | ||
| 93 | enum class DeviceState : u32 { | 71 | IPC::ResponseBuilder rb{ctx, 2, 0}; |
| 94 | Initialized = 0, | 72 | rb.Push(ResultSuccess); |
| 95 | SearchingForTag = 1, | 73 | } |
| 96 | TagFound = 2, | ||
| 97 | TagRemoved = 3, | ||
| 98 | TagNearby = 4, | ||
| 99 | Unknown5 = 5, | ||
| 100 | Finalized = 6 | ||
| 101 | }; | ||
| 102 | 74 | ||
| 103 | struct CommonInfo { | 75 | void IUser::Finalize(Kernel::HLERequestContext& ctx) { |
| 104 | u16_be last_write_year; | 76 | LOG_INFO(Service_NFP, "called"); |
| 105 | u8 last_write_month; | ||
| 106 | u8 last_write_day; | ||
| 107 | u16_be write_counter; | ||
| 108 | u16_be version; | ||
| 109 | u32_be application_area_size; | ||
| 110 | INSERT_PADDING_BYTES(0x34); | ||
| 111 | }; | ||
| 112 | static_assert(sizeof(CommonInfo) == 0x40, "CommonInfo is an invalid size"); | ||
| 113 | 77 | ||
| 114 | void Initialize(Kernel::HLERequestContext& ctx) { | 78 | state = State::NonInitialized; |
| 115 | LOG_DEBUG(Service_NFC, "called"); | ||
| 116 | 79 | ||
| 117 | IPC::ResponseBuilder rb{ctx, 2, 0}; | 80 | // TODO(german77): Loop through all interfaces |
| 118 | rb.Push(ResultSuccess); | 81 | nfp_interface.Finalize(); |
| 119 | 82 | ||
| 120 | state = State::Initialized; | 83 | IPC::ResponseBuilder rb{ctx, 2}; |
| 121 | } | 84 | rb.Push(ResultSuccess); |
| 85 | } | ||
| 122 | 86 | ||
| 123 | void GetState(Kernel::HLERequestContext& ctx) { | 87 | void IUser::ListDevices(Kernel::HLERequestContext& ctx) { |
| 124 | LOG_DEBUG(Service_NFC, "called"); | 88 | LOG_INFO(Service_NFP, "called"); |
| 125 | 89 | ||
| 126 | IPC::ResponseBuilder rb{ctx, 3, 0}; | 90 | std::vector<u64> devices; |
| 127 | rb.Push(ResultSuccess); | 91 | |
| 128 | rb.PushRaw<u32>(static_cast<u32>(state)); | 92 | // TODO(german77): Loop through all interfaces |
| 93 | devices.push_back(nfp_interface.GetHandle()); | ||
| 94 | |||
| 95 | ctx.WriteBuffer(devices); | ||
| 96 | |||
| 97 | IPC::ResponseBuilder rb{ctx, 3}; | ||
| 98 | rb.Push(ResultSuccess); | ||
| 99 | rb.Push(static_cast<s32>(devices.size())); | ||
| 100 | } | ||
| 101 | |||
| 102 | void IUser::StartDetection(Kernel::HLERequestContext& ctx) { | ||
| 103 | IPC::RequestParser rp{ctx}; | ||
| 104 | const auto device_handle{rp.Pop<u64>()}; | ||
| 105 | const auto nfp_protocol{rp.Pop<s32>()}; | ||
| 106 | LOG_INFO(Service_NFP, "called, device_handle={}, nfp_protocol={}", device_handle, nfp_protocol); | ||
| 107 | |||
| 108 | // TODO(german77): Loop through all interfaces | ||
| 109 | if (device_handle == nfp_interface.GetHandle()) { | ||
| 110 | const auto result = nfp_interface.StartDetection(nfp_protocol); | ||
| 111 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 112 | rb.Push(result); | ||
| 113 | return; | ||
| 129 | } | 114 | } |
| 130 | 115 | ||
| 131 | void ListDevices(Kernel::HLERequestContext& ctx) { | 116 | LOG_ERROR(Service_NFP, "Handle not found, device_handle={}", device_handle); |
| 132 | IPC::RequestParser rp{ctx}; | ||
| 133 | const u32 array_size = rp.Pop<u32>(); | ||
| 134 | LOG_DEBUG(Service_NFP, "called, array_size={}", array_size); | ||
| 135 | 117 | ||
| 136 | ctx.WriteBuffer(device_handle); | 118 | IPC::ResponseBuilder rb{ctx, 2}; |
| 119 | rb.Push(ErrCodes::DeviceNotFound); | ||
| 120 | } | ||
| 137 | 121 | ||
| 138 | IPC::ResponseBuilder rb{ctx, 3}; | 122 | void IUser::StopDetection(Kernel::HLERequestContext& ctx) { |
| 139 | rb.Push(ResultSuccess); | 123 | IPC::RequestParser rp{ctx}; |
| 140 | rb.Push<u32>(1); | 124 | const auto device_handle{rp.Pop<u64>()}; |
| 125 | LOG_INFO(Service_NFP, "called, device_handle={}", device_handle); | ||
| 126 | |||
| 127 | // TODO(german77): Loop through all interfaces | ||
| 128 | if (device_handle == nfp_interface.GetHandle()) { | ||
| 129 | const auto result = nfp_interface.StopDetection(); | ||
| 130 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 131 | rb.Push(result); | ||
| 132 | return; | ||
| 141 | } | 133 | } |
| 142 | 134 | ||
| 143 | void GetNpadId(Kernel::HLERequestContext& ctx) { | 135 | LOG_ERROR(Service_NFP, "Handle not found, device_handle={}", device_handle); |
| 144 | IPC::RequestParser rp{ctx}; | ||
| 145 | const u64 dev_handle = rp.Pop<u64>(); | ||
| 146 | LOG_DEBUG(Service_NFP, "called, dev_handle=0x{:X}", dev_handle); | ||
| 147 | 136 | ||
| 148 | IPC::ResponseBuilder rb{ctx, 3}; | 137 | IPC::ResponseBuilder rb{ctx, 2}; |
| 149 | rb.Push(ResultSuccess); | 138 | rb.Push(ErrCodes::DeviceNotFound); |
| 150 | rb.Push<u32>(npad_id); | 139 | } |
| 140 | |||
| 141 | void IUser::Mount(Kernel::HLERequestContext& ctx) { | ||
| 142 | IPC::RequestParser rp{ctx}; | ||
| 143 | const auto device_handle{rp.Pop<u64>()}; | ||
| 144 | const auto model_type{rp.PopEnum<ModelType>()}; | ||
| 145 | const auto mount_target{rp.PopEnum<MountTarget>()}; | ||
| 146 | LOG_INFO(Service_NFP, "called, device_handle={}, model_type={}, mount_target={}", device_handle, | ||
| 147 | model_type, mount_target); | ||
| 148 | |||
| 149 | // TODO(german77): Loop through all interfaces | ||
| 150 | if (device_handle == nfp_interface.GetHandle()) { | ||
| 151 | const auto result = nfp_interface.Mount(); | ||
| 152 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 153 | rb.Push(result); | ||
| 154 | return; | ||
| 151 | } | 155 | } |
| 152 | 156 | ||
| 153 | void AttachActivateEvent(Kernel::HLERequestContext& ctx) { | 157 | LOG_ERROR(Service_NFP, "Handle not found, device_handle={}", device_handle); |
| 154 | IPC::RequestParser rp{ctx}; | ||
| 155 | const u64 dev_handle = rp.Pop<u64>(); | ||
| 156 | LOG_DEBUG(Service_NFP, "called, dev_handle=0x{:X}", dev_handle); | ||
| 157 | 158 | ||
| 158 | IPC::ResponseBuilder rb{ctx, 2, 1}; | 159 | IPC::ResponseBuilder rb{ctx, 2}; |
| 159 | rb.Push(ResultSuccess); | 160 | rb.Push(ErrCodes::DeviceNotFound); |
| 160 | rb.PushCopyObjects(nfp_interface.GetNFCEvent()); | 161 | } |
| 161 | has_attached_handle = true; | 162 | |
| 163 | void IUser::Unmount(Kernel::HLERequestContext& ctx) { | ||
| 164 | IPC::RequestParser rp{ctx}; | ||
| 165 | const auto device_handle{rp.Pop<u64>()}; | ||
| 166 | LOG_INFO(Service_NFP, "called, device_handle={}", device_handle); | ||
| 167 | |||
| 168 | // TODO(german77): Loop through all interfaces | ||
| 169 | if (device_handle == nfp_interface.GetHandle()) { | ||
| 170 | const auto result = nfp_interface.Unmount(); | ||
| 171 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 172 | rb.Push(result); | ||
| 173 | return; | ||
| 162 | } | 174 | } |
| 163 | 175 | ||
| 164 | void AttachDeactivateEvent(Kernel::HLERequestContext& ctx) { | 176 | LOG_ERROR(Service_NFP, "Handle not found, device_handle={}", device_handle); |
| 165 | IPC::RequestParser rp{ctx}; | ||
| 166 | const u64 dev_handle = rp.Pop<u64>(); | ||
| 167 | LOG_DEBUG(Service_NFP, "called, dev_handle=0x{:X}", dev_handle); | ||
| 168 | 177 | ||
| 169 | IPC::ResponseBuilder rb{ctx, 2, 1}; | 178 | IPC::ResponseBuilder rb{ctx, 2}; |
| 170 | rb.Push(ResultSuccess); | 179 | rb.Push(ErrCodes::DeviceNotFound); |
| 171 | rb.PushCopyObjects(deactivate_event->GetReadableEvent()); | 180 | } |
| 172 | } | 181 | |
| 173 | 182 | void IUser::OpenApplicationArea(Kernel::HLERequestContext& ctx) { | |
| 174 | void StopDetection(Kernel::HLERequestContext& ctx) { | 183 | IPC::RequestParser rp{ctx}; |
| 175 | LOG_DEBUG(Service_NFP, "called"); | 184 | const auto device_handle{rp.Pop<u64>()}; |
| 176 | 185 | const auto access_id{rp.Pop<u32>()}; | |
| 177 | switch (device_state) { | 186 | LOG_WARNING(Service_NFP, "(STUBBED) called, device_handle={}, access_id={}", device_handle, |
| 178 | case DeviceState::TagFound: | 187 | access_id); |
| 179 | case DeviceState::TagNearby: | 188 | |
| 180 | deactivate_event->GetWritableEvent().Signal(); | 189 | // TODO(german77): Loop through all interfaces |
| 181 | device_state = DeviceState::Initialized; | 190 | if (device_handle == nfp_interface.GetHandle()) { |
| 182 | break; | 191 | const auto result = nfp_interface.OpenApplicationArea(access_id); |
| 183 | case DeviceState::SearchingForTag: | ||
| 184 | case DeviceState::TagRemoved: | ||
| 185 | device_state = DeviceState::Initialized; | ||
| 186 | break; | ||
| 187 | default: | ||
| 188 | break; | ||
| 189 | } | ||
| 190 | IPC::ResponseBuilder rb{ctx, 2}; | 192 | IPC::ResponseBuilder rb{ctx, 2}; |
| 191 | rb.Push(ResultSuccess); | 193 | rb.Push(result); |
| 194 | return; | ||
| 192 | } | 195 | } |
| 193 | 196 | ||
| 194 | void GetDeviceState(Kernel::HLERequestContext& ctx) { | 197 | LOG_ERROR(Service_NFP, "Handle not found, device_handle={}", device_handle); |
| 195 | LOG_DEBUG(Service_NFP, "called"); | 198 | |
| 199 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 200 | rb.Push(ErrCodes::DeviceNotFound); | ||
| 201 | } | ||
| 202 | |||
| 203 | void IUser::GetApplicationArea(Kernel::HLERequestContext& ctx) { | ||
| 204 | IPC::RequestParser rp{ctx}; | ||
| 205 | const auto device_handle{rp.Pop<u64>()}; | ||
| 206 | LOG_INFO(Service_NFP, "called, device_handle={}", device_handle); | ||
| 196 | 207 | ||
| 208 | // TODO(german77): Loop through all interfaces | ||
| 209 | if (device_handle == nfp_interface.GetHandle()) { | ||
| 210 | std::vector<u8> data{}; | ||
| 211 | const auto result = nfp_interface.GetApplicationArea(data); | ||
| 212 | ctx.WriteBuffer(data); | ||
| 197 | IPC::ResponseBuilder rb{ctx, 3}; | 213 | IPC::ResponseBuilder rb{ctx, 3}; |
| 198 | rb.Push(ResultSuccess); | 214 | rb.Push(result); |
| 199 | rb.Push<u32>(static_cast<u32>(device_state)); | 215 | rb.Push(static_cast<u32>(data.size())); |
| 216 | return; | ||
| 200 | } | 217 | } |
| 201 | 218 | ||
| 202 | void StartDetection(Kernel::HLERequestContext& ctx) { | 219 | LOG_ERROR(Service_NFP, "Handle not found, device_handle={}", device_handle); |
| 203 | LOG_DEBUG(Service_NFP, "called"); | ||
| 204 | 220 | ||
| 205 | if (device_state == DeviceState::Initialized || device_state == DeviceState::TagRemoved) { | 221 | IPC::ResponseBuilder rb{ctx, 2}; |
| 206 | device_state = DeviceState::SearchingForTag; | 222 | rb.Push(ErrCodes::DeviceNotFound); |
| 207 | } | 223 | } |
| 224 | |||
| 225 | void IUser::SetApplicationArea(Kernel::HLERequestContext& ctx) { | ||
| 226 | IPC::RequestParser rp{ctx}; | ||
| 227 | const auto device_handle{rp.Pop<u64>()}; | ||
| 228 | const auto data{ctx.ReadBuffer()}; | ||
| 229 | LOG_WARNING(Service_NFP, "(STUBBED) called, device_handle={}, data_size={}", device_handle, | ||
| 230 | data.size()); | ||
| 231 | |||
| 232 | // TODO(german77): Loop through all interfaces | ||
| 233 | if (device_handle == nfp_interface.GetHandle()) { | ||
| 234 | const auto result = nfp_interface.SetApplicationArea(data); | ||
| 208 | IPC::ResponseBuilder rb{ctx, 2}; | 235 | IPC::ResponseBuilder rb{ctx, 2}; |
| 209 | rb.Push(ResultSuccess); | 236 | rb.Push(result); |
| 237 | return; | ||
| 210 | } | 238 | } |
| 211 | 239 | ||
| 212 | void GetTagInfo(Kernel::HLERequestContext& ctx) { | 240 | LOG_ERROR(Service_NFP, "Handle not found, device_handle={}", device_handle); |
| 213 | LOG_DEBUG(Service_NFP, "called"); | ||
| 214 | 241 | ||
| 242 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 243 | rb.Push(ErrCodes::DeviceNotFound); | ||
| 244 | } | ||
| 245 | |||
| 246 | void IUser::CreateApplicationArea(Kernel::HLERequestContext& ctx) { | ||
| 247 | IPC::RequestParser rp{ctx}; | ||
| 248 | const auto device_handle{rp.Pop<u64>()}; | ||
| 249 | const auto access_id{rp.Pop<u32>()}; | ||
| 250 | const auto data{ctx.ReadBuffer()}; | ||
| 251 | LOG_WARNING(Service_NFP, "(STUBBED) called, device_handle={}, data_size={}, access_id={}", | ||
| 252 | device_handle, access_id, data.size()); | ||
| 253 | |||
| 254 | // TODO(german77): Loop through all interfaces | ||
| 255 | if (device_handle == nfp_interface.GetHandle()) { | ||
| 256 | const auto result = nfp_interface.CreateApplicationArea(access_id, data); | ||
| 215 | IPC::ResponseBuilder rb{ctx, 2}; | 257 | IPC::ResponseBuilder rb{ctx, 2}; |
| 216 | const auto& amiibo = nfp_interface.GetAmiiboBuffer(); | 258 | rb.Push(result); |
| 217 | const TagInfo tag_info{ | 259 | return; |
| 218 | .uuid = amiibo.uuid, | ||
| 219 | .uuid_length = static_cast<u8>(amiibo.uuid.size()), | ||
| 220 | .padding_1 = {}, | ||
| 221 | .protocol = 1, // TODO(ogniK): Figure out actual values | ||
| 222 | .tag_type = 2, | ||
| 223 | .padding_2 = {}, | ||
| 224 | }; | ||
| 225 | ctx.WriteBuffer(tag_info); | ||
| 226 | rb.Push(ResultSuccess); | ||
| 227 | } | 260 | } |
| 228 | 261 | ||
| 229 | void Mount(Kernel::HLERequestContext& ctx) { | 262 | LOG_ERROR(Service_NFP, "Handle not found, device_handle={}", device_handle); |
| 230 | LOG_DEBUG(Service_NFP, "called"); | 263 | |
| 264 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 265 | rb.Push(ErrCodes::DeviceNotFound); | ||
| 266 | } | ||
| 267 | |||
| 268 | void IUser::GetTagInfo(Kernel::HLERequestContext& ctx) { | ||
| 269 | IPC::RequestParser rp{ctx}; | ||
| 270 | const auto device_handle{rp.Pop<u64>()}; | ||
| 271 | LOG_INFO(Service_NFP, "called, device_handle={}", device_handle); | ||
| 231 | 272 | ||
| 232 | device_state = DeviceState::TagNearby; | 273 | // TODO(german77): Loop through all interfaces |
| 274 | if (device_handle == nfp_interface.GetHandle()) { | ||
| 275 | TagInfo tag_info{}; | ||
| 276 | const auto result = nfp_interface.GetTagInfo(tag_info); | ||
| 277 | ctx.WriteBuffer(tag_info); | ||
| 233 | IPC::ResponseBuilder rb{ctx, 2}; | 278 | IPC::ResponseBuilder rb{ctx, 2}; |
| 234 | rb.Push(ResultSuccess); | 279 | rb.Push(result); |
| 280 | return; | ||
| 235 | } | 281 | } |
| 236 | 282 | ||
| 237 | void GetModelInfo(Kernel::HLERequestContext& ctx) { | 283 | LOG_ERROR(Service_NFP, "Handle not found, device_handle={}", device_handle); |
| 238 | LOG_DEBUG(Service_NFP, "called"); | ||
| 239 | 284 | ||
| 285 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 286 | rb.Push(ErrCodes::DeviceNotFound); | ||
| 287 | } | ||
| 288 | |||
| 289 | void IUser::GetRegisterInfo(Kernel::HLERequestContext& ctx) { | ||
| 290 | IPC::RequestParser rp{ctx}; | ||
| 291 | const auto device_handle{rp.Pop<u64>()}; | ||
| 292 | LOG_INFO(Service_NFP, "called, device_handle={}", device_handle); | ||
| 293 | |||
| 294 | // TODO(german77): Loop through all interfaces | ||
| 295 | if (device_handle == nfp_interface.GetHandle()) { | ||
| 296 | RegisterInfo register_info{}; | ||
| 297 | const auto result = nfp_interface.GetRegisterInfo(register_info); | ||
| 298 | ctx.WriteBuffer(register_info); | ||
| 240 | IPC::ResponseBuilder rb{ctx, 2}; | 299 | IPC::ResponseBuilder rb{ctx, 2}; |
| 241 | const auto& amiibo = nfp_interface.GetAmiiboBuffer(); | 300 | rb.Push(result); |
| 242 | ctx.WriteBuffer(amiibo.model_info); | 301 | return; |
| 243 | rb.Push(ResultSuccess); | ||
| 244 | } | 302 | } |
| 245 | 303 | ||
| 246 | void Unmount(Kernel::HLERequestContext& ctx) { | 304 | LOG_ERROR(Service_NFP, "Handle not found, device_handle={}", device_handle); |
| 247 | LOG_DEBUG(Service_NFP, "called"); | ||
| 248 | 305 | ||
| 249 | device_state = DeviceState::TagFound; | 306 | IPC::ResponseBuilder rb{ctx, 2}; |
| 307 | rb.Push(ErrCodes::DeviceNotFound); | ||
| 308 | } | ||
| 250 | 309 | ||
| 310 | void IUser::GetCommonInfo(Kernel::HLERequestContext& ctx) { | ||
| 311 | IPC::RequestParser rp{ctx}; | ||
| 312 | const auto device_handle{rp.Pop<u64>()}; | ||
| 313 | LOG_INFO(Service_NFP, "called, device_handle={}", device_handle); | ||
| 314 | |||
| 315 | // TODO(german77): Loop through all interfaces | ||
| 316 | if (device_handle == nfp_interface.GetHandle()) { | ||
| 317 | CommonInfo common_info{}; | ||
| 318 | const auto result = nfp_interface.GetCommonInfo(common_info); | ||
| 319 | ctx.WriteBuffer(common_info); | ||
| 251 | IPC::ResponseBuilder rb{ctx, 2}; | 320 | IPC::ResponseBuilder rb{ctx, 2}; |
| 252 | rb.Push(ResultSuccess); | 321 | rb.Push(result); |
| 322 | return; | ||
| 253 | } | 323 | } |
| 254 | 324 | ||
| 255 | void Finalize(Kernel::HLERequestContext& ctx) { | 325 | LOG_ERROR(Service_NFP, "Handle not found, device_handle={}", device_handle); |
| 256 | LOG_DEBUG(Service_NFP, "called"); | 326 | |
| 327 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 328 | rb.Push(ErrCodes::DeviceNotFound); | ||
| 329 | } | ||
| 257 | 330 | ||
| 258 | device_state = DeviceState::Finalized; | 331 | void IUser::GetModelInfo(Kernel::HLERequestContext& ctx) { |
| 332 | IPC::RequestParser rp{ctx}; | ||
| 333 | const auto device_handle{rp.Pop<u64>()}; | ||
| 334 | LOG_INFO(Service_NFP, "called, device_handle={}", device_handle); | ||
| 259 | 335 | ||
| 336 | // TODO(german77): Loop through all interfaces | ||
| 337 | if (device_handle == nfp_interface.GetHandle()) { | ||
| 338 | ModelInfo model_info{}; | ||
| 339 | const auto result = nfp_interface.GetModelInfo(model_info); | ||
| 340 | ctx.WriteBuffer(model_info); | ||
| 260 | IPC::ResponseBuilder rb{ctx, 2}; | 341 | IPC::ResponseBuilder rb{ctx, 2}; |
| 261 | rb.Push(ResultSuccess); | 342 | rb.Push(result); |
| 343 | return; | ||
| 262 | } | 344 | } |
| 263 | 345 | ||
| 264 | void AttachAvailabilityChangeEvent(Kernel::HLERequestContext& ctx) { | 346 | LOG_ERROR(Service_NFP, "Handle not found, device_handle={}", device_handle); |
| 265 | LOG_WARNING(Service_NFP, "(STUBBED) called"); | 347 | |
| 348 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 349 | rb.Push(ErrCodes::DeviceNotFound); | ||
| 350 | } | ||
| 266 | 351 | ||
| 352 | void IUser::AttachActivateEvent(Kernel::HLERequestContext& ctx) { | ||
| 353 | IPC::RequestParser rp{ctx}; | ||
| 354 | const auto device_handle{rp.Pop<u64>()}; | ||
| 355 | LOG_DEBUG(Service_NFP, "called, device_handle={}", device_handle); | ||
| 356 | |||
| 357 | // TODO(german77): Loop through all interfaces | ||
| 358 | if (device_handle == nfp_interface.GetHandle()) { | ||
| 267 | IPC::ResponseBuilder rb{ctx, 2, 1}; | 359 | IPC::ResponseBuilder rb{ctx, 2, 1}; |
| 268 | rb.Push(ResultSuccess); | 360 | rb.Push(ResultSuccess); |
| 269 | rb.PushCopyObjects(availability_change_event->GetReadableEvent()); | 361 | rb.PushCopyObjects(nfp_interface.GetActivateEvent()); |
| 362 | return; | ||
| 270 | } | 363 | } |
| 271 | 364 | ||
| 272 | void GetRegisterInfo(Kernel::HLERequestContext& ctx) { | 365 | LOG_ERROR(Service_NFP, "Handle not found, device_handle={}", device_handle); |
| 273 | LOG_WARNING(Service_NFP, "(STUBBED) called"); | ||
| 274 | 366 | ||
| 275 | // TODO(ogniK): Pull Mii and owner data from amiibo | 367 | IPC::ResponseBuilder rb{ctx, 2}; |
| 368 | rb.Push(ErrCodes::DeviceNotFound); | ||
| 369 | } | ||
| 276 | 370 | ||
| 277 | IPC::ResponseBuilder rb{ctx, 2}; | 371 | void IUser::AttachDeactivateEvent(Kernel::HLERequestContext& ctx) { |
| 372 | IPC::RequestParser rp{ctx}; | ||
| 373 | const auto device_handle{rp.Pop<u64>()}; | ||
| 374 | LOG_DEBUG(Service_NFP, "called, device_handle={}", device_handle); | ||
| 375 | |||
| 376 | // TODO(german77): Loop through all interfaces | ||
| 377 | if (device_handle == nfp_interface.GetHandle()) { | ||
| 378 | IPC::ResponseBuilder rb{ctx, 2, 1}; | ||
| 278 | rb.Push(ResultSuccess); | 379 | rb.Push(ResultSuccess); |
| 380 | rb.PushCopyObjects(nfp_interface.GetDeactivateEvent()); | ||
| 381 | return; | ||
| 279 | } | 382 | } |
| 280 | 383 | ||
| 281 | void GetCommonInfo(Kernel::HLERequestContext& ctx) { | 384 | LOG_ERROR(Service_NFP, "Handle not found, device_handle={}", device_handle); |
| 282 | LOG_WARNING(Service_NFP, "(STUBBED) called"); | ||
| 283 | 385 | ||
| 284 | // TODO(ogniK): Pull common information from amiibo | 386 | IPC::ResponseBuilder rb{ctx, 2}; |
| 387 | rb.Push(ErrCodes::DeviceNotFound); | ||
| 388 | } | ||
| 285 | 389 | ||
| 286 | CommonInfo common_info{}; | 390 | void IUser::GetState(Kernel::HLERequestContext& ctx) { |
| 287 | common_info.application_area_size = 0; | 391 | LOG_DEBUG(Service_NFC, "called"); |
| 288 | ctx.WriteBuffer(common_info); | ||
| 289 | 392 | ||
| 290 | IPC::ResponseBuilder rb{ctx, 2}; | 393 | IPC::ResponseBuilder rb{ctx, 3, 0}; |
| 394 | rb.Push(ResultSuccess); | ||
| 395 | rb.PushEnum(state); | ||
| 396 | } | ||
| 397 | |||
| 398 | void IUser::GetDeviceState(Kernel::HLERequestContext& ctx) { | ||
| 399 | IPC::RequestParser rp{ctx}; | ||
| 400 | const auto device_handle{rp.Pop<u64>()}; | ||
| 401 | LOG_DEBUG(Service_NFP, "called, device_handle={}", device_handle); | ||
| 402 | |||
| 403 | // TODO(german77): Loop through all interfaces | ||
| 404 | if (device_handle == nfp_interface.GetHandle()) { | ||
| 405 | IPC::ResponseBuilder rb{ctx, 3}; | ||
| 291 | rb.Push(ResultSuccess); | 406 | rb.Push(ResultSuccess); |
| 407 | rb.PushEnum(nfp_interface.GetCurrentState()); | ||
| 408 | return; | ||
| 292 | } | 409 | } |
| 293 | 410 | ||
| 294 | void OpenApplicationArea(Kernel::HLERequestContext& ctx) { | 411 | LOG_ERROR(Service_NFP, "Handle not found, device_handle={}", device_handle); |
| 295 | LOG_WARNING(Service_NFP, "(STUBBED) called"); | ||
| 296 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 297 | rb.Push(ErrCodes::ERR_NO_APPLICATION_AREA); | ||
| 298 | } | ||
| 299 | 412 | ||
| 300 | void GetApplicationAreaSize(Kernel::HLERequestContext& ctx) { | 413 | IPC::ResponseBuilder rb{ctx, 2}; |
| 301 | LOG_WARNING(Service_NFP, "(STUBBED) called"); | 414 | rb.Push(ErrCodes::DeviceNotFound); |
| 302 | // We don't need to worry about this since we can just open the file | 415 | } |
| 416 | |||
| 417 | void IUser::GetNpadId(Kernel::HLERequestContext& ctx) { | ||
| 418 | IPC::RequestParser rp{ctx}; | ||
| 419 | const auto device_handle{rp.Pop<u64>()}; | ||
| 420 | LOG_DEBUG(Service_NFP, "called, device_handle={}", device_handle); | ||
| 421 | |||
| 422 | // TODO(german77): Loop through all interfaces | ||
| 423 | if (device_handle == nfp_interface.GetHandle()) { | ||
| 303 | IPC::ResponseBuilder rb{ctx, 3}; | 424 | IPC::ResponseBuilder rb{ctx, 3}; |
| 304 | rb.Push(ResultSuccess); | 425 | rb.Push(ResultSuccess); |
| 305 | rb.PushRaw<u32>(0); // This is from the GetCommonInfo stub | 426 | rb.PushEnum(nfp_interface.GetNpadId()); |
| 427 | return; | ||
| 306 | } | 428 | } |
| 307 | 429 | ||
| 308 | void GetApplicationArea(Kernel::HLERequestContext& ctx) { | 430 | LOG_ERROR(Service_NFP, "Handle not found, device_handle={}", device_handle); |
| 309 | LOG_WARNING(Service_NFP, "(STUBBED) called"); | 431 | |
| 432 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 433 | rb.Push(ErrCodes::DeviceNotFound); | ||
| 434 | } | ||
| 310 | 435 | ||
| 311 | // TODO(ogniK): Pull application area from amiibo | 436 | void IUser::GetApplicationAreaSize(Kernel::HLERequestContext& ctx) { |
| 437 | IPC::RequestParser rp{ctx}; | ||
| 438 | const auto device_handle{rp.Pop<u64>()}; | ||
| 439 | LOG_DEBUG(Service_NFP, "called, device_handle={}", device_handle); | ||
| 312 | 440 | ||
| 441 | // TODO(german77): Loop through all interfaces | ||
| 442 | if (device_handle == nfp_interface.GetHandle()) { | ||
| 313 | IPC::ResponseBuilder rb{ctx, 3}; | 443 | IPC::ResponseBuilder rb{ctx, 3}; |
| 314 | rb.Push(ResultSuccess); | 444 | rb.Push(ResultSuccess); |
| 315 | rb.PushRaw<u32>(0); // This is from the GetCommonInfo stub | 445 | rb.Push(ApplicationAreaSize); |
| 446 | return; | ||
| 316 | } | 447 | } |
| 317 | 448 | ||
| 318 | Module::Interface& nfp_interface; | 449 | LOG_ERROR(Service_NFP, "Handle not found, device_handle={}", device_handle); |
| 319 | KernelHelpers::ServiceContext& service_context; | 450 | |
| 451 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 452 | rb.Push(ErrCodes::DeviceNotFound); | ||
| 453 | } | ||
| 454 | |||
| 455 | void IUser::AttachAvailabilityChangeEvent(Kernel::HLERequestContext& ctx) { | ||
| 456 | LOG_DEBUG(Service_NFP, "(STUBBED) called"); | ||
| 320 | 457 | ||
| 321 | bool has_attached_handle{}; | 458 | IPC::ResponseBuilder rb{ctx, 2, 1}; |
| 322 | const u64 device_handle{0}; // Npad device 1 | 459 | rb.Push(ResultSuccess); |
| 323 | const u32 npad_id{0}; // Player 1 controller | 460 | rb.PushCopyObjects(availability_change_event->GetReadableEvent()); |
| 324 | State state{State::NonInitialized}; | 461 | } |
| 325 | DeviceState device_state{DeviceState::Initialized}; | 462 | |
| 326 | Kernel::KEvent* deactivate_event; | 463 | Module::Interface::Interface(std::shared_ptr<Module> module_, Core::System& system_, |
| 327 | Kernel::KEvent* availability_change_event; | 464 | const char* name) |
| 328 | }; | 465 | : ServiceFramework{system_, name}, module{std::move(module_)}, |
| 466 | npad_id{Core::HID::NpadIdType::Player1}, service_context{system_, service_name} { | ||
| 467 | activate_event = service_context.CreateEvent("IUser:NFPActivateEvent"); | ||
| 468 | deactivate_event = service_context.CreateEvent("IUser:NFPDeactivateEvent"); | ||
| 469 | } | ||
| 470 | |||
| 471 | Module::Interface::~Interface() = default; | ||
| 329 | 472 | ||
| 330 | void Module::Interface::CreateUserInterface(Kernel::HLERequestContext& ctx) { | 473 | void Module::Interface::CreateUserInterface(Kernel::HLERequestContext& ctx) { |
| 331 | LOG_DEBUG(Service_NFP, "called"); | 474 | LOG_DEBUG(Service_NFP, "called"); |
| 332 | 475 | ||
| 333 | IPC::ResponseBuilder rb{ctx, 2, 0, 1}; | 476 | IPC::ResponseBuilder rb{ctx, 2, 0, 1}; |
| 334 | rb.Push(ResultSuccess); | 477 | rb.Push(ResultSuccess); |
| 335 | rb.PushIpcInterface<IUser>(*this, system, service_context); | 478 | rb.PushIpcInterface<IUser>(*this, system); |
| 336 | } | 479 | } |
| 337 | 480 | ||
| 338 | bool Module::Interface::LoadAmiibo(const std::vector<u8>& buffer) { | 481 | bool Module::Interface::LoadAmiibo(const std::vector<u8>& buffer) { |
| 339 | if (buffer.size() < sizeof(AmiiboFile)) { | 482 | if (device_state != DeviceState::SearchingForTag) { |
| 483 | LOG_ERROR(Service_NFP, "Game is not looking for amiibos, current state {}", device_state); | ||
| 484 | return false; | ||
| 485 | } | ||
| 486 | |||
| 487 | constexpr auto tag_size = sizeof(NTAG215File); | ||
| 488 | constexpr auto tag_size_without_password = sizeof(NTAG215File) - sizeof(NTAG215Password); | ||
| 489 | |||
| 490 | std::vector<u8> amiibo_buffer = buffer; | ||
| 491 | |||
| 492 | if (amiibo_buffer.size() < tag_size_without_password) { | ||
| 493 | LOG_ERROR(Service_NFP, "Wrong file size {}", buffer.size()); | ||
| 494 | return false; | ||
| 495 | } | ||
| 496 | |||
| 497 | // Ensure it has the correct size | ||
| 498 | if (amiibo_buffer.size() != tag_size) { | ||
| 499 | amiibo_buffer.resize(tag_size, 0); | ||
| 500 | } | ||
| 501 | |||
| 502 | LOG_INFO(Service_NFP, "Amiibo detected"); | ||
| 503 | std::memcpy(&tag_data, buffer.data(), tag_size); | ||
| 504 | |||
| 505 | if (!IsAmiiboValid()) { | ||
| 340 | return false; | 506 | return false; |
| 341 | } | 507 | } |
| 342 | 508 | ||
| 343 | std::memcpy(&amiibo, buffer.data(), sizeof(amiibo)); | 509 | // This value can't be dumped from a tag. Generate it |
| 344 | nfc_tag_load->GetWritableEvent().Signal(); | 510 | tag_data.password.PWD = GetTagPassword(tag_data.uuid); |
| 511 | |||
| 512 | device_state = DeviceState::TagFound; | ||
| 513 | activate_event->GetWritableEvent().Signal(); | ||
| 345 | return true; | 514 | return true; |
| 346 | } | 515 | } |
| 347 | 516 | ||
| 348 | Kernel::KReadableEvent& Module::Interface::GetNFCEvent() { | 517 | void Module::Interface::CloseAmiibo() { |
| 349 | return nfc_tag_load->GetReadableEvent(); | 518 | LOG_INFO(Service_NFP, "Remove amiibo"); |
| 519 | device_state = DeviceState::TagRemoved; | ||
| 520 | is_application_area_initialized = false; | ||
| 521 | application_area_id = 0; | ||
| 522 | application_area_data.clear(); | ||
| 523 | deactivate_event->GetWritableEvent().Signal(); | ||
| 524 | } | ||
| 525 | |||
| 526 | bool Module::Interface::IsAmiiboValid() const { | ||
| 527 | const auto& amiibo_data = tag_data.user_memory; | ||
| 528 | LOG_DEBUG(Service_NFP, "uuid_lock=0x{0:x}", tag_data.lock_bytes); | ||
| 529 | LOG_DEBUG(Service_NFP, "compability_container=0x{0:x}", tag_data.compability_container); | ||
| 530 | LOG_DEBUG(Service_NFP, "crypto_init=0x{0:x}", amiibo_data.crypto_init); | ||
| 531 | LOG_DEBUG(Service_NFP, "write_count={}", amiibo_data.write_count); | ||
| 532 | |||
| 533 | LOG_DEBUG(Service_NFP, "character_id=0x{0:x}", amiibo_data.model_info.character_id); | ||
| 534 | LOG_DEBUG(Service_NFP, "character_variant={}", amiibo_data.model_info.character_variant); | ||
| 535 | LOG_DEBUG(Service_NFP, "amiibo_type={}", amiibo_data.model_info.amiibo_type); | ||
| 536 | LOG_DEBUG(Service_NFP, "model_number=0x{0:x}", amiibo_data.model_info.model_number); | ||
| 537 | LOG_DEBUG(Service_NFP, "series={}", amiibo_data.model_info.series); | ||
| 538 | LOG_DEBUG(Service_NFP, "fixed_value=0x{0:x}", amiibo_data.model_info.fixed); | ||
| 539 | |||
| 540 | LOG_DEBUG(Service_NFP, "tag_dynamic_lock=0x{0:x}", tag_data.dynamic_lock); | ||
| 541 | LOG_DEBUG(Service_NFP, "tag_CFG0=0x{0:x}", tag_data.CFG0); | ||
| 542 | LOG_DEBUG(Service_NFP, "tag_CFG1=0x{0:x}", tag_data.CFG1); | ||
| 543 | |||
| 544 | // Check against all know constants on an amiibo binary | ||
| 545 | if (tag_data.lock_bytes != 0xE00F) { | ||
| 546 | return false; | ||
| 547 | } | ||
| 548 | if (tag_data.compability_container != 0xEEFF10F1U) { | ||
| 549 | return false; | ||
| 550 | } | ||
| 551 | if ((amiibo_data.crypto_init & 0xFF) != 0xA5) { | ||
| 552 | return false; | ||
| 553 | } | ||
| 554 | if (amiibo_data.model_info.fixed != 0x02) { | ||
| 555 | return false; | ||
| 556 | } | ||
| 557 | if ((tag_data.dynamic_lock & 0xFFFFFF) != 0x0F0001) { | ||
| 558 | return false; | ||
| 559 | } | ||
| 560 | if (tag_data.CFG0 != 0x04000000U) { | ||
| 561 | return false; | ||
| 562 | } | ||
| 563 | if (tag_data.CFG1 != 0x5F) { | ||
| 564 | return false; | ||
| 565 | } | ||
| 566 | return true; | ||
| 567 | } | ||
| 568 | |||
| 569 | Kernel::KReadableEvent& Module::Interface::GetActivateEvent() const { | ||
| 570 | return activate_event->GetReadableEvent(); | ||
| 571 | } | ||
| 572 | |||
| 573 | Kernel::KReadableEvent& Module::Interface::GetDeactivateEvent() const { | ||
| 574 | return deactivate_event->GetReadableEvent(); | ||
| 575 | } | ||
| 576 | |||
| 577 | void Module::Interface::Initialize() { | ||
| 578 | device_state = DeviceState::Initialized; | ||
| 579 | } | ||
| 580 | |||
| 581 | void Module::Interface::Finalize() { | ||
| 582 | device_state = DeviceState::Unaviable; | ||
| 583 | is_application_area_initialized = false; | ||
| 584 | application_area_id = 0; | ||
| 585 | application_area_data.clear(); | ||
| 586 | } | ||
| 587 | |||
| 588 | ResultCode Module::Interface::StartDetection(s32 protocol_) { | ||
| 589 | auto npad_device = system.HIDCore().GetEmulatedController(npad_id); | ||
| 590 | |||
| 591 | // TODO(german77): Add callback for when nfc data is available | ||
| 592 | |||
| 593 | if (device_state == DeviceState::Initialized || device_state == DeviceState::TagRemoved) { | ||
| 594 | npad_device->SetPollingMode(Common::Input::PollingMode::NFC); | ||
| 595 | device_state = DeviceState::SearchingForTag; | ||
| 596 | protocol = protocol_; | ||
| 597 | return ResultSuccess; | ||
| 598 | } | ||
| 599 | |||
| 600 | LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); | ||
| 601 | return ErrCodes::WrongDeviceState; | ||
| 602 | } | ||
| 603 | |||
| 604 | ResultCode Module::Interface::StopDetection() { | ||
| 605 | auto npad_device = system.HIDCore().GetEmulatedController(npad_id); | ||
| 606 | npad_device->SetPollingMode(Common::Input::PollingMode::Active); | ||
| 607 | |||
| 608 | if (device_state == DeviceState::TagFound || device_state == DeviceState::TagMounted) { | ||
| 609 | CloseAmiibo(); | ||
| 610 | return ResultSuccess; | ||
| 611 | } | ||
| 612 | if (device_state == DeviceState::SearchingForTag || device_state == DeviceState::TagRemoved) { | ||
| 613 | device_state = DeviceState::Initialized; | ||
| 614 | return ResultSuccess; | ||
| 615 | } | ||
| 616 | |||
| 617 | LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); | ||
| 618 | return ErrCodes::WrongDeviceState; | ||
| 619 | } | ||
| 620 | |||
| 621 | ResultCode Module::Interface::Mount() { | ||
| 622 | if (device_state == DeviceState::TagFound) { | ||
| 623 | device_state = DeviceState::TagMounted; | ||
| 624 | return ResultSuccess; | ||
| 625 | } | ||
| 626 | |||
| 627 | LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); | ||
| 628 | return ErrCodes::WrongDeviceState; | ||
| 629 | } | ||
| 630 | |||
| 631 | ResultCode Module::Interface::Unmount() { | ||
| 632 | if (device_state == DeviceState::TagMounted) { | ||
| 633 | is_application_area_initialized = false; | ||
| 634 | application_area_id = 0; | ||
| 635 | application_area_data.clear(); | ||
| 636 | device_state = DeviceState::TagFound; | ||
| 637 | return ResultSuccess; | ||
| 638 | } | ||
| 639 | |||
| 640 | LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); | ||
| 641 | return ErrCodes::WrongDeviceState; | ||
| 642 | } | ||
| 643 | |||
| 644 | ResultCode Module::Interface::GetTagInfo(TagInfo& tag_info) const { | ||
| 645 | if (device_state == DeviceState::TagFound || device_state == DeviceState::TagMounted) { | ||
| 646 | tag_info = { | ||
| 647 | .uuid = tag_data.uuid, | ||
| 648 | .uuid_length = static_cast<u8>(tag_data.uuid.size()), | ||
| 649 | .protocol = protocol, | ||
| 650 | .tag_type = static_cast<u32>(tag_data.user_memory.model_info.amiibo_type), | ||
| 651 | }; | ||
| 652 | return ResultSuccess; | ||
| 653 | } | ||
| 654 | |||
| 655 | LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); | ||
| 656 | return ErrCodes::WrongDeviceState; | ||
| 657 | } | ||
| 658 | |||
| 659 | ResultCode Module::Interface::GetCommonInfo(CommonInfo& common_info) const { | ||
| 660 | if (device_state != DeviceState::TagMounted) { | ||
| 661 | LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); | ||
| 662 | return ErrCodes::WrongDeviceState; | ||
| 663 | } | ||
| 664 | |||
| 665 | // Read this data from the amiibo save file | ||
| 666 | common_info = { | ||
| 667 | .last_write_year = 2022, | ||
| 668 | .last_write_month = 2, | ||
| 669 | .last_write_day = 7, | ||
| 670 | .write_counter = tag_data.user_memory.write_count, | ||
| 671 | .version = 1, | ||
| 672 | .application_area_size = ApplicationAreaSize, | ||
| 673 | }; | ||
| 674 | return ResultSuccess; | ||
| 675 | } | ||
| 676 | |||
| 677 | ResultCode Module::Interface::GetModelInfo(ModelInfo& model_info) const { | ||
| 678 | if (device_state != DeviceState::TagMounted) { | ||
| 679 | LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); | ||
| 680 | return ErrCodes::WrongDeviceState; | ||
| 681 | } | ||
| 682 | |||
| 683 | model_info = tag_data.user_memory.model_info; | ||
| 684 | return ResultSuccess; | ||
| 685 | } | ||
| 686 | |||
| 687 | ResultCode Module::Interface::GetRegisterInfo(RegisterInfo& register_info) const { | ||
| 688 | if (device_state != DeviceState::TagMounted) { | ||
| 689 | LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); | ||
| 690 | return ErrCodes::WrongDeviceState; | ||
| 691 | } | ||
| 692 | |||
| 693 | Service::Mii::MiiManager manager; | ||
| 694 | |||
| 695 | // Read this data from the amiibo save file | ||
| 696 | register_info = { | ||
| 697 | .mii_char_info = manager.BuildDefault(0), | ||
| 698 | .first_write_year = 2022, | ||
| 699 | .first_write_month = 2, | ||
| 700 | .first_write_day = 7, | ||
| 701 | .amiibo_name = {'Y', 'u', 'z', 'u', 'A', 'm', 'i', 'i', 'b', 'o', 0}, | ||
| 702 | .unknown = {}, | ||
| 703 | }; | ||
| 704 | return ResultSuccess; | ||
| 705 | } | ||
| 706 | |||
| 707 | ResultCode Module::Interface::OpenApplicationArea(u32 access_id) { | ||
| 708 | if (device_state != DeviceState::TagMounted) { | ||
| 709 | LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); | ||
| 710 | return ErrCodes::WrongDeviceState; | ||
| 711 | } | ||
| 712 | if (AmiiboApplicationDataExist(access_id)) { | ||
| 713 | application_area_data = LoadAmiiboApplicationData(access_id); | ||
| 714 | application_area_id = access_id; | ||
| 715 | is_application_area_initialized = true; | ||
| 716 | } | ||
| 717 | if (!is_application_area_initialized) { | ||
| 718 | LOG_WARNING(Service_NFP, "Application area is not initialized"); | ||
| 719 | return ErrCodes::ApplicationAreaIsNotInitialized; | ||
| 720 | } | ||
| 721 | return ResultSuccess; | ||
| 722 | } | ||
| 723 | |||
| 724 | ResultCode Module::Interface::GetApplicationArea(std::vector<u8>& data) const { | ||
| 725 | if (device_state != DeviceState::TagMounted) { | ||
| 726 | LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); | ||
| 727 | return ErrCodes::WrongDeviceState; | ||
| 728 | } | ||
| 729 | if (!is_application_area_initialized) { | ||
| 730 | LOG_ERROR(Service_NFP, "Application area is not initialized"); | ||
| 731 | return ErrCodes::ApplicationAreaIsNotInitialized; | ||
| 732 | } | ||
| 733 | |||
| 734 | data = application_area_data; | ||
| 735 | |||
| 736 | return ResultSuccess; | ||
| 737 | } | ||
| 738 | |||
| 739 | ResultCode Module::Interface::SetApplicationArea(const std::vector<u8>& data) { | ||
| 740 | if (device_state != DeviceState::TagMounted) { | ||
| 741 | LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); | ||
| 742 | return ErrCodes::WrongDeviceState; | ||
| 743 | } | ||
| 744 | if (!is_application_area_initialized) { | ||
| 745 | LOG_ERROR(Service_NFP, "Application area is not initialized"); | ||
| 746 | return ErrCodes::ApplicationAreaIsNotInitialized; | ||
| 747 | } | ||
| 748 | application_area_data = data; | ||
| 749 | SaveAmiiboApplicationData(application_area_id, application_area_data); | ||
| 750 | return ResultSuccess; | ||
| 751 | } | ||
| 752 | |||
| 753 | ResultCode Module::Interface::CreateApplicationArea(u32 access_id, const std::vector<u8>& data) { | ||
| 754 | if (device_state != DeviceState::TagMounted) { | ||
| 755 | LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); | ||
| 756 | return ErrCodes::WrongDeviceState; | ||
| 757 | } | ||
| 758 | if (AmiiboApplicationDataExist(access_id)) { | ||
| 759 | LOG_ERROR(Service_NFP, "Application area already exist"); | ||
| 760 | return ErrCodes::ApplicationAreaExist; | ||
| 761 | } | ||
| 762 | application_area_data = data; | ||
| 763 | application_area_id = access_id; | ||
| 764 | SaveAmiiboApplicationData(application_area_id, application_area_data); | ||
| 765 | return ResultSuccess; | ||
| 766 | } | ||
| 767 | |||
| 768 | bool Module::Interface::AmiiboApplicationDataExist(u32 access_id) const { | ||
| 769 | // TODO(german77): Check if file exist | ||
| 770 | return false; | ||
| 771 | } | ||
| 772 | |||
| 773 | std::vector<u8> Module::Interface::LoadAmiiboApplicationData(u32 access_id) const { | ||
| 774 | // TODO(german77): Read file | ||
| 775 | std::vector<u8> data(ApplicationAreaSize); | ||
| 776 | return data; | ||
| 777 | } | ||
| 778 | |||
| 779 | void Module::Interface::SaveAmiiboApplicationData(u32 access_id, | ||
| 780 | const std::vector<u8>& data) const { | ||
| 781 | // TODO(german77): Save file | ||
| 782 | } | ||
| 783 | |||
| 784 | u64 Module::Interface::GetHandle() const { | ||
| 785 | // Generate a handle based of the npad id | ||
| 786 | return static_cast<u64>(npad_id); | ||
| 787 | } | ||
| 788 | |||
| 789 | DeviceState Module::Interface::GetCurrentState() const { | ||
| 790 | return device_state; | ||
| 791 | } | ||
| 792 | |||
| 793 | Core::HID::NpadIdType Module::Interface::GetNpadId() const { | ||
| 794 | return npad_id; | ||
| 350 | } | 795 | } |
| 351 | 796 | ||
| 352 | const Module::Interface::AmiiboFile& Module::Interface::GetAmiiboBuffer() const { | 797 | u32 Module::Interface::GetTagPassword(const TagUuid& uuid) const { |
| 353 | return amiibo; | 798 | // Verifiy that the generated password is correct |
| 799 | u32 password = 0xAA ^ (uuid[1] ^ uuid[3]); | ||
| 800 | password &= (0x55 ^ (uuid[2] ^ uuid[4])) << 8; | ||
| 801 | password &= (0xAA ^ (uuid[3] ^ uuid[5])) << 16; | ||
| 802 | password &= (0x55 ^ (uuid[4] ^ uuid[6])) << 24; | ||
| 803 | return password; | ||
| 354 | } | 804 | } |
| 355 | 805 | ||
| 356 | void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system) { | 806 | void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system) { |
diff --git a/src/core/hle/service/nfp/nfp.h b/src/core/hle/service/nfp/nfp.h index 95c127efb..022f13b29 100644 --- a/src/core/hle/service/nfp/nfp.h +++ b/src/core/hle/service/nfp/nfp.h | |||
| @@ -7,15 +7,132 @@ | |||
| 7 | #include <array> | 7 | #include <array> |
| 8 | #include <vector> | 8 | #include <vector> |
| 9 | 9 | ||
| 10 | #include "common/common_funcs.h" | ||
| 10 | #include "core/hle/service/kernel_helpers.h" | 11 | #include "core/hle/service/kernel_helpers.h" |
| 12 | #include "core/hle/service/mii/mii_manager.h" | ||
| 11 | #include "core/hle/service/service.h" | 13 | #include "core/hle/service/service.h" |
| 12 | 14 | ||
| 13 | namespace Kernel { | 15 | namespace Kernel { |
| 14 | class KEvent; | 16 | class KEvent; |
| 15 | } | 17 | class KReadableEvent; |
| 18 | } // namespace Kernel | ||
| 19 | |||
| 20 | namespace Core::HID { | ||
| 21 | enum class NpadIdType : u32; | ||
| 22 | } // namespace Core::HID | ||
| 16 | 23 | ||
| 17 | namespace Service::NFP { | 24 | namespace Service::NFP { |
| 18 | 25 | ||
| 26 | enum class ServiceType : u32 { | ||
| 27 | User, | ||
| 28 | Debug, | ||
| 29 | System, | ||
| 30 | }; | ||
| 31 | |||
| 32 | enum class State : u32 { | ||
| 33 | NonInitialized, | ||
| 34 | Initialized, | ||
| 35 | }; | ||
| 36 | |||
| 37 | enum class DeviceState : u32 { | ||
| 38 | Initialized, | ||
| 39 | SearchingForTag, | ||
| 40 | TagFound, | ||
| 41 | TagRemoved, | ||
| 42 | TagMounted, | ||
| 43 | Unaviable, | ||
| 44 | Finalized, | ||
| 45 | }; | ||
| 46 | |||
| 47 | enum class ModelType : u32 { | ||
| 48 | Amiibo, | ||
| 49 | }; | ||
| 50 | |||
| 51 | enum class MountTarget : u32 { | ||
| 52 | Rom, | ||
| 53 | Ram, | ||
| 54 | All, | ||
| 55 | }; | ||
| 56 | |||
| 57 | enum class AmiiboType : u8 { | ||
| 58 | Figure, | ||
| 59 | Card, | ||
| 60 | Yarn, | ||
| 61 | }; | ||
| 62 | |||
| 63 | enum class AmiiboSeries : u8 { | ||
| 64 | SuperSmashBros, | ||
| 65 | SuperMario, | ||
| 66 | ChibiRobo, | ||
| 67 | YoshiWoollyWorld, | ||
| 68 | Splatoon, | ||
| 69 | AnimalCrossing, | ||
| 70 | EightBitMario, | ||
| 71 | Skylanders, | ||
| 72 | Unknown8, | ||
| 73 | TheLegendOfZelda, | ||
| 74 | ShovelKnight, | ||
| 75 | Unknown11, | ||
| 76 | Kiby, | ||
| 77 | Pokemon, | ||
| 78 | MarioSportsSuperstars, | ||
| 79 | MonsterHunter, | ||
| 80 | BoxBoy, | ||
| 81 | Pikmin, | ||
| 82 | FireEmblem, | ||
| 83 | Metroid, | ||
| 84 | Others, | ||
| 85 | MegaMan, | ||
| 86 | Diablo | ||
| 87 | }; | ||
| 88 | |||
| 89 | using TagUuid = std::array<u8, 10>; | ||
| 90 | |||
| 91 | struct TagInfo { | ||
| 92 | TagUuid uuid; | ||
| 93 | u8 uuid_length; | ||
| 94 | INSERT_PADDING_BYTES(0x15); | ||
| 95 | s32 protocol; | ||
| 96 | u32 tag_type; | ||
| 97 | INSERT_PADDING_BYTES(0x30); | ||
| 98 | }; | ||
| 99 | static_assert(sizeof(TagInfo) == 0x58, "TagInfo is an invalid size"); | ||
| 100 | |||
| 101 | struct CommonInfo { | ||
| 102 | u16 last_write_year; | ||
| 103 | u8 last_write_month; | ||
| 104 | u8 last_write_day; | ||
| 105 | u16 write_counter; | ||
| 106 | u16 version; | ||
| 107 | u32 application_area_size; | ||
| 108 | INSERT_PADDING_BYTES(0x34); | ||
| 109 | }; | ||
| 110 | static_assert(sizeof(CommonInfo) == 0x40, "CommonInfo is an invalid size"); | ||
| 111 | |||
| 112 | struct ModelInfo { | ||
| 113 | u16 character_id; | ||
| 114 | u8 character_variant; | ||
| 115 | AmiiboType amiibo_type; | ||
| 116 | u16 model_number; | ||
| 117 | AmiiboSeries series; | ||
| 118 | u8 fixed; // Must be 02 | ||
| 119 | INSERT_PADDING_BYTES(0x4); // Unknown | ||
| 120 | INSERT_PADDING_BYTES(0x20); // Probably a SHA256-(HMAC?) hash | ||
| 121 | INSERT_PADDING_BYTES(0x14); // SHA256-HMAC | ||
| 122 | }; | ||
| 123 | static_assert(sizeof(ModelInfo) == 0x40, "ModelInfo is an invalid size"); | ||
| 124 | |||
| 125 | struct RegisterInfo { | ||
| 126 | Service::Mii::MiiInfo mii_char_info; | ||
| 127 | u16 first_write_year; | ||
| 128 | u8 first_write_month; | ||
| 129 | u8 first_write_day; | ||
| 130 | std::array<u8, 11> amiibo_name; | ||
| 131 | u8 unknown; | ||
| 132 | INSERT_PADDING_BYTES(0x98); | ||
| 133 | }; | ||
| 134 | static_assert(sizeof(RegisterInfo) == 0x100, "RegisterInfo is an invalid size"); | ||
| 135 | |||
| 19 | class Module final { | 136 | class Module final { |
| 20 | public: | 137 | public: |
| 21 | class Interface : public ServiceFramework<Interface> { | 138 | class Interface : public ServiceFramework<Interface> { |
| @@ -24,34 +141,131 @@ public: | |||
| 24 | const char* name); | 141 | const char* name); |
| 25 | ~Interface() override; | 142 | ~Interface() override; |
| 26 | 143 | ||
| 27 | struct ModelInfo { | 144 | struct EncryptedAmiiboFile { |
| 28 | std::array<u8, 0x8> amiibo_identification_block; | 145 | u16 crypto_init; // Must be A5 XX |
| 29 | INSERT_PADDING_BYTES(0x38); | 146 | u16 write_count; // Number of times the amiibo has been written? |
| 147 | INSERT_PADDING_BYTES(0x20); // System crypts | ||
| 148 | INSERT_PADDING_BYTES(0x20); // SHA256-(HMAC?) hash | ||
| 149 | ModelInfo model_info; // This struct is bigger than documentation | ||
| 150 | INSERT_PADDING_BYTES(0xC); // SHA256-HMAC | ||
| 151 | INSERT_PADDING_BYTES(0x114); // section 1 encrypted buffer | ||
| 152 | INSERT_PADDING_BYTES(0x54); // section 2 encrypted buffer | ||
| 153 | }; | ||
| 154 | static_assert(sizeof(EncryptedAmiiboFile) == 0x1F8, "AmiiboFile is an invalid size"); | ||
| 155 | |||
| 156 | struct NTAG215Password { | ||
| 157 | u32 PWD; // Password to allow write access | ||
| 158 | u16 PACK; // Password acknowledge reply | ||
| 159 | u16 RFUI; // Reserved for future use | ||
| 30 | }; | 160 | }; |
| 31 | static_assert(sizeof(ModelInfo) == 0x40, "ModelInfo is an invalid size"); | 161 | static_assert(sizeof(NTAG215Password) == 0x8, "NTAG215Password is an invalid size"); |
| 32 | 162 | ||
| 33 | struct AmiiboFile { | 163 | struct NTAG215File { |
| 34 | std::array<u8, 10> uuid; | 164 | TagUuid uuid; // Unique serial number |
| 35 | INSERT_PADDING_BYTES(0x4a); | 165 | u16 lock_bytes; // Set defined pages as read only |
| 36 | ModelInfo model_info; | 166 | u32 compability_container; // Defines available memory |
| 167 | EncryptedAmiiboFile user_memory; // Writable data | ||
| 168 | u32 dynamic_lock; // Dynamic lock | ||
| 169 | u32 CFG0; // Defines memory protected by password | ||
| 170 | u32 CFG1; // Defines number of verification attempts | ||
| 171 | NTAG215Password password; // Password data | ||
| 37 | }; | 172 | }; |
| 38 | static_assert(sizeof(AmiiboFile) == 0x94, "AmiiboFile is an invalid size"); | 173 | static_assert(sizeof(NTAG215File) == 0x21C, "NTAG215File is an invalid size"); |
| 39 | 174 | ||
| 40 | void CreateUserInterface(Kernel::HLERequestContext& ctx); | 175 | void CreateUserInterface(Kernel::HLERequestContext& ctx); |
| 41 | bool LoadAmiibo(const std::vector<u8>& buffer); | 176 | bool LoadAmiibo(const std::vector<u8>& buffer); |
| 42 | Kernel::KReadableEvent& GetNFCEvent(); | 177 | void CloseAmiibo(); |
| 43 | const AmiiboFile& GetAmiiboBuffer() const; | 178 | |
| 179 | void Initialize(); | ||
| 180 | void Finalize(); | ||
| 181 | |||
| 182 | ResultCode StartDetection(s32 protocol_); | ||
| 183 | ResultCode StopDetection(); | ||
| 184 | ResultCode Mount(); | ||
| 185 | ResultCode Unmount(); | ||
| 186 | |||
| 187 | ResultCode GetTagInfo(TagInfo& tag_info) const; | ||
| 188 | ResultCode GetCommonInfo(CommonInfo& common_info) const; | ||
| 189 | ResultCode GetModelInfo(ModelInfo& model_info) const; | ||
| 190 | ResultCode GetRegisterInfo(RegisterInfo& register_info) const; | ||
| 191 | |||
| 192 | ResultCode OpenApplicationArea(u32 access_id); | ||
| 193 | ResultCode GetApplicationArea(std::vector<u8>& data) const; | ||
| 194 | ResultCode SetApplicationArea(const std::vector<u8>& data); | ||
| 195 | ResultCode CreateApplicationArea(u32 access_id, const std::vector<u8>& data); | ||
| 196 | |||
| 197 | u64 GetHandle() const; | ||
| 198 | DeviceState GetCurrentState() const; | ||
| 199 | Core::HID::NpadIdType GetNpadId() const; | ||
| 200 | |||
| 201 | Kernel::KReadableEvent& GetActivateEvent() const; | ||
| 202 | Kernel::KReadableEvent& GetDeactivateEvent() const; | ||
| 44 | 203 | ||
| 45 | protected: | 204 | protected: |
| 46 | std::shared_ptr<Module> module; | 205 | std::shared_ptr<Module> module; |
| 47 | 206 | ||
| 48 | private: | 207 | private: |
| 208 | /// Validates that the amiibo file is not corrupted | ||
| 209 | bool IsAmiiboValid() const; | ||
| 210 | |||
| 211 | bool AmiiboApplicationDataExist(u32 access_id) const; | ||
| 212 | std::vector<u8> LoadAmiiboApplicationData(u32 access_id) const; | ||
| 213 | void SaveAmiiboApplicationData(u32 access_id, const std::vector<u8>& data) const; | ||
| 214 | |||
| 215 | /// return password needed to allow write access to protected memory | ||
| 216 | u32 GetTagPassword(const TagUuid& uuid) const; | ||
| 217 | |||
| 218 | const Core::HID::NpadIdType npad_id; | ||
| 219 | |||
| 220 | DeviceState device_state{DeviceState::Unaviable}; | ||
| 49 | KernelHelpers::ServiceContext service_context; | 221 | KernelHelpers::ServiceContext service_context; |
| 50 | Kernel::KEvent* nfc_tag_load; | 222 | Kernel::KEvent* activate_event; |
| 51 | AmiiboFile amiibo{}; | 223 | Kernel::KEvent* deactivate_event; |
| 224 | NTAG215File tag_data{}; | ||
| 225 | s32 protocol; | ||
| 226 | bool is_application_area_initialized{}; | ||
| 227 | u32 application_area_id; | ||
| 228 | std::vector<u8> application_area_data; | ||
| 52 | }; | 229 | }; |
| 53 | }; | 230 | }; |
| 54 | 231 | ||
| 232 | class IUser final : public ServiceFramework<IUser> { | ||
| 233 | public: | ||
| 234 | explicit IUser(Module::Interface& nfp_interface_, Core::System& system_); | ||
| 235 | |||
| 236 | private: | ||
| 237 | void Initialize(Kernel::HLERequestContext& ctx); | ||
| 238 | void Finalize(Kernel::HLERequestContext& ctx); | ||
| 239 | void ListDevices(Kernel::HLERequestContext& ctx); | ||
| 240 | void StartDetection(Kernel::HLERequestContext& ctx); | ||
| 241 | void StopDetection(Kernel::HLERequestContext& ctx); | ||
| 242 | void Mount(Kernel::HLERequestContext& ctx); | ||
| 243 | void Unmount(Kernel::HLERequestContext& ctx); | ||
| 244 | void OpenApplicationArea(Kernel::HLERequestContext& ctx); | ||
| 245 | void GetApplicationArea(Kernel::HLERequestContext& ctx); | ||
| 246 | void SetApplicationArea(Kernel::HLERequestContext& ctx); | ||
| 247 | void CreateApplicationArea(Kernel::HLERequestContext& ctx); | ||
| 248 | void GetTagInfo(Kernel::HLERequestContext& ctx); | ||
| 249 | void GetRegisterInfo(Kernel::HLERequestContext& ctx); | ||
| 250 | void GetCommonInfo(Kernel::HLERequestContext& ctx); | ||
| 251 | void GetModelInfo(Kernel::HLERequestContext& ctx); | ||
| 252 | void AttachActivateEvent(Kernel::HLERequestContext& ctx); | ||
| 253 | void AttachDeactivateEvent(Kernel::HLERequestContext& ctx); | ||
| 254 | void GetState(Kernel::HLERequestContext& ctx); | ||
| 255 | void GetDeviceState(Kernel::HLERequestContext& ctx); | ||
| 256 | void GetNpadId(Kernel::HLERequestContext& ctx); | ||
| 257 | void GetApplicationAreaSize(Kernel::HLERequestContext& ctx); | ||
| 258 | void AttachAvailabilityChangeEvent(Kernel::HLERequestContext& ctx); | ||
| 259 | |||
| 260 | KernelHelpers::ServiceContext service_context; | ||
| 261 | |||
| 262 | // TODO(german77): We should have a vector of interfaces | ||
| 263 | Module::Interface& nfp_interface; | ||
| 264 | |||
| 265 | State state{State::NonInitialized}; | ||
| 266 | Kernel::KEvent* availability_change_event; | ||
| 267 | }; | ||
| 268 | |||
| 55 | void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system); | 269 | void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system); |
| 56 | 270 | ||
| 57 | } // namespace Service::NFP | 271 | } // namespace Service::NFP |
diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp index 9ee7992e7..d5ba86c03 100644 --- a/src/yuzu/configuration/config.cpp +++ b/src/yuzu/configuration/config.cpp | |||
| @@ -77,13 +77,13 @@ const std::array<UISettings::Shortcut, 22> Config::default_hotkeys{{ | |||
| 77 | {QStringLiteral("Exit Fullscreen"), QStringLiteral("Main Window"), {QStringLiteral("Esc"), QStringLiteral(""), Qt::WindowShortcut}}, | 77 | {QStringLiteral("Exit Fullscreen"), QStringLiteral("Main Window"), {QStringLiteral("Esc"), QStringLiteral(""), Qt::WindowShortcut}}, |
| 78 | {QStringLiteral("Exit yuzu"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+Q"), QStringLiteral("Home+Minus"), Qt::WindowShortcut}}, | 78 | {QStringLiteral("Exit yuzu"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+Q"), QStringLiteral("Home+Minus"), Qt::WindowShortcut}}, |
| 79 | {QStringLiteral("Fullscreen"), QStringLiteral("Main Window"), {QStringLiteral("F11"), QStringLiteral("Home+B"), Qt::WindowShortcut}}, | 79 | {QStringLiteral("Fullscreen"), QStringLiteral("Main Window"), {QStringLiteral("F11"), QStringLiteral("Home+B"), Qt::WindowShortcut}}, |
| 80 | {QStringLiteral("Load Amiibo"), QStringLiteral("Main Window"), {QStringLiteral("F2"), QStringLiteral("Home+A"), Qt::WidgetWithChildrenShortcut}}, | ||
| 81 | {QStringLiteral("Load File"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+O"), QStringLiteral(""), Qt::WidgetWithChildrenShortcut}}, | 80 | {QStringLiteral("Load File"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+O"), QStringLiteral(""), Qt::WidgetWithChildrenShortcut}}, |
| 81 | {QStringLiteral("Load/Remove Amiibo"), QStringLiteral("Main Window"), {QStringLiteral("F2"), QStringLiteral("Home+A"), Qt::WidgetWithChildrenShortcut}}, | ||
| 82 | {QStringLiteral("Restart Emulation"), QStringLiteral("Main Window"), {QStringLiteral("F6"), QStringLiteral(""), Qt::WindowShortcut}}, | 82 | {QStringLiteral("Restart Emulation"), QStringLiteral("Main Window"), {QStringLiteral("F6"), QStringLiteral(""), Qt::WindowShortcut}}, |
| 83 | {QStringLiteral("Stop Emulation"), QStringLiteral("Main Window"), {QStringLiteral("F5"), QStringLiteral(""), Qt::WindowShortcut}}, | 83 | {QStringLiteral("Stop Emulation"), QStringLiteral("Main Window"), {QStringLiteral("F5"), QStringLiteral(""), Qt::WindowShortcut}}, |
| 84 | {QStringLiteral("TAS Start/Stop"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+F5"), QStringLiteral(""), Qt::ApplicationShortcut}}, | ||
| 85 | {QStringLiteral("TAS Reset"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+F6"), QStringLiteral(""), Qt::ApplicationShortcut}}, | ||
| 86 | {QStringLiteral("TAS Record"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+F7"), QStringLiteral(""), Qt::ApplicationShortcut}}, | 84 | {QStringLiteral("TAS Record"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+F7"), QStringLiteral(""), Qt::ApplicationShortcut}}, |
| 85 | {QStringLiteral("TAS Reset"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+F6"), QStringLiteral(""), Qt::ApplicationShortcut}}, | ||
| 86 | {QStringLiteral("TAS Start/Stop"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+F5"), QStringLiteral(""), Qt::ApplicationShortcut}}, | ||
| 87 | {QStringLiteral("Toggle Filter Bar"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+F"), QStringLiteral(""), Qt::WindowShortcut}}, | 87 | {QStringLiteral("Toggle Filter Bar"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+F"), QStringLiteral(""), Qt::WindowShortcut}}, |
| 88 | {QStringLiteral("Toggle Framerate Limit"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+U"), QStringLiteral("Home+Y"), Qt::ApplicationShortcut}}, | 88 | {QStringLiteral("Toggle Framerate Limit"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+U"), QStringLiteral("Home+Y"), Qt::ApplicationShortcut}}, |
| 89 | {QStringLiteral("Toggle Mouse Panning"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+F9"), QStringLiteral(""), Qt::ApplicationShortcut}}, | 89 | {QStringLiteral("Toggle Mouse Panning"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+F9"), QStringLiteral(""), Qt::ApplicationShortcut}}, |
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index 3c2d7d080..e3fd38a02 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp | |||
| @@ -934,7 +934,7 @@ void GMainWindow::InitializeHotkeys() { | |||
| 934 | hotkey_registry.LoadHotkeys(); | 934 | hotkey_registry.LoadHotkeys(); |
| 935 | 935 | ||
| 936 | LinkActionShortcut(ui->action_Load_File, QStringLiteral("Load File")); | 936 | LinkActionShortcut(ui->action_Load_File, QStringLiteral("Load File")); |
| 937 | LinkActionShortcut(ui->action_Load_Amiibo, QStringLiteral("Load Amiibo")); | 937 | LinkActionShortcut(ui->action_Load_Amiibo, QStringLiteral("Load/Remove Amiibo")); |
| 938 | LinkActionShortcut(ui->action_Exit, QStringLiteral("Exit yuzu")); | 938 | LinkActionShortcut(ui->action_Exit, QStringLiteral("Exit yuzu")); |
| 939 | LinkActionShortcut(ui->action_Restart, QStringLiteral("Restart Emulation")); | 939 | LinkActionShortcut(ui->action_Restart, QStringLiteral("Restart Emulation")); |
| 940 | LinkActionShortcut(ui->action_Pause, QStringLiteral("Continue/Pause Emulation")); | 940 | LinkActionShortcut(ui->action_Pause, QStringLiteral("Continue/Pause Emulation")); |
| @@ -2927,6 +2927,24 @@ void GMainWindow::OnLoadAmiibo() { | |||
| 2927 | return; | 2927 | return; |
| 2928 | } | 2928 | } |
| 2929 | 2929 | ||
| 2930 | Service::SM::ServiceManager& sm = system->ServiceManager(); | ||
| 2931 | auto nfc = sm.GetService<Service::NFP::Module::Interface>("nfp:user"); | ||
| 2932 | if (nfc == nullptr) { | ||
| 2933 | QMessageBox::warning(this, tr("Error"), tr("The current game is not looking for amiibos")); | ||
| 2934 | return; | ||
| 2935 | } | ||
| 2936 | const auto nfc_state = nfc->GetCurrentState(); | ||
| 2937 | if (nfc_state == Service::NFP::DeviceState::TagFound || | ||
| 2938 | nfc_state == Service::NFP::DeviceState::TagMounted) { | ||
| 2939 | nfc->CloseAmiibo(); | ||
| 2940 | return; | ||
| 2941 | } | ||
| 2942 | |||
| 2943 | if (nfc_state != Service::NFP::DeviceState::SearchingForTag) { | ||
| 2944 | QMessageBox::warning(this, tr("Error"), tr("The current game is not looking for amiibos")); | ||
| 2945 | return; | ||
| 2946 | } | ||
| 2947 | |||
| 2930 | is_amiibo_file_select_active = true; | 2948 | is_amiibo_file_select_active = true; |
| 2931 | const QString extensions{QStringLiteral("*.bin")}; | 2949 | const QString extensions{QStringLiteral("*.bin")}; |
| 2932 | const QString file_filter = tr("Amiibo File (%1);; All Files (*.*)").arg(extensions); | 2950 | const QString file_filter = tr("Amiibo File (%1);; All Files (*.*)").arg(extensions); |
diff --git a/src/yuzu/main.ui b/src/yuzu/main.ui index 5719b2ee4..6ab95b9a5 100644 --- a/src/yuzu/main.ui +++ b/src/yuzu/main.ui | |||
| @@ -266,7 +266,7 @@ | |||
| 266 | <bool>false</bool> | 266 | <bool>false</bool> |
| 267 | </property> | 267 | </property> |
| 268 | <property name="text"> | 268 | <property name="text"> |
| 269 | <string>Load &Amiibo...</string> | 269 | <string>Load/Remove &Amiibo...</string> |
| 270 | </property> | 270 | </property> |
| 271 | </action> | 271 | </action> |
| 272 | <action name="action_Report_Compatibility"> | 272 | <action name="action_Report_Compatibility"> |