diff options
| author | David <25727384+ogniK5377@users.noreply.github.com> | 2018-10-24 10:28:17 +1100 | 
|---|---|---|
| committer | bunnei <bunneidev@gmail.com> | 2018-10-23 19:28:17 -0400 | 
| commit | 50e4e81fd3a998813316cdabd05ef56b12c91fae (patch) | |
| tree | b5b77eec3bdc55b411c04ca6a9d4a44f1e670f9c | |
| parent | 5edb2403c2030162bbb602dfd8289c738acff0e3 (diff) | |
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
| -rw-r--r-- | src/core/hle/service/nfc/nfc.cpp | 53 | ||||
| -rw-r--r-- | src/core/hle/service/nfp/nfp.cpp | 268 | ||||
| -rw-r--r-- | src/core/hle/service/nfp/nfp.h | 23 | ||||
| -rw-r--r-- | src/core/settings.h | 1 | ||||
| -rw-r--r-- | src/yuzu/configuration/config.cpp | 2 | ||||
| -rw-r--r-- | src/yuzu/configuration/configure_general.cpp | 2 | ||||
| -rw-r--r-- | src/yuzu/configuration/configure_general.ui | 29 | ||||
| -rw-r--r-- | src/yuzu/main.cpp | 55 | ||||
| -rw-r--r-- | src/yuzu/main.h | 1 | ||||
| -rw-r--r-- | src/yuzu/main.ui | 27 | ||||
| -rw-r--r-- | src/yuzu_cmd/config.cpp | 1 | ||||
| -rw-r--r-- | src/yuzu_cmd/default_ini.h | 4 | 
12 files changed, 386 insertions, 80 deletions
| diff --git a/src/core/hle/service/nfc/nfc.cpp b/src/core/hle/service/nfc/nfc.cpp index 8fec97db8..30e542542 100644 --- a/src/core/hle/service/nfc/nfc.cpp +++ b/src/core/hle/service/nfc/nfc.cpp @@ -10,12 +10,13 @@  #include "core/hle/service/nfc/nfc.h"  #include "core/hle/service/service.h"  #include "core/hle/service/sm/sm.h" +#include "core/settings.h"  namespace Service::NFC {  class IAm final : public ServiceFramework<IAm> {  public: -    explicit IAm() : ServiceFramework{"IAm"} { +    explicit IAm() : ServiceFramework{"NFC::IAm"} {          // clang-format off          static const FunctionInfo functions[] = {              {0, nullptr, "Initialize"}, @@ -52,7 +53,7 @@ private:  class MFIUser final : public ServiceFramework<MFIUser> {  public: -    explicit MFIUser() : ServiceFramework{"IUser"} { +    explicit MFIUser() : ServiceFramework{"NFC::MFIUser"} {          // clang-format off          static const FunctionInfo functions[] = {              {0, nullptr, "Initialize"}, @@ -100,13 +101,13 @@ private:  class IUser final : public ServiceFramework<IUser> {  public: -    explicit IUser() : ServiceFramework{"IUser"} { +    explicit IUser() : ServiceFramework{"NFC::IUser"} {          // clang-format off          static const FunctionInfo functions[] = { -            {0, nullptr, "Initialize"}, -            {1, nullptr, "Finalize"}, -            {2, nullptr, "GetState"}, -            {3, nullptr, "IsNfcEnabled"}, +            {0, &IUser::InitializeOld, "InitializeOld"}, +            {1, &IUser::FinalizeOld, "FinalizeOld"}, +            {2, &IUser::GetStateOld, "GetStateOld"}, +            {3, &IUser::IsNfcEnabledOld, "IsNfcEnabledOld"},              {400, nullptr, "Initialize"},              {401, nullptr, "Finalize"},              {402, nullptr, "GetState"}, @@ -130,11 +131,47 @@ public:          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> {  public: -    explicit NFC_U() : ServiceFramework{"nfc:u"} { +    explicit NFC_U() : ServiceFramework{"nfc:user"} {          // clang-format off          static const FunctionInfo functions[] = {              {0, &NFC_U::CreateUserInterface, "CreateUserInterface"}, diff --git a/src/core/hle/service/nfp/nfp.cpp b/src/core/hle/service/nfp/nfp.cpp index 39c0c1e63..9a4eb9301 100644 --- a/src/core/hle/service/nfp/nfp.cpp +++ b/src/core/hle/service/nfp/nfp.cpp @@ -2,56 +2,67 @@  // Licensed under GPLv2 or any later version  // Refer to the license.txt file included. +#include <atomic> +  #include "common/logging/log.h"  #include "core/core.h"  #include "core/hle/ipc_helpers.h"  #include "core/hle/kernel/event.h" +#include "core/hle/lock.h"  #include "core/hle/service/hid/hid.h"  #include "core/hle/service/nfp/nfp.h"  #include "core/hle/service/nfp/nfp_user.h"  namespace Service::NFP { +namespace ErrCodes { +constexpr ResultCode ERR_TAG_FAILED(ErrorModule::NFP, +                                    -1); // TODO(ogniK): Find the actual error code +} +  Module::Interface::Interface(std::shared_ptr<Module> module, const char* name) -    : ServiceFramework(name), module(std::move(module)) {} +    : ServiceFramework(name), module(std::move(module)) { +    auto& kernel = Core::System::GetInstance().Kernel(); +    nfc_tag_load = +        Kernel::Event::Create(kernel, Kernel::ResetType::OneShot, "IUser:NFCTagDetected"); +}  Module::Interface::~Interface() = default;  class IUser final : public ServiceFramework<IUser> {  public: -    IUser() : ServiceFramework("IUser") { +    IUser(Module::Interface& nfp_interface) +        : ServiceFramework("NFP::IUser"), nfp_interface(nfp_interface) {          static const FunctionInfo functions[] = {              {0, &IUser::Initialize, "Initialize"}, -            {1, nullptr, "Finalize"}, +            {1, &IUser::Finalize, "Finalize"},              {2, &IUser::ListDevices, "ListDevices"}, -            {3, nullptr, "StartDetection"}, -            {4, nullptr, "StopDetection"}, -            {5, nullptr, "Mount"}, -            {6, nullptr, "Unmount"}, -            {7, nullptr, "OpenApplicationArea"}, -            {8, nullptr, "GetApplicationArea"}, +            {3, &IUser::StartDetection, "StartDetection"}, +            {4, &IUser::StopDetection, "StopDetection"}, +            {5, &IUser::Mount, "Mount"}, +            {6, &IUser::Unmount, "Unmount"}, +            {7, &IUser::OpenApplicationArea, "OpenApplicationArea"}, +            {8, &IUser::GetApplicationArea, "GetApplicationArea"},              {9, nullptr, "SetApplicationArea"},              {10, nullptr, "Flush"},              {11, nullptr, "Restore"},              {12, nullptr, "CreateApplicationArea"}, -            {13, nullptr, "GetTagInfo"}, -            {14, nullptr, "GetRegisterInfo"}, -            {15, nullptr, "GetCommonInfo"}, -            {16, nullptr, "GetModelInfo"}, +            {13, &IUser::GetTagInfo, "GetTagInfo"}, +            {14, &IUser::GetRegisterInfo, "GetRegisterInfo"}, +            {15, &IUser::GetCommonInfo, "GetCommonInfo"}, +            {16, &IUser::GetModelInfo, "GetModelInfo"},              {17, &IUser::AttachActivateEvent, "AttachActivateEvent"},              {18, &IUser::AttachDeactivateEvent, "AttachDeactivateEvent"},              {19, &IUser::GetState, "GetState"},              {20, &IUser::GetDeviceState, "GetDeviceState"},              {21, &IUser::GetNpadId, "GetNpadId"}, -            {22, nullptr, "GetApplicationArea2"}, +            {22, &IUser::GetApplicationAreaSize, "GetApplicationAreaSize"},              {23, &IUser::AttachAvailabilityChangeEvent, "AttachAvailabilityChangeEvent"},              {24, nullptr, "RecreateApplicationArea"},          };          RegisterHandlers(functions);          auto& kernel = Core::System::GetInstance().Kernel(); -        activate_event = -            Kernel::Event::Create(kernel, Kernel::ResetType::OneShot, "IUser:ActivateEvent");          deactivate_event =              Kernel::Event::Create(kernel, Kernel::ResetType::OneShot, "IUser:DeactivateEvent");          availability_change_event = Kernel::Event::Create(kernel, Kernel::ResetType::OneShot, @@ -59,6 +70,17 @@ public:      }  private: +    struct TagInfo { +        std::array<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 {          NonInitialized = 0,          Initialized = 1, @@ -66,15 +88,40 @@ private:      enum class DeviceState : u32 {          Initialized = 0, +        SearchingForTag = 1, +        TagFound = 2, +        TagRemoved = 3, +        TagNearby = 4, +        Unknown5 = 5, +        Finalized = 6      }; +    struct CommonInfo { +        u16_be last_write_year; +        u8 last_write_month; +        u8 last_write_day; +        u16_be write_counter; +        u16_be version; +        u32_be application_area_size; +        INSERT_PADDING_BYTES(0x34); +    }; +    static_assert(sizeof(CommonInfo) == 0x40, "CommonInfo is an invalid size"); +      void Initialize(Kernel::HLERequestContext& ctx) { -        LOG_WARNING(Service_NFP, "(STUBBED) called"); +        IPC::ResponseBuilder rb{ctx, 2, 0}; +        rb.Push(RESULT_SUCCESS);          state = State::Initialized; -        IPC::ResponseBuilder rb{ctx, 2}; +        LOG_DEBUG(Service_NFC, "called"); +    } + +    void GetState(Kernel::HLERequestContext& ctx) { +        IPC::ResponseBuilder rb{ctx, 3, 0};          rb.Push(RESULT_SUCCESS); +        rb.PushRaw<u32>(static_cast<u32>(state)); + +        LOG_DEBUG(Service_NFC, "called");      }      void ListDevices(Kernel::HLERequestContext& ctx) { @@ -83,80 +130,217 @@ private:          ctx.WriteBuffer(&device_handle, sizeof(device_handle)); -        LOG_WARNING(Service_NFP, "(STUBBED) called, array_size={}", array_size); +        LOG_DEBUG(Service_NFP, "called, array_size={}", array_size);          IPC::ResponseBuilder rb{ctx, 3};          rb.Push(RESULT_SUCCESS); -        rb.Push<u32>(0); +        rb.Push<u32>(1);      } -    void AttachActivateEvent(Kernel::HLERequestContext& ctx) { +    void GetNpadId(Kernel::HLERequestContext& ctx) {          IPC::RequestParser rp{ctx};          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, 3}; +        rb.Push(RESULT_SUCCESS); +        rb.Push<u32>(npad_id); +    } +    void AttachActivateEvent(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, 2, 1};          rb.Push(RESULT_SUCCESS); -        rb.PushCopyObjects(activate_event); +        rb.PushCopyObjects(nfp_interface.GetNFCEvent()); +        has_attached_handle = true;      }      void AttachDeactivateEvent(Kernel::HLERequestContext& ctx) {          IPC::RequestParser rp{ctx};          const u64 dev_handle = rp.Pop<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};          rb.Push(RESULT_SUCCESS);          rb.PushCopyObjects(deactivate_event);      } -    void GetState(Kernel::HLERequestContext& ctx) { -        LOG_WARNING(Service_NFP, "(STUBBED) called"); -        IPC::ResponseBuilder rb{ctx, 3}; +    void StopDetection(Kernel::HLERequestContext& ctx) { +        LOG_DEBUG(Service_NFP, "called"); +        switch (device_state) { +        case DeviceState::TagFound: +        case DeviceState::TagNearby: +            deactivate_event->Signal(); +            device_state = DeviceState::Initialized; +            break; +        case DeviceState::SearchingForTag: +        case DeviceState::TagRemoved: +            device_state = DeviceState::Initialized; +            break; +        } +        IPC::ResponseBuilder rb{ctx, 2};          rb.Push(RESULT_SUCCESS); -        rb.Push<u32>(static_cast<u32>(state));      }      void GetDeviceState(Kernel::HLERequestContext& ctx) { -        LOG_WARNING(Service_NFP, "(STUBBED) called"); +        LOG_DEBUG(Service_NFP, "called"); +        auto nfc_event = nfp_interface.GetNFCEvent(); +        if (!nfc_event->ShouldWait(Kernel::GetCurrentThread()) && !has_attached_handle) { +            device_state = DeviceState::TagFound; +            nfc_event->Clear(); +        } +          IPC::ResponseBuilder rb{ctx, 3};          rb.Push(RESULT_SUCCESS);          rb.Push<u32>(static_cast<u32>(device_state));      } -    void GetNpadId(Kernel::HLERequestContext& ctx) { -        IPC::RequestParser rp{ctx}; -        const u64 dev_handle = rp.Pop<u64>(); -        LOG_WARNING(Service_NFP, "(STUBBED) called, dev_handle=0x{:X}", dev_handle); -        IPC::ResponseBuilder rb{ctx, 3}; +    void StartDetection(Kernel::HLERequestContext& ctx) { +        LOG_DEBUG(Service_NFP, "called"); + +        if (device_state == DeviceState::Initialized || device_state == DeviceState::TagRemoved) { +            device_state = DeviceState::SearchingForTag; +        } +        IPC::ResponseBuilder rb{ctx, 2}; +        rb.Push(RESULT_SUCCESS); +    } + +    void GetTagInfo(Kernel::HLERequestContext& ctx) { +        LOG_DEBUG(Service_NFP, "called"); + +        IPC::ResponseBuilder rb{ctx, 2}; +        auto amiibo = nfp_interface.GetAmiiboBuffer(); +        TagInfo tag_info{}; +        std::memcpy(tag_info.uuid.data(), amiibo.uuid.data(), sizeof(tag_info.uuid.size())); +        tag_info.uuid_length = static_cast<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<u32>(npad_id);      }      void AttachAvailabilityChangeEvent(Kernel::HLERequestContext& ctx) { -        IPC::RequestParser rp{ctx}; -        const u64 dev_handle = rp.Pop<u64>(); -        LOG_WARNING(Service_NFP, "(STUBBED) called, dev_handle=0x{:X}", dev_handle); +        LOG_WARNING(Service_NFP, "(STUBBED) called");          IPC::ResponseBuilder rb{ctx, 2, 1};          rb.Push(RESULT_SUCCESS);          rb.PushCopyObjects(availability_change_event);      } -    const u64 device_handle{0xDEAD}; -    const u32 npad_id{0}; // This is the first player controller id +    void GetRegisterInfo(Kernel::HLERequestContext& ctx) { +        LOG_WARNING(Service_NFP, "(STUBBED) called"); + +        // TODO(ogniK): Pull Mii and owner data from amiibo + +        IPC::ResponseBuilder rb{ctx, 2}; +        rb.Push(RESULT_SUCCESS); +    } + +    void GetCommonInfo(Kernel::HLERequestContext& ctx) { +        LOG_WARNING(Service_NFP, "(STUBBED) called"); + +        // TODO(ogniK): Pull common information from amiibo + +        CommonInfo common_info{}; +        common_info.application_area_size = 0; +        ctx.WriteBuffer(&common_info, sizeof(CommonInfo)); + +        IPC::ResponseBuilder rb{ctx, 2}; +        rb.Push(RESULT_SUCCESS); +    } + +    void OpenApplicationArea(Kernel::HLERequestContext& ctx) { +        LOG_DEBUG(Service_NFP, "called"); +        // We don't need to worry about this since we can just open the file +        IPC::ResponseBuilder rb{ctx, 2}; +        rb.Push(RESULT_SUCCESS); +    } + +    void GetApplicationAreaSize(Kernel::HLERequestContext& ctx) { +        LOG_WARNING(Service_NFP, "(STUBBED) called"); +        // We don't need to worry about this since we can just open the file +        IPC::ResponseBuilder rb{ctx, 3}; +        rb.Push(RESULT_SUCCESS); +        rb.PushRaw<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};      DeviceState device_state{DeviceState::Initialized}; -    Kernel::SharedPtr<Kernel::Event> activate_event;      Kernel::SharedPtr<Kernel::Event> deactivate_event;      Kernel::SharedPtr<Kernel::Event> availability_change_event; +    const Module::Interface& nfp_interface;  };  void Module::Interface::CreateUserInterface(Kernel::HLERequestContext& ctx) {      LOG_DEBUG(Service_NFP, "called");      IPC::ResponseBuilder rb{ctx, 2, 0, 1};      rb.Push(RESULT_SUCCESS); -    rb.PushIpcInterface<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) { diff --git a/src/core/hle/service/nfp/nfp.h b/src/core/hle/service/nfp/nfp.h index 77df343c4..46370dedd 100644 --- a/src/core/hle/service/nfp/nfp.h +++ b/src/core/hle/service/nfp/nfp.h @@ -4,6 +4,9 @@  #pragma once +#include <array> +#include <vector> +#include "core/hle/kernel/event.h"  #include "core/hle/service/service.h"  namespace Service::NFP { @@ -15,7 +18,27 @@ public:          explicit Interface(std::shared_ptr<Module> module, const char* name);          ~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 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:          std::shared_ptr<Module> module; diff --git a/src/core/settings.h b/src/core/settings.h index 8f2da01c8..ca80718e2 100644 --- a/src/core/settings.h +++ b/src/core/settings.h @@ -113,6 +113,7 @@ static const std::array<const char*, NumAnalogs> mapping = {{  struct Values {      // System      bool use_docked_mode; +    bool enable_nfc;      std::string username;      int language_index; diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp index 71c6ebb41..d029590ff 100644 --- a/src/yuzu/configuration/config.cpp +++ b/src/yuzu/configuration/config.cpp @@ -122,6 +122,7 @@ void Config::ReadValues() {      qt_config->beginGroup("System");      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.language_index = qt_config->value("language_index", 1).toInt();      qt_config->endGroup(); @@ -258,6 +259,7 @@ void Config::SaveValues() {      qt_config->beginGroup("System");      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("language_index", Settings::values.language_index);      qt_config->endGroup(); diff --git a/src/yuzu/configuration/configure_general.cpp b/src/yuzu/configuration/configure_general.cpp index f5db9e55b..537d6e576 100644 --- a/src/yuzu/configuration/configure_general.cpp +++ b/src/yuzu/configuration/configure_general.cpp @@ -31,6 +31,7 @@ void ConfigureGeneral::setConfiguration() {      ui->theme_combobox->setCurrentIndex(ui->theme_combobox->findData(UISettings::values.theme));      ui->use_cpu_jit->setChecked(Settings::values.use_cpu_jit);      ui->use_docked_mode->setChecked(Settings::values.use_docked_mode); +    ui->enable_nfc->setChecked(Settings::values.enable_nfc);  }  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_docked_mode = ui->use_docked_mode->isChecked(); +    Settings::values.enable_nfc = ui->enable_nfc->isChecked();  } diff --git a/src/yuzu/configuration/configure_general.ui b/src/yuzu/configuration/configure_general.ui index 1775c4d40..b82fffde8 100644 --- a/src/yuzu/configuration/configure_general.ui +++ b/src/yuzu/configuration/configure_general.ui @@ -68,19 +68,26 @@         <property name="title">          <string>Emulation</string>         </property> -        <layout class="QHBoxLayout" name="EmulationHorizontalLayout"> +       <layout class="QHBoxLayout" name="EmulationHorizontalLayout"> +        <item> +         <layout class="QVBoxLayout" name="EmulationVerticalLayout"> +          <item> +           <widget class="QCheckBox" name="use_docked_mode"> +            <property name="text"> +             <string>Enable docked mode</string> +            </property> +           </widget> +          </item>            <item> -            <layout class="QVBoxLayout" name="EmulationVerticalLayout"> -              <item> -                <widget class="QCheckBox" name="use_docked_mode"> -                  <property name="text"> -                    <string>Enable docked mode</string> -                  </property> -                </widget> -              </item> -            </layout> +           <widget class="QCheckBox" name="enable_nfc"> +            <property name="text"> +             <string>Enable NFC</string> +            </property> +           </widget>            </item> -        </layout> +         </layout> +        </item> +       </layout>        </widget>       </item>       <item> diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index 36c702195..be9896614 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -60,6 +60,8 @@ static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::Virtual  #include "core/hle/kernel/process.h"  #include "core/hle/service/filesystem/filesystem.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/perf_stats.h"  #include "core/settings.h" @@ -424,6 +426,7 @@ void GMainWindow::ConnectMenuEvents() {      connect(ui.action_Select_SDMC_Directory, &QAction::triggered, this,              [this] { OnMenuSelectEmulatedDirectory(EmulatedDirectoryTarget::SDMC); });      connect(ui.action_Exit, &QAction::triggered, this, &QMainWindow::close); +    connect(ui.action_Load_Amiibo, &QAction::triggered, this, &GMainWindow::OnLoadAmiibo);      // Emulation      connect(ui.action_Start, &QAction::triggered, this, &GMainWindow::OnStartGame); @@ -692,6 +695,7 @@ void GMainWindow::ShutdownGame() {      ui.action_Stop->setEnabled(false);      ui.action_Restart->setEnabled(false);      ui.action_Report_Compatibility->setEnabled(false); +    ui.action_Load_Amiibo->setEnabled(false);      render_window->hide();      game_list->show();      game_list->setFilterFocus(); @@ -1191,6 +1195,7 @@ void GMainWindow::OnStartGame() {      ui.action_Report_Compatibility->setEnabled(true);      discord_rpc->Update(); +    ui.action_Load_Amiibo->setEnabled(true);  }  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() {      AboutDialog aboutDialog(this);      aboutDialog.exec(); @@ -1335,15 +1361,17 @@ void GMainWindow::UpdateStatusBar() {  void GMainWindow::OnCoreError(Core::System::ResultStatus result, std::string details) {      QMessageBox::StandardButton answer;      QString status_message; -    const QString common_message = 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 " -        "following wiki page: <a " -        "href='https://yuzu-emu.org/wiki/" -        "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 " -        "back to the game list? Continuing emulation may result in crashes, corrupted save " -        "data, or other bugs."); +    const QString common_message = +        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 " +           "following wiki page: <a " +           "href='https://yuzu-emu.org/wiki/" +           "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 " +           "back to the game list? Continuing emulation may result in crashes, corrupted save " +           "data, or other bugs.");      switch (result) {      case Core::System::ResultStatus::ErrorSystemFiles: {          QString message = "yuzu was unable to locate a Switch system archive"; @@ -1374,9 +1402,12 @@ void GMainWindow::OnCoreError(Core::System::ResultStatus result, std::string det              this, tr("Fatal Error"),              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: " -               "<a href='https://community.citra-emu.org/t/how-to-upload-the-log-file/296'>How to " -               "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."), +               "<a href='https://community.citra-emu.org/t/how-to-upload-the-log-file/296'>How " +               "to " +               "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);          status_message = "Fatal Error encountered";          break; diff --git a/src/yuzu/main.h b/src/yuzu/main.h index c8cbc0ba8..7c7c223e1 100644 --- a/src/yuzu/main.h +++ b/src/yuzu/main.h @@ -166,6 +166,7 @@ private slots:      void OnMenuSelectEmulatedDirectory(EmulatedDirectoryTarget target);      void OnMenuRecentFile();      void OnConfigure(); +    void OnLoadAmiibo();      void OnAbout();      void OnToggleFilterBar();      void OnDisplayTitleBars(bool); diff --git a/src/yuzu/main.ui b/src/yuzu/main.ui index dffd9c788..48d099591 100644 --- a/src/yuzu/main.ui +++ b/src/yuzu/main.ui @@ -57,8 +57,8 @@        <string>Recent Files</string>       </property>      </widget> -     <addaction name="action_Install_File_NAND" /> -     <addaction name="separator"/> +    <addaction name="action_Install_File_NAND"/> +    <addaction name="separator"/>      <addaction name="action_Load_File"/>      <addaction name="action_Load_Folder"/>      <addaction name="separator"/> @@ -68,6 +68,8 @@      <addaction name="action_Select_NAND_Directory"/>      <addaction name="action_Select_SDMC_Directory"/>      <addaction name="separator"/> +    <addaction name="action_Load_Amiibo"/> +    <addaction name="separator"/>      <addaction name="action_Exit"/>     </widget>     <widget class="QMenu" name="menu_Emulation"> @@ -117,11 +119,14 @@     <addaction name="menu_Tools" />     <addaction name="menu_Help"/>    </widget> -   <action name="action_Install_File_NAND"> -     <property name="text"> -       <string>Install File to NAND...</string> -     </property> -   </action> +  <action name="action_Install_File_NAND"> +   <property name="enabled"> +    <bool>true</bool> +   </property> +   <property name="text"> +    <string>Install File to NAND...</string> +   </property> +  </action>    <action name="action_Load_File">     <property name="text">      <string>Load File...</string> @@ -253,6 +258,14 @@         <string>Restart</string>       </property>     </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">       <property name="enabled">         <bool>false</bool> diff --git a/src/yuzu_cmd/config.cpp b/src/yuzu_cmd/config.cpp index 5e42e48b2..654a15a5c 100644 --- a/src/yuzu_cmd/config.cpp +++ b/src/yuzu_cmd/config.cpp @@ -125,6 +125,7 @@ void Config::ReadValues() {      // System      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");      if (Settings::values.username.empty()) {          Settings::values.username = "yuzu"; diff --git a/src/yuzu_cmd/default_ini.h b/src/yuzu_cmd/default_ini.h index a97b75f7b..e0b223cd6 100644 --- a/src/yuzu_cmd/default_ini.h +++ b/src/yuzu_cmd/default_ini.h @@ -174,6 +174,10 @@ use_virtual_sd =  # 1: Yes, 0 (default): No  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  # yuzu (default)  username = yuzu | 
