Panda3DS/src/core/kernel/file_operations.cpp
2024-07-14 23:05:49 +03:00

268 lines
8.2 KiB
C++

#include "ipc.hpp"
#include "kernel.hpp"
namespace FileOps {
enum : u32 {
Read = 0x080200C2,
Write = 0x08030102,
GetSize = 0x08040000,
SetSize = 0x08050080,
Close = 0x08080000,
Flush = 0x08090000,
SetPriority = 0x080A0040,
OpenLinkFile = 0x080C0000
};
}
void Kernel::handleFileOperation(u32 messagePointer, Handle file) {
const u32 cmd = mem.read32(messagePointer);
switch (cmd) {
case FileOps::Close: closeFile(messagePointer, file); break;
case FileOps::Flush: flushFile(messagePointer, file); break;
case FileOps::GetSize: getFileSize(messagePointer, file); break;
case FileOps::OpenLinkFile: openLinkFile(messagePointer, file); break;
case FileOps::Read: readFile(messagePointer, file); break;
case FileOps::SetSize: setFileSize(messagePointer, file); break;
case FileOps::SetPriority: setFilePriority(messagePointer, file); break;
case FileOps::Write: writeFile(messagePointer, file); break;
default: Helpers::panic("Unknown file operation: %08X", cmd);
}
}
void Kernel::closeFile(u32 messagePointer, Handle fileHandle) {
logFileIO("Closed file %X\n", fileHandle);
const auto p = getObject(fileHandle, KernelObjectType::File);
if (p == nullptr) [[unlikely]] {
Helpers::panic("Called CloseFile on non-existent file");
}
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);
}
void Kernel::readFile(u32 messagePointer, Handle fileHandle) {
u64 offset = mem.read64(messagePointer + 4);
u32 size = mem.read32(messagePointer + 12);
u32 dataPointer = mem.read32(messagePointer + 20);
logFileIO("Trying to read %X bytes from file %X, starting from offset %llX into memory address %08X\n",
size, fileHandle, offset, dataPointer);
const auto p = getObject(fileHandle, KernelObjectType::File);
if (p == nullptr) [[unlikely]] {
Helpers::panic("Called ReadFile on non-existent file");
}
mem.write32(messagePointer, IPC::responseHeader(0x0802, 2, 2));
FileSession* file = p->getData<FileSession>();
if (!file->isOpen) {
Helpers::panic("Tried to read closed file");
}
// Handle files with their own file descriptors by just fread'ing the data
if (file->fd) {
std::unique_ptr<u8[]> data(new u8[size]);
IOFile f(file->fd);
auto [success, bytesRead] = f.readBytes(data.get(), size);
if (!success) {
Helpers::panic("Kernel::ReadFile with file descriptor failed");
}
else {
for (size_t i = 0; i < bytesRead; i++) {
mem.write8(u32(dataPointer + i), data[i]);
}
mem.write32(messagePointer + 4, Result::Success);
mem.write32(messagePointer + 8, u32(bytesRead));
}
return;
}
// Handle files without their own FD, such as SelfNCCH files
auto archive = file->archive;
std::optional<u32> bytesRead = archive->readFile(file, offset, size, dataPointer);
if (!bytesRead.has_value()) {
Helpers::panic("Kernel::ReadFile failed");
} else {
mem.write32(messagePointer + 4, Result::Success);
mem.write32(messagePointer + 8, bytesRead.value());
}
}
void Kernel::writeFile(u32 messagePointer, Handle fileHandle) {
u64 offset = mem.read64(messagePointer + 4);
u32 size = mem.read32(messagePointer + 12);
u32 writeOption = mem.read32(messagePointer + 16);
u32 dataPointer = mem.read32(messagePointer + 24);
logFileIO("Trying to write %X bytes to file %X, starting from file offset %llX and memory address %08X\n",
size, fileHandle, offset, dataPointer);
const auto p = getObject(fileHandle, KernelObjectType::File);
if (p == nullptr) [[unlikely]] {
Helpers::panic("Called ReadFile on non-existent file");
}
FileSession* file = p->getData<FileSession>();
if (!file->isOpen) {
Helpers::panic("Tried to write closed file");
}
if (!file->fd)
Helpers::panic("[Kernel::File::WriteFile] Tried to write to file without a valid file descriptor");
std::unique_ptr<u8[]> data(new u8[size]);
for (size_t i = 0; i < size; i++) {
data[i] = mem.read8(u32(dataPointer + i));
}
IOFile f(file->fd);
auto [success, bytesWritten] = f.writeBytes(data.get(), size);
// TODO: Should this check only the byte?
if (writeOption) {
f.flush();
}
mem.write32(messagePointer, IPC::responseHeader(0x0803, 2, 2));
if (!success) {
Helpers::panic("Kernel::WriteFile failed");
} else {
mem.write32(messagePointer + 4, Result::Success);
mem.write32(messagePointer + 8, u32(bytesWritten));
}
}
void Kernel::setFileSize(u32 messagePointer, Handle fileHandle) {
logFileIO("Setting size of file %X\n", fileHandle);
const auto p = getObject(fileHandle, KernelObjectType::File);
if (p == nullptr) [[unlikely]] {
Helpers::panic("Called SetFileSize on non-existent file");
}
FileSession* file = p->getData<FileSession>();
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);
IOFile f(file->fd);
bool success = f.setSize(newSize);
if (success) {
mem.write32(messagePointer + 4, Result::Success);
} else {
Helpers::warn("FileOp::SetFileSize failed");
mem.write32(messagePointer + 4, Result::FailurePlaceholder);
}
} else {
Helpers::panic("Tried to set file size of file without file descriptor");
}
}
void Kernel::getFileSize(u32 messagePointer, Handle fileHandle) {
logFileIO("Getting size of file %X\n", fileHandle);
const auto p = getObject(fileHandle, KernelObjectType::File);
if (p == nullptr) [[unlikely]] {
Helpers::panic("Called GetFileSize on non-existent file");
}
FileSession* file = p->getData<FileSession>();
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);
std::optional<u64> size = f.size();
if (size.has_value()) {
mem.write32(messagePointer + 4, Result::Success);
mem.write64(messagePointer + 8, size.value());
} else {
Helpers::panic("FileOp::GetFileSize failed");
}
} else {
Helpers::panic("Tried to get file size of file without file descriptor");
}
}
void Kernel::openLinkFile(u32 messagePointer, Handle fileHandle) {
logFileIO("Open link file (clone) of file %X\n", fileHandle);
const auto p = getObject(fileHandle, KernelObjectType::File);
if (p == nullptr) [[unlikely]] {
Helpers::panic("Called GetFileSize on non-existent file");
}
FileSession* file = p->getData<FileSession>();
if (!file->isOpen) {
Helpers::panic("Tried to clone closed file");
}
// Make clone object
auto handle = makeObject(KernelObjectType::File);
auto& cloneFile = getObjects()[handle];
// Make a clone of the file by copying the archive/archive path/file path/file descriptor/etc of the original file
// TODO: Maybe we should duplicate the file handle instead of copying. This way their offsets will be separate
// 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);
}
void Kernel::setFilePriority(u32 messagePointer, Handle fileHandle) {
const u32 priority = mem.read32(messagePointer + 4);
logFileIO("Setting priority of file %X to %d\n", fileHandle, priority);
const auto p = getObject(fileHandle, KernelObjectType::File);
if (p == nullptr) [[unlikely]] {
Helpers::panic("Called GetFileSize on non-existent file");
}
FileSession* file = p->getData<FileSession>();
if (!file->isOpen) {
Helpers::panic("Tried to clone closed file");
}
file->priority = priority;
mem.write32(messagePointer, IPC::responseHeader(0x080A, 1, 0));
mem.write32(messagePointer + 4, Result::Success);
}