From df4cd0642d49de7f055d1620ec7996e85c2c2bf1 Mon Sep 17 00:00:00 2001 From: wheremyfoodat Date: Sun, 9 Oct 2022 18:28:45 +0300 Subject: [PATCH] Implement reading from RomFS --- CMakeLists.txt | 8 ++-- include/fs/archive_base.hpp | 16 +++++++ include/fs/archive_ncch.hpp | 22 ++-------- include/kernel/kernel.hpp | 2 + include/kernel/kernel_types.hpp | 9 ---- include/logger.hpp | 1 + include/memory.hpp | 13 ++++++ src/core/filesystem/archive_ncch.cpp | 62 ++++++++++++++++++++++++++++ src/core/kernel/file_operations.cpp | 53 ++++++++++++++++++++++-- src/core/loader/ncsd.cpp | 4 ++ 10 files changed, 157 insertions(+), 33 deletions(-) create mode 100644 src/core/filesystem/archive_ncch.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index bc764b4a..fc44ed5b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -61,6 +61,7 @@ set(PICA_SOURCE_FILES src/core/PICA/gpu.cpp src/core/PICA/regs.cpp src/core/PICA src/core/PICA/shader_interpreter.cpp src/core/PICA/renderer_opengl.cpp ) set(LOADER_SOURCE_FILES src/core/loader/elf.cpp src/core/loader/ncsd.cpp src/core/loader/ncch.cpp) +set(FS_SOURCE_FILES src/core/filesystem/archive_ncch.cpp) set(HEADER_FILES include/emulator.hpp include/helpers.hpp include/opengl.hpp include/termcolor.hpp include/cpu.hpp include/cpu_dynarmic.hpp include/memory.hpp include/kernel/kernel.hpp @@ -85,12 +86,13 @@ set(THIRD_PARTY_SOURCE_FILES third_party/imgui/imgui.cpp #add_library(Alber ${HEADER_FILES}) source_group("Header Files\\Core" FILES ${HEADER_FILES}) source_group("Source Files\\Core" FILES ${SOURCE_FILES}) -source_group("Source Files\\Loader" FILES ${LOADER_SOURCE_FILES}) +source_group("Source Files\\Core\\Filesystem" FILES ${FS_SOURCE_FILES}) source_group("Source Files\\Core\\Kernel" FILES ${KERNEL_SOURCE_FILES}) +source_group("Source Files\\Core\\Loader" FILES ${LOADER_SOURCE_FILES}) source_group("Source Files\\Core\\Services" FILES ${SERVICE_SOURCE_FILES}) source_group("Source Files\\Core\\PICA" FILES ${PICA_SOURCE_FILES}) source_group("Source Files\\Third Party" FILES ${THIRD_PARTY_SOURCE_FILES}) -add_executable(Alber ${SOURCE_FILES} ${LOADER_SOURCE_FILES} ${KERNEL_SOURCE_FILES} ${SERVICE_SOURCE_FILES} ${PICA_SOURCE_FILES} -${THIRD_PARTY_SOURCE_FILES} ${HEADER_FILES}) +add_executable(Alber ${SOURCE_FILES} ${FS_SOURCE_FILES} ${KERNEL_SOURCE_FILES} ${LOADER_SOURCE_FILES} ${SERVICE_SOURCE_FILES} +${PICA_SOURCE_FILES} ${THIRD_PARTY_SOURCE_FILES} ${HEADER_FILES}) target_link_libraries(Alber PRIVATE dynarmic SDL2-static) \ No newline at end of file diff --git a/include/fs/archive_base.hpp b/include/fs/archive_base.hpp index 61740fd3..7692fc67 100644 --- a/include/fs/archive_base.hpp +++ b/include/fs/archive_base.hpp @@ -44,6 +44,18 @@ struct FSPath { u32 pointer; // Pointer to the actual path data }; +class ArchiveBase; + +struct FileSession { + ArchiveBase* archive = nullptr; + FSPath path; + bool isOpen; + + FileSession(ArchiveBase* archive, const FSPath& filePath, bool isOpen = true) : archive(archive), isOpen(isOpen) { + path = filePath; + } +}; + class ArchiveBase { protected: using Result = u32; @@ -56,6 +68,10 @@ public: virtual bool openFile(const FSPath& path) = 0; virtual ArchiveBase* openArchive(FSPath& path) = 0; + + // 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 + virtual std::optional readFile(FileSession* file, u64 offset, u32 size, u32 dataPointer) = 0; ArchiveBase(Memory& mem) : mem(mem) {} }; \ No newline at end of file diff --git a/include/fs/archive_ncch.hpp b/include/fs/archive_ncch.hpp index 0ae63c73..ade3c0e6 100644 --- a/include/fs/archive_ncch.hpp +++ b/include/fs/archive_ncch.hpp @@ -4,25 +4,11 @@ class SelfNCCHArchive : public ArchiveBase { public: SelfNCCHArchive(Memory& mem) : ArchiveBase(mem) {} - + u64 getFreeBytes() override { return 0; } const char* name() override { return "SelfNCCH"; } - bool openFile(const FSPath& path) override { - if (path.type != PathType::Binary) { - printf("Invalid SelfNCCH path type"); - return false; - } - - return true; - } - - ArchiveBase* openArchive(FSPath& path) override { - if (path.type != PathType::Empty) { - printf("Invalid path type for SelfNCCH archive: %d\n", path.type); - return nullptr; - } - - return this; - } + bool openFile(const FSPath& path) override; + ArchiveBase* openArchive(FSPath& path) override; + std::optional readFile(FileSession* file, u64 offset, u32 size, u32 dataPointer) override; }; \ No newline at end of file diff --git a/include/kernel/kernel.hpp b/include/kernel/kernel.hpp index 67c61c71..94801729 100644 --- a/include/kernel/kernel.hpp +++ b/include/kernel/kernel.hpp @@ -84,6 +84,7 @@ class Kernel { MAKE_LOG_FUNCTION(logSVC, svcLogger) MAKE_LOG_FUNCTION(logDebugString, debugStringLogger) MAKE_LOG_FUNCTION(logError, errorLogger) + MAKE_LOG_FUNCTION(logFileIO, fileIOLogger) // SVC implementations void arbitrateAddress(); @@ -110,6 +111,7 @@ class Kernel { // File operations void handleFileOperation(u32 messagePointer, Handle file); + void closeFile(u32 messagePointer, Handle file); void readFile(u32 messagePointer, Handle file); public: diff --git a/include/kernel/kernel_types.hpp b/include/kernel/kernel_types.hpp index b6f37b6e..be44dce5 100644 --- a/include/kernel/kernel_types.hpp +++ b/include/kernel/kernel_types.hpp @@ -93,15 +93,6 @@ struct Session { Session(Handle portHandle) : portHandle(portHandle) {} }; -struct FileSession { - ArchiveBase* archive = nullptr; - FSPath path; - - FileSession(ArchiveBase* archive, const FSPath& filePath) : archive(archive) { - path = filePath; - } -}; - enum class ThreadStatus { Running, // Currently running Ready, // Ready to run diff --git a/include/logger.hpp b/include/logger.hpp index 762c8144..080637c0 100644 --- a/include/logger.hpp +++ b/include/logger.hpp @@ -21,6 +21,7 @@ namespace Log { static Logger kernelLogger; static Logger debugStringLogger; // Enables output for the outputDebugString SVC static Logger errorLogger; + static Logger fileIOLogger; static Logger svcLogger; static Logger gpuLogger; diff --git a/include/memory.hpp b/include/memory.hpp index b6c91327..4d69194d 100644 --- a/include/memory.hpp +++ b/include/memory.hpp @@ -139,6 +139,14 @@ public: u32 getLinearHeapVaddr(); u8* getFCRAM() { return fcram; } + NCCH* getCXI() { + if (loadedCXI.has_value()) { + return &loadedCXI.value(); + } else { + return nullptr; + } + } + // Returns whether "addr" is aligned to a page (4096 byte) boundary static constexpr bool isAligned(u32 addr) { return (addr & pageMask) == 0; @@ -165,4 +173,9 @@ public: // TODO: Find out // Returns a pointer to the FCRAM block used for the memory if allocation succeeded u8* mapSharedMemory(Handle handle, u32 vaddr, u32 myPerms, u32 otherPerms); + + // Backup of the game's CXI partition info, if any + std::optional loadedCXI = std::nullopt; + // File handle for reading the loaded ncch + IOFile CXIFile; }; \ No newline at end of file diff --git a/src/core/filesystem/archive_ncch.cpp b/src/core/filesystem/archive_ncch.cpp new file mode 100644 index 00000000..b50715f8 --- /dev/null +++ b/src/core/filesystem/archive_ncch.cpp @@ -0,0 +1,62 @@ +#include "fs/archive_ncch.hpp" +#include + +bool SelfNCCHArchive::openFile(const FSPath& path) { + if (path.type != PathType::Binary) { + printf("Invalid SelfNCCH path type"); + return false; + } + + // We currently only know how to read from an NCCH's RomFS + if (mem.read32(path.pointer) != 0) { + Helpers::panic("Read from NCCH's non-RomFS section!"); + } + + return true; +} + +ArchiveBase* SelfNCCHArchive::openArchive(FSPath& path) { + if (path.type != PathType::Empty) { + printf("Invalid path type for SelfNCCH archive: %d\n", path.type); + return nullptr; + } + + return this; +} + +std::optional SelfNCCHArchive::readFile(FileSession* file, u64 offset, u32 size, u32 dataPointer) { + auto cxi = mem.getCXI(); + if (cxi == nullptr) { + Helpers::panic("Tried to read file from non-existent CXI"); + return std::nullopt; + } + + if (!cxi->hasRomFS()) { + Helpers::panic("Tried to read from CXI without RomFS"); + } + + const u32 romFSSize = cxi->romFS.size; + const u32 romFSOffset = cxi->romFS.offset; + if ((offset >> 32) || (offset >= romFSSize) || (offset + size >= romFSSize)) { + Helpers::panic("Tried to read from SelfNCCH with too big of an offset"); + } + + IOFile& ioFile = mem.CXIFile; + if (!ioFile.seek(cxi->fileOffset + romFSOffset + offset)) { + Helpers::panic("Failed to seek while reading from RomFS"); + } + + std::unique_ptr data(new u8[size]); + auto [success, bytesRead] = ioFile.readBytes(&data[0], size); + + if (!success) { + Helpers::panic("Failed to read from RomFS"); + } + + for (u64 i = 0; i < bytesRead; i++) { + mem.write8(dataPointer + i, data[i]); + printf("%x\n", data[i]); + } + + return bytesRead; +} \ No newline at end of file diff --git a/src/core/kernel/file_operations.cpp b/src/core/kernel/file_operations.cpp index f9d1c5ef..0b90b520 100644 --- a/src/core/kernel/file_operations.cpp +++ b/src/core/kernel/file_operations.cpp @@ -2,17 +2,64 @@ namespace FileOps { enum : u32 { - Read = 0x080200C2 + Read = 0x080200C2, + Close = 0x08080000 }; } +namespace Result { + enum : u32 { + Success = 0 + }; +} + + void Kernel::handleFileOperation(u32 messagePointer, Handle file) { const u32 cmd = mem.read32(messagePointer); switch (cmd) { + case FileOps::Close: closeFile(messagePointer, file); break; + case FileOps::Read: readFile(messagePointer, file); break; default: Helpers::panic("Unknown file operation: %08X", cmd); } } -void Kernel::readFile(u32 messagePointer, Handle file) { - +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"); + } + + p->getData()->isOpen = false; + 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"); + } + + FileSession* file = p->getData(); + if (!file->isOpen) { + Helpers::panic("Tried to read closed file"); + } + + 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()); + } } \ No newline at end of file diff --git a/src/core/loader/ncsd.cpp b/src/core/loader/ncsd.cpp index a86606e4..4f0c11eb 100644 --- a/src/core/loader/ncsd.cpp +++ b/src/core/loader/ncsd.cpp @@ -118,5 +118,9 @@ std::optional Memory::loadNCSD(const std::filesystem::path& path) { ncsd.entrypoint = textAddr; + // Back the IOFile for accessing the ROM, as well as the ROM's CXI partition, in the memory class. + CXIFile = ncsd.file; + loadedCXI = cxi; + return ncsd; } \ No newline at end of file