From d4594446bd606efad7fc25a053086bfcc239557b Mon Sep 17 00:00:00 2001 From: Ada Date: Fri, 8 Mar 2024 13:09:11 +0000 Subject: [PATCH] SaveIcon and ReadExtSaveDataIcon --- include/fs/archive_base.hpp | 8 +++ include/fs/archive_ext_save_data.hpp | 3 ++ include/services/fs.hpp | 2 + src/core/fs/archive_ext_save_data.cpp | 29 ++++++++++ src/core/fs/archive_ncch.cpp | 8 --- src/core/services/fs.cpp | 78 +++++++++++++++++++++++---- 6 files changed, 110 insertions(+), 18 deletions(-) diff --git a/include/fs/archive_base.hpp b/include/fs/archive_base.hpp index d95d8afe..72255605 100644 --- a/include/fs/archive_base.hpp +++ b/include/fs/archive_base.hpp @@ -57,6 +57,14 @@ namespace ArchiveID { } } +namespace MediaType { + enum : u8 { + NAND = 0, + SD = 1, + Gamecard = 2 + }; +}; + struct FSPath { u32 type = PathType::Invalid; diff --git a/include/fs/archive_ext_save_data.hpp b/include/fs/archive_ext_save_data.hpp index e9142012..98c668e3 100644 --- a/include/fs/archive_ext_save_data.hpp +++ b/include/fs/archive_ext_save_data.hpp @@ -22,6 +22,9 @@ public: std::optional readFile(FileSession* file, u64 offset, u32 size, u32 dataPointer) override; void format(const FSPath& path, const FormatInfo& info) override; Rust::Result getFormatInfo(const FSPath& path) override; + void clear(const FSPath& path) const; + void saveIcon(const std::vector& data) const; + Rust::Result, HorizonResult> loadIcon() const; std::filesystem::path getFormatInfoPath(const FSPath& path) const; std::filesystem::path getUserDataPath() const; diff --git a/include/services/fs.hpp b/include/services/fs.hpp index d75db962..1e29d875 100644 --- a/include/services/fs.hpp +++ b/include/services/fs.hpp @@ -42,6 +42,7 @@ class FSService { 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); + void writePointer(const u8* data, u32 pointer, u32 size); const EmulatorConfig& config; @@ -78,6 +79,7 @@ class FSService { void setArchivePriority(u32 messagePointer); void setPriority(u32 messagePointer); void setThisSaveDataSecureValue(u32 messagePointer); + void readExtSaveDataIcon(u32 messagePointer); // Used for set/get priority: Not sure what sort of priority this is referring to u32 priority; diff --git a/src/core/fs/archive_ext_save_data.cpp b/src/core/fs/archive_ext_save_data.cpp index 1f23f15d..b037daaf 100644 --- a/src/core/fs/archive_ext_save_data.cpp +++ b/src/core/fs/archive_ext_save_data.cpp @@ -221,6 +221,35 @@ void ExtSaveDataArchive::format(const FSPath& path, const FormatInfo& info) { file.close(); } +void ExtSaveDataArchive::clear(const FSPath& path) const { + const fs::path saveDataPath = IOFile::getAppData() / backingFolder; + const fs::path formatInfoPath = getFormatInfoPath(path); + + fs::remove_all(saveDataPath); + fs::remove(formatInfoPath); +} + +void ExtSaveDataArchive::saveIcon(const std::vector& data) const { + const fs::path iconPath = IOFile::getAppData() / backingFolder / "icon"; + IOFile file(iconPath, "wb"); + file.setSize(data.size()); + file.writeBytes(data.data(), data.size()); + file.flush(); + file.close(); +} + +Rust::Result, HorizonResult> ExtSaveDataArchive::loadIcon() const { + const fs::path iconPath = IOFile::getAppData() / backingFolder / "icon"; + IOFile file(iconPath, "rb"); + const s32 size = static_cast(file.size().value_or(-1)); + if(size < 0) { + return Err(Result::FS::NotFoundInvalid); + } + std::unique_ptr data(new u8[size]); + file.readBytes(data.get(), size); + return Ok(std::vector(data.get(), data.get() + size)); +} + std::filesystem::path ExtSaveDataArchive::getFormatInfoPath(const FSPath& path) const { return IOFile::getAppData() / "FormatInfo" / (getExtSaveDataPathFromBinary(path) + ".format"); } diff --git a/src/core/fs/archive_ncch.cpp b/src/core/fs/archive_ncch.cpp index d5a4bab5..1ca33770 100644 --- a/src/core/fs/archive_ncch.cpp +++ b/src/core/fs/archive_ncch.cpp @@ -13,14 +13,6 @@ namespace PathType { }; }; -namespace MediaType { - enum : u8 { - NAND = 0, - SD = 1, - Gamecard = 2 - }; -}; - HorizonResult NCCHArchive::createFile(const FSPath& path, u64 size) { Helpers::panic("[NCCH] CreateFile not yet supported"); return Result::Success; diff --git a/src/core/services/fs.cpp b/src/core/services/fs.cpp index 9cde56a3..0c1f4539 100644 --- a/src/core/services/fs.cpp +++ b/src/core/services/fs.cpp @@ -37,6 +37,7 @@ namespace FSCommands { FormatSaveData = 0x084C0242, CreateExtSaveData = 0x08510242, DeleteExtSaveData = 0x08520100, + ReadExtSaveDataIcon = 0x08530142, SetArchivePriority = 0x085A00C0, InitializeWithSdkVersion = 0x08610042, SetPriority = 0x08620040, @@ -163,6 +164,11 @@ FSPath FSService::readPath(u32 type, u32 pointer, u32 size) { return FSPath(type, data); } +void FSService::writePointer(const u8* data, u32 pointer, u32 size) { + for (u32 i = 0; i < size; i++) + mem.write8(pointer + i, data[i]); +} + void FSService::handleSyncRequest(u32 messagePointer) { const u32 command = mem.read32(messagePointer); switch (command) { @@ -197,6 +203,7 @@ void FSService::handleSyncRequest(u32 messagePointer) { case FSCommands::SetPriority: setPriority(messagePointer); break; case FSCommands::SetThisSaveDataSecureValue: setThisSaveDataSecureValue(messagePointer); break; case FSCommands::AbnegateAccessRight: abnegateAccessRight(messagePointer); break; + case FSCommands::ReadExtSaveDataIcon: readExtSaveDataIcon(messagePointer); break; case FSCommands::TheGameboyVCFunction: theGameboyVCFunction(messagePointer); break; default: Helpers::panic("FS service requested. Command: %08X\n", command); } @@ -432,7 +439,7 @@ void FSService::deleteDirectory(u32 messagePointer) { const u32 filePathPointer = mem.read32(messagePointer + 28); log("FS::DeleteDirectory\n"); - Helpers::warn("Stubbed FS::DeleteDirectory call!"); // note: should we ensure the system isn't about to delete things outside of the VFS? + Helpers::warn("Stubbed FS::DeleteDirectory call!"); mem.write32(messagePointer, IPC::responseHeader(0x806, 1, 0)); mem.write32(messagePointer + 4, Result::Success); } @@ -444,7 +451,7 @@ void FSService::deleteDirectoryRecursively(u32 messagePointer) { const u32 filePathPointer = mem.read32(messagePointer + 28); log("FS::DeleteDirectoryRecursively\n"); - Helpers::warn("Stubbed FS::DeleteDirectoryRecursively call!"); // note: should we ensure the system isn't about to delete things outside of the VFS? + Helpers::warn("Stubbed FS::DeleteDirectoryRecursively call!"); mem.write32(messagePointer, IPC::responseHeader(0x807, 1, 0)); mem.write32(messagePointer + 4, Result::Success); } @@ -524,26 +531,40 @@ void FSService::deleteExtSaveData(u32 messagePointer) { const u64 saveID = mem.read64(messagePointer + 8); log("FS::DeleteExtSaveData (media type = %d, saveID = %llx) (stubbed)\n", mediaType, saveID); - mem.write32(messagePointer, IPC::responseHeader(0x0852, 1, 0)); // TODO: We can't properly implement this yet until we properly support title/save IDs. We will stub this and insert a warning for now. Required for Planet Robobot - // When we properly implement it, it will just be a recursive directory deletion + /* + FSPath path = readPath(PathType::Binary, messagePointer + 4, 8); + + switch(mediaType) { + case MediaType::NAND: + sharedExtSaveData_nand.clear(path); + break; + case MediaType::SD: + extSaveData_sdmc.clear(path); + break; + default: + Helpers::warn("FS::DeleteExtSaveData - Unhandled ExtSaveData MediaType %d", static_cast(mediaType)); + break; + } + */ + + mem.write32(messagePointer, IPC::responseHeader(0x0852, 1, 0)); mem.write32(messagePointer + 4, Result::Success); } void FSService::createExtSaveData(u32 messagePointer) { - Helpers::warn("Stubbed call to FS::CreateExtSaveData!"); + log("FS::CreateExtSaveData\n"); // First 4 words of parameters are the ExtSaveData info // https://www.3dbrew.org/wiki/Filesystem_services#ExtSaveDataInfo // This creates the ExtSaveData with the specified saveid in the specified media type. It stores the SMDH as "icon" in the root of the created directory. const u8 mediaType = mem.read8(messagePointer + 4); - const u64 saveID = mem.read64(messagePointer + 8); + const u64 saveID = mem.read64(messagePointer + 8); // todo: <-- how should this be used? ATM everything is in the same space. const u32 numOfDirectories = mem.read32(messagePointer + 20); const u32 numOfFiles = mem.read32(messagePointer + 24); const u64 sizeLimit = mem.read64(messagePointer + 28); const u32 smdhSize = mem.read32(messagePointer + 36); const u32 smdhPointer = mem.read32(messagePointer + 44); - log("FS::CreateExtSaveData\n"); ArchiveBase::FormatInfo info { .size = (u32) (sizeLimit * 0x200), .numOfDirectories = numOfDirectories, @@ -554,14 +575,16 @@ void FSService::createExtSaveData(u32 messagePointer) { FSPath smdh = readPath(PathType::Binary, smdhPointer, smdhSize); switch(mediaType) { - case 0: + case MediaType::NAND: sharedExtSaveData_nand.format(path, info); + sharedExtSaveData_nand.saveIcon(smdh.binary); break; - case 1: + case MediaType::SD: extSaveData_sdmc.format(path, info); + extSaveData_sdmc.saveIcon(smdh.binary); break; default: - Helpers::warn("Unhandled ExtSaveData MediaType %d", static_cast(mediaType)); + Helpers::warn("FS::CreateExtSaveData - Unhandled ExtSaveData MediaType %d", static_cast(mediaType)); break; } @@ -569,6 +592,41 @@ void FSService::createExtSaveData(u32 messagePointer) { mem.write32(messagePointer + 4, Result::Success); } +void FSService::readExtSaveDataIcon(u32 messagePointer) { + log("FS::ReadExtSaveDataIcon\n"); + const u8 mediaType = mem.read8(messagePointer + 4); + const u64 saveID = mem.read64(messagePointer + 8); // todo: <-- is this used? + const u32 smdhSize = mem.read32(messagePointer + 20); + const u32 smdhPointer = mem.read32(messagePointer + 28); + + ExtSaveDataArchive* selected = nullptr; + switch(mediaType) { + case MediaType::NAND: + selected = &sharedExtSaveData_nand; + break; + case MediaType::SD: + selected = &extSaveData_sdmc; + break; + default: + Helpers::warn("FS::ReadExtSaveDataIcon - Unhandled ExtSaveData MediaType %d", static_cast(mediaType)); + break; + } + + mem.write32(messagePointer, IPC::responseHeader(0x0851, 1, 0)); + + Rust::Result, HorizonResult> data = selected == nullptr ? Err(Result::FS::NotFoundInvalid) : selected->loadIcon(); + if(data.isErr()) { + mem.write32(messagePointer + 4, data.unwrapErr());; + mem.write32(messagePointer + 8, 0); + } else { + const std::vector buffer = data.unwrap>(); + const u32 copySize = std::min(smdhSize, static_cast(buffer.size())); + writePointer(buffer.data(), smdhPointer, copySize); + mem.write32(messagePointer + 4, Result::Success); + mem.write32(messagePointer + 8, copySize); + } +} + void FSService::formatThisUserSaveData(u32 messagePointer) { log("FS::FormatThisUserSaveData\n");