mirror of
https://github.com/PabloMK7/citra
synced 2024-11-15 05:08:23 +00:00
nfc: Add Amiibo support (REOPENED) (#4337)
* Initial implementation * Various fixes and new features * Address some review comments * Fixes * Address more comments * Use g_hle_lock * Add more state checking, remove unneeded include * Minor changes
This commit is contained in:
parent
6742472133
commit
dec3fb2dcf
7 changed files with 332 additions and 26 deletions
|
@ -45,6 +45,7 @@
|
||||||
#include "citra_qt/util/clickable_label.h"
|
#include "citra_qt/util/clickable_label.h"
|
||||||
#include "common/common_paths.h"
|
#include "common/common_paths.h"
|
||||||
#include "common/detached_tasks.h"
|
#include "common/detached_tasks.h"
|
||||||
|
#include "common/file_util.h"
|
||||||
#include "common/logging/backend.h"
|
#include "common/logging/backend.h"
|
||||||
#include "common/logging/filter.h"
|
#include "common/logging/filter.h"
|
||||||
#include "common/logging/log.h"
|
#include "common/logging/log.h"
|
||||||
|
@ -58,6 +59,7 @@
|
||||||
#include "core/frontend/applets/default_applets.h"
|
#include "core/frontend/applets/default_applets.h"
|
||||||
#include "core/gdbstub/gdbstub.h"
|
#include "core/gdbstub/gdbstub.h"
|
||||||
#include "core/hle/service/fs/archive.h"
|
#include "core/hle/service/fs/archive.h"
|
||||||
|
#include "core/hle/service/nfc/nfc.h"
|
||||||
#include "core/loader/loader.h"
|
#include "core/loader/loader.h"
|
||||||
#include "core/movie.h"
|
#include "core/movie.h"
|
||||||
#include "core/settings.h"
|
#include "core/settings.h"
|
||||||
|
@ -496,6 +498,8 @@ void GMainWindow::ConnectMenuEvents() {
|
||||||
connect(ui.action_Load_File, &QAction::triggered, this, &GMainWindow::OnMenuLoadFile);
|
connect(ui.action_Load_File, &QAction::triggered, this, &GMainWindow::OnMenuLoadFile);
|
||||||
connect(ui.action_Install_CIA, &QAction::triggered, this, &GMainWindow::OnMenuInstallCIA);
|
connect(ui.action_Install_CIA, &QAction::triggered, this, &GMainWindow::OnMenuInstallCIA);
|
||||||
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);
|
||||||
|
connect(ui.action_Remove_Amiibo, &QAction::triggered, this, &GMainWindow::OnRemoveAmiibo);
|
||||||
|
|
||||||
// Emulation
|
// Emulation
|
||||||
connect(ui.action_Start, &QAction::triggered, this, &GMainWindow::OnStartGame);
|
connect(ui.action_Start, &QAction::triggered, this, &GMainWindow::OnStartGame);
|
||||||
|
@ -848,6 +852,8 @@ void GMainWindow::ShutdownGame() {
|
||||||
ui.action_Pause->setEnabled(false);
|
ui.action_Pause->setEnabled(false);
|
||||||
ui.action_Stop->setEnabled(false);
|
ui.action_Stop->setEnabled(false);
|
||||||
ui.action_Restart->setEnabled(false);
|
ui.action_Restart->setEnabled(false);
|
||||||
|
ui.action_Load_Amiibo->setEnabled(false);
|
||||||
|
ui.action_Remove_Amiibo->setEnabled(false);
|
||||||
ui.action_Report_Compatibility->setEnabled(false);
|
ui.action_Report_Compatibility->setEnabled(false);
|
||||||
ui.action_Enable_Frame_Advancing->setEnabled(false);
|
ui.action_Enable_Frame_Advancing->setEnabled(false);
|
||||||
ui.action_Enable_Frame_Advancing->setChecked(false);
|
ui.action_Enable_Frame_Advancing->setChecked(false);
|
||||||
|
@ -1135,6 +1141,7 @@ void GMainWindow::OnStartGame() {
|
||||||
ui.action_Pause->setEnabled(true);
|
ui.action_Pause->setEnabled(true);
|
||||||
ui.action_Stop->setEnabled(true);
|
ui.action_Stop->setEnabled(true);
|
||||||
ui.action_Restart->setEnabled(true);
|
ui.action_Restart->setEnabled(true);
|
||||||
|
ui.action_Load_Amiibo->setEnabled(true);
|
||||||
ui.action_Report_Compatibility->setEnabled(true);
|
ui.action_Report_Compatibility->setEnabled(true);
|
||||||
ui.action_Enable_Frame_Advancing->setEnabled(true);
|
ui.action_Enable_Frame_Advancing->setEnabled(true);
|
||||||
|
|
||||||
|
@ -1289,6 +1296,40 @@ 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::NFC::Module::Interface>("nfc:u");
|
||||||
|
if (nfc != nullptr) {
|
||||||
|
Service::NFC::AmiiboData amiibo_data{};
|
||||||
|
auto nfc_file = FileUtil::IOFile(filename.toStdString(), "rb");
|
||||||
|
std::size_t read_length =
|
||||||
|
nfc_file.ReadBytes(&amiibo_data, sizeof(Service::NFC::AmiiboData));
|
||||||
|
if (read_length != sizeof(Service::NFC::AmiiboData)) {
|
||||||
|
LOG_ERROR(Frontend, "Amiibo file size is incorrect");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
nfc->LoadAmiibo(amiibo_data);
|
||||||
|
ui.action_Remove_Amiibo->setEnabled(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void GMainWindow::OnRemoveAmiibo() {
|
||||||
|
Core::System& system{Core::System::GetInstance()};
|
||||||
|
Service::SM::ServiceManager& sm = system.ServiceManager();
|
||||||
|
auto nfc = sm.GetService<Service::NFC::Module::Interface>("nfc:u");
|
||||||
|
if (nfc != nullptr) {
|
||||||
|
nfc->RemoveAmiibo();
|
||||||
|
ui.action_Remove_Amiibo->setEnabled(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void GMainWindow::OnToggleFilterBar() {
|
void GMainWindow::OnToggleFilterBar() {
|
||||||
game_list->setFilterVisible(ui.action_Show_Filter_Bar->isChecked());
|
game_list->setFilterVisible(ui.action_Show_Filter_Bar->isChecked());
|
||||||
if (ui.action_Show_Filter_Bar->isChecked()) {
|
if (ui.action_Show_Filter_Bar->isChecked()) {
|
||||||
|
|
|
@ -166,6 +166,8 @@ private slots:
|
||||||
void OnCIAInstallFinished();
|
void OnCIAInstallFinished();
|
||||||
void OnMenuRecentFile();
|
void OnMenuRecentFile();
|
||||||
void OnConfigure();
|
void OnConfigure();
|
||||||
|
void OnLoadAmiibo();
|
||||||
|
void OnRemoveAmiibo();
|
||||||
void OnToggleFilterBar();
|
void OnToggleFilterBar();
|
||||||
void OnDisplayTitleBars(bool);
|
void OnDisplayTitleBars(bool);
|
||||||
void ToggleFullscreen();
|
void ToggleFullscreen();
|
||||||
|
|
|
@ -57,11 +57,20 @@
|
||||||
<string>Recent Files</string>
|
<string>Recent Files</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
|
<widget class="QMenu" name="menu_Amiibo">
|
||||||
|
<property name="title">
|
||||||
|
<string>Amiibo</string>
|
||||||
|
</property>
|
||||||
|
<addaction name="action_Load_Amiibo"/>
|
||||||
|
<addaction name="action_Remove_Amiibo"/>
|
||||||
|
</widget>
|
||||||
<addaction name="action_Load_File"/>
|
<addaction name="action_Load_File"/>
|
||||||
<addaction name="action_Install_CIA"/>
|
<addaction name="action_Install_CIA"/>
|
||||||
<addaction name="separator"/>
|
<addaction name="separator"/>
|
||||||
<addaction name="menu_recent_files"/>
|
<addaction name="menu_recent_files"/>
|
||||||
<addaction name="separator"/>
|
<addaction name="separator"/>
|
||||||
|
<addaction name="menu_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">
|
||||||
|
@ -415,6 +424,22 @@
|
||||||
<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...</string>
|
||||||
|
</property>
|
||||||
|
</action>
|
||||||
|
<action name="action_Remove_Amiibo">
|
||||||
|
<property name="enabled">
|
||||||
|
<bool>false</bool>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Remove</string>
|
||||||
|
</property>
|
||||||
|
</action>
|
||||||
</widget>
|
</widget>
|
||||||
<resources/>
|
<resources/>
|
||||||
<connections/>
|
<connections/>
|
||||||
|
|
|
@ -5,19 +5,61 @@
|
||||||
#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/nfc/nfc.h"
|
#include "core/hle/service/nfc/nfc.h"
|
||||||
#include "core/hle/service/nfc/nfc_m.h"
|
#include "core/hle/service/nfc/nfc_m.h"
|
||||||
#include "core/hle/service/nfc/nfc_u.h"
|
#include "core/hle/service/nfc/nfc_u.h"
|
||||||
|
|
||||||
namespace Service::NFC {
|
namespace Service::NFC {
|
||||||
|
|
||||||
|
struct TagInfo {
|
||||||
|
u16_le id_offset_size;
|
||||||
|
u8 unk1;
|
||||||
|
u8 unk2;
|
||||||
|
std::array<u8, 7> uuid;
|
||||||
|
INSERT_PADDING_BYTES(0x20);
|
||||||
|
};
|
||||||
|
static_assert(sizeof(TagInfo) == 0x2C, "TagInfo is an invalid size");
|
||||||
|
|
||||||
|
struct AmiiboConfig {
|
||||||
|
u16_le lastwritedate_year;
|
||||||
|
u8 lastwritedate_month;
|
||||||
|
u8 lastwritedate_day;
|
||||||
|
u16_le write_counter;
|
||||||
|
std::array<u8, 3> characterID;
|
||||||
|
u16_le amiiboID;
|
||||||
|
u8 type;
|
||||||
|
u8 pagex4_byte3;
|
||||||
|
u16_le appdata_size;
|
||||||
|
INSERT_PADDING_BYTES(0x30);
|
||||||
|
};
|
||||||
|
static_assert(sizeof(AmiiboConfig) == 0x40, "AmiiboConfig is an invalid size");
|
||||||
|
|
||||||
|
struct IdentificationBlockReply {
|
||||||
|
u16_le char_id;
|
||||||
|
u8 char_variant;
|
||||||
|
u8 series;
|
||||||
|
u16_le model_number;
|
||||||
|
u8 figure_type;
|
||||||
|
INSERT_PADDING_BYTES(0x2F);
|
||||||
|
};
|
||||||
|
static_assert(sizeof(IdentificationBlockReply) == 0x36,
|
||||||
|
"IdentificationBlockReply is an invalid size");
|
||||||
|
|
||||||
void Module::Interface::Initialize(Kernel::HLERequestContext& ctx) {
|
void Module::Interface::Initialize(Kernel::HLERequestContext& ctx) {
|
||||||
IPC::RequestParser rp(ctx, 0x01, 1, 0);
|
IPC::RequestParser rp(ctx, 0x01, 1, 0);
|
||||||
u8 param = rp.Pop<u8>();
|
u8 param = rp.Pop<u8>();
|
||||||
|
|
||||||
|
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
|
||||||
|
if (nfc->nfc_tag_state != TagState::NotInitialized) {
|
||||||
|
LOG_ERROR(Service_NFC, "Invalid TagState {}", static_cast<int>(nfc->nfc_tag_state.load()));
|
||||||
|
rb.Push(ResultCode(ErrCodes::CommandInvalidForState, ErrorModule::NFC,
|
||||||
|
ErrorSummary::InvalidState, ErrorLevel::Status));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
nfc->nfc_tag_state = TagState::NotScanning;
|
nfc->nfc_tag_state = TagState::NotScanning;
|
||||||
|
|
||||||
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
|
|
||||||
rb.Push(RESULT_SUCCESS);
|
rb.Push(RESULT_SUCCESS);
|
||||||
LOG_WARNING(Service_NFC, "(STUBBED) called, param={}", param);
|
LOG_WARNING(Service_NFC, "(STUBBED) called, param={}", param);
|
||||||
}
|
}
|
||||||
|
@ -53,35 +95,83 @@ void Module::Interface::StartTagScanning(Kernel::HLERequestContext& ctx) {
|
||||||
IPC::RequestParser rp(ctx, 0x05, 1, 0); // 0x00050040
|
IPC::RequestParser rp(ctx, 0x05, 1, 0); // 0x00050040
|
||||||
u16 in_val = rp.Pop<u16>();
|
u16 in_val = rp.Pop<u16>();
|
||||||
|
|
||||||
ResultCode result = RESULT_SUCCESS;
|
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
|
||||||
|
if (nfc->nfc_tag_state != TagState::NotScanning &&
|
||||||
// TODO(shinyquagsire23): Implement NFC tag detection, for now stub result
|
nfc->nfc_tag_state != TagState::TagOutOfRange) {
|
||||||
result = ResultCode(ErrCodes::CommandInvalidForState, ErrorModule::NFC,
|
LOG_ERROR(Service_NFC, "Invalid TagState {}", static_cast<int>(nfc->nfc_tag_state.load()));
|
||||||
ErrorSummary::InvalidState, ErrorLevel::Status);
|
rb.Push(ResultCode(ErrCodes::CommandInvalidForState, ErrorModule::NFC,
|
||||||
|
ErrorSummary::InvalidState, ErrorLevel::Status));
|
||||||
if (result == RESULT_SUCCESS) {
|
return;
|
||||||
nfc->nfc_tag_state = TagState::TagInRange;
|
|
||||||
nfc->tag_in_range_event->Signal();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
|
nfc->nfc_tag_state = TagState::Scanning;
|
||||||
rb.Push(result);
|
|
||||||
|
rb.Push(RESULT_SUCCESS);
|
||||||
LOG_WARNING(Service_NFC, "(STUBBED) called, in_val={:04x}", in_val);
|
LOG_WARNING(Service_NFC, "(STUBBED) called, in_val={:04x}", in_val);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Module::Interface::GetTagInfo(Kernel::HLERequestContext& ctx) {
|
||||||
|
IPC::RequestParser rp(ctx, 0x11, 0, 0);
|
||||||
|
|
||||||
|
if (nfc->nfc_tag_state != TagState::TagInRange &&
|
||||||
|
nfc->nfc_tag_state != TagState::TagDataLoaded && nfc->nfc_tag_state != TagState::Unknown6) {
|
||||||
|
LOG_ERROR(Service_NFC, "Invalid TagState {}", static_cast<int>(nfc->nfc_tag_state.load()));
|
||||||
|
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
|
||||||
|
rb.Push(ResultCode(ErrCodes::CommandInvalidForState, ErrorModule::NFC,
|
||||||
|
ErrorSummary::InvalidState, ErrorLevel::Status));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
TagInfo tag_info{};
|
||||||
|
tag_info.uuid = nfc->amiibo_data.uuid;
|
||||||
|
tag_info.id_offset_size = tag_info.uuid.size();
|
||||||
|
tag_info.unk1 = 0x0;
|
||||||
|
tag_info.unk2 = 0x2;
|
||||||
|
|
||||||
|
IPC::RequestBuilder rb = rp.MakeBuilder(12, 0);
|
||||||
|
rb.Push(RESULT_SUCCESS);
|
||||||
|
rb.PushRaw<TagInfo>(tag_info);
|
||||||
|
LOG_WARNING(Service_NFC, "(STUBBED) called");
|
||||||
|
}
|
||||||
|
|
||||||
|
void Module::Interface::GetAmiiboConfig(Kernel::HLERequestContext& ctx) {
|
||||||
|
IPC::RequestParser rp(ctx, 0x18, 0, 0);
|
||||||
|
|
||||||
|
AmiiboConfig amiibo_config{};
|
||||||
|
amiibo_config.lastwritedate_year = 2017;
|
||||||
|
amiibo_config.lastwritedate_month = 10;
|
||||||
|
amiibo_config.lastwritedate_day = 10;
|
||||||
|
// TODO(FearlessTobi): Find the right values for the struct
|
||||||
|
|
||||||
|
IPC::RequestBuilder rb = rp.MakeBuilder(17, 0);
|
||||||
|
rb.Push(RESULT_SUCCESS);
|
||||||
|
rb.PushRaw<AmiiboConfig>(amiibo_config);
|
||||||
|
LOG_WARNING(Service_NFC, "(STUBBED) called");
|
||||||
|
}
|
||||||
|
|
||||||
void Module::Interface::StopTagScanning(Kernel::HLERequestContext& ctx) {
|
void Module::Interface::StopTagScanning(Kernel::HLERequestContext& ctx) {
|
||||||
IPC::RequestParser rp(ctx, 0x06, 0, 0);
|
IPC::RequestParser rp(ctx, 0x06, 0, 0);
|
||||||
|
|
||||||
|
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
|
||||||
|
if (nfc->nfc_tag_state == TagState::NotInitialized ||
|
||||||
|
nfc->nfc_tag_state == TagState::NotScanning) {
|
||||||
|
LOG_ERROR(Service_NFC, "Invalid TagState {}", static_cast<int>(nfc->nfc_tag_state.load()));
|
||||||
|
rb.Push(ResultCode(ErrCodes::CommandInvalidForState, ErrorModule::NFC,
|
||||||
|
ErrorSummary::InvalidState, ErrorLevel::Status));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
nfc->nfc_tag_state = TagState::NotScanning;
|
nfc->nfc_tag_state = TagState::NotScanning;
|
||||||
|
|
||||||
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
|
|
||||||
rb.Push(RESULT_SUCCESS);
|
rb.Push(RESULT_SUCCESS);
|
||||||
LOG_WARNING(Service_NFC, "(STUBBED) called");
|
LOG_DEBUG(Service_NFC, "called");
|
||||||
}
|
}
|
||||||
|
|
||||||
void Module::Interface::LoadAmiiboData(Kernel::HLERequestContext& ctx) {
|
void Module::Interface::LoadAmiiboData(Kernel::HLERequestContext& ctx) {
|
||||||
IPC::RequestParser rp(ctx, 0x07, 0, 0);
|
IPC::RequestParser rp(ctx, 0x07, 0, 0);
|
||||||
|
|
||||||
|
// TODO(FearlessTobi): Add state checking when this function gets properly implemented
|
||||||
|
|
||||||
nfc->nfc_tag_state = TagState::TagDataLoaded;
|
nfc->nfc_tag_state = TagState::TagDataLoaded;
|
||||||
|
|
||||||
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
|
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
|
||||||
|
@ -92,29 +182,52 @@ void Module::Interface::LoadAmiiboData(Kernel::HLERequestContext& ctx) {
|
||||||
void Module::Interface::ResetTagScanState(Kernel::HLERequestContext& ctx) {
|
void Module::Interface::ResetTagScanState(Kernel::HLERequestContext& ctx) {
|
||||||
IPC::RequestParser rp(ctx, 0x08, 0, 0);
|
IPC::RequestParser rp(ctx, 0x08, 0, 0);
|
||||||
|
|
||||||
nfc->nfc_tag_state = TagState::NotScanning;
|
|
||||||
|
|
||||||
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
|
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
|
||||||
|
if (nfc->nfc_tag_state != TagState::TagDataLoaded && nfc->nfc_tag_state != TagState::Unknown6) {
|
||||||
|
LOG_ERROR(Service_NFC, "Invalid TagState {}", static_cast<int>(nfc->nfc_tag_state.load()));
|
||||||
|
rb.Push(ResultCode(ErrCodes::CommandInvalidForState, ErrorModule::NFC,
|
||||||
|
ErrorSummary::InvalidState, ErrorLevel::Status));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
nfc->nfc_tag_state = TagState::TagInRange;
|
||||||
|
|
||||||
rb.Push(RESULT_SUCCESS);
|
rb.Push(RESULT_SUCCESS);
|
||||||
LOG_WARNING(Service_NFC, "(STUBBED) called");
|
LOG_DEBUG(Service_NFC, "called");
|
||||||
}
|
}
|
||||||
|
|
||||||
void Module::Interface::GetTagInRangeEvent(Kernel::HLERequestContext& ctx) {
|
void Module::Interface::GetTagInRangeEvent(Kernel::HLERequestContext& ctx) {
|
||||||
IPC::RequestParser rp(ctx, 0x0B, 0, 0);
|
IPC::RequestParser rp(ctx, 0x0B, 0, 0);
|
||||||
|
|
||||||
|
if (nfc->nfc_tag_state != TagState::NotScanning) {
|
||||||
|
LOG_ERROR(Service_NFC, "Invalid TagState {}", static_cast<int>(nfc->nfc_tag_state.load()));
|
||||||
|
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
|
||||||
|
rb.Push(ResultCode(ErrCodes::CommandInvalidForState, ErrorModule::NFC,
|
||||||
|
ErrorSummary::InvalidState, ErrorLevel::Status));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
IPC::RequestBuilder rb = rp.MakeBuilder(1, 2);
|
IPC::RequestBuilder rb = rp.MakeBuilder(1, 2);
|
||||||
rb.Push(RESULT_SUCCESS);
|
rb.Push(RESULT_SUCCESS);
|
||||||
rb.PushCopyObjects(nfc->tag_in_range_event);
|
rb.PushCopyObjects(nfc->tag_in_range_event);
|
||||||
LOG_WARNING(Service_NFC, "(STUBBED) called");
|
LOG_DEBUG(Service_NFC, "called");
|
||||||
}
|
}
|
||||||
|
|
||||||
void Module::Interface::GetTagOutOfRangeEvent(Kernel::HLERequestContext& ctx) {
|
void Module::Interface::GetTagOutOfRangeEvent(Kernel::HLERequestContext& ctx) {
|
||||||
IPC::RequestParser rp(ctx, 0x0C, 0, 0);
|
IPC::RequestParser rp(ctx, 0x0C, 0, 0);
|
||||||
|
|
||||||
|
if (nfc->nfc_tag_state != TagState::NotScanning) {
|
||||||
|
LOG_ERROR(Service_NFC, "Invalid TagState {}", static_cast<int>(nfc->nfc_tag_state.load()));
|
||||||
|
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
|
||||||
|
rb.Push(ResultCode(ErrCodes::CommandInvalidForState, ErrorModule::NFC,
|
||||||
|
ErrorSummary::InvalidState, ErrorLevel::Status));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
IPC::RequestBuilder rb = rp.MakeBuilder(1, 2);
|
IPC::RequestBuilder rb = rp.MakeBuilder(1, 2);
|
||||||
rb.Push(RESULT_SUCCESS);
|
rb.Push(RESULT_SUCCESS);
|
||||||
rb.PushCopyObjects(nfc->tag_out_of_range_event);
|
rb.PushCopyObjects(nfc->tag_out_of_range_event);
|
||||||
LOG_WARNING(Service_NFC, "(STUBBED) called");
|
LOG_DEBUG(Service_NFC, "called");
|
||||||
}
|
}
|
||||||
|
|
||||||
void Module::Interface::GetTagState(Kernel::HLERequestContext& ctx) {
|
void Module::Interface::GetTagState(Kernel::HLERequestContext& ctx) {
|
||||||
|
@ -122,8 +235,8 @@ void Module::Interface::GetTagState(Kernel::HLERequestContext& ctx) {
|
||||||
|
|
||||||
IPC::RequestBuilder rb = rp.MakeBuilder(2, 0);
|
IPC::RequestBuilder rb = rp.MakeBuilder(2, 0);
|
||||||
rb.Push(RESULT_SUCCESS);
|
rb.Push(RESULT_SUCCESS);
|
||||||
rb.PushEnum(nfc->nfc_tag_state);
|
rb.PushEnum(nfc->nfc_tag_state.load());
|
||||||
LOG_DEBUG(Service_NFC, "(STUBBED) called");
|
LOG_DEBUG(Service_NFC, "called");
|
||||||
}
|
}
|
||||||
|
|
||||||
void Module::Interface::CommunicationGetStatus(Kernel::HLERequestContext& ctx) {
|
void Module::Interface::CommunicationGetStatus(Kernel::HLERequestContext& ctx) {
|
||||||
|
@ -135,6 +248,65 @@ void Module::Interface::CommunicationGetStatus(Kernel::HLERequestContext& ctx) {
|
||||||
LOG_DEBUG(Service_NFC, "(STUBBED) called");
|
LOG_DEBUG(Service_NFC, "(STUBBED) called");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Module::Interface::Unknown0x1A(Kernel::HLERequestContext& ctx) {
|
||||||
|
IPC::RequestParser rp(ctx, 0x1A, 0, 0);
|
||||||
|
|
||||||
|
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
|
||||||
|
if (nfc->nfc_tag_state != TagState::TagInRange) {
|
||||||
|
LOG_ERROR(Service_NFC, "Invalid TagState {}", static_cast<int>(nfc->nfc_tag_state.load()));
|
||||||
|
rb.Push(ResultCode(ErrCodes::CommandInvalidForState, ErrorModule::NFC,
|
||||||
|
ErrorSummary::InvalidState, ErrorLevel::Status));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
nfc->nfc_tag_state = TagState::Unknown6;
|
||||||
|
|
||||||
|
rb.Push(RESULT_SUCCESS);
|
||||||
|
LOG_DEBUG(Service_NFC, "called");
|
||||||
|
}
|
||||||
|
|
||||||
|
void Module::Interface::GetIdentificationBlock(Kernel::HLERequestContext& ctx) {
|
||||||
|
IPC::RequestParser rp(ctx, 0x1B, 0, 0);
|
||||||
|
|
||||||
|
if (nfc->nfc_tag_state != TagState::TagDataLoaded && nfc->nfc_tag_state != TagState::Unknown6) {
|
||||||
|
LOG_ERROR(Service_NFC, "Invalid TagState {}", static_cast<int>(nfc->nfc_tag_state.load()));
|
||||||
|
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
|
||||||
|
rb.Push(ResultCode(ErrCodes::CommandInvalidForState, ErrorModule::NFC,
|
||||||
|
ErrorSummary::InvalidState, ErrorLevel::Status));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
IdentificationBlockReply identification_block_reply{};
|
||||||
|
identification_block_reply.char_id = nfc->amiibo_data.char_id;
|
||||||
|
identification_block_reply.char_variant = nfc->amiibo_data.char_variant;
|
||||||
|
identification_block_reply.series = nfc->amiibo_data.series;
|
||||||
|
identification_block_reply.model_number = nfc->amiibo_data.model_number;
|
||||||
|
identification_block_reply.figure_type = nfc->amiibo_data.figure_type;
|
||||||
|
|
||||||
|
IPC::RequestBuilder rb = rp.MakeBuilder(0x1F, 0);
|
||||||
|
rb.Push(RESULT_SUCCESS);
|
||||||
|
rb.PushRaw<IdentificationBlockReply>(identification_block_reply);
|
||||||
|
LOG_DEBUG(Service_NFC, "called");
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<Module> Module::Interface::GetModule() const {
|
||||||
|
return nfc;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Module::Interface::LoadAmiibo(const AmiiboData& amiibo_data) {
|
||||||
|
std::lock_guard lock(HLE::g_hle_lock);
|
||||||
|
nfc->amiibo_data = amiibo_data;
|
||||||
|
nfc->nfc_tag_state = Service::NFC::TagState::TagInRange;
|
||||||
|
nfc->tag_in_range_event->Signal();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Module::Interface::RemoveAmiibo() {
|
||||||
|
std::lock_guard lock(HLE::g_hle_lock);
|
||||||
|
nfc->nfc_tag_state = Service::NFC::TagState::TagOutOfRange;
|
||||||
|
nfc->tag_out_of_range_event->Signal();
|
||||||
|
nfc->amiibo_data = {};
|
||||||
|
}
|
||||||
|
|
||||||
Module::Interface::Interface(std::shared_ptr<Module> nfc, const char* name, u32 max_session)
|
Module::Interface::Interface(std::shared_ptr<Module> nfc, const char* name, u32 max_session)
|
||||||
: ServiceFramework(name, max_session), nfc(std::move(nfc)) {}
|
: ServiceFramework(name, max_session), nfc(std::move(nfc)) {}
|
||||||
|
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <atomic>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include "common/common_types.h"
|
#include "common/common_types.h"
|
||||||
#include "core/hle/kernel/kernel.h"
|
#include "core/hle/kernel/kernel.h"
|
||||||
|
@ -25,6 +26,19 @@ enum {
|
||||||
};
|
};
|
||||||
} // namespace ErrCodes
|
} // namespace ErrCodes
|
||||||
|
|
||||||
|
// TODO(FearlessTobi): Add more members to this struct
|
||||||
|
struct AmiiboData {
|
||||||
|
std::array<u8, 7> uuid;
|
||||||
|
INSERT_PADDING_BYTES(0x4D);
|
||||||
|
u16_le char_id;
|
||||||
|
u8 char_variant;
|
||||||
|
u8 figure_type;
|
||||||
|
u16_be model_number;
|
||||||
|
u8 series;
|
||||||
|
INSERT_PADDING_BYTES(0x1C1);
|
||||||
|
};
|
||||||
|
static_assert(sizeof(AmiiboData) == 0x21C, "AmiiboData is an invalid size");
|
||||||
|
|
||||||
enum class TagState : u8 {
|
enum class TagState : u8 {
|
||||||
NotInitialized = 0,
|
NotInitialized = 0,
|
||||||
NotScanning = 1,
|
NotScanning = 1,
|
||||||
|
@ -32,6 +46,7 @@ enum class TagState : u8 {
|
||||||
TagInRange = 3,
|
TagInRange = 3,
|
||||||
TagOutOfRange = 4,
|
TagOutOfRange = 4,
|
||||||
TagDataLoaded = 5,
|
TagDataLoaded = 5,
|
||||||
|
Unknown6 = 6,
|
||||||
};
|
};
|
||||||
|
|
||||||
enum class CommunicationStatus : u8 {
|
enum class CommunicationStatus : u8 {
|
||||||
|
@ -49,6 +64,12 @@ public:
|
||||||
Interface(std::shared_ptr<Module> nfc, const char* name, u32 max_session);
|
Interface(std::shared_ptr<Module> nfc, const char* name, u32 max_session);
|
||||||
~Interface();
|
~Interface();
|
||||||
|
|
||||||
|
std::shared_ptr<Module> GetModule() const;
|
||||||
|
|
||||||
|
void LoadAmiibo(const AmiiboData& amiibo_data);
|
||||||
|
|
||||||
|
void RemoveAmiibo();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
/**
|
/**
|
||||||
* NFC::Initialize service function
|
* NFC::Initialize service function
|
||||||
|
@ -167,6 +188,45 @@ public:
|
||||||
*/
|
*/
|
||||||
void CommunicationGetStatus(Kernel::HLERequestContext& ctx);
|
void CommunicationGetStatus(Kernel::HLERequestContext& ctx);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* NFC::GetTagInfo service function
|
||||||
|
* Inputs:
|
||||||
|
* 0 : Header code [0x00110000]
|
||||||
|
* Outputs:
|
||||||
|
* 1 : Result of function, 0 on success, otherwise error code
|
||||||
|
* 2-12 : 0x2C-byte struct
|
||||||
|
*/
|
||||||
|
void GetTagInfo(Kernel::HLERequestContext& ctx);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* NFC::GetAmiiboConfig service function
|
||||||
|
* Inputs:
|
||||||
|
* 0 : Header code [0x00180000]
|
||||||
|
* Outputs:
|
||||||
|
* 1 : Result of function, 0 on success, otherwise error code
|
||||||
|
* 2-17 : 0x40-byte config struct
|
||||||
|
*/
|
||||||
|
void GetAmiiboConfig(Kernel::HLERequestContext& ctx);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* NFC::Unknown0x1A service function
|
||||||
|
* Inputs:
|
||||||
|
* 0 : Header code [0x001A0000]
|
||||||
|
* Outputs:
|
||||||
|
* 1 : Result of function, 0 on success, otherwise error code
|
||||||
|
*/
|
||||||
|
void Unknown0x1A(Kernel::HLERequestContext& ctx);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* NFC::GetIdentificationBlock service function
|
||||||
|
* Inputs:
|
||||||
|
* 0 : Header code [0x001B0000]
|
||||||
|
* Outputs:
|
||||||
|
* 1 : Result of function, 0 on success, otherwise error code
|
||||||
|
* 2-31 : 0x36-byte struct
|
||||||
|
*/
|
||||||
|
void GetIdentificationBlock(Kernel::HLERequestContext& ctx);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::shared_ptr<Module> nfc;
|
std::shared_ptr<Module> nfc;
|
||||||
};
|
};
|
||||||
|
@ -174,8 +234,10 @@ public:
|
||||||
private:
|
private:
|
||||||
Kernel::SharedPtr<Kernel::Event> tag_in_range_event;
|
Kernel::SharedPtr<Kernel::Event> tag_in_range_event;
|
||||||
Kernel::SharedPtr<Kernel::Event> tag_out_of_range_event;
|
Kernel::SharedPtr<Kernel::Event> tag_out_of_range_event;
|
||||||
TagState nfc_tag_state = TagState::NotInitialized;
|
std::atomic<TagState> nfc_tag_state = TagState::NotInitialized;
|
||||||
CommunicationStatus nfc_status = CommunicationStatus::NfcInitialized;
|
CommunicationStatus nfc_status = CommunicationStatus::NfcInitialized;
|
||||||
|
|
||||||
|
AmiiboData amiibo_data{};
|
||||||
};
|
};
|
||||||
|
|
||||||
void InstallInterfaces(Core::System& system);
|
void InstallInterfaces(Core::System& system);
|
||||||
|
|
|
@ -24,15 +24,17 @@ NFC_M::NFC_M(std::shared_ptr<Module> nfc) : Module::Interface(std::move(nfc), "n
|
||||||
{0x000D0000, &NFC_M::GetTagState, "GetTagState"},
|
{0x000D0000, &NFC_M::GetTagState, "GetTagState"},
|
||||||
{0x000F0000, &NFC_M::CommunicationGetStatus, "CommunicationGetStatus"},
|
{0x000F0000, &NFC_M::CommunicationGetStatus, "CommunicationGetStatus"},
|
||||||
{0x00100000, nullptr, "GetTagInfo2"},
|
{0x00100000, nullptr, "GetTagInfo2"},
|
||||||
{0x00110000, nullptr, "GetTagInfo"},
|
{0x00110000, &NFC_M::GetTagInfo, "GetTagInfo"},
|
||||||
{0x00120000, nullptr, "CommunicationGetResult"},
|
{0x00120000, nullptr, "CommunicationGetResult"},
|
||||||
{0x00130040, nullptr, "OpenAppData"},
|
{0x00130040, nullptr, "OpenAppData"},
|
||||||
{0x00140384, nullptr, "InitializeWriteAppData"},
|
{0x00140384, nullptr, "InitializeWriteAppData"},
|
||||||
{0x00150040, nullptr, "ReadAppData"},
|
{0x00150040, nullptr, "ReadAppData"},
|
||||||
{0x00160242, nullptr, "WriteAppData"},
|
{0x00160242, nullptr, "WriteAppData"},
|
||||||
{0x00170000, nullptr, "GetAmiiboSettings"},
|
{0x00170000, nullptr, "GetAmiiboSettings"},
|
||||||
{0x00180000, nullptr, "GetAmiiboConfig"},
|
{0x00180000, &NFC_M::GetAmiiboConfig, "GetAmiiboConfig"},
|
||||||
{0x00190000, nullptr, "GetAppDataInitStruct"},
|
{0x00190000, nullptr, "GetAppDataInitStruct"},
|
||||||
|
{0x001A0000, &NFC_M::Unknown0x1A, "Unknown0x1A"},
|
||||||
|
{0x001B0000, &NFC_M::GetIdentificationBlock, "GetIdentificationBlock"},
|
||||||
// nfc:m
|
// nfc:m
|
||||||
{0x04040A40, nullptr, "SetAmiiboSettings"}
|
{0x04040A40, nullptr, "SetAmiiboSettings"}
|
||||||
// clang-format on
|
// clang-format on
|
||||||
|
|
|
@ -23,15 +23,17 @@ NFC_U::NFC_U(std::shared_ptr<Module> nfc) : Module::Interface(std::move(nfc), "n
|
||||||
{0x000D0000, &NFC_U::GetTagState, "GetTagState"},
|
{0x000D0000, &NFC_U::GetTagState, "GetTagState"},
|
||||||
{0x000F0000, &NFC_U::CommunicationGetStatus, "CommunicationGetStatus"},
|
{0x000F0000, &NFC_U::CommunicationGetStatus, "CommunicationGetStatus"},
|
||||||
{0x00100000, nullptr, "GetTagInfo2"},
|
{0x00100000, nullptr, "GetTagInfo2"},
|
||||||
{0x00110000, nullptr, "GetTagInfo"},
|
{0x00110000, &NFC_U::GetTagInfo, "GetTagInfo"},
|
||||||
{0x00120000, nullptr, "CommunicationGetResult"},
|
{0x00120000, nullptr, "CommunicationGetResult"},
|
||||||
{0x00130040, nullptr, "OpenAppData"},
|
{0x00130040, nullptr, "OpenAppData"},
|
||||||
{0x00140384, nullptr, "InitializeWriteAppData"},
|
{0x00140384, nullptr, "InitializeWriteAppData"},
|
||||||
{0x00150040, nullptr, "ReadAppData"},
|
{0x00150040, nullptr, "ReadAppData"},
|
||||||
{0x00160242, nullptr, "WriteAppData"},
|
{0x00160242, nullptr, "WriteAppData"},
|
||||||
{0x00170000, nullptr, "GetAmiiboSettings"},
|
{0x00170000, nullptr, "GetAmiiboSettings"},
|
||||||
{0x00180000, nullptr, "GetAmiiboConfig"},
|
{0x00180000, &NFC_U::GetAmiiboConfig, "GetAmiiboConfig"},
|
||||||
{0x00190000, nullptr, "GetAppDataInitStruct"},
|
{0x00190000, nullptr, "GetAppDataInitStruct"},
|
||||||
|
{0x001A0000, &NFC_U::Unknown0x1A, "Unknown0x1A"},
|
||||||
|
{0x001B0000, &NFC_U::GetIdentificationBlock, "GetIdentificationBlock"},
|
||||||
// clang-format on
|
// clang-format on
|
||||||
};
|
};
|
||||||
RegisterHandlers(functions);
|
RegisterHandlers(functions);
|
||||||
|
|
Loading…
Reference in a new issue