#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(); 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(); 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(); 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 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 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(); 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 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(); 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(); 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 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(); 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(); 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); }