mirror of
https://github.com/wheremyfoodat/Panda3DS.git
synced 2025-04-11 00:25:41 +12:00
Merge pull request #7 from wheremyfoodat/DSP-n-FS-work
Better DSP & FS HLE
This commit is contained in:
commit
9ca66f4157
11 changed files with 234 additions and 58 deletions
|
@ -204,10 +204,11 @@ public:
|
||||||
virtual u64 getFreeBytes() = 0;
|
virtual u64 getFreeBytes() = 0;
|
||||||
virtual FSResult createFile(const FSPath& path, u64 size) = 0;
|
virtual FSResult createFile(const FSPath& path, u64 size) = 0;
|
||||||
virtual FSResult deleteFile(const FSPath& path) = 0;
|
virtual FSResult deleteFile(const FSPath& path) = 0;
|
||||||
virtual FormatInfo getFormatInfo(const FSPath& path) {
|
|
||||||
|
virtual Rust::Result<FormatInfo, FSResult> getFormatInfo(const FSPath& path) {
|
||||||
Helpers::panic("Unimplemented GetFormatInfo for %s archive", name().c_str());
|
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 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) {
|
virtual FSResult createDirectory(const FSPath& path) {
|
||||||
|
@ -224,6 +225,10 @@ public:
|
||||||
return Err(FSResult::FileNotFound);
|
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
|
// 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
|
// Returns the number of bytes read, or nullopt if the read failed
|
||||||
virtual std::optional<u32> readFile(FileSession* file, u64 offset, u32 size, u32 dataPointer) = 0;
|
virtual std::optional<u32> readFile(FileSession* file, u64 offset, u32 size, u32 dataPointer) = 0;
|
||||||
|
|
|
@ -17,6 +17,10 @@ public:
|
||||||
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;
|
||||||
|
|
||||||
|
// 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;
|
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.
|
||||||
};
|
};
|
|
@ -11,13 +11,19 @@ public:
|
||||||
FSResult createDirectory(const FSPath& path) override;
|
FSResult createDirectory(const FSPath& path) override;
|
||||||
FSResult createFile(const FSPath& path, u64 size) override;
|
FSResult createFile(const FSPath& path, u64 size) override;
|
||||||
FSResult deleteFile(const FSPath& path) override;
|
FSResult deleteFile(const FSPath& path) override;
|
||||||
FormatInfo getFormatInfo(const FSPath& path) override;
|
|
||||||
|
|
||||||
Rust::Result<ArchiveBase*, FSResult> openArchive(const FSPath& path) override;
|
Rust::Result<ArchiveBase*, FSResult> openArchive(const FSPath& path) override;
|
||||||
Rust::Result<DirectorySession, FSResult> openDirectory(const FSPath& path) override;
|
Rust::Result<DirectorySession, FSResult> 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, FSResult> getFormatInfo(const FSPath& path) override;
|
||||||
|
|
||||||
|
std::filesystem::path getFormatInfoPath() {
|
||||||
|
return IOFile::getAppData() / "FormatInfo" / "SaveData.format";
|
||||||
|
}
|
||||||
|
|
||||||
// Returns whether the cart has save data or not
|
// Returns whether the cart has save data or not
|
||||||
bool cartHasSaveData() {
|
bool cartHasSaveData() {
|
||||||
auto cxi = mem.getCXI();
|
auto cxi = mem.getCXI();
|
||||||
|
|
|
@ -146,6 +146,7 @@ private:
|
||||||
// File operations
|
// File operations
|
||||||
void handleFileOperation(u32 messagePointer, Handle file);
|
void handleFileOperation(u32 messagePointer, Handle file);
|
||||||
void closeFile(u32 messagePointer, Handle file);
|
void closeFile(u32 messagePointer, Handle file);
|
||||||
|
void flushFile(u32 messagePointer, Handle file);
|
||||||
void readFile(u32 messagePointer, Handle file);
|
void readFile(u32 messagePointer, Handle file);
|
||||||
void writeFile(u32 messagePointer, Handle file);
|
void writeFile(u32 messagePointer, Handle file);
|
||||||
void getFileSize(u32 messagePointer, Handle file);
|
void getFileSize(u32 messagePointer, Handle file);
|
||||||
|
|
|
@ -25,13 +25,11 @@ class FSService {
|
||||||
SDMCArchive sdmc;
|
SDMCArchive sdmc;
|
||||||
NCCHArchive ncch;
|
NCCHArchive ncch;
|
||||||
|
|
||||||
ExtSaveDataArchive extSaveData_nand;
|
ExtSaveDataArchive extSaveData_sdmc;
|
||||||
ExtSaveDataArchive extSaveData_cart;
|
|
||||||
ExtSaveDataArchive sharedExtSaveData_nand;
|
ExtSaveDataArchive sharedExtSaveData_nand;
|
||||||
ExtSaveDataArchive sharedExtSaveData_cart;
|
|
||||||
|
|
||||||
ArchiveBase* getArchiveFromID(u32 id, const FSPath& archivePath);
|
ArchiveBase* getArchiveFromID(u32 id, const FSPath& archivePath);
|
||||||
std::optional<Handle> openArchiveHandle(u32 archiveID, const FSPath& path);
|
Rust::Result<Handle, FSResult> openArchiveHandle(u32 archiveID, const FSPath& path);
|
||||||
Rust::Result<Handle, FSResult> openDirectoryHandle(ArchiveBase* archive, const FSPath& path);
|
Rust::Result<Handle, FSResult> openDirectoryHandle(ArchiveBase* archive, const FSPath& path);
|
||||||
std::optional<Handle> openFileHandle(ArchiveBase* archive, const FSPath& path, const FSPath& archivePath, const FilePerms& perms);
|
std::optional<Handle> openFileHandle(ArchiveBase* archive, const FSPath& path, const FSPath& archivePath, const FilePerms& perms);
|
||||||
FSPath readPath(u32 type, u32 pointer, u32 size);
|
FSPath readPath(u32 type, u32 pointer, u32 size);
|
||||||
|
@ -43,6 +41,7 @@ class FSService {
|
||||||
void controlArchive(u32 messagePointer);
|
void controlArchive(u32 messagePointer);
|
||||||
void deleteFile(u32 messagePointer);
|
void deleteFile(u32 messagePointer);
|
||||||
void formatSaveData(u32 messagePointer);
|
void formatSaveData(u32 messagePointer);
|
||||||
|
void formatThisUserSaveData(u32 messagePointer);
|
||||||
void getFreeBytes(u32 messagePointer);
|
void getFreeBytes(u32 messagePointer);
|
||||||
void getFormatInfo(u32 messagePointer);
|
void getFormatInfo(u32 messagePointer);
|
||||||
void getPriority(u32 messagePointer);
|
void getPriority(u32 messagePointer);
|
||||||
|
@ -59,8 +58,8 @@ class FSService {
|
||||||
u32 priority;
|
u32 priority;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
FSService(Memory& mem, Kernel& kernel) : mem(mem), saveData(mem), extSaveData_nand(mem, "NAND"),
|
FSService(Memory& mem, Kernel& kernel) : mem(mem), saveData(mem),
|
||||||
sharedExtSaveData_nand(mem, "NAND", true), extSaveData_cart(mem, "CartSave"), sharedExtSaveData_cart(mem, "CartSave", true),
|
sharedExtSaveData_nand(mem, "../SharedFiles/NAND", true), extSaveData_sdmc(mem, "SDMC"),
|
||||||
sdmc(mem), selfNcch(mem), ncch(mem), kernel(kernel)
|
sdmc(mem), selfNcch(mem), ncch(mem), kernel(kernel)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
|
|
|
@ -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.
|
Nintendo 3DS is a registered trademark of Nintendo Co., Ltd.
|
||||||
|
|
||||||

|

|
||||||
Here's a panda it go blep
|
Here's a panda it go blep
|
||||||
|
|
|
@ -38,12 +38,27 @@ FSResult ExtSaveDataArchive::deleteFile(const FSPath& path) {
|
||||||
fs::path p = IOFile::getAppData() / backingFolder;
|
fs::path p = IOFile::getAppData() / backingFolder;
|
||||||
p += fs::path(path.utf16_string).make_preferred();
|
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;
|
std::error_code ec;
|
||||||
bool success = fs::remove(p, 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;
|
return FSResult::Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -70,11 +85,29 @@ FileDescriptor ExtSaveDataArchive::openFile(const FSPath& path, const FilePerms&
|
||||||
return FileError;
|
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<ArchiveBase*, FSResult> ExtSaveDataArchive::openArchive(const FSPath& path) {
|
Rust::Result<ArchiveBase*, FSResult> ExtSaveDataArchive::openArchive(const FSPath& path) {
|
||||||
if (path.type != PathType::Binary || path.binary.size() != 12) {
|
if (path.type != PathType::Binary || path.binary.size() != 12) {
|
||||||
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
|
||||||
|
// 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);
|
return Ok((ArchiveBase*)this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -31,7 +31,34 @@ FSResult SaveDataArchive::createDirectory(const FSPath& path) {
|
||||||
}
|
}
|
||||||
|
|
||||||
FSResult SaveDataArchive::deleteFile(const FSPath& path) {
|
FSResult SaveDataArchive::deleteFile(const FSPath& path) {
|
||||||
Helpers::panic("[SaveData] Unimplemented DeleteFile");
|
if (path.type == PathType::UTF16) {
|
||||||
|
if (!isPathSafe<PathType::UTF16>(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;
|
return FSResult::Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -93,9 +120,36 @@ Rust::Result<DirectorySession, FSResult> SaveDataArchive::openDirectory(const FS
|
||||||
return Err(FSResult::Success);
|
return Err(FSResult::Success);
|
||||||
}
|
}
|
||||||
|
|
||||||
ArchiveBase::FormatInfo SaveDataArchive::getFormatInfo(const FSPath& path) {
|
Rust::Result<ArchiveBase::FormatInfo, FSResult> SaveDataArchive::getFormatInfo(const FSPath& path) {
|
||||||
//Helpers::panic("Unimplemented SaveData::GetFormatInfo");
|
const fs::path formatInfoPath = getFormatInfoPath();
|
||||||
return FormatInfo{ .size = 0, .numOfDirectories = 255, .numOfFiles = 255, .duplicateData = false };
|
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<ArchiveBase*, FSResult> SaveDataArchive::openArchive(const FSPath& path) {
|
Rust::Result<ArchiveBase*, FSResult> SaveDataArchive::openArchive(const FSPath& path) {
|
||||||
|
@ -104,6 +158,12 @@ Rust::Result<ArchiveBase*, FSResult> SaveDataArchive::openArchive(const FSPath&
|
||||||
return Err(FSResult::NotFoundInvalid);
|
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);
|
return Ok((ArchiveBase*)this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -18,7 +18,7 @@ FileDescriptor SDMCArchive::openFile(const FSPath& path, const FilePerms& perms)
|
||||||
|
|
||||||
Rust::Result<ArchiveBase*, FSResult> SDMCArchive::openArchive(const FSPath& path) {
|
Rust::Result<ArchiveBase*, FSResult> SDMCArchive::openArchive(const FSPath& path) {
|
||||||
printf("SDMCArchive::OpenArchive: Failed\n");
|
printf("SDMCArchive::OpenArchive: Failed\n");
|
||||||
return Ok((ArchiveBase*)nullptr);
|
return Err(FSResult::NotFormatted);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<u32> SDMCArchive::readFile(FileSession* file, u64 offset, u32 size, u32 dataPointer) {
|
std::optional<u32> SDMCArchive::readFile(FileSession* file, u64 offset, u32 size, u32 dataPointer) {
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
#include "ipc.hpp"
|
||||||
#include "kernel.hpp"
|
#include "kernel.hpp"
|
||||||
|
|
||||||
namespace FileOps {
|
namespace FileOps {
|
||||||
|
@ -7,6 +8,7 @@ namespace FileOps {
|
||||||
GetSize = 0x08040000,
|
GetSize = 0x08040000,
|
||||||
SetSize = 0x08050080,
|
SetSize = 0x08050080,
|
||||||
Close = 0x08080000,
|
Close = 0x08080000,
|
||||||
|
Flush = 0x08090000,
|
||||||
SetPriority = 0x080A0040,
|
SetPriority = 0x080A0040,
|
||||||
OpenLinkFile = 0x080C0000
|
OpenLinkFile = 0x080C0000
|
||||||
};
|
};
|
||||||
|
@ -41,7 +43,30 @@ void Kernel::closeFile(u32 messagePointer, Handle fileHandle) {
|
||||||
Helpers::panic("Called CloseFile on non-existent file");
|
Helpers::panic("Called CloseFile on non-existent file");
|
||||||
}
|
}
|
||||||
|
|
||||||
p->getData<FileSession>()->isOpen = false;
|
FileSession* session = p->getData<FileSession>();
|
||||||
|
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<FileSession>();
|
||||||
|
if (session->fd != nullptr) {
|
||||||
|
fflush(session->fd);
|
||||||
|
}
|
||||||
|
|
||||||
|
mem.write32(messagePointer, IPC::responseHeader(0x0809, 1, 0));
|
||||||
mem.write32(messagePointer + 4, Result::Success);
|
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");
|
Helpers::panic("Called ReadFile on non-existent file");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mem.write32(messagePointer, IPC::responseHeader(0x0802, 2, 2));
|
||||||
|
|
||||||
FileSession* file = p->getData<FileSession>();
|
FileSession* file = p->getData<FileSession>();
|
||||||
if (!file->isOpen) {
|
if (!file->isOpen) {
|
||||||
Helpers::panic("Tried to read closed file");
|
Helpers::panic("Tried to read closed file");
|
||||||
|
@ -126,6 +153,7 @@ void Kernel::writeFile(u32 messagePointer, Handle fileHandle) {
|
||||||
IOFile f(file->fd);
|
IOFile f(file->fd);
|
||||||
auto [success, bytesWritten] = f.writeBytes(data.get(), size);
|
auto [success, bytesWritten] = f.writeBytes(data.get(), size);
|
||||||
|
|
||||||
|
mem.write32(messagePointer, IPC::responseHeader(0x0803, 2, 2));
|
||||||
if (!success) {
|
if (!success) {
|
||||||
Helpers::panic("Kernel::WriteFile failed");
|
Helpers::panic("Kernel::WriteFile failed");
|
||||||
} else {
|
} else {
|
||||||
|
@ -146,6 +174,7 @@ void Kernel::setFileSize(u32 messagePointer, Handle fileHandle) {
|
||||||
if (!file->isOpen) {
|
if (!file->isOpen) {
|
||||||
Helpers::panic("Tried to get size of closed file");
|
Helpers::panic("Tried to get size of closed file");
|
||||||
}
|
}
|
||||||
|
mem.write32(messagePointer, IPC::responseHeader(0x0805, 1, 0));
|
||||||
|
|
||||||
if (file->fd) {
|
if (file->fd) {
|
||||||
const u64 newSize = mem.read64(messagePointer + 4);
|
const u64 newSize = mem.read64(messagePointer + 4);
|
||||||
|
@ -174,6 +203,7 @@ void Kernel::getFileSize(u32 messagePointer, Handle fileHandle) {
|
||||||
if (!file->isOpen) {
|
if (!file->isOpen) {
|
||||||
Helpers::panic("Tried to get size of closed file");
|
Helpers::panic("Tried to get size of closed file");
|
||||||
}
|
}
|
||||||
|
mem.write32(messagePointer, IPC::responseHeader(0x0804, 3, 0));
|
||||||
|
|
||||||
if (file->fd) {
|
if (file->fd) {
|
||||||
IOFile f(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
|
// However we do seek properly on every file access so this shouldn't matter
|
||||||
cloneFile.data = new FileSession(*file);
|
cloneFile.data = new FileSession(*file);
|
||||||
|
|
||||||
|
mem.write32(messagePointer, IPC::responseHeader(0x080C, 1, 2));
|
||||||
mem.write32(messagePointer + 4, Result::Success);
|
mem.write32(messagePointer + 4, Result::Success);
|
||||||
mem.write32(messagePointer + 12, handle);
|
mem.write32(messagePointer + 12, handle);
|
||||||
}
|
}
|
||||||
|
@ -231,5 +262,6 @@ void Kernel::setFilePriority(u32 messagePointer, Handle fileHandle) {
|
||||||
}
|
}
|
||||||
file->priority = priority;
|
file->priority = priority;
|
||||||
|
|
||||||
|
mem.write32(messagePointer, IPC::responseHeader(0x080A, 1, 0));
|
||||||
mem.write32(messagePointer + 4, Result::Success);
|
mem.write32(messagePointer + 4, Result::Success);
|
||||||
}
|
}
|
|
@ -21,6 +21,7 @@ namespace FSCommands {
|
||||||
OpenArchive = 0x080C00C2,
|
OpenArchive = 0x080C00C2,
|
||||||
ControlArchive = 0x080D0144,
|
ControlArchive = 0x080D0144,
|
||||||
CloseArchive = 0x080E0080,
|
CloseArchive = 0x080E0080,
|
||||||
|
FormatThisUserSaveData = 0x080F0180,
|
||||||
GetFreeBytes = 0x08120080,
|
GetFreeBytes = 0x08120080,
|
||||||
IsSdmcDetected = 0x08170000,
|
IsSdmcDetected = 0x08170000,
|
||||||
GetFormatInfo = 0x084500C2,
|
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.
|
// Creates directories for NAND, ExtSaveData, etc if they don't already exist. Should be executed after loading a new ROM.
|
||||||
void FSService::initializeFilesystem() {
|
void FSService::initializeFilesystem() {
|
||||||
const auto nandPath = IOFile::getAppData() / "NAND"; // Create NAND
|
const auto sdmcPath = IOFile::getAppData() / "SDMC"; // Create SDMC directory
|
||||||
const auto cartPath = IOFile::getAppData() / "CartSave"; // Create cartridge save folder for use with ExtSaveData
|
const auto nandSharedpath = IOFile::getAppData() / ".." / "SharedFiles" / "NAND";
|
||||||
const auto savePath = IOFile::getAppData() / "SaveData"; // Create SaveData
|
|
||||||
namespace fs = std::filesystem;
|
|
||||||
// TODO: SDMC, etc
|
|
||||||
|
|
||||||
if (!fs::is_directory(nandPath)) {
|
const auto savePath = IOFile::getAppData() / "SaveData"; // Create SaveData
|
||||||
fs::create_directories(nandPath);
|
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)) {
|
if (!fs::is_directory(sdmcPath)) {
|
||||||
fs::create_directories(cartPath);
|
fs::create_directories(sdmcPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!fs::is_directory(savePath)) {
|
if (!fs::is_directory(savePath)) {
|
||||||
fs::create_directories(savePath);
|
fs::create_directories(savePath);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!fs::is_directory(formatPath)) {
|
||||||
|
fs::create_directories(formatPath);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ArchiveBase* FSService::getArchiveFromID(u32 id, const FSPath& archivePath) {
|
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::SelfNCCH: return &selfNcch;
|
||||||
case ArchiveID::SaveData: return &saveData;
|
case ArchiveID::SaveData: return &saveData;
|
||||||
case ArchiveID::ExtSaveData:
|
case ArchiveID::ExtSaveData:
|
||||||
if (archivePath.type == PathType::Binary) {
|
return &extSaveData_sdmc;
|
||||||
switch (archivePath.binary[0]) {
|
|
||||||
case 0: return &extSaveData_nand;
|
|
||||||
case 1: return &extSaveData_cart;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nullptr;
|
|
||||||
|
|
||||||
case ArchiveID::SharedExtSaveData:
|
case ArchiveID::SharedExtSaveData:
|
||||||
if (archivePath.type == PathType::Binary) {
|
return &sharedExtSaveData_nand;
|
||||||
switch (archivePath.binary[0]) {
|
|
||||||
case 0: return &sharedExtSaveData_nand;
|
|
||||||
case 1: return &sharedExtSaveData_cart;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nullptr;
|
|
||||||
|
|
||||||
case ArchiveID::SDMC: return &sdmc;
|
case ArchiveID::SDMC: return &sdmc;
|
||||||
case ArchiveID::SavedataAndNcch: return &ncch; // This can only access NCCH outside of FSPXI
|
case ArchiveID::SavedataAndNcch: return &ncch; // This can only access NCCH outside of FSPXI
|
||||||
|
@ -121,12 +116,12 @@ Rust::Result<Handle, FSResult> FSService::openDirectoryHandle(ArchiveBase* archi
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<Handle> FSService::openArchiveHandle(u32 archiveID, const FSPath& path) {
|
Rust::Result<Handle, FSResult> FSService::openArchiveHandle(u32 archiveID, const FSPath& path) {
|
||||||
ArchiveBase* archive = getArchiveFromID(archiveID, path);
|
ArchiveBase* archive = getArchiveFromID(archiveID, path);
|
||||||
|
|
||||||
if (archive == nullptr) [[unlikely]] {
|
if (archive == nullptr) [[unlikely]] {
|
||||||
Helpers::panic("OpenArchive: Tried to open unknown archive %d.", archiveID);
|
Helpers::panic("OpenArchive: Tried to open unknown archive %d.", archiveID);
|
||||||
return std::nullopt;
|
return Err(FSResult::NotFormatted);
|
||||||
}
|
}
|
||||||
|
|
||||||
Rust::Result<ArchiveBase*, FSResult> res = archive->openArchive(path);
|
Rust::Result<ArchiveBase*, FSResult> res = archive->openArchive(path);
|
||||||
|
@ -135,10 +130,10 @@ std::optional<Handle> FSService::openArchiveHandle(u32 archiveID, const FSPath&
|
||||||
auto& archiveObject = kernel.getObjects()[handle];
|
auto& archiveObject = kernel.getObjects()[handle];
|
||||||
archiveObject.data = new ArchiveSession(res.unwrap(), path);
|
archiveObject.data = new ArchiveSession(res.unwrap(), path);
|
||||||
|
|
||||||
return handle;
|
return Ok(handle);
|
||||||
}
|
}
|
||||||
else {
|
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::CloseArchive: closeArchive(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;
|
||||||
|
case FSCommands::FormatThisUserSaveData: formatThisUserSaveData(messagePointer); break;
|
||||||
case FSCommands::GetFreeBytes: getFreeBytes(messagePointer); break;
|
case FSCommands::GetFreeBytes: getFreeBytes(messagePointer); break;
|
||||||
case FSCommands::GetFormatInfo: getFormatInfo(messagePointer); break;
|
case FSCommands::GetFormatInfo: getFormatInfo(messagePointer); break;
|
||||||
case FSCommands::GetPriority: getPriority(messagePointer); break;
|
case FSCommands::GetPriority: getPriority(messagePointer); break;
|
||||||
|
@ -216,14 +212,15 @@ void FSService::openArchive(u32 messagePointer) {
|
||||||
auto archivePath = readPath(archivePathType, archivePathPointer, archivePathSize);
|
auto archivePath = readPath(archivePathType, archivePathPointer, archivePathSize);
|
||||||
log("FS::OpenArchive(archive ID = %d, archive path type = %d)\n", archiveID, archivePathType);
|
log("FS::OpenArchive(archive ID = %d, archive path type = %d)\n", archiveID, archivePathType);
|
||||||
|
|
||||||
std::optional<Handle> handle = openArchiveHandle(archiveID, archivePath);
|
Rust::Result<Handle, FSResult> res = openArchiveHandle(archiveID, archivePath);
|
||||||
mem.write32(messagePointer, IPC::responseHeader(0x80C, 3, 0));
|
mem.write32(messagePointer, IPC::responseHeader(0x80C, 3, 0));
|
||||||
if (handle.has_value()) {
|
if (res.isOk()) {
|
||||||
mem.write32(messagePointer + 4, ResultCode::Success);
|
mem.write32(messagePointer + 4, ResultCode::Success);
|
||||||
mem.write64(messagePointer + 8, handle.value());
|
mem.write64(messagePointer + 8, res.unwrap());
|
||||||
} else {
|
} else {
|
||||||
log("FS::OpenArchive: Failed to open archive with id = %d\n", archiveID);
|
log("FS::OpenArchive: Failed to open archive with id = %d. Error %08X\n", archiveID, (u32)res.unwrapErr());
|
||||||
mem.write32(messagePointer + 4, ResultCode::Failure);
|
mem.write32(messagePointer + 4, static_cast<u32>(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);
|
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, IPC::responseHeader(0x845, 5, 0));
|
||||||
mem.write32(messagePointer + 4, ResultCode::Success);
|
Rust::Result<ArchiveBase::FormatInfo, FSResult> res = archive->getFormatInfo(path);
|
||||||
mem.write32(messagePointer + 8, info.size);
|
|
||||||
mem.write32(messagePointer + 12, info.numOfDirectories);
|
// If the FormatInfo was returned, write them to the output buffer. Otherwise, write an error code.
|
||||||
mem.write32(messagePointer + 16, info.numOfFiles);
|
if (res.isOk()) {
|
||||||
mem.write8(messagePointer + 20, info.duplicateData ? 1 : 0);
|
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<u32>(res.unwrapErr()));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void FSService::formatSaveData(u32 messagePointer) {
|
void FSService::formatSaveData(u32 messagePointer) {
|
||||||
|
log("FS::FormatSaveData\n");
|
||||||
|
|
||||||
const u32 archiveID = mem.read32(messagePointer + 4);
|
const u32 archiveID = mem.read32(messagePointer + 4);
|
||||||
if (archiveID != ArchiveID::SaveData)
|
if (archiveID != ArchiveID::SaveData)
|
||||||
Helpers::panic("FS::FormatSaveData: Archive is not 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 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 directoryBucketNum = mem.read32(messagePointer + 28); // Not sure what a directory bucket is...?
|
||||||
const u32 fileBucketNum = mem.read32(messagePointer + 32); // Same here
|
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, IPC::responseHeader(0x84C, 1, 0));
|
||||||
mem.write32(messagePointer + 4, ResultCode::Success);
|
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) {
|
void FSService::controlArchive(u32 messagePointer) {
|
||||||
const Handle archiveHandle = mem.read64(messagePointer + 4);
|
const Handle archiveHandle = mem.read64(messagePointer + 4);
|
||||||
const u32 action = mem.read32(messagePointer + 12);
|
const u32 action = mem.read32(messagePointer + 12);
|
||||||
|
|
Loading…
Add table
Reference in a new issue