diff --git a/CMakeLists.txt b/CMakeLists.txt index d0ac2804..b4ca6745 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -137,7 +137,7 @@ set(SERVICE_SOURCE_FILES src/core/services/service_manager.cpp src/core/services src/core/services/y2r.cpp src/core/services/cam.cpp src/core/services/ldr_ro.cpp src/core/services/act.cpp src/core/services/nfc.cpp src/core/services/dlp_srvr.cpp src/core/services/ir_user.cpp src/core/services/http.cpp src/core/services/soc.cpp - src/core/services/ssl.cpp src/core/services/news_u.cpp + src/core/services/ssl.cpp src/core/services/news_u.cpp src/core/services/amiibo_device.cpp ) set(PICA_SOURCE_FILES src/core/PICA/gpu.cpp src/core/PICA/regs.cpp src/core/PICA/shader_unit.cpp src/core/PICA/shader_interpreter.cpp src/core/PICA/dynapica/shader_rec.cpp @@ -181,6 +181,7 @@ set(HEADER_FILES include/emulator.hpp include/helpers.hpp include/termcolor.hpp include/fs/romfs.hpp include/fs/ivfc.hpp include/discord_rpc.hpp include/services/http.hpp include/result/result_cfg.hpp include/applets/applet.hpp include/applets/mii_selector.hpp include/math_util.hpp include/services/soc.hpp include/services/news_u.hpp include/applets/software_keyboard.hpp include/applets/applet_manager.hpp include/fs/archive_user_save_data.hpp + include/services/amiibo_device.hpp ) cmrc_add_resource_library( diff --git a/include/emulator.hpp b/include/emulator.hpp index 770d78df..47358a95 100644 --- a/include/emulator.hpp +++ b/include/emulator.hpp @@ -98,6 +98,7 @@ class Emulator { void pause(); // Pause the emulator void togglePause(); + bool loadAmiibo(const std::filesystem::path& path); bool loadROM(const std::filesystem::path& path); bool loadNCSD(const std::filesystem::path& path, ROMType type); bool load3DSX(const std::filesystem::path& path); diff --git a/include/fs/archive_ext_save_data.hpp b/include/fs/archive_ext_save_data.hpp index fec923b4..adba568f 100644 --- a/include/fs/archive_ext_save_data.hpp +++ b/include/fs/archive_ext_save_data.hpp @@ -18,6 +18,11 @@ public: FileDescriptor openFile(const FSPath& path, const FilePerms& perms) override; std::optional readFile(FileSession* file, u64 offset, u32 size, u32 dataPointer) override; + Rust::Result getFormatInfo(const FSPath& path) override { + Helpers::warn("Stubbed ExtSaveData::GetFormatInfo"); + return Ok(FormatInfo{.size = 1_GB, .numOfDirectories = 255, .numOfFiles = 255, .duplicateData = false}); + } + // Takes in a binary ExtSaveData path, outputs a combination of the backing folder with the low and high save entries of the path // Used for identifying the archive format info files std::string getExtSaveDataPathFromBinary(const FSPath& path); diff --git a/include/kernel/kernel.hpp b/include/kernel/kernel.hpp index b8976796..dc821a68 100644 --- a/include/kernel/kernel.hpp +++ b/include/kernel/kernel.hpp @@ -72,7 +72,15 @@ public: // Signals an event, returns true on success or false if the event does not exist bool signalEvent(Handle e); -private: + + void clearEvent(Handle e) { + KernelObject* object = getObject(e, KernelObjectType::Event); + if (object != nullptr) { + object->getData()->fired = false; + } + } + + private: void signalArbiter(u32 waitingAddress, s32 threadCount); void sleepThread(s64 ns); void sleepThreadOnArbiter(u32 waitingAddress); diff --git a/include/services/amiibo_device.hpp b/include/services/amiibo_device.hpp new file mode 100644 index 00000000..0b9f21e1 --- /dev/null +++ b/include/services/amiibo_device.hpp @@ -0,0 +1,15 @@ +#pragma once +#include + +#include "helpers.hpp" +#include "io_file.hpp" + +class AmiiboDevice { + public: + static constexpr size_t tagSize = 0x21C; + + bool loaded = false; + std::array raw; + + void reset(); +}; \ No newline at end of file diff --git a/include/services/nfc.hpp b/include/services/nfc.hpp index e65c42c1..151d8408 100644 --- a/include/services/nfc.hpp +++ b/include/services/nfc.hpp @@ -1,4 +1,7 @@ #pragma once +#include + +#include "amiibo_device.hpp" #include "helpers.hpp" #include "kernel_types.hpp" #include "logger.hpp" @@ -33,6 +36,7 @@ class NFCService { // Kernel events signaled when an NFC tag goes in and out of range respectively std::optional tagInRangeEvent, tagOutOfRangeEvent; + AmiiboDevice device; Old3DSAdapterStatus adapterStatus; TagStatus tagStatus; bool initialized = false; @@ -49,8 +53,10 @@ class NFCService { void startTagScanning(u32 messagePointer); void stopCommunication(u32 messagePointer); -public: + public: NFCService(Memory& mem, Kernel& kernel) : mem(mem), kernel(kernel) {} void reset(); void handleSyncRequest(u32 messagePointer); + + bool loadAmiibo(const std::filesystem::path& path); }; \ No newline at end of file diff --git a/include/services/service_manager.hpp b/include/services/service_manager.hpp index 42025a3e..aa559ee8 100644 --- a/include/services/service_manager.hpp +++ b/include/services/service_manager.hpp @@ -104,4 +104,5 @@ class ServiceManager { // Input function wrappers HIDService& getHID() { return hid; } + NFCService& getNFC() { return nfc; } }; diff --git a/src/core/services/amiibo_device.cpp b/src/core/services/amiibo_device.cpp new file mode 100644 index 00000000..58a0e2c6 --- /dev/null +++ b/src/core/services/amiibo_device.cpp @@ -0,0 +1,3 @@ +#include "services/amiibo_device.hpp" + +void AmiiboDevice::reset() { loaded = false; } \ No newline at end of file diff --git a/src/core/services/nfc.cpp b/src/core/services/nfc.cpp index 4f71556c..c0ed52c6 100644 --- a/src/core/services/nfc.cpp +++ b/src/core/services/nfc.cpp @@ -1,4 +1,5 @@ #include "services/nfc.hpp" +#include "io_file.hpp" #include "ipc.hpp" #include "kernel.hpp" @@ -18,6 +19,7 @@ namespace NFCCommands { } void NFCService::reset() { + device.reset(); tagInRangeEvent = std::nullopt; tagOutOfRangeEvent = std::nullopt; @@ -43,6 +45,43 @@ void NFCService::handleSyncRequest(u32 messagePointer) { } } +bool NFCService::loadAmiibo(const std::filesystem::path& path) { + IOFile file(path, "rb"); + + if (!initialized || tagStatus != TagStatus::Scanning) { + Helpers::warn("It's not the correct time to load an amiibo! Make sure to load amiibi when the game is searching for one!"); + file.close(); + + return false; + } + + if (!file.isOpen()) { + printf("Failed to open Amiibo file"); + file.close(); + + return false; + } + + auto [success, bytesRead] = file.readBytes(&device.raw, AmiiboDevice::tagSize); + if (!success || bytesRead != AmiiboDevice::tagSize) { + printf("Failed to read entire tag from Amiibo file: File might not be a proper amiibo file\n"); + file.close(); + + return false; + } + + if (tagOutOfRangeEvent.has_value()) { + kernel.clearEvent(tagOutOfRangeEvent.value()); + } + + if (tagInRangeEvent.has_value()) { + kernel.signalEvent(tagInRangeEvent.value()); + } + + file.close(); + return true; +} + void NFCService::initialize(u32 messagePointer) { const u8 type = mem.read8(messagePointer + 4); log("NFC::Initialize (type = %d)\n", type); diff --git a/src/emulator.cpp b/src/emulator.cpp index 2f8c81cc..ec99b0fe 100644 --- a/src/emulator.cpp +++ b/src/emulator.cpp @@ -353,7 +353,14 @@ void Emulator::run() { char* droppedDir = event.drop.file; if (droppedDir) { - loadROM(droppedDir); + const std::filesystem::path path(droppedDir); + + if (path.extension() == ".amiibo") { + loadAmiibo(path); + } else { + loadROM(path); + } + SDL_free(droppedDir); } break; @@ -478,6 +485,11 @@ bool Emulator::loadROM(const std::filesystem::path& path) { return success; } +bool Emulator::loadAmiibo(const std::filesystem::path& path) { + NFCService& nfc = kernel.getServiceManager().getNFC(); + return nfc.loadAmiibo(path); +} + // Used for loading both CXI and NCSD files since they are both so similar and use the same interface // (We promote CXI files to NCSD internally for ease) bool Emulator::loadNCSD(const std::filesystem::path& path, ROMType type) {