diff --git a/include/fs/archive_base.hpp b/include/fs/archive_base.hpp index 4e5f99f1..5cd0b3bd 100644 --- a/include/fs/archive_base.hpp +++ b/include/fs/archive_base.hpp @@ -204,10 +204,11 @@ public: virtual u64 getFreeBytes() = 0; virtual FSResult createFile(const FSPath& path, u64 size) = 0; virtual FSResult deleteFile(const FSPath& path) = 0; - virtual FormatInfo getFormatInfo(const FSPath& path) { + + virtual Rust::Result getFormatInfo(const FSPath& path) { Helpers::panic("Unimplemented GetFormatInfo for %s archive", name().c_str()); // Return a dummy struct just to avoid the UB of not returning anything, even if we panic - return FormatInfo{ .size = 0, .numOfDirectories = 0, .numOfFiles = 0, .duplicateData = false }; + return Ok(FormatInfo{ .size = 0, .numOfDirectories = 0, .numOfFiles = 0, .duplicateData = false }); } virtual FSResult createDirectory(const FSPath& path) { @@ -224,6 +225,10 @@ public: return Err(FSResult::FileNotFound); } + virtual void format(const FSPath& path, const FormatInfo& info) { + Helpers::panic("Unimplemented Format for %s archive", name().c_str()); + } + // Read size bytes from a file starting at offset "offset" into a certain buffer in memory // Returns the number of bytes read, or nullopt if the read failed virtual std::optional readFile(FileSession* file, u64 offset, u32 size, u32 dataPointer) = 0; diff --git a/include/fs/archive_ext_save_data.hpp b/include/fs/archive_ext_save_data.hpp index b2eab9b7..95c822e9 100644 --- a/include/fs/archive_ext_save_data.hpp +++ b/include/fs/archive_ext_save_data.hpp @@ -17,6 +17,10 @@ public: FileDescriptor openFile(const FSPath& path, const FilePerms& perms) override; std::optional readFile(FileSession* file, u64 offset, u32 size, u32 dataPointer) override; + // Takes in a binary ExtSaveData path, outputs a combination of the backing folder with the low and high save entries of the path + // Used for identifying the archive format info files + std::string getExtSaveDataPathFromBinary(const FSPath& path); + bool isShared = false; std::string backingFolder; // Backing folder for the archive. Can be NAND, Gamecard or SD depending on the archive path. }; \ No newline at end of file diff --git a/include/fs/archive_save_data.hpp b/include/fs/archive_save_data.hpp index 4bc0a15e..4831f8df 100644 --- a/include/fs/archive_save_data.hpp +++ b/include/fs/archive_save_data.hpp @@ -11,13 +11,19 @@ public: FSResult createDirectory(const FSPath& path) override; FSResult createFile(const FSPath& path, u64 size) override; FSResult deleteFile(const FSPath& path) override; - FormatInfo getFormatInfo(const FSPath& path) override; Rust::Result openArchive(const FSPath& path) override; Rust::Result openDirectory(const FSPath& path) override; FileDescriptor openFile(const FSPath& path, const FilePerms& perms) override; std::optional readFile(FileSession* file, u64 offset, u32 size, u32 dataPointer) override; + void format(const FSPath& path, const FormatInfo& info) override; + Rust::Result getFormatInfo(const FSPath& path) override; + + std::filesystem::path getFormatInfoPath() { + return IOFile::getAppData() / "FormatInfo" / "SaveData.format"; + } + // Returns whether the cart has save data or not bool cartHasSaveData() { auto cxi = mem.getCXI(); diff --git a/include/kernel/kernel.hpp b/include/kernel/kernel.hpp index d43b88c0..9d46d1b9 100644 --- a/include/kernel/kernel.hpp +++ b/include/kernel/kernel.hpp @@ -146,6 +146,7 @@ private: // File operations void handleFileOperation(u32 messagePointer, Handle file); void closeFile(u32 messagePointer, Handle file); + void flushFile(u32 messagePointer, Handle file); void readFile(u32 messagePointer, Handle file); void writeFile(u32 messagePointer, Handle file); void getFileSize(u32 messagePointer, Handle file); diff --git a/include/services/fs.hpp b/include/services/fs.hpp index 48fada6e..93e28d18 100644 --- a/include/services/fs.hpp +++ b/include/services/fs.hpp @@ -25,13 +25,11 @@ class FSService { SDMCArchive sdmc; NCCHArchive ncch; - ExtSaveDataArchive extSaveData_nand; - ExtSaveDataArchive extSaveData_cart; + ExtSaveDataArchive extSaveData_sdmc; ExtSaveDataArchive sharedExtSaveData_nand; - ExtSaveDataArchive sharedExtSaveData_cart; ArchiveBase* getArchiveFromID(u32 id, const FSPath& archivePath); - std::optional openArchiveHandle(u32 archiveID, const FSPath& path); + Rust::Result openArchiveHandle(u32 archiveID, const FSPath& path); Rust::Result openDirectoryHandle(ArchiveBase* archive, const FSPath& path); std::optional openFileHandle(ArchiveBase* archive, const FSPath& path, const FSPath& archivePath, const FilePerms& perms); FSPath readPath(u32 type, u32 pointer, u32 size); @@ -43,6 +41,7 @@ class FSService { void controlArchive(u32 messagePointer); void deleteFile(u32 messagePointer); void formatSaveData(u32 messagePointer); + void formatThisUserSaveData(u32 messagePointer); void getFreeBytes(u32 messagePointer); void getFormatInfo(u32 messagePointer); void getPriority(u32 messagePointer); @@ -59,8 +58,8 @@ class FSService { u32 priority; public: - FSService(Memory& mem, Kernel& kernel) : mem(mem), saveData(mem), extSaveData_nand(mem, "NAND"), - sharedExtSaveData_nand(mem, "NAND", true), extSaveData_cart(mem, "CartSave"), sharedExtSaveData_cart(mem, "CartSave", true), + FSService(Memory& mem, Kernel& kernel) : mem(mem), saveData(mem), + sharedExtSaveData_nand(mem, "../SharedFiles/NAND", true), extSaveData_sdmc(mem, "SDMC"), sdmc(mem), selfNcch(mem), ncch(mem), kernel(kernel) {} diff --git a/readme.md b/readme.md index a037d81b..a7e75f22 100644 --- a/readme.md +++ b/readme.md @@ -53,4 +53,4 @@ Simply drag and drop a ROM to the executable if supported, or invoke the executa Nintendo 3DS is a registered trademark of Nintendo Co., Ltd. ![pamda](docs/img/panda.jpg) -Here's a panda it go blep \ No newline at end of file +Here's a panda it go blep diff --git a/src/core/fs/archive_ext_save_data.cpp b/src/core/fs/archive_ext_save_data.cpp index 375ba8bc..bb7ce1a7 100644 --- a/src/core/fs/archive_ext_save_data.cpp +++ b/src/core/fs/archive_ext_save_data.cpp @@ -38,12 +38,27 @@ FSResult ExtSaveDataArchive::deleteFile(const FSPath& path) { fs::path p = IOFile::getAppData() / backingFolder; p += fs::path(path.utf16_string).make_preferred(); + if (fs::is_directory(p)) { + Helpers::panic("ExtSaveData::DeleteFile: Tried to delete directory"); + } + + if (!fs::is_regular_file(p)) { + return FSResult::FileNotFound; + } + std::error_code ec; bool success = fs::remove(p, ec); - return success ? FSResult::Success : FSResult::FileNotFound; + + // It might still be possible for fs::remove to fail, if there's eg an open handle to a file being deleted + // In this case, print a warning, but still return success for now + if (!success) { + Helpers::warn("ExtSaveData::DeleteFile: fs::remove failed\n"); + } + + return FSResult::Success; } - Helpers::panic("ExtSaveDataArchive::DeleteFile: Failed"); + Helpers::panic("ExtSaveDataArchive::DeleteFile: Unknown path type"); return FSResult::Success; } @@ -70,11 +85,29 @@ FileDescriptor ExtSaveDataArchive::openFile(const FSPath& path, const FilePerms& return FileError; } +std::string ExtSaveDataArchive::getExtSaveDataPathFromBinary(const FSPath& path) { + // TODO: Remove punning here + const u32 mediaType = *(u32*)&path.binary[0]; + const u32 saveLow = *(u32*)&path.binary[4]; + const u32 saveHigh = *(u32*)&path.binary[8]; + + // TODO: Should the media type be used here + return backingFolder + std::to_string(saveLow) + std::to_string(saveHigh); +} + Rust::Result ExtSaveDataArchive::openArchive(const FSPath& path) { if (path.type != PathType::Binary || path.binary.size() != 12) { Helpers::panic("ExtSaveData accessed with an invalid path in OpenArchive"); } + // 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"); + // Format info not found so the archive is not formatted + // if (!fs::is_regular_file(formatInfopath)) { + // return isShared ? Err(FSResult::NotFormatted) : Err(FSResult::NotFoundInvalid); + //} + return Ok((ArchiveBase*)this); } diff --git a/src/core/fs/archive_save_data.cpp b/src/core/fs/archive_save_data.cpp index 4e8051ac..758c1312 100644 --- a/src/core/fs/archive_save_data.cpp +++ b/src/core/fs/archive_save_data.cpp @@ -31,7 +31,34 @@ FSResult SaveDataArchive::createDirectory(const FSPath& path) { } FSResult SaveDataArchive::deleteFile(const FSPath& path) { - Helpers::panic("[SaveData] Unimplemented DeleteFile"); + if (path.type == PathType::UTF16) { + if (!isPathSafe(path)) + Helpers::panic("Unsafe path in SaveData::DeleteFile"); + + fs::path p = IOFile::getAppData() / "SaveData"; + p += fs::path(path.utf16_string).make_preferred(); + + if (fs::is_directory(p)) { + Helpers::panic("SaveData::DeleteFile: Tried to delete directory"); + } + + if (!fs::is_regular_file(p)) { + return FSResult::FileNotFound; + } + + std::error_code ec; + bool success = fs::remove(p, ec); + + // It might still be possible for fs::remove to fail, if there's eg an open handle to a file being deleted + // In this case, print a warning, but still return success for now + if (!success) { + Helpers::warn("SaveData::DeleteFile: fs::remove failed\n"); + } + + return FSResult::Success; + } + + Helpers::panic("SaveDataArchive::DeleteFile: Unknown path type"); return FSResult::Success; } @@ -93,9 +120,36 @@ Rust::Result SaveDataArchive::openDirectory(const FS return Err(FSResult::Success); } -ArchiveBase::FormatInfo SaveDataArchive::getFormatInfo(const FSPath& path) { - //Helpers::panic("Unimplemented SaveData::GetFormatInfo"); - return FormatInfo{ .size = 0, .numOfDirectories = 255, .numOfFiles = 255, .duplicateData = false }; +Rust::Result SaveDataArchive::getFormatInfo(const FSPath& path) { + const fs::path formatInfoPath = getFormatInfoPath(); + IOFile file(formatInfoPath, "rb"); + + // If the file failed to open somehow, we return that the archive is not formatted + if (!file.isOpen()) { + return Err(FSResult::NotFormatted); + } + + FormatInfo ret; + auto [success, bytesRead] = file.readBytes(&ret, sizeof(FormatInfo)); + if (!success || bytesRead != sizeof(FormatInfo)) { + Helpers::warn("SaveData::GetFormatInfo: Format file exists but was not properly read into the FormatInfo struct"); + return Err(FSResult::NotFormatted); + } + + return Ok(ret); +} + +void SaveDataArchive::format(const FSPath& path, const ArchiveBase::FormatInfo& info) { + const fs::path saveDataPath = IOFile::getAppData() / "SaveData"; + const fs::path formatInfoPath = getFormatInfoPath(); + + // Delete all contents by deleting the directory then recreating it + fs::remove_all(saveDataPath); + fs::create_directories(saveDataPath); + + // Write format info on disk + IOFile file(formatInfoPath, "wb"); + file.writeBytes(&info, sizeof(info)); } Rust::Result SaveDataArchive::openArchive(const FSPath& path) { @@ -104,6 +158,12 @@ Rust::Result SaveDataArchive::openArchive(const FSPath& return Err(FSResult::NotFoundInvalid); } + const fs::path formatInfoPath = getFormatInfoPath(); + // Format info not found so the archive is not formatted + if (!fs::is_regular_file(formatInfoPath)) { + return Err(FSResult::NotFormatted); + } + return Ok((ArchiveBase*)this); } diff --git a/src/core/fs/archive_sdmc.cpp b/src/core/fs/archive_sdmc.cpp index 23564eaa..0759fcee 100644 --- a/src/core/fs/archive_sdmc.cpp +++ b/src/core/fs/archive_sdmc.cpp @@ -18,7 +18,7 @@ FileDescriptor SDMCArchive::openFile(const FSPath& path, const FilePerms& perms) Rust::Result SDMCArchive::openArchive(const FSPath& path) { printf("SDMCArchive::OpenArchive: Failed\n"); - return Ok((ArchiveBase*)nullptr); + return Err(FSResult::NotFormatted); } std::optional SDMCArchive::readFile(FileSession* file, u64 offset, u32 size, u32 dataPointer) { diff --git a/src/core/kernel/file_operations.cpp b/src/core/kernel/file_operations.cpp index 84b77837..c6307e06 100644 --- a/src/core/kernel/file_operations.cpp +++ b/src/core/kernel/file_operations.cpp @@ -1,3 +1,4 @@ +#include "ipc.hpp" #include "kernel.hpp" namespace FileOps { @@ -7,6 +8,7 @@ namespace FileOps { GetSize = 0x08040000, SetSize = 0x08050080, Close = 0x08080000, + Flush = 0x08090000, SetPriority = 0x080A0040, OpenLinkFile = 0x080C0000 }; @@ -41,7 +43,30 @@ void Kernel::closeFile(u32 messagePointer, Handle fileHandle) { Helpers::panic("Called CloseFile on non-existent file"); } - p->getData()->isOpen = false; + FileSession* session = p->getData(); + session->isOpen = false; + if (session->fd != nullptr) { + fclose(session->fd); + } + + mem.write32(messagePointer, IPC::responseHeader(0x0808, 1, 0)); + mem.write32(messagePointer + 4, Result::Success); +} + +void Kernel::flushFile(u32 messagePointer, Handle fileHandle) { + logFileIO("Flushed file %X\n", fileHandle); + + const auto p = getObject(fileHandle, KernelObjectType::File); + if (p == nullptr) [[unlikely]] { + Helpers::panic("Called FlushFile on non-existent file"); + } + + FileSession* session = p->getData(); + if (session->fd != nullptr) { + fflush(session->fd); + } + + mem.write32(messagePointer, IPC::responseHeader(0x0809, 1, 0)); mem.write32(messagePointer + 4, Result::Success); } @@ -58,6 +83,8 @@ void Kernel::readFile(u32 messagePointer, Handle fileHandle) { Helpers::panic("Called ReadFile on non-existent file"); } + mem.write32(messagePointer, IPC::responseHeader(0x0802, 2, 2)); + FileSession* file = p->getData(); if (!file->isOpen) { Helpers::panic("Tried to read closed file"); @@ -126,6 +153,7 @@ void Kernel::writeFile(u32 messagePointer, Handle fileHandle) { IOFile f(file->fd); auto [success, bytesWritten] = f.writeBytes(data.get(), size); + mem.write32(messagePointer, IPC::responseHeader(0x0803, 2, 2)); if (!success) { Helpers::panic("Kernel::WriteFile failed"); } else { @@ -146,6 +174,7 @@ void Kernel::setFileSize(u32 messagePointer, Handle fileHandle) { if (!file->isOpen) { Helpers::panic("Tried to get size of closed file"); } + mem.write32(messagePointer, IPC::responseHeader(0x0805, 1, 0)); if (file->fd) { const u64 newSize = mem.read64(messagePointer + 4); @@ -174,6 +203,7 @@ void Kernel::getFileSize(u32 messagePointer, Handle fileHandle) { if (!file->isOpen) { Helpers::panic("Tried to get size of closed file"); } + mem.write32(messagePointer, IPC::responseHeader(0x0804, 3, 0)); if (file->fd) { IOFile f(file->fd); @@ -212,6 +242,7 @@ void Kernel::openLinkFile(u32 messagePointer, Handle fileHandle) { // However we do seek properly on every file access so this shouldn't matter cloneFile.data = new FileSession(*file); + mem.write32(messagePointer, IPC::responseHeader(0x080C, 1, 2)); mem.write32(messagePointer + 4, Result::Success); mem.write32(messagePointer + 12, handle); } @@ -231,5 +262,6 @@ void Kernel::setFilePriority(u32 messagePointer, Handle fileHandle) { } file->priority = priority; + mem.write32(messagePointer, IPC::responseHeader(0x080A, 1, 0)); mem.write32(messagePointer + 4, Result::Success); } \ No newline at end of file diff --git a/src/core/services/fs.cpp b/src/core/services/fs.cpp index 080c0b49..fe8bce8d 100644 --- a/src/core/services/fs.cpp +++ b/src/core/services/fs.cpp @@ -21,6 +21,7 @@ namespace FSCommands { OpenArchive = 0x080C00C2, ControlArchive = 0x080D0144, CloseArchive = 0x080E0080, + FormatThisUserSaveData = 0x080F0180, GetFreeBytes = 0x08120080, IsSdmcDetected = 0x08170000, GetFormatInfo = 0x084500C2, @@ -45,23 +46,29 @@ void FSService::reset() { // Creates directories for NAND, ExtSaveData, etc if they don't already exist. Should be executed after loading a new ROM. void FSService::initializeFilesystem() { - const auto nandPath = IOFile::getAppData() / "NAND"; // Create NAND - const auto cartPath = IOFile::getAppData() / "CartSave"; // Create cartridge save folder for use with ExtSaveData - const auto savePath = IOFile::getAppData() / "SaveData"; // Create SaveData - namespace fs = std::filesystem; - // TODO: SDMC, etc + const auto sdmcPath = IOFile::getAppData() / "SDMC"; // Create SDMC directory + const auto nandSharedpath = IOFile::getAppData() / ".." / "SharedFiles" / "NAND"; - if (!fs::is_directory(nandPath)) { - fs::create_directories(nandPath); + const auto savePath = IOFile::getAppData() / "SaveData"; // Create SaveData + const auto formatPath = IOFile::getAppData() / "FormatInfo"; // Create folder for storing archive formatting info + namespace fs = std::filesystem; + + + if (!fs::is_directory(nandSharedpath)) { + fs::create_directories(nandSharedpath); } - if (!fs::is_directory(cartPath)) { - fs::create_directories(cartPath); + if (!fs::is_directory(sdmcPath)) { + fs::create_directories(sdmcPath); } if (!fs::is_directory(savePath)) { fs::create_directories(savePath); } + + if (!fs::is_directory(formatPath)) { + fs::create_directories(formatPath); + } } ArchiveBase* FSService::getArchiveFromID(u32 id, const FSPath& archivePath) { @@ -69,22 +76,10 @@ ArchiveBase* FSService::getArchiveFromID(u32 id, const FSPath& archivePath) { case ArchiveID::SelfNCCH: return &selfNcch; case ArchiveID::SaveData: return &saveData; case ArchiveID::ExtSaveData: - if (archivePath.type == PathType::Binary) { - switch (archivePath.binary[0]) { - case 0: return &extSaveData_nand; - case 1: return &extSaveData_cart; - } - } - return nullptr; + return &extSaveData_sdmc; case ArchiveID::SharedExtSaveData: - if (archivePath.type == PathType::Binary) { - switch (archivePath.binary[0]) { - case 0: return &sharedExtSaveData_nand; - case 1: return &sharedExtSaveData_cart; - } - } - return nullptr; + return &sharedExtSaveData_nand; case ArchiveID::SDMC: return &sdmc; case ArchiveID::SavedataAndNcch: return &ncch; // This can only access NCCH outside of FSPXI @@ -121,12 +116,12 @@ Rust::Result FSService::openDirectoryHandle(ArchiveBase* archi } } -std::optional FSService::openArchiveHandle(u32 archiveID, const FSPath& path) { +Rust::Result FSService::openArchiveHandle(u32 archiveID, const FSPath& path) { ArchiveBase* archive = getArchiveFromID(archiveID, path); if (archive == nullptr) [[unlikely]] { Helpers::panic("OpenArchive: Tried to open unknown archive %d.", archiveID); - return std::nullopt; + return Err(FSResult::NotFormatted); } Rust::Result res = archive->openArchive(path); @@ -135,10 +130,10 @@ std::optional FSService::openArchiveHandle(u32 archiveID, const FSPath& auto& archiveObject = kernel.getObjects()[handle]; archiveObject.data = new ArchiveSession(res.unwrap(), path); - return handle; + return Ok(handle); } else { - return std::nullopt; + return Err(res.unwrapErr()); } } @@ -161,6 +156,7 @@ void FSService::handleSyncRequest(u32 messagePointer) { case FSCommands::CloseArchive: closeArchive(messagePointer); break; case FSCommands::DeleteFile: deleteFile(messagePointer); break; case FSCommands::FormatSaveData: formatSaveData(messagePointer); break; + case FSCommands::FormatThisUserSaveData: formatThisUserSaveData(messagePointer); break; case FSCommands::GetFreeBytes: getFreeBytes(messagePointer); break; case FSCommands::GetFormatInfo: getFormatInfo(messagePointer); break; case FSCommands::GetPriority: getPriority(messagePointer); break; @@ -216,14 +212,15 @@ void FSService::openArchive(u32 messagePointer) { auto archivePath = readPath(archivePathType, archivePathPointer, archivePathSize); log("FS::OpenArchive(archive ID = %d, archive path type = %d)\n", archiveID, archivePathType); - std::optional handle = openArchiveHandle(archiveID, archivePath); + Rust::Result res = openArchiveHandle(archiveID, archivePath); mem.write32(messagePointer, IPC::responseHeader(0x80C, 3, 0)); - if (handle.has_value()) { + if (res.isOk()) { mem.write32(messagePointer + 4, ResultCode::Success); - mem.write64(messagePointer + 8, handle.value()); + mem.write64(messagePointer + 8, res.unwrap()); } else { - log("FS::OpenArchive: Failed to open archive with id = %d\n", archiveID); - mem.write32(messagePointer + 4, ResultCode::Failure); + log("FS::OpenArchive: Failed to open archive with id = %d. Error %08X\n", archiveID, (u32)res.unwrapErr()); + mem.write32(messagePointer + 4, static_cast(res.unwrapErr())); + mem.write64(messagePointer + 8, 0); } } @@ -411,16 +408,25 @@ void FSService::getFormatInfo(u32 messagePointer) { Helpers::panic("OpenArchive: Tried to open unknown archive %d.", archiveID); } - ArchiveBase::FormatInfo info = archive->getFormatInfo(path); mem.write32(messagePointer, IPC::responseHeader(0x845, 5, 0)); - mem.write32(messagePointer + 4, ResultCode::Success); - mem.write32(messagePointer + 8, info.size); - mem.write32(messagePointer + 12, info.numOfDirectories); - mem.write32(messagePointer + 16, info.numOfFiles); - mem.write8(messagePointer + 20, info.duplicateData ? 1 : 0); + Rust::Result res = archive->getFormatInfo(path); + + // If the FormatInfo was returned, write them to the output buffer. Otherwise, write an error code. + if (res.isOk()) { + ArchiveBase::FormatInfo info = res.unwrap(); + mem.write32(messagePointer + 4, ResultCode::Success); + mem.write32(messagePointer + 8, info.size); + mem.write32(messagePointer + 12, info.numOfDirectories); + mem.write32(messagePointer + 16, info.numOfFiles); + mem.write8(messagePointer + 20, info.duplicateData ? 1 : 0); + } else { + mem.write32(messagePointer + 4, static_cast(res.unwrapErr())); + } } void FSService::formatSaveData(u32 messagePointer) { + log("FS::FormatSaveData\n"); + const u32 archiveID = mem.read32(messagePointer + 4); if (archiveID != ArchiveID::SaveData) Helpers::panic("FS::FormatSaveData: Archive is not SaveData"); @@ -440,13 +446,43 @@ void FSService::formatSaveData(u32 messagePointer) { const u32 fileNum = mem.read32(messagePointer + 24); // Max number of files const u32 directoryBucketNum = mem.read32(messagePointer + 28); // Not sure what a directory bucket is...? const u32 fileBucketNum = mem.read32(messagePointer + 32); // Same here - const bool duplicateData = mem.read8(messagePointer + 36) != 0; + const bool duplicateData = mem.read8(messagePointer + 36) != 0; + + ArchiveBase::FormatInfo info { + .size = blockSize * 0x200, + .numOfDirectories = directoryNum, + .numOfFiles = fileNum, + .duplicateData = duplicateData + }; + + saveData.format(path, info); - printf("Stubbed FS::FormatSaveData. File num: %d, directory num: %d\n", fileNum, directoryNum); mem.write32(messagePointer, IPC::responseHeader(0x84C, 1, 0)); mem.write32(messagePointer + 4, ResultCode::Success); } +void FSService::formatThisUserSaveData(u32 messagePointer) { + log("FS::FormatThisUserSaveData\n"); + + const u32 blockSize = mem.read32(messagePointer + 4); + const u32 directoryNum = mem.read32(messagePointer + 8); // Max number of directories + const u32 fileNum = mem.read32(messagePointer + 12); // Max number of files + const u32 directoryBucketNum = mem.read32(messagePointer + 16); // Not sure what a directory bucket is...? + const u32 fileBucketNum = mem.read32(messagePointer + 20); // Same here + const bool duplicateData = mem.read8(messagePointer + 24) != 0; + + ArchiveBase::FormatInfo info { + .size = blockSize * 0x200, + .numOfDirectories = directoryNum, + .numOfFiles = fileNum, + .duplicateData = duplicateData + }; + FSPath emptyPath; + + mem.write32(messagePointer, IPC::responseHeader(0x080F, 1, 0)); + saveData.format(emptyPath, info); +} + void FSService::controlArchive(u32 messagePointer) { const Handle archiveHandle = mem.read64(messagePointer + 4); const u32 action = mem.read32(messagePointer + 12);