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
This commit is contained in:
parent
5edb2403c2
commit
50e4e81fd3
|
@ -10,12 +10,13 @@
|
||||||
#include "core/hle/service/nfc/nfc.h"
|
#include "core/hle/service/nfc/nfc.h"
|
||||||
#include "core/hle/service/service.h"
|
#include "core/hle/service/service.h"
|
||||||
#include "core/hle/service/sm/sm.h"
|
#include "core/hle/service/sm/sm.h"
|
||||||
|
#include "core/settings.h"
|
||||||
|
|
||||||
namespace Service::NFC {
|
namespace Service::NFC {
|
||||||
|
|
||||||
class IAm final : public ServiceFramework<IAm> {
|
class IAm final : public ServiceFramework<IAm> {
|
||||||
public:
|
public:
|
||||||
explicit IAm() : ServiceFramework{"IAm"} {
|
explicit IAm() : ServiceFramework{"NFC::IAm"} {
|
||||||
// clang-format off
|
// clang-format off
|
||||||
static const FunctionInfo functions[] = {
|
static const FunctionInfo functions[] = {
|
||||||
{0, nullptr, "Initialize"},
|
{0, nullptr, "Initialize"},
|
||||||
|
@ -52,7 +53,7 @@ private:
|
||||||
|
|
||||||
class MFIUser final : public ServiceFramework<MFIUser> {
|
class MFIUser final : public ServiceFramework<MFIUser> {
|
||||||
public:
|
public:
|
||||||
explicit MFIUser() : ServiceFramework{"IUser"} {
|
explicit MFIUser() : ServiceFramework{"NFC::MFIUser"} {
|
||||||
// clang-format off
|
// clang-format off
|
||||||
static const FunctionInfo functions[] = {
|
static const FunctionInfo functions[] = {
|
||||||
{0, nullptr, "Initialize"},
|
{0, nullptr, "Initialize"},
|
||||||
|
@ -100,13 +101,13 @@ private:
|
||||||
|
|
||||||
class IUser final : public ServiceFramework<IUser> {
|
class IUser final : public ServiceFramework<IUser> {
|
||||||
public:
|
public:
|
||||||
explicit IUser() : ServiceFramework{"IUser"} {
|
explicit IUser() : ServiceFramework{"NFC::IUser"} {
|
||||||
// clang-format off
|
// clang-format off
|
||||||
static const FunctionInfo functions[] = {
|
static const FunctionInfo functions[] = {
|
||||||
{0, nullptr, "Initialize"},
|
{0, &IUser::InitializeOld, "InitializeOld"},
|
||||||
{1, nullptr, "Finalize"},
|
{1, &IUser::FinalizeOld, "FinalizeOld"},
|
||||||
{2, nullptr, "GetState"},
|
{2, &IUser::GetStateOld, "GetStateOld"},
|
||||||
{3, nullptr, "IsNfcEnabled"},
|
{3, &IUser::IsNfcEnabledOld, "IsNfcEnabledOld"},
|
||||||
{400, nullptr, "Initialize"},
|
{400, nullptr, "Initialize"},
|
||||||
{401, nullptr, "Finalize"},
|
{401, nullptr, "Finalize"},
|
||||||
{402, nullptr, "GetState"},
|
{402, nullptr, "GetState"},
|
||||||
|
@ -130,11 +131,47 @@ public:
|
||||||
|
|
||||||
RegisterHandlers(functions);
|
RegisterHandlers(functions);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
enum class NfcStates : u32 {
|
||||||
|
Finalized = 6,
|
||||||
|
};
|
||||||
|
|
||||||
|
void InitializeOld(Kernel::HLERequestContext& ctx) {
|
||||||
|
IPC::ResponseBuilder rb{ctx, 2, 0};
|
||||||
|
rb.Push(RESULT_SUCCESS);
|
||||||
|
|
||||||
|
// We don't deal with hardware initialization so we can just stub this.
|
||||||
|
LOG_DEBUG(Service_NFC, "called");
|
||||||
|
}
|
||||||
|
|
||||||
|
void IsNfcEnabledOld(Kernel::HLERequestContext& ctx) {
|
||||||
|
IPC::ResponseBuilder rb{ctx, 3};
|
||||||
|
rb.Push(RESULT_SUCCESS);
|
||||||
|
rb.PushRaw<u8>(Settings::values.enable_nfc);
|
||||||
|
|
||||||
|
LOG_DEBUG(Service_NFC, "IsNfcEnabledOld");
|
||||||
|
}
|
||||||
|
|
||||||
|
void GetStateOld(Kernel::HLERequestContext& ctx) {
|
||||||
|
LOG_WARNING(Service_NFC, "(STUBBED) called");
|
||||||
|
|
||||||
|
IPC::ResponseBuilder rb{ctx, 3};
|
||||||
|
rb.Push(RESULT_SUCCESS);
|
||||||
|
rb.PushEnum(NfcStates::Finalized); // TODO(ogniK): Figure out if this matches nfp
|
||||||
|
}
|
||||||
|
|
||||||
|
void FinalizeOld(Kernel::HLERequestContext& ctx) {
|
||||||
|
LOG_WARNING(Service_NFC, "(STUBBED) called");
|
||||||
|
|
||||||
|
IPC::ResponseBuilder rb{ctx, 2};
|
||||||
|
rb.Push(RESULT_SUCCESS);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
class NFC_U final : public ServiceFramework<NFC_U> {
|
class NFC_U final : public ServiceFramework<NFC_U> {
|
||||||
public:
|
public:
|
||||||
explicit NFC_U() : ServiceFramework{"nfc:u"} {
|
explicit NFC_U() : ServiceFramework{"nfc:user"} {
|
||||||
// clang-format off
|
// clang-format off
|
||||||
static const FunctionInfo functions[] = {
|
static const FunctionInfo functions[] = {
|
||||||
{0, &NFC_U::CreateUserInterface, "CreateUserInterface"},
|
{0, &NFC_U::CreateUserInterface, "CreateUserInterface"},
|
||||||
|
|
|
@ -2,56 +2,67 @@
|
||||||
// Licensed under GPLv2 or any later version
|
// Licensed under GPLv2 or any later version
|
||||||
// Refer to the license.txt file included.
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
#include <atomic>
|
||||||
|
|
||||||
#include "common/logging/log.h"
|
#include "common/logging/log.h"
|
||||||
#include "core/core.h"
|
#include "core/core.h"
|
||||||
#include "core/hle/ipc_helpers.h"
|
#include "core/hle/ipc_helpers.h"
|
||||||
#include "core/hle/kernel/event.h"
|
#include "core/hle/kernel/event.h"
|
||||||
|
#include "core/hle/lock.h"
|
||||||
#include "core/hle/service/hid/hid.h"
|
#include "core/hle/service/hid/hid.h"
|
||||||
#include "core/hle/service/nfp/nfp.h"
|
#include "core/hle/service/nfp/nfp.h"
|
||||||
#include "core/hle/service/nfp/nfp_user.h"
|
#include "core/hle/service/nfp/nfp_user.h"
|
||||||
|
|
||||||
namespace Service::NFP {
|
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> module, const char* name)
|
Module::Interface::Interface(std::shared_ptr<Module> 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;
|
Module::Interface::~Interface() = default;
|
||||||
|
|
||||||
class IUser final : public ServiceFramework<IUser> {
|
class IUser final : public ServiceFramework<IUser> {
|
||||||
public:
|
public:
|
||||||
IUser() : ServiceFramework("IUser") {
|
IUser(Module::Interface& nfp_interface)
|
||||||
|
: ServiceFramework("NFP::IUser"), nfp_interface(nfp_interface) {
|
||||||
static const FunctionInfo functions[] = {
|
static const FunctionInfo functions[] = {
|
||||||
{0, &IUser::Initialize, "Initialize"},
|
{0, &IUser::Initialize, "Initialize"},
|
||||||
{1, nullptr, "Finalize"},
|
{1, &IUser::Finalize, "Finalize"},
|
||||||
{2, &IUser::ListDevices, "ListDevices"},
|
{2, &IUser::ListDevices, "ListDevices"},
|
||||||
{3, nullptr, "StartDetection"},
|
{3, &IUser::StartDetection, "StartDetection"},
|
||||||
{4, nullptr, "StopDetection"},
|
{4, &IUser::StopDetection, "StopDetection"},
|
||||||
{5, nullptr, "Mount"},
|
{5, &IUser::Mount, "Mount"},
|
||||||
{6, nullptr, "Unmount"},
|
{6, &IUser::Unmount, "Unmount"},
|
||||||
{7, nullptr, "OpenApplicationArea"},
|
{7, &IUser::OpenApplicationArea, "OpenApplicationArea"},
|
||||||
{8, nullptr, "GetApplicationArea"},
|
{8, &IUser::GetApplicationArea, "GetApplicationArea"},
|
||||||
{9, nullptr, "SetApplicationArea"},
|
{9, nullptr, "SetApplicationArea"},
|
||||||
{10, nullptr, "Flush"},
|
{10, nullptr, "Flush"},
|
||||||
{11, nullptr, "Restore"},
|
{11, nullptr, "Restore"},
|
||||||
{12, nullptr, "CreateApplicationArea"},
|
{12, nullptr, "CreateApplicationArea"},
|
||||||
{13, nullptr, "GetTagInfo"},
|
{13, &IUser::GetTagInfo, "GetTagInfo"},
|
||||||
{14, nullptr, "GetRegisterInfo"},
|
{14, &IUser::GetRegisterInfo, "GetRegisterInfo"},
|
||||||
{15, nullptr, "GetCommonInfo"},
|
{15, &IUser::GetCommonInfo, "GetCommonInfo"},
|
||||||
{16, nullptr, "GetModelInfo"},
|
{16, &IUser::GetModelInfo, "GetModelInfo"},
|
||||||
{17, &IUser::AttachActivateEvent, "AttachActivateEvent"},
|
{17, &IUser::AttachActivateEvent, "AttachActivateEvent"},
|
||||||
{18, &IUser::AttachDeactivateEvent, "AttachDeactivateEvent"},
|
{18, &IUser::AttachDeactivateEvent, "AttachDeactivateEvent"},
|
||||||
{19, &IUser::GetState, "GetState"},
|
{19, &IUser::GetState, "GetState"},
|
||||||
{20, &IUser::GetDeviceState, "GetDeviceState"},
|
{20, &IUser::GetDeviceState, "GetDeviceState"},
|
||||||
{21, &IUser::GetNpadId, "GetNpadId"},
|
{21, &IUser::GetNpadId, "GetNpadId"},
|
||||||
{22, nullptr, "GetApplicationArea2"},
|
{22, &IUser::GetApplicationAreaSize, "GetApplicationAreaSize"},
|
||||||
{23, &IUser::AttachAvailabilityChangeEvent, "AttachAvailabilityChangeEvent"},
|
{23, &IUser::AttachAvailabilityChangeEvent, "AttachAvailabilityChangeEvent"},
|
||||||
{24, nullptr, "RecreateApplicationArea"},
|
{24, nullptr, "RecreateApplicationArea"},
|
||||||
};
|
};
|
||||||
RegisterHandlers(functions);
|
RegisterHandlers(functions);
|
||||||
|
|
||||||
auto& kernel = Core::System::GetInstance().Kernel();
|
auto& kernel = Core::System::GetInstance().Kernel();
|
||||||
activate_event =
|
|
||||||
Kernel::Event::Create(kernel, Kernel::ResetType::OneShot, "IUser:ActivateEvent");
|
|
||||||
deactivate_event =
|
deactivate_event =
|
||||||
Kernel::Event::Create(kernel, Kernel::ResetType::OneShot, "IUser:DeactivateEvent");
|
Kernel::Event::Create(kernel, Kernel::ResetType::OneShot, "IUser:DeactivateEvent");
|
||||||
availability_change_event = Kernel::Event::Create(kernel, Kernel::ResetType::OneShot,
|
availability_change_event = Kernel::Event::Create(kernel, Kernel::ResetType::OneShot,
|
||||||
|
@ -59,6 +70,17 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
struct TagInfo {
|
||||||
|
std::array<u8, 10> 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 {
|
enum class State : u32 {
|
||||||
NonInitialized = 0,
|
NonInitialized = 0,
|
||||||
Initialized = 1,
|
Initialized = 1,
|
||||||
|
@ -66,15 +88,40 @@ private:
|
||||||
|
|
||||||
enum class DeviceState : u32 {
|
enum class DeviceState : u32 {
|
||||||
Initialized = 0,
|
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) {
|
void Initialize(Kernel::HLERequestContext& ctx) {
|
||||||
LOG_WARNING(Service_NFP, "(STUBBED) called");
|
IPC::ResponseBuilder rb{ctx, 2, 0};
|
||||||
|
rb.Push(RESULT_SUCCESS);
|
||||||
|
|
||||||
state = State::Initialized;
|
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.Push(RESULT_SUCCESS);
|
||||||
|
rb.PushRaw<u32>(static_cast<u32>(state));
|
||||||
|
|
||||||
|
LOG_DEBUG(Service_NFC, "called");
|
||||||
}
|
}
|
||||||
|
|
||||||
void ListDevices(Kernel::HLERequestContext& ctx) {
|
void ListDevices(Kernel::HLERequestContext& ctx) {
|
||||||
|
@ -83,80 +130,217 @@ private:
|
||||||
|
|
||||||
ctx.WriteBuffer(&device_handle, sizeof(device_handle));
|
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};
|
IPC::ResponseBuilder rb{ctx, 3};
|
||||||
rb.Push(RESULT_SUCCESS);
|
rb.Push(RESULT_SUCCESS);
|
||||||
rb.Push<u32>(0);
|
rb.Push<u32>(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void GetNpadId(Kernel::HLERequestContext& ctx) {
|
||||||
|
IPC::RequestParser rp{ctx};
|
||||||
|
const u64 dev_handle = rp.Pop<u64>();
|
||||||
|
LOG_DEBUG(Service_NFP, "called, dev_handle=0x{:X}", dev_handle);
|
||||||
|
IPC::ResponseBuilder rb{ctx, 3};
|
||||||
|
rb.Push(RESULT_SUCCESS);
|
||||||
|
rb.Push<u32>(npad_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
void AttachActivateEvent(Kernel::HLERequestContext& ctx) {
|
void AttachActivateEvent(Kernel::HLERequestContext& ctx) {
|
||||||
IPC::RequestParser rp{ctx};
|
IPC::RequestParser rp{ctx};
|
||||||
const u64 dev_handle = rp.Pop<u64>();
|
const u64 dev_handle = rp.Pop<u64>();
|
||||||
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};
|
IPC::ResponseBuilder rb{ctx, 2, 1};
|
||||||
rb.Push(RESULT_SUCCESS);
|
rb.Push(RESULT_SUCCESS);
|
||||||
rb.PushCopyObjects(activate_event);
|
rb.PushCopyObjects(nfp_interface.GetNFCEvent());
|
||||||
|
has_attached_handle = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void AttachDeactivateEvent(Kernel::HLERequestContext& ctx) {
|
void AttachDeactivateEvent(Kernel::HLERequestContext& ctx) {
|
||||||
IPC::RequestParser rp{ctx};
|
IPC::RequestParser rp{ctx};
|
||||||
const u64 dev_handle = rp.Pop<u64>();
|
const u64 dev_handle = rp.Pop<u64>();
|
||||||
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};
|
IPC::ResponseBuilder rb{ctx, 2, 1};
|
||||||
rb.Push(RESULT_SUCCESS);
|
rb.Push(RESULT_SUCCESS);
|
||||||
rb.PushCopyObjects(deactivate_event);
|
rb.PushCopyObjects(deactivate_event);
|
||||||
}
|
}
|
||||||
|
|
||||||
void GetState(Kernel::HLERequestContext& ctx) {
|
void StopDetection(Kernel::HLERequestContext& ctx) {
|
||||||
LOG_WARNING(Service_NFP, "(STUBBED) called");
|
LOG_DEBUG(Service_NFP, "called");
|
||||||
IPC::ResponseBuilder rb{ctx, 3};
|
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(RESULT_SUCCESS);
|
||||||
rb.Push<u32>(static_cast<u32>(state));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void GetDeviceState(Kernel::HLERequestContext& ctx) {
|
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};
|
IPC::ResponseBuilder rb{ctx, 3};
|
||||||
rb.Push(RESULT_SUCCESS);
|
rb.Push(RESULT_SUCCESS);
|
||||||
rb.Push<u32>(static_cast<u32>(device_state));
|
rb.Push<u32>(static_cast<u32>(device_state));
|
||||||
}
|
}
|
||||||
|
|
||||||
void GetNpadId(Kernel::HLERequestContext& ctx) {
|
void StartDetection(Kernel::HLERequestContext& ctx) {
|
||||||
IPC::RequestParser rp{ctx};
|
LOG_DEBUG(Service_NFP, "called");
|
||||||
const u64 dev_handle = rp.Pop<u64>();
|
|
||||||
LOG_WARNING(Service_NFP, "(STUBBED) called, dev_handle=0x{:X}", dev_handle);
|
if (device_state == DeviceState::Initialized || device_state == DeviceState::TagRemoved) {
|
||||||
IPC::ResponseBuilder rb{ctx, 3};
|
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<u8>(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(RESULT_SUCCESS);
|
||||||
rb.Push<u32>(npad_id);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void AttachAvailabilityChangeEvent(Kernel::HLERequestContext& ctx) {
|
void AttachAvailabilityChangeEvent(Kernel::HLERequestContext& ctx) {
|
||||||
IPC::RequestParser rp{ctx};
|
LOG_WARNING(Service_NFP, "(STUBBED) called");
|
||||||
const u64 dev_handle = rp.Pop<u64>();
|
|
||||||
LOG_WARNING(Service_NFP, "(STUBBED) called, dev_handle=0x{:X}", dev_handle);
|
|
||||||
|
|
||||||
IPC::ResponseBuilder rb{ctx, 2, 1};
|
IPC::ResponseBuilder rb{ctx, 2, 1};
|
||||||
rb.Push(RESULT_SUCCESS);
|
rb.Push(RESULT_SUCCESS);
|
||||||
rb.PushCopyObjects(availability_change_event);
|
rb.PushCopyObjects(availability_change_event);
|
||||||
}
|
}
|
||||||
|
|
||||||
const u64 device_handle{0xDEAD};
|
void GetRegisterInfo(Kernel::HLERequestContext& ctx) {
|
||||||
const u32 npad_id{0}; // This is the first player controller id
|
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<u32>(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<u32>(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};
|
State state{State::NonInitialized};
|
||||||
DeviceState device_state{DeviceState::Initialized};
|
DeviceState device_state{DeviceState::Initialized};
|
||||||
Kernel::SharedPtr<Kernel::Event> activate_event;
|
|
||||||
Kernel::SharedPtr<Kernel::Event> deactivate_event;
|
Kernel::SharedPtr<Kernel::Event> deactivate_event;
|
||||||
Kernel::SharedPtr<Kernel::Event> availability_change_event;
|
Kernel::SharedPtr<Kernel::Event> availability_change_event;
|
||||||
|
const Module::Interface& nfp_interface;
|
||||||
};
|
};
|
||||||
|
|
||||||
void Module::Interface::CreateUserInterface(Kernel::HLERequestContext& ctx) {
|
void Module::Interface::CreateUserInterface(Kernel::HLERequestContext& ctx) {
|
||||||
LOG_DEBUG(Service_NFP, "called");
|
LOG_DEBUG(Service_NFP, "called");
|
||||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||||
rb.Push(RESULT_SUCCESS);
|
rb.Push(RESULT_SUCCESS);
|
||||||
rb.PushIpcInterface<IUser>();
|
rb.PushIpcInterface<IUser>(*this);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Module::Interface::LoadAmiibo(const std::vector<u8>& buffer) {
|
||||||
|
std::lock_guard<std::recursive_mutex> 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<Kernel::Event>& Module::Interface::GetNFCEvent() const {
|
||||||
|
return nfc_tag_load;
|
||||||
|
}
|
||||||
|
const Module::Interface::AmiiboFile& Module::Interface::GetAmiiboBuffer() const {
|
||||||
|
return amiibo;
|
||||||
}
|
}
|
||||||
|
|
||||||
void InstallInterfaces(SM::ServiceManager& service_manager) {
|
void InstallInterfaces(SM::ServiceManager& service_manager) {
|
||||||
|
|
|
@ -4,6 +4,9 @@
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <array>
|
||||||
|
#include <vector>
|
||||||
|
#include "core/hle/kernel/event.h"
|
||||||
#include "core/hle/service/service.h"
|
#include "core/hle/service/service.h"
|
||||||
|
|
||||||
namespace Service::NFP {
|
namespace Service::NFP {
|
||||||
|
@ -15,7 +18,27 @@ public:
|
||||||
explicit Interface(std::shared_ptr<Module> module, const char* name);
|
explicit Interface(std::shared_ptr<Module> module, const char* name);
|
||||||
~Interface() override;
|
~Interface() override;
|
||||||
|
|
||||||
|
struct ModelInfo {
|
||||||
|
std::array<u8, 0x8> amiibo_identification_block;
|
||||||
|
INSERT_PADDING_BYTES(0x38);
|
||||||
|
};
|
||||||
|
static_assert(sizeof(ModelInfo) == 0x40, "ModelInfo is an invalid size");
|
||||||
|
|
||||||
|
struct AmiiboFile {
|
||||||
|
std::array<u8, 10> uuid;
|
||||||
|
INSERT_PADDING_BYTES(0x4a);
|
||||||
|
ModelInfo model_info;
|
||||||
|
};
|
||||||
|
static_assert(sizeof(AmiiboFile) == 0x94, "AmiiboFile is an invalid size");
|
||||||
|
|
||||||
void CreateUserInterface(Kernel::HLERequestContext& ctx);
|
void CreateUserInterface(Kernel::HLERequestContext& ctx);
|
||||||
|
void LoadAmiibo(const std::vector<u8>& buffer);
|
||||||
|
const Kernel::SharedPtr<Kernel::Event>& GetNFCEvent() const;
|
||||||
|
const AmiiboFile& GetAmiiboBuffer() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
Kernel::SharedPtr<Kernel::Event> nfc_tag_load{};
|
||||||
|
AmiiboFile amiibo{};
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
std::shared_ptr<Module> module;
|
std::shared_ptr<Module> module;
|
||||||
|
|
|
@ -113,6 +113,7 @@ static const std::array<const char*, NumAnalogs> mapping = {{
|
||||||
struct Values {
|
struct Values {
|
||||||
// System
|
// System
|
||||||
bool use_docked_mode;
|
bool use_docked_mode;
|
||||||
|
bool enable_nfc;
|
||||||
std::string username;
|
std::string username;
|
||||||
int language_index;
|
int language_index;
|
||||||
|
|
||||||
|
|
|
@ -122,6 +122,7 @@ void Config::ReadValues() {
|
||||||
|
|
||||||
qt_config->beginGroup("System");
|
qt_config->beginGroup("System");
|
||||||
Settings::values.use_docked_mode = qt_config->value("use_docked_mode", false).toBool();
|
Settings::values.use_docked_mode = qt_config->value("use_docked_mode", false).toBool();
|
||||||
|
Settings::values.enable_nfc = qt_config->value("enable_nfc", true).toBool();
|
||||||
Settings::values.username = qt_config->value("username", "yuzu").toString().toStdString();
|
Settings::values.username = qt_config->value("username", "yuzu").toString().toStdString();
|
||||||
Settings::values.language_index = qt_config->value("language_index", 1).toInt();
|
Settings::values.language_index = qt_config->value("language_index", 1).toInt();
|
||||||
qt_config->endGroup();
|
qt_config->endGroup();
|
||||||
|
@ -258,6 +259,7 @@ void Config::SaveValues() {
|
||||||
|
|
||||||
qt_config->beginGroup("System");
|
qt_config->beginGroup("System");
|
||||||
qt_config->setValue("use_docked_mode", Settings::values.use_docked_mode);
|
qt_config->setValue("use_docked_mode", Settings::values.use_docked_mode);
|
||||||
|
qt_config->setValue("enable_nfc", Settings::values.enable_nfc);
|
||||||
qt_config->setValue("username", QString::fromStdString(Settings::values.username));
|
qt_config->setValue("username", QString::fromStdString(Settings::values.username));
|
||||||
qt_config->setValue("language_index", Settings::values.language_index);
|
qt_config->setValue("language_index", Settings::values.language_index);
|
||||||
qt_config->endGroup();
|
qt_config->endGroup();
|
||||||
|
|
|
@ -31,6 +31,7 @@ void ConfigureGeneral::setConfiguration() {
|
||||||
ui->theme_combobox->setCurrentIndex(ui->theme_combobox->findData(UISettings::values.theme));
|
ui->theme_combobox->setCurrentIndex(ui->theme_combobox->findData(UISettings::values.theme));
|
||||||
ui->use_cpu_jit->setChecked(Settings::values.use_cpu_jit);
|
ui->use_cpu_jit->setChecked(Settings::values.use_cpu_jit);
|
||||||
ui->use_docked_mode->setChecked(Settings::values.use_docked_mode);
|
ui->use_docked_mode->setChecked(Settings::values.use_docked_mode);
|
||||||
|
ui->enable_nfc->setChecked(Settings::values.enable_nfc);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ConfigureGeneral::PopulateHotkeyList(const HotkeyRegistry& registry) {
|
void ConfigureGeneral::PopulateHotkeyList(const HotkeyRegistry& registry) {
|
||||||
|
@ -45,4 +46,5 @@ void ConfigureGeneral::applyConfiguration() {
|
||||||
|
|
||||||
Settings::values.use_cpu_jit = ui->use_cpu_jit->isChecked();
|
Settings::values.use_cpu_jit = ui->use_cpu_jit->isChecked();
|
||||||
Settings::values.use_docked_mode = ui->use_docked_mode->isChecked();
|
Settings::values.use_docked_mode = ui->use_docked_mode->isChecked();
|
||||||
|
Settings::values.enable_nfc = ui->enable_nfc->isChecked();
|
||||||
}
|
}
|
||||||
|
|
|
@ -78,6 +78,13 @@
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QCheckBox" name="enable_nfc">
|
||||||
|
<property name="text">
|
||||||
|
<string>Enable NFC</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</item>
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
|
|
|
@ -60,6 +60,8 @@ static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::Virtual
|
||||||
#include "core/hle/kernel/process.h"
|
#include "core/hle/kernel/process.h"
|
||||||
#include "core/hle/service/filesystem/filesystem.h"
|
#include "core/hle/service/filesystem/filesystem.h"
|
||||||
#include "core/hle/service/filesystem/fsp_ldr.h"
|
#include "core/hle/service/filesystem/fsp_ldr.h"
|
||||||
|
#include "core/hle/service/nfp/nfp.h"
|
||||||
|
#include "core/hle/service/sm/sm.h"
|
||||||
#include "core/loader/loader.h"
|
#include "core/loader/loader.h"
|
||||||
#include "core/perf_stats.h"
|
#include "core/perf_stats.h"
|
||||||
#include "core/settings.h"
|
#include "core/settings.h"
|
||||||
|
@ -424,6 +426,7 @@ void GMainWindow::ConnectMenuEvents() {
|
||||||
connect(ui.action_Select_SDMC_Directory, &QAction::triggered, this,
|
connect(ui.action_Select_SDMC_Directory, &QAction::triggered, this,
|
||||||
[this] { OnMenuSelectEmulatedDirectory(EmulatedDirectoryTarget::SDMC); });
|
[this] { OnMenuSelectEmulatedDirectory(EmulatedDirectoryTarget::SDMC); });
|
||||||
connect(ui.action_Exit, &QAction::triggered, this, &QMainWindow::close);
|
connect(ui.action_Exit, &QAction::triggered, this, &QMainWindow::close);
|
||||||
|
connect(ui.action_Load_Amiibo, &QAction::triggered, this, &GMainWindow::OnLoadAmiibo);
|
||||||
|
|
||||||
// Emulation
|
// Emulation
|
||||||
connect(ui.action_Start, &QAction::triggered, this, &GMainWindow::OnStartGame);
|
connect(ui.action_Start, &QAction::triggered, this, &GMainWindow::OnStartGame);
|
||||||
|
@ -692,6 +695,7 @@ void GMainWindow::ShutdownGame() {
|
||||||
ui.action_Stop->setEnabled(false);
|
ui.action_Stop->setEnabled(false);
|
||||||
ui.action_Restart->setEnabled(false);
|
ui.action_Restart->setEnabled(false);
|
||||||
ui.action_Report_Compatibility->setEnabled(false);
|
ui.action_Report_Compatibility->setEnabled(false);
|
||||||
|
ui.action_Load_Amiibo->setEnabled(false);
|
||||||
render_window->hide();
|
render_window->hide();
|
||||||
game_list->show();
|
game_list->show();
|
||||||
game_list->setFilterFocus();
|
game_list->setFilterFocus();
|
||||||
|
@ -1191,6 +1195,7 @@ void GMainWindow::OnStartGame() {
|
||||||
ui.action_Report_Compatibility->setEnabled(true);
|
ui.action_Report_Compatibility->setEnabled(true);
|
||||||
|
|
||||||
discord_rpc->Update();
|
discord_rpc->Update();
|
||||||
|
ui.action_Load_Amiibo->setEnabled(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
void GMainWindow::OnPauseGame() {
|
void GMainWindow::OnPauseGame() {
|
||||||
|
@ -1295,6 +1300,27 @@ void GMainWindow::OnConfigure() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void GMainWindow::OnLoadAmiibo() {
|
||||||
|
const QString extensions{"*.bin"};
|
||||||
|
const QString file_filter = tr("Amiibo File (%1);; All Files (*.*)").arg(extensions);
|
||||||
|
const QString filename = QFileDialog::getOpenFileName(this, tr("Load Amiibo"), "", file_filter);
|
||||||
|
if (!filename.isEmpty()) {
|
||||||
|
Core::System& system{Core::System::GetInstance()};
|
||||||
|
Service::SM::ServiceManager& sm = system.ServiceManager();
|
||||||
|
auto nfc = sm.GetService<Service::NFP::Module::Interface>("nfp:user");
|
||||||
|
if (nfc != nullptr) {
|
||||||
|
auto nfc_file = FileUtil::IOFile(filename.toStdString(), "rb");
|
||||||
|
if (!nfc_file.IsOpen()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
std::vector<u8> amiibo_buffer(nfc_file.GetSize());
|
||||||
|
nfc_file.ReadBytes(amiibo_buffer.data(), amiibo_buffer.size());
|
||||||
|
nfc_file.Close();
|
||||||
|
nfc->LoadAmiibo(amiibo_buffer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void GMainWindow::OnAbout() {
|
void GMainWindow::OnAbout() {
|
||||||
AboutDialog aboutDialog(this);
|
AboutDialog aboutDialog(this);
|
||||||
aboutDialog.exec();
|
aboutDialog.exec();
|
||||||
|
@ -1335,13 +1361,15 @@ void GMainWindow::UpdateStatusBar() {
|
||||||
void GMainWindow::OnCoreError(Core::System::ResultStatus result, std::string details) {
|
void GMainWindow::OnCoreError(Core::System::ResultStatus result, std::string details) {
|
||||||
QMessageBox::StandardButton answer;
|
QMessageBox::StandardButton answer;
|
||||||
QString status_message;
|
QString status_message;
|
||||||
const QString common_message = tr(
|
const QString common_message =
|
||||||
"The game you are trying to load requires additional files from your Switch to be dumped "
|
tr("The game you are trying to load requires additional files from your Switch to be "
|
||||||
|
"dumped "
|
||||||
"before playing.<br/><br/>For more information on dumping these files, please see the "
|
"before playing.<br/><br/>For more information on dumping these files, please see the "
|
||||||
"following wiki page: <a "
|
"following wiki page: <a "
|
||||||
"href='https://yuzu-emu.org/wiki/"
|
"href='https://yuzu-emu.org/wiki/"
|
||||||
"dumping-system-archives-and-the-shared-fonts-from-a-switch-console/'>Dumping System "
|
"dumping-system-archives-and-the-shared-fonts-from-a-switch-console/'>Dumping System "
|
||||||
"Archives and the Shared Fonts from a Switch Console</a>.<br/><br/>Would you like to quit "
|
"Archives and the Shared Fonts from a Switch Console</a>.<br/><br/>Would you like to "
|
||||||
|
"quit "
|
||||||
"back to the game list? Continuing emulation may result in crashes, corrupted save "
|
"back to the game list? Continuing emulation may result in crashes, corrupted save "
|
||||||
"data, or other bugs.");
|
"data, or other bugs.");
|
||||||
switch (result) {
|
switch (result) {
|
||||||
|
@ -1374,9 +1402,12 @@ void GMainWindow::OnCoreError(Core::System::ResultStatus result, std::string det
|
||||||
this, tr("Fatal Error"),
|
this, tr("Fatal Error"),
|
||||||
tr("yuzu has encountered a fatal error, please see the log for more details. "
|
tr("yuzu has encountered a fatal error, please see the log for more details. "
|
||||||
"For more information on accessing the log, please see the following page: "
|
"For more information on accessing the log, please see the following page: "
|
||||||
"<a href='https://community.citra-emu.org/t/how-to-upload-the-log-file/296'>How to "
|
"<a href='https://community.citra-emu.org/t/how-to-upload-the-log-file/296'>How "
|
||||||
"Upload the Log File</a>.<br/><br/>Would you like to quit back to the game list? "
|
"to "
|
||||||
"Continuing emulation may result in crashes, corrupted save data, or other bugs."),
|
"Upload the Log File</a>.<br/><br/>Would you like to quit back to the game "
|
||||||
|
"list? "
|
||||||
|
"Continuing emulation may result in crashes, corrupted save data, or other "
|
||||||
|
"bugs."),
|
||||||
QMessageBox::Yes | QMessageBox::No, QMessageBox::No);
|
QMessageBox::Yes | QMessageBox::No, QMessageBox::No);
|
||||||
status_message = "Fatal Error encountered";
|
status_message = "Fatal Error encountered";
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -166,6 +166,7 @@ private slots:
|
||||||
void OnMenuSelectEmulatedDirectory(EmulatedDirectoryTarget target);
|
void OnMenuSelectEmulatedDirectory(EmulatedDirectoryTarget target);
|
||||||
void OnMenuRecentFile();
|
void OnMenuRecentFile();
|
||||||
void OnConfigure();
|
void OnConfigure();
|
||||||
|
void OnLoadAmiibo();
|
||||||
void OnAbout();
|
void OnAbout();
|
||||||
void OnToggleFilterBar();
|
void OnToggleFilterBar();
|
||||||
void OnDisplayTitleBars(bool);
|
void OnDisplayTitleBars(bool);
|
||||||
|
|
|
@ -68,6 +68,8 @@
|
||||||
<addaction name="action_Select_NAND_Directory"/>
|
<addaction name="action_Select_NAND_Directory"/>
|
||||||
<addaction name="action_Select_SDMC_Directory"/>
|
<addaction name="action_Select_SDMC_Directory"/>
|
||||||
<addaction name="separator"/>
|
<addaction name="separator"/>
|
||||||
|
<addaction name="action_Load_Amiibo"/>
|
||||||
|
<addaction name="separator"/>
|
||||||
<addaction name="action_Exit"/>
|
<addaction name="action_Exit"/>
|
||||||
</widget>
|
</widget>
|
||||||
<widget class="QMenu" name="menu_Emulation">
|
<widget class="QMenu" name="menu_Emulation">
|
||||||
|
@ -118,6 +120,9 @@
|
||||||
<addaction name="menu_Help"/>
|
<addaction name="menu_Help"/>
|
||||||
</widget>
|
</widget>
|
||||||
<action name="action_Install_File_NAND">
|
<action name="action_Install_File_NAND">
|
||||||
|
<property name="enabled">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Install File to NAND...</string>
|
<string>Install File to NAND...</string>
|
||||||
</property>
|
</property>
|
||||||
|
@ -253,6 +258,14 @@
|
||||||
<string>Restart</string>
|
<string>Restart</string>
|
||||||
</property>
|
</property>
|
||||||
</action>
|
</action>
|
||||||
|
<action name="action_Load_Amiibo">
|
||||||
|
<property name="enabled">
|
||||||
|
<bool>false</bool>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Load Amiibo...</string>
|
||||||
|
</property>
|
||||||
|
</action>
|
||||||
<action name="action_Report_Compatibility">
|
<action name="action_Report_Compatibility">
|
||||||
<property name="enabled">
|
<property name="enabled">
|
||||||
<bool>false</bool>
|
<bool>false</bool>
|
||||||
|
|
|
@ -125,6 +125,7 @@ void Config::ReadValues() {
|
||||||
|
|
||||||
// System
|
// System
|
||||||
Settings::values.use_docked_mode = sdl2_config->GetBoolean("System", "use_docked_mode", false);
|
Settings::values.use_docked_mode = sdl2_config->GetBoolean("System", "use_docked_mode", false);
|
||||||
|
Settings::values.enable_nfc = sdl2_config->GetBoolean("System", "enable_nfc", true);
|
||||||
Settings::values.username = sdl2_config->Get("System", "username", "yuzu");
|
Settings::values.username = sdl2_config->Get("System", "username", "yuzu");
|
||||||
if (Settings::values.username.empty()) {
|
if (Settings::values.username.empty()) {
|
||||||
Settings::values.username = "yuzu";
|
Settings::values.username = "yuzu";
|
||||||
|
|
|
@ -174,6 +174,10 @@ use_virtual_sd =
|
||||||
# 1: Yes, 0 (default): No
|
# 1: Yes, 0 (default): No
|
||||||
use_docked_mode =
|
use_docked_mode =
|
||||||
|
|
||||||
|
# Allow the use of NFC in games
|
||||||
|
# 1 (default): Yes, 0 : No
|
||||||
|
enable_nfc =
|
||||||
|
|
||||||
# Sets the account username, max length is 32 characters
|
# Sets the account username, max length is 32 characters
|
||||||
# yuzu (default)
|
# yuzu (default)
|
||||||
username = yuzu
|
username = yuzu
|
||||||
|
|
Loading…
Reference in New Issue