diff --git a/include/fs/archive_base.hpp b/include/fs/archive_base.hpp index 475ca50e..2cf8e8e8 100644 --- a/include/fs/archive_base.hpp +++ b/include/fs/archive_base.hpp @@ -64,6 +64,14 @@ namespace ArchiveID { } } // namespace ArchiveID +namespace MediaType { + enum : u8 { + NAND = 0, + SD = 1, + Gamecard = 2, + }; +}; + struct FSPath { u32 type = PathType::Invalid; @@ -246,6 +254,16 @@ class ArchiveBase { 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) virtual FileDescriptor openFile(const FSPath& path, const FilePerms& perms) = 0; virtual Rust::Result openArchive(const FSPath& path) = 0; diff --git a/include/fs/archive_ext_save_data.hpp b/include/fs/archive_ext_save_data.hpp index 7c8c7503..fd1a11fa 100644 --- a/include/fs/archive_ext_save_data.hpp +++ b/include/fs/archive_ext_save_data.hpp @@ -1,15 +1,33 @@ #pragma once +#include + #include "archive_base.hpp" -class ExtSaveDataArchive : public ArchiveBase { -public: - ExtSaveDataArchive(Memory& mem, const std::string& folder, bool isShared = false) : ArchiveBase(mem), - isShared(isShared), backingFolder(folder) {} +#pragma pack(push, 1) +struct ExtSaveDataInfo { + u8 media_type; + u8 unknown; + u16 reserved; + u64 save_id; +}; +#pragma pack(pop) - u64 getFreeBytes() override { Helpers::panic("ExtSaveData::GetFreeBytes unimplemented"); return 0; } - std::string name() override { return "ExtSaveData::" + backingFolder; } +class ExtSaveDataArchive : public ArchiveBase { + public: + 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); } 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; @@ -19,15 +37,22 @@ public: FileDescriptor openFile(const FSPath& path, const FilePerms& perms) override; std::optional readFile(FileSession* file, u64 offset, u32 size, u32 dataPointer) override; - Rust::Result getFormatInfo(const FSPath& path) override { - Helpers::warn("Stubbed ExtSaveData::GetFormatInfo"); - return Ok(FormatInfo{.size = 1_GB, .numOfDirectories = 255, .numOfFiles = 255, .duplicateData = false}); - } + void format(const FSPath& path, const FormatInfo& info) override; + void clear(const FSPath& path) const; + + Rust::Result getFormatInfo(const FSPath& path) override; + std::filesystem::path getFormatInfoPath(const FSPath& path) const; + std::filesystem::path getUserDataPath() const; + std::string getExtSaveDataPathFromBinary(const FSPath& path) const; + std::string getExtSaveDataPath() 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); + 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. + bool isNAND = 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 82f07077..0e3431f7 100644 --- a/include/services/fs.hpp +++ b/include/services/fs.hpp @@ -1,4 +1,6 @@ #pragma once +#include + #include "config.hpp" #include "fs/archive_card_spi.hpp" #include "fs/archive_ext_save_data.hpp" @@ -37,16 +39,20 @@ class FSService { // UserSaveData archives UserSaveDataArchive userSaveData1; UserSaveDataArchive userSaveData2; - - ExtSaveDataArchive extSaveData_sdmc; - ExtSaveDataArchive sharedExtSaveData_nand; SystemSaveDataArchive systemSaveData; TWLPhotoArchive twlPhoto; TWLSoundArchive twlSound; CardSPIArchive cardSpi; + std::unordered_map extSaveData_sdmc; + std::unordered_map nandExtSaveData_nand; + + ArchiveBase* getArchiveFromID(u32 id, const FSPath& archivePath); + 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); @@ -63,6 +69,8 @@ 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); @@ -86,14 +94,14 @@ 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; 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), - sdmcWriteOnly(mem, true), selfNcch(mem), ncch(mem), userSaveData1(mem, ArchiveID::UserSaveData1), + : mem(mem), saveData(mem), sdmc(mem), sdmcWriteOnly(mem, true), selfNcch(mem), ncch(mem), userSaveData1(mem, ArchiveID::UserSaveData1), userSaveData2(mem, ArchiveID::UserSaveData2), systemSaveData(mem), twlPhoto(mem), twlSound(mem), cardSpi(mem), kernel(kernel), config(config) {} diff --git a/src/core/fs/archive_ext_save_data.cpp b/src/core/fs/archive_ext_save_data.cpp index 2aaa157e..0fb62629 100644 --- a/src/core/fs/archive_ext_save_data.cpp +++ b/src/core/fs/archive_ext_save_data.cpp @@ -1,4 +1,7 @@ #include "fs/archive_ext_save_data.hpp" + +#include + #include namespace fs = std::filesystem; @@ -38,7 +41,7 @@ HorizonResult ExtSaveDataArchive::deleteFile(const FSPath& path) { if (!isSafeTextPath(path)) Helpers::panic("Unsafe path in ExtSaveData::DeleteFile"); - fs::path p = IOFile::getAppData() / backingFolder; + fs::path p = getUserDataPath(); appendPath(p, path); if (fs::is_directory(p)) { @@ -74,7 +77,7 @@ 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(); appendPath(p, path); if (fs::exists(p)) { // Return file descriptor if the file exists @@ -99,7 +102,7 @@ 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(); @@ -132,7 +135,7 @@ HorizonResult ExtSaveDataArchive::createDirectory(const FSPath& path) { Helpers::panic("Unsafe path in ExtSaveData::OpenFile"); } - fs::path p = IOFile::getAppData() / backingFolder; + fs::path p = getUserDataPath(); appendPath(p, path); if (fs::is_directory(p)) { @@ -150,14 +153,132 @@ HorizonResult ExtSaveDataArchive::createDirectory(const FSPath& path) { } } -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]; +HorizonResult ExtSaveDataArchive::deleteDirectory(const FSPath& path) { + if (path.type == PathType::UTF16) { + if (!isPathSafe(path)) { + Helpers::panic("Unsafe path in ExtSaveData::DeleteDirectory"); + } - // TODO: Should the media type be used here - return backingFolder + std::to_string(saveLow) + std::to_string(saveHigh); + 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() / getExtSaveDataPath(); + const fs::path formatInfoPath = getFormatInfoPath(path); + + 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"); + } + + // Write format info on disk + IOFile file(formatInfoPath, "wb"); + file.writeBytes(&info, sizeof(info)); + file.flush(); + file.close(); +} + +void ExtSaveDataArchive::clear(const FSPath& path) const { + const fs::path saveDataPath = IOFile::getAppData() / getExtSaveDataPath(); + const fs::path formatInfoPath = getFormatInfoPath(path); + + fs::remove_all(saveDataPath); + fs::remove(formatInfoPath); +} + +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() / getExtSaveDataPath(); + 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); + } + + ret.size = 0; + return Ok(ret); +} + +std::string ExtSaveDataArchive::getExtSaveDataPathFromBinary(const FSPath& path) const { + if (!path.isBinary()) { + Helpers::panic("GetExtSaveDataPathFromBinary called without a Binary FSPath!"); + } + + const ExtSaveDataInfo info = *reinterpret_cast(&path.binary[0]); + return fs::path(backingFolder).filename().string() + "_" + std::to_string(info.save_id); +} + +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); } Rust::Result ExtSaveDataArchive::openArchive(const FSPath& path) { @@ -165,13 +286,18 @@ 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 + 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 - // 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 isNAND ? Err(Result::FS::NotFormatted) : Err(Result::FS::NotFoundInvalid); + } return Ok((ArchiveBase*)this); } @@ -181,7 +307,7 @@ Rust::Result ExtSaveDataArchive::openDirectory( if (!isSafeTextPath(path)) Helpers::panic("Unsafe path in ExtSaveData::OpenDirectory"); - fs::path p = IOFile::getAppData() / backingFolder; + fs::path p = getUserDataPath(); appendPath(p, path); if (fs::is_regular_file(p)) { diff --git a/src/core/fs/archive_ncch.cpp b/src/core/fs/archive_ncch.cpp index 5844bbda..70365af6 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/fonts/SharedFontReplacement.bin b/src/core/services/fonts/SharedFontReplacement.bin index c544d677..64d0bd90 100644 Binary files a/src/core/services/fonts/SharedFontReplacement.bin and b/src/core/services/fonts/SharedFontReplacement.bin differ diff --git a/src/core/services/fs.cpp b/src/core/services/fs.cpp index 54a4241d..384736cc 100644 --- a/src/core/services/fs.cpp +++ b/src/core/services/fs.cpp @@ -54,6 +54,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 @@ -61,6 +63,9 @@ void FSService::initializeFilesystem() { const auto systemSaveDataPath = IOFile::getAppData() / ".." / "SharedFiles" / "SystemSaveData"; namespace fs = std::filesystem; + if (!fs::is_directory(smdcSharedpath)) { + fs::create_directories(smdcSharedpath); + } if (!fs::is_directory(nandSharedpath)) { fs::create_directories(nandSharedpath); @@ -74,6 +79,10 @@ void FSService::initializeFilesystem() { fs::create_directories(savePath); } + if (!fs::is_directory(nandPath)) { + fs::create_directories(nandPath); + } + if (!fs::is_directory(formatPath)) { fs::create_directories(formatPath); } @@ -83,17 +92,43 @@ 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, 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, true)); + } + return &nandExtSaveData_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::NAND: return getNANDExtArchiveFromID(info.save_id, false); + case MediaType::SD: return getExtArchiveFromID(info.save_id, false); + 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 getNANDExtArchiveFromID(info.save_id, true); + case MediaType::SD: return getExtArchiveFromID(info.save_id, true); + 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; @@ -178,6 +213,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; @@ -441,6 +477,18 @@ void FSService::deleteDirectory(u32 messagePointer) { 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!"); + 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); @@ -516,14 +564,21 @@ void FSService::deleteExtSaveData(u32 messagePointer) { const u64 saveID = mem.read64(messagePointer + 8); log("FS::DeleteExtSaveData (media type = %d, saveID = %llx) (stubbed)\n", mediaType, saveID); + /* + 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)); - // 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 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. @@ -535,10 +590,24 @@ void FSService::createExtSaveData(u32 messagePointer) { const u32 smdhSize = mem.read32(messagePointer + 36); const u32 smdhPointer = mem.read32(messagePointer + 44); - log("FS::CreateExtSaveData (stubbed)\n"); + ArchiveBase::FormatInfo info{.size = 0, .numOfDirectories = numOfDirectories, .numOfFiles = numOfFiles, .duplicateData = false}; + FSPath path = readPath(PathType::Binary, messagePointer + 4, 32); + + ExtSaveDataArchive* selected = nullptr; + switch (mediaType) { + // 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; + } + + if (selected != nullptr) { + selected->format(path, info); + const FSPath smdh = readPath(PathType::Binary, smdhPointer, smdhSize); + // selected->saveIcon(smdh.binary); + } 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); } @@ -552,11 +621,11 @@ void FSService::formatThisUserSaveData(u32 messagePointer) { const u32 fileBucketNum = mem.read32(messagePointer + 20); // Same here const bool duplicateData = mem.read8(messagePointer + 24) != 0; - ArchiveBase::FormatInfo info { + ArchiveBase::FormatInfo info{ .size = blockSize * 0x200, .numOfDirectories = directoryNum, .numOfFiles = fileNum, - .duplicateData = duplicateData + .duplicateData = duplicateData, }; FSPath emptyPath;