From a4e44cf06093f07bf4787d21f3a64952331de659 Mon Sep 17 00:00:00 2001 From: Ada Date: Thu, 7 Mar 2024 15:37:43 +0000 Subject: [PATCH 01/18] Partially implement ExtSaveData --- include/fs/archive_base.hpp | 18 +++- include/fs/archive_ext_save_data.hpp | 14 +-- include/services/fs.hpp | 3 +- src/core/fs/archive_ext_save_data.cpp | 143 +++++++++++++++++++++++--- src/core/services/fs.cpp | 40 ++++++- 5 files changed, 188 insertions(+), 30 deletions(-) diff --git a/include/fs/archive_base.hpp b/include/fs/archive_base.hpp index 2843be68..d95d8afe 100644 --- a/include/fs/archive_base.hpp +++ b/include/fs/archive_base.hpp @@ -228,9 +228,19 @@ public: return Ok(FormatInfo{ .size = 0, .numOfDirectories = 0, .numOfFiles = 0, .duplicateData = false }); } - virtual HorizonResult createDirectory(const FSPath& path) { - Helpers::panic("Unimplemented CreateDirectory for %s archive", name().c_str()); - return Result::FS::AlreadyExists; + virtual HorizonResult createDirectory(const FSPath& path) { + Helpers::panic("Unimplemented CreateDirectory for %s archive", name().c_str()); + return Result::FS::AlreadyExists; + } + + virtual HorizonResult deleteDirectory(const FSPath& path) { + Helpers::warn("Stubbed DeleteDirectory for %s archive", name().c_str()); + return Result::Success; + } + + virtual HorizonResult deleteDirectoryRecursively(const FSPath& path) { + Helpers::warn("Stubbed DeleteDirectoryRecursively for %s archive", name().c_str()); + return Result::Success; } // Returns nullopt if opening the file failed, otherwise returns a file descriptor to it (nullptr if none is needed) @@ -263,4 +273,4 @@ struct ArchiveResource { u32 clusterSize; // Size of a cluster in bytes u32 partitionCapacityInClusters; u32 freeSpaceInClusters; -}; \ No newline at end of file +}; diff --git a/include/fs/archive_ext_save_data.hpp b/include/fs/archive_ext_save_data.hpp index 7c8c7503..e9142012 100644 --- a/include/fs/archive_ext_save_data.hpp +++ b/include/fs/archive_ext_save_data.hpp @@ -10,6 +10,8 @@ public: std::string name() override { return "ExtSaveData::" + backingFolder; } HorizonResult createDirectory(const FSPath& path) override; + HorizonResult deleteDirectory(const FSPath& path) override; + HorizonResult deleteDirectoryRecursively(const FSPath& path) override; HorizonResult createFile(const FSPath& path, u64 size) override; HorizonResult deleteFile(const FSPath& path) override; HorizonResult renameFile(const FSPath& oldPath, const FSPath& newPath) override; @@ -18,16 +20,16 @@ public: 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; + Rust::Result getFormatInfo(const FSPath& path) 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}); - } + std::filesystem::path getFormatInfoPath(const FSPath& path) const; + std::filesystem::path getUserDataPath() const; // 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); + std::string getExtSaveDataPathFromBinary(const FSPath& path) const; 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/services/fs.hpp b/include/services/fs.hpp index 4a613121..d75db962 100644 --- a/include/services/fs.hpp +++ b/include/services/fs.hpp @@ -54,6 +54,7 @@ class FSService { void closeArchive(u32 messagePointer); void controlArchive(u32 messagePointer); void deleteDirectory(u32 messagePointer); + void deleteDirectoryRecursively(u32 messagePointer); void deleteExtSaveData(u32 messagePointer); void deleteFile(u32 messagePointer); void formatSaveData(u32 messagePointer); @@ -91,4 +92,4 @@ public: void handleSyncRequest(u32 messagePointer); // Creates directories for NAND, ExtSaveData, etc if they don't already exist. Should be executed after loading a new ROM. void initializeFilesystem(); -}; \ No newline at end of file +}; diff --git a/src/core/fs/archive_ext_save_data.cpp b/src/core/fs/archive_ext_save_data.cpp index 4b57f245..c872351d 100644 --- a/src/core/fs/archive_ext_save_data.cpp +++ b/src/core/fs/archive_ext_save_data.cpp @@ -11,7 +11,8 @@ HorizonResult ExtSaveDataArchive::createFile(const FSPath& path, u64 size) { if (!isPathSafe(path)) Helpers::panic("Unsafe path in ExtSaveData::CreateFile"); - fs::path p = IOFile::getAppData() / backingFolder; + fs::path p = getUserDataPath(); + p += fs::path(path.utf16_string).make_preferred(); if (fs::exists(p)) @@ -37,7 +38,8 @@ HorizonResult ExtSaveDataArchive::deleteFile(const FSPath& path) { if (!isPathSafe(path)) Helpers::panic("Unsafe path in ExtSaveData::DeleteFile"); - fs::path p = IOFile::getAppData() / backingFolder; + fs::path p = getUserDataPath(); + p += fs::path(path.utf16_string).make_preferred(); if (fs::is_directory(p)) { @@ -72,7 +74,8 @@ FileDescriptor ExtSaveDataArchive::openFile(const FSPath& path, const FilePerms& if (perms.create()) Helpers::panic("[ExtSaveData] Can't open file with create flag"); - fs::path p = IOFile::getAppData() / backingFolder; + fs::path p = getUserDataPath(); + p += fs::path(path.utf16_string).make_preferred(); if (fs::exists(p)) { // Return file descriptor if the file exists @@ -97,7 +100,8 @@ HorizonResult ExtSaveDataArchive::renameFile(const FSPath& oldPath, const FSPath } // Construct host filesystem paths - fs::path sourcePath = IOFile::getAppData() / backingFolder; + fs::path sourcePath = getUserDataPath(); + fs::path destPath = sourcePath; sourcePath += fs::path(oldPath.utf16_string).make_preferred(); @@ -130,7 +134,7 @@ HorizonResult ExtSaveDataArchive::createDirectory(const FSPath& path) { Helpers::panic("Unsafe path in ExtSaveData::OpenFile"); } - fs::path p = IOFile::getAppData() / backingFolder; + fs::path p = getUserDataPath(); p += fs::path(path.utf16_string).make_preferred(); if (fs::is_directory(p)) return Result::FS::AlreadyExists; @@ -145,14 +149,124 @@ HorizonResult ExtSaveDataArchive::createDirectory(const FSPath& path) { } } -std::string ExtSaveDataArchive::getExtSaveDataPathFromBinary(const FSPath& path) { +HorizonResult ExtSaveDataArchive::deleteDirectory(const FSPath& path) { + if (path.type == PathType::UTF16) { + if (!isPathSafe(path)) { + Helpers::panic("Unsafe path in ExtSaveData::DeleteDirectory"); + } + + fs::path p = getUserDataPath(); + p += fs::path(path.utf16_string).make_preferred(); + + if (!fs::is_directory(p)) { + return Result::FS::NotFoundInvalid; + } + + if (fs::is_regular_file(p)) { + Helpers::panic("File path passed to ExtSaveData::DeleteDirectory"); + } + + Helpers::warn("Stubbed DeleteDirectory for %s archive", name().c_str()); + bool success = fs::remove(p); + return success ? Result::Success : Result::FS::UnexpectedFileOrDir; + } else { + Helpers::panic("Unimplemented ExtSaveData::DeleteDirectory"); + } +} + + +HorizonResult ExtSaveDataArchive::deleteDirectoryRecursively(const FSPath& path) { + if (path.type == PathType::UTF16) { + if (!isPathSafe(path)) { + Helpers::panic("Unsafe path in ExtSaveData::DeleteDirectoryRecursively"); + } + + fs::path p = getUserDataPath(); + p += fs::path(path.utf16_string).make_preferred(); + + if (!fs::is_directory(p)) { + return Result::FS::NotFoundInvalid; + } + + if (fs::is_regular_file(p)) { + Helpers::panic("File path passed to ExtSaveData::DeleteDirectoryRecursively"); + } + + Helpers::warn("Stubbed DeleteDirectoryRecursively for %s archive", name().c_str()); + bool success = fs::remove_all(p); + return success ? Result::Success : Result::FS::UnexpectedFileOrDir; + } else { + Helpers::panic("Unimplemented ExtSaveData::DeleteDirectoryRecursively"); + } +} + +void ExtSaveDataArchive::format(const FSPath& path, const FormatInfo& info) { + const fs::path saveDataPath = IOFile::getAppData() / backingFolder; + const fs::path formatInfoPath = getFormatInfoPath(path); + + // Delete all contents by deleting the directory then recreating it + fs::remove_all(saveDataPath); + fs::create_directories(saveDataPath); + + if(!isShared) { + fs::create_directories(saveDataPath / "user"); + fs::create_directories(saveDataPath / "boss"); + // todo: save icon. + } + + // Write format info on disk + IOFile file(formatInfoPath, "wb"); + file.writeBytes(&info, sizeof(info)); + file.flush(); + file.close(); +} + +std::filesystem::path ExtSaveDataArchive::getFormatInfoPath(const FSPath& path) const { + return IOFile::getAppData() / "FormatInfo" / (getExtSaveDataPathFromBinary(path) + ".format"); +} + +std::filesystem::path ExtSaveDataArchive::getUserDataPath() const { + fs::path p = IOFile::getAppData() / backingFolder; + if(!isShared) { // todo: "boss"? + p /= "user"; + } + return p; +} + + +Rust::Result ExtSaveDataArchive::getFormatInfo(const FSPath& path) { + const fs::path formatInfoPath = getFormatInfoPath(path); + IOFile file(formatInfoPath, "rb"); + + // If the file failed to open somehow, we return that the archive is not formatted + if (!file.isOpen()) { + return Err(Result::FS::NotFormatted); + } + + FormatInfo ret; + auto [success, bytesRead] = file.readBytes(&ret, sizeof(FormatInfo)); + file.close(); + + if (!success || bytesRead != sizeof(FormatInfo)) { + Helpers::warn("ExtSaveDataArchive::GetFormatInfo: Format file exists but was not properly read into the FormatInfo struct"); + return Err(Result::FS::NotFormatted); + } + + return Ok(ret); +} + +std::string ExtSaveDataArchive::getExtSaveDataPathFromBinary(const FSPath& path) const { + if(path.type != PathType::Binary) { + Helpers::panic("GetExtSaveDataPathFromBinary called without a Bianry FSPath!"); + } + // 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); + // TODO: Should the media type be used here, using it just to be safe. + return backingFolder + std::to_string(mediaType) + "_" + std::to_string(saveLow) + "_" + std::to_string(saveHigh); } Rust::Result ExtSaveDataArchive::openArchive(const FSPath& path) { @@ -160,13 +274,12 @@ Rust::Result ExtSaveDataArchive::openArchive(const 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"); + const fs::path formatInfoPath = getFormatInfoPath(path); // Format info not found so the archive is not formatted - // if (!fs::is_regular_file(formatInfopath)) { - // return isShared ? Err(Result::FS::NotFormatted) : Err(Result::FS::NotFoundInvalid); - //} + if (!fs::is_regular_file(formatInfoPath)) { + return isShared ? Err(Result::FS::NotFormatted) : Err(Result::FS::NotFoundInvalid); + } return Ok((ArchiveBase*)this); } @@ -176,7 +289,7 @@ Rust::Result ExtSaveDataArchive::openDirectory( if (!isPathSafe(path)) Helpers::panic("Unsafe path in ExtSaveData::OpenDirectory"); - fs::path p = IOFile::getAppData() / backingFolder; + fs::path p = getUserDataPath(); p += fs::path(path.utf16_string).make_preferred(); if (fs::is_regular_file(p)) { @@ -198,4 +311,4 @@ Rust::Result ExtSaveDataArchive::openDirectory( std::optional ExtSaveDataArchive::readFile(FileSession* file, u64 offset, u32 size, u32 dataPointer) { Helpers::panic("ExtSaveDataArchive::ReadFile: Failed"); return std::nullopt; -} \ No newline at end of file +} diff --git a/src/core/services/fs.cpp b/src/core/services/fs.cpp index 2e102958..69155093 100644 --- a/src/core/services/fs.cpp +++ b/src/core/services/fs.cpp @@ -173,6 +173,7 @@ void FSService::handleSyncRequest(u32 messagePointer) { case FSCommands::ControlArchive: controlArchive(messagePointer); break; case FSCommands::CloseArchive: closeArchive(messagePointer); break; case FSCommands::DeleteDirectory: deleteDirectory(messagePointer); break; + case FSCommands::DeleteDirectoryRecursively: deleteDirectoryRecursively(messagePointer); break; case FSCommands::DeleteExtSaveData: deleteExtSaveData(messagePointer); break; case FSCommands::DeleteFile: deleteFile(messagePointer); break; case FSCommands::FormatSaveData: formatSaveData(messagePointer); break; @@ -431,11 +432,23 @@ void FSService::deleteDirectory(u32 messagePointer) { const u32 filePathPointer = mem.read32(messagePointer + 28); log("FS::DeleteDirectory\n"); - Helpers::warn("Stubbed FS::DeleteDirectory call!"); + Helpers::warn("Stubbed FS::DeleteDirectory call!"); // note: should we ensure the system isn't about to delete things outside of the VFS? mem.write32(messagePointer, IPC::responseHeader(0x806, 1, 0)); mem.write32(messagePointer + 4, Result::Success); } +void FSService::deleteDirectoryRecursively(u32 messagePointer) { + const Handle archiveHandle = Handle(mem.read64(messagePointer + 8)); + const u32 filePathType = mem.read32(messagePointer + 16); + const u32 filePathSize = mem.read32(messagePointer + 20); + 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? + mem.write32(messagePointer, IPC::responseHeader(0x807, 1, 0)); + mem.write32(messagePointer + 4, Result::Success); +} + void FSService::getFormatInfo(u32 messagePointer) { const u32 archiveID = mem.read32(messagePointer + 4); const u32 pathType = mem.read32(messagePointer + 8); @@ -530,10 +543,29 @@ void FSService::createExtSaveData(u32 messagePointer) { const u32 smdhSize = mem.read32(messagePointer + 36); const u32 smdhPointer = mem.read32(messagePointer + 44); - log("FS::CreateExtSaveData (stubbed)\n"); + log("FS::CreateExtSaveData\n"); + ArchiveBase::FormatInfo info { + .size = (u32) (sizeLimit * 0x200), + .numOfDirectories = numOfDirectories, + .numOfFiles = numOfFiles, + .duplicateData = false + }; + FSPath path = readPath(PathType::Binary, messagePointer + 4, 32); + FSPath smdh = readPath(PathType::Binary, smdhPointer, smdhSize); + + switch(mediaType) { + case 0: + sharedExtSaveData_nand.format(path, info); + break; + case 1: + extSaveData_sdmc.format(path, info); + break; + default: + Helpers::warn("Unhanled ExtSaveData MediaType %d", static_cast(mediaType)); + break; + } mem.write32(messagePointer, IPC::responseHeader(0x0851, 1, 0)); - // TODO: Similar to DeleteExtSaveData, we need to refactor how our ExtSaveData stuff works before properly implementing this mem.write32(messagePointer + 4, Result::Success); } @@ -785,4 +817,4 @@ void FSService::getSdmcArchiveResource(u32 messagePointer) { mem.write32(messagePointer + 12, resource.clusterSize); mem.write32(messagePointer + 16, resource.partitionCapacityInClusters); mem.write32(messagePointer + 20, resource.freeSpaceInClusters); -} \ No newline at end of file +} From 556b3b8913f2a118edbaebec97177993d9c654e3 Mon Sep 17 00:00:00 2001 From: Ada Date: Thu, 7 Mar 2024 15:54:48 +0000 Subject: [PATCH 02/18] Typos --- src/core/fs/archive_ext_save_data.cpp | 2 +- src/core/services/fs.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/core/fs/archive_ext_save_data.cpp b/src/core/fs/archive_ext_save_data.cpp index c872351d..1f23f15d 100644 --- a/src/core/fs/archive_ext_save_data.cpp +++ b/src/core/fs/archive_ext_save_data.cpp @@ -257,7 +257,7 @@ Rust::Result ExtSaveDataArchive::getForm std::string ExtSaveDataArchive::getExtSaveDataPathFromBinary(const FSPath& path) const { if(path.type != PathType::Binary) { - Helpers::panic("GetExtSaveDataPathFromBinary called without a Bianry FSPath!"); + Helpers::panic("GetExtSaveDataPathFromBinary called without a Binary FSPath!"); } // TODO: Remove punning here diff --git a/src/core/services/fs.cpp b/src/core/services/fs.cpp index 69155093..9cde56a3 100644 --- a/src/core/services/fs.cpp +++ b/src/core/services/fs.cpp @@ -561,7 +561,7 @@ void FSService::createExtSaveData(u32 messagePointer) { extSaveData_sdmc.format(path, info); break; default: - Helpers::warn("Unhanled ExtSaveData MediaType %d", static_cast(mediaType)); + Helpers::warn("Unhandled ExtSaveData MediaType %d", static_cast(mediaType)); break; } From d4594446bd606efad7fc25a053086bfcc239557b Mon Sep 17 00:00:00 2001 From: Ada Date: Fri, 8 Mar 2024 13:09:11 +0000 Subject: [PATCH 03/18] 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"); From 6e48c7fbdd65d059633b61e7acc4c51501c013c3 Mon Sep 17 00:00:00 2001 From: Ada Date: Fri, 8 Mar 2024 13:12:39 +0000 Subject: [PATCH 04/18] Deduplicate some code --- src/core/fs/archive_ext_save_data.cpp | 4 ++++ src/core/services/fs.cpp | 16 +++++++++++----- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/src/core/fs/archive_ext_save_data.cpp b/src/core/fs/archive_ext_save_data.cpp index b037daaf..6c88fc4e 100644 --- a/src/core/fs/archive_ext_save_data.cpp +++ b/src/core/fs/archive_ext_save_data.cpp @@ -230,6 +230,10 @@ void ExtSaveDataArchive::clear(const FSPath& path) const { } void ExtSaveDataArchive::saveIcon(const std::vector& data) const { + if (data.empty()) { + return; + } + const fs::path iconPath = IOFile::getAppData() / backingFolder / "icon"; IOFile file(iconPath, "wb"); file.setSize(data.size()); diff --git a/src/core/services/fs.cpp b/src/core/services/fs.cpp index 0c1f4539..657cb50d 100644 --- a/src/core/services/fs.cpp +++ b/src/core/services/fs.cpp @@ -572,22 +572,28 @@ void FSService::createExtSaveData(u32 messagePointer) { .duplicateData = false }; FSPath path = readPath(PathType::Binary, messagePointer + 4, 32); - FSPath smdh = readPath(PathType::Binary, smdhPointer, smdhSize); + ExtSaveDataArchive* selected = nullptr; switch(mediaType) { case MediaType::NAND: - sharedExtSaveData_nand.format(path, info); - sharedExtSaveData_nand.saveIcon(smdh.binary); + selected = &sharedExtSaveData_nand; break; case MediaType::SD: - extSaveData_sdmc.format(path, info); - extSaveData_sdmc.saveIcon(smdh.binary); + selected = &extSaveData_sdmc; break; default: Helpers::warn("FS::CreateExtSaveData - Unhandled ExtSaveData MediaType %d", static_cast(mediaType)); break; } + if (selected != nullptr) { + selected->format(path, info); + if(smdhSize > 0) { + const FSPath smdh = readPath(PathType::Binary, smdhPointer, smdhSize); + selected->saveIcon(smdh.binary); + } + } + mem.write32(messagePointer, IPC::responseHeader(0x0851, 1, 0)); mem.write32(messagePointer + 4, Result::Success); } From 47a01a829667c90878346db236be794ec4ad208e Mon Sep 17 00:00:00 2001 From: wheremyfoodat <44909372+wheremyfoodat@users.noreply.github.com> Date: Fri, 8 Mar 2024 17:07:40 +0200 Subject: [PATCH 05/18] format --- include/fs/archive_base.hpp | 30 +++++++++++------------ src/core/fs/archive_ext_save_data.cpp | 15 ++++++------ src/core/services/fs.cpp | 34 ++++++++++----------------- 3 files changed, 35 insertions(+), 44 deletions(-) diff --git a/include/fs/archive_base.hpp b/include/fs/archive_base.hpp index 72255605..d9c7fb0f 100644 --- a/include/fs/archive_base.hpp +++ b/include/fs/archive_base.hpp @@ -61,7 +61,7 @@ namespace MediaType { enum : u8 { NAND = 0, SD = 1, - Gamecard = 2 + Gamecard = 2, }; }; @@ -230,26 +230,26 @@ public: virtual HorizonResult createFile(const FSPath& path, u64 size) = 0; virtual HorizonResult deleteFile(const FSPath& path) = 0; - 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 Ok(FormatInfo{ .size = 0, .numOfDirectories = 0, .numOfFiles = 0, .duplicateData = false }); - } + 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 Ok(FormatInfo{.size = 0, .numOfDirectories = 0, .numOfFiles = 0, .duplicateData = false}); + } virtual HorizonResult createDirectory(const FSPath& path) { - Helpers::panic("Unimplemented CreateDirectory for %s archive", name().c_str()); - return Result::FS::AlreadyExists; - } + Helpers::panic("Unimplemented CreateDirectory for %s archive", name().c_str()); + return Result::FS::AlreadyExists; + } virtual HorizonResult deleteDirectory(const FSPath& path) { - Helpers::warn("Stubbed DeleteDirectory for %s archive", name().c_str()); - return Result::Success; - } + Helpers::warn("Stubbed DeleteDirectory for %s archive", name().c_str()); + return Result::Success; + } virtual HorizonResult deleteDirectoryRecursively(const FSPath& path) { - Helpers::warn("Stubbed DeleteDirectoryRecursively for %s archive", name().c_str()); - return Result::Success; - } + Helpers::warn("Stubbed DeleteDirectoryRecursively for %s archive", name().c_str()); + return Result::Success; + } // Returns nullopt if opening the file failed, otherwise returns a file descriptor to it (nullptr if none is needed) virtual FileDescriptor openFile(const FSPath& path, const FilePerms& perms) = 0; diff --git a/src/core/fs/archive_ext_save_data.cpp b/src/core/fs/archive_ext_save_data.cpp index 6c88fc4e..184600af 100644 --- a/src/core/fs/archive_ext_save_data.cpp +++ b/src/core/fs/archive_ext_save_data.cpp @@ -208,7 +208,7 @@ void ExtSaveDataArchive::format(const FSPath& path, const FormatInfo& info) { fs::remove_all(saveDataPath); fs::create_directories(saveDataPath); - if(!isShared) { + if (!isShared) { fs::create_directories(saveDataPath / "user"); fs::create_directories(saveDataPath / "boss"); // todo: save icon. @@ -246,12 +246,13 @@ Rust::Result, HorizonResult> ExtSaveDataArchive::loadIcon() cons 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) { + 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::vector icon(size); + file.readBytes(icon.data(), size); + return Ok(icon); } std::filesystem::path ExtSaveDataArchive::getFormatInfoPath(const FSPath& path) const { @@ -260,7 +261,7 @@ std::filesystem::path ExtSaveDataArchive::getFormatInfoPath(const FSPath& path) std::filesystem::path ExtSaveDataArchive::getUserDataPath() const { fs::path p = IOFile::getAppData() / backingFolder; - if(!isShared) { // todo: "boss"? + if (!isShared) { // todo: "boss"? p /= "user"; } return p; @@ -289,7 +290,7 @@ Rust::Result ExtSaveDataArchive::getForm } std::string ExtSaveDataArchive::getExtSaveDataPathFromBinary(const FSPath& path) const { - if(path.type != PathType::Binary) { + if (path.type != PathType::Binary) { Helpers::panic("GetExtSaveDataPathFromBinary called without a Binary FSPath!"); } diff --git a/src/core/services/fs.cpp b/src/core/services/fs.cpp index 657cb50d..da09c355 100644 --- a/src/core/services/fs.cpp +++ b/src/core/services/fs.cpp @@ -165,8 +165,9 @@ FSPath FSService::readPath(u32 type, u32 pointer, u32 size) { } void FSService::writePointer(const u8* data, u32 pointer, u32 size) { - for (u32 i = 0; i < size; i++) + for (u32 i = 0; i < size; i++) { mem.write8(pointer + i, data[i]); + } } void FSService::handleSyncRequest(u32 messagePointer) { @@ -535,16 +536,10 @@ void FSService::deleteExtSaveData(u32 messagePointer) { /* 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; + 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; } */ @@ -575,20 +570,15 @@ void FSService::createExtSaveData(u32 messagePointer) { ExtSaveDataArchive* selected = nullptr; switch(mediaType) { - case MediaType::NAND: - selected = &sharedExtSaveData_nand; - break; - case MediaType::SD: - selected = &extSaveData_sdmc; - break; - default: - Helpers::warn("FS::CreateExtSaveData - Unhandled ExtSaveData MediaType %d", static_cast(mediaType)); - break; + case MediaType::NAND: selected = &sharedExtSaveData_nand; break; + case MediaType::SD: selected = &extSaveData_sdmc; break; + default: Helpers::warn("FS::CreateExtSaveData - Unhandled ExtSaveData MediaType %d", static_cast(mediaType)); break; } if (selected != nullptr) { selected->format(path, info); - if(smdhSize > 0) { + + if (smdhSize > 0) { const FSPath smdh = readPath(PathType::Binary, smdhPointer, smdhSize); selected->saveIcon(smdh.binary); } @@ -621,7 +611,7 @@ void FSService::readExtSaveDataIcon(u32 messagePointer) { mem.write32(messagePointer, IPC::responseHeader(0x0851, 1, 0)); Rust::Result, HorizonResult> data = selected == nullptr ? Err(Result::FS::NotFoundInvalid) : selected->loadIcon(); - if(data.isErr()) { + if (data.isErr()) { mem.write32(messagePointer + 4, data.unwrapErr());; mem.write32(messagePointer + 8, 0); } else { From cd25e11f8ed05b2f16470103a76ac89cdaf4663d Mon Sep 17 00:00:00 2001 From: Ada Date: Fri, 8 Mar 2024 15:19:24 +0000 Subject: [PATCH 06/18] force size to be zero --- src/core/fs/archive_ext_save_data.cpp | 4 +++- src/core/services/fs.cpp | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/core/fs/archive_ext_save_data.cpp b/src/core/fs/archive_ext_save_data.cpp index 184600af..38de9b5c 100644 --- a/src/core/fs/archive_ext_save_data.cpp +++ b/src/core/fs/archive_ext_save_data.cpp @@ -277,7 +277,7 @@ Rust::Result ExtSaveDataArchive::getForm return Err(Result::FS::NotFormatted); } - FormatInfo ret; + FormatInfo ret = {}; auto [success, bytesRead] = file.readBytes(&ret, sizeof(FormatInfo)); file.close(); @@ -286,6 +286,8 @@ Rust::Result ExtSaveDataArchive::getForm return Err(Result::FS::NotFormatted); } + ret.size = 0; + return Ok(ret); } diff --git a/src/core/services/fs.cpp b/src/core/services/fs.cpp index da09c355..40df67c7 100644 --- a/src/core/services/fs.cpp +++ b/src/core/services/fs.cpp @@ -561,7 +561,7 @@ void FSService::createExtSaveData(u32 messagePointer) { const u32 smdhPointer = mem.read32(messagePointer + 44); ArchiveBase::FormatInfo info { - .size = (u32) (sizeLimit * 0x200), + .size = 0, .numOfDirectories = numOfDirectories, .numOfFiles = numOfFiles, .duplicateData = false From 68e35d806a26ec493789afe8e07fbea8ce5ad66e Mon Sep 17 00:00:00 2001 From: Ada Date: Fri, 8 Mar 2024 15:24:21 +0000 Subject: [PATCH 07/18] fix potential nullptrs and off by one --- src/core/fs/archive_ext_save_data.cpp | 2 +- src/core/services/fs.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/core/fs/archive_ext_save_data.cpp b/src/core/fs/archive_ext_save_data.cpp index 38de9b5c..6fe4d97c 100644 --- a/src/core/fs/archive_ext_save_data.cpp +++ b/src/core/fs/archive_ext_save_data.cpp @@ -246,7 +246,7 @@ Rust::Result, HorizonResult> ExtSaveDataArchive::loadIcon() cons 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) { + if (size <= 0) { return Err(Result::FS::NotFoundInvalid); } diff --git a/src/core/services/fs.cpp b/src/core/services/fs.cpp index 40df67c7..742f1fb1 100644 --- a/src/core/services/fs.cpp +++ b/src/core/services/fs.cpp @@ -578,7 +578,7 @@ void FSService::createExtSaveData(u32 messagePointer) { if (selected != nullptr) { selected->format(path, info); - if (smdhSize > 0) { + if (smdhSize > 0 && smdhPointer != 0) { const FSPath smdh = readPath(PathType::Binary, smdhPointer, smdhSize); selected->saveIcon(smdh.binary); } From cb98c575d57a9186726eecc3c6556dcd448deb9e Mon Sep 17 00:00:00 2001 From: Ada Date: Fri, 8 Mar 2024 15:26:20 +0000 Subject: [PATCH 08/18] also add the same checks for loadIcon --- src/core/services/fs.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/services/fs.cpp b/src/core/services/fs.cpp index 742f1fb1..6c1a33fb 100644 --- a/src/core/services/fs.cpp +++ b/src/core/services/fs.cpp @@ -611,7 +611,7 @@ void FSService::readExtSaveDataIcon(u32 messagePointer) { mem.write32(messagePointer, IPC::responseHeader(0x0851, 1, 0)); Rust::Result, HorizonResult> data = selected == nullptr ? Err(Result::FS::NotFoundInvalid) : selected->loadIcon(); - if (data.isErr()) { + if (data.isErr() || smdhSize == 0 || smdhPointer == 0) { mem.write32(messagePointer + 4, data.unwrapErr());; mem.write32(messagePointer + 8, 0); } else { From a711236307a99f81e350e008052daaee15f3c05d Mon Sep 17 00:00:00 2001 From: Ada Date: Fri, 8 Mar 2024 15:27:54 +0000 Subject: [PATCH 09/18] oops --- src/core/services/fs.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/services/fs.cpp b/src/core/services/fs.cpp index 6c1a33fb..e9db5c0a 100644 --- a/src/core/services/fs.cpp +++ b/src/core/services/fs.cpp @@ -608,7 +608,7 @@ void FSService::readExtSaveDataIcon(u32 messagePointer) { break; } - mem.write32(messagePointer, IPC::responseHeader(0x0851, 1, 0)); + mem.write32(messagePointer, IPC::responseHeader(0x0853, 1, 0)); Rust::Result, HorizonResult> data = selected == nullptr ? Err(Result::FS::NotFoundInvalid) : selected->loadIcon(); if (data.isErr() || smdhSize == 0 || smdhPointer == 0) { From ee4a85aa4a7d5d6a5ba16f0007ff0f5dcd29d722 Mon Sep 17 00:00:00 2001 From: wheremyfoodat <44909372+wheremyfoodat@users.noreply.github.com> Date: Fri, 8 Mar 2024 17:44:16 +0200 Subject: [PATCH 10/18] Remove untested icon code --- include/fs/archive_ext_save_data.hpp | 2 -- include/services/fs.hpp | 1 - src/core/fs/archive_ext_save_data.cpp | 26 -------------- src/core/services/fs.cpp | 49 ++------------------------- 4 files changed, 2 insertions(+), 76 deletions(-) diff --git a/include/fs/archive_ext_save_data.hpp b/include/fs/archive_ext_save_data.hpp index 98c668e3..183bdf1d 100644 --- a/include/fs/archive_ext_save_data.hpp +++ b/include/fs/archive_ext_save_data.hpp @@ -23,8 +23,6 @@ public: 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 1e29d875..9a15ed5e 100644 --- a/include/services/fs.hpp +++ b/include/services/fs.hpp @@ -42,7 +42,6 @@ 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; diff --git a/src/core/fs/archive_ext_save_data.cpp b/src/core/fs/archive_ext_save_data.cpp index 6fe4d97c..7293341a 100644 --- a/src/core/fs/archive_ext_save_data.cpp +++ b/src/core/fs/archive_ext_save_data.cpp @@ -229,32 +229,6 @@ void ExtSaveDataArchive::clear(const FSPath& path) const { fs::remove(formatInfoPath); } -void ExtSaveDataArchive::saveIcon(const std::vector& data) const { - if (data.empty()) { - return; - } - - 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::vector icon(size); - file.readBytes(icon.data(), size); - return Ok(icon); -} - std::filesystem::path ExtSaveDataArchive::getFormatInfoPath(const FSPath& path) const { return IOFile::getAppData() / "FormatInfo" / (getExtSaveDataPathFromBinary(path) + ".format"); } diff --git a/src/core/services/fs.cpp b/src/core/services/fs.cpp index e9db5c0a..c647c9cb 100644 --- a/src/core/services/fs.cpp +++ b/src/core/services/fs.cpp @@ -164,12 +164,6 @@ 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) { @@ -204,7 +198,6 @@ 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); } @@ -577,52 +570,14 @@ void FSService::createExtSaveData(u32 messagePointer) { if (selected != nullptr) { selected->format(path, info); - - if (smdhSize > 0 && smdhPointer != 0) { - const FSPath smdh = readPath(PathType::Binary, smdhPointer, smdhSize); - selected->saveIcon(smdh.binary); - } + const FSPath smdh = readPath(PathType::Binary, smdhPointer, smdhSize); + // selected->saveIcon(smdh.binary); } mem.write32(messagePointer, IPC::responseHeader(0x0851, 1, 0)); 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(0x0853, 1, 0)); - - Rust::Result, HorizonResult> data = selected == nullptr ? Err(Result::FS::NotFoundInvalid) : selected->loadIcon(); - if (data.isErr() || smdhSize == 0 || smdhPointer == 0) { - 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"); From 9ab49617ea01537a6b525997c94f36b8d1085db0 Mon Sep 17 00:00:00 2001 From: Ada Date: Fri, 8 Mar 2024 16:23:37 +0000 Subject: [PATCH 11/18] properly handle save ids --- include/fs/archive_ext_save_data.hpp | 18 +++++++--- include/services/fs.hpp | 8 +++-- src/core/fs/archive_ext_save_data.cpp | 17 +++++---- src/core/services/fs.cpp | 50 +++++++++++++++++++++++---- 4 files changed, 71 insertions(+), 22 deletions(-) diff --git a/include/fs/archive_ext_save_data.hpp b/include/fs/archive_ext_save_data.hpp index 183bdf1d..614aaf30 100644 --- a/include/fs/archive_ext_save_data.hpp +++ b/include/fs/archive_ext_save_data.hpp @@ -1,13 +1,21 @@ #pragma once #include "archive_base.hpp" +#pragma pack(push, 1) +struct ExtSaveDataInfo { + u8 media_type; + u8 unknown; + u16 reserved; + u64 save_id; +}; +#pragma pack(pop) + class ExtSaveDataArchive : public ArchiveBase { public: - ExtSaveDataArchive(Memory& mem, const std::string& folder, bool isShared = false) : ArchiveBase(mem), - isShared(isShared), backingFolder(folder) {} - + ExtSaveDataArchive(Memory& mem, const std::string& folder, u64 saveId, bool isShared = false) : ArchiveBase(mem), + archiveSaveId(saveId), isShared(isShared), backingFolder(folder) {} u64 getFreeBytes() override { Helpers::panic("ExtSaveData::GetFreeBytes unimplemented"); return 0; } - std::string name() override { return "ExtSaveData::" + backingFolder; } + std::string name() override { return "ExtSaveData::" + backingFolder + "::" + std::to_string(archiveSaveId); } HorizonResult createDirectory(const FSPath& path) override; HorizonResult deleteDirectory(const FSPath& path) override; @@ -30,7 +38,9 @@ public: // 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) const; + std::string getExtSaveDataPath() const; + u64 archiveSaveId = 0; bool isShared = false; std::string backingFolder; // Backing folder for the archive. Can be NAND, Gamecard or SD depending on the archive path. }; diff --git a/include/services/fs.hpp b/include/services/fs.hpp index 9a15ed5e..8a765f7d 100644 --- a/include/services/fs.hpp +++ b/include/services/fs.hpp @@ -33,11 +33,13 @@ class FSService { UserSaveDataArchive userSaveData1; UserSaveDataArchive userSaveData2; - ExtSaveDataArchive extSaveData_sdmc; - ExtSaveDataArchive sharedExtSaveData_nand; + std::unordered_map extSaveData_sdmc; + std::unordered_map sharedExtSaveData_nand; SystemSaveDataArchive systemSaveData; ArchiveBase* getArchiveFromID(u32 id, const FSPath& archivePath); + ExtSaveDataArchive* getExtArchiveFromID(u64 saveId); + ExtSaveDataArchive* getSharedExtArchiveFromID(u64 saveId); 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); @@ -85,7 +87,7 @@ class FSService { public: FSService(Memory& mem, Kernel& kernel, const EmulatorConfig& config) - : mem(mem), saveData(mem), sharedExtSaveData_nand(mem, "../SharedFiles/NAND", true), extSaveData_sdmc(mem, "SDMC"), sdmc(mem), + : mem(mem), saveData(mem), sdmc(mem), sdmcWriteOnly(mem, true), selfNcch(mem), ncch(mem), userSaveData1(mem, ArchiveID::UserSaveData1), userSaveData2(mem, ArchiveID::UserSaveData2), kernel(kernel), config(config), systemSaveData(mem) {} diff --git a/src/core/fs/archive_ext_save_data.cpp b/src/core/fs/archive_ext_save_data.cpp index 7293341a..10d4e8d6 100644 --- a/src/core/fs/archive_ext_save_data.cpp +++ b/src/core/fs/archive_ext_save_data.cpp @@ -201,7 +201,7 @@ HorizonResult ExtSaveDataArchive::deleteDirectoryRecursively(const FSPath& path) } void ExtSaveDataArchive::format(const FSPath& path, const FormatInfo& info) { - const fs::path saveDataPath = IOFile::getAppData() / backingFolder; + const fs::path saveDataPath = IOFile::getAppData() / getExtSaveDataPath(); const fs::path formatInfoPath = getFormatInfoPath(path); // Delete all contents by deleting the directory then recreating it @@ -222,7 +222,7 @@ void ExtSaveDataArchive::format(const FSPath& path, const FormatInfo& info) { } void ExtSaveDataArchive::clear(const FSPath& path) const { - const fs::path saveDataPath = IOFile::getAppData() / backingFolder; + const fs::path saveDataPath = IOFile::getAppData() / getExtSaveDataPath(); const fs::path formatInfoPath = getFormatInfoPath(path); fs::remove_all(saveDataPath); @@ -234,7 +234,7 @@ std::filesystem::path ExtSaveDataArchive::getFormatInfoPath(const FSPath& path) } std::filesystem::path ExtSaveDataArchive::getUserDataPath() const { - fs::path p = IOFile::getAppData() / backingFolder; + fs::path p = IOFile::getAppData() / getExtSaveDataPath(); if (!isShared) { // todo: "boss"? p /= "user"; } @@ -270,13 +270,12 @@ std::string ExtSaveDataArchive::getExtSaveDataPathFromBinary(const FSPath& path) Helpers::panic("GetExtSaveDataPathFromBinary called without a Binary FSPath!"); } - // 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]; + const ExtSaveDataInfo info = *reinterpret_cast(&path.binary[0]); + return backingFolder + "_" + std::to_string(info.save_id); +} - // TODO: Should the media type be used here, using it just to be safe. - return backingFolder + std::to_string(mediaType) + "_" + std::to_string(saveLow) + "_" + std::to_string(saveHigh); +std::string ExtSaveDataArchive::getExtSaveDataPath() const { + return backingFolder + "/" + std::to_string(archiveSaveId); } Rust::Result ExtSaveDataArchive::openArchive(const FSPath& path) { diff --git a/src/core/services/fs.cpp b/src/core/services/fs.cpp index c647c9cb..2593065f 100644 --- a/src/core/services/fs.cpp +++ b/src/core/services/fs.cpp @@ -84,17 +84,55 @@ void FSService::initializeFilesystem() { } } +ExtSaveDataArchive* FSService::getExtArchiveFromID(u64 saveId) { + // todo: should we have title id in here? + if (const auto entry = extSaveData_sdmc.find(saveId); entry == extSaveData_sdmc.end()) { + extSaveData_sdmc.emplace(saveId, ExtSaveDataArchive(mem, "SDMC", saveId)); + } + return &extSaveData_sdmc.at(saveId); +} + + +ExtSaveDataArchive* FSService::getSharedExtArchiveFromID(u64 saveId) { + // todo: should we have title id in here? + if (const auto entry = sharedExtSaveData_nand.find(saveId); entry == sharedExtSaveData_nand.end()) { + sharedExtSaveData_nand.emplace(saveId, ExtSaveDataArchive(mem, "../SharedFiles/NAND", saveId, true)); + } + return &sharedExtSaveData_nand.at(saveId); +} + + ArchiveBase* FSService::getArchiveFromID(u32 id, const FSPath& archivePath) { switch (id) { case ArchiveID::SelfNCCH: return &selfNcch; case ArchiveID::SaveData: return &saveData; case ArchiveID::UserSaveData2: return &userSaveData2; - case ArchiveID::ExtSaveData: - return &extSaveData_sdmc; + case ArchiveID::ExtSaveData: { + const ExtSaveDataInfo info = *reinterpret_cast(&archivePath.binary[0]); + switch(info.media_type) { + case MediaType::SD: { + return getExtArchiveFromID(info.save_id); + break; + } + default: + Helpers::panic("Unknown archive media type. ID: %d\n", info.media_type); + return nullptr; + } + } - case ArchiveID::SharedExtSaveData: - return &sharedExtSaveData_nand; + case ArchiveID::SharedExtSaveData: { + const ExtSaveDataInfo info = *reinterpret_cast(&archivePath.binary[0]); + switch(info.media_type) { + case MediaType::NAND: { + return getExtArchiveFromID(info.save_id); + break; + } + default: + Helpers::panic("Unknown archive media type. ID: %d\n", info.media_type); + return nullptr; + } + } case ArchiveID::SystemSaveData: return &systemSaveData; case ArchiveID::SDMC: return &sdmc; @@ -563,8 +601,8 @@ void FSService::createExtSaveData(u32 messagePointer) { ExtSaveDataArchive* selected = nullptr; switch(mediaType) { - case MediaType::NAND: selected = &sharedExtSaveData_nand; break; - case MediaType::SD: selected = &extSaveData_sdmc; break; + case MediaType::NAND: selected = getSharedExtArchiveFromID(saveID); break; + case MediaType::SD: selected = getExtArchiveFromID(saveID); break; default: Helpers::warn("FS::CreateExtSaveData - Unhandled ExtSaveData MediaType %d", static_cast(mediaType)); break; } From 287ee61e41d72627a4f92a107a27adcbd49f853a Mon Sep 17 00:00:00 2001 From: Ada Date: Fri, 8 Mar 2024 16:26:12 +0000 Subject: [PATCH 12/18] bwaaa i need to copy paste less --- src/core/services/fs.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/services/fs.cpp b/src/core/services/fs.cpp index 2593065f..7b46b3ce 100644 --- a/src/core/services/fs.cpp +++ b/src/core/services/fs.cpp @@ -125,7 +125,7 @@ ArchiveBase* FSService::getArchiveFromID(u32 id, const FSPath& archivePath) { const ExtSaveDataInfo info = *reinterpret_cast(&archivePath.binary[0]); switch(info.media_type) { case MediaType::NAND: { - return getExtArchiveFromID(info.save_id); + return getSharedExtArchiveFromID(info.save_id); break; } default: From 6c78a5ea9e52f30852d86abda40f6ded9d66bf16 Mon Sep 17 00:00:00 2001 From: Ada Date: Fri, 8 Mar 2024 20:53:38 +0000 Subject: [PATCH 13/18] IWYU unordered_map --- include/fs/archive_ext_save_data.hpp | 1 + 1 file changed, 1 insertion(+) diff --git a/include/fs/archive_ext_save_data.hpp b/include/fs/archive_ext_save_data.hpp index 614aaf30..05a04682 100644 --- a/include/fs/archive_ext_save_data.hpp +++ b/include/fs/archive_ext_save_data.hpp @@ -1,4 +1,5 @@ #pragma once +#include #include "archive_base.hpp" #pragma pack(push, 1) From bc0708cd103e54ac2365c55a8c40581d2e7e4949 Mon Sep 17 00:00:00 2001 From: Ada Date: Fri, 8 Mar 2024 21:33:17 +0000 Subject: [PATCH 14/18] fix shared ext data also actually use title id for this one. --- src/core/fs/archive_ext_save_data.cpp | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/src/core/fs/archive_ext_save_data.cpp b/src/core/fs/archive_ext_save_data.cpp index 10d4e8d6..efccc881 100644 --- a/src/core/fs/archive_ext_save_data.cpp +++ b/src/core/fs/archive_ext_save_data.cpp @@ -204,14 +204,12 @@ void ExtSaveDataArchive::format(const FSPath& path, const FormatInfo& info) { const fs::path saveDataPath = IOFile::getAppData() / getExtSaveDataPath(); const fs::path formatInfoPath = getFormatInfoPath(path); - // Delete all contents by deleting the directory then recreating it - fs::remove_all(saveDataPath); - fs::create_directories(saveDataPath); - if (!isShared) { + // Delete all contents by deleting the directory then recreating it + fs::remove_all(saveDataPath); + fs::create_directories(saveDataPath); fs::create_directories(saveDataPath / "user"); fs::create_directories(saveDataPath / "boss"); - // todo: save icon. } // Write format info on disk @@ -275,6 +273,10 @@ std::string ExtSaveDataArchive::getExtSaveDataPathFromBinary(const FSPath& path) } std::string ExtSaveDataArchive::getExtSaveDataPath() const { + if (isShared) { + return backingFolder + "/" + std::to_string(mem.getProgramID().value_or(0)) + "/" + std::to_string(archiveSaveId); + } + return backingFolder + "/" + std::to_string(archiveSaveId); } @@ -283,11 +285,18 @@ Rust::Result ExtSaveDataArchive::openArchive(const Helpers::panic("ExtSaveData accessed with an invalid path in OpenArchive"); } + if (isShared) { + const fs::path saveDataPath = IOFile::getAppData() / getExtSaveDataPath(); + if (!fs::exists(saveDataPath)) + fs::create_directories(saveDataPath); + return Ok((ArchiveBase*) this); + } + // Create a format info path in the style of AppData/FormatInfo/Cartridge10390390194.format const fs::path formatInfoPath = getFormatInfoPath(path); // Format info not found so the archive is not formatted if (!fs::is_regular_file(formatInfoPath)) { - return isShared ? Err(Result::FS::NotFormatted) : Err(Result::FS::NotFoundInvalid); + return Err(Result::FS::NotFoundInvalid); } return Ok((ArchiveBase*)this); From f0c1584e14f5818b3e4b1a421fe79cd690db5238 Mon Sep 17 00:00:00 2001 From: Ada Date: Fri, 8 Mar 2024 21:51:40 +0000 Subject: [PATCH 15/18] more NAND ExtSaveData fixes --- include/services/fs.hpp | 6 ++--- src/core/fs/archive_ext_save_data.cpp | 2 +- src/core/services/fs.cpp | 32 ++++++++++++++++----------- 3 files changed, 23 insertions(+), 17 deletions(-) diff --git a/include/services/fs.hpp b/include/services/fs.hpp index 8a765f7d..172007fd 100644 --- a/include/services/fs.hpp +++ b/include/services/fs.hpp @@ -34,12 +34,12 @@ class FSService { UserSaveDataArchive userSaveData2; std::unordered_map extSaveData_sdmc; - std::unordered_map sharedExtSaveData_nand; + std::unordered_map nandExtSaveData_nand; SystemSaveDataArchive systemSaveData; ArchiveBase* getArchiveFromID(u32 id, const FSPath& archivePath); - ExtSaveDataArchive* getExtArchiveFromID(u64 saveId); - ExtSaveDataArchive* getSharedExtArchiveFromID(u64 saveId); + ExtSaveDataArchive* getExtArchiveFromID(u64 saveId, bool isShared); + ExtSaveDataArchive* getNANDExtArchiveFromID(u64 saveId, bool isShared); 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); diff --git a/src/core/fs/archive_ext_save_data.cpp b/src/core/fs/archive_ext_save_data.cpp index efccc881..e5fc1719 100644 --- a/src/core/fs/archive_ext_save_data.cpp +++ b/src/core/fs/archive_ext_save_data.cpp @@ -269,7 +269,7 @@ std::string ExtSaveDataArchive::getExtSaveDataPathFromBinary(const FSPath& path) } const ExtSaveDataInfo info = *reinterpret_cast(&path.binary[0]); - return backingFolder + "_" + std::to_string(info.save_id); + return fs::path(backingFolder).filename().string() + "_" + std::to_string(info.save_id); } std::string ExtSaveDataArchive::getExtSaveDataPath() const { diff --git a/src/core/services/fs.cpp b/src/core/services/fs.cpp index 7b46b3ce..09220209 100644 --- a/src/core/services/fs.cpp +++ b/src/core/services/fs.cpp @@ -84,21 +84,18 @@ void FSService::initializeFilesystem() { } } -ExtSaveDataArchive* FSService::getExtArchiveFromID(u64 saveId) { - // todo: should we have title id in here? +ExtSaveDataArchive* FSService::getExtArchiveFromID(u64 saveId, bool isShared) { if (const auto entry = extSaveData_sdmc.find(saveId); entry == extSaveData_sdmc.end()) { - extSaveData_sdmc.emplace(saveId, ExtSaveDataArchive(mem, "SDMC", saveId)); + extSaveData_sdmc.emplace(saveId, ExtSaveDataArchive(mem, "SDMC", saveId, isShared)); } return &extSaveData_sdmc.at(saveId); } - -ExtSaveDataArchive* FSService::getSharedExtArchiveFromID(u64 saveId) { - // todo: should we have title id in here? - if (const auto entry = sharedExtSaveData_nand.find(saveId); entry == sharedExtSaveData_nand.end()) { - sharedExtSaveData_nand.emplace(saveId, ExtSaveDataArchive(mem, "../SharedFiles/NAND", saveId, true)); +ExtSaveDataArchive* FSService::getNANDExtArchiveFromID(u64 saveId, bool isShared) { + if (const auto entry = nandExtSaveData_nand.find(saveId); entry == nandExtSaveData_nand.end()) { + nandExtSaveData_nand.emplace(saveId, ExtSaveDataArchive(mem, "../SharedFiles/NAND", saveId, isShared)); } - return &sharedExtSaveData_nand.at(saveId); + return &nandExtSaveData_nand.at(saveId); } @@ -111,8 +108,12 @@ ArchiveBase* FSService::getArchiveFromID(u32 id, const FSPath& archivePath) { case ArchiveID::ExtSaveData: { const ExtSaveDataInfo info = *reinterpret_cast(&archivePath.binary[0]); switch(info.media_type) { + case MediaType::NAND: { + return getNANDExtArchiveFromID(info.save_id, false); + break; + } case MediaType::SD: { - return getExtArchiveFromID(info.save_id); + return getExtArchiveFromID(info.save_id, false); break; } default: @@ -125,7 +126,11 @@ ArchiveBase* FSService::getArchiveFromID(u32 id, const FSPath& archivePath) { const ExtSaveDataInfo info = *reinterpret_cast(&archivePath.binary[0]); switch(info.media_type) { case MediaType::NAND: { - return getSharedExtArchiveFromID(info.save_id); + return getNANDExtArchiveFromID(info.save_id, true); + break; + } + case MediaType::SD: { + return getExtArchiveFromID(info.save_id, true); break; } default: @@ -601,8 +606,9 @@ void FSService::createExtSaveData(u32 messagePointer) { ExtSaveDataArchive* selected = nullptr; switch(mediaType) { - case MediaType::NAND: selected = getSharedExtArchiveFromID(saveID); break; - case MediaType::SD: selected = getExtArchiveFromID(saveID); break; + // is there ever a situation where it formats a shared archive? + case MediaType::NAND: selected = getNANDExtArchiveFromID(saveID, false); break; + case MediaType::SD: selected = getExtArchiveFromID(saveID, false); break; default: Helpers::warn("FS::CreateExtSaveData - Unhandled ExtSaveData MediaType %d", static_cast(mediaType)); break; } From 90fbbc6e401b854134f291e0630188595ae2a013 Mon Sep 17 00:00:00 2001 From: Ada Date: Fri, 8 Mar 2024 21:56:22 +0000 Subject: [PATCH 16/18] unsure if Shared/SDMC or Local/NAND is ever used, but... I think Shared/SDMC is actually handled by SDMCArchive --- src/core/services/fs.cpp | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/core/services/fs.cpp b/src/core/services/fs.cpp index 09220209..74be1f5a 100644 --- a/src/core/services/fs.cpp +++ b/src/core/services/fs.cpp @@ -55,6 +55,8 @@ 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 sdmcPath = IOFile::getAppData() / "SDMC"; // Create SDMC directory + const auto nandPath = IOFile::getAppData() / "NAND"; + const auto smdcSharedpath = IOFile::getAppData() / ".." / "SharedFiles" / "SDMC"; const auto nandSharedpath = IOFile::getAppData() / ".." / "SharedFiles" / "NAND"; const auto savePath = IOFile::getAppData() / "SaveData"; // Create SaveData @@ -63,6 +65,10 @@ void FSService::initializeFilesystem() { namespace fs = std::filesystem; + if (!fs::is_directory(smdcSharedpath)) { + fs::create_directories(smdcSharedpath); + } + if (!fs::is_directory(nandSharedpath)) { fs::create_directories(nandSharedpath); } @@ -71,6 +77,10 @@ void FSService::initializeFilesystem() { fs::create_directories(sdmcPath); } + if (!fs::is_directory(nandPath)) { + fs::create_directories(nandPath); + } + if (!fs::is_directory(savePath)) { fs::create_directories(savePath); } @@ -86,14 +96,14 @@ void FSService::initializeFilesystem() { ExtSaveDataArchive* FSService::getExtArchiveFromID(u64 saveId, bool isShared) { if (const auto entry = extSaveData_sdmc.find(saveId); entry == extSaveData_sdmc.end()) { - extSaveData_sdmc.emplace(saveId, ExtSaveDataArchive(mem, "SDMC", saveId, isShared)); + extSaveData_sdmc.emplace(saveId, ExtSaveDataArchive(mem, isShared ? "../SharedFiles/SDMC" : "SDMC", saveId, isShared)); } return &extSaveData_sdmc.at(saveId); } ExtSaveDataArchive* FSService::getNANDExtArchiveFromID(u64 saveId, bool isShared) { if (const auto entry = nandExtSaveData_nand.find(saveId); entry == nandExtSaveData_nand.end()) { - nandExtSaveData_nand.emplace(saveId, ExtSaveDataArchive(mem, "../SharedFiles/NAND", saveId, isShared)); + nandExtSaveData_nand.emplace(saveId, ExtSaveDataArchive(mem, isShared ? "../SharedFiles/NAND" : "NAND", saveId, isShared)); } return &nandExtSaveData_nand.at(saveId); } From 0cd81c9ed61e56c6a9cef621d7fc0bb0b9c086b2 Mon Sep 17 00:00:00 2001 From: Ada Date: Fri, 8 Mar 2024 21:58:21 +0000 Subject: [PATCH 17/18] cleanup --- src/core/services/fs.cpp | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/src/core/services/fs.cpp b/src/core/services/fs.cpp index 74be1f5a..ed084ff1 100644 --- a/src/core/services/fs.cpp +++ b/src/core/services/fs.cpp @@ -118,14 +118,10 @@ ArchiveBase* FSService::getArchiveFromID(u32 id, const FSPath& archivePath) { case ArchiveID::ExtSaveData: { const ExtSaveDataInfo info = *reinterpret_cast(&archivePath.binary[0]); switch(info.media_type) { - case MediaType::NAND: { + case MediaType::NAND: return getNANDExtArchiveFromID(info.save_id, false); - break; - } - case MediaType::SD: { + case MediaType::SD: return getExtArchiveFromID(info.save_id, false); - break; - } default: Helpers::panic("Unknown archive media type. ID: %d\n", info.media_type); return nullptr; @@ -135,14 +131,10 @@ ArchiveBase* FSService::getArchiveFromID(u32 id, const FSPath& archivePath) { case ArchiveID::SharedExtSaveData: { const ExtSaveDataInfo info = *reinterpret_cast(&archivePath.binary[0]); switch(info.media_type) { - case MediaType::NAND: { + case MediaType::NAND: return getNANDExtArchiveFromID(info.save_id, true); - break; - } - case MediaType::SD: { + case MediaType::SD: return getExtArchiveFromID(info.save_id, true); - break; - } default: Helpers::panic("Unknown archive media type. ID: %d\n", info.media_type); return nullptr; From fe9167ee662675931b14efe0b8501e2ffa11f1e1 Mon Sep 17 00:00:00 2001 From: Ada Date: Fri, 8 Mar 2024 22:05:26 +0000 Subject: [PATCH 18/18] return NotFormatted on non-shared NAND archive (??) --- include/fs/archive_ext_save_data.hpp | 5 +++-- src/core/fs/archive_ext_save_data.cpp | 2 +- src/core/services/fs.cpp | 4 ++-- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/include/fs/archive_ext_save_data.hpp b/include/fs/archive_ext_save_data.hpp index 05a04682..1d1c54b4 100644 --- a/include/fs/archive_ext_save_data.hpp +++ b/include/fs/archive_ext_save_data.hpp @@ -13,8 +13,8 @@ struct ExtSaveDataInfo { class ExtSaveDataArchive : public ArchiveBase { public: - ExtSaveDataArchive(Memory& mem, const std::string& folder, u64 saveId, bool isShared = false) : ArchiveBase(mem), - archiveSaveId(saveId), isShared(isShared), backingFolder(folder) {} + ExtSaveDataArchive(Memory& mem, const std::string& folder, u64 saveId, bool isShared = false, bool isNAND = false) : ArchiveBase(mem), + archiveSaveId(saveId), isShared(isShared), isNAND(isNAND), backingFolder(folder) {} u64 getFreeBytes() override { Helpers::panic("ExtSaveData::GetFreeBytes unimplemented"); return 0; } std::string name() override { return "ExtSaveData::" + backingFolder + "::" + std::to_string(archiveSaveId); } @@ -43,5 +43,6 @@ public: u64 archiveSaveId = 0; bool isShared = false; + bool isNAND = false; std::string backingFolder; // Backing folder for the archive. Can be NAND, Gamecard or SD depending on the archive path. }; diff --git a/src/core/fs/archive_ext_save_data.cpp b/src/core/fs/archive_ext_save_data.cpp index e5fc1719..8d3c90d9 100644 --- a/src/core/fs/archive_ext_save_data.cpp +++ b/src/core/fs/archive_ext_save_data.cpp @@ -296,7 +296,7 @@ Rust::Result ExtSaveDataArchive::openArchive(const const fs::path formatInfoPath = getFormatInfoPath(path); // Format info not found so the archive is not formatted if (!fs::is_regular_file(formatInfoPath)) { - return Err(Result::FS::NotFoundInvalid); + return isNAND ? Err(Result::FS::NotFormatted) : Err(Result::FS::NotFoundInvalid); } return Ok((ArchiveBase*)this); diff --git a/src/core/services/fs.cpp b/src/core/services/fs.cpp index ed084ff1..e4cf633b 100644 --- a/src/core/services/fs.cpp +++ b/src/core/services/fs.cpp @@ -96,14 +96,14 @@ void FSService::initializeFilesystem() { ExtSaveDataArchive* FSService::getExtArchiveFromID(u64 saveId, bool isShared) { if (const auto entry = extSaveData_sdmc.find(saveId); entry == extSaveData_sdmc.end()) { - extSaveData_sdmc.emplace(saveId, ExtSaveDataArchive(mem, isShared ? "../SharedFiles/SDMC" : "SDMC", saveId, isShared)); + extSaveData_sdmc.emplace(saveId, ExtSaveDataArchive(mem, isShared ? "../SharedFiles/SDMC" : "SDMC", saveId, isShared, false)); } return &extSaveData_sdmc.at(saveId); } ExtSaveDataArchive* FSService::getNANDExtArchiveFromID(u64 saveId, bool isShared) { if (const auto entry = nandExtSaveData_nand.find(saveId); entry == nandExtSaveData_nand.end()) { - nandExtSaveData_nand.emplace(saveId, ExtSaveDataArchive(mem, isShared ? "../SharedFiles/NAND" : "NAND", saveId, isShared)); + nandExtSaveData_nand.emplace(saveId, ExtSaveDataArchive(mem, isShared ? "../SharedFiles/NAND" : "NAND", saveId, isShared, true)); } return &nandExtSaveData_nand.at(saveId); }