mirror of
https://github.com/wheremyfoodat/Panda3DS.git
synced 2025-04-08 23:25:40 +12:00
Partially implement ExtSaveData
This commit is contained in:
parent
24705fe67e
commit
a4e44cf060
5 changed files with 188 additions and 30 deletions
|
@ -228,9 +228,19 @@ public:
|
||||||
return Ok(FormatInfo{ .size = 0, .numOfDirectories = 0, .numOfFiles = 0, .duplicateData = false });
|
return Ok(FormatInfo{ .size = 0, .numOfDirectories = 0, .numOfFiles = 0, .duplicateData = false });
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual HorizonResult createDirectory(const FSPath& path) {
|
virtual HorizonResult createDirectory(const FSPath& path) {
|
||||||
Helpers::panic("Unimplemented CreateDirectory for %s archive", name().c_str());
|
Helpers::panic("Unimplemented CreateDirectory for %s archive", name().c_str());
|
||||||
return Result::FS::AlreadyExists;
|
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)
|
// Returns nullopt if opening the file failed, otherwise returns a file descriptor to it (nullptr if none is needed)
|
||||||
|
|
|
@ -10,6 +10,8 @@ public:
|
||||||
std::string name() override { return "ExtSaveData::" + backingFolder; }
|
std::string name() override { return "ExtSaveData::" + backingFolder; }
|
||||||
|
|
||||||
HorizonResult createDirectory(const FSPath& path) override;
|
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 createFile(const FSPath& path, u64 size) override;
|
||||||
HorizonResult deleteFile(const FSPath& path) override;
|
HorizonResult deleteFile(const FSPath& path) override;
|
||||||
HorizonResult renameFile(const FSPath& oldPath, const FSPath& newPath) override;
|
HorizonResult renameFile(const FSPath& oldPath, const FSPath& newPath) override;
|
||||||
|
@ -18,15 +20,15 @@ public:
|
||||||
Rust::Result<DirectorySession, HorizonResult> openDirectory(const FSPath& path) override;
|
Rust::Result<DirectorySession, HorizonResult> openDirectory(const FSPath& path) override;
|
||||||
FileDescriptor openFile(const FSPath& path, const FilePerms& perms) override;
|
FileDescriptor openFile(const FSPath& path, const FilePerms& perms) override;
|
||||||
std::optional<u32> readFile(FileSession* file, u64 offset, u32 size, u32 dataPointer) override;
|
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;
|
||||||
|
|
||||||
Rust::Result<FormatInfo, HorizonResult> getFormatInfo(const FSPath& path) override {
|
std::filesystem::path getFormatInfoPath(const FSPath& path) const;
|
||||||
Helpers::warn("Stubbed ExtSaveData::GetFormatInfo");
|
std::filesystem::path getUserDataPath() const;
|
||||||
return Ok(FormatInfo{.size = 1_GB, .numOfDirectories = 255, .numOfFiles = 255, .duplicateData = false});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Takes in a binary ExtSaveData path, outputs a combination of the backing folder with the low and high save entries of the path
|
// 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
|
// Used for identifying the archive format info files
|
||||||
std::string getExtSaveDataPathFromBinary(const FSPath& path);
|
std::string getExtSaveDataPathFromBinary(const FSPath& path) const;
|
||||||
|
|
||||||
bool isShared = false;
|
bool isShared = false;
|
||||||
std::string backingFolder; // Backing folder for the archive. Can be NAND, Gamecard or SD depending on the archive path.
|
std::string backingFolder; // Backing folder for the archive. Can be NAND, Gamecard or SD depending on the archive path.
|
||||||
|
|
|
@ -54,6 +54,7 @@ class FSService {
|
||||||
void closeArchive(u32 messagePointer);
|
void closeArchive(u32 messagePointer);
|
||||||
void controlArchive(u32 messagePointer);
|
void controlArchive(u32 messagePointer);
|
||||||
void deleteDirectory(u32 messagePointer);
|
void deleteDirectory(u32 messagePointer);
|
||||||
|
void deleteDirectoryRecursively(u32 messagePointer);
|
||||||
void deleteExtSaveData(u32 messagePointer);
|
void deleteExtSaveData(u32 messagePointer);
|
||||||
void deleteFile(u32 messagePointer);
|
void deleteFile(u32 messagePointer);
|
||||||
void formatSaveData(u32 messagePointer);
|
void formatSaveData(u32 messagePointer);
|
||||||
|
|
|
@ -11,7 +11,8 @@ HorizonResult ExtSaveDataArchive::createFile(const FSPath& path, u64 size) {
|
||||||
if (!isPathSafe<PathType::UTF16>(path))
|
if (!isPathSafe<PathType::UTF16>(path))
|
||||||
Helpers::panic("Unsafe path in ExtSaveData::CreateFile");
|
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();
|
p += fs::path(path.utf16_string).make_preferred();
|
||||||
|
|
||||||
if (fs::exists(p))
|
if (fs::exists(p))
|
||||||
|
@ -37,7 +38,8 @@ HorizonResult ExtSaveDataArchive::deleteFile(const FSPath& path) {
|
||||||
if (!isPathSafe<PathType::UTF16>(path))
|
if (!isPathSafe<PathType::UTF16>(path))
|
||||||
Helpers::panic("Unsafe path in ExtSaveData::DeleteFile");
|
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();
|
p += fs::path(path.utf16_string).make_preferred();
|
||||||
|
|
||||||
if (fs::is_directory(p)) {
|
if (fs::is_directory(p)) {
|
||||||
|
@ -72,7 +74,8 @@ FileDescriptor ExtSaveDataArchive::openFile(const FSPath& path, const FilePerms&
|
||||||
if (perms.create())
|
if (perms.create())
|
||||||
Helpers::panic("[ExtSaveData] Can't open file with create flag");
|
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();
|
p += fs::path(path.utf16_string).make_preferred();
|
||||||
|
|
||||||
if (fs::exists(p)) { // Return file descriptor if the file exists
|
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
|
// Construct host filesystem paths
|
||||||
fs::path sourcePath = IOFile::getAppData() / backingFolder;
|
fs::path sourcePath = getUserDataPath();
|
||||||
|
|
||||||
fs::path destPath = sourcePath;
|
fs::path destPath = sourcePath;
|
||||||
|
|
||||||
sourcePath += fs::path(oldPath.utf16_string).make_preferred();
|
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");
|
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();
|
p += fs::path(path.utf16_string).make_preferred();
|
||||||
|
|
||||||
if (fs::is_directory(p)) return Result::FS::AlreadyExists;
|
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<PathType::UTF16>(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<PathType::UTF16>(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<ArchiveBase::FormatInfo, HorizonResult> 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
|
// TODO: Remove punning here
|
||||||
const u32 mediaType = *(u32*)&path.binary[0];
|
const u32 mediaType = *(u32*)&path.binary[0];
|
||||||
const u32 saveLow = *(u32*)&path.binary[4];
|
const u32 saveLow = *(u32*)&path.binary[4];
|
||||||
const u32 saveHigh = *(u32*)&path.binary[8];
|
const u32 saveHigh = *(u32*)&path.binary[8];
|
||||||
|
|
||||||
// TODO: Should the media type be used here
|
// TODO: Should the media type be used here, using it just to be safe.
|
||||||
return backingFolder + std::to_string(saveLow) + std::to_string(saveHigh);
|
return backingFolder + std::to_string(mediaType) + "_" + std::to_string(saveLow) + "_" + std::to_string(saveHigh);
|
||||||
}
|
}
|
||||||
|
|
||||||
Rust::Result<ArchiveBase*, HorizonResult> ExtSaveDataArchive::openArchive(const FSPath& path) {
|
Rust::Result<ArchiveBase*, HorizonResult> ExtSaveDataArchive::openArchive(const FSPath& path) {
|
||||||
|
@ -160,13 +274,12 @@ Rust::Result<ArchiveBase*, HorizonResult> ExtSaveDataArchive::openArchive(const
|
||||||
Helpers::panic("ExtSaveData accessed with an invalid path in OpenArchive");
|
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
|
// 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
|
// Format info not found so the archive is not formatted
|
||||||
// if (!fs::is_regular_file(formatInfopath)) {
|
if (!fs::is_regular_file(formatInfoPath)) {
|
||||||
// return isShared ? Err(Result::FS::NotFormatted) : Err(Result::FS::NotFoundInvalid);
|
return isShared ? Err(Result::FS::NotFormatted) : Err(Result::FS::NotFoundInvalid);
|
||||||
//}
|
}
|
||||||
|
|
||||||
return Ok((ArchiveBase*)this);
|
return Ok((ArchiveBase*)this);
|
||||||
}
|
}
|
||||||
|
@ -176,7 +289,7 @@ Rust::Result<DirectorySession, HorizonResult> ExtSaveDataArchive::openDirectory(
|
||||||
if (!isPathSafe<PathType::UTF16>(path))
|
if (!isPathSafe<PathType::UTF16>(path))
|
||||||
Helpers::panic("Unsafe path in ExtSaveData::OpenDirectory");
|
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();
|
p += fs::path(path.utf16_string).make_preferred();
|
||||||
|
|
||||||
if (fs::is_regular_file(p)) {
|
if (fs::is_regular_file(p)) {
|
||||||
|
|
|
@ -173,6 +173,7 @@ void FSService::handleSyncRequest(u32 messagePointer) {
|
||||||
case FSCommands::ControlArchive: controlArchive(messagePointer); break;
|
case FSCommands::ControlArchive: controlArchive(messagePointer); break;
|
||||||
case FSCommands::CloseArchive: closeArchive(messagePointer); break;
|
case FSCommands::CloseArchive: closeArchive(messagePointer); break;
|
||||||
case FSCommands::DeleteDirectory: deleteDirectory(messagePointer); break;
|
case FSCommands::DeleteDirectory: deleteDirectory(messagePointer); break;
|
||||||
|
case FSCommands::DeleteDirectoryRecursively: deleteDirectoryRecursively(messagePointer); break;
|
||||||
case FSCommands::DeleteExtSaveData: deleteExtSaveData(messagePointer); break;
|
case FSCommands::DeleteExtSaveData: deleteExtSaveData(messagePointer); break;
|
||||||
case FSCommands::DeleteFile: deleteFile(messagePointer); break;
|
case FSCommands::DeleteFile: deleteFile(messagePointer); break;
|
||||||
case FSCommands::FormatSaveData: formatSaveData(messagePointer); break;
|
case FSCommands::FormatSaveData: formatSaveData(messagePointer); break;
|
||||||
|
@ -431,11 +432,23 @@ void FSService::deleteDirectory(u32 messagePointer) {
|
||||||
const u32 filePathPointer = mem.read32(messagePointer + 28);
|
const u32 filePathPointer = mem.read32(messagePointer + 28);
|
||||||
log("FS::DeleteDirectory\n");
|
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, IPC::responseHeader(0x806, 1, 0));
|
||||||
mem.write32(messagePointer + 4, Result::Success);
|
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) {
|
void FSService::getFormatInfo(u32 messagePointer) {
|
||||||
const u32 archiveID = mem.read32(messagePointer + 4);
|
const u32 archiveID = mem.read32(messagePointer + 4);
|
||||||
const u32 pathType = mem.read32(messagePointer + 8);
|
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 smdhSize = mem.read32(messagePointer + 36);
|
||||||
const u32 smdhPointer = mem.read32(messagePointer + 44);
|
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<s32>(mediaType));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
mem.write32(messagePointer, IPC::responseHeader(0x0851, 1, 0));
|
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);
|
mem.write32(messagePointer + 4, Result::Success);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue