From 2f5bb45d587e554bc5c50b8edeb69bbdf84e35f3 Mon Sep 17 00:00:00 2001 From: wheremyfoodat Date: Tue, 16 May 2023 20:49:31 +0300 Subject: [PATCH 01/15] [FS] OpenArchiveHandle returns Result --- include/services/fs.hpp | 2 +- src/core/fs/archive_sdmc.cpp | 2 +- src/core/services/fs.cpp | 16 ++++++++-------- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/include/services/fs.hpp b/include/services/fs.hpp index 48fada6e..e79dbf02 100644 --- a/include/services/fs.hpp +++ b/include/services/fs.hpp @@ -31,7 +31,7 @@ class FSService { ExtSaveDataArchive sharedExtSaveData_cart; ArchiveBase* getArchiveFromID(u32 id, const FSPath& archivePath); - std::optional openArchiveHandle(u32 archiveID, const FSPath& path); + Rust::Result openArchiveHandle(u32 archiveID, const FSPath& path); Rust::Result openDirectoryHandle(ArchiveBase* archive, const FSPath& path); std::optional openFileHandle(ArchiveBase* archive, const FSPath& path, const FSPath& archivePath, const FilePerms& perms); FSPath readPath(u32 type, u32 pointer, u32 size); diff --git a/src/core/fs/archive_sdmc.cpp b/src/core/fs/archive_sdmc.cpp index 23564eaa..0759fcee 100644 --- a/src/core/fs/archive_sdmc.cpp +++ b/src/core/fs/archive_sdmc.cpp @@ -18,7 +18,7 @@ FileDescriptor SDMCArchive::openFile(const FSPath& path, const FilePerms& perms) Rust::Result SDMCArchive::openArchive(const FSPath& path) { printf("SDMCArchive::OpenArchive: Failed\n"); - return Ok((ArchiveBase*)nullptr); + return Err(FSResult::NotFormatted); } std::optional SDMCArchive::readFile(FileSession* file, u64 offset, u32 size, u32 dataPointer) { diff --git a/src/core/services/fs.cpp b/src/core/services/fs.cpp index 080c0b49..b790e229 100644 --- a/src/core/services/fs.cpp +++ b/src/core/services/fs.cpp @@ -121,12 +121,12 @@ Rust::Result FSService::openDirectoryHandle(ArchiveBase* archi } } -std::optional FSService::openArchiveHandle(u32 archiveID, const FSPath& path) { +Rust::Result FSService::openArchiveHandle(u32 archiveID, const FSPath& path) { ArchiveBase* archive = getArchiveFromID(archiveID, path); if (archive == nullptr) [[unlikely]] { Helpers::panic("OpenArchive: Tried to open unknown archive %d.", archiveID); - return std::nullopt; + return Err(FSResult::NotFormatted); } Rust::Result res = archive->openArchive(path); @@ -135,10 +135,10 @@ std::optional FSService::openArchiveHandle(u32 archiveID, const FSPath& auto& archiveObject = kernel.getObjects()[handle]; archiveObject.data = new ArchiveSession(res.unwrap(), path); - return handle; + return Ok(handle); } else { - return std::nullopt; + return Err(res.unwrapErr()); } } @@ -216,14 +216,14 @@ void FSService::openArchive(u32 messagePointer) { auto archivePath = readPath(archivePathType, archivePathPointer, archivePathSize); log("FS::OpenArchive(archive ID = %d, archive path type = %d)\n", archiveID, archivePathType); - std::optional handle = openArchiveHandle(archiveID, archivePath); + Rust::Result res = openArchiveHandle(archiveID, archivePath); mem.write32(messagePointer, IPC::responseHeader(0x80C, 3, 0)); - if (handle.has_value()) { + if (res.isOk()) { mem.write32(messagePointer + 4, ResultCode::Success); - mem.write64(messagePointer + 8, handle.value()); + mem.write64(messagePointer + 8, res.unwrap()); } else { log("FS::OpenArchive: Failed to open archive with id = %d\n", archiveID); - mem.write32(messagePointer + 4, ResultCode::Failure); + mem.write32(messagePointer + 4, static_cast(res.unwrapErr())); } } From 4dc04be350985c27dff651c63f16556d3145521c Mon Sep 17 00:00:00 2001 From: wheremyfoodat Date: Tue, 16 May 2023 23:37:52 +0300 Subject: [PATCH 02/15] [FS] Formatting archives v1 --- include/fs/archive_base.hpp | 4 ++++ include/fs/archive_ext_save_data.hpp | 4 ++++ include/fs/archive_save_data.hpp | 8 +++++++- src/core/fs/archive_ext_save_data.cpp | 17 +++++++++++++++++ src/core/fs/archive_save_data.cpp | 22 ++++++++++++++++++++-- src/core/services/fs.cpp | 16 +++++++++++++++- 6 files changed, 67 insertions(+), 4 deletions(-) diff --git a/include/fs/archive_base.hpp b/include/fs/archive_base.hpp index 4e5f99f1..8de78ad3 100644 --- a/include/fs/archive_base.hpp +++ b/include/fs/archive_base.hpp @@ -224,6 +224,10 @@ public: return Err(FSResult::FileNotFound); } + virtual void format(const FSPath& path, const FormatInfo& info) { + Helpers::panic("Unimplemented Format for %s archive", name().c_str()); + } + // Read size bytes from a file starting at offset "offset" into a certain buffer in memory // Returns the number of bytes read, or nullopt if the read failed virtual std::optional readFile(FileSession* file, u64 offset, u32 size, u32 dataPointer) = 0; diff --git a/include/fs/archive_ext_save_data.hpp b/include/fs/archive_ext_save_data.hpp index b2eab9b7..95c822e9 100644 --- a/include/fs/archive_ext_save_data.hpp +++ b/include/fs/archive_ext_save_data.hpp @@ -17,6 +17,10 @@ public: FileDescriptor openFile(const FSPath& path, const FilePerms& perms) override; std::optional readFile(FileSession* file, u64 offset, u32 size, u32 dataPointer) override; + // 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); + bool isShared = false; std::string backingFolder; // Backing folder for the archive. Can be NAND, Gamecard or SD depending on the archive path. }; \ No newline at end of file diff --git a/include/fs/archive_save_data.hpp b/include/fs/archive_save_data.hpp index 4bc0a15e..f7642955 100644 --- a/include/fs/archive_save_data.hpp +++ b/include/fs/archive_save_data.hpp @@ -11,13 +11,19 @@ public: FSResult createDirectory(const FSPath& path) override; FSResult createFile(const FSPath& path, u64 size) override; FSResult deleteFile(const FSPath& path) override; - FormatInfo getFormatInfo(const FSPath& path) override; Rust::Result openArchive(const FSPath& path) override; Rust::Result openDirectory(const FSPath& path) override; FileDescriptor openFile(const FSPath& path, const FilePerms& perms) override; std::optional readFile(FileSession* file, u64 offset, u32 size, u32 dataPointer) override; + void format(const FSPath& path, const FormatInfo& info) override; + FormatInfo getFormatInfo(const FSPath& path) override; + + std::filesystem::path getFormatInfoPath() { + return IOFile::getAppData() / "FormatInfo" / "SaveData.format"; + } + // Returns whether the cart has save data or not bool cartHasSaveData() { auto cxi = mem.getCXI(); diff --git a/src/core/fs/archive_ext_save_data.cpp b/src/core/fs/archive_ext_save_data.cpp index 375ba8bc..51b89aca 100644 --- a/src/core/fs/archive_ext_save_data.cpp +++ b/src/core/fs/archive_ext_save_data.cpp @@ -70,11 +70,28 @@ FileDescriptor ExtSaveDataArchive::openFile(const FSPath& path, const FilePerms& return FileError; } +std::string ExtSaveDataArchive::getExtSaveDataPathFromBinary(const FSPath& path) { + // TODO: Remove punning here + const u32 mediaType = *(u32*)&path.binary[0]; + const u32 saveLow = *(u32*)&path.binary[4]; + const u32 saveHigh = *(u32*)&path.binary[8]; + + // TODO: Should the media type be used here + return backingFolder + std::to_string(saveLow) + std::to_string(saveHigh); +} + Rust::Result ExtSaveDataArchive::openArchive(const FSPath& path) { if (path.type != PathType::Binary || path.binary.size() != 12) { Helpers::panic("ExtSaveData accessed with an invalid path in OpenArchive"); } + // Create a format info path in the style of AppData/FormatInfo/Cartridge10390390194.format + fs::path formatInfopath = IOFile::getAppData() / "FormatInfo" / (getExtSaveDataPathFromBinary(path) + ".format"); + // Format info not found so the archive is not formatted + if (!fs::is_regular_file(formatInfopath)) { + return isShared ? Err(FSResult::NotFormatted) : Err(FSResult::NotFoundInvalid); + } + return Ok((ArchiveBase*)this); } diff --git a/src/core/fs/archive_save_data.cpp b/src/core/fs/archive_save_data.cpp index 4e8051ac..a25ba08f 100644 --- a/src/core/fs/archive_save_data.cpp +++ b/src/core/fs/archive_save_data.cpp @@ -94,8 +94,20 @@ Rust::Result SaveDataArchive::openDirectory(const FS } ArchiveBase::FormatInfo SaveDataArchive::getFormatInfo(const FSPath& path) { - //Helpers::panic("Unimplemented SaveData::GetFormatInfo"); - return FormatInfo{ .size = 0, .numOfDirectories = 255, .numOfFiles = 255, .duplicateData = false }; + Helpers::panic("Unimplemented SaveData::GetFormatInfo"); +} + +void SaveDataArchive::format(const FSPath& path, const ArchiveBase::FormatInfo& info) { + const fs::path saveDataPath = IOFile::getAppData() / "SaveData"; + const fs::path formatInfoPath = getFormatInfoPath(); + + // Delete all contents by deleting the directory then recreating it + fs::remove_all(saveDataPath); + fs::create_directories(saveDataPath); + + // Write format info on disk + IOFile file(formatInfoPath, "wb"); + file.writeBytes(&info, sizeof(info)); } Rust::Result SaveDataArchive::openArchive(const FSPath& path) { @@ -104,6 +116,12 @@ Rust::Result SaveDataArchive::openArchive(const FSPath& return Err(FSResult::NotFoundInvalid); } + const fs::path formatInfoPath = getFormatInfoPath(); + // Format info not found so the archive is not formatted + if (!fs::is_regular_file(formatInfoPath)) { + return Err(FSResult::NotFormatted); + } + return Ok((ArchiveBase*)this); } diff --git a/src/core/services/fs.cpp b/src/core/services/fs.cpp index b790e229..39766130 100644 --- a/src/core/services/fs.cpp +++ b/src/core/services/fs.cpp @@ -48,6 +48,7 @@ void FSService::initializeFilesystem() { const auto nandPath = IOFile::getAppData() / "NAND"; // Create NAND const auto cartPath = IOFile::getAppData() / "CartSave"; // Create cartridge save folder for use with ExtSaveData const auto savePath = IOFile::getAppData() / "SaveData"; // Create SaveData + const auto formatPath = IOFile::getAppData() / "FormatInfo"; // Create folder for storing archive formatting info namespace fs = std::filesystem; // TODO: SDMC, etc @@ -62,6 +63,10 @@ void FSService::initializeFilesystem() { if (!fs::is_directory(savePath)) { fs::create_directories(savePath); } + + if (!fs::is_directory(formatPath)) { + fs::create_directories(formatPath); + } } ArchiveBase* FSService::getArchiveFromID(u32 id, const FSPath& archivePath) { @@ -440,9 +445,18 @@ void FSService::formatSaveData(u32 messagePointer) { const u32 fileNum = mem.read32(messagePointer + 24); // Max number of files const u32 directoryBucketNum = mem.read32(messagePointer + 28); // Not sure what a directory bucket is...? const u32 fileBucketNum = mem.read32(messagePointer + 32); // Same here - const bool duplicateData = mem.read8(messagePointer + 36) != 0; + const bool duplicateData = mem.read8(messagePointer + 36) != 0; + + ArchiveBase::FormatInfo info { + .size = blockSize * 0x200, + .numOfDirectories = directoryNum, + .numOfFiles = fileNum, + .duplicateData = duplicateData + }; printf("Stubbed FS::FormatSaveData. File num: %d, directory num: %d\n", fileNum, directoryNum); + saveData.format(path, info); + mem.write32(messagePointer, IPC::responseHeader(0x84C, 1, 0)); mem.write32(messagePointer + 4, ResultCode::Success); } From e7085dd8300a677caee2431fd8dad6104f14752e Mon Sep 17 00:00:00 2001 From: wheremyfoodat Date: Wed, 17 May 2023 01:17:10 +0300 Subject: [PATCH 03/15] [DSP] HLE DSP state & some of the audio pipe --- include/services/dsp.hpp | 13 ++++++++ src/core/services/dsp.cpp | 69 ++++++++++++++++++++++++++++++++++++++- 2 files changed, 81 insertions(+), 1 deletion(-) diff --git a/include/services/dsp.hpp b/include/services/dsp.hpp index 5fe42a89..774c25de 100644 --- a/include/services/dsp.hpp +++ b/include/services/dsp.hpp @@ -43,6 +43,12 @@ public: } }; +namespace DSPPipeType { + enum : u32 { + Debug = 0, DMA = 1, Audio = 2, Binary = 3 + }; +} + // Circular dependencies! class Kernel; @@ -52,9 +58,14 @@ class DSPService { Kernel& kernel; MAKE_LOG_FUNCTION(log, dspServiceLogger) + enum class DSPState : u32 { + Off, On, Slep + }; + // Number of DSP pipes static constexpr size_t pipeCount = 8; DSPPipe audioPipe; + DSPState dspState; // DSP service event handles using DSPEvent = std::optional; @@ -78,6 +89,8 @@ class DSPService { void invalidateDCache(u32 messagePointer); void loadComponent(u32 messagePointer); void readPipeIfPossible(u32 messagePointer); + void recvData(u32 messagePointer); + void recvDataIsReady(u32 messagePointer); void registerInterruptEvents(u32 messagePointer); void setSemaphore(u32 messagePointer); void setSemaphoreMask(u32 messagePointer); diff --git a/src/core/services/dsp.cpp b/src/core/services/dsp.cpp index e94fc92d..a8485794 100644 --- a/src/core/services/dsp.cpp +++ b/src/core/services/dsp.cpp @@ -4,6 +4,8 @@ namespace DSPCommands { enum : u32 { + RecvData = 0x00010040, + RecvDataIsReady = 0x00020040, SetSemaphore = 0x00070040, ConvertProcessAddressFromDspDram = 0x000C0040, WriteProcessPipe = 0x000D0082, @@ -29,6 +31,7 @@ namespace Result { void DSPService::reset() { audioPipe.reset(); totalEventCount = 0; + dspState = DSPState::Off; semaphoreEvent = std::nullopt; interrupt0 = std::nullopt; @@ -49,6 +52,8 @@ void DSPService::handleSyncRequest(u32 messagePointer) { case DSPCommands::GetSemaphoreEventHandle: getSemaphoreEventHandle(messagePointer); break; case DSPCommands::LoadComponent: loadComponent(messagePointer); break; case DSPCommands::ReadPipeIfPossible: readPipeIfPossible(messagePointer); break; + case DSPCommands::RecvData: recvData(messagePointer); break; + case DSPCommands::RecvDataIsReady: recvDataIsReady(messagePointer); break; case DSPCommands::RegisterInterruptEvents: registerInterruptEvents(messagePointer); break; case DSPCommands::SetSemaphore: setSemaphore(messagePointer); break; case DSPCommands::SetSemaphoreMask: setSemaphoreMask(messagePointer); break; @@ -107,6 +112,29 @@ void DSPService::readPipeIfPossible(u32 messagePointer) { mem.write16(messagePointer + 8, i); // Number of bytes read } +void DSPService::recvData(u32 messagePointer) { + const u32 registerIndex = mem.read32(messagePointer + 4); + log("DSP::RecvData (register = %d)\n", registerIndex); + if (registerIndex != 0) Helpers::panic("Unknown register in DSP::RecvData"); + + // Return 0 if the DSP is running, otherwise 1 + const u16 ret = dspState == DSPState::On ? 0 : 1; + + mem.write32(messagePointer, IPC::responseHeader(0x01, 2, 0)); + mem.write32(messagePointer + 4, Result::Success); + mem.write16(messagePointer + 8, ret); // Always return that the DSP is on +} + +void DSPService::recvDataIsReady(u32 messagePointer) { + const u32 registerIndex = mem.read32(messagePointer + 4); + log("DSP::RecvDataIsReady (register = %d)\n", registerIndex); + if (registerIndex != 0) Helpers::panic("Unknown register in DSP::RecvDataIsReady"); + + mem.write32(messagePointer, IPC::responseHeader(0x02, 2, 0)); + mem.write32(messagePointer + 4, Result::Success); + mem.write32(messagePointer + 8, 1); // Always return that the register is ready for now +} + DSPService::DSPEvent& DSPService::getEventRef(u32 type, u32 pipe) { switch (type) { case 0: return interrupt0; @@ -192,8 +220,47 @@ void DSPService::writeProcessPipe(u32 messagePointer) { const u32 channel = mem.read32(messagePointer + 4); const u32 size = mem.read32(messagePointer + 8); const u32 buffer = mem.read32(messagePointer + 16); - log("DSP::writeProcessPipe (channel = %d, size = %X, buffer = %08X)\n", channel, size, buffer); + + enum class StateChange : u8 { + Initialize = 0, + Shutdown = 1, + Wakeup = 2, + Sleep = 3, + }; + + switch (channel) { + case DSPPipeType::Audio: { + if (size != 4) { + printf("Invalid size written to DSP Audio Pipe\n"); + break; + } + + // Get new state + const u8 state = mem.read8(buffer); + if (state > 3) { + log("WriteProcessPipe::Audio: Unknown state change type"); + } else { + switch (static_cast(state)) { + case StateChange::Initialize: + // TODO: Other initialization stuff here + dspState = DSPState::On; + break; + + case StateChange::Shutdown: + dspState = DSPState::Off; + break; + + default: Helpers::panic("Unimplemented DSP audio pipe state change %d", state); + } + } + } + + default: + log("DSP: Wrote to unimplemented pipe %d\n", channel); + break; + } + mem.write32(messagePointer, IPC::responseHeader(0xD, 1, 0)); mem.write32(messagePointer + 4, Result::Success); } From 35aea216795a252b075fa5d4b5e885debd5cde84 Mon Sep 17 00:00:00 2001 From: wheremyfoodat Date: Wed, 17 May 2023 01:33:39 +0300 Subject: [PATCH 04/15] [DSP] Add UnloadComponent --- include/services/dsp.hpp | 1 + src/core/services/dsp.cpp | 18 +++++++++++++++--- 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/include/services/dsp.hpp b/include/services/dsp.hpp index 774c25de..b9bc7d54 100644 --- a/include/services/dsp.hpp +++ b/include/services/dsp.hpp @@ -94,6 +94,7 @@ class DSPService { void registerInterruptEvents(u32 messagePointer); void setSemaphore(u32 messagePointer); void setSemaphoreMask(u32 messagePointer); + void unloadComponent(u32 messagePointer); void writeProcessPipe(u32 messagePointer); public: diff --git a/src/core/services/dsp.cpp b/src/core/services/dsp.cpp index a8485794..179b899c 100644 --- a/src/core/services/dsp.cpp +++ b/src/core/services/dsp.cpp @@ -11,6 +11,7 @@ namespace DSPCommands { WriteProcessPipe = 0x000D0082, ReadPipeIfPossible = 0x001000C0, LoadComponent = 0x001100C2, + UnloadComponent = 0x00120000, FlushDataCache = 0x00130082, InvalidateDataCache = 0x00140082, RegisterInterruptEvents = 0x00150082, @@ -52,11 +53,12 @@ void DSPService::handleSyncRequest(u32 messagePointer) { case DSPCommands::GetSemaphoreEventHandle: getSemaphoreEventHandle(messagePointer); break; case DSPCommands::LoadComponent: loadComponent(messagePointer); break; case DSPCommands::ReadPipeIfPossible: readPipeIfPossible(messagePointer); break; - case DSPCommands::RecvData: recvData(messagePointer); break; - case DSPCommands::RecvDataIsReady: recvDataIsReady(messagePointer); break; + case DSPCommands::RecvData: [[likely]] recvData(messagePointer); break; + case DSPCommands::RecvDataIsReady: [[likely]] recvDataIsReady(messagePointer); break; case DSPCommands::RegisterInterruptEvents: registerInterruptEvents(messagePointer); break; case DSPCommands::SetSemaphore: setSemaphore(messagePointer); break; case DSPCommands::SetSemaphoreMask: setSemaphoreMask(messagePointer); break; + case DSPCommands::UnloadComponent: unloadComponent(messagePointer); break; case DSPCommands::WriteProcessPipe: [[likely]] writeProcessPipe(messagePointer); break; default: Helpers::panic("DSP service requested. Command: %08X\n", command); } @@ -85,6 +87,12 @@ void DSPService::loadComponent(u32 messagePointer) { mem.write32(messagePointer + 16, mem.read32(messagePointer + 20)); // Component buffer } +void DSPService::unloadComponent(u32 messagePointer) { + log("DSP::UnloadComponent\n"); + mem.write32(messagePointer, IPC::responseHeader(0x12, 1, 0)); + mem.write32(messagePointer + 4, Result::Success); +} + void DSPService::readPipeIfPossible(u32 messagePointer) { u32 channel = mem.read32(messagePointer + 4); u32 peer = mem.read32(messagePointer + 8); @@ -158,7 +166,11 @@ void DSPService::registerInterruptEvents(u32 messagePointer) { // The event handle being 0 means we're removing an event if (eventHandle == 0) { - Helpers::panic("DSP::DSP::RegisterinterruptEvents Trying to remove a registered interrupt"); + DSPEvent& e = getEventRef(interrupt, channel); // Get event + if (e.has_value()) { // Remove if it exists + totalEventCount--; + e = std::nullopt; + } } else { const KernelObject* object = kernel.getObject(eventHandle, KernelObjectType::Event); if (!object) { From 6369fc8eb02e2814fdec448c824ac9687f9f3e5b Mon Sep 17 00:00:00 2001 From: wheremyfoodat Date: Wed, 17 May 2023 02:34:48 +0300 Subject: [PATCH 05/15] [DSP] Annotate pipe 2 addresses --- include/services/dsp.hpp | 34 ++++++++++++++++++---------------- 1 file changed, 18 insertions(+), 16 deletions(-) diff --git a/include/services/dsp.hpp b/include/services/dsp.hpp index b9bc7d54..a63c4e3f 100644 --- a/include/services/dsp.hpp +++ b/include/services/dsp.hpp @@ -8,23 +8,25 @@ // Stub for DSP audio pipe class DSPPipe { // Hardcoded responses for now + // These are DSP DRAM offsets for various variables + // https://www.3dbrew.org/wiki/DSP_Memory_Region static constexpr std::array pipeData = { - 0x000F, //Number of responses - 0xBFFF, - 0x9E8E, - 0x8680, - 0xA78E, - 0x9430, - 0x8400, - 0x8540, - 0x948E, - 0x8710, - 0x8410, - 0xA90E, - 0xAA0E, - 0xAACE, - 0xAC4E, - 0xAC58 + 0x000F, // Number of responses + 0xBFFF, // Frame counter + 0x9E92, // Source configs + 0x8680, // Source statuses + 0xA792, // ADPCM coefficients + 0x9430, // DSP configs + 0x8400, // DSP status + 0x8540, // Final samples + 0x9492, // Intermediate mix samples + 0x8710, // Compressor + 0x8410, // Debug + 0xA912, // ?? + 0xAA12, // ?? + 0xAAD2, // ?? + 0xAC52, // Surround sound biquad filter 1 + 0xAC5C // Surround sound biquad filter 2 }; uint index = 0; From 5a1cc39cb6028b36cf417277568e0d4396e58f8a Mon Sep 17 00:00:00 2001 From: wheremyfoodat Date: Thu, 18 May 2023 03:36:19 +0300 Subject: [PATCH 06/15] [DSP] Reset audio pipe properly --- src/core/services/dsp.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/core/services/dsp.cpp b/src/core/services/dsp.cpp index 179b899c..8b11c6bf 100644 --- a/src/core/services/dsp.cpp +++ b/src/core/services/dsp.cpp @@ -257,6 +257,7 @@ void DSPService::writeProcessPipe(u32 messagePointer) { case StateChange::Initialize: // TODO: Other initialization stuff here dspState = DSPState::On; + audioPipe.reset(); break; case StateChange::Shutdown: From c7f676efd2d31a36ec6b2f496b0338525fbe2a16 Mon Sep 17 00:00:00 2001 From: wheremyfoodat Date: Thu, 18 May 2023 03:44:29 +0300 Subject: [PATCH 07/15] [DSP] Add missing break --- src/core/fs/archive_save_data.cpp | 2 +- src/core/services/dsp.cpp | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/core/fs/archive_save_data.cpp b/src/core/fs/archive_save_data.cpp index a25ba08f..52ca6ad0 100644 --- a/src/core/fs/archive_save_data.cpp +++ b/src/core/fs/archive_save_data.cpp @@ -106,7 +106,7 @@ void SaveDataArchive::format(const FSPath& path, const ArchiveBase::FormatInfo& fs::create_directories(saveDataPath); // Write format info on disk - IOFile file(formatInfoPath, "wb"); + IOFile file(formatInfoPath, "wb+"); file.writeBytes(&info, sizeof(info)); } diff --git a/src/core/services/dsp.cpp b/src/core/services/dsp.cpp index 8b11c6bf..3ba962ad 100644 --- a/src/core/services/dsp.cpp +++ b/src/core/services/dsp.cpp @@ -267,6 +267,7 @@ void DSPService::writeProcessPipe(u32 messagePointer) { default: Helpers::panic("Unimplemented DSP audio pipe state change %d", state); } } + break; } default: From a530350edaf3eae6a73f32f4830ca818adef3fc0 Mon Sep 17 00:00:00 2001 From: wheremyfoodat Date: Sat, 20 May 2023 01:04:51 +0300 Subject: [PATCH 08/15] [FS] Media type = 2 is cart, not 1... --- src/core/services/fs.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/core/services/fs.cpp b/src/core/services/fs.cpp index 39766130..9a3a7466 100644 --- a/src/core/services/fs.cpp +++ b/src/core/services/fs.cpp @@ -77,7 +77,7 @@ ArchiveBase* FSService::getArchiveFromID(u32 id, const FSPath& archivePath) { if (archivePath.type == PathType::Binary) { switch (archivePath.binary[0]) { case 0: return &extSaveData_nand; - case 1: return &extSaveData_cart; + case 2: return &extSaveData_cart; } } return nullptr; @@ -86,7 +86,7 @@ ArchiveBase* FSService::getArchiveFromID(u32 id, const FSPath& archivePath) { if (archivePath.type == PathType::Binary) { switch (archivePath.binary[0]) { case 0: return &sharedExtSaveData_nand; - case 1: return &sharedExtSaveData_cart; + case 2: return &sharedExtSaveData_cart; } } return nullptr; @@ -227,8 +227,9 @@ void FSService::openArchive(u32 messagePointer) { mem.write32(messagePointer + 4, ResultCode::Success); mem.write64(messagePointer + 8, res.unwrap()); } else { - log("FS::OpenArchive: Failed to open archive with id = %d\n", archiveID); + log("FS::OpenArchive: Failed to open archive with id = %d. Error %08X\n", archiveID, (u32)res.unwrapErr()); mem.write32(messagePointer + 4, static_cast(res.unwrapErr())); + mem.write64(messagePointer + 8, 0); } } @@ -454,7 +455,6 @@ void FSService::formatSaveData(u32 messagePointer) { .duplicateData = duplicateData }; - printf("Stubbed FS::FormatSaveData. File num: %d, directory num: %d\n", fileNum, directoryNum); saveData.format(path, info); mem.write32(messagePointer, IPC::responseHeader(0x84C, 1, 0)); From 9ec29646959b8d8562ef15efd10f0551713b89bb Mon Sep 17 00:00:00 2001 From: wheremyfoodat Date: Sat, 20 May 2023 01:24:30 +0300 Subject: [PATCH 09/15] [FS] Add FormatThisUserSaveData --- include/services/fs.hpp | 1 + src/core/fs/archive_save_data.cpp | 2 +- src/core/services/fs.cpp | 26 ++++++++++++++++++++++++++ 3 files changed, 28 insertions(+), 1 deletion(-) diff --git a/include/services/fs.hpp b/include/services/fs.hpp index e79dbf02..b4cc1fb6 100644 --- a/include/services/fs.hpp +++ b/include/services/fs.hpp @@ -43,6 +43,7 @@ class FSService { void controlArchive(u32 messagePointer); void deleteFile(u32 messagePointer); void formatSaveData(u32 messagePointer); + void formatThisUserSaveData(u32 messagePointer); void getFreeBytes(u32 messagePointer); void getFormatInfo(u32 messagePointer); void getPriority(u32 messagePointer); diff --git a/src/core/fs/archive_save_data.cpp b/src/core/fs/archive_save_data.cpp index 52ca6ad0..a25ba08f 100644 --- a/src/core/fs/archive_save_data.cpp +++ b/src/core/fs/archive_save_data.cpp @@ -106,7 +106,7 @@ void SaveDataArchive::format(const FSPath& path, const ArchiveBase::FormatInfo& fs::create_directories(saveDataPath); // Write format info on disk - IOFile file(formatInfoPath, "wb+"); + IOFile file(formatInfoPath, "wb"); file.writeBytes(&info, sizeof(info)); } diff --git a/src/core/services/fs.cpp b/src/core/services/fs.cpp index 9a3a7466..fd85b484 100644 --- a/src/core/services/fs.cpp +++ b/src/core/services/fs.cpp @@ -21,6 +21,7 @@ namespace FSCommands { OpenArchive = 0x080C00C2, ControlArchive = 0x080D0144, CloseArchive = 0x080E0080, + FormatThisUserSaveData = 0x080F0180, GetFreeBytes = 0x08120080, IsSdmcDetected = 0x08170000, GetFormatInfo = 0x084500C2, @@ -166,6 +167,7 @@ void FSService::handleSyncRequest(u32 messagePointer) { case FSCommands::CloseArchive: closeArchive(messagePointer); break; case FSCommands::DeleteFile: deleteFile(messagePointer); break; case FSCommands::FormatSaveData: formatSaveData(messagePointer); break; + case FSCommands::FormatThisUserSaveData: formatThisUserSaveData(messagePointer); break; case FSCommands::GetFreeBytes: getFreeBytes(messagePointer); break; case FSCommands::GetFormatInfo: getFormatInfo(messagePointer); break; case FSCommands::GetPriority: getPriority(messagePointer); break; @@ -427,6 +429,8 @@ void FSService::getFormatInfo(u32 messagePointer) { } void FSService::formatSaveData(u32 messagePointer) { + log("FS::FormatSaveData\n"); + const u32 archiveID = mem.read32(messagePointer + 4); if (archiveID != ArchiveID::SaveData) Helpers::panic("FS::FormatSaveData: Archive is not SaveData"); @@ -461,6 +465,28 @@ void FSService::formatSaveData(u32 messagePointer) { mem.write32(messagePointer + 4, ResultCode::Success); } +void FSService::formatThisUserSaveData(u32 messagePointer) { + log("FS::FormatThisUserSaveData\n"); + + const u32 blockSize = mem.read32(messagePointer + 4); + const u32 directoryNum = mem.read32(messagePointer + 8); // Max number of directories + const u32 fileNum = mem.read32(messagePointer + 12); // Max number of files + const u32 directoryBucketNum = mem.read32(messagePointer + 16); // Not sure what a directory bucket is...? + const u32 fileBucketNum = mem.read32(messagePointer + 20); // Same here + const bool duplicateData = mem.read8(messagePointer + 24) != 0; + + ArchiveBase::FormatInfo info { + .size = blockSize * 0x200, + .numOfDirectories = directoryNum, + .numOfFiles = fileNum, + .duplicateData = duplicateData + }; + FSPath emptyPath; + + mem.write32(messagePointer, IPC::responseHeader(0x080F, 1, 0)); + saveData.format(emptyPath, info); +} + void FSService::controlArchive(u32 messagePointer) { const Handle archiveHandle = mem.read64(messagePointer + 4); const u32 action = mem.read32(messagePointer + 12); From 01d16fdfd1abd79429b7544cf45a35bc8258d30a Mon Sep 17 00:00:00 2001 From: wheremyfoodat Date: Sat, 20 May 2023 02:32:36 +0300 Subject: [PATCH 10/15] [FS] Better DeleteFile, fclose when file session is closed --- src/core/fs/archive_ext_save_data.cpp | 19 ++++++++++++++++-- src/core/fs/archive_save_data.cpp | 29 ++++++++++++++++++++++++++- src/core/kernel/file_operations.cpp | 7 ++++++- 3 files changed, 51 insertions(+), 4 deletions(-) diff --git a/src/core/fs/archive_ext_save_data.cpp b/src/core/fs/archive_ext_save_data.cpp index 51b89aca..788fd251 100644 --- a/src/core/fs/archive_ext_save_data.cpp +++ b/src/core/fs/archive_ext_save_data.cpp @@ -38,12 +38,27 @@ FSResult ExtSaveDataArchive::deleteFile(const FSPath& path) { fs::path p = IOFile::getAppData() / backingFolder; p += fs::path(path.utf16_string).make_preferred(); + if (fs::is_directory(p)) { + Helpers::panic("ExtSaveData::DeleteFile: Tried to delete directory"); + } + + if (!fs::is_regular_file(p)) { + return FSResult::FileNotFound; + } + std::error_code ec; bool success = fs::remove(p, ec); - return success ? FSResult::Success : FSResult::FileNotFound; + + // It might still be possible for fs::remove to fail, if there's eg an open handle to a file being deleted + // In this case, print a warning, but still return success for now + if (!success) { + Helpers::warn("ExtSaveData::DeleteFile: fs::remove failed\n"); + } + + return FSResult::Success; } - Helpers::panic("ExtSaveDataArchive::DeleteFile: Failed"); + Helpers::panic("ExtSaveDataArchive::DeleteFile: Unknown path type"); return FSResult::Success; } diff --git a/src/core/fs/archive_save_data.cpp b/src/core/fs/archive_save_data.cpp index a25ba08f..57d2f2fb 100644 --- a/src/core/fs/archive_save_data.cpp +++ b/src/core/fs/archive_save_data.cpp @@ -31,7 +31,34 @@ FSResult SaveDataArchive::createDirectory(const FSPath& path) { } FSResult SaveDataArchive::deleteFile(const FSPath& path) { - Helpers::panic("[SaveData] Unimplemented DeleteFile"); + if (path.type == PathType::UTF16) { + if (!isPathSafe(path)) + Helpers::panic("Unsafe path in SaveData::DeleteFile"); + + fs::path p = IOFile::getAppData() / "SaveData"; + p += fs::path(path.utf16_string).make_preferred(); + + if (fs::is_directory(p)) { + Helpers::panic("SaveData::DeleteFile: Tried to delete directory"); + } + + if (!fs::is_regular_file(p)) { + return FSResult::FileNotFound; + } + + std::error_code ec; + bool success = fs::remove(p, ec); + + // It might still be possible for fs::remove to fail, if there's eg an open handle to a file being deleted + // In this case, print a warning, but still return success for now + if (!success) { + Helpers::warn("SaveData::DeleteFile: fs::remove failed\n"); + } + + return FSResult::Success; + } + + Helpers::panic("SaveDataArchive::DeleteFile: Unknown path type"); return FSResult::Success; } diff --git a/src/core/kernel/file_operations.cpp b/src/core/kernel/file_operations.cpp index 84b77837..331a3bd5 100644 --- a/src/core/kernel/file_operations.cpp +++ b/src/core/kernel/file_operations.cpp @@ -41,7 +41,12 @@ void Kernel::closeFile(u32 messagePointer, Handle fileHandle) { Helpers::panic("Called CloseFile on non-existent file"); } - p->getData()->isOpen = false; + FileSession* session = p->getData(); + session->isOpen = false; + if (session->fd != nullptr) { + fclose(session->fd); + } + mem.write32(messagePointer + 4, Result::Success); } From 4767a2053b90fae952a2b3bbbfef8090b104f4e6 Mon Sep 17 00:00:00 2001 From: wheremyfoodat Date: Sat, 20 May 2023 15:39:31 +0300 Subject: [PATCH 11/15] [FS] Better GetFormatInfo --- include/fs/archive_base.hpp | 5 +++-- include/fs/archive_save_data.hpp | 2 +- src/core/fs/archive_save_data.cpp | 19 +++++++++++++++++-- src/core/services/fs.cpp | 19 +++++++++++++------ 4 files changed, 34 insertions(+), 11 deletions(-) diff --git a/include/fs/archive_base.hpp b/include/fs/archive_base.hpp index 8de78ad3..5cd0b3bd 100644 --- a/include/fs/archive_base.hpp +++ b/include/fs/archive_base.hpp @@ -204,10 +204,11 @@ public: virtual u64 getFreeBytes() = 0; virtual FSResult createFile(const FSPath& path, u64 size) = 0; virtual FSResult deleteFile(const FSPath& path) = 0; - virtual FormatInfo getFormatInfo(const FSPath& path) { + + virtual Rust::Result getFormatInfo(const FSPath& path) { Helpers::panic("Unimplemented GetFormatInfo for %s archive", name().c_str()); // Return a dummy struct just to avoid the UB of not returning anything, even if we panic - return FormatInfo{ .size = 0, .numOfDirectories = 0, .numOfFiles = 0, .duplicateData = false }; + return Ok(FormatInfo{ .size = 0, .numOfDirectories = 0, .numOfFiles = 0, .duplicateData = false }); } virtual FSResult createDirectory(const FSPath& path) { diff --git a/include/fs/archive_save_data.hpp b/include/fs/archive_save_data.hpp index f7642955..4831f8df 100644 --- a/include/fs/archive_save_data.hpp +++ b/include/fs/archive_save_data.hpp @@ -18,7 +18,7 @@ public: std::optional readFile(FileSession* file, u64 offset, u32 size, u32 dataPointer) override; void format(const FSPath& path, const FormatInfo& info) override; - FormatInfo getFormatInfo(const FSPath& path) override; + Rust::Result getFormatInfo(const FSPath& path) override; std::filesystem::path getFormatInfoPath() { return IOFile::getAppData() / "FormatInfo" / "SaveData.format"; diff --git a/src/core/fs/archive_save_data.cpp b/src/core/fs/archive_save_data.cpp index 57d2f2fb..758c1312 100644 --- a/src/core/fs/archive_save_data.cpp +++ b/src/core/fs/archive_save_data.cpp @@ -120,8 +120,23 @@ Rust::Result SaveDataArchive::openDirectory(const FS return Err(FSResult::Success); } -ArchiveBase::FormatInfo SaveDataArchive::getFormatInfo(const FSPath& path) { - Helpers::panic("Unimplemented SaveData::GetFormatInfo"); +Rust::Result SaveDataArchive::getFormatInfo(const FSPath& path) { + const fs::path formatInfoPath = getFormatInfoPath(); + IOFile file(formatInfoPath, "rb"); + + // If the file failed to open somehow, we return that the archive is not formatted + if (!file.isOpen()) { + return Err(FSResult::NotFormatted); + } + + FormatInfo ret; + auto [success, bytesRead] = file.readBytes(&ret, sizeof(FormatInfo)); + if (!success || bytesRead != sizeof(FormatInfo)) { + Helpers::warn("SaveData::GetFormatInfo: Format file exists but was not properly read into the FormatInfo struct"); + return Err(FSResult::NotFormatted); + } + + return Ok(ret); } void SaveDataArchive::format(const FSPath& path, const ArchiveBase::FormatInfo& info) { diff --git a/src/core/services/fs.cpp b/src/core/services/fs.cpp index fd85b484..9aebe375 100644 --- a/src/core/services/fs.cpp +++ b/src/core/services/fs.cpp @@ -419,13 +419,20 @@ void FSService::getFormatInfo(u32 messagePointer) { Helpers::panic("OpenArchive: Tried to open unknown archive %d.", archiveID); } - ArchiveBase::FormatInfo info = archive->getFormatInfo(path); mem.write32(messagePointer, IPC::responseHeader(0x845, 5, 0)); - mem.write32(messagePointer + 4, ResultCode::Success); - mem.write32(messagePointer + 8, info.size); - mem.write32(messagePointer + 12, info.numOfDirectories); - mem.write32(messagePointer + 16, info.numOfFiles); - mem.write8(messagePointer + 20, info.duplicateData ? 1 : 0); + Rust::Result res = archive->getFormatInfo(path); + + // If the FormatInfo was returned, write them to the output buffer. Otherwise, write an error code. + if (res.isOk()) { + ArchiveBase::FormatInfo info = res.unwrap(); + mem.write32(messagePointer + 4, ResultCode::Success); + mem.write32(messagePointer + 8, info.size); + mem.write32(messagePointer + 12, info.numOfDirectories); + mem.write32(messagePointer + 16, info.numOfFiles); + mem.write8(messagePointer + 20, info.duplicateData ? 1 : 0); + } else { + mem.write32(messagePointer + 4, static_cast(res.unwrapErr())); + } } void FSService::formatSaveData(u32 messagePointer) { From 7034aa27d76e33dd1b4a655a91250122b60312ec Mon Sep 17 00:00:00 2001 From: wheremyfoodat Date: Sat, 27 May 2023 00:32:43 +0300 Subject: [PATCH 12/15] Add DLP::SRVR service --- CMakeLists.txt | 4 ++-- include/kernel/handles.hpp | 6 +++-- include/logger.hpp | 1 + include/services/dlp_srvr.hpp | 21 ++++++++++++++++++ include/services/service_manager.hpp | 2 ++ src/core/services/dlp_srvr.cpp | 32 +++++++++++++++++++++++++++ src/core/services/service_manager.cpp | 7 ++++-- 7 files changed, 67 insertions(+), 6 deletions(-) create mode 100644 include/services/dlp_srvr.hpp create mode 100644 src/core/services/dlp_srvr.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index b78871c3..6fc6fe2a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -82,7 +82,7 @@ set(SERVICE_SOURCE_FILES src/core/services/service_manager.cpp src/core/services src/core/services/ac.cpp src/core/services/am.cpp src/core/services/boss.cpp src/core/services/frd.cpp src/core/services/nim.cpp src/core/services/shared_font.cpp 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/act.cpp src/core/services/nfc.cpp src/core/services/dlp_srvr.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 @@ -112,7 +112,7 @@ set(HEADER_FILES include/emulator.hpp include/helpers.hpp include/opengl.hpp inc include/fs/archive_ext_save_data.hpp include/services/shared_font.hpp include/fs/archive_ncch.hpp include/renderer_gl/textures.hpp include/colour.hpp include/services/y2r.hpp include/services/cam.hpp include/services/ldr_ro.hpp include/ipc.hpp include/services/act.hpp include/services/nfc.hpp - include/system_models.hpp + include/system_models.hpp include/services/dlp_srvr.hpp ) set(THIRD_PARTY_SOURCE_FILES third_party/imgui/imgui.cpp diff --git a/include/kernel/handles.hpp b/include/kernel/handles.hpp index 9b13460a..589b64f5 100644 --- a/include/kernel/handles.hpp +++ b/include/kernel/handles.hpp @@ -18,11 +18,12 @@ namespace KernelHandles { CAM, // Camera service CECD, // More Streetpass stuff? CFG, // CFG service (Console & region info) + DLP_SRVR, // Download Play: Server. Used for network play. + DSP, // DSP service (Used for audio decoding and output) HID, // HID service (Handles everything input-related including gyro) FRD, // Friend service (Miiverse friend service) FS, // Filesystem service GPU, // GPU service - DSP, // DSP service (Used for audio decoding and output) LCD, // LCD service (Used for configuring the displays) LDR_RO, // Loader service. Used for loading CROs. MIC, // MIC service (Controls the microphone) @@ -64,10 +65,11 @@ namespace KernelHandles { case CAM: return "CAM"; case CECD: return "CECD"; case CFG: return "CFG"; + case DSP: return "DSP"; + case DLP_SRVR: return "DLP::SRVR"; case HID: return "HID"; case FRD: return "FRD"; case FS: return "FS"; - case DSP: return "DSP"; case GPU: return "GSP::GPU"; case LCD: return "GSP::LCD"; case LDR_RO: return "LDR:RO"; diff --git a/include/logger.hpp b/include/logger.hpp index 5ffd707b..eec3b400 100644 --- a/include/logger.hpp +++ b/include/logger.hpp @@ -38,6 +38,7 @@ namespace Log { static Logger cecdLogger; static Logger cfgLogger; static Logger dspServiceLogger; + static Logger dlpSrvrLogger; static Logger frdLogger; static Logger fsLogger; static Logger hidLogger; diff --git a/include/services/dlp_srvr.hpp b/include/services/dlp_srvr.hpp new file mode 100644 index 00000000..c4be004f --- /dev/null +++ b/include/services/dlp_srvr.hpp @@ -0,0 +1,21 @@ +#pragma once +#include "helpers.hpp" +#include "kernel_types.hpp" +#include "logger.hpp" +#include "memory.hpp" + +// Please forgive me for how everything in this file is named +// "dlp:SRVR" is not a nice name to work with +class DlpSrvrService { + Handle handle = KernelHandles::DLP_SRVR; + Memory& mem; + MAKE_LOG_FUNCTION(log, dlpSrvrLogger) + + // Service commands + void isChild(u32 messagePointer); + +public: + DlpSrvrService(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 25e4d8f0..2a50ffaf 100644 --- a/include/services/service_manager.hpp +++ b/include/services/service_manager.hpp @@ -12,6 +12,7 @@ #include "services/cam.hpp" #include "services/cecd.hpp" #include "services/cfg.hpp" +#include "services/dlp_srvr.hpp" #include "services/dsp.hpp" #include "services/hid.hpp" #include "services/frd.hpp" @@ -46,6 +47,7 @@ class ServiceManager { CAMService cam; CECDService cecd; CFGService cfg; + DlpSrvrService dlp_srvr; DSPService dsp; HIDService hid; FRDService frd; diff --git a/src/core/services/dlp_srvr.cpp b/src/core/services/dlp_srvr.cpp new file mode 100644 index 00000000..3e465e21 --- /dev/null +++ b/src/core/services/dlp_srvr.cpp @@ -0,0 +1,32 @@ +#include "services/dlp_srvr.hpp" +#include "ipc.hpp" + +namespace DlpSrvrCommands { + enum : u32 { + IsChild = 0x000E0040 + }; +} + +namespace Result { + enum : u32 { + Success = 0, + }; +} + +void DlpSrvrService::reset() {} + +void DlpSrvrService::handleSyncRequest(u32 messagePointer) { + const u32 command = mem.read32(messagePointer); + switch (command) { + case DlpSrvrCommands::IsChild: isChild(messagePointer); break; + default: Helpers::panic("DLP::SRVR service requested. Command: %08X\n", command); + } +} + +void DlpSrvrService::isChild(u32 messagePointer) { + log("DLP::SRVR: IsChild\n"); + + mem.write32(messagePointer, IPC::responseHeader(0x0E, 2, 0)); + mem.write32(messagePointer + 4, Result::Success); + mem.write32(messagePointer + 8, 0); // We are responsible adults +} \ No newline at end of file diff --git a/src/core/services/service_manager.cpp b/src/core/services/service_manager.cpp index 8b14e5e1..27b39af9 100644 --- a/src/core/services/service_manager.cpp +++ b/src/core/services/service_manager.cpp @@ -5,7 +5,7 @@ ServiceManager::ServiceManager(std::array& regs, Memory& mem, GPU& gpu, u32& currentPID, Kernel& kernel) : regs(regs), mem(mem), kernel(kernel), ac(mem), am(mem), boss(mem), act(mem), apt(mem, kernel), cam(mem), - cecd(mem, kernel), cfg(mem), dsp(mem, kernel), hid(mem, kernel), frd(mem), fs(mem, kernel), + cecd(mem, kernel), cfg(mem), dlp_srvr(mem), dsp(mem, kernel), hid(mem, kernel), frd(mem), fs(mem, kernel), gsp_gpu(mem, gpu, kernel, currentPID), gsp_lcd(mem), ldr(mem), mic(mem), nfc(mem, kernel), nim(mem), ndm(mem), ptm(mem), y2r(mem, kernel) {} @@ -21,6 +21,7 @@ void ServiceManager::reset() { cam.reset(); cecd.reset(); cfg.reset(); + dlp_srvr.reset(); dsp.reset(); hid.reset(); frd.reset(); @@ -97,6 +98,7 @@ static std::map serviceMap = { { "cam:u", KernelHandles::CAM }, { "cecd:u", KernelHandles::CECD }, { "cfg:u", KernelHandles::CFG }, + { "dlp:SRVR", KernelHandles::DLP_SRVR }, { "dsp::DSP", KernelHandles::DSP }, { "hid:USER", KernelHandles::HID }, { "frd:u", KernelHandles::FRD }, @@ -170,6 +172,7 @@ void ServiceManager::sendCommandToService(u32 messagePointer, Handle handle) { case KernelHandles::GPU: [[likely]] gsp_gpu.handleSyncRequest(messagePointer); break; case KernelHandles::FS: [[likely]] fs.handleSyncRequest(messagePointer); break; case KernelHandles::APT: [[likely]] apt.handleSyncRequest(messagePointer); break; + case KernelHandles::DSP: [[likely]] dsp.handleSyncRequest(messagePointer); break; case KernelHandles::AC: ac.handleSyncRequest(messagePointer); break; case KernelHandles::ACT: act.handleSyncRequest(messagePointer); break; @@ -178,7 +181,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: cfg.handleSyncRequest(messagePointer); break; - case KernelHandles::DSP: dsp.handleSyncRequest(messagePointer); break; + case KernelHandles::DLP_SRVR: dlp_srvr.handleSyncRequest(messagePointer); break; case KernelHandles::HID: hid.handleSyncRequest(messagePointer); break; case KernelHandles::FRD: frd.handleSyncRequest(messagePointer); break; case KernelHandles::LCD: gsp_lcd.handleSyncRequest(messagePointer); break; From 1d6235c903e7698b25dbcb4f3e252720a63b3340 Mon Sep 17 00:00:00 2001 From: wheremyfoodat Date: Sat, 3 Jun 2023 01:23:26 +0300 Subject: [PATCH 13/15] Create readme.md --- readme.md | 51 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) create mode 100644 readme.md diff --git a/readme.md b/readme.md new file mode 100644 index 00000000..34171431 --- /dev/null +++ b/readme.md @@ -0,0 +1,51 @@ +# Panda3DS + +Panda3DS is an HLE, red-panda-themed Nintendo 3DS emulator written in C++ which started out as a fun project out of curiosity, but evolved into something that can sort of play games! + +# Compatibility +Panda3DS is still in the early stages of development. Many games boot, many don't. Most games have at least some hilariously broken graphics, audio is not supported, performance leaves a bit to be desired mainly thanks to lack of shader acceleration, and most QoL features (including input, or a GUI!) are missing. + +In fact, the screenshots in the repo were created after I hooked the input state to rand() locally. + +In addition, some games don't quiiite work with the upstream code. A lot of them might need some panics in the source code to be commented out before they work, etc. However, just the fact things can work as well as they do now is promising in itself. + +# Why? +The 3DS emulation scene is already pretty mature, with offerings such as [Citra](https://github.com/citra-emu/citra) which can offer a great playing experience for most games in the library, [Corgi3DS](https://github.com/PSI-Rockin/Corgi3DS), an innovative LLE emulator, or [Mikage](https://mikage.app/). However, there's always room for more emulators! While Panda3DS was initially a mere curiosity, there's many different concepts I would like to explore with it in the future, such as: + +- Virtualization. What motivated the creation of this emulator was actually a discussion on whether it is possible to get fast 3DS emulation on low-end hardware such as the Raspberry Pi 4, using the KVM API. At the moment, Panda3DS is powered by dynarmic rather than using virtualization, but this is definitely a concept I want to explore in the future. + +- Debugging, reverse engineering and modding tools. While contributing to [PCSX-Redux](https://github.com/grumpycoders/pcsx-redux) and collaborating with the other developers, I had the chance to find out how useful tools like these can be. They can serve as indispensable tools for the homebrew devs, modders, reverse engineers, as well as emulator developers themselves. Some tools can even become fun toys the casual user can mess around with. As such, I think they can really improve the experience in a project like this. Of course, I'd like to thank @nicolasnoble and the entire Redux team for helping me learn the value of these tools, as well as making me improve as a programmer. + +- Trying out various other unique features, such as different graphics or audio enhancements, or supporting other niche things such as amiibo. + +- Fun. Writing code is fun and I strongly encourage anyone to do it. + +Keep in mind, these are all long-term plans. Until then, the main focus is just improving compatibility + +# How to build +Panda3DS compiles on Windows, Linux and MacOS (if you use a compiler other than AppleClang), without needing to download any system dependencies. + +All you need is CMake and a generator of your choice (Make, Visual Studio, Ninja, etc). Simply clone the repo recursively and build it like your average CMake project. + +```sh +git clone https://github.com/wheremyfoodat/Panda3DS --recursive + +cd Panda3DS && mkdir build && cd build +cmake . -DCMAKE_BUILD_TYPE=Release # Set up compilers etc here if you'd like + + +``` + +# How to use +Simply drag and drop a ROM to the executable if supported, or invoke the executable from the command line with the path to the ROM as the first argument. + +# Acknowledgements +- [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. + +- [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 +- [Corgi3DS](https://github.com/PSI-Rockin/Corgi3DS), an LLE 3DS emulator which both served as an inspiration, as well as a nice source of documentation for some PICA200-related things + +Nintendo 3DS is a registered trademark of Nintendo Co., Ltd. \ No newline at end of file From e9a8c2cb2e16c729d2ee775d3028d6d96145a5d1 Mon Sep 17 00:00:00 2001 From: wheremyfoodat Date: Sat, 3 Jun 2023 22:04:26 +0300 Subject: [PATCH 14/15] ExtSaveData pls --- include/services/fs.hpp | 8 +++---- src/core/fs/archive_ext_save_data.cpp | 9 ++++---- src/core/services/fs.cpp | 31 +++++++++------------------ 3 files changed, 18 insertions(+), 30 deletions(-) diff --git a/include/services/fs.hpp b/include/services/fs.hpp index b4cc1fb6..93e28d18 100644 --- a/include/services/fs.hpp +++ b/include/services/fs.hpp @@ -25,10 +25,8 @@ class FSService { SDMCArchive sdmc; NCCHArchive ncch; - ExtSaveDataArchive extSaveData_nand; - ExtSaveDataArchive extSaveData_cart; + ExtSaveDataArchive extSaveData_sdmc; ExtSaveDataArchive sharedExtSaveData_nand; - ExtSaveDataArchive sharedExtSaveData_cart; ArchiveBase* getArchiveFromID(u32 id, const FSPath& archivePath); Rust::Result openArchiveHandle(u32 archiveID, const FSPath& path); @@ -60,8 +58,8 @@ class FSService { u32 priority; public: - FSService(Memory& mem, Kernel& kernel) : mem(mem), saveData(mem), extSaveData_nand(mem, "NAND"), - sharedExtSaveData_nand(mem, "NAND", true), extSaveData_cart(mem, "CartSave"), sharedExtSaveData_cart(mem, "CartSave", true), + FSService(Memory& mem, Kernel& kernel) : mem(mem), saveData(mem), + sharedExtSaveData_nand(mem, "../SharedFiles/NAND", true), extSaveData_sdmc(mem, "SDMC"), sdmc(mem), selfNcch(mem), ncch(mem), kernel(kernel) {} diff --git a/src/core/fs/archive_ext_save_data.cpp b/src/core/fs/archive_ext_save_data.cpp index 788fd251..bb7ce1a7 100644 --- a/src/core/fs/archive_ext_save_data.cpp +++ b/src/core/fs/archive_ext_save_data.cpp @@ -100,12 +100,13 @@ Rust::Result ExtSaveDataArchive::openArchive(const FSPat Helpers::panic("ExtSaveData accessed with an invalid path in OpenArchive"); } + // TODO: Readd the format check. I didn't manage to fix it sadly // Create a format info path in the style of AppData/FormatInfo/Cartridge10390390194.format - fs::path formatInfopath = IOFile::getAppData() / "FormatInfo" / (getExtSaveDataPathFromBinary(path) + ".format"); + // fs::path formatInfopath = IOFile::getAppData() / "FormatInfo" / (getExtSaveDataPathFromBinary(path) + ".format"); // Format info not found so the archive is not formatted - if (!fs::is_regular_file(formatInfopath)) { - return isShared ? Err(FSResult::NotFormatted) : Err(FSResult::NotFoundInvalid); - } + // if (!fs::is_regular_file(formatInfopath)) { + // return isShared ? Err(FSResult::NotFormatted) : Err(FSResult::NotFoundInvalid); + //} return Ok((ArchiveBase*)this); } diff --git a/src/core/services/fs.cpp b/src/core/services/fs.cpp index 9aebe375..fe8bce8d 100644 --- a/src/core/services/fs.cpp +++ b/src/core/services/fs.cpp @@ -46,19 +46,20 @@ void FSService::reset() { // Creates directories for NAND, ExtSaveData, etc if they don't already exist. Should be executed after loading a new ROM. void FSService::initializeFilesystem() { - const auto nandPath = IOFile::getAppData() / "NAND"; // Create NAND - const auto cartPath = IOFile::getAppData() / "CartSave"; // Create cartridge save folder for use with ExtSaveData + const auto sdmcPath = IOFile::getAppData() / "SDMC"; // Create SDMC directory + const auto nandSharedpath = IOFile::getAppData() / ".." / "SharedFiles" / "NAND"; + const auto savePath = IOFile::getAppData() / "SaveData"; // Create SaveData const auto formatPath = IOFile::getAppData() / "FormatInfo"; // Create folder for storing archive formatting info namespace fs = std::filesystem; - // TODO: SDMC, etc - if (!fs::is_directory(nandPath)) { - fs::create_directories(nandPath); + + if (!fs::is_directory(nandSharedpath)) { + fs::create_directories(nandSharedpath); } - if (!fs::is_directory(cartPath)) { - fs::create_directories(cartPath); + if (!fs::is_directory(sdmcPath)) { + fs::create_directories(sdmcPath); } if (!fs::is_directory(savePath)) { @@ -75,22 +76,10 @@ ArchiveBase* FSService::getArchiveFromID(u32 id, const FSPath& archivePath) { case ArchiveID::SelfNCCH: return &selfNcch; case ArchiveID::SaveData: return &saveData; case ArchiveID::ExtSaveData: - if (archivePath.type == PathType::Binary) { - switch (archivePath.binary[0]) { - case 0: return &extSaveData_nand; - case 2: return &extSaveData_cart; - } - } - return nullptr; + return &extSaveData_sdmc; case ArchiveID::SharedExtSaveData: - if (archivePath.type == PathType::Binary) { - switch (archivePath.binary[0]) { - case 0: return &sharedExtSaveData_nand; - case 2: return &sharedExtSaveData_cart; - } - } - return nullptr; + return &sharedExtSaveData_nand; case ArchiveID::SDMC: return &sdmc; case ArchiveID::SavedataAndNcch: return &ncch; // This can only access NCCH outside of FSPXI From 7cc7dc09f496bc990f7b81e73d9f03c7ac6978e8 Mon Sep 17 00:00:00 2001 From: wheremyfoodat Date: Sat, 3 Jun 2023 22:25:30 +0300 Subject: [PATCH 15/15] [FS] Implement File::Flush and fix file function headers --- include/kernel/kernel.hpp | 1 + src/core/kernel/file_operations.cpp | 27 +++++++++++++++++++++++++++ 2 files changed, 28 insertions(+) diff --git a/include/kernel/kernel.hpp b/include/kernel/kernel.hpp index d43b88c0..9d46d1b9 100644 --- a/include/kernel/kernel.hpp +++ b/include/kernel/kernel.hpp @@ -146,6 +146,7 @@ private: // File operations void handleFileOperation(u32 messagePointer, Handle file); void closeFile(u32 messagePointer, Handle file); + void flushFile(u32 messagePointer, Handle file); void readFile(u32 messagePointer, Handle file); void writeFile(u32 messagePointer, Handle file); void getFileSize(u32 messagePointer, Handle file); diff --git a/src/core/kernel/file_operations.cpp b/src/core/kernel/file_operations.cpp index 331a3bd5..c6307e06 100644 --- a/src/core/kernel/file_operations.cpp +++ b/src/core/kernel/file_operations.cpp @@ -1,3 +1,4 @@ +#include "ipc.hpp" #include "kernel.hpp" namespace FileOps { @@ -7,6 +8,7 @@ namespace FileOps { GetSize = 0x08040000, SetSize = 0x08050080, Close = 0x08080000, + Flush = 0x08090000, SetPriority = 0x080A0040, OpenLinkFile = 0x080C0000 }; @@ -47,6 +49,24 @@ void Kernel::closeFile(u32 messagePointer, Handle fileHandle) { fclose(session->fd); } + mem.write32(messagePointer, IPC::responseHeader(0x0808, 1, 0)); + mem.write32(messagePointer + 4, Result::Success); +} + +void Kernel::flushFile(u32 messagePointer, Handle fileHandle) { + logFileIO("Flushed file %X\n", fileHandle); + + const auto p = getObject(fileHandle, KernelObjectType::File); + if (p == nullptr) [[unlikely]] { + Helpers::panic("Called FlushFile on non-existent file"); + } + + FileSession* session = p->getData(); + if (session->fd != nullptr) { + fflush(session->fd); + } + + mem.write32(messagePointer, IPC::responseHeader(0x0809, 1, 0)); mem.write32(messagePointer + 4, Result::Success); } @@ -63,6 +83,8 @@ void Kernel::readFile(u32 messagePointer, Handle fileHandle) { Helpers::panic("Called ReadFile on non-existent file"); } + mem.write32(messagePointer, IPC::responseHeader(0x0802, 2, 2)); + FileSession* file = p->getData(); if (!file->isOpen) { Helpers::panic("Tried to read closed file"); @@ -131,6 +153,7 @@ void Kernel::writeFile(u32 messagePointer, Handle fileHandle) { IOFile f(file->fd); auto [success, bytesWritten] = f.writeBytes(data.get(), size); + mem.write32(messagePointer, IPC::responseHeader(0x0803, 2, 2)); if (!success) { Helpers::panic("Kernel::WriteFile failed"); } else { @@ -151,6 +174,7 @@ void Kernel::setFileSize(u32 messagePointer, Handle fileHandle) { if (!file->isOpen) { Helpers::panic("Tried to get size of closed file"); } + mem.write32(messagePointer, IPC::responseHeader(0x0805, 1, 0)); if (file->fd) { const u64 newSize = mem.read64(messagePointer + 4); @@ -179,6 +203,7 @@ void Kernel::getFileSize(u32 messagePointer, Handle fileHandle) { if (!file->isOpen) { Helpers::panic("Tried to get size of closed file"); } + mem.write32(messagePointer, IPC::responseHeader(0x0804, 3, 0)); if (file->fd) { IOFile f(file->fd); @@ -217,6 +242,7 @@ void Kernel::openLinkFile(u32 messagePointer, Handle fileHandle) { // However we do seek properly on every file access so this shouldn't matter cloneFile.data = new FileSession(*file); + mem.write32(messagePointer, IPC::responseHeader(0x080C, 1, 2)); mem.write32(messagePointer + 4, Result::Success); mem.write32(messagePointer + 12, handle); } @@ -236,5 +262,6 @@ void Kernel::setFilePriority(u32 messagePointer, Handle fileHandle) { } file->priority = priority; + mem.write32(messagePointer, IPC::responseHeader(0x080A, 1, 0)); mem.write32(messagePointer + 4, Result::Success); } \ No newline at end of file