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; }