From 710fc585054aeb56b2b86852cb26c03c06a8a24e Mon Sep 17 00:00:00 2001 From: wheremyfoodat <44909372+wheremyfoodat@users.noreply.github.com> Date: Fri, 8 Sep 2023 17:03:22 +0300 Subject: [PATCH 01/17] Add NFC::CommunicationGetResult --- src/core/services/nfc.cpp | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/core/services/nfc.cpp b/src/core/services/nfc.cpp index b1e04ab2..e0fe410b 100644 --- a/src/core/services/nfc.cpp +++ b/src/core/services/nfc.cpp @@ -29,6 +29,7 @@ void NFCService::reset() { void NFCService::handleSyncRequest(u32 messagePointer) { const u32 command = mem.read32(messagePointer); switch (command) { + case NFCCommands::CommunicationGetResult: communicationGetResult(messagePointer); break; case NFCCommands::CommunicationGetStatus: communicationGetStatus(messagePointer); break; case NFCCommands::Initialize: initialize(messagePointer); break; case NFCCommands::GetTagInRangeEvent: getTagInRangeEvent(messagePointer); break; @@ -119,6 +120,21 @@ void NFCService::communicationGetStatus(u32 messagePointer) { mem.write8(messagePointer + 8, static_cast(adapterStatus)); } + +void NFCService::communicationGetResult(u32 messagePointer) { + log("NFC::CommunicationGetResult"); + + if (!initialized) { + Helpers::warn("NFC::CommunicationGetResult: Old 3DS NFC Adapter not initialized\n"); + } + + mem.write32(messagePointer, IPC::responseHeader(0x12, 2, 0)); + mem.write32(messagePointer + 4, Result::Success); + // On N3DS: This always writes 0 here + // On O3DS with the NFC adapter: Returns a result code for NFC communication + mem.write32(messagePointer + 8, 0); +} + void NFCService::startCommunication(u32 messagePointer) { log("NFC::StartCommunication\n"); // adapterStatus = Old3DSAdapterStatus::Active; From f52337f90c22d8fcd96ae5b0fa6c59bd10a36a4c Mon Sep 17 00:00:00 2001 From: wheremyfoodat <44909372+wheremyfoodat@users.noreply.github.com> Date: Fri, 8 Sep 2023 17:53:23 +0300 Subject: [PATCH 02/17] Fix NFC logs --- src/core/services/nfc.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/core/services/nfc.cpp b/src/core/services/nfc.cpp index e0fe410b..4f71556c 100644 --- a/src/core/services/nfc.cpp +++ b/src/core/services/nfc.cpp @@ -101,7 +101,7 @@ void NFCService::getTagOutOfRangeEvent(u32 messagePointer) { } void NFCService::getTagState(u32 messagePointer) { - log("NFC::GetTagState"); + log("NFC::GetTagState\n"); mem.write32(messagePointer, IPC::responseHeader(0xD, 2, 0)); mem.write32(messagePointer + 4, Result::Success); @@ -109,7 +109,7 @@ void NFCService::getTagState(u32 messagePointer) { } void NFCService::communicationGetStatus(u32 messagePointer) { - log("NFC::CommunicationGetStatus"); + log("NFC::CommunicationGetStatus\n"); if (!initialized) { Helpers::warn("NFC::CommunicationGetStatus: Old 3DS NFC Adapter not initialized\n"); @@ -122,7 +122,7 @@ void NFCService::communicationGetStatus(u32 messagePointer) { void NFCService::communicationGetResult(u32 messagePointer) { - log("NFC::CommunicationGetResult"); + log("NFC::CommunicationGetResult\n"); if (!initialized) { Helpers::warn("NFC::CommunicationGetResult: Old 3DS NFC Adapter not initialized\n"); From 3110da1fd6c27d9ffcd6677d7efde1d58acb12ec Mon Sep 17 00:00:00 2001 From: wheremyfoodat <44909372+wheremyfoodat@users.noreply.github.com> Date: Fri, 8 Sep 2023 19:01:44 +0300 Subject: [PATCH 03/17] Add amiibo loading interface --- include/emulator.hpp | 1 + include/services/nfc.hpp | 6 +++++- include/services/service_manager.hpp | 1 + src/core/services/nfc.cpp | 2 ++ src/emulator.cpp | 14 +++++++++++++- 5 files changed, 22 insertions(+), 2 deletions(-) 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/services/nfc.hpp b/include/services/nfc.hpp index e65c42c1..94667f21 100644 --- a/include/services/nfc.hpp +++ b/include/services/nfc.hpp @@ -1,4 +1,6 @@ #pragma once +#include + #include "helpers.hpp" #include "kernel_types.hpp" #include "logger.hpp" @@ -49,8 +51,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/nfc.cpp b/src/core/services/nfc.cpp index 4f71556c..0cb3e699 100644 --- a/src/core/services/nfc.cpp +++ b/src/core/services/nfc.cpp @@ -43,6 +43,8 @@ void NFCService::handleSyncRequest(u32 messagePointer) { } } +bool NFCService::loadAmiibo(const std::filesystem::path& path) { 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) { From 72ae5d2bfa237c75adccae4119a9ac5dcd4a8e01 Mon Sep 17 00:00:00 2001 From: wheremyfoodat <44909372+wheremyfoodat@users.noreply.github.com> Date: Fri, 8 Sep 2023 23:16:33 +0300 Subject: [PATCH 04/17] Add reading amiibo from .amiibo file --- CMakeLists.txt | 3 ++- include/services/amiibo_device.hpp | 15 +++++++++++++++ include/services/nfc.hpp | 2 ++ src/core/services/amiibo_device.cpp | 3 +++ src/core/services/nfc.cpp | 24 +++++++++++++++++++++++- 5 files changed, 45 insertions(+), 2 deletions(-) create mode 100644 include/services/amiibo_device.hpp create mode 100644 src/core/services/amiibo_device.cpp 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/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 94667f21..151d8408 100644 --- a/include/services/nfc.hpp +++ b/include/services/nfc.hpp @@ -1,6 +1,7 @@ #pragma once #include +#include "amiibo_device.hpp" #include "helpers.hpp" #include "kernel_types.hpp" #include "logger.hpp" @@ -35,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; 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 0cb3e699..9d9a37d7 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,7 +45,27 @@ void NFCService::handleSyncRequest(u32 messagePointer) { } } -bool NFCService::loadAmiibo(const std::filesystem::path& path) { return true; } +bool NFCService::loadAmiibo(const std::filesystem::path& path) { + IOFile file(path, "rb"); + + 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; + } + + file.close(); + return true; +} void NFCService::initialize(u32 messagePointer) { const u8 type = mem.read8(messagePointer + 4); From cf54fd6d1d933377b88d46476a00b011a67c099f Mon Sep 17 00:00:00 2001 From: wheremyfoodat <44909372+wheremyfoodat@users.noreply.github.com> Date: Sat, 9 Sep 2023 01:52:28 +0300 Subject: [PATCH 05/17] Fail and warn if an amiibo is loaded at the wrong time --- include/fs/archive_ext_save_data.hpp | 5 +++++ src/core/services/nfc.cpp | 7 +++++++ 2 files changed, 12 insertions(+) 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/src/core/services/nfc.cpp b/src/core/services/nfc.cpp index 9d9a37d7..bac99007 100644 --- a/src/core/services/nfc.cpp +++ b/src/core/services/nfc.cpp @@ -48,6 +48,13 @@ 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(); From 5930a3d8433ca88d92f5fa7eefa13f39731e9ae5 Mon Sep 17 00:00:00 2001 From: wheremyfoodat <44909372+wheremyfoodat@users.noreply.github.com> Date: Sat, 9 Sep 2023 01:56:39 +0300 Subject: [PATCH 06/17] Handle Amiibo tag events better --- include/kernel/kernel.hpp | 10 +++++++++- src/core/services/nfc.cpp | 8 ++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) 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/src/core/services/nfc.cpp b/src/core/services/nfc.cpp index bac99007..c0ed52c6 100644 --- a/src/core/services/nfc.cpp +++ b/src/core/services/nfc.cpp @@ -70,6 +70,14 @@ bool NFCService::loadAmiibo(const std::filesystem::path& path) { return false; } + if (tagOutOfRangeEvent.has_value()) { + kernel.clearEvent(tagOutOfRangeEvent.value()); + } + + if (tagInRangeEvent.has_value()) { + kernel.signalEvent(tagInRangeEvent.value()); + } + file.close(); return true; } From 139ea16fda284d304d0327eb09d3b6a4a96ea936 Mon Sep 17 00:00:00 2001 From: wheremyfoodat <44909372+wheremyfoodat@users.noreply.github.com> Date: Sat, 9 Sep 2023 11:51:36 +0300 Subject: [PATCH 07/17] Captain Toad now thinks we load amiibos --- include/services/nfc.hpp | 4 +++ src/core/services/nfc.cpp | 53 ++++++++++++++++++++++++++++++++++++--- 2 files changed, 53 insertions(+), 4 deletions(-) diff --git a/include/services/nfc.hpp b/include/services/nfc.hpp index 151d8408..8eea8a41 100644 --- a/include/services/nfc.hpp +++ b/include/services/nfc.hpp @@ -45,13 +45,17 @@ class NFCService { void communicationGetResult(u32 messagePointer); void communicationGetStatus(u32 messagePointer); void initialize(u32 messagePointer); + void getModelInfo(u32 messagePointer); + void getTagInfo(u32 messagePointer); void getTagInRangeEvent(u32 messagePointer); void getTagOutOfRangeEvent(u32 messagePointer); void getTagState(u32 messagePointer); + void loadAmiiboPartially(u32 messagePointer); void shutdown(u32 messagePointer); void startCommunication(u32 messagePointer); void startTagScanning(u32 messagePointer); void stopCommunication(u32 messagePointer); + void stopTagScanning(u32 messagePointer); public: NFCService(Memory& mem, Kernel& kernel) : mem(mem), kernel(kernel) {} diff --git a/src/core/services/nfc.cpp b/src/core/services/nfc.cpp index c0ed52c6..2ad20910 100644 --- a/src/core/services/nfc.cpp +++ b/src/core/services/nfc.cpp @@ -10,11 +10,15 @@ namespace NFCCommands { StartCommunication = 0x00030000, StopCommunication = 0x00040000, StartTagScanning = 0x00050040, + StopTagScanning = 0x00060000, GetTagInRangeEvent = 0x000B0000, GetTagOutOfRangeEvent = 0x000C0000, GetTagState = 0x000D0000, CommunicationGetStatus = 0x000F0000, + GetTagInfo = 0x00110000, CommunicationGetResult = 0x00120000, + LoadAmiiboPartially = 0x001A0000, + GetModelInfo = 0x001B0000, }; } @@ -34,27 +38,28 @@ void NFCService::handleSyncRequest(u32 messagePointer) { case NFCCommands::CommunicationGetResult: communicationGetResult(messagePointer); break; case NFCCommands::CommunicationGetStatus: communicationGetStatus(messagePointer); break; case NFCCommands::Initialize: initialize(messagePointer); break; + case NFCCommands::GetModelInfo: getModelInfo(messagePointer); break; + case NFCCommands::GetTagInfo: getTagInfo(messagePointer); break; case NFCCommands::GetTagInRangeEvent: getTagInRangeEvent(messagePointer); break; case NFCCommands::GetTagOutOfRangeEvent: getTagOutOfRangeEvent(messagePointer); break; case NFCCommands::GetTagState: getTagState(messagePointer); break; + case NFCCommands::LoadAmiiboPartially: loadAmiiboPartially(messagePointer); break; case NFCCommands::Shutdown: shutdown(messagePointer); break; case NFCCommands::StartCommunication: startCommunication(messagePointer); break; case NFCCommands::StartTagScanning: startTagScanning(messagePointer); break; case NFCCommands::StopCommunication: stopCommunication(messagePointer); break; + case NFCCommands::StopTagScanning: stopTagScanning(messagePointer); break; default: Helpers::panic("NFC service requested. Command: %08X\n", command); } } 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; } + IOFile file(path, "rb"); if (!file.isOpen()) { printf("Failed to open Amiibo file"); file.close(); @@ -185,12 +190,28 @@ void NFCService::startCommunication(u32 messagePointer) { void NFCService::startTagScanning(u32 messagePointer) { log("NFC::StartTagScanning\n"); + if (!initialized) { + Helpers::warn("Scanning for NFC tags before NFC service is initialized"); + } + tagStatus = TagStatus::Scanning; mem.write32(messagePointer, IPC::responseHeader(0x5, 1, 0)); mem.write32(messagePointer + 4, Result::Success); } +void NFCService::stopTagScanning(u32 messagePointer) { + log("NFC::StopTagScanning\n"); + if (!initialized) { + Helpers::warn("Stopping scanning for NFC tags before NFC service is initialized"); + } + + tagStatus = TagStatus::Initialized; + + mem.write32(messagePointer, IPC::responseHeader(0x6, 1, 0)); + mem.write32(messagePointer + 4, Result::Success); +} + void NFCService::stopCommunication(u32 messagePointer) { log("NFC::StopCommunication\n"); adapterStatus = Old3DSAdapterStatus::InitializationComplete; @@ -198,4 +219,28 @@ void NFCService::stopCommunication(u32 messagePointer) { mem.write32(messagePointer, IPC::responseHeader(0x4, 1, 0)); mem.write32(messagePointer + 4, Result::Success); +} + +void NFCService::getTagInfo(u32 messagePointer) { + log("NFC::GetTagInfo\n"); + Helpers::warn("Unimplemented NFC::GetTagInfo"); + + mem.write32(messagePointer, IPC::responseHeader(0x11, 12, 0)); + mem.write32(messagePointer + 4, Result::Success); +} + +void NFCService::loadAmiiboPartially(u32 messagePointer) { + log("NFC::LoadAmiiboPartially\n"); + Helpers::warn("Unimplemented NFC::LoadAmiiboPartially"); + + mem.write32(messagePointer, IPC::responseHeader(0x1A, 1, 0)); + mem.write32(messagePointer + 4, Result::Success); +} + +void NFCService::getModelInfo(u32 messagePointer) { + log("NFC::GetModelInfo\n"); + Helpers::warn("Unimplemented NFC::GetModelInfo"); + + mem.write32(messagePointer, IPC::responseHeader(0x1B, 14, 0)); + mem.write32(messagePointer + 4, Result::Success); } \ No newline at end of file From 679eb0f7bc8241f1bb2943ecd0adf4519e456216 Mon Sep 17 00:00:00 2001 From: wheremyfoodat <44909372+wheremyfoodat@users.noreply.github.com> Date: Sat, 9 Sep 2023 15:06:04 +0300 Subject: [PATCH 08/17] Add Yuzu NFC types header, clean up headers --- CMakeLists.txt | 2 +- include/services/amiibo_device.hpp | 8 +- include/services/nfc_types.hpp | 411 +++++++++++++++++++++ {third_party/cityhash => include}/swap.hpp | 0 src/core/services/amiibo_device.cpp | 10 +- src/core/services/nfc.cpp | 2 + 6 files changed, 428 insertions(+), 5 deletions(-) create mode 100644 include/services/nfc_types.hpp rename {third_party/cityhash => include}/swap.hpp (100%) diff --git a/CMakeLists.txt b/CMakeLists.txt index b4ca6745..90e2ffb9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -181,7 +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 + include/services/amiibo_device.hpp include/services/nfc_types.hpp include/swap.hpp ) cmrc_add_resource_library( diff --git a/include/services/amiibo_device.hpp b/include/services/amiibo_device.hpp index 0b9f21e1..a07d8ac1 100644 --- a/include/services/amiibo_device.hpp +++ b/include/services/amiibo_device.hpp @@ -1,15 +1,17 @@ #pragma once #include - #include "helpers.hpp" #include "io_file.hpp" +#include "nfc_types.hpp" class AmiiboDevice { + bool loaded = false; + bool encrypted = false; + public: static constexpr size_t tagSize = 0x21C; - - bool loaded = false; std::array raw; + void loadFromRaw(); void reset(); }; \ No newline at end of file diff --git a/include/services/nfc_types.hpp b/include/services/nfc_types.hpp new file mode 100644 index 00000000..fc0c323f --- /dev/null +++ b/include/services/nfc_types.hpp @@ -0,0 +1,411 @@ +// Copyright 2022 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include + +#include "helpers.hpp" +#include "swap.hpp" + +namespace Service::NFC { + static constexpr std::size_t amiiboNameLength = 0xA; + static constexpr std::size_t applicationIDVersionOffset = 0x1c; + static constexpr std::size_t counterLimit = 0xffff; + + enum class ServiceType : u32 { + User, + Debug, + System, + }; + + enum class CommunicationState : u8 { + Idle = 0, + SearchingForAdapter = 1, + Initialized = 2, + Active = 3, + }; + + enum class ConnectionState : u8 { + Success = 0, + NoAdapter = 1, + Lost = 2, + }; + + enum class DeviceState : u32 { + NotInitialized = 0, + Initialized = 1, + SearchingForTag = 2, + TagFound = 3, + TagRemoved = 4, + TagMounted = 5, + TagPartiallyMounted = 6, // Validate this one seems to have other name + }; + + enum class ModelType : u32 { + Amiibo, + }; + + enum class MountTarget : u32 { + None, + Rom, + Ram, + All, + }; + + enum class AmiiboType : u8 { + Figure, + Card, + Yarn, + }; + + enum class AmiiboSeries : u8 { + SuperSmashBros, + SuperMario, + ChibiRobo, + YoshiWoollyWorld, + Splatoon, + AnimalCrossing, + EightBitMario, + Skylanders, + Unknown8, + TheLegendOfZelda, + ShovelKnight, + Unknown11, + Kiby, + Pokemon, + MarioSportsSuperstars, + MonsterHunter, + BoxBoy, + Pikmin, + FireEmblem, + Metroid, + Others, + MegaMan, + Diablo, + }; + + struct ChecksummedMiiData { + u8 raw[0x60]; + }; + static_assert(sizeof(ChecksummedMiiData) == 0x60); + + enum class TagType : u32 { + None, + Type1, // ISO14443A RW 96-2k bytes 106kbit/s + Type2, // ISO14443A RW/RO 540 bytes 106kbit/s + Type3, // Sony Felica RW/RO 2k bytes 212kbit/s + Type4, // ISO14443A RW/RO 4k-32k bytes 424kbit/s + Type5, // ISO15693 RW/RO 540 bytes 106kbit/s + }; + + enum class PackedTagType : u8 { + None, + Type1, // ISO14443A RW 96-2k bytes 106kbit/s + Type2, // ISO14443A RW/RO 540 bytes 106kbit/s + Type3, // Sony Felica RW/RO 2k bytes 212kbit/s + Type4, // ISO14443A RW/RO 4k-32k bytes 424kbit/s + Type5, // ISO15693 RW/RO 540 bytes 106kbit/s + }; + + // Verify this enum. It might be completely wrong default protocol is 0x0 + enum class TagProtocol : u32 { + None, + TypeA = 1U << 0, // ISO14443A + TypeB = 1U << 1, // ISO14443B + TypeF = 1U << 2, // Sony Felica + Unknown1 = 1U << 3, + Unknown2 = 1U << 5, + All = 0xFFFFFFFFU, + }; + + // Verify this enum. It might be completely wrong default protocol is 0x0 + enum class PackedTagProtocol : u8 { + None, + TypeA = 1U << 0, // ISO14443A + TypeB = 1U << 1, // ISO14443B + TypeF = 1U << 2, // Sony Felica + Unknown1 = 1U << 3, + Unknown2 = 1U << 5, + All = 0xFF, + }; + + enum class AppAreaVersion : u8 { + Nintendo3DS = 0, + NintendoWiiU = 1, + Nintendo3DSv2 = 2, + NintendoSwitch = 3, + NotSet = 0xFF, + }; + + using UniqueSerialNumber = std::array; + using LockBytes = std::array; + using HashData = std::array; + using ApplicationArea = std::array; + using AmiiboName = std::array; + using DataBlock = std::array; + using KeyData = std::array; + + struct TagUuid { + UniqueSerialNumber uid; + u8 nintendo_id; + LockBytes lock_bytes; + }; + static_assert(sizeof(TagUuid) == 10, "TagUuid is an invalid size"); + + struct WriteDate { + u16 year; + u8 month; + u8 day; + }; + static_assert(sizeof(WriteDate) == 0x4, "WriteDate is an invalid size"); + + struct AmiiboDate { + u16 raw_date{}; + + u16 GetValue() const { return Common::swap16(raw_date); } + + u16 GetYear() const { return static_cast(((GetValue() & 0xFE00) >> 9) + 2000); } + u8 GetMonth() const { return static_cast((GetValue() & 0x01E0) >> 5); } + u8 GetDay() const { return static_cast(GetValue() & 0x001F); } + + WriteDate GetWriteDate() const { + if (!IsValidDate()) { + return { + .year = 2000, + .month = 1, + .day = 1, + }; + } + return { + .year = GetYear(), + .month = GetMonth(), + .day = GetDay(), + }; + } + + void SetYear(u16 year) { + const u16 year_converted = static_cast((year - 2000) << 9); + raw_date = Common::swap16((GetValue() & ~0xFE00) | year_converted); + } + void SetMonth(u8 month) { + const u16 month_converted = static_cast(month << 5); + raw_date = Common::swap16((GetValue() & ~0x01E0) | month_converted); + } + void SetDay(u8 day) { + const u16 day_converted = static_cast(day); + raw_date = Common::swap16((GetValue() & ~0x001F) | day_converted); + } + + bool IsValidDate() const { + const bool is_day_valid = GetDay() > 0 && GetDay() < 32; + const bool is_month_valid = GetMonth() > 0 && GetMonth() < 13; + const bool is_year_valid = GetYear() >= 2000; + return is_year_valid && is_month_valid && is_day_valid; + } + }; + static_assert(sizeof(AmiiboDate) == 2, "AmiiboDate is an invalid size"); + + struct Settings { + u8 raw{}; + }; + static_assert(sizeof(Settings) == 1, "AmiiboDate is an invalid size"); + + struct AmiiboSettings { + Settings settings; + u8 country_code_id; + u16_be crc_counter; // Incremented each time crc is changed + AmiiboDate init_date; + AmiiboDate write_date; + u32_be crc; + AmiiboName amiibo_name; // UTF-16 text + }; + static_assert(sizeof(AmiiboSettings) == 0x20, "AmiiboSettings is an invalid size"); + + struct AmiiboModelInfo { + u16 character_id; + u8 character_variant; + AmiiboType amiibo_type; + u16_be model_number; + AmiiboSeries series; + PackedTagType tag_type; + u32 pad; // Unknown + }; + static_assert(sizeof(AmiiboModelInfo) == 0xC, "AmiiboModelInfo is an invalid size"); + + struct NTAG215Password { + u32 PWD; // Password to allow write access + u16 PACK; // Password acknowledge reply + u16 RFUI; // Reserved for future use + }; + static_assert(sizeof(NTAG215Password) == 0x8, "NTAG215Password is an invalid size"); + +#pragma pack(1) + struct EncryptedAmiiboFile { + u8 constant_value; // Must be A5 + u16_be write_counter; // Number of times the amiibo has been written? + u8 amiibo_version; // Amiibo file version + AmiiboSettings settings; // Encrypted amiibo settings + HashData hmac_tag; // Hash + AmiiboModelInfo model_info; // Encrypted amiibo model info + HashData keygen_salt; // Salt + HashData hmac_data; // Hash + ChecksummedMiiData owner_mii; // Encrypted Mii data + u64_be application_id; // Encrypted Game id + u16_be application_write_counter; // Encrypted Counter + u32_be application_area_id; // Encrypted Game id + u8 application_id_byte; + u8 unknown; + u64 mii_extension; + std::array unknown2; + u32_be register_info_crc; + ApplicationArea application_area; // Encrypted Game data + }; + static_assert(sizeof(EncryptedAmiiboFile) == 0x1F8, "AmiiboFile is an invalid size"); + + struct NTAG215File { + LockBytes lock_bytes; // Tag UUID + u16 static_lock; // Set defined pages as read only + u32 compability_container; // Defines available memory + HashData hmac_data; // Hash + u8 constant_value; // Must be A5 + u16_be write_counter; // Number of times the amiibo has been written? + u8 amiibo_version; // Amiibo file version + AmiiboSettings settings; + ChecksummedMiiData owner_mii; // Mii data + u64_be application_id; // Game id + u16_be application_write_counter; // Counter + u32_be application_area_id; + u8 application_id_byte; + u8 unknown; + u64 mii_extension; + std::array unknown2; + u32_be register_info_crc; + ApplicationArea application_area; // Game data + HashData hmac_tag; // Hash + UniqueSerialNumber uid; // Unique serial number + u8 nintendo_id; // Tag UUID + AmiiboModelInfo model_info; + HashData keygen_salt; // Salt + u32 dynamic_lock; // Dynamic lock + u32 CFG0; // Defines memory protected by password + u32 CFG1; // Defines number of verification attempts + NTAG215Password password; // Password data + }; + static_assert(sizeof(NTAG215File) == 0x21C, "NTAG215File is an invalid size"); + static_assert(std::is_trivially_copyable_v, "NTAG215File must be trivially copyable."); +#pragma pack() + + struct EncryptedNTAG215File { + TagUuid uuid; // Unique serial number + u16 static_lock; // Set defined pages as read only + u32 compability_container; // Defines available memory + EncryptedAmiiboFile user_memory; // Writable data + u32 dynamic_lock; // Dynamic lock + u32 CFG0; // Defines memory protected by password + u32 CFG1; // Defines number of verification attempts + NTAG215Password password; // Password data + }; + static_assert(sizeof(EncryptedNTAG215File) == 0x21C, "EncryptedNTAG215File is an invalid size"); + static_assert(std::is_trivially_copyable_v, "EncryptedNTAG215File must be trivially copyable."); + + struct SerializableAmiiboFile { + union { + std::array raw; + NTAG215File file; + }; + }; + static_assert(sizeof(SerializableAmiiboFile) == 0x21C, "SerializableAmiiboFile is an invalid size"); + static_assert(std::is_trivially_copyable_v, "SerializableAmiiboFile must be trivially copyable."); + + struct SerializableEncryptedAmiiboFile { + union { + std::array raw; + EncryptedNTAG215File file; + }; + }; + static_assert(sizeof(SerializableEncryptedAmiiboFile) == 0x21C, "SerializableEncryptedAmiiboFile is an invalid size"); + static_assert(std::is_trivially_copyable_v, "SerializableEncryptedAmiiboFile must be trivially copyable."); + + struct TagInfo { + u16 uuid_length; + PackedTagProtocol protocol; + PackedTagType tag_type; + UniqueSerialNumber uuid; + std::array extra_data; + }; + static_assert(sizeof(TagInfo) == 0x2C, "TagInfo is an invalid size"); + + struct TagInfo2 { + u16 uuid_length; + u8 pad; + PackedTagType tag_type; + UniqueSerialNumber uuid; + std::array extra_data; + TagProtocol protocol; + std::array extra_data2; + }; + static_assert(sizeof(TagInfo2) == 0x60, "TagInfo2 is an invalid size"); + + struct CommonInfo { + WriteDate last_write_date; + u16 application_write_counter; + u16 character_id; + u8 character_variant; + AmiiboSeries series; + u16 model_number; + AmiiboType amiibo_type; + u8 version; + u16 application_area_size; + u8 pad[0x30]; + }; + static_assert(sizeof(CommonInfo) == 0x40, "CommonInfo is an invalid size"); + + struct ModelInfo { + u16 character_id; + u8 character_variant; + AmiiboSeries series; + u16 model_number; + AmiiboType amiibo_type; + u8 pad[0x2F]; + }; + static_assert(sizeof(ModelInfo) == 0x36, "ModelInfo is an invalid size"); + + struct RegisterInfo { + ChecksummedMiiData mii_data; + AmiiboName amiibo_name; + u16 pad; // Zero string terminator + u8 flags; + u8 font_region; + WriteDate creation_date; + u8 pad2[0x2C]; + }; + static_assert(sizeof(RegisterInfo) == 0xA8, "RegisterInfo is an invalid size"); + + struct RegisterInfoPrivate { + ChecksummedMiiData mii_data; + AmiiboName amiibo_name; + u16 pad; // Zero string terminator + u8 flags; + u8 font_region; + WriteDate creation_date; + u8 pad2[0x28]; + }; + static_assert(sizeof(RegisterInfoPrivate) == 0xA4, "RegisterInfoPrivate is an invalid size"); + static_assert(std::is_trivial_v, "RegisterInfoPrivate must be trivial."); + static_assert(std::is_trivially_copyable_v, "RegisterInfoPrivate must be trivially copyable."); + + struct AdminInfo { + u64_be application_id; + u32_be application_area_id; + u16 crc_counter; + u8 flags; + PackedTagType tag_type; + AppAreaVersion app_area_version; + u8 pad[0x7]; + u8 pad2[0x28]; + }; + static_assert(sizeof(AdminInfo) == 0x40, "AdminInfo is an invalid size"); + +} // namespace Service::NFC \ No newline at end of file diff --git a/third_party/cityhash/swap.hpp b/include/swap.hpp similarity index 100% rename from third_party/cityhash/swap.hpp rename to include/swap.hpp diff --git a/src/core/services/amiibo_device.cpp b/src/core/services/amiibo_device.cpp index 58a0e2c6..71cabf0e 100644 --- a/src/core/services/amiibo_device.cpp +++ b/src/core/services/amiibo_device.cpp @@ -1,3 +1,11 @@ #include "services/amiibo_device.hpp" -void AmiiboDevice::reset() { loaded = false; } \ No newline at end of file +void AmiiboDevice::reset() { + encrypted = false; + loaded = false; +} + +// Load amiibo information from our raw 540 byte array +void AmiiboDevice::loadFromRaw() { + +} \ No newline at end of file diff --git a/src/core/services/nfc.cpp b/src/core/services/nfc.cpp index 2ad20910..e9a61ef9 100644 --- a/src/core/services/nfc.cpp +++ b/src/core/services/nfc.cpp @@ -75,6 +75,8 @@ bool NFCService::loadAmiibo(const std::filesystem::path& path) { return false; } + device.loadFromRaw(); + if (tagOutOfRangeEvent.has_value()) { kernel.clearEvent(tagOutOfRangeEvent.value()); } From e62ce621c4797c0023d0098414d72519de3ff68a Mon Sep 17 00:00:00 2001 From: wheremyfoodat <44909372+wheremyfoodat@users.noreply.github.com> Date: Sat, 9 Sep 2023 15:19:57 +0300 Subject: [PATCH 09/17] IWYU: Add type_traits to nfc_types --- include/services/nfc_types.hpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/include/services/nfc_types.hpp b/include/services/nfc_types.hpp index fc0c323f..d3bcf09a 100644 --- a/include/services/nfc_types.hpp +++ b/include/services/nfc_types.hpp @@ -5,6 +5,7 @@ #pragma once #include +#include #include "helpers.hpp" #include "swap.hpp" @@ -408,4 +409,4 @@ namespace Service::NFC { }; static_assert(sizeof(AdminInfo) == 0x40, "AdminInfo is an invalid size"); -} // namespace Service::NFC \ No newline at end of file +} // namespace Service::NFC From 1e0fb0e6567b60125e13a7993b405a8891ae16a9 Mon Sep 17 00:00:00 2001 From: wheremyfoodat <44909372+wheremyfoodat@users.noreply.github.com> Date: Sat, 9 Sep 2023 15:42:16 +0300 Subject: [PATCH 10/17] Add ctru-rs to readme --- readme.md | 1 + 1 file changed, 1 insertion(+) diff --git a/readme.md b/readme.md index 4d178d0d..c0d38962 100644 --- a/readme.md +++ b/readme.md @@ -91,6 +91,7 @@ Panda3DS also supports controller input using the SDL2 GameController API. - [3DBrew](https://www.3dbrew.org/wiki/Main_Page), a wiki full of 3DS information and the main source of documentation used. - [GBATek](https://www.problemkaputt.de/gbatek.htm#3dsreference), a GBA, DS and 3DS reference which provided insights on some pieces of hardware as well as neatly documenting things like certain file formats used in games. - [Libctru](https://github.com/devkitPro/libctru), the most well-known 3DS homebrew SDK. Used for developing test ROMs, as well as a source of documentation thanks to its doxygen wiki. +- [ctru-rs](https://github.com/rust3ds/ctru-rs), a wrapper around libctru for writing 3DS homebrew in Rust. - [Citra](https://github.com/citra-emu/citra), an HLE 3DS emulator. Very useful as a reference, with some code snippets inspired or adapted from it. - [3dmoo](https://github.com/plutooo/3dmoo), an HLE 3DS emulator which helped similarly to Citra From 68346d73dc2cf01e8fa5b1566b35a33403f44852 Mon Sep 17 00:00:00 2001 From: wheremyfoodat <44909372+wheremyfoodat@users.noreply.github.com> Date: Sun, 10 Sep 2023 14:45:09 +0300 Subject: [PATCH 11/17] Add empty csnd service --- CMakeLists.txt | 3 ++- include/kernel/handles.hpp | 2 ++ include/logger.hpp | 1 + include/services/csnd.hpp | 16 ++++++++++++++++ include/services/service_manager.hpp | 2 ++ src/core/services/csnd.cpp | 16 ++++++++++++++++ src/core/services/service_manager.cpp | 5 ++++- 7 files changed, 43 insertions(+), 2 deletions(-) create mode 100644 include/services/csnd.hpp create mode 100644 src/core/services/csnd.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 90e2ffb9..5f72ff16 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -138,6 +138,7 @@ set(SERVICE_SOURCE_FILES src/core/services/service_manager.cpp src/core/services 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/amiibo_device.cpp + src/core/services/csnd.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,7 +182,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 include/services/nfc_types.hpp include/swap.hpp + include/services/amiibo_device.hpp include/services/nfc_types.hpp include/swap.hpp include/services/csnd.hpp ) cmrc_add_resource_library( diff --git a/include/kernel/handles.hpp b/include/kernel/handles.hpp index 746f51c6..fac74132 100644 --- a/include/kernel/handles.hpp +++ b/include/kernel/handles.hpp @@ -19,6 +19,7 @@ namespace KernelHandles { CECD, // More Streetpass stuff? CFG_U, // CFG service (Console & region info) CFG_I, + CSND, // Plays audio directly from PCM samples DLP_SRVR, // Download Play: Server. Used for network play. DSP, // DSP service (Used for audio decoding and output) HID, // HID service (Handles input-related things including gyro. Does NOT handle New3DS controls or CirclePadPro) @@ -73,6 +74,7 @@ namespace KernelHandles { case CECD: return "CECD"; case CFG_U: return "CFG:U"; case CFG_I: return "CFG:I"; + case CSND: return "CSND"; case DSP: return "DSP"; case DLP_SRVR: return "DLP::SRVR"; case HID: return "HID"; diff --git a/include/logger.hpp b/include/logger.hpp index c5a62ea9..2ad9e713 100644 --- a/include/logger.hpp +++ b/include/logger.hpp @@ -38,6 +38,7 @@ namespace Log { static Logger camLogger; static Logger cecdLogger; static Logger cfgLogger; + static Logger csndLogger; static Logger dspServiceLogger; static Logger dlpSrvrLogger; static Logger frdLogger; diff --git a/include/services/csnd.hpp b/include/services/csnd.hpp new file mode 100644 index 00000000..8a8c38e9 --- /dev/null +++ b/include/services/csnd.hpp @@ -0,0 +1,16 @@ +#pragma once +#include "helpers.hpp" +#include "kernel_types.hpp" +#include "logger.hpp" +#include "memory.hpp" + +class CSNDService { + Handle handle = KernelHandles::CSND; + Memory& mem; + MAKE_LOG_FUNCTION(log, csndLogger) + + public: + CSNDService(Memory& mem) : mem(mem) {} + void reset(); + void handleSyncRequest(u32 messagePointer); +}; \ No newline at end of file diff --git a/include/services/service_manager.hpp b/include/services/service_manager.hpp index aa559ee8..855585e8 100644 --- a/include/services/service_manager.hpp +++ b/include/services/service_manager.hpp @@ -14,6 +14,7 @@ #include "services/cam.hpp" #include "services/cecd.hpp" #include "services/cfg.hpp" +#include "services/csnd.hpp" #include "services/dlp_srvr.hpp" #include "services/dsp.hpp" #include "services/frd.hpp" @@ -56,6 +57,7 @@ class ServiceManager { CAMService cam; CECDService cecd; CFGService cfg; + CSNDService csnd; DlpSrvrService dlp_srvr; DSPService dsp; HIDService hid; diff --git a/src/core/services/csnd.cpp b/src/core/services/csnd.cpp new file mode 100644 index 00000000..9a78091b --- /dev/null +++ b/src/core/services/csnd.cpp @@ -0,0 +1,16 @@ +#include "services/csnd.hpp" + +#include "ipc.hpp" +#include "result/result.hpp" + +void CSNDService::reset() {} + +void CSNDService::handleSyncRequest(u32 messagePointer) { + const u32 command = mem.read32(messagePointer); + + switch (command) { + default: + Helpers::panic("Unimplemented CSND service requested. Command: %08X\n", command); + break; + } +} \ No newline at end of file diff --git a/src/core/services/service_manager.cpp b/src/core/services/service_manager.cpp index 9a4027a4..0e0f8a98 100644 --- a/src/core/services/service_manager.cpp +++ b/src/core/services/service_manager.cpp @@ -7,7 +7,7 @@ ServiceManager::ServiceManager(std::span regs, Memory& mem, GPU& gpu, u32& currentPID, Kernel& kernel, const EmulatorConfig& config) : regs(regs), mem(mem), kernel(kernel), ac(mem), am(mem), boss(mem), act(mem), apt(mem, kernel), cam(mem, kernel), cecd(mem, kernel), cfg(mem), - dlp_srvr(mem), dsp(mem, kernel), hid(mem, kernel), http(mem), ir_user(mem, kernel), frd(mem), fs(mem, kernel, config), + csnd(mem), dlp_srvr(mem), dsp(mem, kernel), hid(mem, kernel), http(mem), ir_user(mem, kernel), frd(mem), fs(mem, kernel, config), gsp_gpu(mem, gpu, kernel, currentPID), gsp_lcd(mem), ldr(mem), mcu_hwc(mem, config), mic(mem, kernel), nfc(mem, kernel), nim(mem), ndm(mem), news_u(mem), ptm(mem, config), soc(mem), ssl(mem), y2r(mem, kernel) {} @@ -23,6 +23,7 @@ void ServiceManager::reset() { cam.reset(); cecd.reset(); cfg.reset(); + csnd.reset(); dlp_srvr.reset(); dsp.reset(); hid.reset(); @@ -104,6 +105,7 @@ static std::map serviceMap = { { "cecd:u", KernelHandles::CECD }, { "cfg:u", KernelHandles::CFG_U }, { "cfg:i", KernelHandles::CFG_I }, + { "csnd:SND", KernelHandles::CSND }, { "dlp:SRVR", KernelHandles::DLP_SRVR }, { "dsp::DSP", KernelHandles::DSP }, { "hid:USER", KernelHandles::HID }, @@ -202,6 +204,7 @@ void ServiceManager::sendCommandToService(u32 messagePointer, Handle handle) { case KernelHandles::CAM: cam.handleSyncRequest(messagePointer); break; case KernelHandles::CECD: cecd.handleSyncRequest(messagePointer); break; case KernelHandles::CFG_U: cfg.handleSyncRequest(messagePointer); break; + case KernelHandles::CSND: csnd.handleSyncRequest(messagePointer); break; case KernelHandles::DLP_SRVR: dlp_srvr.handleSyncRequest(messagePointer); break; case KernelHandles::HID: hid.handleSyncRequest(messagePointer); break; case KernelHandles::HTTP: http.handleSyncRequest(messagePointer); break; From da29cecf7a7498345a4cc72d36a7ac602334ae8a Mon Sep 17 00:00:00 2001 From: wheremyfoodat <44909372+wheremyfoodat@users.noreply.github.com> Date: Sun, 10 Sep 2023 16:20:45 +0300 Subject: [PATCH 12/17] Make cfg:i not die --- include/services/cfg.hpp | 9 ++++++++- src/core/services/cfg.cpp | 2 +- src/core/services/service_manager.cpp | 3 ++- 3 files changed, 11 insertions(+), 3 deletions(-) diff --git a/include/services/cfg.hpp b/include/services/cfg.hpp index c8c8adde..91010e93 100644 --- a/include/services/cfg.hpp +++ b/include/services/cfg.hpp @@ -22,7 +22,14 @@ class CFGService { void secureInfoGetRegion(u32 messagePointer); public: + enum class Type { + U, // cfg:u + I, // cfg:i + S, // cfg:s + NOR, // cfg:nor + }; + CFGService(Memory& mem) : mem(mem) {} void reset(); - void handleSyncRequest(u32 messagePointer); + void handleSyncRequest(u32 messagePointer, Type type); }; \ No newline at end of file diff --git a/src/core/services/cfg.cpp b/src/core/services/cfg.cpp index 882de420..bcb095ca 100644 --- a/src/core/services/cfg.cpp +++ b/src/core/services/cfg.cpp @@ -21,7 +21,7 @@ namespace CFGCommands { void CFGService::reset() {} -void CFGService::handleSyncRequest(u32 messagePointer) { +void CFGService::handleSyncRequest(u32 messagePointer, CFGService::Type type) { const u32 command = mem.read32(messagePointer); switch (command) { case CFGCommands::GetConfigInfoBlk2: [[likely]] getConfigInfoBlk2(messagePointer); break; diff --git a/src/core/services/service_manager.cpp b/src/core/services/service_manager.cpp index 0e0f8a98..9ac08df4 100644 --- a/src/core/services/service_manager.cpp +++ b/src/core/services/service_manager.cpp @@ -203,7 +203,8 @@ void ServiceManager::sendCommandToService(u32 messagePointer, Handle handle) { case KernelHandles::BOSS: boss.handleSyncRequest(messagePointer); break; case KernelHandles::CAM: cam.handleSyncRequest(messagePointer); break; case KernelHandles::CECD: cecd.handleSyncRequest(messagePointer); break; - case KernelHandles::CFG_U: cfg.handleSyncRequest(messagePointer); break; + case KernelHandles::CFG_U: cfg.handleSyncRequest(messagePointer, CFGService::Type::U); break; + case KernelHandles::CFG_I: cfg.handleSyncRequest(messagePointer, CFGService::Type::I); break; case KernelHandles::CSND: csnd.handleSyncRequest(messagePointer); break; case KernelHandles::DLP_SRVR: dlp_srvr.handleSyncRequest(messagePointer); break; case KernelHandles::HID: hid.handleSyncRequest(messagePointer); break; From b394cacbc7279b9db69a4a7ddcdafc108f268404 Mon Sep 17 00:00:00 2001 From: wheremyfoodat <44909372+wheremyfoodat@users.noreply.github.com> Date: Sun, 10 Sep 2023 16:51:44 +0300 Subject: [PATCH 13/17] More CSND stubbing --- include/kernel/handles.hpp | 1 + include/memory.hpp | 5 +-- include/services/csnd.hpp | 14 +++++++- src/core/kernel/memory_management.cpp | 2 ++ src/core/services/csnd.cpp | 49 +++++++++++++++++++++++++-- src/core/services/service_manager.cpp | 2 +- 6 files changed, 67 insertions(+), 6 deletions(-) diff --git a/include/kernel/handles.hpp b/include/kernel/handles.hpp index fac74132..4d7adcf0 100644 --- a/include/kernel/handles.hpp +++ b/include/kernel/handles.hpp @@ -46,6 +46,7 @@ namespace KernelHandles { GSPSharedMemHandle = MaxServiceHandle + 1, // Handle for the GSP shared memory FontSharedMemHandle, + CSNDSharedMemHandle, HIDSharedMemHandle, MinSharedMemHandle = GSPSharedMemHandle, diff --git a/include/memory.hpp b/include/memory.hpp index 804d1e54..86fb9f36 100644 --- a/include/memory.hpp +++ b/include/memory.hpp @@ -112,10 +112,11 @@ class Memory { // This tracks our OS' memory allocations std::vector memoryInfo; - std::array sharedMemBlocks = { + std::array sharedMemBlocks = { SharedMemoryBlock(0, 0, KernelHandles::FontSharedMemHandle), // Shared memory for the system font (size is 0 because we read the size from the cmrc filesystem SharedMemoryBlock(0, 0x1000, KernelHandles::GSPSharedMemHandle), // GSP shared memory - SharedMemoryBlock(0, 0x1000, KernelHandles::HIDSharedMemHandle) // HID shared memory + SharedMemoryBlock(0, 0x1000, KernelHandles::HIDSharedMemHandle), // HID shared memory + SharedMemoryBlock(0, 0x3000, KernelHandles::CSNDSharedMemHandle), // CSND shared memory }; public: diff --git a/include/services/csnd.hpp b/include/services/csnd.hpp index 8a8c38e9..573acd42 100644 --- a/include/services/csnd.hpp +++ b/include/services/csnd.hpp @@ -1,16 +1,28 @@ #pragma once +#include + #include "helpers.hpp" #include "kernel_types.hpp" #include "logger.hpp" #include "memory.hpp" +// Circular dependencies ^-^ +class Kernel; + class CSNDService { Handle handle = KernelHandles::CSND; Memory& mem; + Kernel& kernel; MAKE_LOG_FUNCTION(log, csndLogger) + bool initialized = false; + std::optional csndMutex = std::nullopt; + + // Service functions + void initialize(u32 messagePointer); + public: - CSNDService(Memory& mem) : mem(mem) {} + CSNDService(Memory& mem, Kernel& kernel) : mem(mem), kernel(kernel) {} void reset(); void handleSyncRequest(u32 messagePointer); }; \ No newline at end of file diff --git a/src/core/kernel/memory_management.cpp b/src/core/kernel/memory_management.cpp index 8138c00d..a5557c6d 100644 --- a/src/core/kernel/memory_management.cpp +++ b/src/core/kernel/memory_management.cpp @@ -139,6 +139,8 @@ void Kernel::mapMemoryBlock() { mem.copySharedFont(ptr); break; + case KernelHandles::CSNDSharedMemHandle: printf("Mapping CSND memory block\n"); break; + default: Helpers::panic("Mapping unknown shared memory block: %X", block); } } else { diff --git a/src/core/services/csnd.cpp b/src/core/services/csnd.cpp index 9a78091b..bbe4823b 100644 --- a/src/core/services/csnd.cpp +++ b/src/core/services/csnd.cpp @@ -1,16 +1,61 @@ #include "services/csnd.hpp" #include "ipc.hpp" +#include "kernel.hpp" #include "result/result.hpp" -void CSNDService::reset() {} +namespace CSNDCommands { + enum : u32 { + Initialize = 0x00010140, + }; +} + +void CSNDService::reset() { + csndMutex = std::nullopt; + initialized = false; +} void CSNDService::handleSyncRequest(u32 messagePointer) { const u32 command = mem.read32(messagePointer); switch (command) { + case CSNDCommands::Initialize: initialize(messagePointer); break; + default: - Helpers::panic("Unimplemented CSND service requested. Command: %08X\n", command); + Helpers::warn("Unimplemented CSND service requested. Command: %08X\n", command); + mem.write32(messagePointer + 4, Result::Success); break; } +} + +void CSNDService::initialize(u32 messagePointer) { + u32 blockSize = mem.read32(messagePointer + 4); + const u32 offset0 = mem.read32(messagePointer + 8); + const u32 offset1 = mem.read32(messagePointer + 12); + const u32 offset2 = mem.read32(messagePointer + 16); + const u32 offset3 = mem.read32(messagePointer + 20); + + log("CSND::Initialize (Block size = %08X, offset0 = %X, offset1 = %X, offset2 = %X, offset3 = %X)\n", blockSize, offset0, offset1, offset2, offset3); + + // Align block size to 4KB. CSND shared memory block is currently stubbed to be 0x3000 == 12KB, so panic if this is more than requested + blockSize = (blockSize + 0xFFF) & ~0xFFF; + if (blockSize != 12_KB) { + Helpers::panic("Unhandled size for CSND shared memory block"); + } + + if (initialized) { + printf("CSND initialized twice\n"); + } + + if (!csndMutex.has_value()) { + csndMutex = kernel.makeMutex(false); + } + + initialized = true; + + mem.write32(messagePointer, IPC::responseHeader(0x1, 1, 3)); + mem.write32(messagePointer + 4, Result::Success); + mem.write32(messagePointer + 8, 0x4000000); + mem.write32(messagePointer + 12, csndMutex.value()); + mem.write32(messagePointer + 16, KernelHandles::CSNDSharedMemHandle); } \ No newline at end of file diff --git a/src/core/services/service_manager.cpp b/src/core/services/service_manager.cpp index 9ac08df4..52cadb9e 100644 --- a/src/core/services/service_manager.cpp +++ b/src/core/services/service_manager.cpp @@ -7,7 +7,7 @@ ServiceManager::ServiceManager(std::span regs, Memory& mem, GPU& gpu, u32& currentPID, Kernel& kernel, const EmulatorConfig& config) : regs(regs), mem(mem), kernel(kernel), ac(mem), am(mem), boss(mem), act(mem), apt(mem, kernel), cam(mem, kernel), cecd(mem, kernel), cfg(mem), - csnd(mem), dlp_srvr(mem), dsp(mem, kernel), hid(mem, kernel), http(mem), ir_user(mem, kernel), frd(mem), fs(mem, kernel, config), + csnd(mem, kernel), dlp_srvr(mem), dsp(mem, kernel), hid(mem, kernel), http(mem), ir_user(mem, kernel), frd(mem), fs(mem, kernel, config), gsp_gpu(mem, gpu, kernel, currentPID), gsp_lcd(mem), ldr(mem), mcu_hwc(mem, config), mic(mem, kernel), nfc(mem, kernel), nim(mem), ndm(mem), news_u(mem), ptm(mem, config), soc(mem), ssl(mem), y2r(mem, kernel) {} From f37b47f2d08c75266e46c9fff54d52b032bef5e3 Mon Sep 17 00:00:00 2001 From: wheremyfoodat <44909372+wheremyfoodat@users.noreply.github.com> Date: Sun, 10 Sep 2023 16:57:55 +0300 Subject: [PATCH 14/17] More NDM stubbing --- include/services/ndm.hpp | 2 ++ src/core/services/ndm.cpp | 16 ++++++++++++++++ 2 files changed, 18 insertions(+) diff --git a/include/services/ndm.hpp b/include/services/ndm.hpp index 4d21ea3e..f8a964c5 100644 --- a/include/services/ndm.hpp +++ b/include/services/ndm.hpp @@ -12,6 +12,8 @@ class NDMService { // Service commands void clearHalfAwakeMacFilter(u32 messagePointer); + void enterExclusiveState(u32 messagePointer); + void exitExclusiveState(u32 messagePointer); void overrideDefaultDaemons(u32 messagePointer); void resumeDaemons(u32 messagePointer); void resumeScheduler(u32 messagePointer); diff --git a/src/core/services/ndm.cpp b/src/core/services/ndm.cpp index ecda3df9..2c68e81f 100644 --- a/src/core/services/ndm.cpp +++ b/src/core/services/ndm.cpp @@ -3,6 +3,8 @@ namespace NDMCommands { enum : u32 { + EnterExclusiveState = 0x00010042, + ExitExclusiveState = 0x00020002, OverrideDefaultDaemons = 0x00140040, SuspendDaemons = 0x00060040, ResumeDaemons = 0x00070040, @@ -17,6 +19,8 @@ void NDMService::reset() {} void NDMService::handleSyncRequest(u32 messagePointer) { const u32 command = mem.read32(messagePointer); switch (command) { + case NDMCommands::EnterExclusiveState: enterExclusiveState(messagePointer); break; + case NDMCommands::ExitExclusiveState: exitExclusiveState(messagePointer); break; case NDMCommands::ClearHalfAwakeMacFilter: clearHalfAwakeMacFilter(messagePointer); break; case NDMCommands::OverrideDefaultDaemons: overrideDefaultDaemons(messagePointer); break; case NDMCommands::ResumeDaemons: resumeDaemons(messagePointer); break; @@ -27,6 +31,18 @@ void NDMService::handleSyncRequest(u32 messagePointer) { } } +void NDMService::enterExclusiveState(u32 messagePointer) { + log("NDM::EnterExclusiveState (stubbed)\n"); + mem.write32(messagePointer, IPC::responseHeader(0x1, 1, 0)); + mem.write32(messagePointer + 4, Result::Success); +} + +void NDMService::exitExclusiveState(u32 messagePointer) { + log("NDM::ExitExclusiveState (stubbed)\n"); + mem.write32(messagePointer, IPC::responseHeader(0x2, 1, 0)); + mem.write32(messagePointer + 4, Result::Success); +} + void NDMService::overrideDefaultDaemons(u32 messagePointer) { log("NDM::OverrideDefaultDaemons (stubbed)\n"); mem.write32(messagePointer, IPC::responseHeader(0x14, 1, 0)); From db50054128ce7f3be30c5abec16b7946b3772920 Mon Sep 17 00:00:00 2001 From: wheremyfoodat <44909372+wheremyfoodat@users.noreply.github.com> Date: Sun, 10 Sep 2023 17:33:21 +0300 Subject: [PATCH 15/17] Add nwm::UDS --- CMakeLists.txt | 4 +-- include/kernel/handles.hpp | 2 ++ include/services/nwm_uds.hpp | 28 +++++++++++++++++ include/services/service_manager.hpp | 2 ++ src/core/services/cfg.cpp | 2 ++ src/core/services/nwm_uds.cpp | 43 +++++++++++++++++++++++++++ src/core/services/service_manager.cpp | 6 ++-- 7 files changed, 83 insertions(+), 4 deletions(-) create mode 100644 include/services/nwm_uds.hpp create mode 100644 src/core/services/nwm_uds.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 5f72ff16..55b8760b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -138,7 +138,7 @@ set(SERVICE_SOURCE_FILES src/core/services/service_manager.cpp src/core/services 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/amiibo_device.cpp - src/core/services/csnd.cpp + src/core/services/csnd.cpp src/core/services/nwm_uds.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 @@ -182,7 +182,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 include/services/nfc_types.hpp include/swap.hpp include/services/csnd.hpp + include/services/amiibo_device.hpp include/services/nfc_types.hpp include/swap.hpp include/services/csnd.hpp include/services/nwm_uds.hpp ) cmrc_add_resource_library( diff --git a/include/kernel/handles.hpp b/include/kernel/handles.hpp index 4d7adcf0..749534b8 100644 --- a/include/kernel/handles.hpp +++ b/include/kernel/handles.hpp @@ -35,6 +35,7 @@ namespace KernelHandles { NFC, // NFC (Duh), used for Amiibo NIM, // Updates, DLC, etc NDM, // ????? + NWM_UDS, // Local multiplayer NEWS_U, // This service literally has 1 command (AddNotification) and I don't even understand what it does PTM, // PTM service (Used for accessing various console info, such as battery, shell and pedometer state) SOC, // Socket service @@ -90,6 +91,7 @@ namespace KernelHandles { case MIC: return "MIC"; case NDM: return "NDM"; case NEWS_U: return "NEWS_U"; + case NWM_UDS: return "nwm::UDS"; case NFC: return "NFC"; case NIM: return "NIM"; case PTM: return "PTM"; diff --git a/include/services/nwm_uds.hpp b/include/services/nwm_uds.hpp new file mode 100644 index 00000000..bf116bcf --- /dev/null +++ b/include/services/nwm_uds.hpp @@ -0,0 +1,28 @@ +#pragma once +#include + +#include "helpers.hpp" +#include "kernel_types.hpp" +#include "logger.hpp" +#include "memory.hpp" + +// More circular dependencies +class Kernel; + +class NwmUdsService { + Handle handle = KernelHandles::NWM_UDS; + Memory& mem; + Kernel& kernel; + MAKE_LOG_FUNCTION(log, nwmUdsLogger) + + bool initialized = false; + std::optional eventHandle = std::nullopt; + + // Service commands + void initializeWithVersion(u32 messagePointer); + + public: + NwmUdsService(Memory& mem, Kernel& kernel) : mem(mem), kernel(kernel) {} + void reset(); + void handleSyncRequest(u32 messagePointer); +}; \ No newline at end of file diff --git a/include/services/service_manager.hpp b/include/services/service_manager.hpp index 855585e8..5d1b37ce 100644 --- a/include/services/service_manager.hpp +++ b/include/services/service_manager.hpp @@ -28,6 +28,7 @@ #include "services/mcu/mcu_hwc.hpp" #include "services/mic.hpp" #include "services/ndm.hpp" +#include "services/nwm_uds.hpp" #include "services/news_u.hpp" #include "services/nfc.hpp" #include "services/nim.hpp" @@ -72,6 +73,7 @@ class ServiceManager { NDMService ndm; NewsUService news_u; NFCService nfc; + NwmUdsService nwm_uds; NIMService nim; PTMService ptm; SOCService soc; diff --git a/src/core/services/cfg.cpp b/src/core/services/cfg.cpp index bcb095ca..dd133a3f 100644 --- a/src/core/services/cfg.cpp +++ b/src/core/services/cfg.cpp @@ -121,6 +121,8 @@ void CFGService::getConfigInfoBlk2(u32 messagePointer) { } } else if (size == 4 && blockID == 0x170000) { // Miiverse access key mem.write32(output, 0); + } else if (size == 8 && blockID == 0x00090000) { + mem.write64(output, 0); // Some sort of key used with nwm::UDS::InitializeWithVersion } else { Helpers::panic("Unhandled GetConfigInfoBlk2 configuration. Size = %d, block = %X", size, blockID); } diff --git a/src/core/services/nwm_uds.cpp b/src/core/services/nwm_uds.cpp new file mode 100644 index 00000000..c9af393b --- /dev/null +++ b/src/core/services/nwm_uds.cpp @@ -0,0 +1,43 @@ +#include "ipc.hpp" +#include "kernel.hpp" +#include "result/result.hpp" +#include "services/nwm_uds.hpp" + +namespace NWMCommands { + enum : u32 { + InitializeWithVersion = 0x001B0302, + }; +} + +void NwmUdsService::reset() { + eventHandle = std::nullopt; + initialized = false; +} + +void NwmUdsService::handleSyncRequest(u32 messagePointer) { + const u32 command = mem.read32(messagePointer); + + switch (command) { + case NWMCommands::InitializeWithVersion: initializeWithVersion(messagePointer); break; + default: Helpers::panic("LCD service requested. Command: %08X\n", command); + } +} + +void NwmUdsService::initializeWithVersion(u32 messagePointer) { + Helpers::warn("Initializing NWM::UDS (Local multiplayer, unimplemented)\n"); + log("NWM::UDS::InitializeWithVersion\n"); + + if (!eventHandle.has_value()) { + eventHandle = kernel.makeEvent(ResetType::OneShot); + } + + if (initialized) { + printf("NWM::UDS initialized twice\n"); + } + + initialized = true; + + mem.write32(messagePointer + 4, Result::Success); + mem.write32(messagePointer + 8, 0); + mem.write32(messagePointer + 12, eventHandle.value()); +} \ No newline at end of file diff --git a/src/core/services/service_manager.cpp b/src/core/services/service_manager.cpp index 52cadb9e..0fbcdac4 100644 --- a/src/core/services/service_manager.cpp +++ b/src/core/services/service_manager.cpp @@ -9,7 +9,7 @@ ServiceManager::ServiceManager(std::span regs, Memory& mem, GPU& gpu, u : regs(regs), mem(mem), kernel(kernel), ac(mem), am(mem), boss(mem), act(mem), apt(mem, kernel), cam(mem, kernel), cecd(mem, kernel), cfg(mem), csnd(mem, kernel), dlp_srvr(mem), dsp(mem, kernel), hid(mem, kernel), http(mem), ir_user(mem, kernel), frd(mem), fs(mem, kernel, config), gsp_gpu(mem, gpu, kernel, currentPID), gsp_lcd(mem), ldr(mem), mcu_hwc(mem, config), mic(mem, kernel), nfc(mem, kernel), nim(mem), ndm(mem), - news_u(mem), ptm(mem, config), soc(mem), ssl(mem), y2r(mem, kernel) {} + news_u(mem), nwm_uds(mem, kernel), ptm(mem, config), soc(mem), ssl(mem), y2r(mem, kernel) {} static constexpr int MAX_NOTIFICATION_COUNT = 16; @@ -121,12 +121,13 @@ static std::map serviceMap = { { "ndm:u", KernelHandles::NDM }, { "news:u", KernelHandles::NEWS_U }, { "nfc:u", KernelHandles::NFC }, + { "nwm::UDS", KernelHandles::NWM_UDS }, { "nim:aoc", KernelHandles::NIM }, { "ptm:u", KernelHandles::PTM }, // TODO: ptm:u and ptm:sysm have very different command sets { "ptm:sysm", KernelHandles::PTM }, { "soc:U", KernelHandles::SOC }, { "ssl:C", KernelHandles::SSL }, - { "y2r:u", KernelHandles::Y2R } + { "y2r:u", KernelHandles::Y2R }, }; // clang-format on @@ -219,6 +220,7 @@ void ServiceManager::sendCommandToService(u32 messagePointer, Handle handle) { case KernelHandles::NIM: nim.handleSyncRequest(messagePointer); break; case KernelHandles::NDM: ndm.handleSyncRequest(messagePointer); break; case KernelHandles::NEWS_U: news_u.handleSyncRequest(messagePointer); break; + case KernelHandles::NWM_UDS: nwm_uds.handleSyncRequest(messagePointer); break; case KernelHandles::PTM: ptm.handleSyncRequest(messagePointer); break; case KernelHandles::SOC: soc.handleSyncRequest(messagePointer); break; case KernelHandles::SSL: ssl.handleSyncRequest(messagePointer); break; From 8ac22e35e9269631647e24f03e8aec163f17122b Mon Sep 17 00:00:00 2001 From: wheremyfoodat <44909372+wheremyfoodat@users.noreply.github.com> Date: Sun, 10 Sep 2023 18:50:45 +0300 Subject: [PATCH 16/17] Add CSND::AcquireSoundChannels --- include/services/csnd.hpp | 1 + src/core/services/csnd.cpp | 13 +++++++++++++ 2 files changed, 14 insertions(+) diff --git a/include/services/csnd.hpp b/include/services/csnd.hpp index 573acd42..7b99dee0 100644 --- a/include/services/csnd.hpp +++ b/include/services/csnd.hpp @@ -19,6 +19,7 @@ class CSNDService { std::optional csndMutex = std::nullopt; // Service functions + void acquireSoundChannels(u32 messagePointer); void initialize(u32 messagePointer); public: diff --git a/src/core/services/csnd.cpp b/src/core/services/csnd.cpp index bbe4823b..5f9a950a 100644 --- a/src/core/services/csnd.cpp +++ b/src/core/services/csnd.cpp @@ -7,6 +7,7 @@ namespace CSNDCommands { enum : u32 { Initialize = 0x00010140, + AcquireSoundChannels = 0x00050000, }; } @@ -19,6 +20,7 @@ void CSNDService::handleSyncRequest(u32 messagePointer) { const u32 command = mem.read32(messagePointer); switch (command) { + case CSNDCommands::AcquireSoundChannels: acquireSoundChannels(messagePointer); break; case CSNDCommands::Initialize: initialize(messagePointer); break; default: @@ -28,6 +30,17 @@ void CSNDService::handleSyncRequest(u32 messagePointer) { } } +void CSNDService::acquireSoundChannels(u32 messagePointer) { + log("CSND::AcquireSoundChannels\n"); + // The CSND service talks to the DSP using the DSP FIFO to negotiate what CSND channels are allocated to the DSP, and this seems to be channels 0-7 (usually). The rest are dedicated to CSND services. + // https://www.3dbrew.org/wiki/CSND_Services + constexpr u32 csndChannelMask = 0xFFFFFF00; + + mem.write32(messagePointer, IPC::responseHeader(0x5, 2, 0)); + mem.write32(messagePointer + 4, Result::Success); + mem.write32(messagePointer + 8, csndChannelMask); +} + void CSNDService::initialize(u32 messagePointer) { u32 blockSize = mem.read32(messagePointer + 4); const u32 offset0 = mem.read32(messagePointer + 8); From 46cf049e3bc6b36d7101de6b53c1c09df2826527 Mon Sep 17 00:00:00 2001 From: wheremyfoodat <44909372+wheremyfoodat@users.noreply.github.com> Date: Sun, 10 Sep 2023 23:00:26 +0300 Subject: [PATCH 17/17] Add CSND::ExecuteCommands --- include/logger.hpp | 1 + include/services/csnd.hpp | 9 +++++++- include/services/service_manager.hpp | 1 + src/core/kernel/memory_management.cpp | 5 ++++- src/core/services/csnd.cpp | 30 +++++++++++++++++++++++++++ 5 files changed, 44 insertions(+), 2 deletions(-) diff --git a/include/logger.hpp b/include/logger.hpp index 2ad9e713..82d90410 100644 --- a/include/logger.hpp +++ b/include/logger.hpp @@ -53,6 +53,7 @@ namespace Log { static Logger micLogger; static Logger newsLogger; static Logger nfcLogger; + static Logger nwmUdsLogger; static Logger nimLogger; static Logger ndmLogger; static Logger ptmLogger; diff --git a/include/services/csnd.hpp b/include/services/csnd.hpp index 7b99dee0..8f6d60f8 100644 --- a/include/services/csnd.hpp +++ b/include/services/csnd.hpp @@ -15,15 +15,22 @@ class CSNDService { Kernel& kernel; MAKE_LOG_FUNCTION(log, csndLogger) - bool initialized = false; + u8* sharedMemory = nullptr; std::optional csndMutex = std::nullopt; + size_t sharedMemSize = 0; + bool initialized = false; // Service functions void acquireSoundChannels(u32 messagePointer); + void executeCommands(u32 messagePointer); void initialize(u32 messagePointer); public: CSNDService(Memory& mem, Kernel& kernel) : mem(mem), kernel(kernel) {} void reset(); void handleSyncRequest(u32 messagePointer); + + void setSharedMemory(u8* ptr) { + sharedMemory = ptr; + } }; \ No newline at end of file diff --git a/include/services/service_manager.hpp b/include/services/service_manager.hpp index 5d1b37ce..93700498 100644 --- a/include/services/service_manager.hpp +++ b/include/services/service_manager.hpp @@ -103,6 +103,7 @@ class ServiceManager { void sendGPUInterrupt(GPUInterrupt type) { gsp_gpu.requestInterrupt(type); } void setGSPSharedMem(u8* ptr) { gsp_gpu.setSharedMem(ptr); } void setHIDSharedMem(u8* ptr) { hid.setSharedMem(ptr); } + void setCSNDSharedMem(u8* ptr) { csnd.setSharedMemory(ptr); } void signalDSPEvents() { dsp.signalEvents(); } diff --git a/src/core/kernel/memory_management.cpp b/src/core/kernel/memory_management.cpp index a5557c6d..e90a5697 100644 --- a/src/core/kernel/memory_management.cpp +++ b/src/core/kernel/memory_management.cpp @@ -139,7 +139,10 @@ void Kernel::mapMemoryBlock() { mem.copySharedFont(ptr); break; - case KernelHandles::CSNDSharedMemHandle: printf("Mapping CSND memory block\n"); break; + case KernelHandles::CSNDSharedMemHandle: + serviceManager.setCSNDSharedMem(ptr); + printf("Mapping CSND memory block\n"); + break; default: Helpers::panic("Mapping unknown shared memory block: %X", block); } diff --git a/src/core/services/csnd.cpp b/src/core/services/csnd.cpp index 5f9a950a..4c86c471 100644 --- a/src/core/services/csnd.cpp +++ b/src/core/services/csnd.cpp @@ -7,6 +7,7 @@ namespace CSNDCommands { enum : u32 { Initialize = 0x00010140, + ExecuteCommands = 0x00030040, AcquireSoundChannels = 0x00050000, }; } @@ -14,6 +15,8 @@ namespace CSNDCommands { void CSNDService::reset() { csndMutex = std::nullopt; initialized = false; + sharedMemory = nullptr; + sharedMemSize = 0; } void CSNDService::handleSyncRequest(u32 messagePointer) { @@ -21,6 +24,7 @@ void CSNDService::handleSyncRequest(u32 messagePointer) { switch (command) { case CSNDCommands::AcquireSoundChannels: acquireSoundChannels(messagePointer); break; + case CSNDCommands::ExecuteCommands: executeCommands(messagePointer); break; case CSNDCommands::Initialize: initialize(messagePointer); break; default: @@ -65,10 +69,36 @@ void CSNDService::initialize(u32 messagePointer) { } initialized = true; + sharedMemSize = blockSize; mem.write32(messagePointer, IPC::responseHeader(0x1, 1, 3)); mem.write32(messagePointer + 4, Result::Success); mem.write32(messagePointer + 8, 0x4000000); mem.write32(messagePointer + 12, csndMutex.value()); mem.write32(messagePointer + 16, KernelHandles::CSNDSharedMemHandle); +} + +void CSNDService::executeCommands(u32 messagePointer) { + const u32 offset = mem.read32(messagePointer + 4); + log("CSND::ExecuteCommands (command offset = %X)\n", offset); + + mem.write32(messagePointer, IPC::responseHeader(0x5, 2, 0)); + + if (!sharedMemory) { + Helpers::warn("CSND::Execute commands without shared memory"); + mem.write32(messagePointer + 4, Result::FailurePlaceholder); + return; + } + + mem.write32(messagePointer + 4, Result::Success); + + // This is initially zero when this command data is written by the user process, once the CSND module finishes processing the command this is set + // to 0x1. This flag is only set to value 1 for the first command(once processing for the entire command chain is finished) at the offset + // specified in the service command, not all type0 commands in the chain. + constexpr u32 commandListDoneOffset = 0x4; + + // Make sure to not access OoB of the shared memory block when marking command list processing as finished + if (offset + commandListDoneOffset < sharedMemSize) { + sharedMemory[offset + commandListDoneOffset] = 1; + } } \ No newline at end of file