diff --git a/CMakeLists.txt b/CMakeLists.txt index e6459bba..bc764b4a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -51,6 +51,7 @@ set(KERNEL_SOURCE_FILES src/core/kernel/kernel.cpp src/core/kernel/resource_limi src/core/kernel/memory_management.cpp src/core/kernel/ports.cpp src/core/kernel/events.cpp src/core/kernel/threads.cpp src/core/kernel/address_arbiter.cpp src/core/kernel/error.cpp + src/core/kernel/file_operations.cpp ) set(SERVICE_SOURCE_FILES src/core/services/service_manager.cpp src/core/services/apt.cpp src/core/services/hid.cpp src/core/services/fs.cpp src/core/services/gsp_gpu.cpp src/core/services/gsp_lcd.cpp @@ -70,7 +71,7 @@ set(HEADER_FILES include/emulator.hpp include/helpers.hpp include/opengl.hpp inc include/PICA/gpu.hpp include/PICA/regs.hpp include/services/ndm.hpp include/PICA/shader.hpp include/PICA/shader_unit.hpp include/PICA/float_types.hpp include/logger.hpp include/loader/ncch.hpp include/loader/ncsd.hpp include/io_file.hpp - include/loader/lz77.hpp + include/loader/lz77.hpp include/fs/archive_base.hpp include/fs/archive_ncch.hpp ) set(THIRD_PARTY_SOURCE_FILES third_party/imgui/imgui.cpp diff --git a/include/fs/archive_base.hpp b/include/fs/archive_base.hpp new file mode 100644 index 00000000..61740fd3 --- /dev/null +++ b/include/fs/archive_base.hpp @@ -0,0 +1,61 @@ +#pragma once +#include +#include "helpers.hpp" +#include "memory.hpp" + +namespace PathType { + enum : u32 { + Invalid = 0, + Empty = 1, + Binary = 2, + ASCII = 3, + UTF8 = 4, + }; +} + +namespace ArchiveID { + enum : u32 { + SelfNCCH = 3, + SaveData = 4, + ExtSaveData = 6, + SharedExtSaveData = 7, + SystemSaveData = 8, + SDMC = 9, + SDMCWriteOnly = 0xA + }; + + static const char* toString(u32 id) { + switch (id) { + case SelfNCCH: return "SelfNCCH"; + case SaveData: return "SaveData"; + case ExtSaveData: return "ExtSaveData"; + case SharedExtSaveData: return "SharedExtSaveData"; + case SystemSaveData: return "SystemSaveData"; + case SDMC: return "SDMC"; + case SDMCWriteOnly: return "SDMC (Write-only)"; + default: return "Unknown archive"; + } + } +} + +struct FSPath { + u32 type; + u32 size; + u32 pointer; // Pointer to the actual path data +}; + +class ArchiveBase { +protected: + using Result = u32; + using Handle = u32; + Memory& mem; + +public: + virtual const char* name() = 0; + virtual u64 getFreeBytes() = 0; + virtual bool openFile(const FSPath& path) = 0; + + virtual ArchiveBase* openArchive(FSPath& path) = 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 new file mode 100644 index 00000000..0ae63c73 --- /dev/null +++ b/include/fs/archive_ncch.hpp @@ -0,0 +1,28 @@ +#include "archive_base.hpp" + +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; + } +}; \ No newline at end of file diff --git a/include/kernel/kernel.hpp b/include/kernel/kernel.hpp index 22ee4cb5..67c61c71 100644 --- a/include/kernel/kernel.hpp +++ b/include/kernel/kernel.hpp @@ -55,16 +55,6 @@ class Kernel { return &objects[handle]; } - Handle makeObject(KernelObjectType type) { - if (handleCounter > KernelHandles::Max) [[unlikely]] { - Helpers::panic("Hlep we somehow created enough kernel objects to overflow this thing"); - } - - objects.push_back(KernelObject(handleCounter, type)); - log("Created %s object with handle %d\n", kernelObjectTypeToString(type), handleCounter); - return handleCounter++; - } - Handle makeArbiter(); Handle makeEvent(ResetType resetType); Handle makeProcess(u32 id); @@ -118,11 +108,29 @@ class Kernel { void outputDebugString(); void waitSynchronization1(); + // File operations + void handleFileOperation(u32 messagePointer, Handle file); + void readFile(u32 messagePointer, Handle file); + public: Kernel(CPU& cpu, Memory& mem, GPU& gpu); void setVersion(u8 major, u8 minor); void serviceSVC(u32 svc); void reset(); + Handle makeObject(KernelObjectType type) { + if (handleCounter > KernelHandles::Max) [[unlikely]] { + Helpers::panic("Hlep we somehow created enough kernel objects to overflow this thing"); + } + + objects.push_back(KernelObject(handleCounter, type)); + log("Created %s object with handle %d\n", kernelObjectTypeToString(type), handleCounter); + return handleCounter++; + } + + std::vector& getObjects() { + return objects; + } + void sendGPUInterrupt(GPUInterrupt type) { serviceManager.requestGPUInterrupt(type); } }; \ No newline at end of file diff --git a/include/kernel/kernel_types.hpp b/include/kernel/kernel_types.hpp index 3dcf8590..b6f37b6e 100644 --- a/include/kernel/kernel_types.hpp +++ b/include/kernel/kernel_types.hpp @@ -1,6 +1,7 @@ #pragma once #include #include +#include "fs/archive_base.hpp" #include "handles.hpp" #include "helpers.hpp" @@ -23,7 +24,7 @@ namespace SVCResult { } enum class KernelObjectType : u8 { - AddressArbiter, Event, Port, Process, ResourceLimit, Session, Thread, Dummy + AddressArbiter, Event, File, Port, Process, ResourceLimit, Session, Thread, Dummy }; enum class ResourceLimitCategory : int { @@ -92,6 +93,15 @@ 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 @@ -127,6 +137,7 @@ static const char* kernelObjectTypeToString(KernelObjectType t) { switch (t) { case KernelObjectType::AddressArbiter: return "address arbiter"; case KernelObjectType::Event: return "event"; + case KernelObjectType::File: return "file"; case KernelObjectType::Port: return "port"; case KernelObjectType::Process: return "process"; case KernelObjectType::ResourceLimit: return "resource limit"; diff --git a/include/logger.hpp b/include/logger.hpp index 7bcde212..762c8144 100644 --- a/include/logger.hpp +++ b/include/logger.hpp @@ -18,19 +18,20 @@ namespace Log { }; // Our loggers here. Enable/disable by toggling the template param - static Logger kernelLogger; + static Logger kernelLogger; static Logger debugStringLogger; // Enables output for the outputDebugString SVC - static Logger svcLogger; - static Logger gpuLogger; + static Logger errorLogger; + static Logger svcLogger; + static Logger gpuLogger; // Service loggers - static Logger aptLogger; - static Logger fsLogger; - static Logger hidLogger; - static Logger gspGPULogger; - static Logger gspLCDLogger; - static Logger ndmLogger; - static Logger srvLogger; + static Logger aptLogger; + static Logger fsLogger; + static Logger hidLogger; + static Logger gspGPULogger; + static Logger gspLCDLogger; + static Logger ndmLogger; + static Logger srvLogger; #define MAKE_LOG_FUNCTION(functionName, logger) \ template \ diff --git a/include/services/fs.hpp b/include/services/fs.hpp index a8d2a0fd..082d9110 100644 --- a/include/services/fs.hpp +++ b/include/services/fs.hpp @@ -1,21 +1,32 @@ #pragma once +#include "fs/archive_ncch.hpp" #include "helpers.hpp" #include "kernel_types.hpp" #include "logger.hpp" #include "memory.hpp" +// Yay, more circular dependencies +class Kernel; + class FSService { Handle handle = KernelHandles::FS; Memory& mem; + Kernel& kernel; + MAKE_LOG_FUNCTION(log, fsLogger) + SelfNCCHArchive selfNcch; + + ArchiveBase* getArchiveFromID(u32 id); + std::optional openFile(ArchiveBase* archive, const FSPath& path); + // Service commands void initialize(u32 messagePointer); void openArchive(u32 messagePointer); void openFileDirectly(u32 messagePointer); public: - FSService(Memory& mem) : mem(mem) {} + FSService(Memory& mem, Kernel& kernel) : mem(mem), selfNcch(mem), kernel(kernel) {} void reset(); void handleSyncRequest(u32 messagePointer); }; \ No newline at end of file diff --git a/include/services/service_manager.hpp b/include/services/service_manager.hpp index d0644f4d..8c43815f 100644 --- a/include/services/service_manager.hpp +++ b/include/services/service_manager.hpp @@ -10,6 +10,8 @@ #include "services/gsp_lcd.hpp" #include "services/ndm.hpp" +class Kernel; + class ServiceManager { std::array& regs; Memory& mem; @@ -28,7 +30,7 @@ class ServiceManager { void registerClient(u32 messagePointer); public: - ServiceManager(std::array& regs, Memory& mem, GPU& gpu, u32& currentPID); + ServiceManager(std::array& regs, Memory& mem, GPU& gpu, u32& currentPID, Kernel& kernel); void reset(); void handleSyncRequest(u32 messagePointer); diff --git a/src/core/kernel/file_operations.cpp b/src/core/kernel/file_operations.cpp new file mode 100644 index 00000000..f9d1c5ef --- /dev/null +++ b/src/core/kernel/file_operations.cpp @@ -0,0 +1,18 @@ +#include "kernel.hpp" + +namespace FileOps { + enum : u32 { + Read = 0x080200C2 + }; +} + +void Kernel::handleFileOperation(u32 messagePointer, Handle file) { + const u32 cmd = mem.read32(messagePointer); + switch (cmd) { + default: Helpers::panic("Unknown file operation: %08X", cmd); + } +} + +void Kernel::readFile(u32 messagePointer, Handle file) { + +} \ No newline at end of file diff --git a/src/core/kernel/kernel.cpp b/src/core/kernel/kernel.cpp index 0664f58e..a96deff4 100644 --- a/src/core/kernel/kernel.cpp +++ b/src/core/kernel/kernel.cpp @@ -4,7 +4,7 @@ #include "cpu.hpp" Kernel::Kernel(CPU& cpu, Memory& mem, GPU& gpu) - : cpu(cpu), regs(cpu.regs()), mem(mem), handleCounter(0), serviceManager(regs, mem, gpu, currentProcess) { + : cpu(cpu), regs(cpu.regs()), mem(mem), handleCounter(0), serviceManager(regs, mem, gpu, currentProcess, *this) { objects.reserve(512); // Make room for a few objects to avoid further memory allocs later portHandles.reserve(32); diff --git a/src/core/kernel/ports.cpp b/src/core/kernel/ports.cpp index c2de8d71..9721361c 100644 --- a/src/core/kernel/ports.cpp +++ b/src/core/kernel/ports.cpp @@ -84,9 +84,18 @@ void Kernel::sendSyncRequest() { return; } + // Check if our sync request is targetting a file instead of a service + bool isFileOperation = getObject(handle, KernelObjectType::File) != nullptr; + if (isFileOperation) { + handleFileOperation(messagePointer, handle); + regs[0] = SVCResult::Success; + return; + } + + // If we're actually communicating with a port const auto session = getObject(handle, KernelObjectType::Session); if (session == nullptr) [[unlikely]] { - Helpers::panic("SendSyncRequest: Invalid session handle"); + Helpers::panic("SendSyncRequest: Invalid handle"); regs[0] = SVCResult::BadHandle; return; } diff --git a/src/core/services/fs.cpp b/src/core/services/fs.cpp index 44c6c228..e51ce948 100644 --- a/src/core/services/fs.cpp +++ b/src/core/services/fs.cpp @@ -1,4 +1,5 @@ #include "services/fs.hpp" +#include "kernel/kernel.hpp" namespace FSCommands { enum : u32 { @@ -17,6 +18,28 @@ namespace Result { void FSService::reset() {} +ArchiveBase* FSService::getArchiveFromID(u32 id) { + switch (id) { + case ArchiveID::SelfNCCH: return &selfNcch; + default: + Helpers::panic("Unknown archive. ID: %d\n", id); + return nullptr; + } +} + +std::optional FSService::openFile(ArchiveBase* archive, const FSPath& path) { + bool opened = archive->openFile(path); + if (opened) { + auto handle = kernel.makeObject(KernelObjectType::File); + auto& file = kernel.getObjects()[handle]; + file.data = new FileSession(archive, path); + + return handle; + } else { + return std::nullopt; + } +} + void FSService::handleSyncRequest(u32 messagePointer) { const u32 command = mem.read32(messagePointer); switch (command) { @@ -48,9 +71,26 @@ void FSService::openFileDirectly(u32 messagePointer) { const u32 archivePathPointer = mem.read32(messagePointer + 40); const u32 filePathPointer = mem.read32(messagePointer + 48); - log("FS::OpenFileDirectly (failure)\n"); + log("FS::OpenFileDirectly\n"); - mem.write32(messagePointer + 4, Result::Success); - mem.write32(messagePointer + 12, 69); - //Helpers::panic("[FS::OpenFileDirectly] Tried to open file. Archive ID = %d\n", archiveID); + ArchiveBase* archive = getArchiveFromID(archiveID); + if (archive == nullptr) [[unlikely]] { + Helpers::panic("OpenFileDirectly: Tried to open unknown archive %d.", archiveID); + } + + FSPath archivePath { .type = archivePathType, .size = archivePathSize, .pointer = archivePathPointer }; + FSPath filePath { .type = filePathType, .size = filePathSize, .pointer = filePathPointer }; + + archive = archive->openArchive(archivePath); + if (archive == nullptr) [[unlikely]] { + Helpers::panic("OpenFileDirectly: Failed to open archive with given path"); + } + + std::optional handle = openFile(archive, filePath); + if (!handle.has_value()) { + Helpers::panic("OpenFileDirectly: Failed to open file with given path"); + } else { + mem.write32(messagePointer + 4, Result::Success); + mem.write32(messagePointer + 12, handle.value()); + } } \ No newline at end of file diff --git a/src/core/services/service_manager.cpp b/src/core/services/service_manager.cpp index 768ca1a6..f3fc7a10 100644 --- a/src/core/services/service_manager.cpp +++ b/src/core/services/service_manager.cpp @@ -1,7 +1,7 @@ #include "services/service_manager.hpp" -ServiceManager::ServiceManager(std::array& regs, Memory& mem, GPU& gpu, u32& currentPID) - : regs(regs), mem(mem), apt(mem), hid(mem), fs(mem), gsp_gpu(mem, gpu, currentPID), gsp_lcd(mem), ndm(mem) {} +ServiceManager::ServiceManager(std::array& regs, Memory& mem, GPU& gpu, u32& currentPID, Kernel& kernel) + : regs(regs), mem(mem), apt(mem), hid(mem), fs(mem, kernel), gsp_gpu(mem, gpu, currentPID), gsp_lcd(mem), ndm(mem) {} void ServiceManager::reset() { apt.reset();