SaveIcon and ReadExtSaveDataIcon

This commit is contained in:
Ada 2024-03-08 13:09:11 +00:00
parent 556b3b8913
commit d4594446bd
No known key found for this signature in database
GPG key ID: 066E56D5C9F4E50D
6 changed files with 110 additions and 18 deletions

View file

@ -57,6 +57,14 @@ namespace ArchiveID {
}
}
namespace MediaType {
enum : u8 {
NAND = 0,
SD = 1,
Gamecard = 2
};
};
struct FSPath {
u32 type = PathType::Invalid;

View file

@ -22,6 +22,9 @@ public:
std::optional<u32> readFile(FileSession* file, u64 offset, u32 size, u32 dataPointer) override;
void format(const FSPath& path, const FormatInfo& info) override;
Rust::Result<FormatInfo, HorizonResult> getFormatInfo(const FSPath& path) override;
void clear(const FSPath& path) const;
void saveIcon(const std::vector<u8>& data) const;
Rust::Result<std::vector<u8>, HorizonResult> loadIcon() const;
std::filesystem::path getFormatInfoPath(const FSPath& path) const;
std::filesystem::path getUserDataPath() const;

View file

@ -42,6 +42,7 @@ class FSService {
Rust::Result<Handle, HorizonResult> openDirectoryHandle(ArchiveBase* archive, const FSPath& path);
std::optional<Handle> 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;

View file

@ -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<u8>& 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<std::vector<u8>, HorizonResult> ExtSaveDataArchive::loadIcon() const {
const fs::path iconPath = IOFile::getAppData() / backingFolder / "icon";
IOFile file(iconPath, "rb");
const s32 size = static_cast<s32>(file.size().value_or(-1));
if(size < 0) {
return Err(Result::FS::NotFoundInvalid);
}
std::unique_ptr<u8[]> 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");
}

View file

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

View file

@ -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<s32>(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<s32>(mediaType));
Helpers::warn("FS::CreateExtSaveData - Unhandled ExtSaveData MediaType %d", static_cast<s32>(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<s32>(mediaType));
break;
}
mem.write32(messagePointer, IPC::responseHeader(0x0851, 1, 0));
Rust::Result<std::vector<u8>, 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<u8> buffer = data.unwrap<std::vector<u8>>();
const u32 copySize = std::min(smdhSize, static_cast<u32>(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");